From 929b6c29d8da616c0fa2f6ed13b4210ac863caa7 Mon Sep 17 00:00:00 2001 From: James M Snell <jasnell@gmail.com> Date: Mon, 2 May 2016 10:27:12 -0700 Subject: [PATCH 001/131] src: refactor require('constants') The require('constants') module is currently undocumented and mashes together unrelated constants. This refactors the require('constants') in favor of distinct os.constants, fs.constants, and crypto.constants that are specific to the modules for which they are relevant. The next step is to document those within the specific modules. PR-URL: https://github.com/nodejs/node/pull/6534 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Robert Lindstaedt <robert.lindstaedt@gmail.com> Conflicts: doc/api/fs.md --- doc/api/crypto.md | 381 ++++++++- doc/api/fs.md | 275 ++++++- doc/api/os.md | 770 ++++++++++++++++++ lib/_tls_common.js | 6 +- lib/child_process.js | 2 +- lib/constants.js | 10 +- lib/crypto.js | 8 +- lib/dgram.js | 4 +- lib/fs.js | 8 +- lib/internal/child_process.js | 2 +- lib/internal/process.js | 6 +- lib/os.js | 6 + lib/tls.js | 4 +- src/node.cc | 2 +- src/node_constants.cc | 30 +- src/node_constants.h | 2 +- test/parallel/test-constants.js | 12 + test/parallel/test-crypto-binary-default.js | 4 +- test/parallel/test-crypto-dh.js | 18 +- test/parallel/test-crypto-rsa-dsa.js | 2 +- test/parallel/test-fs-open-flags.js | 15 +- test/parallel/test-fs-open-numeric-flags.js | 3 +- test/parallel/test-fs-write.js | 3 +- .../test-https-agent-session-eviction.js | 4 +- .../test-process-constants-noatime.js | 6 +- .../test-tls-async-cb-after-socket-end.js | 4 +- test/parallel/test-tls-cipher-list.js | 4 +- test/parallel/test-tls-ocsp-callback.js | 5 +- test/parallel/test-tls-server-verify.js | 6 +- 29 files changed, 1486 insertions(+), 116 deletions(-) create mode 100644 test/parallel/test-constants.js diff --git a/doc/api/crypto.md b/doc/api/crypto.md index e1166230d719c6..e195c98e322746 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -856,6 +856,12 @@ thrown. ## `crypto` module methods and properties +## crypto.constants + +Returns an object containing commonly used constants for crypto and security +related operations. The specific constants currently defined are described in +[Crypto Constants][]. + ### crypto.DEFAULT_ENCODING The default encoding to use for functions that can take either strings @@ -1205,11 +1211,11 @@ keys: * `key` : {String} - PEM encoded private key * `passphrase` : {String} - Optional passphrase for the private key * `padding` : An optional padding value, one of the following: - * `constants.RSA_NO_PADDING` - * `constants.RSA_PKCS1_PADDING` - * `constants.RSA_PKCS1_OAEP_PADDING` + * `crypto.constants.RSA_NO_PADDING` + * `crypto.constants.RSA_PKCS1_PADDING` + * `crypto.constants.RSA_PKCS1_OAEP_PADDING` -All paddings are defined in the `constants` module. +All paddings are defined in `crypto.constants`. ### crypto.privateEncrypt(private_key, buffer) @@ -1223,11 +1229,11 @@ keys: * `key` : {String} - PEM encoded private key * `passphrase` : {String} - Optional passphrase for the private key * `padding` : An optional padding value, one of the following: - * `constants.RSA_NO_PADDING` - * `constants.RSA_PKCS1_PADDING` - * `constants.RSA_PKCS1_OAEP_PADDING` + * `crypto.constants.RSA_NO_PADDING` + * `crypto.constants.RSA_PKCS1_PADDING` + * `crypto.constants.RSA_PKCS1_OAEP_PADDING` -All paddings are defined in the `constants` module. +All paddings are defined in `crypto.constants`. ### crypto.publicDecrypt(public_key, buffer) @@ -1241,14 +1247,14 @@ keys: * `key` : {String} - PEM encoded public key * `passphrase` : {String} - Optional passphrase for the private key * `padding` : An optional padding value, one of the following: - * `constants.RSA_NO_PADDING` - * `constants.RSA_PKCS1_PADDING` - * `constants.RSA_PKCS1_OAEP_PADDING` + * `crypto.constants.RSA_NO_PADDING` + * `crypto.constants.RSA_PKCS1_PADDING` + * `crypto.constants.RSA_PKCS1_OAEP_PADDING` Because RSA public keys can be derived from private keys, a private key may be passed instead of a public key. -All paddings are defined in the `constants` module. +All paddings are defined in `crypto.constants`. ### crypto.publicEncrypt(public_key, buffer) @@ -1262,14 +1268,14 @@ keys: * `key` : {String} - PEM encoded public key * `passphrase` : {String} - Optional passphrase for the private key * `padding` : An optional padding value, one of the following: - * `constants.RSA_NO_PADDING` - * `constants.RSA_PKCS1_PADDING` - * `constants.RSA_PKCS1_OAEP_PADDING` + * `crypto.constants.RSA_NO_PADDING` + * `crypto.constants.RSA_PKCS1_PADDING` + * `crypto.constants.RSA_PKCS1_OAEP_PADDING` Because RSA public keys can be derived from private keys, a private key may be passed instead of a public key. -All paddings are defined in the `constants` module. +All paddings are defined in `crypto.constants`. ### crypto.randomBytes(size[, callback]) @@ -1313,22 +1319,22 @@ Load and set the `engine` for some or all OpenSSL functions (selected by flags). `engine` could be either an id or a path to the engine's shared library. The optional `flags` argument uses `ENGINE_METHOD_ALL` by default. The `flags` -is a bit field taking one of or a mix of the following flags (defined in the -`constants` module): - -* `ENGINE_METHOD_RSA` -* `ENGINE_METHOD_DSA` -* `ENGINE_METHOD_DH` -* `ENGINE_METHOD_RAND` -* `ENGINE_METHOD_ECDH` -* `ENGINE_METHOD_ECDSA` -* `ENGINE_METHOD_CIPHERS` -* `ENGINE_METHOD_DIGESTS` -* `ENGINE_METHOD_STORE` -* `ENGINE_METHOD_PKEY_METHS` -* `ENGINE_METHOD_PKEY_ASN1_METHS` -* `ENGINE_METHOD_ALL` -* `ENGINE_METHOD_NONE` +is a bit field taking one of or a mix of the following flags (defined in +`crypto.constants`): + +* `crypto.constants.ENGINE_METHOD_RSA` +* `crypto.constants.ENGINE_METHOD_DSA` +* `crypto.constants.ENGINE_METHOD_DH` +* `crypto.constants.ENGINE_METHOD_RAND` +* `crypto.constants.ENGINE_METHOD_ECDH` +* `crypto.constants.ENGINE_METHOD_ECDSA` +* `crypto.constants.ENGINE_METHOD_CIPHERS` +* `crypto.constants.ENGINE_METHOD_DIGESTS` +* `crypto.constants.ENGINE_METHOD_STORE` +* `crypto.constants.ENGINE_METHOD_PKEY_METHS` +* `crypto.constants.ENGINE_METHOD_PKEY_ASN1_METHS` +* `crypto.constants.ENGINE_METHOD_ALL` +* `crypto.constants.ENGINE_METHOD_NONE` ## Notes @@ -1380,6 +1386,316 @@ Based on the recommendations of [NIST SP 800-131A][]: See the reference for other recommendations and details. +## Crypto Constants + +The following constants exported by `crypto.constants` apply to various uses of +the `crypto`, `tls`, and `https` modules and are generally specific to OpenSSL. + +### OpenSSL Options + +<table> + <tr> + <th>Constant</th> + <th>Description</th> + </tr> + <tr> + <td><code>SSL_OP_ALL</code></td> + <td>Applies multiple bug workarounds within OpenSSL. See + https://www.openssl.org/docs/manmaster/ssl/SSL_CTX_set_options.html for + detail.</td> + </tr> + <tr> + <td><code>SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION</code></td> + <td>Allows legacy insecure renegotiation between OpenSSL and unpatched + clients or servers. See + https://www.openssl.org/docs/manmaster/ssl/SSL_CTX_set_options.html.</td> + </tr> + <tr> + <td><code>SSL_OP_CIPHER_SERVER_PREFERENCE</code></td> + <td>Uses the server's preferences instead of the clients when selecting a + cipher. See + https://www.openssl.org/docs/manmaster/ssl/SSL_CTX_set_options.html.</td> + </tr> + <tr> + <td><code>SSL_OP_CISCO_ANYCONNECT</code></td> + <td>Instructs OpenSSL to use Cisco's "speshul" version of DTLS_BAD_VER.</td> + </tr> + <tr> + <td><code>SSL_OP_COOKIE_EXCHANGE</code></td> + <td>Instructs OpenSSL to turn on cookie exchange.</td> + </tr> + <tr> + <td><code>SSL_OP_CRYPTOPRO_TLSEXT_BUG</code></td> + <td>Instructs OpenSSL to add server-hello extension from an early version + of the cryptopro draft.</td> + </tr> + <tr> + <td><code>SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS</code></td> + <td>Instructs OpenSSL to disable a SSL 3.0/TLS 1.0 vulnerability + workaround added in OpenSSL 0.9.6d.</td> + </tr> + <tr> + <td><code>SSL_OP_EPHEMERAL_RSA</code></td> + <td>Instructs OpenSSL to always use the tmp_rsa key when performing RSA + operations.</td> + </tr> + <tr> + <td><code>SSL_OP_LEGACY_SERVER_CONNECT</code></td> + <td>Allow initial connection to servers that do not support RI.</td> + </tr> + <tr> + <td><code>SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER</code></td> + <td></td> + </tr> + <tr> + <td><code>SSL_OP_MICROSOFT_SESS_ID_BUG</code></td> + <td></td> + </tr> + <tr> + <td><code>SSL_OP_MSIE_SSLV2_RSA_PADDING</code></td> + <td>Instructs OpenSSL to disable the workaround for a man-in-the-middle + protocol-version vulnerability in the SSL 2.0 server implementation.</td> + </tr> + <tr> + <td><code>SSL_OP_NETSCAPE_CA_DN_BUG</code></td> + <td></td> + </tr> + <tr> + <td><code>SSL_OP_NETSCAPE_CHALLENGE_BUG</code></td> + <td></td> + </tr> + <tr> + <td><code>SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG</code></td> + <td></td> + </tr> + <tr> + <td><code>SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG</code></td> + <td></td> + </tr> + <tr> + <td><code>SSL_OP_NO_COMPRESSION</code></td> + <td>Instructs OpenSSL to disable support for SSL/TLS compression.</td> + </tr> + <tr> + <td><code>SSL_OP_NO_QUERY_MTU</code></td> + <td></td> + </tr> + <tr> + <td><code>SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION</code></td> + <td>Instructs OpenSSL to always start a new session when performing + renegotiation.</td> + </tr> + <tr> + <td><code>SSL_OP_NO_SSLv2</code></td> + <td>Instructs OpenSSL to turn off SSL v2</td> + </tr> + <tr> + <td><code>SSL_OP_NO_SSLv3</code></td> + <td>Instructs OpenSSL to turn off SSL v3</td> + </tr> + <tr> + <td><code>SSL_OP_NO_TICKET</code></td> + <td>Instructs OpenSSL to disable use of RFC4507bis tickets.</td> + </tr> + <tr> + <td><code>SSL_OP_NO_TLSv1</code></td> + <td>Instructs OpenSSL to turn off TLS v1</td> + </tr> + <tr> + <td><code>SSL_OP_NO_TLSv1_1</code></td> + <td>Instructs OpenSSL to turn off TLS v1.1</td> + </tr> + <tr> + <td><code>SSL_OP_NO_TLSv1_2</code></td> + <td>Instructs OpenSSL to turn off TLS v1.2</td> + </tr> + <td><code>SSL_OP_PKCS1_CHECK_1</code></td> + <td></td> + </tr> + <tr> + <td><code>SSL_OP_PKCS1_CHECK_2</code></td> + <td></td> + </tr> + <tr> + <td><code>SSL_OP_SINGLE_DH_USE</code></td> + <td>Instructs OpenSSL to always create a new key when using + temporary/ephemeral DH parameters.</td> + </tr> + <tr> + <td><code>SSL_OP_SINGLE_ECDH_USE</code></td> + <td>Instructs OpenSSL to always create a new key when using + temporary/ephemeral ECDH parameters.</td> + </tr> + <td><code>SSL_OP_SSLEAY_080_CLIENT_DH_BUG</code></td> + <td></td> + </tr> + <tr> + <td><code>SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG</code></td> + <td></td> + </tr> + <tr> + <td><code>SSL_OP_TLS_BLOCK_PADDING_BUG</code></td> + <td></td> + </tr> + <tr> + <td><code>SSL_OP_TLS_D5_BUG</code></td> + <td></td> + </tr> + <tr> + <td><code>SSL_OP_TLS_ROLLBACK_BUG</code></td> + <td>Instructs OpenSSL to disable version rollback attack detection.</td> + </tr> +</table> + +### OpenSSL Engine Constants + +<table> + <tr> + <th>Constant</th> + <th>Description</th> + </tr> + <tr> + <td><code>ENGINE_METHOD_RSA</code></td> + <td>Limit engine usage to RSA</td> + </tr> + <tr> + <td><code>ENGINE_METHOD_DSA</code></td> + <td>Limit engine usage to DSA</td> + </tr> + <tr> + <td><code>ENGINE_METHOD_DH</code></td> + <td>Limit engine usage to DH</td> + </tr> + <tr> + <td><code>ENGINE_METHOD_RAND</code></td> + <td>Limit engine usage to RAND</td> + </tr> + <tr> + <td><code>ENGINE_METHOD_ECDH</code></td> + <td>Limit engine usage to ECDH</td> + </tr> + <tr> + <td><code>ENGINE_METHOD_ECDSA</code></td> + <td>Limit engine usage to ECDSA</td> + </tr> + <tr> + <td><code>ENGINE_METHOD_CIPHERS</code></td> + <td>Limit engine usage to CIPHERS</td> + </tr> + <tr> + <td><code>ENGINE_METHOD_DIGESTS</code></td> + <td>Limit engine usage to DIGESTS</td> + </tr> + <tr> + <td><code>ENGINE_METHOD_STORE</code></td> + <td>Limit engine usage to STORE</td> + </tr> + <tr> + <td><code>ENGINE_METHOD_PKEY_METHS</code></td> + <td>Limit engine usage to PKEY_METHDS</td> + </tr> + <tr> + <td><code>ENGINE_METHOD_PKEY_ASN1_METHS</code></td> + <td>Limit engine usage to PKEY_ASN1_METHS</td> + </tr> + <tr> + <td><code>ENGINE_METHOD_ALL</code></td> + <td></td> + </tr> + <tr> + <td><code>ENGINE_METHOD_NONE</code></td> + <td></td> + </tr> +</table> + +### Other OpenSSL Constants + +<table> + <tr> + <th>Constant</th> + <th>Description</th> + </tr> + <tr> + <td><code>DH_CHECK_P_NOT_SAFE_PRIME</code></td> + <td></td> + </tr> + <tr> + <td><code>DH_CHECK_P_NOT_PRIME</code></td> + <td></td> + </tr> + <tr> + <td><code>DH_UNABLE_TO_CHECK_GENERATOR</code></td> + <td></td> + </tr> + <tr> + <td><code>DH_NOT_SUITABLE_GENERATOR</code></td> + <td></td> + </tr> + <tr> + <td><code>NPN_ENABLED</code></td> + <td></td> + </tr> + <tr> + <td><code>ALPN_ENABLED</code></td> + <td></td> + </tr> + <tr> + <td><code>RSA_PKCS1_PADDING</code></td> + <td></td> + </tr> + <tr> + <td><code>RSA_SSLV23_PADDING</code></td> + <td></td> + </tr> + <tr> + <td><code>RSA_NO_PADDING</code></td> + <td></td> + </tr> + <tr> + <td><code>RSA_PKCS1_OAEP_PADDING</code></td> + <td></td> + </tr> + <tr> + <td><code>RSA_X931_PADDING</code></td> + <td></td> + </tr> + <tr> + <td><code>RSA_PKCS1_PSS_PADDING</code></td> + <td></td> + </tr> + <tr> + <td><code>POINT_CONVERSION_COMPRESSED</code></td> + <td></td> + </tr> + <tr> + <td><code>POINT_CONVERSION_UNCOMPRESSED</code></td> + <td></td> + </tr> + <tr> + <td><code>POINT_CONVERSION_HYBRID</code></td> + <td></td> + </tr> +</table> + +### Node.js Crypto Constants + +<table> + <tr> + <th>Constant</th> + <th>Description</th> + </tr> + <tr> + <td><code>defaultCoreCipherList</code></td> + <td>Specifies the built-in default cipher list used by Node.js.</td> + </tr> + <tr> + <td><code>defaultCipherList</code></td> + <td>Specifies the active default cipher list used by the current Node.js + process.</td> + </tr> +</table> + + [`Buffer`]: buffer.html [`cipher.final()`]: #crypto_cipher_final_output_encoding [`cipher.update()`]: #crypto_cipher_update_data_input_encoding_output_encoding @@ -1423,3 +1739,4 @@ See the reference for other recommendations and details. [RFC 3526]: https://www.rfc-editor.org/rfc/rfc3526.txt [stream]: stream.html [stream-writable-write]: stream.html#stream_writable_write_chunk_encoding_callback +[Crypto Constants]: #crypto_crypto_constants diff --git a/doc/api/fs.md b/doc/api/fs.md index e752a1b33dde94..bd975732c67987 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -315,13 +315,13 @@ checks to be performed. The following constants define the possible values of `mode`. It is possible to create a mask consisting of the bitwise OR of two or more values. -- `fs.F_OK` - `path` is visible to the calling process. This is useful +- `fs.constants.F_OK` - `path` is visible to the calling process. This is useful for determining if a file exists, but says nothing about `rwx` permissions. Default if no `mode` is specified. -- `fs.R_OK` - `path` can be read by the calling process. -- `fs.W_OK` - `path` can be written by the calling process. -- `fs.X_OK` - `path` can be executed by the calling process. This has -no effect on Windows (will behave like `fs.F_OK`). +- `fs.constants.R_OK` - `path` can be read by the calling process. +- `fs.constants.W_OK` - `path` can be written by the calling process. +- `fs.constants.X_OK` - `path` can be executed by the calling process. This has +no effect on Windows (will behave like `fs.constants.F_OK`). The final argument, `callback`, is a callback function that is invoked with a possible error argument. If any of the accessibility checks fail, the error @@ -329,7 +329,7 @@ argument will be populated. The following example checks if the file `/etc/passwd` can be read and written by the current process. ```js -fs.access('/etc/passwd', fs.R_OK | fs.W_OK, (err) => { +fs.access('/etc/passwd', fs.constants.R_OK | fs.constants.W_OK, (err) => { console.log(err ? 'no access!' : 'can read/write'); }); ``` @@ -342,8 +342,8 @@ added: v0.11.15 * `path` {String | Buffer} * `mode` {Integer} -Synchronous version of [`fs.access()`][]. This throws if any accessibility checks -fail, and does nothing otherwise. +Synchronous version of [`fs.access()`][]. This throws if any accessibility +checks fail, and does nothing otherwise. ## fs.appendFile(file, data[, options], callback) <!-- YAML @@ -460,6 +460,12 @@ added: v0.1.21 Synchronous close(2). Returns `undefined`. +## fs.constants + +Returns an object containing commonly used constants for file system +operations. The specific constants currently defined are described in +[FS Constants][]. + ## fs.createReadStream(path[, options]) <!-- YAML added: v0.1.31 @@ -498,9 +504,9 @@ the file instead of the entire file. Both `start` and `end` are inclusive and start at 0. The `encoding` can be any one of those accepted by [`Buffer`][]. If `fd` is specified, `ReadStream` will ignore the `path` argument and will use -the specified file descriptor. This means that no `'open'` event will be emitted. -Note that `fd` should be blocking; non-blocking `fd`s should be passed to -[`net.Socket`][]. +the specified file descriptor. This means that no `'open'` event will be +emitted. Note that `fd` should be blocking; non-blocking `fd`s should be passed +to [`net.Socket`][]. If `autoClose` is false, then the file descriptor won't be closed, even if there's an error. It is your responsibility to close it and make sure @@ -550,7 +556,8 @@ Returns a new [`WriteStream`][] object. (See [Writable Stream][]). `options` may also include a `start` option to allow writing data at some position past the beginning of the file. Modifying a file rather than replacing it may require a `flags` mode of `r+` rather than the -default mode `w`. The `defaultEncoding` can be any one of those accepted by [`Buffer`][]. +default mode `w`. The `defaultEncoding` can be any one of those accepted by +[`Buffer`][]. If `autoClose` is set to true (default behavior) on `error` or `end` the file descriptor will be closed automatically. If `autoClose` is false, @@ -597,7 +604,8 @@ added: v0.1.21 deprecated: v1.0.0 --> - Stability: 0 - Deprecated: Use [`fs.statSync()`][] or [`fs.accessSync()`][] instead. + Stability: 0 - Deprecated: Use [`fs.statSync()`][] or [`fs.accessSync()`][] + instead. * `path` {String | Buffer} @@ -991,7 +999,7 @@ to a non-existent file. The exclusive flag may or may not work with network file systems. `flags` can also be a number as documented by open(2); commonly used constants -are available from `require('constants')`. On Windows, flags are translated to +are available from `fs.constants`. On Windows, flags are translated to their equivalent ones where applicable, e.g. `O_WRONLY` to `FILE_GENERIC_WRITE`, or `O_EXCL|O_CREAT` to `CREATE_NEW`, as accepted by CreateFileW. @@ -1294,11 +1302,11 @@ added: v0.1.31 * `callback` {Function} Asynchronous symlink(2). No arguments other than a possible exception are given -to the completion callback. -The `type` argument can be set to `'dir'`, `'file'`, or `'junction'` (default -is `'file'`) and is only available on Windows (ignored on other platforms). -Note that Windows junction points require the destination path to be absolute. When using -`'junction'`, the `target` argument will automatically be normalized to absolute path. +to the completion callback. The `type` argument can be set to `'dir'`, +`'file'`, or `'junction'` (default is `'file'`) and is only available on +Windows (ignored on other platforms). Note that Windows junction points require +the destination path to be absolute. When using `'junction'`, the `target` +argument will automatically be normalized to absolute path. Here is an example below: @@ -1543,9 +1551,9 @@ _Note: when an `fs.watchFile` operation results in an `ENOENT` error, it will of zero. If the file is created later on, the listener will be called again, with the latest stat objects. This is a change in functionality since v0.10._ -_Note: [`fs.watch()`][] is more efficient than `fs.watchFile` and `fs.unwatchFile`. -`fs.watch` should be used instead of `fs.watchFile` and `fs.unwatchFile` -when possible._ +_Note: [`fs.watch()`][] is more efficient than `fs.watchFile` and +`fs.unwatchFile`. `fs.watch` should be used instead of `fs.watchFile` and +`fs.unwatchFile` when possible._ ## fs.write(fd, buffer, offset, length[, position], callback) <!-- YAML @@ -1693,6 +1701,226 @@ added: v0.11.5 Synchronous versions of [`fs.write()`][]. Returns the number of bytes written. +## FS Constants + +The following constants are exported by `fs.constants`. **Note:** Not every +constant will be available on every operating system. + +### File Access Constants + +The following constants are meant for use with [`fs.access()`][]. + +<table> + <tr> + <th>Constant</th> + <th>Description</th> + </tr> + <tr> + <td><code>F_OK</code></td> + <td>Flag indicating that the file is visible to the calling process.</td> + </tr> + <tr> + <td><code>R_OK</code></td> + <td>Flag indicating that the file can be read by the calling process.</td> + </tr> + <tr> + <td><code>W_OK</code></td> + <td>Flag indicating that the file can be written by the calling + process.</td> + </tr> + <tr> + <td><code>X_OK</code></td> + <td>Flag indicating that the file can be executed by the calling + process.</td> + </tr> +</table> + +### File Open Constants + +The following constants are meant for use with `fs.open()`. + +<table> + <tr> + <th>Constant</th> + <th>Description</th> + </tr> + <tr> + <td><code>O_RDONLY</code></td> + <td>Flag indicating to open a file for read-only access.</td> + </tr> + <tr> + <td><code>O_WRONLY</code></td> + <td>Flag indicating to open a file for write-only access.</td> + </tr> + <tr> + <td><code>O_RDWR</code></td> + <td>Flag indicating to open a file for read-write access.</td> + </tr> + <tr> + <td><code>O_CREAT</code></td> + <td>Flag indicating to create the file if it does not already exist.</td> + </tr> + <tr> + <td><code>O_EXCL</code></td> + <td>Flag indicating that opening a file should fail if the + <code>O_CREAT</code> flag is set and the file already exists.</td> + </tr> + <tr> + <td><code>O_NOCTTY</code></td> + <td>Flag indicating that if path identifies a terminal device, opening the + path shall not cause that terminal to become the controlling terminal for + the process (if the process does not already have one).</td> + </tr> + <tr> + <td><code>O_TRUNC</code></td> + <td>Flag indicating that if the file exists and is a regular file, and the + file is opened successfully for write access, its length shall be truncated + to zero.</td> + </tr> + <tr> + <td><code>O_APPEND</code></td> + <td>Flag indicating that data will be appended to the end of the file.</td> + </tr> + <tr> + <td><code>O_DIRECTORY</code></td> + <td>Flag indicating that the open should fail if the path is not a + directory.</td> + </tr> + <tr> + <td><code>O_NOATIME</code></td> + <td>Flag indicating reading accesses to the file system will no longer + result in an update to the `atime` information associated with the file. + This flag is available on Linux operating systems only.</td> + </tr> + <tr> + <td><code>O_NOFOLLOW</code></td> + <td>Flag indicating that the open should fail if the path is a symbolic + link.</td> + </tr> + <tr> + <td><code>O_SYNC</code></td> + <td>Flag indicating that the file is opened for synchronous I/O.</td> + </tr> + <tr> + <td><code>O_SYMLINK</code></td> + <td>Flag indicating to open the symbolic link itself rather than the + resource it is pointing to.</td> + </tr> + <tr> + <td><code>O_DIRECT</code></td> + <td>When set, an attempt will be made to minimize caching effects of file + I/O.</td> + </tr> + <tr> + <td><code>O_NONBLOCK</code></td> + <td>Flag indicating to open the file in nonblocking mode when possible.</td> + </tr> +</table> + +### File Type Constants + +The following constants are meant for use with the [`fs.Stats`][] object's +`mode` property for determining a file's type. + +<table> + <tr> + <th>Constant</th> + <th>Description</th> + </tr> + <tr> + <td><code>S_IFMT</code></td> + <td>Bit mask used to extract the file type code.</td> + </tr> + <tr> + <td><code>S_IFREG</code></td> + <td>File type constant for a regular file.</td> + </tr> + <tr> + <td><code>S_IFDIR</code></td> + <td>File type constant for a directory.</td> + </tr> + <tr> + <td><code>S_IFCHR</code></td> + <td>File type constant for a character-oriented device file.</td> + </tr> + <tr> + <td><code>S_IFBLK</code></td> + <td>File type constant for a block-oriented device file.</td> + </tr> + <tr> + <td><code>S_IFIFO</code></td> + <td>File type constant for a FIFO/pipe.</td> + </tr> + <tr> + <td><code>S_IFLNK</code></td> + <td>File type constant for a symbolic link.</td> + </tr> + <tr> + <td><code>S_IFSOCK</code></td> + <td>File type constant for a socket.</td> + </tr> +</table> + +### File Mode Constants + +The following constants are meant for use with the [`fs.Stats`][] object's +`mode` property for determining the access permissions for a file. + +<table> + <tr> + <th>Constant</th> + <th>Description</th> + </tr> + <tr> + <td><code>S_IRWXU</code></td> + <td>File mode indicating readable, writable and executable by owner.</td> + </tr> + <tr> + <td><code>S_IRUSR</code></td> + <td>File mode indicating readable by owner.</td> + </tr> + <tr> + <td><code>S_IWUSR</code></td> + <td>File mode indicating writable by owner.</td> + </tr> + <tr> + <td><code>S_IXUSR</code></td> + <td>File mode indicating executable by owner.</td> + </tr> + <tr> + <td><code>S_IRWXG</code></td> + <td>File mode indicating readable, writable and executable by group.</td> + </tr> + <tr> + <td><code>S_IRGRP</code></td> + <td>File mode indicating readable by group.</td> + </tr> + <tr> + <td><code>S_IWGRP</code></td> + <td>File mode indicating writable by group.</td> + </tr> + <tr> + <td><code>S_IXGRP</code></td> + <td>File mode indicating executable by group.</td> + </tr> + <tr> + <td><code>S_IRWXO</code></td> + <td>File mode indicating readable, writable and executable by others.</td> + </tr> + <tr> + <td><code>S_IROTH</code></td> + <td>File mode indicating readable by others.</td> + </tr> + <tr> + <td><code>S_IWOTH</code></td> + <td>File mode indicating writable by others.</td> + </tr> + <tr> + <td><code>S_IXOTH</code></td> + <td>File mode indicating executable by others.</td> + </tr> +</table> + [`Buffer.byteLength`]: buffer.html#buffer_class_method_buffer_bytelength_string_encoding [`Buffer`]: buffer.html#buffer_buffer [Caveats]: #fs_caveats @@ -1726,9 +1954,12 @@ Synchronous versions of [`fs.write()`][]. Returns the number of bytes written. [Writable Stream]: stream.html#stream_class_stream_writable [inode]: http://www.linux.org/threads/intro-to-inodes.4130 [FS Constants]: #fs_fs_constants +<<<<<<< HEAD [`inotify`]: http://man7.org/linux/man-pages/man7/inotify.7.html [`kqueue`]: https://www.freebsd.org/cgi/man.cgi?kqueue [`FSEvents`]: https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/FSEvents_ProgGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40005289-CH1-SW1 [`event ports`]: http://illumos.org/man/port_create [`ReadDirectoryChangesW`]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365465%28v=vs.85%29.aspx [`AHAFS`]: https://www.ibm.com/developerworks/aix/library/au-aix_event_infrastructure/ +======= +>>>>>>> dcccbfd... src: refactor require('constants') diff --git a/doc/api/os.md b/doc/api/os.md index 7e92f2bf434e37..59ed110666d203 100644 --- a/doc/api/os.md +++ b/doc/api/os.md @@ -22,6 +22,12 @@ added: v0.5.0 Returns the operating system CPU architecture. Possible values are `'x64'`, `'arm'` and `'ia32'`. Returns the value of [`process.arch`][]. +## os.constants + +Returns an object containing commonly used operating system specific constants +for error codes, process signals, and so on. The specific constants currently +defined are described in [OS Constants][]. + ## os.cpus() <!-- YAML added: v0.3.3 @@ -246,5 +252,769 @@ operating system. This differs from the result of `os.homedir()`, which queries several environment variables for the home directory before falling back to the operating system response. +## OS Constants + +The following constants are exported by `os.constants`. **Note:** Not all +constants will be available on every operating system. + +### Signal Constants + +The following signal constants are exported by `os.constants.signals`: + +<table> + <tr> + <th>Constant</th> + <th>Description</th> + </tr> + <tr> + <td><code>SIGHUP</code></td> + <td>Sent to indicate when a controlling terminal is closed or a parent + process exits.</td> + </tr> + <tr> + <td><code>SIGINT</code></td> + <td>Sent to indicate when a user wishes to interrupt a process + (`(Ctrl+C)`).</td> + </tr> + <tr> + <td><code>SIGQUIT</code></td> + <td>Sent to indicate when a user wishes to terminate a process and perform a + core dump.</td> + </tr> + <tr> + <td><code>SIGILL</code></td> + <td>Sent to a process to notify that it has attempted to perform an illegal, + malformed, unknown or privileged instruction.</td> + </tr> + <tr> + <td><code>SIGTRAP</code></td> + <td>Sent to a proces when an exception has occurred.</td> + </tr> + <tr> + <td><code>SIGABRT</code></td> + <td>Sent to a process to request that it abort.</td> + </tr> + <tr> + <td><code>SIGIOT</code></td> + <td>Synonym for <code>SIGABRT</code></td> + </tr> + <tr> + <td><code>SIGBUS</code></td> + <td>Sent to a process to notify that it has caused a bus error.</td> + </tr> + <tr> + <td><code>SIGFPE</code></td> + <td>Sent to a process to notify that it has performed an illegal arithmetic + operation.</td> + </tr> + <tr> + <td><code>SIGKILL</code></td> + <td>Sent to a process to terminate it immediately.</td> + </tr> + <tr> + <td><code>SIGUSR1</code> <code>SIGUSR2</code></td> + <td>Sent to a process to identify user-defined conditions.</td> + </tr> + <tr> + <td><code>SIGSEGV</code></td> + <td>Sent to a process to notify of a segmentation fault.</td> + </tr> + <tr> + <td><code>SIGPIPE</code></td> + <td>Sent to a process when it has attempted to write to a disconnected + pipe.</td> + </tr> + <tr> + <td><code>SIGALRM</code></td> + <td>Sent to a process when a system timer elapses.</td> + </tr> + <tr> + <td><code>SIGTERM</code></td> + <td>Sent to a process to request termination.</td> + </tr> + <tr> + <td><code>SIGCHLD</code></td> + <td>Sent to a process when a child process terminates.</td> + </tr> + <tr> + <td><code>SIGSTKFLT</code></td> + <td>Sent to a process to indicate a stack fault on a coprocessor.</td> + </tr> + <tr> + <td><code>SIGCONT</code></td> + <td>Sent to instruct the operating system to continue a paused process.</td> + </tr> + <tr> + <td><code>SIGSTOP</code></td> + <td>Sent to instruct the operating system to halt a process.</td> + </tr> + <tr> + <td><code>SIGTSTP</code></td> + <td>Sent to a process to request it to stop.</td> + </tr> + <tr> + <td><code>SIGBREAK</code></td> + <td>Sent to indicate when a user wishes to interrupt a process.</td> + </tr> + <tr> + <td><code>SIGTTIN</code></td> + <td>Sent to a process when it reads from the TTY while in the + background.</td> + </tr> + <tr> + <td><code>SIGTTOU</code></td> + <td>Sent to a process when it writes to the TTY while in the + background.</td> + </tr> + <tr> + <td><code>SIGURG</code></td> + <td>Sent to a process when a socket has urgent data to read.</td> + </tr> + <tr> + <td><code>SIGXCPU</code></td> + <td>Sent to a process when it has exceeded its limit on CPU usage.</td> + </tr> + <tr> + <td><code>SIGXFSZ</code></td> + <td>Sent to a process when it grows a file larger than the maximum + allowed.</td> + </tr> + <tr> + <td><code>SIGVTALRM</code></td> + <td>Sent to a process when a virtual timer has elapsed.</td> + </tr> + <tr> + <td><code>SIGPROF</code></td> + <td>Sent to a process when a system timer has elapsed.</td> + </tr> + <tr> + <td><code>SIGWINCH</code></td> + <td>Sent to a process when the controlling terminal has changed its + size.</td> + </tr> + <tr> + <td><code>SIGIO</code></td> + <td>Sent to a process when I/O is available.</td> + </tr> + <tr> + <td><code>SIGPOLL</code></td> + <td>Synonym for <code>SIGIO</code></td> + </tr> + <tr> + <td><code>SIGLOST</code></td> + <td>Sent to a process when a file lock has been lost.</td> + </tr> + <tr> + <td><code>SIGPWR</code></td> + <td>Sent to a process to notify of a power failure.</td> + </tr> + <tr> + <td><code>SIGINFO</code></td> + <td>Synonym for <code>SIGPWR</code></td> + </tr> + <tr> + <td><code>SIGSYS</code></td> + <td>Sent to a process to notify of a bad argument.</td> + </tr> + <tr> + <td><code>SIGUNUSED</code></td> + <td>Synonym for <code>SIGSYS</code></td> + </tr> +</table> + +### Error Constants + +The following error constants are exported by `os.constants.errno`: + +#### POSIX Error Constants + +<table> + <tr> + <th>Constant</th> + <th>Description</th> + </tr> + <tr> + <td><code>E2BIG</code></td> + <td>Indicates that the list of arguments is longer than expected.</td> + </tr> + <tr> + <td><code>EACCES</code></td> + <td>Indicates that the operation did not have sufficient permissions.</td> + </tr> + <tr> + <td><code>EADDRINUSE</code></td> + <td>Indicates that the network address is already in use.</td> + </tr> + <tr> + <td><code>EADDRNOTAVAIL</code></td> + <td>Indicates that the network address is currently unavailable for + use.</td> + </tr> + <tr> + <td><code>EAFNOSUPPORT</code></td> + <td>Indicates that the network address family is not supported.</td> + </tr> + <tr> + <td><code>EAGAIN</code></td> + <td>Indicates that there is currently no data available and to try the + operation again later.</td> + </tr> + <tr> + <td><code>EALREADY</code></td> + <td>Indicates that the socket already has a pending connection in + progress.</td> + </tr> + <tr> + <td><code>EBADF</code></td> + <td>Indicates that a file descriptor is not valid.</td> + </tr> + <tr> + <td><code>EBADMSG</code></td> + <td>Indicates an invalid data message.</td> + </tr> + <tr> + <td><code>EBUSY</code></td> + <td>Indicates that a device or resource is busy.</td> + </tr> + <tr> + <td><code>ECANCELED</code></td> + <td>Indicates that an operation was canceled.</td> + </tr> + <tr> + <td><code>ECHILD</code></td> + <td>Indicates that there are no child processes.</td> + </tr> + <tr> + <td><code>ECONNABORTED</code></td> + <td>Indicates that the network connection has been aborted.</td> + </tr> + <tr> + <td><code>ECONNREFUSED</code></td> + <td>Indicates that the network connection has been refused.</td> + </tr> + <tr> + <td><code>ECONNRESET</code></td> + <td>Indicates that the network connection has been reset.</td> + </tr> + <tr> + <td><code>EDEADLK</code></td> + <td>Indicates that a resource deadlock has been avoided.</td> + </tr> + <tr> + <td><code>EDESTADDRREQ</code></td> + <td>Indicates that a destination address is required.</td> + </tr> + <tr> + <td><code>EDOM</code></td> + <td>Indicates that an argument is out of the domain of the function.</td> + </tr> + <tr> + <td><code>EDQUOT</code></td> + <td>Indicates that the disk quota has been exceeded.</td> + </tr> + <tr> + <td><code>EEXIST</code></td> + <td>Indicates that the file already exists.</td> + </tr> + <tr> + <td><code>EFAULT</code></td> + <td>Indicates an invalid pointer address.</td> + </tr> + <tr> + <td><code>EFBIG</code></td> + <td>Indicates that the file is too large.</td> + </tr> + <tr> + <td><code>EHOSTUNREACH</code></td> + <td>Indicates that the host is unreachable.</td> + </tr> + <tr> + <td><code>EIDRM</code></td> + <td>Indicates that the identifier has been removed.</td> + </tr> + <tr> + <td><code>EILSEQ</code></td> + <td>Indicates an illegal byte sequence.</td> + </tr> + <tr> + <td><code>EINPROGRESS</code></td> + <td>Indicates that an operation is already in progress.</td> + </tr> + <tr> + <td><code>EINTR</code></td> + <td>Indicates that a function call was interrupted.</td> + </tr> + <tr> + <td><code>EINVAL</code></td> + <td>Indicates that an invalid argument was provided.</td> + </tr> + <tr> + <td><code>EIO</code></td> + <td>Indicates an otherwise unspecified I/O error.</td> + </tr> + <tr> + <td><code>EISCONN</code></td> + <td>Indicates that the socket is connected.</td> + </tr> + <tr> + <td><code>EISDIR</code></td> + <td>Indicates that the path is a directory.</td> + </tr> + <tr> + <td><code>ELOOP</code></td> + <td>Indicates too many levels of symbolic links in a path.</td> + </tr> + <tr> + <td><code>EMFILE</code></td> + <td>Indicates that there are too many open files.</td> + </tr> + <tr> + <td><code>EMLINK</code></td> + <td>Indicates that there are too many hard links to a file.</td> + </tr> + <tr> + <td><code>EMSGSIZE</code></td> + <td>Indicates that the provided message is too long.</td> + </tr> + <tr> + <td><code>EMULTIHOP</code></td> + <td>Indicates that a multihop was attempted.</td> + </tr> + <tr> + <td><code>ENAMETOOLONG</code></td> + <td>Indicates that the filename is too long.</td> + </tr> + <tr> + <td><code>ENETDOWN</code></td> + <td>Indicates that the network is down.</td> + </tr> + <tr> + <td><code>ENETRESET</code></td> + <td>Indicates that the connection has been aborted by the network.</td> + </tr> + <tr> + <td><code>ENETUNREACH</code></td> + <td>Indicates that the network is unreachable.</td> + </tr> + <tr> + <td><code>ENFILE</code></td> + <td>Indicates too many open files in the system.</td> + </tr> + <tr> + <td><code>ENOBUFS</code></td> + <td>Indicates that no buffer space is available.</td> + </tr> + <tr> + <td><code>ENODATA</code></td> + <td>Indicates that no message is available on the stream head read + queue.</td> + </tr> + <tr> + <td><code>ENODEV</code></td> + <td>Indicates that there is no such device.</td> + </tr> + <tr> + <td><code>ENOENT</code></td> + <td>Indicates that there is no such file or directory.</td> + </tr> + <tr> + <td><code>ENOEXEC</code></td> + <td>Indicates an exec format error.</td> + </tr> + <tr> + <td><code>ENOLCK</code></td> + <td>Indicates that there are no locks available.</td> + </tr> + <tr> + <td><code>ENOLINK</code></td> + <td>Indications that a link has been severed.</td> + </tr> + <tr> + <td><code>ENOMEM</code></td> + <td>Indicates that there is not enough space.</td> + </tr> + <tr> + <td><code>ENOMSG</code></td> + <td>Indicates that there is no message of the desired type.</td> + </tr> + <tr> + <td><code>ENOPROTOOPT</code></td> + <td>Indicates that a given protocol is not available.</td> + </tr> + <tr> + <td><code>ENOSPC</code></td> + <td>Indicates that there is no space available on the device.</td> + </tr> + <tr> + <td><code>ENOSR</code></td> + <td>Indicates that there are no stream resources available.</td> + </tr> + <tr> + <td><code>ENOSTR</code></td> + <td>Indicates that a given resource is not a stream.</td> + </tr> + <tr> + <td><code>ENOSYS</code></td> + <td>Indicates that a function has not been implemented.</td> + </tr> + <tr> + <td><code>ENOTCONN</code></td> + <td>Indicates that the socket is not connected.</td> + </tr> + <tr> + <td><code>ENOTDIR</code></td> + <td>Indicates that the path is not a directory.</td> + </tr> + <tr> + <td><code>ENOTEMPTY</code></td> + <td>Indicates that the directory is not empty.</td> + </tr> + <tr> + <td><code>ENOTSOCK</code></td> + <td>Indicates that the given item is not a socket.</td> + </tr> + <tr> + <td><code>ENOTSUP</code></td> + <td>Indicates that a given operation is not supported.</td> + </tr> + <tr> + <td><code>ENOTTY</code></td> + <td>Indicates an inappropriate I/O control operation.</td> + </tr> + <tr> + <td><code>ENXIO</code></td> + <td>Indicates no such device or address.</td> + </tr> + <tr> + <td><code>EOPNOTSUPP</code></td> + <td>Indicates that an operation is not supported on the socket. + Note that while `ENOTSUP` and `EOPNOTSUPP` have the same value on Linux, + according to POSIX.1 these error values should be distinct.)</td> + </tr> + <tr> + <td><code>EOVERFLOW</code></td> + <td>Indicates that a value is too large to be stored in a given data + type.</td> + </tr> + <tr> + <td><code>EPERM</code></td> + <td>Indicates that the operation is not permitted.</td> + </tr> + <tr> + <td><code>EPIPE</code></td> + <td>Indicates a broken pipe.</td> + </tr> + <tr> + <td><code>EPROTO</code></td> + <td>Indicates a protocol error.</td> + </tr> + <tr> + <td><code>EPROTONOSUPPORT</code></td> + <td>Indicates that a protocol is not supported.</td> + </tr> + <tr> + <td><code>EPROTOTYPE</code></td> + <td>Indicates the wrong type of protocol for a socket.</td> + </tr> + <tr> + <td><code>ERANGE</code></td> + <td>Indicates that the results are too large.</td> + </tr> + <tr> + <td><code>EROFS</code></td> + <td>Indicates that the file system is read only.</td> + </tr> + <tr> + <td><code>ESPIPE</code></td> + <td>Indicates an invalid seek operation.</td> + </tr> + <tr> + <td><code>ESRCH</code></td> + <td>Indicates that there is no such process.</td> + </tr> + <tr> + <td><code>ESTALE</code></td> + <td>Indicates that the file handle is stale.</td> + </tr> + <tr> + <td><code>ETIME</code></td> + <td>Indicates an expired timer.</td> + </tr> + <tr> + <td><code>ETIMEDOUT</code></td> + <td>Indicates that the connection timed out.</td> + </tr> + <tr> + <td><code>ETXTBSY</code></td> + <td>Indicates that a text file is busy.</td> + </tr> + <tr> + <td><code>EWOULDBLOCK</code></td> + <td>Indicates that the operation would block.</td> + </tr> + <tr> + <td><code>EXDEV</code></td> + <td>Indicates an improper link. + </tr> +</table> + +#### Windows Specific Error Constants + +The following error codes are specific to the Windows operating system: + +<table> + <tr> + <th>Constant</th> + <th>Description</th> + </tr> + <tr> + <td><code>WSAEINTR</code></td> + <td>Indicates an interrupted function call.</td> + </tr> + <tr> + <td><code>WSAEBADF</code></td> + <td>Indicates an invalid file handle.</td> + </tr> + <tr> + <td><code>WSAEACCES</code></td> + <td>Indicates insufficient permissions to complete the operation.</td> + </tr> + <tr> + <td><code>WSAEFAULT</code></td> + <td>Indicates an invalid pointer address.</td> + </tr> + <tr> + <td><code>WSAEINVAL</code></td> + <td>Indicates that an invalid argument was passed.</td> + </tr> + <tr> + <td><code>WSAEMFILE</code></td> + <td>Indicates that there are too many open files.</td> + </tr> + <tr> + <td><code>WSAEWOULDBLOCK</code></td> + <td>Indicates that a resource is temporarily unavailable.</td> + </tr> + <tr> + <td><code>WSAEINPROGRESS</code></td> + <td>Indicates that an operation is currently in progress.</td> + </tr> + <tr> + <td><code>WSAEALREADY</code></td> + <td>Indicates that an operation is already in progress.</td> + </tr> + <tr> + <td><code>WSAENOTSOCK</code></td> + <td>Indicates that the resource is not a socket.</td> + </tr> + <tr> + <td><code>WSAEDESTADDRREQ</code></td> + <td>Indicates that a destination address is required.</td> + </tr> + <tr> + <td><code>WSAEMSGSIZE</code></td> + <td>Indicates that the message size is too long.</td> + </tr> + <tr> + <td><code>WSAEPROTOTYPE</code></td> + <td>Indicates the wrong protocol type for the socket.</td> + </tr> + <tr> + <td><code>WSAENOPROTOOPT</code></td> + <td>Indicates a bad protocol option.</td> + </tr> + <tr> + <td><code>WSAEPROTONOSUPPORT</code></td> + <td>Indicates that the protocol is not supported.</td> + </tr> + <tr> + <td><code>WSAESOCKTNOSUPPORT</code></td> + <td>Indicates that the socket type is not supported.</td> + </tr> + <tr> + <td><code>WSAEOPNOTSUPP</code></td> + <td>Indicates that the operation is not supported.</td> + </tr> + <tr> + <td><code>WSAEPFNOSUPPORT</code></td> + <td>Indicates that the protocol family is not supported.</td> + </tr> + <tr> + <td><code>WSAEAFNOSUPPORT</code></td> + <td>Indicates that the address family is not supported.</td> + </tr> + <tr> + <td><code>WSAEADDRINUSE</code></td> + <td>Indicates that the network address is already in use.</td> + </tr> + <tr> + <td><code>WSAEADDRNOTAVAIL</code></td> + <td>Indicates that the network address is not available.</td> + </tr> + <tr> + <td><code>WSAENETDOWN</code></td> + <td>Indicates that the network is down.</td> + </tr> + <tr> + <td><code>WSAENETUNREACH</code></td> + <td>Indicates that the network is unreachable.</td> + </tr> + <tr> + <td><code>WSAENETRESET</code></td> + <td>Indicates that the network connection has been reset.</td> + </tr> + <tr> + <td><code>WSAECONNABORTED</code></td> + <td>Indicates that the connection has been aborted.</td> + </tr> + <tr> + <td><code>WSAECONNRESET</code></td> + <td>Indicates that the connection has been reset by the peer.</td> + </tr> + <tr> + <td><code>WSAENOBUFS</code></td> + <td>Indicates that there is no buffer space available.</td> + </tr> + <tr> + <td><code>WSAEISCONN</code></td> + <td>Indicates that the socket is already connected.</td> + </tr> + <tr> + <td><code>WSAENOTCONN</code></td> + <td>Indicates that the socket is not connected.</td> + </tr> + <tr> + <td><code>WSAESHUTDOWN</code></td> + <td>Indicates that data cannot be sent after the socket has been + shutdown.</td> + </tr> + <tr> + <td><code>WSAETOOMANYREFS</code></td> + <td>Indicates that there are too many references.</td> + </tr> + <tr> + <td><code>WSAETIMEDOUT</code></td> + <td>Indicates that the connection has timed out.</td> + </tr> + <tr> + <td><code>WSAECONNREFUSED</code></td> + <td>Indicates that the connection has been refused.</td> + </tr> + <tr> + <td><code>WSAELOOP</code></td> + <td>Indicates that a name cannot be translated.</td> + </tr> + <tr> + <td><code>WSAENAMETOOLONG</code></td> + <td>Indicates that a name was too long.</td> + </tr> + <tr> + <td><code>WSAEHOSTDOWN</code></td> + <td>Indicates that a network host is down.</td> + </tr> + <tr> + <td><code>WSAEHOSTUNREACH</code></td> + <td>Indicates that there is no route to a network host.</td> + </tr> + <tr> + <td><code>WSAENOTEMPTY</code></td> + <td>Indicates that the directory is not empty.</td> + </tr> + <tr> + <td><code>WSAEPROCLIM</code></td> + <td>Indicates that there are too many processes.</td> + </tr> + <tr> + <td><code>WSAEUSERS</code></td> + <td>Indicates that the user quota has been exceeded.</td> + </tr> + <tr> + <td><code>WSAEDQUOT</code></td> + <td>Indicates that the disk quota has been exceeded.</td> + </tr> + <tr> + <td><code>WSAESTALE</code></td> + <td>Indicates a stale file handle reference.</td> + </tr> + <tr> + <td><code>WSAEREMOTE</code></td> + <td>Indicates that the item is remote.</td> + </tr> + <tr> + <td><code>WSASYSNOTREADY</code></td> + <td>Indicates that the network subsystem is not ready.</td> + </tr> + <tr> + <td><code>WSAVERNOTSUPPORTED</code></td> + <td>Indicates that the winsock.dll version is out of range.</td> + </tr> + <tr> + <td><code>WSANOTINITIALISED</code></td> + <td>Indicates that successful WSAStartup has not yet been performed.</td> + </tr> + <tr> + <td><code>WSAEDISCON</code></td> + <td>Indicates that a graceful shutdown is in progress.</td> + </tr> + <tr> + <td><code>WSAENOMORE</code></td> + <td>Indicates that there are no more results.</td> + </tr> + <tr> + <td><code>WSAECANCELLED</code></td> + <td>Indicates that an operation has been canceled.</td> + </tr> + <tr> + <td><code>WSAEINVALIDPROCTABLE</code></td> + <td>Indicates that the procedure call table is invalid.</td> + </tr> + <tr> + <td><code>WSAEINVALIDPROVIDER</code></td> + <td>Indicates an invalid service provider.</td> + </tr> + <tr> + <td><code>WSAEPROVIDERFAILEDINIT</code></td> + <td>Indicates that the service provider failed to initialized.</td> + </tr> + <tr> + <td><code>WSASYSCALLFAILURE</code></td> + <td>Indicates a system call failure.</td> + </tr> + <tr> + <td><code>WSASERVICE_NOT_FOUND</code></td> + <td>Indicates that a service was not found.</td> + </tr> + <tr> + <td><code>WSATYPE_NOT_FOUND</code></td> + <td>Indicates that a class type was not found.</td> + </tr> + <tr> + <td><code>WSA_E_NO_MORE</code></td> + <td>Indicates that there are no more results.</td> + </tr> + <tr> + <td><code>WSA_E_CANCELLED</code></td> + <td>Indicates that the call was canceled.</td> + </tr> + <tr> + <td><code>WSAEREFUSED</code></td> + <td>Indicates that a database query was refused.</td> + </tr> +</table> + +### libuv Constants + +<table> + <tr> + <th>Constant</th> + <th>Description</th> + </tr> + <tr> + <td><code>UV_UDP_REUSEADDR</code></td> + <td></td> + </tr> +</table> + [`process.arch`]: process.html#process_process_arch [`process.platform`]: process.html#process_process_platform +[OS Constants]: #os_os_constants diff --git a/lib/_tls_common.js b/lib/_tls_common.js index 382ba5e8f479ef..d75b9ffe28188d 100644 --- a/lib/_tls_common.js +++ b/lib/_tls_common.js @@ -1,9 +1,11 @@ 'use strict'; const internalUtil = require('internal/util'); -const constants = require('constants'); const tls = require('tls'); +const SSL_OP_CIPHER_SERVER_PREFERENCE = + process.binding('constants').crypto.SSL_OP_CIPHER_SERVER_PREFERENCE; + // Lazily loaded var crypto = null; @@ -38,7 +40,7 @@ exports.createSecureContext = function createSecureContext(options, context) { var secureOptions = options.secureOptions; if (options.honorCipherOrder) - secureOptions |= constants.SSL_OP_CIPHER_SERVER_PREFERENCE; + secureOptions |= SSL_OP_CIPHER_SERVER_PREFERENCE; var c = new SecureContext(options.secureProtocol, secureOptions, context); diff --git a/lib/child_process.js b/lib/child_process.js index 4d41989ce610ec..5eee5228f040d3 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -3,7 +3,7 @@ const util = require('util'); const internalUtil = require('internal/util'); const debug = util.debuglog('child_process'); -const constants = require('constants'); +const constants = process.binding('constants').os.signals; const uv = process.binding('uv'); const spawn_sync = process.binding('spawn_sync'); diff --git a/lib/constants.js b/lib/constants.js index 9fa017904ceb45..deebf90513da46 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -1,3 +1,11 @@ 'use strict'; -module.exports = process.binding('constants'); +// This module is soft-deprecated. Users should be directed towards using +// the specific constants exposed by the individual modules on which they +// are most relevant. +const constants = process.binding('constants'); +Object.assign(exports, + constants.os.errors, + constants.os.signals, + constants.fs, + constants.crypto); diff --git a/lib/crypto.js b/lib/crypto.js index 0f5652c9c946ef..4bce3717015340 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -8,6 +8,7 @@ internalUtil.assertCrypto(exports); exports.DEFAULT_ENCODING = 'buffer'; +const constants = process.binding('constants').crypto; const binding = process.binding('crypto'); const randomBytes = binding.randomBytes; const getCiphers = binding.getCiphers; @@ -17,13 +18,18 @@ const getFipsCrypto = binding.getFipsCrypto; const setFipsCrypto = binding.setFipsCrypto; const Buffer = require('buffer').Buffer; -const constants = require('constants'); const stream = require('stream'); const util = require('util'); const LazyTransform = require('internal/streams/lazy_transform'); const DH_GENERATOR = 2; +Object.defineProperty(exports, 'constants', { + configurable: false, + enumerable: true, + value: constants +}); + // This is here because many functions accepted binary strings without // any explicit encoding in older versions of node, and we don't want // to break them unnecessarily. diff --git a/lib/dgram.js b/lib/dgram.js index 19ba38ce648529..e6cc169dc644fe 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -4,7 +4,7 @@ const assert = require('assert'); const Buffer = require('buffer').Buffer; const util = require('util'); const EventEmitter = require('events'); -const constants = require('constants'); +const UV_UDP_REUSEADDR = process.binding('constants').os.UV_UDP_REUSEADDR; const UDP = process.binding('udp_wrap').UDP; const SendWrap = process.binding('udp_wrap').SendWrap; @@ -186,7 +186,7 @@ Socket.prototype.bind = function(port_ /*, address, callback*/) { var flags = 0; if (self._reuseAddr) - flags |= constants.UV_UDP_REUSEADDR; + flags |= UV_UDP_REUSEADDR; if (cluster.isWorker && !exclusive) { function onHandle(err, handle) { diff --git a/lib/fs.js b/lib/fs.js index 58d3f8b18f1a59..39bb3777bf9035 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -3,11 +3,11 @@ 'use strict'; +const constants = process.binding('constants').fs; const util = require('util'); const pathModule = require('path'); const binding = process.binding('fs'); -const constants = require('constants'); const fs = exports; const Buffer = require('buffer').Buffer; const Stream = require('stream').Stream; @@ -15,6 +15,12 @@ const EventEmitter = require('events'); const FSReqWrap = binding.FSReqWrap; const FSEvent = process.binding('fs_event_wrap').FSEvent; +Object.defineProperty(exports, 'constants', { + configurable: false, + enumerable: true, + value: constants +}); + const Readable = Stream.Readable; const Writable = Stream.Writable; diff --git a/lib/internal/child_process.js b/lib/internal/child_process.js index 1ebfcd4c0cd97f..789c29ef777042 100644 --- a/lib/internal/child_process.js +++ b/lib/internal/child_process.js @@ -6,7 +6,7 @@ const EventEmitter = require('events'); const net = require('net'); const dgram = require('dgram'); const util = require('util'); -const constants = require('constants'); +const constants = process.binding('constants').os.signals; const assert = require('assert'); const Process = process.binding('process_wrap').Process; diff --git a/lib/internal/process.js b/lib/internal/process.js index c435c2e8712a03..61efa954b64447 100644 --- a/lib/internal/process.js +++ b/lib/internal/process.js @@ -4,7 +4,7 @@ var _lazyConstants = null; function lazyConstants() { if (!_lazyConstants) { - _lazyConstants = process.binding('constants'); + _lazyConstants = process.binding('constants').os.signals; } return _lazyConstants; } @@ -160,8 +160,7 @@ function setupKillAndExit() { err = process._kill(pid, 0); } else { sig = sig || 'SIGTERM'; - if (lazyConstants()[sig] && - sig.slice(0, 3) === 'SIG') { + if (lazyConstants()[sig]) { err = process._kill(pid, lazyConstants()[sig]); } else { throw new Error(`Unknown signal: ${sig}`); @@ -185,7 +184,6 @@ function setupSignalHandlers() { function isSignal(event) { return typeof event === 'string' && - event.slice(0, 3) === 'SIG' && lazyConstants().hasOwnProperty(event); } diff --git a/lib/os.js b/lib/os.js index d065449da84898..206e9941625965 100644 --- a/lib/os.js +++ b/lib/os.js @@ -1,6 +1,7 @@ 'use strict'; const binding = process.binding('os'); +const constants = process.binding('constants').os; const internalUtil = require('internal/util'); const isWindows = process.platform === 'win32'; @@ -16,6 +17,11 @@ exports.networkInterfaces = binding.getInterfaceAddresses; exports.homedir = binding.getHomeDirectory; exports.userInfo = binding.getUserInfo; +Object.defineProperty(exports, 'constants', { + configurable: false, + enumerable: true, + value: constants +}); exports.arch = function() { return process.arch; diff --git a/lib/tls.js b/lib/tls.js index 1d119483a24dd4..80ea0d76977ecc 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -6,7 +6,6 @@ const net = require('net'); const url = require('url'); const binding = process.binding('crypto'); const Buffer = require('buffer').Buffer; -const constants = require('constants'); // Allow {CLIENT_RENEG_LIMIT} client-initiated session renegotiations // every {CLIENT_RENEG_WINDOW} seconds. An error event is emitted if more @@ -17,7 +16,8 @@ exports.CLIENT_RENEG_WINDOW = 600; exports.SLAB_BUFFER_SIZE = 10 * 1024 * 1024; -exports.DEFAULT_CIPHERS = constants.defaultCipherList; +exports.DEFAULT_CIPHERS = + process.binding('constants').crypto.defaultCipherList; exports.DEFAULT_ECDH_CURVE = 'prime256v1'; diff --git a/src/node.cc b/src/node.cc index 107a1eb90fc4b8..34e49e98d4dedf 100644 --- a/src/node.cc +++ b/src/node.cc @@ -2509,7 +2509,7 @@ static void Binding(const FunctionCallbackInfo<Value>& args) { cache->Set(module, exports); } else if (!strcmp(*module_v, "constants")) { exports = Object::New(env->isolate()); - DefineConstants(exports); + DefineConstants(env->isolate(), exports); cache->Set(module, exports); } else if (!strcmp(*module_v, "natives")) { exports = Object::New(env->isolate()); diff --git a/src/node_constants.cc b/src/node_constants.cc index 928502f6affca9..2e6be8df37c345 100644 --- a/src/node_constants.cc +++ b/src/node_constants.cc @@ -1,4 +1,6 @@ #include "node_constants.h" +#include "env.h" +#include "env-inl.h" #include "uv.h" @@ -1140,14 +1142,26 @@ void DefineCryptoConstants(Local<Object> target) { #endif } -void DefineConstants(Local<Object> target) { - DefineErrnoConstants(target); - DefineWindowsErrorConstants(target); - DefineSignalConstants(target); - DefineOpenSSLConstants(target); - DefineSystemConstants(target); - DefineUVConstants(target); - DefineCryptoConstants(target); +void DefineConstants(v8::Isolate* isolate, Local<Object> target) { + Local<Object> os_constants = Object::New(isolate); + Local<Object> err_constants = Object::New(isolate); + Local<Object> sig_constants = Object::New(isolate); + Local<Object> fs_constants = Object::New(isolate); + Local<Object> crypto_constants = Object::New(isolate); + + DefineErrnoConstants(err_constants); + DefineWindowsErrorConstants(err_constants); + DefineSignalConstants(sig_constants); + DefineUVConstants(os_constants); + DefineSystemConstants(fs_constants); + DefineOpenSSLConstants(crypto_constants); + DefineCryptoConstants(crypto_constants); + + os_constants->Set(OneByteString(isolate, "errno"), err_constants); + os_constants->Set(OneByteString(isolate, "signals"), sig_constants); + target->Set(OneByteString(isolate, "os"), os_constants); + target->Set(OneByteString(isolate, "fs"), fs_constants); + target->Set(OneByteString(isolate, "crypto"), crypto_constants); } } // namespace node diff --git a/src/node_constants.h b/src/node_constants.h index 85fa83a58575ce..7ba6ec3bd1b015 100644 --- a/src/node_constants.h +++ b/src/node_constants.h @@ -36,7 +36,7 @@ namespace node { extern const char* default_cipher_list; #endif -void DefineConstants(v8::Local<v8::Object> target); +void DefineConstants(v8::Isolate* isolate, v8::Local<v8::Object> target); } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/test/parallel/test-constants.js b/test/parallel/test-constants.js new file mode 100644 index 00000000000000..ea0b93ba7fab6a --- /dev/null +++ b/test/parallel/test-constants.js @@ -0,0 +1,12 @@ +'use strict'; + +require('../common'); +const constants = process.binding('constants'); +const assert = require('assert'); + +assert.ok(constants); +assert.ok(constants.os); +assert.ok(constants.os.signals); +assert.ok(constants.os.errno); +assert.ok(constants.fs); +assert.ok(constants.crypto); diff --git a/test/parallel/test-crypto-binary-default.js b/test/parallel/test-crypto-binary-default.js index 1297fbe1bff601..74447ce69d2ee9 100644 --- a/test/parallel/test-crypto-binary-default.js +++ b/test/parallel/test-crypto-binary-default.js @@ -5,7 +5,6 @@ var common = require('../common'); var assert = require('assert'); -var constants = require('constants'); if (!common.hasCrypto) { common.skip('missing crypto'); @@ -13,6 +12,7 @@ if (!common.hasCrypto) { } var crypto = require('crypto'); var tls = require('tls'); +const DH_NOT_SUITABLE_GENERATOR = crypto.constants.DH_NOT_SUITABLE_GENERATOR; crypto.DEFAULT_ENCODING = 'binary'; @@ -556,7 +556,7 @@ var p = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' + '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF'; var d = crypto.createDiffieHellman(p, 'hex'); -assert.equal(d.verifyError, constants.DH_NOT_SUITABLE_GENERATOR); +assert.equal(d.verifyError, DH_NOT_SUITABLE_GENERATOR); // Test RSA key signing/verification var rsaSign = crypto.createSign('RSA-SHA1'); diff --git a/test/parallel/test-crypto-dh.js b/test/parallel/test-crypto-dh.js index 6e33c538ed98da..e9fbd30a2effc8 100644 --- a/test/parallel/test-crypto-dh.js +++ b/test/parallel/test-crypto-dh.js @@ -1,13 +1,13 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); -const constants = require('constants'); if (!common.hasCrypto) { common.skip('missing crypto'); return; } const crypto = require('crypto'); +const DH_NOT_SUITABLE_GENERATOR = crypto.constants.DH_NOT_SUITABLE_GENERATOR; // Test Diffie-Hellman with two parties sharing a secret, // using various encodings as we go along @@ -79,8 +79,8 @@ bob.generateKeys(); var aSecret = alice.computeSecret(bob.getPublicKey()).toString('hex'); var bSecret = bob.computeSecret(alice.getPublicKey()).toString('hex'); assert.equal(aSecret, bSecret); -assert.equal(alice.verifyError, constants.DH_NOT_SUITABLE_GENERATOR); -assert.equal(bob.verifyError, constants.DH_NOT_SUITABLE_GENERATOR); +assert.equal(alice.verifyError, DH_NOT_SUITABLE_GENERATOR); +assert.equal(bob.verifyError, DH_NOT_SUITABLE_GENERATOR); /* Ensure specific generator (buffer) works as expected. * The values below (modp2/modp2buf) are for a 1024 bits long prime from @@ -107,8 +107,8 @@ exmodp2.generateKeys(); var modp2Secret = modp2.computeSecret(exmodp2.getPublicKey()).toString('hex'); var exmodp2Secret = exmodp2.computeSecret(modp2.getPublicKey()).toString('hex'); assert.equal(modp2Secret, exmodp2Secret); -assert.equal(modp2.verifyError, constants.DH_NOT_SUITABLE_GENERATOR); -assert.equal(exmodp2.verifyError, constants.DH_NOT_SUITABLE_GENERATOR); +assert.equal(modp2.verifyError, DH_NOT_SUITABLE_GENERATOR); +assert.equal(exmodp2.verifyError, DH_NOT_SUITABLE_GENERATOR); // Ensure specific generator (string with encoding) works as expected. @@ -118,7 +118,7 @@ modp2Secret = modp2.computeSecret(exmodp2_2.getPublicKey()).toString('hex'); var exmodp2_2Secret = exmodp2_2.computeSecret(modp2.getPublicKey()) .toString('hex'); assert.equal(modp2Secret, exmodp2_2Secret); -assert.equal(exmodp2_2.verifyError, constants.DH_NOT_SUITABLE_GENERATOR); +assert.equal(exmodp2_2.verifyError, DH_NOT_SUITABLE_GENERATOR); // Ensure specific generator (string without encoding) works as expected. @@ -128,7 +128,7 @@ modp2Secret = modp2.computeSecret(exmodp2_3.getPublicKey()).toString('hex'); var exmodp2_3Secret = exmodp2_3.computeSecret(modp2.getPublicKey()) .toString('hex'); assert.equal(modp2Secret, exmodp2_3Secret); -assert.equal(exmodp2_3.verifyError, constants.DH_NOT_SUITABLE_GENERATOR); +assert.equal(exmodp2_3.verifyError, DH_NOT_SUITABLE_GENERATOR); // Ensure specific generator (numeric) works as expected. @@ -138,7 +138,7 @@ modp2Secret = modp2.computeSecret(exmodp2_4.getPublicKey()).toString('hex'); var exmodp2_4Secret = exmodp2_4.computeSecret(modp2.getPublicKey()) .toString('hex'); assert.equal(modp2Secret, exmodp2_4Secret); -assert.equal(exmodp2_4.verifyError, constants.DH_NOT_SUITABLE_GENERATOR); +assert.equal(exmodp2_4.verifyError, DH_NOT_SUITABLE_GENERATOR); var p = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' + @@ -146,7 +146,7 @@ var p = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' + '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF'; var bad_dh = crypto.createDiffieHellman(p, 'hex'); -assert.equal(bad_dh.verifyError, constants.DH_NOT_SUITABLE_GENERATOR); +assert.equal(bad_dh.verifyError, DH_NOT_SUITABLE_GENERATOR); // Test ECDH diff --git a/test/parallel/test-crypto-rsa-dsa.js b/test/parallel/test-crypto-rsa-dsa.js index c4b1dfaa383486..2e29cd723d2677 100644 --- a/test/parallel/test-crypto-rsa-dsa.js +++ b/test/parallel/test-crypto-rsa-dsa.js @@ -2,7 +2,7 @@ var common = require('../common'); var assert = require('assert'); var fs = require('fs'); -var constants = require('constants'); +var constants = require('crypto').constants; if (!common.hasCrypto) { common.skip('missing crypto'); diff --git a/test/parallel/test-fs-open-flags.js b/test/parallel/test-fs-open-flags.js index cf451b4f8d5d95..d212130bc903d1 100644 --- a/test/parallel/test-fs-open-flags.js +++ b/test/parallel/test-fs-open-flags.js @@ -2,16 +2,15 @@ require('../common'); var assert = require('assert'); -var constants = require('constants'); var fs = require('fs'); -var O_APPEND = constants.O_APPEND || 0; -var O_CREAT = constants.O_CREAT || 0; -var O_EXCL = constants.O_EXCL || 0; -var O_RDONLY = constants.O_RDONLY || 0; -var O_RDWR = constants.O_RDWR || 0; -var O_TRUNC = constants.O_TRUNC || 0; -var O_WRONLY = constants.O_WRONLY || 0; +var O_APPEND = fs.constants.O_APPEND || 0; +var O_CREAT = fs.constants.O_CREAT || 0; +var O_EXCL = fs.constants.O_EXCL || 0; +var O_RDONLY = fs.constants.O_RDONLY || 0; +var O_RDWR = fs.constants.O_RDWR || 0; +var O_TRUNC = fs.constants.O_TRUNC || 0; +var O_WRONLY = fs.constants.O_WRONLY || 0; assert.equal(fs._stringToFlags('r'), O_RDONLY); assert.equal(fs._stringToFlags('r+'), O_RDWR); diff --git a/test/parallel/test-fs-open-numeric-flags.js b/test/parallel/test-fs-open-numeric-flags.js index d3bd42227c2a53..1bd9a043927039 100644 --- a/test/parallel/test-fs-open-numeric-flags.js +++ b/test/parallel/test-fs-open-numeric-flags.js @@ -1,7 +1,6 @@ 'use strict'; const common = require('../common'); -const constants = require('constants'); const assert = require('assert'); const fs = require('fs'); const path = require('path'); @@ -11,6 +10,6 @@ common.refreshTmpDir(); // O_WRONLY without O_CREAT shall fail with ENOENT const pathNE = path.join(common.tmpDir, 'file-should-not-exist'); assert.throws( - () => fs.openSync(pathNE, constants.O_WRONLY), + () => fs.openSync(pathNE, fs.constants.O_WRONLY), (e) => e.code === 'ENOENT' ); diff --git a/test/parallel/test-fs-write.js b/test/parallel/test-fs-write.js index 5ddd83e026c095..9cdabe40d3e7c9 100644 --- a/test/parallel/test-fs-write.js +++ b/test/parallel/test-fs-write.js @@ -7,7 +7,7 @@ var fs = require('fs'); var fn = path.join(common.tmpDir, 'write.txt'); var fn2 = path.join(common.tmpDir, 'write2.txt'); var expected = 'ümlaut.'; -var constants = require('constants'); +var constants = fs.constants; var found, found2; common.refreshTmpDir(); @@ -55,4 +55,3 @@ process.on('exit', function() { assert.equal(expected, found); assert.equal(expected, found2); }); - diff --git a/test/parallel/test-https-agent-session-eviction.js b/test/parallel/test-https-agent-session-eviction.js index e5cd073e28eba6..8c30e7d8eda528 100644 --- a/test/parallel/test-https-agent-session-eviction.js +++ b/test/parallel/test-https-agent-session-eviction.js @@ -10,12 +10,12 @@ if (!common.hasCrypto) { const assert = require('assert'); const https = require('https'); const fs = require('fs'); -const constants = require('constants'); +const SSL_OP_NO_TICKET = require('crypto').constants.SSL_OP_NO_TICKET; const options = { key: fs.readFileSync(common.fixturesDir + '/keys/agent1-key.pem'), cert: fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem'), - secureOptions: constants.SSL_OP_NO_TICKET + secureOptions: SSL_OP_NO_TICKET }; // Create TLS1.2 server diff --git a/test/parallel/test-process-constants-noatime.js b/test/parallel/test-process-constants-noatime.js index 0503e67c38d6e8..bf4ed4d395bbcf 100644 --- a/test/parallel/test-process-constants-noatime.js +++ b/test/parallel/test-process-constants-noatime.js @@ -5,8 +5,8 @@ const assert = require('assert'); const constants = process.binding('constants'); if (process.platform === 'linux') { - assert('O_NOATIME' in constants); - assert.strictEqual(constants.O_NOATIME, 0x40000); + assert('O_NOATIME' in constants.fs); + assert.strictEqual(constants.fs.O_NOATIME, 0x40000); } else { - assert(!('O_NOATIME' in constants)); + assert(!('O_NOATIME' in constants.fs)); } diff --git a/test/parallel/test-tls-async-cb-after-socket-end.js b/test/parallel/test-tls-async-cb-after-socket-end.js index 647d4ad1f8dd32..c72deb133c3e35 100644 --- a/test/parallel/test-tls-async-cb-after-socket-end.js +++ b/test/parallel/test-tls-async-cb-after-socket-end.js @@ -4,7 +4,7 @@ var common = require('../common'); var path = require('path'); var fs = require('fs'); -var constants = require('constants'); +const SSL_OP_NO_TICKET = require('crypto').constants.SSL_OP_NO_TICKET; if (!common.hasCrypto) { common.skip('missing crypto'); @@ -14,7 +14,7 @@ if (!common.hasCrypto) { var tls = require('tls'); var options = { - secureOptions: constants.SSL_OP_NO_TICKET, + secureOptions: SSL_OP_NO_TICKET, key: fs.readFileSync(path.join(common.fixturesDir, 'test_key.pem')), cert: fs.readFileSync(path.join(common.fixturesDir, 'test_cert.pem')) }; diff --git a/test/parallel/test-tls-cipher-list.js b/test/parallel/test-tls-cipher-list.js index c6abce18ba3f9e..ba844c944e7cf4 100644 --- a/test/parallel/test-tls-cipher-list.js +++ b/test/parallel/test-tls-cipher-list.js @@ -8,13 +8,13 @@ if (!common.hasCrypto) { const assert = require('assert'); const spawn = require('child_process').spawn; -const defaultCoreList = require('constants').defaultCoreCipherList; +const defaultCoreList = require('crypto').constants.defaultCoreCipherList; function doCheck(arg, check) { var out = ''; arg = arg.concat([ '-pe', - 'require("constants").defaultCipherList' + 'require("crypto").constants.defaultCipherList' ]); spawn(process.execPath, arg, {}). on('error', common.fail). diff --git a/test/parallel/test-tls-ocsp-callback.js b/test/parallel/test-tls-ocsp-callback.js index ad9fab42eecd20..442f54791a0f54 100644 --- a/test/parallel/test-tls-ocsp-callback.js +++ b/test/parallel/test-tls-ocsp-callback.js @@ -18,10 +18,11 @@ if (!common.hasCrypto) { var tls = require('tls'); var assert = require('assert'); -var constants = require('constants'); var fs = require('fs'); var join = require('path').join; +const SSL_OP_NO_TICKET = require('crypto').constants.SSL_OP_NO_TICKET; + var pfx = fs.readFileSync(join(common.fixturesDir, 'keys', 'agent1-pfx.pem')); function test(testOptions, cb) { @@ -76,7 +77,7 @@ function test(testOptions, cb) { port: this.address().port, requestOCSP: testOptions.ocsp !== false, secureOptions: testOptions.ocsp === false ? - constants.SSL_OP_NO_TICKET : 0, + SSL_OP_NO_TICKET : 0, rejectUnauthorized: false }, function() { clientSecure++; diff --git a/test/parallel/test-tls-server-verify.js b/test/parallel/test-tls-server-verify.js index 5e8729cecf43fe..45b4579bbb5151 100644 --- a/test/parallel/test-tls-server-verify.js +++ b/test/parallel/test-tls-server-verify.js @@ -104,7 +104,9 @@ if (!common.hasCrypto) { } var tls = require('tls'); -var constants = require('constants'); +const SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = + require('crypto').constants.SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; + var assert = require('assert'); var fs = require('fs'); var spawn = require('child_process').spawn; @@ -262,7 +264,7 @@ function runTest(port, testIndex) { */ if (tcase.renegotiate) { serverOptions.secureOptions = - constants.SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; } var renegotiated = false; From 2a8bd35bac64691506c7c66ae131ac28793be18a Mon Sep 17 00:00:00 2001 From: Cheng Zhao <zcbenz@gmail.com> Date: Mon, 28 Sep 2015 21:42:25 +0800 Subject: [PATCH 002/131] src: add node::FreeEnvironment public API Since debugger::Agent's interface is not exported, third party embedders will have linking errors if they call Environment's destructor directly. PR-URL: https://github.com/nodejs/node/pull/3098 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> --- src/node.cc | 7 +++++++ src/node.h | 1 + 2 files changed, 8 insertions(+) diff --git a/src/node.cc b/src/node.cc index 34e49e98d4dedf..f71c2714a830d8 100644 --- a/src/node.cc +++ b/src/node.cc @@ -3386,6 +3386,13 @@ void LoadEnvironment(Environment* env) { f->Call(Null(env->isolate()), 1, &arg); } + +void FreeEnvironment(Environment* env) { + CHECK_NE(env, nullptr); + env->Dispose(); +} + + static void PrintHelp(); static bool ParseDebugOpt(const char* arg) { diff --git a/src/node.h b/src/node.h index 529ee75f279053..42c5ac59d7ecf2 100644 --- a/src/node.h +++ b/src/node.h @@ -200,6 +200,7 @@ NODE_EXTERN Environment* CreateEnvironment(v8::Isolate* isolate, int exec_argc, const char* const* exec_argv); NODE_EXTERN void LoadEnvironment(Environment* env); +NODE_EXTERN void FreeEnvironment(Environment* env); // NOTE: Calling this is the same as calling // CreateEnvironment() + LoadEnvironment() from above. From 62105288d34a1e2b00c8bb4eb608cbda6b7bf20c Mon Sep 17 00:00:00 2001 From: Ali Ijaz Sheikh <ofrobots@google.com> Date: Sat, 28 May 2016 22:17:12 -0700 Subject: [PATCH 003/131] deps: import v8_inspector Pick up v8 inspector from [1]. This is the standalone version of the devtools debug protocol. [1] https://github.com/pavelfeldman/v8_inspector/commit/e1bb206 PR-URL: https://github.com/nodejs/node/pull/6792 Reviewed-By: jasnell - James M Snell <jasnell@gmail.com> Reviewed-By: addaleax - Anna Henningsen <anna@addaleax.net> Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl> --- deps/v8_inspector/.gitignore | 1 + deps/v8_inspector/.gitmodules | 0 deps/v8_inspector/README.md | 2 + deps/v8_inspector/deps/jinja2/.gitignore | 10 + deps/v8_inspector/deps/jinja2/.travis.yml | 24 + deps/v8_inspector/deps/jinja2/AUTHORS | 33 + deps/v8_inspector/deps/jinja2/CHANGES | 375 ++ deps/v8_inspector/deps/jinja2/LICENSE | 31 + deps/v8_inspector/deps/jinja2/MANIFEST.in | 12 + deps/v8_inspector/deps/jinja2/Makefile | 21 + deps/v8_inspector/deps/jinja2/README.rst | 49 + .../deps/jinja2/artwork/jinjalogo.svg | 132 + deps/v8_inspector/deps/jinja2/docs/Makefile | 118 + .../deps/jinja2/docs/_static/.ignore | 0 .../deps/jinja2/docs/_static/jinja-small.png | Bin 0 -> 10484 bytes .../jinja2/docs/_templates/sidebarintro.html | 20 + .../jinja2/docs/_templates/sidebarlogo.html | 3 + .../deps/jinja2/docs/_themes/LICENSE | 37 + .../deps/jinja2/docs/_themes/README | 31 + .../jinja2/docs/_themes/jinja/layout.html | 8 + .../jinja2/docs/_themes/jinja/relations.html | 19 + .../docs/_themes/jinja/static/jinja.css_t | 396 ++ .../deps/jinja2/docs/_themes/jinja/theme.conf | 3 + deps/v8_inspector/deps/jinja2/docs/api.rst | 808 +++ .../deps/jinja2/docs/cache_extension.py | 56 + .../deps/jinja2/docs/changelog.rst | 3 + deps/v8_inspector/deps/jinja2/docs/conf.py | 160 + .../deps/jinja2/docs/contents.rst.inc | 23 + .../deps/jinja2/docs/extensions.rst | 346 ++ deps/v8_inspector/deps/jinja2/docs/faq.rst | 191 + deps/v8_inspector/deps/jinja2/docs/index.rst | 34 + .../deps/jinja2/docs/integration.rst | 101 + deps/v8_inspector/deps/jinja2/docs/intro.rst | 123 + .../v8_inspector/deps/jinja2/docs/jinjaext.py | 194 + .../deps/jinja2/docs/jinjastyle.sty | 119 + .../deps/jinja2/docs/latexindex.rst | 6 + deps/v8_inspector/deps/jinja2/docs/logo.pdf | Bin 0 -> 5677 bytes .../v8_inspector/deps/jinja2/docs/sandbox.rst | 94 + .../deps/jinja2/docs/switching.rst | 226 + .../deps/jinja2/docs/templates.rst | 1509 +++++ deps/v8_inspector/deps/jinja2/docs/tricks.rst | 100 + .../deps/jinja2/examples/basic/cycle.py | 13 + .../deps/jinja2/examples/basic/debugger.py | 7 + .../deps/jinja2/examples/basic/inheritance.py | 12 + .../examples/basic/templates/broken.html | 6 + .../examples/basic/templates/subbroken.html | 3 + .../deps/jinja2/examples/basic/test.py | 27 + .../basic/test_filter_and_linestatements.py | 25 + .../jinja2/examples/basic/test_loop_filter.py | 12 + .../deps/jinja2/examples/basic/translate.py | 14 + .../deps/jinja2/examples/bench.py | 433 ++ .../deps/jinja2/examples/profile.py | 52 + .../jinja2/examples/rwbench/django/_form.html | 1 + .../examples/rwbench/django/_input_field.html | 1 + .../examples/rwbench/django/_textarea.html | 1 + .../jinja2/examples/rwbench/django/index.html | 29 + .../examples/rwbench/django/layout.html | 29 + .../deps/jinja2/examples/rwbench/djangoext.py | 135 + .../examples/rwbench/genshi/helpers.html | 12 + .../jinja2/examples/rwbench/genshi/index.html | 41 + .../examples/rwbench/genshi/layout.html | 30 + .../examples/rwbench/jinja/helpers.html | 12 + .../jinja2/examples/rwbench/jinja/index.html | 29 + .../jinja2/examples/rwbench/jinja/layout.html | 29 + .../jinja2/examples/rwbench/mako/helpers.html | 11 + .../jinja2/examples/rwbench/mako/index.html | 31 + .../jinja2/examples/rwbench/mako/layout.html | 30 + .../deps/jinja2/examples/rwbench/rwbench.py | 112 + .../deps/jinja2/ext/Vim/jinja.vim | 135 + .../jinja2/ext/django2jinja/django2jinja.py | 768 +++ .../deps/jinja2/ext/django2jinja/example.py | 7 + .../ext/django2jinja/templates/index.html | 58 + .../ext/django2jinja/templates/layout.html | 4 + .../django2jinja/templates/subtemplate.html | 1 + .../deps/jinja2/ext/djangojinja2.py | 86 + .../deps/jinja2/ext/inlinegettext.py | 78 + deps/v8_inspector/deps/jinja2/ext/jinja.el | 128 + .../deps/jinja2/jinja2/__init__.py | 70 + .../deps/jinja2/jinja2/_compat.py | 102 + .../deps/jinja2/jinja2/_stringdefs.py | 132 + .../deps/jinja2/jinja2/bccache.py | 362 ++ .../deps/jinja2/jinja2/compiler.py | 1686 ++++++ .../deps/jinja2/jinja2/constants.py | 32 + deps/v8_inspector/deps/jinja2/jinja2/debug.py | 350 ++ .../deps/jinja2/jinja2/defaults.py | 43 + .../deps/jinja2/jinja2/environment.py | 1213 ++++ .../deps/jinja2/jinja2/exceptions.py | 146 + deps/v8_inspector/deps/jinja2/jinja2/ext.py | 636 ++ .../deps/jinja2/jinja2/filters.py | 996 ++++ deps/v8_inspector/deps/jinja2/jinja2/lexer.py | 734 +++ .../deps/jinja2/jinja2/loaders.py | 481 ++ deps/v8_inspector/deps/jinja2/jinja2/meta.py | 103 + deps/v8_inspector/deps/jinja2/jinja2/nodes.py | 919 +++ .../deps/jinja2/jinja2/optimizer.py | 68 + .../v8_inspector/deps/jinja2/jinja2/parser.py | 899 +++ .../deps/jinja2/jinja2/runtime.py | 667 +++ .../deps/jinja2/jinja2/sandbox.py | 367 ++ deps/v8_inspector/deps/jinja2/jinja2/tests.py | 173 + deps/v8_inspector/deps/jinja2/jinja2/utils.py | 531 ++ .../deps/jinja2/jinja2/visitor.py | 87 + .../deps/jinja2/scripts/jinja2-debug.py | 43 + .../deps/jinja2/scripts/make-release.py | 164 + .../v8_inspector/deps/jinja2/scripts/pylintrc | 301 + deps/v8_inspector/deps/jinja2/setup.cfg | 8 + deps/v8_inspector/deps/jinja2/setup.py | 77 + .../deps/jinja2/tests/conftest.py | 74 + .../deps/jinja2/tests/res/__init__.py | 0 .../jinja2/tests/res/templates/broken.html | 3 + .../jinja2/tests/res/templates/foo/test.html | 1 + .../tests/res/templates/syntaxerror.html | 4 + .../deps/jinja2/tests/res/templates/test.html | 1 + .../deps/jinja2/tests/test_api.py | 327 ++ .../deps/jinja2/tests/test_bytecode_cache.py | 32 + .../deps/jinja2/tests/test_core_tags.py | 337 ++ .../deps/jinja2/tests/test_debug.py | 73 + .../deps/jinja2/tests/test_ext.py | 467 ++ .../deps/jinja2/tests/test_filters.py | 558 ++ .../deps/jinja2/tests/test_imports.py | 148 + .../deps/jinja2/tests/test_inheritance.py | 248 + .../deps/jinja2/tests/test_lexnparse.py | 609 ++ .../deps/jinja2/tests/test_loader.py | 220 + .../deps/jinja2/tests/test_regression.py | 278 + .../deps/jinja2/tests/test_security.py | 161 + .../deps/jinja2/tests/test_tests.py | 104 + .../deps/jinja2/tests/test_utils.py | 76 + deps/v8_inspector/deps/jinja2/tox.ini | 9 + deps/v8_inspector/deps/markupsafe/.gitignore | 10 + deps/v8_inspector/deps/markupsafe/.travis.yml | 11 + deps/v8_inspector/deps/markupsafe/AUTHORS | 13 + deps/v8_inspector/deps/markupsafe/CHANGES | 48 + deps/v8_inspector/deps/markupsafe/LICENSE | 33 + deps/v8_inspector/deps/markupsafe/MANIFEST.in | 2 + deps/v8_inspector/deps/markupsafe/Makefile | 7 + deps/v8_inspector/deps/markupsafe/README.rst | 101 + .../deps/markupsafe/bench/bench_basic.py | 5 + .../markupsafe/bench/bench_largestring.py | 6 + .../bench/bench_long_empty_string.py | 6 + .../markupsafe/bench/bench_long_suffix.py | 6 + .../bench/bench_short_empty_string.py | 5 + .../deps/markupsafe/bench/runbench.py | 43 + .../deps/markupsafe/markupsafe/__init__.py | 305 + .../deps/markupsafe/markupsafe/_compat.py | 26 + .../deps/markupsafe/markupsafe/_constants.py | 267 + .../deps/markupsafe/markupsafe/_native.py | 46 + .../deps/markupsafe/markupsafe/_speedups.c | 239 + .../deps/markupsafe/markupsafe/tests.py | 207 + deps/v8_inspector/deps/markupsafe/setup.py | 133 + deps/v8_inspector/deps/markupsafe/tox.ini | 5 + deps/v8_inspector/deps/wtf/README.md | 1 + deps/v8_inspector/deps/wtf/wtf/Assertions.h | 40 + deps/v8_inspector/deps/wtf/wtf/Compiler.h | 54 + deps/v8_inspector/deps/wtf/wtf/PtrUtil.h | 275 + deps/v8_inspector/devtools/Inspector-1.1.json | 3924 +++++++++++++ deps/v8_inspector/devtools/protocol.json | 5167 +++++++++++++++++ deps/v8_inspector/platform/PlatformExport.h | 66 + .../platform/inspector_protocol/Allocator.h | 22 + .../platform/inspector_protocol/Array.h | 137 + .../inspector_protocol/Backend_cpp.template | 0 .../inspector_protocol/Backend_h.template | 78 + .../inspector_protocol/CodeGenerator.py | 308 + .../platform/inspector_protocol/Collections.h | 14 + .../inspector_protocol/CollectionsSTL.h | 253 + .../inspector_protocol/CollectionsWTF.h | 193 + .../Dispatcher_cpp.template | 365 ++ .../inspector_protocol/Dispatcher_h.template | 65 + .../inspector_protocol/ErrorSupport.cpp | 71 + .../inspector_protocol/ErrorSupport.h | 37 + .../inspector_protocol/FrontendChannel.h | 45 + .../inspector_protocol/Frontend_cpp.template | 53 + .../inspector_protocol/Frontend_h.template | 57 + .../platform/inspector_protocol/Maybe.h | 89 + .../platform/inspector_protocol/OWNERS | 5 + .../platform/inspector_protocol/Parser.cpp | 496 ++ .../platform/inspector_protocol/Parser.h | 22 + .../inspector_protocol/ParserTest.cpp | 504 ++ .../platform/inspector_protocol/String16.h | 14 + .../inspector_protocol/String16STL.cpp | 602 ++ .../platform/inspector_protocol/String16STL.h | 236 + .../inspector_protocol/String16WTF.cpp | 47 + .../platform/inspector_protocol/String16WTF.h | 139 + .../TypeBuilder_cpp.template | 130 + .../inspector_protocol/TypeBuilder_h.template | 211 + .../inspector_protocol/ValueConversions.cpp | 51 + .../inspector_protocol/ValueConversions.h | 163 + .../platform/inspector_protocol/Values.cpp | 354 ++ .../platform/inspector_protocol/Values.h | 221 + .../generate-inspector-protocol-version | 464 ++ .../platform/inspector_protocol/protocol.gyp | 89 + .../platform/v8_inspector/Atomics.h | 27 + .../platform/v8_inspector/DebuggerScript.js | 705 +++ .../platform/v8_inspector/InjectedScript.cpp | 538 ++ .../platform/v8_inspector/InjectedScript.h | 176 + .../v8_inspector/InjectedScriptNative.cpp | 97 + .../v8_inspector/InjectedScriptNative.h | 44 + .../v8_inspector/InjectedScriptSource.js | 1222 ++++ .../v8_inspector/InspectedContext.cpp | 88 + .../platform/v8_inspector/InspectedContext.h | 64 + .../v8_inspector/InspectorWrapper.cpp | 70 + .../platform/v8_inspector/InspectorWrapper.h | 88 + .../v8_inspector/JavaScriptCallFrame.cpp | 133 + .../v8_inspector/JavaScriptCallFrame.h | 75 + .../v8_inspector/platform/v8_inspector/OWNERS | 5 + .../platform/v8_inspector/RemoteObjectId.cpp | 72 + .../platform/v8_inspector/RemoteObjectId.h | 59 + .../platform/v8_inspector/ScriptBreakpoint.h | 55 + .../platform/v8_inspector/V8Compat.h | 28 + .../platform/v8_inspector/V8Console.cpp | 730 +++ .../platform/v8_inspector/V8Console.h | 71 + .../v8_inspector/V8DebuggerAgentImpl.cpp | 1455 +++++ .../v8_inspector/V8DebuggerAgentImpl.h | 248 + .../platform/v8_inspector/V8DebuggerImpl.cpp | 854 +++ .../platform/v8_inspector/V8DebuggerImpl.h | 168 + .../v8_inspector/V8DebuggerScript.cpp | 104 + .../platform/v8_inspector/V8DebuggerScript.h | 98 + .../platform/v8_inspector/V8FunctionCall.cpp | 125 + .../platform/v8_inspector/V8FunctionCall.h | 68 + .../v8_inspector/V8HeapProfilerAgentImpl.cpp | 409 ++ .../v8_inspector/V8HeapProfilerAgentImpl.h | 60 + .../v8_inspector/V8InjectedScriptHost.cpp | 286 + .../v8_inspector/V8InjectedScriptHost.h | 44 + .../v8_inspector/V8InspectorSessionImpl.cpp | 279 + .../v8_inspector/V8InspectorSessionImpl.h | 93 + .../v8_inspector/V8ProfilerAgentImpl.cpp | 302 + .../v8_inspector/V8ProfilerAgentImpl.h | 65 + .../platform/v8_inspector/V8Regex.cpp | 93 + .../platform/v8_inspector/V8Regex.h | 37 + .../v8_inspector/V8RuntimeAgentImpl.cpp | 437 ++ .../v8_inspector/V8RuntimeAgentImpl.h | 133 + .../v8_inspector/V8StackTraceImpl.cpp | 227 + .../platform/v8_inspector/V8StackTraceImpl.h | 74 + .../platform/v8_inspector/V8StringUtil.cpp | 280 + .../platform/v8_inspector/V8StringUtil.h | 22 + .../platform/v8_inspector/build/rjsmin.py | 295 + .../platform/v8_inspector/build/xxd.py | 28 + .../v8_inspector/debugger_script_externs.js | 556 ++ .../v8_inspector/injected_script_externs.js | 97 + .../v8_inspector/public/ConsoleAPITypes.h | 47 + .../v8_inspector/public/ConsoleTypes.h | 55 + .../v8_inspector/public/V8ContentSearchUtil.h | 26 + .../v8_inspector/public/V8ContextInfo.h | 42 + .../platform/v8_inspector/public/V8Debugger.h | 65 + .../v8_inspector/public/V8DebuggerAgent.h | 24 + .../v8_inspector/public/V8DebuggerClient.h | 54 + .../v8_inspector/public/V8EventListenerInfo.h | 36 + .../v8_inspector/public/V8HeapProfilerAgent.h | 23 + .../v8_inspector/public/V8Inspector.cpp | 125 + .../v8_inspector/public/V8Inspector.h | 81 + .../v8_inspector/public/V8InspectorSession.h | 67 + .../public/V8InspectorSessionClient.h | 28 + .../v8_inspector/public/V8ProfilerAgent.h | 22 + .../v8_inspector/public/V8RuntimeAgent.h | 23 + .../v8_inspector/public/V8StackTrace.h | 42 + .../v8_inspector/public/V8ToProtocolValue.h | 17 + .../platform/v8_inspector/v8_inspector.gyp | 48 + deps/v8_inspector/v8_inspector.gyp | 35 + deps/v8_inspector/v8_inspector.gypi | 94 + 256 files changed, 51764 insertions(+) create mode 100644 deps/v8_inspector/.gitignore create mode 100644 deps/v8_inspector/.gitmodules create mode 100644 deps/v8_inspector/README.md create mode 100644 deps/v8_inspector/deps/jinja2/.gitignore create mode 100644 deps/v8_inspector/deps/jinja2/.travis.yml create mode 100644 deps/v8_inspector/deps/jinja2/AUTHORS create mode 100644 deps/v8_inspector/deps/jinja2/CHANGES create mode 100644 deps/v8_inspector/deps/jinja2/LICENSE create mode 100644 deps/v8_inspector/deps/jinja2/MANIFEST.in create mode 100644 deps/v8_inspector/deps/jinja2/Makefile create mode 100644 deps/v8_inspector/deps/jinja2/README.rst create mode 100644 deps/v8_inspector/deps/jinja2/artwork/jinjalogo.svg create mode 100644 deps/v8_inspector/deps/jinja2/docs/Makefile create mode 100644 deps/v8_inspector/deps/jinja2/docs/_static/.ignore create mode 100644 deps/v8_inspector/deps/jinja2/docs/_static/jinja-small.png create mode 100644 deps/v8_inspector/deps/jinja2/docs/_templates/sidebarintro.html create mode 100644 deps/v8_inspector/deps/jinja2/docs/_templates/sidebarlogo.html create mode 100644 deps/v8_inspector/deps/jinja2/docs/_themes/LICENSE create mode 100644 deps/v8_inspector/deps/jinja2/docs/_themes/README create mode 100644 deps/v8_inspector/deps/jinja2/docs/_themes/jinja/layout.html create mode 100644 deps/v8_inspector/deps/jinja2/docs/_themes/jinja/relations.html create mode 100644 deps/v8_inspector/deps/jinja2/docs/_themes/jinja/static/jinja.css_t create mode 100644 deps/v8_inspector/deps/jinja2/docs/_themes/jinja/theme.conf create mode 100644 deps/v8_inspector/deps/jinja2/docs/api.rst create mode 100644 deps/v8_inspector/deps/jinja2/docs/cache_extension.py create mode 100644 deps/v8_inspector/deps/jinja2/docs/changelog.rst create mode 100644 deps/v8_inspector/deps/jinja2/docs/conf.py create mode 100644 deps/v8_inspector/deps/jinja2/docs/contents.rst.inc create mode 100644 deps/v8_inspector/deps/jinja2/docs/extensions.rst create mode 100644 deps/v8_inspector/deps/jinja2/docs/faq.rst create mode 100644 deps/v8_inspector/deps/jinja2/docs/index.rst create mode 100644 deps/v8_inspector/deps/jinja2/docs/integration.rst create mode 100644 deps/v8_inspector/deps/jinja2/docs/intro.rst create mode 100644 deps/v8_inspector/deps/jinja2/docs/jinjaext.py create mode 100644 deps/v8_inspector/deps/jinja2/docs/jinjastyle.sty create mode 100644 deps/v8_inspector/deps/jinja2/docs/latexindex.rst create mode 100644 deps/v8_inspector/deps/jinja2/docs/logo.pdf create mode 100644 deps/v8_inspector/deps/jinja2/docs/sandbox.rst create mode 100644 deps/v8_inspector/deps/jinja2/docs/switching.rst create mode 100644 deps/v8_inspector/deps/jinja2/docs/templates.rst create mode 100644 deps/v8_inspector/deps/jinja2/docs/tricks.rst create mode 100644 deps/v8_inspector/deps/jinja2/examples/basic/cycle.py create mode 100644 deps/v8_inspector/deps/jinja2/examples/basic/debugger.py create mode 100644 deps/v8_inspector/deps/jinja2/examples/basic/inheritance.py create mode 100644 deps/v8_inspector/deps/jinja2/examples/basic/templates/broken.html create mode 100644 deps/v8_inspector/deps/jinja2/examples/basic/templates/subbroken.html create mode 100644 deps/v8_inspector/deps/jinja2/examples/basic/test.py create mode 100644 deps/v8_inspector/deps/jinja2/examples/basic/test_filter_and_linestatements.py create mode 100644 deps/v8_inspector/deps/jinja2/examples/basic/test_loop_filter.py create mode 100644 deps/v8_inspector/deps/jinja2/examples/basic/translate.py create mode 100644 deps/v8_inspector/deps/jinja2/examples/bench.py create mode 100644 deps/v8_inspector/deps/jinja2/examples/profile.py create mode 100644 deps/v8_inspector/deps/jinja2/examples/rwbench/django/_form.html create mode 100644 deps/v8_inspector/deps/jinja2/examples/rwbench/django/_input_field.html create mode 100644 deps/v8_inspector/deps/jinja2/examples/rwbench/django/_textarea.html create mode 100644 deps/v8_inspector/deps/jinja2/examples/rwbench/django/index.html create mode 100644 deps/v8_inspector/deps/jinja2/examples/rwbench/django/layout.html create mode 100644 deps/v8_inspector/deps/jinja2/examples/rwbench/djangoext.py create mode 100644 deps/v8_inspector/deps/jinja2/examples/rwbench/genshi/helpers.html create mode 100644 deps/v8_inspector/deps/jinja2/examples/rwbench/genshi/index.html create mode 100644 deps/v8_inspector/deps/jinja2/examples/rwbench/genshi/layout.html create mode 100644 deps/v8_inspector/deps/jinja2/examples/rwbench/jinja/helpers.html create mode 100644 deps/v8_inspector/deps/jinja2/examples/rwbench/jinja/index.html create mode 100644 deps/v8_inspector/deps/jinja2/examples/rwbench/jinja/layout.html create mode 100644 deps/v8_inspector/deps/jinja2/examples/rwbench/mako/helpers.html create mode 100644 deps/v8_inspector/deps/jinja2/examples/rwbench/mako/index.html create mode 100644 deps/v8_inspector/deps/jinja2/examples/rwbench/mako/layout.html create mode 100644 deps/v8_inspector/deps/jinja2/examples/rwbench/rwbench.py create mode 100644 deps/v8_inspector/deps/jinja2/ext/Vim/jinja.vim create mode 100644 deps/v8_inspector/deps/jinja2/ext/django2jinja/django2jinja.py create mode 100644 deps/v8_inspector/deps/jinja2/ext/django2jinja/example.py create mode 100644 deps/v8_inspector/deps/jinja2/ext/django2jinja/templates/index.html create mode 100644 deps/v8_inspector/deps/jinja2/ext/django2jinja/templates/layout.html create mode 100644 deps/v8_inspector/deps/jinja2/ext/django2jinja/templates/subtemplate.html create mode 100644 deps/v8_inspector/deps/jinja2/ext/djangojinja2.py create mode 100644 deps/v8_inspector/deps/jinja2/ext/inlinegettext.py create mode 100644 deps/v8_inspector/deps/jinja2/ext/jinja.el create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/__init__.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/_compat.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/_stringdefs.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/bccache.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/compiler.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/constants.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/debug.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/defaults.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/environment.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/exceptions.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/ext.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/filters.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/lexer.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/loaders.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/meta.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/nodes.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/optimizer.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/parser.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/runtime.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/sandbox.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/tests.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/utils.py create mode 100644 deps/v8_inspector/deps/jinja2/jinja2/visitor.py create mode 100755 deps/v8_inspector/deps/jinja2/scripts/jinja2-debug.py create mode 100644 deps/v8_inspector/deps/jinja2/scripts/make-release.py create mode 100644 deps/v8_inspector/deps/jinja2/scripts/pylintrc create mode 100644 deps/v8_inspector/deps/jinja2/setup.cfg create mode 100644 deps/v8_inspector/deps/jinja2/setup.py create mode 100644 deps/v8_inspector/deps/jinja2/tests/conftest.py create mode 100644 deps/v8_inspector/deps/jinja2/tests/res/__init__.py create mode 100644 deps/v8_inspector/deps/jinja2/tests/res/templates/broken.html create mode 100644 deps/v8_inspector/deps/jinja2/tests/res/templates/foo/test.html create mode 100644 deps/v8_inspector/deps/jinja2/tests/res/templates/syntaxerror.html create mode 100644 deps/v8_inspector/deps/jinja2/tests/res/templates/test.html create mode 100644 deps/v8_inspector/deps/jinja2/tests/test_api.py create mode 100644 deps/v8_inspector/deps/jinja2/tests/test_bytecode_cache.py create mode 100644 deps/v8_inspector/deps/jinja2/tests/test_core_tags.py create mode 100644 deps/v8_inspector/deps/jinja2/tests/test_debug.py create mode 100644 deps/v8_inspector/deps/jinja2/tests/test_ext.py create mode 100644 deps/v8_inspector/deps/jinja2/tests/test_filters.py create mode 100644 deps/v8_inspector/deps/jinja2/tests/test_imports.py create mode 100644 deps/v8_inspector/deps/jinja2/tests/test_inheritance.py create mode 100644 deps/v8_inspector/deps/jinja2/tests/test_lexnparse.py create mode 100644 deps/v8_inspector/deps/jinja2/tests/test_loader.py create mode 100644 deps/v8_inspector/deps/jinja2/tests/test_regression.py create mode 100644 deps/v8_inspector/deps/jinja2/tests/test_security.py create mode 100644 deps/v8_inspector/deps/jinja2/tests/test_tests.py create mode 100644 deps/v8_inspector/deps/jinja2/tests/test_utils.py create mode 100644 deps/v8_inspector/deps/jinja2/tox.ini create mode 100644 deps/v8_inspector/deps/markupsafe/.gitignore create mode 100644 deps/v8_inspector/deps/markupsafe/.travis.yml create mode 100644 deps/v8_inspector/deps/markupsafe/AUTHORS create mode 100644 deps/v8_inspector/deps/markupsafe/CHANGES create mode 100644 deps/v8_inspector/deps/markupsafe/LICENSE create mode 100644 deps/v8_inspector/deps/markupsafe/MANIFEST.in create mode 100644 deps/v8_inspector/deps/markupsafe/Makefile create mode 100644 deps/v8_inspector/deps/markupsafe/README.rst create mode 100644 deps/v8_inspector/deps/markupsafe/bench/bench_basic.py create mode 100644 deps/v8_inspector/deps/markupsafe/bench/bench_largestring.py create mode 100644 deps/v8_inspector/deps/markupsafe/bench/bench_long_empty_string.py create mode 100644 deps/v8_inspector/deps/markupsafe/bench/bench_long_suffix.py create mode 100644 deps/v8_inspector/deps/markupsafe/bench/bench_short_empty_string.py create mode 100644 deps/v8_inspector/deps/markupsafe/bench/runbench.py create mode 100644 deps/v8_inspector/deps/markupsafe/markupsafe/__init__.py create mode 100644 deps/v8_inspector/deps/markupsafe/markupsafe/_compat.py create mode 100644 deps/v8_inspector/deps/markupsafe/markupsafe/_constants.py create mode 100644 deps/v8_inspector/deps/markupsafe/markupsafe/_native.py create mode 100644 deps/v8_inspector/deps/markupsafe/markupsafe/_speedups.c create mode 100644 deps/v8_inspector/deps/markupsafe/markupsafe/tests.py create mode 100644 deps/v8_inspector/deps/markupsafe/setup.py create mode 100644 deps/v8_inspector/deps/markupsafe/tox.ini create mode 100644 deps/v8_inspector/deps/wtf/README.md create mode 100644 deps/v8_inspector/deps/wtf/wtf/Assertions.h create mode 100644 deps/v8_inspector/deps/wtf/wtf/Compiler.h create mode 100644 deps/v8_inspector/deps/wtf/wtf/PtrUtil.h create mode 100644 deps/v8_inspector/devtools/Inspector-1.1.json create mode 100644 deps/v8_inspector/devtools/protocol.json create mode 100644 deps/v8_inspector/platform/PlatformExport.h create mode 100644 deps/v8_inspector/platform/inspector_protocol/Allocator.h create mode 100644 deps/v8_inspector/platform/inspector_protocol/Array.h create mode 100644 deps/v8_inspector/platform/inspector_protocol/Backend_cpp.template create mode 100644 deps/v8_inspector/platform/inspector_protocol/Backend_h.template create mode 100644 deps/v8_inspector/platform/inspector_protocol/CodeGenerator.py create mode 100644 deps/v8_inspector/platform/inspector_protocol/Collections.h create mode 100644 deps/v8_inspector/platform/inspector_protocol/CollectionsSTL.h create mode 100644 deps/v8_inspector/platform/inspector_protocol/CollectionsWTF.h create mode 100644 deps/v8_inspector/platform/inspector_protocol/Dispatcher_cpp.template create mode 100644 deps/v8_inspector/platform/inspector_protocol/Dispatcher_h.template create mode 100644 deps/v8_inspector/platform/inspector_protocol/ErrorSupport.cpp create mode 100644 deps/v8_inspector/platform/inspector_protocol/ErrorSupport.h create mode 100644 deps/v8_inspector/platform/inspector_protocol/FrontendChannel.h create mode 100644 deps/v8_inspector/platform/inspector_protocol/Frontend_cpp.template create mode 100644 deps/v8_inspector/platform/inspector_protocol/Frontend_h.template create mode 100644 deps/v8_inspector/platform/inspector_protocol/Maybe.h create mode 100644 deps/v8_inspector/platform/inspector_protocol/OWNERS create mode 100644 deps/v8_inspector/platform/inspector_protocol/Parser.cpp create mode 100644 deps/v8_inspector/platform/inspector_protocol/Parser.h create mode 100644 deps/v8_inspector/platform/inspector_protocol/ParserTest.cpp create mode 100644 deps/v8_inspector/platform/inspector_protocol/String16.h create mode 100644 deps/v8_inspector/platform/inspector_protocol/String16STL.cpp create mode 100644 deps/v8_inspector/platform/inspector_protocol/String16STL.h create mode 100644 deps/v8_inspector/platform/inspector_protocol/String16WTF.cpp create mode 100644 deps/v8_inspector/platform/inspector_protocol/String16WTF.h create mode 100644 deps/v8_inspector/platform/inspector_protocol/TypeBuilder_cpp.template create mode 100644 deps/v8_inspector/platform/inspector_protocol/TypeBuilder_h.template create mode 100644 deps/v8_inspector/platform/inspector_protocol/ValueConversions.cpp create mode 100644 deps/v8_inspector/platform/inspector_protocol/ValueConversions.h create mode 100644 deps/v8_inspector/platform/inspector_protocol/Values.cpp create mode 100644 deps/v8_inspector/platform/inspector_protocol/Values.h create mode 100755 deps/v8_inspector/platform/inspector_protocol/generate-inspector-protocol-version create mode 100644 deps/v8_inspector/platform/inspector_protocol/protocol.gyp create mode 100644 deps/v8_inspector/platform/v8_inspector/Atomics.h create mode 100644 deps/v8_inspector/platform/v8_inspector/DebuggerScript.js create mode 100644 deps/v8_inspector/platform/v8_inspector/InjectedScript.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/InjectedScript.h create mode 100644 deps/v8_inspector/platform/v8_inspector/InjectedScriptNative.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/InjectedScriptNative.h create mode 100644 deps/v8_inspector/platform/v8_inspector/InjectedScriptSource.js create mode 100644 deps/v8_inspector/platform/v8_inspector/InspectedContext.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/InspectedContext.h create mode 100644 deps/v8_inspector/platform/v8_inspector/InspectorWrapper.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/InspectorWrapper.h create mode 100644 deps/v8_inspector/platform/v8_inspector/JavaScriptCallFrame.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/JavaScriptCallFrame.h create mode 100644 deps/v8_inspector/platform/v8_inspector/OWNERS create mode 100644 deps/v8_inspector/platform/v8_inspector/RemoteObjectId.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/RemoteObjectId.h create mode 100644 deps/v8_inspector/platform/v8_inspector/ScriptBreakpoint.h create mode 100644 deps/v8_inspector/platform/v8_inspector/V8Compat.h create mode 100644 deps/v8_inspector/platform/v8_inspector/V8Console.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/V8Console.h create mode 100644 deps/v8_inspector/platform/v8_inspector/V8DebuggerAgentImpl.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/V8DebuggerAgentImpl.h create mode 100644 deps/v8_inspector/platform/v8_inspector/V8DebuggerImpl.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/V8DebuggerImpl.h create mode 100644 deps/v8_inspector/platform/v8_inspector/V8DebuggerScript.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/V8DebuggerScript.h create mode 100644 deps/v8_inspector/platform/v8_inspector/V8FunctionCall.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/V8FunctionCall.h create mode 100644 deps/v8_inspector/platform/v8_inspector/V8HeapProfilerAgentImpl.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/V8HeapProfilerAgentImpl.h create mode 100644 deps/v8_inspector/platform/v8_inspector/V8InjectedScriptHost.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/V8InjectedScriptHost.h create mode 100644 deps/v8_inspector/platform/v8_inspector/V8InspectorSessionImpl.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/V8InspectorSessionImpl.h create mode 100644 deps/v8_inspector/platform/v8_inspector/V8ProfilerAgentImpl.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/V8ProfilerAgentImpl.h create mode 100644 deps/v8_inspector/platform/v8_inspector/V8Regex.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/V8Regex.h create mode 100644 deps/v8_inspector/platform/v8_inspector/V8RuntimeAgentImpl.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/V8RuntimeAgentImpl.h create mode 100644 deps/v8_inspector/platform/v8_inspector/V8StackTraceImpl.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/V8StackTraceImpl.h create mode 100644 deps/v8_inspector/platform/v8_inspector/V8StringUtil.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/V8StringUtil.h create mode 100755 deps/v8_inspector/platform/v8_inspector/build/rjsmin.py create mode 100644 deps/v8_inspector/platform/v8_inspector/build/xxd.py create mode 100644 deps/v8_inspector/platform/v8_inspector/debugger_script_externs.js create mode 100644 deps/v8_inspector/platform/v8_inspector/injected_script_externs.js create mode 100644 deps/v8_inspector/platform/v8_inspector/public/ConsoleAPITypes.h create mode 100644 deps/v8_inspector/platform/v8_inspector/public/ConsoleTypes.h create mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8ContentSearchUtil.h create mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8ContextInfo.h create mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8Debugger.h create mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8DebuggerAgent.h create mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8DebuggerClient.h create mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8EventListenerInfo.h create mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8HeapProfilerAgent.h create mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8Inspector.cpp create mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8Inspector.h create mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8InspectorSession.h create mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8InspectorSessionClient.h create mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8ProfilerAgent.h create mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8RuntimeAgent.h create mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8StackTrace.h create mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8ToProtocolValue.h create mode 100644 deps/v8_inspector/platform/v8_inspector/v8_inspector.gyp create mode 100644 deps/v8_inspector/v8_inspector.gyp create mode 100644 deps/v8_inspector/v8_inspector.gypi diff --git a/deps/v8_inspector/.gitignore b/deps/v8_inspector/.gitignore new file mode 100644 index 00000000000000..2a6b83e201c455 --- /dev/null +++ b/deps/v8_inspector/.gitignore @@ -0,0 +1 @@ +platform/v8_inspector/build/rjsmin.pyc diff --git a/deps/v8_inspector/.gitmodules b/deps/v8_inspector/.gitmodules new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/deps/v8_inspector/README.md b/deps/v8_inspector/README.md new file mode 100644 index 00000000000000..9892f5fc810b74 --- /dev/null +++ b/deps/v8_inspector/README.md @@ -0,0 +1,2 @@ +# v8_inspector +# v8_inspector diff --git a/deps/v8_inspector/deps/jinja2/.gitignore b/deps/v8_inspector/deps/jinja2/.gitignore new file mode 100644 index 00000000000000..3ae1fa70b3cd5c --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/.gitignore @@ -0,0 +1,10 @@ +*.so +docs/_build +*.pyc +*.pyo +*.egg-info +*.egg +build +dist +.DS_Store +.tox diff --git a/deps/v8_inspector/deps/jinja2/.travis.yml b/deps/v8_inspector/deps/jinja2/.travis.yml new file mode 100644 index 00000000000000..9cf4d8656f35c9 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/.travis.yml @@ -0,0 +1,24 @@ +language: python +python: + - "2.6" + - "2.7" + - "3.3" + - "3.4" + - "3.5" + - "pypy" + +install: + - pip install tox +script: + - tox -e \ + $(echo $TRAVIS_PYTHON_VERSION | sed 's/^\([0-9]\)\.\([0-9]\).*/py\1\2/') + +notifications: + email: false + irc: + channels: + - "chat.freenode.net#pocoo" + on_success: change + on_failure: always + use_notice: true + skip_join: true diff --git a/deps/v8_inspector/deps/jinja2/AUTHORS b/deps/v8_inspector/deps/jinja2/AUTHORS new file mode 100644 index 00000000000000..943f625f876b17 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/AUTHORS @@ -0,0 +1,33 @@ +Jinja is written and maintained by the Jinja Team and various +contributors: + +Lead Developer: + +- Armin Ronacher <armin.ronacher@active-4.com> + +Developers: + +- Christoph Hack +- Georg Brandl + +Contributors: + +- Bryan McLemore +- Mickaël Guérin <kael@crocobox.org> +- Cameron Knight +- Lawrence Journal-World. +- David Cramer + +Patches and suggestions: + +- Ronny Pfannschmidt +- Axel Böhm +- Alexey Melchakov +- Bryan McLemore +- Clovis Fabricio (nosklo) +- Cameron Knight +- Peter van Dijk (Habbie) +- Stefan Ebner +- Rene Leonhardt +- Thomas Waldmann +- Cory Benfield (Lukasa) diff --git a/deps/v8_inspector/deps/jinja2/CHANGES b/deps/v8_inspector/deps/jinja2/CHANGES new file mode 100644 index 00000000000000..4e5df26c9934f6 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/CHANGES @@ -0,0 +1,375 @@ +Jinja2 Changelog +================ + +Version 2.8.1 +------------- + +(unreleased bugfix release) + +- Fixed the `for_qs` flag for `urlencode`. + +Version 2.8 +----------- +(codename Replacement, released on July 26th 2015) + +- Added `target` parameter to urlize function. +- Added support for `followsymlinks` to the file system loader. +- The truncate filter now counts the length. +- Added equalto filter that helps with select filters. +- Changed cache keys to use absolute file names if available + instead of load names. +- Fixed loop length calculation for some iterators. +- Changed how Jinja2 enforces strings to be native strings in + Python 2 to work when people break their default encoding. +- Added :func:`make_logging_undefined` which returns an undefined + object that logs failures into a logger. +- If unmarshalling of cached data fails the template will be + reloaded now. +- Implemented a block ``set`` tag. +- Default cache size was incrased to 400 from a low 50. +- Fixed ``is number`` test to accept long integers in all Python versions. +- Changed ``is number`` to accept Decimal as a number. +- Added a check for default arguments followed by non-default arguments. This + change makes ``{% macro m(x, y=1, z) %}...{% endmacro %}`` a syntax error. The + previous behavior for this code was broken anyway (resulting in the default + value being applied to `y`). +- Add ability to use custom subclasses of ``jinja2.compiler.CodeGenerator`` and + ``jinja2.runtime.Context`` by adding two new attributes to the environment + (`code_generator_class` and `context_class`) (pull request ``#404``). +- added support for context/environment/evalctx decorator functions on + the finalize callback of the environment. +- escape query strings for urlencode properly. Previously slashes were not + escaped in that place. +- Add 'base' parameter to 'int' filter. + +Version 2.7.3 +------------- +(bugfix release, released on June 6th 2014) + +- Security issue: Corrected the security fix for the cache folder. This + fix was provided by RedHat. + +Version 2.7.2 +------------- +(bugfix release, released on January 10th 2014) + +- Prefix loader was not forwarding the locals properly to + inner loaders. This is now fixed. +- Security issue: Changed the default folder for the filesystem cache to be + user specific and read and write protected on UNIX systems. See `Debian bug + 734747`_ for more information. + +.. _Debian bug 734747: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=734747 + +Version 2.7.1 +------------- +(bugfix release, released on August 7th 2013) + +- Fixed a bug with ``call_filter`` not working properly on environment + and context filters. +- Fixed lack of Python 3 support for bytecode caches. +- Reverted support for defining blocks in included templates as this + broke existing templates for users. +- Fixed some warnings with hashing of undefineds and nodes if Python + is run with warnings for Python 3. +- Added support for properly hashing undefined objects. +- Fixed a bug with the title filter not working on already uppercase + strings. + +Version 2.7 +----------- +(codename Translation, released on May 20th 2013) + +- Choice and prefix loaders now dispatch source and template lookup + separately in order to work in combination with module loaders as + advertised. +- Fixed filesizeformat. +- Added a non-silent option for babel extraction. +- Added `urlencode` filter that automatically quotes values for + URL safe usage with utf-8 as only supported encoding. If applications + want to change this encoding they can override the filter. +- Added `keep-trailing-newline` configuration to environments and + templates to optionally preserve the final trailing newline. +- Accessing `last` on the loop context no longer causes the iterator + to be consumed into a list. +- Python requirement changed: 2.6, 2.7 or >= 3.3 are required now, + supported by same source code, using the "six" compatibility library. +- Allow `contextfunction` and other decorators to be applied to `__call__`. +- Added support for changing from newline to different signs in the `wordwrap` + filter. +- Added support for ignoring memcache errors silently. +- Added support for keeping the trailing newline in templates. +- Added finer grained support for stripping whitespace on the left side + of blocks. +- Added `map`, `select`, `reject`, `selectattr` and `rejectattr` + filters. +- Added support for `loop.depth` to figure out how deep inside a recursive + loop the code is. +- Disabled py_compile for pypy and python 3. + +Version 2.6 +----------- +(codename Convolution, released on July 24th 2011) + +- internal attributes now raise an internal attribute error now instead + of returning an undefined. This fixes problems when passing undefined + objects to Python semantics expecting APIs. +- traceback support now works properly for PyPy. (Tested with 1.4) +- implemented operator intercepting for sandboxed environments. This + allows application developers to disable builtin operators for better + security. (For instance limit the mathematical operators to actual + integers instead of longs) +- groupby filter now supports dotted notation for grouping by attributes + of attributes. +- scoped blocks now properly treat toplevel assignments and imports. + Previously an import suddenly "disappeared" in a scoped block. +- automatically detect newer Python interpreter versions before loading code + from bytecode caches to prevent segfaults on invalid opcodes. The segfault + in earlier Jinja2 versions here was not a Jinja2 bug but a limitation in + the underlying Python interpreter. If you notice Jinja2 segfaulting in + earlier versions after an upgrade of the Python interpreter you don't have + to upgrade, it's enough to flush the bytecode cache. This just no longer + makes this necessary, Jinja2 will automatically detect these cases now. +- the sum filter can now sum up values by attribute. This is a backwards + incompatible change. The argument to the filter previously was the + optional starting index which defaultes to zero. This now became the + second argument to the function because it's rarely used. +- like sum, sort now also makes it possible to order items by attribute. +- like sum and sort, join now also is able to join attributes of objects + as string. +- the internal eval context now has a reference to the environment. +- added a mapping test to see if an object is a dict or an object with + a similar interface. + +Version 2.5.5 +------------- +(re-release of 2.5.4 with built documentation removed for filesize. + Released on October 18th 2010) + +- built documentation is no longer part of release. + +Version 2.5.4 +------------- +(bugfix release, released on October 17th 2010) + +- Fixed extensions not loading properly with overlays. +- Work around a bug in cpython for the debugger that causes segfaults + on 64bit big-endian architectures. + +Version 2.5.3 +------------- +(bugfix release, released on October 17th 2010) + +- fixed an operator precedence error introduced in 2.5.2. Statements + like "-foo.bar" had their implicit parentheses applied around the + first part of the expression ("(-foo).bar") instead of the more + correct "-(foo.bar)". + +Version 2.5.2 +------------- +(bugfix release, released on August 18th 2010) + +- improved setup.py script to better work with assumptions people + might still have from it (``--with-speedups``). +- fixed a packaging error that excluded the new debug support. + +Version 2.5.1 +------------- +(bugfix release, released on August 17th 2010) + +- StopIteration exceptions raised by functions called from templates + are now intercepted and converted to undefineds. This solves a + lot of debugging grief. (StopIteration is used internally to + abort template execution) +- improved performance of macro calls slightly. +- babel extraction can now properly extract newstyle gettext calls. +- using the variable `num` in newstyle gettext for something else + than the pluralize count will no longer raise a :exc:`KeyError`. +- removed builtin markup class and switched to markupsafe. For backwards + compatibility the pure Python implementation still exists but is + pulled from markupsafe by the Jinja2 developers. The debug support + went into a separate feature called "debugsupport" and is disabled + by default because it is only relevant for Python 2.4 +- fixed an issue with unary operators having the wrong precendence. + +Version 2.5 +----------- +(codename Incoherence, relased on May 29th 2010) + +- improved the sort filter (should have worked like this for a + long time) by adding support for case insensitive searches. +- fixed a bug for getattribute constant folding. +- support for newstyle gettext translations which result in a + nicer in-template user interface and more consistent + catalogs. (:ref:`newstyle-gettext`) +- it's now possible to register extensions after an environment + was created. + +Version 2.4.1 +------------- +(bugfix release, released on April 20th 2010) + +- fixed an error reporting bug for undefineds. + +Version 2.4 +----------- +(codename Correlation, released on April 13th 2010) + +- the environment template loading functions now transparently + pass through a template object if it was passed to it. This + makes it possible to import or extend from a template object + that was passed to the template. +- added a :class:`ModuleLoader` that can load templates from + precompiled sources. The environment now features a method + to compile the templates from a configured loader into a zip + file or folder. +- the _speedups C extension now supports Python 3. +- added support for autoescaping toggling sections and support + for evaluation contexts (:ref:`eval-context`). +- extensions have a priority now. + +Version 2.3.1 +------------- +(bugfix release, released on February 19th 2010) + +- fixed an error reporting bug on all python versions +- fixed an error reporting bug on Python 2.4 + +Version 2.3 +----------- +(3000 Pythons, released on February 10th 2010) + +- fixes issue with code generator that causes unbound variables + to be generated if set was used in if-blocks and other small + identifier problems. +- include tags are now able to select between multiple templates + and take the first that exists, if a list of templates is + given. +- fixed a problem with having call blocks in outer scopes that + have an argument that is also used as local variable in an + inner frame (#360). +- greatly improved error message reporting (#339) +- implicit tuple expressions can no longer be totally empty. + This change makes ``{% if %}...{% endif %}`` a syntax error + now. (#364) +- added support for translator comments if extracted via babel. +- added with-statement extension. +- experimental Python 3 support. + +Version 2.2.1 +------------- +(bugfix release, released on September 14th 2009) + +- fixes some smaller problems for Jinja2 on Jython. + +Version 2.2 +----------- +(codename Kong, released on September 13th 2009) + +- Include statements can now be marked with ``ignore missing`` to skip + non existing templates. +- Priority of `not` raised. It's now possible to write `not foo in bar` + as an alias to `foo not in bar` like in python. Previously the grammar + required parentheses (`not (foo in bar)`) which was odd. +- Fixed a bug that caused syntax errors when defining macros or using the + `{% call %}` tag inside loops. +- Fixed a bug in the parser that made ``{{ foo[1, 2] }}`` impossible. +- Made it possible to refer to names from outer scopes in included templates + that were unused in the callers frame (#327) +- Fixed a bug that caused internal errors if names where used as iteration + variable and regular variable *after* the loop if that variable was unused + *before* the loop. (#331) +- Added support for optional `scoped` modifier to blocks. +- Added support for line-comments. +- Added the `meta` module. +- Renamed (undocumented) attribute "overlay" to "overlayed" on the + environment because it was clashing with a method of the same name. +- speedup extension is now disabled by default. + +Version 2.1.1 +------------- +(Bugfix release) + +- Fixed a translation error caused by looping over empty recursive loops. + +Version 2.1 +----------- +(codename Yasuzō, released on November 23rd 2008) + +- fixed a bug with nested loops and the special loop variable. Before the + change an inner loop overwrote the loop variable from the outer one after + iteration. + +- fixed a bug with the i18n extension that caused the explicit pluralization + block to look up the wrong variable. + +- fixed a limitation in the lexer that made ``{{ foo.0.0 }}`` impossible. + +- index based subscribing of variables with a constant value returns an + undefined object now instead of raising an index error. This was a bug + caused by eager optimizing. + +- the i18n extension looks up `foo.ugettext` now followed by `foo.gettext` + if an translations object is installed. This makes dealing with custom + translations classes easier. + +- fixed a confusing behavior with conditional extending. loops were partially + executed under some conditions even though they were not part of a visible + area. + +- added `sort` filter that works like `dictsort` but for arbitrary sequences. + +- fixed a bug with empty statements in macros. + +- implemented a bytecode cache system. (:ref:`bytecode-cache`) + +- the template context is now weakref-able + +- inclusions and imports "with context" forward all variables now, not only + the initial context. + +- added a cycle helper called `cycler`. + +- added a joining helper called `joiner`. + +- added a `compile_expression` method to the environment that allows compiling + of Jinja expressions into callable Python objects. + +- fixed an escaping bug in urlize + +Version 2.0 +----------- +(codename jinjavitus, released on July 17th 2008) + +- the subscribing of objects (looking up attributes and items) changed from + slightly. It's now possible to give attributes or items a higher priority + by either using dot-notation lookup or the bracket syntax. This also + changed the AST slightly. `Subscript` is gone and was replaced with + :class:`~jinja2.nodes.Getitem` and :class:`~jinja2.nodes.Getattr`. + + For more information see :ref:`the implementation details <notes-on-subscriptions>`. + +- added support for preprocessing and token stream filtering for extensions. + This would allow extensions to allow simplified gettext calls in template + data and something similar. + +- added :meth:`jinja2.environment.TemplateStream.dump`. + +- added missing support for implicit string literal concatenation. + ``{{ "foo" "bar" }}`` is equivalent to ``{{ "foobar" }}`` + +- `else` is optional for conditional expressions. If not given it evaluates + to `false`. + +- improved error reporting for undefined values by providing a position. + +- `filesizeformat` filter uses decimal prefixes now per default and can be + set to binary mode with the second parameter. + +- fixed bug in finalizer + +Version 2.0rc1 +-------------- +(no codename, released on June 9th 2008) + +- first release of Jinja2 diff --git a/deps/v8_inspector/deps/jinja2/LICENSE b/deps/v8_inspector/deps/jinja2/LICENSE new file mode 100644 index 00000000000000..31bf900e58e30f --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/LICENSE @@ -0,0 +1,31 @@ +Copyright (c) 2009 by the Jinja Team, see AUTHORS for more details. + +Some rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/deps/v8_inspector/deps/jinja2/MANIFEST.in b/deps/v8_inspector/deps/jinja2/MANIFEST.in new file mode 100644 index 00000000000000..0d6db7c5edce27 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/MANIFEST.in @@ -0,0 +1,12 @@ +include MANIFEST.in Makefile CHANGES LICENSE AUTHORS run-tests.py +recursive-include docs * +recursive-include custom_fixers * +recursive-include ext * +recursive-include artwork * +recursive-include examples * +recursive-include jinja2/testsuite/res * +recursive-exclude docs/_build * +recursive-exclude jinja2 *.pyc +recursive-exclude docs *.pyc +recursive-exclude jinja2 *.pyo +recursive-exclude docs *.pyo diff --git a/deps/v8_inspector/deps/jinja2/Makefile b/deps/v8_inspector/deps/jinja2/Makefile new file mode 100644 index 00000000000000..aadaaa94319a9e --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/Makefile @@ -0,0 +1,21 @@ +test: + py.test + +develop: + pip install --editable . + +tox-test: + @tox + +release: + python scripts/make-release.py + +upload-docs: + $(MAKE) -C docs html dirhtml latex + $(MAKE) -C docs/_build/latex all-pdf + cd docs/_build/; mv html jinja-docs; zip -r jinja-docs.zip jinja-docs; mv jinja-docs html + scp -r docs/_build/dirhtml/* flow.srv.pocoo.org:/srv/websites/jinja.pocoo.org/docs/ + scp -r docs/_build/latex/Jinja2.pdf flow.srv.pocoo.org:/srv/websites/jinja.pocoo.org/docs/jinja-docs.pdf + scp -r docs/_build/jinja-docs.zip flow.srv.pocoo.org:/srv/websites/jinja.pocoo.org/docs/ + +.PHONY: test diff --git a/deps/v8_inspector/deps/jinja2/README.rst b/deps/v8_inspector/deps/jinja2/README.rst new file mode 100644 index 00000000000000..0684e8a1b22508 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/README.rst @@ -0,0 +1,49 @@ +Jinja2 +~~~~~~ + +Jinja2 is a template engine written in pure Python. It provides a +`Django`_ inspired non-XML syntax but supports inline expressions and +an optional `sandboxed`_ environment. + +Nutshell +-------- + +Here a small example of a Jinja template:: + + {% extends 'base.html' %} + {% block title %}Memberlist{% endblock %} + {% block content %} + <ul> + {% for user in users %} + <li><a href="{{ user.url }}">{{ user.username }}</a></li> + {% endfor %} + </ul> + {% endblock %} + +Philosophy +---------- + +Application logic is for the controller, but don't make the template designer's +life difficult by restricting functionality too much. + +For more information visit the new `Jinja2 webpage`_ and `documentation`_. + +The `Jinja2 tip`_ is installable via `easy_install` with ``easy_install +Jinja2==dev``. + +.. _sandboxed: http://en.wikipedia.org/wiki/Sandbox_(computer_security) +.. _Django: http://www.djangoproject.com/ +.. _Jinja2 webpage: http://jinja.pocoo.org/ +.. _documentation: http://jinja.pocoo.org/docs/ +.. _Jinja2 tip: http://jinja.pocoo.org/docs/intro/#as-a-python-egg-via-easy-install + +Builds +------ + ++---------------------+------------------------------------------------------------------------------+ +| ``master`` | .. image:: https://travis-ci.org/mitsuhiko/jinja2.svg?branch=master | +| | :target: https://travis-ci.org/mitsuhiko/jinja2 | ++---------------------+------------------------------------------------------------------------------+ +| ``2.7-maintenance`` | .. image:: https://travis-ci.org/mitsuhiko/jinja2.svg?branch=2.7-maintenance | +| | :target: https://travis-ci.org/mitsuhiko/jinja2 | ++---------------------+------------------------------------------------------------------------------+ diff --git a/deps/v8_inspector/deps/jinja2/artwork/jinjalogo.svg b/deps/v8_inspector/deps/jinja2/artwork/jinjalogo.svg new file mode 100644 index 00000000000000..0bc9ea4e8b4fd9 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/artwork/jinjalogo.svg @@ -0,0 +1,132 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="300" + height="120" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.45.1" + version="1.0" + sodipodi:docbase="/Users/mitsuhiko/Development/jinja2/artwork" + sodipodi:docname="jinjalogo.svg" + inkscape:export-filename="/Users/mitsuhiko/Development/jinja2/docs/_static/jinjabanner.png" + inkscape:export-xdpi="60" + inkscape:export-ydpi="60" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs4"> + <linearGradient + id="linearGradient6558"> + <stop + style="stop-color:#575757;stop-opacity:1;" + offset="0" + id="stop6560" /> + <stop + style="stop-color:#2f2f2f;stop-opacity:1;" + offset="1" + id="stop6562" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6558" + id="radialGradient6564" + cx="61.297766" + cy="60.910986" + fx="61.297766" + fy="60.910986" + r="44.688254" + gradientTransform="matrix(1,0,0,0.945104,0,3.343747)" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6558" + id="radialGradient6580" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,0.945104,0.355158,3.334402)" + cx="61.297766" + cy="60.910986" + fx="61.297766" + fy="60.910986" + r="44.688254" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient6558" + id="linearGradient4173" + x1="255.15521" + y1="32.347946" + x2="279.8912" + y2="32.347946" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.8073249,0,0,0.8073249,57.960878,7.4036303)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient6558" + id="linearGradient5145" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.7902775,0,0,0.82474,60.019977,8.0684132)" + x1="255.15521" + y1="32.347946" + x2="279.8912" + y2="32.347946" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + gridtolerance="10000" + guidetolerance="10" + objecttolerance="10" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="2.8" + inkscape:cx="137.4752" + inkscape:cy="57.574575" + inkscape:document-units="px" + inkscape:current-layer="layer1" + width="300px" + height="120px" + showguides="true" + inkscape:guide-bbox="true" + inkscape:window-width="1396" + inkscape:window-height="900" + inkscape:window-x="0" + inkscape:window-y="22" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <path + style="font-size:12px;font-style:normal;font-weight:normal;fill:#f4f4f4;fill-opacity:1;stroke:#e7e7e7;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;font-family:Bitstream Vera Sans;stroke-miterlimit:4;stroke-dasharray:none" + d="M 165.36463,80.874808 L 165.36463,80.874808 L 153.32556,80.874808 L 153.32556,81.8344 L 147.64994,81.8344 L 147.64994,36.035583 L 165.36463,36.035583 L 165.36463,20.333129 C 170.58154,21.031083 173.07533,22.077914 172.84609,23.473621 C 172.78871,24.055258 172.21545,24.549594 171.12624,24.956624 L 171.12624,36.035583 L 189.09895,36.035583 L 189.09895,82.532286 L 183.33733,82.532286 L 183.33733,80.874808 L 171.12624,80.874808 L 171.12624,102.94548 L 165.36463,102.94548 L 165.36463,80.874808 M 153.32556,55.489173 L 153.32556,55.489173 L 165.36463,55.489173 L 165.36463,41.793146 L 153.32556,41.793146 L 153.32556,55.489173 M 171.12624,55.489173 L 171.12624,55.489173 L 183.33733,55.489173 L 183.33733,41.793146 L 171.12624,41.793146 L 171.12624,55.489173 M 183.33733,61.333977 L 183.33733,61.333977 L 171.12624,61.333977 L 171.12624,75.030006 L 183.33733,75.030006 L 183.33733,61.333977 M 165.36463,61.333977 L 165.36463,61.333977 L 153.32556,61.333977 L 153.32556,75.030006 L 165.36463,75.030006 L 165.36463,61.333977 M 132.85897,59.414792 C 137.33069,63.136883 140.99969,67.934848 143.86618,73.808701 L 139.13654,77.385372 C 137.24467,72.965445 134.6362,69.12707 131.31114,65.87024 L 131.31114,102.94548 L 125.63554,102.94548 L 125.63554,68.57455 C 122.31042,71.947693 118.52671,74.913707 114.28436,77.47261 L 109.64069,73.372526 C 121.50782,67.091566 130.62312,55.489212 136.98668,38.565417 L 116.26221,38.565417 L 116.26221,32.720615 L 125.80754,32.720615 L 125.80754,20.333129 C 130.85245,21.031083 133.31761,22.048838 133.20299,23.386383 C 133.14561,24.026183 132.57235,24.549594 131.48307,24.956624 L 131.48307,32.720615 L 140.77043,32.720615 L 143.60824,36.733469 C 140.68444,45.51526 137.10137,53.075692 132.85897,59.414792 M 254.11016,49.469901 L 254.11016,49.469901 L 254.11016,20.333129 C 259.21243,21.031083 261.67755,22.048838 261.50562,23.386383 C 261.44823,23.909869 261.04699,24.346044 260.30172,24.694917 C 260.30164,24.694986 260.30164,24.694986 260.30172,24.694917 L 260.30172,24.694917 L 259.78578,24.956624 L 259.78578,49.469901 L 277.15652,49.469901 L 277.15652,55.227471 L 259.78578,55.227471 L 259.78578,93.785712 L 281.45616,93.785712 L 281.45616,99.63051 L 232.35378,99.63051 L 232.35378,93.785712 L 254.11016,93.785712 L 254.11016,55.227471 L 236.22346,55.227471 L 236.22346,49.469901 L 254.11016,49.469901 M 225.5603,59.327554 C 231.12111,63.107798 235.62145,67.876693 239.06127,73.634235 L 234.76157,77.647079 C 231.60845,72.180322 227.82475,67.934848 223.41044,64.910648 L 223.41044,102.94548 L 217.73484,102.94548 L 217.73484,67.44049 C 212.91919,71.627831 207.70222,75.030021 202.084,77.647079 L 197.87027,73.198053 C 212.66118,66.917101 224.01239,55.372897 231.92377,38.565417 L 205.35172,38.565417 L 205.35172,32.720615 L 217.99283,32.720615 L 217.99283,20.333129 C 223.03774,21.031083 225.50291,22.048838 225.38829,23.386383 C 225.33089,24.026183 224.75765,24.549594 223.66837,24.956624 L 223.66837,32.720615 L 236.22346,32.720615 L 238.80326,36.733469 C 235.13421,45.51526 230.71987,53.046611 225.5603,59.327554" + id="text4761" /> + <path + style="font-size:44.09793472px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#b41717;fill-opacity:1;stroke:#7f2828;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Candara;stroke-miterlimit:4;stroke-dasharray:none" + d="M 149.14708,37.774469 C 148.97807,41.117899 148.84526,44.824225 148.74871,48.893456 C 148.67626,52.962754 148.3818,70.641328 148.38184,75.524422 C 148.3818,79.065795 148.05588,81.991266 147.40406,84.300835 C 146.75219,86.610422 145.72612,88.557071 144.32585,90.140779 C 142.94969,91.724494 141.17522,92.901283 139.00239,93.671139 C 136.82953,94.440996 134.22211,94.825935 131.18014,94.825935 C 128.83828,94.825935 126.73787,94.59498 124.87889,94.133049 L 125.4221,89.31593 C 127.13623,90.0418 128.92278,90.404734 130.78177,90.404733 C 132.85805,90.404734 134.66875,90.140782 136.2139,89.612876 C 137.78315,89.062981 139.02651,88.216133 139.94396,87.072335 C 140.8855,85.928548 141.54942,84.520804 141.93572,82.8491 C 142.34613,81.177412 142.55134,78.988811 142.55136,76.283285 C 142.55134,66.297119 142.62852,44.659257 142.26641,37.774469 L 149.14708,37.774469 M 166.38498,80.732697 L 159.83024,80.732697 C 160.16821,76.333498 160.33723,71.307412 160.33723,65.654424 C 160.33723,59.2976 159.91471,53.963567 159.06973,49.652319 L 166.31257,48.761483 C 166.02284,53.358679 165.87799,58.98965 165.87799,65.654424 C 165.87799,70.933479 166.04699,75.959565 166.38498,80.732697 M 167.90601,39.490159 C 167.90598,40.611994 167.5076,41.590815 166.7109,42.42662 C 165.91418,43.240515 164.79155,43.647442 163.343,43.647399 C 162.11172,43.647442 161.146,43.295504 160.44588,42.591595 C 159.76988,41.865769 159.43188,40.996927 159.43188,39.98507 C 159.43188,38.885304 159.84231,37.928485 160.66315,37.114591 C 161.48399,36.30078 162.61869,35.893853 164.06727,35.893811 C 165.25023,35.893853 166.17975,36.256783 166.85575,36.982609 C 167.55588,37.686526 167.90598,38.522373 167.90601,39.490159 M 206.72748,80.732697 L 200.13651,80.732697 C 200.66763,74.947749 200.93319,68.634899 200.9332,61.794122 C 200.93319,58.406756 200.1727,56.097177 198.65174,54.865371 C 197.15487,53.61163 195.00619,52.984747 192.20564,52.984714 C 188.77731,52.984747 185.61465,54.117535 182.71753,56.383099 C 182.71753,63.883761 182.76583,72.000287 182.86238,80.732697 L 176.27142,80.732697 C 176.68182,73.254058 176.88707,67.843042 176.88707,64.499632 C 176.88707,59.352589 176.3559,54.359493 175.29363,49.520339 L 181.66734,48.695493 L 182.35539,52.720761 L 182.64511,52.720761 C 186.21823,49.773323 190.04483,48.299592 194.12499,48.299567 C 198.13265,48.299592 201.23499,49.113454 203.43201,50.741118 C 205.62895,52.346863 206.72747,55.217334 206.72748,59.352563 C 206.72747,59.770507 206.70331,60.595362 206.65507,61.827118 C 206.60675,63.058915 206.5826,63.883761 206.58262,64.30167 C 206.5826,67.975018 206.63088,73.452022 206.72748,80.732697 M 222.69791,48.695493 C 222.28747,55.514282 222.08225,62.355041 222.08225,69.21778 C 222.08225,71.043461 222.14262,73.463019 222.26332,76.476468 C 222.40822,79.467925 222.4806,81.502559 222.48063,82.580363 C 222.4806,89.685068 219.51105,93.996287 213.57195,95.514024 L 211.76124,93.006484 C 213.90995,91.356766 215.2378,89.597085 215.74478,87.727431 C 216.49321,85.043912 216.86743,79.324953 216.86743,70.570535 C 216.86743,61.178248 216.3846,54.16153 215.41887,49.520339 L 222.69791,48.695493 M 224.2551,39.490159 C 224.2551,40.611994 223.85673,41.590815 223.06006,42.42662 C 222.26332,43.240515 221.14069,43.647442 219.69213,43.647399 C 218.46084,43.647442 217.49515,43.295504 216.795,42.591595 C 216.119,41.865769 215.781,40.996927 215.781,39.98507 C 215.781,38.885304 216.19144,37.928485 217.01231,37.114591 C 217.83316,36.30078 218.96785,35.893853 220.4164,35.893811 C 221.5994,35.893853 222.52889,36.256783 223.20492,36.982609 C 223.90503,37.686526 224.2551,38.522373 224.2551,39.490159 M 259.60008,80.732697 L 253.91446,80.930661 C 253.62473,79.852857 253.47987,78.830045 253.4799,77.862216 L 253.11774,77.862216 C 250.14817,80.325772 246.10427,81.557546 240.98606,81.557547 C 238.20962,81.557546 235.8195,80.820682 233.81563,79.346948 C 231.81178,77.851221 230.80988,75.728607 230.80988,72.979099 C 230.80988,69.591724 232.37914,66.875216 235.51769,64.829574 C 238.65625,62.761967 244.48667,61.67316 253.00913,61.563165 C 253.08155,61.035275 253.11772,60.430386 253.11774,59.748497 C 253.11772,57.043003 252.32104,55.239336 250.72765,54.337474 C 249.15832,53.435661 246.76819,52.984747 243.55721,52.984714 C 239.76681,52.984747 236.03678,53.413668 232.3671,54.271484 L 232.9827,49.718301 C 236.60411,48.77251 240.76873,48.299592 245.47658,48.299567 C 249.77395,48.299592 253.09359,49.113454 255.43545,50.741118 C 257.77728,52.346863 258.94819,55.096363 258.94824,58.989625 C 258.94819,60.023469 258.88785,61.904117 258.76715,64.631608 C 258.67054,67.337133 258.62228,69.140806 258.6223,70.042632 C 258.62228,74.045913 258.94819,77.609265 259.60008,80.732697 M 253.19019,74.331856 C 253.06945,70.988469 253.00909,67.986016 253.00913,65.324484 C 248.47027,65.324498 245.01786,65.632443 242.65187,66.248318 C 238.69248,67.348131 236.71278,69.448748 236.71278,72.550177 C 236.71278,75.541643 239.03044,77.037371 243.66588,77.037366 C 247.64942,77.037371 250.82416,76.135534 253.19019,74.331856" + id="text3736" + sodipodi:nodetypes="ccsscssccscccsccccsccsccsscsssccccscscccsccccscsssccscscccscccsscsssccccccscsccscsccscscsccccssc" /> + <path + style="fill:url(#radialGradient6564);fill-opacity:1.0;fill-rule:evenodd;stroke:#323232;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" + d="M 105.45673,18.675923 C 105.45673,18.675923 88.211949,26.918461 74.172834,28.737898 C 60.133727,30.557333 33.360434,32.377571 28.045622,31.093256 C 22.730818,29.808941 18.915645,28.309196 18.915645,28.309196 L 20.021441,32.056583 L 16.609513,35.052471 L 17.2144,36.121726 L 18.61792,36.22764 L 22.92773,36.762252 L 23.532621,38.688909 L 25.937975,38.905784 L 27.143021,42.970927 C 27.143021,42.970927 32.254764,43.399628 33.758953,43.399628 C 35.263142,43.399628 38.271966,43.187802 38.271966,43.187802 L 38.371202,44.791657 L 39.477002,45.003495 L 39.477002,46.824227 L 37.066917,48.967759 L 37.671807,49.073671 L 37.671807,49.820127 C 37.671807,49.820127 32.255457,50.252157 30.049301,49.93109 C 27.843157,49.610006 27.440747,49.608286 27.440747,49.608286 L 27.242258,49.820127 L 27.143021,50.783455 L 27.643946,50.783455 L 27.84242,54.959544 L 38.976091,54.530844 L 38.172728,68.980747 L 38.073481,70.796442 L 28.645781,70.261816 L 28.546544,66.408513 L 30.649462,66.408513 L 30.852673,64.910557 L 32.757107,64.481857 L 33.059555,64.058192 L 25.937975,62.343374 L 20.522364,63.947229 L 21.42496,64.698732 L 22.327572,64.698732 L 22.426809,65.984848 L 24.331254,66.09076 L 24.331254,69.838147 L 22.228335,70.372777 L 22.630009,71.225146 L 23.130934,71.547931 L 23.130934,74.437917 L 24.435218,74.437917 L 24.435218,87.813529 L 22.327572,88.13632 L 22.630009,91.989617 L 23.929569,92.206492 L 23.731093,100.98236 L 29.449141,101.08826 L 28.244105,92.418334 L 36.868446,92.206492 L 36.268285,96.912181 L 35.464925,100.23086 L 44.188501,100.33677 L 44.287739,91.777793 L 50.303506,91.243181 L 50.005786,96.700351 L 49.802585,99.90807 L 54.920484,99.90807 L 54.717274,91.132217 L 55.421397,91.243181 L 55.619882,87.067076 L 54.816521,87.067076 L 54.518798,85.352258 L 54.017874,80.429702 L 54.216359,74.760706 L 55.31743,74.760706 L 55.31743,71.336105 L 53.913913,71.442015 L 54.117112,67.402096 L 55.747469,67.240708 L 55.823083,65.929374 L 56.749319,65.793192 L 57.699176,65.071956 L 51.985842,63.896802 L 46.31977,65.15265 L 46.872668,66.060507 L 47.47283,66.010066 L 48.172228,65.984848 L 48.299828,67.639144 L 49.878196,67.563497 L 49.906548,71.144447 L 43.111042,70.988097 L 43.337879,67.160002 L 43.559978,63.679927 L 43.559978,59.105378 L 43.763188,54.288748 L 57.373101,53.592733 L 73.567955,52.659674 L 73.71917,55.736265 L 73.142647,63.120082 L 72.892183,69.9945 L 66.928387,69.888585 L 66.900039,65.071956 L 69.106918,64.991267 L 69.206169,63.629486 L 70.108765,63.493308 L 70.061506,63.226006 L 70.964116,63.175568 L 71.465028,62.504773 L 64.721507,60.926122 L 58.001612,62.368592 L 58.4789,63.200785 L 59.230285,63.1453 L 59.230285,63.523577 L 60.156518,63.523577 L 60.156518,65.046738 L 62.136575,65.071956 L 62.112937,69.298485 L 60.109259,69.298485 L 60.080907,70.261816 L 60.785031,70.342507 L 60.70942,74.009202 L 62.188552,74.089909 L 62.013701,88.620507 L 60.057282,89.018952 L 60.080907,89.714967 L 60.761406,89.714967 L 60.761406,93.437137 L 61.886113,93.437137 L 61.588391,98.52109 L 61.210343,102.95945 L 68.331912,103.14605 L 68.105084,99.29275 L 67.580538,96.085028 L 67.476575,93.300955 L 73.520696,93.195041 L 73.345845,97.502272 L 73.317494,102.05159 L 76.729426,102.3189 L 81.3653,102.1323 L 82.820807,101.70358 L 82.017437,99.26753 L 81.818959,95.439438 L 81.440912,92.710853 L 87.206218,92.499027 L 86.955759,95.842931 L 86.932133,101.08826 L 89.238253,101.30009 L 91.520751,101.24965 L 92.621828,100.90165 L 91.969693,95.923633 L 91.747577,92.176239 L 92.725793,92.070324 L 92.749427,88.726422 L 93.02352,88.670945 L 92.976244,87.949712 L 91.846823,87.949712 L 91.619996,85.488427 L 91.520751,74.811143 L 92.371377,74.785924 L 92.371377,71.280616 L 92.725793,71.336105 L 92.725793,70.640088 L 91.468773,70.529127 L 91.497126,66.463987 L 93.600043,66.277382 L 93.477182,64.910557 L 94.403419,64.829863 L 94.351424,64.562549 L 95.580099,63.947229 L 89.337489,62.69138 L 82.995657,63.977495 L 83.39733,64.723951 L 84.375543,64.643256 L 84.427528,64.966046 L 85.254515,64.966046 L 85.301775,66.569901 L 87.357445,66.544681 L 87.532293,70.478688 L 80.264217,70.423216 L 79.413593,64.512124 L 78.733106,61.380041 L 78.184923,55.761484 L 78.510996,52.473053 L 92.999878,51.373557 L 93.047136,46.476221 L 93.774891,46.289613 L 93.727651,45.543159 L 93.174743,45.220372 C 93.174629,45.220372 85.252181,46.395266 82.745197,46.66284 C 82.0389,46.738209 82.09239,46.733258 81.516524,46.79397 L 81.440912,45.886118 L 78.444837,44.317564 L 78.482644,42.491786 L 79.512842,42.461518 L 79.588444,39.949808 C 79.588444,39.949808 85.728225,39.546834 88.009582,39.0117 C 90.290937,38.476559 93.524432,37.942456 93.524432,37.942456 L 95.055545,33.79662 L 98.089437,32.913987 L 98.339888,32.217972 L 105.20628,30.316548 L 105.98602,29.676006 L 103.37744,23.976741 L 103.62792,22.690624 L 104.95584,21.994611 L 105.91041,19.079404 L 105.45673,18.675923 z M 72.466874,40.403728 L 72.429067,42.476654 L 73.983813,42.542211 L 73.884576,44.509221 L 70.836515,46.506487 L 70.647496,47.081457 L 71.876167,47.091543 L 71.866712,47.575729 L 62.552432,48.029652 L 62.613863,46.652742 L 63.039175,45.966809 L 63.067524,45.528025 L 63.07698,44.579832 L 63.341609,43.949374 L 63.440849,43.439982 L 63.440849,43.076841 L 63.842533,41.47297 L 72.466874,40.403728 z M 52.987688,42.168984 L 52.760853,43.561027 L 53.488599,44.418431 L 53.441349,45.916386 L 54.117112,46.960408 L 53.942262,48.191039 L 54.443185,48.912273 L 44.939872,49.2855 L 44.916247,48.967759 L 46.017333,48.831579 L 46.069307,48.428097 L 43.66394,47.121797 L 43.536351,45.03375 L 44.689411,44.978276 L 44.788661,42.72883 L 52.987688,42.168984 z M 67.051262,74.276518 L 72.81657,74.649742 L 72.618099,82.411833 L 73.36947,88.776857 L 67.254465,88.565018 L 67.051262,74.276518 z M 28.44258,74.599304 L 37.671807,75.078442 L 36.868446,80.429702 L 36.868446,84.928593 L 37.520583,87.440302 L 28.494569,87.869006 L 28.44258,74.599304 z M 87.508658,74.649742 L 87.508658,87.924488 L 81.644113,88.353194 L 81.440912,81.342592 L 80.788764,74.811143 L 87.508658,74.649742 z M 43.087416,74.947312 L 49.906548,74.972531 L 49.977434,87.278902 L 43.611966,87.389863 L 43.285891,83.400379 L 43.262266,79.441156 L 43.087416,74.947312 z " + id="path4735" /> + </g> +</svg> diff --git a/deps/v8_inspector/deps/jinja2/docs/Makefile b/deps/v8_inspector/deps/jinja2/docs/Makefile new file mode 100644 index 00000000000000..52d78d9efe6271 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/Makefile @@ -0,0 +1,118 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp epub latex changes linkcheck doctest + +help: + @echo "Please use \`make <target>' where <target> is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Flask.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Flask.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) _build/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/Flask" + @echo "# ln -s _build/devhelp $$HOME/.local/share/devhelp/Flask" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ + "run these through (pdf)latex." + +latexpdf: latex + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex + @echo "Running LaTeX files through pdflatex..." + make -C _build/latex all-pdf + @echo "pdflatex finished; the PDF files are in _build/latex." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/deps/v8_inspector/deps/jinja2/docs/_static/.ignore b/deps/v8_inspector/deps/jinja2/docs/_static/.ignore new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/deps/v8_inspector/deps/jinja2/docs/_static/jinja-small.png b/deps/v8_inspector/deps/jinja2/docs/_static/jinja-small.png new file mode 100644 index 0000000000000000000000000000000000000000..455b4c3c99cb843696c61fd43785ce74f486f6ea GIT binary patch literal 10484 zcmV<QC=1t#P)<h;3K|Lk000e1NJLTq006WA006281^@s61QBJ~00004b3#c}2nYxW zd<bNS00009a7bBm000|)000|)0Uukc$N&HU8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H1AOJ~3K~#90?VWkBEJt<bf8BR|yWV?R0f9EK*g*$`SqJcd4Z~m@ z3@~5<hA@i)6PTI7Fn|qSW(+oAn<0k5Hh~u~n_&V7WX1^0B9;L|3NQjj2n$ItXn8_U zdeXbwyM259$jrXk*_m0HRn>j(ed;fwqN_5iGqb9`I(72o$&;Oazt0p?>_mvj9PnJ= zKLH<8O8rh}O6Vze8bst2;AOxofx9_be>l4}O`2kga)`(|z^i~?1NH*+`+Yi{jy<I} zmC#dcYlz4U@Km%!ehSb9O=P>x%*@!+FPaj1ifsuIxf@y%zXUkjrn^K2=yW=CyIp{P zQA%AfRXL{EHV}~x@FcV}emXD<qa=3Ws2d-_{$YwKwh|(88t@|ErNH?%Jw;>-htMAe zn5rC8Y#Bu4alj?OGl6-VzOk{9Br#|r+YR`UQtCXIO6Vyj3=ug9ya26M+}Eb(NMnm` zx9dvg+kiLOgDIh>7(0yJ_?Llap>^aUY3#$0xevS<c&k$Cwn5%h<(Og|5Rn6D5<eS{ z#3j<$hhx>aMk#f^pFfq*Q?v#{;-{lY{A5}paRmPoN}NjQDH=gU_OnUiUu3)_2CxGu zYrr{5sT<t9sf3=Q7!HY_g(mTW%^MeqU6|qSd9*Lb>)gDlgq|V`BC?N768{ntCb0`g zkG>5!TPby?&6`T-DWVXOdjd}co`IIci#Bi4Bz9rz?Q}Y)pohl))aFek^b`Y#$XUP> z%=Sd!JU4$UN$kUs($@p$E2Y+9DxvSvFhkCdXOqMihR)kABzECQ>3agt0p14zLW{_m zN~s$s-M}fv3lTX3cp&f~^DDpufKKTAD*uV?Kyb$w`ZqCz9#J_&<mJGB2Hp?6Un%wd z3Ab~K1`v@&;QkDg_d&qvvHv*3$Rk88J)SfiU+5<*r9K@Z^x0^=#Nu0Ma(_@M_2Y@O za*7~Cqzjw@+yl6~+3w-|o(-G}%!K~$5;sNSP?~$Zok;EI(w|mJJvrDZ6p?>Mzu7MO zz}JBP4SW(%N~x{??wlkrlJ#y36V&~^J8%{m{T@qJ*CVZzw57*eNoq$*KQu<@Hv|90 zQM`xVXoH)9e?XJ_GNshfF}8WfLPT~0hs|~dak8G8Q=U)OP`X79i9=~kh`bfVcD(Q2 z7bEmzfy)7Uz1|?*^Fejd1HOtT_a~K7-x^QTw+<pQ3mifd)Bd_!ENwAVUI^~J_3yR2 zA0tgk`Z%TYBnMbLK7Kq#=o#P$aL^@mMu8SLu&J7S1-Nmm)e}R=UBGVhy9+&Y*d=0$ zs0ZrS9V4or?jwg7O-LL{Ym3O^Aa(5PWbntpYoh}zBJx4tIl2C-333Ch0zW}_Hm?VM zY|>7kolH(d_UG6npS$g@k=EGe9=f5SG}B3m-HQS0JoMCm+9I)^ww0t#@T!kCSbPfj zgi`7&kbcO24?HK==I9&Wy@o8H=ZcN^uTB~%y)*T>PnFX~TSBOq6grY0Qb}^sV&}y2 zOY-VC?<em2&A?@75?`j2y4gP=Md;fig=Qf%x>QAy5d8CErl;6()j3t^C2cMFPwrPQ z^`98IL+N#h?GrLZ)>vLHsU7p1ixX&-^;2g1W|it1)1jXYya!n5_xo&YY*aQVr@<xr zC3$s@V`({gIr&XdMS`ZP$0T&;67|EeR9uJF4n75ZPAPRq@{b-u11qlv{>bKScQUt0 z<hG@>|KBD$WhsGU>1`3YDX9}*>+_XT*Nikg69bjQj6%H~_;si^c`<}iGH(|tokQSK zlINaZr91oQRH+^@)u8maj_&O5M>=@7p>^YrHX$<@DQt}YA3!^BZ+kM2i^$`W&~0E6 z){Swh8Ft^%lfR2rEj|f+*}8u=MdzZ6E;=3f1aL8I4>FHS{U}LGrRV0?MP2>54PuX5 zcQ*IzeM+h42QHy8W`RqAhr{rwU=vJ&%qO#yZff0VqITq}5~;i@H3e;O7z<{Bzs3-{ zZR~U@Jq|s(3U&48rncu2ojCWr$cyk(;4>Asp*Fg}p95Ed_FA%Ax7!_T31xl+Rm7;X zUnehi9QuFNd7-@A`%#LV@>6Y2-5;BldtCAy3ijWplxl7|i=eBN>Y<~ee+TV@v1S`! zH<#Ql;eDjYmCA2J{b+LRUvDfum*jP~oU%*W+~hd4pUTVqKVwukfat)=9^hWUxxhJr z-$O9!iI=<IHkotI%Q-)lmXkLQv^{|0r%l4L;p~4?DRt3EgIYvz=vygu9QdBuMj#?z z1s>7qj1FrSyr~l-cFu7uEtOt+P6{#Ny5+|1b&0NQ({gp<R9;TU(W`r`(izNp6*cfN z2uoaxaS*#M>2uYMP+F|LIqE_Qa>~qAF+ypn`{y6mRWthi%`gYn{Ogc;9X*wAtf=c! z`ElUQoleJ{{sQQ?v8HZymzC1vlFl}*O%t6>a|xSJi;e={1g=02o{V_MwT;?=W<=x* zz{Ai!$Kk?jb|A*BOXm`KD|P8o-8qzAXC%-isV`|OxN;+#<h=s8LMe4^>JQ^U<H$)O zvKKfLJzws_=!u-V@Ct&K5W7w}Z5&snm!1<s&NFC}#JygRUavPo-i&ti(RSo(%yu=} z$^HsI3g}j$)lgs}@)zh>V?~m8-1~Ko>!!Dbn02Dp>#?!1F|;i%E{>Q{Q^a0bSz&!W z=sK~1RugUjeuR#}e2ugHNGY|_<hsU<wnKqkiXpT`lTKh8`&D^W=jWu?J<lhdPgH+P z37?QY>>mgI|7Nt`@e#A#YQ`mYEZQdYXMh#VOy@Q_fm<!5$E8QFuCB7azMf53gImxJ z@Yey?0?$LMdqZui>I6FYqEhM$wJ%~DqE$jGrItkGv*@L42iR%EcGGOf2()WJTe|ep z(vnZ$K5!G7sMk5agMBkTFo1|W2{^OU=>&<JvPB!LRW@{oVI0v%FU2FY#a0p9@AQN^ zL$S?bVPSzid-f#ToQw7KbzAoJz>Cp@y<RD`T3D1`n_+Ej;B(`;CHT_%#EBDZY;3qA zyi-DF;8Ng!rgS&A15?srgiSklE}55>mSP>C94w;;pBAjWy@bHAb5EQ&VRtW6N`1O+ z<1o)}b#?PS*t>UcR9c@nal&^Io)WqWN~s@<$hUz9$4o&X9}xH9r~C;378e%@5iGRt z{Qr8it@KuOPd@c~D{xdPwIU*)2maOU>};`)9fR|NeXjV=!}zagbLn2-<-lp~{Y(t7 zya{c|xVmBe+YaLjc>W6DBH&=fw#!um64(HaqXW+@hB`S_>J0F7rPQ};T+4-i`|6O` zC6rUv|EtlS0hChjwfiFSeBiXu*Dr8y->sBtRQGnpSO~3@`V#P^s`Er-fnkF029|(3 z-AyUAWX^vldTr<;{n?;sI=q9o0dKzZ&O149-~h9;qqnTYoY(L7t%UBOU3S~Tep^R{ zbOv}7X0i8^4O_Y-h3N#{Xto^O4x62u+1c44f>~m1Y-|iVUe1F&R<n(75s`=V`~Byv zt*s5}t821bU0t<p`g^6+t+fhqAR>1I-U*!Mwku@maN7KLBJxGx^+x!N4JJ<F{z*lr z-DzvA{1h0n+lC0%@Ap|*85z*J(@!YC_iY_b2VI;Otg-$oA}<2|1URMB>9A+dp5%ba zzJ2>xU0r2mWrcpf|5)IQBJxS#4NW4(nlJ6Szy)SI-}yZU=$fzV8^=xT9YtuR)Z0bm zz39=%KeXw-n&BMZgdPn$-f+lvcc`9*sBK{ok$VAufnI)?`T6-FGTZirNWHMIz}(y% zYin!vYw=XzDI)TBz;k1vwv5k?C8|%_dr<9){0j6?`rGPVbslyUp#hXqM@8fZ_Bcc` zCv90N_1{~lb{Vw2clYk7b72<A*VYC+zNJn>MC7Gtsl2D#?XqXj9_Hught8>T2kmyd zEG#TAKR?gv>ME<Ns~z-8Qon1~>pqWB(w>oOmlY(gK<RWk{wU}vb*{MtJC4u>2mJ(> zOioX~c1&jvZv!q_TU$G0d3l*#yLOG#nS-^pHB0Ctz+aU%LPX9)>&x9vr^BvYyI5Gz z3%aI;yJP#HO`x_<LS%-Lg&n-{Cr(E!I79BI=uYs7V8Jb7#}QgY{w?r7fXa!wz(EoD zOSJCxle%>(rEU|EHvxaPy1E*b(p3a^;sk!YN|!XD+4cbY(01m1z%wzQ_I|(5@#Du? zTwENIiaA8~_w9du!ur24()#xxl7FA3VZL&(qX;b`F9Y5I%*@Zvv$&{Ndk7I}adFXh z3||C1S47?pyrpTU#Gj#4BBh;&d$EYT3r$j&uzQ`f-4I?wFjapEk?p?KzhgwLB5f+o zK`kp&DRvB@MdbH^-vUimt2f4UX!|){Sy^Fqb#)PV4e$~X`E8}tJ8CrH0$X;9#Hl>z zfj`J>NrXP_AwAVEzYeKW`R=vVO+%|3W5>29v{@VFUBG{UYBIZ+nVDg3ZjSZ!bvxXC z5O}eOd<iW9?Y7H14y8W~V0n3&-Me?!vPaL&&9QIazMvlJ#77=^X>w<!loHuu%tW>r zTW}c{V<WVPJQHo2x)$yFSp3N`QBgkvJjJHX&CQLRmm;>EZsg!0=xIZ>(C_!T{r1~A z_0;exY$<{-E-nt#B)7a+J${)n(rrf~2hw(~gcgxY(7kat?I@buN6}N-Pnh4({tQ~0 z9s=$gsv|_45V1p&&;M+@?v}o^3`tTLp55NWCLNQ+K8bBw$crwNw~dHwv2zIxJPt-~ zy7kv+6;;G`rDBMHDd}FN(!_|IOYl@DDu=lK@iry0#g0}v&I0`q=H})qA8?E;u0-_r zbIYicpCWci{Z&Zm$yLO4|Fd0*?A?<cEur_JFJNHf@}*;lKsk9X#!c*!yqtEpn1pn$ zlV3twKW2x&d6)>HTMc92!gY*yZMSU_JLmj1^6MOzBJ><mw%|}+jvYC4u8q-Q3F;Cx zM%qI3lu{G1sExCd5&z@55-lZF+m^^x`E5w)5FvbNY|}#0+HP*J80$_Ononr=mzS4W zTU#3;YYp_#o%^4meNw7^#~uRAVxok$Xp`8fygE`kRc75zq6=3F+WnA(bn`=!Glc#7 z_p@u)uBeJ)LnnCXp{JasNa?2?-LCOFwuI0YTS4qlT8_@{5)DXxo_#X9`z@f4o{o0A zvz-N;Q`PZv>VGQmLo_*Wt6H6PCt+fQo)mdqxSv$}>!+o3#VUzd0~=_chGPs9(%(|~ z$NVK7MC6UY@5WRa_uA1C`LgXQVVh#&5;`RL?5`(9k{zLLx0`fMI(F=s8!n(J*3s8v zw`1P1O{*g0onw-QcR)Ifu5({+K3T~O6CiY8=(!b%S2YCg)NvhJM~o4%GJU%TD8wOj zhPv_73Ab&c7$>3cgd};_&@;S`o8P6taVYPBzzeHX5vSfKD5ZYX>{7NpMkF)@)eV>u zx;8Eao`9adG(yt4pFQIf{lZx1&BA%OO(w_g6buQEkd$_gw?!Q1abawPR!aSp@koO~ z{0SE^-5mV_o$MHy#A^{C_riqQv?Zv!XwX(7NN5XhfTE9?;ChndR0zimv~cpy;v~jS zB=iBu9%^1XnQW{HoCd?LvOXO5gS$<rO<RJUNa&%ES0UWR)C~woXdhDvy?rsFawPQU zyF!N$DhO>~esiA=i^zQN#FGFg1)+W11ts)A<#2IV)T2)-LI*t7?g|}3$WS@F{3)Tg z5h^9LgS$e95IjQ1?#-0Y+Y^;mj;7A`I}kyY!-tc9(poyUeO!(JN+k5QgNV$bgUmd_ zY!62}%iAFFUoMFoZK8rM*iK){PsTCbowO2qM-kct&)&V0$ZSI2{ydumz6i69qlZVK zqdWCQ<a=xq{EJGd>k?IDNoVhzDWP*<0>iBM{<<w<TT5pV*^idEk8rk;FtqdY^DHbZ z47Ogc$I8lz4O`af_xtw;?hm}ol=~A5xy|pD&OOfR5W<jzen5lE+}3DJXc0LNcpC6; zfX4&NBJyS6a`XfEhEl4n<-_*poHVY9zbS~wWxy}s^-E@EW(KiAEP>r%HTKtr=-t@Z zU~O&9lAG1lReHVNJ%E32Hh|kj<SW3(fFFY<bk%h`%Xd6PL>8O6#hM7I7XlAPFDr6| zQtI|5Wi~}Ip-rs0N3%&{KeEw0dWw%-d~;buzJ@0F<-k|{6;TVYoh9_&08awlzPY(M z&N}O?;37j1pXJ@@bOyo5ZQA<!I(OW02gi>ew{D;ZfL}s~70y;UI-L%)v$L$Pug{}Z zpReRzoQQPL0V^Md{t7(|cqlqz>fjPY<cGjF0A+r!P)gNZu&EeXgcgy*==gF^1AY-N z5__kkgTT0w$C<*k3oW@HZ%$tq5rvlCmjhoa2yt8D$1!QVA9y8jalfw@xjb^@2=nvv z96o$_=)_cb0zYkIV}s+zkF&hI?9WO3Bc;?QMdVd5Oz4!PHiQPP*GCC0BIlw>Yd0Yf zK)%z_tJj*jlO5pRW_zw}j#+WRCHXg%Qezp#O%hr}e%&<ek$9g{+pG3%pRSLEl}_GF z=2<rN%U7c>_)6gO==JkbYY8SLw3ToUigbCW1NTU?JpjG9&)1#pN~P4u+%Bb5UqoI3 zyb5?;zu#wRX(<^hAcxp%Yirzo`_NbX2fzn`-!sQi*U?a!b8~YxZuTSnJP|n`E%hG` z9QO0O-R@xNWxMa!WtU!do_7cMIkWw$IekM!u0j+1^T1!V)$gj3(3hi0Hz2d;7mx*e z1MGk7vTJ5)RtG%`<?c8O0Ywlbv`=K`K}J7yBC?2<I*)X=Q1Ac2W_z(YUJ;RRp~?LX z^Lv$2YC|dYbrE?XI{feb(81Y*$O#GqLo-dj0K69Xz8A@L7i7BvxKr1DK5*(az?nE? z?smKE+O><BnVBK8U?^aXZHFt!>^1ayJvW3x7dRW;2l!~4Mua4xl~Ugkk&A(kqV?vH zz$ZQeQgR5rUe8@P$HgTXtD6oHI#!o+lW(&-BC-dq^FInr-iM-BrX65rW`_Cs`N1|f zH#can-A<f1v4|#vONcv+8fB&U$AA^k>#E#%qh5y5>c<BTY<{_YG9Nv9bZCu5hXhwD zrM?!pCs7F<f<w%Eh{yrA6KB3MuLVA3(>69XSYBRcZf<U{o=XYrSH^Qk_*39dlu|z{ zF0cZXI<!*ih=}|G@OpGd;%QAPbS~^#;4<LLz*m$~Kg{@hR;MsTXmps~-$Q@;04=R7 zaTgXAM%E9vDwV&=lqL7->MG02%k=yGeds>I!_hD12@wG_A(obwhT3Kc9g@t;%geFZ z>al*tkc3X@&o*ynX2!lK7ob1;11O~~6Op%~UzA@17JaoT$BGo04t6c@U8U3;DhsHI ztWKd>@%2qcT0hMXno{LMVB!7mx0F)vD{OC;r=3&BfY3^*HPa{f3~2q{N{{{f!`47| z7PVXF!omW_jvZrpdC2$ad%&}lQg$V&@1r}?v*<6u!-3b?a{YP&xCFQg?E-t8_*}qU zv9`zH5c=MDga%Mby#_!;-U2)wct=&|H3qLEd$CgLNaddzpww(PSX<A3V`C&+U0scp zb!}nYkhx5o)3zb0rj+_e5&01C9Q!GcsVpfy-C8jWu13Ekzv-GI&8Pey?6HW9+=vQd zf6;k4QW&TlxukO}BKJ-etd#nR8PuWI>kWNrLSHTmvr=uS?laI!+KmmRguWKN2;(Ve zY5B|OUVR<Z2`w-pp+izMqH<Wg61_&?&w$VN`~7_}N$i$y_uXE+!Np3cP(3~dCV@=i z<vNaybPB7=cdQdhLL02W=5o}5GEyDcC3K+1o6)QKjy=Fl5ur_Qbg<T(h@6u;-@DM+ z79#d!>ChpS!$K)_Gp5rcB1eFIx7~KzAQ;Quy?a?$SQsJl`uaLI-E>o^y4*p7AVCwK z?OZ|^neF=hzDvdnGRnUKcm|q`YryHidoy0jzEWyWM)|E`EY3+06=VVZ#x}u#&?z;? zxv^CX6&(T2a~Ha|L(sMyTZa?<X$kl)Iz+?u1utn*LI=`|By_55KdnSUE2VA$K5mcA zYPLC;$PyGjS|xOdWR7mvDxpL5y1xcheg$tu`v%_$yaY4Y5K}!2E>lYVN{zNh3F;Eg zskf1YP7&K#8TM2WIVDqNvZ#uPWw9mk^I9QvrZZ3*0ZxX7zBBC5NeQi#`d@b6m?lR? z&jK$=mWm-FhZtU5^MQf(rBJLx=XMIM+TmC_zMfeZXp2j~-|u%~<s0mL30;9U2wmxI z=b7zFJimq{N~kkvkgGF42Io_qLWC6ijXUl-u$~xA8(DZfhuDs#;{pl@Z3bVCGwhTU zs&i*ZXc2h;I%eGy*mO7=K|p#wPDH+s6CYVARXU0F6sR_}6{s98*1i7@mFW;VcLC8w z+t!(xnZa<d>qS?Ae{>H-q|fGBj_!8Y))Q*K4|j0I;6gKtW}~1#a3`?0;>Fqigcgy{ zpnZOa5Hh0$@SZ?<>kJ0~KNdN@9eooA9iS>x8+w9liQD4f0fb(TQk4TkXlo%k^UTe@ zRF!@k@|Q;Yyv*SZ*IT4?=n7l(8_k9p-P|x+FY$Y-EjKakYqyAe1l{p|uTm=8RkaC9 zCG_852<<y5#fa=Wo(FF7EPA!rL%9veb;3at;A#<BLz|mUc)xzy!`w3b>v1IXocT%} z0#w4c`v>Uz^}@{d0&w{7;pBje9k8%zwp}?obi@|zqieQGZlo+t=Ks$$`&Uumd))-3 zg#G~V`@p?Y64Ju8dAj})77@$D$O{l7S*Q)(Q0sYx*EiCBx19#RV>Zx}8~$(Tze=gE zipV!Gp?`Bag1LlrC&b$Oo2zQr{n^=B4jnp_5;n;fDP;*-SXf|VW24O#6tYn|u(B>9 zFF<>fMVC3QBDqg$SJKCb@BYSargbJdc<^9Q%7tDWKW%PqF8JcODnY^GKSA}tEjKr> zN@`c_r}9hwlVcwA+>2M3U<9k;PN7ojax(+r5q@3?k*o5Wq(RtWs$}1lhM<>BJ8<9t zolb`%M~;M+M+;%jmXT?yD<ZOgT$k9n|0ylc;A={$cSQ?p8Lb4GDZThqUR!BZ(kHba z`^?AcO&}?u-TFfHwoT?b5;^pr-2ZL`8WDOs6FDb8^}i}|WnxG|hqh2#Lv6`<>2mk$ z<hPMF3A8@2l2QxWRxRALlKj*@#C7!=4`~jxAIL4Y+(M_LXM_9x*KWCMYism+y@ASP zhpC<6>S%kmBe5rqMuc`HMJRf_bvcZ6`0TGcW7*ezCq?9#E!`!#b?7hX6b>AQZI8c7 z+OMvza{PF_4`uJOOY&~F%bq=(j%~Tbwyv<QT9Hyq{NK>hdcaVblSW-a-+><Nn86zY zg;7E@xj5efvbeZ7ux*Ek-0$}}b}V-=Z?vwxd-t-qxHuxMH#RovlzR<&ByT_3&i&yG z4?&Bo&_2Wmoo!F(WpW8!BBgDJgt~AE?Uuh4<~oa7gjPyD2|z?10({e^he&2{HSi{2 zANt|$OYFO8^Eod>NTLFL^eAH&J;-8zqx&)a)w`xu;IGYoGM<Q8-{n@|RuMVXEi(h} zQA+)Rn_z~hU4mbTo~$rZZi&i)7EYHkI^Y!B{{vuuOsF{c5PD|(#4tAJB=5qGDy2S9 zT84-$pox7R@HNlA8B`mb{2P=~cQ2^xo#=UdL0#IBj7ODHmy})$jz4q=0iumO8xs%2 z&HzC~j@owC=_ER;-jY)4`8gM1>iumTM=`(cPJ3Qn5=pfUv>qQiA_3A<Od?Q9tr`dO zIbg)cA++xhdc+H@0wdq@h)!A8r+di3KW6Q4+MgqAMke)@5ZkHibQ9UN7!}fg;_=5@ ztFWTiTZ_;Reh%XE<|6yG2pv;9oV2>jTvQUqt0#wG^Dt}{bj!-wH$q~DK$O%HL<HH+ zCA5pc#Wf}LePA@gW^CUOf8GR^%*Bv|P7tInp$BaVN$7yotSzByO6lUL{u!pbT2j9W zS|xO%v(u`CF6i)-Na=xub`eku8mk;Jc=coxIyQ<~@bW{0WhxTS{p66)FmjQtNa$RZ zqoy9cX&#BF9R`h5j;c{Zr|sD)hu6L$DeZI&vpdi(PFfw>qRmbr43?p|0P<B12gMTF z#ke|!Db$hBk^hezx2<xd>NVK8Do2HFwTXnj7cgJaA#~0yi3>6APT>eaLsGhq4jt<h zj$7r((4lv}gof5Tg#-P$v=>heLc8UJ$W4o8yWH~=2*%<nA@-Pc=$$K}AxW$zguXX~ zmt4qDIZEms(xLr&oPm{A&2}L@+8{?l56z@ck=H+NOXZmA(B&A?gL8Gn8ucd4cFu#( zTDT~H;J`}G`3Yoq3Nv2*;!a^knLC%z75Y?DD@U@fmc9&0vt3hb011g3k<!IxJ6o1t zcI?<wISw0Nkzyq7|HM^}NyWBnh0t!f{wFg~XKB3y2_5(xyZdzsT^ZZXtEb4yQ9@+@ z*y*3_LOUR3<D^zO#-u}!)KOr%tF3alb+y!as&r2BuC0|$Vb!sh*G6<J7dxBKFcuv; zSZ*e%i#vrW-PVx$fLX7l6j{zm4ss;)QCpr{u3t~;*h6T^<}A2vaIkZ&9Fx$Y3p<2R zc72s*yM}5<lAtxM9M=OQOUH#?lB#@zS`s>;LhXDB4V8l|jms`jIZp8t5*X2;{W_hF zVI8%jR6^%Sx9ebYN?44@Ri-@WMJ<-l759JVN@$pj4qa83_kd0>D00Zk5h6!J37zGf z<RDu@=Mp(XN*77!lh$mPAu*c}dT78SRafAqmJmAlL?Ru-MuZMY=s??ZmfTwcZgKx# zrEd7gUS2I7I#J%vBs8>YZSe7fRQVO3&|(Q~%NT=%&N7h}S~<W`JL*Vj55>tjc?19e z2xUn`K~y^QT}I{b(1g%m0Q#xL9}{|LEke6+$?fRS_bVvZV#Y78LQ3b35e(E0zs>%2 zq>c@WBy@(5sXMA#8{6I=+Dx+iwk@+tIs=V$=-3P55c+{5nbjEhL#I$UumYZhZYZT2 zTRFV)O74Fv7;EGtXTO-x#t(Y0*}eljWMgB4nVFf9Iv_f0<9rc$9{M_G%<pXE_|c(j zv9L4B&8XTLBC-PflTzw&nU^#qp<}44a+FHsNE=#FIh>KlM)MTwpcbJ;qzjybUI=~! zo*oN&y^!BPlXD-NMD>r|{gEYq6V{ASodEX)N|#>_N%}!rx7)Q#ye(iBrOZU>y)IHY zT+^M45OEVPR8-{<kq+7ndf2utL*=-iiQTf6xhigBlrBl}G~mCXr$i<7EstP_fw$FZ zpha#hI(zK8n}$B~ewsawt&nQ-7Z(><UthONKHphz2}4t`VrU|vtITK_Wd9YjI>F(< zb}-haM7_Uiwx3~^u^2Z>rN(}orOI4WQ3(D`b{}TM@h3_&cRxgAUz&#EuH_IrRGwR> zhva%IB62!91el<gkxr7?YtMxuax>chdKvvSSPmQyRGC=$RcTJ0Pfs>3(9Wut$e?6^ zs}V{GeFSvFa|0H-!5Sm_b#%Wp58|XRNpsUH@}d%2L|z8`Su8Pr>sX|ex@{#Qrc za8)Ub-1vJ{Du+AaS48$JrRoJ8EI=(nm!g!&DG6Lf+@{3#)BQXTDG5DfL0VW?2!_P< z(dl&9y<5*|aD_&W^&-{Y6d9``Qq|3Kp4%T!g3M4(=v$!L^c2E4rE~7F-}V@Z{RdV@ zLgTF~5?ghuiroHw>WflG-6%b-L1uuGrI#C_)DB%sLR;iWlq$*7<T#d=N_X?z%^)SA zKaHc4hRUeYr*qCPNpE`pO7c8RY!S{PLMx?KDO=2P1i3dM_vXcptB7rbvG;mCdc7X& z>+7tqud}wc79FGjSXo(d7YvHt3#HTzXi`_AgzTwvs?uY}HZ68sa{nXko*1F)*rAW0 z?Zvf`QaK=Py(rO@jlDSMh9}#aKz6k7(t30Mmm+?hv^Lcb=UGf_A^W<7KF^NWL41jZ zf??k1`BlVC9Y-Wx&s@XJ=m`1t_a@-gz-ghEBY+p+1bc6a62hm>txM!kS`(@4Em5+q zCG_Xe4(?~?^w{fkAVWw=>)5e_-#{-0e>3nirBv_@6p{aE+Z`ywzm^XvrPfN?Ul-$+ z(jia1D#QeaeGbmOYOJP~pw7aZuLE8uB2NH5n>yDa*>}-vJ+EbM?lK6k0Ul$^gEAA{ zX0z3IGcIqx*Y1()aWHS%mj!oC?kYQPN{5fM1<;n1n2<@OWsrFptzInw2Y>@rdbPn{ z0xvd8b4&zHoRdoQqJLIOy|hjxBJz4aEhdd)l9h~4o^gg61hol0AdiSeF)ZvtI<b|b zT-Xk4DVnGo1}{Mi9!uUO^l}x0KZwZRqKC6{%3GWynV}h>OCIP24eB`;@}zM>?Z{cj zcxcU#3<;fc{XO90X8&K45o^z@<IjOFnR3?6Z!d8CR^XyY8L_fMX{#AdM2D)%ba0@e ztk%(l(7~7xE}V-!zfJ`rvJ1}zF;_Z!gv{xb><^V=Vbh&Ydgb+-MSh-PuH`2fLKo<( z|D=@q+p3d{D{GN7cGI0}%6U$Q?f)~bW8#Ev;^1E*p$~~j9S4<j>`smyHKEfLN$3y) zb(V(Bs`GG1TxcU0k^H%KYVWv+&@ni9Cm*33O6ehWqvByi(<qS8ZW$F)s8T{(IFl7d z$_{M~atS@8`{&$~$hDo5QP<(jHo*=fboK)^s6*&;LW#K#%t0dw9U)tZgswZcE_Vfj zsDv)OR|hldDk15_+(s=XrE);Mj^RK;H;~TIKtd-_a{U7m6iVoOWtKl6bgbQ(_bX=v zxMW~qzlcm!N^f_S<36GCY7jd|LI-brf^a2Yr~^>TOEGtL`EH1Rxe8wV<d?)u!^(nF z{<*pTX#<m#&_#qcH}BDqKTMmDs5uh)6t|pMc@8NjA))VERj`TaJZMX*?HS~*lF)UA zeRm9@vm{gl34K2*Iv_1~3QvK!pK%)!x&{u2NL3uYp#?pC6iVrtipZ*U2z_3S|J24L zCG_1zq$H5Z;~>vw+ft|Spj%c(y~PqbM>^M#(j(8Xio_N7s+Hg{cLkNBL)qJ!O=yb* z8M7sHth_+EB@(*HjOQvs52R)tRU>eIO6@rLRgPQ<YEYtfJT|xdiaH@rLJtU?S!c-$ zeLCc+Gc_z7!Dm?7A<TS6bvlJ7t;zwJgcgzWFaZ&}-EQs8L6L;cv__Oj=+hy4z$V(R z2nH8gs%rS>82r0LU3Db%IVIMSrfBS(lzRAq8<;DhA8U77@~6%r^oP)aA#-t6!MVwB z1TmtSYp?21MkRC!ob#U{B9|CHls2#koC(~}Qh7-<c1|jE;?Esi`4sR_^uwBAGY;KM zjpGg(D?2Hr8bx-hz(7LRK$V0Z(xIE6-|y4ubPfSeL_dRQU3M0G#mi`MK0AQWSE5}b z=MvkjcbqjBQ7LsJaASkEZ8ua#;Y`@PvsI2J@Cf*9!i1gyu0~%^&S23Mz(>u0CW6|8 zR!Y4acz26!IhoN!LT9TSe(aA-=^4UfLWyR@fcpR#6YQD5S!gTH2Ai{uP5{>{rG8vh zQQH{H!kbfU!uWLWh3be*5dv08=r!Q0=$GbH96NG>D%eB!2|i)A|5Nn`rPK;=t=YB> zri30p2I@x6EcXDCgjPx&1s-XSMPwd5!g{LN4x=Teol<q&|1Fv9yWP=g+L4$d4G}pT qcmrB^ESPQH+0RMaXC&ej75M*hafYi`aToFc0000<MNUMnLSTYJ${Vf# literal 0 HcmV?d00001 diff --git a/deps/v8_inspector/deps/jinja2/docs/_templates/sidebarintro.html b/deps/v8_inspector/deps/jinja2/docs/_templates/sidebarintro.html new file mode 100644 index 00000000000000..b7fcfda412f90a --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/_templates/sidebarintro.html @@ -0,0 +1,20 @@ +<h3>About Jinja2</h3> +<p> + Jinja2 is a full featured template engine for Python. It has full unicode + support, an optional integrated sandboxed execution environment, widely used + and BSD licensed. +</p> +<h3>Other Formats</h3> +<p> + You can download the documentation in other formats as well: +</p> +<ul> + <li><a href="http://jinja.pocoo.org/docs/jinja-docs.pdf">as PDF</a> + <li><a href="http://jinja.pocoo.org/docs/jinja-docs.zip">as zipped HTML</a> +</ul> +<h3>Useful Links</h3> +<ul> + <li><a href="http://jinja.pocoo.org/">The Jinja2 Website</a></li> + <li><a href="http://pypi.python.org/pypi/Jinja2">Jinja2 @ PyPI</a></li> + <li><a href="http://github.com/mitsuhiko/jinja2">Jinja2 @ github</a></li> +</ul> diff --git a/deps/v8_inspector/deps/jinja2/docs/_templates/sidebarlogo.html b/deps/v8_inspector/deps/jinja2/docs/_templates/sidebarlogo.html new file mode 100644 index 00000000000000..ca211c6a3e105f --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/_templates/sidebarlogo.html @@ -0,0 +1,3 @@ +<p class="logo"><a href="{{ pathto(master_doc) }}"> + <img class="logo" src="{{ pathto('_static/jinja-small.png', 1) }}" alt="Logo"/> +</a></p> diff --git a/deps/v8_inspector/deps/jinja2/docs/_themes/LICENSE b/deps/v8_inspector/deps/jinja2/docs/_themes/LICENSE new file mode 100644 index 00000000000000..8daab7ee6efe3b --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/_themes/LICENSE @@ -0,0 +1,37 @@ +Copyright (c) 2010 by Armin Ronacher. + +Some rights reserved. + +Redistribution and use in source and binary forms of the theme, with or +without modification, are permitted provided that the following conditions +are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +* The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + +We kindly ask you to only use these themes in an unmodified manner just +for Flask and Flask-related products, not for unrelated projects. If you +like the visual style and want to use it for your own projects, please +consider making some larger changes to the themes (such as changing +font faces, sizes, colors or margins). + +THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/deps/v8_inspector/deps/jinja2/docs/_themes/README b/deps/v8_inspector/deps/jinja2/docs/_themes/README new file mode 100644 index 00000000000000..b3292bdff8ed54 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/_themes/README @@ -0,0 +1,31 @@ +Flask Sphinx Styles +=================== + +This repository contains sphinx styles for Flask and Flask related +projects. To use this style in your Sphinx documentation, follow +this guide: + +1. put this folder as _themes into your docs folder. Alternatively + you can also use git submodules to check out the contents there. +2. add this to your conf.py: + + sys.path.append(os.path.abspath('_themes')) + html_theme_path = ['_themes'] + html_theme = 'flask' + +The following themes exist: + +- 'flask' - the standard flask documentation theme for large + projects +- 'flask_small' - small one-page theme. Intended to be used by + very small addon libraries for flask. + +The following options exist for the flask_small theme: + + [options] + index_logo = '' filename of a picture in _static + to be used as replacement for the + h1 in the index.rst file. + index_logo_height = 120px height of the index logo + github_fork = '' repository name on github for the + "fork me" badge diff --git a/deps/v8_inspector/deps/jinja2/docs/_themes/jinja/layout.html b/deps/v8_inspector/deps/jinja2/docs/_themes/jinja/layout.html new file mode 100644 index 00000000000000..a0c9cab04203b4 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/_themes/jinja/layout.html @@ -0,0 +1,8 @@ +{%- extends "basic/layout.html" %} +{%- block relbar2 %}{% endblock %} +{%- block footer %} + <div class="footer"> + © Copyright {{ copyright }}. + Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>. + </div> +{%- endblock %} diff --git a/deps/v8_inspector/deps/jinja2/docs/_themes/jinja/relations.html b/deps/v8_inspector/deps/jinja2/docs/_themes/jinja/relations.html new file mode 100644 index 00000000000000..3bbcde85bb48d3 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/_themes/jinja/relations.html @@ -0,0 +1,19 @@ +<h3>Related Topics</h3> +<ul> + <li><a href="{{ pathto(master_doc) }}">Documentation overview</a><ul> + {%- for parent in parents %} + <li><a href="{{ parent.link|e }}">{{ parent.title }}</a><ul> + {%- endfor %} + {%- if prev %} + <li>Previous: <a href="{{ prev.link|e }}" title="{{ _('previous chapter') + }}">{{ prev.title }}</a></li> + {%- endif %} + {%- if next %} + <li>Next: <a href="{{ next.link|e }}" title="{{ _('next chapter') + }}">{{ next.title }}</a></li> + {%- endif %} + {%- for parent in parents %} + </ul></li> + {%- endfor %} + </ul></li> +</ul> diff --git a/deps/v8_inspector/deps/jinja2/docs/_themes/jinja/static/jinja.css_t b/deps/v8_inspector/deps/jinja2/docs/_themes/jinja/static/jinja.css_t new file mode 100644 index 00000000000000..7d3b244ae13bb6 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/_themes/jinja/static/jinja.css_t @@ -0,0 +1,396 @@ +/* + * jinja.css_t + * ~~~~~~~~~~~ + * + * :copyright: Copyright 2011 by Armin Ronacher. + * :license: Flask Design License, see LICENSE for details. + */ + +@import url(http://fonts.googleapis.com/css?family=Crimson+Text); + +{% set page_width = '940px' %} +{% set sidebar_width = '220px' %} +{% set font_family = 'Georgia, serif' %} +{% set header_font_family = 'Crimson Text, ' ~ font_family %} + +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: {{ font_family }}; + font-size: 17px; + background-color: white; + color: #000; + margin: 0; + padding: 0; +} + +div.document { + width: {{ page_width }}; + margin: 30px auto 0 auto; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 {{ sidebar_width }}; +} + +div.sphinxsidebar { + width: {{ sidebar_width }}; +} + +hr { + border: 1px solid #B1B4B6; +} + +div.body { + background-color: #ffffff; + color: #3E4349; + padding: 0 30px 0 30px; +} + +img.floatingflask { + padding: 0 0 10px 10px; + float: right; +} + +div.footer { + width: {{ page_width }}; + margin: 20px auto 30px auto; + font-size: 14px; + color: #888; + text-align: right; +} + +div.footer a { + color: #888; +} + +div.related { + display: none; +} + +div.sphinxsidebar a { + color: #444; + text-decoration: none; + border-bottom: 1px dotted #999; +} + +div.sphinxsidebar a:hover { + border-bottom: 1px solid #999; +} + +div.sphinxsidebar { + font-size: 15px; + line-height: 1.5; +} + +div.sphinxsidebarwrapper { + padding: 18px 10px; +} + +div.sphinxsidebarwrapper p.logo { + padding: 0 0 20px 0; + margin: 0; + text-align: center; +} + +div.sphinxsidebar h3, +div.sphinxsidebar h4 { + font-family: {{ font_family }}; + color: #444; + font-size: 24px; + font-weight: normal; + margin: 0 0 5px 0; + padding: 0; +} + +div.sphinxsidebar h4 { + font-size: 20px; +} + +div.sphinxsidebar h3 a { + color: #444; +} + +div.sphinxsidebar p.logo a, +div.sphinxsidebar h3 a, +div.sphinxsidebar p.logo a:hover, +div.sphinxsidebar h3 a:hover { + border: none; +} + +div.sphinxsidebar p { + color: #555; + margin: 10px 0; +} + +div.sphinxsidebar ul { + margin: 10px 0; + padding: 0; + color: #000; +} + +div.sphinxsidebar input { + border: 1px solid #ccc; + font-family: {{ font_family }}; + font-size: 14px; +} + +div.sphinxsidebar form.search input[name="q"] { + width: 130px; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #aa0000; + text-decoration: underline; +} + +a:hover { + color: #dd0000; + text-decoration: underline; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: {{ header_font_family }}; + font-weight: normal; + margin: 30px 0px 10px 0px; + padding: 0; + color: black; +} + +div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } +div.body h2 { font-size: 180%; } +div.body h3 { font-size: 150%; } +div.body h4 { font-size: 130%; } +div.body h5 { font-size: 100%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #ddd; + padding: 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + color: #444; + background: #eaeaea; +} + +div.body p, div.body dd, div.body li { + line-height: 1.4em; +} + +div.admonition { + background: #fafafa; + margin: 20px -30px; + padding: 10px 30px; + border-top: 1px solid #ccc; + border-bottom: 1px solid #ccc; +} + +div.admonition tt.xref, div.admonition a tt { + border-bottom: 1px solid #fafafa; +} + +dd div.admonition { + margin-left: -60px; + padding-left: 60px; +} + +div.admonition p.admonition-title { + font-family: {{ font_family }}; + font-weight: normal; + font-size: 24px; + margin: 0 0 10px 0; + padding: 0; + line-height: 1; +} + +div.admonition p.last { + margin-bottom: 0; +} + +div.highlight { + background-color: white; +} + +dt:target, .highlight { + background: #FAF3E8; +} + +div.note { + background-color: #eee; + border: 1px solid #ccc; +} + +div.seealso { + background-color: #ffc; + border: 1px solid #ff6; +} + +div.topic { + background-color: #eee; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre, tt { + font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; + font-size: 0.85em; +} + +img.screenshot { +} + +tt.descname, tt.descclassname { + font-size: 0.95em; +} + +tt.descname { + padding-right: 0.08em; +} + +img.screenshot { + -moz-box-shadow: 2px 2px 4px #eee; + -webkit-box-shadow: 2px 2px 4px #eee; + box-shadow: 2px 2px 4px #eee; +} + +table.docutils { + border: 1px solid #888; + -moz-box-shadow: 2px 2px 4px #eee; + -webkit-box-shadow: 2px 2px 4px #eee; + box-shadow: 2px 2px 4px #eee; +} + +table.docutils td, table.docutils th { + border: 1px solid #888; + padding: 0.25em 0.7em; +} + +table.field-list, table.footnote { + border: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +table.footnote { + margin: 15px 0; + width: 100%; + border: 1px solid #eee; + background: #fdfdfd; + font-size: 0.9em; +} + +table.footnote + table.footnote { + margin-top: -15px; + border-top: none; +} + +table.field-list th { + padding: 0 0.8em 0 0; +} + +table.field-list td { + padding: 0; +} + +table.footnote td.label { + width: 0px; + padding: 0.3em 0 0.3em 0.5em; +} + +table.footnote td { + padding: 0.3em 0.5em; +} + +dl { + margin: 0; + padding: 0; +} + +dl dd { + margin-left: 30px; +} + +blockquote { + margin: 0 0 0 30px; + padding: 0; +} + +ul, ol { + margin: 10px 0 10px 30px; + padding: 0; +} + +pre { + background: #eee; + padding: 7px 30px; + margin: 15px -30px; + line-height: 1.3em; +} + +dl pre, blockquote pre, li pre { + margin-left: -60px; + padding-left: 60px; +} + +dl dl pre { + margin-left: -90px; + padding-left: 90px; +} + +tt { + background-color: #E8EFF0; + color: #222; + /* padding: 1px 2px; */ +} + +tt.xref, a tt { + background-color: #E8EFF0; + border-bottom: 1px solid white; +} + +a.reference { + text-decoration: none; + border-bottom: 1px dotted #bb0000; +} + +a.reference:hover { + border-bottom: 1px solid #dd0000; +} + +a.footnote-reference { + text-decoration: none; + font-size: 0.7em; + vertical-align: top; + border-bottom: 1px dotted #bb0000; +} + +a.footnote-reference:hover { + border-bottom: 1px solid #dd0000; +} + +a:hover tt { + background: #EEE; +} diff --git a/deps/v8_inspector/deps/jinja2/docs/_themes/jinja/theme.conf b/deps/v8_inspector/deps/jinja2/docs/_themes/jinja/theme.conf new file mode 100644 index 00000000000000..10c7e56e8371bb --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/_themes/jinja/theme.conf @@ -0,0 +1,3 @@ +[theme] +inherit = basic +stylesheet = jinja.css diff --git a/deps/v8_inspector/deps/jinja2/docs/api.rst b/deps/v8_inspector/deps/jinja2/docs/api.rst new file mode 100644 index 00000000000000..04ba157fbba31c --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/api.rst @@ -0,0 +1,808 @@ +API +=== + +.. module:: jinja2 + :synopsis: public Jinja2 API + +This document describes the API to Jinja2 and not the template language. It +will be most useful as reference to those implementing the template interface +to the application and not those who are creating Jinja2 templates. + +Basics +------ + +Jinja2 uses a central object called the template :class:`Environment`. +Instances of this class are used to store the configuration and global objects, +and are used to load templates from the file system or other locations. +Even if you are creating templates from strings by using the constructor of +:class:`Template` class, an environment is created automatically for you, +albeit a shared one. + +Most applications will create one :class:`Environment` object on application +initialization and use that to load templates. In some cases it's however +useful to have multiple environments side by side, if different configurations +are in use. + +The simplest way to configure Jinja2 to load templates for your application +looks roughly like this:: + + from jinja2 import Environment, PackageLoader + env = Environment(loader=PackageLoader('yourapplication', 'templates')) + +This will create a template environment with the default settings and a +loader that looks up the templates in the `templates` folder inside the +`yourapplication` python package. Different loaders are available +and you can also write your own if you want to load templates from a +database or other resources. + +To load a template from this environment you just have to call the +:meth:`get_template` method which then returns the loaded :class:`Template`:: + + template = env.get_template('mytemplate.html') + +To render it with some variables, just call the :meth:`render` method:: + + print template.render(the='variables', go='here') + +Using a template loader rather than passing strings to :class:`Template` +or :meth:`Environment.from_string` has multiple advantages. Besides being +a lot easier to use it also enables template inheritance. + + +Unicode +------- + +Jinja2 is using Unicode internally which means that you have to pass Unicode +objects to the render function or bytestrings that only consist of ASCII +characters. Additionally newlines are normalized to one end of line +sequence which is per default UNIX style (``\n``). + +Python 2.x supports two ways of representing string objects. One is the +`str` type and the other is the `unicode` type, both of which extend a type +called `basestring`. Unfortunately the default is `str` which should not +be used to store text based information unless only ASCII characters are +used. With Python 2.6 it is possible to make `unicode` the default on a per +module level and with Python 3 it will be the default. + +To explicitly use a Unicode string you have to prefix the string literal +with a `u`: ``u'Hänsel und Gretel sagen Hallo'``. That way Python will +store the string as Unicode by decoding the string with the character +encoding from the current Python module. If no encoding is specified this +defaults to 'ASCII' which means that you can't use any non ASCII identifier. + +To set a better module encoding add the following comment to the first or +second line of the Python module using the Unicode literal:: + + # -*- coding: utf-8 -*- + +We recommend utf-8 as Encoding for Python modules and templates as it's +possible to represent every Unicode character in utf-8 and because it's +backwards compatible to ASCII. For Jinja2 the default encoding of templates +is assumed to be utf-8. + +It is not possible to use Jinja2 to process non-Unicode data. The reason +for this is that Jinja2 uses Unicode already on the language level. For +example Jinja2 treats the non-breaking space as valid whitespace inside +expressions which requires knowledge of the encoding or operating on an +Unicode string. + +For more details about Unicode in Python have a look at the excellent +`Unicode documentation`_. + +Another important thing is how Jinja2 is handling string literals in +templates. A naive implementation would be using Unicode strings for +all string literals but it turned out in the past that this is problematic +as some libraries are typechecking against `str` explicitly. For example +`datetime.strftime` does not accept Unicode arguments. To not break it +completely Jinja2 is returning `str` for strings that fit into ASCII and +for everything else `unicode`: + +>>> m = Template(u"{% set a, b = 'foo', 'föö' %}").module +>>> m.a +'foo' +>>> m.b +u'f\xf6\xf6' + + +.. _Unicode documentation: http://docs.python.org/dev/howto/unicode.html + +High Level API +-------------- + +The high-level API is the API you will use in the application to load and +render Jinja2 templates. The :ref:`low-level-api` on the other side is only +useful if you want to dig deeper into Jinja2 or :ref:`develop extensions +<jinja-extensions>`. + +.. autoclass:: Environment([options]) + :members: from_string, get_template, select_template, + get_or_select_template, join_path, extend, compile_expression, + compile_templates, list_templates, add_extension + + .. attribute:: shared + + If a template was created by using the :class:`Template` constructor + an environment is created automatically. These environments are + created as shared environments which means that multiple templates + may have the same anonymous environment. For all shared environments + this attribute is `True`, else `False`. + + .. attribute:: sandboxed + + If the environment is sandboxed this attribute is `True`. For the + sandbox mode have a look at the documentation for the + :class:`~jinja2.sandbox.SandboxedEnvironment`. + + .. attribute:: filters + + A dict of filters for this environment. As long as no template was + loaded it's safe to add new filters or remove old. For custom filters + see :ref:`writing-filters`. For valid filter names have a look at + :ref:`identifier-naming`. + + .. attribute:: tests + + A dict of test functions for this environment. As long as no + template was loaded it's safe to modify this dict. For custom tests + see :ref:`writing-tests`. For valid test names have a look at + :ref:`identifier-naming`. + + .. attribute:: globals + + A dict of global variables. These variables are always available + in a template. As long as no template was loaded it's safe + to modify this dict. For more details see :ref:`global-namespace`. + For valid object names have a look at :ref:`identifier-naming`. + + .. attribute:: code_generator_class + + The class used for code generation. This should not be changed + in most cases, unless you need to modify the Python code a + template compiles to. + + .. attribute:: context_class + + The context used for templates. This should not be changed + in most cases, unless you need to modify internals of how + template variables are handled. For details, see + :class:`~jinja2.runtime.Context`. + + .. automethod:: overlay([options]) + + .. method:: undefined([hint, obj, name, exc]) + + Creates a new :class:`Undefined` object for `name`. This is useful + for filters or functions that may return undefined objects for + some operations. All parameters except of `hint` should be provided + as keyword parameters for better readability. The `hint` is used as + error message for the exception if provided, otherwise the error + message will be generated from `obj` and `name` automatically. The exception + provided as `exc` is raised if something with the generated undefined + object is done that the undefined object does not allow. The default + exception is :exc:`UndefinedError`. If a `hint` is provided the + `name` may be omitted. + + The most common way to create an undefined object is by providing + a name only:: + + return environment.undefined(name='some_name') + + This means that the name `some_name` is not defined. If the name + was from an attribute of an object it makes sense to tell the + undefined object the holder object to improve the error message:: + + if not hasattr(obj, 'attr'): + return environment.undefined(obj=obj, name='attr') + + For a more complex example you can provide a hint. For example + the :func:`first` filter creates an undefined object that way:: + + return environment.undefined('no first item, sequence was empty') + + If it the `name` or `obj` is known (for example because an attribute + was accessed) it should be passed to the undefined object, even if + a custom `hint` is provided. This gives undefined objects the + possibility to enhance the error message. + +.. autoclass:: Template + :members: module, make_module + + .. attribute:: globals + + The dict with the globals of that template. It's unsafe to modify + this dict as it may be shared with other templates or the environment + that loaded the template. + + .. attribute:: name + + The loading name of the template. If the template was loaded from a + string this is `None`. + + .. attribute:: filename + + The filename of the template on the file system if it was loaded from + there. Otherwise this is `None`. + + .. automethod:: render([context]) + + .. automethod:: generate([context]) + + .. automethod:: stream([context]) + + +.. autoclass:: jinja2.environment.TemplateStream() + :members: disable_buffering, enable_buffering, dump + + +Autoescaping +------------ + +.. versionadded:: 2.4 + +As of Jinja 2.4 the preferred way to do autoescaping is to enable the +:ref:`autoescape-extension` and to configure a sensible default for +autoescaping. This makes it possible to enable and disable autoescaping +on a per-template basis (HTML versus text for instance). + +Here a recommended setup that enables autoescaping for templates ending +in ``'.html'``, ``'.htm'`` and ``'.xml'`` and disabling it by default +for all other extensions:: + + def guess_autoescape(template_name): + if template_name is None or '.' not in template_name: + return False + ext = template_name.rsplit('.', 1)[1] + return ext in ('html', 'htm', 'xml') + + env = Environment(autoescape=guess_autoescape, + loader=PackageLoader('mypackage'), + extensions=['jinja2.ext.autoescape']) + +When implementing a guessing autoescape function, make sure you also +accept `None` as valid template name. This will be passed when generating +templates from strings. + +Inside the templates the behaviour can be temporarily changed by using +the `autoescape` block (see :ref:`autoescape-overrides`). + + +.. _identifier-naming: + +Notes on Identifiers +-------------------- + +Jinja2 uses the regular Python 2.x naming rules. Valid identifiers have to +match ``[a-zA-Z_][a-zA-Z0-9_]*``. As a matter of fact non ASCII characters +are currently not allowed. This limitation will probably go away as soon as +unicode identifiers are fully specified for Python 3. + +Filters and tests are looked up in separate namespaces and have slightly +modified identifier syntax. Filters and tests may contain dots to group +filters and tests by topic. For example it's perfectly valid to add a +function into the filter dict and call it `to.unicode`. The regular +expression for filter and test identifiers is +``[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)*```. + + +Undefined Types +--------------- + +These classes can be used as undefined types. The :class:`Environment` +constructor takes an `undefined` parameter that can be one of those classes +or a custom subclass of :class:`Undefined`. Whenever the template engine is +unable to look up a name or access an attribute one of those objects is +created and returned. Some operations on undefined values are then allowed, +others fail. + +The closest to regular Python behavior is the `StrictUndefined` which +disallows all operations beside testing if it's an undefined object. + +.. autoclass:: jinja2.Undefined() + + .. attribute:: _undefined_hint + + Either `None` or an unicode string with the error message for + the undefined object. + + .. attribute:: _undefined_obj + + Either `None` or the owner object that caused the undefined object + to be created (for example because an attribute does not exist). + + .. attribute:: _undefined_name + + The name for the undefined variable / attribute or just `None` + if no such information exists. + + .. attribute:: _undefined_exception + + The exception that the undefined object wants to raise. This + is usually one of :exc:`UndefinedError` or :exc:`SecurityError`. + + .. method:: _fail_with_undefined_error(\*args, \**kwargs) + + When called with any arguments this method raises + :attr:`_undefined_exception` with an error message generated + from the undefined hints stored on the undefined object. + +.. autoclass:: jinja2.DebugUndefined() + +.. autoclass:: jinja2.StrictUndefined() + +There is also a factory function that can decorate undefined objects to +implement logging on failures: + +.. autofunction:: jinja2.make_logging_undefined + +Undefined objects are created by calling :attr:`undefined`. + +.. admonition:: Implementation + + :class:`Undefined` objects are implemented by overriding the special + `__underscore__` methods. For example the default :class:`Undefined` + class implements `__unicode__` in a way that it returns an empty + string, however `__int__` and others still fail with an exception. To + allow conversion to int by returning ``0`` you can implement your own:: + + class NullUndefined(Undefined): + def __int__(self): + return 0 + def __float__(self): + return 0.0 + + To disallow a method, just override it and raise + :attr:`~Undefined._undefined_exception`. Because this is a very common + idom in undefined objects there is the helper method + :meth:`~Undefined._fail_with_undefined_error` that does the error raising + automatically. Here a class that works like the regular :class:`Undefined` + but chokes on iteration:: + + class NonIterableUndefined(Undefined): + __iter__ = Undefined._fail_with_undefined_error + + +The Context +----------- + +.. autoclass:: jinja2.runtime.Context() + :members: resolve, get_exported, get_all + + .. attribute:: parent + + A dict of read only, global variables the template looks up. These + can either come from another :class:`Context`, from the + :attr:`Environment.globals` or :attr:`Template.globals` or points + to a dict created by combining the globals with the variables + passed to the render function. It must not be altered. + + .. attribute:: vars + + The template local variables. This list contains environment and + context functions from the :attr:`parent` scope as well as local + modifications and exported variables from the template. The template + will modify this dict during template evaluation but filters and + context functions are not allowed to modify it. + + .. attribute:: environment + + The environment that loaded the template. + + .. attribute:: exported_vars + + This set contains all the names the template exports. The values for + the names are in the :attr:`vars` dict. In order to get a copy of the + exported variables as dict, :meth:`get_exported` can be used. + + .. attribute:: name + + The load name of the template owning this context. + + .. attribute:: blocks + + A dict with the current mapping of blocks in the template. The keys + in this dict are the names of the blocks, and the values a list of + blocks registered. The last item in each list is the current active + block (latest in the inheritance chain). + + .. attribute:: eval_ctx + + The current :ref:`eval-context`. + + .. automethod:: jinja2.runtime.Context.call(callable, \*args, \**kwargs) + + +.. admonition:: Implementation + + Context is immutable for the same reason Python's frame locals are + immutable inside functions. Both Jinja2 and Python are not using the + context / frame locals as data storage for variables but only as primary + data source. + + When a template accesses a variable the template does not define, Jinja2 + looks up the variable in the context, after that the variable is treated + as if it was defined in the template. + + +.. _loaders: + +Loaders +------- + +Loaders are responsible for loading templates from a resource such as the +file system. The environment will keep the compiled modules in memory like +Python's `sys.modules`. Unlike `sys.modules` however this cache is limited in +size by default and templates are automatically reloaded. +All loaders are subclasses of :class:`BaseLoader`. If you want to create your +own loader, subclass :class:`BaseLoader` and override `get_source`. + +.. autoclass:: jinja2.BaseLoader + :members: get_source, load + +Here a list of the builtin loaders Jinja2 provides: + +.. autoclass:: jinja2.FileSystemLoader + +.. autoclass:: jinja2.PackageLoader + +.. autoclass:: jinja2.DictLoader + +.. autoclass:: jinja2.FunctionLoader + +.. autoclass:: jinja2.PrefixLoader + +.. autoclass:: jinja2.ChoiceLoader + +.. autoclass:: jinja2.ModuleLoader + + +.. _bytecode-cache: + +Bytecode Cache +-------------- + +Jinja 2.1 and higher support external bytecode caching. Bytecode caches make +it possible to store the generated bytecode on the file system or a different +location to avoid parsing the templates on first use. + +This is especially useful if you have a web application that is initialized on +the first request and Jinja compiles many templates at once which slows down +the application. + +To use a bytecode cache, instantiate it and pass it to the :class:`Environment`. + +.. autoclass:: jinja2.BytecodeCache + :members: load_bytecode, dump_bytecode, clear + +.. autoclass:: jinja2.bccache.Bucket + :members: write_bytecode, load_bytecode, bytecode_from_string, + bytecode_to_string, reset + + .. attribute:: environment + + The :class:`Environment` that created the bucket. + + .. attribute:: key + + The unique cache key for this bucket + + .. attribute:: code + + The bytecode if it's loaded, otherwise `None`. + + +Builtin bytecode caches: + +.. autoclass:: jinja2.FileSystemBytecodeCache + +.. autoclass:: jinja2.MemcachedBytecodeCache + + +Utilities +--------- + +These helper functions and classes are useful if you add custom filters or +functions to a Jinja2 environment. + +.. autofunction:: jinja2.environmentfilter + +.. autofunction:: jinja2.contextfilter + +.. autofunction:: jinja2.evalcontextfilter + +.. autofunction:: jinja2.environmentfunction + +.. autofunction:: jinja2.contextfunction + +.. autofunction:: jinja2.evalcontextfunction + +.. function:: escape(s) + + Convert the characters ``&``, ``<``, ``>``, ``'``, and ``"`` in string `s` + to HTML-safe sequences. Use this if you need to display text that might + contain such characters in HTML. This function will not escaped objects + that do have an HTML representation such as already escaped data. + + The return value is a :class:`Markup` string. + +.. autofunction:: jinja2.clear_caches + +.. autofunction:: jinja2.is_undefined + +.. autoclass:: jinja2.Markup([string]) + :members: escape, unescape, striptags + +.. admonition:: Note + + The Jinja2 :class:`Markup` class is compatible with at least Pylons and + Genshi. It's expected that more template engines and framework will pick + up the `__html__` concept soon. + + +Exceptions +---------- + +.. autoexception:: jinja2.TemplateError + +.. autoexception:: jinja2.UndefinedError + +.. autoexception:: jinja2.TemplateNotFound + +.. autoexception:: jinja2.TemplatesNotFound + +.. autoexception:: jinja2.TemplateSyntaxError + + .. attribute:: message + + The error message as utf-8 bytestring. + + .. attribute:: lineno + + The line number where the error occurred + + .. attribute:: name + + The load name for the template as unicode string. + + .. attribute:: filename + + The filename that loaded the template as bytestring in the encoding + of the file system (most likely utf-8 or mbcs on Windows systems). + + The reason why the filename and error message are bytestrings and not + unicode strings is that Python 2.x is not using unicode for exceptions + and tracebacks as well as the compiler. This will change with Python 3. + +.. autoexception:: jinja2.TemplateAssertionError + + +.. _writing-filters: + +Custom Filters +-------------- + +Custom filters are just regular Python functions that take the left side of +the filter as first argument and the the arguments passed to the filter as +extra arguments or keyword arguments. + +For example in the filter ``{{ 42|myfilter(23) }}`` the function would be +called with ``myfilter(42, 23)``. Here for example a simple filter that can +be applied to datetime objects to format them:: + + def datetimeformat(value, format='%H:%M / %d-%m-%Y'): + return value.strftime(format) + +You can register it on the template environment by updating the +:attr:`~Environment.filters` dict on the environment:: + + environment.filters['datetimeformat'] = datetimeformat + +Inside the template it can then be used as follows: + +.. sourcecode:: jinja + + written on: {{ article.pub_date|datetimeformat }} + publication date: {{ article.pub_date|datetimeformat('%d-%m-%Y') }} + +Filters can also be passed the current template context or environment. This +is useful if a filter wants to return an undefined value or check the current +:attr:`~Environment.autoescape` setting. For this purpose three decorators +exist: :func:`environmentfilter`, :func:`contextfilter` and +:func:`evalcontextfilter`. + +Here a small example filter that breaks a text into HTML line breaks and +paragraphs and marks the return value as safe HTML string if autoescaping is +enabled:: + + import re + from jinja2 import evalcontextfilter, Markup, escape + + _paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}') + + @evalcontextfilter + def nl2br(eval_ctx, value): + result = u'\n\n'.join(u'<p>%s</p>' % p.replace('\n', Markup('<br>\n')) + for p in _paragraph_re.split(escape(value))) + if eval_ctx.autoescape: + result = Markup(result) + return result + +Context filters work the same just that the first argument is the current +active :class:`Context` rather then the environment. + + +.. _eval-context: + +Evaluation Context +------------------ + +The evaluation context (short eval context or eval ctx) is a new object +introduced in Jinja 2.4 that makes it possible to activate and deactivate +compiled features at runtime. + +Currently it is only used to enable and disable the automatic escaping but +can be used for extensions as well. + +In previous Jinja versions filters and functions were marked as +environment callables in order to check for the autoescape status from the +environment. In new versions it's encouraged to check the setting from the +evaluation context instead. + +Previous versions:: + + @environmentfilter + def filter(env, value): + result = do_something(value) + if env.autoescape: + result = Markup(result) + return result + +In new versions you can either use a :func:`contextfilter` and access the +evaluation context from the actual context, or use a +:func:`evalcontextfilter` which directly passes the evaluation context to +the function:: + + @contextfilter + def filter(context, value): + result = do_something(value) + if context.eval_ctx.autoescape: + result = Markup(result) + return result + + @evalcontextfilter + def filter(eval_ctx, value): + result = do_something(value) + if eval_ctx.autoescape: + result = Markup(result) + return result + +The evaluation context must not be modified at runtime. Modifications +must only happen with a :class:`nodes.EvalContextModifier` and +:class:`nodes.ScopedEvalContextModifier` from an extension, not on the +eval context object itself. + +.. autoclass:: jinja2.nodes.EvalContext + + .. attribute:: autoescape + + `True` or `False` depending on if autoescaping is active or not. + + .. attribute:: volatile + + `True` if the compiler cannot evaluate some expressions at compile + time. At runtime this should always be `False`. + + +.. _writing-tests: + +Custom Tests +------------ + +Tests work like filters just that there is no way for a test to get access +to the environment or context and that they can't be chained. The return +value of a test should be `True` or `False`. The purpose of a test is to +give the template designers the possibility to perform type and conformability +checks. + +Here a simple test that checks if a variable is a prime number:: + + import math + + def is_prime(n): + if n == 2: + return True + for i in xrange(2, int(math.ceil(math.sqrt(n))) + 1): + if n % i == 0: + return False + return True + + +You can register it on the template environment by updating the +:attr:`~Environment.tests` dict on the environment:: + + environment.tests['prime'] = is_prime + +A template designer can then use the test like this: + +.. sourcecode:: jinja + + {% if 42 is prime %} + 42 is a prime number + {% else %} + 42 is not a prime number + {% endif %} + + +.. _global-namespace: + +The Global Namespace +-------------------- + +Variables stored in the :attr:`Environment.globals` dict are special as they +are available for imported templates too, even if they are imported without +context. This is the place where you can put variables and functions +that should be available all the time. Additionally :attr:`Template.globals` +exist that are variables available to a specific template that are available +to all :meth:`~Template.render` calls. + + +.. _low-level-api: + +Low Level API +------------- + +The low level API exposes functionality that can be useful to understand some +implementation details, debugging purposes or advanced :ref:`extension +<jinja-extensions>` techniques. Unless you know exactly what you are doing we +don't recommend using any of those. + +.. automethod:: Environment.lex + +.. automethod:: Environment.parse + +.. automethod:: Environment.preprocess + +.. automethod:: Template.new_context + +.. method:: Template.root_render_func(context) + + This is the low level render function. It's passed a :class:`Context` + that has to be created by :meth:`new_context` of the same template or + a compatible template. This render function is generated by the + compiler from the template code and returns a generator that yields + unicode strings. + + If an exception in the template code happens the template engine will + not rewrite the exception but pass through the original one. As a + matter of fact this function should only be called from within a + :meth:`render` / :meth:`generate` / :meth:`stream` call. + +.. attribute:: Template.blocks + + A dict of block render functions. Each of these functions works exactly + like the :meth:`root_render_func` with the same limitations. + +.. attribute:: Template.is_up_to_date + + This attribute is `False` if there is a newer version of the template + available, otherwise `True`. + +.. admonition:: Note + + The low-level API is fragile. Future Jinja2 versions will try not to + change it in a backwards incompatible way but modifications in the Jinja2 + core may shine through. For example if Jinja2 introduces a new AST node + in later versions that may be returned by :meth:`~Environment.parse`. + +The Meta API +------------ + +.. versionadded:: 2.2 + +The meta API returns some information about abstract syntax trees that +could help applications to implement more advanced template concepts. All +the functions of the meta API operate on an abstract syntax tree as +returned by the :meth:`Environment.parse` method. + +.. autofunction:: jinja2.meta.find_undeclared_variables + +.. autofunction:: jinja2.meta.find_referenced_templates diff --git a/deps/v8_inspector/deps/jinja2/docs/cache_extension.py b/deps/v8_inspector/deps/jinja2/docs/cache_extension.py new file mode 100644 index 00000000000000..ccdefa2ff313e6 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/cache_extension.py @@ -0,0 +1,56 @@ +from jinja2 import nodes +from jinja2.ext import Extension + + +class FragmentCacheExtension(Extension): + # a set of names that trigger the extension. + tags = set(['cache']) + + def __init__(self, environment): + super(FragmentCacheExtension, self).__init__(environment) + + # add the defaults to the environment + environment.extend( + fragment_cache_prefix='', + fragment_cache=None + ) + + def parse(self, parser): + # the first token is the token that started the tag. In our case + # we only listen to ``'cache'`` so this will be a name token with + # `cache` as value. We get the line number so that we can give + # that line number to the nodes we create by hand. + lineno = next(parser.stream).lineno + + # now we parse a single expression that is used as cache key. + args = [parser.parse_expression()] + + # if there is a comma, the user provided a timeout. If not use + # None as second parameter. + if parser.stream.skip_if('comma'): + args.append(parser.parse_expression()) + else: + args.append(nodes.Const(None)) + + # now we parse the body of the cache block up to `endcache` and + # drop the needle (which would always be `endcache` in that case) + body = parser.parse_statements(['name:endcache'], drop_needle=True) + + # now return a `CallBlock` node that calls our _cache_support + # helper method on this extension. + return nodes.CallBlock(self.call_method('_cache_support', args), + [], [], body).set_lineno(lineno) + + def _cache_support(self, name, timeout, caller): + """Helper callback.""" + key = self.environment.fragment_cache_prefix + name + + # try to load the block from the cache + # if there is no fragment in the cache, render it and store + # it in the cache. + rv = self.environment.fragment_cache.get(key) + if rv is not None: + return rv + rv = caller() + self.environment.fragment_cache.add(key, rv, timeout) + return rv diff --git a/deps/v8_inspector/deps/jinja2/docs/changelog.rst b/deps/v8_inspector/deps/jinja2/docs/changelog.rst new file mode 100644 index 00000000000000..9f114842654b69 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/changelog.rst @@ -0,0 +1,3 @@ +.. module:: jinja2 + +.. include:: ../CHANGES diff --git a/deps/v8_inspector/deps/jinja2/docs/conf.py b/deps/v8_inspector/deps/jinja2/docs/conf.py new file mode 100644 index 00000000000000..6e990be1c719ca --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/conf.py @@ -0,0 +1,160 @@ +# -*- coding: utf-8 -*- +# +# Jinja2 documentation build configuration file, created by +# sphinx-quickstart on Sun Apr 27 21:42:41 2008. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# The contents of this file are pickled, so don't put values in the namespace +# that aren't pickleable (module imports are okay, they're removed automatically). +# +# All configuration values have a default value; values that are commented out +# serve to show the default value. + +import sys, os + +# If your extensions are in another directory, add it here. If the directory +# is relative to the documentation root, use os.path.abspath to make it +# absolute, like shown here. +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +# General configuration +# --------------------- + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'jinjaext'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General substitutions. +project = 'Jinja2' +copyright = '2008, Armin Ronacher' + +# The default replacements for |version| and |release|, also used in various +# other places throughout the built documents. +# +# The short X.Y version. +import pkg_resources +try: + release = pkg_resources.get_distribution('Jinja2').version +except ImportError: + print 'To build the documentation, The distribution information of Jinja2' + print 'Has to be available. Either install the package into your' + print 'development environment or run "setup.py develop" to setup the' + print 'metadata. A virtualenv is recommended!' + sys.exit(1) +if 'dev' in release: + release = release.split('dev')[0] + 'dev' +version = '.'.join(release.split('.')[:2]) + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +today_fmt = '%B %d, %Y' + +# List of documents that shouldn't be included in the build. +#unused_docs = [] + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'jinjaext.JinjaStyle' + + +# Options for HTML output +# ----------------------- + +html_theme = 'jinja' +html_theme_path = ['_themes'] + +# The name for this set of Sphinx documents. If None, it defaults to +# "<project> v<release> documentation". +#html_title = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# no modindex +html_use_modindex = False + +# If true, the reST sources are included in the HTML build as _sources/<name>. +#html_copy_source = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a <link> tag referring to it. +#html_use_opensearch = False + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Jinja2doc' + + +# Options for LaTeX output +# ------------------------ + +# The paper size ('letter' or 'a4'). +latex_paper_size = 'a4' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, document class [howto/manual]). +latex_documents = [ + ('latexindex', 'Jinja2.tex', 'Jinja2 Documentation', 'Armin Ronacher', + 'manual'), +] + +# Additional stuff for LaTeX +latex_elements = { + 'fontpkg': r'\usepackage{mathpazo}', + 'papersize': 'a4paper', + 'pointsize': '12pt', + 'preamble': r''' +\usepackage{jinjastyle} + +% i hate you latex +\DeclareUnicodeCharacter{14D}{o} +''' +} + +latex_use_parts = True + +latex_additional_files = ['jinjastyle.sty', 'logo.pdf'] + +# If false, no module index is generated. +latex_use_modindex = False + +html_sidebars = { + 'index': ['sidebarlogo.html', 'sidebarintro.html', 'sourcelink.html', + 'searchbox.html'], + '**': ['sidebarlogo.html', 'localtoc.html', 'relations.html', + 'sourcelink.html', 'searchbox.html'] +} diff --git a/deps/v8_inspector/deps/jinja2/docs/contents.rst.inc b/deps/v8_inspector/deps/jinja2/docs/contents.rst.inc new file mode 100644 index 00000000000000..7ee68703f4a347 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/contents.rst.inc @@ -0,0 +1,23 @@ +Jinja2 Documentation +-------------------- + +.. toctree:: + :maxdepth: 2 + + intro + api + sandbox + templates + extensions + integration + switching + tricks + +Additional Information +---------------------- + +.. toctree:: + :maxdepth: 2 + + faq + changelog diff --git a/deps/v8_inspector/deps/jinja2/docs/extensions.rst b/deps/v8_inspector/deps/jinja2/docs/extensions.rst new file mode 100644 index 00000000000000..955708ba4e67d2 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/extensions.rst @@ -0,0 +1,346 @@ +.. _jinja-extensions: + +Extensions +========== + +Jinja2 supports extensions that can add extra filters, tests, globals or even +extend the parser. The main motivation of extensions is to move often used +code into a reusable class like adding support for internationalization. + + +Adding Extensions +----------------- + +Extensions are added to the Jinja2 environment at creation time. Once the +environment is created additional extensions cannot be added. To add an +extension pass a list of extension classes or import paths to the +`extensions` parameter of the :class:`Environment` constructor. The following +example creates a Jinja2 environment with the i18n extension loaded:: + + jinja_env = Environment(extensions=['jinja2.ext.i18n']) + + +.. _i18n-extension: + +i18n Extension +-------------- + +**Import name:** `jinja2.ext.i18n` + +The i18n extension can be used in combination with `gettext`_ or `babel`_. If +the i18n extension is enabled Jinja2 provides a `trans` statement that marks +the wrapped string as translatable and calls `gettext`. + +After enabling, dummy `_` function that forwards calls to `gettext` is added +to the environment globals. An internationalized application then has to +provide a `gettext` function and optionally an `ngettext` function into the +namespace, either globally or for each rendering. + +Environment Methods +~~~~~~~~~~~~~~~~~~~ + +After enabling the extension, the environment provides the following +additional methods: + +.. method:: jinja2.Environment.install_gettext_translations(translations, newstyle=False) + + Installs a translation globally for that environment. The translations + object provided must implement at least `ugettext` and `ungettext`. + The `gettext.NullTranslations` and `gettext.GNUTranslations` classes + as well as `Babel`_\s `Translations` class are supported. + + .. versionchanged:: 2.5 newstyle gettext added + +.. method:: jinja2.Environment.install_null_translations(newstyle=False) + + Install dummy gettext functions. This is useful if you want to prepare + the application for internationalization but don't want to implement the + full internationalization system yet. + + .. versionchanged:: 2.5 newstyle gettext added + +.. method:: jinja2.Environment.install_gettext_callables(gettext, ngettext, newstyle=False) + + Installs the given `gettext` and `ngettext` callables into the + environment as globals. They are supposed to behave exactly like the + standard library's :func:`gettext.ugettext` and + :func:`gettext.ungettext` functions. + + If `newstyle` is activated, the callables are wrapped to work like + newstyle callables. See :ref:`newstyle-gettext` for more information. + + .. versionadded:: 2.5 + +.. method:: jinja2.Environment.uninstall_gettext_translations() + + Uninstall the translations again. + +.. method:: jinja2.Environment.extract_translations(source) + + Extract localizable strings from the given template node or source. + + For every string found this function yields a ``(lineno, function, + message)`` tuple, where: + + * `lineno` is the number of the line on which the string was found, + * `function` is the name of the `gettext` function used (if the + string was extracted from embedded Python code), and + * `message` is the string itself (a `unicode` object, or a tuple + of `unicode` objects for functions with multiple string arguments). + + If `Babel`_ is installed, :ref:`the babel integration <babel-integration>` + can be used to extract strings for babel. + +For a web application that is available in multiple languages but gives all +the users the same language (for example a multilingual forum software +installed for a French community) may load the translations once and add the +translation methods to the environment at environment generation time:: + + translations = get_gettext_translations() + env = Environment(extensions=['jinja2.ext.i18n']) + env.install_gettext_translations(translations) + +The `get_gettext_translations` function would return the translator for the +current configuration. (For example by using `gettext.find`) + +The usage of the `i18n` extension for template designers is covered as part +:ref:`of the template documentation <i18n-in-templates>`. + +.. _gettext: http://docs.python.org/dev/library/gettext +.. _Babel: http://babel.edgewall.org/ + +.. _newstyle-gettext: + +Newstyle Gettext +~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.5 + +Starting with version 2.5 you can use newstyle gettext calls. These are +inspired by trac's internal gettext functions and are fully supported by +the babel extraction tool. They might not work as expected by other +extraction tools in case you are not using Babel's. + +What's the big difference between standard and newstyle gettext calls? In +general they are less to type and less error prone. Also if they are used +in an autoescaping environment they better support automatic escaping. +Here are some common differences between old and new calls: + +standard gettext: + +.. sourcecode:: html+jinja + + {{ gettext('Hello World!') }} + {{ gettext('Hello %(name)s!')|format(name='World') }} + {{ ngettext('%(num)d apple', '%(num)d apples', apples|count)|format( + num=apples|count + )}} + +newstyle gettext looks like this instead: + +.. sourcecode:: html+jinja + + {{ gettext('Hello World!') }} + {{ gettext('Hello %(name)s!', name='World') }} + {{ ngettext('%(num)d apple', '%(num)d apples', apples|count) }} + +The advantages of newstyle gettext are that you have less to type and that +named placeholders become mandatory. The latter sounds like a +disadvantage but solves a lot of troubles translators are often facing +when they are unable to switch the positions of two placeholder. With +newstyle gettext, all format strings look the same. + +Furthermore with newstyle gettext, string formatting is also used if no +placeholders are used which makes all strings behave exactly the same. +Last but not least are newstyle gettext calls able to properly mark +strings for autoescaping which solves lots of escaping related issues many +templates are experiencing over time when using autoescaping. + +Expression Statement +-------------------- + +**Import name:** `jinja2.ext.do` + +The "do" aka expression-statement extension adds a simple `do` tag to the +template engine that works like a variable expression but ignores the +return value. + +.. _loopcontrols-extension: + +Loop Controls +------------- + +**Import name:** `jinja2.ext.loopcontrols` + +This extension adds support for `break` and `continue` in loops. After +enabling, Jinja2 provides those two keywords which work exactly like in +Python. + +.. _with-extension: + +With Statement +-------------- + +**Import name:** `jinja2.ext.with_` + +.. versionadded:: 2.3 + +This extension adds support for the with keyword. Using this keyword it +is possible to enforce a nested scope in a template. Variables can be +declared directly in the opening block of the with statement or using a +standard `set` statement directly within. + +.. _autoescape-extension: + +Autoescape Extension +-------------------- + +**Import name:** `jinja2.ext.autoescape` + +.. versionadded:: 2.4 + +The autoescape extension allows you to toggle the autoescape feature from +within the template. If the environment's :attr:`~Environment.autoescape` +setting is set to `False` it can be activated, if it's `True` it can be +deactivated. The setting overriding is scoped. + + +.. _writing-extensions: + +Writing Extensions +------------------ + +.. module:: jinja2.ext + +By writing extensions you can add custom tags to Jinja2. This is a non-trivial +task and usually not needed as the default tags and expressions cover all +common use cases. The i18n extension is a good example of why extensions are +useful. Another one would be fragment caching. + +When writing extensions you have to keep in mind that you are working with the +Jinja2 template compiler which does not validate the node tree you are passing +to it. If the AST is malformed you will get all kinds of compiler or runtime +errors that are horrible to debug. Always make sure you are using the nodes +you create correctly. The API documentation below shows which nodes exist and +how to use them. + +Example Extension +~~~~~~~~~~~~~~~~~ + +The following example implements a `cache` tag for Jinja2 by using the +`Werkzeug`_ caching contrib module: + +.. literalinclude:: cache_extension.py + :language: python + +And here is how you use it in an environment:: + + from jinja2 import Environment + from werkzeug.contrib.cache import SimpleCache + + env = Environment(extensions=[FragmentCacheExtension]) + env.fragment_cache = SimpleCache() + +Inside the template it's then possible to mark blocks as cacheable. The +following example caches a sidebar for 300 seconds: + +.. sourcecode:: html+jinja + + {% cache 'sidebar', 300 %} + <div class="sidebar"> + ... + </div> + {% endcache %} + +.. _Werkzeug: http://werkzeug.pocoo.org/ + +Extension API +~~~~~~~~~~~~~ + +Extensions always have to extend the :class:`jinja2.ext.Extension` class: + +.. autoclass:: Extension + :members: preprocess, filter_stream, parse, attr, call_method + + .. attribute:: identifier + + The identifier of the extension. This is always the true import name + of the extension class and must not be changed. + + .. attribute:: tags + + If the extension implements custom tags this is a set of tag names + the extension is listening for. + +Parser API +~~~~~~~~~~ + +The parser passed to :meth:`Extension.parse` provides ways to parse +expressions of different types. The following methods may be used by +extensions: + +.. autoclass:: jinja2.parser.Parser + :members: parse_expression, parse_tuple, parse_assign_target, + parse_statements, free_identifier, fail + + .. attribute:: filename + + The filename of the template the parser processes. This is **not** + the load name of the template. For the load name see :attr:`name`. + For templates that were not loaded form the file system this is + `None`. + + .. attribute:: name + + The load name of the template. + + .. attribute:: stream + + The current :class:`~jinja2.lexer.TokenStream` + +.. autoclass:: jinja2.lexer.TokenStream + :members: push, look, eos, skip, next, next_if, skip_if, expect + + .. attribute:: current + + The current :class:`~jinja2.lexer.Token`. + +.. autoclass:: jinja2.lexer.Token + :members: test, test_any + + .. attribute:: lineno + + The line number of the token + + .. attribute:: type + + The type of the token. This string is interned so you may compare + it with arbitrary strings using the `is` operator. + + .. attribute:: value + + The value of the token. + +There is also a utility function in the lexer module that can count newline +characters in strings: + +.. autofunction:: jinja2.lexer.count_newlines + +AST +~~~ + +The AST (Abstract Syntax Tree) is used to represent a template after parsing. +It's build of nodes that the compiler then converts into executable Python +code objects. Extensions that provide custom statements can return nodes to +execute custom Python code. + +The list below describes all nodes that are currently available. The AST may +change between Jinja2 versions but will stay backwards compatible. + +For more information have a look at the repr of :meth:`jinja2.Environment.parse`. + +.. module:: jinja2.nodes + +.. jinjanodes:: + +.. autoexception:: Impossible diff --git a/deps/v8_inspector/deps/jinja2/docs/faq.rst b/deps/v8_inspector/deps/jinja2/docs/faq.rst new file mode 100644 index 00000000000000..0b0375e190863d --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/faq.rst @@ -0,0 +1,191 @@ +Frequently Asked Questions +========================== + +This page answers some of the often asked questions about Jinja. + +.. highlight:: html+jinja + +Why is it called Jinja? +----------------------- + +The name Jinja was chosen because it's the name of a Japanese temple and +temple and template share a similar pronunciation. It is not named after +the city in Uganda. + +How fast is it? +--------------- + +We really hate benchmarks especially since they don't reflect much. The +performance of a template depends on many factors and you would have to +benchmark different engines in different situations. The benchmarks from the +testsuite show that Jinja2 has a similar performance to `Mako`_ and is between +10 and 20 times faster than Django's template engine or Genshi. These numbers +should be taken with tons of salt as the benchmarks that took these numbers +only test a few performance related situations such as looping. Generally +speaking the performance of a template engine doesn't matter much as the +usual bottleneck in a web application is either the database or the application +code. + +.. _Mako: http://www.makotemplates.org/ + +How Compatible is Jinja2 with Django? +------------------------------------- + +The default syntax of Jinja2 matches Django syntax in many ways. However +this similarity doesn't mean that you can use a Django template unmodified +in Jinja2. For example filter arguments use a function call syntax rather +than a colon to separate filter name and arguments. Additionally the +extension interface in Jinja is fundamentally different from the Django one +which means that your custom tags won't work any longer. + +Generally speaking you will use much less custom extensions as the Jinja +template system allows you to use a certain subset of Python expressions +which can replace most Django extensions. For example instead of using +something like this:: + + {% load comments %} + {% get_latest_comments 10 as latest_comments %} + {% for comment in latest_comments %} + ... + {% endfor %} + +You will most likely provide an object with attributes to retrieve +comments from the database:: + + {% for comment in models.comments.latest(10) %} + ... + {% endfor %} + +Or directly provide the model for quick testing:: + + {% for comment in Comment.objects.order_by('-pub_date')[:10] %} + ... + {% endfor %} + +Please keep in mind that even though you may put such things into templates +it still isn't a good idea. Queries should go into the view code and not +the template! + +Isn't it a terrible idea to put Logic into Templates? +----------------------------------------------------- + +Without a doubt you should try to remove as much logic from templates as +possible. But templates without any logic mean that you have to do all +the processing in the code which is boring and stupid. A template engine +that does that is shipped with Python and called `string.Template`. Comes +without loops and if conditions and is by far the fastest template engine +you can get for Python. + +So some amount of logic is required in templates to keep everyone happy. +And Jinja leaves it pretty much to you how much logic you want to put into +templates. There are some restrictions in what you can do and what not. + +Jinja2 neither allows you to put arbitrary Python code into templates nor +does it allow all Python expressions. The operators are limited to the +most common ones and more advanced expressions such as list comprehensions +and generator expressions are not supported. This keeps the template engine +easier to maintain and templates more readable. + +Why is Autoescaping not the Default? +------------------------------------ + +There are multiple reasons why automatic escaping is not the default mode +and also not the recommended one. While automatic escaping of variables +means that you will less likely have an XSS problem it also causes a huge +amount of extra processing in the template engine which can cause serious +performance problems. As Python doesn't provide a way to mark strings as +unsafe Jinja has to hack around that limitation by providing a custom +string class (the :class:`Markup` string) that safely interacts with safe +and unsafe strings. + +With explicit escaping however the template engine doesn't have to perform +any safety checks on variables. Also a human knows not to escape integers +or strings that may never contain characters one has to escape or already +HTML markup. For example when iterating over a list over a table of +integers and floats for a table of statistics the template designer can +omit the escaping because he knows that integers or floats don't contain +any unsafe parameters. + +Additionally Jinja2 is a general purpose template engine and not only used +for HTML/XML generation. For example you may generate LaTeX, emails, +CSS, JavaScript, or configuration files. + +Why is the Context immutable? +----------------------------- + +When writing a :func:`contextfunction` or something similar you may have +noticed that the context tries to stop you from modifying it. If you have +managed to modify the context by using an internal context API you may +have noticed that changes in the context don't seem to be visible in the +template. The reason for this is that Jinja uses the context only as +primary data source for template variables for performance reasons. + +If you want to modify the context write a function that returns a variable +instead that one can assign to a variable by using set:: + + {% set comments = get_latest_comments() %} + +My tracebacks look weird. What's happening? +-------------------------------------------- + +If the debugsupport module is not compiled and you are using a Python +installation without ctypes (Python 2.4 without ctypes, Jython or Google's +AppEngine) Jinja2 is unable to provide correct debugging information and +the traceback may be incomplete. There is currently no good workaround +for Jython or the AppEngine as ctypes is unavailable there and it's not +possible to use the debugsupport extension. + +If you are working in the Google AppEngine development server you can +whitelist the ctypes module to restore the tracebacks. This however won't +work in production environments:: + + import os + if os.environ.get('SERVER_SOFTWARE', '').startswith('Dev'): + from google.appengine.tools.dev_appserver import HardenedModulesHook + HardenedModulesHook._WHITE_LIST_C_MODULES += ['_ctypes', 'gestalt'] + +Credit for this snippet goes to `Thomas Johansson +<http://stackoverflow.com/questions/3086091/debug-jinja2-in-google-app-engine/3694434#3694434>`_ + +Why is there no Python 2.3/2.4/2.5/3.1/3.2 support? +--------------------------------------------------- + +Python 2.3 is missing a lot of features that are used heavily in Jinja2. This +decision was made as with the upcoming Python 2.6 and 3.0 versions it becomes +harder to maintain the code for older Python versions. If you really need +Python 2.3 support you either have to use `Jinja 1`_ or other templating +engines that still support 2.3. + +Python 2.4/2.5/3.1/3.2 support was removed when we switched to supporting +Python 2 and 3 by the same sourcecode (without using 2to3). It was required to +drop support because only Python 2.6/2.7 and >=3.3 support byte and unicode +literals in a way compatible to each other version. If you really need support +for older Python 2 (or 3) versions, you can just use Jinja2 2.6. + +My Macros are overridden by something +------------------------------------- + +In some situations the Jinja scoping appears arbitrary: + +layout.tmpl: + +.. sourcecode:: jinja + + {% macro foo() %}LAYOUT{% endmacro %} + {% block body %}{% endblock %} + +child.tmpl: + +.. sourcecode:: jinja + + {% extends 'layout.tmpl' %} + {% macro foo() %}CHILD{% endmacro %} + {% block body %}{{ foo() }}{% endblock %} + +This will print ``LAYOUT`` in Jinja2. This is a side effect of having +the parent template evaluated after the child one. This allows child +templates passing information to the parent template. To avoid this +issue rename the macro or variable in the parent template to have an +uncommon prefix. + +.. _Jinja 1: http://jinja.pocoo.org/1/ diff --git a/deps/v8_inspector/deps/jinja2/docs/index.rst b/deps/v8_inspector/deps/jinja2/docs/index.rst new file mode 100644 index 00000000000000..a08d6281df47eb --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/index.rst @@ -0,0 +1,34 @@ +Welcome to Jinja2 +================= + +Jinja2 is a modern and designer-friendly templating language for Python, +modelled after Django's templates. It is fast, widely used and secure +with the optional sandboxed template execution environment: + +.. sourcecode:: html+jinja + + <title>{% block title %}{% endblock %}</title> + <ul> + {% for user in users %} + <li><a href="{{ user.url }}">{{ user.username }}</a></li> + {% endfor %} + </ul> + +**Features:** + +- sandboxed execution +- powerful automatic HTML escaping system for XSS prevention +- template inheritance +- compiles down to the optimal python code just in time +- optional ahead-of-time template compilation +- easy to debug. Line numbers of exceptions directly point to + the correct line in the template. +- configurable syntax + +.. include:: contents.rst.inc + +If you can't find the information you're looking for, have a look at the +index or try to find it using the search function: + +* :ref:`genindex` +* :ref:`search` diff --git a/deps/v8_inspector/deps/jinja2/docs/integration.rst b/deps/v8_inspector/deps/jinja2/docs/integration.rst new file mode 100644 index 00000000000000..92a3b14794b39f --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/integration.rst @@ -0,0 +1,101 @@ +Integration +=========== + +Jinja2 provides some code for integration into other tools such as frameworks, +the `Babel`_ library or your favourite editor for fancy code highlighting. +This is a brief description of whats included. + +Files to help integration are available +`here. <https://github.com/mitsuhiko/jinja2/tree/master/ext>`_ + +.. _babel-integration: + +Babel Integration +----------------- + +Jinja provides support for extracting gettext messages from templates via a +`Babel`_ extractor entry point called `jinja2.ext.babel_extract`. The Babel +support is implemented as part of the :ref:`i18n-extension` extension. + +Gettext messages extracted from both `trans` tags and code expressions. + +To extract gettext messages from templates, the project needs a Jinja2 section +in its Babel extraction method `mapping file`_: + +.. sourcecode:: ini + + [jinja2: **/templates/**.html] + encoding = utf-8 + +The syntax related options of the :class:`Environment` are also available as +configuration values in the mapping file. For example to tell the extraction +that templates use ``%`` as `line_statement_prefix` you can use this code: + +.. sourcecode:: ini + + [jinja2: **/templates/**.html] + encoding = utf-8 + line_statement_prefix = % + +:ref:`jinja-extensions` may also be defined by passing a comma separated list +of import paths as `extensions` value. The i18n extension is added +automatically. + +.. versionchanged:: 2.7 + + Until 2.7 template syntax errors were always ignored. This was done + since many people are dropping non template html files into the + templates folder and it would randomly fail. The assumption was that + testsuites will catch syntax errors in templates anyways. If you don't + want that behavior you can add ``silent=false`` to the settings and + exceptions are propagated. + +.. _mapping file: http://babel.edgewall.org/wiki/Documentation/messages.html#extraction-method-mapping-and-configuration + +Pylons +------ + +With `Pylons`_ 0.9.7 onwards it's incredible easy to integrate Jinja into a +Pylons powered application. + +The template engine is configured in `config/environment.py`. The configuration +for Jinja2 looks something like that:: + + from jinja2 import Environment, PackageLoader + config['pylons.app_globals'].jinja_env = Environment( + loader=PackageLoader('yourapplication', 'templates') + ) + +After that you can render Jinja templates by using the `render_jinja` function +from the `pylons.templating` module. + +Additionally it's a good idea to set the Pylons' `c` object into strict mode. +Per default any attribute to not existing attributes on the `c` object return +an empty string and not an undefined object. To change this just use this +snippet and add it into your `config/environment.py`:: + + config['pylons.strict_c'] = True + +.. _Pylons: http://www.pylonshq.com/ + +TextMate +-------- + +There is a bundle for TextMate that supports syntax highlighting for Jinja1 and Jinja2 for text based +templates as well as HTML. It also contains a few often used snippets. + +.. _TextMate Bundle: https://github.com/mitsuhiko/jinja2-tmbundle + +Vim +--- + +A syntax plugin for `Vim`_ exists in the Vim-scripts directory as well as the +`ext` folder at the root of the Jinja2 project. `The script +<http://www.vim.org/scripts/script.php?script_id=1856>`_ supports Jinja1 and +Jinja2. Once installed two file types are available `jinja` and `htmljinja`. +The first one for text based templates, the latter for HTML templates. + +Copy the files into your `syntax` folder. + +.. _Babel: http://babel.edgewall.org/ +.. _Vim: http://www.vim.org/ diff --git a/deps/v8_inspector/deps/jinja2/docs/intro.rst b/deps/v8_inspector/deps/jinja2/docs/intro.rst new file mode 100644 index 00000000000000..b6da5ea5f12ad1 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/intro.rst @@ -0,0 +1,123 @@ +Introduction +============ + +This is the documentation for the Jinja2 general purpose templating language. +Jinja2 is a library for Python that is designed to be flexible, fast and secure. + +If you have any exposure to other text-based template languages, such as Smarty or +Django, you should feel right at home with Jinja2. It's both designer and +developer friendly by sticking to Python's principles and adding functionality +useful for templating environments. + +Prerequisites +------------- + +Jinja2 works with Python 2.6.x, 2.7.x and >= 3.3. If you are using Python +3.2 you can use an older release of Jinja2 (2.6) as support for Python 3.2 +was dropped in Jinja2 version 2.7. + +If you wish to use the :class:`~jinja2.PackageLoader` class, you will also +need `setuptools`_ or `distribute`_ installed at runtime. + +Installation +------------ + +You have multiple ways to install Jinja2. If you are unsure what to do, go +with the Python egg or tarball. + +As a Python egg (via `easy_install`) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can install the most recent Jinja2 version using `easy_install`_ or `pip`_:: + + easy_install Jinja2 + pip install Jinja2 + +This will install a Jinja2 egg in your Python installation's site-packages +directory. + +(If you are installing from the Windows command line omit the `sudo` and make +sure to run the command as user with administrator rights) + +From the tarball release +~~~~~~~~~~~~~~~~~~~~~~~~~ + +1. Download the most recent tarball from the `download page`_ +2. Unpack the tarball +3. ``sudo python setup.py install`` + +Note that you either have to have `setuptools` or `distribute` installed; +the latter is preferred. + +This will install Jinja2 into your Python installation's site-packages directory. + +Installing the development version +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +1. Install `git`_ +2. ``git clone git://github.com/mitsuhiko/jinja2.git`` +3. ``cd jinja2`` +4. ``ln -s jinja2 /usr/lib/python2.X/site-packages`` + +As an alternative to steps 4 you can also do ``python setup.py develop`` +which will install the package via `distribute` in development mode. This also +has the advantage that the C extensions are compiled. + +.. _download page: http://pypi.python.org/pypi/Jinja2 +.. _distribute: http://pypi.python.org/pypi/distribute +.. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools +.. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall +.. _pip: http://pypi.python.org/pypi/pip +.. _git: http://git-scm.org/ + + +MarkupSafe Dependency +~~~~~~~~~~~~~~~~~~~~~ + +As of version 2.7 Jinja2 depends on the `MarkupSafe`_ module. If you +install Jinja2 via `pip` or `easy_install` it will be installed +automatically for you. + +.. _MarkupSafe: http://pypi.python.org/pypi/MarkupSafe + +Basic API Usage +--------------- + +This section gives you a brief introduction to the Python API for Jinja2 +templates. + +The most basic way to create a template and render it is through +:class:`~jinja2.Template`. This however is not the recommended way to +work with it if your templates are not loaded from strings but the file +system or another data source: + +>>> from jinja2 import Template +>>> template = Template('Hello {{ name }}!') +>>> template.render(name='John Doe') +u'Hello John Doe!' + +By creating an instance of :class:`~jinja2.Template` you get back a new template +object that provides a method called :meth:`~jinja2.Template.render` which when +called with a dict or keyword arguments expands the template. The dict +or keywords arguments passed to the template are the so-called "context" +of the template. + +What you can see here is that Jinja2 is using unicode internally and the +return value is an unicode string. So make sure that your application is +indeed using unicode internally. + + +Experimental Python 3 Support +----------------------------- + +Jinja 2.7 brings experimental support for Python >=3.3. It means that all +unittests pass on the new version, but there might still be small bugs in +there and behavior might be inconsistent. If you notice any bugs, please +provide feedback in the `Jinja bug tracker`_. + +Also please keep in mind that the documentation is written with Python 2 +in mind, so you will have to adapt the shown code examples to Python 3 syntax +for yourself. + + +.. _Jinja bug tracker: http://github.com/mitsuhiko/jinja2/issues diff --git a/deps/v8_inspector/deps/jinja2/docs/jinjaext.py b/deps/v8_inspector/deps/jinja2/docs/jinjaext.py new file mode 100644 index 00000000000000..cdacba9d9cf62f --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/jinjaext.py @@ -0,0 +1,194 @@ +# -*- coding: utf-8 -*- +""" + Jinja Documentation Extensions + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Support for automatically documenting filters and tests. + + :copyright: Copyright 2008 by Armin Ronacher. + :license: BSD. +""" +import collections +import os +import re +import inspect +import jinja2 +from itertools import islice +from types import BuiltinFunctionType +from docutils import nodes +from docutils.statemachine import ViewList +from sphinx.ext.autodoc import prepare_docstring +from sphinx.application import TemplateBridge +from pygments.style import Style +from pygments.token import Keyword, Name, Comment, String, Error, \ + Number, Operator, Generic +from jinja2 import Environment, FileSystemLoader + + +def parse_rst(state, content_offset, doc): + node = nodes.section() + # hack around title style bookkeeping + surrounding_title_styles = state.memo.title_styles + surrounding_section_level = state.memo.section_level + state.memo.title_styles = [] + state.memo.section_level = 0 + state.nested_parse(doc, content_offset, node, match_titles=1) + state.memo.title_styles = surrounding_title_styles + state.memo.section_level = surrounding_section_level + return node.children + + +class JinjaStyle(Style): + title = 'Jinja Style' + default_style = "" + styles = { + Comment: 'italic #aaaaaa', + Comment.Preproc: 'noitalic #B11414', + Comment.Special: 'italic #505050', + + Keyword: 'bold #B80000', + Keyword.Type: '#808080', + + Operator.Word: 'bold #B80000', + + Name.Builtin: '#333333', + Name.Function: '#333333', + Name.Class: 'bold #333333', + Name.Namespace: 'bold #333333', + Name.Entity: 'bold #363636', + Name.Attribute: '#686868', + Name.Tag: 'bold #686868', + Name.Decorator: '#686868', + + String: '#AA891C', + Number: '#444444', + + Generic.Heading: 'bold #000080', + Generic.Subheading: 'bold #800080', + Generic.Deleted: '#aa0000', + Generic.Inserted: '#00aa00', + Generic.Error: '#aa0000', + Generic.Emph: 'italic', + Generic.Strong: 'bold', + Generic.Prompt: '#555555', + Generic.Output: '#888888', + Generic.Traceback: '#aa0000', + + Error: '#F00 bg:#FAA' + } + + +_sig_re = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*(\(.*?\))') + + +def format_function(name, aliases, func): + lines = inspect.getdoc(func).splitlines() + signature = '()' + if isinstance(func, BuiltinFunctionType): + match = _sig_re.match(lines[0]) + if match is not None: + del lines[:1 + bool(lines and not lines[0])] + signature = match.group(1) + else: + try: + argspec = inspect.getargspec(func) + if getattr(func, 'environmentfilter', False) or \ + getattr(func, 'contextfilter', False) or \ + getattr(func, 'evalcontextfilter', False): + del argspec[0][0] + signature = inspect.formatargspec(*argspec) + except: + pass + result = ['.. function:: %s%s' % (name, signature), ''] + result.extend(' ' + line for line in lines) + if aliases: + result.extend(('', ' :aliases: %s' % ', '.join( + '``%s``' % x for x in sorted(aliases)))) + return result + + +def dump_functions(mapping): + def directive(dirname, arguments, options, content, lineno, + content_offset, block_text, state, state_machine): + reverse_mapping = {} + for name, func in mapping.items(): + reverse_mapping.setdefault(func, []).append(name) + filters = [] + for func, names in reverse_mapping.items(): + aliases = sorted(names, key=lambda x: len(x)) + name = aliases.pop() + filters.append((name, aliases, func)) + filters.sort() + + result = ViewList() + for name, aliases, func in filters: + for item in format_function(name, aliases, func): + result.append(item, '<jinjaext>') + + node = nodes.paragraph() + state.nested_parse(result, content_offset, node) + return node.children + return directive + + +from jinja2.defaults import DEFAULT_FILTERS, DEFAULT_TESTS +jinja_filters = dump_functions(DEFAULT_FILTERS) +jinja_tests = dump_functions(DEFAULT_TESTS) + + +def jinja_nodes(dirname, arguments, options, content, lineno, + content_offset, block_text, state, state_machine): + from jinja2.nodes import Node + doc = ViewList() + def walk(node, indent): + p = ' ' * indent + sig = ', '.join(node.fields) + doc.append(p + '.. autoclass:: %s(%s)' % (node.__name__, sig), '') + if node.abstract: + members = [] + for key, name in node.__dict__.items(): + if not key.startswith('_') and \ + not hasattr(node.__base__, key) and isinstance(name, collections.Callable): + members.append(key) + if members: + members.sort() + doc.append('%s :members: %s' % (p, ', '.join(members)), '') + if node.__base__ != object: + doc.append('', '') + doc.append('%s :Node type: :class:`%s`' % + (p, node.__base__.__name__), '') + doc.append('', '') + children = node.__subclasses__() + children.sort(key=lambda x: x.__name__.lower()) + for child in children: + walk(child, indent) + walk(Node, 0) + return parse_rst(state, content_offset, doc) + + +def inject_toc(app, doctree, docname): + titleiter = iter(doctree.traverse(nodes.title)) + try: + # skip first title, we are not interested in that one + next(titleiter) + title = next(titleiter) + # and check if there is at least another title + next(titleiter) + except StopIteration: + return + tocnode = nodes.section('') + tocnode['classes'].append('toc') + toctitle = nodes.section('') + toctitle['classes'].append('toctitle') + toctitle.append(nodes.title(text='Table Of Contents')) + tocnode.append(toctitle) + tocnode += doctree.document.settings.env.get_toc_for(docname)[0][1] + title.parent.insert(title.parent.children.index(title), tocnode) + + +def setup(app): + app.add_directive('jinjafilters', jinja_filters, 0, (0, 0, 0)) + app.add_directive('jinjatests', jinja_tests, 0, (0, 0, 0)) + app.add_directive('jinjanodes', jinja_nodes, 0, (0, 0, 0)) + # uncomment for inline toc. links are broken unfortunately + ##app.connect('doctree-resolved', inject_toc) diff --git a/deps/v8_inspector/deps/jinja2/docs/jinjastyle.sty b/deps/v8_inspector/deps/jinja2/docs/jinjastyle.sty new file mode 100644 index 00000000000000..e93c8d1c6974e9 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/jinjastyle.sty @@ -0,0 +1,119 @@ +\definecolor{TitleColor}{rgb}{0,0,0} +\definecolor{InnerLinkColor}{rgb}{0,0,0} +\definecolor{OuterLinkColor}{rgb}{0.8,0,0} + +\renewcommand{\maketitle}{% + \begin{titlepage}% + \let\footnotesize\small + \let\footnoterule\relax + \ifsphinxpdfoutput + \begingroup + % This \def is required to deal with multi-line authors; it + % changes \\ to ', ' (comma-space), making it pass muster for + % generating document info in the PDF file. + \def\\{, } + \pdfinfo{ + /Author (\@author) + /Title (\@title) + } + \endgroup + \fi + \begin{flushright}% + %\sphinxlogo% + {\center + \vspace*{3cm} + \includegraphics{logo.pdf} + \vspace{3cm} + \par + {\rm\Huge \@title \par}% + {\em\LARGE \py@release\releaseinfo \par} + {\large + \@date \par + \py@authoraddress \par + }}% + \end{flushright}%\par + \@thanks + \end{titlepage}% + \cleardoublepage% + \setcounter{footnote}{0}% + \let\thanks\relax\let\maketitle\relax + %\gdef\@thanks{}\gdef\@author{}\gdef\@title{} +} + +\fancypagestyle{normal}{ + \fancyhf{} + \fancyfoot[LE,RO]{{\thepage}} + \fancyfoot[LO]{{\nouppercase{\rightmark}}} + \fancyfoot[RE]{{\nouppercase{\leftmark}}} + \fancyhead[LE,RO]{{ \@title, \py@release}} + \renewcommand{\headrulewidth}{0.4pt} + \renewcommand{\footrulewidth}{0.4pt} +} + +\fancypagestyle{plain}{ + \fancyhf{} + \fancyfoot[LE,RO]{{\thepage}} + \renewcommand{\headrulewidth}{0pt} + \renewcommand{\footrulewidth}{0.4pt} +} + +\titleformat{\section}{\Large}% + {\py@TitleColor\thesection}{0.5em}{\py@TitleColor}{\py@NormalColor} +\titleformat{\subsection}{\large}% + {\py@TitleColor\thesubsection}{0.5em}{\py@TitleColor}{\py@NormalColor} +\titleformat{\subsubsection}{}% + {\py@TitleColor\thesubsubsection}{0.5em}{\py@TitleColor}{\py@NormalColor} +\titleformat{\paragraph}{\large}% + {\py@TitleColor}{0em}{\py@TitleColor}{\py@NormalColor} + +\ChNameVar{\raggedleft\normalsize} +\ChNumVar{\raggedleft \bfseries\Large} +\ChTitleVar{\raggedleft \rm\Huge} + +\renewcommand\thepart{\@Roman\c@part} +\renewcommand\part{% + \pagestyle{plain} + \if@noskipsec \leavevmode \fi + \cleardoublepage + \vspace*{6cm}% + \@afterindentfalse + \secdef\@part\@spart} + +\def\@part[#1]#2{% + \ifnum \c@secnumdepth >\m@ne + \refstepcounter{part}% + \addcontentsline{toc}{part}{\thepart\hspace{1em}#1}% + \else + \addcontentsline{toc}{part}{#1}% + \fi + {\parindent \z@ %\center + \interlinepenalty \@M + \normalfont + \ifnum \c@secnumdepth >\m@ne + \rm\Large \partname~\thepart + \par\nobreak + \fi + \MakeUppercase{\rm\Huge #2}% + \markboth{}{}\par}% + \nobreak + \vskip 8ex + \@afterheading} +\def\@spart#1{% + {\parindent \z@ %\center + \interlinepenalty \@M + \normalfont + \huge \bfseries #1\par}% + \nobreak + \vskip 3ex + \@afterheading} + +% use inconsolata font +\usepackage{inconsolata} + +% fix single quotes, for inconsolata. (does not work) +%%\usepackage{textcomp} +%%\begingroup +%% \catcode`'=\active +%% \g@addto@macro\@noligs{\let'\textsinglequote} +%% \endgroup +%%\endinput diff --git a/deps/v8_inspector/deps/jinja2/docs/latexindex.rst b/deps/v8_inspector/deps/jinja2/docs/latexindex.rst new file mode 100644 index 00000000000000..300e60d10f2a76 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/latexindex.rst @@ -0,0 +1,6 @@ +:orphan: + +Jinja2 Documentation +==================== + +.. include:: contents.rst.inc diff --git a/deps/v8_inspector/deps/jinja2/docs/logo.pdf b/deps/v8_inspector/deps/jinja2/docs/logo.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3e8a9cf480ccbe4a30911bdb7d78c0b80e767c81 GIT binary patch literal 5677 zcma)gc{r49`+t;e$QETcgp_T@%wS~6zVG`|8OtzXhB1>Z`&LMn)Py`p2-(S2*~h*l z43T6jvZY9<?~LkwpXYghfBf!aj{Cl^&vjnsd0wCMyzXO;kEp4pwiH+z3J|SbE87P^ zfFK~o`5Hh;2`FRehIYq!0HI{a8~_9YWwcRVI5#Z$>4m_#X}Y;!T-^XFDgZwm)(zne z2*?Vt?<-rZW1HTVs9|P(ut6NpJVr&`F-dF3rSH@}3{w&~zISM8m6kfF9OZP{uY^S_ zWgyg2{%hx6$lWQ=skcAB9Tu)N2_Ei@+Ax29yj%bL=hmH<?=~`?{ZxE;I2N;ZW_Kt| z?ftW`rshNM{i>LO`PskrW{ciz&8f!+^Px%t%U!$#9d7!T<L9>9+=i?az9@J2Fixj- zyx1kdT?hBX=<#<536b%Ij5Nwf!xTjyl6rg7XKgNrHz42kusDXz1CmSGrIXm!j3_Z) z73X`EEQUdjslimD9&Pe!Pn+)Cj&$5wdC?%>^Yj*cH8Y2(oc@_q7@T&)+I=vXCt@Z0 zc)>fcj()K5Ug!cOR0+{nzw8*?5NFt<OuVsVw7GM$&t*4YhN#?ef+eP<%}0?XJ>+yf z5#sl9qSQ}*A{>R8;4l!xr0%n^G_APwdCTYlHeCvbI9{HaMO682sCbn*`8}xr6uR)# zDN=G9fG`R5f_<v<SDMv+qd#jA6%k0vq?6=|K}8a`MZIVw_^Y0sufH?%CUf?Lt(wf| zjFFwBT8>igNUFev$cl2d9lny&XXiHpg6SO-AFyU8IvP!RJ{sF^zK=&c!V3%p%5Mc+ z&!35_-)!(H4fep7l`K_}H@;Rvc)8PjBfYnbkWi+-d|VRNYgMW1PY|($ON5*rcG2sz z%Pk(>zrMf1;mGKtRK_#ql0r50ku^~n-yo*Vk;V5&w|fejD5;YmG|Dp#jdMv*(M!$A zyoV4p@L6N~UXxa*tZhh;olo^n<r*qA5@08t*5JEDFQEs5bRX;DV{rJ$wWgx?LsdzI z(JL^~L85IYwLz5LnZ)~^+-b3FL7~Q<#ZYds&0M~xZ@%21F74>s4Qe%O;hh!Pg;+FT zB3GX)tcNI_k)_$4y2_Gx{2;qpDNBcmWdp=8NOfiiYTTckGV)>0XXKb*e2g^Y#^aRM zI7NP?b)lMu6%yl2xzMBfV<xvj^PEEV_dh3#zDp*?V8YOL*1-(ZsV!1EDb5+Qr%I{I zhf|Yn+c+nvn;lYKosO1wdden5ocoza-@1&tOsEj~b`~mtxM9je^gS-Bti(@@=8qBe zSr%<LECTD^^>1BUCAk~>+ZRk7uC0cj?|5@k{9RPvSY^k0-{K0vEB~Os&jKsc!u+F4 zLH-WSbaoQeP1f$%(J|k3D8u)lmA}^BlU~e*9#g%RaC&d9y@h4>YK~ceD~@GG?PY@N zfNIBIjUPs3)*ZYU9P|{l#No7+qOr$Fg68Ih!c-wqqGw_PFHQN-R|yq)Uy@?>v?<Ny z;U;oR&(k1)@GtNy)Zf&5BDsCJn@gB3aK_0Br^?i2<GENjD^@B<u^N)5)TKqsrCB<h zwE<Fe?ss5~_bRe-3B8LWB6+gHX9b3J?N15|aPu}dw!Dj?F38H_wPNRKknG>;hI-Gv zi+Xj+sZ;Ha(A!~xi4+~7q(1hw`(PAZeW2vT=r=I0tsugjSzSLe%p{<n5XO)tE63!$ zE(gqEX4N*l8ut-EM<0d;oSKsSbf!4Zn=OgjCR)tX>{~b*$DzR1wHSUj!a65t>AX7f ztU>^^4xomK$v2@hwi+$=O^tvDwdNcPbfgYY97qKvXL5G(4bUF<9sQ8Bmd2UNhCs&3 zE9l6Zk7<S%gqH$B?{?=!>Yf76JDfUhPj3h7m17z$`QS=Oypp-a&c^IyFCy7A>C>G3 zg^qgT%<P6OQ1ZF#L}8I@#9qx$hMB_`=OZMbTl+;f05(o+n~m_KLB&Om1qMM$!GaTJ zY8!p_^a6>Jr#8ciZ~G7mH}R-;aTP(3iOFl3B#BLw%-M&@Q(RMB-}!szeul#ytP`le z4Qe~w4XqExuChH=Nl6zs4pif^d-hR=>e-V*hY{bjD4)<*T+(e`^2}!q+e<|QQzpA} z9BX(52BRIBukc;sE2lEc?j?rY!xAo0Vb)%8fxWvA&E@X58-<vwY%@347sjp4kp!0U z`YlXLx|lhe4khG*J#v{wZP_lK?^#a9q2CJ;;Za_VlhumUg8h}5>Vyu24qQLnwCo|c zUpVeWfK>^;RpYUsMTNt0>;xR4GFCL1Y9_6(L=+WQqgG!+G}jb==+~CVZ1)pXUlV@_ zz0*r=fjpodG`}<s&uu?A*sB-8y+n^9va|Z@AXzFsd&O*YlF-&0W<nMXuELXC+&l8s z7yQFpSt2?$3?rG?;%V&qb84RVX5G4->7(X(j5)d>6PZU9H{!e3CtTqtaEU-CCl!q= zAM11S3MQWFYoTXr47l(mRDDWtK;wKiyP16X4RPCz`Gz^e1cT)C3NP4|>558pAnfYA zIPQwYx&8)jy{?r`RH;VmqA8Kl!hJw|ki$}f!9Hzb%X?r+U0jFN+SQkXX*{S$mPIHl z#bD=SEa(Hz@&X6Y8z!snYB9vbY$3YmRIF3wov%N&N0Q2Q$1F(SG!>p3&7n>^>xVV1 z_v@f>O{}S2<#Tw|+#S^0I`E!*{Mu8VD3XV?H*;Z&+dMpouP<t@fb>;y>DwEhs~jQm z%Be4v+P<guS1@<Slvfy+0oMKFzCM}ZoXCx1`^-O)ThLX5@5Fo1#$gW=1GS&GDf*{< zhm^|C2S}72PmL<Ac=3*N923!|2waVnD0K-Hc*{Rt$usp4u&QLq*B3uUVtRhSHeTsa zxev>cD1J(gQgy(*db{>oL)&-DHO}#?>0w23HB0xkzKYX|speK!YbO-9I5A(Kzr{Pv zPUs41Edu)Lwg9{V3K2Gz>qpd}CsHTWKelj6rn_l~EDLTpsEkCiRAYN_<#rZZ($@ zHLR%B#wQ^%6?cWp68^gMEeC2V(rXLnn=C4Rtz;7g<FpI43uJImw-kC>)j8~&U?Urz zS+SNnDOd3@NVLYb^x)3>!O6xu2B*Snq8a75Up+k^y#=_fV>-tinTriP@$vRWC-tJd zsGQL+plE;Xn30;-=`$p|-b3TCB-rU0*OMRWiY1NnuL)=m_6+C+je%01BftJI{f2i9 zHX~Z9RobArtiCt<5lxdG*gAN6KW6smT3M6u*JKY|!uIjkwB=ZzD=Cgi)D~cbysMZr zSisY}rbipLcLEqPGinWm;4G0fSGCbqtBf{UP4faE&7I{wC!<8Q@^yr}wLU9x&Mb^% zozQzRV@o#PIi!)XO<`=Cp*?8E;$4xhl&U@BDPp%ka6@9b$eW(wTNOEyS`VXWCiR>| z;L;2R2BgA_vu0m)ZfjViy*2SLFpHZ#kwojxKiS}e#;uQDSeJR@A4dLTrTSxgzx>S> zet-R0Rfuxv^KyxbGXk^wg5uAXb1<)`Hg&3YoU~pOcjB!|yuml^PH$h-B(*{xJauQS zrl)qu%-<N{Q?y?OA}hTv=zNqGYhWmL)rRln%(4b&UKc0COPQZmta9maGDjkU7|OY0 z0lAWx_!aE1f9X@MbVAIeyBd?gq`I&&f%|N6&l!%Xp87WzuiMfzNrj)Z0`}65DaA6s zNJ~LEk3-cOlHzvbD`WtKsrU=I7O3zmo`&DM#*y&!T764Qdr)1Eg5se)e;==ZE%C~h zBm9Y^2Xj*7_1^HdwuPJ*k18!5IwV(yo#KDhqr00hZOOh=R-oxgr35~w6VBWCLjS#U z_>SM_Yb6clNDO=A$F6*upln>OEho@0kM3$#9PQ&S-bRlZzg3ZJFZ`20L8R}oev!`A zq6gL}YS;UUpG`UA?D%rZ*bLNvFvnjr$5p8Ry!BC_Gj;h%HuQjlpf)rUDI6i-p?ha~ zs3Ivdi!R`SWP?&MKW!@`WN_p<RIp@Xi0cP;-}4v93BidkPO8^Ww*9a>jUJEq<Xk_K zpq%x^fEMQDfKKe?5b<n$mM3w$So)sPCF&DryvGkzKhh<J%#?3yHWR5T{U({({PcE& zPtJ~r`MJ*DbluFK8KwbUeie!Gusi8n@YCY@iB2W(+3@)j&NIz~h>+=1$VX~RYC++L zQ|FK>G#|x0ec3y5knApTpqZCU_!^iH;}7|1QrM^uEHu15h-%ew=Sl5ime#(VSf6@C z$!BJV%jfKL-w&C&M)Q~pX2KqEv^iVtc;5c1$s-o*0$`I{?VDlUs3GlDb{0tcH}&H` zN2jLuK1S1eJISo-7c9#@Iqc!;nsP$gAzsO^cMVQ_FxiTiIx}b_5}Q);K|3+}+~jrc z#z}+Z<HEOOD=KhF?I3^PWA25^_sfKE%?oJ+=5;rs(f980E0vjAZ2=PH{pNDweh&c` zd!(fs)-1g86OdDLtdM;k8V)g3MGpS+@K~NiC@s2+M*3O#XldSFqRb<%d%M9_jOV_p zf7kB!R;CFOwD6R~fG$M-m1Sd-e&5#?-|p=e!(t9wU%ut}{Ly(3oUW3DTz`MC;8cNh z$0TnJ&BhgrTfeCLt1MP*zcF`uyV~i>wq4JGUW<7J=|-XjOSiPI&F%bFL!Ra@Pp;M8 zEp--*%%c)5c`)A9Y*jd;UM~K&Q6Og&wNrd>)ysLJDr1Gc;xs1Y$G|1dC8uF<wk8W? z)T*gty$fR!($KQNNM)pZoJ+5skbDx7R+(xEJciVK_uOicku8!-{ULh#SWy5|S?}gp z+jq{mOxTw#*^7Hfv*K0xPZ|DxOz#^jG2?GrT#X6`CFoX!j7naQaU-mf&hjZ*J_Z)Y z=_Qi{hF+c6nCQJBpAdfzDXl?YYr?%WMW~i!s;wbB4>?)olX;bk6l8tkVRsqI_%YAd z<E#DL*U~;@C%q`#%3u7nR6e=jl18;j8?#2VGth6L=hT!nsl^mKN4nj3d#mL`{9jwV z#L{p+r22eA+Iu4#)j+9SVy05Kcmj}#Tj^B7&*<v02JM-ZVYiEwUiE>OEjT1T8IP^1 zMXRY+<I`)Jif^u&3RNDbo<QWO+JQ%~1Z!xH{imUwz^I2}XVJ+c^bk3Ph4^73<twj8 z3sH{E7s^K4KPWcSUY5UEP8yiOmgVsB$kl|C4B!hwdM{s@7GE}IL;UoDoVdN1FkLxK z1Ic_@xn_qObGNY`;}P)EIe*W_b2DEHdbyTGjwHl>E40)0iTHKaA+IZoLYaGwD4LLW zsATuidl@Qc5E2Zz?`ZgH1TW{-Ek5wd7h@l|+aKEWmXRGTRN+3er89rBt=RUy|MjM< z5u-x2{i$M;s84zmO-@0s$Q`Bq9j%au+ZJ=z(W+?)MTcI`@Pc2UKklZ&Z&Wfe*S$4= zK*(@ys<wSKs-xAT5}lIMt;v;`PJ{QcTBq*_y}SbGJjbuM%Nt^*0*Flt`O$xM)sh*( zRyWe2AXV1+c5zK2tAj<9X-LdPVvojQv7?VJTKz2w?=Y=#%C>)~@gA2sEe52jt6blw z=V%PO)>vbf&tDr-Wt}CaTC+jS5UvNe2-Dc6_UMJ>gU_EtW{9S9vF2U|l}?q}JK#gm zT8_BOPCKo%#aZ1dChyfBDZQ47eY#pI>>O4m40(D-^~$cqwyrjH9PUS}p#nFWF%1Xa z9&@I$64d7G(o1;em9Uu>QKu3V^zaVmW<hn{My!f8z83>=6|OF-7vF93wg;6@6P~RS zW`i_6sjEM5SL-$}FEes$hFo>{MiN8zW;}UtrZK#B`63L`=^!@<u)M>xBP%!yWaNF* z?6O}^_)I|DtCy(n8xGJ<3~UcLkAeg%L+%s8?+2iG23Eh!2F>k;O@;a#?5f_fb$knO zL%aT#GstfgA%voSP@ZJ{11bxLPz)$T+5hMyN6Ls+08YmOM^;6EN=g)fe90g{<N;7d zLk$Q9%D5oNcd}MOp7?c_F-73WVhZ}m#s>z3QB)Sn9QpS<hTMN*0A(x#ecXWLHSTUl z@k|j|H#Cmy@{crQ<mQS(sAB?v4rB-fl!bzTa5>=RBV!E=8b`MF15(7DUrLS+7K8Ws zTdu#{AEAHJw;%__BG7(5lu#~#e_#zVuIY9i<>F?pqfP;TZ^zut4}-_LxcLDo)R_Nm z8}j7wA4y3Yk9NVKFzBOrvPaozV!RP3^ikg6KM8{<Lum*U43;O8@INoVw*@}30xLiv zU~-kD|NZjI$`}a#TP_rq|K#$A(xWmQrSiKZe@o5?fy1JXNR?I~F9iXmK+^IsIgkQ{ zN;pIsA`1h7WPhh^;el{Pq1}HoMXCL7<j5i9H%@5{4U88CYvF@%A=mUDWRfR;&10~x ze&p@Rfu&{PatiV=pd3gV4hDf?l&1K9p-`ywk=@a*f4lj^Hjq++qr(55Ah#>I$H@M1 zSiIY>+n;s+=s-$;{hKa#H@_pY3{bx^q`3Z5Hw>QAlz-!!LfRh(8f29ahrt5HT@WZN z1_+i`kOqT*;vP7hkD?6ufRI6VEW*bF<>Due!MdM6|Enfgj4R%StUCVxFZ=Hag8wc2 zuX1Z3a0oBVQH_pHli%+%^xriBST`g91O$Tsp#NMzIT#EA10sRHF&G$1c1^i}=sy?~ z3L?||PYgsZ*uOC-M1kC`|FnZDK*@Fe7Y6%Ze^7bQ|JcD~Dc%3iwJ`G0`7egUB2Zp# z*dtBX0u|&&*&C(H%rO`okV4U~Z-O2gi2+gyc650Ca0o2!hzl5m{Mi!~)iTir{2%%h Bpu7M8 literal 0 HcmV?d00001 diff --git a/deps/v8_inspector/deps/jinja2/docs/sandbox.rst b/deps/v8_inspector/deps/jinja2/docs/sandbox.rst new file mode 100644 index 00000000000000..71ccc0d6deff3b --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/sandbox.rst @@ -0,0 +1,94 @@ +Sandbox +======= + +The Jinja2 sandbox can be used to evaluate untrusted code. Access to unsafe +attributes and methods is prohibited. + +Assuming `env` is a :class:`SandboxedEnvironment` in the default configuration +the following piece of code shows how it works: + +>>> env.from_string("{{ func.func_code }}").render(func=lambda:None) +u'' +>>> env.from_string("{{ func.func_code.do_something }}").render(func=lambda:None) +Traceback (most recent call last): + ... +SecurityError: access to attribute 'func_code' of 'function' object is unsafe. + +API +--- + +.. module:: jinja2.sandbox + +.. autoclass:: SandboxedEnvironment([options]) + :members: is_safe_attribute, is_safe_callable, default_binop_table, + default_unop_table, intercepted_binops, intercepted_unops, + call_binop, call_unop + +.. autoclass:: ImmutableSandboxedEnvironment([options]) + +.. autoexception:: SecurityError + +.. autofunction:: unsafe + +.. autofunction:: is_internal_attribute + +.. autofunction:: modifies_known_mutable + +.. admonition:: Note + + The Jinja2 sandbox alone is no solution for perfect security. Especially + for web applications you have to keep in mind that users may create + templates with arbitrary HTML in so it's crucial to ensure that (if you + are running multiple users on the same server) they can't harm each other + via JavaScript insertions and much more. + + Also the sandbox is only as good as the configuration. We strongly + recommend only passing non-shared resources to the template and use + some sort of whitelisting for attributes. + + Also keep in mind that templates may raise runtime or compile time errors, + so make sure to catch them. + +Operator Intercepting +--------------------- + +.. versionadded:: 2.6 + +For maximum performance Jinja2 will let operators call directly the type +specific callback methods. This means that it's not possible to have this +intercepted by overriding :meth:`Environment.call`. Furthermore a +conversion from operator to special method is not always directly possible +due to how operators work. For instance for divisions more than one +special method exist. + +With Jinja 2.6 there is now support for explicit operator intercepting. +This can be used to customize specific operators as necessary. In order +to intercept an operator one has to override the +:attr:`SandboxedEnvironment.intercepted_binops` attribute. Once the +operator that needs to be intercepted is added to that set Jinja2 will +generate bytecode that calls the :meth:`SandboxedEnvironment.call_binop` +function. For unary operators the `unary` attributes and methods have to +be used instead. + +The default implementation of :attr:`SandboxedEnvironment.call_binop` +will use the :attr:`SandboxedEnvironment.binop_table` to translate +operator symbols into callbacks performing the default operator behavior. + +This example shows how the power (``**``) operator can be disabled in +Jinja2:: + + from jinja2.sandbox import SandboxedEnvironment + + + class MyEnvironment(SandboxedEnvironment): + intercepted_binops = frozenset(['**']) + + def call_binop(self, context, operator, left, right): + if operator == '**': + return self.undefined('the power operator is unavailable') + return SandboxedEnvironment.call_binop(self, context, + operator, left, right) + +Make sure to always call into the super method, even if you are not +intercepting the call. Jinja2 might internally call the method to +evaluate expressions. diff --git a/deps/v8_inspector/deps/jinja2/docs/switching.rst b/deps/v8_inspector/deps/jinja2/docs/switching.rst new file mode 100644 index 00000000000000..01a7d0d787bec2 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/switching.rst @@ -0,0 +1,226 @@ +Switching from other Template Engines +===================================== + +.. highlight:: html+jinja + +If you have used a different template engine in the past and want to switch +to Jinja2 here is a small guide that shows the basic syntactic and semantic +changes between some common, similar text template engines for Python. + +Jinja1 +------ + +Jinja2 is mostly compatible with Jinja1 in terms of API usage and template +syntax. The differences between Jinja1 and 2 are explained in the following +list. + +API +~~~ + +Loaders + Jinja2 uses a different loader API. Because the internal representation + of templates changed there is no longer support for external caching + systems such as memcached. The memory consumed by templates is comparable + with regular Python modules now and external caching doesn't give any + advantage. If you have used a custom loader in the past have a look at + the new :ref:`loader API <loaders>`. + +Loading templates from strings + In the past it was possible to generate templates from a string with the + default environment configuration by using `jinja.from_string`. Jinja2 + provides a :class:`Template` class that can be used to do the same, but + with optional additional configuration. + +Automatic unicode conversion + Jinja1 performed automatic conversion of bytestrings in a given encoding + into unicode objects. This conversion is no longer implemented as it + was inconsistent as most libraries are using the regular Python ASCII + bytestring to Unicode conversion. An application powered by Jinja2 + *has to* use unicode internally everywhere or make sure that Jinja2 only + gets unicode strings passed. + +i18n + Jinja1 used custom translators for internationalization. i18n is now + available as Jinja2 extension and uses a simpler, more gettext friendly + interface and has support for babel. For more details see + :ref:`i18n-extension`. + +Internal methods + Jinja1 exposed a few internal methods on the environment object such + as `call_function`, `get_attribute` and others. While they were marked + as being an internal method it was possible to override them. Jinja2 + doesn't have equivalent methods. + +Sandbox + Jinja1 was running sandbox mode by default. Few applications actually + used that feature so it became optional in Jinja2. For more details + about the sandboxed execution see :class:`SandboxedEnvironment`. + +Context + Jinja1 had a stacked context as storage for variables passed to the + environment. In Jinja2 a similar object exists but it doesn't allow + modifications nor is it a singleton. As inheritance is dynamic now + multiple context objects may exist during template evaluation. + +Filters and Tests + Filters and tests are regular functions now. It's no longer necessary + and allowed to use factory functions. + + +Templates +~~~~~~~~~ + +Jinja2 has mostly the same syntax as Jinja1. What's different is that +macros require parentheses around the argument list now. + +Additionally Jinja2 allows dynamic inheritance now and dynamic includes. +The old helper function `rendertemplate` is gone now, `include` can be used +instead. Includes no longer import macros and variable assignments, for +that the new `import` tag is used. This concept is explained in the +:ref:`import` documentation. + +Another small change happened in the `for`-tag. The special loop variable +doesn't have a `parent` attribute, instead you have to alias the loop +yourself. See :ref:`accessing-the-parent-loop` for more details. + + +Django +------ + +If you have previously worked with Django templates, you should find +Jinja2 very familiar. In fact, most of the syntax elements look and +work the same. + +However, Jinja2 provides some more syntax elements covered in the +documentation and some work a bit different. + +This section covers the template changes. As the API is fundamentally +different we won't cover it here. + +Method Calls +~~~~~~~~~~~~ + +In Django method calls work implicitly, while Jinja requires the explicit +Python syntax. Thus this Django code:: + + {% for page in user.get_created_pages %} + ... + {% endfor %} + +...looks like this in Jinja:: + + {% for page in user.get_created_pages() %} + ... + {% endfor %} + +This allows you to pass variables to the method, which is not possible in +Django. This syntax is also used for macros. + +Filter Arguments +~~~~~~~~~~~~~~~~ + +Jinja2 provides more than one argument for filters. Also the syntax for +argument passing is different. A template that looks like this in Django:: + + {{ items|join:", " }} + +looks like this in Jinja2:: + + {{ items|join(', ') }} + +It is a bit more verbose, but it allows different types of arguments - +including variables - and more than one of them. + +Tests +~~~~~ + +In addition to filters there also are tests you can perform using the is +operator. Here are some examples:: + + {% if user.user_id is odd %} + {{ user.username|e }} is odd + {% else %} + hmm. {{ user.username|e }} looks pretty normal + {% endif %} + +Loops +~~~~~ + +For loops work very similarly to Django, but notably the Jinja2 special +variable for the loop context is called `loop`, not `forloop` as in Django. + +In addition, the Django `empty` argument is called `else` in Jinja2. For +example, the Django template:: + + {% for item in items %} + {{ item }} + {% empty %} + No items! + {% endfor %} + +...looks like this in Jinja2:: + + {% for item in items %} + {{ item }} + {% else %} + No items! + {% endfor %} + +Cycle +~~~~~ + +The ``{% cycle %}`` tag does not exist in Jinja2; however, you can achieve the +same output by using the `cycle` method on the loop context special variable. + +The following Django template:: + + {% for user in users %} + <li class="{% cycle 'odd' 'even' %}">{{ user }}</li> + {% endfor %} + +...looks like this in Jinja2:: + + {% for user in users %} + <li class="{{ loop.cycle('odd', 'even') }}">{{ user }}</li> + {% endfor %} + +There is no equivalent of ``{% cycle ... as variable %}``. + + +Mako +---- + +.. highlight:: html+mako + +If you have used Mako so far and want to switch to Jinja2 you can configure +Jinja2 to look more like Mako: + +.. sourcecode:: python + + env = Environment('<%', '%>', '${', '}', '<%doc>', '</%doc>', '%', '##') + +With an environment configured like that, Jinja2 should be able to interpret +a small subset of Mako templates. Jinja2 does not support embedded Python +code, so you would have to move that out of the template. The syntax for defs +(which are called macros in Jinja2) and template inheritance is different too. +The following Mako template:: + + <%inherit file="layout.html" /> + <%def name="title()">Page Title</%def> + <ul> + % for item in list: + <li>${item}</li> + % endfor + </ul> + +Looks like this in Jinja2 with the above configuration:: + + <% extends "layout.html" %> + <% block title %>Page Title<% endblock %> + <% block body %> + <ul> + % for item in list: + <li>${item}</li> + % endfor + </ul> + <% endblock %> diff --git a/deps/v8_inspector/deps/jinja2/docs/templates.rst b/deps/v8_inspector/deps/jinja2/docs/templates.rst new file mode 100644 index 00000000000000..3c169247219908 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/templates.rst @@ -0,0 +1,1509 @@ +Template Designer Documentation +=============================== + +.. highlight:: html+jinja + +This document describes the syntax and semantics of the template engine and +will be most useful as reference to those creating Jinja templates. As the +template engine is very flexible, the configuration from the application can +be slightly different from the code presented here in terms of delimiters and +behavior of undefined values. + + +Synopsis +-------- + +A Jinja template is simply a text file. Jinja can generate any text-based +format (HTML, XML, CSV, LaTeX, etc.). A Jinja template doesn't need to have a +specific extension: ``.html``, ``.xml``, or any other extension is just fine. + +A template contains **variables** and/or **expressions**, which get replaced +with values when a template is *rendered*; and **tags**, which control the +logic of the template. The template syntax is heavily inspired by Django and +Python. + +Below is a minimal template that illustrates a few basics using the default +Jinja configuration. We will cover the details later in this document:: + + <!DOCTYPE html> + <html lang="en"> + <head> + <title>My Webpage</title> + </head> + <body> + <ul id="navigation"> + {% for item in navigation %} + <li><a href="{{ item.href }}">{{ item.caption }}</a></li> + {% endfor %} + </ul> + + <h1>My Webpage</h1> + {{ a_variable }} + + {# a comment #} + </body> + </html> + +The following example shows the default configuration settings. An application +developer can change the syntax configuration from ``{% foo %}`` to ``<% foo +%>``, or something similar. + +There are a few kinds of delimiters. The default Jinja delimiters are +configured as follows: + +* ``{% ... %}`` for :ref:`Statements <list-of-control-structures>` +* ``{{ ... }}`` for :ref:`Expressions` to print to the template output +* ``{# ... #}`` for :ref:`Comments` not included in the template output +* ``# ... ##`` for :ref:`Line Statements <line-statements>` + + +.. _variables: + +Variables +--------- + +Template variables are defined by the context dictionary passed to the +template. + +You can mess around with the variables in templates provided they are passed in +by the application. Variables may have attributes or elements on them you can +access too. What attributes a variable has depends heavily on the application +providing that variable. + +You can use a dot (``.``) to access attributes of a variable in addition +to the standard Python ``__getitem__`` "subscript" syntax (``[]``). + +The following lines do the same thing:: + + {{ foo.bar }} + {{ foo['bar'] }} + +It's important to know that the outer double-curly braces are *not* part of the +variable, but the print statement. If you access variables inside tags don't +put the braces around them. + +If a variable or attribute does not exist, you will get back an undefined +value. What you can do with that kind of value depends on the application +configuration: the default behavior is to evaluate to an empty string if +printed or iterated over, and to fail for every other operation. + +.. _notes-on-subscriptions: + +.. admonition:: Implementation + + For the sake of convenience, ``foo.bar`` in Jinja2 does the following + things on the Python layer: + + - check for an attribute called `bar` on `foo` + (``getattr(foo, 'bar')``) + - if there is not, check for an item ``'bar'`` in `foo` + (``foo.__getitem__('bar')``) + - if there is not, return an undefined object. + + ``foo['bar']`` works mostly the same with a small difference in sequence: + + - check for an item ``'bar'`` in `foo`. + (``foo.__getitem__('bar')``) + - if there is not, check for an attribute called `bar` on `foo`. + (``getattr(foo, 'bar')``) + - if there is not, return an undefined object. + + This is important if an object has an item and attribute with the same + name. Additionally, the :func:`attr` filter only looks up attributes. + +.. _filters: + +Filters +------- + +Variables can be modified by **filters**. Filters are separated from the +variable by a pipe symbol (``|``) and may have optional arguments in +parentheses. Multiple filters can be chained. The output of one filter is +applied to the next. + +For example, ``{{ name|striptags|title }}`` will remove all HTML Tags from +variable `name` and title-case the output (``title(striptags(name))``). + +Filters that accept arguments have parentheses around the arguments, just like +a function call. For example: ``{{ listx|join(', ') }}`` will join a list with +commas (``str.join(', ', listx)``). + +The :ref:`builtin-filters` below describes all the builtin filters. + +.. _tests: + +Tests +----- + +Beside filters, there are also so-called "tests" available. Tests can be used +to test a variable against a common expression. To test a variable or +expression, you add `is` plus the name of the test after the variable. For +example, to find out if a variable is defined, you can do ``name is defined``, +which will then return true or false depending on whether `name` is defined +in the current template context. + +Tests can accept arguments, too. If the test only takes one argument, you can +leave out the parentheses. For example, the following two +expressions do the same thing:: + + {% if loop.index is divisibleby 3 %} + {% if loop.index is divisibleby(3) %} + +The :ref:`builtin-tests` below describes all the builtin tests. + + +.. _comments: + +Comments +-------- + +To comment-out part of a line in a template, use the comment syntax which is +by default set to ``{# ... #}``. This is useful to comment out parts of the +template for debugging or to add information for other template designers or +yourself:: + + {# note: commented-out template because we no longer use this + {% for user in users %} + ... + {% endfor %} + #} + + +Whitespace Control +------------------ + +In the default configuration: + +* a single trailing newline is stripped if present +* other whitespace (spaces, tabs, newlines etc.) is returned unchanged + +If an application configures Jinja to `trim_blocks`, the first newline after a +template tag is removed automatically (like in PHP). The `lstrip_blocks` +option can also be set to strip tabs and spaces from the beginning of a +line to the start of a block. (Nothing will be stripped if there are +other characters before the start of the block.) + +With both `trim_blocks` and `lstrip_blocks` enabled, you can put block tags +on their own lines, and the entire block line will be removed when +rendered, preserving the whitespace of the contents. For example, +without the `trim_blocks` and `lstrip_blocks` options, this template:: + + <div> + {% if True %} + yay + {% endif %} + </div> + +gets rendered with blank lines inside the div:: + + <div> + + yay + + </div> + +But with both `trim_blocks` and `lstrip_blocks` enabled, the template block +lines are removed and other whitespace is preserved:: + + <div> + yay + </div> + +You can manually disable the `lstrip_blocks` behavior by putting a +plus sign (``+``) at the start of a block:: + + <div> + {%+ if something %}yay{% endif %} + </div> + +You can also strip whitespace in templates by hand. If you add a minus +sign (``-``) to the start or end of a block (e.g. a :ref:`for-loop` tag), a +comment, or a variable expression, the whitespaces before or after +that block will be removed:: + + {% for item in seq -%} + {{ item }} + {%- endfor %} + +This will yield all elements without whitespace between them. If `seq` was +a list of numbers from ``1`` to ``9``, the output would be ``123456789``. + +If :ref:`line-statements` are enabled, they strip leading whitespace +automatically up to the beginning of the line. + +By default, Jinja2 also removes trailing newlines. To keep single +trailing newlines, configure Jinja to `keep_trailing_newline`. + +.. admonition:: Note + + You must not add whitespace between the tag and the minus sign. + + **valid**:: + + {%- if foo -%}...{% endif %} + + **invalid**:: + + {% - if foo - %}...{% endif %} + + +Escaping +-------- + +It is sometimes desirable -- even necessary -- to have Jinja ignore parts +it would otherwise handle as variables or blocks. For example, if, with +the default syntax, you want to use ``{{`` as a raw string in a template and +not start a variable, you have to use a trick. + +The easiest way to output a literal variable delimiter (``{{``) is by using a +variable expression:: + + {{ '{{' }} + +For bigger sections, it makes sense to mark a block `raw`. For example, to +include example Jinja syntax in a template, you can use this snippet:: + + {% raw %} + <ul> + {% for item in seq %} + <li>{{ item }}</li> + {% endfor %} + </ul> + {% endraw %} + + +.. _line-statements: + +Line Statements +--------------- + +If line statements are enabled by the application, it's possible to mark a +line as a statement. For example, if the line statement prefix is configured +to ``#``, the following two examples are equivalent:: + + <ul> + # for item in seq + <li>{{ item }}</li> + # endfor + </ul> + + <ul> + {% for item in seq %} + <li>{{ item }}</li> + {% endfor %} + </ul> + +The line statement prefix can appear anywhere on the line as long as no text +precedes it. For better readability, statements that start a block (such as +`for`, `if`, `elif` etc.) may end with a colon:: + + # for item in seq: + ... + # endfor + + +.. admonition:: Note + + Line statements can span multiple lines if there are open parentheses, + braces or brackets:: + + <ul> + # for href, caption in [('index.html', 'Index'), + ('about.html', 'About')]: + <li><a href="{{ href }}">{{ caption }}</a></li> + # endfor + </ul> + +Since Jinja 2.2, line-based comments are available as well. For example, if +the line-comment prefix is configured to be ``##``, everything from ``##`` to +the end of the line is ignored (excluding the newline sign):: + + # for item in seq: + <li>{{ item }}</li> ## this comment is ignored + # endfor + + +.. _template-inheritance: + +Template Inheritance +-------------------- + +The most powerful part of Jinja is template inheritance. Template inheritance +allows you to build a base "skeleton" template that contains all the common +elements of your site and defines **blocks** that child templates can override. + +Sounds complicated but is very basic. It's easiest to understand it by starting +with an example. + + +Base Template +~~~~~~~~~~~~~ + +This template, which we'll call ``base.html``, defines a simple HTML skeleton +document that you might use for a simple two-column page. It's the job of +"child" templates to fill the empty blocks with content:: + + <!DOCTYPE html> + <html lang="en"> + <head> + {% block head %} + <link rel="stylesheet" href="style.css" /> + <title>{% block title %}{% endblock %} - My Webpage</title> + {% endblock %} + </head> + <body> + <div id="content">{% block content %}{% endblock %}</div> + <div id="footer"> + {% block footer %} + © Copyright 2008 by <a href="http://domain.invalid/">you</a>. + {% endblock %} + </div> + </body> + </html> + +In this example, the ``{% block %}`` tags define four blocks that child templates +can fill in. All the `block` tag does is tell the template engine that a +child template may override those placeholders in the template. + +Child Template +~~~~~~~~~~~~~~ + +A child template might look like this:: + + {% extends "base.html" %} + {% block title %}Index{% endblock %} + {% block head %} + {{ super() }} + <style type="text/css"> + .important { color: #336699; } + </style> + {% endblock %} + {% block content %} + <h1>Index</h1> + <p class="important"> + Welcome to my awesome homepage. + </p> + {% endblock %} + +The ``{% extends %}`` tag is the key here. It tells the template engine that +this template "extends" another template. When the template system evaluates +this template, it first locates the parent. The extends tag should be the +first tag in the template. Everything before it is printed out normally and +may cause confusion. For details about this behavior and how to take +advantage of it, see :ref:`null-master-fallback`. + +The filename of the template depends on the template loader. For example, the +:class:`FileSystemLoader` allows you to access other templates by giving the +filename. You can access templates in subdirectories with a slash:: + + {% extends "layout/default.html" %} + +But this behavior can depend on the application embedding Jinja. Note that +since the child template doesn't define the ``footer`` block, the value from +the parent template is used instead. + +You can't define multiple ``{% block %}`` tags with the same name in the +same template. This limitation exists because a block tag works in "both" +directions. That is, a block tag doesn't just provide a placeholder to fill +- it also defines the content that fills the placeholder in the *parent*. +If there were two similarly-named ``{% block %}`` tags in a template, +that template's parent wouldn't know which one of the blocks' content to use. + +If you want to print a block multiple times, you can, however, use the special +`self` variable and call the block with that name:: + + <title>{% block title %}{% endblock %}</title> + <h1>{{ self.title() }}</h1> + {% block body %}{% endblock %} + + +Super Blocks +~~~~~~~~~~~~ + +It's possible to render the contents of the parent block by calling `super`. +This gives back the results of the parent block:: + + {% block sidebar %} + <h3>Table Of Contents</h3> + ... + {{ super() }} + {% endblock %} + + +Named Block End-Tags +~~~~~~~~~~~~~~~~~~~~ + +Jinja2 allows you to put the name of the block after the end tag for better +readability:: + + {% block sidebar %} + {% block inner_sidebar %} + ... + {% endblock inner_sidebar %} + {% endblock sidebar %} + +However, the name after the `endblock` word must match the block name. + + +Block Nesting and Scope +~~~~~~~~~~~~~~~~~~~~~~~ + +Blocks can be nested for more complex layouts. However, per default blocks +may not access variables from outer scopes:: + + {% for item in seq %} + <li>{% block loop_item %}{{ item }}{% endblock %}</li> + {% endfor %} + +This example would output empty ``<li>`` items because `item` is unavailable +inside the block. The reason for this is that if the block is replaced by +a child template, a variable would appear that was not defined in the block or +passed to the context. + +Starting with Jinja 2.2, you can explicitly specify that variables are +available in a block by setting the block to "scoped" by adding the `scoped` +modifier to a block declaration:: + + {% for item in seq %} + <li>{% block loop_item scoped %}{{ item }}{% endblock %}</li> + {% endfor %} + +When overriding a block, the `scoped` modifier does not have to be provided. + + +Template Objects +~~~~~~~~~~~~~~~~ + +.. versionchanged:: 2.4 + +If a template object was passed in the template context, you can +extend from that object as well. Assuming the calling code passes +a layout template as `layout_template` to the environment, this +code works:: + + {% extends layout_template %} + +Previously, the `layout_template` variable had to be a string with +the layout template's filename for this to work. + + +HTML Escaping +------------- + +When generating HTML from templates, there's always a risk that a variable will +include characters that affect the resulting HTML. There are two approaches: + +a. manually escaping each variable; or +b. automatically escaping everything by default. + +Jinja supports both. What is used depends on the application configuration. +The default configuration is no automatic escaping; for various reasons: + +- Escaping everything except for safe values will also mean that Jinja is + escaping variables known to not include HTML (e.g. numbers, booleans) + which can be a huge performance hit. + +- The information about the safety of a variable is very fragile. It could + happen that by coercing safe and unsafe values, the return value is + double-escaped HTML. + +Working with Manual Escaping +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If manual escaping is enabled, it's **your** responsibility to escape +variables if needed. What to escape? If you have a variable that *may* +include any of the following chars (``>``, ``<``, ``&``, or ``"``) you +**SHOULD** escape it unless the variable contains well-formed and trusted +HTML. Escaping works by piping the variable through the ``|e`` filter:: + + {{ user.username|e }} + +Working with Automatic Escaping +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When automatic escaping is enabled, everything is escaped by default except +for values explicitly marked as safe. Variables and expressions +can be marked as safe either in: + +a. the context dictionary by the application with `MarkupSafe.Markup`, or +b. the template, with the `|safe` filter + +The main problem with this approach is that Python itself doesn't have the +concept of tainted values; so whether a value is safe or unsafe can get lost. + +If a value is not marked safe, auto-escaping will take place; which means that +you could end up with double-escaped contents. Double-escaping is easy to +avoid, however: just rely on the tools Jinja2 provides and *don't use builtin +Python constructs such as str.format or the string modulo operator (%)*. + +Jinja2 functions (macros, `super`, `self.BLOCKNAME`) always return template +data that is marked as safe. + +String literals in templates with automatic escaping are considered unsafe +because native Python strings (``str``, ``unicode``, ``basestring``) are not +`MarkupSafe.Markup` strings with an ``__html__`` attribute. + +.. _list-of-control-structures: + +List of Control Structures +-------------------------- + +A control structure refers to all those things that control the flow of a +program - conditionals (i.e. if/elif/else), for-loops, as well as things like +macros and blocks. With the default syntax, control structures appear inside +``{% ... %}`` blocks. + +.. _for-loop: + +For +~~~ + +Loop over each item in a sequence. For example, to display a list of users +provided in a variable called `users`:: + + <h1>Members</h1> + <ul> + {% for user in users %} + <li>{{ user.username|e }}</li> + {% endfor %} + </ul> + +As variables in templates retain their object properties, it is possible to +iterate over containers like `dict`:: + + <dl> + {% for key, value in my_dict.iteritems() %} + <dt>{{ key|e }}</dt> + <dd>{{ value|e }}</dd> + {% endfor %} + </dl> + +Note, however, that **Python dicts are not ordered**; so you might want to +either pass a sorted ``list`` of ``tuple`` s -- or a +``collections.OrderedDict`` -- to the template, or use the `dictsort` filter. + +Inside of a for-loop block, you can access some special variables: + ++-----------------------+---------------------------------------------------+ +| Variable | Description | ++=======================+===================================================+ +| `loop.index` | The current iteration of the loop. (1 indexed) | ++-----------------------+---------------------------------------------------+ +| `loop.index0` | The current iteration of the loop. (0 indexed) | ++-----------------------+---------------------------------------------------+ +| `loop.revindex` | The number of iterations from the end of the loop | +| | (1 indexed) | ++-----------------------+---------------------------------------------------+ +| `loop.revindex0` | The number of iterations from the end of the loop | +| | (0 indexed) | ++-----------------------+---------------------------------------------------+ +| `loop.first` | True if first iteration. | ++-----------------------+---------------------------------------------------+ +| `loop.last` | True if last iteration. | ++-----------------------+---------------------------------------------------+ +| `loop.length` | The number of items in the sequence. | ++-----------------------+---------------------------------------------------+ +| `loop.cycle` | A helper function to cycle between a list of | +| | sequences. See the explanation below. | ++-----------------------+---------------------------------------------------+ +| `loop.depth` | Indicates how deep in deep in a recursive loop | +| | the rendering currently is. Starts at level 1 | ++-----------------------+---------------------------------------------------+ +| `loop.depth0` | Indicates how deep in deep in a recursive loop | +| | the rendering currently is. Starts at level 0 | ++-----------------------+---------------------------------------------------+ + +Within a for-loop, it's possible to cycle among a list of strings/variables +each time through the loop by using the special `loop.cycle` helper:: + + {% for row in rows %} + <li class="{{ loop.cycle('odd', 'even') }}">{{ row }}</li> + {% endfor %} + +Since Jinja 2.1, an extra `cycle` helper exists that allows loop-unbound +cycling. For more information, have a look at the :ref:`builtin-globals`. + +.. _loop-filtering: + +Unlike in Python, it's not possible to `break` or `continue` in a loop. You +can, however, filter the sequence during iteration, which allows you to skip +items. The following example skips all the users which are hidden:: + + {% for user in users if not user.hidden %} + <li>{{ user.username|e }}</li> + {% endfor %} + +The advantage is that the special `loop` variable will count correctly; thus +not counting the users not iterated over. + +If no iteration took place because the sequence was empty or the filtering +removed all the items from the sequence, you can render a default block +by using `else`:: + + <ul> + {% for user in users %} + <li>{{ user.username|e }}</li> + {% else %} + <li><em>no users found</em></li> + {% endfor %} + </ul> + +Note that, in Python, `else` blocks are executed whenever the corresponding +loop **did not** `break`. Since Jinja loops cannot `break` anyway, +a slightly different behavior of the `else` keyword was chosen. + +It is also possible to use loops recursively. This is useful if you are +dealing with recursive data such as sitemaps or RDFa. +To use loops recursively, you basically have to add the `recursive` modifier +to the loop definition and call the `loop` variable with the new iterable +where you want to recurse. + +The following example implements a sitemap with recursive loops:: + + <ul class="sitemap"> + {%- for item in sitemap recursive %} + <li><a href="{{ item.href|e }}">{{ item.title }}</a> + {%- if item.children -%} + <ul class="submenu">{{ loop(item.children) }}</ul> + {%- endif %}</li> + {%- endfor %} + </ul> + +The `loop` variable always refers to the closest (innermost) loop. If we +have more than one level of loops, we can rebind the variable `loop` by +writing `{% set outer_loop = loop %}` after the loop that we want to +use recursively. Then, we can call it using `{{ outer_loop(...) }}` + +.. _if: + +If +~~ + +The `if` statement in Jinja is comparable with the Python if statement. +In the simplest form, you can use it to test if a variable is defined, not +empty or not false:: + + {% if users %} + <ul> + {% for user in users %} + <li>{{ user.username|e }}</li> + {% endfor %} + </ul> + {% endif %} + +For multiple branches, `elif` and `else` can be used like in Python. You can +use more complex :ref:`expressions` there, too:: + + {% if kenny.sick %} + Kenny is sick. + {% elif kenny.dead %} + You killed Kenny! You bastard!!! + {% else %} + Kenny looks okay --- so far + {% endif %} + +If can also be used as an :ref:`inline expression <if-expression>` and for +:ref:`loop filtering <loop-filtering>`. + +.. _macros: + +Macros +~~~~~~ + +Macros are comparable with functions in regular programming languages. They +are useful to put often used idioms into reusable functions to not repeat +yourself ("DRY"). + +Here's a small example of a macro that renders a form element:: + + {% macro input(name, value='', type='text', size=20) -%} + <input type="{{ type }}" name="{{ name }}" value="{{ + value|e }}" size="{{ size }}"> + {%- endmacro %} + +The macro can then be called like a function in the namespace:: + + <p>{{ input('username') }}</p> + <p>{{ input('password', type='password') }}</p> + +If the macro was defined in a different template, you have to +:ref:`import <import>` it first. + +Inside macros, you have access to three special variables: + +`varargs` + If more positional arguments are passed to the macro than accepted by the + macro, they end up in the special `varargs` variable as a list of values. + +`kwargs` + Like `varargs` but for keyword arguments. All unconsumed keyword + arguments are stored in this special variable. + +`caller` + If the macro was called from a :ref:`call<call>` tag, the caller is stored + in this variable as a callable macro. + +Macros also expose some of their internal details. The following attributes +are available on a macro object: + +`name` + The name of the macro. ``{{ input.name }}`` will print ``input``. + +`arguments` + A tuple of the names of arguments the macro accepts. + +`defaults` + A tuple of default values. + +`catch_kwargs` + This is `true` if the macro accepts extra keyword arguments (i.e.: accesses + the special `kwargs` variable). + +`catch_varargs` + This is `true` if the macro accepts extra positional arguments (i.e.: + accesses the special `varargs` variable). + +`caller` + This is `true` if the macro accesses the special `caller` variable and may + be called from a :ref:`call<call>` tag. + +If a macro name starts with an underscore, it's not exported and can't +be imported. + + +.. _call: + +Call +~~~~ + +In some cases it can be useful to pass a macro to another macro. For this +purpose, you can use the special `call` block. The following example shows +a macro that takes advantage of the call functionality and how it can be +used:: + + {% macro render_dialog(title, class='dialog') -%} + <div class="{{ class }}"> + <h2>{{ title }}</h2> + <div class="contents"> + {{ caller() }} + </div> + </div> + {%- endmacro %} + + {% call render_dialog('Hello World') %} + This is a simple dialog rendered by using a macro and + a call block. + {% endcall %} + +It's also possible to pass arguments back to the call block. This makes it +useful as a replacement for loops. Generally speaking, a call block works +exactly like a macro without a name. + +Here's an example of how a call block can be used with arguments:: + + {% macro dump_users(users) -%} + <ul> + {%- for user in users %} + <li><p>{{ user.username|e }}</p>{{ caller(user) }}</li> + {%- endfor %} + </ul> + {%- endmacro %} + + {% call(user) dump_users(list_of_user) %} + <dl> + <dl>Realname</dl> + <dd>{{ user.realname|e }}</dd> + <dl>Description</dl> + <dd>{{ user.description }}</dd> + </dl> + {% endcall %} + + +Filters +~~~~~~~ + +Filter sections allow you to apply regular Jinja2 filters on a block of +template data. Just wrap the code in the special `filter` section:: + + {% filter upper %} + This text becomes uppercase + {% endfilter %} + + +.. _assignments: + +Assignments +~~~~~~~~~~~ + +Inside code blocks, you can also assign values to variables. Assignments at +top level (outside of blocks, macros or loops) are exported from the template +like top level macros and can be imported by other templates. + +Assignments use the `set` tag and can have multiple targets:: + + {% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %} + {% set key, value = call_something() %} + + +Block Assignments +~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.8 + +Starting with Jinja 2.8, it's possible to also use block assignments to +capture the contents of a block into a variable name. This can be useful +in some situations as an alternative for macros. In that case, instead of +using an equals sign and a value, you just write the variable name and then +everything until ``{% endset %}`` is captured. + +Example:: + + {% set navigation %} + <li><a href="/">Index</a> + <li><a href="/downloads">Downloads</a> + {% endset %} + +The `navigation` variable then contains the navigation HTML source. + + +.. _extends: + +Extends +~~~~~~~ + +The `extends` tag can be used to extend one template from another. You can +have multiple `extends` tags in a file, but only one of them may be executed at +a time. + +See the section about :ref:`template-inheritance` above. + + +.. _blocks: + +Blocks +~~~~~~ + +Blocks are used for inheritance and act as both placeholders and replacements +at the same time. They are documented in detail in the +:ref:`template-inheritance` section. + + +Include +~~~~~~~ + +The `include` statement is useful to include a template and return the +rendered contents of that file into the current namespace:: + + {% include 'header.html' %} + Body + {% include 'footer.html' %} + +Included templates have access to the variables of the active context by +default. For more details about context behavior of imports and includes, +see :ref:`import-visibility`. + +From Jinja 2.2 onwards, you can mark an include with ``ignore missing``; in +which case Jinja will ignore the statement if the template to be included +does not exist. When combined with ``with`` or ``without context``, it must +be placed *before* the context visibility statement. Here are some valid +examples:: + + {% include "sidebar.html" ignore missing %} + {% include "sidebar.html" ignore missing with context %} + {% include "sidebar.html" ignore missing without context %} + +.. versionadded:: 2.2 + +You can also provide a list of templates that are checked for existence +before inclusion. The first template that exists will be included. If +`ignore missing` is given, it will fall back to rendering nothing if +none of the templates exist, otherwise it will raise an exception. + +Example:: + + {% include ['page_detailed.html', 'page.html'] %} + {% include ['special_sidebar.html', 'sidebar.html'] ignore missing %} + +.. versionchanged:: 2.4 + If a template object was passed to the template context, you can + include that object using `include`. + +.. _import: + +Import +~~~~~~ + +Jinja2 supports putting often used code into macros. These macros can go into +different templates and get imported from there. This works similarly to the +import statements in Python. It's important to know that imports are cached +and imported templates don't have access to the current template variables, +just the globals by default. For more details about context behavior of +imports and includes, see :ref:`import-visibility`. + +There are two ways to import templates. You can import a complete template +into a variable or request specific macros / exported variables from it. + +Imagine we have a helper module that renders forms (called `forms.html`):: + + {% macro input(name, value='', type='text') -%} + <input type="{{ type }}" value="{{ value|e }}" name="{{ name }}"> + {%- endmacro %} + + {%- macro textarea(name, value='', rows=10, cols=40) -%} + <textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols + }}">{{ value|e }}</textarea> + {%- endmacro %} + +The easiest and most flexible way to access a template's variables +and macros is to import the whole template module into a variable. +That way, you can access the attributes:: + + {% import 'forms.html' as forms %} + <dl> + <dt>Username</dt> + <dd>{{ forms.input('username') }}</dd> + <dt>Password</dt> + <dd>{{ forms.input('password', type='password') }}</dd> + </dl> + <p>{{ forms.textarea('comment') }}</p> + + +Alternatively, you can import specific names from a template into the current +namespace:: + + {% from 'forms.html' import input as input_field, textarea %} + <dl> + <dt>Username</dt> + <dd>{{ input_field('username') }}</dd> + <dt>Password</dt> + <dd>{{ input_field('password', type='password') }}</dd> + </dl> + <p>{{ textarea('comment') }}</p> + +Macros and variables starting with one or more underscores are private and +cannot be imported. + +.. versionchanged:: 2.4 + If a template object was passed to the template context, you can + import from that object. + + +.. _import-visibility: + +Import Context Behavior +----------------------- + +By default, included templates are passed the current context and imported +templates are not. The reason for this is that imports, unlike includes, +are cached; as imports are often used just as a module that holds macros. + +This behavior can be changed explicitly: by adding `with context` +or `without context` to the import/include directive, the current context +can be passed to the template and caching is disabled automatically. + +Here are two examples:: + + {% from 'forms.html' import input with context %} + {% include 'header.html' without context %} + +.. admonition:: Note + + In Jinja 2.0, the context that was passed to the included template + did not include variables defined in the template. As a matter of + fact, this did not work:: + + {% for box in boxes %} + {% include "render_box.html" %} + {% endfor %} + + The included template ``render_box.html`` is *not* able to access + `box` in Jinja 2.0. As of Jinja 2.1, ``render_box.html`` *is* able + to do so. + + +.. _expressions: + +Expressions +----------- + +Jinja allows basic expressions everywhere. These work very similarly to +regular Python; even if you're not working with Python +you should feel comfortable with it. + +Literals +~~~~~~~~ + +The simplest form of expressions are literals. Literals are representations +for Python objects such as strings and numbers. The following literals exist: + +"Hello World": + Everything between two double or single quotes is a string. They are + useful whenever you need a string in the template (e.g. as + arguments to function calls and filters, or just to extend or include a + template). + +42 / 42.23: + Integers and floating point numbers are created by just writing the + number down. If a dot is present, the number is a float, otherwise an + integer. Keep in mind that, in Python, ``42`` and ``42.0`` + are different (``int`` and ``float``, respectively). + +['list', 'of', 'objects']: + Everything between two brackets is a list. Lists are useful for storing + sequential data to be iterated over. For example, you can easily + create a list of links using lists and tuples for (and with) a for loop:: + + <ul> + {% for href, caption in [('index.html', 'Index'), ('about.html', 'About'), + ('downloads.html', 'Downloads')] %} + <li><a href="{{ href }}">{{ caption }}</a></li> + {% endfor %} + </ul> + +('tuple', 'of', 'values'): + Tuples are like lists that cannot be modified ("immutable"). If a tuple + only has one item, it must be followed by a comma (``('1-tuple',)``). + Tuples are usually used to represent items of two or more elements. + See the list example above for more details. + +{'dict': 'of', 'key': 'and', 'value': 'pairs'}: + A dict in Python is a structure that combines keys and values. Keys must + be unique and always have exactly one value. Dicts are rarely used in + templates; they are useful in some rare cases such as the :func:`xmlattr` + filter. + +true / false: + true is always true and false is always false. + +.. admonition:: Note + + The special constants `true`, `false`, and `none` are indeed lowercase. + Because that caused confusion in the past, (`True` used to expand + to an undefined variable that was considered false), + all three can now also be written in title case + (`True`, `False`, and `None`). + However, for consistency, (all Jinja identifiers are lowercase) + you should use the lowercase versions. + +Math +~~~~ + +Jinja allows you to calculate with values. This is rarely useful in templates +but exists for completeness' sake. The following operators are supported: + +\+ + Adds two objects together. Usually the objects are numbers, but if both are + strings or lists, you can concatenate them this way. This, however, is not + the preferred way to concatenate strings! For string concatenation, have + a look-see at the ``~`` operator. ``{{ 1 + 1 }}`` is ``2``. + +\- + Subtract the second number from the first one. ``{{ 3 - 2 }}`` is ``1``. + +/ + Divide two numbers. The return value will be a floating point number. + ``{{ 1 / 2 }}`` is ``{{ 0.5 }}``. + (Just like ``from __future__ import division``.) + +// + Divide two numbers and return the truncated integer result. + ``{{ 20 // 7 }}`` is ``2``. + +% + Calculate the remainder of an integer division. ``{{ 11 % 7 }}`` is ``4``. + +\* + Multiply the left operand with the right one. ``{{ 2 * 2 }}`` would + return ``4``. This can also be used to repeat a string multiple times. + ``{{ '=' * 80 }}`` would print a bar of 80 equal signs. + +\** + Raise the left operand to the power of the right operand. ``{{ 2**3 }}`` + would return ``8``. + +Comparisons +~~~~~~~~~~~ + +== + Compares two objects for equality. + +!= + Compares two objects for inequality. + +> + `true` if the left hand side is greater than the right hand side. + +>= + `true` if the left hand side is greater or equal to the right hand side. + +< + `true` if the left hand side is lower than the right hand side. + +<= + `true` if the left hand side is lower or equal to the right hand side. + +Logic +~~~~~ + +For `if` statements, `for` filtering, and `if` expressions, it can be useful to +combine multiple expressions: + +and + Return true if the left and the right operand are true. + +or + Return true if the left or the right operand are true. + +not + negate a statement (see below). + +(expr) + group an expression. + +.. admonition:: Note + + The ``is`` and ``in`` operators support negation using an infix notation, + too: ``foo is not bar`` and ``foo not in bar`` instead of ``not foo is bar`` + and ``not foo in bar``. All other expressions require a prefix notation: + ``not (foo and bar).`` + + +Other Operators +~~~~~~~~~~~~~~~ + +The following operators are very useful but don't fit into any of the other +two categories: + +in + Perform a sequence / mapping containment test. Returns true if the left + operand is contained in the right. ``{{ 1 in [1, 2, 3] }}`` would, for + example, return true. + +is + Performs a :ref:`test <tests>`. + +\| + Applies a :ref:`filter <filters>`. + +~ + Converts all operands into strings and concatenates them. + + ``{{ "Hello " ~ name ~ "!" }}`` would return (assuming `name` is set + to ``'John'``) ``Hello John!``. + +() + Call a callable: ``{{ post.render() }}``. Inside of the parentheses you + can use positional arguments and keyword arguments like in Python: + + ``{{ post.render(user, full=true) }}``. + +. / [] + Get an attribute of an object. (See :ref:`variables`) + + +.. _if-expression: + +If Expression +~~~~~~~~~~~~~ + +It is also possible to use inline `if` expressions. These are useful in some +situations. For example, you can use this to extend from one template if a +variable is defined, otherwise from the default layout template:: + + {% extends layout_template if layout_template is defined else 'master.html' %} + +The general syntax is ``<do something> if <something is true> else <do +something else>``. + +The `else` part is optional. If not provided, the else block implicitly +evaluates into an undefined object:: + + {{ '[%s]' % page.title if page.title }} + + +.. _builtin-filters: + +List of Builtin Filters +----------------------- + +.. jinjafilters:: + + +.. _builtin-tests: + +List of Builtin Tests +--------------------- + +.. jinjatests:: + +.. _builtin-globals: + +List of Global Functions +------------------------ + +The following functions are available in the global scope by default: + +.. function:: range([start,] stop[, step]) + + Return a list containing an arithmetic progression of integers. + ``range(i, j)`` returns ``[i, i+1, i+2, ..., j-1]``; + start (!) defaults to ``0``. + When step is given, it specifies the increment (or decrement). + For example, ``range(4)`` and ``range(0, 4, 1)`` return ``[0, 1, 2, 3]``. + The end point is omitted! + These are exactly the valid indices for a list of 4 elements. + + This is useful to repeat a template block multiple times, e.g. + to fill a list. Imagine you have 7 users in the list but you want to + render three empty items to enforce a height with CSS:: + + <ul> + {% for user in users %} + <li>{{ user.username }}</li> + {% endfor %} + {% for number in range(10 - users|count) %} + <li class="empty"><span>...</span></li> + {% endfor %} + </ul> + +.. function:: lipsum(n=5, html=True, min=20, max=100) + + Generates some lorem ipsum for the template. By default, five paragraphs + of HTML are generated with each paragraph between 20 and 100 words. + If html is False, regular text is returned. This is useful to generate simple + contents for layout testing. + +.. function:: dict(\**items) + + A convenient alternative to dict literals. ``{'foo': 'bar'}`` is the same + as ``dict(foo='bar')``. + +.. class:: cycler(\*items) + + The cycler allows you to cycle among values similar to how `loop.cycle` + works. Unlike `loop.cycle`, you can use this cycler outside of + loops or over multiple loops. + + This can be very useful if you want to show a list of folders and + files with the folders on top but both in the same list with alternating + row colors. + + The following example shows how `cycler` can be used:: + + {% set row_class = cycler('odd', 'even') %} + <ul class="browser"> + {% for folder in folders %} + <li class="folder {{ row_class.next() }}">{{ folder|e }}</li> + {% endfor %} + {% for filename in files %} + <li class="file {{ row_class.next() }}">{{ filename|e }}</li> + {% endfor %} + </ul> + + A cycler has the following attributes and methods: + + .. method:: reset() + + Resets the cycle to the first item. + + .. method:: next() + + Goes one item ahead and returns the then-current item. + + .. attribute:: current + + Returns the current item. + + **new in Jinja 2.1** + +.. class:: joiner(sep=', ') + + A tiny helper that can be used to "join" multiple sections. A joiner is + passed a string and will return that string every time it's called, except + the first time (in which case it returns an empty string). You can + use this to join things:: + + {% set pipe = joiner("|") %} + {% if categories %} {{ pipe() }} + Categories: {{ categories|join(", ") }} + {% endif %} + {% if author %} {{ pipe() }} + Author: {{ author() }} + {% endif %} + {% if can_edit %} {{ pipe() }} + <a href="?action=edit">Edit</a> + {% endif %} + + **new in Jinja 2.1** + + +Extensions +---------- + +The following sections cover the built-in Jinja2 extensions that may be +enabled by an application. An application could also provide further +extensions not covered by this documentation; in which case there should +be a separate document explaining said :ref:`extensions +<jinja-extensions>`. + +.. _i18n-in-templates: + +i18n +~~~~ + +If the i18n extension is enabled, it's possible to mark parts in the template +as translatable. To mark a section as translatable, you can use `trans`:: + + <p>{% trans %}Hello {{ user }}!{% endtrans %}</p> + +To translate a template expression --- say, using template filters, or by just +accessing an attribute of an object --- you need to bind the expression to a +name for use within the translation block:: + + <p>{% trans user=user.username %}Hello {{ user }}!{% endtrans %}</p> + +If you need to bind more than one expression inside a `trans` tag, separate +the pieces with a comma (``,``):: + + {% trans book_title=book.title, author=author.name %} + This is {{ book_title }} by {{ author }} + {% endtrans %} + +Inside trans tags no statements are allowed, only variable tags are. + +To pluralize, specify both the singular and plural forms with the `pluralize` +tag, which appears between `trans` and `endtrans`:: + + {% trans count=list|length %} + There is {{ count }} {{ name }} object. + {% pluralize %} + There are {{ count }} {{ name }} objects. + {% endtrans %} + +By default, the first variable in a block is used to determine the correct +singular or plural form. If that doesn't work out, you can specify the name +which should be used for pluralizing by adding it as parameter to `pluralize`:: + + {% trans ..., user_count=users|length %}... + {% pluralize user_count %}...{% endtrans %} + +It's also possible to translate strings in expressions. For that purpose, +three functions exist: + +- `gettext`: translate a single string +- `ngettext`: translate a pluralizable string +- `_`: alias for `gettext` + +For example, you can easily print a translated string like this:: + + {{ _('Hello World!') }} + +To use placeholders, use the `format` filter:: + + {{ _('Hello %(user)s!')|format(user=user.username) }} + +For multiple placeholders, always use keyword arguments to `format`, +as other languages may not use the words in the same order. + +.. versionchanged:: 2.5 + +If newstyle gettext calls are activated (:ref:`newstyle-gettext`), using +placeholders is a lot easier: + +.. sourcecode:: html+jinja + + {{ gettext('Hello World!') }} + {{ gettext('Hello %(name)s!', name='World') }} + {{ ngettext('%(num)d apple', '%(num)d apples', apples|count) }} + +Note that the `ngettext` function's format string automatically receives +the count as a `num` parameter in addition to the regular parameters. + + +Expression Statement +~~~~~~~~~~~~~~~~~~~~ + +If the expression-statement extension is loaded, a tag called `do` is available +that works exactly like the regular variable expression (``{{ ... }}``); except +it doesn't print anything. This can be used to modify lists:: + + {% do navigation.append('a string') %} + + +Loop Controls +~~~~~~~~~~~~~ + +If the application enables the :ref:`loopcontrols-extension`, it's possible to +use `break` and `continue` in loops. When `break` is reached, the loop is +terminated; if `continue` is reached, the processing is stopped and continues +with the next iteration. + +Here's a loop that skips every second item:: + + {% for user in users %} + {%- if loop.index is even %}{% continue %}{% endif %} + ... + {% endfor %} + +Likewise, a loop that stops processing after the 10th iteration:: + + {% for user in users %} + {%- if loop.index >= 10 %}{% break %}{% endif %} + {%- endfor %} + +Note that ``loop.index`` starts with 1, and ``loop.index0`` starts with 0 +(See: :ref:`for-loop`). + + +With Statement +~~~~~~~~~~~~~~ + +.. versionadded:: 2.3 + +If the application enables the :ref:`with-extension`, it is possible to +use the `with` keyword in templates. This makes it possible to create +a new inner scope. Variables set within this scope are not visible +outside of the scope. + +With in a nutshell:: + + {% with %} + {% set foo = 42 %} + {{ foo }} foo is 42 here + {% endwith %} + foo is not visible here any longer + +Because it is common to set variables at the beginning of the scope, +you can do that within the `with` statement. The following two examples +are equivalent:: + + {% with foo = 42 %} + {{ foo }} + {% endwith %} + + {% with %} + {% set foo = 42 %} + {{ foo }} + {% endwith %} + +.. _autoescape-overrides: + +Autoescape Extension +-------------------- + +.. versionadded:: 2.4 + +If the application enables the :ref:`autoescape-extension`, one can +activate and deactivate the autoescaping from within the templates. + +Example:: + + {% autoescape true %} + Autoescaping is active within this block + {% endautoescape %} + + {% autoescape false %} + Autoescaping is inactive within this block + {% endautoescape %} + +After an `endautoescape` the behavior is reverted to what it was before. diff --git a/deps/v8_inspector/deps/jinja2/docs/tricks.rst b/deps/v8_inspector/deps/jinja2/docs/tricks.rst new file mode 100644 index 00000000000000..4d33e220706e42 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/docs/tricks.rst @@ -0,0 +1,100 @@ +Tips and Tricks +=============== + +.. highlight:: html+jinja + +This part of the documentation shows some tips and tricks for Jinja2 +templates. + + +.. _null-master-fallback: + +Null-Master Fallback +-------------------- + +Jinja2 supports dynamic inheritance and does not distinguish between parent +and child template as long as no `extends` tag is visited. While this leads +to the surprising behavior that everything before the first `extends` tag +including whitespace is printed out instead of being ignored, it can be used +for a neat trick. + +Usually child templates extend from one template that adds a basic HTML +skeleton. However it's possible to put the `extends` tag into an `if` tag to +only extend from the layout template if the `standalone` variable evaluates +to false which it does per default if it's not defined. Additionally a very +basic skeleton is added to the file so that if it's indeed rendered with +`standalone` set to `True` a very basic HTML skeleton is added:: + + {% if not standalone %}{% extends 'master.html' %}{% endif -%} + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> + <title>{% block title %}The Page Title{% endblock %}</title> + <link rel="stylesheet" href="style.css" type="text/css"> + {% block body %} + <p>This is the page body.</p> + {% endblock %} + + +Alternating Rows +---------------- + +If you want to have different styles for each row of a table or +list you can use the `cycle` method on the `loop` object:: + + <ul> + {% for row in rows %} + <li class="{{ loop.cycle('odd', 'even') }}">{{ row }}</li> + {% endfor %} + </ul> + +`cycle` can take an unlimited amount of strings. Each time this +tag is encountered the next item from the list is rendered. + + +Highlighting Active Menu Items +------------------------------ + +Often you want to have a navigation bar with an active navigation +item. This is really simple to achieve. Because assignments outside +of `block`\s in child templates are global and executed before the layout +template is evaluated it's possible to define the active menu item in the +child template:: + + {% extends "layout.html" %} + {% set active_page = "index" %} + +The layout template can then access `active_page`. Additionally it makes +sense to define a default for that variable:: + + {% set navigation_bar = [ + ('/', 'index', 'Index'), + ('/downloads/', 'downloads', 'Downloads'), + ('/about/', 'about', 'About') + ] -%} + {% set active_page = active_page|default('index') -%} + ... + <ul id="navigation"> + {% for href, id, caption in navigation_bar %} + <li{% if id == active_page %} class="active"{% endif + %}><a href="{{ href|e }}">{{ caption|e }}</a></li> + {% endfor %} + </ul> + ... + +.. _accessing-the-parent-loop: + +Accessing the parent Loop +------------------------- + +The special `loop` variable always points to the innermost loop. If it's +desired to have access to an outer loop it's possible to alias it:: + + <table> + {% for row in table %} + <tr> + {% set rowloop = loop %} + {% for cell in row %} + <td id="cell-{{ rowloop.index }}-{{ loop.index }}">{{ cell }}</td> + {% endfor %} + </tr> + {% endfor %} + </table> diff --git a/deps/v8_inspector/deps/jinja2/examples/basic/cycle.py b/deps/v8_inspector/deps/jinja2/examples/basic/cycle.py new file mode 100644 index 00000000000000..73dd6325eb8064 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/basic/cycle.py @@ -0,0 +1,13 @@ +from jinja2 import Environment + + +env = Environment(line_statement_prefix="#", variable_start_string="${", variable_end_string="}") + + +print env.from_string("""\ +<ul> +# for item in range(10) + <li class="${loop.cycle('odd', 'even')}">${item}</li> +# endfor +</ul>\ +""").render() diff --git a/deps/v8_inspector/deps/jinja2/examples/basic/debugger.py b/deps/v8_inspector/deps/jinja2/examples/basic/debugger.py new file mode 100644 index 00000000000000..4291ff7ac4a9ff --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/basic/debugger.py @@ -0,0 +1,7 @@ +from jinja2 import Environment +from jinja2.loaders import FileSystemLoader + +env = Environment(loader=FileSystemLoader('templates')) + +tmpl = env.get_template('broken.html') +print tmpl.render(seq=[3, 2, 4, 5, 3, 2, 0, 2, 1]) diff --git a/deps/v8_inspector/deps/jinja2/examples/basic/inheritance.py b/deps/v8_inspector/deps/jinja2/examples/basic/inheritance.py new file mode 100644 index 00000000000000..aa687c898a1378 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/basic/inheritance.py @@ -0,0 +1,12 @@ +from jinja2 import Environment +from jinja2.loaders import DictLoader + + +env = Environment(loader=DictLoader({ +'a': '''[A[{% block body %}{% endblock %}]]''', +'b': '''{% extends 'a' %}{% block body %}[B]{% endblock %}''', +'c': '''{% extends 'b' %}{% block body %}###{{ super() }}###{% endblock %}''' +})) + + +print env.get_template('c').render() diff --git a/deps/v8_inspector/deps/jinja2/examples/basic/templates/broken.html b/deps/v8_inspector/deps/jinja2/examples/basic/templates/broken.html new file mode 100644 index 00000000000000..294d5c99894d9f --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/basic/templates/broken.html @@ -0,0 +1,6 @@ +{% from 'subbroken.html' import may_break %} +<ul> +{% for item in seq %} + <li>{{ may_break(item) }}</li> +{% endfor %} +</ul> diff --git a/deps/v8_inspector/deps/jinja2/examples/basic/templates/subbroken.html b/deps/v8_inspector/deps/jinja2/examples/basic/templates/subbroken.html new file mode 100644 index 00000000000000..245eb7e6e6f7f6 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/basic/templates/subbroken.html @@ -0,0 +1,3 @@ +{% macro may_break(item) -%} + [{{ item / 0 }}] +{%- endmacro %} diff --git a/deps/v8_inspector/deps/jinja2/examples/basic/test.py b/deps/v8_inspector/deps/jinja2/examples/basic/test.py new file mode 100644 index 00000000000000..8f7dde79221057 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/basic/test.py @@ -0,0 +1,27 @@ +from jinja2 import Environment +from jinja2.loaders import DictLoader + +env = Environment(loader=DictLoader({ +'child.html': u'''\ +{% extends master_layout or 'master.html' %} +{% include helpers = 'helpers.html' %} +{% macro get_the_answer() %}42{% endmacro %} +{% title = 'Hello World' %} +{% block body %} + {{ get_the_answer() }} + {{ helpers.conspirate() }} +{% endblock %} +''', +'master.html': u'''\ +<!doctype html> +<title>{{ title }}</title> +{% block body %}{% endblock %} +''', +'helpers.html': u'''\ +{% macro conspirate() %}23{% endmacro %} +''' +})) + + +tmpl = env.get_template("child.html") +print(tmpl.render()) diff --git a/deps/v8_inspector/deps/jinja2/examples/basic/test_filter_and_linestatements.py b/deps/v8_inspector/deps/jinja2/examples/basic/test_filter_and_linestatements.py new file mode 100644 index 00000000000000..79e0a4b371a5d0 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/basic/test_filter_and_linestatements.py @@ -0,0 +1,25 @@ +from jinja2 import Environment + + +env = Environment(line_statement_prefix='%', variable_start_string="${", variable_end_string="}") +tmpl = env.from_string("""\ +% macro foo() + ${caller(42)} +% endmacro +<ul> +% for item in seq + <li>${item}</li> +% endfor +</ul> +% call(var) foo() + [${var}] +% endcall +% filter escape + <hello world> + % for item in [1, 2, 3] + - ${item} + % endfor +% endfilter +""") + +print(tmpl.render(seq=range(10))) diff --git a/deps/v8_inspector/deps/jinja2/examples/basic/test_loop_filter.py b/deps/v8_inspector/deps/jinja2/examples/basic/test_loop_filter.py new file mode 100644 index 00000000000000..0469d04e432af8 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/basic/test_loop_filter.py @@ -0,0 +1,12 @@ +from jinja2 import Environment + +tmpl = Environment().from_string("""\ +<ul> +{%- for item in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] if item % 2 == 0 %} + <li>{{ loop.index }} / {{ loop.length }}: {{ item }}</li> +{%- endfor %} +</ul> +if condition: {{ 1 if foo else 0 }} +""") + +print(tmpl.render(foo=True)) diff --git a/deps/v8_inspector/deps/jinja2/examples/basic/translate.py b/deps/v8_inspector/deps/jinja2/examples/basic/translate.py new file mode 100644 index 00000000000000..1fb8ee63bf284b --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/basic/translate.py @@ -0,0 +1,14 @@ +from jinja2 import Environment + +env = Environment(extensions=['jinja2.ext.i18n']) +env.globals['gettext'] = { + 'Hello %(user)s!': 'Hallo %(user)s!' +}.__getitem__ +env.globals['ngettext'] = lambda s, p, n: { + '%(count)s user': '%(count)d Benutzer', + '%(count)s users': '%(count)d Benutzer' +}[n == 1 and s or p] +print env.from_string("""\ +{% trans %}Hello {{ user }}!{% endtrans %} +{% trans count=users|count %}{{ count }} user{% pluralize %}{{ count }} users{% endtrans %} +""").render(user="someone", users=[1, 2, 3]) diff --git a/deps/v8_inspector/deps/jinja2/examples/bench.py b/deps/v8_inspector/deps/jinja2/examples/bench.py new file mode 100644 index 00000000000000..c648dc69b02865 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/bench.py @@ -0,0 +1,433 @@ +"""\ + This benchmark compares some python templating engines with Jinja 2 so + that we get a picture of how fast Jinja 2 is for a semi real world + template. If a template engine is not installed the test is skipped.\ +""" +import sys +import cgi +from timeit import Timer +from jinja2 import Environment as JinjaEnvironment + +context = { + 'page_title': 'mitsuhiko\'s benchmark', + 'table': [dict(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10) for x in range(1000)] +} + +jinja_template = JinjaEnvironment( + line_statement_prefix='%', + variable_start_string="${", + variable_end_string="}" +).from_string("""\ +<!doctype html> +<html> + <head> + <title>${page_title|e}</title> + </head> + <body> + <div class="header"> + <h1>${page_title|e}</h1> + </div> + <ul class="navigation"> + % for href, caption in [ + ('index.html', 'Index'), + ('downloads.html', 'Downloads'), + ('products.html', 'Products') + ] + <li><a href="${href|e}">${caption|e}</a></li> + % endfor + </ul> + <div class="table"> + <table> + % for row in table + <tr> + % for cell in row + <td>${cell}</td> + % endfor + </tr> + % endfor + </table> + </div> + </body> +</html>\ +""") + +def test_jinja(): + jinja_template.render(context) + +try: + from tornado.template import Template +except ImportError: + test_tornado = None +else: + tornado_template = Template("""\ +<!doctype html> +<html> + <head> + <title>{{ page_title }}</title> + </head> + <body> + <div class="header"> + <h1>{{ page_title }}</h1> + </div> + <ul class="navigation"> + {% for href, caption in [ \ + ('index.html', 'Index'), \ + ('downloads.html', 'Downloads'), \ + ('products.html', 'Products') \ + ] %} + <li><a href="{{ href }}">{{ caption }}</a></li> + {% end %} + </ul> + <div class="table"> + <table> + {% for row in table %} + <tr> + {% for cell in row %} + <td>{{ cell }}</td> + {% end %} + </tr> + {% end %} + </table> + </div> + </body> +</html>\ +""") + + def test_tornado(): + tornado_template.generate(**context) + +try: + from django.conf import settings + settings.configure() + from django.template import Template as DjangoTemplate, Context as DjangoContext +except ImportError: + test_django = None +else: + django_template = DjangoTemplate("""\ +<!doctype html> +<html> + <head> + <title>{{ page_title }}</title> + </head> + <body> + <div class="header"> + <h1>{{ page_title }}</h1> + </div> + <ul class="navigation"> + {% for href, caption in navigation %} + <li><a href="{{ href }}">{{ caption }}</a></li> + {% endfor %} + </ul> + <div class="table"> + <table> + {% for row in table %} + <tr> + {% for cell in row %} + <td>{{ cell }}</td> + {% endfor %} + </tr> + {% endfor %} + </table> + </div> + </body> +</html>\ +""") + + def test_django(): + c = DjangoContext(context) + c['navigation'] = [('index.html', 'Index'), ('downloads.html', 'Downloads'), + ('products.html', 'Products')] + django_template.render(c) + +try: + from mako.template import Template as MakoTemplate +except ImportError: + test_mako = None +else: + mako_template = MakoTemplate("""\ +<!doctype html> +<html> + <head> + <title>${page_title|h}</title> + </head> + <body> + <div class="header"> + <h1>${page_title|h}</h1> + </div> + <ul class="navigation"> + % for href, caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]: + <li><a href="${href|h}">${caption|h}</a></li> + % endfor + </ul> + <div class="table"> + <table> + % for row in table: + <tr> + % for cell in row: + <td>${cell}</td> + % endfor + </tr> + % endfor + </table> + </div> + </body> +</html>\ +""") + + def test_mako(): + mako_template.render(**context) + +try: + from genshi.template import MarkupTemplate as GenshiTemplate +except ImportError: + test_genshi = None +else: + genshi_template = GenshiTemplate("""\ +<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/"> + <head> + <title>${page_title}</title> + </head> + <body> + <div class="header"> + <h1>${page_title}</h1> + </div> + <ul class="navigation"> + <li py:for="href, caption in [ + ('index.html', 'Index'), + ('downloads.html', 'Downloads'), + ('products.html', 'Products')]"><a href="${href}">${caption}</a></li> + </ul> + <div class="table"> + <table> + <tr py:for="row in table"> + <td py:for="cell in row">${cell}</td> + </tr> + </table> + </div> + </body> +</html>\ +""") + + def test_genshi(): + genshi_template.generate(**context).render('html', strip_whitespace=False) + +try: + from Cheetah.Template import Template as CheetahTemplate +except ImportError: + test_cheetah = None +else: + cheetah_template = CheetahTemplate("""\ +#import cgi +<!doctype html> +<html> + <head> + <title>$cgi.escape($page_title)</title> + </head> + <body> + <div class="header"> + <h1>$cgi.escape($page_title)</h1> + </div> + <ul class="navigation"> + #for $href, $caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]: + <li><a href="$cgi.escape($href)">$cgi.escape($caption)</a></li> + #end for + </ul> + <div class="table"> + <table> + #for $row in $table: + <tr> + #for $cell in $row: + <td>$cell</td> + #end for + </tr> + #end for + </table> + </div> + </body> +</html>\ +""", searchList=[dict(context)]) + + def test_cheetah(): + unicode(cheetah_template) + +try: + import tenjin +except ImportError: + test_tenjin = None +else: + tenjin_template = tenjin.Template() + tenjin_template.convert("""\ +<!doctype html> +<html> + <head> + <title>${page_title}</title> + </head> + <body> + <div class="header"> + <h1>${page_title}</h1> + </div> + <ul class="navigation"> +<?py for href, caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]: ?> + <li><a href="${href}">${caption}</a></li> +<?py #end ?> + </ul> + <div class="table"> + <table> +<?py for row in table: ?> + <tr> +<?py for cell in row: ?> + <td>#{cell}</td> +<?py #end ?> + </tr> +<?py #end ?> + </table> + </div> + </body> +</html>\ +""") + + def test_tenjin(): + from tenjin.helpers import escape, to_str + tenjin_template.render(context, locals()) + +try: + from spitfire.compiler import util as SpitfireTemplate + from spitfire.compiler.analyzer import o2_options as spitfire_optimizer +except ImportError: + test_spitfire = None +else: + spitfire_template = SpitfireTemplate.load_template("""\ +<!doctype html> +<html> + <head> + <title>$cgi.escape($page_title)</title> + </head> + <body> + <div class="header"> + <h1>$cgi.escape($page_title)</h1> + </div> + <ul class="navigation"> + #for $href, $caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')] + <li><a href="$cgi.escape($href)">$cgi.escape($caption)</a></li> + #end for + </ul> + <div class="table"> + <table> + #for $row in $table + <tr> + #for $cell in $row + <td>$cell</td> + #end for + </tr> + #end for + </table> + </div> + </body> +</html>\ +""", 'spitfire_tmpl', spitfire_optimizer, {'enable_filters': False}) + spitfire_context = dict(context, **{'cgi': cgi}) + + def test_spitfire(): + spitfire_template(search_list=[spitfire_context]).main() + + +try: + from chameleon.zpt.template import PageTemplate +except ImportError: + test_chameleon = None +else: + chameleon_template = PageTemplate("""\ +<html xmlns:tal="http://xml.zope.org/namespaces/tal"> + <head> + <title tal:content="page_title">Page Title</title> + </head> + <body> + <div class="header"> + <h1 tal:content="page_title">Page Title</h1> + </div> + <ul class="navigation"> + <li tal:repeat="item sections"><a tal:attributes="href item[0]" tal:content="item[1]">caption</a></li> + </ul> + <div class="table"> + <table> + <tr tal:repeat="row table"> + <td tal:repeat="cell row" tal:content="row[cell]">cell</td> + </tr> + </table> + </div> + </body> +</html>\ +""") + chameleon_context = dict(context) + chameleon_context['sections'] = [ + ('index.html', 'Index'), + ('downloads.html', 'Downloads'), + ('products.html', 'Products') + ] + def test_chameleon(): + chameleon_template.render(**chameleon_context) + +try: + from chameleon.zpt.template import PageTemplate + from chameleon.genshi import language +except ImportError: + test_chameleon_genshi = None +else: + chameleon_genshi_template = PageTemplate("""\ +<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/"> + <head> + <title>${page_title}</title> + </head> + <body> + <div class="header"> + <h1>${page_title}</h1> + </div> + <ul class="navigation"> + <li py:for="info in sections"><a href="${info[0]}">${info[1]}</a></li> + </ul> + <div class="table"> + <table> + <tr py:for="row in table"> + <td py:for="cell in row">${row[cell]}</td> + </tr> + </table> + </div> + </body> +</html>\ +""", parser=language.Parser()) + chameleon_genshi_context = dict(context) + chameleon_genshi_context['sections'] = [ + ('index.html', 'Index'), + ('downloads.html', 'Downloads'), + ('products.html', 'Products') + ] + def test_chameleon_genshi(): + chameleon_genshi_template.render(**chameleon_genshi_context) + + +sys.stdout.write('\r' + '\n'.join(( + '=' * 80, + 'Template Engine BigTable Benchmark'.center(80), + '=' * 80, + __doc__, + '-' * 80 +)) + '\n') + + +for test in 'jinja', 'mako', 'tornado', 'tenjin', 'spitfire', 'django', 'genshi', 'cheetah', 'chameleon', 'chameleon_genshi': + if locals()['test_' + test] is None: + sys.stdout.write(' %-20s*not installed*\n' % test) + continue + t = Timer(setup='from __main__ import test_%s as bench' % test, + stmt='bench()') + sys.stdout.write(' >> %-20s<running>' % test) + sys.stdout.flush() + sys.stdout.write('\r %-20s%.4f seconds\n' % (test, t.timeit(number=50) / 50)) +sys.stdout.write('-' * 80 + '\n') +sys.stdout.write('''\ + WARNING: The results of this benchmark are useless to compare the + performance of template engines and should not be taken seriously in any + way. It's testing the performance of simple loops and has no real-world + usefulnes. It only used to check if changes on the Jinja code affect + performance in a good or bad way and how it roughly compares to others. +''' + '=' * 80 + '\n') diff --git a/deps/v8_inspector/deps/jinja2/examples/profile.py b/deps/v8_inspector/deps/jinja2/examples/profile.py new file mode 100644 index 00000000000000..0c907ae3f12a64 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/profile.py @@ -0,0 +1,52 @@ +try: + from cProfile import Profile +except ImportError: + from profile import Profile +from pstats import Stats +from jinja2 import Environment as JinjaEnvironment + +context = { + 'page_title': 'mitsuhiko\'s benchmark', + 'table': [dict(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10) for x in range(1000)] +} + +source = """\ +% macro testmacro(x) + <span>${x}</span> +% endmacro +<!doctype html> +<html> + <head> + <title>${page_title|e}</title> + </head> + <body> + <div class="header"> + <h1>${page_title|e}</h1> + </div> + <div class="table"> + <table> + % for row in table + <tr> + % for cell in row + <td>${testmacro(cell)}</td> + % endfor + </tr> + % endfor + </table> + </div> + </body> +</html>\ +""" +jinja_template = JinjaEnvironment( + line_statement_prefix='%', + variable_start_string="${", + variable_end_string="}" +).from_string(source) +print jinja_template.environment.compile(source, raw=True) + + +p = Profile() +p.runcall(lambda: jinja_template.render(context)) +stats = Stats(p) +stats.sort_stats('time', 'calls') +stats.print_stats() diff --git a/deps/v8_inspector/deps/jinja2/examples/rwbench/django/_form.html b/deps/v8_inspector/deps/jinja2/examples/rwbench/django/_form.html new file mode 100644 index 00000000000000..9c4f710eacc405 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/rwbench/django/_form.html @@ -0,0 +1 @@ +<form action="{{ action }}" method="{{ method }}">{{ body }}</form> diff --git a/deps/v8_inspector/deps/jinja2/examples/rwbench/django/_input_field.html b/deps/v8_inspector/deps/jinja2/examples/rwbench/django/_input_field.html new file mode 100644 index 00000000000000..290fdbd446ed26 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/rwbench/django/_input_field.html @@ -0,0 +1 @@ +<input type="{{ type }}" value="{{ value }}" name="{{ name }}"> diff --git a/deps/v8_inspector/deps/jinja2/examples/rwbench/django/_textarea.html b/deps/v8_inspector/deps/jinja2/examples/rwbench/django/_textarea.html new file mode 100644 index 00000000000000..7f104240559c96 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/rwbench/django/_textarea.html @@ -0,0 +1 @@ +<textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols }}">{{ value }}</textarea> diff --git a/deps/v8_inspector/deps/jinja2/examples/rwbench/django/index.html b/deps/v8_inspector/deps/jinja2/examples/rwbench/django/index.html new file mode 100644 index 00000000000000..6f620bb2c04f55 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/rwbench/django/index.html @@ -0,0 +1,29 @@ +{% extends "layout.html" %} +{% block page_title %}Index Page{% endblock %} +{% block body %} + {% for article in articles %} + {% if article.published %} + <div class="article"> + <h2><a href="{{ article.href }}">{{ article.title }}</a></h2> + <p class="meta">written by <a href="{{ article.user.href }}">{{ article.user.username }}</a> on {{ article.pub_date|dateformat }}</p> + <div class="text">{{ article.body|safe }}</div> + </div> + {% endif %} + {% endfor %} + {% form %} + <dl> + <dt>Name</dt> + <dd>{% input_field 'name' %}</dd> + <dt>E-Mail</dt> + <dd>{% input_field 'email' %}</dd> + <dt>URL</dt> + <dd>{% input_field 'url' %}</dd> + <dt>Comment</dt> + <dd>{% textarea 'comment' %}</dd> + <dt>Captcha</dt> + <dd>{% input_field 'captcha' %}</dd> + </dl> + {% input_field '' 'submit' 'Submit' %} + {% input_field 'cancel' 'submit' 'Cancel' %} + {% endform %} +{% endblock %} diff --git a/deps/v8_inspector/deps/jinja2/examples/rwbench/django/layout.html b/deps/v8_inspector/deps/jinja2/examples/rwbench/django/layout.html new file mode 100644 index 00000000000000..60039cebad1f69 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/rwbench/django/layout.html @@ -0,0 +1,29 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> +<head> + <title>{% block page_title %}{% endblock %} | RealWorld Benchmark</title> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> +</head> +<body> + <div class="contents"> + <div class="header"> + <h1>RealWorld Benchmark</h1> + <blockquote><p> + A less stupid benchmark for Mako and Jinja2 to get an impression how + code changes affect runtime performance. + </p></blockquote> + </div> + <ul class="navigation"> + {% for href, caption in page_navigation %} + <li><a href="{{ href }}">{{ caption }}</a></li> + {% endfor %} + </ul> + <div class="body"> + {% block body %}{% endblock %} + </div> + <div class="footer"> + © Copyright 2008 by I don't know who. + </div> + </div> +</body> +</html> diff --git a/deps/v8_inspector/deps/jinja2/examples/rwbench/djangoext.py b/deps/v8_inspector/deps/jinja2/examples/rwbench/djangoext.py new file mode 100644 index 00000000000000..9e9fa6cf0d7095 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/rwbench/djangoext.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- +from rwbench import ROOT +from os.path import join +from django.conf import settings +settings.configure( + TEMPLATE_DIRS=(join(ROOT, 'django'),), + TEMPLATE_LOADERS=( + ('django.template.loaders.cached.Loader', ( + 'django.template.loaders.filesystem.Loader', + )), + ) +) +from django.template import loader as django_loader, Context as DjangoContext, \ + Node, NodeList, Variable, TokenParser +from django import template as django_template_module +from django.template import Library + + +# for django extensions. We monkey patch our extensions in so that +# we don't have to initialize a more complex django setup. +django_extensions = django_template_module.Library() +django_template_module.builtins.append(django_extensions) + + +from rwbench import dateformat +django_extensions.filter(dateformat) + + +def var_or_none(x): + if x is not None: + return Variable(x) + + +# and more django extensions +@django_extensions.tag +def input_field(parser, token): + p = TokenParser(token.contents) + args = [p.value()] + while p.more(): + args.append(p.value()) + return InputFieldNode(*args) + + +@django_extensions.tag +def textarea(parser, token): + p = TokenParser(token.contents) + args = [p.value()] + while p.more(): + args.append(p.value()) + return TextareaNode(*args) + + +@django_extensions.tag +def form(parser, token): + p = TokenParser(token.contents) + args = [] + while p.more(): + args.append(p.value()) + body = parser.parse(('endform',)) + parser.delete_first_token() + return FormNode(body, *args) + + +class InputFieldNode(Node): + + def __init__(self, name, type=None, value=None): + self.name = var_or_none(name) + self.type = var_or_none(type) + self.value = var_or_none(value) + + def render(self, context): + name = self.name.resolve(context) + type = 'text' + value = '' + if self.type is not None: + type = self.type.resolve(context) + if self.value is not None: + value = self.value.resolve(context) + tmpl = django_loader.get_template('_input_field.html') + return tmpl.render(DjangoContext({ + 'name': name, + 'type': type, + 'value': value + })) + + +class TextareaNode(Node): + + def __init__(self, name, rows=None, cols=None, value=None): + self.name = var_or_none(name) + self.rows = var_or_none(rows) + self.cols = var_or_none(cols) + self.value = var_or_none(value) + + def render(self, context): + name = self.name.resolve(context) + rows = 10 + cols = 40 + value = '' + if self.rows is not None: + rows = int(self.rows.resolve(context)) + if self.cols is not None: + cols = int(self.cols.resolve(context)) + if self.value is not None: + value = self.value.resolve(context) + tmpl = django_loader.get_template('_textarea.html') + return tmpl.render(DjangoContext({ + 'name': name, + 'rows': rows, + 'cols': cols, + 'value': value + })) + + +class FormNode(Node): + + def __init__(self, body, action=None, method=None): + self.body = body + self.action = action + self.method = method + + def render(self, context): + body = self.body.render(context) + action = '' + method = 'post' + if self.action is not None: + action = self.action.resolve(context) + if self.method is not None: + method = self.method.resolve(context) + tmpl = django_loader.get_template('_form.html') + return tmpl.render(DjangoContext({ + 'body': body, + 'action': action, + 'method': method + })) diff --git a/deps/v8_inspector/deps/jinja2/examples/rwbench/genshi/helpers.html b/deps/v8_inspector/deps/jinja2/examples/rwbench/genshi/helpers.html new file mode 100644 index 00000000000000..ecc6dc4d9ca897 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/rwbench/genshi/helpers.html @@ -0,0 +1,12 @@ +<div xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/" + py:strip=""> + + <py:def function="input_field(name='', value='', type='text')"> + <input type="$type" value="$value" name="$name" /> + </py:def> + + <py:def function="textarea(name, value='', rows=10, cols=40)"> + <textarea name="$name" rows="$rows" cols="cols">$value</textarea> + </py:def> + +</div> diff --git a/deps/v8_inspector/deps/jinja2/examples/rwbench/genshi/index.html b/deps/v8_inspector/deps/jinja2/examples/rwbench/genshi/index.html new file mode 100644 index 00000000000000..70f697d5765651 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/rwbench/genshi/index.html @@ -0,0 +1,41 @@ +<?python + from rwbench import dateformat +?> +<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xi="http://www.w3.org/2001/XInclude" + xmlns:py="http://genshi.edgewall.org/"> + <xi:include href="layout.html" /> + <xi:include href="helpers.html" /> + <head><title>Index Page</title></head> + <body> + <div class="article" py:for="article in articles"> + <py:if test="article.published"> + <h2><a href="${article.href}">${article.title}</a></h2> + <p class="meta">written by <a href="${article.user.href}" + >${article.user.username}</a> on ${dateformat(article.pub_date)}</p> + <div class="text">${Markup(article.body)}</div> + </py:if> + </div> + <!-- + For a fair and balanced comparison we would have to use a def here + that wraps the form data but I don't know what would be the best + Genshi equivalent for that. Quite frankly I doubt that this makes + sense in Genshi anyways. + --> + <form action="" method="post"> + <dl> + <dt>Name</dt> + <dd>${input_field('name')}</dd> + <dt>E-Mail</dt> + <dd>${input_field('email')}</dd> + <dt>URL</dt> + <dd>${input_field('url')}</dd> + <dt>Comment</dt> + <dd>${textarea('comment')}</dd> + <dt>Captcha</dt> + <dd>${input_field('captcha')}</dd> + </dl> + ${input_field(type='submit', value='Submit')} + ${input_field(name='cancel', type='submit', value='Cancel')} + </form> + </body> +</html> diff --git a/deps/v8_inspector/deps/jinja2/examples/rwbench/genshi/layout.html b/deps/v8_inspector/deps/jinja2/examples/rwbench/genshi/layout.html new file mode 100644 index 00000000000000..b12aec48d88327 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/rwbench/genshi/layout.html @@ -0,0 +1,30 @@ +<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/" > + <py:match path="head" once="true"> + <head> + <title>${select('title/text()')} | RealWorld Benchmark</title> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + </head> + </py:match> + <py:match path="body" once="true"> + <body> + <div class="contents"> + <div class="header"> + <h1>RealWorld Benchmark</h1> + <blockquote><p> + A less stupid benchmark for Mako and Jinja2 to get an impression how + code changes affect runtime performance. + </p></blockquote> + </div> + <ul class="navigation"> + <li py:for="href, caption in page_navigation"><a href="$href">$caption</a></li> + </ul> + <div class="body"> + ${select('*|text()')} + </div> + <div class="footer"> + © Copyright 2008 by I don't know who. + </div> + </div> + </body> + </py:match> +</html> diff --git a/deps/v8_inspector/deps/jinja2/examples/rwbench/jinja/helpers.html b/deps/v8_inspector/deps/jinja2/examples/rwbench/jinja/helpers.html new file mode 100644 index 00000000000000..89976aa05f1404 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/rwbench/jinja/helpers.html @@ -0,0 +1,12 @@ +{% macro input_field(name, value='', type='text') -%} + <input type="{{ type }}" value="{{ value|e }}" name="{{ name }}"> +{%- endmacro %} + +{% macro textarea(name, value='', rows=10, cols=40) -%} + <textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols }}">{{ + value|e }}</textarea> +{%- endmacro %} + +{% macro form(action='', method='post') -%} + <form action="{{ action|e }}" method="{{ method }}">{{ caller() }}</form> +{%- endmacro %} diff --git a/deps/v8_inspector/deps/jinja2/examples/rwbench/jinja/index.html b/deps/v8_inspector/deps/jinja2/examples/rwbench/jinja/index.html new file mode 100644 index 00000000000000..b006d0565e1a04 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/rwbench/jinja/index.html @@ -0,0 +1,29 @@ +{% extends "layout.html" %} +{% from "helpers.html" import input_field, textarea, form %} +{% block page_title %}Index Page{% endblock %} +{% block body %} + {%- for article in articles if article.published %} + <div class="article"> + <h2><a href="{{ article.href|e }}">{{ article.title|e }}</a></h2> + <p class="meta">written by <a href="{{ article.user.href|e + }}">{{ article.user.username|e }}</a> on {{ article.pub_date|dateformat }}</p> + <div class="text">{{ article.body }}</div> + </div> + {%- endfor %} + {%- call form() %} + <dl> + <dt>Name</dt> + <dd>{{ input_field('name') }}</dd> + <dt>E-Mail</dt> + <dd>{{ input_field('email') }}</dd> + <dt>URL</dt> + <dd>{{ input_field('url') }}</dd> + <dt>Comment</dt> + <dd>{{ textarea('comment') }}</dd> + <dt>Captcha</dt> + <dd>{{ input_field('captcha') }}</dd> + </dl> + {{ input_field(type='submit', value='Submit') }} + {{ input_field('cancel', type='submit', value='Cancel') }} + {%- endcall %} +{% endblock %} diff --git a/deps/v8_inspector/deps/jinja2/examples/rwbench/jinja/layout.html b/deps/v8_inspector/deps/jinja2/examples/rwbench/jinja/layout.html new file mode 100644 index 00000000000000..755789e24877fb --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/rwbench/jinja/layout.html @@ -0,0 +1,29 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> +<head> + <title>{% block page_title %}{% endblock %} | RealWorld Benchmark</title> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> +</head> +<body> + <div class="contents"> + <div class="header"> + <h1>RealWorld Benchmark</h1> + <blockquote><p> + A less stupid benchmark for Mako and Jinja2 to get an impression how + code changes affect runtime performance. + </p></blockquote> + </div> + <ul class="navigation"> + {%- for href, caption in page_navigation %} + <li><a href="{{ href|e }}">{{ caption }}</a></li> + {%- endfor %} + </ul> + <div class="body"> + {% block body %}{% endblock %} + </div> + <div class="footer"> + © Copyright 2008 by I don't know who. + </div> + </div> +</body> +</html> diff --git a/deps/v8_inspector/deps/jinja2/examples/rwbench/mako/helpers.html b/deps/v8_inspector/deps/jinja2/examples/rwbench/mako/helpers.html new file mode 100644 index 00000000000000..a0290ebb2e9fae --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/rwbench/mako/helpers.html @@ -0,0 +1,11 @@ +<%def name="input_field(name='', value='', type='text')"> + <input type="${type}" value="${value|h}" name="${name}"> +</%def> + +<%def name="textarea(name, value='', rows=10, cols=40)"> + <textarea name="${name}" rows="${rows}" cols="${cols}">${value|h}</textarea> +</%def> + +<%def name="form(action='', method='post')"> + <form action="${action|h}" method="${method}">${caller.body()}</form> +</%def> diff --git a/deps/v8_inspector/deps/jinja2/examples/rwbench/mako/index.html b/deps/v8_inspector/deps/jinja2/examples/rwbench/mako/index.html new file mode 100644 index 00000000000000..c4c6303246e364 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/rwbench/mako/index.html @@ -0,0 +1,31 @@ +<%! + from rwbench import dateformat +%> +<%inherit file="layout.html" /> +<%namespace file="helpers.html" import="input_field, textarea, form" /> +<%def name="page_title()">Index Page</%def> +% for article in articles: + <% if not article.published: continue %> +<div class="article"> + <h2><a href="${article.href|h}">${article.title|h}</a></h2> + <p class="meta">written by <a href="${article.user.href|h + }">${article.user.username|h}</a> on ${dateformat(article.pub_date)}</p> + <div class="text">${article.body}</div> +</div> +% endfor +<%call expr="form()"> + <dl> + <dt>Name</dt> + <dd>${input_field('name')}</dd> + <dt>E-Mail</dt> + <dd>${input_field('email')}</dd> + <dt>URL</dt> + <dd>${input_field('url')}</dd> + <dt>Comment</dt> + <dd>${textarea('comment')}</dd> + <dt>Captcha</dt> + <dd>${input_field('captcha')}</dd> + </dl> + ${input_field(type='submit', value='Submit')} + ${input_field(name='cancel', type='submit', value='Cancel')} +</%call> diff --git a/deps/v8_inspector/deps/jinja2/examples/rwbench/mako/layout.html b/deps/v8_inspector/deps/jinja2/examples/rwbench/mako/layout.html new file mode 100644 index 00000000000000..a9c353e1c65a5d --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/rwbench/mako/layout.html @@ -0,0 +1,30 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> +<head> + <title>${self.page_title()} | RealWorld Benchmark</title> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> +</head> +<body> + <div class="contents"> + <div class="header"> + <h1>RealWorld Benchmark</h1> + <blockquote><p> + A less stupid benchmark for Mako and Jinja2 to get an impression how + code changes affect runtime performance. + </p></blockquote> + </div> + <ul class="navigation"> + % for href, caption in page_navigation: + <li><a href="${href|h}">${caption}</a></li> + % endfor + </ul> + <div class="body"> + ${self.body()} + </div> + <div class="footer"> + © Copyright 2008 by I don't know who. + </div> + </div> +</body> +</html> +<%def name="page_title()"></%def> diff --git a/deps/v8_inspector/deps/jinja2/examples/rwbench/rwbench.py b/deps/v8_inspector/deps/jinja2/examples/rwbench/rwbench.py new file mode 100644 index 00000000000000..813dd56cf71431 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/examples/rwbench/rwbench.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +""" + RealWorldish Benchmark + ~~~~~~~~~~~~~~~~~~~~~~ + + A more real-world benchmark of Jinja2. Like the other benchmark in the + Jinja2 repository this has no real-world usefulnes (despite the name). + Just go away and ignore it. NOW! + + :copyright: (c) 2009 by the Jinja Team. + :license: BSD. +""" +import sys +from os.path import join, dirname, abspath +try: + from cProfile import Profile +except ImportError: + from profile import Profile +from pstats import Stats +ROOT = abspath(dirname(__file__)) + +from random import choice, randrange +from datetime import datetime +from timeit import Timer +from jinja2 import Environment, FileSystemLoader +from jinja2.utils import generate_lorem_ipsum +from mako.lookup import TemplateLookup +from genshi.template import TemplateLoader as GenshiTemplateLoader + + +def dateformat(x): + return x.strftime('%Y-%m-%d') + + +jinja_env = Environment(loader=FileSystemLoader(join(ROOT, 'jinja'))) +jinja_env.filters['dateformat'] = dateformat +mako_lookup = TemplateLookup(directories=[join(ROOT, 'mako')]) +genshi_loader = GenshiTemplateLoader([join(ROOT, 'genshi')]) + +class Article(object): + + def __init__(self, id): + self.id = id + self.href = '/article/%d' % self.id + self.title = generate_lorem_ipsum(1, False, 5, 10) + self.user = choice(users) + self.body = generate_lorem_ipsum() + self.pub_date = datetime.utcfromtimestamp(randrange(10 ** 9, 2 * 10 ** 9)) + self.published = True + + +class User(object): + + def __init__(self, username): + self.href = '/user/%s' % username + self.username = username + + +users = map(User, [u'John Doe', u'Jane Doe', u'Peter Somewhat']) +articles = map(Article, range(20)) +navigation = [ + ('index', 'Index'), + ('about', 'About'), + ('foo?bar=1', 'Foo with Bar'), + ('foo?bar=2&s=x', 'Foo with X'), + ('blah', 'Blub Blah'), + ('hehe', 'Haha'), +] * 5 + +context = dict(users=users, articles=articles, page_navigation=navigation) + + +jinja_template = jinja_env.get_template('index.html') +mako_template = mako_lookup.get_template('index.html') +genshi_template = genshi_loader.load('index.html') + + +def test_jinja(): + jinja_template.render(context) + +def test_mako(): + mako_template.render_unicode(**context) + + +from djangoext import django_loader, DjangoContext +def test_django(): + # not cached because django is not thread safe and does + # not cache by itself so it would be unfair to cache it here. + django_template = django_loader.get_template('index.html') + django_template.render(DjangoContext(context)) + + +def test_genshi(): + genshi_template.generate(**context).render('html', doctype='html') + + +if __name__ == '__main__': + sys.stdout.write('Realworldish Benchmark:\n') + for test in 'jinja', 'mako', 'django', 'genshi': + t = Timer(setup='from __main__ import test_%s as bench' % test, + stmt='bench()') + sys.stdout.write(' >> %-20s<running>' % test) + sys.stdout.flush() + sys.stdout.write('\r %-20s%.4f seconds\n' % (test, t.timeit(number=200) / 200)) + + if '-p' in sys.argv: + print 'Jinja profile' + p = Profile() + p.runcall(test_jinja) + stats = Stats(p) + stats.sort_stats('time', 'calls') + stats.print_stats() diff --git a/deps/v8_inspector/deps/jinja2/ext/Vim/jinja.vim b/deps/v8_inspector/deps/jinja2/ext/Vim/jinja.vim new file mode 100644 index 00000000000000..ac72efe796e476 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/ext/Vim/jinja.vim @@ -0,0 +1,135 @@ +" Vim syntax file +" Language: Jinja template +" Maintainer: Armin Ronacher <armin.ronacher@active-4.com> +" Last Change: 2008 May 9 +" Version: 1.1 +" +" Known Bugs: +" because of odd limitations dicts and the modulo operator +" appear wrong in the template. +" +" Changes: +" +" 2008 May 9: Added support for Jinja2 changes (new keyword rules) + +" .vimrc variable to disable html highlighting +if !exists('g:jinja_syntax_html') + let g:jinja_syntax_html=1 +endif + +" For version 5.x: Clear all syntax items +" For version 6.x: Quit when a syntax file was already loaded +if !exists("main_syntax") + if version < 600 + syntax clear + elseif exists("b:current_syntax") + finish +endif + let main_syntax = 'jinja' +endif + +" Pull in the HTML syntax. +if g:jinja_syntax_html + if version < 600 + so <sfile>:p:h/html.vim + else + runtime! syntax/html.vim + unlet b:current_syntax + endif +endif + +syntax case match + +" Jinja template built-in tags and parameters (without filter, macro, is and raw, they +" have special threatment) +syn keyword jinjaStatement containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained and if else in not or recursive as import + +syn keyword jinjaStatement containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained is filter skipwhite nextgroup=jinjaFilter +syn keyword jinjaStatement containedin=jinjaTagBlock contained macro skipwhite nextgroup=jinjaFunction +syn keyword jinjaStatement containedin=jinjaTagBlock contained block skipwhite nextgroup=jinjaBlockName + +" Variable Names +syn match jinjaVariable containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained /[a-zA-Z_][a-zA-Z0-9_]*/ +syn keyword jinjaSpecial containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained false true none False True None loop super caller varargs kwargs + +" Filters +syn match jinjaOperator "|" containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained skipwhite nextgroup=jinjaFilter +syn match jinjaFilter contained /[a-zA-Z_][a-zA-Z0-9_]*/ +syn match jinjaFunction contained /[a-zA-Z_][a-zA-Z0-9_]*/ +syn match jinjaBlockName contained /[a-zA-Z_][a-zA-Z0-9_]*/ + +" Jinja template constants +syn region jinjaString containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained start=/"/ skip=/\(\\\)\@<!\(\(\\\\\)\@>\)*\\"/ end=/"/ +syn region jinjaString containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained start=/'/ skip=/\(\\\)\@<!\(\(\\\\\)\@>\)*\\'/ end=/'/ +syn match jinjaNumber containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained /[0-9]\+\(\.[0-9]\+\)\?/ + +" Operators +syn match jinjaOperator containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained /[+\-*\/<>=!,:]/ +syn match jinjaPunctuation containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained /[()\[\]]/ +syn match jinjaOperator containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained /\./ nextgroup=jinjaAttribute +syn match jinjaAttribute contained /[a-zA-Z_][a-zA-Z0-9_]*/ + +" Jinja template tag and variable blocks +syn region jinjaNested matchgroup=jinjaOperator start="(" end=")" transparent display containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained +syn region jinjaNested matchgroup=jinjaOperator start="\[" end="\]" transparent display containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained +syn region jinjaNested matchgroup=jinjaOperator start="{" end="}" transparent display containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained +syn region jinjaTagBlock matchgroup=jinjaTagDelim start=/{%-\?/ end=/-\?%}/ containedin=ALLBUT,jinjaTagBlock,jinjaVarBlock,jinjaRaw,jinjaString,jinjaNested,jinjaComment + +syn region jinjaVarBlock matchgroup=jinjaVarDelim start=/{{-\?/ end=/-\?}}/ containedin=ALLBUT,jinjaTagBlock,jinjaVarBlock,jinjaRaw,jinjaString,jinjaNested,jinjaComment + +" Jinja template 'raw' tag +syn region jinjaRaw matchgroup=jinjaRawDelim start="{%\s*raw\s*%}" end="{%\s*endraw\s*%}" containedin=ALLBUT,jinjaTagBlock,jinjaVarBlock,jinjaString,jinjaComment + +" Jinja comments +syn region jinjaComment matchgroup=jinjaCommentDelim start="{#" end="#}" containedin=ALLBUT,jinjaTagBlock,jinjaVarBlock,jinjaString + +" Block start keywords. A bit tricker. We only highlight at the start of a +" tag block and only if the name is not followed by a comma or equals sign +" which usually means that we have to deal with an assignment. +syn match jinjaStatement containedin=jinjaTagBlock contained /\({%-\?\s*\)\@<=\<[a-zA-Z_][a-zA-Z0-9_]*\>\(\s*[,=]\)\@!/ + +" and context modifiers +syn match jinjaStatement containedin=jinjaTagBlock contained /\<with\(out\)\?\s\+context\>/ + + +" Define the default highlighting. +" For version 5.7 and earlier: only when not done already +" For version 5.8 and later: only when an item doesn't have highlighting yet +if version >= 508 || !exists("did_jinja_syn_inits") + if version < 508 + let did_jinja_syn_inits = 1 + command -nargs=+ HiLink hi link <args> + else + command -nargs=+ HiLink hi def link <args> + endif + + HiLink jinjaPunctuation jinjaOperator + HiLink jinjaAttribute jinjaVariable + HiLink jinjaFunction jinjaFilter + + HiLink jinjaTagDelim jinjaTagBlock + HiLink jinjaVarDelim jinjaVarBlock + HiLink jinjaCommentDelim jinjaComment + HiLink jinjaRawDelim jinja + + HiLink jinjaSpecial Special + HiLink jinjaOperator Normal + HiLink jinjaRaw Normal + HiLink jinjaTagBlock PreProc + HiLink jinjaVarBlock PreProc + HiLink jinjaStatement Statement + HiLink jinjaFilter Function + HiLink jinjaBlockName Function + HiLink jinjaVariable Identifier + HiLink jinjaString Constant + HiLink jinjaNumber Constant + HiLink jinjaComment Comment + + delcommand HiLink +endif + +let b:current_syntax = "jinja" + +if main_syntax == 'jinja' + unlet main_syntax +endif diff --git a/deps/v8_inspector/deps/jinja2/ext/django2jinja/django2jinja.py b/deps/v8_inspector/deps/jinja2/ext/django2jinja/django2jinja.py new file mode 100644 index 00000000000000..824d6a1c3679b5 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/ext/django2jinja/django2jinja.py @@ -0,0 +1,768 @@ +# -*- coding: utf-8 -*- +""" + Django to Jinja + ~~~~~~~~~~~~~~~ + + Helper module that can convert django templates into Jinja2 templates. + + This file is not intended to be used as stand alone application but to + be used as library. To convert templates you basically create your own + writer, add extra conversion logic for your custom template tags, + configure your django environment and run the `convert_templates` + function. + + Here a simple example:: + + # configure django (or use settings.configure) + import os + os.environ['DJANGO_SETTINGS_MODULE'] = 'yourapplication.settings' + from yourapplication.foo.templatetags.bar import MyNode + + from django2jinja import Writer, convert_templates + + def write_my_node(writer, node): + writer.start_variable() + writer.write('myfunc(') + for idx, arg in enumerate(node.args): + if idx: + writer.write(', ') + writer.node(arg) + writer.write(')') + writer.end_variable() + + writer = Writer() + writer.node_handlers[MyNode] = write_my_node + convert_templates('/path/to/output/folder', writer=writer) + + Here is an example hos to automatically translate your django + variables to jinja2:: + + import re + # List of tuple (Match pattern, Replace pattern, Exclusion pattern) + + var_re = ((re.compile(r"(u|user)\.is_authenticated"), r"\1.is_authenticated()", None), + (re.compile(r"\.non_field_errors"), r".non_field_errors()", None), + (re.compile(r"\.label_tag"), r".label_tag()", None), + (re.compile(r"\.as_dl"), r".as_dl()", None), + (re.compile(r"\.as_table"), r".as_table()", None), + (re.compile(r"\.as_widget"), r".as_widget()", None), + (re.compile(r"\.as_hidden"), r".as_hidden()", None), + + (re.compile(r"\.get_([0-9_\w]+)_url"), r".get_\1_url()", None), + (re.compile(r"\.url"), r".url()", re.compile(r"(form|calendar).url")), + (re.compile(r"\.get_([0-9_\w]+)_display"), r".get_\1_display()", None), + (re.compile(r"loop\.counter"), r"loop.index", None), + (re.compile(r"loop\.revcounter"), r"loop.revindex", None), + (re.compile(r"request\.GET\.([0-9_\w]+)"), r"request.GET.get('\1', '')", None), + (re.compile(r"request\.get_host"), r"request.get_host()", None), + + (re.compile(r"\.all(?!_)"), r".all()", None), + (re.compile(r"\.all\.0"), r".all()[0]", None), + (re.compile(r"\.([0-9])($|\s+)"), r"[\1]\2", None), + (re.compile(r"\.items"), r".items()", None), + ) + writer = Writer(var_re=var_re) + + For details about the writing process have a look at the module code. + + :copyright: (c) 2009 by the Jinja Team. + :license: BSD. +""" +import re +import os +import sys +from jinja2.defaults import * +from django.conf import settings +from django.template import defaulttags as core_tags, loader, TextNode, \ + FilterExpression, libraries, Variable, loader_tags, TOKEN_TEXT, \ + TOKEN_VAR +from django.template.debug import DebugVariableNode as VariableNode +from django.templatetags import i18n as i18n_tags +from StringIO import StringIO + + +_node_handlers = {} +_resolved_filters = {} +_newline_re = re.compile(r'(?:\r\n|\r|\n)') + + +# Django stores an itertools object on the cycle node. Not only is this +# thread unsafe but also a problem for the converter which needs the raw +# string values passed to the constructor to create a jinja loop.cycle() +# call from it. +_old_cycle_init = core_tags.CycleNode.__init__ +def _fixed_cycle_init(self, cyclevars, variable_name=None): + self.raw_cycle_vars = map(Variable, cyclevars) + _old_cycle_init(self, cyclevars, variable_name) +core_tags.CycleNode.__init__ = _fixed_cycle_init + + +def node(cls): + def proxy(f): + _node_handlers[cls] = f + return f + return proxy + + +def convert_templates(output_dir, extensions=('.html', '.txt'), writer=None, + callback=None): + """Iterates over all templates in the template dirs configured and + translates them and writes the new templates into the output directory. + """ + if writer is None: + writer = Writer() + + def filter_templates(files): + for filename in files: + ifilename = filename.lower() + for extension in extensions: + if ifilename.endswith(extension): + yield filename + + def translate(f, loadname): + template = loader.get_template(loadname) + original = writer.stream + writer.stream = f + writer.body(template.nodelist) + writer.stream = original + + if callback is None: + def callback(template): + print template + + for directory in settings.TEMPLATE_DIRS: + for dirname, _, files in os.walk(directory): + dirname = dirname[len(directory) + 1:] + for filename in filter_templates(files): + source = os.path.normpath(os.path.join(dirname, filename)) + target = os.path.join(output_dir, dirname, filename) + basetarget = os.path.dirname(target) + if not os.path.exists(basetarget): + os.makedirs(basetarget) + callback(source) + f = file(target, 'w') + try: + translate(f, source) + finally: + f.close() + + +class Writer(object): + """The core writer class.""" + + def __init__(self, stream=None, error_stream=None, + block_start_string=BLOCK_START_STRING, + block_end_string=BLOCK_END_STRING, + variable_start_string=VARIABLE_START_STRING, + variable_end_string=VARIABLE_END_STRING, + comment_start_string=COMMENT_START_STRING, + comment_end_string=COMMENT_END_STRING, + initial_autoescape=True, + use_jinja_autoescape=False, + custom_node_handlers=None, + var_re=[], + env=None): + if stream is None: + stream = sys.stdout + if error_stream is None: + error_stream = sys.stderr + self.stream = stream + self.error_stream = error_stream + self.block_start_string = block_start_string + self.block_end_string = block_end_string + self.variable_start_string = variable_start_string + self.variable_end_string = variable_end_string + self.comment_start_string = comment_start_string + self.comment_end_string = comment_end_string + self.autoescape = initial_autoescape + self.spaceless = False + self.use_jinja_autoescape = use_jinja_autoescape + self.node_handlers = dict(_node_handlers, + **(custom_node_handlers or {})) + self._loop_depth = 0 + self._filters_warned = set() + self.var_re = var_re + self.env = env + + def enter_loop(self): + """Increments the loop depth so that write functions know if they + are in a loop. + """ + self._loop_depth += 1 + + def leave_loop(self): + """Reverse of enter_loop.""" + self._loop_depth -= 1 + + @property + def in_loop(self): + """True if we are in a loop.""" + return self._loop_depth > 0 + + def write(self, s): + """Writes stuff to the stream.""" + self.stream.write(s.encode(settings.FILE_CHARSET)) + + def print_expr(self, expr): + """Open a variable tag, write to the string to the stream and close.""" + self.start_variable() + self.write(expr) + self.end_variable() + + def _post_open(self): + if self.spaceless: + self.write('- ') + else: + self.write(' ') + + def _pre_close(self): + if self.spaceless: + self.write(' -') + else: + self.write(' ') + + def start_variable(self): + """Start a variable.""" + self.write(self.variable_start_string) + self._post_open() + + def end_variable(self, always_safe=False): + """End a variable.""" + if not always_safe and self.autoescape and \ + not self.use_jinja_autoescape: + self.write('|e') + self._pre_close() + self.write(self.variable_end_string) + + def start_block(self): + """Starts a block.""" + self.write(self.block_start_string) + self._post_open() + + def end_block(self): + """Ends a block.""" + self._pre_close() + self.write(self.block_end_string) + + def tag(self, name): + """Like `print_expr` just for blocks.""" + self.start_block() + self.write(name) + self.end_block() + + def variable(self, name): + """Prints a variable. This performs variable name transformation.""" + self.write(self.translate_variable_name(name)) + + def literal(self, value): + """Writes a value as literal.""" + value = repr(value) + if value[:2] in ('u"', "u'"): + value = value[1:] + self.write(value) + + def filters(self, filters, is_block=False): + """Dumps a list of filters.""" + want_pipe = not is_block + for filter, args in filters: + name = self.get_filter_name(filter) + if name is None: + self.warn('Could not find filter %s' % name) + continue + if name not in DEFAULT_FILTERS and \ + name not in self._filters_warned: + self._filters_warned.add(name) + self.warn('Filter %s probably doesn\'t exist in Jinja' % + name) + if not want_pipe: + want_pipe = True + else: + self.write('|') + self.write(name) + if args: + self.write('(') + for idx, (is_var, value) in enumerate(args): + if idx: + self.write(', ') + if is_var: + self.node(value) + else: + self.literal(value) + self.write(')') + + def get_location(self, origin, position): + """Returns the location for an origin and position tuple as name + and lineno. + """ + if hasattr(origin, 'source'): + source = origin.source + name = '<unknown source>' + else: + source = origin.loader(origin.loadname, origin.dirs)[0] + name = origin.loadname + lineno = len(_newline_re.findall(source[:position[0]])) + 1 + return name, lineno + + def warn(self, message, node=None): + """Prints a warning to the error stream.""" + if node is not None and hasattr(node, 'source'): + filename, lineno = self.get_location(*node.source) + message = '[%s:%d] %s' % (filename, lineno, message) + print >> self.error_stream, message + + def translate_variable_name(self, var): + """Performs variable name translation.""" + if self.in_loop and var == 'forloop' or var.startswith('forloop.'): + var = var[3:] + + for reg, rep, unless in self.var_re: + no_unless = unless and unless.search(var) or True + if reg.search(var) and no_unless: + var = reg.sub(rep, var) + break + return var + + def get_filter_name(self, filter): + """Returns the filter name for a filter function or `None` if there + is no such filter. + """ + if filter not in _resolved_filters: + for library in libraries.values(): + for key, value in library.filters.iteritems(): + _resolved_filters[value] = key + return _resolved_filters.get(filter, None) + + def node(self, node): + """Invokes the node handler for a node.""" + for cls, handler in self.node_handlers.iteritems(): + if type(node) is cls or type(node).__name__ == cls: + handler(self, node) + break + else: + self.warn('Untranslatable node %s.%s found' % ( + node.__module__, + node.__class__.__name__ + ), node) + + def body(self, nodes): + """Calls node() for every node in the iterable passed.""" + for node in nodes: + self.node(node) + + +@node(TextNode) +def text_node(writer, node): + writer.write(node.s) + + +@node(Variable) +def variable(writer, node): + if node.translate: + writer.warn('i18n system used, make sure to install translations', node) + writer.write('_(') + if node.literal is not None: + writer.literal(node.literal) + else: + writer.variable(node.var) + if node.translate: + writer.write(')') + + +@node(VariableNode) +def variable_node(writer, node): + writer.start_variable() + if node.filter_expression.var.var == 'block.super' \ + and not node.filter_expression.filters: + writer.write('super()') + else: + writer.node(node.filter_expression) + writer.end_variable() + + +@node(FilterExpression) +def filter_expression(writer, node): + writer.node(node.var) + writer.filters(node.filters) + + +@node(core_tags.CommentNode) +def comment_tag(writer, node): + pass + + +@node(core_tags.DebugNode) +def comment_tag(writer, node): + writer.warn('Debug tag detected. Make sure to add a global function ' + 'called debug to the namespace.', node=node) + writer.print_expr('debug()') + + +@node(core_tags.ForNode) +def for_loop(writer, node): + writer.start_block() + writer.write('for ') + for idx, var in enumerate(node.loopvars): + if idx: + writer.write(', ') + writer.variable(var) + writer.write(' in ') + if node.is_reversed: + writer.write('(') + writer.node(node.sequence) + if node.is_reversed: + writer.write(')|reverse') + writer.end_block() + writer.enter_loop() + writer.body(node.nodelist_loop) + writer.leave_loop() + writer.tag('endfor') + + +@node(core_tags.IfNode) +def if_condition(writer, node): + writer.start_block() + writer.write('if ') + join_with = 'and' + if node.link_type == core_tags.IfNode.LinkTypes.or_: + join_with = 'or' + + for idx, (ifnot, expr) in enumerate(node.bool_exprs): + if idx: + writer.write(' %s ' % join_with) + if ifnot: + writer.write('not ') + writer.node(expr) + writer.end_block() + writer.body(node.nodelist_true) + if node.nodelist_false: + writer.tag('else') + writer.body(node.nodelist_false) + writer.tag('endif') + + +@node(core_tags.IfEqualNode) +def if_equal(writer, node): + writer.start_block() + writer.write('if ') + writer.node(node.var1) + if node.negate: + writer.write(' != ') + else: + writer.write(' == ') + writer.node(node.var2) + writer.end_block() + writer.body(node.nodelist_true) + if node.nodelist_false: + writer.tag('else') + writer.body(node.nodelist_false) + writer.tag('endif') + + +@node(loader_tags.BlockNode) +def block(writer, node): + writer.tag('block ' + node.name.replace('-', '_').rstrip('_')) + node = node + while node.parent is not None: + node = node.parent + writer.body(node.nodelist) + writer.tag('endblock') + + +@node(loader_tags.ExtendsNode) +def extends(writer, node): + writer.start_block() + writer.write('extends ') + if node.parent_name_expr: + writer.node(node.parent_name_expr) + else: + writer.literal(node.parent_name) + writer.end_block() + writer.body(node.nodelist) + + +@node(loader_tags.ConstantIncludeNode) +@node(loader_tags.IncludeNode) +def include(writer, node): + writer.start_block() + writer.write('include ') + if hasattr(node, 'template'): + writer.literal(node.template.name) + else: + writer.node(node.template_name) + writer.end_block() + + +@node(core_tags.CycleNode) +def cycle(writer, node): + if not writer.in_loop: + writer.warn('Untranslatable free cycle (cycle outside loop)', node=node) + return + if node.variable_name is not None: + writer.start_block() + writer.write('set %s = ' % node.variable_name) + else: + writer.start_variable() + writer.write('loop.cycle(') + for idx, var in enumerate(node.raw_cycle_vars): + if idx: + writer.write(', ') + writer.node(var) + writer.write(')') + if node.variable_name is not None: + writer.end_block() + else: + writer.end_variable() + + +@node(core_tags.FilterNode) +def filter(writer, node): + writer.start_block() + writer.write('filter ') + writer.filters(node.filter_expr.filters, True) + writer.end_block() + writer.body(node.nodelist) + writer.tag('endfilter') + + +@node(core_tags.AutoEscapeControlNode) +def autoescape_control(writer, node): + original = writer.autoescape + writer.autoescape = node.setting + writer.body(node.nodelist) + writer.autoescape = original + + +@node(core_tags.SpacelessNode) +def spaceless(writer, node): + original = writer.spaceless + writer.spaceless = True + writer.warn('entering spaceless mode with different semantics', node) + # do the initial stripping + nodelist = list(node.nodelist) + if nodelist: + if isinstance(nodelist[0], TextNode): + nodelist[0] = TextNode(nodelist[0].s.lstrip()) + if isinstance(nodelist[-1], TextNode): + nodelist[-1] = TextNode(nodelist[-1].s.rstrip()) + writer.body(nodelist) + writer.spaceless = original + + +@node(core_tags.TemplateTagNode) +def template_tag(writer, node): + tag = { + 'openblock': writer.block_start_string, + 'closeblock': writer.block_end_string, + 'openvariable': writer.variable_start_string, + 'closevariable': writer.variable_end_string, + 'opencomment': writer.comment_start_string, + 'closecomment': writer.comment_end_string, + 'openbrace': '{', + 'closebrace': '}' + }.get(node.tagtype) + if tag: + writer.start_variable() + writer.literal(tag) + writer.end_variable() + + +@node(core_tags.URLNode) +def url_tag(writer, node): + writer.warn('url node used. make sure to provide a proper url() ' + 'function', node) + if node.asvar: + writer.start_block() + writer.write('set %s = ' % node.asvar) + else: + writer.start_variable() + autoescape = writer.autoescape + writer.write('url(') + writer.literal(node.view_name) + for arg in node.args: + writer.write(', ') + writer.node(arg) + for key, arg in node.kwargs.items(): + writer.write(', %s=' % key) + writer.node(arg) + writer.write(')') + if node.asvar: + writer.end_block() + else: + writer.end_variable() + + +@node(core_tags.WidthRatioNode) +def width_ratio(writer, node): + writer.warn('widthratio expanded into formula. You may want to provide ' + 'a helper function for this calculation', node) + writer.start_variable() + writer.write('(') + writer.node(node.val_expr) + writer.write(' / ') + writer.node(node.max_expr) + writer.write(' * ') + writer.write(str(int(node.max_width))) + writer.write(')|round|int') + writer.end_variable(always_safe=True) + + +@node(core_tags.WithNode) +def with_block(writer, node): + writer.warn('with block expanded into set statement. This could cause ' + 'variables following that block to be overridden.', node) + writer.start_block() + writer.write('set %s = ' % node.name) + writer.node(node.var) + writer.end_block() + writer.body(node.nodelist) + + +@node(core_tags.RegroupNode) +def regroup(writer, node): + if node.expression.var.literal: + writer.warn('literal in groupby filter used. Behavior in that ' + 'situation is undefined and translation is skipped.', node) + return + elif node.expression.filters: + writer.warn('filters in groupby filter used. Behavior in that ' + 'situation is undefined which is most likely a bug ' + 'in your code. Filters were ignored.', node) + writer.start_block() + writer.write('set %s = ' % node.var_name) + writer.node(node.target) + writer.write('|groupby(') + writer.literal(node.expression.var.var) + writer.write(')') + writer.end_block() + + +@node(core_tags.LoadNode) +def warn_load(writer, node): + writer.warn('load statement used which was ignored on conversion', node) + + +@node(i18n_tags.GetAvailableLanguagesNode) +def get_available_languages(writer, node): + writer.warn('make sure to provide a get_available_languages function', node) + writer.tag('set %s = get_available_languages()' % + writer.translate_variable_name(node.variable)) + + +@node(i18n_tags.GetCurrentLanguageNode) +def get_current_language(writer, node): + writer.warn('make sure to provide a get_current_language function', node) + writer.tag('set %s = get_current_language()' % + writer.translate_variable_name(node.variable)) + + +@node(i18n_tags.GetCurrentLanguageBidiNode) +def get_current_language_bidi(writer, node): + writer.warn('make sure to provide a get_current_language_bidi function', node) + writer.tag('set %s = get_current_language_bidi()' % + writer.translate_variable_name(node.variable)) + + +@node(i18n_tags.TranslateNode) +def simple_gettext(writer, node): + writer.warn('i18n system used, make sure to install translations', node) + writer.start_variable() + writer.write('_(') + writer.node(node.value) + writer.write(')') + writer.end_variable() + + +@node(i18n_tags.BlockTranslateNode) +def translate_block(writer, node): + first_var = [] + variables = set() + + def touch_var(name): + variables.add(name) + if not first_var: + first_var.append(name) + + def dump_token_list(tokens): + for token in tokens: + if token.token_type == TOKEN_TEXT: + writer.write(token.contents) + elif token.token_type == TOKEN_VAR: + writer.print_expr(token.contents) + touch_var(token.contents) + + writer.warn('i18n system used, make sure to install translations', node) + writer.start_block() + writer.write('trans') + idx = -1 + for idx, (key, var) in enumerate(node.extra_context.items()): + if idx: + writer.write(',') + writer.write(' %s=' % key) + touch_var(key) + writer.node(var.filter_expression) + + have_plural = False + plural_var = None + if node.plural and node.countervar and node.counter: + have_plural = True + plural_var = node.countervar + if plural_var not in variables: + if idx > -1: + writer.write(',') + touch_var(plural_var) + writer.write(' %s=' % plural_var) + writer.node(node.counter) + + writer.end_block() + dump_token_list(node.singular) + if node.plural and node.countervar and node.counter: + writer.start_block() + writer.write('pluralize') + if node.countervar != first_var[0]: + writer.write(' ' + node.countervar) + writer.end_block() + dump_token_list(node.plural) + writer.tag('endtrans') + +@node("SimpleNode") +def simple_tag(writer, node): + """Check if the simple tag exist as a filter in """ + name = node.tag_name + if writer.env and \ + name not in writer.env.filters and \ + name not in writer._filters_warned: + writer._filters_warned.add(name) + writer.warn('Filter %s probably doesn\'t exist in Jinja' % + name) + + if not node.vars_to_resolve: + # No argument, pass the request + writer.start_variable() + writer.write('request|') + writer.write(name) + writer.end_variable() + return + + first_var = node.vars_to_resolve[0] + args = node.vars_to_resolve[1:] + writer.start_variable() + + # Copied from Writer.filters() + writer.node(first_var) + + writer.write('|') + writer.write(name) + if args: + writer.write('(') + for idx, var in enumerate(args): + if idx: + writer.write(', ') + if var.var: + writer.node(var) + else: + writer.literal(var.literal) + writer.write(')') + writer.end_variable() + +# get rid of node now, it shouldn't be used normally +del node diff --git a/deps/v8_inspector/deps/jinja2/ext/django2jinja/example.py b/deps/v8_inspector/deps/jinja2/ext/django2jinja/example.py new file mode 100644 index 00000000000000..2d4ab9add849a8 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/ext/django2jinja/example.py @@ -0,0 +1,7 @@ +from django.conf import settings +settings.configure(TEMPLATE_DIRS=['templates'], TEMPLATE_DEBUG=True) + +from django2jinja import convert_templates, Writer + +writer = Writer(use_jinja_autoescape=True) +convert_templates('converted', writer=writer) diff --git a/deps/v8_inspector/deps/jinja2/ext/django2jinja/templates/index.html b/deps/v8_inspector/deps/jinja2/ext/django2jinja/templates/index.html new file mode 100644 index 00000000000000..d0fbe3854d4bb6 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/ext/django2jinja/templates/index.html @@ -0,0 +1,58 @@ +{% extends "layout.html" %} +{% load i18n %} +{% block title %}Foo{% endblock %} +{% block page-body %} + {{ block.super }} + Hello {{ name|cut:"d" }}! + + {% for item in seq reversed %} + {% if forloop.index|divisibleby:2 %} + <li class="{% cycle 'a' 'b' %}">{{ item }}</li> + {% endif %} + {% endfor %} + {% ifequal foo bar %} + haha + {% else %} + hmm + {% endifequal %} + {% filter upper %} + {% include "subtemplate.html" %} + {% include foo %} + {% endfilter %} + {% spaceless %} + Hello World + {{ foo }} + Hmm + {% endspaceless %} + {% templatetag opencomment %}...{% templatetag closecomment %} + {% url foo a, b, c=d %} + {% url foo a, b, c=d as hmm %} + + {% with object.value as value %} + <img src='bar.gif' height='10' width='{% widthratio value 200 100 %}'> + {% endwith %} + + <pre>{% debug %}</pre> + + {% blocktrans with book|title as book_t and author|title as author_t %} + This is {{ book_t }} by {{ author_t }} + {% endblocktrans %} + + {% blocktrans count list|length as counter %} + There is only one {{ name }} object. + {% plural %} + There are {{ counter }} {{ name }} objects. + {% endblocktrans %} + + {% blocktrans with name|escape as name count list|length as counter %} + There is only one {{ name }} object. + {% plural %} + There are {{ counter }} {{ name }} objects. + {% endblocktrans %} + + {% blocktrans %}This string will have {{ value }} inside.{% endblocktrans %} + + <p>{% trans "This is the title." %}</p> + + {% regroup people by gender as grouped %} +{% endblock %} diff --git a/deps/v8_inspector/deps/jinja2/ext/django2jinja/templates/layout.html b/deps/v8_inspector/deps/jinja2/ext/django2jinja/templates/layout.html new file mode 100644 index 00000000000000..3f21a122df85e4 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/ext/django2jinja/templates/layout.html @@ -0,0 +1,4 @@ +<title>{% block title %}{% endblock %}</title> +<div class="body"> + {% block page-body %}{% endblock %} +</div> diff --git a/deps/v8_inspector/deps/jinja2/ext/django2jinja/templates/subtemplate.html b/deps/v8_inspector/deps/jinja2/ext/django2jinja/templates/subtemplate.html new file mode 100644 index 00000000000000..980a0d5f19a64b --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/ext/django2jinja/templates/subtemplate.html @@ -0,0 +1 @@ +Hello World! diff --git a/deps/v8_inspector/deps/jinja2/ext/djangojinja2.py b/deps/v8_inspector/deps/jinja2/ext/djangojinja2.py new file mode 100644 index 00000000000000..c7b4884d725a36 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/ext/djangojinja2.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +""" + djangojinja2 + ~~~~~~~~~~~~ + + Adds support for Jinja2 to Django. + + Configuration variables: + + ======================= ============================================= + Key Description + ======================= ============================================= + `JINJA2_TEMPLATE_DIRS` List of template folders + `JINJA2_EXTENSIONS` List of Jinja2 extensions to use + `JINJA2_CACHE_SIZE` The size of the Jinja2 template cache. + ======================= ============================================= + + :copyright: (c) 2009 by the Jinja Team. + :license: BSD. +""" +from itertools import chain +from django.conf import settings +from django.http import HttpResponse +from django.core.exceptions import ImproperlyConfigured +from django.template.context import get_standard_processors +from django.template import TemplateDoesNotExist +from jinja2 import Environment, FileSystemLoader, TemplateNotFound +from jinja2.defaults import DEFAULT_NAMESPACE + + +# the environment is unconfigured until the first template is loaded. +_jinja_env = None + + +def get_env(): + """Get the Jinja2 env and initialize it if necessary.""" + global _jinja_env + if _jinja_env is None: + _jinja_env = create_env() + return _jinja_env + + +def create_env(): + """Create a new Jinja2 environment.""" + searchpath = list(settings.JINJA2_TEMPLATE_DIRS) + return Environment(loader=FileSystemLoader(searchpath), + auto_reload=settings.TEMPLATE_DEBUG, + cache_size=getattr(settings, 'JINJA2_CACHE_SIZE', 400), + extensions=getattr(settings, 'JINJA2_EXTENSIONS', ())) + + +def get_template(template_name, globals=None): + """Load a template.""" + try: + return get_env().get_template(template_name, globals=globals) + except TemplateNotFound, e: + raise TemplateDoesNotExist(str(e)) + + +def select_template(templates, globals=None): + """Try to load one of the given templates.""" + env = get_env() + for template in templates: + try: + return env.get_template(template, globals=globals) + except TemplateNotFound: + continue + raise TemplateDoesNotExist(', '.join(templates)) + + +def render_to_string(template_name, context=None, request=None, + processors=None): + """Render a template into a string.""" + context = dict(context or {}) + if request is not None: + context['request'] = request + for processor in chain(get_standard_processors(), processors or ()): + context.update(processor(request)) + return get_template(template_name).render(context) + + +def render_to_response(template_name, context=None, request=None, + processors=None, mimetype=None): + """Render a template into a response object.""" + return HttpResponse(render_to_string(template_name, context, request, + processors), mimetype=mimetype) diff --git a/deps/v8_inspector/deps/jinja2/ext/inlinegettext.py b/deps/v8_inspector/deps/jinja2/ext/inlinegettext.py new file mode 100644 index 00000000000000..cf4ed5e4418cd6 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/ext/inlinegettext.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +""" + Inline Gettext + ~~~~~~~~~~~~~~ + + An example extension for Jinja2 that supports inline gettext calls. + Requires the i18n extension to be loaded. + + :copyright: (c) 2009 by the Jinja Team. + :license: BSD. +""" +import re +from jinja2.ext import Extension +from jinja2.lexer import Token, count_newlines +from jinja2.exceptions import TemplateSyntaxError + + +_outside_re = re.compile(r'\\?(gettext|_)\(') +_inside_re = re.compile(r'\\?[()]') + + +class InlineGettext(Extension): + """This extension implements support for inline gettext blocks:: + + <h1>_(Welcome)</h1> + <p>_(This is a paragraph)</p> + + Requires the i18n extension to be loaded and configured. + """ + + def filter_stream(self, stream): + paren_stack = 0 + + for token in stream: + if token.type is not 'data': + yield token + continue + + pos = 0 + lineno = token.lineno + + while 1: + if not paren_stack: + match = _outside_re.search(token.value, pos) + else: + match = _inside_re.search(token.value, pos) + if match is None: + break + new_pos = match.start() + if new_pos > pos: + preval = token.value[pos:new_pos] + yield Token(lineno, 'data', preval) + lineno += count_newlines(preval) + gtok = match.group() + if gtok[0] == '\\': + yield Token(lineno, 'data', gtok[1:]) + elif not paren_stack: + yield Token(lineno, 'block_begin', None) + yield Token(lineno, 'name', 'trans') + yield Token(lineno, 'block_end', None) + paren_stack = 1 + else: + if gtok == '(' or paren_stack > 1: + yield Token(lineno, 'data', gtok) + paren_stack += gtok == ')' and -1 or 1 + if not paren_stack: + yield Token(lineno, 'block_begin', None) + yield Token(lineno, 'name', 'endtrans') + yield Token(lineno, 'block_end', None) + pos = match.end() + + if pos < len(token.value): + yield Token(lineno, 'data', token.value[pos:]) + + if paren_stack: + raise TemplateSyntaxError('unclosed gettext expression', + token.lineno, stream.name, + stream.filename) diff --git a/deps/v8_inspector/deps/jinja2/ext/jinja.el b/deps/v8_inspector/deps/jinja2/ext/jinja.el new file mode 100644 index 00000000000000..4cf0c72e29af1d --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/ext/jinja.el @@ -0,0 +1,128 @@ +;;; jinja.el --- Jinja mode highlighting +;; +;; Author: Georg Brandl +;; Copyright: (c) 2009 by the Jinja Team +;; Last modified: 2008-05-22 23:04 by gbr +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Commentary: +;; +;; Mostly ripped off django-mode by Lennart Borgman. +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation; either version 2, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth +;; Floor, Boston, MA 02110-1301, USA. +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Code: + +(defconst jinja-font-lock-keywords + (list +; (cons (rx "{% comment %}" (submatch (0+ anything)) +; "{% endcomment %}") (list 1 font-lock-comment-face)) + '("{# ?\\(.*?\\) ?#}" . (1 font-lock-comment-face)) + '("{%-?\\|-?%}\\|{{\\|}}" . font-lock-preprocessor-face) + '("{#\\|#}" . font-lock-comment-delimiter-face) + ;; first word in a block is a command + '("{%-?[ \t\n]*\\([a-zA-Z_]+\\)" . (1 font-lock-keyword-face)) + ;; variables + '("\\({{ ?\\)\\([^|]*?\\)\\(|.*?\\)? ?}}" . (1 font-lock-variable-name-face)) + ;; keywords and builtins + (cons (rx word-start + (or "in" "as" "recursive" "not" "and" "or" "if" "else" + "import" "with" "without" "context") + word-end) + font-lock-keyword-face) + (cons (rx word-start + (or "true" "false" "none" "loop" "self" "super") + word-end) + font-lock-builtin-face) + ;; tests + '("\\(is\\)[ \t]*\\(not\\)[ \t]*\\([a-zA-Z_]+\\)" + (1 font-lock-keyword-face) (2 font-lock-keyword-face) + (3 font-lock-function-name-face)) + ;; builtin filters + (cons (rx + "|" (* space) + (submatch + (or "abs" "batch" "capitalize" "capture" "center" "count" "default" + "dformat" "dictsort" "e" "escape" "filesizeformat" "first" + "float" "format" "getattribute" "getitem" "groupby" "indent" + "int" "join" "jsonencode" "last" "length" "lower" "markdown" + "pprint" "random" "replace" "reverse" "round" "rst" "slice" + "sort" "string" "striptags" "sum" "textile" "title" "trim" + "truncate" "upper" "urlencode" "urlize" "wordcount" "wordwrap" + "xmlattr"))) + (list 1 font-lock-builtin-face)) + ) + "Minimal highlighting expressions for Jinja mode") + +(define-derived-mode jinja-mode nil "Jinja" + "Simple Jinja mode for use with `mumamo-mode'. +This mode only provides syntax highlighting." + ;;(set (make-local-variable 'comment-start) "{#") + ;;(set (make-local-variable 'comment-end) "#}") + (setq font-lock-defaults '(jinja-font-lock-keywords))) + +;; mumamo stuff +(when (require 'mumamo nil t) + + (defun mumamo-chunk-jinja3 (pos max) + "Find {# ... #}" + (mumamo-quick-chunk-forward pos max "{#" "#}" 'borders 'jinja-mode)) + + (defun mumamo-chunk-jinja2 (pos max) + "Find {{ ... }}" + (mumamo-quick-chunk-forward pos max "{{" "}}" 'borders 'jinja-mode)) + + (defun mumamo-chunk-jinja (pos max) + "Find {% ... %}" + (mumamo-quick-chunk-forward pos max "{%" "%}" 'borders 'jinja-mode)) + +;;;###autoload + (define-mumamo-multi-major-mode jinja-html-mumamo + "Turn on multiple major modes for Jinja with main mode `html-mode'. +This also covers inlined style and javascript." + ("Jinja HTML Family" html-mode + (mumamo-chunk-jinja + mumamo-chunk-jinja2 + mumamo-chunk-jinja3 + mumamo-chunk-inlined-style + mumamo-chunk-inlined-script + mumamo-chunk-style= + mumamo-chunk-onjs= + ))) + +;;;###autoload + (define-mumamo-multi-major-mode jinja-nxhtml-mumamo + "Turn on multiple major modes for Jinja with main mode `nxhtml-mode'. +This also covers inlined style and javascript." + ("Jinja nXhtml Family" nxhtml-mode + (mumamo-chunk-jinja + mumamo-chunk-jinja2 + mumamo-chunk-jinja3 + mumamo-chunk-inlined-style + mumamo-chunk-inlined-script + mumamo-chunk-style= + mumamo-chunk-onjs= + ))) + ) + +(provide 'jinja) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; jinja.el ends here diff --git a/deps/v8_inspector/deps/jinja2/jinja2/__init__.py b/deps/v8_inspector/deps/jinja2/jinja2/__init__.py new file mode 100644 index 00000000000000..e68c28562be3af --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/__init__.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +""" + jinja2 + ~~~~~~ + + Jinja2 is a template engine written in pure Python. It provides a + Django inspired non-XML syntax but supports inline expressions and + an optional sandboxed environment. + + Nutshell + -------- + + Here a small example of a Jinja2 template:: + + {% extends 'base.html' %} + {% block title %}Memberlist{% endblock %} + {% block content %} + <ul> + {% for user in users %} + <li><a href="{{ user.url }}">{{ user.username }}</a></li> + {% endfor %} + </ul> + {% endblock %} + + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +__docformat__ = 'restructuredtext en' +__version__ = '2.9.dev' + +# high level interface +from jinja2.environment import Environment, Template + +# loaders +from jinja2.loaders import BaseLoader, FileSystemLoader, PackageLoader, \ + DictLoader, FunctionLoader, PrefixLoader, ChoiceLoader, \ + ModuleLoader + +# bytecode caches +from jinja2.bccache import BytecodeCache, FileSystemBytecodeCache, \ + MemcachedBytecodeCache + +# undefined types +from jinja2.runtime import Undefined, DebugUndefined, StrictUndefined, \ + make_logging_undefined + +# exceptions +from jinja2.exceptions import TemplateError, UndefinedError, \ + TemplateNotFound, TemplatesNotFound, TemplateSyntaxError, \ + TemplateAssertionError + +# decorators and public utilities +from jinja2.filters import environmentfilter, contextfilter, \ + evalcontextfilter +from jinja2.utils import Markup, escape, clear_caches, \ + environmentfunction, evalcontextfunction, contextfunction, \ + is_undefined + +__all__ = [ + 'Environment', 'Template', 'BaseLoader', 'FileSystemLoader', + 'PackageLoader', 'DictLoader', 'FunctionLoader', 'PrefixLoader', + 'ChoiceLoader', 'BytecodeCache', 'FileSystemBytecodeCache', + 'MemcachedBytecodeCache', 'Undefined', 'DebugUndefined', + 'StrictUndefined', 'TemplateError', 'UndefinedError', 'TemplateNotFound', + 'TemplatesNotFound', 'TemplateSyntaxError', 'TemplateAssertionError', + 'ModuleLoader', 'environmentfilter', 'contextfilter', 'Markup', 'escape', + 'environmentfunction', 'contextfunction', 'clear_caches', 'is_undefined', + 'evalcontextfilter', 'evalcontextfunction', 'make_logging_undefined', +] diff --git a/deps/v8_inspector/deps/jinja2/jinja2/_compat.py b/deps/v8_inspector/deps/jinja2/jinja2/_compat.py new file mode 100644 index 00000000000000..ebe743307451c7 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/_compat.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- +""" + jinja2._compat + ~~~~~~~~~~~~~~ + + Some py2/py3 compatibility support based on a stripped down + version of six so we don't have to depend on a specific version + of it. + + :copyright: Copyright 2013 by the Jinja team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +import sys + +PY2 = sys.version_info[0] == 2 +PYPY = hasattr(sys, 'pypy_translation_info') +_identity = lambda x: x + + +if not PY2: + unichr = chr + range_type = range + text_type = str + string_types = (str,) + integer_types = (int,) + + iterkeys = lambda d: iter(d.keys()) + itervalues = lambda d: iter(d.values()) + iteritems = lambda d: iter(d.items()) + + import pickle + from io import BytesIO, StringIO + NativeStringIO = StringIO + + def reraise(tp, value, tb=None): + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + + ifilter = filter + imap = map + izip = zip + intern = sys.intern + + implements_iterator = _identity + implements_to_string = _identity + encode_filename = _identity + get_next = lambda x: x.__next__ + +else: + unichr = unichr + text_type = unicode + range_type = xrange + string_types = (str, unicode) + integer_types = (int, long) + + iterkeys = lambda d: d.iterkeys() + itervalues = lambda d: d.itervalues() + iteritems = lambda d: d.iteritems() + + import cPickle as pickle + from cStringIO import StringIO as BytesIO, StringIO + NativeStringIO = BytesIO + + exec('def reraise(tp, value, tb=None):\n raise tp, value, tb') + + from itertools import imap, izip, ifilter + intern = intern + + def implements_iterator(cls): + cls.next = cls.__next__ + del cls.__next__ + return cls + + def implements_to_string(cls): + cls.__unicode__ = cls.__str__ + cls.__str__ = lambda x: x.__unicode__().encode('utf-8') + return cls + + get_next = lambda x: x.next + + def encode_filename(filename): + if isinstance(filename, unicode): + return filename.encode('utf-8') + return filename + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a + # dummy metaclass for one level of class instantiation that replaces + # itself with the actual metaclass. + class metaclass(type): + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +try: + from urllib.parse import quote_from_bytes as url_quote +except ImportError: + from urllib import quote as url_quote diff --git a/deps/v8_inspector/deps/jinja2/jinja2/_stringdefs.py b/deps/v8_inspector/deps/jinja2/jinja2/_stringdefs.py new file mode 100644 index 00000000000000..da5830e9f19fd7 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/_stringdefs.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 -*- +""" + jinja2._stringdefs + ~~~~~~~~~~~~~~~~~~ + + Strings of all Unicode characters of a certain category. + Used for matching in Unicode-aware languages. Run to regenerate. + + Inspired by chartypes_create.py from the MoinMoin project, original + implementation from Pygments. + + :copyright: Copyright 2006-2009 by the Jinja team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from jinja2._compat import unichr + +Cc = u'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f' + +Cf = u'\xad\u0600\u0601\u0602\u0603\u06dd\u070f\u17b4\u17b5\u200b\u200c\u200d\u200e\u200f\u202a\u202b\u202c\u202d\u202e\u2060\u2061\u2062\u2063\u206a\u206b\u206c\u206d\u206e\u206f\ufeff\ufff9\ufffa\ufffb' + +Cn = u'\u0242\u0243\u0244\u0245\u0246\u0247\u0248\u0249\u024a\u024b\u024c\u024d\u024e\u024f\u0370\u0371\u0372\u0373\u0376\u0377\u0378\u0379\u037b\u037c\u037d\u037f\u0380\u0381\u0382\u0383\u038b\u038d\u03a2\u03cf\u0487\u04cf\u04fa\u04fb\u04fc\u04fd\u04fe\u04ff\u0510\u0511\u0512\u0513\u0514\u0515\u0516\u0517\u0518\u0519\u051a\u051b\u051c\u051d\u051e\u051f\u0520\u0521\u0522\u0523\u0524\u0525\u0526\u0527\u0528\u0529\u052a\u052b\u052c\u052d\u052e\u052f\u0530\u0557\u0558\u0560\u0588\u058b\u058c\u058d\u058e\u058f\u0590\u05ba\u05c8\u05c9\u05ca\u05cb\u05cc\u05cd\u05ce\u05cf\u05eb\u05ec\u05ed\u05ee\u05ef\u05f5\u05f6\u05f7\u05f8\u05f9\u05fa\u05fb\u05fc\u05fd\u05fe\u05ff\u0604\u0605\u0606\u0607\u0608\u0609\u060a\u0616\u0617\u0618\u0619\u061a\u061c\u061d\u0620\u063b\u063c\u063d\u063e\u063f\u065f\u070e\u074b\u074c\u076e\u076f\u0770\u0771\u0772\u0773\u0774\u0775\u0776\u0777\u0778\u0779\u077a\u077b\u077c\u077d\u077e\u077f\u07b2\u07b3\u07b4\u07b5\u07b6\u07b7\u07b8\u07b9\u07ba\u07bb\u07bc\u07bd\u07be\u07bf\u07c0\u07c1\u07c2\u07c3\u07c4\u07c5\u07c6\u07c7\u07c8\u07c9\u07ca\u07cb\u07cc\u07cd\u07ce\u07cf\u07d0\u07d1\u07d2\u07d3\u07d4\u07d5\u07d6\u07d7\u07d8\u07d9\u07da\u07db\u07dc\u07dd\u07de\u07df\u07e0\u07e1\u07e2\u07e3\u07e4\u07e5\u07e6\u07e7\u07e8\u07e9\u07ea\u07eb\u07ec\u07ed\u07ee\u07ef\u07f0\u07f1\u07f2\u07f3\u07f4\u07f5\u07f6\u07f7\u07f8\u07f9\u07fa\u07fb\u07fc\u07fd\u07fe\u07ff\u0800\u0801\u0802\u0803\u0804\u0805\u0806\u0807\u0808\u0809\u080a\u080b\u080c\u080d\u080e\u080f\u0810\u0811\u0812\u0813\u0814\u0815\u0816\u0817\u0818\u0819\u081a\u081b\u081c\u081d\u081e\u081f\u0820\u0821\u0822\u0823\u0824\u0825\u0826\u0827\u0828\u0829\u082a\u082b\u082c\u082d\u082e\u082f\u0830\u0831\u0832\u0833\u0834\u0835\u0836\u0837\u0838\u0839\u083a\u083b\u083c\u083d\u083e\u083f\u0840\u0841\u0842\u0843\u0844\u0845\u0846\u0847\u0848\u0849\u084a\u084b\u084c\u084d\u084e\u084f\u0850\u0851\u0852\u0853\u0854\u0855\u0856\u0857\u0858\u0859\u085a\u085b\u085c\u085d\u085e\u085f\u0860\u0861\u0862\u0863\u0864\u0865\u0866\u0867\u0868\u0869\u086a\u086b\u086c\u086d\u086e\u086f\u0870\u0871\u0872\u0873\u0874\u0875\u0876\u0877\u0878\u0879\u087a\u087b\u087c\u087d\u087e\u087f\u0880\u0881\u0882\u0883\u0884\u0885\u0886\u0887\u0888\u0889\u088a\u088b\u088c\u088d\u088e\u088f\u0890\u0891\u0892\u0893\u0894\u0895\u0896\u0897\u0898\u0899\u089a\u089b\u089c\u089d\u089e\u089f\u08a0\u08a1\u08a2\u08a3\u08a4\u08a5\u08a6\u08a7\u08a8\u08a9\u08aa\u08ab\u08ac\u08ad\u08ae\u08af\u08b0\u08b1\u08b2\u08b3\u08b4\u08b5\u08b6\u08b7\u08b8\u08b9\u08ba\u08bb\u08bc\u08bd\u08be\u08bf\u08c0\u08c1\u08c2\u08c3\u08c4\u08c5\u08c6\u08c7\u08c8\u08c9\u08ca\u08cb\u08cc\u08cd\u08ce\u08cf\u08d0\u08d1\u08d2\u08d3\u08d4\u08d5\u08d6\u08d7\u08d8\u08d9\u08da\u08db\u08dc\u08dd\u08de\u08df\u08e0\u08e1\u08e2\u08e3\u08e4\u08e5\u08e6\u08e7\u08e8\u08e9\u08ea\u08eb\u08ec\u08ed\u08ee\u08ef\u08f0\u08f1\u08f2\u08f3\u08f4\u08f5\u08f6\u08f7\u08f8\u08f9\u08fa\u08fb\u08fc\u08fd\u08fe\u08ff\u0900\u093a\u093b\u094e\u094f\u0955\u0956\u0957\u0971\u0972\u0973\u0974\u0975\u0976\u0977\u0978\u0979\u097a\u097b\u097c\u097e\u097f\u0980\u0984\u098d\u098e\u0991\u0992\u09a9\u09b1\u09b3\u09b4\u09b5\u09ba\u09bb\u09c5\u09c6\u09c9\u09ca\u09cf\u09d0\u09d1\u09d2\u09d3\u09d4\u09d5\u09d6\u09d8\u09d9\u09da\u09db\u09de\u09e4\u09e5\u09fb\u09fc\u09fd\u09fe\u09ff\u0a00\u0a04\u0a0b\u0a0c\u0a0d\u0a0e\u0a11\u0a12\u0a29\u0a31\u0a34\u0a37\u0a3a\u0a3b\u0a3d\u0a43\u0a44\u0a45\u0a46\u0a49\u0a4a\u0a4e\u0a4f\u0a50\u0a51\u0a52\u0a53\u0a54\u0a55\u0a56\u0a57\u0a58\u0a5d\u0a5f\u0a60\u0a61\u0a62\u0a63\u0a64\u0a65\u0a75\u0a76\u0a77\u0a78\u0a79\u0a7a\u0a7b\u0a7c\u0a7d\u0a7e\u0a7f\u0a80\u0a84\u0a8e\u0a92\u0aa9\u0ab1\u0ab4\u0aba\u0abb\u0ac6\u0aca\u0ace\u0acf\u0ad1\u0ad2\u0ad3\u0ad4\u0ad5\u0ad6\u0ad7\u0ad8\u0ad9\u0ada\u0adb\u0adc\u0add\u0ade\u0adf\u0ae4\u0ae5\u0af0\u0af2\u0af3\u0af4\u0af5\u0af6\u0af7\u0af8\u0af9\u0afa\u0afb\u0afc\u0afd\u0afe\u0aff\u0b00\u0b04\u0b0d\u0b0e\u0b11\u0b12\u0b29\u0b31\u0b34\u0b3a\u0b3b\u0b44\u0b45\u0b46\u0b49\u0b4a\u0b4e\u0b4f\u0b50\u0b51\u0b52\u0b53\u0b54\u0b55\u0b58\u0b59\u0b5a\u0b5b\u0b5e\u0b62\u0b63\u0b64\u0b65\u0b72\u0b73\u0b74\u0b75\u0b76\u0b77\u0b78\u0b79\u0b7a\u0b7b\u0b7c\u0b7d\u0b7e\u0b7f\u0b80\u0b81\u0b84\u0b8b\u0b8c\u0b8d\u0b91\u0b96\u0b97\u0b98\u0b9b\u0b9d\u0ba0\u0ba1\u0ba2\u0ba5\u0ba6\u0ba7\u0bab\u0bac\u0bad\u0bba\u0bbb\u0bbc\u0bbd\u0bc3\u0bc4\u0bc5\u0bc9\u0bce\u0bcf\u0bd0\u0bd1\u0bd2\u0bd3\u0bd4\u0bd5\u0bd6\u0bd8\u0bd9\u0bda\u0bdb\u0bdc\u0bdd\u0bde\u0bdf\u0be0\u0be1\u0be2\u0be3\u0be4\u0be5\u0bfb\u0bfc\u0bfd\u0bfe\u0bff\u0c00\u0c04\u0c0d\u0c11\u0c29\u0c34\u0c3a\u0c3b\u0c3c\u0c3d\u0c45\u0c49\u0c4e\u0c4f\u0c50\u0c51\u0c52\u0c53\u0c54\u0c57\u0c58\u0c59\u0c5a\u0c5b\u0c5c\u0c5d\u0c5e\u0c5f\u0c62\u0c63\u0c64\u0c65\u0c70\u0c71\u0c72\u0c73\u0c74\u0c75\u0c76\u0c77\u0c78\u0c79\u0c7a\u0c7b\u0c7c\u0c7d\u0c7e\u0c7f\u0c80\u0c81\u0c84\u0c8d\u0c91\u0ca9\u0cb4\u0cba\u0cbb\u0cc5\u0cc9\u0cce\u0ccf\u0cd0\u0cd1\u0cd2\u0cd3\u0cd4\u0cd7\u0cd8\u0cd9\u0cda\u0cdb\u0cdc\u0cdd\u0cdf\u0ce2\u0ce3\u0ce4\u0ce5\u0cf0\u0cf1\u0cf2\u0cf3\u0cf4\u0cf5\u0cf6\u0cf7\u0cf8\u0cf9\u0cfa\u0cfb\u0cfc\u0cfd\u0cfe\u0cff\u0d00\u0d01\u0d04\u0d0d\u0d11\u0d29\u0d3a\u0d3b\u0d3c\u0d3d\u0d44\u0d45\u0d49\u0d4e\u0d4f\u0d50\u0d51\u0d52\u0d53\u0d54\u0d55\u0d56\u0d58\u0d59\u0d5a\u0d5b\u0d5c\u0d5d\u0d5e\u0d5f\u0d62\u0d63\u0d64\u0d65\u0d70\u0d71\u0d72\u0d73\u0d74\u0d75\u0d76\u0d77\u0d78\u0d79\u0d7a\u0d7b\u0d7c\u0d7d\u0d7e\u0d7f\u0d80\u0d81\u0d84\u0d97\u0d98\u0d99\u0db2\u0dbc\u0dbe\u0dbf\u0dc7\u0dc8\u0dc9\u0dcb\u0dcc\u0dcd\u0dce\u0dd5\u0dd7\u0de0\u0de1\u0de2\u0de3\u0de4\u0de5\u0de6\u0de7\u0de8\u0de9\u0dea\u0deb\u0dec\u0ded\u0dee\u0def\u0df0\u0df1\u0df5\u0df6\u0df7\u0df8\u0df9\u0dfa\u0dfb\u0dfc\u0dfd\u0dfe\u0dff\u0e00\u0e3b\u0e3c\u0e3d\u0e3e\u0e5c\u0e5d\u0e5e\u0e5f\u0e60\u0e61\u0e62\u0e63\u0e64\u0e65\u0e66\u0e67\u0e68\u0e69\u0e6a\u0e6b\u0e6c\u0e6d\u0e6e\u0e6f\u0e70\u0e71\u0e72\u0e73\u0e74\u0e75\u0e76\u0e77\u0e78\u0e79\u0e7a\u0e7b\u0e7c\u0e7d\u0e7e\u0e7f\u0e80\u0e83\u0e85\u0e86\u0e89\u0e8b\u0e8c\u0e8e\u0e8f\u0e90\u0e91\u0e92\u0e93\u0e98\u0ea0\u0ea4\u0ea6\u0ea8\u0ea9\u0eac\u0eba\u0ebe\u0ebf\u0ec5\u0ec7\u0ece\u0ecf\u0eda\u0edb\u0ede\u0edf\u0ee0\u0ee1\u0ee2\u0ee3\u0ee4\u0ee5\u0ee6\u0ee7\u0ee8\u0ee9\u0eea\u0eeb\u0eec\u0eed\u0eee\u0eef\u0ef0\u0ef1\u0ef2\u0ef3\u0ef4\u0ef5\u0ef6\u0ef7\u0ef8\u0ef9\u0efa\u0efb\u0efc\u0efd\u0efe\u0eff\u0f48\u0f6b\u0f6c\u0f6d\u0f6e\u0f6f\u0f70\u0f8c\u0f8d\u0f8e\u0f8f\u0f98\u0fbd\u0fcd\u0fce\u0fd2\u0fd3\u0fd4\u0fd5\u0fd6\u0fd7\u0fd8\u0fd9\u0fda\u0fdb\u0fdc\u0fdd\u0fde\u0fdf\u0fe0\u0fe1\u0fe2\u0fe3\u0fe4\u0fe5\u0fe6\u0fe7\u0fe8\u0fe9\u0fea\u0feb\u0fec\u0fed\u0fee\u0fef\u0ff0\u0ff1\u0ff2\u0ff3\u0ff4\u0ff5\u0ff6\u0ff7\u0ff8\u0ff9\u0ffa\u0ffb\u0ffc\u0ffd\u0ffe\u0fff\u1022\u1028\u102b\u1033\u1034\u1035\u103a\u103b\u103c\u103d\u103e\u103f\u105a\u105b\u105c\u105d\u105e\u105f\u1060\u1061\u1062\u1063\u1064\u1065\u1066\u1067\u1068\u1069\u106a\u106b\u106c\u106d\u106e\u106f\u1070\u1071\u1072\u1073\u1074\u1075\u1076\u1077\u1078\u1079\u107a\u107b\u107c\u107d\u107e\u107f\u1080\u1081\u1082\u1083\u1084\u1085\u1086\u1087\u1088\u1089\u108a\u108b\u108c\u108d\u108e\u108f\u1090\u1091\u1092\u1093\u1094\u1095\u1096\u1097\u1098\u1099\u109a\u109b\u109c\u109d\u109e\u109f\u10c6\u10c7\u10c8\u10c9\u10ca\u10cb\u10cc\u10cd\u10ce\u10cf\u10fd\u10fe\u10ff\u115a\u115b\u115c\u115d\u115e\u11a3\u11a4\u11a5\u11a6\u11a7\u11fa\u11fb\u11fc\u11fd\u11fe\u11ff\u1249\u124e\u124f\u1257\u1259\u125e\u125f\u1289\u128e\u128f\u12b1\u12b6\u12b7\u12bf\u12c1\u12c6\u12c7\u12d7\u1311\u1316\u1317\u135b\u135c\u135d\u135e\u137d\u137e\u137f\u139a\u139b\u139c\u139d\u139e\u139f\u13f5\u13f6\u13f7\u13f8\u13f9\u13fa\u13fb\u13fc\u13fd\u13fe\u13ff\u1400\u1677\u1678\u1679\u167a\u167b\u167c\u167d\u167e\u167f\u169d\u169e\u169f\u16f1\u16f2\u16f3\u16f4\u16f5\u16f6\u16f7\u16f8\u16f9\u16fa\u16fb\u16fc\u16fd\u16fe\u16ff\u170d\u1715\u1716\u1717\u1718\u1719\u171a\u171b\u171c\u171d\u171e\u171f\u1737\u1738\u1739\u173a\u173b\u173c\u173d\u173e\u173f\u1754\u1755\u1756\u1757\u1758\u1759\u175a\u175b\u175c\u175d\u175e\u175f\u176d\u1771\u1774\u1775\u1776\u1777\u1778\u1779\u177a\u177b\u177c\u177d\u177e\u177f\u17de\u17df\u17ea\u17eb\u17ec\u17ed\u17ee\u17ef\u17fa\u17fb\u17fc\u17fd\u17fe\u17ff\u180f\u181a\u181b\u181c\u181d\u181e\u181f\u1878\u1879\u187a\u187b\u187c\u187d\u187e\u187f\u18aa\u18ab\u18ac\u18ad\u18ae\u18af\u18b0\u18b1\u18b2\u18b3\u18b4\u18b5\u18b6\u18b7\u18b8\u18b9\u18ba\u18bb\u18bc\u18bd\u18be\u18bf\u18c0\u18c1\u18c2\u18c3\u18c4\u18c5\u18c6\u18c7\u18c8\u18c9\u18ca\u18cb\u18cc\u18cd\u18ce\u18cf\u18d0\u18d1\u18d2\u18d3\u18d4\u18d5\u18d6\u18d7\u18d8\u18d9\u18da\u18db\u18dc\u18dd\u18de\u18df\u18e0\u18e1\u18e2\u18e3\u18e4\u18e5\u18e6\u18e7\u18e8\u18e9\u18ea\u18eb\u18ec\u18ed\u18ee\u18ef\u18f0\u18f1\u18f2\u18f3\u18f4\u18f5\u18f6\u18f7\u18f8\u18f9\u18fa\u18fb\u18fc\u18fd\u18fe\u18ff\u191d\u191e\u191f\u192c\u192d\u192e\u192f\u193c\u193d\u193e\u193f\u1941\u1942\u1943\u196e\u196f\u1975\u1976\u1977\u1978\u1979\u197a\u197b\u197c\u197d\u197e\u197f\u19aa\u19ab\u19ac\u19ad\u19ae\u19af\u19ca\u19cb\u19cc\u19cd\u19ce\u19cf\u19da\u19db\u19dc\u19dd\u1a1c\u1a1d\u1a20\u1a21\u1a22\u1a23\u1a24\u1a25\u1a26\u1a27\u1a28\u1a29\u1a2a\u1a2b\u1a2c\u1a2d\u1a2e\u1a2f\u1a30\u1a31\u1a32\u1a33\u1a34\u1a35\u1a36\u1a37\u1a38\u1a39\u1a3a\u1a3b\u1a3c\u1a3d\u1a3e\u1a3f\u1a40\u1a41\u1a42\u1a43\u1a44\u1a45\u1a46\u1a47\u1a48\u1a49\u1a4a\u1a4b\u1a4c\u1a4d\u1a4e\u1a4f\u1a50\u1a51\u1a52\u1a53\u1a54\u1a55\u1a56\u1a57\u1a58\u1a59\u1a5a\u1a5b\u1a5c\u1a5d\u1a5e\u1a5f\u1a60\u1a61\u1a62\u1a63\u1a64\u1a65\u1a66\u1a67\u1a68\u1a69\u1a6a\u1a6b\u1a6c\u1a6d\u1a6e\u1a6f\u1a70\u1a71\u1a72\u1a73\u1a74\u1a75\u1a76\u1a77\u1a78\u1a79\u1a7a\u1a7b\u1a7c\u1a7d\u1a7e\u1a7f\u1a80\u1a81\u1a82\u1a83\u1a84\u1a85\u1a86\u1a87\u1a88\u1a89\u1a8a\u1a8b\u1a8c\u1a8d\u1a8e\u1a8f\u1a90\u1a91\u1a92\u1a93\u1a94\u1a95\u1a96\u1a97\u1a98\u1a99\u1a9a\u1a9b\u1a9c\u1a9d\u1a9e\u1a9f\u1aa0\u1aa1\u1aa2\u1aa3\u1aa4\u1aa5\u1aa6\u1aa7\u1aa8\u1aa9\u1aaa\u1aab\u1aac\u1aad\u1aae\u1aaf\u1ab0\u1ab1\u1ab2\u1ab3\u1ab4\u1ab5\u1ab6\u1ab7\u1ab8\u1ab9\u1aba\u1abb\u1abc\u1abd\u1abe\u1abf\u1ac0\u1ac1\u1ac2\u1ac3\u1ac4\u1ac5\u1ac6\u1ac7\u1ac8\u1ac9\u1aca\u1acb\u1acc\u1acd\u1ace\u1acf\u1ad0\u1ad1\u1ad2\u1ad3\u1ad4\u1ad5\u1ad6\u1ad7\u1ad8\u1ad9\u1ada\u1adb\u1adc\u1add\u1ade\u1adf\u1ae0\u1ae1\u1ae2\u1ae3\u1ae4\u1ae5\u1ae6\u1ae7\u1ae8\u1ae9\u1aea\u1aeb\u1aec\u1aed\u1aee\u1aef\u1af0\u1af1\u1af2\u1af3\u1af4\u1af5\u1af6\u1af7\u1af8\u1af9\u1afa\u1afb\u1afc\u1afd\u1afe\u1aff\u1b00\u1b01\u1b02\u1b03\u1b04\u1b05\u1b06\u1b07\u1b08\u1b09\u1b0a\u1b0b\u1b0c\u1b0d\u1b0e\u1b0f\u1b10\u1b11\u1b12\u1b13\u1b14\u1b15\u1b16\u1b17\u1b18\u1b19\u1b1a\u1b1b\u1b1c\u1b1d\u1b1e\u1b1f\u1b20\u1b21\u1b22\u1b23\u1b24\u1b25\u1b26\u1b27\u1b28\u1b29\u1b2a\u1b2b\u1b2c\u1b2d\u1b2e\u1b2f\u1b30\u1b31\u1b32\u1b33\u1b34\u1b35\u1b36\u1b37\u1b38\u1b39\u1b3a\u1b3b\u1b3c\u1b3d\u1b3e\u1b3f\u1b40\u1b41\u1b42\u1b43\u1b44\u1b45\u1b46\u1b47\u1b48\u1b49\u1b4a\u1b4b\u1b4c\u1b4d\u1b4e\u1b4f\u1b50\u1b51\u1b52\u1b53\u1b54\u1b55\u1b56\u1b57\u1b58\u1b59\u1b5a\u1b5b\u1b5c\u1b5d\u1b5e\u1b5f\u1b60\u1b61\u1b62\u1b63\u1b64\u1b65\u1b66\u1b67\u1b68\u1b69\u1b6a\u1b6b\u1b6c\u1b6d\u1b6e\u1b6f\u1b70\u1b71\u1b72\u1b73\u1b74\u1b75\u1b76\u1b77\u1b78\u1b79\u1b7a\u1b7b\u1b7c\u1b7d\u1b7e\u1b7f\u1b80\u1b81\u1b82\u1b83\u1b84\u1b85\u1b86\u1b87\u1b88\u1b89\u1b8a\u1b8b\u1b8c\u1b8d\u1b8e\u1b8f\u1b90\u1b91\u1b92\u1b93\u1b94\u1b95\u1b96\u1b97\u1b98\u1b99\u1b9a\u1b9b\u1b9c\u1b9d\u1b9e\u1b9f\u1ba0\u1ba1\u1ba2\u1ba3\u1ba4\u1ba5\u1ba6\u1ba7\u1ba8\u1ba9\u1baa\u1bab\u1bac\u1bad\u1bae\u1baf\u1bb0\u1bb1\u1bb2\u1bb3\u1bb4\u1bb5\u1bb6\u1bb7\u1bb8\u1bb9\u1bba\u1bbb\u1bbc\u1bbd\u1bbe\u1bbf\u1bc0\u1bc1\u1bc2\u1bc3\u1bc4\u1bc5\u1bc6\u1bc7\u1bc8\u1bc9\u1bca\u1bcb\u1bcc\u1bcd\u1bce\u1bcf\u1bd0\u1bd1\u1bd2\u1bd3\u1bd4\u1bd5\u1bd6\u1bd7\u1bd8\u1bd9\u1bda\u1bdb\u1bdc\u1bdd\u1bde\u1bdf\u1be0\u1be1\u1be2\u1be3\u1be4\u1be5\u1be6\u1be7\u1be8\u1be9\u1bea\u1beb\u1bec\u1bed\u1bee\u1bef\u1bf0\u1bf1\u1bf2\u1bf3\u1bf4\u1bf5\u1bf6\u1bf7\u1bf8\u1bf9\u1bfa\u1bfb\u1bfc\u1bfd\u1bfe\u1bff\u1c00\u1c01\u1c02\u1c03\u1c04\u1c05\u1c06\u1c07\u1c08\u1c09\u1c0a\u1c0b\u1c0c\u1c0d\u1c0e\u1c0f\u1c10\u1c11\u1c12\u1c13\u1c14\u1c15\u1c16\u1c17\u1c18\u1c19\u1c1a\u1c1b\u1c1c\u1c1d\u1c1e\u1c1f\u1c20\u1c21\u1c22\u1c23\u1c24\u1c25\u1c26\u1c27\u1c28\u1c29\u1c2a\u1c2b\u1c2c\u1c2d\u1c2e\u1c2f\u1c30\u1c31\u1c32\u1c33\u1c34\u1c35\u1c36\u1c37\u1c38\u1c39\u1c3a\u1c3b\u1c3c\u1c3d\u1c3e\u1c3f\u1c40\u1c41\u1c42\u1c43\u1c44\u1c45\u1c46\u1c47\u1c48\u1c49\u1c4a\u1c4b\u1c4c\u1c4d\u1c4e\u1c4f\u1c50\u1c51\u1c52\u1c53\u1c54\u1c55\u1c56\u1c57\u1c58\u1c59\u1c5a\u1c5b\u1c5c\u1c5d\u1c5e\u1c5f\u1c60\u1c61\u1c62\u1c63\u1c64\u1c65\u1c66\u1c67\u1c68\u1c69\u1c6a\u1c6b\u1c6c\u1c6d\u1c6e\u1c6f\u1c70\u1c71\u1c72\u1c73\u1c74\u1c75\u1c76\u1c77\u1c78\u1c79\u1c7a\u1c7b\u1c7c\u1c7d\u1c7e\u1c7f\u1c80\u1c81\u1c82\u1c83\u1c84\u1c85\u1c86\u1c87\u1c88\u1c89\u1c8a\u1c8b\u1c8c\u1c8d\u1c8e\u1c8f\u1c90\u1c91\u1c92\u1c93\u1c94\u1c95\u1c96\u1c97\u1c98\u1c99\u1c9a\u1c9b\u1c9c\u1c9d\u1c9e\u1c9f\u1ca0\u1ca1\u1ca2\u1ca3\u1ca4\u1ca5\u1ca6\u1ca7\u1ca8\u1ca9\u1caa\u1cab\u1cac\u1cad\u1cae\u1caf\u1cb0\u1cb1\u1cb2\u1cb3\u1cb4\u1cb5\u1cb6\u1cb7\u1cb8\u1cb9\u1cba\u1cbb\u1cbc\u1cbd\u1cbe\u1cbf\u1cc0\u1cc1\u1cc2\u1cc3\u1cc4\u1cc5\u1cc6\u1cc7\u1cc8\u1cc9\u1cca\u1ccb\u1ccc\u1ccd\u1cce\u1ccf\u1cd0\u1cd1\u1cd2\u1cd3\u1cd4\u1cd5\u1cd6\u1cd7\u1cd8\u1cd9\u1cda\u1cdb\u1cdc\u1cdd\u1cde\u1cdf\u1ce0\u1ce1\u1ce2\u1ce3\u1ce4\u1ce5\u1ce6\u1ce7\u1ce8\u1ce9\u1cea\u1ceb\u1cec\u1ced\u1cee\u1cef\u1cf0\u1cf1\u1cf2\u1cf3\u1cf4\u1cf5\u1cf6\u1cf7\u1cf8\u1cf9\u1cfa\u1cfb\u1cfc\u1cfd\u1cfe\u1cff\u1dc4\u1dc5\u1dc6\u1dc7\u1dc8\u1dc9\u1dca\u1dcb\u1dcc\u1dcd\u1dce\u1dcf\u1dd0\u1dd1\u1dd2\u1dd3\u1dd4\u1dd5\u1dd6\u1dd7\u1dd8\u1dd9\u1dda\u1ddb\u1ddc\u1ddd\u1dde\u1ddf\u1de0\u1de1\u1de2\u1de3\u1de4\u1de5\u1de6\u1de7\u1de8\u1de9\u1dea\u1deb\u1dec\u1ded\u1dee\u1def\u1df0\u1df1\u1df2\u1df3\u1df4\u1df5\u1df6\u1df7\u1df8\u1df9\u1dfa\u1dfb\u1dfc\u1dfd\u1dfe\u1dff\u1e9c\u1e9d\u1e9e\u1e9f\u1efa\u1efb\u1efc\u1efd\u1efe\u1eff\u1f16\u1f17\u1f1e\u1f1f\u1f46\u1f47\u1f4e\u1f4f\u1f58\u1f5a\u1f5c\u1f5e\u1f7e\u1f7f\u1fb5\u1fc5\u1fd4\u1fd5\u1fdc\u1ff0\u1ff1\u1ff5\u1fff\u2064\u2065\u2066\u2067\u2068\u2069\u2072\u2073\u208f\u2095\u2096\u2097\u2098\u2099\u209a\u209b\u209c\u209d\u209e\u209f\u20b6\u20b7\u20b8\u20b9\u20ba\u20bb\u20bc\u20bd\u20be\u20bf\u20c0\u20c1\u20c2\u20c3\u20c4\u20c5\u20c6\u20c7\u20c8\u20c9\u20ca\u20cb\u20cc\u20cd\u20ce\u20cf\u20ec\u20ed\u20ee\u20ef\u20f0\u20f1\u20f2\u20f3\u20f4\u20f5\u20f6\u20f7\u20f8\u20f9\u20fa\u20fb\u20fc\u20fd\u20fe\u20ff\u214d\u214e\u214f\u2150\u2151\u2152\u2184\u2185\u2186\u2187\u2188\u2189\u218a\u218b\u218c\u218d\u218e\u218f\u23dc\u23dd\u23de\u23df\u23e0\u23e1\u23e2\u23e3\u23e4\u23e5\u23e6\u23e7\u23e8\u23e9\u23ea\u23eb\u23ec\u23ed\u23ee\u23ef\u23f0\u23f1\u23f2\u23f3\u23f4\u23f5\u23f6\u23f7\u23f8\u23f9\u23fa\u23fb\u23fc\u23fd\u23fe\u23ff\u2427\u2428\u2429\u242a\u242b\u242c\u242d\u242e\u242f\u2430\u2431\u2432\u2433\u2434\u2435\u2436\u2437\u2438\u2439\u243a\u243b\u243c\u243d\u243e\u243f\u244b\u244c\u244d\u244e\u244f\u2450\u2451\u2452\u2453\u2454\u2455\u2456\u2457\u2458\u2459\u245a\u245b\u245c\u245d\u245e\u245f\u269d\u269e\u269f\u26b2\u26b3\u26b4\u26b5\u26b6\u26b7\u26b8\u26b9\u26ba\u26bb\u26bc\u26bd\u26be\u26bf\u26c0\u26c1\u26c2\u26c3\u26c4\u26c5\u26c6\u26c7\u26c8\u26c9\u26ca\u26cb\u26cc\u26cd\u26ce\u26cf\u26d0\u26d1\u26d2\u26d3\u26d4\u26d5\u26d6\u26d7\u26d8\u26d9\u26da\u26db\u26dc\u26dd\u26de\u26df\u26e0\u26e1\u26e2\u26e3\u26e4\u26e5\u26e6\u26e7\u26e8\u26e9\u26ea\u26eb\u26ec\u26ed\u26ee\u26ef\u26f0\u26f1\u26f2\u26f3\u26f4\u26f5\u26f6\u26f7\u26f8\u26f9\u26fa\u26fb\u26fc\u26fd\u26fe\u26ff\u2700\u2705\u270a\u270b\u2728\u274c\u274e\u2753\u2754\u2755\u2757\u275f\u2760\u2795\u2796\u2797\u27b0\u27bf\u27c7\u27c8\u27c9\u27ca\u27cb\u27cc\u27cd\u27ce\u27cf\u27ec\u27ed\u27ee\u27ef\u2b14\u2b15\u2b16\u2b17\u2b18\u2b19\u2b1a\u2b1b\u2b1c\u2b1d\u2b1e\u2b1f\u2b20\u2b21\u2b22\u2b23\u2b24\u2b25\u2b26\u2b27\u2b28\u2b29\u2b2a\u2b2b\u2b2c\u2b2d\u2b2e\u2b2f\u2b30\u2b31\u2b32\u2b33\u2b34\u2b35\u2b36\u2b37\u2b38\u2b39\u2b3a\u2b3b\u2b3c\u2b3d\u2b3e\u2b3f\u2b40\u2b41\u2b42\u2b43\u2b44\u2b45\u2b46\u2b47\u2b48\u2b49\u2b4a\u2b4b\u2b4c\u2b4d\u2b4e\u2b4f\u2b50\u2b51\u2b52\u2b53\u2b54\u2b55\u2b56\u2b57\u2b58\u2b59\u2b5a\u2b5b\u2b5c\u2b5d\u2b5e\u2b5f\u2b60\u2b61\u2b62\u2b63\u2b64\u2b65\u2b66\u2b67\u2b68\u2b69\u2b6a\u2b6b\u2b6c\u2b6d\u2b6e\u2b6f\u2b70\u2b71\u2b72\u2b73\u2b74\u2b75\u2b76\u2b77\u2b78\u2b79\u2b7a\u2b7b\u2b7c\u2b7d\u2b7e\u2b7f\u2b80\u2b81\u2b82\u2b83\u2b84\u2b85\u2b86\u2b87\u2b88\u2b89\u2b8a\u2b8b\u2b8c\u2b8d\u2b8e\u2b8f\u2b90\u2b91\u2b92\u2b93\u2b94\u2b95\u2b96\u2b97\u2b98\u2b99\u2b9a\u2b9b\u2b9c\u2b9d\u2b9e\u2b9f\u2ba0\u2ba1\u2ba2\u2ba3\u2ba4\u2ba5\u2ba6\u2ba7\u2ba8\u2ba9\u2baa\u2bab\u2bac\u2bad\u2bae\u2baf\u2bb0\u2bb1\u2bb2\u2bb3\u2bb4\u2bb5\u2bb6\u2bb7\u2bb8\u2bb9\u2bba\u2bbb\u2bbc\u2bbd\u2bbe\u2bbf\u2bc0\u2bc1\u2bc2\u2bc3\u2bc4\u2bc5\u2bc6\u2bc7\u2bc8\u2bc9\u2bca\u2bcb\u2bcc\u2bcd\u2bce\u2bcf\u2bd0\u2bd1\u2bd2\u2bd3\u2bd4\u2bd5\u2bd6\u2bd7\u2bd8\u2bd9\u2bda\u2bdb\u2bdc\u2bdd\u2bde\u2bdf\u2be0\u2be1\u2be2\u2be3\u2be4\u2be5\u2be6\u2be7\u2be8\u2be9\u2bea\u2beb\u2bec\u2bed\u2bee\u2bef\u2bf0\u2bf1\u2bf2\u2bf3\u2bf4\u2bf5\u2bf6\u2bf7\u2bf8\u2bf9\u2bfa\u2bfb\u2bfc\u2bfd\u2bfe\u2bff\u2c2f\u2c5f\u2c60\u2c61\u2c62\u2c63\u2c64\u2c65\u2c66\u2c67\u2c68\u2c69\u2c6a\u2c6b\u2c6c\u2c6d\u2c6e\u2c6f\u2c70\u2c71\u2c72\u2c73\u2c74\u2c75\u2c76\u2c77\u2c78\u2c79\u2c7a\u2c7b\u2c7c\u2c7d\u2c7e\u2c7f\u2ceb\u2cec\u2ced\u2cee\u2cef\u2cf0\u2cf1\u2cf2\u2cf3\u2cf4\u2cf5\u2cf6\u2cf7\u2cf8\u2d26\u2d27\u2d28\u2d29\u2d2a\u2d2b\u2d2c\u2d2d\u2d2e\u2d2f\u2d66\u2d67\u2d68\u2d69\u2d6a\u2d6b\u2d6c\u2d6d\u2d6e\u2d70\u2d71\u2d72\u2d73\u2d74\u2d75\u2d76\u2d77\u2d78\u2d79\u2d7a\u2d7b\u2d7c\u2d7d\u2d7e\u2d7f\u2d97\u2d98\u2d99\u2d9a\u2d9b\u2d9c\u2d9d\u2d9e\u2d9f\u2da7\u2daf\u2db7\u2dbf\u2dc7\u2dcf\u2dd7\u2ddf\u2de0\u2de1\u2de2\u2de3\u2de4\u2de5\u2de6\u2de7\u2de8\u2de9\u2dea\u2deb\u2dec\u2ded\u2dee\u2def\u2df0\u2df1\u2df2\u2df3\u2df4\u2df5\u2df6\u2df7\u2df8\u2df9\u2dfa\u2dfb\u2dfc\u2dfd\u2dfe\u2dff\u2e18\u2e19\u2e1a\u2e1b\u2e1e\u2e1f\u2e20\u2e21\u2e22\u2e23\u2e24\u2e25\u2e26\u2e27\u2e28\u2e29\u2e2a\u2e2b\u2e2c\u2e2d\u2e2e\u2e2f\u2e30\u2e31\u2e32\u2e33\u2e34\u2e35\u2e36\u2e37\u2e38\u2e39\u2e3a\u2e3b\u2e3c\u2e3d\u2e3e\u2e3f\u2e40\u2e41\u2e42\u2e43\u2e44\u2e45\u2e46\u2e47\u2e48\u2e49\u2e4a\u2e4b\u2e4c\u2e4d\u2e4e\u2e4f\u2e50\u2e51\u2e52\u2e53\u2e54\u2e55\u2e56\u2e57\u2e58\u2e59\u2e5a\u2e5b\u2e5c\u2e5d\u2e5e\u2e5f\u2e60\u2e61\u2e62\u2e63\u2e64\u2e65\u2e66\u2e67\u2e68\u2e69\u2e6a\u2e6b\u2e6c\u2e6d\u2e6e\u2e6f\u2e70\u2e71\u2e72\u2e73\u2e74\u2e75\u2e76\u2e77\u2e78\u2e79\u2e7a\u2e7b\u2e7c\u2e7d\u2e7e\u2e7f\u2e9a\u2ef4\u2ef5\u2ef6\u2ef7\u2ef8\u2ef9\u2efa\u2efb\u2efc\u2efd\u2efe\u2eff\u2fd6\u2fd7\u2fd8\u2fd9\u2fda\u2fdb\u2fdc\u2fdd\u2fde\u2fdf\u2fe0\u2fe1\u2fe2\u2fe3\u2fe4\u2fe5\u2fe6\u2fe7\u2fe8\u2fe9\u2fea\u2feb\u2fec\u2fed\u2fee\u2fef\u2ffc\u2ffd\u2ffe\u2fff\u3040\u3097\u3098\u3100\u3101\u3102\u3103\u3104\u312d\u312e\u312f\u3130\u318f\u31b8\u31b9\u31ba\u31bb\u31bc\u31bd\u31be\u31bf\u31d0\u31d1\u31d2\u31d3\u31d4\u31d5\u31d6\u31d7\u31d8\u31d9\u31da\u31db\u31dc\u31dd\u31de\u31df\u31e0\u31e1\u31e2\u31e3\u31e4\u31e5\u31e6\u31e7\u31e8\u31e9\u31ea\u31eb\u31ec\u31ed\u31ee\u31ef\u321f\u3244\u3245\u3246\u3247\u3248\u3249\u324a\u324b\u324c\u324d\u324e\u324f\u32ff\u4db6\u4db7\u4db8\u4db9\u4dba\u4dbb\u4dbc\u4dbd\u4dbe\u4dbf\u9fbc\u9fbd\u9fbe\u9fbf\u9fc0\u9fc1\u9fc2\u9fc3\u9fc4\u9fc5\u9fc6\u9fc7\u9fc8\u9fc9\u9fca\u9fcb\u9fcc\u9fcd\u9fce\u9fcf\u9fd0\u9fd1\u9fd2\u9fd3\u9fd4\u9fd5\u9fd6\u9fd7\u9fd8\u9fd9\u9fda\u9fdb\u9fdc\u9fdd\u9fde\u9fdf\u9fe0\u9fe1\u9fe2\u9fe3\u9fe4\u9fe5\u9fe6\u9fe7\u9fe8\u9fe9\u9fea\u9feb\u9fec\u9fed\u9fee\u9fef\u9ff0\u9ff1\u9ff2\u9ff3\u9ff4\u9ff5\u9ff6\u9ff7\u9ff8\u9ff9\u9ffa\u9ffb\u9ffc\u9ffd\u9ffe\u9fff\ua48d\ua48e\ua48f\ua4c7\ua4c8\ua4c9\ua4ca\ua4cb\ua4cc\ua4cd\ua4ce\ua4cf\ua4d0\ua4d1\ua4d2\ua4d3\ua4d4\ua4d5\ua4d6\ua4d7\ua4d8\ua4d9\ua4da\ua4db\ua4dc\ua4dd\ua4de\ua4df\ua4e0\ua4e1\ua4e2\ua4e3\ua4e4\ua4e5\ua4e6\ua4e7\ua4e8\ua4e9\ua4ea\ua4eb\ua4ec\ua4ed\ua4ee\ua4ef\ua4f0\ua4f1\ua4f2\ua4f3\ua4f4\ua4f5\ua4f6\ua4f7\ua4f8\ua4f9\ua4fa\ua4fb\ua4fc\ua4fd\ua4fe\ua4ff\ua500\ua501\ua502\ua503\ua504\ua505\ua506\ua507\ua508\ua509\ua50a\ua50b\ua50c\ua50d\ua50e\ua50f\ua510\ua511\ua512\ua513\ua514\ua515\ua516\ua517\ua518\ua519\ua51a\ua51b\ua51c\ua51d\ua51e\ua51f\ua520\ua521\ua522\ua523\ua524\ua525\ua526\ua527\ua528\ua529\ua52a\ua52b\ua52c\ua52d\ua52e\ua52f\ua530\ua531\ua532\ua533\ua534\ua535\ua536\ua537\ua538\ua539\ua53a\ua53b\ua53c\ua53d\ua53e\ua53f\ua540\ua541\ua542\ua543\ua544\ua545\ua546\ua547\ua548\ua549\ua54a\ua54b\ua54c\ua54d\ua54e\ua54f\ua550\ua551\ua552\ua553\ua554\ua555\ua556\ua557\ua558\ua559\ua55a\ua55b\ua55c\ua55d\ua55e\ua55f\ua560\ua561\ua562\ua563\ua564\ua565\ua566\ua567\ua568\ua569\ua56a\ua56b\ua56c\ua56d\ua56e\ua56f\ua570\ua571\ua572\ua573\ua574\ua575\ua576\ua577\ua578\ua579\ua57a\ua57b\ua57c\ua57d\ua57e\ua57f\ua580\ua581\ua582\ua583\ua584\ua585\ua586\ua587\ua588\ua589\ua58a\ua58b\ua58c\ua58d\ua58e\ua58f\ua590\ua591\ua592\ua593\ua594\ua595\ua596\ua597\ua598\ua599\ua59a\ua59b\ua59c\ua59d\ua59e\ua59f\ua5a0\ua5a1\ua5a2\ua5a3\ua5a4\ua5a5\ua5a6\ua5a7\ua5a8\ua5a9\ua5aa\ua5ab\ua5ac\ua5ad\ua5ae\ua5af\ua5b0\ua5b1\ua5b2\ua5b3\ua5b4\ua5b5\ua5b6\ua5b7\ua5b8\ua5b9\ua5ba\ua5bb\ua5bc\ua5bd\ua5be\ua5bf\ua5c0\ua5c1\ua5c2\ua5c3\ua5c4\ua5c5\ua5c6\ua5c7\ua5c8\ua5c9\ua5ca\ua5cb\ua5cc\ua5cd\ua5ce\ua5cf\ua5d0\ua5d1\ua5d2\ua5d3\ua5d4\ua5d5\ua5d6\ua5d7\ua5d8\ua5d9\ua5da\ua5db\ua5dc\ua5dd\ua5de\ua5df\ua5e0\ua5e1\ua5e2\ua5e3\ua5e4\ua5e5\ua5e6\ua5e7\ua5e8\ua5e9\ua5ea\ua5eb\ua5ec\ua5ed\ua5ee\ua5ef\ua5f0\ua5f1\ua5f2\ua5f3\ua5f4\ua5f5\ua5f6\ua5f7\ua5f8\ua5f9\ua5fa\ua5fb\ua5fc\ua5fd\ua5fe\ua5ff\ua600\ua601\ua602\ua603\ua604\ua605\ua606\ua607\ua608\ua609\ua60a\ua60b\ua60c\ua60d\ua60e\ua60f\ua610\ua611\ua612\ua613\ua614\ua615\ua616\ua617\ua618\ua619\ua61a\ua61b\ua61c\ua61d\ua61e\ua61f\ua620\ua621\ua622\ua623\ua624\ua625\ua626\ua627\ua628\ua629\ua62a\ua62b\ua62c\ua62d\ua62e\ua62f\ua630\ua631\ua632\ua633\ua634\ua635\ua636\ua637\ua638\ua639\ua63a\ua63b\ua63c\ua63d\ua63e\ua63f\ua640\ua641\ua642\ua643\ua644\ua645\ua646\ua647\ua648\ua649\ua64a\ua64b\ua64c\ua64d\ua64e\ua64f\ua650\ua651\ua652\ua653\ua654\ua655\ua656\ua657\ua658\ua659\ua65a\ua65b\ua65c\ua65d\ua65e\ua65f\ua660\ua661\ua662\ua663\ua664\ua665\ua666\ua667\ua668\ua669\ua66a\ua66b\ua66c\ua66d\ua66e\ua66f\ua670\ua671\ua672\ua673\ua674\ua675\ua676\ua677\ua678\ua679\ua67a\ua67b\ua67c\ua67d\ua67e\ua67f\ua680\ua681\ua682\ua683\ua684\ua685\ua686\ua687\ua688\ua689\ua68a\ua68b\ua68c\ua68d\ua68e\ua68f\ua690\ua691\ua692\ua693\ua694\ua695\ua696\ua697\ua698\ua699\ua69a\ua69b\ua69c\ua69d\ua69e\ua69f\ua6a0\ua6a1\ua6a2\ua6a3\ua6a4\ua6a5\ua6a6\ua6a7\ua6a8\ua6a9\ua6aa\ua6ab\ua6ac\ua6ad\ua6ae\ua6af\ua6b0\ua6b1\ua6b2\ua6b3\ua6b4\ua6b5\ua6b6\ua6b7\ua6b8\ua6b9\ua6ba\ua6bb\ua6bc\ua6bd\ua6be\ua6bf\ua6c0\ua6c1\ua6c2\ua6c3\ua6c4\ua6c5\ua6c6\ua6c7\ua6c8\ua6c9\ua6ca\ua6cb\ua6cc\ua6cd\ua6ce\ua6cf\ua6d0\ua6d1\ua6d2\ua6d3\ua6d4\ua6d5\ua6d6\ua6d7\ua6d8\ua6d9\ua6da\ua6db\ua6dc\ua6dd\ua6de\ua6df\ua6e0\ua6e1\ua6e2\ua6e3\ua6e4\ua6e5\ua6e6\ua6e7\ua6e8\ua6e9\ua6ea\ua6eb\ua6ec\ua6ed\ua6ee\ua6ef\ua6f0\ua6f1\ua6f2\ua6f3\ua6f4\ua6f5\ua6f6\ua6f7\ua6f8\ua6f9\ua6fa\ua6fb\ua6fc\ua6fd\ua6fe\ua6ff\ua717\ua718\ua719\ua71a\ua71b\ua71c\ua71d\ua71e\ua71f\ua720\ua721\ua722\ua723\ua724\ua725\ua726\ua727\ua728\ua729\ua72a\ua72b\ua72c\ua72d\ua72e\ua72f\ua730\ua731\ua732\ua733\ua734\ua735\ua736\ua737\ua738\ua739\ua73a\ua73b\ua73c\ua73d\ua73e\ua73f\ua740\ua741\ua742\ua743\ua744\ua745\ua746\ua747\ua748\ua749\ua74a\ua74b\ua74c\ua74d\ua74e\ua74f\ua750\ua751\ua752\ua753\ua754\ua755\ua756\ua757\ua758\ua759\ua75a\ua75b\ua75c\ua75d\ua75e\ua75f\ua760\ua761\ua762\ua763\ua764\ua765\ua766\ua767\ua768\ua769\ua76a\ua76b\ua76c\ua76d\ua76e\ua76f\ua770\ua771\ua772\ua773\ua774\ua775\ua776\ua777\ua778\ua779\ua77a\ua77b\ua77c\ua77d\ua77e\ua77f\ua780\ua781\ua782\ua783\ua784\ua785\ua786\ua787\ua788\ua789\ua78a\ua78b\ua78c\ua78d\ua78e\ua78f\ua790\ua791\ua792\ua793\ua794\ua795\ua796\ua797\ua798\ua799\ua79a\ua79b\ua79c\ua79d\ua79e\ua79f\ua7a0\ua7a1\ua7a2\ua7a3\ua7a4\ua7a5\ua7a6\ua7a7\ua7a8\ua7a9\ua7aa\ua7ab\ua7ac\ua7ad\ua7ae\ua7af\ua7b0\ua7b1\ua7b2\ua7b3\ua7b4\ua7b5\ua7b6\ua7b7\ua7b8\ua7b9\ua7ba\ua7bb\ua7bc\ua7bd\ua7be\ua7bf\ua7c0\ua7c1\ua7c2\ua7c3\ua7c4\ua7c5\ua7c6\ua7c7\ua7c8\ua7c9\ua7ca\ua7cb\ua7cc\ua7cd\ua7ce\ua7cf\ua7d0\ua7d1\ua7d2\ua7d3\ua7d4\ua7d5\ua7d6\ua7d7\ua7d8\ua7d9\ua7da\ua7db\ua7dc\ua7dd\ua7de\ua7df\ua7e0\ua7e1\ua7e2\ua7e3\ua7e4\ua7e5\ua7e6\ua7e7\ua7e8\ua7e9\ua7ea\ua7eb\ua7ec\ua7ed\ua7ee\ua7ef\ua7f0\ua7f1\ua7f2\ua7f3\ua7f4\ua7f5\ua7f6\ua7f7\ua7f8\ua7f9\ua7fa\ua7fb\ua7fc\ua7fd\ua7fe\ua7ff\ua82c\ua82d\ua82e\ua82f\ua830\ua831\ua832\ua833\ua834\ua835\ua836\ua837\ua838\ua839\ua83a\ua83b\ua83c\ua83d\ua83e\ua83f\ua840\ua841\ua842\ua843\ua844\ua845\ua846\ua847\ua848\ua849\ua84a\ua84b\ua84c\ua84d\ua84e\ua84f\ua850\ua851\ua852\ua853\ua854\ua855\ua856\ua857\ua858\ua859\ua85a\ua85b\ua85c\ua85d\ua85e\ua85f\ua860\ua861\ua862\ua863\ua864\ua865\ua866\ua867\ua868\ua869\ua86a\ua86b\ua86c\ua86d\ua86e\ua86f\ua870\ua871\ua872\ua873\ua874\ua875\ua876\ua877\ua878\ua879\ua87a\ua87b\ua87c\ua87d\ua87e\ua87f\ua880\ua881\ua882\ua883\ua884\ua885\ua886\ua887\ua888\ua889\ua88a\ua88b\ua88c\ua88d\ua88e\ua88f\ua890\ua891\ua892\ua893\ua894\ua895\ua896\ua897\ua898\ua899\ua89a\ua89b\ua89c\ua89d\ua89e\ua89f\ua8a0\ua8a1\ua8a2\ua8a3\ua8a4\ua8a5\ua8a6\ua8a7\ua8a8\ua8a9\ua8aa\ua8ab\ua8ac\ua8ad\ua8ae\ua8af\ua8b0\ua8b1\ua8b2\ua8b3\ua8b4\ua8b5\ua8b6\ua8b7\ua8b8\ua8b9\ua8ba\ua8bb\ua8bc\ua8bd\ua8be\ua8bf\ua8c0\ua8c1\ua8c2\ua8c3\ua8c4\ua8c5\ua8c6\ua8c7\ua8c8\ua8c9\ua8ca\ua8cb\ua8cc\ua8cd\ua8ce\ua8cf\ua8d0\ua8d1\ua8d2\ua8d3\ua8d4\ua8d5\ua8d6\ua8d7\ua8d8\ua8d9\ua8da\ua8db\ua8dc\ua8dd\ua8de\ua8df\ua8e0\ua8e1\ua8e2\ua8e3\ua8e4\ua8e5\ua8e6\ua8e7\ua8e8\ua8e9\ua8ea\ua8eb\ua8ec\ua8ed\ua8ee\ua8ef\ua8f0\ua8f1\ua8f2\ua8f3\ua8f4\ua8f5\ua8f6\ua8f7\ua8f8\ua8f9\ua8fa\ua8fb\ua8fc\ua8fd\ua8fe\ua8ff\ua900\ua901\ua902\ua903\ua904\ua905\ua906\ua907\ua908\ua909\ua90a\ua90b\ua90c\ua90d\ua90e\ua90f\ua910\ua911\ua912\ua913\ua914\ua915\ua916\ua917\ua918\ua919\ua91a\ua91b\ua91c\ua91d\ua91e\ua91f\ua920\ua921\ua922\ua923\ua924\ua925\ua926\ua927\ua928\ua929\ua92a\ua92b\ua92c\ua92d\ua92e\ua92f\ua930\ua931\ua932\ua933\ua934\ua935\ua936\ua937\ua938\ua939\ua93a\ua93b\ua93c\ua93d\ua93e\ua93f\ua940\ua941\ua942\ua943\ua944\ua945\ua946\ua947\ua948\ua949\ua94a\ua94b\ua94c\ua94d\ua94e\ua94f\ua950\ua951\ua952\ua953\ua954\ua955\ua956\ua957\ua958\ua959\ua95a\ua95b\ua95c\ua95d\ua95e\ua95f\ua960\ua961\ua962\ua963\ua964\ua965\ua966\ua967\ua968\ua969\ua96a\ua96b\ua96c\ua96d\ua96e\ua96f\ua970\ua971\ua972\ua973\ua974\ua975\ua976\ua977\ua978\ua979\ua97a\ua97b\ua97c\ua97d\ua97e\ua97f\ua980\ua981\ua982\ua983\ua984\ua985\ua986\ua987\ua988\ua989\ua98a\ua98b\ua98c\ua98d\ua98e\ua98f\ua990\ua991\ua992\ua993\ua994\ua995\ua996\ua997\ua998\ua999\ua99a\ua99b\ua99c\ua99d\ua99e\ua99f\ua9a0\ua9a1\ua9a2\ua9a3\ua9a4\ua9a5\ua9a6\ua9a7\ua9a8\ua9a9\ua9aa\ua9ab\ua9ac\ua9ad\ua9ae\ua9af\ua9b0\ua9b1\ua9b2\ua9b3\ua9b4\ua9b5\ua9b6\ua9b7\ua9b8\ua9b9\ua9ba\ua9bb\ua9bc\ua9bd\ua9be\ua9bf\ua9c0\ua9c1\ua9c2\ua9c3\ua9c4\ua9c5\ua9c6\ua9c7\ua9c8\ua9c9\ua9ca\ua9cb\ua9cc\ua9cd\ua9ce\ua9cf\ua9d0\ua9d1\ua9d2\ua9d3\ua9d4\ua9d5\ua9d6\ua9d7\ua9d8\ua9d9\ua9da\ua9db\ua9dc\ua9dd\ua9de\ua9df\ua9e0\ua9e1\ua9e2\ua9e3\ua9e4\ua9e5\ua9e6\ua9e7\ua9e8\ua9e9\ua9ea\ua9eb\ua9ec\ua9ed\ua9ee\ua9ef\ua9f0\ua9f1\ua9f2\ua9f3\ua9f4\ua9f5\ua9f6\ua9f7\ua9f8\ua9f9\ua9fa\ua9fb\ua9fc\ua9fd\ua9fe\ua9ff\uaa00\uaa01\uaa02\uaa03\uaa04\uaa05\uaa06\uaa07\uaa08\uaa09\uaa0a\uaa0b\uaa0c\uaa0d\uaa0e\uaa0f\uaa10\uaa11\uaa12\uaa13\uaa14\uaa15\uaa16\uaa17\uaa18\uaa19\uaa1a\uaa1b\uaa1c\uaa1d\uaa1e\uaa1f\uaa20\uaa21\uaa22\uaa23\uaa24\uaa25\uaa26\uaa27\uaa28\uaa29\uaa2a\uaa2b\uaa2c\uaa2d\uaa2e\uaa2f\uaa30\uaa31\uaa32\uaa33\uaa34\uaa35\uaa36\uaa37\uaa38\uaa39\uaa3a\uaa3b\uaa3c\uaa3d\uaa3e\uaa3f\uaa40\uaa41\uaa42\uaa43\uaa44\uaa45\uaa46\uaa47\uaa48\uaa49\uaa4a\uaa4b\uaa4c\uaa4d\uaa4e\uaa4f\uaa50\uaa51\uaa52\uaa53\uaa54\uaa55\uaa56\uaa57\uaa58\uaa59\uaa5a\uaa5b\uaa5c\uaa5d\uaa5e\uaa5f\uaa60\uaa61\uaa62\uaa63\uaa64\uaa65\uaa66\uaa67\uaa68\uaa69\uaa6a\uaa6b\uaa6c\uaa6d\uaa6e\uaa6f\uaa70\uaa71\uaa72\uaa73\uaa74\uaa75\uaa76\uaa77\uaa78\uaa79\uaa7a\uaa7b\uaa7c\uaa7d\uaa7e\uaa7f\uaa80\uaa81\uaa82\uaa83\uaa84\uaa85\uaa86\uaa87\uaa88\uaa89\uaa8a\uaa8b\uaa8c\uaa8d\uaa8e\uaa8f\uaa90\uaa91\uaa92\uaa93\uaa94\uaa95\uaa96\uaa97\uaa98\uaa99\uaa9a\uaa9b\uaa9c\uaa9d\uaa9e\uaa9f\uaaa0\uaaa1\uaaa2\uaaa3\uaaa4\uaaa5\uaaa6\uaaa7\uaaa8\uaaa9\uaaaa\uaaab\uaaac\uaaad\uaaae\uaaaf\uaab0\uaab1\uaab2\uaab3\uaab4\uaab5\uaab6\uaab7\uaab8\uaab9\uaaba\uaabb\uaabc\uaabd\uaabe\uaabf\uaac0\uaac1\uaac2\uaac3\uaac4\uaac5\uaac6\uaac7\uaac8\uaac9\uaaca\uaacb\uaacc\uaacd\uaace\uaacf\uaad0\uaad1\uaad2\uaad3\uaad4\uaad5\uaad6\uaad7\uaad8\uaad9\uaada\uaadb\uaadc\uaadd\uaade\uaadf\uaae0\uaae1\uaae2\uaae3\uaae4\uaae5\uaae6\uaae7\uaae8\uaae9\uaaea\uaaeb\uaaec\uaaed\uaaee\uaaef\uaaf0\uaaf1\uaaf2\uaaf3\uaaf4\uaaf5\uaaf6\uaaf7\uaaf8\uaaf9\uaafa\uaafb\uaafc\uaafd\uaafe\uaaff\uab00\uab01\uab02\uab03\uab04\uab05\uab06\uab07\uab08\uab09\uab0a\uab0b\uab0c\uab0d\uab0e\uab0f\uab10\uab11\uab12\uab13\uab14\uab15\uab16\uab17\uab18\uab19\uab1a\uab1b\uab1c\uab1d\uab1e\uab1f\uab20\uab21\uab22\uab23\uab24\uab25\uab26\uab27\uab28\uab29\uab2a\uab2b\uab2c\uab2d\uab2e\uab2f\uab30\uab31\uab32\uab33\uab34\uab35\uab36\uab37\uab38\uab39\uab3a\uab3b\uab3c\uab3d\uab3e\uab3f\uab40\uab41\uab42\uab43\uab44\uab45\uab46\uab47\uab48\uab49\uab4a\uab4b\uab4c\uab4d\uab4e\uab4f\uab50\uab51\uab52\uab53\uab54\uab55\uab56\uab57\uab58\uab59\uab5a\uab5b\uab5c\uab5d\uab5e\uab5f\uab60\uab61\uab62\uab63\uab64\uab65\uab66\uab67\uab68\uab69\uab6a\uab6b\uab6c\uab6d\uab6e\uab6f\uab70\uab71\uab72\uab73\uab74\uab75\uab76\uab77\uab78\uab79\uab7a\uab7b\uab7c\uab7d\uab7e\uab7f\uab80\uab81\uab82\uab83\uab84\uab85\uab86\uab87\uab88\uab89\uab8a\uab8b\uab8c\uab8d\uab8e\uab8f\uab90\uab91\uab92\uab93\uab94\uab95\uab96\uab97\uab98\uab99\uab9a\uab9b\uab9c\uab9d\uab9e\uab9f\uaba0\uaba1\uaba2\uaba3\uaba4\uaba5\uaba6\uaba7\uaba8\uaba9\uabaa\uabab\uabac\uabad\uabae\uabaf\uabb0\uabb1\uabb2\uabb3\uabb4\uabb5\uabb6\uabb7\uabb8\uabb9\uabba\uabbb\uabbc\uabbd\uabbe\uabbf\uabc0\uabc1\uabc2\uabc3\uabc4\uabc5\uabc6\uabc7\uabc8\uabc9\uabca\uabcb\uabcc\uabcd\uabce\uabcf\uabd0\uabd1\uabd2\uabd3\uabd4\uabd5\uabd6\uabd7\uabd8\uabd9\uabda\uabdb\uabdc\uabdd\uabde\uabdf\uabe0\uabe1\uabe2\uabe3\uabe4\uabe5\uabe6\uabe7\uabe8\uabe9\uabea\uabeb\uabec\uabed\uabee\uabef\uabf0\uabf1\uabf2\uabf3\uabf4\uabf5\uabf6\uabf7\uabf8\uabf9\uabfa\uabfb\uabfc\uabfd\uabfe\uabff\ud7a4\ud7a5\ud7a6\ud7a7\ud7a8\ud7a9\ud7aa\ud7ab\ud7ac\ud7ad\ud7ae\ud7af\ud7b0\ud7b1\ud7b2\ud7b3\ud7b4\ud7b5\ud7b6\ud7b7\ud7b8\ud7b9\ud7ba\ud7bb\ud7bc\ud7bd\ud7be\ud7bf\ud7c0\ud7c1\ud7c2\ud7c3\ud7c4\ud7c5\ud7c6\ud7c7\ud7c8\ud7c9\ud7ca\ud7cb\ud7cc\ud7cd\ud7ce\ud7cf\ud7d0\ud7d1\ud7d2\ud7d3\ud7d4\ud7d5\ud7d6\ud7d7\ud7d8\ud7d9\ud7da\ud7db\ud7dc\ud7dd\ud7de\ud7df\ud7e0\ud7e1\ud7e2\ud7e3\ud7e4\ud7e5\ud7e6\ud7e7\ud7e8\ud7e9\ud7ea\ud7eb\ud7ec\ud7ed\ud7ee\ud7ef\ud7f0\ud7f1\ud7f2\ud7f3\ud7f4\ud7f5\ud7f6\ud7f7\ud7f8\ud7f9\ud7fa\ud7fb\ud7fc\ud7fd\ud7fe\ud7ff\ufa2e\ufa2f\ufa6b\ufa6c\ufa6d\ufa6e\ufa6f\ufada\ufadb\ufadc\ufadd\ufade\ufadf\ufae0\ufae1\ufae2\ufae3\ufae4\ufae5\ufae6\ufae7\ufae8\ufae9\ufaea\ufaeb\ufaec\ufaed\ufaee\ufaef\ufaf0\ufaf1\ufaf2\ufaf3\ufaf4\ufaf5\ufaf6\ufaf7\ufaf8\ufaf9\ufafa\ufafb\ufafc\ufafd\ufafe\ufaff\ufb07\ufb08\ufb09\ufb0a\ufb0b\ufb0c\ufb0d\ufb0e\ufb0f\ufb10\ufb11\ufb12\ufb18\ufb19\ufb1a\ufb1b\ufb1c\ufb37\ufb3d\ufb3f\ufb42\ufb45\ufbb2\ufbb3\ufbb4\ufbb5\ufbb6\ufbb7\ufbb8\ufbb9\ufbba\ufbbb\ufbbc\ufbbd\ufbbe\ufbbf\ufbc0\ufbc1\ufbc2\ufbc3\ufbc4\ufbc5\ufbc6\ufbc7\ufbc8\ufbc9\ufbca\ufbcb\ufbcc\ufbcd\ufbce\ufbcf\ufbd0\ufbd1\ufbd2\ufd40\ufd41\ufd42\ufd43\ufd44\ufd45\ufd46\ufd47\ufd48\ufd49\ufd4a\ufd4b\ufd4c\ufd4d\ufd4e\ufd4f\ufd90\ufd91\ufdc8\ufdc9\ufdca\ufdcb\ufdcc\ufdcd\ufdce\ufdcf\ufdd0\ufdd1\ufdd2\ufdd3\ufdd4\ufdd5\ufdd6\ufdd7\ufdd8\ufdd9\ufdda\ufddb\ufddc\ufddd\ufdde\ufddf\ufde0\ufde1\ufde2\ufde3\ufde4\ufde5\ufde6\ufde7\ufde8\ufde9\ufdea\ufdeb\ufdec\ufded\ufdee\ufdef\ufdfe\ufdff\ufe1a\ufe1b\ufe1c\ufe1d\ufe1e\ufe1f\ufe24\ufe25\ufe26\ufe27\ufe28\ufe29\ufe2a\ufe2b\ufe2c\ufe2d\ufe2e\ufe2f\ufe53\ufe67\ufe6c\ufe6d\ufe6e\ufe6f\ufe75\ufefd\ufefe\uff00\uffbf\uffc0\uffc1\uffc8\uffc9\uffd0\uffd1\uffd8\uffd9\uffdd\uffde\uffdf\uffe7\uffef\ufff0\ufff1\ufff2\ufff3\ufff4\ufff5\ufff6\ufff7\ufff8\ufffe' + +Co = u'\ue000\ue001\ue002\ue003\ue004\ue005\ue006\ue007\ue008\ue009\ue00a\ue00b\ue00c\ue00d\ue00e\ue00f\ue010\ue011\ue012\ue013\ue014\ue015\ue016\ue017\ue018\ue019\ue01a\ue01b\ue01c\ue01d\ue01e\ue01f\ue020\ue021\ue022\ue023\ue024\ue025\ue026\ue027\ue028\ue029\ue02a\ue02b\ue02c\ue02d\ue02e\ue02f\ue030\ue031\ue032\ue033\ue034\ue035\ue036\ue037\ue038\ue039\ue03a\ue03b\ue03c\ue03d\ue03e\ue03f\ue040\ue041\ue042\ue043\ue044\ue045\ue046\ue047\ue048\ue049\ue04a\ue04b\ue04c\ue04d\ue04e\ue04f\ue050\ue051\ue052\ue053\ue054\ue055\ue056\ue057\ue058\ue059\ue05a\ue05b\ue05c\ue05d\ue05e\ue05f\ue060\ue061\ue062\ue063\ue064\ue065\ue066\ue067\ue068\ue069\ue06a\ue06b\ue06c\ue06d\ue06e\ue06f\ue070\ue071\ue072\ue073\ue074\ue075\ue076\ue077\ue078\ue079\ue07a\ue07b\ue07c\ue07d\ue07e\ue07f\ue080\ue081\ue082\ue083\ue084\ue085\ue086\ue087\ue088\ue089\ue08a\ue08b\ue08c\ue08d\ue08e\ue08f\ue090\ue091\ue092\ue093\ue094\ue095\ue096\ue097\ue098\ue099\ue09a\ue09b\ue09c\ue09d\ue09e\ue09f\ue0a0\ue0a1\ue0a2\ue0a3\ue0a4\ue0a5\ue0a6\ue0a7\ue0a8\ue0a9\ue0aa\ue0ab\ue0ac\ue0ad\ue0ae\ue0af\ue0b0\ue0b1\ue0b2\ue0b3\ue0b4\ue0b5\ue0b6\ue0b7\ue0b8\ue0b9\ue0ba\ue0bb\ue0bc\ue0bd\ue0be\ue0bf\ue0c0\ue0c1\ue0c2\ue0c3\ue0c4\ue0c5\ue0c6\ue0c7\ue0c8\ue0c9\ue0ca\ue0cb\ue0cc\ue0cd\ue0ce\ue0cf\ue0d0\ue0d1\ue0d2\ue0d3\ue0d4\ue0d5\ue0d6\ue0d7\ue0d8\ue0d9\ue0da\ue0db\ue0dc\ue0dd\ue0de\ue0df\ue0e0\ue0e1\ue0e2\ue0e3\ue0e4\ue0e5\ue0e6\ue0e7\ue0e8\ue0e9\ue0ea\ue0eb\ue0ec\ue0ed\ue0ee\ue0ef\ue0f0\ue0f1\ue0f2\ue0f3\ue0f4\ue0f5\ue0f6\ue0f7\ue0f8\ue0f9\ue0fa\ue0fb\ue0fc\ue0fd\ue0fe\ue0ff\ue100\ue101\ue102\ue103\ue104\ue105\ue106\ue107\ue108\ue109\ue10a\ue10b\ue10c\ue10d\ue10e\ue10f\ue110\ue111\ue112\ue113\ue114\ue115\ue116\ue117\ue118\ue119\ue11a\ue11b\ue11c\ue11d\ue11e\ue11f\ue120\ue121\ue122\ue123\ue124\ue125\ue126\ue127\ue128\ue129\ue12a\ue12b\ue12c\ue12d\ue12e\ue12f\ue130\ue131\ue132\ue133\ue134\ue135\ue136\ue137\ue138\ue139\ue13a\ue13b\ue13c\ue13d\ue13e\ue13f\ue140\ue141\ue142\ue143\ue144\ue145\ue146\ue147\ue148\ue149\ue14a\ue14b\ue14c\ue14d\ue14e\ue14f\ue150\ue151\ue152\ue153\ue154\ue155\ue156\ue157\ue158\ue159\ue15a\ue15b\ue15c\ue15d\ue15e\ue15f\ue160\ue161\ue162\ue163\ue164\ue165\ue166\ue167\ue168\ue169\ue16a\ue16b\ue16c\ue16d\ue16e\ue16f\ue170\ue171\ue172\ue173\ue174\ue175\ue176\ue177\ue178\ue179\ue17a\ue17b\ue17c\ue17d\ue17e\ue17f\ue180\ue181\ue182\ue183\ue184\ue185\ue186\ue187\ue188\ue189\ue18a\ue18b\ue18c\ue18d\ue18e\ue18f\ue190\ue191\ue192\ue193\ue194\ue195\ue196\ue197\ue198\ue199\ue19a\ue19b\ue19c\ue19d\ue19e\ue19f\ue1a0\ue1a1\ue1a2\ue1a3\ue1a4\ue1a5\ue1a6\ue1a7\ue1a8\ue1a9\ue1aa\ue1ab\ue1ac\ue1ad\ue1ae\ue1af\ue1b0\ue1b1\ue1b2\ue1b3\ue1b4\ue1b5\ue1b6\ue1b7\ue1b8\ue1b9\ue1ba\ue1bb\ue1bc\ue1bd\ue1be\ue1bf\ue1c0\ue1c1\ue1c2\ue1c3\ue1c4\ue1c5\ue1c6\ue1c7\ue1c8\ue1c9\ue1ca\ue1cb\ue1cc\ue1cd\ue1ce\ue1cf\ue1d0\ue1d1\ue1d2\ue1d3\ue1d4\ue1d5\ue1d6\ue1d7\ue1d8\ue1d9\ue1da\ue1db\ue1dc\ue1dd\ue1de\ue1df\ue1e0\ue1e1\ue1e2\ue1e3\ue1e4\ue1e5\ue1e6\ue1e7\ue1e8\ue1e9\ue1ea\ue1eb\ue1ec\ue1ed\ue1ee\ue1ef\ue1f0\ue1f1\ue1f2\ue1f3\ue1f4\ue1f5\ue1f6\ue1f7\ue1f8\ue1f9\ue1fa\ue1fb\ue1fc\ue1fd\ue1fe\ue1ff\ue200\ue201\ue202\ue203\ue204\ue205\ue206\ue207\ue208\ue209\ue20a\ue20b\ue20c\ue20d\ue20e\ue20f\ue210\ue211\ue212\ue213\ue214\ue215\ue216\ue217\ue218\ue219\ue21a\ue21b\ue21c\ue21d\ue21e\ue21f\ue220\ue221\ue222\ue223\ue224\ue225\ue226\ue227\ue228\ue229\ue22a\ue22b\ue22c\ue22d\ue22e\ue22f\ue230\ue231\ue232\ue233\ue234\ue235\ue236\ue237\ue238\ue239\ue23a\ue23b\ue23c\ue23d\ue23e\ue23f\ue240\ue241\ue242\ue243\ue244\ue245\ue246\ue247\ue248\ue249\ue24a\ue24b\ue24c\ue24d\ue24e\ue24f\ue250\ue251\ue252\ue253\ue254\ue255\ue256\ue257\ue258\ue259\ue25a\ue25b\ue25c\ue25d\ue25e\ue25f\ue260\ue261\ue262\ue263\ue264\ue265\ue266\ue267\ue268\ue269\ue26a\ue26b\ue26c\ue26d\ue26e\ue26f\ue270\ue271\ue272\ue273\ue274\ue275\ue276\ue277\ue278\ue279\ue27a\ue27b\ue27c\ue27d\ue27e\ue27f\ue280\ue281\ue282\ue283\ue284\ue285\ue286\ue287\ue288\ue289\ue28a\ue28b\ue28c\ue28d\ue28e\ue28f\ue290\ue291\ue292\ue293\ue294\ue295\ue296\ue297\ue298\ue299\ue29a\ue29b\ue29c\ue29d\ue29e\ue29f\ue2a0\ue2a1\ue2a2\ue2a3\ue2a4\ue2a5\ue2a6\ue2a7\ue2a8\ue2a9\ue2aa\ue2ab\ue2ac\ue2ad\ue2ae\ue2af\ue2b0\ue2b1\ue2b2\ue2b3\ue2b4\ue2b5\ue2b6\ue2b7\ue2b8\ue2b9\ue2ba\ue2bb\ue2bc\ue2bd\ue2be\ue2bf\ue2c0\ue2c1\ue2c2\ue2c3\ue2c4\ue2c5\ue2c6\ue2c7\ue2c8\ue2c9\ue2ca\ue2cb\ue2cc\ue2cd\ue2ce\ue2cf\ue2d0\ue2d1\ue2d2\ue2d3\ue2d4\ue2d5\ue2d6\ue2d7\ue2d8\ue2d9\ue2da\ue2db\ue2dc\ue2dd\ue2de\ue2df\ue2e0\ue2e1\ue2e2\ue2e3\ue2e4\ue2e5\ue2e6\ue2e7\ue2e8\ue2e9\ue2ea\ue2eb\ue2ec\ue2ed\ue2ee\ue2ef\ue2f0\ue2f1\ue2f2\ue2f3\ue2f4\ue2f5\ue2f6\ue2f7\ue2f8\ue2f9\ue2fa\ue2fb\ue2fc\ue2fd\ue2fe\ue2ff\ue300\ue301\ue302\ue303\ue304\ue305\ue306\ue307\ue308\ue309\ue30a\ue30b\ue30c\ue30d\ue30e\ue30f\ue310\ue311\ue312\ue313\ue314\ue315\ue316\ue317\ue318\ue319\ue31a\ue31b\ue31c\ue31d\ue31e\ue31f\ue320\ue321\ue322\ue323\ue324\ue325\ue326\ue327\ue328\ue329\ue32a\ue32b\ue32c\ue32d\ue32e\ue32f\ue330\ue331\ue332\ue333\ue334\ue335\ue336\ue337\ue338\ue339\ue33a\ue33b\ue33c\ue33d\ue33e\ue33f\ue340\ue341\ue342\ue343\ue344\ue345\ue346\ue347\ue348\ue349\ue34a\ue34b\ue34c\ue34d\ue34e\ue34f\ue350\ue351\ue352\ue353\ue354\ue355\ue356\ue357\ue358\ue359\ue35a\ue35b\ue35c\ue35d\ue35e\ue35f\ue360\ue361\ue362\ue363\ue364\ue365\ue366\ue367\ue368\ue369\ue36a\ue36b\ue36c\ue36d\ue36e\ue36f\ue370\ue371\ue372\ue373\ue374\ue375\ue376\ue377\ue378\ue379\ue37a\ue37b\ue37c\ue37d\ue37e\ue37f\ue380\ue381\ue382\ue383\ue384\ue385\ue386\ue387\ue388\ue389\ue38a\ue38b\ue38c\ue38d\ue38e\ue38f\ue390\ue391\ue392\ue393\ue394\ue395\ue396\ue397\ue398\ue399\ue39a\ue39b\ue39c\ue39d\ue39e\ue39f\ue3a0\ue3a1\ue3a2\ue3a3\ue3a4\ue3a5\ue3a6\ue3a7\ue3a8\ue3a9\ue3aa\ue3ab\ue3ac\ue3ad\ue3ae\ue3af\ue3b0\ue3b1\ue3b2\ue3b3\ue3b4\ue3b5\ue3b6\ue3b7\ue3b8\ue3b9\ue3ba\ue3bb\ue3bc\ue3bd\ue3be\ue3bf\ue3c0\ue3c1\ue3c2\ue3c3\ue3c4\ue3c5\ue3c6\ue3c7\ue3c8\ue3c9\ue3ca\ue3cb\ue3cc\ue3cd\ue3ce\ue3cf\ue3d0\ue3d1\ue3d2\ue3d3\ue3d4\ue3d5\ue3d6\ue3d7\ue3d8\ue3d9\ue3da\ue3db\ue3dc\ue3dd\ue3de\ue3df\ue3e0\ue3e1\ue3e2\ue3e3\ue3e4\ue3e5\ue3e6\ue3e7\ue3e8\ue3e9\ue3ea\ue3eb\ue3ec\ue3ed\ue3ee\ue3ef\ue3f0\ue3f1\ue3f2\ue3f3\ue3f4\ue3f5\ue3f6\ue3f7\ue3f8\ue3f9\ue3fa\ue3fb\ue3fc\ue3fd\ue3fe\ue3ff\ue400\ue401\ue402\ue403\ue404\ue405\ue406\ue407\ue408\ue409\ue40a\ue40b\ue40c\ue40d\ue40e\ue40f\ue410\ue411\ue412\ue413\ue414\ue415\ue416\ue417\ue418\ue419\ue41a\ue41b\ue41c\ue41d\ue41e\ue41f\ue420\ue421\ue422\ue423\ue424\ue425\ue426\ue427\ue428\ue429\ue42a\ue42b\ue42c\ue42d\ue42e\ue42f\ue430\ue431\ue432\ue433\ue434\ue435\ue436\ue437\ue438\ue439\ue43a\ue43b\ue43c\ue43d\ue43e\ue43f\ue440\ue441\ue442\ue443\ue444\ue445\ue446\ue447\ue448\ue449\ue44a\ue44b\ue44c\ue44d\ue44e\ue44f\ue450\ue451\ue452\ue453\ue454\ue455\ue456\ue457\ue458\ue459\ue45a\ue45b\ue45c\ue45d\ue45e\ue45f\ue460\ue461\ue462\ue463\ue464\ue465\ue466\ue467\ue468\ue469\ue46a\ue46b\ue46c\ue46d\ue46e\ue46f\ue470\ue471\ue472\ue473\ue474\ue475\ue476\ue477\ue478\ue479\ue47a\ue47b\ue47c\ue47d\ue47e\ue47f\ue480\ue481\ue482\ue483\ue484\ue485\ue486\ue487\ue488\ue489\ue48a\ue48b\ue48c\ue48d\ue48e\ue48f\ue490\ue491\ue492\ue493\ue494\ue495\ue496\ue497\ue498\ue499\ue49a\ue49b\ue49c\ue49d\ue49e\ue49f\ue4a0\ue4a1\ue4a2\ue4a3\ue4a4\ue4a5\ue4a6\ue4a7\ue4a8\ue4a9\ue4aa\ue4ab\ue4ac\ue4ad\ue4ae\ue4af\ue4b0\ue4b1\ue4b2\ue4b3\ue4b4\ue4b5\ue4b6\ue4b7\ue4b8\ue4b9\ue4ba\ue4bb\ue4bc\ue4bd\ue4be\ue4bf\ue4c0\ue4c1\ue4c2\ue4c3\ue4c4\ue4c5\ue4c6\ue4c7\ue4c8\ue4c9\ue4ca\ue4cb\ue4cc\ue4cd\ue4ce\ue4cf\ue4d0\ue4d1\ue4d2\ue4d3\ue4d4\ue4d5\ue4d6\ue4d7\ue4d8\ue4d9\ue4da\ue4db\ue4dc\ue4dd\ue4de\ue4df\ue4e0\ue4e1\ue4e2\ue4e3\ue4e4\ue4e5\ue4e6\ue4e7\ue4e8\ue4e9\ue4ea\ue4eb\ue4ec\ue4ed\ue4ee\ue4ef\ue4f0\ue4f1\ue4f2\ue4f3\ue4f4\ue4f5\ue4f6\ue4f7\ue4f8\ue4f9\ue4fa\ue4fb\ue4fc\ue4fd\ue4fe\ue4ff\ue500\ue501\ue502\ue503\ue504\ue505\ue506\ue507\ue508\ue509\ue50a\ue50b\ue50c\ue50d\ue50e\ue50f\ue510\ue511\ue512\ue513\ue514\ue515\ue516\ue517\ue518\ue519\ue51a\ue51b\ue51c\ue51d\ue51e\ue51f\ue520\ue521\ue522\ue523\ue524\ue525\ue526\ue527\ue528\ue529\ue52a\ue52b\ue52c\ue52d\ue52e\ue52f\ue530\ue531\ue532\ue533\ue534\ue535\ue536\ue537\ue538\ue539\ue53a\ue53b\ue53c\ue53d\ue53e\ue53f\ue540\ue541\ue542\ue543\ue544\ue545\ue546\ue547\ue548\ue549\ue54a\ue54b\ue54c\ue54d\ue54e\ue54f\ue550\ue551\ue552\ue553\ue554\ue555\ue556\ue557\ue558\ue559\ue55a\ue55b\ue55c\ue55d\ue55e\ue55f\ue560\ue561\ue562\ue563\ue564\ue565\ue566\ue567\ue568\ue569\ue56a\ue56b\ue56c\ue56d\ue56e\ue56f\ue570\ue571\ue572\ue573\ue574\ue575\ue576\ue577\ue578\ue579\ue57a\ue57b\ue57c\ue57d\ue57e\ue57f\ue580\ue581\ue582\ue583\ue584\ue585\ue586\ue587\ue588\ue589\ue58a\ue58b\ue58c\ue58d\ue58e\ue58f\ue590\ue591\ue592\ue593\ue594\ue595\ue596\ue597\ue598\ue599\ue59a\ue59b\ue59c\ue59d\ue59e\ue59f\ue5a0\ue5a1\ue5a2\ue5a3\ue5a4\ue5a5\ue5a6\ue5a7\ue5a8\ue5a9\ue5aa\ue5ab\ue5ac\ue5ad\ue5ae\ue5af\ue5b0\ue5b1\ue5b2\ue5b3\ue5b4\ue5b5\ue5b6\ue5b7\ue5b8\ue5b9\ue5ba\ue5bb\ue5bc\ue5bd\ue5be\ue5bf\ue5c0\ue5c1\ue5c2\ue5c3\ue5c4\ue5c5\ue5c6\ue5c7\ue5c8\ue5c9\ue5ca\ue5cb\ue5cc\ue5cd\ue5ce\ue5cf\ue5d0\ue5d1\ue5d2\ue5d3\ue5d4\ue5d5\ue5d6\ue5d7\ue5d8\ue5d9\ue5da\ue5db\ue5dc\ue5dd\ue5de\ue5df\ue5e0\ue5e1\ue5e2\ue5e3\ue5e4\ue5e5\ue5e6\ue5e7\ue5e8\ue5e9\ue5ea\ue5eb\ue5ec\ue5ed\ue5ee\ue5ef\ue5f0\ue5f1\ue5f2\ue5f3\ue5f4\ue5f5\ue5f6\ue5f7\ue5f8\ue5f9\ue5fa\ue5fb\ue5fc\ue5fd\ue5fe\ue5ff\ue600\ue601\ue602\ue603\ue604\ue605\ue606\ue607\ue608\ue609\ue60a\ue60b\ue60c\ue60d\ue60e\ue60f\ue610\ue611\ue612\ue613\ue614\ue615\ue616\ue617\ue618\ue619\ue61a\ue61b\ue61c\ue61d\ue61e\ue61f\ue620\ue621\ue622\ue623\ue624\ue625\ue626\ue627\ue628\ue629\ue62a\ue62b\ue62c\ue62d\ue62e\ue62f\ue630\ue631\ue632\ue633\ue634\ue635\ue636\ue637\ue638\ue639\ue63a\ue63b\ue63c\ue63d\ue63e\ue63f\ue640\ue641\ue642\ue643\ue644\ue645\ue646\ue647\ue648\ue649\ue64a\ue64b\ue64c\ue64d\ue64e\ue64f\ue650\ue651\ue652\ue653\ue654\ue655\ue656\ue657\ue658\ue659\ue65a\ue65b\ue65c\ue65d\ue65e\ue65f\ue660\ue661\ue662\ue663\ue664\ue665\ue666\ue667\ue668\ue669\ue66a\ue66b\ue66c\ue66d\ue66e\ue66f\ue670\ue671\ue672\ue673\ue674\ue675\ue676\ue677\ue678\ue679\ue67a\ue67b\ue67c\ue67d\ue67e\ue67f\ue680\ue681\ue682\ue683\ue684\ue685\ue686\ue687\ue688\ue689\ue68a\ue68b\ue68c\ue68d\ue68e\ue68f\ue690\ue691\ue692\ue693\ue694\ue695\ue696\ue697\ue698\ue699\ue69a\ue69b\ue69c\ue69d\ue69e\ue69f\ue6a0\ue6a1\ue6a2\ue6a3\ue6a4\ue6a5\ue6a6\ue6a7\ue6a8\ue6a9\ue6aa\ue6ab\ue6ac\ue6ad\ue6ae\ue6af\ue6b0\ue6b1\ue6b2\ue6b3\ue6b4\ue6b5\ue6b6\ue6b7\ue6b8\ue6b9\ue6ba\ue6bb\ue6bc\ue6bd\ue6be\ue6bf\ue6c0\ue6c1\ue6c2\ue6c3\ue6c4\ue6c5\ue6c6\ue6c7\ue6c8\ue6c9\ue6ca\ue6cb\ue6cc\ue6cd\ue6ce\ue6cf\ue6d0\ue6d1\ue6d2\ue6d3\ue6d4\ue6d5\ue6d6\ue6d7\ue6d8\ue6d9\ue6da\ue6db\ue6dc\ue6dd\ue6de\ue6df\ue6e0\ue6e1\ue6e2\ue6e3\ue6e4\ue6e5\ue6e6\ue6e7\ue6e8\ue6e9\ue6ea\ue6eb\ue6ec\ue6ed\ue6ee\ue6ef\ue6f0\ue6f1\ue6f2\ue6f3\ue6f4\ue6f5\ue6f6\ue6f7\ue6f8\ue6f9\ue6fa\ue6fb\ue6fc\ue6fd\ue6fe\ue6ff\ue700\ue701\ue702\ue703\ue704\ue705\ue706\ue707\ue708\ue709\ue70a\ue70b\ue70c\ue70d\ue70e\ue70f\ue710\ue711\ue712\ue713\ue714\ue715\ue716\ue717\ue718\ue719\ue71a\ue71b\ue71c\ue71d\ue71e\ue71f\ue720\ue721\ue722\ue723\ue724\ue725\ue726\ue727\ue728\ue729\ue72a\ue72b\ue72c\ue72d\ue72e\ue72f\ue730\ue731\ue732\ue733\ue734\ue735\ue736\ue737\ue738\ue739\ue73a\ue73b\ue73c\ue73d\ue73e\ue73f\ue740\ue741\ue742\ue743\ue744\ue745\ue746\ue747\ue748\ue749\ue74a\ue74b\ue74c\ue74d\ue74e\ue74f\ue750\ue751\ue752\ue753\ue754\ue755\ue756\ue757\ue758\ue759\ue75a\ue75b\ue75c\ue75d\ue75e\ue75f\ue760\ue761\ue762\ue763\ue764\ue765\ue766\ue767\ue768\ue769\ue76a\ue76b\ue76c\ue76d\ue76e\ue76f\ue770\ue771\ue772\ue773\ue774\ue775\ue776\ue777\ue778\ue779\ue77a\ue77b\ue77c\ue77d\ue77e\ue77f\ue780\ue781\ue782\ue783\ue784\ue785\ue786\ue787\ue788\ue789\ue78a\ue78b\ue78c\ue78d\ue78e\ue78f\ue790\ue791\ue792\ue793\ue794\ue795\ue796\ue797\ue798\ue799\ue79a\ue79b\ue79c\ue79d\ue79e\ue79f\ue7a0\ue7a1\ue7a2\ue7a3\ue7a4\ue7a5\ue7a6\ue7a7\ue7a8\ue7a9\ue7aa\ue7ab\ue7ac\ue7ad\ue7ae\ue7af\ue7b0\ue7b1\ue7b2\ue7b3\ue7b4\ue7b5\ue7b6\ue7b7\ue7b8\ue7b9\ue7ba\ue7bb\ue7bc\ue7bd\ue7be\ue7bf\ue7c0\ue7c1\ue7c2\ue7c3\ue7c4\ue7c5\ue7c6\ue7c7\ue7c8\ue7c9\ue7ca\ue7cb\ue7cc\ue7cd\ue7ce\ue7cf\ue7d0\ue7d1\ue7d2\ue7d3\ue7d4\ue7d5\ue7d6\ue7d7\ue7d8\ue7d9\ue7da\ue7db\ue7dc\ue7dd\ue7de\ue7df\ue7e0\ue7e1\ue7e2\ue7e3\ue7e4\ue7e5\ue7e6\ue7e7\ue7e8\ue7e9\ue7ea\ue7eb\ue7ec\ue7ed\ue7ee\ue7ef\ue7f0\ue7f1\ue7f2\ue7f3\ue7f4\ue7f5\ue7f6\ue7f7\ue7f8\ue7f9\ue7fa\ue7fb\ue7fc\ue7fd\ue7fe\ue7ff\ue800\ue801\ue802\ue803\ue804\ue805\ue806\ue807\ue808\ue809\ue80a\ue80b\ue80c\ue80d\ue80e\ue80f\ue810\ue811\ue812\ue813\ue814\ue815\ue816\ue817\ue818\ue819\ue81a\ue81b\ue81c\ue81d\ue81e\ue81f\ue820\ue821\ue822\ue823\ue824\ue825\ue826\ue827\ue828\ue829\ue82a\ue82b\ue82c\ue82d\ue82e\ue82f\ue830\ue831\ue832\ue833\ue834\ue835\ue836\ue837\ue838\ue839\ue83a\ue83b\ue83c\ue83d\ue83e\ue83f\ue840\ue841\ue842\ue843\ue844\ue845\ue846\ue847\ue848\ue849\ue84a\ue84b\ue84c\ue84d\ue84e\ue84f\ue850\ue851\ue852\ue853\ue854\ue855\ue856\ue857\ue858\ue859\ue85a\ue85b\ue85c\ue85d\ue85e\ue85f\ue860\ue861\ue862\ue863\ue864\ue865\ue866\ue867\ue868\ue869\ue86a\ue86b\ue86c\ue86d\ue86e\ue86f\ue870\ue871\ue872\ue873\ue874\ue875\ue876\ue877\ue878\ue879\ue87a\ue87b\ue87c\ue87d\ue87e\ue87f\ue880\ue881\ue882\ue883\ue884\ue885\ue886\ue887\ue888\ue889\ue88a\ue88b\ue88c\ue88d\ue88e\ue88f\ue890\ue891\ue892\ue893\ue894\ue895\ue896\ue897\ue898\ue899\ue89a\ue89b\ue89c\ue89d\ue89e\ue89f\ue8a0\ue8a1\ue8a2\ue8a3\ue8a4\ue8a5\ue8a6\ue8a7\ue8a8\ue8a9\ue8aa\ue8ab\ue8ac\ue8ad\ue8ae\ue8af\ue8b0\ue8b1\ue8b2\ue8b3\ue8b4\ue8b5\ue8b6\ue8b7\ue8b8\ue8b9\ue8ba\ue8bb\ue8bc\ue8bd\ue8be\ue8bf\ue8c0\ue8c1\ue8c2\ue8c3\ue8c4\ue8c5\ue8c6\ue8c7\ue8c8\ue8c9\ue8ca\ue8cb\ue8cc\ue8cd\ue8ce\ue8cf\ue8d0\ue8d1\ue8d2\ue8d3\ue8d4\ue8d5\ue8d6\ue8d7\ue8d8\ue8d9\ue8da\ue8db\ue8dc\ue8dd\ue8de\ue8df\ue8e0\ue8e1\ue8e2\ue8e3\ue8e4\ue8e5\ue8e6\ue8e7\ue8e8\ue8e9\ue8ea\ue8eb\ue8ec\ue8ed\ue8ee\ue8ef\ue8f0\ue8f1\ue8f2\ue8f3\ue8f4\ue8f5\ue8f6\ue8f7\ue8f8\ue8f9\ue8fa\ue8fb\ue8fc\ue8fd\ue8fe\ue8ff\ue900\ue901\ue902\ue903\ue904\ue905\ue906\ue907\ue908\ue909\ue90a\ue90b\ue90c\ue90d\ue90e\ue90f\ue910\ue911\ue912\ue913\ue914\ue915\ue916\ue917\ue918\ue919\ue91a\ue91b\ue91c\ue91d\ue91e\ue91f\ue920\ue921\ue922\ue923\ue924\ue925\ue926\ue927\ue928\ue929\ue92a\ue92b\ue92c\ue92d\ue92e\ue92f\ue930\ue931\ue932\ue933\ue934\ue935\ue936\ue937\ue938\ue939\ue93a\ue93b\ue93c\ue93d\ue93e\ue93f\ue940\ue941\ue942\ue943\ue944\ue945\ue946\ue947\ue948\ue949\ue94a\ue94b\ue94c\ue94d\ue94e\ue94f\ue950\ue951\ue952\ue953\ue954\ue955\ue956\ue957\ue958\ue959\ue95a\ue95b\ue95c\ue95d\ue95e\ue95f\ue960\ue961\ue962\ue963\ue964\ue965\ue966\ue967\ue968\ue969\ue96a\ue96b\ue96c\ue96d\ue96e\ue96f\ue970\ue971\ue972\ue973\ue974\ue975\ue976\ue977\ue978\ue979\ue97a\ue97b\ue97c\ue97d\ue97e\ue97f\ue980\ue981\ue982\ue983\ue984\ue985\ue986\ue987\ue988\ue989\ue98a\ue98b\ue98c\ue98d\ue98e\ue98f\ue990\ue991\ue992\ue993\ue994\ue995\ue996\ue997\ue998\ue999\ue99a\ue99b\ue99c\ue99d\ue99e\ue99f\ue9a0\ue9a1\ue9a2\ue9a3\ue9a4\ue9a5\ue9a6\ue9a7\ue9a8\ue9a9\ue9aa\ue9ab\ue9ac\ue9ad\ue9ae\ue9af\ue9b0\ue9b1\ue9b2\ue9b3\ue9b4\ue9b5\ue9b6\ue9b7\ue9b8\ue9b9\ue9ba\ue9bb\ue9bc\ue9bd\ue9be\ue9bf\ue9c0\ue9c1\ue9c2\ue9c3\ue9c4\ue9c5\ue9c6\ue9c7\ue9c8\ue9c9\ue9ca\ue9cb\ue9cc\ue9cd\ue9ce\ue9cf\ue9d0\ue9d1\ue9d2\ue9d3\ue9d4\ue9d5\ue9d6\ue9d7\ue9d8\ue9d9\ue9da\ue9db\ue9dc\ue9dd\ue9de\ue9df\ue9e0\ue9e1\ue9e2\ue9e3\ue9e4\ue9e5\ue9e6\ue9e7\ue9e8\ue9e9\ue9ea\ue9eb\ue9ec\ue9ed\ue9ee\ue9ef\ue9f0\ue9f1\ue9f2\ue9f3\ue9f4\ue9f5\ue9f6\ue9f7\ue9f8\ue9f9\ue9fa\ue9fb\ue9fc\ue9fd\ue9fe\ue9ff\uea00\uea01\uea02\uea03\uea04\uea05\uea06\uea07\uea08\uea09\uea0a\uea0b\uea0c\uea0d\uea0e\uea0f\uea10\uea11\uea12\uea13\uea14\uea15\uea16\uea17\uea18\uea19\uea1a\uea1b\uea1c\uea1d\uea1e\uea1f\uea20\uea21\uea22\uea23\uea24\uea25\uea26\uea27\uea28\uea29\uea2a\uea2b\uea2c\uea2d\uea2e\uea2f\uea30\uea31\uea32\uea33\uea34\uea35\uea36\uea37\uea38\uea39\uea3a\uea3b\uea3c\uea3d\uea3e\uea3f\uea40\uea41\uea42\uea43\uea44\uea45\uea46\uea47\uea48\uea49\uea4a\uea4b\uea4c\uea4d\uea4e\uea4f\uea50\uea51\uea52\uea53\uea54\uea55\uea56\uea57\uea58\uea59\uea5a\uea5b\uea5c\uea5d\uea5e\uea5f\uea60\uea61\uea62\uea63\uea64\uea65\uea66\uea67\uea68\uea69\uea6a\uea6b\uea6c\uea6d\uea6e\uea6f\uea70\uea71\uea72\uea73\uea74\uea75\uea76\uea77\uea78\uea79\uea7a\uea7b\uea7c\uea7d\uea7e\uea7f\uea80\uea81\uea82\uea83\uea84\uea85\uea86\uea87\uea88\uea89\uea8a\uea8b\uea8c\uea8d\uea8e\uea8f\uea90\uea91\uea92\uea93\uea94\uea95\uea96\uea97\uea98\uea99\uea9a\uea9b\uea9c\uea9d\uea9e\uea9f\ueaa0\ueaa1\ueaa2\ueaa3\ueaa4\ueaa5\ueaa6\ueaa7\ueaa8\ueaa9\ueaaa\ueaab\ueaac\ueaad\ueaae\ueaaf\ueab0\ueab1\ueab2\ueab3\ueab4\ueab5\ueab6\ueab7\ueab8\ueab9\ueaba\ueabb\ueabc\ueabd\ueabe\ueabf\ueac0\ueac1\ueac2\ueac3\ueac4\ueac5\ueac6\ueac7\ueac8\ueac9\ueaca\ueacb\ueacc\ueacd\ueace\ueacf\uead0\uead1\uead2\uead3\uead4\uead5\uead6\uead7\uead8\uead9\ueada\ueadb\ueadc\ueadd\ueade\ueadf\ueae0\ueae1\ueae2\ueae3\ueae4\ueae5\ueae6\ueae7\ueae8\ueae9\ueaea\ueaeb\ueaec\ueaed\ueaee\ueaef\ueaf0\ueaf1\ueaf2\ueaf3\ueaf4\ueaf5\ueaf6\ueaf7\ueaf8\ueaf9\ueafa\ueafb\ueafc\ueafd\ueafe\ueaff\ueb00\ueb01\ueb02\ueb03\ueb04\ueb05\ueb06\ueb07\ueb08\ueb09\ueb0a\ueb0b\ueb0c\ueb0d\ueb0e\ueb0f\ueb10\ueb11\ueb12\ueb13\ueb14\ueb15\ueb16\ueb17\ueb18\ueb19\ueb1a\ueb1b\ueb1c\ueb1d\ueb1e\ueb1f\ueb20\ueb21\ueb22\ueb23\ueb24\ueb25\ueb26\ueb27\ueb28\ueb29\ueb2a\ueb2b\ueb2c\ueb2d\ueb2e\ueb2f\ueb30\ueb31\ueb32\ueb33\ueb34\ueb35\ueb36\ueb37\ueb38\ueb39\ueb3a\ueb3b\ueb3c\ueb3d\ueb3e\ueb3f\ueb40\ueb41\ueb42\ueb43\ueb44\ueb45\ueb46\ueb47\ueb48\ueb49\ueb4a\ueb4b\ueb4c\ueb4d\ueb4e\ueb4f\ueb50\ueb51\ueb52\ueb53\ueb54\ueb55\ueb56\ueb57\ueb58\ueb59\ueb5a\ueb5b\ueb5c\ueb5d\ueb5e\ueb5f\ueb60\ueb61\ueb62\ueb63\ueb64\ueb65\ueb66\ueb67\ueb68\ueb69\ueb6a\ueb6b\ueb6c\ueb6d\ueb6e\ueb6f\ueb70\ueb71\ueb72\ueb73\ueb74\ueb75\ueb76\ueb77\ueb78\ueb79\ueb7a\ueb7b\ueb7c\ueb7d\ueb7e\ueb7f\ueb80\ueb81\ueb82\ueb83\ueb84\ueb85\ueb86\ueb87\ueb88\ueb89\ueb8a\ueb8b\ueb8c\ueb8d\ueb8e\ueb8f\ueb90\ueb91\ueb92\ueb93\ueb94\ueb95\ueb96\ueb97\ueb98\ueb99\ueb9a\ueb9b\ueb9c\ueb9d\ueb9e\ueb9f\ueba0\ueba1\ueba2\ueba3\ueba4\ueba5\ueba6\ueba7\ueba8\ueba9\uebaa\uebab\uebac\uebad\uebae\uebaf\uebb0\uebb1\uebb2\uebb3\uebb4\uebb5\uebb6\uebb7\uebb8\uebb9\uebba\uebbb\uebbc\uebbd\uebbe\uebbf\uebc0\uebc1\uebc2\uebc3\uebc4\uebc5\uebc6\uebc7\uebc8\uebc9\uebca\uebcb\uebcc\uebcd\uebce\uebcf\uebd0\uebd1\uebd2\uebd3\uebd4\uebd5\uebd6\uebd7\uebd8\uebd9\uebda\uebdb\uebdc\uebdd\uebde\uebdf\uebe0\uebe1\uebe2\uebe3\uebe4\uebe5\uebe6\uebe7\uebe8\uebe9\uebea\uebeb\uebec\uebed\uebee\uebef\uebf0\uebf1\uebf2\uebf3\uebf4\uebf5\uebf6\uebf7\uebf8\uebf9\uebfa\uebfb\uebfc\uebfd\uebfe\uebff\uec00\uec01\uec02\uec03\uec04\uec05\uec06\uec07\uec08\uec09\uec0a\uec0b\uec0c\uec0d\uec0e\uec0f\uec10\uec11\uec12\uec13\uec14\uec15\uec16\uec17\uec18\uec19\uec1a\uec1b\uec1c\uec1d\uec1e\uec1f\uec20\uec21\uec22\uec23\uec24\uec25\uec26\uec27\uec28\uec29\uec2a\uec2b\uec2c\uec2d\uec2e\uec2f\uec30\uec31\uec32\uec33\uec34\uec35\uec36\uec37\uec38\uec39\uec3a\uec3b\uec3c\uec3d\uec3e\uec3f\uec40\uec41\uec42\uec43\uec44\uec45\uec46\uec47\uec48\uec49\uec4a\uec4b\uec4c\uec4d\uec4e\uec4f\uec50\uec51\uec52\uec53\uec54\uec55\uec56\uec57\uec58\uec59\uec5a\uec5b\uec5c\uec5d\uec5e\uec5f\uec60\uec61\uec62\uec63\uec64\uec65\uec66\uec67\uec68\uec69\uec6a\uec6b\uec6c\uec6d\uec6e\uec6f\uec70\uec71\uec72\uec73\uec74\uec75\uec76\uec77\uec78\uec79\uec7a\uec7b\uec7c\uec7d\uec7e\uec7f\uec80\uec81\uec82\uec83\uec84\uec85\uec86\uec87\uec88\uec89\uec8a\uec8b\uec8c\uec8d\uec8e\uec8f\uec90\uec91\uec92\uec93\uec94\uec95\uec96\uec97\uec98\uec99\uec9a\uec9b\uec9c\uec9d\uec9e\uec9f\ueca0\ueca1\ueca2\ueca3\ueca4\ueca5\ueca6\ueca7\ueca8\ueca9\uecaa\uecab\uecac\uecad\uecae\uecaf\uecb0\uecb1\uecb2\uecb3\uecb4\uecb5\uecb6\uecb7\uecb8\uecb9\uecba\uecbb\uecbc\uecbd\uecbe\uecbf\uecc0\uecc1\uecc2\uecc3\uecc4\uecc5\uecc6\uecc7\uecc8\uecc9\uecca\ueccb\ueccc\ueccd\uecce\ueccf\uecd0\uecd1\uecd2\uecd3\uecd4\uecd5\uecd6\uecd7\uecd8\uecd9\uecda\uecdb\uecdc\uecdd\uecde\uecdf\uece0\uece1\uece2\uece3\uece4\uece5\uece6\uece7\uece8\uece9\uecea\ueceb\uecec\ueced\uecee\uecef\uecf0\uecf1\uecf2\uecf3\uecf4\uecf5\uecf6\uecf7\uecf8\uecf9\uecfa\uecfb\uecfc\uecfd\uecfe\uecff\ued00\ued01\ued02\ued03\ued04\ued05\ued06\ued07\ued08\ued09\ued0a\ued0b\ued0c\ued0d\ued0e\ued0f\ued10\ued11\ued12\ued13\ued14\ued15\ued16\ued17\ued18\ued19\ued1a\ued1b\ued1c\ued1d\ued1e\ued1f\ued20\ued21\ued22\ued23\ued24\ued25\ued26\ued27\ued28\ued29\ued2a\ued2b\ued2c\ued2d\ued2e\ued2f\ued30\ued31\ued32\ued33\ued34\ued35\ued36\ued37\ued38\ued39\ued3a\ued3b\ued3c\ued3d\ued3e\ued3f\ued40\ued41\ued42\ued43\ued44\ued45\ued46\ued47\ued48\ued49\ued4a\ued4b\ued4c\ued4d\ued4e\ued4f\ued50\ued51\ued52\ued53\ued54\ued55\ued56\ued57\ued58\ued59\ued5a\ued5b\ued5c\ued5d\ued5e\ued5f\ued60\ued61\ued62\ued63\ued64\ued65\ued66\ued67\ued68\ued69\ued6a\ued6b\ued6c\ued6d\ued6e\ued6f\ued70\ued71\ued72\ued73\ued74\ued75\ued76\ued77\ued78\ued79\ued7a\ued7b\ued7c\ued7d\ued7e\ued7f\ued80\ued81\ued82\ued83\ued84\ued85\ued86\ued87\ued88\ued89\ued8a\ued8b\ued8c\ued8d\ued8e\ued8f\ued90\ued91\ued92\ued93\ued94\ued95\ued96\ued97\ued98\ued99\ued9a\ued9b\ued9c\ued9d\ued9e\ued9f\ueda0\ueda1\ueda2\ueda3\ueda4\ueda5\ueda6\ueda7\ueda8\ueda9\uedaa\uedab\uedac\uedad\uedae\uedaf\uedb0\uedb1\uedb2\uedb3\uedb4\uedb5\uedb6\uedb7\uedb8\uedb9\uedba\uedbb\uedbc\uedbd\uedbe\uedbf\uedc0\uedc1\uedc2\uedc3\uedc4\uedc5\uedc6\uedc7\uedc8\uedc9\uedca\uedcb\uedcc\uedcd\uedce\uedcf\uedd0\uedd1\uedd2\uedd3\uedd4\uedd5\uedd6\uedd7\uedd8\uedd9\uedda\ueddb\ueddc\ueddd\uedde\ueddf\uede0\uede1\uede2\uede3\uede4\uede5\uede6\uede7\uede8\uede9\uedea\uedeb\uedec\ueded\uedee\uedef\uedf0\uedf1\uedf2\uedf3\uedf4\uedf5\uedf6\uedf7\uedf8\uedf9\uedfa\uedfb\uedfc\uedfd\uedfe\uedff\uee00\uee01\uee02\uee03\uee04\uee05\uee06\uee07\uee08\uee09\uee0a\uee0b\uee0c\uee0d\uee0e\uee0f\uee10\uee11\uee12\uee13\uee14\uee15\uee16\uee17\uee18\uee19\uee1a\uee1b\uee1c\uee1d\uee1e\uee1f\uee20\uee21\uee22\uee23\uee24\uee25\uee26\uee27\uee28\uee29\uee2a\uee2b\uee2c\uee2d\uee2e\uee2f\uee30\uee31\uee32\uee33\uee34\uee35\uee36\uee37\uee38\uee39\uee3a\uee3b\uee3c\uee3d\uee3e\uee3f\uee40\uee41\uee42\uee43\uee44\uee45\uee46\uee47\uee48\uee49\uee4a\uee4b\uee4c\uee4d\uee4e\uee4f\uee50\uee51\uee52\uee53\uee54\uee55\uee56\uee57\uee58\uee59\uee5a\uee5b\uee5c\uee5d\uee5e\uee5f\uee60\uee61\uee62\uee63\uee64\uee65\uee66\uee67\uee68\uee69\uee6a\uee6b\uee6c\uee6d\uee6e\uee6f\uee70\uee71\uee72\uee73\uee74\uee75\uee76\uee77\uee78\uee79\uee7a\uee7b\uee7c\uee7d\uee7e\uee7f\uee80\uee81\uee82\uee83\uee84\uee85\uee86\uee87\uee88\uee89\uee8a\uee8b\uee8c\uee8d\uee8e\uee8f\uee90\uee91\uee92\uee93\uee94\uee95\uee96\uee97\uee98\uee99\uee9a\uee9b\uee9c\uee9d\uee9e\uee9f\ueea0\ueea1\ueea2\ueea3\ueea4\ueea5\ueea6\ueea7\ueea8\ueea9\ueeaa\ueeab\ueeac\ueead\ueeae\ueeaf\ueeb0\ueeb1\ueeb2\ueeb3\ueeb4\ueeb5\ueeb6\ueeb7\ueeb8\ueeb9\ueeba\ueebb\ueebc\ueebd\ueebe\ueebf\ueec0\ueec1\ueec2\ueec3\ueec4\ueec5\ueec6\ueec7\ueec8\ueec9\ueeca\ueecb\ueecc\ueecd\ueece\ueecf\ueed0\ueed1\ueed2\ueed3\ueed4\ueed5\ueed6\ueed7\ueed8\ueed9\ueeda\ueedb\ueedc\ueedd\ueede\ueedf\ueee0\ueee1\ueee2\ueee3\ueee4\ueee5\ueee6\ueee7\ueee8\ueee9\ueeea\ueeeb\ueeec\ueeed\ueeee\ueeef\ueef0\ueef1\ueef2\ueef3\ueef4\ueef5\ueef6\ueef7\ueef8\ueef9\ueefa\ueefb\ueefc\ueefd\ueefe\ueeff\uef00\uef01\uef02\uef03\uef04\uef05\uef06\uef07\uef08\uef09\uef0a\uef0b\uef0c\uef0d\uef0e\uef0f\uef10\uef11\uef12\uef13\uef14\uef15\uef16\uef17\uef18\uef19\uef1a\uef1b\uef1c\uef1d\uef1e\uef1f\uef20\uef21\uef22\uef23\uef24\uef25\uef26\uef27\uef28\uef29\uef2a\uef2b\uef2c\uef2d\uef2e\uef2f\uef30\uef31\uef32\uef33\uef34\uef35\uef36\uef37\uef38\uef39\uef3a\uef3b\uef3c\uef3d\uef3e\uef3f\uef40\uef41\uef42\uef43\uef44\uef45\uef46\uef47\uef48\uef49\uef4a\uef4b\uef4c\uef4d\uef4e\uef4f\uef50\uef51\uef52\uef53\uef54\uef55\uef56\uef57\uef58\uef59\uef5a\uef5b\uef5c\uef5d\uef5e\uef5f\uef60\uef61\uef62\uef63\uef64\uef65\uef66\uef67\uef68\uef69\uef6a\uef6b\uef6c\uef6d\uef6e\uef6f\uef70\uef71\uef72\uef73\uef74\uef75\uef76\uef77\uef78\uef79\uef7a\uef7b\uef7c\uef7d\uef7e\uef7f\uef80\uef81\uef82\uef83\uef84\uef85\uef86\uef87\uef88\uef89\uef8a\uef8b\uef8c\uef8d\uef8e\uef8f\uef90\uef91\uef92\uef93\uef94\uef95\uef96\uef97\uef98\uef99\uef9a\uef9b\uef9c\uef9d\uef9e\uef9f\uefa0\uefa1\uefa2\uefa3\uefa4\uefa5\uefa6\uefa7\uefa8\uefa9\uefaa\uefab\uefac\uefad\uefae\uefaf\uefb0\uefb1\uefb2\uefb3\uefb4\uefb5\uefb6\uefb7\uefb8\uefb9\uefba\uefbb\uefbc\uefbd\uefbe\uefbf\uefc0\uefc1\uefc2\uefc3\uefc4\uefc5\uefc6\uefc7\uefc8\uefc9\uefca\uefcb\uefcc\uefcd\uefce\uefcf\uefd0\uefd1\uefd2\uefd3\uefd4\uefd5\uefd6\uefd7\uefd8\uefd9\uefda\uefdb\uefdc\uefdd\uefde\uefdf\uefe0\uefe1\uefe2\uefe3\uefe4\uefe5\uefe6\uefe7\uefe8\uefe9\uefea\uefeb\uefec\uefed\uefee\uefef\ueff0\ueff1\ueff2\ueff3\ueff4\ueff5\ueff6\ueff7\ueff8\ueff9\ueffa\ueffb\ueffc\ueffd\ueffe\uefff\uf000\uf001\uf002\uf003\uf004\uf005\uf006\uf007\uf008\uf009\uf00a\uf00b\uf00c\uf00d\uf00e\uf00f\uf010\uf011\uf012\uf013\uf014\uf015\uf016\uf017\uf018\uf019\uf01a\uf01b\uf01c\uf01d\uf01e\uf01f\uf020\uf021\uf022\uf023\uf024\uf025\uf026\uf027\uf028\uf029\uf02a\uf02b\uf02c\uf02d\uf02e\uf02f\uf030\uf031\uf032\uf033\uf034\uf035\uf036\uf037\uf038\uf039\uf03a\uf03b\uf03c\uf03d\uf03e\uf03f\uf040\uf041\uf042\uf043\uf044\uf045\uf046\uf047\uf048\uf049\uf04a\uf04b\uf04c\uf04d\uf04e\uf04f\uf050\uf051\uf052\uf053\uf054\uf055\uf056\uf057\uf058\uf059\uf05a\uf05b\uf05c\uf05d\uf05e\uf05f\uf060\uf061\uf062\uf063\uf064\uf065\uf066\uf067\uf068\uf069\uf06a\uf06b\uf06c\uf06d\uf06e\uf06f\uf070\uf071\uf072\uf073\uf074\uf075\uf076\uf077\uf078\uf079\uf07a\uf07b\uf07c\uf07d\uf07e\uf07f\uf080\uf081\uf082\uf083\uf084\uf085\uf086\uf087\uf088\uf089\uf08a\uf08b\uf08c\uf08d\uf08e\uf08f\uf090\uf091\uf092\uf093\uf094\uf095\uf096\uf097\uf098\uf099\uf09a\uf09b\uf09c\uf09d\uf09e\uf09f\uf0a0\uf0a1\uf0a2\uf0a3\uf0a4\uf0a5\uf0a6\uf0a7\uf0a8\uf0a9\uf0aa\uf0ab\uf0ac\uf0ad\uf0ae\uf0af\uf0b0\uf0b1\uf0b2\uf0b3\uf0b4\uf0b5\uf0b6\uf0b7\uf0b8\uf0b9\uf0ba\uf0bb\uf0bc\uf0bd\uf0be\uf0bf\uf0c0\uf0c1\uf0c2\uf0c3\uf0c4\uf0c5\uf0c6\uf0c7\uf0c8\uf0c9\uf0ca\uf0cb\uf0cc\uf0cd\uf0ce\uf0cf\uf0d0\uf0d1\uf0d2\uf0d3\uf0d4\uf0d5\uf0d6\uf0d7\uf0d8\uf0d9\uf0da\uf0db\uf0dc\uf0dd\uf0de\uf0df\uf0e0\uf0e1\uf0e2\uf0e3\uf0e4\uf0e5\uf0e6\uf0e7\uf0e8\uf0e9\uf0ea\uf0eb\uf0ec\uf0ed\uf0ee\uf0ef\uf0f0\uf0f1\uf0f2\uf0f3\uf0f4\uf0f5\uf0f6\uf0f7\uf0f8\uf0f9\uf0fa\uf0fb\uf0fc\uf0fd\uf0fe\uf0ff\uf100\uf101\uf102\uf103\uf104\uf105\uf106\uf107\uf108\uf109\uf10a\uf10b\uf10c\uf10d\uf10e\uf10f\uf110\uf111\uf112\uf113\uf114\uf115\uf116\uf117\uf118\uf119\uf11a\uf11b\uf11c\uf11d\uf11e\uf11f\uf120\uf121\uf122\uf123\uf124\uf125\uf126\uf127\uf128\uf129\uf12a\uf12b\uf12c\uf12d\uf12e\uf12f\uf130\uf131\uf132\uf133\uf134\uf135\uf136\uf137\uf138\uf139\uf13a\uf13b\uf13c\uf13d\uf13e\uf13f\uf140\uf141\uf142\uf143\uf144\uf145\uf146\uf147\uf148\uf149\uf14a\uf14b\uf14c\uf14d\uf14e\uf14f\uf150\uf151\uf152\uf153\uf154\uf155\uf156\uf157\uf158\uf159\uf15a\uf15b\uf15c\uf15d\uf15e\uf15f\uf160\uf161\uf162\uf163\uf164\uf165\uf166\uf167\uf168\uf169\uf16a\uf16b\uf16c\uf16d\uf16e\uf16f\uf170\uf171\uf172\uf173\uf174\uf175\uf176\uf177\uf178\uf179\uf17a\uf17b\uf17c\uf17d\uf17e\uf17f\uf180\uf181\uf182\uf183\uf184\uf185\uf186\uf187\uf188\uf189\uf18a\uf18b\uf18c\uf18d\uf18e\uf18f\uf190\uf191\uf192\uf193\uf194\uf195\uf196\uf197\uf198\uf199\uf19a\uf19b\uf19c\uf19d\uf19e\uf19f\uf1a0\uf1a1\uf1a2\uf1a3\uf1a4\uf1a5\uf1a6\uf1a7\uf1a8\uf1a9\uf1aa\uf1ab\uf1ac\uf1ad\uf1ae\uf1af\uf1b0\uf1b1\uf1b2\uf1b3\uf1b4\uf1b5\uf1b6\uf1b7\uf1b8\uf1b9\uf1ba\uf1bb\uf1bc\uf1bd\uf1be\uf1bf\uf1c0\uf1c1\uf1c2\uf1c3\uf1c4\uf1c5\uf1c6\uf1c7\uf1c8\uf1c9\uf1ca\uf1cb\uf1cc\uf1cd\uf1ce\uf1cf\uf1d0\uf1d1\uf1d2\uf1d3\uf1d4\uf1d5\uf1d6\uf1d7\uf1d8\uf1d9\uf1da\uf1db\uf1dc\uf1dd\uf1de\uf1df\uf1e0\uf1e1\uf1e2\uf1e3\uf1e4\uf1e5\uf1e6\uf1e7\uf1e8\uf1e9\uf1ea\uf1eb\uf1ec\uf1ed\uf1ee\uf1ef\uf1f0\uf1f1\uf1f2\uf1f3\uf1f4\uf1f5\uf1f6\uf1f7\uf1f8\uf1f9\uf1fa\uf1fb\uf1fc\uf1fd\uf1fe\uf1ff\uf200\uf201\uf202\uf203\uf204\uf205\uf206\uf207\uf208\uf209\uf20a\uf20b\uf20c\uf20d\uf20e\uf20f\uf210\uf211\uf212\uf213\uf214\uf215\uf216\uf217\uf218\uf219\uf21a\uf21b\uf21c\uf21d\uf21e\uf21f\uf220\uf221\uf222\uf223\uf224\uf225\uf226\uf227\uf228\uf229\uf22a\uf22b\uf22c\uf22d\uf22e\uf22f\uf230\uf231\uf232\uf233\uf234\uf235\uf236\uf237\uf238\uf239\uf23a\uf23b\uf23c\uf23d\uf23e\uf23f\uf240\uf241\uf242\uf243\uf244\uf245\uf246\uf247\uf248\uf249\uf24a\uf24b\uf24c\uf24d\uf24e\uf24f\uf250\uf251\uf252\uf253\uf254\uf255\uf256\uf257\uf258\uf259\uf25a\uf25b\uf25c\uf25d\uf25e\uf25f\uf260\uf261\uf262\uf263\uf264\uf265\uf266\uf267\uf268\uf269\uf26a\uf26b\uf26c\uf26d\uf26e\uf26f\uf270\uf271\uf272\uf273\uf274\uf275\uf276\uf277\uf278\uf279\uf27a\uf27b\uf27c\uf27d\uf27e\uf27f\uf280\uf281\uf282\uf283\uf284\uf285\uf286\uf287\uf288\uf289\uf28a\uf28b\uf28c\uf28d\uf28e\uf28f\uf290\uf291\uf292\uf293\uf294\uf295\uf296\uf297\uf298\uf299\uf29a\uf29b\uf29c\uf29d\uf29e\uf29f\uf2a0\uf2a1\uf2a2\uf2a3\uf2a4\uf2a5\uf2a6\uf2a7\uf2a8\uf2a9\uf2aa\uf2ab\uf2ac\uf2ad\uf2ae\uf2af\uf2b0\uf2b1\uf2b2\uf2b3\uf2b4\uf2b5\uf2b6\uf2b7\uf2b8\uf2b9\uf2ba\uf2bb\uf2bc\uf2bd\uf2be\uf2bf\uf2c0\uf2c1\uf2c2\uf2c3\uf2c4\uf2c5\uf2c6\uf2c7\uf2c8\uf2c9\uf2ca\uf2cb\uf2cc\uf2cd\uf2ce\uf2cf\uf2d0\uf2d1\uf2d2\uf2d3\uf2d4\uf2d5\uf2d6\uf2d7\uf2d8\uf2d9\uf2da\uf2db\uf2dc\uf2dd\uf2de\uf2df\uf2e0\uf2e1\uf2e2\uf2e3\uf2e4\uf2e5\uf2e6\uf2e7\uf2e8\uf2e9\uf2ea\uf2eb\uf2ec\uf2ed\uf2ee\uf2ef\uf2f0\uf2f1\uf2f2\uf2f3\uf2f4\uf2f5\uf2f6\uf2f7\uf2f8\uf2f9\uf2fa\uf2fb\uf2fc\uf2fd\uf2fe\uf2ff\uf300\uf301\uf302\uf303\uf304\uf305\uf306\uf307\uf308\uf309\uf30a\uf30b\uf30c\uf30d\uf30e\uf30f\uf310\uf311\uf312\uf313\uf314\uf315\uf316\uf317\uf318\uf319\uf31a\uf31b\uf31c\uf31d\uf31e\uf31f\uf320\uf321\uf322\uf323\uf324\uf325\uf326\uf327\uf328\uf329\uf32a\uf32b\uf32c\uf32d\uf32e\uf32f\uf330\uf331\uf332\uf333\uf334\uf335\uf336\uf337\uf338\uf339\uf33a\uf33b\uf33c\uf33d\uf33e\uf33f\uf340\uf341\uf342\uf343\uf344\uf345\uf346\uf347\uf348\uf349\uf34a\uf34b\uf34c\uf34d\uf34e\uf34f\uf350\uf351\uf352\uf353\uf354\uf355\uf356\uf357\uf358\uf359\uf35a\uf35b\uf35c\uf35d\uf35e\uf35f\uf360\uf361\uf362\uf363\uf364\uf365\uf366\uf367\uf368\uf369\uf36a\uf36b\uf36c\uf36d\uf36e\uf36f\uf370\uf371\uf372\uf373\uf374\uf375\uf376\uf377\uf378\uf379\uf37a\uf37b\uf37c\uf37d\uf37e\uf37f\uf380\uf381\uf382\uf383\uf384\uf385\uf386\uf387\uf388\uf389\uf38a\uf38b\uf38c\uf38d\uf38e\uf38f\uf390\uf391\uf392\uf393\uf394\uf395\uf396\uf397\uf398\uf399\uf39a\uf39b\uf39c\uf39d\uf39e\uf39f\uf3a0\uf3a1\uf3a2\uf3a3\uf3a4\uf3a5\uf3a6\uf3a7\uf3a8\uf3a9\uf3aa\uf3ab\uf3ac\uf3ad\uf3ae\uf3af\uf3b0\uf3b1\uf3b2\uf3b3\uf3b4\uf3b5\uf3b6\uf3b7\uf3b8\uf3b9\uf3ba\uf3bb\uf3bc\uf3bd\uf3be\uf3bf\uf3c0\uf3c1\uf3c2\uf3c3\uf3c4\uf3c5\uf3c6\uf3c7\uf3c8\uf3c9\uf3ca\uf3cb\uf3cc\uf3cd\uf3ce\uf3cf\uf3d0\uf3d1\uf3d2\uf3d3\uf3d4\uf3d5\uf3d6\uf3d7\uf3d8\uf3d9\uf3da\uf3db\uf3dc\uf3dd\uf3de\uf3df\uf3e0\uf3e1\uf3e2\uf3e3\uf3e4\uf3e5\uf3e6\uf3e7\uf3e8\uf3e9\uf3ea\uf3eb\uf3ec\uf3ed\uf3ee\uf3ef\uf3f0\uf3f1\uf3f2\uf3f3\uf3f4\uf3f5\uf3f6\uf3f7\uf3f8\uf3f9\uf3fa\uf3fb\uf3fc\uf3fd\uf3fe\uf3ff\uf400\uf401\uf402\uf403\uf404\uf405\uf406\uf407\uf408\uf409\uf40a\uf40b\uf40c\uf40d\uf40e\uf40f\uf410\uf411\uf412\uf413\uf414\uf415\uf416\uf417\uf418\uf419\uf41a\uf41b\uf41c\uf41d\uf41e\uf41f\uf420\uf421\uf422\uf423\uf424\uf425\uf426\uf427\uf428\uf429\uf42a\uf42b\uf42c\uf42d\uf42e\uf42f\uf430\uf431\uf432\uf433\uf434\uf435\uf436\uf437\uf438\uf439\uf43a\uf43b\uf43c\uf43d\uf43e\uf43f\uf440\uf441\uf442\uf443\uf444\uf445\uf446\uf447\uf448\uf449\uf44a\uf44b\uf44c\uf44d\uf44e\uf44f\uf450\uf451\uf452\uf453\uf454\uf455\uf456\uf457\uf458\uf459\uf45a\uf45b\uf45c\uf45d\uf45e\uf45f\uf460\uf461\uf462\uf463\uf464\uf465\uf466\uf467\uf468\uf469\uf46a\uf46b\uf46c\uf46d\uf46e\uf46f\uf470\uf471\uf472\uf473\uf474\uf475\uf476\uf477\uf478\uf479\uf47a\uf47b\uf47c\uf47d\uf47e\uf47f\uf480\uf481\uf482\uf483\uf484\uf485\uf486\uf487\uf488\uf489\uf48a\uf48b\uf48c\uf48d\uf48e\uf48f\uf490\uf491\uf492\uf493\uf494\uf495\uf496\uf497\uf498\uf499\uf49a\uf49b\uf49c\uf49d\uf49e\uf49f\uf4a0\uf4a1\uf4a2\uf4a3\uf4a4\uf4a5\uf4a6\uf4a7\uf4a8\uf4a9\uf4aa\uf4ab\uf4ac\uf4ad\uf4ae\uf4af\uf4b0\uf4b1\uf4b2\uf4b3\uf4b4\uf4b5\uf4b6\uf4b7\uf4b8\uf4b9\uf4ba\uf4bb\uf4bc\uf4bd\uf4be\uf4bf\uf4c0\uf4c1\uf4c2\uf4c3\uf4c4\uf4c5\uf4c6\uf4c7\uf4c8\uf4c9\uf4ca\uf4cb\uf4cc\uf4cd\uf4ce\uf4cf\uf4d0\uf4d1\uf4d2\uf4d3\uf4d4\uf4d5\uf4d6\uf4d7\uf4d8\uf4d9\uf4da\uf4db\uf4dc\uf4dd\uf4de\uf4df\uf4e0\uf4e1\uf4e2\uf4e3\uf4e4\uf4e5\uf4e6\uf4e7\uf4e8\uf4e9\uf4ea\uf4eb\uf4ec\uf4ed\uf4ee\uf4ef\uf4f0\uf4f1\uf4f2\uf4f3\uf4f4\uf4f5\uf4f6\uf4f7\uf4f8\uf4f9\uf4fa\uf4fb\uf4fc\uf4fd\uf4fe\uf4ff\uf500\uf501\uf502\uf503\uf504\uf505\uf506\uf507\uf508\uf509\uf50a\uf50b\uf50c\uf50d\uf50e\uf50f\uf510\uf511\uf512\uf513\uf514\uf515\uf516\uf517\uf518\uf519\uf51a\uf51b\uf51c\uf51d\uf51e\uf51f\uf520\uf521\uf522\uf523\uf524\uf525\uf526\uf527\uf528\uf529\uf52a\uf52b\uf52c\uf52d\uf52e\uf52f\uf530\uf531\uf532\uf533\uf534\uf535\uf536\uf537\uf538\uf539\uf53a\uf53b\uf53c\uf53d\uf53e\uf53f\uf540\uf541\uf542\uf543\uf544\uf545\uf546\uf547\uf548\uf549\uf54a\uf54b\uf54c\uf54d\uf54e\uf54f\uf550\uf551\uf552\uf553\uf554\uf555\uf556\uf557\uf558\uf559\uf55a\uf55b\uf55c\uf55d\uf55e\uf55f\uf560\uf561\uf562\uf563\uf564\uf565\uf566\uf567\uf568\uf569\uf56a\uf56b\uf56c\uf56d\uf56e\uf56f\uf570\uf571\uf572\uf573\uf574\uf575\uf576\uf577\uf578\uf579\uf57a\uf57b\uf57c\uf57d\uf57e\uf57f\uf580\uf581\uf582\uf583\uf584\uf585\uf586\uf587\uf588\uf589\uf58a\uf58b\uf58c\uf58d\uf58e\uf58f\uf590\uf591\uf592\uf593\uf594\uf595\uf596\uf597\uf598\uf599\uf59a\uf59b\uf59c\uf59d\uf59e\uf59f\uf5a0\uf5a1\uf5a2\uf5a3\uf5a4\uf5a5\uf5a6\uf5a7\uf5a8\uf5a9\uf5aa\uf5ab\uf5ac\uf5ad\uf5ae\uf5af\uf5b0\uf5b1\uf5b2\uf5b3\uf5b4\uf5b5\uf5b6\uf5b7\uf5b8\uf5b9\uf5ba\uf5bb\uf5bc\uf5bd\uf5be\uf5bf\uf5c0\uf5c1\uf5c2\uf5c3\uf5c4\uf5c5\uf5c6\uf5c7\uf5c8\uf5c9\uf5ca\uf5cb\uf5cc\uf5cd\uf5ce\uf5cf\uf5d0\uf5d1\uf5d2\uf5d3\uf5d4\uf5d5\uf5d6\uf5d7\uf5d8\uf5d9\uf5da\uf5db\uf5dc\uf5dd\uf5de\uf5df\uf5e0\uf5e1\uf5e2\uf5e3\uf5e4\uf5e5\uf5e6\uf5e7\uf5e8\uf5e9\uf5ea\uf5eb\uf5ec\uf5ed\uf5ee\uf5ef\uf5f0\uf5f1\uf5f2\uf5f3\uf5f4\uf5f5\uf5f6\uf5f7\uf5f8\uf5f9\uf5fa\uf5fb\uf5fc\uf5fd\uf5fe\uf5ff\uf600\uf601\uf602\uf603\uf604\uf605\uf606\uf607\uf608\uf609\uf60a\uf60b\uf60c\uf60d\uf60e\uf60f\uf610\uf611\uf612\uf613\uf614\uf615\uf616\uf617\uf618\uf619\uf61a\uf61b\uf61c\uf61d\uf61e\uf61f\uf620\uf621\uf622\uf623\uf624\uf625\uf626\uf627\uf628\uf629\uf62a\uf62b\uf62c\uf62d\uf62e\uf62f\uf630\uf631\uf632\uf633\uf634\uf635\uf636\uf637\uf638\uf639\uf63a\uf63b\uf63c\uf63d\uf63e\uf63f\uf640\uf641\uf642\uf643\uf644\uf645\uf646\uf647\uf648\uf649\uf64a\uf64b\uf64c\uf64d\uf64e\uf64f\uf650\uf651\uf652\uf653\uf654\uf655\uf656\uf657\uf658\uf659\uf65a\uf65b\uf65c\uf65d\uf65e\uf65f\uf660\uf661\uf662\uf663\uf664\uf665\uf666\uf667\uf668\uf669\uf66a\uf66b\uf66c\uf66d\uf66e\uf66f\uf670\uf671\uf672\uf673\uf674\uf675\uf676\uf677\uf678\uf679\uf67a\uf67b\uf67c\uf67d\uf67e\uf67f\uf680\uf681\uf682\uf683\uf684\uf685\uf686\uf687\uf688\uf689\uf68a\uf68b\uf68c\uf68d\uf68e\uf68f\uf690\uf691\uf692\uf693\uf694\uf695\uf696\uf697\uf698\uf699\uf69a\uf69b\uf69c\uf69d\uf69e\uf69f\uf6a0\uf6a1\uf6a2\uf6a3\uf6a4\uf6a5\uf6a6\uf6a7\uf6a8\uf6a9\uf6aa\uf6ab\uf6ac\uf6ad\uf6ae\uf6af\uf6b0\uf6b1\uf6b2\uf6b3\uf6b4\uf6b5\uf6b6\uf6b7\uf6b8\uf6b9\uf6ba\uf6bb\uf6bc\uf6bd\uf6be\uf6bf\uf6c0\uf6c1\uf6c2\uf6c3\uf6c4\uf6c5\uf6c6\uf6c7\uf6c8\uf6c9\uf6ca\uf6cb\uf6cc\uf6cd\uf6ce\uf6cf\uf6d0\uf6d1\uf6d2\uf6d3\uf6d4\uf6d5\uf6d6\uf6d7\uf6d8\uf6d9\uf6da\uf6db\uf6dc\uf6dd\uf6de\uf6df\uf6e0\uf6e1\uf6e2\uf6e3\uf6e4\uf6e5\uf6e6\uf6e7\uf6e8\uf6e9\uf6ea\uf6eb\uf6ec\uf6ed\uf6ee\uf6ef\uf6f0\uf6f1\uf6f2\uf6f3\uf6f4\uf6f5\uf6f6\uf6f7\uf6f8\uf6f9\uf6fa\uf6fb\uf6fc\uf6fd\uf6fe\uf6ff\uf700\uf701\uf702\uf703\uf704\uf705\uf706\uf707\uf708\uf709\uf70a\uf70b\uf70c\uf70d\uf70e\uf70f\uf710\uf711\uf712\uf713\uf714\uf715\uf716\uf717\uf718\uf719\uf71a\uf71b\uf71c\uf71d\uf71e\uf71f\uf720\uf721\uf722\uf723\uf724\uf725\uf726\uf727\uf728\uf729\uf72a\uf72b\uf72c\uf72d\uf72e\uf72f\uf730\uf731\uf732\uf733\uf734\uf735\uf736\uf737\uf738\uf739\uf73a\uf73b\uf73c\uf73d\uf73e\uf73f\uf740\uf741\uf742\uf743\uf744\uf745\uf746\uf747\uf748\uf749\uf74a\uf74b\uf74c\uf74d\uf74e\uf74f\uf750\uf751\uf752\uf753\uf754\uf755\uf756\uf757\uf758\uf759\uf75a\uf75b\uf75c\uf75d\uf75e\uf75f\uf760\uf761\uf762\uf763\uf764\uf765\uf766\uf767\uf768\uf769\uf76a\uf76b\uf76c\uf76d\uf76e\uf76f\uf770\uf771\uf772\uf773\uf774\uf775\uf776\uf777\uf778\uf779\uf77a\uf77b\uf77c\uf77d\uf77e\uf77f\uf780\uf781\uf782\uf783\uf784\uf785\uf786\uf787\uf788\uf789\uf78a\uf78b\uf78c\uf78d\uf78e\uf78f\uf790\uf791\uf792\uf793\uf794\uf795\uf796\uf797\uf798\uf799\uf79a\uf79b\uf79c\uf79d\uf79e\uf79f\uf7a0\uf7a1\uf7a2\uf7a3\uf7a4\uf7a5\uf7a6\uf7a7\uf7a8\uf7a9\uf7aa\uf7ab\uf7ac\uf7ad\uf7ae\uf7af\uf7b0\uf7b1\uf7b2\uf7b3\uf7b4\uf7b5\uf7b6\uf7b7\uf7b8\uf7b9\uf7ba\uf7bb\uf7bc\uf7bd\uf7be\uf7bf\uf7c0\uf7c1\uf7c2\uf7c3\uf7c4\uf7c5\uf7c6\uf7c7\uf7c8\uf7c9\uf7ca\uf7cb\uf7cc\uf7cd\uf7ce\uf7cf\uf7d0\uf7d1\uf7d2\uf7d3\uf7d4\uf7d5\uf7d6\uf7d7\uf7d8\uf7d9\uf7da\uf7db\uf7dc\uf7dd\uf7de\uf7df\uf7e0\uf7e1\uf7e2\uf7e3\uf7e4\uf7e5\uf7e6\uf7e7\uf7e8\uf7e9\uf7ea\uf7eb\uf7ec\uf7ed\uf7ee\uf7ef\uf7f0\uf7f1\uf7f2\uf7f3\uf7f4\uf7f5\uf7f6\uf7f7\uf7f8\uf7f9\uf7fa\uf7fb\uf7fc\uf7fd\uf7fe\uf7ff\uf800\uf801\uf802\uf803\uf804\uf805\uf806\uf807\uf808\uf809\uf80a\uf80b\uf80c\uf80d\uf80e\uf80f\uf810\uf811\uf812\uf813\uf814\uf815\uf816\uf817\uf818\uf819\uf81a\uf81b\uf81c\uf81d\uf81e\uf81f\uf820\uf821\uf822\uf823\uf824\uf825\uf826\uf827\uf828\uf829\uf82a\uf82b\uf82c\uf82d\uf82e\uf82f\uf830\uf831\uf832\uf833\uf834\uf835\uf836\uf837\uf838\uf839\uf83a\uf83b\uf83c\uf83d\uf83e\uf83f\uf840\uf841\uf842\uf843\uf844\uf845\uf846\uf847\uf848\uf849\uf84a\uf84b\uf84c\uf84d\uf84e\uf84f\uf850\uf851\uf852\uf853\uf854\uf855\uf856\uf857\uf858\uf859\uf85a\uf85b\uf85c\uf85d\uf85e\uf85f\uf860\uf861\uf862\uf863\uf864\uf865\uf866\uf867\uf868\uf869\uf86a\uf86b\uf86c\uf86d\uf86e\uf86f\uf870\uf871\uf872\uf873\uf874\uf875\uf876\uf877\uf878\uf879\uf87a\uf87b\uf87c\uf87d\uf87e\uf87f\uf880\uf881\uf882\uf883\uf884\uf885\uf886\uf887\uf888\uf889\uf88a\uf88b\uf88c\uf88d\uf88e\uf88f\uf890\uf891\uf892\uf893\uf894\uf895\uf896\uf897\uf898\uf899\uf89a\uf89b\uf89c\uf89d\uf89e\uf89f\uf8a0\uf8a1\uf8a2\uf8a3\uf8a4\uf8a5\uf8a6\uf8a7\uf8a8\uf8a9\uf8aa\uf8ab\uf8ac\uf8ad\uf8ae\uf8af\uf8b0\uf8b1\uf8b2\uf8b3\uf8b4\uf8b5\uf8b6\uf8b7\uf8b8\uf8b9\uf8ba\uf8bb\uf8bc\uf8bd\uf8be\uf8bf\uf8c0\uf8c1\uf8c2\uf8c3\uf8c4\uf8c5\uf8c6\uf8c7\uf8c8\uf8c9\uf8ca\uf8cb\uf8cc\uf8cd\uf8ce\uf8cf\uf8d0\uf8d1\uf8d2\uf8d3\uf8d4\uf8d5\uf8d6\uf8d7\uf8d8\uf8d9\uf8da\uf8db\uf8dc\uf8dd\uf8de\uf8df\uf8e0\uf8e1\uf8e2\uf8e3\uf8e4\uf8e5\uf8e6\uf8e7\uf8e8\uf8e9\uf8ea\uf8eb\uf8ec\uf8ed\uf8ee\uf8ef\uf8f0\uf8f1\uf8f2\uf8f3\uf8f4\uf8f5\uf8f6\uf8f7\uf8f8\uf8f9\uf8fa\uf8fb\uf8fc\uf8fd\uf8fe\uf8ff' + +try: + Cs = eval(r"'\ud800\ud801\ud802\ud803\ud804\ud805\ud806\ud807\ud808\ud809\ud80a\ud80b\ud80c\ud80d\ud80e\ud80f\ud810\ud811\ud812\ud813\ud814\ud815\ud816\ud817\ud818\ud819\ud81a\ud81b\ud81c\ud81d\ud81e\ud81f\ud820\ud821\ud822\ud823\ud824\ud825\ud826\ud827\ud828\ud829\ud82a\ud82b\ud82c\ud82d\ud82e\ud82f\ud830\ud831\ud832\ud833\ud834\ud835\ud836\ud837\ud838\ud839\ud83a\ud83b\ud83c\ud83d\ud83e\ud83f\ud840\ud841\ud842\ud843\ud844\ud845\ud846\ud847\ud848\ud849\ud84a\ud84b\ud84c\ud84d\ud84e\ud84f\ud850\ud851\ud852\ud853\ud854\ud855\ud856\ud857\ud858\ud859\ud85a\ud85b\ud85c\ud85d\ud85e\ud85f\ud860\ud861\ud862\ud863\ud864\ud865\ud866\ud867\ud868\ud869\ud86a\ud86b\ud86c\ud86d\ud86e\ud86f\ud870\ud871\ud872\ud873\ud874\ud875\ud876\ud877\ud878\ud879\ud87a\ud87b\ud87c\ud87d\ud87e\ud87f\ud880\ud881\ud882\ud883\ud884\ud885\ud886\ud887\ud888\ud889\ud88a\ud88b\ud88c\ud88d\ud88e\ud88f\ud890\ud891\ud892\ud893\ud894\ud895\ud896\ud897\ud898\ud899\ud89a\ud89b\ud89c\ud89d\ud89e\ud89f\ud8a0\ud8a1\ud8a2\ud8a3\ud8a4\ud8a5\ud8a6\ud8a7\ud8a8\ud8a9\ud8aa\ud8ab\ud8ac\ud8ad\ud8ae\ud8af\ud8b0\ud8b1\ud8b2\ud8b3\ud8b4\ud8b5\ud8b6\ud8b7\ud8b8\ud8b9\ud8ba\ud8bb\ud8bc\ud8bd\ud8be\ud8bf\ud8c0\ud8c1\ud8c2\ud8c3\ud8c4\ud8c5\ud8c6\ud8c7\ud8c8\ud8c9\ud8ca\ud8cb\ud8cc\ud8cd\ud8ce\ud8cf\ud8d0\ud8d1\ud8d2\ud8d3\ud8d4\ud8d5\ud8d6\ud8d7\ud8d8\ud8d9\ud8da\ud8db\ud8dc\ud8dd\ud8de\ud8df\ud8e0\ud8e1\ud8e2\ud8e3\ud8e4\ud8e5\ud8e6\ud8e7\ud8e8\ud8e9\ud8ea\ud8eb\ud8ec\ud8ed\ud8ee\ud8ef\ud8f0\ud8f1\ud8f2\ud8f3\ud8f4\ud8f5\ud8f6\ud8f7\ud8f8\ud8f9\ud8fa\ud8fb\ud8fc\ud8fd\ud8fe\ud8ff\ud900\ud901\ud902\ud903\ud904\ud905\ud906\ud907\ud908\ud909\ud90a\ud90b\ud90c\ud90d\ud90e\ud90f\ud910\ud911\ud912\ud913\ud914\ud915\ud916\ud917\ud918\ud919\ud91a\ud91b\ud91c\ud91d\ud91e\ud91f\ud920\ud921\ud922\ud923\ud924\ud925\ud926\ud927\ud928\ud929\ud92a\ud92b\ud92c\ud92d\ud92e\ud92f\ud930\ud931\ud932\ud933\ud934\ud935\ud936\ud937\ud938\ud939\ud93a\ud93b\ud93c\ud93d\ud93e\ud93f\ud940\ud941\ud942\ud943\ud944\ud945\ud946\ud947\ud948\ud949\ud94a\ud94b\ud94c\ud94d\ud94e\ud94f\ud950\ud951\ud952\ud953\ud954\ud955\ud956\ud957\ud958\ud959\ud95a\ud95b\ud95c\ud95d\ud95e\ud95f\ud960\ud961\ud962\ud963\ud964\ud965\ud966\ud967\ud968\ud969\ud96a\ud96b\ud96c\ud96d\ud96e\ud96f\ud970\ud971\ud972\ud973\ud974\ud975\ud976\ud977\ud978\ud979\ud97a\ud97b\ud97c\ud97d\ud97e\ud97f\ud980\ud981\ud982\ud983\ud984\ud985\ud986\ud987\ud988\ud989\ud98a\ud98b\ud98c\ud98d\ud98e\ud98f\ud990\ud991\ud992\ud993\ud994\ud995\ud996\ud997\ud998\ud999\ud99a\ud99b\ud99c\ud99d\ud99e\ud99f\ud9a0\ud9a1\ud9a2\ud9a3\ud9a4\ud9a5\ud9a6\ud9a7\ud9a8\ud9a9\ud9aa\ud9ab\ud9ac\ud9ad\ud9ae\ud9af\ud9b0\ud9b1\ud9b2\ud9b3\ud9b4\ud9b5\ud9b6\ud9b7\ud9b8\ud9b9\ud9ba\ud9bb\ud9bc\ud9bd\ud9be\ud9bf\ud9c0\ud9c1\ud9c2\ud9c3\ud9c4\ud9c5\ud9c6\ud9c7\ud9c8\ud9c9\ud9ca\ud9cb\ud9cc\ud9cd\ud9ce\ud9cf\ud9d0\ud9d1\ud9d2\ud9d3\ud9d4\ud9d5\ud9d6\ud9d7\ud9d8\ud9d9\ud9da\ud9db\ud9dc\ud9dd\ud9de\ud9df\ud9e0\ud9e1\ud9e2\ud9e3\ud9e4\ud9e5\ud9e6\ud9e7\ud9e8\ud9e9\ud9ea\ud9eb\ud9ec\ud9ed\ud9ee\ud9ef\ud9f0\ud9f1\ud9f2\ud9f3\ud9f4\ud9f5\ud9f6\ud9f7\ud9f8\ud9f9\ud9fa\ud9fb\ud9fc\ud9fd\ud9fe\ud9ff\uda00\uda01\uda02\uda03\uda04\uda05\uda06\uda07\uda08\uda09\uda0a\uda0b\uda0c\uda0d\uda0e\uda0f\uda10\uda11\uda12\uda13\uda14\uda15\uda16\uda17\uda18\uda19\uda1a\uda1b\uda1c\uda1d\uda1e\uda1f\uda20\uda21\uda22\uda23\uda24\uda25\uda26\uda27\uda28\uda29\uda2a\uda2b\uda2c\uda2d\uda2e\uda2f\uda30\uda31\uda32\uda33\uda34\uda35\uda36\uda37\uda38\uda39\uda3a\uda3b\uda3c\uda3d\uda3e\uda3f\uda40\uda41\uda42\uda43\uda44\uda45\uda46\uda47\uda48\uda49\uda4a\uda4b\uda4c\uda4d\uda4e\uda4f\uda50\uda51\uda52\uda53\uda54\uda55\uda56\uda57\uda58\uda59\uda5a\uda5b\uda5c\uda5d\uda5e\uda5f\uda60\uda61\uda62\uda63\uda64\uda65\uda66\uda67\uda68\uda69\uda6a\uda6b\uda6c\uda6d\uda6e\uda6f\uda70\uda71\uda72\uda73\uda74\uda75\uda76\uda77\uda78\uda79\uda7a\uda7b\uda7c\uda7d\uda7e\uda7f\uda80\uda81\uda82\uda83\uda84\uda85\uda86\uda87\uda88\uda89\uda8a\uda8b\uda8c\uda8d\uda8e\uda8f\uda90\uda91\uda92\uda93\uda94\uda95\uda96\uda97\uda98\uda99\uda9a\uda9b\uda9c\uda9d\uda9e\uda9f\udaa0\udaa1\udaa2\udaa3\udaa4\udaa5\udaa6\udaa7\udaa8\udaa9\udaaa\udaab\udaac\udaad\udaae\udaaf\udab0\udab1\udab2\udab3\udab4\udab5\udab6\udab7\udab8\udab9\udaba\udabb\udabc\udabd\udabe\udabf\udac0\udac1\udac2\udac3\udac4\udac5\udac6\udac7\udac8\udac9\udaca\udacb\udacc\udacd\udace\udacf\udad0\udad1\udad2\udad3\udad4\udad5\udad6\udad7\udad8\udad9\udada\udadb\udadc\udadd\udade\udadf\udae0\udae1\udae2\udae3\udae4\udae5\udae6\udae7\udae8\udae9\udaea\udaeb\udaec\udaed\udaee\udaef\udaf0\udaf1\udaf2\udaf3\udaf4\udaf5\udaf6\udaf7\udaf8\udaf9\udafa\udafb\udafc\udafd\udafe\udaff\udb00\udb01\udb02\udb03\udb04\udb05\udb06\udb07\udb08\udb09\udb0a\udb0b\udb0c\udb0d\udb0e\udb0f\udb10\udb11\udb12\udb13\udb14\udb15\udb16\udb17\udb18\udb19\udb1a\udb1b\udb1c\udb1d\udb1e\udb1f\udb20\udb21\udb22\udb23\udb24\udb25\udb26\udb27\udb28\udb29\udb2a\udb2b\udb2c\udb2d\udb2e\udb2f\udb30\udb31\udb32\udb33\udb34\udb35\udb36\udb37\udb38\udb39\udb3a\udb3b\udb3c\udb3d\udb3e\udb3f\udb40\udb41\udb42\udb43\udb44\udb45\udb46\udb47\udb48\udb49\udb4a\udb4b\udb4c\udb4d\udb4e\udb4f\udb50\udb51\udb52\udb53\udb54\udb55\udb56\udb57\udb58\udb59\udb5a\udb5b\udb5c\udb5d\udb5e\udb5f\udb60\udb61\udb62\udb63\udb64\udb65\udb66\udb67\udb68\udb69\udb6a\udb6b\udb6c\udb6d\udb6e\udb6f\udb70\udb71\udb72\udb73\udb74\udb75\udb76\udb77\udb78\udb79\udb7a\udb7b\udb7c\udb7d\udb7e\udb7f\udb80\udb81\udb82\udb83\udb84\udb85\udb86\udb87\udb88\udb89\udb8a\udb8b\udb8c\udb8d\udb8e\udb8f\udb90\udb91\udb92\udb93\udb94\udb95\udb96\udb97\udb98\udb99\udb9a\udb9b\udb9c\udb9d\udb9e\udb9f\udba0\udba1\udba2\udba3\udba4\udba5\udba6\udba7\udba8\udba9\udbaa\udbab\udbac\udbad\udbae\udbaf\udbb0\udbb1\udbb2\udbb3\udbb4\udbb5\udbb6\udbb7\udbb8\udbb9\udbba\udbbb\udbbc\udbbd\udbbe\udbbf\udbc0\udbc1\udbc2\udbc3\udbc4\udbc5\udbc6\udbc7\udbc8\udbc9\udbca\udbcb\udbcc\udbcd\udbce\udbcf\udbd0\udbd1\udbd2\udbd3\udbd4\udbd5\udbd6\udbd7\udbd8\udbd9\udbda\udbdb\udbdc\udbdd\udbde\udbdf\udbe0\udbe1\udbe2\udbe3\udbe4\udbe5\udbe6\udbe7\udbe8\udbe9\udbea\udbeb\udbec\udbed\udbee\udbef\udbf0\udbf1\udbf2\udbf3\udbf4\udbf5\udbf6\udbf7\udbf8\udbf9\udbfa\udbfb\udbfc\udbfd\udbfe\U0010fc00\udc01\udc02\udc03\udc04\udc05\udc06\udc07\udc08\udc09\udc0a\udc0b\udc0c\udc0d\udc0e\udc0f\udc10\udc11\udc12\udc13\udc14\udc15\udc16\udc17\udc18\udc19\udc1a\udc1b\udc1c\udc1d\udc1e\udc1f\udc20\udc21\udc22\udc23\udc24\udc25\udc26\udc27\udc28\udc29\udc2a\udc2b\udc2c\udc2d\udc2e\udc2f\udc30\udc31\udc32\udc33\udc34\udc35\udc36\udc37\udc38\udc39\udc3a\udc3b\udc3c\udc3d\udc3e\udc3f\udc40\udc41\udc42\udc43\udc44\udc45\udc46\udc47\udc48\udc49\udc4a\udc4b\udc4c\udc4d\udc4e\udc4f\udc50\udc51\udc52\udc53\udc54\udc55\udc56\udc57\udc58\udc59\udc5a\udc5b\udc5c\udc5d\udc5e\udc5f\udc60\udc61\udc62\udc63\udc64\udc65\udc66\udc67\udc68\udc69\udc6a\udc6b\udc6c\udc6d\udc6e\udc6f\udc70\udc71\udc72\udc73\udc74\udc75\udc76\udc77\udc78\udc79\udc7a\udc7b\udc7c\udc7d\udc7e\udc7f\udc80\udc81\udc82\udc83\udc84\udc85\udc86\udc87\udc88\udc89\udc8a\udc8b\udc8c\udc8d\udc8e\udc8f\udc90\udc91\udc92\udc93\udc94\udc95\udc96\udc97\udc98\udc99\udc9a\udc9b\udc9c\udc9d\udc9e\udc9f\udca0\udca1\udca2\udca3\udca4\udca5\udca6\udca7\udca8\udca9\udcaa\udcab\udcac\udcad\udcae\udcaf\udcb0\udcb1\udcb2\udcb3\udcb4\udcb5\udcb6\udcb7\udcb8\udcb9\udcba\udcbb\udcbc\udcbd\udcbe\udcbf\udcc0\udcc1\udcc2\udcc3\udcc4\udcc5\udcc6\udcc7\udcc8\udcc9\udcca\udccb\udccc\udccd\udcce\udccf\udcd0\udcd1\udcd2\udcd3\udcd4\udcd5\udcd6\udcd7\udcd8\udcd9\udcda\udcdb\udcdc\udcdd\udcde\udcdf\udce0\udce1\udce2\udce3\udce4\udce5\udce6\udce7\udce8\udce9\udcea\udceb\udcec\udced\udcee\udcef\udcf0\udcf1\udcf2\udcf3\udcf4\udcf5\udcf6\udcf7\udcf8\udcf9\udcfa\udcfb\udcfc\udcfd\udcfe\udcff\udd00\udd01\udd02\udd03\udd04\udd05\udd06\udd07\udd08\udd09\udd0a\udd0b\udd0c\udd0d\udd0e\udd0f\udd10\udd11\udd12\udd13\udd14\udd15\udd16\udd17\udd18\udd19\udd1a\udd1b\udd1c\udd1d\udd1e\udd1f\udd20\udd21\udd22\udd23\udd24\udd25\udd26\udd27\udd28\udd29\udd2a\udd2b\udd2c\udd2d\udd2e\udd2f\udd30\udd31\udd32\udd33\udd34\udd35\udd36\udd37\udd38\udd39\udd3a\udd3b\udd3c\udd3d\udd3e\udd3f\udd40\udd41\udd42\udd43\udd44\udd45\udd46\udd47\udd48\udd49\udd4a\udd4b\udd4c\udd4d\udd4e\udd4f\udd50\udd51\udd52\udd53\udd54\udd55\udd56\udd57\udd58\udd59\udd5a\udd5b\udd5c\udd5d\udd5e\udd5f\udd60\udd61\udd62\udd63\udd64\udd65\udd66\udd67\udd68\udd69\udd6a\udd6b\udd6c\udd6d\udd6e\udd6f\udd70\udd71\udd72\udd73\udd74\udd75\udd76\udd77\udd78\udd79\udd7a\udd7b\udd7c\udd7d\udd7e\udd7f\udd80\udd81\udd82\udd83\udd84\udd85\udd86\udd87\udd88\udd89\udd8a\udd8b\udd8c\udd8d\udd8e\udd8f\udd90\udd91\udd92\udd93\udd94\udd95\udd96\udd97\udd98\udd99\udd9a\udd9b\udd9c\udd9d\udd9e\udd9f\udda0\udda1\udda2\udda3\udda4\udda5\udda6\udda7\udda8\udda9\uddaa\uddab\uddac\uddad\uddae\uddaf\uddb0\uddb1\uddb2\uddb3\uddb4\uddb5\uddb6\uddb7\uddb8\uddb9\uddba\uddbb\uddbc\uddbd\uddbe\uddbf\uddc0\uddc1\uddc2\uddc3\uddc4\uddc5\uddc6\uddc7\uddc8\uddc9\uddca\uddcb\uddcc\uddcd\uddce\uddcf\uddd0\uddd1\uddd2\uddd3\uddd4\uddd5\uddd6\uddd7\uddd8\uddd9\uddda\udddb\udddc\udddd\uddde\udddf\udde0\udde1\udde2\udde3\udde4\udde5\udde6\udde7\udde8\udde9\uddea\uddeb\uddec\udded\uddee\uddef\uddf0\uddf1\uddf2\uddf3\uddf4\uddf5\uddf6\uddf7\uddf8\uddf9\uddfa\uddfb\uddfc\uddfd\uddfe\uddff\ude00\ude01\ude02\ude03\ude04\ude05\ude06\ude07\ude08\ude09\ude0a\ude0b\ude0c\ude0d\ude0e\ude0f\ude10\ude11\ude12\ude13\ude14\ude15\ude16\ude17\ude18\ude19\ude1a\ude1b\ude1c\ude1d\ude1e\ude1f\ude20\ude21\ude22\ude23\ude24\ude25\ude26\ude27\ude28\ude29\ude2a\ude2b\ude2c\ude2d\ude2e\ude2f\ude30\ude31\ude32\ude33\ude34\ude35\ude36\ude37\ude38\ude39\ude3a\ude3b\ude3c\ude3d\ude3e\ude3f\ude40\ude41\ude42\ude43\ude44\ude45\ude46\ude47\ude48\ude49\ude4a\ude4b\ude4c\ude4d\ude4e\ude4f\ude50\ude51\ude52\ude53\ude54\ude55\ude56\ude57\ude58\ude59\ude5a\ude5b\ude5c\ude5d\ude5e\ude5f\ude60\ude61\ude62\ude63\ude64\ude65\ude66\ude67\ude68\ude69\ude6a\ude6b\ude6c\ude6d\ude6e\ude6f\ude70\ude71\ude72\ude73\ude74\ude75\ude76\ude77\ude78\ude79\ude7a\ude7b\ude7c\ude7d\ude7e\ude7f\ude80\ude81\ude82\ude83\ude84\ude85\ude86\ude87\ude88\ude89\ude8a\ude8b\ude8c\ude8d\ude8e\ude8f\ude90\ude91\ude92\ude93\ude94\ude95\ude96\ude97\ude98\ude99\ude9a\ude9b\ude9c\ude9d\ude9e\ude9f\udea0\udea1\udea2\udea3\udea4\udea5\udea6\udea7\udea8\udea9\udeaa\udeab\udeac\udead\udeae\udeaf\udeb0\udeb1\udeb2\udeb3\udeb4\udeb5\udeb6\udeb7\udeb8\udeb9\udeba\udebb\udebc\udebd\udebe\udebf\udec0\udec1\udec2\udec3\udec4\udec5\udec6\udec7\udec8\udec9\udeca\udecb\udecc\udecd\udece\udecf\uded0\uded1\uded2\uded3\uded4\uded5\uded6\uded7\uded8\uded9\udeda\udedb\udedc\udedd\udede\udedf\udee0\udee1\udee2\udee3\udee4\udee5\udee6\udee7\udee8\udee9\udeea\udeeb\udeec\udeed\udeee\udeef\udef0\udef1\udef2\udef3\udef4\udef5\udef6\udef7\udef8\udef9\udefa\udefb\udefc\udefd\udefe\udeff\udf00\udf01\udf02\udf03\udf04\udf05\udf06\udf07\udf08\udf09\udf0a\udf0b\udf0c\udf0d\udf0e\udf0f\udf10\udf11\udf12\udf13\udf14\udf15\udf16\udf17\udf18\udf19\udf1a\udf1b\udf1c\udf1d\udf1e\udf1f\udf20\udf21\udf22\udf23\udf24\udf25\udf26\udf27\udf28\udf29\udf2a\udf2b\udf2c\udf2d\udf2e\udf2f\udf30\udf31\udf32\udf33\udf34\udf35\udf36\udf37\udf38\udf39\udf3a\udf3b\udf3c\udf3d\udf3e\udf3f\udf40\udf41\udf42\udf43\udf44\udf45\udf46\udf47\udf48\udf49\udf4a\udf4b\udf4c\udf4d\udf4e\udf4f\udf50\udf51\udf52\udf53\udf54\udf55\udf56\udf57\udf58\udf59\udf5a\udf5b\udf5c\udf5d\udf5e\udf5f\udf60\udf61\udf62\udf63\udf64\udf65\udf66\udf67\udf68\udf69\udf6a\udf6b\udf6c\udf6d\udf6e\udf6f\udf70\udf71\udf72\udf73\udf74\udf75\udf76\udf77\udf78\udf79\udf7a\udf7b\udf7c\udf7d\udf7e\udf7f\udf80\udf81\udf82\udf83\udf84\udf85\udf86\udf87\udf88\udf89\udf8a\udf8b\udf8c\udf8d\udf8e\udf8f\udf90\udf91\udf92\udf93\udf94\udf95\udf96\udf97\udf98\udf99\udf9a\udf9b\udf9c\udf9d\udf9e\udf9f\udfa0\udfa1\udfa2\udfa3\udfa4\udfa5\udfa6\udfa7\udfa8\udfa9\udfaa\udfab\udfac\udfad\udfae\udfaf\udfb0\udfb1\udfb2\udfb3\udfb4\udfb5\udfb6\udfb7\udfb8\udfb9\udfba\udfbb\udfbc\udfbd\udfbe\udfbf\udfc0\udfc1\udfc2\udfc3\udfc4\udfc5\udfc6\udfc7\udfc8\udfc9\udfca\udfcb\udfcc\udfcd\udfce\udfcf\udfd0\udfd1\udfd2\udfd3\udfd4\udfd5\udfd6\udfd7\udfd8\udfd9\udfda\udfdb\udfdc\udfdd\udfde\udfdf\udfe0\udfe1\udfe2\udfe3\udfe4\udfe5\udfe6\udfe7\udfe8\udfe9\udfea\udfeb\udfec\udfed\udfee\udfef\udff0\udff1\udff2\udff3\udff4\udff5\udff6\udff7\udff8\udff9\udffa\udffb\udffc\udffd\udffe\udfff'") +except UnicodeDecodeError: + Cs = '' # Jython can't handle isolated surrogates + +Ll = u'abcdefghijklmnopqrstuvwxyz\xaa\xb5\xba\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff\u0101\u0103\u0105\u0107\u0109\u010b\u010d\u010f\u0111\u0113\u0115\u0117\u0119\u011b\u011d\u011f\u0121\u0123\u0125\u0127\u0129\u012b\u012d\u012f\u0131\u0133\u0135\u0137\u0138\u013a\u013c\u013e\u0140\u0142\u0144\u0146\u0148\u0149\u014b\u014d\u014f\u0151\u0153\u0155\u0157\u0159\u015b\u015d\u015f\u0161\u0163\u0165\u0167\u0169\u016b\u016d\u016f\u0171\u0173\u0175\u0177\u017a\u017c\u017e\u017f\u0180\u0183\u0185\u0188\u018c\u018d\u0192\u0195\u0199\u019a\u019b\u019e\u01a1\u01a3\u01a5\u01a8\u01aa\u01ab\u01ad\u01b0\u01b4\u01b6\u01b9\u01ba\u01bd\u01be\u01bf\u01c6\u01c9\u01cc\u01ce\u01d0\u01d2\u01d4\u01d6\u01d8\u01da\u01dc\u01dd\u01df\u01e1\u01e3\u01e5\u01e7\u01e9\u01eb\u01ed\u01ef\u01f0\u01f3\u01f5\u01f9\u01fb\u01fd\u01ff\u0201\u0203\u0205\u0207\u0209\u020b\u020d\u020f\u0211\u0213\u0215\u0217\u0219\u021b\u021d\u021f\u0221\u0223\u0225\u0227\u0229\u022b\u022d\u022f\u0231\u0233\u0234\u0235\u0236\u0237\u0238\u0239\u023c\u023f\u0240\u0250\u0251\u0252\u0253\u0254\u0255\u0256\u0257\u0258\u0259\u025a\u025b\u025c\u025d\u025e\u025f\u0260\u0261\u0262\u0263\u0264\u0265\u0266\u0267\u0268\u0269\u026a\u026b\u026c\u026d\u026e\u026f\u0270\u0271\u0272\u0273\u0274\u0275\u0276\u0277\u0278\u0279\u027a\u027b\u027c\u027d\u027e\u027f\u0280\u0281\u0282\u0283\u0284\u0285\u0286\u0287\u0288\u0289\u028a\u028b\u028c\u028d\u028e\u028f\u0290\u0291\u0292\u0293\u0294\u0295\u0296\u0297\u0298\u0299\u029a\u029b\u029c\u029d\u029e\u029f\u02a0\u02a1\u02a2\u02a3\u02a4\u02a5\u02a6\u02a7\u02a8\u02a9\u02aa\u02ab\u02ac\u02ad\u02ae\u02af\u0390\u03ac\u03ad\u03ae\u03af\u03b0\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u03ca\u03cb\u03cc\u03cd\u03ce\u03d0\u03d1\u03d5\u03d6\u03d7\u03d9\u03db\u03dd\u03df\u03e1\u03e3\u03e5\u03e7\u03e9\u03eb\u03ed\u03ef\u03f0\u03f1\u03f2\u03f3\u03f5\u03f8\u03fb\u03fc\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u044f\u0450\u0451\u0452\u0453\u0454\u0455\u0456\u0457\u0458\u0459\u045a\u045b\u045c\u045d\u045e\u045f\u0461\u0463\u0465\u0467\u0469\u046b\u046d\u046f\u0471\u0473\u0475\u0477\u0479\u047b\u047d\u047f\u0481\u048b\u048d\u048f\u0491\u0493\u0495\u0497\u0499\u049b\u049d\u049f\u04a1\u04a3\u04a5\u04a7\u04a9\u04ab\u04ad\u04af\u04b1\u04b3\u04b5\u04b7\u04b9\u04bb\u04bd\u04bf\u04c2\u04c4\u04c6\u04c8\u04ca\u04cc\u04ce\u04d1\u04d3\u04d5\u04d7\u04d9\u04db\u04dd\u04df\u04e1\u04e3\u04e5\u04e7\u04e9\u04eb\u04ed\u04ef\u04f1\u04f3\u04f5\u04f7\u04f9\u0501\u0503\u0505\u0507\u0509\u050b\u050d\u050f\u0561\u0562\u0563\u0564\u0565\u0566\u0567\u0568\u0569\u056a\u056b\u056c\u056d\u056e\u056f\u0570\u0571\u0572\u0573\u0574\u0575\u0576\u0577\u0578\u0579\u057a\u057b\u057c\u057d\u057e\u057f\u0580\u0581\u0582\u0583\u0584\u0585\u0586\u0587\u1d00\u1d01\u1d02\u1d03\u1d04\u1d05\u1d06\u1d07\u1d08\u1d09\u1d0a\u1d0b\u1d0c\u1d0d\u1d0e\u1d0f\u1d10\u1d11\u1d12\u1d13\u1d14\u1d15\u1d16\u1d17\u1d18\u1d19\u1d1a\u1d1b\u1d1c\u1d1d\u1d1e\u1d1f\u1d20\u1d21\u1d22\u1d23\u1d24\u1d25\u1d26\u1d27\u1d28\u1d29\u1d2a\u1d2b\u1d62\u1d63\u1d64\u1d65\u1d66\u1d67\u1d68\u1d69\u1d6a\u1d6b\u1d6c\u1d6d\u1d6e\u1d6f\u1d70\u1d71\u1d72\u1d73\u1d74\u1d75\u1d76\u1d77\u1d79\u1d7a\u1d7b\u1d7c\u1d7d\u1d7e\u1d7f\u1d80\u1d81\u1d82\u1d83\u1d84\u1d85\u1d86\u1d87\u1d88\u1d89\u1d8a\u1d8b\u1d8c\u1d8d\u1d8e\u1d8f\u1d90\u1d91\u1d92\u1d93\u1d94\u1d95\u1d96\u1d97\u1d98\u1d99\u1d9a\u1e01\u1e03\u1e05\u1e07\u1e09\u1e0b\u1e0d\u1e0f\u1e11\u1e13\u1e15\u1e17\u1e19\u1e1b\u1e1d\u1e1f\u1e21\u1e23\u1e25\u1e27\u1e29\u1e2b\u1e2d\u1e2f\u1e31\u1e33\u1e35\u1e37\u1e39\u1e3b\u1e3d\u1e3f\u1e41\u1e43\u1e45\u1e47\u1e49\u1e4b\u1e4d\u1e4f\u1e51\u1e53\u1e55\u1e57\u1e59\u1e5b\u1e5d\u1e5f\u1e61\u1e63\u1e65\u1e67\u1e69\u1e6b\u1e6d\u1e6f\u1e71\u1e73\u1e75\u1e77\u1e79\u1e7b\u1e7d\u1e7f\u1e81\u1e83\u1e85\u1e87\u1e89\u1e8b\u1e8d\u1e8f\u1e91\u1e93\u1e95\u1e96\u1e97\u1e98\u1e99\u1e9a\u1e9b\u1ea1\u1ea3\u1ea5\u1ea7\u1ea9\u1eab\u1ead\u1eaf\u1eb1\u1eb3\u1eb5\u1eb7\u1eb9\u1ebb\u1ebd\u1ebf\u1ec1\u1ec3\u1ec5\u1ec7\u1ec9\u1ecb\u1ecd\u1ecf\u1ed1\u1ed3\u1ed5\u1ed7\u1ed9\u1edb\u1edd\u1edf\u1ee1\u1ee3\u1ee5\u1ee7\u1ee9\u1eeb\u1eed\u1eef\u1ef1\u1ef3\u1ef5\u1ef7\u1ef9\u1f00\u1f01\u1f02\u1f03\u1f04\u1f05\u1f06\u1f07\u1f10\u1f11\u1f12\u1f13\u1f14\u1f15\u1f20\u1f21\u1f22\u1f23\u1f24\u1f25\u1f26\u1f27\u1f30\u1f31\u1f32\u1f33\u1f34\u1f35\u1f36\u1f37\u1f40\u1f41\u1f42\u1f43\u1f44\u1f45\u1f50\u1f51\u1f52\u1f53\u1f54\u1f55\u1f56\u1f57\u1f60\u1f61\u1f62\u1f63\u1f64\u1f65\u1f66\u1f67\u1f70\u1f71\u1f72\u1f73\u1f74\u1f75\u1f76\u1f77\u1f78\u1f79\u1f7a\u1f7b\u1f7c\u1f7d\u1f80\u1f81\u1f82\u1f83\u1f84\u1f85\u1f86\u1f87\u1f90\u1f91\u1f92\u1f93\u1f94\u1f95\u1f96\u1f97\u1fa0\u1fa1\u1fa2\u1fa3\u1fa4\u1fa5\u1fa6\u1fa7\u1fb0\u1fb1\u1fb2\u1fb3\u1fb4\u1fb6\u1fb7\u1fbe\u1fc2\u1fc3\u1fc4\u1fc6\u1fc7\u1fd0\u1fd1\u1fd2\u1fd3\u1fd6\u1fd7\u1fe0\u1fe1\u1fe2\u1fe3\u1fe4\u1fe5\u1fe6\u1fe7\u1ff2\u1ff3\u1ff4\u1ff6\u1ff7\u2071\u207f\u210a\u210e\u210f\u2113\u212f\u2134\u2139\u213c\u213d\u2146\u2147\u2148\u2149\u2c30\u2c31\u2c32\u2c33\u2c34\u2c35\u2c36\u2c37\u2c38\u2c39\u2c3a\u2c3b\u2c3c\u2c3d\u2c3e\u2c3f\u2c40\u2c41\u2c42\u2c43\u2c44\u2c45\u2c46\u2c47\u2c48\u2c49\u2c4a\u2c4b\u2c4c\u2c4d\u2c4e\u2c4f\u2c50\u2c51\u2c52\u2c53\u2c54\u2c55\u2c56\u2c57\u2c58\u2c59\u2c5a\u2c5b\u2c5c\u2c5d\u2c5e\u2c81\u2c83\u2c85\u2c87\u2c89\u2c8b\u2c8d\u2c8f\u2c91\u2c93\u2c95\u2c97\u2c99\u2c9b\u2c9d\u2c9f\u2ca1\u2ca3\u2ca5\u2ca7\u2ca9\u2cab\u2cad\u2caf\u2cb1\u2cb3\u2cb5\u2cb7\u2cb9\u2cbb\u2cbd\u2cbf\u2cc1\u2cc3\u2cc5\u2cc7\u2cc9\u2ccb\u2ccd\u2ccf\u2cd1\u2cd3\u2cd5\u2cd7\u2cd9\u2cdb\u2cdd\u2cdf\u2ce1\u2ce3\u2ce4\u2d00\u2d01\u2d02\u2d03\u2d04\u2d05\u2d06\u2d07\u2d08\u2d09\u2d0a\u2d0b\u2d0c\u2d0d\u2d0e\u2d0f\u2d10\u2d11\u2d12\u2d13\u2d14\u2d15\u2d16\u2d17\u2d18\u2d19\u2d1a\u2d1b\u2d1c\u2d1d\u2d1e\u2d1f\u2d20\u2d21\u2d22\u2d23\u2d24\u2d25\ufb00\ufb01\ufb02\ufb03\ufb04\ufb05\ufb06\ufb13\ufb14\ufb15\ufb16\ufb17\uff41\uff42\uff43\uff44\uff45\uff46\uff47\uff48\uff49\uff4a\uff4b\uff4c\uff4d\uff4e\uff4f\uff50\uff51\uff52\uff53\uff54\uff55\uff56\uff57\uff58\uff59\uff5a' + +Lm = u'\u02b0\u02b1\u02b2\u02b3\u02b4\u02b5\u02b6\u02b7\u02b8\u02b9\u02ba\u02bb\u02bc\u02bd\u02be\u02bf\u02c0\u02c1\u02c6\u02c7\u02c8\u02c9\u02ca\u02cb\u02cc\u02cd\u02ce\u02cf\u02d0\u02d1\u02e0\u02e1\u02e2\u02e3\u02e4\u02ee\u037a\u0559\u0640\u06e5\u06e6\u0e46\u0ec6\u10fc\u17d7\u1843\u1d2c\u1d2d\u1d2e\u1d2f\u1d30\u1d31\u1d32\u1d33\u1d34\u1d35\u1d36\u1d37\u1d38\u1d39\u1d3a\u1d3b\u1d3c\u1d3d\u1d3e\u1d3f\u1d40\u1d41\u1d42\u1d43\u1d44\u1d45\u1d46\u1d47\u1d48\u1d49\u1d4a\u1d4b\u1d4c\u1d4d\u1d4e\u1d4f\u1d50\u1d51\u1d52\u1d53\u1d54\u1d55\u1d56\u1d57\u1d58\u1d59\u1d5a\u1d5b\u1d5c\u1d5d\u1d5e\u1d5f\u1d60\u1d61\u1d78\u1d9b\u1d9c\u1d9d\u1d9e\u1d9f\u1da0\u1da1\u1da2\u1da3\u1da4\u1da5\u1da6\u1da7\u1da8\u1da9\u1daa\u1dab\u1dac\u1dad\u1dae\u1daf\u1db0\u1db1\u1db2\u1db3\u1db4\u1db5\u1db6\u1db7\u1db8\u1db9\u1dba\u1dbb\u1dbc\u1dbd\u1dbe\u1dbf\u2090\u2091\u2092\u2093\u2094\u2d6f\u3005\u3031\u3032\u3033\u3034\u3035\u303b\u309d\u309e\u30fc\u30fd\u30fe\ua015\uff70\uff9e\uff9f' + +Lo = u'\u01bb\u01c0\u01c1\u01c2\u01c3\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\u05f0\u05f1\u05f2\u0621\u0622\u0623\u0624\u0625\u0626\u0627\u0628\u0629\u062a\u062b\u062c\u062d\u062e\u062f\u0630\u0631\u0632\u0633\u0634\u0635\u0636\u0637\u0638\u0639\u063a\u0641\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a\u066e\u066f\u0671\u0672\u0673\u0674\u0675\u0676\u0677\u0678\u0679\u067a\u067b\u067c\u067d\u067e\u067f\u0680\u0681\u0682\u0683\u0684\u0685\u0686\u0687\u0688\u0689\u068a\u068b\u068c\u068d\u068e\u068f\u0690\u0691\u0692\u0693\u0694\u0695\u0696\u0697\u0698\u0699\u069a\u069b\u069c\u069d\u069e\u069f\u06a0\u06a1\u06a2\u06a3\u06a4\u06a5\u06a6\u06a7\u06a8\u06a9\u06aa\u06ab\u06ac\u06ad\u06ae\u06af\u06b0\u06b1\u06b2\u06b3\u06b4\u06b5\u06b6\u06b7\u06b8\u06b9\u06ba\u06bb\u06bc\u06bd\u06be\u06bf\u06c0\u06c1\u06c2\u06c3\u06c4\u06c5\u06c6\u06c7\u06c8\u06c9\u06ca\u06cb\u06cc\u06cd\u06ce\u06cf\u06d0\u06d1\u06d2\u06d3\u06d5\u06ee\u06ef\u06fa\u06fb\u06fc\u06ff\u0710\u0712\u0713\u0714\u0715\u0716\u0717\u0718\u0719\u071a\u071b\u071c\u071d\u071e\u071f\u0720\u0721\u0722\u0723\u0724\u0725\u0726\u0727\u0728\u0729\u072a\u072b\u072c\u072d\u072e\u072f\u074d\u074e\u074f\u0750\u0751\u0752\u0753\u0754\u0755\u0756\u0757\u0758\u0759\u075a\u075b\u075c\u075d\u075e\u075f\u0760\u0761\u0762\u0763\u0764\u0765\u0766\u0767\u0768\u0769\u076a\u076b\u076c\u076d\u0780\u0781\u0782\u0783\u0784\u0785\u0786\u0787\u0788\u0789\u078a\u078b\u078c\u078d\u078e\u078f\u0790\u0791\u0792\u0793\u0794\u0795\u0796\u0797\u0798\u0799\u079a\u079b\u079c\u079d\u079e\u079f\u07a0\u07a1\u07a2\u07a3\u07a4\u07a5\u07b1\u0904\u0905\u0906\u0907\u0908\u0909\u090a\u090b\u090c\u090d\u090e\u090f\u0910\u0911\u0912\u0913\u0914\u0915\u0916\u0917\u0918\u0919\u091a\u091b\u091c\u091d\u091e\u091f\u0920\u0921\u0922\u0923\u0924\u0925\u0926\u0927\u0928\u0929\u092a\u092b\u092c\u092d\u092e\u092f\u0930\u0931\u0932\u0933\u0934\u0935\u0936\u0937\u0938\u0939\u093d\u0950\u0958\u0959\u095a\u095b\u095c\u095d\u095e\u095f\u0960\u0961\u097d\u0985\u0986\u0987\u0988\u0989\u098a\u098b\u098c\u098f\u0990\u0993\u0994\u0995\u0996\u0997\u0998\u0999\u099a\u099b\u099c\u099d\u099e\u099f\u09a0\u09a1\u09a2\u09a3\u09a4\u09a5\u09a6\u09a7\u09a8\u09aa\u09ab\u09ac\u09ad\u09ae\u09af\u09b0\u09b2\u09b6\u09b7\u09b8\u09b9\u09bd\u09ce\u09dc\u09dd\u09df\u09e0\u09e1\u09f0\u09f1\u0a05\u0a06\u0a07\u0a08\u0a09\u0a0a\u0a0f\u0a10\u0a13\u0a14\u0a15\u0a16\u0a17\u0a18\u0a19\u0a1a\u0a1b\u0a1c\u0a1d\u0a1e\u0a1f\u0a20\u0a21\u0a22\u0a23\u0a24\u0a25\u0a26\u0a27\u0a28\u0a2a\u0a2b\u0a2c\u0a2d\u0a2e\u0a2f\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59\u0a5a\u0a5b\u0a5c\u0a5e\u0a72\u0a73\u0a74\u0a85\u0a86\u0a87\u0a88\u0a89\u0a8a\u0a8b\u0a8c\u0a8d\u0a8f\u0a90\u0a91\u0a93\u0a94\u0a95\u0a96\u0a97\u0a98\u0a99\u0a9a\u0a9b\u0a9c\u0a9d\u0a9e\u0a9f\u0aa0\u0aa1\u0aa2\u0aa3\u0aa4\u0aa5\u0aa6\u0aa7\u0aa8\u0aaa\u0aab\u0aac\u0aad\u0aae\u0aaf\u0ab0\u0ab2\u0ab3\u0ab5\u0ab6\u0ab7\u0ab8\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05\u0b06\u0b07\u0b08\u0b09\u0b0a\u0b0b\u0b0c\u0b0f\u0b10\u0b13\u0b14\u0b15\u0b16\u0b17\u0b18\u0b19\u0b1a\u0b1b\u0b1c\u0b1d\u0b1e\u0b1f\u0b20\u0b21\u0b22\u0b23\u0b24\u0b25\u0b26\u0b27\u0b28\u0b2a\u0b2b\u0b2c\u0b2d\u0b2e\u0b2f\u0b30\u0b32\u0b33\u0b35\u0b36\u0b37\u0b38\u0b39\u0b3d\u0b5c\u0b5d\u0b5f\u0b60\u0b61\u0b71\u0b83\u0b85\u0b86\u0b87\u0b88\u0b89\u0b8a\u0b8e\u0b8f\u0b90\u0b92\u0b93\u0b94\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8\u0ba9\u0baa\u0bae\u0baf\u0bb0\u0bb1\u0bb2\u0bb3\u0bb4\u0bb5\u0bb6\u0bb7\u0bb8\u0bb9\u0c05\u0c06\u0c07\u0c08\u0c09\u0c0a\u0c0b\u0c0c\u0c0e\u0c0f\u0c10\u0c12\u0c13\u0c14\u0c15\u0c16\u0c17\u0c18\u0c19\u0c1a\u0c1b\u0c1c\u0c1d\u0c1e\u0c1f\u0c20\u0c21\u0c22\u0c23\u0c24\u0c25\u0c26\u0c27\u0c28\u0c2a\u0c2b\u0c2c\u0c2d\u0c2e\u0c2f\u0c30\u0c31\u0c32\u0c33\u0c35\u0c36\u0c37\u0c38\u0c39\u0c60\u0c61\u0c85\u0c86\u0c87\u0c88\u0c89\u0c8a\u0c8b\u0c8c\u0c8e\u0c8f\u0c90\u0c92\u0c93\u0c94\u0c95\u0c96\u0c97\u0c98\u0c99\u0c9a\u0c9b\u0c9c\u0c9d\u0c9e\u0c9f\u0ca0\u0ca1\u0ca2\u0ca3\u0ca4\u0ca5\u0ca6\u0ca7\u0ca8\u0caa\u0cab\u0cac\u0cad\u0cae\u0caf\u0cb0\u0cb1\u0cb2\u0cb3\u0cb5\u0cb6\u0cb7\u0cb8\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0d05\u0d06\u0d07\u0d08\u0d09\u0d0a\u0d0b\u0d0c\u0d0e\u0d0f\u0d10\u0d12\u0d13\u0d14\u0d15\u0d16\u0d17\u0d18\u0d19\u0d1a\u0d1b\u0d1c\u0d1d\u0d1e\u0d1f\u0d20\u0d21\u0d22\u0d23\u0d24\u0d25\u0d26\u0d27\u0d28\u0d2a\u0d2b\u0d2c\u0d2d\u0d2e\u0d2f\u0d30\u0d31\u0d32\u0d33\u0d34\u0d35\u0d36\u0d37\u0d38\u0d39\u0d60\u0d61\u0d85\u0d86\u0d87\u0d88\u0d89\u0d8a\u0d8b\u0d8c\u0d8d\u0d8e\u0d8f\u0d90\u0d91\u0d92\u0d93\u0d94\u0d95\u0d96\u0d9a\u0d9b\u0d9c\u0d9d\u0d9e\u0d9f\u0da0\u0da1\u0da2\u0da3\u0da4\u0da5\u0da6\u0da7\u0da8\u0da9\u0daa\u0dab\u0dac\u0dad\u0dae\u0daf\u0db0\u0db1\u0db3\u0db4\u0db5\u0db6\u0db7\u0db8\u0db9\u0dba\u0dbb\u0dbd\u0dc0\u0dc1\u0dc2\u0dc3\u0dc4\u0dc5\u0dc6\u0e01\u0e02\u0e03\u0e04\u0e05\u0e06\u0e07\u0e08\u0e09\u0e0a\u0e0b\u0e0c\u0e0d\u0e0e\u0e0f\u0e10\u0e11\u0e12\u0e13\u0e14\u0e15\u0e16\u0e17\u0e18\u0e19\u0e1a\u0e1b\u0e1c\u0e1d\u0e1e\u0e1f\u0e20\u0e21\u0e22\u0e23\u0e24\u0e25\u0e26\u0e27\u0e28\u0e29\u0e2a\u0e2b\u0e2c\u0e2d\u0e2e\u0e2f\u0e30\u0e32\u0e33\u0e40\u0e41\u0e42\u0e43\u0e44\u0e45\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94\u0e95\u0e96\u0e97\u0e99\u0e9a\u0e9b\u0e9c\u0e9d\u0e9e\u0e9f\u0ea1\u0ea2\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead\u0eae\u0eaf\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0\u0ec1\u0ec2\u0ec3\u0ec4\u0edc\u0edd\u0f00\u0f40\u0f41\u0f42\u0f43\u0f44\u0f45\u0f46\u0f47\u0f49\u0f4a\u0f4b\u0f4c\u0f4d\u0f4e\u0f4f\u0f50\u0f51\u0f52\u0f53\u0f54\u0f55\u0f56\u0f57\u0f58\u0f59\u0f5a\u0f5b\u0f5c\u0f5d\u0f5e\u0f5f\u0f60\u0f61\u0f62\u0f63\u0f64\u0f65\u0f66\u0f67\u0f68\u0f69\u0f6a\u0f88\u0f89\u0f8a\u0f8b\u1000\u1001\u1002\u1003\u1004\u1005\u1006\u1007\u1008\u1009\u100a\u100b\u100c\u100d\u100e\u100f\u1010\u1011\u1012\u1013\u1014\u1015\u1016\u1017\u1018\u1019\u101a\u101b\u101c\u101d\u101e\u101f\u1020\u1021\u1023\u1024\u1025\u1026\u1027\u1029\u102a\u1050\u1051\u1052\u1053\u1054\u1055\u10d0\u10d1\u10d2\u10d3\u10d4\u10d5\u10d6\u10d7\u10d8\u10d9\u10da\u10db\u10dc\u10dd\u10de\u10df\u10e0\u10e1\u10e2\u10e3\u10e4\u10e5\u10e6\u10e7\u10e8\u10e9\u10ea\u10eb\u10ec\u10ed\u10ee\u10ef\u10f0\u10f1\u10f2\u10f3\u10f4\u10f5\u10f6\u10f7\u10f8\u10f9\u10fa\u1100\u1101\u1102\u1103\u1104\u1105\u1106\u1107\u1108\u1109\u110a\u110b\u110c\u110d\u110e\u110f\u1110\u1111\u1112\u1113\u1114\u1115\u1116\u1117\u1118\u1119\u111a\u111b\u111c\u111d\u111e\u111f\u1120\u1121\u1122\u1123\u1124\u1125\u1126\u1127\u1128\u1129\u112a\u112b\u112c\u112d\u112e\u112f\u1130\u1131\u1132\u1133\u1134\u1135\u1136\u1137\u1138\u1139\u113a\u113b\u113c\u113d\u113e\u113f\u1140\u1141\u1142\u1143\u1144\u1145\u1146\u1147\u1148\u1149\u114a\u114b\u114c\u114d\u114e\u114f\u1150\u1151\u1152\u1153\u1154\u1155\u1156\u1157\u1158\u1159\u115f\u1160\u1161\u1162\u1163\u1164\u1165\u1166\u1167\u1168\u1169\u116a\u116b\u116c\u116d\u116e\u116f\u1170\u1171\u1172\u1173\u1174\u1175\u1176\u1177\u1178\u1179\u117a\u117b\u117c\u117d\u117e\u117f\u1180\u1181\u1182\u1183\u1184\u1185\u1186\u1187\u1188\u1189\u118a\u118b\u118c\u118d\u118e\u118f\u1190\u1191\u1192\u1193\u1194\u1195\u1196\u1197\u1198\u1199\u119a\u119b\u119c\u119d\u119e\u119f\u11a0\u11a1\u11a2\u11a8\u11a9\u11aa\u11ab\u11ac\u11ad\u11ae\u11af\u11b0\u11b1\u11b2\u11b3\u11b4\u11b5\u11b6\u11b7\u11b8\u11b9\u11ba\u11bb\u11bc\u11bd\u11be\u11bf\u11c0\u11c1\u11c2\u11c3\u11c4\u11c5\u11c6\u11c7\u11c8\u11c9\u11ca\u11cb\u11cc\u11cd\u11ce\u11cf\u11d0\u11d1\u11d2\u11d3\u11d4\u11d5\u11d6\u11d7\u11d8\u11d9\u11da\u11db\u11dc\u11dd\u11de\u11df\u11e0\u11e1\u11e2\u11e3\u11e4\u11e5\u11e6\u11e7\u11e8\u11e9\u11ea\u11eb\u11ec\u11ed\u11ee\u11ef\u11f0\u11f1\u11f2\u11f3\u11f4\u11f5\u11f6\u11f7\u11f8\u11f9\u1200\u1201\u1202\u1203\u1204\u1205\u1206\u1207\u1208\u1209\u120a\u120b\u120c\u120d\u120e\u120f\u1210\u1211\u1212\u1213\u1214\u1215\u1216\u1217\u1218\u1219\u121a\u121b\u121c\u121d\u121e\u121f\u1220\u1221\u1222\u1223\u1224\u1225\u1226\u1227\u1228\u1229\u122a\u122b\u122c\u122d\u122e\u122f\u1230\u1231\u1232\u1233\u1234\u1235\u1236\u1237\u1238\u1239\u123a\u123b\u123c\u123d\u123e\u123f\u1240\u1241\u1242\u1243\u1244\u1245\u1246\u1247\u1248\u124a\u124b\u124c\u124d\u1250\u1251\u1252\u1253\u1254\u1255\u1256\u1258\u125a\u125b\u125c\u125d\u1260\u1261\u1262\u1263\u1264\u1265\u1266\u1267\u1268\u1269\u126a\u126b\u126c\u126d\u126e\u126f\u1270\u1271\u1272\u1273\u1274\u1275\u1276\u1277\u1278\u1279\u127a\u127b\u127c\u127d\u127e\u127f\u1280\u1281\u1282\u1283\u1284\u1285\u1286\u1287\u1288\u128a\u128b\u128c\u128d\u1290\u1291\u1292\u1293\u1294\u1295\u1296\u1297\u1298\u1299\u129a\u129b\u129c\u129d\u129e\u129f\u12a0\u12a1\u12a2\u12a3\u12a4\u12a5\u12a6\u12a7\u12a8\u12a9\u12aa\u12ab\u12ac\u12ad\u12ae\u12af\u12b0\u12b2\u12b3\u12b4\u12b5\u12b8\u12b9\u12ba\u12bb\u12bc\u12bd\u12be\u12c0\u12c2\u12c3\u12c4\u12c5\u12c8\u12c9\u12ca\u12cb\u12cc\u12cd\u12ce\u12cf\u12d0\u12d1\u12d2\u12d3\u12d4\u12d5\u12d6\u12d8\u12d9\u12da\u12db\u12dc\u12dd\u12de\u12df\u12e0\u12e1\u12e2\u12e3\u12e4\u12e5\u12e6\u12e7\u12e8\u12e9\u12ea\u12eb\u12ec\u12ed\u12ee\u12ef\u12f0\u12f1\u12f2\u12f3\u12f4\u12f5\u12f6\u12f7\u12f8\u12f9\u12fa\u12fb\u12fc\u12fd\u12fe\u12ff\u1300\u1301\u1302\u1303\u1304\u1305\u1306\u1307\u1308\u1309\u130a\u130b\u130c\u130d\u130e\u130f\u1310\u1312\u1313\u1314\u1315\u1318\u1319\u131a\u131b\u131c\u131d\u131e\u131f\u1320\u1321\u1322\u1323\u1324\u1325\u1326\u1327\u1328\u1329\u132a\u132b\u132c\u132d\u132e\u132f\u1330\u1331\u1332\u1333\u1334\u1335\u1336\u1337\u1338\u1339\u133a\u133b\u133c\u133d\u133e\u133f\u1340\u1341\u1342\u1343\u1344\u1345\u1346\u1347\u1348\u1349\u134a\u134b\u134c\u134d\u134e\u134f\u1350\u1351\u1352\u1353\u1354\u1355\u1356\u1357\u1358\u1359\u135a\u1380\u1381\u1382\u1383\u1384\u1385\u1386\u1387\u1388\u1389\u138a\u138b\u138c\u138d\u138e\u138f\u13a0\u13a1\u13a2\u13a3\u13a4\u13a5\u13a6\u13a7\u13a8\u13a9\u13aa\u13ab\u13ac\u13ad\u13ae\u13af\u13b0\u13b1\u13b2\u13b3\u13b4\u13b5\u13b6\u13b7\u13b8\u13b9\u13ba\u13bb\u13bc\u13bd\u13be\u13bf\u13c0\u13c1\u13c2\u13c3\u13c4\u13c5\u13c6\u13c7\u13c8\u13c9\u13ca\u13cb\u13cc\u13cd\u13ce\u13cf\u13d0\u13d1\u13d2\u13d3\u13d4\u13d5\u13d6\u13d7\u13d8\u13d9\u13da\u13db\u13dc\u13dd\u13de\u13df\u13e0\u13e1\u13e2\u13e3\u13e4\u13e5\u13e6\u13e7\u13e8\u13e9\u13ea\u13eb\u13ec\u13ed\u13ee\u13ef\u13f0\u13f1\u13f2\u13f3\u13f4\u1401\u1402\u1403\u1404\u1405\u1406\u1407\u1408\u1409\u140a\u140b\u140c\u140d\u140e\u140f\u1410\u1411\u1412\u1413\u1414\u1415\u1416\u1417\u1418\u1419\u141a\u141b\u141c\u141d\u141e\u141f\u1420\u1421\u1422\u1423\u1424\u1425\u1426\u1427\u1428\u1429\u142a\u142b\u142c\u142d\u142e\u142f\u1430\u1431\u1432\u1433\u1434\u1435\u1436\u1437\u1438\u1439\u143a\u143b\u143c\u143d\u143e\u143f\u1440\u1441\u1442\u1443\u1444\u1445\u1446\u1447\u1448\u1449\u144a\u144b\u144c\u144d\u144e\u144f\u1450\u1451\u1452\u1453\u1454\u1455\u1456\u1457\u1458\u1459\u145a\u145b\u145c\u145d\u145e\u145f\u1460\u1461\u1462\u1463\u1464\u1465\u1466\u1467\u1468\u1469\u146a\u146b\u146c\u146d\u146e\u146f\u1470\u1471\u1472\u1473\u1474\u1475\u1476\u1477\u1478\u1479\u147a\u147b\u147c\u147d\u147e\u147f\u1480\u1481\u1482\u1483\u1484\u1485\u1486\u1487\u1488\u1489\u148a\u148b\u148c\u148d\u148e\u148f\u1490\u1491\u1492\u1493\u1494\u1495\u1496\u1497\u1498\u1499\u149a\u149b\u149c\u149d\u149e\u149f\u14a0\u14a1\u14a2\u14a3\u14a4\u14a5\u14a6\u14a7\u14a8\u14a9\u14aa\u14ab\u14ac\u14ad\u14ae\u14af\u14b0\u14b1\u14b2\u14b3\u14b4\u14b5\u14b6\u14b7\u14b8\u14b9\u14ba\u14bb\u14bc\u14bd\u14be\u14bf\u14c0\u14c1\u14c2\u14c3\u14c4\u14c5\u14c6\u14c7\u14c8\u14c9\u14ca\u14cb\u14cc\u14cd\u14ce\u14cf\u14d0\u14d1\u14d2\u14d3\u14d4\u14d5\u14d6\u14d7\u14d8\u14d9\u14da\u14db\u14dc\u14dd\u14de\u14df\u14e0\u14e1\u14e2\u14e3\u14e4\u14e5\u14e6\u14e7\u14e8\u14e9\u14ea\u14eb\u14ec\u14ed\u14ee\u14ef\u14f0\u14f1\u14f2\u14f3\u14f4\u14f5\u14f6\u14f7\u14f8\u14f9\u14fa\u14fb\u14fc\u14fd\u14fe\u14ff\u1500\u1501\u1502\u1503\u1504\u1505\u1506\u1507\u1508\u1509\u150a\u150b\u150c\u150d\u150e\u150f\u1510\u1511\u1512\u1513\u1514\u1515\u1516\u1517\u1518\u1519\u151a\u151b\u151c\u151d\u151e\u151f\u1520\u1521\u1522\u1523\u1524\u1525\u1526\u1527\u1528\u1529\u152a\u152b\u152c\u152d\u152e\u152f\u1530\u1531\u1532\u1533\u1534\u1535\u1536\u1537\u1538\u1539\u153a\u153b\u153c\u153d\u153e\u153f\u1540\u1541\u1542\u1543\u1544\u1545\u1546\u1547\u1548\u1549\u154a\u154b\u154c\u154d\u154e\u154f\u1550\u1551\u1552\u1553\u1554\u1555\u1556\u1557\u1558\u1559\u155a\u155b\u155c\u155d\u155e\u155f\u1560\u1561\u1562\u1563\u1564\u1565\u1566\u1567\u1568\u1569\u156a\u156b\u156c\u156d\u156e\u156f\u1570\u1571\u1572\u1573\u1574\u1575\u1576\u1577\u1578\u1579\u157a\u157b\u157c\u157d\u157e\u157f\u1580\u1581\u1582\u1583\u1584\u1585\u1586\u1587\u1588\u1589\u158a\u158b\u158c\u158d\u158e\u158f\u1590\u1591\u1592\u1593\u1594\u1595\u1596\u1597\u1598\u1599\u159a\u159b\u159c\u159d\u159e\u159f\u15a0\u15a1\u15a2\u15a3\u15a4\u15a5\u15a6\u15a7\u15a8\u15a9\u15aa\u15ab\u15ac\u15ad\u15ae\u15af\u15b0\u15b1\u15b2\u15b3\u15b4\u15b5\u15b6\u15b7\u15b8\u15b9\u15ba\u15bb\u15bc\u15bd\u15be\u15bf\u15c0\u15c1\u15c2\u15c3\u15c4\u15c5\u15c6\u15c7\u15c8\u15c9\u15ca\u15cb\u15cc\u15cd\u15ce\u15cf\u15d0\u15d1\u15d2\u15d3\u15d4\u15d5\u15d6\u15d7\u15d8\u15d9\u15da\u15db\u15dc\u15dd\u15de\u15df\u15e0\u15e1\u15e2\u15e3\u15e4\u15e5\u15e6\u15e7\u15e8\u15e9\u15ea\u15eb\u15ec\u15ed\u15ee\u15ef\u15f0\u15f1\u15f2\u15f3\u15f4\u15f5\u15f6\u15f7\u15f8\u15f9\u15fa\u15fb\u15fc\u15fd\u15fe\u15ff\u1600\u1601\u1602\u1603\u1604\u1605\u1606\u1607\u1608\u1609\u160a\u160b\u160c\u160d\u160e\u160f\u1610\u1611\u1612\u1613\u1614\u1615\u1616\u1617\u1618\u1619\u161a\u161b\u161c\u161d\u161e\u161f\u1620\u1621\u1622\u1623\u1624\u1625\u1626\u1627\u1628\u1629\u162a\u162b\u162c\u162d\u162e\u162f\u1630\u1631\u1632\u1633\u1634\u1635\u1636\u1637\u1638\u1639\u163a\u163b\u163c\u163d\u163e\u163f\u1640\u1641\u1642\u1643\u1644\u1645\u1646\u1647\u1648\u1649\u164a\u164b\u164c\u164d\u164e\u164f\u1650\u1651\u1652\u1653\u1654\u1655\u1656\u1657\u1658\u1659\u165a\u165b\u165c\u165d\u165e\u165f\u1660\u1661\u1662\u1663\u1664\u1665\u1666\u1667\u1668\u1669\u166a\u166b\u166c\u166f\u1670\u1671\u1672\u1673\u1674\u1675\u1676\u1681\u1682\u1683\u1684\u1685\u1686\u1687\u1688\u1689\u168a\u168b\u168c\u168d\u168e\u168f\u1690\u1691\u1692\u1693\u1694\u1695\u1696\u1697\u1698\u1699\u169a\u16a0\u16a1\u16a2\u16a3\u16a4\u16a5\u16a6\u16a7\u16a8\u16a9\u16aa\u16ab\u16ac\u16ad\u16ae\u16af\u16b0\u16b1\u16b2\u16b3\u16b4\u16b5\u16b6\u16b7\u16b8\u16b9\u16ba\u16bb\u16bc\u16bd\u16be\u16bf\u16c0\u16c1\u16c2\u16c3\u16c4\u16c5\u16c6\u16c7\u16c8\u16c9\u16ca\u16cb\u16cc\u16cd\u16ce\u16cf\u16d0\u16d1\u16d2\u16d3\u16d4\u16d5\u16d6\u16d7\u16d8\u16d9\u16da\u16db\u16dc\u16dd\u16de\u16df\u16e0\u16e1\u16e2\u16e3\u16e4\u16e5\u16e6\u16e7\u16e8\u16e9\u16ea\u1700\u1701\u1702\u1703\u1704\u1705\u1706\u1707\u1708\u1709\u170a\u170b\u170c\u170e\u170f\u1710\u1711\u1720\u1721\u1722\u1723\u1724\u1725\u1726\u1727\u1728\u1729\u172a\u172b\u172c\u172d\u172e\u172f\u1730\u1731\u1740\u1741\u1742\u1743\u1744\u1745\u1746\u1747\u1748\u1749\u174a\u174b\u174c\u174d\u174e\u174f\u1750\u1751\u1760\u1761\u1762\u1763\u1764\u1765\u1766\u1767\u1768\u1769\u176a\u176b\u176c\u176e\u176f\u1770\u1780\u1781\u1782\u1783\u1784\u1785\u1786\u1787\u1788\u1789\u178a\u178b\u178c\u178d\u178e\u178f\u1790\u1791\u1792\u1793\u1794\u1795\u1796\u1797\u1798\u1799\u179a\u179b\u179c\u179d\u179e\u179f\u17a0\u17a1\u17a2\u17a3\u17a4\u17a5\u17a6\u17a7\u17a8\u17a9\u17aa\u17ab\u17ac\u17ad\u17ae\u17af\u17b0\u17b1\u17b2\u17b3\u17dc\u1820\u1821\u1822\u1823\u1824\u1825\u1826\u1827\u1828\u1829\u182a\u182b\u182c\u182d\u182e\u182f\u1830\u1831\u1832\u1833\u1834\u1835\u1836\u1837\u1838\u1839\u183a\u183b\u183c\u183d\u183e\u183f\u1840\u1841\u1842\u1844\u1845\u1846\u1847\u1848\u1849\u184a\u184b\u184c\u184d\u184e\u184f\u1850\u1851\u1852\u1853\u1854\u1855\u1856\u1857\u1858\u1859\u185a\u185b\u185c\u185d\u185e\u185f\u1860\u1861\u1862\u1863\u1864\u1865\u1866\u1867\u1868\u1869\u186a\u186b\u186c\u186d\u186e\u186f\u1870\u1871\u1872\u1873\u1874\u1875\u1876\u1877\u1880\u1881\u1882\u1883\u1884\u1885\u1886\u1887\u1888\u1889\u188a\u188b\u188c\u188d\u188e\u188f\u1890\u1891\u1892\u1893\u1894\u1895\u1896\u1897\u1898\u1899\u189a\u189b\u189c\u189d\u189e\u189f\u18a0\u18a1\u18a2\u18a3\u18a4\u18a5\u18a6\u18a7\u18a8\u1900\u1901\u1902\u1903\u1904\u1905\u1906\u1907\u1908\u1909\u190a\u190b\u190c\u190d\u190e\u190f\u1910\u1911\u1912\u1913\u1914\u1915\u1916\u1917\u1918\u1919\u191a\u191b\u191c\u1950\u1951\u1952\u1953\u1954\u1955\u1956\u1957\u1958\u1959\u195a\u195b\u195c\u195d\u195e\u195f\u1960\u1961\u1962\u1963\u1964\u1965\u1966\u1967\u1968\u1969\u196a\u196b\u196c\u196d\u1970\u1971\u1972\u1973\u1974\u1980\u1981\u1982\u1983\u1984\u1985\u1986\u1987\u1988\u1989\u198a\u198b\u198c\u198d\u198e\u198f\u1990\u1991\u1992\u1993\u1994\u1995\u1996\u1997\u1998\u1999\u199a\u199b\u199c\u199d\u199e\u199f\u19a0\u19a1\u19a2\u19a3\u19a4\u19a5\u19a6\u19a7\u19a8\u19a9\u19c1\u19c2\u19c3\u19c4\u19c5\u19c6\u19c7\u1a00\u1a01\u1a02\u1a03\u1a04\u1a05\u1a06\u1a07\u1a08\u1a09\u1a0a\u1a0b\u1a0c\u1a0d\u1a0e\u1a0f\u1a10\u1a11\u1a12\u1a13\u1a14\u1a15\u1a16\u2135\u2136\u2137\u2138\u2d30\u2d31\u2d32\u2d33\u2d34\u2d35\u2d36\u2d37\u2d38\u2d39\u2d3a\u2d3b\u2d3c\u2d3d\u2d3e\u2d3f\u2d40\u2d41\u2d42\u2d43\u2d44\u2d45\u2d46\u2d47\u2d48\u2d49\u2d4a\u2d4b\u2d4c\u2d4d\u2d4e\u2d4f\u2d50\u2d51\u2d52\u2d53\u2d54\u2d55\u2d56\u2d57\u2d58\u2d59\u2d5a\u2d5b\u2d5c\u2d5d\u2d5e\u2d5f\u2d60\u2d61\u2d62\u2d63\u2d64\u2d65\u2d80\u2d81\u2d82\u2d83\u2d84\u2d85\u2d86\u2d87\u2d88\u2d89\u2d8a\u2d8b\u2d8c\u2d8d\u2d8e\u2d8f\u2d90\u2d91\u2d92\u2d93\u2d94\u2d95\u2d96\u2da0\u2da1\u2da2\u2da3\u2da4\u2da5\u2da6\u2da8\u2da9\u2daa\u2dab\u2dac\u2dad\u2dae\u2db0\u2db1\u2db2\u2db3\u2db4\u2db5\u2db6\u2db8\u2db9\u2dba\u2dbb\u2dbc\u2dbd\u2dbe\u2dc0\u2dc1\u2dc2\u2dc3\u2dc4\u2dc5\u2dc6\u2dc8\u2dc9\u2dca\u2dcb\u2dcc\u2dcd\u2dce\u2dd0\u2dd1\u2dd2\u2dd3\u2dd4\u2dd5\u2dd6\u2dd8\u2dd9\u2dda\u2ddb\u2ddc\u2ddd\u2dde\u3006\u303c\u3041\u3042\u3043\u3044\u3045\u3046\u3047\u3048\u3049\u304a\u304b\u304c\u304d\u304e\u304f\u3050\u3051\u3052\u3053\u3054\u3055\u3056\u3057\u3058\u3059\u305a\u305b\u305c\u305d\u305e\u305f\u3060\u3061\u3062\u3063\u3064\u3065\u3066\u3067\u3068\u3069\u306a\u306b\u306c\u306d\u306e\u306f\u3070\u3071\u3072\u3073\u3074\u3075\u3076\u3077\u3078\u3079\u307a\u307b\u307c\u307d\u307e\u307f\u3080\u3081\u3082\u3083\u3084\u3085\u3086\u3087\u3088\u3089\u308a\u308b\u308c\u308d\u308e\u308f\u3090\u3091\u3092\u3093\u3094\u3095\u3096\u309f\u30a1\u30a2\u30a3\u30a4\u30a5\u30a6\u30a7\u30a8\u30a9\u30aa\u30ab\u30ac\u30ad\u30ae\u30af\u30b0\u30b1\u30b2\u30b3\u30b4\u30b5\u30b6\u30b7\u30b8\u30b9\u30ba\u30bb\u30bc\u30bd\u30be\u30bf\u30c0\u30c1\u30c2\u30c3\u30c4\u30c5\u30c6\u30c7\u30c8\u30c9\u30ca\u30cb\u30cc\u30cd\u30ce\u30cf\u30d0\u30d1\u30d2\u30d3\u30d4\u30d5\u30d6\u30d7\u30d8\u30d9\u30da\u30db\u30dc\u30dd\u30de\u30df\u30e0\u30e1\u30e2\u30e3\u30e4\u30e5\u30e6\u30e7\u30e8\u30e9\u30ea\u30eb\u30ec\u30ed\u30ee\u30ef\u30f0\u30f1\u30f2\u30f3\u30f4\u30f5\u30f6\u30f7\u30f8\u30f9\u30fa\u30ff\u3105\u3106\u3107\u3108\u3109\u310a\u310b\u310c\u310d\u310e\u310f\u3110\u3111\u3112\u3113\u3114\u3115\u3116\u3117\u3118\u3119\u311a\u311b\u311c\u311d\u311e\u311f\u3120\u3121\u3122\u3123\u3124\u3125\u3126\u3127\u3128\u3129\u312a\u312b\u312c\u3131\u3132\u3133\u3134\u3135\u3136\u3137\u3138\u3139\u313a\u313b\u313c\u313d\u313e\u313f\u3140\u3141\u3142\u3143\u3144\u3145\u3146\u3147\u3148\u3149\u314a\u314b\u314c\u314d\u314e\u314f\u3150\u3151\u3152\u3153\u3154\u3155\u3156\u3157\u3158\u3159\u315a\u315b\u315c\u315d\u315e\u315f\u3160\u3161\u3162\u3163\u3164\u3165\u3166\u3167\u3168\u3169\u316a\u316b\u316c\u316d\u316e\u316f\u3170\u3171\u3172\u3173\u3174\u3175\u3176\u3177\u3178\u3179\u317a\u317b\u317c\u317d\u317e\u317f\u3180\u3181\u3182\u3183\u3184\u3185\u3186\u3187\u3188\u3189\u318a\u318b\u318c\u318d\u318e\u31a0\u31a1\u31a2\u31a3\u31a4\u31a5\u31a6\u31a7\u31a8\u31a9\u31aa\u31ab\u31ac\u31ad\u31ae\u31af\u31b0\u31b1\u31b2\u31b3\u31b4\u31b5\u31b6\u31b7\u31f0\u31f1\u31f2\u31f3\u31f4\u31f5\u31f6\u31f7\u31f8\u31f9\u31fa\u31fb\u31fc\u31fd\u31fe\u31ff\u3400\u3401\u3402\u3403\u3404\u3405\u3406\u3407\u3408\u3409\u340a\u340b\u340c\u340d\u340e\u340f\u3410\u3411\u3412\u3413\u3414\u3415\u3416\u3417\u3418\u3419\u341a\u341b\u341c\u341d\u341e\u341f\u3420\u3421\u3422\u3423\u3424\u3425\u3426\u3427\u3428\u3429\u342a\u342b\u342c\u342d\u342e\u342f\u3430\u3431\u3432\u3433\u3434\u3435\u3436\u3437\u3438\u3439\u343a\u343b\u343c\u343d\u343e\u343f\u3440\u3441\u3442\u3443\u3444\u3445\u3446\u3447\u3448\u3449\u344a\u344b\u344c\u344d\u344e\u344f\u3450\u3451\u3452\u3453\u3454\u3455\u3456\u3457\u3458\u3459\u345a\u345b\u345c\u345d\u345e\u345f\u3460\u3461\u3462\u3463\u3464\u3465\u3466\u3467\u3468\u3469\u346a\u346b\u346c\u346d\u346e\u346f\u3470\u3471\u3472\u3473\u3474\u3475\u3476\u3477\u3478\u3479\u347a\u347b\u347c\u347d\u347e\u347f\u3480\u3481\u3482\u3483\u3484\u3485\u3486\u3487\u3488\u3489\u348a\u348b\u348c\u348d\u348e\u348f\u3490\u3491\u3492\u3493\u3494\u3495\u3496\u3497\u3498\u3499\u349a\u349b\u349c\u349d\u349e\u349f\u34a0\u34a1\u34a2\u34a3\u34a4\u34a5\u34a6\u34a7\u34a8\u34a9\u34aa\u34ab\u34ac\u34ad\u34ae\u34af\u34b0\u34b1\u34b2\u34b3\u34b4\u34b5\u34b6\u34b7\u34b8\u34b9\u34ba\u34bb\u34bc\u34bd\u34be\u34bf\u34c0\u34c1\u34c2\u34c3\u34c4\u34c5\u34c6\u34c7\u34c8\u34c9\u34ca\u34cb\u34cc\u34cd\u34ce\u34cf\u34d0\u34d1\u34d2\u34d3\u34d4\u34d5\u34d6\u34d7\u34d8\u34d9\u34da\u34db\u34dc\u34dd\u34de\u34df\u34e0\u34e1\u34e2\u34e3\u34e4\u34e5\u34e6\u34e7\u34e8\u34e9\u34ea\u34eb\u34ec\u34ed\u34ee\u34ef\u34f0\u34f1\u34f2\u34f3\u34f4\u34f5\u34f6\u34f7\u34f8\u34f9\u34fa\u34fb\u34fc\u34fd\u34fe\u34ff\u3500\u3501\u3502\u3503\u3504\u3505\u3506\u3507\u3508\u3509\u350a\u350b\u350c\u350d\u350e\u350f\u3510\u3511\u3512\u3513\u3514\u3515\u3516\u3517\u3518\u3519\u351a\u351b\u351c\u351d\u351e\u351f\u3520\u3521\u3522\u3523\u3524\u3525\u3526\u3527\u3528\u3529\u352a\u352b\u352c\u352d\u352e\u352f\u3530\u3531\u3532\u3533\u3534\u3535\u3536\u3537\u3538\u3539\u353a\u353b\u353c\u353d\u353e\u353f\u3540\u3541\u3542\u3543\u3544\u3545\u3546\u3547\u3548\u3549\u354a\u354b\u354c\u354d\u354e\u354f\u3550\u3551\u3552\u3553\u3554\u3555\u3556\u3557\u3558\u3559\u355a\u355b\u355c\u355d\u355e\u355f\u3560\u3561\u3562\u3563\u3564\u3565\u3566\u3567\u3568\u3569\u356a\u356b\u356c\u356d\u356e\u356f\u3570\u3571\u3572\u3573\u3574\u3575\u3576\u3577\u3578\u3579\u357a\u357b\u357c\u357d\u357e\u357f\u3580\u3581\u3582\u3583\u3584\u3585\u3586\u3587\u3588\u3589\u358a\u358b\u358c\u358d\u358e\u358f\u3590\u3591\u3592\u3593\u3594\u3595\u3596\u3597\u3598\u3599\u359a\u359b\u359c\u359d\u359e\u359f\u35a0\u35a1\u35a2\u35a3\u35a4\u35a5\u35a6\u35a7\u35a8\u35a9\u35aa\u35ab\u35ac\u35ad\u35ae\u35af\u35b0\u35b1\u35b2\u35b3\u35b4\u35b5\u35b6\u35b7\u35b8\u35b9\u35ba\u35bb\u35bc\u35bd\u35be\u35bf\u35c0\u35c1\u35c2\u35c3\u35c4\u35c5\u35c6\u35c7\u35c8\u35c9\u35ca\u35cb\u35cc\u35cd\u35ce\u35cf\u35d0\u35d1\u35d2\u35d3\u35d4\u35d5\u35d6\u35d7\u35d8\u35d9\u35da\u35db\u35dc\u35dd\u35de\u35df\u35e0\u35e1\u35e2\u35e3\u35e4\u35e5\u35e6\u35e7\u35e8\u35e9\u35ea\u35eb\u35ec\u35ed\u35ee\u35ef\u35f0\u35f1\u35f2\u35f3\u35f4\u35f5\u35f6\u35f7\u35f8\u35f9\u35fa\u35fb\u35fc\u35fd\u35fe\u35ff\u3600\u3601\u3602\u3603\u3604\u3605\u3606\u3607\u3608\u3609\u360a\u360b\u360c\u360d\u360e\u360f\u3610\u3611\u3612\u3613\u3614\u3615\u3616\u3617\u3618\u3619\u361a\u361b\u361c\u361d\u361e\u361f\u3620\u3621\u3622\u3623\u3624\u3625\u3626\u3627\u3628\u3629\u362a\u362b\u362c\u362d\u362e\u362f\u3630\u3631\u3632\u3633\u3634\u3635\u3636\u3637\u3638\u3639\u363a\u363b\u363c\u363d\u363e\u363f\u3640\u3641\u3642\u3643\u3644\u3645\u3646\u3647\u3648\u3649\u364a\u364b\u364c\u364d\u364e\u364f\u3650\u3651\u3652\u3653\u3654\u3655\u3656\u3657\u3658\u3659\u365a\u365b\u365c\u365d\u365e\u365f\u3660\u3661\u3662\u3663\u3664\u3665\u3666\u3667\u3668\u3669\u366a\u366b\u366c\u366d\u366e\u366f\u3670\u3671\u3672\u3673\u3674\u3675\u3676\u3677\u3678\u3679\u367a\u367b\u367c\u367d\u367e\u367f\u3680\u3681\u3682\u3683\u3684\u3685\u3686\u3687\u3688\u3689\u368a\u368b\u368c\u368d\u368e\u368f\u3690\u3691\u3692\u3693\u3694\u3695\u3696\u3697\u3698\u3699\u369a\u369b\u369c\u369d\u369e\u369f\u36a0\u36a1\u36a2\u36a3\u36a4\u36a5\u36a6\u36a7\u36a8\u36a9\u36aa\u36ab\u36ac\u36ad\u36ae\u36af\u36b0\u36b1\u36b2\u36b3\u36b4\u36b5\u36b6\u36b7\u36b8\u36b9\u36ba\u36bb\u36bc\u36bd\u36be\u36bf\u36c0\u36c1\u36c2\u36c3\u36c4\u36c5\u36c6\u36c7\u36c8\u36c9\u36ca\u36cb\u36cc\u36cd\u36ce\u36cf\u36d0\u36d1\u36d2\u36d3\u36d4\u36d5\u36d6\u36d7\u36d8\u36d9\u36da\u36db\u36dc\u36dd\u36de\u36df\u36e0\u36e1\u36e2\u36e3\u36e4\u36e5\u36e6\u36e7\u36e8\u36e9\u36ea\u36eb\u36ec\u36ed\u36ee\u36ef\u36f0\u36f1\u36f2\u36f3\u36f4\u36f5\u36f6\u36f7\u36f8\u36f9\u36fa\u36fb\u36fc\u36fd\u36fe\u36ff\u3700\u3701\u3702\u3703\u3704\u3705\u3706\u3707\u3708\u3709\u370a\u370b\u370c\u370d\u370e\u370f\u3710\u3711\u3712\u3713\u3714\u3715\u3716\u3717\u3718\u3719\u371a\u371b\u371c\u371d\u371e\u371f\u3720\u3721\u3722\u3723\u3724\u3725\u3726\u3727\u3728\u3729\u372a\u372b\u372c\u372d\u372e\u372f\u3730\u3731\u3732\u3733\u3734\u3735\u3736\u3737\u3738\u3739\u373a\u373b\u373c\u373d\u373e\u373f\u3740\u3741\u3742\u3743\u3744\u3745\u3746\u3747\u3748\u3749\u374a\u374b\u374c\u374d\u374e\u374f\u3750\u3751\u3752\u3753\u3754\u3755\u3756\u3757\u3758\u3759\u375a\u375b\u375c\u375d\u375e\u375f\u3760\u3761\u3762\u3763\u3764\u3765\u3766\u3767\u3768\u3769\u376a\u376b\u376c\u376d\u376e\u376f\u3770\u3771\u3772\u3773\u3774\u3775\u3776\u3777\u3778\u3779\u377a\u377b\u377c\u377d\u377e\u377f\u3780\u3781\u3782\u3783\u3784\u3785\u3786\u3787\u3788\u3789\u378a\u378b\u378c\u378d\u378e\u378f\u3790\u3791\u3792\u3793\u3794\u3795\u3796\u3797\u3798\u3799\u379a\u379b\u379c\u379d\u379e\u379f\u37a0\u37a1\u37a2\u37a3\u37a4\u37a5\u37a6\u37a7\u37a8\u37a9\u37aa\u37ab\u37ac\u37ad\u37ae\u37af\u37b0\u37b1\u37b2\u37b3\u37b4\u37b5\u37b6\u37b7\u37b8\u37b9\u37ba\u37bb\u37bc\u37bd\u37be\u37bf\u37c0\u37c1\u37c2\u37c3\u37c4\u37c5\u37c6\u37c7\u37c8\u37c9\u37ca\u37cb\u37cc\u37cd\u37ce\u37cf\u37d0\u37d1\u37d2\u37d3\u37d4\u37d5\u37d6\u37d7\u37d8\u37d9\u37da\u37db\u37dc\u37dd\u37de\u37df\u37e0\u37e1\u37e2\u37e3\u37e4\u37e5\u37e6\u37e7\u37e8\u37e9\u37ea\u37eb\u37ec\u37ed\u37ee\u37ef\u37f0\u37f1\u37f2\u37f3\u37f4\u37f5\u37f6\u37f7\u37f8\u37f9\u37fa\u37fb\u37fc\u37fd\u37fe\u37ff\u3800\u3801\u3802\u3803\u3804\u3805\u3806\u3807\u3808\u3809\u380a\u380b\u380c\u380d\u380e\u380f\u3810\u3811\u3812\u3813\u3814\u3815\u3816\u3817\u3818\u3819\u381a\u381b\u381c\u381d\u381e\u381f\u3820\u3821\u3822\u3823\u3824\u3825\u3826\u3827\u3828\u3829\u382a\u382b\u382c\u382d\u382e\u382f\u3830\u3831\u3832\u3833\u3834\u3835\u3836\u3837\u3838\u3839\u383a\u383b\u383c\u383d\u383e\u383f\u3840\u3841\u3842\u3843\u3844\u3845\u3846\u3847\u3848\u3849\u384a\u384b\u384c\u384d\u384e\u384f\u3850\u3851\u3852\u3853\u3854\u3855\u3856\u3857\u3858\u3859\u385a\u385b\u385c\u385d\u385e\u385f\u3860\u3861\u3862\u3863\u3864\u3865\u3866\u3867\u3868\u3869\u386a\u386b\u386c\u386d\u386e\u386f\u3870\u3871\u3872\u3873\u3874\u3875\u3876\u3877\u3878\u3879\u387a\u387b\u387c\u387d\u387e\u387f\u3880\u3881\u3882\u3883\u3884\u3885\u3886\u3887\u3888\u3889\u388a\u388b\u388c\u388d\u388e\u388f\u3890\u3891\u3892\u3893\u3894\u3895\u3896\u3897\u3898\u3899\u389a\u389b\u389c\u389d\u389e\u389f\u38a0\u38a1\u38a2\u38a3\u38a4\u38a5\u38a6\u38a7\u38a8\u38a9\u38aa\u38ab\u38ac\u38ad\u38ae\u38af\u38b0\u38b1\u38b2\u38b3\u38b4\u38b5\u38b6\u38b7\u38b8\u38b9\u38ba\u38bb\u38bc\u38bd\u38be\u38bf\u38c0\u38c1\u38c2\u38c3\u38c4\u38c5\u38c6\u38c7\u38c8\u38c9\u38ca\u38cb\u38cc\u38cd\u38ce\u38cf\u38d0\u38d1\u38d2\u38d3\u38d4\u38d5\u38d6\u38d7\u38d8\u38d9\u38da\u38db\u38dc\u38dd\u38de\u38df\u38e0\u38e1\u38e2\u38e3\u38e4\u38e5\u38e6\u38e7\u38e8\u38e9\u38ea\u38eb\u38ec\u38ed\u38ee\u38ef\u38f0\u38f1\u38f2\u38f3\u38f4\u38f5\u38f6\u38f7\u38f8\u38f9\u38fa\u38fb\u38fc\u38fd\u38fe\u38ff\u3900\u3901\u3902\u3903\u3904\u3905\u3906\u3907\u3908\u3909\u390a\u390b\u390c\u390d\u390e\u390f\u3910\u3911\u3912\u3913\u3914\u3915\u3916\u3917\u3918\u3919\u391a\u391b\u391c\u391d\u391e\u391f\u3920\u3921\u3922\u3923\u3924\u3925\u3926\u3927\u3928\u3929\u392a\u392b\u392c\u392d\u392e\u392f\u3930\u3931\u3932\u3933\u3934\u3935\u3936\u3937\u3938\u3939\u393a\u393b\u393c\u393d\u393e\u393f\u3940\u3941\u3942\u3943\u3944\u3945\u3946\u3947\u3948\u3949\u394a\u394b\u394c\u394d\u394e\u394f\u3950\u3951\u3952\u3953\u3954\u3955\u3956\u3957\u3958\u3959\u395a\u395b\u395c\u395d\u395e\u395f\u3960\u3961\u3962\u3963\u3964\u3965\u3966\u3967\u3968\u3969\u396a\u396b\u396c\u396d\u396e\u396f\u3970\u3971\u3972\u3973\u3974\u3975\u3976\u3977\u3978\u3979\u397a\u397b\u397c\u397d\u397e\u397f\u3980\u3981\u3982\u3983\u3984\u3985\u3986\u3987\u3988\u3989\u398a\u398b\u398c\u398d\u398e\u398f\u3990\u3991\u3992\u3993\u3994\u3995\u3996\u3997\u3998\u3999\u399a\u399b\u399c\u399d\u399e\u399f\u39a0\u39a1\u39a2\u39a3\u39a4\u39a5\u39a6\u39a7\u39a8\u39a9\u39aa\u39ab\u39ac\u39ad\u39ae\u39af\u39b0\u39b1\u39b2\u39b3\u39b4\u39b5\u39b6\u39b7\u39b8\u39b9\u39ba\u39bb\u39bc\u39bd\u39be\u39bf\u39c0\u39c1\u39c2\u39c3\u39c4\u39c5\u39c6\u39c7\u39c8\u39c9\u39ca\u39cb\u39cc\u39cd\u39ce\u39cf\u39d0\u39d1\u39d2\u39d3\u39d4\u39d5\u39d6\u39d7\u39d8\u39d9\u39da\u39db\u39dc\u39dd\u39de\u39df\u39e0\u39e1\u39e2\u39e3\u39e4\u39e5\u39e6\u39e7\u39e8\u39e9\u39ea\u39eb\u39ec\u39ed\u39ee\u39ef\u39f0\u39f1\u39f2\u39f3\u39f4\u39f5\u39f6\u39f7\u39f8\u39f9\u39fa\u39fb\u39fc\u39fd\u39fe\u39ff\u3a00\u3a01\u3a02\u3a03\u3a04\u3a05\u3a06\u3a07\u3a08\u3a09\u3a0a\u3a0b\u3a0c\u3a0d\u3a0e\u3a0f\u3a10\u3a11\u3a12\u3a13\u3a14\u3a15\u3a16\u3a17\u3a18\u3a19\u3a1a\u3a1b\u3a1c\u3a1d\u3a1e\u3a1f\u3a20\u3a21\u3a22\u3a23\u3a24\u3a25\u3a26\u3a27\u3a28\u3a29\u3a2a\u3a2b\u3a2c\u3a2d\u3a2e\u3a2f\u3a30\u3a31\u3a32\u3a33\u3a34\u3a35\u3a36\u3a37\u3a38\u3a39\u3a3a\u3a3b\u3a3c\u3a3d\u3a3e\u3a3f\u3a40\u3a41\u3a42\u3a43\u3a44\u3a45\u3a46\u3a47\u3a48\u3a49\u3a4a\u3a4b\u3a4c\u3a4d\u3a4e\u3a4f\u3a50\u3a51\u3a52\u3a53\u3a54\u3a55\u3a56\u3a57\u3a58\u3a59\u3a5a\u3a5b\u3a5c\u3a5d\u3a5e\u3a5f\u3a60\u3a61\u3a62\u3a63\u3a64\u3a65\u3a66\u3a67\u3a68\u3a69\u3a6a\u3a6b\u3a6c\u3a6d\u3a6e\u3a6f\u3a70\u3a71\u3a72\u3a73\u3a74\u3a75\u3a76\u3a77\u3a78\u3a79\u3a7a\u3a7b\u3a7c\u3a7d\u3a7e\u3a7f\u3a80\u3a81\u3a82\u3a83\u3a84\u3a85\u3a86\u3a87\u3a88\u3a89\u3a8a\u3a8b\u3a8c\u3a8d\u3a8e\u3a8f\u3a90\u3a91\u3a92\u3a93\u3a94\u3a95\u3a96\u3a97\u3a98\u3a99\u3a9a\u3a9b\u3a9c\u3a9d\u3a9e\u3a9f\u3aa0\u3aa1\u3aa2\u3aa3\u3aa4\u3aa5\u3aa6\u3aa7\u3aa8\u3aa9\u3aaa\u3aab\u3aac\u3aad\u3aae\u3aaf\u3ab0\u3ab1\u3ab2\u3ab3\u3ab4\u3ab5\u3ab6\u3ab7\u3ab8\u3ab9\u3aba\u3abb\u3abc\u3abd\u3abe\u3abf\u3ac0\u3ac1\u3ac2\u3ac3\u3ac4\u3ac5\u3ac6\u3ac7\u3ac8\u3ac9\u3aca\u3acb\u3acc\u3acd\u3ace\u3acf\u3ad0\u3ad1\u3ad2\u3ad3\u3ad4\u3ad5\u3ad6\u3ad7\u3ad8\u3ad9\u3ada\u3adb\u3adc\u3add\u3ade\u3adf\u3ae0\u3ae1\u3ae2\u3ae3\u3ae4\u3ae5\u3ae6\u3ae7\u3ae8\u3ae9\u3aea\u3aeb\u3aec\u3aed\u3aee\u3aef\u3af0\u3af1\u3af2\u3af3\u3af4\u3af5\u3af6\u3af7\u3af8\u3af9\u3afa\u3afb\u3afc\u3afd\u3afe\u3aff\u3b00\u3b01\u3b02\u3b03\u3b04\u3b05\u3b06\u3b07\u3b08\u3b09\u3b0a\u3b0b\u3b0c\u3b0d\u3b0e\u3b0f\u3b10\u3b11\u3b12\u3b13\u3b14\u3b15\u3b16\u3b17\u3b18\u3b19\u3b1a\u3b1b\u3b1c\u3b1d\u3b1e\u3b1f\u3b20\u3b21\u3b22\u3b23\u3b24\u3b25\u3b26\u3b27\u3b28\u3b29\u3b2a\u3b2b\u3b2c\u3b2d\u3b2e\u3b2f\u3b30\u3b31\u3b32\u3b33\u3b34\u3b35\u3b36\u3b37\u3b38\u3b39\u3b3a\u3b3b\u3b3c\u3b3d\u3b3e\u3b3f\u3b40\u3b41\u3b42\u3b43\u3b44\u3b45\u3b46\u3b47\u3b48\u3b49\u3b4a\u3b4b\u3b4c\u3b4d\u3b4e\u3b4f\u3b50\u3b51\u3b52\u3b53\u3b54\u3b55\u3b56\u3b57\u3b58\u3b59\u3b5a\u3b5b\u3b5c\u3b5d\u3b5e\u3b5f\u3b60\u3b61\u3b62\u3b63\u3b64\u3b65\u3b66\u3b67\u3b68\u3b69\u3b6a\u3b6b\u3b6c\u3b6d\u3b6e\u3b6f\u3b70\u3b71\u3b72\u3b73\u3b74\u3b75\u3b76\u3b77\u3b78\u3b79\u3b7a\u3b7b\u3b7c\u3b7d\u3b7e\u3b7f\u3b80\u3b81\u3b82\u3b83\u3b84\u3b85\u3b86\u3b87\u3b88\u3b89\u3b8a\u3b8b\u3b8c\u3b8d\u3b8e\u3b8f\u3b90\u3b91\u3b92\u3b93\u3b94\u3b95\u3b96\u3b97\u3b98\u3b99\u3b9a\u3b9b\u3b9c\u3b9d\u3b9e\u3b9f\u3ba0\u3ba1\u3ba2\u3ba3\u3ba4\u3ba5\u3ba6\u3ba7\u3ba8\u3ba9\u3baa\u3bab\u3bac\u3bad\u3bae\u3baf\u3bb0\u3bb1\u3bb2\u3bb3\u3bb4\u3bb5\u3bb6\u3bb7\u3bb8\u3bb9\u3bba\u3bbb\u3bbc\u3bbd\u3bbe\u3bbf\u3bc0\u3bc1\u3bc2\u3bc3\u3bc4\u3bc5\u3bc6\u3bc7\u3bc8\u3bc9\u3bca\u3bcb\u3bcc\u3bcd\u3bce\u3bcf\u3bd0\u3bd1\u3bd2\u3bd3\u3bd4\u3bd5\u3bd6\u3bd7\u3bd8\u3bd9\u3bda\u3bdb\u3bdc\u3bdd\u3bde\u3bdf\u3be0\u3be1\u3be2\u3be3\u3be4\u3be5\u3be6\u3be7\u3be8\u3be9\u3bea\u3beb\u3bec\u3bed\u3bee\u3bef\u3bf0\u3bf1\u3bf2\u3bf3\u3bf4\u3bf5\u3bf6\u3bf7\u3bf8\u3bf9\u3bfa\u3bfb\u3bfc\u3bfd\u3bfe\u3bff\u3c00\u3c01\u3c02\u3c03\u3c04\u3c05\u3c06\u3c07\u3c08\u3c09\u3c0a\u3c0b\u3c0c\u3c0d\u3c0e\u3c0f\u3c10\u3c11\u3c12\u3c13\u3c14\u3c15\u3c16\u3c17\u3c18\u3c19\u3c1a\u3c1b\u3c1c\u3c1d\u3c1e\u3c1f\u3c20\u3c21\u3c22\u3c23\u3c24\u3c25\u3c26\u3c27\u3c28\u3c29\u3c2a\u3c2b\u3c2c\u3c2d\u3c2e\u3c2f\u3c30\u3c31\u3c32\u3c33\u3c34\u3c35\u3c36\u3c37\u3c38\u3c39\u3c3a\u3c3b\u3c3c\u3c3d\u3c3e\u3c3f\u3c40\u3c41\u3c42\u3c43\u3c44\u3c45\u3c46\u3c47\u3c48\u3c49\u3c4a\u3c4b\u3c4c\u3c4d\u3c4e\u3c4f\u3c50\u3c51\u3c52\u3c53\u3c54\u3c55\u3c56\u3c57\u3c58\u3c59\u3c5a\u3c5b\u3c5c\u3c5d\u3c5e\u3c5f\u3c60\u3c61\u3c62\u3c63\u3c64\u3c65\u3c66\u3c67\u3c68\u3c69\u3c6a\u3c6b\u3c6c\u3c6d\u3c6e\u3c6f\u3c70\u3c71\u3c72\u3c73\u3c74\u3c75\u3c76\u3c77\u3c78\u3c79\u3c7a\u3c7b\u3c7c\u3c7d\u3c7e\u3c7f\u3c80\u3c81\u3c82\u3c83\u3c84\u3c85\u3c86\u3c87\u3c88\u3c89\u3c8a\u3c8b\u3c8c\u3c8d\u3c8e\u3c8f\u3c90\u3c91\u3c92\u3c93\u3c94\u3c95\u3c96\u3c97\u3c98\u3c99\u3c9a\u3c9b\u3c9c\u3c9d\u3c9e\u3c9f\u3ca0\u3ca1\u3ca2\u3ca3\u3ca4\u3ca5\u3ca6\u3ca7\u3ca8\u3ca9\u3caa\u3cab\u3cac\u3cad\u3cae\u3caf\u3cb0\u3cb1\u3cb2\u3cb3\u3cb4\u3cb5\u3cb6\u3cb7\u3cb8\u3cb9\u3cba\u3cbb\u3cbc\u3cbd\u3cbe\u3cbf\u3cc0\u3cc1\u3cc2\u3cc3\u3cc4\u3cc5\u3cc6\u3cc7\u3cc8\u3cc9\u3cca\u3ccb\u3ccc\u3ccd\u3cce\u3ccf\u3cd0\u3cd1\u3cd2\u3cd3\u3cd4\u3cd5\u3cd6\u3cd7\u3cd8\u3cd9\u3cda\u3cdb\u3cdc\u3cdd\u3cde\u3cdf\u3ce0\u3ce1\u3ce2\u3ce3\u3ce4\u3ce5\u3ce6\u3ce7\u3ce8\u3ce9\u3cea\u3ceb\u3cec\u3ced\u3cee\u3cef\u3cf0\u3cf1\u3cf2\u3cf3\u3cf4\u3cf5\u3cf6\u3cf7\u3cf8\u3cf9\u3cfa\u3cfb\u3cfc\u3cfd\u3cfe\u3cff\u3d00\u3d01\u3d02\u3d03\u3d04\u3d05\u3d06\u3d07\u3d08\u3d09\u3d0a\u3d0b\u3d0c\u3d0d\u3d0e\u3d0f\u3d10\u3d11\u3d12\u3d13\u3d14\u3d15\u3d16\u3d17\u3d18\u3d19\u3d1a\u3d1b\u3d1c\u3d1d\u3d1e\u3d1f\u3d20\u3d21\u3d22\u3d23\u3d24\u3d25\u3d26\u3d27\u3d28\u3d29\u3d2a\u3d2b\u3d2c\u3d2d\u3d2e\u3d2f\u3d30\u3d31\u3d32\u3d33\u3d34\u3d35\u3d36\u3d37\u3d38\u3d39\u3d3a\u3d3b\u3d3c\u3d3d\u3d3e\u3d3f\u3d40\u3d41\u3d42\u3d43\u3d44\u3d45\u3d46\u3d47\u3d48\u3d49\u3d4a\u3d4b\u3d4c\u3d4d\u3d4e\u3d4f\u3d50\u3d51\u3d52\u3d53\u3d54\u3d55\u3d56\u3d57\u3d58\u3d59\u3d5a\u3d5b\u3d5c\u3d5d\u3d5e\u3d5f\u3d60\u3d61\u3d62\u3d63\u3d64\u3d65\u3d66\u3d67\u3d68\u3d69\u3d6a\u3d6b\u3d6c\u3d6d\u3d6e\u3d6f\u3d70\u3d71\u3d72\u3d73\u3d74\u3d75\u3d76\u3d77\u3d78\u3d79\u3d7a\u3d7b\u3d7c\u3d7d\u3d7e\u3d7f\u3d80\u3d81\u3d82\u3d83\u3d84\u3d85\u3d86\u3d87\u3d88\u3d89\u3d8a\u3d8b\u3d8c\u3d8d\u3d8e\u3d8f\u3d90\u3d91\u3d92\u3d93\u3d94\u3d95\u3d96\u3d97\u3d98\u3d99\u3d9a\u3d9b\u3d9c\u3d9d\u3d9e\u3d9f\u3da0\u3da1\u3da2\u3da3\u3da4\u3da5\u3da6\u3da7\u3da8\u3da9\u3daa\u3dab\u3dac\u3dad\u3dae\u3daf\u3db0\u3db1\u3db2\u3db3\u3db4\u3db5\u3db6\u3db7\u3db8\u3db9\u3dba\u3dbb\u3dbc\u3dbd\u3dbe\u3dbf\u3dc0\u3dc1\u3dc2\u3dc3\u3dc4\u3dc5\u3dc6\u3dc7\u3dc8\u3dc9\u3dca\u3dcb\u3dcc\u3dcd\u3dce\u3dcf\u3dd0\u3dd1\u3dd2\u3dd3\u3dd4\u3dd5\u3dd6\u3dd7\u3dd8\u3dd9\u3dda\u3ddb\u3ddc\u3ddd\u3dde\u3ddf\u3de0\u3de1\u3de2\u3de3\u3de4\u3de5\u3de6\u3de7\u3de8\u3de9\u3dea\u3deb\u3dec\u3ded\u3dee\u3def\u3df0\u3df1\u3df2\u3df3\u3df4\u3df5\u3df6\u3df7\u3df8\u3df9\u3dfa\u3dfb\u3dfc\u3dfd\u3dfe\u3dff\u3e00\u3e01\u3e02\u3e03\u3e04\u3e05\u3e06\u3e07\u3e08\u3e09\u3e0a\u3e0b\u3e0c\u3e0d\u3e0e\u3e0f\u3e10\u3e11\u3e12\u3e13\u3e14\u3e15\u3e16\u3e17\u3e18\u3e19\u3e1a\u3e1b\u3e1c\u3e1d\u3e1e\u3e1f\u3e20\u3e21\u3e22\u3e23\u3e24\u3e25\u3e26\u3e27\u3e28\u3e29\u3e2a\u3e2b\u3e2c\u3e2d\u3e2e\u3e2f\u3e30\u3e31\u3e32\u3e33\u3e34\u3e35\u3e36\u3e37\u3e38\u3e39\u3e3a\u3e3b\u3e3c\u3e3d\u3e3e\u3e3f\u3e40\u3e41\u3e42\u3e43\u3e44\u3e45\u3e46\u3e47\u3e48\u3e49\u3e4a\u3e4b\u3e4c\u3e4d\u3e4e\u3e4f\u3e50\u3e51\u3e52\u3e53\u3e54\u3e55\u3e56\u3e57\u3e58\u3e59\u3e5a\u3e5b\u3e5c\u3e5d\u3e5e\u3e5f\u3e60\u3e61\u3e62\u3e63\u3e64\u3e65\u3e66\u3e67\u3e68\u3e69\u3e6a\u3e6b\u3e6c\u3e6d\u3e6e\u3e6f\u3e70\u3e71\u3e72\u3e73\u3e74\u3e75\u3e76\u3e77\u3e78\u3e79\u3e7a\u3e7b\u3e7c\u3e7d\u3e7e\u3e7f\u3e80\u3e81\u3e82\u3e83\u3e84\u3e85\u3e86\u3e87\u3e88\u3e89\u3e8a\u3e8b\u3e8c\u3e8d\u3e8e\u3e8f\u3e90\u3e91\u3e92\u3e93\u3e94\u3e95\u3e96\u3e97\u3e98\u3e99\u3e9a\u3e9b\u3e9c\u3e9d\u3e9e\u3e9f\u3ea0\u3ea1\u3ea2\u3ea3\u3ea4\u3ea5\u3ea6\u3ea7\u3ea8\u3ea9\u3eaa\u3eab\u3eac\u3ead\u3eae\u3eaf\u3eb0\u3eb1\u3eb2\u3eb3\u3eb4\u3eb5\u3eb6\u3eb7\u3eb8\u3eb9\u3eba\u3ebb\u3ebc\u3ebd\u3ebe\u3ebf\u3ec0\u3ec1\u3ec2\u3ec3\u3ec4\u3ec5\u3ec6\u3ec7\u3ec8\u3ec9\u3eca\u3ecb\u3ecc\u3ecd\u3ece\u3ecf\u3ed0\u3ed1\u3ed2\u3ed3\u3ed4\u3ed5\u3ed6\u3ed7\u3ed8\u3ed9\u3eda\u3edb\u3edc\u3edd\u3ede\u3edf\u3ee0\u3ee1\u3ee2\u3ee3\u3ee4\u3ee5\u3ee6\u3ee7\u3ee8\u3ee9\u3eea\u3eeb\u3eec\u3eed\u3eee\u3eef\u3ef0\u3ef1\u3ef2\u3ef3\u3ef4\u3ef5\u3ef6\u3ef7\u3ef8\u3ef9\u3efa\u3efb\u3efc\u3efd\u3efe\u3eff\u3f00\u3f01\u3f02\u3f03\u3f04\u3f05\u3f06\u3f07\u3f08\u3f09\u3f0a\u3f0b\u3f0c\u3f0d\u3f0e\u3f0f\u3f10\u3f11\u3f12\u3f13\u3f14\u3f15\u3f16\u3f17\u3f18\u3f19\u3f1a\u3f1b\u3f1c\u3f1d\u3f1e\u3f1f\u3f20\u3f21\u3f22\u3f23\u3f24\u3f25\u3f26\u3f27\u3f28\u3f29\u3f2a\u3f2b\u3f2c\u3f2d\u3f2e\u3f2f\u3f30\u3f31\u3f32\u3f33\u3f34\u3f35\u3f36\u3f37\u3f38\u3f39\u3f3a\u3f3b\u3f3c\u3f3d\u3f3e\u3f3f\u3f40\u3f41\u3f42\u3f43\u3f44\u3f45\u3f46\u3f47\u3f48\u3f49\u3f4a\u3f4b\u3f4c\u3f4d\u3f4e\u3f4f\u3f50\u3f51\u3f52\u3f53\u3f54\u3f55\u3f56\u3f57\u3f58\u3f59\u3f5a\u3f5b\u3f5c\u3f5d\u3f5e\u3f5f\u3f60\u3f61\u3f62\u3f63\u3f64\u3f65\u3f66\u3f67\u3f68\u3f69\u3f6a\u3f6b\u3f6c\u3f6d\u3f6e\u3f6f\u3f70\u3f71\u3f72\u3f73\u3f74\u3f75\u3f76\u3f77\u3f78\u3f79\u3f7a\u3f7b\u3f7c\u3f7d\u3f7e\u3f7f\u3f80\u3f81\u3f82\u3f83\u3f84\u3f85\u3f86\u3f87\u3f88\u3f89\u3f8a\u3f8b\u3f8c\u3f8d\u3f8e\u3f8f\u3f90\u3f91\u3f92\u3f93\u3f94\u3f95\u3f96\u3f97\u3f98\u3f99\u3f9a\u3f9b\u3f9c\u3f9d\u3f9e\u3f9f\u3fa0\u3fa1\u3fa2\u3fa3\u3fa4\u3fa5\u3fa6\u3fa7\u3fa8\u3fa9\u3faa\u3fab\u3fac\u3fad\u3fae\u3faf\u3fb0\u3fb1\u3fb2\u3fb3\u3fb4\u3fb5\u3fb6\u3fb7\u3fb8\u3fb9\u3fba\u3fbb\u3fbc\u3fbd\u3fbe\u3fbf\u3fc0\u3fc1\u3fc2\u3fc3\u3fc4\u3fc5\u3fc6\u3fc7\u3fc8\u3fc9\u3fca\u3fcb\u3fcc\u3fcd\u3fce\u3fcf\u3fd0\u3fd1\u3fd2\u3fd3\u3fd4\u3fd5\u3fd6\u3fd7\u3fd8\u3fd9\u3fda\u3fdb\u3fdc\u3fdd\u3fde\u3fdf\u3fe0\u3fe1\u3fe2\u3fe3\u3fe4\u3fe5\u3fe6\u3fe7\u3fe8\u3fe9\u3fea\u3feb\u3fec\u3fed\u3fee\u3fef\u3ff0\u3ff1\u3ff2\u3ff3\u3ff4\u3ff5\u3ff6\u3ff7\u3ff8\u3ff9\u3ffa\u3ffb\u3ffc\u3ffd\u3ffe\u3fff\u4000\u4001\u4002\u4003\u4004\u4005\u4006\u4007\u4008\u4009\u400a\u400b\u400c\u400d\u400e\u400f\u4010\u4011\u4012\u4013\u4014\u4015\u4016\u4017\u4018\u4019\u401a\u401b\u401c\u401d\u401e\u401f\u4020\u4021\u4022\u4023\u4024\u4025\u4026\u4027\u4028\u4029\u402a\u402b\u402c\u402d\u402e\u402f\u4030\u4031\u4032\u4033\u4034\u4035\u4036\u4037\u4038\u4039\u403a\u403b\u403c\u403d\u403e\u403f\u4040\u4041\u4042\u4043\u4044\u4045\u4046\u4047\u4048\u4049\u404a\u404b\u404c\u404d\u404e\u404f\u4050\u4051\u4052\u4053\u4054\u4055\u4056\u4057\u4058\u4059\u405a\u405b\u405c\u405d\u405e\u405f\u4060\u4061\u4062\u4063\u4064\u4065\u4066\u4067\u4068\u4069\u406a\u406b\u406c\u406d\u406e\u406f\u4070\u4071\u4072\u4073\u4074\u4075\u4076\u4077\u4078\u4079\u407a\u407b\u407c\u407d\u407e\u407f\u4080\u4081\u4082\u4083\u4084\u4085\u4086\u4087\u4088\u4089\u408a\u408b\u408c\u408d\u408e\u408f\u4090\u4091\u4092\u4093\u4094\u4095\u4096\u4097\u4098\u4099\u409a\u409b\u409c\u409d\u409e\u409f\u40a0\u40a1\u40a2\u40a3\u40a4\u40a5\u40a6\u40a7\u40a8\u40a9\u40aa\u40ab\u40ac\u40ad\u40ae\u40af\u40b0\u40b1\u40b2\u40b3\u40b4\u40b5\u40b6\u40b7\u40b8\u40b9\u40ba\u40bb\u40bc\u40bd\u40be\u40bf\u40c0\u40c1\u40c2\u40c3\u40c4\u40c5\u40c6\u40c7\u40c8\u40c9\u40ca\u40cb\u40cc\u40cd\u40ce\u40cf\u40d0\u40d1\u40d2\u40d3\u40d4\u40d5\u40d6\u40d7\u40d8\u40d9\u40da\u40db\u40dc\u40dd\u40de\u40df\u40e0\u40e1\u40e2\u40e3\u40e4\u40e5\u40e6\u40e7\u40e8\u40e9\u40ea\u40eb\u40ec\u40ed\u40ee\u40ef\u40f0\u40f1\u40f2\u40f3\u40f4\u40f5\u40f6\u40f7\u40f8\u40f9\u40fa\u40fb\u40fc\u40fd\u40fe\u40ff\u4100\u4101\u4102\u4103\u4104\u4105\u4106\u4107\u4108\u4109\u410a\u410b\u410c\u410d\u410e\u410f\u4110\u4111\u4112\u4113\u4114\u4115\u4116\u4117\u4118\u4119\u411a\u411b\u411c\u411d\u411e\u411f\u4120\u4121\u4122\u4123\u4124\u4125\u4126\u4127\u4128\u4129\u412a\u412b\u412c\u412d\u412e\u412f\u4130\u4131\u4132\u4133\u4134\u4135\u4136\u4137\u4138\u4139\u413a\u413b\u413c\u413d\u413e\u413f\u4140\u4141\u4142\u4143\u4144\u4145\u4146\u4147\u4148\u4149\u414a\u414b\u414c\u414d\u414e\u414f\u4150\u4151\u4152\u4153\u4154\u4155\u4156\u4157\u4158\u4159\u415a\u415b\u415c\u415d\u415e\u415f\u4160\u4161\u4162\u4163\u4164\u4165\u4166\u4167\u4168\u4169\u416a\u416b\u416c\u416d\u416e\u416f\u4170\u4171\u4172\u4173\u4174\u4175\u4176\u4177\u4178\u4179\u417a\u417b\u417c\u417d\u417e\u417f\u4180\u4181\u4182\u4183\u4184\u4185\u4186\u4187\u4188\u4189\u418a\u418b\u418c\u418d\u418e\u418f\u4190\u4191\u4192\u4193\u4194\u4195\u4196\u4197\u4198\u4199\u419a\u419b\u419c\u419d\u419e\u419f\u41a0\u41a1\u41a2\u41a3\u41a4\u41a5\u41a6\u41a7\u41a8\u41a9\u41aa\u41ab\u41ac\u41ad\u41ae\u41af\u41b0\u41b1\u41b2\u41b3\u41b4\u41b5\u41b6\u41b7\u41b8\u41b9\u41ba\u41bb\u41bc\u41bd\u41be\u41bf\u41c0\u41c1\u41c2\u41c3\u41c4\u41c5\u41c6\u41c7\u41c8\u41c9\u41ca\u41cb\u41cc\u41cd\u41ce\u41cf\u41d0\u41d1\u41d2\u41d3\u41d4\u41d5\u41d6\u41d7\u41d8\u41d9\u41da\u41db\u41dc\u41dd\u41de\u41df\u41e0\u41e1\u41e2\u41e3\u41e4\u41e5\u41e6\u41e7\u41e8\u41e9\u41ea\u41eb\u41ec\u41ed\u41ee\u41ef\u41f0\u41f1\u41f2\u41f3\u41f4\u41f5\u41f6\u41f7\u41f8\u41f9\u41fa\u41fb\u41fc\u41fd\u41fe\u41ff\u4200\u4201\u4202\u4203\u4204\u4205\u4206\u4207\u4208\u4209\u420a\u420b\u420c\u420d\u420e\u420f\u4210\u4211\u4212\u4213\u4214\u4215\u4216\u4217\u4218\u4219\u421a\u421b\u421c\u421d\u421e\u421f\u4220\u4221\u4222\u4223\u4224\u4225\u4226\u4227\u4228\u4229\u422a\u422b\u422c\u422d\u422e\u422f\u4230\u4231\u4232\u4233\u4234\u4235\u4236\u4237\u4238\u4239\u423a\u423b\u423c\u423d\u423e\u423f\u4240\u4241\u4242\u4243\u4244\u4245\u4246\u4247\u4248\u4249\u424a\u424b\u424c\u424d\u424e\u424f\u4250\u4251\u4252\u4253\u4254\u4255\u4256\u4257\u4258\u4259\u425a\u425b\u425c\u425d\u425e\u425f\u4260\u4261\u4262\u4263\u4264\u4265\u4266\u4267\u4268\u4269\u426a\u426b\u426c\u426d\u426e\u426f\u4270\u4271\u4272\u4273\u4274\u4275\u4276\u4277\u4278\u4279\u427a\u427b\u427c\u427d\u427e\u427f\u4280\u4281\u4282\u4283\u4284\u4285\u4286\u4287\u4288\u4289\u428a\u428b\u428c\u428d\u428e\u428f\u4290\u4291\u4292\u4293\u4294\u4295\u4296\u4297\u4298\u4299\u429a\u429b\u429c\u429d\u429e\u429f\u42a0\u42a1\u42a2\u42a3\u42a4\u42a5\u42a6\u42a7\u42a8\u42a9\u42aa\u42ab\u42ac\u42ad\u42ae\u42af\u42b0\u42b1\u42b2\u42b3\u42b4\u42b5\u42b6\u42b7\u42b8\u42b9\u42ba\u42bb\u42bc\u42bd\u42be\u42bf\u42c0\u42c1\u42c2\u42c3\u42c4\u42c5\u42c6\u42c7\u42c8\u42c9\u42ca\u42cb\u42cc\u42cd\u42ce\u42cf\u42d0\u42d1\u42d2\u42d3\u42d4\u42d5\u42d6\u42d7\u42d8\u42d9\u42da\u42db\u42dc\u42dd\u42de\u42df\u42e0\u42e1\u42e2\u42e3\u42e4\u42e5\u42e6\u42e7\u42e8\u42e9\u42ea\u42eb\u42ec\u42ed\u42ee\u42ef\u42f0\u42f1\u42f2\u42f3\u42f4\u42f5\u42f6\u42f7\u42f8\u42f9\u42fa\u42fb\u42fc\u42fd\u42fe\u42ff\u4300\u4301\u4302\u4303\u4304\u4305\u4306\u4307\u4308\u4309\u430a\u430b\u430c\u430d\u430e\u430f\u4310\u4311\u4312\u4313\u4314\u4315\u4316\u4317\u4318\u4319\u431a\u431b\u431c\u431d\u431e\u431f\u4320\u4321\u4322\u4323\u4324\u4325\u4326\u4327\u4328\u4329\u432a\u432b\u432c\u432d\u432e\u432f\u4330\u4331\u4332\u4333\u4334\u4335\u4336\u4337\u4338\u4339\u433a\u433b\u433c\u433d\u433e\u433f\u4340\u4341\u4342\u4343\u4344\u4345\u4346\u4347\u4348\u4349\u434a\u434b\u434c\u434d\u434e\u434f\u4350\u4351\u4352\u4353\u4354\u4355\u4356\u4357\u4358\u4359\u435a\u435b\u435c\u435d\u435e\u435f\u4360\u4361\u4362\u4363\u4364\u4365\u4366\u4367\u4368\u4369\u436a\u436b\u436c\u436d\u436e\u436f\u4370\u4371\u4372\u4373\u4374\u4375\u4376\u4377\u4378\u4379\u437a\u437b\u437c\u437d\u437e\u437f\u4380\u4381\u4382\u4383\u4384\u4385\u4386\u4387\u4388\u4389\u438a\u438b\u438c\u438d\u438e\u438f\u4390\u4391\u4392\u4393\u4394\u4395\u4396\u4397\u4398\u4399\u439a\u439b\u439c\u439d\u439e\u439f\u43a0\u43a1\u43a2\u43a3\u43a4\u43a5\u43a6\u43a7\u43a8\u43a9\u43aa\u43ab\u43ac\u43ad\u43ae\u43af\u43b0\u43b1\u43b2\u43b3\u43b4\u43b5\u43b6\u43b7\u43b8\u43b9\u43ba\u43bb\u43bc\u43bd\u43be\u43bf\u43c0\u43c1\u43c2\u43c3\u43c4\u43c5\u43c6\u43c7\u43c8\u43c9\u43ca\u43cb\u43cc\u43cd\u43ce\u43cf\u43d0\u43d1\u43d2\u43d3\u43d4\u43d5\u43d6\u43d7\u43d8\u43d9\u43da\u43db\u43dc\u43dd\u43de\u43df\u43e0\u43e1\u43e2\u43e3\u43e4\u43e5\u43e6\u43e7\u43e8\u43e9\u43ea\u43eb\u43ec\u43ed\u43ee\u43ef\u43f0\u43f1\u43f2\u43f3\u43f4\u43f5\u43f6\u43f7\u43f8\u43f9\u43fa\u43fb\u43fc\u43fd\u43fe\u43ff\u4400\u4401\u4402\u4403\u4404\u4405\u4406\u4407\u4408\u4409\u440a\u440b\u440c\u440d\u440e\u440f\u4410\u4411\u4412\u4413\u4414\u4415\u4416\u4417\u4418\u4419\u441a\u441b\u441c\u441d\u441e\u441f\u4420\u4421\u4422\u4423\u4424\u4425\u4426\u4427\u4428\u4429\u442a\u442b\u442c\u442d\u442e\u442f\u4430\u4431\u4432\u4433\u4434\u4435\u4436\u4437\u4438\u4439\u443a\u443b\u443c\u443d\u443e\u443f\u4440\u4441\u4442\u4443\u4444\u4445\u4446\u4447\u4448\u4449\u444a\u444b\u444c\u444d\u444e\u444f\u4450\u4451\u4452\u4453\u4454\u4455\u4456\u4457\u4458\u4459\u445a\u445b\u445c\u445d\u445e\u445f\u4460\u4461\u4462\u4463\u4464\u4465\u4466\u4467\u4468\u4469\u446a\u446b\u446c\u446d\u446e\u446f\u4470\u4471\u4472\u4473\u4474\u4475\u4476\u4477\u4478\u4479\u447a\u447b\u447c\u447d\u447e\u447f\u4480\u4481\u4482\u4483\u4484\u4485\u4486\u4487\u4488\u4489\u448a\u448b\u448c\u448d\u448e\u448f\u4490\u4491\u4492\u4493\u4494\u4495\u4496\u4497\u4498\u4499\u449a\u449b\u449c\u449d\u449e\u449f\u44a0\u44a1\u44a2\u44a3\u44a4\u44a5\u44a6\u44a7\u44a8\u44a9\u44aa\u44ab\u44ac\u44ad\u44ae\u44af\u44b0\u44b1\u44b2\u44b3\u44b4\u44b5\u44b6\u44b7\u44b8\u44b9\u44ba\u44bb\u44bc\u44bd\u44be\u44bf\u44c0\u44c1\u44c2\u44c3\u44c4\u44c5\u44c6\u44c7\u44c8\u44c9\u44ca\u44cb\u44cc\u44cd\u44ce\u44cf\u44d0\u44d1\u44d2\u44d3\u44d4\u44d5\u44d6\u44d7\u44d8\u44d9\u44da\u44db\u44dc\u44dd\u44de\u44df\u44e0\u44e1\u44e2\u44e3\u44e4\u44e5\u44e6\u44e7\u44e8\u44e9\u44ea\u44eb\u44ec\u44ed\u44ee\u44ef\u44f0\u44f1\u44f2\u44f3\u44f4\u44f5\u44f6\u44f7\u44f8\u44f9\u44fa\u44fb\u44fc\u44fd\u44fe\u44ff\u4500\u4501\u4502\u4503\u4504\u4505\u4506\u4507\u4508\u4509\u450a\u450b\u450c\u450d\u450e\u450f\u4510\u4511\u4512\u4513\u4514\u4515\u4516\u4517\u4518\u4519\u451a\u451b\u451c\u451d\u451e\u451f\u4520\u4521\u4522\u4523\u4524\u4525\u4526\u4527\u4528\u4529\u452a\u452b\u452c\u452d\u452e\u452f\u4530\u4531\u4532\u4533\u4534\u4535\u4536\u4537\u4538\u4539\u453a\u453b\u453c\u453d\u453e\u453f\u4540\u4541\u4542\u4543\u4544\u4545\u4546\u4547\u4548\u4549\u454a\u454b\u454c\u454d\u454e\u454f\u4550\u4551\u4552\u4553\u4554\u4555\u4556\u4557\u4558\u4559\u455a\u455b\u455c\u455d\u455e\u455f\u4560\u4561\u4562\u4563\u4564\u4565\u4566\u4567\u4568\u4569\u456a\u456b\u456c\u456d\u456e\u456f\u4570\u4571\u4572\u4573\u4574\u4575\u4576\u4577\u4578\u4579\u457a\u457b\u457c\u457d\u457e\u457f\u4580\u4581\u4582\u4583\u4584\u4585\u4586\u4587\u4588\u4589\u458a\u458b\u458c\u458d\u458e\u458f\u4590\u4591\u4592\u4593\u4594\u4595\u4596\u4597\u4598\u4599\u459a\u459b\u459c\u459d\u459e\u459f\u45a0\u45a1\u45a2\u45a3\u45a4\u45a5\u45a6\u45a7\u45a8\u45a9\u45aa\u45ab\u45ac\u45ad\u45ae\u45af\u45b0\u45b1\u45b2\u45b3\u45b4\u45b5\u45b6\u45b7\u45b8\u45b9\u45ba\u45bb\u45bc\u45bd\u45be\u45bf\u45c0\u45c1\u45c2\u45c3\u45c4\u45c5\u45c6\u45c7\u45c8\u45c9\u45ca\u45cb\u45cc\u45cd\u45ce\u45cf\u45d0\u45d1\u45d2\u45d3\u45d4\u45d5\u45d6\u45d7\u45d8\u45d9\u45da\u45db\u45dc\u45dd\u45de\u45df\u45e0\u45e1\u45e2\u45e3\u45e4\u45e5\u45e6\u45e7\u45e8\u45e9\u45ea\u45eb\u45ec\u45ed\u45ee\u45ef\u45f0\u45f1\u45f2\u45f3\u45f4\u45f5\u45f6\u45f7\u45f8\u45f9\u45fa\u45fb\u45fc\u45fd\u45fe\u45ff\u4600\u4601\u4602\u4603\u4604\u4605\u4606\u4607\u4608\u4609\u460a\u460b\u460c\u460d\u460e\u460f\u4610\u4611\u4612\u4613\u4614\u4615\u4616\u4617\u4618\u4619\u461a\u461b\u461c\u461d\u461e\u461f\u4620\u4621\u4622\u4623\u4624\u4625\u4626\u4627\u4628\u4629\u462a\u462b\u462c\u462d\u462e\u462f\u4630\u4631\u4632\u4633\u4634\u4635\u4636\u4637\u4638\u4639\u463a\u463b\u463c\u463d\u463e\u463f\u4640\u4641\u4642\u4643\u4644\u4645\u4646\u4647\u4648\u4649\u464a\u464b\u464c\u464d\u464e\u464f\u4650\u4651\u4652\u4653\u4654\u4655\u4656\u4657\u4658\u4659\u465a\u465b\u465c\u465d\u465e\u465f\u4660\u4661\u4662\u4663\u4664\u4665\u4666\u4667\u4668\u4669\u466a\u466b\u466c\u466d\u466e\u466f\u4670\u4671\u4672\u4673\u4674\u4675\u4676\u4677\u4678\u4679\u467a\u467b\u467c\u467d\u467e\u467f\u4680\u4681\u4682\u4683\u4684\u4685\u4686\u4687\u4688\u4689\u468a\u468b\u468c\u468d\u468e\u468f\u4690\u4691\u4692\u4693\u4694\u4695\u4696\u4697\u4698\u4699\u469a\u469b\u469c\u469d\u469e\u469f\u46a0\u46a1\u46a2\u46a3\u46a4\u46a5\u46a6\u46a7\u46a8\u46a9\u46aa\u46ab\u46ac\u46ad\u46ae\u46af\u46b0\u46b1\u46b2\u46b3\u46b4\u46b5\u46b6\u46b7\u46b8\u46b9\u46ba\u46bb\u46bc\u46bd\u46be\u46bf\u46c0\u46c1\u46c2\u46c3\u46c4\u46c5\u46c6\u46c7\u46c8\u46c9\u46ca\u46cb\u46cc\u46cd\u46ce\u46cf\u46d0\u46d1\u46d2\u46d3\u46d4\u46d5\u46d6\u46d7\u46d8\u46d9\u46da\u46db\u46dc\u46dd\u46de\u46df\u46e0\u46e1\u46e2\u46e3\u46e4\u46e5\u46e6\u46e7\u46e8\u46e9\u46ea\u46eb\u46ec\u46ed\u46ee\u46ef\u46f0\u46f1\u46f2\u46f3\u46f4\u46f5\u46f6\u46f7\u46f8\u46f9\u46fa\u46fb\u46fc\u46fd\u46fe\u46ff\u4700\u4701\u4702\u4703\u4704\u4705\u4706\u4707\u4708\u4709\u470a\u470b\u470c\u470d\u470e\u470f\u4710\u4711\u4712\u4713\u4714\u4715\u4716\u4717\u4718\u4719\u471a\u471b\u471c\u471d\u471e\u471f\u4720\u4721\u4722\u4723\u4724\u4725\u4726\u4727\u4728\u4729\u472a\u472b\u472c\u472d\u472e\u472f\u4730\u4731\u4732\u4733\u4734\u4735\u4736\u4737\u4738\u4739\u473a\u473b\u473c\u473d\u473e\u473f\u4740\u4741\u4742\u4743\u4744\u4745\u4746\u4747\u4748\u4749\u474a\u474b\u474c\u474d\u474e\u474f\u4750\u4751\u4752\u4753\u4754\u4755\u4756\u4757\u4758\u4759\u475a\u475b\u475c\u475d\u475e\u475f\u4760\u4761\u4762\u4763\u4764\u4765\u4766\u4767\u4768\u4769\u476a\u476b\u476c\u476d\u476e\u476f\u4770\u4771\u4772\u4773\u4774\u4775\u4776\u4777\u4778\u4779\u477a\u477b\u477c\u477d\u477e\u477f\u4780\u4781\u4782\u4783\u4784\u4785\u4786\u4787\u4788\u4789\u478a\u478b\u478c\u478d\u478e\u478f\u4790\u4791\u4792\u4793\u4794\u4795\u4796\u4797\u4798\u4799\u479a\u479b\u479c\u479d\u479e\u479f\u47a0\u47a1\u47a2\u47a3\u47a4\u47a5\u47a6\u47a7\u47a8\u47a9\u47aa\u47ab\u47ac\u47ad\u47ae\u47af\u47b0\u47b1\u47b2\u47b3\u47b4\u47b5\u47b6\u47b7\u47b8\u47b9\u47ba\u47bb\u47bc\u47bd\u47be\u47bf\u47c0\u47c1\u47c2\u47c3\u47c4\u47c5\u47c6\u47c7\u47c8\u47c9\u47ca\u47cb\u47cc\u47cd\u47ce\u47cf\u47d0\u47d1\u47d2\u47d3\u47d4\u47d5\u47d6\u47d7\u47d8\u47d9\u47da\u47db\u47dc\u47dd\u47de\u47df\u47e0\u47e1\u47e2\u47e3\u47e4\u47e5\u47e6\u47e7\u47e8\u47e9\u47ea\u47eb\u47ec\u47ed\u47ee\u47ef\u47f0\u47f1\u47f2\u47f3\u47f4\u47f5\u47f6\u47f7\u47f8\u47f9\u47fa\u47fb\u47fc\u47fd\u47fe\u47ff\u4800\u4801\u4802\u4803\u4804\u4805\u4806\u4807\u4808\u4809\u480a\u480b\u480c\u480d\u480e\u480f\u4810\u4811\u4812\u4813\u4814\u4815\u4816\u4817\u4818\u4819\u481a\u481b\u481c\u481d\u481e\u481f\u4820\u4821\u4822\u4823\u4824\u4825\u4826\u4827\u4828\u4829\u482a\u482b\u482c\u482d\u482e\u482f\u4830\u4831\u4832\u4833\u4834\u4835\u4836\u4837\u4838\u4839\u483a\u483b\u483c\u483d\u483e\u483f\u4840\u4841\u4842\u4843\u4844\u4845\u4846\u4847\u4848\u4849\u484a\u484b\u484c\u484d\u484e\u484f\u4850\u4851\u4852\u4853\u4854\u4855\u4856\u4857\u4858\u4859\u485a\u485b\u485c\u485d\u485e\u485f\u4860\u4861\u4862\u4863\u4864\u4865\u4866\u4867\u4868\u4869\u486a\u486b\u486c\u486d\u486e\u486f\u4870\u4871\u4872\u4873\u4874\u4875\u4876\u4877\u4878\u4879\u487a\u487b\u487c\u487d\u487e\u487f\u4880\u4881\u4882\u4883\u4884\u4885\u4886\u4887\u4888\u4889\u488a\u488b\u488c\u488d\u488e\u488f\u4890\u4891\u4892\u4893\u4894\u4895\u4896\u4897\u4898\u4899\u489a\u489b\u489c\u489d\u489e\u489f\u48a0\u48a1\u48a2\u48a3\u48a4\u48a5\u48a6\u48a7\u48a8\u48a9\u48aa\u48ab\u48ac\u48ad\u48ae\u48af\u48b0\u48b1\u48b2\u48b3\u48b4\u48b5\u48b6\u48b7\u48b8\u48b9\u48ba\u48bb\u48bc\u48bd\u48be\u48bf\u48c0\u48c1\u48c2\u48c3\u48c4\u48c5\u48c6\u48c7\u48c8\u48c9\u48ca\u48cb\u48cc\u48cd\u48ce\u48cf\u48d0\u48d1\u48d2\u48d3\u48d4\u48d5\u48d6\u48d7\u48d8\u48d9\u48da\u48db\u48dc\u48dd\u48de\u48df\u48e0\u48e1\u48e2\u48e3\u48e4\u48e5\u48e6\u48e7\u48e8\u48e9\u48ea\u48eb\u48ec\u48ed\u48ee\u48ef\u48f0\u48f1\u48f2\u48f3\u48f4\u48f5\u48f6\u48f7\u48f8\u48f9\u48fa\u48fb\u48fc\u48fd\u48fe\u48ff\u4900\u4901\u4902\u4903\u4904\u4905\u4906\u4907\u4908\u4909\u490a\u490b\u490c\u490d\u490e\u490f\u4910\u4911\u4912\u4913\u4914\u4915\u4916\u4917\u4918\u4919\u491a\u491b\u491c\u491d\u491e\u491f\u4920\u4921\u4922\u4923\u4924\u4925\u4926\u4927\u4928\u4929\u492a\u492b\u492c\u492d\u492e\u492f\u4930\u4931\u4932\u4933\u4934\u4935\u4936\u4937\u4938\u4939\u493a\u493b\u493c\u493d\u493e\u493f\u4940\u4941\u4942\u4943\u4944\u4945\u4946\u4947\u4948\u4949\u494a\u494b\u494c\u494d\u494e\u494f\u4950\u4951\u4952\u4953\u4954\u4955\u4956\u4957\u4958\u4959\u495a\u495b\u495c\u495d\u495e\u495f\u4960\u4961\u4962\u4963\u4964\u4965\u4966\u4967\u4968\u4969\u496a\u496b\u496c\u496d\u496e\u496f\u4970\u4971\u4972\u4973\u4974\u4975\u4976\u4977\u4978\u4979\u497a\u497b\u497c\u497d\u497e\u497f\u4980\u4981\u4982\u4983\u4984\u4985\u4986\u4987\u4988\u4989\u498a\u498b\u498c\u498d\u498e\u498f\u4990\u4991\u4992\u4993\u4994\u4995\u4996\u4997\u4998\u4999\u499a\u499b\u499c\u499d\u499e\u499f\u49a0\u49a1\u49a2\u49a3\u49a4\u49a5\u49a6\u49a7\u49a8\u49a9\u49aa\u49ab\u49ac\u49ad\u49ae\u49af\u49b0\u49b1\u49b2\u49b3\u49b4\u49b5\u49b6\u49b7\u49b8\u49b9\u49ba\u49bb\u49bc\u49bd\u49be\u49bf\u49c0\u49c1\u49c2\u49c3\u49c4\u49c5\u49c6\u49c7\u49c8\u49c9\u49ca\u49cb\u49cc\u49cd\u49ce\u49cf\u49d0\u49d1\u49d2\u49d3\u49d4\u49d5\u49d6\u49d7\u49d8\u49d9\u49da\u49db\u49dc\u49dd\u49de\u49df\u49e0\u49e1\u49e2\u49e3\u49e4\u49e5\u49e6\u49e7\u49e8\u49e9\u49ea\u49eb\u49ec\u49ed\u49ee\u49ef\u49f0\u49f1\u49f2\u49f3\u49f4\u49f5\u49f6\u49f7\u49f8\u49f9\u49fa\u49fb\u49fc\u49fd\u49fe\u49ff\u4a00\u4a01\u4a02\u4a03\u4a04\u4a05\u4a06\u4a07\u4a08\u4a09\u4a0a\u4a0b\u4a0c\u4a0d\u4a0e\u4a0f\u4a10\u4a11\u4a12\u4a13\u4a14\u4a15\u4a16\u4a17\u4a18\u4a19\u4a1a\u4a1b\u4a1c\u4a1d\u4a1e\u4a1f\u4a20\u4a21\u4a22\u4a23\u4a24\u4a25\u4a26\u4a27\u4a28\u4a29\u4a2a\u4a2b\u4a2c\u4a2d\u4a2e\u4a2f\u4a30\u4a31\u4a32\u4a33\u4a34\u4a35\u4a36\u4a37\u4a38\u4a39\u4a3a\u4a3b\u4a3c\u4a3d\u4a3e\u4a3f\u4a40\u4a41\u4a42\u4a43\u4a44\u4a45\u4a46\u4a47\u4a48\u4a49\u4a4a\u4a4b\u4a4c\u4a4d\u4a4e\u4a4f\u4a50\u4a51\u4a52\u4a53\u4a54\u4a55\u4a56\u4a57\u4a58\u4a59\u4a5a\u4a5b\u4a5c\u4a5d\u4a5e\u4a5f\u4a60\u4a61\u4a62\u4a63\u4a64\u4a65\u4a66\u4a67\u4a68\u4a69\u4a6a\u4a6b\u4a6c\u4a6d\u4a6e\u4a6f\u4a70\u4a71\u4a72\u4a73\u4a74\u4a75\u4a76\u4a77\u4a78\u4a79\u4a7a\u4a7b\u4a7c\u4a7d\u4a7e\u4a7f\u4a80\u4a81\u4a82\u4a83\u4a84\u4a85\u4a86\u4a87\u4a88\u4a89\u4a8a\u4a8b\u4a8c\u4a8d\u4a8e\u4a8f\u4a90\u4a91\u4a92\u4a93\u4a94\u4a95\u4a96\u4a97\u4a98\u4a99\u4a9a\u4a9b\u4a9c\u4a9d\u4a9e\u4a9f\u4aa0\u4aa1\u4aa2\u4aa3\u4aa4\u4aa5\u4aa6\u4aa7\u4aa8\u4aa9\u4aaa\u4aab\u4aac\u4aad\u4aae\u4aaf\u4ab0\u4ab1\u4ab2\u4ab3\u4ab4\u4ab5\u4ab6\u4ab7\u4ab8\u4ab9\u4aba\u4abb\u4abc\u4abd\u4abe\u4abf\u4ac0\u4ac1\u4ac2\u4ac3\u4ac4\u4ac5\u4ac6\u4ac7\u4ac8\u4ac9\u4aca\u4acb\u4acc\u4acd\u4ace\u4acf\u4ad0\u4ad1\u4ad2\u4ad3\u4ad4\u4ad5\u4ad6\u4ad7\u4ad8\u4ad9\u4ada\u4adb\u4adc\u4add\u4ade\u4adf\u4ae0\u4ae1\u4ae2\u4ae3\u4ae4\u4ae5\u4ae6\u4ae7\u4ae8\u4ae9\u4aea\u4aeb\u4aec\u4aed\u4aee\u4aef\u4af0\u4af1\u4af2\u4af3\u4af4\u4af5\u4af6\u4af7\u4af8\u4af9\u4afa\u4afb\u4afc\u4afd\u4afe\u4aff\u4b00\u4b01\u4b02\u4b03\u4b04\u4b05\u4b06\u4b07\u4b08\u4b09\u4b0a\u4b0b\u4b0c\u4b0d\u4b0e\u4b0f\u4b10\u4b11\u4b12\u4b13\u4b14\u4b15\u4b16\u4b17\u4b18\u4b19\u4b1a\u4b1b\u4b1c\u4b1d\u4b1e\u4b1f\u4b20\u4b21\u4b22\u4b23\u4b24\u4b25\u4b26\u4b27\u4b28\u4b29\u4b2a\u4b2b\u4b2c\u4b2d\u4b2e\u4b2f\u4b30\u4b31\u4b32\u4b33\u4b34\u4b35\u4b36\u4b37\u4b38\u4b39\u4b3a\u4b3b\u4b3c\u4b3d\u4b3e\u4b3f\u4b40\u4b41\u4b42\u4b43\u4b44\u4b45\u4b46\u4b47\u4b48\u4b49\u4b4a\u4b4b\u4b4c\u4b4d\u4b4e\u4b4f\u4b50\u4b51\u4b52\u4b53\u4b54\u4b55\u4b56\u4b57\u4b58\u4b59\u4b5a\u4b5b\u4b5c\u4b5d\u4b5e\u4b5f\u4b60\u4b61\u4b62\u4b63\u4b64\u4b65\u4b66\u4b67\u4b68\u4b69\u4b6a\u4b6b\u4b6c\u4b6d\u4b6e\u4b6f\u4b70\u4b71\u4b72\u4b73\u4b74\u4b75\u4b76\u4b77\u4b78\u4b79\u4b7a\u4b7b\u4b7c\u4b7d\u4b7e\u4b7f\u4b80\u4b81\u4b82\u4b83\u4b84\u4b85\u4b86\u4b87\u4b88\u4b89\u4b8a\u4b8b\u4b8c\u4b8d\u4b8e\u4b8f\u4b90\u4b91\u4b92\u4b93\u4b94\u4b95\u4b96\u4b97\u4b98\u4b99\u4b9a\u4b9b\u4b9c\u4b9d\u4b9e\u4b9f\u4ba0\u4ba1\u4ba2\u4ba3\u4ba4\u4ba5\u4ba6\u4ba7\u4ba8\u4ba9\u4baa\u4bab\u4bac\u4bad\u4bae\u4baf\u4bb0\u4bb1\u4bb2\u4bb3\u4bb4\u4bb5\u4bb6\u4bb7\u4bb8\u4bb9\u4bba\u4bbb\u4bbc\u4bbd\u4bbe\u4bbf\u4bc0\u4bc1\u4bc2\u4bc3\u4bc4\u4bc5\u4bc6\u4bc7\u4bc8\u4bc9\u4bca\u4bcb\u4bcc\u4bcd\u4bce\u4bcf\u4bd0\u4bd1\u4bd2\u4bd3\u4bd4\u4bd5\u4bd6\u4bd7\u4bd8\u4bd9\u4bda\u4bdb\u4bdc\u4bdd\u4bde\u4bdf\u4be0\u4be1\u4be2\u4be3\u4be4\u4be5\u4be6\u4be7\u4be8\u4be9\u4bea\u4beb\u4bec\u4bed\u4bee\u4bef\u4bf0\u4bf1\u4bf2\u4bf3\u4bf4\u4bf5\u4bf6\u4bf7\u4bf8\u4bf9\u4bfa\u4bfb\u4bfc\u4bfd\u4bfe\u4bff\u4c00\u4c01\u4c02\u4c03\u4c04\u4c05\u4c06\u4c07\u4c08\u4c09\u4c0a\u4c0b\u4c0c\u4c0d\u4c0e\u4c0f\u4c10\u4c11\u4c12\u4c13\u4c14\u4c15\u4c16\u4c17\u4c18\u4c19\u4c1a\u4c1b\u4c1c\u4c1d\u4c1e\u4c1f\u4c20\u4c21\u4c22\u4c23\u4c24\u4c25\u4c26\u4c27\u4c28\u4c29\u4c2a\u4c2b\u4c2c\u4c2d\u4c2e\u4c2f\u4c30\u4c31\u4c32\u4c33\u4c34\u4c35\u4c36\u4c37\u4c38\u4c39\u4c3a\u4c3b\u4c3c\u4c3d\u4c3e\u4c3f\u4c40\u4c41\u4c42\u4c43\u4c44\u4c45\u4c46\u4c47\u4c48\u4c49\u4c4a\u4c4b\u4c4c\u4c4d\u4c4e\u4c4f\u4c50\u4c51\u4c52\u4c53\u4c54\u4c55\u4c56\u4c57\u4c58\u4c59\u4c5a\u4c5b\u4c5c\u4c5d\u4c5e\u4c5f\u4c60\u4c61\u4c62\u4c63\u4c64\u4c65\u4c66\u4c67\u4c68\u4c69\u4c6a\u4c6b\u4c6c\u4c6d\u4c6e\u4c6f\u4c70\u4c71\u4c72\u4c73\u4c74\u4c75\u4c76\u4c77\u4c78\u4c79\u4c7a\u4c7b\u4c7c\u4c7d\u4c7e\u4c7f\u4c80\u4c81\u4c82\u4c83\u4c84\u4c85\u4c86\u4c87\u4c88\u4c89\u4c8a\u4c8b\u4c8c\u4c8d\u4c8e\u4c8f\u4c90\u4c91\u4c92\u4c93\u4c94\u4c95\u4c96\u4c97\u4c98\u4c99\u4c9a\u4c9b\u4c9c\u4c9d\u4c9e\u4c9f\u4ca0\u4ca1\u4ca2\u4ca3\u4ca4\u4ca5\u4ca6\u4ca7\u4ca8\u4ca9\u4caa\u4cab\u4cac\u4cad\u4cae\u4caf\u4cb0\u4cb1\u4cb2\u4cb3\u4cb4\u4cb5\u4cb6\u4cb7\u4cb8\u4cb9\u4cba\u4cbb\u4cbc\u4cbd\u4cbe\u4cbf\u4cc0\u4cc1\u4cc2\u4cc3\u4cc4\u4cc5\u4cc6\u4cc7\u4cc8\u4cc9\u4cca\u4ccb\u4ccc\u4ccd\u4cce\u4ccf\u4cd0\u4cd1\u4cd2\u4cd3\u4cd4\u4cd5\u4cd6\u4cd7\u4cd8\u4cd9\u4cda\u4cdb\u4cdc\u4cdd\u4cde\u4cdf\u4ce0\u4ce1\u4ce2\u4ce3\u4ce4\u4ce5\u4ce6\u4ce7\u4ce8\u4ce9\u4cea\u4ceb\u4cec\u4ced\u4cee\u4cef\u4cf0\u4cf1\u4cf2\u4cf3\u4cf4\u4cf5\u4cf6\u4cf7\u4cf8\u4cf9\u4cfa\u4cfb\u4cfc\u4cfd\u4cfe\u4cff\u4d00\u4d01\u4d02\u4d03\u4d04\u4d05\u4d06\u4d07\u4d08\u4d09\u4d0a\u4d0b\u4d0c\u4d0d\u4d0e\u4d0f\u4d10\u4d11\u4d12\u4d13\u4d14\u4d15\u4d16\u4d17\u4d18\u4d19\u4d1a\u4d1b\u4d1c\u4d1d\u4d1e\u4d1f\u4d20\u4d21\u4d22\u4d23\u4d24\u4d25\u4d26\u4d27\u4d28\u4d29\u4d2a\u4d2b\u4d2c\u4d2d\u4d2e\u4d2f\u4d30\u4d31\u4d32\u4d33\u4d34\u4d35\u4d36\u4d37\u4d38\u4d39\u4d3a\u4d3b\u4d3c\u4d3d\u4d3e\u4d3f\u4d40\u4d41\u4d42\u4d43\u4d44\u4d45\u4d46\u4d47\u4d48\u4d49\u4d4a\u4d4b\u4d4c\u4d4d\u4d4e\u4d4f\u4d50\u4d51\u4d52\u4d53\u4d54\u4d55\u4d56\u4d57\u4d58\u4d59\u4d5a\u4d5b\u4d5c\u4d5d\u4d5e\u4d5f\u4d60\u4d61\u4d62\u4d63\u4d64\u4d65\u4d66\u4d67\u4d68\u4d69\u4d6a\u4d6b\u4d6c\u4d6d\u4d6e\u4d6f\u4d70\u4d71\u4d72\u4d73\u4d74\u4d75\u4d76\u4d77\u4d78\u4d79\u4d7a\u4d7b\u4d7c\u4d7d\u4d7e\u4d7f\u4d80\u4d81\u4d82\u4d83\u4d84\u4d85\u4d86\u4d87\u4d88\u4d89\u4d8a\u4d8b\u4d8c\u4d8d\u4d8e\u4d8f\u4d90\u4d91\u4d92\u4d93\u4d94\u4d95\u4d96\u4d97\u4d98\u4d99\u4d9a\u4d9b\u4d9c\u4d9d\u4d9e\u4d9f\u4da0\u4da1\u4da2\u4da3\u4da4\u4da5\u4da6\u4da7\u4da8\u4da9\u4daa\u4dab\u4dac\u4dad\u4dae\u4daf\u4db0\u4db1\u4db2\u4db3\u4db4\u4db5\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08\u4e09\u4e0a\u4e0b\u4e0c\u4e0d\u4e0e\u4e0f\u4e10\u4e11\u4e12\u4e13\u4e14\u4e15\u4e16\u4e17\u4e18\u4e19\u4e1a\u4e1b\u4e1c\u4e1d\u4e1e\u4e1f\u4e20\u4e21\u4e22\u4e23\u4e24\u4e25\u4e26\u4e27\u4e28\u4e29\u4e2a\u4e2b\u4e2c\u4e2d\u4e2e\u4e2f\u4e30\u4e31\u4e32\u4e33\u4e34\u4e35\u4e36\u4e37\u4e38\u4e39\u4e3a\u4e3b\u4e3c\u4e3d\u4e3e\u4e3f\u4e40\u4e41\u4e42\u4e43\u4e44\u4e45\u4e46\u4e47\u4e48\u4e49\u4e4a\u4e4b\u4e4c\u4e4d\u4e4e\u4e4f\u4e50\u4e51\u4e52\u4e53\u4e54\u4e55\u4e56\u4e57\u4e58\u4e59\u4e5a\u4e5b\u4e5c\u4e5d\u4e5e\u4e5f\u4e60\u4e61\u4e62\u4e63\u4e64\u4e65\u4e66\u4e67\u4e68\u4e69\u4e6a\u4e6b\u4e6c\u4e6d\u4e6e\u4e6f\u4e70\u4e71\u4e72\u4e73\u4e74\u4e75\u4e76\u4e77\u4e78\u4e79\u4e7a\u4e7b\u4e7c\u4e7d\u4e7e\u4e7f\u4e80\u4e81\u4e82\u4e83\u4e84\u4e85\u4e86\u4e87\u4e88\u4e89\u4e8a\u4e8b\u4e8c\u4e8d\u4e8e\u4e8f\u4e90\u4e91\u4e92\u4e93\u4e94\u4e95\u4e96\u4e97\u4e98\u4e99\u4e9a\u4e9b\u4e9c\u4e9d\u4e9e\u4e9f\u4ea0\u4ea1\u4ea2\u4ea3\u4ea4\u4ea5\u4ea6\u4ea7\u4ea8\u4ea9\u4eaa\u4eab\u4eac\u4ead\u4eae\u4eaf\u4eb0\u4eb1\u4eb2\u4eb3\u4eb4\u4eb5\u4eb6\u4eb7\u4eb8\u4eb9\u4eba\u4ebb\u4ebc\u4ebd\u4ebe\u4ebf\u4ec0\u4ec1\u4ec2\u4ec3\u4ec4\u4ec5\u4ec6\u4ec7\u4ec8\u4ec9\u4eca\u4ecb\u4ecc\u4ecd\u4ece\u4ecf\u4ed0\u4ed1\u4ed2\u4ed3\u4ed4\u4ed5\u4ed6\u4ed7\u4ed8\u4ed9\u4eda\u4edb\u4edc\u4edd\u4ede\u4edf\u4ee0\u4ee1\u4ee2\u4ee3\u4ee4\u4ee5\u4ee6\u4ee7\u4ee8\u4ee9\u4eea\u4eeb\u4eec\u4eed\u4eee\u4eef\u4ef0\u4ef1\u4ef2\u4ef3\u4ef4\u4ef5\u4ef6\u4ef7\u4ef8\u4ef9\u4efa\u4efb\u4efc\u4efd\u4efe\u4eff\u4f00\u4f01\u4f02\u4f03\u4f04\u4f05\u4f06\u4f07\u4f08\u4f09\u4f0a\u4f0b\u4f0c\u4f0d\u4f0e\u4f0f\u4f10\u4f11\u4f12\u4f13\u4f14\u4f15\u4f16\u4f17\u4f18\u4f19\u4f1a\u4f1b\u4f1c\u4f1d\u4f1e\u4f1f\u4f20\u4f21\u4f22\u4f23\u4f24\u4f25\u4f26\u4f27\u4f28\u4f29\u4f2a\u4f2b\u4f2c\u4f2d\u4f2e\u4f2f\u4f30\u4f31\u4f32\u4f33\u4f34\u4f35\u4f36\u4f37\u4f38\u4f39\u4f3a\u4f3b\u4f3c\u4f3d\u4f3e\u4f3f\u4f40\u4f41\u4f42\u4f43\u4f44\u4f45\u4f46\u4f47\u4f48\u4f49\u4f4a\u4f4b\u4f4c\u4f4d\u4f4e\u4f4f\u4f50\u4f51\u4f52\u4f53\u4f54\u4f55\u4f56\u4f57\u4f58\u4f59\u4f5a\u4f5b\u4f5c\u4f5d\u4f5e\u4f5f\u4f60\u4f61\u4f62\u4f63\u4f64\u4f65\u4f66\u4f67\u4f68\u4f69\u4f6a\u4f6b\u4f6c\u4f6d\u4f6e\u4f6f\u4f70\u4f71\u4f72\u4f73\u4f74\u4f75\u4f76\u4f77\u4f78\u4f79\u4f7a\u4f7b\u4f7c\u4f7d\u4f7e\u4f7f\u4f80\u4f81\u4f82\u4f83\u4f84\u4f85\u4f86\u4f87\u4f88\u4f89\u4f8a\u4f8b\u4f8c\u4f8d\u4f8e\u4f8f\u4f90\u4f91\u4f92\u4f93\u4f94\u4f95\u4f96\u4f97\u4f98\u4f99\u4f9a\u4f9b\u4f9c\u4f9d\u4f9e\u4f9f\u4fa0\u4fa1\u4fa2\u4fa3\u4fa4\u4fa5\u4fa6\u4fa7\u4fa8\u4fa9\u4faa\u4fab\u4fac\u4fad\u4fae\u4faf\u4fb0\u4fb1\u4fb2\u4fb3\u4fb4\u4fb5\u4fb6\u4fb7\u4fb8\u4fb9\u4fba\u4fbb\u4fbc\u4fbd\u4fbe\u4fbf\u4fc0\u4fc1\u4fc2\u4fc3\u4fc4\u4fc5\u4fc6\u4fc7\u4fc8\u4fc9\u4fca\u4fcb\u4fcc\u4fcd\u4fce\u4fcf\u4fd0\u4fd1\u4fd2\u4fd3\u4fd4\u4fd5\u4fd6\u4fd7\u4fd8\u4fd9\u4fda\u4fdb\u4fdc\u4fdd\u4fde\u4fdf\u4fe0\u4fe1\u4fe2\u4fe3\u4fe4\u4fe5\u4fe6\u4fe7\u4fe8\u4fe9\u4fea\u4feb\u4fec\u4fed\u4fee\u4fef\u4ff0\u4ff1\u4ff2\u4ff3\u4ff4\u4ff5\u4ff6\u4ff7\u4ff8\u4ff9\u4ffa\u4ffb\u4ffc\u4ffd\u4ffe\u4fff\u5000\u5001\u5002\u5003\u5004\u5005\u5006\u5007\u5008\u5009\u500a\u500b\u500c\u500d\u500e\u500f\u5010\u5011\u5012\u5013\u5014\u5015\u5016\u5017\u5018\u5019\u501a\u501b\u501c\u501d\u501e\u501f\u5020\u5021\u5022\u5023\u5024\u5025\u5026\u5027\u5028\u5029\u502a\u502b\u502c\u502d\u502e\u502f\u5030\u5031\u5032\u5033\u5034\u5035\u5036\u5037\u5038\u5039\u503a\u503b\u503c\u503d\u503e\u503f\u5040\u5041\u5042\u5043\u5044\u5045\u5046\u5047\u5048\u5049\u504a\u504b\u504c\u504d\u504e\u504f\u5050\u5051\u5052\u5053\u5054\u5055\u5056\u5057\u5058\u5059\u505a\u505b\u505c\u505d\u505e\u505f\u5060\u5061\u5062\u5063\u5064\u5065\u5066\u5067\u5068\u5069\u506a\u506b\u506c\u506d\u506e\u506f\u5070\u5071\u5072\u5073\u5074\u5075\u5076\u5077\u5078\u5079\u507a\u507b\u507c\u507d\u507e\u507f\u5080\u5081\u5082\u5083\u5084\u5085\u5086\u5087\u5088\u5089\u508a\u508b\u508c\u508d\u508e\u508f\u5090\u5091\u5092\u5093\u5094\u5095\u5096\u5097\u5098\u5099\u509a\u509b\u509c\u509d\u509e\u509f\u50a0\u50a1\u50a2\u50a3\u50a4\u50a5\u50a6\u50a7\u50a8\u50a9\u50aa\u50ab\u50ac\u50ad\u50ae\u50af\u50b0\u50b1\u50b2\u50b3\u50b4\u50b5\u50b6\u50b7\u50b8\u50b9\u50ba\u50bb\u50bc\u50bd\u50be\u50bf\u50c0\u50c1\u50c2\u50c3\u50c4\u50c5\u50c6\u50c7\u50c8\u50c9\u50ca\u50cb\u50cc\u50cd\u50ce\u50cf\u50d0\u50d1\u50d2\u50d3\u50d4\u50d5\u50d6\u50d7\u50d8\u50d9\u50da\u50db\u50dc\u50dd\u50de\u50df\u50e0\u50e1\u50e2\u50e3\u50e4\u50e5\u50e6\u50e7\u50e8\u50e9\u50ea\u50eb\u50ec\u50ed\u50ee\u50ef\u50f0\u50f1\u50f2\u50f3\u50f4\u50f5\u50f6\u50f7\u50f8\u50f9\u50fa\u50fb\u50fc\u50fd\u50fe\u50ff\u5100\u5101\u5102\u5103\u5104\u5105\u5106\u5107\u5108\u5109\u510a\u510b\u510c\u510d\u510e\u510f\u5110\u5111\u5112\u5113\u5114\u5115\u5116\u5117\u5118\u5119\u511a\u511b\u511c\u511d\u511e\u511f\u5120\u5121\u5122\u5123\u5124\u5125\u5126\u5127\u5128\u5129\u512a\u512b\u512c\u512d\u512e\u512f\u5130\u5131\u5132\u5133\u5134\u5135\u5136\u5137\u5138\u5139\u513a\u513b\u513c\u513d\u513e\u513f\u5140\u5141\u5142\u5143\u5144\u5145\u5146\u5147\u5148\u5149\u514a\u514b\u514c\u514d\u514e\u514f\u5150\u5151\u5152\u5153\u5154\u5155\u5156\u5157\u5158\u5159\u515a\u515b\u515c\u515d\u515e\u515f\u5160\u5161\u5162\u5163\u5164\u5165\u5166\u5167\u5168\u5169\u516a\u516b\u516c\u516d\u516e\u516f\u5170\u5171\u5172\u5173\u5174\u5175\u5176\u5177\u5178\u5179\u517a\u517b\u517c\u517d\u517e\u517f\u5180\u5181\u5182\u5183\u5184\u5185\u5186\u5187\u5188\u5189\u518a\u518b\u518c\u518d\u518e\u518f\u5190\u5191\u5192\u5193\u5194\u5195\u5196\u5197\u5198\u5199\u519a\u519b\u519c\u519d\u519e\u519f\u51a0\u51a1\u51a2\u51a3\u51a4\u51a5\u51a6\u51a7\u51a8\u51a9\u51aa\u51ab\u51ac\u51ad\u51ae\u51af\u51b0\u51b1\u51b2\u51b3\u51b4\u51b5\u51b6\u51b7\u51b8\u51b9\u51ba\u51bb\u51bc\u51bd\u51be\u51bf\u51c0\u51c1\u51c2\u51c3\u51c4\u51c5\u51c6\u51c7\u51c8\u51c9\u51ca\u51cb\u51cc\u51cd\u51ce\u51cf\u51d0\u51d1\u51d2\u51d3\u51d4\u51d5\u51d6\u51d7\u51d8\u51d9\u51da\u51db\u51dc\u51dd\u51de\u51df\u51e0\u51e1\u51e2\u51e3\u51e4\u51e5\u51e6\u51e7\u51e8\u51e9\u51ea\u51eb\u51ec\u51ed\u51ee\u51ef\u51f0\u51f1\u51f2\u51f3\u51f4\u51f5\u51f6\u51f7\u51f8\u51f9\u51fa\u51fb\u51fc\u51fd\u51fe\u51ff\u5200\u5201\u5202\u5203\u5204\u5205\u5206\u5207\u5208\u5209\u520a\u520b\u520c\u520d\u520e\u520f\u5210\u5211\u5212\u5213\u5214\u5215\u5216\u5217\u5218\u5219\u521a\u521b\u521c\u521d\u521e\u521f\u5220\u5221\u5222\u5223\u5224\u5225\u5226\u5227\u5228\u5229\u522a\u522b\u522c\u522d\u522e\u522f\u5230\u5231\u5232\u5233\u5234\u5235\u5236\u5237\u5238\u5239\u523a\u523b\u523c\u523d\u523e\u523f\u5240\u5241\u5242\u5243\u5244\u5245\u5246\u5247\u5248\u5249\u524a\u524b\u524c\u524d\u524e\u524f\u5250\u5251\u5252\u5253\u5254\u5255\u5256\u5257\u5258\u5259\u525a\u525b\u525c\u525d\u525e\u525f\u5260\u5261\u5262\u5263\u5264\u5265\u5266\u5267\u5268\u5269\u526a\u526b\u526c\u526d\u526e\u526f\u5270\u5271\u5272\u5273\u5274\u5275\u5276\u5277\u5278\u5279\u527a\u527b\u527c\u527d\u527e\u527f\u5280\u5281\u5282\u5283\u5284\u5285\u5286\u5287\u5288\u5289\u528a\u528b\u528c\u528d\u528e\u528f\u5290\u5291\u5292\u5293\u5294\u5295\u5296\u5297\u5298\u5299\u529a\u529b\u529c\u529d\u529e\u529f\u52a0\u52a1\u52a2\u52a3\u52a4\u52a5\u52a6\u52a7\u52a8\u52a9\u52aa\u52ab\u52ac\u52ad\u52ae\u52af\u52b0\u52b1\u52b2\u52b3\u52b4\u52b5\u52b6\u52b7\u52b8\u52b9\u52ba\u52bb\u52bc\u52bd\u52be\u52bf\u52c0\u52c1\u52c2\u52c3\u52c4\u52c5\u52c6\u52c7\u52c8\u52c9\u52ca\u52cb\u52cc\u52cd\u52ce\u52cf\u52d0\u52d1\u52d2\u52d3\u52d4\u52d5\u52d6\u52d7\u52d8\u52d9\u52da\u52db\u52dc\u52dd\u52de\u52df\u52e0\u52e1\u52e2\u52e3\u52e4\u52e5\u52e6\u52e7\u52e8\u52e9\u52ea\u52eb\u52ec\u52ed\u52ee\u52ef\u52f0\u52f1\u52f2\u52f3\u52f4\u52f5\u52f6\u52f7\u52f8\u52f9\u52fa\u52fb\u52fc\u52fd\u52fe\u52ff\u5300\u5301\u5302\u5303\u5304\u5305\u5306\u5307\u5308\u5309\u530a\u530b\u530c\u530d\u530e\u530f\u5310\u5311\u5312\u5313\u5314\u5315\u5316\u5317\u5318\u5319\u531a\u531b\u531c\u531d\u531e\u531f\u5320\u5321\u5322\u5323\u5324\u5325\u5326\u5327\u5328\u5329\u532a\u532b\u532c\u532d\u532e\u532f\u5330\u5331\u5332\u5333\u5334\u5335\u5336\u5337\u5338\u5339\u533a\u533b\u533c\u533d\u533e\u533f\u5340\u5341\u5342\u5343\u5344\u5345\u5346\u5347\u5348\u5349\u534a\u534b\u534c\u534d\u534e\u534f\u5350\u5351\u5352\u5353\u5354\u5355\u5356\u5357\u5358\u5359\u535a\u535b\u535c\u535d\u535e\u535f\u5360\u5361\u5362\u5363\u5364\u5365\u5366\u5367\u5368\u5369\u536a\u536b\u536c\u536d\u536e\u536f\u5370\u5371\u5372\u5373\u5374\u5375\u5376\u5377\u5378\u5379\u537a\u537b\u537c\u537d\u537e\u537f\u5380\u5381\u5382\u5383\u5384\u5385\u5386\u5387\u5388\u5389\u538a\u538b\u538c\u538d\u538e\u538f\u5390\u5391\u5392\u5393\u5394\u5395\u5396\u5397\u5398\u5399\u539a\u539b\u539c\u539d\u539e\u539f\u53a0\u53a1\u53a2\u53a3\u53a4\u53a5\u53a6\u53a7\u53a8\u53a9\u53aa\u53ab\u53ac\u53ad\u53ae\u53af\u53b0\u53b1\u53b2\u53b3\u53b4\u53b5\u53b6\u53b7\u53b8\u53b9\u53ba\u53bb\u53bc\u53bd\u53be\u53bf\u53c0\u53c1\u53c2\u53c3\u53c4\u53c5\u53c6\u53c7\u53c8\u53c9\u53ca\u53cb\u53cc\u53cd\u53ce\u53cf\u53d0\u53d1\u53d2\u53d3\u53d4\u53d5\u53d6\u53d7\u53d8\u53d9\u53da\u53db\u53dc\u53dd\u53de\u53df\u53e0\u53e1\u53e2\u53e3\u53e4\u53e5\u53e6\u53e7\u53e8\u53e9\u53ea\u53eb\u53ec\u53ed\u53ee\u53ef\u53f0\u53f1\u53f2\u53f3\u53f4\u53f5\u53f6\u53f7\u53f8\u53f9\u53fa\u53fb\u53fc\u53fd\u53fe\u53ff\u5400\u5401\u5402\u5403\u5404\u5405\u5406\u5407\u5408\u5409\u540a\u540b\u540c\u540d\u540e\u540f\u5410\u5411\u5412\u5413\u5414\u5415\u5416\u5417\u5418\u5419\u541a\u541b\u541c\u541d\u541e\u541f\u5420\u5421\u5422\u5423\u5424\u5425\u5426\u5427\u5428\u5429\u542a\u542b\u542c\u542d\u542e\u542f\u5430\u5431\u5432\u5433\u5434\u5435\u5436\u5437\u5438\u5439\u543a\u543b\u543c\u543d\u543e\u543f\u5440\u5441\u5442\u5443\u5444\u5445\u5446\u5447\u5448\u5449\u544a\u544b\u544c\u544d\u544e\u544f\u5450\u5451\u5452\u5453\u5454\u5455\u5456\u5457\u5458\u5459\u545a\u545b\u545c\u545d\u545e\u545f\u5460\u5461\u5462\u5463\u5464\u5465\u5466\u5467\u5468\u5469\u546a\u546b\u546c\u546d\u546e\u546f\u5470\u5471\u5472\u5473\u5474\u5475\u5476\u5477\u5478\u5479\u547a\u547b\u547c\u547d\u547e\u547f\u5480\u5481\u5482\u5483\u5484\u5485\u5486\u5487\u5488\u5489\u548a\u548b\u548c\u548d\u548e\u548f\u5490\u5491\u5492\u5493\u5494\u5495\u5496\u5497\u5498\u5499\u549a\u549b\u549c\u549d\u549e\u549f\u54a0\u54a1\u54a2\u54a3\u54a4\u54a5\u54a6\u54a7\u54a8\u54a9\u54aa\u54ab\u54ac\u54ad\u54ae\u54af\u54b0\u54b1\u54b2\u54b3\u54b4\u54b5\u54b6\u54b7\u54b8\u54b9\u54ba\u54bb\u54bc\u54bd\u54be\u54bf\u54c0\u54c1\u54c2\u54c3\u54c4\u54c5\u54c6\u54c7\u54c8\u54c9\u54ca\u54cb\u54cc\u54cd\u54ce\u54cf\u54d0\u54d1\u54d2\u54d3\u54d4\u54d5\u54d6\u54d7\u54d8\u54d9\u54da\u54db\u54dc\u54dd\u54de\u54df\u54e0\u54e1\u54e2\u54e3\u54e4\u54e5\u54e6\u54e7\u54e8\u54e9\u54ea\u54eb\u54ec\u54ed\u54ee\u54ef\u54f0\u54f1\u54f2\u54f3\u54f4\u54f5\u54f6\u54f7\u54f8\u54f9\u54fa\u54fb\u54fc\u54fd\u54fe\u54ff\u5500\u5501\u5502\u5503\u5504\u5505\u5506\u5507\u5508\u5509\u550a\u550b\u550c\u550d\u550e\u550f\u5510\u5511\u5512\u5513\u5514\u5515\u5516\u5517\u5518\u5519\u551a\u551b\u551c\u551d\u551e\u551f\u5520\u5521\u5522\u5523\u5524\u5525\u5526\u5527\u5528\u5529\u552a\u552b\u552c\u552d\u552e\u552f\u5530\u5531\u5532\u5533\u5534\u5535\u5536\u5537\u5538\u5539\u553a\u553b\u553c\u553d\u553e\u553f\u5540\u5541\u5542\u5543\u5544\u5545\u5546\u5547\u5548\u5549\u554a\u554b\u554c\u554d\u554e\u554f\u5550\u5551\u5552\u5553\u5554\u5555\u5556\u5557\u5558\u5559\u555a\u555b\u555c\u555d\u555e\u555f\u5560\u5561\u5562\u5563\u5564\u5565\u5566\u5567\u5568\u5569\u556a\u556b\u556c\u556d\u556e\u556f\u5570\u5571\u5572\u5573\u5574\u5575\u5576\u5577\u5578\u5579\u557a\u557b\u557c\u557d\u557e\u557f\u5580\u5581\u5582\u5583\u5584\u5585\u5586\u5587\u5588\u5589\u558a\u558b\u558c\u558d\u558e\u558f\u5590\u5591\u5592\u5593\u5594\u5595\u5596\u5597\u5598\u5599\u559a\u559b\u559c\u559d\u559e\u559f\u55a0\u55a1\u55a2\u55a3\u55a4\u55a5\u55a6\u55a7\u55a8\u55a9\u55aa\u55ab\u55ac\u55ad\u55ae\u55af\u55b0\u55b1\u55b2\u55b3\u55b4\u55b5\u55b6\u55b7\u55b8\u55b9\u55ba\u55bb\u55bc\u55bd\u55be\u55bf\u55c0\u55c1\u55c2\u55c3\u55c4\u55c5\u55c6\u55c7\u55c8\u55c9\u55ca\u55cb\u55cc\u55cd\u55ce\u55cf\u55d0\u55d1\u55d2\u55d3\u55d4\u55d5\u55d6\u55d7\u55d8\u55d9\u55da\u55db\u55dc\u55dd\u55de\u55df\u55e0\u55e1\u55e2\u55e3\u55e4\u55e5\u55e6\u55e7\u55e8\u55e9\u55ea\u55eb\u55ec\u55ed\u55ee\u55ef\u55f0\u55f1\u55f2\u55f3\u55f4\u55f5\u55f6\u55f7\u55f8\u55f9\u55fa\u55fb\u55fc\u55fd\u55fe\u55ff\u5600\u5601\u5602\u5603\u5604\u5605\u5606\u5607\u5608\u5609\u560a\u560b\u560c\u560d\u560e\u560f\u5610\u5611\u5612\u5613\u5614\u5615\u5616\u5617\u5618\u5619\u561a\u561b\u561c\u561d\u561e\u561f\u5620\u5621\u5622\u5623\u5624\u5625\u5626\u5627\u5628\u5629\u562a\u562b\u562c\u562d\u562e\u562f\u5630\u5631\u5632\u5633\u5634\u5635\u5636\u5637\u5638\u5639\u563a\u563b\u563c\u563d\u563e\u563f\u5640\u5641\u5642\u5643\u5644\u5645\u5646\u5647\u5648\u5649\u564a\u564b\u564c\u564d\u564e\u564f\u5650\u5651\u5652\u5653\u5654\u5655\u5656\u5657\u5658\u5659\u565a\u565b\u565c\u565d\u565e\u565f\u5660\u5661\u5662\u5663\u5664\u5665\u5666\u5667\u5668\u5669\u566a\u566b\u566c\u566d\u566e\u566f\u5670\u5671\u5672\u5673\u5674\u5675\u5676\u5677\u5678\u5679\u567a\u567b\u567c\u567d\u567e\u567f\u5680\u5681\u5682\u5683\u5684\u5685\u5686\u5687\u5688\u5689\u568a\u568b\u568c\u568d\u568e\u568f\u5690\u5691\u5692\u5693\u5694\u5695\u5696\u5697\u5698\u5699\u569a\u569b\u569c\u569d\u569e\u569f\u56a0\u56a1\u56a2\u56a3\u56a4\u56a5\u56a6\u56a7\u56a8\u56a9\u56aa\u56ab\u56ac\u56ad\u56ae\u56af\u56b0\u56b1\u56b2\u56b3\u56b4\u56b5\u56b6\u56b7\u56b8\u56b9\u56ba\u56bb\u56bc\u56bd\u56be\u56bf\u56c0\u56c1\u56c2\u56c3\u56c4\u56c5\u56c6\u56c7\u56c8\u56c9\u56ca\u56cb\u56cc\u56cd\u56ce\u56cf\u56d0\u56d1\u56d2\u56d3\u56d4\u56d5\u56d6\u56d7\u56d8\u56d9\u56da\u56db\u56dc\u56dd\u56de\u56df\u56e0\u56e1\u56e2\u56e3\u56e4\u56e5\u56e6\u56e7\u56e8\u56e9\u56ea\u56eb\u56ec\u56ed\u56ee\u56ef\u56f0\u56f1\u56f2\u56f3\u56f4\u56f5\u56f6\u56f7\u56f8\u56f9\u56fa\u56fb\u56fc\u56fd\u56fe\u56ff\u5700\u5701\u5702\u5703\u5704\u5705\u5706\u5707\u5708\u5709\u570a\u570b\u570c\u570d\u570e\u570f\u5710\u5711\u5712\u5713\u5714\u5715\u5716\u5717\u5718\u5719\u571a\u571b\u571c\u571d\u571e\u571f\u5720\u5721\u5722\u5723\u5724\u5725\u5726\u5727\u5728\u5729\u572a\u572b\u572c\u572d\u572e\u572f\u5730\u5731\u5732\u5733\u5734\u5735\u5736\u5737\u5738\u5739\u573a\u573b\u573c\u573d\u573e\u573f\u5740\u5741\u5742\u5743\u5744\u5745\u5746\u5747\u5748\u5749\u574a\u574b\u574c\u574d\u574e\u574f\u5750\u5751\u5752\u5753\u5754\u5755\u5756\u5757\u5758\u5759\u575a\u575b\u575c\u575d\u575e\u575f\u5760\u5761\u5762\u5763\u5764\u5765\u5766\u5767\u5768\u5769\u576a\u576b\u576c\u576d\u576e\u576f\u5770\u5771\u5772\u5773\u5774\u5775\u5776\u5777\u5778\u5779\u577a\u577b\u577c\u577d\u577e\u577f\u5780\u5781\u5782\u5783\u5784\u5785\u5786\u5787\u5788\u5789\u578a\u578b\u578c\u578d\u578e\u578f\u5790\u5791\u5792\u5793\u5794\u5795\u5796\u5797\u5798\u5799\u579a\u579b\u579c\u579d\u579e\u579f\u57a0\u57a1\u57a2\u57a3\u57a4\u57a5\u57a6\u57a7\u57a8\u57a9\u57aa\u57ab\u57ac\u57ad\u57ae\u57af\u57b0\u57b1\u57b2\u57b3\u57b4\u57b5\u57b6\u57b7\u57b8\u57b9\u57ba\u57bb\u57bc\u57bd\u57be\u57bf\u57c0\u57c1\u57c2\u57c3\u57c4\u57c5\u57c6\u57c7\u57c8\u57c9\u57ca\u57cb\u57cc\u57cd\u57ce\u57cf\u57d0\u57d1\u57d2\u57d3\u57d4\u57d5\u57d6\u57d7\u57d8\u57d9\u57da\u57db\u57dc\u57dd\u57de\u57df\u57e0\u57e1\u57e2\u57e3\u57e4\u57e5\u57e6\u57e7\u57e8\u57e9\u57ea\u57eb\u57ec\u57ed\u57ee\u57ef\u57f0\u57f1\u57f2\u57f3\u57f4\u57f5\u57f6\u57f7\u57f8\u57f9\u57fa\u57fb\u57fc\u57fd\u57fe\u57ff\u5800\u5801\u5802\u5803\u5804\u5805\u5806\u5807\u5808\u5809\u580a\u580b\u580c\u580d\u580e\u580f\u5810\u5811\u5812\u5813\u5814\u5815\u5816\u5817\u5818\u5819\u581a\u581b\u581c\u581d\u581e\u581f\u5820\u5821\u5822\u5823\u5824\u5825\u5826\u5827\u5828\u5829\u582a\u582b\u582c\u582d\u582e\u582f\u5830\u5831\u5832\u5833\u5834\u5835\u5836\u5837\u5838\u5839\u583a\u583b\u583c\u583d\u583e\u583f\u5840\u5841\u5842\u5843\u5844\u5845\u5846\u5847\u5848\u5849\u584a\u584b\u584c\u584d\u584e\u584f\u5850\u5851\u5852\u5853\u5854\u5855\u5856\u5857\u5858\u5859\u585a\u585b\u585c\u585d\u585e\u585f\u5860\u5861\u5862\u5863\u5864\u5865\u5866\u5867\u5868\u5869\u586a\u586b\u586c\u586d\u586e\u586f\u5870\u5871\u5872\u5873\u5874\u5875\u5876\u5877\u5878\u5879\u587a\u587b\u587c\u587d\u587e\u587f\u5880\u5881\u5882\u5883\u5884\u5885\u5886\u5887\u5888\u5889\u588a\u588b\u588c\u588d\u588e\u588f\u5890\u5891\u5892\u5893\u5894\u5895\u5896\u5897\u5898\u5899\u589a\u589b\u589c\u589d\u589e\u589f\u58a0\u58a1\u58a2\u58a3\u58a4\u58a5\u58a6\u58a7\u58a8\u58a9\u58aa\u58ab\u58ac\u58ad\u58ae\u58af\u58b0\u58b1\u58b2\u58b3\u58b4\u58b5\u58b6\u58b7\u58b8\u58b9\u58ba\u58bb\u58bc\u58bd\u58be\u58bf\u58c0\u58c1\u58c2\u58c3\u58c4\u58c5\u58c6\u58c7\u58c8\u58c9\u58ca\u58cb\u58cc\u58cd\u58ce\u58cf\u58d0\u58d1\u58d2\u58d3\u58d4\u58d5\u58d6\u58d7\u58d8\u58d9\u58da\u58db\u58dc\u58dd\u58de\u58df\u58e0\u58e1\u58e2\u58e3\u58e4\u58e5\u58e6\u58e7\u58e8\u58e9\u58ea\u58eb\u58ec\u58ed\u58ee\u58ef\u58f0\u58f1\u58f2\u58f3\u58f4\u58f5\u58f6\u58f7\u58f8\u58f9\u58fa\u58fb\u58fc\u58fd\u58fe\u58ff\u5900\u5901\u5902\u5903\u5904\u5905\u5906\u5907\u5908\u5909\u590a\u590b\u590c\u590d\u590e\u590f\u5910\u5911\u5912\u5913\u5914\u5915\u5916\u5917\u5918\u5919\u591a\u591b\u591c\u591d\u591e\u591f\u5920\u5921\u5922\u5923\u5924\u5925\u5926\u5927\u5928\u5929\u592a\u592b\u592c\u592d\u592e\u592f\u5930\u5931\u5932\u5933\u5934\u5935\u5936\u5937\u5938\u5939\u593a\u593b\u593c\u593d\u593e\u593f\u5940\u5941\u5942\u5943\u5944\u5945\u5946\u5947\u5948\u5949\u594a\u594b\u594c\u594d\u594e\u594f\u5950\u5951\u5952\u5953\u5954\u5955\u5956\u5957\u5958\u5959\u595a\u595b\u595c\u595d\u595e\u595f\u5960\u5961\u5962\u5963\u5964\u5965\u5966\u5967\u5968\u5969\u596a\u596b\u596c\u596d\u596e\u596f\u5970\u5971\u5972\u5973\u5974\u5975\u5976\u5977\u5978\u5979\u597a\u597b\u597c\u597d\u597e\u597f\u5980\u5981\u5982\u5983\u5984\u5985\u5986\u5987\u5988\u5989\u598a\u598b\u598c\u598d\u598e\u598f\u5990\u5991\u5992\u5993\u5994\u5995\u5996\u5997\u5998\u5999\u599a\u599b\u599c\u599d\u599e\u599f\u59a0\u59a1\u59a2\u59a3\u59a4\u59a5\u59a6\u59a7\u59a8\u59a9\u59aa\u59ab\u59ac\u59ad\u59ae\u59af\u59b0\u59b1\u59b2\u59b3\u59b4\u59b5\u59b6\u59b7\u59b8\u59b9\u59ba\u59bb\u59bc\u59bd\u59be\u59bf\u59c0\u59c1\u59c2\u59c3\u59c4\u59c5\u59c6\u59c7\u59c8\u59c9\u59ca\u59cb\u59cc\u59cd\u59ce\u59cf\u59d0\u59d1\u59d2\u59d3\u59d4\u59d5\u59d6\u59d7\u59d8\u59d9\u59da\u59db\u59dc\u59dd\u59de\u59df\u59e0\u59e1\u59e2\u59e3\u59e4\u59e5\u59e6\u59e7\u59e8\u59e9\u59ea\u59eb\u59ec\u59ed\u59ee\u59ef\u59f0\u59f1\u59f2\u59f3\u59f4\u59f5\u59f6\u59f7\u59f8\u59f9\u59fa\u59fb\u59fc\u59fd\u59fe\u59ff\u5a00\u5a01\u5a02\u5a03\u5a04\u5a05\u5a06\u5a07\u5a08\u5a09\u5a0a\u5a0b\u5a0c\u5a0d\u5a0e\u5a0f\u5a10\u5a11\u5a12\u5a13\u5a14\u5a15\u5a16\u5a17\u5a18\u5a19\u5a1a\u5a1b\u5a1c\u5a1d\u5a1e\u5a1f\u5a20\u5a21\u5a22\u5a23\u5a24\u5a25\u5a26\u5a27\u5a28\u5a29\u5a2a\u5a2b\u5a2c\u5a2d\u5a2e\u5a2f\u5a30\u5a31\u5a32\u5a33\u5a34\u5a35\u5a36\u5a37\u5a38\u5a39\u5a3a\u5a3b\u5a3c\u5a3d\u5a3e\u5a3f\u5a40\u5a41\u5a42\u5a43\u5a44\u5a45\u5a46\u5a47\u5a48\u5a49\u5a4a\u5a4b\u5a4c\u5a4d\u5a4e\u5a4f\u5a50\u5a51\u5a52\u5a53\u5a54\u5a55\u5a56\u5a57\u5a58\u5a59\u5a5a\u5a5b\u5a5c\u5a5d\u5a5e\u5a5f\u5a60\u5a61\u5a62\u5a63\u5a64\u5a65\u5a66\u5a67\u5a68\u5a69\u5a6a\u5a6b\u5a6c\u5a6d\u5a6e\u5a6f\u5a70\u5a71\u5a72\u5a73\u5a74\u5a75\u5a76\u5a77\u5a78\u5a79\u5a7a\u5a7b\u5a7c\u5a7d\u5a7e\u5a7f\u5a80\u5a81\u5a82\u5a83\u5a84\u5a85\u5a86\u5a87\u5a88\u5a89\u5a8a\u5a8b\u5a8c\u5a8d\u5a8e\u5a8f\u5a90\u5a91\u5a92\u5a93\u5a94\u5a95\u5a96\u5a97\u5a98\u5a99\u5a9a\u5a9b\u5a9c\u5a9d\u5a9e\u5a9f\u5aa0\u5aa1\u5aa2\u5aa3\u5aa4\u5aa5\u5aa6\u5aa7\u5aa8\u5aa9\u5aaa\u5aab\u5aac\u5aad\u5aae\u5aaf\u5ab0\u5ab1\u5ab2\u5ab3\u5ab4\u5ab5\u5ab6\u5ab7\u5ab8\u5ab9\u5aba\u5abb\u5abc\u5abd\u5abe\u5abf\u5ac0\u5ac1\u5ac2\u5ac3\u5ac4\u5ac5\u5ac6\u5ac7\u5ac8\u5ac9\u5aca\u5acb\u5acc\u5acd\u5ace\u5acf\u5ad0\u5ad1\u5ad2\u5ad3\u5ad4\u5ad5\u5ad6\u5ad7\u5ad8\u5ad9\u5ada\u5adb\u5adc\u5add\u5ade\u5adf\u5ae0\u5ae1\u5ae2\u5ae3\u5ae4\u5ae5\u5ae6\u5ae7\u5ae8\u5ae9\u5aea\u5aeb\u5aec\u5aed\u5aee\u5aef\u5af0\u5af1\u5af2\u5af3\u5af4\u5af5\u5af6\u5af7\u5af8\u5af9\u5afa\u5afb\u5afc\u5afd\u5afe\u5aff\u5b00\u5b01\u5b02\u5b03\u5b04\u5b05\u5b06\u5b07\u5b08\u5b09\u5b0a\u5b0b\u5b0c\u5b0d\u5b0e\u5b0f\u5b10\u5b11\u5b12\u5b13\u5b14\u5b15\u5b16\u5b17\u5b18\u5b19\u5b1a\u5b1b\u5b1c\u5b1d\u5b1e\u5b1f\u5b20\u5b21\u5b22\u5b23\u5b24\u5b25\u5b26\u5b27\u5b28\u5b29\u5b2a\u5b2b\u5b2c\u5b2d\u5b2e\u5b2f\u5b30\u5b31\u5b32\u5b33\u5b34\u5b35\u5b36\u5b37\u5b38\u5b39\u5b3a\u5b3b\u5b3c\u5b3d\u5b3e\u5b3f\u5b40\u5b41\u5b42\u5b43\u5b44\u5b45\u5b46\u5b47\u5b48\u5b49\u5b4a\u5b4b\u5b4c\u5b4d\u5b4e\u5b4f\u5b50\u5b51\u5b52\u5b53\u5b54\u5b55\u5b56\u5b57\u5b58\u5b59\u5b5a\u5b5b\u5b5c\u5b5d\u5b5e\u5b5f\u5b60\u5b61\u5b62\u5b63\u5b64\u5b65\u5b66\u5b67\u5b68\u5b69\u5b6a\u5b6b\u5b6c\u5b6d\u5b6e\u5b6f\u5b70\u5b71\u5b72\u5b73\u5b74\u5b75\u5b76\u5b77\u5b78\u5b79\u5b7a\u5b7b\u5b7c\u5b7d\u5b7e\u5b7f\u5b80\u5b81\u5b82\u5b83\u5b84\u5b85\u5b86\u5b87\u5b88\u5b89\u5b8a\u5b8b\u5b8c\u5b8d\u5b8e\u5b8f\u5b90\u5b91\u5b92\u5b93\u5b94\u5b95\u5b96\u5b97\u5b98\u5b99\u5b9a\u5b9b\u5b9c\u5b9d\u5b9e\u5b9f\u5ba0\u5ba1\u5ba2\u5ba3\u5ba4\u5ba5\u5ba6\u5ba7\u5ba8\u5ba9\u5baa\u5bab\u5bac\u5bad\u5bae\u5baf\u5bb0\u5bb1\u5bb2\u5bb3\u5bb4\u5bb5\u5bb6\u5bb7\u5bb8\u5bb9\u5bba\u5bbb\u5bbc\u5bbd\u5bbe\u5bbf\u5bc0\u5bc1\u5bc2\u5bc3\u5bc4\u5bc5\u5bc6\u5bc7\u5bc8\u5bc9\u5bca\u5bcb\u5bcc\u5bcd\u5bce\u5bcf\u5bd0\u5bd1\u5bd2\u5bd3\u5bd4\u5bd5\u5bd6\u5bd7\u5bd8\u5bd9\u5bda\u5bdb\u5bdc\u5bdd\u5bde\u5bdf\u5be0\u5be1\u5be2\u5be3\u5be4\u5be5\u5be6\u5be7\u5be8\u5be9\u5bea\u5beb\u5bec\u5bed\u5bee\u5bef\u5bf0\u5bf1\u5bf2\u5bf3\u5bf4\u5bf5\u5bf6\u5bf7\u5bf8\u5bf9\u5bfa\u5bfb\u5bfc\u5bfd\u5bfe\u5bff\u5c00\u5c01\u5c02\u5c03\u5c04\u5c05\u5c06\u5c07\u5c08\u5c09\u5c0a\u5c0b\u5c0c\u5c0d\u5c0e\u5c0f\u5c10\u5c11\u5c12\u5c13\u5c14\u5c15\u5c16\u5c17\u5c18\u5c19\u5c1a\u5c1b\u5c1c\u5c1d\u5c1e\u5c1f\u5c20\u5c21\u5c22\u5c23\u5c24\u5c25\u5c26\u5c27\u5c28\u5c29\u5c2a\u5c2b\u5c2c\u5c2d\u5c2e\u5c2f\u5c30\u5c31\u5c32\u5c33\u5c34\u5c35\u5c36\u5c37\u5c38\u5c39\u5c3a\u5c3b\u5c3c\u5c3d\u5c3e\u5c3f\u5c40\u5c41\u5c42\u5c43\u5c44\u5c45\u5c46\u5c47\u5c48\u5c49\u5c4a\u5c4b\u5c4c\u5c4d\u5c4e\u5c4f\u5c50\u5c51\u5c52\u5c53\u5c54\u5c55\u5c56\u5c57\u5c58\u5c59\u5c5a\u5c5b\u5c5c\u5c5d\u5c5e\u5c5f\u5c60\u5c61\u5c62\u5c63\u5c64\u5c65\u5c66\u5c67\u5c68\u5c69\u5c6a\u5c6b\u5c6c\u5c6d\u5c6e\u5c6f\u5c70\u5c71\u5c72\u5c73\u5c74\u5c75\u5c76\u5c77\u5c78\u5c79\u5c7a\u5c7b\u5c7c\u5c7d\u5c7e\u5c7f\u5c80\u5c81\u5c82\u5c83\u5c84\u5c85\u5c86\u5c87\u5c88\u5c89\u5c8a\u5c8b\u5c8c\u5c8d\u5c8e\u5c8f\u5c90\u5c91\u5c92\u5c93\u5c94\u5c95\u5c96\u5c97\u5c98\u5c99\u5c9a\u5c9b\u5c9c\u5c9d\u5c9e\u5c9f\u5ca0\u5ca1\u5ca2\u5ca3\u5ca4\u5ca5\u5ca6\u5ca7\u5ca8\u5ca9\u5caa\u5cab\u5cac\u5cad\u5cae\u5caf\u5cb0\u5cb1\u5cb2\u5cb3\u5cb4\u5cb5\u5cb6\u5cb7\u5cb8\u5cb9\u5cba\u5cbb\u5cbc\u5cbd\u5cbe\u5cbf\u5cc0\u5cc1\u5cc2\u5cc3\u5cc4\u5cc5\u5cc6\u5cc7\u5cc8\u5cc9\u5cca\u5ccb\u5ccc\u5ccd\u5cce\u5ccf\u5cd0\u5cd1\u5cd2\u5cd3\u5cd4\u5cd5\u5cd6\u5cd7\u5cd8\u5cd9\u5cda\u5cdb\u5cdc\u5cdd\u5cde\u5cdf\u5ce0\u5ce1\u5ce2\u5ce3\u5ce4\u5ce5\u5ce6\u5ce7\u5ce8\u5ce9\u5cea\u5ceb\u5cec\u5ced\u5cee\u5cef\u5cf0\u5cf1\u5cf2\u5cf3\u5cf4\u5cf5\u5cf6\u5cf7\u5cf8\u5cf9\u5cfa\u5cfb\u5cfc\u5cfd\u5cfe\u5cff\u5d00\u5d01\u5d02\u5d03\u5d04\u5d05\u5d06\u5d07\u5d08\u5d09\u5d0a\u5d0b\u5d0c\u5d0d\u5d0e\u5d0f\u5d10\u5d11\u5d12\u5d13\u5d14\u5d15\u5d16\u5d17\u5d18\u5d19\u5d1a\u5d1b\u5d1c\u5d1d\u5d1e\u5d1f\u5d20\u5d21\u5d22\u5d23\u5d24\u5d25\u5d26\u5d27\u5d28\u5d29\u5d2a\u5d2b\u5d2c\u5d2d\u5d2e\u5d2f\u5d30\u5d31\u5d32\u5d33\u5d34\u5d35\u5d36\u5d37\u5d38\u5d39\u5d3a\u5d3b\u5d3c\u5d3d\u5d3e\u5d3f\u5d40\u5d41\u5d42\u5d43\u5d44\u5d45\u5d46\u5d47\u5d48\u5d49\u5d4a\u5d4b\u5d4c\u5d4d\u5d4e\u5d4f\u5d50\u5d51\u5d52\u5d53\u5d54\u5d55\u5d56\u5d57\u5d58\u5d59\u5d5a\u5d5b\u5d5c\u5d5d\u5d5e\u5d5f\u5d60\u5d61\u5d62\u5d63\u5d64\u5d65\u5d66\u5d67\u5d68\u5d69\u5d6a\u5d6b\u5d6c\u5d6d\u5d6e\u5d6f\u5d70\u5d71\u5d72\u5d73\u5d74\u5d75\u5d76\u5d77\u5d78\u5d79\u5d7a\u5d7b\u5d7c\u5d7d\u5d7e\u5d7f\u5d80\u5d81\u5d82\u5d83\u5d84\u5d85\u5d86\u5d87\u5d88\u5d89\u5d8a\u5d8b\u5d8c\u5d8d\u5d8e\u5d8f\u5d90\u5d91\u5d92\u5d93\u5d94\u5d95\u5d96\u5d97\u5d98\u5d99\u5d9a\u5d9b\u5d9c\u5d9d\u5d9e\u5d9f\u5da0\u5da1\u5da2\u5da3\u5da4\u5da5\u5da6\u5da7\u5da8\u5da9\u5daa\u5dab\u5dac\u5dad\u5dae\u5daf\u5db0\u5db1\u5db2\u5db3\u5db4\u5db5\u5db6\u5db7\u5db8\u5db9\u5dba\u5dbb\u5dbc\u5dbd\u5dbe\u5dbf\u5dc0\u5dc1\u5dc2\u5dc3\u5dc4\u5dc5\u5dc6\u5dc7\u5dc8\u5dc9\u5dca\u5dcb\u5dcc\u5dcd\u5dce\u5dcf\u5dd0\u5dd1\u5dd2\u5dd3\u5dd4\u5dd5\u5dd6\u5dd7\u5dd8\u5dd9\u5dda\u5ddb\u5ddc\u5ddd\u5dde\u5ddf\u5de0\u5de1\u5de2\u5de3\u5de4\u5de5\u5de6\u5de7\u5de8\u5de9\u5dea\u5deb\u5dec\u5ded\u5dee\u5def\u5df0\u5df1\u5df2\u5df3\u5df4\u5df5\u5df6\u5df7\u5df8\u5df9\u5dfa\u5dfb\u5dfc\u5dfd\u5dfe\u5dff\u5e00\u5e01\u5e02\u5e03\u5e04\u5e05\u5e06\u5e07\u5e08\u5e09\u5e0a\u5e0b\u5e0c\u5e0d\u5e0e\u5e0f\u5e10\u5e11\u5e12\u5e13\u5e14\u5e15\u5e16\u5e17\u5e18\u5e19\u5e1a\u5e1b\u5e1c\u5e1d\u5e1e\u5e1f\u5e20\u5e21\u5e22\u5e23\u5e24\u5e25\u5e26\u5e27\u5e28\u5e29\u5e2a\u5e2b\u5e2c\u5e2d\u5e2e\u5e2f\u5e30\u5e31\u5e32\u5e33\u5e34\u5e35\u5e36\u5e37\u5e38\u5e39\u5e3a\u5e3b\u5e3c\u5e3d\u5e3e\u5e3f\u5e40\u5e41\u5e42\u5e43\u5e44\u5e45\u5e46\u5e47\u5e48\u5e49\u5e4a\u5e4b\u5e4c\u5e4d\u5e4e\u5e4f\u5e50\u5e51\u5e52\u5e53\u5e54\u5e55\u5e56\u5e57\u5e58\u5e59\u5e5a\u5e5b\u5e5c\u5e5d\u5e5e\u5e5f\u5e60\u5e61\u5e62\u5e63\u5e64\u5e65\u5e66\u5e67\u5e68\u5e69\u5e6a\u5e6b\u5e6c\u5e6d\u5e6e\u5e6f\u5e70\u5e71\u5e72\u5e73\u5e74\u5e75\u5e76\u5e77\u5e78\u5e79\u5e7a\u5e7b\u5e7c\u5e7d\u5e7e\u5e7f\u5e80\u5e81\u5e82\u5e83\u5e84\u5e85\u5e86\u5e87\u5e88\u5e89\u5e8a\u5e8b\u5e8c\u5e8d\u5e8e\u5e8f\u5e90\u5e91\u5e92\u5e93\u5e94\u5e95\u5e96\u5e97\u5e98\u5e99\u5e9a\u5e9b\u5e9c\u5e9d\u5e9e\u5e9f\u5ea0\u5ea1\u5ea2\u5ea3\u5ea4\u5ea5\u5ea6\u5ea7\u5ea8\u5ea9\u5eaa\u5eab\u5eac\u5ead\u5eae\u5eaf\u5eb0\u5eb1\u5eb2\u5eb3\u5eb4\u5eb5\u5eb6\u5eb7\u5eb8\u5eb9\u5eba\u5ebb\u5ebc\u5ebd\u5ebe\u5ebf\u5ec0\u5ec1\u5ec2\u5ec3\u5ec4\u5ec5\u5ec6\u5ec7\u5ec8\u5ec9\u5eca\u5ecb\u5ecc\u5ecd\u5ece\u5ecf\u5ed0\u5ed1\u5ed2\u5ed3\u5ed4\u5ed5\u5ed6\u5ed7\u5ed8\u5ed9\u5eda\u5edb\u5edc\u5edd\u5ede\u5edf\u5ee0\u5ee1\u5ee2\u5ee3\u5ee4\u5ee5\u5ee6\u5ee7\u5ee8\u5ee9\u5eea\u5eeb\u5eec\u5eed\u5eee\u5eef\u5ef0\u5ef1\u5ef2\u5ef3\u5ef4\u5ef5\u5ef6\u5ef7\u5ef8\u5ef9\u5efa\u5efb\u5efc\u5efd\u5efe\u5eff\u5f00\u5f01\u5f02\u5f03\u5f04\u5f05\u5f06\u5f07\u5f08\u5f09\u5f0a\u5f0b\u5f0c\u5f0d\u5f0e\u5f0f\u5f10\u5f11\u5f12\u5f13\u5f14\u5f15\u5f16\u5f17\u5f18\u5f19\u5f1a\u5f1b\u5f1c\u5f1d\u5f1e\u5f1f\u5f20\u5f21\u5f22\u5f23\u5f24\u5f25\u5f26\u5f27\u5f28\u5f29\u5f2a\u5f2b\u5f2c\u5f2d\u5f2e\u5f2f\u5f30\u5f31\u5f32\u5f33\u5f34\u5f35\u5f36\u5f37\u5f38\u5f39\u5f3a\u5f3b\u5f3c\u5f3d\u5f3e\u5f3f\u5f40\u5f41\u5f42\u5f43\u5f44\u5f45\u5f46\u5f47\u5f48\u5f49\u5f4a\u5f4b\u5f4c\u5f4d\u5f4e\u5f4f\u5f50\u5f51\u5f52\u5f53\u5f54\u5f55\u5f56\u5f57\u5f58\u5f59\u5f5a\u5f5b\u5f5c\u5f5d\u5f5e\u5f5f\u5f60\u5f61\u5f62\u5f63\u5f64\u5f65\u5f66\u5f67\u5f68\u5f69\u5f6a\u5f6b\u5f6c\u5f6d\u5f6e\u5f6f\u5f70\u5f71\u5f72\u5f73\u5f74\u5f75\u5f76\u5f77\u5f78\u5f79\u5f7a\u5f7b\u5f7c\u5f7d\u5f7e\u5f7f\u5f80\u5f81\u5f82\u5f83\u5f84\u5f85\u5f86\u5f87\u5f88\u5f89\u5f8a\u5f8b\u5f8c\u5f8d\u5f8e\u5f8f\u5f90\u5f91\u5f92\u5f93\u5f94\u5f95\u5f96\u5f97\u5f98\u5f99\u5f9a\u5f9b\u5f9c\u5f9d\u5f9e\u5f9f\u5fa0\u5fa1\u5fa2\u5fa3\u5fa4\u5fa5\u5fa6\u5fa7\u5fa8\u5fa9\u5faa\u5fab\u5fac\u5fad\u5fae\u5faf\u5fb0\u5fb1\u5fb2\u5fb3\u5fb4\u5fb5\u5fb6\u5fb7\u5fb8\u5fb9\u5fba\u5fbb\u5fbc\u5fbd\u5fbe\u5fbf\u5fc0\u5fc1\u5fc2\u5fc3\u5fc4\u5fc5\u5fc6\u5fc7\u5fc8\u5fc9\u5fca\u5fcb\u5fcc\u5fcd\u5fce\u5fcf\u5fd0\u5fd1\u5fd2\u5fd3\u5fd4\u5fd5\u5fd6\u5fd7\u5fd8\u5fd9\u5fda\u5fdb\u5fdc\u5fdd\u5fde\u5fdf\u5fe0\u5fe1\u5fe2\u5fe3\u5fe4\u5fe5\u5fe6\u5fe7\u5fe8\u5fe9\u5fea\u5feb\u5fec\u5fed\u5fee\u5fef\u5ff0\u5ff1\u5ff2\u5ff3\u5ff4\u5ff5\u5ff6\u5ff7\u5ff8\u5ff9\u5ffa\u5ffb\u5ffc\u5ffd\u5ffe\u5fff\u6000\u6001\u6002\u6003\u6004\u6005\u6006\u6007\u6008\u6009\u600a\u600b\u600c\u600d\u600e\u600f\u6010\u6011\u6012\u6013\u6014\u6015\u6016\u6017\u6018\u6019\u601a\u601b\u601c\u601d\u601e\u601f\u6020\u6021\u6022\u6023\u6024\u6025\u6026\u6027\u6028\u6029\u602a\u602b\u602c\u602d\u602e\u602f\u6030\u6031\u6032\u6033\u6034\u6035\u6036\u6037\u6038\u6039\u603a\u603b\u603c\u603d\u603e\u603f\u6040\u6041\u6042\u6043\u6044\u6045\u6046\u6047\u6048\u6049\u604a\u604b\u604c\u604d\u604e\u604f\u6050\u6051\u6052\u6053\u6054\u6055\u6056\u6057\u6058\u6059\u605a\u605b\u605c\u605d\u605e\u605f\u6060\u6061\u6062\u6063\u6064\u6065\u6066\u6067\u6068\u6069\u606a\u606b\u606c\u606d\u606e\u606f\u6070\u6071\u6072\u6073\u6074\u6075\u6076\u6077\u6078\u6079\u607a\u607b\u607c\u607d\u607e\u607f\u6080\u6081\u6082\u6083\u6084\u6085\u6086\u6087\u6088\u6089\u608a\u608b\u608c\u608d\u608e\u608f\u6090\u6091\u6092\u6093\u6094\u6095\u6096\u6097\u6098\u6099\u609a\u609b\u609c\u609d\u609e\u609f\u60a0\u60a1\u60a2\u60a3\u60a4\u60a5\u60a6\u60a7\u60a8\u60a9\u60aa\u60ab\u60ac\u60ad\u60ae\u60af\u60b0\u60b1\u60b2\u60b3\u60b4\u60b5\u60b6\u60b7\u60b8\u60b9\u60ba\u60bb\u60bc\u60bd\u60be\u60bf\u60c0\u60c1\u60c2\u60c3\u60c4\u60c5\u60c6\u60c7\u60c8\u60c9\u60ca\u60cb\u60cc\u60cd\u60ce\u60cf\u60d0\u60d1\u60d2\u60d3\u60d4\u60d5\u60d6\u60d7\u60d8\u60d9\u60da\u60db\u60dc\u60dd\u60de\u60df\u60e0\u60e1\u60e2\u60e3\u60e4\u60e5\u60e6\u60e7\u60e8\u60e9\u60ea\u60eb\u60ec\u60ed\u60ee\u60ef\u60f0\u60f1\u60f2\u60f3\u60f4\u60f5\u60f6\u60f7\u60f8\u60f9\u60fa\u60fb\u60fc\u60fd\u60fe\u60ff\u6100\u6101\u6102\u6103\u6104\u6105\u6106\u6107\u6108\u6109\u610a\u610b\u610c\u610d\u610e\u610f\u6110\u6111\u6112\u6113\u6114\u6115\u6116\u6117\u6118\u6119\u611a\u611b\u611c\u611d\u611e\u611f\u6120\u6121\u6122\u6123\u6124\u6125\u6126\u6127\u6128\u6129\u612a\u612b\u612c\u612d\u612e\u612f\u6130\u6131\u6132\u6133\u6134\u6135\u6136\u6137\u6138\u6139\u613a\u613b\u613c\u613d\u613e\u613f\u6140\u6141\u6142\u6143\u6144\u6145\u6146\u6147\u6148\u6149\u614a\u614b\u614c\u614d\u614e\u614f\u6150\u6151\u6152\u6153\u6154\u6155\u6156\u6157\u6158\u6159\u615a\u615b\u615c\u615d\u615e\u615f\u6160\u6161\u6162\u6163\u6164\u6165\u6166\u6167\u6168\u6169\u616a\u616b\u616c\u616d\u616e\u616f\u6170\u6171\u6172\u6173\u6174\u6175\u6176\u6177\u6178\u6179\u617a\u617b\u617c\u617d\u617e\u617f\u6180\u6181\u6182\u6183\u6184\u6185\u6186\u6187\u6188\u6189\u618a\u618b\u618c\u618d\u618e\u618f\u6190\u6191\u6192\u6193\u6194\u6195\u6196\u6197\u6198\u6199\u619a\u619b\u619c\u619d\u619e\u619f\u61a0\u61a1\u61a2\u61a3\u61a4\u61a5\u61a6\u61a7\u61a8\u61a9\u61aa\u61ab\u61ac\u61ad\u61ae\u61af\u61b0\u61b1\u61b2\u61b3\u61b4\u61b5\u61b6\u61b7\u61b8\u61b9\u61ba\u61bb\u61bc\u61bd\u61be\u61bf\u61c0\u61c1\u61c2\u61c3\u61c4\u61c5\u61c6\u61c7\u61c8\u61c9\u61ca\u61cb\u61cc\u61cd\u61ce\u61cf\u61d0\u61d1\u61d2\u61d3\u61d4\u61d5\u61d6\u61d7\u61d8\u61d9\u61da\u61db\u61dc\u61dd\u61de\u61df\u61e0\u61e1\u61e2\u61e3\u61e4\u61e5\u61e6\u61e7\u61e8\u61e9\u61ea\u61eb\u61ec\u61ed\u61ee\u61ef\u61f0\u61f1\u61f2\u61f3\u61f4\u61f5\u61f6\u61f7\u61f8\u61f9\u61fa\u61fb\u61fc\u61fd\u61fe\u61ff\u6200\u6201\u6202\u6203\u6204\u6205\u6206\u6207\u6208\u6209\u620a\u620b\u620c\u620d\u620e\u620f\u6210\u6211\u6212\u6213\u6214\u6215\u6216\u6217\u6218\u6219\u621a\u621b\u621c\u621d\u621e\u621f\u6220\u6221\u6222\u6223\u6224\u6225\u6226\u6227\u6228\u6229\u622a\u622b\u622c\u622d\u622e\u622f\u6230\u6231\u6232\u6233\u6234\u6235\u6236\u6237\u6238\u6239\u623a\u623b\u623c\u623d\u623e\u623f\u6240\u6241\u6242\u6243\u6244\u6245\u6246\u6247\u6248\u6249\u624a\u624b\u624c\u624d\u624e\u624f\u6250\u6251\u6252\u6253\u6254\u6255\u6256\u6257\u6258\u6259\u625a\u625b\u625c\u625d\u625e\u625f\u6260\u6261\u6262\u6263\u6264\u6265\u6266\u6267\u6268\u6269\u626a\u626b\u626c\u626d\u626e\u626f\u6270\u6271\u6272\u6273\u6274\u6275\u6276\u6277\u6278\u6279\u627a\u627b\u627c\u627d\u627e\u627f\u6280\u6281\u6282\u6283\u6284\u6285\u6286\u6287\u6288\u6289\u628a\u628b\u628c\u628d\u628e\u628f\u6290\u6291\u6292\u6293\u6294\u6295\u6296\u6297\u6298\u6299\u629a\u629b\u629c\u629d\u629e\u629f\u62a0\u62a1\u62a2\u62a3\u62a4\u62a5\u62a6\u62a7\u62a8\u62a9\u62aa\u62ab\u62ac\u62ad\u62ae\u62af\u62b0\u62b1\u62b2\u62b3\u62b4\u62b5\u62b6\u62b7\u62b8\u62b9\u62ba\u62bb\u62bc\u62bd\u62be\u62bf\u62c0\u62c1\u62c2\u62c3\u62c4\u62c5\u62c6\u62c7\u62c8\u62c9\u62ca\u62cb\u62cc\u62cd\u62ce\u62cf\u62d0\u62d1\u62d2\u62d3\u62d4\u62d5\u62d6\u62d7\u62d8\u62d9\u62da\u62db\u62dc\u62dd\u62de\u62df\u62e0\u62e1\u62e2\u62e3\u62e4\u62e5\u62e6\u62e7\u62e8\u62e9\u62ea\u62eb\u62ec\u62ed\u62ee\u62ef\u62f0\u62f1\u62f2\u62f3\u62f4\u62f5\u62f6\u62f7\u62f8\u62f9\u62fa\u62fb\u62fc\u62fd\u62fe\u62ff\u6300\u6301\u6302\u6303\u6304\u6305\u6306\u6307\u6308\u6309\u630a\u630b\u630c\u630d\u630e\u630f\u6310\u6311\u6312\u6313\u6314\u6315\u6316\u6317\u6318\u6319\u631a\u631b\u631c\u631d\u631e\u631f\u6320\u6321\u6322\u6323\u6324\u6325\u6326\u6327\u6328\u6329\u632a\u632b\u632c\u632d\u632e\u632f\u6330\u6331\u6332\u6333\u6334\u6335\u6336\u6337\u6338\u6339\u633a\u633b\u633c\u633d\u633e\u633f\u6340\u6341\u6342\u6343\u6344\u6345\u6346\u6347\u6348\u6349\u634a\u634b\u634c\u634d\u634e\u634f\u6350\u6351\u6352\u6353\u6354\u6355\u6356\u6357\u6358\u6359\u635a\u635b\u635c\u635d\u635e\u635f\u6360\u6361\u6362\u6363\u6364\u6365\u6366\u6367\u6368\u6369\u636a\u636b\u636c\u636d\u636e\u636f\u6370\u6371\u6372\u6373\u6374\u6375\u6376\u6377\u6378\u6379\u637a\u637b\u637c\u637d\u637e\u637f\u6380\u6381\u6382\u6383\u6384\u6385\u6386\u6387\u6388\u6389\u638a\u638b\u638c\u638d\u638e\u638f\u6390\u6391\u6392\u6393\u6394\u6395\u6396\u6397\u6398\u6399\u639a\u639b\u639c\u639d\u639e\u639f\u63a0\u63a1\u63a2\u63a3\u63a4\u63a5\u63a6\u63a7\u63a8\u63a9\u63aa\u63ab\u63ac\u63ad\u63ae\u63af\u63b0\u63b1\u63b2\u63b3\u63b4\u63b5\u63b6\u63b7\u63b8\u63b9\u63ba\u63bb\u63bc\u63bd\u63be\u63bf\u63c0\u63c1\u63c2\u63c3\u63c4\u63c5\u63c6\u63c7\u63c8\u63c9\u63ca\u63cb\u63cc\u63cd\u63ce\u63cf\u63d0\u63d1\u63d2\u63d3\u63d4\u63d5\u63d6\u63d7\u63d8\u63d9\u63da\u63db\u63dc\u63dd\u63de\u63df\u63e0\u63e1\u63e2\u63e3\u63e4\u63e5\u63e6\u63e7\u63e8\u63e9\u63ea\u63eb\u63ec\u63ed\u63ee\u63ef\u63f0\u63f1\u63f2\u63f3\u63f4\u63f5\u63f6\u63f7\u63f8\u63f9\u63fa\u63fb\u63fc\u63fd\u63fe\u63ff\u6400\u6401\u6402\u6403\u6404\u6405\u6406\u6407\u6408\u6409\u640a\u640b\u640c\u640d\u640e\u640f\u6410\u6411\u6412\u6413\u6414\u6415\u6416\u6417\u6418\u6419\u641a\u641b\u641c\u641d\u641e\u641f\u6420\u6421\u6422\u6423\u6424\u6425\u6426\u6427\u6428\u6429\u642a\u642b\u642c\u642d\u642e\u642f\u6430\u6431\u6432\u6433\u6434\u6435\u6436\u6437\u6438\u6439\u643a\u643b\u643c\u643d\u643e\u643f\u6440\u6441\u6442\u6443\u6444\u6445\u6446\u6447\u6448\u6449\u644a\u644b\u644c\u644d\u644e\u644f\u6450\u6451\u6452\u6453\u6454\u6455\u6456\u6457\u6458\u6459\u645a\u645b\u645c\u645d\u645e\u645f\u6460\u6461\u6462\u6463\u6464\u6465\u6466\u6467\u6468\u6469\u646a\u646b\u646c\u646d\u646e\u646f\u6470\u6471\u6472\u6473\u6474\u6475\u6476\u6477\u6478\u6479\u647a\u647b\u647c\u647d\u647e\u647f\u6480\u6481\u6482\u6483\u6484\u6485\u6486\u6487\u6488\u6489\u648a\u648b\u648c\u648d\u648e\u648f\u6490\u6491\u6492\u6493\u6494\u6495\u6496\u6497\u6498\u6499\u649a\u649b\u649c\u649d\u649e\u649f\u64a0\u64a1\u64a2\u64a3\u64a4\u64a5\u64a6\u64a7\u64a8\u64a9\u64aa\u64ab\u64ac\u64ad\u64ae\u64af\u64b0\u64b1\u64b2\u64b3\u64b4\u64b5\u64b6\u64b7\u64b8\u64b9\u64ba\u64bb\u64bc\u64bd\u64be\u64bf\u64c0\u64c1\u64c2\u64c3\u64c4\u64c5\u64c6\u64c7\u64c8\u64c9\u64ca\u64cb\u64cc\u64cd\u64ce\u64cf\u64d0\u64d1\u64d2\u64d3\u64d4\u64d5\u64d6\u64d7\u64d8\u64d9\u64da\u64db\u64dc\u64dd\u64de\u64df\u64e0\u64e1\u64e2\u64e3\u64e4\u64e5\u64e6\u64e7\u64e8\u64e9\u64ea\u64eb\u64ec\u64ed\u64ee\u64ef\u64f0\u64f1\u64f2\u64f3\u64f4\u64f5\u64f6\u64f7\u64f8\u64f9\u64fa\u64fb\u64fc\u64fd\u64fe\u64ff\u6500\u6501\u6502\u6503\u6504\u6505\u6506\u6507\u6508\u6509\u650a\u650b\u650c\u650d\u650e\u650f\u6510\u6511\u6512\u6513\u6514\u6515\u6516\u6517\u6518\u6519\u651a\u651b\u651c\u651d\u651e\u651f\u6520\u6521\u6522\u6523\u6524\u6525\u6526\u6527\u6528\u6529\u652a\u652b\u652c\u652d\u652e\u652f\u6530\u6531\u6532\u6533\u6534\u6535\u6536\u6537\u6538\u6539\u653a\u653b\u653c\u653d\u653e\u653f\u6540\u6541\u6542\u6543\u6544\u6545\u6546\u6547\u6548\u6549\u654a\u654b\u654c\u654d\u654e\u654f\u6550\u6551\u6552\u6553\u6554\u6555\u6556\u6557\u6558\u6559\u655a\u655b\u655c\u655d\u655e\u655f\u6560\u6561\u6562\u6563\u6564\u6565\u6566\u6567\u6568\u6569\u656a\u656b\u656c\u656d\u656e\u656f\u6570\u6571\u6572\u6573\u6574\u6575\u6576\u6577\u6578\u6579\u657a\u657b\u657c\u657d\u657e\u657f\u6580\u6581\u6582\u6583\u6584\u6585\u6586\u6587\u6588\u6589\u658a\u658b\u658c\u658d\u658e\u658f\u6590\u6591\u6592\u6593\u6594\u6595\u6596\u6597\u6598\u6599\u659a\u659b\u659c\u659d\u659e\u659f\u65a0\u65a1\u65a2\u65a3\u65a4\u65a5\u65a6\u65a7\u65a8\u65a9\u65aa\u65ab\u65ac\u65ad\u65ae\u65af\u65b0\u65b1\u65b2\u65b3\u65b4\u65b5\u65b6\u65b7\u65b8\u65b9\u65ba\u65bb\u65bc\u65bd\u65be\u65bf\u65c0\u65c1\u65c2\u65c3\u65c4\u65c5\u65c6\u65c7\u65c8\u65c9\u65ca\u65cb\u65cc\u65cd\u65ce\u65cf\u65d0\u65d1\u65d2\u65d3\u65d4\u65d5\u65d6\u65d7\u65d8\u65d9\u65da\u65db\u65dc\u65dd\u65de\u65df\u65e0\u65e1\u65e2\u65e3\u65e4\u65e5\u65e6\u65e7\u65e8\u65e9\u65ea\u65eb\u65ec\u65ed\u65ee\u65ef\u65f0\u65f1\u65f2\u65f3\u65f4\u65f5\u65f6\u65f7\u65f8\u65f9\u65fa\u65fb\u65fc\u65fd\u65fe\u65ff\u6600\u6601\u6602\u6603\u6604\u6605\u6606\u6607\u6608\u6609\u660a\u660b\u660c\u660d\u660e\u660f\u6610\u6611\u6612\u6613\u6614\u6615\u6616\u6617\u6618\u6619\u661a\u661b\u661c\u661d\u661e\u661f\u6620\u6621\u6622\u6623\u6624\u6625\u6626\u6627\u6628\u6629\u662a\u662b\u662c\u662d\u662e\u662f\u6630\u6631\u6632\u6633\u6634\u6635\u6636\u6637\u6638\u6639\u663a\u663b\u663c\u663d\u663e\u663f\u6640\u6641\u6642\u6643\u6644\u6645\u6646\u6647\u6648\u6649\u664a\u664b\u664c\u664d\u664e\u664f\u6650\u6651\u6652\u6653\u6654\u6655\u6656\u6657\u6658\u6659\u665a\u665b\u665c\u665d\u665e\u665f\u6660\u6661\u6662\u6663\u6664\u6665\u6666\u6667\u6668\u6669\u666a\u666b\u666c\u666d\u666e\u666f\u6670\u6671\u6672\u6673\u6674\u6675\u6676\u6677\u6678\u6679\u667a\u667b\u667c\u667d\u667e\u667f\u6680\u6681\u6682\u6683\u6684\u6685\u6686\u6687\u6688\u6689\u668a\u668b\u668c\u668d\u668e\u668f\u6690\u6691\u6692\u6693\u6694\u6695\u6696\u6697\u6698\u6699\u669a\u669b\u669c\u669d\u669e\u669f\u66a0\u66a1\u66a2\u66a3\u66a4\u66a5\u66a6\u66a7\u66a8\u66a9\u66aa\u66ab\u66ac\u66ad\u66ae\u66af\u66b0\u66b1\u66b2\u66b3\u66b4\u66b5\u66b6\u66b7\u66b8\u66b9\u66ba\u66bb\u66bc\u66bd\u66be\u66bf\u66c0\u66c1\u66c2\u66c3\u66c4\u66c5\u66c6\u66c7\u66c8\u66c9\u66ca\u66cb\u66cc\u66cd\u66ce\u66cf\u66d0\u66d1\u66d2\u66d3\u66d4\u66d5\u66d6\u66d7\u66d8\u66d9\u66da\u66db\u66dc\u66dd\u66de\u66df\u66e0\u66e1\u66e2\u66e3\u66e4\u66e5\u66e6\u66e7\u66e8\u66e9\u66ea\u66eb\u66ec\u66ed\u66ee\u66ef\u66f0\u66f1\u66f2\u66f3\u66f4\u66f5\u66f6\u66f7\u66f8\u66f9\u66fa\u66fb\u66fc\u66fd\u66fe\u66ff\u6700\u6701\u6702\u6703\u6704\u6705\u6706\u6707\u6708\u6709\u670a\u670b\u670c\u670d\u670e\u670f\u6710\u6711\u6712\u6713\u6714\u6715\u6716\u6717\u6718\u6719\u671a\u671b\u671c\u671d\u671e\u671f\u6720\u6721\u6722\u6723\u6724\u6725\u6726\u6727\u6728\u6729\u672a\u672b\u672c\u672d\u672e\u672f\u6730\u6731\u6732\u6733\u6734\u6735\u6736\u6737\u6738\u6739\u673a\u673b\u673c\u673d\u673e\u673f\u6740\u6741\u6742\u6743\u6744\u6745\u6746\u6747\u6748\u6749\u674a\u674b\u674c\u674d\u674e\u674f\u6750\u6751\u6752\u6753\u6754\u6755\u6756\u6757\u6758\u6759\u675a\u675b\u675c\u675d\u675e\u675f\u6760\u6761\u6762\u6763\u6764\u6765\u6766\u6767\u6768\u6769\u676a\u676b\u676c\u676d\u676e\u676f\u6770\u6771\u6772\u6773\u6774\u6775\u6776\u6777\u6778\u6779\u677a\u677b\u677c\u677d\u677e\u677f\u6780\u6781\u6782\u6783\u6784\u6785\u6786\u6787\u6788\u6789\u678a\u678b\u678c\u678d\u678e\u678f\u6790\u6791\u6792\u6793\u6794\u6795\u6796\u6797\u6798\u6799\u679a\u679b\u679c\u679d\u679e\u679f\u67a0\u67a1\u67a2\u67a3\u67a4\u67a5\u67a6\u67a7\u67a8\u67a9\u67aa\u67ab\u67ac\u67ad\u67ae\u67af\u67b0\u67b1\u67b2\u67b3\u67b4\u67b5\u67b6\u67b7\u67b8\u67b9\u67ba\u67bb\u67bc\u67bd\u67be\u67bf\u67c0\u67c1\u67c2\u67c3\u67c4\u67c5\u67c6\u67c7\u67c8\u67c9\u67ca\u67cb\u67cc\u67cd\u67ce\u67cf\u67d0\u67d1\u67d2\u67d3\u67d4\u67d5\u67d6\u67d7\u67d8\u67d9\u67da\u67db\u67dc\u67dd\u67de\u67df\u67e0\u67e1\u67e2\u67e3\u67e4\u67e5\u67e6\u67e7\u67e8\u67e9\u67ea\u67eb\u67ec\u67ed\u67ee\u67ef\u67f0\u67f1\u67f2\u67f3\u67f4\u67f5\u67f6\u67f7\u67f8\u67f9\u67fa\u67fb\u67fc\u67fd\u67fe\u67ff\u6800\u6801\u6802\u6803\u6804\u6805\u6806\u6807\u6808\u6809\u680a\u680b\u680c\u680d\u680e\u680f\u6810\u6811\u6812\u6813\u6814\u6815\u6816\u6817\u6818\u6819\u681a\u681b\u681c\u681d\u681e\u681f\u6820\u6821\u6822\u6823\u6824\u6825\u6826\u6827\u6828\u6829\u682a\u682b\u682c\u682d\u682e\u682f\u6830\u6831\u6832\u6833\u6834\u6835\u6836\u6837\u6838\u6839\u683a\u683b\u683c\u683d\u683e\u683f\u6840\u6841\u6842\u6843\u6844\u6845\u6846\u6847\u6848\u6849\u684a\u684b\u684c\u684d\u684e\u684f\u6850\u6851\u6852\u6853\u6854\u6855\u6856\u6857\u6858\u6859\u685a\u685b\u685c\u685d\u685e\u685f\u6860\u6861\u6862\u6863\u6864\u6865\u6866\u6867\u6868\u6869\u686a\u686b\u686c\u686d\u686e\u686f\u6870\u6871\u6872\u6873\u6874\u6875\u6876\u6877\u6878\u6879\u687a\u687b\u687c\u687d\u687e\u687f\u6880\u6881\u6882\u6883\u6884\u6885\u6886\u6887\u6888\u6889\u688a\u688b\u688c\u688d\u688e\u688f\u6890\u6891\u6892\u6893\u6894\u6895\u6896\u6897\u6898\u6899\u689a\u689b\u689c\u689d\u689e\u689f\u68a0\u68a1\u68a2\u68a3\u68a4\u68a5\u68a6\u68a7\u68a8\u68a9\u68aa\u68ab\u68ac\u68ad\u68ae\u68af\u68b0\u68b1\u68b2\u68b3\u68b4\u68b5\u68b6\u68b7\u68b8\u68b9\u68ba\u68bb\u68bc\u68bd\u68be\u68bf\u68c0\u68c1\u68c2\u68c3\u68c4\u68c5\u68c6\u68c7\u68c8\u68c9\u68ca\u68cb\u68cc\u68cd\u68ce\u68cf\u68d0\u68d1\u68d2\u68d3\u68d4\u68d5\u68d6\u68d7\u68d8\u68d9\u68da\u68db\u68dc\u68dd\u68de\u68df\u68e0\u68e1\u68e2\u68e3\u68e4\u68e5\u68e6\u68e7\u68e8\u68e9\u68ea\u68eb\u68ec\u68ed\u68ee\u68ef\u68f0\u68f1\u68f2\u68f3\u68f4\u68f5\u68f6\u68f7\u68f8\u68f9\u68fa\u68fb\u68fc\u68fd\u68fe\u68ff\u6900\u6901\u6902\u6903\u6904\u6905\u6906\u6907\u6908\u6909\u690a\u690b\u690c\u690d\u690e\u690f\u6910\u6911\u6912\u6913\u6914\u6915\u6916\u6917\u6918\u6919\u691a\u691b\u691c\u691d\u691e\u691f\u6920\u6921\u6922\u6923\u6924\u6925\u6926\u6927\u6928\u6929\u692a\u692b\u692c\u692d\u692e\u692f\u6930\u6931\u6932\u6933\u6934\u6935\u6936\u6937\u6938\u6939\u693a\u693b\u693c\u693d\u693e\u693f\u6940\u6941\u6942\u6943\u6944\u6945\u6946\u6947\u6948\u6949\u694a\u694b\u694c\u694d\u694e\u694f\u6950\u6951\u6952\u6953\u6954\u6955\u6956\u6957\u6958\u6959\u695a\u695b\u695c\u695d\u695e\u695f\u6960\u6961\u6962\u6963\u6964\u6965\u6966\u6967\u6968\u6969\u696a\u696b\u696c\u696d\u696e\u696f\u6970\u6971\u6972\u6973\u6974\u6975\u6976\u6977\u6978\u6979\u697a\u697b\u697c\u697d\u697e\u697f\u6980\u6981\u6982\u6983\u6984\u6985\u6986\u6987\u6988\u6989\u698a\u698b\u698c\u698d\u698e\u698f\u6990\u6991\u6992\u6993\u6994\u6995\u6996\u6997\u6998\u6999\u699a\u699b\u699c\u699d\u699e\u699f\u69a0\u69a1\u69a2\u69a3\u69a4\u69a5\u69a6\u69a7\u69a8\u69a9\u69aa\u69ab\u69ac\u69ad\u69ae\u69af\u69b0\u69b1\u69b2\u69b3\u69b4\u69b5\u69b6\u69b7\u69b8\u69b9\u69ba\u69bb\u69bc\u69bd\u69be\u69bf\u69c0\u69c1\u69c2\u69c3\u69c4\u69c5\u69c6\u69c7\u69c8\u69c9\u69ca\u69cb\u69cc\u69cd\u69ce\u69cf\u69d0\u69d1\u69d2\u69d3\u69d4\u69d5\u69d6\u69d7\u69d8\u69d9\u69da\u69db\u69dc\u69dd\u69de\u69df\u69e0\u69e1\u69e2\u69e3\u69e4\u69e5\u69e6\u69e7\u69e8\u69e9\u69ea\u69eb\u69ec\u69ed\u69ee\u69ef\u69f0\u69f1\u69f2\u69f3\u69f4\u69f5\u69f6\u69f7\u69f8\u69f9\u69fa\u69fb\u69fc\u69fd\u69fe\u69ff\u6a00\u6a01\u6a02\u6a03\u6a04\u6a05\u6a06\u6a07\u6a08\u6a09\u6a0a\u6a0b\u6a0c\u6a0d\u6a0e\u6a0f\u6a10\u6a11\u6a12\u6a13\u6a14\u6a15\u6a16\u6a17\u6a18\u6a19\u6a1a\u6a1b\u6a1c\u6a1d\u6a1e\u6a1f\u6a20\u6a21\u6a22\u6a23\u6a24\u6a25\u6a26\u6a27\u6a28\u6a29\u6a2a\u6a2b\u6a2c\u6a2d\u6a2e\u6a2f\u6a30\u6a31\u6a32\u6a33\u6a34\u6a35\u6a36\u6a37\u6a38\u6a39\u6a3a\u6a3b\u6a3c\u6a3d\u6a3e\u6a3f\u6a40\u6a41\u6a42\u6a43\u6a44\u6a45\u6a46\u6a47\u6a48\u6a49\u6a4a\u6a4b\u6a4c\u6a4d\u6a4e\u6a4f\u6a50\u6a51\u6a52\u6a53\u6a54\u6a55\u6a56\u6a57\u6a58\u6a59\u6a5a\u6a5b\u6a5c\u6a5d\u6a5e\u6a5f\u6a60\u6a61\u6a62\u6a63\u6a64\u6a65\u6a66\u6a67\u6a68\u6a69\u6a6a\u6a6b\u6a6c\u6a6d\u6a6e\u6a6f\u6a70\u6a71\u6a72\u6a73\u6a74\u6a75\u6a76\u6a77\u6a78\u6a79\u6a7a\u6a7b\u6a7c\u6a7d\u6a7e\u6a7f\u6a80\u6a81\u6a82\u6a83\u6a84\u6a85\u6a86\u6a87\u6a88\u6a89\u6a8a\u6a8b\u6a8c\u6a8d\u6a8e\u6a8f\u6a90\u6a91\u6a92\u6a93\u6a94\u6a95\u6a96\u6a97\u6a98\u6a99\u6a9a\u6a9b\u6a9c\u6a9d\u6a9e\u6a9f\u6aa0\u6aa1\u6aa2\u6aa3\u6aa4\u6aa5\u6aa6\u6aa7\u6aa8\u6aa9\u6aaa\u6aab\u6aac\u6aad\u6aae\u6aaf\u6ab0\u6ab1\u6ab2\u6ab3\u6ab4\u6ab5\u6ab6\u6ab7\u6ab8\u6ab9\u6aba\u6abb\u6abc\u6abd\u6abe\u6abf\u6ac0\u6ac1\u6ac2\u6ac3\u6ac4\u6ac5\u6ac6\u6ac7\u6ac8\u6ac9\u6aca\u6acb\u6acc\u6acd\u6ace\u6acf\u6ad0\u6ad1\u6ad2\u6ad3\u6ad4\u6ad5\u6ad6\u6ad7\u6ad8\u6ad9\u6ada\u6adb\u6adc\u6add\u6ade\u6adf\u6ae0\u6ae1\u6ae2\u6ae3\u6ae4\u6ae5\u6ae6\u6ae7\u6ae8\u6ae9\u6aea\u6aeb\u6aec\u6aed\u6aee\u6aef\u6af0\u6af1\u6af2\u6af3\u6af4\u6af5\u6af6\u6af7\u6af8\u6af9\u6afa\u6afb\u6afc\u6afd\u6afe\u6aff\u6b00\u6b01\u6b02\u6b03\u6b04\u6b05\u6b06\u6b07\u6b08\u6b09\u6b0a\u6b0b\u6b0c\u6b0d\u6b0e\u6b0f\u6b10\u6b11\u6b12\u6b13\u6b14\u6b15\u6b16\u6b17\u6b18\u6b19\u6b1a\u6b1b\u6b1c\u6b1d\u6b1e\u6b1f\u6b20\u6b21\u6b22\u6b23\u6b24\u6b25\u6b26\u6b27\u6b28\u6b29\u6b2a\u6b2b\u6b2c\u6b2d\u6b2e\u6b2f\u6b30\u6b31\u6b32\u6b33\u6b34\u6b35\u6b36\u6b37\u6b38\u6b39\u6b3a\u6b3b\u6b3c\u6b3d\u6b3e\u6b3f\u6b40\u6b41\u6b42\u6b43\u6b44\u6b45\u6b46\u6b47\u6b48\u6b49\u6b4a\u6b4b\u6b4c\u6b4d\u6b4e\u6b4f\u6b50\u6b51\u6b52\u6b53\u6b54\u6b55\u6b56\u6b57\u6b58\u6b59\u6b5a\u6b5b\u6b5c\u6b5d\u6b5e\u6b5f\u6b60\u6b61\u6b62\u6b63\u6b64\u6b65\u6b66\u6b67\u6b68\u6b69\u6b6a\u6b6b\u6b6c\u6b6d\u6b6e\u6b6f\u6b70\u6b71\u6b72\u6b73\u6b74\u6b75\u6b76\u6b77\u6b78\u6b79\u6b7a\u6b7b\u6b7c\u6b7d\u6b7e\u6b7f\u6b80\u6b81\u6b82\u6b83\u6b84\u6b85\u6b86\u6b87\u6b88\u6b89\u6b8a\u6b8b\u6b8c\u6b8d\u6b8e\u6b8f\u6b90\u6b91\u6b92\u6b93\u6b94\u6b95\u6b96\u6b97\u6b98\u6b99\u6b9a\u6b9b\u6b9c\u6b9d\u6b9e\u6b9f\u6ba0\u6ba1\u6ba2\u6ba3\u6ba4\u6ba5\u6ba6\u6ba7\u6ba8\u6ba9\u6baa\u6bab\u6bac\u6bad\u6bae\u6baf\u6bb0\u6bb1\u6bb2\u6bb3\u6bb4\u6bb5\u6bb6\u6bb7\u6bb8\u6bb9\u6bba\u6bbb\u6bbc\u6bbd\u6bbe\u6bbf\u6bc0\u6bc1\u6bc2\u6bc3\u6bc4\u6bc5\u6bc6\u6bc7\u6bc8\u6bc9\u6bca\u6bcb\u6bcc\u6bcd\u6bce\u6bcf\u6bd0\u6bd1\u6bd2\u6bd3\u6bd4\u6bd5\u6bd6\u6bd7\u6bd8\u6bd9\u6bda\u6bdb\u6bdc\u6bdd\u6bde\u6bdf\u6be0\u6be1\u6be2\u6be3\u6be4\u6be5\u6be6\u6be7\u6be8\u6be9\u6bea\u6beb\u6bec\u6bed\u6bee\u6bef\u6bf0\u6bf1\u6bf2\u6bf3\u6bf4\u6bf5\u6bf6\u6bf7\u6bf8\u6bf9\u6bfa\u6bfb\u6bfc\u6bfd\u6bfe\u6bff\u6c00\u6c01\u6c02\u6c03\u6c04\u6c05\u6c06\u6c07\u6c08\u6c09\u6c0a\u6c0b\u6c0c\u6c0d\u6c0e\u6c0f\u6c10\u6c11\u6c12\u6c13\u6c14\u6c15\u6c16\u6c17\u6c18\u6c19\u6c1a\u6c1b\u6c1c\u6c1d\u6c1e\u6c1f\u6c20\u6c21\u6c22\u6c23\u6c24\u6c25\u6c26\u6c27\u6c28\u6c29\u6c2a\u6c2b\u6c2c\u6c2d\u6c2e\u6c2f\u6c30\u6c31\u6c32\u6c33\u6c34\u6c35\u6c36\u6c37\u6c38\u6c39\u6c3a\u6c3b\u6c3c\u6c3d\u6c3e\u6c3f\u6c40\u6c41\u6c42\u6c43\u6c44\u6c45\u6c46\u6c47\u6c48\u6c49\u6c4a\u6c4b\u6c4c\u6c4d\u6c4e\u6c4f\u6c50\u6c51\u6c52\u6c53\u6c54\u6c55\u6c56\u6c57\u6c58\u6c59\u6c5a\u6c5b\u6c5c\u6c5d\u6c5e\u6c5f\u6c60\u6c61\u6c62\u6c63\u6c64\u6c65\u6c66\u6c67\u6c68\u6c69\u6c6a\u6c6b\u6c6c\u6c6d\u6c6e\u6c6f\u6c70\u6c71\u6c72\u6c73\u6c74\u6c75\u6c76\u6c77\u6c78\u6c79\u6c7a\u6c7b\u6c7c\u6c7d\u6c7e\u6c7f\u6c80\u6c81\u6c82\u6c83\u6c84\u6c85\u6c86\u6c87\u6c88\u6c89\u6c8a\u6c8b\u6c8c\u6c8d\u6c8e\u6c8f\u6c90\u6c91\u6c92\u6c93\u6c94\u6c95\u6c96\u6c97\u6c98\u6c99\u6c9a\u6c9b\u6c9c\u6c9d\u6c9e\u6c9f\u6ca0\u6ca1\u6ca2\u6ca3\u6ca4\u6ca5\u6ca6\u6ca7\u6ca8\u6ca9\u6caa\u6cab\u6cac\u6cad\u6cae\u6caf\u6cb0\u6cb1\u6cb2\u6cb3\u6cb4\u6cb5\u6cb6\u6cb7\u6cb8\u6cb9\u6cba\u6cbb\u6cbc\u6cbd\u6cbe\u6cbf\u6cc0\u6cc1\u6cc2\u6cc3\u6cc4\u6cc5\u6cc6\u6cc7\u6cc8\u6cc9\u6cca\u6ccb\u6ccc\u6ccd\u6cce\u6ccf\u6cd0\u6cd1\u6cd2\u6cd3\u6cd4\u6cd5\u6cd6\u6cd7\u6cd8\u6cd9\u6cda\u6cdb\u6cdc\u6cdd\u6cde\u6cdf\u6ce0\u6ce1\u6ce2\u6ce3\u6ce4\u6ce5\u6ce6\u6ce7\u6ce8\u6ce9\u6cea\u6ceb\u6cec\u6ced\u6cee\u6cef\u6cf0\u6cf1\u6cf2\u6cf3\u6cf4\u6cf5\u6cf6\u6cf7\u6cf8\u6cf9\u6cfa\u6cfb\u6cfc\u6cfd\u6cfe\u6cff\u6d00\u6d01\u6d02\u6d03\u6d04\u6d05\u6d06\u6d07\u6d08\u6d09\u6d0a\u6d0b\u6d0c\u6d0d\u6d0e\u6d0f\u6d10\u6d11\u6d12\u6d13\u6d14\u6d15\u6d16\u6d17\u6d18\u6d19\u6d1a\u6d1b\u6d1c\u6d1d\u6d1e\u6d1f\u6d20\u6d21\u6d22\u6d23\u6d24\u6d25\u6d26\u6d27\u6d28\u6d29\u6d2a\u6d2b\u6d2c\u6d2d\u6d2e\u6d2f\u6d30\u6d31\u6d32\u6d33\u6d34\u6d35\u6d36\u6d37\u6d38\u6d39\u6d3a\u6d3b\u6d3c\u6d3d\u6d3e\u6d3f\u6d40\u6d41\u6d42\u6d43\u6d44\u6d45\u6d46\u6d47\u6d48\u6d49\u6d4a\u6d4b\u6d4c\u6d4d\u6d4e\u6d4f\u6d50\u6d51\u6d52\u6d53\u6d54\u6d55\u6d56\u6d57\u6d58\u6d59\u6d5a\u6d5b\u6d5c\u6d5d\u6d5e\u6d5f\u6d60\u6d61\u6d62\u6d63\u6d64\u6d65\u6d66\u6d67\u6d68\u6d69\u6d6a\u6d6b\u6d6c\u6d6d\u6d6e\u6d6f\u6d70\u6d71\u6d72\u6d73\u6d74\u6d75\u6d76\u6d77\u6d78\u6d79\u6d7a\u6d7b\u6d7c\u6d7d\u6d7e\u6d7f\u6d80\u6d81\u6d82\u6d83\u6d84\u6d85\u6d86\u6d87\u6d88\u6d89\u6d8a\u6d8b\u6d8c\u6d8d\u6d8e\u6d8f\u6d90\u6d91\u6d92\u6d93\u6d94\u6d95\u6d96\u6d97\u6d98\u6d99\u6d9a\u6d9b\u6d9c\u6d9d\u6d9e\u6d9f\u6da0\u6da1\u6da2\u6da3\u6da4\u6da5\u6da6\u6da7\u6da8\u6da9\u6daa\u6dab\u6dac\u6dad\u6dae\u6daf\u6db0\u6db1\u6db2\u6db3\u6db4\u6db5\u6db6\u6db7\u6db8\u6db9\u6dba\u6dbb\u6dbc\u6dbd\u6dbe\u6dbf\u6dc0\u6dc1\u6dc2\u6dc3\u6dc4\u6dc5\u6dc6\u6dc7\u6dc8\u6dc9\u6dca\u6dcb\u6dcc\u6dcd\u6dce\u6dcf\u6dd0\u6dd1\u6dd2\u6dd3\u6dd4\u6dd5\u6dd6\u6dd7\u6dd8\u6dd9\u6dda\u6ddb\u6ddc\u6ddd\u6dde\u6ddf\u6de0\u6de1\u6de2\u6de3\u6de4\u6de5\u6de6\u6de7\u6de8\u6de9\u6dea\u6deb\u6dec\u6ded\u6dee\u6def\u6df0\u6df1\u6df2\u6df3\u6df4\u6df5\u6df6\u6df7\u6df8\u6df9\u6dfa\u6dfb\u6dfc\u6dfd\u6dfe\u6dff\u6e00\u6e01\u6e02\u6e03\u6e04\u6e05\u6e06\u6e07\u6e08\u6e09\u6e0a\u6e0b\u6e0c\u6e0d\u6e0e\u6e0f\u6e10\u6e11\u6e12\u6e13\u6e14\u6e15\u6e16\u6e17\u6e18\u6e19\u6e1a\u6e1b\u6e1c\u6e1d\u6e1e\u6e1f\u6e20\u6e21\u6e22\u6e23\u6e24\u6e25\u6e26\u6e27\u6e28\u6e29\u6e2a\u6e2b\u6e2c\u6e2d\u6e2e\u6e2f\u6e30\u6e31\u6e32\u6e33\u6e34\u6e35\u6e36\u6e37\u6e38\u6e39\u6e3a\u6e3b\u6e3c\u6e3d\u6e3e\u6e3f\u6e40\u6e41\u6e42\u6e43\u6e44\u6e45\u6e46\u6e47\u6e48\u6e49\u6e4a\u6e4b\u6e4c\u6e4d\u6e4e\u6e4f\u6e50\u6e51\u6e52\u6e53\u6e54\u6e55\u6e56\u6e57\u6e58\u6e59\u6e5a\u6e5b\u6e5c\u6e5d\u6e5e\u6e5f\u6e60\u6e61\u6e62\u6e63\u6e64\u6e65\u6e66\u6e67\u6e68\u6e69\u6e6a\u6e6b\u6e6c\u6e6d\u6e6e\u6e6f\u6e70\u6e71\u6e72\u6e73\u6e74\u6e75\u6e76\u6e77\u6e78\u6e79\u6e7a\u6e7b\u6e7c\u6e7d\u6e7e\u6e7f\u6e80\u6e81\u6e82\u6e83\u6e84\u6e85\u6e86\u6e87\u6e88\u6e89\u6e8a\u6e8b\u6e8c\u6e8d\u6e8e\u6e8f\u6e90\u6e91\u6e92\u6e93\u6e94\u6e95\u6e96\u6e97\u6e98\u6e99\u6e9a\u6e9b\u6e9c\u6e9d\u6e9e\u6e9f\u6ea0\u6ea1\u6ea2\u6ea3\u6ea4\u6ea5\u6ea6\u6ea7\u6ea8\u6ea9\u6eaa\u6eab\u6eac\u6ead\u6eae\u6eaf\u6eb0\u6eb1\u6eb2\u6eb3\u6eb4\u6eb5\u6eb6\u6eb7\u6eb8\u6eb9\u6eba\u6ebb\u6ebc\u6ebd\u6ebe\u6ebf\u6ec0\u6ec1\u6ec2\u6ec3\u6ec4\u6ec5\u6ec6\u6ec7\u6ec8\u6ec9\u6eca\u6ecb\u6ecc\u6ecd\u6ece\u6ecf\u6ed0\u6ed1\u6ed2\u6ed3\u6ed4\u6ed5\u6ed6\u6ed7\u6ed8\u6ed9\u6eda\u6edb\u6edc\u6edd\u6ede\u6edf\u6ee0\u6ee1\u6ee2\u6ee3\u6ee4\u6ee5\u6ee6\u6ee7\u6ee8\u6ee9\u6eea\u6eeb\u6eec\u6eed\u6eee\u6eef\u6ef0\u6ef1\u6ef2\u6ef3\u6ef4\u6ef5\u6ef6\u6ef7\u6ef8\u6ef9\u6efa\u6efb\u6efc\u6efd\u6efe\u6eff\u6f00\u6f01\u6f02\u6f03\u6f04\u6f05\u6f06\u6f07\u6f08\u6f09\u6f0a\u6f0b\u6f0c\u6f0d\u6f0e\u6f0f\u6f10\u6f11\u6f12\u6f13\u6f14\u6f15\u6f16\u6f17\u6f18\u6f19\u6f1a\u6f1b\u6f1c\u6f1d\u6f1e\u6f1f\u6f20\u6f21\u6f22\u6f23\u6f24\u6f25\u6f26\u6f27\u6f28\u6f29\u6f2a\u6f2b\u6f2c\u6f2d\u6f2e\u6f2f\u6f30\u6f31\u6f32\u6f33\u6f34\u6f35\u6f36\u6f37\u6f38\u6f39\u6f3a\u6f3b\u6f3c\u6f3d\u6f3e\u6f3f\u6f40\u6f41\u6f42\u6f43\u6f44\u6f45\u6f46\u6f47\u6f48\u6f49\u6f4a\u6f4b\u6f4c\u6f4d\u6f4e\u6f4f\u6f50\u6f51\u6f52\u6f53\u6f54\u6f55\u6f56\u6f57\u6f58\u6f59\u6f5a\u6f5b\u6f5c\u6f5d\u6f5e\u6f5f\u6f60\u6f61\u6f62\u6f63\u6f64\u6f65\u6f66\u6f67\u6f68\u6f69\u6f6a\u6f6b\u6f6c\u6f6d\u6f6e\u6f6f\u6f70\u6f71\u6f72\u6f73\u6f74\u6f75\u6f76\u6f77\u6f78\u6f79\u6f7a\u6f7b\u6f7c\u6f7d\u6f7e\u6f7f\u6f80\u6f81\u6f82\u6f83\u6f84\u6f85\u6f86\u6f87\u6f88\u6f89\u6f8a\u6f8b\u6f8c\u6f8d\u6f8e\u6f8f\u6f90\u6f91\u6f92\u6f93\u6f94\u6f95\u6f96\u6f97\u6f98\u6f99\u6f9a\u6f9b\u6f9c\u6f9d\u6f9e\u6f9f\u6fa0\u6fa1\u6fa2\u6fa3\u6fa4\u6fa5\u6fa6\u6fa7\u6fa8\u6fa9\u6faa\u6fab\u6fac\u6fad\u6fae\u6faf\u6fb0\u6fb1\u6fb2\u6fb3\u6fb4\u6fb5\u6fb6\u6fb7\u6fb8\u6fb9\u6fba\u6fbb\u6fbc\u6fbd\u6fbe\u6fbf\u6fc0\u6fc1\u6fc2\u6fc3\u6fc4\u6fc5\u6fc6\u6fc7\u6fc8\u6fc9\u6fca\u6fcb\u6fcc\u6fcd\u6fce\u6fcf\u6fd0\u6fd1\u6fd2\u6fd3\u6fd4\u6fd5\u6fd6\u6fd7\u6fd8\u6fd9\u6fda\u6fdb\u6fdc\u6fdd\u6fde\u6fdf\u6fe0\u6fe1\u6fe2\u6fe3\u6fe4\u6fe5\u6fe6\u6fe7\u6fe8\u6fe9\u6fea\u6feb\u6fec\u6fed\u6fee\u6fef\u6ff0\u6ff1\u6ff2\u6ff3\u6ff4\u6ff5\u6ff6\u6ff7\u6ff8\u6ff9\u6ffa\u6ffb\u6ffc\u6ffd\u6ffe\u6fff\u7000\u7001\u7002\u7003\u7004\u7005\u7006\u7007\u7008\u7009\u700a\u700b\u700c\u700d\u700e\u700f\u7010\u7011\u7012\u7013\u7014\u7015\u7016\u7017\u7018\u7019\u701a\u701b\u701c\u701d\u701e\u701f\u7020\u7021\u7022\u7023\u7024\u7025\u7026\u7027\u7028\u7029\u702a\u702b\u702c\u702d\u702e\u702f\u7030\u7031\u7032\u7033\u7034\u7035\u7036\u7037\u7038\u7039\u703a\u703b\u703c\u703d\u703e\u703f\u7040\u7041\u7042\u7043\u7044\u7045\u7046\u7047\u7048\u7049\u704a\u704b\u704c\u704d\u704e\u704f\u7050\u7051\u7052\u7053\u7054\u7055\u7056\u7057\u7058\u7059\u705a\u705b\u705c\u705d\u705e\u705f\u7060\u7061\u7062\u7063\u7064\u7065\u7066\u7067\u7068\u7069\u706a\u706b\u706c\u706d\u706e\u706f\u7070\u7071\u7072\u7073\u7074\u7075\u7076\u7077\u7078\u7079\u707a\u707b\u707c\u707d\u707e\u707f\u7080\u7081\u7082\u7083\u7084\u7085\u7086\u7087\u7088\u7089\u708a\u708b\u708c\u708d\u708e\u708f\u7090\u7091\u7092\u7093\u7094\u7095\u7096\u7097\u7098\u7099\u709a\u709b\u709c\u709d\u709e\u709f\u70a0\u70a1\u70a2\u70a3\u70a4\u70a5\u70a6\u70a7\u70a8\u70a9\u70aa\u70ab\u70ac\u70ad\u70ae\u70af\u70b0\u70b1\u70b2\u70b3\u70b4\u70b5\u70b6\u70b7\u70b8\u70b9\u70ba\u70bb\u70bc\u70bd\u70be\u70bf\u70c0\u70c1\u70c2\u70c3\u70c4\u70c5\u70c6\u70c7\u70c8\u70c9\u70ca\u70cb\u70cc\u70cd\u70ce\u70cf\u70d0\u70d1\u70d2\u70d3\u70d4\u70d5\u70d6\u70d7\u70d8\u70d9\u70da\u70db\u70dc\u70dd\u70de\u70df\u70e0\u70e1\u70e2\u70e3\u70e4\u70e5\u70e6\u70e7\u70e8\u70e9\u70ea\u70eb\u70ec\u70ed\u70ee\u70ef\u70f0\u70f1\u70f2\u70f3\u70f4\u70f5\u70f6\u70f7\u70f8\u70f9\u70fa\u70fb\u70fc\u70fd\u70fe\u70ff\u7100\u7101\u7102\u7103\u7104\u7105\u7106\u7107\u7108\u7109\u710a\u710b\u710c\u710d\u710e\u710f\u7110\u7111\u7112\u7113\u7114\u7115\u7116\u7117\u7118\u7119\u711a\u711b\u711c\u711d\u711e\u711f\u7120\u7121\u7122\u7123\u7124\u7125\u7126\u7127\u7128\u7129\u712a\u712b\u712c\u712d\u712e\u712f\u7130\u7131\u7132\u7133\u7134\u7135\u7136\u7137\u7138\u7139\u713a\u713b\u713c\u713d\u713e\u713f\u7140\u7141\u7142\u7143\u7144\u7145\u7146\u7147\u7148\u7149\u714a\u714b\u714c\u714d\u714e\u714f\u7150\u7151\u7152\u7153\u7154\u7155\u7156\u7157\u7158\u7159\u715a\u715b\u715c\u715d\u715e\u715f\u7160\u7161\u7162\u7163\u7164\u7165\u7166\u7167\u7168\u7169\u716a\u716b\u716c\u716d\u716e\u716f\u7170\u7171\u7172\u7173\u7174\u7175\u7176\u7177\u7178\u7179\u717a\u717b\u717c\u717d\u717e\u717f\u7180\u7181\u7182\u7183\u7184\u7185\u7186\u7187\u7188\u7189\u718a\u718b\u718c\u718d\u718e\u718f\u7190\u7191\u7192\u7193\u7194\u7195\u7196\u7197\u7198\u7199\u719a\u719b\u719c\u719d\u719e\u719f\u71a0\u71a1\u71a2\u71a3\u71a4\u71a5\u71a6\u71a7\u71a8\u71a9\u71aa\u71ab\u71ac\u71ad\u71ae\u71af\u71b0\u71b1\u71b2\u71b3\u71b4\u71b5\u71b6\u71b7\u71b8\u71b9\u71ba\u71bb\u71bc\u71bd\u71be\u71bf\u71c0\u71c1\u71c2\u71c3\u71c4\u71c5\u71c6\u71c7\u71c8\u71c9\u71ca\u71cb\u71cc\u71cd\u71ce\u71cf\u71d0\u71d1\u71d2\u71d3\u71d4\u71d5\u71d6\u71d7\u71d8\u71d9\u71da\u71db\u71dc\u71dd\u71de\u71df\u71e0\u71e1\u71e2\u71e3\u71e4\u71e5\u71e6\u71e7\u71e8\u71e9\u71ea\u71eb\u71ec\u71ed\u71ee\u71ef\u71f0\u71f1\u71f2\u71f3\u71f4\u71f5\u71f6\u71f7\u71f8\u71f9\u71fa\u71fb\u71fc\u71fd\u71fe\u71ff\u7200\u7201\u7202\u7203\u7204\u7205\u7206\u7207\u7208\u7209\u720a\u720b\u720c\u720d\u720e\u720f\u7210\u7211\u7212\u7213\u7214\u7215\u7216\u7217\u7218\u7219\u721a\u721b\u721c\u721d\u721e\u721f\u7220\u7221\u7222\u7223\u7224\u7225\u7226\u7227\u7228\u7229\u722a\u722b\u722c\u722d\u722e\u722f\u7230\u7231\u7232\u7233\u7234\u7235\u7236\u7237\u7238\u7239\u723a\u723b\u723c\u723d\u723e\u723f\u7240\u7241\u7242\u7243\u7244\u7245\u7246\u7247\u7248\u7249\u724a\u724b\u724c\u724d\u724e\u724f\u7250\u7251\u7252\u7253\u7254\u7255\u7256\u7257\u7258\u7259\u725a\u725b\u725c\u725d\u725e\u725f\u7260\u7261\u7262\u7263\u7264\u7265\u7266\u7267\u7268\u7269\u726a\u726b\u726c\u726d\u726e\u726f\u7270\u7271\u7272\u7273\u7274\u7275\u7276\u7277\u7278\u7279\u727a\u727b\u727c\u727d\u727e\u727f\u7280\u7281\u7282\u7283\u7284\u7285\u7286\u7287\u7288\u7289\u728a\u728b\u728c\u728d\u728e\u728f\u7290\u7291\u7292\u7293\u7294\u7295\u7296\u7297\u7298\u7299\u729a\u729b\u729c\u729d\u729e\u729f\u72a0\u72a1\u72a2\u72a3\u72a4\u72a5\u72a6\u72a7\u72a8\u72a9\u72aa\u72ab\u72ac\u72ad\u72ae\u72af\u72b0\u72b1\u72b2\u72b3\u72b4\u72b5\u72b6\u72b7\u72b8\u72b9\u72ba\u72bb\u72bc\u72bd\u72be\u72bf\u72c0\u72c1\u72c2\u72c3\u72c4\u72c5\u72c6\u72c7\u72c8\u72c9\u72ca\u72cb\u72cc\u72cd\u72ce\u72cf\u72d0\u72d1\u72d2\u72d3\u72d4\u72d5\u72d6\u72d7\u72d8\u72d9\u72da\u72db\u72dc\u72dd\u72de\u72df\u72e0\u72e1\u72e2\u72e3\u72e4\u72e5\u72e6\u72e7\u72e8\u72e9\u72ea\u72eb\u72ec\u72ed\u72ee\u72ef\u72f0\u72f1\u72f2\u72f3\u72f4\u72f5\u72f6\u72f7\u72f8\u72f9\u72fa\u72fb\u72fc\u72fd\u72fe\u72ff\u7300\u7301\u7302\u7303\u7304\u7305\u7306\u7307\u7308\u7309\u730a\u730b\u730c\u730d\u730e\u730f\u7310\u7311\u7312\u7313\u7314\u7315\u7316\u7317\u7318\u7319\u731a\u731b\u731c\u731d\u731e\u731f\u7320\u7321\u7322\u7323\u7324\u7325\u7326\u7327\u7328\u7329\u732a\u732b\u732c\u732d\u732e\u732f\u7330\u7331\u7332\u7333\u7334\u7335\u7336\u7337\u7338\u7339\u733a\u733b\u733c\u733d\u733e\u733f\u7340\u7341\u7342\u7343\u7344\u7345\u7346\u7347\u7348\u7349\u734a\u734b\u734c\u734d\u734e\u734f\u7350\u7351\u7352\u7353\u7354\u7355\u7356\u7357\u7358\u7359\u735a\u735b\u735c\u735d\u735e\u735f\u7360\u7361\u7362\u7363\u7364\u7365\u7366\u7367\u7368\u7369\u736a\u736b\u736c\u736d\u736e\u736f\u7370\u7371\u7372\u7373\u7374\u7375\u7376\u7377\u7378\u7379\u737a\u737b\u737c\u737d\u737e\u737f\u7380\u7381\u7382\u7383\u7384\u7385\u7386\u7387\u7388\u7389\u738a\u738b\u738c\u738d\u738e\u738f\u7390\u7391\u7392\u7393\u7394\u7395\u7396\u7397\u7398\u7399\u739a\u739b\u739c\u739d\u739e\u739f\u73a0\u73a1\u73a2\u73a3\u73a4\u73a5\u73a6\u73a7\u73a8\u73a9\u73aa\u73ab\u73ac\u73ad\u73ae\u73af\u73b0\u73b1\u73b2\u73b3\u73b4\u73b5\u73b6\u73b7\u73b8\u73b9\u73ba\u73bb\u73bc\u73bd\u73be\u73bf\u73c0\u73c1\u73c2\u73c3\u73c4\u73c5\u73c6\u73c7\u73c8\u73c9\u73ca\u73cb\u73cc\u73cd\u73ce\u73cf\u73d0\u73d1\u73d2\u73d3\u73d4\u73d5\u73d6\u73d7\u73d8\u73d9\u73da\u73db\u73dc\u73dd\u73de\u73df\u73e0\u73e1\u73e2\u73e3\u73e4\u73e5\u73e6\u73e7\u73e8\u73e9\u73ea\u73eb\u73ec\u73ed\u73ee\u73ef\u73f0\u73f1\u73f2\u73f3\u73f4\u73f5\u73f6\u73f7\u73f8\u73f9\u73fa\u73fb\u73fc\u73fd\u73fe\u73ff\u7400\u7401\u7402\u7403\u7404\u7405\u7406\u7407\u7408\u7409\u740a\u740b\u740c\u740d\u740e\u740f\u7410\u7411\u7412\u7413\u7414\u7415\u7416\u7417\u7418\u7419\u741a\u741b\u741c\u741d\u741e\u741f\u7420\u7421\u7422\u7423\u7424\u7425\u7426\u7427\u7428\u7429\u742a\u742b\u742c\u742d\u742e\u742f\u7430\u7431\u7432\u7433\u7434\u7435\u7436\u7437\u7438\u7439\u743a\u743b\u743c\u743d\u743e\u743f\u7440\u7441\u7442\u7443\u7444\u7445\u7446\u7447\u7448\u7449\u744a\u744b\u744c\u744d\u744e\u744f\u7450\u7451\u7452\u7453\u7454\u7455\u7456\u7457\u7458\u7459\u745a\u745b\u745c\u745d\u745e\u745f\u7460\u7461\u7462\u7463\u7464\u7465\u7466\u7467\u7468\u7469\u746a\u746b\u746c\u746d\u746e\u746f\u7470\u7471\u7472\u7473\u7474\u7475\u7476\u7477\u7478\u7479\u747a\u747b\u747c\u747d\u747e\u747f\u7480\u7481\u7482\u7483\u7484\u7485\u7486\u7487\u7488\u7489\u748a\u748b\u748c\u748d\u748e\u748f\u7490\u7491\u7492\u7493\u7494\u7495\u7496\u7497\u7498\u7499\u749a\u749b\u749c\u749d\u749e\u749f\u74a0\u74a1\u74a2\u74a3\u74a4\u74a5\u74a6\u74a7\u74a8\u74a9\u74aa\u74ab\u74ac\u74ad\u74ae\u74af\u74b0\u74b1\u74b2\u74b3\u74b4\u74b5\u74b6\u74b7\u74b8\u74b9\u74ba\u74bb\u74bc\u74bd\u74be\u74bf\u74c0\u74c1\u74c2\u74c3\u74c4\u74c5\u74c6\u74c7\u74c8\u74c9\u74ca\u74cb\u74cc\u74cd\u74ce\u74cf\u74d0\u74d1\u74d2\u74d3\u74d4\u74d5\u74d6\u74d7\u74d8\u74d9\u74da\u74db\u74dc\u74dd\u74de\u74df\u74e0\u74e1\u74e2\u74e3\u74e4\u74e5\u74e6\u74e7\u74e8\u74e9\u74ea\u74eb\u74ec\u74ed\u74ee\u74ef\u74f0\u74f1\u74f2\u74f3\u74f4\u74f5\u74f6\u74f7\u74f8\u74f9\u74fa\u74fb\u74fc\u74fd\u74fe\u74ff\u7500\u7501\u7502\u7503\u7504\u7505\u7506\u7507\u7508\u7509\u750a\u750b\u750c\u750d\u750e\u750f\u7510\u7511\u7512\u7513\u7514\u7515\u7516\u7517\u7518\u7519\u751a\u751b\u751c\u751d\u751e\u751f\u7520\u7521\u7522\u7523\u7524\u7525\u7526\u7527\u7528\u7529\u752a\u752b\u752c\u752d\u752e\u752f\u7530\u7531\u7532\u7533\u7534\u7535\u7536\u7537\u7538\u7539\u753a\u753b\u753c\u753d\u753e\u753f\u7540\u7541\u7542\u7543\u7544\u7545\u7546\u7547\u7548\u7549\u754a\u754b\u754c\u754d\u754e\u754f\u7550\u7551\u7552\u7553\u7554\u7555\u7556\u7557\u7558\u7559\u755a\u755b\u755c\u755d\u755e\u755f\u7560\u7561\u7562\u7563\u7564\u7565\u7566\u7567\u7568\u7569\u756a\u756b\u756c\u756d\u756e\u756f\u7570\u7571\u7572\u7573\u7574\u7575\u7576\u7577\u7578\u7579\u757a\u757b\u757c\u757d\u757e\u757f\u7580\u7581\u7582\u7583\u7584\u7585\u7586\u7587\u7588\u7589\u758a\u758b\u758c\u758d\u758e\u758f\u7590\u7591\u7592\u7593\u7594\u7595\u7596\u7597\u7598\u7599\u759a\u759b\u759c\u759d\u759e\u759f\u75a0\u75a1\u75a2\u75a3\u75a4\u75a5\u75a6\u75a7\u75a8\u75a9\u75aa\u75ab\u75ac\u75ad\u75ae\u75af\u75b0\u75b1\u75b2\u75b3\u75b4\u75b5\u75b6\u75b7\u75b8\u75b9\u75ba\u75bb\u75bc\u75bd\u75be\u75bf\u75c0\u75c1\u75c2\u75c3\u75c4\u75c5\u75c6\u75c7\u75c8\u75c9\u75ca\u75cb\u75cc\u75cd\u75ce\u75cf\u75d0\u75d1\u75d2\u75d3\u75d4\u75d5\u75d6\u75d7\u75d8\u75d9\u75da\u75db\u75dc\u75dd\u75de\u75df\u75e0\u75e1\u75e2\u75e3\u75e4\u75e5\u75e6\u75e7\u75e8\u75e9\u75ea\u75eb\u75ec\u75ed\u75ee\u75ef\u75f0\u75f1\u75f2\u75f3\u75f4\u75f5\u75f6\u75f7\u75f8\u75f9\u75fa\u75fb\u75fc\u75fd\u75fe\u75ff\u7600\u7601\u7602\u7603\u7604\u7605\u7606\u7607\u7608\u7609\u760a\u760b\u760c\u760d\u760e\u760f\u7610\u7611\u7612\u7613\u7614\u7615\u7616\u7617\u7618\u7619\u761a\u761b\u761c\u761d\u761e\u761f\u7620\u7621\u7622\u7623\u7624\u7625\u7626\u7627\u7628\u7629\u762a\u762b\u762c\u762d\u762e\u762f\u7630\u7631\u7632\u7633\u7634\u7635\u7636\u7637\u7638\u7639\u763a\u763b\u763c\u763d\u763e\u763f\u7640\u7641\u7642\u7643\u7644\u7645\u7646\u7647\u7648\u7649\u764a\u764b\u764c\u764d\u764e\u764f\u7650\u7651\u7652\u7653\u7654\u7655\u7656\u7657\u7658\u7659\u765a\u765b\u765c\u765d\u765e\u765f\u7660\u7661\u7662\u7663\u7664\u7665\u7666\u7667\u7668\u7669\u766a\u766b\u766c\u766d\u766e\u766f\u7670\u7671\u7672\u7673\u7674\u7675\u7676\u7677\u7678\u7679\u767a\u767b\u767c\u767d\u767e\u767f\u7680\u7681\u7682\u7683\u7684\u7685\u7686\u7687\u7688\u7689\u768a\u768b\u768c\u768d\u768e\u768f\u7690\u7691\u7692\u7693\u7694\u7695\u7696\u7697\u7698\u7699\u769a\u769b\u769c\u769d\u769e\u769f\u76a0\u76a1\u76a2\u76a3\u76a4\u76a5\u76a6\u76a7\u76a8\u76a9\u76aa\u76ab\u76ac\u76ad\u76ae\u76af\u76b0\u76b1\u76b2\u76b3\u76b4\u76b5\u76b6\u76b7\u76b8\u76b9\u76ba\u76bb\u76bc\u76bd\u76be\u76bf\u76c0\u76c1\u76c2\u76c3\u76c4\u76c5\u76c6\u76c7\u76c8\u76c9\u76ca\u76cb\u76cc\u76cd\u76ce\u76cf\u76d0\u76d1\u76d2\u76d3\u76d4\u76d5\u76d6\u76d7\u76d8\u76d9\u76da\u76db\u76dc\u76dd\u76de\u76df\u76e0\u76e1\u76e2\u76e3\u76e4\u76e5\u76e6\u76e7\u76e8\u76e9\u76ea\u76eb\u76ec\u76ed\u76ee\u76ef\u76f0\u76f1\u76f2\u76f3\u76f4\u76f5\u76f6\u76f7\u76f8\u76f9\u76fa\u76fb\u76fc\u76fd\u76fe\u76ff\u7700\u7701\u7702\u7703\u7704\u7705\u7706\u7707\u7708\u7709\u770a\u770b\u770c\u770d\u770e\u770f\u7710\u7711\u7712\u7713\u7714\u7715\u7716\u7717\u7718\u7719\u771a\u771b\u771c\u771d\u771e\u771f\u7720\u7721\u7722\u7723\u7724\u7725\u7726\u7727\u7728\u7729\u772a\u772b\u772c\u772d\u772e\u772f\u7730\u7731\u7732\u7733\u7734\u7735\u7736\u7737\u7738\u7739\u773a\u773b\u773c\u773d\u773e\u773f\u7740\u7741\u7742\u7743\u7744\u7745\u7746\u7747\u7748\u7749\u774a\u774b\u774c\u774d\u774e\u774f\u7750\u7751\u7752\u7753\u7754\u7755\u7756\u7757\u7758\u7759\u775a\u775b\u775c\u775d\u775e\u775f\u7760\u7761\u7762\u7763\u7764\u7765\u7766\u7767\u7768\u7769\u776a\u776b\u776c\u776d\u776e\u776f\u7770\u7771\u7772\u7773\u7774\u7775\u7776\u7777\u7778\u7779\u777a\u777b\u777c\u777d\u777e\u777f\u7780\u7781\u7782\u7783\u7784\u7785\u7786\u7787\u7788\u7789\u778a\u778b\u778c\u778d\u778e\u778f\u7790\u7791\u7792\u7793\u7794\u7795\u7796\u7797\u7798\u7799\u779a\u779b\u779c\u779d\u779e\u779f\u77a0\u77a1\u77a2\u77a3\u77a4\u77a5\u77a6\u77a7\u77a8\u77a9\u77aa\u77ab\u77ac\u77ad\u77ae\u77af\u77b0\u77b1\u77b2\u77b3\u77b4\u77b5\u77b6\u77b7\u77b8\u77b9\u77ba\u77bb\u77bc\u77bd\u77be\u77bf\u77c0\u77c1\u77c2\u77c3\u77c4\u77c5\u77c6\u77c7\u77c8\u77c9\u77ca\u77cb\u77cc\u77cd\u77ce\u77cf\u77d0\u77d1\u77d2\u77d3\u77d4\u77d5\u77d6\u77d7\u77d8\u77d9\u77da\u77db\u77dc\u77dd\u77de\u77df\u77e0\u77e1\u77e2\u77e3\u77e4\u77e5\u77e6\u77e7\u77e8\u77e9\u77ea\u77eb\u77ec\u77ed\u77ee\u77ef\u77f0\u77f1\u77f2\u77f3\u77f4\u77f5\u77f6\u77f7\u77f8\u77f9\u77fa\u77fb\u77fc\u77fd\u77fe\u77ff\u7800\u7801\u7802\u7803\u7804\u7805\u7806\u7807\u7808\u7809\u780a\u780b\u780c\u780d\u780e\u780f\u7810\u7811\u7812\u7813\u7814\u7815\u7816\u7817\u7818\u7819\u781a\u781b\u781c\u781d\u781e\u781f\u7820\u7821\u7822\u7823\u7824\u7825\u7826\u7827\u7828\u7829\u782a\u782b\u782c\u782d\u782e\u782f\u7830\u7831\u7832\u7833\u7834\u7835\u7836\u7837\u7838\u7839\u783a\u783b\u783c\u783d\u783e\u783f\u7840\u7841\u7842\u7843\u7844\u7845\u7846\u7847\u7848\u7849\u784a\u784b\u784c\u784d\u784e\u784f\u7850\u7851\u7852\u7853\u7854\u7855\u7856\u7857\u7858\u7859\u785a\u785b\u785c\u785d\u785e\u785f\u7860\u7861\u7862\u7863\u7864\u7865\u7866\u7867\u7868\u7869\u786a\u786b\u786c\u786d\u786e\u786f\u7870\u7871\u7872\u7873\u7874\u7875\u7876\u7877\u7878\u7879\u787a\u787b\u787c\u787d\u787e\u787f\u7880\u7881\u7882\u7883\u7884\u7885\u7886\u7887\u7888\u7889\u788a\u788b\u788c\u788d\u788e\u788f\u7890\u7891\u7892\u7893\u7894\u7895\u7896\u7897\u7898\u7899\u789a\u789b\u789c\u789d\u789e\u789f\u78a0\u78a1\u78a2\u78a3\u78a4\u78a5\u78a6\u78a7\u78a8\u78a9\u78aa\u78ab\u78ac\u78ad\u78ae\u78af\u78b0\u78b1\u78b2\u78b3\u78b4\u78b5\u78b6\u78b7\u78b8\u78b9\u78ba\u78bb\u78bc\u78bd\u78be\u78bf\u78c0\u78c1\u78c2\u78c3\u78c4\u78c5\u78c6\u78c7\u78c8\u78c9\u78ca\u78cb\u78cc\u78cd\u78ce\u78cf\u78d0\u78d1\u78d2\u78d3\u78d4\u78d5\u78d6\u78d7\u78d8\u78d9\u78da\u78db\u78dc\u78dd\u78de\u78df\u78e0\u78e1\u78e2\u78e3\u78e4\u78e5\u78e6\u78e7\u78e8\u78e9\u78ea\u78eb\u78ec\u78ed\u78ee\u78ef\u78f0\u78f1\u78f2\u78f3\u78f4\u78f5\u78f6\u78f7\u78f8\u78f9\u78fa\u78fb\u78fc\u78fd\u78fe\u78ff\u7900\u7901\u7902\u7903\u7904\u7905\u7906\u7907\u7908\u7909\u790a\u790b\u790c\u790d\u790e\u790f\u7910\u7911\u7912\u7913\u7914\u7915\u7916\u7917\u7918\u7919\u791a\u791b\u791c\u791d\u791e\u791f\u7920\u7921\u7922\u7923\u7924\u7925\u7926\u7927\u7928\u7929\u792a\u792b\u792c\u792d\u792e\u792f\u7930\u7931\u7932\u7933\u7934\u7935\u7936\u7937\u7938\u7939\u793a\u793b\u793c\u793d\u793e\u793f\u7940\u7941\u7942\u7943\u7944\u7945\u7946\u7947\u7948\u7949\u794a\u794b\u794c\u794d\u794e\u794f\u7950\u7951\u7952\u7953\u7954\u7955\u7956\u7957\u7958\u7959\u795a\u795b\u795c\u795d\u795e\u795f\u7960\u7961\u7962\u7963\u7964\u7965\u7966\u7967\u7968\u7969\u796a\u796b\u796c\u796d\u796e\u796f\u7970\u7971\u7972\u7973\u7974\u7975\u7976\u7977\u7978\u7979\u797a\u797b\u797c\u797d\u797e\u797f\u7980\u7981\u7982\u7983\u7984\u7985\u7986\u7987\u7988\u7989\u798a\u798b\u798c\u798d\u798e\u798f\u7990\u7991\u7992\u7993\u7994\u7995\u7996\u7997\u7998\u7999\u799a\u799b\u799c\u799d\u799e\u799f\u79a0\u79a1\u79a2\u79a3\u79a4\u79a5\u79a6\u79a7\u79a8\u79a9\u79aa\u79ab\u79ac\u79ad\u79ae\u79af\u79b0\u79b1\u79b2\u79b3\u79b4\u79b5\u79b6\u79b7\u79b8\u79b9\u79ba\u79bb\u79bc\u79bd\u79be\u79bf\u79c0\u79c1\u79c2\u79c3\u79c4\u79c5\u79c6\u79c7\u79c8\u79c9\u79ca\u79cb\u79cc\u79cd\u79ce\u79cf\u79d0\u79d1\u79d2\u79d3\u79d4\u79d5\u79d6\u79d7\u79d8\u79d9\u79da\u79db\u79dc\u79dd\u79de\u79df\u79e0\u79e1\u79e2\u79e3\u79e4\u79e5\u79e6\u79e7\u79e8\u79e9\u79ea\u79eb\u79ec\u79ed\u79ee\u79ef\u79f0\u79f1\u79f2\u79f3\u79f4\u79f5\u79f6\u79f7\u79f8\u79f9\u79fa\u79fb\u79fc\u79fd\u79fe\u79ff\u7a00\u7a01\u7a02\u7a03\u7a04\u7a05\u7a06\u7a07\u7a08\u7a09\u7a0a\u7a0b\u7a0c\u7a0d\u7a0e\u7a0f\u7a10\u7a11\u7a12\u7a13\u7a14\u7a15\u7a16\u7a17\u7a18\u7a19\u7a1a\u7a1b\u7a1c\u7a1d\u7a1e\u7a1f\u7a20\u7a21\u7a22\u7a23\u7a24\u7a25\u7a26\u7a27\u7a28\u7a29\u7a2a\u7a2b\u7a2c\u7a2d\u7a2e\u7a2f\u7a30\u7a31\u7a32\u7a33\u7a34\u7a35\u7a36\u7a37\u7a38\u7a39\u7a3a\u7a3b\u7a3c\u7a3d\u7a3e\u7a3f\u7a40\u7a41\u7a42\u7a43\u7a44\u7a45\u7a46\u7a47\u7a48\u7a49\u7a4a\u7a4b\u7a4c\u7a4d\u7a4e\u7a4f\u7a50\u7a51\u7a52\u7a53\u7a54\u7a55\u7a56\u7a57\u7a58\u7a59\u7a5a\u7a5b\u7a5c\u7a5d\u7a5e\u7a5f\u7a60\u7a61\u7a62\u7a63\u7a64\u7a65\u7a66\u7a67\u7a68\u7a69\u7a6a\u7a6b\u7a6c\u7a6d\u7a6e\u7a6f\u7a70\u7a71\u7a72\u7a73\u7a74\u7a75\u7a76\u7a77\u7a78\u7a79\u7a7a\u7a7b\u7a7c\u7a7d\u7a7e\u7a7f\u7a80\u7a81\u7a82\u7a83\u7a84\u7a85\u7a86\u7a87\u7a88\u7a89\u7a8a\u7a8b\u7a8c\u7a8d\u7a8e\u7a8f\u7a90\u7a91\u7a92\u7a93\u7a94\u7a95\u7a96\u7a97\u7a98\u7a99\u7a9a\u7a9b\u7a9c\u7a9d\u7a9e\u7a9f\u7aa0\u7aa1\u7aa2\u7aa3\u7aa4\u7aa5\u7aa6\u7aa7\u7aa8\u7aa9\u7aaa\u7aab\u7aac\u7aad\u7aae\u7aaf\u7ab0\u7ab1\u7ab2\u7ab3\u7ab4\u7ab5\u7ab6\u7ab7\u7ab8\u7ab9\u7aba\u7abb\u7abc\u7abd\u7abe\u7abf\u7ac0\u7ac1\u7ac2\u7ac3\u7ac4\u7ac5\u7ac6\u7ac7\u7ac8\u7ac9\u7aca\u7acb\u7acc\u7acd\u7ace\u7acf\u7ad0\u7ad1\u7ad2\u7ad3\u7ad4\u7ad5\u7ad6\u7ad7\u7ad8\u7ad9\u7ada\u7adb\u7adc\u7add\u7ade\u7adf\u7ae0\u7ae1\u7ae2\u7ae3\u7ae4\u7ae5\u7ae6\u7ae7\u7ae8\u7ae9\u7aea\u7aeb\u7aec\u7aed\u7aee\u7aef\u7af0\u7af1\u7af2\u7af3\u7af4\u7af5\u7af6\u7af7\u7af8\u7af9\u7afa\u7afb\u7afc\u7afd\u7afe\u7aff\u7b00\u7b01\u7b02\u7b03\u7b04\u7b05\u7b06\u7b07\u7b08\u7b09\u7b0a\u7b0b\u7b0c\u7b0d\u7b0e\u7b0f\u7b10\u7b11\u7b12\u7b13\u7b14\u7b15\u7b16\u7b17\u7b18\u7b19\u7b1a\u7b1b\u7b1c\u7b1d\u7b1e\u7b1f\u7b20\u7b21\u7b22\u7b23\u7b24\u7b25\u7b26\u7b27\u7b28\u7b29\u7b2a\u7b2b\u7b2c\u7b2d\u7b2e\u7b2f\u7b30\u7b31\u7b32\u7b33\u7b34\u7b35\u7b36\u7b37\u7b38\u7b39\u7b3a\u7b3b\u7b3c\u7b3d\u7b3e\u7b3f\u7b40\u7b41\u7b42\u7b43\u7b44\u7b45\u7b46\u7b47\u7b48\u7b49\u7b4a\u7b4b\u7b4c\u7b4d\u7b4e\u7b4f\u7b50\u7b51\u7b52\u7b53\u7b54\u7b55\u7b56\u7b57\u7b58\u7b59\u7b5a\u7b5b\u7b5c\u7b5d\u7b5e\u7b5f\u7b60\u7b61\u7b62\u7b63\u7b64\u7b65\u7b66\u7b67\u7b68\u7b69\u7b6a\u7b6b\u7b6c\u7b6d\u7b6e\u7b6f\u7b70\u7b71\u7b72\u7b73\u7b74\u7b75\u7b76\u7b77\u7b78\u7b79\u7b7a\u7b7b\u7b7c\u7b7d\u7b7e\u7b7f\u7b80\u7b81\u7b82\u7b83\u7b84\u7b85\u7b86\u7b87\u7b88\u7b89\u7b8a\u7b8b\u7b8c\u7b8d\u7b8e\u7b8f\u7b90\u7b91\u7b92\u7b93\u7b94\u7b95\u7b96\u7b97\u7b98\u7b99\u7b9a\u7b9b\u7b9c\u7b9d\u7b9e\u7b9f\u7ba0\u7ba1\u7ba2\u7ba3\u7ba4\u7ba5\u7ba6\u7ba7\u7ba8\u7ba9\u7baa\u7bab\u7bac\u7bad\u7bae\u7baf\u7bb0\u7bb1\u7bb2\u7bb3\u7bb4\u7bb5\u7bb6\u7bb7\u7bb8\u7bb9\u7bba\u7bbb\u7bbc\u7bbd\u7bbe\u7bbf\u7bc0\u7bc1\u7bc2\u7bc3\u7bc4\u7bc5\u7bc6\u7bc7\u7bc8\u7bc9\u7bca\u7bcb\u7bcc\u7bcd\u7bce\u7bcf\u7bd0\u7bd1\u7bd2\u7bd3\u7bd4\u7bd5\u7bd6\u7bd7\u7bd8\u7bd9\u7bda\u7bdb\u7bdc\u7bdd\u7bde\u7bdf\u7be0\u7be1\u7be2\u7be3\u7be4\u7be5\u7be6\u7be7\u7be8\u7be9\u7bea\u7beb\u7bec\u7bed\u7bee\u7bef\u7bf0\u7bf1\u7bf2\u7bf3\u7bf4\u7bf5\u7bf6\u7bf7\u7bf8\u7bf9\u7bfa\u7bfb\u7bfc\u7bfd\u7bfe\u7bff\u7c00\u7c01\u7c02\u7c03\u7c04\u7c05\u7c06\u7c07\u7c08\u7c09\u7c0a\u7c0b\u7c0c\u7c0d\u7c0e\u7c0f\u7c10\u7c11\u7c12\u7c13\u7c14\u7c15\u7c16\u7c17\u7c18\u7c19\u7c1a\u7c1b\u7c1c\u7c1d\u7c1e\u7c1f\u7c20\u7c21\u7c22\u7c23\u7c24\u7c25\u7c26\u7c27\u7c28\u7c29\u7c2a\u7c2b\u7c2c\u7c2d\u7c2e\u7c2f\u7c30\u7c31\u7c32\u7c33\u7c34\u7c35\u7c36\u7c37\u7c38\u7c39\u7c3a\u7c3b\u7c3c\u7c3d\u7c3e\u7c3f\u7c40\u7c41\u7c42\u7c43\u7c44\u7c45\u7c46\u7c47\u7c48\u7c49\u7c4a\u7c4b\u7c4c\u7c4d\u7c4e\u7c4f\u7c50\u7c51\u7c52\u7c53\u7c54\u7c55\u7c56\u7c57\u7c58\u7c59\u7c5a\u7c5b\u7c5c\u7c5d\u7c5e\u7c5f\u7c60\u7c61\u7c62\u7c63\u7c64\u7c65\u7c66\u7c67\u7c68\u7c69\u7c6a\u7c6b\u7c6c\u7c6d\u7c6e\u7c6f\u7c70\u7c71\u7c72\u7c73\u7c74\u7c75\u7c76\u7c77\u7c78\u7c79\u7c7a\u7c7b\u7c7c\u7c7d\u7c7e\u7c7f\u7c80\u7c81\u7c82\u7c83\u7c84\u7c85\u7c86\u7c87\u7c88\u7c89\u7c8a\u7c8b\u7c8c\u7c8d\u7c8e\u7c8f\u7c90\u7c91\u7c92\u7c93\u7c94\u7c95\u7c96\u7c97\u7c98\u7c99\u7c9a\u7c9b\u7c9c\u7c9d\u7c9e\u7c9f\u7ca0\u7ca1\u7ca2\u7ca3\u7ca4\u7ca5\u7ca6\u7ca7\u7ca8\u7ca9\u7caa\u7cab\u7cac\u7cad\u7cae\u7caf\u7cb0\u7cb1\u7cb2\u7cb3\u7cb4\u7cb5\u7cb6\u7cb7\u7cb8\u7cb9\u7cba\u7cbb\u7cbc\u7cbd\u7cbe\u7cbf\u7cc0\u7cc1\u7cc2\u7cc3\u7cc4\u7cc5\u7cc6\u7cc7\u7cc8\u7cc9\u7cca\u7ccb\u7ccc\u7ccd\u7cce\u7ccf\u7cd0\u7cd1\u7cd2\u7cd3\u7cd4\u7cd5\u7cd6\u7cd7\u7cd8\u7cd9\u7cda\u7cdb\u7cdc\u7cdd\u7cde\u7cdf\u7ce0\u7ce1\u7ce2\u7ce3\u7ce4\u7ce5\u7ce6\u7ce7\u7ce8\u7ce9\u7cea\u7ceb\u7cec\u7ced\u7cee\u7cef\u7cf0\u7cf1\u7cf2\u7cf3\u7cf4\u7cf5\u7cf6\u7cf7\u7cf8\u7cf9\u7cfa\u7cfb\u7cfc\u7cfd\u7cfe\u7cff\u7d00\u7d01\u7d02\u7d03\u7d04\u7d05\u7d06\u7d07\u7d08\u7d09\u7d0a\u7d0b\u7d0c\u7d0d\u7d0e\u7d0f\u7d10\u7d11\u7d12\u7d13\u7d14\u7d15\u7d16\u7d17\u7d18\u7d19\u7d1a\u7d1b\u7d1c\u7d1d\u7d1e\u7d1f\u7d20\u7d21\u7d22\u7d23\u7d24\u7d25\u7d26\u7d27\u7d28\u7d29\u7d2a\u7d2b\u7d2c\u7d2d\u7d2e\u7d2f\u7d30\u7d31\u7d32\u7d33\u7d34\u7d35\u7d36\u7d37\u7d38\u7d39\u7d3a\u7d3b\u7d3c\u7d3d\u7d3e\u7d3f\u7d40\u7d41\u7d42\u7d43\u7d44\u7d45\u7d46\u7d47\u7d48\u7d49\u7d4a\u7d4b\u7d4c\u7d4d\u7d4e\u7d4f\u7d50\u7d51\u7d52\u7d53\u7d54\u7d55\u7d56\u7d57\u7d58\u7d59\u7d5a\u7d5b\u7d5c\u7d5d\u7d5e\u7d5f\u7d60\u7d61\u7d62\u7d63\u7d64\u7d65\u7d66\u7d67\u7d68\u7d69\u7d6a\u7d6b\u7d6c\u7d6d\u7d6e\u7d6f\u7d70\u7d71\u7d72\u7d73\u7d74\u7d75\u7d76\u7d77\u7d78\u7d79\u7d7a\u7d7b\u7d7c\u7d7d\u7d7e\u7d7f\u7d80\u7d81\u7d82\u7d83\u7d84\u7d85\u7d86\u7d87\u7d88\u7d89\u7d8a\u7d8b\u7d8c\u7d8d\u7d8e\u7d8f\u7d90\u7d91\u7d92\u7d93\u7d94\u7d95\u7d96\u7d97\u7d98\u7d99\u7d9a\u7d9b\u7d9c\u7d9d\u7d9e\u7d9f\u7da0\u7da1\u7da2\u7da3\u7da4\u7da5\u7da6\u7da7\u7da8\u7da9\u7daa\u7dab\u7dac\u7dad\u7dae\u7daf\u7db0\u7db1\u7db2\u7db3\u7db4\u7db5\u7db6\u7db7\u7db8\u7db9\u7dba\u7dbb\u7dbc\u7dbd\u7dbe\u7dbf\u7dc0\u7dc1\u7dc2\u7dc3\u7dc4\u7dc5\u7dc6\u7dc7\u7dc8\u7dc9\u7dca\u7dcb\u7dcc\u7dcd\u7dce\u7dcf\u7dd0\u7dd1\u7dd2\u7dd3\u7dd4\u7dd5\u7dd6\u7dd7\u7dd8\u7dd9\u7dda\u7ddb\u7ddc\u7ddd\u7dde\u7ddf\u7de0\u7de1\u7de2\u7de3\u7de4\u7de5\u7de6\u7de7\u7de8\u7de9\u7dea\u7deb\u7dec\u7ded\u7dee\u7def\u7df0\u7df1\u7df2\u7df3\u7df4\u7df5\u7df6\u7df7\u7df8\u7df9\u7dfa\u7dfb\u7dfc\u7dfd\u7dfe\u7dff\u7e00\u7e01\u7e02\u7e03\u7e04\u7e05\u7e06\u7e07\u7e08\u7e09\u7e0a\u7e0b\u7e0c\u7e0d\u7e0e\u7e0f\u7e10\u7e11\u7e12\u7e13\u7e14\u7e15\u7e16\u7e17\u7e18\u7e19\u7e1a\u7e1b\u7e1c\u7e1d\u7e1e\u7e1f\u7e20\u7e21\u7e22\u7e23\u7e24\u7e25\u7e26\u7e27\u7e28\u7e29\u7e2a\u7e2b\u7e2c\u7e2d\u7e2e\u7e2f\u7e30\u7e31\u7e32\u7e33\u7e34\u7e35\u7e36\u7e37\u7e38\u7e39\u7e3a\u7e3b\u7e3c\u7e3d\u7e3e\u7e3f\u7e40\u7e41\u7e42\u7e43\u7e44\u7e45\u7e46\u7e47\u7e48\u7e49\u7e4a\u7e4b\u7e4c\u7e4d\u7e4e\u7e4f\u7e50\u7e51\u7e52\u7e53\u7e54\u7e55\u7e56\u7e57\u7e58\u7e59\u7e5a\u7e5b\u7e5c\u7e5d\u7e5e\u7e5f\u7e60\u7e61\u7e62\u7e63\u7e64\u7e65\u7e66\u7e67\u7e68\u7e69\u7e6a\u7e6b\u7e6c\u7e6d\u7e6e\u7e6f\u7e70\u7e71\u7e72\u7e73\u7e74\u7e75\u7e76\u7e77\u7e78\u7e79\u7e7a\u7e7b\u7e7c\u7e7d\u7e7e\u7e7f\u7e80\u7e81\u7e82\u7e83\u7e84\u7e85\u7e86\u7e87\u7e88\u7e89\u7e8a\u7e8b\u7e8c\u7e8d\u7e8e\u7e8f\u7e90\u7e91\u7e92\u7e93\u7e94\u7e95\u7e96\u7e97\u7e98\u7e99\u7e9a\u7e9b\u7e9c\u7e9d\u7e9e\u7e9f\u7ea0\u7ea1\u7ea2\u7ea3\u7ea4\u7ea5\u7ea6\u7ea7\u7ea8\u7ea9\u7eaa\u7eab\u7eac\u7ead\u7eae\u7eaf\u7eb0\u7eb1\u7eb2\u7eb3\u7eb4\u7eb5\u7eb6\u7eb7\u7eb8\u7eb9\u7eba\u7ebb\u7ebc\u7ebd\u7ebe\u7ebf\u7ec0\u7ec1\u7ec2\u7ec3\u7ec4\u7ec5\u7ec6\u7ec7\u7ec8\u7ec9\u7eca\u7ecb\u7ecc\u7ecd\u7ece\u7ecf\u7ed0\u7ed1\u7ed2\u7ed3\u7ed4\u7ed5\u7ed6\u7ed7\u7ed8\u7ed9\u7eda\u7edb\u7edc\u7edd\u7ede\u7edf\u7ee0\u7ee1\u7ee2\u7ee3\u7ee4\u7ee5\u7ee6\u7ee7\u7ee8\u7ee9\u7eea\u7eeb\u7eec\u7eed\u7eee\u7eef\u7ef0\u7ef1\u7ef2\u7ef3\u7ef4\u7ef5\u7ef6\u7ef7\u7ef8\u7ef9\u7efa\u7efb\u7efc\u7efd\u7efe\u7eff\u7f00\u7f01\u7f02\u7f03\u7f04\u7f05\u7f06\u7f07\u7f08\u7f09\u7f0a\u7f0b\u7f0c\u7f0d\u7f0e\u7f0f\u7f10\u7f11\u7f12\u7f13\u7f14\u7f15\u7f16\u7f17\u7f18\u7f19\u7f1a\u7f1b\u7f1c\u7f1d\u7f1e\u7f1f\u7f20\u7f21\u7f22\u7f23\u7f24\u7f25\u7f26\u7f27\u7f28\u7f29\u7f2a\u7f2b\u7f2c\u7f2d\u7f2e\u7f2f\u7f30\u7f31\u7f32\u7f33\u7f34\u7f35\u7f36\u7f37\u7f38\u7f39\u7f3a\u7f3b\u7f3c\u7f3d\u7f3e\u7f3f\u7f40\u7f41\u7f42\u7f43\u7f44\u7f45\u7f46\u7f47\u7f48\u7f49\u7f4a\u7f4b\u7f4c\u7f4d\u7f4e\u7f4f\u7f50\u7f51\u7f52\u7f53\u7f54\u7f55\u7f56\u7f57\u7f58\u7f59\u7f5a\u7f5b\u7f5c\u7f5d\u7f5e\u7f5f\u7f60\u7f61\u7f62\u7f63\u7f64\u7f65\u7f66\u7f67\u7f68\u7f69\u7f6a\u7f6b\u7f6c\u7f6d\u7f6e\u7f6f\u7f70\u7f71\u7f72\u7f73\u7f74\u7f75\u7f76\u7f77\u7f78\u7f79\u7f7a\u7f7b\u7f7c\u7f7d\u7f7e\u7f7f\u7f80\u7f81\u7f82\u7f83\u7f84\u7f85\u7f86\u7f87\u7f88\u7f89\u7f8a\u7f8b\u7f8c\u7f8d\u7f8e\u7f8f\u7f90\u7f91\u7f92\u7f93\u7f94\u7f95\u7f96\u7f97\u7f98\u7f99\u7f9a\u7f9b\u7f9c\u7f9d\u7f9e\u7f9f\u7fa0\u7fa1\u7fa2\u7fa3\u7fa4\u7fa5\u7fa6\u7fa7\u7fa8\u7fa9\u7faa\u7fab\u7fac\u7fad\u7fae\u7faf\u7fb0\u7fb1\u7fb2\u7fb3\u7fb4\u7fb5\u7fb6\u7fb7\u7fb8\u7fb9\u7fba\u7fbb\u7fbc\u7fbd\u7fbe\u7fbf\u7fc0\u7fc1\u7fc2\u7fc3\u7fc4\u7fc5\u7fc6\u7fc7\u7fc8\u7fc9\u7fca\u7fcb\u7fcc\u7fcd\u7fce\u7fcf\u7fd0\u7fd1\u7fd2\u7fd3\u7fd4\u7fd5\u7fd6\u7fd7\u7fd8\u7fd9\u7fda\u7fdb\u7fdc\u7fdd\u7fde\u7fdf\u7fe0\u7fe1\u7fe2\u7fe3\u7fe4\u7fe5\u7fe6\u7fe7\u7fe8\u7fe9\u7fea\u7feb\u7fec\u7fed\u7fee\u7fef\u7ff0\u7ff1\u7ff2\u7ff3\u7ff4\u7ff5\u7ff6\u7ff7\u7ff8\u7ff9\u7ffa\u7ffb\u7ffc\u7ffd\u7ffe\u7fff\u8000\u8001\u8002\u8003\u8004\u8005\u8006\u8007\u8008\u8009\u800a\u800b\u800c\u800d\u800e\u800f\u8010\u8011\u8012\u8013\u8014\u8015\u8016\u8017\u8018\u8019\u801a\u801b\u801c\u801d\u801e\u801f\u8020\u8021\u8022\u8023\u8024\u8025\u8026\u8027\u8028\u8029\u802a\u802b\u802c\u802d\u802e\u802f\u8030\u8031\u8032\u8033\u8034\u8035\u8036\u8037\u8038\u8039\u803a\u803b\u803c\u803d\u803e\u803f\u8040\u8041\u8042\u8043\u8044\u8045\u8046\u8047\u8048\u8049\u804a\u804b\u804c\u804d\u804e\u804f\u8050\u8051\u8052\u8053\u8054\u8055\u8056\u8057\u8058\u8059\u805a\u805b\u805c\u805d\u805e\u805f\u8060\u8061\u8062\u8063\u8064\u8065\u8066\u8067\u8068\u8069\u806a\u806b\u806c\u806d\u806e\u806f\u8070\u8071\u8072\u8073\u8074\u8075\u8076\u8077\u8078\u8079\u807a\u807b\u807c\u807d\u807e\u807f\u8080\u8081\u8082\u8083\u8084\u8085\u8086\u8087\u8088\u8089\u808a\u808b\u808c\u808d\u808e\u808f\u8090\u8091\u8092\u8093\u8094\u8095\u8096\u8097\u8098\u8099\u809a\u809b\u809c\u809d\u809e\u809f\u80a0\u80a1\u80a2\u80a3\u80a4\u80a5\u80a6\u80a7\u80a8\u80a9\u80aa\u80ab\u80ac\u80ad\u80ae\u80af\u80b0\u80b1\u80b2\u80b3\u80b4\u80b5\u80b6\u80b7\u80b8\u80b9\u80ba\u80bb\u80bc\u80bd\u80be\u80bf\u80c0\u80c1\u80c2\u80c3\u80c4\u80c5\u80c6\u80c7\u80c8\u80c9\u80ca\u80cb\u80cc\u80cd\u80ce\u80cf\u80d0\u80d1\u80d2\u80d3\u80d4\u80d5\u80d6\u80d7\u80d8\u80d9\u80da\u80db\u80dc\u80dd\u80de\u80df\u80e0\u80e1\u80e2\u80e3\u80e4\u80e5\u80e6\u80e7\u80e8\u80e9\u80ea\u80eb\u80ec\u80ed\u80ee\u80ef\u80f0\u80f1\u80f2\u80f3\u80f4\u80f5\u80f6\u80f7\u80f8\u80f9\u80fa\u80fb\u80fc\u80fd\u80fe\u80ff\u8100\u8101\u8102\u8103\u8104\u8105\u8106\u8107\u8108\u8109\u810a\u810b\u810c\u810d\u810e\u810f\u8110\u8111\u8112\u8113\u8114\u8115\u8116\u8117\u8118\u8119\u811a\u811b\u811c\u811d\u811e\u811f\u8120\u8121\u8122\u8123\u8124\u8125\u8126\u8127\u8128\u8129\u812a\u812b\u812c\u812d\u812e\u812f\u8130\u8131\u8132\u8133\u8134\u8135\u8136\u8137\u8138\u8139\u813a\u813b\u813c\u813d\u813e\u813f\u8140\u8141\u8142\u8143\u8144\u8145\u8146\u8147\u8148\u8149\u814a\u814b\u814c\u814d\u814e\u814f\u8150\u8151\u8152\u8153\u8154\u8155\u8156\u8157\u8158\u8159\u815a\u815b\u815c\u815d\u815e\u815f\u8160\u8161\u8162\u8163\u8164\u8165\u8166\u8167\u8168\u8169\u816a\u816b\u816c\u816d\u816e\u816f\u8170\u8171\u8172\u8173\u8174\u8175\u8176\u8177\u8178\u8179\u817a\u817b\u817c\u817d\u817e\u817f\u8180\u8181\u8182\u8183\u8184\u8185\u8186\u8187\u8188\u8189\u818a\u818b\u818c\u818d\u818e\u818f\u8190\u8191\u8192\u8193\u8194\u8195\u8196\u8197\u8198\u8199\u819a\u819b\u819c\u819d\u819e\u819f\u81a0\u81a1\u81a2\u81a3\u81a4\u81a5\u81a6\u81a7\u81a8\u81a9\u81aa\u81ab\u81ac\u81ad\u81ae\u81af\u81b0\u81b1\u81b2\u81b3\u81b4\u81b5\u81b6\u81b7\u81b8\u81b9\u81ba\u81bb\u81bc\u81bd\u81be\u81bf\u81c0\u81c1\u81c2\u81c3\u81c4\u81c5\u81c6\u81c7\u81c8\u81c9\u81ca\u81cb\u81cc\u81cd\u81ce\u81cf\u81d0\u81d1\u81d2\u81d3\u81d4\u81d5\u81d6\u81d7\u81d8\u81d9\u81da\u81db\u81dc\u81dd\u81de\u81df\u81e0\u81e1\u81e2\u81e3\u81e4\u81e5\u81e6\u81e7\u81e8\u81e9\u81ea\u81eb\u81ec\u81ed\u81ee\u81ef\u81f0\u81f1\u81f2\u81f3\u81f4\u81f5\u81f6\u81f7\u81f8\u81f9\u81fa\u81fb\u81fc\u81fd\u81fe\u81ff\u8200\u8201\u8202\u8203\u8204\u8205\u8206\u8207\u8208\u8209\u820a\u820b\u820c\u820d\u820e\u820f\u8210\u8211\u8212\u8213\u8214\u8215\u8216\u8217\u8218\u8219\u821a\u821b\u821c\u821d\u821e\u821f\u8220\u8221\u8222\u8223\u8224\u8225\u8226\u8227\u8228\u8229\u822a\u822b\u822c\u822d\u822e\u822f\u8230\u8231\u8232\u8233\u8234\u8235\u8236\u8237\u8238\u8239\u823a\u823b\u823c\u823d\u823e\u823f\u8240\u8241\u8242\u8243\u8244\u8245\u8246\u8247\u8248\u8249\u824a\u824b\u824c\u824d\u824e\u824f\u8250\u8251\u8252\u8253\u8254\u8255\u8256\u8257\u8258\u8259\u825a\u825b\u825c\u825d\u825e\u825f\u8260\u8261\u8262\u8263\u8264\u8265\u8266\u8267\u8268\u8269\u826a\u826b\u826c\u826d\u826e\u826f\u8270\u8271\u8272\u8273\u8274\u8275\u8276\u8277\u8278\u8279\u827a\u827b\u827c\u827d\u827e\u827f\u8280\u8281\u8282\u8283\u8284\u8285\u8286\u8287\u8288\u8289\u828a\u828b\u828c\u828d\u828e\u828f\u8290\u8291\u8292\u8293\u8294\u8295\u8296\u8297\u8298\u8299\u829a\u829b\u829c\u829d\u829e\u829f\u82a0\u82a1\u82a2\u82a3\u82a4\u82a5\u82a6\u82a7\u82a8\u82a9\u82aa\u82ab\u82ac\u82ad\u82ae\u82af\u82b0\u82b1\u82b2\u82b3\u82b4\u82b5\u82b6\u82b7\u82b8\u82b9\u82ba\u82bb\u82bc\u82bd\u82be\u82bf\u82c0\u82c1\u82c2\u82c3\u82c4\u82c5\u82c6\u82c7\u82c8\u82c9\u82ca\u82cb\u82cc\u82cd\u82ce\u82cf\u82d0\u82d1\u82d2\u82d3\u82d4\u82d5\u82d6\u82d7\u82d8\u82d9\u82da\u82db\u82dc\u82dd\u82de\u82df\u82e0\u82e1\u82e2\u82e3\u82e4\u82e5\u82e6\u82e7\u82e8\u82e9\u82ea\u82eb\u82ec\u82ed\u82ee\u82ef\u82f0\u82f1\u82f2\u82f3\u82f4\u82f5\u82f6\u82f7\u82f8\u82f9\u82fa\u82fb\u82fc\u82fd\u82fe\u82ff\u8300\u8301\u8302\u8303\u8304\u8305\u8306\u8307\u8308\u8309\u830a\u830b\u830c\u830d\u830e\u830f\u8310\u8311\u8312\u8313\u8314\u8315\u8316\u8317\u8318\u8319\u831a\u831b\u831c\u831d\u831e\u831f\u8320\u8321\u8322\u8323\u8324\u8325\u8326\u8327\u8328\u8329\u832a\u832b\u832c\u832d\u832e\u832f\u8330\u8331\u8332\u8333\u8334\u8335\u8336\u8337\u8338\u8339\u833a\u833b\u833c\u833d\u833e\u833f\u8340\u8341\u8342\u8343\u8344\u8345\u8346\u8347\u8348\u8349\u834a\u834b\u834c\u834d\u834e\u834f\u8350\u8351\u8352\u8353\u8354\u8355\u8356\u8357\u8358\u8359\u835a\u835b\u835c\u835d\u835e\u835f\u8360\u8361\u8362\u8363\u8364\u8365\u8366\u8367\u8368\u8369\u836a\u836b\u836c\u836d\u836e\u836f\u8370\u8371\u8372\u8373\u8374\u8375\u8376\u8377\u8378\u8379\u837a\u837b\u837c\u837d\u837e\u837f\u8380\u8381\u8382\u8383\u8384\u8385\u8386\u8387\u8388\u8389\u838a\u838b\u838c\u838d\u838e\u838f\u8390\u8391\u8392\u8393\u8394\u8395\u8396\u8397\u8398\u8399\u839a\u839b\u839c\u839d\u839e\u839f\u83a0\u83a1\u83a2\u83a3\u83a4\u83a5\u83a6\u83a7\u83a8\u83a9\u83aa\u83ab\u83ac\u83ad\u83ae\u83af\u83b0\u83b1\u83b2\u83b3\u83b4\u83b5\u83b6\u83b7\u83b8\u83b9\u83ba\u83bb\u83bc\u83bd\u83be\u83bf\u83c0\u83c1\u83c2\u83c3\u83c4\u83c5\u83c6\u83c7\u83c8\u83c9\u83ca\u83cb\u83cc\u83cd\u83ce\u83cf\u83d0\u83d1\u83d2\u83d3\u83d4\u83d5\u83d6\u83d7\u83d8\u83d9\u83da\u83db\u83dc\u83dd\u83de\u83df\u83e0\u83e1\u83e2\u83e3\u83e4\u83e5\u83e6\u83e7\u83e8\u83e9\u83ea\u83eb\u83ec\u83ed\u83ee\u83ef\u83f0\u83f1\u83f2\u83f3\u83f4\u83f5\u83f6\u83f7\u83f8\u83f9\u83fa\u83fb\u83fc\u83fd\u83fe\u83ff\u8400\u8401\u8402\u8403\u8404\u8405\u8406\u8407\u8408\u8409\u840a\u840b\u840c\u840d\u840e\u840f\u8410\u8411\u8412\u8413\u8414\u8415\u8416\u8417\u8418\u8419\u841a\u841b\u841c\u841d\u841e\u841f\u8420\u8421\u8422\u8423\u8424\u8425\u8426\u8427\u8428\u8429\u842a\u842b\u842c\u842d\u842e\u842f\u8430\u8431\u8432\u8433\u8434\u8435\u8436\u8437\u8438\u8439\u843a\u843b\u843c\u843d\u843e\u843f\u8440\u8441\u8442\u8443\u8444\u8445\u8446\u8447\u8448\u8449\u844a\u844b\u844c\u844d\u844e\u844f\u8450\u8451\u8452\u8453\u8454\u8455\u8456\u8457\u8458\u8459\u845a\u845b\u845c\u845d\u845e\u845f\u8460\u8461\u8462\u8463\u8464\u8465\u8466\u8467\u8468\u8469\u846a\u846b\u846c\u846d\u846e\u846f\u8470\u8471\u8472\u8473\u8474\u8475\u8476\u8477\u8478\u8479\u847a\u847b\u847c\u847d\u847e\u847f\u8480\u8481\u8482\u8483\u8484\u8485\u8486\u8487\u8488\u8489\u848a\u848b\u848c\u848d\u848e\u848f\u8490\u8491\u8492\u8493\u8494\u8495\u8496\u8497\u8498\u8499\u849a\u849b\u849c\u849d\u849e\u849f\u84a0\u84a1\u84a2\u84a3\u84a4\u84a5\u84a6\u84a7\u84a8\u84a9\u84aa\u84ab\u84ac\u84ad\u84ae\u84af\u84b0\u84b1\u84b2\u84b3\u84b4\u84b5\u84b6\u84b7\u84b8\u84b9\u84ba\u84bb\u84bc\u84bd\u84be\u84bf\u84c0\u84c1\u84c2\u84c3\u84c4\u84c5\u84c6\u84c7\u84c8\u84c9\u84ca\u84cb\u84cc\u84cd\u84ce\u84cf\u84d0\u84d1\u84d2\u84d3\u84d4\u84d5\u84d6\u84d7\u84d8\u84d9\u84da\u84db\u84dc\u84dd\u84de\u84df\u84e0\u84e1\u84e2\u84e3\u84e4\u84e5\u84e6\u84e7\u84e8\u84e9\u84ea\u84eb\u84ec\u84ed\u84ee\u84ef\u84f0\u84f1\u84f2\u84f3\u84f4\u84f5\u84f6\u84f7\u84f8\u84f9\u84fa\u84fb\u84fc\u84fd\u84fe\u84ff\u8500\u8501\u8502\u8503\u8504\u8505\u8506\u8507\u8508\u8509\u850a\u850b\u850c\u850d\u850e\u850f\u8510\u8511\u8512\u8513\u8514\u8515\u8516\u8517\u8518\u8519\u851a\u851b\u851c\u851d\u851e\u851f\u8520\u8521\u8522\u8523\u8524\u8525\u8526\u8527\u8528\u8529\u852a\u852b\u852c\u852d\u852e\u852f\u8530\u8531\u8532\u8533\u8534\u8535\u8536\u8537\u8538\u8539\u853a\u853b\u853c\u853d\u853e\u853f\u8540\u8541\u8542\u8543\u8544\u8545\u8546\u8547\u8548\u8549\u854a\u854b\u854c\u854d\u854e\u854f\u8550\u8551\u8552\u8553\u8554\u8555\u8556\u8557\u8558\u8559\u855a\u855b\u855c\u855d\u855e\u855f\u8560\u8561\u8562\u8563\u8564\u8565\u8566\u8567\u8568\u8569\u856a\u856b\u856c\u856d\u856e\u856f\u8570\u8571\u8572\u8573\u8574\u8575\u8576\u8577\u8578\u8579\u857a\u857b\u857c\u857d\u857e\u857f\u8580\u8581\u8582\u8583\u8584\u8585\u8586\u8587\u8588\u8589\u858a\u858b\u858c\u858d\u858e\u858f\u8590\u8591\u8592\u8593\u8594\u8595\u8596\u8597\u8598\u8599\u859a\u859b\u859c\u859d\u859e\u859f\u85a0\u85a1\u85a2\u85a3\u85a4\u85a5\u85a6\u85a7\u85a8\u85a9\u85aa\u85ab\u85ac\u85ad\u85ae\u85af\u85b0\u85b1\u85b2\u85b3\u85b4\u85b5\u85b6\u85b7\u85b8\u85b9\u85ba\u85bb\u85bc\u85bd\u85be\u85bf\u85c0\u85c1\u85c2\u85c3\u85c4\u85c5\u85c6\u85c7\u85c8\u85c9\u85ca\u85cb\u85cc\u85cd\u85ce\u85cf\u85d0\u85d1\u85d2\u85d3\u85d4\u85d5\u85d6\u85d7\u85d8\u85d9\u85da\u85db\u85dc\u85dd\u85de\u85df\u85e0\u85e1\u85e2\u85e3\u85e4\u85e5\u85e6\u85e7\u85e8\u85e9\u85ea\u85eb\u85ec\u85ed\u85ee\u85ef\u85f0\u85f1\u85f2\u85f3\u85f4\u85f5\u85f6\u85f7\u85f8\u85f9\u85fa\u85fb\u85fc\u85fd\u85fe\u85ff\u8600\u8601\u8602\u8603\u8604\u8605\u8606\u8607\u8608\u8609\u860a\u860b\u860c\u860d\u860e\u860f\u8610\u8611\u8612\u8613\u8614\u8615\u8616\u8617\u8618\u8619\u861a\u861b\u861c\u861d\u861e\u861f\u8620\u8621\u8622\u8623\u8624\u8625\u8626\u8627\u8628\u8629\u862a\u862b\u862c\u862d\u862e\u862f\u8630\u8631\u8632\u8633\u8634\u8635\u8636\u8637\u8638\u8639\u863a\u863b\u863c\u863d\u863e\u863f\u8640\u8641\u8642\u8643\u8644\u8645\u8646\u8647\u8648\u8649\u864a\u864b\u864c\u864d\u864e\u864f\u8650\u8651\u8652\u8653\u8654\u8655\u8656\u8657\u8658\u8659\u865a\u865b\u865c\u865d\u865e\u865f\u8660\u8661\u8662\u8663\u8664\u8665\u8666\u8667\u8668\u8669\u866a\u866b\u866c\u866d\u866e\u866f\u8670\u8671\u8672\u8673\u8674\u8675\u8676\u8677\u8678\u8679\u867a\u867b\u867c\u867d\u867e\u867f\u8680\u8681\u8682\u8683\u8684\u8685\u8686\u8687\u8688\u8689\u868a\u868b\u868c\u868d\u868e\u868f\u8690\u8691\u8692\u8693\u8694\u8695\u8696\u8697\u8698\u8699\u869a\u869b\u869c\u869d\u869e\u869f\u86a0\u86a1\u86a2\u86a3\u86a4\u86a5\u86a6\u86a7\u86a8\u86a9\u86aa\u86ab\u86ac\u86ad\u86ae\u86af\u86b0\u86b1\u86b2\u86b3\u86b4\u86b5\u86b6\u86b7\u86b8\u86b9\u86ba\u86bb\u86bc\u86bd\u86be\u86bf\u86c0\u86c1\u86c2\u86c3\u86c4\u86c5\u86c6\u86c7\u86c8\u86c9\u86ca\u86cb\u86cc\u86cd\u86ce\u86cf\u86d0\u86d1\u86d2\u86d3\u86d4\u86d5\u86d6\u86d7\u86d8\u86d9\u86da\u86db\u86dc\u86dd\u86de\u86df\u86e0\u86e1\u86e2\u86e3\u86e4\u86e5\u86e6\u86e7\u86e8\u86e9\u86ea\u86eb\u86ec\u86ed\u86ee\u86ef\u86f0\u86f1\u86f2\u86f3\u86f4\u86f5\u86f6\u86f7\u86f8\u86f9\u86fa\u86fb\u86fc\u86fd\u86fe\u86ff\u8700\u8701\u8702\u8703\u8704\u8705\u8706\u8707\u8708\u8709\u870a\u870b\u870c\u870d\u870e\u870f\u8710\u8711\u8712\u8713\u8714\u8715\u8716\u8717\u8718\u8719\u871a\u871b\u871c\u871d\u871e\u871f\u8720\u8721\u8722\u8723\u8724\u8725\u8726\u8727\u8728\u8729\u872a\u872b\u872c\u872d\u872e\u872f\u8730\u8731\u8732\u8733\u8734\u8735\u8736\u8737\u8738\u8739\u873a\u873b\u873c\u873d\u873e\u873f\u8740\u8741\u8742\u8743\u8744\u8745\u8746\u8747\u8748\u8749\u874a\u874b\u874c\u874d\u874e\u874f\u8750\u8751\u8752\u8753\u8754\u8755\u8756\u8757\u8758\u8759\u875a\u875b\u875c\u875d\u875e\u875f\u8760\u8761\u8762\u8763\u8764\u8765\u8766\u8767\u8768\u8769\u876a\u876b\u876c\u876d\u876e\u876f\u8770\u8771\u8772\u8773\u8774\u8775\u8776\u8777\u8778\u8779\u877a\u877b\u877c\u877d\u877e\u877f\u8780\u8781\u8782\u8783\u8784\u8785\u8786\u8787\u8788\u8789\u878a\u878b\u878c\u878d\u878e\u878f\u8790\u8791\u8792\u8793\u8794\u8795\u8796\u8797\u8798\u8799\u879a\u879b\u879c\u879d\u879e\u879f\u87a0\u87a1\u87a2\u87a3\u87a4\u87a5\u87a6\u87a7\u87a8\u87a9\u87aa\u87ab\u87ac\u87ad\u87ae\u87af\u87b0\u87b1\u87b2\u87b3\u87b4\u87b5\u87b6\u87b7\u87b8\u87b9\u87ba\u87bb\u87bc\u87bd\u87be\u87bf\u87c0\u87c1\u87c2\u87c3\u87c4\u87c5\u87c6\u87c7\u87c8\u87c9\u87ca\u87cb\u87cc\u87cd\u87ce\u87cf\u87d0\u87d1\u87d2\u87d3\u87d4\u87d5\u87d6\u87d7\u87d8\u87d9\u87da\u87db\u87dc\u87dd\u87de\u87df\u87e0\u87e1\u87e2\u87e3\u87e4\u87e5\u87e6\u87e7\u87e8\u87e9\u87ea\u87eb\u87ec\u87ed\u87ee\u87ef\u87f0\u87f1\u87f2\u87f3\u87f4\u87f5\u87f6\u87f7\u87f8\u87f9\u87fa\u87fb\u87fc\u87fd\u87fe\u87ff\u8800\u8801\u8802\u8803\u8804\u8805\u8806\u8807\u8808\u8809\u880a\u880b\u880c\u880d\u880e\u880f\u8810\u8811\u8812\u8813\u8814\u8815\u8816\u8817\u8818\u8819\u881a\u881b\u881c\u881d\u881e\u881f\u8820\u8821\u8822\u8823\u8824\u8825\u8826\u8827\u8828\u8829\u882a\u882b\u882c\u882d\u882e\u882f\u8830\u8831\u8832\u8833\u8834\u8835\u8836\u8837\u8838\u8839\u883a\u883b\u883c\u883d\u883e\u883f\u8840\u8841\u8842\u8843\u8844\u8845\u8846\u8847\u8848\u8849\u884a\u884b\u884c\u884d\u884e\u884f\u8850\u8851\u8852\u8853\u8854\u8855\u8856\u8857\u8858\u8859\u885a\u885b\u885c\u885d\u885e\u885f\u8860\u8861\u8862\u8863\u8864\u8865\u8866\u8867\u8868\u8869\u886a\u886b\u886c\u886d\u886e\u886f\u8870\u8871\u8872\u8873\u8874\u8875\u8876\u8877\u8878\u8879\u887a\u887b\u887c\u887d\u887e\u887f\u8880\u8881\u8882\u8883\u8884\u8885\u8886\u8887\u8888\u8889\u888a\u888b\u888c\u888d\u888e\u888f\u8890\u8891\u8892\u8893\u8894\u8895\u8896\u8897\u8898\u8899\u889a\u889b\u889c\u889d\u889e\u889f\u88a0\u88a1\u88a2\u88a3\u88a4\u88a5\u88a6\u88a7\u88a8\u88a9\u88aa\u88ab\u88ac\u88ad\u88ae\u88af\u88b0\u88b1\u88b2\u88b3\u88b4\u88b5\u88b6\u88b7\u88b8\u88b9\u88ba\u88bb\u88bc\u88bd\u88be\u88bf\u88c0\u88c1\u88c2\u88c3\u88c4\u88c5\u88c6\u88c7\u88c8\u88c9\u88ca\u88cb\u88cc\u88cd\u88ce\u88cf\u88d0\u88d1\u88d2\u88d3\u88d4\u88d5\u88d6\u88d7\u88d8\u88d9\u88da\u88db\u88dc\u88dd\u88de\u88df\u88e0\u88e1\u88e2\u88e3\u88e4\u88e5\u88e6\u88e7\u88e8\u88e9\u88ea\u88eb\u88ec\u88ed\u88ee\u88ef\u88f0\u88f1\u88f2\u88f3\u88f4\u88f5\u88f6\u88f7\u88f8\u88f9\u88fa\u88fb\u88fc\u88fd\u88fe\u88ff\u8900\u8901\u8902\u8903\u8904\u8905\u8906\u8907\u8908\u8909\u890a\u890b\u890c\u890d\u890e\u890f\u8910\u8911\u8912\u8913\u8914\u8915\u8916\u8917\u8918\u8919\u891a\u891b\u891c\u891d\u891e\u891f\u8920\u8921\u8922\u8923\u8924\u8925\u8926\u8927\u8928\u8929\u892a\u892b\u892c\u892d\u892e\u892f\u8930\u8931\u8932\u8933\u8934\u8935\u8936\u8937\u8938\u8939\u893a\u893b\u893c\u893d\u893e\u893f\u8940\u8941\u8942\u8943\u8944\u8945\u8946\u8947\u8948\u8949\u894a\u894b\u894c\u894d\u894e\u894f\u8950\u8951\u8952\u8953\u8954\u8955\u8956\u8957\u8958\u8959\u895a\u895b\u895c\u895d\u895e\u895f\u8960\u8961\u8962\u8963\u8964\u8965\u8966\u8967\u8968\u8969\u896a\u896b\u896c\u896d\u896e\u896f\u8970\u8971\u8972\u8973\u8974\u8975\u8976\u8977\u8978\u8979\u897a\u897b\u897c\u897d\u897e\u897f\u8980\u8981\u8982\u8983\u8984\u8985\u8986\u8987\u8988\u8989\u898a\u898b\u898c\u898d\u898e\u898f\u8990\u8991\u8992\u8993\u8994\u8995\u8996\u8997\u8998\u8999\u899a\u899b\u899c\u899d\u899e\u899f\u89a0\u89a1\u89a2\u89a3\u89a4\u89a5\u89a6\u89a7\u89a8\u89a9\u89aa\u89ab\u89ac\u89ad\u89ae\u89af\u89b0\u89b1\u89b2\u89b3\u89b4\u89b5\u89b6\u89b7\u89b8\u89b9\u89ba\u89bb\u89bc\u89bd\u89be\u89bf\u89c0\u89c1\u89c2\u89c3\u89c4\u89c5\u89c6\u89c7\u89c8\u89c9\u89ca\u89cb\u89cc\u89cd\u89ce\u89cf\u89d0\u89d1\u89d2\u89d3\u89d4\u89d5\u89d6\u89d7\u89d8\u89d9\u89da\u89db\u89dc\u89dd\u89de\u89df\u89e0\u89e1\u89e2\u89e3\u89e4\u89e5\u89e6\u89e7\u89e8\u89e9\u89ea\u89eb\u89ec\u89ed\u89ee\u89ef\u89f0\u89f1\u89f2\u89f3\u89f4\u89f5\u89f6\u89f7\u89f8\u89f9\u89fa\u89fb\u89fc\u89fd\u89fe\u89ff\u8a00\u8a01\u8a02\u8a03\u8a04\u8a05\u8a06\u8a07\u8a08\u8a09\u8a0a\u8a0b\u8a0c\u8a0d\u8a0e\u8a0f\u8a10\u8a11\u8a12\u8a13\u8a14\u8a15\u8a16\u8a17\u8a18\u8a19\u8a1a\u8a1b\u8a1c\u8a1d\u8a1e\u8a1f\u8a20\u8a21\u8a22\u8a23\u8a24\u8a25\u8a26\u8a27\u8a28\u8a29\u8a2a\u8a2b\u8a2c\u8a2d\u8a2e\u8a2f\u8a30\u8a31\u8a32\u8a33\u8a34\u8a35\u8a36\u8a37\u8a38\u8a39\u8a3a\u8a3b\u8a3c\u8a3d\u8a3e\u8a3f\u8a40\u8a41\u8a42\u8a43\u8a44\u8a45\u8a46\u8a47\u8a48\u8a49\u8a4a\u8a4b\u8a4c\u8a4d\u8a4e\u8a4f\u8a50\u8a51\u8a52\u8a53\u8a54\u8a55\u8a56\u8a57\u8a58\u8a59\u8a5a\u8a5b\u8a5c\u8a5d\u8a5e\u8a5f\u8a60\u8a61\u8a62\u8a63\u8a64\u8a65\u8a66\u8a67\u8a68\u8a69\u8a6a\u8a6b\u8a6c\u8a6d\u8a6e\u8a6f\u8a70\u8a71\u8a72\u8a73\u8a74\u8a75\u8a76\u8a77\u8a78\u8a79\u8a7a\u8a7b\u8a7c\u8a7d\u8a7e\u8a7f\u8a80\u8a81\u8a82\u8a83\u8a84\u8a85\u8a86\u8a87\u8a88\u8a89\u8a8a\u8a8b\u8a8c\u8a8d\u8a8e\u8a8f\u8a90\u8a91\u8a92\u8a93\u8a94\u8a95\u8a96\u8a97\u8a98\u8a99\u8a9a\u8a9b\u8a9c\u8a9d\u8a9e\u8a9f\u8aa0\u8aa1\u8aa2\u8aa3\u8aa4\u8aa5\u8aa6\u8aa7\u8aa8\u8aa9\u8aaa\u8aab\u8aac\u8aad\u8aae\u8aaf\u8ab0\u8ab1\u8ab2\u8ab3\u8ab4\u8ab5\u8ab6\u8ab7\u8ab8\u8ab9\u8aba\u8abb\u8abc\u8abd\u8abe\u8abf\u8ac0\u8ac1\u8ac2\u8ac3\u8ac4\u8ac5\u8ac6\u8ac7\u8ac8\u8ac9\u8aca\u8acb\u8acc\u8acd\u8ace\u8acf\u8ad0\u8ad1\u8ad2\u8ad3\u8ad4\u8ad5\u8ad6\u8ad7\u8ad8\u8ad9\u8ada\u8adb\u8adc\u8add\u8ade\u8adf\u8ae0\u8ae1\u8ae2\u8ae3\u8ae4\u8ae5\u8ae6\u8ae7\u8ae8\u8ae9\u8aea\u8aeb\u8aec\u8aed\u8aee\u8aef\u8af0\u8af1\u8af2\u8af3\u8af4\u8af5\u8af6\u8af7\u8af8\u8af9\u8afa\u8afb\u8afc\u8afd\u8afe\u8aff\u8b00\u8b01\u8b02\u8b03\u8b04\u8b05\u8b06\u8b07\u8b08\u8b09\u8b0a\u8b0b\u8b0c\u8b0d\u8b0e\u8b0f\u8b10\u8b11\u8b12\u8b13\u8b14\u8b15\u8b16\u8b17\u8b18\u8b19\u8b1a\u8b1b\u8b1c\u8b1d\u8b1e\u8b1f\u8b20\u8b21\u8b22\u8b23\u8b24\u8b25\u8b26\u8b27\u8b28\u8b29\u8b2a\u8b2b\u8b2c\u8b2d\u8b2e\u8b2f\u8b30\u8b31\u8b32\u8b33\u8b34\u8b35\u8b36\u8b37\u8b38\u8b39\u8b3a\u8b3b\u8b3c\u8b3d\u8b3e\u8b3f\u8b40\u8b41\u8b42\u8b43\u8b44\u8b45\u8b46\u8b47\u8b48\u8b49\u8b4a\u8b4b\u8b4c\u8b4d\u8b4e\u8b4f\u8b50\u8b51\u8b52\u8b53\u8b54\u8b55\u8b56\u8b57\u8b58\u8b59\u8b5a\u8b5b\u8b5c\u8b5d\u8b5e\u8b5f\u8b60\u8b61\u8b62\u8b63\u8b64\u8b65\u8b66\u8b67\u8b68\u8b69\u8b6a\u8b6b\u8b6c\u8b6d\u8b6e\u8b6f\u8b70\u8b71\u8b72\u8b73\u8b74\u8b75\u8b76\u8b77\u8b78\u8b79\u8b7a\u8b7b\u8b7c\u8b7d\u8b7e\u8b7f\u8b80\u8b81\u8b82\u8b83\u8b84\u8b85\u8b86\u8b87\u8b88\u8b89\u8b8a\u8b8b\u8b8c\u8b8d\u8b8e\u8b8f\u8b90\u8b91\u8b92\u8b93\u8b94\u8b95\u8b96\u8b97\u8b98\u8b99\u8b9a\u8b9b\u8b9c\u8b9d\u8b9e\u8b9f\u8ba0\u8ba1\u8ba2\u8ba3\u8ba4\u8ba5\u8ba6\u8ba7\u8ba8\u8ba9\u8baa\u8bab\u8bac\u8bad\u8bae\u8baf\u8bb0\u8bb1\u8bb2\u8bb3\u8bb4\u8bb5\u8bb6\u8bb7\u8bb8\u8bb9\u8bba\u8bbb\u8bbc\u8bbd\u8bbe\u8bbf\u8bc0\u8bc1\u8bc2\u8bc3\u8bc4\u8bc5\u8bc6\u8bc7\u8bc8\u8bc9\u8bca\u8bcb\u8bcc\u8bcd\u8bce\u8bcf\u8bd0\u8bd1\u8bd2\u8bd3\u8bd4\u8bd5\u8bd6\u8bd7\u8bd8\u8bd9\u8bda\u8bdb\u8bdc\u8bdd\u8bde\u8bdf\u8be0\u8be1\u8be2\u8be3\u8be4\u8be5\u8be6\u8be7\u8be8\u8be9\u8bea\u8beb\u8bec\u8bed\u8bee\u8bef\u8bf0\u8bf1\u8bf2\u8bf3\u8bf4\u8bf5\u8bf6\u8bf7\u8bf8\u8bf9\u8bfa\u8bfb\u8bfc\u8bfd\u8bfe\u8bff\u8c00\u8c01\u8c02\u8c03\u8c04\u8c05\u8c06\u8c07\u8c08\u8c09\u8c0a\u8c0b\u8c0c\u8c0d\u8c0e\u8c0f\u8c10\u8c11\u8c12\u8c13\u8c14\u8c15\u8c16\u8c17\u8c18\u8c19\u8c1a\u8c1b\u8c1c\u8c1d\u8c1e\u8c1f\u8c20\u8c21\u8c22\u8c23\u8c24\u8c25\u8c26\u8c27\u8c28\u8c29\u8c2a\u8c2b\u8c2c\u8c2d\u8c2e\u8c2f\u8c30\u8c31\u8c32\u8c33\u8c34\u8c35\u8c36\u8c37\u8c38\u8c39\u8c3a\u8c3b\u8c3c\u8c3d\u8c3e\u8c3f\u8c40\u8c41\u8c42\u8c43\u8c44\u8c45\u8c46\u8c47\u8c48\u8c49\u8c4a\u8c4b\u8c4c\u8c4d\u8c4e\u8c4f\u8c50\u8c51\u8c52\u8c53\u8c54\u8c55\u8c56\u8c57\u8c58\u8c59\u8c5a\u8c5b\u8c5c\u8c5d\u8c5e\u8c5f\u8c60\u8c61\u8c62\u8c63\u8c64\u8c65\u8c66\u8c67\u8c68\u8c69\u8c6a\u8c6b\u8c6c\u8c6d\u8c6e\u8c6f\u8c70\u8c71\u8c72\u8c73\u8c74\u8c75\u8c76\u8c77\u8c78\u8c79\u8c7a\u8c7b\u8c7c\u8c7d\u8c7e\u8c7f\u8c80\u8c81\u8c82\u8c83\u8c84\u8c85\u8c86\u8c87\u8c88\u8c89\u8c8a\u8c8b\u8c8c\u8c8d\u8c8e\u8c8f\u8c90\u8c91\u8c92\u8c93\u8c94\u8c95\u8c96\u8c97\u8c98\u8c99\u8c9a\u8c9b\u8c9c\u8c9d\u8c9e\u8c9f\u8ca0\u8ca1\u8ca2\u8ca3\u8ca4\u8ca5\u8ca6\u8ca7\u8ca8\u8ca9\u8caa\u8cab\u8cac\u8cad\u8cae\u8caf\u8cb0\u8cb1\u8cb2\u8cb3\u8cb4\u8cb5\u8cb6\u8cb7\u8cb8\u8cb9\u8cba\u8cbb\u8cbc\u8cbd\u8cbe\u8cbf\u8cc0\u8cc1\u8cc2\u8cc3\u8cc4\u8cc5\u8cc6\u8cc7\u8cc8\u8cc9\u8cca\u8ccb\u8ccc\u8ccd\u8cce\u8ccf\u8cd0\u8cd1\u8cd2\u8cd3\u8cd4\u8cd5\u8cd6\u8cd7\u8cd8\u8cd9\u8cda\u8cdb\u8cdc\u8cdd\u8cde\u8cdf\u8ce0\u8ce1\u8ce2\u8ce3\u8ce4\u8ce5\u8ce6\u8ce7\u8ce8\u8ce9\u8cea\u8ceb\u8cec\u8ced\u8cee\u8cef\u8cf0\u8cf1\u8cf2\u8cf3\u8cf4\u8cf5\u8cf6\u8cf7\u8cf8\u8cf9\u8cfa\u8cfb\u8cfc\u8cfd\u8cfe\u8cff\u8d00\u8d01\u8d02\u8d03\u8d04\u8d05\u8d06\u8d07\u8d08\u8d09\u8d0a\u8d0b\u8d0c\u8d0d\u8d0e\u8d0f\u8d10\u8d11\u8d12\u8d13\u8d14\u8d15\u8d16\u8d17\u8d18\u8d19\u8d1a\u8d1b\u8d1c\u8d1d\u8d1e\u8d1f\u8d20\u8d21\u8d22\u8d23\u8d24\u8d25\u8d26\u8d27\u8d28\u8d29\u8d2a\u8d2b\u8d2c\u8d2d\u8d2e\u8d2f\u8d30\u8d31\u8d32\u8d33\u8d34\u8d35\u8d36\u8d37\u8d38\u8d39\u8d3a\u8d3b\u8d3c\u8d3d\u8d3e\u8d3f\u8d40\u8d41\u8d42\u8d43\u8d44\u8d45\u8d46\u8d47\u8d48\u8d49\u8d4a\u8d4b\u8d4c\u8d4d\u8d4e\u8d4f\u8d50\u8d51\u8d52\u8d53\u8d54\u8d55\u8d56\u8d57\u8d58\u8d59\u8d5a\u8d5b\u8d5c\u8d5d\u8d5e\u8d5f\u8d60\u8d61\u8d62\u8d63\u8d64\u8d65\u8d66\u8d67\u8d68\u8d69\u8d6a\u8d6b\u8d6c\u8d6d\u8d6e\u8d6f\u8d70\u8d71\u8d72\u8d73\u8d74\u8d75\u8d76\u8d77\u8d78\u8d79\u8d7a\u8d7b\u8d7c\u8d7d\u8d7e\u8d7f\u8d80\u8d81\u8d82\u8d83\u8d84\u8d85\u8d86\u8d87\u8d88\u8d89\u8d8a\u8d8b\u8d8c\u8d8d\u8d8e\u8d8f\u8d90\u8d91\u8d92\u8d93\u8d94\u8d95\u8d96\u8d97\u8d98\u8d99\u8d9a\u8d9b\u8d9c\u8d9d\u8d9e\u8d9f\u8da0\u8da1\u8da2\u8da3\u8da4\u8da5\u8da6\u8da7\u8da8\u8da9\u8daa\u8dab\u8dac\u8dad\u8dae\u8daf\u8db0\u8db1\u8db2\u8db3\u8db4\u8db5\u8db6\u8db7\u8db8\u8db9\u8dba\u8dbb\u8dbc\u8dbd\u8dbe\u8dbf\u8dc0\u8dc1\u8dc2\u8dc3\u8dc4\u8dc5\u8dc6\u8dc7\u8dc8\u8dc9\u8dca\u8dcb\u8dcc\u8dcd\u8dce\u8dcf\u8dd0\u8dd1\u8dd2\u8dd3\u8dd4\u8dd5\u8dd6\u8dd7\u8dd8\u8dd9\u8dda\u8ddb\u8ddc\u8ddd\u8dde\u8ddf\u8de0\u8de1\u8de2\u8de3\u8de4\u8de5\u8de6\u8de7\u8de8\u8de9\u8dea\u8deb\u8dec\u8ded\u8dee\u8def\u8df0\u8df1\u8df2\u8df3\u8df4\u8df5\u8df6\u8df7\u8df8\u8df9\u8dfa\u8dfb\u8dfc\u8dfd\u8dfe\u8dff\u8e00\u8e01\u8e02\u8e03\u8e04\u8e05\u8e06\u8e07\u8e08\u8e09\u8e0a\u8e0b\u8e0c\u8e0d\u8e0e\u8e0f\u8e10\u8e11\u8e12\u8e13\u8e14\u8e15\u8e16\u8e17\u8e18\u8e19\u8e1a\u8e1b\u8e1c\u8e1d\u8e1e\u8e1f\u8e20\u8e21\u8e22\u8e23\u8e24\u8e25\u8e26\u8e27\u8e28\u8e29\u8e2a\u8e2b\u8e2c\u8e2d\u8e2e\u8e2f\u8e30\u8e31\u8e32\u8e33\u8e34\u8e35\u8e36\u8e37\u8e38\u8e39\u8e3a\u8e3b\u8e3c\u8e3d\u8e3e\u8e3f\u8e40\u8e41\u8e42\u8e43\u8e44\u8e45\u8e46\u8e47\u8e48\u8e49\u8e4a\u8e4b\u8e4c\u8e4d\u8e4e\u8e4f\u8e50\u8e51\u8e52\u8e53\u8e54\u8e55\u8e56\u8e57\u8e58\u8e59\u8e5a\u8e5b\u8e5c\u8e5d\u8e5e\u8e5f\u8e60\u8e61\u8e62\u8e63\u8e64\u8e65\u8e66\u8e67\u8e68\u8e69\u8e6a\u8e6b\u8e6c\u8e6d\u8e6e\u8e6f\u8e70\u8e71\u8e72\u8e73\u8e74\u8e75\u8e76\u8e77\u8e78\u8e79\u8e7a\u8e7b\u8e7c\u8e7d\u8e7e\u8e7f\u8e80\u8e81\u8e82\u8e83\u8e84\u8e85\u8e86\u8e87\u8e88\u8e89\u8e8a\u8e8b\u8e8c\u8e8d\u8e8e\u8e8f\u8e90\u8e91\u8e92\u8e93\u8e94\u8e95\u8e96\u8e97\u8e98\u8e99\u8e9a\u8e9b\u8e9c\u8e9d\u8e9e\u8e9f\u8ea0\u8ea1\u8ea2\u8ea3\u8ea4\u8ea5\u8ea6\u8ea7\u8ea8\u8ea9\u8eaa\u8eab\u8eac\u8ead\u8eae\u8eaf\u8eb0\u8eb1\u8eb2\u8eb3\u8eb4\u8eb5\u8eb6\u8eb7\u8eb8\u8eb9\u8eba\u8ebb\u8ebc\u8ebd\u8ebe\u8ebf\u8ec0\u8ec1\u8ec2\u8ec3\u8ec4\u8ec5\u8ec6\u8ec7\u8ec8\u8ec9\u8eca\u8ecb\u8ecc\u8ecd\u8ece\u8ecf\u8ed0\u8ed1\u8ed2\u8ed3\u8ed4\u8ed5\u8ed6\u8ed7\u8ed8\u8ed9\u8eda\u8edb\u8edc\u8edd\u8ede\u8edf\u8ee0\u8ee1\u8ee2\u8ee3\u8ee4\u8ee5\u8ee6\u8ee7\u8ee8\u8ee9\u8eea\u8eeb\u8eec\u8eed\u8eee\u8eef\u8ef0\u8ef1\u8ef2\u8ef3\u8ef4\u8ef5\u8ef6\u8ef7\u8ef8\u8ef9\u8efa\u8efb\u8efc\u8efd\u8efe\u8eff\u8f00\u8f01\u8f02\u8f03\u8f04\u8f05\u8f06\u8f07\u8f08\u8f09\u8f0a\u8f0b\u8f0c\u8f0d\u8f0e\u8f0f\u8f10\u8f11\u8f12\u8f13\u8f14\u8f15\u8f16\u8f17\u8f18\u8f19\u8f1a\u8f1b\u8f1c\u8f1d\u8f1e\u8f1f\u8f20\u8f21\u8f22\u8f23\u8f24\u8f25\u8f26\u8f27\u8f28\u8f29\u8f2a\u8f2b\u8f2c\u8f2d\u8f2e\u8f2f\u8f30\u8f31\u8f32\u8f33\u8f34\u8f35\u8f36\u8f37\u8f38\u8f39\u8f3a\u8f3b\u8f3c\u8f3d\u8f3e\u8f3f\u8f40\u8f41\u8f42\u8f43\u8f44\u8f45\u8f46\u8f47\u8f48\u8f49\u8f4a\u8f4b\u8f4c\u8f4d\u8f4e\u8f4f\u8f50\u8f51\u8f52\u8f53\u8f54\u8f55\u8f56\u8f57\u8f58\u8f59\u8f5a\u8f5b\u8f5c\u8f5d\u8f5e\u8f5f\u8f60\u8f61\u8f62\u8f63\u8f64\u8f65\u8f66\u8f67\u8f68\u8f69\u8f6a\u8f6b\u8f6c\u8f6d\u8f6e\u8f6f\u8f70\u8f71\u8f72\u8f73\u8f74\u8f75\u8f76\u8f77\u8f78\u8f79\u8f7a\u8f7b\u8f7c\u8f7d\u8f7e\u8f7f\u8f80\u8f81\u8f82\u8f83\u8f84\u8f85\u8f86\u8f87\u8f88\u8f89\u8f8a\u8f8b\u8f8c\u8f8d\u8f8e\u8f8f\u8f90\u8f91\u8f92\u8f93\u8f94\u8f95\u8f96\u8f97\u8f98\u8f99\u8f9a\u8f9b\u8f9c\u8f9d\u8f9e\u8f9f\u8fa0\u8fa1\u8fa2\u8fa3\u8fa4\u8fa5\u8fa6\u8fa7\u8fa8\u8fa9\u8faa\u8fab\u8fac\u8fad\u8fae\u8faf\u8fb0\u8fb1\u8fb2\u8fb3\u8fb4\u8fb5\u8fb6\u8fb7\u8fb8\u8fb9\u8fba\u8fbb\u8fbc\u8fbd\u8fbe\u8fbf\u8fc0\u8fc1\u8fc2\u8fc3\u8fc4\u8fc5\u8fc6\u8fc7\u8fc8\u8fc9\u8fca\u8fcb\u8fcc\u8fcd\u8fce\u8fcf\u8fd0\u8fd1\u8fd2\u8fd3\u8fd4\u8fd5\u8fd6\u8fd7\u8fd8\u8fd9\u8fda\u8fdb\u8fdc\u8fdd\u8fde\u8fdf\u8fe0\u8fe1\u8fe2\u8fe3\u8fe4\u8fe5\u8fe6\u8fe7\u8fe8\u8fe9\u8fea\u8feb\u8fec\u8fed\u8fee\u8fef\u8ff0\u8ff1\u8ff2\u8ff3\u8ff4\u8ff5\u8ff6\u8ff7\u8ff8\u8ff9\u8ffa\u8ffb\u8ffc\u8ffd\u8ffe\u8fff\u9000\u9001\u9002\u9003\u9004\u9005\u9006\u9007\u9008\u9009\u900a\u900b\u900c\u900d\u900e\u900f\u9010\u9011\u9012\u9013\u9014\u9015\u9016\u9017\u9018\u9019\u901a\u901b\u901c\u901d\u901e\u901f\u9020\u9021\u9022\u9023\u9024\u9025\u9026\u9027\u9028\u9029\u902a\u902b\u902c\u902d\u902e\u902f\u9030\u9031\u9032\u9033\u9034\u9035\u9036\u9037\u9038\u9039\u903a\u903b\u903c\u903d\u903e\u903f\u9040\u9041\u9042\u9043\u9044\u9045\u9046\u9047\u9048\u9049\u904a\u904b\u904c\u904d\u904e\u904f\u9050\u9051\u9052\u9053\u9054\u9055\u9056\u9057\u9058\u9059\u905a\u905b\u905c\u905d\u905e\u905f\u9060\u9061\u9062\u9063\u9064\u9065\u9066\u9067\u9068\u9069\u906a\u906b\u906c\u906d\u906e\u906f\u9070\u9071\u9072\u9073\u9074\u9075\u9076\u9077\u9078\u9079\u907a\u907b\u907c\u907d\u907e\u907f\u9080\u9081\u9082\u9083\u9084\u9085\u9086\u9087\u9088\u9089\u908a\u908b\u908c\u908d\u908e\u908f\u9090\u9091\u9092\u9093\u9094\u9095\u9096\u9097\u9098\u9099\u909a\u909b\u909c\u909d\u909e\u909f\u90a0\u90a1\u90a2\u90a3\u90a4\u90a5\u90a6\u90a7\u90a8\u90a9\u90aa\u90ab\u90ac\u90ad\u90ae\u90af\u90b0\u90b1\u90b2\u90b3\u90b4\u90b5\u90b6\u90b7\u90b8\u90b9\u90ba\u90bb\u90bc\u90bd\u90be\u90bf\u90c0\u90c1\u90c2\u90c3\u90c4\u90c5\u90c6\u90c7\u90c8\u90c9\u90ca\u90cb\u90cc\u90cd\u90ce\u90cf\u90d0\u90d1\u90d2\u90d3\u90d4\u90d5\u90d6\u90d7\u90d8\u90d9\u90da\u90db\u90dc\u90dd\u90de\u90df\u90e0\u90e1\u90e2\u90e3\u90e4\u90e5\u90e6\u90e7\u90e8\u90e9\u90ea\u90eb\u90ec\u90ed\u90ee\u90ef\u90f0\u90f1\u90f2\u90f3\u90f4\u90f5\u90f6\u90f7\u90f8\u90f9\u90fa\u90fb\u90fc\u90fd\u90fe\u90ff\u9100\u9101\u9102\u9103\u9104\u9105\u9106\u9107\u9108\u9109\u910a\u910b\u910c\u910d\u910e\u910f\u9110\u9111\u9112\u9113\u9114\u9115\u9116\u9117\u9118\u9119\u911a\u911b\u911c\u911d\u911e\u911f\u9120\u9121\u9122\u9123\u9124\u9125\u9126\u9127\u9128\u9129\u912a\u912b\u912c\u912d\u912e\u912f\u9130\u9131\u9132\u9133\u9134\u9135\u9136\u9137\u9138\u9139\u913a\u913b\u913c\u913d\u913e\u913f\u9140\u9141\u9142\u9143\u9144\u9145\u9146\u9147\u9148\u9149\u914a\u914b\u914c\u914d\u914e\u914f\u9150\u9151\u9152\u9153\u9154\u9155\u9156\u9157\u9158\u9159\u915a\u915b\u915c\u915d\u915e\u915f\u9160\u9161\u9162\u9163\u9164\u9165\u9166\u9167\u9168\u9169\u916a\u916b\u916c\u916d\u916e\u916f\u9170\u9171\u9172\u9173\u9174\u9175\u9176\u9177\u9178\u9179\u917a\u917b\u917c\u917d\u917e\u917f\u9180\u9181\u9182\u9183\u9184\u9185\u9186\u9187\u9188\u9189\u918a\u918b\u918c\u918d\u918e\u918f\u9190\u9191\u9192\u9193\u9194\u9195\u9196\u9197\u9198\u9199\u919a\u919b\u919c\u919d\u919e\u919f\u91a0\u91a1\u91a2\u91a3\u91a4\u91a5\u91a6\u91a7\u91a8\u91a9\u91aa\u91ab\u91ac\u91ad\u91ae\u91af\u91b0\u91b1\u91b2\u91b3\u91b4\u91b5\u91b6\u91b7\u91b8\u91b9\u91ba\u91bb\u91bc\u91bd\u91be\u91bf\u91c0\u91c1\u91c2\u91c3\u91c4\u91c5\u91c6\u91c7\u91c8\u91c9\u91ca\u91cb\u91cc\u91cd\u91ce\u91cf\u91d0\u91d1\u91d2\u91d3\u91d4\u91d5\u91d6\u91d7\u91d8\u91d9\u91da\u91db\u91dc\u91dd\u91de\u91df\u91e0\u91e1\u91e2\u91e3\u91e4\u91e5\u91e6\u91e7\u91e8\u91e9\u91ea\u91eb\u91ec\u91ed\u91ee\u91ef\u91f0\u91f1\u91f2\u91f3\u91f4\u91f5\u91f6\u91f7\u91f8\u91f9\u91fa\u91fb\u91fc\u91fd\u91fe\u91ff\u9200\u9201\u9202\u9203\u9204\u9205\u9206\u9207\u9208\u9209\u920a\u920b\u920c\u920d\u920e\u920f\u9210\u9211\u9212\u9213\u9214\u9215\u9216\u9217\u9218\u9219\u921a\u921b\u921c\u921d\u921e\u921f\u9220\u9221\u9222\u9223\u9224\u9225\u9226\u9227\u9228\u9229\u922a\u922b\u922c\u922d\u922e\u922f\u9230\u9231\u9232\u9233\u9234\u9235\u9236\u9237\u9238\u9239\u923a\u923b\u923c\u923d\u923e\u923f\u9240\u9241\u9242\u9243\u9244\u9245\u9246\u9247\u9248\u9249\u924a\u924b\u924c\u924d\u924e\u924f\u9250\u9251\u9252\u9253\u9254\u9255\u9256\u9257\u9258\u9259\u925a\u925b\u925c\u925d\u925e\u925f\u9260\u9261\u9262\u9263\u9264\u9265\u9266\u9267\u9268\u9269\u926a\u926b\u926c\u926d\u926e\u926f\u9270\u9271\u9272\u9273\u9274\u9275\u9276\u9277\u9278\u9279\u927a\u927b\u927c\u927d\u927e\u927f\u9280\u9281\u9282\u9283\u9284\u9285\u9286\u9287\u9288\u9289\u928a\u928b\u928c\u928d\u928e\u928f\u9290\u9291\u9292\u9293\u9294\u9295\u9296\u9297\u9298\u9299\u929a\u929b\u929c\u929d\u929e\u929f\u92a0\u92a1\u92a2\u92a3\u92a4\u92a5\u92a6\u92a7\u92a8\u92a9\u92aa\u92ab\u92ac\u92ad\u92ae\u92af\u92b0\u92b1\u92b2\u92b3\u92b4\u92b5\u92b6\u92b7\u92b8\u92b9\u92ba\u92bb\u92bc\u92bd\u92be\u92bf\u92c0\u92c1\u92c2\u92c3\u92c4\u92c5\u92c6\u92c7\u92c8\u92c9\u92ca\u92cb\u92cc\u92cd\u92ce\u92cf\u92d0\u92d1\u92d2\u92d3\u92d4\u92d5\u92d6\u92d7\u92d8\u92d9\u92da\u92db\u92dc\u92dd\u92de\u92df\u92e0\u92e1\u92e2\u92e3\u92e4\u92e5\u92e6\u92e7\u92e8\u92e9\u92ea\u92eb\u92ec\u92ed\u92ee\u92ef\u92f0\u92f1\u92f2\u92f3\u92f4\u92f5\u92f6\u92f7\u92f8\u92f9\u92fa\u92fb\u92fc\u92fd\u92fe\u92ff\u9300\u9301\u9302\u9303\u9304\u9305\u9306\u9307\u9308\u9309\u930a\u930b\u930c\u930d\u930e\u930f\u9310\u9311\u9312\u9313\u9314\u9315\u9316\u9317\u9318\u9319\u931a\u931b\u931c\u931d\u931e\u931f\u9320\u9321\u9322\u9323\u9324\u9325\u9326\u9327\u9328\u9329\u932a\u932b\u932c\u932d\u932e\u932f\u9330\u9331\u9332\u9333\u9334\u9335\u9336\u9337\u9338\u9339\u933a\u933b\u933c\u933d\u933e\u933f\u9340\u9341\u9342\u9343\u9344\u9345\u9346\u9347\u9348\u9349\u934a\u934b\u934c\u934d\u934e\u934f\u9350\u9351\u9352\u9353\u9354\u9355\u9356\u9357\u9358\u9359\u935a\u935b\u935c\u935d\u935e\u935f\u9360\u9361\u9362\u9363\u9364\u9365\u9366\u9367\u9368\u9369\u936a\u936b\u936c\u936d\u936e\u936f\u9370\u9371\u9372\u9373\u9374\u9375\u9376\u9377\u9378\u9379\u937a\u937b\u937c\u937d\u937e\u937f\u9380\u9381\u9382\u9383\u9384\u9385\u9386\u9387\u9388\u9389\u938a\u938b\u938c\u938d\u938e\u938f\u9390\u9391\u9392\u9393\u9394\u9395\u9396\u9397\u9398\u9399\u939a\u939b\u939c\u939d\u939e\u939f\u93a0\u93a1\u93a2\u93a3\u93a4\u93a5\u93a6\u93a7\u93a8\u93a9\u93aa\u93ab\u93ac\u93ad\u93ae\u93af\u93b0\u93b1\u93b2\u93b3\u93b4\u93b5\u93b6\u93b7\u93b8\u93b9\u93ba\u93bb\u93bc\u93bd\u93be\u93bf\u93c0\u93c1\u93c2\u93c3\u93c4\u93c5\u93c6\u93c7\u93c8\u93c9\u93ca\u93cb\u93cc\u93cd\u93ce\u93cf\u93d0\u93d1\u93d2\u93d3\u93d4\u93d5\u93d6\u93d7\u93d8\u93d9\u93da\u93db\u93dc\u93dd\u93de\u93df\u93e0\u93e1\u93e2\u93e3\u93e4\u93e5\u93e6\u93e7\u93e8\u93e9\u93ea\u93eb\u93ec\u93ed\u93ee\u93ef\u93f0\u93f1\u93f2\u93f3\u93f4\u93f5\u93f6\u93f7\u93f8\u93f9\u93fa\u93fb\u93fc\u93fd\u93fe\u93ff\u9400\u9401\u9402\u9403\u9404\u9405\u9406\u9407\u9408\u9409\u940a\u940b\u940c\u940d\u940e\u940f\u9410\u9411\u9412\u9413\u9414\u9415\u9416\u9417\u9418\u9419\u941a\u941b\u941c\u941d\u941e\u941f\u9420\u9421\u9422\u9423\u9424\u9425\u9426\u9427\u9428\u9429\u942a\u942b\u942c\u942d\u942e\u942f\u9430\u9431\u9432\u9433\u9434\u9435\u9436\u9437\u9438\u9439\u943a\u943b\u943c\u943d\u943e\u943f\u9440\u9441\u9442\u9443\u9444\u9445\u9446\u9447\u9448\u9449\u944a\u944b\u944c\u944d\u944e\u944f\u9450\u9451\u9452\u9453\u9454\u9455\u9456\u9457\u9458\u9459\u945a\u945b\u945c\u945d\u945e\u945f\u9460\u9461\u9462\u9463\u9464\u9465\u9466\u9467\u9468\u9469\u946a\u946b\u946c\u946d\u946e\u946f\u9470\u9471\u9472\u9473\u9474\u9475\u9476\u9477\u9478\u9479\u947a\u947b\u947c\u947d\u947e\u947f\u9480\u9481\u9482\u9483\u9484\u9485\u9486\u9487\u9488\u9489\u948a\u948b\u948c\u948d\u948e\u948f\u9490\u9491\u9492\u9493\u9494\u9495\u9496\u9497\u9498\u9499\u949a\u949b\u949c\u949d\u949e\u949f\u94a0\u94a1\u94a2\u94a3\u94a4\u94a5\u94a6\u94a7\u94a8\u94a9\u94aa\u94ab\u94ac\u94ad\u94ae\u94af\u94b0\u94b1\u94b2\u94b3\u94b4\u94b5\u94b6\u94b7\u94b8\u94b9\u94ba\u94bb\u94bc\u94bd\u94be\u94bf\u94c0\u94c1\u94c2\u94c3\u94c4\u94c5\u94c6\u94c7\u94c8\u94c9\u94ca\u94cb\u94cc\u94cd\u94ce\u94cf\u94d0\u94d1\u94d2\u94d3\u94d4\u94d5\u94d6\u94d7\u94d8\u94d9\u94da\u94db\u94dc\u94dd\u94de\u94df\u94e0\u94e1\u94e2\u94e3\u94e4\u94e5\u94e6\u94e7\u94e8\u94e9\u94ea\u94eb\u94ec\u94ed\u94ee\u94ef\u94f0\u94f1\u94f2\u94f3\u94f4\u94f5\u94f6\u94f7\u94f8\u94f9\u94fa\u94fb\u94fc\u94fd\u94fe\u94ff\u9500\u9501\u9502\u9503\u9504\u9505\u9506\u9507\u9508\u9509\u950a\u950b\u950c\u950d\u950e\u950f\u9510\u9511\u9512\u9513\u9514\u9515\u9516\u9517\u9518\u9519\u951a\u951b\u951c\u951d\u951e\u951f\u9520\u9521\u9522\u9523\u9524\u9525\u9526\u9527\u9528\u9529\u952a\u952b\u952c\u952d\u952e\u952f\u9530\u9531\u9532\u9533\u9534\u9535\u9536\u9537\u9538\u9539\u953a\u953b\u953c\u953d\u953e\u953f\u9540\u9541\u9542\u9543\u9544\u9545\u9546\u9547\u9548\u9549\u954a\u954b\u954c\u954d\u954e\u954f\u9550\u9551\u9552\u9553\u9554\u9555\u9556\u9557\u9558\u9559\u955a\u955b\u955c\u955d\u955e\u955f\u9560\u9561\u9562\u9563\u9564\u9565\u9566\u9567\u9568\u9569\u956a\u956b\u956c\u956d\u956e\u956f\u9570\u9571\u9572\u9573\u9574\u9575\u9576\u9577\u9578\u9579\u957a\u957b\u957c\u957d\u957e\u957f\u9580\u9581\u9582\u9583\u9584\u9585\u9586\u9587\u9588\u9589\u958a\u958b\u958c\u958d\u958e\u958f\u9590\u9591\u9592\u9593\u9594\u9595\u9596\u9597\u9598\u9599\u959a\u959b\u959c\u959d\u959e\u959f\u95a0\u95a1\u95a2\u95a3\u95a4\u95a5\u95a6\u95a7\u95a8\u95a9\u95aa\u95ab\u95ac\u95ad\u95ae\u95af\u95b0\u95b1\u95b2\u95b3\u95b4\u95b5\u95b6\u95b7\u95b8\u95b9\u95ba\u95bb\u95bc\u95bd\u95be\u95bf\u95c0\u95c1\u95c2\u95c3\u95c4\u95c5\u95c6\u95c7\u95c8\u95c9\u95ca\u95cb\u95cc\u95cd\u95ce\u95cf\u95d0\u95d1\u95d2\u95d3\u95d4\u95d5\u95d6\u95d7\u95d8\u95d9\u95da\u95db\u95dc\u95dd\u95de\u95df\u95e0\u95e1\u95e2\u95e3\u95e4\u95e5\u95e6\u95e7\u95e8\u95e9\u95ea\u95eb\u95ec\u95ed\u95ee\u95ef\u95f0\u95f1\u95f2\u95f3\u95f4\u95f5\u95f6\u95f7\u95f8\u95f9\u95fa\u95fb\u95fc\u95fd\u95fe\u95ff\u9600\u9601\u9602\u9603\u9604\u9605\u9606\u9607\u9608\u9609\u960a\u960b\u960c\u960d\u960e\u960f\u9610\u9611\u9612\u9613\u9614\u9615\u9616\u9617\u9618\u9619\u961a\u961b\u961c\u961d\u961e\u961f\u9620\u9621\u9622\u9623\u9624\u9625\u9626\u9627\u9628\u9629\u962a\u962b\u962c\u962d\u962e\u962f\u9630\u9631\u9632\u9633\u9634\u9635\u9636\u9637\u9638\u9639\u963a\u963b\u963c\u963d\u963e\u963f\u9640\u9641\u9642\u9643\u9644\u9645\u9646\u9647\u9648\u9649\u964a\u964b\u964c\u964d\u964e\u964f\u9650\u9651\u9652\u9653\u9654\u9655\u9656\u9657\u9658\u9659\u965a\u965b\u965c\u965d\u965e\u965f\u9660\u9661\u9662\u9663\u9664\u9665\u9666\u9667\u9668\u9669\u966a\u966b\u966c\u966d\u966e\u966f\u9670\u9671\u9672\u9673\u9674\u9675\u9676\u9677\u9678\u9679\u967a\u967b\u967c\u967d\u967e\u967f\u9680\u9681\u9682\u9683\u9684\u9685\u9686\u9687\u9688\u9689\u968a\u968b\u968c\u968d\u968e\u968f\u9690\u9691\u9692\u9693\u9694\u9695\u9696\u9697\u9698\u9699\u969a\u969b\u969c\u969d\u969e\u969f\u96a0\u96a1\u96a2\u96a3\u96a4\u96a5\u96a6\u96a7\u96a8\u96a9\u96aa\u96ab\u96ac\u96ad\u96ae\u96af\u96b0\u96b1\u96b2\u96b3\u96b4\u96b5\u96b6\u96b7\u96b8\u96b9\u96ba\u96bb\u96bc\u96bd\u96be\u96bf\u96c0\u96c1\u96c2\u96c3\u96c4\u96c5\u96c6\u96c7\u96c8\u96c9\u96ca\u96cb\u96cc\u96cd\u96ce\u96cf\u96d0\u96d1\u96d2\u96d3\u96d4\u96d5\u96d6\u96d7\u96d8\u96d9\u96da\u96db\u96dc\u96dd\u96de\u96df\u96e0\u96e1\u96e2\u96e3\u96e4\u96e5\u96e6\u96e7\u96e8\u96e9\u96ea\u96eb\u96ec\u96ed\u96ee\u96ef\u96f0\u96f1\u96f2\u96f3\u96f4\u96f5\u96f6\u96f7\u96f8\u96f9\u96fa\u96fb\u96fc\u96fd\u96fe\u96ff\u9700\u9701\u9702\u9703\u9704\u9705\u9706\u9707\u9708\u9709\u970a\u970b\u970c\u970d\u970e\u970f\u9710\u9711\u9712\u9713\u9714\u9715\u9716\u9717\u9718\u9719\u971a\u971b\u971c\u971d\u971e\u971f\u9720\u9721\u9722\u9723\u9724\u9725\u9726\u9727\u9728\u9729\u972a\u972b\u972c\u972d\u972e\u972f\u9730\u9731\u9732\u9733\u9734\u9735\u9736\u9737\u9738\u9739\u973a\u973b\u973c\u973d\u973e\u973f\u9740\u9741\u9742\u9743\u9744\u9745\u9746\u9747\u9748\u9749\u974a\u974b\u974c\u974d\u974e\u974f\u9750\u9751\u9752\u9753\u9754\u9755\u9756\u9757\u9758\u9759\u975a\u975b\u975c\u975d\u975e\u975f\u9760\u9761\u9762\u9763\u9764\u9765\u9766\u9767\u9768\u9769\u976a\u976b\u976c\u976d\u976e\u976f\u9770\u9771\u9772\u9773\u9774\u9775\u9776\u9777\u9778\u9779\u977a\u977b\u977c\u977d\u977e\u977f\u9780\u9781\u9782\u9783\u9784\u9785\u9786\u9787\u9788\u9789\u978a\u978b\u978c\u978d\u978e\u978f\u9790\u9791\u9792\u9793\u9794\u9795\u9796\u9797\u9798\u9799\u979a\u979b\u979c\u979d\u979e\u979f\u97a0\u97a1\u97a2\u97a3\u97a4\u97a5\u97a6\u97a7\u97a8\u97a9\u97aa\u97ab\u97ac\u97ad\u97ae\u97af\u97b0\u97b1\u97b2\u97b3\u97b4\u97b5\u97b6\u97b7\u97b8\u97b9\u97ba\u97bb\u97bc\u97bd\u97be\u97bf\u97c0\u97c1\u97c2\u97c3\u97c4\u97c5\u97c6\u97c7\u97c8\u97c9\u97ca\u97cb\u97cc\u97cd\u97ce\u97cf\u97d0\u97d1\u97d2\u97d3\u97d4\u97d5\u97d6\u97d7\u97d8\u97d9\u97da\u97db\u97dc\u97dd\u97de\u97df\u97e0\u97e1\u97e2\u97e3\u97e4\u97e5\u97e6\u97e7\u97e8\u97e9\u97ea\u97eb\u97ec\u97ed\u97ee\u97ef\u97f0\u97f1\u97f2\u97f3\u97f4\u97f5\u97f6\u97f7\u97f8\u97f9\u97fa\u97fb\u97fc\u97fd\u97fe\u97ff\u9800\u9801\u9802\u9803\u9804\u9805\u9806\u9807\u9808\u9809\u980a\u980b\u980c\u980d\u980e\u980f\u9810\u9811\u9812\u9813\u9814\u9815\u9816\u9817\u9818\u9819\u981a\u981b\u981c\u981d\u981e\u981f\u9820\u9821\u9822\u9823\u9824\u9825\u9826\u9827\u9828\u9829\u982a\u982b\u982c\u982d\u982e\u982f\u9830\u9831\u9832\u9833\u9834\u9835\u9836\u9837\u9838\u9839\u983a\u983b\u983c\u983d\u983e\u983f\u9840\u9841\u9842\u9843\u9844\u9845\u9846\u9847\u9848\u9849\u984a\u984b\u984c\u984d\u984e\u984f\u9850\u9851\u9852\u9853\u9854\u9855\u9856\u9857\u9858\u9859\u985a\u985b\u985c\u985d\u985e\u985f\u9860\u9861\u9862\u9863\u9864\u9865\u9866\u9867\u9868\u9869\u986a\u986b\u986c\u986d\u986e\u986f\u9870\u9871\u9872\u9873\u9874\u9875\u9876\u9877\u9878\u9879\u987a\u987b\u987c\u987d\u987e\u987f\u9880\u9881\u9882\u9883\u9884\u9885\u9886\u9887\u9888\u9889\u988a\u988b\u988c\u988d\u988e\u988f\u9890\u9891\u9892\u9893\u9894\u9895\u9896\u9897\u9898\u9899\u989a\u989b\u989c\u989d\u989e\u989f\u98a0\u98a1\u98a2\u98a3\u98a4\u98a5\u98a6\u98a7\u98a8\u98a9\u98aa\u98ab\u98ac\u98ad\u98ae\u98af\u98b0\u98b1\u98b2\u98b3\u98b4\u98b5\u98b6\u98b7\u98b8\u98b9\u98ba\u98bb\u98bc\u98bd\u98be\u98bf\u98c0\u98c1\u98c2\u98c3\u98c4\u98c5\u98c6\u98c7\u98c8\u98c9\u98ca\u98cb\u98cc\u98cd\u98ce\u98cf\u98d0\u98d1\u98d2\u98d3\u98d4\u98d5\u98d6\u98d7\u98d8\u98d9\u98da\u98db\u98dc\u98dd\u98de\u98df\u98e0\u98e1\u98e2\u98e3\u98e4\u98e5\u98e6\u98e7\u98e8\u98e9\u98ea\u98eb\u98ec\u98ed\u98ee\u98ef\u98f0\u98f1\u98f2\u98f3\u98f4\u98f5\u98f6\u98f7\u98f8\u98f9\u98fa\u98fb\u98fc\u98fd\u98fe\u98ff\u9900\u9901\u9902\u9903\u9904\u9905\u9906\u9907\u9908\u9909\u990a\u990b\u990c\u990d\u990e\u990f\u9910\u9911\u9912\u9913\u9914\u9915\u9916\u9917\u9918\u9919\u991a\u991b\u991c\u991d\u991e\u991f\u9920\u9921\u9922\u9923\u9924\u9925\u9926\u9927\u9928\u9929\u992a\u992b\u992c\u992d\u992e\u992f\u9930\u9931\u9932\u9933\u9934\u9935\u9936\u9937\u9938\u9939\u993a\u993b\u993c\u993d\u993e\u993f\u9940\u9941\u9942\u9943\u9944\u9945\u9946\u9947\u9948\u9949\u994a\u994b\u994c\u994d\u994e\u994f\u9950\u9951\u9952\u9953\u9954\u9955\u9956\u9957\u9958\u9959\u995a\u995b\u995c\u995d\u995e\u995f\u9960\u9961\u9962\u9963\u9964\u9965\u9966\u9967\u9968\u9969\u996a\u996b\u996c\u996d\u996e\u996f\u9970\u9971\u9972\u9973\u9974\u9975\u9976\u9977\u9978\u9979\u997a\u997b\u997c\u997d\u997e\u997f\u9980\u9981\u9982\u9983\u9984\u9985\u9986\u9987\u9988\u9989\u998a\u998b\u998c\u998d\u998e\u998f\u9990\u9991\u9992\u9993\u9994\u9995\u9996\u9997\u9998\u9999\u999a\u999b\u999c\u999d\u999e\u999f\u99a0\u99a1\u99a2\u99a3\u99a4\u99a5\u99a6\u99a7\u99a8\u99a9\u99aa\u99ab\u99ac\u99ad\u99ae\u99af\u99b0\u99b1\u99b2\u99b3\u99b4\u99b5\u99b6\u99b7\u99b8\u99b9\u99ba\u99bb\u99bc\u99bd\u99be\u99bf\u99c0\u99c1\u99c2\u99c3\u99c4\u99c5\u99c6\u99c7\u99c8\u99c9\u99ca\u99cb\u99cc\u99cd\u99ce\u99cf\u99d0\u99d1\u99d2\u99d3\u99d4\u99d5\u99d6\u99d7\u99d8\u99d9\u99da\u99db\u99dc\u99dd\u99de\u99df\u99e0\u99e1\u99e2\u99e3\u99e4\u99e5\u99e6\u99e7\u99e8\u99e9\u99ea\u99eb\u99ec\u99ed\u99ee\u99ef\u99f0\u99f1\u99f2\u99f3\u99f4\u99f5\u99f6\u99f7\u99f8\u99f9\u99fa\u99fb\u99fc\u99fd\u99fe\u99ff\u9a00\u9a01\u9a02\u9a03\u9a04\u9a05\u9a06\u9a07\u9a08\u9a09\u9a0a\u9a0b\u9a0c\u9a0d\u9a0e\u9a0f\u9a10\u9a11\u9a12\u9a13\u9a14\u9a15\u9a16\u9a17\u9a18\u9a19\u9a1a\u9a1b\u9a1c\u9a1d\u9a1e\u9a1f\u9a20\u9a21\u9a22\u9a23\u9a24\u9a25\u9a26\u9a27\u9a28\u9a29\u9a2a\u9a2b\u9a2c\u9a2d\u9a2e\u9a2f\u9a30\u9a31\u9a32\u9a33\u9a34\u9a35\u9a36\u9a37\u9a38\u9a39\u9a3a\u9a3b\u9a3c\u9a3d\u9a3e\u9a3f\u9a40\u9a41\u9a42\u9a43\u9a44\u9a45\u9a46\u9a47\u9a48\u9a49\u9a4a\u9a4b\u9a4c\u9a4d\u9a4e\u9a4f\u9a50\u9a51\u9a52\u9a53\u9a54\u9a55\u9a56\u9a57\u9a58\u9a59\u9a5a\u9a5b\u9a5c\u9a5d\u9a5e\u9a5f\u9a60\u9a61\u9a62\u9a63\u9a64\u9a65\u9a66\u9a67\u9a68\u9a69\u9a6a\u9a6b\u9a6c\u9a6d\u9a6e\u9a6f\u9a70\u9a71\u9a72\u9a73\u9a74\u9a75\u9a76\u9a77\u9a78\u9a79\u9a7a\u9a7b\u9a7c\u9a7d\u9a7e\u9a7f\u9a80\u9a81\u9a82\u9a83\u9a84\u9a85\u9a86\u9a87\u9a88\u9a89\u9a8a\u9a8b\u9a8c\u9a8d\u9a8e\u9a8f\u9a90\u9a91\u9a92\u9a93\u9a94\u9a95\u9a96\u9a97\u9a98\u9a99\u9a9a\u9a9b\u9a9c\u9a9d\u9a9e\u9a9f\u9aa0\u9aa1\u9aa2\u9aa3\u9aa4\u9aa5\u9aa6\u9aa7\u9aa8\u9aa9\u9aaa\u9aab\u9aac\u9aad\u9aae\u9aaf\u9ab0\u9ab1\u9ab2\u9ab3\u9ab4\u9ab5\u9ab6\u9ab7\u9ab8\u9ab9\u9aba\u9abb\u9abc\u9abd\u9abe\u9abf\u9ac0\u9ac1\u9ac2\u9ac3\u9ac4\u9ac5\u9ac6\u9ac7\u9ac8\u9ac9\u9aca\u9acb\u9acc\u9acd\u9ace\u9acf\u9ad0\u9ad1\u9ad2\u9ad3\u9ad4\u9ad5\u9ad6\u9ad7\u9ad8\u9ad9\u9ada\u9adb\u9adc\u9add\u9ade\u9adf\u9ae0\u9ae1\u9ae2\u9ae3\u9ae4\u9ae5\u9ae6\u9ae7\u9ae8\u9ae9\u9aea\u9aeb\u9aec\u9aed\u9aee\u9aef\u9af0\u9af1\u9af2\u9af3\u9af4\u9af5\u9af6\u9af7\u9af8\u9af9\u9afa\u9afb\u9afc\u9afd\u9afe\u9aff\u9b00\u9b01\u9b02\u9b03\u9b04\u9b05\u9b06\u9b07\u9b08\u9b09\u9b0a\u9b0b\u9b0c\u9b0d\u9b0e\u9b0f\u9b10\u9b11\u9b12\u9b13\u9b14\u9b15\u9b16\u9b17\u9b18\u9b19\u9b1a\u9b1b\u9b1c\u9b1d\u9b1e\u9b1f\u9b20\u9b21\u9b22\u9b23\u9b24\u9b25\u9b26\u9b27\u9b28\u9b29\u9b2a\u9b2b\u9b2c\u9b2d\u9b2e\u9b2f\u9b30\u9b31\u9b32\u9b33\u9b34\u9b35\u9b36\u9b37\u9b38\u9b39\u9b3a\u9b3b\u9b3c\u9b3d\u9b3e\u9b3f\u9b40\u9b41\u9b42\u9b43\u9b44\u9b45\u9b46\u9b47\u9b48\u9b49\u9b4a\u9b4b\u9b4c\u9b4d\u9b4e\u9b4f\u9b50\u9b51\u9b52\u9b53\u9b54\u9b55\u9b56\u9b57\u9b58\u9b59\u9b5a\u9b5b\u9b5c\u9b5d\u9b5e\u9b5f\u9b60\u9b61\u9b62\u9b63\u9b64\u9b65\u9b66\u9b67\u9b68\u9b69\u9b6a\u9b6b\u9b6c\u9b6d\u9b6e\u9b6f\u9b70\u9b71\u9b72\u9b73\u9b74\u9b75\u9b76\u9b77\u9b78\u9b79\u9b7a\u9b7b\u9b7c\u9b7d\u9b7e\u9b7f\u9b80\u9b81\u9b82\u9b83\u9b84\u9b85\u9b86\u9b87\u9b88\u9b89\u9b8a\u9b8b\u9b8c\u9b8d\u9b8e\u9b8f\u9b90\u9b91\u9b92\u9b93\u9b94\u9b95\u9b96\u9b97\u9b98\u9b99\u9b9a\u9b9b\u9b9c\u9b9d\u9b9e\u9b9f\u9ba0\u9ba1\u9ba2\u9ba3\u9ba4\u9ba5\u9ba6\u9ba7\u9ba8\u9ba9\u9baa\u9bab\u9bac\u9bad\u9bae\u9baf\u9bb0\u9bb1\u9bb2\u9bb3\u9bb4\u9bb5\u9bb6\u9bb7\u9bb8\u9bb9\u9bba\u9bbb\u9bbc\u9bbd\u9bbe\u9bbf\u9bc0\u9bc1\u9bc2\u9bc3\u9bc4\u9bc5\u9bc6\u9bc7\u9bc8\u9bc9\u9bca\u9bcb\u9bcc\u9bcd\u9bce\u9bcf\u9bd0\u9bd1\u9bd2\u9bd3\u9bd4\u9bd5\u9bd6\u9bd7\u9bd8\u9bd9\u9bda\u9bdb\u9bdc\u9bdd\u9bde\u9bdf\u9be0\u9be1\u9be2\u9be3\u9be4\u9be5\u9be6\u9be7\u9be8\u9be9\u9bea\u9beb\u9bec\u9bed\u9bee\u9bef\u9bf0\u9bf1\u9bf2\u9bf3\u9bf4\u9bf5\u9bf6\u9bf7\u9bf8\u9bf9\u9bfa\u9bfb\u9bfc\u9bfd\u9bfe\u9bff\u9c00\u9c01\u9c02\u9c03\u9c04\u9c05\u9c06\u9c07\u9c08\u9c09\u9c0a\u9c0b\u9c0c\u9c0d\u9c0e\u9c0f\u9c10\u9c11\u9c12\u9c13\u9c14\u9c15\u9c16\u9c17\u9c18\u9c19\u9c1a\u9c1b\u9c1c\u9c1d\u9c1e\u9c1f\u9c20\u9c21\u9c22\u9c23\u9c24\u9c25\u9c26\u9c27\u9c28\u9c29\u9c2a\u9c2b\u9c2c\u9c2d\u9c2e\u9c2f\u9c30\u9c31\u9c32\u9c33\u9c34\u9c35\u9c36\u9c37\u9c38\u9c39\u9c3a\u9c3b\u9c3c\u9c3d\u9c3e\u9c3f\u9c40\u9c41\u9c42\u9c43\u9c44\u9c45\u9c46\u9c47\u9c48\u9c49\u9c4a\u9c4b\u9c4c\u9c4d\u9c4e\u9c4f\u9c50\u9c51\u9c52\u9c53\u9c54\u9c55\u9c56\u9c57\u9c58\u9c59\u9c5a\u9c5b\u9c5c\u9c5d\u9c5e\u9c5f\u9c60\u9c61\u9c62\u9c63\u9c64\u9c65\u9c66\u9c67\u9c68\u9c69\u9c6a\u9c6b\u9c6c\u9c6d\u9c6e\u9c6f\u9c70\u9c71\u9c72\u9c73\u9c74\u9c75\u9c76\u9c77\u9c78\u9c79\u9c7a\u9c7b\u9c7c\u9c7d\u9c7e\u9c7f\u9c80\u9c81\u9c82\u9c83\u9c84\u9c85\u9c86\u9c87\u9c88\u9c89\u9c8a\u9c8b\u9c8c\u9c8d\u9c8e\u9c8f\u9c90\u9c91\u9c92\u9c93\u9c94\u9c95\u9c96\u9c97\u9c98\u9c99\u9c9a\u9c9b\u9c9c\u9c9d\u9c9e\u9c9f\u9ca0\u9ca1\u9ca2\u9ca3\u9ca4\u9ca5\u9ca6\u9ca7\u9ca8\u9ca9\u9caa\u9cab\u9cac\u9cad\u9cae\u9caf\u9cb0\u9cb1\u9cb2\u9cb3\u9cb4\u9cb5\u9cb6\u9cb7\u9cb8\u9cb9\u9cba\u9cbb\u9cbc\u9cbd\u9cbe\u9cbf\u9cc0\u9cc1\u9cc2\u9cc3\u9cc4\u9cc5\u9cc6\u9cc7\u9cc8\u9cc9\u9cca\u9ccb\u9ccc\u9ccd\u9cce\u9ccf\u9cd0\u9cd1\u9cd2\u9cd3\u9cd4\u9cd5\u9cd6\u9cd7\u9cd8\u9cd9\u9cda\u9cdb\u9cdc\u9cdd\u9cde\u9cdf\u9ce0\u9ce1\u9ce2\u9ce3\u9ce4\u9ce5\u9ce6\u9ce7\u9ce8\u9ce9\u9cea\u9ceb\u9cec\u9ced\u9cee\u9cef\u9cf0\u9cf1\u9cf2\u9cf3\u9cf4\u9cf5\u9cf6\u9cf7\u9cf8\u9cf9\u9cfa\u9cfb\u9cfc\u9cfd\u9cfe\u9cff\u9d00\u9d01\u9d02\u9d03\u9d04\u9d05\u9d06\u9d07\u9d08\u9d09\u9d0a\u9d0b\u9d0c\u9d0d\u9d0e\u9d0f\u9d10\u9d11\u9d12\u9d13\u9d14\u9d15\u9d16\u9d17\u9d18\u9d19\u9d1a\u9d1b\u9d1c\u9d1d\u9d1e\u9d1f\u9d20\u9d21\u9d22\u9d23\u9d24\u9d25\u9d26\u9d27\u9d28\u9d29\u9d2a\u9d2b\u9d2c\u9d2d\u9d2e\u9d2f\u9d30\u9d31\u9d32\u9d33\u9d34\u9d35\u9d36\u9d37\u9d38\u9d39\u9d3a\u9d3b\u9d3c\u9d3d\u9d3e\u9d3f\u9d40\u9d41\u9d42\u9d43\u9d44\u9d45\u9d46\u9d47\u9d48\u9d49\u9d4a\u9d4b\u9d4c\u9d4d\u9d4e\u9d4f\u9d50\u9d51\u9d52\u9d53\u9d54\u9d55\u9d56\u9d57\u9d58\u9d59\u9d5a\u9d5b\u9d5c\u9d5d\u9d5e\u9d5f\u9d60\u9d61\u9d62\u9d63\u9d64\u9d65\u9d66\u9d67\u9d68\u9d69\u9d6a\u9d6b\u9d6c\u9d6d\u9d6e\u9d6f\u9d70\u9d71\u9d72\u9d73\u9d74\u9d75\u9d76\u9d77\u9d78\u9d79\u9d7a\u9d7b\u9d7c\u9d7d\u9d7e\u9d7f\u9d80\u9d81\u9d82\u9d83\u9d84\u9d85\u9d86\u9d87\u9d88\u9d89\u9d8a\u9d8b\u9d8c\u9d8d\u9d8e\u9d8f\u9d90\u9d91\u9d92\u9d93\u9d94\u9d95\u9d96\u9d97\u9d98\u9d99\u9d9a\u9d9b\u9d9c\u9d9d\u9d9e\u9d9f\u9da0\u9da1\u9da2\u9da3\u9da4\u9da5\u9da6\u9da7\u9da8\u9da9\u9daa\u9dab\u9dac\u9dad\u9dae\u9daf\u9db0\u9db1\u9db2\u9db3\u9db4\u9db5\u9db6\u9db7\u9db8\u9db9\u9dba\u9dbb\u9dbc\u9dbd\u9dbe\u9dbf\u9dc0\u9dc1\u9dc2\u9dc3\u9dc4\u9dc5\u9dc6\u9dc7\u9dc8\u9dc9\u9dca\u9dcb\u9dcc\u9dcd\u9dce\u9dcf\u9dd0\u9dd1\u9dd2\u9dd3\u9dd4\u9dd5\u9dd6\u9dd7\u9dd8\u9dd9\u9dda\u9ddb\u9ddc\u9ddd\u9dde\u9ddf\u9de0\u9de1\u9de2\u9de3\u9de4\u9de5\u9de6\u9de7\u9de8\u9de9\u9dea\u9deb\u9dec\u9ded\u9dee\u9def\u9df0\u9df1\u9df2\u9df3\u9df4\u9df5\u9df6\u9df7\u9df8\u9df9\u9dfa\u9dfb\u9dfc\u9dfd\u9dfe\u9dff\u9e00\u9e01\u9e02\u9e03\u9e04\u9e05\u9e06\u9e07\u9e08\u9e09\u9e0a\u9e0b\u9e0c\u9e0d\u9e0e\u9e0f\u9e10\u9e11\u9e12\u9e13\u9e14\u9e15\u9e16\u9e17\u9e18\u9e19\u9e1a\u9e1b\u9e1c\u9e1d\u9e1e\u9e1f\u9e20\u9e21\u9e22\u9e23\u9e24\u9e25\u9e26\u9e27\u9e28\u9e29\u9e2a\u9e2b\u9e2c\u9e2d\u9e2e\u9e2f\u9e30\u9e31\u9e32\u9e33\u9e34\u9e35\u9e36\u9e37\u9e38\u9e39\u9e3a\u9e3b\u9e3c\u9e3d\u9e3e\u9e3f\u9e40\u9e41\u9e42\u9e43\u9e44\u9e45\u9e46\u9e47\u9e48\u9e49\u9e4a\u9e4b\u9e4c\u9e4d\u9e4e\u9e4f\u9e50\u9e51\u9e52\u9e53\u9e54\u9e55\u9e56\u9e57\u9e58\u9e59\u9e5a\u9e5b\u9e5c\u9e5d\u9e5e\u9e5f\u9e60\u9e61\u9e62\u9e63\u9e64\u9e65\u9e66\u9e67\u9e68\u9e69\u9e6a\u9e6b\u9e6c\u9e6d\u9e6e\u9e6f\u9e70\u9e71\u9e72\u9e73\u9e74\u9e75\u9e76\u9e77\u9e78\u9e79\u9e7a\u9e7b\u9e7c\u9e7d\u9e7e\u9e7f\u9e80\u9e81\u9e82\u9e83\u9e84\u9e85\u9e86\u9e87\u9e88\u9e89\u9e8a\u9e8b\u9e8c\u9e8d\u9e8e\u9e8f\u9e90\u9e91\u9e92\u9e93\u9e94\u9e95\u9e96\u9e97\u9e98\u9e99\u9e9a\u9e9b\u9e9c\u9e9d\u9e9e\u9e9f\u9ea0\u9ea1\u9ea2\u9ea3\u9ea4\u9ea5\u9ea6\u9ea7\u9ea8\u9ea9\u9eaa\u9eab\u9eac\u9ead\u9eae\u9eaf\u9eb0\u9eb1\u9eb2\u9eb3\u9eb4\u9eb5\u9eb6\u9eb7\u9eb8\u9eb9\u9eba\u9ebb\u9ebc\u9ebd\u9ebe\u9ebf\u9ec0\u9ec1\u9ec2\u9ec3\u9ec4\u9ec5\u9ec6\u9ec7\u9ec8\u9ec9\u9eca\u9ecb\u9ecc\u9ecd\u9ece\u9ecf\u9ed0\u9ed1\u9ed2\u9ed3\u9ed4\u9ed5\u9ed6\u9ed7\u9ed8\u9ed9\u9eda\u9edb\u9edc\u9edd\u9ede\u9edf\u9ee0\u9ee1\u9ee2\u9ee3\u9ee4\u9ee5\u9ee6\u9ee7\u9ee8\u9ee9\u9eea\u9eeb\u9eec\u9eed\u9eee\u9eef\u9ef0\u9ef1\u9ef2\u9ef3\u9ef4\u9ef5\u9ef6\u9ef7\u9ef8\u9ef9\u9efa\u9efb\u9efc\u9efd\u9efe\u9eff\u9f00\u9f01\u9f02\u9f03\u9f04\u9f05\u9f06\u9f07\u9f08\u9f09\u9f0a\u9f0b\u9f0c\u9f0d\u9f0e\u9f0f\u9f10\u9f11\u9f12\u9f13\u9f14\u9f15\u9f16\u9f17\u9f18\u9f19\u9f1a\u9f1b\u9f1c\u9f1d\u9f1e\u9f1f\u9f20\u9f21\u9f22\u9f23\u9f24\u9f25\u9f26\u9f27\u9f28\u9f29\u9f2a\u9f2b\u9f2c\u9f2d\u9f2e\u9f2f\u9f30\u9f31\u9f32\u9f33\u9f34\u9f35\u9f36\u9f37\u9f38\u9f39\u9f3a\u9f3b\u9f3c\u9f3d\u9f3e\u9f3f\u9f40\u9f41\u9f42\u9f43\u9f44\u9f45\u9f46\u9f47\u9f48\u9f49\u9f4a\u9f4b\u9f4c\u9f4d\u9f4e\u9f4f\u9f50\u9f51\u9f52\u9f53\u9f54\u9f55\u9f56\u9f57\u9f58\u9f59\u9f5a\u9f5b\u9f5c\u9f5d\u9f5e\u9f5f\u9f60\u9f61\u9f62\u9f63\u9f64\u9f65\u9f66\u9f67\u9f68\u9f69\u9f6a\u9f6b\u9f6c\u9f6d\u9f6e\u9f6f\u9f70\u9f71\u9f72\u9f73\u9f74\u9f75\u9f76\u9f77\u9f78\u9f79\u9f7a\u9f7b\u9f7c\u9f7d\u9f7e\u9f7f\u9f80\u9f81\u9f82\u9f83\u9f84\u9f85\u9f86\u9f87\u9f88\u9f89\u9f8a\u9f8b\u9f8c\u9f8d\u9f8e\u9f8f\u9f90\u9f91\u9f92\u9f93\u9f94\u9f95\u9f96\u9f97\u9f98\u9f99\u9f9a\u9f9b\u9f9c\u9f9d\u9f9e\u9f9f\u9fa0\u9fa1\u9fa2\u9fa3\u9fa4\u9fa5\u9fa6\u9fa7\u9fa8\u9fa9\u9faa\u9fab\u9fac\u9fad\u9fae\u9faf\u9fb0\u9fb1\u9fb2\u9fb3\u9fb4\u9fb5\u9fb6\u9fb7\u9fb8\u9fb9\u9fba\u9fbb\ua000\ua001\ua002\ua003\ua004\ua005\ua006\ua007\ua008\ua009\ua00a\ua00b\ua00c\ua00d\ua00e\ua00f\ua010\ua011\ua012\ua013\ua014\ua016\ua017\ua018\ua019\ua01a\ua01b\ua01c\ua01d\ua01e\ua01f\ua020\ua021\ua022\ua023\ua024\ua025\ua026\ua027\ua028\ua029\ua02a\ua02b\ua02c\ua02d\ua02e\ua02f\ua030\ua031\ua032\ua033\ua034\ua035\ua036\ua037\ua038\ua039\ua03a\ua03b\ua03c\ua03d\ua03e\ua03f\ua040\ua041\ua042\ua043\ua044\ua045\ua046\ua047\ua048\ua049\ua04a\ua04b\ua04c\ua04d\ua04e\ua04f\ua050\ua051\ua052\ua053\ua054\ua055\ua056\ua057\ua058\ua059\ua05a\ua05b\ua05c\ua05d\ua05e\ua05f\ua060\ua061\ua062\ua063\ua064\ua065\ua066\ua067\ua068\ua069\ua06a\ua06b\ua06c\ua06d\ua06e\ua06f\ua070\ua071\ua072\ua073\ua074\ua075\ua076\ua077\ua078\ua079\ua07a\ua07b\ua07c\ua07d\ua07e\ua07f\ua080\ua081\ua082\ua083\ua084\ua085\ua086\ua087\ua088\ua089\ua08a\ua08b\ua08c\ua08d\ua08e\ua08f\ua090\ua091\ua092\ua093\ua094\ua095\ua096\ua097\ua098\ua099\ua09a\ua09b\ua09c\ua09d\ua09e\ua09f\ua0a0\ua0a1\ua0a2\ua0a3\ua0a4\ua0a5\ua0a6\ua0a7\ua0a8\ua0a9\ua0aa\ua0ab\ua0ac\ua0ad\ua0ae\ua0af\ua0b0\ua0b1\ua0b2\ua0b3\ua0b4\ua0b5\ua0b6\ua0b7\ua0b8\ua0b9\ua0ba\ua0bb\ua0bc\ua0bd\ua0be\ua0bf\ua0c0\ua0c1\ua0c2\ua0c3\ua0c4\ua0c5\ua0c6\ua0c7\ua0c8\ua0c9\ua0ca\ua0cb\ua0cc\ua0cd\ua0ce\ua0cf\ua0d0\ua0d1\ua0d2\ua0d3\ua0d4\ua0d5\ua0d6\ua0d7\ua0d8\ua0d9\ua0da\ua0db\ua0dc\ua0dd\ua0de\ua0df\ua0e0\ua0e1\ua0e2\ua0e3\ua0e4\ua0e5\ua0e6\ua0e7\ua0e8\ua0e9\ua0ea\ua0eb\ua0ec\ua0ed\ua0ee\ua0ef\ua0f0\ua0f1\ua0f2\ua0f3\ua0f4\ua0f5\ua0f6\ua0f7\ua0f8\ua0f9\ua0fa\ua0fb\ua0fc\ua0fd\ua0fe\ua0ff\ua100\ua101\ua102\ua103\ua104\ua105\ua106\ua107\ua108\ua109\ua10a\ua10b\ua10c\ua10d\ua10e\ua10f\ua110\ua111\ua112\ua113\ua114\ua115\ua116\ua117\ua118\ua119\ua11a\ua11b\ua11c\ua11d\ua11e\ua11f\ua120\ua121\ua122\ua123\ua124\ua125\ua126\ua127\ua128\ua129\ua12a\ua12b\ua12c\ua12d\ua12e\ua12f\ua130\ua131\ua132\ua133\ua134\ua135\ua136\ua137\ua138\ua139\ua13a\ua13b\ua13c\ua13d\ua13e\ua13f\ua140\ua141\ua142\ua143\ua144\ua145\ua146\ua147\ua148\ua149\ua14a\ua14b\ua14c\ua14d\ua14e\ua14f\ua150\ua151\ua152\ua153\ua154\ua155\ua156\ua157\ua158\ua159\ua15a\ua15b\ua15c\ua15d\ua15e\ua15f\ua160\ua161\ua162\ua163\ua164\ua165\ua166\ua167\ua168\ua169\ua16a\ua16b\ua16c\ua16d\ua16e\ua16f\ua170\ua171\ua172\ua173\ua174\ua175\ua176\ua177\ua178\ua179\ua17a\ua17b\ua17c\ua17d\ua17e\ua17f\ua180\ua181\ua182\ua183\ua184\ua185\ua186\ua187\ua188\ua189\ua18a\ua18b\ua18c\ua18d\ua18e\ua18f\ua190\ua191\ua192\ua193\ua194\ua195\ua196\ua197\ua198\ua199\ua19a\ua19b\ua19c\ua19d\ua19e\ua19f\ua1a0\ua1a1\ua1a2\ua1a3\ua1a4\ua1a5\ua1a6\ua1a7\ua1a8\ua1a9\ua1aa\ua1ab\ua1ac\ua1ad\ua1ae\ua1af\ua1b0\ua1b1\ua1b2\ua1b3\ua1b4\ua1b5\ua1b6\ua1b7\ua1b8\ua1b9\ua1ba\ua1bb\ua1bc\ua1bd\ua1be\ua1bf\ua1c0\ua1c1\ua1c2\ua1c3\ua1c4\ua1c5\ua1c6\ua1c7\ua1c8\ua1c9\ua1ca\ua1cb\ua1cc\ua1cd\ua1ce\ua1cf\ua1d0\ua1d1\ua1d2\ua1d3\ua1d4\ua1d5\ua1d6\ua1d7\ua1d8\ua1d9\ua1da\ua1db\ua1dc\ua1dd\ua1de\ua1df\ua1e0\ua1e1\ua1e2\ua1e3\ua1e4\ua1e5\ua1e6\ua1e7\ua1e8\ua1e9\ua1ea\ua1eb\ua1ec\ua1ed\ua1ee\ua1ef\ua1f0\ua1f1\ua1f2\ua1f3\ua1f4\ua1f5\ua1f6\ua1f7\ua1f8\ua1f9\ua1fa\ua1fb\ua1fc\ua1fd\ua1fe\ua1ff\ua200\ua201\ua202\ua203\ua204\ua205\ua206\ua207\ua208\ua209\ua20a\ua20b\ua20c\ua20d\ua20e\ua20f\ua210\ua211\ua212\ua213\ua214\ua215\ua216\ua217\ua218\ua219\ua21a\ua21b\ua21c\ua21d\ua21e\ua21f\ua220\ua221\ua222\ua223\ua224\ua225\ua226\ua227\ua228\ua229\ua22a\ua22b\ua22c\ua22d\ua22e\ua22f\ua230\ua231\ua232\ua233\ua234\ua235\ua236\ua237\ua238\ua239\ua23a\ua23b\ua23c\ua23d\ua23e\ua23f\ua240\ua241\ua242\ua243\ua244\ua245\ua246\ua247\ua248\ua249\ua24a\ua24b\ua24c\ua24d\ua24e\ua24f\ua250\ua251\ua252\ua253\ua254\ua255\ua256\ua257\ua258\ua259\ua25a\ua25b\ua25c\ua25d\ua25e\ua25f\ua260\ua261\ua262\ua263\ua264\ua265\ua266\ua267\ua268\ua269\ua26a\ua26b\ua26c\ua26d\ua26e\ua26f\ua270\ua271\ua272\ua273\ua274\ua275\ua276\ua277\ua278\ua279\ua27a\ua27b\ua27c\ua27d\ua27e\ua27f\ua280\ua281\ua282\ua283\ua284\ua285\ua286\ua287\ua288\ua289\ua28a\ua28b\ua28c\ua28d\ua28e\ua28f\ua290\ua291\ua292\ua293\ua294\ua295\ua296\ua297\ua298\ua299\ua29a\ua29b\ua29c\ua29d\ua29e\ua29f\ua2a0\ua2a1\ua2a2\ua2a3\ua2a4\ua2a5\ua2a6\ua2a7\ua2a8\ua2a9\ua2aa\ua2ab\ua2ac\ua2ad\ua2ae\ua2af\ua2b0\ua2b1\ua2b2\ua2b3\ua2b4\ua2b5\ua2b6\ua2b7\ua2b8\ua2b9\ua2ba\ua2bb\ua2bc\ua2bd\ua2be\ua2bf\ua2c0\ua2c1\ua2c2\ua2c3\ua2c4\ua2c5\ua2c6\ua2c7\ua2c8\ua2c9\ua2ca\ua2cb\ua2cc\ua2cd\ua2ce\ua2cf\ua2d0\ua2d1\ua2d2\ua2d3\ua2d4\ua2d5\ua2d6\ua2d7\ua2d8\ua2d9\ua2da\ua2db\ua2dc\ua2dd\ua2de\ua2df\ua2e0\ua2e1\ua2e2\ua2e3\ua2e4\ua2e5\ua2e6\ua2e7\ua2e8\ua2e9\ua2ea\ua2eb\ua2ec\ua2ed\ua2ee\ua2ef\ua2f0\ua2f1\ua2f2\ua2f3\ua2f4\ua2f5\ua2f6\ua2f7\ua2f8\ua2f9\ua2fa\ua2fb\ua2fc\ua2fd\ua2fe\ua2ff\ua300\ua301\ua302\ua303\ua304\ua305\ua306\ua307\ua308\ua309\ua30a\ua30b\ua30c\ua30d\ua30e\ua30f\ua310\ua311\ua312\ua313\ua314\ua315\ua316\ua317\ua318\ua319\ua31a\ua31b\ua31c\ua31d\ua31e\ua31f\ua320\ua321\ua322\ua323\ua324\ua325\ua326\ua327\ua328\ua329\ua32a\ua32b\ua32c\ua32d\ua32e\ua32f\ua330\ua331\ua332\ua333\ua334\ua335\ua336\ua337\ua338\ua339\ua33a\ua33b\ua33c\ua33d\ua33e\ua33f\ua340\ua341\ua342\ua343\ua344\ua345\ua346\ua347\ua348\ua349\ua34a\ua34b\ua34c\ua34d\ua34e\ua34f\ua350\ua351\ua352\ua353\ua354\ua355\ua356\ua357\ua358\ua359\ua35a\ua35b\ua35c\ua35d\ua35e\ua35f\ua360\ua361\ua362\ua363\ua364\ua365\ua366\ua367\ua368\ua369\ua36a\ua36b\ua36c\ua36d\ua36e\ua36f\ua370\ua371\ua372\ua373\ua374\ua375\ua376\ua377\ua378\ua379\ua37a\ua37b\ua37c\ua37d\ua37e\ua37f\ua380\ua381\ua382\ua383\ua384\ua385\ua386\ua387\ua388\ua389\ua38a\ua38b\ua38c\ua38d\ua38e\ua38f\ua390\ua391\ua392\ua393\ua394\ua395\ua396\ua397\ua398\ua399\ua39a\ua39b\ua39c\ua39d\ua39e\ua39f\ua3a0\ua3a1\ua3a2\ua3a3\ua3a4\ua3a5\ua3a6\ua3a7\ua3a8\ua3a9\ua3aa\ua3ab\ua3ac\ua3ad\ua3ae\ua3af\ua3b0\ua3b1\ua3b2\ua3b3\ua3b4\ua3b5\ua3b6\ua3b7\ua3b8\ua3b9\ua3ba\ua3bb\ua3bc\ua3bd\ua3be\ua3bf\ua3c0\ua3c1\ua3c2\ua3c3\ua3c4\ua3c5\ua3c6\ua3c7\ua3c8\ua3c9\ua3ca\ua3cb\ua3cc\ua3cd\ua3ce\ua3cf\ua3d0\ua3d1\ua3d2\ua3d3\ua3d4\ua3d5\ua3d6\ua3d7\ua3d8\ua3d9\ua3da\ua3db\ua3dc\ua3dd\ua3de\ua3df\ua3e0\ua3e1\ua3e2\ua3e3\ua3e4\ua3e5\ua3e6\ua3e7\ua3e8\ua3e9\ua3ea\ua3eb\ua3ec\ua3ed\ua3ee\ua3ef\ua3f0\ua3f1\ua3f2\ua3f3\ua3f4\ua3f5\ua3f6\ua3f7\ua3f8\ua3f9\ua3fa\ua3fb\ua3fc\ua3fd\ua3fe\ua3ff\ua400\ua401\ua402\ua403\ua404\ua405\ua406\ua407\ua408\ua409\ua40a\ua40b\ua40c\ua40d\ua40e\ua40f\ua410\ua411\ua412\ua413\ua414\ua415\ua416\ua417\ua418\ua419\ua41a\ua41b\ua41c\ua41d\ua41e\ua41f\ua420\ua421\ua422\ua423\ua424\ua425\ua426\ua427\ua428\ua429\ua42a\ua42b\ua42c\ua42d\ua42e\ua42f\ua430\ua431\ua432\ua433\ua434\ua435\ua436\ua437\ua438\ua439\ua43a\ua43b\ua43c\ua43d\ua43e\ua43f\ua440\ua441\ua442\ua443\ua444\ua445\ua446\ua447\ua448\ua449\ua44a\ua44b\ua44c\ua44d\ua44e\ua44f\ua450\ua451\ua452\ua453\ua454\ua455\ua456\ua457\ua458\ua459\ua45a\ua45b\ua45c\ua45d\ua45e\ua45f\ua460\ua461\ua462\ua463\ua464\ua465\ua466\ua467\ua468\ua469\ua46a\ua46b\ua46c\ua46d\ua46e\ua46f\ua470\ua471\ua472\ua473\ua474\ua475\ua476\ua477\ua478\ua479\ua47a\ua47b\ua47c\ua47d\ua47e\ua47f\ua480\ua481\ua482\ua483\ua484\ua485\ua486\ua487\ua488\ua489\ua48a\ua48b\ua48c\ua800\ua801\ua803\ua804\ua805\ua807\ua808\ua809\ua80a\ua80c\ua80d\ua80e\ua80f\ua810\ua811\ua812\ua813\ua814\ua815\ua816\ua817\ua818\ua819\ua81a\ua81b\ua81c\ua81d\ua81e\ua81f\ua820\ua821\ua822\uac00\uac01\uac02\uac03\uac04\uac05\uac06\uac07\uac08\uac09\uac0a\uac0b\uac0c\uac0d\uac0e\uac0f\uac10\uac11\uac12\uac13\uac14\uac15\uac16\uac17\uac18\uac19\uac1a\uac1b\uac1c\uac1d\uac1e\uac1f\uac20\uac21\uac22\uac23\uac24\uac25\uac26\uac27\uac28\uac29\uac2a\uac2b\uac2c\uac2d\uac2e\uac2f\uac30\uac31\uac32\uac33\uac34\uac35\uac36\uac37\uac38\uac39\uac3a\uac3b\uac3c\uac3d\uac3e\uac3f\uac40\uac41\uac42\uac43\uac44\uac45\uac46\uac47\uac48\uac49\uac4a\uac4b\uac4c\uac4d\uac4e\uac4f\uac50\uac51\uac52\uac53\uac54\uac55\uac56\uac57\uac58\uac59\uac5a\uac5b\uac5c\uac5d\uac5e\uac5f\uac60\uac61\uac62\uac63\uac64\uac65\uac66\uac67\uac68\uac69\uac6a\uac6b\uac6c\uac6d\uac6e\uac6f\uac70\uac71\uac72\uac73\uac74\uac75\uac76\uac77\uac78\uac79\uac7a\uac7b\uac7c\uac7d\uac7e\uac7f\uac80\uac81\uac82\uac83\uac84\uac85\uac86\uac87\uac88\uac89\uac8a\uac8b\uac8c\uac8d\uac8e\uac8f\uac90\uac91\uac92\uac93\uac94\uac95\uac96\uac97\uac98\uac99\uac9a\uac9b\uac9c\uac9d\uac9e\uac9f\uaca0\uaca1\uaca2\uaca3\uaca4\uaca5\uaca6\uaca7\uaca8\uaca9\uacaa\uacab\uacac\uacad\uacae\uacaf\uacb0\uacb1\uacb2\uacb3\uacb4\uacb5\uacb6\uacb7\uacb8\uacb9\uacba\uacbb\uacbc\uacbd\uacbe\uacbf\uacc0\uacc1\uacc2\uacc3\uacc4\uacc5\uacc6\uacc7\uacc8\uacc9\uacca\uaccb\uaccc\uaccd\uacce\uaccf\uacd0\uacd1\uacd2\uacd3\uacd4\uacd5\uacd6\uacd7\uacd8\uacd9\uacda\uacdb\uacdc\uacdd\uacde\uacdf\uace0\uace1\uace2\uace3\uace4\uace5\uace6\uace7\uace8\uace9\uacea\uaceb\uacec\uaced\uacee\uacef\uacf0\uacf1\uacf2\uacf3\uacf4\uacf5\uacf6\uacf7\uacf8\uacf9\uacfa\uacfb\uacfc\uacfd\uacfe\uacff\uad00\uad01\uad02\uad03\uad04\uad05\uad06\uad07\uad08\uad09\uad0a\uad0b\uad0c\uad0d\uad0e\uad0f\uad10\uad11\uad12\uad13\uad14\uad15\uad16\uad17\uad18\uad19\uad1a\uad1b\uad1c\uad1d\uad1e\uad1f\uad20\uad21\uad22\uad23\uad24\uad25\uad26\uad27\uad28\uad29\uad2a\uad2b\uad2c\uad2d\uad2e\uad2f\uad30\uad31\uad32\uad33\uad34\uad35\uad36\uad37\uad38\uad39\uad3a\uad3b\uad3c\uad3d\uad3e\uad3f\uad40\uad41\uad42\uad43\uad44\uad45\uad46\uad47\uad48\uad49\uad4a\uad4b\uad4c\uad4d\uad4e\uad4f\uad50\uad51\uad52\uad53\uad54\uad55\uad56\uad57\uad58\uad59\uad5a\uad5b\uad5c\uad5d\uad5e\uad5f\uad60\uad61\uad62\uad63\uad64\uad65\uad66\uad67\uad68\uad69\uad6a\uad6b\uad6c\uad6d\uad6e\uad6f\uad70\uad71\uad72\uad73\uad74\uad75\uad76\uad77\uad78\uad79\uad7a\uad7b\uad7c\uad7d\uad7e\uad7f\uad80\uad81\uad82\uad83\uad84\uad85\uad86\uad87\uad88\uad89\uad8a\uad8b\uad8c\uad8d\uad8e\uad8f\uad90\uad91\uad92\uad93\uad94\uad95\uad96\uad97\uad98\uad99\uad9a\uad9b\uad9c\uad9d\uad9e\uad9f\uada0\uada1\uada2\uada3\uada4\uada5\uada6\uada7\uada8\uada9\uadaa\uadab\uadac\uadad\uadae\uadaf\uadb0\uadb1\uadb2\uadb3\uadb4\uadb5\uadb6\uadb7\uadb8\uadb9\uadba\uadbb\uadbc\uadbd\uadbe\uadbf\uadc0\uadc1\uadc2\uadc3\uadc4\uadc5\uadc6\uadc7\uadc8\uadc9\uadca\uadcb\uadcc\uadcd\uadce\uadcf\uadd0\uadd1\uadd2\uadd3\uadd4\uadd5\uadd6\uadd7\uadd8\uadd9\uadda\uaddb\uaddc\uaddd\uadde\uaddf\uade0\uade1\uade2\uade3\uade4\uade5\uade6\uade7\uade8\uade9\uadea\uadeb\uadec\uaded\uadee\uadef\uadf0\uadf1\uadf2\uadf3\uadf4\uadf5\uadf6\uadf7\uadf8\uadf9\uadfa\uadfb\uadfc\uadfd\uadfe\uadff\uae00\uae01\uae02\uae03\uae04\uae05\uae06\uae07\uae08\uae09\uae0a\uae0b\uae0c\uae0d\uae0e\uae0f\uae10\uae11\uae12\uae13\uae14\uae15\uae16\uae17\uae18\uae19\uae1a\uae1b\uae1c\uae1d\uae1e\uae1f\uae20\uae21\uae22\uae23\uae24\uae25\uae26\uae27\uae28\uae29\uae2a\uae2b\uae2c\uae2d\uae2e\uae2f\uae30\uae31\uae32\uae33\uae34\uae35\uae36\uae37\uae38\uae39\uae3a\uae3b\uae3c\uae3d\uae3e\uae3f\uae40\uae41\uae42\uae43\uae44\uae45\uae46\uae47\uae48\uae49\uae4a\uae4b\uae4c\uae4d\uae4e\uae4f\uae50\uae51\uae52\uae53\uae54\uae55\uae56\uae57\uae58\uae59\uae5a\uae5b\uae5c\uae5d\uae5e\uae5f\uae60\uae61\uae62\uae63\uae64\uae65\uae66\uae67\uae68\uae69\uae6a\uae6b\uae6c\uae6d\uae6e\uae6f\uae70\uae71\uae72\uae73\uae74\uae75\uae76\uae77\uae78\uae79\uae7a\uae7b\uae7c\uae7d\uae7e\uae7f\uae80\uae81\uae82\uae83\uae84\uae85\uae86\uae87\uae88\uae89\uae8a\uae8b\uae8c\uae8d\uae8e\uae8f\uae90\uae91\uae92\uae93\uae94\uae95\uae96\uae97\uae98\uae99\uae9a\uae9b\uae9c\uae9d\uae9e\uae9f\uaea0\uaea1\uaea2\uaea3\uaea4\uaea5\uaea6\uaea7\uaea8\uaea9\uaeaa\uaeab\uaeac\uaead\uaeae\uaeaf\uaeb0\uaeb1\uaeb2\uaeb3\uaeb4\uaeb5\uaeb6\uaeb7\uaeb8\uaeb9\uaeba\uaebb\uaebc\uaebd\uaebe\uaebf\uaec0\uaec1\uaec2\uaec3\uaec4\uaec5\uaec6\uaec7\uaec8\uaec9\uaeca\uaecb\uaecc\uaecd\uaece\uaecf\uaed0\uaed1\uaed2\uaed3\uaed4\uaed5\uaed6\uaed7\uaed8\uaed9\uaeda\uaedb\uaedc\uaedd\uaede\uaedf\uaee0\uaee1\uaee2\uaee3\uaee4\uaee5\uaee6\uaee7\uaee8\uaee9\uaeea\uaeeb\uaeec\uaeed\uaeee\uaeef\uaef0\uaef1\uaef2\uaef3\uaef4\uaef5\uaef6\uaef7\uaef8\uaef9\uaefa\uaefb\uaefc\uaefd\uaefe\uaeff\uaf00\uaf01\uaf02\uaf03\uaf04\uaf05\uaf06\uaf07\uaf08\uaf09\uaf0a\uaf0b\uaf0c\uaf0d\uaf0e\uaf0f\uaf10\uaf11\uaf12\uaf13\uaf14\uaf15\uaf16\uaf17\uaf18\uaf19\uaf1a\uaf1b\uaf1c\uaf1d\uaf1e\uaf1f\uaf20\uaf21\uaf22\uaf23\uaf24\uaf25\uaf26\uaf27\uaf28\uaf29\uaf2a\uaf2b\uaf2c\uaf2d\uaf2e\uaf2f\uaf30\uaf31\uaf32\uaf33\uaf34\uaf35\uaf36\uaf37\uaf38\uaf39\uaf3a\uaf3b\uaf3c\uaf3d\uaf3e\uaf3f\uaf40\uaf41\uaf42\uaf43\uaf44\uaf45\uaf46\uaf47\uaf48\uaf49\uaf4a\uaf4b\uaf4c\uaf4d\uaf4e\uaf4f\uaf50\uaf51\uaf52\uaf53\uaf54\uaf55\uaf56\uaf57\uaf58\uaf59\uaf5a\uaf5b\uaf5c\uaf5d\uaf5e\uaf5f\uaf60\uaf61\uaf62\uaf63\uaf64\uaf65\uaf66\uaf67\uaf68\uaf69\uaf6a\uaf6b\uaf6c\uaf6d\uaf6e\uaf6f\uaf70\uaf71\uaf72\uaf73\uaf74\uaf75\uaf76\uaf77\uaf78\uaf79\uaf7a\uaf7b\uaf7c\uaf7d\uaf7e\uaf7f\uaf80\uaf81\uaf82\uaf83\uaf84\uaf85\uaf86\uaf87\uaf88\uaf89\uaf8a\uaf8b\uaf8c\uaf8d\uaf8e\uaf8f\uaf90\uaf91\uaf92\uaf93\uaf94\uaf95\uaf96\uaf97\uaf98\uaf99\uaf9a\uaf9b\uaf9c\uaf9d\uaf9e\uaf9f\uafa0\uafa1\uafa2\uafa3\uafa4\uafa5\uafa6\uafa7\uafa8\uafa9\uafaa\uafab\uafac\uafad\uafae\uafaf\uafb0\uafb1\uafb2\uafb3\uafb4\uafb5\uafb6\uafb7\uafb8\uafb9\uafba\uafbb\uafbc\uafbd\uafbe\uafbf\uafc0\uafc1\uafc2\uafc3\uafc4\uafc5\uafc6\uafc7\uafc8\uafc9\uafca\uafcb\uafcc\uafcd\uafce\uafcf\uafd0\uafd1\uafd2\uafd3\uafd4\uafd5\uafd6\uafd7\uafd8\uafd9\uafda\uafdb\uafdc\uafdd\uafde\uafdf\uafe0\uafe1\uafe2\uafe3\uafe4\uafe5\uafe6\uafe7\uafe8\uafe9\uafea\uafeb\uafec\uafed\uafee\uafef\uaff0\uaff1\uaff2\uaff3\uaff4\uaff5\uaff6\uaff7\uaff8\uaff9\uaffa\uaffb\uaffc\uaffd\uaffe\uafff\ub000\ub001\ub002\ub003\ub004\ub005\ub006\ub007\ub008\ub009\ub00a\ub00b\ub00c\ub00d\ub00e\ub00f\ub010\ub011\ub012\ub013\ub014\ub015\ub016\ub017\ub018\ub019\ub01a\ub01b\ub01c\ub01d\ub01e\ub01f\ub020\ub021\ub022\ub023\ub024\ub025\ub026\ub027\ub028\ub029\ub02a\ub02b\ub02c\ub02d\ub02e\ub02f\ub030\ub031\ub032\ub033\ub034\ub035\ub036\ub037\ub038\ub039\ub03a\ub03b\ub03c\ub03d\ub03e\ub03f\ub040\ub041\ub042\ub043\ub044\ub045\ub046\ub047\ub048\ub049\ub04a\ub04b\ub04c\ub04d\ub04e\ub04f\ub050\ub051\ub052\ub053\ub054\ub055\ub056\ub057\ub058\ub059\ub05a\ub05b\ub05c\ub05d\ub05e\ub05f\ub060\ub061\ub062\ub063\ub064\ub065\ub066\ub067\ub068\ub069\ub06a\ub06b\ub06c\ub06d\ub06e\ub06f\ub070\ub071\ub072\ub073\ub074\ub075\ub076\ub077\ub078\ub079\ub07a\ub07b\ub07c\ub07d\ub07e\ub07f\ub080\ub081\ub082\ub083\ub084\ub085\ub086\ub087\ub088\ub089\ub08a\ub08b\ub08c\ub08d\ub08e\ub08f\ub090\ub091\ub092\ub093\ub094\ub095\ub096\ub097\ub098\ub099\ub09a\ub09b\ub09c\ub09d\ub09e\ub09f\ub0a0\ub0a1\ub0a2\ub0a3\ub0a4\ub0a5\ub0a6\ub0a7\ub0a8\ub0a9\ub0aa\ub0ab\ub0ac\ub0ad\ub0ae\ub0af\ub0b0\ub0b1\ub0b2\ub0b3\ub0b4\ub0b5\ub0b6\ub0b7\ub0b8\ub0b9\ub0ba\ub0bb\ub0bc\ub0bd\ub0be\ub0bf\ub0c0\ub0c1\ub0c2\ub0c3\ub0c4\ub0c5\ub0c6\ub0c7\ub0c8\ub0c9\ub0ca\ub0cb\ub0cc\ub0cd\ub0ce\ub0cf\ub0d0\ub0d1\ub0d2\ub0d3\ub0d4\ub0d5\ub0d6\ub0d7\ub0d8\ub0d9\ub0da\ub0db\ub0dc\ub0dd\ub0de\ub0df\ub0e0\ub0e1\ub0e2\ub0e3\ub0e4\ub0e5\ub0e6\ub0e7\ub0e8\ub0e9\ub0ea\ub0eb\ub0ec\ub0ed\ub0ee\ub0ef\ub0f0\ub0f1\ub0f2\ub0f3\ub0f4\ub0f5\ub0f6\ub0f7\ub0f8\ub0f9\ub0fa\ub0fb\ub0fc\ub0fd\ub0fe\ub0ff\ub100\ub101\ub102\ub103\ub104\ub105\ub106\ub107\ub108\ub109\ub10a\ub10b\ub10c\ub10d\ub10e\ub10f\ub110\ub111\ub112\ub113\ub114\ub115\ub116\ub117\ub118\ub119\ub11a\ub11b\ub11c\ub11d\ub11e\ub11f\ub120\ub121\ub122\ub123\ub124\ub125\ub126\ub127\ub128\ub129\ub12a\ub12b\ub12c\ub12d\ub12e\ub12f\ub130\ub131\ub132\ub133\ub134\ub135\ub136\ub137\ub138\ub139\ub13a\ub13b\ub13c\ub13d\ub13e\ub13f\ub140\ub141\ub142\ub143\ub144\ub145\ub146\ub147\ub148\ub149\ub14a\ub14b\ub14c\ub14d\ub14e\ub14f\ub150\ub151\ub152\ub153\ub154\ub155\ub156\ub157\ub158\ub159\ub15a\ub15b\ub15c\ub15d\ub15e\ub15f\ub160\ub161\ub162\ub163\ub164\ub165\ub166\ub167\ub168\ub169\ub16a\ub16b\ub16c\ub16d\ub16e\ub16f\ub170\ub171\ub172\ub173\ub174\ub175\ub176\ub177\ub178\ub179\ub17a\ub17b\ub17c\ub17d\ub17e\ub17f\ub180\ub181\ub182\ub183\ub184\ub185\ub186\ub187\ub188\ub189\ub18a\ub18b\ub18c\ub18d\ub18e\ub18f\ub190\ub191\ub192\ub193\ub194\ub195\ub196\ub197\ub198\ub199\ub19a\ub19b\ub19c\ub19d\ub19e\ub19f\ub1a0\ub1a1\ub1a2\ub1a3\ub1a4\ub1a5\ub1a6\ub1a7\ub1a8\ub1a9\ub1aa\ub1ab\ub1ac\ub1ad\ub1ae\ub1af\ub1b0\ub1b1\ub1b2\ub1b3\ub1b4\ub1b5\ub1b6\ub1b7\ub1b8\ub1b9\ub1ba\ub1bb\ub1bc\ub1bd\ub1be\ub1bf\ub1c0\ub1c1\ub1c2\ub1c3\ub1c4\ub1c5\ub1c6\ub1c7\ub1c8\ub1c9\ub1ca\ub1cb\ub1cc\ub1cd\ub1ce\ub1cf\ub1d0\ub1d1\ub1d2\ub1d3\ub1d4\ub1d5\ub1d6\ub1d7\ub1d8\ub1d9\ub1da\ub1db\ub1dc\ub1dd\ub1de\ub1df\ub1e0\ub1e1\ub1e2\ub1e3\ub1e4\ub1e5\ub1e6\ub1e7\ub1e8\ub1e9\ub1ea\ub1eb\ub1ec\ub1ed\ub1ee\ub1ef\ub1f0\ub1f1\ub1f2\ub1f3\ub1f4\ub1f5\ub1f6\ub1f7\ub1f8\ub1f9\ub1fa\ub1fb\ub1fc\ub1fd\ub1fe\ub1ff\ub200\ub201\ub202\ub203\ub204\ub205\ub206\ub207\ub208\ub209\ub20a\ub20b\ub20c\ub20d\ub20e\ub20f\ub210\ub211\ub212\ub213\ub214\ub215\ub216\ub217\ub218\ub219\ub21a\ub21b\ub21c\ub21d\ub21e\ub21f\ub220\ub221\ub222\ub223\ub224\ub225\ub226\ub227\ub228\ub229\ub22a\ub22b\ub22c\ub22d\ub22e\ub22f\ub230\ub231\ub232\ub233\ub234\ub235\ub236\ub237\ub238\ub239\ub23a\ub23b\ub23c\ub23d\ub23e\ub23f\ub240\ub241\ub242\ub243\ub244\ub245\ub246\ub247\ub248\ub249\ub24a\ub24b\ub24c\ub24d\ub24e\ub24f\ub250\ub251\ub252\ub253\ub254\ub255\ub256\ub257\ub258\ub259\ub25a\ub25b\ub25c\ub25d\ub25e\ub25f\ub260\ub261\ub262\ub263\ub264\ub265\ub266\ub267\ub268\ub269\ub26a\ub26b\ub26c\ub26d\ub26e\ub26f\ub270\ub271\ub272\ub273\ub274\ub275\ub276\ub277\ub278\ub279\ub27a\ub27b\ub27c\ub27d\ub27e\ub27f\ub280\ub281\ub282\ub283\ub284\ub285\ub286\ub287\ub288\ub289\ub28a\ub28b\ub28c\ub28d\ub28e\ub28f\ub290\ub291\ub292\ub293\ub294\ub295\ub296\ub297\ub298\ub299\ub29a\ub29b\ub29c\ub29d\ub29e\ub29f\ub2a0\ub2a1\ub2a2\ub2a3\ub2a4\ub2a5\ub2a6\ub2a7\ub2a8\ub2a9\ub2aa\ub2ab\ub2ac\ub2ad\ub2ae\ub2af\ub2b0\ub2b1\ub2b2\ub2b3\ub2b4\ub2b5\ub2b6\ub2b7\ub2b8\ub2b9\ub2ba\ub2bb\ub2bc\ub2bd\ub2be\ub2bf\ub2c0\ub2c1\ub2c2\ub2c3\ub2c4\ub2c5\ub2c6\ub2c7\ub2c8\ub2c9\ub2ca\ub2cb\ub2cc\ub2cd\ub2ce\ub2cf\ub2d0\ub2d1\ub2d2\ub2d3\ub2d4\ub2d5\ub2d6\ub2d7\ub2d8\ub2d9\ub2da\ub2db\ub2dc\ub2dd\ub2de\ub2df\ub2e0\ub2e1\ub2e2\ub2e3\ub2e4\ub2e5\ub2e6\ub2e7\ub2e8\ub2e9\ub2ea\ub2eb\ub2ec\ub2ed\ub2ee\ub2ef\ub2f0\ub2f1\ub2f2\ub2f3\ub2f4\ub2f5\ub2f6\ub2f7\ub2f8\ub2f9\ub2fa\ub2fb\ub2fc\ub2fd\ub2fe\ub2ff\ub300\ub301\ub302\ub303\ub304\ub305\ub306\ub307\ub308\ub309\ub30a\ub30b\ub30c\ub30d\ub30e\ub30f\ub310\ub311\ub312\ub313\ub314\ub315\ub316\ub317\ub318\ub319\ub31a\ub31b\ub31c\ub31d\ub31e\ub31f\ub320\ub321\ub322\ub323\ub324\ub325\ub326\ub327\ub328\ub329\ub32a\ub32b\ub32c\ub32d\ub32e\ub32f\ub330\ub331\ub332\ub333\ub334\ub335\ub336\ub337\ub338\ub339\ub33a\ub33b\ub33c\ub33d\ub33e\ub33f\ub340\ub341\ub342\ub343\ub344\ub345\ub346\ub347\ub348\ub349\ub34a\ub34b\ub34c\ub34d\ub34e\ub34f\ub350\ub351\ub352\ub353\ub354\ub355\ub356\ub357\ub358\ub359\ub35a\ub35b\ub35c\ub35d\ub35e\ub35f\ub360\ub361\ub362\ub363\ub364\ub365\ub366\ub367\ub368\ub369\ub36a\ub36b\ub36c\ub36d\ub36e\ub36f\ub370\ub371\ub372\ub373\ub374\ub375\ub376\ub377\ub378\ub379\ub37a\ub37b\ub37c\ub37d\ub37e\ub37f\ub380\ub381\ub382\ub383\ub384\ub385\ub386\ub387\ub388\ub389\ub38a\ub38b\ub38c\ub38d\ub38e\ub38f\ub390\ub391\ub392\ub393\ub394\ub395\ub396\ub397\ub398\ub399\ub39a\ub39b\ub39c\ub39d\ub39e\ub39f\ub3a0\ub3a1\ub3a2\ub3a3\ub3a4\ub3a5\ub3a6\ub3a7\ub3a8\ub3a9\ub3aa\ub3ab\ub3ac\ub3ad\ub3ae\ub3af\ub3b0\ub3b1\ub3b2\ub3b3\ub3b4\ub3b5\ub3b6\ub3b7\ub3b8\ub3b9\ub3ba\ub3bb\ub3bc\ub3bd\ub3be\ub3bf\ub3c0\ub3c1\ub3c2\ub3c3\ub3c4\ub3c5\ub3c6\ub3c7\ub3c8\ub3c9\ub3ca\ub3cb\ub3cc\ub3cd\ub3ce\ub3cf\ub3d0\ub3d1\ub3d2\ub3d3\ub3d4\ub3d5\ub3d6\ub3d7\ub3d8\ub3d9\ub3da\ub3db\ub3dc\ub3dd\ub3de\ub3df\ub3e0\ub3e1\ub3e2\ub3e3\ub3e4\ub3e5\ub3e6\ub3e7\ub3e8\ub3e9\ub3ea\ub3eb\ub3ec\ub3ed\ub3ee\ub3ef\ub3f0\ub3f1\ub3f2\ub3f3\ub3f4\ub3f5\ub3f6\ub3f7\ub3f8\ub3f9\ub3fa\ub3fb\ub3fc\ub3fd\ub3fe\ub3ff\ub400\ub401\ub402\ub403\ub404\ub405\ub406\ub407\ub408\ub409\ub40a\ub40b\ub40c\ub40d\ub40e\ub40f\ub410\ub411\ub412\ub413\ub414\ub415\ub416\ub417\ub418\ub419\ub41a\ub41b\ub41c\ub41d\ub41e\ub41f\ub420\ub421\ub422\ub423\ub424\ub425\ub426\ub427\ub428\ub429\ub42a\ub42b\ub42c\ub42d\ub42e\ub42f\ub430\ub431\ub432\ub433\ub434\ub435\ub436\ub437\ub438\ub439\ub43a\ub43b\ub43c\ub43d\ub43e\ub43f\ub440\ub441\ub442\ub443\ub444\ub445\ub446\ub447\ub448\ub449\ub44a\ub44b\ub44c\ub44d\ub44e\ub44f\ub450\ub451\ub452\ub453\ub454\ub455\ub456\ub457\ub458\ub459\ub45a\ub45b\ub45c\ub45d\ub45e\ub45f\ub460\ub461\ub462\ub463\ub464\ub465\ub466\ub467\ub468\ub469\ub46a\ub46b\ub46c\ub46d\ub46e\ub46f\ub470\ub471\ub472\ub473\ub474\ub475\ub476\ub477\ub478\ub479\ub47a\ub47b\ub47c\ub47d\ub47e\ub47f\ub480\ub481\ub482\ub483\ub484\ub485\ub486\ub487\ub488\ub489\ub48a\ub48b\ub48c\ub48d\ub48e\ub48f\ub490\ub491\ub492\ub493\ub494\ub495\ub496\ub497\ub498\ub499\ub49a\ub49b\ub49c\ub49d\ub49e\ub49f\ub4a0\ub4a1\ub4a2\ub4a3\ub4a4\ub4a5\ub4a6\ub4a7\ub4a8\ub4a9\ub4aa\ub4ab\ub4ac\ub4ad\ub4ae\ub4af\ub4b0\ub4b1\ub4b2\ub4b3\ub4b4\ub4b5\ub4b6\ub4b7\ub4b8\ub4b9\ub4ba\ub4bb\ub4bc\ub4bd\ub4be\ub4bf\ub4c0\ub4c1\ub4c2\ub4c3\ub4c4\ub4c5\ub4c6\ub4c7\ub4c8\ub4c9\ub4ca\ub4cb\ub4cc\ub4cd\ub4ce\ub4cf\ub4d0\ub4d1\ub4d2\ub4d3\ub4d4\ub4d5\ub4d6\ub4d7\ub4d8\ub4d9\ub4da\ub4db\ub4dc\ub4dd\ub4de\ub4df\ub4e0\ub4e1\ub4e2\ub4e3\ub4e4\ub4e5\ub4e6\ub4e7\ub4e8\ub4e9\ub4ea\ub4eb\ub4ec\ub4ed\ub4ee\ub4ef\ub4f0\ub4f1\ub4f2\ub4f3\ub4f4\ub4f5\ub4f6\ub4f7\ub4f8\ub4f9\ub4fa\ub4fb\ub4fc\ub4fd\ub4fe\ub4ff\ub500\ub501\ub502\ub503\ub504\ub505\ub506\ub507\ub508\ub509\ub50a\ub50b\ub50c\ub50d\ub50e\ub50f\ub510\ub511\ub512\ub513\ub514\ub515\ub516\ub517\ub518\ub519\ub51a\ub51b\ub51c\ub51d\ub51e\ub51f\ub520\ub521\ub522\ub523\ub524\ub525\ub526\ub527\ub528\ub529\ub52a\ub52b\ub52c\ub52d\ub52e\ub52f\ub530\ub531\ub532\ub533\ub534\ub535\ub536\ub537\ub538\ub539\ub53a\ub53b\ub53c\ub53d\ub53e\ub53f\ub540\ub541\ub542\ub543\ub544\ub545\ub546\ub547\ub548\ub549\ub54a\ub54b\ub54c\ub54d\ub54e\ub54f\ub550\ub551\ub552\ub553\ub554\ub555\ub556\ub557\ub558\ub559\ub55a\ub55b\ub55c\ub55d\ub55e\ub55f\ub560\ub561\ub562\ub563\ub564\ub565\ub566\ub567\ub568\ub569\ub56a\ub56b\ub56c\ub56d\ub56e\ub56f\ub570\ub571\ub572\ub573\ub574\ub575\ub576\ub577\ub578\ub579\ub57a\ub57b\ub57c\ub57d\ub57e\ub57f\ub580\ub581\ub582\ub583\ub584\ub585\ub586\ub587\ub588\ub589\ub58a\ub58b\ub58c\ub58d\ub58e\ub58f\ub590\ub591\ub592\ub593\ub594\ub595\ub596\ub597\ub598\ub599\ub59a\ub59b\ub59c\ub59d\ub59e\ub59f\ub5a0\ub5a1\ub5a2\ub5a3\ub5a4\ub5a5\ub5a6\ub5a7\ub5a8\ub5a9\ub5aa\ub5ab\ub5ac\ub5ad\ub5ae\ub5af\ub5b0\ub5b1\ub5b2\ub5b3\ub5b4\ub5b5\ub5b6\ub5b7\ub5b8\ub5b9\ub5ba\ub5bb\ub5bc\ub5bd\ub5be\ub5bf\ub5c0\ub5c1\ub5c2\ub5c3\ub5c4\ub5c5\ub5c6\ub5c7\ub5c8\ub5c9\ub5ca\ub5cb\ub5cc\ub5cd\ub5ce\ub5cf\ub5d0\ub5d1\ub5d2\ub5d3\ub5d4\ub5d5\ub5d6\ub5d7\ub5d8\ub5d9\ub5da\ub5db\ub5dc\ub5dd\ub5de\ub5df\ub5e0\ub5e1\ub5e2\ub5e3\ub5e4\ub5e5\ub5e6\ub5e7\ub5e8\ub5e9\ub5ea\ub5eb\ub5ec\ub5ed\ub5ee\ub5ef\ub5f0\ub5f1\ub5f2\ub5f3\ub5f4\ub5f5\ub5f6\ub5f7\ub5f8\ub5f9\ub5fa\ub5fb\ub5fc\ub5fd\ub5fe\ub5ff\ub600\ub601\ub602\ub603\ub604\ub605\ub606\ub607\ub608\ub609\ub60a\ub60b\ub60c\ub60d\ub60e\ub60f\ub610\ub611\ub612\ub613\ub614\ub615\ub616\ub617\ub618\ub619\ub61a\ub61b\ub61c\ub61d\ub61e\ub61f\ub620\ub621\ub622\ub623\ub624\ub625\ub626\ub627\ub628\ub629\ub62a\ub62b\ub62c\ub62d\ub62e\ub62f\ub630\ub631\ub632\ub633\ub634\ub635\ub636\ub637\ub638\ub639\ub63a\ub63b\ub63c\ub63d\ub63e\ub63f\ub640\ub641\ub642\ub643\ub644\ub645\ub646\ub647\ub648\ub649\ub64a\ub64b\ub64c\ub64d\ub64e\ub64f\ub650\ub651\ub652\ub653\ub654\ub655\ub656\ub657\ub658\ub659\ub65a\ub65b\ub65c\ub65d\ub65e\ub65f\ub660\ub661\ub662\ub663\ub664\ub665\ub666\ub667\ub668\ub669\ub66a\ub66b\ub66c\ub66d\ub66e\ub66f\ub670\ub671\ub672\ub673\ub674\ub675\ub676\ub677\ub678\ub679\ub67a\ub67b\ub67c\ub67d\ub67e\ub67f\ub680\ub681\ub682\ub683\ub684\ub685\ub686\ub687\ub688\ub689\ub68a\ub68b\ub68c\ub68d\ub68e\ub68f\ub690\ub691\ub692\ub693\ub694\ub695\ub696\ub697\ub698\ub699\ub69a\ub69b\ub69c\ub69d\ub69e\ub69f\ub6a0\ub6a1\ub6a2\ub6a3\ub6a4\ub6a5\ub6a6\ub6a7\ub6a8\ub6a9\ub6aa\ub6ab\ub6ac\ub6ad\ub6ae\ub6af\ub6b0\ub6b1\ub6b2\ub6b3\ub6b4\ub6b5\ub6b6\ub6b7\ub6b8\ub6b9\ub6ba\ub6bb\ub6bc\ub6bd\ub6be\ub6bf\ub6c0\ub6c1\ub6c2\ub6c3\ub6c4\ub6c5\ub6c6\ub6c7\ub6c8\ub6c9\ub6ca\ub6cb\ub6cc\ub6cd\ub6ce\ub6cf\ub6d0\ub6d1\ub6d2\ub6d3\ub6d4\ub6d5\ub6d6\ub6d7\ub6d8\ub6d9\ub6da\ub6db\ub6dc\ub6dd\ub6de\ub6df\ub6e0\ub6e1\ub6e2\ub6e3\ub6e4\ub6e5\ub6e6\ub6e7\ub6e8\ub6e9\ub6ea\ub6eb\ub6ec\ub6ed\ub6ee\ub6ef\ub6f0\ub6f1\ub6f2\ub6f3\ub6f4\ub6f5\ub6f6\ub6f7\ub6f8\ub6f9\ub6fa\ub6fb\ub6fc\ub6fd\ub6fe\ub6ff\ub700\ub701\ub702\ub703\ub704\ub705\ub706\ub707\ub708\ub709\ub70a\ub70b\ub70c\ub70d\ub70e\ub70f\ub710\ub711\ub712\ub713\ub714\ub715\ub716\ub717\ub718\ub719\ub71a\ub71b\ub71c\ub71d\ub71e\ub71f\ub720\ub721\ub722\ub723\ub724\ub725\ub726\ub727\ub728\ub729\ub72a\ub72b\ub72c\ub72d\ub72e\ub72f\ub730\ub731\ub732\ub733\ub734\ub735\ub736\ub737\ub738\ub739\ub73a\ub73b\ub73c\ub73d\ub73e\ub73f\ub740\ub741\ub742\ub743\ub744\ub745\ub746\ub747\ub748\ub749\ub74a\ub74b\ub74c\ub74d\ub74e\ub74f\ub750\ub751\ub752\ub753\ub754\ub755\ub756\ub757\ub758\ub759\ub75a\ub75b\ub75c\ub75d\ub75e\ub75f\ub760\ub761\ub762\ub763\ub764\ub765\ub766\ub767\ub768\ub769\ub76a\ub76b\ub76c\ub76d\ub76e\ub76f\ub770\ub771\ub772\ub773\ub774\ub775\ub776\ub777\ub778\ub779\ub77a\ub77b\ub77c\ub77d\ub77e\ub77f\ub780\ub781\ub782\ub783\ub784\ub785\ub786\ub787\ub788\ub789\ub78a\ub78b\ub78c\ub78d\ub78e\ub78f\ub790\ub791\ub792\ub793\ub794\ub795\ub796\ub797\ub798\ub799\ub79a\ub79b\ub79c\ub79d\ub79e\ub79f\ub7a0\ub7a1\ub7a2\ub7a3\ub7a4\ub7a5\ub7a6\ub7a7\ub7a8\ub7a9\ub7aa\ub7ab\ub7ac\ub7ad\ub7ae\ub7af\ub7b0\ub7b1\ub7b2\ub7b3\ub7b4\ub7b5\ub7b6\ub7b7\ub7b8\ub7b9\ub7ba\ub7bb\ub7bc\ub7bd\ub7be\ub7bf\ub7c0\ub7c1\ub7c2\ub7c3\ub7c4\ub7c5\ub7c6\ub7c7\ub7c8\ub7c9\ub7ca\ub7cb\ub7cc\ub7cd\ub7ce\ub7cf\ub7d0\ub7d1\ub7d2\ub7d3\ub7d4\ub7d5\ub7d6\ub7d7\ub7d8\ub7d9\ub7da\ub7db\ub7dc\ub7dd\ub7de\ub7df\ub7e0\ub7e1\ub7e2\ub7e3\ub7e4\ub7e5\ub7e6\ub7e7\ub7e8\ub7e9\ub7ea\ub7eb\ub7ec\ub7ed\ub7ee\ub7ef\ub7f0\ub7f1\ub7f2\ub7f3\ub7f4\ub7f5\ub7f6\ub7f7\ub7f8\ub7f9\ub7fa\ub7fb\ub7fc\ub7fd\ub7fe\ub7ff\ub800\ub801\ub802\ub803\ub804\ub805\ub806\ub807\ub808\ub809\ub80a\ub80b\ub80c\ub80d\ub80e\ub80f\ub810\ub811\ub812\ub813\ub814\ub815\ub816\ub817\ub818\ub819\ub81a\ub81b\ub81c\ub81d\ub81e\ub81f\ub820\ub821\ub822\ub823\ub824\ub825\ub826\ub827\ub828\ub829\ub82a\ub82b\ub82c\ub82d\ub82e\ub82f\ub830\ub831\ub832\ub833\ub834\ub835\ub836\ub837\ub838\ub839\ub83a\ub83b\ub83c\ub83d\ub83e\ub83f\ub840\ub841\ub842\ub843\ub844\ub845\ub846\ub847\ub848\ub849\ub84a\ub84b\ub84c\ub84d\ub84e\ub84f\ub850\ub851\ub852\ub853\ub854\ub855\ub856\ub857\ub858\ub859\ub85a\ub85b\ub85c\ub85d\ub85e\ub85f\ub860\ub861\ub862\ub863\ub864\ub865\ub866\ub867\ub868\ub869\ub86a\ub86b\ub86c\ub86d\ub86e\ub86f\ub870\ub871\ub872\ub873\ub874\ub875\ub876\ub877\ub878\ub879\ub87a\ub87b\ub87c\ub87d\ub87e\ub87f\ub880\ub881\ub882\ub883\ub884\ub885\ub886\ub887\ub888\ub889\ub88a\ub88b\ub88c\ub88d\ub88e\ub88f\ub890\ub891\ub892\ub893\ub894\ub895\ub896\ub897\ub898\ub899\ub89a\ub89b\ub89c\ub89d\ub89e\ub89f\ub8a0\ub8a1\ub8a2\ub8a3\ub8a4\ub8a5\ub8a6\ub8a7\ub8a8\ub8a9\ub8aa\ub8ab\ub8ac\ub8ad\ub8ae\ub8af\ub8b0\ub8b1\ub8b2\ub8b3\ub8b4\ub8b5\ub8b6\ub8b7\ub8b8\ub8b9\ub8ba\ub8bb\ub8bc\ub8bd\ub8be\ub8bf\ub8c0\ub8c1\ub8c2\ub8c3\ub8c4\ub8c5\ub8c6\ub8c7\ub8c8\ub8c9\ub8ca\ub8cb\ub8cc\ub8cd\ub8ce\ub8cf\ub8d0\ub8d1\ub8d2\ub8d3\ub8d4\ub8d5\ub8d6\ub8d7\ub8d8\ub8d9\ub8da\ub8db\ub8dc\ub8dd\ub8de\ub8df\ub8e0\ub8e1\ub8e2\ub8e3\ub8e4\ub8e5\ub8e6\ub8e7\ub8e8\ub8e9\ub8ea\ub8eb\ub8ec\ub8ed\ub8ee\ub8ef\ub8f0\ub8f1\ub8f2\ub8f3\ub8f4\ub8f5\ub8f6\ub8f7\ub8f8\ub8f9\ub8fa\ub8fb\ub8fc\ub8fd\ub8fe\ub8ff\ub900\ub901\ub902\ub903\ub904\ub905\ub906\ub907\ub908\ub909\ub90a\ub90b\ub90c\ub90d\ub90e\ub90f\ub910\ub911\ub912\ub913\ub914\ub915\ub916\ub917\ub918\ub919\ub91a\ub91b\ub91c\ub91d\ub91e\ub91f\ub920\ub921\ub922\ub923\ub924\ub925\ub926\ub927\ub928\ub929\ub92a\ub92b\ub92c\ub92d\ub92e\ub92f\ub930\ub931\ub932\ub933\ub934\ub935\ub936\ub937\ub938\ub939\ub93a\ub93b\ub93c\ub93d\ub93e\ub93f\ub940\ub941\ub942\ub943\ub944\ub945\ub946\ub947\ub948\ub949\ub94a\ub94b\ub94c\ub94d\ub94e\ub94f\ub950\ub951\ub952\ub953\ub954\ub955\ub956\ub957\ub958\ub959\ub95a\ub95b\ub95c\ub95d\ub95e\ub95f\ub960\ub961\ub962\ub963\ub964\ub965\ub966\ub967\ub968\ub969\ub96a\ub96b\ub96c\ub96d\ub96e\ub96f\ub970\ub971\ub972\ub973\ub974\ub975\ub976\ub977\ub978\ub979\ub97a\ub97b\ub97c\ub97d\ub97e\ub97f\ub980\ub981\ub982\ub983\ub984\ub985\ub986\ub987\ub988\ub989\ub98a\ub98b\ub98c\ub98d\ub98e\ub98f\ub990\ub991\ub992\ub993\ub994\ub995\ub996\ub997\ub998\ub999\ub99a\ub99b\ub99c\ub99d\ub99e\ub99f\ub9a0\ub9a1\ub9a2\ub9a3\ub9a4\ub9a5\ub9a6\ub9a7\ub9a8\ub9a9\ub9aa\ub9ab\ub9ac\ub9ad\ub9ae\ub9af\ub9b0\ub9b1\ub9b2\ub9b3\ub9b4\ub9b5\ub9b6\ub9b7\ub9b8\ub9b9\ub9ba\ub9bb\ub9bc\ub9bd\ub9be\ub9bf\ub9c0\ub9c1\ub9c2\ub9c3\ub9c4\ub9c5\ub9c6\ub9c7\ub9c8\ub9c9\ub9ca\ub9cb\ub9cc\ub9cd\ub9ce\ub9cf\ub9d0\ub9d1\ub9d2\ub9d3\ub9d4\ub9d5\ub9d6\ub9d7\ub9d8\ub9d9\ub9da\ub9db\ub9dc\ub9dd\ub9de\ub9df\ub9e0\ub9e1\ub9e2\ub9e3\ub9e4\ub9e5\ub9e6\ub9e7\ub9e8\ub9e9\ub9ea\ub9eb\ub9ec\ub9ed\ub9ee\ub9ef\ub9f0\ub9f1\ub9f2\ub9f3\ub9f4\ub9f5\ub9f6\ub9f7\ub9f8\ub9f9\ub9fa\ub9fb\ub9fc\ub9fd\ub9fe\ub9ff\uba00\uba01\uba02\uba03\uba04\uba05\uba06\uba07\uba08\uba09\uba0a\uba0b\uba0c\uba0d\uba0e\uba0f\uba10\uba11\uba12\uba13\uba14\uba15\uba16\uba17\uba18\uba19\uba1a\uba1b\uba1c\uba1d\uba1e\uba1f\uba20\uba21\uba22\uba23\uba24\uba25\uba26\uba27\uba28\uba29\uba2a\uba2b\uba2c\uba2d\uba2e\uba2f\uba30\uba31\uba32\uba33\uba34\uba35\uba36\uba37\uba38\uba39\uba3a\uba3b\uba3c\uba3d\uba3e\uba3f\uba40\uba41\uba42\uba43\uba44\uba45\uba46\uba47\uba48\uba49\uba4a\uba4b\uba4c\uba4d\uba4e\uba4f\uba50\uba51\uba52\uba53\uba54\uba55\uba56\uba57\uba58\uba59\uba5a\uba5b\uba5c\uba5d\uba5e\uba5f\uba60\uba61\uba62\uba63\uba64\uba65\uba66\uba67\uba68\uba69\uba6a\uba6b\uba6c\uba6d\uba6e\uba6f\uba70\uba71\uba72\uba73\uba74\uba75\uba76\uba77\uba78\uba79\uba7a\uba7b\uba7c\uba7d\uba7e\uba7f\uba80\uba81\uba82\uba83\uba84\uba85\uba86\uba87\uba88\uba89\uba8a\uba8b\uba8c\uba8d\uba8e\uba8f\uba90\uba91\uba92\uba93\uba94\uba95\uba96\uba97\uba98\uba99\uba9a\uba9b\uba9c\uba9d\uba9e\uba9f\ubaa0\ubaa1\ubaa2\ubaa3\ubaa4\ubaa5\ubaa6\ubaa7\ubaa8\ubaa9\ubaaa\ubaab\ubaac\ubaad\ubaae\ubaaf\ubab0\ubab1\ubab2\ubab3\ubab4\ubab5\ubab6\ubab7\ubab8\ubab9\ubaba\ubabb\ubabc\ubabd\ubabe\ubabf\ubac0\ubac1\ubac2\ubac3\ubac4\ubac5\ubac6\ubac7\ubac8\ubac9\ubaca\ubacb\ubacc\ubacd\ubace\ubacf\ubad0\ubad1\ubad2\ubad3\ubad4\ubad5\ubad6\ubad7\ubad8\ubad9\ubada\ubadb\ubadc\ubadd\ubade\ubadf\ubae0\ubae1\ubae2\ubae3\ubae4\ubae5\ubae6\ubae7\ubae8\ubae9\ubaea\ubaeb\ubaec\ubaed\ubaee\ubaef\ubaf0\ubaf1\ubaf2\ubaf3\ubaf4\ubaf5\ubaf6\ubaf7\ubaf8\ubaf9\ubafa\ubafb\ubafc\ubafd\ubafe\ubaff\ubb00\ubb01\ubb02\ubb03\ubb04\ubb05\ubb06\ubb07\ubb08\ubb09\ubb0a\ubb0b\ubb0c\ubb0d\ubb0e\ubb0f\ubb10\ubb11\ubb12\ubb13\ubb14\ubb15\ubb16\ubb17\ubb18\ubb19\ubb1a\ubb1b\ubb1c\ubb1d\ubb1e\ubb1f\ubb20\ubb21\ubb22\ubb23\ubb24\ubb25\ubb26\ubb27\ubb28\ubb29\ubb2a\ubb2b\ubb2c\ubb2d\ubb2e\ubb2f\ubb30\ubb31\ubb32\ubb33\ubb34\ubb35\ubb36\ubb37\ubb38\ubb39\ubb3a\ubb3b\ubb3c\ubb3d\ubb3e\ubb3f\ubb40\ubb41\ubb42\ubb43\ubb44\ubb45\ubb46\ubb47\ubb48\ubb49\ubb4a\ubb4b\ubb4c\ubb4d\ubb4e\ubb4f\ubb50\ubb51\ubb52\ubb53\ubb54\ubb55\ubb56\ubb57\ubb58\ubb59\ubb5a\ubb5b\ubb5c\ubb5d\ubb5e\ubb5f\ubb60\ubb61\ubb62\ubb63\ubb64\ubb65\ubb66\ubb67\ubb68\ubb69\ubb6a\ubb6b\ubb6c\ubb6d\ubb6e\ubb6f\ubb70\ubb71\ubb72\ubb73\ubb74\ubb75\ubb76\ubb77\ubb78\ubb79\ubb7a\ubb7b\ubb7c\ubb7d\ubb7e\ubb7f\ubb80\ubb81\ubb82\ubb83\ubb84\ubb85\ubb86\ubb87\ubb88\ubb89\ubb8a\ubb8b\ubb8c\ubb8d\ubb8e\ubb8f\ubb90\ubb91\ubb92\ubb93\ubb94\ubb95\ubb96\ubb97\ubb98\ubb99\ubb9a\ubb9b\ubb9c\ubb9d\ubb9e\ubb9f\ubba0\ubba1\ubba2\ubba3\ubba4\ubba5\ubba6\ubba7\ubba8\ubba9\ubbaa\ubbab\ubbac\ubbad\ubbae\ubbaf\ubbb0\ubbb1\ubbb2\ubbb3\ubbb4\ubbb5\ubbb6\ubbb7\ubbb8\ubbb9\ubbba\ubbbb\ubbbc\ubbbd\ubbbe\ubbbf\ubbc0\ubbc1\ubbc2\ubbc3\ubbc4\ubbc5\ubbc6\ubbc7\ubbc8\ubbc9\ubbca\ubbcb\ubbcc\ubbcd\ubbce\ubbcf\ubbd0\ubbd1\ubbd2\ubbd3\ubbd4\ubbd5\ubbd6\ubbd7\ubbd8\ubbd9\ubbda\ubbdb\ubbdc\ubbdd\ubbde\ubbdf\ubbe0\ubbe1\ubbe2\ubbe3\ubbe4\ubbe5\ubbe6\ubbe7\ubbe8\ubbe9\ubbea\ubbeb\ubbec\ubbed\ubbee\ubbef\ubbf0\ubbf1\ubbf2\ubbf3\ubbf4\ubbf5\ubbf6\ubbf7\ubbf8\ubbf9\ubbfa\ubbfb\ubbfc\ubbfd\ubbfe\ubbff\ubc00\ubc01\ubc02\ubc03\ubc04\ubc05\ubc06\ubc07\ubc08\ubc09\ubc0a\ubc0b\ubc0c\ubc0d\ubc0e\ubc0f\ubc10\ubc11\ubc12\ubc13\ubc14\ubc15\ubc16\ubc17\ubc18\ubc19\ubc1a\ubc1b\ubc1c\ubc1d\ubc1e\ubc1f\ubc20\ubc21\ubc22\ubc23\ubc24\ubc25\ubc26\ubc27\ubc28\ubc29\ubc2a\ubc2b\ubc2c\ubc2d\ubc2e\ubc2f\ubc30\ubc31\ubc32\ubc33\ubc34\ubc35\ubc36\ubc37\ubc38\ubc39\ubc3a\ubc3b\ubc3c\ubc3d\ubc3e\ubc3f\ubc40\ubc41\ubc42\ubc43\ubc44\ubc45\ubc46\ubc47\ubc48\ubc49\ubc4a\ubc4b\ubc4c\ubc4d\ubc4e\ubc4f\ubc50\ubc51\ubc52\ubc53\ubc54\ubc55\ubc56\ubc57\ubc58\ubc59\ubc5a\ubc5b\ubc5c\ubc5d\ubc5e\ubc5f\ubc60\ubc61\ubc62\ubc63\ubc64\ubc65\ubc66\ubc67\ubc68\ubc69\ubc6a\ubc6b\ubc6c\ubc6d\ubc6e\ubc6f\ubc70\ubc71\ubc72\ubc73\ubc74\ubc75\ubc76\ubc77\ubc78\ubc79\ubc7a\ubc7b\ubc7c\ubc7d\ubc7e\ubc7f\ubc80\ubc81\ubc82\ubc83\ubc84\ubc85\ubc86\ubc87\ubc88\ubc89\ubc8a\ubc8b\ubc8c\ubc8d\ubc8e\ubc8f\ubc90\ubc91\ubc92\ubc93\ubc94\ubc95\ubc96\ubc97\ubc98\ubc99\ubc9a\ubc9b\ubc9c\ubc9d\ubc9e\ubc9f\ubca0\ubca1\ubca2\ubca3\ubca4\ubca5\ubca6\ubca7\ubca8\ubca9\ubcaa\ubcab\ubcac\ubcad\ubcae\ubcaf\ubcb0\ubcb1\ubcb2\ubcb3\ubcb4\ubcb5\ubcb6\ubcb7\ubcb8\ubcb9\ubcba\ubcbb\ubcbc\ubcbd\ubcbe\ubcbf\ubcc0\ubcc1\ubcc2\ubcc3\ubcc4\ubcc5\ubcc6\ubcc7\ubcc8\ubcc9\ubcca\ubccb\ubccc\ubccd\ubcce\ubccf\ubcd0\ubcd1\ubcd2\ubcd3\ubcd4\ubcd5\ubcd6\ubcd7\ubcd8\ubcd9\ubcda\ubcdb\ubcdc\ubcdd\ubcde\ubcdf\ubce0\ubce1\ubce2\ubce3\ubce4\ubce5\ubce6\ubce7\ubce8\ubce9\ubcea\ubceb\ubcec\ubced\ubcee\ubcef\ubcf0\ubcf1\ubcf2\ubcf3\ubcf4\ubcf5\ubcf6\ubcf7\ubcf8\ubcf9\ubcfa\ubcfb\ubcfc\ubcfd\ubcfe\ubcff\ubd00\ubd01\ubd02\ubd03\ubd04\ubd05\ubd06\ubd07\ubd08\ubd09\ubd0a\ubd0b\ubd0c\ubd0d\ubd0e\ubd0f\ubd10\ubd11\ubd12\ubd13\ubd14\ubd15\ubd16\ubd17\ubd18\ubd19\ubd1a\ubd1b\ubd1c\ubd1d\ubd1e\ubd1f\ubd20\ubd21\ubd22\ubd23\ubd24\ubd25\ubd26\ubd27\ubd28\ubd29\ubd2a\ubd2b\ubd2c\ubd2d\ubd2e\ubd2f\ubd30\ubd31\ubd32\ubd33\ubd34\ubd35\ubd36\ubd37\ubd38\ubd39\ubd3a\ubd3b\ubd3c\ubd3d\ubd3e\ubd3f\ubd40\ubd41\ubd42\ubd43\ubd44\ubd45\ubd46\ubd47\ubd48\ubd49\ubd4a\ubd4b\ubd4c\ubd4d\ubd4e\ubd4f\ubd50\ubd51\ubd52\ubd53\ubd54\ubd55\ubd56\ubd57\ubd58\ubd59\ubd5a\ubd5b\ubd5c\ubd5d\ubd5e\ubd5f\ubd60\ubd61\ubd62\ubd63\ubd64\ubd65\ubd66\ubd67\ubd68\ubd69\ubd6a\ubd6b\ubd6c\ubd6d\ubd6e\ubd6f\ubd70\ubd71\ubd72\ubd73\ubd74\ubd75\ubd76\ubd77\ubd78\ubd79\ubd7a\ubd7b\ubd7c\ubd7d\ubd7e\ubd7f\ubd80\ubd81\ubd82\ubd83\ubd84\ubd85\ubd86\ubd87\ubd88\ubd89\ubd8a\ubd8b\ubd8c\ubd8d\ubd8e\ubd8f\ubd90\ubd91\ubd92\ubd93\ubd94\ubd95\ubd96\ubd97\ubd98\ubd99\ubd9a\ubd9b\ubd9c\ubd9d\ubd9e\ubd9f\ubda0\ubda1\ubda2\ubda3\ubda4\ubda5\ubda6\ubda7\ubda8\ubda9\ubdaa\ubdab\ubdac\ubdad\ubdae\ubdaf\ubdb0\ubdb1\ubdb2\ubdb3\ubdb4\ubdb5\ubdb6\ubdb7\ubdb8\ubdb9\ubdba\ubdbb\ubdbc\ubdbd\ubdbe\ubdbf\ubdc0\ubdc1\ubdc2\ubdc3\ubdc4\ubdc5\ubdc6\ubdc7\ubdc8\ubdc9\ubdca\ubdcb\ubdcc\ubdcd\ubdce\ubdcf\ubdd0\ubdd1\ubdd2\ubdd3\ubdd4\ubdd5\ubdd6\ubdd7\ubdd8\ubdd9\ubdda\ubddb\ubddc\ubddd\ubdde\ubddf\ubde0\ubde1\ubde2\ubde3\ubde4\ubde5\ubde6\ubde7\ubde8\ubde9\ubdea\ubdeb\ubdec\ubded\ubdee\ubdef\ubdf0\ubdf1\ubdf2\ubdf3\ubdf4\ubdf5\ubdf6\ubdf7\ubdf8\ubdf9\ubdfa\ubdfb\ubdfc\ubdfd\ubdfe\ubdff\ube00\ube01\ube02\ube03\ube04\ube05\ube06\ube07\ube08\ube09\ube0a\ube0b\ube0c\ube0d\ube0e\ube0f\ube10\ube11\ube12\ube13\ube14\ube15\ube16\ube17\ube18\ube19\ube1a\ube1b\ube1c\ube1d\ube1e\ube1f\ube20\ube21\ube22\ube23\ube24\ube25\ube26\ube27\ube28\ube29\ube2a\ube2b\ube2c\ube2d\ube2e\ube2f\ube30\ube31\ube32\ube33\ube34\ube35\ube36\ube37\ube38\ube39\ube3a\ube3b\ube3c\ube3d\ube3e\ube3f\ube40\ube41\ube42\ube43\ube44\ube45\ube46\ube47\ube48\ube49\ube4a\ube4b\ube4c\ube4d\ube4e\ube4f\ube50\ube51\ube52\ube53\ube54\ube55\ube56\ube57\ube58\ube59\ube5a\ube5b\ube5c\ube5d\ube5e\ube5f\ube60\ube61\ube62\ube63\ube64\ube65\ube66\ube67\ube68\ube69\ube6a\ube6b\ube6c\ube6d\ube6e\ube6f\ube70\ube71\ube72\ube73\ube74\ube75\ube76\ube77\ube78\ube79\ube7a\ube7b\ube7c\ube7d\ube7e\ube7f\ube80\ube81\ube82\ube83\ube84\ube85\ube86\ube87\ube88\ube89\ube8a\ube8b\ube8c\ube8d\ube8e\ube8f\ube90\ube91\ube92\ube93\ube94\ube95\ube96\ube97\ube98\ube99\ube9a\ube9b\ube9c\ube9d\ube9e\ube9f\ubea0\ubea1\ubea2\ubea3\ubea4\ubea5\ubea6\ubea7\ubea8\ubea9\ubeaa\ubeab\ubeac\ubead\ubeae\ubeaf\ubeb0\ubeb1\ubeb2\ubeb3\ubeb4\ubeb5\ubeb6\ubeb7\ubeb8\ubeb9\ubeba\ubebb\ubebc\ubebd\ubebe\ubebf\ubec0\ubec1\ubec2\ubec3\ubec4\ubec5\ubec6\ubec7\ubec8\ubec9\ubeca\ubecb\ubecc\ubecd\ubece\ubecf\ubed0\ubed1\ubed2\ubed3\ubed4\ubed5\ubed6\ubed7\ubed8\ubed9\ubeda\ubedb\ubedc\ubedd\ubede\ubedf\ubee0\ubee1\ubee2\ubee3\ubee4\ubee5\ubee6\ubee7\ubee8\ubee9\ubeea\ubeeb\ubeec\ubeed\ubeee\ubeef\ubef0\ubef1\ubef2\ubef3\ubef4\ubef5\ubef6\ubef7\ubef8\ubef9\ubefa\ubefb\ubefc\ubefd\ubefe\ubeff\ubf00\ubf01\ubf02\ubf03\ubf04\ubf05\ubf06\ubf07\ubf08\ubf09\ubf0a\ubf0b\ubf0c\ubf0d\ubf0e\ubf0f\ubf10\ubf11\ubf12\ubf13\ubf14\ubf15\ubf16\ubf17\ubf18\ubf19\ubf1a\ubf1b\ubf1c\ubf1d\ubf1e\ubf1f\ubf20\ubf21\ubf22\ubf23\ubf24\ubf25\ubf26\ubf27\ubf28\ubf29\ubf2a\ubf2b\ubf2c\ubf2d\ubf2e\ubf2f\ubf30\ubf31\ubf32\ubf33\ubf34\ubf35\ubf36\ubf37\ubf38\ubf39\ubf3a\ubf3b\ubf3c\ubf3d\ubf3e\ubf3f\ubf40\ubf41\ubf42\ubf43\ubf44\ubf45\ubf46\ubf47\ubf48\ubf49\ubf4a\ubf4b\ubf4c\ubf4d\ubf4e\ubf4f\ubf50\ubf51\ubf52\ubf53\ubf54\ubf55\ubf56\ubf57\ubf58\ubf59\ubf5a\ubf5b\ubf5c\ubf5d\ubf5e\ubf5f\ubf60\ubf61\ubf62\ubf63\ubf64\ubf65\ubf66\ubf67\ubf68\ubf69\ubf6a\ubf6b\ubf6c\ubf6d\ubf6e\ubf6f\ubf70\ubf71\ubf72\ubf73\ubf74\ubf75\ubf76\ubf77\ubf78\ubf79\ubf7a\ubf7b\ubf7c\ubf7d\ubf7e\ubf7f\ubf80\ubf81\ubf82\ubf83\ubf84\ubf85\ubf86\ubf87\ubf88\ubf89\ubf8a\ubf8b\ubf8c\ubf8d\ubf8e\ubf8f\ubf90\ubf91\ubf92\ubf93\ubf94\ubf95\ubf96\ubf97\ubf98\ubf99\ubf9a\ubf9b\ubf9c\ubf9d\ubf9e\ubf9f\ubfa0\ubfa1\ubfa2\ubfa3\ubfa4\ubfa5\ubfa6\ubfa7\ubfa8\ubfa9\ubfaa\ubfab\ubfac\ubfad\ubfae\ubfaf\ubfb0\ubfb1\ubfb2\ubfb3\ubfb4\ubfb5\ubfb6\ubfb7\ubfb8\ubfb9\ubfba\ubfbb\ubfbc\ubfbd\ubfbe\ubfbf\ubfc0\ubfc1\ubfc2\ubfc3\ubfc4\ubfc5\ubfc6\ubfc7\ubfc8\ubfc9\ubfca\ubfcb\ubfcc\ubfcd\ubfce\ubfcf\ubfd0\ubfd1\ubfd2\ubfd3\ubfd4\ubfd5\ubfd6\ubfd7\ubfd8\ubfd9\ubfda\ubfdb\ubfdc\ubfdd\ubfde\ubfdf\ubfe0\ubfe1\ubfe2\ubfe3\ubfe4\ubfe5\ubfe6\ubfe7\ubfe8\ubfe9\ubfea\ubfeb\ubfec\ubfed\ubfee\ubfef\ubff0\ubff1\ubff2\ubff3\ubff4\ubff5\ubff6\ubff7\ubff8\ubff9\ubffa\ubffb\ubffc\ubffd\ubffe\ubfff\uc000\uc001\uc002\uc003\uc004\uc005\uc006\uc007\uc008\uc009\uc00a\uc00b\uc00c\uc00d\uc00e\uc00f\uc010\uc011\uc012\uc013\uc014\uc015\uc016\uc017\uc018\uc019\uc01a\uc01b\uc01c\uc01d\uc01e\uc01f\uc020\uc021\uc022\uc023\uc024\uc025\uc026\uc027\uc028\uc029\uc02a\uc02b\uc02c\uc02d\uc02e\uc02f\uc030\uc031\uc032\uc033\uc034\uc035\uc036\uc037\uc038\uc039\uc03a\uc03b\uc03c\uc03d\uc03e\uc03f\uc040\uc041\uc042\uc043\uc044\uc045\uc046\uc047\uc048\uc049\uc04a\uc04b\uc04c\uc04d\uc04e\uc04f\uc050\uc051\uc052\uc053\uc054\uc055\uc056\uc057\uc058\uc059\uc05a\uc05b\uc05c\uc05d\uc05e\uc05f\uc060\uc061\uc062\uc063\uc064\uc065\uc066\uc067\uc068\uc069\uc06a\uc06b\uc06c\uc06d\uc06e\uc06f\uc070\uc071\uc072\uc073\uc074\uc075\uc076\uc077\uc078\uc079\uc07a\uc07b\uc07c\uc07d\uc07e\uc07f\uc080\uc081\uc082\uc083\uc084\uc085\uc086\uc087\uc088\uc089\uc08a\uc08b\uc08c\uc08d\uc08e\uc08f\uc090\uc091\uc092\uc093\uc094\uc095\uc096\uc097\uc098\uc099\uc09a\uc09b\uc09c\uc09d\uc09e\uc09f\uc0a0\uc0a1\uc0a2\uc0a3\uc0a4\uc0a5\uc0a6\uc0a7\uc0a8\uc0a9\uc0aa\uc0ab\uc0ac\uc0ad\uc0ae\uc0af\uc0b0\uc0b1\uc0b2\uc0b3\uc0b4\uc0b5\uc0b6\uc0b7\uc0b8\uc0b9\uc0ba\uc0bb\uc0bc\uc0bd\uc0be\uc0bf\uc0c0\uc0c1\uc0c2\uc0c3\uc0c4\uc0c5\uc0c6\uc0c7\uc0c8\uc0c9\uc0ca\uc0cb\uc0cc\uc0cd\uc0ce\uc0cf\uc0d0\uc0d1\uc0d2\uc0d3\uc0d4\uc0d5\uc0d6\uc0d7\uc0d8\uc0d9\uc0da\uc0db\uc0dc\uc0dd\uc0de\uc0df\uc0e0\uc0e1\uc0e2\uc0e3\uc0e4\uc0e5\uc0e6\uc0e7\uc0e8\uc0e9\uc0ea\uc0eb\uc0ec\uc0ed\uc0ee\uc0ef\uc0f0\uc0f1\uc0f2\uc0f3\uc0f4\uc0f5\uc0f6\uc0f7\uc0f8\uc0f9\uc0fa\uc0fb\uc0fc\uc0fd\uc0fe\uc0ff\uc100\uc101\uc102\uc103\uc104\uc105\uc106\uc107\uc108\uc109\uc10a\uc10b\uc10c\uc10d\uc10e\uc10f\uc110\uc111\uc112\uc113\uc114\uc115\uc116\uc117\uc118\uc119\uc11a\uc11b\uc11c\uc11d\uc11e\uc11f\uc120\uc121\uc122\uc123\uc124\uc125\uc126\uc127\uc128\uc129\uc12a\uc12b\uc12c\uc12d\uc12e\uc12f\uc130\uc131\uc132\uc133\uc134\uc135\uc136\uc137\uc138\uc139\uc13a\uc13b\uc13c\uc13d\uc13e\uc13f\uc140\uc141\uc142\uc143\uc144\uc145\uc146\uc147\uc148\uc149\uc14a\uc14b\uc14c\uc14d\uc14e\uc14f\uc150\uc151\uc152\uc153\uc154\uc155\uc156\uc157\uc158\uc159\uc15a\uc15b\uc15c\uc15d\uc15e\uc15f\uc160\uc161\uc162\uc163\uc164\uc165\uc166\uc167\uc168\uc169\uc16a\uc16b\uc16c\uc16d\uc16e\uc16f\uc170\uc171\uc172\uc173\uc174\uc175\uc176\uc177\uc178\uc179\uc17a\uc17b\uc17c\uc17d\uc17e\uc17f\uc180\uc181\uc182\uc183\uc184\uc185\uc186\uc187\uc188\uc189\uc18a\uc18b\uc18c\uc18d\uc18e\uc18f\uc190\uc191\uc192\uc193\uc194\uc195\uc196\uc197\uc198\uc199\uc19a\uc19b\uc19c\uc19d\uc19e\uc19f\uc1a0\uc1a1\uc1a2\uc1a3\uc1a4\uc1a5\uc1a6\uc1a7\uc1a8\uc1a9\uc1aa\uc1ab\uc1ac\uc1ad\uc1ae\uc1af\uc1b0\uc1b1\uc1b2\uc1b3\uc1b4\uc1b5\uc1b6\uc1b7\uc1b8\uc1b9\uc1ba\uc1bb\uc1bc\uc1bd\uc1be\uc1bf\uc1c0\uc1c1\uc1c2\uc1c3\uc1c4\uc1c5\uc1c6\uc1c7\uc1c8\uc1c9\uc1ca\uc1cb\uc1cc\uc1cd\uc1ce\uc1cf\uc1d0\uc1d1\uc1d2\uc1d3\uc1d4\uc1d5\uc1d6\uc1d7\uc1d8\uc1d9\uc1da\uc1db\uc1dc\uc1dd\uc1de\uc1df\uc1e0\uc1e1\uc1e2\uc1e3\uc1e4\uc1e5\uc1e6\uc1e7\uc1e8\uc1e9\uc1ea\uc1eb\uc1ec\uc1ed\uc1ee\uc1ef\uc1f0\uc1f1\uc1f2\uc1f3\uc1f4\uc1f5\uc1f6\uc1f7\uc1f8\uc1f9\uc1fa\uc1fb\uc1fc\uc1fd\uc1fe\uc1ff\uc200\uc201\uc202\uc203\uc204\uc205\uc206\uc207\uc208\uc209\uc20a\uc20b\uc20c\uc20d\uc20e\uc20f\uc210\uc211\uc212\uc213\uc214\uc215\uc216\uc217\uc218\uc219\uc21a\uc21b\uc21c\uc21d\uc21e\uc21f\uc220\uc221\uc222\uc223\uc224\uc225\uc226\uc227\uc228\uc229\uc22a\uc22b\uc22c\uc22d\uc22e\uc22f\uc230\uc231\uc232\uc233\uc234\uc235\uc236\uc237\uc238\uc239\uc23a\uc23b\uc23c\uc23d\uc23e\uc23f\uc240\uc241\uc242\uc243\uc244\uc245\uc246\uc247\uc248\uc249\uc24a\uc24b\uc24c\uc24d\uc24e\uc24f\uc250\uc251\uc252\uc253\uc254\uc255\uc256\uc257\uc258\uc259\uc25a\uc25b\uc25c\uc25d\uc25e\uc25f\uc260\uc261\uc262\uc263\uc264\uc265\uc266\uc267\uc268\uc269\uc26a\uc26b\uc26c\uc26d\uc26e\uc26f\uc270\uc271\uc272\uc273\uc274\uc275\uc276\uc277\uc278\uc279\uc27a\uc27b\uc27c\uc27d\uc27e\uc27f\uc280\uc281\uc282\uc283\uc284\uc285\uc286\uc287\uc288\uc289\uc28a\uc28b\uc28c\uc28d\uc28e\uc28f\uc290\uc291\uc292\uc293\uc294\uc295\uc296\uc297\uc298\uc299\uc29a\uc29b\uc29c\uc29d\uc29e\uc29f\uc2a0\uc2a1\uc2a2\uc2a3\uc2a4\uc2a5\uc2a6\uc2a7\uc2a8\uc2a9\uc2aa\uc2ab\uc2ac\uc2ad\uc2ae\uc2af\uc2b0\uc2b1\uc2b2\uc2b3\uc2b4\uc2b5\uc2b6\uc2b7\uc2b8\uc2b9\uc2ba\uc2bb\uc2bc\uc2bd\uc2be\uc2bf\uc2c0\uc2c1\uc2c2\uc2c3\uc2c4\uc2c5\uc2c6\uc2c7\uc2c8\uc2c9\uc2ca\uc2cb\uc2cc\uc2cd\uc2ce\uc2cf\uc2d0\uc2d1\uc2d2\uc2d3\uc2d4\uc2d5\uc2d6\uc2d7\uc2d8\uc2d9\uc2da\uc2db\uc2dc\uc2dd\uc2de\uc2df\uc2e0\uc2e1\uc2e2\uc2e3\uc2e4\uc2e5\uc2e6\uc2e7\uc2e8\uc2e9\uc2ea\uc2eb\uc2ec\uc2ed\uc2ee\uc2ef\uc2f0\uc2f1\uc2f2\uc2f3\uc2f4\uc2f5\uc2f6\uc2f7\uc2f8\uc2f9\uc2fa\uc2fb\uc2fc\uc2fd\uc2fe\uc2ff\uc300\uc301\uc302\uc303\uc304\uc305\uc306\uc307\uc308\uc309\uc30a\uc30b\uc30c\uc30d\uc30e\uc30f\uc310\uc311\uc312\uc313\uc314\uc315\uc316\uc317\uc318\uc319\uc31a\uc31b\uc31c\uc31d\uc31e\uc31f\uc320\uc321\uc322\uc323\uc324\uc325\uc326\uc327\uc328\uc329\uc32a\uc32b\uc32c\uc32d\uc32e\uc32f\uc330\uc331\uc332\uc333\uc334\uc335\uc336\uc337\uc338\uc339\uc33a\uc33b\uc33c\uc33d\uc33e\uc33f\uc340\uc341\uc342\uc343\uc344\uc345\uc346\uc347\uc348\uc349\uc34a\uc34b\uc34c\uc34d\uc34e\uc34f\uc350\uc351\uc352\uc353\uc354\uc355\uc356\uc357\uc358\uc359\uc35a\uc35b\uc35c\uc35d\uc35e\uc35f\uc360\uc361\uc362\uc363\uc364\uc365\uc366\uc367\uc368\uc369\uc36a\uc36b\uc36c\uc36d\uc36e\uc36f\uc370\uc371\uc372\uc373\uc374\uc375\uc376\uc377\uc378\uc379\uc37a\uc37b\uc37c\uc37d\uc37e\uc37f\uc380\uc381\uc382\uc383\uc384\uc385\uc386\uc387\uc388\uc389\uc38a\uc38b\uc38c\uc38d\uc38e\uc38f\uc390\uc391\uc392\uc393\uc394\uc395\uc396\uc397\uc398\uc399\uc39a\uc39b\uc39c\uc39d\uc39e\uc39f\uc3a0\uc3a1\uc3a2\uc3a3\uc3a4\uc3a5\uc3a6\uc3a7\uc3a8\uc3a9\uc3aa\uc3ab\uc3ac\uc3ad\uc3ae\uc3af\uc3b0\uc3b1\uc3b2\uc3b3\uc3b4\uc3b5\uc3b6\uc3b7\uc3b8\uc3b9\uc3ba\uc3bb\uc3bc\uc3bd\uc3be\uc3bf\uc3c0\uc3c1\uc3c2\uc3c3\uc3c4\uc3c5\uc3c6\uc3c7\uc3c8\uc3c9\uc3ca\uc3cb\uc3cc\uc3cd\uc3ce\uc3cf\uc3d0\uc3d1\uc3d2\uc3d3\uc3d4\uc3d5\uc3d6\uc3d7\uc3d8\uc3d9\uc3da\uc3db\uc3dc\uc3dd\uc3de\uc3df\uc3e0\uc3e1\uc3e2\uc3e3\uc3e4\uc3e5\uc3e6\uc3e7\uc3e8\uc3e9\uc3ea\uc3eb\uc3ec\uc3ed\uc3ee\uc3ef\uc3f0\uc3f1\uc3f2\uc3f3\uc3f4\uc3f5\uc3f6\uc3f7\uc3f8\uc3f9\uc3fa\uc3fb\uc3fc\uc3fd\uc3fe\uc3ff\uc400\uc401\uc402\uc403\uc404\uc405\uc406\uc407\uc408\uc409\uc40a\uc40b\uc40c\uc40d\uc40e\uc40f\uc410\uc411\uc412\uc413\uc414\uc415\uc416\uc417\uc418\uc419\uc41a\uc41b\uc41c\uc41d\uc41e\uc41f\uc420\uc421\uc422\uc423\uc424\uc425\uc426\uc427\uc428\uc429\uc42a\uc42b\uc42c\uc42d\uc42e\uc42f\uc430\uc431\uc432\uc433\uc434\uc435\uc436\uc437\uc438\uc439\uc43a\uc43b\uc43c\uc43d\uc43e\uc43f\uc440\uc441\uc442\uc443\uc444\uc445\uc446\uc447\uc448\uc449\uc44a\uc44b\uc44c\uc44d\uc44e\uc44f\uc450\uc451\uc452\uc453\uc454\uc455\uc456\uc457\uc458\uc459\uc45a\uc45b\uc45c\uc45d\uc45e\uc45f\uc460\uc461\uc462\uc463\uc464\uc465\uc466\uc467\uc468\uc469\uc46a\uc46b\uc46c\uc46d\uc46e\uc46f\uc470\uc471\uc472\uc473\uc474\uc475\uc476\uc477\uc478\uc479\uc47a\uc47b\uc47c\uc47d\uc47e\uc47f\uc480\uc481\uc482\uc483\uc484\uc485\uc486\uc487\uc488\uc489\uc48a\uc48b\uc48c\uc48d\uc48e\uc48f\uc490\uc491\uc492\uc493\uc494\uc495\uc496\uc497\uc498\uc499\uc49a\uc49b\uc49c\uc49d\uc49e\uc49f\uc4a0\uc4a1\uc4a2\uc4a3\uc4a4\uc4a5\uc4a6\uc4a7\uc4a8\uc4a9\uc4aa\uc4ab\uc4ac\uc4ad\uc4ae\uc4af\uc4b0\uc4b1\uc4b2\uc4b3\uc4b4\uc4b5\uc4b6\uc4b7\uc4b8\uc4b9\uc4ba\uc4bb\uc4bc\uc4bd\uc4be\uc4bf\uc4c0\uc4c1\uc4c2\uc4c3\uc4c4\uc4c5\uc4c6\uc4c7\uc4c8\uc4c9\uc4ca\uc4cb\uc4cc\uc4cd\uc4ce\uc4cf\uc4d0\uc4d1\uc4d2\uc4d3\uc4d4\uc4d5\uc4d6\uc4d7\uc4d8\uc4d9\uc4da\uc4db\uc4dc\uc4dd\uc4de\uc4df\uc4e0\uc4e1\uc4e2\uc4e3\uc4e4\uc4e5\uc4e6\uc4e7\uc4e8\uc4e9\uc4ea\uc4eb\uc4ec\uc4ed\uc4ee\uc4ef\uc4f0\uc4f1\uc4f2\uc4f3\uc4f4\uc4f5\uc4f6\uc4f7\uc4f8\uc4f9\uc4fa\uc4fb\uc4fc\uc4fd\uc4fe\uc4ff\uc500\uc501\uc502\uc503\uc504\uc505\uc506\uc507\uc508\uc509\uc50a\uc50b\uc50c\uc50d\uc50e\uc50f\uc510\uc511\uc512\uc513\uc514\uc515\uc516\uc517\uc518\uc519\uc51a\uc51b\uc51c\uc51d\uc51e\uc51f\uc520\uc521\uc522\uc523\uc524\uc525\uc526\uc527\uc528\uc529\uc52a\uc52b\uc52c\uc52d\uc52e\uc52f\uc530\uc531\uc532\uc533\uc534\uc535\uc536\uc537\uc538\uc539\uc53a\uc53b\uc53c\uc53d\uc53e\uc53f\uc540\uc541\uc542\uc543\uc544\uc545\uc546\uc547\uc548\uc549\uc54a\uc54b\uc54c\uc54d\uc54e\uc54f\uc550\uc551\uc552\uc553\uc554\uc555\uc556\uc557\uc558\uc559\uc55a\uc55b\uc55c\uc55d\uc55e\uc55f\uc560\uc561\uc562\uc563\uc564\uc565\uc566\uc567\uc568\uc569\uc56a\uc56b\uc56c\uc56d\uc56e\uc56f\uc570\uc571\uc572\uc573\uc574\uc575\uc576\uc577\uc578\uc579\uc57a\uc57b\uc57c\uc57d\uc57e\uc57f\uc580\uc581\uc582\uc583\uc584\uc585\uc586\uc587\uc588\uc589\uc58a\uc58b\uc58c\uc58d\uc58e\uc58f\uc590\uc591\uc592\uc593\uc594\uc595\uc596\uc597\uc598\uc599\uc59a\uc59b\uc59c\uc59d\uc59e\uc59f\uc5a0\uc5a1\uc5a2\uc5a3\uc5a4\uc5a5\uc5a6\uc5a7\uc5a8\uc5a9\uc5aa\uc5ab\uc5ac\uc5ad\uc5ae\uc5af\uc5b0\uc5b1\uc5b2\uc5b3\uc5b4\uc5b5\uc5b6\uc5b7\uc5b8\uc5b9\uc5ba\uc5bb\uc5bc\uc5bd\uc5be\uc5bf\uc5c0\uc5c1\uc5c2\uc5c3\uc5c4\uc5c5\uc5c6\uc5c7\uc5c8\uc5c9\uc5ca\uc5cb\uc5cc\uc5cd\uc5ce\uc5cf\uc5d0\uc5d1\uc5d2\uc5d3\uc5d4\uc5d5\uc5d6\uc5d7\uc5d8\uc5d9\uc5da\uc5db\uc5dc\uc5dd\uc5de\uc5df\uc5e0\uc5e1\uc5e2\uc5e3\uc5e4\uc5e5\uc5e6\uc5e7\uc5e8\uc5e9\uc5ea\uc5eb\uc5ec\uc5ed\uc5ee\uc5ef\uc5f0\uc5f1\uc5f2\uc5f3\uc5f4\uc5f5\uc5f6\uc5f7\uc5f8\uc5f9\uc5fa\uc5fb\uc5fc\uc5fd\uc5fe\uc5ff\uc600\uc601\uc602\uc603\uc604\uc605\uc606\uc607\uc608\uc609\uc60a\uc60b\uc60c\uc60d\uc60e\uc60f\uc610\uc611\uc612\uc613\uc614\uc615\uc616\uc617\uc618\uc619\uc61a\uc61b\uc61c\uc61d\uc61e\uc61f\uc620\uc621\uc622\uc623\uc624\uc625\uc626\uc627\uc628\uc629\uc62a\uc62b\uc62c\uc62d\uc62e\uc62f\uc630\uc631\uc632\uc633\uc634\uc635\uc636\uc637\uc638\uc639\uc63a\uc63b\uc63c\uc63d\uc63e\uc63f\uc640\uc641\uc642\uc643\uc644\uc645\uc646\uc647\uc648\uc649\uc64a\uc64b\uc64c\uc64d\uc64e\uc64f\uc650\uc651\uc652\uc653\uc654\uc655\uc656\uc657\uc658\uc659\uc65a\uc65b\uc65c\uc65d\uc65e\uc65f\uc660\uc661\uc662\uc663\uc664\uc665\uc666\uc667\uc668\uc669\uc66a\uc66b\uc66c\uc66d\uc66e\uc66f\uc670\uc671\uc672\uc673\uc674\uc675\uc676\uc677\uc678\uc679\uc67a\uc67b\uc67c\uc67d\uc67e\uc67f\uc680\uc681\uc682\uc683\uc684\uc685\uc686\uc687\uc688\uc689\uc68a\uc68b\uc68c\uc68d\uc68e\uc68f\uc690\uc691\uc692\uc693\uc694\uc695\uc696\uc697\uc698\uc699\uc69a\uc69b\uc69c\uc69d\uc69e\uc69f\uc6a0\uc6a1\uc6a2\uc6a3\uc6a4\uc6a5\uc6a6\uc6a7\uc6a8\uc6a9\uc6aa\uc6ab\uc6ac\uc6ad\uc6ae\uc6af\uc6b0\uc6b1\uc6b2\uc6b3\uc6b4\uc6b5\uc6b6\uc6b7\uc6b8\uc6b9\uc6ba\uc6bb\uc6bc\uc6bd\uc6be\uc6bf\uc6c0\uc6c1\uc6c2\uc6c3\uc6c4\uc6c5\uc6c6\uc6c7\uc6c8\uc6c9\uc6ca\uc6cb\uc6cc\uc6cd\uc6ce\uc6cf\uc6d0\uc6d1\uc6d2\uc6d3\uc6d4\uc6d5\uc6d6\uc6d7\uc6d8\uc6d9\uc6da\uc6db\uc6dc\uc6dd\uc6de\uc6df\uc6e0\uc6e1\uc6e2\uc6e3\uc6e4\uc6e5\uc6e6\uc6e7\uc6e8\uc6e9\uc6ea\uc6eb\uc6ec\uc6ed\uc6ee\uc6ef\uc6f0\uc6f1\uc6f2\uc6f3\uc6f4\uc6f5\uc6f6\uc6f7\uc6f8\uc6f9\uc6fa\uc6fb\uc6fc\uc6fd\uc6fe\uc6ff\uc700\uc701\uc702\uc703\uc704\uc705\uc706\uc707\uc708\uc709\uc70a\uc70b\uc70c\uc70d\uc70e\uc70f\uc710\uc711\uc712\uc713\uc714\uc715\uc716\uc717\uc718\uc719\uc71a\uc71b\uc71c\uc71d\uc71e\uc71f\uc720\uc721\uc722\uc723\uc724\uc725\uc726\uc727\uc728\uc729\uc72a\uc72b\uc72c\uc72d\uc72e\uc72f\uc730\uc731\uc732\uc733\uc734\uc735\uc736\uc737\uc738\uc739\uc73a\uc73b\uc73c\uc73d\uc73e\uc73f\uc740\uc741\uc742\uc743\uc744\uc745\uc746\uc747\uc748\uc749\uc74a\uc74b\uc74c\uc74d\uc74e\uc74f\uc750\uc751\uc752\uc753\uc754\uc755\uc756\uc757\uc758\uc759\uc75a\uc75b\uc75c\uc75d\uc75e\uc75f\uc760\uc761\uc762\uc763\uc764\uc765\uc766\uc767\uc768\uc769\uc76a\uc76b\uc76c\uc76d\uc76e\uc76f\uc770\uc771\uc772\uc773\uc774\uc775\uc776\uc777\uc778\uc779\uc77a\uc77b\uc77c\uc77d\uc77e\uc77f\uc780\uc781\uc782\uc783\uc784\uc785\uc786\uc787\uc788\uc789\uc78a\uc78b\uc78c\uc78d\uc78e\uc78f\uc790\uc791\uc792\uc793\uc794\uc795\uc796\uc797\uc798\uc799\uc79a\uc79b\uc79c\uc79d\uc79e\uc79f\uc7a0\uc7a1\uc7a2\uc7a3\uc7a4\uc7a5\uc7a6\uc7a7\uc7a8\uc7a9\uc7aa\uc7ab\uc7ac\uc7ad\uc7ae\uc7af\uc7b0\uc7b1\uc7b2\uc7b3\uc7b4\uc7b5\uc7b6\uc7b7\uc7b8\uc7b9\uc7ba\uc7bb\uc7bc\uc7bd\uc7be\uc7bf\uc7c0\uc7c1\uc7c2\uc7c3\uc7c4\uc7c5\uc7c6\uc7c7\uc7c8\uc7c9\uc7ca\uc7cb\uc7cc\uc7cd\uc7ce\uc7cf\uc7d0\uc7d1\uc7d2\uc7d3\uc7d4\uc7d5\uc7d6\uc7d7\uc7d8\uc7d9\uc7da\uc7db\uc7dc\uc7dd\uc7de\uc7df\uc7e0\uc7e1\uc7e2\uc7e3\uc7e4\uc7e5\uc7e6\uc7e7\uc7e8\uc7e9\uc7ea\uc7eb\uc7ec\uc7ed\uc7ee\uc7ef\uc7f0\uc7f1\uc7f2\uc7f3\uc7f4\uc7f5\uc7f6\uc7f7\uc7f8\uc7f9\uc7fa\uc7fb\uc7fc\uc7fd\uc7fe\uc7ff\uc800\uc801\uc802\uc803\uc804\uc805\uc806\uc807\uc808\uc809\uc80a\uc80b\uc80c\uc80d\uc80e\uc80f\uc810\uc811\uc812\uc813\uc814\uc815\uc816\uc817\uc818\uc819\uc81a\uc81b\uc81c\uc81d\uc81e\uc81f\uc820\uc821\uc822\uc823\uc824\uc825\uc826\uc827\uc828\uc829\uc82a\uc82b\uc82c\uc82d\uc82e\uc82f\uc830\uc831\uc832\uc833\uc834\uc835\uc836\uc837\uc838\uc839\uc83a\uc83b\uc83c\uc83d\uc83e\uc83f\uc840\uc841\uc842\uc843\uc844\uc845\uc846\uc847\uc848\uc849\uc84a\uc84b\uc84c\uc84d\uc84e\uc84f\uc850\uc851\uc852\uc853\uc854\uc855\uc856\uc857\uc858\uc859\uc85a\uc85b\uc85c\uc85d\uc85e\uc85f\uc860\uc861\uc862\uc863\uc864\uc865\uc866\uc867\uc868\uc869\uc86a\uc86b\uc86c\uc86d\uc86e\uc86f\uc870\uc871\uc872\uc873\uc874\uc875\uc876\uc877\uc878\uc879\uc87a\uc87b\uc87c\uc87d\uc87e\uc87f\uc880\uc881\uc882\uc883\uc884\uc885\uc886\uc887\uc888\uc889\uc88a\uc88b\uc88c\uc88d\uc88e\uc88f\uc890\uc891\uc892\uc893\uc894\uc895\uc896\uc897\uc898\uc899\uc89a\uc89b\uc89c\uc89d\uc89e\uc89f\uc8a0\uc8a1\uc8a2\uc8a3\uc8a4\uc8a5\uc8a6\uc8a7\uc8a8\uc8a9\uc8aa\uc8ab\uc8ac\uc8ad\uc8ae\uc8af\uc8b0\uc8b1\uc8b2\uc8b3\uc8b4\uc8b5\uc8b6\uc8b7\uc8b8\uc8b9\uc8ba\uc8bb\uc8bc\uc8bd\uc8be\uc8bf\uc8c0\uc8c1\uc8c2\uc8c3\uc8c4\uc8c5\uc8c6\uc8c7\uc8c8\uc8c9\uc8ca\uc8cb\uc8cc\uc8cd\uc8ce\uc8cf\uc8d0\uc8d1\uc8d2\uc8d3\uc8d4\uc8d5\uc8d6\uc8d7\uc8d8\uc8d9\uc8da\uc8db\uc8dc\uc8dd\uc8de\uc8df\uc8e0\uc8e1\uc8e2\uc8e3\uc8e4\uc8e5\uc8e6\uc8e7\uc8e8\uc8e9\uc8ea\uc8eb\uc8ec\uc8ed\uc8ee\uc8ef\uc8f0\uc8f1\uc8f2\uc8f3\uc8f4\uc8f5\uc8f6\uc8f7\uc8f8\uc8f9\uc8fa\uc8fb\uc8fc\uc8fd\uc8fe\uc8ff\uc900\uc901\uc902\uc903\uc904\uc905\uc906\uc907\uc908\uc909\uc90a\uc90b\uc90c\uc90d\uc90e\uc90f\uc910\uc911\uc912\uc913\uc914\uc915\uc916\uc917\uc918\uc919\uc91a\uc91b\uc91c\uc91d\uc91e\uc91f\uc920\uc921\uc922\uc923\uc924\uc925\uc926\uc927\uc928\uc929\uc92a\uc92b\uc92c\uc92d\uc92e\uc92f\uc930\uc931\uc932\uc933\uc934\uc935\uc936\uc937\uc938\uc939\uc93a\uc93b\uc93c\uc93d\uc93e\uc93f\uc940\uc941\uc942\uc943\uc944\uc945\uc946\uc947\uc948\uc949\uc94a\uc94b\uc94c\uc94d\uc94e\uc94f\uc950\uc951\uc952\uc953\uc954\uc955\uc956\uc957\uc958\uc959\uc95a\uc95b\uc95c\uc95d\uc95e\uc95f\uc960\uc961\uc962\uc963\uc964\uc965\uc966\uc967\uc968\uc969\uc96a\uc96b\uc96c\uc96d\uc96e\uc96f\uc970\uc971\uc972\uc973\uc974\uc975\uc976\uc977\uc978\uc979\uc97a\uc97b\uc97c\uc97d\uc97e\uc97f\uc980\uc981\uc982\uc983\uc984\uc985\uc986\uc987\uc988\uc989\uc98a\uc98b\uc98c\uc98d\uc98e\uc98f\uc990\uc991\uc992\uc993\uc994\uc995\uc996\uc997\uc998\uc999\uc99a\uc99b\uc99c\uc99d\uc99e\uc99f\uc9a0\uc9a1\uc9a2\uc9a3\uc9a4\uc9a5\uc9a6\uc9a7\uc9a8\uc9a9\uc9aa\uc9ab\uc9ac\uc9ad\uc9ae\uc9af\uc9b0\uc9b1\uc9b2\uc9b3\uc9b4\uc9b5\uc9b6\uc9b7\uc9b8\uc9b9\uc9ba\uc9bb\uc9bc\uc9bd\uc9be\uc9bf\uc9c0\uc9c1\uc9c2\uc9c3\uc9c4\uc9c5\uc9c6\uc9c7\uc9c8\uc9c9\uc9ca\uc9cb\uc9cc\uc9cd\uc9ce\uc9cf\uc9d0\uc9d1\uc9d2\uc9d3\uc9d4\uc9d5\uc9d6\uc9d7\uc9d8\uc9d9\uc9da\uc9db\uc9dc\uc9dd\uc9de\uc9df\uc9e0\uc9e1\uc9e2\uc9e3\uc9e4\uc9e5\uc9e6\uc9e7\uc9e8\uc9e9\uc9ea\uc9eb\uc9ec\uc9ed\uc9ee\uc9ef\uc9f0\uc9f1\uc9f2\uc9f3\uc9f4\uc9f5\uc9f6\uc9f7\uc9f8\uc9f9\uc9fa\uc9fb\uc9fc\uc9fd\uc9fe\uc9ff\uca00\uca01\uca02\uca03\uca04\uca05\uca06\uca07\uca08\uca09\uca0a\uca0b\uca0c\uca0d\uca0e\uca0f\uca10\uca11\uca12\uca13\uca14\uca15\uca16\uca17\uca18\uca19\uca1a\uca1b\uca1c\uca1d\uca1e\uca1f\uca20\uca21\uca22\uca23\uca24\uca25\uca26\uca27\uca28\uca29\uca2a\uca2b\uca2c\uca2d\uca2e\uca2f\uca30\uca31\uca32\uca33\uca34\uca35\uca36\uca37\uca38\uca39\uca3a\uca3b\uca3c\uca3d\uca3e\uca3f\uca40\uca41\uca42\uca43\uca44\uca45\uca46\uca47\uca48\uca49\uca4a\uca4b\uca4c\uca4d\uca4e\uca4f\uca50\uca51\uca52\uca53\uca54\uca55\uca56\uca57\uca58\uca59\uca5a\uca5b\uca5c\uca5d\uca5e\uca5f\uca60\uca61\uca62\uca63\uca64\uca65\uca66\uca67\uca68\uca69\uca6a\uca6b\uca6c\uca6d\uca6e\uca6f\uca70\uca71\uca72\uca73\uca74\uca75\uca76\uca77\uca78\uca79\uca7a\uca7b\uca7c\uca7d\uca7e\uca7f\uca80\uca81\uca82\uca83\uca84\uca85\uca86\uca87\uca88\uca89\uca8a\uca8b\uca8c\uca8d\uca8e\uca8f\uca90\uca91\uca92\uca93\uca94\uca95\uca96\uca97\uca98\uca99\uca9a\uca9b\uca9c\uca9d\uca9e\uca9f\ucaa0\ucaa1\ucaa2\ucaa3\ucaa4\ucaa5\ucaa6\ucaa7\ucaa8\ucaa9\ucaaa\ucaab\ucaac\ucaad\ucaae\ucaaf\ucab0\ucab1\ucab2\ucab3\ucab4\ucab5\ucab6\ucab7\ucab8\ucab9\ucaba\ucabb\ucabc\ucabd\ucabe\ucabf\ucac0\ucac1\ucac2\ucac3\ucac4\ucac5\ucac6\ucac7\ucac8\ucac9\ucaca\ucacb\ucacc\ucacd\ucace\ucacf\ucad0\ucad1\ucad2\ucad3\ucad4\ucad5\ucad6\ucad7\ucad8\ucad9\ucada\ucadb\ucadc\ucadd\ucade\ucadf\ucae0\ucae1\ucae2\ucae3\ucae4\ucae5\ucae6\ucae7\ucae8\ucae9\ucaea\ucaeb\ucaec\ucaed\ucaee\ucaef\ucaf0\ucaf1\ucaf2\ucaf3\ucaf4\ucaf5\ucaf6\ucaf7\ucaf8\ucaf9\ucafa\ucafb\ucafc\ucafd\ucafe\ucaff\ucb00\ucb01\ucb02\ucb03\ucb04\ucb05\ucb06\ucb07\ucb08\ucb09\ucb0a\ucb0b\ucb0c\ucb0d\ucb0e\ucb0f\ucb10\ucb11\ucb12\ucb13\ucb14\ucb15\ucb16\ucb17\ucb18\ucb19\ucb1a\ucb1b\ucb1c\ucb1d\ucb1e\ucb1f\ucb20\ucb21\ucb22\ucb23\ucb24\ucb25\ucb26\ucb27\ucb28\ucb29\ucb2a\ucb2b\ucb2c\ucb2d\ucb2e\ucb2f\ucb30\ucb31\ucb32\ucb33\ucb34\ucb35\ucb36\ucb37\ucb38\ucb39\ucb3a\ucb3b\ucb3c\ucb3d\ucb3e\ucb3f\ucb40\ucb41\ucb42\ucb43\ucb44\ucb45\ucb46\ucb47\ucb48\ucb49\ucb4a\ucb4b\ucb4c\ucb4d\ucb4e\ucb4f\ucb50\ucb51\ucb52\ucb53\ucb54\ucb55\ucb56\ucb57\ucb58\ucb59\ucb5a\ucb5b\ucb5c\ucb5d\ucb5e\ucb5f\ucb60\ucb61\ucb62\ucb63\ucb64\ucb65\ucb66\ucb67\ucb68\ucb69\ucb6a\ucb6b\ucb6c\ucb6d\ucb6e\ucb6f\ucb70\ucb71\ucb72\ucb73\ucb74\ucb75\ucb76\ucb77\ucb78\ucb79\ucb7a\ucb7b\ucb7c\ucb7d\ucb7e\ucb7f\ucb80\ucb81\ucb82\ucb83\ucb84\ucb85\ucb86\ucb87\ucb88\ucb89\ucb8a\ucb8b\ucb8c\ucb8d\ucb8e\ucb8f\ucb90\ucb91\ucb92\ucb93\ucb94\ucb95\ucb96\ucb97\ucb98\ucb99\ucb9a\ucb9b\ucb9c\ucb9d\ucb9e\ucb9f\ucba0\ucba1\ucba2\ucba3\ucba4\ucba5\ucba6\ucba7\ucba8\ucba9\ucbaa\ucbab\ucbac\ucbad\ucbae\ucbaf\ucbb0\ucbb1\ucbb2\ucbb3\ucbb4\ucbb5\ucbb6\ucbb7\ucbb8\ucbb9\ucbba\ucbbb\ucbbc\ucbbd\ucbbe\ucbbf\ucbc0\ucbc1\ucbc2\ucbc3\ucbc4\ucbc5\ucbc6\ucbc7\ucbc8\ucbc9\ucbca\ucbcb\ucbcc\ucbcd\ucbce\ucbcf\ucbd0\ucbd1\ucbd2\ucbd3\ucbd4\ucbd5\ucbd6\ucbd7\ucbd8\ucbd9\ucbda\ucbdb\ucbdc\ucbdd\ucbde\ucbdf\ucbe0\ucbe1\ucbe2\ucbe3\ucbe4\ucbe5\ucbe6\ucbe7\ucbe8\ucbe9\ucbea\ucbeb\ucbec\ucbed\ucbee\ucbef\ucbf0\ucbf1\ucbf2\ucbf3\ucbf4\ucbf5\ucbf6\ucbf7\ucbf8\ucbf9\ucbfa\ucbfb\ucbfc\ucbfd\ucbfe\ucbff\ucc00\ucc01\ucc02\ucc03\ucc04\ucc05\ucc06\ucc07\ucc08\ucc09\ucc0a\ucc0b\ucc0c\ucc0d\ucc0e\ucc0f\ucc10\ucc11\ucc12\ucc13\ucc14\ucc15\ucc16\ucc17\ucc18\ucc19\ucc1a\ucc1b\ucc1c\ucc1d\ucc1e\ucc1f\ucc20\ucc21\ucc22\ucc23\ucc24\ucc25\ucc26\ucc27\ucc28\ucc29\ucc2a\ucc2b\ucc2c\ucc2d\ucc2e\ucc2f\ucc30\ucc31\ucc32\ucc33\ucc34\ucc35\ucc36\ucc37\ucc38\ucc39\ucc3a\ucc3b\ucc3c\ucc3d\ucc3e\ucc3f\ucc40\ucc41\ucc42\ucc43\ucc44\ucc45\ucc46\ucc47\ucc48\ucc49\ucc4a\ucc4b\ucc4c\ucc4d\ucc4e\ucc4f\ucc50\ucc51\ucc52\ucc53\ucc54\ucc55\ucc56\ucc57\ucc58\ucc59\ucc5a\ucc5b\ucc5c\ucc5d\ucc5e\ucc5f\ucc60\ucc61\ucc62\ucc63\ucc64\ucc65\ucc66\ucc67\ucc68\ucc69\ucc6a\ucc6b\ucc6c\ucc6d\ucc6e\ucc6f\ucc70\ucc71\ucc72\ucc73\ucc74\ucc75\ucc76\ucc77\ucc78\ucc79\ucc7a\ucc7b\ucc7c\ucc7d\ucc7e\ucc7f\ucc80\ucc81\ucc82\ucc83\ucc84\ucc85\ucc86\ucc87\ucc88\ucc89\ucc8a\ucc8b\ucc8c\ucc8d\ucc8e\ucc8f\ucc90\ucc91\ucc92\ucc93\ucc94\ucc95\ucc96\ucc97\ucc98\ucc99\ucc9a\ucc9b\ucc9c\ucc9d\ucc9e\ucc9f\ucca0\ucca1\ucca2\ucca3\ucca4\ucca5\ucca6\ucca7\ucca8\ucca9\uccaa\uccab\uccac\uccad\uccae\uccaf\uccb0\uccb1\uccb2\uccb3\uccb4\uccb5\uccb6\uccb7\uccb8\uccb9\uccba\uccbb\uccbc\uccbd\uccbe\uccbf\uccc0\uccc1\uccc2\uccc3\uccc4\uccc5\uccc6\uccc7\uccc8\uccc9\uccca\ucccb\ucccc\ucccd\uccce\ucccf\uccd0\uccd1\uccd2\uccd3\uccd4\uccd5\uccd6\uccd7\uccd8\uccd9\uccda\uccdb\uccdc\uccdd\uccde\uccdf\ucce0\ucce1\ucce2\ucce3\ucce4\ucce5\ucce6\ucce7\ucce8\ucce9\uccea\ucceb\uccec\ucced\uccee\uccef\uccf0\uccf1\uccf2\uccf3\uccf4\uccf5\uccf6\uccf7\uccf8\uccf9\uccfa\uccfb\uccfc\uccfd\uccfe\uccff\ucd00\ucd01\ucd02\ucd03\ucd04\ucd05\ucd06\ucd07\ucd08\ucd09\ucd0a\ucd0b\ucd0c\ucd0d\ucd0e\ucd0f\ucd10\ucd11\ucd12\ucd13\ucd14\ucd15\ucd16\ucd17\ucd18\ucd19\ucd1a\ucd1b\ucd1c\ucd1d\ucd1e\ucd1f\ucd20\ucd21\ucd22\ucd23\ucd24\ucd25\ucd26\ucd27\ucd28\ucd29\ucd2a\ucd2b\ucd2c\ucd2d\ucd2e\ucd2f\ucd30\ucd31\ucd32\ucd33\ucd34\ucd35\ucd36\ucd37\ucd38\ucd39\ucd3a\ucd3b\ucd3c\ucd3d\ucd3e\ucd3f\ucd40\ucd41\ucd42\ucd43\ucd44\ucd45\ucd46\ucd47\ucd48\ucd49\ucd4a\ucd4b\ucd4c\ucd4d\ucd4e\ucd4f\ucd50\ucd51\ucd52\ucd53\ucd54\ucd55\ucd56\ucd57\ucd58\ucd59\ucd5a\ucd5b\ucd5c\ucd5d\ucd5e\ucd5f\ucd60\ucd61\ucd62\ucd63\ucd64\ucd65\ucd66\ucd67\ucd68\ucd69\ucd6a\ucd6b\ucd6c\ucd6d\ucd6e\ucd6f\ucd70\ucd71\ucd72\ucd73\ucd74\ucd75\ucd76\ucd77\ucd78\ucd79\ucd7a\ucd7b\ucd7c\ucd7d\ucd7e\ucd7f\ucd80\ucd81\ucd82\ucd83\ucd84\ucd85\ucd86\ucd87\ucd88\ucd89\ucd8a\ucd8b\ucd8c\ucd8d\ucd8e\ucd8f\ucd90\ucd91\ucd92\ucd93\ucd94\ucd95\ucd96\ucd97\ucd98\ucd99\ucd9a\ucd9b\ucd9c\ucd9d\ucd9e\ucd9f\ucda0\ucda1\ucda2\ucda3\ucda4\ucda5\ucda6\ucda7\ucda8\ucda9\ucdaa\ucdab\ucdac\ucdad\ucdae\ucdaf\ucdb0\ucdb1\ucdb2\ucdb3\ucdb4\ucdb5\ucdb6\ucdb7\ucdb8\ucdb9\ucdba\ucdbb\ucdbc\ucdbd\ucdbe\ucdbf\ucdc0\ucdc1\ucdc2\ucdc3\ucdc4\ucdc5\ucdc6\ucdc7\ucdc8\ucdc9\ucdca\ucdcb\ucdcc\ucdcd\ucdce\ucdcf\ucdd0\ucdd1\ucdd2\ucdd3\ucdd4\ucdd5\ucdd6\ucdd7\ucdd8\ucdd9\ucdda\ucddb\ucddc\ucddd\ucdde\ucddf\ucde0\ucde1\ucde2\ucde3\ucde4\ucde5\ucde6\ucde7\ucde8\ucde9\ucdea\ucdeb\ucdec\ucded\ucdee\ucdef\ucdf0\ucdf1\ucdf2\ucdf3\ucdf4\ucdf5\ucdf6\ucdf7\ucdf8\ucdf9\ucdfa\ucdfb\ucdfc\ucdfd\ucdfe\ucdff\uce00\uce01\uce02\uce03\uce04\uce05\uce06\uce07\uce08\uce09\uce0a\uce0b\uce0c\uce0d\uce0e\uce0f\uce10\uce11\uce12\uce13\uce14\uce15\uce16\uce17\uce18\uce19\uce1a\uce1b\uce1c\uce1d\uce1e\uce1f\uce20\uce21\uce22\uce23\uce24\uce25\uce26\uce27\uce28\uce29\uce2a\uce2b\uce2c\uce2d\uce2e\uce2f\uce30\uce31\uce32\uce33\uce34\uce35\uce36\uce37\uce38\uce39\uce3a\uce3b\uce3c\uce3d\uce3e\uce3f\uce40\uce41\uce42\uce43\uce44\uce45\uce46\uce47\uce48\uce49\uce4a\uce4b\uce4c\uce4d\uce4e\uce4f\uce50\uce51\uce52\uce53\uce54\uce55\uce56\uce57\uce58\uce59\uce5a\uce5b\uce5c\uce5d\uce5e\uce5f\uce60\uce61\uce62\uce63\uce64\uce65\uce66\uce67\uce68\uce69\uce6a\uce6b\uce6c\uce6d\uce6e\uce6f\uce70\uce71\uce72\uce73\uce74\uce75\uce76\uce77\uce78\uce79\uce7a\uce7b\uce7c\uce7d\uce7e\uce7f\uce80\uce81\uce82\uce83\uce84\uce85\uce86\uce87\uce88\uce89\uce8a\uce8b\uce8c\uce8d\uce8e\uce8f\uce90\uce91\uce92\uce93\uce94\uce95\uce96\uce97\uce98\uce99\uce9a\uce9b\uce9c\uce9d\uce9e\uce9f\ucea0\ucea1\ucea2\ucea3\ucea4\ucea5\ucea6\ucea7\ucea8\ucea9\uceaa\uceab\uceac\ucead\uceae\uceaf\uceb0\uceb1\uceb2\uceb3\uceb4\uceb5\uceb6\uceb7\uceb8\uceb9\uceba\ucebb\ucebc\ucebd\ucebe\ucebf\ucec0\ucec1\ucec2\ucec3\ucec4\ucec5\ucec6\ucec7\ucec8\ucec9\uceca\ucecb\ucecc\ucecd\ucece\ucecf\uced0\uced1\uced2\uced3\uced4\uced5\uced6\uced7\uced8\uced9\uceda\ucedb\ucedc\ucedd\ucede\ucedf\ucee0\ucee1\ucee2\ucee3\ucee4\ucee5\ucee6\ucee7\ucee8\ucee9\uceea\uceeb\uceec\uceed\uceee\uceef\ucef0\ucef1\ucef2\ucef3\ucef4\ucef5\ucef6\ucef7\ucef8\ucef9\ucefa\ucefb\ucefc\ucefd\ucefe\uceff\ucf00\ucf01\ucf02\ucf03\ucf04\ucf05\ucf06\ucf07\ucf08\ucf09\ucf0a\ucf0b\ucf0c\ucf0d\ucf0e\ucf0f\ucf10\ucf11\ucf12\ucf13\ucf14\ucf15\ucf16\ucf17\ucf18\ucf19\ucf1a\ucf1b\ucf1c\ucf1d\ucf1e\ucf1f\ucf20\ucf21\ucf22\ucf23\ucf24\ucf25\ucf26\ucf27\ucf28\ucf29\ucf2a\ucf2b\ucf2c\ucf2d\ucf2e\ucf2f\ucf30\ucf31\ucf32\ucf33\ucf34\ucf35\ucf36\ucf37\ucf38\ucf39\ucf3a\ucf3b\ucf3c\ucf3d\ucf3e\ucf3f\ucf40\ucf41\ucf42\ucf43\ucf44\ucf45\ucf46\ucf47\ucf48\ucf49\ucf4a\ucf4b\ucf4c\ucf4d\ucf4e\ucf4f\ucf50\ucf51\ucf52\ucf53\ucf54\ucf55\ucf56\ucf57\ucf58\ucf59\ucf5a\ucf5b\ucf5c\ucf5d\ucf5e\ucf5f\ucf60\ucf61\ucf62\ucf63\ucf64\ucf65\ucf66\ucf67\ucf68\ucf69\ucf6a\ucf6b\ucf6c\ucf6d\ucf6e\ucf6f\ucf70\ucf71\ucf72\ucf73\ucf74\ucf75\ucf76\ucf77\ucf78\ucf79\ucf7a\ucf7b\ucf7c\ucf7d\ucf7e\ucf7f\ucf80\ucf81\ucf82\ucf83\ucf84\ucf85\ucf86\ucf87\ucf88\ucf89\ucf8a\ucf8b\ucf8c\ucf8d\ucf8e\ucf8f\ucf90\ucf91\ucf92\ucf93\ucf94\ucf95\ucf96\ucf97\ucf98\ucf99\ucf9a\ucf9b\ucf9c\ucf9d\ucf9e\ucf9f\ucfa0\ucfa1\ucfa2\ucfa3\ucfa4\ucfa5\ucfa6\ucfa7\ucfa8\ucfa9\ucfaa\ucfab\ucfac\ucfad\ucfae\ucfaf\ucfb0\ucfb1\ucfb2\ucfb3\ucfb4\ucfb5\ucfb6\ucfb7\ucfb8\ucfb9\ucfba\ucfbb\ucfbc\ucfbd\ucfbe\ucfbf\ucfc0\ucfc1\ucfc2\ucfc3\ucfc4\ucfc5\ucfc6\ucfc7\ucfc8\ucfc9\ucfca\ucfcb\ucfcc\ucfcd\ucfce\ucfcf\ucfd0\ucfd1\ucfd2\ucfd3\ucfd4\ucfd5\ucfd6\ucfd7\ucfd8\ucfd9\ucfda\ucfdb\ucfdc\ucfdd\ucfde\ucfdf\ucfe0\ucfe1\ucfe2\ucfe3\ucfe4\ucfe5\ucfe6\ucfe7\ucfe8\ucfe9\ucfea\ucfeb\ucfec\ucfed\ucfee\ucfef\ucff0\ucff1\ucff2\ucff3\ucff4\ucff5\ucff6\ucff7\ucff8\ucff9\ucffa\ucffb\ucffc\ucffd\ucffe\ucfff\ud000\ud001\ud002\ud003\ud004\ud005\ud006\ud007\ud008\ud009\ud00a\ud00b\ud00c\ud00d\ud00e\ud00f\ud010\ud011\ud012\ud013\ud014\ud015\ud016\ud017\ud018\ud019\ud01a\ud01b\ud01c\ud01d\ud01e\ud01f\ud020\ud021\ud022\ud023\ud024\ud025\ud026\ud027\ud028\ud029\ud02a\ud02b\ud02c\ud02d\ud02e\ud02f\ud030\ud031\ud032\ud033\ud034\ud035\ud036\ud037\ud038\ud039\ud03a\ud03b\ud03c\ud03d\ud03e\ud03f\ud040\ud041\ud042\ud043\ud044\ud045\ud046\ud047\ud048\ud049\ud04a\ud04b\ud04c\ud04d\ud04e\ud04f\ud050\ud051\ud052\ud053\ud054\ud055\ud056\ud057\ud058\ud059\ud05a\ud05b\ud05c\ud05d\ud05e\ud05f\ud060\ud061\ud062\ud063\ud064\ud065\ud066\ud067\ud068\ud069\ud06a\ud06b\ud06c\ud06d\ud06e\ud06f\ud070\ud071\ud072\ud073\ud074\ud075\ud076\ud077\ud078\ud079\ud07a\ud07b\ud07c\ud07d\ud07e\ud07f\ud080\ud081\ud082\ud083\ud084\ud085\ud086\ud087\ud088\ud089\ud08a\ud08b\ud08c\ud08d\ud08e\ud08f\ud090\ud091\ud092\ud093\ud094\ud095\ud096\ud097\ud098\ud099\ud09a\ud09b\ud09c\ud09d\ud09e\ud09f\ud0a0\ud0a1\ud0a2\ud0a3\ud0a4\ud0a5\ud0a6\ud0a7\ud0a8\ud0a9\ud0aa\ud0ab\ud0ac\ud0ad\ud0ae\ud0af\ud0b0\ud0b1\ud0b2\ud0b3\ud0b4\ud0b5\ud0b6\ud0b7\ud0b8\ud0b9\ud0ba\ud0bb\ud0bc\ud0bd\ud0be\ud0bf\ud0c0\ud0c1\ud0c2\ud0c3\ud0c4\ud0c5\ud0c6\ud0c7\ud0c8\ud0c9\ud0ca\ud0cb\ud0cc\ud0cd\ud0ce\ud0cf\ud0d0\ud0d1\ud0d2\ud0d3\ud0d4\ud0d5\ud0d6\ud0d7\ud0d8\ud0d9\ud0da\ud0db\ud0dc\ud0dd\ud0de\ud0df\ud0e0\ud0e1\ud0e2\ud0e3\ud0e4\ud0e5\ud0e6\ud0e7\ud0e8\ud0e9\ud0ea\ud0eb\ud0ec\ud0ed\ud0ee\ud0ef\ud0f0\ud0f1\ud0f2\ud0f3\ud0f4\ud0f5\ud0f6\ud0f7\ud0f8\ud0f9\ud0fa\ud0fb\ud0fc\ud0fd\ud0fe\ud0ff\ud100\ud101\ud102\ud103\ud104\ud105\ud106\ud107\ud108\ud109\ud10a\ud10b\ud10c\ud10d\ud10e\ud10f\ud110\ud111\ud112\ud113\ud114\ud115\ud116\ud117\ud118\ud119\ud11a\ud11b\ud11c\ud11d\ud11e\ud11f\ud120\ud121\ud122\ud123\ud124\ud125\ud126\ud127\ud128\ud129\ud12a\ud12b\ud12c\ud12d\ud12e\ud12f\ud130\ud131\ud132\ud133\ud134\ud135\ud136\ud137\ud138\ud139\ud13a\ud13b\ud13c\ud13d\ud13e\ud13f\ud140\ud141\ud142\ud143\ud144\ud145\ud146\ud147\ud148\ud149\ud14a\ud14b\ud14c\ud14d\ud14e\ud14f\ud150\ud151\ud152\ud153\ud154\ud155\ud156\ud157\ud158\ud159\ud15a\ud15b\ud15c\ud15d\ud15e\ud15f\ud160\ud161\ud162\ud163\ud164\ud165\ud166\ud167\ud168\ud169\ud16a\ud16b\ud16c\ud16d\ud16e\ud16f\ud170\ud171\ud172\ud173\ud174\ud175\ud176\ud177\ud178\ud179\ud17a\ud17b\ud17c\ud17d\ud17e\ud17f\ud180\ud181\ud182\ud183\ud184\ud185\ud186\ud187\ud188\ud189\ud18a\ud18b\ud18c\ud18d\ud18e\ud18f\ud190\ud191\ud192\ud193\ud194\ud195\ud196\ud197\ud198\ud199\ud19a\ud19b\ud19c\ud19d\ud19e\ud19f\ud1a0\ud1a1\ud1a2\ud1a3\ud1a4\ud1a5\ud1a6\ud1a7\ud1a8\ud1a9\ud1aa\ud1ab\ud1ac\ud1ad\ud1ae\ud1af\ud1b0\ud1b1\ud1b2\ud1b3\ud1b4\ud1b5\ud1b6\ud1b7\ud1b8\ud1b9\ud1ba\ud1bb\ud1bc\ud1bd\ud1be\ud1bf\ud1c0\ud1c1\ud1c2\ud1c3\ud1c4\ud1c5\ud1c6\ud1c7\ud1c8\ud1c9\ud1ca\ud1cb\ud1cc\ud1cd\ud1ce\ud1cf\ud1d0\ud1d1\ud1d2\ud1d3\ud1d4\ud1d5\ud1d6\ud1d7\ud1d8\ud1d9\ud1da\ud1db\ud1dc\ud1dd\ud1de\ud1df\ud1e0\ud1e1\ud1e2\ud1e3\ud1e4\ud1e5\ud1e6\ud1e7\ud1e8\ud1e9\ud1ea\ud1eb\ud1ec\ud1ed\ud1ee\ud1ef\ud1f0\ud1f1\ud1f2\ud1f3\ud1f4\ud1f5\ud1f6\ud1f7\ud1f8\ud1f9\ud1fa\ud1fb\ud1fc\ud1fd\ud1fe\ud1ff\ud200\ud201\ud202\ud203\ud204\ud205\ud206\ud207\ud208\ud209\ud20a\ud20b\ud20c\ud20d\ud20e\ud20f\ud210\ud211\ud212\ud213\ud214\ud215\ud216\ud217\ud218\ud219\ud21a\ud21b\ud21c\ud21d\ud21e\ud21f\ud220\ud221\ud222\ud223\ud224\ud225\ud226\ud227\ud228\ud229\ud22a\ud22b\ud22c\ud22d\ud22e\ud22f\ud230\ud231\ud232\ud233\ud234\ud235\ud236\ud237\ud238\ud239\ud23a\ud23b\ud23c\ud23d\ud23e\ud23f\ud240\ud241\ud242\ud243\ud244\ud245\ud246\ud247\ud248\ud249\ud24a\ud24b\ud24c\ud24d\ud24e\ud24f\ud250\ud251\ud252\ud253\ud254\ud255\ud256\ud257\ud258\ud259\ud25a\ud25b\ud25c\ud25d\ud25e\ud25f\ud260\ud261\ud262\ud263\ud264\ud265\ud266\ud267\ud268\ud269\ud26a\ud26b\ud26c\ud26d\ud26e\ud26f\ud270\ud271\ud272\ud273\ud274\ud275\ud276\ud277\ud278\ud279\ud27a\ud27b\ud27c\ud27d\ud27e\ud27f\ud280\ud281\ud282\ud283\ud284\ud285\ud286\ud287\ud288\ud289\ud28a\ud28b\ud28c\ud28d\ud28e\ud28f\ud290\ud291\ud292\ud293\ud294\ud295\ud296\ud297\ud298\ud299\ud29a\ud29b\ud29c\ud29d\ud29e\ud29f\ud2a0\ud2a1\ud2a2\ud2a3\ud2a4\ud2a5\ud2a6\ud2a7\ud2a8\ud2a9\ud2aa\ud2ab\ud2ac\ud2ad\ud2ae\ud2af\ud2b0\ud2b1\ud2b2\ud2b3\ud2b4\ud2b5\ud2b6\ud2b7\ud2b8\ud2b9\ud2ba\ud2bb\ud2bc\ud2bd\ud2be\ud2bf\ud2c0\ud2c1\ud2c2\ud2c3\ud2c4\ud2c5\ud2c6\ud2c7\ud2c8\ud2c9\ud2ca\ud2cb\ud2cc\ud2cd\ud2ce\ud2cf\ud2d0\ud2d1\ud2d2\ud2d3\ud2d4\ud2d5\ud2d6\ud2d7\ud2d8\ud2d9\ud2da\ud2db\ud2dc\ud2dd\ud2de\ud2df\ud2e0\ud2e1\ud2e2\ud2e3\ud2e4\ud2e5\ud2e6\ud2e7\ud2e8\ud2e9\ud2ea\ud2eb\ud2ec\ud2ed\ud2ee\ud2ef\ud2f0\ud2f1\ud2f2\ud2f3\ud2f4\ud2f5\ud2f6\ud2f7\ud2f8\ud2f9\ud2fa\ud2fb\ud2fc\ud2fd\ud2fe\ud2ff\ud300\ud301\ud302\ud303\ud304\ud305\ud306\ud307\ud308\ud309\ud30a\ud30b\ud30c\ud30d\ud30e\ud30f\ud310\ud311\ud312\ud313\ud314\ud315\ud316\ud317\ud318\ud319\ud31a\ud31b\ud31c\ud31d\ud31e\ud31f\ud320\ud321\ud322\ud323\ud324\ud325\ud326\ud327\ud328\ud329\ud32a\ud32b\ud32c\ud32d\ud32e\ud32f\ud330\ud331\ud332\ud333\ud334\ud335\ud336\ud337\ud338\ud339\ud33a\ud33b\ud33c\ud33d\ud33e\ud33f\ud340\ud341\ud342\ud343\ud344\ud345\ud346\ud347\ud348\ud349\ud34a\ud34b\ud34c\ud34d\ud34e\ud34f\ud350\ud351\ud352\ud353\ud354\ud355\ud356\ud357\ud358\ud359\ud35a\ud35b\ud35c\ud35d\ud35e\ud35f\ud360\ud361\ud362\ud363\ud364\ud365\ud366\ud367\ud368\ud369\ud36a\ud36b\ud36c\ud36d\ud36e\ud36f\ud370\ud371\ud372\ud373\ud374\ud375\ud376\ud377\ud378\ud379\ud37a\ud37b\ud37c\ud37d\ud37e\ud37f\ud380\ud381\ud382\ud383\ud384\ud385\ud386\ud387\ud388\ud389\ud38a\ud38b\ud38c\ud38d\ud38e\ud38f\ud390\ud391\ud392\ud393\ud394\ud395\ud396\ud397\ud398\ud399\ud39a\ud39b\ud39c\ud39d\ud39e\ud39f\ud3a0\ud3a1\ud3a2\ud3a3\ud3a4\ud3a5\ud3a6\ud3a7\ud3a8\ud3a9\ud3aa\ud3ab\ud3ac\ud3ad\ud3ae\ud3af\ud3b0\ud3b1\ud3b2\ud3b3\ud3b4\ud3b5\ud3b6\ud3b7\ud3b8\ud3b9\ud3ba\ud3bb\ud3bc\ud3bd\ud3be\ud3bf\ud3c0\ud3c1\ud3c2\ud3c3\ud3c4\ud3c5\ud3c6\ud3c7\ud3c8\ud3c9\ud3ca\ud3cb\ud3cc\ud3cd\ud3ce\ud3cf\ud3d0\ud3d1\ud3d2\ud3d3\ud3d4\ud3d5\ud3d6\ud3d7\ud3d8\ud3d9\ud3da\ud3db\ud3dc\ud3dd\ud3de\ud3df\ud3e0\ud3e1\ud3e2\ud3e3\ud3e4\ud3e5\ud3e6\ud3e7\ud3e8\ud3e9\ud3ea\ud3eb\ud3ec\ud3ed\ud3ee\ud3ef\ud3f0\ud3f1\ud3f2\ud3f3\ud3f4\ud3f5\ud3f6\ud3f7\ud3f8\ud3f9\ud3fa\ud3fb\ud3fc\ud3fd\ud3fe\ud3ff\ud400\ud401\ud402\ud403\ud404\ud405\ud406\ud407\ud408\ud409\ud40a\ud40b\ud40c\ud40d\ud40e\ud40f\ud410\ud411\ud412\ud413\ud414\ud415\ud416\ud417\ud418\ud419\ud41a\ud41b\ud41c\ud41d\ud41e\ud41f\ud420\ud421\ud422\ud423\ud424\ud425\ud426\ud427\ud428\ud429\ud42a\ud42b\ud42c\ud42d\ud42e\ud42f\ud430\ud431\ud432\ud433\ud434\ud435\ud436\ud437\ud438\ud439\ud43a\ud43b\ud43c\ud43d\ud43e\ud43f\ud440\ud441\ud442\ud443\ud444\ud445\ud446\ud447\ud448\ud449\ud44a\ud44b\ud44c\ud44d\ud44e\ud44f\ud450\ud451\ud452\ud453\ud454\ud455\ud456\ud457\ud458\ud459\ud45a\ud45b\ud45c\ud45d\ud45e\ud45f\ud460\ud461\ud462\ud463\ud464\ud465\ud466\ud467\ud468\ud469\ud46a\ud46b\ud46c\ud46d\ud46e\ud46f\ud470\ud471\ud472\ud473\ud474\ud475\ud476\ud477\ud478\ud479\ud47a\ud47b\ud47c\ud47d\ud47e\ud47f\ud480\ud481\ud482\ud483\ud484\ud485\ud486\ud487\ud488\ud489\ud48a\ud48b\ud48c\ud48d\ud48e\ud48f\ud490\ud491\ud492\ud493\ud494\ud495\ud496\ud497\ud498\ud499\ud49a\ud49b\ud49c\ud49d\ud49e\ud49f\ud4a0\ud4a1\ud4a2\ud4a3\ud4a4\ud4a5\ud4a6\ud4a7\ud4a8\ud4a9\ud4aa\ud4ab\ud4ac\ud4ad\ud4ae\ud4af\ud4b0\ud4b1\ud4b2\ud4b3\ud4b4\ud4b5\ud4b6\ud4b7\ud4b8\ud4b9\ud4ba\ud4bb\ud4bc\ud4bd\ud4be\ud4bf\ud4c0\ud4c1\ud4c2\ud4c3\ud4c4\ud4c5\ud4c6\ud4c7\ud4c8\ud4c9\ud4ca\ud4cb\ud4cc\ud4cd\ud4ce\ud4cf\ud4d0\ud4d1\ud4d2\ud4d3\ud4d4\ud4d5\ud4d6\ud4d7\ud4d8\ud4d9\ud4da\ud4db\ud4dc\ud4dd\ud4de\ud4df\ud4e0\ud4e1\ud4e2\ud4e3\ud4e4\ud4e5\ud4e6\ud4e7\ud4e8\ud4e9\ud4ea\ud4eb\ud4ec\ud4ed\ud4ee\ud4ef\ud4f0\ud4f1\ud4f2\ud4f3\ud4f4\ud4f5\ud4f6\ud4f7\ud4f8\ud4f9\ud4fa\ud4fb\ud4fc\ud4fd\ud4fe\ud4ff\ud500\ud501\ud502\ud503\ud504\ud505\ud506\ud507\ud508\ud509\ud50a\ud50b\ud50c\ud50d\ud50e\ud50f\ud510\ud511\ud512\ud513\ud514\ud515\ud516\ud517\ud518\ud519\ud51a\ud51b\ud51c\ud51d\ud51e\ud51f\ud520\ud521\ud522\ud523\ud524\ud525\ud526\ud527\ud528\ud529\ud52a\ud52b\ud52c\ud52d\ud52e\ud52f\ud530\ud531\ud532\ud533\ud534\ud535\ud536\ud537\ud538\ud539\ud53a\ud53b\ud53c\ud53d\ud53e\ud53f\ud540\ud541\ud542\ud543\ud544\ud545\ud546\ud547\ud548\ud549\ud54a\ud54b\ud54c\ud54d\ud54e\ud54f\ud550\ud551\ud552\ud553\ud554\ud555\ud556\ud557\ud558\ud559\ud55a\ud55b\ud55c\ud55d\ud55e\ud55f\ud560\ud561\ud562\ud563\ud564\ud565\ud566\ud567\ud568\ud569\ud56a\ud56b\ud56c\ud56d\ud56e\ud56f\ud570\ud571\ud572\ud573\ud574\ud575\ud576\ud577\ud578\ud579\ud57a\ud57b\ud57c\ud57d\ud57e\ud57f\ud580\ud581\ud582\ud583\ud584\ud585\ud586\ud587\ud588\ud589\ud58a\ud58b\ud58c\ud58d\ud58e\ud58f\ud590\ud591\ud592\ud593\ud594\ud595\ud596\ud597\ud598\ud599\ud59a\ud59b\ud59c\ud59d\ud59e\ud59f\ud5a0\ud5a1\ud5a2\ud5a3\ud5a4\ud5a5\ud5a6\ud5a7\ud5a8\ud5a9\ud5aa\ud5ab\ud5ac\ud5ad\ud5ae\ud5af\ud5b0\ud5b1\ud5b2\ud5b3\ud5b4\ud5b5\ud5b6\ud5b7\ud5b8\ud5b9\ud5ba\ud5bb\ud5bc\ud5bd\ud5be\ud5bf\ud5c0\ud5c1\ud5c2\ud5c3\ud5c4\ud5c5\ud5c6\ud5c7\ud5c8\ud5c9\ud5ca\ud5cb\ud5cc\ud5cd\ud5ce\ud5cf\ud5d0\ud5d1\ud5d2\ud5d3\ud5d4\ud5d5\ud5d6\ud5d7\ud5d8\ud5d9\ud5da\ud5db\ud5dc\ud5dd\ud5de\ud5df\ud5e0\ud5e1\ud5e2\ud5e3\ud5e4\ud5e5\ud5e6\ud5e7\ud5e8\ud5e9\ud5ea\ud5eb\ud5ec\ud5ed\ud5ee\ud5ef\ud5f0\ud5f1\ud5f2\ud5f3\ud5f4\ud5f5\ud5f6\ud5f7\ud5f8\ud5f9\ud5fa\ud5fb\ud5fc\ud5fd\ud5fe\ud5ff\ud600\ud601\ud602\ud603\ud604\ud605\ud606\ud607\ud608\ud609\ud60a\ud60b\ud60c\ud60d\ud60e\ud60f\ud610\ud611\ud612\ud613\ud614\ud615\ud616\ud617\ud618\ud619\ud61a\ud61b\ud61c\ud61d\ud61e\ud61f\ud620\ud621\ud622\ud623\ud624\ud625\ud626\ud627\ud628\ud629\ud62a\ud62b\ud62c\ud62d\ud62e\ud62f\ud630\ud631\ud632\ud633\ud634\ud635\ud636\ud637\ud638\ud639\ud63a\ud63b\ud63c\ud63d\ud63e\ud63f\ud640\ud641\ud642\ud643\ud644\ud645\ud646\ud647\ud648\ud649\ud64a\ud64b\ud64c\ud64d\ud64e\ud64f\ud650\ud651\ud652\ud653\ud654\ud655\ud656\ud657\ud658\ud659\ud65a\ud65b\ud65c\ud65d\ud65e\ud65f\ud660\ud661\ud662\ud663\ud664\ud665\ud666\ud667\ud668\ud669\ud66a\ud66b\ud66c\ud66d\ud66e\ud66f\ud670\ud671\ud672\ud673\ud674\ud675\ud676\ud677\ud678\ud679\ud67a\ud67b\ud67c\ud67d\ud67e\ud67f\ud680\ud681\ud682\ud683\ud684\ud685\ud686\ud687\ud688\ud689\ud68a\ud68b\ud68c\ud68d\ud68e\ud68f\ud690\ud691\ud692\ud693\ud694\ud695\ud696\ud697\ud698\ud699\ud69a\ud69b\ud69c\ud69d\ud69e\ud69f\ud6a0\ud6a1\ud6a2\ud6a3\ud6a4\ud6a5\ud6a6\ud6a7\ud6a8\ud6a9\ud6aa\ud6ab\ud6ac\ud6ad\ud6ae\ud6af\ud6b0\ud6b1\ud6b2\ud6b3\ud6b4\ud6b5\ud6b6\ud6b7\ud6b8\ud6b9\ud6ba\ud6bb\ud6bc\ud6bd\ud6be\ud6bf\ud6c0\ud6c1\ud6c2\ud6c3\ud6c4\ud6c5\ud6c6\ud6c7\ud6c8\ud6c9\ud6ca\ud6cb\ud6cc\ud6cd\ud6ce\ud6cf\ud6d0\ud6d1\ud6d2\ud6d3\ud6d4\ud6d5\ud6d6\ud6d7\ud6d8\ud6d9\ud6da\ud6db\ud6dc\ud6dd\ud6de\ud6df\ud6e0\ud6e1\ud6e2\ud6e3\ud6e4\ud6e5\ud6e6\ud6e7\ud6e8\ud6e9\ud6ea\ud6eb\ud6ec\ud6ed\ud6ee\ud6ef\ud6f0\ud6f1\ud6f2\ud6f3\ud6f4\ud6f5\ud6f6\ud6f7\ud6f8\ud6f9\ud6fa\ud6fb\ud6fc\ud6fd\ud6fe\ud6ff\ud700\ud701\ud702\ud703\ud704\ud705\ud706\ud707\ud708\ud709\ud70a\ud70b\ud70c\ud70d\ud70e\ud70f\ud710\ud711\ud712\ud713\ud714\ud715\ud716\ud717\ud718\ud719\ud71a\ud71b\ud71c\ud71d\ud71e\ud71f\ud720\ud721\ud722\ud723\ud724\ud725\ud726\ud727\ud728\ud729\ud72a\ud72b\ud72c\ud72d\ud72e\ud72f\ud730\ud731\ud732\ud733\ud734\ud735\ud736\ud737\ud738\ud739\ud73a\ud73b\ud73c\ud73d\ud73e\ud73f\ud740\ud741\ud742\ud743\ud744\ud745\ud746\ud747\ud748\ud749\ud74a\ud74b\ud74c\ud74d\ud74e\ud74f\ud750\ud751\ud752\ud753\ud754\ud755\ud756\ud757\ud758\ud759\ud75a\ud75b\ud75c\ud75d\ud75e\ud75f\ud760\ud761\ud762\ud763\ud764\ud765\ud766\ud767\ud768\ud769\ud76a\ud76b\ud76c\ud76d\ud76e\ud76f\ud770\ud771\ud772\ud773\ud774\ud775\ud776\ud777\ud778\ud779\ud77a\ud77b\ud77c\ud77d\ud77e\ud77f\ud780\ud781\ud782\ud783\ud784\ud785\ud786\ud787\ud788\ud789\ud78a\ud78b\ud78c\ud78d\ud78e\ud78f\ud790\ud791\ud792\ud793\ud794\ud795\ud796\ud797\ud798\ud799\ud79a\ud79b\ud79c\ud79d\ud79e\ud79f\ud7a0\ud7a1\ud7a2\ud7a3\uf900\uf901\uf902\uf903\uf904\uf905\uf906\uf907\uf908\uf909\uf90a\uf90b\uf90c\uf90d\uf90e\uf90f\uf910\uf911\uf912\uf913\uf914\uf915\uf916\uf917\uf918\uf919\uf91a\uf91b\uf91c\uf91d\uf91e\uf91f\uf920\uf921\uf922\uf923\uf924\uf925\uf926\uf927\uf928\uf929\uf92a\uf92b\uf92c\uf92d\uf92e\uf92f\uf930\uf931\uf932\uf933\uf934\uf935\uf936\uf937\uf938\uf939\uf93a\uf93b\uf93c\uf93d\uf93e\uf93f\uf940\uf941\uf942\uf943\uf944\uf945\uf946\uf947\uf948\uf949\uf94a\uf94b\uf94c\uf94d\uf94e\uf94f\uf950\uf951\uf952\uf953\uf954\uf955\uf956\uf957\uf958\uf959\uf95a\uf95b\uf95c\uf95d\uf95e\uf95f\uf960\uf961\uf962\uf963\uf964\uf965\uf966\uf967\uf968\uf969\uf96a\uf96b\uf96c\uf96d\uf96e\uf96f\uf970\uf971\uf972\uf973\uf974\uf975\uf976\uf977\uf978\uf979\uf97a\uf97b\uf97c\uf97d\uf97e\uf97f\uf980\uf981\uf982\uf983\uf984\uf985\uf986\uf987\uf988\uf989\uf98a\uf98b\uf98c\uf98d\uf98e\uf98f\uf990\uf991\uf992\uf993\uf994\uf995\uf996\uf997\uf998\uf999\uf99a\uf99b\uf99c\uf99d\uf99e\uf99f\uf9a0\uf9a1\uf9a2\uf9a3\uf9a4\uf9a5\uf9a6\uf9a7\uf9a8\uf9a9\uf9aa\uf9ab\uf9ac\uf9ad\uf9ae\uf9af\uf9b0\uf9b1\uf9b2\uf9b3\uf9b4\uf9b5\uf9b6\uf9b7\uf9b8\uf9b9\uf9ba\uf9bb\uf9bc\uf9bd\uf9be\uf9bf\uf9c0\uf9c1\uf9c2\uf9c3\uf9c4\uf9c5\uf9c6\uf9c7\uf9c8\uf9c9\uf9ca\uf9cb\uf9cc\uf9cd\uf9ce\uf9cf\uf9d0\uf9d1\uf9d2\uf9d3\uf9d4\uf9d5\uf9d6\uf9d7\uf9d8\uf9d9\uf9da\uf9db\uf9dc\uf9dd\uf9de\uf9df\uf9e0\uf9e1\uf9e2\uf9e3\uf9e4\uf9e5\uf9e6\uf9e7\uf9e8\uf9e9\uf9ea\uf9eb\uf9ec\uf9ed\uf9ee\uf9ef\uf9f0\uf9f1\uf9f2\uf9f3\uf9f4\uf9f5\uf9f6\uf9f7\uf9f8\uf9f9\uf9fa\uf9fb\uf9fc\uf9fd\uf9fe\uf9ff\ufa00\ufa01\ufa02\ufa03\ufa04\ufa05\ufa06\ufa07\ufa08\ufa09\ufa0a\ufa0b\ufa0c\ufa0d\ufa0e\ufa0f\ufa10\ufa11\ufa12\ufa13\ufa14\ufa15\ufa16\ufa17\ufa18\ufa19\ufa1a\ufa1b\ufa1c\ufa1d\ufa1e\ufa1f\ufa20\ufa21\ufa22\ufa23\ufa24\ufa25\ufa26\ufa27\ufa28\ufa29\ufa2a\ufa2b\ufa2c\ufa2d\ufa30\ufa31\ufa32\ufa33\ufa34\ufa35\ufa36\ufa37\ufa38\ufa39\ufa3a\ufa3b\ufa3c\ufa3d\ufa3e\ufa3f\ufa40\ufa41\ufa42\ufa43\ufa44\ufa45\ufa46\ufa47\ufa48\ufa49\ufa4a\ufa4b\ufa4c\ufa4d\ufa4e\ufa4f\ufa50\ufa51\ufa52\ufa53\ufa54\ufa55\ufa56\ufa57\ufa58\ufa59\ufa5a\ufa5b\ufa5c\ufa5d\ufa5e\ufa5f\ufa60\ufa61\ufa62\ufa63\ufa64\ufa65\ufa66\ufa67\ufa68\ufa69\ufa6a\ufa70\ufa71\ufa72\ufa73\ufa74\ufa75\ufa76\ufa77\ufa78\ufa79\ufa7a\ufa7b\ufa7c\ufa7d\ufa7e\ufa7f\ufa80\ufa81\ufa82\ufa83\ufa84\ufa85\ufa86\ufa87\ufa88\ufa89\ufa8a\ufa8b\ufa8c\ufa8d\ufa8e\ufa8f\ufa90\ufa91\ufa92\ufa93\ufa94\ufa95\ufa96\ufa97\ufa98\ufa99\ufa9a\ufa9b\ufa9c\ufa9d\ufa9e\ufa9f\ufaa0\ufaa1\ufaa2\ufaa3\ufaa4\ufaa5\ufaa6\ufaa7\ufaa8\ufaa9\ufaaa\ufaab\ufaac\ufaad\ufaae\ufaaf\ufab0\ufab1\ufab2\ufab3\ufab4\ufab5\ufab6\ufab7\ufab8\ufab9\ufaba\ufabb\ufabc\ufabd\ufabe\ufabf\ufac0\ufac1\ufac2\ufac3\ufac4\ufac5\ufac6\ufac7\ufac8\ufac9\ufaca\ufacb\ufacc\ufacd\uface\ufacf\ufad0\ufad1\ufad2\ufad3\ufad4\ufad5\ufad6\ufad7\ufad8\ufad9\ufb1d\ufb1f\ufb20\ufb21\ufb22\ufb23\ufb24\ufb25\ufb26\ufb27\ufb28\ufb2a\ufb2b\ufb2c\ufb2d\ufb2e\ufb2f\ufb30\ufb31\ufb32\ufb33\ufb34\ufb35\ufb36\ufb38\ufb39\ufb3a\ufb3b\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46\ufb47\ufb48\ufb49\ufb4a\ufb4b\ufb4c\ufb4d\ufb4e\ufb4f\ufb50\ufb51\ufb52\ufb53\ufb54\ufb55\ufb56\ufb57\ufb58\ufb59\ufb5a\ufb5b\ufb5c\ufb5d\ufb5e\ufb5f\ufb60\ufb61\ufb62\ufb63\ufb64\ufb65\ufb66\ufb67\ufb68\ufb69\ufb6a\ufb6b\ufb6c\ufb6d\ufb6e\ufb6f\ufb70\ufb71\ufb72\ufb73\ufb74\ufb75\ufb76\ufb77\ufb78\ufb79\ufb7a\ufb7b\ufb7c\ufb7d\ufb7e\ufb7f\ufb80\ufb81\ufb82\ufb83\ufb84\ufb85\ufb86\ufb87\ufb88\ufb89\ufb8a\ufb8b\ufb8c\ufb8d\ufb8e\ufb8f\ufb90\ufb91\ufb92\ufb93\ufb94\ufb95\ufb96\ufb97\ufb98\ufb99\ufb9a\ufb9b\ufb9c\ufb9d\ufb9e\ufb9f\ufba0\ufba1\ufba2\ufba3\ufba4\ufba5\ufba6\ufba7\ufba8\ufba9\ufbaa\ufbab\ufbac\ufbad\ufbae\ufbaf\ufbb0\ufbb1\ufbd3\ufbd4\ufbd5\ufbd6\ufbd7\ufbd8\ufbd9\ufbda\ufbdb\ufbdc\ufbdd\ufbde\ufbdf\ufbe0\ufbe1\ufbe2\ufbe3\ufbe4\ufbe5\ufbe6\ufbe7\ufbe8\ufbe9\ufbea\ufbeb\ufbec\ufbed\ufbee\ufbef\ufbf0\ufbf1\ufbf2\ufbf3\ufbf4\ufbf5\ufbf6\ufbf7\ufbf8\ufbf9\ufbfa\ufbfb\ufbfc\ufbfd\ufbfe\ufbff\ufc00\ufc01\ufc02\ufc03\ufc04\ufc05\ufc06\ufc07\ufc08\ufc09\ufc0a\ufc0b\ufc0c\ufc0d\ufc0e\ufc0f\ufc10\ufc11\ufc12\ufc13\ufc14\ufc15\ufc16\ufc17\ufc18\ufc19\ufc1a\ufc1b\ufc1c\ufc1d\ufc1e\ufc1f\ufc20\ufc21\ufc22\ufc23\ufc24\ufc25\ufc26\ufc27\ufc28\ufc29\ufc2a\ufc2b\ufc2c\ufc2d\ufc2e\ufc2f\ufc30\ufc31\ufc32\ufc33\ufc34\ufc35\ufc36\ufc37\ufc38\ufc39\ufc3a\ufc3b\ufc3c\ufc3d\ufc3e\ufc3f\ufc40\ufc41\ufc42\ufc43\ufc44\ufc45\ufc46\ufc47\ufc48\ufc49\ufc4a\ufc4b\ufc4c\ufc4d\ufc4e\ufc4f\ufc50\ufc51\ufc52\ufc53\ufc54\ufc55\ufc56\ufc57\ufc58\ufc59\ufc5a\ufc5b\ufc5c\ufc5d\ufc5e\ufc5f\ufc60\ufc61\ufc62\ufc63\ufc64\ufc65\ufc66\ufc67\ufc68\ufc69\ufc6a\ufc6b\ufc6c\ufc6d\ufc6e\ufc6f\ufc70\ufc71\ufc72\ufc73\ufc74\ufc75\ufc76\ufc77\ufc78\ufc79\ufc7a\ufc7b\ufc7c\ufc7d\ufc7e\ufc7f\ufc80\ufc81\ufc82\ufc83\ufc84\ufc85\ufc86\ufc87\ufc88\ufc89\ufc8a\ufc8b\ufc8c\ufc8d\ufc8e\ufc8f\ufc90\ufc91\ufc92\ufc93\ufc94\ufc95\ufc96\ufc97\ufc98\ufc99\ufc9a\ufc9b\ufc9c\ufc9d\ufc9e\ufc9f\ufca0\ufca1\ufca2\ufca3\ufca4\ufca5\ufca6\ufca7\ufca8\ufca9\ufcaa\ufcab\ufcac\ufcad\ufcae\ufcaf\ufcb0\ufcb1\ufcb2\ufcb3\ufcb4\ufcb5\ufcb6\ufcb7\ufcb8\ufcb9\ufcba\ufcbb\ufcbc\ufcbd\ufcbe\ufcbf\ufcc0\ufcc1\ufcc2\ufcc3\ufcc4\ufcc5\ufcc6\ufcc7\ufcc8\ufcc9\ufcca\ufccb\ufccc\ufccd\ufcce\ufccf\ufcd0\ufcd1\ufcd2\ufcd3\ufcd4\ufcd5\ufcd6\ufcd7\ufcd8\ufcd9\ufcda\ufcdb\ufcdc\ufcdd\ufcde\ufcdf\ufce0\ufce1\ufce2\ufce3\ufce4\ufce5\ufce6\ufce7\ufce8\ufce9\ufcea\ufceb\ufcec\ufced\ufcee\ufcef\ufcf0\ufcf1\ufcf2\ufcf3\ufcf4\ufcf5\ufcf6\ufcf7\ufcf8\ufcf9\ufcfa\ufcfb\ufcfc\ufcfd\ufcfe\ufcff\ufd00\ufd01\ufd02\ufd03\ufd04\ufd05\ufd06\ufd07\ufd08\ufd09\ufd0a\ufd0b\ufd0c\ufd0d\ufd0e\ufd0f\ufd10\ufd11\ufd12\ufd13\ufd14\ufd15\ufd16\ufd17\ufd18\ufd19\ufd1a\ufd1b\ufd1c\ufd1d\ufd1e\ufd1f\ufd20\ufd21\ufd22\ufd23\ufd24\ufd25\ufd26\ufd27\ufd28\ufd29\ufd2a\ufd2b\ufd2c\ufd2d\ufd2e\ufd2f\ufd30\ufd31\ufd32\ufd33\ufd34\ufd35\ufd36\ufd37\ufd38\ufd39\ufd3a\ufd3b\ufd3c\ufd3d\ufd50\ufd51\ufd52\ufd53\ufd54\ufd55\ufd56\ufd57\ufd58\ufd59\ufd5a\ufd5b\ufd5c\ufd5d\ufd5e\ufd5f\ufd60\ufd61\ufd62\ufd63\ufd64\ufd65\ufd66\ufd67\ufd68\ufd69\ufd6a\ufd6b\ufd6c\ufd6d\ufd6e\ufd6f\ufd70\ufd71\ufd72\ufd73\ufd74\ufd75\ufd76\ufd77\ufd78\ufd79\ufd7a\ufd7b\ufd7c\ufd7d\ufd7e\ufd7f\ufd80\ufd81\ufd82\ufd83\ufd84\ufd85\ufd86\ufd87\ufd88\ufd89\ufd8a\ufd8b\ufd8c\ufd8d\ufd8e\ufd8f\ufd92\ufd93\ufd94\ufd95\ufd96\ufd97\ufd98\ufd99\ufd9a\ufd9b\ufd9c\ufd9d\ufd9e\ufd9f\ufda0\ufda1\ufda2\ufda3\ufda4\ufda5\ufda6\ufda7\ufda8\ufda9\ufdaa\ufdab\ufdac\ufdad\ufdae\ufdaf\ufdb0\ufdb1\ufdb2\ufdb3\ufdb4\ufdb5\ufdb6\ufdb7\ufdb8\ufdb9\ufdba\ufdbb\ufdbc\ufdbd\ufdbe\ufdbf\ufdc0\ufdc1\ufdc2\ufdc3\ufdc4\ufdc5\ufdc6\ufdc7\ufdf0\ufdf1\ufdf2\ufdf3\ufdf4\ufdf5\ufdf6\ufdf7\ufdf8\ufdf9\ufdfa\ufdfb\ufe70\ufe71\ufe72\ufe73\ufe74\ufe76\ufe77\ufe78\ufe79\ufe7a\ufe7b\ufe7c\ufe7d\ufe7e\ufe7f\ufe80\ufe81\ufe82\ufe83\ufe84\ufe85\ufe86\ufe87\ufe88\ufe89\ufe8a\ufe8b\ufe8c\ufe8d\ufe8e\ufe8f\ufe90\ufe91\ufe92\ufe93\ufe94\ufe95\ufe96\ufe97\ufe98\ufe99\ufe9a\ufe9b\ufe9c\ufe9d\ufe9e\ufe9f\ufea0\ufea1\ufea2\ufea3\ufea4\ufea5\ufea6\ufea7\ufea8\ufea9\ufeaa\ufeab\ufeac\ufead\ufeae\ufeaf\ufeb0\ufeb1\ufeb2\ufeb3\ufeb4\ufeb5\ufeb6\ufeb7\ufeb8\ufeb9\ufeba\ufebb\ufebc\ufebd\ufebe\ufebf\ufec0\ufec1\ufec2\ufec3\ufec4\ufec5\ufec6\ufec7\ufec8\ufec9\ufeca\ufecb\ufecc\ufecd\ufece\ufecf\ufed0\ufed1\ufed2\ufed3\ufed4\ufed5\ufed6\ufed7\ufed8\ufed9\ufeda\ufedb\ufedc\ufedd\ufede\ufedf\ufee0\ufee1\ufee2\ufee3\ufee4\ufee5\ufee6\ufee7\ufee8\ufee9\ufeea\ufeeb\ufeec\ufeed\ufeee\ufeef\ufef0\ufef1\ufef2\ufef3\ufef4\ufef5\ufef6\ufef7\ufef8\ufef9\ufefa\ufefb\ufefc\uff66\uff67\uff68\uff69\uff6a\uff6b\uff6c\uff6d\uff6e\uff6f\uff71\uff72\uff73\uff74\uff75\uff76\uff77\uff78\uff79\uff7a\uff7b\uff7c\uff7d\uff7e\uff7f\uff80\uff81\uff82\uff83\uff84\uff85\uff86\uff87\uff88\uff89\uff8a\uff8b\uff8c\uff8d\uff8e\uff8f\uff90\uff91\uff92\uff93\uff94\uff95\uff96\uff97\uff98\uff99\uff9a\uff9b\uff9c\uff9d\uffa0\uffa1\uffa2\uffa3\uffa4\uffa5\uffa6\uffa7\uffa8\uffa9\uffaa\uffab\uffac\uffad\uffae\uffaf\uffb0\uffb1\uffb2\uffb3\uffb4\uffb5\uffb6\uffb7\uffb8\uffb9\uffba\uffbb\uffbc\uffbd\uffbe\uffc2\uffc3\uffc4\uffc5\uffc6\uffc7\uffca\uffcb\uffcc\uffcd\uffce\uffcf\uffd2\uffd3\uffd4\uffd5\uffd6\uffd7\uffda\uffdb\uffdc' + +Lt = u'\u01c5\u01c8\u01cb\u01f2\u1f88\u1f89\u1f8a\u1f8b\u1f8c\u1f8d\u1f8e\u1f8f\u1f98\u1f99\u1f9a\u1f9b\u1f9c\u1f9d\u1f9e\u1f9f\u1fa8\u1fa9\u1faa\u1fab\u1fac\u1fad\u1fae\u1faf\u1fbc\u1fcc\u1ffc' + +Lu = u'ABCDEFGHIJKLMNOPQRSTUVWXYZ\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd8\xd9\xda\xdb\xdc\xdd\xde\u0100\u0102\u0104\u0106\u0108\u010a\u010c\u010e\u0110\u0112\u0114\u0116\u0118\u011a\u011c\u011e\u0120\u0122\u0124\u0126\u0128\u012a\u012c\u012e\u0130\u0132\u0134\u0136\u0139\u013b\u013d\u013f\u0141\u0143\u0145\u0147\u014a\u014c\u014e\u0150\u0152\u0154\u0156\u0158\u015a\u015c\u015e\u0160\u0162\u0164\u0166\u0168\u016a\u016c\u016e\u0170\u0172\u0174\u0176\u0178\u0179\u017b\u017d\u0181\u0182\u0184\u0186\u0187\u0189\u018a\u018b\u018e\u018f\u0190\u0191\u0193\u0194\u0196\u0197\u0198\u019c\u019d\u019f\u01a0\u01a2\u01a4\u01a6\u01a7\u01a9\u01ac\u01ae\u01af\u01b1\u01b2\u01b3\u01b5\u01b7\u01b8\u01bc\u01c4\u01c7\u01ca\u01cd\u01cf\u01d1\u01d3\u01d5\u01d7\u01d9\u01db\u01de\u01e0\u01e2\u01e4\u01e6\u01e8\u01ea\u01ec\u01ee\u01f1\u01f4\u01f6\u01f7\u01f8\u01fa\u01fc\u01fe\u0200\u0202\u0204\u0206\u0208\u020a\u020c\u020e\u0210\u0212\u0214\u0216\u0218\u021a\u021c\u021e\u0220\u0222\u0224\u0226\u0228\u022a\u022c\u022e\u0230\u0232\u023a\u023b\u023d\u023e\u0241\u0386\u0388\u0389\u038a\u038c\u038e\u038f\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03aa\u03ab\u03d2\u03d3\u03d4\u03d8\u03da\u03dc\u03de\u03e0\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u03f7\u03f9\u03fa\u03fd\u03fe\u03ff\u0400\u0401\u0402\u0403\u0404\u0405\u0406\u0407\u0408\u0409\u040a\u040b\u040c\u040d\u040e\u040f\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b\u042c\u042d\u042e\u042f\u0460\u0462\u0464\u0466\u0468\u046a\u046c\u046e\u0470\u0472\u0474\u0476\u0478\u047a\u047c\u047e\u0480\u048a\u048c\u048e\u0490\u0492\u0494\u0496\u0498\u049a\u049c\u049e\u04a0\u04a2\u04a4\u04a6\u04a8\u04aa\u04ac\u04ae\u04b0\u04b2\u04b4\u04b6\u04b8\u04ba\u04bc\u04be\u04c0\u04c1\u04c3\u04c5\u04c7\u04c9\u04cb\u04cd\u04d0\u04d2\u04d4\u04d6\u04d8\u04da\u04dc\u04de\u04e0\u04e2\u04e4\u04e6\u04e8\u04ea\u04ec\u04ee\u04f0\u04f2\u04f4\u04f6\u04f8\u0500\u0502\u0504\u0506\u0508\u050a\u050c\u050e\u0531\u0532\u0533\u0534\u0535\u0536\u0537\u0538\u0539\u053a\u053b\u053c\u053d\u053e\u053f\u0540\u0541\u0542\u0543\u0544\u0545\u0546\u0547\u0548\u0549\u054a\u054b\u054c\u054d\u054e\u054f\u0550\u0551\u0552\u0553\u0554\u0555\u0556\u10a0\u10a1\u10a2\u10a3\u10a4\u10a5\u10a6\u10a7\u10a8\u10a9\u10aa\u10ab\u10ac\u10ad\u10ae\u10af\u10b0\u10b1\u10b2\u10b3\u10b4\u10b5\u10b6\u10b7\u10b8\u10b9\u10ba\u10bb\u10bc\u10bd\u10be\u10bf\u10c0\u10c1\u10c2\u10c3\u10c4\u10c5\u1e00\u1e02\u1e04\u1e06\u1e08\u1e0a\u1e0c\u1e0e\u1e10\u1e12\u1e14\u1e16\u1e18\u1e1a\u1e1c\u1e1e\u1e20\u1e22\u1e24\u1e26\u1e28\u1e2a\u1e2c\u1e2e\u1e30\u1e32\u1e34\u1e36\u1e38\u1e3a\u1e3c\u1e3e\u1e40\u1e42\u1e44\u1e46\u1e48\u1e4a\u1e4c\u1e4e\u1e50\u1e52\u1e54\u1e56\u1e58\u1e5a\u1e5c\u1e5e\u1e60\u1e62\u1e64\u1e66\u1e68\u1e6a\u1e6c\u1e6e\u1e70\u1e72\u1e74\u1e76\u1e78\u1e7a\u1e7c\u1e7e\u1e80\u1e82\u1e84\u1e86\u1e88\u1e8a\u1e8c\u1e8e\u1e90\u1e92\u1e94\u1ea0\u1ea2\u1ea4\u1ea6\u1ea8\u1eaa\u1eac\u1eae\u1eb0\u1eb2\u1eb4\u1eb6\u1eb8\u1eba\u1ebc\u1ebe\u1ec0\u1ec2\u1ec4\u1ec6\u1ec8\u1eca\u1ecc\u1ece\u1ed0\u1ed2\u1ed4\u1ed6\u1ed8\u1eda\u1edc\u1ede\u1ee0\u1ee2\u1ee4\u1ee6\u1ee8\u1eea\u1eec\u1eee\u1ef0\u1ef2\u1ef4\u1ef6\u1ef8\u1f08\u1f09\u1f0a\u1f0b\u1f0c\u1f0d\u1f0e\u1f0f\u1f18\u1f19\u1f1a\u1f1b\u1f1c\u1f1d\u1f28\u1f29\u1f2a\u1f2b\u1f2c\u1f2d\u1f2e\u1f2f\u1f38\u1f39\u1f3a\u1f3b\u1f3c\u1f3d\u1f3e\u1f3f\u1f48\u1f49\u1f4a\u1f4b\u1f4c\u1f4d\u1f59\u1f5b\u1f5d\u1f5f\u1f68\u1f69\u1f6a\u1f6b\u1f6c\u1f6d\u1f6e\u1f6f\u1fb8\u1fb9\u1fba\u1fbb\u1fc8\u1fc9\u1fca\u1fcb\u1fd8\u1fd9\u1fda\u1fdb\u1fe8\u1fe9\u1fea\u1feb\u1fec\u1ff8\u1ff9\u1ffa\u1ffb\u2102\u2107\u210b\u210c\u210d\u2110\u2111\u2112\u2115\u2119\u211a\u211b\u211c\u211d\u2124\u2126\u2128\u212a\u212b\u212c\u212d\u2130\u2131\u2133\u213e\u213f\u2145\u2c00\u2c01\u2c02\u2c03\u2c04\u2c05\u2c06\u2c07\u2c08\u2c09\u2c0a\u2c0b\u2c0c\u2c0d\u2c0e\u2c0f\u2c10\u2c11\u2c12\u2c13\u2c14\u2c15\u2c16\u2c17\u2c18\u2c19\u2c1a\u2c1b\u2c1c\u2c1d\u2c1e\u2c1f\u2c20\u2c21\u2c22\u2c23\u2c24\u2c25\u2c26\u2c27\u2c28\u2c29\u2c2a\u2c2b\u2c2c\u2c2d\u2c2e\u2c80\u2c82\u2c84\u2c86\u2c88\u2c8a\u2c8c\u2c8e\u2c90\u2c92\u2c94\u2c96\u2c98\u2c9a\u2c9c\u2c9e\u2ca0\u2ca2\u2ca4\u2ca6\u2ca8\u2caa\u2cac\u2cae\u2cb0\u2cb2\u2cb4\u2cb6\u2cb8\u2cba\u2cbc\u2cbe\u2cc0\u2cc2\u2cc4\u2cc6\u2cc8\u2cca\u2ccc\u2cce\u2cd0\u2cd2\u2cd4\u2cd6\u2cd8\u2cda\u2cdc\u2cde\u2ce0\u2ce2\uff21\uff22\uff23\uff24\uff25\uff26\uff27\uff28\uff29\uff2a\uff2b\uff2c\uff2d\uff2e\uff2f\uff30\uff31\uff32\uff33\uff34\uff35\uff36\uff37\uff38\uff39\uff3a' + +Mc = u'\u0903\u093e\u093f\u0940\u0949\u094a\u094b\u094c\u0982\u0983\u09be\u09bf\u09c0\u09c7\u09c8\u09cb\u09cc\u09d7\u0a03\u0a3e\u0a3f\u0a40\u0a83\u0abe\u0abf\u0ac0\u0ac9\u0acb\u0acc\u0b02\u0b03\u0b3e\u0b40\u0b47\u0b48\u0b4b\u0b4c\u0b57\u0bbe\u0bbf\u0bc1\u0bc2\u0bc6\u0bc7\u0bc8\u0bca\u0bcb\u0bcc\u0bd7\u0c01\u0c02\u0c03\u0c41\u0c42\u0c43\u0c44\u0c82\u0c83\u0cbe\u0cc0\u0cc1\u0cc2\u0cc3\u0cc4\u0cc7\u0cc8\u0cca\u0ccb\u0cd5\u0cd6\u0d02\u0d03\u0d3e\u0d3f\u0d40\u0d46\u0d47\u0d48\u0d4a\u0d4b\u0d4c\u0d57\u0d82\u0d83\u0dcf\u0dd0\u0dd1\u0dd8\u0dd9\u0dda\u0ddb\u0ddc\u0ddd\u0dde\u0ddf\u0df2\u0df3\u0f3e\u0f3f\u0f7f\u102c\u1031\u1038\u1056\u1057\u17b6\u17be\u17bf\u17c0\u17c1\u17c2\u17c3\u17c4\u17c5\u17c7\u17c8\u1923\u1924\u1925\u1926\u1929\u192a\u192b\u1930\u1931\u1933\u1934\u1935\u1936\u1937\u1938\u19b0\u19b1\u19b2\u19b3\u19b4\u19b5\u19b6\u19b7\u19b8\u19b9\u19ba\u19bb\u19bc\u19bd\u19be\u19bf\u19c0\u19c8\u19c9\u1a19\u1a1a\u1a1b\ua802\ua823\ua824\ua827' + +Me = u'\u0488\u0489\u06de\u20dd\u20de\u20df\u20e0\u20e2\u20e3\u20e4' + +Mn = u'\u0300\u0301\u0302\u0303\u0304\u0305\u0306\u0307\u0308\u0309\u030a\u030b\u030c\u030d\u030e\u030f\u0310\u0311\u0312\u0313\u0314\u0315\u0316\u0317\u0318\u0319\u031a\u031b\u031c\u031d\u031e\u031f\u0320\u0321\u0322\u0323\u0324\u0325\u0326\u0327\u0328\u0329\u032a\u032b\u032c\u032d\u032e\u032f\u0330\u0331\u0332\u0333\u0334\u0335\u0336\u0337\u0338\u0339\u033a\u033b\u033c\u033d\u033e\u033f\u0340\u0341\u0342\u0343\u0344\u0345\u0346\u0347\u0348\u0349\u034a\u034b\u034c\u034d\u034e\u034f\u0350\u0351\u0352\u0353\u0354\u0355\u0356\u0357\u0358\u0359\u035a\u035b\u035c\u035d\u035e\u035f\u0360\u0361\u0362\u0363\u0364\u0365\u0366\u0367\u0368\u0369\u036a\u036b\u036c\u036d\u036e\u036f\u0483\u0484\u0485\u0486\u0591\u0592\u0593\u0594\u0595\u0596\u0597\u0598\u0599\u059a\u059b\u059c\u059d\u059e\u059f\u05a0\u05a1\u05a2\u05a3\u05a4\u05a5\u05a6\u05a7\u05a8\u05a9\u05aa\u05ab\u05ac\u05ad\u05ae\u05af\u05b0\u05b1\u05b2\u05b3\u05b4\u05b5\u05b6\u05b7\u05b8\u05b9\u05bb\u05bc\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610\u0611\u0612\u0613\u0614\u0615\u064b\u064c\u064d\u064e\u064f\u0650\u0651\u0652\u0653\u0654\u0655\u0656\u0657\u0658\u0659\u065a\u065b\u065c\u065d\u065e\u0670\u06d6\u06d7\u06d8\u06d9\u06da\u06db\u06dc\u06df\u06e0\u06e1\u06e2\u06e3\u06e4\u06e7\u06e8\u06ea\u06eb\u06ec\u06ed\u0711\u0730\u0731\u0732\u0733\u0734\u0735\u0736\u0737\u0738\u0739\u073a\u073b\u073c\u073d\u073e\u073f\u0740\u0741\u0742\u0743\u0744\u0745\u0746\u0747\u0748\u0749\u074a\u07a6\u07a7\u07a8\u07a9\u07aa\u07ab\u07ac\u07ad\u07ae\u07af\u07b0\u0901\u0902\u093c\u0941\u0942\u0943\u0944\u0945\u0946\u0947\u0948\u094d\u0951\u0952\u0953\u0954\u0962\u0963\u0981\u09bc\u09c1\u09c2\u09c3\u09c4\u09cd\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b\u0a4c\u0a4d\u0a70\u0a71\u0a81\u0a82\u0abc\u0ac1\u0ac2\u0ac3\u0ac4\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3f\u0b41\u0b42\u0b43\u0b4d\u0b56\u0b82\u0bc0\u0bcd\u0c3e\u0c3f\u0c40\u0c46\u0c47\u0c48\u0c4a\u0c4b\u0c4c\u0c4d\u0c55\u0c56\u0cbc\u0cbf\u0cc6\u0ccc\u0ccd\u0d41\u0d42\u0d43\u0d4d\u0dca\u0dd2\u0dd3\u0dd4\u0dd6\u0e31\u0e34\u0e35\u0e36\u0e37\u0e38\u0e39\u0e3a\u0e47\u0e48\u0e49\u0e4a\u0e4b\u0e4c\u0e4d\u0e4e\u0eb1\u0eb4\u0eb5\u0eb6\u0eb7\u0eb8\u0eb9\u0ebb\u0ebc\u0ec8\u0ec9\u0eca\u0ecb\u0ecc\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71\u0f72\u0f73\u0f74\u0f75\u0f76\u0f77\u0f78\u0f79\u0f7a\u0f7b\u0f7c\u0f7d\u0f7e\u0f80\u0f81\u0f82\u0f83\u0f84\u0f86\u0f87\u0f90\u0f91\u0f92\u0f93\u0f94\u0f95\u0f96\u0f97\u0f99\u0f9a\u0f9b\u0f9c\u0f9d\u0f9e\u0f9f\u0fa0\u0fa1\u0fa2\u0fa3\u0fa4\u0fa5\u0fa6\u0fa7\u0fa8\u0fa9\u0faa\u0fab\u0fac\u0fad\u0fae\u0faf\u0fb0\u0fb1\u0fb2\u0fb3\u0fb4\u0fb5\u0fb6\u0fb7\u0fb8\u0fb9\u0fba\u0fbb\u0fbc\u0fc6\u102d\u102e\u102f\u1030\u1032\u1036\u1037\u1039\u1058\u1059\u135f\u1712\u1713\u1714\u1732\u1733\u1734\u1752\u1753\u1772\u1773\u17b7\u17b8\u17b9\u17ba\u17bb\u17bc\u17bd\u17c6\u17c9\u17ca\u17cb\u17cc\u17cd\u17ce\u17cf\u17d0\u17d1\u17d2\u17d3\u17dd\u180b\u180c\u180d\u18a9\u1920\u1921\u1922\u1927\u1928\u1932\u1939\u193a\u193b\u1a17\u1a18\u1dc0\u1dc1\u1dc2\u1dc3\u20d0\u20d1\u20d2\u20d3\u20d4\u20d5\u20d6\u20d7\u20d8\u20d9\u20da\u20db\u20dc\u20e1\u20e5\u20e6\u20e7\u20e8\u20e9\u20ea\u20eb\u302a\u302b\u302c\u302d\u302e\u302f\u3099\u309a\ua806\ua80b\ua825\ua826\ufb1e\ufe00\ufe01\ufe02\ufe03\ufe04\ufe05\ufe06\ufe07\ufe08\ufe09\ufe0a\ufe0b\ufe0c\ufe0d\ufe0e\ufe0f\ufe20\ufe21\ufe22\ufe23' + +Nd = u'0123456789\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9\u0966\u0967\u0968\u0969\u096a\u096b\u096c\u096d\u096e\u096f\u09e6\u09e7\u09e8\u09e9\u09ea\u09eb\u09ec\u09ed\u09ee\u09ef\u0a66\u0a67\u0a68\u0a69\u0a6a\u0a6b\u0a6c\u0a6d\u0a6e\u0a6f\u0ae6\u0ae7\u0ae8\u0ae9\u0aea\u0aeb\u0aec\u0aed\u0aee\u0aef\u0b66\u0b67\u0b68\u0b69\u0b6a\u0b6b\u0b6c\u0b6d\u0b6e\u0b6f\u0be6\u0be7\u0be8\u0be9\u0bea\u0beb\u0bec\u0bed\u0bee\u0bef\u0c66\u0c67\u0c68\u0c69\u0c6a\u0c6b\u0c6c\u0c6d\u0c6e\u0c6f\u0ce6\u0ce7\u0ce8\u0ce9\u0cea\u0ceb\u0cec\u0ced\u0cee\u0cef\u0d66\u0d67\u0d68\u0d69\u0d6a\u0d6b\u0d6c\u0d6d\u0d6e\u0d6f\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56\u0e57\u0e58\u0e59\u0ed0\u0ed1\u0ed2\u0ed3\u0ed4\u0ed5\u0ed6\u0ed7\u0ed8\u0ed9\u0f20\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\u0f29\u1040\u1041\u1042\u1043\u1044\u1045\u1046\u1047\u1048\u1049\u17e0\u17e1\u17e2\u17e3\u17e4\u17e5\u17e6\u17e7\u17e8\u17e9\u1810\u1811\u1812\u1813\u1814\u1815\u1816\u1817\u1818\u1819\u1946\u1947\u1948\u1949\u194a\u194b\u194c\u194d\u194e\u194f\u19d0\u19d1\u19d2\u19d3\u19d4\u19d5\u19d6\u19d7\u19d8\u19d9\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19' + +Nl = u'\u16ee\u16ef\u16f0\u2160\u2161\u2162\u2163\u2164\u2165\u2166\u2167\u2168\u2169\u216a\u216b\u216c\u216d\u216e\u216f\u2170\u2171\u2172\u2173\u2174\u2175\u2176\u2177\u2178\u2179\u217a\u217b\u217c\u217d\u217e\u217f\u2180\u2181\u2182\u2183\u3007\u3021\u3022\u3023\u3024\u3025\u3026\u3027\u3028\u3029\u3038\u3039\u303a' + +No = u'\xb2\xb3\xb9\xbc\xbd\xbe\u09f4\u09f5\u09f6\u09f7\u09f8\u09f9\u0bf0\u0bf1\u0bf2\u0f2a\u0f2b\u0f2c\u0f2d\u0f2e\u0f2f\u0f30\u0f31\u0f32\u0f33\u1369\u136a\u136b\u136c\u136d\u136e\u136f\u1370\u1371\u1372\u1373\u1374\u1375\u1376\u1377\u1378\u1379\u137a\u137b\u137c\u17f0\u17f1\u17f2\u17f3\u17f4\u17f5\u17f6\u17f7\u17f8\u17f9\u2070\u2074\u2075\u2076\u2077\u2078\u2079\u2080\u2081\u2082\u2083\u2084\u2085\u2086\u2087\u2088\u2089\u2153\u2154\u2155\u2156\u2157\u2158\u2159\u215a\u215b\u215c\u215d\u215e\u215f\u2460\u2461\u2462\u2463\u2464\u2465\u2466\u2467\u2468\u2469\u246a\u246b\u246c\u246d\u246e\u246f\u2470\u2471\u2472\u2473\u2474\u2475\u2476\u2477\u2478\u2479\u247a\u247b\u247c\u247d\u247e\u247f\u2480\u2481\u2482\u2483\u2484\u2485\u2486\u2487\u2488\u2489\u248a\u248b\u248c\u248d\u248e\u248f\u2490\u2491\u2492\u2493\u2494\u2495\u2496\u2497\u2498\u2499\u249a\u249b\u24ea\u24eb\u24ec\u24ed\u24ee\u24ef\u24f0\u24f1\u24f2\u24f3\u24f4\u24f5\u24f6\u24f7\u24f8\u24f9\u24fa\u24fb\u24fc\u24fd\u24fe\u24ff\u2776\u2777\u2778\u2779\u277a\u277b\u277c\u277d\u277e\u277f\u2780\u2781\u2782\u2783\u2784\u2785\u2786\u2787\u2788\u2789\u278a\u278b\u278c\u278d\u278e\u278f\u2790\u2791\u2792\u2793\u2cfd\u3192\u3193\u3194\u3195\u3220\u3221\u3222\u3223\u3224\u3225\u3226\u3227\u3228\u3229\u3251\u3252\u3253\u3254\u3255\u3256\u3257\u3258\u3259\u325a\u325b\u325c\u325d\u325e\u325f\u3280\u3281\u3282\u3283\u3284\u3285\u3286\u3287\u3288\u3289\u32b1\u32b2\u32b3\u32b4\u32b5\u32b6\u32b7\u32b8\u32b9\u32ba\u32bb\u32bc\u32bd\u32be\u32bf' + +Pc = u'_\u203f\u2040\u2054\ufe33\ufe34\ufe4d\ufe4e\ufe4f\uff3f' + +Pd = u'-\u058a\u1806\u2010\u2011\u2012\u2013\u2014\u2015\u2e17\u301c\u3030\u30a0\ufe31\ufe32\ufe58\ufe63\uff0d' + +Pe = u')]}\u0f3b\u0f3d\u169c\u2046\u207e\u208e\u232a\u23b5\u2769\u276b\u276d\u276f\u2771\u2773\u2775\u27c6\u27e7\u27e9\u27eb\u2984\u2986\u2988\u298a\u298c\u298e\u2990\u2992\u2994\u2996\u2998\u29d9\u29db\u29fd\u3009\u300b\u300d\u300f\u3011\u3015\u3017\u3019\u301b\u301e\u301f\ufd3f\ufe18\ufe36\ufe38\ufe3a\ufe3c\ufe3e\ufe40\ufe42\ufe44\ufe48\ufe5a\ufe5c\ufe5e\uff09\uff3d\uff5d\uff60\uff63' + +Pf = u'\xbb\u2019\u201d\u203a\u2e03\u2e05\u2e0a\u2e0d\u2e1d' + +Pi = u'\xab\u2018\u201b\u201c\u201f\u2039\u2e02\u2e04\u2e09\u2e0c\u2e1c' + +Po = u'!"#%&\'*,./:;?@\\\xa1\xb7\xbf\u037e\u0387\u055a\u055b\u055c\u055d\u055e\u055f\u0589\u05be\u05c0\u05c3\u05c6\u05f3\u05f4\u060c\u060d\u061b\u061e\u061f\u066a\u066b\u066c\u066d\u06d4\u0700\u0701\u0702\u0703\u0704\u0705\u0706\u0707\u0708\u0709\u070a\u070b\u070c\u070d\u0964\u0965\u0970\u0df4\u0e4f\u0e5a\u0e5b\u0f04\u0f05\u0f06\u0f07\u0f08\u0f09\u0f0a\u0f0b\u0f0c\u0f0d\u0f0e\u0f0f\u0f10\u0f11\u0f12\u0f85\u0fd0\u0fd1\u104a\u104b\u104c\u104d\u104e\u104f\u10fb\u1361\u1362\u1363\u1364\u1365\u1366\u1367\u1368\u166d\u166e\u16eb\u16ec\u16ed\u1735\u1736\u17d4\u17d5\u17d6\u17d8\u17d9\u17da\u1800\u1801\u1802\u1803\u1804\u1805\u1807\u1808\u1809\u180a\u1944\u1945\u19de\u19df\u1a1e\u1a1f\u2016\u2017\u2020\u2021\u2022\u2023\u2024\u2025\u2026\u2027\u2030\u2031\u2032\u2033\u2034\u2035\u2036\u2037\u2038\u203b\u203c\u203d\u203e\u2041\u2042\u2043\u2047\u2048\u2049\u204a\u204b\u204c\u204d\u204e\u204f\u2050\u2051\u2053\u2055\u2056\u2057\u2058\u2059\u205a\u205b\u205c\u205d\u205e\u23b6\u2cf9\u2cfa\u2cfb\u2cfc\u2cfe\u2cff\u2e00\u2e01\u2e06\u2e07\u2e08\u2e0b\u2e0e\u2e0f\u2e10\u2e11\u2e12\u2e13\u2e14\u2e15\u2e16\u3001\u3002\u3003\u303d\u30fb\ufe10\ufe11\ufe12\ufe13\ufe14\ufe15\ufe16\ufe19\ufe30\ufe45\ufe46\ufe49\ufe4a\ufe4b\ufe4c\ufe50\ufe51\ufe52\ufe54\ufe55\ufe56\ufe57\ufe5f\ufe60\ufe61\ufe68\ufe6a\ufe6b\uff01\uff02\uff03\uff05\uff06\uff07\uff0a\uff0c\uff0e\uff0f\uff1a\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65' + +Ps = u'([{\u0f3a\u0f3c\u169b\u201a\u201e\u2045\u207d\u208d\u2329\u23b4\u2768\u276a\u276c\u276e\u2770\u2772\u2774\u27c5\u27e6\u27e8\u27ea\u2983\u2985\u2987\u2989\u298b\u298d\u298f\u2991\u2993\u2995\u2997\u29d8\u29da\u29fc\u3008\u300a\u300c\u300e\u3010\u3014\u3016\u3018\u301a\u301d\ufd3e\ufe17\ufe35\ufe37\ufe39\ufe3b\ufe3d\ufe3f\ufe41\ufe43\ufe47\ufe59\ufe5b\ufe5d\uff08\uff3b\uff5b\uff5f\uff62' + +Sc = u'$\xa2\xa3\xa4\xa5\u060b\u09f2\u09f3\u0af1\u0bf9\u0e3f\u17db\u20a0\u20a1\u20a2\u20a3\u20a4\u20a5\u20a6\u20a7\u20a8\u20a9\u20aa\u20ab\u20ac\u20ad\u20ae\u20af\u20b0\u20b1\u20b2\u20b3\u20b4\u20b5\ufdfc\ufe69\uff04\uffe0\uffe1\uffe5\uffe6' + +Sk = u'^`\xa8\xaf\xb4\xb8\u02c2\u02c3\u02c4\u02c5\u02d2\u02d3\u02d4\u02d5\u02d6\u02d7\u02d8\u02d9\u02da\u02db\u02dc\u02dd\u02de\u02df\u02e5\u02e6\u02e7\u02e8\u02e9\u02ea\u02eb\u02ec\u02ed\u02ef\u02f0\u02f1\u02f2\u02f3\u02f4\u02f5\u02f6\u02f7\u02f8\u02f9\u02fa\u02fb\u02fc\u02fd\u02fe\u02ff\u0374\u0375\u0384\u0385\u1fbd\u1fbf\u1fc0\u1fc1\u1fcd\u1fce\u1fcf\u1fdd\u1fde\u1fdf\u1fed\u1fee\u1fef\u1ffd\u1ffe\u309b\u309c\ua700\ua701\ua702\ua703\ua704\ua705\ua706\ua707\ua708\ua709\ua70a\ua70b\ua70c\ua70d\ua70e\ua70f\ua710\ua711\ua712\ua713\ua714\ua715\ua716\uff3e\uff40\uffe3' + +Sm = u'+<=>|~\xac\xb1\xd7\xf7\u03f6\u2044\u2052\u207a\u207b\u207c\u208a\u208b\u208c\u2140\u2141\u2142\u2143\u2144\u214b\u2190\u2191\u2192\u2193\u2194\u219a\u219b\u21a0\u21a3\u21a6\u21ae\u21ce\u21cf\u21d2\u21d4\u21f4\u21f5\u21f6\u21f7\u21f8\u21f9\u21fa\u21fb\u21fc\u21fd\u21fe\u21ff\u2200\u2201\u2202\u2203\u2204\u2205\u2206\u2207\u2208\u2209\u220a\u220b\u220c\u220d\u220e\u220f\u2210\u2211\u2212\u2213\u2214\u2215\u2216\u2217\u2218\u2219\u221a\u221b\u221c\u221d\u221e\u221f\u2220\u2221\u2222\u2223\u2224\u2225\u2226\u2227\u2228\u2229\u222a\u222b\u222c\u222d\u222e\u222f\u2230\u2231\u2232\u2233\u2234\u2235\u2236\u2237\u2238\u2239\u223a\u223b\u223c\u223d\u223e\u223f\u2240\u2241\u2242\u2243\u2244\u2245\u2246\u2247\u2248\u2249\u224a\u224b\u224c\u224d\u224e\u224f\u2250\u2251\u2252\u2253\u2254\u2255\u2256\u2257\u2258\u2259\u225a\u225b\u225c\u225d\u225e\u225f\u2260\u2261\u2262\u2263\u2264\u2265\u2266\u2267\u2268\u2269\u226a\u226b\u226c\u226d\u226e\u226f\u2270\u2271\u2272\u2273\u2274\u2275\u2276\u2277\u2278\u2279\u227a\u227b\u227c\u227d\u227e\u227f\u2280\u2281\u2282\u2283\u2284\u2285\u2286\u2287\u2288\u2289\u228a\u228b\u228c\u228d\u228e\u228f\u2290\u2291\u2292\u2293\u2294\u2295\u2296\u2297\u2298\u2299\u229a\u229b\u229c\u229d\u229e\u229f\u22a0\u22a1\u22a2\u22a3\u22a4\u22a5\u22a6\u22a7\u22a8\u22a9\u22aa\u22ab\u22ac\u22ad\u22ae\u22af\u22b0\u22b1\u22b2\u22b3\u22b4\u22b5\u22b6\u22b7\u22b8\u22b9\u22ba\u22bb\u22bc\u22bd\u22be\u22bf\u22c0\u22c1\u22c2\u22c3\u22c4\u22c5\u22c6\u22c7\u22c8\u22c9\u22ca\u22cb\u22cc\u22cd\u22ce\u22cf\u22d0\u22d1\u22d2\u22d3\u22d4\u22d5\u22d6\u22d7\u22d8\u22d9\u22da\u22db\u22dc\u22dd\u22de\u22df\u22e0\u22e1\u22e2\u22e3\u22e4\u22e5\u22e6\u22e7\u22e8\u22e9\u22ea\u22eb\u22ec\u22ed\u22ee\u22ef\u22f0\u22f1\u22f2\u22f3\u22f4\u22f5\u22f6\u22f7\u22f8\u22f9\u22fa\u22fb\u22fc\u22fd\u22fe\u22ff\u2308\u2309\u230a\u230b\u2320\u2321\u237c\u239b\u239c\u239d\u239e\u239f\u23a0\u23a1\u23a2\u23a3\u23a4\u23a5\u23a6\u23a7\u23a8\u23a9\u23aa\u23ab\u23ac\u23ad\u23ae\u23af\u23b0\u23b1\u23b2\u23b3\u25b7\u25c1\u25f8\u25f9\u25fa\u25fb\u25fc\u25fd\u25fe\u25ff\u266f\u27c0\u27c1\u27c2\u27c3\u27c4\u27d0\u27d1\u27d2\u27d3\u27d4\u27d5\u27d6\u27d7\u27d8\u27d9\u27da\u27db\u27dc\u27dd\u27de\u27df\u27e0\u27e1\u27e2\u27e3\u27e4\u27e5\u27f0\u27f1\u27f2\u27f3\u27f4\u27f5\u27f6\u27f7\u27f8\u27f9\u27fa\u27fb\u27fc\u27fd\u27fe\u27ff\u2900\u2901\u2902\u2903\u2904\u2905\u2906\u2907\u2908\u2909\u290a\u290b\u290c\u290d\u290e\u290f\u2910\u2911\u2912\u2913\u2914\u2915\u2916\u2917\u2918\u2919\u291a\u291b\u291c\u291d\u291e\u291f\u2920\u2921\u2922\u2923\u2924\u2925\u2926\u2927\u2928\u2929\u292a\u292b\u292c\u292d\u292e\u292f\u2930\u2931\u2932\u2933\u2934\u2935\u2936\u2937\u2938\u2939\u293a\u293b\u293c\u293d\u293e\u293f\u2940\u2941\u2942\u2943\u2944\u2945\u2946\u2947\u2948\u2949\u294a\u294b\u294c\u294d\u294e\u294f\u2950\u2951\u2952\u2953\u2954\u2955\u2956\u2957\u2958\u2959\u295a\u295b\u295c\u295d\u295e\u295f\u2960\u2961\u2962\u2963\u2964\u2965\u2966\u2967\u2968\u2969\u296a\u296b\u296c\u296d\u296e\u296f\u2970\u2971\u2972\u2973\u2974\u2975\u2976\u2977\u2978\u2979\u297a\u297b\u297c\u297d\u297e\u297f\u2980\u2981\u2982\u2999\u299a\u299b\u299c\u299d\u299e\u299f\u29a0\u29a1\u29a2\u29a3\u29a4\u29a5\u29a6\u29a7\u29a8\u29a9\u29aa\u29ab\u29ac\u29ad\u29ae\u29af\u29b0\u29b1\u29b2\u29b3\u29b4\u29b5\u29b6\u29b7\u29b8\u29b9\u29ba\u29bb\u29bc\u29bd\u29be\u29bf\u29c0\u29c1\u29c2\u29c3\u29c4\u29c5\u29c6\u29c7\u29c8\u29c9\u29ca\u29cb\u29cc\u29cd\u29ce\u29cf\u29d0\u29d1\u29d2\u29d3\u29d4\u29d5\u29d6\u29d7\u29dc\u29dd\u29de\u29df\u29e0\u29e1\u29e2\u29e3\u29e4\u29e5\u29e6\u29e7\u29e8\u29e9\u29ea\u29eb\u29ec\u29ed\u29ee\u29ef\u29f0\u29f1\u29f2\u29f3\u29f4\u29f5\u29f6\u29f7\u29f8\u29f9\u29fa\u29fb\u29fe\u29ff\u2a00\u2a01\u2a02\u2a03\u2a04\u2a05\u2a06\u2a07\u2a08\u2a09\u2a0a\u2a0b\u2a0c\u2a0d\u2a0e\u2a0f\u2a10\u2a11\u2a12\u2a13\u2a14\u2a15\u2a16\u2a17\u2a18\u2a19\u2a1a\u2a1b\u2a1c\u2a1d\u2a1e\u2a1f\u2a20\u2a21\u2a22\u2a23\u2a24\u2a25\u2a26\u2a27\u2a28\u2a29\u2a2a\u2a2b\u2a2c\u2a2d\u2a2e\u2a2f\u2a30\u2a31\u2a32\u2a33\u2a34\u2a35\u2a36\u2a37\u2a38\u2a39\u2a3a\u2a3b\u2a3c\u2a3d\u2a3e\u2a3f\u2a40\u2a41\u2a42\u2a43\u2a44\u2a45\u2a46\u2a47\u2a48\u2a49\u2a4a\u2a4b\u2a4c\u2a4d\u2a4e\u2a4f\u2a50\u2a51\u2a52\u2a53\u2a54\u2a55\u2a56\u2a57\u2a58\u2a59\u2a5a\u2a5b\u2a5c\u2a5d\u2a5e\u2a5f\u2a60\u2a61\u2a62\u2a63\u2a64\u2a65\u2a66\u2a67\u2a68\u2a69\u2a6a\u2a6b\u2a6c\u2a6d\u2a6e\u2a6f\u2a70\u2a71\u2a72\u2a73\u2a74\u2a75\u2a76\u2a77\u2a78\u2a79\u2a7a\u2a7b\u2a7c\u2a7d\u2a7e\u2a7f\u2a80\u2a81\u2a82\u2a83\u2a84\u2a85\u2a86\u2a87\u2a88\u2a89\u2a8a\u2a8b\u2a8c\u2a8d\u2a8e\u2a8f\u2a90\u2a91\u2a92\u2a93\u2a94\u2a95\u2a96\u2a97\u2a98\u2a99\u2a9a\u2a9b\u2a9c\u2a9d\u2a9e\u2a9f\u2aa0\u2aa1\u2aa2\u2aa3\u2aa4\u2aa5\u2aa6\u2aa7\u2aa8\u2aa9\u2aaa\u2aab\u2aac\u2aad\u2aae\u2aaf\u2ab0\u2ab1\u2ab2\u2ab3\u2ab4\u2ab5\u2ab6\u2ab7\u2ab8\u2ab9\u2aba\u2abb\u2abc\u2abd\u2abe\u2abf\u2ac0\u2ac1\u2ac2\u2ac3\u2ac4\u2ac5\u2ac6\u2ac7\u2ac8\u2ac9\u2aca\u2acb\u2acc\u2acd\u2ace\u2acf\u2ad0\u2ad1\u2ad2\u2ad3\u2ad4\u2ad5\u2ad6\u2ad7\u2ad8\u2ad9\u2ada\u2adb\u2adc\u2add\u2ade\u2adf\u2ae0\u2ae1\u2ae2\u2ae3\u2ae4\u2ae5\u2ae6\u2ae7\u2ae8\u2ae9\u2aea\u2aeb\u2aec\u2aed\u2aee\u2aef\u2af0\u2af1\u2af2\u2af3\u2af4\u2af5\u2af6\u2af7\u2af8\u2af9\u2afa\u2afb\u2afc\u2afd\u2afe\u2aff\ufb29\ufe62\ufe64\ufe65\ufe66\uff0b\uff1c\uff1d\uff1e\uff5c\uff5e\uffe2\uffe9\uffea\uffeb\uffec' + +So = u'\xa6\xa7\xa9\xae\xb0\xb6\u0482\u060e\u060f\u06e9\u06fd\u06fe\u09fa\u0b70\u0bf3\u0bf4\u0bf5\u0bf6\u0bf7\u0bf8\u0bfa\u0f01\u0f02\u0f03\u0f13\u0f14\u0f15\u0f16\u0f17\u0f1a\u0f1b\u0f1c\u0f1d\u0f1e\u0f1f\u0f34\u0f36\u0f38\u0fbe\u0fbf\u0fc0\u0fc1\u0fc2\u0fc3\u0fc4\u0fc5\u0fc7\u0fc8\u0fc9\u0fca\u0fcb\u0fcc\u0fcf\u1360\u1390\u1391\u1392\u1393\u1394\u1395\u1396\u1397\u1398\u1399\u1940\u19e0\u19e1\u19e2\u19e3\u19e4\u19e5\u19e6\u19e7\u19e8\u19e9\u19ea\u19eb\u19ec\u19ed\u19ee\u19ef\u19f0\u19f1\u19f2\u19f3\u19f4\u19f5\u19f6\u19f7\u19f8\u19f9\u19fa\u19fb\u19fc\u19fd\u19fe\u19ff\u2100\u2101\u2103\u2104\u2105\u2106\u2108\u2109\u2114\u2116\u2117\u2118\u211e\u211f\u2120\u2121\u2122\u2123\u2125\u2127\u2129\u212e\u2132\u213a\u213b\u214a\u214c\u2195\u2196\u2197\u2198\u2199\u219c\u219d\u219e\u219f\u21a1\u21a2\u21a4\u21a5\u21a7\u21a8\u21a9\u21aa\u21ab\u21ac\u21ad\u21af\u21b0\u21b1\u21b2\u21b3\u21b4\u21b5\u21b6\u21b7\u21b8\u21b9\u21ba\u21bb\u21bc\u21bd\u21be\u21bf\u21c0\u21c1\u21c2\u21c3\u21c4\u21c5\u21c6\u21c7\u21c8\u21c9\u21ca\u21cb\u21cc\u21cd\u21d0\u21d1\u21d3\u21d5\u21d6\u21d7\u21d8\u21d9\u21da\u21db\u21dc\u21dd\u21de\u21df\u21e0\u21e1\u21e2\u21e3\u21e4\u21e5\u21e6\u21e7\u21e8\u21e9\u21ea\u21eb\u21ec\u21ed\u21ee\u21ef\u21f0\u21f1\u21f2\u21f3\u2300\u2301\u2302\u2303\u2304\u2305\u2306\u2307\u230c\u230d\u230e\u230f\u2310\u2311\u2312\u2313\u2314\u2315\u2316\u2317\u2318\u2319\u231a\u231b\u231c\u231d\u231e\u231f\u2322\u2323\u2324\u2325\u2326\u2327\u2328\u232b\u232c\u232d\u232e\u232f\u2330\u2331\u2332\u2333\u2334\u2335\u2336\u2337\u2338\u2339\u233a\u233b\u233c\u233d\u233e\u233f\u2340\u2341\u2342\u2343\u2344\u2345\u2346\u2347\u2348\u2349\u234a\u234b\u234c\u234d\u234e\u234f\u2350\u2351\u2352\u2353\u2354\u2355\u2356\u2357\u2358\u2359\u235a\u235b\u235c\u235d\u235e\u235f\u2360\u2361\u2362\u2363\u2364\u2365\u2366\u2367\u2368\u2369\u236a\u236b\u236c\u236d\u236e\u236f\u2370\u2371\u2372\u2373\u2374\u2375\u2376\u2377\u2378\u2379\u237a\u237b\u237d\u237e\u237f\u2380\u2381\u2382\u2383\u2384\u2385\u2386\u2387\u2388\u2389\u238a\u238b\u238c\u238d\u238e\u238f\u2390\u2391\u2392\u2393\u2394\u2395\u2396\u2397\u2398\u2399\u239a\u23b7\u23b8\u23b9\u23ba\u23bb\u23bc\u23bd\u23be\u23bf\u23c0\u23c1\u23c2\u23c3\u23c4\u23c5\u23c6\u23c7\u23c8\u23c9\u23ca\u23cb\u23cc\u23cd\u23ce\u23cf\u23d0\u23d1\u23d2\u23d3\u23d4\u23d5\u23d6\u23d7\u23d8\u23d9\u23da\u23db\u2400\u2401\u2402\u2403\u2404\u2405\u2406\u2407\u2408\u2409\u240a\u240b\u240c\u240d\u240e\u240f\u2410\u2411\u2412\u2413\u2414\u2415\u2416\u2417\u2418\u2419\u241a\u241b\u241c\u241d\u241e\u241f\u2420\u2421\u2422\u2423\u2424\u2425\u2426\u2440\u2441\u2442\u2443\u2444\u2445\u2446\u2447\u2448\u2449\u244a\u249c\u249d\u249e\u249f\u24a0\u24a1\u24a2\u24a3\u24a4\u24a5\u24a6\u24a7\u24a8\u24a9\u24aa\u24ab\u24ac\u24ad\u24ae\u24af\u24b0\u24b1\u24b2\u24b3\u24b4\u24b5\u24b6\u24b7\u24b8\u24b9\u24ba\u24bb\u24bc\u24bd\u24be\u24bf\u24c0\u24c1\u24c2\u24c3\u24c4\u24c5\u24c6\u24c7\u24c8\u24c9\u24ca\u24cb\u24cc\u24cd\u24ce\u24cf\u24d0\u24d1\u24d2\u24d3\u24d4\u24d5\u24d6\u24d7\u24d8\u24d9\u24da\u24db\u24dc\u24dd\u24de\u24df\u24e0\u24e1\u24e2\u24e3\u24e4\u24e5\u24e6\u24e7\u24e8\u24e9\u2500\u2501\u2502\u2503\u2504\u2505\u2506\u2507\u2508\u2509\u250a\u250b\u250c\u250d\u250e\u250f\u2510\u2511\u2512\u2513\u2514\u2515\u2516\u2517\u2518\u2519\u251a\u251b\u251c\u251d\u251e\u251f\u2520\u2521\u2522\u2523\u2524\u2525\u2526\u2527\u2528\u2529\u252a\u252b\u252c\u252d\u252e\u252f\u2530\u2531\u2532\u2533\u2534\u2535\u2536\u2537\u2538\u2539\u253a\u253b\u253c\u253d\u253e\u253f\u2540\u2541\u2542\u2543\u2544\u2545\u2546\u2547\u2548\u2549\u254a\u254b\u254c\u254d\u254e\u254f\u2550\u2551\u2552\u2553\u2554\u2555\u2556\u2557\u2558\u2559\u255a\u255b\u255c\u255d\u255e\u255f\u2560\u2561\u2562\u2563\u2564\u2565\u2566\u2567\u2568\u2569\u256a\u256b\u256c\u256d\u256e\u256f\u2570\u2571\u2572\u2573\u2574\u2575\u2576\u2577\u2578\u2579\u257a\u257b\u257c\u257d\u257e\u257f\u2580\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588\u2589\u258a\u258b\u258c\u258d\u258e\u258f\u2590\u2591\u2592\u2593\u2594\u2595\u2596\u2597\u2598\u2599\u259a\u259b\u259c\u259d\u259e\u259f\u25a0\u25a1\u25a2\u25a3\u25a4\u25a5\u25a6\u25a7\u25a8\u25a9\u25aa\u25ab\u25ac\u25ad\u25ae\u25af\u25b0\u25b1\u25b2\u25b3\u25b4\u25b5\u25b6\u25b8\u25b9\u25ba\u25bb\u25bc\u25bd\u25be\u25bf\u25c0\u25c2\u25c3\u25c4\u25c5\u25c6\u25c7\u25c8\u25c9\u25ca\u25cb\u25cc\u25cd\u25ce\u25cf\u25d0\u25d1\u25d2\u25d3\u25d4\u25d5\u25d6\u25d7\u25d8\u25d9\u25da\u25db\u25dc\u25dd\u25de\u25df\u25e0\u25e1\u25e2\u25e3\u25e4\u25e5\u25e6\u25e7\u25e8\u25e9\u25ea\u25eb\u25ec\u25ed\u25ee\u25ef\u25f0\u25f1\u25f2\u25f3\u25f4\u25f5\u25f6\u25f7\u2600\u2601\u2602\u2603\u2604\u2605\u2606\u2607\u2608\u2609\u260a\u260b\u260c\u260d\u260e\u260f\u2610\u2611\u2612\u2613\u2614\u2615\u2616\u2617\u2618\u2619\u261a\u261b\u261c\u261d\u261e\u261f\u2620\u2621\u2622\u2623\u2624\u2625\u2626\u2627\u2628\u2629\u262a\u262b\u262c\u262d\u262e\u262f\u2630\u2631\u2632\u2633\u2634\u2635\u2636\u2637\u2638\u2639\u263a\u263b\u263c\u263d\u263e\u263f\u2640\u2641\u2642\u2643\u2644\u2645\u2646\u2647\u2648\u2649\u264a\u264b\u264c\u264d\u264e\u264f\u2650\u2651\u2652\u2653\u2654\u2655\u2656\u2657\u2658\u2659\u265a\u265b\u265c\u265d\u265e\u265f\u2660\u2661\u2662\u2663\u2664\u2665\u2666\u2667\u2668\u2669\u266a\u266b\u266c\u266d\u266e\u2670\u2671\u2672\u2673\u2674\u2675\u2676\u2677\u2678\u2679\u267a\u267b\u267c\u267d\u267e\u267f\u2680\u2681\u2682\u2683\u2684\u2685\u2686\u2687\u2688\u2689\u268a\u268b\u268c\u268d\u268e\u268f\u2690\u2691\u2692\u2693\u2694\u2695\u2696\u2697\u2698\u2699\u269a\u269b\u269c\u26a0\u26a1\u26a2\u26a3\u26a4\u26a5\u26a6\u26a7\u26a8\u26a9\u26aa\u26ab\u26ac\u26ad\u26ae\u26af\u26b0\u26b1\u2701\u2702\u2703\u2704\u2706\u2707\u2708\u2709\u270c\u270d\u270e\u270f\u2710\u2711\u2712\u2713\u2714\u2715\u2716\u2717\u2718\u2719\u271a\u271b\u271c\u271d\u271e\u271f\u2720\u2721\u2722\u2723\u2724\u2725\u2726\u2727\u2729\u272a\u272b\u272c\u272d\u272e\u272f\u2730\u2731\u2732\u2733\u2734\u2735\u2736\u2737\u2738\u2739\u273a\u273b\u273c\u273d\u273e\u273f\u2740\u2741\u2742\u2743\u2744\u2745\u2746\u2747\u2748\u2749\u274a\u274b\u274d\u274f\u2750\u2751\u2752\u2756\u2758\u2759\u275a\u275b\u275c\u275d\u275e\u2761\u2762\u2763\u2764\u2765\u2766\u2767\u2794\u2798\u2799\u279a\u279b\u279c\u279d\u279e\u279f\u27a0\u27a1\u27a2\u27a3\u27a4\u27a5\u27a6\u27a7\u27a8\u27a9\u27aa\u27ab\u27ac\u27ad\u27ae\u27af\u27b1\u27b2\u27b3\u27b4\u27b5\u27b6\u27b7\u27b8\u27b9\u27ba\u27bb\u27bc\u27bd\u27be\u2800\u2801\u2802\u2803\u2804\u2805\u2806\u2807\u2808\u2809\u280a\u280b\u280c\u280d\u280e\u280f\u2810\u2811\u2812\u2813\u2814\u2815\u2816\u2817\u2818\u2819\u281a\u281b\u281c\u281d\u281e\u281f\u2820\u2821\u2822\u2823\u2824\u2825\u2826\u2827\u2828\u2829\u282a\u282b\u282c\u282d\u282e\u282f\u2830\u2831\u2832\u2833\u2834\u2835\u2836\u2837\u2838\u2839\u283a\u283b\u283c\u283d\u283e\u283f\u2840\u2841\u2842\u2843\u2844\u2845\u2846\u2847\u2848\u2849\u284a\u284b\u284c\u284d\u284e\u284f\u2850\u2851\u2852\u2853\u2854\u2855\u2856\u2857\u2858\u2859\u285a\u285b\u285c\u285d\u285e\u285f\u2860\u2861\u2862\u2863\u2864\u2865\u2866\u2867\u2868\u2869\u286a\u286b\u286c\u286d\u286e\u286f\u2870\u2871\u2872\u2873\u2874\u2875\u2876\u2877\u2878\u2879\u287a\u287b\u287c\u287d\u287e\u287f\u2880\u2881\u2882\u2883\u2884\u2885\u2886\u2887\u2888\u2889\u288a\u288b\u288c\u288d\u288e\u288f\u2890\u2891\u2892\u2893\u2894\u2895\u2896\u2897\u2898\u2899\u289a\u289b\u289c\u289d\u289e\u289f\u28a0\u28a1\u28a2\u28a3\u28a4\u28a5\u28a6\u28a7\u28a8\u28a9\u28aa\u28ab\u28ac\u28ad\u28ae\u28af\u28b0\u28b1\u28b2\u28b3\u28b4\u28b5\u28b6\u28b7\u28b8\u28b9\u28ba\u28bb\u28bc\u28bd\u28be\u28bf\u28c0\u28c1\u28c2\u28c3\u28c4\u28c5\u28c6\u28c7\u28c8\u28c9\u28ca\u28cb\u28cc\u28cd\u28ce\u28cf\u28d0\u28d1\u28d2\u28d3\u28d4\u28d5\u28d6\u28d7\u28d8\u28d9\u28da\u28db\u28dc\u28dd\u28de\u28df\u28e0\u28e1\u28e2\u28e3\u28e4\u28e5\u28e6\u28e7\u28e8\u28e9\u28ea\u28eb\u28ec\u28ed\u28ee\u28ef\u28f0\u28f1\u28f2\u28f3\u28f4\u28f5\u28f6\u28f7\u28f8\u28f9\u28fa\u28fb\u28fc\u28fd\u28fe\u28ff\u2b00\u2b01\u2b02\u2b03\u2b04\u2b05\u2b06\u2b07\u2b08\u2b09\u2b0a\u2b0b\u2b0c\u2b0d\u2b0e\u2b0f\u2b10\u2b11\u2b12\u2b13\u2ce5\u2ce6\u2ce7\u2ce8\u2ce9\u2cea\u2e80\u2e81\u2e82\u2e83\u2e84\u2e85\u2e86\u2e87\u2e88\u2e89\u2e8a\u2e8b\u2e8c\u2e8d\u2e8e\u2e8f\u2e90\u2e91\u2e92\u2e93\u2e94\u2e95\u2e96\u2e97\u2e98\u2e99\u2e9b\u2e9c\u2e9d\u2e9e\u2e9f\u2ea0\u2ea1\u2ea2\u2ea3\u2ea4\u2ea5\u2ea6\u2ea7\u2ea8\u2ea9\u2eaa\u2eab\u2eac\u2ead\u2eae\u2eaf\u2eb0\u2eb1\u2eb2\u2eb3\u2eb4\u2eb5\u2eb6\u2eb7\u2eb8\u2eb9\u2eba\u2ebb\u2ebc\u2ebd\u2ebe\u2ebf\u2ec0\u2ec1\u2ec2\u2ec3\u2ec4\u2ec5\u2ec6\u2ec7\u2ec8\u2ec9\u2eca\u2ecb\u2ecc\u2ecd\u2ece\u2ecf\u2ed0\u2ed1\u2ed2\u2ed3\u2ed4\u2ed5\u2ed6\u2ed7\u2ed8\u2ed9\u2eda\u2edb\u2edc\u2edd\u2ede\u2edf\u2ee0\u2ee1\u2ee2\u2ee3\u2ee4\u2ee5\u2ee6\u2ee7\u2ee8\u2ee9\u2eea\u2eeb\u2eec\u2eed\u2eee\u2eef\u2ef0\u2ef1\u2ef2\u2ef3\u2f00\u2f01\u2f02\u2f03\u2f04\u2f05\u2f06\u2f07\u2f08\u2f09\u2f0a\u2f0b\u2f0c\u2f0d\u2f0e\u2f0f\u2f10\u2f11\u2f12\u2f13\u2f14\u2f15\u2f16\u2f17\u2f18\u2f19\u2f1a\u2f1b\u2f1c\u2f1d\u2f1e\u2f1f\u2f20\u2f21\u2f22\u2f23\u2f24\u2f25\u2f26\u2f27\u2f28\u2f29\u2f2a\u2f2b\u2f2c\u2f2d\u2f2e\u2f2f\u2f30\u2f31\u2f32\u2f33\u2f34\u2f35\u2f36\u2f37\u2f38\u2f39\u2f3a\u2f3b\u2f3c\u2f3d\u2f3e\u2f3f\u2f40\u2f41\u2f42\u2f43\u2f44\u2f45\u2f46\u2f47\u2f48\u2f49\u2f4a\u2f4b\u2f4c\u2f4d\u2f4e\u2f4f\u2f50\u2f51\u2f52\u2f53\u2f54\u2f55\u2f56\u2f57\u2f58\u2f59\u2f5a\u2f5b\u2f5c\u2f5d\u2f5e\u2f5f\u2f60\u2f61\u2f62\u2f63\u2f64\u2f65\u2f66\u2f67\u2f68\u2f69\u2f6a\u2f6b\u2f6c\u2f6d\u2f6e\u2f6f\u2f70\u2f71\u2f72\u2f73\u2f74\u2f75\u2f76\u2f77\u2f78\u2f79\u2f7a\u2f7b\u2f7c\u2f7d\u2f7e\u2f7f\u2f80\u2f81\u2f82\u2f83\u2f84\u2f85\u2f86\u2f87\u2f88\u2f89\u2f8a\u2f8b\u2f8c\u2f8d\u2f8e\u2f8f\u2f90\u2f91\u2f92\u2f93\u2f94\u2f95\u2f96\u2f97\u2f98\u2f99\u2f9a\u2f9b\u2f9c\u2f9d\u2f9e\u2f9f\u2fa0\u2fa1\u2fa2\u2fa3\u2fa4\u2fa5\u2fa6\u2fa7\u2fa8\u2fa9\u2faa\u2fab\u2fac\u2fad\u2fae\u2faf\u2fb0\u2fb1\u2fb2\u2fb3\u2fb4\u2fb5\u2fb6\u2fb7\u2fb8\u2fb9\u2fba\u2fbb\u2fbc\u2fbd\u2fbe\u2fbf\u2fc0\u2fc1\u2fc2\u2fc3\u2fc4\u2fc5\u2fc6\u2fc7\u2fc8\u2fc9\u2fca\u2fcb\u2fcc\u2fcd\u2fce\u2fcf\u2fd0\u2fd1\u2fd2\u2fd3\u2fd4\u2fd5\u2ff0\u2ff1\u2ff2\u2ff3\u2ff4\u2ff5\u2ff6\u2ff7\u2ff8\u2ff9\u2ffa\u2ffb\u3004\u3012\u3013\u3020\u3036\u3037\u303e\u303f\u3190\u3191\u3196\u3197\u3198\u3199\u319a\u319b\u319c\u319d\u319e\u319f\u31c0\u31c1\u31c2\u31c3\u31c4\u31c5\u31c6\u31c7\u31c8\u31c9\u31ca\u31cb\u31cc\u31cd\u31ce\u31cf\u3200\u3201\u3202\u3203\u3204\u3205\u3206\u3207\u3208\u3209\u320a\u320b\u320c\u320d\u320e\u320f\u3210\u3211\u3212\u3213\u3214\u3215\u3216\u3217\u3218\u3219\u321a\u321b\u321c\u321d\u321e\u322a\u322b\u322c\u322d\u322e\u322f\u3230\u3231\u3232\u3233\u3234\u3235\u3236\u3237\u3238\u3239\u323a\u323b\u323c\u323d\u323e\u323f\u3240\u3241\u3242\u3243\u3250\u3260\u3261\u3262\u3263\u3264\u3265\u3266\u3267\u3268\u3269\u326a\u326b\u326c\u326d\u326e\u326f\u3270\u3271\u3272\u3273\u3274\u3275\u3276\u3277\u3278\u3279\u327a\u327b\u327c\u327d\u327e\u327f\u328a\u328b\u328c\u328d\u328e\u328f\u3290\u3291\u3292\u3293\u3294\u3295\u3296\u3297\u3298\u3299\u329a\u329b\u329c\u329d\u329e\u329f\u32a0\u32a1\u32a2\u32a3\u32a4\u32a5\u32a6\u32a7\u32a8\u32a9\u32aa\u32ab\u32ac\u32ad\u32ae\u32af\u32b0\u32c0\u32c1\u32c2\u32c3\u32c4\u32c5\u32c6\u32c7\u32c8\u32c9\u32ca\u32cb\u32cc\u32cd\u32ce\u32cf\u32d0\u32d1\u32d2\u32d3\u32d4\u32d5\u32d6\u32d7\u32d8\u32d9\u32da\u32db\u32dc\u32dd\u32de\u32df\u32e0\u32e1\u32e2\u32e3\u32e4\u32e5\u32e6\u32e7\u32e8\u32e9\u32ea\u32eb\u32ec\u32ed\u32ee\u32ef\u32f0\u32f1\u32f2\u32f3\u32f4\u32f5\u32f6\u32f7\u32f8\u32f9\u32fa\u32fb\u32fc\u32fd\u32fe\u3300\u3301\u3302\u3303\u3304\u3305\u3306\u3307\u3308\u3309\u330a\u330b\u330c\u330d\u330e\u330f\u3310\u3311\u3312\u3313\u3314\u3315\u3316\u3317\u3318\u3319\u331a\u331b\u331c\u331d\u331e\u331f\u3320\u3321\u3322\u3323\u3324\u3325\u3326\u3327\u3328\u3329\u332a\u332b\u332c\u332d\u332e\u332f\u3330\u3331\u3332\u3333\u3334\u3335\u3336\u3337\u3338\u3339\u333a\u333b\u333c\u333d\u333e\u333f\u3340\u3341\u3342\u3343\u3344\u3345\u3346\u3347\u3348\u3349\u334a\u334b\u334c\u334d\u334e\u334f\u3350\u3351\u3352\u3353\u3354\u3355\u3356\u3357\u3358\u3359\u335a\u335b\u335c\u335d\u335e\u335f\u3360\u3361\u3362\u3363\u3364\u3365\u3366\u3367\u3368\u3369\u336a\u336b\u336c\u336d\u336e\u336f\u3370\u3371\u3372\u3373\u3374\u3375\u3376\u3377\u3378\u3379\u337a\u337b\u337c\u337d\u337e\u337f\u3380\u3381\u3382\u3383\u3384\u3385\u3386\u3387\u3388\u3389\u338a\u338b\u338c\u338d\u338e\u338f\u3390\u3391\u3392\u3393\u3394\u3395\u3396\u3397\u3398\u3399\u339a\u339b\u339c\u339d\u339e\u339f\u33a0\u33a1\u33a2\u33a3\u33a4\u33a5\u33a6\u33a7\u33a8\u33a9\u33aa\u33ab\u33ac\u33ad\u33ae\u33af\u33b0\u33b1\u33b2\u33b3\u33b4\u33b5\u33b6\u33b7\u33b8\u33b9\u33ba\u33bb\u33bc\u33bd\u33be\u33bf\u33c0\u33c1\u33c2\u33c3\u33c4\u33c5\u33c6\u33c7\u33c8\u33c9\u33ca\u33cb\u33cc\u33cd\u33ce\u33cf\u33d0\u33d1\u33d2\u33d3\u33d4\u33d5\u33d6\u33d7\u33d8\u33d9\u33da\u33db\u33dc\u33dd\u33de\u33df\u33e0\u33e1\u33e2\u33e3\u33e4\u33e5\u33e6\u33e7\u33e8\u33e9\u33ea\u33eb\u33ec\u33ed\u33ee\u33ef\u33f0\u33f1\u33f2\u33f3\u33f4\u33f5\u33f6\u33f7\u33f8\u33f9\u33fa\u33fb\u33fc\u33fd\u33fe\u33ff\u4dc0\u4dc1\u4dc2\u4dc3\u4dc4\u4dc5\u4dc6\u4dc7\u4dc8\u4dc9\u4dca\u4dcb\u4dcc\u4dcd\u4dce\u4dcf\u4dd0\u4dd1\u4dd2\u4dd3\u4dd4\u4dd5\u4dd6\u4dd7\u4dd8\u4dd9\u4dda\u4ddb\u4ddc\u4ddd\u4dde\u4ddf\u4de0\u4de1\u4de2\u4de3\u4de4\u4de5\u4de6\u4de7\u4de8\u4de9\u4dea\u4deb\u4dec\u4ded\u4dee\u4def\u4df0\u4df1\u4df2\u4df3\u4df4\u4df5\u4df6\u4df7\u4df8\u4df9\u4dfa\u4dfb\u4dfc\u4dfd\u4dfe\u4dff\ua490\ua491\ua492\ua493\ua494\ua495\ua496\ua497\ua498\ua499\ua49a\ua49b\ua49c\ua49d\ua49e\ua49f\ua4a0\ua4a1\ua4a2\ua4a3\ua4a4\ua4a5\ua4a6\ua4a7\ua4a8\ua4a9\ua4aa\ua4ab\ua4ac\ua4ad\ua4ae\ua4af\ua4b0\ua4b1\ua4b2\ua4b3\ua4b4\ua4b5\ua4b6\ua4b7\ua4b8\ua4b9\ua4ba\ua4bb\ua4bc\ua4bd\ua4be\ua4bf\ua4c0\ua4c1\ua4c2\ua4c3\ua4c4\ua4c5\ua4c6\ua828\ua829\ua82a\ua82b\ufdfd\uffe4\uffe8\uffed\uffee\ufffc\ufffd' + +Zl = u'\u2028' + +Zp = u'\u2029' + +Zs = u' \xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000' + +cats = ['Cc', 'Cf', 'Cn', 'Co', 'Cs', 'Ll', 'Lm', 'Lo', 'Lt', 'Lu', 'Mc', 'Me', 'Mn', 'Nd', 'Nl', 'No', 'Pc', 'Pd', 'Pe', 'Pf', 'Pi', 'Po', 'Ps', 'Sc', 'Sk', 'Sm', 'So', 'Zl', 'Zp', 'Zs'] + +def combine(*args): + return u''.join([globals()[cat] for cat in args]) + +xid_start = u'\u0041-\u005A\u005F\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u01BA\u01BB\u01BC-\u01BF\u01C0-\u01C3\u01C4-\u0241\u0250-\u02AF\u02B0-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EE\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03F5\u03F7-\u0481\u048A-\u04CE\u04D0-\u04F9\u0500-\u050F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0621-\u063A\u0640\u0641-\u064A\u066E-\u066F\u0671-\u06D3\u06D5\u06E5-\u06E6\u06EE-\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u076D\u0780-\u07A5\u07B1\u0904-\u0939\u093D\u0950\u0958-\u0961\u097D\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0-\u0AE1\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B35-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C60-\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0-\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D60-\u0D61\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E40-\u0E45\u0E46\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EB0\u0EB2\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDD\u0F00\u0F40-\u0F47\u0F49-\u0F6A\u0F88-\u0F8B\u1000-\u1021\u1023-\u1027\u1029-\u102A\u1050-\u1055\u10A0-\u10C5\u10D0-\u10FA\u10FC\u1100-\u1159\u115F-\u11A2\u11A8-\u11F9\u1200-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u1676\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1842\u1843\u1844-\u1877\u1880-\u18A8\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19A9\u19C1-\u19C7\u1A00-\u1A16\u1D00-\u1D2B\u1D2C-\u1D61\u1D62-\u1D77\u1D78\u1D79-\u1D9A\u1D9B-\u1DBF\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u2094\u2102\u2107\u210A-\u2113\u2115\u2118\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212E\u212F-\u2131\u2133-\u2134\u2135-\u2138\u2139\u213C-\u213F\u2145-\u2149\u2160-\u2183\u2C00-\u2C2E\u2C30-\u2C5E\u2C80-\u2CE4\u2D00-\u2D25\u2D30-\u2D65\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005\u3006\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303A\u303B\u303C\u3041-\u3096\u309D-\u309E\u309F\u30A1-\u30FA\u30FC-\u30FE\u30FF\u3105-\u312C\u3131-\u318E\u31A0-\u31B7\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FBB\uA000-\uA014\uA015\uA016-\uA48C\uA800-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uAC00-\uD7A3\uF900-\uFA2D\uFA30-\uFA6A\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40-\uFB41\uFB43-\uFB44\uFB46-\uFBB1\uFBD3-\uFC5D\uFC64-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDF9\uFE71\uFE73\uFE77\uFE79\uFE7B\uFE7D\uFE7F-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFF6F\uFF70\uFF71-\uFF9D\uFFA0-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC' + +xid_continue = u'\u0030-\u0039\u0041-\u005A\u005F\u0061-\u007A\u00AA\u00B5\u00B7\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u01BA\u01BB\u01BC-\u01BF\u01C0-\u01C3\u01C4-\u0241\u0250-\u02AF\u02B0-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EE\u0300-\u036F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03F5\u03F7-\u0481\u0483-\u0486\u048A-\u04CE\u04D0-\u04F9\u0500-\u050F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05B9\u05BB-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u0615\u0621-\u063A\u0640\u0641-\u064A\u064B-\u065E\u0660-\u0669\u066E-\u066F\u0670\u0671-\u06D3\u06D5\u06D6-\u06DC\u06DF-\u06E4\u06E5-\u06E6\u06E7-\u06E8\u06EA-\u06ED\u06EE-\u06EF\u06F0-\u06F9\u06FA-\u06FC\u06FF\u0710\u0711\u0712-\u072F\u0730-\u074A\u074D-\u076D\u0780-\u07A5\u07A6-\u07B0\u07B1\u0901-\u0902\u0903\u0904-\u0939\u093C\u093D\u093E-\u0940\u0941-\u0948\u0949-\u094C\u094D\u0950\u0951-\u0954\u0958-\u0961\u0962-\u0963\u0966-\u096F\u097D\u0981\u0982-\u0983\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC\u09BD\u09BE-\u09C0\u09C1-\u09C4\u09C7-\u09C8\u09CB-\u09CC\u09CD\u09CE\u09D7\u09DC-\u09DD\u09DF-\u09E1\u09E2-\u09E3\u09E6-\u09EF\u09F0-\u09F1\u0A01-\u0A02\u0A03\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A3C\u0A3E-\u0A40\u0A41-\u0A42\u0A47-\u0A48\u0A4B-\u0A4D\u0A59-\u0A5C\u0A5E\u0A66-\u0A6F\u0A70-\u0A71\u0A72-\u0A74\u0A81-\u0A82\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABC\u0ABD\u0ABE-\u0AC0\u0AC1-\u0AC5\u0AC7-\u0AC8\u0AC9\u0ACB-\u0ACC\u0ACD\u0AD0\u0AE0-\u0AE1\u0AE2-\u0AE3\u0AE6-\u0AEF\u0B01\u0B02-\u0B03\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B35-\u0B39\u0B3C\u0B3D\u0B3E\u0B3F\u0B40\u0B41-\u0B43\u0B47-\u0B48\u0B4B-\u0B4C\u0B4D\u0B56\u0B57\u0B5C-\u0B5D\u0B5F-\u0B61\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BBF\u0BC0\u0BC1-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCC\u0BCD\u0BD7\u0BE6-\u0BEF\u0C01-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3E-\u0C40\u0C41-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55-\u0C56\u0C60-\u0C61\u0C66-\u0C6F\u0C82-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC\u0CBD\u0CBE\u0CBF\u0CC0-\u0CC4\u0CC6\u0CC7-\u0CC8\u0CCA-\u0CCB\u0CCC-\u0CCD\u0CD5-\u0CD6\u0CDE\u0CE0-\u0CE1\u0CE6-\u0CEF\u0D02-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D3E-\u0D40\u0D41-\u0D43\u0D46-\u0D48\u0D4A-\u0D4C\u0D4D\u0D57\u0D60-\u0D61\u0D66-\u0D6F\u0D82-\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD1\u0DD2-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2-\u0DF3\u0E01-\u0E30\u0E31\u0E32-\u0E33\u0E34-\u0E3A\u0E40-\u0E45\u0E46\u0E47-\u0E4E\u0E50-\u0E59\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EB0\u0EB1\u0EB2-\u0EB3\u0EB4-\u0EB9\u0EBB-\u0EBC\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDD\u0F00\u0F18-\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F3F\u0F40-\u0F47\u0F49-\u0F6A\u0F71-\u0F7E\u0F7F\u0F80-\u0F84\u0F86-\u0F87\u0F88-\u0F8B\u0F90-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1021\u1023-\u1027\u1029-\u102A\u102C\u102D-\u1030\u1031\u1032\u1036-\u1037\u1038\u1039\u1040-\u1049\u1050-\u1055\u1056-\u1057\u1058-\u1059\u10A0-\u10C5\u10D0-\u10FA\u10FC\u1100-\u1159\u115F-\u11A2\u11A8-\u11F9\u1200-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u1676\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1711\u1712-\u1714\u1720-\u1731\u1732-\u1734\u1740-\u1751\u1752-\u1753\u1760-\u176C\u176E-\u1770\u1772-\u1773\u1780-\u17B3\u17B6\u17B7-\u17BD\u17BE-\u17C5\u17C6\u17C7-\u17C8\u17C9-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1842\u1843\u1844-\u1877\u1880-\u18A8\u18A9\u1900-\u191C\u1920-\u1922\u1923-\u1926\u1927-\u1928\u1929-\u192B\u1930-\u1931\u1932\u1933-\u1938\u1939-\u193B\u1946-\u194F\u1950-\u196D\u1970-\u1974\u1980-\u19A9\u19B0-\u19C0\u19C1-\u19C7\u19C8-\u19C9\u19D0-\u19D9\u1A00-\u1A16\u1A17-\u1A18\u1A19-\u1A1B\u1D00-\u1D2B\u1D2C-\u1D61\u1D62-\u1D77\u1D78\u1D79-\u1D9A\u1D9B-\u1DBF\u1DC0-\u1DC3\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u203F-\u2040\u2054\u2071\u207F\u2090-\u2094\u20D0-\u20DC\u20E1\u20E5-\u20EB\u2102\u2107\u210A-\u2113\u2115\u2118\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212E\u212F-\u2131\u2133-\u2134\u2135-\u2138\u2139\u213C-\u213F\u2145-\u2149\u2160-\u2183\u2C00-\u2C2E\u2C30-\u2C5E\u2C80-\u2CE4\u2D00-\u2D25\u2D30-\u2D65\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005\u3006\u3007\u3021-\u3029\u302A-\u302F\u3031-\u3035\u3038-\u303A\u303B\u303C\u3041-\u3096\u3099-\u309A\u309D-\u309E\u309F\u30A1-\u30FA\u30FC-\u30FE\u30FF\u3105-\u312C\u3131-\u318E\u31A0-\u31B7\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FBB\uA000-\uA014\uA015\uA016-\uA48C\uA800-\uA801\uA802\uA803-\uA805\uA806\uA807-\uA80A\uA80B\uA80C-\uA822\uA823-\uA824\uA825-\uA826\uA827\uAC00-\uD7A3\uF900-\uFA2D\uFA30-\uFA6A\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1E\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40-\uFB41\uFB43-\uFB44\uFB46-\uFBB1\uFBD3-\uFC5D\uFC64-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDF9\uFE00-\uFE0F\uFE20-\uFE23\uFE33-\uFE34\uFE4D-\uFE4F\uFE71\uFE73\uFE77\uFE79\uFE7B\uFE7D\uFE7F-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFF6F\uFF70\uFF71-\uFF9D\uFF9E-\uFF9F\uFFA0-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC' + +def allexcept(*args): + newcats = cats[:] + for arg in args: + newcats.remove(arg) + return u''.join([globals()[cat] for cat in newcats]) + +if __name__ == '__main__': + import unicodedata + + categories = {} + + f = open(__file__.rstrip('co')) + try: + content = f.read() + finally: + f.close() + + header = content[:content.find('Cc =')] + footer = content[content.find("def combine("):] + + for code in range(65535): + c = unichr(code) + cat = unicodedata.category(c) + categories.setdefault(cat, []).append(c) + + f = open(__file__, 'w') + f.write(header) + + for cat in sorted(categories): + val = u''.join(categories[cat]) + if cat == 'Cs': + # Jython can't handle isolated surrogates + f.write("""\ +try: + Cs = eval(r"%r") +except UnicodeDecodeError: + Cs = '' # Jython can't handle isolated surrogates\n\n""" % val) + else: + f.write('%s = %r\n\n' % (cat, val)) + f.write('cats = %r\n\n' % sorted(categories.keys())) + + f.write(footer) + f.close() diff --git a/deps/v8_inspector/deps/jinja2/jinja2/bccache.py b/deps/v8_inspector/deps/jinja2/jinja2/bccache.py new file mode 100644 index 00000000000000..f5bd3145f6b9ff --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/bccache.py @@ -0,0 +1,362 @@ +# -*- coding: utf-8 -*- +""" + jinja2.bccache + ~~~~~~~~~~~~~~ + + This module implements the bytecode cache system Jinja is optionally + using. This is useful if you have very complex template situations and + the compiliation of all those templates slow down your application too + much. + + Situations where this is useful are often forking web applications that + are initialized on the first request. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD. +""" +from os import path, listdir +import os +import sys +import stat +import errno +import marshal +import tempfile +import fnmatch +from hashlib import sha1 +from jinja2.utils import open_if_exists +from jinja2._compat import BytesIO, pickle, PY2, text_type + + +# marshal works better on 3.x, one hack less required +if not PY2: + marshal_dump = marshal.dump + marshal_load = marshal.load +else: + + def marshal_dump(code, f): + if isinstance(f, file): + marshal.dump(code, f) + else: + f.write(marshal.dumps(code)) + + def marshal_load(f): + if isinstance(f, file): + return marshal.load(f) + return marshal.loads(f.read()) + + +bc_version = 2 + +# magic version used to only change with new jinja versions. With 2.6 +# we change this to also take Python version changes into account. The +# reason for this is that Python tends to segfault if fed earlier bytecode +# versions because someone thought it would be a good idea to reuse opcodes +# or make Python incompatible with earlier versions. +bc_magic = 'j2'.encode('ascii') + \ + pickle.dumps(bc_version, 2) + \ + pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1]) + + +class Bucket(object): + """Buckets are used to store the bytecode for one template. It's created + and initialized by the bytecode cache and passed to the loading functions. + + The buckets get an internal checksum from the cache assigned and use this + to automatically reject outdated cache material. Individual bytecode + cache subclasses don't have to care about cache invalidation. + """ + + def __init__(self, environment, key, checksum): + self.environment = environment + self.key = key + self.checksum = checksum + self.reset() + + def reset(self): + """Resets the bucket (unloads the bytecode).""" + self.code = None + + def load_bytecode(self, f): + """Loads bytecode from a file or file like object.""" + # make sure the magic header is correct + magic = f.read(len(bc_magic)) + if magic != bc_magic: + self.reset() + return + # the source code of the file changed, we need to reload + checksum = pickle.load(f) + if self.checksum != checksum: + self.reset() + return + # if marshal_load fails then we need to reload + try: + self.code = marshal_load(f) + except (EOFError, ValueError, TypeError): + self.reset() + return + + def write_bytecode(self, f): + """Dump the bytecode into the file or file like object passed.""" + if self.code is None: + raise TypeError('can\'t write empty bucket') + f.write(bc_magic) + pickle.dump(self.checksum, f, 2) + marshal_dump(self.code, f) + + def bytecode_from_string(self, string): + """Load bytecode from a string.""" + self.load_bytecode(BytesIO(string)) + + def bytecode_to_string(self): + """Return the bytecode as string.""" + out = BytesIO() + self.write_bytecode(out) + return out.getvalue() + + +class BytecodeCache(object): + """To implement your own bytecode cache you have to subclass this class + and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of + these methods are passed a :class:`~jinja2.bccache.Bucket`. + + A very basic bytecode cache that saves the bytecode on the file system:: + + from os import path + + class MyCache(BytecodeCache): + + def __init__(self, directory): + self.directory = directory + + def load_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + if path.exists(filename): + with open(filename, 'rb') as f: + bucket.load_bytecode(f) + + def dump_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + with open(filename, 'wb') as f: + bucket.write_bytecode(f) + + A more advanced version of a filesystem based bytecode cache is part of + Jinja2. + """ + + def load_bytecode(self, bucket): + """Subclasses have to override this method to load bytecode into a + bucket. If they are not able to find code in the cache for the + bucket, it must not do anything. + """ + raise NotImplementedError() + + def dump_bytecode(self, bucket): + """Subclasses have to override this method to write the bytecode + from a bucket back to the cache. If it unable to do so it must not + fail silently but raise an exception. + """ + raise NotImplementedError() + + def clear(self): + """Clears the cache. This method is not used by Jinja2 but should be + implemented to allow applications to clear the bytecode cache used + by a particular environment. + """ + + def get_cache_key(self, name, filename=None): + """Returns the unique hash key for this template name.""" + hash = sha1(name.encode('utf-8')) + if filename is not None: + filename = '|' + filename + if isinstance(filename, text_type): + filename = filename.encode('utf-8') + hash.update(filename) + return hash.hexdigest() + + def get_source_checksum(self, source): + """Returns a checksum for the source.""" + return sha1(source.encode('utf-8')).hexdigest() + + def get_bucket(self, environment, name, filename, source): + """Return a cache bucket for the given template. All arguments are + mandatory but filename may be `None`. + """ + key = self.get_cache_key(name, filename) + checksum = self.get_source_checksum(source) + bucket = Bucket(environment, key, checksum) + self.load_bytecode(bucket) + return bucket + + def set_bucket(self, bucket): + """Put the bucket into the cache.""" + self.dump_bytecode(bucket) + + +class FileSystemBytecodeCache(BytecodeCache): + """A bytecode cache that stores bytecode on the filesystem. It accepts + two arguments: The directory where the cache items are stored and a + pattern string that is used to build the filename. + + If no directory is specified a default cache directory is selected. On + Windows the user's temp directory is used, on UNIX systems a directory + is created for the user in the system temp directory. + + The pattern can be used to have multiple separate caches operate on the + same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s`` + is replaced with the cache key. + + >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache') + + This bytecode cache supports clearing of the cache using the clear method. + """ + + def __init__(self, directory=None, pattern='__jinja2_%s.cache'): + if directory is None: + directory = self._get_default_cache_dir() + self.directory = directory + self.pattern = pattern + + def _get_default_cache_dir(self): + def _unsafe_dir(): + raise RuntimeError('Cannot determine safe temp directory. You ' + 'need to explicitly provide one.') + + tmpdir = tempfile.gettempdir() + + # On windows the temporary directory is used specific unless + # explicitly forced otherwise. We can just use that. + if os.name == 'nt': + return tmpdir + if not hasattr(os, 'getuid'): + _unsafe_dir() + + dirname = '_jinja2-cache-%d' % os.getuid() + actual_dir = os.path.join(tmpdir, dirname) + + try: + os.mkdir(actual_dir, stat.S_IRWXU) + except OSError as e: + if e.errno != errno.EEXIST: + raise + try: + os.chmod(actual_dir, stat.S_IRWXU) + actual_dir_stat = os.lstat(actual_dir) + if actual_dir_stat.st_uid != os.getuid() \ + or not stat.S_ISDIR(actual_dir_stat.st_mode) \ + or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU: + _unsafe_dir() + except OSError as e: + if e.errno != errno.EEXIST: + raise + + actual_dir_stat = os.lstat(actual_dir) + if actual_dir_stat.st_uid != os.getuid() \ + or not stat.S_ISDIR(actual_dir_stat.st_mode) \ + or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU: + _unsafe_dir() + + return actual_dir + + def _get_cache_filename(self, bucket): + return path.join(self.directory, self.pattern % bucket.key) + + def load_bytecode(self, bucket): + f = open_if_exists(self._get_cache_filename(bucket), 'rb') + if f is not None: + try: + bucket.load_bytecode(f) + finally: + f.close() + + def dump_bytecode(self, bucket): + f = open(self._get_cache_filename(bucket), 'wb') + try: + bucket.write_bytecode(f) + finally: + f.close() + + def clear(self): + # imported lazily here because google app-engine doesn't support + # write access on the file system and the function does not exist + # normally. + from os import remove + files = fnmatch.filter(listdir(self.directory), self.pattern % '*') + for filename in files: + try: + remove(path.join(self.directory, filename)) + except OSError: + pass + + +class MemcachedBytecodeCache(BytecodeCache): + """This class implements a bytecode cache that uses a memcache cache for + storing the information. It does not enforce a specific memcache library + (tummy's memcache or cmemcache) but will accept any class that provides + the minimal interface required. + + Libraries compatible with this class: + + - `werkzeug <http://werkzeug.pocoo.org/>`_.contrib.cache + - `python-memcached <http://www.tummy.com/Community/software/python-memcached/>`_ + - `cmemcache <http://gijsbert.org/cmemcache/>`_ + + (Unfortunately the django cache interface is not compatible because it + does not support storing binary data, only unicode. You can however pass + the underlying cache client to the bytecode cache which is available + as `django.core.cache.cache._client`.) + + The minimal interface for the client passed to the constructor is this: + + .. class:: MinimalClientInterface + + .. method:: set(key, value[, timeout]) + + Stores the bytecode in the cache. `value` is a string and + `timeout` the timeout of the key. If timeout is not provided + a default timeout or no timeout should be assumed, if it's + provided it's an integer with the number of seconds the cache + item should exist. + + .. method:: get(key) + + Returns the value for the cache key. If the item does not + exist in the cache the return value must be `None`. + + The other arguments to the constructor are the prefix for all keys that + is added before the actual cache key and the timeout for the bytecode in + the cache system. We recommend a high (or no) timeout. + + This bytecode cache does not support clearing of used items in the cache. + The clear method is a no-operation function. + + .. versionadded:: 2.7 + Added support for ignoring memcache errors through the + `ignore_memcache_errors` parameter. + """ + + def __init__(self, client, prefix='jinja2/bytecode/', timeout=None, + ignore_memcache_errors=True): + self.client = client + self.prefix = prefix + self.timeout = timeout + self.ignore_memcache_errors = ignore_memcache_errors + + def load_bytecode(self, bucket): + try: + code = self.client.get(self.prefix + bucket.key) + except Exception: + if not self.ignore_memcache_errors: + raise + code = None + if code is not None: + bucket.bytecode_from_string(code) + + def dump_bytecode(self, bucket): + args = (self.prefix + bucket.key, bucket.bytecode_to_string()) + if self.timeout is not None: + args += (self.timeout,) + try: + self.client.set(*args) + except Exception: + if not self.ignore_memcache_errors: + raise diff --git a/deps/v8_inspector/deps/jinja2/jinja2/compiler.py b/deps/v8_inspector/deps/jinja2/jinja2/compiler.py new file mode 100644 index 00000000000000..fad007b596f66f --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/compiler.py @@ -0,0 +1,1686 @@ +# -*- coding: utf-8 -*- +""" + jinja2.compiler + ~~~~~~~~~~~~~~~ + + Compiles nodes into python code. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +from itertools import chain +from copy import deepcopy +from keyword import iskeyword as is_python_keyword +from jinja2 import nodes +from jinja2.nodes import EvalContext +from jinja2.visitor import NodeVisitor +from jinja2.exceptions import TemplateAssertionError +from jinja2.utils import Markup, concat, escape +from jinja2._compat import range_type, text_type, string_types, \ + iteritems, NativeStringIO, imap + + +operators = { + 'eq': '==', + 'ne': '!=', + 'gt': '>', + 'gteq': '>=', + 'lt': '<', + 'lteq': '<=', + 'in': 'in', + 'notin': 'not in' +} + +# what method to iterate over items do we want to use for dict iteration +# in generated code? on 2.x let's go with iteritems, on 3.x with items +if hasattr(dict, 'iteritems'): + dict_item_iter = 'iteritems' +else: + dict_item_iter = 'items' + + +# does if 0: dummy(x) get us x into the scope? +def unoptimize_before_dead_code(): + x = 42 + def f(): + if 0: dummy(x) + return f + +# The getattr is necessary for pypy which does not set this attribute if +# no closure is on the function +unoptimize_before_dead_code = bool( + getattr(unoptimize_before_dead_code(), '__closure__', None)) + + +def generate(node, environment, name, filename, stream=None, + defer_init=False): + """Generate the python source for a node tree.""" + if not isinstance(node, nodes.Template): + raise TypeError('Can\'t compile non template nodes') + generator = environment.code_generator_class(environment, name, filename, + stream, defer_init) + generator.visit(node) + if stream is None: + return generator.stream.getvalue() + + +def has_safe_repr(value): + """Does the node have a safe representation?""" + if value is None or value is NotImplemented or value is Ellipsis: + return True + if isinstance(value, (bool, int, float, complex, range_type, + Markup) + string_types): + return True + if isinstance(value, (tuple, list, set, frozenset)): + for item in value: + if not has_safe_repr(item): + return False + return True + elif isinstance(value, dict): + for key, value in iteritems(value): + if not has_safe_repr(key): + return False + if not has_safe_repr(value): + return False + return True + return False + + +def find_undeclared(nodes, names): + """Check if the names passed are accessed undeclared. The return value + is a set of all the undeclared names from the sequence of names found. + """ + visitor = UndeclaredNameVisitor(names) + try: + for node in nodes: + visitor.visit(node) + except VisitorExit: + pass + return visitor.undeclared + + +class Identifiers(object): + """Tracks the status of identifiers in frames.""" + + def __init__(self): + # variables that are known to be declared (probably from outer + # frames or because they are special for the frame) + self.declared = set() + + # undeclared variables from outer scopes + self.outer_undeclared = set() + + # names that are accessed without being explicitly declared by + # this one or any of the outer scopes. Names can appear both in + # declared and undeclared. + self.undeclared = set() + + # names that are declared locally + self.declared_locally = set() + + # names that are declared by parameters + self.declared_parameter = set() + + def add_special(self, name): + """Register a special name like `loop`.""" + self.undeclared.discard(name) + self.declared.add(name) + + def is_declared(self, name): + """Check if a name is declared in this or an outer scope.""" + if name in self.declared_locally or name in self.declared_parameter: + return True + return name in self.declared + + def copy(self): + return deepcopy(self) + + +class Frame(object): + """Holds compile time information for us.""" + + def __init__(self, eval_ctx, parent=None): + self.eval_ctx = eval_ctx + self.identifiers = Identifiers() + + # a toplevel frame is the root + soft frames such as if conditions. + self.toplevel = False + + # the root frame is basically just the outermost frame, so no if + # conditions. This information is used to optimize inheritance + # situations. + self.rootlevel = False + + # in some dynamic inheritance situations the compiler needs to add + # write tests around output statements. + self.require_output_check = parent and parent.require_output_check + + # inside some tags we are using a buffer rather than yield statements. + # this for example affects {% filter %} or {% macro %}. If a frame + # is buffered this variable points to the name of the list used as + # buffer. + self.buffer = None + + # the name of the block we're in, otherwise None. + self.block = parent and parent.block or None + + # a set of actually assigned names + self.assigned_names = set() + + # the parent of this frame + self.parent = parent + + if parent is not None: + self.identifiers.declared.update( + parent.identifiers.declared | + parent.identifiers.declared_parameter | + parent.assigned_names + ) + self.identifiers.outer_undeclared.update( + parent.identifiers.undeclared - + self.identifiers.declared + ) + self.buffer = parent.buffer + + def copy(self): + """Create a copy of the current one.""" + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.identifiers = object.__new__(self.identifiers.__class__) + rv.identifiers.__dict__.update(self.identifiers.__dict__) + return rv + + def inspect(self, nodes): + """Walk the node and check for identifiers. If the scope is hard (eg: + enforce on a python level) overrides from outer scopes are tracked + differently. + """ + visitor = FrameIdentifierVisitor(self.identifiers) + for node in nodes: + visitor.visit(node) + + def find_shadowed(self, extra=()): + """Find all the shadowed names. extra is an iterable of variables + that may be defined with `add_special` which may occour scoped. + """ + i = self.identifiers + return (i.declared | i.outer_undeclared) & \ + (i.declared_locally | i.declared_parameter) | \ + set(x for x in extra if i.is_declared(x)) + + def inner(self): + """Return an inner frame.""" + return Frame(self.eval_ctx, self) + + def soft(self): + """Return a soft frame. A soft frame may not be modified as + standalone thing as it shares the resources with the frame it + was created of, but it's not a rootlevel frame any longer. + """ + rv = self.copy() + rv.rootlevel = False + return rv + + __copy__ = copy + + +class VisitorExit(RuntimeError): + """Exception used by the `UndeclaredNameVisitor` to signal a stop.""" + + +class DependencyFinderVisitor(NodeVisitor): + """A visitor that collects filter and test calls.""" + + def __init__(self): + self.filters = set() + self.tests = set() + + def visit_Filter(self, node): + self.generic_visit(node) + self.filters.add(node.name) + + def visit_Test(self, node): + self.generic_visit(node) + self.tests.add(node.name) + + def visit_Block(self, node): + """Stop visiting at blocks.""" + + +class UndeclaredNameVisitor(NodeVisitor): + """A visitor that checks if a name is accessed without being + declared. This is different from the frame visitor as it will + not stop at closure frames. + """ + + def __init__(self, names): + self.names = set(names) + self.undeclared = set() + + def visit_Name(self, node): + if node.ctx == 'load' and node.name in self.names: + self.undeclared.add(node.name) + if self.undeclared == self.names: + raise VisitorExit() + else: + self.names.discard(node.name) + + def visit_Block(self, node): + """Stop visiting a blocks.""" + + +class FrameIdentifierVisitor(NodeVisitor): + """A visitor for `Frame.inspect`.""" + + def __init__(self, identifiers): + self.identifiers = identifiers + + def visit_Name(self, node): + """All assignments to names go through this function.""" + if node.ctx == 'store': + self.identifiers.declared_locally.add(node.name) + elif node.ctx == 'param': + self.identifiers.declared_parameter.add(node.name) + elif node.ctx == 'load' and not \ + self.identifiers.is_declared(node.name): + self.identifiers.undeclared.add(node.name) + + def visit_If(self, node): + self.visit(node.test) + real_identifiers = self.identifiers + + old_names = real_identifiers.declared_locally | \ + real_identifiers.declared_parameter + + def inner_visit(nodes): + if not nodes: + return set() + self.identifiers = real_identifiers.copy() + for subnode in nodes: + self.visit(subnode) + rv = self.identifiers.declared_locally - old_names + # we have to remember the undeclared variables of this branch + # because we will have to pull them. + real_identifiers.undeclared.update(self.identifiers.undeclared) + self.identifiers = real_identifiers + return rv + + body = inner_visit(node.body) + else_ = inner_visit(node.else_ or ()) + + # the differences between the two branches are also pulled as + # undeclared variables + real_identifiers.undeclared.update(body.symmetric_difference(else_) - + real_identifiers.declared) + + # remember those that are declared. + real_identifiers.declared_locally.update(body | else_) + + def visit_Macro(self, node): + self.identifiers.declared_locally.add(node.name) + + def visit_Import(self, node): + self.generic_visit(node) + self.identifiers.declared_locally.add(node.target) + + def visit_FromImport(self, node): + self.generic_visit(node) + for name in node.names: + if isinstance(name, tuple): + self.identifiers.declared_locally.add(name[1]) + else: + self.identifiers.declared_locally.add(name) + + def visit_Assign(self, node): + """Visit assignments in the correct order.""" + self.visit(node.node) + self.visit(node.target) + + def visit_For(self, node): + """Visiting stops at for blocks. However the block sequence + is visited as part of the outer scope. + """ + self.visit(node.iter) + + def visit_CallBlock(self, node): + self.visit(node.call) + + def visit_FilterBlock(self, node): + self.visit(node.filter) + + def visit_AssignBlock(self, node): + """Stop visiting at block assigns.""" + + def visit_Scope(self, node): + """Stop visiting at scopes.""" + + def visit_Block(self, node): + """Stop visiting at blocks.""" + + +class CompilerExit(Exception): + """Raised if the compiler encountered a situation where it just + doesn't make sense to further process the code. Any block that + raises such an exception is not further processed. + """ + + +class CodeGenerator(NodeVisitor): + + def __init__(self, environment, name, filename, stream=None, + defer_init=False): + if stream is None: + stream = NativeStringIO() + self.environment = environment + self.name = name + self.filename = filename + self.stream = stream + self.created_block_context = False + self.defer_init = defer_init + + # aliases for imports + self.import_aliases = {} + + # a registry for all blocks. Because blocks are moved out + # into the global python scope they are registered here + self.blocks = {} + + # the number of extends statements so far + self.extends_so_far = 0 + + # some templates have a rootlevel extends. In this case we + # can safely assume that we're a child template and do some + # more optimizations. + self.has_known_extends = False + + # the current line number + self.code_lineno = 1 + + # registry of all filters and tests (global, not block local) + self.tests = {} + self.filters = {} + + # the debug information + self.debug_info = [] + self._write_debug_info = None + + # the number of new lines before the next write() + self._new_lines = 0 + + # the line number of the last written statement + self._last_line = 0 + + # true if nothing was written so far. + self._first_write = True + + # used by the `temporary_identifier` method to get new + # unique, temporary identifier + self._last_identifier = 0 + + # the current indentation + self._indentation = 0 + + # -- Various compilation helpers + + def fail(self, msg, lineno): + """Fail with a :exc:`TemplateAssertionError`.""" + raise TemplateAssertionError(msg, lineno, self.name, self.filename) + + def temporary_identifier(self): + """Get a new unique identifier.""" + self._last_identifier += 1 + return 't_%d' % self._last_identifier + + def buffer(self, frame): + """Enable buffering for the frame from that point onwards.""" + frame.buffer = self.temporary_identifier() + self.writeline('%s = []' % frame.buffer) + + def return_buffer_contents(self, frame): + """Return the buffer contents of the frame.""" + if frame.eval_ctx.volatile: + self.writeline('if context.eval_ctx.autoescape:') + self.indent() + self.writeline('return Markup(concat(%s))' % frame.buffer) + self.outdent() + self.writeline('else:') + self.indent() + self.writeline('return concat(%s)' % frame.buffer) + self.outdent() + elif frame.eval_ctx.autoescape: + self.writeline('return Markup(concat(%s))' % frame.buffer) + else: + self.writeline('return concat(%s)' % frame.buffer) + + def indent(self): + """Indent by one.""" + self._indentation += 1 + + def outdent(self, step=1): + """Outdent by step.""" + self._indentation -= step + + def start_write(self, frame, node=None): + """Yield or write into the frame buffer.""" + if frame.buffer is None: + self.writeline('yield ', node) + else: + self.writeline('%s.append(' % frame.buffer, node) + + def end_write(self, frame): + """End the writing process started by `start_write`.""" + if frame.buffer is not None: + self.write(')') + + def simple_write(self, s, frame, node=None): + """Simple shortcut for start_write + write + end_write.""" + self.start_write(frame, node) + self.write(s) + self.end_write(frame) + + def blockvisit(self, nodes, frame): + """Visit a list of nodes as block in a frame. If the current frame + is no buffer a dummy ``if 0: yield None`` is written automatically + unless the force_generator parameter is set to False. + """ + if frame.buffer is None: + self.writeline('if 0: yield None') + else: + self.writeline('pass') + try: + for node in nodes: + self.visit(node, frame) + except CompilerExit: + pass + + def write(self, x): + """Write a string into the output stream.""" + if self._new_lines: + if not self._first_write: + self.stream.write('\n' * self._new_lines) + self.code_lineno += self._new_lines + if self._write_debug_info is not None: + self.debug_info.append((self._write_debug_info, + self.code_lineno)) + self._write_debug_info = None + self._first_write = False + self.stream.write(' ' * self._indentation) + self._new_lines = 0 + self.stream.write(x) + + def writeline(self, x, node=None, extra=0): + """Combination of newline and write.""" + self.newline(node, extra) + self.write(x) + + def newline(self, node=None, extra=0): + """Add one or more newlines before the next write.""" + self._new_lines = max(self._new_lines, 1 + extra) + if node is not None and node.lineno != self._last_line: + self._write_debug_info = node.lineno + self._last_line = node.lineno + + def signature(self, node, frame, extra_kwargs=None): + """Writes a function call to the stream for the current node. + A leading comma is added automatically. The extra keyword + arguments may not include python keywords otherwise a syntax + error could occour. The extra keyword arguments should be given + as python dict. + """ + # if any of the given keyword arguments is a python keyword + # we have to make sure that no invalid call is created. + kwarg_workaround = False + for kwarg in chain((x.key for x in node.kwargs), extra_kwargs or ()): + if is_python_keyword(kwarg): + kwarg_workaround = True + break + + for arg in node.args: + self.write(', ') + self.visit(arg, frame) + + if not kwarg_workaround: + for kwarg in node.kwargs: + self.write(', ') + self.visit(kwarg, frame) + if extra_kwargs is not None: + for key, value in iteritems(extra_kwargs): + self.write(', %s=%s' % (key, value)) + if node.dyn_args: + self.write(', *') + self.visit(node.dyn_args, frame) + + if kwarg_workaround: + if node.dyn_kwargs is not None: + self.write(', **dict({') + else: + self.write(', **{') + for kwarg in node.kwargs: + self.write('%r: ' % kwarg.key) + self.visit(kwarg.value, frame) + self.write(', ') + if extra_kwargs is not None: + for key, value in iteritems(extra_kwargs): + self.write('%r: %s, ' % (key, value)) + if node.dyn_kwargs is not None: + self.write('}, **') + self.visit(node.dyn_kwargs, frame) + self.write(')') + else: + self.write('}') + + elif node.dyn_kwargs is not None: + self.write(', **') + self.visit(node.dyn_kwargs, frame) + + def pull_locals(self, frame): + """Pull all the references identifiers into the local scope.""" + for name in frame.identifiers.undeclared: + self.writeline('l_%s = context.resolve(%r)' % (name, name)) + + def pull_dependencies(self, nodes): + """Pull all the dependencies.""" + visitor = DependencyFinderVisitor() + for node in nodes: + visitor.visit(node) + for dependency in 'filters', 'tests': + mapping = getattr(self, dependency) + for name in getattr(visitor, dependency): + if name not in mapping: + mapping[name] = self.temporary_identifier() + self.writeline('%s = environment.%s[%r]' % + (mapping[name], dependency, name)) + + def unoptimize_scope(self, frame): + """Disable Python optimizations for the frame.""" + # XXX: this is not that nice but it has no real overhead. It + # mainly works because python finds the locals before dead code + # is removed. If that breaks we have to add a dummy function + # that just accepts the arguments and does nothing. + if frame.identifiers.declared: + self.writeline('%sdummy(%s)' % ( + unoptimize_before_dead_code and 'if 0: ' or '', + ', '.join('l_' + name for name in frame.identifiers.declared) + )) + + def push_scope(self, frame, extra_vars=()): + """This function returns all the shadowed variables in a dict + in the form name: alias and will write the required assignments + into the current scope. No indentation takes place. + + This also predefines locally declared variables from the loop + body because under some circumstances it may be the case that + + `extra_vars` is passed to `Frame.find_shadowed`. + """ + aliases = {} + for name in frame.find_shadowed(extra_vars): + aliases[name] = ident = self.temporary_identifier() + self.writeline('%s = l_%s' % (ident, name)) + to_declare = set() + for name in frame.identifiers.declared_locally: + if name not in aliases: + to_declare.add('l_' + name) + if to_declare: + self.writeline(' = '.join(to_declare) + ' = missing') + return aliases + + def pop_scope(self, aliases, frame): + """Restore all aliases and delete unused variables.""" + for name, alias in iteritems(aliases): + self.writeline('l_%s = %s' % (name, alias)) + to_delete = set() + for name in frame.identifiers.declared_locally: + if name not in aliases: + to_delete.add('l_' + name) + if to_delete: + # we cannot use the del statement here because enclosed + # scopes can trigger a SyntaxError: + # a = 42; b = lambda: a; del a + self.writeline(' = '.join(to_delete) + ' = missing') + + def function_scoping(self, node, frame, children=None, + find_special=True): + """In Jinja a few statements require the help of anonymous + functions. Those are currently macros and call blocks and in + the future also recursive loops. As there is currently + technical limitation that doesn't allow reading and writing a + variable in a scope where the initial value is coming from an + outer scope, this function tries to fall back with a common + error message. Additionally the frame passed is modified so + that the argumetns are collected and callers are looked up. + + This will return the modified frame. + """ + # we have to iterate twice over it, make sure that works + if children is None: + children = node.iter_child_nodes() + children = list(children) + func_frame = frame.inner() + func_frame.inspect(children) + + # variables that are undeclared (accessed before declaration) and + # declared locally *and* part of an outside scope raise a template + # assertion error. Reason: we can't generate reasonable code from + # it without aliasing all the variables. + # this could be fixed in Python 3 where we have the nonlocal + # keyword or if we switch to bytecode generation + overridden_closure_vars = ( + func_frame.identifiers.undeclared & + func_frame.identifiers.declared & + (func_frame.identifiers.declared_locally | + func_frame.identifiers.declared_parameter) + ) + if overridden_closure_vars: + self.fail('It\'s not possible to set and access variables ' + 'derived from an outer scope! (affects: %s)' % + ', '.join(sorted(overridden_closure_vars)), node.lineno) + + # remove variables from a closure from the frame's undeclared + # identifiers. + func_frame.identifiers.undeclared -= ( + func_frame.identifiers.undeclared & + func_frame.identifiers.declared + ) + + # no special variables for this scope, abort early + if not find_special: + return func_frame + + func_frame.accesses_kwargs = False + func_frame.accesses_varargs = False + func_frame.accesses_caller = False + func_frame.arguments = args = ['l_' + x.name for x in node.args] + + undeclared = find_undeclared(children, ('caller', 'kwargs', 'varargs')) + + if 'caller' in undeclared: + func_frame.accesses_caller = True + func_frame.identifiers.add_special('caller') + args.append('l_caller') + if 'kwargs' in undeclared: + func_frame.accesses_kwargs = True + func_frame.identifiers.add_special('kwargs') + args.append('l_kwargs') + if 'varargs' in undeclared: + func_frame.accesses_varargs = True + func_frame.identifiers.add_special('varargs') + args.append('l_varargs') + return func_frame + + def macro_body(self, node, frame, children=None): + """Dump the function def of a macro or call block.""" + frame = self.function_scoping(node, frame, children) + # macros are delayed, they never require output checks + frame.require_output_check = False + args = frame.arguments + # XXX: this is an ugly fix for the loop nesting bug + # (tests.test_old_bugs.test_loop_call_bug). This works around + # a identifier nesting problem we have in general. It's just more + # likely to happen in loops which is why we work around it. The + # real solution would be "nonlocal" all the identifiers that are + # leaking into a new python frame and might be used both unassigned + # and assigned. + if 'loop' in frame.identifiers.declared: + args = args + ['l_loop=l_loop'] + self.writeline('def macro(%s):' % ', '.join(args), node) + self.indent() + self.buffer(frame) + self.pull_locals(frame) + self.blockvisit(node.body, frame) + self.return_buffer_contents(frame) + self.outdent() + return frame + + def macro_def(self, node, frame): + """Dump the macro definition for the def created by macro_body.""" + arg_tuple = ', '.join(repr(x.name) for x in node.args) + name = getattr(node, 'name', None) + if len(node.args) == 1: + arg_tuple += ',' + self.write('Macro(environment, macro, %r, (%s), (' % + (name, arg_tuple)) + for arg in node.defaults: + self.visit(arg, frame) + self.write(', ') + self.write('), %r, %r, %r)' % ( + bool(frame.accesses_kwargs), + bool(frame.accesses_varargs), + bool(frame.accesses_caller) + )) + + def position(self, node): + """Return a human readable position for the node.""" + rv = 'line %d' % node.lineno + if self.name is not None: + rv += ' in ' + repr(self.name) + return rv + + # -- Statement Visitors + + def visit_Template(self, node, frame=None): + assert frame is None, 'no root frame allowed' + eval_ctx = EvalContext(self.environment, self.name) + + from jinja2.runtime import __all__ as exported + self.writeline('from __future__ import division') + self.writeline('from jinja2.runtime import ' + ', '.join(exported)) + if not unoptimize_before_dead_code: + self.writeline('dummy = lambda *x: None') + + # if we want a deferred initialization we cannot move the + # environment into a local name + envenv = not self.defer_init and ', environment=environment' or '' + + # do we have an extends tag at all? If not, we can save some + # overhead by just not processing any inheritance code. + have_extends = node.find(nodes.Extends) is not None + + # find all blocks + for block in node.find_all(nodes.Block): + if block.name in self.blocks: + self.fail('block %r defined twice' % block.name, block.lineno) + self.blocks[block.name] = block + + # find all imports and import them + for import_ in node.find_all(nodes.ImportedName): + if import_.importname not in self.import_aliases: + imp = import_.importname + self.import_aliases[imp] = alias = self.temporary_identifier() + if '.' in imp: + module, obj = imp.rsplit('.', 1) + self.writeline('from %s import %s as %s' % + (module, obj, alias)) + else: + self.writeline('import %s as %s' % (imp, alias)) + + # add the load name + self.writeline('name = %r' % self.name) + + # generate the root render function. + self.writeline('def root(context%s):' % envenv, extra=1) + + # process the root + frame = Frame(eval_ctx) + frame.inspect(node.body) + frame.toplevel = frame.rootlevel = True + frame.require_output_check = have_extends and not self.has_known_extends + self.indent() + if have_extends: + self.writeline('parent_template = None') + if 'self' in find_undeclared(node.body, ('self',)): + frame.identifiers.add_special('self') + self.writeline('l_self = TemplateReference(context)') + self.pull_locals(frame) + self.pull_dependencies(node.body) + self.blockvisit(node.body, frame) + self.outdent() + + # make sure that the parent root is called. + if have_extends: + if not self.has_known_extends: + self.indent() + self.writeline('if parent_template is not None:') + self.indent() + self.writeline('for event in parent_template.' + 'root_render_func(context):') + self.indent() + self.writeline('yield event') + self.outdent(2 + (not self.has_known_extends)) + + # at this point we now have the blocks collected and can visit them too. + for name, block in iteritems(self.blocks): + block_frame = Frame(eval_ctx) + block_frame.inspect(block.body) + block_frame.block = name + self.writeline('def block_%s(context%s):' % (name, envenv), + block, 1) + self.indent() + undeclared = find_undeclared(block.body, ('self', 'super')) + if 'self' in undeclared: + block_frame.identifiers.add_special('self') + self.writeline('l_self = TemplateReference(context)') + if 'super' in undeclared: + block_frame.identifiers.add_special('super') + self.writeline('l_super = context.super(%r, ' + 'block_%s)' % (name, name)) + self.pull_locals(block_frame) + self.pull_dependencies(block.body) + self.blockvisit(block.body, block_frame) + self.outdent() + + self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x) + for x in self.blocks), + extra=1) + + # add a function that returns the debug info + self.writeline('debug_info = %r' % '&'.join('%s=%s' % x for x + in self.debug_info)) + + def visit_Block(self, node, frame): + """Call a block and register it for the template.""" + level = 1 + if frame.toplevel: + # if we know that we are a child template, there is no need to + # check if we are one + if self.has_known_extends: + return + if self.extends_so_far > 0: + self.writeline('if parent_template is None:') + self.indent() + level += 1 + context = node.scoped and 'context.derived(locals())' or 'context' + self.writeline('for event in context.blocks[%r][0](%s):' % ( + node.name, context), node) + self.indent() + self.simple_write('event', frame) + self.outdent(level) + + def visit_Extends(self, node, frame): + """Calls the extender.""" + if not frame.toplevel: + self.fail('cannot use extend from a non top-level scope', + node.lineno) + + # if the number of extends statements in general is zero so + # far, we don't have to add a check if something extended + # the template before this one. + if self.extends_so_far > 0: + + # if we have a known extends we just add a template runtime + # error into the generated code. We could catch that at compile + # time too, but i welcome it not to confuse users by throwing the + # same error at different times just "because we can". + if not self.has_known_extends: + self.writeline('if parent_template is not None:') + self.indent() + self.writeline('raise TemplateRuntimeError(%r)' % + 'extended multiple times') + + # if we have a known extends already we don't need that code here + # as we know that the template execution will end here. + if self.has_known_extends: + raise CompilerExit() + else: + self.outdent() + + self.writeline('parent_template = environment.get_template(', node) + self.visit(node.template, frame) + self.write(', %r)' % self.name) + self.writeline('for name, parent_block in parent_template.' + 'blocks.%s():' % dict_item_iter) + self.indent() + self.writeline('context.blocks.setdefault(name, []).' + 'append(parent_block)') + self.outdent() + + # if this extends statement was in the root level we can take + # advantage of that information and simplify the generated code + # in the top level from this point onwards + if frame.rootlevel: + self.has_known_extends = True + + # and now we have one more + self.extends_so_far += 1 + + def visit_Include(self, node, frame): + """Handles includes.""" + if node.with_context: + self.unoptimize_scope(frame) + if node.ignore_missing: + self.writeline('try:') + self.indent() + + func_name = 'get_or_select_template' + if isinstance(node.template, nodes.Const): + if isinstance(node.template.value, string_types): + func_name = 'get_template' + elif isinstance(node.template.value, (tuple, list)): + func_name = 'select_template' + elif isinstance(node.template, (nodes.Tuple, nodes.List)): + func_name = 'select_template' + + self.writeline('template = environment.%s(' % func_name, node) + self.visit(node.template, frame) + self.write(', %r)' % self.name) + if node.ignore_missing: + self.outdent() + self.writeline('except TemplateNotFound:') + self.indent() + self.writeline('pass') + self.outdent() + self.writeline('else:') + self.indent() + + if node.with_context: + self.writeline('for event in template.root_render_func(' + 'template.new_context(context.parent, True, ' + 'locals())):') + else: + self.writeline('for event in template.module._body_stream:') + + self.indent() + self.simple_write('event', frame) + self.outdent() + + if node.ignore_missing: + self.outdent() + + def visit_Import(self, node, frame): + """Visit regular imports.""" + if node.with_context: + self.unoptimize_scope(frame) + self.writeline('l_%s = ' % node.target, node) + if frame.toplevel: + self.write('context.vars[%r] = ' % node.target) + self.write('environment.get_template(') + self.visit(node.template, frame) + self.write(', %r).' % self.name) + if node.with_context: + self.write('make_module(context.parent, True, locals())') + else: + self.write('module') + if frame.toplevel and not node.target.startswith('_'): + self.writeline('context.exported_vars.discard(%r)' % node.target) + frame.assigned_names.add(node.target) + + def visit_FromImport(self, node, frame): + """Visit named imports.""" + self.newline(node) + self.write('included_template = environment.get_template(') + self.visit(node.template, frame) + self.write(', %r).' % self.name) + if node.with_context: + self.write('make_module(context.parent, True)') + else: + self.write('module') + + var_names = [] + discarded_names = [] + for name in node.names: + if isinstance(name, tuple): + name, alias = name + else: + alias = name + self.writeline('l_%s = getattr(included_template, ' + '%r, missing)' % (alias, name)) + self.writeline('if l_%s is missing:' % alias) + self.indent() + self.writeline('l_%s = environment.undefined(%r %% ' + 'included_template.__name__, ' + 'name=%r)' % + (alias, 'the template %%r (imported on %s) does ' + 'not export the requested name %s' % ( + self.position(node), + repr(name) + ), name)) + self.outdent() + if frame.toplevel: + var_names.append(alias) + if not alias.startswith('_'): + discarded_names.append(alias) + frame.assigned_names.add(alias) + + if var_names: + if len(var_names) == 1: + name = var_names[0] + self.writeline('context.vars[%r] = l_%s' % (name, name)) + else: + self.writeline('context.vars.update({%s})' % ', '.join( + '%r: l_%s' % (name, name) for name in var_names + )) + if discarded_names: + if len(discarded_names) == 1: + self.writeline('context.exported_vars.discard(%r)' % + discarded_names[0]) + else: + self.writeline('context.exported_vars.difference_' + 'update((%s))' % ', '.join(imap(repr, discarded_names))) + + def visit_For(self, node, frame): + # when calculating the nodes for the inner frame we have to exclude + # the iterator contents from it + children = node.iter_child_nodes(exclude=('iter',)) + if node.recursive: + loop_frame = self.function_scoping(node, frame, children, + find_special=False) + else: + loop_frame = frame.inner() + loop_frame.inspect(children) + + # try to figure out if we have an extended loop. An extended loop + # is necessary if the loop is in recursive mode if the special loop + # variable is accessed in the body. + extended_loop = node.recursive or 'loop' in \ + find_undeclared(node.iter_child_nodes( + only=('body',)), ('loop',)) + + # if we don't have an recursive loop we have to find the shadowed + # variables at that point. Because loops can be nested but the loop + # variable is a special one we have to enforce aliasing for it. + if not node.recursive: + aliases = self.push_scope(loop_frame, ('loop',)) + + # otherwise we set up a buffer and add a function def + else: + self.writeline('def loop(reciter, loop_render_func, depth=0):', node) + self.indent() + self.buffer(loop_frame) + aliases = {} + + # make sure the loop variable is a special one and raise a template + # assertion error if a loop tries to write to loop + if extended_loop: + self.writeline('l_loop = missing') + loop_frame.identifiers.add_special('loop') + for name in node.find_all(nodes.Name): + if name.ctx == 'store' and name.name == 'loop': + self.fail('Can\'t assign to special loop variable ' + 'in for-loop target', name.lineno) + + self.pull_locals(loop_frame) + if node.else_: + iteration_indicator = self.temporary_identifier() + self.writeline('%s = 1' % iteration_indicator) + + # Create a fake parent loop if the else or test section of a + # loop is accessing the special loop variable and no parent loop + # exists. + if 'loop' not in aliases and 'loop' in find_undeclared( + node.iter_child_nodes(only=('else_', 'test')), ('loop',)): + self.writeline("l_loop = environment.undefined(%r, name='loop')" % + ("'loop' is undefined. the filter section of a loop as well " + "as the else block don't have access to the special 'loop'" + " variable of the current loop. Because there is no parent " + "loop it's undefined. Happened in loop on %s" % + self.position(node))) + + self.writeline('for ', node) + self.visit(node.target, loop_frame) + self.write(extended_loop and ', l_loop in LoopContext(' or ' in ') + + # if we have an extened loop and a node test, we filter in the + # "outer frame". + if extended_loop and node.test is not None: + self.write('(') + self.visit(node.target, loop_frame) + self.write(' for ') + self.visit(node.target, loop_frame) + self.write(' in ') + if node.recursive: + self.write('reciter') + else: + self.visit(node.iter, loop_frame) + self.write(' if (') + test_frame = loop_frame.copy() + self.visit(node.test, test_frame) + self.write('))') + + elif node.recursive: + self.write('reciter') + else: + self.visit(node.iter, loop_frame) + + if node.recursive: + self.write(', loop_render_func, depth):') + else: + self.write(extended_loop and '):' or ':') + + # tests in not extended loops become a continue + if not extended_loop and node.test is not None: + self.indent() + self.writeline('if not ') + self.visit(node.test, loop_frame) + self.write(':') + self.indent() + self.writeline('continue') + self.outdent(2) + + self.indent() + self.blockvisit(node.body, loop_frame) + if node.else_: + self.writeline('%s = 0' % iteration_indicator) + self.outdent() + + if node.else_: + self.writeline('if %s:' % iteration_indicator) + self.indent() + self.blockvisit(node.else_, loop_frame) + self.outdent() + + # reset the aliases if there are any. + if not node.recursive: + self.pop_scope(aliases, loop_frame) + + # if the node was recursive we have to return the buffer contents + # and start the iteration code + if node.recursive: + self.return_buffer_contents(loop_frame) + self.outdent() + self.start_write(frame, node) + self.write('loop(') + self.visit(node.iter, frame) + self.write(', loop)') + self.end_write(frame) + + def visit_If(self, node, frame): + if_frame = frame.soft() + self.writeline('if ', node) + self.visit(node.test, if_frame) + self.write(':') + self.indent() + self.blockvisit(node.body, if_frame) + self.outdent() + if node.else_: + self.writeline('else:') + self.indent() + self.blockvisit(node.else_, if_frame) + self.outdent() + + def visit_Macro(self, node, frame): + macro_frame = self.macro_body(node, frame) + self.newline() + if frame.toplevel: + if not node.name.startswith('_'): + self.write('context.exported_vars.add(%r)' % node.name) + self.writeline('context.vars[%r] = ' % node.name) + self.write('l_%s = ' % node.name) + self.macro_def(node, macro_frame) + frame.assigned_names.add(node.name) + + def visit_CallBlock(self, node, frame): + children = node.iter_child_nodes(exclude=('call',)) + call_frame = self.macro_body(node, frame, children) + self.writeline('caller = ') + self.macro_def(node, call_frame) + self.start_write(frame, node) + self.visit_Call(node.call, call_frame, forward_caller=True) + self.end_write(frame) + + def visit_FilterBlock(self, node, frame): + filter_frame = frame.inner() + filter_frame.inspect(node.iter_child_nodes()) + aliases = self.push_scope(filter_frame) + self.pull_locals(filter_frame) + self.buffer(filter_frame) + self.blockvisit(node.body, filter_frame) + self.start_write(frame, node) + self.visit_Filter(node.filter, filter_frame) + self.end_write(frame) + self.pop_scope(aliases, filter_frame) + + def visit_ExprStmt(self, node, frame): + self.newline(node) + self.visit(node.node, frame) + + def visit_Output(self, node, frame): + # if we have a known extends statement, we don't output anything + # if we are in a require_output_check section + if self.has_known_extends and frame.require_output_check: + return + + allow_constant_finalize = True + if self.environment.finalize: + func = self.environment.finalize + if getattr(func, 'contextfunction', False) or \ + getattr(func, 'evalcontextfunction', False): + allow_constant_finalize = False + elif getattr(func, 'environmentfunction', False): + finalize = lambda x: text_type( + self.environment.finalize(self.environment, x)) + else: + finalize = lambda x: text_type(self.environment.finalize(x)) + else: + finalize = text_type + + # if we are inside a frame that requires output checking, we do so + outdent_later = False + if frame.require_output_check: + self.writeline('if parent_template is None:') + self.indent() + outdent_later = True + + # try to evaluate as many chunks as possible into a static + # string at compile time. + body = [] + for child in node.nodes: + try: + if not allow_constant_finalize: + raise nodes.Impossible() + const = child.as_const(frame.eval_ctx) + except nodes.Impossible: + body.append(child) + continue + # the frame can't be volatile here, becaus otherwise the + # as_const() function would raise an Impossible exception + # at that point. + try: + if frame.eval_ctx.autoescape: + if hasattr(const, '__html__'): + const = const.__html__() + else: + const = escape(const) + const = finalize(const) + except Exception: + # if something goes wrong here we evaluate the node + # at runtime for easier debugging + body.append(child) + continue + if body and isinstance(body[-1], list): + body[-1].append(const) + else: + body.append([const]) + + # if we have less than 3 nodes or a buffer we yield or extend/append + if len(body) < 3 or frame.buffer is not None: + if frame.buffer is not None: + # for one item we append, for more we extend + if len(body) == 1: + self.writeline('%s.append(' % frame.buffer) + else: + self.writeline('%s.extend((' % frame.buffer) + self.indent() + for item in body: + if isinstance(item, list): + val = repr(concat(item)) + if frame.buffer is None: + self.writeline('yield ' + val) + else: + self.writeline(val + ', ') + else: + if frame.buffer is None: + self.writeline('yield ', item) + else: + self.newline(item) + close = 1 + if frame.eval_ctx.volatile: + self.write('(context.eval_ctx.autoescape and' + ' escape or to_string)(') + elif frame.eval_ctx.autoescape: + self.write('escape(') + else: + self.write('to_string(') + if self.environment.finalize is not None: + self.write('environment.finalize(') + if getattr(self.environment.finalize, + "contextfunction", False): + self.write('context, ') + close += 1 + self.visit(item, frame) + self.write(')' * close) + if frame.buffer is not None: + self.write(', ') + if frame.buffer is not None: + # close the open parentheses + self.outdent() + self.writeline(len(body) == 1 and ')' or '))') + + # otherwise we create a format string as this is faster in that case + else: + format = [] + arguments = [] + for item in body: + if isinstance(item, list): + format.append(concat(item).replace('%', '%%')) + else: + format.append('%s') + arguments.append(item) + self.writeline('yield ') + self.write(repr(concat(format)) + ' % (') + self.indent() + for argument in arguments: + self.newline(argument) + close = 0 + if frame.eval_ctx.volatile: + self.write('(context.eval_ctx.autoescape and' + ' escape or to_string)(') + close += 1 + elif frame.eval_ctx.autoescape: + self.write('escape(') + close += 1 + if self.environment.finalize is not None: + self.write('environment.finalize(') + if getattr(self.environment.finalize, + 'contextfunction', False): + self.write('context, ') + elif getattr(self.environment.finalize, + 'evalcontextfunction', False): + self.write('context.eval_ctx, ') + elif getattr(self.environment.finalize, + 'environmentfunction', False): + self.write('environment, ') + close += 1 + self.visit(argument, frame) + self.write(')' * close + ', ') + self.outdent() + self.writeline(')') + + if outdent_later: + self.outdent() + + def make_assignment_frame(self, frame): + # toplevel assignments however go into the local namespace and + # the current template's context. We create a copy of the frame + # here and add a set so that the Name visitor can add the assigned + # names here. + if not frame.toplevel: + return frame + assignment_frame = frame.copy() + assignment_frame.toplevel_assignments = set() + return assignment_frame + + def export_assigned_vars(self, frame, assignment_frame): + if not frame.toplevel: + return + public_names = [x for x in assignment_frame.toplevel_assignments + if not x.startswith('_')] + if len(assignment_frame.toplevel_assignments) == 1: + name = next(iter(assignment_frame.toplevel_assignments)) + self.writeline('context.vars[%r] = l_%s' % (name, name)) + else: + self.writeline('context.vars.update({') + for idx, name in enumerate(assignment_frame.toplevel_assignments): + if idx: + self.write(', ') + self.write('%r: l_%s' % (name, name)) + self.write('})') + if public_names: + if len(public_names) == 1: + self.writeline('context.exported_vars.add(%r)' % + public_names[0]) + else: + self.writeline('context.exported_vars.update((%s))' % + ', '.join(imap(repr, public_names))) + + def visit_Assign(self, node, frame): + self.newline(node) + assignment_frame = self.make_assignment_frame(frame) + self.visit(node.target, assignment_frame) + self.write(' = ') + self.visit(node.node, frame) + self.export_assigned_vars(frame, assignment_frame) + + def visit_AssignBlock(self, node, frame): + block_frame = frame.inner() + block_frame.inspect(node.body) + aliases = self.push_scope(block_frame) + self.pull_locals(block_frame) + self.buffer(block_frame) + self.blockvisit(node.body, block_frame) + self.pop_scope(aliases, block_frame) + + assignment_frame = self.make_assignment_frame(frame) + self.newline(node) + self.visit(node.target, assignment_frame) + self.write(' = concat(%s)' % block_frame.buffer) + self.export_assigned_vars(frame, assignment_frame) + + # -- Expression Visitors + + def visit_Name(self, node, frame): + if node.ctx == 'store' and frame.toplevel: + frame.toplevel_assignments.add(node.name) + self.write('l_' + node.name) + frame.assigned_names.add(node.name) + + def visit_Const(self, node, frame): + val = node.value + if isinstance(val, float): + self.write(str(val)) + else: + self.write(repr(val)) + + def visit_TemplateData(self, node, frame): + try: + self.write(repr(node.as_const(frame.eval_ctx))) + except nodes.Impossible: + self.write('(context.eval_ctx.autoescape and Markup or identity)(%r)' + % node.data) + + def visit_Tuple(self, node, frame): + self.write('(') + idx = -1 + for idx, item in enumerate(node.items): + if idx: + self.write(', ') + self.visit(item, frame) + self.write(idx == 0 and ',)' or ')') + + def visit_List(self, node, frame): + self.write('[') + for idx, item in enumerate(node.items): + if idx: + self.write(', ') + self.visit(item, frame) + self.write(']') + + def visit_Dict(self, node, frame): + self.write('{') + for idx, item in enumerate(node.items): + if idx: + self.write(', ') + self.visit(item.key, frame) + self.write(': ') + self.visit(item.value, frame) + self.write('}') + + def binop(operator, interceptable=True): + def visitor(self, node, frame): + if self.environment.sandboxed and \ + operator in self.environment.intercepted_binops: + self.write('environment.call_binop(context, %r, ' % operator) + self.visit(node.left, frame) + self.write(', ') + self.visit(node.right, frame) + else: + self.write('(') + self.visit(node.left, frame) + self.write(' %s ' % operator) + self.visit(node.right, frame) + self.write(')') + return visitor + + def uaop(operator, interceptable=True): + def visitor(self, node, frame): + if self.environment.sandboxed and \ + operator in self.environment.intercepted_unops: + self.write('environment.call_unop(context, %r, ' % operator) + self.visit(node.node, frame) + else: + self.write('(' + operator) + self.visit(node.node, frame) + self.write(')') + return visitor + + visit_Add = binop('+') + visit_Sub = binop('-') + visit_Mul = binop('*') + visit_Div = binop('/') + visit_FloorDiv = binop('//') + visit_Pow = binop('**') + visit_Mod = binop('%') + visit_And = binop('and', interceptable=False) + visit_Or = binop('or', interceptable=False) + visit_Pos = uaop('+') + visit_Neg = uaop('-') + visit_Not = uaop('not ', interceptable=False) + del binop, uaop + + def visit_Concat(self, node, frame): + if frame.eval_ctx.volatile: + func_name = '(context.eval_ctx.volatile and' \ + ' markup_join or unicode_join)' + elif frame.eval_ctx.autoescape: + func_name = 'markup_join' + else: + func_name = 'unicode_join' + self.write('%s((' % func_name) + for arg in node.nodes: + self.visit(arg, frame) + self.write(', ') + self.write('))') + + def visit_Compare(self, node, frame): + self.visit(node.expr, frame) + for op in node.ops: + self.visit(op, frame) + + def visit_Operand(self, node, frame): + self.write(' %s ' % operators[node.op]) + self.visit(node.expr, frame) + + def visit_Getattr(self, node, frame): + self.write('environment.getattr(') + self.visit(node.node, frame) + self.write(', %r)' % node.attr) + + def visit_Getitem(self, node, frame): + # slices bypass the environment getitem method. + if isinstance(node.arg, nodes.Slice): + self.visit(node.node, frame) + self.write('[') + self.visit(node.arg, frame) + self.write(']') + else: + self.write('environment.getitem(') + self.visit(node.node, frame) + self.write(', ') + self.visit(node.arg, frame) + self.write(')') + + def visit_Slice(self, node, frame): + if node.start is not None: + self.visit(node.start, frame) + self.write(':') + if node.stop is not None: + self.visit(node.stop, frame) + if node.step is not None: + self.write(':') + self.visit(node.step, frame) + + def visit_Filter(self, node, frame): + self.write(self.filters[node.name] + '(') + func = self.environment.filters.get(node.name) + if func is None: + self.fail('no filter named %r' % node.name, node.lineno) + if getattr(func, 'contextfilter', False): + self.write('context, ') + elif getattr(func, 'evalcontextfilter', False): + self.write('context.eval_ctx, ') + elif getattr(func, 'environmentfilter', False): + self.write('environment, ') + + # if the filter node is None we are inside a filter block + # and want to write to the current buffer + if node.node is not None: + self.visit(node.node, frame) + elif frame.eval_ctx.volatile: + self.write('(context.eval_ctx.autoescape and' + ' Markup(concat(%s)) or concat(%s))' % + (frame.buffer, frame.buffer)) + elif frame.eval_ctx.autoescape: + self.write('Markup(concat(%s))' % frame.buffer) + else: + self.write('concat(%s)' % frame.buffer) + self.signature(node, frame) + self.write(')') + + def visit_Test(self, node, frame): + self.write(self.tests[node.name] + '(') + if node.name not in self.environment.tests: + self.fail('no test named %r' % node.name, node.lineno) + self.visit(node.node, frame) + self.signature(node, frame) + self.write(')') + + def visit_CondExpr(self, node, frame): + def write_expr2(): + if node.expr2 is not None: + return self.visit(node.expr2, frame) + self.write('environment.undefined(%r)' % ('the inline if-' + 'expression on %s evaluated to false and ' + 'no else section was defined.' % self.position(node))) + + self.write('(') + self.visit(node.expr1, frame) + self.write(' if ') + self.visit(node.test, frame) + self.write(' else ') + write_expr2() + self.write(')') + + def visit_Call(self, node, frame, forward_caller=False): + if self.environment.sandboxed: + self.write('environment.call(context, ') + else: + self.write('context.call(') + self.visit(node.node, frame) + extra_kwargs = forward_caller and {'caller': 'caller'} or None + self.signature(node, frame, extra_kwargs) + self.write(')') + + def visit_Keyword(self, node, frame): + self.write(node.key + '=') + self.visit(node.value, frame) + + # -- Unused nodes for extensions + + def visit_MarkSafe(self, node, frame): + self.write('Markup(') + self.visit(node.expr, frame) + self.write(')') + + def visit_MarkSafeIfAutoescape(self, node, frame): + self.write('(context.eval_ctx.autoescape and Markup or identity)(') + self.visit(node.expr, frame) + self.write(')') + + def visit_EnvironmentAttribute(self, node, frame): + self.write('environment.' + node.name) + + def visit_ExtensionAttribute(self, node, frame): + self.write('environment.extensions[%r].%s' % (node.identifier, node.name)) + + def visit_ImportedName(self, node, frame): + self.write(self.import_aliases[node.importname]) + + def visit_InternalName(self, node, frame): + self.write(node.name) + + def visit_ContextReference(self, node, frame): + self.write('context') + + def visit_Continue(self, node, frame): + self.writeline('continue', node) + + def visit_Break(self, node, frame): + self.writeline('break', node) + + def visit_Scope(self, node, frame): + scope_frame = frame.inner() + scope_frame.inspect(node.iter_child_nodes()) + aliases = self.push_scope(scope_frame) + self.pull_locals(scope_frame) + self.blockvisit(node.body, scope_frame) + self.pop_scope(aliases, scope_frame) + + def visit_EvalContextModifier(self, node, frame): + for keyword in node.options: + self.writeline('context.eval_ctx.%s = ' % keyword.key) + self.visit(keyword.value, frame) + try: + val = keyword.value.as_const(frame.eval_ctx) + except nodes.Impossible: + frame.eval_ctx.volatile = True + else: + setattr(frame.eval_ctx, keyword.key, val) + + def visit_ScopedEvalContextModifier(self, node, frame): + old_ctx_name = self.temporary_identifier() + safed_ctx = frame.eval_ctx.save() + self.writeline('%s = context.eval_ctx.save()' % old_ctx_name) + self.visit_EvalContextModifier(node, frame) + for child in node.body: + self.visit(child, frame) + frame.eval_ctx.revert(safed_ctx) + self.writeline('context.eval_ctx.revert(%s)' % old_ctx_name) diff --git a/deps/v8_inspector/deps/jinja2/jinja2/constants.py b/deps/v8_inspector/deps/jinja2/jinja2/constants.py new file mode 100644 index 00000000000000..cab203cc772f71 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/constants.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +""" + jinja.constants + ~~~~~~~~~~~~~~~ + + Various constants. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" + + +#: list of lorem ipsum words used by the lipsum() helper function +LOREM_IPSUM_WORDS = u'''\ +a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at +auctor augue bibendum blandit class commodo condimentum congue consectetuer +consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus +diam dictum dictumst dignissim dis dolor donec dui duis egestas eget eleifend +elementum elit enim erat eros est et etiam eu euismod facilisi facilisis fames +faucibus felis fermentum feugiat fringilla fusce gravida habitant habitasse hac +hendrerit hymenaeos iaculis id imperdiet in inceptos integer interdum ipsum +justo lacinia lacus laoreet lectus leo libero ligula litora lobortis lorem +luctus maecenas magna magnis malesuada massa mattis mauris metus mi molestie +mollis montes morbi mus nam nascetur natoque nec neque netus nibh nisi nisl non +nonummy nostra nulla nullam nunc odio orci ornare parturient pede pellentesque +penatibus per pharetra phasellus placerat platea porta porttitor posuere +potenti praesent pretium primis proin pulvinar purus quam quis quisque rhoncus +ridiculus risus rutrum sagittis sapien scelerisque sed sem semper senectus sit +sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor +tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices +ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus +viverra volutpat vulputate''' diff --git a/deps/v8_inspector/deps/jinja2/jinja2/debug.py b/deps/v8_inspector/deps/jinja2/jinja2/debug.py new file mode 100644 index 00000000000000..3252748369484f --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/debug.py @@ -0,0 +1,350 @@ +# -*- coding: utf-8 -*- +""" + jinja2.debug + ~~~~~~~~~~~~ + + Implements the debug interface for Jinja. This module does some pretty + ugly stuff with the Python traceback system in order to achieve tracebacks + with correct line numbers, locals and contents. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import sys +import traceback +from types import TracebackType, CodeType +from jinja2.utils import missing, internal_code +from jinja2.exceptions import TemplateSyntaxError +from jinja2._compat import iteritems, reraise, PY2 + +# on pypy we can take advantage of transparent proxies +try: + from __pypy__ import tproxy +except ImportError: + tproxy = None + + +# how does the raise helper look like? +try: + exec("raise TypeError, 'foo'") +except SyntaxError: + raise_helper = 'raise __jinja_exception__[1]' +except TypeError: + raise_helper = 'raise __jinja_exception__[0], __jinja_exception__[1]' + + +class TracebackFrameProxy(object): + """Proxies a traceback frame.""" + + def __init__(self, tb): + self.tb = tb + self._tb_next = None + + @property + def tb_next(self): + return self._tb_next + + def set_next(self, next): + if tb_set_next is not None: + try: + tb_set_next(self.tb, next and next.tb or None) + except Exception: + # this function can fail due to all the hackery it does + # on various python implementations. We just catch errors + # down and ignore them if necessary. + pass + self._tb_next = next + + @property + def is_jinja_frame(self): + return '__jinja_template__' in self.tb.tb_frame.f_globals + + def __getattr__(self, name): + return getattr(self.tb, name) + + +def make_frame_proxy(frame): + proxy = TracebackFrameProxy(frame) + if tproxy is None: + return proxy + def operation_handler(operation, *args, **kwargs): + if operation in ('__getattribute__', '__getattr__'): + return getattr(proxy, args[0]) + elif operation == '__setattr__': + proxy.__setattr__(*args, **kwargs) + else: + return getattr(proxy, operation)(*args, **kwargs) + return tproxy(TracebackType, operation_handler) + + +class ProcessedTraceback(object): + """Holds a Jinja preprocessed traceback for printing or reraising.""" + + def __init__(self, exc_type, exc_value, frames): + assert frames, 'no frames for this traceback?' + self.exc_type = exc_type + self.exc_value = exc_value + self.frames = frames + + # newly concatenate the frames (which are proxies) + prev_tb = None + for tb in self.frames: + if prev_tb is not None: + prev_tb.set_next(tb) + prev_tb = tb + prev_tb.set_next(None) + + def render_as_text(self, limit=None): + """Return a string with the traceback.""" + lines = traceback.format_exception(self.exc_type, self.exc_value, + self.frames[0], limit=limit) + return ''.join(lines).rstrip() + + def render_as_html(self, full=False): + """Return a unicode string with the traceback as rendered HTML.""" + from jinja2.debugrenderer import render_traceback + return u'%s\n\n<!--\n%s\n-->' % ( + render_traceback(self, full=full), + self.render_as_text().decode('utf-8', 'replace') + ) + + @property + def is_template_syntax_error(self): + """`True` if this is a template syntax error.""" + return isinstance(self.exc_value, TemplateSyntaxError) + + @property + def exc_info(self): + """Exception info tuple with a proxy around the frame objects.""" + return self.exc_type, self.exc_value, self.frames[0] + + @property + def standard_exc_info(self): + """Standard python exc_info for re-raising""" + tb = self.frames[0] + # the frame will be an actual traceback (or transparent proxy) if + # we are on pypy or a python implementation with support for tproxy + if type(tb) is not TracebackType: + tb = tb.tb + return self.exc_type, self.exc_value, tb + + +def make_traceback(exc_info, source_hint=None): + """Creates a processed traceback object from the exc_info.""" + exc_type, exc_value, tb = exc_info + if isinstance(exc_value, TemplateSyntaxError): + exc_info = translate_syntax_error(exc_value, source_hint) + initial_skip = 0 + else: + initial_skip = 1 + return translate_exception(exc_info, initial_skip) + + +def translate_syntax_error(error, source=None): + """Rewrites a syntax error to please traceback systems.""" + error.source = source + error.translated = True + exc_info = (error.__class__, error, None) + filename = error.filename + if filename is None: + filename = '<unknown>' + return fake_exc_info(exc_info, filename, error.lineno) + + +def translate_exception(exc_info, initial_skip=0): + """If passed an exc_info it will automatically rewrite the exceptions + all the way down to the correct line numbers and frames. + """ + tb = exc_info[2] + frames = [] + + # skip some internal frames if wanted + for x in range(initial_skip): + if tb is not None: + tb = tb.tb_next + initial_tb = tb + + while tb is not None: + # skip frames decorated with @internalcode. These are internal + # calls we can't avoid and that are useless in template debugging + # output. + if tb.tb_frame.f_code in internal_code: + tb = tb.tb_next + continue + + # save a reference to the next frame if we override the current + # one with a faked one. + next = tb.tb_next + + # fake template exceptions + template = tb.tb_frame.f_globals.get('__jinja_template__') + if template is not None: + lineno = template.get_corresponding_lineno(tb.tb_lineno) + tb = fake_exc_info(exc_info[:2] + (tb,), template.filename, + lineno)[2] + + frames.append(make_frame_proxy(tb)) + tb = next + + # if we don't have any exceptions in the frames left, we have to + # reraise it unchanged. + # XXX: can we backup here? when could this happen? + if not frames: + reraise(exc_info[0], exc_info[1], exc_info[2]) + + return ProcessedTraceback(exc_info[0], exc_info[1], frames) + + +def fake_exc_info(exc_info, filename, lineno): + """Helper for `translate_exception`.""" + exc_type, exc_value, tb = exc_info + + # figure the real context out + if tb is not None: + real_locals = tb.tb_frame.f_locals.copy() + ctx = real_locals.get('context') + if ctx: + locals = ctx.get_all() + else: + locals = {} + for name, value in iteritems(real_locals): + if name.startswith('l_') and value is not missing: + locals[name[2:]] = value + + # if there is a local called __jinja_exception__, we get + # rid of it to not break the debug functionality. + locals.pop('__jinja_exception__', None) + else: + locals = {} + + # assamble fake globals we need + globals = { + '__name__': filename, + '__file__': filename, + '__jinja_exception__': exc_info[:2], + + # we don't want to keep the reference to the template around + # to not cause circular dependencies, but we mark it as Jinja + # frame for the ProcessedTraceback + '__jinja_template__': None + } + + # and fake the exception + code = compile('\n' * (lineno - 1) + raise_helper, filename, 'exec') + + # if it's possible, change the name of the code. This won't work + # on some python environments such as google appengine + try: + if tb is None: + location = 'template' + else: + function = tb.tb_frame.f_code.co_name + if function == 'root': + location = 'top-level template code' + elif function.startswith('block_'): + location = 'block "%s"' % function[6:] + else: + location = 'template' + + if PY2: + code = CodeType(0, code.co_nlocals, code.co_stacksize, + code.co_flags, code.co_code, code.co_consts, + code.co_names, code.co_varnames, filename, + location, code.co_firstlineno, + code.co_lnotab, (), ()) + else: + code = CodeType(0, code.co_kwonlyargcount, + code.co_nlocals, code.co_stacksize, + code.co_flags, code.co_code, code.co_consts, + code.co_names, code.co_varnames, filename, + location, code.co_firstlineno, + code.co_lnotab, (), ()) + except Exception as e: + pass + + # execute the code and catch the new traceback + try: + exec(code, globals, locals) + except: + exc_info = sys.exc_info() + new_tb = exc_info[2].tb_next + + # return without this frame + return exc_info[:2] + (new_tb,) + + +def _init_ugly_crap(): + """This function implements a few ugly things so that we can patch the + traceback objects. The function returned allows resetting `tb_next` on + any python traceback object. Do not attempt to use this on non cpython + interpreters + """ + import ctypes + from types import TracebackType + + if PY2: + # figure out size of _Py_ssize_t for Python 2: + if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'): + _Py_ssize_t = ctypes.c_int64 + else: + _Py_ssize_t = ctypes.c_int + else: + # platform ssize_t on Python 3 + _Py_ssize_t = ctypes.c_ssize_t + + # regular python + class _PyObject(ctypes.Structure): + pass + _PyObject._fields_ = [ + ('ob_refcnt', _Py_ssize_t), + ('ob_type', ctypes.POINTER(_PyObject)) + ] + + # python with trace + if hasattr(sys, 'getobjects'): + class _PyObject(ctypes.Structure): + pass + _PyObject._fields_ = [ + ('_ob_next', ctypes.POINTER(_PyObject)), + ('_ob_prev', ctypes.POINTER(_PyObject)), + ('ob_refcnt', _Py_ssize_t), + ('ob_type', ctypes.POINTER(_PyObject)) + ] + + class _Traceback(_PyObject): + pass + _Traceback._fields_ = [ + ('tb_next', ctypes.POINTER(_Traceback)), + ('tb_frame', ctypes.POINTER(_PyObject)), + ('tb_lasti', ctypes.c_int), + ('tb_lineno', ctypes.c_int) + ] + + def tb_set_next(tb, next): + """Set the tb_next attribute of a traceback object.""" + if not (isinstance(tb, TracebackType) and + (next is None or isinstance(next, TracebackType))): + raise TypeError('tb_set_next arguments must be traceback objects') + obj = _Traceback.from_address(id(tb)) + if tb.tb_next is not None: + old = _Traceback.from_address(id(tb.tb_next)) + old.ob_refcnt -= 1 + if next is None: + obj.tb_next = ctypes.POINTER(_Traceback)() + else: + next = _Traceback.from_address(id(next)) + next.ob_refcnt += 1 + obj.tb_next = ctypes.pointer(next) + + return tb_set_next + + +# try to get a tb_set_next implementation if we don't have transparent +# proxies. +tb_set_next = None +if tproxy is None: + try: + tb_set_next = _init_ugly_crap() + except: + pass + del _init_ugly_crap diff --git a/deps/v8_inspector/deps/jinja2/jinja2/defaults.py b/deps/v8_inspector/deps/jinja2/jinja2/defaults.py new file mode 100644 index 00000000000000..3717a7223f6882 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/defaults.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +""" + jinja2.defaults + ~~~~~~~~~~~~~~~ + + Jinja default filters and tags. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +from jinja2._compat import range_type +from jinja2.utils import generate_lorem_ipsum, Cycler, Joiner + + +# defaults for the parser / lexer +BLOCK_START_STRING = '{%' +BLOCK_END_STRING = '%}' +VARIABLE_START_STRING = '{{' +VARIABLE_END_STRING = '}}' +COMMENT_START_STRING = '{#' +COMMENT_END_STRING = '#}' +LINE_STATEMENT_PREFIX = None +LINE_COMMENT_PREFIX = None +TRIM_BLOCKS = False +LSTRIP_BLOCKS = False +NEWLINE_SEQUENCE = '\n' +KEEP_TRAILING_NEWLINE = False + + +# default filters, tests and namespace +from jinja2.filters import FILTERS as DEFAULT_FILTERS +from jinja2.tests import TESTS as DEFAULT_TESTS +DEFAULT_NAMESPACE = { + 'range': range_type, + 'dict': dict, + 'lipsum': generate_lorem_ipsum, + 'cycler': Cycler, + 'joiner': Joiner +} + + +# export all constants +__all__ = tuple(x for x in locals().keys() if x.isupper()) diff --git a/deps/v8_inspector/deps/jinja2/jinja2/environment.py b/deps/v8_inspector/deps/jinja2/jinja2/environment.py new file mode 100644 index 00000000000000..8b2572bb8c176e --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/environment.py @@ -0,0 +1,1213 @@ +# -*- coding: utf-8 -*- +""" + jinja2.environment + ~~~~~~~~~~~~~~~~~~ + + Provides a class that holds runtime and parsing time options. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import os +import sys +from jinja2 import nodes +from jinja2.defaults import BLOCK_START_STRING, \ + BLOCK_END_STRING, VARIABLE_START_STRING, VARIABLE_END_STRING, \ + COMMENT_START_STRING, COMMENT_END_STRING, LINE_STATEMENT_PREFIX, \ + LINE_COMMENT_PREFIX, TRIM_BLOCKS, NEWLINE_SEQUENCE, \ + DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE, \ + KEEP_TRAILING_NEWLINE, LSTRIP_BLOCKS +from jinja2.lexer import get_lexer, TokenStream +from jinja2.parser import Parser +from jinja2.nodes import EvalContext +from jinja2.optimizer import optimize +from jinja2.compiler import generate, CodeGenerator +from jinja2.runtime import Undefined, new_context, Context +from jinja2.exceptions import TemplateSyntaxError, TemplateNotFound, \ + TemplatesNotFound, TemplateRuntimeError +from jinja2.utils import import_string, LRUCache, Markup, missing, \ + concat, consume, internalcode +from jinja2._compat import imap, ifilter, string_types, iteritems, \ + text_type, reraise, implements_iterator, implements_to_string, \ + get_next, encode_filename, PY2, PYPY +from functools import reduce + + +# for direct template usage we have up to ten living environments +_spontaneous_environments = LRUCache(10) + +# the function to create jinja traceback objects. This is dynamically +# imported on the first exception in the exception handler. +_make_traceback = None + + +def get_spontaneous_environment(*args): + """Return a new spontaneous environment. A spontaneous environment is an + unnamed and unaccessible (in theory) environment that is used for + templates generated from a string and not from the file system. + """ + try: + env = _spontaneous_environments.get(args) + except TypeError: + return Environment(*args) + if env is not None: + return env + _spontaneous_environments[args] = env = Environment(*args) + env.shared = True + return env + + +def create_cache(size): + """Return the cache class for the given size.""" + if size == 0: + return None + if size < 0: + return {} + return LRUCache(size) + + +def copy_cache(cache): + """Create an empty copy of the given cache.""" + if cache is None: + return None + elif type(cache) is dict: + return {} + return LRUCache(cache.capacity) + + +def load_extensions(environment, extensions): + """Load the extensions from the list and bind it to the environment. + Returns a dict of instantiated environments. + """ + result = {} + for extension in extensions: + if isinstance(extension, string_types): + extension = import_string(extension) + result[extension.identifier] = extension(environment) + return result + + +def _environment_sanity_check(environment): + """Perform a sanity check on the environment.""" + assert issubclass(environment.undefined, Undefined), 'undefined must ' \ + 'be a subclass of undefined because filters depend on it.' + assert environment.block_start_string != \ + environment.variable_start_string != \ + environment.comment_start_string, 'block, variable and comment ' \ + 'start strings must be different' + assert environment.newline_sequence in ('\r', '\r\n', '\n'), \ + 'newline_sequence set to unknown line ending string.' + return environment + + +class Environment(object): + r"""The core component of Jinja is the `Environment`. It contains + important shared variables like configuration, filters, tests, + globals and others. Instances of this class may be modified if + they are not shared and if no template was loaded so far. + Modifications on environments after the first template was loaded + will lead to surprising effects and undefined behavior. + + Here are the possible initialization parameters: + + `block_start_string` + The string marking the beginning of a block. Defaults to ``'{%'``. + + `block_end_string` + The string marking the end of a block. Defaults to ``'%}'``. + + `variable_start_string` + The string marking the beginning of a print statement. + Defaults to ``'{{'``. + + `variable_end_string` + The string marking the end of a print statement. Defaults to + ``'}}'``. + + `comment_start_string` + The string marking the beginning of a comment. Defaults to ``'{#'``. + + `comment_end_string` + The string marking the end of a comment. Defaults to ``'#}'``. + + `line_statement_prefix` + If given and a string, this will be used as prefix for line based + statements. See also :ref:`line-statements`. + + `line_comment_prefix` + If given and a string, this will be used as prefix for line based + comments. See also :ref:`line-statements`. + + .. versionadded:: 2.2 + + `trim_blocks` + If this is set to ``True`` the first newline after a block is + removed (block, not variable tag!). Defaults to `False`. + + `lstrip_blocks` + If this is set to ``True`` leading spaces and tabs are stripped + from the start of a line to a block. Defaults to `False`. + + `newline_sequence` + The sequence that starts a newline. Must be one of ``'\r'``, + ``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a + useful default for Linux and OS X systems as well as web + applications. + + `keep_trailing_newline` + Preserve the trailing newline when rendering templates. + The default is ``False``, which causes a single newline, + if present, to be stripped from the end of the template. + + .. versionadded:: 2.7 + + `extensions` + List of Jinja extensions to use. This can either be import paths + as strings or extension classes. For more information have a + look at :ref:`the extensions documentation <jinja-extensions>`. + + `optimized` + should the optimizer be enabled? Default is `True`. + + `undefined` + :class:`Undefined` or a subclass of it that is used to represent + undefined values in the template. + + `finalize` + A callable that can be used to process the result of a variable + expression before it is output. For example one can convert + `None` implicitly into an empty string here. + + `autoescape` + If set to true the XML/HTML autoescaping feature is enabled by + default. For more details about autoescaping see + :class:`~jinja2.utils.Markup`. As of Jinja 2.4 this can also + be a callable that is passed the template name and has to + return `True` or `False` depending on autoescape should be + enabled by default. + + .. versionchanged:: 2.4 + `autoescape` can now be a function + + `loader` + The template loader for this environment. + + `cache_size` + The size of the cache. Per default this is ``400`` which means + that if more than 400 templates are loaded the loader will clean + out the least recently used template. If the cache size is set to + ``0`` templates are recompiled all the time, if the cache size is + ``-1`` the cache will not be cleaned. + + .. versionchanged:: 2.8 + The cache size was increased to 400 from a low 50. + + `auto_reload` + Some loaders load templates from locations where the template + sources may change (ie: file system or database). If + `auto_reload` is set to `True` (default) every time a template is + requested the loader checks if the source changed and if yes, it + will reload the template. For higher performance it's possible to + disable that. + + `bytecode_cache` + If set to a bytecode cache object, this object will provide a + cache for the internal Jinja bytecode so that templates don't + have to be parsed if they were not changed. + + See :ref:`bytecode-cache` for more information. + """ + + #: if this environment is sandboxed. Modifying this variable won't make + #: the environment sandboxed though. For a real sandboxed environment + #: have a look at jinja2.sandbox. This flag alone controls the code + #: generation by the compiler. + sandboxed = False + + #: True if the environment is just an overlay + overlayed = False + + #: the environment this environment is linked to if it is an overlay + linked_to = None + + #: shared environments have this set to `True`. A shared environment + #: must not be modified + shared = False + + #: these are currently EXPERIMENTAL undocumented features. + exception_handler = None + exception_formatter = None + + #: the class that is used for code generation. See + #: :class:`~jinja2.compiler.CodeGenerator` for more information. + code_generator_class = CodeGenerator + + #: the context class thatis used for templates. See + #: :class:`~jinja2.runtime.Context` for more information. + context_class = Context + + def __init__(self, + block_start_string=BLOCK_START_STRING, + block_end_string=BLOCK_END_STRING, + variable_start_string=VARIABLE_START_STRING, + variable_end_string=VARIABLE_END_STRING, + comment_start_string=COMMENT_START_STRING, + comment_end_string=COMMENT_END_STRING, + line_statement_prefix=LINE_STATEMENT_PREFIX, + line_comment_prefix=LINE_COMMENT_PREFIX, + trim_blocks=TRIM_BLOCKS, + lstrip_blocks=LSTRIP_BLOCKS, + newline_sequence=NEWLINE_SEQUENCE, + keep_trailing_newline=KEEP_TRAILING_NEWLINE, + extensions=(), + optimized=True, + undefined=Undefined, + finalize=None, + autoescape=False, + loader=None, + cache_size=400, + auto_reload=True, + bytecode_cache=None): + # !!Important notice!! + # The constructor accepts quite a few arguments that should be + # passed by keyword rather than position. However it's important to + # not change the order of arguments because it's used at least + # internally in those cases: + # - spontaneous environments (i18n extension and Template) + # - unittests + # If parameter changes are required only add parameters at the end + # and don't change the arguments (or the defaults!) of the arguments + # existing already. + + # lexer / parser information + self.block_start_string = block_start_string + self.block_end_string = block_end_string + self.variable_start_string = variable_start_string + self.variable_end_string = variable_end_string + self.comment_start_string = comment_start_string + self.comment_end_string = comment_end_string + self.line_statement_prefix = line_statement_prefix + self.line_comment_prefix = line_comment_prefix + self.trim_blocks = trim_blocks + self.lstrip_blocks = lstrip_blocks + self.newline_sequence = newline_sequence + self.keep_trailing_newline = keep_trailing_newline + + # runtime information + self.undefined = undefined + self.optimized = optimized + self.finalize = finalize + self.autoescape = autoescape + + # defaults + self.filters = DEFAULT_FILTERS.copy() + self.tests = DEFAULT_TESTS.copy() + self.globals = DEFAULT_NAMESPACE.copy() + + # set the loader provided + self.loader = loader + self.cache = create_cache(cache_size) + self.bytecode_cache = bytecode_cache + self.auto_reload = auto_reload + + # load extensions + self.extensions = load_extensions(self, extensions) + + _environment_sanity_check(self) + + def add_extension(self, extension): + """Adds an extension after the environment was created. + + .. versionadded:: 2.5 + """ + self.extensions.update(load_extensions(self, [extension])) + + def extend(self, **attributes): + """Add the items to the instance of the environment if they do not exist + yet. This is used by :ref:`extensions <writing-extensions>` to register + callbacks and configuration values without breaking inheritance. + """ + for key, value in iteritems(attributes): + if not hasattr(self, key): + setattr(self, key, value) + + def overlay(self, block_start_string=missing, block_end_string=missing, + variable_start_string=missing, variable_end_string=missing, + comment_start_string=missing, comment_end_string=missing, + line_statement_prefix=missing, line_comment_prefix=missing, + trim_blocks=missing, lstrip_blocks=missing, + extensions=missing, optimized=missing, + undefined=missing, finalize=missing, autoescape=missing, + loader=missing, cache_size=missing, auto_reload=missing, + bytecode_cache=missing): + """Create a new overlay environment that shares all the data with the + current environment except for cache and the overridden attributes. + Extensions cannot be removed for an overlayed environment. An overlayed + environment automatically gets all the extensions of the environment it + is linked to plus optional extra extensions. + + Creating overlays should happen after the initial environment was set + up completely. Not all attributes are truly linked, some are just + copied over so modifications on the original environment may not shine + through. + """ + args = dict(locals()) + del args['self'], args['cache_size'], args['extensions'] + + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.overlayed = True + rv.linked_to = self + + for key, value in iteritems(args): + if value is not missing: + setattr(rv, key, value) + + if cache_size is not missing: + rv.cache = create_cache(cache_size) + else: + rv.cache = copy_cache(self.cache) + + rv.extensions = {} + for key, value in iteritems(self.extensions): + rv.extensions[key] = value.bind(rv) + if extensions is not missing: + rv.extensions.update(load_extensions(rv, extensions)) + + return _environment_sanity_check(rv) + + lexer = property(get_lexer, doc="The lexer for this environment.") + + def iter_extensions(self): + """Iterates over the extensions by priority.""" + return iter(sorted(self.extensions.values(), + key=lambda x: x.priority)) + + def getitem(self, obj, argument): + """Get an item or attribute of an object but prefer the item.""" + try: + return obj[argument] + except (TypeError, LookupError): + if isinstance(argument, string_types): + try: + attr = str(argument) + except Exception: + pass + else: + try: + return getattr(obj, attr) + except AttributeError: + pass + return self.undefined(obj=obj, name=argument) + + def getattr(self, obj, attribute): + """Get an item or attribute of an object but prefer the attribute. + Unlike :meth:`getitem` the attribute *must* be a bytestring. + """ + try: + return getattr(obj, attribute) + except AttributeError: + pass + try: + return obj[attribute] + except (TypeError, LookupError, AttributeError): + return self.undefined(obj=obj, name=attribute) + + def call_filter(self, name, value, args=None, kwargs=None, + context=None, eval_ctx=None): + """Invokes a filter on a value the same way the compiler does it. + + .. versionadded:: 2.7 + """ + func = self.filters.get(name) + if func is None: + raise TemplateRuntimeError('no filter named %r' % name) + args = [value] + list(args or ()) + if getattr(func, 'contextfilter', False): + if context is None: + raise TemplateRuntimeError('Attempted to invoke context ' + 'filter without context') + args.insert(0, context) + elif getattr(func, 'evalcontextfilter', False): + if eval_ctx is None: + if context is not None: + eval_ctx = context.eval_ctx + else: + eval_ctx = EvalContext(self) + args.insert(0, eval_ctx) + elif getattr(func, 'environmentfilter', False): + args.insert(0, self) + return func(*args, **(kwargs or {})) + + def call_test(self, name, value, args=None, kwargs=None): + """Invokes a test on a value the same way the compiler does it. + + .. versionadded:: 2.7 + """ + func = self.tests.get(name) + if func is None: + raise TemplateRuntimeError('no test named %r' % name) + return func(value, *(args or ()), **(kwargs or {})) + + @internalcode + def parse(self, source, name=None, filename=None): + """Parse the sourcecode and return the abstract syntax tree. This + tree of nodes is used by the compiler to convert the template into + executable source- or bytecode. This is useful for debugging or to + extract information from templates. + + If you are :ref:`developing Jinja2 extensions <writing-extensions>` + this gives you a good overview of the node tree generated. + """ + try: + return self._parse(source, name, filename) + except TemplateSyntaxError: + exc_info = sys.exc_info() + self.handle_exception(exc_info, source_hint=source) + + def _parse(self, source, name, filename): + """Internal parsing function used by `parse` and `compile`.""" + return Parser(self, source, name, encode_filename(filename)).parse() + + def lex(self, source, name=None, filename=None): + """Lex the given sourcecode and return a generator that yields + tokens as tuples in the form ``(lineno, token_type, value)``. + This can be useful for :ref:`extension development <writing-extensions>` + and debugging templates. + + This does not perform preprocessing. If you want the preprocessing + of the extensions to be applied you have to filter source through + the :meth:`preprocess` method. + """ + source = text_type(source) + try: + return self.lexer.tokeniter(source, name, filename) + except TemplateSyntaxError: + exc_info = sys.exc_info() + self.handle_exception(exc_info, source_hint=source) + + def preprocess(self, source, name=None, filename=None): + """Preprocesses the source with all extensions. This is automatically + called for all parsing and compiling methods but *not* for :meth:`lex` + because there you usually only want the actual source tokenized. + """ + return reduce(lambda s, e: e.preprocess(s, name, filename), + self.iter_extensions(), text_type(source)) + + def _tokenize(self, source, name, filename=None, state=None): + """Called by the parser to do the preprocessing and filtering + for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`. + """ + source = self.preprocess(source, name, filename) + stream = self.lexer.tokenize(source, name, filename, state) + for ext in self.iter_extensions(): + stream = ext.filter_stream(stream) + if not isinstance(stream, TokenStream): + stream = TokenStream(stream, name, filename) + return stream + + def _generate(self, source, name, filename, defer_init=False): + """Internal hook that can be overridden to hook a different generate + method in. + + .. versionadded:: 2.5 + """ + return generate(source, self, name, filename, defer_init=defer_init) + + def _compile(self, source, filename): + """Internal hook that can be overridden to hook a different compile + method in. + + .. versionadded:: 2.5 + """ + return compile(source, filename, 'exec') + + @internalcode + def compile(self, source, name=None, filename=None, raw=False, + defer_init=False): + """Compile a node or template source code. The `name` parameter is + the load name of the template after it was joined using + :meth:`join_path` if necessary, not the filename on the file system. + the `filename` parameter is the estimated filename of the template on + the file system. If the template came from a database or memory this + can be omitted. + + The return value of this method is a python code object. If the `raw` + parameter is `True` the return value will be a string with python + code equivalent to the bytecode returned otherwise. This method is + mainly used internally. + + `defer_init` is use internally to aid the module code generator. This + causes the generated code to be able to import without the global + environment variable to be set. + + .. versionadded:: 2.4 + `defer_init` parameter added. + """ + source_hint = None + try: + if isinstance(source, string_types): + source_hint = source + source = self._parse(source, name, filename) + if self.optimized: + source = optimize(source, self) + source = self._generate(source, name, filename, + defer_init=defer_init) + if raw: + return source + if filename is None: + filename = '<template>' + else: + filename = encode_filename(filename) + return self._compile(source, filename) + except TemplateSyntaxError: + exc_info = sys.exc_info() + self.handle_exception(exc_info, source_hint=source_hint) + + def compile_expression(self, source, undefined_to_none=True): + """A handy helper method that returns a callable that accepts keyword + arguments that appear as variables in the expression. If called it + returns the result of the expression. + + This is useful if applications want to use the same rules as Jinja + in template "configuration files" or similar situations. + + Example usage: + + >>> env = Environment() + >>> expr = env.compile_expression('foo == 42') + >>> expr(foo=23) + False + >>> expr(foo=42) + True + + Per default the return value is converted to `None` if the + expression returns an undefined value. This can be changed + by setting `undefined_to_none` to `False`. + + >>> env.compile_expression('var')() is None + True + >>> env.compile_expression('var', undefined_to_none=False)() + Undefined + + .. versionadded:: 2.1 + """ + parser = Parser(self, source, state='variable') + exc_info = None + try: + expr = parser.parse_expression() + if not parser.stream.eos: + raise TemplateSyntaxError('chunk after expression', + parser.stream.current.lineno, + None, None) + expr.set_environment(self) + except TemplateSyntaxError: + exc_info = sys.exc_info() + if exc_info is not None: + self.handle_exception(exc_info, source_hint=source) + body = [nodes.Assign(nodes.Name('result', 'store'), expr, lineno=1)] + template = self.from_string(nodes.Template(body, lineno=1)) + return TemplateExpression(template, undefined_to_none) + + def compile_templates(self, target, extensions=None, filter_func=None, + zip='deflated', log_function=None, + ignore_errors=True, py_compile=False): + """Finds all the templates the loader can find, compiles them + and stores them in `target`. If `zip` is `None`, instead of in a + zipfile, the templates will be stored in a directory. + By default a deflate zip algorithm is used. To switch to + the stored algorithm, `zip` can be set to ``'stored'``. + + `extensions` and `filter_func` are passed to :meth:`list_templates`. + Each template returned will be compiled to the target folder or + zipfile. + + By default template compilation errors are ignored. In case a + log function is provided, errors are logged. If you want template + syntax errors to abort the compilation you can set `ignore_errors` + to `False` and you will get an exception on syntax errors. + + If `py_compile` is set to `True` .pyc files will be written to the + target instead of standard .py files. This flag does not do anything + on pypy and Python 3 where pyc files are not picked up by itself and + don't give much benefit. + + .. versionadded:: 2.4 + """ + from jinja2.loaders import ModuleLoader + + if log_function is None: + log_function = lambda x: None + + if py_compile: + if not PY2 or PYPY: + from warnings import warn + warn(Warning('py_compile has no effect on pypy or Python 3')) + py_compile = False + else: + import imp + import marshal + py_header = imp.get_magic() + \ + u'\xff\xff\xff\xff'.encode('iso-8859-15') + + # Python 3.3 added a source filesize to the header + if sys.version_info >= (3, 3): + py_header += u'\x00\x00\x00\x00'.encode('iso-8859-15') + + def write_file(filename, data, mode): + if zip: + info = ZipInfo(filename) + info.external_attr = 0o755 << 16 + zip_file.writestr(info, data) + else: + f = open(os.path.join(target, filename), mode) + try: + f.write(data) + finally: + f.close() + + if zip is not None: + from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED, ZIP_STORED + zip_file = ZipFile(target, 'w', dict(deflated=ZIP_DEFLATED, + stored=ZIP_STORED)[zip]) + log_function('Compiling into Zip archive "%s"' % target) + else: + if not os.path.isdir(target): + os.makedirs(target) + log_function('Compiling into folder "%s"' % target) + + try: + for name in self.list_templates(extensions, filter_func): + source, filename, _ = self.loader.get_source(self, name) + try: + code = self.compile(source, name, filename, True, True) + except TemplateSyntaxError as e: + if not ignore_errors: + raise + log_function('Could not compile "%s": %s' % (name, e)) + continue + + filename = ModuleLoader.get_module_filename(name) + + if py_compile: + c = self._compile(code, encode_filename(filename)) + write_file(filename + 'c', py_header + + marshal.dumps(c), 'wb') + log_function('Byte-compiled "%s" as %s' % + (name, filename + 'c')) + else: + write_file(filename, code, 'w') + log_function('Compiled "%s" as %s' % (name, filename)) + finally: + if zip: + zip_file.close() + + log_function('Finished compiling templates') + + def list_templates(self, extensions=None, filter_func=None): + """Returns a list of templates for this environment. This requires + that the loader supports the loader's + :meth:`~BaseLoader.list_templates` method. + + If there are other files in the template folder besides the + actual templates, the returned list can be filtered. There are two + ways: either `extensions` is set to a list of file extensions for + templates, or a `filter_func` can be provided which is a callable that + is passed a template name and should return `True` if it should end up + in the result list. + + If the loader does not support that, a :exc:`TypeError` is raised. + + .. versionadded:: 2.4 + """ + x = self.loader.list_templates() + if extensions is not None: + if filter_func is not None: + raise TypeError('either extensions or filter_func ' + 'can be passed, but not both') + filter_func = lambda x: '.' in x and \ + x.rsplit('.', 1)[1] in extensions + if filter_func is not None: + x = list(ifilter(filter_func, x)) + return x + + def handle_exception(self, exc_info=None, rendered=False, source_hint=None): + """Exception handling helper. This is used internally to either raise + rewritten exceptions or return a rendered traceback for the template. + """ + global _make_traceback + if exc_info is None: + exc_info = sys.exc_info() + + # the debugging module is imported when it's used for the first time. + # we're doing a lot of stuff there and for applications that do not + # get any exceptions in template rendering there is no need to load + # all of that. + if _make_traceback is None: + from jinja2.debug import make_traceback as _make_traceback + traceback = _make_traceback(exc_info, source_hint) + if rendered and self.exception_formatter is not None: + return self.exception_formatter(traceback) + if self.exception_handler is not None: + self.exception_handler(traceback) + exc_type, exc_value, tb = traceback.standard_exc_info + reraise(exc_type, exc_value, tb) + + def join_path(self, template, parent): + """Join a template with the parent. By default all the lookups are + relative to the loader root so this method returns the `template` + parameter unchanged, but if the paths should be relative to the + parent template, this function can be used to calculate the real + template name. + + Subclasses may override this method and implement template path + joining here. + """ + return template + + @internalcode + def _load_template(self, name, globals): + if self.loader is None: + raise TypeError('no loader for this environment specified') + try: + # use abs path for cache key + cache_key = self.loader.get_source(self, name)[1] + except RuntimeError: + # if loader does not implement get_source() + cache_key = None + # if template is not file, use name for cache key + if cache_key is None: + cache_key = name + if self.cache is not None: + template = self.cache.get(cache_key) + if template is not None and (not self.auto_reload or + template.is_up_to_date): + return template + template = self.loader.load(self, name, globals) + if self.cache is not None: + self.cache[cache_key] = template + return template + + @internalcode + def get_template(self, name, parent=None, globals=None): + """Load a template from the loader. If a loader is configured this + method ask the loader for the template and returns a :class:`Template`. + If the `parent` parameter is not `None`, :meth:`join_path` is called + to get the real template name before loading. + + The `globals` parameter can be used to provide template wide globals. + These variables are available in the context at render time. + + If the template does not exist a :exc:`TemplateNotFound` exception is + raised. + + .. versionchanged:: 2.4 + If `name` is a :class:`Template` object it is returned from the + function unchanged. + """ + if isinstance(name, Template): + return name + if parent is not None: + name = self.join_path(name, parent) + return self._load_template(name, self.make_globals(globals)) + + @internalcode + def select_template(self, names, parent=None, globals=None): + """Works like :meth:`get_template` but tries a number of templates + before it fails. If it cannot find any of the templates, it will + raise a :exc:`TemplatesNotFound` exception. + + .. versionadded:: 2.3 + + .. versionchanged:: 2.4 + If `names` contains a :class:`Template` object it is returned + from the function unchanged. + """ + if not names: + raise TemplatesNotFound(message=u'Tried to select from an empty list ' + u'of templates.') + globals = self.make_globals(globals) + for name in names: + if isinstance(name, Template): + return name + if parent is not None: + name = self.join_path(name, parent) + try: + return self._load_template(name, globals) + except TemplateNotFound: + pass + raise TemplatesNotFound(names) + + @internalcode + def get_or_select_template(self, template_name_or_list, + parent=None, globals=None): + """Does a typecheck and dispatches to :meth:`select_template` + if an iterable of template names is given, otherwise to + :meth:`get_template`. + + .. versionadded:: 2.3 + """ + if isinstance(template_name_or_list, string_types): + return self.get_template(template_name_or_list, parent, globals) + elif isinstance(template_name_or_list, Template): + return template_name_or_list + return self.select_template(template_name_or_list, parent, globals) + + def from_string(self, source, globals=None, template_class=None): + """Load a template from a string. This parses the source given and + returns a :class:`Template` object. + """ + globals = self.make_globals(globals) + cls = template_class or self.template_class + return cls.from_code(self, self.compile(source), globals, None) + + def make_globals(self, d): + """Return a dict for the globals.""" + if not d: + return self.globals + return dict(self.globals, **d) + + +class Template(object): + """The central template object. This class represents a compiled template + and is used to evaluate it. + + Normally the template object is generated from an :class:`Environment` but + it also has a constructor that makes it possible to create a template + instance directly using the constructor. It takes the same arguments as + the environment constructor but it's not possible to specify a loader. + + Every template object has a few methods and members that are guaranteed + to exist. However it's important that a template object should be + considered immutable. Modifications on the object are not supported. + + Template objects created from the constructor rather than an environment + do have an `environment` attribute that points to a temporary environment + that is probably shared with other templates created with the constructor + and compatible settings. + + >>> template = Template('Hello {{ name }}!') + >>> template.render(name='John Doe') == u'Hello John Doe!' + True + >>> stream = template.stream(name='John Doe') + >>> next(stream) == u'Hello John Doe!' + True + >>> next(stream) + Traceback (most recent call last): + ... + StopIteration + """ + + def __new__(cls, source, + block_start_string=BLOCK_START_STRING, + block_end_string=BLOCK_END_STRING, + variable_start_string=VARIABLE_START_STRING, + variable_end_string=VARIABLE_END_STRING, + comment_start_string=COMMENT_START_STRING, + comment_end_string=COMMENT_END_STRING, + line_statement_prefix=LINE_STATEMENT_PREFIX, + line_comment_prefix=LINE_COMMENT_PREFIX, + trim_blocks=TRIM_BLOCKS, + lstrip_blocks=LSTRIP_BLOCKS, + newline_sequence=NEWLINE_SEQUENCE, + keep_trailing_newline=KEEP_TRAILING_NEWLINE, + extensions=(), + optimized=True, + undefined=Undefined, + finalize=None, + autoescape=False): + env = get_spontaneous_environment( + block_start_string, block_end_string, variable_start_string, + variable_end_string, comment_start_string, comment_end_string, + line_statement_prefix, line_comment_prefix, trim_blocks, + lstrip_blocks, newline_sequence, keep_trailing_newline, + frozenset(extensions), optimized, undefined, finalize, autoescape, + None, 0, False, None) + return env.from_string(source, template_class=cls) + + @classmethod + def from_code(cls, environment, code, globals, uptodate=None): + """Creates a template object from compiled code and the globals. This + is used by the loaders and environment to create a template object. + """ + namespace = { + 'environment': environment, + '__file__': code.co_filename + } + exec(code, namespace) + rv = cls._from_namespace(environment, namespace, globals) + rv._uptodate = uptodate + return rv + + @classmethod + def from_module_dict(cls, environment, module_dict, globals): + """Creates a template object from a module. This is used by the + module loader to create a template object. + + .. versionadded:: 2.4 + """ + return cls._from_namespace(environment, module_dict, globals) + + @classmethod + def _from_namespace(cls, environment, namespace, globals): + t = object.__new__(cls) + t.environment = environment + t.globals = globals + t.name = namespace['name'] + t.filename = namespace['__file__'] + t.blocks = namespace['blocks'] + + # render function and module + t.root_render_func = namespace['root'] + t._module = None + + # debug and loader helpers + t._debug_info = namespace['debug_info'] + t._uptodate = None + + # store the reference + namespace['environment'] = environment + namespace['__jinja_template__'] = t + + return t + + def render(self, *args, **kwargs): + """This method accepts the same arguments as the `dict` constructor: + A dict, a dict subclass or some keyword arguments. If no arguments + are given the context will be empty. These two calls do the same:: + + template.render(knights='that say nih') + template.render({'knights': 'that say nih'}) + + This will return the rendered template as unicode string. + """ + vars = dict(*args, **kwargs) + try: + return concat(self.root_render_func(self.new_context(vars))) + except Exception: + exc_info = sys.exc_info() + return self.environment.handle_exception(exc_info, True) + + def stream(self, *args, **kwargs): + """Works exactly like :meth:`generate` but returns a + :class:`TemplateStream`. + """ + return TemplateStream(self.generate(*args, **kwargs)) + + def generate(self, *args, **kwargs): + """For very large templates it can be useful to not render the whole + template at once but evaluate each statement after another and yield + piece for piece. This method basically does exactly that and returns + a generator that yields one item after another as unicode strings. + + It accepts the same arguments as :meth:`render`. + """ + vars = dict(*args, **kwargs) + try: + for event in self.root_render_func(self.new_context(vars)): + yield event + except Exception: + exc_info = sys.exc_info() + else: + return + yield self.environment.handle_exception(exc_info, True) + + def new_context(self, vars=None, shared=False, locals=None): + """Create a new :class:`Context` for this template. The vars + provided will be passed to the template. Per default the globals + are added to the context. If shared is set to `True` the data + is passed as it to the context without adding the globals. + + `locals` can be a dict of local variables for internal usage. + """ + return new_context(self.environment, self.name, self.blocks, + vars, shared, self.globals, locals) + + def make_module(self, vars=None, shared=False, locals=None): + """This method works like the :attr:`module` attribute when called + without arguments but it will evaluate the template on every call + rather than caching it. It's also possible to provide + a dict which is then used as context. The arguments are the same + as for the :meth:`new_context` method. + """ + return TemplateModule(self, self.new_context(vars, shared, locals)) + + @property + def module(self): + """The template as module. This is used for imports in the + template runtime but is also useful if one wants to access + exported template variables from the Python layer: + + >>> t = Template('{% macro foo() %}42{% endmacro %}23') + >>> str(t.module) + '23' + >>> t.module.foo() == u'42' + True + """ + if self._module is not None: + return self._module + self._module = rv = self.make_module() + return rv + + def get_corresponding_lineno(self, lineno): + """Return the source line number of a line number in the + generated bytecode as they are not in sync. + """ + for template_line, code_line in reversed(self.debug_info): + if code_line <= lineno: + return template_line + return 1 + + @property + def is_up_to_date(self): + """If this variable is `False` there is a newer version available.""" + if self._uptodate is None: + return True + return self._uptodate() + + @property + def debug_info(self): + """The debug info mapping.""" + return [tuple(imap(int, x.split('='))) for x in + self._debug_info.split('&')] + + def __repr__(self): + if self.name is None: + name = 'memory:%x' % id(self) + else: + name = repr(self.name) + return '<%s %s>' % (self.__class__.__name__, name) + + +@implements_to_string +class TemplateModule(object): + """Represents an imported template. All the exported names of the + template are available as attributes on this object. Additionally + converting it into an unicode- or bytestrings renders the contents. + """ + + def __init__(self, template, context): + self._body_stream = list(template.root_render_func(context)) + self.__dict__.update(context.get_exported()) + self.__name__ = template.name + + def __html__(self): + return Markup(concat(self._body_stream)) + + def __str__(self): + return concat(self._body_stream) + + def __repr__(self): + if self.__name__ is None: + name = 'memory:%x' % id(self) + else: + name = repr(self.__name__) + return '<%s %s>' % (self.__class__.__name__, name) + + +class TemplateExpression(object): + """The :meth:`jinja2.Environment.compile_expression` method returns an + instance of this object. It encapsulates the expression-like access + to the template with an expression it wraps. + """ + + def __init__(self, template, undefined_to_none): + self._template = template + self._undefined_to_none = undefined_to_none + + def __call__(self, *args, **kwargs): + context = self._template.new_context(dict(*args, **kwargs)) + consume(self._template.root_render_func(context)) + rv = context.vars['result'] + if self._undefined_to_none and isinstance(rv, Undefined): + rv = None + return rv + + +@implements_iterator +class TemplateStream(object): + """A template stream works pretty much like an ordinary python generator + but it can buffer multiple items to reduce the number of total iterations. + Per default the output is unbuffered which means that for every unbuffered + instruction in the template one unicode string is yielded. + + If buffering is enabled with a buffer size of 5, five items are combined + into a new unicode string. This is mainly useful if you are streaming + big templates to a client via WSGI which flushes after each iteration. + """ + + def __init__(self, gen): + self._gen = gen + self.disable_buffering() + + def dump(self, fp, encoding=None, errors='strict'): + """Dump the complete stream into a file or file-like object. + Per default unicode strings are written, if you want to encode + before writing specify an `encoding`. + + Example usage:: + + Template('Hello {{ name }}!').stream(name='foo').dump('hello.html') + """ + close = False + if isinstance(fp, string_types): + if encoding is None: + encoding = 'utf-8' + fp = open(fp, 'wb') + close = True + try: + if encoding is not None: + iterable = (x.encode(encoding, errors) for x in self) + else: + iterable = self + if hasattr(fp, 'writelines'): + fp.writelines(iterable) + else: + for item in iterable: + fp.write(item) + finally: + if close: + fp.close() + + def disable_buffering(self): + """Disable the output buffering.""" + self._next = get_next(self._gen) + self.buffered = False + + def enable_buffering(self, size=5): + """Enable buffering. Buffer `size` items before yielding them.""" + if size <= 1: + raise ValueError('buffer size too small') + + def generator(next): + buf = [] + c_size = 0 + push = buf.append + + while 1: + try: + while c_size < size: + c = next() + push(c) + if c: + c_size += 1 + except StopIteration: + if not c_size: + return + yield concat(buf) + del buf[:] + c_size = 0 + + self.buffered = True + self._next = get_next(generator(get_next(self._gen))) + + def __iter__(self): + return self + + def __next__(self): + return self._next() + + +# hook in default template class. if anyone reads this comment: ignore that +# it's possible to use custom templates ;-) +Environment.template_class = Template diff --git a/deps/v8_inspector/deps/jinja2/jinja2/exceptions.py b/deps/v8_inspector/deps/jinja2/jinja2/exceptions.py new file mode 100644 index 00000000000000..c9df6dc7c28a11 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/exceptions.py @@ -0,0 +1,146 @@ +# -*- coding: utf-8 -*- +""" + jinja2.exceptions + ~~~~~~~~~~~~~~~~~ + + Jinja exceptions. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +from jinja2._compat import imap, text_type, PY2, implements_to_string + + +class TemplateError(Exception): + """Baseclass for all template errors.""" + + if PY2: + def __init__(self, message=None): + if message is not None: + message = text_type(message).encode('utf-8') + Exception.__init__(self, message) + + @property + def message(self): + if self.args: + message = self.args[0] + if message is not None: + return message.decode('utf-8', 'replace') + + def __unicode__(self): + return self.message or u'' + else: + def __init__(self, message=None): + Exception.__init__(self, message) + + @property + def message(self): + if self.args: + message = self.args[0] + if message is not None: + return message + + +@implements_to_string +class TemplateNotFound(IOError, LookupError, TemplateError): + """Raised if a template does not exist.""" + + # looks weird, but removes the warning descriptor that just + # bogusly warns us about message being deprecated + message = None + + def __init__(self, name, message=None): + IOError.__init__(self) + if message is None: + message = name + self.message = message + self.name = name + self.templates = [name] + + def __str__(self): + return self.message + + +class TemplatesNotFound(TemplateNotFound): + """Like :class:`TemplateNotFound` but raised if multiple templates + are selected. This is a subclass of :class:`TemplateNotFound` + exception, so just catching the base exception will catch both. + + .. versionadded:: 2.2 + """ + + def __init__(self, names=(), message=None): + if message is None: + message = u'none of the templates given were found: ' + \ + u', '.join(imap(text_type, names)) + TemplateNotFound.__init__(self, names and names[-1] or None, message) + self.templates = list(names) + + +@implements_to_string +class TemplateSyntaxError(TemplateError): + """Raised to tell the user that there is a problem with the template.""" + + def __init__(self, message, lineno, name=None, filename=None): + TemplateError.__init__(self, message) + self.lineno = lineno + self.name = name + self.filename = filename + self.source = None + + # this is set to True if the debug.translate_syntax_error + # function translated the syntax error into a new traceback + self.translated = False + + def __str__(self): + # for translated errors we only return the message + if self.translated: + return self.message + + # otherwise attach some stuff + location = 'line %d' % self.lineno + name = self.filename or self.name + if name: + location = 'File "%s", %s' % (name, location) + lines = [self.message, ' ' + location] + + # if the source is set, add the line to the output + if self.source is not None: + try: + line = self.source.splitlines()[self.lineno - 1] + except IndexError: + line = None + if line: + lines.append(' ' + line.strip()) + + return u'\n'.join(lines) + + +class TemplateAssertionError(TemplateSyntaxError): + """Like a template syntax error, but covers cases where something in the + template caused an error at compile time that wasn't necessarily caused + by a syntax error. However it's a direct subclass of + :exc:`TemplateSyntaxError` and has the same attributes. + """ + + +class TemplateRuntimeError(TemplateError): + """A generic runtime error in the template engine. Under some situations + Jinja may raise this exception. + """ + + +class UndefinedError(TemplateRuntimeError): + """Raised if a template tries to operate on :class:`Undefined`.""" + + +class SecurityError(TemplateRuntimeError): + """Raised if a template tries to do something insecure if the + sandbox is enabled. + """ + + +class FilterArgumentError(TemplateRuntimeError): + """This error is raised if a filter was called with inappropriate + arguments + """ diff --git a/deps/v8_inspector/deps/jinja2/jinja2/ext.py b/deps/v8_inspector/deps/jinja2/jinja2/ext.py new file mode 100644 index 00000000000000..562ab506ffefc9 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/ext.py @@ -0,0 +1,636 @@ +# -*- coding: utf-8 -*- +""" + jinja2.ext + ~~~~~~~~~~ + + Jinja extensions allow to add custom tags similar to the way django custom + tags work. By default two example extensions exist: an i18n and a cache + extension. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD. +""" +from jinja2 import nodes +from jinja2.defaults import BLOCK_START_STRING, \ + BLOCK_END_STRING, VARIABLE_START_STRING, VARIABLE_END_STRING, \ + COMMENT_START_STRING, COMMENT_END_STRING, LINE_STATEMENT_PREFIX, \ + LINE_COMMENT_PREFIX, TRIM_BLOCKS, NEWLINE_SEQUENCE, \ + KEEP_TRAILING_NEWLINE, LSTRIP_BLOCKS +from jinja2.environment import Environment +from jinja2.runtime import concat +from jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError +from jinja2.utils import contextfunction, import_string, Markup +from jinja2._compat import with_metaclass, string_types, iteritems + + +# the only real useful gettext functions for a Jinja template. Note +# that ugettext must be assigned to gettext as Jinja doesn't support +# non unicode strings. +GETTEXT_FUNCTIONS = ('_', 'gettext', 'ngettext') + + +class ExtensionRegistry(type): + """Gives the extension an unique identifier.""" + + def __new__(cls, name, bases, d): + rv = type.__new__(cls, name, bases, d) + rv.identifier = rv.__module__ + '.' + rv.__name__ + return rv + + +class Extension(with_metaclass(ExtensionRegistry, object)): + """Extensions can be used to add extra functionality to the Jinja template + system at the parser level. Custom extensions are bound to an environment + but may not store environment specific data on `self`. The reason for + this is that an extension can be bound to another environment (for + overlays) by creating a copy and reassigning the `environment` attribute. + + As extensions are created by the environment they cannot accept any + arguments for configuration. One may want to work around that by using + a factory function, but that is not possible as extensions are identified + by their import name. The correct way to configure the extension is + storing the configuration values on the environment. Because this way the + environment ends up acting as central configuration storage the + attributes may clash which is why extensions have to ensure that the names + they choose for configuration are not too generic. ``prefix`` for example + is a terrible name, ``fragment_cache_prefix`` on the other hand is a good + name as includes the name of the extension (fragment cache). + """ + + #: if this extension parses this is the list of tags it's listening to. + tags = set() + + #: the priority of that extension. This is especially useful for + #: extensions that preprocess values. A lower value means higher + #: priority. + #: + #: .. versionadded:: 2.4 + priority = 100 + + def __init__(self, environment): + self.environment = environment + + def bind(self, environment): + """Create a copy of this extension bound to another environment.""" + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.environment = environment + return rv + + def preprocess(self, source, name, filename=None): + """This method is called before the actual lexing and can be used to + preprocess the source. The `filename` is optional. The return value + must be the preprocessed source. + """ + return source + + def filter_stream(self, stream): + """It's passed a :class:`~jinja2.lexer.TokenStream` that can be used + to filter tokens returned. This method has to return an iterable of + :class:`~jinja2.lexer.Token`\s, but it doesn't have to return a + :class:`~jinja2.lexer.TokenStream`. + + In the `ext` folder of the Jinja2 source distribution there is a file + called `inlinegettext.py` which implements a filter that utilizes this + method. + """ + return stream + + def parse(self, parser): + """If any of the :attr:`tags` matched this method is called with the + parser as first argument. The token the parser stream is pointing at + is the name token that matched. This method has to return one or a + list of multiple nodes. + """ + raise NotImplementedError() + + def attr(self, name, lineno=None): + """Return an attribute node for the current extension. This is useful + to pass constants on extensions to generated template code. + + :: + + self.attr('_my_attribute', lineno=lineno) + """ + return nodes.ExtensionAttribute(self.identifier, name, lineno=lineno) + + def call_method(self, name, args=None, kwargs=None, dyn_args=None, + dyn_kwargs=None, lineno=None): + """Call a method of the extension. This is a shortcut for + :meth:`attr` + :class:`jinja2.nodes.Call`. + """ + if args is None: + args = [] + if kwargs is None: + kwargs = [] + return nodes.Call(self.attr(name, lineno=lineno), args, kwargs, + dyn_args, dyn_kwargs, lineno=lineno) + + +@contextfunction +def _gettext_alias(__context, *args, **kwargs): + return __context.call(__context.resolve('gettext'), *args, **kwargs) + + +def _make_new_gettext(func): + @contextfunction + def gettext(__context, __string, **variables): + rv = __context.call(func, __string) + if __context.eval_ctx.autoescape: + rv = Markup(rv) + return rv % variables + return gettext + + +def _make_new_ngettext(func): + @contextfunction + def ngettext(__context, __singular, __plural, __num, **variables): + variables.setdefault('num', __num) + rv = __context.call(func, __singular, __plural, __num) + if __context.eval_ctx.autoescape: + rv = Markup(rv) + return rv % variables + return ngettext + + +class InternationalizationExtension(Extension): + """This extension adds gettext support to Jinja2.""" + tags = set(['trans']) + + # TODO: the i18n extension is currently reevaluating values in a few + # situations. Take this example: + # {% trans count=something() %}{{ count }} foo{% pluralize + # %}{{ count }} fooss{% endtrans %} + # something is called twice here. One time for the gettext value and + # the other time for the n-parameter of the ngettext function. + + def __init__(self, environment): + Extension.__init__(self, environment) + environment.globals['_'] = _gettext_alias + environment.extend( + install_gettext_translations=self._install, + install_null_translations=self._install_null, + install_gettext_callables=self._install_callables, + uninstall_gettext_translations=self._uninstall, + extract_translations=self._extract, + newstyle_gettext=False + ) + + def _install(self, translations, newstyle=None): + gettext = getattr(translations, 'ugettext', None) + if gettext is None: + gettext = translations.gettext + ngettext = getattr(translations, 'ungettext', None) + if ngettext is None: + ngettext = translations.ngettext + self._install_callables(gettext, ngettext, newstyle) + + def _install_null(self, newstyle=None): + self._install_callables( + lambda x: x, + lambda s, p, n: (n != 1 and (p,) or (s,))[0], + newstyle + ) + + def _install_callables(self, gettext, ngettext, newstyle=None): + if newstyle is not None: + self.environment.newstyle_gettext = newstyle + if self.environment.newstyle_gettext: + gettext = _make_new_gettext(gettext) + ngettext = _make_new_ngettext(ngettext) + self.environment.globals.update( + gettext=gettext, + ngettext=ngettext + ) + + def _uninstall(self, translations): + for key in 'gettext', 'ngettext': + self.environment.globals.pop(key, None) + + def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS): + if isinstance(source, string_types): + source = self.environment.parse(source) + return extract_from_ast(source, gettext_functions) + + def parse(self, parser): + """Parse a translatable tag.""" + lineno = next(parser.stream).lineno + num_called_num = False + + # find all the variables referenced. Additionally a variable can be + # defined in the body of the trans block too, but this is checked at + # a later state. + plural_expr = None + plural_expr_assignment = None + variables = {} + while parser.stream.current.type != 'block_end': + if variables: + parser.stream.expect('comma') + + # skip colon for python compatibility + if parser.stream.skip_if('colon'): + break + + name = parser.stream.expect('name') + if name.value in variables: + parser.fail('translatable variable %r defined twice.' % + name.value, name.lineno, + exc=TemplateAssertionError) + + # expressions + if parser.stream.current.type == 'assign': + next(parser.stream) + variables[name.value] = var = parser.parse_expression() + else: + variables[name.value] = var = nodes.Name(name.value, 'load') + + if plural_expr is None: + if isinstance(var, nodes.Call): + plural_expr = nodes.Name('_trans', 'load') + variables[name.value] = plural_expr + plural_expr_assignment = nodes.Assign( + nodes.Name('_trans', 'store'), var) + else: + plural_expr = var + num_called_num = name.value == 'num' + + parser.stream.expect('block_end') + + plural = plural_names = None + have_plural = False + referenced = set() + + # now parse until endtrans or pluralize + singular_names, singular = self._parse_block(parser, True) + if singular_names: + referenced.update(singular_names) + if plural_expr is None: + plural_expr = nodes.Name(singular_names[0], 'load') + num_called_num = singular_names[0] == 'num' + + # if we have a pluralize block, we parse that too + if parser.stream.current.test('name:pluralize'): + have_plural = True + next(parser.stream) + if parser.stream.current.type != 'block_end': + name = parser.stream.expect('name') + if name.value not in variables: + parser.fail('unknown variable %r for pluralization' % + name.value, name.lineno, + exc=TemplateAssertionError) + plural_expr = variables[name.value] + num_called_num = name.value == 'num' + parser.stream.expect('block_end') + plural_names, plural = self._parse_block(parser, False) + next(parser.stream) + referenced.update(plural_names) + else: + next(parser.stream) + + # register free names as simple name expressions + for var in referenced: + if var not in variables: + variables[var] = nodes.Name(var, 'load') + + if not have_plural: + plural_expr = None + elif plural_expr is None: + parser.fail('pluralize without variables', lineno) + + node = self._make_node(singular, plural, variables, plural_expr, + bool(referenced), + num_called_num and have_plural) + node.set_lineno(lineno) + if plural_expr_assignment is not None: + return [plural_expr_assignment, node] + else: + return node + + def _parse_block(self, parser, allow_pluralize): + """Parse until the next block tag with a given name.""" + referenced = [] + buf = [] + while 1: + if parser.stream.current.type == 'data': + buf.append(parser.stream.current.value.replace('%', '%%')) + next(parser.stream) + elif parser.stream.current.type == 'variable_begin': + next(parser.stream) + name = parser.stream.expect('name').value + referenced.append(name) + buf.append('%%(%s)s' % name) + parser.stream.expect('variable_end') + elif parser.stream.current.type == 'block_begin': + next(parser.stream) + if parser.stream.current.test('name:endtrans'): + break + elif parser.stream.current.test('name:pluralize'): + if allow_pluralize: + break + parser.fail('a translatable section can have only one ' + 'pluralize section') + parser.fail('control structures in translatable sections are ' + 'not allowed') + elif parser.stream.eos: + parser.fail('unclosed translation block') + else: + assert False, 'internal parser error' + + return referenced, concat(buf) + + def _make_node(self, singular, plural, variables, plural_expr, + vars_referenced, num_called_num): + """Generates a useful node from the data provided.""" + # no variables referenced? no need to escape for old style + # gettext invocations only if there are vars. + if not vars_referenced and not self.environment.newstyle_gettext: + singular = singular.replace('%%', '%') + if plural: + plural = plural.replace('%%', '%') + + # singular only: + if plural_expr is None: + gettext = nodes.Name('gettext', 'load') + node = nodes.Call(gettext, [nodes.Const(singular)], + [], None, None) + + # singular and plural + else: + ngettext = nodes.Name('ngettext', 'load') + node = nodes.Call(ngettext, [ + nodes.Const(singular), + nodes.Const(plural), + plural_expr + ], [], None, None) + + # in case newstyle gettext is used, the method is powerful + # enough to handle the variable expansion and autoescape + # handling itself + if self.environment.newstyle_gettext: + for key, value in iteritems(variables): + # the function adds that later anyways in case num was + # called num, so just skip it. + if num_called_num and key == 'num': + continue + node.kwargs.append(nodes.Keyword(key, value)) + + # otherwise do that here + else: + # mark the return value as safe if we are in an + # environment with autoescaping turned on + node = nodes.MarkSafeIfAutoescape(node) + if variables: + node = nodes.Mod(node, nodes.Dict([ + nodes.Pair(nodes.Const(key), value) + for key, value in variables.items() + ])) + return nodes.Output([node]) + + +class ExprStmtExtension(Extension): + """Adds a `do` tag to Jinja2 that works like the print statement just + that it doesn't print the return value. + """ + tags = set(['do']) + + def parse(self, parser): + node = nodes.ExprStmt(lineno=next(parser.stream).lineno) + node.node = parser.parse_tuple() + return node + + +class LoopControlExtension(Extension): + """Adds break and continue to the template engine.""" + tags = set(['break', 'continue']) + + def parse(self, parser): + token = next(parser.stream) + if token.value == 'break': + return nodes.Break(lineno=token.lineno) + return nodes.Continue(lineno=token.lineno) + + +class WithExtension(Extension): + """Adds support for a django-like with block.""" + tags = set(['with']) + + def parse(self, parser): + node = nodes.Scope(lineno=next(parser.stream).lineno) + assignments = [] + while parser.stream.current.type != 'block_end': + lineno = parser.stream.current.lineno + if assignments: + parser.stream.expect('comma') + target = parser.parse_assign_target() + parser.stream.expect('assign') + expr = parser.parse_expression() + assignments.append(nodes.Assign(target, expr, lineno=lineno)) + node.body = assignments + \ + list(parser.parse_statements(('name:endwith',), + drop_needle=True)) + return node + + +class AutoEscapeExtension(Extension): + """Changes auto escape rules for a scope.""" + tags = set(['autoescape']) + + def parse(self, parser): + node = nodes.ScopedEvalContextModifier(lineno=next(parser.stream).lineno) + node.options = [ + nodes.Keyword('autoescape', parser.parse_expression()) + ] + node.body = parser.parse_statements(('name:endautoescape',), + drop_needle=True) + return nodes.Scope([node]) + + +def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS, + babel_style=True): + """Extract localizable strings from the given template node. Per + default this function returns matches in babel style that means non string + parameters as well as keyword arguments are returned as `None`. This + allows Babel to figure out what you really meant if you are using + gettext functions that allow keyword arguments for placeholder expansion. + If you don't want that behavior set the `babel_style` parameter to `False` + which causes only strings to be returned and parameters are always stored + in tuples. As a consequence invalid gettext calls (calls without a single + string parameter or string parameters after non-string parameters) are + skipped. + + This example explains the behavior: + + >>> from jinja2 import Environment + >>> env = Environment() + >>> node = env.parse('{{ (_("foo"), _(), ngettext("foo", "bar", 42)) }}') + >>> list(extract_from_ast(node)) + [(1, '_', 'foo'), (1, '_', ()), (1, 'ngettext', ('foo', 'bar', None))] + >>> list(extract_from_ast(node, babel_style=False)) + [(1, '_', ('foo',)), (1, 'ngettext', ('foo', 'bar'))] + + For every string found this function yields a ``(lineno, function, + message)`` tuple, where: + + * ``lineno`` is the number of the line on which the string was found, + * ``function`` is the name of the ``gettext`` function used (if the + string was extracted from embedded Python code), and + * ``message`` is the string itself (a ``unicode`` object, or a tuple + of ``unicode`` objects for functions with multiple string arguments). + + This extraction function operates on the AST and is because of that unable + to extract any comments. For comment support you have to use the babel + extraction interface or extract comments yourself. + """ + for node in node.find_all(nodes.Call): + if not isinstance(node.node, nodes.Name) or \ + node.node.name not in gettext_functions: + continue + + strings = [] + for arg in node.args: + if isinstance(arg, nodes.Const) and \ + isinstance(arg.value, string_types): + strings.append(arg.value) + else: + strings.append(None) + + for arg in node.kwargs: + strings.append(None) + if node.dyn_args is not None: + strings.append(None) + if node.dyn_kwargs is not None: + strings.append(None) + + if not babel_style: + strings = tuple(x for x in strings if x is not None) + if not strings: + continue + else: + if len(strings) == 1: + strings = strings[0] + else: + strings = tuple(strings) + yield node.lineno, node.node.name, strings + + +class _CommentFinder(object): + """Helper class to find comments in a token stream. Can only + find comments for gettext calls forwards. Once the comment + from line 4 is found, a comment for line 1 will not return a + usable value. + """ + + def __init__(self, tokens, comment_tags): + self.tokens = tokens + self.comment_tags = comment_tags + self.offset = 0 + self.last_lineno = 0 + + def find_backwards(self, offset): + try: + for _, token_type, token_value in \ + reversed(self.tokens[self.offset:offset]): + if token_type in ('comment', 'linecomment'): + try: + prefix, comment = token_value.split(None, 1) + except ValueError: + continue + if prefix in self.comment_tags: + return [comment.rstrip()] + return [] + finally: + self.offset = offset + + def find_comments(self, lineno): + if not self.comment_tags or self.last_lineno > lineno: + return [] + for idx, (token_lineno, _, _) in enumerate(self.tokens[self.offset:]): + if token_lineno > lineno: + return self.find_backwards(self.offset + idx) + return self.find_backwards(len(self.tokens)) + + +def babel_extract(fileobj, keywords, comment_tags, options): + """Babel extraction method for Jinja templates. + + .. versionchanged:: 2.3 + Basic support for translation comments was added. If `comment_tags` + is now set to a list of keywords for extraction, the extractor will + try to find the best preceeding comment that begins with one of the + keywords. For best results, make sure to not have more than one + gettext call in one line of code and the matching comment in the + same line or the line before. + + .. versionchanged:: 2.5.1 + The `newstyle_gettext` flag can be set to `True` to enable newstyle + gettext calls. + + .. versionchanged:: 2.7 + A `silent` option can now be provided. If set to `False` template + syntax errors are propagated instead of being ignored. + + :param fileobj: the file-like object the messages should be extracted from + :param keywords: a list of keywords (i.e. function names) that should be + recognized as translation functions + :param comment_tags: a list of translator tags to search for and include + in the results. + :param options: a dictionary of additional options (optional) + :return: an iterator over ``(lineno, funcname, message, comments)`` tuples. + (comments will be empty currently) + """ + extensions = set() + for extension in options.get('extensions', '').split(','): + extension = extension.strip() + if not extension: + continue + extensions.add(import_string(extension)) + if InternationalizationExtension not in extensions: + extensions.add(InternationalizationExtension) + + def getbool(options, key, default=False): + return options.get(key, str(default)).lower() in \ + ('1', 'on', 'yes', 'true') + + silent = getbool(options, 'silent', True) + environment = Environment( + options.get('block_start_string', BLOCK_START_STRING), + options.get('block_end_string', BLOCK_END_STRING), + options.get('variable_start_string', VARIABLE_START_STRING), + options.get('variable_end_string', VARIABLE_END_STRING), + options.get('comment_start_string', COMMENT_START_STRING), + options.get('comment_end_string', COMMENT_END_STRING), + options.get('line_statement_prefix') or LINE_STATEMENT_PREFIX, + options.get('line_comment_prefix') or LINE_COMMENT_PREFIX, + getbool(options, 'trim_blocks', TRIM_BLOCKS), + getbool(options, 'lstrip_blocks', LSTRIP_BLOCKS), + NEWLINE_SEQUENCE, + getbool(options, 'keep_trailing_newline', KEEP_TRAILING_NEWLINE), + frozenset(extensions), + cache_size=0, + auto_reload=False + ) + + if getbool(options, 'newstyle_gettext'): + environment.newstyle_gettext = True + + source = fileobj.read().decode(options.get('encoding', 'utf-8')) + try: + node = environment.parse(source) + tokens = list(environment.lex(environment.preprocess(source))) + except TemplateSyntaxError as e: + if not silent: + raise + # skip templates with syntax errors + return + + finder = _CommentFinder(tokens, comment_tags) + for lineno, func, message in extract_from_ast(node, keywords): + yield lineno, func, message, finder.find_comments(lineno) + + +#: nicer import names +i18n = InternationalizationExtension +do = ExprStmtExtension +loopcontrols = LoopControlExtension +with_ = WithExtension +autoescape = AutoEscapeExtension diff --git a/deps/v8_inspector/deps/jinja2/jinja2/filters.py b/deps/v8_inspector/deps/jinja2/jinja2/filters.py new file mode 100644 index 00000000000000..cd89f6ffef3639 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/filters.py @@ -0,0 +1,996 @@ +# -*- coding: utf-8 -*- +""" + jinja2.filters + ~~~~~~~~~~~~~~ + + Bundled jinja filters. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import re +import math + +from random import choice +from operator import itemgetter +from itertools import groupby +from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode, \ + unicode_urlencode +from jinja2.runtime import Undefined +from jinja2.exceptions import FilterArgumentError +from jinja2._compat import imap, string_types, text_type, iteritems + + +_word_re = re.compile(r'\w+(?u)') + + +def contextfilter(f): + """Decorator for marking context dependent filters. The current + :class:`Context` will be passed as first argument. + """ + f.contextfilter = True + return f + + +def evalcontextfilter(f): + """Decorator for marking eval-context dependent filters. An eval + context object is passed as first argument. For more information + about the eval context, see :ref:`eval-context`. + + .. versionadded:: 2.4 + """ + f.evalcontextfilter = True + return f + + +def environmentfilter(f): + """Decorator for marking environment dependent filters. The current + :class:`Environment` is passed to the filter as first argument. + """ + f.environmentfilter = True + return f + + +def make_attrgetter(environment, attribute): + """Returns a callable that looks up the given attribute from a + passed object with the rules of the environment. Dots are allowed + to access attributes of attributes. Integer parts in paths are + looked up as integers. + """ + if not isinstance(attribute, string_types) \ + or ('.' not in attribute and not attribute.isdigit()): + return lambda x: environment.getitem(x, attribute) + attribute = attribute.split('.') + def attrgetter(item): + for part in attribute: + if part.isdigit(): + part = int(part) + item = environment.getitem(item, part) + return item + return attrgetter + + +def do_forceescape(value): + """Enforce HTML escaping. This will probably double escape variables.""" + if hasattr(value, '__html__'): + value = value.__html__() + return escape(text_type(value)) + + +def do_urlencode(value): + """Escape strings for use in URLs (uses UTF-8 encoding). It accepts both + dictionaries and regular strings as well as pairwise iterables. + + .. versionadded:: 2.7 + """ + itemiter = None + if isinstance(value, dict): + itemiter = iteritems(value) + elif not isinstance(value, string_types): + try: + itemiter = iter(value) + except TypeError: + pass + if itemiter is None: + return unicode_urlencode(value) + return u'&'.join(unicode_urlencode(k) + '=' + + unicode_urlencode(v, for_qs=True) + for k, v in itemiter) + + +@evalcontextfilter +def do_replace(eval_ctx, s, old, new, count=None): + """Return a copy of the value with all occurrences of a substring + replaced with a new one. The first argument is the substring + that should be replaced, the second is the replacement string. + If the optional third argument ``count`` is given, only the first + ``count`` occurrences are replaced: + + .. sourcecode:: jinja + + {{ "Hello World"|replace("Hello", "Goodbye") }} + -> Goodbye World + + {{ "aaaaargh"|replace("a", "d'oh, ", 2) }} + -> d'oh, d'oh, aaargh + """ + if count is None: + count = -1 + if not eval_ctx.autoescape: + return text_type(s).replace(text_type(old), text_type(new), count) + if hasattr(old, '__html__') or hasattr(new, '__html__') and \ + not hasattr(s, '__html__'): + s = escape(s) + else: + s = soft_unicode(s) + return s.replace(soft_unicode(old), soft_unicode(new), count) + + +def do_upper(s): + """Convert a value to uppercase.""" + return soft_unicode(s).upper() + + +def do_lower(s): + """Convert a value to lowercase.""" + return soft_unicode(s).lower() + + +@evalcontextfilter +def do_xmlattr(_eval_ctx, d, autospace=True): + """Create an SGML/XML attribute string based on the items in a dict. + All values that are neither `none` nor `undefined` are automatically + escaped: + + .. sourcecode:: html+jinja + + <ul{{ {'class': 'my_list', 'missing': none, + 'id': 'list-%d'|format(variable)}|xmlattr }}> + ... + </ul> + + Results in something like this: + + .. sourcecode:: html + + <ul class="my_list" id="list-42"> + ... + </ul> + + As you can see it automatically prepends a space in front of the item + if the filter returned something unless the second parameter is false. + """ + rv = u' '.join( + u'%s="%s"' % (escape(key), escape(value)) + for key, value in iteritems(d) + if value is not None and not isinstance(value, Undefined) + ) + if autospace and rv: + rv = u' ' + rv + if _eval_ctx.autoescape: + rv = Markup(rv) + return rv + + +def do_capitalize(s): + """Capitalize a value. The first character will be uppercase, all others + lowercase. + """ + return soft_unicode(s).capitalize() + + +def do_title(s): + """Return a titlecased version of the value. I.e. words will start with + uppercase letters, all remaining characters are lowercase. + """ + rv = [] + for item in re.compile(r'([-\s]+)(?u)').split(soft_unicode(s)): + if not item: + continue + rv.append(item[0].upper() + item[1:].lower()) + return ''.join(rv) + + +def do_dictsort(value, case_sensitive=False, by='key'): + """Sort a dict and yield (key, value) pairs. Because python dicts are + unsorted you may want to use this function to order them by either + key or value: + + .. sourcecode:: jinja + + {% for item in mydict|dictsort %} + sort the dict by key, case insensitive + + {% for item in mydict|dictsort(true) %} + sort the dict by key, case sensitive + + {% for item in mydict|dictsort(false, 'value') %} + sort the dict by value, case insensitive + """ + if by == 'key': + pos = 0 + elif by == 'value': + pos = 1 + else: + raise FilterArgumentError('You can only sort by either ' + '"key" or "value"') + def sort_func(item): + value = item[pos] + if isinstance(value, string_types) and not case_sensitive: + value = value.lower() + return value + + return sorted(value.items(), key=sort_func) + + +@environmentfilter +def do_sort(environment, value, reverse=False, case_sensitive=False, + attribute=None): + """Sort an iterable. Per default it sorts ascending, if you pass it + true as first argument it will reverse the sorting. + + If the iterable is made of strings the third parameter can be used to + control the case sensitiveness of the comparison which is disabled by + default. + + .. sourcecode:: jinja + + {% for item in iterable|sort %} + ... + {% endfor %} + + It is also possible to sort by an attribute (for example to sort + by the date of an object) by specifying the `attribute` parameter: + + .. sourcecode:: jinja + + {% for item in iterable|sort(attribute='date') %} + ... + {% endfor %} + + .. versionchanged:: 2.6 + The `attribute` parameter was added. + """ + if not case_sensitive: + def sort_func(item): + if isinstance(item, string_types): + item = item.lower() + return item + else: + sort_func = None + if attribute is not None: + getter = make_attrgetter(environment, attribute) + def sort_func(item, processor=sort_func or (lambda x: x)): + return processor(getter(item)) + return sorted(value, key=sort_func, reverse=reverse) + + +def do_default(value, default_value=u'', boolean=False): + """If the value is undefined it will return the passed default value, + otherwise the value of the variable: + + .. sourcecode:: jinja + + {{ my_variable|default('my_variable is not defined') }} + + This will output the value of ``my_variable`` if the variable was + defined, otherwise ``'my_variable is not defined'``. If you want + to use default with variables that evaluate to false you have to + set the second parameter to `true`: + + .. sourcecode:: jinja + + {{ ''|default('the string was empty', true) }} + """ + if isinstance(value, Undefined) or (boolean and not value): + return default_value + return value + + +@evalcontextfilter +def do_join(eval_ctx, value, d=u'', attribute=None): + """Return a string which is the concatenation of the strings in the + sequence. The separator between elements is an empty string per + default, you can define it with the optional parameter: + + .. sourcecode:: jinja + + {{ [1, 2, 3]|join('|') }} + -> 1|2|3 + + {{ [1, 2, 3]|join }} + -> 123 + + It is also possible to join certain attributes of an object: + + .. sourcecode:: jinja + + {{ users|join(', ', attribute='username') }} + + .. versionadded:: 2.6 + The `attribute` parameter was added. + """ + if attribute is not None: + value = imap(make_attrgetter(eval_ctx.environment, attribute), value) + + # no automatic escaping? joining is a lot eaiser then + if not eval_ctx.autoescape: + return text_type(d).join(imap(text_type, value)) + + # if the delimiter doesn't have an html representation we check + # if any of the items has. If yes we do a coercion to Markup + if not hasattr(d, '__html__'): + value = list(value) + do_escape = False + for idx, item in enumerate(value): + if hasattr(item, '__html__'): + do_escape = True + else: + value[idx] = text_type(item) + if do_escape: + d = escape(d) + else: + d = text_type(d) + return d.join(value) + + # no html involved, to normal joining + return soft_unicode(d).join(imap(soft_unicode, value)) + + +def do_center(value, width=80): + """Centers the value in a field of a given width.""" + return text_type(value).center(width) + + +@environmentfilter +def do_first(environment, seq): + """Return the first item of a sequence.""" + try: + return next(iter(seq)) + except StopIteration: + return environment.undefined('No first item, sequence was empty.') + + +@environmentfilter +def do_last(environment, seq): + """Return the last item of a sequence.""" + try: + return next(iter(reversed(seq))) + except StopIteration: + return environment.undefined('No last item, sequence was empty.') + + +@environmentfilter +def do_random(environment, seq): + """Return a random item from the sequence.""" + try: + return choice(seq) + except IndexError: + return environment.undefined('No random item, sequence was empty.') + + +def do_filesizeformat(value, binary=False): + """Format the value like a 'human-readable' file size (i.e. 13 kB, + 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega, + Giga, etc.), if the second parameter is set to `True` the binary + prefixes are used (Mebi, Gibi). + """ + bytes = float(value) + base = binary and 1024 or 1000 + prefixes = [ + (binary and 'KiB' or 'kB'), + (binary and 'MiB' or 'MB'), + (binary and 'GiB' or 'GB'), + (binary and 'TiB' or 'TB'), + (binary and 'PiB' or 'PB'), + (binary and 'EiB' or 'EB'), + (binary and 'ZiB' or 'ZB'), + (binary and 'YiB' or 'YB') + ] + if bytes == 1: + return '1 Byte' + elif bytes < base: + return '%d Bytes' % bytes + else: + for i, prefix in enumerate(prefixes): + unit = base ** (i + 2) + if bytes < unit: + return '%.1f %s' % ((base * bytes / unit), prefix) + return '%.1f %s' % ((base * bytes / unit), prefix) + + +def do_pprint(value, verbose=False): + """Pretty print a variable. Useful for debugging. + + With Jinja 1.2 onwards you can pass it a parameter. If this parameter + is truthy the output will be more verbose (this requires `pretty`) + """ + return pformat(value, verbose=verbose) + + +@evalcontextfilter +def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False, + target=None): + """Converts URLs in plain text into clickable links. + + If you pass the filter an additional integer it will shorten the urls + to that number. Also a third argument exists that makes the urls + "nofollow": + + .. sourcecode:: jinja + + {{ mytext|urlize(40, true) }} + links are shortened to 40 chars and defined with rel="nofollow" + + If *target* is specified, the ``target`` attribute will be added to the + ``<a>`` tag: + + .. sourcecode:: jinja + + {{ mytext|urlize(40, target='_blank') }} + + .. versionchanged:: 2.8+ + The *target* parameter was added. + """ + rv = urlize(value, trim_url_limit, nofollow, target) + if eval_ctx.autoescape: + rv = Markup(rv) + return rv + + +def do_indent(s, width=4, indentfirst=False): + """Return a copy of the passed string, each line indented by + 4 spaces. The first line is not indented. If you want to + change the number of spaces or indent the first line too + you can pass additional parameters to the filter: + + .. sourcecode:: jinja + + {{ mytext|indent(2, true) }} + indent by two spaces and indent the first line too. + """ + indention = u' ' * width + rv = (u'\n' + indention).join(s.splitlines()) + if indentfirst: + rv = indention + rv + return rv + + +def do_truncate(s, length=255, killwords=False, end='...'): + """Return a truncated copy of the string. The length is specified + with the first parameter which defaults to ``255``. If the second + parameter is ``true`` the filter will cut the text at length. Otherwise + it will discard the last word. If the text was in fact + truncated it will append an ellipsis sign (``"..."``). If you want a + different ellipsis sign than ``"..."`` you can specify it using the + third parameter. + + .. sourcecode:: jinja + + {{ "foo bar baz"|truncate(9) }} + -> "foo ..." + {{ "foo bar baz"|truncate(9, True) }} + -> "foo ba..." + + """ + if len(s) <= length: + return s + elif killwords: + return s[:length - len(end)] + end + + result = s[:length - len(end)].rsplit(' ', 1)[0] + if len(result) < length: + result += ' ' + return result + end + + +@environmentfilter +def do_wordwrap(environment, s, width=79, break_long_words=True, + wrapstring=None): + """ + Return a copy of the string passed to the filter wrapped after + ``79`` characters. You can override this default using the first + parameter. If you set the second parameter to `false` Jinja will not + split words apart if they are longer than `width`. By default, the newlines + will be the default newlines for the environment, but this can be changed + using the wrapstring keyword argument. + + .. versionadded:: 2.7 + Added support for the `wrapstring` parameter. + """ + if not wrapstring: + wrapstring = environment.newline_sequence + import textwrap + return wrapstring.join(textwrap.wrap(s, width=width, expand_tabs=False, + replace_whitespace=False, + break_long_words=break_long_words)) + + +def do_wordcount(s): + """Count the words in that string.""" + return len(_word_re.findall(s)) + + +def do_int(value, default=0, base=10): + """Convert the value into an integer. If the + conversion doesn't work it will return ``0``. You can + override this default using the first parameter. You + can also override the default base (10) in the second + parameter, which handles input with prefixes such as + 0b, 0o and 0x for bases 2, 8 and 16 respectively. + """ + try: + return int(value, base) + except (TypeError, ValueError): + # this quirk is necessary so that "42.23"|int gives 42. + try: + return int(float(value)) + except (TypeError, ValueError): + return default + + +def do_float(value, default=0.0): + """Convert the value into a floating point number. If the + conversion doesn't work it will return ``0.0``. You can + override this default using the first parameter. + """ + try: + return float(value) + except (TypeError, ValueError): + return default + + +def do_format(value, *args, **kwargs): + """ + Apply python string formatting on an object: + + .. sourcecode:: jinja + + {{ "%s - %s"|format("Hello?", "Foo!") }} + -> Hello? - Foo! + """ + if args and kwargs: + raise FilterArgumentError('can\'t handle positional and keyword ' + 'arguments at the same time') + return soft_unicode(value) % (kwargs or args) + + +def do_trim(value): + """Strip leading and trailing whitespace.""" + return soft_unicode(value).strip() + + +def do_striptags(value): + """Strip SGML/XML tags and replace adjacent whitespace by one space. + """ + if hasattr(value, '__html__'): + value = value.__html__() + return Markup(text_type(value)).striptags() + + +def do_slice(value, slices, fill_with=None): + """Slice an iterator and return a list of lists containing + those items. Useful if you want to create a div containing + three ul tags that represent columns: + + .. sourcecode:: html+jinja + + <div class="columwrapper"> + {%- for column in items|slice(3) %} + <ul class="column-{{ loop.index }}"> + {%- for item in column %} + <li>{{ item }}</li> + {%- endfor %} + </ul> + {%- endfor %} + </div> + + If you pass it a second argument it's used to fill missing + values on the last iteration. + """ + seq = list(value) + length = len(seq) + items_per_slice = length // slices + slices_with_extra = length % slices + offset = 0 + for slice_number in range(slices): + start = offset + slice_number * items_per_slice + if slice_number < slices_with_extra: + offset += 1 + end = offset + (slice_number + 1) * items_per_slice + tmp = seq[start:end] + if fill_with is not None and slice_number >= slices_with_extra: + tmp.append(fill_with) + yield tmp + + +def do_batch(value, linecount, fill_with=None): + """ + A filter that batches items. It works pretty much like `slice` + just the other way round. It returns a list of lists with the + given number of items. If you provide a second parameter this + is used to fill up missing items. See this example: + + .. sourcecode:: html+jinja + + <table> + {%- for row in items|batch(3, ' ') %} + <tr> + {%- for column in row %} + <td>{{ column }}</td> + {%- endfor %} + </tr> + {%- endfor %} + </table> + """ + tmp = [] + for item in value: + if len(tmp) == linecount: + yield tmp + tmp = [] + tmp.append(item) + if tmp: + if fill_with is not None and len(tmp) < linecount: + tmp += [fill_with] * (linecount - len(tmp)) + yield tmp + + +def do_round(value, precision=0, method='common'): + """Round the number to a given precision. The first + parameter specifies the precision (default is ``0``), the + second the rounding method: + + - ``'common'`` rounds either up or down + - ``'ceil'`` always rounds up + - ``'floor'`` always rounds down + + If you don't specify a method ``'common'`` is used. + + .. sourcecode:: jinja + + {{ 42.55|round }} + -> 43.0 + {{ 42.55|round(1, 'floor') }} + -> 42.5 + + Note that even if rounded to 0 precision, a float is returned. If + you need a real integer, pipe it through `int`: + + .. sourcecode:: jinja + + {{ 42.55|round|int }} + -> 43 + """ + if not method in ('common', 'ceil', 'floor'): + raise FilterArgumentError('method must be common, ceil or floor') + if method == 'common': + return round(value, precision) + func = getattr(math, method) + return func(value * (10 ** precision)) / (10 ** precision) + + +@environmentfilter +def do_groupby(environment, value, attribute): + """Group a sequence of objects by a common attribute. + + If you for example have a list of dicts or objects that represent persons + with `gender`, `first_name` and `last_name` attributes and you want to + group all users by genders you can do something like the following + snippet: + + .. sourcecode:: html+jinja + + <ul> + {% for group in persons|groupby('gender') %} + <li>{{ group.grouper }}<ul> + {% for person in group.list %} + <li>{{ person.first_name }} {{ person.last_name }}</li> + {% endfor %}</ul></li> + {% endfor %} + </ul> + + Additionally it's possible to use tuple unpacking for the grouper and + list: + + .. sourcecode:: html+jinja + + <ul> + {% for grouper, list in persons|groupby('gender') %} + ... + {% endfor %} + </ul> + + As you can see the item we're grouping by is stored in the `grouper` + attribute and the `list` contains all the objects that have this grouper + in common. + + .. versionchanged:: 2.6 + It's now possible to use dotted notation to group by the child + attribute of another attribute. + """ + expr = make_attrgetter(environment, attribute) + return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr))) + + +class _GroupTuple(tuple): + __slots__ = () + grouper = property(itemgetter(0)) + list = property(itemgetter(1)) + + def __new__(cls, xxx_todo_changeme): + (key, value) = xxx_todo_changeme + return tuple.__new__(cls, (key, list(value))) + + +@environmentfilter +def do_sum(environment, iterable, attribute=None, start=0): + """Returns the sum of a sequence of numbers plus the value of parameter + 'start' (which defaults to 0). When the sequence is empty it returns + start. + + It is also possible to sum up only certain attributes: + + .. sourcecode:: jinja + + Total: {{ items|sum(attribute='price') }} + + .. versionchanged:: 2.6 + The `attribute` parameter was added to allow suming up over + attributes. Also the `start` parameter was moved on to the right. + """ + if attribute is not None: + iterable = imap(make_attrgetter(environment, attribute), iterable) + return sum(iterable, start) + + +def do_list(value): + """Convert the value into a list. If it was a string the returned list + will be a list of characters. + """ + return list(value) + + +def do_mark_safe(value): + """Mark the value as safe which means that in an environment with automatic + escaping enabled this variable will not be escaped. + """ + return Markup(value) + + +def do_mark_unsafe(value): + """Mark a value as unsafe. This is the reverse operation for :func:`safe`.""" + return text_type(value) + + +def do_reverse(value): + """Reverse the object or return an iterator that iterates over it the other + way round. + """ + if isinstance(value, string_types): + return value[::-1] + try: + return reversed(value) + except TypeError: + try: + rv = list(value) + rv.reverse() + return rv + except TypeError: + raise FilterArgumentError('argument must be iterable') + + +@environmentfilter +def do_attr(environment, obj, name): + """Get an attribute of an object. ``foo|attr("bar")`` works like + ``foo.bar`` just that always an attribute is returned and items are not + looked up. + + See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details. + """ + try: + name = str(name) + except UnicodeError: + pass + else: + try: + value = getattr(obj, name) + except AttributeError: + pass + else: + if environment.sandboxed and not \ + environment.is_safe_attribute(obj, name, value): + return environment.unsafe_undefined(obj, name) + return value + return environment.undefined(obj=obj, name=name) + + +@contextfilter +def do_map(*args, **kwargs): + """Applies a filter on a sequence of objects or looks up an attribute. + This is useful when dealing with lists of objects but you are really + only interested in a certain value of it. + + The basic usage is mapping on an attribute. Imagine you have a list + of users but you are only interested in a list of usernames: + + .. sourcecode:: jinja + + Users on this page: {{ users|map(attribute='username')|join(', ') }} + + Alternatively you can let it invoke a filter by passing the name of the + filter and the arguments afterwards. A good example would be applying a + text conversion filter on a sequence: + + .. sourcecode:: jinja + + Users on this page: {{ titles|map('lower')|join(', ') }} + + .. versionadded:: 2.7 + """ + context = args[0] + seq = args[1] + + if len(args) == 2 and 'attribute' in kwargs: + attribute = kwargs.pop('attribute') + if kwargs: + raise FilterArgumentError('Unexpected keyword argument %r' % + next(iter(kwargs))) + func = make_attrgetter(context.environment, attribute) + else: + try: + name = args[2] + args = args[3:] + except LookupError: + raise FilterArgumentError('map requires a filter argument') + func = lambda item: context.environment.call_filter( + name, item, args, kwargs, context=context) + + if seq: + for item in seq: + yield func(item) + + +@contextfilter +def do_select(*args, **kwargs): + """Filters a sequence of objects by applying a test to the object and only + selecting the ones with the test succeeding. + + Example usage: + + .. sourcecode:: jinja + + {{ numbers|select("odd") }} + {{ numbers|select("odd") }} + + .. versionadded:: 2.7 + """ + return _select_or_reject(args, kwargs, lambda x: x, False) + + +@contextfilter +def do_reject(*args, **kwargs): + """Filters a sequence of objects by applying a test to the object and + rejecting the ones with the test succeeding. + + Example usage: + + .. sourcecode:: jinja + + {{ numbers|reject("odd") }} + + .. versionadded:: 2.7 + """ + return _select_or_reject(args, kwargs, lambda x: not x, False) + + +@contextfilter +def do_selectattr(*args, **kwargs): + """Filters a sequence of objects by applying a test to an attribute of an + object and only selecting the ones with the test succeeding. + + Example usage: + + .. sourcecode:: jinja + + {{ users|selectattr("is_active") }} + {{ users|selectattr("email", "none") }} + + .. versionadded:: 2.7 + """ + return _select_or_reject(args, kwargs, lambda x: x, True) + + +@contextfilter +def do_rejectattr(*args, **kwargs): + """Filters a sequence of objects by applying a test to an attribute of an + object or the attribute and rejecting the ones with the test succeeding. + + .. sourcecode:: jinja + + {{ users|rejectattr("is_active") }} + {{ users|rejectattr("email", "none") }} + + .. versionadded:: 2.7 + """ + return _select_or_reject(args, kwargs, lambda x: not x, True) + + +def _select_or_reject(args, kwargs, modfunc, lookup_attr): + context = args[0] + seq = args[1] + if lookup_attr: + try: + attr = args[2] + except LookupError: + raise FilterArgumentError('Missing parameter for attribute name') + transfunc = make_attrgetter(context.environment, attr) + off = 1 + else: + off = 0 + transfunc = lambda x: x + + try: + name = args[2 + off] + args = args[3 + off:] + func = lambda item: context.environment.call_test( + name, item, args, kwargs) + except LookupError: + func = bool + + if seq: + for item in seq: + if modfunc(func(transfunc(item))): + yield item + + +FILTERS = { + 'abs': abs, + 'attr': do_attr, + 'batch': do_batch, + 'capitalize': do_capitalize, + 'center': do_center, + 'count': len, + 'd': do_default, + 'default': do_default, + 'dictsort': do_dictsort, + 'e': escape, + 'escape': escape, + 'filesizeformat': do_filesizeformat, + 'first': do_first, + 'float': do_float, + 'forceescape': do_forceescape, + 'format': do_format, + 'groupby': do_groupby, + 'indent': do_indent, + 'int': do_int, + 'join': do_join, + 'last': do_last, + 'length': len, + 'list': do_list, + 'lower': do_lower, + 'map': do_map, + 'pprint': do_pprint, + 'random': do_random, + 'reject': do_reject, + 'rejectattr': do_rejectattr, + 'replace': do_replace, + 'reverse': do_reverse, + 'round': do_round, + 'safe': do_mark_safe, + 'select': do_select, + 'selectattr': do_selectattr, + 'slice': do_slice, + 'sort': do_sort, + 'string': soft_unicode, + 'striptags': do_striptags, + 'sum': do_sum, + 'title': do_title, + 'trim': do_trim, + 'truncate': do_truncate, + 'upper': do_upper, + 'urlencode': do_urlencode, + 'urlize': do_urlize, + 'wordcount': do_wordcount, + 'wordwrap': do_wordwrap, + 'xmlattr': do_xmlattr, +} diff --git a/deps/v8_inspector/deps/jinja2/jinja2/lexer.py b/deps/v8_inspector/deps/jinja2/jinja2/lexer.py new file mode 100644 index 00000000000000..c8dac214eddd80 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/lexer.py @@ -0,0 +1,734 @@ +# -*- coding: utf-8 -*- +""" + jinja2.lexer + ~~~~~~~~~~~~ + + This module implements a Jinja / Python combination lexer. The + `Lexer` class provided by this module is used to do some preprocessing + for Jinja. + + On the one hand it filters out invalid operators like the bitshift + operators we don't allow in templates. On the other hand it separates + template code and python code in expressions. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import re + +from operator import itemgetter +from collections import deque +from jinja2.exceptions import TemplateSyntaxError +from jinja2.utils import LRUCache +from jinja2._compat import iteritems, implements_iterator, text_type, \ + intern, PY2 + + +# cache for the lexers. Exists in order to be able to have multiple +# environments with the same lexer +_lexer_cache = LRUCache(50) + +# static regular expressions +whitespace_re = re.compile(r'\s+', re.U) +string_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'" + r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S) +integer_re = re.compile(r'\d+') + +# we use the unicode identifier rule if this python version is able +# to handle unicode identifiers, otherwise the standard ASCII one. +try: + compile('föö', '<unknown>', 'eval') +except SyntaxError: + name_re = re.compile(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b') +else: + from jinja2 import _stringdefs + name_re = re.compile(r'[%s][%s]*' % (_stringdefs.xid_start, + _stringdefs.xid_continue)) + +float_re = re.compile(r'(?<!\.)\d+\.\d+') +newline_re = re.compile(r'(\r\n|\r|\n)') + +# internal the tokens and keep references to them +TOKEN_ADD = intern('add') +TOKEN_ASSIGN = intern('assign') +TOKEN_COLON = intern('colon') +TOKEN_COMMA = intern('comma') +TOKEN_DIV = intern('div') +TOKEN_DOT = intern('dot') +TOKEN_EQ = intern('eq') +TOKEN_FLOORDIV = intern('floordiv') +TOKEN_GT = intern('gt') +TOKEN_GTEQ = intern('gteq') +TOKEN_LBRACE = intern('lbrace') +TOKEN_LBRACKET = intern('lbracket') +TOKEN_LPAREN = intern('lparen') +TOKEN_LT = intern('lt') +TOKEN_LTEQ = intern('lteq') +TOKEN_MOD = intern('mod') +TOKEN_MUL = intern('mul') +TOKEN_NE = intern('ne') +TOKEN_PIPE = intern('pipe') +TOKEN_POW = intern('pow') +TOKEN_RBRACE = intern('rbrace') +TOKEN_RBRACKET = intern('rbracket') +TOKEN_RPAREN = intern('rparen') +TOKEN_SEMICOLON = intern('semicolon') +TOKEN_SUB = intern('sub') +TOKEN_TILDE = intern('tilde') +TOKEN_WHITESPACE = intern('whitespace') +TOKEN_FLOAT = intern('float') +TOKEN_INTEGER = intern('integer') +TOKEN_NAME = intern('name') +TOKEN_STRING = intern('string') +TOKEN_OPERATOR = intern('operator') +TOKEN_BLOCK_BEGIN = intern('block_begin') +TOKEN_BLOCK_END = intern('block_end') +TOKEN_VARIABLE_BEGIN = intern('variable_begin') +TOKEN_VARIABLE_END = intern('variable_end') +TOKEN_RAW_BEGIN = intern('raw_begin') +TOKEN_RAW_END = intern('raw_end') +TOKEN_COMMENT_BEGIN = intern('comment_begin') +TOKEN_COMMENT_END = intern('comment_end') +TOKEN_COMMENT = intern('comment') +TOKEN_LINESTATEMENT_BEGIN = intern('linestatement_begin') +TOKEN_LINESTATEMENT_END = intern('linestatement_end') +TOKEN_LINECOMMENT_BEGIN = intern('linecomment_begin') +TOKEN_LINECOMMENT_END = intern('linecomment_end') +TOKEN_LINECOMMENT = intern('linecomment') +TOKEN_DATA = intern('data') +TOKEN_INITIAL = intern('initial') +TOKEN_EOF = intern('eof') + +# bind operators to token types +operators = { + '+': TOKEN_ADD, + '-': TOKEN_SUB, + '/': TOKEN_DIV, + '//': TOKEN_FLOORDIV, + '*': TOKEN_MUL, + '%': TOKEN_MOD, + '**': TOKEN_POW, + '~': TOKEN_TILDE, + '[': TOKEN_LBRACKET, + ']': TOKEN_RBRACKET, + '(': TOKEN_LPAREN, + ')': TOKEN_RPAREN, + '{': TOKEN_LBRACE, + '}': TOKEN_RBRACE, + '==': TOKEN_EQ, + '!=': TOKEN_NE, + '>': TOKEN_GT, + '>=': TOKEN_GTEQ, + '<': TOKEN_LT, + '<=': TOKEN_LTEQ, + '=': TOKEN_ASSIGN, + '.': TOKEN_DOT, + ':': TOKEN_COLON, + '|': TOKEN_PIPE, + ',': TOKEN_COMMA, + ';': TOKEN_SEMICOLON +} + +reverse_operators = dict([(v, k) for k, v in iteritems(operators)]) +assert len(operators) == len(reverse_operators), 'operators dropped' +operator_re = re.compile('(%s)' % '|'.join(re.escape(x) for x in + sorted(operators, key=lambda x: -len(x)))) + +ignored_tokens = frozenset([TOKEN_COMMENT_BEGIN, TOKEN_COMMENT, + TOKEN_COMMENT_END, TOKEN_WHITESPACE, + TOKEN_LINECOMMENT_BEGIN, TOKEN_LINECOMMENT_END, + TOKEN_LINECOMMENT]) +ignore_if_empty = frozenset([TOKEN_WHITESPACE, TOKEN_DATA, + TOKEN_COMMENT, TOKEN_LINECOMMENT]) + + +def _describe_token_type(token_type): + if token_type in reverse_operators: + return reverse_operators[token_type] + return { + TOKEN_COMMENT_BEGIN: 'begin of comment', + TOKEN_COMMENT_END: 'end of comment', + TOKEN_COMMENT: 'comment', + TOKEN_LINECOMMENT: 'comment', + TOKEN_BLOCK_BEGIN: 'begin of statement block', + TOKEN_BLOCK_END: 'end of statement block', + TOKEN_VARIABLE_BEGIN: 'begin of print statement', + TOKEN_VARIABLE_END: 'end of print statement', + TOKEN_LINESTATEMENT_BEGIN: 'begin of line statement', + TOKEN_LINESTATEMENT_END: 'end of line statement', + TOKEN_DATA: 'template data / text', + TOKEN_EOF: 'end of template' + }.get(token_type, token_type) + + +def describe_token(token): + """Returns a description of the token.""" + if token.type == 'name': + return token.value + return _describe_token_type(token.type) + + +def describe_token_expr(expr): + """Like `describe_token` but for token expressions.""" + if ':' in expr: + type, value = expr.split(':', 1) + if type == 'name': + return value + else: + type = expr + return _describe_token_type(type) + + +def count_newlines(value): + """Count the number of newline characters in the string. This is + useful for extensions that filter a stream. + """ + return len(newline_re.findall(value)) + + +def compile_rules(environment): + """Compiles all the rules from the environment into a list of rules.""" + e = re.escape + rules = [ + (len(environment.comment_start_string), 'comment', + e(environment.comment_start_string)), + (len(environment.block_start_string), 'block', + e(environment.block_start_string)), + (len(environment.variable_start_string), 'variable', + e(environment.variable_start_string)) + ] + + if environment.line_statement_prefix is not None: + rules.append((len(environment.line_statement_prefix), 'linestatement', + r'^[ \t\v]*' + e(environment.line_statement_prefix))) + if environment.line_comment_prefix is not None: + rules.append((len(environment.line_comment_prefix), 'linecomment', + r'(?:^|(?<=\S))[^\S\r\n]*' + + e(environment.line_comment_prefix))) + + return [x[1:] for x in sorted(rules, reverse=True)] + + +class Failure(object): + """Class that raises a `TemplateSyntaxError` if called. + Used by the `Lexer` to specify known errors. + """ + + def __init__(self, message, cls=TemplateSyntaxError): + self.message = message + self.error_class = cls + + def __call__(self, lineno, filename): + raise self.error_class(self.message, lineno, filename) + + +class Token(tuple): + """Token class.""" + __slots__ = () + lineno, type, value = (property(itemgetter(x)) for x in range(3)) + + def __new__(cls, lineno, type, value): + return tuple.__new__(cls, (lineno, intern(str(type)), value)) + + def __str__(self): + if self.type in reverse_operators: + return reverse_operators[self.type] + elif self.type == 'name': + return self.value + return self.type + + def test(self, expr): + """Test a token against a token expression. This can either be a + token type or ``'token_type:token_value'``. This can only test + against string values and types. + """ + # here we do a regular string equality check as test_any is usually + # passed an iterable of not interned strings. + if self.type == expr: + return True + elif ':' in expr: + return expr.split(':', 1) == [self.type, self.value] + return False + + def test_any(self, *iterable): + """Test against multiple token expressions.""" + for expr in iterable: + if self.test(expr): + return True + return False + + def __repr__(self): + return 'Token(%r, %r, %r)' % ( + self.lineno, + self.type, + self.value + ) + + +@implements_iterator +class TokenStreamIterator(object): + """The iterator for tokenstreams. Iterate over the stream + until the eof token is reached. + """ + + def __init__(self, stream): + self.stream = stream + + def __iter__(self): + return self + + def __next__(self): + token = self.stream.current + if token.type is TOKEN_EOF: + self.stream.close() + raise StopIteration() + next(self.stream) + return token + + +@implements_iterator +class TokenStream(object): + """A token stream is an iterable that yields :class:`Token`\s. The + parser however does not iterate over it but calls :meth:`next` to go + one token ahead. The current active token is stored as :attr:`current`. + """ + + def __init__(self, generator, name, filename): + self._iter = iter(generator) + self._pushed = deque() + self.name = name + self.filename = filename + self.closed = False + self.current = Token(1, TOKEN_INITIAL, '') + next(self) + + def __iter__(self): + return TokenStreamIterator(self) + + def __bool__(self): + return bool(self._pushed) or self.current.type is not TOKEN_EOF + __nonzero__ = __bool__ # py2 + + eos = property(lambda x: not x, doc="Are we at the end of the stream?") + + def push(self, token): + """Push a token back to the stream.""" + self._pushed.append(token) + + def look(self): + """Look at the next token.""" + old_token = next(self) + result = self.current + self.push(result) + self.current = old_token + return result + + def skip(self, n=1): + """Got n tokens ahead.""" + for x in range(n): + next(self) + + def next_if(self, expr): + """Perform the token test and return the token if it matched. + Otherwise the return value is `None`. + """ + if self.current.test(expr): + return next(self) + + def skip_if(self, expr): + """Like :meth:`next_if` but only returns `True` or `False`.""" + return self.next_if(expr) is not None + + def __next__(self): + """Go one token ahead and return the old one""" + rv = self.current + if self._pushed: + self.current = self._pushed.popleft() + elif self.current.type is not TOKEN_EOF: + try: + self.current = next(self._iter) + except StopIteration: + self.close() + return rv + + def close(self): + """Close the stream.""" + self.current = Token(self.current.lineno, TOKEN_EOF, '') + self._iter = None + self.closed = True + + def expect(self, expr): + """Expect a given token type and return it. This accepts the same + argument as :meth:`jinja2.lexer.Token.test`. + """ + if not self.current.test(expr): + expr = describe_token_expr(expr) + if self.current.type is TOKEN_EOF: + raise TemplateSyntaxError('unexpected end of template, ' + 'expected %r.' % expr, + self.current.lineno, + self.name, self.filename) + raise TemplateSyntaxError("expected token %r, got %r" % + (expr, describe_token(self.current)), + self.current.lineno, + self.name, self.filename) + try: + return self.current + finally: + next(self) + + +def get_lexer(environment): + """Return a lexer which is probably cached.""" + key = (environment.block_start_string, + environment.block_end_string, + environment.variable_start_string, + environment.variable_end_string, + environment.comment_start_string, + environment.comment_end_string, + environment.line_statement_prefix, + environment.line_comment_prefix, + environment.trim_blocks, + environment.lstrip_blocks, + environment.newline_sequence, + environment.keep_trailing_newline) + lexer = _lexer_cache.get(key) + if lexer is None: + lexer = Lexer(environment) + _lexer_cache[key] = lexer + return lexer + + +class Lexer(object): + """Class that implements a lexer for a given environment. Automatically + created by the environment class, usually you don't have to do that. + + Note that the lexer is not automatically bound to an environment. + Multiple environments can share the same lexer. + """ + + def __init__(self, environment): + # shortcuts + c = lambda x: re.compile(x, re.M | re.S) + e = re.escape + + # lexing rules for tags + tag_rules = [ + (whitespace_re, TOKEN_WHITESPACE, None), + (float_re, TOKEN_FLOAT, None), + (integer_re, TOKEN_INTEGER, None), + (name_re, TOKEN_NAME, None), + (string_re, TOKEN_STRING, None), + (operator_re, TOKEN_OPERATOR, None) + ] + + # assemble the root lexing rule. because "|" is ungreedy + # we have to sort by length so that the lexer continues working + # as expected when we have parsing rules like <% for block and + # <%= for variables. (if someone wants asp like syntax) + # variables are just part of the rules if variable processing + # is required. + root_tag_rules = compile_rules(environment) + + # block suffix if trimming is enabled + block_suffix_re = environment.trim_blocks and '\\n?' or '' + + # strip leading spaces if lstrip_blocks is enabled + prefix_re = {} + if environment.lstrip_blocks: + # use '{%+' to manually disable lstrip_blocks behavior + no_lstrip_re = e('+') + # detect overlap between block and variable or comment strings + block_diff = c(r'^%s(.*)' % e(environment.block_start_string)) + # make sure we don't mistake a block for a variable or a comment + m = block_diff.match(environment.comment_start_string) + no_lstrip_re += m and r'|%s' % e(m.group(1)) or '' + m = block_diff.match(environment.variable_start_string) + no_lstrip_re += m and r'|%s' % e(m.group(1)) or '' + + # detect overlap between comment and variable strings + comment_diff = c(r'^%s(.*)' % e(environment.comment_start_string)) + m = comment_diff.match(environment.variable_start_string) + no_variable_re = m and r'(?!%s)' % e(m.group(1)) or '' + + lstrip_re = r'^[ \t]*' + block_prefix_re = r'%s%s(?!%s)|%s\+?' % ( + lstrip_re, + e(environment.block_start_string), + no_lstrip_re, + e(environment.block_start_string), + ) + comment_prefix_re = r'%s%s%s|%s\+?' % ( + lstrip_re, + e(environment.comment_start_string), + no_variable_re, + e(environment.comment_start_string), + ) + prefix_re['block'] = block_prefix_re + prefix_re['comment'] = comment_prefix_re + else: + block_prefix_re = '%s' % e(environment.block_start_string) + + self.newline_sequence = environment.newline_sequence + self.keep_trailing_newline = environment.keep_trailing_newline + + # global lexing rules + self.rules = { + 'root': [ + # directives + (c('(.*?)(?:%s)' % '|'.join( + [r'(?P<raw_begin>(?:\s*%s\-|%s)\s*raw\s*(?:\-%s\s*|%s))' % ( + e(environment.block_start_string), + block_prefix_re, + e(environment.block_end_string), + e(environment.block_end_string) + )] + [ + r'(?P<%s_begin>\s*%s\-|%s)' % (n, r, prefix_re.get(n,r)) + for n, r in root_tag_rules + ])), (TOKEN_DATA, '#bygroup'), '#bygroup'), + # data + (c('.+'), TOKEN_DATA, None) + ], + # comments + TOKEN_COMMENT_BEGIN: [ + (c(r'(.*?)((?:\-%s\s*|%s)%s)' % ( + e(environment.comment_end_string), + e(environment.comment_end_string), + block_suffix_re + )), (TOKEN_COMMENT, TOKEN_COMMENT_END), '#pop'), + (c('(.)'), (Failure('Missing end of comment tag'),), None) + ], + # blocks + TOKEN_BLOCK_BEGIN: [ + (c('(?:\-%s\s*|%s)%s' % ( + e(environment.block_end_string), + e(environment.block_end_string), + block_suffix_re + )), TOKEN_BLOCK_END, '#pop'), + ] + tag_rules, + # variables + TOKEN_VARIABLE_BEGIN: [ + (c('\-%s\s*|%s' % ( + e(environment.variable_end_string), + e(environment.variable_end_string) + )), TOKEN_VARIABLE_END, '#pop') + ] + tag_rules, + # raw block + TOKEN_RAW_BEGIN: [ + (c('(.*?)((?:\s*%s\-|%s)\s*endraw\s*(?:\-%s\s*|%s%s))' % ( + e(environment.block_start_string), + block_prefix_re, + e(environment.block_end_string), + e(environment.block_end_string), + block_suffix_re + )), (TOKEN_DATA, TOKEN_RAW_END), '#pop'), + (c('(.)'), (Failure('Missing end of raw directive'),), None) + ], + # line statements + TOKEN_LINESTATEMENT_BEGIN: [ + (c(r'\s*(\n|$)'), TOKEN_LINESTATEMENT_END, '#pop') + ] + tag_rules, + # line comments + TOKEN_LINECOMMENT_BEGIN: [ + (c(r'(.*?)()(?=\n|$)'), (TOKEN_LINECOMMENT, + TOKEN_LINECOMMENT_END), '#pop') + ] + } + + def _normalize_newlines(self, value): + """Called for strings and template data to normalize it to unicode.""" + return newline_re.sub(self.newline_sequence, value) + + def tokenize(self, source, name=None, filename=None, state=None): + """Calls tokeniter + tokenize and wraps it in a token stream. + """ + stream = self.tokeniter(source, name, filename, state) + return TokenStream(self.wrap(stream, name, filename), name, filename) + + def wrap(self, stream, name=None, filename=None): + """This is called with the stream as returned by `tokenize` and wraps + every token in a :class:`Token` and converts the value. + """ + for lineno, token, value in stream: + if token in ignored_tokens: + continue + elif token == 'linestatement_begin': + token = 'block_begin' + elif token == 'linestatement_end': + token = 'block_end' + # we are not interested in those tokens in the parser + elif token in ('raw_begin', 'raw_end'): + continue + elif token == 'data': + value = self._normalize_newlines(value) + elif token == 'keyword': + token = value + elif token == 'name': + value = str(value) + elif token == 'string': + # try to unescape string + try: + value = self._normalize_newlines(value[1:-1]) \ + .encode('ascii', 'backslashreplace') \ + .decode('unicode-escape') + except Exception as e: + msg = str(e).split(':')[-1].strip() + raise TemplateSyntaxError(msg, lineno, name, filename) + # if we can express it as bytestring (ascii only) + # we do that for support of semi broken APIs + # as datetime.datetime.strftime. On python 3 this + # call becomes a noop thanks to 2to3 + if PY2: + try: + value = value.encode('ascii') + except UnicodeError: + pass + elif token == 'integer': + value = int(value) + elif token == 'float': + value = float(value) + elif token == 'operator': + token = operators[value] + yield Token(lineno, token, value) + + def tokeniter(self, source, name, filename=None, state=None): + """This method tokenizes the text and returns the tokens in a + generator. Use this method if you just want to tokenize a template. + """ + source = text_type(source) + lines = source.splitlines() + if self.keep_trailing_newline and source: + for newline in ('\r\n', '\r', '\n'): + if source.endswith(newline): + lines.append('') + break + source = '\n'.join(lines) + pos = 0 + lineno = 1 + stack = ['root'] + if state is not None and state != 'root': + assert state in ('variable', 'block'), 'invalid state' + stack.append(state + '_begin') + else: + state = 'root' + statetokens = self.rules[stack[-1]] + source_length = len(source) + + balancing_stack = [] + + while 1: + # tokenizer loop + for regex, tokens, new_state in statetokens: + m = regex.match(source, pos) + # if no match we try again with the next rule + if m is None: + continue + + # we only match blocks and variables if braces / parentheses + # are balanced. continue parsing with the lower rule which + # is the operator rule. do this only if the end tags look + # like operators + if balancing_stack and \ + tokens in ('variable_end', 'block_end', + 'linestatement_end'): + continue + + # tuples support more options + if isinstance(tokens, tuple): + for idx, token in enumerate(tokens): + # failure group + if token.__class__ is Failure: + raise token(lineno, filename) + # bygroup is a bit more complex, in that case we + # yield for the current token the first named + # group that matched + elif token == '#bygroup': + for key, value in iteritems(m.groupdict()): + if value is not None: + yield lineno, key, value + lineno += value.count('\n') + break + else: + raise RuntimeError('%r wanted to resolve ' + 'the token dynamically' + ' but no group matched' + % regex) + # normal group + else: + data = m.group(idx + 1) + if data or token not in ignore_if_empty: + yield lineno, token, data + lineno += data.count('\n') + + # strings as token just are yielded as it. + else: + data = m.group() + # update brace/parentheses balance + if tokens == 'operator': + if data == '{': + balancing_stack.append('}') + elif data == '(': + balancing_stack.append(')') + elif data == '[': + balancing_stack.append(']') + elif data in ('}', ')', ']'): + if not balancing_stack: + raise TemplateSyntaxError('unexpected \'%s\'' % + data, lineno, name, + filename) + expected_op = balancing_stack.pop() + if expected_op != data: + raise TemplateSyntaxError('unexpected \'%s\', ' + 'expected \'%s\'' % + (data, expected_op), + lineno, name, + filename) + # yield items + if data or tokens not in ignore_if_empty: + yield lineno, tokens, data + lineno += data.count('\n') + + # fetch new position into new variable so that we can check + # if there is a internal parsing error which would result + # in an infinite loop + pos2 = m.end() + + # handle state changes + if new_state is not None: + # remove the uppermost state + if new_state == '#pop': + stack.pop() + # resolve the new state by group checking + elif new_state == '#bygroup': + for key, value in iteritems(m.groupdict()): + if value is not None: + stack.append(key) + break + else: + raise RuntimeError('%r wanted to resolve the ' + 'new state dynamically but' + ' no group matched' % + regex) + # direct state name given + else: + stack.append(new_state) + statetokens = self.rules[stack[-1]] + # we are still at the same position and no stack change. + # this means a loop without break condition, avoid that and + # raise error + elif pos2 == pos: + raise RuntimeError('%r yielded empty string without ' + 'stack change' % regex) + # publish new function and start again + pos = pos2 + break + # if loop terminated without break we haven't found a single match + # either we are at the end of the file or we have a problem + else: + # end of text + if pos >= source_length: + return + # something went wrong + raise TemplateSyntaxError('unexpected char %r at %d' % + (source[pos], pos), lineno, + name, filename) diff --git a/deps/v8_inspector/deps/jinja2/jinja2/loaders.py b/deps/v8_inspector/deps/jinja2/jinja2/loaders.py new file mode 100644 index 00000000000000..44aa3925a2d11e --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/loaders.py @@ -0,0 +1,481 @@ +# -*- coding: utf-8 -*- +""" + jinja2.loaders + ~~~~~~~~~~~~~~ + + Jinja loader classes. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import os +import sys +import weakref +from types import ModuleType +from os import path +from hashlib import sha1 +from jinja2.exceptions import TemplateNotFound +from jinja2.utils import open_if_exists, internalcode +from jinja2._compat import string_types, iteritems + + +def split_template_path(template): + """Split a path into segments and perform a sanity check. If it detects + '..' in the path it will raise a `TemplateNotFound` error. + """ + pieces = [] + for piece in template.split('/'): + if path.sep in piece \ + or (path.altsep and path.altsep in piece) or \ + piece == path.pardir: + raise TemplateNotFound(template) + elif piece and piece != '.': + pieces.append(piece) + return pieces + + +class BaseLoader(object): + """Baseclass for all loaders. Subclass this and override `get_source` to + implement a custom loading mechanism. The environment provides a + `get_template` method that calls the loader's `load` method to get the + :class:`Template` object. + + A very basic example for a loader that looks up templates on the file + system could look like this:: + + from jinja2 import BaseLoader, TemplateNotFound + from os.path import join, exists, getmtime + + class MyLoader(BaseLoader): + + def __init__(self, path): + self.path = path + + def get_source(self, environment, template): + path = join(self.path, template) + if not exists(path): + raise TemplateNotFound(template) + mtime = getmtime(path) + with file(path) as f: + source = f.read().decode('utf-8') + return source, path, lambda: mtime == getmtime(path) + """ + + #: if set to `False` it indicates that the loader cannot provide access + #: to the source of templates. + #: + #: .. versionadded:: 2.4 + has_source_access = True + + def get_source(self, environment, template): + """Get the template source, filename and reload helper for a template. + It's passed the environment and template name and has to return a + tuple in the form ``(source, filename, uptodate)`` or raise a + `TemplateNotFound` error if it can't locate the template. + + The source part of the returned tuple must be the source of the + template as unicode string or a ASCII bytestring. The filename should + be the name of the file on the filesystem if it was loaded from there, + otherwise `None`. The filename is used by python for the tracebacks + if no loader extension is used. + + The last item in the tuple is the `uptodate` function. If auto + reloading is enabled it's always called to check if the template + changed. No arguments are passed so the function must store the + old state somewhere (for example in a closure). If it returns `False` + the template will be reloaded. + """ + if not self.has_source_access: + raise RuntimeError('%s cannot provide access to the source' % + self.__class__.__name__) + raise TemplateNotFound(template) + + def list_templates(self): + """Iterates over all templates. If the loader does not support that + it should raise a :exc:`TypeError` which is the default behavior. + """ + raise TypeError('this loader cannot iterate over all templates') + + @internalcode + def load(self, environment, name, globals=None): + """Loads a template. This method looks up the template in the cache + or loads one by calling :meth:`get_source`. Subclasses should not + override this method as loaders working on collections of other + loaders (such as :class:`PrefixLoader` or :class:`ChoiceLoader`) + will not call this method but `get_source` directly. + """ + code = None + if globals is None: + globals = {} + + # first we try to get the source for this template together + # with the filename and the uptodate function. + source, filename, uptodate = self.get_source(environment, name) + + # try to load the code from the bytecode cache if there is a + # bytecode cache configured. + bcc = environment.bytecode_cache + if bcc is not None: + bucket = bcc.get_bucket(environment, name, filename, source) + code = bucket.code + + # if we don't have code so far (not cached, no longer up to + # date) etc. we compile the template + if code is None: + code = environment.compile(source, name, filename) + + # if the bytecode cache is available and the bucket doesn't + # have a code so far, we give the bucket the new code and put + # it back to the bytecode cache. + if bcc is not None and bucket.code is None: + bucket.code = code + bcc.set_bucket(bucket) + + return environment.template_class.from_code(environment, code, + globals, uptodate) + + +class FileSystemLoader(BaseLoader): + """Loads templates from the file system. This loader can find templates + in folders on the file system and is the preferred way to load them. + + The loader takes the path to the templates as string, or if multiple + locations are wanted a list of them which is then looked up in the + given order:: + + >>> loader = FileSystemLoader('/path/to/templates') + >>> loader = FileSystemLoader(['/path/to/templates', '/other/path']) + + Per default the template encoding is ``'utf-8'`` which can be changed + by setting the `encoding` parameter to something else. + + To follow symbolic links, set the *followlinks* parameter to ``True``:: + + >>> loader = FileSystemLoader('/path/to/templates', followlinks=True) + + .. versionchanged:: 2.8+ + The *followlinks* parameter was added. + """ + + def __init__(self, searchpath, encoding='utf-8', followlinks=False): + if isinstance(searchpath, string_types): + searchpath = [searchpath] + self.searchpath = list(searchpath) + self.encoding = encoding + self.followlinks = followlinks + + def get_source(self, environment, template): + pieces = split_template_path(template) + for searchpath in self.searchpath: + filename = path.join(searchpath, *pieces) + f = open_if_exists(filename) + if f is None: + continue + try: + contents = f.read().decode(self.encoding) + finally: + f.close() + + mtime = path.getmtime(filename) + + def uptodate(): + try: + return path.getmtime(filename) == mtime + except OSError: + return False + return contents, filename, uptodate + raise TemplateNotFound(template) + + def list_templates(self): + found = set() + for searchpath in self.searchpath: + walk_dir = os.walk(searchpath, followlinks=self.followlinks) + for dirpath, dirnames, filenames in walk_dir: + for filename in filenames: + template = os.path.join(dirpath, filename) \ + [len(searchpath):].strip(os.path.sep) \ + .replace(os.path.sep, '/') + if template[:2] == './': + template = template[2:] + if template not in found: + found.add(template) + return sorted(found) + + +class PackageLoader(BaseLoader): + """Load templates from python eggs or packages. It is constructed with + the name of the python package and the path to the templates in that + package:: + + loader = PackageLoader('mypackage', 'views') + + If the package path is not given, ``'templates'`` is assumed. + + Per default the template encoding is ``'utf-8'`` which can be changed + by setting the `encoding` parameter to something else. Due to the nature + of eggs it's only possible to reload templates if the package was loaded + from the file system and not a zip file. + """ + + def __init__(self, package_name, package_path='templates', + encoding='utf-8'): + from pkg_resources import DefaultProvider, ResourceManager, \ + get_provider + provider = get_provider(package_name) + self.encoding = encoding + self.manager = ResourceManager() + self.filesystem_bound = isinstance(provider, DefaultProvider) + self.provider = provider + self.package_path = package_path + + def get_source(self, environment, template): + pieces = split_template_path(template) + p = '/'.join((self.package_path,) + tuple(pieces)) + if not self.provider.has_resource(p): + raise TemplateNotFound(template) + + filename = uptodate = None + if self.filesystem_bound: + filename = self.provider.get_resource_filename(self.manager, p) + mtime = path.getmtime(filename) + def uptodate(): + try: + return path.getmtime(filename) == mtime + except OSError: + return False + + source = self.provider.get_resource_string(self.manager, p) + return source.decode(self.encoding), filename, uptodate + + def list_templates(self): + path = self.package_path + if path[:2] == './': + path = path[2:] + elif path == '.': + path = '' + offset = len(path) + results = [] + def _walk(path): + for filename in self.provider.resource_listdir(path): + fullname = path + '/' + filename + if self.provider.resource_isdir(fullname): + _walk(fullname) + else: + results.append(fullname[offset:].lstrip('/')) + _walk(path) + results.sort() + return results + + +class DictLoader(BaseLoader): + """Loads a template from a python dict. It's passed a dict of unicode + strings bound to template names. This loader is useful for unittesting: + + >>> loader = DictLoader({'index.html': 'source here'}) + + Because auto reloading is rarely useful this is disabled per default. + """ + + def __init__(self, mapping): + self.mapping = mapping + + def get_source(self, environment, template): + if template in self.mapping: + source = self.mapping[template] + return source, None, lambda: source == self.mapping.get(template) + raise TemplateNotFound(template) + + def list_templates(self): + return sorted(self.mapping) + + +class FunctionLoader(BaseLoader): + """A loader that is passed a function which does the loading. The + function receives the name of the template and has to return either + an unicode string with the template source, a tuple in the form ``(source, + filename, uptodatefunc)`` or `None` if the template does not exist. + + >>> def load_template(name): + ... if name == 'index.html': + ... return '...' + ... + >>> loader = FunctionLoader(load_template) + + The `uptodatefunc` is a function that is called if autoreload is enabled + and has to return `True` if the template is still up to date. For more + details have a look at :meth:`BaseLoader.get_source` which has the same + return value. + """ + + def __init__(self, load_func): + self.load_func = load_func + + def get_source(self, environment, template): + rv = self.load_func(template) + if rv is None: + raise TemplateNotFound(template) + elif isinstance(rv, string_types): + return rv, None, None + return rv + + +class PrefixLoader(BaseLoader): + """A loader that is passed a dict of loaders where each loader is bound + to a prefix. The prefix is delimited from the template by a slash per + default, which can be changed by setting the `delimiter` argument to + something else:: + + loader = PrefixLoader({ + 'app1': PackageLoader('mypackage.app1'), + 'app2': PackageLoader('mypackage.app2') + }) + + By loading ``'app1/index.html'`` the file from the app1 package is loaded, + by loading ``'app2/index.html'`` the file from the second. + """ + + def __init__(self, mapping, delimiter='/'): + self.mapping = mapping + self.delimiter = delimiter + + def get_loader(self, template): + try: + prefix, name = template.split(self.delimiter, 1) + loader = self.mapping[prefix] + except (ValueError, KeyError): + raise TemplateNotFound(template) + return loader, name + + def get_source(self, environment, template): + loader, name = self.get_loader(template) + try: + return loader.get_source(environment, name) + except TemplateNotFound: + # re-raise the exception with the correct fileame here. + # (the one that includes the prefix) + raise TemplateNotFound(template) + + @internalcode + def load(self, environment, name, globals=None): + loader, local_name = self.get_loader(name) + try: + return loader.load(environment, local_name, globals) + except TemplateNotFound: + # re-raise the exception with the correct fileame here. + # (the one that includes the prefix) + raise TemplateNotFound(name) + + def list_templates(self): + result = [] + for prefix, loader in iteritems(self.mapping): + for template in loader.list_templates(): + result.append(prefix + self.delimiter + template) + return result + + +class ChoiceLoader(BaseLoader): + """This loader works like the `PrefixLoader` just that no prefix is + specified. If a template could not be found by one loader the next one + is tried. + + >>> loader = ChoiceLoader([ + ... FileSystemLoader('/path/to/user/templates'), + ... FileSystemLoader('/path/to/system/templates') + ... ]) + + This is useful if you want to allow users to override builtin templates + from a different location. + """ + + def __init__(self, loaders): + self.loaders = loaders + + def get_source(self, environment, template): + for loader in self.loaders: + try: + return loader.get_source(environment, template) + except TemplateNotFound: + pass + raise TemplateNotFound(template) + + @internalcode + def load(self, environment, name, globals=None): + for loader in self.loaders: + try: + return loader.load(environment, name, globals) + except TemplateNotFound: + pass + raise TemplateNotFound(name) + + def list_templates(self): + found = set() + for loader in self.loaders: + found.update(loader.list_templates()) + return sorted(found) + + +class _TemplateModule(ModuleType): + """Like a normal module but with support for weak references""" + + +class ModuleLoader(BaseLoader): + """This loader loads templates from precompiled templates. + + Example usage: + + >>> loader = ChoiceLoader([ + ... ModuleLoader('/path/to/compiled/templates'), + ... FileSystemLoader('/path/to/templates') + ... ]) + + Templates can be precompiled with :meth:`Environment.compile_templates`. + """ + + has_source_access = False + + def __init__(self, path): + package_name = '_jinja2_module_templates_%x' % id(self) + + # create a fake module that looks for the templates in the + # path given. + mod = _TemplateModule(package_name) + if isinstance(path, string_types): + path = [path] + else: + path = list(path) + mod.__path__ = path + + sys.modules[package_name] = weakref.proxy(mod, + lambda x: sys.modules.pop(package_name, None)) + + # the only strong reference, the sys.modules entry is weak + # so that the garbage collector can remove it once the + # loader that created it goes out of business. + self.module = mod + self.package_name = package_name + + @staticmethod + def get_template_key(name): + return 'tmpl_' + sha1(name.encode('utf-8')).hexdigest() + + @staticmethod + def get_module_filename(name): + return ModuleLoader.get_template_key(name) + '.py' + + @internalcode + def load(self, environment, name, globals=None): + key = self.get_template_key(name) + module = '%s.%s' % (self.package_name, key) + mod = getattr(self.module, module, None) + if mod is None: + try: + mod = __import__(module, None, None, ['root']) + except ImportError: + raise TemplateNotFound(name) + + # remove the entry from sys.modules, we only want the attribute + # on the module object we have stored on the loader. + sys.modules.pop(module, None) + + return environment.template_class.from_module_dict( + environment, mod.__dict__, globals) diff --git a/deps/v8_inspector/deps/jinja2/jinja2/meta.py b/deps/v8_inspector/deps/jinja2/jinja2/meta.py new file mode 100644 index 00000000000000..3dbab7c22d5dba --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/meta.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +""" + jinja2.meta + ~~~~~~~~~~~ + + This module implements various functions that exposes information about + templates that might be interesting for various kinds of applications. + + :copyright: (c) 2010 by the Jinja Team, see AUTHORS for more details. + :license: BSD, see LICENSE for more details. +""" +from jinja2 import nodes +from jinja2.compiler import CodeGenerator +from jinja2._compat import string_types + + +class TrackingCodeGenerator(CodeGenerator): + """We abuse the code generator for introspection.""" + + def __init__(self, environment): + CodeGenerator.__init__(self, environment, '<introspection>', + '<introspection>') + self.undeclared_identifiers = set() + + def write(self, x): + """Don't write.""" + + def pull_locals(self, frame): + """Remember all undeclared identifiers.""" + self.undeclared_identifiers.update(frame.identifiers.undeclared) + + +def find_undeclared_variables(ast): + """Returns a set of all variables in the AST that will be looked up from + the context at runtime. Because at compile time it's not known which + variables will be used depending on the path the execution takes at + runtime, all variables are returned. + + >>> from jinja2 import Environment, meta + >>> env = Environment() + >>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}') + >>> meta.find_undeclared_variables(ast) == set(['bar']) + True + + .. admonition:: Implementation + + Internally the code generator is used for finding undeclared variables. + This is good to know because the code generator might raise a + :exc:`TemplateAssertionError` during compilation and as a matter of + fact this function can currently raise that exception as well. + """ + codegen = TrackingCodeGenerator(ast.environment) + codegen.visit(ast) + return codegen.undeclared_identifiers + + +def find_referenced_templates(ast): + """Finds all the referenced templates from the AST. This will return an + iterator over all the hardcoded template extensions, inclusions and + imports. If dynamic inheritance or inclusion is used, `None` will be + yielded. + + >>> from jinja2 import Environment, meta + >>> env = Environment() + >>> ast = env.parse('{% extends "layout.html" %}{% include helper %}') + >>> list(meta.find_referenced_templates(ast)) + ['layout.html', None] + + This function is useful for dependency tracking. For example if you want + to rebuild parts of the website after a layout template has changed. + """ + for node in ast.find_all((nodes.Extends, nodes.FromImport, nodes.Import, + nodes.Include)): + if not isinstance(node.template, nodes.Const): + # a tuple with some non consts in there + if isinstance(node.template, (nodes.Tuple, nodes.List)): + for template_name in node.template.items: + # something const, only yield the strings and ignore + # non-string consts that really just make no sense + if isinstance(template_name, nodes.Const): + if isinstance(template_name.value, string_types): + yield template_name.value + # something dynamic in there + else: + yield None + # something dynamic we don't know about here + else: + yield None + continue + # constant is a basestring, direct template name + if isinstance(node.template.value, string_types): + yield node.template.value + # a tuple or list (latter *should* not happen) made of consts, + # yield the consts that are strings. We could warn here for + # non string values + elif isinstance(node, nodes.Include) and \ + isinstance(node.template.value, (tuple, list)): + for template_name in node.template.value: + if isinstance(template_name, string_types): + yield template_name + # something else we don't care about, we could warn here + else: + yield None diff --git a/deps/v8_inspector/deps/jinja2/jinja2/nodes.py b/deps/v8_inspector/deps/jinja2/jinja2/nodes.py new file mode 100644 index 00000000000000..d32046ce5c3c5c --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/nodes.py @@ -0,0 +1,919 @@ +# -*- coding: utf-8 -*- +""" + jinja2.nodes + ~~~~~~~~~~~~ + + This module implements additional nodes derived from the ast base node. + + It also provides some node tree helper functions like `in_lineno` and + `get_nodes` used by the parser and translator in order to normalize + python and jinja nodes. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import types +import operator + +from collections import deque +from jinja2.utils import Markup +from jinja2._compat import izip, with_metaclass, text_type + + +#: the types we support for context functions +_context_function_types = (types.FunctionType, types.MethodType) + + +_binop_to_func = { + '*': operator.mul, + '/': operator.truediv, + '//': operator.floordiv, + '**': operator.pow, + '%': operator.mod, + '+': operator.add, + '-': operator.sub +} + +_uaop_to_func = { + 'not': operator.not_, + '+': operator.pos, + '-': operator.neg +} + +_cmpop_to_func = { + 'eq': operator.eq, + 'ne': operator.ne, + 'gt': operator.gt, + 'gteq': operator.ge, + 'lt': operator.lt, + 'lteq': operator.le, + 'in': lambda a, b: a in b, + 'notin': lambda a, b: a not in b +} + + +class Impossible(Exception): + """Raised if the node could not perform a requested action.""" + + +class NodeType(type): + """A metaclass for nodes that handles the field and attribute + inheritance. fields and attributes from the parent class are + automatically forwarded to the child.""" + + def __new__(cls, name, bases, d): + for attr in 'fields', 'attributes': + storage = [] + storage.extend(getattr(bases[0], attr, ())) + storage.extend(d.get(attr, ())) + assert len(bases) == 1, 'multiple inheritance not allowed' + assert len(storage) == len(set(storage)), 'layout conflict' + d[attr] = tuple(storage) + d.setdefault('abstract', False) + return type.__new__(cls, name, bases, d) + + +class EvalContext(object): + """Holds evaluation time information. Custom attributes can be attached + to it in extensions. + """ + + def __init__(self, environment, template_name=None): + self.environment = environment + if callable(environment.autoescape): + self.autoescape = environment.autoescape(template_name) + else: + self.autoescape = environment.autoescape + self.volatile = False + + def save(self): + return self.__dict__.copy() + + def revert(self, old): + self.__dict__.clear() + self.__dict__.update(old) + + +def get_eval_context(node, ctx): + if ctx is None: + if node.environment is None: + raise RuntimeError('if no eval context is passed, the ' + 'node must have an attached ' + 'environment.') + return EvalContext(node.environment) + return ctx + + +class Node(with_metaclass(NodeType, object)): + """Baseclass for all Jinja2 nodes. There are a number of nodes available + of different types. There are four major types: + + - :class:`Stmt`: statements + - :class:`Expr`: expressions + - :class:`Helper`: helper nodes + - :class:`Template`: the outermost wrapper node + + All nodes have fields and attributes. Fields may be other nodes, lists, + or arbitrary values. Fields are passed to the constructor as regular + positional arguments, attributes as keyword arguments. Each node has + two attributes: `lineno` (the line number of the node) and `environment`. + The `environment` attribute is set at the end of the parsing process for + all nodes automatically. + """ + fields = () + attributes = ('lineno', 'environment') + abstract = True + + def __init__(self, *fields, **attributes): + if self.abstract: + raise TypeError('abstract nodes are not instanciable') + if fields: + if len(fields) != len(self.fields): + if not self.fields: + raise TypeError('%r takes 0 arguments' % + self.__class__.__name__) + raise TypeError('%r takes 0 or %d argument%s' % ( + self.__class__.__name__, + len(self.fields), + len(self.fields) != 1 and 's' or '' + )) + for name, arg in izip(self.fields, fields): + setattr(self, name, arg) + for attr in self.attributes: + setattr(self, attr, attributes.pop(attr, None)) + if attributes: + raise TypeError('unknown attribute %r' % + next(iter(attributes))) + + def iter_fields(self, exclude=None, only=None): + """This method iterates over all fields that are defined and yields + ``(key, value)`` tuples. Per default all fields are returned, but + it's possible to limit that to some fields by providing the `only` + parameter or to exclude some using the `exclude` parameter. Both + should be sets or tuples of field names. + """ + for name in self.fields: + if (exclude is only is None) or \ + (exclude is not None and name not in exclude) or \ + (only is not None and name in only): + try: + yield name, getattr(self, name) + except AttributeError: + pass + + def iter_child_nodes(self, exclude=None, only=None): + """Iterates over all direct child nodes of the node. This iterates + over all fields and yields the values of they are nodes. If the value + of a field is a list all the nodes in that list are returned. + """ + for field, item in self.iter_fields(exclude, only): + if isinstance(item, list): + for n in item: + if isinstance(n, Node): + yield n + elif isinstance(item, Node): + yield item + + def find(self, node_type): + """Find the first node of a given type. If no such node exists the + return value is `None`. + """ + for result in self.find_all(node_type): + return result + + def find_all(self, node_type): + """Find all the nodes of a given type. If the type is a tuple, + the check is performed for any of the tuple items. + """ + for child in self.iter_child_nodes(): + if isinstance(child, node_type): + yield child + for result in child.find_all(node_type): + yield result + + def set_ctx(self, ctx): + """Reset the context of a node and all child nodes. Per default the + parser will all generate nodes that have a 'load' context as it's the + most common one. This method is used in the parser to set assignment + targets and other nodes to a store context. + """ + todo = deque([self]) + while todo: + node = todo.popleft() + if 'ctx' in node.fields: + node.ctx = ctx + todo.extend(node.iter_child_nodes()) + return self + + def set_lineno(self, lineno, override=False): + """Set the line numbers of the node and children.""" + todo = deque([self]) + while todo: + node = todo.popleft() + if 'lineno' in node.attributes: + if node.lineno is None or override: + node.lineno = lineno + todo.extend(node.iter_child_nodes()) + return self + + def set_environment(self, environment): + """Set the environment for all nodes.""" + todo = deque([self]) + while todo: + node = todo.popleft() + node.environment = environment + todo.extend(node.iter_child_nodes()) + return self + + def __eq__(self, other): + return type(self) is type(other) and \ + tuple(self.iter_fields()) == tuple(other.iter_fields()) + + def __ne__(self, other): + return not self.__eq__(other) + + # Restore Python 2 hashing behavior on Python 3 + __hash__ = object.__hash__ + + def __repr__(self): + return '%s(%s)' % ( + self.__class__.__name__, + ', '.join('%s=%r' % (arg, getattr(self, arg, None)) for + arg in self.fields) + ) + + +class Stmt(Node): + """Base node for all statements.""" + abstract = True + + +class Helper(Node): + """Nodes that exist in a specific context only.""" + abstract = True + + +class Template(Node): + """Node that represents a template. This must be the outermost node that + is passed to the compiler. + """ + fields = ('body',) + + +class Output(Stmt): + """A node that holds multiple expressions which are then printed out. + This is used both for the `print` statement and the regular template data. + """ + fields = ('nodes',) + + +class Extends(Stmt): + """Represents an extends statement.""" + fields = ('template',) + + +class For(Stmt): + """The for loop. `target` is the target for the iteration (usually a + :class:`Name` or :class:`Tuple`), `iter` the iterable. `body` is a list + of nodes that are used as loop-body, and `else_` a list of nodes for the + `else` block. If no else node exists it has to be an empty list. + + For filtered nodes an expression can be stored as `test`, otherwise `None`. + """ + fields = ('target', 'iter', 'body', 'else_', 'test', 'recursive') + + +class If(Stmt): + """If `test` is true, `body` is rendered, else `else_`.""" + fields = ('test', 'body', 'else_') + + +class Macro(Stmt): + """A macro definition. `name` is the name of the macro, `args` a list of + arguments and `defaults` a list of defaults if there are any. `body` is + a list of nodes for the macro body. + """ + fields = ('name', 'args', 'defaults', 'body') + + +class CallBlock(Stmt): + """Like a macro without a name but a call instead. `call` is called with + the unnamed macro as `caller` argument this node holds. + """ + fields = ('call', 'args', 'defaults', 'body') + + +class FilterBlock(Stmt): + """Node for filter sections.""" + fields = ('body', 'filter') + + +class Block(Stmt): + """A node that represents a block.""" + fields = ('name', 'body', 'scoped') + + +class Include(Stmt): + """A node that represents the include tag.""" + fields = ('template', 'with_context', 'ignore_missing') + + +class Import(Stmt): + """A node that represents the import tag.""" + fields = ('template', 'target', 'with_context') + + +class FromImport(Stmt): + """A node that represents the from import tag. It's important to not + pass unsafe names to the name attribute. The compiler translates the + attribute lookups directly into getattr calls and does *not* use the + subscript callback of the interface. As exported variables may not + start with double underscores (which the parser asserts) this is not a + problem for regular Jinja code, but if this node is used in an extension + extra care must be taken. + + The list of names may contain tuples if aliases are wanted. + """ + fields = ('template', 'names', 'with_context') + + +class ExprStmt(Stmt): + """A statement that evaluates an expression and discards the result.""" + fields = ('node',) + + +class Assign(Stmt): + """Assigns an expression to a target.""" + fields = ('target', 'node') + + +class AssignBlock(Stmt): + """Assigns a block to a target.""" + fields = ('target', 'body') + + +class Expr(Node): + """Baseclass for all expressions.""" + abstract = True + + def as_const(self, eval_ctx=None): + """Return the value of the expression as constant or raise + :exc:`Impossible` if this was not possible. + + An :class:`EvalContext` can be provided, if none is given + a default context is created which requires the nodes to have + an attached environment. + + .. versionchanged:: 2.4 + the `eval_ctx` parameter was added. + """ + raise Impossible() + + def can_assign(self): + """Check if it's possible to assign something to this node.""" + return False + + +class BinExpr(Expr): + """Baseclass for all binary expressions.""" + fields = ('left', 'right') + operator = None + abstract = True + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + # intercepted operators cannot be folded at compile time + if self.environment.sandboxed and \ + self.operator in self.environment.intercepted_binops: + raise Impossible() + f = _binop_to_func[self.operator] + try: + return f(self.left.as_const(eval_ctx), self.right.as_const(eval_ctx)) + except Exception: + raise Impossible() + + +class UnaryExpr(Expr): + """Baseclass for all unary expressions.""" + fields = ('node',) + operator = None + abstract = True + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + # intercepted operators cannot be folded at compile time + if self.environment.sandboxed and \ + self.operator in self.environment.intercepted_unops: + raise Impossible() + f = _uaop_to_func[self.operator] + try: + return f(self.node.as_const(eval_ctx)) + except Exception: + raise Impossible() + + +class Name(Expr): + """Looks up a name or stores a value in a name. + The `ctx` of the node can be one of the following values: + + - `store`: store a value in the name + - `load`: load that name + - `param`: like `store` but if the name was defined as function parameter. + """ + fields = ('name', 'ctx') + + def can_assign(self): + return self.name not in ('true', 'false', 'none', + 'True', 'False', 'None') + + +class Literal(Expr): + """Baseclass for literals.""" + abstract = True + + +class Const(Literal): + """All constant values. The parser will return this node for simple + constants such as ``42`` or ``"foo"`` but it can be used to store more + complex values such as lists too. Only constants with a safe + representation (objects where ``eval(repr(x)) == x`` is true). + """ + fields = ('value',) + + def as_const(self, eval_ctx=None): + return self.value + + @classmethod + def from_untrusted(cls, value, lineno=None, environment=None): + """Return a const object if the value is representable as + constant value in the generated code, otherwise it will raise + an `Impossible` exception. + """ + from .compiler import has_safe_repr + if not has_safe_repr(value): + raise Impossible() + return cls(value, lineno=lineno, environment=environment) + + +class TemplateData(Literal): + """A constant template string.""" + fields = ('data',) + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if eval_ctx.volatile: + raise Impossible() + if eval_ctx.autoescape: + return Markup(self.data) + return self.data + + +class Tuple(Literal): + """For loop unpacking and some other things like multiple arguments + for subscripts. Like for :class:`Name` `ctx` specifies if the tuple + is used for loading the names or storing. + """ + fields = ('items', 'ctx') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return tuple(x.as_const(eval_ctx) for x in self.items) + + def can_assign(self): + for item in self.items: + if not item.can_assign(): + return False + return True + + +class List(Literal): + """Any list literal such as ``[1, 2, 3]``""" + fields = ('items',) + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return [x.as_const(eval_ctx) for x in self.items] + + +class Dict(Literal): + """Any dict literal such as ``{1: 2, 3: 4}``. The items must be a list of + :class:`Pair` nodes. + """ + fields = ('items',) + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return dict(x.as_const(eval_ctx) for x in self.items) + + +class Pair(Helper): + """A key, value pair for dicts.""" + fields = ('key', 'value') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return self.key.as_const(eval_ctx), self.value.as_const(eval_ctx) + + +class Keyword(Helper): + """A key, value pair for keyword arguments where key is a string.""" + fields = ('key', 'value') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return self.key, self.value.as_const(eval_ctx) + + +class CondExpr(Expr): + """A conditional expression (inline if expression). (``{{ + foo if bar else baz }}``) + """ + fields = ('test', 'expr1', 'expr2') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if self.test.as_const(eval_ctx): + return self.expr1.as_const(eval_ctx) + + # if we evaluate to an undefined object, we better do that at runtime + if self.expr2 is None: + raise Impossible() + + return self.expr2.as_const(eval_ctx) + + +class Filter(Expr): + """This node applies a filter on an expression. `name` is the name of + the filter, the rest of the fields are the same as for :class:`Call`. + + If the `node` of a filter is `None` the contents of the last buffer are + filtered. Buffers are created by macros and filter blocks. + """ + fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if eval_ctx.volatile or self.node is None: + raise Impossible() + # we have to be careful here because we call filter_ below. + # if this variable would be called filter, 2to3 would wrap the + # call in a list beause it is assuming we are talking about the + # builtin filter function here which no longer returns a list in + # python 3. because of that, do not rename filter_ to filter! + filter_ = self.environment.filters.get(self.name) + if filter_ is None or getattr(filter_, 'contextfilter', False): + raise Impossible() + obj = self.node.as_const(eval_ctx) + args = [x.as_const(eval_ctx) for x in self.args] + if getattr(filter_, 'evalcontextfilter', False): + args.insert(0, eval_ctx) + elif getattr(filter_, 'environmentfilter', False): + args.insert(0, self.environment) + kwargs = dict(x.as_const(eval_ctx) for x in self.kwargs) + if self.dyn_args is not None: + try: + args.extend(self.dyn_args.as_const(eval_ctx)) + except Exception: + raise Impossible() + if self.dyn_kwargs is not None: + try: + kwargs.update(self.dyn_kwargs.as_const(eval_ctx)) + except Exception: + raise Impossible() + try: + return filter_(obj, *args, **kwargs) + except Exception: + raise Impossible() + + +class Test(Expr): + """Applies a test on an expression. `name` is the name of the test, the + rest of the fields are the same as for :class:`Call`. + """ + fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs') + + +class Call(Expr): + """Calls an expression. `args` is a list of arguments, `kwargs` a list + of keyword arguments (list of :class:`Keyword` nodes), and `dyn_args` + and `dyn_kwargs` has to be either `None` or a node that is used as + node for dynamic positional (``*args``) or keyword (``**kwargs``) + arguments. + """ + fields = ('node', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if eval_ctx.volatile: + raise Impossible() + obj = self.node.as_const(eval_ctx) + + # don't evaluate context functions + args = [x.as_const(eval_ctx) for x in self.args] + if isinstance(obj, _context_function_types): + if getattr(obj, 'contextfunction', False): + raise Impossible() + elif getattr(obj, 'evalcontextfunction', False): + args.insert(0, eval_ctx) + elif getattr(obj, 'environmentfunction', False): + args.insert(0, self.environment) + + kwargs = dict(x.as_const(eval_ctx) for x in self.kwargs) + if self.dyn_args is not None: + try: + args.extend(self.dyn_args.as_const(eval_ctx)) + except Exception: + raise Impossible() + if self.dyn_kwargs is not None: + try: + kwargs.update(self.dyn_kwargs.as_const(eval_ctx)) + except Exception: + raise Impossible() + try: + return obj(*args, **kwargs) + except Exception: + raise Impossible() + + +class Getitem(Expr): + """Get an attribute or item from an expression and prefer the item.""" + fields = ('node', 'arg', 'ctx') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if self.ctx != 'load': + raise Impossible() + try: + return self.environment.getitem(self.node.as_const(eval_ctx), + self.arg.as_const(eval_ctx)) + except Exception: + raise Impossible() + + def can_assign(self): + return False + + +class Getattr(Expr): + """Get an attribute or item from an expression that is a ascii-only + bytestring and prefer the attribute. + """ + fields = ('node', 'attr', 'ctx') + + def as_const(self, eval_ctx=None): + if self.ctx != 'load': + raise Impossible() + try: + eval_ctx = get_eval_context(self, eval_ctx) + return self.environment.getattr(self.node.as_const(eval_ctx), + self.attr) + except Exception: + raise Impossible() + + def can_assign(self): + return False + + +class Slice(Expr): + """Represents a slice object. This must only be used as argument for + :class:`Subscript`. + """ + fields = ('start', 'stop', 'step') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + def const(obj): + if obj is None: + return None + return obj.as_const(eval_ctx) + return slice(const(self.start), const(self.stop), const(self.step)) + + +class Concat(Expr): + """Concatenates the list of expressions provided after converting them to + unicode. + """ + fields = ('nodes',) + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return ''.join(text_type(x.as_const(eval_ctx)) for x in self.nodes) + + +class Compare(Expr): + """Compares an expression with some other expressions. `ops` must be a + list of :class:`Operand`\s. + """ + fields = ('expr', 'ops') + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + result = value = self.expr.as_const(eval_ctx) + try: + for op in self.ops: + new_value = op.expr.as_const(eval_ctx) + result = _cmpop_to_func[op.op](value, new_value) + value = new_value + except Exception: + raise Impossible() + return result + + +class Operand(Helper): + """Holds an operator and an expression.""" + fields = ('op', 'expr') + +if __debug__: + Operand.__doc__ += '\nThe following operators are available: ' + \ + ', '.join(sorted('``%s``' % x for x in set(_binop_to_func) | + set(_uaop_to_func) | set(_cmpop_to_func))) + + +class Mul(BinExpr): + """Multiplies the left with the right node.""" + operator = '*' + + +class Div(BinExpr): + """Divides the left by the right node.""" + operator = '/' + + +class FloorDiv(BinExpr): + """Divides the left by the right node and truncates conver the + result into an integer by truncating. + """ + operator = '//' + + +class Add(BinExpr): + """Add the left to the right node.""" + operator = '+' + + +class Sub(BinExpr): + """Subtract the right from the left node.""" + operator = '-' + + +class Mod(BinExpr): + """Left modulo right.""" + operator = '%' + + +class Pow(BinExpr): + """Left to the power of right.""" + operator = '**' + + +class And(BinExpr): + """Short circuited AND.""" + operator = 'and' + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return self.left.as_const(eval_ctx) and self.right.as_const(eval_ctx) + + +class Or(BinExpr): + """Short circuited OR.""" + operator = 'or' + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return self.left.as_const(eval_ctx) or self.right.as_const(eval_ctx) + + +class Not(UnaryExpr): + """Negate the expression.""" + operator = 'not' + + +class Neg(UnaryExpr): + """Make the expression negative.""" + operator = '-' + + +class Pos(UnaryExpr): + """Make the expression positive (noop for most expressions)""" + operator = '+' + + +# Helpers for extensions + + +class EnvironmentAttribute(Expr): + """Loads an attribute from the environment object. This is useful for + extensions that want to call a callback stored on the environment. + """ + fields = ('name',) + + +class ExtensionAttribute(Expr): + """Returns the attribute of an extension bound to the environment. + The identifier is the identifier of the :class:`Extension`. + + This node is usually constructed by calling the + :meth:`~jinja2.ext.Extension.attr` method on an extension. + """ + fields = ('identifier', 'name') + + +class ImportedName(Expr): + """If created with an import name the import name is returned on node + access. For example ``ImportedName('cgi.escape')`` returns the `escape` + function from the cgi module on evaluation. Imports are optimized by the + compiler so there is no need to assign them to local variables. + """ + fields = ('importname',) + + +class InternalName(Expr): + """An internal name in the compiler. You cannot create these nodes + yourself but the parser provides a + :meth:`~jinja2.parser.Parser.free_identifier` method that creates + a new identifier for you. This identifier is not available from the + template and is not threated specially by the compiler. + """ + fields = ('name',) + + def __init__(self): + raise TypeError('Can\'t create internal names. Use the ' + '`free_identifier` method on a parser.') + + +class MarkSafe(Expr): + """Mark the wrapped expression as safe (wrap it as `Markup`).""" + fields = ('expr',) + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + return Markup(self.expr.as_const(eval_ctx)) + + +class MarkSafeIfAutoescape(Expr): + """Mark the wrapped expression as safe (wrap it as `Markup`) but + only if autoescaping is active. + + .. versionadded:: 2.5 + """ + fields = ('expr',) + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) + if eval_ctx.volatile: + raise Impossible() + expr = self.expr.as_const(eval_ctx) + if eval_ctx.autoescape: + return Markup(expr) + return expr + + +class ContextReference(Expr): + """Returns the current template context. It can be used like a + :class:`Name` node, with a ``'load'`` ctx and will return the + current :class:`~jinja2.runtime.Context` object. + + Here an example that assigns the current template name to a + variable named `foo`:: + + Assign(Name('foo', ctx='store'), + Getattr(ContextReference(), 'name')) + """ + + +class Continue(Stmt): + """Continue a loop.""" + + +class Break(Stmt): + """Break a loop.""" + + +class Scope(Stmt): + """An artificial scope.""" + fields = ('body',) + + +class EvalContextModifier(Stmt): + """Modifies the eval context. For each option that should be modified, + a :class:`Keyword` has to be added to the :attr:`options` list. + + Example to change the `autoescape` setting:: + + EvalContextModifier(options=[Keyword('autoescape', Const(True))]) + """ + fields = ('options',) + + +class ScopedEvalContextModifier(EvalContextModifier): + """Modifies the eval context and reverts it later. Works exactly like + :class:`EvalContextModifier` but will only modify the + :class:`~jinja2.nodes.EvalContext` for nodes in the :attr:`body`. + """ + fields = ('body',) + + +# make sure nobody creates custom nodes +def _failing_new(*args, **kwargs): + raise TypeError('can\'t create custom node types') +NodeType.__new__ = staticmethod(_failing_new); del _failing_new diff --git a/deps/v8_inspector/deps/jinja2/jinja2/optimizer.py b/deps/v8_inspector/deps/jinja2/jinja2/optimizer.py new file mode 100644 index 00000000000000..00eab115e1c19a --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/optimizer.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +""" + jinja2.optimizer + ~~~~~~~~~~~~~~~~ + + The jinja optimizer is currently trying to constant fold a few expressions + and modify the AST in place so that it should be easier to evaluate it. + + Because the AST does not contain all the scoping information and the + compiler has to find that out, we cannot do all the optimizations we + want. For example loop unrolling doesn't work because unrolled loops would + have a different scoping. + + The solution would be a second syntax tree that has the scoping rules stored. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD. +""" +from jinja2 import nodes +from jinja2.visitor import NodeTransformer + + +def optimize(node, environment): + """The context hint can be used to perform an static optimization + based on the context given.""" + optimizer = Optimizer(environment) + return optimizer.visit(node) + + +class Optimizer(NodeTransformer): + + def __init__(self, environment): + self.environment = environment + + def visit_If(self, node): + """Eliminate dead code.""" + # do not optimize ifs that have a block inside so that it doesn't + # break super(). + if node.find(nodes.Block) is not None: + return self.generic_visit(node) + try: + val = self.visit(node.test).as_const() + except nodes.Impossible: + return self.generic_visit(node) + if val: + body = node.body + else: + body = node.else_ + result = [] + for node in body: + result.extend(self.visit_list(node)) + return result + + def fold(self, node): + """Do constant folding.""" + node = self.generic_visit(node) + try: + return nodes.Const.from_untrusted(node.as_const(), + lineno=node.lineno, + environment=self.environment) + except nodes.Impossible: + return node + + visit_Add = visit_Sub = visit_Mul = visit_Div = visit_FloorDiv = \ + visit_Pow = visit_Mod = visit_And = visit_Or = visit_Pos = visit_Neg = \ + visit_Not = visit_Compare = visit_Getitem = visit_Getattr = visit_Call = \ + visit_Filter = visit_Test = visit_CondExpr = fold + del fold diff --git a/deps/v8_inspector/deps/jinja2/jinja2/parser.py b/deps/v8_inspector/deps/jinja2/jinja2/parser.py new file mode 100644 index 00000000000000..d24da180ea61ae --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/parser.py @@ -0,0 +1,899 @@ +# -*- coding: utf-8 -*- +""" + jinja2.parser + ~~~~~~~~~~~~~ + + Implements the template parser. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +from jinja2 import nodes +from jinja2.exceptions import TemplateSyntaxError, TemplateAssertionError +from jinja2.lexer import describe_token, describe_token_expr +from jinja2._compat import imap + + +_statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print', + 'macro', 'include', 'from', 'import', + 'set']) +_compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq']) + + +class Parser(object): + """This is the central parsing class Jinja2 uses. It's passed to + extensions and can be used to parse expressions or statements. + """ + + def __init__(self, environment, source, name=None, filename=None, + state=None): + self.environment = environment + self.stream = environment._tokenize(source, name, filename, state) + self.name = name + self.filename = filename + self.closed = False + self.extensions = {} + for extension in environment.iter_extensions(): + for tag in extension.tags: + self.extensions[tag] = extension.parse + self._last_identifier = 0 + self._tag_stack = [] + self._end_token_stack = [] + + def fail(self, msg, lineno=None, exc=TemplateSyntaxError): + """Convenience method that raises `exc` with the message, passed + line number or last line number as well as the current name and + filename. + """ + if lineno is None: + lineno = self.stream.current.lineno + raise exc(msg, lineno, self.name, self.filename) + + def _fail_ut_eof(self, name, end_token_stack, lineno): + expected = [] + for exprs in end_token_stack: + expected.extend(imap(describe_token_expr, exprs)) + if end_token_stack: + currently_looking = ' or '.join( + "'%s'" % describe_token_expr(expr) + for expr in end_token_stack[-1]) + else: + currently_looking = None + + if name is None: + message = ['Unexpected end of template.'] + else: + message = ['Encountered unknown tag \'%s\'.' % name] + + if currently_looking: + if name is not None and name in expected: + message.append('You probably made a nesting mistake. Jinja ' + 'is expecting this tag, but currently looking ' + 'for %s.' % currently_looking) + else: + message.append('Jinja was looking for the following tags: ' + '%s.' % currently_looking) + + if self._tag_stack: + message.append('The innermost block that needs to be ' + 'closed is \'%s\'.' % self._tag_stack[-1]) + + self.fail(' '.join(message), lineno) + + def fail_unknown_tag(self, name, lineno=None): + """Called if the parser encounters an unknown tag. Tries to fail + with a human readable error message that could help to identify + the problem. + """ + return self._fail_ut_eof(name, self._end_token_stack, lineno) + + def fail_eof(self, end_tokens=None, lineno=None): + """Like fail_unknown_tag but for end of template situations.""" + stack = list(self._end_token_stack) + if end_tokens is not None: + stack.append(end_tokens) + return self._fail_ut_eof(None, stack, lineno) + + def is_tuple_end(self, extra_end_rules=None): + """Are we at the end of a tuple?""" + if self.stream.current.type in ('variable_end', 'block_end', 'rparen'): + return True + elif extra_end_rules is not None: + return self.stream.current.test_any(extra_end_rules) + return False + + def free_identifier(self, lineno=None): + """Return a new free identifier as :class:`~jinja2.nodes.InternalName`.""" + self._last_identifier += 1 + rv = object.__new__(nodes.InternalName) + nodes.Node.__init__(rv, 'fi%d' % self._last_identifier, lineno=lineno) + return rv + + def parse_statement(self): + """Parse a single statement.""" + token = self.stream.current + if token.type != 'name': + self.fail('tag name expected', token.lineno) + self._tag_stack.append(token.value) + pop_tag = True + try: + if token.value in _statement_keywords: + return getattr(self, 'parse_' + self.stream.current.value)() + if token.value == 'call': + return self.parse_call_block() + if token.value == 'filter': + return self.parse_filter_block() + ext = self.extensions.get(token.value) + if ext is not None: + return ext(self) + + # did not work out, remove the token we pushed by accident + # from the stack so that the unknown tag fail function can + # produce a proper error message. + self._tag_stack.pop() + pop_tag = False + self.fail_unknown_tag(token.value, token.lineno) + finally: + if pop_tag: + self._tag_stack.pop() + + def parse_statements(self, end_tokens, drop_needle=False): + """Parse multiple statements into a list until one of the end tokens + is reached. This is used to parse the body of statements as it also + parses template data if appropriate. The parser checks first if the + current token is a colon and skips it if there is one. Then it checks + for the block end and parses until if one of the `end_tokens` is + reached. Per default the active token in the stream at the end of + the call is the matched end token. If this is not wanted `drop_needle` + can be set to `True` and the end token is removed. + """ + # the first token may be a colon for python compatibility + self.stream.skip_if('colon') + + # in the future it would be possible to add whole code sections + # by adding some sort of end of statement token and parsing those here. + self.stream.expect('block_end') + result = self.subparse(end_tokens) + + # we reached the end of the template too early, the subparser + # does not check for this, so we do that now + if self.stream.current.type == 'eof': + self.fail_eof(end_tokens) + + if drop_needle: + next(self.stream) + return result + + def parse_set(self): + """Parse an assign statement.""" + lineno = next(self.stream).lineno + target = self.parse_assign_target() + if self.stream.skip_if('assign'): + expr = self.parse_tuple() + return nodes.Assign(target, expr, lineno=lineno) + body = self.parse_statements(('name:endset',), + drop_needle=True) + return nodes.AssignBlock(target, body, lineno=lineno) + + def parse_for(self): + """Parse a for loop.""" + lineno = self.stream.expect('name:for').lineno + target = self.parse_assign_target(extra_end_rules=('name:in',)) + self.stream.expect('name:in') + iter = self.parse_tuple(with_condexpr=False, + extra_end_rules=('name:recursive',)) + test = None + if self.stream.skip_if('name:if'): + test = self.parse_expression() + recursive = self.stream.skip_if('name:recursive') + body = self.parse_statements(('name:endfor', 'name:else')) + if next(self.stream).value == 'endfor': + else_ = [] + else: + else_ = self.parse_statements(('name:endfor',), drop_needle=True) + return nodes.For(target, iter, body, else_, test, + recursive, lineno=lineno) + + def parse_if(self): + """Parse an if construct.""" + node = result = nodes.If(lineno=self.stream.expect('name:if').lineno) + while 1: + node.test = self.parse_tuple(with_condexpr=False) + node.body = self.parse_statements(('name:elif', 'name:else', + 'name:endif')) + token = next(self.stream) + if token.test('name:elif'): + new_node = nodes.If(lineno=self.stream.current.lineno) + node.else_ = [new_node] + node = new_node + continue + elif token.test('name:else'): + node.else_ = self.parse_statements(('name:endif',), + drop_needle=True) + else: + node.else_ = [] + break + return result + + def parse_block(self): + node = nodes.Block(lineno=next(self.stream).lineno) + node.name = self.stream.expect('name').value + node.scoped = self.stream.skip_if('name:scoped') + + # common problem people encounter when switching from django + # to jinja. we do not support hyphens in block names, so let's + # raise a nicer error message in that case. + if self.stream.current.type == 'sub': + self.fail('Block names in Jinja have to be valid Python ' + 'identifiers and may not contain hyphens, use an ' + 'underscore instead.') + + node.body = self.parse_statements(('name:endblock',), drop_needle=True) + self.stream.skip_if('name:' + node.name) + return node + + def parse_extends(self): + node = nodes.Extends(lineno=next(self.stream).lineno) + node.template = self.parse_expression() + return node + + def parse_import_context(self, node, default): + if self.stream.current.test_any('name:with', 'name:without') and \ + self.stream.look().test('name:context'): + node.with_context = next(self.stream).value == 'with' + self.stream.skip() + else: + node.with_context = default + return node + + def parse_include(self): + node = nodes.Include(lineno=next(self.stream).lineno) + node.template = self.parse_expression() + if self.stream.current.test('name:ignore') and \ + self.stream.look().test('name:missing'): + node.ignore_missing = True + self.stream.skip(2) + else: + node.ignore_missing = False + return self.parse_import_context(node, True) + + def parse_import(self): + node = nodes.Import(lineno=next(self.stream).lineno) + node.template = self.parse_expression() + self.stream.expect('name:as') + node.target = self.parse_assign_target(name_only=True).name + return self.parse_import_context(node, False) + + def parse_from(self): + node = nodes.FromImport(lineno=next(self.stream).lineno) + node.template = self.parse_expression() + self.stream.expect('name:import') + node.names = [] + + def parse_context(): + if self.stream.current.value in ('with', 'without') and \ + self.stream.look().test('name:context'): + node.with_context = next(self.stream).value == 'with' + self.stream.skip() + return True + return False + + while 1: + if node.names: + self.stream.expect('comma') + if self.stream.current.type == 'name': + if parse_context(): + break + target = self.parse_assign_target(name_only=True) + if target.name.startswith('_'): + self.fail('names starting with an underline can not ' + 'be imported', target.lineno, + exc=TemplateAssertionError) + if self.stream.skip_if('name:as'): + alias = self.parse_assign_target(name_only=True) + node.names.append((target.name, alias.name)) + else: + node.names.append(target.name) + if parse_context() or self.stream.current.type != 'comma': + break + else: + break + if not hasattr(node, 'with_context'): + node.with_context = False + self.stream.skip_if('comma') + return node + + def parse_signature(self, node): + node.args = args = [] + node.defaults = defaults = [] + self.stream.expect('lparen') + while self.stream.current.type != 'rparen': + if args: + self.stream.expect('comma') + arg = self.parse_assign_target(name_only=True) + arg.set_ctx('param') + if self.stream.skip_if('assign'): + defaults.append(self.parse_expression()) + elif defaults: + self.fail('non-default argument follows default argument') + args.append(arg) + self.stream.expect('rparen') + + def parse_call_block(self): + node = nodes.CallBlock(lineno=next(self.stream).lineno) + if self.stream.current.type == 'lparen': + self.parse_signature(node) + else: + node.args = [] + node.defaults = [] + + node.call = self.parse_expression() + if not isinstance(node.call, nodes.Call): + self.fail('expected call', node.lineno) + node.body = self.parse_statements(('name:endcall',), drop_needle=True) + return node + + def parse_filter_block(self): + node = nodes.FilterBlock(lineno=next(self.stream).lineno) + node.filter = self.parse_filter(None, start_inline=True) + node.body = self.parse_statements(('name:endfilter',), + drop_needle=True) + return node + + def parse_macro(self): + node = nodes.Macro(lineno=next(self.stream).lineno) + node.name = self.parse_assign_target(name_only=True).name + self.parse_signature(node) + node.body = self.parse_statements(('name:endmacro',), + drop_needle=True) + return node + + def parse_print(self): + node = nodes.Output(lineno=next(self.stream).lineno) + node.nodes = [] + while self.stream.current.type != 'block_end': + if node.nodes: + self.stream.expect('comma') + node.nodes.append(self.parse_expression()) + return node + + def parse_assign_target(self, with_tuple=True, name_only=False, + extra_end_rules=None): + """Parse an assignment target. As Jinja2 allows assignments to + tuples, this function can parse all allowed assignment targets. Per + default assignments to tuples are parsed, that can be disable however + by setting `with_tuple` to `False`. If only assignments to names are + wanted `name_only` can be set to `True`. The `extra_end_rules` + parameter is forwarded to the tuple parsing function. + """ + if name_only: + token = self.stream.expect('name') + target = nodes.Name(token.value, 'store', lineno=token.lineno) + else: + if with_tuple: + target = self.parse_tuple(simplified=True, + extra_end_rules=extra_end_rules) + else: + target = self.parse_primary() + target.set_ctx('store') + if not target.can_assign(): + self.fail('can\'t assign to %r' % target.__class__. + __name__.lower(), target.lineno) + return target + + def parse_expression(self, with_condexpr=True): + """Parse an expression. Per default all expressions are parsed, if + the optional `with_condexpr` parameter is set to `False` conditional + expressions are not parsed. + """ + if with_condexpr: + return self.parse_condexpr() + return self.parse_or() + + def parse_condexpr(self): + lineno = self.stream.current.lineno + expr1 = self.parse_or() + while self.stream.skip_if('name:if'): + expr2 = self.parse_or() + if self.stream.skip_if('name:else'): + expr3 = self.parse_condexpr() + else: + expr3 = None + expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno) + lineno = self.stream.current.lineno + return expr1 + + def parse_or(self): + lineno = self.stream.current.lineno + left = self.parse_and() + while self.stream.skip_if('name:or'): + right = self.parse_and() + left = nodes.Or(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_and(self): + lineno = self.stream.current.lineno + left = self.parse_not() + while self.stream.skip_if('name:and'): + right = self.parse_not() + left = nodes.And(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_not(self): + if self.stream.current.test('name:not'): + lineno = next(self.stream).lineno + return nodes.Not(self.parse_not(), lineno=lineno) + return self.parse_compare() + + def parse_compare(self): + lineno = self.stream.current.lineno + expr = self.parse_add() + ops = [] + while 1: + token_type = self.stream.current.type + if token_type in _compare_operators: + next(self.stream) + ops.append(nodes.Operand(token_type, self.parse_add())) + elif self.stream.skip_if('name:in'): + ops.append(nodes.Operand('in', self.parse_add())) + elif (self.stream.current.test('name:not') and + self.stream.look().test('name:in')): + self.stream.skip(2) + ops.append(nodes.Operand('notin', self.parse_add())) + else: + break + lineno = self.stream.current.lineno + if not ops: + return expr + return nodes.Compare(expr, ops, lineno=lineno) + + def parse_add(self): + lineno = self.stream.current.lineno + left = self.parse_sub() + while self.stream.current.type == 'add': + next(self.stream) + right = self.parse_sub() + left = nodes.Add(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_sub(self): + lineno = self.stream.current.lineno + left = self.parse_concat() + while self.stream.current.type == 'sub': + next(self.stream) + right = self.parse_concat() + left = nodes.Sub(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_concat(self): + lineno = self.stream.current.lineno + args = [self.parse_mul()] + while self.stream.current.type == 'tilde': + next(self.stream) + args.append(self.parse_mul()) + if len(args) == 1: + return args[0] + return nodes.Concat(args, lineno=lineno) + + def parse_mul(self): + lineno = self.stream.current.lineno + left = self.parse_div() + while self.stream.current.type == 'mul': + next(self.stream) + right = self.parse_div() + left = nodes.Mul(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_div(self): + lineno = self.stream.current.lineno + left = self.parse_floordiv() + while self.stream.current.type == 'div': + next(self.stream) + right = self.parse_floordiv() + left = nodes.Div(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_floordiv(self): + lineno = self.stream.current.lineno + left = self.parse_mod() + while self.stream.current.type == 'floordiv': + next(self.stream) + right = self.parse_mod() + left = nodes.FloorDiv(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_mod(self): + lineno = self.stream.current.lineno + left = self.parse_pow() + while self.stream.current.type == 'mod': + next(self.stream) + right = self.parse_pow() + left = nodes.Mod(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_pow(self): + lineno = self.stream.current.lineno + left = self.parse_unary() + while self.stream.current.type == 'pow': + next(self.stream) + right = self.parse_unary() + left = nodes.Pow(left, right, lineno=lineno) + lineno = self.stream.current.lineno + return left + + def parse_unary(self, with_filter=True): + token_type = self.stream.current.type + lineno = self.stream.current.lineno + if token_type == 'sub': + next(self.stream) + node = nodes.Neg(self.parse_unary(False), lineno=lineno) + elif token_type == 'add': + next(self.stream) + node = nodes.Pos(self.parse_unary(False), lineno=lineno) + else: + node = self.parse_primary() + node = self.parse_postfix(node) + if with_filter: + node = self.parse_filter_expr(node) + return node + + def parse_primary(self): + token = self.stream.current + if token.type == 'name': + if token.value in ('true', 'false', 'True', 'False'): + node = nodes.Const(token.value in ('true', 'True'), + lineno=token.lineno) + elif token.value in ('none', 'None'): + node = nodes.Const(None, lineno=token.lineno) + else: + node = nodes.Name(token.value, 'load', lineno=token.lineno) + next(self.stream) + elif token.type == 'string': + next(self.stream) + buf = [token.value] + lineno = token.lineno + while self.stream.current.type == 'string': + buf.append(self.stream.current.value) + next(self.stream) + node = nodes.Const(''.join(buf), lineno=lineno) + elif token.type in ('integer', 'float'): + next(self.stream) + node = nodes.Const(token.value, lineno=token.lineno) + elif token.type == 'lparen': + next(self.stream) + node = self.parse_tuple(explicit_parentheses=True) + self.stream.expect('rparen') + elif token.type == 'lbracket': + node = self.parse_list() + elif token.type == 'lbrace': + node = self.parse_dict() + else: + self.fail("unexpected '%s'" % describe_token(token), token.lineno) + return node + + def parse_tuple(self, simplified=False, with_condexpr=True, + extra_end_rules=None, explicit_parentheses=False): + """Works like `parse_expression` but if multiple expressions are + delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created. + This method could also return a regular expression instead of a tuple + if no commas where found. + + The default parsing mode is a full tuple. If `simplified` is `True` + only names and literals are parsed. The `no_condexpr` parameter is + forwarded to :meth:`parse_expression`. + + Because tuples do not require delimiters and may end in a bogus comma + an extra hint is needed that marks the end of a tuple. For example + for loops support tuples between `for` and `in`. In that case the + `extra_end_rules` is set to ``['name:in']``. + + `explicit_parentheses` is true if the parsing was triggered by an + expression in parentheses. This is used to figure out if an empty + tuple is a valid expression or not. + """ + lineno = self.stream.current.lineno + if simplified: + parse = self.parse_primary + elif with_condexpr: + parse = self.parse_expression + else: + parse = lambda: self.parse_expression(with_condexpr=False) + args = [] + is_tuple = False + while 1: + if args: + self.stream.expect('comma') + if self.is_tuple_end(extra_end_rules): + break + args.append(parse()) + if self.stream.current.type == 'comma': + is_tuple = True + else: + break + lineno = self.stream.current.lineno + + if not is_tuple: + if args: + return args[0] + + # if we don't have explicit parentheses, an empty tuple is + # not a valid expression. This would mean nothing (literally + # nothing) in the spot of an expression would be an empty + # tuple. + if not explicit_parentheses: + self.fail('Expected an expression, got \'%s\'' % + describe_token(self.stream.current)) + + return nodes.Tuple(args, 'load', lineno=lineno) + + def parse_list(self): + token = self.stream.expect('lbracket') + items = [] + while self.stream.current.type != 'rbracket': + if items: + self.stream.expect('comma') + if self.stream.current.type == 'rbracket': + break + items.append(self.parse_expression()) + self.stream.expect('rbracket') + return nodes.List(items, lineno=token.lineno) + + def parse_dict(self): + token = self.stream.expect('lbrace') + items = [] + while self.stream.current.type != 'rbrace': + if items: + self.stream.expect('comma') + if self.stream.current.type == 'rbrace': + break + key = self.parse_expression() + self.stream.expect('colon') + value = self.parse_expression() + items.append(nodes.Pair(key, value, lineno=key.lineno)) + self.stream.expect('rbrace') + return nodes.Dict(items, lineno=token.lineno) + + def parse_postfix(self, node): + while 1: + token_type = self.stream.current.type + if token_type == 'dot' or token_type == 'lbracket': + node = self.parse_subscript(node) + # calls are valid both after postfix expressions (getattr + # and getitem) as well as filters and tests + elif token_type == 'lparen': + node = self.parse_call(node) + else: + break + return node + + def parse_filter_expr(self, node): + while 1: + token_type = self.stream.current.type + if token_type == 'pipe': + node = self.parse_filter(node) + elif token_type == 'name' and self.stream.current.value == 'is': + node = self.parse_test(node) + # calls are valid both after postfix expressions (getattr + # and getitem) as well as filters and tests + elif token_type == 'lparen': + node = self.parse_call(node) + else: + break + return node + + def parse_subscript(self, node): + token = next(self.stream) + if token.type == 'dot': + attr_token = self.stream.current + next(self.stream) + if attr_token.type == 'name': + return nodes.Getattr(node, attr_token.value, 'load', + lineno=token.lineno) + elif attr_token.type != 'integer': + self.fail('expected name or number', attr_token.lineno) + arg = nodes.Const(attr_token.value, lineno=attr_token.lineno) + return nodes.Getitem(node, arg, 'load', lineno=token.lineno) + if token.type == 'lbracket': + args = [] + while self.stream.current.type != 'rbracket': + if args: + self.stream.expect('comma') + args.append(self.parse_subscribed()) + self.stream.expect('rbracket') + if len(args) == 1: + arg = args[0] + else: + arg = nodes.Tuple(args, 'load', lineno=token.lineno) + return nodes.Getitem(node, arg, 'load', lineno=token.lineno) + self.fail('expected subscript expression', self.lineno) + + def parse_subscribed(self): + lineno = self.stream.current.lineno + + if self.stream.current.type == 'colon': + next(self.stream) + args = [None] + else: + node = self.parse_expression() + if self.stream.current.type != 'colon': + return node + next(self.stream) + args = [node] + + if self.stream.current.type == 'colon': + args.append(None) + elif self.stream.current.type not in ('rbracket', 'comma'): + args.append(self.parse_expression()) + else: + args.append(None) + + if self.stream.current.type == 'colon': + next(self.stream) + if self.stream.current.type not in ('rbracket', 'comma'): + args.append(self.parse_expression()) + else: + args.append(None) + else: + args.append(None) + + return nodes.Slice(lineno=lineno, *args) + + def parse_call(self, node): + token = self.stream.expect('lparen') + args = [] + kwargs = [] + dyn_args = dyn_kwargs = None + require_comma = False + + def ensure(expr): + if not expr: + self.fail('invalid syntax for function call expression', + token.lineno) + + while self.stream.current.type != 'rparen': + if require_comma: + self.stream.expect('comma') + # support for trailing comma + if self.stream.current.type == 'rparen': + break + if self.stream.current.type == 'mul': + ensure(dyn_args is None and dyn_kwargs is None) + next(self.stream) + dyn_args = self.parse_expression() + elif self.stream.current.type == 'pow': + ensure(dyn_kwargs is None) + next(self.stream) + dyn_kwargs = self.parse_expression() + else: + ensure(dyn_args is None and dyn_kwargs is None) + if self.stream.current.type == 'name' and \ + self.stream.look().type == 'assign': + key = self.stream.current.value + self.stream.skip(2) + value = self.parse_expression() + kwargs.append(nodes.Keyword(key, value, + lineno=value.lineno)) + else: + ensure(not kwargs) + args.append(self.parse_expression()) + + require_comma = True + self.stream.expect('rparen') + + if node is None: + return args, kwargs, dyn_args, dyn_kwargs + return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs, + lineno=token.lineno) + + def parse_filter(self, node, start_inline=False): + while self.stream.current.type == 'pipe' or start_inline: + if not start_inline: + next(self.stream) + token = self.stream.expect('name') + name = token.value + while self.stream.current.type == 'dot': + next(self.stream) + name += '.' + self.stream.expect('name').value + if self.stream.current.type == 'lparen': + args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None) + else: + args = [] + kwargs = [] + dyn_args = dyn_kwargs = None + node = nodes.Filter(node, name, args, kwargs, dyn_args, + dyn_kwargs, lineno=token.lineno) + start_inline = False + return node + + def parse_test(self, node): + token = next(self.stream) + if self.stream.current.test('name:not'): + next(self.stream) + negated = True + else: + negated = False + name = self.stream.expect('name').value + while self.stream.current.type == 'dot': + next(self.stream) + name += '.' + self.stream.expect('name').value + dyn_args = dyn_kwargs = None + kwargs = [] + if self.stream.current.type == 'lparen': + args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None) + elif (self.stream.current.type in ('name', 'string', 'integer', + 'float', 'lparen', 'lbracket', + 'lbrace') and not + self.stream.current.test_any('name:else', 'name:or', + 'name:and')): + if self.stream.current.test('name:is'): + self.fail('You cannot chain multiple tests with is') + args = [self.parse_expression()] + else: + args = [] + node = nodes.Test(node, name, args, kwargs, dyn_args, + dyn_kwargs, lineno=token.lineno) + if negated: + node = nodes.Not(node, lineno=token.lineno) + return node + + def subparse(self, end_tokens=None): + body = [] + data_buffer = [] + add_data = data_buffer.append + + if end_tokens is not None: + self._end_token_stack.append(end_tokens) + + def flush_data(): + if data_buffer: + lineno = data_buffer[0].lineno + body.append(nodes.Output(data_buffer[:], lineno=lineno)) + del data_buffer[:] + + try: + while self.stream: + token = self.stream.current + if token.type == 'data': + if token.value: + add_data(nodes.TemplateData(token.value, + lineno=token.lineno)) + next(self.stream) + elif token.type == 'variable_begin': + next(self.stream) + add_data(self.parse_tuple(with_condexpr=True)) + self.stream.expect('variable_end') + elif token.type == 'block_begin': + flush_data() + next(self.stream) + if end_tokens is not None and \ + self.stream.current.test_any(*end_tokens): + return body + rv = self.parse_statement() + if isinstance(rv, list): + body.extend(rv) + else: + body.append(rv) + self.stream.expect('block_end') + else: + raise AssertionError('internal parsing error') + + flush_data() + finally: + if end_tokens is not None: + self._end_token_stack.pop() + + return body + + def parse(self): + """Parse the whole template into a `Template` node.""" + result = nodes.Template(self.subparse(), lineno=1) + result.set_environment(self.environment) + return result diff --git a/deps/v8_inspector/deps/jinja2/jinja2/runtime.py b/deps/v8_inspector/deps/jinja2/jinja2/runtime.py new file mode 100644 index 00000000000000..685a12da068c48 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/runtime.py @@ -0,0 +1,667 @@ +# -*- coding: utf-8 -*- +""" + jinja2.runtime + ~~~~~~~~~~~~~~ + + Runtime helpers. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD. +""" +import sys + +from itertools import chain +from jinja2.nodes import EvalContext, _context_function_types +from jinja2.utils import Markup, soft_unicode, escape, missing, concat, \ + internalcode, object_type_repr +from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \ + TemplateNotFound +from jinja2._compat import imap, text_type, iteritems, \ + implements_iterator, implements_to_string, string_types, PY2 + + +# these variables are exported to the template runtime +__all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup', + 'TemplateRuntimeError', 'missing', 'concat', 'escape', + 'markup_join', 'unicode_join', 'to_string', 'identity', + 'TemplateNotFound', 'make_logging_undefined'] + +#: the name of the function that is used to convert something into +#: a string. We can just use the text type here. +to_string = text_type + +#: the identity function. Useful for certain things in the environment +identity = lambda x: x + +_last_iteration = object() + + +def markup_join(seq): + """Concatenation that escapes if necessary and converts to unicode.""" + buf = [] + iterator = imap(soft_unicode, seq) + for arg in iterator: + buf.append(arg) + if hasattr(arg, '__html__'): + return Markup(u'').join(chain(buf, iterator)) + return concat(buf) + + +def unicode_join(seq): + """Simple args to unicode conversion and concatenation.""" + return concat(imap(text_type, seq)) + + +def new_context(environment, template_name, blocks, vars=None, + shared=None, globals=None, locals=None): + """Internal helper to for context creation.""" + if vars is None: + vars = {} + if shared: + parent = vars + else: + parent = dict(globals or (), **vars) + if locals: + # if the parent is shared a copy should be created because + # we don't want to modify the dict passed + if shared: + parent = dict(parent) + for key, value in iteritems(locals): + if key[:2] == 'l_' and value is not missing: + parent[key[2:]] = value + return environment.context_class(environment, parent, template_name, + blocks) + + +class TemplateReference(object): + """The `self` in templates.""" + + def __init__(self, context): + self.__context = context + + def __getitem__(self, name): + blocks = self.__context.blocks[name] + return BlockReference(name, self.__context, blocks, 0) + + def __repr__(self): + return '<%s %r>' % ( + self.__class__.__name__, + self.__context.name + ) + + +class Context(object): + """The template context holds the variables of a template. It stores the + values passed to the template and also the names the template exports. + Creating instances is neither supported nor useful as it's created + automatically at various stages of the template evaluation and should not + be created by hand. + + The context is immutable. Modifications on :attr:`parent` **must not** + happen and modifications on :attr:`vars` are allowed from generated + template code only. Template filters and global functions marked as + :func:`contextfunction`\s get the active context passed as first argument + and are allowed to access the context read-only. + + The template context supports read only dict operations (`get`, + `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`, + `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve` + method that doesn't fail with a `KeyError` but returns an + :class:`Undefined` object for missing variables. + """ + __slots__ = ('parent', 'vars', 'environment', 'eval_ctx', 'exported_vars', + 'name', 'blocks', '__weakref__') + + def __init__(self, environment, parent, name, blocks): + self.parent = parent + self.vars = {} + self.environment = environment + self.eval_ctx = EvalContext(self.environment, name) + self.exported_vars = set() + self.name = name + + # create the initial mapping of blocks. Whenever template inheritance + # takes place the runtime will update this mapping with the new blocks + # from the template. + self.blocks = dict((k, [v]) for k, v in iteritems(blocks)) + + def super(self, name, current): + """Render a parent block.""" + try: + blocks = self.blocks[name] + index = blocks.index(current) + 1 + blocks[index] + except LookupError: + return self.environment.undefined('there is no parent block ' + 'called %r.' % name, + name='super') + return BlockReference(name, self, blocks, index) + + def get(self, key, default=None): + """Returns an item from the template context, if it doesn't exist + `default` is returned. + """ + try: + return self[key] + except KeyError: + return default + + def resolve(self, key): + """Looks up a variable like `__getitem__` or `get` but returns an + :class:`Undefined` object with the name of the name looked up. + """ + if key in self.vars: + return self.vars[key] + if key in self.parent: + return self.parent[key] + return self.environment.undefined(name=key) + + def get_exported(self): + """Get a new dict with the exported variables.""" + return dict((k, self.vars[k]) for k in self.exported_vars) + + def get_all(self): + """Return a copy of the complete context as dict including the + exported variables. + """ + return dict(self.parent, **self.vars) + + @internalcode + def call(__self, __obj, *args, **kwargs): + """Call the callable with the arguments and keyword arguments + provided but inject the active context or environment as first + argument if the callable is a :func:`contextfunction` or + :func:`environmentfunction`. + """ + if __debug__: + __traceback_hide__ = True # noqa + + # Allow callable classes to take a context + fn = __obj.__call__ + for fn_type in ('contextfunction', + 'evalcontextfunction', + 'environmentfunction'): + if hasattr(fn, fn_type): + __obj = fn + break + + if isinstance(__obj, _context_function_types): + if getattr(__obj, 'contextfunction', 0): + args = (__self,) + args + elif getattr(__obj, 'evalcontextfunction', 0): + args = (__self.eval_ctx,) + args + elif getattr(__obj, 'environmentfunction', 0): + args = (__self.environment,) + args + try: + return __obj(*args, **kwargs) + except StopIteration: + return __self.environment.undefined('value was undefined because ' + 'a callable raised a ' + 'StopIteration exception') + + def derived(self, locals=None): + """Internal helper function to create a derived context.""" + context = new_context(self.environment, self.name, {}, + self.parent, True, None, locals) + context.vars.update(self.vars) + context.eval_ctx = self.eval_ctx + context.blocks.update((k, list(v)) for k, v in iteritems(self.blocks)) + return context + + def _all(meth): + proxy = lambda self: getattr(self.get_all(), meth)() + proxy.__doc__ = getattr(dict, meth).__doc__ + proxy.__name__ = meth + return proxy + + keys = _all('keys') + values = _all('values') + items = _all('items') + + # not available on python 3 + if PY2: + iterkeys = _all('iterkeys') + itervalues = _all('itervalues') + iteritems = _all('iteritems') + del _all + + def __contains__(self, name): + return name in self.vars or name in self.parent + + def __getitem__(self, key): + """Lookup a variable or raise `KeyError` if the variable is + undefined. + """ + item = self.resolve(key) + if isinstance(item, Undefined): + raise KeyError(key) + return item + + def __repr__(self): + return '<%s %s of %r>' % ( + self.__class__.__name__, + repr(self.get_all()), + self.name + ) + + +# register the context as mapping if possible +try: + from collections import Mapping + Mapping.register(Context) +except ImportError: + pass + + +class BlockReference(object): + """One block on a template reference.""" + + def __init__(self, name, context, stack, depth): + self.name = name + self._context = context + self._stack = stack + self._depth = depth + + @property + def super(self): + """Super the block.""" + if self._depth + 1 >= len(self._stack): + return self._context.environment. \ + undefined('there is no parent block called %r.' % + self.name, name='super') + return BlockReference(self.name, self._context, self._stack, + self._depth + 1) + + @internalcode + def __call__(self): + rv = concat(self._stack[self._depth](self._context)) + if self._context.eval_ctx.autoescape: + rv = Markup(rv) + return rv + + +class LoopContext(object): + """A loop context for dynamic iteration.""" + + def __init__(self, iterable, recurse=None, depth0=0): + self._iterator = iter(iterable) + self._recurse = recurse + self._after = self._safe_next() + self.index0 = -1 + self.depth0 = depth0 + + # try to get the length of the iterable early. This must be done + # here because there are some broken iterators around where there + # __len__ is the number of iterations left (i'm looking at your + # listreverseiterator!). + try: + self._length = len(iterable) + except (TypeError, AttributeError): + self._length = None + + def cycle(self, *args): + """Cycles among the arguments with the current loop index.""" + if not args: + raise TypeError('no items for cycling given') + return args[self.index0 % len(args)] + + first = property(lambda x: x.index0 == 0) + last = property(lambda x: x._after is _last_iteration) + index = property(lambda x: x.index0 + 1) + revindex = property(lambda x: x.length - x.index0) + revindex0 = property(lambda x: x.length - x.index) + depth = property(lambda x: x.depth0 + 1) + + def __len__(self): + return self.length + + def __iter__(self): + return LoopContextIterator(self) + + def _safe_next(self): + try: + return next(self._iterator) + except StopIteration: + return _last_iteration + + @internalcode + def loop(self, iterable): + if self._recurse is None: + raise TypeError('Tried to call non recursive loop. Maybe you ' + "forgot the 'recursive' modifier.") + return self._recurse(iterable, self._recurse, self.depth0 + 1) + + # a nifty trick to enhance the error message if someone tried to call + # the the loop without or with too many arguments. + __call__ = loop + del loop + + @property + def length(self): + if self._length is None: + # if was not possible to get the length of the iterator when + # the loop context was created (ie: iterating over a generator) + # we have to convert the iterable into a sequence and use the + # length of that + the number of iterations so far. + iterable = tuple(self._iterator) + self._iterator = iter(iterable) + iterations_done = self.index0 + 2 + self._length = len(iterable) + iterations_done + return self._length + + def __repr__(self): + return '<%s %r/%r>' % ( + self.__class__.__name__, + self.index, + self.length + ) + + +@implements_iterator +class LoopContextIterator(object): + """The iterator for a loop context.""" + __slots__ = ('context',) + + def __init__(self, context): + self.context = context + + def __iter__(self): + return self + + def __next__(self): + ctx = self.context + ctx.index0 += 1 + if ctx._after is _last_iteration: + raise StopIteration() + next_elem = ctx._after + ctx._after = ctx._safe_next() + return next_elem, ctx + + +class Macro(object): + """Wraps a macro function.""" + + def __init__(self, environment, func, name, arguments, defaults, + catch_kwargs, catch_varargs, caller): + self._environment = environment + self._func = func + self._argument_count = len(arguments) + self.name = name + self.arguments = arguments + self.defaults = defaults + self.catch_kwargs = catch_kwargs + self.catch_varargs = catch_varargs + self.caller = caller + + @internalcode + def __call__(self, *args, **kwargs): + # try to consume the positional arguments + arguments = list(args[:self._argument_count]) + off = len(arguments) + + # if the number of arguments consumed is not the number of + # arguments expected we start filling in keyword arguments + # and defaults. + if off != self._argument_count: + for idx, name in enumerate(self.arguments[len(arguments):]): + try: + value = kwargs.pop(name) + except KeyError: + try: + value = self.defaults[idx - self._argument_count + off] + except IndexError: + value = self._environment.undefined( + 'parameter %r was not provided' % name, name=name) + arguments.append(value) + + # it's important that the order of these arguments does not change + # if not also changed in the compiler's `function_scoping` method. + # the order is caller, keyword arguments, positional arguments! + if self.caller: + caller = kwargs.pop('caller', None) + if caller is None: + caller = self._environment.undefined('No caller defined', + name='caller') + arguments.append(caller) + if self.catch_kwargs: + arguments.append(kwargs) + elif kwargs: + raise TypeError('macro %r takes no keyword argument %r' % + (self.name, next(iter(kwargs)))) + if self.catch_varargs: + arguments.append(args[self._argument_count:]) + elif len(args) > self._argument_count: + raise TypeError('macro %r takes not more than %d argument(s)' % + (self.name, len(self.arguments))) + return self._func(*arguments) + + def __repr__(self): + return '<%s %s>' % ( + self.__class__.__name__, + self.name is None and 'anonymous' or repr(self.name) + ) + + +@implements_to_string +class Undefined(object): + """The default undefined type. This undefined type can be printed and + iterated over, but every other access will raise an :exc:`jinja2.exceptions.UndefinedError`: + + >>> foo = Undefined(name='foo') + >>> str(foo) + '' + >>> not foo + True + >>> foo + 42 + Traceback (most recent call last): + ... + jinja2.exceptions.UndefinedError: 'foo' is undefined + """ + __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name', + '_undefined_exception') + + def __init__(self, hint=None, obj=missing, name=None, exc=UndefinedError): + self._undefined_hint = hint + self._undefined_obj = obj + self._undefined_name = name + self._undefined_exception = exc + + @internalcode + def _fail_with_undefined_error(self, *args, **kwargs): + """Regular callback function for undefined objects that raises an + `jinja2.exceptions.UndefinedError` on call. + """ + if self._undefined_hint is None: + if self._undefined_obj is missing: + hint = '%r is undefined' % self._undefined_name + elif not isinstance(self._undefined_name, string_types): + hint = '%s has no element %r' % ( + object_type_repr(self._undefined_obj), + self._undefined_name + ) + else: + hint = '%r has no attribute %r' % ( + object_type_repr(self._undefined_obj), + self._undefined_name + ) + else: + hint = self._undefined_hint + raise self._undefined_exception(hint) + + @internalcode + def __getattr__(self, name): + if name[:2] == '__': + raise AttributeError(name) + return self._fail_with_undefined_error() + + __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \ + __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \ + __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \ + __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = __int__ = \ + __float__ = __complex__ = __pow__ = __rpow__ = \ + _fail_with_undefined_error + + def __eq__(self, other): + return type(self) is type(other) + + def __ne__(self, other): + return not self.__eq__(other) + + def __hash__(self): + return id(type(self)) + + def __str__(self): + return u'' + + def __len__(self): + return 0 + + def __iter__(self): + if 0: + yield None + + def __nonzero__(self): + return False + __bool__ = __nonzero__ + + def __repr__(self): + return 'Undefined' + + +def make_logging_undefined(logger=None, base=None): + """Given a logger object this returns a new undefined class that will + log certain failures. It will log iterations and printing. If no + logger is given a default logger is created. + + Example:: + + logger = logging.getLogger(__name__) + LoggingUndefined = make_logging_undefined( + logger=logger, + base=Undefined + ) + + .. versionadded:: 2.8 + + :param logger: the logger to use. If not provided, a default logger + is created. + :param base: the base class to add logging functionality to. This + defaults to :class:`Undefined`. + """ + if logger is None: + import logging + logger = logging.getLogger(__name__) + logger.addHandler(logging.StreamHandler(sys.stderr)) + if base is None: + base = Undefined + + def _log_message(undef): + if undef._undefined_hint is None: + if undef._undefined_obj is missing: + hint = '%s is undefined' % undef._undefined_name + elif not isinstance(undef._undefined_name, string_types): + hint = '%s has no element %s' % ( + object_type_repr(undef._undefined_obj), + undef._undefined_name) + else: + hint = '%s has no attribute %s' % ( + object_type_repr(undef._undefined_obj), + undef._undefined_name) + else: + hint = undef._undefined_hint + logger.warning('Template variable warning: %s', hint) + + class LoggingUndefined(base): + + def _fail_with_undefined_error(self, *args, **kwargs): + try: + return base._fail_with_undefined_error(self, *args, **kwargs) + except self._undefined_exception as e: + logger.error('Template variable error: %s', str(e)) + raise e + + def __str__(self): + rv = base.__str__(self) + _log_message(self) + return rv + + def __iter__(self): + rv = base.__iter__(self) + _log_message(self) + return rv + + if PY2: + def __nonzero__(self): + rv = base.__nonzero__(self) + _log_message(self) + return rv + + def __unicode__(self): + rv = base.__unicode__(self) + _log_message(self) + return rv + else: + def __bool__(self): + rv = base.__bool__(self) + _log_message(self) + return rv + + return LoggingUndefined + + +@implements_to_string +class DebugUndefined(Undefined): + """An undefined that returns the debug info when printed. + + >>> foo = DebugUndefined(name='foo') + >>> str(foo) + '{{ foo }}' + >>> not foo + True + >>> foo + 42 + Traceback (most recent call last): + ... + jinja2.exceptions.UndefinedError: 'foo' is undefined + """ + __slots__ = () + + def __str__(self): + if self._undefined_hint is None: + if self._undefined_obj is missing: + return u'{{ %s }}' % self._undefined_name + return '{{ no such element: %s[%r] }}' % ( + object_type_repr(self._undefined_obj), + self._undefined_name + ) + return u'{{ undefined value printed: %s }}' % self._undefined_hint + + +@implements_to_string +class StrictUndefined(Undefined): + """An undefined that barks on print and iteration as well as boolean + tests and all kinds of comparisons. In other words: you can do nothing + with it except checking if it's defined using the `defined` test. + + >>> foo = StrictUndefined(name='foo') + >>> str(foo) + Traceback (most recent call last): + ... + jinja2.exceptions.UndefinedError: 'foo' is undefined + >>> not foo + Traceback (most recent call last): + ... + jinja2.exceptions.UndefinedError: 'foo' is undefined + >>> foo + 42 + Traceback (most recent call last): + ... + jinja2.exceptions.UndefinedError: 'foo' is undefined + """ + __slots__ = () + __iter__ = __str__ = __len__ = __nonzero__ = __eq__ = \ + __ne__ = __bool__ = __hash__ = \ + Undefined._fail_with_undefined_error + + +# remove remaining slots attributes, after the metaclass did the magic they +# are unneeded and irritating as they contain wrong data for the subclasses. +del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__ diff --git a/deps/v8_inspector/deps/jinja2/jinja2/sandbox.py b/deps/v8_inspector/deps/jinja2/jinja2/sandbox.py new file mode 100644 index 00000000000000..8b491e7c3c23c3 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/sandbox.py @@ -0,0 +1,367 @@ +# -*- coding: utf-8 -*- +""" + jinja2.sandbox + ~~~~~~~~~~~~~~ + + Adds a sandbox layer to Jinja as it was the default behavior in the old + Jinja 1 releases. This sandbox is slightly different from Jinja 1 as the + default behavior is easier to use. + + The behavior can be changed by subclassing the environment. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD. +""" +import types +import operator +from jinja2.environment import Environment +from jinja2.exceptions import SecurityError +from jinja2._compat import string_types, PY2 + + +#: maximum number of items a range may produce +MAX_RANGE = 100000 + +#: attributes of function objects that are considered unsafe. +if PY2: + UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict', + 'func_defaults', 'func_globals']) +else: + # On versions > python 2 the special attributes on functions are gone, + # but they remain on methods and generators for whatever reason. + UNSAFE_FUNCTION_ATTRIBUTES = set() + + +#: unsafe method attributes. function attributes are unsafe for methods too +UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self']) + +#: unsafe generator attirbutes. +UNSAFE_GENERATOR_ATTRIBUTES = set(['gi_frame', 'gi_code']) + +import warnings + +# make sure we don't warn in python 2.6 about stuff we don't care about +warnings.filterwarnings('ignore', 'the sets module', DeprecationWarning, + module='jinja2.sandbox') + +from collections import deque + +_mutable_set_types = (set,) +_mutable_mapping_types = (dict,) +_mutable_sequence_types = (list,) + + +# on python 2.x we can register the user collection types +try: + from UserDict import UserDict, DictMixin + from UserList import UserList + _mutable_mapping_types += (UserDict, DictMixin) + _mutable_set_types += (UserList,) +except ImportError: + pass + +# if sets is still available, register the mutable set from there as well +try: + from sets import Set + _mutable_set_types += (Set,) +except ImportError: + pass + +#: register Python 2.6 abstract base classes +try: + from collections import MutableSet, MutableMapping, MutableSequence + _mutable_set_types += (MutableSet,) + _mutable_mapping_types += (MutableMapping,) + _mutable_sequence_types += (MutableSequence,) +except ImportError: + pass + +_mutable_spec = ( + (_mutable_set_types, frozenset([ + 'add', 'clear', 'difference_update', 'discard', 'pop', 'remove', + 'symmetric_difference_update', 'update' + ])), + (_mutable_mapping_types, frozenset([ + 'clear', 'pop', 'popitem', 'setdefault', 'update' + ])), + (_mutable_sequence_types, frozenset([ + 'append', 'reverse', 'insert', 'sort', 'extend', 'remove' + ])), + (deque, frozenset([ + 'append', 'appendleft', 'clear', 'extend', 'extendleft', 'pop', + 'popleft', 'remove', 'rotate' + ])) +) + + +def safe_range(*args): + """A range that can't generate ranges with a length of more than + MAX_RANGE items. + """ + rng = range(*args) + if len(rng) > MAX_RANGE: + raise OverflowError('range too big, maximum size for range is %d' % + MAX_RANGE) + return rng + + +def unsafe(f): + """Marks a function or method as unsafe. + + :: + + @unsafe + def delete(self): + pass + """ + f.unsafe_callable = True + return f + + +def is_internal_attribute(obj, attr): + """Test if the attribute given is an internal python attribute. For + example this function returns `True` for the `func_code` attribute of + python objects. This is useful if the environment method + :meth:`~SandboxedEnvironment.is_safe_attribute` is overridden. + + >>> from jinja2.sandbox import is_internal_attribute + >>> is_internal_attribute(str, "mro") + True + >>> is_internal_attribute(str, "upper") + False + """ + if isinstance(obj, types.FunctionType): + if attr in UNSAFE_FUNCTION_ATTRIBUTES: + return True + elif isinstance(obj, types.MethodType): + if attr in UNSAFE_FUNCTION_ATTRIBUTES or \ + attr in UNSAFE_METHOD_ATTRIBUTES: + return True + elif isinstance(obj, type): + if attr == 'mro': + return True + elif isinstance(obj, (types.CodeType, types.TracebackType, types.FrameType)): + return True + elif isinstance(obj, types.GeneratorType): + if attr in UNSAFE_GENERATOR_ATTRIBUTES: + return True + return attr.startswith('__') + + +def modifies_known_mutable(obj, attr): + """This function checks if an attribute on a builtin mutable object + (list, dict, set or deque) would modify it if called. It also supports + the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and + with Python 2.6 onwards the abstract base classes `MutableSet`, + `MutableMapping`, and `MutableSequence`. + + >>> modifies_known_mutable({}, "clear") + True + >>> modifies_known_mutable({}, "keys") + False + >>> modifies_known_mutable([], "append") + True + >>> modifies_known_mutable([], "index") + False + + If called with an unsupported object (such as unicode) `False` is + returned. + + >>> modifies_known_mutable("foo", "upper") + False + """ + for typespec, unsafe in _mutable_spec: + if isinstance(obj, typespec): + return attr in unsafe + return False + + +class SandboxedEnvironment(Environment): + """The sandboxed environment. It works like the regular environment but + tells the compiler to generate sandboxed code. Additionally subclasses of + this environment may override the methods that tell the runtime what + attributes or functions are safe to access. + + If the template tries to access insecure code a :exc:`SecurityError` is + raised. However also other exceptions may occur during the rendering so + the caller has to ensure that all exceptions are caught. + """ + sandboxed = True + + #: default callback table for the binary operators. A copy of this is + #: available on each instance of a sandboxed environment as + #: :attr:`binop_table` + default_binop_table = { + '+': operator.add, + '-': operator.sub, + '*': operator.mul, + '/': operator.truediv, + '//': operator.floordiv, + '**': operator.pow, + '%': operator.mod + } + + #: default callback table for the unary operators. A copy of this is + #: available on each instance of a sandboxed environment as + #: :attr:`unop_table` + default_unop_table = { + '+': operator.pos, + '-': operator.neg + } + + #: a set of binary operators that should be intercepted. Each operator + #: that is added to this set (empty by default) is delegated to the + #: :meth:`call_binop` method that will perform the operator. The default + #: operator callback is specified by :attr:`binop_table`. + #: + #: The following binary operators are interceptable: + #: ``//``, ``%``, ``+``, ``*``, ``-``, ``/``, and ``**`` + #: + #: The default operation form the operator table corresponds to the + #: builtin function. Intercepted calls are always slower than the native + #: operator call, so make sure only to intercept the ones you are + #: interested in. + #: + #: .. versionadded:: 2.6 + intercepted_binops = frozenset() + + #: a set of unary operators that should be intercepted. Each operator + #: that is added to this set (empty by default) is delegated to the + #: :meth:`call_unop` method that will perform the operator. The default + #: operator callback is specified by :attr:`unop_table`. + #: + #: The following unary operators are interceptable: ``+``, ``-`` + #: + #: The default operation form the operator table corresponds to the + #: builtin function. Intercepted calls are always slower than the native + #: operator call, so make sure only to intercept the ones you are + #: interested in. + #: + #: .. versionadded:: 2.6 + intercepted_unops = frozenset() + + def intercept_unop(self, operator): + """Called during template compilation with the name of a unary + operator to check if it should be intercepted at runtime. If this + method returns `True`, :meth:`call_unop` is excuted for this unary + operator. The default implementation of :meth:`call_unop` will use + the :attr:`unop_table` dictionary to perform the operator with the + same logic as the builtin one. + + The following unary operators are interceptable: ``+`` and ``-`` + + Intercepted calls are always slower than the native operator call, + so make sure only to intercept the ones you are interested in. + + .. versionadded:: 2.6 + """ + return False + + + def __init__(self, *args, **kwargs): + Environment.__init__(self, *args, **kwargs) + self.globals['range'] = safe_range + self.binop_table = self.default_binop_table.copy() + self.unop_table = self.default_unop_table.copy() + + def is_safe_attribute(self, obj, attr, value): + """The sandboxed environment will call this method to check if the + attribute of an object is safe to access. Per default all attributes + starting with an underscore are considered private as well as the + special attributes of internal python objects as returned by the + :func:`is_internal_attribute` function. + """ + return not (attr.startswith('_') or is_internal_attribute(obj, attr)) + + def is_safe_callable(self, obj): + """Check if an object is safely callable. Per default a function is + considered safe unless the `unsafe_callable` attribute exists and is + True. Override this method to alter the behavior, but this won't + affect the `unsafe` decorator from this module. + """ + return not (getattr(obj, 'unsafe_callable', False) or + getattr(obj, 'alters_data', False)) + + def call_binop(self, context, operator, left, right): + """For intercepted binary operator calls (:meth:`intercepted_binops`) + this function is executed instead of the builtin operator. This can + be used to fine tune the behavior of certain operators. + + .. versionadded:: 2.6 + """ + return self.binop_table[operator](left, right) + + def call_unop(self, context, operator, arg): + """For intercepted unary operator calls (:meth:`intercepted_unops`) + this function is executed instead of the builtin operator. This can + be used to fine tune the behavior of certain operators. + + .. versionadded:: 2.6 + """ + return self.unop_table[operator](arg) + + def getitem(self, obj, argument): + """Subscribe an object from sandboxed code.""" + try: + return obj[argument] + except (TypeError, LookupError): + if isinstance(argument, string_types): + try: + attr = str(argument) + except Exception: + pass + else: + try: + value = getattr(obj, attr) + except AttributeError: + pass + else: + if self.is_safe_attribute(obj, argument, value): + return value + return self.unsafe_undefined(obj, argument) + return self.undefined(obj=obj, name=argument) + + def getattr(self, obj, attribute): + """Subscribe an object from sandboxed code and prefer the + attribute. The attribute passed *must* be a bytestring. + """ + try: + value = getattr(obj, attribute) + except AttributeError: + try: + return obj[attribute] + except (TypeError, LookupError): + pass + else: + if self.is_safe_attribute(obj, attribute, value): + return value + return self.unsafe_undefined(obj, attribute) + return self.undefined(obj=obj, name=attribute) + + def unsafe_undefined(self, obj, attribute): + """Return an undefined object for unsafe attributes.""" + return self.undefined('access to attribute %r of %r ' + 'object is unsafe.' % ( + attribute, + obj.__class__.__name__ + ), name=attribute, obj=obj, exc=SecurityError) + + def call(__self, __context, __obj, *args, **kwargs): + """Call an object from sandboxed code.""" + # the double prefixes are to avoid double keyword argument + # errors when proxying the call. + if not __self.is_safe_callable(__obj): + raise SecurityError('%r is not safely callable' % (__obj,)) + return __context.call(__obj, *args, **kwargs) + + +class ImmutableSandboxedEnvironment(SandboxedEnvironment): + """Works exactly like the regular `SandboxedEnvironment` but does not + permit modifications on the builtin mutable objects `list`, `set`, and + `dict` by using the :func:`modifies_known_mutable` function. + """ + + def is_safe_attribute(self, obj, attr, value): + if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value): + return False + return not modifies_known_mutable(obj, attr) diff --git a/deps/v8_inspector/deps/jinja2/jinja2/tests.py b/deps/v8_inspector/deps/jinja2/jinja2/tests.py new file mode 100644 index 00000000000000..bb32349df0b7e6 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/tests.py @@ -0,0 +1,173 @@ +# -*- coding: utf-8 -*- +""" + jinja2.tests + ~~~~~~~~~~~~ + + Jinja test functions. Used with the "is" operator. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import re +from collections import Mapping +from jinja2.runtime import Undefined +from jinja2._compat import text_type, string_types, integer_types +import decimal + +number_re = re.compile(r'^-?\d+(\.\d+)?$') +regex_type = type(number_re) + + +test_callable = callable + + +def test_odd(value): + """Return true if the variable is odd.""" + return value % 2 == 1 + + +def test_even(value): + """Return true if the variable is even.""" + return value % 2 == 0 + + +def test_divisibleby(value, num): + """Check if a variable is divisible by a number.""" + return value % num == 0 + + +def test_defined(value): + """Return true if the variable is defined: + + .. sourcecode:: jinja + + {% if variable is defined %} + value of variable: {{ variable }} + {% else %} + variable is not defined + {% endif %} + + See the :func:`default` filter for a simple way to set undefined + variables. + """ + return not isinstance(value, Undefined) + + +def test_undefined(value): + """Like :func:`defined` but the other way round.""" + return isinstance(value, Undefined) + + +def test_none(value): + """Return true if the variable is none.""" + return value is None + + +def test_lower(value): + """Return true if the variable is lowercased.""" + return text_type(value).islower() + + +def test_upper(value): + """Return true if the variable is uppercased.""" + return text_type(value).isupper() + + +def test_string(value): + """Return true if the object is a string.""" + return isinstance(value, string_types) + + +def test_mapping(value): + """Return true if the object is a mapping (dict etc.). + + .. versionadded:: 2.6 + """ + return isinstance(value, Mapping) + + +def test_number(value): + """Return true if the variable is a number.""" + return isinstance(value, integer_types + (float, complex, decimal.Decimal)) + + +def test_sequence(value): + """Return true if the variable is a sequence. Sequences are variables + that are iterable. + """ + try: + len(value) + value.__getitem__ + except: + return False + return True + + +def test_equalto(value, other): + """Check if an object has the same value as another object: + + .. sourcecode:: jinja + + {% if foo.expression is equalto 42 %} + the foo attribute evaluates to the constant 42 + {% endif %} + + This appears to be a useless test as it does exactly the same as the + ``==`` operator, but it can be useful when used together with the + `selectattr` function: + + .. sourcecode:: jinja + + {{ users|selectattr("email", "equalto", "foo@bar.invalid") }} + + .. versionadded:: 2.8 + """ + return value == other + + +def test_sameas(value, other): + """Check if an object points to the same memory address than another + object: + + .. sourcecode:: jinja + + {% if foo.attribute is sameas false %} + the foo attribute really is the `False` singleton + {% endif %} + """ + return value is other + + +def test_iterable(value): + """Check if it's possible to iterate over an object.""" + try: + iter(value) + except TypeError: + return False + return True + + +def test_escaped(value): + """Check if the value is escaped.""" + return hasattr(value, '__html__') + + +TESTS = { + 'odd': test_odd, + 'even': test_even, + 'divisibleby': test_divisibleby, + 'defined': test_defined, + 'undefined': test_undefined, + 'none': test_none, + 'lower': test_lower, + 'upper': test_upper, + 'string': test_string, + 'mapping': test_mapping, + 'number': test_number, + 'sequence': test_sequence, + 'iterable': test_iterable, + 'callable': test_callable, + 'sameas': test_sameas, + 'equalto': test_equalto, + 'escaped': test_escaped +} diff --git a/deps/v8_inspector/deps/jinja2/jinja2/utils.py b/deps/v8_inspector/deps/jinja2/jinja2/utils.py new file mode 100644 index 00000000000000..612d5c3d8bb9b7 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/utils.py @@ -0,0 +1,531 @@ +# -*- coding: utf-8 -*- +""" + jinja2.utils + ~~~~~~~~~~~~ + + Utility functions. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import re +import errno +from collections import deque +from threading import Lock +from jinja2._compat import text_type, string_types, implements_iterator, \ + url_quote + + +_word_split_re = re.compile(r'(\s+)') +_punctuation_re = re.compile( + '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % ( + '|'.join(map(re.escape, ('(', '<', '<'))), + '|'.join(map(re.escape, ('.', ',', ')', '>', '\n', '>'))) + ) +) +_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$') +_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)') +_entity_re = re.compile(r'&([^;]+);') +_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' +_digits = '0123456789' + +# special singleton representing missing values for the runtime +missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})() + +# internal code +internal_code = set() + +concat = u''.join + + +def contextfunction(f): + """This decorator can be used to mark a function or method context callable. + A context callable is passed the active :class:`Context` as first argument when + called from the template. This is useful if a function wants to get access + to the context or functions provided on the context object. For example + a function that returns a sorted list of template variables the current + template exports could look like this:: + + @contextfunction + def get_exported_names(context): + return sorted(context.exported_vars) + """ + f.contextfunction = True + return f + + +def evalcontextfunction(f): + """This decorator can be used to mark a function or method as an eval + context callable. This is similar to the :func:`contextfunction` + but instead of passing the context, an evaluation context object is + passed. For more information about the eval context, see + :ref:`eval-context`. + + .. versionadded:: 2.4 + """ + f.evalcontextfunction = True + return f + + +def environmentfunction(f): + """This decorator can be used to mark a function or method as environment + callable. This decorator works exactly like the :func:`contextfunction` + decorator just that the first argument is the active :class:`Environment` + and not context. + """ + f.environmentfunction = True + return f + + +def internalcode(f): + """Marks the function as internally used""" + internal_code.add(f.__code__) + return f + + +def is_undefined(obj): + """Check if the object passed is undefined. This does nothing more than + performing an instance check against :class:`Undefined` but looks nicer. + This can be used for custom filters or tests that want to react to + undefined variables. For example a custom default filter can look like + this:: + + def default(var, default=''): + if is_undefined(var): + return default + return var + """ + from jinja2.runtime import Undefined + return isinstance(obj, Undefined) + + +def consume(iterable): + """Consumes an iterable without doing anything with it.""" + for event in iterable: + pass + + +def clear_caches(): + """Jinja2 keeps internal caches for environments and lexers. These are + used so that Jinja2 doesn't have to recreate environments and lexers all + the time. Normally you don't have to care about that but if you are + messuring memory consumption you may want to clean the caches. + """ + from jinja2.environment import _spontaneous_environments + from jinja2.lexer import _lexer_cache + _spontaneous_environments.clear() + _lexer_cache.clear() + + +def import_string(import_name, silent=False): + """Imports an object based on a string. This is useful if you want to + use import paths as endpoints or something similar. An import path can + be specified either in dotted notation (``xml.sax.saxutils.escape``) + or with a colon as object delimiter (``xml.sax.saxutils:escape``). + + If the `silent` is True the return value will be `None` if the import + fails. + + :return: imported object + """ + try: + if ':' in import_name: + module, obj = import_name.split(':', 1) + elif '.' in import_name: + items = import_name.split('.') + module = '.'.join(items[:-1]) + obj = items[-1] + else: + return __import__(import_name) + return getattr(__import__(module, None, None, [obj]), obj) + except (ImportError, AttributeError): + if not silent: + raise + + +def open_if_exists(filename, mode='rb'): + """Returns a file descriptor for the filename if that file exists, + otherwise `None`. + """ + try: + return open(filename, mode) + except IOError as e: + if e.errno not in (errno.ENOENT, errno.EISDIR, errno.EINVAL): + raise + + +def object_type_repr(obj): + """Returns the name of the object's type. For some recognized + singletons the name of the object is returned instead. (For + example for `None` and `Ellipsis`). + """ + if obj is None: + return 'None' + elif obj is Ellipsis: + return 'Ellipsis' + # __builtin__ in 2.x, builtins in 3.x + if obj.__class__.__module__ in ('__builtin__', 'builtins'): + name = obj.__class__.__name__ + else: + name = obj.__class__.__module__ + '.' + obj.__class__.__name__ + return '%s object' % name + + +def pformat(obj, verbose=False): + """Prettyprint an object. Either use the `pretty` library or the + builtin `pprint`. + """ + try: + from pretty import pretty + return pretty(obj, verbose=verbose) + except ImportError: + from pprint import pformat + return pformat(obj) + + +def urlize(text, trim_url_limit=None, nofollow=False, target=None): + """Converts any URLs in text into clickable links. Works on http://, + https:// and www. links. Links can have trailing punctuation (periods, + commas, close-parens) and leading punctuation (opening parens) and + it'll still do the right thing. + + If trim_url_limit is not None, the URLs in link text will be limited + to trim_url_limit characters. + + If nofollow is True, the URLs in link text will get a rel="nofollow" + attribute. + + If target is not None, a target attribute will be added to the link. + """ + trim_url = lambda x, limit=trim_url_limit: limit is not None \ + and (x[:limit] + (len(x) >=limit and '...' + or '')) or x + words = _word_split_re.split(text_type(escape(text))) + nofollow_attr = nofollow and ' rel="nofollow"' or '' + if target is not None and isinstance(target, string_types): + target_attr = ' target="%s"' % target + else: + target_attr = '' + for i, word in enumerate(words): + match = _punctuation_re.match(word) + if match: + lead, middle, trail = match.groups() + if middle.startswith('www.') or ( + '@' not in middle and + not middle.startswith('http://') and + not middle.startswith('https://') and + len(middle) > 0 and + middle[0] in _letters + _digits and ( + middle.endswith('.org') or + middle.endswith('.net') or + middle.endswith('.com') + )): + middle = '<a href="http://%s"%s%s>%s</a>' % (middle, + nofollow_attr, target_attr, trim_url(middle)) + if middle.startswith('http://') or \ + middle.startswith('https://'): + middle = '<a href="%s"%s%s>%s</a>' % (middle, + nofollow_attr, target_attr, trim_url(middle)) + if '@' in middle and not middle.startswith('www.') and \ + not ':' in middle and _simple_email_re.match(middle): + middle = '<a href="mailto:%s">%s</a>' % (middle, middle) + if lead + middle + trail != word: + words[i] = lead + middle + trail + return u''.join(words) + + +def generate_lorem_ipsum(n=5, html=True, min=20, max=100): + """Generate some lorem ipsum for the template.""" + from jinja2.constants import LOREM_IPSUM_WORDS + from random import choice, randrange + words = LOREM_IPSUM_WORDS.split() + result = [] + + for _ in range(n): + next_capitalized = True + last_comma = last_fullstop = 0 + word = None + last = None + p = [] + + # each paragraph contains out of 20 to 100 words. + for idx, _ in enumerate(range(randrange(min, max))): + while True: + word = choice(words) + if word != last: + last = word + break + if next_capitalized: + word = word.capitalize() + next_capitalized = False + # add commas + if idx - randrange(3, 8) > last_comma: + last_comma = idx + last_fullstop += 2 + word += ',' + # add end of sentences + if idx - randrange(10, 20) > last_fullstop: + last_comma = last_fullstop = idx + word += '.' + next_capitalized = True + p.append(word) + + # ensure that the paragraph ends with a dot. + p = u' '.join(p) + if p.endswith(','): + p = p[:-1] + '.' + elif not p.endswith('.'): + p += '.' + result.append(p) + + if not html: + return u'\n\n'.join(result) + return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result)) + + +def unicode_urlencode(obj, charset='utf-8', for_qs=False): + """URL escapes a single bytestring or unicode string with the + given charset if applicable to URL safe quoting under all rules + that need to be considered under all supported Python versions. + + If non strings are provided they are converted to their unicode + representation first. + """ + if not isinstance(obj, string_types): + obj = text_type(obj) + if isinstance(obj, text_type): + obj = obj.encode(charset) + safe = not for_qs and b'/' or b'' + rv = text_type(url_quote(obj, safe)) + if for_qs: + rv = rv.replace('%20', '+') + return rv + + +class LRUCache(object): + """A simple LRU Cache implementation.""" + + # this is fast for small capacities (something below 1000) but doesn't + # scale. But as long as it's only used as storage for templates this + # won't do any harm. + + def __init__(self, capacity): + self.capacity = capacity + self._mapping = {} + self._queue = deque() + self._postinit() + + def _postinit(self): + # alias all queue methods for faster lookup + self._popleft = self._queue.popleft + self._pop = self._queue.pop + self._remove = self._queue.remove + self._wlock = Lock() + self._append = self._queue.append + + def __getstate__(self): + return { + 'capacity': self.capacity, + '_mapping': self._mapping, + '_queue': self._queue + } + + def __setstate__(self, d): + self.__dict__.update(d) + self._postinit() + + def __getnewargs__(self): + return (self.capacity,) + + def copy(self): + """Return a shallow copy of the instance.""" + rv = self.__class__(self.capacity) + rv._mapping.update(self._mapping) + rv._queue = deque(self._queue) + return rv + + def get(self, key, default=None): + """Return an item from the cache dict or `default`""" + try: + return self[key] + except KeyError: + return default + + def setdefault(self, key, default=None): + """Set `default` if the key is not in the cache otherwise + leave unchanged. Return the value of this key. + """ + self._wlock.acquire() + try: + try: + return self[key] + except KeyError: + self[key] = default + return default + finally: + self._wlock.release() + + def clear(self): + """Clear the cache.""" + self._wlock.acquire() + try: + self._mapping.clear() + self._queue.clear() + finally: + self._wlock.release() + + def __contains__(self, key): + """Check if a key exists in this cache.""" + return key in self._mapping + + def __len__(self): + """Return the current size of the cache.""" + return len(self._mapping) + + def __repr__(self): + return '<%s %r>' % ( + self.__class__.__name__, + self._mapping + ) + + def __getitem__(self, key): + """Get an item from the cache. Moves the item up so that it has the + highest priority then. + + Raise a `KeyError` if it does not exist. + """ + self._wlock.acquire() + try: + rv = self._mapping[key] + if self._queue[-1] != key: + try: + self._remove(key) + except ValueError: + # if something removed the key from the container + # when we read, ignore the ValueError that we would + # get otherwise. + pass + self._append(key) + return rv + finally: + self._wlock.release() + + def __setitem__(self, key, value): + """Sets the value for an item. Moves the item up so that it + has the highest priority then. + """ + self._wlock.acquire() + try: + if key in self._mapping: + self._remove(key) + elif len(self._mapping) == self.capacity: + del self._mapping[self._popleft()] + self._append(key) + self._mapping[key] = value + finally: + self._wlock.release() + + def __delitem__(self, key): + """Remove an item from the cache dict. + Raise a `KeyError` if it does not exist. + """ + self._wlock.acquire() + try: + del self._mapping[key] + try: + self._remove(key) + except ValueError: + # __getitem__ is not locked, it might happen + pass + finally: + self._wlock.release() + + def items(self): + """Return a list of items.""" + result = [(key, self._mapping[key]) for key in list(self._queue)] + result.reverse() + return result + + def iteritems(self): + """Iterate over all items.""" + return iter(self.items()) + + def values(self): + """Return a list of all values.""" + return [x[1] for x in self.items()] + + def itervalue(self): + """Iterate over all values.""" + return iter(self.values()) + + def keys(self): + """Return a list of all keys ordered by most recent usage.""" + return list(self) + + def iterkeys(self): + """Iterate over all keys in the cache dict, ordered by + the most recent usage. + """ + return reversed(tuple(self._queue)) + + __iter__ = iterkeys + + def __reversed__(self): + """Iterate over the values in the cache dict, oldest items + coming first. + """ + return iter(tuple(self._queue)) + + __copy__ = copy + + +# register the LRU cache as mutable mapping if possible +try: + from collections import MutableMapping + MutableMapping.register(LRUCache) +except ImportError: + pass + + +@implements_iterator +class Cycler(object): + """A cycle helper for templates.""" + + def __init__(self, *items): + if not items: + raise RuntimeError('at least one item has to be provided') + self.items = items + self.reset() + + def reset(self): + """Resets the cycle.""" + self.pos = 0 + + @property + def current(self): + """Returns the current item.""" + return self.items[self.pos] + + def __next__(self): + """Goes one item ahead and returns it.""" + rv = self.current + self.pos = (self.pos + 1) % len(self.items) + return rv + + +class Joiner(object): + """A joining helper for templates.""" + + def __init__(self, sep=u', '): + self.sep = sep + self.used = False + + def __call__(self): + if not self.used: + self.used = True + return u'' + return self.sep + + +# Imported here because that's where it was in the past +from markupsafe import Markup, escape, soft_unicode diff --git a/deps/v8_inspector/deps/jinja2/jinja2/visitor.py b/deps/v8_inspector/deps/jinja2/jinja2/visitor.py new file mode 100644 index 00000000000000..413e7c309dc7fc --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/jinja2/visitor.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +""" + jinja2.visitor + ~~~~~~~~~~~~~~ + + This module implements a visitor for the nodes. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD. +""" +from jinja2.nodes import Node + + +class NodeVisitor(object): + """Walks the abstract syntax tree and call visitor functions for every + node found. The visitor functions may return values which will be + forwarded by the `visit` method. + + Per default the visitor functions for the nodes are ``'visit_'`` + + class name of the node. So a `TryFinally` node visit function would + be `visit_TryFinally`. This behavior can be changed by overriding + the `get_visitor` function. If no visitor function exists for a node + (return value `None`) the `generic_visit` visitor is used instead. + """ + + def get_visitor(self, node): + """Return the visitor function for this node or `None` if no visitor + exists for this node. In that case the generic visit function is + used instead. + """ + method = 'visit_' + node.__class__.__name__ + return getattr(self, method, None) + + def visit(self, node, *args, **kwargs): + """Visit a node.""" + f = self.get_visitor(node) + if f is not None: + return f(node, *args, **kwargs) + return self.generic_visit(node, *args, **kwargs) + + def generic_visit(self, node, *args, **kwargs): + """Called if no explicit visitor function exists for a node.""" + for node in node.iter_child_nodes(): + self.visit(node, *args, **kwargs) + + +class NodeTransformer(NodeVisitor): + """Walks the abstract syntax tree and allows modifications of nodes. + + The `NodeTransformer` will walk the AST and use the return value of the + visitor functions to replace or remove the old node. If the return + value of the visitor function is `None` the node will be removed + from the previous location otherwise it's replaced with the return + value. The return value may be the original node in which case no + replacement takes place. + """ + + def generic_visit(self, node, *args, **kwargs): + for field, old_value in node.iter_fields(): + if isinstance(old_value, list): + new_values = [] + for value in old_value: + if isinstance(value, Node): + value = self.visit(value, *args, **kwargs) + if value is None: + continue + elif not isinstance(value, Node): + new_values.extend(value) + continue + new_values.append(value) + old_value[:] = new_values + elif isinstance(old_value, Node): + new_node = self.visit(old_value, *args, **kwargs) + if new_node is None: + delattr(node, field) + else: + setattr(node, field, new_node) + return node + + def visit_list(self, node, *args, **kwargs): + """As transformers may return lists in some places this method + can be used to enforce a list as return value. + """ + rv = self.visit(node, *args, **kwargs) + if not isinstance(rv, list): + rv = [rv] + return rv diff --git a/deps/v8_inspector/deps/jinja2/scripts/jinja2-debug.py b/deps/v8_inspector/deps/jinja2/scripts/jinja2-debug.py new file mode 100755 index 00000000000000..d052adc37e5753 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/scripts/jinja2-debug.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + Jinja2 Debug Interface + ~~~~~~~~~~~~~~~~~~~~~~ + + Helper script for internal Jinja2 debugging. Requires Werkzeug. + + :copyright: Copyright 2010 by Armin Ronacher. + :license: BSD. +""" +import sys +import jinja2 +from werkzeug import script + +env = jinja2.Environment(extensions=['jinja2.ext.i18n', 'jinja2.ext.do', + 'jinja2.ext.loopcontrols', + 'jinja2.ext.with_', + 'jinja2.ext.autoescape'], + autoescape=True) + +def shell_init_func(): + def _compile(x): + print(env.compile(x, raw=True)) + result = { + 'e': env, + 'c': _compile, + 't': env.from_string, + 'p': env.parse + } + for key in jinja2.__all__: + result[key] = getattr(jinja2, key) + return result + + +def action_compile(): + print(env.compile(sys.stdin.read(), raw=True)) + +action_shell = script.make_shell(shell_init_func) + + +if __name__ == '__main__': + script.run() diff --git a/deps/v8_inspector/deps/jinja2/scripts/make-release.py b/deps/v8_inspector/deps/jinja2/scripts/make-release.py new file mode 100644 index 00000000000000..6fd98fed546a3d --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/scripts/make-release.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + make-release + ~~~~~~~~~~~~ + + Helper script that performs a release. Does pretty much everything + automatically for us. + + :copyright: (c) 2011 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" +import sys +import os +import re +from datetime import datetime, date +from subprocess import Popen, PIPE + +try: + import wheel +except ImportError: + wheel = None + +_date_strip_re = re.compile(r'(?<=\d)(st|nd|rd|th)') + + +def parse_changelog(): + with open('CHANGES') as f: + lineiter = iter(f) + for line in lineiter: + match = re.search('^Version\s+(.*)', line.strip()) + if match is None: + continue + length = len(match.group(1)) + version = match.group(1).strip() + if lineiter.next().count('-') != len(match.group(0)): + continue + while 1: + change_info = lineiter.next().strip() + if change_info: + break + + match = re.search(r'(?:codename (.*),\s*)?' + r'released on (\w+\s+\d+\w+\s+\d+)(?i)', + change_info) + if match is None: + continue + + codename, datestr = match.groups() + return version, parse_date(datestr), codename + + +def bump_version(version): + try: + parts = map(int, version.split('.')) + except ValueError: + fail('Current version is not numeric') + parts[-1] += 1 + return '.'.join(map(str, parts)) + + +def parse_date(string): + string = _date_strip_re.sub('', string) + return datetime.strptime(string, '%B %d %Y') + + +def set_filename_version(filename, version_number, pattern): + changed = [] + def inject_version(match): + before, old, after = match.groups() + changed.append(True) + return before + version_number + after + with open(filename) as f: + contents = re.sub(r"^(\s*%s\s*=\s*')(.+?)(')(?sm)" % pattern, + inject_version, f.read()) + + if not changed: + fail('Could not find %s in %s', pattern, filename) + + with open(filename, 'w') as f: + f.write(contents) + + +def set_init_version(version): + info('Setting __init__.py version to %s', version) + set_filename_version('jinja2/__init__.py', version, '__version__') + + +def set_setup_version(version): + info('Setting setup.py version to %s', version) + set_filename_version('setup.py', version, 'version') + + +def build_and_upload(): + cmd = [sys.executable, 'setup.py', 'release', 'sdist', 'bdist_wheel', 'upload'] + if wheel is not None: + cmd.insert(4, 'bdist_wheel') + Popen(cmd).wait() + + +def fail(message, *args): + print >> sys.stderr, 'Error:', message % args + sys.exit(1) + + +def info(message, *args): + print >> sys.stderr, message % args + + +def get_git_tags(): + return set(Popen(['git', 'tag'], stdout=PIPE).communicate()[0].splitlines()) + + +def git_is_clean(): + return Popen(['git', 'diff', '--quiet']).wait() == 0 + + +def make_git_commit(message, *args): + message = message % args + Popen(['git', 'commit', '-am', message]).wait() + + +def make_git_tag(tag): + info('Tagging "%s"', tag) + Popen(['git', 'tag', tag]).wait() + + +def main(): + os.chdir(os.path.join(os.path.dirname(__file__), '..')) + + rv = parse_changelog() + if rv is None: + fail('Could not parse changelog') + + version, release_date, codename = rv + dev_version = bump_version(version) + '.dev' + + info('Releasing %s (codename %s, release date %s)', + version, codename, release_date.strftime('%d/%m/%Y')) + tags = get_git_tags() + + if version in tags: + fail('Version "%s" is already tagged', version) + if release_date.date() != date.today(): + fail('Release date is not today (%s != %s)', release_date.date(), date.today()) + + if not git_is_clean(): + fail('You have uncommitted changes in git') + + if wheel is None: + print ('Warning: You need to install the wheel package ' + 'to upload a wheel distribution.') + + set_init_version(version) + set_setup_version(version) + make_git_commit('Bump version number to %s', version) + make_git_tag(version) + build_and_upload() + set_init_version(dev_version) + set_setup_version(dev_version) + + +if __name__ == '__main__': + main() diff --git a/deps/v8_inspector/deps/jinja2/scripts/pylintrc b/deps/v8_inspector/deps/jinja2/scripts/pylintrc new file mode 100644 index 00000000000000..6ebf385bcee24e --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/scripts/pylintrc @@ -0,0 +1,301 @@ +# lint Python modules using external checkers. +# +# This is the main checker controling the other ones and the reports +# generation. It is itself both a raw checker and an astng checker in order +# to: +# * handle message activation / deactivation at the module level +# * handle some basic but necessary stats'data (number of classes, methods...) +# +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Profiled execution. +profile=no + +# Add <file or directory> to the black list. It should be a base name, not a +# path. You may set this option multiple times. +ignore=.svn + +# Pickle collected data for later comparisons. +persistent=yes + +# Set the cache size for astng objects. +cache-size=500 + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + + +[MESSAGES CONTROL] + +# Enable only checker(s) with the given id(s). This option conflict with the +# disable-checker option +#enable-checker= + +# Enable all checker(s) except those with the given id(s). This option conflict +# with the disable-checker option +#disable-checker= + +# Enable all messages in the listed categories. +#enable-msg-cat= + +# Disable all messages in the listed categories. +#disable-msg-cat= + +# Enable the message(s) with the given id(s). +#enable-msg= + +# Disable the message(s) with the given id(s). +disable-msg=C0323,W0142,C0301,C0103,C0111,E0213,C0302,C0203,W0703,R0201 + + +[REPORTS] + +# set the output format. Available formats are text, parseable, colorized and +# html +output-format=colorized + +# Include message's id in output +include-ids=yes + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Tells wether to display a full report or only the messages +reports=yes + +# Python expression which should return a note less than 10 (10 is the highest +# note).You have access to the variables errors warning, statement which +# respectivly contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (R0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Add a comment according to your evaluation note. This is used by the global +# evaluation report (R0004). +comment=no + +# Enable the report(s) with the given id(s). +#enable-report= + +# Disable the report(s) with the given id(s). +#disable-report= + + +# checks for +# * unused variables / imports +# * undefined variables +# * redefinition of variable from builtins or from an outer scope +# * use of variable before assigment +# +[VARIABLES] + +# Tells wether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching names used for dummy variables (i.e. not used). +dummy-variables-rgx=_|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + + +# try to find bugs in the code using type inference +# +[TYPECHECK] + +# Tells wether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# When zope mode is activated, consider the acquired-members option to ignore +# access to some undefined attributes. +zope=no + +# List of members which are usually get through zope's acquisition mecanism and +# so shouldn't trigger E0201 when accessed (need zope=yes to be considered). +acquired-members=REQUEST,acl_users,aq_parent + + +# checks for : +# * doc strings +# * modules / classes / functions / methods / arguments / variables name +# * number of arguments, local variables, branchs, returns and statements in +# functions, methods +# * required module attributes +# * dangerous default values as arguments +# * redefinition of function / method / class +# * uses of the global statement +# +[BASIC] + +# Required attributes for module, separated by a comma +required-attributes= + +# Regular expression which should only match functions or classes name which do +# not require a docstring +no-docstring-rgx=__.*__ + +# Regular expression which should only match correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression which should only match correct module level names +const-rgx=(([A-Z_][A-Z1-9_]*)|(__.*__))$ + +# Regular expression which should only match correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression which should only match correct function names +function-rgx=[a-z_][a-z0-9_]*$ + +# Regular expression which should only match correct method names +method-rgx=[a-z_][a-z0-9_]*$ + +# Regular expression which should only match correct instance attribute names +attr-rgx=[a-z_][a-z0-9_]*$ + +# Regular expression which should only match correct argument names +argument-rgx=[a-z_][a-z0-9_]*$ + +# Regular expression which should only match correct variable names +variable-rgx=[a-z_][a-z0-9_]*$ + +# Regular expression which should only match correct list comprehension / +# generator expression variable names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# List of builtins function names that should not be used, separated by a comma +bad-functions=apply,input + + +# checks for sign of poor/misdesign: +# * number of methods, attributes, local variables... +# * size, complexity of functions, methods +# +[DESIGN] + +# Maximum number of arguments for function / method +max-args=12 + +# Maximum number of locals for function / method body +max-locals=30 + +# Maximum number of return / yield for function / method body +max-returns=12 + +# Maximum number of branch for function / method body +max-branchs=30 + +# Maximum number of statements in function / method body +max-statements=60 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=20 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=0 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + + +# checks for +# * external modules dependencies +# * relative / wildcard imports +# * cyclic imports +# * uses of deprecated modules +# +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,string,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report R0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report R0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report R0402 must +# not be disabled) +int-import-graph= + + +# checks for : +# * methods without self as first argument +# * overridden methods signature +# * access only to existant members via self +# * attributes not defined in the __init__ method +# * supported interfaces implementation +# * unreachable code +# +[CLASSES] + +# List of interface methods to ignore, separated by a comma. This is used for +# instance to not check methods defines in Zope's Interface base class. +ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + + +# checks for similarities and duplicated code. This computation may be +# memory / CPU intensive, so you should disable it if you experiments some +# problems. +# +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=10 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + + +# checks for: +# * warning notes in the code like FIXME, XXX +# * PEP 263: source code with non ascii character but no encoding declaration +# +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +# checks for : +# * unauthorized constructions +# * strict indentation +# * line length +# * use of <> instead of != +# +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=90 + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' diff --git a/deps/v8_inspector/deps/jinja2/setup.cfg b/deps/v8_inspector/deps/jinja2/setup.cfg new file mode 100644 index 00000000000000..058cdfc1c68e17 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/setup.cfg @@ -0,0 +1,8 @@ +[wheel] +universal = 1 + +[aliases] +release = egg_info -RDb '' + +[pytest] +norecursedirs = .* *.egg *.egg-info env* artwork docs examples diff --git a/deps/v8_inspector/deps/jinja2/setup.py b/deps/v8_inspector/deps/jinja2/setup.py new file mode 100644 index 00000000000000..507fd073533fb9 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/setup.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +""" +Jinja2 +~~~~~~ + +Jinja2 is a template engine written in pure Python. It provides a +`Django`_ inspired non-XML syntax but supports inline expressions and +an optional `sandboxed`_ environment. + +Nutshell +-------- + +Here a small example of a Jinja template:: + + {% extends 'base.html' %} + {% block title %}Memberlist{% endblock %} + {% block content %} + <ul> + {% for user in users %} + <li><a href="{{ user.url }}">{{ user.username }}</a></li> + {% endfor %} + </ul> + {% endblock %} + +Philosophy +---------- + +Application logic is for the controller but don't try to make the life +for the template designer too hard by giving him too few functionality. + +For more informations visit the new `Jinja2 webpage`_ and `documentation`_. + +.. _sandboxed: http://en.wikipedia.org/wiki/Sandbox_(computer_security) +.. _Django: http://www.djangoproject.com/ +.. _Jinja2 webpage: http://jinja.pocoo.org/ +.. _documentation: http://jinja.pocoo.org/2/documentation/ +""" +from setuptools import setup + + +setup( + name='Jinja2', + version='2.9.dev', + url='http://jinja.pocoo.org/', + license='BSD', + author='Armin Ronacher', + author_email='armin.ronacher@active-4.com', + description='A small but fast and easy to use stand-alone template ' + 'engine written in pure python.', + long_description=__doc__, + # jinja is egg safe. But we hate eggs + zip_safe=False, + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Environment :: Web Environment', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', + 'Topic :: Software Development :: Libraries :: Python Modules', + 'Topic :: Text Processing :: Markup :: HTML' + ], + packages=['jinja2'], + install_requires=['MarkupSafe'], + extras_require={'i18n': ['Babel>=0.8']}, + include_package_data=True, + entry_points=""" + [babel.extractors] + jinja2 = jinja2.ext:babel_extract[i18n] + """ +) diff --git a/deps/v8_inspector/deps/jinja2/tests/conftest.py b/deps/v8_inspector/deps/jinja2/tests/conftest.py new file mode 100644 index 00000000000000..b5582323d7ee46 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tests/conftest.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +""" + jinja2.testsuite.conftest + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + Configuration and Fixtures for the tests + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import pytest +import os +import re +import sys + +from traceback import format_exception +from jinja2 import loaders +from jinja2._compat import PY2 +from jinja2 import Environment + + +@pytest.fixture +def env(): + '''returns a new environment. + ''' + return Environment() + + +@pytest.fixture +def dict_loader(): + '''returns DictLoader + ''' + return loaders.DictLoader({ + 'justdict.html': 'FOO' + }) + + +@pytest.fixture +def package_loader(): + '''returns PackageLoader initialized from templates + ''' + return loaders.PackageLoader('res', 'templates') + + +@pytest.fixture +def filesystem_loader(): + '''returns FileSystemLoader initialized to res/templates directory + ''' + here = os.path.dirname(os.path.abspath(__file__)) + return loaders.FileSystemLoader(here + '/res/templates') + + +@pytest.fixture +def function_loader(): + '''returns a FunctionLoader + ''' + return loaders.FunctionLoader({'justfunction.html': 'FOO'}.get) + + +@pytest.fixture +def choice_loader(dict_loader, package_loader): + '''returns a ChoiceLoader + ''' + return loaders.ChoiceLoader([dict_loader, package_loader]) + + +@pytest.fixture +def prefix_loader(filesystem_loader, dict_loader): + '''returns a PrefixLoader + ''' + return loaders.PrefixLoader({ + 'a': filesystem_loader, + 'b': dict_loader + }) diff --git a/deps/v8_inspector/deps/jinja2/tests/res/__init__.py b/deps/v8_inspector/deps/jinja2/tests/res/__init__.py new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/deps/v8_inspector/deps/jinja2/tests/res/templates/broken.html b/deps/v8_inspector/deps/jinja2/tests/res/templates/broken.html new file mode 100644 index 00000000000000..77669fae57cdc2 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tests/res/templates/broken.html @@ -0,0 +1,3 @@ +Before +{{ fail() }} +After diff --git a/deps/v8_inspector/deps/jinja2/tests/res/templates/foo/test.html b/deps/v8_inspector/deps/jinja2/tests/res/templates/foo/test.html new file mode 100644 index 00000000000000..b7d6715e2df11b --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tests/res/templates/foo/test.html @@ -0,0 +1 @@ +FOO diff --git a/deps/v8_inspector/deps/jinja2/tests/res/templates/syntaxerror.html b/deps/v8_inspector/deps/jinja2/tests/res/templates/syntaxerror.html new file mode 100644 index 00000000000000..f21b817939831b --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tests/res/templates/syntaxerror.html @@ -0,0 +1,4 @@ +Foo +{% for item in broken %} + ... +{% endif %} diff --git a/deps/v8_inspector/deps/jinja2/tests/res/templates/test.html b/deps/v8_inspector/deps/jinja2/tests/res/templates/test.html new file mode 100644 index 00000000000000..ba578e48b18366 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tests/res/templates/test.html @@ -0,0 +1 @@ +BAR diff --git a/deps/v8_inspector/deps/jinja2/tests/test_api.py b/deps/v8_inspector/deps/jinja2/tests/test_api.py new file mode 100644 index 00000000000000..99d8dc1e13ea99 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tests/test_api.py @@ -0,0 +1,327 @@ +# -*- coding: utf-8 -*- +""" + jinja2.testsuite.api + ~~~~~~~~~~~~~~~~~~~~ + + Tests the public API and related stuff. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import os +import tempfile +import shutil + +import pytest +from jinja2 import Environment, Undefined, DebugUndefined, \ + StrictUndefined, UndefinedError, meta, \ + is_undefined, Template, DictLoader, make_logging_undefined +from jinja2.compiler import CodeGenerator +from jinja2.runtime import Context +from jinja2.utils import Cycler + + +@pytest.mark.api +@pytest.mark.extended +class TestExtendedAPI(): + + def test_item_and_attribute(self, env): + from jinja2.sandbox import SandboxedEnvironment + + for env in Environment(), SandboxedEnvironment(): + # the |list is necessary for python3 + tmpl = env.from_string('{{ foo.items()|list }}') + assert tmpl.render(foo={'items': 42}) == "[('items', 42)]" + tmpl = env.from_string('{{ foo|attr("items")()|list }}') + assert tmpl.render(foo={'items': 42}) == "[('items', 42)]" + tmpl = env.from_string('{{ foo["items"] }}') + assert tmpl.render(foo={'items': 42}) == '42' + + def test_finalizer(self, env): + def finalize_none_empty(value): + if value is None: + value = u'' + return value + env = Environment(finalize=finalize_none_empty) + tmpl = env.from_string('{% for item in seq %}|{{ item }}{% endfor %}') + assert tmpl.render(seq=(None, 1, "foo")) == '||1|foo' + tmpl = env.from_string('<{{ none }}>') + assert tmpl.render() == '<>' + + def test_cycler(self, env): + items = 1, 2, 3 + c = Cycler(*items) + for item in items + items: + assert c.current == item + assert next(c) == item + next(c) + assert c.current == 2 + c.reset() + assert c.current == 1 + + def test_expressions(self, env): + expr = env.compile_expression("foo") + assert expr() is None + assert expr(foo=42) == 42 + expr2 = env.compile_expression("foo", undefined_to_none=False) + assert is_undefined(expr2()) + + expr = env.compile_expression("42 + foo") + assert expr(foo=42) == 84 + + def test_template_passthrough(self, env): + t = Template('Content') + assert env.get_template(t) is t + assert env.select_template([t]) is t + assert env.get_or_select_template([t]) is t + assert env.get_or_select_template(t) is t + + def test_autoescape_autoselect(self, env): + def select_autoescape(name): + if name is None or '.' not in name: + return False + return name.endswith('.html') + env = Environment(autoescape=select_autoescape, + loader=DictLoader({ + 'test.txt': '{{ foo }}', + 'test.html': '{{ foo }}' + })) + t = env.get_template('test.txt') + assert t.render(foo='<foo>') == '<foo>' + t = env.get_template('test.html') + assert t.render(foo='<foo>') == '<foo>' + t = env.from_string('{{ foo }}') + assert t.render(foo='<foo>') == '<foo>' + + +@pytest.mark.api +@pytest.mark.meta +class TestMeta(): + + def test_find_undeclared_variables(self, env): + ast = env.parse('{% set foo = 42 %}{{ bar + foo }}') + x = meta.find_undeclared_variables(ast) + assert x == set(['bar']) + + ast = env.parse('{% set foo = 42 %}{{ bar + foo }}' + '{% macro meh(x) %}{{ x }}{% endmacro %}' + '{% for item in seq %}{{ muh(item) + meh(seq) }}' + '{% endfor %}') + x = meta.find_undeclared_variables(ast) + assert x == set(['bar', 'seq', 'muh']) + + def test_find_refererenced_templates(self, env): + ast = env.parse('{% extends "layout.html" %}{% include helper %}') + i = meta.find_referenced_templates(ast) + assert next(i) == 'layout.html' + assert next(i) is None + assert list(i) == [] + + ast = env.parse('{% extends "layout.html" %}' + '{% from "test.html" import a, b as c %}' + '{% import "meh.html" as meh %}' + '{% include "muh.html" %}') + i = meta.find_referenced_templates(ast) + assert list(i) == ['layout.html', 'test.html', 'meh.html', 'muh.html'] + + def test_find_included_templates(self, env): + ast = env.parse('{% include ["foo.html", "bar.html"] %}') + i = meta.find_referenced_templates(ast) + assert list(i) == ['foo.html', 'bar.html'] + + ast = env.parse('{% include ("foo.html", "bar.html") %}') + i = meta.find_referenced_templates(ast) + assert list(i) == ['foo.html', 'bar.html'] + + ast = env.parse('{% include ["foo.html", "bar.html", foo] %}') + i = meta.find_referenced_templates(ast) + assert list(i) == ['foo.html', 'bar.html', None] + + ast = env.parse('{% include ("foo.html", "bar.html", foo) %}') + i = meta.find_referenced_templates(ast) + assert list(i) == ['foo.html', 'bar.html', None] + + +@pytest.mark.api +@pytest.mark.streaming +class TestStreaming(): + + def test_basic_streaming(self, env): + tmpl = env.from_string("<ul>{% for item in seq %}<li>{{ loop.index " + "}} - {{ item }}</li>{%- endfor %}</ul>") + stream = tmpl.stream(seq=list(range(4))) + assert next(stream) == '<ul>' + assert next(stream) == '<li>1 - 0</li>' + assert next(stream) == '<li>2 - 1</li>' + assert next(stream) == '<li>3 - 2</li>' + assert next(stream) == '<li>4 - 3</li>' + assert next(stream) == '</ul>' + + def test_buffered_streaming(self, env): + tmpl = env.from_string("<ul>{% for item in seq %}<li>{{ loop.index " + "}} - {{ item }}</li>{%- endfor %}</ul>") + stream = tmpl.stream(seq=list(range(4))) + stream.enable_buffering(size=3) + assert next(stream) == u'<ul><li>1 - 0</li><li>2 - 1</li>' + assert next(stream) == u'<li>3 - 2</li><li>4 - 3</li></ul>' + + def test_streaming_behavior(self, env): + tmpl = env.from_string("") + stream = tmpl.stream() + assert not stream.buffered + stream.enable_buffering(20) + assert stream.buffered + stream.disable_buffering() + assert not stream.buffered + + def test_dump_stream(self, env): + tmp = tempfile.mkdtemp() + try: + tmpl = env.from_string(u"\u2713") + stream = tmpl.stream() + stream.dump(os.path.join(tmp, 'dump.txt'), 'utf-8') + with open(os.path.join(tmp, 'dump.txt'), 'rb') as f: + assert f.read() == b'\xe2\x9c\x93' + finally: + shutil.rmtree(tmp) + + +@pytest.mark.api +@pytest.mark.undefined +class TestUndefined(): + + def test_stopiteration_is_undefined(self): + def test(): + raise StopIteration() + t = Template('A{{ test() }}B') + assert t.render(test=test) == 'AB' + t = Template('A{{ test().missingattribute }}B') + pytest.raises(UndefinedError, t.render, test=test) + + def test_undefined_and_special_attributes(self): + try: + Undefined('Foo').__dict__ + except AttributeError: + pass + else: + assert False, "Expected actual attribute error" + + def test_logging_undefined(self): + _messages = [] + + class DebugLogger(object): + def warning(self, msg, *args): + _messages.append('W:' + msg % args) + + def error(self, msg, *args): + _messages.append('E:' + msg % args) + + logging_undefined = make_logging_undefined(DebugLogger()) + env = Environment(undefined=logging_undefined) + assert env.from_string('{{ missing }}').render() == u'' + pytest.raises(UndefinedError, + env.from_string('{{ missing.attribute }}').render) + assert env.from_string('{{ missing|list }}').render() == '[]' + assert env.from_string('{{ missing is not defined }}').render() \ + == 'True' + assert env.from_string('{{ foo.missing }}').render(foo=42) == '' + assert env.from_string('{{ not missing }}').render() == 'True' + assert _messages == [ + 'W:Template variable warning: missing is undefined', + "E:Template variable error: 'missing' is undefined", + 'W:Template variable warning: missing is undefined', + 'W:Template variable warning: int object has no attribute missing', + 'W:Template variable warning: missing is undefined', + ] + + def test_default_undefined(self): + env = Environment(undefined=Undefined) + assert env.from_string('{{ missing }}').render() == u'' + pytest.raises(UndefinedError, + env.from_string('{{ missing.attribute }}').render) + assert env.from_string('{{ missing|list }}').render() == '[]' + assert env.from_string('{{ missing is not defined }}').render() \ + == 'True' + assert env.from_string('{{ foo.missing }}').render(foo=42) == '' + assert env.from_string('{{ not missing }}').render() == 'True' + + def test_debug_undefined(self): + env = Environment(undefined=DebugUndefined) + assert env.from_string('{{ missing }}').render() == '{{ missing }}' + pytest.raises(UndefinedError, + env.from_string('{{ missing.attribute }}').render) + assert env.from_string('{{ missing|list }}').render() == '[]' + assert env.from_string('{{ missing is not defined }}').render() \ + == 'True' + assert env.from_string('{{ foo.missing }}').render(foo=42) \ + == u"{{ no such element: int object['missing'] }}" + assert env.from_string('{{ not missing }}').render() == 'True' + + def test_strict_undefined(self): + env = Environment(undefined=StrictUndefined) + pytest.raises(UndefinedError, env.from_string('{{ missing }}').render) + pytest.raises(UndefinedError, + env.from_string('{{ missing.attribute }}').render) + pytest.raises(UndefinedError, + env.from_string('{{ missing|list }}').render) + assert env.from_string('{{ missing is not defined }}').render() \ + == 'True' + pytest.raises(UndefinedError, + env.from_string('{{ foo.missing }}').render, foo=42) + pytest.raises(UndefinedError, + env.from_string('{{ not missing }}').render) + assert env.from_string('{{ missing|default("default", true) }}')\ + .render() == 'default' + + def test_indexing_gives_undefined(self): + t = Template("{{ var[42].foo }}") + pytest.raises(UndefinedError, t.render, var=0) + + def test_none_gives_proper_error(self): + try: + Environment().getattr(None, 'split')() + except UndefinedError as e: + assert e.message == "'None' has no attribute 'split'" + else: + assert False, 'expected exception' + + def test_object_repr(self): + try: + Undefined(obj=42, name='upper')() + except UndefinedError as e: + assert e.message == "'int object' has no attribute 'upper'" + else: + assert False, 'expected exception' + + +@pytest.mark.api +@pytest.mark.lowlevel +class TestLowLevel(): + + def test_custom_code_generator(self): + class CustomCodeGenerator(CodeGenerator): + def visit_Const(self, node, frame=None): + # This method is pure nonsense, but works fine for testing... + if node.value == 'foo': + self.write(repr('bar')) + else: + super(CustomCodeGenerator, self).visit_Const(node, frame) + + class CustomEnvironment(Environment): + code_generator_class = CustomCodeGenerator + + env = CustomEnvironment() + tmpl = env.from_string('{% set foo = "foo" %}{{ foo }}') + assert tmpl.render() == 'bar' + + def test_custom_context(self): + class CustomContext(Context): + def resolve(self, key): + return 'resolve-' + key + + class CustomEnvironment(Environment): + context_class = CustomContext + + env = CustomEnvironment() + tmpl = env.from_string('{{ foo }}') + assert tmpl.render() == 'resolve-foo' diff --git a/deps/v8_inspector/deps/jinja2/tests/test_bytecode_cache.py b/deps/v8_inspector/deps/jinja2/tests/test_bytecode_cache.py new file mode 100644 index 00000000000000..2ffe4c60379bc4 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tests/test_bytecode_cache.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +""" + jinja2.testsuite.bytecode_cache + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Test bytecode caching + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import pytest +from jinja2 import Environment +from jinja2.bccache import FileSystemBytecodeCache +from jinja2.exceptions import TemplateNotFound + + +@pytest.fixture +def env(package_loader): + bytecode_cache = FileSystemBytecodeCache() + return Environment( + loader=package_loader, + bytecode_cache=bytecode_cache, + ) + + +@pytest.mark.byte_code_cache +class TestByteCodeCache(): + + def test_simple(self, env): + tmpl = env.get_template('test.html') + assert tmpl.render().strip() == 'BAR' + pytest.raises(TemplateNotFound, env.get_template, 'missing.html') diff --git a/deps/v8_inspector/deps/jinja2/tests/test_core_tags.py b/deps/v8_inspector/deps/jinja2/tests/test_core_tags.py new file mode 100644 index 00000000000000..2ea7757e4862aa --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tests/test_core_tags.py @@ -0,0 +1,337 @@ +# -*- coding: utf-8 -*- +""" + jinja2.testsuite.core_tags + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Test the core tags like for and if. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import pytest +from jinja2 import Environment, TemplateSyntaxError, UndefinedError, \ + DictLoader + + +@pytest.fixture +def env_trim(): + return Environment(trim_blocks=True) + + +@pytest.mark.core_tags +@pytest.mark.for_loop +class TestForLoop(): + + def test_simple(self, env): + tmpl = env.from_string('{% for item in seq %}{{ item }}{% endfor %}') + assert tmpl.render(seq=list(range(10))) == '0123456789' + + def test_else(self, env): + tmpl = env.from_string( + '{% for item in seq %}XXX{% else %}...{% endfor %}') + assert tmpl.render() == '...' + + def test_empty_blocks(self, env): + tmpl = env.from_string('<{% for item in seq %}{% else %}{% endfor %}>') + assert tmpl.render() == '<>' + + def test_context_vars(self, env): + slist = [42, 24] + for seq in [slist, iter(slist), reversed(slist), (_ for _ in slist)]: + tmpl = env.from_string('''{% for item in seq -%} + {{ loop.index }}|{{ loop.index0 }}|{{ loop.revindex }}|{{ + loop.revindex0 }}|{{ loop.first }}|{{ loop.last }}|{{ + loop.length }}###{% endfor %}''') + one, two, _ = tmpl.render(seq=seq).split('###') + (one_index, one_index0, one_revindex, one_revindex0, one_first, + one_last, one_length) = one.split('|') + (two_index, two_index0, two_revindex, two_revindex0, two_first, + two_last, two_length) = two.split('|') + + assert int(one_index) == 1 and int(two_index) == 2 + assert int(one_index0) == 0 and int(two_index0) == 1 + assert int(one_revindex) == 2 and int(two_revindex) == 1 + assert int(one_revindex0) == 1 and int(two_revindex0) == 0 + assert one_first == 'True' and two_first == 'False' + assert one_last == 'False' and two_last == 'True' + assert one_length == two_length == '2' + + def test_cycling(self, env): + tmpl = env.from_string('''{% for item in seq %}{{ + loop.cycle('<1>', '<2>') }}{% endfor %}{% + for item in seq %}{{ loop.cycle(*through) }}{% endfor %}''') + output = tmpl.render(seq=list(range(4)), through=('<1>', '<2>')) + assert output == '<1><2>' * 4 + + def test_scope(self, env): + tmpl = env.from_string('{% for item in seq %}{% endfor %}{{ item }}') + output = tmpl.render(seq=list(range(10))) + assert not output + + def test_varlen(self, env): + def inner(): + for item in range(5): + yield item + tmpl = env.from_string('{% for item in iter %}{{ item }}{% endfor %}') + output = tmpl.render(iter=inner()) + assert output == '01234' + + def test_noniter(self, env): + tmpl = env.from_string('{% for item in none %}...{% endfor %}') + pytest.raises(TypeError, tmpl.render) + + def test_recursive(self, env): + tmpl = env.from_string('''{% for item in seq recursive -%} + [{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}] + {%- endfor %}''') + assert tmpl.render(seq=[ + dict(a=1, b=[dict(a=1), dict(a=2)]), + dict(a=2, b=[dict(a=1), dict(a=2)]), + dict(a=3, b=[dict(a='a')]) + ]) == '[1<[1][2]>][2<[1][2]>][3<[a]>]' + + def test_recursive_depth0(self, env): + tmpl = env.from_string('''{% for item in seq recursive -%} + [{{ loop.depth0 }}:{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}] + {%- endfor %}''') + assert tmpl.render(seq=[ + dict(a=1, b=[dict(a=1), dict(a=2)]), + dict(a=2, b=[dict(a=1), dict(a=2)]), + dict(a=3, b=[dict(a='a')]) + ]) == '[0:1<[1:1][1:2]>][0:2<[1:1][1:2]>][0:3<[1:a]>]' + + def test_recursive_depth(self, env): + tmpl = env.from_string('''{% for item in seq recursive -%} + [{{ loop.depth }}:{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}] + {%- endfor %}''') + assert tmpl.render(seq=[ + dict(a=1, b=[dict(a=1), dict(a=2)]), + dict(a=2, b=[dict(a=1), dict(a=2)]), + dict(a=3, b=[dict(a='a')]) + ]) == '[1:1<[2:1][2:2]>][1:2<[2:1][2:2]>][1:3<[2:a]>]' + + def test_looploop(self, env): + tmpl = env.from_string('''{% for row in table %} + {%- set rowloop = loop -%} + {% for cell in row -%} + [{{ rowloop.index }}|{{ loop.index }}] + {%- endfor %} + {%- endfor %}''') + assert tmpl.render(table=['ab', 'cd']) == '[1|1][1|2][2|1][2|2]' + + def test_reversed_bug(self, env): + tmpl = env.from_string('{% for i in items %}{{ i }}' + '{% if not loop.last %}' + ',{% endif %}{% endfor %}') + assert tmpl.render(items=reversed([3, 2, 1])) == '1,2,3' + + def test_loop_errors(self, env): + tmpl = env.from_string('''{% for item in [1] if loop.index + == 0 %}...{% endfor %}''') + pytest.raises(UndefinedError, tmpl.render) + tmpl = env.from_string('''{% for item in [] %}...{% else + %}{{ loop }}{% endfor %}''') + assert tmpl.render() == '' + + def test_loop_filter(self, env): + tmpl = env.from_string('{% for item in range(10) if item ' + 'is even %}[{{ item }}]{% endfor %}') + assert tmpl.render() == '[0][2][4][6][8]' + tmpl = env.from_string(''' + {%- for item in range(10) if item is even %}[{{ + loop.index }}:{{ item }}]{% endfor %}''') + assert tmpl.render() == '[1:0][2:2][3:4][4:6][5:8]' + + def test_loop_unassignable(self, env): + pytest.raises(TemplateSyntaxError, env.from_string, + '{% for loop in seq %}...{% endfor %}') + + def test_scoped_special_var(self, env): + t = env.from_string( + '{% for s in seq %}[{{ loop.first }}{% for c in s %}' + '|{{ loop.first }}{% endfor %}]{% endfor %}') + assert t.render(seq=('ab', 'cd')) \ + == '[True|True|False][False|True|False]' + + def test_scoped_loop_var(self, env): + t = env.from_string('{% for x in seq %}{{ loop.first }}' + '{% for y in seq %}{% endfor %}{% endfor %}') + assert t.render(seq='ab') == 'TrueFalse' + t = env.from_string('{% for x in seq %}{% for y in seq %}' + '{{ loop.first }}{% endfor %}{% endfor %}') + assert t.render(seq='ab') == 'TrueFalseTrueFalse' + + def test_recursive_empty_loop_iter(self, env): + t = env.from_string(''' + {%- for item in foo recursive -%}{%- endfor -%} + ''') + assert t.render(dict(foo=[])) == '' + + def test_call_in_loop(self, env): + t = env.from_string(''' + {%- macro do_something() -%} + [{{ caller() }}] + {%- endmacro %} + + {%- for i in [1, 2, 3] %} + {%- call do_something() -%} + {{ i }} + {%- endcall %} + {%- endfor -%} + ''') + assert t.render() == '[1][2][3]' + + def test_scoping_bug(self, env): + t = env.from_string(''' + {%- for item in foo %}...{{ item }}...{% endfor %} + {%- macro item(a) %}...{{ a }}...{% endmacro %} + {{- item(2) -}} + ''') + assert t.render(foo=(1,)) == '...1......2...' + + def test_unpacking(self, env): + tmpl = env.from_string('{% for a, b, c in [[1, 2, 3]] %}' + '{{ a }}|{{ b }}|{{ c }}{% endfor %}') + assert tmpl.render() == '1|2|3' + + +@pytest.mark.core_tags +@pytest.mark.if_condition +class TestIfCondition(): + + def test_simple(self, env): + tmpl = env.from_string('''{% if true %}...{% endif %}''') + assert tmpl.render() == '...' + + def test_elif(self, env): + tmpl = env.from_string('''{% if false %}XXX{% elif true + %}...{% else %}XXX{% endif %}''') + assert tmpl.render() == '...' + + def test_else(self, env): + tmpl = env.from_string('{% if false %}XXX{% else %}...{% endif %}') + assert tmpl.render() == '...' + + def test_empty(self, env): + tmpl = env.from_string('[{% if true %}{% else %}{% endif %}]') + assert tmpl.render() == '[]' + + def test_complete(self, env): + tmpl = env.from_string('{% if a %}A{% elif b %}B{% elif c == d %}' + 'C{% else %}D{% endif %}') + assert tmpl.render(a=0, b=False, c=42, d=42.0) == 'C' + + def test_no_scope(self, env): + tmpl = env.from_string( + '{% if a %}{% set foo = 1 %}{% endif %}{{ foo }}') + assert tmpl.render(a=True) == '1' + tmpl = env.from_string( + '{% if true %}{% set foo = 1 %}{% endif %}{{ foo }}') + assert tmpl.render() == '1' + + +@pytest.mark.core_tags +@pytest.mark.macros +class TestMacros(): + def test_simple(self, env_trim): + tmpl = env_trim.from_string('''\ +{% macro say_hello(name) %}Hello {{ name }}!{% endmacro %} +{{ say_hello('Peter') }}''') + assert tmpl.render() == 'Hello Peter!' + + def test_scoping(self, env_trim): + tmpl = env_trim.from_string('''\ +{% macro level1(data1) %} +{% macro level2(data2) %}{{ data1 }}|{{ data2 }}{% endmacro %} +{{ level2('bar') }}{% endmacro %} +{{ level1('foo') }}''') + assert tmpl.render() == 'foo|bar' + + def test_arguments(self, env_trim): + tmpl = env_trim.from_string('''\ +{% macro m(a, b, c='c', d='d') %}{{ a }}|{{ b }}|{{ c }}|{{ d }}{% endmacro %} +{{ m() }}|{{ m('a') }}|{{ m('a', 'b') }}|{{ m(1, 2, 3) }}''') + assert tmpl.render() == '||c|d|a||c|d|a|b|c|d|1|2|3|d' + + def test_arguments_defaults_nonsense(self, env_trim): + pytest.raises(TemplateSyntaxError, env_trim.from_string, '''\ +{% macro m(a, b=1, c) %}a={{ a }}, b={{ b }}, c={{ c }}{% endmacro %}''') + + def test_caller_defaults_nonsense(self, env_trim): + pytest.raises(TemplateSyntaxError, env_trim.from_string, '''\ +{% macro a() %}{{ caller() }}{% endmacro %} +{% call(x, y=1, z) a() %}{% endcall %}''') + + def test_varargs(self, env_trim): + tmpl = env_trim.from_string('''\ +{% macro test() %}{{ varargs|join('|') }}{% endmacro %}\ +{{ test(1, 2, 3) }}''') + assert tmpl.render() == '1|2|3' + + def test_simple_call(self, env_trim): + tmpl = env_trim.from_string('''\ +{% macro test() %}[[{{ caller() }}]]{% endmacro %}\ +{% call test() %}data{% endcall %}''') + assert tmpl.render() == '[[data]]' + + def test_complex_call(self, env_trim): + tmpl = env_trim.from_string('''\ +{% macro test() %}[[{{ caller('data') }}]]{% endmacro %}\ +{% call(data) test() %}{{ data }}{% endcall %}''') + assert tmpl.render() == '[[data]]' + + def test_caller_undefined(self, env_trim): + tmpl = env_trim.from_string('''\ +{% set caller = 42 %}\ +{% macro test() %}{{ caller is not defined }}{% endmacro %}\ +{{ test() }}''') + assert tmpl.render() == 'True' + + def test_include(self, env_trim): + env_trim = Environment( + loader=DictLoader({ + 'include': '{% macro test(foo) %}[{{ foo }}]{% endmacro %}' + }) + ) + tmpl = env_trim.from_string( + '{% from "include" import test %}{{ test("foo") }}') + assert tmpl.render() == '[foo]' + + def test_macro_api(self, env_trim): + tmpl = env_trim.from_string( + '{% macro foo(a, b) %}{% endmacro %}' + '{% macro bar() %}{{ varargs }}{{ kwargs }}{% endmacro %}' + '{% macro baz() %}{{ caller() }}{% endmacro %}') + assert tmpl.module.foo.arguments == ('a', 'b') + assert tmpl.module.foo.defaults == () + assert tmpl.module.foo.name == 'foo' + assert not tmpl.module.foo.caller + assert not tmpl.module.foo.catch_kwargs + assert not tmpl.module.foo.catch_varargs + assert tmpl.module.bar.arguments == () + assert tmpl.module.bar.defaults == () + assert not tmpl.module.bar.caller + assert tmpl.module.bar.catch_kwargs + assert tmpl.module.bar.catch_varargs + assert tmpl.module.baz.caller + + def test_callself(self, env_trim): + tmpl = env_trim.from_string('{% macro foo(x) %}{{ x }}{% if x > 1 %}|' + '{{ foo(x - 1) }}{% endif %}{% endmacro %}' + '{{ foo(5) }}') + assert tmpl.render() == '5|4|3|2|1' + + +@pytest.mark.core_tags +@pytest.mark.set +class TestSet(): + + def test_normal(self, env_trim): + tmpl = env_trim.from_string('{% set foo = 1 %}{{ foo }}') + assert tmpl.render() == '1' + assert tmpl.module.foo == 1 + + def test_block(self, env_trim): + tmpl = env_trim.from_string('{% set foo %}42{% endset %}{{ foo }}') + assert tmpl.render() == '42' + assert tmpl.module.foo == u'42' diff --git a/deps/v8_inspector/deps/jinja2/tests/test_debug.py b/deps/v8_inspector/deps/jinja2/tests/test_debug.py new file mode 100644 index 00000000000000..d8617ae08e1176 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tests/test_debug.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +""" + jinja2.testsuite.debug + ~~~~~~~~~~~~~~~~~~~~~~ + + Tests the debug system. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import pytest + +import re + +import sys +from traceback import format_exception + +from jinja2 import Environment, TemplateSyntaxError +from traceback import format_exception + + +@pytest.fixture +def fs_env(filesystem_loader): + '''returns a new environment. + ''' + return Environment(loader=filesystem_loader) + + +@pytest.mark.debug +class TestDebug(): + + def assert_traceback_matches(self, callback, expected_tb): + try: + callback() + except Exception as e: + tb = format_exception(*sys.exc_info()) + if re.search(expected_tb.strip(), ''.join(tb)) is None: + assert False, ('Traceback did not match:\n\n%s\nexpected:\n%s' % + (''.join(tb), expected_tb)) + else: + assert False, 'Expected exception' + + def test_runtime_error(self, fs_env): + def test(): + tmpl.render(fail=lambda: 1 / 0) + tmpl = fs_env.get_template('broken.html') + self.assert_traceback_matches(test, r''' + File ".*?broken.html", line 2, in (top-level template code|<module>) + \{\{ fail\(\) \}\} + File ".*debug?.pyc?", line \d+, in <lambda> + tmpl\.render\(fail=lambda: 1 / 0\) +ZeroDivisionError: (int(eger)? )?division (or modulo )?by zero +''') + + def test_syntax_error(self, fs_env): + # XXX: the .*? is necessary for python3 which does not hide + # some of the stack frames we don't want to show. Not sure + # what's up with that, but that is not that critical. Should + # be fixed though. + self.assert_traceback_matches(lambda: fs_env.get_template('syntaxerror.html'), r'''(?sm) + File ".*?syntaxerror.html", line 4, in (template|<module>) + \{% endif %\}.*? +(jinja2\.exceptions\.)?TemplateSyntaxError: Encountered unknown tag 'endif'. Jinja was looking for the following tags: 'endfor' or 'else'. The innermost block that needs to be closed is 'for'. + ''') + + def test_regular_syntax_error(self, fs_env): + def test(): + raise TemplateSyntaxError('wtf', 42) + self.assert_traceback_matches(test, r''' + File ".*debug.pyc?", line \d+, in test + raise TemplateSyntaxError\('wtf', 42\) +(jinja2\.exceptions\.)?TemplateSyntaxError: wtf + line 42''') diff --git a/deps/v8_inspector/deps/jinja2/tests/test_ext.py b/deps/v8_inspector/deps/jinja2/tests/test_ext.py new file mode 100644 index 00000000000000..8985416c9cb356 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tests/test_ext.py @@ -0,0 +1,467 @@ +# -*- coding: utf-8 -*- +""" + jinja2.testsuite.ext + ~~~~~~~~~~~~~~~~~~~~ + + Tests for the extensions. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import re +import pytest + +from jinja2 import Environment, DictLoader, contextfunction, nodes +from jinja2.exceptions import TemplateAssertionError +from jinja2.ext import Extension +from jinja2.lexer import Token, count_newlines +from jinja2._compat import BytesIO, itervalues, text_type + +importable_object = 23 + +_gettext_re = re.compile(r'_\((.*?)\)(?s)') + + +i18n_templates = { + 'master.html': '<title>{{ page_title|default(_("missing")) }}</title>' + '{% block body %}{% endblock %}', + 'child.html': '{% extends "master.html" %}{% block body %}' + '{% trans %}watch out{% endtrans %}{% endblock %}', + 'plural.html': '{% trans user_count %}One user online{% pluralize %}' + '{{ user_count }} users online{% endtrans %}', + 'plural2.html': '{% trans user_count=get_user_count() %}{{ user_count }}s' + '{% pluralize %}{{ user_count }}p{% endtrans %}', + 'stringformat.html': '{{ _("User: %(num)s")|format(num=user_count) }}' +} + +newstyle_i18n_templates = { + 'master.html': '<title>{{ page_title|default(_("missing")) }}</title>' + '{% block body %}{% endblock %}', + 'child.html': '{% extends "master.html" %}{% block body %}' + '{% trans %}watch out{% endtrans %}{% endblock %}', + 'plural.html': '{% trans user_count %}One user online{% pluralize %}' + '{{ user_count }} users online{% endtrans %}', + 'stringformat.html': '{{ _("User: %(num)s", num=user_count) }}', + 'ngettext.html': '{{ ngettext("%(num)s apple", "%(num)s apples", apples) }}', + 'ngettext_long.html': '{% trans num=apples %}{{ num }} apple{% pluralize %}' + '{{ num }} apples{% endtrans %}', + 'transvars1.html': '{% trans %}User: {{ num }}{% endtrans %}', + 'transvars2.html': '{% trans num=count %}User: {{ num }}{% endtrans %}', + 'transvars3.html': '{% trans count=num %}User: {{ count }}{% endtrans %}', + 'novars.html': '{% trans %}%(hello)s{% endtrans %}', + 'vars.html': '{% trans %}{{ foo }}%(foo)s{% endtrans %}', + 'explicitvars.html': '{% trans foo="42" %}%(foo)s{% endtrans %}' +} + + +languages = { + 'de': { + 'missing': u'fehlend', + 'watch out': u'pass auf', + 'One user online': u'Ein Benutzer online', + '%(user_count)s users online': u'%(user_count)s Benutzer online', + 'User: %(num)s': u'Benutzer: %(num)s', + 'User: %(count)s': u'Benutzer: %(count)s', + '%(num)s apple': u'%(num)s Apfel', + '%(num)s apples': u'%(num)s Äpfel' + } +} + + +@contextfunction +def gettext(context, string): + language = context.get('LANGUAGE', 'en') + return languages.get(language, {}).get(string, string) + + +@contextfunction +def ngettext(context, s, p, n): + language = context.get('LANGUAGE', 'en') + if n != 1: + return languages.get(language, {}).get(p, p) + return languages.get(language, {}).get(s, s) + + +i18n_env = Environment( + loader=DictLoader(i18n_templates), + extensions=['jinja2.ext.i18n'] +) +i18n_env.globals.update({ + '_': gettext, + 'gettext': gettext, + 'ngettext': ngettext +}) + +newstyle_i18n_env = Environment( + loader=DictLoader(newstyle_i18n_templates), + extensions=['jinja2.ext.i18n'] +) +newstyle_i18n_env.install_gettext_callables(gettext, ngettext, newstyle=True) + + +class TestExtension(Extension): + tags = set(['test']) + ext_attr = 42 + + def parse(self, parser): + return nodes.Output([self.call_method('_dump', [ + nodes.EnvironmentAttribute('sandboxed'), + self.attr('ext_attr'), + nodes.ImportedName(__name__ + '.importable_object'), + nodes.ContextReference() + ])]).set_lineno(next(parser.stream).lineno) + + def _dump(self, sandboxed, ext_attr, imported_object, context): + return '%s|%s|%s|%s' % ( + sandboxed, + ext_attr, + imported_object, + context.blocks + ) + + +class PreprocessorExtension(Extension): + + def preprocess(self, source, name, filename=None): + return source.replace('[[TEST]]', '({{ foo }})') + + +class StreamFilterExtension(Extension): + + def filter_stream(self, stream): + for token in stream: + if token.type == 'data': + for t in self.interpolate(token): + yield t + else: + yield token + + def interpolate(self, token): + pos = 0 + end = len(token.value) + lineno = token.lineno + while 1: + match = _gettext_re.search(token.value, pos) + if match is None: + break + value = token.value[pos:match.start()] + if value: + yield Token(lineno, 'data', value) + lineno += count_newlines(token.value) + yield Token(lineno, 'variable_begin', None) + yield Token(lineno, 'name', 'gettext') + yield Token(lineno, 'lparen', None) + yield Token(lineno, 'string', match.group(1)) + yield Token(lineno, 'rparen', None) + yield Token(lineno, 'variable_end', None) + pos = match.end() + if pos < end: + yield Token(lineno, 'data', token.value[pos:]) + + +@pytest.mark.ext +class TestExtensions(): + + def test_extend_late(self): + env = Environment() + env.add_extension('jinja2.ext.autoescape') + t = env.from_string( + '{% autoescape true %}{{ "<test>" }}{% endautoescape %}') + assert t.render() == '<test>' + + def test_loop_controls(self): + env = Environment(extensions=['jinja2.ext.loopcontrols']) + + tmpl = env.from_string(''' + {%- for item in [1, 2, 3, 4] %} + {%- if item % 2 == 0 %}{% continue %}{% endif -%} + {{ item }} + {%- endfor %}''') + assert tmpl.render() == '13' + + tmpl = env.from_string(''' + {%- for item in [1, 2, 3, 4] %} + {%- if item > 2 %}{% break %}{% endif -%} + {{ item }} + {%- endfor %}''') + assert tmpl.render() == '12' + + def test_do(self): + env = Environment(extensions=['jinja2.ext.do']) + tmpl = env.from_string(''' + {%- set items = [] %} + {%- for char in "foo" %} + {%- do items.append(loop.index0 ~ char) %} + {%- endfor %}{{ items|join(', ') }}''') + assert tmpl.render() == '0f, 1o, 2o' + + def test_with(self): + env = Environment(extensions=['jinja2.ext.with_']) + tmpl = env.from_string('''\ + {% with a=42, b=23 -%} + {{ a }} = {{ b }} + {% endwith -%} + {{ a }} = {{ b }}\ + ''') + assert [x.strip() for x in tmpl.render(a=1, b=2).splitlines()] \ + == ['42 = 23', '1 = 2'] + + def test_extension_nodes(self): + env = Environment(extensions=[TestExtension]) + tmpl = env.from_string('{% test %}') + assert tmpl.render() == 'False|42|23|{}' + + def test_identifier(self): + assert TestExtension.identifier == __name__ + '.TestExtension' + + def test_rebinding(self): + original = Environment(extensions=[TestExtension]) + overlay = original.overlay() + for env in original, overlay: + for ext in itervalues(env.extensions): + assert ext.environment is env + + def test_preprocessor_extension(self): + env = Environment(extensions=[PreprocessorExtension]) + tmpl = env.from_string('{[[TEST]]}') + assert tmpl.render(foo=42) == '{(42)}' + + def test_streamfilter_extension(self): + env = Environment(extensions=[StreamFilterExtension]) + env.globals['gettext'] = lambda x: x.upper() + tmpl = env.from_string('Foo _(bar) Baz') + out = tmpl.render() + assert out == 'Foo BAR Baz' + + def test_extension_ordering(self): + class T1(Extension): + priority = 1 + + class T2(Extension): + priority = 2 + env = Environment(extensions=[T1, T2]) + ext = list(env.iter_extensions()) + assert ext[0].__class__ is T1 + assert ext[1].__class__ is T2 + + +@pytest.mark.ext +class TestInternationalization(): + + def test_trans(self): + tmpl = i18n_env.get_template('child.html') + assert tmpl.render(LANGUAGE='de') == '<title>fehlend</title>pass auf' + + def test_trans_plural(self): + tmpl = i18n_env.get_template('plural.html') + assert tmpl.render(LANGUAGE='de', user_count=1) \ + == 'Ein Benutzer online' + assert tmpl.render(LANGUAGE='de', user_count=2) == '2 Benutzer online' + + def test_trans_plural_with_functions(self): + tmpl = i18n_env.get_template('plural2.html') + + def get_user_count(): + get_user_count.called += 1 + return 1 + get_user_count.called = 0 + assert tmpl.render(LANGUAGE='de', get_user_count=get_user_count) \ + == '1s' + assert get_user_count.called == 1 + + def test_complex_plural(self): + tmpl = i18n_env.from_string( + '{% trans foo=42, count=2 %}{{ count }} item{% ' + 'pluralize count %}{{ count }} items{% endtrans %}') + assert tmpl.render() == '2 items' + pytest.raises(TemplateAssertionError, i18n_env.from_string, + '{% trans foo %}...{% pluralize bar %}...{% endtrans %}') + + def test_trans_stringformatting(self): + tmpl = i18n_env.get_template('stringformat.html') + assert tmpl.render(LANGUAGE='de', user_count=5) == 'Benutzer: 5' + + def test_extract(self): + from jinja2.ext import babel_extract + source = BytesIO(''' + {{ gettext('Hello World') }} + {% trans %}Hello World{% endtrans %} + {% trans %}{{ users }} user{% pluralize %}{{ users }} users{% endtrans %} + '''.encode('ascii')) # make python 3 happy + assert list(babel_extract(source, + ('gettext', 'ngettext', '_'), [], {})) == [ + (2, 'gettext', u'Hello World', []), + (3, 'gettext', u'Hello World', []), + (4, 'ngettext', (u'%(users)s user', u'%(users)s users', None), []) + ] + + def test_comment_extract(self): + from jinja2.ext import babel_extract + source = BytesIO(''' + {# trans first #} + {{ gettext('Hello World') }} + {% trans %}Hello World{% endtrans %}{# trans second #} + {#: third #} + {% trans %}{{ users }} user{% pluralize %}{{ users }} users{% endtrans %} + '''.encode('utf-8')) # make python 3 happy + assert list(babel_extract(source, + ('gettext', 'ngettext', '_'), + ['trans', ':'], {})) == [ + (3, 'gettext', u'Hello World', ['first']), + (4, 'gettext', u'Hello World', ['second']), + (6, 'ngettext', (u'%(users)s user', u'%(users)s users', None), + ['third']) + ] + + +@pytest.mark.ext +class TestNewstyleInternationalization(): + + def test_trans(self): + tmpl = newstyle_i18n_env.get_template('child.html') + assert tmpl.render(LANGUAGE='de') == '<title>fehlend</title>pass auf' + + def test_trans_plural(self): + tmpl = newstyle_i18n_env.get_template('plural.html') + assert tmpl.render(LANGUAGE='de', user_count=1) \ + == 'Ein Benutzer online' + assert tmpl.render(LANGUAGE='de', user_count=2) == '2 Benutzer online' + + def test_complex_plural(self): + tmpl = newstyle_i18n_env.from_string( + '{% trans foo=42, count=2 %}{{ count }} item{% ' + 'pluralize count %}{{ count }} items{% endtrans %}') + assert tmpl.render() == '2 items' + pytest.raises(TemplateAssertionError, i18n_env.from_string, + '{% trans foo %}...{% pluralize bar %}...{% endtrans %}') + + def test_trans_stringformatting(self): + tmpl = newstyle_i18n_env.get_template('stringformat.html') + assert tmpl.render(LANGUAGE='de', user_count=5) == 'Benutzer: 5' + + def test_newstyle_plural(self): + tmpl = newstyle_i18n_env.get_template('ngettext.html') + assert tmpl.render(LANGUAGE='de', apples=1) == '1 Apfel' + assert tmpl.render(LANGUAGE='de', apples=5) == u'5 Äpfel' + + def test_autoescape_support(self): + env = Environment(extensions=['jinja2.ext.autoescape', + 'jinja2.ext.i18n']) + env.install_gettext_callables( + lambda x: u'<strong>Wert: %(name)s</strong>', + lambda s, p, n: s, newstyle=True) + t = env.from_string('{% autoescape ae %}{{ gettext("foo", name=' + '"<test>") }}{% endautoescape %}') + assert t.render(ae=True) == '<strong>Wert: <test></strong>' + assert t.render(ae=False) == '<strong>Wert: <test></strong>' + + def test_num_used_twice(self): + tmpl = newstyle_i18n_env.get_template('ngettext_long.html') + assert tmpl.render(apples=5, LANGUAGE='de') == u'5 Äpfel' + + def test_num_called_num(self): + source = newstyle_i18n_env.compile(''' + {% trans num=3 %}{{ num }} apple{% pluralize + %}{{ num }} apples{% endtrans %} + ''', raw=True) + # quite hacky, but the only way to properly test that. The idea is + # that the generated code does not pass num twice (although that + # would work) for better performance. This only works on the + # newstyle gettext of course + assert re.search(r"l_ngettext, u?'\%\(num\)s apple', u?'\%\(num\)s " + r"apples', 3", source) is not None + + def test_trans_vars(self): + t1 = newstyle_i18n_env.get_template('transvars1.html') + t2 = newstyle_i18n_env.get_template('transvars2.html') + t3 = newstyle_i18n_env.get_template('transvars3.html') + assert t1.render(num=1, LANGUAGE='de') == 'Benutzer: 1' + assert t2.render(count=23, LANGUAGE='de') == 'Benutzer: 23' + assert t3.render(num=42, LANGUAGE='de') == 'Benutzer: 42' + + def test_novars_vars_escaping(self): + t = newstyle_i18n_env.get_template('novars.html') + assert t.render() == '%(hello)s' + t = newstyle_i18n_env.get_template('vars.html') + assert t.render(foo='42') == '42%(foo)s' + t = newstyle_i18n_env.get_template('explicitvars.html') + assert t.render() == '%(foo)s' + + +@pytest.mark.ext +class TestAutoEscape(): + + def test_scoped_setting(self): + env = Environment(extensions=['jinja2.ext.autoescape'], + autoescape=True) + tmpl = env.from_string(''' + {{ "<HelloWorld>" }} + {% autoescape false %} + {{ "<HelloWorld>" }} + {% endautoescape %} + {{ "<HelloWorld>" }} + ''') + assert tmpl.render().split() == \ + [u'<HelloWorld>', u'<HelloWorld>', u'<HelloWorld>'] + + env = Environment(extensions=['jinja2.ext.autoescape'], + autoescape=False) + tmpl = env.from_string(''' + {{ "<HelloWorld>" }} + {% autoescape true %} + {{ "<HelloWorld>" }} + {% endautoescape %} + {{ "<HelloWorld>" }} + ''') + assert tmpl.render().split() == \ + [u'<HelloWorld>', u'<HelloWorld>', u'<HelloWorld>'] + + def test_nonvolatile(self): + env = Environment(extensions=['jinja2.ext.autoescape'], + autoescape=True) + tmpl = env.from_string('{{ {"foo": "<test>"}|xmlattr|escape }}') + assert tmpl.render() == ' foo="<test>"' + tmpl = env.from_string('{% autoescape false %}{{ {"foo": "<test>"}' + '|xmlattr|escape }}{% endautoescape %}') + assert tmpl.render() == ' foo="&lt;test&gt;"' + + def test_volatile(self): + env = Environment(extensions=['jinja2.ext.autoescape'], + autoescape=True) + tmpl = env.from_string('{% autoescape foo %}{{ {"foo": "<test>"}' + '|xmlattr|escape }}{% endautoescape %}') + assert tmpl.render(foo=False) == ' foo="&lt;test&gt;"' + assert tmpl.render(foo=True) == ' foo="<test>"' + + def test_scoping(self): + env = Environment(extensions=['jinja2.ext.autoescape']) + tmpl = env.from_string( + '{% autoescape true %}{% set x = "<x>" %}{{ x }}' + '{% endautoescape %}{{ x }}{{ "<y>" }}') + assert tmpl.render(x=1) == '<x>1<y>' + + def test_volatile_scoping(self): + env = Environment(extensions=['jinja2.ext.autoescape']) + tmplsource = ''' + {% autoescape val %} + {% macro foo(x) %} + [{{ x }}] + {% endmacro %} + {{ foo().__class__.__name__ }} + {% endautoescape %} + {{ '<testing>' }} + ''' + tmpl = env.from_string(tmplsource) + assert tmpl.render(val=True).split()[0] == 'Markup' + assert tmpl.render(val=False).split()[0] == text_type.__name__ + + # looking at the source we should see <testing> there in raw + # (and then escaped as well) + env = Environment(extensions=['jinja2.ext.autoescape']) + pysource = env.compile(tmplsource, raw=True) + assert '<testing>\\n' in pysource + + env = Environment(extensions=['jinja2.ext.autoescape'], + autoescape=True) + pysource = env.compile(tmplsource, raw=True) + assert '<testing>\\n' in pysource diff --git a/deps/v8_inspector/deps/jinja2/tests/test_filters.py b/deps/v8_inspector/deps/jinja2/tests/test_filters.py new file mode 100644 index 00000000000000..741ef341b1b0f7 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tests/test_filters.py @@ -0,0 +1,558 @@ +# -*- coding: utf-8 -*- +""" + jinja2.testsuite.filters + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Tests for the jinja filters. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import pytest +from jinja2 import Markup, Environment +from jinja2._compat import text_type, implements_to_string + + +@pytest.mark.filter +class TestFilter(): + + def test_filter_calling(self, env): + rv = env.call_filter('sum', [1, 2, 3]) + assert rv == 6 + + def test_capitalize(self, env): + tmpl = env.from_string('{{ "foo bar"|capitalize }}') + assert tmpl.render() == 'Foo bar' + + def test_center(self, env): + tmpl = env.from_string('{{ "foo"|center(9) }}') + assert tmpl.render() == ' foo ' + + def test_default(self, env): + tmpl = env.from_string( + "{{ missing|default('no') }}|{{ false|default('no') }}|" + "{{ false|default('no', true) }}|{{ given|default('no') }}" + ) + assert tmpl.render(given='yes') == 'no|False|no|yes' + + def test_dictsort(self, env): + tmpl = env.from_string( + '{{ foo|dictsort }}|' + '{{ foo|dictsort(true) }}|' + '{{ foo|dictsort(false, "value") }}' + ) + out = tmpl.render(foo={"aa": 0, "b": 1, "c": 2, "AB": 3}) + assert out == ("[('aa', 0), ('AB', 3), ('b', 1), ('c', 2)]|" + "[('AB', 3), ('aa', 0), ('b', 1), ('c', 2)]|" + "[('aa', 0), ('b', 1), ('c', 2), ('AB', 3)]") + + def test_batch(self, env): + tmpl = env.from_string("{{ foo|batch(3)|list }}|" + "{{ foo|batch(3, 'X')|list }}") + out = tmpl.render(foo=list(range(10))) + assert out == ("[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]|" + "[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 'X', 'X']]") + + def test_slice(self, env): + tmpl = env.from_string('{{ foo|slice(3)|list }}|' + '{{ foo|slice(3, "X")|list }}') + out = tmpl.render(foo=list(range(10))) + assert out == ("[[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]]|" + "[[0, 1, 2, 3], [4, 5, 6, 'X'], [7, 8, 9, 'X']]") + + def test_escape(self, env): + tmpl = env.from_string('''{{ '<">&'|escape }}''') + out = tmpl.render() + assert out == '<">&' + + def test_striptags(self, env): + tmpl = env.from_string('''{{ foo|striptags }}''') + out = tmpl.render(foo=' <p>just a small \n <a href="#">' + 'example</a> link</p>\n<p>to a webpage</p> ' + '<!-- <p>and some commented stuff</p> -->') + assert out == 'just a small example link to a webpage' + + def test_filesizeformat(self, env): + tmpl = env.from_string( + '{{ 100|filesizeformat }}|' + '{{ 1000|filesizeformat }}|' + '{{ 1000000|filesizeformat }}|' + '{{ 1000000000|filesizeformat }}|' + '{{ 1000000000000|filesizeformat }}|' + '{{ 100|filesizeformat(true) }}|' + '{{ 1000|filesizeformat(true) }}|' + '{{ 1000000|filesizeformat(true) }}|' + '{{ 1000000000|filesizeformat(true) }}|' + '{{ 1000000000000|filesizeformat(true) }}' + ) + out = tmpl.render() + assert out == ( + '100 Bytes|1.0 kB|1.0 MB|1.0 GB|1.0 TB|100 Bytes|' + '1000 Bytes|976.6 KiB|953.7 MiB|931.3 GiB' + ) + + def test_filesizeformat_issue59(self, env): + tmpl = env.from_string( + '{{ 300|filesizeformat }}|' + '{{ 3000|filesizeformat }}|' + '{{ 3000000|filesizeformat }}|' + '{{ 3000000000|filesizeformat }}|' + '{{ 3000000000000|filesizeformat }}|' + '{{ 300|filesizeformat(true) }}|' + '{{ 3000|filesizeformat(true) }}|' + '{{ 3000000|filesizeformat(true) }}' + ) + out = tmpl.render() + assert out == ( + '300 Bytes|3.0 kB|3.0 MB|3.0 GB|3.0 TB|300 Bytes|' + '2.9 KiB|2.9 MiB' + ) + + def test_first(self, env): + tmpl = env.from_string('{{ foo|first }}') + out = tmpl.render(foo=list(range(10))) + assert out == '0' + + def test_float(self, env): + tmpl = env.from_string('{{ "42"|float }}|' + '{{ "ajsghasjgd"|float }}|' + '{{ "32.32"|float }}') + out = tmpl.render() + assert out == '42.0|0.0|32.32' + + def test_format(self, env): + tmpl = env.from_string('''{{ "%s|%s"|format("a", "b") }}''') + out = tmpl.render() + assert out == 'a|b' + + def test_indent(self, env): + tmpl = env.from_string('{{ foo|indent(2) }}|{{ foo|indent(2, true) }}') + text = '\n'.join([' '.join(['foo', 'bar'] * 2)] * 2) + out = tmpl.render(foo=text) + assert out == ('foo bar foo bar\n foo bar foo bar| ' + 'foo bar foo bar\n foo bar foo bar') + + def test_int(self, env): + tmpl = env.from_string('{{ "42"|int }}|{{ "ajsghasjgd"|int }}|' + '{{ "32.32"|int }}|{{ "0x4d32"|int(0, 16) }}|' + '{{ "011"|int(0, 8)}}|{{ "0x33FU"|int(0, 16) }}') + out = tmpl.render() + assert out == '42|0|32|19762|9|0' + + def test_join(self, env): + tmpl = env.from_string('{{ [1, 2, 3]|join("|") }}') + out = tmpl.render() + assert out == '1|2|3' + + env2 = Environment(autoescape=True) + tmpl = env2.from_string( + '{{ ["<foo>", "<span>foo</span>"|safe]|join }}') + assert tmpl.render() == '<foo><span>foo</span>' + + def test_join_attribute(self, env): + class User(object): + def __init__(self, username): + self.username = username + tmpl = env.from_string('''{{ users|join(', ', 'username') }}''') + assert tmpl.render(users=map(User, ['foo', 'bar'])) == 'foo, bar' + + def test_last(self, env): + tmpl = env.from_string('''{{ foo|last }}''') + out = tmpl.render(foo=list(range(10))) + assert out == '9' + + def test_length(self, env): + tmpl = env.from_string('''{{ "hello world"|length }}''') + out = tmpl.render() + assert out == '11' + + def test_lower(self, env): + tmpl = env.from_string('''{{ "FOO"|lower }}''') + out = tmpl.render() + assert out == 'foo' + + def test_pprint(self, env): + from pprint import pformat + tmpl = env.from_string('''{{ data|pprint }}''') + data = list(range(1000)) + assert tmpl.render(data=data) == pformat(data) + + def test_random(self, env): + tmpl = env.from_string('''{{ seq|random }}''') + seq = list(range(100)) + for _ in range(10): + assert int(tmpl.render(seq=seq)) in seq + + def test_reverse(self, env): + tmpl = env.from_string('{{ "foobar"|reverse|join }}|' + '{{ [1, 2, 3]|reverse|list }}') + assert tmpl.render() == 'raboof|[3, 2, 1]' + + def test_string(self, env): + x = [1, 2, 3, 4, 5] + tmpl = env.from_string('''{{ obj|string }}''') + assert tmpl.render(obj=x) == text_type(x) + + def test_title(self, env): + tmpl = env.from_string('''{{ "foo bar"|title }}''') + assert tmpl.render() == "Foo Bar" + tmpl = env.from_string('''{{ "foo's bar"|title }}''') + assert tmpl.render() == "Foo's Bar" + tmpl = env.from_string('''{{ "foo bar"|title }}''') + assert tmpl.render() == "Foo Bar" + tmpl = env.from_string('''{{ "f bar f"|title }}''') + assert tmpl.render() == "F Bar F" + tmpl = env.from_string('''{{ "foo-bar"|title }}''') + assert tmpl.render() == "Foo-Bar" + tmpl = env.from_string('''{{ "foo\tbar"|title }}''') + assert tmpl.render() == "Foo\tBar" + tmpl = env.from_string('''{{ "FOO\tBAR"|title }}''') + assert tmpl.render() == "Foo\tBar" + + class Foo: + def __str__(self): + return 'foo-bar' + + tmpl = env.from_string('''{{ data|title }}''') + out = tmpl.render(data=Foo()) + assert out == 'Foo-Bar' + + def test_truncate(self, env): + tmpl = env.from_string( + '{{ data|truncate(15, true, ">>>") }}|' + '{{ data|truncate(15, false, ">>>") }}|' + '{{ smalldata|truncate(15) }}' + ) + out = tmpl.render(data='foobar baz bar' * 1000, + smalldata='foobar baz bar') + msg = 'Current output: %s' % out + assert out == 'foobar baz b>>>|foobar baz >>>|foobar baz bar', msg + + def test_truncate_very_short(self, env): + tmpl = env.from_string( + '{{ "foo bar baz"|truncate(9) }}|' + '{{ "foo bar baz"|truncate(9, true) }}' + ) + out = tmpl.render() + assert out == 'foo ...|foo ba...', out + + def test_truncate_end_length(self, env): + tmpl = env.from_string('{{ "Joel is a slug"|truncate(9, true) }}') + out = tmpl.render() + assert out == 'Joel i...', 'Current output: %s' % out + + def test_upper(self, env): + tmpl = env.from_string('{{ "foo"|upper }}') + assert tmpl.render() == 'FOO' + + def test_urlize(self, env): + tmpl = env.from_string( + '{{ "foo http://www.example.com/ bar"|urlize }}') + assert tmpl.render() == 'foo <a href="http://www.example.com/">'\ + 'http://www.example.com/</a> bar' + + def test_urlize_target_parameter(self, env): + tmpl = env.from_string( + '{{ "foo http://www.example.com/ bar"|urlize(target="_blank") }}' + ) + assert tmpl.render() \ + == 'foo <a href="http://www.example.com/" target="_blank">'\ + 'http://www.example.com/</a> bar' + tmpl = env.from_string( + '{{ "foo http://www.example.com/ bar"|urlize(target=42) }}' + ) + assert tmpl.render() == 'foo <a href="http://www.example.com/">'\ + 'http://www.example.com/</a> bar' + + def test_wordcount(self, env): + tmpl = env.from_string('{{ "foo bar baz"|wordcount }}') + assert tmpl.render() == '3' + + def test_block(self, env): + tmpl = env.from_string( + '{% filter lower|escape %}<HEHE>{% endfilter %}' + ) + assert tmpl.render() == '<hehe>' + + def test_chaining(self, env): + tmpl = env.from_string( + '''{{ ['<foo>', '<bar>']|first|upper|escape }}''' + ) + assert tmpl.render() == '<FOO>' + + def test_sum(self, env): + tmpl = env.from_string('''{{ [1, 2, 3, 4, 5, 6]|sum }}''') + assert tmpl.render() == '21' + + def test_sum_attributes(self, env): + tmpl = env.from_string('''{{ values|sum('value') }}''') + assert tmpl.render(values=[ + {'value': 23}, + {'value': 1}, + {'value': 18}, + ]) == '42' + + def test_sum_attributes_nested(self, env): + tmpl = env.from_string('''{{ values|sum('real.value') }}''') + assert tmpl.render(values=[ + {'real': {'value': 23}}, + {'real': {'value': 1}}, + {'real': {'value': 18}}, + ]) == '42' + + def test_sum_attributes_tuple(self, env): + tmpl = env.from_string('''{{ values.items()|sum('1') }}''') + assert tmpl.render(values={ + 'foo': 23, + 'bar': 1, + 'baz': 18, + }) == '42' + + def test_abs(self, env): + tmpl = env.from_string('''{{ -1|abs }}|{{ 1|abs }}''') + assert tmpl.render() == '1|1', tmpl.render() + + def test_round_positive(self, env): + tmpl = env.from_string('{{ 2.7|round }}|{{ 2.1|round }}|' + "{{ 2.1234|round(3, 'floor') }}|" + "{{ 2.1|round(0, 'ceil') }}") + assert tmpl.render() == '3.0|2.0|2.123|3.0', tmpl.render() + + def test_round_negative(self, env): + tmpl = env.from_string('{{ 21.3|round(-1)}}|' + "{{ 21.3|round(-1, 'ceil')}}|" + "{{ 21.3|round(-1, 'floor')}}") + assert tmpl.render() == '20.0|30.0|20.0', tmpl.render() + + def test_xmlattr(self, env): + tmpl = env.from_string( + "{{ {'foo': 42, 'bar': 23, 'fish': none, " + "'spam': missing, 'blub:blub': '<?>'}|xmlattr }}") + out = tmpl.render().split() + assert len(out) == 3 + assert 'foo="42"' in out + assert 'bar="23"' in out + assert 'blub:blub="<?>"' in out + + def test_sort1(self, env): + tmpl = env.from_string( + '{{ [2, 3, 1]|sort }}|{{ [2, 3, 1]|sort(true) }}') + assert tmpl.render() == '[1, 2, 3]|[3, 2, 1]' + + def test_sort2(self, env): + tmpl = env.from_string('{{ "".join(["c", "A", "b", "D"]|sort) }}') + assert tmpl.render() == 'AbcD' + + def test_sort3(self, env): + tmpl = env.from_string('''{{ ['foo', 'Bar', 'blah']|sort }}''') + assert tmpl.render() == "['Bar', 'blah', 'foo']" + + def test_sort4(self, env): + @implements_to_string + class Magic(object): + def __init__(self, value): + self.value = value + + def __str__(self): + return text_type(self.value) + tmpl = env.from_string('''{{ items|sort(attribute='value')|join }}''') + assert tmpl.render(items=map(Magic, [3, 2, 4, 1])) == '1234' + + def test_groupby(self, env): + tmpl = env.from_string(''' + {%- for grouper, list in [{'foo': 1, 'bar': 2}, + {'foo': 2, 'bar': 3}, + {'foo': 1, 'bar': 1}, + {'foo': 3, 'bar': 4}]|groupby('foo') -%} + {{ grouper }}{% for x in list %}: {{ x.foo }}, {{ x.bar }}{% endfor %}| + {%- endfor %}''') + assert tmpl.render().split('|') == [ + "1: 1, 2: 1, 1", + "2: 2, 3", + "3: 3, 4", + "" + ] + + def test_groupby_tuple_index(self, env): + tmpl = env.from_string(''' + {%- for grouper, list in [('a', 1), ('a', 2), ('b', 1)]|groupby(0) -%} + {{ grouper }}{% for x in list %}:{{ x.1 }}{% endfor %}| + {%- endfor %}''') + assert tmpl.render() == 'a:1:2|b:1|' + + def test_groupby_multidot(self, env): + class Date(object): + def __init__(self, day, month, year): + self.day = day + self.month = month + self.year = year + + class Article(object): + def __init__(self, title, *date): + self.date = Date(*date) + self.title = title + articles = [ + Article('aha', 1, 1, 1970), + Article('interesting', 2, 1, 1970), + Article('really?', 3, 1, 1970), + Article('totally not', 1, 1, 1971) + ] + tmpl = env.from_string(''' + {%- for year, list in articles|groupby('date.year') -%} + {{ year }}{% for x in list %}[{{ x.title }}]{% endfor %}| + {%- endfor %}''') + assert tmpl.render(articles=articles).split('|') == [ + '1970[aha][interesting][really?]', + '1971[totally not]', + '' + ] + + def test_filtertag(self, env): + tmpl = env.from_string("{% filter upper|replace('FOO', 'foo') %}" + "foobar{% endfilter %}") + assert tmpl.render() == 'fooBAR' + + def test_replace(self, env): + env = Environment() + tmpl = env.from_string('{{ string|replace("o", 42) }}') + assert tmpl.render(string='<foo>') == '<f4242>' + env = Environment(autoescape=True) + tmpl = env.from_string('{{ string|replace("o", 42) }}') + assert tmpl.render(string='<foo>') == '<f4242>' + tmpl = env.from_string('{{ string|replace("<", 42) }}') + assert tmpl.render(string='<foo>') == '42foo>' + tmpl = env.from_string('{{ string|replace("o", ">x<") }}') + assert tmpl.render(string=Markup('foo')) == 'f>x<>x<' + + def test_forceescape(self, env): + tmpl = env.from_string('{{ x|forceescape }}') + assert tmpl.render(x=Markup('<div />')) == u'<div />' + + def test_safe(self, env): + env = Environment(autoescape=True) + tmpl = env.from_string('{{ "<div>foo</div>"|safe }}') + assert tmpl.render() == '<div>foo</div>' + tmpl = env.from_string('{{ "<div>foo</div>" }}') + assert tmpl.render() == '<div>foo</div>' + + def test_urlencode(self, env): + env = Environment(autoescape=True) + tmpl = env.from_string('{{ "Hello, world!"|urlencode }}') + assert tmpl.render() == 'Hello%2C%20world%21' + tmpl = env.from_string('{{ o|urlencode }}') + assert tmpl.render(o=u"Hello, world\u203d") \ + == "Hello%2C%20world%E2%80%BD" + assert tmpl.render(o=(("f", 1),)) == "f=1" + assert tmpl.render(o=(('f', 1), ("z", 2))) == "f=1&z=2" + assert tmpl.render(o=((u"\u203d", 1),)) == "%E2%80%BD=1" + assert tmpl.render(o={u"\u203d": 1}) == "%E2%80%BD=1" + assert tmpl.render(o={0: 1}) == "0=1" + + def test_simple_map(self, env): + env = Environment() + tmpl = env.from_string('{{ ["1", "2", "3"]|map("int")|sum }}') + assert tmpl.render() == '6' + + def test_attribute_map(self, env): + class User(object): + def __init__(self, name): + self.name = name + env = Environment() + users = [ + User('john'), + User('jane'), + User('mike'), + ] + tmpl = env.from_string('{{ users|map(attribute="name")|join("|") }}') + assert tmpl.render(users=users) == 'john|jane|mike' + + def test_empty_map(self, env): + env = Environment() + tmpl = env.from_string('{{ none|map("upper")|list }}') + assert tmpl.render() == '[]' + + def test_simple_select(self, env): + env = Environment() + tmpl = env.from_string('{{ [1, 2, 3, 4, 5]|select("odd")|join("|") }}') + assert tmpl.render() == '1|3|5' + + def test_bool_select(self, env): + env = Environment() + tmpl = env.from_string( + '{{ [none, false, 0, 1, 2, 3, 4, 5]|select|join("|") }}' + ) + assert tmpl.render() == '1|2|3|4|5' + + def test_simple_reject(self, env): + env = Environment() + tmpl = env.from_string('{{ [1, 2, 3, 4, 5]|reject("odd")|join("|") }}') + assert tmpl.render() == '2|4' + + def test_bool_reject(self, env): + env = Environment() + tmpl = env.from_string( + '{{ [none, false, 0, 1, 2, 3, 4, 5]|reject|join("|") }}' + ) + assert tmpl.render() == 'None|False|0' + + def test_simple_select_attr(self, env): + class User(object): + def __init__(self, name, is_active): + self.name = name + self.is_active = is_active + env = Environment() + users = [ + User('john', True), + User('jane', True), + User('mike', False), + ] + tmpl = env.from_string( + '{{ users|selectattr("is_active")|' + 'map(attribute="name")|join("|") }}' + ) + assert tmpl.render(users=users) == 'john|jane' + + def test_simple_reject_attr(self, env): + class User(object): + def __init__(self, name, is_active): + self.name = name + self.is_active = is_active + env = Environment() + users = [ + User('john', True), + User('jane', True), + User('mike', False), + ] + tmpl = env.from_string('{{ users|rejectattr("is_active")|' + 'map(attribute="name")|join("|") }}') + assert tmpl.render(users=users) == 'mike' + + def test_func_select_attr(self, env): + class User(object): + def __init__(self, id, name): + self.id = id + self.name = name + env = Environment() + users = [ + User(1, 'john'), + User(2, 'jane'), + User(3, 'mike'), + ] + tmpl = env.from_string('{{ users|selectattr("id", "odd")|' + 'map(attribute="name")|join("|") }}') + assert tmpl.render(users=users) == 'john|mike' + + def test_func_reject_attr(self, env): + class User(object): + def __init__(self, id, name): + self.id = id + self.name = name + env = Environment() + users = [ + User(1, 'john'), + User(2, 'jane'), + User(3, 'mike'), + ] + tmpl = env.from_string('{{ users|rejectattr("id", "odd")|' + 'map(attribute="name")|join("|") }}') + assert tmpl.render(users=users) == 'jane' diff --git a/deps/v8_inspector/deps/jinja2/tests/test_imports.py b/deps/v8_inspector/deps/jinja2/tests/test_imports.py new file mode 100644 index 00000000000000..643c995c6e6ade --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tests/test_imports.py @@ -0,0 +1,148 @@ +# -*- coding: utf-8 -*- +""" + jinja2.testsuite.imports + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Tests the import features (with includes). + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import pytest + +from jinja2 import Environment, DictLoader +from jinja2.exceptions import TemplateNotFound, TemplatesNotFound + + +@pytest.fixture +def test_env(): + env = Environment(loader=DictLoader(dict( + module='{% macro test() %}[{{ foo }}|{{ bar }}]{% endmacro %}', + header='[{{ foo }}|{{ 23 }}]', + o_printer='({{ o }})' + ))) + env.globals['bar'] = 23 + return env + + +@pytest.mark.imports +class TestImports(): + + def test_context_imports(self, test_env): + t = test_env.from_string('{% import "module" as m %}{{ m.test() }}') + assert t.render(foo=42) == '[|23]' + t = test_env.from_string( + '{% import "module" as m without context %}{{ m.test() }}' + ) + assert t.render(foo=42) == '[|23]' + t = test_env.from_string( + '{% import "module" as m with context %}{{ m.test() }}' + ) + assert t.render(foo=42) == '[42|23]' + t = test_env.from_string('{% from "module" import test %}{{ test() }}') + assert t.render(foo=42) == '[|23]' + t = test_env.from_string( + '{% from "module" import test without context %}{{ test() }}' + ) + assert t.render(foo=42) == '[|23]' + t = test_env.from_string( + '{% from "module" import test with context %}{{ test() }}' + ) + assert t.render(foo=42) == '[42|23]' + + def test_trailing_comma(self, test_env): + test_env.from_string('{% from "foo" import bar, baz with context %}') + test_env.from_string('{% from "foo" import bar, baz, with context %}') + test_env.from_string('{% from "foo" import bar, with context %}') + test_env.from_string('{% from "foo" import bar, with, context %}') + test_env.from_string('{% from "foo" import bar, with with context %}') + + def test_exports(self, test_env): + m = test_env.from_string(''' + {% macro toplevel() %}...{% endmacro %} + {% macro __private() %}...{% endmacro %} + {% set variable = 42 %} + {% for item in [1] %} + {% macro notthere() %}{% endmacro %} + {% endfor %} + ''').module + assert m.toplevel() == '...' + assert not hasattr(m, '__missing') + assert m.variable == 42 + assert not hasattr(m, 'notthere') + + +@pytest.mark.imports +@pytest.mark.includes +class TestIncludes(): + + def test_context_include(self, test_env): + t = test_env.from_string('{% include "header" %}') + assert t.render(foo=42) == '[42|23]' + t = test_env.from_string('{% include "header" with context %}') + assert t.render(foo=42) == '[42|23]' + t = test_env.from_string('{% include "header" without context %}') + assert t.render(foo=42) == '[|23]' + + def test_choice_includes(self, test_env): + t = test_env.from_string('{% include ["missing", "header"] %}') + assert t.render(foo=42) == '[42|23]' + + t = test_env.from_string( + '{% include ["missing", "missing2"] ignore missing %}' + ) + assert t.render(foo=42) == '' + + t = test_env.from_string('{% include ["missing", "missing2"] %}') + pytest.raises(TemplateNotFound, t.render) + try: + t.render() + except TemplatesNotFound as e: + assert e.templates == ['missing', 'missing2'] + assert e.name == 'missing2' + else: + assert False, 'thou shalt raise' + + def test_includes(t, **ctx): + ctx['foo'] = 42 + assert t.render(ctx) == '[42|23]' + + t = test_env.from_string('{% include ["missing", "header"] %}') + test_includes(t) + t = test_env.from_string('{% include x %}') + test_includes(t, x=['missing', 'header']) + t = test_env.from_string('{% include [x, "header"] %}') + test_includes(t, x='missing') + t = test_env.from_string('{% include x %}') + test_includes(t, x='header') + t = test_env.from_string('{% include x %}') + test_includes(t, x='header') + t = test_env.from_string('{% include [x] %}') + test_includes(t, x='header') + + def test_include_ignoring_missing(self, test_env): + t = test_env.from_string('{% include "missing" %}') + pytest.raises(TemplateNotFound, t.render) + for extra in '', 'with context', 'without context': + t = test_env.from_string('{% include "missing" ignore missing ' + + extra + ' %}') + assert t.render() == '' + + def test_context_include_with_overrides(self, test_env): + env = Environment(loader=DictLoader(dict( + main="{% for item in [1, 2, 3] %}{% include 'item' %}{% endfor %}", + item="{{ item }}" + ))) + assert env.get_template("main").render() == "123" + + def test_unoptimized_scopes(self, test_env): + t = test_env.from_string(""" + {% macro outer(o) %} + {% macro inner() %} + {% include "o_printer" %} + {% endmacro %} + {{ inner() }} + {% endmacro %} + {{ outer("FOO") }} + """) + assert t.render().strip() == '(FOO)' diff --git a/deps/v8_inspector/deps/jinja2/tests/test_inheritance.py b/deps/v8_inspector/deps/jinja2/tests/test_inheritance.py new file mode 100644 index 00000000000000..1cb7390441c09e --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tests/test_inheritance.py @@ -0,0 +1,248 @@ +# -*- coding: utf-8 -*- +""" + jinja2.testsuite.inheritance + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Tests the template inheritance feature. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import pytest + +from jinja2 import Environment, DictLoader, TemplateError + + +LAYOUTTEMPLATE = '''\ +|{% block block1 %}block 1 from layout{% endblock %} +|{% block block2 %}block 2 from layout{% endblock %} +|{% block block3 %} +{% block block4 %}nested block 4 from layout{% endblock %} +{% endblock %}|''' + +LEVEL1TEMPLATE = '''\ +{% extends "layout" %} +{% block block1 %}block 1 from level1{% endblock %}''' + +LEVEL2TEMPLATE = '''\ +{% extends "level1" %} +{% block block2 %}{% block block5 %}nested block 5 from level2{% +endblock %}{% endblock %}''' + +LEVEL3TEMPLATE = '''\ +{% extends "level2" %} +{% block block5 %}block 5 from level3{% endblock %} +{% block block4 %}block 4 from level3{% endblock %} +''' + +LEVEL4TEMPLATE = '''\ +{% extends "level3" %} +{% block block3 %}block 3 from level4{% endblock %} +''' + +WORKINGTEMPLATE = '''\ +{% extends "layout" %} +{% block block1 %} + {% if false %} + {% block block2 %} + this should workd + {% endblock %} + {% endif %} +{% endblock %} +''' + +DOUBLEEXTENDS = '''\ +{% extends "layout" %} +{% extends "layout" %} +{% block block1 %} + {% if false %} + {% block block2 %} + this should workd + {% endblock %} + {% endif %} +{% endblock %} +''' + + +@pytest.fixture +def env(): + return Environment(loader=DictLoader({ + 'layout': LAYOUTTEMPLATE, + 'level1': LEVEL1TEMPLATE, + 'level2': LEVEL2TEMPLATE, + 'level3': LEVEL3TEMPLATE, + 'level4': LEVEL4TEMPLATE, + 'working': WORKINGTEMPLATE, + 'doublee': DOUBLEEXTENDS, + }), trim_blocks=True) + + +@pytest.mark.inheritance +class TestInheritance(): + + def test_layout(self, env): + tmpl = env.get_template('layout') + assert tmpl.render() == ('|block 1 from layout|block 2 from ' + 'layout|nested block 4 from layout|') + + def test_level1(self, env): + tmpl = env.get_template('level1') + assert tmpl.render() == ('|block 1 from level1|block 2 from ' + 'layout|nested block 4 from layout|') + + def test_level2(self, env): + tmpl = env.get_template('level2') + assert tmpl.render() == ('|block 1 from level1|nested block 5 from ' + 'level2|nested block 4 from layout|') + + def test_level3(self, env): + tmpl = env.get_template('level3') + assert tmpl.render() == ('|block 1 from level1|block 5 from level3|' + 'block 4 from level3|') + + def test_level4(self, env): + tmpl = env.get_template('level4') + assert tmpl.render() == ('|block 1 from level1|block 5 from ' + 'level3|block 3 from level4|') + + def test_super(self, env): + env = Environment(loader=DictLoader({ + 'a': '{% block intro %}INTRO{% endblock %}|' + 'BEFORE|{% block data %}INNER{% endblock %}|AFTER', + 'b': '{% extends "a" %}{% block data %}({{ ' + 'super() }}){% endblock %}', + 'c': '{% extends "b" %}{% block intro %}--{{ ' + 'super() }}--{% endblock %}\n{% block data ' + '%}[{{ super() }}]{% endblock %}' + })) + tmpl = env.get_template('c') + assert tmpl.render() == '--INTRO--|BEFORE|[(INNER)]|AFTER' + + def test_working(self, env): + tmpl = env.get_template('working') + + def test_reuse_blocks(self, env): + tmpl = env.from_string('{{ self.foo() }}|{% block foo %}42' + '{% endblock %}|{{ self.foo() }}') + assert tmpl.render() == '42|42|42' + + def test_preserve_blocks(self, env): + env = Environment(loader=DictLoader({ + 'a': '{% if false %}{% block x %}A{% endblock %}' + '{% endif %}{{ self.x() }}', + 'b': '{% extends "a" %}{% block x %}B{{ super() }}{% endblock %}' + })) + tmpl = env.get_template('b') + assert tmpl.render() == 'BA' + + def test_dynamic_inheritance(self, env): + env = Environment(loader=DictLoader({ + 'master1': 'MASTER1{% block x %}{% endblock %}', + 'master2': 'MASTER2{% block x %}{% endblock %}', + 'child': '{% extends master %}{% block x %}CHILD{% endblock %}' + })) + tmpl = env.get_template('child') + for m in range(1, 3): + assert tmpl.render(master='master%d' % m) == 'MASTER%dCHILD' % m + + def test_multi_inheritance(self, env): + env = Environment(loader=DictLoader({ + 'master1': 'MASTER1{% block x %}{% endblock %}', + 'master2': 'MASTER2{% block x %}{% endblock %}', + 'child': + '''{% if master %}{% extends master %}{% else %}{% extends + 'master1' %}{% endif %}{% block x %}CHILD{% endblock %}''' + })) + tmpl = env.get_template('child') + assert tmpl.render(master='master2') == 'MASTER2CHILD' + assert tmpl.render(master='master1') == 'MASTER1CHILD' + assert tmpl.render() == 'MASTER1CHILD' + + def test_scoped_block(self, env): + env = Environment(loader=DictLoader({ + 'master.html': '{% for item in seq %}[{% block item scoped %}' + '{% endblock %}]{% endfor %}' + })) + t = env.from_string('{% extends "master.html" %}{% block item %}' + '{{ item }}{% endblock %}') + assert t.render(seq=list(range(5))) == '[0][1][2][3][4]' + + def test_super_in_scoped_block(self, env): + env = Environment(loader=DictLoader({ + 'master.html': '{% for item in seq %}[{% block item scoped %}' + '{{ item }}{% endblock %}]{% endfor %}' + })) + t = env.from_string('{% extends "master.html" %}{% block item %}' + '{{ super() }}|{{ item * 2 }}{% endblock %}') + assert t.render(seq=list(range(5))) == '[0|0][1|2][2|4][3|6][4|8]' + + def test_scoped_block_after_inheritance(self, env): + env = Environment(loader=DictLoader({ + 'layout.html': ''' + {% block useless %}{% endblock %} + ''', + 'index.html': ''' + {%- extends 'layout.html' %} + {% from 'helpers.html' import foo with context %} + {% block useless %} + {% for x in [1, 2, 3] %} + {% block testing scoped %} + {{ foo(x) }} + {% endblock %} + {% endfor %} + {% endblock %} + ''', + 'helpers.html': ''' + {% macro foo(x) %}{{ the_foo + x }}{% endmacro %} + ''' + })) + rv = env.get_template('index.html').render(the_foo=42).split() + assert rv == ['43', '44', '45'] + + +@pytest.mark.inheritance +class TestBugFix(): + + def test_fixed_macro_scoping_bug(self, env): + assert Environment(loader=DictLoader({ + 'test.html': '''\ + {% extends 'details.html' %} + + {% macro my_macro() %} + my_macro + {% endmacro %} + + {% block inner_box %} + {{ my_macro() }} + {% endblock %} + ''', + 'details.html': '''\ + {% extends 'standard.html' %} + + {% macro my_macro() %} + my_macro + {% endmacro %} + + {% block content %} + {% block outer_box %} + outer_box + {% block inner_box %} + inner_box + {% endblock %} + {% endblock %} + {% endblock %} + ''', + 'standard.html': ''' + {% block content %} {% endblock %} + ''' + })).get_template("test.html").render().split() \ + == [u'outer_box', u'my_macro'] + + def test_double_extends(self, env): + """Ensures that a template with more than 1 {% extends ... %} usage + raises a ``TemplateError``. + """ + try: + tmpl = env.get_template('doublee') + except Exception as e: + assert isinstance(e, TemplateError) diff --git a/deps/v8_inspector/deps/jinja2/tests/test_lexnparse.py b/deps/v8_inspector/deps/jinja2/tests/test_lexnparse.py new file mode 100644 index 00000000000000..ff334bf0ca8b08 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tests/test_lexnparse.py @@ -0,0 +1,609 @@ +# -*- coding: utf-8 -*- +""" + jinja2.testsuite.lexnparse + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + All the unittests regarding lexing, parsing and syntax. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import pytest + +from jinja2 import Environment, Template, TemplateSyntaxError, \ + UndefinedError, nodes +from jinja2._compat import iteritems, text_type, PY2 +from jinja2.lexer import Token, TokenStream, TOKEN_EOF, \ + TOKEN_BLOCK_BEGIN, TOKEN_BLOCK_END + + +# how does a string look like in jinja syntax? +if PY2: + def jinja_string_repr(string): + return repr(string)[1:] +else: + jinja_string_repr = repr + + +@pytest.mark.lexnparse +@pytest.mark.tokenstream +class TestTokenStream(): + test_tokens = [Token(1, TOKEN_BLOCK_BEGIN, ''), + Token(2, TOKEN_BLOCK_END, ''), + ] + + def test_simple(self, env): + ts = TokenStream(self.test_tokens, "foo", "bar") + assert ts.current.type is TOKEN_BLOCK_BEGIN + assert bool(ts) + assert not bool(ts.eos) + next(ts) + assert ts.current.type is TOKEN_BLOCK_END + assert bool(ts) + assert not bool(ts.eos) + next(ts) + assert ts.current.type is TOKEN_EOF + assert not bool(ts) + assert bool(ts.eos) + + def test_iter(self, env): + token_types = [ + t.type for t in TokenStream(self.test_tokens, "foo", "bar") + ] + assert token_types == ['block_begin', 'block_end', ] + + +@pytest.mark.lexnparse +@pytest.mark.lexer +class TestLexer(): + + def test_raw1(self, env): + tmpl = env.from_string( + '{% raw %}foo{% endraw %}|' + '{%raw%}{{ bar }}|{% baz %}{% endraw %}') + assert tmpl.render() == 'foo|{{ bar }}|{% baz %}' + + def test_raw2(self, env): + tmpl = env.from_string('1 {%- raw -%} 2 {%- endraw -%} 3') + assert tmpl.render() == '123' + + def test_balancing(self, env): + env = Environment('{%', '%}', '${', '}') + tmpl = env.from_string('''{% for item in seq + %}${{'foo': item}|upper}{% endfor %}''') + assert tmpl.render(seq=list(range(3))) \ + == "{'FOO': 0}{'FOO': 1}{'FOO': 2}" + + def test_comments(self, env): + env = Environment('<!--', '-->', '{', '}') + tmpl = env.from_string('''\ +<ul> +<!--- for item in seq --> + <li>{item}</li> +<!--- endfor --> +</ul>''') + assert tmpl.render(seq=list(range(3))) \ + == ("<ul>\n <li>0</li>\n ""<li>1</li>\n <li>2</li>\n</ul>") + + def test_string_escapes(self, env): + for char in u'\0', u'\u2668', u'\xe4', u'\t', u'\r', u'\n': + tmpl = env.from_string('{{ %s }}' % jinja_string_repr(char)) + assert tmpl.render() == char + assert env.from_string('{{ "\N{HOT SPRINGS}" }}').render() == u'\u2668' + + def test_bytefallback(self, env): + from pprint import pformat + tmpl = env.from_string(u'''{{ 'foo'|pprint }}|{{ 'bär'|pprint }}''') + assert tmpl.render() == pformat('foo') + '|' + pformat(u'bär') + + def test_operators(self, env): + from jinja2.lexer import operators + for test, expect in iteritems(operators): + if test in '([{}])': + continue + stream = env.lexer.tokenize('{{ %s }}' % test) + next(stream) + assert stream.current.type == expect + + def test_normalizing(self, env): + for seq in '\r', '\r\n', '\n': + env = Environment(newline_sequence=seq) + tmpl = env.from_string('1\n2\r\n3\n4\n') + result = tmpl.render() + assert result.replace(seq, 'X') == '1X2X3X4' + + def test_trailing_newline(self, env): + for keep in [True, False]: + env = Environment(keep_trailing_newline=keep) + for template, expected in [ + ('', {}), + ('no\nnewline', {}), + ('with\nnewline\n', {False: 'with\nnewline'}), + ('with\nseveral\n\n\n', {False: 'with\nseveral\n\n'}), + ]: + tmpl = env.from_string(template) + expect = expected.get(keep, template) + result = tmpl.render() + assert result == expect, (keep, template, result, expect) + + +@pytest.mark.lexnparse +@pytest.mark.parser +class TestParser(): + + def test_php_syntax(self, env): + env = Environment('<?', '?>', '<?=', '?>', '<!--', '-->') + tmpl = env.from_string('''\ +<!-- I'm a comment, I'm not interesting -->\ +<? for item in seq -?> + <?= item ?> +<?- endfor ?>''') + assert tmpl.render(seq=list(range(5))) == '01234' + + def test_erb_syntax(self, env): + env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>') + tmpl = env.from_string('''\ +<%# I'm a comment, I'm not interesting %>\ +<% for item in seq -%> + <%= item %> +<%- endfor %>''') + assert tmpl.render(seq=list(range(5))) == '01234' + + def test_comment_syntax(self, env): + env = Environment('<!--', '-->', '${', '}', '<!--#', '-->') + tmpl = env.from_string('''\ +<!--# I'm a comment, I'm not interesting -->\ +<!-- for item in seq ---> + ${item} +<!--- endfor -->''') + assert tmpl.render(seq=list(range(5))) == '01234' + + def test_balancing(self, env): + tmpl = env.from_string('''{{{'foo':'bar'}.foo}}''') + assert tmpl.render() == 'bar' + + def test_start_comment(self, env): + tmpl = env.from_string('''{# foo comment +and bar comment #} +{% macro blub() %}foo{% endmacro %} +{{ blub() }}''') + assert tmpl.render().strip() == 'foo' + + def test_line_syntax(self, env): + env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%') + tmpl = env.from_string('''\ +<%# regular comment %> +% for item in seq: + ${item} +% endfor''') + assert [ + int(x.strip()) for x in tmpl.render(seq=list(range(5))).split() + ] == list(range(5)) + + env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##') + tmpl = env.from_string('''\ +<%# regular comment %> +% for item in seq: + ${item} ## the rest of the stuff +% endfor''') + assert [ + int(x.strip()) for x in tmpl.render(seq=list(range(5))).split() + ] == list(range(5)) + + def test_line_syntax_priority(self, env): + # XXX: why is the whitespace there in front of the newline? + env = Environment('{%', '%}', '${', '}', '/*', '*/', '##', '#') + tmpl = env.from_string('''\ +/* ignore me. + I'm a multiline comment */ +## for item in seq: +* ${item} # this is just extra stuff +## endfor''') + assert tmpl.render(seq=[1, 2]).strip() == '* 1\n* 2' + env = Environment('{%', '%}', '${', '}', '/*', '*/', '#', '##') + tmpl = env.from_string('''\ +/* ignore me. + I'm a multiline comment */ +# for item in seq: +* ${item} ## this is just extra stuff + ## extra stuff i just want to ignore +# endfor''') + assert tmpl.render(seq=[1, 2]).strip() == '* 1\n\n* 2' + + def test_error_messages(self, env): + def assert_error(code, expected): + try: + Template(code) + except TemplateSyntaxError as e: + assert str(e) == expected, 'unexpected error message' + else: + assert False, 'that was supposed to be an error' + + assert_error('{% for item in seq %}...{% endif %}', + "Encountered unknown tag 'endif'. Jinja was looking " + "for the following tags: 'endfor' or 'else'. The " + "innermost block that needs to be closed is 'for'.") + assert_error( + '{% if foo %}{% for item in seq %}...{% endfor %}{% endfor %}', + "Encountered unknown tag 'endfor'. Jinja was looking for " + "the following tags: 'elif' or 'else' or 'endif'. The " + "innermost block that needs to be closed is 'if'.") + assert_error('{% if foo %}', + "Unexpected end of template. Jinja was looking for the " + "following tags: 'elif' or 'else' or 'endif'. The " + "innermost block that needs to be closed is 'if'.") + assert_error('{% for item in seq %}', + "Unexpected end of template. Jinja was looking for the " + "following tags: 'endfor' or 'else'. The innermost block " + "that needs to be closed is 'for'.") + assert_error( + '{% block foo-bar-baz %}', + "Block names in Jinja have to be valid Python identifiers " + "and may not contain hyphens, use an underscore instead.") + assert_error('{% unknown_tag %}', + "Encountered unknown tag 'unknown_tag'.") + + +@pytest.mark.lexnparse +@pytest.mark.syntax +class TestSyntax(): + + def test_call(self, env): + env = Environment() + env.globals['foo'] = lambda a, b, c, e, g: a + b + c + e + g + tmpl = env.from_string( + "{{ foo('a', c='d', e='f', *['b'], **{'g': 'h'}) }}" + ) + assert tmpl.render() == 'abdfh' + + def test_slicing(self, env): + tmpl = env.from_string('{{ [1, 2, 3][:] }}|{{ [1, 2, 3][::-1] }}') + assert tmpl.render() == '[1, 2, 3]|[3, 2, 1]' + + def test_attr(self, env): + tmpl = env.from_string("{{ foo.bar }}|{{ foo['bar'] }}") + assert tmpl.render(foo={'bar': 42}) == '42|42' + + def test_subscript(self, env): + tmpl = env.from_string("{{ foo[0] }}|{{ foo[-1] }}") + assert tmpl.render(foo=[0, 1, 2]) == '0|2' + + def test_tuple(self, env): + tmpl = env.from_string('{{ () }}|{{ (1,) }}|{{ (1, 2) }}') + assert tmpl.render() == '()|(1,)|(1, 2)' + + def test_math(self, env): + tmpl = env.from_string('{{ (1 + 1 * 2) - 3 / 2 }}|{{ 2**3 }}') + assert tmpl.render() == '1.5|8' + + def test_div(self, env): + tmpl = env.from_string('{{ 3 // 2 }}|{{ 3 / 2 }}|{{ 3 % 2 }}') + assert tmpl.render() == '1|1.5|1' + + def test_unary(self, env): + tmpl = env.from_string('{{ +3 }}|{{ -3 }}') + assert tmpl.render() == '3|-3' + + def test_concat(self, env): + tmpl = env.from_string("{{ [1, 2] ~ 'foo' }}") + assert tmpl.render() == '[1, 2]foo' + + def test_compare(self, env): + tmpl = env.from_string('{{ 1 > 0 }}|{{ 1 >= 1 }}|{{ 2 < 3 }}|' + '{{ 2 == 2 }}|{{ 1 <= 1 }}') + assert tmpl.render() == 'True|True|True|True|True' + + def test_inop(self, env): + tmpl = env.from_string('{{ 1 in [1, 2, 3] }}|{{ 1 not in [1, 2, 3] }}') + assert tmpl.render() == 'True|False' + + def test_literals(self, env): + tmpl = env.from_string('{{ [] }}|{{ {} }}|{{ () }}') + assert tmpl.render().lower() == '[]|{}|()' + + def test_bool(self, env): + tmpl = env.from_string('{{ true and false }}|{{ false ' + 'or true }}|{{ not false }}') + assert tmpl.render() == 'False|True|True' + + def test_grouping(self, env): + tmpl = env.from_string( + '{{ (true and false) or (false and true) and not false }}') + assert tmpl.render() == 'False' + + def test_django_attr(self, env): + tmpl = env.from_string('{{ [1, 2, 3].0 }}|{{ [[1]].0.0 }}') + assert tmpl.render() == '1|1' + + def test_conditional_expression(self, env): + tmpl = env.from_string('''{{ 0 if true else 1 }}''') + assert tmpl.render() == '0' + + def test_short_conditional_expression(self, env): + tmpl = env.from_string('<{{ 1 if false }}>') + assert tmpl.render() == '<>' + + tmpl = env.from_string('<{{ (1 if false).bar }}>') + pytest.raises(UndefinedError, tmpl.render) + + def test_filter_priority(self, env): + tmpl = env.from_string('{{ "foo"|upper + "bar"|upper }}') + assert tmpl.render() == 'FOOBAR' + + def test_function_calls(self, env): + tests = [ + (True, '*foo, bar'), + (True, '*foo, *bar'), + (True, '*foo, bar=42'), + (True, '**foo, *bar'), + (True, '**foo, bar'), + (False, 'foo, bar'), + (False, 'foo, bar=42'), + (False, 'foo, bar=23, *args'), + (False, 'a, b=c, *d, **e'), + (False, '*foo, **bar') + ] + for should_fail, sig in tests: + if should_fail: + pytest.raises(TemplateSyntaxError, + env.from_string, '{{ foo(%s) }}' % sig) + else: + env.from_string('foo(%s)' % sig) + + def test_tuple_expr(self, env): + for tmpl in [ + '{{ () }}', + '{{ (1, 2) }}', + '{{ (1, 2,) }}', + '{{ 1, }}', + '{{ 1, 2 }}', + '{% for foo, bar in seq %}...{% endfor %}', + '{% for x in foo, bar %}...{% endfor %}', + '{% for x in foo, %}...{% endfor %}' + ]: + assert env.from_string(tmpl) + + def test_trailing_comma(self, env): + tmpl = env.from_string('{{ (1, 2,) }}|{{ [1, 2,] }}|{{ {1: 2,} }}') + assert tmpl.render().lower() == '(1, 2)|[1, 2]|{1: 2}' + + def test_block_end_name(self, env): + env.from_string('{% block foo %}...{% endblock foo %}') + pytest.raises(TemplateSyntaxError, env.from_string, + '{% block x %}{% endblock y %}') + + def test_constant_casing(self, env): + for const in True, False, None: + tmpl = env.from_string('{{ %s }}|{{ %s }}|{{ %s }}' % ( + str(const), str(const).lower(), str(const).upper() + )) + assert tmpl.render() == '%s|%s|' % (const, const) + + def test_test_chaining(self, env): + pytest.raises(TemplateSyntaxError, env.from_string, + '{{ foo is string is sequence }}') + assert env.from_string( + '{{ 42 is string or 42 is number }}' + ).render() == 'True' + + def test_string_concatenation(self, env): + tmpl = env.from_string('{{ "foo" "bar" "baz" }}') + assert tmpl.render() == 'foobarbaz' + + def test_notin(self, env): + bar = range(100) + tmpl = env.from_string('''{{ not 42 in bar }}''') + assert tmpl.render(bar=bar) == text_type(not 42 in bar) + + def test_implicit_subscribed_tuple(self, env): + class Foo(object): + def __getitem__(self, x): + return x + t = env.from_string('{{ foo[1, 2] }}') + assert t.render(foo=Foo()) == u'(1, 2)' + + def test_raw2(self, env): + tmpl = env.from_string('{% raw %}{{ FOO }} and {% BAR %}{% endraw %}') + assert tmpl.render() == '{{ FOO }} and {% BAR %}' + + def test_const(self, env): + tmpl = env.from_string( + '{{ true }}|{{ false }}|{{ none }}|' + '{{ none is defined }}|{{ missing is defined }}') + assert tmpl.render() == 'True|False|None|True|False' + + def test_neg_filter_priority(self, env): + node = env.parse('{{ -1|foo }}') + assert isinstance(node.body[0].nodes[0], nodes.Filter) + assert isinstance(node.body[0].nodes[0].node, nodes.Neg) + + def test_const_assign(self, env): + constass1 = '''{% set true = 42 %}''' + constass2 = '''{% for none in seq %}{% endfor %}''' + for tmpl in constass1, constass2: + pytest.raises(TemplateSyntaxError, env.from_string, tmpl) + + def test_localset(self, env): + tmpl = env.from_string('''{% set foo = 0 %}\ +{% for item in [1, 2] %}{% set foo = 1 %}{% endfor %}\ +{{ foo }}''') + assert tmpl.render() == '0' + + def test_parse_unary(self, env): + tmpl = env.from_string('{{ -foo["bar"] }}') + assert tmpl.render(foo={'bar': 42}) == '-42' + tmpl = env.from_string('{{ -foo["bar"]|abs }}') + assert tmpl.render(foo={'bar': 42}) == '42' + + +@pytest.mark.lexnparse +@pytest.mark.lstripblocks +class TestLstripBlocks(): + + def test_lstrip(self, env): + env = Environment(lstrip_blocks=True, trim_blocks=False) + tmpl = env.from_string(''' {% if True %}\n {% endif %}''') + assert tmpl.render() == "\n" + + def test_lstrip_trim(self, env): + env = Environment(lstrip_blocks=True, trim_blocks=True) + tmpl = env.from_string(''' {% if True %}\n {% endif %}''') + assert tmpl.render() == "" + + def test_no_lstrip(self, env): + env = Environment(lstrip_blocks=True, trim_blocks=False) + tmpl = env.from_string(''' {%+ if True %}\n {%+ endif %}''') + assert tmpl.render() == " \n " + + def test_lstrip_endline(self, env): + env = Environment(lstrip_blocks=True, trim_blocks=False) + tmpl = env.from_string( + ''' hello{% if True %}\n goodbye{% endif %}''') + assert tmpl.render() == " hello\n goodbye" + + def test_lstrip_inline(self, env): + env = Environment(lstrip_blocks=True, trim_blocks=False) + tmpl = env.from_string(''' {% if True %}hello {% endif %}''') + assert tmpl.render() == 'hello ' + + def test_lstrip_nested(self, env): + env = Environment(lstrip_blocks=True, trim_blocks=False) + tmpl = env.from_string( + ''' {% if True %}a {% if True %}b {% endif %}c {% endif %}''') + assert tmpl.render() == 'a b c ' + + def test_lstrip_left_chars(self, env): + env = Environment(lstrip_blocks=True, trim_blocks=False) + tmpl = env.from_string(''' abc {% if True %} + hello{% endif %}''') + assert tmpl.render() == ' abc \n hello' + + def test_lstrip_embeded_strings(self, env): + env = Environment(lstrip_blocks=True, trim_blocks=False) + tmpl = env.from_string(''' {% set x = " {% str %} " %}{{ x }}''') + assert tmpl.render() == ' {% str %} ' + + def test_lstrip_preserve_leading_newlines(self, env): + env = Environment(lstrip_blocks=True, trim_blocks=False) + tmpl = env.from_string('''\n\n\n{% set hello = 1 %}''') + assert tmpl.render() == '\n\n\n' + + def test_lstrip_comment(self, env): + env = Environment(lstrip_blocks=True, trim_blocks=False) + tmpl = env.from_string(''' {# if True #} +hello + {#endif#}''') + assert tmpl.render() == '\nhello\n' + + def test_lstrip_angle_bracket_simple(self, env): + env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##', + lstrip_blocks=True, trim_blocks=True) + tmpl = env.from_string(''' <% if True %>hello <% endif %>''') + assert tmpl.render() == 'hello ' + + def test_lstrip_angle_bracket_comment(self, env): + env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##', + lstrip_blocks=True, trim_blocks=True) + tmpl = env.from_string(''' <%# if True %>hello <%# endif %>''') + assert tmpl.render() == 'hello ' + + def test_lstrip_angle_bracket(self, env): + env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##', + lstrip_blocks=True, trim_blocks=True) + tmpl = env.from_string('''\ + <%# regular comment %> + <% for item in seq %> +${item} ## the rest of the stuff + <% endfor %>''') + assert tmpl.render(seq=range(5)) == \ + ''.join('%s\n' % x for x in range(5)) + + def test_lstrip_angle_bracket_compact(self, env): + env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##', + lstrip_blocks=True, trim_blocks=True) + tmpl = env.from_string('''\ + <%#regular comment%> + <%for item in seq%> +${item} ## the rest of the stuff + <%endfor%>''') + assert tmpl.render(seq=range(5)) == \ + ''.join('%s\n' % x for x in range(5)) + + def test_php_syntax_with_manual(self, env): + env = Environment('<?', '?>', '<?=', '?>', '<!--', '-->', + lstrip_blocks=True, trim_blocks=True) + tmpl = env.from_string('''\ + <!-- I'm a comment, I'm not interesting --> + <? for item in seq -?> + <?= item ?> + <?- endfor ?>''') + assert tmpl.render(seq=range(5)) == '01234' + + def test_php_syntax(self, env): + env = Environment('<?', '?>', '<?=', '?>', '<!--', '-->', + lstrip_blocks=True, trim_blocks=True) + tmpl = env.from_string('''\ + <!-- I'm a comment, I'm not interesting --> + <? for item in seq ?> + <?= item ?> + <? endfor ?>''') + assert tmpl.render(seq=range(5)) \ + == ''.join(' %s\n' % x for x in range(5)) + + def test_php_syntax_compact(self, env): + env = Environment('<?', '?>', '<?=', '?>', '<!--', '-->', + lstrip_blocks=True, trim_blocks=True) + tmpl = env.from_string('''\ + <!-- I'm a comment, I'm not interesting --> + <?for item in seq?> + <?=item?> + <?endfor?>''') + assert tmpl.render(seq=range(5)) \ + == ''.join(' %s\n' % x for x in range(5)) + + def test_erb_syntax(self, env): + env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>', + lstrip_blocks=True, trim_blocks=True) + # env.from_string('') + # for n,r in env.lexer.rules.iteritems(): + # print n + # print env.lexer.rules['root'][0][0].pattern + # print "'%s'" % tmpl.render(seq=range(5)) + tmpl = env.from_string('''\ +<%# I'm a comment, I'm not interesting %> + <% for item in seq %> + <%= item %> + <% endfor %> +''') + assert tmpl.render(seq=range(5)) \ + == ''.join(' %s\n' % x for x in range(5)) + + def test_erb_syntax_with_manual(self, env): + env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>', + lstrip_blocks=True, trim_blocks=True) + tmpl = env.from_string('''\ +<%# I'm a comment, I'm not interesting %> + <% for item in seq -%> + <%= item %> + <%- endfor %>''') + assert tmpl.render(seq=range(5)) == '01234' + + def test_erb_syntax_no_lstrip(self, env): + env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>', + lstrip_blocks=True, trim_blocks=True) + tmpl = env.from_string('''\ +<%# I'm a comment, I'm not interesting %> + <%+ for item in seq -%> + <%= item %> + <%- endfor %>''') + assert tmpl.render(seq=range(5)) == ' 01234' + + def test_comment_syntax(self, env): + env = Environment('<!--', '-->', '${', '}', '<!--#', '-->', + lstrip_blocks=True, trim_blocks=True) + tmpl = env.from_string('''\ +<!--# I'm a comment, I'm not interesting -->\ +<!-- for item in seq ---> + ${item} +<!--- endfor -->''') + assert tmpl.render(seq=range(5)) == '01234' diff --git a/deps/v8_inspector/deps/jinja2/tests/test_loader.py b/deps/v8_inspector/deps/jinja2/tests/test_loader.py new file mode 100644 index 00000000000000..6d22fad390cb80 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tests/test_loader.py @@ -0,0 +1,220 @@ +# -*- coding: utf-8 -*- +""" + jinja2.testsuite.loader + ~~~~~~~~~~~~~~~~~~~~~~~ + + Test the loaders. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import os +import sys +import tempfile +import shutil +import pytest + +from jinja2 import Environment, loaders +from jinja2._compat import PYPY, PY2 +from jinja2.loaders import split_template_path +from jinja2.exceptions import TemplateNotFound + + +@pytest.mark.loaders +class TestLoaders(): + + def test_dict_loader(self, dict_loader): + env = Environment(loader=dict_loader) + tmpl = env.get_template('justdict.html') + assert tmpl.render().strip() == 'FOO' + pytest.raises(TemplateNotFound, env.get_template, 'missing.html') + + def test_package_loader(self, package_loader): + env = Environment(loader=package_loader) + tmpl = env.get_template('test.html') + assert tmpl.render().strip() == 'BAR' + pytest.raises(TemplateNotFound, env.get_template, 'missing.html') + + def test_filesystem_loader(self, filesystem_loader): + env = Environment(loader=filesystem_loader) + tmpl = env.get_template('test.html') + assert tmpl.render().strip() == 'BAR' + tmpl = env.get_template('foo/test.html') + assert tmpl.render().strip() == 'FOO' + pytest.raises(TemplateNotFound, env.get_template, 'missing.html') + + def test_choice_loader(self, choice_loader): + env = Environment(loader=choice_loader) + tmpl = env.get_template('justdict.html') + assert tmpl.render().strip() == 'FOO' + tmpl = env.get_template('test.html') + assert tmpl.render().strip() == 'BAR' + pytest.raises(TemplateNotFound, env.get_template, 'missing.html') + + def test_function_loader(self, function_loader): + env = Environment(loader=function_loader) + tmpl = env.get_template('justfunction.html') + assert tmpl.render().strip() == 'FOO' + pytest.raises(TemplateNotFound, env.get_template, 'missing.html') + + def test_prefix_loader(self, prefix_loader): + env = Environment(loader=prefix_loader) + tmpl = env.get_template('a/test.html') + assert tmpl.render().strip() == 'BAR' + tmpl = env.get_template('b/justdict.html') + assert tmpl.render().strip() == 'FOO' + pytest.raises(TemplateNotFound, env.get_template, 'missing') + + def test_caching(self): + changed = False + + class TestLoader(loaders.BaseLoader): + def get_source(self, environment, template): + return u'foo', None, lambda: not changed + env = Environment(loader=TestLoader(), cache_size=-1) + tmpl = env.get_template('template') + assert tmpl is env.get_template('template') + changed = True + assert tmpl is not env.get_template('template') + changed = False + + env = Environment(loader=TestLoader(), cache_size=0) + assert env.get_template('template') \ + is not env.get_template('template') + + env = Environment(loader=TestLoader(), cache_size=2) + t1 = env.get_template('one') + t2 = env.get_template('two') + assert t2 is env.get_template('two') + assert t1 is env.get_template('one') + t3 = env.get_template('three') + assert 'one' in env.cache + assert 'two' not in env.cache + assert 'three' in env.cache + + def test_dict_loader_cache_invalidates(self): + mapping = {'foo': "one"} + env = Environment(loader=loaders.DictLoader(mapping)) + assert env.get_template('foo').render() == "one" + mapping['foo'] = "two" + assert env.get_template('foo').render() == "two" + + def test_split_template_path(self): + assert split_template_path('foo/bar') == ['foo', 'bar'] + assert split_template_path('./foo/bar') == ['foo', 'bar'] + pytest.raises(TemplateNotFound, split_template_path, '../foo') + + +@pytest.mark.loaders +@pytest.mark.moduleloader +class TestModuleLoader(): + archive = None + + def compile_down(self, prefix_loader, zip='deflated', py_compile=False): + log = [] + self.reg_env = Environment(loader=prefix_loader) + if zip is not None: + fd, self.archive = tempfile.mkstemp(suffix='.zip') + os.close(fd) + else: + self.archive = tempfile.mkdtemp() + self.reg_env.compile_templates(self.archive, zip=zip, + log_function=log.append, + py_compile=py_compile) + self.mod_env = Environment(loader=loaders.ModuleLoader(self.archive)) + return ''.join(log) + + def teardown(self): + if hasattr(self, 'mod_env'): + if os.path.isfile(self.archive): + os.remove(self.archive) + else: + shutil.rmtree(self.archive) + self.archive = None + + def test_log(self, prefix_loader): + log = self.compile_down(prefix_loader) + assert 'Compiled "a/foo/test.html" as ' \ + 'tmpl_a790caf9d669e39ea4d280d597ec891c4ef0404a' in log + assert 'Finished compiling templates' in log + assert 'Could not compile "a/syntaxerror.html": ' \ + 'Encountered unknown tag \'endif\'' in log + + def _test_common(self): + tmpl1 = self.reg_env.get_template('a/test.html') + tmpl2 = self.mod_env.get_template('a/test.html') + assert tmpl1.render() == tmpl2.render() + + tmpl1 = self.reg_env.get_template('b/justdict.html') + tmpl2 = self.mod_env.get_template('b/justdict.html') + assert tmpl1.render() == tmpl2.render() + + def test_deflated_zip_compile(self, prefix_loader): + self.compile_down(prefix_loader, zip='deflated') + self._test_common() + + def test_stored_zip_compile(self, prefix_loader): + self.compile_down(prefix_loader, zip='stored') + self._test_common() + + def test_filesystem_compile(self, prefix_loader): + self.compile_down(prefix_loader, zip=None) + self._test_common() + + def test_weak_references(self, prefix_loader): + self.compile_down(prefix_loader) + tmpl = self.mod_env.get_template('a/test.html') + key = loaders.ModuleLoader.get_template_key('a/test.html') + name = self.mod_env.loader.module.__name__ + + assert hasattr(self.mod_env.loader.module, key) + assert name in sys.modules + + # unset all, ensure the module is gone from sys.modules + self.mod_env = tmpl = None + + try: + import gc + gc.collect() + except: + pass + + assert name not in sys.modules + + # This test only makes sense on non-pypy python 2 + @pytest.mark.skipif( + not (PY2 and not PYPY), + reason='This test only makes sense on non-pypy python 2') + def test_byte_compilation(self, prefix_loader): + log = self.compile_down(prefix_loader, py_compile=True) + assert 'Byte-compiled "a/test.html"' in log + tmpl1 = self.mod_env.get_template('a/test.html') + mod = self.mod_env.loader.module. \ + tmpl_3c4ddf650c1a73df961a6d3d2ce2752f1b8fd490 + assert mod.__file__.endswith('.pyc') + + def test_choice_loader(self, prefix_loader): + log = self.compile_down(prefix_loader) + + self.mod_env.loader = loaders.ChoiceLoader([ + self.mod_env.loader, + loaders.DictLoader({'DICT_SOURCE': 'DICT_TEMPLATE'}) + ]) + + tmpl1 = self.mod_env.get_template('a/test.html') + assert tmpl1.render() == 'BAR' + tmpl2 = self.mod_env.get_template('DICT_SOURCE') + assert tmpl2.render() == 'DICT_TEMPLATE' + + def test_prefix_loader(self, prefix_loader): + log = self.compile_down(prefix_loader) + + self.mod_env.loader = loaders.PrefixLoader({ + 'MOD': self.mod_env.loader, + 'DICT': loaders.DictLoader({'test.html': 'DICT_TEMPLATE'}) + }) + + tmpl1 = self.mod_env.get_template('MOD/a/test.html') + assert tmpl1.render() == 'BAR' + tmpl2 = self.mod_env.get_template('DICT/test.html') + assert tmpl2.render() == 'DICT_TEMPLATE' diff --git a/deps/v8_inspector/deps/jinja2/tests/test_regression.py b/deps/v8_inspector/deps/jinja2/tests/test_regression.py new file mode 100644 index 00000000000000..a4aa157110395f --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tests/test_regression.py @@ -0,0 +1,278 @@ +# -*- coding: utf-8 -*- +""" + jinja2.testsuite.regression + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Tests corner cases and bugs. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import pytest + +from jinja2 import Template, Environment, DictLoader, TemplateSyntaxError, \ + TemplateNotFound, PrefixLoader +from jinja2._compat import text_type + + +@pytest.mark.regression +class TestCorner(): + + def test_assigned_scoping(self, env): + t = env.from_string(''' + {%- for item in (1, 2, 3, 4) -%} + [{{ item }}] + {%- endfor %} + {{- item -}} + ''') + assert t.render(item=42) == '[1][2][3][4]42' + + t = env.from_string(''' + {%- for item in (1, 2, 3, 4) -%} + [{{ item }}] + {%- endfor %} + {%- set item = 42 %} + {{- item -}} + ''') + assert t.render() == '[1][2][3][4]42' + + t = env.from_string(''' + {%- set item = 42 %} + {%- for item in (1, 2, 3, 4) -%} + [{{ item }}] + {%- endfor %} + {{- item -}} + ''') + assert t.render() == '[1][2][3][4]42' + + def test_closure_scoping(self, env): + t = env.from_string(''' + {%- set wrapper = "<FOO>" %} + {%- for item in (1, 2, 3, 4) %} + {%- macro wrapper() %}[{{ item }}]{% endmacro %} + {{- wrapper() }} + {%- endfor %} + {{- wrapper -}} + ''') + assert t.render() == '[1][2][3][4]<FOO>' + + t = env.from_string(''' + {%- for item in (1, 2, 3, 4) %} + {%- macro wrapper() %}[{{ item }}]{% endmacro %} + {{- wrapper() }} + {%- endfor %} + {%- set wrapper = "<FOO>" %} + {{- wrapper -}} + ''') + assert t.render() == '[1][2][3][4]<FOO>' + + t = env.from_string(''' + {%- for item in (1, 2, 3, 4) %} + {%- macro wrapper() %}[{{ item }}]{% endmacro %} + {{- wrapper() }} + {%- endfor %} + {{- wrapper -}} + ''') + assert t.render(wrapper=23) == '[1][2][3][4]23' + + +@pytest.mark.regression +class TestBug(): + + def test_keyword_folding(self, env): + env = Environment() + env.filters['testing'] = lambda value, some: value + some + assert env.from_string("{{ 'test'|testing(some='stuff') }}") \ + .render() == 'teststuff' + + def test_extends_output_bugs(self, env): + env = Environment(loader=DictLoader({ + 'parent.html': '(({% block title %}{% endblock %}))' + })) + + t = env.from_string( + '{% if expr %}{% extends "parent.html" %}{% endif %}' + '[[{% block title %}title{% endblock %}]]' + '{% for item in [1, 2, 3] %}({{ item }}){% endfor %}' + ) + assert t.render(expr=False) == '[[title]](1)(2)(3)' + assert t.render(expr=True) == '((title))' + + def test_urlize_filter_escaping(self, env): + tmpl = env.from_string('{{ "http://www.example.org/<foo"|urlize }}') + assert tmpl.render() == '<a href="http://www.example.org/<foo">'\ + 'http://www.example.org/<foo</a>' + + def test_loop_call_loop(self, env): + tmpl = env.from_string(''' + + {% macro test() %} + {{ caller() }} + {% endmacro %} + + {% for num1 in range(5) %} + {% call test() %} + {% for num2 in range(10) %} + {{ loop.index }} + {% endfor %} + {% endcall %} + {% endfor %} + + ''') + + assert tmpl.render().split() \ + == [text_type(x) for x in range(1, 11)] * 5 + + def test_weird_inline_comment(self, env): + env = Environment(line_statement_prefix='%') + pytest.raises(TemplateSyntaxError, env.from_string, + '% for item in seq {# missing #}\n...% endfor') + + def test_old_macro_loop_scoping_bug(self, env): + tmpl = env.from_string('{% for i in (1, 2) %}{{ i }}{% endfor %}' + '{% macro i() %}3{% endmacro %}{{ i() }}') + assert tmpl.render() == '123' + + def test_partial_conditional_assignments(self, env): + tmpl = env.from_string('{% if b %}{% set a = 42 %}{% endif %}{{ a }}') + assert tmpl.render(a=23) == '23' + assert tmpl.render(b=True) == '42' + + def test_stacked_locals_scoping_bug(self, env): + env = Environment(line_statement_prefix='#') + t = env.from_string('''\ +# for j in [1, 2]: +# set x = 1 +# for i in [1, 2]: +# print x +# if i % 2 == 0: +# set x = x + 1 +# endif +# endfor +# endfor +# if a +# print 'A' +# elif b +# print 'B' +# elif c == d +# print 'C' +# else +# print 'D' +# endif + ''') + assert t.render(a=0, b=False, c=42, d=42.0) == '1111C' + + def test_stacked_locals_scoping_bug_twoframe(self, env): + t = Template(''' + {% set x = 1 %} + {% for item in foo %} + {% if item == 1 %} + {% set x = 2 %} + {% endif %} + {% endfor %} + {{ x }} + ''') + rv = t.render(foo=[1]).strip() + assert rv == u'1' + + def test_call_with_args(self, env): + t = Template("""{% macro dump_users(users) -%} + <ul> + {%- for user in users -%} + <li><p>{{ user.username|e }}</p>{{ caller(user) }}</li> + {%- endfor -%} + </ul> + {%- endmacro -%} + + {% call(user) dump_users(list_of_user) -%} + <dl> + <dl>Realname</dl> + <dd>{{ user.realname|e }}</dd> + <dl>Description</dl> + <dd>{{ user.description }}</dd> + </dl> + {% endcall %}""") + + assert [x.strip() for x in t.render(list_of_user=[{ + 'username': 'apo', + 'realname': 'something else', + 'description': 'test' + }]).splitlines()] == [ + u'<ul><li><p>apo</p><dl>', + u'<dl>Realname</dl>', + u'<dd>something else</dd>', + u'<dl>Description</dl>', + u'<dd>test</dd>', + u'</dl>', + u'</li></ul>' + ] + + def test_empty_if_condition_fails(self, env): + pytest.raises(TemplateSyntaxError, + Template, '{% if %}....{% endif %}') + pytest.raises(TemplateSyntaxError, + Template, '{% if foo %}...{% elif %}...{% endif %}') + pytest.raises(TemplateSyntaxError, + Template, '{% for x in %}..{% endfor %}') + + def test_recursive_loop_bug(self, env): + tpl1 = Template(""" + {% for p in foo recursive%} + {{p.bar}} + {% for f in p.fields recursive%} + {{f.baz}} + {{p.bar}} + {% if f.rec %} + {{ loop(f.sub) }} + {% endif %} + {% endfor %} + {% endfor %} + """) + + tpl2 = Template(""" + {% for p in foo%} + {{p.bar}} + {% for f in p.fields recursive%} + {{f.baz}} + {{p.bar}} + {% if f.rec %} + {{ loop(f.sub) }} + {% endif %} + {% endfor %} + {% endfor %} + """) + + def test_else_loop_bug(self, env): + t = Template(''' + {% for x in y %} + {{ loop.index0 }} + {% else %} + {% for i in range(3) %}{{ i }}{% endfor %} + {% endfor %} + ''') + assert t.render(y=[]).strip() == '012' + + def test_correct_prefix_loader_name(self, env): + env = Environment(loader=PrefixLoader({ + 'foo': DictLoader({}) + })) + try: + env.get_template('foo/bar.html') + except TemplateNotFound as e: + assert e.name == 'foo/bar.html' + else: + assert False, 'expected error here' + + def test_contextfunction_callable_classes(self, env): + from jinja2.utils import contextfunction + + class CallableClass(object): + @contextfunction + def __call__(self, ctx): + return ctx.resolve('hello') + + tpl = Template("""{{ callableclass() }}""") + output = tpl.render(callableclass=CallableClass(), hello='TEST') + expected = 'TEST' + + assert output == expected diff --git a/deps/v8_inspector/deps/jinja2/tests/test_security.py b/deps/v8_inspector/deps/jinja2/tests/test_security.py new file mode 100644 index 00000000000000..e5b463fc989263 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tests/test_security.py @@ -0,0 +1,161 @@ +# -*- coding: utf-8 -*- +""" + jinja2.testsuite.security + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + Checks the sandbox and other security features. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import pytest + +from jinja2 import Environment +from jinja2.sandbox import SandboxedEnvironment, \ + ImmutableSandboxedEnvironment, unsafe +from jinja2 import Markup, escape +from jinja2.exceptions import SecurityError, TemplateSyntaxError, \ + TemplateRuntimeError +from jinja2._compat import text_type + + +class PrivateStuff(object): + + def bar(self): + return 23 + + @unsafe + def foo(self): + return 42 + + def __repr__(self): + return 'PrivateStuff' + + +class PublicStuff(object): + bar = lambda self: 23 + _foo = lambda self: 42 + + def __repr__(self): + return 'PublicStuff' + + +@pytest.mark.sandbox +class TestSandbox(): + + def test_unsafe(self, env): + env = SandboxedEnvironment() + pytest.raises(SecurityError, env.from_string("{{ foo.foo() }}").render, + foo=PrivateStuff()) + assert env.from_string("{{ foo.bar() }}").render(foo=PrivateStuff()) == '23' + + pytest.raises(SecurityError, + env.from_string("{{ foo._foo() }}").render, + foo=PublicStuff()) + assert env.from_string("{{ foo.bar() }}").render(foo=PublicStuff()) == '23' + assert env.from_string("{{ foo.__class__ }}").render(foo=42) == '' + assert env.from_string("{{ foo.func_code }}").render(foo=lambda:None) == '' + # security error comes from __class__ already. + pytest.raises(SecurityError, env.from_string( + "{{ foo.__class__.__subclasses__() }}").render, foo=42) + + def test_immutable_environment(self, env): + env = ImmutableSandboxedEnvironment() + pytest.raises(SecurityError, env.from_string( + '{{ [].append(23) }}').render) + pytest.raises(SecurityError, env.from_string( + '{{ {1:2}.clear() }}').render) + + def test_restricted(self, env): + env = SandboxedEnvironment() + pytest.raises(TemplateSyntaxError, env.from_string, + "{% for item.attribute in seq %}...{% endfor %}") + pytest.raises(TemplateSyntaxError, env.from_string, + "{% for foo, bar.baz in seq %}...{% endfor %}") + + def test_markup_operations(self, env): + # adding two strings should escape the unsafe one + unsafe = '<script type="application/x-some-script">alert("foo");</script>' + safe = Markup('<em>username</em>') + assert unsafe + safe == text_type(escape(unsafe)) + text_type(safe) + + # string interpolations are safe to use too + assert Markup('<em>%s</em>') % '<bad user>' == \ + '<em><bad user></em>' + assert Markup('<em>%(username)s</em>') % { + 'username': '<bad user>' + } == '<em><bad user></em>' + + # an escaped object is markup too + assert type(Markup('foo') + 'bar') is Markup + + # and it implements __html__ by returning itself + x = Markup("foo") + assert x.__html__() is x + + # it also knows how to treat __html__ objects + class Foo(object): + def __html__(self): + return '<em>awesome</em>' + + def __unicode__(self): + return 'awesome' + assert Markup(Foo()) == '<em>awesome</em>' + assert Markup('<strong>%s</strong>') % Foo() == \ + '<strong><em>awesome</em></strong>' + + # escaping and unescaping + assert escape('"<>&\'') == '"<>&'' + assert Markup("<em>Foo & Bar</em>").striptags() == "Foo & Bar" + assert Markup("<test>").unescape() == "<test>" + + def test_template_data(self, env): + env = Environment(autoescape=True) + t = env.from_string('{% macro say_hello(name) %}' + '<p>Hello {{ name }}!</p>{% endmacro %}' + '{{ say_hello("<blink>foo</blink>") }}') + escaped_out = '<p>Hello <blink>foo</blink>!</p>' + assert t.render() == escaped_out + assert text_type(t.module) == escaped_out + assert escape(t.module) == escaped_out + assert t.module.say_hello('<blink>foo</blink>') == escaped_out + assert escape(t.module.say_hello('<blink>foo</blink>')) == escaped_out + + def test_attr_filter(self, env): + env = SandboxedEnvironment() + tmpl = env.from_string('{{ cls|attr("__subclasses__")() }}') + pytest.raises(SecurityError, tmpl.render, cls=int) + + def test_binary_operator_intercepting(self, env): + def disable_op(left, right): + raise TemplateRuntimeError('that operator so does not work') + for expr, ctx, rv in ('1 + 2', {}, '3'), ('a + 2', {'a': 2}, '4'): + env = SandboxedEnvironment() + env.binop_table['+'] = disable_op + t = env.from_string('{{ %s }}' % expr) + assert t.render(ctx) == rv + env.intercepted_binops = frozenset(['+']) + t = env.from_string('{{ %s }}' % expr) + try: + t.render(ctx) + except TemplateRuntimeError as e: + pass + else: + assert False, 'expected runtime error' + + def test_unary_operator_intercepting(self, env): + def disable_op(arg): + raise TemplateRuntimeError('that operator so does not work') + for expr, ctx, rv in ('-1', {}, '-1'), ('-a', {'a': 2}, '-2'): + env = SandboxedEnvironment() + env.unop_table['-'] = disable_op + t = env.from_string('{{ %s }}' % expr) + assert t.render(ctx) == rv + env.intercepted_unops = frozenset(['-']) + t = env.from_string('{{ %s }}' % expr) + try: + t.render(ctx) + except TemplateRuntimeError as e: + pass + else: + assert False, 'expected runtime error' diff --git a/deps/v8_inspector/deps/jinja2/tests/test_tests.py b/deps/v8_inspector/deps/jinja2/tests/test_tests.py new file mode 100644 index 00000000000000..9e54038c9a2dc1 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tests/test_tests.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- +""" + jinja2.testsuite.tests + ~~~~~~~~~~~~~~~~~~~~~~ + + Who tests the tests? + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import pytest + +from jinja2 import Markup, Environment + + +@pytest.mark.test_tests +class TestTestsCase(): + + def test_defined(self, env): + tmpl = env.from_string('{{ missing is defined }}|' + '{{ true is defined }}') + assert tmpl.render() == 'False|True' + + def test_even(self, env): + tmpl = env.from_string('''{{ 1 is even }}|{{ 2 is even }}''') + assert tmpl.render() == 'False|True' + + def test_odd(self, env): + tmpl = env.from_string('''{{ 1 is odd }}|{{ 2 is odd }}''') + assert tmpl.render() == 'True|False' + + def test_lower(self, env): + tmpl = env.from_string('''{{ "foo" is lower }}|{{ "FOO" is lower }}''') + assert tmpl.render() == 'True|False' + + def test_typechecks(self, env): + tmpl = env.from_string(''' + {{ 42 is undefined }} + {{ 42 is defined }} + {{ 42 is none }} + {{ none is none }} + {{ 42 is number }} + {{ 42 is string }} + {{ "foo" is string }} + {{ "foo" is sequence }} + {{ [1] is sequence }} + {{ range is callable }} + {{ 42 is callable }} + {{ range(5) is iterable }} + {{ {} is mapping }} + {{ mydict is mapping }} + {{ [] is mapping }} + {{ 10 is number }} + {{ (10 ** 100) is number }} + {{ 3.14159 is number }} + {{ complex is number }} + ''') + + class MyDict(dict): + pass + + assert tmpl.render(mydict=MyDict(), complex=complex(1, 2)).split() == [ + 'False', 'True', 'False', 'True', 'True', 'False', + 'True', 'True', 'True', 'True', 'False', 'True', + 'True', 'True', 'False', 'True', 'True', 'True', 'True' + ] + + def test_sequence(self, env): + tmpl = env.from_string( + '{{ [1, 2, 3] is sequence }}|' + '{{ "foo" is sequence }}|' + '{{ 42 is sequence }}' + ) + assert tmpl.render() == 'True|True|False' + + def test_upper(self, env): + tmpl = env.from_string('{{ "FOO" is upper }}|{{ "foo" is upper }}') + assert tmpl.render() == 'True|False' + + def test_equalto(self, env): + tmpl = env.from_string('{{ foo is equalto 12 }}|' + '{{ foo is equalto 0 }}|' + '{{ foo is equalto (3 * 4) }}|' + '{{ bar is equalto "baz" }}|' + '{{ bar is equalto "zab" }}|' + '{{ bar is equalto ("ba" + "z") }}|' + '{{ bar is equalto bar }}|' + '{{ bar is equalto foo }}') + assert tmpl.render(foo=12, bar="baz") \ + == 'True|False|True|True|False|True|True|False' + + def test_sameas(self, env): + tmpl = env.from_string('{{ foo is sameas false }}|' + '{{ 0 is sameas false }}') + assert tmpl.render(foo=False) == 'True|False' + + def test_no_paren_for_arg1(self, env): + tmpl = env.from_string('{{ foo is sameas none }}') + assert tmpl.render(foo=None) == 'True' + + def test_escaped(self, env): + env = Environment(autoescape=True) + tmpl = env.from_string('{{ x is escaped }}|{{ y is escaped }}') + assert tmpl.render(x='foo', y=Markup('foo')) == 'False|True' diff --git a/deps/v8_inspector/deps/jinja2/tests/test_utils.py b/deps/v8_inspector/deps/jinja2/tests/test_utils.py new file mode 100644 index 00000000000000..373103618d2f30 --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tests/test_utils.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +""" + jinja2.testsuite.utils + ~~~~~~~~~~~~~~~~~~~~~~ + + Tests utilities jinja uses. + + :copyright: (c) 2010 by the Jinja Team. + :license: BSD, see LICENSE for more details. +""" +import gc + +import pytest + +import pickle + +from jinja2.utils import LRUCache, escape, object_type_repr + + +@pytest.mark.utils +@pytest.mark.lrucache +class TestLRUCache(): + + def test_simple(self): + d = LRUCache(3) + d["a"] = 1 + d["b"] = 2 + d["c"] = 3 + d["a"] + d["d"] = 4 + assert len(d) == 3 + assert 'a' in d and 'c' in d and 'd' in d and 'b' not in d + + def test_pickleable(self): + cache = LRUCache(2) + cache["foo"] = 42 + cache["bar"] = 23 + cache["foo"] + + for protocol in range(3): + copy = pickle.loads(pickle.dumps(cache, protocol)) + assert copy.capacity == cache.capacity + assert copy._mapping == cache._mapping + assert copy._queue == cache._queue + + +@pytest.mark.utils +@pytest.mark.helpers +class TestHelpers(): + + def test_object_type_repr(self): + class X(object): + pass + assert object_type_repr(42) == 'int object' + assert object_type_repr([]) == 'list object' + assert object_type_repr(X()) == 'test_utils.X object' + assert object_type_repr(None) == 'None' + assert object_type_repr(Ellipsis) == 'Ellipsis' + + +@pytest.mark.utils +@pytest.mark.markupleak +@pytest.mark.skipif(hasattr(escape, 'func_code'), + reason='this test only tests the c extension') +class TestMarkupLeak(): + + def test_markup_leaks(self): + counts = set() + for count in range(20): + for item in range(1000): + escape("foo") + escape("<foo>") + escape(u"foo") + escape(u"<foo>") + counts.add(len(gc.get_objects())) + assert len(counts) == 1, 'ouch, c extension seems to leak objects' diff --git a/deps/v8_inspector/deps/jinja2/tox.ini b/deps/v8_inspector/deps/jinja2/tox.ini new file mode 100644 index 00000000000000..0daabf75b6160d --- /dev/null +++ b/deps/v8_inspector/deps/jinja2/tox.ini @@ -0,0 +1,9 @@ +[tox] +envlist = py26, py27, pypy, py33, py34, py35 + +[testenv] +commands = + py.test [] + +deps = + pytest diff --git a/deps/v8_inspector/deps/markupsafe/.gitignore b/deps/v8_inspector/deps/markupsafe/.gitignore new file mode 100644 index 00000000000000..e3a879439104c2 --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +*.pyc +*.pyo +*.o +*.so +env +dist +build +.tox +*.egg-info diff --git a/deps/v8_inspector/deps/markupsafe/.travis.yml b/deps/v8_inspector/deps/markupsafe/.travis.yml new file mode 100644 index 00000000000000..c0128baf9c546b --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/.travis.yml @@ -0,0 +1,11 @@ +sudo: false +language: python +python: + - "2.6" + - "2.7" + - "3.3" + - "3.4" + - "3.5" +install: + - "python setup.py install" +script: make test diff --git a/deps/v8_inspector/deps/markupsafe/AUTHORS b/deps/v8_inspector/deps/markupsafe/AUTHORS new file mode 100644 index 00000000000000..f7e2942eccd11e --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/AUTHORS @@ -0,0 +1,13 @@ +MarkupSafe is written and maintained by Armin Ronacher and +various contributors: + +Development Lead +```````````````` + +- Armin Ronacher <armin.ronacher@active-4.com> + +Patches and Suggestions +``````````````````````` + +- Georg Brandl +- Mickaël Guérin diff --git a/deps/v8_inspector/deps/markupsafe/CHANGES b/deps/v8_inspector/deps/markupsafe/CHANGES new file mode 100644 index 00000000000000..706a230c754412 --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/CHANGES @@ -0,0 +1,48 @@ +MarkupSafe Changelog +==================== + +Version 1.0 +----------- + +- Fixed custom types not invoking `__unicode__` when used + with `format()`. +- Added `__version__` module attribute +- Improve unescape code to leave lone ampersands alone. + +Version 0.18 +------------ + +- Fixed `__mul__` and string splitting on Python 3. + +Version 0.17 +------------ + +- Fixed a bug with broken interpolation on tuples. + +Version 0.16 +------------ + +- Improved Python 3 Support and removed 2to3 +- Removed support for Python 3.2 and 2.5 + +Version 0.15 +------------ + +- Fixed a typo that caused the library to fail to install + on pypy and jython -.- + +Version 0.14 +------------ + +- Release fix for 0.13. + +Version 0.13 +------------ + +- Do not attempt to compile extension for PyPy or Jython. +- Work around some 64bit Windows issues. + +Version 0.12 +------------ + +- improved PyPy compatibility diff --git a/deps/v8_inspector/deps/markupsafe/LICENSE b/deps/v8_inspector/deps/markupsafe/LICENSE new file mode 100644 index 00000000000000..5d2693890dddc3 --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/LICENSE @@ -0,0 +1,33 @@ +Copyright (c) 2010 by Armin Ronacher and contributors. See AUTHORS +for more details. + +Some rights reserved. + +Redistribution and use in source and binary forms of the software as well +as documentation, with or without modification, are permitted provided +that the following conditions are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +* The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/deps/v8_inspector/deps/markupsafe/MANIFEST.in b/deps/v8_inspector/deps/markupsafe/MANIFEST.in new file mode 100644 index 00000000000000..d50e4e0dc798d6 --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/MANIFEST.in @@ -0,0 +1,2 @@ +include LICENSE README.rst AUTHORS +recursive-include markupsafe *.c diff --git a/deps/v8_inspector/deps/markupsafe/Makefile b/deps/v8_inspector/deps/markupsafe/Makefile new file mode 100644 index 00000000000000..fd263b160a5e22 --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/Makefile @@ -0,0 +1,7 @@ +test: + python setup.py test + +tox-test: + tox + +.PYONY: test tox-test diff --git a/deps/v8_inspector/deps/markupsafe/README.rst b/deps/v8_inspector/deps/markupsafe/README.rst new file mode 100644 index 00000000000000..53dcd023a98924 --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/README.rst @@ -0,0 +1,101 @@ +MarkupSafe +========== + +Implements a unicode subclass that supports HTML strings: + +>>> from markupsafe import Markup, escape +>>> escape("<script>alert(document.cookie);</script>") +Markup(u'<script>alert(document.cookie);</script>') +>>> tmpl = Markup("<em>%s</em>") +>>> tmpl % "Peter > Lustig" +Markup(u'<em>Peter > Lustig</em>') + +If you want to make an object unicode that is not yet unicode +but don't want to lose the taint information, you can use the +`soft_unicode` function. (On Python 3 you can also use `soft_str` which +is a different name for the same function). + +>>> from markupsafe import soft_unicode +>>> soft_unicode(42) +u'42' +>>> soft_unicode(Markup('foo')) +Markup(u'foo') + +HTML Representations +-------------------- + +Objects can customize their HTML markup equivalent by overriding +the `__html__` function: + +>>> class Foo(object): +... def __html__(self): +... return '<strong>Nice</strong>' +... +>>> escape(Foo()) +Markup(u'<strong>Nice</strong>') +>>> Markup(Foo()) +Markup(u'<strong>Nice</strong>') + +Silent Escapes +-------------- + +Since MarkupSafe 0.10 there is now also a separate escape function +called `escape_silent` that returns an empty string for `None` for +consistency with other systems that return empty strings for `None` +when escaping (for instance Pylons' webhelpers). + +If you also want to use this for the escape method of the Markup +object, you can create your own subclass that does that:: + + from markupsafe import Markup, escape_silent as escape + + class SilentMarkup(Markup): + __slots__ = () + + @classmethod + def escape(cls, s): + return cls(escape(s)) + +New-Style String Formatting +--------------------------- + +Starting with MarkupSafe 0.21 new style string formats from Python 2.6 and +3.x are now fully supported. Previously the escape behavior of those +functions was spotty at best. The new implementations operates under the +following algorithm: + +1. if an object has an ``__html_format__`` method it is called as + replacement for ``__format__`` with the format specifier. It either + has to return a string or markup object. +2. if an object has an ``__html__`` method it is called. +3. otherwise the default format system of Python kicks in and the result + is HTML escaped. + +Here is how you can implement your own formatting:: + + class User(object): + + def __init__(self, id, username): + self.id = id + self.username = username + + def __html_format__(self, format_spec): + if format_spec == 'link': + return Markup('<a href="/user/{0}">{1}</a>').format( + self.id, + self.__html__(), + ) + elif format_spec: + raise ValueError('Invalid format spec') + return self.__html__() + + def __html__(self): + return Markup('<span class=user>{0}</span>').format(self.username) + +And to format that user: + +>>> user = User(1, 'foo') +>>> Markup('<p>User: {0:link}').format(user) +Markup(u'<p>User: <a href="/user/1"><span class=user>foo</span></a>') + +Markupsafe supports Python 2.6, 2.7 and Python 3.3 and higher. diff --git a/deps/v8_inspector/deps/markupsafe/bench/bench_basic.py b/deps/v8_inspector/deps/markupsafe/bench/bench_basic.py new file mode 100644 index 00000000000000..1172d150bb2721 --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/bench/bench_basic.py @@ -0,0 +1,5 @@ +from markupsafe import escape + + +def run(): + escape('<strong>Hello World!</strong>') diff --git a/deps/v8_inspector/deps/markupsafe/bench/bench_largestring.py b/deps/v8_inspector/deps/markupsafe/bench/bench_largestring.py new file mode 100644 index 00000000000000..7bde595c10a8f8 --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/bench/bench_largestring.py @@ -0,0 +1,6 @@ +from markupsafe import escape + + +def run(): + string = '<strong>Hello World!</strong>' * 1000 + escape(string) diff --git a/deps/v8_inspector/deps/markupsafe/bench/bench_long_empty_string.py b/deps/v8_inspector/deps/markupsafe/bench/bench_long_empty_string.py new file mode 100644 index 00000000000000..320fb0a93bf7be --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/bench/bench_long_empty_string.py @@ -0,0 +1,6 @@ +from markupsafe import escape + + +def run(): + string = 'Hello World!' * 1000 + escape(string) diff --git a/deps/v8_inspector/deps/markupsafe/bench/bench_long_suffix.py b/deps/v8_inspector/deps/markupsafe/bench/bench_long_suffix.py new file mode 100644 index 00000000000000..43fe56371ea4d8 --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/bench/bench_long_suffix.py @@ -0,0 +1,6 @@ +from markupsafe import escape + + +def run(): + string = '<strong>Hello World!</strong>' + 'x' * 100000 + escape(string) diff --git a/deps/v8_inspector/deps/markupsafe/bench/bench_short_empty_string.py b/deps/v8_inspector/deps/markupsafe/bench/bench_short_empty_string.py new file mode 100644 index 00000000000000..91cccc49916f3d --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/bench/bench_short_empty_string.py @@ -0,0 +1,5 @@ +from markupsafe import escape + + +def run(): + escape('Hello World!') diff --git a/deps/v8_inspector/deps/markupsafe/bench/runbench.py b/deps/v8_inspector/deps/markupsafe/bench/runbench.py new file mode 100644 index 00000000000000..2ebdc53c58d467 --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/bench/runbench.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +""" + Runs the benchmarks +""" +import sys +import os +import re +from subprocess import Popen + +_filename_re = re.compile(r'^bench_(.*?)\.py$') +bench_directory = os.path.abspath(os.path.dirname(__file__)) + + +def list_benchmarks(): + result = [] + for name in os.listdir(bench_directory): + match = _filename_re.match(name) + if match is not None: + result.append(match.group(1)) + result.sort(key=lambda x: (x.startswith('logging_'), x.lower())) + return result + + +def run_bench(name): + sys.stdout.write('%-32s' % name) + sys.stdout.flush() + Popen([sys.executable, '-mtimeit', '-s', + 'from bench_%s import run' % name, + 'run()']).wait() + + +def main(): + print '=' * 80 + print 'Running benchmark for MarkupSafe' + print '-' * 80 + os.chdir(bench_directory) + for bench in list_benchmarks(): + run_bench(bench) + print '-' * 80 + + +if __name__ == '__main__': + main() diff --git a/deps/v8_inspector/deps/markupsafe/markupsafe/__init__.py b/deps/v8_inspector/deps/markupsafe/markupsafe/__init__.py new file mode 100644 index 00000000000000..68dc85f61200a0 --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/markupsafe/__init__.py @@ -0,0 +1,305 @@ +# -*- coding: utf-8 -*- +""" + markupsafe + ~~~~~~~~~~ + + Implements a Markup string. + + :copyright: (c) 2010 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" +import re +import string +from collections import Mapping +from markupsafe._compat import text_type, string_types, int_types, \ + unichr, iteritems, PY2 + +__version__ = "1.0" + +__all__ = ['Markup', 'soft_unicode', 'escape', 'escape_silent'] + + +_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)') +_entity_re = re.compile(r'&([^& ;]+);') + + +class Markup(text_type): + r"""Marks a string as being safe for inclusion in HTML/XML output without + needing to be escaped. This implements the `__html__` interface a couple + of frameworks and web applications use. :class:`Markup` is a direct + subclass of `unicode` and provides all the methods of `unicode` just that + it escapes arguments passed and always returns `Markup`. + + The `escape` function returns markup objects so that double escaping can't + happen. + + The constructor of the :class:`Markup` class can be used for three + different things: When passed an unicode object it's assumed to be safe, + when passed an object with an HTML representation (has an `__html__` + method) that representation is used, otherwise the object passed is + converted into a unicode string and then assumed to be safe: + + >>> Markup("Hello <em>World</em>!") + Markup(u'Hello <em>World</em>!') + >>> class Foo(object): + ... def __html__(self): + ... return '<a href="#">foo</a>' + ... + >>> Markup(Foo()) + Markup(u'<a href="#">foo</a>') + + If you want object passed being always treated as unsafe you can use the + :meth:`escape` classmethod to create a :class:`Markup` object: + + >>> Markup.escape("Hello <em>World</em>!") + Markup(u'Hello <em>World</em>!') + + Operations on a markup string are markup aware which means that all + arguments are passed through the :func:`escape` function: + + >>> em = Markup("<em>%s</em>") + >>> em % "foo & bar" + Markup(u'<em>foo & bar</em>') + >>> strong = Markup("<strong>%(text)s</strong>") + >>> strong % {'text': '<blink>hacker here</blink>'} + Markup(u'<strong><blink>hacker here</blink></strong>') + >>> Markup("<em>Hello</em> ") + "<foo>" + Markup(u'<em>Hello</em> <foo>') + """ + __slots__ = () + + def __new__(cls, base=u'', encoding=None, errors='strict'): + if hasattr(base, '__html__'): + base = base.__html__() + if encoding is None: + return text_type.__new__(cls, base) + return text_type.__new__(cls, base, encoding, errors) + + def __html__(self): + return self + + def __add__(self, other): + if isinstance(other, string_types) or hasattr(other, '__html__'): + return self.__class__(super(Markup, self).__add__(self.escape(other))) + return NotImplemented + + def __radd__(self, other): + if hasattr(other, '__html__') or isinstance(other, string_types): + return self.escape(other).__add__(self) + return NotImplemented + + def __mul__(self, num): + if isinstance(num, int_types): + return self.__class__(text_type.__mul__(self, num)) + return NotImplemented + __rmul__ = __mul__ + + def __mod__(self, arg): + if isinstance(arg, tuple): + arg = tuple(_MarkupEscapeHelper(x, self.escape) for x in arg) + else: + arg = _MarkupEscapeHelper(arg, self.escape) + return self.__class__(text_type.__mod__(self, arg)) + + def __repr__(self): + return '%s(%s)' % ( + self.__class__.__name__, + text_type.__repr__(self) + ) + + def join(self, seq): + return self.__class__(text_type.join(self, map(self.escape, seq))) + join.__doc__ = text_type.join.__doc__ + + def split(self, *args, **kwargs): + return list(map(self.__class__, text_type.split(self, *args, **kwargs))) + split.__doc__ = text_type.split.__doc__ + + def rsplit(self, *args, **kwargs): + return list(map(self.__class__, text_type.rsplit(self, *args, **kwargs))) + rsplit.__doc__ = text_type.rsplit.__doc__ + + def splitlines(self, *args, **kwargs): + return list(map(self.__class__, text_type.splitlines( + self, *args, **kwargs))) + splitlines.__doc__ = text_type.splitlines.__doc__ + + def unescape(self): + r"""Unescape markup again into an text_type string. This also resolves + known HTML4 and XHTML entities: + + >>> Markup("Main » <em>About</em>").unescape() + u'Main \xbb <em>About</em>' + """ + from markupsafe._constants import HTML_ENTITIES + def handle_match(m): + name = m.group(1) + if name in HTML_ENTITIES: + return unichr(HTML_ENTITIES[name]) + try: + if name[:2] in ('#x', '#X'): + return unichr(int(name[2:], 16)) + elif name.startswith('#'): + return unichr(int(name[1:])) + except ValueError: + pass + # Don't modify unexpected input. + return m.group() + return _entity_re.sub(handle_match, text_type(self)) + + def striptags(self): + r"""Unescape markup into an text_type string and strip all tags. This + also resolves known HTML4 and XHTML entities. Whitespace is + normalized to one: + + >>> Markup("Main » <em>About</em>").striptags() + u'Main \xbb About' + """ + stripped = u' '.join(_striptags_re.sub('', self).split()) + return Markup(stripped).unescape() + + @classmethod + def escape(cls, s): + """Escape the string. Works like :func:`escape` with the difference + that for subclasses of :class:`Markup` this function would return the + correct subclass. + """ + rv = escape(s) + if rv.__class__ is not cls: + return cls(rv) + return rv + + def make_simple_escaping_wrapper(name): + orig = getattr(text_type, name) + def func(self, *args, **kwargs): + args = _escape_argspec(list(args), enumerate(args), self.escape) + _escape_argspec(kwargs, iteritems(kwargs), self.escape) + return self.__class__(orig(self, *args, **kwargs)) + func.__name__ = orig.__name__ + func.__doc__ = orig.__doc__ + return func + + for method in '__getitem__', 'capitalize', \ + 'title', 'lower', 'upper', 'replace', 'ljust', \ + 'rjust', 'lstrip', 'rstrip', 'center', 'strip', \ + 'translate', 'expandtabs', 'swapcase', 'zfill': + locals()[method] = make_simple_escaping_wrapper(method) + + # new in python 2.5 + if hasattr(text_type, 'partition'): + def partition(self, sep): + return tuple(map(self.__class__, + text_type.partition(self, self.escape(sep)))) + def rpartition(self, sep): + return tuple(map(self.__class__, + text_type.rpartition(self, self.escape(sep)))) + + # new in python 2.6 + if hasattr(text_type, 'format'): + def format(*args, **kwargs): + self, args = args[0], args[1:] + formatter = EscapeFormatter(self.escape) + kwargs = _MagicFormatMapping(args, kwargs) + return self.__class__(formatter.vformat(self, args, kwargs)) + + def __html_format__(self, format_spec): + if format_spec: + raise ValueError('Unsupported format specification ' + 'for Markup.') + return self + + # not in python 3 + if hasattr(text_type, '__getslice__'): + __getslice__ = make_simple_escaping_wrapper('__getslice__') + + del method, make_simple_escaping_wrapper + + +class _MagicFormatMapping(Mapping): + """This class implements a dummy wrapper to fix a bug in the Python + standard library for string formatting. + + See http://bugs.python.org/issue13598 for information about why + this is necessary. + """ + + def __init__(self, args, kwargs): + self._args = args + self._kwargs = kwargs + self._last_index = 0 + + def __getitem__(self, key): + if key == '': + idx = self._last_index + self._last_index += 1 + try: + return self._args[idx] + except LookupError: + pass + key = str(idx) + return self._kwargs[key] + + def __iter__(self): + return iter(self._kwargs) + + def __len__(self): + return len(self._kwargs) + + +if hasattr(text_type, 'format'): + class EscapeFormatter(string.Formatter): + + def __init__(self, escape): + self.escape = escape + + def format_field(self, value, format_spec): + if hasattr(value, '__html_format__'): + rv = value.__html_format__(format_spec) + elif hasattr(value, '__html__'): + if format_spec: + raise ValueError('No format specification allowed ' + 'when formatting an object with ' + 'its __html__ method.') + rv = value.__html__() + else: + # We need to make sure the format spec is unicode here as + # otherwise the wrong callback methods are invoked. For + # instance a byte string there would invoke __str__ and + # not __unicode__. + rv = string.Formatter.format_field( + self, value, text_type(format_spec)) + return text_type(self.escape(rv)) + + +def _escape_argspec(obj, iterable, escape): + """Helper for various string-wrapped functions.""" + for key, value in iterable: + if hasattr(value, '__html__') or isinstance(value, string_types): + obj[key] = escape(value) + return obj + + +class _MarkupEscapeHelper(object): + """Helper for Markup.__mod__""" + + def __init__(self, obj, escape): + self.obj = obj + self.escape = escape + + __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x], s.escape) + __unicode__ = __str__ = lambda s: text_type(s.escape(s.obj)) + __repr__ = lambda s: str(s.escape(repr(s.obj))) + __int__ = lambda s: int(s.obj) + __float__ = lambda s: float(s.obj) + + +# we have to import it down here as the speedups and native +# modules imports the markup type which is define above. +try: + from markupsafe._speedups import escape, escape_silent, soft_unicode +except ImportError: + from markupsafe._native import escape, escape_silent, soft_unicode + +if not PY2: + soft_str = soft_unicode + __all__.append('soft_str') diff --git a/deps/v8_inspector/deps/markupsafe/markupsafe/_compat.py b/deps/v8_inspector/deps/markupsafe/markupsafe/_compat.py new file mode 100644 index 00000000000000..62e5632ad8fd53 --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/markupsafe/_compat.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +""" + markupsafe._compat + ~~~~~~~~~~~~~~~~~~ + + Compatibility module for different Python versions. + + :copyright: (c) 2013 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" +import sys + +PY2 = sys.version_info[0] == 2 + +if not PY2: + text_type = str + string_types = (str,) + unichr = chr + int_types = (int,) + iteritems = lambda x: iter(x.items()) +else: + text_type = unicode + string_types = (str, unicode) + unichr = unichr + int_types = (int, long) + iteritems = lambda x: x.iteritems() diff --git a/deps/v8_inspector/deps/markupsafe/markupsafe/_constants.py b/deps/v8_inspector/deps/markupsafe/markupsafe/_constants.py new file mode 100644 index 00000000000000..919bf03c5092e5 --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/markupsafe/_constants.py @@ -0,0 +1,267 @@ +# -*- coding: utf-8 -*- +""" + markupsafe._constants + ~~~~~~~~~~~~~~~~~~~~~ + + Highlevel implementation of the Markup string. + + :copyright: (c) 2010 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" + + +HTML_ENTITIES = { + 'AElig': 198, + 'Aacute': 193, + 'Acirc': 194, + 'Agrave': 192, + 'Alpha': 913, + 'Aring': 197, + 'Atilde': 195, + 'Auml': 196, + 'Beta': 914, + 'Ccedil': 199, + 'Chi': 935, + 'Dagger': 8225, + 'Delta': 916, + 'ETH': 208, + 'Eacute': 201, + 'Ecirc': 202, + 'Egrave': 200, + 'Epsilon': 917, + 'Eta': 919, + 'Euml': 203, + 'Gamma': 915, + 'Iacute': 205, + 'Icirc': 206, + 'Igrave': 204, + 'Iota': 921, + 'Iuml': 207, + 'Kappa': 922, + 'Lambda': 923, + 'Mu': 924, + 'Ntilde': 209, + 'Nu': 925, + 'OElig': 338, + 'Oacute': 211, + 'Ocirc': 212, + 'Ograve': 210, + 'Omega': 937, + 'Omicron': 927, + 'Oslash': 216, + 'Otilde': 213, + 'Ouml': 214, + 'Phi': 934, + 'Pi': 928, + 'Prime': 8243, + 'Psi': 936, + 'Rho': 929, + 'Scaron': 352, + 'Sigma': 931, + 'THORN': 222, + 'Tau': 932, + 'Theta': 920, + 'Uacute': 218, + 'Ucirc': 219, + 'Ugrave': 217, + 'Upsilon': 933, + 'Uuml': 220, + 'Xi': 926, + 'Yacute': 221, + 'Yuml': 376, + 'Zeta': 918, + 'aacute': 225, + 'acirc': 226, + 'acute': 180, + 'aelig': 230, + 'agrave': 224, + 'alefsym': 8501, + 'alpha': 945, + 'amp': 38, + 'and': 8743, + 'ang': 8736, + 'apos': 39, + 'aring': 229, + 'asymp': 8776, + 'atilde': 227, + 'auml': 228, + 'bdquo': 8222, + 'beta': 946, + 'brvbar': 166, + 'bull': 8226, + 'cap': 8745, + 'ccedil': 231, + 'cedil': 184, + 'cent': 162, + 'chi': 967, + 'circ': 710, + 'clubs': 9827, + 'cong': 8773, + 'copy': 169, + 'crarr': 8629, + 'cup': 8746, + 'curren': 164, + 'dArr': 8659, + 'dagger': 8224, + 'darr': 8595, + 'deg': 176, + 'delta': 948, + 'diams': 9830, + 'divide': 247, + 'eacute': 233, + 'ecirc': 234, + 'egrave': 232, + 'empty': 8709, + 'emsp': 8195, + 'ensp': 8194, + 'epsilon': 949, + 'equiv': 8801, + 'eta': 951, + 'eth': 240, + 'euml': 235, + 'euro': 8364, + 'exist': 8707, + 'fnof': 402, + 'forall': 8704, + 'frac12': 189, + 'frac14': 188, + 'frac34': 190, + 'frasl': 8260, + 'gamma': 947, + 'ge': 8805, + 'gt': 62, + 'hArr': 8660, + 'harr': 8596, + 'hearts': 9829, + 'hellip': 8230, + 'iacute': 237, + 'icirc': 238, + 'iexcl': 161, + 'igrave': 236, + 'image': 8465, + 'infin': 8734, + 'int': 8747, + 'iota': 953, + 'iquest': 191, + 'isin': 8712, + 'iuml': 239, + 'kappa': 954, + 'lArr': 8656, + 'lambda': 955, + 'lang': 9001, + 'laquo': 171, + 'larr': 8592, + 'lceil': 8968, + 'ldquo': 8220, + 'le': 8804, + 'lfloor': 8970, + 'lowast': 8727, + 'loz': 9674, + 'lrm': 8206, + 'lsaquo': 8249, + 'lsquo': 8216, + 'lt': 60, + 'macr': 175, + 'mdash': 8212, + 'micro': 181, + 'middot': 183, + 'minus': 8722, + 'mu': 956, + 'nabla': 8711, + 'nbsp': 160, + 'ndash': 8211, + 'ne': 8800, + 'ni': 8715, + 'not': 172, + 'notin': 8713, + 'nsub': 8836, + 'ntilde': 241, + 'nu': 957, + 'oacute': 243, + 'ocirc': 244, + 'oelig': 339, + 'ograve': 242, + 'oline': 8254, + 'omega': 969, + 'omicron': 959, + 'oplus': 8853, + 'or': 8744, + 'ordf': 170, + 'ordm': 186, + 'oslash': 248, + 'otilde': 245, + 'otimes': 8855, + 'ouml': 246, + 'para': 182, + 'part': 8706, + 'permil': 8240, + 'perp': 8869, + 'phi': 966, + 'pi': 960, + 'piv': 982, + 'plusmn': 177, + 'pound': 163, + 'prime': 8242, + 'prod': 8719, + 'prop': 8733, + 'psi': 968, + 'quot': 34, + 'rArr': 8658, + 'radic': 8730, + 'rang': 9002, + 'raquo': 187, + 'rarr': 8594, + 'rceil': 8969, + 'rdquo': 8221, + 'real': 8476, + 'reg': 174, + 'rfloor': 8971, + 'rho': 961, + 'rlm': 8207, + 'rsaquo': 8250, + 'rsquo': 8217, + 'sbquo': 8218, + 'scaron': 353, + 'sdot': 8901, + 'sect': 167, + 'shy': 173, + 'sigma': 963, + 'sigmaf': 962, + 'sim': 8764, + 'spades': 9824, + 'sub': 8834, + 'sube': 8838, + 'sum': 8721, + 'sup': 8835, + 'sup1': 185, + 'sup2': 178, + 'sup3': 179, + 'supe': 8839, + 'szlig': 223, + 'tau': 964, + 'there4': 8756, + 'theta': 952, + 'thetasym': 977, + 'thinsp': 8201, + 'thorn': 254, + 'tilde': 732, + 'times': 215, + 'trade': 8482, + 'uArr': 8657, + 'uacute': 250, + 'uarr': 8593, + 'ucirc': 251, + 'ugrave': 249, + 'uml': 168, + 'upsih': 978, + 'upsilon': 965, + 'uuml': 252, + 'weierp': 8472, + 'xi': 958, + 'yacute': 253, + 'yen': 165, + 'yuml': 255, + 'zeta': 950, + 'zwj': 8205, + 'zwnj': 8204 +} diff --git a/deps/v8_inspector/deps/markupsafe/markupsafe/_native.py b/deps/v8_inspector/deps/markupsafe/markupsafe/_native.py new file mode 100644 index 00000000000000..5e83f10a117c47 --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/markupsafe/_native.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +""" + markupsafe._native + ~~~~~~~~~~~~~~~~~~ + + Native Python implementation the C module is not compiled. + + :copyright: (c) 2010 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" +from markupsafe import Markup +from markupsafe._compat import text_type + + +def escape(s): + """Convert the characters &, <, >, ' and " in string s to HTML-safe + sequences. Use this if you need to display text that might contain + such characters in HTML. Marks return value as markup string. + """ + if hasattr(s, '__html__'): + return s.__html__() + return Markup(text_type(s) + .replace('&', '&') + .replace('>', '>') + .replace('<', '<') + .replace("'", ''') + .replace('"', '"') + ) + + +def escape_silent(s): + """Like :func:`escape` but converts `None` into an empty + markup string. + """ + if s is None: + return Markup() + return escape(s) + + +def soft_unicode(s): + """Make a string unicode if it isn't already. That way a markup + string is not converted back to unicode. + """ + if not isinstance(s, text_type): + s = text_type(s) + return s diff --git a/deps/v8_inspector/deps/markupsafe/markupsafe/_speedups.c b/deps/v8_inspector/deps/markupsafe/markupsafe/_speedups.c new file mode 100644 index 00000000000000..d779a68cc554fa --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/markupsafe/_speedups.c @@ -0,0 +1,239 @@ +/** + * markupsafe._speedups + * ~~~~~~~~~~~~~~~~~~~~ + * + * This module implements functions for automatic escaping in C for better + * performance. + * + * :copyright: (c) 2010 by Armin Ronacher. + * :license: BSD. + */ + +#include <Python.h> + +#define ESCAPED_CHARS_TABLE_SIZE 63 +#define UNICHR(x) (PyUnicode_AS_UNICODE((PyUnicodeObject*)PyUnicode_DecodeASCII(x, strlen(x), NULL))); + +#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) +typedef int Py_ssize_t; +#define PY_SSIZE_T_MAX INT_MAX +#define PY_SSIZE_T_MIN INT_MIN +#endif + + +static PyObject* markup; +static Py_ssize_t escaped_chars_delta_len[ESCAPED_CHARS_TABLE_SIZE]; +static Py_UNICODE *escaped_chars_repl[ESCAPED_CHARS_TABLE_SIZE]; + +static int +init_constants(void) +{ + PyObject *module; + /* mapping of characters to replace */ + escaped_chars_repl['"'] = UNICHR("""); + escaped_chars_repl['\''] = UNICHR("'"); + escaped_chars_repl['&'] = UNICHR("&"); + escaped_chars_repl['<'] = UNICHR("<"); + escaped_chars_repl['>'] = UNICHR(">"); + + /* lengths of those characters when replaced - 1 */ + memset(escaped_chars_delta_len, 0, sizeof (escaped_chars_delta_len)); + escaped_chars_delta_len['"'] = escaped_chars_delta_len['\''] = \ + escaped_chars_delta_len['&'] = 4; + escaped_chars_delta_len['<'] = escaped_chars_delta_len['>'] = 3; + + /* import markup type so that we can mark the return value */ + module = PyImport_ImportModule("markupsafe"); + if (!module) + return 0; + markup = PyObject_GetAttrString(module, "Markup"); + Py_DECREF(module); + + return 1; +} + +static PyObject* +escape_unicode(PyUnicodeObject *in) +{ + PyUnicodeObject *out; + Py_UNICODE *inp = PyUnicode_AS_UNICODE(in); + const Py_UNICODE *inp_end = PyUnicode_AS_UNICODE(in) + PyUnicode_GET_SIZE(in); + Py_UNICODE *next_escp; + Py_UNICODE *outp; + Py_ssize_t delta=0, erepl=0, delta_len=0; + + /* First we need to figure out how long the escaped string will be */ + while (*(inp) || inp < inp_end) { + if (*inp < ESCAPED_CHARS_TABLE_SIZE) { + delta += escaped_chars_delta_len[*inp]; + erepl += !!escaped_chars_delta_len[*inp]; + } + ++inp; + } + + /* Do we need to escape anything at all? */ + if (!erepl) { + Py_INCREF(in); + return (PyObject*)in; + } + + out = (PyUnicodeObject*)PyUnicode_FromUnicode(NULL, PyUnicode_GET_SIZE(in) + delta); + if (!out) + return NULL; + + outp = PyUnicode_AS_UNICODE(out); + inp = PyUnicode_AS_UNICODE(in); + while (erepl-- > 0) { + /* look for the next substitution */ + next_escp = inp; + while (next_escp < inp_end) { + if (*next_escp < ESCAPED_CHARS_TABLE_SIZE && + (delta_len = escaped_chars_delta_len[*next_escp])) { + ++delta_len; + break; + } + ++next_escp; + } + + if (next_escp > inp) { + /* copy unescaped chars between inp and next_escp */ + Py_UNICODE_COPY(outp, inp, next_escp-inp); + outp += next_escp - inp; + } + + /* escape 'next_escp' */ + Py_UNICODE_COPY(outp, escaped_chars_repl[*next_escp], delta_len); + outp += delta_len; + + inp = next_escp + 1; + } + if (inp < inp_end) + Py_UNICODE_COPY(outp, inp, PyUnicode_GET_SIZE(in) - (inp - PyUnicode_AS_UNICODE(in))); + + return (PyObject*)out; +} + + +static PyObject* +escape(PyObject *self, PyObject *text) +{ + PyObject *s = NULL, *rv = NULL, *html; + + /* we don't have to escape integers, bools or floats */ + if (PyLong_CheckExact(text) || +#if PY_MAJOR_VERSION < 3 + PyInt_CheckExact(text) || +#endif + PyFloat_CheckExact(text) || PyBool_Check(text) || + text == Py_None) + return PyObject_CallFunctionObjArgs(markup, text, NULL); + + /* if the object has an __html__ method that performs the escaping */ + html = PyObject_GetAttrString(text, "__html__"); + if (html) { + rv = PyObject_CallObject(html, NULL); + Py_DECREF(html); + return rv; + } + + /* otherwise make the object unicode if it isn't, then escape */ + PyErr_Clear(); + if (!PyUnicode_Check(text)) { +#if PY_MAJOR_VERSION < 3 + PyObject *unicode = PyObject_Unicode(text); +#else + PyObject *unicode = PyObject_Str(text); +#endif + if (!unicode) + return NULL; + s = escape_unicode((PyUnicodeObject*)unicode); + Py_DECREF(unicode); + } + else + s = escape_unicode((PyUnicodeObject*)text); + + /* convert the unicode string into a markup object. */ + rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL); + Py_DECREF(s); + return rv; +} + + +static PyObject* +escape_silent(PyObject *self, PyObject *text) +{ + if (text != Py_None) + return escape(self, text); + return PyObject_CallFunctionObjArgs(markup, NULL); +} + + +static PyObject* +soft_unicode(PyObject *self, PyObject *s) +{ + if (!PyUnicode_Check(s)) +#if PY_MAJOR_VERSION < 3 + return PyObject_Unicode(s); +#else + return PyObject_Str(s); +#endif + Py_INCREF(s); + return s; +} + + +static PyMethodDef module_methods[] = { + {"escape", (PyCFunction)escape, METH_O, + "escape(s) -> markup\n\n" + "Convert the characters &, <, >, ', and \" in string s to HTML-safe\n" + "sequences. Use this if you need to display text that might contain\n" + "such characters in HTML. Marks return value as markup string."}, + {"escape_silent", (PyCFunction)escape_silent, METH_O, + "escape_silent(s) -> markup\n\n" + "Like escape but converts None to an empty string."}, + {"soft_unicode", (PyCFunction)soft_unicode, METH_O, + "soft_unicode(object) -> string\n\n" + "Make a string unicode if it isn't already. That way a markup\n" + "string is not converted back to unicode."}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + + +#if PY_MAJOR_VERSION < 3 + +#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ +#define PyMODINIT_FUNC void +#endif +PyMODINIT_FUNC +init_speedups(void) +{ + if (!init_constants()) + return; + + Py_InitModule3("markupsafe._speedups", module_methods, ""); +} + +#else /* Python 3.x module initialization */ + +static struct PyModuleDef module_definition = { + PyModuleDef_HEAD_INIT, + "markupsafe._speedups", + NULL, + -1, + module_methods, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit__speedups(void) +{ + if (!init_constants()) + return NULL; + + return PyModule_Create(&module_definition); +} + +#endif diff --git a/deps/v8_inspector/deps/markupsafe/markupsafe/tests.py b/deps/v8_inspector/deps/markupsafe/markupsafe/tests.py new file mode 100644 index 00000000000000..f3e2379e0c8341 --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/markupsafe/tests.py @@ -0,0 +1,207 @@ +# -*- coding: utf-8 -*- +import gc +import sys +import unittest +from markupsafe import Markup, escape, escape_silent +from markupsafe._compat import text_type, PY2 + + +class MarkupTestCase(unittest.TestCase): + + def test_adding(self): + # adding two strings should escape the unsafe one + unsafe = '<script type="application/x-some-script">alert("foo");</script>' + safe = Markup('<em>username</em>') + assert unsafe + safe == text_type(escape(unsafe)) + text_type(safe) + + def test_string_interpolation(self): + # string interpolations are safe to use too + assert Markup('<em>%s</em>') % '<bad user>' == \ + '<em><bad user></em>' + assert Markup('<em>%(username)s</em>') % { + 'username': '<bad user>' + } == '<em><bad user></em>' + + assert Markup('%i') % 3.14 == '3' + assert Markup('%.2f') % 3.14 == '3.14' + + def test_type_behavior(self): + # an escaped object is markup too + assert type(Markup('foo') + 'bar') is Markup + + # and it implements __html__ by returning itself + x = Markup("foo") + assert x.__html__() is x + + def test_html_interop(self): + # it also knows how to treat __html__ objects + class Foo(object): + def __html__(self): + return '<em>awesome</em>' + def __unicode__(self): + return 'awesome' + __str__ = __unicode__ + assert Markup(Foo()) == '<em>awesome</em>' + assert Markup('<strong>%s</strong>') % Foo() == \ + '<strong><em>awesome</em></strong>' + + def test_tuple_interpol(self): + self.assertEqual(Markup('<em>%s:%s</em>') % ( + '<foo>', + '<bar>', + ), Markup(u'<em><foo>:<bar></em>')) + + def test_dict_interpol(self): + self.assertEqual(Markup('<em>%(foo)s</em>') % { + 'foo': '<foo>', + }, Markup(u'<em><foo></em>')) + self.assertEqual(Markup('<em>%(foo)s:%(bar)s</em>') % { + 'foo': '<foo>', + 'bar': '<bar>', + }, Markup(u'<em><foo>:<bar></em>')) + + def test_escaping(self): + # escaping + assert escape('"<>&\'') == '"<>&'' + assert Markup("<em>Foo & Bar</em>").striptags() == "Foo & Bar" + + def test_unescape(self): + assert Markup("<test>").unescape() == "<test>" + assert "jack & tavi are cooler than mike & russ" == \ + Markup("jack & tavi are cooler than mike & russ").unescape(), \ + Markup("jack & tavi are cooler than mike & russ").unescape() + + # Test that unescape is idempotent + original = '&foo;' + once = Markup(original).unescape() + twice = Markup(once).unescape() + expected = "&foo;" + assert expected == once == twice, (once, twice) + + def test_formatting(self): + for actual, expected in ( + (Markup('%i') % 3.14, '3'), + (Markup('%.2f') % 3.14159, '3.14'), + (Markup('%s %s %s') % ('<', 123, '>'), '< 123 >'), + (Markup('<em>{awesome}</em>').format(awesome='<awesome>'), + '<em><awesome></em>'), + (Markup('{0[1][bar]}').format([0, {'bar': '<bar/>'}]), + '<bar/>'), + (Markup('{0[1][bar]}').format([0, {'bar': Markup('<bar/>')}]), + '<bar/>')): + assert actual == expected, "%r should be %r!" % (actual, expected) + + # This is new in 2.7 + if sys.version_info >= (2, 7): + def test_formatting_empty(self): + formatted = Markup('{}').format(0) + assert formatted == Markup('0') + + def test_custom_formatting(self): + class HasHTMLOnly(object): + def __html__(self): + return Markup('<foo>') + + class HasHTMLAndFormat(object): + def __html__(self): + return Markup('<foo>') + def __html_format__(self, spec): + return Markup('<FORMAT>') + + assert Markup('{0}').format(HasHTMLOnly()) == Markup('<foo>') + assert Markup('{0}').format(HasHTMLAndFormat()) == Markup('<FORMAT>') + + def test_complex_custom_formatting(self): + class User(object): + def __init__(self, id, username): + self.id = id + self.username = username + def __html_format__(self, format_spec): + if format_spec == 'link': + return Markup('<a href="/user/{0}">{1}</a>').format( + self.id, + self.__html__(), + ) + elif format_spec: + raise ValueError('Invalid format spec') + return self.__html__() + def __html__(self): + return Markup('<span class=user>{0}</span>').format(self.username) + + user = User(1, 'foo') + assert Markup('<p>User: {0:link}').format(user) == \ + Markup('<p>User: <a href="/user/1"><span class=user>foo</span></a>') + + def test_formatting_with_objects(self): + class Stringable(object): + def __unicode__(self): + return u'строка' + if PY2: + def __str__(self): + return 'some other value' + else: + __str__ = __unicode__ + + assert Markup('{s}').format(s=Stringable()) == \ + Markup(u'строка') + + def test_all_set(self): + import markupsafe as markup + for item in markup.__all__: + getattr(markup, item) + + def test_escape_silent(self): + assert escape_silent(None) == Markup() + assert escape(None) == Markup(None) + assert escape_silent('<foo>') == Markup(u'<foo>') + + def test_splitting(self): + self.assertEqual(Markup('a b').split(), [ + Markup('a'), + Markup('b') + ]) + self.assertEqual(Markup('a b').rsplit(), [ + Markup('a'), + Markup('b') + ]) + self.assertEqual(Markup('a\nb').splitlines(), [ + Markup('a'), + Markup('b') + ]) + + def test_mul(self): + self.assertEqual(Markup('a') * 3, Markup('aaa')) + + +class MarkupLeakTestCase(unittest.TestCase): + + def test_markup_leaks(self): + counts = set() + for count in range(20): + for item in range(1000): + escape("foo") + escape("<foo>") + escape(u"foo") + escape(u"<foo>") + if hasattr(sys, 'pypy_version_info'): + gc.collect() + counts.add(len(gc.get_objects())) + assert len(counts) == 1, 'ouch, c extension seems to ' \ + 'leak objects, got: ' + str(len(counts)) + + +def suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(MarkupTestCase)) + + # this test only tests the c extension + if not hasattr(escape, 'func_code'): + suite.addTest(unittest.makeSuite(MarkupLeakTestCase)) + + return suite + + +if __name__ == '__main__': + unittest.main(defaultTest='suite') + +# vim:sts=4:sw=4:et: diff --git a/deps/v8_inspector/deps/markupsafe/setup.py b/deps/v8_inspector/deps/markupsafe/setup.py new file mode 100644 index 00000000000000..aff786d5e95301 --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/setup.py @@ -0,0 +1,133 @@ +import os +import re +import ast +import sys +from setuptools import setup, Extension, Feature +from distutils.command.build_ext import build_ext +from distutils.errors import CCompilerError, DistutilsExecError, \ + DistutilsPlatformError + + +# fail safe compilation shamelessly stolen from the simplejson +# setup.py file. Original author: Bob Ippolito + +is_jython = 'java' in sys.platform +is_pypy = hasattr(sys, 'pypy_version_info') + +with open('markupsafe/__init__.py') as f: + version = ast.literal_eval(re.search( + '^__version__\s+=\s+(.*?)$(?sm)', f.read()).group(1)) + + +speedups = Feature( + 'optional C speed-enhancement module', + standard=True, + ext_modules=[ + Extension('markupsafe._speedups', ['markupsafe/_speedups.c']), + ], +) + +# Known errors when running build_ext.build_extension method +ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError) +if sys.platform == 'win32' and sys.version_info > (2, 6): + # 2.6's distutils.msvc9compiler can raise an IOError when failing to + # find the compiler + ext_errors += (IOError,) +# Known errors when running build_ext.run method +run_errors = (DistutilsPlatformError,) +if sys.platform == 'darwin': + run_errors += (SystemError,) + + +class BuildFailed(Exception): + pass + + +class ve_build_ext(build_ext): + """This class allows C extension building to fail.""" + + def run(self): + try: + build_ext.run(self) + except run_errors: + raise BuildFailed() + + def build_extension(self, ext): + try: + build_ext.build_extension(self, ext) + except ext_errors: + raise BuildFailed() + except ValueError: + # this can happen on Windows 64 bit, see Python issue 7511 + if "'path'" in str(sys.exc_info()[1]): # works with Python 2 and 3 + raise BuildFailed() + raise + + +def echo(msg=''): + sys.stdout.write(msg + '\n') + + +readme = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read() + + +def run_setup(with_binary): + features = {} + if with_binary: + features['speedups'] = speedups + setup( + name='MarkupSafe', + version=version, + url='http://github.com/mitsuhiko/markupsafe', + license='BSD', + author='Armin Ronacher', + author_email='armin.ronacher@active-4.com', + description='Implements a XML/HTML/XHTML Markup safe string for Python', + long_description=readme, + zip_safe=False, + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Environment :: Web Environment', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', + 'Topic :: Software Development :: Libraries :: Python Modules', + 'Topic :: Text Processing :: Markup :: HTML' + ], + packages=['markupsafe'], + test_suite='markupsafe.tests.suite', + include_package_data=True, + cmdclass={'build_ext': ve_build_ext}, + features=features, + ) + + +def try_building_extension(): + try: + run_setup(True) + except BuildFailed: + LINE = '=' * 74 + BUILD_EXT_WARNING = 'WARNING: The C extension could not be ' \ + 'compiled, speedups are not enabled.' + + echo(LINE) + echo(BUILD_EXT_WARNING) + echo('Failure information, if any, is above.') + echo('Retrying the build without the C extension now.') + echo() + + run_setup(False) + + echo(LINE) + echo(BUILD_EXT_WARNING) + echo('Plain-Python installation succeeded.') + echo(LINE) + + +if not (is_pypy or is_jython): + try_building_extension() +else: + run_setup(False) diff --git a/deps/v8_inspector/deps/markupsafe/tox.ini b/deps/v8_inspector/deps/markupsafe/tox.ini new file mode 100644 index 00000000000000..9b854eb2bf91d9 --- /dev/null +++ b/deps/v8_inspector/deps/markupsafe/tox.ini @@ -0,0 +1,5 @@ +[tox] +envlist = py26, py27, pypy, py33, py34, py35 + +[testenv] +commands = python setup.py test diff --git a/deps/v8_inspector/deps/wtf/README.md b/deps/v8_inspector/deps/wtf/README.md new file mode 100644 index 00000000000000..6fe75ce24cd74a --- /dev/null +++ b/deps/v8_inspector/deps/wtf/README.md @@ -0,0 +1 @@ +# wtf diff --git a/deps/v8_inspector/deps/wtf/wtf/Assertions.h b/deps/v8_inspector/deps/wtf/wtf/Assertions.h new file mode 100644 index 00000000000000..18f21cacb740f0 --- /dev/null +++ b/deps/v8_inspector/deps/wtf/wtf/Assertions.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2013 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_Assertions_h +#define WTF_Assertions_h + +#ifndef CHECK +#define CHECK(condition) ((void) 0) +#endif +#define DCHECK(condition) ((void) 0) +#define NOTREACHED() +#define DCHECK_GE(i, j) DCHECK(i >= j) + +template <typename T> +inline void USE(T) { } + +#endif /* WTF_Assertions_h */ diff --git a/deps/v8_inspector/deps/wtf/wtf/Compiler.h b/deps/v8_inspector/deps/wtf/wtf/Compiler.h new file mode 100644 index 00000000000000..61926a054fa1b9 --- /dev/null +++ b/deps/v8_inspector/deps/wtf/wtf/Compiler.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2011, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_Compiler_h +#define WTF_Compiler_h + +/* COMPILER() - the compiler being used to build the project */ +#define COMPILER(WTF_FEATURE) (defined WTF_COMPILER_##WTF_FEATURE && WTF_COMPILER_##WTF_FEATURE) + +/* ==== COMPILER() - the compiler being used to build the project ==== */ + +/* COMPILER(CLANG) - Clang */ +#if defined(__clang__) +#define WTF_COMPILER_CLANG 1 +#endif + +/* COMPILER(MSVC) - Microsoft Visual C++ (and Clang when compiling for Windows). */ +#if defined(_MSC_VER) +#define WTF_COMPILER_MSVC 1 +#endif + +/* COMPILER(GCC) - GNU Compiler Collection (and Clang when compiling for platforms other than Windows). */ +#if defined(__GNUC__) +#define WTF_COMPILER_GCC 1 +#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#define GCC_VERSION_AT_LEAST(major, minor, patch) (GCC_VERSION >= (major * 10000 + minor * 100 + patch)) +#else +/* Define this for !GCC compilers, just so we can write things like GCC_VERSION_AT_LEAST(4, 1, 0). */ +#define GCC_VERSION_AT_LEAST(major, minor, patch) 0 +#endif + +#endif /* WTF_Compiler_h */ diff --git a/deps/v8_inspector/deps/wtf/wtf/PtrUtil.h b/deps/v8_inspector/deps/wtf/wtf/PtrUtil.h new file mode 100644 index 00000000000000..0d5a7fdd810141 --- /dev/null +++ b/deps/v8_inspector/deps/wtf/wtf/PtrUtil.h @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef WTF_PtrUtil_h +#define WTF_PtrUtil_h + +#include "wtf/Assertions.h" +#include <memory> + +#if defined(__APPLE__) && !defined(_LIBCPP_VERSION) + +namespace std { + +template <typename T1, typename T2> +struct is_convertible +{ +private: + struct True_ { char x[2]; }; + struct False_ { }; + + static True_ helper(T2 const &); + static False_ helper(...); + +public: + static bool const value = ( + sizeof(True_) == sizeof(is_convertible::helper(T1())) + ); +}; + +template <bool, class T = void> +struct enable_if {}; + +template <class T> +struct enable_if<true, T> +{ + typedef T type; +}; + +template<class T> +struct remove_extent { typedef T type; }; + +template<class T> +struct remove_extent<T[]> { typedef T type; }; + +template<class T, std::size_t N> +struct remove_extent<T[N]> { typedef T type;}; + +typedef decltype(nullptr) nullptr_t; + +template<class T, T v> +struct integral_constant { + static constexpr T value = v; + typedef T value_type; + typedef integral_constant type; + constexpr operator value_type() const noexcept { return value; } + constexpr value_type operator()() const noexcept { return value; } //since c++14 +}; + +typedef integral_constant<bool, true> true_type; +typedef integral_constant<bool, false> false_type; + +template<class T> +struct is_array : false_type {}; + +template<class T> +struct is_array<T[]> : true_type {}; + +template<class T, std::size_t N> +struct is_array<T[N]> : true_type {}; + +template <typename T> +struct OwnedPtrDeleter { + static void deletePtr(T* ptr) + { + static_assert(sizeof(T) > 0, "type must be complete"); + delete ptr; + } +}; + +template <typename T> +struct OwnedPtrDeleter<T[]> { + static void deletePtr(T* ptr) + { + static_assert(sizeof(T) > 0, "type must be complete"); + delete[] ptr; + } +}; + +template <class T, int n> +struct OwnedPtrDeleter<T[n]> { + static_assert(sizeof(T) < 0, "do not use array with size as type"); +}; + + +template <typename T> class unique_ptr { +public: + typedef typename remove_extent<T>::type ValueType; + typedef ValueType* PtrType; + + unique_ptr() : m_ptr(nullptr) {} + unique_ptr(std::nullptr_t) : m_ptr(nullptr) {} + unique_ptr(unique_ptr&&); + template <typename U, typename = typename enable_if<is_convertible<U*, T*>::value>::type> unique_ptr(unique_ptr<U>&&); + + ~unique_ptr() + { + OwnedPtrDeleter<T>::deletePtr(m_ptr); + m_ptr = nullptr; + } + + PtrType get() const { return m_ptr; } + + void reset(PtrType ptr = nullptr); + PtrType release(); + + ValueType& operator*() const { DCHECK(m_ptr); return *m_ptr; } + PtrType operator->() const { DCHECK(m_ptr); return m_ptr; } + + ValueType& operator[](std::ptrdiff_t i) const; + + bool operator!() const { return !m_ptr; } + explicit operator bool() const { return m_ptr; } + + unique_ptr& operator=(std::nullptr_t) { reset(); return *this; } + + + unique_ptr& operator=(unique_ptr&&); + template <typename U> unique_ptr& operator=(unique_ptr<U>&&); + + void swap(unique_ptr& o) { std::swap(m_ptr, o.m_ptr); } + + static T* hashTableDeletedValue() { return reinterpret_cast<T*>(-1); } + + explicit unique_ptr(PtrType ptr) : m_ptr(ptr) {} + +private: + + // We should never have two unique_ptrs for the same underlying object + // (otherwise we'll get double-destruction), so these equality operators + // should never be needed. + template <typename U> bool operator==(const unique_ptr<U>&) const + { + static_assert(!sizeof(U*), "unique_ptrs should never be equal"); + return false; + } + template <typename U> bool operator!=(const unique_ptr<U>&) const + { + static_assert(!sizeof(U*), "unique_ptrs should never be equal"); + return false; + } + + PtrType m_ptr; +}; + + +template <typename T> inline void unique_ptr<T>::reset(PtrType ptr) +{ + PtrType p = m_ptr; + m_ptr = ptr; + OwnedPtrDeleter<T>::deletePtr(p); +} + +template <typename T> inline typename unique_ptr<T>::PtrType unique_ptr<T>::release() +{ + PtrType ptr = m_ptr; + m_ptr = nullptr; + return ptr; +} + +template <typename T> inline typename unique_ptr<T>::ValueType& unique_ptr<T>::operator[](std::ptrdiff_t i) const +{ + static_assert(is_array<T>::value, "elements access is possible for arrays only"); + DCHECK(m_ptr); + DCHECK(i >= 0); + return m_ptr[i]; +} + +template <typename T> inline unique_ptr<T>::unique_ptr(unique_ptr<T>&& o) + : m_ptr(o.release()) +{ +} + +template <typename T> +template <typename U, typename> inline unique_ptr<T>::unique_ptr(unique_ptr<U>&& o) + : m_ptr(o.release()) +{ + static_assert(!is_array<T>::value, "pointers to array must never be converted"); +} + +template <typename T> inline unique_ptr<T>& unique_ptr<T>::operator=(unique_ptr<T>&& o) +{ + PtrType ptr = m_ptr; + m_ptr = o.release(); + DCHECK(!ptr || m_ptr != ptr); + OwnedPtrDeleter<T>::deletePtr(ptr); + + return *this; +} + +template <typename T> +template <typename U> inline unique_ptr<T>& unique_ptr<T>::operator=(unique_ptr<U>&& o) +{ + static_assert(!is_array<T>::value, "pointers to array must never be converted"); + PtrType ptr = m_ptr; + m_ptr = o.release(); + DCHECK(!ptr || m_ptr != ptr); + OwnedPtrDeleter<T>::deletePtr(ptr); + + return *this; +} + +template <typename T> inline void swap(unique_ptr<T>& a, unique_ptr<T>& b) +{ + a.swap(b); +} + +template <typename T, typename U> inline bool operator==(const unique_ptr<T>& a, U* b) +{ + return a.get() == b; +} + +template <typename T, typename U> inline bool operator==(T* a, const unique_ptr<U>& b) +{ + return a == b.get(); +} + +template <typename T, typename U> inline bool operator!=(const unique_ptr<T>& a, U* b) +{ + return a.get() != b; +} + +template <typename T, typename U> inline bool operator!=(T* a, const unique_ptr<U>& b) +{ + return a != b.get(); +} + +template <typename T> inline typename unique_ptr<T>::PtrType getPtr(const unique_ptr<T>& p) +{ + return p.get(); +} + +template <typename T> +unique_ptr<T> move(unique_ptr<T>& ptr) +{ + return unique_ptr<T>(ptr.release()); +} + +} + +#endif + +template <typename T> +std::unique_ptr<T> wrapUnique(T* ptr) { + return std::unique_ptr<T>(ptr); +} + +#endif // WTF_PtrUtil_h diff --git a/deps/v8_inspector/devtools/Inspector-1.1.json b/deps/v8_inspector/devtools/Inspector-1.1.json new file mode 100644 index 00000000000000..55afa73e9df34b --- /dev/null +++ b/deps/v8_inspector/devtools/Inspector-1.1.json @@ -0,0 +1,3924 @@ +{ + "version": { "major": "1", "minor": "1" }, + "domains": [{ + "domain": "Inspector", + "hidden": true, + "types": [], + "commands": [ + { + "name": "enable", + "description": "Enables inspector domain notifications." + }, + { + "name": "disable", + "description": "Disables inspector domain notifications." + }, + { + "name": "reset", + "description": "Resets all domains." + } + ], + "events": [ + { + "name": "evaluateForTestInFrontend", + "parameters": [ + { "name": "testCallId", "type": "integer" }, + { "name": "script", "type": "string" } + ] + }, + { + "name": "inspect", + "parameters": [ + { "name": "object", "$ref": "Runtime.RemoteObject" }, + { "name": "hints", "type": "object" } + ] + }, + { + "name": "detached", + "description": "Fired when remote debugging connection is about to be terminated. Contains detach reason.", + "parameters": [ + { "name": "reason", "type": "string", "description": "The reason why connection has been terminated." } + ] + }, + { + "name": "targetCrashed", + "description": "Fired when debugging target has crashed" + } + ] + }, + { + "domain": "Memory", + "hidden": true, + "types": [ + { + "id": "MemoryBlock", + "type": "object", + "properties": [ + { "name": "size", "type": "number", "optional": true, "description": "Size of the block in bytes if available" }, + { "name": "name", "type": "string", "description": "Unique name used to identify the component that allocated this block" }, + { "name": "children", "type": "array", "optional": true, "items": { "$ref": "MemoryBlock" }} + ] + }, + { + "id": "HeapSnapshotChunk", + "type": "object", + "properties": [ + { "name": "strings", "type": "array", "items": { "type": "string" }, "description": "An array of strings that were found since last update." }, + { "name": "nodes", "type": "array", "items": { "type": "integer" }, "description": "An array of nodes that were found since last update." }, + { "name": "edges", "type": "array", "items": { "type": "integer" }, "description": "An array of edges that were found since last update." }, + { "name": "baseToRealNodeId", "type": "array", "items": { "type": "integer" }, "description": "An array of integers for nodeId remapping. Even nodeId has to be mapped to the following odd nodeId." } + ] + } + ], + "commands": [ + { + "name": "getDOMCounters", + "returns": [ + { "name": "documents", "type": "integer" }, + { "name": "nodes", "type": "integer" }, + { "name": "jsEventListeners", "type": "integer" } + ] + } + ], + "events": [ + { + "name": "addNativeSnapshotChunk", + "parameters": [ + { "name": "chunk", "$ref": "HeapSnapshotChunk", "description": "A chunk of the serialized the snapshot." } + ] + } + ] + }, + { + "domain": "Page", + "description": "Actions and events related to the inspected page belong to the page domain.", + "types": [ + { + "id": "ResourceType", + "type": "string", + "enum": ["Document", "Stylesheet", "Image", "Font", "Script", "XHR", "WebSocket", "Other"], + "description": "Resource type as it was perceived by the rendering engine." + }, + { + "id": "FrameId", + "type": "string", + "description": "Unique frame identifier." + }, + { + "id": "Frame", + "type": "object", + "description": "Information about the Frame on the page.", + "properties": [ + { "name": "id", "type": "string", "description": "Frame unique identifier." }, + { "name": "parentId", "type": "string", "optional": true, "description": "Parent frame identifier." }, + { "name": "loaderId", "$ref": "Network.LoaderId", "description": "Identifier of the loader associated with this frame." }, + { "name": "name", "type": "string", "optional": true, "description": "Frame's name as specified in the tag." }, + { "name": "url", "type": "string", "description": "Frame document's URL." }, + { "name": "securityOrigin", "type": "string", "description": "Frame document's security origin." }, + { "name": "mimeType", "type": "string", "description": "Frame document's mimeType as determined by the browser." } + ] + }, + { + "id": "FrameResourceTree", + "type": "object", + "description": "Information about the Frame hierarchy along with their cached resources.", + "properties": [ + { "name": "frame", "$ref": "Frame", "description": "Frame information for this tree item." }, + { "name": "childFrames", "type": "array", "optional": true, "items": { "$ref": "FrameResourceTree" }, "description": "Child frames." }, + { "name": "resources", "type": "array", + "items": { + "type": "object", + "properties": [ + { "name": "url", "type": "string", "description": "Resource URL." }, + { "name": "type", "$ref": "ResourceType", "description": "Type of this resource." }, + { "name": "mimeType", "type": "string", "description": "Resource mimeType as determined by the browser." }, + { "name": "failed", "type": "boolean", "optional": true, "description": "True if the resource failed to load." }, + { "name": "canceled", "type": "boolean", "optional": true, "description": "True if the resource was canceled during loading." } + ] + }, + "description": "Information about frame resources." + } + ], + "hidden": true + }, + { + "id": "SearchMatch", + "type": "object", + "description": "Search match for resource.", + "properties": [ + { "name": "lineNumber", "type": "number", "description": "Line number in resource content." }, + { "name": "lineContent", "type": "string", "description": "Line with match content." } + ], + "hidden": true + }, + { + "id": "SearchResult", + "type": "object", + "description": "Search result for resource.", + "properties": [ + { "name": "url", "type": "string", "description": "Resource URL." }, + { "name": "frameId", "$ref": "FrameId", "description": "Resource frame id." }, + { "name": "matchesCount", "type": "number", "description": "Number of matches in the resource content." } + ], + "hidden": true + }, + { + "id": "Cookie", + "type": "object", + "description": "Cookie object", + "properties": [ + { "name": "name", "type": "string", "description": "Cookie name." }, + { "name": "value", "type": "string", "description": "Cookie value." }, + { "name": "domain", "type": "string", "description": "Cookie domain." }, + { "name": "path", "type": "string", "description": "Cookie path." }, + { "name": "expires", "type": "number", "description": "Cookie expires." }, + { "name": "size", "type": "integer", "description": "Cookie size." }, + { "name": "httpOnly", "type": "boolean", "description": "True if cookie is http-only." }, + { "name": "secure", "type": "boolean", "description": "True if cookie is secure." }, + { "name": "session", "type": "boolean", "description": "True in case of session cookie." } + ], + "hidden": true + }, + { + "id": "ScriptIdentifier", + "type": "string", + "description": "Unique script identifier.", + "hidden": true + }, + { + "id": "NavigationEntry", + "type": "object", + "description": "Navigation history entry.", + "properties": [ + { "name": "id", "type": "integer", "description": "Unique id of the navigation history entry." }, + { "name": "url", "type": "string", "description": "URL of the navigation history entry." }, + { "name": "title", "type": "string", "description": "Title of the navigation history entry." } + ], + "hidden": true + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables page domain notifications." + }, + { + "name": "disable", + "description": "Disables page domain notifications." + }, + { + "name": "addScriptToEvaluateOnLoad", + "parameters": [ + { "name": "scriptSource", "type": "string" } + ], + "returns": [ + { "name": "identifier", "$ref": "ScriptIdentifier", "description": "Identifier of the added script." } + ], + "hidden": true + }, + { + "name": "removeScriptToEvaluateOnLoad", + "parameters": [ + { "name": "identifier", "$ref": "ScriptIdentifier" } + ], + "hidden": true + }, + { + "name": "reload", + "parameters": [ + { "name": "ignoreCache", "type": "boolean", "optional": true, "description": "If true, browser cache is ignored (as if the user pressed Shift+refresh)." }, + { "name": "scriptToEvaluateOnLoad", "type": "string", "optional": true, "description": "If set, the script will be injected into all frames of the inspected page after reload." }, + { "name": "scriptPreprocessor", "type": "string", "optional": true, "description": "Script body that should evaluate to function that will preprocess all the scripts before their compilation.", "hidden": true } + ], + "description": "Reloads given page optionally ignoring the cache." + }, + { + "name": "navigate", + "parameters": [ + { "name": "url", "type": "string", "description": "URL to navigate the page to." } + ], + "description": "Navigates current page to the given URL." + }, + { + "name": "getNavigationHistory", + "parameters": [], + "returns": [ + { "name": "currentIndex", "type": "integer", "description": "Index of the current navigation history entry." }, + { "name": "entries", "type": "array", "items": { "$ref": "NavigationEntry"}, "description": "Array of navigation history entries." } + ], + "description": "Returns navigation history for the current page.", + "hidden": true + }, + { + "name": "navigateToHistoryEntry", + "parameters": [ + { "name": "entryId", "type": "integer", "description": "Unique id of the entry to navigate to." } + ], + "description": "Navigates current page to the given history entry.", + "hidden": true + }, + { + "name": "getCookies", + "returns": [ + { "name": "cookies", "type": "array", "items": { "$ref": "Cookie"}, "description": "Array of cookie objects." }, + { "name": "cookiesString", "type": "string", "description": "document.cookie string representation of the cookies." } + ], + "description": "Returns all browser cookies. Depending on the backend support, will either return detailed cookie information in the <code>cookie</code> field or string cookie representation using <code>cookieString</code>.", + "hidden": true + }, + { + "name": "deleteCookie", + "parameters": [ + { "name": "cookieName", "type": "string", "description": "Name of the cookie to remove." }, + { "name": "url", "type": "string", "description": "URL to match cooke domain and path." } + ], + "description": "Deletes browser cookie with given name, domain and path.", + "hidden": true + }, + { + "name": "getResourceTree", + "description": "Returns present frame / resource tree structure.", + "returns": [ + { "name": "frameTree", "$ref": "FrameResourceTree", "description": "Present frame / resource tree structure." } + ], + "hidden": true + }, + { + "name": "getResourceContent", + "description": "Returns content of the given resource.", + "parameters": [ + { "name": "frameId", "$ref": "FrameId", "description": "Frame id to get resource for." }, + { "name": "url", "type": "string", "description": "URL of the resource to get content for." } + ], + "returns": [ + { "name": "content", "type": "string", "description": "Resource content." }, + { "name": "base64Encoded", "type": "boolean", "description": "True, if content was served as base64." } + ], + "hidden": true + }, + { + "name": "searchInResource", + "description": "Searches for given string in resource content.", + "parameters": [ + { "name": "frameId", "$ref": "FrameId", "description": "Frame id for resource to search in." }, + { "name": "url", "type": "string", "description": "URL of the resource to search in." }, + { "name": "query", "type": "string", "description": "String to search for." }, + { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." }, + { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." } + ], + "returns": [ + { "name": "result", "type": "array", "items": { "$ref": "SearchMatch" }, "description": "List of search matches." } + ], + "hidden": true + }, + { + "name": "searchInResources", + "description": "Searches for given string in frame / resource tree structure.", + "parameters": [ + { "name": "text", "type": "string", "description": "String to search for." }, + { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." }, + { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." } + ], + "returns": [ + { "name": "result", "type": "array", "items": { "$ref": "SearchResult" }, "description": "List of search results." } + ], + "hidden": true + }, + { + "name": "setDocumentContent", + "description": "Sets given markup as the document's HTML.", + "parameters": [ + { "name": "frameId", "$ref": "FrameId", "description": "Frame id to set HTML for." }, + { "name": "html", "type": "string", "description": "HTML content to set." } + ], + "hidden": true + }, + { + "name": "setDeviceMetricsOverride", + "description": "Overrides the values of device screen dimensions (window.screen.width, window.screen.height, window.innerWidth, window.innerHeight, and \"device-width\"/\"device-height\"-related CSS media query results) and the font scale factor.", + "parameters": [ + { "name": "width", "type": "integer", "description": "Overriding width value in pixels (minimum 0, maximum 10000000). 0 disables the override." }, + { "name": "height", "type": "integer", "description": "Overriding height value in pixels (minimum 0, maximum 10000000). 0 disables the override." }, + { "name": "fontScaleFactor", "type": "number", "description": "Overriding font scale factor value (must be positive). 1 disables the override." }, + { "name": "fitWindow", "type": "boolean", "description": "Whether a view that exceeds the available browser window area should be scaled down to fit." } + ], + "hidden": true + }, + { + "name": "setShowPaintRects", + "description": "Requests that backend shows paint rectangles", + "parameters": [ + { "name": "result", "type": "boolean", "description": "True for showing paint rectangles" } + ], + "hidden": true + }, + { + "name": "setShowDebugBorders", + "description": "Requests that backend shows debug borders on layers", + "parameters": [ + { "name": "show", "type": "boolean", "description": "True for showing debug borders" } + ], + "hidden": true + }, + { + "name": "setShowFPSCounter", + "description": "Requests that backend shows the FPS counter", + "parameters": [ + { "name": "show", "type": "boolean", "description": "True for showing the FPS counter" } + ], + "hidden": true + }, + { + "name": "setContinuousPaintingEnabled", + "description": "Requests that backend enables continuous painting", + "parameters": [ + { "name": "enabled", "type": "boolean", "description": "True for enabling cointinuous painting" } + ], + "hidden": true + }, + { + "name": "setShowScrollBottleneckRects", + "description": "Requests that backend shows scroll bottleneck rects", + "parameters": [ + { "name": "show", "type": "boolean", "description": "True for showing scroll bottleneck rects" } + ], + "hidden": true + }, + { + "name": "getScriptExecutionStatus", + "description": "Determines if scripts can be executed in the page.", + "returns": [ + { "name": "result", "type": "string", "enum": ["allowed", "disabled", "forbidden"], "description": "Script execution status: \"allowed\" if scripts can be executed, \"disabled\" if script execution has been disabled through page settings, \"forbidden\" if script execution for the given page is not possible for other reasons." } + ], + "hidden": true + }, + { + "name": "setScriptExecutionDisabled", + "description": "Switches script execution in the page.", + "parameters": [ + { "name": "value", "type": "boolean", "description": "Whether script execution should be disabled in the page." } + ], + "hidden": true + }, + { + "name": "setGeolocationOverride", + "description": "Overrides the Geolocation Position or Error.", + "parameters": [ + { "name": "latitude", "type": "number", "optional": true, "description": "Mock longitude"}, + { "name": "longitude", "type": "number", "optional": true, "description": "Mock latitude"}, + { "name": "accuracy", "type": "number", "optional": true, "description": "Mock accuracy"} + ] + }, + { + "name": "clearGeolocationOverride", + "description": "Clears the overriden Geolocation Position and Error." + }, + { + "name": "setDeviceOrientationOverride", + "description": "Overrides the Device Orientation.", + "parameters": [ + { "name": "alpha", "type": "number", "description": "Mock alpha"}, + { "name": "beta", "type": "number", "description": "Mock beta"}, + { "name": "gamma", "type": "number", "description": "Mock gamma"} + ], + "hidden": true + }, + { + "name": "clearDeviceOrientationOverride", + "description": "Clears the overridden Device Orientation.", + "hidden": true + }, + { + "name": "setTouchEmulationEnabled", + "parameters": [ + { "name": "enabled", "type": "boolean", "description": "Whether the touch event emulation should be enabled." } + ], + "description": "Toggles mouse event-based touch event emulation.", + "hidden": true + }, + { + "name": "setEmulatedMedia", + "parameters": [ + { "name": "media", "type": "string", "description": "Media type to emulate. Empty string disables the override." } + ], + "description": "Emulates the given media for CSS media queries.", + "hidden": true + }, + { + "name": "captureScreenshot", + "description": "Capture page screenshot.", + "parameters": [ + { "name": "format", "type": "string", "optional": true, "enum": ["jpeg", "png"], "description": "Image compression format." }, + { "name": "quality", "type": "integer", "hidden": true, "optional": true, "description": "Compression quality from range [0..100]." }, + { "name": "maxWidth", "type": "integer", "hidden": true, "optional": true, "description": "Maximum screenshot width." }, + { "name": "maxHeight", "type": "integer", "hidden": true, "optional": true, "description": "Maximum screenshot height." } + ], + "returns": [ + { "name": "data", "type": "string", "description": "Base64-encoded image data (PNG)." }, + { "name": "deviceScaleFactor", "type": "number", "hidden": true, "description": "Device scale factor." }, + { "name": "pageScaleFactor", "type": "number", "hidden": true, "description": "Page scale factor." }, + { "name": "viewport", "$ref": "DOM.Rect", "hidden": true, "description": "Viewport in CSS pixels." } + ], + "hidden": true + }, + { + "name": "startScreencast", + "description": "Starts sending each frame using the <code>screencastFrame</code> event.", + "parameters": [ + { "name": "format", "type": "string", "optional": true, "enum": ["jpeg", "png"], "description": "Image compression format." }, + { "name": "quality", "type": "integer", "optional": true, "description": "Compression quality from range [0..100]." }, + { "name": "maxWidth", "type": "integer", "optional": true, "description": "Maximum screenshot width." }, + { "name": "maxHeight", "type": "integer", "optional": true, "description": "Maximum screenshot height." } + ], + "hidden": true + }, + { + "name": "stopScreencast", + "description": "Stops sending each frame in the <code>screencastFrame</code>.", + "hidden": true + }, + { + "name": "handleJavaScriptDialog", + "description": "Accepts or dismisses a JavaScript initiated dialog (alert, confirm, prompt, or onbeforeunload).", + "parameters": [ + { "name": "accept", "type": "boolean", "description": "Whether to accept or dismiss the dialog." }, + { "name": "promptText", "type": "string", "optional": true, "description": "The text to enter into the dialog prompt before accepting. Used only if this is a prompt dialog." } + ], + "hidden": true + }, + { + "name": "setShowViewportSizeOnResize", + "description": "Paints viewport size upon main frame resize.", + "parameters": [ + { "name": "show", "type": "boolean", "description": "Whether to paint size or not." }, + { "name": "showGrid", "type": "boolean", "optional": true, "description": "Whether to paint grid as well." } + ], + "hidden": true + }, + { + "name": "setForceCompositingMode", + "description": "Force accelerated compositing mode for inspected page.", + "parameters": [ + { "name": "force", "type": "boolean", "description": "Whether to force accelerated compositing or not." } + ], + "hidden": true + } + ], + "events": [ + { + "name": "domContentEventFired", + "parameters": [ + { "name": "timestamp", "type": "number" } + ] + }, + { + "name": "loadEventFired", + "parameters": [ + { "name": "timestamp", "type": "number" } + ] + }, + { + "name": "frameAttached", + "description": "Fired when frame has been attached to its parent.", + "parameters": [ + { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has been attached." } + ] + }, + { + "name": "frameNavigated", + "description": "Fired once navigation of the frame has completed. Frame is now associated with the new loader.", + "parameters": [ + { "name": "frame", "$ref": "Frame", "description": "Frame object." } + ] + }, + { + "name": "frameDetached", + "description": "Fired when frame has been detached from its parent.", + "parameters": [ + { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has been detached." } + ] + }, + { + "name": "frameStartedLoading", + "description": "Fired when frame has started loading.", + "parameters": [ + { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has started loading." } + ], + "hidden": true + }, + { + "name": "frameStoppedLoading", + "description": "Fired when frame has stopped loading.", + "parameters": [ + { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has stopped loading." } + ], + "hidden": true + }, + { + "name": "frameScheduledNavigation", + "description": "Fired when frame schedules a potential navigation.", + "parameters": [ + { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has scheduled a navigation." }, + { "name": "delay", "type": "number", "description": "Delay (in seconds) until the navigation is scheduled to begin. The navigation is not guaranteed to start." } + ], + "hidden": true + }, + { + "name": "frameClearedScheduledNavigation", + "description": "Fired when frame no longer has a scheduled navigation.", + "parameters": [ + { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has cleared its scheduled navigation." } + ], + "hidden": true + }, + { + "name": "javascriptDialogOpening", + "description": "Fired when a JavaScript initiated dialog (alert, confirm, prompt, or onbeforeunload) is about to open.", + "parameters": [ + { "name": "message", "type": "string", "description": "Message that will be displayed by the dialog." } + ], + "hidden": true + }, + { + "name": "javascriptDialogClosed", + "description": "Fired when a JavaScript initiated dialog (alert, confirm, prompt, or onbeforeunload) has been closed.", + "hidden": true + }, + { + "name": "scriptsEnabled", + "description": "Fired when the JavaScript is enabled/disabled on the page", + "parameters": [ + { "name": "isEnabled", "type": "boolean", "description": "Whether script execution is enabled or disabled on the page." } + ], + "hidden": true + }, + { + "name": "screencastFrame", + "description": "Compressed image data requested by the <code>startScreencast</code>.", + "parameters": [ + { "name": "data", "type": "string", "description": "Base64-encoded compressed image." }, + { "name": "deviceScaleFactor", "type": "number", "hidden": true, "optional": true, "description": "Device scale factor." }, + { "name": "pageScaleFactor", "type": "number", "hidden": true, "optional": true, "description": "Page scale factor." }, + { "name": "viewport", "$ref": "DOM.Rect", "hidden": true, "optional": true, "description": "Viewport in CSS pixels." }, + { "name": "offsetTop", "type": "number", "hidden": true, "optional": true, "description": "Top offset in DIP." }, + { "name": "offsetBottom", "type": "number", "hidden": true, "optional": true, "description": "Bottom offset in DIP." } + ], + "hidden": true + } + ] + }, + { + "domain": "Runtime", + "description": "Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. Evaluation results are returned as mirror object that expose object type, string representation and unique identifier that can be used for further object reference. Original objects are maintained in memory unless they are either explicitly released or are released along with the other objects in their object group.", + "types": [ + { + "id": "RemoteObjectId", + "type": "string", + "description": "Unique object identifier." + }, + { + "id": "RemoteObject", + "type": "object", + "description": "Mirror object referencing original JavaScript object.", + "properties": [ + { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean"], "description": "Object type." }, + { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date"], "description": "Object subtype hint. Specified for <code>object</code> type values only." }, + { "name": "className", "type": "string", "optional": true, "description": "Object class (constructor) name. Specified for <code>object</code> type values only." }, + { "name": "value", "type": "any", "optional": true, "description": "Remote object value (in case of primitive values or JSON values if it was requested)." }, + { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." }, + { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Unique object identifier (for non-primitive values)." }, + { "name": "preview", "$ref": "ObjectPreview", "optional": true, "description": "Preview containing abbreviated property values.", "hidden": true } + ] + }, + { + "id": "ObjectPreview", + "type": "object", + "hidden": true, + "description": "Object containing abbreviated remote object value.", + "properties": [ + { "name": "lossless", "type": "boolean", "description": "Determines whether preview is lossless (contains all information of the original object)." }, + { "name": "overflow", "type": "boolean", "description": "True iff some of the properties of the original did not fit." }, + { "name": "properties", "type": "array", "items": { "$ref": "PropertyPreview" }, "description": "List of the properties." } + ] + }, + { + "id": "PropertyPreview", + "type": "object", + "hidden": true, + "properties": [ + { "name": "name", "type": "string", "description": "Property name." }, + { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean"], "description": "Object type." }, + { "name": "value", "type": "string", "optional": true, "description": "User-friendly property value string." }, + { "name": "valuePreview", "$ref": "ObjectPreview", "optional": true, "description": "Nested value preview." }, + { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date"], "description": "Object subtype hint. Specified for <code>object</code> type values only." } + ] + }, + { + "id": "PropertyDescriptor", + "type": "object", + "description": "Object property descriptor.", + "properties": [ + { "name": "name", "type": "string", "description": "Property name." }, + { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." }, + { "name": "writable", "type": "boolean", "optional": true, "description": "True if the value associated with the property may be changed (data descriptors only)." }, + { "name": "get", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a getter for the property, or <code>undefined</code> if there is no getter (accessor descriptors only)." }, + { "name": "set", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a setter for the property, or <code>undefined</code> if there is no setter (accessor descriptors only)." }, + { "name": "configurable", "type": "boolean", "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." }, + { "name": "enumerable", "type": "boolean", "description": "True if this property shows up during enumeration of the properties on the corresponding object." }, + { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }, + { "name": "isOwn", "optional": true, "type": "boolean", "description": "True if the property is owned for the object.", "hidden": true } + + ] + }, + { + "id": "InternalPropertyDescriptor", + "type": "object", + "description": "Object internal property descriptor. This property isn't normally visible in JavaScript code.", + "properties": [ + { "name": "name", "type": "string", "description": "Conventional property name." }, + { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." } + ], + "hidden": true + }, + { + "id": "CallArgument", + "type": "object", + "description": "Represents function call argument. Either remote object id <code>objectId</code> or primitive <code>value</code> or neither of (for undefined) them should be specified.", + "properties": [ + { "name": "value", "type": "any", "optional": true, "description": "Primitive value." }, + { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Remote object handle." } + ] + }, + { + "id": "ExecutionContextId", + "type": "integer", + "description": "Id of an execution context." + }, + { + "id": "ExecutionContextDescription", + "type": "object", + "description": "Description of an isolated world.", + "properties": [ + { "name": "id", "$ref": "ExecutionContextId", "description": "Unique id of the execution context. It can be used to specify in which execution context script evaluation should be performed." }, + { "name": "isPageContext", "type": "boolean", "description": "True if this is a context where inpspected web page scripts run. False if it is a content script isolated context.", "hidden": true }, + { "name": "name", "type": "string", "description": "Human readable name describing given context.", "hidden": true}, + { "name": "frameId", "$ref": "Page.FrameId", "description": "Id of the owning frame." } + ] + } + + ], + "commands": [ + { + "name": "evaluate", + "parameters": [ + { "name": "expression", "type": "string", "description": "Expression to evaluate." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, + { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation.", "hidden": true }, + { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether evaluation should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true }, + { "name": "contextId", "$ref": "Runtime.ExecutionContextId", "optional": true, "description": "Specifies in which isolated context to perform evaluation. Each content script lives in an isolated context and this parameter may be used to specify one of those contexts. If the parameter is omitted or 0 the evaluation will be performed in the context of the inspected page." }, + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." } + ], + "returns": [ + { "name": "result", "$ref": "RemoteObject", "description": "Evaluation result." }, + { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." } + ], + "description": "Evaluates expression on global object." + }, + { + "name": "callFunctionOn", + "parameters": [ + { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to call function on." }, + { "name": "functionDeclaration", "type": "string", "description": "Declaration of the function to call." }, + { "name": "arguments", "type": "array", "items": { "$ref": "CallArgument", "description": "Call argument." }, "optional": true, "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target object." }, + { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether function call should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true }, + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." } + ], + "returns": [ + { "name": "result", "$ref": "RemoteObject", "description": "Call result." }, + { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." } + ], + "description": "Calls function with given declaration on the given object. Object group of the result is inherited from the target object." + }, + { + "name": "getProperties", + "parameters": [ + { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." }, + { "name": "ownProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging only to the element itself, not to its prototype chain." }, + { "name": "accessorPropertiesOnly", "optional": true, "type": "boolean", "description": "If true, returns accessor properties (with getter/setter) only; internal properties are not returned either.", "hidden": true } + ], + "returns": [ + { "name": "result", "type": "array", "items": { "$ref": "PropertyDescriptor"}, "description": "Object properties." }, + { "name": "internalProperties", "optional": true, "type": "array", "items": { "$ref": "InternalPropertyDescriptor"}, "description": "Internal object properties (only of the element itself).", "hidden": true } + ], + "description": "Returns properties of a given object. Object group of the result is inherited from the target object." + }, + { + "name": "releaseObject", + "parameters": [ + { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to release." } + ], + "description": "Releases remote object with given id." + }, + { + "name": "releaseObjectGroup", + "parameters": [ + { "name": "objectGroup", "type": "string", "description": "Symbolic object group name." } + ], + "description": "Releases all remote objects that belong to a given group." + }, + { + "name": "run", + "hidden": true, + "description": "Tells inspected instance(worker or page) that it can run in case it was started paused." + }, + { + "name": "enable", + "description": "Enables reporting of execution contexts creation by means of <code>executionContextCreated</code> event. When the reporting gets enabled the event will be sent immediately for each existing execution context." + }, + { + "name": "disable", + "hidden": true, + "description": "Disables reporting of execution contexts creation." + } + ], + "events": [ + { + "name": "executionContextCreated", + "parameters": [ + { "name": "context", "$ref": "ExecutionContextDescription", "description": "A newly created execution contex." } + ], + "description": "Issued when new execution context is created." + } + ] + }, + { + "domain": "Console", + "description": "Console domain defines methods and events for interaction with the JavaScript console. Console collects messages created by means of the <a href='http://getfirebug.com/wiki/index.php/Console_API'>JavaScript Console API</a>. One needs to enable this domain using <code>enable</code> command in order to start receiving the console messages. Browser collects messages issued while console domain is not enabled as well and reports them using <code>messageAdded</code> notification upon enabling.", + "types": [ + { + "id": "Timestamp", + "type": "number", + "description": "Number of seconds since epoch.", + "hidden": true + }, + { + "id": "ConsoleMessage", + "type": "object", + "description": "Console message.", + "properties": [ + { "name": "source", "type": "string", "enum": ["xml", "javascript", "network", "console-api", "storage", "appcache", "rendering", "css", "security", "other", "deprecation"], "description": "Message source." }, + { "name": "level", "type": "string", "enum": ["log", "warning", "error", "debug"], "description": "Message severity." }, + { "name": "text", "type": "string", "description": "Message text." }, + { "name": "type", "type": "string", "optional": true, "enum": ["log", "dir", "dirxml", "table", "trace", "clear", "startGroup", "startGroupCollapsed", "endGroup", "assert", "timing", "profile", "profileEnd"], "description": "Console message type." }, + { "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." }, + { "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message." }, + { "name": "column", "type": "integer", "optional": true, "description": "Column number in the resource that generated this message." }, + { "name": "repeatCount", "type": "integer", "optional": true, "description": "Repeat count for repeated messages." }, + { "name": "parameters", "type": "array", "items": { "$ref": "Runtime.RemoteObject" }, "optional": true, "description": "Message parameters in case of the formatted message." }, + { "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "JavaScript stack trace for assertions and error messages." }, + { "name": "networkRequestId", "$ref": "Network.RequestId", "optional": true, "description": "Identifier of the network request associated with this message." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp, when this message was fired.", "hidden": true } + ] + }, + { + "id": "CallFrame", + "type": "object", + "description": "Stack entry for console errors and assertions.", + "properties": [ + { "name": "functionName", "type": "string", "description": "JavaScript function name." }, + { "name": "scriptId", "type": "string", "description": "JavaScript script id." }, + { "name": "url", "type": "string", "description": "JavaScript script name or url." }, + { "name": "lineNumber", "type": "integer", "description": "JavaScript script line number." }, + { "name": "columnNumber", "type": "integer", "description": "JavaScript script column number." } + ] + }, + { + "id": "StackTrace", + "type": "array", + "items": { "$ref": "CallFrame" }, + "description": "Call frames for assertions or error messages." + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables console domain, sends the messages collected so far to the client by means of the <code>messageAdded</code> notification." + }, + { + "name": "disable", + "description": "Disables console domain, prevents further console messages from being reported to the client." + }, + { + "name": "clearMessages", + "description": "Clears console messages collected in the browser." + }, + { + "name": "setMonitoringXHREnabled", + "parameters": [ + { "name": "enabled", "type": "boolean", "description": "Monitoring enabled state." } + ], + "description": "Toggles monitoring of XMLHttpRequest. If <code>true</code>, console will receive messages upon each XHR issued.", + "hidden": true + }, + { + "name": "addInspectedNode", + "parameters": [ + { "name": "nodeId", "$ref": "DOM.NodeId", "description": "DOM node id to be accessible by means of $x command line API." } + ], + "description": "Enables console to refer to the node with given id via $x (see Command Line API for more details $x functions).", + "hidden": true + }, + { + "name": "addInspectedHeapObject", + "parameters": [ + { "name": "heapObjectId", "type": "integer" } + ], + "hidden": true + } + ], + "events": [ + { + "name": "messageAdded", + "parameters": [ + { "name": "message", "$ref": "ConsoleMessage", "description": "Console message that has been added." } + ], + "description": "Issued when new console message is added." + }, + { + "name": "messageRepeatCountUpdated", + "parameters": [ + { "name": "count", "type": "integer", "description": "New repeat count value." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp of most recent message in batch.", "hidden": true } + ], + "description": "Issued when subsequent message(s) are equal to the previous one(s)." + }, + { + "name": "messagesCleared", + "description": "Issued when console is cleared. This happens either upon <code>clearMessages</code> command or after page navigation." + } + ] + }, + { + "domain": "Network", + "description": "Network domain allows tracking network activities of the page. It exposes information about http, file, data and other requests and responses, their headers, bodies, timing, etc.", + "types": [ + { + "id": "LoaderId", + "type": "string", + "description": "Unique loader identifier." + }, + { + "id": "RequestId", + "type": "string", + "description": "Unique request identifier." + }, + { + "id": "Timestamp", + "type": "number", + "description": "Number of seconds since epoch." + }, + { + "id": "Headers", + "type": "object", + "description": "Request / response headers as keys / values of JSON object." + }, + { + "id": "ResourceTiming", + "type": "object", + "description": "Timing information for the request.", + "properties": [ + { "name": "requestTime", "type": "number", "description": "Timing's requestTime is a baseline in seconds, while the other numbers are ticks in milliseconds relatively to this requestTime." }, + { "name": "proxyStart", "type": "number", "description": "Started resolving proxy." }, + { "name": "proxyEnd", "type": "number", "description": "Finished resolving proxy." }, + { "name": "dnsStart", "type": "number", "description": "Started DNS address resolve." }, + { "name": "dnsEnd", "type": "number", "description": "Finished DNS address resolve." }, + { "name": "connectStart", "type": "number", "description": "Started connecting to the remote host." }, + { "name": "connectEnd", "type": "number", "description": "Connected to the remote host." }, + { "name": "sslStart", "type": "number", "description": "Started SSL handshake." }, + { "name": "sslEnd", "type": "number", "description": "Finished SSL handshake." }, + { "name": "sendStart", "type": "number", "description": "Started sending request." }, + { "name": "sendEnd", "type": "number", "description": "Finished sending request." }, + { "name": "receiveHeadersEnd", "type": "number", "description": "Finished receiving response headers." } + ] + }, + { + "id": "Request", + "type": "object", + "description": "HTTP request data.", + "properties": [ + { "name": "url", "type": "string", "description": "Request URL." }, + { "name": "method", "type": "string", "description": "HTTP request method." }, + { "name": "headers", "$ref": "Headers", "description": "HTTP request headers." }, + { "name": "postData", "type": "string", "optional": true, "description": "HTTP POST request data." } + ] + }, + { + "id": "Response", + "type": "object", + "description": "HTTP response data.", + "properties": [ + { "name": "url", "type": "string", "description": "Response URL. This URL can be different from CachedResource.url in case of redirect." }, + { "name": "status", "type": "number", "description": "HTTP response status code." }, + { "name": "statusText", "type": "string", "description": "HTTP response status text." }, + { "name": "headers", "$ref": "Headers", "description": "HTTP response headers." }, + { "name": "headersText", "type": "string", "optional": true, "description": "HTTP response headers text." }, + { "name": "mimeType", "type": "string", "description": "Resource mimeType as determined by the browser." }, + { "name": "requestHeaders", "$ref": "Headers", "optional": true, "description": "Refined HTTP request headers that were actually transmitted over the network." }, + { "name": "requestHeadersText", "type": "string", "optional": true, "description": "HTTP request headers text." }, + { "name": "connectionReused", "type": "boolean", "description": "Specifies whether physical connection was actually reused for this request." }, + { "name": "connectionId", "type": "number", "description": "Physical connection id that was actually used for this request." }, + { "name": "fromDiskCache", "type": "boolean", "optional": true, "description": "Specifies that the request was served from the disk cache." }, + { "name": "timing", "$ref": "ResourceTiming", "optional": true, "description": "Timing information for the given request." } + ] + }, + { + "id": "WebSocketRequest", + "type": "object", + "description": "WebSocket request data.", + "hidden": true, + "properties": [ + { "name": "headers", "$ref": "Headers", "description": "HTTP response headers." } + ] + }, + { + "id": "WebSocketResponse", + "type": "object", + "description": "WebSocket response data.", + "hidden": true, + "properties": [ + { "name": "status", "type": "number", "description": "HTTP response status code." }, + { "name": "statusText", "type": "string", "description": "HTTP response status text." }, + { "name": "headers", "$ref": "Headers", "description": "HTTP response headers." } + ] + }, + { + "id": "WebSocketFrame", + "type": "object", + "description": "WebSocket frame data.", + "hidden": true, + "properties": [ + { "name": "opcode", "type": "number", "description": "WebSocket frame opcode." }, + { "name": "mask", "type": "boolean", "description": "WebSocke frame mask." }, + { "name": "payloadData", "type": "string", "description": "WebSocke frame payload data." } + ] + }, + { + "id": "CachedResource", + "type": "object", + "description": "Information about the cached resource.", + "properties": [ + { "name": "url", "type": "string", "description": "Resource URL. This is the url of the original network request." }, + { "name": "type", "$ref": "Page.ResourceType", "description": "Type of this resource." }, + { "name": "response", "$ref": "Response", "optional": true, "description": "Cached response data." }, + { "name": "bodySize", "type": "number", "description": "Cached response body size." } + ] + }, + { + "id": "Initiator", + "type": "object", + "description": "Information about the request initiator.", + "properties": [ + { "name": "type", "type": "string", "enum": ["parser", "script", "other"], "description": "Type of this initiator." }, + { "name": "stackTrace", "$ref": "Console.StackTrace", "optional": true, "description": "Initiator JavaScript stack trace, set for Script only." }, + { "name": "url", "type": "string", "optional": true, "description": "Initiator URL, set for Parser type only." }, + { "name": "lineNumber", "type": "number", "optional": true, "description": "Initiator line number, set for Parser type only." } + ] + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables network tracking, network events will now be delivered to the client." + }, + { + "name": "disable", + "description": "Disables network tracking, prevents network events from being sent to the client." + }, + { + "name": "setUserAgentOverride", + "description": "Allows overriding user agent with the given string.", + "parameters": [ + { "name": "userAgent", "type": "string", "description": "User agent to use." } + ] + }, + { + "name": "setExtraHTTPHeaders", + "description": "Specifies whether to always send extra HTTP headers with the requests from this page.", + "parameters": [ + { "name": "headers", "$ref": "Headers", "description": "Map with extra HTTP headers." } + ] + }, + { + "name": "getResponseBody", + "description": "Returns content served for the given request.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Identifier of the network request to get content for." } + ], + "returns": [ + { "name": "body", "type": "string", "description": "Response body." }, + { "name": "base64Encoded", "type": "boolean", "description": "True, if content was sent as base64." } + ] + }, + { + "name": "replayXHR", + "description": "This method sends a new XMLHttpRequest which is identical to the original one. The following parameters should be identical: method, url, async, request body, extra headers, withCredentials attribute, user, password.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Identifier of XHR to replay." } + ], + "hidden": true + }, + { + "name": "canClearBrowserCache", + "description": "Tells whether clearing browser cache is supported.", + "returns": [ + { "name": "result", "type": "boolean", "description": "True if browser cache can be cleared." } + ] + }, + { + "name": "clearBrowserCache", + "description": "Clears browser cache." + }, + { + "name": "canClearBrowserCookies", + "description": "Tells whether clearing browser cookies is supported.", + "returns": [ + { "name": "result", "type": "boolean", "description": "True if browser cookies can be cleared." } + ] + }, + { + "name": "clearBrowserCookies", + "description": "Clears browser cookies." + }, + { + "name": "setCacheDisabled", + "parameters": [ + { "name": "cacheDisabled", "type": "boolean", "description": "Cache disabled state." } + ], + "description": "Toggles ignoring cache for each request. If <code>true</code>, cache will not be used." + }, + { + "name": "loadResourceForFrontend", + "async": true, + "parameters": [ + { "name": "frameId", "$ref": "Page.FrameId", "description": "Frame to load the resource from." }, + { "name": "url", "type": "string", "description": "URL of the resource to load." }, + { "name": "requestHeaders", "$ref": "Network.Headers", "optional": true, "description": "Request headers." } + ], + "returns": [ + { "name": "statusCode", "type": "number", "description": "HTTP status code." }, + { "name": "responseHeaders", "$ref": "Network.Headers", "description": "Response headers." }, + { "name": "content", "type": "string", "description": "Resource content." } + ], + "description": "Loads a resource in the context of a frame on the inspected page without cross origin checks.", + "hidden": true + } + ], + "events": [ + { + "name": "requestWillBeSent", + "description": "Fired when page is about to send HTTP request.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "frameId", "$ref": "Page.FrameId", "description": "Frame identifier.", "hidden": true }, + { "name": "loaderId", "$ref": "LoaderId", "description": "Loader identifier." }, + { "name": "documentURL", "type": "string", "description": "URL of the document this request is loaded for." }, + { "name": "request", "$ref": "Request", "description": "Request data." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "initiator", "$ref": "Initiator", "description": "Request initiator." }, + { "name": "redirectResponse", "optional": true, "$ref": "Response", "description": "Redirect response data." } + ] + }, + { + "name": "requestServedFromCache", + "description": "Fired if request ended up loading from cache.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." } + ] + }, + { + "name": "responseReceived", + "description": "Fired when HTTP response is available.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "frameId", "$ref": "Page.FrameId", "description": "Frame identifier.", "hidden": true }, + { "name": "loaderId", "$ref": "LoaderId", "description": "Loader identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "type", "$ref": "Page.ResourceType", "description": "Resource type." }, + { "name": "response", "$ref": "Response", "description": "Response data." } + ] + }, + { + "name": "dataReceived", + "description": "Fired when data chunk was received over the network.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "dataLength", "type": "integer", "description": "Data chunk length." }, + { "name": "encodedDataLength", "type": "integer", "description": "Actual bytes received (might be less than dataLength for compressed encodings)." } + ] + }, + { + "name": "loadingFinished", + "description": "Fired when HTTP request has finished loading.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." } + ] + }, + { + "name": "loadingFailed", + "description": "Fired when HTTP request has failed to load.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "errorText", "type": "string", "description": "User friendly error message." }, + { "name": "canceled", "type": "boolean", "optional": true, "description": "True if loading was canceled." } + ] + }, + { + "name": "webSocketWillSendHandshakeRequest", + "description": "Fired when WebSocket is about to initiate handshake.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "request", "$ref": "WebSocketRequest", "description": "WebSocket request data." } + ], + "hidden": true + }, + { + "name": "webSocketHandshakeResponseReceived", + "description": "Fired when WebSocket handshake response becomes available.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "response", "$ref": "WebSocketResponse", "description": "WebSocket response data." } + ], + "hidden": true + }, + { + "name": "webSocketCreated", + "description": "Fired upon WebSocket creation.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "url", "type": "string", "description": "WebSocket request URL." } + ], + "hidden": true + }, + { + "name": "webSocketClosed", + "description": "Fired when WebSocket is closed.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." } + ], + "hidden": true + }, + { + "name": "webSocketFrameReceived", + "description": "Fired when WebSocket frame is received.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "response", "$ref": "WebSocketFrame", "description": "WebSocket response data." } + ], + "hidden": true + }, + { + "name": "webSocketFrameError", + "description": "Fired when WebSocket frame error occurs.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "errorMessage", "type": "string", "description": "WebSocket frame error message." } + ], + "hidden": true + }, + { + "name": "webSocketFrameSent", + "description": "Fired when WebSocket frame is sent.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "response", "$ref": "WebSocketFrame", "description": "WebSocket response data." } + ], + "hidden": true + } + ] + }, + { + "domain": "Database", + "hidden": true, + "types": [ + { + "id": "DatabaseId", + "type": "string", + "description": "Unique identifier of Database object.", + "hidden": true + }, + { + "id": "Database", + "type": "object", + "description": "Database object.", + "hidden": true, + "properties": [ + { "name": "id", "$ref": "DatabaseId", "description": "Database ID." }, + { "name": "domain", "type": "string", "description": "Database domain." }, + { "name": "name", "type": "string", "description": "Database name." }, + { "name": "version", "type": "string", "description": "Database version." } + ] + }, + { + "id": "Error", + "type": "object", + "description": "Database error.", + "properties": [ + { "name": "message", "type": "string", "description": "Error message." }, + { "name": "code", "type": "integer", "description": "Error code." } + ] + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables database tracking, database events will now be delivered to the client." + }, + { + "name": "disable", + "description": "Disables database tracking, prevents database events from being sent to the client." + }, + { + "name": "getDatabaseTableNames", + "parameters": [ + { "name": "databaseId", "$ref": "DatabaseId" } + ], + "returns": [ + { "name": "tableNames", "type": "array", "items": { "type": "string" } } + ] + }, + { + "name": "executeSQL", + "async": true, + "parameters": [ + { "name": "databaseId", "$ref": "DatabaseId" }, + { "name": "query", "type": "string" } + ], + "returns": [ + { "name": "columnNames", "type": "array", "optional": true, "items": { "type": "string" } }, + { "name": "values", "type": "array", "optional": true, "items": { "type": "any" }}, + { "name": "sqlError", "$ref": "Error", "optional": true } + ] + } + ], + "events": [ + { + "name": "addDatabase", + "parameters": [ + { "name": "database", "$ref": "Database" } + ] + } + ] + }, + { + "domain": "IndexedDB", + "hidden": true, + "types": [ + { + "id": "DatabaseWithObjectStores", + "type": "object", + "description": "Database with an array of object stores.", + "properties": [ + { "name": "name", "type": "string", "description": "Database name." }, + { "name": "version", "type": "integer", "description": "Database version." }, + { "name": "objectStores", "type": "array", "items": { "$ref": "ObjectStore" }, "description": "Object stores in this database." } + ] + }, + { + "id": "ObjectStore", + "type": "object", + "description": "Object store.", + "properties": [ + { "name": "name", "type": "string", "description": "Object store name." }, + { "name": "keyPath", "$ref": "KeyPath", "description": "Object store key path." }, + { "name": "autoIncrement", "type": "boolean", "description": "If true, object store has auto increment flag set." }, + { "name": "indexes", "type": "array", "items": { "$ref": "ObjectStoreIndex" }, "description": "Indexes in this object store." } + ] + }, + { + "id": "ObjectStoreIndex", + "type": "object", + "description": "Object store index.", + "properties": [ + { "name": "name", "type": "string", "description": "Index name." }, + { "name": "keyPath", "$ref": "KeyPath", "description": "Index key path." }, + { "name": "unique", "type": "boolean", "description": "If true, index is unique." }, + { "name": "multiEntry", "type": "boolean", "description": "If true, index allows multiple entries for a key." } + ] + }, + { + "id": "Key", + "type": "object", + "description": "Key.", + "properties": [ + { "name": "type", "type": "string", "enum": ["number", "string", "date", "array"], "description": "Key type." }, + { "name": "number", "type": "number", "optional": true, "description": "Number value." }, + { "name": "string", "type": "string", "optional": true, "description": "String value." }, + { "name": "date", "type": "number", "optional": true, "description": "Date value." }, + { "name": "array", "type": "array", "optional": true, "items": { "$ref": "Key" }, "description": "Array value." } + ] + }, + { + "id": "KeyRange", + "type": "object", + "description": "Key range.", + "properties": [ + { "name": "lower", "$ref": "Key", "optional": true, "description": "Lower bound." }, + { "name": "upper", "$ref": "Key", "optional": true, "description": "Upper bound." }, + { "name": "lowerOpen", "type": "boolean", "description": "If true lower bound is open." }, + { "name": "upperOpen", "type": "boolean", "description": "If true upper bound is open." } + ] + }, + { + "id": "DataEntry", + "type": "object", + "description": "Data entry.", + "properties": [ + { "name": "key", "$ref": "Runtime.RemoteObject", "description": "Key." }, + { "name": "primaryKey", "$ref": "Runtime.RemoteObject", "description": "Primary key." }, + { "name": "value", "$ref": "Runtime.RemoteObject", "description": "Value." } + ] + }, + { + "id": "KeyPath", + "type": "object", + "description": "Key path.", + "properties": [ + { "name": "type", "type": "string", "enum": ["null", "string", "array"], "description": "Key path type." }, + { "name": "string", "type": "string", "optional": true, "description": "String value." }, + { "name": "array", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Array value." } + ] + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables events from backend." + }, + { + "name": "disable", + "description": "Disables events from backend." + }, + { + "name": "requestDatabaseNames", + "async": true, + "parameters": [ + { "name": "securityOrigin", "type": "string", "description": "Security origin." } + ], + "returns": [ + { "name": "databaseNames", "type": "array", "items": { "type": "string" }, "description": "Database names for origin." } + ], + "description": "Requests database names for given security origin." + }, + { + "name": "requestDatabase", + "async": true, + "parameters": [ + { "name": "securityOrigin", "type": "string", "description": "Security origin." }, + { "name": "databaseName", "type": "string", "description": "Database name." } + ], + "returns": [ + { "name": "databaseWithObjectStores", "$ref": "DatabaseWithObjectStores", "description": "Database with an array of object stores." } + ], + "description": "Requests database with given name in given frame." + }, + { + "name": "requestData", + "async": true, + "parameters": [ + { "name": "securityOrigin", "type": "string", "description": "Security origin." }, + { "name": "databaseName", "type": "string", "description": "Database name." }, + { "name": "objectStoreName", "type": "string", "description": "Object store name." }, + { "name": "indexName", "type": "string", "description": "Index name, empty string for object store data requests." }, + { "name": "skipCount", "type": "integer", "description": "Number of records to skip." }, + { "name": "pageSize", "type": "integer", "description": "Number of records to fetch." }, + { "name": "keyRange", "$ref": "KeyRange", "optional": true, "description": "Key range." } + ], + "returns": [ + { "name": "objectStoreDataEntries", "type": "array", "items": { "$ref": "DataEntry" }, "description": "Array of object store data entries." }, + { "name": "hasMore", "type": "boolean", "description": "If true, there are more entries to fetch in the given range." } + ], + "description": "Requests data from object store or index." + }, + { + "name": "clearObjectStore", + "async": true, + "parameters": [ + { "name": "securityOrigin", "type": "string", "description": "Security origin." }, + { "name": "databaseName", "type": "string", "description": "Database name." }, + { "name": "objectStoreName", "type": "string", "description": "Object store name." } + ], + "returns": [ + ], + "description": "Clears all entries from an object store." + } + ] + }, + { + "domain": "DOMStorage", + "hidden": true, + "description": "Query and modify DOM storage.", + "types": [ + { + "id": "StorageId", + "type": "object", + "description": "DOM Storage identifier.", + "hidden": true, + "properties": [ + { "name": "securityOrigin", "type": "string", "description": "Security origin for the storage." }, + { "name": "isLocalStorage", "type": "boolean", "description": "Whether the storage is local storage (not session storage)." } + ] + }, + { + "id": "Item", + "type": "array", + "description": "DOM Storage item.", + "hidden": true, + "items": { "type": "string" } + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables storage tracking, storage events will now be delivered to the client." + }, + { + "name": "disable", + "description": "Disables storage tracking, prevents storage events from being sent to the client." + }, + { + "name": "getDOMStorageItems", + "parameters": [ + { "name": "storageId", "$ref": "StorageId" } + ], + "returns": [ + { "name": "entries", "type": "array", "items": { "$ref": "Item" } } + ] + }, + { + "name": "setDOMStorageItem", + "parameters": [ + { "name": "storageId", "$ref": "StorageId" }, + { "name": "key", "type": "string" }, + { "name": "value", "type": "string" } + ] + }, + { + "name": "removeDOMStorageItem", + "parameters": [ + { "name": "storageId", "$ref": "StorageId" }, + { "name": "key", "type": "string" } + ] + } + ], + "events": [ + { + "name": "domStorageItemsCleared", + "parameters": [ + { "name": "storageId", "$ref": "StorageId" } + ] + }, + { + "name": "domStorageItemRemoved", + "parameters": [ + { "name": "storageId", "$ref": "StorageId" }, + { "name": "key", "type": "string" } + ] + }, + { + "name": "domStorageItemAdded", + "parameters": [ + { "name": "storageId", "$ref": "StorageId" }, + { "name": "key", "type": "string" }, + { "name": "newValue", "type": "string" } + ] + }, + { + "name": "domStorageItemUpdated", + "parameters": [ + { "name": "storageId", "$ref": "StorageId" }, + { "name": "key", "type": "string" }, + { "name": "oldValue", "type": "string" }, + { "name": "newValue", "type": "string" } + ] + } + ] + }, + { + "domain": "ApplicationCache", + "hidden": true, + "types": [ + { + "id": "ApplicationCacheResource", + "type": "object", + "description": "Detailed application cache resource information.", + "properties": [ + { "name": "url", "type": "string", "description": "Resource url." }, + { "name": "size", "type": "integer", "description": "Resource size." }, + { "name": "type", "type": "string", "description": "Resource type." } + ] + }, + { + "id": "ApplicationCache", + "type": "object", + "description": "Detailed application cache information.", + "properties": [ + { "name": "manifestURL", "type": "string", "description": "Manifest URL." }, + { "name": "size", "type": "number", "description": "Application cache size." }, + { "name": "creationTime", "type": "number", "description": "Application cache creation time." }, + { "name": "updateTime", "type": "number", "description": "Application cache update time." }, + { "name": "resources", "type": "array", "items": { "$ref": "ApplicationCacheResource" }, "description": "Application cache resources." } + ] + }, + { + "id": "FrameWithManifest", + "type": "object", + "description": "Frame identifier - manifest URL pair.", + "properties": [ + { "name": "frameId", "$ref": "Page.FrameId", "description": "Frame identifier." }, + { "name": "manifestURL", "type": "string", "description": "Manifest URL." }, + { "name": "status", "type": "integer", "description": "Application cache status." } + ] + } + ], + "commands": [ + { + "name": "getFramesWithManifests", + "returns": [ + { "name": "frameIds", "type": "array", "items": { "$ref": "FrameWithManifest" }, "description": "Array of frame identifiers with manifest urls for each frame containing a document associated with some application cache." } + ], + "description": "Returns array of frame identifiers with manifest urls for each frame containing a document associated with some application cache." + }, + { + "name": "enable", + "description": "Enables application cache domain notifications." + }, + { + "name": "getManifestForFrame", + "parameters": [ + { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame containing document whose manifest is retrieved." } + ], + "returns": [ + { "name": "manifestURL", "type": "string", "description": "Manifest URL for document in the given frame." } + ], + "description": "Returns manifest URL for document in the given frame." + }, + { + "name": "getApplicationCacheForFrame", + "parameters": [ + { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame containing document whose application cache is retrieved." } + ], + "returns": [ + { "name": "applicationCache", "$ref": "ApplicationCache", "description": "Relevant application cache data for the document in given frame." } + ], + "description": "Returns relevant application cache data for the document in given frame." + } + ], + "events": [ + { + "name": "applicationCacheStatusUpdated", + "parameters": [ + { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame containing document whose application cache updated status." }, + { "name": "manifestURL", "type": "string", "description": "Manifest URL." }, + { "name": "status", "type": "integer", "description": "Updated application cache status." } + ] + }, + { + "name": "networkStateUpdated", + "parameters": [ + { "name": "isNowOnline", "type": "boolean" } + ] + } + ] + }, + { + "domain": "FileSystem", + "hidden": true, + "types": [ + { + "id": "Entry", + "type": "object", + "properties": [ + { "name": "url", "type": "string", "description": "filesystem: URL for the entry." }, + { "name": "name", "type": "string", "description": "The name of the file or directory." }, + { "name": "isDirectory", "type": "boolean", "description": "True if the entry is a directory." }, + { "name": "mimeType", "type": "string", "optional": true, "description": "MIME type of the entry, available for a file only." }, + { "name": "resourceType", "$ref": "Page.ResourceType", "optional": true, "description": "ResourceType of the entry, available for a file only." }, + { "name": "isTextFile", "type": "boolean", "optional": true, "description": "True if the entry is a text file." } + ], + "description": "Represents a browser side file or directory." + }, + { + "id": "Metadata", + "type": "object", + "properties": [ + { "name": "modificationTime", "type": "number", "description": "Modification time." }, + { "name": "size", "type": "number", "description": "File size. This field is always zero for directories." } + ], + "description": "Represents metadata of a file or entry." + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables events from backend." + }, + { + "name": "disable", + "description": "Disables events from backend." + }, + { + "name": "requestFileSystemRoot", + "async": true, + "parameters": [ + { "name": "origin", "type": "string", "description": "Security origin of requesting FileSystem. One of frames in current page needs to have this security origin." }, + { "name": "type", "type": "string", "enum": ["temporary", "persistent"], "description": "FileSystem type of requesting FileSystem." } + ], + "returns": [ + { "name": "errorCode", "type": "integer", "description": "0, if no error. Otherwise, errorCode is set to FileError::ErrorCode value." }, + { "name": "root", "$ref": "Entry", "optional": true, "description": "Contains root of the requested FileSystem if the command completed successfully." } + ], + "description": "Returns root directory of the FileSystem, if exists." + }, + { + "name": "requestDirectoryContent", + "async": true, + "parameters": [ + { "name": "url", "type": "string", "description": "URL of the directory that the frontend is requesting to read from." } + ], + "returns": [ + { "name": "errorCode", "type": "integer", "description": "0, if no error. Otherwise, errorCode is set to FileError::ErrorCode value." }, + { "name": "entries", "type": "array", "items": { "$ref": "Entry" }, "optional": true, "description": "Contains all entries on directory if the command completed successfully." } + ], + "description": "Returns content of the directory." + }, + { + "name": "requestMetadata", + "async": true, + "parameters": [ + { "name": "url", "type": "string", "description": "URL of the entry that the frontend is requesting to get metadata from." } + ], + "returns": [ + { "name": "errorCode", "type": "integer", "description": "0, if no error. Otherwise, errorCode is set to FileError::ErrorCode value." }, + { "name": "metadata", "$ref": "Metadata", "optional": true, "description": "Contains metadata of the entry if the command completed successfully." } + ], + "description": "Returns metadata of the entry." + }, + { + "name": "requestFileContent", + "async": true, + "parameters": [ + { "name": "url", "type": "string", "description": "URL of the file that the frontend is requesting to read from." }, + { "name": "readAsText", "type": "boolean", "description": "True if the content should be read as text, otherwise the result will be returned as base64 encoded text." }, + { "name": "start", "type": "integer", "optional": true, "description": "Specifies the start of range to read." }, + { "name": "end", "type": "integer", "optional": true, "description": "Specifies the end of range to read exclusively." }, + { "name": "charset", "type": "string", "optional": true, "description": "Overrides charset of the content when content is served as text." } + ], + "returns": [ + { "name": "errorCode", "type": "integer", "description": "0, if no error. Otherwise, errorCode is set to FileError::ErrorCode value." }, + { "name": "content", "type": "string", "optional": true, "description": "Content of the file." }, + { "name": "charset", "type": "string", "optional": true, "description": "Charset of the content if it is served as text." } + ], + "description": "Returns content of the file. Result should be sliced into [start, end)." + }, + { + "name": "deleteEntry", + "async": true, + "parameters": [ + { "name": "url", "type": "string", "description": "URL of the entry to delete." } + ], + "returns": [ + { "name": "errorCode", "type": "integer", "description": "0, if no error. Otherwise errorCode is set to FileError::ErrorCode value." } + ], + "description": "Deletes specified entry. If the entry is a directory, the agent deletes children recursively." + } + ] + }, + { + "domain": "DOM", + "description": "This domain exposes DOM read/write operations. Each DOM Node is represented with its mirror object that has an <code>id</code>. This <code>id</code> can be used to get additional information on the Node, resolve it into the JavaScript object wrapper, etc. It is important that client receives DOM events only for the nodes that are known to the client. Backend keeps track of the nodes that were sent to the client and never sends the same node twice. It is client's responsibility to collect information about the nodes that were sent to the client.<p>Note that <code>iframe</code> owner elements will return corresponding document elements as their child nodes.</p>", + "types": [ + { + "id": "NodeId", + "type": "integer", + "description": "Unique DOM node identifier." + }, + { + "id": "BackendNodeId", + "type": "integer", + "description": "Unique DOM node identifier used to reference a node that may not have been pushed to the front-end.", + "hidden": true + }, + { + "id": "Node", + "type": "object", + "properties": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Node identifier that is passed into the rest of the DOM messages as the <code>nodeId</code>. Backend will only push node with given <code>id</code> once. It is aware of all requested nodes and will only fire DOM events for nodes known to the client." }, + { "name": "nodeType", "type": "integer", "description": "<code>Node</code>'s nodeType." }, + { "name": "nodeName", "type": "string", "description": "<code>Node</code>'s nodeName." }, + { "name": "localName", "type": "string", "description": "<code>Node</code>'s localName." }, + { "name": "nodeValue", "type": "string", "description": "<code>Node</code>'s nodeValue." }, + { "name": "childNodeCount", "type": "integer", "optional": true, "description": "Child count for <code>Container</code> nodes." }, + { "name": "children", "type": "array", "optional": true, "items": { "$ref": "Node" }, "description": "Child nodes of this node when requested with children." }, + { "name": "attributes", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Attributes of the <code>Element</code> node in the form of flat array <code>[name1, value1, name2, value2]</code>." }, + { "name": "documentURL", "type": "string", "optional": true, "description": "Document URL that <code>Document</code> or <code>FrameOwner</code> node points to." }, + { "name": "baseURL", "type": "string", "optional": true, "description": "Base URL that <code>Document</code> or <code>FrameOwner</code> node uses for URL completion.", "hidden": true }, + { "name": "publicId", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s publicId." }, + { "name": "systemId", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s systemId." }, + { "name": "internalSubset", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s internalSubset." }, + { "name": "xmlVersion", "type": "string", "optional": true, "description": "<code>Document</code>'s XML version in case of XML documents." }, + { "name": "name", "type": "string", "optional": true, "description": "<code>Attr</code>'s name." }, + { "name": "value", "type": "string", "optional": true, "description": "<code>Attr</code>'s value." }, + { "name": "frameId", "$ref": "Page.FrameId", "optional": true, "description": "Frame ID for frame owner elements.", "hidden": true }, + { "name": "contentDocument", "$ref": "Node", "optional": true, "description": "Content document for frame owner elements." }, + { "name": "shadowRoots", "type": "array", "optional": true, "items": { "$ref": "Node" }, "description": "Shadow root list for given element host.", "hidden": true }, + { "name": "templateContent", "$ref": "Node", "optional": true, "description": "Content document fragment for template elements", "hidden": true } + ], + "description": "DOM interaction is implemented in terms of mirror objects that represent the actual DOM nodes. DOMNode is a base node mirror type." + }, + { + "id": "EventListener", + "type": "object", + "hidden": true, + "properties": [ + { "name": "type", "type": "string", "description": "<code>EventListener</code>'s type." }, + { "name": "useCapture", "type": "boolean", "description": "<code>EventListener</code>'s useCapture." }, + { "name": "isAttribute", "type": "boolean", "description": "<code>EventListener</code>'s isAttribute." }, + { "name": "nodeId", "$ref": "NodeId", "description": "Target <code>DOMNode</code> id." }, + { "name": "handlerBody", "type": "string", "description": "Event handler function body." }, + { "name": "location", "$ref": "Debugger.Location", "optional": true, "description": "Handler code location." }, + { "name": "sourceName", "type": "string", "optional": true, "description": "Source script URL." }, + { "name": "handler", "$ref": "Runtime.RemoteObject", "optional": true, "description": "Event handler function value." } + ], + "description": "DOM interaction is implemented in terms of mirror objects that represent the actual DOM nodes. DOMNode is a base node mirror type." + }, + { + "id": "RGBA", + "type": "object", + "properties": [ + { "name": "r", "type": "integer", "description": "The red component, in the [0-255] range." }, + { "name": "g", "type": "integer", "description": "The green component, in the [0-255] range." }, + { "name": "b", "type": "integer", "description": "The blue component, in the [0-255] range." }, + { "name": "a", "type": "number", "optional": true, "description": "The alpha component, in the [0-1] range (default: 1)." } + ], + "description": "A structure holding an RGBA color." + }, + { + "id": "Quad", + "type": "array", + "items": { "type": "number" }, + "minItems": 8, + "maxItems": 8, + "description": "An array of quad vertices, x immediately followed by y for each point, points clock-wise.", + "hidden": true + }, + { + "id": "BoxModel", + "type": "object", + "hidden": true, + "properties": [ + { "name": "content", "$ref": "Quad", "description": "Content box" }, + { "name": "padding", "$ref": "Quad", "description": "Padding box" }, + { "name": "border", "$ref": "Quad", "description": "Border box" }, + { "name": "margin", "$ref": "Quad", "description": "Margin box" }, + { "name": "width", "type": "integer", "description": "Node width" }, + { "name": "height", "type": "integer", "description": "Node height" }, + { "name": "shapeOutside", "type": "string", "description": "CSS Shape Outside" } + ], + "description": "Box model." + }, + { + "id": "Rect", + "type": "object", + "hidden": true, + "properties": [ + { "name": "x", "type": "number", "description": "X coordinate" }, + { "name": "y", "type": "number", "description": "Y coordinate" }, + { "name": "width", "type": "number", "description": "Rectangle width" }, + { "name": "height", "type": "number", "description": "Rectangle height" } + ], + "description": "Rectangle." + }, + { + "id": "HighlightConfig", + "type": "object", + "properties": [ + { "name": "showInfo", "type": "boolean", "optional": true, "description": "Whether the node info tooltip should be shown (default: false)." }, + { "name": "contentColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight fill color (default: transparent)." }, + { "name": "paddingColor", "$ref": "RGBA", "optional": true, "description": "The padding highlight fill color (default: transparent)." }, + { "name": "borderColor", "$ref": "RGBA", "optional": true, "description": "The border highlight fill color (default: transparent)." }, + { "name": "marginColor", "$ref": "RGBA", "optional": true, "description": "The margin highlight fill color (default: transparent)." }, + { "name": "eventTargetColor", "$ref": "RGBA", "optional": true, "hidden": true, "description": "The event target element highlight fill color (default: transparent)." } + ], + "description": "Configuration data for the highlighting of page elements." + } + ], + "commands": [ + { + "name": "getDocument", + "returns": [ + { "name": "root", "$ref": "Node", "description": "Resulting node." } + ], + "description": "Returns the root DOM node to the caller." + }, + { + "name": "requestChildNodes", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get children for." }, + { "name": "depth", "type": "integer", "optional": true, "description": "The maximum depth at which children should be retrieved, defaults to 1. Use -1 for the entire subtree or provide an integer larger than 0.", "hidden": true } + ], + "description": "Requests that children of the node with given id are returned to the caller in form of <code>setChildNodes</code> events where not only immediate children are retrieved, but all children down to the specified depth." + }, + { + "name": "querySelector", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to query upon." }, + { "name": "selector", "type": "string", "description": "Selector string." } + ], + "returns": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Query selector result." } + ], + "description": "Executes <code>querySelector</code> on a given node." + }, + { + "name": "querySelectorAll", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to query upon." }, + { "name": "selector", "type": "string", "description": "Selector string." } + ], + "returns": [ + { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Query selector result." } + ], + "description": "Executes <code>querySelectorAll</code> on a given node." + }, + { + "name": "setNodeName", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set name for." }, + { "name": "name", "type": "string", "description": "New node's name." } + ], + "returns": [ + { "name": "nodeId", "$ref": "NodeId", "description": "New node's id." } + ], + "description": "Sets node name for a node with given id." + }, + { + "name": "setNodeValue", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set value for." }, + { "name": "value", "type": "string", "description": "New node's value." } + ], + "description": "Sets node value for a node with given id." + }, + { + "name": "removeNode", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to remove." } + ], + "description": "Removes node with given id." + }, + { + "name": "setAttributeValue", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to set attribute for." }, + { "name": "name", "type": "string", "description": "Attribute name." }, + { "name": "value", "type": "string", "description": "Attribute value." } + ], + "description": "Sets attribute for an element with given id." + }, + { + "name": "setAttributesAsText", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to set attributes for." }, + { "name": "text", "type": "string", "description": "Text with a number of attributes. Will parse this text using HTML parser." }, + { "name": "name", "type": "string", "optional": true, "description": "Attribute name to replace with new attributes derived from text in case text parsed successfully." } + ], + "description": "Sets attributes on element with given id. This method is useful when user edits some existing attribute value and types in several attribute name/value pairs." + }, + { + "name": "removeAttribute", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to remove attribute from." }, + { "name": "name", "type": "string", "description": "Name of the attribute to remove." } + ], + "description": "Removes attribute with given name from an element with given id." + }, + { + "name": "getEventListenersForNode", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get listeners for." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name for handler value. Handler value is not returned without this parameter specified." } + ], + "returns": [ + { "name": "listeners", "type": "array", "items": { "$ref": "EventListener"}, "description": "Array of relevant listeners." } + ], + "description": "Returns event listeners relevant to the node.", + "hidden": true + }, + { + "name": "getOuterHTML", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get markup for." } + ], + "returns": [ + { "name": "outerHTML", "type": "string", "description": "Outer HTML markup." } + ], + "description": "Returns node's HTML markup." + }, + { + "name": "setOuterHTML", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set markup for." }, + { "name": "outerHTML", "type": "string", "description": "Outer HTML markup to set." } + ], + "description": "Sets node HTML markup, returns new node id." + }, + { + "name": "performSearch", + "parameters": [ + { "name": "query", "type": "string", "description": "Plain text or query selector or XPath search query." } + ], + "returns": [ + { "name": "searchId", "type": "string", "description": "Unique search session identifier." }, + { "name": "resultCount", "type": "integer", "description": "Number of search results." } + ], + "description": "Searches for a given string in the DOM tree. Use <code>getSearchResults</code> to access search results or <code>cancelSearch</code> to end this search session.", + "hidden": true + }, + { + "name": "getSearchResults", + "parameters": [ + { "name": "searchId", "type": "string", "description": "Unique search session identifier." }, + { "name": "fromIndex", "type": "integer", "description": "Start index of the search result to be returned." }, + { "name": "toIndex", "type": "integer", "description": "End index of the search result to be returned." } + ], + "returns": [ + { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Ids of the search result nodes." } + ], + "description": "Returns search results from given <code>fromIndex</code> to given <code>toIndex</code> from the sarch with the given identifier.", + "hidden": true + }, + { + "name": "discardSearchResults", + "parameters": [ + { "name": "searchId", "type": "string", "description": "Unique search session identifier." } + ], + "description": "Discards search results from the session with the given id. <code>getSearchResults</code> should no longer be called for that search.", + "hidden": true + }, + { + "name": "requestNode", + "parameters": [ + { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "JavaScript object id to convert into node." } + ], + "returns": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Node id for given object." } + ], + "description": "Requests that the node is sent to the caller given the JavaScript node object reference. All nodes that form the path from the node to the root are also sent to the client as a series of <code>setChildNodes</code> notifications." + }, + { + "name": "setInspectModeEnabled", + "hidden": true, + "parameters": [ + { "name": "enabled", "type": "boolean", "description": "True to enable inspection mode, false to disable it." }, + { "name": "inspectShadowDOM", "type": "boolean", "optional": true, "description": "True to enable inspection mode for shadow DOM." }, + { "name": "highlightConfig", "$ref": "HighlightConfig", "optional": true, "description": "A descriptor for the highlight appearance of hovered-over nodes. May be omitted if <code>enabled == false</code>." } + ], + "description": "Enters the 'inspect' mode. In this mode, elements that user is hovering over are highlighted. Backend then generates 'inspectNodeRequested' event upon element selection." + }, + { + "name": "highlightRect", + "parameters": [ + { "name": "x", "type": "integer", "description": "X coordinate" }, + { "name": "y", "type": "integer", "description": "Y coordinate" }, + { "name": "width", "type": "integer", "description": "Rectangle width" }, + { "name": "height", "type": "integer", "description": "Rectangle height" }, + { "name": "color", "$ref": "RGBA", "optional": true, "description": "The highlight fill color (default: transparent)." }, + { "name": "outlineColor", "$ref": "RGBA", "optional": true, "description": "The highlight outline color (default: transparent)." } + ], + "description": "Highlights given rectangle. Coordinates are absolute with respect to the main frame viewport." + }, + { + "name": "highlightQuad", + "parameters": [ + { "name": "quad", "$ref": "Quad", "description": "Quad to highlight" }, + { "name": "color", "$ref": "RGBA", "optional": true, "description": "The highlight fill color (default: transparent)." }, + { "name": "outlineColor", "$ref": "RGBA", "optional": true, "description": "The highlight outline color (default: transparent)." } + ], + "description": "Highlights given quad. Coordinates are absolute with respect to the main frame viewport.", + "hidden": true + }, + { + "name": "highlightNode", + "parameters": [ + { "name": "highlightConfig", "$ref": "HighlightConfig", "description": "A descriptor for the highlight appearance." }, + { "name": "nodeId", "$ref": "NodeId", "optional": true, "description": "Identifier of the node to highlight." }, + { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "optional": true, "description": "JavaScript object id of the node to be highlighted.", "hidden": true } + ], + "description": "Highlights DOM node with given id or with the given JavaScript object wrapper. Either nodeId or objectId must be specified." + }, + { + "name": "hideHighlight", + "description": "Hides DOM node highlight." + }, + { + "name": "highlightFrame", + "parameters": [ + { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame to highlight." }, + { "name": "contentColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight fill color (default: transparent)." }, + { "name": "contentOutlineColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight outline color (default: transparent)." } + ], + "description": "Highlights owner element of the frame with given id.", + "hidden": true + }, + { + "name": "pushNodeByPathToFrontend", + "parameters": [ + { "name": "path", "type": "string", "description": "Path to node in the proprietary format." } + ], + "returns": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node for given path." } + ], + "description": "Requests that the node is sent to the caller given its path. // FIXME, use XPath", + "hidden": true + }, + { + "name": "pushNodeByBackendIdToFrontend", + "parameters": [ + { "name": "backendNodeId", "$ref": "BackendNodeId", "description": "The backend node id of the node." } + ], + "returns": [ + { "name": "nodeId", "$ref": "NodeId", "description": "The pushed node's id." } + ], + "description": "Requests that the node is sent to the caller given its backend node id.", + "hidden": true + }, + { + "name": "releaseBackendNodeIds", + "parameters": [ + { "name": "nodeGroup", "type": "string", "description": "The backend node ids group name." } + ], + "description": "Requests that group of <code>BackendNodeIds</code> is released.", + "hidden": true + }, + { + "name": "resolveNode", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to resolve." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." } + ], + "returns": [ + { "name": "object", "$ref": "Runtime.RemoteObject", "description": "JavaScript object wrapper for given node." } + ], + "description": "Resolves JavaScript node object for given node id." + }, + { + "name": "getAttributes", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to retrieve attibutes for." } + ], + "returns": [ + { "name": "attributes", "type": "array", "items": { "type": "string" }, "description": "An interleaved array of node attribute names and values." } + ], + "description": "Returns attributes for the specified node." + }, + { + "name": "moveTo", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to drop." }, + { "name": "targetNodeId", "$ref": "NodeId", "description": "Id of the element to drop into." }, + { "name": "insertBeforeNodeId", "$ref": "NodeId", "optional": true, "description": "Drop node before given one." } + ], + "returns": [ + { "name": "nodeId", "$ref": "NodeId", "description": "New id of the moved node." } + ], + "description": "Moves node into the new container, places it before the given anchor." + }, + { + "name": "undo", + "description": "Undoes the last performed action.", + "hidden": true + }, + { + "name": "redo", + "description": "Re-does the last undone action.", + "hidden": true + }, + { + "name": "markUndoableState", + "description": "Marks last undoable state.", + "hidden": true + }, + { + "name": "focus", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to focus." } + ], + "description": "Focuses the given element.", + "hidden": true + }, + { + "name": "setFileInputFiles", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the file input node to set files for." }, + { "name": "files", "type": "array", "items": { "type": "string" }, "description": "Array of file paths to set." } + ], + "description": "Sets files for the given file input element.", + "hidden": true + }, + { + "name": "getBoxModel", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get box model for." } + ], + "returns": [ + { "name": "model", "$ref": "BoxModel", "description": "Box model for the node." } + ], + "description": "Returns boxes for the currently selected nodes.", + "hidden": true + }, + { + "name": "getNodeForLocation", + "parameters": [ + { "name": "x", "type": "integer", "description": "X coordinate." }, + { "name": "y", "type": "integer", "description": "Y coordinate." } + ], + "returns": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node at given coordinates." } + ], + "description": "Returns node id at given location.", + "hidden": true + } + ], + "events": [ + { + "name": "documentUpdated", + "description": "Fired when <code>Document</code> has been totally updated. Node ids are no longer valid." + }, + { + "name": "inspectNodeRequested", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to inspect." } + ], + "description": "Fired when the node should be inspected. This happens after call to <code>setInspectModeEnabled</code>.", + "hidden" : true + }, + { + "name": "setChildNodes", + "parameters": [ + { "name": "parentId", "$ref": "NodeId", "description": "Parent node id to populate with children." }, + { "name": "nodes", "type": "array", "items": { "$ref": "Node"}, "description": "Child nodes array." } + ], + "description": "Fired when backend wants to provide client with the missing DOM structure. This happens upon most of the calls requesting node ids." + }, + { + "name": "attributeModified", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, + { "name": "name", "type": "string", "description": "Attribute name." }, + { "name": "value", "type": "string", "description": "Attribute value." } + ], + "description": "Fired when <code>Element</code>'s attribute is modified." + }, + { + "name": "attributeRemoved", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, + { "name": "name", "type": "string", "description": "A ttribute name." } + ], + "description": "Fired when <code>Element</code>'s attribute is removed." + }, + { + "name": "inlineStyleInvalidated", + "parameters": [ + { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Ids of the nodes for which the inline styles have been invalidated." } + ], + "description": "Fired when <code>Element</code>'s inline style is modified via a CSS property modification.", + "hidden": true + }, + { + "name": "characterDataModified", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, + { "name": "characterData", "type": "string", "description": "New text value." } + ], + "description": "Mirrors <code>DOMCharacterDataModified</code> event." + }, + { + "name": "childNodeCountUpdated", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, + { "name": "childNodeCount", "type": "integer", "description": "New node count." } + ], + "description": "Fired when <code>Container</code>'s child node count has changed." + }, + { + "name": "childNodeInserted", + "parameters": [ + { "name": "parentNodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, + { "name": "previousNodeId", "$ref": "NodeId", "description": "If of the previous siblint." }, + { "name": "node", "$ref": "Node", "description": "Inserted node data." } + ], + "description": "Mirrors <code>DOMNodeInserted</code> event." + }, + { + "name": "childNodeRemoved", + "parameters": [ + { "name": "parentNodeId", "$ref": "NodeId", "description": "Parent id." }, + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has been removed." } + ], + "description": "Mirrors <code>DOMNodeRemoved</code> event." + }, + { + "name": "shadowRootPushed", + "parameters": [ + { "name": "hostId", "$ref": "NodeId", "description": "Host element id." }, + { "name": "root", "$ref": "Node", "description": "Shadow root." } + ], + "description": "Called when shadow root is pushed into the element.", + "hidden": true + }, + { + "name": "shadowRootPopped", + "parameters": [ + { "name": "hostId", "$ref": "NodeId", "description": "Host element id." }, + { "name": "rootId", "$ref": "NodeId", "description": "Shadow root id." } + ], + "description": "Called when shadow root is popped from the element.", + "hidden": true + } + ] + }, + { + "domain": "CSS", + "hidden": true, + "description": "This domain exposes CSS read/write operations. All CSS objects (stylesheets, rules, and styles) have an associated <code>id</code> used in subsequent operations on the related object. Each object type has a specific <code>id</code> structure, and those are not interchangeable between objects of different kinds. CSS objects can be loaded using the <code>get*ForNode()</code> calls (which accept a DOM node id). A client can also discover all the existing stylesheets with the <code>getAllStyleSheets()</code> method (or keeping track of the <code>styleSheetAdded</code>/<code>styleSheetRemoved</code> events) and subsequently load the required stylesheet contents using the <code>getStyleSheet[Text]()</code> methods.", + "types": [ + { + "id": "StyleSheetId", + "type": "string" + }, + { + "id": "CSSStyleId", + "type": "object", + "properties": [ + { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "Enclosing stylesheet identifier." }, + { "name": "ordinal", "type": "integer", "description": "The style ordinal within the stylesheet." } + ], + "description": "This object identifies a CSS style in a unique way." + }, + { + "id": "StyleSheetOrigin", + "type": "string", + "enum": ["user", "user-agent", "inspector", "regular"], + "description": "Stylesheet type: \"user\" for user stylesheets, \"user-agent\" for user-agent stylesheets, \"inspector\" for stylesheets created by the inspector (i.e. those holding the \"via inspector\" rules), \"regular\" for regular stylesheets." + }, + { + "id": "CSSRuleId", + "type": "object", + "properties": [ + { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "Enclosing stylesheet identifier." }, + { "name": "ordinal", "type": "integer", "description": "The rule ordinal within the stylesheet." } + ], + "description": "This object identifies a CSS rule in a unique way." + }, + { + "id": "PseudoIdMatches", + "type": "object", + "properties": [ + { "name": "pseudoId", "type": "integer", "description": "Pseudo style identifier (see <code>enum PseudoId</code> in <code>ComputedStyleConstants.h</code>)."}, + { "name": "matches", "type": "array", "items": { "$ref": "RuleMatch" }, "description": "Matches of CSS rules applicable to the pseudo style."} + ], + "description": "CSS rule collection for a single pseudo style." + }, + { + "id": "InheritedStyleEntry", + "type": "object", + "properties": [ + { "name": "inlineStyle", "$ref": "CSSStyle", "optional": true, "description": "The ancestor node's inline style, if any, in the style inheritance chain." }, + { "name": "matchedCSSRules", "type": "array", "items": { "$ref": "RuleMatch" }, "description": "Matches of CSS rules matching the ancestor node in the style inheritance chain." } + ], + "description": "CSS rule collection for a single pseudo style." + }, + { + "id": "RuleMatch", + "type": "object", + "properties": [ + { "name": "rule", "$ref": "CSSRule", "description": "CSS rule in the match." }, + { "name": "matchingSelectors", "type": "array", "items": { "type": "integer" }, "description": "Matching selector indices in the rule's selectorList selectors (0-based)." } + ], + "description": "Match data for a CSS rule." + }, + { + "id": "SelectorList", + "type": "object", + "properties": [ + { "name": "selectors", "type": "array", "items": { "type": "string" }, "description": "Selectors in the list." }, + { "name": "text", "type": "string", "description": "Rule selector text." }, + { "name": "range", "$ref": "SourceRange", "optional": true, "description": "Rule selector range in the underlying resource (if available)." } + ], + "description": "Selector list data." + }, + { + "id": "CSSStyleAttribute", + "type": "object", + "properties": [ + { "name": "name", "type": "string", "description": "DOM attribute name (e.g. \"width\")."}, + { "name": "style", "$ref": "CSSStyle", "description": "CSS style generated by the respective DOM attribute."} + ], + "description": "CSS style information for a DOM style attribute." + }, + { + "id": "CSSStyleSheetHeader", + "type": "object", + "properties": [ + { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "The stylesheet identifier."}, + { "name": "frameId", "$ref": "Page.FrameId", "description": "Owner frame identifier."}, + { "name": "sourceURL", "type": "string", "description": "Stylesheet resource URL."}, + { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with the stylesheet (if any)." }, + { "name": "origin", "$ref": "StyleSheetOrigin", "description": "Stylesheet origin."}, + { "name": "title", "type": "string", "description": "Stylesheet title."}, + { "name": "disabled", "type": "boolean", "description": "Denotes whether the stylesheet is disabled."}, + { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "Whether the sourceURL field value comes from the sourceURL comment." }, + { "name": "isInline", "type": "boolean", "description": "Whether this stylesheet is created for STYLE tag by parser. This flag is not set for document.written STYLE tags." }, + { "name": "startLine", "type": "number", "description": "Line offset of the stylesheet within the resource (zero based)." }, + { "name": "startColumn", "type": "number", "description": "Column offset of the stylesheet within the resource (zero based)." } + ], + "description": "CSS stylesheet metainformation." + }, + { + "id": "CSSStyleSheetBody", + "type": "object", + "properties": [ + { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "The stylesheet identifier."}, + { "name": "rules", "type": "array", "items": { "$ref": "CSSRule" }, "description": "Stylesheet resource URL."}, + { "name": "text", "type": "string", "optional": true, "description": "Stylesheet resource contents (if available)."} + ], + "description": "CSS stylesheet contents." + }, + { + "id": "CSSRule", + "type": "object", + "properties": [ + { "name": "ruleId", "$ref": "CSSRuleId", "optional": true, "description": "The CSS rule identifier (absent for user agent stylesheet and user-specified stylesheet rules)."}, + { "name": "selectorList", "$ref": "SelectorList", "description": "Rule selector data." }, + { "name": "sourceURL", "type": "string", "optional": true, "description": "Parent stylesheet resource URL (for regular rules)."}, + { "name": "origin", "$ref": "StyleSheetOrigin", "description": "Parent stylesheet's origin."}, + { "name": "style", "$ref": "CSSStyle", "description": "Associated style declaration." }, + { "name": "media", "type": "array", "items": { "$ref": "CSSMedia" }, "optional": true, "description": "Media list array (for rules involving media queries). The array enumerates media queries starting with the innermost one, going outwards." } + ], + "description": "CSS rule representation." + }, + { + "id": "SourceRange", + "type": "object", + "properties": [ + { "name": "startLine", "type": "integer", "description": "Start line of range." }, + { "name": "startColumn", "type": "integer", "description": "Start column of range (inclusive)." }, + { "name": "endLine", "type": "integer", "description": "End line of range" }, + { "name": "endColumn", "type": "integer", "description": "End column of range (exclusive)." } + ], + "description": "Text range within a resource. All numbers are zero-based." + }, + { + "id": "ShorthandEntry", + "type": "object", + "properties": [ + { "name": "name", "type": "string", "description": "Shorthand name." }, + { "name": "value", "type": "string", "description": "Shorthand value." } + ] + }, + { + "id": "CSSPropertyInfo", + "type": "object", + "properties": [ + { "name": "name", "type": "string", "description": "Property name." }, + { "name": "longhands", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Longhand property names." } + ] + }, + { + "id": "CSSComputedStyleProperty", + "type": "object", + "properties": [ + { "name": "name", "type": "string", "description": "Computed style property name." }, + { "name": "value", "type": "string", "description": "Computed style property value." } + ] + }, + { + "id": "CSSStyle", + "type": "object", + "properties": [ + { "name": "styleId", "$ref": "CSSStyleId", "optional": true, "description": "The CSS style identifier (absent for attribute styles)." }, + { "name": "cssProperties", "type": "array", "items": { "$ref": "CSSProperty" }, "description": "CSS properties in the style." }, + { "name": "shorthandEntries", "type": "array", "items": { "$ref": "ShorthandEntry" }, "description": "Computed values for all shorthands found in the style." }, + { "name": "cssText", "type": "string", "optional": true, "description": "Style declaration text (if available)." }, + { "name": "range", "$ref": "SourceRange", "optional": true, "description": "Style declaration range in the enclosing stylesheet (if available)." }, + { "name": "width", "type": "string", "optional": true, "description": "The effective \"width\" property value from this style." }, + { "name": "height", "type": "string", "optional": true, "description": "The effective \"height\" property value from this style." } + ], + "description": "CSS style representation." + }, + { + "id": "CSSProperty", + "type": "object", + "properties": [ + { "name": "name", "type": "string", "description": "The property name." }, + { "name": "value", "type": "string", "description": "The property value." }, + { "name": "priority", "type": "string", "optional": true, "description": "The property priority (implies \"\" if absent)." }, + { "name": "implicit", "type": "boolean", "optional": true, "description": "Whether the property is implicit (implies <code>false</code> if absent)." }, + { "name": "text", "type": "string", "optional": true, "description": "The full property text as specified in the style." }, + { "name": "parsedOk", "type": "boolean", "optional": true, "description": "Whether the property is understood by the browser (implies <code>true</code> if absent)." }, + { "name": "status", "type": "string", "enum": ["active", "inactive", "disabled", "style"], "optional": true, "description": "The property status: \"active\" if the property is effective in the style, \"inactive\" if the property is overridden by a same-named property in this style later on, \"disabled\" if the property is disabled by the user, \"style\" (implied if absent) if the property is reported by the browser rather than by the CSS source parser." }, + { "name": "range", "$ref": "SourceRange", "optional": true, "description": "The entire property range in the enclosing style declaration (if available)." } + ], + "description": "CSS property declaration data." + }, + { + "id": "CSSMedia", + "type": "object", + "properties": [ + { "name": "text", "type": "string", "description": "Media query text." }, + { "name": "source", "type": "string", "enum": ["mediaRule", "importRule", "linkedSheet", "inlineSheet"], "description": "Source of the media query: \"mediaRule\" if specified by a @media rule, \"importRule\" if specified by an @import rule, \"linkedSheet\" if specified by a \"media\" attribute in a linked stylesheet's LINK tag, \"inlineSheet\" if specified by a \"media\" attribute in an inline stylesheet's STYLE tag." }, + { "name": "sourceURL", "type": "string", "optional": true, "description": "URL of the document containing the media query description." }, + { "name": "range", "$ref": "SourceRange", "optional": true, "description": "The associated rule (@media or @import) header range in the enclosing stylesheet (if available)." }, + { "name": "parentStyleSheetId", "$ref": "StyleSheetId", "optional": true, "description": "Identifier of the stylesheet containing this object (if exists)." } + ], + "description": "CSS media query descriptor." + }, + { + "id": "SelectorProfileEntry", + "type": "object", + "properties": [ + { "name": "selector", "type": "string", "description": "CSS selector of the corresponding rule." }, + { "name": "url", "type": "string", "description": "URL of the resource containing the corresponding rule." }, + { "name": "lineNumber", "type": "integer", "description": "Selector line number in the resource for the corresponding rule." }, + { "name": "time", "type": "number", "description": "Total time this rule handling contributed to the browser running time during profiling (in milliseconds)." }, + { "name": "hitCount", "type": "integer", "description": "Number of times this rule was considered a candidate for matching against DOM elements." }, + { "name": "matchCount", "type": "integer", "description": "Number of times this rule actually matched a DOM element." } + ], + "description": "CSS selector profile entry." + }, + { + "id": "SelectorProfile", + "type": "object", + "properties": [ + { "name": "totalTime", "type": "number", "description": "Total processing time for all selectors in the profile (in milliseconds)." }, + { "name": "data", "type": "array", "items": { "$ref": "SelectorProfileEntry" }, "description": "CSS selector profile entries." } + ] + }, + { + "id": "Region", + "type": "object", + "properties": [ + { "name": "regionOverset", "type": "string", "enum": ["overset", "fit", "empty"], "description": "The \"overset\" attribute of a Named Flow." }, + { "name": "nodeId", "$ref": "DOM.NodeId", "description": "The corresponding DOM node id." } + ], + "description": "This object represents a region that flows from a Named Flow.", + "hidden": true + }, + { + "id": "NamedFlow", + "type": "object", + "properties": [ + { "name": "documentNodeId", "$ref": "DOM.NodeId", "description": "The document node id." }, + { "name": "name", "type": "string", "description": "Named Flow identifier." }, + { "name": "overset", "type": "boolean", "description": "The \"overset\" attribute of a Named Flow." }, + { "name": "content", "type": "array", "items": { "$ref": "DOM.NodeId" }, "description": "An array of nodes that flow into the Named Flow." }, + { "name": "regions", "type": "array", "items": { "$ref": "Region" }, "description": "An array of regions associated with the Named Flow." } + ], + "description": "This object represents a Named Flow.", + "hidden": true + }, + { + "id": "PlatformFontUsage", + "type": "object", + "properties": [ + { "name": "familyName", "type": "string", "description": "Font's family name reported by platform."}, + { "name": "glyphCount", "type": "number", "description": "Amount of glyphs that were rendered with this font."} + ], + "description": "Information about amount of glyphs that were rendered with given font." + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables the CSS agent for the given page. Clients should not assume that the CSS agent has been enabled until the result of this command is received." + }, + { + "name": "disable", + "description": "Disables the CSS agent for the given page." + }, + { + "name": "getMatchedStylesForNode", + "parameters": [ + { "name": "nodeId", "$ref": "DOM.NodeId" }, + { "name": "includePseudo", "type": "boolean", "optional": true, "description": "Whether to include pseudo styles (default: true)." }, + { "name": "includeInherited", "type": "boolean", "optional": true, "description": "Whether to include inherited styles (default: true)." } + ], + "returns": [ + { "name": "matchedCSSRules", "type": "array", "items": { "$ref": "RuleMatch" }, "optional": true, "description": "CSS rules matching this node, from all applicable stylesheets." }, + { "name": "pseudoElements", "type": "array", "items": { "$ref": "PseudoIdMatches" }, "optional": true, "description": "Pseudo style matches for this node." }, + { "name": "inherited", "type": "array", "items": { "$ref": "InheritedStyleEntry" }, "optional": true, "description": "A chain of inherited styles (from the immediate node parent up to the DOM tree root)." } + ], + "description": "Returns requested styles for a DOM node identified by <code>nodeId</code>." + }, + { + "name": "getInlineStylesForNode", + "parameters": [ + { "name": "nodeId", "$ref": "DOM.NodeId" } + ], + "returns": [ + { "name": "inlineStyle", "$ref": "CSSStyle", "optional": true, "description": "Inline style for the specified DOM node." }, + { "name": "attributesStyle", "$ref": "CSSStyle", "optional": true, "description": "Attribute-defined element style (e.g. resulting from \"width=20 height=100%\")."} + ], + "description": "Returns the styles defined inline (explicitly in the \"style\" attribute and implicitly, using DOM attributes) for a DOM node identified by <code>nodeId</code>." + }, + { + "name": "getComputedStyleForNode", + "parameters": [ + { "name": "nodeId", "$ref": "DOM.NodeId" } + ], + "returns": [ + { "name": "computedStyle", "type": "array", "items": { "$ref": "CSSComputedStyleProperty" }, "description": "Computed style for the specified DOM node." } + ], + "description": "Returns the computed style for a DOM node identified by <code>nodeId</code>." + }, + { + "name": "getPlatformFontsForNode", + "parameters": [ + { "name": "nodeId", "$ref": "DOM.NodeId" } + ], + "returns": [ + { "name": "cssFamilyName", "type": "string", "description": "Font family name which is determined by computed style." }, + { "name": "fonts", "type": "array", "items": { "$ref": "PlatformFontUsage"}, "description": "Usage statistics for every employed platform font." } + ], + "description": "Requests information about platform fonts which we used to render child TextNodes in the given node.", + "hidden": true + }, + { + "name": "getAllStyleSheets", + "returns": [ + { "name": "headers", "type": "array", "items": { "$ref": "CSSStyleSheetHeader" }, "description": "Descriptor entries for all available stylesheets." } + ], + "description": "Returns metainfo entries for all known stylesheets." + }, + { + "name": "getStyleSheet", + "parameters": [ + { "name": "styleSheetId", "$ref": "StyleSheetId" } + ], + "returns": [ + { "name": "styleSheet", "$ref": "CSSStyleSheetBody", "description": "Stylesheet contents for the specified <code>styleSheetId</code>." } + ], + "description": "Returns stylesheet data for the specified <code>styleSheetId</code>." + }, + { + "name": "getStyleSheetText", + "parameters": [ + { "name": "styleSheetId", "$ref": "StyleSheetId" } + ], + "returns": [ + { "name": "text", "type": "string", "description": "The stylesheet text." } + ], + "description": "Returns the current textual content and the URL for a stylesheet." + }, + { + "name": "setStyleSheetText", + "parameters": [ + { "name": "styleSheetId", "$ref": "StyleSheetId" }, + { "name": "text", "type": "string" } + ], + "description": "Sets the new stylesheet text, thereby invalidating all existing <code>CSSStyleId</code>'s and <code>CSSRuleId</code>'s contained by this stylesheet." + }, + { + "name": "setStyleText", + "parameters": [ + { "name": "styleId", "$ref": "CSSStyleId" }, + { "name": "text", "type": "string" } + ], + "returns": [ + { "name": "style", "$ref": "CSSStyle", "description": "The resulting style after the text modification." } + ], + "description": "Updates the CSSStyleDeclaration text." + }, + { + "name": "setPropertyText", + "parameters": [ + { "name": "styleId", "$ref": "CSSStyleId" }, + { "name": "propertyIndex", "type": "integer" }, + { "name": "text", "type": "string" }, + { "name": "overwrite", "type": "boolean" } + ], + "returns": [ + { "name": "style", "$ref": "CSSStyle", "description": "The resulting style after the property text modification." } + ], + "description": "Sets the new <code>text</code> for a property in the respective style, at offset <code>propertyIndex</code>. If <code>overwrite</code> is <code>true</code>, a property at the given offset is overwritten, otherwise inserted. <code>text</code> entirely replaces the property <code>name: value</code>." + }, + { + "name": "toggleProperty", + "parameters": [ + { "name": "styleId", "$ref": "CSSStyleId" }, + { "name": "propertyIndex", "type": "integer" }, + { "name": "disable", "type": "boolean" } + ], + "returns": [ + { "name": "style", "$ref": "CSSStyle", "description": "The resulting style after the property toggling." } + ], + "description": "Toggles the property in the respective style, at offset <code>propertyIndex</code>. The <code>disable</code> parameter denotes whether the property should be disabled (i.e. removed from the style declaration). If <code>disable == false</code>, the property gets put back into its original place in the style declaration." + }, + { + "name": "setRuleSelector", + "parameters": [ + { "name": "ruleId", "$ref": "CSSRuleId" }, + { "name": "selector", "type": "string" } + ], + "returns": [ + { "name": "rule", "$ref": "CSSRule", "description": "The resulting rule after the selector modification." } + ], + "description": "Modifies the rule selector." + }, + { + "name": "addRule", + "parameters": [ + { "name": "contextNodeId", "$ref": "DOM.NodeId" }, + { "name": "selector", "type": "string" } + ], + "returns": [ + { "name": "rule", "$ref": "CSSRule", "description": "The newly created rule." } + ], + "description": "Creates a new empty rule with the given <code>selector</code> in a special \"inspector\" stylesheet in the owner document of the context node." + }, + { + "name": "getSupportedCSSProperties", + "returns": [ + { "name": "cssProperties", "type": "array", "items": { "$ref": "CSSPropertyInfo" }, "description": "Supported property metainfo." } + ], + "description": "Returns all supported CSS property names." + }, + { + "name": "forcePseudoState", + "parameters": [ + { "name": "nodeId", "$ref": "DOM.NodeId", "description": "The element id for which to force the pseudo state." }, + { "name": "forcedPseudoClasses", "type": "array", "items": { "type": "string", "enum": ["active", "focus", "hover", "visited"] }, "description": "Element pseudo classes to force when computing the element's style." } + ], + "description": "Ensures that the given node will have specified pseudo-classes whenever its style is computed by the browser." + }, + { + "name": "getNamedFlowCollection", + "parameters": [ + { "name": "documentNodeId", "$ref": "DOM.NodeId", "description": "The document node id for which to get the Named Flow Collection." } + ], + "returns": [ + { "name": "namedFlows", "type": "array", "items": { "$ref": "NamedFlow" }, "description": "An array containing the Named Flows in the document." } + ], + "description": "Returns the Named Flows from the document.", + "hidden": true + } + ], + "events": [ + { + "name": "mediaQueryResultChanged", + "description": "Fires whenever a MediaQuery result changes (for example, after a browser window has been resized.) The current implementation considers only viewport-dependent media features." + }, + { + "name": "styleSheetChanged", + "parameters": [ + { "name": "styleSheetId", "$ref": "StyleSheetId" } + ], + "description": "Fired whenever a stylesheet is changed as a result of the client operation." + }, + { + "name": "styleSheetAdded", + "parameters": [ + { "name": "header", "$ref": "CSSStyleSheetHeader", "description": "Added stylesheet metainfo." } + ], + "description": "Fired whenever an active document stylesheet is added." + }, + { + "name": "styleSheetRemoved", + "parameters": [ + { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "Identifier of the removed stylesheet." } + ], + "description": "Fired whenever an active document stylesheet is removed." + }, + { + "name": "namedFlowCreated", + "parameters": [ + { "name": "namedFlow", "$ref": "NamedFlow", "description": "The new Named Flow." } + ], + "description": "Fires when a Named Flow is created.", + "hidden": true + }, + { + "name": "namedFlowRemoved", + "parameters": [ + { "name": "documentNodeId", "$ref": "DOM.NodeId", "description": "The document node id." }, + { "name": "flowName", "type": "string", "description": "Identifier of the removed Named Flow." } + ], + "description": "Fires when a Named Flow is removed: has no associated content nodes and regions.", + "hidden": true + }, + { + "name": "regionLayoutUpdated", + "parameters": [ + { "name": "namedFlow", "$ref": "NamedFlow", "description": "The Named Flow whose layout may have changed." } + ], + "description": "Fires when a Named Flow's layout may have changed.", + "hidden": true + }, + { + "name": "regionOversetChanged", + "parameters": [ + { "name": "namedFlow", "$ref": "NamedFlow", "description": "The Named Flow containing the regions whose regionOverset values changed." } + ], + "description": "Fires if any of the regionOverset values changed in a Named Flow's region chain.", + "hidden": true + } + ] + }, + { + "domain": "Debugger", + "description": "Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing breakpoints, stepping through execution, exploring stack traces, etc.", + "types": [ + { + "id": "BreakpointId", + "type": "string", + "description": "Breakpoint identifier." + }, + { + "id": "ScriptId", + "type": "string", + "description": "Unique script identifier." + }, + { + "id": "CallFrameId", + "type": "string", + "description": "Call frame identifier." + }, + { + "id": "Location", + "type": "object", + "properties": [ + { "name": "scriptId", "$ref": "ScriptId", "description": "Script identifier as reported in the <code>Debugger.scriptParsed</code>." }, + { "name": "lineNumber", "type": "integer", "description": "Line number in the script (0-based)." }, + { "name": "columnNumber", "type": "integer", "optional": true, "description": "Column number in the script (0-based)." } + ], + "description": "Location in the source code." + }, + { + "id": "FunctionDetails", + "hidden": true, + "type": "object", + "properties": [ + { "name": "location", "$ref": "Location", "description": "Location of the function." }, + { "name": "name", "type": "string", "optional": true, "description": "Name of the function. Not present for anonymous functions." }, + { "name": "displayName", "type": "string", "optional": true, "description": "Display name of the function(specified in 'displayName' property on the function object)." }, + { "name": "inferredName", "type": "string", "optional": true, "description": "Name of the function inferred from its initial assignment." }, + { "name": "scopeChain", "type": "array", "optional": true, "items": { "$ref": "Scope" }, "description": "Scope chain for this closure." } + ], + "description": "Information about the function." + }, + { + "id": "CallFrame", + "type": "object", + "properties": [ + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier. This identifier is only valid while the virtual machine is paused." }, + { "name": "functionName", "type": "string", "description": "Name of the JavaScript function called on this call frame." }, + { "name": "location", "$ref": "Location", "description": "Location in the source code." }, + { "name": "scopeChain", "type": "array", "items": { "$ref": "Scope" }, "description": "Scope chain for this call frame." }, + { "name": "this", "$ref": "Runtime.RemoteObject", "description": "<code>this</code> object for this call frame." } + ], + "description": "JavaScript call frame. Array of call frames form the call stack." + }, + { + "id": "Scope", + "type": "object", + "properties": [ + { "name": "type", "type": "string", "enum": ["global", "local", "with", "closure", "catch"], "description": "Scope type." }, + { "name": "object", "$ref": "Runtime.RemoteObject", "description": "Object representing the scope. For <code>global</code> and <code>with</code> scopes it represents the actual object; for the rest of the scopes, it is artificial transient object enumerating scope variables as its properties." } + ], + "description": "Scope description." + }, + { + "id": "SetScriptSourceError", + "type": "object", + "properties": [ + { "name": "compileError", "optional": true, "type": "object", "properties": + [ + { "name": "message", "type": "string", "description": "Compiler error message" }, + { "name": "lineNumber", "type": "integer", "description": "Compile error line number (1-based)" }, + { "name": "columnNumber", "type": "integer", "description": "Compile error column number (1-based)" } + ] + } + ], + "description": "Error data for setScriptSource command. compileError is a case type for uncompilable script source error.", + "hidden": true + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables debugger for the given page. Clients should not assume that the debugging has been enabled until the result for this command is received." + }, + { + "name": "disable", + "description": "Disables debugger for given page." + }, + { + "name": "setBreakpointsActive", + "parameters": [ + { "name": "active", "type": "boolean", "description": "New value for breakpoints active state." } + ], + "description": "Activates / deactivates all breakpoints on the page." + }, + { + "name": "setSkipAllPauses", + "hidden": true, + "parameters": [ + { "name": "skipped", "type": "boolean", "description": "New value for skip pauses state." }, + { "name": "untilReload", "type": "boolean", "optional": true, "description": "Whether page reload should set skipped to false." } + ], + "description": "Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc)." + }, + { + "name": "setBreakpointByUrl", + "parameters": [ + { "name": "lineNumber", "type": "integer", "description": "Line number to set breakpoint at." }, + { "name": "url", "type": "string", "optional": true, "description": "URL of the resources to set breakpoint on." }, + { "name": "urlRegex", "type": "string", "optional": true, "description": "Regex pattern for the URLs of the resources to set breakpoints on. Either <code>url</code> or <code>urlRegex</code> must be specified." }, + { "name": "columnNumber", "type": "integer", "optional": true, "description": "Offset in the line to set breakpoint at." }, + { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." }, + { "name": "isAntibreakpoint", "type": "boolean", "optional": true, "hidden": true, "description": "Creates pseudo-breakpoint that prevents debugger from pausing on exception at this location." } + ], + "returns": [ + { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, + { "name": "locations", "type": "array", "items": { "$ref": "Location"}, "description": "List of the locations this breakpoint resolved into upon addition." } + ], + "description": "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this command is issued, all existing parsed scripts will have breakpoints resolved and returned in <code>locations</code> property. Further matching script parsing will result in subsequent <code>breakpointResolved</code> events issued. This logical breakpoint will survive page reloads." + }, + { + "name": "setBreakpoint", + "parameters": [ + { "name": "location", "$ref": "Location", "description": "Location to set breakpoint in." }, + { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." } + ], + "returns": [ + { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, + { "name": "actualLocation", "$ref": "Location", "description": "Location this breakpoint resolved into." } + ], + "description": "Sets JavaScript breakpoint at a given location." + }, + { + "name": "removeBreakpoint", + "parameters": [ + { "name": "breakpointId", "$ref": "BreakpointId" } + ], + "description": "Removes JavaScript breakpoint." + }, + { + "name": "continueToLocation", + "parameters": [ + { "name": "location", "$ref": "Location", "description": "Location to continue to." }, + { "name": "interstatementLocation", "type": "boolean", "optional": true, "hidden": true, "description": "Allows breakpoints at the intemediate positions inside statements." } + ], + "description": "Continues execution until specific location is reached." + }, + { + "name": "stepOver", + "description": "Steps over the statement." + }, + { + "name": "stepInto", + "description": "Steps into the function call." + }, + { + "name": "stepOut", + "description": "Steps out of the function call." + }, + { + "name": "pause", + "description": "Stops on the next JavaScript statement." + }, + { + "name": "resume", + "description": "Resumes JavaScript execution." + }, + { + "name": "searchInContent", + "parameters": [ + { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to search in." }, + { "name": "query", "type": "string", "description": "String to search for." }, + { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." }, + { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." } + ], + "returns": [ + { "name": "result", "type": "array", "items": { "$ref": "Page.SearchMatch" }, "description": "List of search matches." } + ], + "description": "Searches for given string in script content." + }, + { + "name": "canSetScriptSource", + "returns": [ + { "name": "result", "type": "boolean", "description": "True if <code>setScriptSource</code> is supported." } + ], + "description": "Always returns true." + }, + { + "name": "setScriptSource", + "parameters": [ + { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to edit." }, + { "name": "scriptSource", "type": "string", "description": "New content of the script." }, + { "name": "preview", "type": "boolean", "optional": true, "description": " If true the change will not actually be applied. Preview mode may be used to get result description without actually modifying the code.", "hidden": true } + ], + "returns": [ + { "name": "callFrames", "type": "array", "optional": true, "items": { "$ref": "CallFrame"}, "description": "New stack trace in case editing has happened while VM was stopped." }, + { "name": "result", "type": "object", "optional": true, "description": "VM-specific description of the changes applied.", "hidden": true } + ], + "error": { + "$ref": "SetScriptSourceError" + }, + "description": "Edits JavaScript source live." + }, + { + "name": "restartFrame", + "parameters": [ + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." } + ], + "returns": [ + { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame"}, "description": "New stack trace." }, + { "name": "result", "type": "object", "description": "VM-specific description.", "hidden": true } + ], + "hidden": true, + "description": "Restarts particular call frame from the beginning." + }, + { + "name": "getScriptSource", + "parameters": [ + { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to get source for." } + ], + "returns": [ + { "name": "scriptSource", "type": "string", "description": "Script source." } + ], + "description": "Returns source for the script with given id." + }, + { + "name": "getFunctionDetails", + "hidden": true, + "parameters": [ + { "name": "functionId", "$ref": "Runtime.RemoteObjectId", "description": "Id of the function to get location for." } + ], + "returns": [ + { "name": "details", "$ref": "FunctionDetails", "description": "Information about the function." } + ], + "description": "Returns detailed informtation on given function." + }, + { + "name": "setPauseOnExceptions", + "parameters": [ + { "name": "state", "type": "string", "enum": ["none", "uncaught", "all"], "description": "Pause on exceptions mode." } + ], + "description": "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions or no exceptions. Initial pause on exceptions state is <code>none</code>." + }, + { + "name": "evaluateOnCallFrame", + "parameters": [ + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." }, + { "name": "expression", "type": "string", "description": "Expression to evaluate." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "String object group name to put result into (allows rapid releasing resulting object handles using <code>releaseObjectGroup</code>)." }, + { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false.", "hidden": true }, + { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether evaluation should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true }, + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." } + ], + "returns": [ + { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." }, + { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." } + ], + "description": "Evaluates expression on a given call frame." + }, + { + "name": "compileScript", + "hidden": true, + "parameters": [ + { "name": "expression", "type": "string", "description": "Expression to compile." }, + { "name": "sourceURL", "type": "string", "description": "Source url to be set for the script." } + ], + "returns": [ + { "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Id of the script." }, + { "name": "syntaxErrorMessage", "type": "string", "optional": true, "description": "Syntax error message if compilation failed." } + ], + "description": "Compiles expression." + }, + { + "name": "runScript", + "hidden": true, + "parameters": [ + { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to run." }, + { "name": "contextId", "$ref": "Runtime.ExecutionContextId", "optional": true, "description": "Specifies in which isolated context to perform script run. Each content script lives in an isolated context and this parameter may be used to specify one of those contexts. If the parameter is omitted or 0 the evaluation will be performed in the context of the inspected page." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, + { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether script run should stop on exceptions and mute console. Overrides setPauseOnException state." } + ], + "returns": [ + { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Run result." }, + { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the script run." } + ], + "description": "Runs script with given id in a given context." + }, + { + "name": "setOverlayMessage", + "parameters": [ + { "name": "message", "type": "string", "optional": true, "description": "Overlay message to display when paused in debugger." } + ], + "hidden": true, + "description": "Sets overlay message." + }, + { + "name": "setVariableValue", + "parameters": [ + { "name": "scopeNumber", "type": "integer", "description": "0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch' scope types are allowed. Other scopes could be manipulated manually." }, + { "name": "variableName", "type": "string", "description": "Variable name." }, + { "name": "newValue", "$ref": "Runtime.CallArgument", "description": "New variable value." }, + { "name": "callFrameId", "$ref": "CallFrameId", "optional": true, "description": "Id of callframe that holds variable." }, + { "name": "functionObjectId", "$ref": "Runtime.RemoteObjectId", "optional": true, "description": "Object id of closure (function) that holds variable." } + ], + "hidden": true, + "description": "Changes value of variable in a callframe or a closure. Either callframe or function must be specified. Object-based scopes are not supported and must be mutated manually." + }, + { + "name": "getStepInPositions", + "parameters": [ + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Id of a call frame where the current statement should be analized" } + ], + "returns": [ + { "name": "stepInPositions", "type": "array", "items": { "$ref": "Location" }, "optional": true, "description": "experimental" } + ], + "hidden": true, + "description": "Lists all positions where step-in is possible for a current statement in a specified call frame" + }, + { + "name": "getBacktrace", + "returns": [ + { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame"}, "description": "Call stack the virtual machine stopped on." } + ], + "hidden": true, + "description": "Returns call stack including variables changed since VM was paused. VM must be paused." + }, + { + "name": "skipStackFrames", + "parameters": [ + { "name": "script", "optional": true, "type": "string", "description": "Regular expression defining the scripts to ignore while stepping." } + ], + "hidden": true, + "description": "Makes backend skip steps in the sources with names matching given pattern. VM will try leave blacklisted scripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful." + } + ], + "events": [ + { + "name": "globalObjectCleared", + "description": "Called when global has been cleared and debugger client should reset its state. Happens upon navigation or reload." + }, + { + "name": "scriptParsed", + "parameters": [ + { "name": "scriptId", "$ref": "ScriptId", "description": "Identifier of the script parsed." }, + { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, + { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, + { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, + { "name": "endLine", "type": "integer", "description": "Last line of the script." }, + { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, + { "name": "isContentScript", "type": "boolean", "optional": true, "description": "Determines whether this script is a user extension script." }, + { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, + { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "hidden": true } + ], + "description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger." + }, + { + "name": "scriptFailedToParse", + "parameters": [ + { "name": "scriptId", "$ref": "ScriptId", "description": "Identifier of the script parsed." }, + { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, + { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, + { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, + { "name": "endLine", "type": "integer", "description": "Last line of the script." }, + { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, + { "name": "isContentScript", "type": "boolean", "optional": true, "description": "Determines whether this script is a user extension script." }, + { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, + { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "hidden": true } + ], + "description": "Fired when virtual machine fails to parse the script." + }, + { + "name": "breakpointResolved", + "parameters": [ + { "name": "breakpointId", "$ref": "BreakpointId", "description": "Breakpoint unique identifier." }, + { "name": "location", "$ref": "Location", "description": "Actual breakpoint location." } + ], + "description": "Fired when breakpoint is resolved to an actual script and location." + }, + { + "name": "paused", + "parameters": [ + { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." }, + { "name": "reason", "type": "string", "enum": [ "XHR", "DOM", "EventListener", "exception", "assert", "CSPViolation", "debugCommand", "other" ], "description": "Pause reason." }, + { "name": "data", "type": "object", "optional": true, "description": "Object containing break-specific auxiliary properties." }, + { "name": "hitBreakpoints", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Hit breakpoints IDs", "hidden": true } + ], + "description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria." + }, + { + "name": "resumed", + "description": "Fired when the virtual machine resumed execution." + } + ] + }, + { + "domain": "DOMDebugger", + "description": "DOM debugging allows setting breakpoints on particular DOM operations and events. JavaScript execution will stop on these operations as if there was a regular breakpoint set.", + "types": [ + { + "id": "DOMBreakpointType", + "type": "string", + "enum": ["subtree-modified", "attribute-modified", "node-removed"], + "description": "DOM breakpoint type." + } + ], + "commands": [ + { + "name": "setDOMBreakpoint", + "parameters": [ + { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Identifier of the node to set breakpoint on." }, + { "name": "type", "$ref": "DOMBreakpointType", "description": "Type of the operation to stop upon." } + ], + "description": "Sets breakpoint on particular operation with DOM." + }, + { + "name": "removeDOMBreakpoint", + "parameters": [ + { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Identifier of the node to remove breakpoint from." }, + { "name": "type", "$ref": "DOMBreakpointType", "description": "Type of the breakpoint to remove." } + ], + "description": "Removes DOM breakpoint that was set using <code>setDOMBreakpoint</code>." + }, + { + "name": "setEventListenerBreakpoint", + "parameters": [ + { "name": "eventName", "type": "string", "description": "DOM Event name to stop on (any DOM event will do)." } + ], + "description": "Sets breakpoint on particular DOM event." + }, + { + "name": "removeEventListenerBreakpoint", + "parameters": [ + { "name": "eventName", "type": "string", "description": "Event name." } + ], + "description": "Removes breakpoint on particular DOM event." + }, + { + "name": "setInstrumentationBreakpoint", + "parameters": [ + { "name": "eventName", "type": "string", "description": "Instrumentation name to stop on." } + ], + "description": "Sets breakpoint on particular native event.", + "hidden": true + }, + { + "name": "removeInstrumentationBreakpoint", + "parameters": [ + { "name": "eventName", "type": "string", "description": "Instrumentation name to stop on." } + ], + "description": "Removes breakpoint on particular native event.", + "hidden": true + }, + { + "name": "setXHRBreakpoint", + "parameters": [ + { "name": "url", "type": "string", "description": "Resource URL substring. All XHRs having this substring in the URL will get stopped upon." } + ], + "description": "Sets breakpoint on XMLHttpRequest." + }, + { + "name": "removeXHRBreakpoint", + "parameters": [ + { "name": "url", "type": "string", "description": "Resource URL substring." } + ], + "description": "Removes breakpoint from XMLHttpRequest." + } + ] + }, + { + "domain": "Profiler", + "hidden": true, + "types": [ + { + "id": "ProfileHeader", + "type": "object", + "description": "Profile header.", + "properties": [ + { "name": "title", "type": "string", "description": "Profile title." }, + { "name": "uid", "type": "integer", "description": "Unique identifier of the profile." } + ] + }, + { + "id": "CPUProfileNode", + "type": "object", + "description": "CPU Profile node. Holds callsite information, execution statistics and child nodes.", + "properties": [ + { "name": "functionName", "type": "string", "description": "Function name." }, + { "name": "scriptId", "$ref": "Debugger.ScriptId", "description": "Script identifier." }, + { "name": "url", "type": "string", "description": "URL." }, + { "name": "lineNumber", "type": "integer", "description": "Line number." }, + { "name": "hitCount", "type": "integer", "description": "Number of samples where this node was on top of the call stack." }, + { "name": "callUID", "type": "number", "description": "Call UID." }, + { "name": "children", "type": "array", "items": { "$ref": "CPUProfileNode" }, "description": "Child nodes." }, + { "name": "deoptReason", "type": "string", "description": "The reason of being not optimized. The function may be deoptimized or marked as don't optimize."}, + { "name": "id", "optional": true, "type": "integer", "description": "Unique id of the node." } + ] + }, + { + "id": "CPUProfile", + "type": "object", + "description": "Profile.", + "properties": [ + { "name": "head", "$ref": "CPUProfileNode" }, + { "name": "startTime", "type": "number", "description": "Profiling start time in seconds." }, + { "name": "endTime", "type": "number", "description": "Profiling end time in seconds." }, + { "name": "samples", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Ids of samples top nodes." } + ] + }, + { + "id": "HeapSnapshotObjectId", + "type": "string", + "description": "Heap snashot object id." + } + ], + "commands": [ + { + "name": "enable" + }, + { + "name": "disable" + }, + { + "name": "start" + }, + { + "name": "stop", + "returns": [ + { "name": "header", "$ref": "ProfileHeader", "description": "The header of the recorded profile."} + ] + }, + { + "name": "getProfileHeaders", + "returns": [ + { "name": "headers", "type": "array", "items": { "$ref": "ProfileHeader"} } + ] + }, + { + "name": "getCPUProfile", + "parameters": [ + { "name": "uid", "type": "integer" } + ], + "returns": [ + { "name": "profile", "$ref": "CPUProfile" } + ] + }, + { + "name": "removeProfile", + "parameters": [ + { "name": "type", "type": "string" }, + { "name": "uid", "type": "integer" } + ] + }, + { + "name": "clearProfiles" + } + ], + "events": [ + { + "name": "addProfileHeader", + "parameters": [ + { "name": "header", "$ref": "ProfileHeader" } + ] + }, + { + "name": "setRecordingProfile", + "parameters": [ + { "name": "isProfiling", "type": "boolean" } + ] + }, + { + "name": "resetProfiles" + } + ] + }, + { + "domain": "HeapProfiler", + "hidden": true, + "types": [ + { + "id": "ProfileHeader", + "type": "object", + "description": "Profile header.", + "properties": [ + { "name": "title", "type": "string", "description": "Profile title." }, + { "name": "uid", "type": "integer", "description": "Unique identifier of the profile." }, + { "name": "maxJSObjectId", "type": "integer", "optional": true, "description": "Last seen JS object Id." } + ] + }, + { + "id": "HeapSnapshotObjectId", + "type": "string", + "description": "Heap snashot object id." + } + ], + "commands": [ + { + "name": "getProfileHeaders", + "returns": [ + { "name": "headers", "type": "array", "items": { "$ref": "ProfileHeader"} } + ] + }, + { + "name": "startTrackingHeapObjects" + }, + { + "name": "stopTrackingHeapObjects" + }, + { + "name": "getHeapSnapshot", + "parameters": [ + { "name": "uid", "type": "integer" } + ] + }, + { + "name": "removeProfile", + "parameters": [ + { "name": "uid", "type": "integer" } + ] + }, + { + "name": "clearProfiles" + }, + { + "name": "takeHeapSnapshot", + "parameters": [ + { "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken." } + ] + }, + { + "name": "collectGarbage" + }, + { + "name": "getObjectByHeapObjectId", + "parameters": [ + { "name": "objectId", "$ref": "HeapSnapshotObjectId" }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." } + ], + "returns": [ + { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Evaluation result." } + ] + }, + { + "name": "getHeapObjectId", + "parameters": [ + { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Identifier of the object to get heap object id for." } + ], + "returns": [ + { "name": "heapSnapshotObjectId", "$ref": "HeapSnapshotObjectId", "description": "Id of the heap snapshot object corresponding to the passed remote object id." } + ] + } + ], + "events": [ + { + "name": "addProfileHeader", + "parameters": [ + { "name": "header", "$ref": "ProfileHeader" } + ] + }, + { + "name": "addHeapSnapshotChunk", + "parameters": [ + { "name": "uid", "type": "integer" }, + { "name": "chunk", "type": "string" } + ] + }, + { + "name": "finishHeapSnapshot", + "parameters": [ + { "name": "uid", "type": "integer" } + ] + }, + { + "name": "resetProfiles" + }, + { + "name": "reportHeapSnapshotProgress", + "parameters": [ + { "name": "done", "type": "integer" }, + { "name": "total", "type": "integer" } + ] + }, + { + "name": "lastSeenObjectId", + "description": "If heap objects tracking has been started then backend regulary sends a current value for last seen object id and corresponding timestamp. If the were changes in the heap since last event then one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event.", + "parameters": [ + { "name": "lastSeenObjectId", "type": "integer" }, + { "name": "timestamp", "type": "number" } + ] + }, + { + "name": "heapStatsUpdate", + "description": "If heap objects tracking has been started then backend may send update for one or more fragments", + "parameters": [ + { "name": "statsUpdate", "type": "array", "items": { "type": "integer" }, "description": "An array of triplets. Each triplet describes a fragment. The first integer is the fragment index, the second integer is a total count of objects for the fragment, the third integer is a total size of the objects for the fragment."} + ] + } + ] + }, + { + "domain": "Worker", + "hidden": true, + "types": [], + "commands": [ + { + "name": "enable" + }, + { + "name": "disable" + }, + { + "name": "sendMessageToWorker", + "parameters": [ + { "name": "workerId", "type": "integer" }, + { "name": "message", "type": "object" } + ] + }, + { + "name": "canInspectWorkers", + "description": "Tells whether browser supports workers inspection.", + "returns": [ + { "name": "result", "type": "boolean", "description": "True if browser has workers support." } + ] + }, + { + "name": "connectToWorker", + "parameters": [ + { "name": "workerId", "type": "integer" } + ] + }, + { + "name": "disconnectFromWorker", + "parameters": [ + { "name": "workerId", "type": "integer" } + ] + }, + { + "name": "setAutoconnectToWorkers", + "parameters": [ + { "name": "value", "type": "boolean" } + ] + } + ], + "events": [ + { + "name": "workerCreated", + "parameters": [ + { "name": "workerId", "type": "integer" }, + { "name": "url", "type": "string" }, + { "name": "inspectorConnected", "type": "boolean" } + ] + }, + { + "name": "workerTerminated", + "parameters": [ + { "name": "workerId", "type": "integer" } + ] + }, + { + "name": "dispatchMessageFromWorker", + "parameters": [ + { "name": "workerId", "type": "integer" }, + { "name": "message", "type": "object" } + ] + }, + { + "name": "disconnectedFromWorker" + } + ] + }, + { + "domain": "Canvas", + "hidden": true, + "types": [ + { + "id": "ResourceId", + "type": "string", + "description": "Unique resource identifier." + }, + { + "id": "ResourceStateDescriptor", + "type": "object", + "description": "Resource state descriptor.", + "properties": [ + { "name": "name", "type": "string", "description": "State name." }, + { "name": "enumValueForName", "type": "string", "optional": true, "description": "String representation of the enum value, if <code>name</code> stands for an enum." }, + { "name": "value", "$ref": "CallArgument", "optional": true, "description": "The value associated with the particular state." }, + { "name": "values", "type": "array", "items": { "$ref": "ResourceStateDescriptor" }, "optional": true, "description": "Array of values associated with the particular state. Either <code>value</code> or <code>values</code> will be specified." }, + { "name": "isArray", "type": "boolean", "optional": true, "description": "True iff the given <code>values</code> items stand for an array rather than a list of grouped states." } + ] + }, + { + "id": "ResourceState", + "type": "object", + "description": "Resource state.", + "properties": [ + { "name": "id", "$ref": "ResourceId" }, + { "name": "traceLogId", "$ref": "TraceLogId" }, + { "name": "descriptors", "type": "array", "items": { "$ref": "ResourceStateDescriptor" }, "optional": true, "description": "Describes current <code>Resource</code> state." }, + { "name": "imageURL", "type": "string", "optional": true, "description": "Screenshot image data URL." } + ] + }, + { + "id": "CallArgument", + "type": "object", + "properties": [ + { "name": "description", "type": "string", "description": "String representation of the object." }, + { "name": "enumName", "type": "string", "optional": true, "description": "Enum name, if any, that stands for the value (for example, a WebGL enum name)." }, + { "name": "resourceId", "$ref": "ResourceId", "optional": true, "description": "Resource identifier. Specified for <code>Resource</code> objects only." }, + { "name": "type", "type": "string", "optional": true, "enum": ["object", "function", "undefined", "string", "number", "boolean"], "description": "Object type. Specified for non <code>Resource</code> objects only." }, + { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date"], "description": "Object subtype hint. Specified for <code>object</code> type values only." }, + { "name": "remoteObject", "$ref": "Runtime.RemoteObject", "optional": true, "description": "The <code>RemoteObject</code>, if requested." } + ] + }, + { + "id": "Call", + "type": "object", + "properties": [ + { "name": "contextId", "$ref": "ResourceId" }, + { "name": "functionName", "type": "string", "optional": true }, + { "name": "arguments", "type": "array", "items": { "$ref": "CallArgument" }, "optional": true }, + { "name": "result", "$ref": "CallArgument", "optional": true }, + { "name": "isDrawingCall", "type": "boolean", "optional": true }, + { "name": "isFrameEndCall", "type": "boolean", "optional": true }, + { "name": "property", "type": "string", "optional": true }, + { "name": "value", "$ref": "CallArgument", "optional": true }, + { "name": "sourceURL", "type": "string", "optional": true }, + { "name": "lineNumber", "type": "integer", "optional": true }, + { "name": "columnNumber", "type": "integer", "optional": true } + ] + }, + { + "id": "TraceLogId", + "type": "string", + "description": "Unique trace log identifier." + }, + { + "id": "TraceLog", + "type": "object", + "properties": [ + { "name": "id", "$ref": "TraceLogId" }, + { "name": "calls", "type": "array", "items": { "$ref": "Call" } }, + { "name": "contexts", "type": "array", "items": { "$ref": "CallArgument" } }, + { "name": "startOffset", "type": "integer" }, + { "name": "alive", "type": "boolean" }, + { "name": "totalAvailableCalls", "type": "number" } + ] + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables Canvas inspection." + }, + { + "name": "disable", + "description": "Disables Canvas inspection." + }, + { + "name": "dropTraceLog", + "parameters": [ + { "name": "traceLogId", "$ref": "TraceLogId" } + ] + }, + { + "name": "hasUninstrumentedCanvases", + "returns": [ + { "name": "result", "type": "boolean" } + ], + "description": "Checks if there is any uninstrumented canvas in the inspected page." + }, + { + "name": "captureFrame", + "parameters": [ + { "name": "frameId", "$ref": "Page.FrameId", "optional": true, "description": "Identifier of the frame containing document whose canvases are to be captured. If omitted, main frame is assumed." } + ], + "returns": [ + { "name": "traceLogId", "$ref": "TraceLogId", "description": "Identifier of the trace log containing captured canvas calls." } + ], + "description": "Starts (or continues) a canvas frame capturing which will be stopped automatically after the next frame is prepared." + }, + { + "name": "startCapturing", + "parameters": [ + { "name": "frameId", "$ref": "Page.FrameId", "optional": true, "description": "Identifier of the frame containing document whose canvases are to be captured. If omitted, main frame is assumed." } + ], + "returns": [ + { "name": "traceLogId", "$ref": "TraceLogId", "description": "Identifier of the trace log containing captured canvas calls." } + ], + "description": "Starts (or continues) consecutive canvas frames capturing. The capturing is stopped by the corresponding stopCapturing command." + }, + { + "name": "stopCapturing", + "parameters": [ + { "name": "traceLogId", "$ref": "TraceLogId" } + ] + }, + { + "name": "getTraceLog", + "parameters": [ + { "name": "traceLogId", "$ref": "TraceLogId" }, + { "name": "startOffset", "type": "integer", "optional": true }, + { "name": "maxLength", "type": "integer", "optional": true } + ], + "returns": [ + { "name": "traceLog", "$ref": "TraceLog" } + ] + }, + { + "name": "replayTraceLog", + "parameters": [ + { "name": "traceLogId", "$ref": "TraceLogId" }, + { "name": "stepNo", "type": "integer", "description": "Last call index in the trace log to replay (zero based)." } + ], + "returns": [ + { "name": "resourceState", "$ref": "ResourceState" }, + { "name": "replayTime", "type": "number", "description": "Replay time (in milliseconds)." } + ] + }, + { + "name": "getResourceState", + "parameters": [ + { "name": "traceLogId", "$ref": "TraceLogId" }, + { "name": "resourceId", "$ref": "ResourceId" } + ], + "returns": [ + { "name": "resourceState", "$ref": "ResourceState" } + ] + }, + { + "name": "evaluateTraceLogCallArgument", + "parameters": [ + { "name": "traceLogId", "$ref": "TraceLogId" }, + { "name": "callIndex", "type": "integer", "description": "Index of the call to evaluate on (zero based)." }, + { "name": "argumentIndex", "type": "integer", "description": "Index of the argument to evaluate (zero based). Provide <code>-1</code> to evaluate call result." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "String object group name to put result into (allows rapid releasing resulting object handles using <code>Runtime.releaseObjectGroup</code>)." } + ], + "returns": [ + { "name": "result", "$ref": "Runtime.RemoteObject", "optional": true, "description": "Object wrapper for the evaluation result." }, + { "name": "resourceState", "$ref": "ResourceState", "optional": true, "description": "State of the <code>Resource</code> object." } + ], + "description": "Evaluates a given trace call argument or its result." + } + ], + "events": [ + { + "name": "contextCreated", + "parameters": [ + { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame containing a canvas with a context." } + ], + "description": "Fired when a canvas context has been created in the given frame. The context may not be instrumented (see hasUninstrumentedCanvases command)." + }, + { + "name": "traceLogsRemoved", + "parameters": [ + { "name": "frameId", "$ref": "Page.FrameId", "optional": true, "description": "If given, trace logs from the given frame were removed." }, + { "name": "traceLogId", "$ref": "TraceLogId", "optional": true, "description": "If given, trace log with the given ID was removed." } + ], + "description": "Fired when a set of trace logs were removed from the backend. If no parameters are given, all trace logs were removed." + } + ] + }, + { + "domain": "Input", + "types": [ + { + "id": "TouchPoint", + "type": "object", + "hidden": true, + "properties": [ + { "name": "state", "type": "string", "enum": ["touchPressed", "touchReleased", "touchMoved", "touchStationary", "touchCancelled"], "description": "State of the touch point." }, + { "name": "x", "type": "integer", "description": "X coordinate of the event relative to the main frame's viewport."}, + { "name": "y", "type": "integer", "description": "Y coordinate of the event relative to the main frame's viewport. 0 refers to the top of the viewport and Y increases as it proceeds towards the bottom of the viewport."}, + { "name": "radiusX", "type": "integer", "optional": true, "description": "X radius of the touch area (default: 1)."}, + { "name": "radiusY", "type": "integer", "optional": true, "description": "Y radius of the touch area (default: 1)."}, + { "name": "rotationAngle", "type": "number", "optional": true, "description": "Rotation angle (default: 0.0)."}, + { "name": "force", "type": "number", "optional": true, "description": "Force (default: 1.0)."}, + { "name": "id", "type": "number", "optional": true, "description": "Identifier used to track touch sources between events, must be unique within an event."} + ] + } + ], + "commands": [ + { + "name": "dispatchKeyEvent", + "parameters": [ + { "name": "type", "type": "string", "enum": ["keyDown", "keyUp", "rawKeyDown", "char"], "description": "Type of the key event." }, + { "name": "modifiers", "type": "integer", "optional": true, "description": "Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 (default: 0)." }, + { "name": "timestamp", "type": "number", "optional": true, "description": "Time at which the event occurred. Measured in UTC time in seconds since January 1, 1970 (default: current time)." }, + { "name": "text", "type": "string", "optional": true, "description": "Text as generated by processing a virtual key code with a keyboard layout. Not needed for for <code>keyUp</code> and <code>rawKeyDown</code> events (default: \"\")" }, + { "name": "unmodifiedText", "type": "string", "optional": true, "description": "Text that would have been generated by the keyboard if no modifiers were pressed (except for shift). Useful for shortcut (accelerator) key handling (default: \"\")." }, + { "name": "keyIdentifier", "type": "string", "optional": true, "description": "Unique key identifier (e.g., 'U+0041') (default: \"\")." }, + { "name": "windowsVirtualKeyCode", "type": "integer", "optional": true, "description": "Windows virtual key code (default: 0)." }, + { "name": "nativeVirtualKeyCode", "type": "integer", "optional": true, "description": "Native virtual key code (default: 0)." }, + { "name": "autoRepeat", "type": "boolean", "optional": true, "description": "Whether the event was generated from auto repeat (default: false)." }, + { "name": "isKeypad", "type": "boolean", "optional": true, "description": "Whether the event was generated from the keypad (default: false)." }, + { "name": "isSystemKey", "type": "boolean", "optional": true, "description": "Whether the event was a system key event (default: false)." } + ], + "description": "Dispatches a key event to the page." + }, + { + "name": "dispatchMouseEvent", + "parameters": [ + { "name": "type", "type": "string", "enum": ["mousePressed", "mouseReleased", "mouseMoved"], "description": "Type of the mouse event." }, + { "name": "x", "type": "integer", "description": "X coordinate of the event relative to the main frame's viewport."}, + { "name": "y", "type": "integer", "description": "Y coordinate of the event relative to the main frame's viewport. 0 refers to the top of the viewport and Y increases as it proceeds towards the bottom of the viewport."}, + { "name": "modifiers", "type": "integer", "optional": true, "description": "Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 (default: 0)." }, + { "name": "timestamp", "type": "number", "optional": true, "description": "Time at which the event occurred. Measured in UTC time in seconds since January 1, 1970 (default: current time)." }, + { "name": "button", "type": "string", "enum": ["none", "left", "middle", "right"], "optional": true, "description": "Mouse button (default: \"none\")." }, + { "name": "clickCount", "type": "integer", "optional": true, "description": "Number of times the mouse button was clicked (default: 0)." }, + { "name": "deviceSpace", "type": "boolean", "optional": true, "hidden": true, "description": "If true, x and y are given in dip wrt current viewport." } + ], + "description": "Dispatches a mouse event to the page." + }, + { + "name": "dispatchTouchEvent", + "hidden": true, + "parameters": [ + { "name": "type", "type": "string", "enum": ["touchStart", "touchEnd", "touchMove"], "description": "Type of the touch event." }, + { "name": "touchPoints", "type": "array", "items": { "$ref": "TouchPoint" }, "description": "Touch points." }, + { "name": "modifiers", "type": "integer", "optional": true, "description": "Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 (default: 0)." }, + { "name": "timestamp", "type": "number", "optional": true, "description": "Time at which the event occurred. Measured in UTC time in seconds since January 1, 1970 (default: current time)." } + ], + "description": "Dispatches a touch event to the page." + }, + { + "name": "dispatchGestureEvent", + "hidden": true, + "parameters": [ + { "name": "type", "type": "string", "enum": ["scrollBegin", "scrollEnd", "scrollUpdate", "tapDown", "tap", "pinchBegin", "pinchEnd", "pinchUpdate"], "description": "Type of the gesture event." }, + { "name": "x", "type": "integer", "description": "X coordinate relative to the screen's viewport."}, + { "name": "y", "type": "integer", "description": "Y coordinate relative to the screen's viewport."}, + { "name": "timestamp", "type": "number", "optional": true, "description": "Time at which the event occurred. Measured in UTC time in seconds since January 1, 1970 (default: current time)." }, + { "name": "deltaX", "type": "integer", "optional": true, "description": "Delta X where apllies."}, + { "name": "deltaY", "type": "integer", "optional": true, "description": "Delta Y where apllies."}, + { "name": "pinchScale", "type": "number", "optional": true, "description": "Pinch scale." } + ], + "description": "Dispatches a gesture event to the page." + } + ], + "events": [] + }, + { + "domain": "LayerTree", + "hidden": true, + "types": [ + { + "id": "LayerId", + "type": "string", + "description": "Unique Layer identifier." + }, + { + "id": "Layer", + "type": "object", + "description": "Information about a compositing layer.", + "properties": [ + { "name": "layerId", "$ref": "LayerId", "description": "The unique id for this layer." }, + { "name": "parentLayerId", "$ref": "LayerId", "optional": true, "description": "The id of parent (not present for root)." }, + { "name": "nodeId", "$ref": "DOM.NodeId", "optional": true, "description": "The id for the node associated with this layer." }, + { "name": "offsetX", "type": "number", "description": "Offset from parent layer, X coordinate." }, + { "name": "offsetY", "type": "number", "description": "Offset from parent layer, X coordinate." }, + { "name": "width", "type": "number", "description": "Layer width." }, + { "name": "height", "type": "number", "description": "Layer height." }, + { "name": "transform", "type": "array", "items": { "type": "number" }, "minItems": 16, "maxItems": 16, "optional": true, "description": "Transformation matrix for layer, default is identity matrix" }, + { "name": "anchorX", "type": "number", "optional": true, "description": "Transform anchor point X, absent if no transform specified" }, + { "name": "anchorY", "type": "number", "optional": true, "description": "Transform anchor point Y, absent if no transform specified" }, + { "name": "anchorZ", "type": "number", "optional": true, "description": "Transform anchor point Z, absent if no transform specified" }, + { "name": "paintCount", "type": "integer", "description": "Indicates how many time this layer has painted." }, + { "name": "invisible", "type": "boolean", "optional": true, "description": "Set if layer is not visible." } + ] + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables compositing tree inspection." + }, + { + "name": "disable", + "description": "Disables compositing tree inspection." + }, + { + "name": "getLayers", + "parameters": [ + { "name": "nodeId", "optional": true, "$ref": "DOM.NodeId", "description": "Root of the subtree for which we want to gather layers (return entire tree if not specified)" } + ], + "description": "Returns the layer tree structure of the current page.", + "returns": [ + { "name": "layers", "type": "array", "items": { "$ref": "Layer" }, "description": "Child layers." } + ] + }, + { + "name": "compositingReasons", + "parameters": [ + { "name": "layerId", "$ref": "LayerId", "description": "The id of the layer for which we want to get the reasons it was composited." } + ], + "description": "Provides the reasons why the given layer was composited.", + "returns": [ + { "name": "compositingReasons", "type": "array", "items": { "type": "string" }, "description": "A list of strings specifying reasons for the given layer to become composited." } + ] + } + ], + "events": [ + { + "name": "layerTreeDidChange" + } + ] + }, + { + "domain": "Tracing", + "hidden": true, + "commands": [ + { + "name": "start", + "description": "Strart trace events collection.", + "parameters": [ + { "name": "categories", "type": "string", "description": "Category/tag filter" } + ] + }, + { + "name": "end", + "description": "Stop trace events collection." + } + ], + "events": [ + { + "name": "dataCollected", + "parameters": [ + { "name": "value", "type": "array", "items": { "type": "object" } } + ] + }, + { + "name": "tracingComplete" + } + ] + }] +} diff --git a/deps/v8_inspector/devtools/protocol.json b/deps/v8_inspector/devtools/protocol.json new file mode 100644 index 00000000000000..213274bd7c8adc --- /dev/null +++ b/deps/v8_inspector/devtools/protocol.json @@ -0,0 +1,5167 @@ +{ + "version": { "major": "1", "minor": "1" }, + "domains": [{ + "domain": "Inspector", + "hidden": true, + "types": [], + "commands": [ + { + "name": "enable", + "description": "Enables inspector domain notifications.", + "handlers": ["browser"] + }, + { + "name": "disable", + "description": "Disables inspector domain notifications.", + "handlers": ["browser"] + } + ], + "events": [ + { + "name": "detached", + "description": "Fired when remote debugging connection is about to be terminated. Contains detach reason.", + "parameters": [ + { "name": "reason", "type": "string", "description": "The reason why connection has been terminated." } + ], + "handlers": ["browser"] + }, + { + "name": "targetCrashed", + "description": "Fired when debugging target has crashed", + "handlers": ["browser"] + } + ] + }, + { + "domain": "Memory", + "hidden": true, + "types": [ + { + "id": "PressureLevel", + "type": "string", + "enum": ["moderate", "critical"], + "description": "Memory pressure level." + } + ], + "commands": [ + { + "name": "getDOMCounters", + "returns": [ + { "name": "documents", "type": "integer" }, + { "name": "nodes", "type": "integer" }, + { "name": "jsEventListeners", "type": "integer" } + ] + }, + { + "name": "setPressureNotificationsSuppressed", + "description": "Enable/disable suppressing memory pressure notifications in all processes.", + "parameters": [ + { "name": "suppressed", "type": "boolean", "description": "If true, memory pressure notifications will be suppressed."} + ], + "handlers": ["browser"] + }, + { + "name": "simulatePressureNotification", + "description": "Simulate a memory pressure notification in all processes.", + "parameters": [ + { "name": "level", "$ref": "PressureLevel", "description": "Memory pressure level of the notification." } + ], + "handlers": ["browser"] + } + ] + }, + { + "domain": "Page", + "description": "Actions and events related to the inspected page belong to the page domain.", + "types": [ + { + "id": "ResourceType", + "type": "string", + "enum": ["Document", "Stylesheet", "Image", "Media", "Font", "Script", "TextTrack", "XHR", "Fetch", "EventSource", "WebSocket", "Manifest", "Other"], + "description": "Resource type as it was perceived by the rendering engine." + }, + { + "id": "FrameId", + "type": "string", + "description": "Unique frame identifier." + }, + { + "id": "Frame", + "type": "object", + "description": "Information about the Frame on the page.", + "properties": [ + { "name": "id", "type": "string", "description": "Frame unique identifier." }, + { "name": "parentId", "type": "string", "optional": true, "description": "Parent frame identifier." }, + { "name": "loaderId", "$ref": "Network.LoaderId", "description": "Identifier of the loader associated with this frame." }, + { "name": "name", "type": "string", "optional": true, "description": "Frame's name as specified in the tag." }, + { "name": "url", "type": "string", "description": "Frame document's URL." }, + { "name": "securityOrigin", "type": "string", "description": "Frame document's security origin." }, + { "name": "mimeType", "type": "string", "description": "Frame document's mimeType as determined by the browser." } + ] + }, + { + "id": "FrameResource", + "type": "object", + "description": "Information about the Resource on the page.", + "properties": [ + { "name": "url", "type": "string", "description": "Resource URL." }, + { "name": "type", "$ref": "ResourceType", "description": "Type of this resource." }, + { "name": "mimeType", "type": "string", "description": "Resource mimeType as determined by the browser." }, + { "name": "failed", "type": "boolean", "optional": true, "description": "True if the resource failed to load." }, + { "name": "canceled", "type": "boolean", "optional": true, "description": "True if the resource was canceled during loading." } + ], + "hidden": true + }, + { + "id": "FrameResourceTree", + "type": "object", + "description": "Information about the Frame hierarchy along with their cached resources.", + "properties": [ + { "name": "frame", "$ref": "Frame", "description": "Frame information for this tree item." }, + { "name": "childFrames", "type": "array", "optional": true, "items": { "$ref": "FrameResourceTree" }, "description": "Child frames." }, + { "name": "resources", "type": "array", "items": { "$ref": "FrameResource" }, "description": "Information about frame resources." } + ], + "hidden": true + }, + { + "id": "ScriptIdentifier", + "type": "string", + "description": "Unique script identifier.", + "hidden": true + }, + { + "id": "NavigationEntry", + "type": "object", + "description": "Navigation history entry.", + "properties": [ + { "name": "id", "type": "integer", "description": "Unique id of the navigation history entry." }, + { "name": "url", "type": "string", "description": "URL of the navigation history entry." }, + { "name": "title", "type": "string", "description": "Title of the navigation history entry." } + ], + "hidden": true + }, + { + "id": "ScreencastFrameMetadata", + "type": "object", + "description": "Screencast frame metadata.", + "properties": [ + { "name": "offsetTop", "type": "number", "hidden": true, "description": "Top offset in DIP." }, + { "name": "pageScaleFactor", "type": "number", "hidden": true, "description": "Page scale factor." }, + { "name": "deviceWidth", "type": "number", "hidden": true, "description": "Device screen width in DIP." }, + { "name": "deviceHeight", "type": "number", "hidden": true, "description": "Device screen height in DIP." }, + { "name": "scrollOffsetX", "type": "number", "hidden": true, "description": "Position of horizontal scroll in CSS pixels." }, + { "name": "scrollOffsetY", "type": "number", "hidden": true, "description": "Position of vertical scroll in CSS pixels." }, + { "name": "timestamp", "type": "number", "optional": true, "hidden": true, "description": "Frame swap timestamp." } + ], + "hidden": true + }, + { + "id": "DialogType", + "description": "Javascript dialog type.", + "type": "string", + "enum": ["alert", "confirm", "prompt", "beforeunload"], + "hidden": true + }, + { + "id": "AppManifestError", + "description": "Error while paring app manifest.", + "type": "object", + "properties": [ + { "name": "message", "type": "string", "description": "Error message." }, + { "name": "critical", "type": "integer", "description": "If criticial, this is a non-recoverable parse error." }, + { "name": "line", "type": "integer", "description": "Error line." }, + { "name": "column", "type": "integer", "description": "Error column." } + ], + "hidden": true + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables page domain notifications.", + "handlers": ["browser", "renderer"] + }, + { + "name": "disable", + "description": "Disables page domain notifications.", + "handlers": ["browser", "renderer"] + }, + { + "name": "addScriptToEvaluateOnLoad", + "parameters": [ + { "name": "scriptSource", "type": "string" } + ], + "returns": [ + { "name": "identifier", "$ref": "ScriptIdentifier", "description": "Identifier of the added script." } + ], + "hidden": true + }, + { + "name": "removeScriptToEvaluateOnLoad", + "parameters": [ + { "name": "identifier", "$ref": "ScriptIdentifier" } + ], + "hidden": true + }, + { + "name": "setAutoAttachToCreatedPages", + "parameters": [ + { "name": "autoAttach", "type": "boolean", "description": "If true, browser will open a new inspector window for every page created from this one." } + ], + "description": "Controls whether browser will open a new inspector window for connected pages.", + "hidden": true + }, + { + "name": "reload", + "parameters": [ + { "name": "ignoreCache", "type": "boolean", "optional": true, "description": "If true, browser cache is ignored (as if the user pressed Shift+refresh)." }, + { "name": "scriptToEvaluateOnLoad", "type": "string", "optional": true, "description": "If set, the script will be injected into all frames of the inspected page after reload." } + ], + "description": "Reloads given page optionally ignoring the cache.", + "handlers": ["browser", "renderer"] + }, + { + "name": "navigate", + "parameters": [ + { "name": "url", "type": "string", "description": "URL to navigate the page to." } + ], + "returns": [ + { "name": "frameId", "$ref": "FrameId", "hidden": true, "description": "Frame id that will be navigated." } + ], + "description": "Navigates current page to the given URL.", + "handlers": ["browser", "renderer"] + }, + { + "name": "getNavigationHistory", + "returns": [ + { "name": "currentIndex", "type": "integer", "description": "Index of the current navigation history entry." }, + { "name": "entries", "type": "array", "items": { "$ref": "NavigationEntry" }, "description": "Array of navigation history entries." } + ], + "description": "Returns navigation history for the current page.", + "hidden": true, + "handlers": ["browser"] + }, + { + "name": "navigateToHistoryEntry", + "parameters": [ + { "name": "entryId", "type": "integer", "description": "Unique id of the entry to navigate to." } + ], + "description": "Navigates current page to the given history entry.", + "hidden": true, + "handlers": ["browser"] + }, + { + "name": "getCookies", + "returns": [ + { "name": "cookies", "type": "array", "items": { "$ref": "Network.Cookie" }, "description": "Array of cookie objects." } + ], + "description": "Returns all browser cookies. Depending on the backend support, will return detailed cookie information in the <code>cookies</code> field.", + "handlers": ["browser"], + "async": true, + "hidden": true, + "redirect": "Network" + }, + { + "name": "deleteCookie", + "parameters": [ + { "name": "cookieName", "type": "string", "description": "Name of the cookie to remove." }, + { "name": "url", "type": "string", "description": "URL to match cooke domain and path." } + ], + "description": "Deletes browser cookie with given name, domain and path.", + "handlers": ["browser"], + "async": true, + "hidden": true, + "redirect": "Network" + }, + { + "name": "getResourceTree", + "description": "Returns present frame / resource tree structure.", + "returns": [ + { "name": "frameTree", "$ref": "FrameResourceTree", "description": "Present frame / resource tree structure." } + ], + "hidden": true + }, + { + "name": "getResourceContent", + "async": true, + "description": "Returns content of the given resource.", + "parameters": [ + { "name": "frameId", "$ref": "FrameId", "description": "Frame id to get resource for." }, + { "name": "url", "type": "string", "description": "URL of the resource to get content for." } + ], + "returns": [ + { "name": "content", "type": "string", "description": "Resource content." }, + { "name": "base64Encoded", "type": "boolean", "description": "True, if content was served as base64." } + ], + "hidden": true + }, + { + "name": "searchInResource", + "async": true, + "description": "Searches for given string in resource content.", + "parameters": [ + { "name": "frameId", "$ref": "FrameId", "description": "Frame id for resource to search in." }, + { "name": "url", "type": "string", "description": "URL of the resource to search in." }, + { "name": "query", "type": "string", "description": "String to search for." }, + { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." }, + { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." } + ], + "returns": [ + { "name": "result", "type": "array", "items": { "$ref": "Debugger.SearchMatch" }, "description": "List of search matches." } + ], + "hidden": true + }, + { + "name": "setDocumentContent", + "description": "Sets given markup as the document's HTML.", + "parameters": [ + { "name": "frameId", "$ref": "FrameId", "description": "Frame id to set HTML for." }, + { "name": "html", "type": "string", "description": "HTML content to set." } + ], + "hidden": true + }, + { + "name": "setDeviceMetricsOverride", + "description": "Overrides the values of device screen dimensions (window.screen.width, window.screen.height, window.innerWidth, window.innerHeight, and \"device-width\"/\"device-height\"-related CSS media query results).", + "parameters": [ + { "name": "width", "type": "integer", "description": "Overriding width value in pixels (minimum 0, maximum 10000000). 0 disables the override." }, + { "name": "height", "type": "integer", "description": "Overriding height value in pixels (minimum 0, maximum 10000000). 0 disables the override." }, + { "name": "deviceScaleFactor", "type": "number", "description": "Overriding device scale factor value. 0 disables the override." }, + { "name": "mobile", "type": "boolean", "description": "Whether to emulate mobile device. This includes viewport meta tag, overlay scrollbars, text autosizing and more." }, + { "name": "fitWindow", "type": "boolean", "description": "Whether a view that exceeds the available browser window area should be scaled down to fit." }, + { "name": "scale", "type": "number", "optional": true, "description": "Scale to apply to resulting view image. Ignored in |fitWindow| mode." }, + { "name": "offsetX", "type": "number", "optional": true, "description": "X offset to shift resulting view image by. Ignored in |fitWindow| mode." }, + { "name": "offsetY", "type": "number", "optional": true, "description": "Y offset to shift resulting view image by. Ignored in |fitWindow| mode." }, + { "name": "screenWidth", "type": "integer", "optional": true, "description": "Overriding screen width value in pixels (minimum 0, maximum 10000000). Only used for |mobile==true|." }, + { "name": "screenHeight", "type": "integer", "optional": true, "description": "Overriding screen height value in pixels (minimum 0, maximum 10000000). Only used for |mobile==true|." }, + { "name": "positionX", "type": "integer", "optional": true, "description": "Overriding view X position on screen in pixels (minimum 0, maximum 10000000). Only used for |mobile==true|." }, + { "name": "positionY", "type": "integer", "optional": true, "description": "Overriding view Y position on screen in pixels (minimum 0, maximum 10000000). Only used for |mobile==true|." }, + { "name": "screenOrientation", "$ref": "Emulation.ScreenOrientation", "optional": true, "description": "Screen orientation override." } + ], + "handlers": ["browser"], + "redirect": "Emulation", + "hidden": true + }, + { + "name": "clearDeviceMetricsOverride", + "description": "Clears the overriden device metrics.", + "handlers": ["browser"], + "redirect": "Emulation", + "hidden": true + }, + { + "name": "setGeolocationOverride", + "description": "Overrides the Geolocation Position or Error. Omitting any of the parameters emulates position unavailable.", + "parameters": [ + { "name": "latitude", "type": "number", "optional": true, "description": "Mock latitude"}, + { "name": "longitude", "type": "number", "optional": true, "description": "Mock longitude"}, + { "name": "accuracy", "type": "number", "optional": true, "description": "Mock accuracy"} + ], + "redirect": "Emulation", + "handlers": ["browser"] + }, + { + "name": "clearGeolocationOverride", + "description": "Clears the overriden Geolocation Position and Error.", + "redirect": "Emulation", + "handlers": ["browser"] + }, + { + "name": "setDeviceOrientationOverride", + "description": "Overrides the Device Orientation.", + "parameters": [ + { "name": "alpha", "type": "number", "description": "Mock alpha"}, + { "name": "beta", "type": "number", "description": "Mock beta"}, + { "name": "gamma", "type": "number", "description": "Mock gamma"} + ], + "redirect": "DeviceOrientation", + "hidden": true + }, + { + "name": "clearDeviceOrientationOverride", + "description": "Clears the overridden Device Orientation.", + "redirect": "DeviceOrientation", + "hidden": true + }, + { + "name": "setTouchEmulationEnabled", + "parameters": [ + { "name": "enabled", "type": "boolean", "description": "Whether the touch event emulation should be enabled." }, + { "name": "configuration", "type": "string", "enum": ["mobile", "desktop"], "optional": true, "description": "Touch/gesture events configuration. Default: current platform." } + ], + "description": "Toggles mouse event-based touch event emulation.", + "hidden": true, + "redirect": "Emulation", + "handlers": ["browser", "renderer"] + }, + { + "name": "captureScreenshot", + "async": true, + "description": "Capture page screenshot.", + "returns": [ + { "name": "data", "type": "string", "description": "Base64-encoded image data (PNG)." } + ], + "hidden": true, + "handlers": ["browser"] + }, + { + "name": "startScreencast", + "description": "Starts sending each frame using the <code>screencastFrame</code> event.", + "parameters": [ + { "name": "format", "type": "string", "optional": true, "enum": ["jpeg", "png"], "description": "Image compression format." }, + { "name": "quality", "type": "integer", "optional": true, "description": "Compression quality from range [0..100]." }, + { "name": "maxWidth", "type": "integer", "optional": true, "description": "Maximum screenshot width." }, + { "name": "maxHeight", "type": "integer", "optional": true, "description": "Maximum screenshot height." }, + { "name": "everyNthFrame", "type": "integer", "optional": true, "description": "Send every n-th frame." } + ], + "hidden": true, + "handlers": ["browser", "renderer"] + }, + { + "name": "stopScreencast", + "description": "Stops sending each frame in the <code>screencastFrame</code>.", + "hidden": true, + "handlers": ["browser", "renderer"] + }, + { + "name": "screencastFrameAck", + "description": "Acknowledges that a screencast frame has been received by the frontend.", + "parameters": [ + { "name": "sessionId", "type": "integer", "description": "Frame number." } + ], + "hidden": true, + "handlers": ["browser"] + }, + { + "name": "handleJavaScriptDialog", + "description": "Accepts or dismisses a JavaScript initiated dialog (alert, confirm, prompt, or onbeforeunload).", + "parameters": [ + { "name": "accept", "type": "boolean", "description": "Whether to accept or dismiss the dialog." }, + { "name": "promptText", "type": "string", "optional": true, "description": "The text to enter into the dialog prompt before accepting. Used only if this is a prompt dialog." } + ], + "hidden": true, + "handlers": ["browser"] + }, + { + "name": "setColorPickerEnabled", + "parameters": [ + { "name": "enabled", "type": "boolean", "description": "Shows / hides color picker" } + ], + "description": "Shows / hides color picker", + "hidden": true, + "handlers": ["browser"] + }, + { + "name": "setOverlayMessage", + "parameters": [ + { "name": "message", "type": "string", "optional": true, "description": "Overlay message to display when paused in debugger." } + ], + "hidden": true, + "description": "Sets overlay message." + }, + { + "name": "getAppManifest", + "hidden": true, + "returns": [ + { "name": "url", "type": "string", "description": "Manifest location." }, + { "name": "errors", "type": "array", "items": { "$ref": "AppManifestError" } }, + { "name": "data", "type": "string", "optional": true, "description": "Manifest content." } + ], + "handlers": ["none"] + }, + { + "name": "requestAppBanner", + "hidden": true, + "handlers": ["browser"] + }, + { + "name": "setBlockedEventsWarningThreshold", + "hidden": true, + "parameters": [ + { "name": "threshold", "type": "number", "description": "If set to a positive number, specifies threshold in seconds for input event latency that will cause a console warning about blocked event to be issued. If zero or less, the warning is disabled." } + ] + } + ], + "events": [ + { + "name": "domContentEventFired", + "parameters": [ + { "name": "timestamp", "type": "number" } + ] + }, + { + "name": "loadEventFired", + "parameters": [ + { "name": "timestamp", "type": "number" } + ] + }, + { + "name": "frameAttached", + "description": "Fired when frame has been attached to its parent.", + "parameters": [ + { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has been attached." }, + { "name": "parentFrameId", "$ref": "FrameId", "description": "Parent frame identifier." } + ] + }, + { + "name": "frameNavigated", + "description": "Fired once navigation of the frame has completed. Frame is now associated with the new loader.", + "parameters": [ + { "name": "frame", "$ref": "Frame", "description": "Frame object." } + ] + }, + { + "name": "frameDetached", + "description": "Fired when frame has been detached from its parent.", + "parameters": [ + { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has been detached." } + ] + }, + { + "name": "frameStartedLoading", + "description": "Fired when frame has started loading.", + "parameters": [ + { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has started loading." } + ], + "hidden": true + }, + { + "name": "frameStoppedLoading", + "description": "Fired when frame has stopped loading.", + "parameters": [ + { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has stopped loading." } + ], + "hidden": true + }, + { + "name": "frameScheduledNavigation", + "description": "Fired when frame schedules a potential navigation.", + "parameters": [ + { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has scheduled a navigation." }, + { "name": "delay", "type": "number", "description": "Delay (in seconds) until the navigation is scheduled to begin. The navigation is not guaranteed to start." } + ], + "hidden": true + }, + { + "name": "frameClearedScheduledNavigation", + "description": "Fired when frame no longer has a scheduled navigation.", + "parameters": [ + { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has cleared its scheduled navigation." } + ], + "hidden": true + }, + { + "name": "frameResized", + "hidden": true + }, + { + "name": "javascriptDialogOpening", + "description": "Fired when a JavaScript initiated dialog (alert, confirm, prompt, or onbeforeunload) is about to open.", + "parameters": [ + { "name": "message", "type": "string", "description": "Message that will be displayed by the dialog." }, + { "name": "type", "$ref": "DialogType", "description": "Dialog type." } + ], + "hidden": true + }, + { + "name": "javascriptDialogClosed", + "description": "Fired when a JavaScript initiated dialog (alert, confirm, prompt, or onbeforeunload) has been closed.", + "parameters": [ + { "name": "result", "type": "boolean", "description": "Whether dialog was confirmed." } + ], + "hidden": true + }, + { + "name": "screencastFrame", + "description": "Compressed image data requested by the <code>startScreencast</code>.", + "parameters": [ + { "name": "data", "type": "string", "description": "Base64-encoded compressed image." }, + { "name": "metadata", "$ref": "ScreencastFrameMetadata", "description": "Screencast frame metadata."}, + { "name": "sessionId", "type": "integer", "description": "Frame number."} + ], + "hidden": true, + "handlers": ["browser"] + }, + { + "name": "screencastVisibilityChanged", + "description": "Fired when the page with currently enabled screencast was shown or hidden </code>.", + "parameters": [ + { "name": "visible", "type": "boolean", "description": "True if the page is visible." } + ], + "hidden": true, + "handlers": ["browser"] + }, + { + "name": "colorPicked", + "description": "Fired when a color has been picked.", + "parameters": [ + { "name": "color", "$ref": "DOM.RGBA", "description": "RGBA of the picked color." } + ], + "hidden": true, + "handlers": ["browser"] + }, + { + "name": "interstitialShown", + "description": "Fired when interstitial page was shown", + "hidden": true, + "handlers": ["browser"] + }, + { + "name": "interstitialHidden", + "description": "Fired when interstitial page was hidden", + "hidden": true, + "handlers": ["browser"] + } + ] + }, + { + "domain": "Rendering", + "description": "This domain allows to control rendering of the page.", + "hidden": true, + "commands": [ + { + "name": "setShowPaintRects", + "description": "Requests that backend shows paint rectangles", + "parameters": [ + { "name": "result", "type": "boolean", "description": "True for showing paint rectangles" } + ] + }, + { + "name": "setShowDebugBorders", + "description": "Requests that backend shows debug borders on layers", + "parameters": [ + { "name": "show", "type": "boolean", "description": "True for showing debug borders" } + ] + }, + { + "name": "setShowFPSCounter", + "description": "Requests that backend shows the FPS counter", + "parameters": [ + { "name": "show", "type": "boolean", "description": "True for showing the FPS counter" } + ] + }, + { + "name": "setShowScrollBottleneckRects", + "description": "Requests that backend shows scroll bottleneck rects", + "parameters": [ + { "name": "show", "type": "boolean", "description": "True for showing scroll bottleneck rects" } + ] + }, + { + "name": "setShowViewportSizeOnResize", + "description": "Paints viewport size upon main frame resize.", + "parameters": [ + { "name": "show", "type": "boolean", "description": "Whether to paint size or not." } + ] + } + ] + }, + { + "domain": "Emulation", + "description": "This domain emulates different environments for the page.", + "hidden": true, + "types": [ + { + "id": "ScreenOrientation", + "type": "object", + "description": "Screen orientation.", + "properties": [ + { "name": "type", "type": "string", "enum": ["portraitPrimary", "portraitSecondary", "landscapePrimary", "landscapeSecondary"], "description": "Orientation type." }, + { "name": "angle", "type": "integer", "description": "Orientation angle." } + ] + } + ], + "commands": [ + { + "name": "setDeviceMetricsOverride", + "description": "Overrides the values of device screen dimensions (window.screen.width, window.screen.height, window.innerWidth, window.innerHeight, and \"device-width\"/\"device-height\"-related CSS media query results).", + "parameters": [ + { "name": "width", "type": "integer", "description": "Overriding width value in pixels (minimum 0, maximum 10000000). 0 disables the override." }, + { "name": "height", "type": "integer", "description": "Overriding height value in pixels (minimum 0, maximum 10000000). 0 disables the override." }, + { "name": "deviceScaleFactor", "type": "number", "description": "Overriding device scale factor value. 0 disables the override." }, + { "name": "mobile", "type": "boolean", "description": "Whether to emulate mobile device. This includes viewport meta tag, overlay scrollbars, text autosizing and more." }, + { "name": "fitWindow", "type": "boolean", "description": "Whether a view that exceeds the available browser window area should be scaled down to fit." }, + { "name": "scale", "type": "number", "optional": true, "description": "Scale to apply to resulting view image. Ignored in |fitWindow| mode." }, + { "name": "offsetX", "type": "number", "optional": true, "description": "X offset to shift resulting view image by. Ignored in |fitWindow| mode." }, + { "name": "offsetY", "type": "number", "optional": true, "description": "Y offset to shift resulting view image by. Ignored in |fitWindow| mode." }, + { "name": "screenWidth", "type": "integer", "optional": true, "description": "Overriding screen width value in pixels (minimum 0, maximum 10000000). Only used for |mobile==true|." }, + { "name": "screenHeight", "type": "integer", "optional": true, "description": "Overriding screen height value in pixels (minimum 0, maximum 10000000). Only used for |mobile==true|." }, + { "name": "positionX", "type": "integer", "optional": true, "description": "Overriding view X position on screen in pixels (minimum 0, maximum 10000000). Only used for |mobile==true|." }, + { "name": "positionY", "type": "integer", "optional": true, "description": "Overriding view Y position on screen in pixels (minimum 0, maximum 10000000). Only used for |mobile==true|." }, + { "name": "screenOrientation", "$ref": "ScreenOrientation", "optional": true, "description": "Screen orientation override." } + ], + "handlers": ["browser"] + }, + { + "name": "clearDeviceMetricsOverride", + "description": "Clears the overriden device metrics.", + "handlers": ["browser"] + }, + { + "name": "resetPageScaleFactor", + "description": "Requests that page scale factor is reset to initial values." + }, + { + "name": "setPageScaleFactor", + "description": "Sets a specified page scale factor.", + "parameters": [ + { "name": "pageScaleFactor", "type": "number", "description": "Page scale factor." } + ] + }, + { + "name": "setScriptExecutionDisabled", + "description": "Switches script execution in the page.", + "parameters": [ + { "name": "value", "type": "boolean", "description": "Whether script execution should be disabled in the page." } + ] + }, + { + "name": "setGeolocationOverride", + "description": "Overrides the Geolocation Position or Error. Omitting any of the parameters emulates position unavailable.", + "parameters": [ + { "name": "latitude", "type": "number", "optional": true, "description": "Mock latitude"}, + { "name": "longitude", "type": "number", "optional": true, "description": "Mock longitude"}, + { "name": "accuracy", "type": "number", "optional": true, "description": "Mock accuracy"} + ], + "handlers": ["browser"] + }, + { + "name": "clearGeolocationOverride", + "description": "Clears the overriden Geolocation Position and Error.", + "handlers": ["browser"] + }, + { + "name": "setTouchEmulationEnabled", + "parameters": [ + { "name": "enabled", "type": "boolean", "description": "Whether the touch event emulation should be enabled." }, + { "name": "configuration", "type": "string", "enum": ["mobile", "desktop"], "optional": true, "description": "Touch/gesture events configuration. Default: current platform." } + ], + "description": "Toggles mouse event-based touch event emulation.", + "handlers": ["browser", "renderer"] + }, + { + "name": "setEmulatedMedia", + "parameters": [ + { "name": "media", "type": "string", "description": "Media type to emulate. Empty string disables the override." } + ], + "description": "Emulates the given media for CSS media queries." + }, + { + "name": "setCPUThrottlingRate", + "parameters": [ + { "name": "rate", "type": "number", "description": "Throttling rate as a slowdown factor (1 is no throttle, 2 is 2x slowdown, etc)." } + ], + "description": "Enables CPU throttling to emulate slow CPUs." + }, + { + "name": "canEmulate", + "description": "Tells whether emulation is supported.", + "returns": [ + { "name": "result", "type": "boolean", "description": "True if emulation is supported." } + ], + "handlers": ["browser"] + } + ] + }, + { + "domain": "Runtime", + "description": "Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. Evaluation results are returned as mirror object that expose object type, string representation and unique identifier that can be used for further object reference. Original objects are maintained in memory unless they are either explicitly released or are released along with the other objects in their object group.", + "types": [ + { + "id": "ScriptId", + "type": "string", + "description": "Unique script identifier." + }, + { + "id": "RemoteObjectId", + "type": "string", + "description": "Unique object identifier." + }, + { + "id": "RemoteObject", + "type": "object", + "description": "Mirror object referencing original JavaScript object.", + "properties": [ + { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." }, + { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for <code>object</code> type values only." }, + { "name": "className", "type": "string", "optional": true, "description": "Object class (constructor) name. Specified for <code>object</code> type values only." }, + { "name": "value", "type": "any", "optional": true, "description": "Remote object value in case of primitive values or JSON values (if it was requested), or description string if the value can not be JSON-stringified (like NaN, Infinity, -Infinity, -0)." }, + { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." }, + { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Unique object identifier (for non-primitive values)." }, + { "name": "preview", "$ref": "ObjectPreview", "optional": true, "description": "Preview containing abbreviated property values. Specified for <code>object</code> type values only.", "hidden": true }, + { "name": "customPreview", "$ref": "CustomPreview", "optional": true, "hidden": true} + ] + }, + { + "id": "CustomPreview", + "type": "object", + "hidden": true, + "properties": [ + { "name": "header", "type": "string"}, + { "name": "hasBody", "type": "boolean"}, + { "name": "formatterObjectId", "$ref": "RemoteObjectId"}, + { "name": "bindRemoteObjectFunctionId", "$ref": "RemoteObjectId" }, + { "name": "configObjectId", "$ref": "RemoteObjectId", "optional": true } + ] + }, + { + "id": "ObjectPreview", + "type": "object", + "hidden": true, + "description": "Object containing abbreviated remote object value.", + "properties": [ + { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." }, + { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for <code>object</code> type values only." }, + { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." }, + { "name": "overflow", "type": "boolean", "description": "True iff some of the properties or entries of the original object did not fit." }, + { "name": "properties", "type": "array", "items": { "$ref": "PropertyPreview" }, "description": "List of the properties." }, + { "name": "entries", "type": "array", "items": { "$ref": "EntryPreview" }, "optional": true, "description": "List of the entries. Specified for <code>map</code> and <code>set</code> subtype values only." } + ] + }, + { + "id": "PropertyPreview", + "type": "object", + "hidden": true, + "properties": [ + { "name": "name", "type": "string", "description": "Property name." }, + { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol", "accessor"], "description": "Object type. Accessor means that the property itself is an accessor property." }, + { "name": "value", "type": "string", "optional": true, "description": "User-friendly property value string." }, + { "name": "valuePreview", "$ref": "ObjectPreview", "optional": true, "description": "Nested value preview." }, + { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for <code>object</code> type values only." } + ] + }, + { + "id": "EntryPreview", + "type": "object", + "hidden": true, + "properties": [ + { "name": "key", "$ref": "ObjectPreview", "optional": true, "description": "Preview of the key. Specified for map-like collection entries." }, + { "name": "value", "$ref": "ObjectPreview", "description": "Preview of the value." } + ] + }, + { + "id": "PropertyDescriptor", + "type": "object", + "description": "Object property descriptor.", + "properties": [ + { "name": "name", "type": "string", "description": "Property name or symbol description." }, + { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." }, + { "name": "writable", "type": "boolean", "optional": true, "description": "True if the value associated with the property may be changed (data descriptors only)." }, + { "name": "get", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a getter for the property, or <code>undefined</code> if there is no getter (accessor descriptors only)." }, + { "name": "set", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a setter for the property, or <code>undefined</code> if there is no setter (accessor descriptors only)." }, + { "name": "configurable", "type": "boolean", "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." }, + { "name": "enumerable", "type": "boolean", "description": "True if this property shows up during enumeration of the properties on the corresponding object." }, + { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }, + { "name": "isOwn", "optional": true, "type": "boolean", "description": "True if the property is owned for the object.", "hidden": true }, + { "name": "symbol", "$ref": "RemoteObject", "optional": true, "description": "Property symbol object, if the property is of the <code>symbol</code> type.", "hidden": true } + ] + }, + { + "id": "InternalPropertyDescriptor", + "type": "object", + "description": "Object internal property descriptor. This property isn't normally visible in JavaScript code.", + "properties": [ + { "name": "name", "type": "string", "description": "Conventional property name." }, + { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." } + ], + "hidden": true + }, + { + "id": "CallArgument", + "type": "object", + "description": "Represents function call argument. Either remote object id <code>objectId</code> or primitive <code>value</code> or neither of (for undefined) them should be specified.", + "properties": [ + { "name": "value", "type": "any", "optional": true, "description": "Primitive value, or description string if the value can not be JSON-stringified (like NaN, Infinity, -Infinity, -0)." }, + { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Remote object handle." }, + { "name": "type", "optional": true, "hidden": true, "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." } + ] + }, + { + "id": "ExecutionContextId", + "type": "integer", + "description": "Id of an execution context." + }, + { + "id": "ExecutionContextDescription", + "type": "object", + "description": "Description of an isolated world.", + "properties": [ + { "name": "id", "$ref": "ExecutionContextId", "description": "Unique id of the execution context. It can be used to specify in which execution context script evaluation should be performed." }, + { "name": "isDefault", "type": "boolean", "description": "Whether context is the default page context (as opposite to e.g. context of content script).", "hidden": true }, + { "name": "origin", "type": "string", "description": "Execution context origin.", "hidden": true}, + { "name": "name", "type": "string", "description": "Human readable name describing given context.", "hidden": true}, + { "name": "frameId", "type": "string", "description": "Id of the owning frame. May be an empty string if the context is not associated with a frame." } + ] + }, + { + "id": "ExceptionDetails", + "type": "object", + "description": "Detailed information on exception (or error) that was thrown during script compilation or execution.", + "properties": [ + { "name": "text", "type": "string", "description": "Exception text." }, + { "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." }, + { "name": "scriptId", "type": "string", "optional": true, "description": "Script ID of the message origin." }, + { "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message." }, + { "name": "column", "type": "integer", "optional": true, "description": "Column number in the resource that generated this message." }, + { "name": "stack", "$ref": "StackTrace", "optional": true, "description": "JavaScript stack trace for assertions and error messages." } + ] + }, + { + "id": "CallFrame", + "type": "object", + "description": "Stack entry for runtime errors and assertions.", + "properties": [ + { "name": "functionName", "type": "string", "description": "JavaScript function name." }, + { "name": "scriptId", "$ref": "ScriptId", "description": "JavaScript script id." }, + { "name": "url", "type": "string", "description": "JavaScript script name or url." }, + { "name": "lineNumber", "type": "integer", "description": "JavaScript script line number." }, + { "name": "columnNumber", "type": "integer", "description": "JavaScript script column number." } + ] + }, + { + "id": "StackTrace", + "type": "object", + "description": "Call frames for assertions or error messages.", + "properties": [ + { "name": "description", "type": "string", "optional": true, "description": "String label of this stack trace. For async traces this may be a name of the function that initiated the async call." }, + { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "JavaScript function name." }, + { "name": "parent", "$ref": "StackTrace", "optional": true, "hidden": true, "hidden": true, "description": "Asynchronous JavaScript stack trace that preceded this stack, if available." } + ] + } + ], + "commands": [ + { + "name": "evaluate", + "parameters": [ + { "name": "expression", "type": "string", "description": "Expression to evaluate." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, + { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation.", "hidden": true }, + { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether evaluation should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true }, + { "name": "contextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which isolated context to perform evaluation. Each content script lives in an isolated context and this parameter may be used to specify one of those contexts. If the parameter is omitted or 0 the evaluation will be performed in the context of the inspected page." }, + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." }, + { "name": "userGesture", "type": "boolean", "optional": true, "hidden": true, "description": "Whether execution should be treated as initiated by user in the UI." } + ], + "returns": [ + { "name": "result", "$ref": "RemoteObject", "description": "Evaluation result." }, + { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "hidden": true, "description": "Exception details."} + ], + "description": "Evaluates expression on global object." + }, + { + "name": "callFunctionOn", + "parameters": [ + { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to call function on." }, + { "name": "functionDeclaration", "type": "string", "description": "Declaration of the function to call." }, + { "name": "arguments", "type": "array", "items": { "$ref": "CallArgument", "description": "Call argument." }, "optional": true, "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target object." }, + { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether function call should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true }, + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." }, + { "name": "userGesture", "type": "boolean", "optional": true, "hidden": true, "description": "Whether execution should be treated as initiated by user in the UI." } + ], + "returns": [ + { "name": "result", "$ref": "RemoteObject", "description": "Call result." }, + { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." } + ], + "description": "Calls function with given declaration on the given object. Object group of the result is inherited from the target object." + }, + { + "name": "getProperties", + "parameters": [ + { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." }, + { "name": "ownProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging only to the element itself, not to its prototype chain." }, + { "name": "accessorPropertiesOnly", "optional": true, "type": "boolean", "description": "If true, returns accessor properties (with getter/setter) only; internal properties are not returned either.", "hidden": true }, + { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the results." } + ], + "returns": [ + { "name": "result", "type": "array", "items": { "$ref": "PropertyDescriptor" }, "description": "Object properties." }, + { "name": "internalProperties", "optional": true, "type": "array", "items": { "$ref": "InternalPropertyDescriptor" }, "description": "Internal object properties (only of the element itself).", "hidden": true }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "hidden": true, "description": "Exception details."} + ], + "description": "Returns properties of a given object. Object group of the result is inherited from the target object." + }, + { + "name": "releaseObject", + "parameters": [ + { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to release." } + ], + "description": "Releases remote object with given id." + }, + { + "name": "releaseObjectGroup", + "parameters": [ + { "name": "objectGroup", "type": "string", "description": "Symbolic object group name." } + ], + "description": "Releases all remote objects that belong to a given group." + }, + { + "name": "run", + "hidden": true, + "description": "Tells inspected instance(worker or page) that it can run in case it was started paused." + }, + { + "name": "enable", + "description": "Enables reporting of execution contexts creation by means of <code>executionContextCreated</code> event. When the reporting gets enabled the event will be sent immediately for each existing execution context." + }, + { + "name": "disable", + "hidden": true, + "description": "Disables reporting of execution contexts creation." + }, + { + "name": "setCustomObjectFormatterEnabled", + "parameters": [ + { + "name": "enabled", + "type": "boolean" + } + ], + "hidden": true + }, + { + "name": "compileScript", + "hidden": true, + "parameters": [ + { "name": "expression", "type": "string", "description": "Expression to compile." }, + { "name": "sourceURL", "type": "string", "description": "Source url to be set for the script." }, + { "name": "persistScript", "type": "boolean", "description": "Specifies whether the compiled script should be persisted." }, + { "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Specifies in which isolated context to perform script run. Each content script lives in an isolated context and this parameter is used to specify one of those contexts." } + ], + "returns": [ + { "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Id of the script." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} + ], + "description": "Compiles expression." + }, + { + "name": "runScript", + "hidden": true, + "parameters": [ + { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to run." }, + { "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Specifies in which isolated context to perform script run. Each content script lives in an isolated context and this parameter is used to specify one of those contexts." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, + { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether script run should stop on exceptions and mute console. Overrides setPauseOnException state." }, + { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." } + ], + "returns": [ + { "name": "result", "$ref": "RemoteObject", "description": "Run result." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} + ], + "description": "Runs script with given id in a given context." + } + ], + "events": [ + { + "name": "executionContextCreated", + "parameters": [ + { "name": "context", "$ref": "ExecutionContextDescription", "description": "A newly created execution contex." } + ], + "description": "Issued when new execution context is created." + }, + { + "name": "executionContextDestroyed", + "parameters": [ + { "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Id of the destroyed context" } + ], + "description": "Issued when execution context is destroyed." + }, + { + "name": "executionContextsCleared", + "description": "Issued when all executionContexts were cleared in browser" + }, + { + "name": "inspectRequested", + "parameters": [ + { "name": "object", "$ref": "RemoteObject" }, + { "name": "hints", "type": "object" } + ], + "hidden": true + } + ] + }, + { + "domain": "Console", + "description": "Console domain defines methods and events for interaction with the JavaScript console. Console collects messages created by means of the <a href='http://getfirebug.com/wiki/index.php/Console_API'>JavaScript Console API</a>. One needs to enable this domain using <code>enable</code> command in order to start receiving the console messages. Browser collects messages issued while console domain is not enabled as well and reports them using <code>messageAdded</code> notification upon enabling.", + "types": [ + { + "id": "Timestamp", + "type": "number", + "description": "Number of seconds since epoch.", + "hidden": true + }, + { + "id": "ConsoleMessage", + "type": "object", + "description": "Console message.", + "properties": [ + { "name": "source", "type": "string", "enum": ["xml", "javascript", "network", "console-api", "storage", "appcache", "rendering", "security", "other", "deprecation"], "description": "Message source." }, + { "name": "level", "type": "string", "enum": ["log", "warning", "error", "debug", "info", "revokedError"], "description": "Message severity." }, + { "name": "text", "type": "string", "description": "Message text." }, + { "name": "type", "type": "string", "optional": true, "enum": ["log", "dir", "dirxml", "table", "trace", "clear", "startGroup", "startGroupCollapsed", "endGroup", "assert", "profile", "profileEnd"], "description": "Console message type." }, + { "name": "scriptId", "type": "string", "optional": true, "description": "Script ID of the message origin." }, + { "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." }, + { "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message." }, + { "name": "column", "type": "integer", "optional": true, "description": "Column number in the resource that generated this message." }, + { "name": "repeatCount", "type": "integer", "optional": true, "description": "Repeat count for repeated messages." }, + { "name": "parameters", "type": "array", "items": { "$ref": "Runtime.RemoteObject" }, "optional": true, "description": "Message parameters in case of the formatted message." }, + { "name": "stack", "$ref": "Runtime.StackTrace", "optional": true, "description": "JavaScript stack trace for assertions and error messages." }, + { "name": "networkRequestId", "$ref": "Network.RequestId", "optional": true, "description": "Identifier of the network request associated with this message." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp, when this message was fired.", "hidden": true }, + { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "optional": true, "description": "Identifier of the context where this message was created", "hidden": true }, + { "name": "messageId", "type": "integer", "hidden": true, "optional": true, "description": "Message id." }, + { "name": "relatedMessageId", "type": "integer", "hidden": true, "optional": true, "description": "Related message id." } + ] + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables console domain, sends the messages collected so far to the client by means of the <code>messageAdded</code> notification." + }, + { + "name": "disable", + "description": "Disables console domain, prevents further console messages from being reported to the client." + }, + { + "name": "clearMessages", + "description": "Clears console messages collected in the browser." + } + ], + "events": [ + { + "name": "messageAdded", + "parameters": [ + { "name": "message", "$ref": "ConsoleMessage", "description": "Console message that has been added." } + ], + "description": "Issued when new console message is added." + }, + { + "name": "messageRepeatCountUpdated", + "parameters": [ + { "name": "count", "type": "integer", "description": "New repeat count value." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp of most recent message in batch.", "hidden": true } + ], + "description": "Is not issued. Will be gone in the future versions of the protocol.", + "deprecated": true + }, + { + "name": "messagesCleared", + "description": "Issued when console is cleared. This happens either upon <code>clearMessages</code> command or after page navigation." + } + ] + }, + { + "domain": "Security", + "description": "Security", + "hidden": true, + "types": [ + { + "id": "SecurityState", + "type": "string", + "enum": ["unknown", "neutral", "insecure", "warning", "secure", "info"], + "description": "The security level of a page or resource." + }, + { + "id": "SecurityStateExplanation", + "type": "object", + "properties": [ + { "name": "securityState", "$ref": "SecurityState", "description": "Security state representing the severity of the factor being explained." }, + { "name": "summary", "type": "string", "description": "Short phrase describing the type of factor." }, + { "name": "description", "type": "string", "description": "Full text explanation of the factor." }, + { "name": "certificateId", "$ref": "Network.CertificateId", "optional": true, "description": "Associated certificate id." } + ], + "description": "An explanation of an factor contributing to the security state." + }, + { + "id": "MixedContentStatus", + "type": "object", + "properties": [ + { "name": "ranInsecureContent", "type": "boolean", "description": "True if the page ran insecure content such as scripts." }, + { "name": "displayedInsecureContent", "type": "boolean", "description": "True if the page displayed insecure content such as images." }, + { "name": "ranInsecureContentStyle", "$ref": "SecurityState", "description": "Security state representing a page that ran insecure content." }, + { "name": "displayedInsecureContentStyle", "$ref": "SecurityState", "description": "Security state representing a page that displayed insecure content." } + ], + "description": "Information about mixed content on the page." + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables tracking security state changes.", + "handlers": ["browser"] + }, + { + "name": "disable", + "description": "Disables tracking security state changes.", + "handlers": ["browser"] + } + ], + "events": [ + { + "name": "securityStateChanged", + "description": "The security state of the page changed.", + "parameters": [ + { "name": "securityState", "$ref": "SecurityState", "description": "Security state." }, + { "name": "explanations", "type": "array", "items": { "$ref": "SecurityStateExplanation" }, "description": "List of explanations for the security state. If the overall security state is `insecure` or `warning`, at least one corresponding explanation should be included.", "optional": true }, + { "name": "mixedContentStatus", "$ref": "MixedContentStatus", "description": "Information about mixed content on the page.", "optional": true }, + { "name": "schemeIsCryptographic", "type": "boolean", "description": "True if the page was loaded over cryptographic transport such as HTTPS.", "optional": true } + ], + "handlers": ["browser"] + } + ] + }, + { + "domain": "Network", + "description": "Network domain allows tracking network activities of the page. It exposes information about http, file, data and other requests and responses, their headers, bodies, timing, etc.", + "types": [ + { + "id": "LoaderId", + "type": "string", + "description": "Unique loader identifier." + }, + { + "id": "RequestId", + "type": "string", + "description": "Unique request identifier." + }, + { + "id": "Timestamp", + "type": "number", + "description": "Number of seconds since epoch." + }, + { + "id": "Headers", + "type": "object", + "description": "Request / response headers as keys / values of JSON object." + }, + { + "id": "ResourceTiming", + "type": "object", + "description": "Timing information for the request.", + "properties": [ + { "name": "requestTime", "type": "number", "description": "Timing's requestTime is a baseline in seconds, while the other numbers are ticks in milliseconds relatively to this requestTime." }, + { "name": "proxyStart", "type": "number", "description": "Started resolving proxy." }, + { "name": "proxyEnd", "type": "number", "description": "Finished resolving proxy." }, + { "name": "dnsStart", "type": "number", "description": "Started DNS address resolve." }, + { "name": "dnsEnd", "type": "number", "description": "Finished DNS address resolve." }, + { "name": "connectStart", "type": "number", "description": "Started connecting to the remote host." }, + { "name": "connectEnd", "type": "number", "description": "Connected to the remote host." }, + { "name": "sslStart", "type": "number", "description": "Started SSL handshake." }, + { "name": "sslEnd", "type": "number", "description": "Finished SSL handshake." }, + { "name": "workerStart", "type": "number", "description": "Started running ServiceWorker.", "hidden": true }, + { "name": "workerReady", "type": "number", "description": "Finished Starting ServiceWorker.", "hidden": true }, + { "name": "sendStart", "type": "number", "description": "Started sending request." }, + { "name": "sendEnd", "type": "number", "description": "Finished sending request." }, + { "name": "pushStart", "type": "number", "description": "Time the server started pushing request.", "hidden": true }, + { "name": "pushEnd", "type": "number", "description": "Time the server finished pushing request.", "hidden": true }, + { "name": "receiveHeadersEnd", "type": "number", "description": "Finished receiving response headers." } + ] + }, + { + "id": "ResourcePriority", + "type": "string", + "enum": ["VeryLow", "Low", "Medium", "High", "VeryHigh"], + "description": "Loading priority of a resource request." + }, + { + "id": "Request", + "type": "object", + "description": "HTTP request data.", + "properties": [ + { "name": "url", "type": "string", "description": "Request URL." }, + { "name": "method", "type": "string", "description": "HTTP request method." }, + { "name": "headers", "$ref": "Headers", "description": "HTTP request headers." }, + { "name": "postData", "type": "string", "optional": true, "description": "HTTP POST request data." }, + { "name": "mixedContentType", "optional": true, "type": "string", "enum": ["blockable", "optionally-blockable", "none"], "description": "The mixed content status of the request, as defined in http://www.w3.org/TR/mixed-content/" }, + { "name": "initialPriority", "$ref": "ResourcePriority", "description": "Priority of the resource request at the time request is sent."} + ] + }, + { + "id": "CertificateId", + "type": "integer", + "description": "An internal certificate ID value." + }, + { + "id": "CertificateSubject", + "type": "object", + "description": "Subject of a certificate.", + "properties": [ + { "name": "name", "type": "string", "description": "Certificate subject name." }, + { "name": "sanDnsNames", "type": "array", "items": { "type": "string" }, "description": "Subject Alternative Name (SAN) DNS names." }, + { "name": "sanIpAddresses", "type": "array", "items": { "type": "string" }, "description": "Subject Alternative Name (SAN) IP addresses." } + ] + }, + { + "id": "CertificateDetails", + "type": "object", + "description": "Details about a request's certificate.", + "properties": [ + { "name": "subject", "$ref": "CertificateSubject", "description": "Certificate subject." }, + { "name": "issuer", "type": "string", "description": "Name of the issuing CA." }, + { "name": "validFrom", "$ref": "Timestamp", "description": "Certificate valid from date." }, + { "name": "validTo", "$ref": "Timestamp", "description": "Certificate valid to (expiration) date" } + ] + }, + { + "id": "CertificateValidationDetails", + "type": "object", + "description": "Details about the validation status of a request's certificate.", + "properties": [ + { "name": "numUnknownScts", "type": "integer", "description": "The number of SCTs from unknown logs." }, + { "name": "numInvalidScts", "type": "integer", "description": "The number of invalid SCTs." }, + { "name": "numValidScts", "type": "integer", "description": "The number of valid SCTs." } + ] + }, + { + "id": "SecurityDetails", + "type": "object", + "description": "Security details about a request.", + "properties": [ + { "name": "protocol", "type": "string", "description": "Protocol name (e.g. \"TLS 1.2\" or \"QUIC\")." }, + { "name": "keyExchange", "type": "string", "description": "Key Exchange used by the connection." }, + { "name": "cipher", "type": "string", "description": "Cipher name." }, + { "name": "mac", "type": "string", "optional": true, "description": "TLS MAC. Note that AEAD ciphers do not have separate MACs." }, + { "name": "certificateId", "$ref": "CertificateId", "description": "Certificate ID value." }, + { "name": "certificateValidationDetails", "$ref": "CertificateValidationDetails", "optional": true, "description": "Validation details for the request's certficate." } + ] + }, + { + "id": "BlockedReason", + "type": "string", + "description": "The reason why request was blocked.", + "enum": ["csp", "mixed-content", "origin", "inspector", "other"], + "hidden": true + }, + { + "id": "Response", + "type": "object", + "description": "HTTP response data.", + "properties": [ + { "name": "url", "type": "string", "description": "Response URL. This URL can be different from CachedResource.url in case of redirect." }, + { "name": "status", "type": "number", "description": "HTTP response status code." }, + { "name": "statusText", "type": "string", "description": "HTTP response status text." }, + { "name": "headers", "$ref": "Headers", "description": "HTTP response headers." }, + { "name": "headersText", "type": "string", "optional": true, "description": "HTTP response headers text." }, + { "name": "mimeType", "type": "string", "description": "Resource mimeType as determined by the browser." }, + { "name": "requestHeaders", "$ref": "Headers", "optional": true, "description": "Refined HTTP request headers that were actually transmitted over the network." }, + { "name": "requestHeadersText", "type": "string", "optional": true, "description": "HTTP request headers text." }, + { "name": "connectionReused", "type": "boolean", "description": "Specifies whether physical connection was actually reused for this request." }, + { "name": "connectionId", "type": "number", "description": "Physical connection id that was actually used for this request." }, + { "name": "remoteIPAddress", "type": "string", "optional": true, "hidden": true, "description": "Remote IP address." }, + { "name": "remotePort", "type": "integer", "optional": true, "hidden": true, "description": "Remote port."}, + { "name": "fromDiskCache", "type": "boolean", "optional": true, "description": "Specifies that the request was served from the disk cache." }, + { "name": "fromServiceWorker", "type": "boolean", "optional": true, "description": "Specifies that the request was served from the ServiceWorker." }, + { "name": "encodedDataLength", "type": "number", "optional": false, "description": "Total number of bytes received for this request so far." }, + { "name": "timing", "$ref": "ResourceTiming", "optional": true, "description": "Timing information for the given request." }, + { "name": "protocol", "type": "string", "optional": true, "description": "Protocol used to fetch this request." }, + { "name": "securityState", "$ref": "Security.SecurityState", "description": "Security state of the request resource." }, + { "name": "securityDetails", "$ref": "SecurityDetails", "optional": true, "description": "Security details for the request." } + ] + }, + { + "id": "WebSocketRequest", + "type": "object", + "description": "WebSocket request data.", + "hidden": true, + "properties": [ + { "name": "headers", "$ref": "Headers", "description": "HTTP request headers." } + ] + }, + { + "id": "WebSocketResponse", + "type": "object", + "description": "WebSocket response data.", + "hidden": true, + "properties": [ + { "name": "status", "type": "number", "description": "HTTP response status code." }, + { "name": "statusText", "type": "string", "description": "HTTP response status text." }, + { "name": "headers", "$ref": "Headers", "description": "HTTP response headers." }, + { "name": "headersText", "type": "string", "optional": true, "description": "HTTP response headers text." }, + { "name": "requestHeaders", "$ref": "Headers", "optional": true, "description": "HTTP request headers." }, + { "name": "requestHeadersText", "type": "string", "optional": true, "description": "HTTP request headers text." } + ] + }, + { + "id": "WebSocketFrame", + "type": "object", + "description": "WebSocket frame data.", + "hidden": true, + "properties": [ + { "name": "opcode", "type": "number", "description": "WebSocket frame opcode." }, + { "name": "mask", "type": "boolean", "description": "WebSocke frame mask." }, + { "name": "payloadData", "type": "string", "description": "WebSocke frame payload data." } + ] + }, + { + "id": "CachedResource", + "type": "object", + "description": "Information about the cached resource.", + "properties": [ + { "name": "url", "type": "string", "description": "Resource URL. This is the url of the original network request." }, + { "name": "type", "$ref": "Page.ResourceType", "description": "Type of this resource." }, + { "name": "response", "$ref": "Response", "optional": true, "description": "Cached response data." }, + { "name": "bodySize", "type": "number", "description": "Cached response body size." } + ] + }, + { + "id": "Initiator", + "type": "object", + "description": "Information about the request initiator.", + "properties": [ + { "name": "type", "type": "string", "enum": ["parser", "script", "other"], "description": "Type of this initiator." }, + { "name": "stack", "$ref": "Runtime.StackTrace", "optional": true, "description": "Initiator JavaScript stack trace, set for Script only." }, + { "name": "url", "type": "string", "optional": true, "description": "Initiator URL, set for Parser type only." }, + { "name": "lineNumber", "type": "number", "optional": true, "description": "Initiator line number, set for Parser type only." } + ] + }, + { + "id": "Cookie", + "type": "object", + "description": "Cookie object", + "properties": [ + { "name": "name", "type": "string", "description": "Cookie name." }, + { "name": "value", "type": "string", "description": "Cookie value." }, + { "name": "domain", "type": "string", "description": "Cookie domain." }, + { "name": "path", "type": "string", "description": "Cookie path." }, + { "name": "expires", "type": "number", "description": "Cookie expires." }, + { "name": "size", "type": "integer", "description": "Cookie size." }, + { "name": "httpOnly", "type": "boolean", "description": "True if cookie is http-only." }, + { "name": "secure", "type": "boolean", "description": "True if cookie is secure." }, + { "name": "session", "type": "boolean", "description": "True in case of session cookie." }, + { "name": "sameSite", "type": "string", "optional": true, "enum": ["Strict", "Lax"], "description": "Represents the cookies' 'SameSite' status: https://tools.ietf.org/html/draft-west-first-party-cookies" } + ], + "hidden": true + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables network tracking, network events will now be delivered to the client.", + "parameters": [ + { "name": "maxTotalBufferSize", "type": "integer", "optional": true, "hidden": true, "description": "Buffer size in bytes to use when preserving network payloads (XHRs, etc)." }, + { "name": "maxResourceBufferSize", "type": "integer", "optional": true, "hidden": true, "description": "Per-resource buffer size in bytes to use when preserving network payloads (XHRs, etc)." } + ] + }, + { + "name": "disable", + "description": "Disables network tracking, prevents network events from being sent to the client." + }, + { + "name": "setUserAgentOverride", + "description": "Allows overriding user agent with the given string.", + "parameters": [ + { "name": "userAgent", "type": "string", "description": "User agent to use." } + ] + }, + { + "name": "setExtraHTTPHeaders", + "description": "Specifies whether to always send extra HTTP headers with the requests from this page.", + "parameters": [ + { "name": "headers", "$ref": "Headers", "description": "Map with extra HTTP headers." } + ] + }, + { + "name": "getResponseBody", + "async": true, + "description": "Returns content served for the given request.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Identifier of the network request to get content for." } + ], + "returns": [ + { "name": "body", "type": "string", "description": "Response body." }, + { "name": "base64Encoded", "type": "boolean", "description": "True, if content was sent as base64." } + ] + }, + { + "name": "addBlockedURL", + "description": "Blocks specific URL from loading.", + "parameters": [ + { "name": "url", "type": "string", "description": "URL to block." } + ], + "hidden": true + }, + { + "name": "removeBlockedURL", + "description": "Cancels blocking of a specific URL from loading.", + "parameters": [ + { "name": "url", "type": "string", "description": "URL to stop blocking." } + ], + "hidden": true + }, + { + "name": "replayXHR", + "description": "This method sends a new XMLHttpRequest which is identical to the original one. The following parameters should be identical: method, url, async, request body, extra headers, withCredentials attribute, user, password.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Identifier of XHR to replay." } + ], + "hidden": true + }, + { + "name": "setMonitoringXHREnabled", + "parameters": [ + { "name": "enabled", "type": "boolean", "description": "Monitoring enabled state." } + ], + "description": "Toggles monitoring of XMLHttpRequest. If <code>true</code>, console will receive messages upon each XHR issued.", + "hidden": true + }, + { + "name": "canClearBrowserCache", + "description": "Tells whether clearing browser cache is supported.", + "returns": [ + { "name": "result", "type": "boolean", "description": "True if browser cache can be cleared." } + ] + }, + { + "name": "clearBrowserCache", + "description": "Clears browser cache.", + "handlers": ["browser"] + }, + { + "name": "canClearBrowserCookies", + "description": "Tells whether clearing browser cookies is supported.", + "returns": [ + { "name": "result", "type": "boolean", "description": "True if browser cookies can be cleared." } + ] + }, + { + "name": "clearBrowserCookies", + "description": "Clears browser cookies.", + "handlers": ["browser"] + }, + { + "name": "getCookies", + "returns": [ + { "name": "cookies", "type": "array", "items": { "$ref": "Cookie" }, "description": "Array of cookie objects." } + ], + "description": "Returns all browser cookies. Depending on the backend support, will return detailed cookie information in the <code>cookies</code> field.", + "handlers": ["browser"], + "async": true, + "hidden": true + }, + { + "name": "deleteCookie", + "parameters": [ + { "name": "cookieName", "type": "string", "description": "Name of the cookie to remove." }, + { "name": "url", "type": "string", "description": "URL to match cooke domain and path." } + ], + "description": "Deletes browser cookie with given name, domain and path.", + "handlers": ["browser"], + "async": true, + "hidden": true + }, + { + "name": "canEmulateNetworkConditions", + "description": "Tells whether emulation of network conditions is supported.", + "returns": [ + { "name": "result", "type": "boolean", "description": "True if emulation of network conditions is supported." } + ], + "hidden": true, + "handlers": ["browser"] + }, + { + "name": "emulateNetworkConditions", + "description": "Activates emulation of network conditions.", + "parameters": [ + { "name": "offline", "type": "boolean", "description": "True to emulate internet disconnection." }, + { "name": "latency", "type": "number", "description": "Additional latency (ms)." }, + { "name": "downloadThroughput", "type": "number", "description": "Maximal aggregated download throughput." }, + { "name": "uploadThroughput", "type": "number", "description": "Maximal aggregated upload throughput." } + ], + "hidden": true, + "handlers": ["browser", "renderer"] + }, + { + "name": "setCacheDisabled", + "parameters": [ + { "name": "cacheDisabled", "type": "boolean", "description": "Cache disabled state." } + ], + "description": "Toggles ignoring cache for each request. If <code>true</code>, cache will not be used." + }, + { + "name": "setBypassServiceWorker", + "parameters": [ + { "name": "bypass", "type": "boolean", "description": "Bypass service worker and load from network." } + ], + "hidden": true, + "description": "Toggles ignoring of service worker for each request." + }, + { + "name": "setDataSizeLimitsForTest", + "parameters": [ + { "name": "maxTotalSize", "type": "integer", "description": "Maximum total buffer size." }, + { "name": "maxResourceSize", "type": "integer", "description": "Maximum per-resource size." } + ], + "description": "For testing.", + "hidden": true + }, + { + "name": "getCertificateDetails", + "description": "Returns details for the given certificate.", + "parameters": [ + { "name": "certificateId", "$ref": "CertificateId", "description": "ID of the certificate to get details for." } + ], + "returns": [ + { "name": "result", "$ref": "CertificateDetails", "description": "Certificate details." } + ], + "handlers": ["browser"] + }, + { + "name": "showCertificateViewer", + "description": "Displays native dialog with the certificate details.", + "parameters": [ + { "name": "certificateId", "$ref": "CertificateId", "description": "Certificate id." } + ], + "handlers": ["browser"] + } + ], + "events": [ + { + "name": "resourceChangedPriority", + "description": "Fired when resource loading priority is changed", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "newPriority", "$ref": "ResourcePriority", "description": "New priority" }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." } + ], + "hidden": true + }, + { + "name": "requestWillBeSent", + "description": "Fired when page is about to send HTTP request.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "frameId", "$ref": "Page.FrameId", "description": "Frame identifier.", "hidden": true }, + { "name": "loaderId", "$ref": "LoaderId", "description": "Loader identifier." }, + { "name": "documentURL", "type": "string", "description": "URL of the document this request is loaded for." }, + { "name": "request", "$ref": "Request", "description": "Request data." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "wallTime", "$ref": "Timestamp", "hidden": true, "description": "UTC Timestamp." }, + { "name": "initiator", "$ref": "Initiator", "description": "Request initiator." }, + { "name": "redirectResponse", "optional": true, "$ref": "Response", "description": "Redirect response data." }, + { "name": "type", "$ref": "Page.ResourceType", "optional": true, "hidden": true, "description": "Type of this resource." } + ] + }, + { + "name": "requestServedFromCache", + "description": "Fired if request ended up loading from cache.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." } + ] + }, + { + "name": "responseReceived", + "description": "Fired when HTTP response is available.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "frameId", "$ref": "Page.FrameId", "description": "Frame identifier.", "hidden": true }, + { "name": "loaderId", "$ref": "LoaderId", "description": "Loader identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "type", "$ref": "Page.ResourceType", "description": "Resource type." }, + { "name": "response", "$ref": "Response", "description": "Response data." } + ] + }, + { + "name": "dataReceived", + "description": "Fired when data chunk was received over the network.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "dataLength", "type": "integer", "description": "Data chunk length." }, + { "name": "encodedDataLength", "type": "integer", "description": "Actual bytes received (might be less than dataLength for compressed encodings)." } + ] + }, + { + "name": "loadingFinished", + "description": "Fired when HTTP request has finished loading.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "encodedDataLength", "type": "number", "description": "Total number of bytes received for this request." } + ] + }, + { + "name": "loadingFailed", + "description": "Fired when HTTP request has failed to load.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "type", "$ref": "Page.ResourceType", "description": "Resource type." }, + { "name": "errorText", "type": "string", "description": "User friendly error message." }, + { "name": "canceled", "type": "boolean", "optional": true, "description": "True if loading was canceled." }, + { "name": "blockedReason", "$ref": "BlockedReason", "optional": true, "description": "The reason why loading was blocked, if any.", "hidden": true } + ] + }, + { + "name": "webSocketWillSendHandshakeRequest", + "description": "Fired when WebSocket is about to initiate handshake.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "wallTime", "$ref": "Timestamp", "hidden": true, "description": "UTC Timestamp." }, + { "name": "request", "$ref": "WebSocketRequest", "description": "WebSocket request data." } + ], + "hidden": true + }, + { + "name": "webSocketHandshakeResponseReceived", + "description": "Fired when WebSocket handshake response becomes available.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "response", "$ref": "WebSocketResponse", "description": "WebSocket response data." } + ], + "hidden": true + }, + { + "name": "webSocketCreated", + "description": "Fired upon WebSocket creation.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "url", "type": "string", "description": "WebSocket request URL." } + ], + "hidden": true + }, + { + "name": "webSocketClosed", + "description": "Fired when WebSocket is closed.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." } + ], + "hidden": true + }, + { + "name": "webSocketFrameReceived", + "description": "Fired when WebSocket frame is received.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "response", "$ref": "WebSocketFrame", "description": "WebSocket response data." } + ], + "hidden": true + }, + { + "name": "webSocketFrameError", + "description": "Fired when WebSocket frame error occurs.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "errorMessage", "type": "string", "description": "WebSocket frame error message." } + ], + "hidden": true + }, + { + "name": "webSocketFrameSent", + "description": "Fired when WebSocket frame is sent.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "response", "$ref": "WebSocketFrame", "description": "WebSocket response data." } + ], + "hidden": true + }, + { + "name": "eventSourceMessageReceived", + "description": "Fired when EventSource message is received.", + "parameters": [ + { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, + { "name": "eventName", "type": "string", "description": "Message type." }, + { "name": "eventId", "type": "string", "description": "Message identifier." }, + { "name": "data", "type": "string", "description": "Message content." } + ], + "hidden": true + } + ] + }, + { + "domain": "Database", + "hidden": true, + "types": [ + { + "id": "DatabaseId", + "type": "string", + "description": "Unique identifier of Database object.", + "hidden": true + }, + { + "id": "Database", + "type": "object", + "description": "Database object.", + "hidden": true, + "properties": [ + { "name": "id", "$ref": "DatabaseId", "description": "Database ID." }, + { "name": "domain", "type": "string", "description": "Database domain." }, + { "name": "name", "type": "string", "description": "Database name." }, + { "name": "version", "type": "string", "description": "Database version." } + ] + }, + { + "id": "Error", + "type": "object", + "description": "Database error.", + "properties": [ + { "name": "message", "type": "string", "description": "Error message." }, + { "name": "code", "type": "integer", "description": "Error code." } + ] + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables database tracking, database events will now be delivered to the client." + }, + { + "name": "disable", + "description": "Disables database tracking, prevents database events from being sent to the client." + }, + { + "name": "getDatabaseTableNames", + "parameters": [ + { "name": "databaseId", "$ref": "DatabaseId" } + ], + "returns": [ + { "name": "tableNames", "type": "array", "items": { "type": "string" } } + ] + }, + { + "name": "executeSQL", + "async": true, + "parameters": [ + { "name": "databaseId", "$ref": "DatabaseId" }, + { "name": "query", "type": "string" } + ], + "returns": [ + { "name": "columnNames", "type": "array", "optional": true, "items": { "type": "string" } }, + { "name": "values", "type": "array", "optional": true, "items": { "type": "any" }}, + { "name": "sqlError", "$ref": "Error", "optional": true } + ] + } + ], + "events": [ + { + "name": "addDatabase", + "parameters": [ + { "name": "database", "$ref": "Database" } + ] + } + ] + }, + { + "domain": "IndexedDB", + "hidden": true, + "types": [ + { + "id": "DatabaseWithObjectStores", + "type": "object", + "description": "Database with an array of object stores.", + "properties": [ + { "name": "name", "type": "string", "description": "Database name." }, + { "name": "version", "type": "integer", "description": "Database version." }, + { "name": "objectStores", "type": "array", "items": { "$ref": "ObjectStore" }, "description": "Object stores in this database." } + ] + }, + { + "id": "ObjectStore", + "type": "object", + "description": "Object store.", + "properties": [ + { "name": "name", "type": "string", "description": "Object store name." }, + { "name": "keyPath", "$ref": "KeyPath", "description": "Object store key path." }, + { "name": "autoIncrement", "type": "boolean", "description": "If true, object store has auto increment flag set." }, + { "name": "indexes", "type": "array", "items": { "$ref": "ObjectStoreIndex" }, "description": "Indexes in this object store." } + ] + }, + { + "id": "ObjectStoreIndex", + "type": "object", + "description": "Object store index.", + "properties": [ + { "name": "name", "type": "string", "description": "Index name." }, + { "name": "keyPath", "$ref": "KeyPath", "description": "Index key path." }, + { "name": "unique", "type": "boolean", "description": "If true, index is unique." }, + { "name": "multiEntry", "type": "boolean", "description": "If true, index allows multiple entries for a key." } + ] + }, + { + "id": "Key", + "type": "object", + "description": "Key.", + "properties": [ + { "name": "type", "type": "string", "enum": ["number", "string", "date", "array"], "description": "Key type." }, + { "name": "number", "type": "number", "optional": true, "description": "Number value." }, + { "name": "string", "type": "string", "optional": true, "description": "String value." }, + { "name": "date", "type": "number", "optional": true, "description": "Date value." }, + { "name": "array", "type": "array", "optional": true, "items": { "$ref": "Key" }, "description": "Array value." } + ] + }, + { + "id": "KeyRange", + "type": "object", + "description": "Key range.", + "properties": [ + { "name": "lower", "$ref": "Key", "optional": true, "description": "Lower bound." }, + { "name": "upper", "$ref": "Key", "optional": true, "description": "Upper bound." }, + { "name": "lowerOpen", "type": "boolean", "description": "If true lower bound is open." }, + { "name": "upperOpen", "type": "boolean", "description": "If true upper bound is open." } + ] + }, + { + "id": "DataEntry", + "type": "object", + "description": "Data entry.", + "properties": [ + { "name": "key", "type": "string", "description": "JSON-stringified key object." }, + { "name": "primaryKey", "type": "string", "description": "JSON-stringified primary key object." }, + { "name": "value", "type": "string", "description": "JSON-stringified value object." } + ] + }, + { + "id": "KeyPath", + "type": "object", + "description": "Key path.", + "properties": [ + { "name": "type", "type": "string", "enum": ["null", "string", "array"], "description": "Key path type." }, + { "name": "string", "type": "string", "optional": true, "description": "String value." }, + { "name": "array", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Array value." } + ] + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables events from backend." + }, + { + "name": "disable", + "description": "Disables events from backend." + }, + { + "name": "requestDatabaseNames", + "async": true, + "parameters": [ + { "name": "securityOrigin", "type": "string", "description": "Security origin." } + ], + "returns": [ + { "name": "databaseNames", "type": "array", "items": { "type": "string" }, "description": "Database names for origin." } + ], + "description": "Requests database names for given security origin." + }, + { + "name": "requestDatabase", + "async": true, + "parameters": [ + { "name": "securityOrigin", "type": "string", "description": "Security origin." }, + { "name": "databaseName", "type": "string", "description": "Database name." } + ], + "returns": [ + { "name": "databaseWithObjectStores", "$ref": "DatabaseWithObjectStores", "description": "Database with an array of object stores." } + ], + "description": "Requests database with given name in given frame." + }, + { + "name": "requestData", + "async": true, + "parameters": [ + { "name": "securityOrigin", "type": "string", "description": "Security origin." }, + { "name": "databaseName", "type": "string", "description": "Database name." }, + { "name": "objectStoreName", "type": "string", "description": "Object store name." }, + { "name": "indexName", "type": "string", "description": "Index name, empty string for object store data requests." }, + { "name": "skipCount", "type": "integer", "description": "Number of records to skip." }, + { "name": "pageSize", "type": "integer", "description": "Number of records to fetch." }, + { "name": "keyRange", "$ref": "KeyRange", "optional": true, "description": "Key range." } + ], + "returns": [ + { "name": "objectStoreDataEntries", "type": "array", "items": { "$ref": "DataEntry" }, "description": "Array of object store data entries." }, + { "name": "hasMore", "type": "boolean", "description": "If true, there are more entries to fetch in the given range." } + ], + "description": "Requests data from object store or index." + }, + { + "name": "clearObjectStore", + "async": true, + "parameters": [ + { "name": "securityOrigin", "type": "string", "description": "Security origin." }, + { "name": "databaseName", "type": "string", "description": "Database name." }, + { "name": "objectStoreName", "type": "string", "description": "Object store name." } + ], + "returns": [ + ], + "description": "Clears all entries from an object store." + } + ] + }, + { + "domain": "CacheStorage", + "hidden": true, + "types": [ + { + "id": "CacheId", + "type": "string", + "description": "Unique identifier of the Cache object." + }, + { + "id": "DataEntry", + "type": "object", + "description": "Data entry.", + "properties": [ + { "name": "request", "type": "string", "description": "Request url spec." }, + { "name": "response", "type": "string", "description": "Response stataus text." } + ] + }, + { + "id": "Cache", + "type": "object", + "description": "Cache identifier.", + "properties": [ + { "name": "cacheId", "$ref": "CacheId", "description": "An opaque unique id of the cache." }, + { "name": "securityOrigin", "type": "string", "description": "Security origin of the cache." }, + { "name": "cacheName", "type": "string", "description": "The name of the cache." } + ] + } + ], + "commands": [ + { + "name": "requestCacheNames", + "async": true, + "parameters": [ + { "name": "securityOrigin", "type": "string", "description": "Security origin." } + ], + "returns": [ + { "name": "caches", "type": "array", "items": { "$ref": "Cache" }, "description": "Caches for the security origin." } + ], + "description": "Requests cache names." + }, + { + "name": "requestEntries", + "async": true, + "parameters": [ + { "name": "cacheId", "$ref": "CacheId", "description": "ID of cache to get entries from." }, + { "name": "skipCount", "type": "integer", "description": "Number of records to skip." }, + { "name": "pageSize", "type": "integer", "description": "Number of records to fetch." } + ], + "returns": [ + { "name": "cacheDataEntries", "type": "array", "items": { "$ref": "DataEntry" }, "description": "Array of object store data entries." }, + { "name": "hasMore", "type": "boolean", "description": "If true, there are more entries to fetch in the given range." } + ], + "description": "Requests data from cache." + }, + { + "name": "deleteCache", + "async": true, + "parameters": [ + { "name": "cacheId", "$ref": "CacheId", "description": "Id of cache for deletion." } + ], + "description": "Deletes a cache." + }, + { + "name": "deleteEntry", + "async": true, + "parameters": [ + { "name": "cacheId", "$ref": "CacheId", "description": "Id of cache where the entry will be deleted." }, + { "name": "request", "type": "string", "description": "URL spec of the request." } + ], + "description": "Deletes a cache entry." + } + ] + }, + { + "domain": "DOMStorage", + "hidden": true, + "description": "Query and modify DOM storage.", + "types": [ + { + "id": "StorageId", + "type": "object", + "description": "DOM Storage identifier.", + "hidden": true, + "properties": [ + { "name": "securityOrigin", "type": "string", "description": "Security origin for the storage." }, + { "name": "isLocalStorage", "type": "boolean", "description": "Whether the storage is local storage (not session storage)." } + ] + }, + { + "id": "Item", + "type": "array", + "description": "DOM Storage item.", + "hidden": true, + "items": { "type": "string" } + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables storage tracking, storage events will now be delivered to the client." + }, + { + "name": "disable", + "description": "Disables storage tracking, prevents storage events from being sent to the client." + }, + { + "name": "getDOMStorageItems", + "parameters": [ + { "name": "storageId", "$ref": "StorageId" } + ], + "returns": [ + { "name": "entries", "type": "array", "items": { "$ref": "Item" } } + ] + }, + { + "name": "setDOMStorageItem", + "parameters": [ + { "name": "storageId", "$ref": "StorageId" }, + { "name": "key", "type": "string" }, + { "name": "value", "type": "string" } + ] + }, + { + "name": "removeDOMStorageItem", + "parameters": [ + { "name": "storageId", "$ref": "StorageId" }, + { "name": "key", "type": "string" } + ] + } + ], + "events": [ + { + "name": "domStorageItemsCleared", + "parameters": [ + { "name": "storageId", "$ref": "StorageId" } + ] + }, + { + "name": "domStorageItemRemoved", + "parameters": [ + { "name": "storageId", "$ref": "StorageId" }, + { "name": "key", "type": "string" } + ] + }, + { + "name": "domStorageItemAdded", + "parameters": [ + { "name": "storageId", "$ref": "StorageId" }, + { "name": "key", "type": "string" }, + { "name": "newValue", "type": "string" } + ] + }, + { + "name": "domStorageItemUpdated", + "parameters": [ + { "name": "storageId", "$ref": "StorageId" }, + { "name": "key", "type": "string" }, + { "name": "oldValue", "type": "string" }, + { "name": "newValue", "type": "string" } + ] + } + ] + }, + { + "domain": "ApplicationCache", + "hidden": true, + "types": [ + { + "id": "ApplicationCacheResource", + "type": "object", + "description": "Detailed application cache resource information.", + "properties": [ + { "name": "url", "type": "string", "description": "Resource url." }, + { "name": "size", "type": "integer", "description": "Resource size." }, + { "name": "type", "type": "string", "description": "Resource type." } + ] + }, + { + "id": "ApplicationCache", + "type": "object", + "description": "Detailed application cache information.", + "properties": [ + { "name": "manifestURL", "type": "string", "description": "Manifest URL." }, + { "name": "size", "type": "number", "description": "Application cache size." }, + { "name": "creationTime", "type": "number", "description": "Application cache creation time." }, + { "name": "updateTime", "type": "number", "description": "Application cache update time." }, + { "name": "resources", "type": "array", "items": { "$ref": "ApplicationCacheResource" }, "description": "Application cache resources." } + ] + }, + { + "id": "FrameWithManifest", + "type": "object", + "description": "Frame identifier - manifest URL pair.", + "properties": [ + { "name": "frameId", "$ref": "Page.FrameId", "description": "Frame identifier." }, + { "name": "manifestURL", "type": "string", "description": "Manifest URL." }, + { "name": "status", "type": "integer", "description": "Application cache status." } + ] + } + ], + "commands": [ + { + "name": "getFramesWithManifests", + "returns": [ + { "name": "frameIds", "type": "array", "items": { "$ref": "FrameWithManifest" }, "description": "Array of frame identifiers with manifest urls for each frame containing a document associated with some application cache." } + ], + "description": "Returns array of frame identifiers with manifest urls for each frame containing a document associated with some application cache." + }, + { + "name": "enable", + "description": "Enables application cache domain notifications." + }, + { + "name": "getManifestForFrame", + "parameters": [ + { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame containing document whose manifest is retrieved." } + ], + "returns": [ + { "name": "manifestURL", "type": "string", "description": "Manifest URL for document in the given frame." } + ], + "description": "Returns manifest URL for document in the given frame." + }, + { + "name": "getApplicationCacheForFrame", + "parameters": [ + { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame containing document whose application cache is retrieved." } + ], + "returns": [ + { "name": "applicationCache", "$ref": "ApplicationCache", "description": "Relevant application cache data for the document in given frame." } + ], + "description": "Returns relevant application cache data for the document in given frame." + } + ], + "events": [ + { + "name": "applicationCacheStatusUpdated", + "parameters": [ + { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame containing document whose application cache updated status." }, + { "name": "manifestURL", "type": "string", "description": "Manifest URL." }, + { "name": "status", "type": "integer", "description": "Updated application cache status." } + ] + }, + { + "name": "networkStateUpdated", + "parameters": [ + { "name": "isNowOnline", "type": "boolean" } + ] + } + ] + }, + { + "domain": "DOM", + "description": "This domain exposes DOM read/write operations. Each DOM Node is represented with its mirror object that has an <code>id</code>. This <code>id</code> can be used to get additional information on the Node, resolve it into the JavaScript object wrapper, etc. It is important that client receives DOM events only for the nodes that are known to the client. Backend keeps track of the nodes that were sent to the client and never sends the same node twice. It is client's responsibility to collect information about the nodes that were sent to the client.<p>Note that <code>iframe</code> owner elements will return corresponding document elements as their child nodes.</p>", + "types": [ + { + "id": "NodeId", + "type": "integer", + "description": "Unique DOM node identifier." + }, + { + "id": "BackendNodeId", + "type": "integer", + "description": "Unique DOM node identifier used to reference a node that may not have been pushed to the front-end.", + "hidden": true + }, + { + "id": "BackendNode", + "type": "object", + "properties": [ + { "name": "nodeType", "type": "integer", "description": "<code>Node</code>'s nodeType." }, + { "name": "nodeName", "type": "string", "description": "<code>Node</code>'s nodeName." }, + { "name": "backendNodeId", "$ref": "BackendNodeId" } + ], + "hidden": true, + "description": "Backend node with a friendly name." + }, + { + "id": "PseudoType", + "type": "string", + "enum": [ + "first-line", + "first-letter", + "before", + "after", + "backdrop", + "selection", + "first-line-inherited", + "scrollbar", + "scrollbar-thumb", + "scrollbar-button", + "scrollbar-track", + "scrollbar-track-piece", + "scrollbar-corner", + "resizer", + "input-list-button" + ], + "description": "Pseudo element type." + }, + { + "id": "ShadowRootType", + "type": "string", + "enum": ["user-agent", "open", "closed"], + "description": "Shadow root type." + }, + { + "id": "Node", + "type": "object", + "properties": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Node identifier that is passed into the rest of the DOM messages as the <code>nodeId</code>. Backend will only push node with given <code>id</code> once. It is aware of all requested nodes and will only fire DOM events for nodes known to the client." }, + { "name": "nodeType", "type": "integer", "description": "<code>Node</code>'s nodeType." }, + { "name": "nodeName", "type": "string", "description": "<code>Node</code>'s nodeName." }, + { "name": "localName", "type": "string", "description": "<code>Node</code>'s localName." }, + { "name": "nodeValue", "type": "string", "description": "<code>Node</code>'s nodeValue." }, + { "name": "childNodeCount", "type": "integer", "optional": true, "description": "Child count for <code>Container</code> nodes." }, + { "name": "children", "type": "array", "optional": true, "items": { "$ref": "Node" }, "description": "Child nodes of this node when requested with children." }, + { "name": "attributes", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Attributes of the <code>Element</code> node in the form of flat array <code>[name1, value1, name2, value2]</code>." }, + { "name": "documentURL", "type": "string", "optional": true, "description": "Document URL that <code>Document</code> or <code>FrameOwner</code> node points to." }, + { "name": "baseURL", "type": "string", "optional": true, "description": "Base URL that <code>Document</code> or <code>FrameOwner</code> node uses for URL completion.", "hidden": true }, + { "name": "publicId", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s publicId." }, + { "name": "systemId", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s systemId." }, + { "name": "internalSubset", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s internalSubset." }, + { "name": "xmlVersion", "type": "string", "optional": true, "description": "<code>Document</code>'s XML version in case of XML documents." }, + { "name": "name", "type": "string", "optional": true, "description": "<code>Attr</code>'s name." }, + { "name": "value", "type": "string", "optional": true, "description": "<code>Attr</code>'s value." }, + { "name": "pseudoType", "$ref": "PseudoType", "optional": true, "description": "Pseudo element type for this node." }, + { "name": "shadowRootType", "$ref": "ShadowRootType", "optional": true, "description": "Shadow root type." }, + { "name": "frameId", "$ref": "Page.FrameId", "optional": true, "description": "Frame ID for frame owner elements.", "hidden": true }, + { "name": "contentDocument", "$ref": "Node", "optional": true, "description": "Content document for frame owner elements." }, + { "name": "shadowRoots", "type": "array", "optional": true, "items": { "$ref": "Node" }, "description": "Shadow root list for given element host.", "hidden": true }, + { "name": "templateContent", "$ref": "Node", "optional": true, "description": "Content document fragment for template elements.", "hidden": true }, + { "name": "pseudoElements", "type": "array", "items": { "$ref": "Node" }, "optional": true, "description": "Pseudo elements associated with this node.", "hidden": true }, + { "name": "importedDocument", "$ref": "Node", "optional": true, "description": "Import document for the HTMLImport links." }, + { "name": "distributedNodes", "type": "array", "items": { "$ref": "BackendNode" }, "optional": true, "description": "Distributed nodes for given insertion point.", "hidden": true } + ], + "description": "DOM interaction is implemented in terms of mirror objects that represent the actual DOM nodes. DOMNode is a base node mirror type." + }, + { + "id": "RGBA", + "type": "object", + "properties": [ + { "name": "r", "type": "integer", "description": "The red component, in the [0-255] range." }, + { "name": "g", "type": "integer", "description": "The green component, in the [0-255] range." }, + { "name": "b", "type": "integer", "description": "The blue component, in the [0-255] range." }, + { "name": "a", "type": "number", "optional": true, "description": "The alpha component, in the [0-1] range (default: 1)." } + ], + "description": "A structure holding an RGBA color." + }, + { + "id": "Quad", + "type": "array", + "items": { "type": "number" }, + "minItems": 8, + "maxItems": 8, + "description": "An array of quad vertices, x immediately followed by y for each point, points clock-wise.", + "hidden": true + }, + { + "id": "BoxModel", + "type": "object", + "hidden": true, + "properties": [ + { "name": "content", "$ref": "Quad", "description": "Content box" }, + { "name": "padding", "$ref": "Quad", "description": "Padding box" }, + { "name": "border", "$ref": "Quad", "description": "Border box" }, + { "name": "margin", "$ref": "Quad", "description": "Margin box" }, + { "name": "width", "type": "integer", "description": "Node width" }, + { "name": "height", "type": "integer", "description": "Node height" }, + { "name": "shapeOutside", "$ref": "ShapeOutsideInfo", "optional": true, "description": "Shape outside coordinates" } + ], + "description": "Box model." + }, + { + "id": "ShapeOutsideInfo", + "type": "object", + "hidden": true, + "properties": [ + { "name": "bounds", "$ref": "Quad", "description": "Shape bounds" }, + { "name": "shape", "type": "array", "items": { "type": "any"}, "description": "Shape coordinate details" }, + { "name": "marginShape", "type": "array", "items": { "type": "any"}, "description": "Margin shape bounds" } + ], + "description": "CSS Shape Outside details." + }, + { + "id": "Rect", + "type": "object", + "hidden": true, + "properties": [ + { "name": "x", "type": "number", "description": "X coordinate" }, + { "name": "y", "type": "number", "description": "Y coordinate" }, + { "name": "width", "type": "number", "description": "Rectangle width" }, + { "name": "height", "type": "number", "description": "Rectangle height" } + ], + "description": "Rectangle." + }, + { + "id": "HighlightConfig", + "type": "object", + "properties": [ + { "name": "showInfo", "type": "boolean", "optional": true, "description": "Whether the node info tooltip should be shown (default: false)." }, + { "name": "showRulers", "type": "boolean", "optional": true, "description": "Whether the rulers should be shown (default: false)." }, + { "name": "showExtensionLines", "type": "boolean", "optional": true, "description": "Whether the extension lines from node to the rulers should be shown (default: false)." }, + { "name": "displayAsMaterial", "type": "boolean", "optional": true, "hidden": true}, + { "name": "contentColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight fill color (default: transparent)." }, + { "name": "paddingColor", "$ref": "RGBA", "optional": true, "description": "The padding highlight fill color (default: transparent)." }, + { "name": "borderColor", "$ref": "RGBA", "optional": true, "description": "The border highlight fill color (default: transparent)." }, + { "name": "marginColor", "$ref": "RGBA", "optional": true, "description": "The margin highlight fill color (default: transparent)." }, + { "name": "eventTargetColor", "$ref": "RGBA", "optional": true, "hidden": true, "description": "The event target element highlight fill color (default: transparent)." }, + { "name": "shapeColor", "$ref": "RGBA", "optional": true, "hidden": true, "description": "The shape outside fill color (default: transparent)." }, + { "name": "shapeMarginColor", "$ref": "RGBA", "optional": true, "hidden": true, "description": "The shape margin fill color (default: transparent)." }, + { "name": "selectorList", "type": "string", "optional": true, "description": "Selectors to highlight relevant nodes."} + ], + "description": "Configuration data for the highlighting of page elements." + }, + { + "id": "InspectMode", + "type": "string", + "hidden": true, + "enum": [ + "searchForNode", + "searchForUAShadowDOM", + "showLayoutEditor", + "none" + ] + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables DOM agent for the given page." + }, + { + "name": "disable", + "description": "Disables DOM agent for the given page." + }, + { + "name": "getDocument", + "returns": [ + { "name": "root", "$ref": "Node", "description": "Resulting node." } + ], + "description": "Returns the root DOM node to the caller." + }, + { + "name": "requestChildNodes", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get children for." }, + { "name": "depth", "type": "integer", "optional": true, "description": "The maximum depth at which children should be retrieved, defaults to 1. Use -1 for the entire subtree or provide an integer larger than 0.", "hidden": true } + ], + "description": "Requests that children of the node with given id are returned to the caller in form of <code>setChildNodes</code> events where not only immediate children are retrieved, but all children down to the specified depth." + }, + { + "name": "querySelector", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to query upon." }, + { "name": "selector", "type": "string", "description": "Selector string." } + ], + "returns": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Query selector result." } + ], + "description": "Executes <code>querySelector</code> on a given node." + }, + { + "name": "querySelectorAll", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to query upon." }, + { "name": "selector", "type": "string", "description": "Selector string." } + ], + "returns": [ + { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Query selector result." } + ], + "description": "Executes <code>querySelectorAll</code> on a given node." + }, + { + "name": "setNodeName", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set name for." }, + { "name": "name", "type": "string", "description": "New node's name." } + ], + "returns": [ + { "name": "nodeId", "$ref": "NodeId", "description": "New node's id." } + ], + "description": "Sets node name for a node with given id." + }, + { + "name": "setNodeValue", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set value for." }, + { "name": "value", "type": "string", "description": "New node's value." } + ], + "description": "Sets node value for a node with given id." + }, + { + "name": "removeNode", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to remove." } + ], + "description": "Removes node with given id." + }, + { + "name": "setAttributeValue", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to set attribute for." }, + { "name": "name", "type": "string", "description": "Attribute name." }, + { "name": "value", "type": "string", "description": "Attribute value." } + ], + "description": "Sets attribute for an element with given id." + }, + { + "name": "setAttributesAsText", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to set attributes for." }, + { "name": "text", "type": "string", "description": "Text with a number of attributes. Will parse this text using HTML parser." }, + { "name": "name", "type": "string", "optional": true, "description": "Attribute name to replace with new attributes derived from text in case text parsed successfully." } + ], + "description": "Sets attributes on element with given id. This method is useful when user edits some existing attribute value and types in several attribute name/value pairs." + }, + { + "name": "removeAttribute", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to remove attribute from." }, + { "name": "name", "type": "string", "description": "Name of the attribute to remove." } + ], + "description": "Removes attribute with given name from an element with given id." + }, + { + "name": "getOuterHTML", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get markup for." } + ], + "returns": [ + { "name": "outerHTML", "type": "string", "description": "Outer HTML markup." } + ], + "description": "Returns node's HTML markup." + }, + { + "name": "setOuterHTML", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set markup for." }, + { "name": "outerHTML", "type": "string", "description": "Outer HTML markup to set." } + ], + "description": "Sets node HTML markup, returns new node id." + }, + { + "name": "performSearch", + "parameters": [ + { "name": "query", "type": "string", "description": "Plain text or query selector or XPath search query." }, + { "name": "includeUserAgentShadowDOM", "type": "boolean", "optional": true, "description": "True to search in user agent shadow DOM.", "hidden": true } + ], + "returns": [ + { "name": "searchId", "type": "string", "description": "Unique search session identifier." }, + { "name": "resultCount", "type": "integer", "description": "Number of search results." } + ], + "description": "Searches for a given string in the DOM tree. Use <code>getSearchResults</code> to access search results or <code>cancelSearch</code> to end this search session.", + "hidden": true + }, + { + "name": "getSearchResults", + "parameters": [ + { "name": "searchId", "type": "string", "description": "Unique search session identifier." }, + { "name": "fromIndex", "type": "integer", "description": "Start index of the search result to be returned." }, + { "name": "toIndex", "type": "integer", "description": "End index of the search result to be returned." } + ], + "returns": [ + { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Ids of the search result nodes." } + ], + "description": "Returns search results from given <code>fromIndex</code> to given <code>toIndex</code> from the sarch with the given identifier.", + "hidden": true + }, + { + "name": "discardSearchResults", + "parameters": [ + { "name": "searchId", "type": "string", "description": "Unique search session identifier." } + ], + "description": "Discards search results from the session with the given id. <code>getSearchResults</code> should no longer be called for that search.", + "hidden": true + }, + { + "name": "requestNode", + "parameters": [ + { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "JavaScript object id to convert into node." } + ], + "returns": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Node id for given object." } + ], + "description": "Requests that the node is sent to the caller given the JavaScript node object reference. All nodes that form the path from the node to the root are also sent to the client as a series of <code>setChildNodes</code> notifications." + }, + { + "name": "setInspectMode", + "hidden": true, + "parameters": [ + { "name": "mode", "$ref": "InspectMode", "description": "Set an inspection mode." }, + { "name": "highlightConfig", "$ref": "HighlightConfig", "optional": true, "description": "A descriptor for the highlight appearance of hovered-over nodes. May be omitted if <code>enabled == false</code>." } + ], + "description": "Enters the 'inspect' mode. In this mode, elements that user is hovering over are highlighted. Backend then generates 'inspectNodeRequested' event upon element selection." + }, + { + "name": "highlightRect", + "parameters": [ + { "name": "x", "type": "integer", "description": "X coordinate" }, + { "name": "y", "type": "integer", "description": "Y coordinate" }, + { "name": "width", "type": "integer", "description": "Rectangle width" }, + { "name": "height", "type": "integer", "description": "Rectangle height" }, + { "name": "color", "$ref": "RGBA", "optional": true, "description": "The highlight fill color (default: transparent)." }, + { "name": "outlineColor", "$ref": "RGBA", "optional": true, "description": "The highlight outline color (default: transparent)." } + ], + "description": "Highlights given rectangle. Coordinates are absolute with respect to the main frame viewport." + }, + { + "name": "highlightQuad", + "parameters": [ + { "name": "quad", "$ref": "Quad", "description": "Quad to highlight" }, + { "name": "color", "$ref": "RGBA", "optional": true, "description": "The highlight fill color (default: transparent)." }, + { "name": "outlineColor", "$ref": "RGBA", "optional": true, "description": "The highlight outline color (default: transparent)." } + ], + "description": "Highlights given quad. Coordinates are absolute with respect to the main frame viewport.", + "hidden": true + }, + { + "name": "highlightNode", + "parameters": [ + { "name": "highlightConfig", "$ref": "HighlightConfig", "description": "A descriptor for the highlight appearance." }, + { "name": "nodeId", "$ref": "NodeId", "optional": true, "description": "Identifier of the node to highlight." }, + { "name": "backendNodeId", "$ref": "BackendNodeId", "optional": true, "description": "Identifier of the backend node to highlight." }, + { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "optional": true, "description": "JavaScript object id of the node to be highlighted.", "hidden": true } + ], + "description": "Highlights DOM node with given id or with the given JavaScript object wrapper. Either nodeId or objectId must be specified." + }, + { + "name": "hideHighlight", + "description": "Hides DOM node highlight." + }, + { + "name": "highlightFrame", + "parameters": [ + { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame to highlight." }, + { "name": "contentColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight fill color (default: transparent)." }, + { "name": "contentOutlineColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight outline color (default: transparent)." } + ], + "description": "Highlights owner element of the frame with given id.", + "hidden": true + }, + { + "name": "pushNodeByPathToFrontend", + "parameters": [ + { "name": "path", "type": "string", "description": "Path to node in the proprietary format." } + ], + "returns": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node for given path." } + ], + "description": "Requests that the node is sent to the caller given its path. // FIXME, use XPath", + "hidden": true + }, + { + "name": "pushNodesByBackendIdsToFrontend", + "parameters": [ + { "name": "backendNodeIds", "type": "array", "items": {"$ref": "BackendNodeId"}, "description": "The array of backend node ids." } + ], + "returns": [ + { "name": "nodeIds", "type": "array", "items": {"$ref": "NodeId"}, "description": "The array of ids of pushed nodes that correspond to the backend ids specified in backendNodeIds." } + ], + "description": "Requests that a batch of nodes is sent to the caller given their backend node ids.", + "hidden": true + }, + { + "name": "setInspectedNode", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "DOM node id to be accessible by means of $x command line API." } + ], + "description": "Enables console to refer to the node with given id via $x (see Command Line API for more details $x functions).", + "hidden": true + }, + { + "name": "resolveNode", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to resolve." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." } + ], + "returns": [ + { "name": "object", "$ref": "Runtime.RemoteObject", "description": "JavaScript object wrapper for given node." } + ], + "description": "Resolves JavaScript node object for given node id." + }, + { + "name": "getAttributes", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to retrieve attibutes for." } + ], + "returns": [ + { "name": "attributes", "type": "array", "items": { "type": "string" }, "description": "An interleaved array of node attribute names and values." } + ], + "description": "Returns attributes for the specified node." + }, + { + "name": "copyTo", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to copy." }, + { "name": "targetNodeId", "$ref": "NodeId", "description": "Id of the element to drop the copy into." }, + { "name": "insertBeforeNodeId", "$ref": "NodeId", "optional": true, "description": "Drop the copy before this node (if absent, the copy becomes the last child of <code>targetNodeId</code>)." } + ], + "returns": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node clone." } + ], + "description": "Creates a deep copy of the specified node and places it into the target container before the given anchor.", + "hidden": true + }, + { + "name": "moveTo", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to move." }, + { "name": "targetNodeId", "$ref": "NodeId", "description": "Id of the element to drop the moved node into." }, + { "name": "insertBeforeNodeId", "$ref": "NodeId", "optional": true, "description": "Drop node before this one (if absent, the moved node becomes the last child of <code>targetNodeId</code>)." } + ], + "returns": [ + { "name": "nodeId", "$ref": "NodeId", "description": "New id of the moved node." } + ], + "description": "Moves node into the new container, places it before the given anchor." + }, + { + "name": "undo", + "description": "Undoes the last performed action.", + "hidden": true + }, + { + "name": "redo", + "description": "Re-does the last undone action.", + "hidden": true + }, + { + "name": "markUndoableState", + "description": "Marks last undoable state.", + "hidden": true + }, + { + "name": "focus", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to focus." } + ], + "description": "Focuses the given element.", + "hidden": true + }, + { + "name": "setFileInputFiles", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the file input node to set files for." }, + { "name": "files", "type": "array", "items": { "type": "string" }, "description": "Array of file paths to set." } + ], + "description": "Sets files for the given file input element.", + "hidden": true, + "handlers": ["browser", "renderer"] + }, + { + "name": "getBoxModel", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get box model for." } + ], + "returns": [ + { "name": "model", "$ref": "BoxModel", "description": "Box model for the node." } + ], + "description": "Returns boxes for the currently selected nodes.", + "hidden": true + }, + { + "name": "getNodeForLocation", + "parameters": [ + { "name": "x", "type": "integer", "description": "X coordinate." }, + { "name": "y", "type": "integer", "description": "Y coordinate." } + ], + "returns": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node at given coordinates." } + ], + "description": "Returns node id at given location.", + "hidden": true + }, + { + "name": "getRelayoutBoundary", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node." } + ], + "returns": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Relayout boundary node id for the given node." } + ], + "description": "Returns the id of the nearest ancestor that is a relayout boundary.", + "hidden": true + }, + { + "name": "getHighlightObjectForTest", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get highlight object for." } + ], + "returns": [ + { "name": "highlight", "type": "object", "description": "Highlight data for the node." } + ], + "description": "For testing.", + "hidden": true + } + ], + "events": [ + { + "name": "documentUpdated", + "description": "Fired when <code>Document</code> has been totally updated. Node ids are no longer valid." + }, + { + "name": "inspectNodeRequested", + "parameters": [ + { "name": "backendNodeId", "$ref": "BackendNodeId", "description": "Id of the node to inspect." } + ], + "description": "Fired when the node should be inspected. This happens after call to <code>setInspectMode</code>.", + "hidden" : true + }, + { + "name": "setChildNodes", + "parameters": [ + { "name": "parentId", "$ref": "NodeId", "description": "Parent node id to populate with children." }, + { "name": "nodes", "type": "array", "items": { "$ref": "Node" }, "description": "Child nodes array." } + ], + "description": "Fired when backend wants to provide client with the missing DOM structure. This happens upon most of the calls requesting node ids." + }, + { + "name": "attributeModified", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, + { "name": "name", "type": "string", "description": "Attribute name." }, + { "name": "value", "type": "string", "description": "Attribute value." } + ], + "description": "Fired when <code>Element</code>'s attribute is modified." + }, + { + "name": "attributeRemoved", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, + { "name": "name", "type": "string", "description": "A ttribute name." } + ], + "description": "Fired when <code>Element</code>'s attribute is removed." + }, + { + "name": "inlineStyleInvalidated", + "parameters": [ + { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Ids of the nodes for which the inline styles have been invalidated." } + ], + "description": "Fired when <code>Element</code>'s inline style is modified via a CSS property modification.", + "hidden": true + }, + { + "name": "characterDataModified", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, + { "name": "characterData", "type": "string", "description": "New text value." } + ], + "description": "Mirrors <code>DOMCharacterDataModified</code> event." + }, + { + "name": "childNodeCountUpdated", + "parameters": [ + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, + { "name": "childNodeCount", "type": "integer", "description": "New node count." } + ], + "description": "Fired when <code>Container</code>'s child node count has changed." + }, + { + "name": "childNodeInserted", + "parameters": [ + { "name": "parentNodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, + { "name": "previousNodeId", "$ref": "NodeId", "description": "If of the previous siblint." }, + { "name": "node", "$ref": "Node", "description": "Inserted node data." } + ], + "description": "Mirrors <code>DOMNodeInserted</code> event." + }, + { + "name": "childNodeRemoved", + "parameters": [ + { "name": "parentNodeId", "$ref": "NodeId", "description": "Parent id." }, + { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has been removed." } + ], + "description": "Mirrors <code>DOMNodeRemoved</code> event." + }, + { + "name": "shadowRootPushed", + "parameters": [ + { "name": "hostId", "$ref": "NodeId", "description": "Host element id." }, + { "name": "root", "$ref": "Node", "description": "Shadow root." } + ], + "description": "Called when shadow root is pushed into the element.", + "hidden": true + }, + { + "name": "shadowRootPopped", + "parameters": [ + { "name": "hostId", "$ref": "NodeId", "description": "Host element id." }, + { "name": "rootId", "$ref": "NodeId", "description": "Shadow root id." } + ], + "description": "Called when shadow root is popped from the element.", + "hidden": true + }, + { + "name": "pseudoElementAdded", + "parameters": [ + { "name": "parentId", "$ref": "NodeId", "description": "Pseudo element's parent element id." }, + { "name": "pseudoElement", "$ref": "Node", "description": "The added pseudo element." } + ], + "description": "Called when a pseudo element is added to an element.", + "hidden": true + }, + { + "name": "pseudoElementRemoved", + "parameters": [ + { "name": "parentId", "$ref": "NodeId", "description": "Pseudo element's parent element id." }, + { "name": "pseudoElementId", "$ref": "NodeId", "description": "The removed pseudo element id." } + ], + "description": "Called when a pseudo element is removed from an element.", + "hidden": true + }, + { + "name": "distributedNodesUpdated", + "parameters": [ + { "name": "insertionPointId", "$ref": "NodeId", "description": "Insertion point where distrubuted nodes were updated." }, + { "name": "distributedNodes", "type": "array", "items": { "$ref": "BackendNode" }, "description": "Distributed nodes for given insertion point." } + ], + "description": "Called when distrubution is changed.", + "hidden": true + }, + { + "name": "nodeHighlightRequested", + "parameters": [ + {"name": "nodeId", "$ref": "NodeId"} + ], + "hidden": true + } + ] + }, + { + "domain": "CSS", + "hidden": true, + "description": "This domain exposes CSS read/write operations. All CSS objects (stylesheets, rules, and styles) have an associated <code>id</code> used in subsequent operations on the related object. Each object type has a specific <code>id</code> structure, and those are not interchangeable between objects of different kinds. CSS objects can be loaded using the <code>get*ForNode()</code> calls (which accept a DOM node id). A client can also discover all the existing stylesheets with the <code>getAllStyleSheets()</code> method (or keeping track of the <code>styleSheetAdded</code>/<code>styleSheetRemoved</code> events) and subsequently load the required stylesheet contents using the <code>getStyleSheet[Text]()</code> methods.", + "types": [ + { + "id": "StyleSheetId", + "type": "string" + }, + { + "id": "StyleSheetOrigin", + "type": "string", + "enum": ["injected", "user-agent", "inspector", "regular"], + "description": "Stylesheet type: \"injected\" for stylesheets injected via extension, \"user-agent\" for user-agent stylesheets, \"inspector\" for stylesheets created by the inspector (i.e. those holding the \"via inspector\" rules), \"regular\" for regular stylesheets." + }, + { + "id": "PseudoElementMatches", + "type": "object", + "properties": [ + { "name": "pseudoType", "$ref": "DOM.PseudoType", "description": "Pseudo element type."}, + { "name": "matches", "type": "array", "items": { "$ref": "RuleMatch" }, "description": "Matches of CSS rules applicable to the pseudo style."} + ], + "description": "CSS rule collection for a single pseudo style." + }, + { + "id": "InheritedStyleEntry", + "type": "object", + "properties": [ + { "name": "inlineStyle", "$ref": "CSSStyle", "optional": true, "description": "The ancestor node's inline style, if any, in the style inheritance chain." }, + { "name": "matchedCSSRules", "type": "array", "items": { "$ref": "RuleMatch" }, "description": "Matches of CSS rules matching the ancestor node in the style inheritance chain." } + ], + "description": "Inherited CSS rule collection from ancestor node." + }, + { + "id": "RuleMatch", + "type": "object", + "properties": [ + { "name": "rule", "$ref": "CSSRule", "description": "CSS rule in the match." }, + { "name": "matchingSelectors", "type": "array", "items": { "type": "integer" }, "description": "Matching selector indices in the rule's selectorList selectors (0-based)." } + ], + "description": "Match data for a CSS rule." + }, + { + "id": "Value", + "type": "object", + "properties": [ + { "name": "text", "type": "string", "description": "Value text." }, + { "name": "range", "$ref": "SourceRange", "optional": true, "description": "Value range in the underlying resource (if available)." } + ], + "description": "Data for a simple selector (these are delimited by commas in a selector list)." + }, + { + "id": "SelectorList", + "type": "object", + "properties": [ + { "name": "selectors", "type": "array", "items": { "$ref": "Value" }, "description": "Selectors in the list." }, + { "name": "text", "type": "string", "description": "Rule selector text." } + ], + "description": "Selector list data." + }, + { + "id": "CSSStyleSheetHeader", + "type": "object", + "properties": [ + { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "The stylesheet identifier."}, + { "name": "frameId", "$ref": "Page.FrameId", "description": "Owner frame identifier."}, + { "name": "sourceURL", "type": "string", "description": "Stylesheet resource URL."}, + { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with the stylesheet (if any)." }, + { "name": "origin", "$ref": "StyleSheetOrigin", "description": "Stylesheet origin."}, + { "name": "title", "type": "string", "description": "Stylesheet title."}, + { "name": "ownerNode", "$ref": "DOM.BackendNodeId", "optional": true, "description": "The backend id for the owner node of the stylesheet." }, + { "name": "disabled", "type": "boolean", "description": "Denotes whether the stylesheet is disabled."}, + { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "Whether the sourceURL field value comes from the sourceURL comment." }, + { "name": "isInline", "type": "boolean", "description": "Whether this stylesheet is created for STYLE tag by parser. This flag is not set for document.written STYLE tags." }, + { "name": "startLine", "type": "number", "description": "Line offset of the stylesheet within the resource (zero based)." }, + { "name": "startColumn", "type": "number", "description": "Column offset of the stylesheet within the resource (zero based)." } + ], + "description": "CSS stylesheet metainformation." + }, + { + "id": "CSSRule", + "type": "object", + "properties": [ + { "name": "styleSheetId", "$ref": "StyleSheetId", "optional": true, "description": "The css style sheet identifier (absent for user agent stylesheet and user-specified stylesheet rules) this rule came from." }, + { "name": "selectorList", "$ref": "SelectorList", "description": "Rule selector data." }, + { "name": "origin", "$ref": "StyleSheetOrigin", "description": "Parent stylesheet's origin."}, + { "name": "style", "$ref": "CSSStyle", "description": "Associated style declaration." }, + { "name": "media", "type": "array", "items": { "$ref": "CSSMedia" }, "optional": true, "description": "Media list array (for rules involving media queries). The array enumerates media queries starting with the innermost one, going outwards." } + ], + "description": "CSS rule representation." + }, + { + "id": "SourceRange", + "type": "object", + "properties": [ + { "name": "startLine", "type": "integer", "description": "Start line of range." }, + { "name": "startColumn", "type": "integer", "description": "Start column of range (inclusive)." }, + { "name": "endLine", "type": "integer", "description": "End line of range" }, + { "name": "endColumn", "type": "integer", "description": "End column of range (exclusive)." } + ], + "description": "Text range within a resource. All numbers are zero-based." + }, + { + "id": "ShorthandEntry", + "type": "object", + "properties": [ + { "name": "name", "type": "string", "description": "Shorthand name." }, + { "name": "value", "type": "string", "description": "Shorthand value." }, + { "name": "important", "type": "boolean", "optional": true, "description": "Whether the property has \"!important\" annotation (implies <code>false</code> if absent)." } + ] + }, + { + "id": "CSSComputedStyleProperty", + "type": "object", + "properties": [ + { "name": "name", "type": "string", "description": "Computed style property name." }, + { "name": "value", "type": "string", "description": "Computed style property value." } + ] + }, + { + "id": "CSSStyle", + "type": "object", + "properties": [ + { "name": "styleSheetId", "$ref": "StyleSheetId", "optional": true, "description": "The css style sheet identifier (absent for user agent stylesheet and user-specified stylesheet rules) this rule came from." }, + { "name": "cssProperties", "type": "array", "items": { "$ref": "CSSProperty" }, "description": "CSS properties in the style." }, + { "name": "shorthandEntries", "type": "array", "items": { "$ref": "ShorthandEntry" }, "description": "Computed values for all shorthands found in the style." }, + { "name": "cssText", "type": "string", "optional": true, "description": "Style declaration text (if available)." }, + { "name": "range", "$ref": "SourceRange", "optional": true, "description": "Style declaration range in the enclosing stylesheet (if available)." } + ], + "description": "CSS style representation." + }, + { + "id": "CSSProperty", + "type": "object", + "properties": [ + { "name": "name", "type": "string", "description": "The property name." }, + { "name": "value", "type": "string", "description": "The property value." }, + { "name": "important", "type": "boolean", "optional": true, "description": "Whether the property has \"!important\" annotation (implies <code>false</code> if absent)." }, + { "name": "implicit", "type": "boolean", "optional": true, "description": "Whether the property is implicit (implies <code>false</code> if absent)." }, + { "name": "text", "type": "string", "optional": true, "description": "The full property text as specified in the style." }, + { "name": "parsedOk", "type": "boolean", "optional": true, "description": "Whether the property is understood by the browser (implies <code>true</code> if absent)." }, + { "name": "disabled", "type": "boolean", "optional": true, "description": "Whether the property is disabled by the user (present for source-based properties only)." }, + { "name": "range", "$ref": "SourceRange", "optional": true, "description": "The entire property range in the enclosing style declaration (if available)." } + ], + "description": "CSS property declaration data." + }, + { + "id": "CSSMedia", + "type": "object", + "properties": [ + { "name": "text", "type": "string", "description": "Media query text." }, + { "name": "source", "type": "string", "enum": ["mediaRule", "importRule", "linkedSheet", "inlineSheet"], "description": "Source of the media query: \"mediaRule\" if specified by a @media rule, \"importRule\" if specified by an @import rule, \"linkedSheet\" if specified by a \"media\" attribute in a linked stylesheet's LINK tag, \"inlineSheet\" if specified by a \"media\" attribute in an inline stylesheet's STYLE tag." }, + { "name": "sourceURL", "type": "string", "optional": true, "description": "URL of the document containing the media query description." }, + { "name": "range", "$ref": "SourceRange", "optional": true, "description": "The associated rule (@media or @import) header range in the enclosing stylesheet (if available)." }, + { "name": "styleSheetId", "$ref": "StyleSheetId", "optional": true, "description": "Identifier of the stylesheet containing this object (if exists)." }, + { "name": "mediaList", "type": "array", "items": { "$ref": "MediaQuery" }, "optional": true, "hidden": true, "description": "Array of media queries." } + ], + "description": "CSS media rule descriptor." + }, + { + "id": "MediaQuery", + "type": "object", + "properties": [ + { "name": "expressions", "type": "array", "items": { "$ref": "MediaQueryExpression" }, "description": "Array of media query expressions." }, + { "name": "active", "type": "boolean", "description": "Whether the media query condition is satisfied." } + ], + "description": "Media query descriptor.", + "hidden": true + }, + { + "id": "MediaQueryExpression", + "type": "object", + "properties": [ + { "name": "value", "type": "number", "description": "Media query expression value."}, + { "name": "unit", "type": "string", "description": "Media query expression units."}, + { "name": "feature", "type": "string", "description": "Media query expression feature."}, + { "name": "valueRange", "$ref": "SourceRange", "optional": true, "description": "The associated range of the value text in the enclosing stylesheet (if available)." }, + { "name": "computedLength", "type": "number", "optional": true, "description": "Computed length of media query expression (if applicable)."} + ], + "description": "Media query expression descriptor.", + "hidden": true + }, + { + "id": "PlatformFontUsage", + "type": "object", + "properties": [ + { "name": "familyName", "type": "string", "description": "Font's family name reported by platform."}, + { "name": "isCustomFont", "type": "boolean", "description": "Indicates if the font was downloaded or resolved locally."}, + { "name": "glyphCount", "type": "number", "description": "Amount of glyphs that were rendered with this font."} + ], + "description": "Information about amount of glyphs that were rendered with given font.", + "hidden": true + }, + { + "id": "CSSKeyframesRule", + "type": "object", + "properties": [ + { "name": "animationName", "$ref": "Value", "description": "Animation name." }, + { "name": "keyframes", "type": "array", "items": { "$ref": "CSSKeyframeRule" }, "description": "List of keyframes." } + ], + "description": "CSS keyframes rule representation." + }, + { + "id": "CSSKeyframeRule", + "type": "object", + "properties": [ + { "name": "styleSheetId", "$ref": "StyleSheetId", "optional": true, "description": "The css style sheet identifier (absent for user agent stylesheet and user-specified stylesheet rules) this rule came from." }, + { "name": "origin", "$ref": "StyleSheetOrigin", "description": "Parent stylesheet's origin."}, + { "name": "keyText", "$ref": "Value", "description": "Associated key text." }, + { "name": "style", "$ref": "CSSStyle", "description": "Associated style declaration." } + ], + "description": "CSS keyframe rule representation." + }, + { + "id": "StyleDeclarationEdit", + "type": "object", + "properties": [ + { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "The css style sheet identifier." }, + { "name": "range", "$ref": "SourceRange", "description": "The range of the style text in the enclosing stylesheet." }, + { "name": "text", "type": "string", "description": "New style text."} + ], + "description": "A descriptor of operation to mutate style declaration text." + } + ], + "commands": [ + { + "name": "enable", + "async": true, + "description": "Enables the CSS agent for the given page. Clients should not assume that the CSS agent has been enabled until the result of this command is received." + }, + { + "name": "disable", + "description": "Disables the CSS agent for the given page." + }, + { + "name": "getMatchedStylesForNode", + "parameters": [ + { "name": "nodeId", "$ref": "DOM.NodeId" } + ], + "returns": [ + { "name": "inlineStyle", "$ref": "CSSStyle", "optional": true, "description": "Inline style for the specified DOM node." }, + { "name": "attributesStyle", "$ref": "CSSStyle", "optional": true, "description": "Attribute-defined element style (e.g. resulting from \"width=20 height=100%\")."}, + { "name": "matchedCSSRules", "type": "array", "items": { "$ref": "RuleMatch" }, "optional": true, "description": "CSS rules matching this node, from all applicable stylesheets." }, + { "name": "pseudoElements", "type": "array", "items": { "$ref": "PseudoElementMatches" }, "optional": true, "description": "Pseudo style matches for this node." }, + { "name": "inherited", "type": "array", "items": { "$ref": "InheritedStyleEntry" }, "optional": true, "description": "A chain of inherited styles (from the immediate node parent up to the DOM tree root)." }, + { "name": "cssKeyframesRules", "type": "array", "items": { "$ref": "CSSKeyframesRule" }, "optional": true, "description": "A list of CSS keyframed animations matching this node." } + ], + "description": "Returns requested styles for a DOM node identified by <code>nodeId</code>." + }, + { + "name": "getInlineStylesForNode", + "parameters": [ + { "name": "nodeId", "$ref": "DOM.NodeId" } + ], + "returns": [ + { "name": "inlineStyle", "$ref": "CSSStyle", "optional": true, "description": "Inline style for the specified DOM node." }, + { "name": "attributesStyle", "$ref": "CSSStyle", "optional": true, "description": "Attribute-defined element style (e.g. resulting from \"width=20 height=100%\")."} + ], + "description": "Returns the styles defined inline (explicitly in the \"style\" attribute and implicitly, using DOM attributes) for a DOM node identified by <code>nodeId</code>." + }, + { + "name": "getComputedStyleForNode", + "parameters": [ + { "name": "nodeId", "$ref": "DOM.NodeId" } + ], + "returns": [ + { "name": "computedStyle", "type": "array", "items": { "$ref": "CSSComputedStyleProperty" }, "description": "Computed style for the specified DOM node." } + ], + "description": "Returns the computed style for a DOM node identified by <code>nodeId</code>." + }, + { + "name": "getPlatformFontsForNode", + "parameters": [ + { "name": "nodeId", "$ref": "DOM.NodeId" } + ], + "returns": [ + { "name": "fonts", "type": "array", "items": { "$ref": "PlatformFontUsage" }, "description": "Usage statistics for every employed platform font." } + ], + "description": "Requests information about platform fonts which we used to render child TextNodes in the given node.", + "hidden": true + }, + { + "name": "getStyleSheetText", + "parameters": [ + { "name": "styleSheetId", "$ref": "StyleSheetId" } + ], + "returns": [ + { "name": "text", "type": "string", "description": "The stylesheet text." } + ], + "description": "Returns the current textual content and the URL for a stylesheet." + }, + { + "name": "setStyleSheetText", + "parameters": [ + { "name": "styleSheetId", "$ref": "StyleSheetId" }, + { "name": "text", "type": "string" } + ], + "returns": [ + { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." } + ], + "description": "Sets the new stylesheet text." + }, + { + "name": "setRuleSelector", + "parameters": [ + { "name": "styleSheetId", "$ref": "StyleSheetId" }, + { "name": "range", "$ref": "SourceRange" }, + { "name": "selector", "type": "string" } + ], + "returns": [ + { "name": "selectorList", "$ref": "SelectorList", "description": "The resulting selector list after modification." } + ], + "description": "Modifies the rule selector." + }, + { + "name": "setKeyframeKey", + "parameters": [ + { "name": "styleSheetId", "$ref": "StyleSheetId" }, + { "name": "range", "$ref": "SourceRange" }, + { "name": "keyText", "type": "string" } + ], + "returns": [ + { "name": "keyText", "$ref": "Value", "description": "The resulting key text after modification." } + ], + "description": "Modifies the keyframe rule key text." + }, + { + "name": "setStyleTexts", + "parameters": [ + { "name": "edits", "type": "array", "items": { "$ref": "StyleDeclarationEdit" }} + ], + "returns": [ + { "name": "styles", "type": "array", "items": { "$ref": "CSSStyle" }, "description": "The resulting styles after modification." } + ], + "description": "Applies specified style edits one after another in the given order." + }, + { + "name": "setMediaText", + "parameters": [ + { "name": "styleSheetId", "$ref": "StyleSheetId" }, + { "name": "range", "$ref": "SourceRange" }, + { "name": "text", "type": "string" } + ], + "returns": [ + { "name": "media", "$ref": "CSSMedia", "description": "The resulting CSS media rule after modification." } + ], + "description": "Modifies the rule selector." + }, + { + "name": "createStyleSheet", + "parameters": [ + { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame where \"via-inspector\" stylesheet should be created."} + ], + "returns": [ + { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "Identifier of the created \"via-inspector\" stylesheet." } + ], + "description": "Creates a new special \"via-inspector\" stylesheet in the frame with given <code>frameId</code>." + }, + { + "name": "addRule", + "parameters": [ + { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "The css style sheet identifier where a new rule should be inserted." }, + { "name": "ruleText", "type": "string", "description": "The text of a new rule." }, + { "name": "location", "$ref": "SourceRange", "description": "Text position of a new rule in the target style sheet." } + ], + "returns": [ + { "name": "rule", "$ref": "CSSRule", "description": "The newly created rule." } + ], + "description": "Inserts a new rule with the given <code>ruleText</code> in a stylesheet with given <code>styleSheetId</code>, at the position specified by <code>location</code>." + }, + { + "name": "forcePseudoState", + "parameters": [ + { "name": "nodeId", "$ref": "DOM.NodeId", "description": "The element id for which to force the pseudo state." }, + { "name": "forcedPseudoClasses", "type": "array", "items": { "type": "string", "enum": ["active", "focus", "hover", "visited"] }, "description": "Element pseudo classes to force when computing the element's style." } + ], + "description": "Ensures that the given node will have specified pseudo-classes whenever its style is computed by the browser." + }, + { + "name": "getMediaQueries", + "returns": [ + { "name": "medias", "type": "array", "items": { "$ref": "CSSMedia" } } + ], + "description": "Returns all media queries parsed by the rendering engine.", + "hidden": true + }, + { + "name": "setEffectivePropertyValueForNode", + "parameters": [ + { "name": "nodeId", "$ref": "DOM.NodeId", "description": "The element id for which to set property." }, + { "name": "propertyName", "type": "string"}, + { "name": "value", "type": "string"} + ], + "description": "Find a rule with the given active property for the given node and set the new value for this property", + "hidden": true + }, + { + "name": "getBackgroundColors", + "parameters": [ + { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Id of the node to get background colors for." } + ], + "returns": [ + { "name": "backgroundColors", "type": "array", "items": { "type": "string" }, "description": "The range of background colors behind this element, if it contains any visible text. If no visible text is present, this will be undefined. In the case of a flat background color, this will consist of simply that color. In the case of a gradient, this will consist of each of the color stops. For anything more complicated, this will be an empty array. Images will be ignored (as if the image had failed to load).", "optional": true } + ], + "hidden": true + } + ], + "events": [ + { + "name": "mediaQueryResultChanged", + "description": "Fires whenever a MediaQuery result changes (for example, after a browser window has been resized.) The current implementation considers only viewport-dependent media features." + }, + { + "name": "styleSheetChanged", + "parameters": [ + { "name": "styleSheetId", "$ref": "StyleSheetId" } + ], + "description": "Fired whenever a stylesheet is changed as a result of the client operation." + }, + { + "name": "styleSheetAdded", + "parameters": [ + { "name": "header", "$ref": "CSSStyleSheetHeader", "description": "Added stylesheet metainfo." } + ], + "description": "Fired whenever an active document stylesheet is added." + }, + { + "name": "styleSheetRemoved", + "parameters": [ + { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "Identifier of the removed stylesheet." } + ], + "description": "Fired whenever an active document stylesheet is removed." + }, + { + "name": "layoutEditorChange", + "parameters": [ + { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "Identifier of the stylesheet where the modification occurred." }, + { "name": "changeRange", "$ref": "SourceRange", "description": "Range where the modification occurred." } + ] + } + ] + }, + { + "domain": "IO", + "description": "Input/Output operations for streams produced by DevTools.", + "hidden": true, + "types": [ + { + "id": "StreamHandle", + "type": "string" + } + ], + "commands": [ + { + "name": "read", + "description": "Read a chunk of the stream", + "async": true, + "parameters": [ + { "name": "handle", "$ref": "StreamHandle", "description": "Handle of the stream to read." }, + { "name": "offset", "type": "integer", "optional": true, "description": "Seek to the specified offset before reading (if not specificed, proceed with offset following the last read)." }, + { "name": "size", "type": "integer", "optional": true, "description": "Maximum number of bytes to read (left upon the agent discretion if not specified)." } + ], + "returns": [ + { "name": "data", "type": "string", "description": "Data that were read." }, + { "name": "eof", "type": "boolean", "description": "Set if the end-of-file condition occured while reading." } + ], + "handlers": ["browser"] + }, + { + "name": "close", + "description": "Close the stream, discard any temporary backing storage.", + "parameters": [ + { "name": "handle", "$ref": "StreamHandle", "description": "Handle of the stream to close." } + ], + "handlers": ["browser"] + } + ] + }, + { + "domain": "Debugger", + "description": "Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing breakpoints, stepping through execution, exploring stack traces, etc.", + "types": [ + { + "id": "BreakpointId", + "type": "string", + "description": "Breakpoint identifier." + }, + { + "id": "CallFrameId", + "type": "string", + "description": "Call frame identifier." + }, + { + "id": "Location", + "type": "object", + "properties": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Script identifier as reported in the <code>Debugger.scriptParsed</code>." }, + { "name": "lineNumber", "type": "integer", "description": "Line number in the script (0-based)." }, + { "name": "columnNumber", "type": "integer", "optional": true, "description": "Column number in the script (0-based)." } + ], + "description": "Location in the source code." + }, + { + "id": "ScriptPosition", + "hidden": true, + "type": "object", + "properties": [ + { "name": "line", "type": "integer" }, + { "name": "column", "type": "integer" } + ], + "description": "Location in the source code." + }, + { + "id": "FunctionDetails", + "hidden": true, + "type": "object", + "properties": [ + { "name": "location", "$ref": "Location", "optional": true, "description": "Location of the function, none for native functions." }, + { "name": "functionName", "type": "string", "description": "Name of the function." }, + { "name": "isGenerator", "type": "boolean", "description": "Whether this is a generator function." }, + { "name": "scopeChain", "type": "array", "optional": true, "items": { "$ref": "Scope" }, "description": "Scope chain for this closure." } + ], + "description": "Information about the function." + }, + { + "id": "GeneratorObjectDetails", + "hidden": true, + "type": "object", + "properties": [ + { "name": "function", "$ref": "Runtime.RemoteObject", "description": "Generator function." }, + { "name": "functionName", "type": "string", "description": "Name of the generator function." }, + { "name": "status", "type": "string", "enum": ["running", "suspended", "closed"], "description": "Current generator object status." }, + { "name": "location", "$ref": "Location", "optional": true, "description": "If suspended, location where generator function was suspended (e.g. location of the last 'yield'). Otherwise, location of the generator function." } + ], + "description": "Information about the generator object." + }, + { + "id": "CollectionEntry", + "hidden": true, + "type": "object", + "properties": [ + { "name": "key", "$ref": "Runtime.RemoteObject", "optional": true, "description": "Entry key of a map-like collection, otherwise not provided." }, + { "name": "value", "$ref": "Runtime.RemoteObject", "description": "Entry value." } + ], + "description": "Collection entry." + }, + { + "id": "CallFrame", + "type": "object", + "properties": [ + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier. This identifier is only valid while the virtual machine is paused." }, + { "name": "functionName", "type": "string", "description": "Name of the JavaScript function called on this call frame." }, + { "name": "functionLocation", "$ref": "Location", "optional": true, "hidden": true, "description": "Location in the source code." }, + { "name": "location", "$ref": "Location", "description": "Location in the source code." }, + { "name": "scopeChain", "type": "array", "items": { "$ref": "Scope" }, "description": "Scope chain for this call frame." }, + { "name": "this", "$ref": "Runtime.RemoteObject", "description": "<code>this</code> object for this call frame." }, + { "name": "returnValue", "$ref": "Runtime.RemoteObject", "optional": true, "hidden": true, "description": "The value being returned, if the function is at return point." } + ], + "description": "JavaScript call frame. Array of call frames form the call stack." + }, + { + "id": "Scope", + "type": "object", + "properties": [ + { "name": "type", "type": "string", "enum": ["global", "local", "with", "closure", "catch", "block", "script"], "description": "Scope type." }, + { "name": "object", "$ref": "Runtime.RemoteObject", "description": "Object representing the scope. For <code>global</code> and <code>with</code> scopes it represents the actual object; for the rest of the scopes, it is artificial transient object enumerating scope variables as its properties." }, + { "name": "name", "type": "string", "optional": true, "hidden": true }, + { "name": "startLocation", "$ref": "Location", "optional": true, "hidden": true, "description": "Location in the source code where scope starts" }, + { "name": "endLocation", "$ref": "Location", "optional": true, "hidden": true, "description": "Location in the source code where scope ends" } + ], + "description": "Scope description." + }, + { + "id": "SetScriptSourceError", + "type": "object", + "properties": [ + { "name": "message", "type": "string", "description": "Compiler error message" }, + { "name": "lineNumber", "type": "integer", "description": "Compile error line number (1-based)" }, + { "name": "columnNumber", "type": "integer", "description": "Compile error column number (1-based)" } + ], + "description": "Error data for setScriptSource command. Contains uncompilable script source error.", + "hidden": true + }, + { + "id": "SearchMatch", + "type": "object", + "description": "Search match for resource.", + "properties": [ + { "name": "lineNumber", "type": "number", "description": "Line number in resource content." }, + { "name": "lineContent", "type": "string", "description": "Line with match content." } + ], + "hidden": true + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables debugger for the given page. Clients should not assume that the debugging has been enabled until the result for this command is received." + }, + { + "name": "disable", + "description": "Disables debugger for given page." + }, + { + "name": "setBreakpointsActive", + "parameters": [ + { "name": "active", "type": "boolean", "description": "New value for breakpoints active state." } + ], + "description": "Activates / deactivates all breakpoints on the page." + }, + { + "name": "setSkipAllPauses", + "hidden": true, + "parameters": [ + { "name": "skipped", "type": "boolean", "description": "New value for skip pauses state." } + ], + "description": "Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc)." + }, + { + "name": "setBreakpointByUrl", + "parameters": [ + { "name": "lineNumber", "type": "integer", "description": "Line number to set breakpoint at." }, + { "name": "url", "type": "string", "optional": true, "description": "URL of the resources to set breakpoint on." }, + { "name": "urlRegex", "type": "string", "optional": true, "description": "Regex pattern for the URLs of the resources to set breakpoints on. Either <code>url</code> or <code>urlRegex</code> must be specified." }, + { "name": "columnNumber", "type": "integer", "optional": true, "description": "Offset in the line to set breakpoint at." }, + { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." } + ], + "returns": [ + { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, + { "name": "locations", "type": "array", "items": { "$ref": "Location" }, "description": "List of the locations this breakpoint resolved into upon addition." } + ], + "description": "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this command is issued, all existing parsed scripts will have breakpoints resolved and returned in <code>locations</code> property. Further matching script parsing will result in subsequent <code>breakpointResolved</code> events issued. This logical breakpoint will survive page reloads." + }, + { + "name": "setBreakpoint", + "parameters": [ + { "name": "location", "$ref": "Location", "description": "Location to set breakpoint in." }, + { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." } + ], + "returns": [ + { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, + { "name": "actualLocation", "$ref": "Location", "description": "Location this breakpoint resolved into." } + ], + "description": "Sets JavaScript breakpoint at a given location." + }, + { + "name": "removeBreakpoint", + "parameters": [ + { "name": "breakpointId", "$ref": "BreakpointId" } + ], + "description": "Removes JavaScript breakpoint." + }, + { + "name": "continueToLocation", + "parameters": [ + { "name": "location", "$ref": "Location", "description": "Location to continue to." }, + { "name": "interstatementLocation", "type": "boolean", "optional": true, "hidden": true, "description": "Allows breakpoints at the intemediate positions inside statements." } + ], + "description": "Continues execution until specific location is reached." + }, + { + "name": "stepOver", + "description": "Steps over the statement." + }, + { + "name": "stepInto", + "description": "Steps into the function call." + }, + { + "name": "stepOut", + "description": "Steps out of the function call." + }, + { + "name": "pause", + "description": "Stops on the next JavaScript statement." + }, + { + "name": "resume", + "description": "Resumes JavaScript execution." + }, + { + "name": "searchInContent", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to search in." }, + { "name": "query", "type": "string", "description": "String to search for." }, + { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." }, + { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." } + ], + "returns": [ + { "name": "result", "type": "array", "items": { "$ref": "SearchMatch" }, "description": "List of search matches." } + ], + "description": "Searches for given string in script content." + }, + { + "name": "canSetScriptSource", + "returns": [ + { "name": "result", "type": "boolean", "description": "True if <code>setScriptSource</code> is supported." } + ], + "description": "Always returns true." + }, + { + "name": "setScriptSource", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to edit." }, + { "name": "scriptSource", "type": "string", "description": "New content of the script." }, + { "name": "preview", "type": "boolean", "optional": true, "description": " If true the change will not actually be applied. Preview mode may be used to get result description without actually modifying the code.", "hidden": true } + ], + "returns": [ + { "name": "callFrames", "type": "array", "optional": true, "items": { "$ref": "CallFrame" }, "description": "New stack trace in case editing has happened while VM was stopped." }, + { "name": "stackChanged", "type": "boolean", "optional": true, "description": "Whether current call stack was modified after applying the changes.", "hidden": true }, + { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any.", "hidden": true }, + { "name": "compileError", "optional": true, "$ref": "SetScriptSourceError", "description": "Error data if any." } + ], + "description": "Edits JavaScript source live." + }, + { + "name": "restartFrame", + "parameters": [ + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." } + ], + "returns": [ + { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "New stack trace." }, + { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." } + ], + "hidden": true, + "description": "Restarts particular call frame from the beginning." + }, + { + "name": "getScriptSource", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to get source for." } + ], + "returns": [ + { "name": "scriptSource", "type": "string", "description": "Script source." } + ], + "description": "Returns source for the script with given id." + }, + { + "name": "getFunctionDetails", + "hidden": true, + "parameters": [ + { "name": "functionId", "$ref": "Runtime.RemoteObjectId", "description": "Id of the function to get details for." } + ], + "returns": [ + { "name": "details", "$ref": "FunctionDetails", "description": "Information about the function." } + ], + "description": "Returns detailed information on given function." + }, + { + "name": "getGeneratorObjectDetails", + "hidden": true, + "parameters": [ + { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Id of the generator object to get details for." } + ], + "returns": [ + { "name": "details", "$ref": "GeneratorObjectDetails", "description": "Information about the generator object." } + ], + "description": "Returns detailed information on given generator object." + }, + { + "name": "getCollectionEntries", + "hidden": true, + "parameters": [ + { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Id of the collection to get entries for." } + ], + "returns": [ + { "name": "entries", "type": "array", "items": { "$ref": "CollectionEntry" }, "description": "Array of collection entries." } + ], + "description": "Returns entries of given collection." + }, + { + "name": "setPauseOnExceptions", + "parameters": [ + { "name": "state", "type": "string", "enum": ["none", "uncaught", "all"], "description": "Pause on exceptions mode." } + ], + "description": "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions or no exceptions. Initial pause on exceptions state is <code>none</code>." + }, + { + "name": "evaluateOnCallFrame", + "parameters": [ + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." }, + { "name": "expression", "type": "string", "description": "Expression to evaluate." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "String object group name to put result into (allows rapid releasing resulting object handles using <code>releaseObjectGroup</code>)." }, + { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false.", "hidden": true }, + { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether evaluation should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true }, + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." } + ], + "returns": [ + { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." }, + { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }, + { "name": "exceptionDetails", "$ref": "Runtime.ExceptionDetails", "optional": true, "hidden": true, "description": "Exception details."} + ], + "description": "Evaluates expression on a given call frame." + }, + { + "name": "setVariableValue", + "parameters": [ + { "name": "scopeNumber", "type": "integer", "description": "0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch' scope types are allowed. Other scopes could be manipulated manually." }, + { "name": "variableName", "type": "string", "description": "Variable name." }, + { "name": "newValue", "$ref": "Runtime.CallArgument", "description": "New variable value." }, + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Id of callframe that holds variable." } + ], + "hidden": true, + "description": "Changes value of variable in a callframe. Object-based scopes are not supported and must be mutated manually." + }, + { + "name": "getBacktrace", + "returns": [ + { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." }, + { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." } + ], + "hidden": true, + "description": "Returns call stack including variables changed since VM was paused. VM must be paused." + }, + { + "name": "setAsyncCallStackDepth", + "parameters": [ + { "name": "maxDepth", "type": "integer", "description": "Maximum depth of async call stacks. Setting to <code>0</code> will effectively disable collecting async call stacks (default)." } + ], + "hidden": true, + "description": "Enables or disables async call stacks tracking." + }, + { + "name": "setBlackboxPatterns", + "parameters": [ + { "name": "patterns", "type": "array", "items": { "type": "string" }, "description": "Array of regexps that will be used to check script url for blackbox state." } + ], + "hidden": true, + "description": "Replace previous blackbox patterns with passed ones. Forces backend to skip stepping/pausing in scripts with url matching one of the patterns. VM will try to leave blackboxed script by performing 'step in' several times, finally resorting to 'step out' if unsuccessful." + }, + { + "name": "setBlackboxedRanges", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script." }, + { "name": "positions", "type": "array", "items": { "$ref": "ScriptPosition" } } + ], + "hidden": true, + "description": "Makes backend skip steps in the script in blackboxed ranges. VM will try leave blacklisted scripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful. Positions array contains positions where blackbox state is changed. First interval isn't blackboxed. Array should be sorted." + } + ], + "events": [ + { + "name": "scriptParsed", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." }, + { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, + { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, + { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, + { "name": "endLine", "type": "integer", "description": "Last line of the script." }, + { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, + { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context.", "hidden": true }, + { "name": "hash", "type": "string", "hidden": true, "description": "Content hash of the script."}, + { "name": "isContentScript", "type": "boolean", "optional": true, "description": "Determines whether this script is a user extension script." }, + { "name": "isInternalScript", "type": "boolean", "optional": true, "description": "Determines whether this script is an internal script.", "hidden": true }, + { "name": "isLiveEdit", "type": "boolean", "optional": true, "description": "True, if this script is generated as a result of the live edit operation.", "hidden": true }, + { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, + { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "hidden": true }, + { "name": "deprecatedCommentWasUsed", "type": "boolean", "optional": true, "hidden": true, "description": "True, if '//@ sourceURL' or '//@ sourceMappingURL' was used."} + ], + "description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger." + }, + { + "name": "scriptFailedToParse", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." }, + { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, + { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, + { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, + { "name": "endLine", "type": "integer", "description": "Last line of the script." }, + { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, + { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context.", "hidden": true }, + { "name": "hash", "type": "string", "hidden": true, "description": "Content hash of the script."}, + { "name": "isContentScript", "type": "boolean", "optional": true, "description": "Determines whether this script is a user extension script." }, + { "name": "isInternalScript", "type": "boolean", "optional": true, "description": "Determines whether this script is an internal script.", "hidden": true }, + { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, + { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "hidden": true }, + { "name": "deprecatedCommentWasUsed", "type": "boolean", "optional": true, "hidden": true, "description": "True, if '//@ sourceURL' or '//@ sourceMappingURL' was used."} + ], + "description": "Fired when virtual machine fails to parse the script." + }, + { + "name": "breakpointResolved", + "parameters": [ + { "name": "breakpointId", "$ref": "BreakpointId", "description": "Breakpoint unique identifier." }, + { "name": "location", "$ref": "Location", "description": "Actual breakpoint location." } + ], + "description": "Fired when breakpoint is resolved to an actual script and location." + }, + { + "name": "paused", + "parameters": [ + { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." }, + { "name": "reason", "type": "string", "enum": [ "XHR", "DOM", "EventListener", "exception", "assert", "CSPViolation", "debugCommand", "promiseRejection", "other" ], "description": "Pause reason." }, + { "name": "data", "type": "object", "optional": true, "description": "Object containing break-specific auxiliary properties." }, + { "name": "hitBreakpoints", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Hit breakpoints IDs", "hidden": true }, + { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any.", "hidden": true } + ], + "description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria." + }, + { + "name": "resumed", + "description": "Fired when the virtual machine resumed execution." + } + ] + }, + { + "domain": "DOMDebugger", + "description": "DOM debugging allows setting breakpoints on particular DOM operations and events. JavaScript execution will stop on these operations as if there was a regular breakpoint set.", + "types": [ + { + "id": "DOMBreakpointType", + "type": "string", + "enum": ["subtree-modified", "attribute-modified", "node-removed"], + "description": "DOM breakpoint type." + }, + { + "id": "EventListener", + "type": "object", + "description": "Object event listener.", + "properties": [ + { "name": "type", "type": "string", "description": "<code>EventListener</code>'s type." }, + { "name": "useCapture", "type": "boolean", "description": "<code>EventListener</code>'s useCapture." }, + { "name": "passive", "type": "boolean", "description": "<code>EventListener</code>'s passive flag." }, + { "name": "location", "$ref": "Debugger.Location", "description": "Handler code location." }, + { "name": "handler", "$ref": "Runtime.RemoteObject", "optional": true, "description": "Event handler function value." }, + { "name": "originalHandler", "$ref": "Runtime.RemoteObject", "optional": true, "description": "Event original handler function value." } + ], + "hidden": true + } + ], + "commands": [ + { + "name": "setDOMBreakpoint", + "parameters": [ + { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Identifier of the node to set breakpoint on." }, + { "name": "type", "$ref": "DOMBreakpointType", "description": "Type of the operation to stop upon." } + ], + "description": "Sets breakpoint on particular operation with DOM." + }, + { + "name": "removeDOMBreakpoint", + "parameters": [ + { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Identifier of the node to remove breakpoint from." }, + { "name": "type", "$ref": "DOMBreakpointType", "description": "Type of the breakpoint to remove." } + ], + "description": "Removes DOM breakpoint that was set using <code>setDOMBreakpoint</code>." + }, + { + "name": "setEventListenerBreakpoint", + "parameters": [ + { "name": "eventName", "type": "string", "description": "DOM Event name to stop on (any DOM event will do)." }, + { "name": "targetName", "type": "string", "optional": true, "description": "EventTarget interface name to stop on. If equal to <code>\"*\"</code> or not provided, will stop on any EventTarget.", "hidden": true } + ], + "description": "Sets breakpoint on particular DOM event." + }, + { + "name": "removeEventListenerBreakpoint", + "parameters": [ + { "name": "eventName", "type": "string", "description": "Event name." }, + { "name": "targetName", "type": "string", "optional": true, "description": "EventTarget interface name.", "hidden": true } + ], + "description": "Removes breakpoint on particular DOM event." + }, + { + "name": "setInstrumentationBreakpoint", + "parameters": [ + { "name": "eventName", "type": "string", "description": "Instrumentation name to stop on." } + ], + "description": "Sets breakpoint on particular native event.", + "hidden": true + }, + { + "name": "removeInstrumentationBreakpoint", + "parameters": [ + { "name": "eventName", "type": "string", "description": "Instrumentation name to stop on." } + ], + "description": "Removes breakpoint on particular native event.", + "hidden": true + }, + { + "name": "setXHRBreakpoint", + "parameters": [ + { "name": "url", "type": "string", "description": "Resource URL substring. All XHRs having this substring in the URL will get stopped upon." } + ], + "description": "Sets breakpoint on XMLHttpRequest." + }, + { + "name": "removeXHRBreakpoint", + "parameters": [ + { "name": "url", "type": "string", "description": "Resource URL substring." } + ], + "description": "Removes breakpoint from XMLHttpRequest." + }, + { + "name": "getEventListeners", + "hidden": true, + "parameters": [ + { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Identifier of the object to return listeners for." } + ], + "returns": [ + { "name": "listeners", "type": "array", "items": { "$ref": "EventListener" }, "description": "Array of relevant listeners." } + ], + "description": "Returns event listeners of the given object." + } + ] + }, + { + "domain": "Profiler", + "hidden": true, + "types": [ + { + "id": "CPUProfileNode", + "type": "object", + "description": "CPU Profile node. Holds callsite information, execution statistics and child nodes.", + "properties": [ + { "name": "functionName", "type": "string", "description": "Function name." }, + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Script identifier." }, + { "name": "url", "type": "string", "description": "URL." }, + { "name": "lineNumber", "type": "integer", "description": "1-based line number of the function start position." }, + { "name": "columnNumber", "type": "integer", "description": "1-based column number of the function start position." }, + { "name": "hitCount", "type": "integer", "description": "Number of samples where this node was on top of the call stack." }, + { "name": "callUID", "type": "number", "description": "Call UID." }, + { "name": "children", "type": "array", "items": { "$ref": "CPUProfileNode" }, "description": "Child nodes." }, + { "name": "deoptReason", "type": "string", "description": "The reason of being not optimized. The function may be deoptimized or marked as don't optimize."}, + { "name": "id", "type": "integer", "description": "Unique id of the node." }, + { "name": "positionTicks", "type": "array", "items": { "$ref": "PositionTickInfo" }, "description": "An array of source position ticks." } + ] + }, + { + "id": "CPUProfile", + "type": "object", + "description": "Profile.", + "properties": [ + { "name": "head", "$ref": "CPUProfileNode" }, + { "name": "startTime", "type": "number", "description": "Profiling start time in seconds." }, + { "name": "endTime", "type": "number", "description": "Profiling end time in seconds." }, + { "name": "samples", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Ids of samples top nodes." }, + { "name": "timestamps", "optional": true, "type": "array", "items": { "type": "number" }, "description": "Timestamps of the samples in microseconds." } + ] + }, + { + "id": "PositionTickInfo", + "type": "object", + "description": "Specifies a number of samples attributed to a certain source position.", + "properties": [ + { "name": "line", "type": "integer", "description": "Source line number (1-based)." }, + { "name": "ticks", "type": "integer", "description": "Number of samples attributed to the source line." } + ] + } + ], + "commands": [ + { + "name": "enable" + }, + { + "name": "disable" + }, + { + "name": "setSamplingInterval", + "parameters": [ + { "name": "interval", "type": "integer", "description": "New sampling interval in microseconds." } + ], + "description": "Changes CPU profiler sampling interval. Must be called before CPU profiles recording started." + }, + { + "name": "start" + }, + { + "name": "stop", + "returns": [ + { "name": "profile", "$ref": "CPUProfile", "description": "Recorded profile." } + ] + } + ], + "events": [ + { + "name": "consoleProfileStarted", + "parameters": [ + { "name": "id", "type": "string" }, + { "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profile()." }, + { "name": "title", "type": "string", "optional": true, "description": "Profile title passed as argument to console.profile()." } + + ], + "description": "Sent when new profile recodring is started using console.profile() call." + }, + { + "name": "consoleProfileFinished", + "parameters": [ + { "name": "id", "type": "string" }, + { "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profileEnd()." }, + { "name": "profile", "$ref": "CPUProfile" }, + { "name": "title", "type": "string", "optional": true, "description": "Profile title passed as argunet to console.profile()." } + ] + } + ] + }, + { + "domain": "HeapProfiler", + "hidden": true, + "types": [ + { + "id": "HeapSnapshotObjectId", + "type": "string", + "description": "Heap snapshot object id." + }, + { + "id": "SamplingHeapProfileNode", + "type": "object", + "description": "Sampling Heap Profile node. Holds callsite information, allocation statistics and child nodes.", + "properties": [ + { "name": "functionName", "type": "string", "description": "Function name." }, + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Script identifier." }, + { "name": "url", "type": "string", "description": "URL." }, + { "name": "lineNumber", "type": "integer", "description": "1-based line number of the function start position." }, + { "name": "columnNumber", "type": "integer", "description": "1-based column number of the function start position." }, + { "name": "selfSize", "type": "number", "description": "Allocations size in bytes for the node excluding children." }, + { "name": "children", "type": "array", "items": { "$ref": "SamplingHeapProfileNode" }, "description": "Child nodes." } + ] + }, + { + "id": "SamplingHeapProfile", + "type": "object", + "description": "Profile.", + "properties": [ + { "name": "head", "$ref": "SamplingHeapProfileNode" } + ] + } + ], + "commands": [ + { + "name": "enable" + }, + { + "name": "disable" + }, + { + "name": "startTrackingHeapObjects", + "parameters": [ + { "name": "trackAllocations", "type": "boolean", "optional": true } + ] + }, + { + "name": "stopTrackingHeapObjects", + "parameters": [ + { "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken when the tracking is stopped." } + ] + + }, + { + "name": "takeHeapSnapshot", + "parameters": [ + { "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken." } + ] + }, + { + "name": "collectGarbage" + }, + { + "name": "getObjectByHeapObjectId", + "parameters": [ + { "name": "objectId", "$ref": "HeapSnapshotObjectId" }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." } + ], + "returns": [ + { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Evaluation result." } + ] + }, + { + "name": "addInspectedHeapObject", + "parameters": [ + { "name": "heapObjectId", "$ref": "HeapSnapshotObjectId", "description": "Heap snapshot object id to be accessible by means of $x command line API." } + ], + "description": "Enables console to refer to the node with given id via $x (see Command Line API for more details $x functions)." + }, + { + "name": "getHeapObjectId", + "parameters": [ + { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Identifier of the object to get heap object id for." } + ], + "returns": [ + { "name": "heapSnapshotObjectId", "$ref": "HeapSnapshotObjectId", "description": "Id of the heap snapshot object corresponding to the passed remote object id." } + ] + }, + { + "name": "startSampling", + "parameters": [ + { "name": "samplingInterval", "type": "number", "optional": true, "description": "Average sample interval in bytes. Poisson distribution is used for the intervals. The default value is 32768 bytes." } + ] + }, + { + "name": "stopSampling", + "returns": [ + { "name": "profile", "$ref": "SamplingHeapProfile", "description": "Recorded sampling heap profile." } + ] + } + ], + "events": [ + { + "name": "addHeapSnapshotChunk", + "parameters": [ + { "name": "chunk", "type": "string" } + ] + }, + { + "name": "resetProfiles" + }, + { + "name": "reportHeapSnapshotProgress", + "parameters": [ + { "name": "done", "type": "integer" }, + { "name": "total", "type": "integer" }, + { "name": "finished", "type": "boolean", "optional": true } + ] + }, + { + "name": "lastSeenObjectId", + "description": "If heap objects tracking has been started then backend regulary sends a current value for last seen object id and corresponding timestamp. If the were changes in the heap since last event then one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event.", + "parameters": [ + { "name": "lastSeenObjectId", "type": "integer" }, + { "name": "timestamp", "type": "number" } + ] + }, + { + "name": "heapStatsUpdate", + "description": "If heap objects tracking has been started then backend may send update for one or more fragments", + "parameters": [ + { "name": "statsUpdate", "type": "array", "items": { "type": "integer" }, "description": "An array of triplets. Each triplet describes a fragment. The first integer is the fragment index, the second integer is a total count of objects for the fragment, the third integer is a total size of the objects for the fragment."} + ] + } + ] + }, + { + "domain": "Worker", + "hidden": true, + "types": [], + "commands": [ + { + "name": "enable" + }, + { + "name": "disable" + }, + { + "name": "sendMessageToWorker", + "parameters": [ + { "name": "workerId", "type": "string" }, + { "name": "message", "type": "string" } + ] + }, + { + "name": "setWaitForDebuggerOnStart", + "parameters": [ + { "name": "value", "type": "boolean" } + ] + } + ], + "events": [ + { + "name": "workerCreated", + "parameters": [ + { "name": "workerId", "type": "string" }, + { "name": "url", "type": "string" }, + { "name": "waitingForDebugger", "type": "boolean" } + ] + }, + { + "name": "workerTerminated", + "parameters": [ + { "name": "workerId", "type": "string" } + ] + }, + { + "name": "dispatchMessageFromWorker", + "parameters": [ + { "name": "workerId", "type": "string" }, + { "name": "message", "type": "string" } + ] + } + ] + }, + { + "domain": "ServiceWorker", + "hidden": true, + "types": [ + { + "id": "ServiceWorkerRegistration", + "type": "object", + "description": "ServiceWorker registration.", + "properties": [ + { "name": "registrationId", "type": "string" }, + { "name": "scopeURL", "type": "string" }, + { "name": "isDeleted", "type": "boolean" } + ] + }, + { + "id": "ServiceWorkerVersionRunningStatus", + "type": "string", + "enum": ["stopped", "starting", "running", "stopping"] + }, + { + "id": "ServiceWorkerVersionStatus", + "type": "string", + "enum": ["new", "installing", "installed", "activating", "activated", "redundant"] + }, + { + "id": "TargetID", + "type": "string" + }, + { + "id": "ServiceWorkerVersion", + "type": "object", + "description": "ServiceWorker version.", + "properties": [ + { "name": "versionId", "type": "string" }, + { "name": "registrationId", "type": "string" }, + { "name": "scriptURL", "type": "string" }, + { "name": "runningStatus", "$ref": "ServiceWorkerVersionRunningStatus" }, + { "name": "status", "$ref": "ServiceWorkerVersionStatus" }, + { "name": "scriptLastModified", "type": "number", "optional": true, "description": "The Last-Modified header value of the main script." }, + { "name": "scriptResponseTime", "type": "number", "optional": true, "description": "The time at which the response headers of the main script were received from the server. For cached script it is the last time the cache entry was validated." }, + { "name": "controlledClients", "type": "array", "optional": true, "items": { "$ref": "TargetID" } } + ] + }, + { + "id": "ServiceWorkerErrorMessage", + "type": "object", + "description": "ServiceWorker error message.", + "properties": [ + { "name": "errorMessage", "type": "string" }, + { "name": "registrationId", "type": "string" }, + { "name": "versionId", "type": "string" }, + { "name": "sourceURL", "type": "string" }, + { "name": "lineNumber", "type": "integer" }, + { "name": "columnNumber", "type": "integer" } + ] + }, + { + "id": "TargetInfo", + "type": "object", + "properties": [ + { "name": "id", "$ref": "TargetID" }, + { "name": "type", "type": "string" }, + { "name": "title", "type": "string" }, + { "name": "url", "type": "string" } + ] + } + ], + "commands": [ + { + "name": "enable", + "handlers": ["browser"] + }, + { + "name": "disable", + "handlers": ["browser"] + }, + { + "name": "sendMessage", + "parameters": [ + { "name": "workerId", "type": "string" }, + { "name": "message", "type": "string" } + ], + "handlers": ["browser"] + }, + { + "name": "stop", + "parameters": [ + { "name": "workerId", "type": "string" } + ], + "handlers": ["browser"] + }, + { + "name": "unregister", + "parameters": [ + { "name": "scopeURL", "type": "string" } + ], + "handlers": ["browser"] + }, + { + "name": "updateRegistration", + "parameters": [ + { "name": "scopeURL", "type": "string" } + ], + "handlers": ["browser"] + }, + { + "name": "startWorker", + "parameters": [ + { "name": "scopeURL", "type": "string" } + ], + "handlers": ["browser"] + }, + { + "name": "skipWaiting", + "parameters": [ + { "name": "scopeURL", "type": "string" } + ], + "handlers": ["browser"] + }, + { + "name": "stopWorker", + "parameters": [ + { "name": "versionId", "type": "string" } + ], + "handlers": ["browser"] + }, + { + "name": "inspectWorker", + "parameters": [ + { "name": "versionId", "type": "string" } + ], + "handlers": ["browser"] + }, + { + "name": "setForceUpdateOnPageLoad", + "parameters": [ + { "name": "forceUpdateOnPageLoad", "type": "boolean" } + ], + "handlers": ["browser"] + }, + { + "name": "deliverPushMessage", + "parameters": [ + { "name": "origin", "type": "string" }, + { "name": "registrationId", "type": "string" }, + { "name": "data", "type": "string" } + ], + "handlers": ["browser"] + }, + { + "name": "getTargetInfo", + "parameters": [ + { "name": "targetId", "$ref": "TargetID" } + ], + "returns": [ + { "name": "targetInfo","$ref": "TargetInfo" } + ], + "handlers": ["browser"] + }, + { + "name": "activateTarget", + "parameters": [ + { "name": "targetId", "$ref": "TargetID" } + ], + "handlers": ["browser"] + } + ], + "events": [ + { + "name": "workerCreated", + "parameters": [ + { "name": "workerId", "type": "string" }, + { "name": "url", "type": "string" }, + { "name": "versionId", "type": "string" } + ], + "handlers": ["browser"] + }, + { + "name": "workerTerminated", + "parameters": [ + { "name": "workerId", "type": "string" } + ], + "handlers": ["browser"] + }, + { + "name": "dispatchMessage", + "parameters": [ + { "name": "workerId", "type": "string" }, + { "name": "message", "type": "string" } + ], + "handlers": ["browser"] + }, + { + "name": "workerRegistrationUpdated", + "parameters": [ + { "name": "registrations", "type": "array", "items": { "$ref": "ServiceWorkerRegistration" } } + ], + "handlers": ["browser"] + }, + { + "name": "workerVersionUpdated", + "parameters": [ + { "name": "versions", "type": "array", "items": { "$ref": "ServiceWorkerVersion" } } + ], + "handlers": ["browser"] + }, + { + "name": "workerErrorReported", + "parameters": [ + { "name": "errorMessage", "$ref": "ServiceWorkerErrorMessage" } + ], + "handlers": ["browser"] + } + ] + }, + { + "domain": "Input", + "types": [ + { + "id": "TouchPoint", + "type": "object", + "hidden": true, + "properties": [ + { "name": "state", "type": "string", "enum": ["touchPressed", "touchReleased", "touchMoved", "touchStationary", "touchCancelled"], "description": "State of the touch point." }, + { "name": "x", "type": "integer", "description": "X coordinate of the event relative to the main frame's viewport."}, + { "name": "y", "type": "integer", "description": "Y coordinate of the event relative to the main frame's viewport. 0 refers to the top of the viewport and Y increases as it proceeds towards the bottom of the viewport."}, + { "name": "radiusX", "type": "integer", "optional": true, "description": "X radius of the touch area (default: 1)."}, + { "name": "radiusY", "type": "integer", "optional": true, "description": "Y radius of the touch area (default: 1)."}, + { "name": "rotationAngle", "type": "number", "optional": true, "description": "Rotation angle (default: 0.0)."}, + { "name": "force", "type": "number", "optional": true, "description": "Force (default: 1.0)."}, + { "name": "id", "type": "number", "optional": true, "description": "Identifier used to track touch sources between events, must be unique within an event."} + ] + }, + { + "id": "GestureSourceType", + "type": "string", + "hidden": true, + "enum": ["default", "touch", "mouse"] + } + ], + "commands": [ + { + "name": "dispatchKeyEvent", + "parameters": [ + { "name": "type", "type": "string", "enum": ["keyDown", "keyUp", "rawKeyDown", "char"], "description": "Type of the key event." }, + { "name": "modifiers", "type": "integer", "optional": true, "description": "Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 (default: 0)." }, + { "name": "timestamp", "type": "number", "optional": true, "description": "Time at which the event occurred. Measured in UTC time in seconds since January 1, 1970 (default: current time)." }, + { "name": "text", "type": "string", "optional": true, "description": "Text as generated by processing a virtual key code with a keyboard layout. Not needed for for <code>keyUp</code> and <code>rawKeyDown</code> events (default: \"\")" }, + { "name": "unmodifiedText", "type": "string", "optional": true, "description": "Text that would have been generated by the keyboard if no modifiers were pressed (except for shift). Useful for shortcut (accelerator) key handling (default: \"\")." }, + { "name": "keyIdentifier", "type": "string", "optional": true, "description": "Unique key identifier (e.g., 'U+0041') (default: \"\")." }, + { "name": "code", "type": "string", "optional": true, "description": "Unique DOM defined string value for each physical key (e.g., 'KeyA') (default: \"\")." }, + { "name": "key", "type": "string", "optional": true, "description": "Unique DOM defined string value describing the meaning of the key in the context of active modifiers, keyboard layout, etc (e.g., 'AltGr') (default: \"\")." }, + { "name": "windowsVirtualKeyCode", "type": "integer", "optional": true, "description": "Windows virtual key code (default: 0)." }, + { "name": "nativeVirtualKeyCode", "type": "integer", "optional": true, "description": "Native virtual key code (default: 0)." }, + { "name": "autoRepeat", "type": "boolean", "optional": true, "description": "Whether the event was generated from auto repeat (default: false)." }, + { "name": "isKeypad", "type": "boolean", "optional": true, "description": "Whether the event was generated from the keypad (default: false)." }, + { "name": "isSystemKey", "type": "boolean", "optional": true, "description": "Whether the event was a system key event (default: false)." } + ], + "description": "Dispatches a key event to the page.", + "handlers": ["browser"] + }, + { + "name": "dispatchMouseEvent", + "parameters": [ + { "name": "type", "type": "string", "enum": ["mousePressed", "mouseReleased", "mouseMoved"], "description": "Type of the mouse event." }, + { "name": "x", "type": "integer", "description": "X coordinate of the event relative to the main frame's viewport."}, + { "name": "y", "type": "integer", "description": "Y coordinate of the event relative to the main frame's viewport. 0 refers to the top of the viewport and Y increases as it proceeds towards the bottom of the viewport."}, + { "name": "modifiers", "type": "integer", "optional": true, "description": "Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 (default: 0)." }, + { "name": "timestamp", "type": "number", "optional": true, "description": "Time at which the event occurred. Measured in UTC time in seconds since January 1, 1970 (default: current time)." }, + { "name": "button", "type": "string", "enum": ["none", "left", "middle", "right"], "optional": true, "description": "Mouse button (default: \"none\")." }, + { "name": "clickCount", "type": "integer", "optional": true, "description": "Number of times the mouse button was clicked (default: 0)." } + ], + "description": "Dispatches a mouse event to the page.", + "handlers": ["browser"] + }, + { + "name": "dispatchTouchEvent", + "hidden": true, + "parameters": [ + { "name": "type", "type": "string", "enum": ["touchStart", "touchEnd", "touchMove"], "description": "Type of the touch event." }, + { "name": "touchPoints", "type": "array", "items": { "$ref": "TouchPoint" }, "description": "Touch points." }, + { "name": "modifiers", "type": "integer", "optional": true, "description": "Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 (default: 0)." }, + { "name": "timestamp", "type": "number", "optional": true, "description": "Time at which the event occurred. Measured in UTC time in seconds since January 1, 1970 (default: current time)." } + ], + "description": "Dispatches a touch event to the page." + }, + { + "name": "emulateTouchFromMouseEvent", + "hidden": true, + "parameters": [ + { "name": "type", "type": "string", "enum": ["mousePressed", "mouseReleased", "mouseMoved", "mouseWheel"], "description": "Type of the mouse event." }, + { "name": "x", "type": "integer", "description": "X coordinate of the mouse pointer in DIP."}, + { "name": "y", "type": "integer", "description": "Y coordinate of the mouse pointer in DIP."}, + { "name": "timestamp", "type": "number", "description": "Time at which the event occurred. Measured in UTC time in seconds since January 1, 1970." }, + { "name": "button", "type": "string", "enum": ["none", "left", "middle", "right"], "description": "Mouse button." }, + { "name": "deltaX", "type": "number", "optional": true, "description": "X delta in DIP for mouse wheel event (default: 0)."}, + { "name": "deltaY", "type": "number", "optional": true, "description": "Y delta in DIP for mouse wheel event (default: 0)."}, + { "name": "modifiers", "type": "integer", "optional": true, "description": "Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 (default: 0)." }, + { "name": "clickCount", "type": "integer", "optional": true, "description": "Number of times the mouse button was clicked (default: 0)." } + ], + "description": "Emulates touch event from the mouse event parameters.", + "handlers": ["browser"] + }, + { + "name": "synthesizePinchGesture", + "async": true, + "parameters": [ + { "name": "x", "type": "integer", "description": "X coordinate of the start of the gesture in CSS pixels." }, + { "name": "y", "type": "integer", "description": "Y coordinate of the start of the gesture in CSS pixels." }, + { "name": "scaleFactor", "type": "number", "description": "Relative scale factor after zooming (>1.0 zooms in, <1.0 zooms out)." }, + { "name": "relativeSpeed", "type": "integer", "optional": true, "description": "Relative pointer speed in pixels per second (default: 800)." }, + { "name": "gestureSourceType", "$ref": "GestureSourceType", "optional": true, "description": "Which type of input events to be generated (default: 'default', which queries the platform for the preferred input type)." } + ], + "description": "Synthesizes a pinch gesture over a time period by issuing appropriate touch events.", + "hidden": true, + "handlers": ["browser"] + }, + { + "name": "synthesizeScrollGesture", + "async": true, + "parameters": [ + { "name": "x", "type": "integer", "description": "X coordinate of the start of the gesture in CSS pixels." }, + { "name": "y", "type": "integer", "description": "Y coordinate of the start of the gesture in CSS pixels." }, + { "name": "xDistance", "type": "integer", "optional": true, "description": "The distance to scroll along the X axis (positive to scroll left)." }, + { "name": "yDistance", "type": "integer", "optional": true, "description": "The distance to scroll along the Y axis (positive to scroll up)." }, + { "name": "xOverscroll", "type": "integer", "optional": true, "description": "The number of additional pixels to scroll back along the X axis, in addition to the given distance." }, + { "name": "yOverscroll", "type": "integer", "optional": true, "description": "The number of additional pixels to scroll back along the Y axis, in addition to the given distance." }, + { "name": "preventFling", "type": "boolean", "optional": true, "description": "Prevent fling (default: true)." }, + { "name": "speed", "type": "integer", "optional": true, "description": "Swipe speed in pixels per second (default: 800)." }, + { "name": "gestureSourceType", "$ref": "GestureSourceType", "optional": true, "description": "Which type of input events to be generated (default: 'default', which queries the platform for the preferred input type)." }, + { "name": "repeatCount", "type": "integer", "optional": true, "description": "The number of times to repeat the gesture (default: 0)." }, + { "name": "repeatDelayMs", "type": "integer", "optional": true, "description": "The number of milliseconds delay between each repeat. (default: 250)." }, + { "name": "interactionMarkerName", "type": "string", "optional": true, "description": "The name of the interaction markers to generate, if not empty (default: \"\")." } + ], + "description": "Synthesizes a scroll gesture over a time period by issuing appropriate touch events.", + "hidden": true, + "handlers": ["browser"] + }, + { + "name": "synthesizeTapGesture", + "async": true, + "parameters": [ + { "name": "x", "type": "integer", "description": "X coordinate of the start of the gesture in CSS pixels." }, + { "name": "y", "type": "integer", "description": "Y coordinate of the start of the gesture in CSS pixels." }, + { "name": "duration", "type": "integer", "optional": true, "description": "Duration between touchdown and touchup events in ms (default: 50)." }, + { "name": "tapCount", "type": "integer", "optional": true, "description": "Number of times to perform the tap (e.g. 2 for double tap, default: 1)." }, + { "name": "gestureSourceType", "$ref": "GestureSourceType", "optional": true, "description": "Which type of input events to be generated (default: 'default', which queries the platform for the preferred input type)." } + ], + "description": "Synthesizes a tap gesture over a time period by issuing appropriate touch events.", + "hidden": true, + "handlers": ["browser"] + } + ], + "events": [] + }, + { + "domain": "LayerTree", + "hidden": true, + "types": [ + { + "id": "LayerId", + "type": "string", + "description": "Unique Layer identifier." + }, + { + "id": "SnapshotId", + "type": "string", + "description": "Unique snapshot identifier." + }, + { + "id": "ScrollRect", + "type": "object", + "description": "Rectangle where scrolling happens on the main thread.", + "properties": [ + { "name": "rect", "$ref": "DOM.Rect", "description": "Rectangle itself." }, + { "name": "type", "type": "string", "enum": ["RepaintsOnScroll", "TouchEventHandler", "WheelEventHandler"], "description": "Reason for rectangle to force scrolling on the main thread" } + ] + }, + { + "id": "PictureTile", + "type": "object", + "description": "Serialized fragment of layer picture along with its offset within the layer.", + "properties": [ + { "name": "x", "type": "number", "description": "Offset from owning layer left boundary" }, + { "name": "y", "type": "number", "description": "Offset from owning layer top boundary" }, + { "name": "picture", "type": "string", "description": "Base64-encoded snapshot data." } + ] + }, + { + "id": "Layer", + "type": "object", + "description": "Information about a compositing layer.", + "properties": [ + { "name": "layerId", "$ref": "LayerId", "description": "The unique id for this layer." }, + { "name": "parentLayerId", "$ref": "LayerId", "optional": true, "description": "The id of parent (not present for root)." }, + { "name": "backendNodeId", "$ref": "DOM.BackendNodeId", "optional": true, "description": "The backend id for the node associated with this layer." }, + { "name": "offsetX", "type": "number", "description": "Offset from parent layer, X coordinate." }, + { "name": "offsetY", "type": "number", "description": "Offset from parent layer, Y coordinate." }, + { "name": "width", "type": "number", "description": "Layer width." }, + { "name": "height", "type": "number", "description": "Layer height." }, + { "name": "transform", "type": "array", "items": { "type": "number" }, "minItems": 16, "maxItems": 16, "optional": true, "description": "Transformation matrix for layer, default is identity matrix" }, + { "name": "anchorX", "type": "number", "optional": true, "description": "Transform anchor point X, absent if no transform specified" }, + { "name": "anchorY", "type": "number", "optional": true, "description": "Transform anchor point Y, absent if no transform specified" }, + { "name": "anchorZ", "type": "number", "optional": true, "description": "Transform anchor point Z, absent if no transform specified" }, + { "name": "paintCount", "type": "integer", "description": "Indicates how many time this layer has painted." }, + { "name": "drawsContent", "type": "boolean", "description": "Indicates whether this layer hosts any content, rather than being used for transform/scrolling purposes only." }, + { "name": "invisible", "type": "boolean", "optional": true, "description": "Set if layer is not visible." }, + { "name": "scrollRects", "type": "array", "items": { "$ref": "ScrollRect"}, "optional": true, "description": "Rectangles scrolling on main thread only."} + ] + }, + { + "id": "PaintProfile", + "type": "array", + "description": "Array of timings, one per paint step.", + "items": { + "type": "number", + "description": "A time in seconds since the end of previous step (for the first step, time since painting started)" + } + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables compositing tree inspection." + }, + { + "name": "disable", + "description": "Disables compositing tree inspection." + }, + { + "name": "compositingReasons", + "parameters": [ + { "name": "layerId", "$ref": "LayerId", "description": "The id of the layer for which we want to get the reasons it was composited." } + ], + "description": "Provides the reasons why the given layer was composited.", + "returns": [ + { "name": "compositingReasons", "type": "array", "items": { "type": "string" }, "description": "A list of strings specifying reasons for the given layer to become composited." } + ] + }, + { + "name": "makeSnapshot", + "parameters": [ + { "name": "layerId", "$ref": "LayerId", "description": "The id of the layer." } + ], + "description": "Returns the layer snapshot identifier.", + "returns": [ + { "name": "snapshotId", "$ref": "SnapshotId", "description": "The id of the layer snapshot." } + ] + }, + { + "name": "loadSnapshot", + "parameters": [ + { "name": "tiles", "type": "array", "items": { "$ref": "PictureTile" }, "minItems": 1, "description": "An array of tiles composing the snapshot." } + ], + "description": "Returns the snapshot identifier.", + "returns": [ + { "name": "snapshotId", "$ref": "SnapshotId", "description": "The id of the snapshot." } + ] + }, + { + "name": "releaseSnapshot", + "parameters": [ + { "name": "snapshotId", "$ref": "SnapshotId", "description": "The id of the layer snapshot." } + ], + "description": "Releases layer snapshot captured by the back-end." + }, + { + "name": "profileSnapshot", + "parameters": [ + { "name": "snapshotId", "$ref": "SnapshotId", "description": "The id of the layer snapshot." }, + { "name": "minRepeatCount", "type": "integer", "optional": true, "description": "The maximum number of times to replay the snapshot (1, if not specified)." }, + { "name": "minDuration", "type": "number", "optional": true, "description": "The minimum duration (in seconds) to replay the snapshot." }, + { "name": "clipRect", "$ref": "DOM.Rect", "optional": true, "description": "The clip rectangle to apply when replaying the snapshot." } + ], + "returns": [ + { "name": "timings", "type": "array", "items": { "$ref": "PaintProfile" }, "description": "The array of paint profiles, one per run." } + ] + }, + { + "name": "replaySnapshot", + "parameters": [ + { "name": "snapshotId", "$ref": "SnapshotId", "description": "The id of the layer snapshot." }, + { "name": "fromStep", "type": "integer", "optional": true, "description": "The first step to replay from (replay from the very start if not specified)." }, + { "name": "toStep", "type": "integer", "optional": true, "description": "The last step to replay to (replay till the end if not specified)." }, + { "name": "scale", "type": "number", "optional": true, "description": "The scale to apply while replaying (defaults to 1)." } + ], + "description": "Replays the layer snapshot and returns the resulting bitmap.", + "returns": [ + { "name": "dataURL", "type": "string", "description": "A data: URL for resulting image." } + ] + }, + { + "name": "snapshotCommandLog", + "parameters": [ + { "name": "snapshotId", "$ref": "SnapshotId", "description": "The id of the layer snapshot." } + ], + "description": "Replays the layer snapshot and returns canvas log.", + "returns": [ + { "name": "commandLog", "type": "array", "items": { "type": "object" }, "description": "The array of canvas function calls." } + ] + } + ], + "events": [ + { + "name": "layerTreeDidChange", + "parameters": [ + { "name": "layers", "type": "array", "items": { "$ref": "Layer" }, "optional": true, "description": "Layer tree, absent if not in the comspositing mode." } + ] + }, + { + "name": "layerPainted", + "parameters": [ + { "name": "layerId", "$ref": "LayerId", "description": "The id of the painted layer." }, + { "name": "clip", "$ref": "DOM.Rect", "description": "Clip rectangle." } + ] + } + ] + }, + { + "domain": "DeviceOrientation", + "hidden": true, + "commands": [ + { + "name": "setDeviceOrientationOverride", + "description": "Overrides the Device Orientation.", + "parameters": [ + { "name": "alpha", "type": "number", "description": "Mock alpha"}, + { "name": "beta", "type": "number", "description": "Mock beta"}, + { "name": "gamma", "type": "number", "description": "Mock gamma"} + ] + }, + { + "name": "clearDeviceOrientationOverride", + "description": "Clears the overridden Device Orientation." + } + ] + }, + { + "domain": "Tracing", + "types": [ + { + "id": "MemoryDumpConfig", + "type": "object", + "description": "Configuration for memory dump. Used only when \"memory-infra\" category is enabled." + }, + { + "id": "TraceConfig", + "type": "object", + "properties": [ + { "name": "recordMode", "type": "string", "optional": true, "enum": ["recordUntilFull", "recordContinuously", "recordAsMuchAsPossible", "echoToConsole"], "description": "Controls how the trace buffer stores data." }, + { "name": "enableSampling", "type": "boolean", "optional": true, "description": "Turns on JavaScript stack sampling." }, + { "name": "enableSystrace", "type": "boolean", "optional": true, "description": "Turns on system tracing." }, + { "name": "enableArgumentFilter", "type": "boolean", "optional": true, "description": "Turns on argument filter." }, + { "name": "includedCategories", "type": "array", "items": { "type": "string" }, "optional": true, "description": "Included category filters." }, + { "name": "excludedCategories", "type": "array", "items": { "type": "string" }, "optional": true, "description": "Excluded category filters." }, + { "name": "syntheticDelays", "type": "array", "items": { "type": "string" }, "optional": true, "description": "Configuration to synthesize the delays in tracing." }, + { "name": "memoryDumpConfig", "$ref": "MemoryDumpConfig", "optional": true, "description": "Configuration for memory dump triggers. Used only when \"memory-infra\" category is enabled." } + ] + } + ], + "commands": [ + { + "name": "start", + "async": true, + "description": "Start trace events collection.", + "parameters": [ + { "name": "categories", "type": "string", "optional": true, "deprecated": true, "description": "Category/tag filter" }, + { "name": "options", "type": "string", "optional": true, "deprecated": true, "description": "Tracing options" }, + { "name": "bufferUsageReportingInterval", "type": "number", "optional": true, "description": "If set, the agent will issue bufferUsage events at this interval, specified in milliseconds" }, + { "name": "transferMode", "type": "string", "enum": ["ReportEvents", "ReturnAsStream"], "optional": true, "description": "Whether to report trace events as series of dataCollected events or to save trace to a stream (defaults to <code>ReportEvents</code>)." }, + { "name": "traceConfig", "$ref": "TraceConfig", "optional": true, "description": "" } + ], + "handlers": ["browser", "renderer"] + }, + { + "name": "end", + "async": true, + "description": "Stop trace events collection.", + "handlers": ["browser", "renderer"] + }, + { + "name": "getCategories", + "async": true, + "description": "Gets supported tracing categories.", + "returns": [ + { "name": "categories", "type": "array", "items": { "type": "string" }, "description": "A list of supported tracing categories." } + ], + "handlers": ["browser"] + }, + { + "name": "requestMemoryDump", + "async": true, + "description": "Request a global memory dump.", + "returns": [ + { "name": "dumpGuid", "type": "string", "description": "GUID of the resulting global memory dump." }, + { "name": "success", "type": "boolean", "description": "True iff the global memory dump succeeded." } + ], + "handlers": ["browser"] + }, + { + "name": "recordClockSyncMarker", + "description": "Record a clock sync marker in the trace.", + "parameters": [ + { "name": "syncId", "type": "string", "description": "The ID of this clock sync marker" } + ], + "handlers": ["browser"] + } + ], + "events": [ + { + "name": "dataCollected", + "parameters": [ + { "name": "value", "type": "array", "items": { "type": "object" } } + ], + "description": "Contains an bucket of collected trace events. When tracing is stopped collected events will be send as a sequence of dataCollected events followed by tracingComplete event.", + "handlers": ["browser"] + }, + { + "name": "tracingComplete", + "description": "Signals that tracing is stopped and there is no trace buffers pending flush, all data were delivered via dataCollected events.", + "parameters": [ + { "name": "stream", "$ref": "IO.StreamHandle", "optional": true, "description": "A handle of the stream that holds resulting trace data." } + ], + "handlers": ["browser"] + }, + { + "name": "bufferUsage", + "parameters": [ + { "name": "percentFull", "type": "number", "optional": true, "description": "A number in range [0..1] that indicates the used size of event buffer as a fraction of its total size." }, + { "name": "eventCount", "type": "number", "optional": true, "description": "An approximate number of events in the trace log." }, + { "name": "value", "type": "number", "optional": true, "description": "A number in range [0..1] that indicates the used size of event buffer as a fraction of its total size." } + ], + "handlers": ["browser"] + } + ] + }, + { + "domain": "Animation", + "hidden": true, + "types": [ + { + "id": "Animation", + "type": "object", + "hidden": true, + "properties": [ + { "name": "id", "type": "string", "description": "<code>Animation</code>'s id." }, + { "name": "name", "type": "string", "description": "<code>Animation</code>'s name." }, + { "name": "pausedState", "type": "boolean", "hidden": "true", "description": "<code>Animation</code>'s internal paused state." }, + { "name": "playState", "type": "string", "description": "<code>Animation</code>'s play state." }, + { "name": "playbackRate", "type": "number", "description": "<code>Animation</code>'s playback rate." }, + { "name": "startTime", "type": "number", "description": "<code>Animation</code>'s start time." }, + { "name": "currentTime", "type": "number", "description": "<code>Animation</code>'s current time." }, + { "name": "source", "$ref": "AnimationEffect", "description": "<code>Animation</code>'s source animation node." }, + { "name": "type", "type": "string", "enum": ["CSSTransition", "CSSAnimation", "WebAnimation"], "description": "Animation type of <code>Animation</code>." }, + { "name": "cssId", "type": "string", "optional": true, "description": "A unique ID for <code>Animation</code> representing the sources that triggered this CSS animation/transition."} + ], + "description": "Animation instance." + }, + { + "id": "AnimationEffect", + "type": "object", + "hidden": true, + "properties": [ + { "name": "delay", "type": "number", "description": "<code>AnimationEffect</code>'s delay." }, + { "name": "endDelay", "type": "number", "description": "<code>AnimationEffect</code>'s end delay." }, + { "name": "playbackRate", "type": "number", "description": "<code>AnimationEffect</code>'s playbackRate." }, + { "name": "iterationStart", "type": "number", "description": "<code>AnimationEffect</code>'s iteration start." }, + { "name": "iterations", "type": "number", "description": "<code>AnimationEffect</code>'s iterations." }, + { "name": "duration", "type": "number", "description": "<code>AnimationEffect</code>'s iteration duration." }, + { "name": "direction", "type": "string", "description": "<code>AnimationEffect</code>'s playback direction." }, + { "name": "fill", "type": "string", "description": "<code>AnimationEffect</code>'s fill mode." }, + { "name": "backendNodeId", "$ref": "DOM.BackendNodeId", "description": "<code>AnimationEffect</code>'s target node." }, + { "name": "keyframesRule", "$ref": "KeyframesRule", "optional": true, "description": "<code>AnimationEffect</code>'s keyframes." }, + { "name": "easing", "type": "string", "description": "<code>AnimationEffect</code>'s timing function." } + ], + "description": "AnimationEffect instance" + }, + { + "id": "KeyframesRule", + "type": "object", + "properties": [ + { "name": "name", "type": "string", "optional": true, "description": "CSS keyframed animation's name." }, + { "name": "keyframes", "type": "array", "items": { "$ref": "KeyframeStyle" }, "description": "List of animation keyframes." } + ], + "description": "Keyframes Rule" + }, + { + "id": "KeyframeStyle", + "type": "object", + "properties": [ + { "name": "offset", "type": "string", "description": "Keyframe's time offset." }, + { "name": "easing", "type": "string", "description": "<code>AnimationEffect</code>'s timing function." } + ], + "description": "Keyframe Style" + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables animation domain notifications." + }, + { + "name": "disable", + "description": "Disables animation domain notifications." + }, + { + "name": "getPlaybackRate", + "returns": [ + { "name": "playbackRate", "type": "number", "description": "Playback rate for animations on page."} + ], + "description": "Gets the playback rate of the document timeline." + }, + { + "name": "setPlaybackRate", + "parameters": [ + { "name": "playbackRate", "type": "number", "description": "Playback rate for animations on page" } + ], + "description": "Sets the playback rate of the document timeline." + }, + { + "name": "getCurrentTime", + "parameters": [ + { "name": "id", "type": "string", "description": "Id of animation." } + ], + "returns": [ + { "name": "currentTime", "type": "number", "description": "Current time of the page." } + ], + "description": "Returns the current time of the an animation." + }, + { + "name": "setPaused", + "parameters": [ + { "name": "animations", "type": "array", "items": { "type": "string" }, "description": "Animations to set the pause state of." }, + { "name": "paused", "type": "boolean", "description": "Paused state to set to." } + ], + "description": "Sets the paused state of a set of animations." + }, + { + "name": "setTiming", + "parameters": [ + { "name": "animationId", "type": "string", "description": "Animation id." }, + { "name": "duration", "type": "number", "description": "Duration of the animation." }, + { "name": "delay", "type": "number", "description": "Delay of the animation." } + ], + "description": "Sets the timing of an animation node." + }, + { + "name": "seekAnimations", + "parameters": [ + { "name": "animations", "type": "array", "items": { "type": "string" }, "description": "List of animation ids to seek." }, + { "name": "currentTime", "type": "number", "description": "Set the current time of each animation." } + ], + "description": "Seek a set of animations to a particular time within each animation." + }, + { + "name": "releaseAnimations", + "parameters": [ + { "name": "animations", "type": "array", "items": { "type": "string" }, "description": "List of animation ids to seek." } + ], + "description": "Releases a set of animations to no longer be manipulated." + }, + { + "name": "resolveAnimation", + "parameters": [ + { "name": "animationId", "type": "string", "description": "Animation id." } + ], + "returns": [ + { "name": "remoteObject", "$ref": "Runtime.RemoteObject", "description": "Corresponding remote object." } + ], + "description": "Gets the remote object of the Animation." + } + ], + "events": [ + { + "name": "animationCreated", + "parameters": [ + { "name": "id", "type": "string", "description": "Id of the animation that was created." } + ], + "description": "Event for each animation that has been created." + }, + { + "name": "animationStarted", + "parameters": [ + { "name": "animation", "$ref": "Animation", "description": "Animation that was started." } + ], + "description": "Event for animation that has been started." + }, + { + "name": "animationCanceled", + "parameters": [ + { "name": "id", "type": "string", "description": "Id of the animation that was cancelled."} + ], + "description": "Event for when an animation has been cancelled." + } + ] + }, + { + "domain": "Accessibility", + "hidden": true, + "types": [ + { + "id": "AXNodeId", + "type": "string", + "description": "Unique accessibility node identifier." + }, + { + "id": "AXValueType", + "type": "string", + "enum": [ "boolean", "tristate", "booleanOrUndefined", "idref", "idrefList", "integer", "node", "nodeList", "number", "string", "computedString", "token", "tokenList", "domRelation", "role", "internalRole", "valueUndefined" ], + "description": "Enum of possible property types." + }, + { + "id": "AXValueSourceType", + "type": "string", + "enum": [ "attribute", "implicit", "style", "contents", "placeholder", "relatedElement" ], + "description": "Enum of possible property sources." + }, + { "id": "AXValueNativeSourceType", + "type": "string", + "enum": [ "figcaption", "label", "labelfor", "labelwrapped", "legend", "tablecaption", "title", "other" ], + "description": "Enum of possible native property sources (as a subtype of a particular AXValueSourceType)." + }, + { + "id": "AXValueSource", + "type": "object", + "properties": [ + { "name": "type", "$ref": "AXValueSourceType", "description": "What type of source this is." }, + { "name": "value", "$ref": "AXValue", "description": "The value of this property source.", "optional": true }, + { "name": "attribute", "type": "string", "description": "The name of the relevant attribute, if any.", "optional": true }, + { "name": "attributeValue", "$ref": "AXValue", "description": "The value of the relevant attribute, if any.", "optional": true }, + { "name": "superseded", "type": "boolean", "description": "Whether this source is superseded by a higher priority source.", "optional": true }, + { "name": "nativeSource", "$ref": "AXValueNativeSourceType", "description": "The native markup source for this value, e.g. a <label> element.", "optional": true }, + { "name": "nativeSourceValue", "$ref": "AXValue", "description": "The value, such as a node or node list, of the native source.", "optional": true }, + { "name": "invalid", "type": "boolean", "description": "Whether the value for this property is invalid.", "optional": true }, + { "name": "invalidReason", "type": "string", "description": "Reason for the value being invalid, if it is.", "optional": true } + ], + "description": "A single source for a computed AX property." + }, + { + "id": "AXRelatedNode", + "type": "object", + "properties": [ + { "name": "backendNodeId", "$ref": "DOM.BackendNodeId", "description": "The BackendNodeId of the related node." }, + { "name": "idref", "type": "string", "description": "The IDRef value provided, if any.", "optional": true }, + { "name": "text", "type": "string", "description": "The text alternative of this node in the current context.", "optional": true } + ] + }, + { + "id": "AXProperty", + "type": "object", + "properties": [ + { "name": "name", "type": "string", "description": "The name of this property." }, + { "name": "value", "$ref": "AXValue", "description": "The value of this property." } + ] + }, + { + "id": "AXValue", + "type": "object", + "properties": [ + { "name": "type", "$ref": "AXValueType", "description": "The type of this value." }, + + { "name": "value", "type": "any", "description": "The computed value of this property.", "optional": true }, + { "name": "relatedNodes", "type": "array", "items": { "$ref": "AXRelatedNode" }, "description": "One or more related nodes, if applicable.", "optional": true }, + { "name": "sources", "type": "array", "items": { "$ref": "AXValueSource" }, "description": "The sources which contributed to the computation of this property.", "optional": true } + ], + "description": "A single computed AX property." + }, + { + "id": "AXGlobalStates", + "type": "string", + "enum": [ "disabled", "hidden", "hiddenRoot", "invalid" ], + "description": "States which apply to every AX node." + }, + { + "id": "AXLiveRegionAttributes", + "type": "string", + "enum": [ "live", "atomic", "relevant", "busy", "root" ], + "description": "Attributes which apply to nodes in live regions." + }, + { + "id": "AXWidgetAttributes", + "type": "string", + "enum": [ "autocomplete", "haspopup", "level", "multiselectable", "orientation", "multiline", "readonly", "required", "valuemin", "valuemax", "valuetext" ], + "Description": "Attributes which apply to widgets." + }, + { + "id": "AXWidgetStates", + "type": "string", + "enum": [ "checked", "expanded", "pressed", "selected" ], + "description": "States which apply to widgets." + }, + { + "id": "AXRelationshipAttributes", + "type": "string", + "enum": [ "activedescendant", "flowto", "controls", "describedby", "labelledby", "owns" ], + "description": "Relationships between elements other than parent/child/sibling." + }, + { + "id": "AXNode", + "type": "object", + "properties": [ + { "name": "nodeId", "$ref": "AXNodeId", "description": "Unique identifier for this node." }, + { "name": "ignored", "type": "boolean", "description": "Whether this node is ignored for accessibility" }, + { "name": "ignoredReasons", "type": "array", "items": { "$ref": "AXProperty" }, "description": "Collection of reasons why this node is hidden.", "optional": true }, + { "name": "role", "$ref": "AXValue", "description": "This <code>Node</code>'s role, whether explicit or implicit.", "optional": true}, + { "name": "name", "$ref": "AXValue", "description": "The accessible name for this <code>Node</code>.", "optional": true }, + { "name": "description", "$ref": "AXValue", "description": "The accessible description for this <code>Node</code>.", "optional": true }, + { "name": "value", "$ref": "AXValue", "description": "The value for this <code>Node</code>.", "optional": true }, + { "name": "properties", "type": "array", "items": { "$ref": "AXProperty" }, "description": "All other properties", "optional": true } + ], + "description": "A node in the accessibility tree." + } + ], + "commands": [ + { + "name": "getAXNode", + "parameters": [ + { "name": "nodeId", "$ref": "DOM.NodeId", "description": "ID of node to get accessibility node for." } + ], + "returns": [ + { "name": "accessibilityNode", "$ref": "AXNode", "description": "The <code>Accessibility.AXNode</code> for this DOM node, if it exists.", "optional": true } + ], + "description": "Fetches the accessibility node for this DOM node, if it exists.", + "hidden": true + } + ] + }, + { + "domain": "Storage", + "hidden": true, + "types": [ + { + "id": "StorageType", + "type": "string", + "enum": [ + "appcache", + "cookies", + "file_systems", + "indexeddb", + "local_storage", + "shader_cache", + "websql", + "webrtc_indetity", + "service_workers", + "cache_storage", + "all" + ], + "description": "Enum of possible storage types." + } + ], + "commands": [ + { + "name": "clearDataForOrigin", + "parameters": [ + { "name": "origin", "type": "string", "description": "Security origin." }, + { "name": "storageTypes", "type": "string", "description": "Comma separated origin names." } + ], + "description": "Clears storage for origin.", + "handlers": ["browser"] + } + ] + }] +} diff --git a/deps/v8_inspector/platform/PlatformExport.h b/deps/v8_inspector/platform/PlatformExport.h new file mode 100644 index 00000000000000..8230fbb80fe30d --- /dev/null +++ b/deps/v8_inspector/platform/PlatformExport.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef PlatformExport_h +#define PlatformExport_h + +#if !defined(BLINK_PLATFORM_IMPLEMENTATION) +#define BLINK_PLATFORM_IMPLEMENTATION 0 +#endif + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) +#if BLINK_PLATFORM_IMPLEMENTATION +#define PLATFORM_EXPORT __declspec(dllexport) +#else +#define PLATFORM_EXPORT __declspec(dllimport) +#endif +#else // defined(WIN32) +#define PLATFORM_EXPORT __attribute__((visibility("default"))) +#endif +#else // defined(COMPONENT_BUILD) +#define PLATFORM_EXPORT +#endif + +#if defined(_MSC_VER) +// MSVC Compiler warning C4275: +// non dll-interface class 'Bar' used as base for dll-interface class 'Foo'. +// Note that this is intended to be used only when no access to the base class' +// static data is done through derived classes or inline methods. For more info, +// see http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx +// +// This pragma will allow exporting a class that inherits from a non-exported +// base class, anywhere in the Blink platform component. This is only +// a problem when using the MSVC compiler on Windows. +#pragma warning(suppress:4275) +#endif + +#endif // PlatformExport_h diff --git a/deps/v8_inspector/platform/inspector_protocol/Allocator.h b/deps/v8_inspector/platform/inspector_protocol/Allocator.h new file mode 100644 index 00000000000000..e38b76cd62b7d3 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/Allocator.h @@ -0,0 +1,22 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef Allocator_h +#define Allocator_h + +enum NotNullTagEnum { NotNullLiteral }; + +#define PROTOCOL_DISALLOW_NEW() \ + private: \ + void* operator new(size_t) = delete; \ + void* operator new(size_t, NotNullTagEnum, void*) = delete; \ + void* operator new(size_t, void*) = delete; \ + public: + +#define PROTOCOL_DISALLOW_COPY(ClassName) \ + private: \ + ClassName(const ClassName&) = delete; \ + ClassName& operator=(const ClassName&) = delete + +#endif /* Allocator_h */ diff --git a/deps/v8_inspector/platform/inspector_protocol/Array.h b/deps/v8_inspector/platform/inspector_protocol/Array.h new file mode 100644 index 00000000000000..07a2993d5c3f7f --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/Array.h @@ -0,0 +1,137 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef Array_h +#define Array_h + +#include "platform/PlatformExport.h" +#include "platform/inspector_protocol/Collections.h" +#include "platform/inspector_protocol/ErrorSupport.h" +#include "platform/inspector_protocol/String16.h" +#include "platform/inspector_protocol/ValueConversions.h" +#include "platform/inspector_protocol/Values.h" + +namespace blink { +namespace protocol { + +template<typename T> +class ArrayBase { +public: + static std::unique_ptr<Array<T>> create() + { + return wrapUnique(new Array<T>()); + } + + static std::unique_ptr<Array<T>> parse(protocol::Value* value, ErrorSupport* errors) + { + protocol::ListValue* array = ListValue::cast(value); + if (!array) { + errors->addError("array expected"); + return nullptr; + } + errors->push(); + std::unique_ptr<Array<T>> result(new Array<T>()); + for (size_t i = 0; i < array->size(); ++i) { + errors->setName(String16::number(i)); + T item = FromValue<T>::parse(array->at(i), errors); + result->m_vector.append(item); + } + errors->pop(); + if (errors->hasErrors()) + return nullptr; + return result; + } + + void addItem(const T& value) + { + m_vector.append(value); + } + + size_t length() + { + return m_vector.size(); + } + + T get(size_t index) + { + return m_vector[index]; + } + + std::unique_ptr<protocol::ListValue> serialize() + { + std::unique_ptr<protocol::ListValue> result = ListValue::create(); + for (auto& item : m_vector) + result->pushValue(toValue(item)); + return result; + } + +private: + protocol::Vector<T> m_vector; +}; + +template<> class Array<String> : public ArrayBase<String> {}; +template<> class Array<String16> : public ArrayBase<String16> {}; +template<> class Array<int> : public ArrayBase<int> {}; +template<> class Array<double> : public ArrayBase<double> {}; +template<> class Array<bool> : public ArrayBase<bool> {}; + +template<typename T> +class Array { +public: + static std::unique_ptr<Array<T>> create() + { + return wrapUnique(new Array<T>()); + } + + static std::unique_ptr<Array<T>> parse(protocol::Value* value, ErrorSupport* errors) + { + protocol::ListValue* array = ListValue::cast(value); + if (!array) { + errors->addError("array expected"); + return nullptr; + } + std::unique_ptr<Array<T>> result = wrapUnique(new Array<T>()); + errors->push(); + for (size_t i = 0; i < array->size(); ++i) { + errors->setName(String16::number(i)); + std::unique_ptr<T> item = FromValue<T>::parse(array->at(i), errors); + result->m_vector.append(std::move(item)); + } + errors->pop(); + if (errors->hasErrors()) + return nullptr; + return result; + } + + void addItem(std::unique_ptr<T> value) + { + m_vector.append(std::move(value)); + } + + size_t length() + { + return m_vector.size(); + } + + T* get(size_t index) + { + return m_vector[index]; + } + + std::unique_ptr<protocol::ListValue> serialize() + { + std::unique_ptr<protocol::ListValue> result = ListValue::create(); + for (auto& item : m_vector) + result->pushValue(toValue(item)); + return result; + } + +private: + protocol::Vector<std::unique_ptr<T>> m_vector; +}; + +} // namespace platform +} // namespace blink + +#endif // !defined(Array_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/Backend_cpp.template b/deps/v8_inspector/platform/inspector_protocol/Backend_cpp.template new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/deps/v8_inspector/platform/inspector_protocol/Backend_h.template b/deps/v8_inspector/platform/inspector_protocol/Backend_h.template new file mode 100644 index 00000000000000..4d6ac711f6dfac --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/Backend_h.template @@ -0,0 +1,78 @@ +// This file is generated + +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef {{class_name}}_h +#define {{class_name}}_h + +#include "platform/inspector_protocol/TypeBuilder.h" + +namespace blink { +namespace protocol { + +class FrontendChannel; +class BackendImplWeakPtr; + +class PLATFORM_EXPORT Backend { +public: + class PLATFORM_EXPORT CallbackBase { + public: + virtual ~CallbackBase() { } + virtual void sendFailure(const ErrorString&) = 0; + }; +{% for domain in api.domains %} + + class PLATFORM_EXPORT {{domain.domain}} { + public: + {% for command in domain.commands %} + {% if "redirect" in command %}{% continue %}{% endif %} + {% if ("handlers" in command) and not ("renderer" in command["handlers"]) %}{% continue %}{% endif %} + {% if "async" in command %} + class PLATFORM_EXPORT {{command.name | to_title_case}}Callback : public CallbackBase { + public: + virtual void sendSuccess( + {%- for parameter in command.returns -%} + {%- if "optional" in parameter -%} + const Maybe<{{resolve_type(parameter).raw_type}}>& {{parameter.name}} + {%- else -%} + {{resolve_type(parameter).pass_type}} {{parameter.name}} + {%- endif -%} + {%- if not loop.last -%}, {% endif -%} + {%- endfor -%} + ) = 0; + }; + {% endif %} + virtual void {{command.name}}(ErrorString* + {%- for parameter in command.parameters -%} + {%- if "optional" in parameter -%} + , const Maybe<{{resolve_type(parameter).raw_type}}>& in_{{parameter.name}} + {%- else -%} + , {{resolve_type(parameter).pass_type}} in_{{parameter.name}} + {%- endif -%} + {%- endfor -%} + {%- if "async" in command -%} + , std::unique_ptr<{{command.name | to_title_case}}Callback> callback + {%- else -%} + {%- for parameter in command.returns -%} + {%- if "optional" in parameter -%} + , Maybe<{{resolve_type(parameter).raw_type}}>* out_{{parameter.name}} + {%- else -%} + , {{resolve_type(parameter).type}}* out_{{parameter.name}} + {%- endif -%} + {%- endfor -%} + {%- endif -%} + ) = 0; + {% endfor %} + + protected: + virtual ~{{domain.domain}}() { } + }; +{% endfor %} +}; + +} // namespace protocol +} // namespace blink + +#endif // !defined({{class_name}}_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/CodeGenerator.py b/deps/v8_inspector/platform/inspector_protocol/CodeGenerator.py new file mode 100644 index 00000000000000..5107feb680860a --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/CodeGenerator.py @@ -0,0 +1,308 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import os.path +import sys +import string +import optparse +import re +try: + import json +except ImportError: + import simplejson as json + +# Path handling for libraries and templates +# Paths have to be normalized because Jinja uses the exact template path to +# determine the hash used in the cache filename, and we need a pre-caching step +# to be concurrency-safe. Use absolute path because __file__ is absolute if +# module is imported, and relative if executed directly. +# If paths differ between pre-caching and individual file compilation, the cache +# is regenerated, which causes a race condition and breaks concurrent build, +# since some compile processes will try to read the partially written cache. +module_path, module_filename = os.path.split(os.path.realpath(__file__)) +templates_dir = module_path +deps_dir = os.path.normpath(os.path.join( + module_path, os.pardir, os.pardir, 'deps')) + +sys.path.insert(1, os.path.join(deps_dir, "jinja2")) +sys.path.insert(1, os.path.join(deps_dir, "markupsafe")) +import jinja2 + +cmdline_parser = optparse.OptionParser() +cmdline_parser.add_option("--output_dir") +cmdline_parser.add_option("--generate_dispatcher") + +try: + arg_options, arg_values = cmdline_parser.parse_args() + if (len(arg_values) == 0): + raise Exception("At least one plain argument expected (found %s)" % len(arg_values)) + output_dirname = arg_options.output_dir + generate_dispatcher = arg_options.generate_dispatcher + if not output_dirname: + raise Exception("Output directory must be specified") +except Exception: + # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html + exc = sys.exc_info()[1] + sys.stderr.write("Failed to parse command-line arguments: %s\n\n" % exc) + sys.stderr.write("Usage: <script> --output_dir <output_dir> blink_protocol.json v8_protocol.json ...\n") + exit(1) + +json_api = {"domains": []} + +json_timestamp = 0 + +for filename in arg_values: + json_timestamp = max(os.path.getmtime(filename), json_timestamp) + input_file = open(filename, "r") + json_string = input_file.read() + parsed_json = json.loads(json_string) + json_api["domains"] += parsed_json["domains"] + +def to_title_case(name): + return name[:1].upper() + name[1:] + + +def dash_to_camelcase(word): + return ''.join(to_title_case(x) or '-' for x in word.split('-')) + + +def initialize_jinja_env(cache_dir): + jinja_env = jinja2.Environment( + loader=jinja2.FileSystemLoader(templates_dir), + # Bytecode cache is not concurrency-safe unless pre-cached: + # if pre-cached this is read-only, but writing creates a race condition. + bytecode_cache=jinja2.FileSystemBytecodeCache(cache_dir), + keep_trailing_newline=True, # newline-terminate generated files + lstrip_blocks=True, # so can indent control flow tags + trim_blocks=True) + jinja_env.filters.update({"to_title_case": to_title_case, "dash_to_camelcase": dash_to_camelcase}) + jinja_env.add_extension('jinja2.ext.loopcontrols') + return jinja_env + + +def output_file(file_name): + return open(file_name, "w") + + +def patch_full_qualified_refs(): + def patch_full_qualified_refs_in_domain(json, domain_name): + if isinstance(json, list): + for item in json: + patch_full_qualified_refs_in_domain(item, domain_name) + + if not isinstance(json, dict): + return + for key in json: + if key == "type" and json[key] == "string": + json[key] = domain_name + ".string" + if key != "$ref": + patch_full_qualified_refs_in_domain(json[key], domain_name) + continue + if json["$ref"].find(".") == -1: + json["$ref"] = domain_name + "." + json["$ref"] + + for domain in json_api["domains"]: + patch_full_qualified_refs_in_domain(domain, domain["domain"]) + + +def create_user_type_definition(domain_name, type): + return { + "return_type": "std::unique_ptr<protocol::%s::%s>" % (domain_name, type["id"]), + "pass_type": "std::unique_ptr<protocol::%s::%s>" % (domain_name, type["id"]), + "to_raw_type": "%s.get()", + "to_pass_type": "std::move(%s)", + "to_rvalue": "std::move(%s)", + "type": "std::unique_ptr<protocol::%s::%s>" % (domain_name, type["id"]), + "raw_type": "protocol::%s::%s" % (domain_name, type["id"]), + "raw_pass_type": "protocol::%s::%s*" % (domain_name, type["id"]), + "raw_return_type": "protocol::%s::%s*" % (domain_name, type["id"]), + } + + +def create_object_type_definition(): + return { + "return_type": "std::unique_ptr<protocol::DictionaryValue>", + "pass_type": "std::unique_ptr<protocol::DictionaryValue>", + "to_raw_type": "%s.get()", + "to_pass_type": "std::move(%s)", + "to_rvalue": "std::move(%s)", + "type": "std::unique_ptr<protocol::DictionaryValue>", + "raw_type": "protocol::DictionaryValue", + "raw_pass_type": "protocol::DictionaryValue*", + "raw_return_type": "protocol::DictionaryValue*", + } + + +def create_any_type_definition(): + return { + "return_type": "std::unique_ptr<protocol::Value>", + "pass_type": "std::unique_ptr<protocol::Value>", + "to_raw_type": "%s.get()", + "to_pass_type": "std::move(%s)", + "to_rvalue": "std::move(%s)", + "type": "std::unique_ptr<protocol::Value>", + "raw_type": "protocol::Value", + "raw_pass_type": "protocol::Value*", + "raw_return_type": "protocol::Value*", + } + + +def create_string_type_definition(domain): + if domain in ["Runtime", "Debugger", "Profiler", "HeapProfiler"]: + return { + "return_type": "String16", + "pass_type": "const String16&", + "to_pass_type": "%s", + "to_raw_type": "%s", + "to_rvalue": "%s", + "type": "String16", + "raw_type": "String16", + "raw_pass_type": "const String16&", + "raw_return_type": "String16", + } + return { + "return_type": "String", + "pass_type": "const String&", + "to_pass_type": "%s", + "to_raw_type": "%s", + "to_rvalue": "%s", + "type": "String", + "raw_type": "String", + "raw_pass_type": "const String&", + "raw_return_type": "String", + } + + +def create_primitive_type_definition(type): + typedefs = { + "number": "double", + "integer": "int", + "boolean": "bool" + } + jsontypes = { + "number": "TypeNumber", + "integer": "TypeNumber", + "boolean": "TypeBoolean", + } + return { + "return_type": typedefs[type], + "pass_type": typedefs[type], + "to_pass_type": "%s", + "to_raw_type": "%s", + "to_rvalue": "%s", + "type": typedefs[type], + "raw_type": typedefs[type], + "raw_pass_type": typedefs[type], + "raw_return_type": typedefs[type], + } + +type_definitions = {} +type_definitions["number"] = create_primitive_type_definition("number") +type_definitions["integer"] = create_primitive_type_definition("integer") +type_definitions["boolean"] = create_primitive_type_definition("boolean") +type_definitions["object"] = create_object_type_definition() +type_definitions["any"] = create_any_type_definition() + +def wrap_array_definition(type): + return { + "return_type": "std::unique_ptr<protocol::Array<%s>>" % type["raw_type"], + "pass_type": "std::unique_ptr<protocol::Array<%s>>" % type["raw_type"], + "to_raw_type": "%s.get()", + "to_pass_type": "std::move(%s)", + "to_rvalue": "std::move(%s)", + "type": "std::unique_ptr<protocol::Array<%s>>" % type["raw_type"], + "raw_type": "protocol::Array<%s>" % type["raw_type"], + "raw_pass_type": "protocol::Array<%s>*" % type["raw_type"], + "raw_return_type": "protocol::Array<%s>*" % type["raw_type"], + "create_type": "wrapUnique(new protocol::Array<%s>())" % type["raw_type"], + "out_type": "protocol::Array<%s>&" % type["raw_type"], + } + + +def create_type_definitions(): + for domain in json_api["domains"]: + type_definitions[domain["domain"] + ".string"] = create_string_type_definition(domain["domain"]) + if not ("types" in domain): + continue + for type in domain["types"]: + if type["type"] == "object": + type_definitions[domain["domain"] + "." + type["id"]] = create_user_type_definition(domain["domain"], type) + elif type["type"] == "array": + items_type = type["items"]["type"] + type_definitions[domain["domain"] + "." + type["id"]] = wrap_array_definition(type_definitions[items_type]) + elif type["type"] == domain["domain"] + ".string": + type_definitions[domain["domain"] + "." + type["id"]] = create_string_type_definition(domain["domain"]) + else: + type_definitions[domain["domain"] + "." + type["id"]] = create_primitive_type_definition(type["type"]) + +patch_full_qualified_refs() +create_type_definitions() + + +def type_definition(name): + return type_definitions[name] + + +def resolve_type(property): + if "$ref" in property: + return type_definitions[property["$ref"]] + if property["type"] == "array": + return wrap_array_definition(resolve_type(property["items"])) + return type_definitions[property["type"]] + + +def join_arrays(dict, keys): + result = [] + for key in keys: + if key in dict: + result += dict[key] + return result + + +if os.path.exists(__file__): + current_script_timestamp = os.path.getmtime(__file__) +else: + current_script_timestamp = 0 + + +def is_up_to_date(file, template): + if not os.path.exists(file): + return False + timestamp = os.path.getmtime(file) + return timestamp > max(os.path.getmtime(module_path + template), + current_script_timestamp, json_timestamp) + + +def generate(class_name): + h_template_name = "/%s_h.template" % class_name + cpp_template_name = "/%s_cpp.template" % class_name + h_file_name = output_dirname + "/" + class_name + ".h" + cpp_file_name = output_dirname + "/" + class_name + ".cpp" + + if (is_up_to_date(cpp_file_name, cpp_template_name) and + is_up_to_date(h_file_name, h_template_name)): + return + + template_context = { + "class_name": class_name, + "api": json_api, + "join_arrays": join_arrays, + "resolve_type": resolve_type, + "type_definition": type_definition + } + h_template = jinja_env.get_template(h_template_name) + cpp_template = jinja_env.get_template(cpp_template_name) + h_file = output_file(h_file_name) + cpp_file = output_file(cpp_file_name) + h_file.write(h_template.render(template_context)) + cpp_file.write(cpp_template.render(template_context)) + h_file.close() + cpp_file.close() + + +jinja_env = initialize_jinja_env(output_dirname) +generate("Backend") +generate("Dispatcher") +generate("Frontend") +generate("TypeBuilder") diff --git a/deps/v8_inspector/platform/inspector_protocol/Collections.h b/deps/v8_inspector/platform/inspector_protocol/Collections.h new file mode 100644 index 00000000000000..960c747bae4c65 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/Collections.h @@ -0,0 +1,14 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef Collections_h +#define Collections_h + +#if V8_INSPECTOR_USE_STL +#include "platform/inspector_protocol/CollectionsSTL.h" +#else +#include "platform/inspector_protocol/CollectionsWTF.h" +#endif // V8_INSPECTOR_USE_STL + +#endif // !defined(Collections_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/CollectionsSTL.h b/deps/v8_inspector/platform/inspector_protocol/CollectionsSTL.h new file mode 100644 index 00000000000000..09a1d399716f36 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/CollectionsSTL.h @@ -0,0 +1,253 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CollectionsSTL_h +#define CollectionsSTL_h + +#include "platform/inspector_protocol/String16.h" +#include "wtf/Compiler.h" +#include "wtf/PtrUtil.h" + +#include <algorithm> +#include <map> +#include <vector> + +namespace blink { +namespace protocol { + +template <typename T> +class Vector { +public: + Vector() { } + Vector(size_t capacity) : m_impl(capacity) { } + typedef typename std::vector<T>::iterator iterator; + typedef typename std::vector<T>::const_iterator const_iterator; + + iterator begin() { return m_impl.begin(); } + iterator end() { return m_impl.end(); } + const_iterator begin() const { return m_impl.begin(); } + const_iterator end() const { return m_impl.end(); } + + void resize(size_t s) { m_impl.resize(s); } + size_t size() const { return m_impl.size(); } + bool isEmpty() const { return !m_impl.size(); } + T& operator[](size_t i) { return at(i); } + const T& operator[](size_t i) const { return at(i); } + T& at(size_t i) { return m_impl[i]; } + const T& at(size_t i) const { return m_impl.at(i); } + T& last() { return m_impl[m_impl.size() - 1]; } + const T& last() const { return m_impl[m_impl.size() - 1]; } + void append(const T& t) { m_impl.push_back(t); } + void prepend(const T& t) { m_impl.insert(m_impl.begin(), t); } + void remove(size_t i) { m_impl.erase(m_impl.begin() + i); } + void clear() { m_impl.clear(); } + void swap(Vector& other) { m_impl.swap(other.m_impl); } + void removeLast() { m_impl.pop_back(); } + +private: + std::vector<T> m_impl; +}; + +template <typename T> +class Vector<std::unique_ptr<T>> { +public: + Vector() { } + Vector(size_t capacity) : m_impl(capacity) { } + Vector(Vector&& other) { m_impl.swap(other.m_impl); } + ~Vector() { clear(); } + + typedef typename std::vector<T*>::iterator iterator; + typedef typename std::vector<T*>::const_iterator const_iterator; + + iterator begin() { return m_impl.begin(); } + iterator end() { return m_impl.end(); } + const_iterator begin() const { return m_impl.begin(); } + const_iterator end() const { return m_impl.end(); } + + void resize(size_t s) { m_impl.resize(s); } + size_t size() const { return m_impl.size(); } + bool isEmpty() const { return !m_impl.size(); } + T* operator[](size_t i) { return at(i); } + const T* operator[](size_t i) const { return at(i); } + T* at(size_t i) { return m_impl[i]; } + const T* at(size_t i) const { return m_impl.at(i); } + T* last() { return m_impl[m_impl.size() - 1]; } + const T* last() const { return m_impl[m_impl.size() - 1]; } + void append(std::unique_ptr<T> t) { m_impl.push_back(t.release()); } + void prepend(std::unique_ptr<T> t) { m_impl.insert(m_impl.begin(), t.release()); } + + void remove(size_t i) + { + delete m_impl[i]; + m_impl.erase(m_impl.begin() + i); + } + + void clear() + { + for (auto t : m_impl) + delete t; + m_impl.clear(); + } + + void swap(Vector& other) { m_impl.swap(other.m_impl); } + void swap(Vector&& other) { m_impl.swap(other.m_impl); } + void removeLast() + { + delete last(); + m_impl.pop_back(); + } + +private: + Vector(const Vector&) = delete; + Vector& operator=(const Vector&) = delete; + std::vector<T*> m_impl; +}; + +template <typename K, typename V, typename I> +class HashMapIterator { +public: + HashMapIterator(const I& impl) : m_impl(impl) { } + std::pair<K, V*>* get() const { m_pair.first = m_impl->first; m_pair.second = &m_impl->second; return &m_pair; } + std::pair<K, V*>& operator*() const { return *get(); } + std::pair<K, V*>* operator->() const { return get(); } + + bool operator==(const HashMapIterator<K, V, I>& other) const { return m_impl == other.m_impl; } + bool operator!=(const HashMapIterator<K, V, I>& other) const { return m_impl != other.m_impl; } + + HashMapIterator<K, V, I>& operator++() { ++m_impl; return *this; } + +private: + mutable std::pair<K, V*> m_pair; + I m_impl; +}; + +template <typename K, typename V, typename I> +class HashMapIterator<K, std::unique_ptr<V>, I> { +public: + HashMapIterator(const I& impl) : m_impl(impl) { } + std::pair<K, V*>* get() const { m_pair.first = m_impl->first; m_pair.second = m_impl->second; return &m_pair; } + std::pair<K, V*>& operator*() const { return *get(); } + std::pair<K, V*>* operator->() const { return get(); } + + bool operator==(const HashMapIterator<K, std::unique_ptr<V>, I>& other) const { return m_impl == other.m_impl; } + bool operator!=(const HashMapIterator<K, std::unique_ptr<V>, I>& other) const { return m_impl != other.m_impl; } + + HashMapIterator<K, std::unique_ptr<V>, I>& operator++() { ++m_impl; return *this; } + +private: + mutable std::pair<K, V*> m_pair; + I m_impl; +}; + +template <typename K, typename V> +class HashMap { +public: + HashMap() { } + ~HashMap() { } + + using iterator = HashMapIterator<K, V, typename std::map<K, V>::iterator>; + using const_iterator = HashMapIterator<K, const V, typename std::map<K, V>::const_iterator>; + + iterator begin() { return iterator(m_impl.begin()); } + iterator end() { return iterator(m_impl.end()); } + iterator find(const K& k) { return iterator(m_impl.find(k)); } + const_iterator begin() const { return const_iterator(m_impl.begin()); } + const_iterator end() const { return const_iterator(m_impl.end()); } + const_iterator find(const K& k) const { return const_iterator(m_impl.find(k)); } + + size_t size() const { return m_impl.size(); } + bool isEmpty() const { return !m_impl.size(); } + bool set(const K& k, const V& v) + { + bool isNew = m_impl.find(k) == m_impl.end(); + m_impl[k] = v; + return isNew; + } + bool contains(const K& k) const { return m_impl.find(k) != m_impl.end(); } + V get(const K& k) const { auto it = m_impl.find(k); return it == m_impl.end() ? V() : it->second; } + void remove(const K& k) { m_impl.erase(k); } + void clear() { m_impl.clear(); } + V take(const K& k) + { + V result = m_impl[k]; + m_impl.erase(k); + return result; + } + +private: + std::map<K, V> m_impl; +}; + +template <typename K, typename V> +class HashMap<K, std::unique_ptr<V>> { +public: + HashMap() { } + ~HashMap() { clear(); } + + using iterator = HashMapIterator<K, std::unique_ptr<V>, typename std::map<K, V*>::iterator>; + using const_iterator = HashMapIterator<K, std::unique_ptr<V>, typename std::map<K, V*>::const_iterator>; + + iterator begin() { return iterator(m_impl.begin()); } + iterator end() { return iterator(m_impl.end()); } + iterator find(const K& k) { return iterator(m_impl.find(k)); } + const_iterator begin() const { return const_iterator(m_impl.begin()); } + const_iterator end() const { return const_iterator(m_impl.end()); } + const_iterator find(const K& k) const { return const_iterator(m_impl.find(k)); } + + size_t size() const { return m_impl.size(); } + bool isEmpty() const { return !m_impl.size(); } + bool set(const K& k, std::unique_ptr<V> v) + { + bool isNew = m_impl.find(k) == m_impl.end(); + if (!isNew) + delete m_impl[k]; + m_impl[k] = v.release(); + return isNew; + } + bool contains(const K& k) const { return m_impl.find(k) != m_impl.end(); } + V* get(const K& k) const { auto it = m_impl.find(k); return it == m_impl.end() ? nullptr : it->second; } + std::unique_ptr<V> take(const K& k) + { + if (!contains(k)) + return nullptr; + std::unique_ptr<V> result(m_impl[k]); + delete m_impl[k]; + m_impl.erase(k); + return result; + } + void remove(const K& k) + { + delete m_impl[k]; + m_impl.erase(k); + } + + void clear() + { + for (auto pair : m_impl) + delete pair.second; + m_impl.clear(); + } + +private: + std::map<K, V*> m_impl; +}; + +template <typename K> +class HashSet : public protocol::HashMap<K, K> { +public: + void add(const K& k) { this->set(k, k); } +}; + +} // namespace platform +} // namespace blink + +// Macro that returns a compile time constant with the length of an array, but gives an error if passed a non-array. +template<typename T, size_t Size> char (&ArrayLengthHelperFunction(T (&)[Size]))[Size]; +// GCC needs some help to deduce a 0 length array. +#if COMPILER(GCC) +template<typename T> char (&ArrayLengthHelperFunction(T (&)[0]))[0]; +#endif +#define PROTOCOL_ARRAY_LENGTH(array) sizeof(::ArrayLengthHelperFunction(array)) + +#endif // !defined(CollectionsSTL_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/CollectionsWTF.h b/deps/v8_inspector/platform/inspector_protocol/CollectionsWTF.h new file mode 100644 index 00000000000000..4642c610b84217 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/CollectionsWTF.h @@ -0,0 +1,193 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CollectionsWTF_h +#define CollectionsWTF_h + +#include "wtf/Assertions.h" +#include "wtf/HashMap.h" +#include "wtf/PtrUtil.h" +#include "wtf/Vector.h" +#include "wtf/VectorTraits.h" + +namespace blink { +namespace protocol { + +template <typename T> +class Vector { +public: + Vector() { } + Vector(size_t capacity) : m_impl(capacity) { } + typedef T* iterator; + typedef const T* const_iterator; + + iterator begin() { return m_impl.begin(); } + iterator end() { return m_impl.end(); } + const_iterator begin() const { return m_impl.begin(); } + const_iterator end() const { return m_impl.end(); } + + void resize(size_t s) { m_impl.resize(s); } + size_t size() const { return m_impl.size(); } + bool isEmpty() const { return m_impl.isEmpty(); } + T& operator[](size_t i) { return at(i); } + const T& operator[](size_t i) const { return at(i); } + T& at(size_t i) { return m_impl.at(i); } + const T& at(size_t i) const { return m_impl.at(i); } + T& last() { return m_impl.last(); } + const T& last() const { return m_impl.last(); } + void append(const T& t) { m_impl.append(t); } + void prepend(const T& t) { m_impl.prepend(t); } + void remove(size_t i) { m_impl.remove(i); } + void clear() { m_impl.clear(); } + void swap(Vector<T>& other) { m_impl.swap(other.m_impl); } + void removeLast() { m_impl.removeLast(); } + +private: + WTF::Vector<T> m_impl; +}; + +template <typename T> +class Vector<std::unique_ptr<T>> { + WTF_MAKE_NONCOPYABLE(Vector); +public: + Vector() { } + Vector(size_t capacity) : m_impl(capacity) { } + Vector(Vector<std::unique_ptr<T>>&& other) : m_impl(std::move(other.m_impl)) { } + ~Vector() { } + + typedef std::unique_ptr<T>* iterator; + typedef const std::unique_ptr<T>* const_iterator; + + iterator begin() { return m_impl.begin(); } + iterator end() { return m_impl.end(); } + const_iterator begin() const { return m_impl.begin(); } + const_iterator end() const { return m_impl.end(); } + + void resize(size_t s) { m_impl.resize(s); } + size_t size() const { return m_impl.size(); } + bool isEmpty() const { return m_impl.isEmpty(); } + std::unique_ptr<T>& operator[](size_t i) { return m_impl.at(i); } + const std::unique_ptr<T>& operator[](size_t i) const { return m_impl.at(i); } + std::unique_ptr<T>& at(size_t i) { return m_impl.at(i); } + const std::unique_ptr<T>& at(size_t i) const { return m_impl.at(i); } + std::unique_ptr<T>& last() { return m_impl.last(); } + const std::unique_ptr<T>& last() const { return m_impl.last(); } + void append(std::unique_ptr<T> t) { m_impl.append(std::move(t)); } + void prepend(std::unique_ptr<T> t) { m_impl.prepend(std::move(t)); } + void remove(size_t i) { m_impl.remove(i); } + void clear() { m_impl.clear(); } + void swap(Vector<std::unique_ptr<T>>& other) { m_impl.swap(other.m_impl); } + void swap(Vector<std::unique_ptr<T>>&& other) { m_impl.swap(other.m_impl); } + void removeLast() { m_impl.removeLast(); } + +private: + WTF::Vector<std::unique_ptr<T>> m_impl; +}; + +template <typename K, typename V, typename I> +class HashMapIterator { + STACK_ALLOCATED(); +public: + HashMapIterator(const I& impl) : m_impl(impl) { } + std::pair<K, V*>* get() const { m_pair = std::make_pair(m_impl->key, &m_impl->value); return &m_pair; } + std::pair<K, V*>& operator*() const { return *get(); } + std::pair<K, V*>* operator->() const { return get(); } + + bool operator==(const HashMapIterator<K, V, I>& other) const { return m_impl == other.m_impl; } + bool operator!=(const HashMapIterator<K, V, I>& other) const { return m_impl != other.m_impl; } + + HashMapIterator<K, V, I>& operator++() { ++m_impl; return *this; } + +private: + mutable std::pair<K, V*> m_pair; + I m_impl; +}; + +template <typename K, typename V, typename I> +class HashMapIterator<K, std::unique_ptr<V>, I> { + STACK_ALLOCATED(); +public: + HashMapIterator(const I& impl) : m_impl(impl) { } + std::pair<K, V*>* get() const { m_pair = std::make_pair(m_impl->key, m_impl->value.get()); return &m_pair; } + std::pair<K, V*>& operator*() const { return *get(); } + std::pair<K, V*>* operator->() const { return get(); } + + bool operator==(const HashMapIterator<K, std::unique_ptr<V>, I>& other) const { return m_impl == other.m_impl; } + bool operator!=(const HashMapIterator<K, std::unique_ptr<V>, I>& other) const { return m_impl != other.m_impl; } + + HashMapIterator<K, std::unique_ptr<V>, I>& operator++() { ++m_impl; return *this; } + +private: + mutable std::pair<K, V*> m_pair; + I m_impl; +}; + +template <typename K, typename V> +class HashMap { +public: + HashMap() { } + ~HashMap() { } + + using iterator = HashMapIterator<K, V, typename WTF::HashMap<K, V>::iterator>; + using const_iterator = HashMapIterator<K, const V, typename WTF::HashMap<K, V>::const_iterator>; + + iterator begin() { return iterator(m_impl.begin()); } + iterator end() { return iterator(m_impl.end()); } + iterator find(const K& k) { return iterator(m_impl.find(k)); } + const_iterator begin() const { return const_iterator(m_impl.begin()); } + const_iterator end() const { return const_iterator(m_impl.end()); } + const_iterator find(const K& k) const { return const_iterator(m_impl.find(k)); } + + size_t size() const { return m_impl.size(); } + bool isEmpty() const { return m_impl.isEmpty(); } + bool set(const K& k, const V& v) { return m_impl.set(k, v).isNewEntry; } + bool contains(const K& k) const { return m_impl.contains(k); } + V get(const K& k) const { return m_impl.get(k); } + void remove(const K& k) { m_impl.remove(k); } + void clear() { m_impl.clear(); } + V take(const K& k) { return m_impl.take(k); } + +private: + WTF::HashMap<K, V> m_impl; +}; + +template <typename K, typename V> +class HashMap<K, std::unique_ptr<V>> { +public: + HashMap() { } + ~HashMap() { } + + using iterator = HashMapIterator<K, std::unique_ptr<V>, typename WTF::HashMap<K, std::unique_ptr<V>>::iterator>; + using const_iterator = HashMapIterator<K, std::unique_ptr<V>, typename WTF::HashMap<K, std::unique_ptr<V>>::const_iterator>; + + iterator begin() { return iterator(m_impl.begin()); } + iterator end() { return iterator(m_impl.end()); } + iterator find(const K& k) { return iterator(m_impl.find(k)); } + const_iterator begin() const { return const_iterator(m_impl.begin()); } + const_iterator end() const { return const_iterator(m_impl.end()); } + const_iterator find(const K& k) const { return const_iterator(m_impl.find(k)); } + + size_t size() const { return m_impl.size(); } + bool isEmpty() const { return m_impl.isEmpty(); } + bool set(const K& k, std::unique_ptr<V> v) { return m_impl.set(k, std::move(v)).isNewEntry; } + bool contains(const K& k) const { return m_impl.contains(k); } + V* get(const K& k) const { return m_impl.get(k); } + std::unique_ptr<V> take(const K& k) { return m_impl.take(k); } + void remove(const K& k) { m_impl.remove(k); } + void clear() { m_impl.clear(); } + +private: + WTF::HashMap<K, std::unique_ptr<V>> m_impl; +}; + +template <typename K> +class HashSet : public protocol::HashMap<K, K> { +public: + void add(const K& k) { this->set(k, k); } +}; + +} // namespace platform +} // namespace blink + +#endif // !defined(CollectionsWTF_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/Dispatcher_cpp.template b/deps/v8_inspector/platform/inspector_protocol/Dispatcher_cpp.template new file mode 100644 index 00000000000000..bb7c17b5eed33b --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/Dispatcher_cpp.template @@ -0,0 +1,365 @@ +// This file is generated + +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/inspector_protocol/{{class_name}}.h" + +#include "platform/inspector_protocol/Collections.h" +#include "platform/inspector_protocol/FrontendChannel.h" +#include "platform/inspector_protocol/Parser.h" + +namespace blink { +namespace protocol { + +using protocol::Maybe; + +class DispatcherImpl; + +class DispatcherImplWeakPtr { +public: + DispatcherImplWeakPtr(DispatcherImpl* dispatcher) : m_dispatcher(dispatcher) { } + ~DispatcherImplWeakPtr(); + DispatcherImpl* get() { return m_dispatcher; } + void dispose() { m_dispatcher = nullptr; } +private: + DispatcherImpl* m_dispatcher; +}; + +class DispatcherImpl : public Dispatcher { +public: + DispatcherImpl(FrontendChannel* frontendChannel) + : m_frontendChannel(frontendChannel) +{% for domain in api.domains %} + , m_{{domain.domain | lower}}Agent(0) +{% endfor %} + { +{% for domain in api.domains %} + {% for command in domain.commands %} + {% if "redirect" in command %}{% continue %}{% endif %} + {% if "handlers" in command and not ("renderer" in command["handlers"]) %}{% continue %}{% endif %} + m_dispatchMap.set("{{domain.domain}}.{{command.name}}", &DispatcherImpl::{{domain.domain}}_{{command.name}}); + {% endfor %} +{% endfor %} + + // Initialize common errors. + m_commonErrors.resize(LastEntry); + m_commonErrors[ParseError] = -32700; + m_commonErrors[InvalidRequest] = -32600; + m_commonErrors[MethodNotFound] = -32601; + m_commonErrors[InvalidParams] = -32602; + m_commonErrors[InternalError] = -32603; + m_commonErrors[ServerError] = -32000; + } + + ~DispatcherImpl() { clearFrontend(); } + + virtual void clearFrontend() + { + m_frontendChannel = nullptr; + for (auto& weak : m_weakPtrs) + weak.first->dispose(); + m_weakPtrs.clear(); + } + + std::unique_ptr<DispatcherImplWeakPtr> weakPtr() + { + std::unique_ptr<DispatcherImplWeakPtr> weak(new DispatcherImplWeakPtr(this)); + m_weakPtrs.add(weak.get()); + return weak; + } + + virtual void dispatch(int sessionId, const String16& message); + virtual void reportProtocolError(int sessionId, int callId, CommonErrorCode, const String16& errorMessage, ErrorSupport* errors) const; + using Dispatcher::reportProtocolError; + + void sendResponse(int sessionId, int callId, const ErrorString& invocationError, ErrorSupport* errors, std::unique_ptr<protocol::DictionaryValue> result); + +{% for domain in api.domains %} + virtual void registerAgent(blink::protocol::Backend::{{domain.domain}}* agent) { DCHECK(!m_{{domain.domain | lower}}Agent); m_{{domain.domain | lower}}Agent = agent; } +{% endfor %} + +private: + friend class DispatcherCallbackBase; + friend class DispatcherImplWeakPtr; + using CallHandler = void (DispatcherImpl::*)(int sessionId, int callId, std::unique_ptr<DictionaryValue> messageObject, ErrorSupport* errors); + using DispatchMap = protocol::HashMap<String16, CallHandler>; + +{% for domain in api.domains %} + {% for command in domain.commands %} + {% if "redirect" in command %}{% continue %}{% endif %} + {% if "handlers" in command and not ("renderer" in command["handlers"]) %}{% continue %}{% endif %} + void {{domain.domain}}_{{command.name}}(int sessionId, int callId, std::unique_ptr<DictionaryValue> requestMessageObject, ErrorSupport*); + {% endfor %} +{% endfor %} + + FrontendChannel* m_frontendChannel; + +{% for domain in api.domains %} + Backend::{{domain.domain}}* m_{{domain.domain | lower}}Agent; +{% endfor %} + + void sendResponse(int sessionId, int callId, ErrorString invocationError, std::unique_ptr<protocol::DictionaryValue> result) + { + sendResponse(sessionId, callId, invocationError, nullptr, std::move(result)); + } + + void sendResponse(int sessionId, int callId, ErrorString invocationError) + { + sendResponse(sessionId, callId, invocationError, nullptr, DictionaryValue::create()); + } + + static const char kInvalidRequest[]; + + DispatchMap m_dispatchMap; + protocol::Vector<int> m_commonErrors; + protocol::HashSet<DispatcherImplWeakPtr*> m_weakPtrs; +}; + +class PLATFORM_EXPORT DispatcherCallbackBase : public protocol::Backend::CallbackBase { +public: + DispatcherCallbackBase(std::unique_ptr<DispatcherImplWeakPtr> backendImpl, int sessionId, int id) + : m_backendImpl(std::move(backendImpl)), m_sessionId(sessionId), m_id(id) { } + virtual ~DispatcherCallbackBase() { } + void dispose() { m_backendImpl = nullptr; } + +protected: + void sendIfActive(std::unique_ptr<protocol::DictionaryValue> partialMessage, const ErrorString& invocationError) + { + if (!m_backendImpl->get()) + return; + m_backendImpl->get()->sendResponse(m_sessionId, m_id, invocationError, nullptr, std::move(partialMessage)); + m_backendImpl = nullptr; + } + +private: + std::unique_ptr<DispatcherImplWeakPtr> m_backendImpl; + int m_sessionId; + int m_id; +}; + +DispatcherImplWeakPtr::~DispatcherImplWeakPtr() +{ + if (m_dispatcher) + m_dispatcher->m_weakPtrs.remove(this); +} + +const char DispatcherImpl::kInvalidRequest[] = "Invalid request"; + +{% for domain in api.domains %} + {% for command in domain.commands %} + {% if "redirect" in command %}{% continue %}{% endif %} + {% if "handlers" in command and not ("renderer" in command["handlers"]) %}{% continue %}{% endif %} + + {% if "async" in command %} + +class PLATFORM_EXPORT {{domain.domain}}{{command.name | to_title_case}}Callback : public Backend::{{domain.domain}}::{{command.name | to_title_case}}Callback, public DispatcherCallbackBase { +public: + {{domain.domain}}{{command.name | to_title_case}}Callback(std::unique_ptr<DispatcherImplWeakPtr> backendImpl, int sessionId, int id) + : DispatcherCallbackBase(std::move(backendImpl), sessionId, id) { } + + void sendSuccess( + {%- for parameter in command.returns -%} + {%- if "optional" in parameter -%} + const Maybe<{{resolve_type(parameter).raw_type}}>& {{parameter.name}} + {%- else -%} + {{resolve_type(parameter).pass_type}} {{parameter.name}} + {%- endif -%} + {%- if not loop.last -%}, {% endif -%} + {%- endfor -%}) override + { + std::unique_ptr<protocol::DictionaryValue> resultObject = DictionaryValue::create(); + {% for parameter in command.returns %} + {% if "optional" in parameter %} + if ({{parameter.name}}.isJust()) + resultObject->setValue("{{parameter.name}}", toValue({{parameter.name}}.fromJust())); + {% else %} + resultObject->setValue("{{parameter.name}}", toValue({{resolve_type(parameter).to_raw_type % parameter.name}})); + {% endif %} + {% endfor %} + sendIfActive(std::move(resultObject), ErrorString()); + } + + void sendFailure(const ErrorString& error) override + { + DCHECK(error.length()); + sendIfActive(nullptr, error); + } +}; + {% endif %} + +void DispatcherImpl::{{domain.domain}}_{{command.name}}(int sessionId, int callId, std::unique_ptr<DictionaryValue> requestMessageObject, ErrorSupport* errors) +{ + if (!m_{{domain.domain | lower}}Agent) + errors->addError("{{domain.domain}} handler is not available."); + + if (errors->hasErrors()) { + reportProtocolError(sessionId, callId, InvalidParams, kInvalidRequest, errors); + return; + } + {% if "parameters" in command %} + + // Prepare input parameters. + protocol::DictionaryValue* object = DictionaryValue::cast(requestMessageObject->get("params")); + errors->push(); + {% for property in command.parameters %} + protocol::Value* {{property.name}}Value = object ? object->get("{{property.name}}") : nullptr; + {% if property.optional %} + Maybe<{{resolve_type(property).raw_type}}> in_{{property.name}}; + if ({{property.name}}Value) { + errors->setName("{{property.name}}"); + in_{{property.name}} = FromValue<{{resolve_type(property).raw_type}}>::parse({{property.name}}Value, errors); + } + {% else %} + errors->setName("{{property.name}}"); + {{resolve_type(property).type}} in_{{property.name}} = FromValue<{{resolve_type(property).raw_type}}>::parse({{property.name}}Value, errors); + {% endif %} + {% endfor %} + errors->pop(); + if (errors->hasErrors()) { + reportProtocolError(sessionId, callId, InvalidParams, kInvalidRequest, errors); + return; + } + {% endif %} + + {% if "async" in command %} + std::unique_ptr<{{domain.domain}}{{command.name | to_title_case}}Callback> callback(new {{domain.domain}}{{command.name | to_title_case}}Callback(weakPtr(), sessionId, callId)); + {% elif "returns" in command %} + // Declare output parameters. + std::unique_ptr<protocol::DictionaryValue> result = DictionaryValue::create(); + {% for property in command.returns %} + {% if "optional" in property %} + Maybe<{{resolve_type(property).raw_type}}> out_{{property.name}}; + {% else %} + {{resolve_type(property).type}} out_{{property.name}}; + {% endif %} + {% endfor %} + {% endif %} + + std::unique_ptr<DispatcherImplWeakPtr> weak = weakPtr(); + ErrorString error; + m_{{domain.domain | lower}}Agent->{{command.name}}(&error + {%- for property in command.parameters -%} + {%- if "optional" in property -%} + , in_{{property.name}} + {%- else -%} + , {{resolve_type(property).to_pass_type % ("in_" + property.name)}} + {%- endif -%} + {%- endfor %} + {%- if "async" in command -%} + , std::move(callback) + {%- elif "returns" in command %} + {%- for property in command.returns -%} + , &out_{{property.name}} + {%- endfor %} + {% endif %}); + {% if "returns" in command and not("async" in command) %} + if (!error.length()) { + {% for parameter in command.returns %} + {% if "optional" in parameter %} + if (out_{{parameter.name}}.isJust()) + result->setValue("{{parameter.name}}", toValue(out_{{parameter.name}}.fromJust())); + {% else %} + result->setValue("{{parameter.name}}", toValue({{resolve_type(parameter).to_raw_type % ("out_" + parameter.name)}})); + {% endif %} + {% endfor %} + } + if (weak->get()) + weak->get()->sendResponse(sessionId, callId, error, std::move(result)); + {% elif not("async" in command) %} + if (weak->get()) + weak->get()->sendResponse(sessionId, callId, error); + {% endif %} +} + {% endfor %} +{% endfor %} + +std::unique_ptr<Dispatcher> Dispatcher::create(FrontendChannel* frontendChannel) +{ + return wrapUnique(new DispatcherImpl(frontendChannel)); +} + +void DispatcherImpl::dispatch(int sessionId, const String16& message) +{ + int callId = 0; + std::unique_ptr<protocol::Value> parsedMessage = parseJSON(message); + DCHECK(parsedMessage); + std::unique_ptr<protocol::DictionaryValue> messageObject = DictionaryValue::cast(std::move(parsedMessage)); + DCHECK(messageObject); + + protocol::Value* callIdValue = messageObject->get("id"); + bool success = callIdValue->asNumber(&callId); + DCHECK(success); + + protocol::Value* methodValue = messageObject->get("method"); + String16 method; + success = methodValue && methodValue->asString(&method); + DCHECK(success); + + protocol::HashMap<String16, CallHandler>::iterator it = m_dispatchMap.find(method); + if (it == m_dispatchMap.end()) { + reportProtocolError(sessionId, callId, MethodNotFound, "'" + method + "' wasn't found"); + return; + } + + protocol::ErrorSupport errors; + ((*this).*(*it->second))(sessionId, callId, std::move(messageObject), &errors); +} + +void DispatcherImpl::sendResponse(int sessionId, int callId, const ErrorString& invocationError, ErrorSupport* errors, std::unique_ptr<protocol::DictionaryValue> result) +{ + if (invocationError.length() || (errors && errors->hasErrors())) { + reportProtocolError(sessionId, callId, ServerError, invocationError, errors); + return; + } + + std::unique_ptr<protocol::DictionaryValue> responseMessage = DictionaryValue::create(); + responseMessage->setNumber("id", callId); + responseMessage->setObject("result", std::move(result)); + if (m_frontendChannel) + m_frontendChannel->sendProtocolResponse(sessionId, callId, std::move(responseMessage)); +} + +void Dispatcher::reportProtocolError(int sessionId, int callId, CommonErrorCode code, const String16& errorMessage) const +{ + ErrorSupport errors; + reportProtocolError(sessionId, callId, code, errorMessage, &errors); +} + +void DispatcherImpl::reportProtocolError(int sessionId, int callId, CommonErrorCode code, const String16& errorMessage, ErrorSupport* errors) const +{ + DCHECK(code >=0); + DCHECK((unsigned)code < m_commonErrors.size()); + DCHECK(m_commonErrors[code]); + std::unique_ptr<protocol::DictionaryValue> error = DictionaryValue::create(); + error->setNumber("code", m_commonErrors[code]); + error->setString("message", errorMessage); + DCHECK(error); + if (errors && errors->hasErrors()) + error->setString("data", errors->errors()); + std::unique_ptr<protocol::DictionaryValue> message = DictionaryValue::create(); + message->setObject("error", std::move(error)); + message->setNumber("id", callId); + if (m_frontendChannel) + m_frontendChannel->sendProtocolResponse(sessionId, callId, std::move(message)); +} + +bool Dispatcher::getCommandName(const String16& message, String16* result) +{ + std::unique_ptr<protocol::Value> value = parseJSON(message); + if (!value) + return false; + + protocol::DictionaryValue* object = DictionaryValue::cast(value.get()); + if (!object) + return false; + + if (!object->getString("method", result)) + return false; + + return true; +} + +} // namespace protocol +} // namespace blink diff --git a/deps/v8_inspector/platform/inspector_protocol/Dispatcher_h.template b/deps/v8_inspector/platform/inspector_protocol/Dispatcher_h.template new file mode 100644 index 00000000000000..6a809bbff0742a --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/Dispatcher_h.template @@ -0,0 +1,65 @@ +// This file is generated + +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef {{class_name}}_h +#define {{class_name}}_h + +#include "platform/inspector_protocol/Backend.h" +#include "platform/inspector_protocol/TypeBuilder.h" + +namespace blink { +namespace protocol { + +class FrontendChannel; +class DispatcherImplWeakPtr; + +class PLATFORM_EXPORT Dispatcher { +public: + static std::unique_ptr<Dispatcher> create(FrontendChannel* frontendChannel); + virtual ~Dispatcher() { } + + class PLATFORM_EXPORT CallbackBase { + public: + CallbackBase(std::unique_ptr<DispatcherImplWeakPtr> backendImpl, int sessionId, int id); + virtual ~CallbackBase(); + void sendFailure(const ErrorString&); + void dispose(); + + protected: + void sendIfActive(std::unique_ptr<protocol::DictionaryValue> partialMessage, const ErrorString& invocationError); + + private: + std::unique_ptr<DispatcherImplWeakPtr> m_backendImpl; + int m_sessionId; + int m_id; + }; + +{% for domain in api.domains %} + virtual void registerAgent(blink::protocol::Backend::{{domain.domain}}*) = 0; +{% endfor %} + + virtual void clearFrontend() = 0; + + enum CommonErrorCode { + ParseError = 0, + InvalidRequest, + MethodNotFound, + InvalidParams, + InternalError, + ServerError, + LastEntry, + }; + + void reportProtocolError(int sessionId, int callId, CommonErrorCode, const String16& errorMessage) const; + virtual void reportProtocolError(int sessionId, int callId, CommonErrorCode, const String16& errorMessage, ErrorSupport*) const = 0; + virtual void dispatch(int sessionId, const String16& message) = 0; + static bool getCommandName(const String16& message, String16* result); +}; + +} // namespace protocol +} // namespace blink + +#endif // !defined({{class_name}}_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/ErrorSupport.cpp b/deps/v8_inspector/platform/inspector_protocol/ErrorSupport.cpp new file mode 100644 index 00000000000000..87603e980b6e9e --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/ErrorSupport.cpp @@ -0,0 +1,71 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/inspector_protocol/ErrorSupport.h" + +#include "platform/inspector_protocol/String16.h" +#include "wtf/Assertions.h" + +namespace blink { +namespace protocol { + +ErrorSupport::ErrorSupport() : m_errorString(nullptr) { } +ErrorSupport::ErrorSupport(String16* errorString) : m_errorString(errorString) { } +ErrorSupport::~ErrorSupport() +{ + if (m_errorString && hasErrors()) { + String16Builder builder; + builder.append("Internal error(s): "); + builder.append(errors()); + *m_errorString = builder.toString(); + } +} + +void ErrorSupport::setName(const String16& name) +{ + DCHECK(m_path.size()); + m_path[m_path.size() - 1] = name; +} + +void ErrorSupport::push() +{ + m_path.append(String16()); +} + +void ErrorSupport::pop() +{ + m_path.removeLast(); +} + +void ErrorSupport::addError(const String16& error) +{ + String16Builder builder; + for (size_t i = 0; i < m_path.size(); ++i) { + if (i) + builder.append("."); + builder.append(m_path[i]); + } + builder.append(": "); + builder.append(error); + m_errors.append(builder.toString()); +} + +bool ErrorSupport::hasErrors() +{ + return m_errors.size(); +} + +String16 ErrorSupport::errors() +{ + String16Builder builder; + for (size_t i = 0; i < m_errors.size(); ++i) { + if (i) + builder.append("; "); + builder.append(m_errors[i]); + } + return builder.toString(); +} + +} // namespace protocol +} // namespace blink diff --git a/deps/v8_inspector/platform/inspector_protocol/ErrorSupport.h b/deps/v8_inspector/platform/inspector_protocol/ErrorSupport.h new file mode 100644 index 00000000000000..98a3340f2c6fde --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/ErrorSupport.h @@ -0,0 +1,37 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ErrorSupport_h +#define ErrorSupport_h + +#include "platform/PlatformExport.h" +#include "platform/inspector_protocol/Collections.h" +#include "platform/inspector_protocol/String16.h" + +namespace blink { +namespace protocol { + +class PLATFORM_EXPORT ErrorSupport { +public: + ErrorSupport(); + ErrorSupport(String16* errorString); + ~ErrorSupport(); + + void push(); + void setName(const String16&); + void pop(); + void addError(const String16&); + bool hasErrors(); + String16 errors(); + +private: + protocol::Vector<String16> m_path; + protocol::Vector<String16> m_errors; + String16* m_errorString; +}; + +} // namespace platform +} // namespace blink + +#endif // !defined(ErrorSupport_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/FrontendChannel.h b/deps/v8_inspector/platform/inspector_protocol/FrontendChannel.h new file mode 100644 index 00000000000000..27ba557c4a29b5 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/FrontendChannel.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FrontendChannel_h +#define FrontendChannel_h + +#include "platform/inspector_protocol/Values.h" + +namespace blink { +namespace protocol { + +class FrontendChannel { +public: + virtual ~FrontendChannel() { } + virtual void sendProtocolResponse(int sessionId, int callId, std::unique_ptr<protocol::DictionaryValue> message) = 0; + virtual void sendProtocolNotification(std::unique_ptr<protocol::DictionaryValue> message) = 0; + virtual void flush() = 0; +}; + +} // namespace protocol +} // namespace blink + +#endif // !defined(FrontendChannel_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/Frontend_cpp.template b/deps/v8_inspector/platform/inspector_protocol/Frontend_cpp.template new file mode 100644 index 00000000000000..137dc8e2b1d0d3 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/Frontend_cpp.template @@ -0,0 +1,53 @@ +// This file is generated + +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/inspector_protocol/{{class_name}}.h" + +#include "platform/inspector_protocol/String16.h" + +namespace blink { +namespace protocol { + +Frontend::Frontend(FrontendChannel* frontendChannel) + : m_frontendChannel(frontendChannel) +{% for domain in api.domains %} + , m_{{domain.domain | lower}}(frontendChannel) +{% endfor %} +{ +} + +{% for domain in api.domains %} + {% for event in domain.events %} + {% if "handlers" in event and not ("renderer" in event["handlers"]) %}{% continue %}{% endif %} +void Frontend::{{domain.domain}}::{{event.name}}( + {%- for parameter in event.parameters %} + {% if "optional" in parameter -%} + const Maybe<{{resolve_type(parameter).raw_type}}>& + {%- else -%} + {{resolve_type(parameter).pass_type}} + {%- endif %} {{parameter.name}}{%- if not loop.last -%}, {% endif -%} + {% endfor -%}) +{ + std::unique_ptr<protocol::DictionaryValue> jsonMessage = DictionaryValue::create(); + jsonMessage->setString("method", "{{domain.domain}}.{{event.name}}"); + std::unique_ptr<protocol::DictionaryValue> paramsObject = DictionaryValue::create(); + {% for parameter in event.parameters %} + {% if "optional" in parameter %} + if ({{parameter.name}}.isJust()) + paramsObject->setValue("{{parameter.name}}", toValue({{parameter.name}}.fromJust())); + {% else %} + paramsObject->setValue("{{parameter.name}}", toValue({{resolve_type(parameter).to_raw_type % parameter.name}})); + {% endif %} + {% endfor %} + jsonMessage->setObject("params", std::move(paramsObject)); + if (m_frontendChannel) + m_frontendChannel->sendProtocolNotification(std::move(jsonMessage)); +} + {% endfor %} +{% endfor %} + +} // namespace protocol +} // namespace blink diff --git a/deps/v8_inspector/platform/inspector_protocol/Frontend_h.template b/deps/v8_inspector/platform/inspector_protocol/Frontend_h.template new file mode 100644 index 00000000000000..79cc64b48e2af8 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/Frontend_h.template @@ -0,0 +1,57 @@ +// This file is generated + +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef {{class_name}}_h +#define {{class_name}}_h + + +#include "platform/inspector_protocol/FrontendChannel.h" +#include "platform/inspector_protocol/TypeBuilder.h" + +namespace blink { +namespace protocol { + +class PLATFORM_EXPORT Frontend { +public: + Frontend(FrontendChannel*); + FrontendChannel* channel() { return m_frontendChannel; } + +{% for domain in api.domains %} + + class PLATFORM_EXPORT {{domain.domain}} { + public: + static {{domain.domain}}* from(Frontend* frontend) { return &(frontend->m_{{domain.domain | lower}}) ;} + {{domain.domain}}(FrontendChannel* frontendChannel) : m_frontendChannel(frontendChannel) { } + {% for event in domain.events %} + {% if "handlers" in event and not ("renderer" in event["handlers"]) %}{% continue %}{% endif %} + void {{event.name}}( + {%- for parameter in event.parameters -%} + {%- if "optional" in parameter -%} + const Maybe<{{resolve_type(parameter).raw_type}}>& {{parameter.name}} = Maybe<{{resolve_type(parameter).raw_type}}>() + {%- else -%} + {{resolve_type(parameter).pass_type}} {{parameter.name}} + {%- endif -%}{%- if not loop.last -%}, {% endif -%} + {%- endfor -%} + ); + {% endfor %} + + void flush() { m_frontendChannel->flush(); } + private: + FrontendChannel* m_frontendChannel; + }; +{% endfor %} + +private: + FrontendChannel* m_frontendChannel; +{% for domain in api.domains %} + {{domain.domain}} m_{{domain.domain | lower}}; +{% endfor %} +}; + +} // namespace protocol +} // namespace blink + +#endif // !defined({{class_name}}_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/Maybe.h b/deps/v8_inspector/platform/inspector_protocol/Maybe.h new file mode 100644 index 00000000000000..652d5aa5026a9b --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/Maybe.h @@ -0,0 +1,89 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef Maybe_h +#define Maybe_h + +#include "platform/PlatformExport.h" +#include "wtf/Assertions.h" + +namespace blink { +namespace protocol { + +class String16; + +template<typename T> +class Maybe { +public: + Maybe() { } + Maybe(std::unique_ptr<T> value) : m_value(std::move(value)) { } + void operator=(std::unique_ptr<T> value) { m_value = std::move(value); } + T* fromJust() const { DCHECK(m_value); return m_value.get(); } + T* fromMaybe(T* defaultValue) const { return m_value ? m_value.get() : defaultValue; } + bool isJust() const { return !!m_value; } + std::unique_ptr<T> takeJust() { DCHECK(m_value); return m_value.release(); } +private: + std::unique_ptr<T> m_value; +}; + +template<typename T> +class MaybeBase { +public: + MaybeBase() : m_isJust(false) { } + MaybeBase(T value) : m_isJust(true), m_value(value) { } + void operator=(T value) { m_value = value; m_isJust = true; } + T fromJust() const { DCHECK(m_isJust); return m_value; } + T fromMaybe(const T& defaultValue) const { return m_isJust ? m_value : defaultValue; } + bool isJust() const { return m_isJust; } + T takeJust() { DCHECK(m_isJust); return m_value; } + +protected: + bool m_isJust; + T m_value; +}; + +template<> +class Maybe<bool> : public MaybeBase<bool> { +public: + Maybe() { } + Maybe(bool value) : MaybeBase(value) { } + using MaybeBase::operator=; +}; + +template<> +class Maybe<int> : public MaybeBase<int> { +public: + Maybe() { } + Maybe(int value) : MaybeBase(value) { } + using MaybeBase::operator=; +}; + +template<> +class Maybe<double> : public MaybeBase<double> { +public: + Maybe() { } + Maybe(double value) : MaybeBase(value) { } + using MaybeBase::operator=; +}; + +template<> +class Maybe<String> : public MaybeBase<String> { +public: + Maybe() { } + Maybe(const String& value) : MaybeBase(value) { } + using MaybeBase::operator=; +}; + +template<> +class Maybe<String16> : public MaybeBase<String16> { +public: + Maybe() { } + Maybe(const String16& value) : MaybeBase(value) { } + using MaybeBase::operator=; +}; + +} // namespace platform +} // namespace blink + +#endif // !defined(Maybe_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/OWNERS b/deps/v8_inspector/platform/inspector_protocol/OWNERS new file mode 100644 index 00000000000000..c34ad775ca7e08 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/OWNERS @@ -0,0 +1,5 @@ +alph@chromium.org +caseq@chromium.org +dgozman@chromium.org +kozyatinskiy@chromium.org +pfeldman@chromium.org diff --git a/deps/v8_inspector/platform/inspector_protocol/Parser.cpp b/deps/v8_inspector/platform/inspector_protocol/Parser.cpp new file mode 100644 index 00000000000000..4425660dca44e1 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/Parser.cpp @@ -0,0 +1,496 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/inspector_protocol/Parser.h" + +#include "platform/inspector_protocol/String16.h" +#include "platform/inspector_protocol/Values.h" +#include "wtf/Assertions.h" + +namespace blink { +namespace protocol { + +namespace { + +const int stackLimit = 1000; + +enum Token { + ObjectBegin, + ObjectEnd, + ArrayBegin, + ArrayEnd, + StringLiteral, + Number, + BoolTrue, + BoolFalse, + NullToken, + ListSeparator, + ObjectPairSeparator, + InvalidToken, +}; + +const char* const nullString = "null"; +const char* const trueString = "true"; +const char* const falseString = "false"; + +bool parseConstToken(const UChar* start, const UChar* end, const UChar** tokenEnd, const char* token) +{ + while (start < end && *token != '\0' && *start++ == *token++) { } + if (*token != '\0') + return false; + *tokenEnd = start; + return true; +} + +bool readInt(const UChar* start, const UChar* end, const UChar** tokenEnd, bool canHaveLeadingZeros) +{ + if (start == end) + return false; + bool haveLeadingZero = '0' == *start; + int length = 0; + while (start < end && '0' <= *start && *start <= '9') { + ++start; + ++length; + } + if (!length) + return false; + if (!canHaveLeadingZeros && length > 1 && haveLeadingZero) + return false; + *tokenEnd = start; + return true; +} + +bool parseNumberToken(const UChar* start, const UChar* end, const UChar** tokenEnd) +{ + // We just grab the number here. We validate the size in DecodeNumber. + // According to RFC4627, a valid number is: [minus] int [frac] [exp] + if (start == end) + return false; + UChar c = *start; + if ('-' == c) + ++start; + + if (!readInt(start, end, &start, false)) + return false; + if (start == end) { + *tokenEnd = start; + return true; + } + + // Optional fraction part + c = *start; + if ('.' == c) { + ++start; + if (!readInt(start, end, &start, true)) + return false; + if (start == end) { + *tokenEnd = start; + return true; + } + c = *start; + } + + // Optional exponent part + if ('e' == c || 'E' == c) { + ++start; + if (start == end) + return false; + c = *start; + if ('-' == c || '+' == c) { + ++start; + if (start == end) + return false; + } + if (!readInt(start, end, &start, true)) + return false; + } + + *tokenEnd = start; + return true; +} + +bool readHexDigits(const UChar* start, const UChar* end, const UChar** tokenEnd, int digits) +{ + if (end - start < digits) + return false; + for (int i = 0; i < digits; ++i) { + UChar c = *start++; + if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'))) + return false; + } + *tokenEnd = start; + return true; +} + +bool parseStringToken(const UChar* start, const UChar* end, const UChar** tokenEnd) +{ + while (start < end) { + UChar c = *start++; + if ('\\' == c) { + c = *start++; + // Make sure the escaped char is valid. + switch (c) { + case 'x': + if (!readHexDigits(start, end, &start, 2)) + return false; + break; + case 'u': + if (!readHexDigits(start, end, &start, 4)) + return false; + break; + case '\\': + case '/': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + case 'v': + case '"': + break; + default: + return false; + } + } else if ('"' == c) { + *tokenEnd = start; + return true; + } + } + return false; +} + +bool skipComment(const UChar* start, const UChar* end, const UChar** commentEnd) +{ + if (start == end) + return false; + + if (*start != '/' || start + 1 >= end) + return false; + ++start; + + if (*start == '/') { + // Single line comment, read to newline. + for (++start; start < end; ++start) { + if (*start == '\n' || *start == '\r') { + *commentEnd = start + 1; + return true; + } + } + *commentEnd = end; + // Comment reaches end-of-input, which is fine. + return true; + } + + if (*start == '*') { + UChar previous = '\0'; + // Block comment, read until end marker. + for (++start; start < end; previous = *start++) { + if (previous == '*' && *start == '/') { + *commentEnd = start + 1; + return true; + } + } + // Block comment must close before end-of-input. + return false; + } + + return false; +} + +void skipWhitespaceAndComments(const UChar* start, const UChar* end, const UChar** whitespaceEnd) +{ + while (start < end) { + if (isSpaceOrNewline(*start)) { + ++start; + } else if (*start == '/') { + const UChar* commentEnd; + if (!skipComment(start, end, &commentEnd)) + break; + start = commentEnd; + } else { + break; + } + } + *whitespaceEnd = start; +} + +Token parseToken(const UChar* start, const UChar* end, const UChar** tokenStart, const UChar** tokenEnd) +{ + skipWhitespaceAndComments(start, end, tokenStart); + start = *tokenStart; + + if (start == end) + return InvalidToken; + + switch (*start) { + case 'n': + if (parseConstToken(start, end, tokenEnd, nullString)) + return NullToken; + break; + case 't': + if (parseConstToken(start, end, tokenEnd, trueString)) + return BoolTrue; + break; + case 'f': + if (parseConstToken(start, end, tokenEnd, falseString)) + return BoolFalse; + break; + case '[': + *tokenEnd = start + 1; + return ArrayBegin; + case ']': + *tokenEnd = start + 1; + return ArrayEnd; + case ',': + *tokenEnd = start + 1; + return ListSeparator; + case '{': + *tokenEnd = start + 1; + return ObjectBegin; + case '}': + *tokenEnd = start + 1; + return ObjectEnd; + case ':': + *tokenEnd = start + 1; + return ObjectPairSeparator; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + if (parseNumberToken(start, end, tokenEnd)) + return Number; + break; + case '"': + if (parseStringToken(start + 1, end, tokenEnd)) + return StringLiteral; + break; + } + return InvalidToken; +} + +inline int hexToInt(UChar c) +{ + if ('0' <= c && c <= '9') + return c - '0'; + if ('A' <= c && c <= 'F') + return c - 'A' + 10; + if ('a' <= c && c <= 'f') + return c - 'a' + 10; + NOTREACHED(); + return 0; +} + +bool decodeString(const UChar* start, const UChar* end, String16Builder* output) +{ + while (start < end) { + UChar c = *start++; + if ('\\' != c) { + output->append(c); + continue; + } + c = *start++; + + if (c == 'x') { + // \x is not supported. + return false; + } + + switch (c) { + case '"': + case '/': + case '\\': + break; + case 'b': + c = '\b'; + break; + case 'f': + c = '\f'; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + case 'v': + c = '\v'; + break; + case 'u': + c = (hexToInt(*start) << 12) + + (hexToInt(*(start + 1)) << 8) + + (hexToInt(*(start + 2)) << 4) + + hexToInt(*(start + 3)); + start += 4; + break; + default: + return false; + } + output->append(c); + } + return true; +} + +bool decodeString(const UChar* start, const UChar* end, String16* output) +{ + if (start == end) { + *output = ""; + return true; + } + if (start > end) + return false; + String16Builder buffer; + buffer.reserveCapacity(end - start); + if (!decodeString(start, end, &buffer)) + return false; + *output = buffer.toString(); + return true; +} + +std::unique_ptr<Value> buildValue(const UChar* start, const UChar* end, const UChar** valueTokenEnd, int depth) +{ + if (depth > stackLimit) + return nullptr; + + std::unique_ptr<Value> result; + const UChar* tokenStart; + const UChar* tokenEnd; + Token token = parseToken(start, end, &tokenStart, &tokenEnd); + switch (token) { + case InvalidToken: + return nullptr; + case NullToken: + result = Value::null(); + break; + case BoolTrue: + result = FundamentalValue::create(true); + break; + case BoolFalse: + result = FundamentalValue::create(false); + break; + case Number: { + bool ok; + double value = String16::charactersToDouble(tokenStart, tokenEnd - tokenStart, &ok); + if (!ok) + return nullptr; + result = FundamentalValue::create(value); + break; + } + case StringLiteral: { + String16 value; + bool ok = decodeString(tokenStart + 1, tokenEnd - 1, &value); + if (!ok) + return nullptr; + result = StringValue::create(value); + break; + } + case ArrayBegin: { + std::unique_ptr<ListValue> array = ListValue::create(); + start = tokenEnd; + token = parseToken(start, end, &tokenStart, &tokenEnd); + while (token != ArrayEnd) { + std::unique_ptr<Value> arrayNode = buildValue(start, end, &tokenEnd, depth + 1); + if (!arrayNode) + return nullptr; + array->pushValue(std::move(arrayNode)); + + // After a list value, we expect a comma or the end of the list. + start = tokenEnd; + token = parseToken(start, end, &tokenStart, &tokenEnd); + if (token == ListSeparator) { + start = tokenEnd; + token = parseToken(start, end, &tokenStart, &tokenEnd); + if (token == ArrayEnd) + return nullptr; + } else if (token != ArrayEnd) { + // Unexpected value after list value. Bail out. + return nullptr; + } + } + if (token != ArrayEnd) + return nullptr; + result = std::move(array); + break; + } + case ObjectBegin: { + std::unique_ptr<DictionaryValue> object = DictionaryValue::create(); + start = tokenEnd; + token = parseToken(start, end, &tokenStart, &tokenEnd); + while (token != ObjectEnd) { + if (token != StringLiteral) + return nullptr; + String16 key; + if (!decodeString(tokenStart + 1, tokenEnd - 1, &key)) + return nullptr; + start = tokenEnd; + + token = parseToken(start, end, &tokenStart, &tokenEnd); + if (token != ObjectPairSeparator) + return nullptr; + start = tokenEnd; + + std::unique_ptr<Value> value = buildValue(start, end, &tokenEnd, depth + 1); + if (!value) + return nullptr; + object->setValue(key, std::move(value)); + start = tokenEnd; + + // After a key/value pair, we expect a comma or the end of the + // object. + token = parseToken(start, end, &tokenStart, &tokenEnd); + if (token == ListSeparator) { + start = tokenEnd; + token = parseToken(start, end, &tokenStart, &tokenEnd); + if (token == ObjectEnd) + return nullptr; + } else if (token != ObjectEnd) { + // Unexpected value after last object value. Bail out. + return nullptr; + } + } + if (token != ObjectEnd) + return nullptr; + result = std::move(object); + break; + } + + default: + // We got a token that's not a value. + return nullptr; + } + + skipWhitespaceAndComments(tokenEnd, end, valueTokenEnd); + return result; +} + +std::unique_ptr<Value> parseJSONInternal(const UChar* start, unsigned length) +{ + const UChar* end = start + length; + const UChar *tokenEnd; + std::unique_ptr<Value> value = buildValue(start, end, &tokenEnd, 0); + if (!value || tokenEnd != end) + return nullptr; + return value; +} + +} // anonymous namespace + +std::unique_ptr<Value> parseJSON(const String16& json) +{ + if (json.isEmpty()) + return nullptr; + return parseJSONInternal(json.characters16(), json.length()); +} + +} // namespace protocol +} // namespace blink diff --git a/deps/v8_inspector/platform/inspector_protocol/Parser.h b/deps/v8_inspector/platform/inspector_protocol/Parser.h new file mode 100644 index 00000000000000..5f2f44bf6490d8 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/Parser.h @@ -0,0 +1,22 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef Parser_h +#define Parser_h + +#include "platform/PlatformExport.h" +#include "platform/inspector_protocol/String16.h" +#include "wtf/PtrUtil.h" + +namespace blink { +namespace protocol { + +class Value; + +PLATFORM_EXPORT std::unique_ptr<Value> parseJSON(const String16& json); + +} // namespace platform +} // namespace blink + +#endif // !defined(Parser_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/ParserTest.cpp b/deps/v8_inspector/platform/inspector_protocol/ParserTest.cpp new file mode 100644 index 00000000000000..1dfeb4a4fbb983 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/ParserTest.cpp @@ -0,0 +1,504 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/inspector_protocol/Parser.h" + +#include "platform/inspector_protocol/String16.h" +#include "platform/inspector_protocol/Values.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace blink { +namespace protocol { + +TEST(ParserTest, Reading) +{ + protocol::Value* tmpValue; + std::unique_ptr<protocol::Value> root; + std::unique_ptr<protocol::Value> root2; + String16 strVal; + int intVal = 0; + + // some whitespace checking + root = parseJSON(" null "); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeNull, root->type()); + + // Invalid JSON string + root = parseJSON("nu"); + EXPECT_FALSE(root.get()); + + // Simple bool + root = parseJSON("true "); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeBoolean, root->type()); + + // Embedded comment + root = parseJSON("40 /*/"); + EXPECT_FALSE(root.get()); + root = parseJSON("/* comment */null"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeNull, root->type()); + root = parseJSON("40 /* comment */"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeNumber, root->type()); + EXPECT_TRUE(root->asNumber(&intVal)); + EXPECT_EQ(40, intVal); + root = parseJSON("/**/ 40 /* multi-line\n comment */ // more comment"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeNumber, root->type()); + EXPECT_TRUE(root->asNumber(&intVal)); + EXPECT_EQ(40, intVal); + root = parseJSON("true // comment"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeBoolean, root->type()); + root = parseJSON("/* comment */\"sample string\""); + ASSERT_TRUE(root.get()); + EXPECT_TRUE(root->asString(&strVal)); + EXPECT_EQ("sample string", strVal); + root = parseJSON("[1, /* comment, 2 ] */ \n 3]"); + ASSERT_TRUE(root.get()); + protocol::ListValue* list = ListValue::cast(root.get()); + ASSERT_TRUE(list); + EXPECT_EQ(2u, list->size()); + tmpValue = list->at(0); + ASSERT_TRUE(tmpValue); + EXPECT_TRUE(tmpValue->asNumber(&intVal)); + EXPECT_EQ(1, intVal); + tmpValue = list->at(1); + ASSERT_TRUE(tmpValue); + EXPECT_TRUE(tmpValue->asNumber(&intVal)); + EXPECT_EQ(3, intVal); + root = parseJSON("[1, /*a*/2, 3]"); + ASSERT_TRUE(root.get()); + list = ListValue::cast(root.get()); + ASSERT_TRUE(list); + EXPECT_EQ(3u, list->size()); + root = parseJSON("/* comment **/42"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeNumber, root->type()); + EXPECT_TRUE(root->asNumber(&intVal)); + EXPECT_EQ(42, intVal); + root = parseJSON( + "/* comment **/\n" + "// */ 43\n" + "44"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeNumber, root->type()); + EXPECT_TRUE(root->asNumber(&intVal)); + EXPECT_EQ(44, intVal); + + // Test number formats + root = parseJSON("43"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeNumber, root->type()); + EXPECT_TRUE(root->asNumber(&intVal)); + EXPECT_EQ(43, intVal); + + // According to RFC4627, oct, hex, and leading zeros are invalid JSON. + root = parseJSON("043"); + EXPECT_FALSE(root.get()); + root = parseJSON("0x43"); + EXPECT_FALSE(root.get()); + root = parseJSON("00"); + EXPECT_FALSE(root.get()); + + // Test 0 (which needs to be special cased because of the leading zero + // clause). + root = parseJSON("0"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeNumber, root->type()); + intVal = 1; + EXPECT_TRUE(root->asNumber(&intVal)); + EXPECT_EQ(0, intVal); + + // Numbers that overflow ints should succeed, being internally promoted to + // storage as doubles + root = parseJSON("2147483648"); + ASSERT_TRUE(root.get()); + double doubleVal; + EXPECT_EQ(Value::TypeNumber, root->type()); + doubleVal = 0.0; + EXPECT_TRUE(root->asNumber(&doubleVal)); + EXPECT_DOUBLE_EQ(2147483648.0, doubleVal); + root = parseJSON("-2147483649"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeNumber, root->type()); + doubleVal = 0.0; + EXPECT_TRUE(root->asNumber(&doubleVal)); + EXPECT_DOUBLE_EQ(-2147483649.0, doubleVal); + + // Parse a double + root = parseJSON("43.1"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeNumber, root->type()); + doubleVal = 0.0; + EXPECT_TRUE(root->asNumber(&doubleVal)); + EXPECT_DOUBLE_EQ(43.1, doubleVal); + + root = parseJSON("4.3e-1"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeNumber, root->type()); + doubleVal = 0.0; + EXPECT_TRUE(root->asNumber(&doubleVal)); + EXPECT_DOUBLE_EQ(.43, doubleVal); + + root = parseJSON("2.1e0"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeNumber, root->type()); + doubleVal = 0.0; + EXPECT_TRUE(root->asNumber(&doubleVal)); + EXPECT_DOUBLE_EQ(2.1, doubleVal); + + root = parseJSON("2.1e+0001"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeNumber, root->type()); + doubleVal = 0.0; + EXPECT_TRUE(root->asNumber(&doubleVal)); + EXPECT_DOUBLE_EQ(21.0, doubleVal); + + root = parseJSON("0.01"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeNumber, root->type()); + doubleVal = 0.0; + EXPECT_TRUE(root->asNumber(&doubleVal)); + EXPECT_DOUBLE_EQ(0.01, doubleVal); + + root = parseJSON("1.00"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeNumber, root->type()); + doubleVal = 0.0; + EXPECT_TRUE(root->asNumber(&doubleVal)); + EXPECT_DOUBLE_EQ(1.0, doubleVal); + + // Fractional parts must have a digit before and after the decimal point. + root = parseJSON("1."); + EXPECT_FALSE(root.get()); + root = parseJSON(".1"); + EXPECT_FALSE(root.get()); + root = parseJSON("1.e10"); + EXPECT_FALSE(root.get()); + + // Exponent must have a digit following the 'e'. + root = parseJSON("1e"); + EXPECT_FALSE(root.get()); + root = parseJSON("1E"); + EXPECT_FALSE(root.get()); + root = parseJSON("1e1."); + EXPECT_FALSE(root.get()); + root = parseJSON("1e1.0"); + EXPECT_FALSE(root.get()); + + // INF/-INF/NaN are not valid + root = parseJSON("NaN"); + EXPECT_FALSE(root.get()); + root = parseJSON("nan"); + EXPECT_FALSE(root.get()); + root = parseJSON("inf"); + EXPECT_FALSE(root.get()); + + // Invalid number formats + root = parseJSON("4.3.1"); + EXPECT_FALSE(root.get()); + root = parseJSON("4e3.1"); + EXPECT_FALSE(root.get()); + + // Test string parser + root = parseJSON("\"hello world\""); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeString, root->type()); + EXPECT_TRUE(root->asString(&strVal)); + EXPECT_EQ("hello world", strVal); + + // Empty string + root = parseJSON("\"\""); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeString, root->type()); + EXPECT_TRUE(root->asString(&strVal)); + EXPECT_EQ("", strVal); + + // Test basic string escapes + root = parseJSON("\" \\\"\\\\\\/\\b\\f\\n\\r\\t\\v\""); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeString, root->type()); + EXPECT_TRUE(root->asString(&strVal)); + EXPECT_EQ(" \"\\/\b\f\n\r\t\v", strVal); + + // Test hex and unicode escapes including the null character. + root = parseJSON("\"\\x41\\x00\\u1234\""); + EXPECT_FALSE(root.get()); + + // Test invalid strings + root = parseJSON("\"no closing quote"); + EXPECT_FALSE(root.get()); + root = parseJSON("\"\\z invalid escape char\""); + EXPECT_FALSE(root.get()); + root = parseJSON("\"not enough escape chars\\u123\""); + EXPECT_FALSE(root.get()); + root = parseJSON("\"extra backslash at end of input\\\""); + EXPECT_FALSE(root.get()); + + // Basic array + root = parseJSON("[true, false, null]"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeArray, root->type()); + list = ListValue::cast(root.get()); + ASSERT_TRUE(list); + EXPECT_EQ(3U, list->size()); + + // Empty array + root = parseJSON("[]"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeArray, root->type()); + list = ListValue::cast(root.get()); + ASSERT_TRUE(list); + EXPECT_EQ(0U, list->size()); + + // Nested arrays + root = parseJSON("[[true], [], [false, [], [null]], null]"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeArray, root->type()); + list = ListValue::cast(root.get()); + ASSERT_TRUE(list); + EXPECT_EQ(4U, list->size()); + + // Invalid, missing close brace. + root = parseJSON("[[true], [], [false, [], [null]], null"); + EXPECT_FALSE(root.get()); + + // Invalid, too many commas + root = parseJSON("[true,, null]"); + EXPECT_FALSE(root.get()); + + // Invalid, no commas + root = parseJSON("[true null]"); + EXPECT_FALSE(root.get()); + + // Invalid, trailing comma + root = parseJSON("[true,]"); + EXPECT_FALSE(root.get()); + + root = parseJSON("[true]"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeArray, root->type()); + list = ListValue::cast(root.get()); + ASSERT_TRUE(list); + EXPECT_EQ(1U, list->size()); + tmpValue = list->at(0); + ASSERT_TRUE(tmpValue); + EXPECT_EQ(Value::TypeBoolean, tmpValue->type()); + bool boolValue = false; + EXPECT_TRUE(tmpValue->asBoolean(&boolValue)); + EXPECT_TRUE(boolValue); + + // Don't allow empty elements. + root = parseJSON("[,]"); + EXPECT_FALSE(root.get()); + root = parseJSON("[true,,]"); + EXPECT_FALSE(root.get()); + root = parseJSON("[,true,]"); + EXPECT_FALSE(root.get()); + root = parseJSON("[true,,false]"); + EXPECT_FALSE(root.get()); + + // Test objects + root = parseJSON("{}"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeObject, root->type()); + + root = parseJSON("{\"number\":9.87654321, \"null\":null , \"S\" : \"str\" }"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeObject, root->type()); + protocol::DictionaryValue* objectVal = DictionaryValue::cast(root.get()); + ASSERT_TRUE(objectVal); + doubleVal = 0.0; + EXPECT_TRUE(objectVal->getNumber("number", &doubleVal)); + EXPECT_DOUBLE_EQ(9.87654321, doubleVal); + protocol::Value* nullVal = objectVal->get("null"); + ASSERT_TRUE(nullVal); + EXPECT_EQ(Value::TypeNull, nullVal->type()); + EXPECT_TRUE(objectVal->getString("S", &strVal)); + EXPECT_EQ("str", strVal); + + // Test newline equivalence. + root2 = parseJSON( + "{\n" + " \"number\":9.87654321,\n" + " \"null\":null,\n" + " \"S\":\"str\"\n" + "}\n"); + ASSERT_TRUE(root2.get()); + EXPECT_EQ(root->toJSONString(), root2->toJSONString()); + + root2 = parseJSON( + "{\r\n" + " \"number\":9.87654321,\r\n" + " \"null\":null,\r\n" + " \"S\":\"str\"\r\n" + "}\r\n"); + ASSERT_TRUE(root2.get()); + EXPECT_EQ(root->toJSONString(), root2->toJSONString()); + + // Test nesting + root = parseJSON("{\"inner\":{\"array\":[true]},\"false\":false,\"d\":{}}"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeObject, root->type()); + objectVal = DictionaryValue::cast(root.get()); + ASSERT_TRUE(objectVal); + protocol::DictionaryValue* innerObject = objectVal->getObject("inner"); + ASSERT_TRUE(innerObject); + protocol::ListValue* innerArray = innerObject->getArray("array"); + ASSERT_TRUE(innerArray); + EXPECT_EQ(1U, innerArray->size()); + boolValue = true; + EXPECT_TRUE(objectVal->getBoolean("false", &boolValue)); + EXPECT_FALSE(boolValue); + innerObject = objectVal->getObject("d"); + EXPECT_TRUE(innerObject); + + // Test keys with periods + root = parseJSON("{\"a.b\":3,\"c\":2,\"d.e.f\":{\"g.h.i.j\":1}}"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeObject, root->type()); + objectVal = DictionaryValue::cast(root.get()); + ASSERT_TRUE(objectVal); + int integerValue = 0; + EXPECT_TRUE(objectVal->getNumber("a.b", &integerValue)); + EXPECT_EQ(3, integerValue); + EXPECT_TRUE(objectVal->getNumber("c", &integerValue)); + EXPECT_EQ(2, integerValue); + innerObject = objectVal->getObject("d.e.f"); + ASSERT_TRUE(innerObject); + EXPECT_EQ(1U, innerObject->size()); + EXPECT_TRUE(innerObject->getNumber("g.h.i.j", &integerValue)); + EXPECT_EQ(1, integerValue); + + root = parseJSON("{\"a\":{\"b\":2},\"a.b\":1}"); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeObject, root->type()); + objectVal = DictionaryValue::cast(root.get()); + ASSERT_TRUE(objectVal); + innerObject = objectVal->getObject("a"); + ASSERT_TRUE(innerObject); + EXPECT_TRUE(innerObject->getNumber("b", &integerValue)); + EXPECT_EQ(2, integerValue); + EXPECT_TRUE(objectVal->getNumber("a.b", &integerValue)); + EXPECT_EQ(1, integerValue); + + // Invalid, no closing brace + root = parseJSON("{\"a\": true"); + EXPECT_FALSE(root.get()); + + // Invalid, keys must be quoted + root = parseJSON("{foo:true}"); + EXPECT_FALSE(root.get()); + + // Invalid, trailing comma + root = parseJSON("{\"a\":true,}"); + EXPECT_FALSE(root.get()); + + // Invalid, too many commas + root = parseJSON("{\"a\":true,,\"b\":false}"); + EXPECT_FALSE(root.get()); + + // Invalid, no separator + root = parseJSON("{\"a\" \"b\"}"); + EXPECT_FALSE(root.get()); + + // Invalid, lone comma. + root = parseJSON("{,}"); + EXPECT_FALSE(root.get()); + root = parseJSON("{\"a\":true,,}"); + EXPECT_FALSE(root.get()); + root = parseJSON("{,\"a\":true}"); + EXPECT_FALSE(root.get()); + root = parseJSON("{\"a\":true,,\"b\":false}"); + EXPECT_FALSE(root.get()); + + // Test stack overflow + String16Builder evil; + evil.reserveCapacity(2000000); + for (int i = 0; i < 1000000; ++i) + evil.append('['); + for (int i = 0; i < 1000000; ++i) + evil.append(']'); + root = parseJSON(evil.toString()); + EXPECT_FALSE(root.get()); + + // A few thousand adjacent lists is fine. + String16Builder notEvil; + notEvil.reserveCapacity(15010); + notEvil.append('['); + for (int i = 0; i < 5000; ++i) + notEvil.append("[],"); + notEvil.append("[]]"); + root = parseJSON(notEvil.toString()); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeArray, root->type()); + list = ListValue::cast(root.get()); + ASSERT_TRUE(list); + EXPECT_EQ(5001U, list->size()); + + // Test utf8 encoded input + root = parseJSON("\"\\xe7\\xbd\\x91\\xe9\\xa1\\xb5\""); + ASSERT_FALSE(root.get()); + + // Test utf16 encoded strings. + root = parseJSON("\"\\u20ac3,14\""); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeString, root->type()); + EXPECT_TRUE(root->asString(&strVal)); + UChar tmp2[] = {0x20ac, 0x33, 0x2c, 0x31, 0x34}; + EXPECT_EQ(String16(tmp2, 5), strVal); + + root = parseJSON("\"\\ud83d\\udca9\\ud83d\\udc6c\""); + ASSERT_TRUE(root.get()); + EXPECT_EQ(Value::TypeString, root->type()); + EXPECT_TRUE(root->asString(&strVal)); + UChar tmp3[] = {0xd83d, 0xdca9, 0xd83d, 0xdc6c}; + EXPECT_EQ(String16(tmp3, 4), strVal); + + // Test literal root objects. + root = parseJSON("null"); + EXPECT_EQ(Value::TypeNull, root->type()); + + root = parseJSON("true"); + ASSERT_TRUE(root.get()); + EXPECT_TRUE(root->asBoolean(&boolValue)); + EXPECT_TRUE(boolValue); + + root = parseJSON("10"); + ASSERT_TRUE(root.get()); + EXPECT_TRUE(root->asNumber(&integerValue)); + EXPECT_EQ(10, integerValue); + + root = parseJSON("\"root\""); + ASSERT_TRUE(root.get()); + EXPECT_TRUE(root->asString(&strVal)); + EXPECT_EQ("root", strVal); +} + +TEST(ParserTest, InvalidSanity) +{ + const char* const invalidJson[] = { + "/* test *", + "{\"foo\"", + "{\"foo\":", + " [", + "\"\\u123g\"", + "{\n\"eh:\n}", + "////", + "*/**/", + "/**/", + "/*/", + "//**/" + }; + + for (size_t i = 0; i < 11; ++i) { + std::unique_ptr<protocol::Value> result = parseJSON(invalidJson[i]); + EXPECT_FALSE(result.get()); + } +} + +} // namespace protocol +} // namespace blink diff --git a/deps/v8_inspector/platform/inspector_protocol/String16.h b/deps/v8_inspector/platform/inspector_protocol/String16.h new file mode 100644 index 00000000000000..6a1b4a09de3346 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/String16.h @@ -0,0 +1,14 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef String16_h +#define String16_h + +#if V8_INSPECTOR_USE_STL +#include "platform/inspector_protocol/String16STL.h" +#else +#include "platform/inspector_protocol/String16WTF.h" +#endif // V8_INSPECTOR_USE_STL + +#endif // !defined(String16_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/String16STL.cpp b/deps/v8_inspector/platform/inspector_protocol/String16STL.cpp new file mode 100644 index 00000000000000..9f6afbf07578a8 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/String16STL.cpp @@ -0,0 +1,602 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/inspector_protocol/String16STL.h" + +#include <algorithm> +#include <cctype> +#include <cstdio> +#include <functional> +#include <locale> + +#define DCHECK(k) + +namespace blink { +namespace protocol { + +const UChar replacementCharacter = 0xFFFD; + +template<typename CharType> inline bool isASCII(CharType c) +{ + return !(c & ~0x7F); +} + +template<typename CharType> inline bool isASCIIAlpha(CharType c) +{ + return (c | 0x20) >= 'a' && (c | 0x20) <= 'z'; +} + +template<typename CharType> inline bool isASCIIDigit(CharType c) +{ + return c >= '0' && c <= '9'; +} + +template<typename CharType> inline bool isASCIIAlphanumeric(CharType c) +{ + return isASCIIDigit(c) || isASCIIAlpha(c); +} + +template<typename CharType> inline bool isASCIIHexDigit(CharType c) +{ + return isASCIIDigit(c) || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); +} + +template<typename CharType> inline bool isASCIIOctalDigit(CharType c) +{ + return (c >= '0') & (c <= '7'); +} + +template<typename CharType> inline bool isASCIIPrintable(CharType c) +{ + return c >= ' ' && c <= '~'; +} + +/* + Statistics from a run of Apple's page load test for callers of isASCIISpace: + + character count + --------- ----- + non-spaces 689383 + 20 space 294720 + 0A \n 89059 + 09 \t 28320 + 0D \r 0 + 0C \f 0 + 0B \v 0 + */ +template<typename CharType> inline bool isASCIISpace(CharType c) +{ + return c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); +} + +extern const LChar ASCIICaseFoldTable[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +template<typename CharType> inline int toASCIIHexValue(CharType c) +{ + DCHECK(isASCIIHexDigit(c)); + return c < 'A' ? c - '0' : (c - 'A' + 10) & 0xF; +} + +template<typename CharType> inline int toASCIIHexValue(CharType upperValue, CharType lowerValue) +{ + DCHECK(isASCIIHexDigit(upperValue) && isASCIIHexDigit(lowerValue)); + return ((toASCIIHexValue(upperValue) << 4) & 0xF0) | toASCIIHexValue(lowerValue); +} + +inline char lowerNibbleToASCIIHexDigit(char c) +{ + char nibble = c & 0xF; + return nibble < 10 ? '0' + nibble : 'A' + nibble - 10; +} + +inline char upperNibbleToASCIIHexDigit(char c) +{ + char nibble = (c >> 4) & 0xF; + return nibble < 10 ? '0' + nibble : 'A' + nibble - 10; +} + +template<typename CharType> inline bool isASCIIAlphaCaselessEqual(CharType cssCharacter, char character) +{ + // This function compares a (preferrably) constant ASCII + // lowercase letter to any input character. + DCHECK(character >= 'a' && character <= 'z'); + return LIKELY(toASCIILowerUnchecked(cssCharacter) == character); +} + +inline int inlineUTF8SequenceLengthNonASCII(char b0) +{ + if ((b0 & 0xC0) != 0xC0) + return 0; + if ((b0 & 0xE0) == 0xC0) + return 2; + if ((b0 & 0xF0) == 0xE0) + return 3; + if ((b0 & 0xF8) == 0xF0) + return 4; + return 0; +} + +inline int inlineUTF8SequenceLength(char b0) +{ + return isASCII(b0) ? 1 : inlineUTF8SequenceLengthNonASCII(b0); +} + +// Once the bits are split out into bytes of UTF-8, this is a mask OR-ed +// into the first byte, depending on how many bytes follow. There are +// as many entries in this table as there are UTF-8 sequence types. +// (I.e., one byte sequence, two byte... etc.). Remember that sequences +// for *legal* UTF-8 will be 4 or fewer bytes total. +static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +typedef enum { + conversionOK, // conversion successful + sourceExhausted, // partial character in source, but hit end + targetExhausted, // insuff. room in target for conversion + sourceIllegal // source sequence is illegal/malformed +} ConversionResult; + +ConversionResult convertLatin1ToUTF8( + const LChar** sourceStart, const LChar* sourceEnd, + char** targetStart, char* targetEnd) +{ + ConversionResult result = conversionOK; + const LChar* source = *sourceStart; + char* target = *targetStart; + while (source < sourceEnd) { + UChar32 ch; + unsigned short bytesToWrite = 0; + const UChar32 byteMask = 0xBF; + const UChar32 byteMark = 0x80; + const LChar* oldSource = source; // In case we have to back up because of target overflow. + ch = static_cast<unsigned short>(*source++); + + // Figure out how many bytes the result will require + if (ch < (UChar32)0x80) + bytesToWrite = 1; + else + bytesToWrite = 2; + + target += bytesToWrite; + if (target > targetEnd) { + source = oldSource; // Back up source pointer! + target -= bytesToWrite; + result = targetExhausted; + break; + } + switch (bytesToWrite) { // note: everything falls through. + case 2: + *--target = (char)((ch | byteMark) & byteMask); + ch >>= 6; + case 1: + *--target = (char)(ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } + *sourceStart = source; + *targetStart = target; + return result; +} + +ConversionResult convertUTF16ToUTF8( + const UChar** sourceStart, const UChar* sourceEnd, + char** targetStart, char* targetEnd, bool strict) +{ + ConversionResult result = conversionOK; + const UChar* source = *sourceStart; + char* target = *targetStart; + while (source < sourceEnd) { + UChar32 ch; + unsigned short bytesToWrite = 0; + const UChar32 byteMask = 0xBF; + const UChar32 byteMark = 0x80; + const UChar* oldSource = source; // In case we have to back up because of target overflow. + ch = static_cast<unsigned short>(*source++); + // If we have a surrogate pair, convert to UChar32 first. + if (ch >= 0xD800 && ch <= 0xDBFF) { + // If the 16 bits following the high surrogate are in the source buffer... + if (source < sourceEnd) { + UChar32 ch2 = static_cast<unsigned short>(*source); + // If it's a low surrogate, convert to UChar32. + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { + ch = ((ch - 0xD800) << 10) + (ch2 - 0xDC00) + 0x0010000; + ++source; + } else if (strict) { // it's an unpaired high surrogate + --source; // return to the illegal value itself + result = sourceIllegal; + break; + } + } else { // We don't have the 16 bits following the high surrogate. + --source; // return to the high surrogate + result = sourceExhausted; + break; + } + } else if (strict) { + // UTF-16 surrogate values are illegal in UTF-32 + if (ch >= 0xDC00 && ch <= 0xDFFF) { + --source; // return to the illegal value itself + result = sourceIllegal; + break; + } + } + // Figure out how many bytes the result will require + if (ch < (UChar32)0x80) { + bytesToWrite = 1; + } else if (ch < (UChar32)0x800) { + bytesToWrite = 2; + } else if (ch < (UChar32)0x10000) { + bytesToWrite = 3; + } else if (ch < (UChar32)0x110000) { + bytesToWrite = 4; + } else { + bytesToWrite = 3; + ch = replacementCharacter; + } + + target += bytesToWrite; + if (target > targetEnd) { + source = oldSource; // Back up source pointer! + target -= bytesToWrite; + result = targetExhausted; + break; + } + switch (bytesToWrite) { // note: everything falls through. + case 4: + *--target = (char)((ch | byteMark) & byteMask); + ch >>= 6; + case 3: + *--target = (char)((ch | byteMark) & byteMask); + ch >>= 6; + case 2: + *--target = (char)((ch | byteMark) & byteMask); + ch >>= 6; + case 1: + *--target = (char)(ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/** + * Is this code point a BMP code point (U+0000..U+ffff)? + * @param c 32-bit code point + * @return TRUE or FALSE + * @stable ICU 2.8 + */ +#define U_IS_BMP(c) ((uint32_t)(c)<=0xffff) + +/** + * Is this code point a supplementary code point (U+10000..U+10ffff)? + * @param c 32-bit code point + * @return TRUE or FALSE + * @stable ICU 2.8 + */ +#define U_IS_SUPPLEMENTARY(c) ((uint32_t)((c)-0x10000)<=0xfffff) + +/** + * Is this code point a surrogate (U+d800..U+dfff)? + * @param c 32-bit code point + * @return TRUE or FALSE + * @stable ICU 2.4 + */ +#define U_IS_SURROGATE(c) (((c)&0xfffff800)==0xd800) + +/** + * Get the lead surrogate (0xd800..0xdbff) for a + * supplementary code point (0x10000..0x10ffff). + * @param supplementary 32-bit code point (U+10000..U+10ffff) + * @return lead surrogate (U+d800..U+dbff) for supplementary + * @stable ICU 2.4 + */ +#define U16_LEAD(supplementary) (UChar)(((supplementary)>>10)+0xd7c0) + +/** + * Get the trail surrogate (0xdc00..0xdfff) for a + * supplementary code point (0x10000..0x10ffff). + * @param supplementary 32-bit code point (U+10000..U+10ffff) + * @return trail surrogate (U+dc00..U+dfff) for supplementary + * @stable ICU 2.4 + */ +#define U16_TRAIL(supplementary) (UChar)(((supplementary)&0x3ff)|0xdc00) + +// This must be called with the length pre-determined by the first byte. +// If presented with a length > 4, this returns false. The Unicode +// definition of UTF-8 goes up to 4-byte sequences. +static bool isLegalUTF8(const unsigned char* source, int length) +{ + unsigned char a; + const unsigned char* srcptr = source + length; + switch (length) { + default: + return false; + // Everything else falls through when "true"... + case 4: + if ((a = (*--srcptr)) < 0x80 || a > 0xBF) + return false; + case 3: + if ((a = (*--srcptr)) < 0x80 || a > 0xBF) + return false; + case 2: + if ((a = (*--srcptr)) > 0xBF) + return false; + + // no fall-through in this inner switch + switch (*source) { + case 0xE0: + if (a < 0xA0) + return false; + break; + case 0xED: + if (a > 0x9F) + return false; + break; + case 0xF0: + if (a < 0x90) + return false; + break; + case 0xF4: + if (a > 0x8F) + return false; + break; + default: + if (a < 0x80) + return false; + } + + case 1: + if (*source >= 0x80 && *source < 0xC2) + return false; + } + if (*source > 0xF4) + return false; + return true; +} + +// Magic values subtracted from a buffer value during UTF8 conversion. +// This table contains as many values as there might be trailing bytes +// in a UTF-8 sequence. +static const UChar32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 0x03C82080UL, static_cast<UChar32>(0xFA082080UL), static_cast<UChar32>(0x82082080UL) }; + +static inline UChar32 readUTF8Sequence(const char*& sequence, unsigned length) +{ + UChar32 character = 0; + + // The cases all fall through. + switch (length) { + case 6: + character += static_cast<unsigned char>(*sequence++); + character <<= 6; + case 5: + character += static_cast<unsigned char>(*sequence++); + character <<= 6; + case 4: + character += static_cast<unsigned char>(*sequence++); + character <<= 6; + case 3: + character += static_cast<unsigned char>(*sequence++); + character <<= 6; + case 2: + character += static_cast<unsigned char>(*sequence++); + character <<= 6; + case 1: + character += static_cast<unsigned char>(*sequence++); + } + + return character - offsetsFromUTF8[length - 1]; +} + +ConversionResult convertUTF8ToUTF16( + const char** sourceStart, const char* sourceEnd, + UChar** targetStart, UChar* targetEnd, bool* sourceAllASCII, bool strict) +{ + ConversionResult result = conversionOK; + const char* source = *sourceStart; + UChar* target = *targetStart; + UChar orAllData = 0; + while (source < sourceEnd) { + int utf8SequenceLength = inlineUTF8SequenceLength(*source); + if (sourceEnd - source < utf8SequenceLength) { + result = sourceExhausted; + break; + } + // Do this check whether lenient or strict + if (!isLegalUTF8(reinterpret_cast<const unsigned char*>(source), utf8SequenceLength)) { + result = sourceIllegal; + break; + } + + UChar32 character = readUTF8Sequence(source, utf8SequenceLength); + + if (target >= targetEnd) { + source -= utf8SequenceLength; // Back up source pointer! + result = targetExhausted; + break; + } + + if (U_IS_BMP(character)) { + // UTF-16 surrogate values are illegal in UTF-32 + if (U_IS_SURROGATE(character)) { + if (strict) { + source -= utf8SequenceLength; // return to the illegal value itself + result = sourceIllegal; + break; + } + *target++ = replacementCharacter; + orAllData |= replacementCharacter; + } else { + *target++ = static_cast<UChar>(character); // normal case + orAllData |= character; + } + } else if (U_IS_SUPPLEMENTARY(character)) { + // target is a character in range 0xFFFF - 0x10FFFF + if (target + 1 >= targetEnd) { + source -= utf8SequenceLength; // Back up source pointer! + result = targetExhausted; + break; + } + *target++ = U16_LEAD(character); + *target++ = U16_TRAIL(character); + orAllData = 0xffff; + } else { + if (strict) { + source -= utf8SequenceLength; // return to the start + result = sourceIllegal; + break; // Bail out; shouldn't continue + } else { + *target++ = replacementCharacter; + orAllData |= replacementCharacter; + } + } + } + *sourceStart = source; + *targetStart = target; + + if (sourceAllASCII) + *sourceAllASCII = !(orAllData & ~0x7f); + + return result; +} + +// Helper to write a three-byte UTF-8 code point to the buffer, caller must check room is available. +static inline void putUTF8Triple(char*& buffer, UChar ch) +{ + DCHECK(ch >= 0x0800); + *buffer++ = static_cast<char>(((ch >> 12) & 0x0F) | 0xE0); + *buffer++ = static_cast<char>(((ch >> 6) & 0x3F) | 0x80); + *buffer++ = static_cast<char>((ch & 0x3F) | 0x80); +} + +String16 String16::fromUTF8(const char* stringStart, size_t length) +{ + if (!stringStart || !length) + return String16(); + + std::vector<UChar> buffer(length); + UChar* bufferStart = buffer.data(); + + UChar* bufferCurrent = bufferStart; + const char* stringCurrent = stringStart; + if (convertUTF8ToUTF16(&stringCurrent, stringStart + length, &bufferCurrent, bufferCurrent + buffer.size(), 0, true) != conversionOK) + return String16(); + + unsigned utf16Length = bufferCurrent - bufferStart; + return String16(bufferStart, utf16Length); +} + +// trim from start +static inline wstring <rim(wstring &s) +{ + s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace)))); + return s; +} + +// trim from end +static inline wstring &rtrim(wstring &s) +{ + s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end()); + return s; +} + +// trim from both ends +static inline wstring &trim(wstring &s) +{ + return ltrim(rtrim(s)); +} + +// static +std::string String16::intToString(int i) +{ + char buffer[50]; + std::sprintf(buffer, "%d", i); + return std::string(buffer); +} + +// static +std::string String16::doubleToString(double d) +{ + char buffer[100]; + std::sprintf(buffer, "%f", d); + return std::string(buffer); +} + +std::string String16::utf8() const +{ + unsigned length = this->length(); + + if (!length) + return std::string(""); + + // Allocate a buffer big enough to hold all the characters + // (an individual UTF-16 UChar can only expand to 3 UTF-8 bytes). + // Optimization ideas, if we find this function is hot: + // * We could speculatively create a CStringBuffer to contain 'length' + // characters, and resize if necessary (i.e. if the buffer contains + // non-ascii characters). (Alternatively, scan the buffer first for + // ascii characters, so we know this will be sufficient). + // * We could allocate a CStringBuffer with an appropriate size to + // have a good chance of being able to write the string into the + // buffer without reallocing (say, 1.5 x length). + if (length > std::numeric_limits<unsigned>::max() / 3) + return std::string(); + std::vector<char> bufferVector(length * 3); + char* buffer = bufferVector.data(); + const UChar* characters = m_impl.data(); + + bool strict = false; + ConversionResult result = convertUTF16ToUTF8(&characters, characters + length, &buffer, buffer + bufferVector.size(), strict); + DCHECK(result != targetExhausted); // (length * 3) should be sufficient for any conversion + + // Only produced from strict conversion. + if (result == sourceIllegal) { + DCHECK(strict); + return std::string(); + } + + // Check for an unconverted high surrogate. + if (result == sourceExhausted) { + if (strict) + return std::string(); + // This should be one unpaired high surrogate. Treat it the same + // was as an unpaired high surrogate would have been handled in + // the middle of a string with non-strict conversion - which is + // to say, simply encode it to UTF-8. + DCHECK((characters + 1) == (m_impl.data() + length)); + DCHECK((*characters >= 0xD800) && (*characters <= 0xDBFF)); + // There should be room left, since one UChar hasn't been + // converted. + DCHECK((buffer + 3) <= (buffer + bufferVector.size())); + putUTF8Triple(buffer, *characters); + } + + return std::string(bufferVector.data(), buffer - bufferVector.data()); +} + +String16 String16::stripWhiteSpace() const +{ + wstring result(m_impl); + trim(result); + return result; +} + +} // namespace protocol +} // namespace blink diff --git a/deps/v8_inspector/platform/inspector_protocol/String16STL.h b/deps/v8_inspector/platform/inspector_protocol/String16STL.h new file mode 100644 index 00000000000000..2ac0e2ea7fead5 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/String16STL.h @@ -0,0 +1,236 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef String16STL_h +#define String16STL_h + +#include <stdint.h> + +#include <cstdlib> +#include <cstring> +#include <string> +#include <vector> + +using UChar = uint16_t; +using UChar32 = uint32_t; +using LChar = unsigned char; +// presubmit: allow wstring +using wstring = std::basic_string<UChar>; +const size_t kNotFound = static_cast<size_t>(-1); + +namespace blink { +namespace protocol { + +class String16 { +public: + String16() { } + String16(const String16& other) : m_impl(other.m_impl) { } + // presubmit: allow wstring + String16(const wstring& impl) : m_impl(impl) { } + String16(const UChar* characters) : m_impl(characters) { } + String16(const char* characters) : String16(characters, std::strlen(characters)) { } + String16(const char* characters, size_t size) + { + m_impl.resize(size); + for (size_t i = 0; i < size; ++i) + m_impl[i] = characters[i]; + } + String16(const UChar* characters, size_t size) : m_impl(characters, size) { } + + unsigned sizeInBytes() const { return m_impl.size() * sizeof(UChar); } + const UChar* characters16() const { return m_impl.c_str(); } + std::string utf8() const; + static String16 fromUTF8(const char* stringStart, size_t length); + static String16 number(int i) { return String16(String16::intToString(i).c_str()); } + static String16 fromDouble(double d) { return String16(String16::doubleToString(d).c_str()); } + static String16 fromDoubleFixedPrecision(double d, int len) { return String16(String16::doubleToString(d).c_str()); } + + static double charactersToDouble(const UChar* characters, size_t length, bool* ok = 0) + { + std::string str; + str.resize(length); + for (size_t i = 0; i < length; ++i) + str[i] = static_cast<char>(characters[i]); + + const char* buffer = str.c_str(); + char* endptr; + double result = strtod(buffer, &endptr); + if (ok) + *ok = buffer + length == endptr; + return result; + } + + String16 substring(unsigned pos, unsigned len = 0xFFFFFFFF) const + { + return String16(m_impl.substr(pos, len)); + } + + String16 stripWhiteSpace() const; + + int toInt(bool* ok = 0) const + { + size_t length = m_impl.length(); + std::string str; + str.resize(length); + for (size_t i = 0; i < length; ++i) + str[i] = static_cast<char>(m_impl[i]); + + const char* buffer = str.c_str(); + char* endptr; + int result = strtol(buffer, &endptr, 10); + if (ok) + *ok = buffer + length == endptr; + return result; + } + + size_t length() const { return m_impl.length(); } + bool isEmpty() const { return !m_impl.length(); } + UChar operator[](unsigned index) const { return m_impl[index]; } + + size_t find(UChar c, unsigned start = 0) const + { + return m_impl.find(c, start); + } + + size_t find(const String16& str, unsigned start = 0) const + { + return m_impl.find(str.m_impl, start); + } + + size_t reverseFind(const String16& str, unsigned start = 0xFFFFFFFF) const + { + return m_impl.rfind(str.m_impl, start); + } + + bool endsWith(UChar character) const + { + return m_impl.length() && m_impl[m_impl.length() - 1] == character; + } + + // presubmit: allow wstring + const wstring& impl() const { return m_impl; } + + std::size_t hash() const + { + if (!has_hash) { + size_t hash = 0; + for (size_t i = 0; i < length(); ++i) + hash = 31 * hash + m_impl[i]; + hash_code = hash; + has_hash = true; + } + return hash_code; + } + +private: + static std::string intToString(int i); + static std::string doubleToString(double d); + // presubmit: allow wstring + wstring m_impl; + mutable bool has_hash = false; + mutable std::size_t hash_code; +}; + +static inline bool isSpaceOrNewline(UChar c) +{ + return false; +} + +class String16Builder { +public: + String16Builder() { } + + void append(const String16& str) + { + m_impl += str.impl(); + } + + void append(UChar c) + { + m_impl += c; + } + + void append(LChar c) + { + m_impl += c; + } + + void append(char c) + { + m_impl += c; + } + + void appendNumber(int i) + { + m_impl = m_impl + String16::number(i).impl(); + } + + void append(const UChar* c, size_t length) + { + // presubmit: allow wstring + m_impl += wstring(c, length); + } + + void append(const char* c, size_t length) + { + m_impl += String16(c, length).impl(); + } + + String16 toString() + { + return String16(m_impl); + } + + void reserveCapacity(unsigned newCapacity) + { + } + +private: + // presubmit: allow wstring + wstring m_impl; +}; + +inline bool operator==(const String16& a, const String16& b) { return a.impl() == b.impl(); } +inline bool operator!=(const String16& a, const String16& b) { return a.impl() != b.impl(); } +inline bool operator==(const String16& a, const char* b) { return a.impl() == String16(b).impl(); } +inline bool operator<(const String16& a, const String16& b) { return a.impl() < b.impl(); } + +inline String16 operator+(const String16& a, const char* b) +{ + return String16(a.impl() + String16(b).impl()); +} + +inline String16 operator+(const char* a, const String16& b) +{ + return String16(String16(a).impl() + b.impl()); +} + +inline String16 operator+(const String16& a, const String16& b) +{ + return String16(a.impl() + b.impl()); +} + +} // namespace protocol +} // namespace blink + +using String16 = blink::protocol::String16; +using String16Builder = blink::protocol::String16Builder; + + +namespace WTF { +// Interim solution for those headers that reference WTF::String for overrides. +// It does nothing. If the code actually relies on WTF:String, it will not +// compile! +// TODO(eostroukhov): Eradicate +class String { +public: + String() {}; + String(const String16& other) {}; + operator String16() const { return String16(); }; +}; +} // namespace WTF + +using String = WTF::String; + +#endif // !defined(String16STL_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/String16WTF.cpp b/deps/v8_inspector/platform/inspector_protocol/String16WTF.cpp new file mode 100644 index 00000000000000..387ecc3f48c45c --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/String16WTF.cpp @@ -0,0 +1,47 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/inspector_protocol/String16WTF.h" + +namespace blink { +namespace protocol { + +String16::String16(const String16& other) : m_impl(other.m_impl) { } + +String16::String16(const UChar* u, unsigned length) : m_impl(u, length) { } + +String16::String16(const char* characters) : String16(characters, strlen(characters)) { } + +String16::String16(const WTF::String& other) +{ + if (other.isNull()) + return; + if (!other.is8Bit()) { + m_impl = other; + return; + } + + UChar* data; + const LChar* characters = other.characters8(); + size_t length = other.length(); + m_impl = String::createUninitialized(length, data); + for (size_t i = 0; i < length; ++i) + data[i] = characters[i]; +} + +String16::String16(const char* characters, size_t length) +{ + UChar* data; + m_impl = String::createUninitialized(length, data); + for (size_t i = 0; i < length; ++i) + data[i] = characters[i]; +} + +String16 String16::createUninitialized(unsigned length, UChar*& data) +{ + return String::createUninitialized(length, data); +} + +} // namespace protocol +} // namespace blink diff --git a/deps/v8_inspector/platform/inspector_protocol/String16WTF.h b/deps/v8_inspector/platform/inspector_protocol/String16WTF.h new file mode 100644 index 00000000000000..4ee9c8df52db4e --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/String16WTF.h @@ -0,0 +1,139 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef String16WTF_h +#define String16WTF_h + +#include "platform/Decimal.h" +#include "public/platform/WebString.h" +#include "wtf/text/StringBuilder.h" +#include "wtf/text/StringConcatenate.h" +#include "wtf/text/StringHash.h" +#include "wtf/text/WTFString.h" + +namespace blink { +namespace protocol { + +class PLATFORM_EXPORT String16 { +public: + String16() { } + String16(const String16& other); + String16(const UChar*, unsigned); + String16(const char*); + String16(const char*, size_t); + static String16 createUninitialized(unsigned length, UChar*& data); + + // WTF convenience constructors and helper methods. + String16(const WebString& other) : String16(String(other)) { } + template<typename StringType1, typename StringType2> + String16(const WTF::StringAppend<StringType1, StringType2>& impl) : String16(String(impl)) { } + String16(const WTF::AtomicString& impl) : String16(String(impl)) { } + String16(const WTF::String& impl); + String16(WTF::HashTableDeletedValueType) : m_impl(WTF::HashTableDeletedValue) { } + bool isHashTableDeletedValue() const { return m_impl.isHashTableDeletedValue(); } + operator WTF::String() const { return m_impl; } + operator WebString() { return m_impl; } + const WTF::String& impl() const { return m_impl; } + + ~String16() { } + + static String16 number(int i) { return String::number(i); } + static String16 fromDouble(double number) { return Decimal::fromDouble(number).toString(); } + static String16 fromDoubleFixedPrecision(double number, int precision) { return String::numberToStringFixedWidth(number, precision); } + + size_t length() const { return m_impl.length(); } + bool isEmpty() const { return m_impl.isEmpty(); } + UChar operator[](unsigned index) const { return m_impl[index]; } + + unsigned sizeInBytes() const { return m_impl.sizeInBytes(); } + const UChar* characters16() const { return m_impl.isEmpty() ? nullptr : m_impl.characters16(); } + + static double charactersToDouble(const LChar* characters, size_t length, bool* ok = 0) { return ::charactersToDouble(characters, length, ok); } + static double charactersToDouble(const UChar* characters, size_t length, bool* ok = 0) { return ::charactersToDouble(characters, length, ok); } + + String16 substring(unsigned pos, unsigned len = UINT_MAX) const { return m_impl.substring(pos, len); } + String16 stripWhiteSpace() const { return m_impl.stripWhiteSpace(); } + + int toInt(bool* ok = 0) const { return m_impl.toInt(ok); } + + size_t find(UChar c, unsigned start = 0) const { return m_impl.find(c, start); } + size_t find(const String16& str, unsigned start = 0) const { return m_impl.find(str.impl(), start); } + size_t reverseFind(const String16& str, unsigned start = UINT_MAX) const { return m_impl.reverseFind(str.impl(), start); } + + bool startWith(const String16& s) const { return m_impl.startsWith(s); } + bool startWith(UChar character) const { return m_impl.startsWith(character); } + bool endsWith(const String16& s) const { return m_impl.endsWith(s); } + bool endsWith(UChar character) const { return m_impl.endsWith(character); } + +private: + WTF::String m_impl; +}; + +class String16Builder { +public: + String16Builder() { } + void append(const String16& str) { m_impl.append(str); }; + void append(UChar c) { m_impl.append(c); }; + void append(LChar c) { m_impl.append(c); }; + void append(char c) { m_impl.append(c); }; + void append(const UChar* c, size_t size) { m_impl.append(c, size); }; + void append(const char* characters, unsigned length) { m_impl.append(characters, length); } + void appendNumber(int number) { m_impl.appendNumber(number); } + String16 toString() { return m_impl.toString(); } + void reserveCapacity(unsigned newCapacity) { m_impl.reserveCapacity(newCapacity); } + +private: + WTF::StringBuilder m_impl; +}; + +inline bool operator==(const String16& a, const String16& b) { return a.impl() == b.impl(); } +inline bool operator!=(const String16& a, const String16& b) { return a.impl() != b.impl(); } +inline bool operator==(const String16& a, const char* b) { return a.impl() == b; } + +inline String16 operator+(const String16& a, const char* b) +{ + return String(a.impl() + b); +} + +inline String16 operator+(const char* a, const String16& b) +{ + return String(a + b.impl()); +} + +inline String16 operator+(const String16& a, const String16& b) +{ + return String(a.impl() + b.impl()); +} + +} // namespace protocol +} // namespace blink + +using String16 = blink::protocol::String16; +using String16Builder = blink::protocol::String16Builder; + +namespace WTF { + +struct String16Hash { + static unsigned hash(const String16& key) { return StringHash::hash(key.impl()); } + static bool equal(const String16& a, const String16& b) + { + return StringHash::equal(a.impl(), b.impl()); + } + static const bool safeToCompareToEmptyOrDeleted = false; +}; + +template<typename T> struct DefaultHash; +template<> struct DefaultHash<String16> { + typedef String16Hash Hash; +}; + +template<> +struct HashTraits<String16> : SimpleClassHashTraits<String16> { + static const bool hasIsEmptyValueFunction = true; + static bool isEmptyValue(const String16& a) { return a.impl().isNull(); } +}; + +} // namespace WTF + +#endif // !defined(String16WTF_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/TypeBuilder_cpp.template b/deps/v8_inspector/platform/inspector_protocol/TypeBuilder_cpp.template new file mode 100644 index 00000000000000..d80473a9b79f62 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/TypeBuilder_cpp.template @@ -0,0 +1,130 @@ +// This file is generated + +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/inspector_protocol/{{class_name}}.h" + +namespace blink { +namespace protocol { + +std::unique_ptr<Object> Object::parse(protocol::Value* value, ErrorSupport* errors) +{ + protocol::DictionaryValue* object = DictionaryValue::cast(value); + if (!object) { + errors->addError("object expected"); + return nullptr; + } + return wrapUnique(new Object(wrapUnique(static_cast<DictionaryValue*>(object->clone().release())))); +} + +std::unique_ptr<protocol::DictionaryValue> Object::serialize() const +{ + return DictionaryValue::cast(m_object->clone()); +} + +std::unique_ptr<Object> Object::clone() const +{ + return wrapUnique(new Object(DictionaryValue::cast(m_object->clone()))); +} + +Object::Object(std::unique_ptr<protocol::DictionaryValue> object) : m_object(std::move(object)) { } +Object::~Object() { } + +// ------------- Enum values from types. +{% for domain in api.domains %} + +namespace {{domain.domain}} { + {% for type in domain.types %} + {% if "enum" in type %} + +namespace {{type.id}}Enum { + {% for literal in type.enum %} +const char* {{ literal | dash_to_camelcase}} = "{{literal}}"; + {% endfor %} +} // {{type.id}}Enum + {% endif %} + {% for property in type.properties %} + {% if "enum" in property %} + + {% for literal in property.enum %} +const char* {{type.id}}::{{property.name | to_title_case}}Enum::{{ literal | dash_to_camelcase}} = "{{literal}}"; + {% endfor %} + {% endif %} + {% endfor %} + {% if not (type.type == "object") or not ("properties" in type) %}{% continue %}{% endif %} + +std::unique_ptr<{{type.id}}> {{type.id}}::parse(protocol::Value* value, ErrorSupport* errors) +{ + if (!value || value->type() != protocol::Value::TypeObject) { + errors->addError("object expected"); + return nullptr; + } + + std::unique_ptr<{{type.id}}> result(new {{type.id}}()); + protocol::DictionaryValue* object = DictionaryValue::cast(value); + errors->push(); + {% for property in type.properties %} + protocol::Value* {{property.name}}Value = object->get("{{property.name}}"); + {% if property.optional %} + if ({{property.name}}Value) { + errors->setName("{{property.name}}"); + result->m_{{property.name}} = FromValue<{{resolve_type(property).raw_type}}>::parse({{property.name}}Value, errors); + } + {% else %} + errors->setName("{{property.name}}"); + result->m_{{property.name}} = FromValue<{{resolve_type(property).raw_type}}>::parse({{property.name}}Value, errors); + {% endif %} + {% endfor %} + errors->pop(); + if (errors->hasErrors()) + return nullptr; + return result; +} + +std::unique_ptr<protocol::DictionaryValue> {{type.id}}::serialize() const +{ + std::unique_ptr<protocol::DictionaryValue> result = DictionaryValue::create(); + {% for property in type.properties %} + {% if property.optional %} + if (m_{{property.name}}.isJust()) + result->setValue("{{property.name}}", toValue(m_{{property.name}}.fromJust())); + {% else %} + result->setValue("{{property.name}}", toValue({{resolve_type(property).to_raw_type % ("m_" + property.name)}})); + {% endif %} + {% endfor %} + return result; +} + +std::unique_ptr<{{type.id}}> {{type.id}}::clone() const +{ + ErrorSupport errors; + return parse(serialize().get(), &errors); +} + {% endfor %} +} // {{domain.domain}} +{% endfor %} + +// ------------- Enum values from params. +{% for domain in api.domains %} + {% for command in join_arrays(domain, ["commands", "events"]) %} + {% for param in join_arrays(command, ["parameters", "returns"]) %} + {% if "enum" in param %} + +namespace {{domain.domain}} { +namespace {{command.name | to_title_case}} { +namespace {{param.name | to_title_case}}Enum { + {% for literal in param.enum %} +const char* {{ literal | to_title_case}} = "{{literal}}"; + {% endfor %} +} // {{param.name | to_title_case}}Enum +} // {{command.name | to_title_case }} +} // {{domain.domain}} + {% endif %} + {% endfor %} + {% endfor %} +{% endfor %} + +} // namespace protocol +} // namespace blink diff --git a/deps/v8_inspector/platform/inspector_protocol/TypeBuilder_h.template b/deps/v8_inspector/platform/inspector_protocol/TypeBuilder_h.template new file mode 100644 index 00000000000000..75f67043282b63 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/TypeBuilder_h.template @@ -0,0 +1,211 @@ +// This file is generated + +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef {{class_name}}_h +#define {{class_name}}_h + +#include "platform/PlatformExport.h" +#include "platform/inspector_protocol/Array.h" +#include "platform/inspector_protocol/ErrorSupport.h" +#include "platform/inspector_protocol/Maybe.h" +#include "platform/inspector_protocol/String16.h" +#include "platform/inspector_protocol/Values.h" +#include "platform/inspector_protocol/ValueConversions.h" +#include "wtf/Assertions.h" +#include "wtf/PtrUtil.h" + +namespace blink { +namespace protocol { + +using ErrorString = String16; + +class PLATFORM_EXPORT Object { +public: + static std::unique_ptr<Object> parse(protocol::Value* value, ErrorSupport* errors); + ~Object(); + + std::unique_ptr<protocol::DictionaryValue> serialize() const; + std::unique_ptr<Object> clone() const; +private: + Object(std::unique_ptr<protocol::DictionaryValue> object); + std::unique_ptr<protocol::DictionaryValue> m_object; +}; + +{% for domain in api.domains %} + +// ------------- Forward declarations and typedefs. + +namespace {{domain.domain}} { + {% for type in domain.types %} + {% if type.type == "object" %} + {% if "properties" in type %} +// {{type.description}} +class {{type.id}}; + {% else %} +// {{type.description}} +using {{type.id}} = Object; + {% endif %} + {% elif type.type != "array" %} +// {{type.description}} +using {{type.id}} = {{resolve_type(type).type}}; + {% endif %} + {% endfor %} +} // {{domain.domain}} +{% endfor %} + +// ------------- Enum values from types. +{% for domain in api.domains %} + {% for type in domain.types %} + {% if "enum" in type %} + +namespace {{domain.domain}} { +namespace {{type.id}}Enum { + {% for literal in type.enum %} +PLATFORM_EXPORT extern const char* {{ literal | dash_to_camelcase}}; + {% endfor %} +} // {{type.id}}Enum +} // {{domain.domain}} + {% endif %} + {% endfor %} +{% endfor %} + +// ------------- Enum values from params. +{% for domain in api.domains %} + {% for command in join_arrays(domain, ["commands", "events"]) %} + {% for param in join_arrays(command, ["parameters", "returns"]) %} + {% if "enum" in param %} + +namespace {{domain.domain}} { +namespace {{command.name | to_title_case}} { +namespace {{param.name | to_title_case}}Enum { + {% for literal in param.enum %} +PLATFORM_EXPORT extern const char* {{ literal | dash_to_camelcase}}; + {% endfor %} +} // {{param.name | to_title_case}}Enum +} // {{command.name | to_title_case }} +} // {{domain.domain}} + {% endif %} + {% endfor %} + {% endfor %} +{% endfor %} + +// ------------- Type and builder declarations. +{% for domain in api.domains %} + +namespace {{domain.domain}} { + {% for type in domain.types %} + {% if not (type.type == "object") or not ("properties" in type) %}{% continue %}{% endif %} + {% set type_def = type_definition(domain.domain + "." + type.id)%} + +// {{type.description}} +class PLATFORM_EXPORT {{type.id}} { +public: + static std::unique_ptr<{{type.id}}> parse(protocol::Value* value, ErrorSupport* errors); + + ~{{type.id}}() { } + {% for property in type.properties %} + {% if "enum" in property %} + + struct PLATFORM_EXPORT {{property.name | to_title_case}}Enum { + {% for literal in property.enum %} + static const char* {{ literal | dash_to_camelcase}}; + {% endfor %} + }; // {{property.name | to_title_case}}Enum + {% endif %} + + {% if property.optional %} + bool has{{property.name | to_title_case}}() { return m_{{property.name}}.isJust(); } + {{resolve_type(property).raw_return_type}} get{{property.name | to_title_case}}({{resolve_type(property).raw_pass_type}} defaultValue) { return m_{{property.name}}.isJust() ? m_{{property.name}}.fromJust() : defaultValue; } + {% else %} + {{resolve_type(property).raw_return_type}} get{{property.name | to_title_case}}() { return {{resolve_type(property).to_raw_type % ("m_" + property.name)}}; } + {% endif %} + void set{{property.name | to_title_case}}({{resolve_type(property).pass_type}} value) { m_{{property.name}} = {{resolve_type(property).to_rvalue % "value"}}; } + {% endfor %} + + std::unique_ptr<protocol::DictionaryValue> serialize() const; + std::unique_ptr<{{type.id}}> clone() const; + + template<int STATE> + class {{type.id}}Builder { + public: + enum { + NoFieldsSet = 0, + {% set count = 0 %} + {% for property in type.properties %} + {% if not(property.optional) %} + {% set count = count + 1 %} + {{property.name | to_title_case}}Set = 1 << {{count}}, + {% endif %} + {% endfor %} + AllFieldsSet = ( + {%- for property in type.properties %} + {% if not(property.optional) %}{{property.name | to_title_case}}Set | {%endif %} + {% endfor %}0)}; + + {% for property in type.properties %} + + {% if property.optional %} + {{type.id}}Builder<STATE>& set{{property.name | to_title_case}}({{resolve_type(property).pass_type}} value) + { + m_result->set{{property.name | to_title_case}}({{resolve_type(property).to_rvalue % "value"}}); + return *this; + } + {% else %} + {{type.id}}Builder<STATE | {{property.name | to_title_case}}Set>& set{{property.name | to_title_case}}({{resolve_type(property).pass_type}} value) + { + static_assert(!(STATE & {{property.name | to_title_case}}Set), "property {{property.name}} should not be set yet"); + m_result->set{{property.name | to_title_case}}({{resolve_type(property).to_rvalue % "value"}}); + return castState<{{property.name | to_title_case}}Set>(); + } + {% endif %} + {% endfor %} + + std::unique_ptr<{{type.id}}> build() + { + static_assert(STATE == AllFieldsSet, "state should be AllFieldsSet"); + return std::move(m_result); + } + + private: + friend class {{type.id}}; + {{type.id}}Builder() : m_result(new {{type.id}}()) { } + + template<int STEP> {{type.id}}Builder<STATE | STEP>& castState() + { + return *reinterpret_cast<{{type.id}}Builder<STATE | STEP>*>(this); + } + + {{type_def.type}} m_result; + }; + + static {{type.id}}Builder<0> create() + { + return {{type.id}}Builder<0>(); + } + +private: + {{type.id}}() { } + + {% for property in type.properties %} + {% if property.optional %} + Maybe<{{resolve_type(property).raw_type}}> m_{{property.name}}; + {% else %} + {{resolve_type(property).type}} m_{{property.name}}; + {% endif %} + {% endfor %} +}; + + {% endfor %} + +} // {{domain.domain}} +{% endfor %} + +} // namespace protocol +} // namespace blink + +using blink::protocol::ErrorString; + +#endif // !defined({{class_name}}_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/ValueConversions.cpp b/deps/v8_inspector/platform/inspector_protocol/ValueConversions.cpp new file mode 100644 index 00000000000000..98b8e7f5dff48d --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/ValueConversions.cpp @@ -0,0 +1,51 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/inspector_protocol/ValueConversions.h" + +namespace blink { +namespace protocol { + +std::unique_ptr<protocol::Value> toValue(int value) +{ + return FundamentalValue::create(value); +} + +std::unique_ptr<protocol::Value> toValue(double value) +{ + return FundamentalValue::create(value); +} + +std::unique_ptr<protocol::Value> toValue(bool value) +{ + return FundamentalValue::create(value); +} + +std::unique_ptr<protocol::Value> toValue(const String16& param) +{ + return StringValue::create(param); +} + +std::unique_ptr<protocol::Value> toValue(const String& param) +{ + return StringValue::create(param); +} + +std::unique_ptr<protocol::Value> toValue(Value* param) +{ + return param->clone(); +} + +std::unique_ptr<protocol::Value> toValue(DictionaryValue* param) +{ + return param->clone(); +} + +std::unique_ptr<protocol::Value> toValue(ListValue* param) +{ + return param->clone(); +} + +} // namespace protocol +} // namespace blink diff --git a/deps/v8_inspector/platform/inspector_protocol/ValueConversions.h b/deps/v8_inspector/platform/inspector_protocol/ValueConversions.h new file mode 100644 index 00000000000000..4a5066c928661f --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/ValueConversions.h @@ -0,0 +1,163 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ValueConversions_h +#define ValueConversions_h + +#include "platform/PlatformExport.h" +#include "platform/inspector_protocol/ErrorSupport.h" +#include "platform/inspector_protocol/String16.h" +#include "platform/inspector_protocol/Values.h" + +namespace blink { +namespace protocol { + +PLATFORM_EXPORT std::unique_ptr<protocol::Value> toValue(int value); + +PLATFORM_EXPORT std::unique_ptr<protocol::Value> toValue(double value); + +PLATFORM_EXPORT std::unique_ptr<protocol::Value> toValue(bool value); + +PLATFORM_EXPORT std::unique_ptr<protocol::Value> toValue(const String16& param); + +PLATFORM_EXPORT std::unique_ptr<protocol::Value> toValue(const String& param); + +PLATFORM_EXPORT std::unique_ptr<protocol::Value> toValue(protocol::Value* param); + +PLATFORM_EXPORT std::unique_ptr<protocol::Value> toValue(protocol::DictionaryValue* param); + +PLATFORM_EXPORT std::unique_ptr<protocol::Value> toValue(protocol::ListValue* param); + +template<typename T> std::unique_ptr<protocol::Value> toValue(T* param) +{ + return param->serialize(); +} + +template<typename T> std::unique_ptr<protocol::Value> toValue(const std::unique_ptr<T>& param) +{ + static_assert(sizeof(T) == 0, "use raw pointer version."); + return nullptr; +} + +template<typename T> std::unique_ptr<protocol::Value> toValue(std::unique_ptr<T> param) +{ + static_assert(sizeof(T) == 0, "use raw pointer version."); + return nullptr; +} + +template<typename T> +struct FromValue { + static std::unique_ptr<T> parse(protocol::Value* value, ErrorSupport* errors) + { + return T::parse(value, errors); + } +}; + +template<> +struct FromValue<bool> { + static bool parse(protocol::Value* value, ErrorSupport* errors) + { + bool result = false; + bool success = value ? value->asBoolean(&result) : false; + if (!success) + errors->addError("boolean value expected"); + return result; + } +}; + +template<> +struct FromValue<int> { + static int parse(protocol::Value* value, ErrorSupport* errors) + { + int result = 0; + bool success = value ? value->asNumber(&result) : false; + if (!success) + errors->addError("integer value expected"); + return result; + } +}; + +template<> +struct FromValue<double> { + static double parse(protocol::Value* value, ErrorSupport* errors) + { + double result = 0; + bool success = value ? value->asNumber(&result) : false; + if (!success) + errors->addError("double value expected"); + return result; + } +}; + +template<> +struct FromValue<String> { + static String parse(protocol::Value* value, ErrorSupport* errors) + { + String16 result; + bool success = value ? value->asString(&result) : false; + if (!success) + errors->addError("string value expected"); + return result; + } +}; + +template<> +struct FromValue<String16> { + static String16 parse(protocol::Value* value, ErrorSupport* errors) + { + String16 result; + bool success = value ? value->asString(&result) : false; + if (!success) + errors->addError("string value expected"); + return result; + } +}; + +template<> +struct FromValue<Value> { + static std::unique_ptr<Value> parse(protocol::Value* value, ErrorSupport* errors) + { + bool success = !!value; + if (!success) + errors->addError("value expected"); + return value->clone(); + } +}; + +template<> +struct FromValue<DictionaryValue> { + static std::unique_ptr<DictionaryValue> parse(protocol::Value* value, ErrorSupport* errors) + { + bool success = value && value->type() == protocol::Value::TypeObject; + if (!success) + errors->addError("object expected"); + return DictionaryValue::cast(value->clone()); + } +}; + +template<> +struct FromValue<ListValue> { + static std::unique_ptr<ListValue> parse(protocol::Value* value, ErrorSupport* errors) + { + bool success = value && value->type() == protocol::Value::TypeArray; + if (!success) + errors->addError("list expected"); + return ListValue::cast(value->clone()); + } +}; + +template<typename T> class Array; + +template<typename T> +struct FromValue<protocol::Array<T>> { + static std::unique_ptr<protocol::Array<T>> parse(protocol::Value* value, ErrorSupport* errors) + { + return protocol::Array<T>::parse(value, errors); + } +}; + +} // namespace platform +} // namespace blink + +#endif // !defined(ValueConversions_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/Values.cpp b/deps/v8_inspector/platform/inspector_protocol/Values.cpp new file mode 100644 index 00000000000000..979e3d8b2b3bba --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/Values.cpp @@ -0,0 +1,354 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/inspector_protocol/Values.h" + +#include "platform/inspector_protocol/Parser.h" +#include "platform/inspector_protocol/String16.h" +#include "wtf/Assertions.h" +#include <cmath> + +namespace blink { +namespace protocol { + +namespace { + +const char* const nullString = "null"; +const char* const trueString = "true"; +const char* const falseString = "false"; + +inline bool escapeChar(UChar c, String16Builder* dst) +{ + switch (c) { + case '\b': dst->append("\\b"); break; + case '\f': dst->append("\\f"); break; + case '\n': dst->append("\\n"); break; + case '\r': dst->append("\\r"); break; + case '\t': dst->append("\\t"); break; + case '\\': dst->append("\\\\"); break; + case '"': dst->append("\\\""); break; + default: + return false; + } + return true; +} + +const LChar hexDigits[17] = "0123456789ABCDEF"; + +void appendUnsignedAsHex(UChar number, String16Builder* dst) +{ + dst->append("\\u"); + for (size_t i = 0; i < 4; ++i) { + dst->append(hexDigits[(number & 0xF000) >> 12]); + number <<= 4; + } +} + +void escapeStringForJSON(const String16& str, String16Builder* dst) +{ + for (unsigned i = 0; i < str.length(); ++i) { + UChar c = str[i]; + if (!escapeChar(c, dst)) { + if (c < 32 || c > 126 || c == '<' || c == '>') { + // 1. Escaping <, > to prevent script execution. + // 2. Technically, we could also pass through c > 126 as UTF8, but this + // is also optional. It would also be a pain to implement here. + appendUnsignedAsHex(c, dst); + } else { + dst->append(c); + } + } + } +} + +void doubleQuoteStringForJSON(const String16& str, String16Builder* dst) +{ + dst->append('"'); + escapeStringForJSON(str, dst); + dst->append('"'); +} + +} // anonymous namespace + +bool Value::asBoolean(bool*) const +{ + return false; +} + +bool Value::asNumber(double*) const +{ + return false; +} + +bool Value::asNumber(int*) const +{ + return false; +} + +bool Value::asString(String16*) const +{ + return false; +} + +String16 Value::toJSONString() const +{ + String16Builder result; + result.reserveCapacity(512); + writeJSON(&result); + return result.toString(); +} + +void Value::writeJSON(String16Builder* output) const +{ + DCHECK(m_type == TypeNull); + output->append(nullString, 4); +} + +std::unique_ptr<Value> Value::clone() const +{ + return Value::null(); +} + +bool FundamentalValue::asBoolean(bool* output) const +{ + if (type() != TypeBoolean) + return false; + *output = m_boolValue; + return true; +} + +bool FundamentalValue::asNumber(double* output) const +{ + if (type() != TypeNumber) + return false; + *output = m_doubleValue; + return true; +} + +bool FundamentalValue::asNumber(int* output) const +{ + if (type() != TypeNumber) + return false; + *output = static_cast<int>(m_doubleValue); + return true; +} + +void FundamentalValue::writeJSON(String16Builder* output) const +{ + DCHECK(type() == TypeBoolean || type() == TypeNumber); + if (type() == TypeBoolean) { + if (m_boolValue) + output->append(trueString, 4); + else + output->append(falseString, 5); + } else if (type() == TypeNumber) { + if (!std::isfinite(m_doubleValue)) { + output->append(nullString, 4); + return; + } + output->append(String16::fromDouble(m_doubleValue)); + } +} + +std::unique_ptr<Value> FundamentalValue::clone() const +{ + return type() == TypeNumber ? FundamentalValue::create(m_doubleValue) : FundamentalValue::create(m_boolValue); +} + +bool StringValue::asString(String16* output) const +{ + *output = m_stringValue; + return true; +} + +void StringValue::writeJSON(String16Builder* output) const +{ + DCHECK(type() == TypeString); + doubleQuoteStringForJSON(m_stringValue, output); +} + +std::unique_ptr<Value> StringValue::clone() const +{ + return StringValue::create(m_stringValue); +} + +DictionaryValue::~DictionaryValue() +{ +} + +void DictionaryValue::setBoolean(const String16& name, bool value) +{ + setValue(name, FundamentalValue::create(value)); +} + +void DictionaryValue::setNumber(const String16& name, double value) +{ + setValue(name, FundamentalValue::create(value)); +} + +void DictionaryValue::setString(const String16& name, const String16& value) +{ + setValue(name, StringValue::create(value)); +} + +void DictionaryValue::setValue(const String16& name, std::unique_ptr<Value> value) +{ + DCHECK(value); + if (m_data.set(name, std::move(value))) + m_order.append(name); +} + +void DictionaryValue::setObject(const String16& name, std::unique_ptr<DictionaryValue> value) +{ + DCHECK(value); + if (m_data.set(name, std::move(value))) + m_order.append(name); +} + +void DictionaryValue::setArray(const String16& name, std::unique_ptr<ListValue> value) +{ + DCHECK(value); + if (m_data.set(name, std::move(value))) + m_order.append(name); +} + +bool DictionaryValue::getBoolean(const String16& name, bool* output) const +{ + protocol::Value* value = get(name); + if (!value) + return false; + return value->asBoolean(output); +} + +bool DictionaryValue::getString(const String16& name, String16* output) const +{ + protocol::Value* value = get(name); + if (!value) + return false; + return value->asString(output); +} + +DictionaryValue* DictionaryValue::getObject(const String16& name) const +{ + return DictionaryValue::cast(get(name)); +} + +protocol::ListValue* DictionaryValue::getArray(const String16& name) const +{ + return ListValue::cast(get(name)); +} + +protocol::Value* DictionaryValue::get(const String16& name) const +{ + Dictionary::const_iterator it = m_data.find(name); + if (it == m_data.end()) + return nullptr; + return it->second; +} + +DictionaryValue::Entry DictionaryValue::at(size_t index) const +{ + String16 key = m_order[index]; + return std::make_pair(key, m_data.get(key)); +} + +bool DictionaryValue::booleanProperty(const String16& name, bool defaultValue) const +{ + bool result = defaultValue; + getBoolean(name, &result); + return result; +} + +double DictionaryValue::numberProperty(const String16& name, double defaultValue) const +{ + double result = defaultValue; + getNumber(name, &result); + return result; +} + +void DictionaryValue::remove(const String16& name) +{ + m_data.remove(name); + for (size_t i = 0; i < m_order.size(); ++i) { + if (m_order[i] == name) { + m_order.remove(i); + break; + } + } +} + +void DictionaryValue::writeJSON(String16Builder* output) const +{ + output->append('{'); + for (size_t i = 0; i < m_order.size(); ++i) { + Dictionary::const_iterator it = m_data.find(m_order[i]); + CHECK(it != m_data.end()); + if (i) + output->append(','); + doubleQuoteStringForJSON(it->first, output); + output->append(':'); + it->second->writeJSON(output); + } + output->append('}'); +} + +std::unique_ptr<Value> DictionaryValue::clone() const +{ + std::unique_ptr<DictionaryValue> result = DictionaryValue::create(); + for (size_t i = 0; i < m_order.size(); ++i) { + String16 key = m_order[i]; + Value* value = m_data.get(key); + DCHECK(value); + result->setValue(key, value->clone()); + } + return std::move(result); +} + +DictionaryValue::DictionaryValue() + : Value(TypeObject) +{ +} + +ListValue::~ListValue() +{ +} + +void ListValue::writeJSON(String16Builder* output) const +{ + output->append('['); + for (Vector<std::unique_ptr<protocol::Value>>::const_iterator it = m_data.begin(); it != m_data.end(); ++it) { + if (it != m_data.begin()) + output->append(','); + (*it)->writeJSON(output); + } + output->append(']'); +} + +std::unique_ptr<Value> ListValue::clone() const +{ + std::unique_ptr<ListValue> result = ListValue::create(); + for (Vector<std::unique_ptr<protocol::Value>>::const_iterator it = m_data.begin(); it != m_data.end(); ++it) + result->pushValue((*it)->clone()); + return std::move(result); +} + +ListValue::ListValue() + : Value(TypeArray) +{ +} + +void ListValue::pushValue(std::unique_ptr<protocol::Value> value) +{ + DCHECK(value); + m_data.append(std::move(value)); +} + +protocol::Value* ListValue::at(size_t index) +{ + CHECK(index < m_data.size()); + return m_data[index]; +} + +} // namespace protocol +} // namespace blink diff --git a/deps/v8_inspector/platform/inspector_protocol/Values.h b/deps/v8_inspector/platform/inspector_protocol/Values.h new file mode 100644 index 00000000000000..4202d33ec87dba --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/Values.h @@ -0,0 +1,221 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef Values_h +#define Values_h + +#include "platform/PlatformExport.h" +#include "platform/inspector_protocol/Allocator.h" +#include "platform/inspector_protocol/Collections.h" +#include "platform/inspector_protocol/String16.h" +#include "wtf/PtrUtil.h" + +namespace blink { +namespace protocol { + +class ListValue; +class DictionaryValue; +class Value; + +class PLATFORM_EXPORT Value { + PROTOCOL_DISALLOW_COPY(Value); +public: + static const int maxDepth = 1000; + + virtual ~Value() { } + + static std::unique_ptr<Value> null() + { + return std::unique_ptr<Value>(new Value()); + } + + enum ValueType { + TypeNull = 0, + TypeBoolean, + TypeNumber, + TypeString, + TypeObject, + TypeArray + }; + + ValueType type() const { return m_type; } + + bool isNull() const { return m_type == TypeNull; } + + virtual bool asBoolean(bool* output) const; + virtual bool asNumber(double* output) const; + virtual bool asNumber(int* output) const; + virtual bool asString(String16* output) const; + + String16 toJSONString() const; + virtual void writeJSON(String16Builder* output) const; + virtual std::unique_ptr<Value> clone() const; + +protected: + Value() : m_type(TypeNull) { } + explicit Value(ValueType type) : m_type(type) { } + +private: + friend class DictionaryValue; + friend class ListValue; + + ValueType m_type; +}; + +class PLATFORM_EXPORT FundamentalValue : public Value { +public: + static std::unique_ptr<FundamentalValue> create(bool value) + { + return wrapUnique(new FundamentalValue(value)); + } + + static std::unique_ptr<FundamentalValue> create(int value) + { + return wrapUnique(new FundamentalValue(value)); + } + + static std::unique_ptr<FundamentalValue> create(double value) + { + return wrapUnique(new FundamentalValue(value)); + } + + bool asBoolean(bool* output) const override; + bool asNumber(double* output) const override; + bool asNumber(int* output) const override; + void writeJSON(String16Builder* output) const override; + std::unique_ptr<Value> clone() const override; + +private: + explicit FundamentalValue(bool value) : Value(TypeBoolean), m_boolValue(value) { } + explicit FundamentalValue(int value) : Value(TypeNumber), m_doubleValue((double)value) { } + explicit FundamentalValue(double value) : Value(TypeNumber), m_doubleValue(value) { } + + union { + bool m_boolValue; + double m_doubleValue; + }; +}; + +class PLATFORM_EXPORT StringValue : public Value { +public: + static std::unique_ptr<StringValue> create(const String16& value) + { + return wrapUnique(new StringValue(value)); + } + + static std::unique_ptr<StringValue> create(const char* value) + { + return wrapUnique(new StringValue(value)); + } + + bool asString(String16* output) const override; + void writeJSON(String16Builder* output) const override; + std::unique_ptr<Value> clone() const override; + +private: + explicit StringValue(const String16& value) : Value(TypeString), m_stringValue(value) { } + explicit StringValue(const char* value) : Value(TypeString), m_stringValue(value) { } + + String16 m_stringValue; +}; + +class PLATFORM_EXPORT DictionaryValue : public Value { +public: + using Entry = std::pair<String16, Value*>; + static std::unique_ptr<DictionaryValue> create() + { + return wrapUnique(new DictionaryValue()); + } + + static DictionaryValue* cast(Value* value) + { + if (!value || value->type() != TypeObject) + return nullptr; + return static_cast<DictionaryValue*>(value); + } + + static std::unique_ptr<DictionaryValue> cast(std::unique_ptr<Value> value) + { + return wrapUnique(DictionaryValue::cast(value.release())); + } + + void writeJSON(String16Builder* output) const override; + std::unique_ptr<Value> clone() const override; + + size_t size() const { return m_data.size(); } + + void setBoolean(const String16& name, bool); + void setNumber(const String16& name, double); + void setString(const String16& name, const String16&); + void setValue(const String16& name, std::unique_ptr<Value>); + void setObject(const String16& name, std::unique_ptr<DictionaryValue>); + void setArray(const String16& name, std::unique_ptr<ListValue>); + + bool getBoolean(const String16& name, bool* output) const; + template<class T> bool getNumber(const String16& name, T* output) const + { + Value* value = get(name); + if (!value) + return false; + return value->asNumber(output); + } + bool getString(const String16& name, String16* output) const; + + DictionaryValue* getObject(const String16& name) const; + ListValue* getArray(const String16& name) const; + Value* get(const String16& name) const; + Entry at(size_t index) const; + + bool booleanProperty(const String16& name, bool defaultValue) const; + double numberProperty(const String16& name, double defaultValue) const; + void remove(const String16& name); + + ~DictionaryValue() override; + +private: + DictionaryValue(); + + using Dictionary = protocol::HashMap<String16, std::unique_ptr<Value>>; + Dictionary m_data; + protocol::Vector<String16> m_order; +}; + +class PLATFORM_EXPORT ListValue : public Value { +public: + static std::unique_ptr<ListValue> create() + { + return wrapUnique(new ListValue()); + } + + static ListValue* cast(Value* value) + { + if (!value || value->type() != TypeArray) + return nullptr; + return static_cast<ListValue*>(value); + } + + static std::unique_ptr<ListValue> cast(std::unique_ptr<Value> value) + { + return wrapUnique(ListValue::cast(value.release())); + } + + ~ListValue() override; + + void writeJSON(String16Builder* output) const override; + std::unique_ptr<Value> clone() const override; + + void pushValue(std::unique_ptr<Value>); + + Value* at(size_t index); + size_t size() const { return m_data.size(); } + +private: + ListValue(); + protocol::Vector<std::unique_ptr<Value>> m_data; +}; + +} // namespace protocol +} // namespace blink + +#endif // Values_h diff --git a/deps/v8_inspector/platform/inspector_protocol/generate-inspector-protocol-version b/deps/v8_inspector/platform/inspector_protocol/generate-inspector-protocol-version new file mode 100755 index 00000000000000..514b6dfca43d15 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/generate-inspector-protocol-version @@ -0,0 +1,464 @@ +#!/usr/bin/env python +# Copyright (c) 2011 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Inspector protocol validator. +# +# Tests that subsequent protocol changes are not breaking backwards compatibility. +# Following violations are reported: +# +# - Domain has been removed +# - Command has been removed +# - Required command parameter was added or changed from optional +# - Required response parameter was removed or changed to optional +# - Event has been removed +# - Required event parameter was removed or changed to optional +# - Parameter type has changed. +# +# For the parameters with composite types the above checks are also applied +# recursively to every property of the type. +# +# Adding --show_changes to the command line prints out a list of valid public API changes. + +import os.path +import re +import sys + +def list_to_map(items, key): + result = {} + for item in items: + if not "hidden" in item: + result[item[key]] = item + return result + +def named_list_to_map(container, name, key): + if name in container: + return list_to_map(container[name], key) + return {} + +def removed(reverse): + if reverse: + return "added" + return "removed" + +def required(reverse): + if reverse: + return "optional" + return "required" + +def compare_schemas(schema_1, schema_2, reverse): + errors = [] + types_1 = normalize_types_in_schema(schema_1) + types_2 = normalize_types_in_schema(schema_2) + + domains_by_name_1 = list_to_map(schema_1, "domain") + domains_by_name_2 = list_to_map(schema_2, "domain") + + for name in domains_by_name_1: + domain_1 = domains_by_name_1[name] + if not name in domains_by_name_2: + errors.append("%s: domain has been %s" % (name, removed(reverse))) + continue + compare_domains(domain_1, domains_by_name_2[name], types_1, types_2, errors, reverse) + return errors + +def compare_domains(domain_1, domain_2, types_map_1, types_map_2, errors, reverse): + domain_name = domain_1["domain"] + commands_1 = named_list_to_map(domain_1, "commands", "name") + commands_2 = named_list_to_map(domain_2, "commands", "name") + for name in commands_1: + command_1 = commands_1[name] + if not name in commands_2: + errors.append("%s.%s: command has been %s" % (domain_1["domain"], name, removed(reverse))) + continue + compare_commands(domain_name, command_1, commands_2[name], types_map_1, types_map_2, errors, reverse) + + events_1 = named_list_to_map(domain_1, "events", "name") + events_2 = named_list_to_map(domain_2, "events", "name") + for name in events_1: + event_1 = events_1[name] + if not name in events_2: + errors.append("%s.%s: event has been %s" % (domain_1["domain"], name, removed(reverse))) + continue + compare_events(domain_name, event_1, events_2[name], types_map_1, types_map_2, errors, reverse) + +def compare_commands(domain_name, command_1, command_2, types_map_1, types_map_2, errors, reverse): + context = domain_name + "." + command_1["name"] + + params_1 = named_list_to_map(command_1, "parameters", "name") + params_2 = named_list_to_map(command_2, "parameters", "name") + # Note the reversed order: we allow removing but forbid adding parameters. + compare_params_list(context, "parameter", params_2, params_1, types_map_2, types_map_1, 0, errors, not reverse) + + returns_1 = named_list_to_map(command_1, "returns", "name") + returns_2 = named_list_to_map(command_2, "returns", "name") + compare_params_list(context, "response parameter", returns_1, returns_2, types_map_1, types_map_2, 0, errors, reverse) + +def compare_events(domain_name, event_1, event_2, types_map_1, types_map_2, errors, reverse): + context = domain_name + "." + event_1["name"] + params_1 = named_list_to_map(event_1, "parameters", "name") + params_2 = named_list_to_map(event_2, "parameters", "name") + compare_params_list(context, "parameter", params_1, params_2, types_map_1, types_map_2, 0, errors, reverse) + +def compare_params_list(context, kind, params_1, params_2, types_map_1, types_map_2, depth, errors, reverse): + for name in params_1: + param_1 = params_1[name] + if not name in params_2: + if not "optional" in param_1: + errors.append("%s.%s: required %s has been %s" % (context, name, kind, removed(reverse))) + continue + + param_2 = params_2[name] + if param_2 and "optional" in param_2 and not "optional" in param_1: + errors.append("%s.%s: %s %s is now %s" % (context, name, required(reverse), kind, required(not reverse))) + continue + type_1 = extract_type(param_1, types_map_1, errors) + type_2 = extract_type(param_2, types_map_2, errors) + compare_types(context + "." + name, kind, type_1, type_2, types_map_1, types_map_2, depth, errors, reverse) + +def compare_types(context, kind, type_1, type_2, types_map_1, types_map_2, depth, errors, reverse): + if depth > 10: + return + + base_type_1 = type_1["type"] + base_type_2 = type_2["type"] + + if base_type_1 != base_type_2: + errors.append("%s: %s base type mismatch, '%s' vs '%s'" % (context, kind, base_type_1, base_type_2)) + elif base_type_1 == "object": + params_1 = named_list_to_map(type_1, "properties", "name") + params_2 = named_list_to_map(type_2, "properties", "name") + # If both parameters have the same named type use it in the context. + if "id" in type_1 and "id" in type_2 and type_1["id"] == type_2["id"]: + type_name = type_1["id"] + else: + type_name = "<object>" + context += " %s->%s" % (kind, type_name) + compare_params_list(context, "property", params_1, params_2, types_map_1, types_map_2, depth + 1, errors, reverse) + elif base_type_1 == "array": + item_type_1 = extract_type(type_1["items"], types_map_1, errors) + item_type_2 = extract_type(type_2["items"], types_map_2, errors) + compare_types(context, kind, item_type_1, item_type_2, types_map_1, types_map_2, depth + 1, errors, reverse) + +def extract_type(typed_object, types_map, errors): + if "type" in typed_object: + result = { "id": "<transient>", "type": typed_object["type"] } + if typed_object["type"] == "object": + result["properties"] = [] + elif typed_object["type"] == "array": + result["items"] = typed_object["items"] + return result + elif "$ref" in typed_object: + ref = typed_object["$ref"] + if not ref in types_map: + errors.append("Can not resolve type: %s" % ref) + types_map[ref] = { "id": "<transient>", "type": "object" } + return types_map[ref] + +def normalize_types_in_schema(schema): + types = {} + for domain in schema: + domain_name = domain["domain"] + normalize_types(domain, domain_name, types) + return types + +def normalize_types(obj, domain_name, types): + if isinstance(obj, list): + for item in obj: + normalize_types(item, domain_name, types) + elif isinstance(obj, dict): + for key, value in obj.items(): + if key == "$ref" and value.find(".") == -1: + obj[key] = "%s.%s" % (domain_name, value) + elif key == "id": + obj[key] = "%s.%s" % (domain_name, value) + types[obj[key]] = obj + else: + normalize_types(value, domain_name, types) + +def load_json(filename): + input_file = open(filename, "r") + json_string = input_file.read() + json_string = re.sub(":\s*true", ": True", json_string) + json_string = re.sub(":\s*false", ": False", json_string) + return eval(json_string) + +def self_test(): + def create_test_schema_1(): + return [ + { + "domain": "Network", + "types": [ + { + "id": "LoaderId", + "type": "string" + }, + { + "id": "Headers", + "type": "object" + }, + { + "id": "Request", + "type": "object", + "properties": [ + { "name": "url", "type": "string" }, + { "name": "method", "type": "string" }, + { "name": "headers", "$ref": "Headers" }, + { "name": "becameOptionalField", "type": "string" }, + { "name": "removedField", "type": "string" }, + ] + } + ], + "commands": [ + { + "name": "removedCommand", + }, + { + "name": "setExtraHTTPHeaders", + "parameters": [ + { "name": "headers", "$ref": "Headers" }, + { "name": "mismatched", "type": "string" }, + { "name": "becameOptional", "$ref": "Headers" }, + { "name": "removedRequired", "$ref": "Headers" }, + { "name": "becameRequired", "$ref": "Headers", "optional": True }, + { "name": "removedOptional", "$ref": "Headers", "optional": True }, + ], + "returns": [ + { "name": "mimeType", "type": "string" }, + { "name": "becameOptional", "type": "string" }, + { "name": "removedRequired", "type": "string" }, + { "name": "becameRequired", "type": "string", "optional": True }, + { "name": "removedOptional", "type": "string", "optional": True }, + ] + } + ], + "events": [ + { + "name": "requestWillBeSent", + "parameters": [ + { "name": "frameId", "type": "string", "hidden": True }, + { "name": "request", "$ref": "Request" }, + { "name": "becameOptional", "type": "string" }, + { "name": "removedRequired", "type": "string" }, + { "name": "becameRequired", "type": "string", "optional": True }, + { "name": "removedOptional", "type": "string", "optional": True }, + ] + }, + { + "name": "removedEvent", + "parameters": [ + { "name": "errorText", "type": "string" }, + { "name": "canceled", "type": "boolean", "optional": True } + ] + } + ] + }, + { + "domain": "removedDomain" + } + ] + + def create_test_schema_2(): + return [ + { + "domain": "Network", + "types": [ + { + "id": "LoaderId", + "type": "string" + }, + { + "id": "Request", + "type": "object", + "properties": [ + { "name": "url", "type": "string" }, + { "name": "method", "type": "string" }, + { "name": "headers", "type": "object" }, + { "name": "becameOptionalField", "type": "string", "optional": True }, + ] + } + ], + "commands": [ + { + "name": "addedCommand", + }, + { + "name": "setExtraHTTPHeaders", + "parameters": [ + { "name": "headers", "type": "object" }, + { "name": "mismatched", "type": "object" }, + { "name": "becameOptional", "type": "object" , "optional": True }, + { "name": "addedRequired", "type": "object" }, + { "name": "becameRequired", "type": "object" }, + { "name": "addedOptional", "type": "object", "optional": True }, + ], + "returns": [ + { "name": "mimeType", "type": "string" }, + { "name": "becameOptional", "type": "string", "optional": True }, + { "name": "addedRequired", "type": "string"}, + { "name": "becameRequired", "type": "string" }, + { "name": "addedOptional", "type": "string", "optional": True }, + ] + } + ], + "events": [ + { + "name": "requestWillBeSent", + "parameters": [ + { "name": "request", "$ref": "Request" }, + { "name": "becameOptional", "type": "string", "optional": True }, + { "name": "addedRequired", "type": "string"}, + { "name": "becameRequired", "type": "string" }, + { "name": "addedOptional", "type": "string", "optional": True }, + ] + }, + { + "name": "addedEvent" + } + ] + }, + { + "domain": "addedDomain" + } + ] + + expected_errors = [ + "removedDomain: domain has been removed", + "Network.removedCommand: command has been removed", + "Network.removedEvent: event has been removed", + "Network.setExtraHTTPHeaders.mismatched: parameter base type mismatch, 'object' vs 'string'", + "Network.setExtraHTTPHeaders.addedRequired: required parameter has been added", + "Network.setExtraHTTPHeaders.becameRequired: optional parameter is now required", + "Network.setExtraHTTPHeaders.removedRequired: required response parameter has been removed", + "Network.setExtraHTTPHeaders.becameOptional: required response parameter is now optional", + "Network.requestWillBeSent.removedRequired: required parameter has been removed", + "Network.requestWillBeSent.becameOptional: required parameter is now optional", + "Network.requestWillBeSent.request parameter->Network.Request.removedField: required property has been removed", + "Network.requestWillBeSent.request parameter->Network.Request.becameOptionalField: required property is now optional", + ] + + expected_errors_reverse = [ + "addedDomain: domain has been added", + "Network.addedEvent: event has been added", + "Network.addedCommand: command has been added", + "Network.setExtraHTTPHeaders.mismatched: parameter base type mismatch, 'string' vs 'object'", + "Network.setExtraHTTPHeaders.removedRequired: required parameter has been removed", + "Network.setExtraHTTPHeaders.becameOptional: required parameter is now optional", + "Network.setExtraHTTPHeaders.addedRequired: required response parameter has been added", + "Network.setExtraHTTPHeaders.becameRequired: optional response parameter is now required", + "Network.requestWillBeSent.becameRequired: optional parameter is now required", + "Network.requestWillBeSent.addedRequired: required parameter has been added", + ] + + def is_subset(subset, superset, message): + for i in range(len(subset)): + if subset[i] not in superset: + sys.stderr.write("%s error: %s\n" % (message, subset[i])) + return False + return True + + def errors_match(expected, actual): + return (is_subset(actual, expected, "Unexpected") and + is_subset(expected, actual, "Missing")) + + return (errors_match(expected_errors, + compare_schemas(create_test_schema_1(), create_test_schema_2(), False)) and + errors_match(expected_errors_reverse, + compare_schemas(create_test_schema_2(), create_test_schema_1(), True))) + + +def main(): + if not self_test(): + sys.stderr.write("Self-test failed") + return 1 + + if len(sys.argv) < 4 or sys.argv[1] != "-o": + sys.stderr.write("Usage: %s -o OUTPUT_FILE INPUT_FILE [--show-changes]\n" % sys.argv[0]) + return 1 + + output_path = sys.argv[2] + output_file = open(output_path, "w") + + input_path = sys.argv[3] + dir_name = os.path.dirname(input_path) + schema = load_json(input_path) + + major = schema["version"]["major"] + minor = schema["version"]["minor"] + version = "%s.%s" % (major, minor) + if len(dir_name) == 0: + dir_name = "." + baseline_path = os.path.normpath(dir_name + "/Inspector-" + version + ".json") + baseline_schema = load_json(baseline_path) + + expected_errors = [ + "Debugger.globalObjectCleared: event has been removed" + ] + + errors = compare_schemas(baseline_schema["domains"], schema["domains"], False) + unexpected_errors = [] + for i in range(len(errors)): + if errors[i] not in expected_errors: + unexpected_errors.append(errors[i]) + if len(unexpected_errors) > 0: + sys.stderr.write(" Compatibility with %s: FAILED\n" % version) + for error in unexpected_errors: + sys.stderr.write( " %s\n" % error) + return 1 + + if len(sys.argv) > 4 and sys.argv[4] == "--show-changes": + changes = compare_schemas( + load_json(input_path)["domains"], load_json(baseline_path)["domains"], True) + if len(changes) > 0: + print " Public changes since %s:" % version + for change in changes: + print " %s" % change + + output_file.write(""" +#ifndef InspectorProtocolVersion_h +#define InspectorProtocolVersion_h + +#include "platform/inspector_protocol/String16.h" + +namespace blink { + +String inspectorProtocolVersion() { return "%s"; } + +int inspectorProtocolVersionMajor() { return %s; } + +int inspectorProtocolVersionMinor() { return %s; } + +} + +#endif // !defined(InspectorProtocolVersion_h) +""" % (version, major, minor)) + + output_file.close() + +if __name__ == '__main__': + sys.exit(main()) diff --git a/deps/v8_inspector/platform/inspector_protocol/protocol.gyp b/deps/v8_inspector/platform/inspector_protocol/protocol.gyp new file mode 100644 index 00000000000000..e30f531e7ee7ac --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/protocol.gyp @@ -0,0 +1,89 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'blink_platform_output_dir': '<(SHARED_INTERMEDIATE_DIR)/blink/platform', + 'jinja_module_files': [ + # jinja2/__init__.py contains version string, so sufficient for package + '../../deps/jinja2/jinja2/__init__.py', + '../../deps/markupsafe/markupsafe/__init__.py', # jinja2 dep + ], + }, + + 'targets': [ + { + # GN version: //third_party/WebKit/Source/platform/inspector_protocol_sources + 'target_name': 'protocol_sources', + 'type': 'none', + 'dependencies': [ + 'protocol_version' + ], + 'actions': [ + { + 'action_name': 'generateInspectorProtocolBackendSources', + 'inputs': [ + '<@(jinja_module_files)', + # The python script in action below. + 'CodeGenerator.py', + # Input files for the script. + '../../devtools/protocol.json', + 'Backend_h.template', + 'Dispatcher_h.template', + 'Dispatcher_cpp.template', + 'Frontend_h.template', + 'Frontend_cpp.template', + 'TypeBuilder_h.template', + 'TypeBuilder_cpp.template', + ], + 'outputs': [ + '<(blink_platform_output_dir)/inspector_protocol/Backend.h', + '<(blink_platform_output_dir)/inspector_protocol/Dispatcher.cpp', + '<(blink_platform_output_dir)/inspector_protocol/Dispatcher.h', + '<(blink_platform_output_dir)/inspector_protocol/Frontend.cpp', + '<(blink_platform_output_dir)/inspector_protocol/Frontend.h', + '<(blink_platform_output_dir)/inspector_protocol/TypeBuilder.cpp', + '<(blink_platform_output_dir)/inspector_protocol/TypeBuilder.h', + ], + 'action': [ + 'python', + 'CodeGenerator.py', + '../../devtools/protocol.json', + '--output_dir', '<(blink_platform_output_dir)/inspector_protocol', + ], + 'message': 'Generating Inspector protocol backend sources from protocol.json', + }, + ] + }, + { + # GN version: //third_party/WebKit/Source/platform/inspector_protocol_version + 'target_name': 'protocol_version', + 'type': 'none', + 'actions': [ + { + 'action_name': 'generateInspectorProtocolVersion', + 'inputs': [ + 'generate-inspector-protocol-version', + '../../devtools/protocol.json', + ], + 'outputs': [ + '<(blink_platform_output_dir)/inspector_protocol/InspectorProtocolVersion.h', + ], + 'variables': { + 'generator_include_dirs': [ + ], + }, + 'action': [ + 'python', + 'generate-inspector-protocol-version', + '-o', + '<@(_outputs)', + '<@(_inputs)' + ], + 'message': 'Validate inspector protocol for backwards compatibility and generate version file', + } + ] + }, + ], # targets +} diff --git a/deps/v8_inspector/platform/v8_inspector/Atomics.h b/deps/v8_inspector/platform/v8_inspector/Atomics.h new file mode 100644 index 00000000000000..8076f19588b402 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/Atomics.h @@ -0,0 +1,27 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef Atomics_h +#define Atomics_h + +#include <stdint.h> + +#if COMPILER(MSVC) +#include <windows.h> +#endif + +namespace blink { + +#if COMPILER(MSVC) +inline int atomicIncrement(int volatile* addend) { return InterlockedIncrement(reinterpret_cast<long volatile*>(addend)); } +#else + +inline int atomicAdd(int volatile* addend, int increment) { return __sync_add_and_fetch(addend, increment); } +inline int atomicIncrement(int volatile* addend) { return atomicAdd(addend, 1); } + +#endif + +} // namespace blink + +#endif /* Atomics_h */ diff --git a/deps/v8_inspector/platform/v8_inspector/DebuggerScript.js b/deps/v8_inspector/platform/v8_inspector/DebuggerScript.js new file mode 100644 index 00000000000000..85d214e06efad6 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/DebuggerScript.js @@ -0,0 +1,705 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +"use strict"; + +(function () { + +var DebuggerScript = {}; + +/** @enum */ +DebuggerScript.PauseOnExceptionsState = { + DontPauseOnExceptions: 0, + PauseOnAllExceptions: 1, + PauseOnUncaughtExceptions: 2 +}; + +DebuggerScript._pauseOnExceptionsState = DebuggerScript.PauseOnExceptionsState.DontPauseOnExceptions; +Debug.clearBreakOnException(); +Debug.clearBreakOnUncaughtException(); + +/** + * @param {!CompileEvent} eventData + */ +DebuggerScript.getAfterCompileScript = function(eventData) +{ + return DebuggerScript._formatScript(eventData.script().value()); +} + +/** @type {!Map<!ScopeType, string>} */ +DebuggerScript._scopeTypeNames = new Map(); +DebuggerScript._scopeTypeNames.set(ScopeType.Global, "global"); +DebuggerScript._scopeTypeNames.set(ScopeType.Local, "local"); +DebuggerScript._scopeTypeNames.set(ScopeType.With, "with"); +DebuggerScript._scopeTypeNames.set(ScopeType.Closure, "closure"); +DebuggerScript._scopeTypeNames.set(ScopeType.Catch, "catch"); +DebuggerScript._scopeTypeNames.set(ScopeType.Block, "block"); +DebuggerScript._scopeTypeNames.set(ScopeType.Script, "script"); + +/** + * @param {function()} fun + * @return {?Array<!Scope>} + */ +DebuggerScript.getFunctionScopes = function(fun) +{ + var mirror = MakeMirror(fun); + if (!mirror.isFunction()) + return null; + var functionMirror = /** @type {!FunctionMirror} */(mirror); + var count = functionMirror.scopeCount(); + if (count == 0) + return null; + var result = []; + for (var i = 0; i < count; i++) { + var scopeDetails = functionMirror.scope(i).details(); + var scopeObject = DebuggerScript._buildScopeObject(scopeDetails.type(), scopeDetails.object()); + if (!scopeObject) + continue; + result.push({ + type: /** @type {string} */(DebuggerScript._scopeTypeNames.get(scopeDetails.type())), + object: scopeObject, + name: scopeDetails.name() || "" + }); + } + return result; +} + +/** + * @param {Object} object + * @return {?GeneratorObjectDetails} + */ +DebuggerScript.getGeneratorObjectDetails = function(object) +{ + var mirror = MakeMirror(object, true /* transient */); + if (!mirror.isGenerator()) + return null; + var generatorMirror = /** @type {!GeneratorMirror} */(mirror); + var funcMirror = generatorMirror.func(); + if (!funcMirror.resolved()) + return null; + var result = { + "function": funcMirror.value(), + "functionName": funcMirror.debugName(), + "status": generatorMirror.status() + }; + var script = funcMirror.script(); + var location = generatorMirror.sourceLocation() || funcMirror.sourceLocation(); + if (script && location) { + result["location"] = { + "scriptId": String(script.id()), + "lineNumber": location.line, + "columnNumber": location.column + }; + } + return result; +} + +/** + * @param {Object} object + * @return {!Array<!{value: *}>|undefined} + */ +DebuggerScript.getCollectionEntries = function(object) +{ + var mirror = MakeMirror(object, true /* transient */); + if (mirror.isMap()) + return /** @type {!MapMirror} */(mirror).entries(); + if (mirror.isSet() || mirror.isIterator()) { + var result = []; + var values = mirror.isSet() ? /** @type {!SetMirror} */(mirror).values() : /** @type {!IteratorMirror} */(mirror).preview(); + for (var i = 0; i < values.length; ++i) + result.push({ value: values[i] }); + return result; + } +} + +/** + * @param {string|undefined} contextData + * @return {number} + */ +DebuggerScript._executionContextId = function(contextData) +{ + if (!contextData) + return 0; + var firstComma = contextData.indexOf(","); + if (firstComma === -1) + return 0; + var secondComma = contextData.indexOf(",", firstComma + 1); + if (secondComma === -1) + return 0; + + return parseInt(contextData.substring(firstComma + 1, secondComma), 10) || 0; +} + +/** + * @param {string} contextGroupId + * @return {!Array<!FormattedScript>} + */ +DebuggerScript.getScripts = function(contextGroupId) +{ + var result = []; + var scripts = Debug.scripts(); + var contextDataPrefix = null; + if (contextGroupId) + contextDataPrefix = contextGroupId + ","; + for (var i = 0; i < scripts.length; ++i) { + var script = scripts[i]; + if (contextDataPrefix) { + if (!script.context_data) + continue; + // Context data is a string in the following format: + // <contextGroupId>,<contextId>,("default"|"nondefault") + if (script.context_data.indexOf(contextDataPrefix) !== 0) + continue; + } + result.push(DebuggerScript._formatScript(script)); + } + return result; +} + +/** + * @param {!Script} script + * @return {!FormattedScript} + */ +DebuggerScript._formatScript = function(script) +{ + var lineEnds = script.line_ends; + var lineCount = lineEnds.length; + var endLine = script.line_offset + lineCount - 1; + var endColumn; + // V8 will not count last line if script source ends with \n. + if (script.source[script.source.length - 1] === '\n') { + endLine += 1; + endColumn = 0; + } else { + if (lineCount === 1) + endColumn = script.source.length + script.column_offset; + else + endColumn = script.source.length - (lineEnds[lineCount - 2] + 1); + } + return { + id: script.id, + name: script.nameOrSourceURL(), + sourceURL: script.source_url, + sourceMappingURL: script.source_mapping_url, + source: script.source, + startLine: script.line_offset, + startColumn: script.column_offset, + endLine: endLine, + endColumn: endColumn, + executionContextId: DebuggerScript._executionContextId(script.context_data), + isContentScript: !!script.context_data && script.context_data.endsWith(",nondefault"), + isInternalScript: script.is_debugger_script + }; +} + +/** + * @param {!ExecutionState} execState + * @param {!BreakpointInfo} info + * @return {string|undefined} + */ +DebuggerScript.setBreakpoint = function(execState, info) +{ + var positionAlignment = info.interstatementLocation ? Debug.BreakPositionAlignment.BreakPosition : Debug.BreakPositionAlignment.Statement; + var breakId = Debug.setScriptBreakPointById(info.sourceID, info.lineNumber, info.columnNumber, info.condition, undefined, positionAlignment); + + var locations = Debug.findBreakPointActualLocations(breakId); + if (!locations.length) + return undefined; + info.lineNumber = locations[0].line; + info.columnNumber = locations[0].column; + return breakId.toString(); +} + +/** + * @param {!ExecutionState} execState + * @param {!{breakpointId: number}} info + */ +DebuggerScript.removeBreakpoint = function(execState, info) +{ + Debug.findBreakPoint(info.breakpointId, true); +} + +/** + * @return {number} + */ +DebuggerScript.pauseOnExceptionsState = function() +{ + return DebuggerScript._pauseOnExceptionsState; +} + +/** + * @param {number} newState + */ +DebuggerScript.setPauseOnExceptionsState = function(newState) +{ + DebuggerScript._pauseOnExceptionsState = newState; + + if (DebuggerScript.PauseOnExceptionsState.PauseOnAllExceptions === newState) + Debug.setBreakOnException(); + else + Debug.clearBreakOnException(); + + if (DebuggerScript.PauseOnExceptionsState.PauseOnUncaughtExceptions === newState) + Debug.setBreakOnUncaughtException(); + else + Debug.clearBreakOnUncaughtException(); +} + +/** + * @param {!ExecutionState} execState + * @param {number} limit + * @return {!Array<!JavaScriptCallFrame>} + */ +DebuggerScript.currentCallFrames = function(execState, limit) +{ + var frames = []; + for (var i = 0; i < execState.frameCount() && (!limit || i < limit); ++i) + frames.push(DebuggerScript._frameMirrorToJSCallFrame(execState.frame(i))); + return frames; +} + +/** + * @param {!ExecutionState} execState + */ +DebuggerScript.stepIntoStatement = function(execState) +{ + execState.prepareStep(Debug.StepAction.StepIn); +} + +/** + * @param {!ExecutionState} execState + */ +DebuggerScript.stepFrameStatement = function(execState) +{ + execState.prepareStep(Debug.StepAction.StepFrame); +} + +/** + * @param {!ExecutionState} execState + */ +DebuggerScript.stepOverStatement = function(execState) +{ + execState.prepareStep(Debug.StepAction.StepNext); +} + +/** + * @param {!ExecutionState} execState + */ +DebuggerScript.stepOutOfFunction = function(execState) +{ + execState.prepareStep(Debug.StepAction.StepOut); +} + +DebuggerScript.clearStepping = function() +{ + Debug.clearStepping(); +} + +// Returns array in form: +// [ 0, <v8_result_report> ] in case of success +// or [ 1, <general_error_message>, <compiler_message>, <line_number>, <column_number> ] in case of compile error, numbers are 1-based. +// or throws exception with message. +/** + * @param {number} scriptId + * @param {string} newSource + * @param {boolean} preview + * @return {!Array<*>} + */ +DebuggerScript.liveEditScriptSource = function(scriptId, newSource, preview) +{ + var scripts = Debug.scripts(); + var scriptToEdit = null; + for (var i = 0; i < scripts.length; i++) { + if (scripts[i].id == scriptId) { + scriptToEdit = scripts[i]; + break; + } + } + if (!scriptToEdit) + throw("Script not found"); + + var changeLog = []; + try { + var result = Debug.LiveEdit.SetScriptSource(scriptToEdit, newSource, preview, changeLog); + return [0, result.stack_modified]; + } catch (e) { + if (e instanceof Debug.LiveEdit.Failure && "details" in e) { + var details = /** @type {!LiveEditErrorDetails} */(e.details); + if (details.type === "liveedit_compile_error") { + var startPosition = details.position.start; + return [1, String(e), String(details.syntaxErrorMessage), Number(startPosition.line), Number(startPosition.column)]; + } + } + throw e; + } +} + +/** + * @param {!ExecutionState} execState + */ +DebuggerScript.clearBreakpoints = function(execState) +{ + Debug.clearAllBreakPoints(); +} + +/** + * @param {!ExecutionState} execState + * @param {!{enabled: boolean}} info + */ +DebuggerScript.setBreakpointsActivated = function(execState, info) +{ + Debug.debuggerFlags().breakPointsActive.setValue(info.enabled); +} + +/** + * @param {!BreakEvent} eventData + */ +DebuggerScript.getBreakpointNumbers = function(eventData) +{ + var breakpoints = eventData.breakPointsHit(); + var numbers = []; + if (!breakpoints) + return numbers; + + for (var i = 0; i < breakpoints.length; i++) { + var breakpoint = breakpoints[i]; + var scriptBreakPoint = breakpoint.script_break_point(); + numbers.push(scriptBreakPoint ? scriptBreakPoint.number() : breakpoint.number()); + } + return numbers; +} + +// NOTE: This function is performance critical, as it can be run on every +// statement that generates an async event (like addEventListener) to support +// asynchronous call stacks. Thus, when possible, initialize the data lazily. +/** + * @param {!FrameMirror} frameMirror + * @return {!JavaScriptCallFrame} + */ +DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror) +{ + // Stuff that can not be initialized lazily (i.e. valid while paused with a valid break_id). + // The frameMirror and scopeMirror can be accessed only while paused on the debugger. + var frameDetails = frameMirror.details(); + + var funcObject = frameDetails.func(); + var sourcePosition = frameDetails.sourcePosition(); + var thisObject = frameDetails.receiver(); + + var isAtReturn = !!frameDetails.isAtReturn(); + var returnValue = isAtReturn ? frameDetails.returnValue() : undefined; + + var scopeMirrors = frameMirror.allScopes(false); + /** @type {!Array<ScopeType>} */ + var scopeTypes = new Array(scopeMirrors.length); + /** @type {?Array<!Object>} */ + var scopeObjects = new Array(scopeMirrors.length); + /** @type {!Array<string|undefined>} */ + var scopeNames = new Array(scopeMirrors.length); + /** @type {?Array<number>} */ + var scopeStartPositions = new Array(scopeMirrors.length); + /** @type {?Array<number>} */ + var scopeEndPositions = new Array(scopeMirrors.length); + /** @type {?Array<function()|null>} */ + var scopeFunctions = new Array(scopeMirrors.length); + for (var i = 0; i < scopeMirrors.length; ++i) { + var scopeDetails = scopeMirrors[i].details(); + scopeTypes[i] = scopeDetails.type(); + scopeObjects[i] = scopeDetails.object(); + scopeNames[i] = scopeDetails.name(); + scopeStartPositions[i] = scopeDetails.startPosition ? scopeDetails.startPosition() : 0; + scopeEndPositions[i] = scopeDetails.endPosition ? scopeDetails.endPosition() : 0; + scopeFunctions[i] = scopeDetails.func ? scopeDetails.func() : null; + } + + // Calculated lazily. + var scopeChain; + var funcMirror; + var location; + /** @type {!Array<?RawLocation>} */ + var scopeStartLocations; + /** @type {!Array<?RawLocation>} */ + var scopeEndLocations; + var details; + + /** + * @param {!ScriptMirror|undefined} script + * @param {number} pos + * @return {?RawLocation} + */ + function createLocation(script, pos) + { + if (!script) + return null; + + var location = script.locationFromPosition(pos, true); + return { + "lineNumber": location.line, + "columnNumber": location.column, + "scriptId": String(script.id()) + } + } + + /** + * @return {!Array<!Object>} + */ + function ensureScopeChain() + { + if (!scopeChain) { + scopeChain = []; + scopeStartLocations = []; + scopeEndLocations = []; + for (var i = 0, j = 0; i < scopeObjects.length; ++i) { + var scopeObject = DebuggerScript._buildScopeObject(scopeTypes[i], scopeObjects[i]); + if (scopeObject) { + scopeTypes[j] = scopeTypes[i]; + scopeNames[j] = scopeNames[i]; + scopeChain[j] = scopeObject; + + var funcMirror = scopeFunctions ? MakeMirror(scopeFunctions[i]) : null; + if (!funcMirror || !funcMirror.isFunction()) + funcMirror = new UnresolvedFunctionMirror(funcObject); + + var script = /** @type {!FunctionMirror} */(funcMirror).script(); + scopeStartLocations[j] = createLocation(script, scopeStartPositions[i]); + scopeEndLocations[j] = createLocation(script, scopeEndPositions[i]); + ++j; + } + } + scopeTypes.length = scopeChain.length; + scopeNames.length = scopeChain.length; + scopeObjects = null; // Free for GC. + scopeFunctions = null; + scopeStartPositions = null; + scopeEndPositions = null; + } + return scopeChain; + } + + /** + * @return {!JavaScriptCallFrameDetails} + */ + function lazyDetails() + { + if (!details) { + var scopeObjects = ensureScopeChain(); + var script = ensureFuncMirror().script(); + /** @type {!Array<Scope>} */ + var scopes = []; + for (var i = 0; i < scopeObjects.length; ++i) { + var scope = { + "type": /** @type {string} */(DebuggerScript._scopeTypeNames.get(scopeTypes[i])), + "object": scopeObjects[i], + }; + if (scopeNames[i]) + scope.name = scopeNames[i]; + if (scopeStartLocations[i]) + scope.startLocation = /** @type {!RawLocation} */(scopeStartLocations[i]); + if (scopeEndLocations[i]) + scope.endLocation = /** @type {!RawLocation} */(scopeEndLocations[i]); + scopes.push(scope); + } + details = { + "functionName": ensureFuncMirror().debugName(), + "location": { + "lineNumber": line(), + "columnNumber": column(), + "scriptId": String(script.id()) + }, + "this": thisObject, + "scopeChain": scopes + }; + var functionLocation = ensureFuncMirror().sourceLocation(); + if (functionLocation) { + details.functionLocation = { + "lineNumber": functionLocation.line, + "columnNumber": functionLocation.column, + "scriptId": String(script.id()) + }; + } + if (isAtReturn) + details.returnValue = returnValue; + } + return details; + } + + /** + * @return {!FunctionMirror} + */ + function ensureFuncMirror() + { + if (!funcMirror) { + funcMirror = MakeMirror(funcObject); + if (!funcMirror.isFunction()) + funcMirror = new UnresolvedFunctionMirror(funcObject); + } + return /** @type {!FunctionMirror} */(funcMirror); + } + + /** + * @return {!{line: number, column: number}} + */ + function ensureLocation() + { + if (!location) { + var script = ensureFuncMirror().script(); + if (script) + location = script.locationFromPosition(sourcePosition, true); + if (!location) + location = { line: 0, column: 0 }; + } + return location; + } + + /** + * @return {number} + */ + function line() + { + return ensureLocation().line; + } + + /** + * @return {number} + */ + function column() + { + return ensureLocation().column; + } + + /** + * @return {number} + */ + function contextId() + { + var mirror = ensureFuncMirror(); + // Old V8 do not have context() function on these objects + if (!mirror.context) + return DebuggerScript._executionContextId(mirror.script().value().context_data); + var context = mirror.context(); + if (context) + return DebuggerScript._executionContextId(context.data()); + return 0; + } + + /** + * @return {number|undefined} + */ + function sourceID() + { + var script = ensureFuncMirror().script(); + return script && script.id(); + } + + /** + * @param {string} expression + * @return {*} + */ + function evaluate(expression) + { + return frameMirror.evaluate(expression, false).value(); + } + + /** @return {undefined} */ + function restart() + { + return frameMirror.restart(); + } + + /** + * @param {number} scopeNumber + * @param {string} variableName + * @param {*} newValue + */ + function setVariableValue(scopeNumber, variableName, newValue) + { + var scopeMirror = frameMirror.scope(scopeNumber); + if (!scopeMirror) + throw new Error("Incorrect scope index"); + scopeMirror.setVariableValue(variableName, newValue); + } + + return { + "sourceID": sourceID, + "line": line, + "column": column, + "contextId": contextId, + "thisObject": thisObject, + "evaluate": evaluate, + "restart": restart, + "setVariableValue": setVariableValue, + "isAtReturn": isAtReturn, + "details": lazyDetails + }; +} + +/** + * @param {number} scopeType + * @param {!Object} scopeObject + * @return {!Object|undefined} + */ +DebuggerScript._buildScopeObject = function(scopeType, scopeObject) +{ + var result; + switch (scopeType) { + case ScopeType.Local: + case ScopeType.Closure: + case ScopeType.Catch: + case ScopeType.Block: + case ScopeType.Script: + // For transient objects we create a "persistent" copy that contains + // the same properties. + // Reset scope object prototype to null so that the proto properties + // don't appear in the local scope section. + var properties = /** @type {!ObjectMirror} */(MakeMirror(scopeObject, true /* transient */)).properties(); + // Almost always Script scope will be empty, so just filter out that noise. + // Also drop empty Block scopes, should we get any. + if (!properties.length && (scopeType === ScopeType.Script || scopeType === ScopeType.Block)) + break; + result = { __proto__: null }; + for (var j = 0; j < properties.length; j++) { + var name = properties[j].name(); + if (name.length === 0 || name.charAt(0) === ".") + continue; // Skip internal variables like ".arguments" and variables with empty name + result[name] = properties[j].value_; + } + break; + case ScopeType.Global: + case ScopeType.With: + result = scopeObject; + break; + } + return result; +} + +// We never resolve Mirror by its handle so to avoid memory leaks caused by Mirrors in the cache we disable it. +ToggleMirrorCache(false); + +return DebuggerScript; +})(); diff --git a/deps/v8_inspector/platform/v8_inspector/InjectedScript.cpp b/deps/v8_inspector/platform/v8_inspector/InjectedScript.cpp new file mode 100644 index 00000000000000..65ed3bdce9e25c --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/InjectedScript.cpp @@ -0,0 +1,538 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "platform/v8_inspector/InjectedScript.h" + +#include "platform/inspector_protocol/Parser.h" +#include "platform/inspector_protocol/String16.h" +#include "platform/inspector_protocol/Values.h" +#include "platform/v8_inspector/InjectedScriptNative.h" +#include "platform/v8_inspector/InjectedScriptSource.h" +#include "platform/v8_inspector/InspectedContext.h" +#include "platform/v8_inspector/RemoteObjectId.h" +#include "platform/v8_inspector/V8Compat.h" +#include "platform/v8_inspector/V8Console.h" +#include "platform/v8_inspector/V8DebuggerImpl.h" +#include "platform/v8_inspector/V8FunctionCall.h" +#include "platform/v8_inspector/V8InjectedScriptHost.h" +#include "platform/v8_inspector/V8InspectorSessionImpl.h" +#include "platform/v8_inspector/V8StackTraceImpl.h" +#include "platform/v8_inspector/V8StringUtil.h" +#include "platform/v8_inspector/public/V8Debugger.h" +#include "platform/v8_inspector/public/V8DebuggerClient.h" +#include "platform/v8_inspector/public/V8ToProtocolValue.h" + +using blink::protocol::Array; +using blink::protocol::Debugger::CallFrame; +using blink::protocol::Debugger::CollectionEntry; +using blink::protocol::Debugger::FunctionDetails; +using blink::protocol::Debugger::GeneratorObjectDetails; +using blink::protocol::Runtime::PropertyDescriptor; +using blink::protocol::Runtime::InternalPropertyDescriptor; +using blink::protocol::Runtime::RemoteObject; +using blink::protocol::Maybe; + +namespace blink { + +static bool hasInternalError(ErrorString* errorString, bool hasError) +{ + if (hasError) + *errorString = "Internal error"; + return hasError; +} + +std::unique_ptr<InjectedScript> InjectedScript::create(InspectedContext* inspectedContext) +{ + v8::Isolate* isolate = inspectedContext->isolate(); + v8::HandleScope handles(isolate); + v8::Local<v8::Context> context = inspectedContext->context(); + v8::Context::Scope scope(context); + + std::unique_ptr<InjectedScriptNative> injectedScriptNative(new InjectedScriptNative(isolate)); + v8::Local<v8::Object> scriptHostWrapper = V8InjectedScriptHost::create(context, inspectedContext->debugger()); + injectedScriptNative->setOnInjectedScriptHost(scriptHostWrapper); + + // Inject javascript into the context. The compiled script is supposed to evaluate into + // a single anonymous function(it's anonymous to avoid cluttering the global object with + // inspector's stuff) the function is called a few lines below with InjectedScriptHost wrapper, + // injected script id and explicit reference to the inspected global object. The function is expected + // to create and configure InjectedScript instance that is going to be used by the inspector. + String16 injectedScriptSource(reinterpret_cast<const char*>(InjectedScriptSource_js), sizeof(InjectedScriptSource_js)); + v8::Local<v8::Value> value; + if (!inspectedContext->debugger()->compileAndRunInternalScript(context, toV8String(isolate, injectedScriptSource)).ToLocal(&value)) + return nullptr; + DCHECK(value->IsFunction()); + v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(value); + v8::Local<v8::Object> windowGlobal = context->Global(); + v8::Local<v8::Value> info[] = { scriptHostWrapper, windowGlobal, v8::Number::New(isolate, inspectedContext->contextId()) }; + v8::MicrotasksScope microtasksScope(isolate, v8::MicrotasksScope::kDoNotRunMicrotasks); + v8::Local<v8::Value> injectedScriptValue; + if (!function->Call(context, windowGlobal, PROTOCOL_ARRAY_LENGTH(info), info).ToLocal(&injectedScriptValue)) + return nullptr; + if (!injectedScriptValue->IsObject()) + return nullptr; + return wrapUnique(new InjectedScript(inspectedContext, injectedScriptValue.As<v8::Object>(), std::move(injectedScriptNative))); +} + +InjectedScript::InjectedScript(InspectedContext* context, v8::Local<v8::Object> object, std::unique_ptr<InjectedScriptNative> injectedScriptNative) + : m_context(context) + , m_value(context->isolate(), object) + , m_native(std::move(injectedScriptNative)) +{ +} + +InjectedScript::~InjectedScript() +{ +} + +void InjectedScript::getProperties(ErrorString* errorString, v8::Local<v8::Object> object, const String16& groupName, bool ownProperties, bool accessorPropertiesOnly, bool generatePreview, std::unique_ptr<Array<PropertyDescriptor>>* properties, Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) +{ + v8::HandleScope handles(m_context->isolate()); + V8FunctionCall function(m_context->debugger(), m_context->context(), v8Value(), "getProperties"); + function.appendArgument(object); + function.appendArgument(groupName); + function.appendArgument(ownProperties); + function.appendArgument(accessorPropertiesOnly); + function.appendArgument(generatePreview); + + v8::TryCatch tryCatch(m_context->isolate()); + v8::Local<v8::Value> resultValue = function.callWithoutExceptionHandling(); + if (tryCatch.HasCaught()) { + *exceptionDetails = createExceptionDetails(tryCatch.Message()); + // FIXME: make properties optional + *properties = Array<PropertyDescriptor>::create(); + return; + } + + std::unique_ptr<protocol::Value> protocolValue = toProtocolValue(function.context(), resultValue); + if (hasInternalError(errorString, !protocolValue)) + return; + protocol::ErrorSupport errors(errorString); + std::unique_ptr<Array<PropertyDescriptor>> result = Array<PropertyDescriptor>::parse(protocolValue.get(), &errors); + if (!hasInternalError(errorString, errors.hasErrors())) + *properties = std::move(result); +} + +void InjectedScript::releaseObject(const String16& objectId) +{ + std::unique_ptr<protocol::Value> parsedObjectId = protocol::parseJSON(objectId); + if (!parsedObjectId) + return; + protocol::DictionaryValue* object = protocol::DictionaryValue::cast(parsedObjectId.get()); + if (!object) + return; + int boundId = 0; + if (!object->getNumber("id", &boundId)) + return; + m_native->unbind(boundId); +} + +std::unique_ptr<protocol::Runtime::RemoteObject> InjectedScript::wrapObject(ErrorString* errorString, v8::Local<v8::Value> value, const String16& groupName, bool forceValueType, bool generatePreview) const +{ + v8::HandleScope handles(m_context->isolate()); + v8::Local<v8::Value> wrappedObject; + if (!wrapValue(errorString, value, groupName, forceValueType, generatePreview).ToLocal(&wrappedObject)) + return nullptr; + protocol::ErrorSupport errors; + std::unique_ptr<protocol::Runtime::RemoteObject> remoteObject = protocol::Runtime::RemoteObject::parse(toProtocolValue(m_context->context(), wrappedObject).get(), &errors); + if (!remoteObject) + *errorString = "Object has too long reference chain"; + return remoteObject; +} + +bool InjectedScript::wrapObjectProperty(ErrorString* errorString, v8::Local<v8::Object> object, v8::Local<v8::Value> key, const String16& groupName, bool forceValueType, bool generatePreview) const +{ + v8::Local<v8::Value> property; + if (hasInternalError(errorString, !object->Get(m_context->context(), key).ToLocal(&property))) + return false; + v8::Local<v8::Value> wrappedProperty; + if (!wrapValue(errorString, property, groupName, forceValueType, generatePreview).ToLocal(&wrappedProperty)) + return false; + v8::Maybe<bool> success = object->Set(m_context->context(), key, wrappedProperty); + if (hasInternalError(errorString, success.IsNothing() || !success.FromJust())) + return false; + return true; +} + +bool InjectedScript::wrapPropertyInArray(ErrorString* errorString, v8::Local<v8::Array> array, v8::Local<v8::String> property, const String16& groupName, bool forceValueType, bool generatePreview) const +{ + V8FunctionCall function(m_context->debugger(), m_context->context(), v8Value(), "wrapPropertyInArray"); + function.appendArgument(array); + function.appendArgument(property); + function.appendArgument(groupName); + function.appendArgument(canAccessInspectedWindow()); + function.appendArgument(forceValueType); + function.appendArgument(generatePreview); + bool hadException = false; + function.call(hadException); + return !hasInternalError(errorString, hadException); +} + +bool InjectedScript::wrapObjectsInArray(ErrorString* errorString, v8::Local<v8::Array> array, const String16& groupName, bool forceValueType, bool generatePreview) const +{ + V8FunctionCall function(m_context->debugger(), m_context->context(), v8Value(), "wrapObjectsInArray"); + function.appendArgument(array); + function.appendArgument(groupName); + function.appendArgument(canAccessInspectedWindow()); + function.appendArgument(forceValueType); + function.appendArgument(generatePreview); + bool hadException = false; + function.call(hadException); + return !hasInternalError(errorString, hadException); +} + +v8::MaybeLocal<v8::Value> InjectedScript::wrapValue(ErrorString* errorString, v8::Local<v8::Value> value, const String16& groupName, bool forceValueType, bool generatePreview) const +{ + V8FunctionCall function(m_context->debugger(), m_context->context(), v8Value(), "wrapObject"); + function.appendArgument(value); + function.appendArgument(groupName); + function.appendArgument(canAccessInspectedWindow()); + function.appendArgument(forceValueType); + function.appendArgument(generatePreview); + bool hadException = false; + v8::Local<v8::Value> r = function.call(hadException); + if (hasInternalError(errorString, hadException || r.IsEmpty())) + return v8::MaybeLocal<v8::Value>(); + return r; +} + +std::unique_ptr<protocol::Runtime::RemoteObject> InjectedScript::wrapTable(v8::Local<v8::Value> table, v8::Local<v8::Value> columns) const +{ + v8::HandleScope handles(m_context->isolate()); + V8FunctionCall function(m_context->debugger(), m_context->context(), v8Value(), "wrapTable"); + function.appendArgument(canAccessInspectedWindow()); + function.appendArgument(table); + if (columns.IsEmpty()) + function.appendArgument(false); + else + function.appendArgument(columns); + bool hadException = false; + v8::Local<v8::Value> r = function.call(hadException); + if (hadException) + return nullptr; + protocol::ErrorSupport errors; + return protocol::Runtime::RemoteObject::parse(toProtocolValue(m_context->context(), r).get(), &errors); +} + +bool InjectedScript::findObject(ErrorString* errorString, const RemoteObjectId& objectId, v8::Local<v8::Value>* outObject) const +{ + *outObject = m_native->objectForId(objectId.id()); + if (outObject->IsEmpty()) + *errorString = "Could not find object with given id"; + return !outObject->IsEmpty(); +} + +String16 InjectedScript::objectGroupName(const RemoteObjectId& objectId) const +{ + return m_native->groupName(objectId.id()); +} + +void InjectedScript::releaseObjectGroup(const String16& objectGroup) +{ + m_native->releaseObjectGroup(objectGroup); + if (objectGroup == "console") + m_lastEvaluationResult.Reset(); +} + +void InjectedScript::setCustomObjectFormatterEnabled(bool enabled) +{ + v8::HandleScope handles(m_context->isolate()); + V8FunctionCall function(m_context->debugger(), m_context->context(), v8Value(), "setCustomObjectFormatterEnabled"); + function.appendArgument(enabled); + bool hadException = false; + function.call(hadException); + DCHECK(!hadException); +} + +bool InjectedScript::canAccessInspectedWindow() const +{ + v8::Local<v8::Context> callingContext = m_context->isolate()->GetCallingContext(); + if (callingContext.IsEmpty()) + return true; + return m_context->debugger()->client()->callingContextCanAccessContext(callingContext, m_context->context()); +} + +v8::Local<v8::Value> InjectedScript::v8Value() const +{ + return m_value.Get(m_context->isolate()); +} + +v8::Local<v8::Value> InjectedScript::lastEvaluationResult() const +{ + if (m_lastEvaluationResult.IsEmpty()) + return v8::Undefined(m_context->isolate()); + return m_lastEvaluationResult.Get(m_context->isolate()); +} + +v8::MaybeLocal<v8::Value> InjectedScript::resolveCallArgument(ErrorString* errorString, protocol::Runtime::CallArgument* callArgument) +{ + if (callArgument->hasObjectId()) { + std::unique_ptr<RemoteObjectId> remoteObjectId = RemoteObjectId::parse(errorString, callArgument->getObjectId("")); + if (!remoteObjectId) + return v8::MaybeLocal<v8::Value>(); + if (remoteObjectId->contextId() != m_context->contextId()) { + *errorString = "Argument should belong to the same JavaScript world as target object"; + return v8::MaybeLocal<v8::Value>(); + } + v8::Local<v8::Value> object; + if (!findObject(errorString, *remoteObjectId, &object)) + return v8::MaybeLocal<v8::Value>(); + return object; + } + if (callArgument->hasValue()) { + String16 value = callArgument->getValue(nullptr)->toJSONString(); + if (callArgument->getType(String16()) == "number") + value = "Number(" + value + ")"; + v8::Local<v8::Value> object; + if (!m_context->debugger()->compileAndRunInternalScript(m_context->context(), toV8String(m_context->isolate(), value)).ToLocal(&object)) { + *errorString = "Couldn't parse value object in call argument"; + return v8::MaybeLocal<v8::Value>(); + } + return object; + } + return v8::Undefined(m_context->isolate()); +} + +std::unique_ptr<protocol::Runtime::ExceptionDetails> InjectedScript::createExceptionDetails(v8::Local<v8::Message> message) +{ + std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetailsObject = protocol::Runtime::ExceptionDetails::create().setText(toProtocolString(message->Get())).build(); + exceptionDetailsObject->setUrl(toProtocolStringWithTypeCheck(message->GetScriptResourceName())); + exceptionDetailsObject->setScriptId(String16::number(message->GetScriptOrigin().ScriptID()->Value())); + + v8::Maybe<int> lineNumber = message->GetLineNumber(m_context->context()); + if (lineNumber.IsJust()) + exceptionDetailsObject->setLine(lineNumber.FromJust()); + v8::Maybe<int> columnNumber = message->GetStartColumn(m_context->context()); + if (columnNumber.IsJust()) + exceptionDetailsObject->setColumn(columnNumber.FromJust()); + + v8::Local<v8::StackTrace> stackTrace = message->GetStackTrace(); + if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0) + exceptionDetailsObject->setStack(m_context->debugger()->createStackTrace(stackTrace, stackTrace->GetFrameCount())->buildInspectorObject()); + return exceptionDetailsObject; +} + +void InjectedScript::wrapEvaluateResult(ErrorString* errorString, v8::MaybeLocal<v8::Value> maybeResultValue, const v8::TryCatch& tryCatch, const String16& objectGroup, bool returnByValue, bool generatePreview, std::unique_ptr<protocol::Runtime::RemoteObject>* result, Maybe<bool>* wasThrown, Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) +{ + v8::Local<v8::Value> resultValue; + if (!tryCatch.HasCaught()) { + if (hasInternalError(errorString, !maybeResultValue.ToLocal(&resultValue))) + return; + std::unique_ptr<RemoteObject> remoteObject = wrapObject(errorString, resultValue, objectGroup, returnByValue, generatePreview); + if (!remoteObject) + return; + if (objectGroup == "console") + m_lastEvaluationResult.Reset(m_context->isolate(), resultValue); + *result = std::move(remoteObject); + if (wasThrown) + *wasThrown = false; + } else { + v8::Local<v8::Value> exception = tryCatch.Exception(); + std::unique_ptr<RemoteObject> remoteObject = wrapObject(errorString, exception, objectGroup, false, generatePreview && !exception->IsNativeError()); + if (!remoteObject) + return; + *result = std::move(remoteObject); + if (exceptionDetails) + *exceptionDetails = createExceptionDetails(tryCatch.Message()); + if (wasThrown) + *wasThrown = true; + } +} + +v8::MaybeLocal<v8::Object> InjectedScript::commandLineAPI(ErrorString* errorString) +{ + v8::Isolate* isolate = m_context->isolate(); + if (m_commandLineAPI.IsEmpty()) { + V8FunctionCall function(m_context->debugger(), m_context->context(), v8Value(), "installCommandLineAPI"); + function.appendArgument(V8Console::createCommandLineAPI(m_context)); + bool hadException = false; + v8::Local<v8::Value> extension = function.call(hadException, false); + if (hasInternalError(errorString, hadException || extension.IsEmpty() || !extension->IsObject())) + return v8::MaybeLocal<v8::Object>(); + m_commandLineAPI.Reset(isolate, extension.As<v8::Object>()); + } + return m_commandLineAPI.Get(m_context->isolate()); +} + +InjectedScript::Scope::Scope(ErrorString* errorString, V8DebuggerImpl* debugger, int contextGroupId) + : m_errorString(errorString) + , m_debugger(debugger) + , m_contextGroupId(contextGroupId) + , m_injectedScript(nullptr) + , m_handleScope(debugger->isolate()) + , m_tryCatch(debugger->isolate()) + , m_ignoreExceptionsAndMuteConsole(false) + , m_previousPauseOnExceptionsState(V8DebuggerImpl::DontPauseOnExceptions) + , m_userGesture(false) +{ +} + +bool InjectedScript::Scope::initialize() +{ + cleanup(); + // TODO(dgozman): what if we reattach to the same context group during evaluate? Introduce a session id? + V8InspectorSessionImpl* session = m_debugger->sessionForContextGroup(m_contextGroupId); + if (!session) { + *m_errorString = "Internal error"; + return false; + } + findInjectedScript(session); + if (!m_injectedScript) + return false; + m_context = m_injectedScript->context()->context(); + m_context->Enter(); + return true; +} + +bool InjectedScript::Scope::installCommandLineAPI() +{ + DCHECK(m_injectedScript && !m_context.IsEmpty() && m_global.IsEmpty()); + v8::Local<v8::Object> extensionObject; + if (!m_injectedScript->commandLineAPI(m_errorString).ToLocal(&extensionObject)) + return false; + m_extensionPrivate = V8Debugger::scopeExtensionPrivate(m_debugger->isolate()); + v8::Local<v8::Object> global = m_context->Global(); + if (!global->SetPrivate(m_context, m_extensionPrivate, extensionObject).FromMaybe(false)) { + *m_errorString = "Internal error"; + return false; + } + m_global = global; + return true; +} + +void InjectedScript::Scope::ignoreExceptionsAndMuteConsole() +{ + DCHECK(!m_ignoreExceptionsAndMuteConsole); + m_ignoreExceptionsAndMuteConsole = true; + m_debugger->client()->muteConsole(); + m_previousPauseOnExceptionsState = setPauseOnExceptionsState(V8DebuggerImpl::DontPauseOnExceptions); +} + +V8DebuggerImpl::PauseOnExceptionsState InjectedScript::Scope::setPauseOnExceptionsState(V8DebuggerImpl::PauseOnExceptionsState newState) +{ + if (!m_debugger->enabled()) + return newState; + V8DebuggerImpl::PauseOnExceptionsState presentState = m_debugger->getPauseOnExceptionsState(); + if (presentState != newState) + m_debugger->setPauseOnExceptionsState(newState); + return presentState; +} + +void InjectedScript::Scope::pretendUserGesture() +{ + DCHECK(!m_userGesture); + m_userGesture = true; + m_debugger->client()->beginUserGesture(); +} + +void InjectedScript::Scope::cleanup() +{ + v8::Local<v8::Object> global; + if (m_global.ToLocal(&global)) { + DCHECK(!m_context.IsEmpty()); + global->DeletePrivate(m_context, m_extensionPrivate); + m_global = v8::MaybeLocal<v8::Object>(); + } + if (!m_context.IsEmpty()) { + m_context->Exit(); + m_context.Clear(); + } +} + +InjectedScript::Scope::~Scope() +{ + if (m_ignoreExceptionsAndMuteConsole) { + setPauseOnExceptionsState(m_previousPauseOnExceptionsState); + m_debugger->client()->unmuteConsole(); + } + if (m_userGesture) + m_debugger->client()->endUserGesture(); + cleanup(); +} + +InjectedScript::ContextScope::ContextScope(ErrorString* errorString, V8DebuggerImpl* debugger, int contextGroupId, int executionContextId) + : InjectedScript::Scope(errorString, debugger, contextGroupId) + , m_executionContextId(executionContextId) +{ +} + +InjectedScript::ContextScope::~ContextScope() +{ +} + +void InjectedScript::ContextScope::findInjectedScript(V8InspectorSessionImpl* session) +{ + m_injectedScript = session->findInjectedScript(m_errorString, m_executionContextId); +} + +InjectedScript::ObjectScope::ObjectScope(ErrorString* errorString, V8DebuggerImpl* debugger, int contextGroupId, const String16& remoteObjectId) + : InjectedScript::Scope(errorString, debugger, contextGroupId) + , m_remoteObjectId(remoteObjectId) +{ +} + +InjectedScript::ObjectScope::~ObjectScope() +{ +} + +void InjectedScript::ObjectScope::findInjectedScript(V8InspectorSessionImpl* session) +{ + std::unique_ptr<RemoteObjectId> remoteId = RemoteObjectId::parse(m_errorString, m_remoteObjectId); + if (!remoteId) + return; + InjectedScript* injectedScript = session->findInjectedScript(m_errorString, remoteId.get()); + if (!injectedScript) + return; + m_objectGroupName = injectedScript->objectGroupName(*remoteId); + if (!injectedScript->findObject(m_errorString, *remoteId, &m_object)) + return; + m_injectedScript = injectedScript; +} + +InjectedScript::CallFrameScope::CallFrameScope(ErrorString* errorString, V8DebuggerImpl* debugger, int contextGroupId, const String16& remoteObjectId) + : InjectedScript::Scope(errorString, debugger, contextGroupId) + , m_remoteCallFrameId(remoteObjectId) +{ +} + +InjectedScript::CallFrameScope::~CallFrameScope() +{ +} + +void InjectedScript::CallFrameScope::findInjectedScript(V8InspectorSessionImpl* session) +{ + std::unique_ptr<RemoteCallFrameId> remoteId = RemoteCallFrameId::parse(m_errorString, m_remoteCallFrameId); + if (!remoteId) + return; + m_frameOrdinal = static_cast<size_t>(remoteId->frameOrdinal()); + m_injectedScript = session->findInjectedScript(m_errorString, remoteId.get()); +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/InjectedScript.h b/deps/v8_inspector/platform/v8_inspector/InjectedScript.h new file mode 100644 index 00000000000000..8a33e73f53eac8 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/InjectedScript.h @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef InjectedScript_h +#define InjectedScript_h + +#include "platform/inspector_protocol/Allocator.h" +#include "platform/inspector_protocol/TypeBuilder.h" +#include "platform/v8_inspector/InjectedScriptNative.h" +#include "platform/v8_inspector/InspectedContext.h" +#include "platform/v8_inspector/V8DebuggerImpl.h" +#include "wtf/PtrUtil.h" + +#include <v8.h> + +namespace blink { + +class RemoteObjectId; +class V8FunctionCall; +class V8InspectorSessionImpl; + +namespace protocol { +class DictionaryValue; +} + +using protocol::Maybe; + +class InjectedScript final { + PROTOCOL_DISALLOW_COPY(InjectedScript); +public: + static std::unique_ptr<InjectedScript> create(InspectedContext*); + ~InjectedScript(); + + InspectedContext* context() const { return m_context; } + + void getProperties(ErrorString*, v8::Local<v8::Object>, const String16& groupName, bool ownProperties, bool accessorPropertiesOnly, bool generatePreview, std::unique_ptr<protocol::Array<protocol::Runtime::PropertyDescriptor>>* result, Maybe<protocol::Runtime::ExceptionDetails>*); + void releaseObject(const String16& objectId); + + std::unique_ptr<protocol::Runtime::RemoteObject> wrapObject(ErrorString*, v8::Local<v8::Value>, const String16& groupName, bool forceValueType = false, bool generatePreview = false) const; + bool wrapObjectProperty(ErrorString*, v8::Local<v8::Object>, v8::Local<v8::Value> key, const String16& groupName, bool forceValueType = false, bool generatePreview = false) const; + bool wrapPropertyInArray(ErrorString*, v8::Local<v8::Array>, v8::Local<v8::String> property, const String16& groupName, bool forceValueType = false, bool generatePreview = false) const; + bool wrapObjectsInArray(ErrorString*, v8::Local<v8::Array>, const String16& groupName, bool forceValueType = false, bool generatePreview = false) const; + std::unique_ptr<protocol::Runtime::RemoteObject> wrapTable(v8::Local<v8::Value> table, v8::Local<v8::Value> columns) const; + + bool findObject(ErrorString*, const RemoteObjectId&, v8::Local<v8::Value>*) const; + String16 objectGroupName(const RemoteObjectId&) const; + void releaseObjectGroup(const String16&); + void setCustomObjectFormatterEnabled(bool); + v8::MaybeLocal<v8::Value> resolveCallArgument(ErrorString*, protocol::Runtime::CallArgument*); + + std::unique_ptr<protocol::Runtime::ExceptionDetails> createExceptionDetails(v8::Local<v8::Message>); + void wrapEvaluateResult(ErrorString*, + v8::MaybeLocal<v8::Value> maybeResultValue, + const v8::TryCatch&, + const String16& objectGroup, + bool returnByValue, + bool generatePreview, + std::unique_ptr<protocol::Runtime::RemoteObject>* result, + Maybe<bool>* wasThrown, + Maybe<protocol::Runtime::ExceptionDetails>*); + v8::Local<v8::Value> lastEvaluationResult() const; + + class Scope { + public: + bool initialize(); + bool installCommandLineAPI(); + void ignoreExceptionsAndMuteConsole(); + void pretendUserGesture(); + v8::Local<v8::Context> context() const { return m_context; } + InjectedScript* injectedScript() const { return m_injectedScript; } + const v8::TryCatch& tryCatch() const { return m_tryCatch; } + + protected: + Scope(ErrorString*, V8DebuggerImpl*, int contextGroupId); + ~Scope(); + virtual void findInjectedScript(V8InspectorSessionImpl*) = 0; + + ErrorString* m_errorString; + V8DebuggerImpl* m_debugger; + int m_contextGroupId; + InjectedScript* m_injectedScript; + + private: + void cleanup(); + V8DebuggerImpl::PauseOnExceptionsState setPauseOnExceptionsState(V8DebuggerImpl::PauseOnExceptionsState); + + v8::HandleScope m_handleScope; + v8::TryCatch m_tryCatch; + v8::Local<v8::Context> m_context; + v8::Local<v8::Private> m_extensionPrivate; + v8::MaybeLocal<v8::Object> m_global; + bool m_ignoreExceptionsAndMuteConsole; + V8DebuggerImpl::PauseOnExceptionsState m_previousPauseOnExceptionsState; + bool m_userGesture; + }; + + class ContextScope: public Scope { + PROTOCOL_DISALLOW_COPY(ContextScope); + public: + ContextScope(ErrorString*, V8DebuggerImpl*, int contextGroupId, int executionContextId); + ~ContextScope(); + private: + void findInjectedScript(V8InspectorSessionImpl*) override; + int m_executionContextId; + }; + + class ObjectScope: public Scope { + PROTOCOL_DISALLOW_COPY(ObjectScope); + public: + ObjectScope(ErrorString*, V8DebuggerImpl*, int contextGroupId, const String16& remoteObjectId); + ~ObjectScope(); + const String16& objectGroupName() const { return m_objectGroupName; } + v8::Local<v8::Value> object() const { return m_object; } + private: + void findInjectedScript(V8InspectorSessionImpl*) override; + String16 m_remoteObjectId; + String16 m_objectGroupName; + v8::Local<v8::Value> m_object; + }; + + class CallFrameScope: public Scope { + PROTOCOL_DISALLOW_COPY(CallFrameScope); + public: + CallFrameScope(ErrorString*, V8DebuggerImpl*, int contextGroupId, const String16& remoteCallFrameId); + ~CallFrameScope(); + size_t frameOrdinal() const { return m_frameOrdinal; } + private: + void findInjectedScript(V8InspectorSessionImpl*) override; + String16 m_remoteCallFrameId; + size_t m_frameOrdinal; + }; + +private: + InjectedScript(InspectedContext*, v8::Local<v8::Object>, std::unique_ptr<InjectedScriptNative>); + bool canAccessInspectedWindow() const; + v8::Local<v8::Value> v8Value() const; + v8::MaybeLocal<v8::Value> wrapValue(ErrorString*, v8::Local<v8::Value>, const String16& groupName, bool forceValueType, bool generatePreview) const; + v8::MaybeLocal<v8::Object> commandLineAPI(ErrorString*); + + InspectedContext* m_context; + v8::Global<v8::Value> m_value; + v8::Global<v8::Value> m_lastEvaluationResult; + std::unique_ptr<InjectedScriptNative> m_native; + v8::Global<v8::Object> m_commandLineAPI; +}; + +} // namespace blink + +#endif diff --git a/deps/v8_inspector/platform/v8_inspector/InjectedScriptNative.cpp b/deps/v8_inspector/platform/v8_inspector/InjectedScriptNative.cpp new file mode 100644 index 00000000000000..723eb867faa399 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/InjectedScriptNative.cpp @@ -0,0 +1,97 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/v8_inspector/InjectedScriptNative.h" + +#include "platform/inspector_protocol/Values.h" +#include "wtf/Assertions.h" + +namespace blink { + +InjectedScriptNative::InjectedScriptNative(v8::Isolate* isolate) + : m_lastBoundObjectId(1) + , m_isolate(isolate) +{ +} + +static const char privateKeyName[] = "v8-inspector#injectedScript"; + +InjectedScriptNative::~InjectedScriptNative() { } + +void InjectedScriptNative::setOnInjectedScriptHost(v8::Local<v8::Object> injectedScriptHost) +{ + v8::HandleScope handleScope(m_isolate); + v8::Local<v8::External> external = v8::External::New(m_isolate, this); + v8::Local<v8::Private> privateKey = v8::Private::ForApi(m_isolate, v8::String::NewFromUtf8(m_isolate, privateKeyName, v8::NewStringType::kInternalized).ToLocalChecked()); + injectedScriptHost->SetPrivate(m_isolate->GetCurrentContext(), privateKey, external); +} + +InjectedScriptNative* InjectedScriptNative::fromInjectedScriptHost(v8::Local<v8::Object> injectedScriptObject) +{ + v8::Isolate* isolate = injectedScriptObject->GetIsolate(); + v8::HandleScope handleScope(isolate); + v8::Local<v8::Context> context = isolate->GetCurrentContext(); + v8::Local<v8::Private> privateKey = v8::Private::ForApi(isolate, v8::String::NewFromUtf8(isolate, privateKeyName, v8::NewStringType::kInternalized).ToLocalChecked()); + v8::Local<v8::Value> value = injectedScriptObject->GetPrivate(context, privateKey).ToLocalChecked(); + DCHECK(value->IsExternal()); + v8::Local<v8::External> external = value.As<v8::External>(); + return static_cast<InjectedScriptNative*>(external->Value()); +} + +int InjectedScriptNative::bind(v8::Local<v8::Value> value, const String16& groupName) +{ + if (m_lastBoundObjectId <= 0) + m_lastBoundObjectId = 1; + int id = m_lastBoundObjectId++; + m_idToWrappedObject.set(id, wrapUnique(new v8::Global<v8::Value>(m_isolate, value))); + addObjectToGroup(id, groupName); + return id; +} + +void InjectedScriptNative::unbind(int id) +{ + m_idToWrappedObject.remove(id); + m_idToObjectGroupName.remove(id); +} + +v8::Local<v8::Value> InjectedScriptNative::objectForId(int id) +{ + return m_idToWrappedObject.contains(id) ? m_idToWrappedObject.get(id)->Get(m_isolate) : v8::Local<v8::Value>(); +} + +void InjectedScriptNative::addObjectToGroup(int objectId, const String16& groupName) +{ + if (groupName.isEmpty()) + return; + if (objectId <= 0) + return; + m_idToObjectGroupName.set(objectId, groupName); + auto it = m_nameToObjectGroup.find(groupName); + if (it == m_nameToObjectGroup.end()) { + m_nameToObjectGroup.set(groupName, protocol::Vector<int>()); + it = m_nameToObjectGroup.find(groupName); + } + it->second->append(objectId); +} + +void InjectedScriptNative::releaseObjectGroup(const String16& groupName) +{ + if (groupName.isEmpty()) + return; + NameToObjectGroup::iterator groupIt = m_nameToObjectGroup.find(groupName); + if (groupIt == m_nameToObjectGroup.end()) + return; + for (int id : *groupIt->second) + unbind(id); + m_nameToObjectGroup.remove(groupName); +} + +String16 InjectedScriptNative::groupName(int objectId) const +{ + if (objectId <= 0) + return String16(); + return m_idToObjectGroupName.get(objectId); +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/InjectedScriptNative.h b/deps/v8_inspector/platform/v8_inspector/InjectedScriptNative.h new file mode 100644 index 00000000000000..435bcdb8b75c52 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/InjectedScriptNative.h @@ -0,0 +1,44 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef InjectedScriptNative_h +#define InjectedScriptNative_h + +#include "platform/inspector_protocol/Collections.h" +#include "platform/inspector_protocol/String16.h" +#include "wtf/PtrUtil.h" +#include <v8.h> + +namespace blink { + +class InjectedScriptNative final { +public: + explicit InjectedScriptNative(v8::Isolate*); + ~InjectedScriptNative(); + + void setOnInjectedScriptHost(v8::Local<v8::Object>); + static InjectedScriptNative* fromInjectedScriptHost(v8::Local<v8::Object>); + + int bind(v8::Local<v8::Value>, const String16& groupName); + void unbind(int id); + v8::Local<v8::Value> objectForId(int id); + + void releaseObjectGroup(const String16& groupName); + String16 groupName(int objectId) const; + +private: + void addObjectToGroup(int objectId, const String16& groupName); + + int m_lastBoundObjectId; + v8::Isolate* m_isolate; + protocol::HashMap<int, std::unique_ptr<v8::Global<v8::Value>>> m_idToWrappedObject; + typedef protocol::HashMap<int, String16> IdToObjectGroupName; + IdToObjectGroupName m_idToObjectGroupName; + typedef protocol::HashMap<String16, protocol::Vector<int>> NameToObjectGroup; + NameToObjectGroup m_nameToObjectGroup; +}; + +} // namespace blink + +#endif diff --git a/deps/v8_inspector/platform/v8_inspector/InjectedScriptSource.js b/deps/v8_inspector/platform/v8_inspector/InjectedScriptSource.js new file mode 100644 index 00000000000000..bf0ed2bfd208b3 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/InjectedScriptSource.js @@ -0,0 +1,1222 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2013 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +"use strict"; + +/** + * @param {!InjectedScriptHostClass} InjectedScriptHost + * @param {!Window|!WorkerGlobalScope} inspectedGlobalObject + * @param {number} injectedScriptId + */ +(function (InjectedScriptHost, inspectedGlobalObject, injectedScriptId) { + +/** + * Protect against Object overwritten by the user code. + * @suppress {duplicate} + */ +var Object = /** @type {function(new:Object, *=)} */ ({}.constructor); + +/** + * @param {!Array.<T>} array + * @param {...} var_args + * @template T + */ +function push(array, var_args) +{ + for (var i = 1; i < arguments.length; ++i) + array[array.length] = arguments[i]; +} + +/** + * @param {(!Arguments.<T>|!NodeList)} array + * @param {number=} index + * @return {!Array.<T>} + * @template T + */ +function slice(array, index) +{ + var result = []; + for (var i = index || 0, j = 0; i < array.length; ++i, ++j) + result[j] = array[i]; + return result; +} + +/** + * @param {*} obj + * @return {string} + * @suppress {uselessCode} + */ +function toString(obj) +{ + // We don't use String(obj) because String16 could be overridden. + // Also the ("" + obj) expression may throw. + try { + return "" + obj; + } catch (e) { + var name = InjectedScriptHost.internalConstructorName(obj) || InjectedScriptHost.subtype(obj) || (typeof obj); + return "#<" + name + ">"; + } +} + +/** + * @param {*} obj + * @return {string} + */ +function toStringDescription(obj) +{ + if (typeof obj === "number" && obj === 0 && 1 / obj < 0) + return "-0"; // Negative zero. + return toString(obj); +} + +/** + * @param {T} obj + * @return {T} + * @template T + */ +function nullifyObjectProto(obj) +{ + if (obj && typeof obj === "object") + obj.__proto__ = null; + return obj; +} + +/** + * @param {number|string} obj + * @return {boolean} + */ +function isUInt32(obj) +{ + if (typeof obj === "number") + return obj >>> 0 === obj && (obj > 0 || 1 / obj > 0); + return "" + (obj >>> 0) === obj; +} + +/** + * FireBug's array detection. + * @param {*} obj + * @return {boolean} + */ +function isArrayLike(obj) +{ + if (typeof obj !== "object") + return false; + try { + if (typeof obj.splice === "function") { + if (!InjectedScriptHost.suppressWarningsAndCallFunction(Object.prototype.hasOwnProperty, obj, ["length"])) + return false; + var len = obj.length; + return typeof len === "number" && isUInt32(len); + } + } catch (e) { + } + return false; +} + +/** + * @param {number} a + * @param {number} b + * @return {number} + */ +function max(a, b) +{ + return a > b ? a : b; +} + +/** + * FIXME: Remove once ES6 is supported natively by JS compiler. + * @param {*} obj + * @return {boolean} + */ +function isSymbol(obj) +{ + var type = typeof obj; + return (type === "symbol"); +} + +/** + * DOM Attributes which have observable side effect on getter, in the form of + * {interfaceName1: {attributeName1: true, + * attributeName2: true, + * ...}, + * interfaceName2: {...}, + * ...} + * @type {!Object<string, !Object<string, boolean>>} + * @const + */ +var domAttributesWithObservableSideEffectOnGet = nullifyObjectProto({}); +domAttributesWithObservableSideEffectOnGet["Request"] = nullifyObjectProto({}); +domAttributesWithObservableSideEffectOnGet["Request"]["body"] = true; +domAttributesWithObservableSideEffectOnGet["Response"] = nullifyObjectProto({}); +domAttributesWithObservableSideEffectOnGet["Response"]["body"] = true; + +/** + * @param {!Object} object + * @param {string} attribute + * @return {boolean} + */ +function doesAttributeHaveObservableSideEffectOnGet(object, attribute) +{ + for (var interfaceName in domAttributesWithObservableSideEffectOnGet) { + var isInstance = InjectedScriptHost.suppressWarningsAndCallFunction(function(object, interfaceName) { + return /* suppressBlacklist */ typeof inspectedGlobalObject[interfaceName] === "function" && object instanceof inspectedGlobalObject[interfaceName]; + }, null, [object, interfaceName]); + if (isInstance) { + return attribute in domAttributesWithObservableSideEffectOnGet[interfaceName]; + } + } + return false; +} + +/** + * @constructor + */ +var InjectedScript = function() +{ +} + +/** + * @type {!Object.<string, boolean>} + * @const + */ +InjectedScript.primitiveTypes = { + "undefined": true, + "boolean": true, + "number": true, + "string": true, + __proto__: null +} + +InjectedScript.prototype = { + /** + * @param {*} object + * @return {boolean} + */ + isPrimitiveValue: function(object) + { + // FIXME(33716): typeof document.all is always 'undefined'. + return InjectedScript.primitiveTypes[typeof object] && !this._isHTMLAllCollection(object); + }, + + /** + * @param {*} object + * @param {string} groupName + * @param {boolean} canAccessInspectedGlobalObject + * @param {boolean} forceValueType + * @param {boolean} generatePreview + * @return {!RuntimeAgent.RemoteObject} + */ + wrapObject: function(object, groupName, canAccessInspectedGlobalObject, forceValueType, generatePreview) + { + if (canAccessInspectedGlobalObject) + return this._wrapObject(object, groupName, forceValueType, generatePreview); + return this._fallbackWrapper(object); + }, + + /** + * @param {!Array<!Object>} array + * @param {string} property + * @param {string} groupName + * @param {boolean} canAccessInspectedGlobalObject + * @param {boolean} forceValueType + * @param {boolean} generatePreview + */ + wrapPropertyInArray: function(array, property, groupName, canAccessInspectedGlobalObject, forceValueType, generatePreview) + { + for (var i = 0; i < array.length; ++i) { + if (typeof array[i] === "object" && property in array[i]) + array[i][property] = this.wrapObject(array[i][property], groupName, canAccessInspectedGlobalObject, forceValueType, generatePreview); + } + }, + + /** + * @param {!Array<*>} array + * @param {string} groupName + * @param {boolean} canAccessInspectedGlobalObject + * @param {boolean} forceValueType + * @param {boolean} generatePreview + */ + wrapObjectsInArray: function(array, groupName, canAccessInspectedGlobalObject, forceValueType, generatePreview) + { + for (var i = 0; i < array.length; ++i) + array[i] = this.wrapObject(array[i], groupName, canAccessInspectedGlobalObject, forceValueType, generatePreview); + }, + + /** + * @param {*} object + * @return {!RuntimeAgent.RemoteObject} + */ + _fallbackWrapper: function(object) + { + var result = { __proto__: null }; + result.type = typeof object; + if (this.isPrimitiveValue(object)) + result.value = object; + else + result.description = toString(object); + return /** @type {!RuntimeAgent.RemoteObject} */ (result); + }, + + /** + * @param {boolean} canAccessInspectedGlobalObject + * @param {!Object} table + * @param {!Array.<string>|string|boolean} columns + * @return {!RuntimeAgent.RemoteObject} + */ + wrapTable: function(canAccessInspectedGlobalObject, table, columns) + { + if (!canAccessInspectedGlobalObject) + return this._fallbackWrapper(table); + var columnNames = null; + if (typeof columns === "string") + columns = [columns]; + if (InjectedScriptHost.subtype(columns) === "array") { + columnNames = []; + for (var i = 0; i < columns.length; ++i) + columnNames[i] = toString(columns[i]); + } + return this._wrapObject(table, "console", false, true, columnNames, true); + }, + + /** + * This method cannot throw. + * @param {*} object + * @param {string=} objectGroupName + * @param {boolean=} forceValueType + * @param {boolean=} generatePreview + * @param {?Array.<string>=} columnNames + * @param {boolean=} isTable + * @param {boolean=} doNotBind + * @param {*=} customObjectConfig + * @return {!RuntimeAgent.RemoteObject} + * @suppress {checkTypes} + */ + _wrapObject: function(object, objectGroupName, forceValueType, generatePreview, columnNames, isTable, doNotBind, customObjectConfig) + { + try { + return new InjectedScript.RemoteObject(object, objectGroupName, doNotBind, forceValueType, generatePreview, columnNames, isTable, undefined, customObjectConfig); + } catch (e) { + try { + var description = injectedScript._describe(e); + } catch (ex) { + var description = "<failed to convert exception to string>"; + } + return new InjectedScript.RemoteObject(description); + } + }, + + /** + * @param {!Object|symbol} object + * @param {string=} objectGroupName + * @return {string} + */ + _bind: function(object, objectGroupName) + { + var id = InjectedScriptHost.bind(object, objectGroupName || ""); + return "{\"injectedScriptId\":" + injectedScriptId + ",\"id\":" + id + "}"; + }, + + /** + * @param {!Object} object + * @param {string} objectGroupName + * @param {boolean} ownProperties + * @param {boolean} accessorPropertiesOnly + * @param {boolean} generatePreview + * @return {!Array<!RuntimeAgent.PropertyDescriptor>|boolean} + */ + getProperties: function(object, objectGroupName, ownProperties, accessorPropertiesOnly, generatePreview) + { + var descriptors = []; + var iter = this._propertyDescriptors(object, ownProperties, accessorPropertiesOnly, undefined); + // Go over properties, wrap object values. + for (var descriptor of iter) { + if ("get" in descriptor) + descriptor.get = this._wrapObject(descriptor.get, objectGroupName); + if ("set" in descriptor) + descriptor.set = this._wrapObject(descriptor.set, objectGroupName); + if ("value" in descriptor) + descriptor.value = this._wrapObject(descriptor.value, objectGroupName, false, generatePreview); + if (!("configurable" in descriptor)) + descriptor.configurable = false; + if (!("enumerable" in descriptor)) + descriptor.enumerable = false; + if ("symbol" in descriptor) + descriptor.symbol = this._wrapObject(descriptor.symbol, objectGroupName); + push(descriptors, descriptor); + } + return descriptors; + }, + + /** + * @param {!Object} object + * @param {boolean=} ownProperties + * @param {boolean=} accessorPropertiesOnly + * @param {?Array.<string>=} propertyNamesOnly + */ + _propertyDescriptors: function*(object, ownProperties, accessorPropertiesOnly, propertyNamesOnly) + { + var propertyProcessed = { __proto__: null }; + + /** + * @param {?Object} o + * @param {!Iterable<string|symbol|number>|!Array<string|number|symbol>} properties + */ + function* process(o, properties) + { + for (var property of properties) { + var name; + if (isSymbol(property)) + name = /** @type {string} */ (injectedScript._describe(property)); + else + name = typeof property === "number" ? ("" + property) : /** @type {string} */(property); + + if (propertyProcessed[property]) + continue; + + try { + propertyProcessed[property] = true; + var descriptor = nullifyObjectProto(InjectedScriptHost.suppressWarningsAndCallFunction(Object.getOwnPropertyDescriptor, Object, [o, property])); + if (descriptor) { + if (accessorPropertiesOnly && !("get" in descriptor || "set" in descriptor)) + continue; + if ("get" in descriptor && "set" in descriptor && name != "__proto__" && InjectedScriptHost.formatAccessorsAsProperties(object) && !doesAttributeHaveObservableSideEffectOnGet(object, name)) { + descriptor.value = InjectedScriptHost.suppressWarningsAndCallFunction(function(attribute) { return this[attribute]; }, object, [property]); + descriptor.isOwn = true; + delete descriptor.get; + delete descriptor.set; + } + } else { + // Not all bindings provide proper descriptors. Fall back to the writable, configurable property. + if (accessorPropertiesOnly) + continue; + try { + descriptor = { name: name, value: o[property], writable: false, configurable: false, enumerable: false, __proto__: null }; + if (o === object) + descriptor.isOwn = true; + yield descriptor; + } catch (e) { + // Silent catch. + } + continue; + } + } catch (e) { + if (accessorPropertiesOnly) + continue; + var descriptor = { __proto__: null }; + descriptor.value = e; + descriptor.wasThrown = true; + } + + descriptor.name = name; + if (o === object) + descriptor.isOwn = true; + if (isSymbol(property)) + descriptor.symbol = property; + yield descriptor; + } + } + + if (propertyNamesOnly) { + for (var i = 0; i < propertyNamesOnly.length; ++i) { + var name = propertyNamesOnly[i]; + for (var o = object; this._isDefined(o); o = InjectedScriptHost.prototype(o)) { + if (InjectedScriptHost.suppressWarningsAndCallFunction(Object.prototype.hasOwnProperty, o, [name])) { + for (var descriptor of process(o, [name])) + yield descriptor; + break; + } + if (ownProperties) + break; + } + } + return; + } + + /** + * @param {number} length + */ + function* arrayIndexNames(length) + { + for (var i = 0; i < length; ++i) + yield "" + i; + } + + var skipGetOwnPropertyNames; + try { + skipGetOwnPropertyNames = InjectedScriptHost.isTypedArray(object) && object.length > 500000; + } catch (e) { + } + + for (var o = object; this._isDefined(o); o = InjectedScriptHost.prototype(o)) { + if (InjectedScriptHost.subtype(o) === "proxy") + continue; + if (skipGetOwnPropertyNames && o === object) { + // Avoid OOM crashes from getting all own property names of a large TypedArray. + for (var descriptor of process(o, arrayIndexNames(o.length))) + yield descriptor; + } else { + // First call Object.keys() to enforce ordering of the property descriptors. + for (var descriptor of process(o, Object.keys(/** @type {!Object} */ (o)))) + yield descriptor; + for (var descriptor of process(o, Object.getOwnPropertyNames(/** @type {!Object} */ (o)))) + yield descriptor; + } + if (Object.getOwnPropertySymbols) { + for (var descriptor of process(o, Object.getOwnPropertySymbols(/** @type {!Object} */ (o)))) + yield descriptor; + } + if (ownProperties) { + var proto = InjectedScriptHost.prototype(o); + if (proto && !accessorPropertiesOnly) + yield { name: "__proto__", value: proto, writable: true, configurable: true, enumerable: false, isOwn: true, __proto__: null }; + break; + } + } + }, + + /** + * @param {string|undefined} objectGroupName + * @param {*} jsonMLObject + * @throws {string} error message + */ + _substituteObjectTagsInCustomPreview: function(objectGroupName, jsonMLObject) + { + var maxCustomPreviewRecursionDepth = 20; + this._customPreviewRecursionDepth = (this._customPreviewRecursionDepth || 0) + 1 + try { + if (this._customPreviewRecursionDepth >= maxCustomPreviewRecursionDepth) + throw new Error("Too deep hierarchy of inlined custom previews"); + + if (!isArrayLike(jsonMLObject)) + return; + + if (jsonMLObject[0] === "object") { + var attributes = jsonMLObject[1]; + var originObject = attributes["object"]; + var config = attributes["config"]; + if (typeof originObject === "undefined") + throw new Error("Illegal format: obligatory attribute \"object\" isn't specified"); + + jsonMLObject[1] = this._wrapObject(originObject, objectGroupName, false, false, null, false, false, config); + return; + } + + for (var i = 0; i < jsonMLObject.length; ++i) + this._substituteObjectTagsInCustomPreview(objectGroupName, jsonMLObject[i]); + } finally { + this._customPreviewRecursionDepth--; + } + }, + + /** + * @param {!Object} nativeCommandLineAPI + * @return {!Object} + */ + installCommandLineAPI: function(nativeCommandLineAPI) + { + // NOTE: This list contains only not native Command Line API methods. For full list: V8Console. + // NOTE: Argument names of these methods will be printed in the console, so use pretty names! + var members = [ "$", "$$", "$x", "monitorEvents", "unmonitorEvents", "getEventListeners" ]; + for (var member of members) + nativeCommandLineAPI[member] = CommandLineAPIImpl[member]; + var functionToStringMap = new Map([ + ["$", "function $(selector, [startNode]) { [Command Line API] }"], + ["$$", "function $$(selector, [startNode]) { [Command Line API] }"], + ["$x", "function $x(xpath, [startNode]) { [Command Line API] }"], + ["monitorEvents", "function monitorEvents(object, [types]) { [Command Line API] }"], + ["unmonitorEvents", "function unmonitorEvents(object, [types]) { [Command Line API] }"], + ["getEventListeners", "function getEventListeners(node) { [Command Line API] }"] + ]); + for (let entry of functionToStringMap) + nativeCommandLineAPI[entry[0]].toString = (() => entry[1]); + return nativeCommandLineAPI; + }, + + /** + * @param {*} object + * @return {boolean} + */ + _isDefined: function(object) + { + return !!object || this._isHTMLAllCollection(object); + }, + + /** + * @param {*} object + * @return {boolean} + */ + _isHTMLAllCollection: function(object) + { + // document.all is reported as undefined, but we still want to process it. + return (typeof object === "undefined") && !!InjectedScriptHost.subtype(object); + }, + + /** + * @param {*} obj + * @return {?string} + */ + _subtype: function(obj) + { + if (obj === null) + return "null"; + + if (this.isPrimitiveValue(obj)) + return null; + + var subtype = InjectedScriptHost.subtype(obj); + if (subtype) + return subtype; + + if (isArrayLike(obj)) + return "array"; + + // If owning frame has navigated to somewhere else window properties will be undefined. + return null; + }, + + /** + * @param {*} obj + * @return {?string} + */ + _describe: function(obj) + { + if (this.isPrimitiveValue(obj)) + return null; + + var subtype = this._subtype(obj); + + if (subtype === "regexp") + return toString(obj); + + if (subtype === "date") + return toString(obj); + + if (subtype === "node") { + var description = obj.nodeName.toLowerCase(); + switch (obj.nodeType) { + case 1 /* Node.ELEMENT_NODE */: + description += obj.id ? "#" + obj.id : ""; + var className = obj.className; + description += (className && typeof className === "string") ? "." + className.trim().replace(/\s+/g, ".") : ""; + break; + case 10 /*Node.DOCUMENT_TYPE_NODE */: + description = "<!DOCTYPE " + description + ">"; + break; + } + return description; + } + + if (subtype === "proxy") + return "Proxy"; + + var className = InjectedScriptHost.internalConstructorName(obj); + if (subtype === "array") { + if (typeof obj.length === "number") + className += "[" + obj.length + "]"; + return className; + } + + if (typeof obj === "function") + return toString(obj); + + if (isSymbol(obj)) { + try { + return /** @type {string} */ (InjectedScriptHost.suppressWarningsAndCallFunction(Symbol.prototype.toString, obj)) || "Symbol"; + } catch (e) { + return "Symbol"; + } + } + + if (InjectedScriptHost.subtype(obj) === "error") { + try { + var stack = obj.stack; + var message = obj.message && obj.message.length ? ": " + obj.message : ""; + var firstCallFrame = /^\s+at\s/m.exec(stack); + var stackMessageEnd = firstCallFrame ? firstCallFrame.index : -1; + if (stackMessageEnd !== -1) { + var stackTrace = stack.substr(stackMessageEnd); + return className + message + "\n" + stackTrace; + } + return className + message; + } catch(e) { + } + } + + return className; + }, + + /** + * @param {boolean} enabled + */ + setCustomObjectFormatterEnabled: function(enabled) + { + this._customObjectFormatterEnabled = enabled; + } +} + +/** + * @type {!InjectedScript} + * @const + */ +var injectedScript = new InjectedScript(); + +/** + * @constructor + * @param {*} object + * @param {string=} objectGroupName + * @param {boolean=} doNotBind + * @param {boolean=} forceValueType + * @param {boolean=} generatePreview + * @param {?Array.<string>=} columnNames + * @param {boolean=} isTable + * @param {boolean=} skipEntriesPreview + * @param {*=} customObjectConfig + */ +InjectedScript.RemoteObject = function(object, objectGroupName, doNotBind, forceValueType, generatePreview, columnNames, isTable, skipEntriesPreview, customObjectConfig) +{ + this.type = typeof object; + if (this.type === "undefined" && injectedScript._isHTMLAllCollection(object)) + this.type = "object"; + + if (injectedScript.isPrimitiveValue(object) || object === null || forceValueType) { + // We don't send undefined values over JSON. + if (this.type !== "undefined") + this.value = object; + + // Null object is object with 'null' subtype. + if (object === null) + this.subtype = "null"; + + // Provide user-friendly number values. + if (this.type === "number") { + this.description = toStringDescription(object); + // Override "value" property for values that can not be JSON-stringified. + switch (this.description) { + case "NaN": + case "Infinity": + case "-Infinity": + case "-0": + this.value = this.description; + break; + } + } + + return; + } + + object = /** @type {!Object} */ (object); + + if (!doNotBind) + this.objectId = injectedScript._bind(object, objectGroupName); + var subtype = injectedScript._subtype(object); + if (subtype) + this.subtype = subtype; + var className = InjectedScriptHost.internalConstructorName(object); + if (className) + this.className = className; + this.description = injectedScript._describe(object); + + if (generatePreview && this.type === "object") { + if (this.subtype === "proxy") + this.preview = this._generatePreview(InjectedScriptHost.proxyTargetValue(object), undefined, columnNames, isTable, skipEntriesPreview); + else if (this.subtype !== "node") + this.preview = this._generatePreview(object, undefined, columnNames, isTable, skipEntriesPreview); + } + + if (injectedScript._customObjectFormatterEnabled) { + var customPreview = this._customPreview(object, objectGroupName, customObjectConfig); + if (customPreview) + this.customPreview = customPreview; + } +} + +InjectedScript.RemoteObject.prototype = { + + /** + * @param {*} object + * @param {string=} objectGroupName + * @param {*=} customObjectConfig + * @return {?RuntimeAgent.CustomPreview} + */ + _customPreview: function(object, objectGroupName, customObjectConfig) + { + /** + * @param {!Error} error + */ + function logError(error) + { + Promise.resolve().then(inspectedGlobalObject.console.error.bind(inspectedGlobalObject.console, "Custom Formatter Failed: " + error.message)); + } + + /** + * @suppressReceiverCheck + * @param {*} object + * @param {*=} customObjectConfig + * @return {*} + */ + function wrap(object, customObjectConfig) + { + return InjectedScriptHost.suppressWarningsAndCallFunction(injectedScript._wrapObject, injectedScript, [ object, objectGroupName, false, false, null, false, false, customObjectConfig ]); + } + + try { + var formatters = inspectedGlobalObject["devtoolsFormatters"]; + if (!formatters || !isArrayLike(formatters)) + return null; + + for (var i = 0; i < formatters.length; ++i) { + try { + var formatted = formatters[i].header(object, customObjectConfig); + if (!formatted) + continue; + + var hasBody = formatters[i].hasBody(object, customObjectConfig); + injectedScript._substituteObjectTagsInCustomPreview(objectGroupName, formatted); + var formatterObjectId = injectedScript._bind(formatters[i], objectGroupName); + var bindRemoteObjectFunctionId = injectedScript._bind(wrap, objectGroupName); + var result = {header: JSON.stringify(formatted), hasBody: !!hasBody, formatterObjectId: formatterObjectId, bindRemoteObjectFunctionId: bindRemoteObjectFunctionId}; + if (customObjectConfig) + result["configObjectId"] = injectedScript._bind(customObjectConfig, objectGroupName); + return result; + } catch (e) { + logError(e); + } + } + } catch (e) { + logError(e); + } + return null; + }, + + /** + * @return {!RuntimeAgent.ObjectPreview} preview + */ + _createEmptyPreview: function() + { + var preview = { + type: /** @type {!RuntimeAgent.ObjectPreviewType.<string>} */ (this.type), + description: this.description || toStringDescription(this.value), + overflow: false, + properties: [], + __proto__: null + }; + if (this.subtype) + preview.subtype = /** @type {!RuntimeAgent.ObjectPreviewSubtype.<string>} */ (this.subtype); + return preview; + }, + + /** + * @param {!Object} object + * @param {?Array.<string>=} firstLevelKeys + * @param {?Array.<string>=} secondLevelKeys + * @param {boolean=} isTable + * @param {boolean=} skipEntriesPreview + * @return {!RuntimeAgent.ObjectPreview} preview + */ + _generatePreview: function(object, firstLevelKeys, secondLevelKeys, isTable, skipEntriesPreview) + { + var preview = this._createEmptyPreview(); + var firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0; + + var propertiesThreshold = { + properties: isTable ? 1000 : max(5, firstLevelKeysCount), + indexes: isTable ? 1000 : max(100, firstLevelKeysCount), + __proto__: null + }; + + try { + var descriptors = injectedScript._propertyDescriptors(object, undefined, undefined, firstLevelKeys); + + this._appendPropertyDescriptors(preview, descriptors, propertiesThreshold, secondLevelKeys, isTable); + if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0) + return preview; + + // Add internal properties to preview. + var rawInternalProperties = InjectedScriptHost.getInternalProperties(object) || []; + var internalProperties = []; + for (var i = 0; i < rawInternalProperties.length; i += 2) { + push(internalProperties, { + name: rawInternalProperties[i], + value: rawInternalProperties[i + 1], + isOwn: true, + enumerable: true, + __proto__: null + }); + } + this._appendPropertyDescriptors(preview, internalProperties, propertiesThreshold, secondLevelKeys, isTable); + + if (this.subtype === "map" || this.subtype === "set" || this.subtype === "iterator") + this._appendEntriesPreview(object, preview, skipEntriesPreview); + + } catch (e) {} + + return preview; + }, + + /** + * @param {!RuntimeAgent.ObjectPreview} preview + * @param {!Array.<*>|!Iterable.<*>} descriptors + * @param {!Object} propertiesThreshold + * @param {?Array.<string>=} secondLevelKeys + * @param {boolean=} isTable + */ + _appendPropertyDescriptors: function(preview, descriptors, propertiesThreshold, secondLevelKeys, isTable) + { + for (var descriptor of descriptors) { + if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0) + break; + if (!descriptor || descriptor.wasThrown) + continue; + + var name = descriptor.name; + + // Ignore __proto__ property. + if (name === "__proto__") + continue; + + // Ignore length property of array. + if (this.subtype === "array" && name === "length") + continue; + + // Ignore size property of map, set. + if ((this.subtype === "map" || this.subtype === "set") && name === "size") + continue; + + // Never preview prototype properties. + if (!descriptor.isOwn) + continue; + + // Ignore computed properties. + if (!("value" in descriptor)) + continue; + + var value = descriptor.value; + var type = typeof value; + + // Never render functions in object preview. + if (type === "function" && (this.subtype !== "array" || !isUInt32(name))) + continue; + + // Special-case HTMLAll. + if (type === "undefined" && injectedScript._isHTMLAllCollection(value)) + type = "object"; + + // Render own properties. + if (value === null) { + this._appendPropertyPreview(preview, { name: name, type: "object", subtype: "null", value: "null", __proto__: null }, propertiesThreshold); + continue; + } + + var maxLength = 100; + if (InjectedScript.primitiveTypes[type]) { + if (type === "string" && value.length > maxLength) + value = this._abbreviateString(value, maxLength, true); + this._appendPropertyPreview(preview, { name: name, type: type, value: toStringDescription(value), __proto__: null }, propertiesThreshold); + continue; + } + + var property = { name: name, type: type, __proto__: null }; + var subtype = injectedScript._subtype(value); + if (subtype) + property.subtype = subtype; + + if (secondLevelKeys === null || secondLevelKeys) { + var subPreview = this._generatePreview(value, secondLevelKeys || undefined, undefined, isTable); + property.valuePreview = subPreview; + if (subPreview.overflow) + preview.overflow = true; + } else { + var description = ""; + if (type !== "function") + description = this._abbreviateString(/** @type {string} */ (injectedScript._describe(value)), maxLength, subtype === "regexp"); + property.value = description; + } + this._appendPropertyPreview(preview, property, propertiesThreshold); + } + }, + + /** + * @param {!RuntimeAgent.ObjectPreview} preview + * @param {!Object} property + * @param {!Object} propertiesThreshold + */ + _appendPropertyPreview: function(preview, property, propertiesThreshold) + { + if (toString(property.name >>> 0) === property.name) + propertiesThreshold.indexes--; + else + propertiesThreshold.properties--; + if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0) { + preview.overflow = true; + } else { + push(preview.properties, property); + } + }, + + /** + * @param {!Object} object + * @param {!RuntimeAgent.ObjectPreview} preview + * @param {boolean=} skipEntriesPreview + */ + _appendEntriesPreview: function(object, preview, skipEntriesPreview) + { + var entries = InjectedScriptHost.collectionEntries(object); + if (!entries) + return; + if (skipEntriesPreview) { + if (entries.length) + preview.overflow = true; + return; + } + preview.entries = []; + var entriesThreshold = 5; + for (var i = 0; i < entries.length; ++i) { + if (preview.entries.length >= entriesThreshold) { + preview.overflow = true; + break; + } + var entry = nullifyObjectProto(entries[i]); + var previewEntry = { + value: generateValuePreview(entry.value), + __proto__: null + }; + if ("key" in entry) + previewEntry.key = generateValuePreview(entry.key); + push(preview.entries, previewEntry); + } + + /** + * @param {*} value + * @return {!RuntimeAgent.ObjectPreview} + */ + function generateValuePreview(value) + { + var remoteObject = new InjectedScript.RemoteObject(value, undefined, true, undefined, true, undefined, undefined, true); + var valuePreview = remoteObject.preview || remoteObject._createEmptyPreview(); + return valuePreview; + } + }, + + /** + * @param {string} string + * @param {number} maxLength + * @param {boolean=} middle + * @return {string} + */ + _abbreviateString: function(string, maxLength, middle) + { + if (string.length <= maxLength) + return string; + if (middle) { + var leftHalf = maxLength >> 1; + var rightHalf = maxLength - leftHalf - 1; + return string.substr(0, leftHalf) + "\u2026" + string.substr(string.length - rightHalf, rightHalf); + } + return string.substr(0, maxLength) + "\u2026"; + }, + + __proto__: null +} + +var CommandLineAPIImpl = { __proto__: null } + +/** + * @param {string} selector + * @param {!Node=} opt_startNode + * @return {*} + */ +CommandLineAPIImpl.$ = function (selector, opt_startNode) +{ + if (CommandLineAPIImpl._canQuerySelectorOnNode(opt_startNode)) + return opt_startNode.querySelector(selector); + + return inspectedGlobalObject.document.querySelector(selector); +} + +/** + * @param {string} selector + * @param {!Node=} opt_startNode + * @return {*} + */ +CommandLineAPIImpl.$$ = function (selector, opt_startNode) +{ + if (CommandLineAPIImpl._canQuerySelectorOnNode(opt_startNode)) + return slice(opt_startNode.querySelectorAll(selector)); + return slice(inspectedGlobalObject.document.querySelectorAll(selector)); +} + +/** + * @param {!Node=} node + * @return {boolean} + */ +CommandLineAPIImpl._canQuerySelectorOnNode = function(node) +{ + return !!node && InjectedScriptHost.subtype(node) === "node" && (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.DOCUMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE); +} + +/** + * @param {string} xpath + * @param {!Node=} opt_startNode + * @return {*} + */ +CommandLineAPIImpl.$x = function(xpath, opt_startNode) +{ + var doc = (opt_startNode && opt_startNode.ownerDocument) || inspectedGlobalObject.document; + var result = doc.evaluate(xpath, opt_startNode || doc, null, XPathResult.ANY_TYPE, null); + switch (result.resultType) { + case XPathResult.NUMBER_TYPE: + return result.numberValue; + case XPathResult.STRING_TYPE: + return result.stringValue; + case XPathResult.BOOLEAN_TYPE: + return result.booleanValue; + default: + var nodes = []; + var node; + while (node = result.iterateNext()) + push(nodes, node); + return nodes; + } +} + +/** + * @param {!Object} object + * @param {!Array.<string>|string=} opt_types + */ +CommandLineAPIImpl.monitorEvents = function(object, opt_types) +{ + if (!object || !object.addEventListener || !object.removeEventListener) + return; + var types = CommandLineAPIImpl._normalizeEventTypes(opt_types); + for (var i = 0; i < types.length; ++i) { + object.removeEventListener(types[i], CommandLineAPIImpl._logEvent, false); + object.addEventListener(types[i], CommandLineAPIImpl._logEvent, false); + } +} + +/** + * @param {!Object} object + * @param {!Array.<string>|string=} opt_types + */ +CommandLineAPIImpl.unmonitorEvents = function(object, opt_types) +{ + if (!object || !object.addEventListener || !object.removeEventListener) + return; + var types = CommandLineAPIImpl._normalizeEventTypes(opt_types); + for (var i = 0; i < types.length; ++i) + object.removeEventListener(types[i], CommandLineAPIImpl._logEvent, false); +} + +/** + * @param {!Node} node + * @return {!Object|undefined} + */ +CommandLineAPIImpl.getEventListeners = function(node) +{ + var result = nullifyObjectProto(InjectedScriptHost.getEventListeners(node)); + if (!result) + return; + + // TODO(dtapuska): Remove this one closure compiler is updated + // to handle EventListenerOptions and passive event listeners + // has shipped. Don't JSDoc these otherwise it will fail. + // @param {boolean} capture + // @param {boolean} passive + // @return {boolean|undefined|{capture: (boolean|undefined), passive: boolean}} + function eventListenerOptions(capture, passive) + { + return {"capture": capture, "passive": passive}; + } + + /** + * @param {!Node} node + * @param {string} type + * @param {function()} listener + * @param {boolean} capture + * @param {boolean} passive + */ + function removeEventListenerWrapper(node, type, listener, capture, passive) + { + node.removeEventListener(type, listener, eventListenerOptions(capture, passive)); + } + + /** @this {{type: string, listener: function(), useCapture: boolean, passive: boolean}} */ + var removeFunc = function() + { + removeEventListenerWrapper(node, this.type, this.listener, this.useCapture, this.passive); + } + for (var type in result) { + var listeners = result[type]; + for (var i = 0, listener; listener = listeners[i]; ++i) { + listener["type"] = type; + listener["remove"] = removeFunc; + } + } + return result; +} + +/** + * @param {!Array.<string>|string=} types + * @return {!Array.<string>} + */ +CommandLineAPIImpl._normalizeEventTypes = function(types) +{ + if (typeof types === "undefined") + types = ["mouse", "key", "touch", "pointer", "control", "load", "unload", "abort", "error", "select", "input", "change", "submit", "reset", "focus", "blur", "resize", "scroll", "search", "devicemotion", "deviceorientation"]; + else if (typeof types === "string") + types = [types]; + + var result = []; + for (var i = 0; i < types.length; ++i) { + if (types[i] === "mouse") + push(result, "click", "dblclick", "mousedown", "mouseeenter", "mouseleave", "mousemove", "mouseout", "mouseover", "mouseup", "mouseleave", "mousewheel"); + else if (types[i] === "key") + push(result, "keydown", "keyup", "keypress", "textInput"); + else if (types[i] === "touch") + push(result, "touchstart", "touchmove", "touchend", "touchcancel"); + else if (types[i] === "pointer") + push(result, "pointerover", "pointerout", "pointerenter", "pointerleave", "pointerdown", "pointerup", "pointermove", "pointercancel", "gotpointercapture", "lostpointercapture"); + else if (types[i] === "control") + push(result, "resize", "scroll", "zoom", "focus", "blur", "select", "input", "change", "submit", "reset"); + else + push(result, types[i]); + } + return result; +} + +/** + * @param {!Event} event + */ +CommandLineAPIImpl._logEvent = function(event) +{ + inspectedGlobalObject.console.log(event.type, event); +} + +return injectedScript; +}) diff --git a/deps/v8_inspector/platform/v8_inspector/InspectedContext.cpp b/deps/v8_inspector/platform/v8_inspector/InspectedContext.cpp new file mode 100644 index 00000000000000..5b52e015a54363 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/InspectedContext.cpp @@ -0,0 +1,88 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/v8_inspector/InspectedContext.h" + +#include "platform/v8_inspector/InjectedScript.h" +#include "platform/v8_inspector/V8Console.h" +#include "platform/v8_inspector/V8DebuggerImpl.h" +#include "platform/v8_inspector/V8StringUtil.h" +#include "platform/v8_inspector/public/V8ContextInfo.h" +#include "platform/v8_inspector/public/V8DebuggerClient.h" + +namespace blink { + +void InspectedContext::weakCallback(const v8::WeakCallbackInfo<InspectedContext>& data) +{ + InspectedContext* context = data.GetParameter(); + if (!context->m_context.IsEmpty()) { + context->m_context.Reset(); + data.SetSecondPassCallback(&InspectedContext::weakCallback); + } else { + context->m_debugger->discardInspectedContext(context->m_contextGroupId, context->m_contextId); + } +} + +void InspectedContext::consoleWeakCallback(const v8::WeakCallbackInfo<InspectedContext>& data) +{ + data.GetParameter()->m_console.Reset(); +} + +InspectedContext::InspectedContext(V8DebuggerImpl* debugger, const V8ContextInfo& info, int contextId) + : m_debugger(debugger) + , m_context(info.context->GetIsolate(), info.context) + , m_contextId(contextId) + , m_contextGroupId(info.contextGroupId) + , m_isDefault(info.isDefault) + , m_origin(info.origin) + , m_humanReadableName(info.humanReadableName) + , m_frameId(info.frameId) + , m_reported(false) +{ + m_context.SetWeak(this, &InspectedContext::weakCallback, v8::WeakCallbackType::kParameter); + + v8::Isolate* isolate = m_debugger->isolate(); + v8::Local<v8::Object> global = info.context->Global(); + v8::Local<v8::Object> console = V8Console::createConsole(this, info.hasMemoryOnConsole); + if (!global->Set(info.context, toV8StringInternalized(isolate, "console"), console).FromMaybe(false)) + return; + m_console.Reset(isolate, console); + m_console.SetWeak(this, &InspectedContext::consoleWeakCallback, v8::WeakCallbackType::kParameter); +} + +InspectedContext::~InspectedContext() +{ + if (!m_context.IsEmpty() && !m_console.IsEmpty()) { + v8::HandleScope scope(isolate()); + V8Console::clearInspectedContextIfNeeded(context(), m_console.Get(isolate())); + } +} + +v8::Local<v8::Context> InspectedContext::context() const +{ + return m_context.Get(isolate()); +} + +v8::Isolate* InspectedContext::isolate() const +{ + return m_debugger->isolate(); +} + +void InspectedContext::createInjectedScript() +{ + DCHECK(!m_injectedScript); + v8::HandleScope handles(isolate()); + v8::Local<v8::Context> localContext = context(); + v8::Local<v8::Context> callingContext = isolate()->GetCallingContext(); + if (!callingContext.IsEmpty() && !m_debugger->client()->callingContextCanAccessContext(callingContext, localContext)) + return; + m_injectedScript = InjectedScript::create(this); +} + +void InspectedContext::discardInjectedScript() +{ + m_injectedScript.reset(); +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/InspectedContext.h b/deps/v8_inspector/platform/v8_inspector/InspectedContext.h new file mode 100644 index 00000000000000..284679ddc0ca10 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/InspectedContext.h @@ -0,0 +1,64 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef InspectedContext_h +#define InspectedContext_h + +#include "platform/inspector_protocol/Allocator.h" +#include "platform/inspector_protocol/Collections.h" +#include "platform/inspector_protocol/String16.h" +#include <v8.h> + +namespace blink { + +class InjectedScript; +class InjectedScriptHost; +class V8ContextInfo; +class V8DebuggerImpl; + +class InspectedContext { + PROTOCOL_DISALLOW_COPY(InspectedContext); +public: + ~InspectedContext(); + + v8::Local<v8::Context> context() const; + int contextId() const { return m_contextId; } + int contextGroupId() const { return m_contextGroupId; } + bool isDefault() const { return m_isDefault; } + String16 origin() const { return m_origin; } + String16 humanReadableName() const { return m_humanReadableName; } + String16 frameId() const { return m_frameId; } + + bool isReported() const { return m_reported; } + void setReported(bool reported) { m_reported = reported; } + + v8::Isolate* isolate() const; + V8DebuggerImpl* debugger() const { return m_debugger; } + + InjectedScript* getInjectedScript() { return m_injectedScript.get(); } + void createInjectedScript(); + void discardInjectedScript(); + +private: + friend class V8DebuggerImpl; + InspectedContext(V8DebuggerImpl*, const V8ContextInfo&, int contextId); + static void weakCallback(const v8::WeakCallbackInfo<InspectedContext>&); + static void consoleWeakCallback(const v8::WeakCallbackInfo<InspectedContext>&); + + V8DebuggerImpl* m_debugger; + v8::Global<v8::Context> m_context; + int m_contextId; + int m_contextGroupId; + bool m_isDefault; + const String16 m_origin; + const String16 m_humanReadableName; + const String16 m_frameId; + bool m_reported; + std::unique_ptr<InjectedScript> m_injectedScript; + v8::Global<v8::Object> m_console; +}; + +} // namespace blink + +#endif diff --git a/deps/v8_inspector/platform/v8_inspector/InspectorWrapper.cpp b/deps/v8_inspector/platform/v8_inspector/InspectorWrapper.cpp new file mode 100644 index 00000000000000..4f5f2784177d0e --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/InspectorWrapper.cpp @@ -0,0 +1,70 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/v8_inspector/InspectorWrapper.h" + +#include "platform/v8_inspector/V8Compat.h" +#include "platform/v8_inspector/public/V8DebuggerClient.h" +#include "wtf/Assertions.h" + +#include <v8-debug.h> + +namespace blink { + +v8::Local<v8::FunctionTemplate> InspectorWrapperBase::createWrapperTemplate(v8::Isolate* isolate, const char* className, const protocol::Vector<V8MethodConfiguration>& methods, const protocol::Vector<V8AttributeConfiguration>& attributes) +{ + v8::Local<v8::FunctionTemplate> functionTemplate = v8::FunctionTemplate::New(isolate); + + functionTemplate->SetClassName(v8::String::NewFromUtf8(isolate, className, v8::NewStringType::kInternalized).ToLocalChecked()); + v8::Local<v8::ObjectTemplate> instanceTemplate = functionTemplate->InstanceTemplate(); + + for (auto& config : attributes) { + v8::Local<v8::Name> v8name = v8::String::NewFromUtf8(isolate, config.name, v8::NewStringType::kInternalized).ToLocalChecked(); + instanceTemplate->SetAccessor(v8name, config.callback); + } + + for (auto& config : methods) { + v8::Local<v8::Name> v8name = v8::String::NewFromUtf8(isolate, config.name, v8::NewStringType::kInternalized).ToLocalChecked(); + v8::Local<v8::FunctionTemplate> functionTemplate = v8::FunctionTemplate::New(isolate, config.callback); + functionTemplate->RemovePrototype(); + instanceTemplate->Set(v8name, functionTemplate, static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::DontEnum | v8::ReadOnly)); + } + + return functionTemplate; +} + +v8::Local<v8::Object> InspectorWrapperBase::createWrapper(v8::Local<v8::FunctionTemplate> constructorTemplate, v8::Local<v8::Context> context) +{ + v8::MicrotasksScope microtasks(context->GetIsolate(), v8::MicrotasksScope::kDoNotRunMicrotasks); + v8::Local<v8::Function> function; + if (!constructorTemplate->GetFunction(context).ToLocal(&function)) + return v8::Local<v8::Object>(); + + v8::Local<v8::Object> result; + if (!function->NewInstance(context).ToLocal(&result)) + return v8::Local<v8::Object>(); + return result; +} + +void* InspectorWrapperBase::unwrap(v8::Local<v8::Context> context, v8::Local<v8::Object> object, const char* name) +{ + v8::Isolate* isolate = context->GetIsolate(); + DCHECK(context != v8::Debug::GetDebugContext(isolate)); + + v8::Local<v8::Private> privateKey = v8::Private::ForApi(isolate, v8::String::NewFromUtf8(isolate, name, v8::NewStringType::kInternalized).ToLocalChecked()); + + v8::Local<v8::Value> value; + if (!object->GetPrivate(context, privateKey).ToLocal(&value)) + return nullptr; + if (!value->IsExternal()) + return nullptr; + return value.As<v8::External>()->Value(); +} + +v8::Local<v8::String> InspectorWrapperBase::v8InternalizedString(v8::Isolate* isolate, const char* name) +{ + return v8::String::NewFromUtf8(isolate, name, v8::NewStringType::kInternalized).ToLocalChecked(); +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/InspectorWrapper.h b/deps/v8_inspector/platform/v8_inspector/InspectorWrapper.h new file mode 100644 index 00000000000000..e28697dbd75824 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/InspectorWrapper.h @@ -0,0 +1,88 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef InspectorWrapper_h +#define InspectorWrapper_h + +#include "platform/inspector_protocol/Collections.h" +#include <v8.h> + +namespace blink { + +class InspectorWrapperBase { +public: + struct V8MethodConfiguration { + const char* name; + v8::FunctionCallback callback; + }; + + struct V8AttributeConfiguration { + const char* name; + v8::AccessorNameGetterCallback callback; + }; + + static v8::Local<v8::FunctionTemplate> createWrapperTemplate(v8::Isolate*, const char* className, const protocol::Vector<V8MethodConfiguration>& methods, const protocol::Vector<V8AttributeConfiguration>& attributes); + +protected: + static v8::Local<v8::Object> createWrapper(v8::Local<v8::FunctionTemplate>, v8::Local<v8::Context>); + static void* unwrap(v8::Local<v8::Context>, v8::Local<v8::Object>, const char* name); + + static v8::Local<v8::String> v8InternalizedString(v8::Isolate*, const char* name); +}; + +template<class T, char* const hiddenPropertyName, char* const className> +class InspectorWrapper final : public InspectorWrapperBase { +public: + class WeakCallbackData final { + public: + WeakCallbackData(v8::Isolate* isolate, T* impl, v8::Local<v8::Object> wrapper) + : m_impl(impl) + , m_persistent(isolate, wrapper) + { + m_persistent.SetWeak(this, &WeakCallbackData::weakCallback, v8::WeakCallbackType::kParameter); + } + + T* m_impl; + std::unique_ptr<T> m_implOwn; + + private: + static void weakCallback(const v8::WeakCallbackInfo<WeakCallbackData>& info) + { + delete info.GetParameter(); + } + + v8::Global<v8::Object> m_persistent; + }; + + static v8::Local<v8::FunctionTemplate> createWrapperTemplate(v8::Isolate* isolate, const protocol::Vector<V8MethodConfiguration>& methods, const protocol::Vector<V8AttributeConfiguration>& attributes) + { + return InspectorWrapperBase::createWrapperTemplate(isolate, className, methods, attributes); + } + + static v8::Local<v8::Object> wrap(v8::Local<v8::FunctionTemplate> constructorTemplate, v8::Local<v8::Context> context, T* object) + { + v8::Context::Scope contextScope(context); + v8::Local<v8::Object> result = InspectorWrapperBase::createWrapper(constructorTemplate, context); + if (result.IsEmpty()) + return v8::Local<v8::Object>(); + v8::Isolate* isolate = context->GetIsolate(); + v8::Local<v8::External> objectReference = v8::External::New(isolate, new WeakCallbackData(isolate, object, result)); + + v8::Local<v8::Private> privateKey = v8::Private::ForApi(isolate, v8::String::NewFromUtf8(isolate, hiddenPropertyName, v8::NewStringType::kInternalized).ToLocalChecked()); + result->SetPrivate(context, privateKey, objectReference); + return result; + } + + static T* unwrap(v8::Local<v8::Context> context, v8::Local<v8::Object> object) + { + void* data = InspectorWrapperBase::unwrap(context, object, hiddenPropertyName); + if (!data) + return nullptr; + return reinterpret_cast<WeakCallbackData*>(data)->m_impl; + } +}; + +} // namespace blink + +#endif // InspectorWrapper_h diff --git a/deps/v8_inspector/platform/v8_inspector/JavaScriptCallFrame.cpp b/deps/v8_inspector/platform/v8_inspector/JavaScriptCallFrame.cpp new file mode 100644 index 00000000000000..c72cadcafe2612 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/JavaScriptCallFrame.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "platform/v8_inspector/JavaScriptCallFrame.h" + +#include "platform/v8_inspector/V8Compat.h" +#include "platform/v8_inspector/V8StringUtil.h" + +#include <v8-debug.h> + +namespace blink { + +JavaScriptCallFrame::JavaScriptCallFrame(v8::Local<v8::Context> debuggerContext, v8::Local<v8::Object> callFrame) + : m_isolate(debuggerContext->GetIsolate()) + , m_debuggerContext(m_isolate, debuggerContext) + , m_callFrame(m_isolate, callFrame) +{ +} + +JavaScriptCallFrame::~JavaScriptCallFrame() +{ +} + +int JavaScriptCallFrame::callV8FunctionReturnInt(const char* name) const +{ + v8::HandleScope handleScope(m_isolate); + v8::MicrotasksScope microtasks(m_isolate, v8::MicrotasksScope::kDoNotRunMicrotasks); + v8::Local<v8::Context> context = v8::Local<v8::Context>::New(m_isolate, m_debuggerContext); + v8::Local<v8::Object> callFrame = v8::Local<v8::Object>::New(m_isolate, m_callFrame); + v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(callFrame->Get(toV8StringInternalized(m_isolate, name))); + v8::Local<v8::Value> result; + if (!func->Call(context, callFrame, 0, nullptr).ToLocal(&result) || !result->IsInt32()) + return 0; + return result.As<v8::Int32>()->Value(); +} + +int JavaScriptCallFrame::sourceID() const +{ + return callV8FunctionReturnInt("sourceID"); +} + +int JavaScriptCallFrame::line() const +{ + return callV8FunctionReturnInt("line"); +} + +int JavaScriptCallFrame::column() const +{ + return callV8FunctionReturnInt("column"); +} + +int JavaScriptCallFrame::contextId() const +{ + return callV8FunctionReturnInt("contextId"); +} + +bool JavaScriptCallFrame::isAtReturn() const +{ + v8::HandleScope handleScope(m_isolate); + v8::Local<v8::Value> result = v8::Local<v8::Object>::New(m_isolate, m_callFrame)->Get(toV8StringInternalized(m_isolate, "isAtReturn")); + if (result.IsEmpty() || !result->IsBoolean()) + return false; + return result->BooleanValue(); +} + +v8::Local<v8::Object> JavaScriptCallFrame::details() const +{ + v8::MicrotasksScope microtasks(m_isolate, v8::MicrotasksScope::kDoNotRunMicrotasks); + v8::Local<v8::Object> callFrame = v8::Local<v8::Object>::New(m_isolate, m_callFrame); + v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(callFrame->Get(toV8StringInternalized(m_isolate, "details"))); + return v8::Local<v8::Object>::Cast(func->Call(m_isolate->GetCurrentContext(), callFrame, 0, nullptr).ToLocalChecked()); +} + +v8::MaybeLocal<v8::Value> JavaScriptCallFrame::evaluate(v8::Local<v8::Value> expression) +{ + v8::MicrotasksScope microtasks(m_isolate, v8::MicrotasksScope::kRunMicrotasks); + v8::Local<v8::Object> callFrame = v8::Local<v8::Object>::New(m_isolate, m_callFrame); + v8::Local<v8::Function> evalFunction = v8::Local<v8::Function>::Cast(callFrame->Get(toV8StringInternalized(m_isolate, "evaluate"))); + return evalFunction->Call(m_isolate->GetCurrentContext(), callFrame, 1, &expression); +} + +v8::MaybeLocal<v8::Value> JavaScriptCallFrame::restart() +{ + v8::MicrotasksScope microtasks(m_isolate, v8::MicrotasksScope::kDoNotRunMicrotasks); + v8::Local<v8::Object> callFrame = v8::Local<v8::Object>::New(m_isolate, m_callFrame); + v8::Local<v8::Function> restartFunction = v8::Local<v8::Function>::Cast(callFrame->Get(toV8StringInternalized(m_isolate, "restart"))); + v8::Debug::SetLiveEditEnabled(m_isolate, true); + v8::MaybeLocal<v8::Value> result = restartFunction->Call(m_isolate->GetCurrentContext(), callFrame, 0, nullptr); + v8::Debug::SetLiveEditEnabled(m_isolate, false); + return result; +} + +v8::MaybeLocal<v8::Value> JavaScriptCallFrame::setVariableValue(int scopeNumber, v8::Local<v8::Value> variableName, v8::Local<v8::Value> newValue) +{ + v8::MicrotasksScope microtasks(m_isolate, v8::MicrotasksScope::kDoNotRunMicrotasks); + v8::Local<v8::Object> callFrame = v8::Local<v8::Object>::New(m_isolate, m_callFrame); + v8::Local<v8::Function> setVariableValueFunction = v8::Local<v8::Function>::Cast(callFrame->Get(toV8StringInternalized(m_isolate, "setVariableValue"))); + v8::Local<v8::Value> argv[] = { + v8::Local<v8::Value>(v8::Integer::New(m_isolate, scopeNumber)), + variableName, + newValue + }; + return setVariableValueFunction->Call(m_isolate->GetCurrentContext(), callFrame, PROTOCOL_ARRAY_LENGTH(argv), argv); +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/JavaScriptCallFrame.h b/deps/v8_inspector/platform/v8_inspector/JavaScriptCallFrame.h new file mode 100644 index 00000000000000..e34d1d03ec09a9 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/JavaScriptCallFrame.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JavaScriptCallFrame_h +#define JavaScriptCallFrame_h + +#include "platform/inspector_protocol/Collections.h" +#include "platform/inspector_protocol/String16.h" +#include "wtf/PtrUtil.h" +#include "wtf/PtrUtil.h" +#include <v8.h> + +namespace blink { + +class JavaScriptCallFrame { +public: + static std::unique_ptr<JavaScriptCallFrame> create(v8::Local<v8::Context> debuggerContext, v8::Local<v8::Object> callFrame) + { + return wrapUnique(new JavaScriptCallFrame(debuggerContext, callFrame)); + } + ~JavaScriptCallFrame(); + + int sourceID() const; + int line() const; + int column() const; + int contextId() const; + + bool isAtReturn() const; + v8::Local<v8::Object> details() const; + + v8::MaybeLocal<v8::Value> evaluate(v8::Local<v8::Value> expression); + v8::MaybeLocal<v8::Value> restart(); + v8::MaybeLocal<v8::Value> setVariableValue(int scopeNumber, v8::Local<v8::Value> variableName, v8::Local<v8::Value> newValue); +private: + JavaScriptCallFrame(v8::Local<v8::Context> debuggerContext, v8::Local<v8::Object> callFrame); + + int callV8FunctionReturnInt(const char* name) const; + + v8::Isolate* m_isolate; + v8::Global<v8::Context> m_debuggerContext; + v8::Global<v8::Object> m_callFrame; +}; + +using JavaScriptCallFrames = protocol::Vector<std::unique_ptr<JavaScriptCallFrame>>; + +} // namespace blink + +#endif // JavaScriptCallFrame_h diff --git a/deps/v8_inspector/platform/v8_inspector/OWNERS b/deps/v8_inspector/platform/v8_inspector/OWNERS new file mode 100644 index 00000000000000..c34ad775ca7e08 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/OWNERS @@ -0,0 +1,5 @@ +alph@chromium.org +caseq@chromium.org +dgozman@chromium.org +kozyatinskiy@chromium.org +pfeldman@chromium.org diff --git a/deps/v8_inspector/platform/v8_inspector/RemoteObjectId.cpp b/deps/v8_inspector/platform/v8_inspector/RemoteObjectId.cpp new file mode 100644 index 00000000000000..1f0332b44fb1a0 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/RemoteObjectId.cpp @@ -0,0 +1,72 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/v8_inspector/RemoteObjectId.h" + +#include "platform/inspector_protocol/Parser.h" +#include "platform/inspector_protocol/Values.h" +#include "wtf/PtrUtil.h" + +namespace blink { + +RemoteObjectIdBase::RemoteObjectIdBase() : m_injectedScriptId(0) { } + +std::unique_ptr<protocol::DictionaryValue> RemoteObjectIdBase::parseInjectedScriptId(const String16& objectId) +{ + std::unique_ptr<protocol::Value> parsedValue = protocol::parseJSON(objectId); + if (!parsedValue || parsedValue->type() != protocol::Value::TypeObject) + return nullptr; + + std::unique_ptr<protocol::DictionaryValue> parsedObjectId(protocol::DictionaryValue::cast(parsedValue.release())); + bool success = parsedObjectId->getNumber("injectedScriptId", &m_injectedScriptId); + if (success) + return parsedObjectId; + return nullptr; +} + +RemoteObjectId::RemoteObjectId() : RemoteObjectIdBase(), m_id(0) { } + +std::unique_ptr<RemoteObjectId> RemoteObjectId::parse(ErrorString* errorString, const String16& objectId) +{ + std::unique_ptr<RemoteObjectId> result(new RemoteObjectId()); + std::unique_ptr<protocol::DictionaryValue> parsedObjectId = result->parseInjectedScriptId(objectId); + if (!parsedObjectId) { + *errorString = "Invalid remote object id"; + return nullptr; + } + + bool success = parsedObjectId->getNumber("id", &result->m_id); + if (!success) { + *errorString = "Invalid remote object id"; + return nullptr; + } + return result; +} + +RemoteCallFrameId::RemoteCallFrameId() : RemoteObjectIdBase(), m_frameOrdinal(0) { } + +std::unique_ptr<RemoteCallFrameId> RemoteCallFrameId::parse(ErrorString* errorString, const String16& objectId) +{ + std::unique_ptr<RemoteCallFrameId> result(new RemoteCallFrameId()); + std::unique_ptr<protocol::DictionaryValue> parsedObjectId = result->parseInjectedScriptId(objectId); + if (!parsedObjectId) { + *errorString = "Invalid call frame id"; + return nullptr; + } + + bool success = parsedObjectId->getNumber("ordinal", &result->m_frameOrdinal); + if (!success) { + *errorString = "Invalid call frame id"; + return nullptr; + } + + return result; +} + +String16 RemoteCallFrameId::serialize(int injectedScriptId, int frameOrdinal) +{ + return "{\"ordinal\":" + String16::number(frameOrdinal) + ",\"injectedScriptId\":" + String16::number(injectedScriptId) + "}"; +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/RemoteObjectId.h b/deps/v8_inspector/platform/v8_inspector/RemoteObjectId.h new file mode 100644 index 00000000000000..ae1cf47ca45be1 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/RemoteObjectId.h @@ -0,0 +1,59 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef RemoteObjectId_h +#define RemoteObjectId_h + +#include "platform/inspector_protocol/String16.h" +#include "platform/inspector_protocol/TypeBuilder.h" +#include "wtf/PtrUtil.h" + +namespace blink { + +namespace protocol { +class DictionaryValue; +} + +class RemoteObjectIdBase { +public: + int contextId() const { return m_injectedScriptId; } + +protected: + RemoteObjectIdBase(); + ~RemoteObjectIdBase() { } + + std::unique_ptr<protocol::DictionaryValue> parseInjectedScriptId(const String16&); + + int m_injectedScriptId; +}; + +class RemoteObjectId final : public RemoteObjectIdBase { +public: + static std::unique_ptr<RemoteObjectId> parse(ErrorString*, const String16&); + ~RemoteObjectId() { } + int id() const { return m_id; } + +private: + RemoteObjectId(); + + int m_id; +}; + +class RemoteCallFrameId final : public RemoteObjectIdBase { +public: + static std::unique_ptr<RemoteCallFrameId> parse(ErrorString*, const String16&); + ~RemoteCallFrameId() { } + + int frameOrdinal() const { return m_frameOrdinal; } + + static String16 serialize(int injectedScriptId, int frameOrdinal); +private: + RemoteCallFrameId(); + + int m_frameOrdinal; +}; + +} // namespace blink + +#endif // !defined(RemoteObjectId_h) diff --git a/deps/v8_inspector/platform/v8_inspector/ScriptBreakpoint.h b/deps/v8_inspector/platform/v8_inspector/ScriptBreakpoint.h new file mode 100644 index 00000000000000..6b2db214985c85 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/ScriptBreakpoint.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ScriptBreakpoint_h +#define ScriptBreakpoint_h + +#include "platform/inspector_protocol/Allocator.h" +#include "platform/inspector_protocol/String16.h" + +namespace blink { + +struct ScriptBreakpoint { + ScriptBreakpoint() : ScriptBreakpoint(0, 0, String16()) { } + + ScriptBreakpoint(int lineNumber, int columnNumber, const String16& condition) + : lineNumber(lineNumber) + , columnNumber(columnNumber) + , condition(condition) + { + } + + int lineNumber; + int columnNumber; + String16 condition; +}; + +} // namespace blink + +#endif // !defined(ScriptBreakpoint_h) diff --git a/deps/v8_inspector/platform/v8_inspector/V8Compat.h b/deps/v8_inspector/platform/v8_inspector/V8Compat.h new file mode 100644 index 00000000000000..17d104f7736ea7 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8Compat.h @@ -0,0 +1,28 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8Compat_h +#define V8Compat_h + +#include <v8.h> + +#if V8_MAJOR_VERSION < 5 || (V8_MAJOR_VERSION == 5 && V8_MINOR_VERSION < 2) +namespace v8 { + +// In standalone V8 inspector this is expected to be noop anyways... +class V8_EXPORT MicrotasksScope { +public: + enum Type { kRunMicrotasks, kDoNotRunMicrotasks }; + + MicrotasksScope(Isolate* isolate, Type type) + { + // No-op + } +}; + +} // namespace v8 + +#endif // V8_MAJOR_VERSION < 5 || (V8_MAJOR_VERSION == 5 && V8_MINOR_VERSION < 2) + +#endif // V8Compat_h diff --git a/deps/v8_inspector/platform/v8_inspector/V8Console.cpp b/deps/v8_inspector/platform/v8_inspector/V8Console.cpp new file mode 100644 index 00000000000000..f3f7fe171e3bf9 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8Console.cpp @@ -0,0 +1,730 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/v8_inspector/V8Console.h" + +#include "platform/inspector_protocol/String16.h" +#include "platform/v8_inspector/InjectedScript.h" +#include "platform/v8_inspector/InspectedContext.h" +#include "platform/v8_inspector/V8Compat.h" +#include "platform/v8_inspector/V8DebuggerAgentImpl.h" +#include "platform/v8_inspector/V8DebuggerImpl.h" +#include "platform/v8_inspector/V8InspectorSessionImpl.h" +#include "platform/v8_inspector/V8ProfilerAgentImpl.h" +#include "platform/v8_inspector/V8RuntimeAgentImpl.h" +#include "platform/v8_inspector/V8StackTraceImpl.h" +#include "platform/v8_inspector/V8StringUtil.h" +#include "platform/v8_inspector/public/ConsoleAPITypes.h" +#include "platform/v8_inspector/public/ConsoleTypes.h" +#include "platform/v8_inspector/public/V8DebuggerClient.h" + +namespace blink { + +namespace { + +v8::Local<v8::Private> inspectedContextPrivateKey(v8::Isolate* isolate) +{ + return v8::Private::ForApi(isolate, toV8StringInternalized(isolate, "V8Console#InspectedContext")); +} + +class ConsoleHelper { + PROTOCOL_DISALLOW_COPY(ConsoleHelper); +public: + ConsoleHelper(const v8::FunctionCallbackInfo<v8::Value>& info) + : m_info(info) + , m_isolate(info.GetIsolate()) + , m_context(info.GetIsolate()->GetCurrentContext()) + , m_inspectedContext(nullptr) + , m_debuggerClient(nullptr) + { + } + + v8::Local<v8::Object> ensureConsole() + { + if (m_console.IsEmpty()) { + DCHECK(!m_info.Data().IsEmpty()); + DCHECK(!m_info.Data()->IsUndefined()); + m_console = m_info.Data().As<v8::Object>(); + } + return m_console; + } + + InspectedContext* ensureInspectedContext() + { + if (m_inspectedContext) + return m_inspectedContext; + v8::Local<v8::Object> console = ensureConsole(); + + v8::Local<v8::Private> key = inspectedContextPrivateKey(m_isolate); + v8::Local<v8::Value> inspectedContextValue; + if (!console->GetPrivate(m_context, key).ToLocal(&inspectedContextValue)) + return nullptr; + DCHECK(inspectedContextValue->IsExternal()); + m_inspectedContext = static_cast<InspectedContext*>(inspectedContextValue.As<v8::External>()->Value()); + return m_inspectedContext; + } + + V8DebuggerClient* ensureDebuggerClient() + { + if (m_debuggerClient) + return m_debuggerClient; + InspectedContext* inspectedContext = ensureInspectedContext(); + if (!inspectedContext) + return nullptr; + m_debuggerClient = inspectedContext->debugger()->client(); + return m_debuggerClient; + } + + void addMessage(MessageType type, MessageLevel level, bool allowEmptyArguments, int skipArgumentCount) + { + if (!allowEmptyArguments && !m_info.Length()) + return; + if (V8DebuggerClient* debuggerClient = ensureDebuggerClient()) + debuggerClient->reportMessageToConsole(m_context, type, level, String16(), &m_info, skipArgumentCount, -1); + } + + void addMessage(MessageType type, MessageLevel level, const String16& message) + { + if (V8DebuggerClient* debuggerClient = ensureDebuggerClient()) + debuggerClient->reportMessageToConsole(m_context, type, level, message, nullptr, 0 /* skipArgumentsCount */, 1 /* maxStackSize */); + } + + void addDeprecationMessage(const char* id, const String16& message) + { + if (checkAndSetPrivateFlagOnConsole(id, false)) + return; + if (V8DebuggerClient* debuggerClient = ensureDebuggerClient()) + debuggerClient->reportMessageToConsole(m_context, LogMessageType, WarningMessageLevel, message, nullptr, 0 /* skipArgumentsCount */, 0 /* maxStackSize */); + } + + bool firstArgToBoolean(bool defaultValue) + { + if (m_info.Length() < 1) + return defaultValue; + if (m_info[0]->IsBoolean()) + return m_info[0].As<v8::Boolean>()->Value(); + return m_info[0]->BooleanValue(m_context).FromMaybe(defaultValue); + } + + String16 firstArgToString(const String16& defaultValue) + { + if (m_info.Length() < 1) + return defaultValue; + v8::Local<v8::String> titleValue; + if (m_info[0]->IsObject()) { + if (!m_info[0].As<v8::Object>()->ObjectProtoToString(m_context).ToLocal(&titleValue)) + return defaultValue; + } else { + if (!m_info[0]->ToString(m_context).ToLocal(&titleValue)) + return defaultValue; + } + return toProtocolString(titleValue); + } + + v8::MaybeLocal<v8::Object> firstArgAsObject() + { + if (m_info.Length() < 1 || !m_info[0]->IsObject()) + return v8::MaybeLocal<v8::Object>(); + return m_info[0].As<v8::Object>(); + } + + v8::MaybeLocal<v8::Function> firstArgAsFunction() + { + if (m_info.Length() < 1 || !m_info[0]->IsFunction()) + return v8::MaybeLocal<v8::Function>(); + return m_info[0].As<v8::Function>(); + } + + v8::MaybeLocal<v8::Map> privateMap(const char* name) + { + v8::Local<v8::Object> console = ensureConsole(); + v8::Local<v8::Private> privateKey = v8::Private::ForApi(m_isolate, toV8StringInternalized(m_isolate, name)); + v8::Local<v8::Value> mapValue; + if (!console->GetPrivate(m_context, privateKey).ToLocal(&mapValue)) + return v8::MaybeLocal<v8::Map>(); + if (mapValue->IsUndefined()) { + v8::Local<v8::Map> map = v8::Map::New(m_isolate); + if (!console->SetPrivate(m_context, privateKey, map).FromMaybe(false)) + return v8::MaybeLocal<v8::Map>(); + return map; + } + return mapValue->IsMap() ? mapValue.As<v8::Map>() : v8::MaybeLocal<v8::Map>(); + } + + int64_t getIntFromMap(v8::Local<v8::Map> map, const String16& key, int64_t defaultValue) + { + v8::Local<v8::String> v8Key = toV8String(m_isolate, key); + if (!map->Has(m_context, v8Key).FromMaybe(false)) + return defaultValue; + v8::Local<v8::Value> intValue; + if (!map->Get(m_context, v8Key).ToLocal(&intValue)) + return defaultValue; + return intValue.As<v8::Integer>()->Value(); + } + + void setIntOnMap(v8::Local<v8::Map> map, const String16& key, int64_t value) + { + v8::Local<v8::String> v8Key = toV8String(m_isolate, key); + if (!map->Set(m_context, v8Key, v8::Integer::New(m_isolate, value)).ToLocal(&map)) + return; + } + + double getDoubleFromMap(v8::Local<v8::Map> map, const String16& key, double defaultValue) + { + v8::Local<v8::String> v8Key = toV8String(m_isolate, key); + if (!map->Has(m_context, v8Key).FromMaybe(false)) + return defaultValue; + v8::Local<v8::Value> intValue; + if (!map->Get(m_context, v8Key).ToLocal(&intValue)) + return defaultValue; + return intValue.As<v8::Number>()->Value(); + } + + void setDoubleOnMap(v8::Local<v8::Map> map, const String16& key, double value) + { + v8::Local<v8::String> v8Key = toV8String(m_isolate, key); + if (!map->Set(m_context, v8Key, v8::Number::New(m_isolate, value)).ToLocal(&map)) + return; + } + + V8ProfilerAgentImpl* profilerAgent() + { + if (V8InspectorSessionImpl* session = currentSession()) { + if (session && session->profilerAgentImpl()->enabled()) + return session->profilerAgentImpl(); + } + return nullptr; + } + + V8DebuggerAgentImpl* debuggerAgent() + { + if (V8InspectorSessionImpl* session = currentSession()) { + if (session && session->debuggerAgentImpl()->enabled()) + return session->debuggerAgentImpl(); + } + return nullptr; + } + + V8InspectorSessionImpl* currentSession() + { + InspectedContext* inspectedContext = ensureInspectedContext(); + if (!inspectedContext) + return nullptr; + return inspectedContext->debugger()->sessionForContextGroup(inspectedContext->contextGroupId()); + } + +private: + const v8::FunctionCallbackInfo<v8::Value>& m_info; + v8::Isolate* m_isolate; + v8::Local<v8::Context> m_context; + v8::Local<v8::Object> m_console; + InspectedContext* m_inspectedContext; + V8DebuggerClient* m_debuggerClient; + + bool checkAndSetPrivateFlagOnConsole(const char* name, bool defaultValue) + { + v8::Local<v8::Object> console = ensureConsole(); + v8::Local<v8::Private> key = v8::Private::ForApi(m_isolate, toV8StringInternalized(m_isolate, name)); + v8::Local<v8::Value> flagValue; + if (!console->GetPrivate(m_context, key).ToLocal(&flagValue)) + return defaultValue; + DCHECK(flagValue->IsUndefined() || flagValue->IsBoolean()); + if (flagValue->IsBoolean()) { + DCHECK(flagValue.As<v8::Boolean>()->Value()); + return true; + } + if (!console->SetPrivate(m_context, key, v8::True(m_isolate)).FromMaybe(false)) + return defaultValue; + return false; + } +}; + +void returnDataCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + info.GetReturnValue().Set(info.Data()); +} + +void createBoundFunctionProperty(v8::Local<v8::Context> context, v8::Local<v8::Object> console, const char* name, v8::FunctionCallback callback, const char* description = nullptr) +{ + v8::Local<v8::String> funcName = toV8StringInternalized(context->GetIsolate(), name); + v8::Local<v8::Function> func; + if (!v8::Function::New(context, callback, console).ToLocal(&func)) + return; + func->SetName(funcName); + if (description) { + v8::Local<v8::String> returnValue = toV8String(context->GetIsolate(), description); + v8::Local<v8::Function> toStringFunction; + if (v8::Function::New(context, returnDataCallback, returnValue).ToLocal(&toStringFunction)) + func->Set(toV8StringInternalized(context->GetIsolate(), "toString"), toStringFunction); + } + if (!console->Set(context, funcName, func).FromMaybe(false)) + return; +} + +} // namespace + +void V8Console::debugCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper(info).addMessage(LogMessageType, DebugMessageLevel, false, 0); +} + +void V8Console::errorCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper(info).addMessage(LogMessageType, ErrorMessageLevel, false, 0); +} + +void V8Console::infoCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper(info).addMessage(LogMessageType, InfoMessageLevel, false, 0); +} + +void V8Console::logCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper(info).addMessage(LogMessageType, LogMessageLevel, false, 0); +} + +void V8Console::warnCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper(info).addMessage(LogMessageType, WarningMessageLevel, false, 0); +} + +void V8Console::dirCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper(info).addMessage(DirMessageType, LogMessageLevel, false, 0); +} + +void V8Console::dirxmlCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper(info).addMessage(DirXMLMessageType, LogMessageLevel, false, 0); +} + +void V8Console::tableCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper(info).addMessage(TableMessageType, LogMessageLevel, false, 0); +} + +void V8Console::traceCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper(info).addMessage(TraceMessageType, LogMessageLevel, true, 0); +} + +void V8Console::groupCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper(info).addMessage(StartGroupMessageType, LogMessageLevel, true, 0); +} + +void V8Console::groupCollapsedCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper(info).addMessage(StartGroupCollapsedMessageType, LogMessageLevel, true, 0); +} + +void V8Console::groupEndCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper(info).addMessage(EndGroupMessageType, LogMessageLevel, true, 0); +} + +void V8Console::clearCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper(info).addMessage(ClearMessageType, LogMessageLevel, true, 0); +} + +void V8Console::countCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper helper(info); + + String16 title = helper.firstArgToString(String16()); + String16 identifier; + if (title.isEmpty()) { + std::unique_ptr<V8StackTraceImpl> stackTrace = V8StackTraceImpl::capture(nullptr, 1); + if (stackTrace) + identifier = stackTrace->topSourceURL() + ":" + String16::number(stackTrace->topLineNumber()); + } else { + identifier = title + "@"; + } + + v8::Local<v8::Map> countMap; + if (!helper.privateMap("V8Console#countMap").ToLocal(&countMap)) + return; + int64_t count = helper.getIntFromMap(countMap, identifier, 0) + 1; + helper.setIntOnMap(countMap, identifier, count); + helper.addMessage(CountMessageType, DebugMessageLevel, title + ": " + String16::number(count)); +} + +void V8Console::assertCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper helper(info); + if (helper.firstArgToBoolean(false)) + return; + helper.addMessage(AssertMessageType, ErrorMessageLevel, true, 1); + if (V8DebuggerAgentImpl* debuggerAgent = helper.debuggerAgent()) + debuggerAgent->breakProgramOnException(protocol::Debugger::Paused::ReasonEnum::Assert, nullptr); +} + +void V8Console::markTimelineCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper(info).addDeprecationMessage("V8Console#markTimelineDeprecated", "'console.markTimeline' is deprecated. Please use 'console.timeStamp' instead."); + timeStampCallback(info); +} + +void V8Console::profileCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper helper(info); + if (V8ProfilerAgentImpl* profilerAgent = helper.profilerAgent()) + profilerAgent->consoleProfile(helper.firstArgToString(String16())); +} + +void V8Console::profileEndCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper helper(info); + if (V8ProfilerAgentImpl* profilerAgent = helper.profilerAgent()) + profilerAgent->consoleProfileEnd(helper.firstArgToString(String16())); +} + +static void timeFunction(const v8::FunctionCallbackInfo<v8::Value>& info, bool timelinePrefix) +{ + ConsoleHelper helper(info); + if (V8DebuggerClient* client = helper.ensureDebuggerClient()) { + String16 protocolTitle = helper.firstArgToString("default"); + if (timelinePrefix) + protocolTitle = "Timeline '" + protocolTitle + "'"; + client->consoleTime(protocolTitle); + + v8::Local<v8::Map> timeMap; + if (!helper.privateMap("V8Console#timeMap").ToLocal(&timeMap)) + return; + helper.setDoubleOnMap(timeMap, protocolTitle, client->currentTimeMS()); + } +} + +static void timeEndFunction(const v8::FunctionCallbackInfo<v8::Value>& info, bool timelinePrefix) +{ + ConsoleHelper helper(info); + if (V8DebuggerClient* client = helper.ensureDebuggerClient()) { + String16 protocolTitle = helper.firstArgToString("default"); + if (timelinePrefix) + protocolTitle = "Timeline '" + protocolTitle + "'"; + client->consoleTimeEnd(protocolTitle); + + v8::Local<v8::Map> timeMap; + if (!helper.privateMap("V8Console#timeMap").ToLocal(&timeMap)) + return; + double elapsed = client->currentTimeMS() - helper.getDoubleFromMap(timeMap, protocolTitle, 0.0); + String16 message = protocolTitle + ": " + String16::fromDoubleFixedPrecision(elapsed, 3) + "ms"; + helper.addMessage(TimeEndMessageType, DebugMessageLevel, message); + } +} + +void V8Console::timelineCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper(info).addDeprecationMessage("V8Console#timeline", "'console.timeline' is deprecated. Please use 'console.time' instead."); + timeFunction(info, true); +} + +void V8Console::timelineEndCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper(info).addDeprecationMessage("V8Console#timelineEnd", "'console.timelineEnd' is deprecated. Please use 'console.timeEnd' instead."); + timeEndFunction(info, true); +} + +void V8Console::timeCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + timeFunction(info, false); +} + +void V8Console::timeEndCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + timeEndFunction(info, false); +} + +void V8Console::timeStampCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper helper(info); + if (V8DebuggerClient* client = helper.ensureDebuggerClient()) + client->consoleTimeStamp(helper.firstArgToString(String16())); +} + +void V8Console::memoryGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + if (V8DebuggerClient* client = ConsoleHelper(info).ensureDebuggerClient()) { + v8::Local<v8::Value> memoryValue; + if (!client->memoryInfo(info.GetIsolate(), info.GetIsolate()->GetCurrentContext(), info.Holder()).ToLocal(&memoryValue)) + return; + info.GetReturnValue().Set(memoryValue); + } +} + +void V8Console::memorySetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + // We can't make the attribute readonly as it breaks existing code that relies on being able to assign to console.memory in strict mode. Instead, the setter just ignores the passed value. http://crbug.com/468611 +} + +void V8Console::keysCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + v8::Isolate* isolate = info.GetIsolate(); + info.GetReturnValue().Set(v8::Array::New(isolate)); + + ConsoleHelper helper(info); + v8::Local<v8::Object> obj; + if (!helper.firstArgAsObject().ToLocal(&obj)) + return; + v8::Local<v8::Array> names; + if (!obj->GetOwnPropertyNames(isolate->GetCurrentContext()).ToLocal(&names)) + return; + info.GetReturnValue().Set(names); +} + +void V8Console::valuesCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + v8::Isolate* isolate = info.GetIsolate(); + info.GetReturnValue().Set(v8::Array::New(isolate)); + + ConsoleHelper helper(info); + v8::Local<v8::Object> obj; + if (!helper.firstArgAsObject().ToLocal(&obj)) + return; + v8::Local<v8::Array> names; + v8::Local<v8::Context> context = isolate->GetCurrentContext(); + if (!obj->GetOwnPropertyNames(context).ToLocal(&names)) + return; + v8::Local<v8::Array> values = v8::Array::New(isolate, names->Length()); + for (size_t i = 0; i < names->Length(); ++i) { + v8::Local<v8::Value> key; + if (!names->Get(context, i).ToLocal(&key)) + continue; + v8::Local<v8::Value> value; + if (!obj->Get(context, key).ToLocal(&value)) + continue; + if (!values->Set(context, i, value).FromMaybe(false)) + continue; + } + info.GetReturnValue().Set(values); +} + +static void setFunctionBreakpoint(ConsoleHelper& helper, v8::Local<v8::Function> function, V8DebuggerAgentImpl::BreakpointSource source, const String16& condition, bool enable) +{ + V8DebuggerAgentImpl* debuggerAgent = helper.debuggerAgent(); + if (!debuggerAgent) + return; + String16 scriptId = String16::number(function->ScriptId()); + int lineNumber = function->GetScriptLineNumber(); + int columnNumber = function->GetScriptColumnNumber(); + if (lineNumber == v8::Function::kLineOffsetNotFound || columnNumber == v8::Function::kLineOffsetNotFound) + return; + if (enable) + debuggerAgent->setBreakpointAt(scriptId, lineNumber, columnNumber, source, condition); + else + debuggerAgent->removeBreakpointAt(scriptId, lineNumber, columnNumber, source); +} + +void V8Console::debugFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper helper(info); + v8::Local<v8::Function> function; + if (!helper.firstArgAsFunction().ToLocal(&function)) + return; + setFunctionBreakpoint(helper, function, V8DebuggerAgentImpl::DebugCommandBreakpointSource, String16(), true); +} + +void V8Console::undebugFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper helper(info); + v8::Local<v8::Function> function; + if (!helper.firstArgAsFunction().ToLocal(&function)) + return; + setFunctionBreakpoint(helper, function, V8DebuggerAgentImpl::DebugCommandBreakpointSource, String16(), false); +} + +void V8Console::monitorFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper helper(info); + v8::Local<v8::Function> function; + if (!helper.firstArgAsFunction().ToLocal(&function)) + return; + v8::Local<v8::Value> name = function->GetName(); + if (!name->IsString() || !v8::Local<v8::String>::Cast(name)->Length()) + name = function->GetInferredName(); + String16 functionName = toProtocolStringWithTypeCheck(name); + String16Builder builder; + builder.append("console.log(\"function "); + if (functionName.isEmpty()) + builder.append("(anonymous function)"); + else + builder.append(functionName); + builder.append(" called\" + (arguments.length > 0 ? \" with arguments: \" + Array.prototype.join.call(arguments, \", \") : \"\")) && false"); + setFunctionBreakpoint(helper, function, V8DebuggerAgentImpl::MonitorCommandBreakpointSource, builder.toString(), true); +} + +void V8Console::unmonitorFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper helper(info); + v8::Local<v8::Function> function; + if (!helper.firstArgAsFunction().ToLocal(&function)) + return; + setFunctionBreakpoint(helper, function, V8DebuggerAgentImpl::MonitorCommandBreakpointSource, String16(), false); +} + +void V8Console::lastEvaluationResultCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + ConsoleHelper helper(info); + InspectedContext* context = helper.ensureInspectedContext(); + if (!context) + return; + if (InjectedScript* injectedScript = context->getInjectedScript()) + info.GetReturnValue().Set(injectedScript->lastEvaluationResult()); +} + +static void inspectImpl(const v8::FunctionCallbackInfo<v8::Value>& info, bool copyToClipboard) +{ + if (info.Length() < 1) + return; + if (!copyToClipboard) + info.GetReturnValue().Set(info[0]); + + ConsoleHelper helper(info); + InspectedContext* context = helper.ensureInspectedContext(); + if (!context) + return; + InjectedScript* injectedScript = context->getInjectedScript(); + if (!injectedScript) + return; + ErrorString errorString; + std::unique_ptr<protocol::Runtime::RemoteObject> wrappedObject = injectedScript->wrapObject(&errorString, info[0], "", false /** forceValueType */, false /** generatePreview */); + if (!wrappedObject || !errorString.isEmpty()) + return; + + std::unique_ptr<protocol::DictionaryValue> hints = protocol::DictionaryValue::create(); + if (copyToClipboard) + hints->setBoolean("copyToClipboard", true); + if (V8InspectorSessionImpl* session = helper.currentSession()) + session->runtimeAgentImpl()->inspect(std::move(wrappedObject), std::move(hints)); +} + +void V8Console::inspectCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + inspectImpl(info, false); +} + +void V8Console::copyCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + inspectImpl(info, true); +} + +void V8Console::inspectedObject(const v8::FunctionCallbackInfo<v8::Value>& info, unsigned num) +{ + DCHECK(num < V8InspectorSessionImpl::kInspectedObjectBufferSize); + ConsoleHelper helper(info); + if (V8InspectorSessionImpl* session = helper.currentSession()) { + V8InspectorSession::Inspectable* object = session->inspectedObject(num); + v8::Isolate* isolate = info.GetIsolate(); + if (object) + info.GetReturnValue().Set(object->get(isolate->GetCurrentContext())); + else + info.GetReturnValue().Set(v8::Undefined(isolate)); + } +} + +v8::Local<v8::Object> V8Console::createConsole(InspectedContext* inspectedContext, bool hasMemoryAttribute) +{ + v8::Local<v8::Context> context = inspectedContext->context(); + v8::Isolate* isolate = context->GetIsolate(); + v8::MicrotasksScope microtasksScope(isolate, v8::MicrotasksScope::kDoNotRunMicrotasks); + + v8::Local<v8::Object> console = v8::Object::New(isolate); + + createBoundFunctionProperty(context, console, "debug", V8Console::debugCallback); + createBoundFunctionProperty(context, console, "error", V8Console::errorCallback); + createBoundFunctionProperty(context, console, "info", V8Console::infoCallback); + createBoundFunctionProperty(context, console, "log", V8Console::logCallback); + createBoundFunctionProperty(context, console, "warn", V8Console::warnCallback); + createBoundFunctionProperty(context, console, "dir", V8Console::dirCallback); + createBoundFunctionProperty(context, console, "dirxml", V8Console::dirxmlCallback); + createBoundFunctionProperty(context, console, "table", V8Console::tableCallback); + createBoundFunctionProperty(context, console, "trace", V8Console::traceCallback); + createBoundFunctionProperty(context, console, "group", V8Console::groupCallback); + createBoundFunctionProperty(context, console, "groupCollapsed", V8Console::groupCollapsedCallback); + createBoundFunctionProperty(context, console, "groupEnd", V8Console::groupEndCallback); + createBoundFunctionProperty(context, console, "clear", V8Console::clearCallback); + createBoundFunctionProperty(context, console, "count", V8Console::countCallback); + createBoundFunctionProperty(context, console, "assert", V8Console::assertCallback); + createBoundFunctionProperty(context, console, "markTimeline", V8Console::markTimelineCallback); + createBoundFunctionProperty(context, console, "profile", V8Console::profileCallback); + createBoundFunctionProperty(context, console, "profileEnd", V8Console::profileEndCallback); + createBoundFunctionProperty(context, console, "timeline", V8Console::timelineCallback); + createBoundFunctionProperty(context, console, "timelineEnd", V8Console::timelineEndCallback); + createBoundFunctionProperty(context, console, "time", V8Console::timeCallback); + createBoundFunctionProperty(context, console, "timeEnd", V8Console::timeEndCallback); + createBoundFunctionProperty(context, console, "timeStamp", V8Console::timeStampCallback); + + if (hasMemoryAttribute) + console->SetAccessorProperty(toV8StringInternalized(isolate, "memory"), v8::Function::New(isolate, V8Console::memoryGetterCallback, console), v8::Function::New(isolate, V8Console::memorySetterCallback), static_cast<v8::PropertyAttribute>(v8::None), v8::DEFAULT); + + console->SetPrivate(context, inspectedContextPrivateKey(isolate), v8::External::New(isolate, inspectedContext)); + return console; +} + +v8::Local<v8::Object> V8Console::createCommandLineAPI(InspectedContext* inspectedContext) +{ + v8::Local<v8::Context> context = inspectedContext->context(); + v8::Isolate* isolate = context->GetIsolate(); + v8::MicrotasksScope microtasksScope(isolate, v8::MicrotasksScope::kDoNotRunMicrotasks); + + v8::Local<v8::Object> commandLineAPI = v8::Object::New(isolate); + + createBoundFunctionProperty(context, commandLineAPI, "dir", V8Console::dirCallback, "function dir(value) { [Command Line API] }"); + createBoundFunctionProperty(context, commandLineAPI, "dirxml", V8Console::dirxmlCallback, "function dirxml(value) { [Command Line API] }"); + createBoundFunctionProperty(context, commandLineAPI, "profile", V8Console::profileCallback, "function profile(title) { [Command Line API] }"); + createBoundFunctionProperty(context, commandLineAPI, "profileEnd", V8Console::profileEndCallback, "function profileEnd(title) { [Command Line API] }"); + createBoundFunctionProperty(context, commandLineAPI, "clear", V8Console::clearCallback, "function clear() { [Command Line API] }"); + createBoundFunctionProperty(context, commandLineAPI, "table", V8Console::tableCallback, "function table(data, [columns]) { [Command Line API] }"); + + createBoundFunctionProperty(context, commandLineAPI, "keys", V8Console::keysCallback, "function keys(object) { [Command Line API] }"); + createBoundFunctionProperty(context, commandLineAPI, "values", V8Console::valuesCallback, "function values(object) { [Command Line API] }"); + createBoundFunctionProperty(context, commandLineAPI, "debug", V8Console::debugFunctionCallback, "function debug(function) { [Command Line API] }"); + createBoundFunctionProperty(context, commandLineAPI, "undebug", V8Console::undebugFunctionCallback, "function undebug(function) { [Command Line API] }"); + createBoundFunctionProperty(context, commandLineAPI, "monitor", V8Console::monitorFunctionCallback, "function monitor(function) { [Command Line API] }"); + createBoundFunctionProperty(context, commandLineAPI, "unmonitor", V8Console::unmonitorFunctionCallback, "function unmonitor(function) { [Command Line API] }"); + createBoundFunctionProperty(context, commandLineAPI, "inspect", V8Console::inspectCallback, "function inspect(object) { [Command Line API] }"); + createBoundFunctionProperty(context, commandLineAPI, "copy", V8Console::copyCallback, "function copy(value) { [Command Line API] }"); + createBoundFunctionProperty(context, commandLineAPI, "$_", V8Console::lastEvaluationResultCallback); + createBoundFunctionProperty(context, commandLineAPI, "$0", V8Console::inspectedObject0); + createBoundFunctionProperty(context, commandLineAPI, "$1", V8Console::inspectedObject1); + createBoundFunctionProperty(context, commandLineAPI, "$2", V8Console::inspectedObject2); + createBoundFunctionProperty(context, commandLineAPI, "$3", V8Console::inspectedObject3); + createBoundFunctionProperty(context, commandLineAPI, "$4", V8Console::inspectedObject4); + + commandLineAPI->SetPrivate(context, inspectedContextPrivateKey(isolate), v8::External::New(isolate, inspectedContext)); + return commandLineAPI; +} + +void V8Console::clearInspectedContextIfNeeded(v8::Local<v8::Context> context, v8::Local<v8::Object> console) +{ + v8::Isolate* isolate = context->GetIsolate(); + console->SetPrivate(context, inspectedContextPrivateKey(isolate), v8::External::New(isolate, nullptr)); +} + +bool V8Debugger::isCommandLineAPIMethod(const String16& name) +{ + static protocol::HashSet<String16> methods; + if (methods.size() == 0) { + const char* members[] = { "$", "$$", "$x", "dir", "dirxml", "keys", "values", "profile", "profileEnd", + "monitorEvents", "unmonitorEvents", "inspect", "copy", "clear", "getEventListeners", + "debug", "undebug", "monitor", "unmonitor", "table" }; + for (size_t i = 0; i < PROTOCOL_ARRAY_LENGTH(members); ++i) + methods.add(members[i]); + } + return methods.find(name) != methods.end(); +} + +bool V8Debugger::isCommandLineAPIGetter(const String16& name) +{ + protocol::HashSet<String16> getters; + if (getters.size() == 0) { + const char* members[] = { "$0", "$1", "$2", "$3", "$4", "$_" }; + for (size_t i = 0; i < PROTOCOL_ARRAY_LENGTH(members); ++i) + getters.add(members[i]); + } + return getters.find(name) != getters.end(); +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/V8Console.h b/deps/v8_inspector/platform/v8_inspector/V8Console.h new file mode 100644 index 00000000000000..bd5fb8cd296fe1 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8Console.h @@ -0,0 +1,71 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8Console_h +#define V8Console_h + +#include <v8.h> + +namespace blink { + +class InspectedContext; + +// Console API +// https://console.spec.whatwg.org/#console-interface +class V8Console { +public: + static v8::Local<v8::Object> createConsole(InspectedContext*, bool hasMemoryAttribute); + static v8::Local<v8::Object> createCommandLineAPI(InspectedContext*); + static void clearInspectedContextIfNeeded(v8::Local<v8::Context>, v8::Local<v8::Object> console); + +private: + static void debugCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void errorCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void infoCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void logCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void warnCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void dirCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void dirxmlCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void tableCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void traceCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void groupCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void groupCollapsedCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void groupEndCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void clearCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void countCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void assertCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void markTimelineCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void profileCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void profileEndCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void timelineCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void timelineEndCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void timeCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void timeEndCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void timeStampCallback(const v8::FunctionCallbackInfo<v8::Value>&); + // TODO(philipj): There is no spec for the Memory Info API, see blink-dev: + // https://groups.google.com/a/chromium.org/d/msg/blink-dev/g5YRCGpC9vs/b4OJz71NmPwJ + static void memoryGetterCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void memorySetterCallback(const v8::FunctionCallbackInfo<v8::Value>&); + + // CommandLineAPI + static void keysCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void valuesCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void debugFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void undebugFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void monitorFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void unmonitorFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void lastEvaluationResultCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void inspectCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void copyCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void inspectedObject(const v8::FunctionCallbackInfo<v8::Value>&, unsigned num); + static void inspectedObject0(const v8::FunctionCallbackInfo<v8::Value>& info) { inspectedObject(info, 0); } + static void inspectedObject1(const v8::FunctionCallbackInfo<v8::Value>& info) { inspectedObject(info, 1); } + static void inspectedObject2(const v8::FunctionCallbackInfo<v8::Value>& info) { inspectedObject(info, 2); } + static void inspectedObject3(const v8::FunctionCallbackInfo<v8::Value>& info) { inspectedObject(info, 3); } + static void inspectedObject4(const v8::FunctionCallbackInfo<v8::Value>& info) { inspectedObject(info, 4); } +}; + +} // namespace blink + +#endif // V8Console_h diff --git a/deps/v8_inspector/platform/v8_inspector/V8DebuggerAgentImpl.cpp b/deps/v8_inspector/platform/v8_inspector/V8DebuggerAgentImpl.cpp new file mode 100644 index 00000000000000..57cad6bdb36615 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8DebuggerAgentImpl.cpp @@ -0,0 +1,1455 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/v8_inspector/V8DebuggerAgentImpl.h" + +#include "platform/inspector_protocol/String16.h" +#include "platform/inspector_protocol/Values.h" +#include "platform/v8_inspector/InjectedScript.h" +#include "platform/v8_inspector/InspectedContext.h" +#include "platform/v8_inspector/JavaScriptCallFrame.h" +#include "platform/v8_inspector/RemoteObjectId.h" +#include "platform/v8_inspector/ScriptBreakpoint.h" +#include "platform/v8_inspector/V8InspectorSessionImpl.h" +#include "platform/v8_inspector/V8Regex.h" +#include "platform/v8_inspector/V8RuntimeAgentImpl.h" +#include "platform/v8_inspector/V8StackTraceImpl.h" +#include "platform/v8_inspector/V8StringUtil.h" +#include "platform/v8_inspector/public/V8ContentSearchUtil.h" +#include "platform/v8_inspector/public/V8Debugger.h" +#include "platform/v8_inspector/public/V8DebuggerClient.h" +#include "platform/v8_inspector/public/V8ToProtocolValue.h" + +using blink::protocol::Array; +using blink::protocol::Maybe; +using blink::protocol::Debugger::BreakpointId; +using blink::protocol::Debugger::CallFrame; +using blink::protocol::Debugger::CollectionEntry; +using blink::protocol::Runtime::ExceptionDetails; +using blink::protocol::Debugger::FunctionDetails; +using blink::protocol::Debugger::GeneratorObjectDetails; +using blink::protocol::Runtime::ScriptId; +using blink::protocol::Runtime::StackTrace; +using blink::protocol::Runtime::RemoteObject; + +namespace { +static const char v8AsyncTaskEventEnqueue[] = "enqueue"; +static const char v8AsyncTaskEventWillHandle[] = "willHandle"; +static const char v8AsyncTaskEventDidHandle[] = "didHandle"; +} + +namespace blink { + +namespace DebuggerAgentState { +static const char javaScriptBreakpoints[] = "javaScriptBreakopints"; +static const char pauseOnExceptionsState[] = "pauseOnExceptionsState"; +static const char asyncCallStackDepth[] = "asyncCallStackDepth"; +static const char blackboxPattern[] = "blackboxPattern"; +static const char debuggerEnabled[] = "debuggerEnabled"; + +// Breakpoint properties. +static const char url[] = "url"; +static const char isRegex[] = "isRegex"; +static const char lineNumber[] = "lineNumber"; +static const char columnNumber[] = "columnNumber"; +static const char condition[] = "condition"; +static const char skipAllPauses[] = "skipAllPauses"; + +} // namespace DebuggerAgentState; + +static const int maxSkipStepFrameCount = 128; + +static String16 breakpointIdSuffix(V8DebuggerAgentImpl::BreakpointSource source) +{ + switch (source) { + case V8DebuggerAgentImpl::UserBreakpointSource: + break; + case V8DebuggerAgentImpl::DebugCommandBreakpointSource: + return ":debug"; + case V8DebuggerAgentImpl::MonitorCommandBreakpointSource: + return ":monitor"; + } + return String16(); +} + +static String16 generateBreakpointId(const String16& scriptId, int lineNumber, int columnNumber, V8DebuggerAgentImpl::BreakpointSource source) +{ + return scriptId + ":" + String16::number(lineNumber) + ":" + String16::number(columnNumber) + breakpointIdSuffix(source); +} + +static bool positionComparator(const std::pair<int, int>& a, const std::pair<int, int>& b) +{ + if (a.first != b.first) + return a.first < b.first; + return a.second < b.second; +} + +static const LChar hexDigits[17] = "0123456789ABCDEF"; + +static void appendUnsignedAsHex(unsigned number, String16Builder* destination) +{ + for (size_t i = 0; i < 8; ++i) { + destination->append(hexDigits[number & 0xF]); + number >>= 4; + } +} + +// Hash algorithm for substrings is described in "Über die Komplexität der Multiplikation in +// eingeschränkten Branchingprogrammmodellen" by Woelfe. +// http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000 +static String16 calculateHash(const String16& str) +{ + static uint64_t prime[] = { 0x3FB75161, 0xAB1F4E4F, 0x82675BC5, 0xCD924D35, 0x81ABE279 }; + static uint64_t random[] = { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 }; + static uint32_t randomOdd[] = { 0xB4663807, 0xCC322BF5, 0xD4F91BBD, 0xA7BEA11D, 0x8F462907 }; + + uint64_t hashes[] = { 0, 0, 0, 0, 0 }; + uint64_t zi[] = { 1, 1, 1, 1, 1 }; + + const size_t hashesSize = PROTOCOL_ARRAY_LENGTH(hashes); + + size_t current = 0; + const uint32_t* data = nullptr; + data = reinterpret_cast<const uint32_t*>(str.characters16()); + for (size_t i = 0; i < str.sizeInBytes() / 4; i += 4) { + uint32_t v = data[i]; + uint64_t xi = v * randomOdd[current] & 0x7FFFFFFF; + hashes[current] = (hashes[current] + zi[current] * xi) % prime[current]; + zi[current] = (zi[current] * random[current]) % prime[current]; + current = current == hashesSize - 1 ? 0 : current + 1; + } + if (str.sizeInBytes() % 4) { + uint32_t v = 0; + for (size_t i = str.sizeInBytes() - str.sizeInBytes() % 4; i < str.sizeInBytes(); ++i) { + v <<= 8; + v |= reinterpret_cast<const uint8_t*>(data)[i]; + } + uint64_t xi = v * randomOdd[current] & 0x7FFFFFFF; + hashes[current] = (hashes[current] + zi[current] * xi) % prime[current]; + zi[current] = (zi[current] * random[current]) % prime[current]; + current = current == hashesSize - 1 ? 0 : current + 1; + } + + for (size_t i = 0; i < hashesSize; ++i) + hashes[i] = (hashes[i] + zi[i] * (prime[i] - 1)) % prime[i]; + + String16Builder hash; + for (size_t i = 0; i < hashesSize; ++i) + appendUnsignedAsHex(hashes[i], &hash); + return hash.toString(); +} + +static bool hasInternalError(ErrorString* errorString, bool hasError) +{ + if (hasError) + *errorString = "Internal error"; + return hasError; +} + +static std::unique_ptr<protocol::Debugger::Location> buildProtocolLocation(const String16& scriptId, int lineNumber, int columnNumber) +{ + return protocol::Debugger::Location::create() + .setScriptId(scriptId) + .setLineNumber(lineNumber) + .setColumnNumber(columnNumber).build(); +} + +V8DebuggerAgentImpl::V8DebuggerAgentImpl(V8InspectorSessionImpl* session) + : m_debugger(session->debugger()) + , m_session(session) + , m_enabled(false) + , m_state(nullptr) + , m_frontend(nullptr) + , m_isolate(m_debugger->isolate()) + , m_breakReason(protocol::Debugger::Paused::ReasonEnum::Other) + , m_scheduledDebuggerStep(NoStep) + , m_skipNextDebuggerStepOut(false) + , m_javaScriptPauseScheduled(false) + , m_steppingFromFramework(false) + , m_pausingOnNativeEvent(false) + , m_skippedStepFrameCount(0) + , m_recursionLevelForStepOut(0) + , m_recursionLevelForStepFrame(0) + , m_skipAllPauses(false) + , m_maxAsyncCallStackDepth(0) +{ + clearBreakDetails(); +} + +V8DebuggerAgentImpl::~V8DebuggerAgentImpl() +{ +} + +bool V8DebuggerAgentImpl::checkEnabled(ErrorString* errorString) +{ + if (enabled()) + return true; + *errorString = "Debugger agent is not enabled"; + return false; +} + +void V8DebuggerAgentImpl::enable() +{ + // debugger().addListener may result in reporting all parsed scripts to + // the agent so it should already be in enabled state by then. + m_enabled = true; + m_state->setBoolean(DebuggerAgentState::debuggerEnabled, true); + debugger().debuggerAgentEnabled(); + + protocol::Vector<V8DebuggerParsedScript> compiledScripts; + debugger().getCompiledScripts(m_session->contextGroupId(), compiledScripts); + for (size_t i = 0; i < compiledScripts.size(); i++) + didParseSource(compiledScripts[i]); + + // FIXME(WK44513): breakpoints activated flag should be synchronized between all front-ends + debugger().setBreakpointsActivated(true); + m_session->changeInstrumentationCounter(+1); +} + +bool V8DebuggerAgentImpl::enabled() +{ + return m_enabled; +} + +void V8DebuggerAgentImpl::enable(ErrorString* errorString) +{ + if (enabled()) + return; + + if (!m_session->client()->canExecuteScripts()) { + *errorString = "Script execution is prohibited"; + return; + } + + enable(); + DCHECK(m_frontend); +} + +void V8DebuggerAgentImpl::disable(ErrorString*) +{ + if (!enabled()) + return; + m_session->changeInstrumentationCounter(-1); + + m_state->setObject(DebuggerAgentState::javaScriptBreakpoints, protocol::DictionaryValue::create()); + m_state->setNumber(DebuggerAgentState::pauseOnExceptionsState, V8DebuggerImpl::DontPauseOnExceptions); + m_state->setNumber(DebuggerAgentState::asyncCallStackDepth, 0); + + if (!m_pausedContext.IsEmpty()) + debugger().continueProgram(); + debugger().debuggerAgentDisabled(); + m_pausedContext.Reset(); + JavaScriptCallFrames emptyCallFrames; + m_pausedCallFrames.swap(emptyCallFrames); + m_scripts.clear(); + m_blackboxedPositions.clear(); + m_breakpointIdToDebuggerBreakpointIds.clear(); + internalSetAsyncCallStackDepth(0); + m_continueToLocationBreakpointId = String16(); + clearBreakDetails(); + m_scheduledDebuggerStep = NoStep; + m_skipNextDebuggerStepOut = false; + m_javaScriptPauseScheduled = false; + m_steppingFromFramework = false; + m_pausingOnNativeEvent = false; + m_skippedStepFrameCount = 0; + m_recursionLevelForStepFrame = 0; + m_skipAllPauses = false; + m_blackboxPattern = nullptr; + m_state->remove(DebuggerAgentState::blackboxPattern); + m_enabled = false; + m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false); +} + +void V8DebuggerAgentImpl::internalSetAsyncCallStackDepth(int depth) +{ + if (depth <= 0) { + m_maxAsyncCallStackDepth = 0; + allAsyncTasksCanceled(); + } else { + m_maxAsyncCallStackDepth = depth; + } +} + +void V8DebuggerAgentImpl::setInspectorState(protocol::DictionaryValue* state) +{ + m_state = state; +} + +void V8DebuggerAgentImpl::clearFrontend() +{ + ErrorString error; + disable(&error); + DCHECK(m_frontend); + m_frontend = nullptr; +} + +void V8DebuggerAgentImpl::restore() +{ + DCHECK(!m_enabled); + if (!m_state->booleanProperty(DebuggerAgentState::debuggerEnabled, false)) + return; + if (!m_session->client()->canExecuteScripts()) + return; + + enable(); + ErrorString error; + + int pauseState = V8DebuggerImpl::DontPauseOnExceptions; + m_state->getNumber(DebuggerAgentState::pauseOnExceptionsState, &pauseState); + setPauseOnExceptionsImpl(&error, pauseState); + DCHECK(error.isEmpty()); + + m_skipAllPauses = m_state->booleanProperty(DebuggerAgentState::skipAllPauses, false); + + int asyncCallStackDepth = 0; + m_state->getNumber(DebuggerAgentState::asyncCallStackDepth, &asyncCallStackDepth); + internalSetAsyncCallStackDepth(asyncCallStackDepth); + + String16 blackboxPattern; + if (m_state->getString(DebuggerAgentState::blackboxPattern, &blackboxPattern)) { + if (!setBlackboxPattern(&error, blackboxPattern)) + NOTREACHED(); + } +} + +void V8DebuggerAgentImpl::setBreakpointsActive(ErrorString* errorString, bool active) +{ + if (!checkEnabled(errorString)) + return; + debugger().setBreakpointsActivated(active); +} + +void V8DebuggerAgentImpl::setSkipAllPauses(ErrorString*, bool skipped) +{ + m_skipAllPauses = skipped; + m_state->setBoolean(DebuggerAgentState::skipAllPauses, m_skipAllPauses); +} + +static std::unique_ptr<protocol::DictionaryValue> buildObjectForBreakpointCookie(const String16& url, int lineNumber, int columnNumber, const String16& condition, bool isRegex) +{ + std::unique_ptr<protocol::DictionaryValue> breakpointObject = protocol::DictionaryValue::create(); + breakpointObject->setString(DebuggerAgentState::url, url); + breakpointObject->setNumber(DebuggerAgentState::lineNumber, lineNumber); + breakpointObject->setNumber(DebuggerAgentState::columnNumber, columnNumber); + breakpointObject->setString(DebuggerAgentState::condition, condition); + breakpointObject->setBoolean(DebuggerAgentState::isRegex, isRegex); + return breakpointObject; +} + +static bool matches(V8DebuggerImpl* debugger, const String16& url, const String16& pattern, bool isRegex) +{ + if (isRegex) { + V8Regex regex(debugger, pattern, true); + return regex.match(url) != -1; + } + return url == pattern; +} + +void V8DebuggerAgentImpl::setBreakpointByUrl(ErrorString* errorString, + int lineNumber, + const Maybe<String16>& optionalURL, + const Maybe<String16>& optionalURLRegex, + const Maybe<int>& optionalColumnNumber, + const Maybe<String16>& optionalCondition, + String16* outBreakpointId, + std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations) +{ + *locations = Array<protocol::Debugger::Location>::create(); + if (optionalURL.isJust() == optionalURLRegex.isJust()) { + *errorString = "Either url or urlRegex must be specified."; + return; + } + + String16 url = optionalURL.isJust() ? optionalURL.fromJust() : optionalURLRegex.fromJust(); + int columnNumber = 0; + if (optionalColumnNumber.isJust()) { + columnNumber = optionalColumnNumber.fromJust(); + if (columnNumber < 0) { + *errorString = "Incorrect column number"; + return; + } + } + String16 condition = optionalCondition.fromMaybe(""); + bool isRegex = optionalURLRegex.isJust(); + + String16 breakpointId = (isRegex ? "/" + url + "/" : url) + ":" + String16::number(lineNumber) + ":" + String16::number(columnNumber); + protocol::DictionaryValue* breakpointsCookie = m_state->getObject(DebuggerAgentState::javaScriptBreakpoints); + if (!breakpointsCookie) { + std::unique_ptr<protocol::DictionaryValue> newValue = protocol::DictionaryValue::create(); + breakpointsCookie = newValue.get(); + m_state->setObject(DebuggerAgentState::javaScriptBreakpoints, std::move(newValue)); + } + if (breakpointsCookie->get(breakpointId)) { + *errorString = "Breakpoint at specified location already exists."; + return; + } + + breakpointsCookie->setObject(breakpointId, buildObjectForBreakpointCookie(url, lineNumber, columnNumber, condition, isRegex)); + + ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition); + for (auto& script : m_scripts) { + if (!matches(m_debugger, script.second->sourceURL(), url, isRegex)) + continue; + std::unique_ptr<protocol::Debugger::Location> location = resolveBreakpoint(breakpointId, script.first, breakpoint, UserBreakpointSource); + if (location) + (*locations)->addItem(std::move(location)); + } + + *outBreakpointId = breakpointId; +} + +static bool parseLocation(ErrorString* errorString, std::unique_ptr<protocol::Debugger::Location> location, String16* scriptId, int* lineNumber, int* columnNumber) +{ + *scriptId = location->getScriptId(); + *lineNumber = location->getLineNumber(); + *columnNumber = location->getColumnNumber(0); + return true; +} + +void V8DebuggerAgentImpl::setBreakpoint(ErrorString* errorString, + std::unique_ptr<protocol::Debugger::Location> location, + const Maybe<String16>& optionalCondition, + String16* outBreakpointId, + std::unique_ptr<protocol::Debugger::Location>* actualLocation) +{ + String16 scriptId; + int lineNumber; + int columnNumber; + + if (!parseLocation(errorString, std::move(location), &scriptId, &lineNumber, &columnNumber)) + return; + + String16 condition = optionalCondition.fromMaybe(""); + + String16 breakpointId = generateBreakpointId(scriptId, lineNumber, columnNumber, UserBreakpointSource); + if (m_breakpointIdToDebuggerBreakpointIds.contains(breakpointId)) { + *errorString = "Breakpoint at specified location already exists."; + return; + } + ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition); + *actualLocation = resolveBreakpoint(breakpointId, scriptId, breakpoint, UserBreakpointSource); + if (*actualLocation) + *outBreakpointId = breakpointId; + else + *errorString = "Could not resolve breakpoint"; +} + +void V8DebuggerAgentImpl::removeBreakpoint(ErrorString* errorString, const String16& breakpointId) +{ + if (!checkEnabled(errorString)) + return; + protocol::DictionaryValue* breakpointsCookie = m_state->getObject(DebuggerAgentState::javaScriptBreakpoints); + if (breakpointsCookie) + breakpointsCookie->remove(breakpointId); + removeBreakpoint(breakpointId); +} + +void V8DebuggerAgentImpl::removeBreakpoint(const String16& breakpointId) +{ + DCHECK(enabled()); + BreakpointIdToDebuggerBreakpointIdsMap::iterator debuggerBreakpointIdsIterator = m_breakpointIdToDebuggerBreakpointIds.find(breakpointId); + if (debuggerBreakpointIdsIterator == m_breakpointIdToDebuggerBreakpointIds.end()) + return; + protocol::Vector<String16>* ids = debuggerBreakpointIdsIterator->second; + for (size_t i = 0; i < ids->size(); ++i) { + const String16& debuggerBreakpointId = ids->at(i); + + debugger().removeBreakpoint(debuggerBreakpointId); + m_serverBreakpoints.remove(debuggerBreakpointId); + } + m_breakpointIdToDebuggerBreakpointIds.remove(breakpointId); +} + +void V8DebuggerAgentImpl::continueToLocation(ErrorString* errorString, + std::unique_ptr<protocol::Debugger::Location> location, + const protocol::Maybe<bool>& interstateLocationOpt) +{ + if (!checkEnabled(errorString)) + return; + if (!m_continueToLocationBreakpointId.isEmpty()) { + debugger().removeBreakpoint(m_continueToLocationBreakpointId); + m_continueToLocationBreakpointId = ""; + } + + String16 scriptId; + int lineNumber; + int columnNumber; + + if (!parseLocation(errorString, std::move(location), &scriptId, &lineNumber, &columnNumber)) + return; + + ScriptBreakpoint breakpoint(lineNumber, columnNumber, ""); + m_continueToLocationBreakpointId = debugger().setBreakpoint(scriptId, breakpoint, &lineNumber, &columnNumber, interstateLocationOpt.fromMaybe(false)); + resume(errorString); +} + +void V8DebuggerAgentImpl::getBacktrace(ErrorString* errorString, std::unique_ptr<Array<CallFrame>>* callFrames, Maybe<StackTrace>* asyncStackTrace) +{ + if (!assertPaused(errorString)) + return; + m_pausedCallFrames.swap(debugger().currentCallFrames()); + *callFrames = currentCallFrames(errorString); + if (!*callFrames) + return; + *asyncStackTrace = currentAsyncStackTrace(); +} + +bool V8DebuggerAgentImpl::isCurrentCallStackEmptyOrBlackboxed() +{ + DCHECK(enabled()); + JavaScriptCallFrames callFrames = debugger().currentCallFrames(); + for (size_t index = 0; index < callFrames.size(); ++index) { + if (!isCallFrameWithUnknownScriptOrBlackboxed(callFrames[index])) + return false; + } + return true; +} + +bool V8DebuggerAgentImpl::isTopPausedCallFrameBlackboxed() +{ + DCHECK(enabled()); + return isCallFrameWithUnknownScriptOrBlackboxed(m_pausedCallFrames.size() ? m_pausedCallFrames[0] : nullptr); +} + +bool V8DebuggerAgentImpl::isCallFrameWithUnknownScriptOrBlackboxed(JavaScriptCallFrame* frame) +{ + if (!frame) + return true; + ScriptsMap::iterator it = m_scripts.find(String16::number(frame->sourceID())); + if (it == m_scripts.end()) { + // Unknown scripts are blackboxed. + return true; + } + if (m_blackboxPattern) { + String16 scriptSourceURL = it->second->sourceURL(); + if (!scriptSourceURL.isEmpty() && m_blackboxPattern->match(scriptSourceURL) != -1) + return true; + } + auto itBlackboxedPositions = m_blackboxedPositions.find(String16::number(frame->sourceID())); + if (itBlackboxedPositions == m_blackboxedPositions.end()) + return false; + + protocol::Vector<std::pair<int, int>>* ranges = itBlackboxedPositions->second; + auto itRange = std::lower_bound(ranges->begin(), ranges->end(), std::make_pair(frame->line(), frame->column()), positionComparator); + // Ranges array contains positions in script where blackbox state is changed. + // [(0,0) ... ranges[0]) isn't blackboxed, [ranges[0] ... ranges[1]) is blackboxed... + return std::distance(ranges->begin(), itRange) % 2; +} + +V8DebuggerAgentImpl::SkipPauseRequest V8DebuggerAgentImpl::shouldSkipExceptionPause(JavaScriptCallFrame* topCallFrame) +{ + if (m_steppingFromFramework) + return RequestNoSkip; + if (isCallFrameWithUnknownScriptOrBlackboxed(topCallFrame)) + return RequestContinue; + return RequestNoSkip; +} + +V8DebuggerAgentImpl::SkipPauseRequest V8DebuggerAgentImpl::shouldSkipStepPause(JavaScriptCallFrame* topCallFrame) +{ + if (m_steppingFromFramework) + return RequestNoSkip; + + if (m_skipNextDebuggerStepOut) { + m_skipNextDebuggerStepOut = false; + if (m_scheduledDebuggerStep == StepOut) + return RequestStepOut; + } + + if (!isCallFrameWithUnknownScriptOrBlackboxed(topCallFrame)) + return RequestNoSkip; + + if (m_skippedStepFrameCount >= maxSkipStepFrameCount) + return RequestStepOut; + + if (!m_skippedStepFrameCount) + m_recursionLevelForStepFrame = 1; + + ++m_skippedStepFrameCount; + return RequestStepFrame; +} + +std::unique_ptr<protocol::Debugger::Location> V8DebuggerAgentImpl::resolveBreakpoint(const String16& breakpointId, const String16& scriptId, const ScriptBreakpoint& breakpoint, BreakpointSource source) +{ + DCHECK(enabled()); + // FIXME: remove these checks once crbug.com/520702 is resolved. + CHECK(!breakpointId.isEmpty()); + CHECK(!scriptId.isEmpty()); + ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId); + if (scriptIterator == m_scripts.end()) + return nullptr; + V8DebuggerScript* script = scriptIterator->second; + if (breakpoint.lineNumber < script->startLine() || script->endLine() < breakpoint.lineNumber) + return nullptr; + + int actualLineNumber; + int actualColumnNumber; + String16 debuggerBreakpointId = debugger().setBreakpoint(scriptId, breakpoint, &actualLineNumber, &actualColumnNumber, false); + if (debuggerBreakpointId.isEmpty()) + return nullptr; + + m_serverBreakpoints.set(debuggerBreakpointId, std::make_pair(breakpointId, source)); + CHECK(!breakpointId.isEmpty()); + if (!m_breakpointIdToDebuggerBreakpointIds.contains(breakpointId)) + m_breakpointIdToDebuggerBreakpointIds.set(breakpointId, protocol::Vector<String16>()); + + BreakpointIdToDebuggerBreakpointIdsMap::iterator debuggerBreakpointIdsIterator = m_breakpointIdToDebuggerBreakpointIds.find(breakpointId); + debuggerBreakpointIdsIterator->second->append(debuggerBreakpointId); + + return buildProtocolLocation(scriptId, actualLineNumber, actualColumnNumber); +} + +void V8DebuggerAgentImpl::searchInContent(ErrorString* error, const String16& scriptId, const String16& query, + const Maybe<bool>& optionalCaseSensitive, + const Maybe<bool>& optionalIsRegex, + std::unique_ptr<Array<protocol::Debugger::SearchMatch>>* results) +{ + ScriptsMap::iterator it = m_scripts.find(scriptId); + if (it != m_scripts.end()) + *results = V8ContentSearchUtil::searchInTextByLines(m_session, it->second->source(), query, optionalCaseSensitive.fromMaybe(false), optionalIsRegex.fromMaybe(false)); + else + *error = String16("No script for id: " + scriptId); +} + +void V8DebuggerAgentImpl::setScriptSource(ErrorString* errorString, + const String16& scriptId, + const String16& newContent, + const Maybe<bool>& preview, + Maybe<protocol::Array<protocol::Debugger::CallFrame>>* newCallFrames, + Maybe<bool>* stackChanged, + Maybe<StackTrace>* asyncStackTrace, + Maybe<protocol::Debugger::SetScriptSourceError>* optOutCompileError) +{ + if (!checkEnabled(errorString)) + return; + if (!debugger().setScriptSource(scriptId, newContent, preview.fromMaybe(false), errorString, optOutCompileError, &m_pausedCallFrames, stackChanged)) + return; + + std::unique_ptr<Array<CallFrame>> callFrames = currentCallFrames(errorString); + if (!callFrames) + return; + *newCallFrames = std::move(callFrames); + *asyncStackTrace = currentAsyncStackTrace(); + + ScriptsMap::iterator it = m_scripts.find(scriptId); + if (it == m_scripts.end()) + return; + it->second->setSource(newContent); +} + +void V8DebuggerAgentImpl::restartFrame(ErrorString* errorString, + const String16& callFrameId, + std::unique_ptr<Array<CallFrame>>* newCallFrames, + Maybe<StackTrace>* asyncStackTrace) +{ + if (!assertPaused(errorString)) + return; + InjectedScript::CallFrameScope scope(errorString, m_debugger, m_session->contextGroupId(), callFrameId); + if (!scope.initialize()) + return; + if (scope.frameOrdinal() >= m_pausedCallFrames.size()) { + *errorString = "Could not find call frame with given id"; + return; + } + + v8::Local<v8::Value> resultValue; + v8::Local<v8::Boolean> result; + if (!m_pausedCallFrames[scope.frameOrdinal()]->restart().ToLocal(&resultValue) || scope.tryCatch().HasCaught() || !resultValue->ToBoolean(scope.context()).ToLocal(&result) || !result->Value()) { + *errorString = "Internal error"; + return; + } + m_pausedCallFrames.swap(debugger().currentCallFrames()); + + *newCallFrames = currentCallFrames(errorString); + if (!*newCallFrames) + return; + *asyncStackTrace = currentAsyncStackTrace(); +} + +void V8DebuggerAgentImpl::getScriptSource(ErrorString* error, const String16& scriptId, String16* scriptSource) +{ + if (!checkEnabled(error)) + return; + ScriptsMap::iterator it = m_scripts.find(scriptId); + if (it == m_scripts.end()) { + *error = "No script for id: " + scriptId; + return; + } + *scriptSource = it->second->source(); +} + +void V8DebuggerAgentImpl::getFunctionDetails(ErrorString* errorString, const String16& functionId, std::unique_ptr<FunctionDetails>* details) +{ + if (!checkEnabled(errorString)) + return; + InjectedScript::ObjectScope scope(errorString, m_debugger, m_session->contextGroupId(), functionId); + if (!scope.initialize()) + return; + if (!scope.object()->IsFunction()) { + *errorString = "Value with given id is not a function"; + return; + } + v8::Local<v8::Function> function = scope.object().As<v8::Function>(); + + v8::Local<v8::Value> scopesValue; + v8::Local<v8::Array> scopes; + if (m_debugger->functionScopes(function).ToLocal(&scopesValue) && scopesValue->IsArray()) { + scopes = scopesValue.As<v8::Array>(); + if (!scope.injectedScript()->wrapPropertyInArray(errorString, scopes, toV8StringInternalized(m_isolate, "object"), scope.objectGroupName())) + return; + } + + std::unique_ptr<FunctionDetails> functionDetails = FunctionDetails::create() + .setLocation(buildProtocolLocation(String16::number(function->ScriptId()), function->GetScriptLineNumber(), function->GetScriptColumnNumber())) + .setFunctionName(toProtocolStringWithTypeCheck(function->GetDebugName())) + .setIsGenerator(function->IsGeneratorFunction()).build(); + + if (!scopes.IsEmpty()) { + protocol::ErrorSupport errorSupport; + std::unique_ptr<protocol::Array<protocol::Debugger::Scope>> scopeChain = protocol::Array<protocol::Debugger::Scope>::parse(toProtocolValue(scope.context(), scopes).get(), &errorSupport); + if (hasInternalError(errorString, errorSupport.hasErrors())) + return; + functionDetails->setScopeChain(std::move(scopeChain)); + } + + *details = std::move(functionDetails); +} + +void V8DebuggerAgentImpl::getGeneratorObjectDetails(ErrorString* errorString, const String16& objectId, std::unique_ptr<GeneratorObjectDetails>* outDetails) +{ + if (!checkEnabled(errorString)) + return; + InjectedScript::ObjectScope scope(errorString, m_debugger, m_session->contextGroupId(), objectId); + if (!scope.initialize()) + return; + if (!scope.object()->IsObject()) { + *errorString = "Value with given id is not an Object"; + return; + } + v8::Local<v8::Object> object = scope.object().As<v8::Object>(); + + v8::Local<v8::Object> detailsObject; + v8::Local<v8::Value> detailsValue = debugger().generatorObjectDetails(object); + if (hasInternalError(errorString, !detailsValue->IsObject() || !detailsValue->ToObject(scope.context()).ToLocal(&detailsObject))) + return; + + if (!scope.injectedScript()->wrapObjectProperty(errorString, detailsObject, toV8StringInternalized(m_isolate, "function"), scope.objectGroupName())) + return; + + protocol::ErrorSupport errors; + std::unique_ptr<GeneratorObjectDetails> protocolDetails = GeneratorObjectDetails::parse(toProtocolValue(scope.context(), detailsObject).get(), &errors); + if (hasInternalError(errorString, !protocolDetails)) + return; + *outDetails = std::move(protocolDetails); +} + +void V8DebuggerAgentImpl::getCollectionEntries(ErrorString* errorString, const String16& objectId, std::unique_ptr<protocol::Array<CollectionEntry>>* outEntries) +{ + if (!checkEnabled(errorString)) + return; + InjectedScript::ObjectScope scope(errorString, m_debugger, m_session->contextGroupId(), objectId); + if (!scope.initialize()) + return; + if (!scope.object()->IsObject()) { + *errorString = "Object with given id is not a collection"; + return; + } + v8::Local<v8::Object> object = scope.object().As<v8::Object>(); + + v8::Local<v8::Value> entriesValue = m_debugger->collectionEntries(object); + if (hasInternalError(errorString, entriesValue.IsEmpty())) + return; + if (entriesValue->IsUndefined()) { + *errorString = "Object with given id is not a collection"; + return; + } + if (hasInternalError(errorString, !entriesValue->IsArray())) + return; + v8::Local<v8::Array> entriesArray = entriesValue.As<v8::Array>(); + if (!scope.injectedScript()->wrapPropertyInArray(errorString, entriesArray, toV8StringInternalized(m_isolate, "key"), scope.objectGroupName())) + return; + if (!scope.injectedScript()->wrapPropertyInArray(errorString, entriesArray, toV8StringInternalized(m_isolate, "value"), scope.objectGroupName())) + return; + protocol::ErrorSupport errors; + std::unique_ptr<protocol::Array<CollectionEntry>> entries = protocol::Array<CollectionEntry>::parse(toProtocolValue(scope.context(), entriesArray).get(), &errors); + if (hasInternalError(errorString, !entries)) + return; + *outEntries = std::move(entries); +} + +void V8DebuggerAgentImpl::schedulePauseOnNextStatement(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data) +{ + if (!enabled() || m_scheduledDebuggerStep == StepInto || m_javaScriptPauseScheduled || debugger().isPaused() || !debugger().breakpointsActivated()) + return; + m_breakReason = breakReason; + m_breakAuxData = std::move(data); + m_pausingOnNativeEvent = true; + m_skipNextDebuggerStepOut = false; + debugger().setPauseOnNextStatement(true); +} + +void V8DebuggerAgentImpl::schedulePauseOnNextStatementIfSteppingInto() +{ + DCHECK(enabled()); + if (m_scheduledDebuggerStep != StepInto || m_javaScriptPauseScheduled || debugger().isPaused()) + return; + clearBreakDetails(); + m_pausingOnNativeEvent = false; + m_skippedStepFrameCount = 0; + m_recursionLevelForStepFrame = 0; + debugger().setPauseOnNextStatement(true); +} + +void V8DebuggerAgentImpl::cancelPauseOnNextStatement() +{ + if (m_javaScriptPauseScheduled || debugger().isPaused()) + return; + clearBreakDetails(); + m_pausingOnNativeEvent = false; + debugger().setPauseOnNextStatement(false); +} + +bool V8DebuggerAgentImpl::v8AsyncTaskEventsEnabled() const +{ + return m_maxAsyncCallStackDepth; +} + +void V8DebuggerAgentImpl::didReceiveV8AsyncTaskEvent(v8::Local<v8::Context> context, const String16& eventType, const String16& eventName, int id) +{ + DCHECK(m_maxAsyncCallStackDepth); + // The scopes for the ids are defined by the eventName namespaces. There are currently two namespaces: "Object." and "Promise.". + void* ptr = reinterpret_cast<void*>(id * 4 + (eventName[0] == 'P' ? 2 : 0) + 1); + if (eventType == v8AsyncTaskEventEnqueue) + asyncTaskScheduled(eventName, ptr, false); + else if (eventType == v8AsyncTaskEventWillHandle) + asyncTaskStarted(ptr); + else if (eventType == v8AsyncTaskEventDidHandle) + asyncTaskFinished(ptr); + else + NOTREACHED(); +} + +void V8DebuggerAgentImpl::pause(ErrorString* errorString) +{ + if (!checkEnabled(errorString)) + return; + if (m_javaScriptPauseScheduled || debugger().isPaused()) + return; + clearBreakDetails(); + m_javaScriptPauseScheduled = true; + m_scheduledDebuggerStep = NoStep; + m_skippedStepFrameCount = 0; + m_steppingFromFramework = false; + debugger().setPauseOnNextStatement(true); +} + +void V8DebuggerAgentImpl::resume(ErrorString* errorString) +{ + if (!assertPaused(errorString)) + return; + m_scheduledDebuggerStep = NoStep; + m_steppingFromFramework = false; + m_session->releaseObjectGroup(V8InspectorSession::backtraceObjectGroup); + debugger().continueProgram(); +} + +void V8DebuggerAgentImpl::stepOver(ErrorString* errorString) +{ + if (!assertPaused(errorString)) + return; + // StepOver at function return point should fallback to StepInto. + JavaScriptCallFrame* frame = m_pausedCallFrames.size() ? m_pausedCallFrames[0] : nullptr; + if (frame && frame->isAtReturn()) { + stepInto(errorString); + return; + } + m_scheduledDebuggerStep = StepOver; + m_steppingFromFramework = isTopPausedCallFrameBlackboxed(); + m_session->releaseObjectGroup(V8InspectorSession::backtraceObjectGroup); + debugger().stepOverStatement(); +} + +void V8DebuggerAgentImpl::stepInto(ErrorString* errorString) +{ + if (!assertPaused(errorString)) + return; + m_scheduledDebuggerStep = StepInto; + m_steppingFromFramework = isTopPausedCallFrameBlackboxed(); + m_session->releaseObjectGroup(V8InspectorSession::backtraceObjectGroup); + debugger().stepIntoStatement(); +} + +void V8DebuggerAgentImpl::stepOut(ErrorString* errorString) +{ + if (!assertPaused(errorString)) + return; + m_scheduledDebuggerStep = StepOut; + m_skipNextDebuggerStepOut = false; + m_recursionLevelForStepOut = 1; + m_steppingFromFramework = isTopPausedCallFrameBlackboxed(); + m_session->releaseObjectGroup(V8InspectorSession::backtraceObjectGroup); + debugger().stepOutOfFunction(); +} + +void V8DebuggerAgentImpl::setPauseOnExceptions(ErrorString* errorString, const String16& stringPauseState) +{ + if (!checkEnabled(errorString)) + return; + V8DebuggerImpl::PauseOnExceptionsState pauseState; + if (stringPauseState == "none") { + pauseState = V8DebuggerImpl::DontPauseOnExceptions; + } else if (stringPauseState == "all") { + pauseState = V8DebuggerImpl::PauseOnAllExceptions; + } else if (stringPauseState == "uncaught") { + pauseState = V8DebuggerImpl::PauseOnUncaughtExceptions; + } else { + *errorString = "Unknown pause on exceptions mode: " + stringPauseState; + return; + } + setPauseOnExceptionsImpl(errorString, pauseState); +} + +void V8DebuggerAgentImpl::setPauseOnExceptionsImpl(ErrorString* errorString, int pauseState) +{ + debugger().setPauseOnExceptionsState(static_cast<V8DebuggerImpl::PauseOnExceptionsState>(pauseState)); + if (debugger().getPauseOnExceptionsState() != pauseState) + *errorString = "Internal error. Could not change pause on exceptions state"; + else + m_state->setNumber(DebuggerAgentState::pauseOnExceptionsState, pauseState); +} + +void V8DebuggerAgentImpl::evaluateOnCallFrame(ErrorString* errorString, + const String16& callFrameId, + const String16& expression, + const Maybe<String16>& objectGroup, + const Maybe<bool>& includeCommandLineAPI, + const Maybe<bool>& doNotPauseOnExceptionsAndMuteConsole, + const Maybe<bool>& returnByValue, + const Maybe<bool>& generatePreview, + std::unique_ptr<RemoteObject>* result, + Maybe<bool>* wasThrown, + Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) +{ + if (!assertPaused(errorString)) + return; + InjectedScript::CallFrameScope scope(errorString, m_debugger, m_session->contextGroupId(), callFrameId); + if (!scope.initialize()) + return; + if (scope.frameOrdinal() >= m_pausedCallFrames.size()) { + *errorString = "Could not find call frame with given id"; + return; + } + + if (includeCommandLineAPI.fromMaybe(false) && !scope.installCommandLineAPI()) + return; + if (doNotPauseOnExceptionsAndMuteConsole.fromMaybe(false)) + scope.ignoreExceptionsAndMuteConsole(); + + v8::MaybeLocal<v8::Value> maybeResultValue = m_pausedCallFrames[scope.frameOrdinal()]->evaluate(toV8String(m_isolate, expression)); + + // Re-initialize after running client's code, as it could have destroyed context or session. + if (!scope.initialize()) + return; + scope.injectedScript()->wrapEvaluateResult(errorString, + maybeResultValue, + scope.tryCatch(), + objectGroup.fromMaybe(""), + returnByValue.fromMaybe(false), + generatePreview.fromMaybe(false), + result, + wasThrown, + exceptionDetails); +} + +void V8DebuggerAgentImpl::setVariableValue(ErrorString* errorString, + int scopeNumber, + const String16& variableName, + std::unique_ptr<protocol::Runtime::CallArgument> newValueArgument, + const String16& callFrameId) +{ + if (!checkEnabled(errorString)) + return; + if (!assertPaused(errorString)) + return; + InjectedScript::CallFrameScope scope(errorString, m_debugger, m_session->contextGroupId(), callFrameId); + if (!scope.initialize()) + return; + + v8::Local<v8::Value> newValue; + if (!scope.injectedScript()->resolveCallArgument(errorString, newValueArgument.get()).ToLocal(&newValue)) + return; + + if (scope.frameOrdinal() >= m_pausedCallFrames.size()) { + *errorString = "Could not find call frame with given id"; + return; + } + v8::MaybeLocal<v8::Value> result = m_pausedCallFrames[scope.frameOrdinal()]->setVariableValue(scopeNumber, toV8String(m_isolate, variableName), newValue); + if (scope.tryCatch().HasCaught() || result.IsEmpty()) { + *errorString = "Internal error"; + return; + } +} + +void V8DebuggerAgentImpl::setAsyncCallStackDepth(ErrorString* errorString, int depth) +{ + if (!checkEnabled(errorString)) + return; + m_state->setNumber(DebuggerAgentState::asyncCallStackDepth, depth); + internalSetAsyncCallStackDepth(depth); +} + +void V8DebuggerAgentImpl::asyncTaskScheduled(const String16& taskName, void* task, bool recurring) +{ + if (!m_maxAsyncCallStackDepth) + return; + v8::HandleScope scope(m_isolate); + std::unique_ptr<V8StackTraceImpl> chain = V8StackTraceImpl::capture(this, V8StackTrace::maxCallStackSizeToCapture, taskName); + if (chain) { + m_asyncTaskStacks.set(task, std::move(chain)); + if (recurring) + m_recurringTasks.add(task); + } +} + +void V8DebuggerAgentImpl::asyncTaskCanceled(void* task) +{ + if (!m_maxAsyncCallStackDepth) + return; + m_asyncTaskStacks.remove(task); + m_recurringTasks.remove(task); +} + +void V8DebuggerAgentImpl::asyncTaskStarted(void* task) +{ + // Not enabled, return. + if (!m_maxAsyncCallStackDepth) + return; + + V8StackTraceImpl* stack = m_asyncTaskStacks.get(task); + // Needs to support following order of events: + // - asyncTaskScheduled + // <-- attached here --> + // - asyncTaskStarted + // - asyncTaskCanceled <-- canceled before finished + // <-- async stack requested here --> + // - asyncTaskFinished + m_currentStacks.append(stack ? stack->clone() : nullptr); +} + +void V8DebuggerAgentImpl::asyncTaskFinished(void* task) +{ + if (!m_maxAsyncCallStackDepth) + return; + // We could start instrumenting half way and the stack is empty. + if (!m_currentStacks.size()) + return; + + m_currentStacks.removeLast(); + if (!m_recurringTasks.contains(task)) + m_asyncTaskStacks.remove(task); +} + +void V8DebuggerAgentImpl::allAsyncTasksCanceled() +{ + m_asyncTaskStacks.clear(); + m_recurringTasks.clear(); + m_currentStacks.clear(); +} + +void V8DebuggerAgentImpl::setBlackboxPatterns(ErrorString* errorString, std::unique_ptr<protocol::Array<String16>> patterns) +{ + if (!patterns->length()) { + m_blackboxPattern = nullptr; + m_state->remove(DebuggerAgentState::blackboxPattern); + return; + } + + String16Builder patternBuilder; + patternBuilder.append("("); + for (size_t i = 0; i < patterns->length() - 1; ++i) + patternBuilder.append(patterns->get(i) + "|"); + patternBuilder.append(patterns->get(patterns->length() - 1) + ")"); + String16 pattern = patternBuilder.toString(); + if (!setBlackboxPattern(errorString, pattern)) + return; + m_state->setString(DebuggerAgentState::blackboxPattern, pattern); +} + +bool V8DebuggerAgentImpl::setBlackboxPattern(ErrorString* errorString, const String16& pattern) +{ + std::unique_ptr<V8Regex> regex = wrapUnique(new V8Regex(m_debugger, pattern, true /** caseSensitive */, false /** multiline */)); + if (!regex->isValid()) { + *errorString = "Pattern parser error: " + regex->errorMessage(); + return false; + } + m_blackboxPattern = std::move(regex); + return true; +} + +void V8DebuggerAgentImpl::setBlackboxedRanges(ErrorString* error, const String16& scriptId, std::unique_ptr<protocol::Array<protocol::Debugger::ScriptPosition>> inPositions) +{ + if (!m_scripts.contains(scriptId)) { + *error = "No script with passed id."; + return; + } + + if (!inPositions->length()) { + m_blackboxedPositions.remove(scriptId); + return; + } + + protocol::Vector<std::pair<int, int>> positions(inPositions->length()); + for (size_t i = 0; i < positions.size(); ++i) { + protocol::Debugger::ScriptPosition* position = inPositions->get(i); + if (position->getLine() < 0) { + *error = "Position missing 'line' or 'line' < 0."; + return; + } + if (position->getColumn() < 0) { + *error = "Position missing 'column' or 'column' < 0."; + return; + } + positions[i] = std::make_pair(position->getLine(), position->getColumn()); + } + + for (size_t i = 1; i < positions.size(); ++i) { + if (positions[i - 1].first < positions[i].first) + continue; + if (positions[i - 1].first == positions[i].first && positions[i - 1].second < positions[i].second) + continue; + *error = "Input positions array is not sorted or contains duplicate values."; + return; + } + + m_blackboxedPositions.set(scriptId, positions); +} + +void V8DebuggerAgentImpl::willExecuteScript(int scriptId) +{ + changeJavaScriptRecursionLevel(+1); + // Fast return. + if (m_scheduledDebuggerStep != StepInto) + return; + // Skip unknown scripts (e.g. InjectedScript). + if (!m_scripts.contains(String16::number(scriptId))) + return; + schedulePauseOnNextStatementIfSteppingInto(); +} + +void V8DebuggerAgentImpl::didExecuteScript() +{ + changeJavaScriptRecursionLevel(-1); +} + +void V8DebuggerAgentImpl::changeJavaScriptRecursionLevel(int step) +{ + if (m_javaScriptPauseScheduled && !m_skipAllPauses && !debugger().isPaused()) { + // Do not ever loose user's pause request until we have actually paused. + debugger().setPauseOnNextStatement(true); + } + if (m_scheduledDebuggerStep == StepOut) { + m_recursionLevelForStepOut += step; + if (!m_recursionLevelForStepOut) { + // When StepOut crosses a task boundary (i.e. js -> blink_c++) from where it was requested, + // switch stepping to step into a next JS task, as if we exited to a blackboxed framework. + m_scheduledDebuggerStep = StepInto; + m_skipNextDebuggerStepOut = false; + } + } + if (m_recursionLevelForStepFrame) { + m_recursionLevelForStepFrame += step; + if (!m_recursionLevelForStepFrame) { + // We have walked through a blackboxed framework and got back to where we started. + // If there was no stepping scheduled, we should cancel the stepping explicitly, + // since there may be a scheduled StepFrame left. + // Otherwise, if we were stepping in/over, the StepFrame will stop at the right location, + // whereas if we were stepping out, we should continue doing so after debugger pauses + // from the old StepFrame. + m_skippedStepFrameCount = 0; + if (m_scheduledDebuggerStep == NoStep) + debugger().clearStepping(); + else if (m_scheduledDebuggerStep == StepOut) + m_skipNextDebuggerStepOut = true; + } + } +} + +std::unique_ptr<Array<CallFrame>> V8DebuggerAgentImpl::currentCallFrames(ErrorString* errorString) +{ + if (m_pausedContext.IsEmpty() || !m_pausedCallFrames.size()) + return Array<CallFrame>::create(); + ErrorString ignored; + InjectedScript* topFrameInjectedScript = m_session->findInjectedScript(&ignored, V8Debugger::contextId(m_pausedContext.Get(m_isolate))); + if (!topFrameInjectedScript) { + // Context has been reported as removed while on pause. + return Array<CallFrame>::create(); + } + + v8::HandleScope handles(m_isolate); + v8::Local<v8::Context> context = topFrameInjectedScript->context()->context(); + v8::Context::Scope contextScope(context); + + v8::Local<v8::Array> objects = v8::Array::New(m_isolate); + for (size_t frameOrdinal = 0; frameOrdinal < m_pausedCallFrames.size(); ++frameOrdinal) { + JavaScriptCallFrame* currentCallFrame = m_pausedCallFrames[frameOrdinal]; + + v8::Local<v8::Object> details = currentCallFrame->details(); + if (hasInternalError(errorString, details.IsEmpty())) + return Array<CallFrame>::create(); + + int contextId = currentCallFrame->contextId(); + InjectedScript* injectedScript = contextId ? m_session->findInjectedScript(&ignored, contextId) : nullptr; + if (!injectedScript) + injectedScript = topFrameInjectedScript; + + String16 callFrameId = RemoteCallFrameId::serialize(injectedScript->context()->contextId(), frameOrdinal); + if (hasInternalError(errorString, !details->Set(context, toV8StringInternalized(m_isolate, "callFrameId"), toV8String(m_isolate, callFrameId)).FromMaybe(false))) + return Array<CallFrame>::create(); + + v8::Local<v8::Value> scopeChain; + if (hasInternalError(errorString, !details->Get(context, toV8StringInternalized(m_isolate, "scopeChain")).ToLocal(&scopeChain) || !scopeChain->IsArray())) + return Array<CallFrame>::create(); + v8::Local<v8::Array> scopeChainArray = scopeChain.As<v8::Array>(); + if (!injectedScript->wrapPropertyInArray(errorString, scopeChainArray, toV8StringInternalized(m_isolate, "object"), V8InspectorSession::backtraceObjectGroup)) + return Array<CallFrame>::create(); + + if (!injectedScript->wrapObjectProperty(errorString, details, toV8StringInternalized(m_isolate, "this"), V8InspectorSession::backtraceObjectGroup)) + return Array<CallFrame>::create(); + + if (details->Has(context, toV8StringInternalized(m_isolate, "returnValue")).FromMaybe(false)) { + if (!injectedScript->wrapObjectProperty(errorString, details, toV8StringInternalized(m_isolate, "returnValue"), V8InspectorSession::backtraceObjectGroup)) + return Array<CallFrame>::create(); + } + + if (hasInternalError(errorString, !objects->Set(context, frameOrdinal, details).FromMaybe(false))) + return Array<CallFrame>::create(); + } + + protocol::ErrorSupport errorSupport; + std::unique_ptr<Array<CallFrame>> callFrames = Array<CallFrame>::parse(toProtocolValue(context, objects).get(), &errorSupport); + if (hasInternalError(errorString, !callFrames)) + return Array<CallFrame>::create(); + return callFrames; +} + +std::unique_ptr<StackTrace> V8DebuggerAgentImpl::currentAsyncStackTrace() +{ + if (m_pausedContext.IsEmpty() || !m_maxAsyncCallStackDepth || !m_currentStacks.size() || !m_currentStacks.last()) + return nullptr; + + return m_currentStacks.last()->buildInspectorObjectForTail(this); +} + +V8StackTraceImpl* V8DebuggerAgentImpl::currentAsyncCallChain() +{ + if (!m_currentStacks.size()) + return nullptr; + return m_currentStacks.last(); +} + +void V8DebuggerAgentImpl::didParseSource(const V8DebuggerParsedScript& parsedScript) +{ + V8DebuggerScript script = parsedScript.script; + + bool isDeprecatedSourceURL = false; + if (!parsedScript.success) + script.setSourceURL(V8ContentSearchUtil::findSourceURL(script.source(), false, &isDeprecatedSourceURL)); + else if (script.hasSourceURL()) + V8ContentSearchUtil::findSourceURL(script.source(), false, &isDeprecatedSourceURL); + + bool isDeprecatedSourceMappingURL = false; + if (!parsedScript.success) + script.setSourceMappingURL(V8ContentSearchUtil::findSourceMapURL(script.source(), false, &isDeprecatedSourceMappingURL)); + else if (!script.sourceMappingURL().isEmpty()) + V8ContentSearchUtil::findSourceMapURL(script.source(), false, &isDeprecatedSourceMappingURL); + + script.setHash(calculateHash(script.source())); + + int executionContextId = script.executionContextId(); + bool isContentScript = script.isContentScript(); + bool isInternalScript = script.isInternalScript(); + bool isLiveEdit = script.isLiveEdit(); + bool hasSourceURL = script.hasSourceURL(); + String16 scriptURL = script.sourceURL(); + String16 sourceMapURL = script.sourceMappingURL(); + bool deprecatedCommentWasUsed = isDeprecatedSourceURL || isDeprecatedSourceMappingURL; + + const Maybe<String16>& sourceMapURLParam = sourceMapURL; + const bool* isContentScriptParam = isContentScript ? &isContentScript : nullptr; + const bool* isInternalScriptParam = isInternalScript ? &isInternalScript : nullptr; + const bool* isLiveEditParam = isLiveEdit ? &isLiveEdit : nullptr; + const bool* hasSourceURLParam = hasSourceURL ? &hasSourceURL : nullptr; + const bool* deprecatedCommentWasUsedParam = deprecatedCommentWasUsed ? &deprecatedCommentWasUsed : nullptr; + if (parsedScript.success) + m_frontend->scriptParsed(parsedScript.scriptId, scriptURL, script.startLine(), script.startColumn(), script.endLine(), script.endColumn(), executionContextId, script.hash(), isContentScriptParam, isInternalScriptParam, isLiveEditParam, sourceMapURLParam, hasSourceURLParam, deprecatedCommentWasUsedParam); + else + m_frontend->scriptFailedToParse(parsedScript.scriptId, scriptURL, script.startLine(), script.startColumn(), script.endLine(), script.endColumn(), executionContextId, script.hash(), isContentScriptParam, isInternalScriptParam, sourceMapURLParam, hasSourceURLParam, deprecatedCommentWasUsedParam); + + m_scripts.set(parsedScript.scriptId, script); + + if (scriptURL.isEmpty() || !parsedScript.success) + return; + + protocol::DictionaryValue* breakpointsCookie = m_state->getObject(DebuggerAgentState::javaScriptBreakpoints); + if (!breakpointsCookie) + return; + + for (size_t i = 0; i < breakpointsCookie->size(); ++i) { + auto cookie = breakpointsCookie->at(i); + protocol::DictionaryValue* breakpointObject = protocol::DictionaryValue::cast(cookie.second); + bool isRegex; + breakpointObject->getBoolean(DebuggerAgentState::isRegex, &isRegex); + String16 url; + breakpointObject->getString(DebuggerAgentState::url, &url); + if (!matches(m_debugger, scriptURL, url, isRegex)) + continue; + ScriptBreakpoint breakpoint; + breakpointObject->getNumber(DebuggerAgentState::lineNumber, &breakpoint.lineNumber); + breakpointObject->getNumber(DebuggerAgentState::columnNumber, &breakpoint.columnNumber); + breakpointObject->getString(DebuggerAgentState::condition, &breakpoint.condition); + std::unique_ptr<protocol::Debugger::Location> location = resolveBreakpoint(cookie.first, parsedScript.scriptId, breakpoint, UserBreakpointSource); + if (location) + m_frontend->breakpointResolved(cookie.first, std::move(location)); + } +} + +V8DebuggerAgentImpl::SkipPauseRequest V8DebuggerAgentImpl::didPause(v8::Local<v8::Context> context, v8::Local<v8::Value> exception, const protocol::Vector<String16>& hitBreakpoints, bool isPromiseRejection) +{ + JavaScriptCallFrames callFrames = debugger().currentCallFrames(1); + JavaScriptCallFrame* topCallFrame = callFrames.size() > 0 ? callFrames[0] : nullptr; + + V8DebuggerAgentImpl::SkipPauseRequest result; + if (m_skipAllPauses) + result = RequestContinue; + else if (!hitBreakpoints.isEmpty()) + result = RequestNoSkip; // Don't skip explicit breakpoints even if set in frameworks. + else if (!exception.IsEmpty()) + result = shouldSkipExceptionPause(topCallFrame); + else if (m_scheduledDebuggerStep != NoStep || m_javaScriptPauseScheduled || m_pausingOnNativeEvent) + result = shouldSkipStepPause(topCallFrame); + else + result = RequestNoSkip; + + m_skipNextDebuggerStepOut = false; + if (result != RequestNoSkip) + return result; + // Skip pauses inside V8 internal scripts and on syntax errors. + if (!topCallFrame) + return RequestContinue; + + DCHECK(m_pausedContext.IsEmpty()); + m_pausedCallFrames.swap(debugger().currentCallFrames()); + m_pausedContext.Reset(m_isolate, context); + v8::HandleScope handles(m_isolate); + + if (!exception.IsEmpty()) { + ErrorString ignored; + InjectedScript* injectedScript = m_session->findInjectedScript(&ignored, V8Debugger::contextId(context)); + if (injectedScript) { + m_breakReason = isPromiseRejection ? protocol::Debugger::Paused::ReasonEnum::PromiseRejection : protocol::Debugger::Paused::ReasonEnum::Exception; + ErrorString errorString; + auto obj = injectedScript->wrapObject(&errorString, exception, V8InspectorSession::backtraceObjectGroup); + m_breakAuxData = obj ? obj->serialize() : nullptr; + // m_breakAuxData might be null after this. + } + } + + std::unique_ptr<Array<String16>> hitBreakpointIds = Array<String16>::create(); + + for (const auto& point : hitBreakpoints) { + DebugServerBreakpointToBreakpointIdAndSourceMap::iterator breakpointIterator = m_serverBreakpoints.find(point); + if (breakpointIterator != m_serverBreakpoints.end()) { + const String16& localId = breakpointIterator->second->first; + hitBreakpointIds->addItem(localId); + + BreakpointSource source = breakpointIterator->second->second; + if (m_breakReason == protocol::Debugger::Paused::ReasonEnum::Other && source == DebugCommandBreakpointSource) + m_breakReason = protocol::Debugger::Paused::ReasonEnum::DebugCommand; + } + } + + ErrorString errorString; + m_frontend->paused(currentCallFrames(&errorString), m_breakReason, std::move(m_breakAuxData), std::move(hitBreakpointIds), currentAsyncStackTrace()); + m_scheduledDebuggerStep = NoStep; + m_javaScriptPauseScheduled = false; + m_steppingFromFramework = false; + m_pausingOnNativeEvent = false; + m_skippedStepFrameCount = 0; + m_recursionLevelForStepFrame = 0; + + if (!m_continueToLocationBreakpointId.isEmpty()) { + debugger().removeBreakpoint(m_continueToLocationBreakpointId); + m_continueToLocationBreakpointId = ""; + } + return result; +} + +void V8DebuggerAgentImpl::didContinue() +{ + m_pausedContext.Reset(); + JavaScriptCallFrames emptyCallFrames; + m_pausedCallFrames.swap(emptyCallFrames); + clearBreakDetails(); + m_frontend->resumed(); +} + +void V8DebuggerAgentImpl::breakProgram(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data) +{ + if (!enabled() || m_skipAllPauses || !m_pausedContext.IsEmpty() || isCurrentCallStackEmptyOrBlackboxed() || !debugger().breakpointsActivated()) + return; + m_breakReason = breakReason; + m_breakAuxData = std::move(data); + m_scheduledDebuggerStep = NoStep; + m_steppingFromFramework = false; + m_pausingOnNativeEvent = false; + debugger().breakProgram(); +} + +void V8DebuggerAgentImpl::breakProgramOnException(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data) +{ + if (!enabled() || m_debugger->getPauseOnExceptionsState() == V8DebuggerImpl::DontPauseOnExceptions) + return; + breakProgram(breakReason, std::move(data)); +} + +bool V8DebuggerAgentImpl::assertPaused(ErrorString* errorString) +{ + if (m_pausedContext.IsEmpty()) { + *errorString = "Can only perform operation while paused."; + return false; + } + return true; +} + +void V8DebuggerAgentImpl::clearBreakDetails() +{ + m_breakReason = protocol::Debugger::Paused::ReasonEnum::Other; + m_breakAuxData = nullptr; +} + +void V8DebuggerAgentImpl::setBreakpointAt(const String16& scriptId, int lineNumber, int columnNumber, BreakpointSource source, const String16& condition) +{ + String16 breakpointId = generateBreakpointId(scriptId, lineNumber, columnNumber, source); + ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition); + resolveBreakpoint(breakpointId, scriptId, breakpoint, source); +} + +void V8DebuggerAgentImpl::removeBreakpointAt(const String16& scriptId, int lineNumber, int columnNumber, BreakpointSource source) +{ + removeBreakpoint(generateBreakpointId(scriptId, lineNumber, columnNumber, source)); +} + +void V8DebuggerAgentImpl::reset() +{ + if (!enabled()) + return; + m_scheduledDebuggerStep = NoStep; + m_scripts.clear(); + m_blackboxedPositions.clear(); + m_breakpointIdToDebuggerBreakpointIds.clear(); + allAsyncTasksCanceled(); +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/V8DebuggerAgentImpl.h b/deps/v8_inspector/platform/v8_inspector/V8DebuggerAgentImpl.h new file mode 100644 index 00000000000000..a2651970b73ab0 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8DebuggerAgentImpl.h @@ -0,0 +1,248 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8DebuggerAgentImpl_h +#define V8DebuggerAgentImpl_h + +#include "platform/inspector_protocol/Collections.h" +#include "platform/inspector_protocol/Dispatcher.h" +#include "platform/inspector_protocol/Frontend.h" +#include "platform/inspector_protocol/String16.h" +#include "platform/v8_inspector/V8DebuggerImpl.h" +#include "platform/v8_inspector/public/V8DebuggerAgent.h" + +namespace blink { + +class JavaScriptCallFrame; +class PromiseTracker; +class V8InspectorSessionImpl; +class V8Regex; +class V8StackTraceImpl; + +namespace protocol { +class DictionaryValue; +} + +using protocol::Maybe; + +class V8DebuggerAgentImpl : public V8DebuggerAgent { + PROTOCOL_DISALLOW_COPY(V8DebuggerAgentImpl); +public: + enum SkipPauseRequest { + RequestNoSkip, + RequestContinue, + RequestStepInto, + RequestStepOut, + RequestStepFrame + }; + + enum BreakpointSource { + UserBreakpointSource, + DebugCommandBreakpointSource, + MonitorCommandBreakpointSource + }; + + explicit V8DebuggerAgentImpl(V8InspectorSessionImpl*); + ~V8DebuggerAgentImpl() override; + + void setInspectorState(protocol::DictionaryValue*) override; + void setFrontend(protocol::Frontend::Debugger* frontend) override { m_frontend = frontend; } + void clearFrontend() override; + void restore() override; + void disable(ErrorString*) override; + + // Part of the protocol. + void enable(ErrorString*) override; + void setBreakpointsActive(ErrorString*, bool active) override; + void setSkipAllPauses(ErrorString*, bool skipped) override; + void setBreakpointByUrl(ErrorString*, + int lineNumber, + const Maybe<String16>& optionalURL, + const Maybe<String16>& optionalURLRegex, + const Maybe<int>& optionalColumnNumber, + const Maybe<String16>& optionalCondition, + String16*, + std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations) override; + void setBreakpoint(ErrorString*, + std::unique_ptr<protocol::Debugger::Location>, + const Maybe<String16>& optionalCondition, + String16*, + std::unique_ptr<protocol::Debugger::Location>* actualLocation) override; + void removeBreakpoint(ErrorString*, const String16& breakpointId) override; + void continueToLocation(ErrorString*, + std::unique_ptr<protocol::Debugger::Location>, + const Maybe<bool>& interstateLocationOpt) override; + void getBacktrace(ErrorString*, + std::unique_ptr<protocol::Array<protocol::Debugger::CallFrame>>*, + Maybe<protocol::Runtime::StackTrace>*) override; + void searchInContent(ErrorString*, + const String16& scriptId, + const String16& query, + const Maybe<bool>& optionalCaseSensitive, + const Maybe<bool>& optionalIsRegex, + std::unique_ptr<protocol::Array<protocol::Debugger::SearchMatch>>*) override; + void canSetScriptSource(ErrorString*, bool* result) override { *result = true; } + void setScriptSource(ErrorString*, + const String16& inScriptId, + const String16& inScriptSource, + const Maybe<bool>& inPreview, + Maybe<protocol::Array<protocol::Debugger::CallFrame>>* optOutCallFrames, + Maybe<bool>* optOutStackChanged, + Maybe<protocol::Runtime::StackTrace>* optOutAsyncStackTrace, + Maybe<protocol::Debugger::SetScriptSourceError>* optOutCompileError) override; + void restartFrame(ErrorString*, + const String16& callFrameId, + std::unique_ptr<protocol::Array<protocol::Debugger::CallFrame>>* newCallFrames, + Maybe<protocol::Runtime::StackTrace>* asyncStackTrace) override; + void getScriptSource(ErrorString*, const String16& scriptId, String16* scriptSource) override; + void getFunctionDetails(ErrorString*, + const String16& functionId, + std::unique_ptr<protocol::Debugger::FunctionDetails>*) override; + void getGeneratorObjectDetails(ErrorString*, + const String16& objectId, + std::unique_ptr<protocol::Debugger::GeneratorObjectDetails>*) override; + void getCollectionEntries(ErrorString*, + const String16& objectId, + std::unique_ptr<protocol::Array<protocol::Debugger::CollectionEntry>>*) override; + void pause(ErrorString*) override; + void resume(ErrorString*) override; + void stepOver(ErrorString*) override; + void stepInto(ErrorString*) override; + void stepOut(ErrorString*) override; + void setPauseOnExceptions(ErrorString*, const String16& pauseState) override; + void evaluateOnCallFrame(ErrorString*, + const String16& callFrameId, + const String16& expression, + const Maybe<String16>& objectGroup, + const Maybe<bool>& includeCommandLineAPI, + const Maybe<bool>& doNotPauseOnExceptionsAndMuteConsole, + const Maybe<bool>& returnByValue, + const Maybe<bool>& generatePreview, + std::unique_ptr<protocol::Runtime::RemoteObject>* result, + Maybe<bool>* wasThrown, + Maybe<protocol::Runtime::ExceptionDetails>*) override; + void setVariableValue(ErrorString*, + int scopeNumber, + const String16& variableName, + std::unique_ptr<protocol::Runtime::CallArgument> newValue, + const String16& callFrame) override; + void setAsyncCallStackDepth(ErrorString*, int depth) override; + void setBlackboxPatterns(ErrorString*, + std::unique_ptr<protocol::Array<String16>> patterns) override; + void setBlackboxedRanges(ErrorString*, + const String16& scriptId, + std::unique_ptr<protocol::Array<protocol::Debugger::ScriptPosition>> positions) override; + + bool enabled(); + V8DebuggerImpl& debugger() { return *m_debugger; } + + void setBreakpointAt(const String16& scriptId, int lineNumber, int columnNumber, BreakpointSource, const String16& condition = String16()); + void removeBreakpointAt(const String16& scriptId, int lineNumber, int columnNumber, BreakpointSource); + void schedulePauseOnNextStatement(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data); + void cancelPauseOnNextStatement(); + void breakProgram(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data); + void breakProgramOnException(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data); + + // Async call stacks implementation. + void asyncTaskScheduled(const String16& taskName, void* task, bool recurring); + void asyncTaskCanceled(void* task); + void asyncTaskStarted(void* task); + void asyncTaskFinished(void* task); + void allAsyncTasksCanceled(); + + void reset(); + + // Interface for V8DebuggerImpl + SkipPauseRequest didPause(v8::Local<v8::Context>, v8::Local<v8::Value> exception, const protocol::Vector<String16>& hitBreakpoints, bool isPromiseRejection); + void didContinue(); + void didParseSource(const V8DebuggerParsedScript&); + bool v8AsyncTaskEventsEnabled() const; + void didReceiveV8AsyncTaskEvent(v8::Local<v8::Context>, const String16& eventType, const String16& eventName, int id); + void willExecuteScript(int scriptId); + void didExecuteScript(); + + v8::Isolate* isolate() { return m_isolate; } + int maxAsyncCallChainDepth() { return m_maxAsyncCallStackDepth; } + V8StackTraceImpl* currentAsyncCallChain(); + +private: + bool checkEnabled(ErrorString*); + void enable(); + + SkipPauseRequest shouldSkipExceptionPause(JavaScriptCallFrame* topCallFrame); + SkipPauseRequest shouldSkipStepPause(JavaScriptCallFrame* topCallFrame); + + void schedulePauseOnNextStatementIfSteppingInto(); + + std::unique_ptr<protocol::Array<protocol::Debugger::CallFrame>> currentCallFrames(ErrorString*); + std::unique_ptr<protocol::Runtime::StackTrace> currentAsyncStackTrace(); + + void changeJavaScriptRecursionLevel(int step); + + void setPauseOnExceptionsImpl(ErrorString*, int); + + std::unique_ptr<protocol::Debugger::Location> resolveBreakpoint(const String16& breakpointId, const String16& scriptId, const ScriptBreakpoint&, BreakpointSource); + void removeBreakpoint(const String16& breakpointId); + bool assertPaused(ErrorString*); + void clearBreakDetails(); + + bool isCurrentCallStackEmptyOrBlackboxed(); + bool isTopPausedCallFrameBlackboxed(); + bool isCallFrameWithUnknownScriptOrBlackboxed(JavaScriptCallFrame*); + + void internalSetAsyncCallStackDepth(int); + void increaseCachedSkipStackGeneration(); + + bool setBlackboxPattern(ErrorString*, const String16& pattern); + + using ScriptsMap = protocol::HashMap<String16, V8DebuggerScript>; + using BreakpointIdToDebuggerBreakpointIdsMap = protocol::HashMap<String16, protocol::Vector<String16>>; + using DebugServerBreakpointToBreakpointIdAndSourceMap = protocol::HashMap<String16, std::pair<String16, BreakpointSource>>; + using MuteBreakpoins = protocol::HashMap<String16, std::pair<String16, int>>; + + enum DebuggerStep { + NoStep = 0, + StepInto, + StepOver, + StepOut + }; + + V8DebuggerImpl* m_debugger; + V8InspectorSessionImpl* m_session; + bool m_enabled; + protocol::DictionaryValue* m_state; + protocol::Frontend::Debugger* m_frontend; + v8::Isolate* m_isolate; + v8::Global<v8::Context> m_pausedContext; + JavaScriptCallFrames m_pausedCallFrames; + ScriptsMap m_scripts; + BreakpointIdToDebuggerBreakpointIdsMap m_breakpointIdToDebuggerBreakpointIds; + DebugServerBreakpointToBreakpointIdAndSourceMap m_serverBreakpoints; + String16 m_continueToLocationBreakpointId; + String16 m_breakReason; + std::unique_ptr<protocol::DictionaryValue> m_breakAuxData; + DebuggerStep m_scheduledDebuggerStep; + bool m_skipNextDebuggerStepOut; + bool m_javaScriptPauseScheduled; + bool m_steppingFromFramework; + bool m_pausingOnNativeEvent; + + int m_skippedStepFrameCount; + int m_recursionLevelForStepOut; + int m_recursionLevelForStepFrame; + bool m_skipAllPauses; + + using AsyncTaskToStackTrace = protocol::HashMap<void*, std::unique_ptr<V8StackTraceImpl>>; + AsyncTaskToStackTrace m_asyncTaskStacks; + protocol::HashSet<void*> m_recurringTasks; + int m_maxAsyncCallStackDepth; + protocol::Vector<std::unique_ptr<V8StackTraceImpl>> m_currentStacks; + std::unique_ptr<V8Regex> m_blackboxPattern; + protocol::HashMap<String16, protocol::Vector<std::pair<int, int>>> m_blackboxedPositions; +}; + +} // namespace blink + + +#endif // V8DebuggerAgentImpl_h diff --git a/deps/v8_inspector/platform/v8_inspector/V8DebuggerImpl.cpp b/deps/v8_inspector/platform/v8_inspector/V8DebuggerImpl.cpp new file mode 100644 index 00000000000000..6cb4371ede20ce --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8DebuggerImpl.cpp @@ -0,0 +1,854 @@ +/* + * Copyright (c) 2010-2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "platform/v8_inspector/V8DebuggerImpl.h" + +#include "platform/inspector_protocol/Values.h" +#include "platform/v8_inspector/Atomics.h" +#include "platform/v8_inspector/DebuggerScript.h" +#include "platform/v8_inspector/InspectedContext.h" +#include "platform/v8_inspector/ScriptBreakpoint.h" +#include "platform/v8_inspector/V8Compat.h" +#include "platform/v8_inspector/V8DebuggerAgentImpl.h" +#include "platform/v8_inspector/V8InspectorSessionImpl.h" +#include "platform/v8_inspector/V8RuntimeAgentImpl.h" +#include "platform/v8_inspector/V8StackTraceImpl.h" +#include "platform/v8_inspector/V8StringUtil.h" +#include "platform/v8_inspector/public/V8DebuggerClient.h" +#include <v8-profiler.h> + +namespace blink { + +namespace { +const char stepIntoV8MethodName[] = "stepIntoStatement"; +const char stepOutV8MethodName[] = "stepOutOfFunction"; +volatile int s_lastContextId = 0; + +inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate) +{ + return value ? v8::True(isolate) : v8::False(isolate); +} + +} + +static bool inLiveEditScope = false; + +v8::MaybeLocal<v8::Value> V8DebuggerImpl::callDebuggerMethod(const char* functionName, int argc, v8::Local<v8::Value> argv[]) +{ + v8::MicrotasksScope microtasks(m_isolate, v8::MicrotasksScope::kDoNotRunMicrotasks); + v8::Local<v8::Object> debuggerScript = m_debuggerScript.Get(m_isolate); + v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(debuggerScript->Get(v8InternalizedString(functionName))); + DCHECK(m_isolate->InContext()); + return function->Call(m_isolate->GetCurrentContext(), debuggerScript, argc, argv); +} + +std::unique_ptr<V8Debugger> V8Debugger::create(v8::Isolate* isolate, V8DebuggerClient* client) +{ + return wrapUnique(new V8DebuggerImpl(isolate, client)); +} + +V8DebuggerImpl::V8DebuggerImpl(v8::Isolate* isolate, V8DebuggerClient* client) + : m_isolate(isolate) + , m_client(client) + , m_enabledAgentsCount(0) + , m_breakpointsActivated(true) + , m_runningNestedMessageLoop(false) +{ +} + +V8DebuggerImpl::~V8DebuggerImpl() +{ +} + +void V8DebuggerImpl::enable() +{ + DCHECK(!enabled()); + v8::HandleScope scope(m_isolate); + v8::Debug::SetDebugEventListener(m_isolate, &V8DebuggerImpl::v8DebugEventCallback, v8::External::New(m_isolate, this)); + m_debuggerContext.Reset(m_isolate, v8::Debug::GetDebugContext(m_isolate)); + compileDebuggerScript(); +} + +void V8DebuggerImpl::disable() +{ + DCHECK(enabled()); + clearBreakpoints(); + m_debuggerScript.Reset(); + m_debuggerContext.Reset(); + v8::Debug::SetDebugEventListener(m_isolate, nullptr); +} + +bool V8DebuggerImpl::enabled() const +{ + return !m_debuggerScript.IsEmpty(); +} + +int V8Debugger::contextId(v8::Local<v8::Context> context) +{ + v8::Local<v8::Value> data = context->GetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex)); + if (data.IsEmpty() || !data->IsString()) + return 0; + String16 dataString = toProtocolString(data.As<v8::String>()); + if (dataString.isEmpty()) + return 0; + size_t commaPos = dataString.find(","); + if (commaPos == kNotFound) + return 0; + size_t commaPos2 = dataString.find(",", commaPos + 1); + if (commaPos2 == kNotFound) + return 0; + return dataString.substring(commaPos + 1, commaPos2 - commaPos - 1).toInt(); +} + +static int getGroupId(v8::Local<v8::Context> context) +{ + v8::Local<v8::Value> data = context->GetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex)); + if (data.IsEmpty() || !data->IsString()) + return 0; + String16 dataString = toProtocolString(data.As<v8::String>()); + if (dataString.isEmpty()) + return 0; + size_t commaPos = dataString.find(","); + if (commaPos == kNotFound) + return 0; + return dataString.substring(0, commaPos).toInt(); +} + +void V8DebuggerImpl::debuggerAgentEnabled() +{ + if (!m_enabledAgentsCount++) + enable(); +} + +void V8DebuggerImpl::debuggerAgentDisabled() +{ + if (!--m_enabledAgentsCount) + disable(); +} + +V8DebuggerAgentImpl* V8DebuggerImpl::findEnabledDebuggerAgent(int contextGroupId) +{ + if (!contextGroupId) + return nullptr; + V8InspectorSessionImpl* session = m_sessions.get(contextGroupId); + if (session && session->debuggerAgentImpl()->enabled()) + return session->debuggerAgentImpl(); + return nullptr; +} + +V8DebuggerAgentImpl* V8DebuggerImpl::findEnabledDebuggerAgent(v8::Local<v8::Context> context) +{ + return findEnabledDebuggerAgent(getGroupId(context)); +} + +void V8DebuggerImpl::getCompiledScripts(int contextGroupId, protocol::Vector<V8DebuggerParsedScript>& result) +{ + v8::HandleScope scope(m_isolate); + v8::MicrotasksScope microtasks(m_isolate, v8::MicrotasksScope::kDoNotRunMicrotasks); + v8::Local<v8::Object> debuggerScript = m_debuggerScript.Get(m_isolate); + DCHECK(!debuggerScript->IsUndefined()); + v8::Local<v8::Function> getScriptsFunction = v8::Local<v8::Function>::Cast(debuggerScript->Get(v8InternalizedString("getScripts"))); + v8::Local<v8::Value> argv[] = { v8::Integer::New(m_isolate, contextGroupId) }; + v8::Local<v8::Value> value; + if (!getScriptsFunction->Call(debuggerContext(), debuggerScript, PROTOCOL_ARRAY_LENGTH(argv), argv).ToLocal(&value)) + return; + DCHECK(value->IsArray()); + v8::Local<v8::Array> scriptsArray = v8::Local<v8::Array>::Cast(value); + result.resize(scriptsArray->Length()); + for (unsigned i = 0; i < scriptsArray->Length(); ++i) + result[i] = createParsedScript(v8::Local<v8::Object>::Cast(scriptsArray->Get(v8::Integer::New(m_isolate, i))), true); +} + +String16 V8DebuggerImpl::setBreakpoint(const String16& sourceID, const ScriptBreakpoint& scriptBreakpoint, int* actualLineNumber, int* actualColumnNumber, bool interstatementLocation) +{ + v8::HandleScope scope(m_isolate); + v8::Context::Scope contextScope(debuggerContext()); + + v8::Local<v8::Object> info = v8::Object::New(m_isolate); + info->Set(v8InternalizedString("sourceID"), toV8String(m_isolate, sourceID)); + info->Set(v8InternalizedString("lineNumber"), v8::Integer::New(m_isolate, scriptBreakpoint.lineNumber)); + info->Set(v8InternalizedString("columnNumber"), v8::Integer::New(m_isolate, scriptBreakpoint.columnNumber)); + info->Set(v8InternalizedString("interstatementLocation"), v8Boolean(interstatementLocation, m_isolate)); + info->Set(v8InternalizedString("condition"), toV8String(m_isolate, scriptBreakpoint.condition)); + + v8::Local<v8::Function> setBreakpointFunction = v8::Local<v8::Function>::Cast(m_debuggerScript.Get(m_isolate)->Get(v8InternalizedString("setBreakpoint"))); + v8::Local<v8::Value> breakpointId = v8::Debug::Call(debuggerContext(), setBreakpointFunction, info).ToLocalChecked(); + if (!breakpointId->IsString()) + return ""; + *actualLineNumber = info->Get(v8InternalizedString("lineNumber"))->Int32Value(); + *actualColumnNumber = info->Get(v8InternalizedString("columnNumber"))->Int32Value(); + return toProtocolString(breakpointId.As<v8::String>()); +} + +void V8DebuggerImpl::removeBreakpoint(const String16& breakpointId) +{ + v8::HandleScope scope(m_isolate); + v8::Context::Scope contextScope(debuggerContext()); + + v8::Local<v8::Object> info = v8::Object::New(m_isolate); + info->Set(v8InternalizedString("breakpointId"), toV8String(m_isolate, breakpointId)); + + v8::Local<v8::Function> removeBreakpointFunction = v8::Local<v8::Function>::Cast(m_debuggerScript.Get(m_isolate)->Get(v8InternalizedString("removeBreakpoint"))); + v8::Debug::Call(debuggerContext(), removeBreakpointFunction, info).ToLocalChecked(); +} + +void V8DebuggerImpl::clearBreakpoints() +{ + v8::HandleScope scope(m_isolate); + v8::Context::Scope contextScope(debuggerContext()); + + v8::Local<v8::Function> clearBreakpoints = v8::Local<v8::Function>::Cast(m_debuggerScript.Get(m_isolate)->Get(v8InternalizedString("clearBreakpoints"))); + v8::Debug::Call(debuggerContext(), clearBreakpoints).ToLocalChecked(); +} + +void V8DebuggerImpl::setBreakpointsActivated(bool activated) +{ + if (!enabled()) { + NOTREACHED(); + return; + } + v8::HandleScope scope(m_isolate); + v8::Context::Scope contextScope(debuggerContext()); + + v8::Local<v8::Object> info = v8::Object::New(m_isolate); + info->Set(v8InternalizedString("enabled"), v8::Boolean::New(m_isolate, activated)); + v8::Local<v8::Function> setBreakpointsActivated = v8::Local<v8::Function>::Cast(m_debuggerScript.Get(m_isolate)->Get(v8InternalizedString("setBreakpointsActivated"))); + v8::Debug::Call(debuggerContext(), setBreakpointsActivated, info).ToLocalChecked(); + + m_breakpointsActivated = activated; +} + +V8DebuggerImpl::PauseOnExceptionsState V8DebuggerImpl::getPauseOnExceptionsState() +{ + DCHECK(enabled()); + v8::HandleScope scope(m_isolate); + v8::Context::Scope contextScope(debuggerContext()); + + v8::Local<v8::Value> argv[] = { v8::Undefined(m_isolate) }; + v8::Local<v8::Value> result = callDebuggerMethod("pauseOnExceptionsState", 0, argv).ToLocalChecked(); + return static_cast<V8DebuggerImpl::PauseOnExceptionsState>(result->Int32Value()); +} + +void V8DebuggerImpl::setPauseOnExceptionsState(PauseOnExceptionsState pauseOnExceptionsState) +{ + DCHECK(enabled()); + v8::HandleScope scope(m_isolate); + v8::Context::Scope contextScope(debuggerContext()); + + v8::Local<v8::Value> argv[] = { v8::Int32::New(m_isolate, pauseOnExceptionsState) }; + callDebuggerMethod("setPauseOnExceptionsState", 1, argv); +} + +void V8DebuggerImpl::setPauseOnNextStatement(bool pause) +{ + if (m_runningNestedMessageLoop) + return; + if (pause) + v8::Debug::DebugBreak(m_isolate); + else + v8::Debug::CancelDebugBreak(m_isolate); +} + +bool V8DebuggerImpl::canBreakProgram() +{ + if (!m_breakpointsActivated) + return false; + return m_isolate->InContext(); +} + +void V8DebuggerImpl::breakProgram() +{ + if (isPaused()) { + DCHECK(!m_runningNestedMessageLoop); + v8::Local<v8::Value> exception; + v8::Local<v8::Array> hitBreakpoints; + handleProgramBreak(m_pausedContext, m_executionState, exception, hitBreakpoints); + return; + } + + if (!canBreakProgram()) + return; + + v8::HandleScope scope(m_isolate); + v8::Local<v8::Function> breakFunction; + if (!v8::Function::New(m_isolate->GetCurrentContext(), &V8DebuggerImpl::breakProgramCallback, v8::External::New(m_isolate, this)).ToLocal(&breakFunction)) + return; + v8::Debug::Call(debuggerContext(), breakFunction).ToLocalChecked(); +} + +void V8DebuggerImpl::continueProgram() +{ + if (isPaused()) + m_client->quitMessageLoopOnPause(); + m_pausedContext.Clear(); + m_executionState.Clear(); +} + +void V8DebuggerImpl::stepIntoStatement() +{ + DCHECK(isPaused()); + DCHECK(!m_executionState.IsEmpty()); + v8::HandleScope handleScope(m_isolate); + v8::Local<v8::Value> argv[] = { m_executionState }; + callDebuggerMethod(stepIntoV8MethodName, 1, argv); + continueProgram(); +} + +void V8DebuggerImpl::stepOverStatement() +{ + DCHECK(isPaused()); + DCHECK(!m_executionState.IsEmpty()); + v8::HandleScope handleScope(m_isolate); + v8::Local<v8::Value> argv[] = { m_executionState }; + callDebuggerMethod("stepOverStatement", 1, argv); + continueProgram(); +} + +void V8DebuggerImpl::stepOutOfFunction() +{ + DCHECK(isPaused()); + DCHECK(!m_executionState.IsEmpty()); + v8::HandleScope handleScope(m_isolate); + v8::Local<v8::Value> argv[] = { m_executionState }; + callDebuggerMethod(stepOutV8MethodName, 1, argv); + continueProgram(); +} + +void V8DebuggerImpl::clearStepping() +{ + DCHECK(enabled()); + v8::HandleScope scope(m_isolate); + v8::Context::Scope contextScope(debuggerContext()); + + v8::Local<v8::Value> argv[] = { v8::Undefined(m_isolate) }; + callDebuggerMethod("clearStepping", 0, argv); +} + +bool V8DebuggerImpl::setScriptSource(const String16& sourceID, const String16& newContent, bool preview, ErrorString* error, Maybe<protocol::Debugger::SetScriptSourceError>* errorData, JavaScriptCallFrames* newCallFrames, Maybe<bool>* stackChanged) +{ + class EnableLiveEditScope { + public: + explicit EnableLiveEditScope(v8::Isolate* isolate) : m_isolate(isolate) + { + v8::Debug::SetLiveEditEnabled(m_isolate, true); + inLiveEditScope = true; + } + ~EnableLiveEditScope() + { + v8::Debug::SetLiveEditEnabled(m_isolate, false); + inLiveEditScope = false; + } + private: + v8::Isolate* m_isolate; + }; + + DCHECK(enabled()); + v8::HandleScope scope(m_isolate); + + std::unique_ptr<v8::Context::Scope> contextScope; + if (!isPaused()) + contextScope = wrapUnique(new v8::Context::Scope(debuggerContext())); + + v8::Local<v8::Value> argv[] = { toV8String(m_isolate, sourceID), toV8String(m_isolate, newContent), v8Boolean(preview, m_isolate) }; + + v8::Local<v8::Value> v8result; + { + EnableLiveEditScope enableLiveEditScope(m_isolate); + v8::TryCatch tryCatch(m_isolate); + tryCatch.SetVerbose(false); + v8::MaybeLocal<v8::Value> maybeResult = callDebuggerMethod("liveEditScriptSource", 3, argv); + if (tryCatch.HasCaught()) { + v8::Local<v8::Message> message = tryCatch.Message(); + if (!message.IsEmpty()) + *error = toProtocolStringWithTypeCheck(message->Get()); + else + *error = "Unknown error."; + return false; + } + v8result = maybeResult.ToLocalChecked(); + } + DCHECK(!v8result.IsEmpty()); + v8::Local<v8::Object> resultTuple = v8result->ToObject(m_isolate); + int code = static_cast<int>(resultTuple->Get(0)->ToInteger(m_isolate)->Value()); + switch (code) { + case 0: + { + *stackChanged = resultTuple->Get(1)->BooleanValue(); + // Call stack may have changed after if the edited function was on the stack. + if (!preview && isPaused()) + newCallFrames->swap(currentCallFrames()); + return true; + } + // Compile error. + case 1: + { + *errorData = protocol::Debugger::SetScriptSourceError::create() + .setMessage(toProtocolStringWithTypeCheck(resultTuple->Get(2))) + .setLineNumber(resultTuple->Get(3)->ToInteger(m_isolate)->Value()) + .setColumnNumber(resultTuple->Get(4)->ToInteger(m_isolate)->Value()).build(); + return false; + } + } + *error = "Unknown error."; + return false; +} + +JavaScriptCallFrames V8DebuggerImpl::currentCallFrames(int limit) +{ + if (!m_isolate->InContext()) + return JavaScriptCallFrames(); + v8::Local<v8::Value> currentCallFramesV8; + if (m_executionState.IsEmpty()) { + v8::Local<v8::Function> currentCallFramesFunction = v8::Local<v8::Function>::Cast(m_debuggerScript.Get(m_isolate)->Get(v8InternalizedString("currentCallFrames"))); + currentCallFramesV8 = v8::Debug::Call(debuggerContext(), currentCallFramesFunction, v8::Integer::New(m_isolate, limit)).ToLocalChecked(); + } else { + v8::Local<v8::Value> argv[] = { m_executionState, v8::Integer::New(m_isolate, limit) }; + currentCallFramesV8 = callDebuggerMethod("currentCallFrames", PROTOCOL_ARRAY_LENGTH(argv), argv).ToLocalChecked(); + } + DCHECK(!currentCallFramesV8.IsEmpty()); + if (!currentCallFramesV8->IsArray()) + return JavaScriptCallFrames(); + v8::Local<v8::Array> callFramesArray = currentCallFramesV8.As<v8::Array>(); + JavaScriptCallFrames callFrames; + for (size_t i = 0; i < callFramesArray->Length(); ++i) { + v8::Local<v8::Value> callFrameValue; + if (!callFramesArray->Get(debuggerContext(), i).ToLocal(&callFrameValue)) + return JavaScriptCallFrames(); + if (!callFrameValue->IsObject()) + return JavaScriptCallFrames(); + v8::Local<v8::Object> callFrameObject = callFrameValue.As<v8::Object>(); + callFrames.append(JavaScriptCallFrame::create(debuggerContext(), v8::Local<v8::Object>::Cast(callFrameObject))); + } + return callFrames; +} + +static V8DebuggerImpl* toV8DebuggerImpl(v8::Local<v8::Value> data) +{ + void* p = v8::Local<v8::External>::Cast(data)->Value(); + return static_cast<V8DebuggerImpl*>(p); +} + +void V8DebuggerImpl::breakProgramCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + DCHECK(2 == info.Length()); + V8DebuggerImpl* thisPtr = toV8DebuggerImpl(info.Data()); + v8::Local<v8::Context> pausedContext = thisPtr->m_isolate->GetCurrentContext(); + v8::Local<v8::Value> exception; + v8::Local<v8::Array> hitBreakpoints; + thisPtr->handleProgramBreak(pausedContext, v8::Local<v8::Object>::Cast(info[0]), exception, hitBreakpoints); +} + +void V8DebuggerImpl::handleProgramBreak(v8::Local<v8::Context> pausedContext, v8::Local<v8::Object> executionState, v8::Local<v8::Value> exception, v8::Local<v8::Array> hitBreakpointNumbers, bool isPromiseRejection) +{ + // Don't allow nested breaks. + if (m_runningNestedMessageLoop) + return; + + V8DebuggerAgentImpl* agent = findEnabledDebuggerAgent(pausedContext); + if (!agent) + return; + + protocol::Vector<String16> breakpointIds; + if (!hitBreakpointNumbers.IsEmpty()) { + breakpointIds.resize(hitBreakpointNumbers->Length()); + for (size_t i = 0; i < hitBreakpointNumbers->Length(); i++) { + v8::Local<v8::Value> hitBreakpointNumber = hitBreakpointNumbers->Get(i); + DCHECK(!hitBreakpointNumber.IsEmpty() && hitBreakpointNumber->IsInt32()); + breakpointIds[i] = String16::number(hitBreakpointNumber->Int32Value()); + } + } + + m_pausedContext = pausedContext; + m_executionState = executionState; + V8DebuggerAgentImpl::SkipPauseRequest result = agent->didPause(pausedContext, exception, breakpointIds, isPromiseRejection); + if (result == V8DebuggerAgentImpl::RequestNoSkip) { + m_runningNestedMessageLoop = true; + int groupId = getGroupId(pausedContext); + DCHECK(groupId); + m_client->runMessageLoopOnPause(groupId); + // The agent may have been removed in the nested loop. + agent = findEnabledDebuggerAgent(pausedContext); + if (agent) + agent->didContinue(); + m_runningNestedMessageLoop = false; + } + m_pausedContext.Clear(); + m_executionState.Clear(); + + if (result == V8DebuggerAgentImpl::RequestStepFrame) { + v8::Local<v8::Value> argv[] = { executionState }; + callDebuggerMethod("stepFrameStatement", 1, argv); + } else if (result == V8DebuggerAgentImpl::RequestStepInto) { + v8::Local<v8::Value> argv[] = { executionState }; + callDebuggerMethod(stepIntoV8MethodName, 1, argv); + } else if (result == V8DebuggerAgentImpl::RequestStepOut) { + v8::Local<v8::Value> argv[] = { executionState }; + callDebuggerMethod(stepOutV8MethodName, 1, argv); + } +} + +void V8DebuggerImpl::v8DebugEventCallback(const v8::Debug::EventDetails& eventDetails) +{ + V8DebuggerImpl* thisPtr = toV8DebuggerImpl(eventDetails.GetCallbackData()); + thisPtr->handleV8DebugEvent(eventDetails); +} + +v8::Local<v8::Value> V8DebuggerImpl::callInternalGetterFunction(v8::Local<v8::Object> object, const char* functionName) +{ + v8::MicrotasksScope microtasks(m_isolate, v8::MicrotasksScope::kDoNotRunMicrotasks); + v8::Local<v8::Value> getterValue = object->Get(v8InternalizedString(functionName)); + DCHECK(!getterValue.IsEmpty() && getterValue->IsFunction()); + return v8::Local<v8::Function>::Cast(getterValue)->Call(m_isolate->GetCurrentContext(), object, 0, 0).ToLocalChecked(); +} + +void V8DebuggerImpl::handleV8DebugEvent(const v8::Debug::EventDetails& eventDetails) +{ + if (!enabled()) + return; + v8::DebugEvent event = eventDetails.GetEvent(); + if (event != v8::AsyncTaskEvent && event != v8::Break && event != v8::Exception && event != v8::AfterCompile && event != v8::BeforeCompile && event != v8::CompileError) + return; + + v8::Local<v8::Context> eventContext = eventDetails.GetEventContext(); + DCHECK(!eventContext.IsEmpty()); + + V8DebuggerAgentImpl* agent = findEnabledDebuggerAgent(eventContext); + if (agent) { + v8::HandleScope scope(m_isolate); + if (event == v8::AfterCompile || event == v8::CompileError) { + v8::Context::Scope contextScope(debuggerContext()); + v8::Local<v8::Value> argv[] = { eventDetails.GetEventData() }; + v8::Local<v8::Value> value = callDebuggerMethod("getAfterCompileScript", 1, argv).ToLocalChecked(); + DCHECK(value->IsObject()); + v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(value); + agent->didParseSource(createParsedScript(object, event == v8::AfterCompile)); + } else if (event == v8::Exception) { + v8::Local<v8::Object> eventData = eventDetails.GetEventData(); + v8::Local<v8::Value> exception = callInternalGetterFunction(eventData, "exception"); + v8::Local<v8::Value> promise = callInternalGetterFunction(eventData, "promise"); + bool isPromiseRejection = !promise.IsEmpty() && promise->IsObject(); + handleProgramBreak(eventContext, eventDetails.GetExecutionState(), exception, v8::Local<v8::Array>(), isPromiseRejection); + } else if (event == v8::Break) { + v8::Local<v8::Value> argv[] = { eventDetails.GetEventData() }; + v8::Local<v8::Value> hitBreakpoints = callDebuggerMethod("getBreakpointNumbers", 1, argv).ToLocalChecked(); + DCHECK(hitBreakpoints->IsArray()); + handleProgramBreak(eventContext, eventDetails.GetExecutionState(), v8::Local<v8::Value>(), hitBreakpoints.As<v8::Array>()); + } else if (event == v8::AsyncTaskEvent) { + if (agent->v8AsyncTaskEventsEnabled()) + handleV8AsyncTaskEvent(agent, eventContext, eventDetails.GetExecutionState(), eventDetails.GetEventData()); + } + } +} + +void V8DebuggerImpl::handleV8AsyncTaskEvent(V8DebuggerAgentImpl* agent, v8::Local<v8::Context> context, v8::Local<v8::Object> executionState, v8::Local<v8::Object> eventData) +{ + String16 type = toProtocolStringWithTypeCheck(callInternalGetterFunction(eventData, "type")); + String16 name = toProtocolStringWithTypeCheck(callInternalGetterFunction(eventData, "name")); + int id = callInternalGetterFunction(eventData, "id")->ToInteger(m_isolate)->Value(); + + m_pausedContext = context; + m_executionState = executionState; + agent->didReceiveV8AsyncTaskEvent(context, type, name, id); + m_pausedContext.Clear(); + m_executionState.Clear(); +} + +V8DebuggerParsedScript V8DebuggerImpl::createParsedScript(v8::Local<v8::Object> object, bool success) +{ + v8::Local<v8::Value> id = object->Get(v8InternalizedString("id")); + DCHECK(!id.IsEmpty() && id->IsInt32()); + + V8DebuggerParsedScript parsedScript; + parsedScript.scriptId = String16::number(id->Int32Value()); + parsedScript.script.setURL(toProtocolStringWithTypeCheck(object->Get(v8InternalizedString("name")))) + .setSourceURL(toProtocolStringWithTypeCheck(object->Get(v8InternalizedString("sourceURL")))) + .setSourceMappingURL(toProtocolStringWithTypeCheck(object->Get(v8InternalizedString("sourceMappingURL")))) + .setSource(toProtocolStringWithTypeCheck(object->Get(v8InternalizedString("source")))) + .setStartLine(object->Get(v8InternalizedString("startLine"))->ToInteger(m_isolate)->Value()) + .setStartColumn(object->Get(v8InternalizedString("startColumn"))->ToInteger(m_isolate)->Value()) + .setEndLine(object->Get(v8InternalizedString("endLine"))->ToInteger(m_isolate)->Value()) + .setEndColumn(object->Get(v8InternalizedString("endColumn"))->ToInteger(m_isolate)->Value()) + .setIsContentScript(object->Get(v8InternalizedString("isContentScript"))->ToBoolean(m_isolate)->Value()) + .setIsInternalScript(object->Get(v8InternalizedString("isInternalScript"))->ToBoolean(m_isolate)->Value()) + .setExecutionContextId(object->Get(v8InternalizedString("executionContextId"))->ToInteger(m_isolate)->Value()) + .setIsLiveEdit(inLiveEditScope); + parsedScript.success = success; + return parsedScript; +} + +void V8DebuggerImpl::compileDebuggerScript() +{ + if (!m_debuggerScript.IsEmpty()) { + NOTREACHED(); + return; + } + + v8::HandleScope scope(m_isolate); + v8::Context::Scope contextScope(debuggerContext()); + + v8::Local<v8::String> scriptValue = v8::String::NewFromUtf8(m_isolate, DebuggerScript_js, v8::NewStringType::kInternalized, sizeof(DebuggerScript_js)).ToLocalChecked(); + v8::Local<v8::Value> value; + if (!compileAndRunInternalScript(debuggerContext(), scriptValue).ToLocal(&value)) + return; + DCHECK(value->IsObject()); + m_debuggerScript.Reset(m_isolate, value.As<v8::Object>()); +} + +v8::Local<v8::Context> V8DebuggerImpl::debuggerContext() const +{ + DCHECK(!m_debuggerContext.IsEmpty()); + return m_debuggerContext.Get(m_isolate); +} + +v8::Local<v8::String> V8DebuggerImpl::v8InternalizedString(const char* str) const +{ + return v8::String::NewFromUtf8(m_isolate, str, v8::NewStringType::kInternalized).ToLocalChecked(); +} + +v8::MaybeLocal<v8::Value> V8DebuggerImpl::functionScopes(v8::Local<v8::Function> function) +{ + if (!enabled()) { + NOTREACHED(); + return v8::Local<v8::Value>::New(m_isolate, v8::Undefined(m_isolate)); + } + v8::Local<v8::Value> argv[] = { function }; + return callDebuggerMethod("getFunctionScopes", 1, argv); +} + +v8::Local<v8::Value> V8DebuggerImpl::generatorObjectDetails(v8::Local<v8::Object>& object) +{ + if (!enabled()) { + NOTREACHED(); + return v8::Local<v8::Value>::New(m_isolate, v8::Undefined(m_isolate)); + } + v8::Local<v8::Value> argv[] = { object }; + return callDebuggerMethod("getGeneratorObjectDetails", 1, argv).ToLocalChecked(); +} + +v8::Local<v8::Value> V8DebuggerImpl::collectionEntries(v8::Local<v8::Object>& object) +{ + if (!enabled()) { + NOTREACHED(); + return v8::Local<v8::Value>::New(m_isolate, v8::Undefined(m_isolate)); + } + v8::Local<v8::Value> argv[] = { object }; + return callDebuggerMethod("getCollectionEntries", 1, argv).ToLocalChecked(); +} + +bool V8DebuggerImpl::isPaused() +{ + return !m_pausedContext.IsEmpty(); +} + +v8::MaybeLocal<v8::Value> V8DebuggerImpl::runCompiledScript(v8::Local<v8::Context> context, v8::Local<v8::Script> script) +{ + // TODO(dgozman): get rid of this check. + if (!m_client->isExecutionAllowed()) + return v8::MaybeLocal<v8::Value>(); + + v8::MicrotasksScope microtasksScope(m_isolate, v8::MicrotasksScope::kRunMicrotasks); + int groupId = getGroupId(context); + if (V8DebuggerAgentImpl* agent = findEnabledDebuggerAgent(groupId)) + agent->willExecuteScript(script->GetUnboundScript()->GetId()); + v8::MaybeLocal<v8::Value> result = script->Run(context); + // Get agent from the map again, since it could have detached during script execution. + if (V8DebuggerAgentImpl* agent = findEnabledDebuggerAgent(groupId)) + agent->didExecuteScript(); + return result; +} + +v8::MaybeLocal<v8::Value> V8DebuggerImpl::callFunction(v8::Local<v8::Function> function, v8::Local<v8::Context> context, v8::Local<v8::Value> receiver, int argc, v8::Local<v8::Value> info[]) +{ + // TODO(dgozman): get rid of this check. + if (!m_client->isExecutionAllowed()) + return v8::MaybeLocal<v8::Value>(); + + v8::MicrotasksScope microtasksScope(m_isolate, v8::MicrotasksScope::kRunMicrotasks); + int groupId = getGroupId(context); + if (V8DebuggerAgentImpl* agent = findEnabledDebuggerAgent(groupId)) + agent->willExecuteScript(function->ScriptId()); + v8::MaybeLocal<v8::Value> result = function->Call(context, receiver, argc, info); + // Get agent from the map again, since it could have detached during script execution. + if (V8DebuggerAgentImpl* agent = findEnabledDebuggerAgent(groupId)) + agent->didExecuteScript(); + return result; +} + +v8::MaybeLocal<v8::Value> V8DebuggerImpl::compileAndRunInternalScript(v8::Local<v8::Context> context, v8::Local<v8::String> source) +{ + v8::Local<v8::Script> script = compileInternalScript(context, source, String()); + if (script.IsEmpty()) + return v8::MaybeLocal<v8::Value>(); + v8::MicrotasksScope microtasksScope(m_isolate, v8::MicrotasksScope::kDoNotRunMicrotasks); + return script->Run(context); +} + +v8::Local<v8::Script> V8DebuggerImpl::compileInternalScript(v8::Local<v8::Context> context, v8::Local<v8::String> code, const String16& fileName) +{ + // NOTE: For compatibility with WebCore, ScriptSourceCode's line starts at + // 1, whereas v8 starts at 0. + v8::ScriptOrigin origin( + toV8String(m_isolate, fileName), + v8::Integer::New(m_isolate, 0), + v8::Integer::New(m_isolate, 0), + v8::False(m_isolate), // sharable + v8::Local<v8::Integer>(), + v8::True(m_isolate), // internal + toV8String(m_isolate, String16()), // sourceMap + v8::True(m_isolate)); // opaqueresource + v8::ScriptCompiler::Source source(code, origin); + v8::Local<v8::Script> script; + if (!v8::ScriptCompiler::Compile(context, &source, v8::ScriptCompiler::kNoCompileOptions).ToLocal(&script)) + return v8::Local<v8::Script>(); + return script; +} + +std::unique_ptr<V8StackTrace> V8DebuggerImpl::createStackTrace(v8::Local<v8::StackTrace> stackTrace, size_t maxStackSize) +{ + V8DebuggerAgentImpl* agent = findEnabledDebuggerAgent(m_isolate->GetCurrentContext()); + return V8StackTraceImpl::create(agent, stackTrace, maxStackSize); +} + +std::unique_ptr<V8InspectorSession> V8DebuggerImpl::connect(int contextGroupId) +{ + DCHECK(!m_sessions.contains(contextGroupId)); + std::unique_ptr<V8InspectorSessionImpl> session = V8InspectorSessionImpl::create(this, contextGroupId); + m_sessions.set(contextGroupId, session.get()); + return std::move(session); +} + +void V8DebuggerImpl::disconnect(V8InspectorSessionImpl* session) +{ + DCHECK(m_sessions.contains(session->contextGroupId())); + m_sessions.remove(session->contextGroupId()); +} + +void V8DebuggerImpl::contextCreated(const V8ContextInfo& info) +{ + DCHECK(info.context->GetIsolate() == m_isolate); + // TODO(dgozman): make s_lastContextId non-static. + int contextId = atomicIncrement(&s_lastContextId); + String16 debugData = String16::number(info.contextGroupId) + "," + String16::number(contextId) + "," + (info.isDefault ? "default" : "nondefault"); + v8::HandleScope scope(m_isolate); + v8::Context::Scope contextScope(info.context); + info.context->SetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex), toV8String(m_isolate, debugData)); + + if (!m_contexts.contains(info.contextGroupId)) + m_contexts.set(info.contextGroupId, wrapUnique(new ContextByIdMap())); + DCHECK(!m_contexts.get(info.contextGroupId)->contains(contextId)); + + std::unique_ptr<InspectedContext> contextOwner(new InspectedContext(this, info, contextId)); + InspectedContext* inspectedContext = contextOwner.get(); + m_contexts.get(info.contextGroupId)->set(contextId, std::move(contextOwner)); + + if (V8InspectorSessionImpl* session = m_sessions.get(info.contextGroupId)) + session->runtimeAgentImpl()->reportExecutionContextCreated(inspectedContext); +} + +void V8DebuggerImpl::contextDestroyed(v8::Local<v8::Context> context) +{ + int contextId = V8Debugger::contextId(context); + int contextGroupId = getGroupId(context); + if (!m_contexts.contains(contextGroupId) || !m_contexts.get(contextGroupId)->contains(contextId)) + return; + + InspectedContext* inspectedContext = m_contexts.get(contextGroupId)->get(contextId); + if (V8InspectorSessionImpl* session = m_sessions.get(contextGroupId)) + session->runtimeAgentImpl()->reportExecutionContextDestroyed(inspectedContext); + + m_contexts.get(contextGroupId)->remove(contextId); + if (m_contexts.get(contextGroupId)->isEmpty()) + m_contexts.remove(contextGroupId); +} + +void V8DebuggerImpl::resetContextGroup(int contextGroupId) +{ + if (V8InspectorSessionImpl* session = m_sessions.get(contextGroupId)) + session->reset(); + m_contexts.remove(contextGroupId); +} + +void V8DebuggerImpl::willExecuteScript(v8::Local<v8::Context> context, int scriptId) +{ + if (V8DebuggerAgentImpl* agent = findEnabledDebuggerAgent(context)) + agent->willExecuteScript(scriptId); +} + +void V8DebuggerImpl::didExecuteScript(v8::Local<v8::Context> context) +{ + if (V8DebuggerAgentImpl* agent = findEnabledDebuggerAgent(context)) + agent->didExecuteScript(); +} + +void V8DebuggerImpl::idleStarted() +{ + m_isolate->GetCpuProfiler()->SetIdle(true); +} + +void V8DebuggerImpl::idleFinished() +{ + m_isolate->GetCpuProfiler()->SetIdle(false); +} + +std::unique_ptr<V8StackTrace> V8DebuggerImpl::captureStackTrace(size_t maxStackSize) +{ + V8DebuggerAgentImpl* agent = findEnabledDebuggerAgent(m_isolate->GetCurrentContext()); + return V8StackTraceImpl::capture(agent, maxStackSize); +} + +v8::Local<v8::Context> V8DebuggerImpl::regexContext() +{ + if (m_regexContext.IsEmpty()) + m_regexContext.Reset(m_isolate, v8::Context::New(m_isolate)); + return m_regexContext.Get(m_isolate); +} + +void V8DebuggerImpl::discardInspectedContext(int contextGroupId, int contextId) +{ + if (!m_contexts.contains(contextGroupId) || !m_contexts.get(contextGroupId)->contains(contextId)) + return; + m_contexts.get(contextGroupId)->remove(contextId); + if (m_contexts.get(contextGroupId)->isEmpty()) + m_contexts.remove(contextGroupId); +} + +const V8DebuggerImpl::ContextByIdMap* V8DebuggerImpl::contextGroup(int contextGroupId) +{ + if (!m_contexts.contains(contextGroupId)) + return nullptr; + return m_contexts.get(contextGroupId); +} + +V8InspectorSessionImpl* V8DebuggerImpl::sessionForContextGroup(int contextGroupId) +{ + return contextGroupId ? m_sessions.get(contextGroupId) : nullptr; +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/V8DebuggerImpl.h b/deps/v8_inspector/platform/v8_inspector/V8DebuggerImpl.h new file mode 100644 index 00000000000000..48f335004a093c --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8DebuggerImpl.h @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef V8DebuggerImpl_h +#define V8DebuggerImpl_h + +#include "platform/inspector_protocol/TypeBuilder.h" +#include "platform/v8_inspector/JavaScriptCallFrame.h" +#include "platform/v8_inspector/V8DebuggerScript.h" +#include "platform/v8_inspector/public/V8Debugger.h" +#include "wtf/PtrUtil.h" + +#include <v8-debug.h> +#include <v8.h> + +namespace blink { + +using protocol::Maybe; + +struct ScriptBreakpoint; +class InspectedContext; +class V8DebuggerAgentImpl; +class V8InspectorSessionImpl; +class V8RuntimeAgentImpl; + +class V8DebuggerImpl : public V8Debugger { + PROTOCOL_DISALLOW_COPY(V8DebuggerImpl); +public: + V8DebuggerImpl(v8::Isolate*, V8DebuggerClient*); + ~V8DebuggerImpl() override; + + bool enabled() const; + + String16 setBreakpoint(const String16& sourceID, const ScriptBreakpoint&, int* actualLineNumber, int* actualColumnNumber, bool interstatementLocation); + void removeBreakpoint(const String16& breakpointId); + void setBreakpointsActivated(bool); + bool breakpointsActivated() const { return m_breakpointsActivated; } + + enum PauseOnExceptionsState { + DontPauseOnExceptions, + PauseOnAllExceptions, + PauseOnUncaughtExceptions + }; + PauseOnExceptionsState getPauseOnExceptionsState(); + void setPauseOnExceptionsState(PauseOnExceptionsState); + void setPauseOnNextStatement(bool); + bool canBreakProgram(); + void breakProgram(); + void continueProgram(); + void stepIntoStatement(); + void stepOverStatement(); + void stepOutOfFunction(); + void clearStepping(); + + bool setScriptSource(const String16& sourceID, const String16& newContent, bool preview, ErrorString*, Maybe<protocol::Debugger::SetScriptSourceError>*, JavaScriptCallFrames* newCallFrames, Maybe<bool>* stackChanged); + JavaScriptCallFrames currentCallFrames(int limit = 0); + + // Each script inherits debug data from v8::Context where it has been compiled. + // Only scripts whose debug data matches |contextGroupId| will be reported. + // Passing 0 will result in reporting all scripts. + void getCompiledScripts(int contextGroupId, protocol::Vector<V8DebuggerParsedScript>&); + void debuggerAgentEnabled(); + void debuggerAgentDisabled(); + + bool isPaused() override; + v8::Local<v8::Context> pausedContext() { return m_pausedContext; } + + v8::MaybeLocal<v8::Value> functionScopes(v8::Local<v8::Function>); + v8::Local<v8::Value> generatorObjectDetails(v8::Local<v8::Object>&); + v8::Local<v8::Value> collectionEntries(v8::Local<v8::Object>&); + + v8::Isolate* isolate() const { return m_isolate; } + V8DebuggerClient* client() { return m_client; } + + v8::MaybeLocal<v8::Value> runCompiledScript(v8::Local<v8::Context>, v8::Local<v8::Script>); + v8::MaybeLocal<v8::Value> callFunction(v8::Local<v8::Function>, v8::Local<v8::Context>, v8::Local<v8::Value> receiver, int argc, v8::Local<v8::Value> info[]); + v8::MaybeLocal<v8::Value> compileAndRunInternalScript(v8::Local<v8::Context>, v8::Local<v8::String>); + v8::Local<v8::Script> compileInternalScript(v8::Local<v8::Context>, v8::Local<v8::String>, const String16& fileName); + v8::Local<v8::Context> regexContext(); + + // V8Debugger implementation + std::unique_ptr<V8InspectorSession> connect(int contextGroupId) override; + void contextCreated(const V8ContextInfo&) override; + void contextDestroyed(v8::Local<v8::Context>) override; + void resetContextGroup(int contextGroupId) override; + void willExecuteScript(v8::Local<v8::Context>, int scriptId) override; + void didExecuteScript(v8::Local<v8::Context>) override; + void idleStarted() override; + void idleFinished() override; + std::unique_ptr<V8StackTrace> createStackTrace(v8::Local<v8::StackTrace>, size_t maxStackSize) override; + std::unique_ptr<V8StackTrace> captureStackTrace(size_t maxStackSize) override; + + using ContextByIdMap = protocol::HashMap<int, std::unique_ptr<InspectedContext>>; + void discardInspectedContext(int contextGroupId, int contextId); + const ContextByIdMap* contextGroup(int contextGroupId); + void disconnect(V8InspectorSessionImpl*); + V8InspectorSessionImpl* sessionForContextGroup(int contextGroupId); + +private: + void enable(); + void disable(); + V8DebuggerAgentImpl* findEnabledDebuggerAgent(int contextGroupId); + V8DebuggerAgentImpl* findEnabledDebuggerAgent(v8::Local<v8::Context>); + + void compileDebuggerScript(); + v8::MaybeLocal<v8::Value> callDebuggerMethod(const char* functionName, int argc, v8::Local<v8::Value> argv[]); + v8::Local<v8::Context> debuggerContext() const; + void clearBreakpoints(); + + V8DebuggerParsedScript createParsedScript(v8::Local<v8::Object> sourceObject, bool success); + + static void breakProgramCallback(const v8::FunctionCallbackInfo<v8::Value>&); + void handleProgramBreak(v8::Local<v8::Context> pausedContext, v8::Local<v8::Object> executionState, v8::Local<v8::Value> exception, v8::Local<v8::Array> hitBreakpoints, bool isPromiseRejection = false); + static void v8DebugEventCallback(const v8::Debug::EventDetails&); + v8::Local<v8::Value> callInternalGetterFunction(v8::Local<v8::Object>, const char* functionName); + void handleV8DebugEvent(const v8::Debug::EventDetails&); + + v8::Local<v8::String> v8InternalizedString(const char*) const; + + void handleV8AsyncTaskEvent(V8DebuggerAgentImpl*, v8::Local<v8::Context>, v8::Local<v8::Object> executionState, v8::Local<v8::Object> eventData); + + v8::Isolate* m_isolate; + V8DebuggerClient* m_client; + using ContextsByGroupMap = protocol::HashMap<int, std::unique_ptr<ContextByIdMap>>; + ContextsByGroupMap m_contexts; + using SessionMap = protocol::HashMap<int, V8InspectorSessionImpl*>; + SessionMap m_sessions; + int m_enabledAgentsCount; + bool m_breakpointsActivated; + v8::Global<v8::Object> m_debuggerScript; + v8::Global<v8::Context> m_debuggerContext; + v8::Local<v8::Object> m_executionState; + v8::Local<v8::Context> m_pausedContext; + bool m_runningNestedMessageLoop; + v8::Global<v8::Context> m_regexContext; +}; + +} // namespace blink + + +#endif // V8DebuggerImpl_h diff --git a/deps/v8_inspector/platform/v8_inspector/V8DebuggerScript.cpp b/deps/v8_inspector/platform/v8_inspector/V8DebuggerScript.cpp new file mode 100644 index 00000000000000..14dba693e97653 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8DebuggerScript.cpp @@ -0,0 +1,104 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/v8_inspector/V8DebuggerScript.h" + +namespace blink { + +V8DebuggerScript::V8DebuggerScript() + : m_startLine(0) + , m_startColumn(0) + , m_endLine(0) + , m_endColumn(0) + , m_executionContextId(0) + , m_isContentScript(false) + , m_isInternalScript(false) + , m_isLiveEdit(false) +{ +} + +String16 V8DebuggerScript::sourceURL() const +{ + return m_sourceURL.isEmpty() ? m_url : m_sourceURL; +} + +V8DebuggerScript& V8DebuggerScript::setURL(const String16& url) +{ + m_url = url; + return *this; +} + +V8DebuggerScript& V8DebuggerScript::setSourceURL(const String16& sourceURL) +{ + m_sourceURL = sourceURL; + return *this; +} + +V8DebuggerScript& V8DebuggerScript::setSourceMappingURL(const String16& sourceMappingURL) +{ + m_sourceMappingURL = sourceMappingURL; + return *this; +} + +V8DebuggerScript& V8DebuggerScript::setSource(const String16& source) +{ + m_source = source; + return *this; +} + +V8DebuggerScript& V8DebuggerScript::setHash(const String16& hash) +{ + m_hash = hash; + return *this; +} + +V8DebuggerScript& V8DebuggerScript::setStartLine(int startLine) +{ + m_startLine = startLine; + return *this; +} + +V8DebuggerScript& V8DebuggerScript::setStartColumn(int startColumn) +{ + m_startColumn = startColumn; + return *this; +} + +V8DebuggerScript& V8DebuggerScript::setEndLine(int endLine) +{ + m_endLine = endLine; + return *this; +} + +V8DebuggerScript& V8DebuggerScript::setEndColumn(int endColumn) +{ + m_endColumn = endColumn; + return *this; +} + +V8DebuggerScript& V8DebuggerScript::setExecutionContextId(int executionContextId) +{ + m_executionContextId = executionContextId; + return *this; +} + +V8DebuggerScript& V8DebuggerScript::setIsContentScript(bool isContentScript) +{ + m_isContentScript = isContentScript; + return *this; +} + +V8DebuggerScript& V8DebuggerScript::setIsInternalScript(bool isInternalScript) +{ + m_isInternalScript = isInternalScript; + return *this; +} + +V8DebuggerScript& V8DebuggerScript::setIsLiveEdit(bool isLiveEdit) +{ + m_isLiveEdit = isLiveEdit; + return *this; +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/V8DebuggerScript.h b/deps/v8_inspector/platform/v8_inspector/V8DebuggerScript.h new file mode 100644 index 00000000000000..55080be0dacea7 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8DebuggerScript.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef V8DebuggerScript_h +#define V8DebuggerScript_h + +#include "platform/inspector_protocol/Allocator.h" +#include "platform/inspector_protocol/String16.h" +#include <v8.h> + +namespace blink { + +class V8DebuggerScript { + PROTOCOL_DISALLOW_NEW(); +public: + V8DebuggerScript(); + + String16 url() const { return m_url; } + bool hasSourceURL() const { return !m_sourceURL.isEmpty(); } + String16 sourceURL() const; + String16 sourceMappingURL() const { return m_sourceMappingURL; } + String16 source() const { return m_source; } + String16 hash() const { return m_hash; } + int startLine() const { return m_startLine; } + int startColumn() const { return m_startColumn; } + int endLine() const { return m_endLine; } + int endColumn() const { return m_endColumn; } + int executionContextId() const { return m_executionContextId; } + bool isContentScript() const { return m_isContentScript; } + bool isInternalScript() const { return m_isInternalScript; } + bool isLiveEdit() const { return m_isLiveEdit; } + + V8DebuggerScript& setURL(const String16&); + V8DebuggerScript& setSourceURL(const String16&); + V8DebuggerScript& setSourceMappingURL(const String16&); + V8DebuggerScript& setSource(const String16&); + V8DebuggerScript& setHash(const String16&); + V8DebuggerScript& setStartLine(int); + V8DebuggerScript& setStartColumn(int); + V8DebuggerScript& setEndLine(int); + V8DebuggerScript& setEndColumn(int); + V8DebuggerScript& setExecutionContextId(int); + V8DebuggerScript& setIsContentScript(bool); + V8DebuggerScript& setIsInternalScript(bool); + V8DebuggerScript& setIsLiveEdit(bool); + +private: + String16 m_url; + String16 m_sourceURL; + String16 m_sourceMappingURL; + String16 m_source; + String16 m_hash; + int m_startLine; + int m_startColumn; + int m_endLine; + int m_endColumn; + int m_executionContextId; + bool m_isContentScript; + bool m_isInternalScript; + bool m_isLiveEdit; +}; + +struct V8DebuggerParsedScript { + String16 scriptId; + V8DebuggerScript script; + bool success; +}; + +} // namespace blink + + +#endif // V8DebuggerScript_h diff --git a/deps/v8_inspector/platform/v8_inspector/V8FunctionCall.cpp b/deps/v8_inspector/platform/v8_inspector/V8FunctionCall.cpp new file mode 100644 index 00000000000000..872758129d74d9 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8FunctionCall.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "platform/v8_inspector/V8FunctionCall.h" + +#include "platform/v8_inspector/V8Compat.h" +#include "platform/v8_inspector/V8DebuggerImpl.h" +#include "platform/v8_inspector/V8StringUtil.h" +#include "platform/v8_inspector/public/V8DebuggerClient.h" +#include "wtf/PtrUtil.h" + +#include <v8.h> + +namespace blink { + +V8FunctionCall::V8FunctionCall(V8DebuggerImpl* debugger, v8::Local<v8::Context> context, v8::Local<v8::Value> value, const String16& name) + : m_debugger(debugger) + , m_context(context) + , m_name(toV8String(context->GetIsolate(), name)) + , m_value(value) +{ +} + +void V8FunctionCall::appendArgument(v8::Local<v8::Value> value) +{ + m_arguments.append(value); +} + +void V8FunctionCall::appendArgument(const String16& argument) +{ + m_arguments.append(toV8String(m_context->GetIsolate(), argument)); +} + +void V8FunctionCall::appendArgument(int argument) +{ + m_arguments.append(v8::Number::New(m_context->GetIsolate(), argument)); +} + +void V8FunctionCall::appendArgument(bool argument) +{ + m_arguments.append(argument ? v8::True(m_context->GetIsolate()) : v8::False(m_context->GetIsolate())); +} + +void V8FunctionCall::appendUndefinedArgument() +{ + m_arguments.append(v8::Undefined(m_context->GetIsolate())); +} + +v8::Local<v8::Value> V8FunctionCall::call(bool& hadException, bool reportExceptions) +{ + v8::TryCatch tryCatch(m_context->GetIsolate()); + tryCatch.SetVerbose(reportExceptions); + + v8::Local<v8::Value> result = callWithoutExceptionHandling(); + hadException = tryCatch.HasCaught(); + return result; +} + +v8::Local<v8::Value> V8FunctionCall::callWithoutExceptionHandling() +{ + // TODO(dgozman): get rid of this check. + if (!m_debugger->client()->isExecutionAllowed()) + return v8::Local<v8::Value>(); + + v8::Local<v8::Object> thisObject = v8::Local<v8::Object>::Cast(m_value); + v8::Local<v8::Value> value; + if (!thisObject->Get(m_context, m_name).ToLocal(&value)) + return v8::Local<v8::Value>(); + + DCHECK(value->IsFunction()); + + v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(value); + std::unique_ptr<v8::Local<v8::Value>[]> info(new v8::Local<v8::Value>[m_arguments.size()]); + for (size_t i = 0; i < m_arguments.size(); ++i) { + info[i] = m_arguments[i]; + DCHECK(!info[i].IsEmpty()); + } + + v8::MicrotasksScope microtasksScope(m_context->GetIsolate(), v8::MicrotasksScope::kDoNotRunMicrotasks); + v8::Local<v8::Value> result; + if (!function->Call(m_context, thisObject, m_arguments.size(), info.get()).ToLocal(&result)) + return v8::Local<v8::Value>(); + return result; +} + +v8::Local<v8::Function> V8FunctionCall::function() +{ + v8::TryCatch tryCatch(m_context->GetIsolate()); + v8::Local<v8::Object> thisObject = v8::Local<v8::Object>::Cast(m_value); + v8::Local<v8::Value> value; + if (!thisObject->Get(m_context, m_name).ToLocal(&value)) + return v8::Local<v8::Function>(); + + DCHECK(value->IsFunction()); + return v8::Local<v8::Function>::Cast(value); +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/V8FunctionCall.h b/deps/v8_inspector/platform/v8_inspector/V8FunctionCall.h new file mode 100644 index 00000000000000..1c94d240aa91de --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8FunctionCall.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef V8FunctionCall_h +#define V8FunctionCall_h + +#include "platform/inspector_protocol/Collections.h" +#include "platform/inspector_protocol/String16.h" + +#include <v8.h> + +namespace blink { + +class V8DebuggerImpl; + +class V8FunctionCall { +public: + V8FunctionCall(V8DebuggerImpl*, v8::Local<v8::Context>, v8::Local<v8::Value>, const String16& name); + + void appendArgument(v8::Local<v8::Value>); + void appendArgument(const String16&); + void appendArgument(int); + void appendArgument(bool); + void appendUndefinedArgument(); + + v8::Local<v8::Value> call(bool& hadException, bool reportExceptions = true); + v8::Local<v8::Function> function(); + v8::Local<v8::Value> callWithoutExceptionHandling(); + v8::Local<v8::Context> context() { return m_context; } + +protected: + V8DebuggerImpl* m_debugger; + v8::Local<v8::Context> m_context; + protocol::Vector<v8::Local<v8::Value>> m_arguments; + v8::Local<v8::String> m_name; + v8::Local<v8::Value> m_value; +}; + +} // namespace blink + +#endif // V8FunctionCall diff --git a/deps/v8_inspector/platform/v8_inspector/V8HeapProfilerAgentImpl.cpp b/deps/v8_inspector/platform/v8_inspector/V8HeapProfilerAgentImpl.cpp new file mode 100644 index 00000000000000..b6f8179cc078e5 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8HeapProfilerAgentImpl.cpp @@ -0,0 +1,409 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/v8_inspector/V8HeapProfilerAgentImpl.h" + +#include "platform/v8_inspector/InjectedScript.h" +#include "platform/v8_inspector/V8DebuggerImpl.h" +#include "platform/v8_inspector/V8InspectorSessionImpl.h" +#include "platform/v8_inspector/V8StringUtil.h" +#include "platform/v8_inspector/public/V8DebuggerClient.h" +#include <v8-profiler.h> +#include <v8-version.h> + +namespace blink { + +namespace { + +namespace HeapProfilerAgentState { +static const char heapProfilerEnabled[] = "heapProfilerEnabled"; +static const char heapObjectsTrackingEnabled[] = "heapObjectsTrackingEnabled"; +static const char allocationTrackingEnabled[] = "allocationTrackingEnabled"; +#if V8_MAJOR_VERSION >= 5 +static const char samplingHeapProfilerEnabled[] = "samplingHeapProfilerEnabled"; +static const char samplingHeapProfilerInterval[] = "samplingHeapProfilerInterval"; +#endif +} + +class HeapSnapshotProgress final : public v8::ActivityControl { +public: + HeapSnapshotProgress(protocol::Frontend::HeapProfiler* frontend) + : m_frontend(frontend) { } + ControlOption ReportProgressValue(int done, int total) override + { + m_frontend->reportHeapSnapshotProgress(done, total, protocol::Maybe<bool>()); + if (done >= total) { + m_frontend->reportHeapSnapshotProgress(total, total, true); + } + m_frontend->flush(); + return kContinue; + } +private: + protocol::Frontend::HeapProfiler* m_frontend; +}; + +class GlobalObjectNameResolver final : public v8::HeapProfiler::ObjectNameResolver { +public: + explicit GlobalObjectNameResolver(V8InspectorSessionImpl* session) : m_offset(0), m_session(session) + { + m_strings.resize(10000); + } + + const char* GetName(v8::Local<v8::Object> object) override + { + int contextId = V8Debugger::contextId(object->CreationContext()); + if (!contextId) + return ""; + ErrorString errorString; + InjectedScript* injectedScript = m_session->findInjectedScript(&errorString, contextId); + if (!injectedScript) + return ""; + String16 name = injectedScript->context()->origin(); + size_t length = name.length(); + if (m_offset + length + 1 >= m_strings.size()) + return ""; + for (size_t i = 0; i < length; ++i) { + UChar ch = name[i]; + m_strings[m_offset + i] = ch > 0xff ? '?' : static_cast<char>(ch); + } + m_strings[m_offset + length] = '\0'; + char* result = &*m_strings.begin() + m_offset; + m_offset += length + 1; + return result; + } + +private: + size_t m_offset; + protocol::Vector<char> m_strings; + V8InspectorSessionImpl* m_session; +}; + +class HeapSnapshotOutputStream final : public v8::OutputStream { +public: + HeapSnapshotOutputStream(protocol::Frontend::HeapProfiler* frontend) + : m_frontend(frontend) { } + void EndOfStream() override { } + int GetChunkSize() override { return 102400; } + WriteResult WriteAsciiChunk(char* data, int size) override + { + m_frontend->addHeapSnapshotChunk(String16(data, size)); + m_frontend->flush(); + return kContinue; + } +private: + protocol::Frontend::HeapProfiler* m_frontend; +}; + +v8::Local<v8::Object> objectByHeapObjectId(v8::Isolate* isolate, int id) +{ + v8::HeapProfiler* profiler = isolate->GetHeapProfiler(); + v8::Local<v8::Value> value = profiler->FindObjectById(id); + if (value.IsEmpty() || !value->IsObject()) + return v8::Local<v8::Object>(); + return value.As<v8::Object>(); +} + +class InspectableHeapObject final : public V8InspectorSession::Inspectable { +public: + explicit InspectableHeapObject(int heapObjectId) : m_heapObjectId(heapObjectId) { } + v8::Local<v8::Value> get(v8::Local<v8::Context> context) override + { + return objectByHeapObjectId(context->GetIsolate(), m_heapObjectId); + } +private: + int m_heapObjectId; +}; + +class HeapStatsStream final : public v8::OutputStream { +public: + HeapStatsStream(protocol::Frontend::HeapProfiler* frontend) + : m_frontend(frontend) + { + } + + void EndOfStream() override { } + + WriteResult WriteAsciiChunk(char* data, int size) override + { + DCHECK(false); + return kAbort; + } + + WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* updateData, int count) override + { + DCHECK(count > 0); + std::unique_ptr<protocol::Array<int>> statsDiff = protocol::Array<int>::create(); + for (int i = 0; i < count; ++i) { + statsDiff->addItem(updateData[i].index); + statsDiff->addItem(updateData[i].count); + statsDiff->addItem(updateData[i].size); + } + m_frontend->heapStatsUpdate(std::move(statsDiff)); + return kContinue; + } + +private: + protocol::Frontend::HeapProfiler* m_frontend; +}; + +} // namespace + +V8HeapProfilerAgentImpl::V8HeapProfilerAgentImpl(V8InspectorSessionImpl* session) + : m_session(session) + , m_isolate(session->debugger()->isolate()) + , m_hasTimer(false) +{ +} + +V8HeapProfilerAgentImpl::~V8HeapProfilerAgentImpl() +{ +} + +void V8HeapProfilerAgentImpl::clearFrontend() +{ + ErrorString error; + disable(&error); + DCHECK(m_frontend); + m_frontend = nullptr; +} + +void V8HeapProfilerAgentImpl::restore() +{ + if (m_state->booleanProperty(HeapProfilerAgentState::heapProfilerEnabled, false)) + m_frontend->resetProfiles(); + if (m_state->booleanProperty(HeapProfilerAgentState::heapObjectsTrackingEnabled, false)) + startTrackingHeapObjectsInternal(m_state->booleanProperty(HeapProfilerAgentState::allocationTrackingEnabled, false)); +#if V8_MAJOR_VERSION >= 5 + if (m_state->booleanProperty(HeapProfilerAgentState::samplingHeapProfilerEnabled, false)) { + ErrorString error; + double samplingInterval = m_state->numberProperty(HeapProfilerAgentState::samplingHeapProfilerInterval, -1); + DCHECK_GE(samplingInterval, 0); + startSampling(&error, Maybe<double>(samplingInterval)); + } +#endif +} + +void V8HeapProfilerAgentImpl::collectGarbage(ErrorString*) +{ + m_isolate->LowMemoryNotification(); +} + +void V8HeapProfilerAgentImpl::startTrackingHeapObjects(ErrorString*, const protocol::Maybe<bool>& trackAllocations) +{ + m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled, true); + bool allocationTrackingEnabled = trackAllocations.fromMaybe(false); + m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled, allocationTrackingEnabled); + startTrackingHeapObjectsInternal(allocationTrackingEnabled); +} + +void V8HeapProfilerAgentImpl::stopTrackingHeapObjects(ErrorString* error, const protocol::Maybe<bool>& reportProgress) +{ + requestHeapStatsUpdate(); + takeHeapSnapshot(error, reportProgress); + stopTrackingHeapObjectsInternal(); +} + +void V8HeapProfilerAgentImpl::enable(ErrorString*) +{ + m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, true); +} + +void V8HeapProfilerAgentImpl::disable(ErrorString* error) +{ + stopTrackingHeapObjectsInternal(); +#if V8_MAJOR_VERSION >= 5 + if (m_state->booleanProperty(HeapProfilerAgentState::samplingHeapProfilerEnabled, false)) { + v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); + if (profiler) + profiler->StopSamplingHeapProfiler(); + } +#endif + m_isolate->GetHeapProfiler()->ClearObjectIds(); + m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, false); +} + +void V8HeapProfilerAgentImpl::takeHeapSnapshot(ErrorString* errorString, const protocol::Maybe<bool>& reportProgress) +{ + v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); + if (!profiler) { + *errorString = "Cannot access v8 heap profiler"; + return; + } + std::unique_ptr<HeapSnapshotProgress> progress; + if (reportProgress.fromMaybe(false)) + progress = wrapUnique(new HeapSnapshotProgress(m_frontend)); + + GlobalObjectNameResolver resolver(m_session); + const v8::HeapSnapshot* snapshot = profiler->TakeHeapSnapshot(progress.get(), &resolver); + if (!snapshot) { + *errorString = "Failed to take heap snapshot"; + return; + } + HeapSnapshotOutputStream stream(m_frontend); + snapshot->Serialize(&stream); + const_cast<v8::HeapSnapshot*>(snapshot)->Delete(); +} + +void V8HeapProfilerAgentImpl::getObjectByHeapObjectId(ErrorString* error, const String16& heapSnapshotObjectId, const protocol::Maybe<String16>& objectGroup, std::unique_ptr<protocol::Runtime::RemoteObject>* result) +{ + bool ok; + int id = heapSnapshotObjectId.toInt(&ok); + if (!ok) { + *error = "Invalid heap snapshot object id"; + return; + } + + v8::HandleScope handles(m_isolate); + v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id); + if (heapObject.IsEmpty()) { + *error = "Object is not available"; + return; + } + + if (!m_session->debugger()->client()->isInspectableHeapObject(heapObject)) { + *error = "Object is not available"; + return; + } + + *result = m_session->wrapObject(heapObject->CreationContext(), heapObject, objectGroup.fromMaybe("")); + if (!result) + *error = "Object is not available"; +} + +void V8HeapProfilerAgentImpl::addInspectedHeapObject(ErrorString* errorString, const String16& inspectedHeapObjectId) +{ + bool ok; + int id = inspectedHeapObjectId.toInt(&ok); + if (!ok) { + *errorString = "Invalid heap snapshot object id"; + return; + } + + v8::HandleScope handles(m_isolate); + v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id); + if (heapObject.IsEmpty()) { + *errorString = "Object is not available"; + return; + } + + if (!m_session->debugger()->client()->isInspectableHeapObject(heapObject)) { + *errorString = "Object is not available"; + return; + } + + m_session->addInspectedObject(wrapUnique(new InspectableHeapObject(id))); +} + +void V8HeapProfilerAgentImpl::getHeapObjectId(ErrorString* errorString, const String16& objectId, String16* heapSnapshotObjectId) +{ + v8::HandleScope handles(m_isolate); + v8::Local<v8::Value> value = m_session->findObject(errorString, objectId); + if (value.IsEmpty() || value->IsUndefined()) + return; + + v8::SnapshotObjectId id = m_isolate->GetHeapProfiler()->GetObjectId(value); + *heapSnapshotObjectId = String16::number(id); +} + +void V8HeapProfilerAgentImpl::requestHeapStatsUpdate() +{ + if (!m_frontend) + return; + HeapStatsStream stream(m_frontend); + v8::SnapshotObjectId lastSeenObjectId = m_isolate->GetHeapProfiler()->GetHeapStats(&stream); + m_frontend->lastSeenObjectId(lastSeenObjectId, m_session->debugger()->client()->currentTimeMS()); +} + +// static +void V8HeapProfilerAgentImpl::onTimer(void* data) +{ + reinterpret_cast<V8HeapProfilerAgentImpl*>(data)->requestHeapStatsUpdate(); +} + +void V8HeapProfilerAgentImpl::startTrackingHeapObjectsInternal(bool trackAllocations) +{ + m_isolate->GetHeapProfiler()->StartTrackingHeapObjects(trackAllocations); + if (!m_hasTimer) { + m_hasTimer = true; + m_session->debugger()->client()->startRepeatingTimer(0.05, &V8HeapProfilerAgentImpl::onTimer, reinterpret_cast<void*>(this)); + } +} + +void V8HeapProfilerAgentImpl::stopTrackingHeapObjectsInternal() +{ + if (m_hasTimer) { + m_session->debugger()->client()->cancelTimer(reinterpret_cast<void*>(this)); + m_hasTimer = false; + } + m_isolate->GetHeapProfiler()->StopTrackingHeapObjects(); + m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled, false); + m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled, false); +} + +void V8HeapProfilerAgentImpl::startSampling(ErrorString* errorString, const Maybe<double>& samplingInterval) +{ +#if V8_MAJOR_VERSION >= 5 + v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); + if (!profiler) { + *errorString = "Cannot access v8 heap profiler"; + return; + } + const unsigned defaultSamplingInterval = 1 << 15; + double samplingIntervalValue = samplingInterval.fromMaybe(defaultSamplingInterval); + m_state->setNumber(HeapProfilerAgentState::samplingHeapProfilerInterval, samplingIntervalValue); + m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled, true); +#if V8_MAJOR_VERSION * 1000 + V8_MINOR_VERSION >= 5002 + profiler->StartSamplingHeapProfiler(static_cast<uint64_t>(samplingIntervalValue), 128, v8::HeapProfiler::kSamplingForceGC); +#else + profiler->StartSamplingHeapProfiler(static_cast<uint64_t>(samplingIntervalValue), 128); +#endif +#endif +} + +#if V8_MAJOR_VERSION >= 5 +namespace { +std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode> buildSampingHeapProfileNode(const v8::AllocationProfile::Node* node) +{ + auto children = protocol::Array<protocol::HeapProfiler::SamplingHeapProfileNode>::create(); + for (const auto* child : node->children) + children->addItem(buildSampingHeapProfileNode(child)); + size_t selfSize = 0; + for (const auto& allocation : node->allocations) + selfSize += allocation.size * allocation.count; + std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode> result = protocol::HeapProfiler::SamplingHeapProfileNode::create() + .setFunctionName(toProtocolString(node->name)) + .setScriptId(String16::number(node->script_id)) + .setUrl(toProtocolString(node->script_name)) + .setLineNumber(node->line_number) + .setColumnNumber(node->column_number) + .setSelfSize(selfSize) + .setChildren(std::move(children)).build(); + return result; +} +} // namespace +#endif + +void V8HeapProfilerAgentImpl::stopSampling(ErrorString* errorString, std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>* profile) +{ +#if V8_MAJOR_VERSION >= 5 + v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); + if (!profiler) { + *errorString = "Cannot access v8 heap profiler"; + return; + } + v8::HandleScope scope(m_isolate); // Allocation profile contains Local handles. + std::unique_ptr<v8::AllocationProfile> v8Profile(profiler->GetAllocationProfile()); + profiler->StopSamplingHeapProfiler(); + m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled, false); + if (!v8Profile) { + *errorString = "Cannot access v8 sampled heap profile."; + return; + } + v8::AllocationProfile::Node* root = v8Profile->GetRootNode(); + *profile = protocol::HeapProfiler::SamplingHeapProfile::create() + .setHead(buildSampingHeapProfileNode(root)).build(); +#endif +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/V8HeapProfilerAgentImpl.h b/deps/v8_inspector/platform/v8_inspector/V8HeapProfilerAgentImpl.h new file mode 100644 index 00000000000000..082e533d603531 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8HeapProfilerAgentImpl.h @@ -0,0 +1,60 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8HeapProfilerAgentImpl_h +#define V8HeapProfilerAgentImpl_h + +#include "platform/inspector_protocol/Allocator.h" +#include "platform/v8_inspector/public/V8HeapProfilerAgent.h" + +namespace blink { + +class V8InspectorSessionImpl; + +using protocol::Maybe; + +class V8HeapProfilerAgentImpl : public V8HeapProfilerAgent { + PROTOCOL_DISALLOW_COPY(V8HeapProfilerAgentImpl); +public: + explicit V8HeapProfilerAgentImpl(V8InspectorSessionImpl*); + ~V8HeapProfilerAgentImpl() override; + + void setInspectorState(protocol::DictionaryValue* state) override { m_state = state; } + void setFrontend(protocol::Frontend::HeapProfiler* frontend) override { m_frontend = frontend; } + void clearFrontend() override; + void restore() override; + + void collectGarbage(ErrorString*) override; + + void enable(ErrorString*) override; + void startTrackingHeapObjects(ErrorString*, const Maybe<bool>& trackAllocations) override; + void stopTrackingHeapObjects(ErrorString*, const Maybe<bool>& reportProgress) override; + + void disable(ErrorString*) override; + + void takeHeapSnapshot(ErrorString*, const Maybe<bool>& reportProgress) override; + + void getObjectByHeapObjectId(ErrorString*, const String16& heapSnapshotObjectId, const Maybe<String16>& objectGroup, std::unique_ptr<protocol::Runtime::RemoteObject>* result) override; + void addInspectedHeapObject(ErrorString*, const String16& inspectedHeapObjectId) override; + void getHeapObjectId(ErrorString*, const String16& objectId, String16* heapSnapshotObjectId) override; + + void startSampling(ErrorString*, const Maybe<double>& samplingInterval) override; + void stopSampling(ErrorString*, std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>*) override; + +private: + void startTrackingHeapObjectsInternal(bool trackAllocations); + void stopTrackingHeapObjectsInternal(); + void requestHeapStatsUpdate(); + static void onTimer(void*); + + V8InspectorSessionImpl* m_session; + v8::Isolate* m_isolate; + protocol::Frontend::HeapProfiler* m_frontend; + protocol::DictionaryValue* m_state; + bool m_hasTimer; +}; + +} // namespace blink + +#endif // !defined(V8HeapProfilerAgentImpl_h) diff --git a/deps/v8_inspector/platform/v8_inspector/V8InjectedScriptHost.cpp b/deps/v8_inspector/platform/v8_inspector/V8InjectedScriptHost.cpp new file mode 100644 index 00000000000000..e25431f3cbc991 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8InjectedScriptHost.cpp @@ -0,0 +1,286 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/v8_inspector/V8InjectedScriptHost.h" + +#include "platform/inspector_protocol/String16.h" +#include "platform/v8_inspector/InjectedScriptNative.h" +#include "platform/v8_inspector/V8Compat.h" +#include "platform/v8_inspector/V8DebuggerImpl.h" +#include "platform/v8_inspector/V8StringUtil.h" +#include "platform/v8_inspector/public/V8DebuggerClient.h" +#include "platform/v8_inspector/public/V8EventListenerInfo.h" + +namespace blink { + +namespace { + +void setFunctionProperty(v8::Local<v8::Context> context, v8::Local<v8::Object> obj, const char* name, v8::FunctionCallback callback, v8::Local<v8::External> external) +{ + v8::Local<v8::String> funcName = toV8StringInternalized(context->GetIsolate(), name); + v8::Local<v8::Function> func; + if (!v8::Function::New(context, callback, external).ToLocal(&func)) + return; + func->SetName(funcName); + if (!obj->Set(context, funcName, func).FromMaybe(false)) + return; +} + +V8DebuggerImpl* unwrapDebugger(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + DCHECK(!info.Data().IsEmpty()); + DCHECK(info.Data()->IsExternal()); + V8DebuggerImpl* debugger = static_cast<V8DebuggerImpl*>(info.Data().As<v8::External>()->Value()); + DCHECK(debugger); + return debugger; +} + +} // namespace + +v8::Local<v8::Object> V8InjectedScriptHost::create(v8::Local<v8::Context> context, V8DebuggerImpl* debugger) +{ + v8::Isolate* isolate = debugger->isolate(); + v8::Local<v8::Object> injectedScriptHost = v8::Object::New(isolate); + v8::Local<v8::External> debuggerExternal = v8::External::New(isolate, debugger); + setFunctionProperty(context, injectedScriptHost, "internalConstructorName", V8InjectedScriptHost::internalConstructorNameCallback, debuggerExternal); + setFunctionProperty(context, injectedScriptHost, "formatAccessorsAsProperties", V8InjectedScriptHost::formatAccessorsAsProperties, debuggerExternal); + setFunctionProperty(context, injectedScriptHost, "isTypedArray", V8InjectedScriptHost::isTypedArrayCallback, debuggerExternal); + setFunctionProperty(context, injectedScriptHost, "subtype", V8InjectedScriptHost::subtypeCallback, debuggerExternal); + setFunctionProperty(context, injectedScriptHost, "collectionEntries", V8InjectedScriptHost::collectionEntriesCallback, debuggerExternal); + setFunctionProperty(context, injectedScriptHost, "getInternalProperties", V8InjectedScriptHost::getInternalPropertiesCallback, debuggerExternal); + setFunctionProperty(context, injectedScriptHost, "getEventListeners", V8InjectedScriptHost::getEventListenersCallback, debuggerExternal); + setFunctionProperty(context, injectedScriptHost, "suppressWarningsAndCallFunction", V8InjectedScriptHost::suppressWarningsAndCallFunctionCallback, debuggerExternal); + setFunctionProperty(context, injectedScriptHost, "setNonEnumProperty", V8InjectedScriptHost::setNonEnumPropertyCallback, debuggerExternal); + setFunctionProperty(context, injectedScriptHost, "bind", V8InjectedScriptHost::bindCallback, debuggerExternal); + setFunctionProperty(context, injectedScriptHost, "proxyTargetValue", V8InjectedScriptHost::proxyTargetValueCallback, debuggerExternal); + setFunctionProperty(context, injectedScriptHost, "prototype", V8InjectedScriptHost::prototypeCallback, debuggerExternal); + return injectedScriptHost; +} + +void V8InjectedScriptHost::internalConstructorNameCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + if (info.Length() < 1 || !info[0]->IsObject()) + return; + + v8::Local<v8::Object> object = info[0].As<v8::Object>(); + info.GetReturnValue().Set(object->GetConstructorName()); +} + +void V8InjectedScriptHost::formatAccessorsAsProperties(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + if (info.Length() < 1) + return; + + info.GetReturnValue().Set(unwrapDebugger(info)->client()->formatAccessorsAsProperties(info[0])); +} + +void V8InjectedScriptHost::isTypedArrayCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + if (info.Length() < 1) + return; + + info.GetReturnValue().Set(info[0]->IsTypedArray()); +} + +void V8InjectedScriptHost::subtypeCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + if (info.Length() < 1) + return; + + v8::Isolate* isolate = info.GetIsolate(); + v8::Local<v8::Value> value = info[0]; + if (value->IsArray() || value->IsTypedArray() || value->IsArgumentsObject()) { + info.GetReturnValue().Set(toV8StringInternalized(isolate, "array")); + return; + } + if (value->IsDate()) { + info.GetReturnValue().Set(toV8StringInternalized(isolate, "date")); + return; + } + if (value->IsRegExp()) { + info.GetReturnValue().Set(toV8StringInternalized(isolate, "regexp")); + return; + } + if (value->IsMap() || value->IsWeakMap()) { + info.GetReturnValue().Set(toV8StringInternalized(isolate, "map")); + return; + } + if (value->IsSet() || value->IsWeakSet()) { + info.GetReturnValue().Set(toV8StringInternalized(isolate, "set")); + return; + } + if (value->IsMapIterator() || value->IsSetIterator()) { + info.GetReturnValue().Set(toV8StringInternalized(isolate, "iterator")); + return; + } + if (value->IsGeneratorObject()) { + info.GetReturnValue().Set(toV8StringInternalized(isolate, "generator")); + return; + } + if (value->IsNativeError()) { + info.GetReturnValue().Set(toV8StringInternalized(isolate, "error")); + return; + } + if (value->IsProxy()) { + info.GetReturnValue().Set(toV8StringInternalized(isolate, "proxy")); + return; + } + String16 subtype = unwrapDebugger(info)->client()->valueSubtype(value); + if (!subtype.isEmpty()) { + info.GetReturnValue().Set(toV8String(isolate, subtype)); + return; + } +} + +void V8InjectedScriptHost::collectionEntriesCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + if (info.Length() < 1 || !info[0]->IsObject()) + return; + + v8::Local<v8::Object> object = info[0].As<v8::Object>(); + info.GetReturnValue().Set(unwrapDebugger(info)->collectionEntries(object)); +} + +void V8InjectedScriptHost::getInternalPropertiesCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + if (info.Length() < 1 || !info[0]->IsObject()) + return; + + v8::Local<v8::Object> object = info[0].As<v8::Object>(); + v8::Local<v8::Array> properties; + if (v8::Debug::GetInternalProperties(info.GetIsolate(), object).ToLocal(&properties)) + info.GetReturnValue().Set(properties); +} + +static v8::Local<v8::Array> wrapListenerFunctions(v8::Isolate* isolate, const V8EventListenerInfoList& listeners, const String16& type) +{ + v8::Local<v8::Array> result = v8::Array::New(isolate); + size_t handlersCount = listeners.size(); + for (size_t i = 0, outputIndex = 0; i < handlersCount; ++i) { + if (listeners[i].eventType != type) + continue; + v8::Local<v8::Object> function = listeners[i].handler; + v8::Local<v8::Object> listenerEntry = v8::Object::New(isolate); + listenerEntry->Set(toV8StringInternalized(isolate, "listener"), function); + listenerEntry->Set(toV8StringInternalized(isolate, "useCapture"), v8::Boolean::New(isolate, listeners[i].useCapture)); + listenerEntry->Set(toV8StringInternalized(isolate, "passive"), v8::Boolean::New(isolate, listeners[i].passive)); + result->Set(v8::Number::New(isolate, outputIndex++), listenerEntry); + } + return result; +} + +void V8InjectedScriptHost::getEventListenersCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + if (info.Length() < 1) + return; + + V8DebuggerClient* client = unwrapDebugger(info)->client(); + V8EventListenerInfoList listenerInfo; + // eventListeners call can produce message on ErrorEvent during lazy event listener compilation. + client->muteWarningsAndDeprecations(); + client->eventListeners(info[0], listenerInfo); + client->unmuteWarningsAndDeprecations(); + + v8::Local<v8::Object> result = v8::Object::New(info.GetIsolate()); + protocol::HashSet<String16> types; + for (auto& info : listenerInfo) + types.add(info.eventType); + for (const auto& it : types) { + v8::Local<v8::Array> listeners = wrapListenerFunctions(info.GetIsolate(), listenerInfo, it.first); + if (!listeners->Length()) + continue; + result->Set(toV8String(info.GetIsolate(), it.first), listeners); + } + info.GetReturnValue().Set(result); +} + +void V8InjectedScriptHost::suppressWarningsAndCallFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + if (info.Length() < 2 || info.Length() > 3 || !info[0]->IsFunction()) { + NOTREACHED(); + return; + } + if (info.Length() > 2 && (!info[2]->IsArray() && !info[2]->IsUndefined())) { + NOTREACHED(); + return; + } + + v8::Isolate* isolate = info.GetIsolate(); + v8::Local<v8::Context> context = isolate->GetCurrentContext(); + + v8::Local<v8::Function> function = info[0].As<v8::Function>(); + v8::Local<v8::Value> receiver = info[1]; + std::unique_ptr<v8::Local<v8::Value>[]> argv = nullptr; + size_t argc = 0; + + if (info.Length() > 2 && info[2]->IsArray()) { + v8::Local<v8::Array> arguments = info[2].As<v8::Array>(); + argc = arguments->Length(); + argv.reset(new v8::Local<v8::Value>[argc]); + for (size_t i = 0; i < argc; ++i) { + if (!arguments->Get(context, i).ToLocal(&argv[i])) + return; + } + } + + V8DebuggerClient* client = unwrapDebugger(info)->client(); + client->muteWarningsAndDeprecations(); + + v8::MicrotasksScope microtasks(isolate, v8::MicrotasksScope::kDoNotRunMicrotasks); + v8::Local<v8::Value> result; + if (function->Call(context, receiver, argc, argv.get()).ToLocal(&result)) + info.GetReturnValue().Set(result); + + client->unmuteWarningsAndDeprecations(); +} + +void V8InjectedScriptHost::setNonEnumPropertyCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + if (info.Length() < 3 || !info[0]->IsObject() || !info[1]->IsString()) + return; + + v8::Local<v8::Object> object = info[0].As<v8::Object>(); + v8::Maybe<bool> success = object->DefineOwnProperty(info.GetIsolate()->GetCurrentContext(), info[1].As<v8::String>(), info[2], v8::DontEnum); + USE(success); + DCHECK(!success.IsNothing()); +} + +void V8InjectedScriptHost::bindCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + if (info.Length() < 2 || !info[1]->IsString()) + return; + InjectedScriptNative* injectedScriptNative = InjectedScriptNative::fromInjectedScriptHost(info.Holder()); + if (!injectedScriptNative) + return; + + v8::Local<v8::String> v8groupName = info[1]->ToString(info.GetIsolate()); + String16 groupName = toProtocolStringWithTypeCheck(v8groupName); + int id = injectedScriptNative->bind(info[0], groupName); + info.GetReturnValue().Set(id); +} + +void V8InjectedScriptHost::proxyTargetValueCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + if (info.Length() != 1 || !info[0]->IsProxy()) { + NOTREACHED(); + return; + } + v8::Local<v8::Object> target = info[0].As<v8::Proxy>(); + while (target->IsProxy()) + target = v8::Local<v8::Proxy>::Cast(target)->GetTarget(); + info.GetReturnValue().Set(target); +} + +void V8InjectedScriptHost::prototypeCallback(const v8::FunctionCallbackInfo<v8::Value>& info) +{ + DCHECK(info.Length() > 0 && info[0]->IsObject()); + info.GetReturnValue().Set(info[0].As<v8::Object>()->GetPrototype()); +} + +v8::Local<v8::Private> V8Debugger::scopeExtensionPrivate(v8::Isolate* isolate) +{ + return v8::Private::ForApi(isolate, toV8StringInternalized(isolate, "V8Debugger#scopeExtension")); +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/V8InjectedScriptHost.h b/deps/v8_inspector/platform/v8_inspector/V8InjectedScriptHost.h new file mode 100644 index 00000000000000..a05dd29cee3c50 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8InjectedScriptHost.h @@ -0,0 +1,44 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8InjectedScriptHost_h +#define V8InjectedScriptHost_h + +#include <v8.h> + +namespace blink { + +class V8DebuggerImpl; + +// SECURITY NOTE: Although the InjectedScriptHost is intended for use solely by the inspector, +// a reference to the InjectedScriptHost may be leaked to the page being inspected. Thus, the +// InjectedScriptHost must never implemment methods that have more power over the page than the +// page already has itself (e.g. origin restriction bypasses). + +class V8InjectedScriptHost { +public: + // We expect that debugger outlives any JS context and thus V8InjectedScriptHost (owned by JS) + // is destroyed before debugger. + static v8::Local<v8::Object> create(v8::Local<v8::Context>, V8DebuggerImpl*); + +private: + static void internalConstructorNameCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void formatAccessorsAsProperties(const v8::FunctionCallbackInfo<v8::Value>&); + static void isTypedArrayCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void subtypeCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void generatorObjectDetailsCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void collectionEntriesCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void getInternalPropertiesCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void getEventListenersCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void suppressWarningsAndCallFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void setNonEnumPropertyCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void setFunctionVariableValueCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void bindCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void proxyTargetValueCallback(const v8::FunctionCallbackInfo<v8::Value>&); + static void prototypeCallback(const v8::FunctionCallbackInfo<v8::Value>&); +}; + +} // namespace blink + +#endif // V8InjectedScriptHost_h diff --git a/deps/v8_inspector/platform/v8_inspector/V8InspectorSessionImpl.cpp b/deps/v8_inspector/platform/v8_inspector/V8InspectorSessionImpl.cpp new file mode 100644 index 00000000000000..6453ce649ec3b3 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8InspectorSessionImpl.cpp @@ -0,0 +1,279 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/v8_inspector/V8InspectorSessionImpl.h" + +#include "platform/v8_inspector/InjectedScript.h" +#include "platform/v8_inspector/InspectedContext.h" +#include "platform/v8_inspector/RemoteObjectId.h" +#include "platform/v8_inspector/V8DebuggerAgentImpl.h" +#include "platform/v8_inspector/V8DebuggerImpl.h" +#include "platform/v8_inspector/V8HeapProfilerAgentImpl.h" +#include "platform/v8_inspector/V8ProfilerAgentImpl.h" +#include "platform/v8_inspector/V8RuntimeAgentImpl.h" +#include "platform/v8_inspector/public/V8ContextInfo.h" +#include "platform/v8_inspector/public/V8DebuggerClient.h" + +namespace blink { + +const char V8InspectorSession::backtraceObjectGroup[] = "backtrace"; + +std::unique_ptr<V8InspectorSessionImpl> V8InspectorSessionImpl::create(V8DebuggerImpl* debugger, int contextGroupId) +{ + return wrapUnique(new V8InspectorSessionImpl(debugger, contextGroupId)); +} + +V8InspectorSessionImpl::V8InspectorSessionImpl(V8DebuggerImpl* debugger, int contextGroupId) + : m_contextGroupId(contextGroupId) + , m_debugger(debugger) + , m_client(nullptr) + , m_customObjectFormatterEnabled(false) + , m_instrumentationCounter(0) + , m_runtimeAgent(wrapUnique(new V8RuntimeAgentImpl(this))) + , m_debuggerAgent(wrapUnique(new V8DebuggerAgentImpl(this))) + , m_heapProfilerAgent(wrapUnique(new V8HeapProfilerAgentImpl(this))) + , m_profilerAgent(wrapUnique(new V8ProfilerAgentImpl(this))) +{ +} + +V8InspectorSessionImpl::~V8InspectorSessionImpl() +{ + discardInjectedScripts(); + m_debugger->disconnect(this); +} + +V8DebuggerAgent* V8InspectorSessionImpl::debuggerAgent() +{ + return m_debuggerAgent.get(); +} + +V8HeapProfilerAgent* V8InspectorSessionImpl::heapProfilerAgent() +{ + return m_heapProfilerAgent.get(); +} + +V8ProfilerAgent* V8InspectorSessionImpl::profilerAgent() +{ + return m_profilerAgent.get(); +} + +V8RuntimeAgent* V8InspectorSessionImpl::runtimeAgent() +{ + return m_runtimeAgent.get(); +} + +void V8InspectorSessionImpl::setClient(V8InspectorSessionClient* client) +{ + m_client = client; +} + +void V8InspectorSessionImpl::reset() +{ + m_debuggerAgent->reset(); + m_runtimeAgent->reset(); + discardInjectedScripts(); +} + +void V8InspectorSessionImpl::discardInjectedScripts() +{ + m_inspectedObjects.clear(); + const V8DebuggerImpl::ContextByIdMap* contexts = m_debugger->contextGroup(m_contextGroupId); + if (!contexts) + return; + + protocol::Vector<int> keys; + for (auto& idContext : *contexts) + keys.append(idContext.first); + for (auto& key : keys) { + contexts = m_debugger->contextGroup(m_contextGroupId); + if (contexts && contexts->contains(key)) + contexts->get(key)->discardInjectedScript(); // This may destroy some contexts. + } +} + +InjectedScript* V8InspectorSessionImpl::findInjectedScript(ErrorString* errorString, int contextId) +{ + if (!contextId) { + *errorString = "Cannot find context with specified id"; + return nullptr; + } + + const V8DebuggerImpl::ContextByIdMap* contexts = m_debugger->contextGroup(m_contextGroupId); + if (!contexts || !contexts->contains(contextId)) { + *errorString = "Cannot find context with specified id"; + return nullptr; + } + + InspectedContext* context = contexts->get(contextId); + if (!context->getInjectedScript()) { + context->createInjectedScript(); + if (!context->getInjectedScript()) { + *errorString = "Cannot access specified execution context"; + return nullptr; + } + if (m_customObjectFormatterEnabled) + context->getInjectedScript()->setCustomObjectFormatterEnabled(true); + } + return context->getInjectedScript(); +} + +InjectedScript* V8InspectorSessionImpl::findInjectedScript(ErrorString* errorString, RemoteObjectIdBase* objectId) +{ + return objectId ? findInjectedScript(errorString, objectId->contextId()) : nullptr; +} + +void V8InspectorSessionImpl::releaseObjectGroup(const String16& objectGroup) +{ + const V8DebuggerImpl::ContextByIdMap* contexts = m_debugger->contextGroup(m_contextGroupId); + if (!contexts) + return; + + protocol::Vector<int> keys; + for (auto& idContext : *contexts) + keys.append(idContext.first); + for (auto& key : keys) { + contexts = m_debugger->contextGroup(m_contextGroupId); + if (contexts && contexts->contains(key)) { + InjectedScript* injectedScript = contexts->get(key)->getInjectedScript(); + if (injectedScript) + injectedScript->releaseObjectGroup(objectGroup); // This may destroy some contexts. + } + } +} + +v8::Local<v8::Value> V8InspectorSessionImpl::findObject(ErrorString* errorString, const String16& objectId, v8::Local<v8::Context>* context, String16* groupName) +{ + std::unique_ptr<RemoteObjectId> remoteId = RemoteObjectId::parse(errorString, objectId); + if (!remoteId) + return v8::Local<v8::Value>(); + InjectedScript* injectedScript = findInjectedScript(errorString, remoteId.get()); + if (!injectedScript) + return v8::Local<v8::Value>(); + v8::Local<v8::Value> objectValue; + injectedScript->findObject(errorString, *remoteId, &objectValue); + if (objectValue.IsEmpty()) + return v8::Local<v8::Value>(); + if (context) + *context = injectedScript->context()->context(); + if (groupName) + *groupName = injectedScript->objectGroupName(*remoteId); + return objectValue; +} + +std::unique_ptr<protocol::Runtime::RemoteObject> V8InspectorSessionImpl::wrapObject(v8::Local<v8::Context> context, v8::Local<v8::Value> value, const String16& groupName, bool generatePreview) +{ + ErrorString errorString; + InjectedScript* injectedScript = findInjectedScript(&errorString, V8Debugger::contextId(context)); + if (!injectedScript) + return nullptr; + return injectedScript->wrapObject(&errorString, value, groupName, false, generatePreview); +} + +std::unique_ptr<protocol::Runtime::RemoteObject> V8InspectorSessionImpl::wrapTable(v8::Local<v8::Context> context, v8::Local<v8::Value> table, v8::Local<v8::Value> columns) +{ + ErrorString errorString; + InjectedScript* injectedScript = findInjectedScript(&errorString, V8Debugger::contextId(context)); + if (!injectedScript) + return nullptr; + return injectedScript->wrapTable(table, columns); +} + +void V8InspectorSessionImpl::setCustomObjectFormatterEnabled(bool enabled) +{ + m_customObjectFormatterEnabled = enabled; + const V8DebuggerImpl::ContextByIdMap* contexts = m_debugger->contextGroup(m_contextGroupId); + if (!contexts) + return; + for (auto& idContext : *contexts) { + InjectedScript* injectedScript = idContext.second->getInjectedScript(); + if (injectedScript) + injectedScript->setCustomObjectFormatterEnabled(enabled); + } +} + +void V8InspectorSessionImpl::reportAllContexts(V8RuntimeAgentImpl* agent) +{ + const V8DebuggerImpl::ContextByIdMap* contexts = m_debugger->contextGroup(m_contextGroupId); + if (!contexts) + return; + for (auto& idContext : *contexts) + agent->reportExecutionContextCreated(idContext.second); +} + +void V8InspectorSessionImpl::changeInstrumentationCounter(int delta) +{ + DCHECK(m_instrumentationCounter + delta >= 0); + if (!m_instrumentationCounter && m_client) + m_client->startInstrumenting(); + m_instrumentationCounter += delta; + if (!m_instrumentationCounter && m_client) + m_client->stopInstrumenting(); +} + +void V8InspectorSessionImpl::addInspectedObject(std::unique_ptr<V8InspectorSession::Inspectable> inspectable) +{ + m_inspectedObjects.prepend(std::move(inspectable)); + while (m_inspectedObjects.size() > kInspectedObjectBufferSize) + m_inspectedObjects.removeLast(); +} + +V8InspectorSession::Inspectable* V8InspectorSessionImpl::inspectedObject(unsigned num) +{ + if (num >= m_inspectedObjects.size()) + return nullptr; + return m_inspectedObjects[num]; +} + +void V8InspectorSessionImpl::schedulePauseOnNextStatement(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data) +{ + m_debuggerAgent->schedulePauseOnNextStatement(breakReason, std::move(data)); +} + +void V8InspectorSessionImpl::cancelPauseOnNextStatement() +{ + m_debuggerAgent->cancelPauseOnNextStatement(); +} + +void V8InspectorSessionImpl::breakProgram(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data) +{ + m_debuggerAgent->breakProgram(breakReason, std::move(data)); +} + +void V8InspectorSessionImpl::breakProgramOnException(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data) +{ + m_debuggerAgent->breakProgramOnException(breakReason, std::move(data)); +} + +void V8InspectorSessionImpl::setSkipAllPauses(bool skip) +{ + ErrorString errorString; + m_debuggerAgent->setSkipAllPauses(&errorString, skip); +} + +void V8InspectorSessionImpl::asyncTaskScheduled(const String16& taskName, void* task, bool recurring) +{ + m_debuggerAgent->asyncTaskScheduled(taskName, task, recurring); +} + +void V8InspectorSessionImpl::asyncTaskCanceled(void* task) +{ + m_debuggerAgent->asyncTaskCanceled(task); +} + +void V8InspectorSessionImpl::asyncTaskStarted(void* task) +{ + m_debuggerAgent->asyncTaskStarted(task); +} + +void V8InspectorSessionImpl::asyncTaskFinished(void* task) +{ + m_debuggerAgent->asyncTaskFinished(task); +} + +void V8InspectorSessionImpl::allAsyncTasksCanceled() +{ + m_debuggerAgent->allAsyncTasksCanceled(); +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/V8InspectorSessionImpl.h b/deps/v8_inspector/platform/v8_inspector/V8InspectorSessionImpl.h new file mode 100644 index 00000000000000..ecc108760567a2 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8InspectorSessionImpl.h @@ -0,0 +1,93 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8InspectorSessionImpl_h +#define V8InspectorSessionImpl_h + +#include "platform/inspector_protocol/Allocator.h" +#include "platform/inspector_protocol/Collections.h" +#include "platform/inspector_protocol/String16.h" +#include "platform/inspector_protocol/TypeBuilder.h" +#include "platform/v8_inspector/public/V8InspectorSession.h" +#include "platform/v8_inspector/public/V8InspectorSessionClient.h" +#include "platform/v8_inspector/public/V8RuntimeAgent.h" +#include "wtf/PtrUtil.h" + +#include <v8.h> + +namespace blink { + +class InjectedScript; +class RemoteObjectIdBase; +class V8DebuggerAgentImpl; +class V8DebuggerImpl; +class V8HeapProfilerAgentImpl; +class V8ProfilerAgentImpl; +class V8RuntimeAgentImpl; + +class V8InspectorSessionImpl : public V8InspectorSession { + PROTOCOL_DISALLOW_COPY(V8InspectorSessionImpl); +public: + static std::unique_ptr<V8InspectorSessionImpl> create(V8DebuggerImpl*, int contextGroupId); + ~V8InspectorSessionImpl(); + + V8DebuggerImpl* debugger() const { return m_debugger; } + V8InspectorSessionClient* client() const { return m_client; } + V8DebuggerAgentImpl* debuggerAgentImpl() { return m_debuggerAgent.get(); } + V8ProfilerAgentImpl* profilerAgentImpl() { return m_profilerAgent.get(); } + V8RuntimeAgentImpl* runtimeAgentImpl() { return m_runtimeAgent.get(); } + int contextGroupId() const { return m_contextGroupId; } + + InjectedScript* findInjectedScript(ErrorString*, int contextId); + InjectedScript* findInjectedScript(ErrorString*, RemoteObjectIdBase*); + void reset(); + void discardInjectedScripts(); + void reportAllContexts(V8RuntimeAgentImpl*); + void setCustomObjectFormatterEnabled(bool); + void changeInstrumentationCounter(int delta); + + // V8InspectorSession implementation. + void setClient(V8InspectorSessionClient*) override; + void addInspectedObject(std::unique_ptr<V8InspectorSession::Inspectable>) override; + V8DebuggerAgent* debuggerAgent() override; + V8HeapProfilerAgent* heapProfilerAgent() override; + V8ProfilerAgent* profilerAgent() override; + V8RuntimeAgent* runtimeAgent() override; + void schedulePauseOnNextStatement(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data) override; + void cancelPauseOnNextStatement() override; + void breakProgram(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data) override; + void breakProgramOnException(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data) override; + void setSkipAllPauses(bool) override; + void asyncTaskScheduled(const String16& taskName, void* task, bool recurring) override; + void asyncTaskCanceled(void* task) override; + void asyncTaskStarted(void* task) override; + void asyncTaskFinished(void* task) override; + void allAsyncTasksCanceled() override; + void releaseObjectGroup(const String16& objectGroup) override; + v8::Local<v8::Value> findObject(ErrorString*, const String16& objectId, v8::Local<v8::Context>* = nullptr, String16* groupName = nullptr) override; + std::unique_ptr<protocol::Runtime::RemoteObject> wrapObject(v8::Local<v8::Context>, v8::Local<v8::Value>, const String16& groupName, bool generatePreview = false) override; + std::unique_ptr<protocol::Runtime::RemoteObject> wrapTable(v8::Local<v8::Context>, v8::Local<v8::Value> table, v8::Local<v8::Value> columns) override; + + V8InspectorSession::Inspectable* inspectedObject(unsigned num); + static const unsigned kInspectedObjectBufferSize = 5; + +private: + V8InspectorSessionImpl(V8DebuggerImpl*, int contextGroupId); + + int m_contextGroupId; + V8DebuggerImpl* m_debugger; + V8InspectorSessionClient* m_client; + bool m_customObjectFormatterEnabled; + int m_instrumentationCounter; + + std::unique_ptr<V8RuntimeAgentImpl> m_runtimeAgent; + std::unique_ptr<V8DebuggerAgentImpl> m_debuggerAgent; + std::unique_ptr<V8HeapProfilerAgentImpl> m_heapProfilerAgent; + std::unique_ptr<V8ProfilerAgentImpl> m_profilerAgent; + protocol::Vector<std::unique_ptr<V8InspectorSession::Inspectable>> m_inspectedObjects; +}; + +} // namespace blink + +#endif diff --git a/deps/v8_inspector/platform/v8_inspector/V8ProfilerAgentImpl.cpp b/deps/v8_inspector/platform/v8_inspector/V8ProfilerAgentImpl.cpp new file mode 100644 index 00000000000000..dd5c2e58259e53 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8ProfilerAgentImpl.cpp @@ -0,0 +1,302 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/v8_inspector/V8ProfilerAgentImpl.h" + +#include "platform/v8_inspector/Atomics.h" +#include "platform/v8_inspector/V8DebuggerImpl.h" +#include "platform/v8_inspector/V8InspectorSessionImpl.h" +#include "platform/v8_inspector/V8StackTraceImpl.h" +#include "platform/v8_inspector/V8StringUtil.h" +#include <v8-profiler.h> + +namespace blink { + +namespace ProfilerAgentState { +static const char samplingInterval[] = "samplingInterval"; +static const char userInitiatedProfiling[] = "userInitiatedProfiling"; +static const char profilerEnabled[] = "profilerEnabled"; +} + +namespace { + +std::unique_ptr<protocol::Array<protocol::Profiler::PositionTickInfo>> buildInspectorObjectForPositionTicks(const v8::CpuProfileNode* node) +{ + std::unique_ptr<protocol::Array<protocol::Profiler::PositionTickInfo>> array = protocol::Array<protocol::Profiler::PositionTickInfo>::create(); + unsigned lineCount = node->GetHitLineCount(); + if (!lineCount) + return array; + + protocol::Vector<v8::CpuProfileNode::LineTick> entries(lineCount); + if (node->GetLineTicks(&entries[0], lineCount)) { + for (unsigned i = 0; i < lineCount; i++) { + std::unique_ptr<protocol::Profiler::PositionTickInfo> line = protocol::Profiler::PositionTickInfo::create() + .setLine(entries[i].line) + .setTicks(entries[i].hit_count).build(); + array->addItem(std::move(line)); + } + } + + return array; +} + +std::unique_ptr<protocol::Profiler::CPUProfileNode> buildInspectorObjectFor(v8::Isolate* isolate, const v8::CpuProfileNode* node) +{ + v8::HandleScope handleScope(isolate); + + std::unique_ptr<protocol::Array<protocol::Profiler::CPUProfileNode>> children = protocol::Array<protocol::Profiler::CPUProfileNode>::create(); + const int childrenCount = node->GetChildrenCount(); + for (int i = 0; i < childrenCount; i++) { + const v8::CpuProfileNode* child = node->GetChild(i); + children->addItem(buildInspectorObjectFor(isolate, child)); + } + + std::unique_ptr<protocol::Array<protocol::Profiler::PositionTickInfo>> positionTicks = buildInspectorObjectForPositionTicks(node); + + std::unique_ptr<protocol::Profiler::CPUProfileNode> result = protocol::Profiler::CPUProfileNode::create() + .setFunctionName(toProtocolString(node->GetFunctionName())) + .setScriptId(String16::number(node->GetScriptId())) + .setUrl(toProtocolString(node->GetScriptResourceName())) + .setLineNumber(node->GetLineNumber()) + .setColumnNumber(node->GetColumnNumber()) + .setHitCount(node->GetHitCount()) + .setCallUID(node->GetCallUid()) + .setChildren(std::move(children)) + .setPositionTicks(std::move(positionTicks)) + .setDeoptReason(node->GetBailoutReason()) + .setId(node->GetNodeId()).build(); + return result; +} + +std::unique_ptr<protocol::Array<int>> buildInspectorObjectForSamples(v8::CpuProfile* v8profile) +{ + std::unique_ptr<protocol::Array<int>> array = protocol::Array<int>::create(); + int count = v8profile->GetSamplesCount(); + for (int i = 0; i < count; i++) + array->addItem(v8profile->GetSample(i)->GetNodeId()); + return array; +} + +std::unique_ptr<protocol::Array<double>> buildInspectorObjectForTimestamps(v8::CpuProfile* v8profile) +{ + std::unique_ptr<protocol::Array<double>> array = protocol::Array<double>::create(); + int count = v8profile->GetSamplesCount(); + for (int i = 0; i < count; i++) + array->addItem(v8profile->GetSampleTimestamp(i)); + return array; +} + +std::unique_ptr<protocol::Profiler::CPUProfile> createCPUProfile(v8::Isolate* isolate, v8::CpuProfile* v8profile) +{ + std::unique_ptr<protocol::Profiler::CPUProfile> profile = protocol::Profiler::CPUProfile::create() + .setHead(buildInspectorObjectFor(isolate, v8profile->GetTopDownRoot())) + .setStartTime(static_cast<double>(v8profile->GetStartTime()) / 1000000) + .setEndTime(static_cast<double>(v8profile->GetEndTime()) / 1000000).build(); + profile->setSamples(buildInspectorObjectForSamples(v8profile)); + profile->setTimestamps(buildInspectorObjectForTimestamps(v8profile)); + return profile; +} + +std::unique_ptr<protocol::Debugger::Location> currentDebugLocation(V8DebuggerImpl* debugger) +{ + std::unique_ptr<V8StackTrace> callStack = debugger->captureStackTrace(1); + std::unique_ptr<protocol::Debugger::Location> location = protocol::Debugger::Location::create() + .setScriptId(callStack->topScriptId()) + .setLineNumber(callStack->topLineNumber()).build(); + location->setColumnNumber(callStack->topColumnNumber()); + return location; +} + +volatile int s_lastProfileId = 0; + +} // namespace + +class V8ProfilerAgentImpl::ProfileDescriptor { +public: + ProfileDescriptor(const String16& id, const String16& title) + : m_id(id) + , m_title(title) { } + String16 m_id; + String16 m_title; +}; + +V8ProfilerAgentImpl::V8ProfilerAgentImpl(V8InspectorSessionImpl* session) + : m_session(session) + , m_isolate(m_session->debugger()->isolate()) + , m_state(nullptr) + , m_frontend(nullptr) + , m_enabled(false) + , m_recordingCPUProfile(false) +{ +} + +V8ProfilerAgentImpl::~V8ProfilerAgentImpl() +{ +} + +void V8ProfilerAgentImpl::consoleProfile(const String16& title) +{ + if (!m_enabled) + return; + DCHECK(m_frontend); + String16 id = nextProfileId(); + m_startedProfiles.append(ProfileDescriptor(id, title)); + startProfiling(id); + m_frontend->consoleProfileStarted(id, currentDebugLocation(m_session->debugger()), title); +} + +void V8ProfilerAgentImpl::consoleProfileEnd(const String16& title) +{ + if (!m_enabled) + return; + DCHECK(m_frontend); + String16 id; + String16 resolvedTitle; + // Take last started profile if no title was passed. + if (title.isEmpty()) { + if (m_startedProfiles.isEmpty()) + return; + id = m_startedProfiles.last().m_id; + resolvedTitle = m_startedProfiles.last().m_title; + m_startedProfiles.removeLast(); + } else { + for (size_t i = 0; i < m_startedProfiles.size(); i++) { + if (m_startedProfiles[i].m_title == title) { + resolvedTitle = title; + id = m_startedProfiles[i].m_id; + m_startedProfiles.remove(i); + break; + } + } + if (id.isEmpty()) + return; + } + std::unique_ptr<protocol::Profiler::CPUProfile> profile = stopProfiling(id, true); + if (!profile) + return; + std::unique_ptr<protocol::Debugger::Location> location = currentDebugLocation(m_session->debugger()); + m_frontend->consoleProfileFinished(id, std::move(location), std::move(profile), resolvedTitle); +} + +void V8ProfilerAgentImpl::enable(ErrorString*) +{ + if (m_enabled) + return; + m_enabled = true; + m_state->setBoolean(ProfilerAgentState::profilerEnabled, true); + m_session->changeInstrumentationCounter(+1); +} + +void V8ProfilerAgentImpl::disable(ErrorString* errorString) +{ + if (!m_enabled) + return; + m_session->changeInstrumentationCounter(-1); + for (size_t i = m_startedProfiles.size(); i > 0; --i) + stopProfiling(m_startedProfiles[i - 1].m_id, false); + m_startedProfiles.clear(); + stop(nullptr, nullptr); + m_enabled = false; + m_state->setBoolean(ProfilerAgentState::profilerEnabled, false); +} + +void V8ProfilerAgentImpl::setSamplingInterval(ErrorString* error, int interval) +{ + if (m_recordingCPUProfile) { + *error = "Cannot change sampling interval when profiling."; + return; + } + m_state->setNumber(ProfilerAgentState::samplingInterval, interval); + m_isolate->GetCpuProfiler()->SetSamplingInterval(interval); +} + +void V8ProfilerAgentImpl::clearFrontend() +{ + ErrorString error; + disable(&error); + DCHECK(m_frontend); + m_frontend = nullptr; +} + +void V8ProfilerAgentImpl::restore() +{ + DCHECK(!m_enabled); + if (!m_state->booleanProperty(ProfilerAgentState::profilerEnabled, false)) + return; + m_enabled = true; + m_session->changeInstrumentationCounter(+1); + int interval = 0; + m_state->getNumber(ProfilerAgentState::samplingInterval, &interval); + if (interval) + m_isolate->GetCpuProfiler()->SetSamplingInterval(interval); + if (m_state->booleanProperty(ProfilerAgentState::userInitiatedProfiling, false)) { + ErrorString error; + start(&error); + } +} + +void V8ProfilerAgentImpl::start(ErrorString* error) +{ + if (m_recordingCPUProfile) + return; + if (!m_enabled) { + *error = "Profiler is not enabled"; + return; + } + m_recordingCPUProfile = true; + m_frontendInitiatedProfileId = nextProfileId(); + startProfiling(m_frontendInitiatedProfileId); + m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, true); + m_session->client()->profilingStarted(); +} + +void V8ProfilerAgentImpl::stop(ErrorString* errorString, std::unique_ptr<protocol::Profiler::CPUProfile>* profile) +{ + if (!m_recordingCPUProfile) { + if (errorString) + *errorString = "No recording profiles found"; + return; + } + m_recordingCPUProfile = false; + std::unique_ptr<protocol::Profiler::CPUProfile> cpuProfile = stopProfiling(m_frontendInitiatedProfileId, !!profile); + if (profile) { + *profile = std::move(cpuProfile); + if (!profile->get() && errorString) + *errorString = "Profile is not found"; + } + m_frontendInitiatedProfileId = String16(); + m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, false); + m_session->client()->profilingStopped(); +} + +String16 V8ProfilerAgentImpl::nextProfileId() +{ + return String16::number(atomicIncrement(&s_lastProfileId)); +} + +void V8ProfilerAgentImpl::startProfiling(const String16& title) +{ + v8::HandleScope handleScope(m_isolate); + m_isolate->GetCpuProfiler()->StartProfiling(toV8String(m_isolate, title), true); +} + +std::unique_ptr<protocol::Profiler::CPUProfile> V8ProfilerAgentImpl::stopProfiling(const String16& title, bool serialize) +{ + v8::HandleScope handleScope(m_isolate); + v8::CpuProfile* profile = m_isolate->GetCpuProfiler()->StopProfiling(toV8String(m_isolate, title)); + if (!profile) + return nullptr; + std::unique_ptr<protocol::Profiler::CPUProfile> result; + if (serialize) + result = createCPUProfile(m_isolate, profile); + profile->Delete(); + return result; +} + +bool V8ProfilerAgentImpl::isRecording() const +{ + return m_recordingCPUProfile || !m_startedProfiles.isEmpty(); +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/V8ProfilerAgentImpl.h b/deps/v8_inspector/platform/v8_inspector/V8ProfilerAgentImpl.h new file mode 100644 index 00000000000000..76bbfd4306c064 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8ProfilerAgentImpl.h @@ -0,0 +1,65 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8ProfilerAgentImpl_h +#define V8ProfilerAgentImpl_h + +#include "platform/inspector_protocol/Allocator.h" +#include "platform/inspector_protocol/Frontend.h" +#include "platform/inspector_protocol/String16.h" +#include "platform/v8_inspector/public/V8ProfilerAgent.h" + +namespace v8 { +class Isolate; +} + +namespace blink { + +class V8InspectorSessionImpl; + +class V8ProfilerAgentImpl : public V8ProfilerAgent { + PROTOCOL_DISALLOW_COPY(V8ProfilerAgentImpl); +public: + explicit V8ProfilerAgentImpl(V8InspectorSessionImpl*); + ~V8ProfilerAgentImpl() override; + + bool enabled() const { return m_enabled; } + + void setInspectorState(protocol::DictionaryValue* state) override { m_state = state; } + void setFrontend(protocol::Frontend::Profiler* frontend) override { m_frontend = frontend; } + void clearFrontend() override; + void restore() override; + + void enable(ErrorString*) override; + void disable(ErrorString*) override; + void setSamplingInterval(ErrorString*, int) override; + void start(ErrorString*) override; + void stop(ErrorString*, std::unique_ptr<protocol::Profiler::CPUProfile>*) override; + + void consoleProfile(const String16& title); + void consoleProfileEnd(const String16& title); + +private: + String16 nextProfileId(); + + void startProfiling(const String16& title); + std::unique_ptr<protocol::Profiler::CPUProfile> stopProfiling(const String16& title, bool serialize); + + bool isRecording() const; + + V8InspectorSessionImpl* m_session; + v8::Isolate* m_isolate; + protocol::DictionaryValue* m_state; + protocol::Frontend::Profiler* m_frontend; + bool m_enabled; + bool m_recordingCPUProfile; + class ProfileDescriptor; + protocol::Vector<ProfileDescriptor> m_startedProfiles; + String16 m_frontendInitiatedProfileId; +}; + +} // namespace blink + + +#endif // !defined(V8ProfilerAgentImpl_h) diff --git a/deps/v8_inspector/platform/v8_inspector/V8Regex.cpp b/deps/v8_inspector/platform/v8_inspector/V8Regex.cpp new file mode 100644 index 00000000000000..d29c916aa2c842 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8Regex.cpp @@ -0,0 +1,93 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/v8_inspector/V8Regex.h" + +#include "platform/inspector_protocol/Collections.h" +#include "platform/v8_inspector/V8Compat.h" +#include "platform/v8_inspector/V8DebuggerImpl.h" +#include "platform/v8_inspector/V8StringUtil.h" +#include "platform/v8_inspector/public/V8DebuggerClient.h" + +#include <limits.h> + +namespace blink { + +V8Regex::V8Regex(V8DebuggerImpl* debugger, const String16& pattern, bool caseSensitive, bool multiline) + : m_debugger(debugger) +{ + v8::Isolate* isolate = m_debugger->isolate(); + v8::HandleScope handleScope(isolate); + v8::Local<v8::Context> context = m_debugger->regexContext(); + v8::Context::Scope contextScope(context); + v8::TryCatch tryCatch(isolate); + + unsigned flags = v8::RegExp::kNone; + if (!caseSensitive) + flags |= v8::RegExp::kIgnoreCase; + if (multiline) + flags |= v8::RegExp::kMultiline; + + v8::Local<v8::RegExp> regex; + if (v8::RegExp::New(context, toV8String(isolate, pattern), static_cast<v8::RegExp::Flags>(flags)).ToLocal(®ex)) + m_regex.Reset(isolate, regex); + else if (tryCatch.HasCaught()) + m_errorMessage = toProtocolString(tryCatch.Message()->Get()); + else + m_errorMessage = "Internal error"; +} + +int V8Regex::match(const String16& string, int startFrom, int* matchLength) const +{ + if (matchLength) + *matchLength = 0; + + if (m_regex.IsEmpty() || string.isEmpty()) + return -1; + + // v8 strings are limited to int. + if (string.length() > INT_MAX) + return -1; + + v8::Isolate* isolate = m_debugger->isolate(); + v8::HandleScope handleScope(isolate); + v8::Local<v8::Context> context = m_debugger->regexContext(); + v8::MicrotasksScope microtasks(isolate, v8::MicrotasksScope::kDoNotRunMicrotasks); + v8::TryCatch tryCatch(isolate); + + v8::Local<v8::RegExp> regex = m_regex.Get(isolate); + v8::Local<v8::Value> exec; + if (!regex->Get(context, toV8StringInternalized(isolate, "exec")).ToLocal(&exec)) + return -1; + v8::Local<v8::Value> argv[] = { toV8String(isolate, string.substring(startFrom)) }; + v8::Local<v8::Value> returnValue; + if (!exec.As<v8::Function>()->Call(context, regex, PROTOCOL_ARRAY_LENGTH(argv), argv).ToLocal(&returnValue)) + return -1; + + // RegExp#exec returns null if there's no match, otherwise it returns an + // Array of strings with the first being the whole match string and others + // being subgroups. The Array also has some random properties tacked on like + // "index" which is the offset of the match. + // + // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/exec + + DCHECK(!returnValue.IsEmpty()); + if (!returnValue->IsArray()) + return -1; + + v8::Local<v8::Array> result = returnValue.As<v8::Array>(); + v8::Local<v8::Value> matchOffset; + if (!result->Get(context, toV8StringInternalized(isolate, "index")).ToLocal(&matchOffset)) + return -1; + if (matchLength) { + v8::Local<v8::Value> match; + if (!result->Get(context, 0).ToLocal(&match)) + return -1; + *matchLength = match.As<v8::String>()->Length(); + } + + return matchOffset.As<v8::Int32>()->Value() + startFrom; +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/V8Regex.h b/deps/v8_inspector/platform/v8_inspector/V8Regex.h new file mode 100644 index 00000000000000..983717d971a0bc --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8Regex.h @@ -0,0 +1,37 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8Regex_h +#define V8Regex_h + +#include "platform/inspector_protocol/Allocator.h" +#include "platform/inspector_protocol/String16.h" +#include <v8.h> + +namespace blink { + +class V8DebuggerImpl; + +enum MultilineMode { + MultilineDisabled, + MultilineEnabled +}; + +class V8Regex { + PROTOCOL_DISALLOW_COPY(V8Regex); +public: + V8Regex(V8DebuggerImpl*, const String16&, bool caseSensitive, bool multiline = false); + int match(const String16&, int startFrom = 0, int* matchLength = 0) const; + bool isValid() const { return !m_regex.IsEmpty(); } + const String16& errorMessage() const { return m_errorMessage; } + +private: + V8DebuggerImpl* m_debugger; + v8::Global<v8::RegExp> m_regex; + String16 m_errorMessage; +}; + +} // namespace blink + +#endif // V8Regex_h diff --git a/deps/v8_inspector/platform/v8_inspector/V8RuntimeAgentImpl.cpp b/deps/v8_inspector/platform/v8_inspector/V8RuntimeAgentImpl.cpp new file mode 100644 index 00000000000000..6a2611f87e3a65 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8RuntimeAgentImpl.cpp @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "platform/v8_inspector/V8RuntimeAgentImpl.h" + +#include "platform/inspector_protocol/Values.h" +#include "platform/v8_inspector/InjectedScript.h" +#include "platform/v8_inspector/InspectedContext.h" +#include "platform/v8_inspector/RemoteObjectId.h" +#include "platform/v8_inspector/V8DebuggerImpl.h" +#include "platform/v8_inspector/V8InspectorSessionImpl.h" +#include "platform/v8_inspector/V8StringUtil.h" +#include "platform/v8_inspector/public/V8DebuggerClient.h" + +namespace blink { + +namespace V8RuntimeAgentImplState { +static const char customObjectFormatterEnabled[] = "customObjectFormatterEnabled"; +static const char runtimeEnabled[] = "runtimeEnabled"; +}; + +using protocol::Runtime::ExceptionDetails; +using protocol::Runtime::RemoteObject; + +static bool hasInternalError(ErrorString* errorString, bool hasError) +{ + if (hasError) + *errorString = "Internal error"; + return hasError; +} + +V8RuntimeAgentImpl::V8RuntimeAgentImpl(V8InspectorSessionImpl* session) + : m_session(session) + , m_state(nullptr) + , m_frontend(nullptr) + , m_debugger(session->debugger()) + , m_enabled(false) +{ +} + +V8RuntimeAgentImpl::~V8RuntimeAgentImpl() +{ +} + +void V8RuntimeAgentImpl::evaluate( + ErrorString* errorString, + const String16& expression, + const Maybe<String16>& objectGroup, + const Maybe<bool>& includeCommandLineAPI, + const Maybe<bool>& doNotPauseOnExceptionsAndMuteConsole, + const Maybe<int>& executionContextId, + const Maybe<bool>& returnByValue, + const Maybe<bool>& generatePreview, + const Maybe<bool>& userGesture, + std::unique_ptr<RemoteObject>* result, + Maybe<bool>* wasThrown, + Maybe<ExceptionDetails>* exceptionDetails) +{ + int contextId; + if (executionContextId.isJust()) { + contextId = executionContextId.fromJust(); + } else { + contextId = m_debugger->client()->ensureDefaultContextInGroup(m_session->contextGroupId()); + if (!contextId) { + *errorString = "Cannot find default execution context"; + return; + } + } + + InjectedScript::ContextScope scope(errorString, m_debugger, m_session->contextGroupId(), contextId); + if (!scope.initialize()) + return; + + if (doNotPauseOnExceptionsAndMuteConsole.fromMaybe(false)) + scope.ignoreExceptionsAndMuteConsole(); + if (userGesture.fromMaybe(false)) + scope.pretendUserGesture(); + + if (includeCommandLineAPI.fromMaybe(false) && !scope.installCommandLineAPI()) + return; + + bool evalIsDisabled = !scope.context()->IsCodeGenerationFromStringsAllowed(); + // Temporarily enable allow evals for inspector. + if (evalIsDisabled) + scope.context()->AllowCodeGenerationFromStrings(true); + + v8::MaybeLocal<v8::Value> maybeResultValue; + v8::Local<v8::Script> script = m_debugger->compileInternalScript(scope.context(), toV8String(m_debugger->isolate(), expression), String16()); + if (!script.IsEmpty()) + maybeResultValue = m_debugger->runCompiledScript(scope.context(), script); + + if (evalIsDisabled) + scope.context()->AllowCodeGenerationFromStrings(false); + + // Re-initialize after running client's code, as it could have destroyed context or session. + if (!scope.initialize()) + return; + scope.injectedScript()->wrapEvaluateResult(errorString, + maybeResultValue, + scope.tryCatch(), + objectGroup.fromMaybe(""), + returnByValue.fromMaybe(false), + generatePreview.fromMaybe(false), + result, + wasThrown, + exceptionDetails); +} + +void V8RuntimeAgentImpl::callFunctionOn(ErrorString* errorString, + const String16& objectId, + const String16& expression, + const Maybe<protocol::Array<protocol::Runtime::CallArgument>>& optionalArguments, + const Maybe<bool>& doNotPauseOnExceptionsAndMuteConsole, + const Maybe<bool>& returnByValue, + const Maybe<bool>& generatePreview, + const Maybe<bool>& userGesture, + std::unique_ptr<RemoteObject>* result, + Maybe<bool>* wasThrown) +{ + InjectedScript::ObjectScope scope(errorString, m_debugger, m_session->contextGroupId(), objectId); + if (!scope.initialize()) + return; + + std::unique_ptr<v8::Local<v8::Value>[]> argv = nullptr; + int argc = 0; + if (optionalArguments.isJust()) { + protocol::Array<protocol::Runtime::CallArgument>* arguments = optionalArguments.fromJust(); + argc = arguments->length(); + argv.reset(new v8::Local<v8::Value>[argc]); + for (int i = 0; i < argc; ++i) { + v8::Local<v8::Value> argumentValue; + if (!scope.injectedScript()->resolveCallArgument(errorString, arguments->get(i)).ToLocal(&argumentValue)) + return; + argv[i] = argumentValue; + } + } + + if (doNotPauseOnExceptionsAndMuteConsole.fromMaybe(false)) + scope.ignoreExceptionsAndMuteConsole(); + if (userGesture.fromMaybe(false)) + scope.pretendUserGesture(); + + v8::MaybeLocal<v8::Value> maybeFunctionValue = m_debugger->compileAndRunInternalScript(scope.context(), toV8String(m_debugger->isolate(), "(" + expression + ")")); + // Re-initialize after running client's code, as it could have destroyed context or session. + if (!scope.initialize()) + return; + + if (scope.tryCatch().HasCaught()) { + scope.injectedScript()->wrapEvaluateResult(errorString, maybeFunctionValue, scope.tryCatch(), scope.objectGroupName(), false, false, result, wasThrown, nullptr); + return; + } + + v8::Local<v8::Value> functionValue; + if (!maybeFunctionValue.ToLocal(&functionValue) || !functionValue->IsFunction()) { + *errorString = "Given expression does not evaluate to a function"; + return; + } + + v8::MaybeLocal<v8::Value> maybeResultValue = m_debugger->callFunction(functionValue.As<v8::Function>(), scope.context(), scope.object(), argc, argv.get()); + // Re-initialize after running client's code, as it could have destroyed context or session. + if (!scope.initialize()) + return; + + scope.injectedScript()->wrapEvaluateResult(errorString, maybeResultValue, scope.tryCatch(), scope.objectGroupName(), returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), result, wasThrown, nullptr); +} + +void V8RuntimeAgentImpl::getProperties( + ErrorString* errorString, + const String16& objectId, + const Maybe<bool>& ownProperties, + const Maybe<bool>& accessorPropertiesOnly, + const Maybe<bool>& generatePreview, + std::unique_ptr<protocol::Array<protocol::Runtime::PropertyDescriptor>>* result, + Maybe<protocol::Array<protocol::Runtime::InternalPropertyDescriptor>>* internalProperties, + Maybe<ExceptionDetails>* exceptionDetails) +{ + using protocol::Runtime::InternalPropertyDescriptor; + + InjectedScript::ObjectScope scope(errorString, m_debugger, m_session->contextGroupId(), objectId); + if (!scope.initialize()) + return; + + scope.ignoreExceptionsAndMuteConsole(); + if (!scope.object()->IsObject()) { + *errorString = "Value with given id is not an object"; + return; + } + + v8::Local<v8::Object> object = scope.object().As<v8::Object>(); + scope.injectedScript()->getProperties(errorString, object, scope.objectGroupName(), ownProperties.fromMaybe(false), accessorPropertiesOnly.fromMaybe(false), generatePreview.fromMaybe(false), result, exceptionDetails); + if (!errorString->isEmpty() || exceptionDetails->isJust() || accessorPropertiesOnly.fromMaybe(false)) + return; + v8::Local<v8::Array> propertiesArray; + if (hasInternalError(errorString, !v8::Debug::GetInternalProperties(m_debugger->isolate(), scope.object()).ToLocal(&propertiesArray))) + return; + std::unique_ptr<protocol::Array<InternalPropertyDescriptor>> propertiesProtocolArray = protocol::Array<InternalPropertyDescriptor>::create(); + for (uint32_t i = 0; i < propertiesArray->Length(); i += 2) { + v8::Local<v8::Value> name; + if (hasInternalError(errorString, !propertiesArray->Get(scope.context(), i).ToLocal(&name)) || !name->IsString()) + return; + v8::Local<v8::Value> value; + if (hasInternalError(errorString, !propertiesArray->Get(scope.context(), i + 1).ToLocal(&value))) + return; + std::unique_ptr<RemoteObject> wrappedValue = scope.injectedScript()->wrapObject(errorString, value, scope.objectGroupName()); + if (!wrappedValue) + return; + propertiesProtocolArray->addItem(InternalPropertyDescriptor::create() + .setName(toProtocolString(name.As<v8::String>())) + .setValue(std::move(wrappedValue)).build()); + } + if (!propertiesProtocolArray->length()) + return; + *internalProperties = std::move(propertiesProtocolArray); +} + +void V8RuntimeAgentImpl::releaseObject(ErrorString* errorString, const String16& objectId) +{ + InjectedScript::ObjectScope scope(errorString, m_debugger, m_session->contextGroupId(), objectId); + if (!scope.initialize()) + return; + scope.injectedScript()->releaseObject(objectId); +} + +void V8RuntimeAgentImpl::releaseObjectGroup(ErrorString*, const String16& objectGroup) +{ + m_session->releaseObjectGroup(objectGroup); +} + +void V8RuntimeAgentImpl::run(ErrorString* errorString) +{ + m_session->client()->resumeStartup(); +} + +void V8RuntimeAgentImpl::setCustomObjectFormatterEnabled(ErrorString*, bool enabled) +{ + m_state->setBoolean(V8RuntimeAgentImplState::customObjectFormatterEnabled, enabled); + m_session->setCustomObjectFormatterEnabled(enabled); +} + +void V8RuntimeAgentImpl::compileScript(ErrorString* errorString, + const String16& expression, + const String16& sourceURL, + bool persistScript, + int executionContextId, + Maybe<String16>* scriptId, + Maybe<ExceptionDetails>* exceptionDetails) +{ + if (!m_enabled) { + *errorString = "Runtime agent is not enabled"; + return; + } + InjectedScript::ContextScope scope(errorString, m_debugger, m_session->contextGroupId(), executionContextId); + if (!scope.initialize()) + return; + + v8::Local<v8::Script> script = m_debugger->compileInternalScript(scope.context(), toV8String(m_debugger->isolate(), expression), sourceURL); + if (script.IsEmpty()) { + v8::Local<v8::Message> message = scope.tryCatch().Message(); + if (!message.IsEmpty()) + *exceptionDetails = scope.injectedScript()->createExceptionDetails(message); + else + *errorString = "Script compilation failed"; + return; + } + + if (!persistScript) + return; + + String16 scriptValueId = String16::number(script->GetUnboundScript()->GetId()); + std::unique_ptr<v8::Global<v8::Script>> global(new v8::Global<v8::Script>(m_debugger->isolate(), script)); + m_compiledScripts.set(scriptValueId, std::move(global)); + *scriptId = scriptValueId; +} + +void V8RuntimeAgentImpl::runScript(ErrorString* errorString, + const String16& scriptId, + int executionContextId, + const Maybe<String16>& objectGroup, + const Maybe<bool>& doNotPauseOnExceptionsAndMuteConsole, + const Maybe<bool>& includeCommandLineAPI, + std::unique_ptr<RemoteObject>* result, + Maybe<ExceptionDetails>* exceptionDetails) +{ + if (!m_enabled) { + *errorString = "Runtime agent is not enabled"; + return; + } + + if (!m_compiledScripts.contains(scriptId)) { + *errorString = "Script execution failed"; + return; + } + + InjectedScript::ContextScope scope(errorString, m_debugger, m_session->contextGroupId(), executionContextId); + if (!scope.initialize()) + return; + + if (doNotPauseOnExceptionsAndMuteConsole.fromMaybe(false)) + scope.ignoreExceptionsAndMuteConsole(); + + std::unique_ptr<v8::Global<v8::Script>> scriptWrapper = m_compiledScripts.take(scriptId); + v8::Local<v8::Script> script = scriptWrapper->Get(m_debugger->isolate()); + if (script.IsEmpty()) { + *errorString = "Script execution failed"; + return; + } + + if (includeCommandLineAPI.fromMaybe(false) && !scope.installCommandLineAPI()) + return; + + v8::MaybeLocal<v8::Value> maybeResultValue = m_debugger->runCompiledScript(scope.context(), script); + + // Re-initialize after running client's code, as it could have destroyed context or session. + if (!scope.initialize()) + return; + scope.injectedScript()->wrapEvaluateResult(errorString, maybeResultValue, scope.tryCatch(), objectGroup.fromMaybe(""), false, false, result, nullptr, exceptionDetails); +} + +void V8RuntimeAgentImpl::setInspectorState(protocol::DictionaryValue* state) +{ + m_state = state; +} + +void V8RuntimeAgentImpl::setFrontend(protocol::Frontend::Runtime* frontend) +{ + m_frontend = frontend; +} + +void V8RuntimeAgentImpl::clearFrontend() +{ + ErrorString error; + disable(&error); + DCHECK(m_frontend); + m_frontend = nullptr; +} + +void V8RuntimeAgentImpl::restore() +{ + if (!m_state->booleanProperty(V8RuntimeAgentImplState::runtimeEnabled, false)) + return; + m_frontend->executionContextsCleared(); + ErrorString error; + enable(&error); + if (m_state->booleanProperty(V8RuntimeAgentImplState::customObjectFormatterEnabled, false)) + m_session->setCustomObjectFormatterEnabled(true); +} + +void V8RuntimeAgentImpl::enable(ErrorString* errorString) +{ + if (m_enabled) + return; + m_session->changeInstrumentationCounter(+1); + m_enabled = true; + m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, true); + v8::HandleScope handles(m_debugger->isolate()); + m_session->reportAllContexts(this); +} + +void V8RuntimeAgentImpl::disable(ErrorString* errorString) +{ + if (!m_enabled) + return; + m_enabled = false; + m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, false); + m_session->discardInjectedScripts(); + reset(); + m_session->changeInstrumentationCounter(-1); +} + +void V8RuntimeAgentImpl::reset() +{ + m_compiledScripts.clear(); + if (m_enabled) { + if (const V8DebuggerImpl::ContextByIdMap* contexts = m_debugger->contextGroup(m_session->contextGroupId())) { + for (auto& idContext : *contexts) + idContext.second->setReported(false); + } + m_frontend->executionContextsCleared(); + } +} + +void V8RuntimeAgentImpl::reportExecutionContextCreated(InspectedContext* context) +{ + if (!m_enabled) + return; + context->setReported(true); + std::unique_ptr<protocol::Runtime::ExecutionContextDescription> description = protocol::Runtime::ExecutionContextDescription::create() + .setId(context->contextId()) + .setIsDefault(context->isDefault()) + .setName(context->humanReadableName()) + .setOrigin(context->origin()) + .setFrameId(context->frameId()).build(); + m_frontend->executionContextCreated(std::move(description)); +} + +void V8RuntimeAgentImpl::reportExecutionContextDestroyed(InspectedContext* context) +{ + if (m_enabled && context->isReported()) { + context->setReported(false); + m_frontend->executionContextDestroyed(context->contextId()); + } +} + +void V8RuntimeAgentImpl::inspect(std::unique_ptr<protocol::Runtime::RemoteObject> objectToInspect, std::unique_ptr<protocol::DictionaryValue> hints) +{ + if (m_enabled) + m_frontend->inspectRequested(std::move(objectToInspect), std::move(hints)); +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/V8RuntimeAgentImpl.h b/deps/v8_inspector/platform/v8_inspector/V8RuntimeAgentImpl.h new file mode 100644 index 00000000000000..3870106acb1081 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8RuntimeAgentImpl.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef V8RuntimeAgentImpl_h +#define V8RuntimeAgentImpl_h + +#include "platform/inspector_protocol/Allocator.h" +#include "platform/inspector_protocol/Frontend.h" +#include "platform/v8_inspector/public/V8RuntimeAgent.h" + +namespace blink { + +class InjectedScript; +class InspectedContext; +class RemoteObjectIdBase; +class V8DebuggerImpl; +class V8InspectorSessionImpl; + +namespace protocol { +class DictionaryValue; +} + +using protocol::Maybe; + +class V8RuntimeAgentImpl : public V8RuntimeAgent { + PROTOCOL_DISALLOW_COPY(V8RuntimeAgentImpl); +public: + explicit V8RuntimeAgentImpl(V8InspectorSessionImpl*); + ~V8RuntimeAgentImpl() override; + + // State management methods. + void setInspectorState(protocol::DictionaryValue*) override; + void setFrontend(protocol::Frontend::Runtime*) override; + void clearFrontend() override; + void restore() override; + + // Part of the protocol. + void enable(ErrorString*) override; + void disable(ErrorString*) override; + void evaluate(ErrorString*, + const String16& expression, + const Maybe<String16>& objectGroup, + const Maybe<bool>& includeCommandLineAPI, + const Maybe<bool>& doNotPauseOnExceptionsAndMuteConsole, + const Maybe<int>& executionContextId, + const Maybe<bool>& returnByValue, + const Maybe<bool>& generatePreview, + const Maybe<bool>& userGesture, + std::unique_ptr<protocol::Runtime::RemoteObject>* result, + Maybe<bool>* wasThrown, + Maybe<protocol::Runtime::ExceptionDetails>*) override; + void callFunctionOn(ErrorString*, + const String16& objectId, + const String16& expression, + const Maybe<protocol::Array<protocol::Runtime::CallArgument>>& optionalArguments, + const Maybe<bool>& doNotPauseOnExceptionsAndMuteConsole, + const Maybe<bool>& returnByValue, + const Maybe<bool>& generatePreview, + const Maybe<bool>& userGesture, + std::unique_ptr<protocol::Runtime::RemoteObject>* result, + Maybe<bool>* wasThrown) override; + void releaseObject(ErrorString*, const String16& objectId) override; + void getProperties(ErrorString*, + const String16& objectId, + const Maybe<bool>& ownProperties, + const Maybe<bool>& accessorPropertiesOnly, + const Maybe<bool>& generatePreview, + std::unique_ptr<protocol::Array<protocol::Runtime::PropertyDescriptor>>* result, + Maybe<protocol::Array<protocol::Runtime::InternalPropertyDescriptor>>* internalProperties, + Maybe<protocol::Runtime::ExceptionDetails>*) override; + void releaseObjectGroup(ErrorString*, const String16& objectGroup) override; + void run(ErrorString*) override; + void setCustomObjectFormatterEnabled(ErrorString*, bool) override; + void compileScript(ErrorString*, + const String16& expression, + const String16& sourceURL, + bool persistScript, + int executionContextId, + Maybe<String16>*, + Maybe<protocol::Runtime::ExceptionDetails>*) override; + void runScript(ErrorString*, + const String16&, + int executionContextId, + const Maybe<String16>& objectGroup, + const Maybe<bool>& doNotPauseOnExceptionsAndMuteConsole, + const Maybe<bool>& includeCommandLineAPI, + std::unique_ptr<protocol::Runtime::RemoteObject>* result, + Maybe<protocol::Runtime::ExceptionDetails>*) override; + + void reset(); + void reportExecutionContextCreated(InspectedContext*); + void reportExecutionContextDestroyed(InspectedContext*); + void inspect(std::unique_ptr<protocol::Runtime::RemoteObject> objectToInspect, std::unique_ptr<protocol::DictionaryValue> hints); + +private: + V8InspectorSessionImpl* m_session; + protocol::DictionaryValue* m_state; + protocol::Frontend::Runtime* m_frontend; + V8DebuggerImpl* m_debugger; + bool m_enabled; + protocol::HashMap<String16, std::unique_ptr<v8::Global<v8::Script>>> m_compiledScripts; +}; + +} // namespace blink + +#endif // V8RuntimeAgentImpl_h diff --git a/deps/v8_inspector/platform/v8_inspector/V8StackTraceImpl.cpp b/deps/v8_inspector/platform/v8_inspector/V8StackTraceImpl.cpp new file mode 100644 index 00000000000000..3cba145eaf0015 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8StackTraceImpl.cpp @@ -0,0 +1,227 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/v8_inspector/V8StackTraceImpl.h" + +#include "platform/inspector_protocol/String16.h" +#include "platform/v8_inspector/V8DebuggerAgentImpl.h" +#include "platform/v8_inspector/V8DebuggerImpl.h" +#include "platform/v8_inspector/V8StringUtil.h" +#include "wtf/PtrUtil.h" + +#include <v8-debug.h> +#include <v8-profiler.h> +#include <v8-version.h> + +namespace blink { + +namespace { + +V8StackTraceImpl::Frame toFrame(v8::Local<v8::StackFrame> frame) +{ + String16 scriptId = String16::number(frame->GetScriptId()); + String16 sourceName; + v8::Local<v8::String> sourceNameValue(frame->GetScriptNameOrSourceURL()); + if (!sourceNameValue.IsEmpty()) + sourceName = toProtocolString(sourceNameValue); + + String16 functionName; + v8::Local<v8::String> functionNameValue(frame->GetFunctionName()); + if (!functionNameValue.IsEmpty()) + functionName = toProtocolString(functionNameValue); + + int sourceLineNumber = frame->GetLineNumber(); + int sourceColumn = frame->GetColumn(); + return V8StackTraceImpl::Frame(functionName, scriptId, sourceName, sourceLineNumber, sourceColumn); +} + +void toFramesVector(v8::Local<v8::StackTrace> stackTrace, protocol::Vector<V8StackTraceImpl::Frame>& frames, size_t maxStackSize, v8::Isolate* isolate) +{ + DCHECK(isolate->InContext()); + int frameCount = stackTrace->GetFrameCount(); + if (frameCount > static_cast<int>(maxStackSize)) + frameCount = maxStackSize; + for (int i = 0; i < frameCount; i++) { + v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(i); + frames.append(toFrame(stackFrame)); + } +} + +} // namespace + +V8StackTraceImpl::Frame::Frame() + : m_functionName("undefined") + , m_scriptId("") + , m_scriptName("undefined") + , m_lineNumber(0) + , m_columnNumber(0) +{ +} + +V8StackTraceImpl::Frame::Frame(const String16& functionName, const String16& scriptId, const String16& scriptName, int lineNumber, int column) + : m_functionName(functionName) + , m_scriptId(scriptId) + , m_scriptName(scriptName) + , m_lineNumber(lineNumber) + , m_columnNumber(column) +{ +} + +V8StackTraceImpl::Frame::~Frame() +{ +} + +// buildInspectorObject() and ScriptCallStack's toTracedValue() should set the same fields. +// If either of them is modified, the other should be also modified. +std::unique_ptr<protocol::Runtime::CallFrame> V8StackTraceImpl::Frame::buildInspectorObject() const +{ + return protocol::Runtime::CallFrame::create() + .setFunctionName(m_functionName) + .setScriptId(m_scriptId) + .setUrl(m_scriptName) + .setLineNumber(m_lineNumber) + .setColumnNumber(m_columnNumber) + .build(); +} + +std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(V8DebuggerAgentImpl* agent, v8::Local<v8::StackTrace> stackTrace, size_t maxStackSize, const String16& description) +{ + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::HandleScope scope(isolate); + protocol::Vector<V8StackTraceImpl::Frame> frames; + if (!stackTrace.IsEmpty()) + toFramesVector(stackTrace, frames, maxStackSize, isolate); + + int maxAsyncCallChainDepth = 1; + V8StackTraceImpl* asyncCallChain = nullptr; + if (agent && maxStackSize > 1) { + asyncCallChain = agent->currentAsyncCallChain(); + maxAsyncCallChainDepth = agent->maxAsyncCallChainDepth(); + } + + // Only the top stack in the chain may be empty, so ensure that second stack is non-empty (it's the top of appended chain). + if (asyncCallChain && asyncCallChain->isEmpty()) + asyncCallChain = asyncCallChain->m_parent.get(); + + if (stackTrace.IsEmpty() && !asyncCallChain) + return nullptr; + + std::unique_ptr<V8StackTraceImpl> result(new V8StackTraceImpl(description, frames, asyncCallChain ? asyncCallChain->clone() : nullptr)); + + // Crop to not exceed maxAsyncCallChainDepth. + V8StackTraceImpl* deepest = result.get(); + while (deepest && maxAsyncCallChainDepth) { + deepest = deepest->m_parent.get(); + maxAsyncCallChainDepth--; + } + if (deepest) + deepest->m_parent.reset(); + + return result; +} + +std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture(V8DebuggerAgentImpl* agent, size_t maxStackSize, const String16& description) +{ + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::HandleScope handleScope(isolate); + v8::Local<v8::StackTrace> stackTrace; + if (isolate->InContext()) { +#if V8_MAJOR_VERSION >= 5 + isolate->GetCpuProfiler()->CollectSample(); +#endif + stackTrace = v8::StackTrace::CurrentStackTrace(isolate, maxStackSize, stackTraceOptions); + } + return V8StackTraceImpl::create(agent, stackTrace, maxStackSize, description); +} + +std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::clone() +{ + protocol::Vector<Frame> framesCopy(m_frames); + return wrapUnique(new V8StackTraceImpl(m_description, framesCopy, m_parent ? m_parent->clone() : nullptr)); +} + +V8StackTraceImpl::V8StackTraceImpl(const String16& description, protocol::Vector<Frame>& frames, std::unique_ptr<V8StackTraceImpl> parent) + : m_description(description) + , m_parent(std::move(parent)) +{ + m_frames.swap(frames); +} + +V8StackTraceImpl::~V8StackTraceImpl() +{ +} + +String16 V8StackTraceImpl::topSourceURL() const +{ + DCHECK(m_frames.size()); + return m_frames[0].m_scriptName; +} + +int V8StackTraceImpl::topLineNumber() const +{ + DCHECK(m_frames.size()); + return m_frames[0].m_lineNumber; +} + +int V8StackTraceImpl::topColumnNumber() const +{ + DCHECK(m_frames.size()); + return m_frames[0].m_columnNumber; +} + +String16 V8StackTraceImpl::topFunctionName() const +{ + DCHECK(m_frames.size()); + return m_frames[0].m_functionName; +} + +String16 V8StackTraceImpl::topScriptId() const +{ + DCHECK(m_frames.size()); + return m_frames[0].m_scriptId; +} + +std::unique_ptr<protocol::Runtime::StackTrace> V8StackTraceImpl::buildInspectorObject() const +{ + std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>> frames = protocol::Array<protocol::Runtime::CallFrame>::create(); + for (size_t i = 0; i < m_frames.size(); i++) + frames->addItem(m_frames.at(i).buildInspectorObject()); + + std::unique_ptr<protocol::Runtime::StackTrace> stackTrace = protocol::Runtime::StackTrace::create() + .setCallFrames(std::move(frames)).build(); + if (!m_description.isEmpty()) + stackTrace->setDescription(m_description); + if (m_parent) + stackTrace->setParent(m_parent->buildInspectorObject()); + return stackTrace; +} + +std::unique_ptr<protocol::Runtime::StackTrace> V8StackTraceImpl::buildInspectorObjectForTail(V8DebuggerAgentImpl* agent) const +{ + v8::HandleScope handleScope(v8::Isolate::GetCurrent()); + // Next call collapses possible empty stack and ensures maxAsyncCallChainDepth. + std::unique_ptr<V8StackTraceImpl> fullChain = V8StackTraceImpl::create(agent, v8::Local<v8::StackTrace>(), V8StackTrace::maxCallStackSizeToCapture); + if (!fullChain || !fullChain->m_parent) + return nullptr; + return fullChain->m_parent->buildInspectorObject(); +} + +String16 V8StackTraceImpl::toString() const +{ + String16Builder stackTrace; + for (size_t i = 0; i < m_frames.size(); ++i) { + const Frame& frame = m_frames[i]; + stackTrace.append("\n at " + (frame.functionName().length() ? frame.functionName() : "(anonymous function)")); + stackTrace.append(" ("); + stackTrace.append(frame.sourceURL()); + stackTrace.append(':'); + stackTrace.appendNumber(frame.lineNumber()); + stackTrace.append(':'); + stackTrace.appendNumber(frame.columnNumber()); + stackTrace.append(')'); + } + return stackTrace.toString(); +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/V8StackTraceImpl.h b/deps/v8_inspector/platform/v8_inspector/V8StackTraceImpl.h new file mode 100644 index 00000000000000..9b9bd1920a114a --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8StackTraceImpl.h @@ -0,0 +1,74 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8StackTraceImpl_h +#define V8StackTraceImpl_h + +#include "platform/inspector_protocol/Collections.h" +#include "platform/v8_inspector/public/V8StackTrace.h" +#include "wtf/PtrUtil.h" + +namespace blink { + +class TracedValue; +class V8DebuggerAgentImpl; + +// Note: async stack trace may have empty top stack with non-empty tail to indicate +// that current native-only state had some async story. +// On the other hand, any non-top async stack is guaranteed to be non-empty. +class V8StackTraceImpl final : public V8StackTrace { + PROTOCOL_DISALLOW_COPY(V8StackTraceImpl); +public: + class Frame { + public: + Frame(); + Frame(const String16& functionName, const String16& scriptId, const String16& scriptName, int lineNumber, int column = 0); + ~Frame(); + + const String16& functionName() const { return m_functionName; } + const String16& scriptId() const { return m_scriptId; } + const String16& sourceURL() const { return m_scriptName; } + int lineNumber() const { return m_lineNumber; } + int columnNumber() const { return m_columnNumber; } + + private: + friend class V8StackTraceImpl; + std::unique_ptr<protocol::Runtime::CallFrame> buildInspectorObject() const; + void toTracedValue(TracedValue*) const; + + String16 m_functionName; + String16 m_scriptId; + String16 m_scriptName; + int m_lineNumber; + int m_columnNumber; + }; + + static std::unique_ptr<V8StackTraceImpl> create(V8DebuggerAgentImpl*, v8::Local<v8::StackTrace>, size_t maxStackSize, const String16& description = String16()); + static std::unique_ptr<V8StackTraceImpl> capture(V8DebuggerAgentImpl*, size_t maxStackSize, const String16& description = String16()); + + std::unique_ptr<V8StackTraceImpl> clone(); + std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectForTail(V8DebuggerAgentImpl*) const; + ~V8StackTraceImpl() override; + + // V8StackTrace implementation. + bool isEmpty() const override { return !m_frames.size(); }; + String16 topSourceURL() const override; + int topLineNumber() const override; + int topColumnNumber() const override; + String16 topScriptId() const override; + String16 topFunctionName() const override; + std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObject() const override; + String16 toString() const override; + +private: + V8StackTraceImpl(const String16& description, protocol::Vector<Frame>& frames, std::unique_ptr<V8StackTraceImpl> parent); + + String16 m_description; + protocol::Vector<Frame> m_frames; + std::unique_ptr<V8StackTraceImpl> m_parent; +}; + +} // namespace blink + +#endif // V8StackTraceImpl_h diff --git a/deps/v8_inspector/platform/v8_inspector/V8StringUtil.cpp b/deps/v8_inspector/platform/v8_inspector/V8StringUtil.cpp new file mode 100644 index 00000000000000..e47981e72ccc97 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8StringUtil.cpp @@ -0,0 +1,280 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/v8_inspector/V8StringUtil.h" + +#include "platform/inspector_protocol/String16.h" +#include "platform/v8_inspector/V8DebuggerImpl.h" +#include "platform/v8_inspector/V8InspectorSessionImpl.h" +#include "platform/v8_inspector/V8Regex.h" +#include "platform/v8_inspector/public/V8ContentSearchUtil.h" +#include "platform/v8_inspector/public/V8ToProtocolValue.h" + +namespace blink { + +namespace { + +String16 findMagicComment(const String16& content, const String16& name, bool multiline, bool* deprecated) +{ + DCHECK(name.find("=") == kNotFound); + if (deprecated) + *deprecated = false; + + unsigned length = content.length(); + unsigned nameLength = name.length(); + + size_t pos = length; + size_t equalSignPos = 0; + size_t closingCommentPos = 0; + while (true) { + pos = content.reverseFind(name, pos); + if (pos == kNotFound) + return String16(); + + // Check for a /\/[\/*][@#][ \t]/ regexp (length of 4) before found name. + if (pos < 4) + return String16(); + pos -= 4; + if (content[pos] != '/') + continue; + if ((content[pos + 1] != '/' || multiline) + && (content[pos + 1] != '*' || !multiline)) + continue; + if (content[pos + 2] != '#' && content[pos + 2] != '@') + continue; + if (content[pos + 3] != ' ' && content[pos + 3] != '\t') + continue; + equalSignPos = pos + 4 + nameLength; + if (equalSignPos < length && content[equalSignPos] != '=') + continue; + if (multiline) { + closingCommentPos = content.find("*/", equalSignPos + 1); + if (closingCommentPos == kNotFound) + return String16(); + } + + break; + } + + if (deprecated && content[pos + 2] == '@') + *deprecated = true; + + DCHECK(equalSignPos); + DCHECK(!multiline || closingCommentPos); + size_t urlPos = equalSignPos + 1; + String16 match = multiline + ? content.substring(urlPos, closingCommentPos - urlPos) + : content.substring(urlPos); + + size_t newLine = match.find("\n"); + if (newLine != kNotFound) + match = match.substring(0, newLine); + match = match.stripWhiteSpace(); + + String16 disallowedChars("\"' \t"); + for (unsigned i = 0; i < match.length(); ++i) { + if (disallowedChars.find(match[i]) != kNotFound) + return ""; + } + + return match; +} + +String16 createSearchRegexSource(const String16& text) +{ + String16Builder result; + String16 specials("[](){}+-*.,?\\^$|"); + + for (unsigned i = 0; i < text.length(); i++) { + if (specials.find(text[i]) != kNotFound) + result.append('\\'); + result.append(text[i]); + } + + return result.toString(); +} + +std::unique_ptr<protocol::Vector<unsigned>> lineEndings(const String16& text) +{ + std::unique_ptr<protocol::Vector<unsigned>> result(new protocol::Vector<unsigned>()); + + unsigned start = 0; + while (start < text.length()) { + size_t lineEnd = text.find('\n', start); + if (lineEnd == kNotFound) + break; + + result->append(static_cast<unsigned>(lineEnd)); + start = lineEnd + 1; + } + result->append(text.length()); + + return result; +} + +protocol::Vector<std::pair<int, String16>> scriptRegexpMatchesByLines(const V8Regex& regex, const String16& text) +{ + protocol::Vector<std::pair<int, String16>> result; + if (text.isEmpty()) + return result; + + std::unique_ptr<protocol::Vector<unsigned>> endings(lineEndings(text)); + unsigned size = endings->size(); + unsigned start = 0; + for (unsigned lineNumber = 0; lineNumber < size; ++lineNumber) { + unsigned lineEnd = endings->at(lineNumber); + String16 line = text.substring(start, lineEnd - start); + if (line.endsWith('\r')) + line = line.substring(0, line.length() - 1); + + int matchLength; + if (regex.match(line, 0, &matchLength) != -1) + result.append(std::pair<int, String16>(lineNumber, line)); + + start = lineEnd + 1; + } + return result; +} + +std::unique_ptr<protocol::Debugger::SearchMatch> buildObjectForSearchMatch(int lineNumber, const String16& lineContent) +{ + return protocol::Debugger::SearchMatch::create() + .setLineNumber(lineNumber) + .setLineContent(lineContent) + .build(); +} + +std::unique_ptr<V8Regex> createSearchRegex(V8DebuggerImpl* debugger, const String16& query, bool caseSensitive, bool isRegex) +{ + String16 regexSource = isRegex ? query : createSearchRegexSource(query); + return wrapUnique(new V8Regex(debugger, regexSource, caseSensitive)); +} + +} // namespace + +v8::Local<v8::String> toV8String(v8::Isolate* isolate, const String16& string) +{ + if (string.isEmpty()) + return v8::String::Empty(isolate); + return v8::String::NewFromTwoByte(isolate, reinterpret_cast<const uint16_t*>(string.characters16()), v8::NewStringType::kNormal, string.length()).ToLocalChecked(); +} + +v8::Local<v8::String> toV8StringInternalized(v8::Isolate* isolate, const String16& string) +{ + if (string.isEmpty()) + return v8::String::Empty(isolate); + return v8::String::NewFromTwoByte(isolate, reinterpret_cast<const uint16_t*>(string.characters16()), v8::NewStringType::kInternalized, string.length()).ToLocalChecked(); +} + +String16 toProtocolString(v8::Local<v8::String> value) +{ + if (value.IsEmpty() || value->IsNull() || value->IsUndefined()) + return String16(); + std::unique_ptr<UChar[]> buffer(new UChar[value->Length()]); + value->Write(reinterpret_cast<uint16_t*>(buffer.get()), 0, value->Length()); + return String16(buffer.get(), value->Length()); +} + +String16 toProtocolStringWithTypeCheck(v8::Local<v8::Value> value) +{ + if (value.IsEmpty() || !value->IsString()) + return String16(); + return toProtocolString(value.As<v8::String>()); +} + +namespace V8ContentSearchUtil { + +String16 findSourceURL(const String16& content, bool multiline, bool* deprecated) +{ + return findMagicComment(content, "sourceURL", multiline, deprecated); +} + +String16 findSourceMapURL(const String16& content, bool multiline, bool* deprecated) +{ + return findMagicComment(content, "sourceMappingURL", multiline, deprecated); +} + +std::unique_ptr<protocol::Array<protocol::Debugger::SearchMatch>> searchInTextByLines(V8InspectorSession* session, const String16& text, const String16& query, const bool caseSensitive, const bool isRegex) +{ + std::unique_ptr<protocol::Array<protocol::Debugger::SearchMatch>> result = protocol::Array<protocol::Debugger::SearchMatch>::create(); + std::unique_ptr<V8Regex> regex = createSearchRegex(static_cast<V8InspectorSessionImpl*>(session)->debugger(), query, caseSensitive, isRegex); + protocol::Vector<std::pair<int, String16>> matches = scriptRegexpMatchesByLines(*regex.get(), text); + + for (const auto& match : matches) + result->addItem(buildObjectForSearchMatch(match.first, match.second)); + + return result; +} + +} // namespace V8ContentSearchUtil + +std::unique_ptr<protocol::Value> toProtocolValue(v8::Local<v8::Context> context, v8::Local<v8::Value> value, int maxDepth) +{ + if (value.IsEmpty()) { + NOTREACHED(); + return nullptr; + } + + if (!maxDepth) + return nullptr; + maxDepth--; + + if (value->IsNull() || value->IsUndefined()) + return protocol::Value::null(); + if (value->IsBoolean()) + return protocol::FundamentalValue::create(value.As<v8::Boolean>()->Value()); + if (value->IsNumber()) + return protocol::FundamentalValue::create(value.As<v8::Number>()->Value()); + if (value->IsString()) + return protocol::StringValue::create(toProtocolString(value.As<v8::String>())); + if (value->IsArray()) { + v8::Local<v8::Array> array = value.As<v8::Array>(); + std::unique_ptr<protocol::ListValue> inspectorArray = protocol::ListValue::create(); + uint32_t length = array->Length(); + for (uint32_t i = 0; i < length; i++) { + v8::Local<v8::Value> value; + if (!array->Get(context, i).ToLocal(&value)) + return nullptr; + std::unique_ptr<protocol::Value> element = toProtocolValue(context, value, maxDepth); + if (!element) + return nullptr; + inspectorArray->pushValue(std::move(element)); + } + return std::move(inspectorArray); + } + if (value->IsObject()) { + std::unique_ptr<protocol::DictionaryValue> jsonObject = protocol::DictionaryValue::create(); + v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(value); + v8::Local<v8::Array> propertyNames; + if (!object->GetPropertyNames(context).ToLocal(&propertyNames)) + return nullptr; + uint32_t length = propertyNames->Length(); + for (uint32_t i = 0; i < length; i++) { + v8::Local<v8::Value> name; + if (!propertyNames->Get(context, i).ToLocal(&name)) + return nullptr; + // FIXME(yurys): v8::Object should support GetOwnPropertyNames + if (name->IsString()) { + v8::Maybe<bool> hasRealNamedProperty = object->HasRealNamedProperty(context, v8::Local<v8::String>::Cast(name)); + if (!hasRealNamedProperty.IsJust() || !hasRealNamedProperty.FromJust()) + continue; + } + v8::Local<v8::String> propertyName; + if (!name->ToString(context).ToLocal(&propertyName)) + continue; + v8::Local<v8::Value> property; + if (!object->Get(context, name).ToLocal(&property)) + return nullptr; + std::unique_ptr<protocol::Value> propertyValue = toProtocolValue(context, property, maxDepth); + if (!propertyValue) + return nullptr; + jsonObject->setValue(toProtocolString(propertyName), std::move(propertyValue)); + } + return std::move(jsonObject); + } + NOTREACHED(); + return nullptr; +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/V8StringUtil.h b/deps/v8_inspector/platform/v8_inspector/V8StringUtil.h new file mode 100644 index 00000000000000..c1c506baae8140 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/V8StringUtil.h @@ -0,0 +1,22 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8StringUtil_h +#define V8StringUtil_h + +#include "platform/inspector_protocol/String16.h" +#include <v8.h> + +namespace blink { + +v8::Local<v8::String> toV8String(v8::Isolate*, const String16&); +v8::Local<v8::String> toV8StringInternalized(v8::Isolate*, const String16&); + +String16 toProtocolString(v8::Local<v8::String>); +String16 toProtocolStringWithTypeCheck(v8::Local<v8::Value>); + +} // namespace blink + + +#endif // !defined(V8StringUtil_h) diff --git a/deps/v8_inspector/platform/v8_inspector/build/rjsmin.py b/deps/v8_inspector/platform/v8_inspector/build/rjsmin.py new file mode 100755 index 00000000000000..8357a6dcc10e57 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/build/rjsmin.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python +# +# Copyright 2011 - 2013 +# Andr\xe9 Malo or his licensors, as applicable +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r""" +===================== + Javascript Minifier +===================== + +rJSmin is a javascript minifier written in python. + +The minifier is based on the semantics of `jsmin.c by Douglas Crockford`_\. + +The module is a re-implementation aiming for speed, so it can be used at +runtime (rather than during a preprocessing step). Usually it produces the +same results as the original ``jsmin.c``. It differs in the following ways: + +- there is no error detection: unterminated string, regex and comment + literals are treated as regular javascript code and minified as such. +- Control characters inside string and regex literals are left untouched; they + are not converted to spaces (nor to \n) +- Newline characters are not allowed inside string and regex literals, except + for line continuations in string literals (ECMA-5). +- "return /regex/" is recognized correctly. +- "+ +" and "- -" sequences are not collapsed to '++' or '--' +- Newlines before ! operators are removed more sensibly +- rJSmin does not handle streams, but only complete strings. (However, the + module provides a "streamy" interface). + +Since most parts of the logic are handled by the regex engine it's way +faster than the original python port of ``jsmin.c`` by Baruch Even. The speed +factor varies between about 6 and 55 depending on input and python version +(it gets faster the more compressed the input already is). Compared to the +speed-refactored python port by Dave St.Germain the performance gain is less +dramatic but still between 1.2 and 7. See the docs/BENCHMARKS file for +details. + +rjsmin.c is a reimplementation of rjsmin.py in C and speeds it up even more. + +Both python 2 and python 3 are supported. + +.. _jsmin.c by Douglas Crockford: + http://www.crockford.com/javascript/jsmin.c +""" +__author__ = "Andr\xe9 Malo" +__author__ = getattr(__author__, 'decode', lambda x: __author__)('latin-1') +__docformat__ = "restructuredtext en" +__license__ = "Apache License, Version 2.0" +__version__ = '1.0.7' +__all__ = ['jsmin'] + +import re as _re + + +def _make_jsmin(python_only=False): + """ + Generate JS minifier based on `jsmin.c by Douglas Crockford`_ + + .. _jsmin.c by Douglas Crockford: + http://www.crockford.com/javascript/jsmin.c + + :Parameters: + `python_only` : ``bool`` + Use only the python variant. If true, the c extension is not even + tried to be loaded. + + :Return: Minifier + :Rtype: ``callable`` + """ + # pylint: disable = R0912, R0914, W0612 + if not python_only: + try: + import _rjsmin + except ImportError: + pass + else: + return _rjsmin.jsmin + try: + xrange + except NameError: + xrange = range # pylint: disable = W0622 + + space_chars = r'[\000-\011\013\014\016-\040]' + + line_comment = r'(?://[^\r\n]*)' + space_comment = r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)' + string1 = \ + r'(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|\r)[^\047\\\r\n]*)*\047)' + string2 = r'(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|\r)[^"\\\r\n]*)*")' + strings = r'(?:%s|%s)' % (string1, string2) + + charclass = r'(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\])' + nospecial = r'[^/\\\[\r\n]' + regex = r'(?:/(?![\r\n/*])%s*(?:(?:\\[^\r\n]|%s)%s*)*/)' % ( + nospecial, charclass, nospecial) + space = r'(?:%s|%s)' % (space_chars, space_comment) + newline = r'(?:%s?[\r\n])' % line_comment + + def fix_charclass(result): + """ Fixup string of chars to fit into a regex char class """ + pos = result.find('-') + if pos >= 0: + result = r'%s%s-' % (result[:pos], result[pos + 1:]) + + def sequentize(string): + """ + Notate consecutive characters as sequence + + (1-4 instead of 1234) + """ + first, last, result = None, None, [] + for char in map(ord, string): + if last is None: + first = last = char + elif last + 1 == char: + last = char + else: + result.append((first, last)) + first = last = char + if last is not None: + result.append((first, last)) + return ''.join(['%s%s%s' % ( + chr(first), + last > first + 1 and '-' or '', + last != first and chr(last) or '') for first, last in result]) + + return _re.sub(r'([\000-\040\047])', # for better portability + lambda m: '\\%03o' % ord(m.group(1)), (sequentize(result) + .replace('\\', '\\\\') + .replace('[', '\\[') + .replace(']', '\\]'))) + + def id_literal_(what): + """ Make id_literal like char class """ + match = _re.compile(what).match + result = ''.join([chr(c) for c in xrange(127) if not match(chr(c))]) + return '[^%s]' % fix_charclass(result) + + def not_id_literal_(keep): + """ Make negated id_literal like char class """ + match = _re.compile(id_literal_(keep)).match + result = ''.join([chr(c) for c in xrange(127) if not match(chr(c))]) + return r'[%s]' % fix_charclass(result) + + not_id_literal = not_id_literal_(r'[a-zA-Z0-9_$]') + preregex1 = r'[(,=:\[!&|?{};\r\n]' + preregex2 = r'%(not_id_literal)sreturn' % locals() + + id_literal = id_literal_(r'[a-zA-Z0-9_$]') + id_literal_open = id_literal_(r'[a-zA-Z0-9_${\[(!+-]') + id_literal_close = id_literal_(r'[a-zA-Z0-9_$}\])"\047+-]') + + dull = r'[^\047"/\000-\040]' + + space_sub = _re.compile(( + r'(%(dull)s+)' + r'|(%(strings)s%(dull)s*)' + r'|(?<=%(preregex1)s)' + r'%(space)s*(?:%(newline)s%(space)s*)*' + r'(%(regex)s%(dull)s*)' + r'|(?<=%(preregex2)s)' + r'%(space)s*(?:%(newline)s%(space)s)*' + r'(%(regex)s%(dull)s*)' + r'|(?<=%(id_literal_close)s)' + r'%(space)s*(?:(%(newline)s)%(space)s*)+' + r'(?=%(id_literal_open)s)' + r'|(?<=%(id_literal)s)(%(space)s)+(?=%(id_literal)s)' + r'|(?<=\+)(%(space)s)+(?=\+)' + r'|(?<=-)(%(space)s)+(?=-)' + r'|%(space)s+' + r'|(?:%(newline)s%(space)s*)+') % locals()).sub + #print space_sub.__self__.pattern + + def space_subber(match): + """ Substitution callback """ + # pylint: disable = C0321, R0911 + groups = match.groups() + if groups[0]: + return groups[0] + elif groups[1]: + return groups[1] + elif groups[2]: + return groups[2] + elif groups[3]: + return groups[3] + elif groups[4]: + return '\n' + elif groups[5] or groups[6] or groups[7]: + return ' ' + else: + return '' + + def jsmin(script): # pylint: disable = W0621 + r""" + Minify javascript based on `jsmin.c by Douglas Crockford`_\. + + Instead of parsing the stream char by char, it uses a regular + expression approach which minifies the whole script with one big + substitution regex. + + .. _jsmin.c by Douglas Crockford: + http://www.crockford.com/javascript/jsmin.c + + :Parameters: + `script` : ``str`` + Script to minify + + :Return: Minified script + :Rtype: ``str`` + """ + return space_sub(space_subber, '\n%s\n' % script).strip() + + return jsmin + +jsmin = _make_jsmin() + + +def jsmin_for_posers(script): + r""" + Minify javascript based on `jsmin.c by Douglas Crockford`_\. + + Instead of parsing the stream char by char, it uses a regular + expression approach which minifies the whole script with one big + substitution regex. + + .. _jsmin.c by Douglas Crockford: + http://www.crockford.com/javascript/jsmin.c + + :Warning: This function is the digest of a _make_jsmin() call. It just + utilizes the resulting regex. It's just for fun here and may + vanish any time. Use the `jsmin` function instead. + + :Parameters: + `script` : ``str`` + Script to minify + + :Return: Minified script + :Rtype: ``str`` + """ + def subber(match): + """ Substitution callback """ + groups = match.groups() + return ( + groups[0] or + groups[1] or + groups[2] or + groups[3] or + (groups[4] and '\n') or + (groups[5] and ' ') or + (groups[6] and ' ') or + (groups[7] and ' ') or + '') + + return _re.sub( + r'([^\047"/\000-\040]+)|((?:(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]|\r?' + r'\n|\r)[^\047\\\r\n]*)*\047)|(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|' + r'\r)[^"\\\r\n]*)*"))[^\047"/\000-\040]*)|(?<=[(,=:\[!&|?{};\r\n])(?' + r':[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*' + r'(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*' + r'[^*]*\*+(?:[^/*][^*]*\*+)*/))*)*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?:(' + r'?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\[' + r'\r\n]*)*/)[^\047"/\000-\040]*)|(?<=[\000-#%-,./:-@\[-^`{-~-]return' + r')(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/' + r'))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:' + r'/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?' + r':(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/' + r'\\\[\r\n]*)*/)[^\047"/\000-\040]*)|(?<=[^\000-!#%&(*,./:-@\[\\^`{|' + r'~])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)' + r'*/))*(?:((?:(?://[^\r\n]*)?[\r\n]))(?:[\000-\011\013\014\016-\040]' + r'|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040"#%-\047)*,./' + r':-@\\-^`|-~])|(?<=[^\000-#%-,./:-@\[-^`{-~-])((?:[\000-\011\013\01' + r'4\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=[^\000-#%-,./:' + r'-@\[-^`{-~-])|(?<=\+)((?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*' + r'\*+(?:[^/*][^*]*\*+)*/)))+(?=\+)|(?<=-)((?:[\000-\011\013\014\016-' + r'\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=-)|(?:[\000-\011\013' + r'\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))+|(?:(?:(?://[^' + r'\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^' + r'/*][^*]*\*+)*/))*)+', subber, '\n%s\n' % script).strip() + + +if __name__ == '__main__': + import sys as _sys + _sys.stdout.write(jsmin(_sys.stdin.read())) diff --git a/deps/v8_inspector/platform/v8_inspector/build/xxd.py b/deps/v8_inspector/platform/v8_inspector/build/xxd.py new file mode 100644 index 00000000000000..635d80c7d47509 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/build/xxd.py @@ -0,0 +1,28 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Represent a file as a C++ constant string. + +Usage: +python xxd.py VAR SOURCE DEST +""" + + +import sys +import rjsmin + + +def main(): + variable_name, input_filename, output_filename = sys.argv[1:] + with open(input_filename) as input_file: + input_text = input_file.read() + input_text = rjsmin.jsmin(input_text) + hex_values = ['0x{0:02x}'.format(ord(char)) for char in input_text] + const_declaration = 'const char %s[] = {\n%s\n};\n' % ( + variable_name, ', '.join(hex_values)) + with open(output_filename, 'w') as output_file: + output_file.write(const_declaration) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/deps/v8_inspector/platform/v8_inspector/debugger_script_externs.js b/deps/v8_inspector/platform/v8_inspector/debugger_script_externs.js new file mode 100644 index 00000000000000..25d07542792e92 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/debugger_script_externs.js @@ -0,0 +1,556 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** @typedef {{ + type: string, + object: !Object, + name: (string|undefined), + startLocation: (!RawLocation|undefined), + endLocation: (!RawLocation|undefined) + }} */ +var Scope; + +/** @typedef {{ + scriptId: string, + lineNumber: number, + columnNumber: number + }} */ +var RawLocation; + +/** @typedef {{ + function: function(), + functionName: string, + status: string, + location: (!RawLocation|undefined) + }} */ +var GeneratorObjectDetails; + +/** @typedef {{ + id: number, + name: string, + sourceURL: (string|undefined), + sourceMappingURL: (string|undefined), + source: string, + startLine: number, + endLine: number, + startColumn: number, + endColumn: number, + executionContextId: number, + isContentScript: boolean, + isInternalScript: boolean + }} */ +var FormattedScript; + +/** @typedef {{ + functionName: string, + location: !RawLocation, + this: !Object, + scopeChain: !Array<!Scope>, + functionLocation: (RawLocation|undefined), + returnValue: (*|undefined) + }} */ +var JavaScriptCallFrameDetails; + +/** @typedef {{ + sourceID: function():(number|undefined), + line: function():number, + column: function():number, + thisObject: !Object, + evaluate: function(string):*, + restart: function():undefined, + setVariableValue: function(number, string, *):undefined, + isAtReturn: boolean, + details: function():!JavaScriptCallFrameDetails + }} */ +var JavaScriptCallFrame; + +/** @interface */ +function DebugClass() +{ + /** @type {!LiveEditClass} */ + this.LiveEdit; +} + +DebugClass.prototype.setBreakOnException = function() {} + +DebugClass.prototype.clearBreakOnException = function() {} + +DebugClass.prototype.setBreakOnUncaughtException = function() {} + +DebugClass.prototype.clearBreakOnUncaughtException = function() {} + +DebugClass.prototype.clearStepping = function() {} + +DebugClass.prototype.clearAllBreakPoints = function() {} + +/** @return {!Array<!Script>} */ +DebugClass.prototype.scripts = function() {} + +/** + * @param {number} scriptId + * @param {number=} line + * @param {number=} column + * @param {string=} condition + * @param {string=} groupId + * @param {Debug.BreakPositionAlignment=} positionAlignment + */ +DebugClass.prototype.setScriptBreakPointById = function(scriptId, line, column, condition, groupId, positionAlignment) {} + +/** + * @param {number} breakId + * @return {!Array<!SourceLocation>} + */ +DebugClass.prototype.findBreakPointActualLocations = function(breakId) {} + +/** + * @param {number} breakId + * @param {boolean} remove + * @return {!BreakPoint|undefined} + */ +DebugClass.prototype.findBreakPoint = function(breakId, remove) {} + +/** @return {!DebuggerFlags} */ +DebugClass.prototype.debuggerFlags = function() {} + +/** @type {!DebugClass} */ +var Debug; + + +/** @enum */ +Debug.BreakPositionAlignment = { + Statement: 0, + BreakPosition: 1 +}; + +/** @enum */ +Debug.StepAction = { StepOut: 0, + StepNext: 1, + StepIn: 2, + StepFrame: 3 }; + +/** @enum */ +Debug.ScriptCompilationType = { Host: 0, + Eval: 1, + JSON: 2 }; + + +/** @interface */ +function DebuggerFlag() {} + +/** @param {boolean} value */ +DebuggerFlag.prototype.setValue = function(value) {} + + +/** @interface */ +function DebuggerFlags() +{ + /** @type {!DebuggerFlag} */ + this.breakPointsActive; +} + + +/** @interface */ +function LiveEditClass() {} + +/** + * @param {!Script} script + * @param {string} newSource + * @param {boolean} previewOnly + * @return {!{stack_modified: (boolean|undefined)}} + */ +LiveEditClass.prototype.SetScriptSource = function(script, newSource, previewOnly, change_log) {} + + +/** @interface */ +function LiveEditErrorDetails() +{ + /** @type {string} */ + this.syntaxErrorMessage; + /** @type {!{start: !{line: number, column: number}}} */ + this.position; +} + + +/** @interface */ +function BreakpointInfo() +{ + /** @type {number} */ + this.breakpointId; + /** @type {number} */ + this.sourceID; + /** @type {number|undefined} */ + this.lineNumber; + /** @type {number|undefined} */ + this.columnNumber; + /** @type {string|undefined} */ + this.condition; + /** @type {boolean|undefined} */ + this.interstatementLocation; +} + + +/** @interface */ +function BreakPoint() {} + +/** @return {!BreakPoint|undefined} */ +BreakPoint.prototype.script_break_point = function() {} + +/** @return {number} */ +BreakPoint.prototype.number = function() {} + + +/** @interface */ +function CompileEvent() {} + +/** @return {!ScriptMirror} */ +CompileEvent.prototype.script = function() {} + + +/** @interface */ +function BreakEvent() {} + +/** @return {!Array<!BreakPoint>|undefined} */ +BreakEvent.prototype.breakPointsHit = function() {} + + +/** @interface */ +function ExecutionState() {} + +/** @param {!Debug.StepAction} action */ +ExecutionState.prototype.prepareStep = function(action) {} + +/** + * @param {string} source + * @param {boolean} disableBreak + * @param {*=} additionalContext + */ +ExecutionState.prototype.evaluateGlobal = function(source, disableBreak, additionalContext) {} + +/** @return {number} */ +ExecutionState.prototype.frameCount = function() {} + +/** + * @param {number} index + * @return {!FrameMirror} + */ +ExecutionState.prototype.frame = function(index) {} + +/** @param {number} index */ +ExecutionState.prototype.setSelectedFrame = function(index) {} + +/** @return {number} */ +ExecutionState.prototype.selectedFrame = function() {} + + +/** @enum */ +var ScopeType = { Global: 0, + Local: 1, + With: 2, + Closure: 3, + Catch: 4, + Block: 5, + Script: 6 }; + + +/** @interface */ +function SourceLocation() +{ + /** @type {number} */ + this.script; + /** @type {number} */ + this.position; + /** @type {number} */ + this.line; + /** @type {number} */ + this.column; + /** @type {number} */ + this.start; + /** @type {number} */ + this.end; +} + + +/** @interface */ +function Script() +{ + /** @type {number} */ + this.id; + /** @type {string|undefined} */ + this.context_data; + /** @type {string|undefined} */ + this.source_url; + /** @type {string|undefined} */ + this.source_mapping_url; + /** @type {boolean} */ + this.is_debugger_script; + /** @type {string} */ + this.source; + /** @type {!Array<number>} */ + this.line_ends; + /** @type {number} */ + this.line_offset; + /** @type {number} */ + this.column_offset; +} + +/** @return {string} */ +Script.prototype.nameOrSourceURL = function() {} + +/** @return {!Debug.ScriptCompilationType} */ +Script.prototype.compilationType = function() {} + + +/** @interface */ +function ScopeDetails() {} + +/** @return {!Object} */ +ScopeDetails.prototype.object = function() {} + +/** @return {string|undefined} */ +ScopeDetails.prototype.name = function() {} + + +/** @interface */ +function FrameDetails() {} + +/** @return {!Object} */ +FrameDetails.prototype.receiver = function() {} + +/** @return {function()} */ +FrameDetails.prototype.func = function() {} + +/** @return {boolean} */ +FrameDetails.prototype.isAtReturn = function() {} + +/** @return {number} */ +FrameDetails.prototype.sourcePosition = function() {} + +/** @return {*} */ +FrameDetails.prototype.returnValue = function() {} + +/** @return {number} */ +FrameDetails.prototype.scopeCount = function() {} + + +/** @param {boolean} value */ +function ToggleMirrorCache(value) {} + +/** + * @param {*} value + * @param {boolean=} transient + * @return {!Mirror} + */ +function MakeMirror(value, transient) {} + + +/** @interface */ +function Mirror() {} + +/** @return {boolean} */ +Mirror.prototype.isFunction = function() {} + +/** @return {boolean} */ +Mirror.prototype.isGenerator = function() {} + +/** @return {boolean} */ +Mirror.prototype.isMap = function() {} + +/** @return {boolean} */ +Mirror.prototype.isSet = function() {} + +/** @return {boolean} */ +Mirror.prototype.isIterator = function() {} + + +/** + * @interface + * @extends {Mirror} + */ +function ObjectMirror() {} + +/** @return {!Array<!PropertyMirror>} */ +ObjectMirror.prototype.properties = function() {} + + +/** + * @interface + * @extends {ObjectMirror} + */ +function FunctionMirror () {} + +/** @return {number} */ +FunctionMirror.prototype.scopeCount = function() {} + +/** + * @param {number} index + * @return {!ScopeMirror|undefined} + */ +FunctionMirror.prototype.scope = function(index) {} + +/** @return {boolean} */ +FunctionMirror.prototype.resolved = function() {} + +/** @return {function()} */ +FunctionMirror.prototype.value = function() {} + +/** @return {string} */ +FunctionMirror.prototype.debugName = function() {} + +/** @return {!ScriptMirror|undefined} */ +FunctionMirror.prototype.script = function() {} + +/** @return {!SourceLocation|undefined} */ +FunctionMirror.prototype.sourceLocation = function() {} + +/** @return {!ContextMirror|undefined} */ +FunctionMirror.prototype.context = function() {} + +/** + * @constructor + * @param {*} value + */ +function UnresolvedFunctionMirror(value) {} + + +/** + * @interface + * @extends {ObjectMirror} + */ +function MapMirror () {} + +/** + * @param {number=} limit + * @return {!Array<!{key: *, value: *}>} + */ +MapMirror.prototype.entries = function(limit) {} + + +/** + * @interface + * @extends {ObjectMirror} + */ +function SetMirror () {} + +/** + * @param {number=} limit + * @return {!Array<*>} + */ +SetMirror.prototype.values = function(limit) {} + + +/** + * @interface + * @extends {ObjectMirror} + */ +function IteratorMirror () {} + +/** + * @param {number=} limit + * @return {!Array<*>} + */ +IteratorMirror.prototype.preview = function(limit) {} + + +/** + * @interface + * @extends {ObjectMirror} + */ +function GeneratorMirror () {} + +/** @return {string} */ +GeneratorMirror.prototype.status = function() {} + +/** @return {!SourceLocation|undefined} */ +GeneratorMirror.prototype.sourceLocation = function() {} + +/** @return {!FunctionMirror} */ +GeneratorMirror.prototype.func = function() {} + + +/** + * @interface + * @extends {Mirror} + */ +function PropertyMirror() +{ + /** @type {*} */ + this.value_; +} + +/** @return {!Mirror} */ +PropertyMirror.prototype.value = function() {} + +/** @return {string} */ +PropertyMirror.prototype.name = function() {} + + +/** + * @interface + * @extends {Mirror} + */ +function FrameMirror() {} + +/** + * @param {boolean=} ignoreNestedScopes + * @return {!Array<!ScopeMirror>} + */ +FrameMirror.prototype.allScopes = function(ignoreNestedScopes) {} + +/** @return {!FrameDetails} */ +FrameMirror.prototype.details = function() {} + +/** + * @param {string} source + * @param {boolean} disableBreak + */ +FrameMirror.prototype.evaluate = function(source, disableBreak) {} + +FrameMirror.prototype.restart = function() {} + +/** @param {number} index */ +FrameMirror.prototype.scope = function(index) {} + + +/** + * @interface + * @extends {Mirror} + */ +function ScriptMirror() {} + +/** @return {!Script} */ +ScriptMirror.prototype.value = function() {} + +/** @return {number} */ +ScriptMirror.prototype.id = function() {} + +/** + * @param {number} position + * @param {boolean=} includeResourceOffset + */ +ScriptMirror.prototype.locationFromPosition = function(position, includeResourceOffset) {} + + +/** + * @interface + * @extends {Mirror} + */ +function ScopeMirror() {} + +/** @return {!ScopeDetails} */ +ScopeMirror.prototype.details = function() {} + +/** + * @param {string} name + * @param {*} newValue + */ +ScopeMirror.prototype.setVariableValue = function(name, newValue) {} + +/** + * @interface + * @extends {Mirror} + */ +function ContextMirror() {} + +/** @return {string|undefined} */ +ContextMirror.prototype.data = function() {} diff --git a/deps/v8_inspector/platform/v8_inspector/injected_script_externs.js b/deps/v8_inspector/platform/v8_inspector/injected_script_externs.js new file mode 100644 index 00000000000000..a543e5471fce0c --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/injected_script_externs.js @@ -0,0 +1,97 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** @interface */ +function InjectedScriptHostClass() +{ +} + +/** + * @param {*} objectId + * @param {!Object} hints + */ +InjectedScriptHostClass.prototype.inspect = function(objectId, hints) {} + +/** + * @param {*} obj + * @return {string} + */ +InjectedScriptHostClass.prototype.internalConstructorName = function(obj) {} + +/** + * @param {*} obj + * @return {boolean} + */ +InjectedScriptHostClass.prototype.formatAccessorsAsProperties = function(obj) {} + +/** + * @param {*} obj + * @return {string} + */ +InjectedScriptHostClass.prototype.subtype = function(obj) {} + +/** + * @param {*} obj + * @return {boolean} + */ +InjectedScriptHostClass.prototype.isTypedArray = function(obj) {} + +/** + * @param {!Object} obj + * @return {?Array.<*>} + */ +InjectedScriptHostClass.prototype.collectionEntries = function(obj) {} + +/** + * @param {*} obj + * @return {!Array.<*>} + */ +InjectedScriptHostClass.prototype.getInternalProperties = function(obj) {} + +/** + * @param {!EventTarget} target + * @return {!Object|undefined} + */ +InjectedScriptHostClass.prototype.getEventListeners = function(target) {} + +/** + * @param {!Function} fn + * @param {*} receiver + * @param {!Array.<*>=} argv + * @return {*} + */ +InjectedScriptHostClass.prototype.suppressWarningsAndCallFunction = function(fn, receiver, argv) {} + +/** + * @param {!Object} obj + * @param {string} key + * @param {*} value + */ +InjectedScriptHostClass.prototype.setNonEnumProperty = function(obj, key, value) {} + +/** + * @param {*} value + * @param {string} groupName + * @return {number} + */ +InjectedScriptHostClass.prototype.bind = function(value, groupName) {} + +/** + * @param {!Object} object + * @return {!Object} + */ +InjectedScriptHostClass.prototype.proxyTargetValue = function(object) {} + +/** + * @param {!Object} object + * @return {Object|undefined} + */ +InjectedScriptHostClass.prototype.prototype = function(object) {} + +/** @type {!InjectedScriptHostClass} */ +var InjectedScriptHost; +/** @type {!Window} */ +var inspectedGlobalObject; +/** @type {number} */ +var injectedScriptId; diff --git a/deps/v8_inspector/platform/v8_inspector/public/ConsoleAPITypes.h b/deps/v8_inspector/platform/v8_inspector/public/ConsoleAPITypes.h new file mode 100644 index 00000000000000..59487b86a9cfbb --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/public/ConsoleAPITypes.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ConsoleAPITypes_h +#define ConsoleAPITypes_h + +namespace blink { + +enum MessageType { + LogMessageType = 1, + DirMessageType, + DirXMLMessageType, + TableMessageType, + TraceMessageType, + StartGroupMessageType, + StartGroupCollapsedMessageType, + EndGroupMessageType, + ClearMessageType, + AssertMessageType, + TimeEndMessageType, + CountMessageType +}; + +} // namespace blink + +#endif // ConsoleAPITypes_h diff --git a/deps/v8_inspector/platform/v8_inspector/public/ConsoleTypes.h b/deps/v8_inspector/platform/v8_inspector/public/ConsoleTypes.h new file mode 100644 index 00000000000000..fb06fb8ed017ec --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/public/ConsoleTypes.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ConsoleTypes_h +#define ConsoleTypes_h + +namespace blink { + +enum MessageSource { + XMLMessageSource, + JSMessageSource, + NetworkMessageSource, + ConsoleAPIMessageSource, + StorageMessageSource, + AppCacheMessageSource, + RenderingMessageSource, + SecurityMessageSource, + OtherMessageSource, + DeprecationMessageSource, +}; + +enum MessageLevel { + DebugMessageLevel = 4, + LogMessageLevel = 1, + InfoMessageLevel = 5, + WarningMessageLevel = 2, + ErrorMessageLevel = 3, + RevokedErrorMessageLevel = 6 +}; + +} // namespace blink + +#endif // ConsoleTypes_h diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8ContentSearchUtil.h b/deps/v8_inspector/platform/v8_inspector/public/V8ContentSearchUtil.h new file mode 100644 index 00000000000000..565fc9673de83f --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/public/V8ContentSearchUtil.h @@ -0,0 +1,26 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8ContentSearchUtil_h +#define V8ContentSearchUtil_h + +#include "platform/PlatformExport.h" +#include "platform/inspector_protocol/String16.h" +#include "platform/inspector_protocol/TypeBuilder.h" + +namespace blink { + +class V8InspectorSession; + +namespace V8ContentSearchUtil { + +PLATFORM_EXPORT String16 findSourceURL(const String16& content, bool multiline, bool* deprecated = nullptr); +PLATFORM_EXPORT String16 findSourceMapURL(const String16& content, bool multiline, bool* deprecated = nullptr); +PLATFORM_EXPORT std::unique_ptr<protocol::Array<protocol::Debugger::SearchMatch>> searchInTextByLines(V8InspectorSession*, const String16& text, const String16& query, const bool caseSensitive, const bool isRegex); + +} + +} + +#endif // !defined(V8ContentSearchUtil_h) diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8ContextInfo.h b/deps/v8_inspector/platform/v8_inspector/public/V8ContextInfo.h new file mode 100644 index 00000000000000..43ce46f9f6a35f --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/public/V8ContextInfo.h @@ -0,0 +1,42 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8ContextInfo_h +#define V8ContextInfo_h + +#include "platform/inspector_protocol/Collections.h" +#include "platform/inspector_protocol/String16.h" + +#include <v8.h> + +namespace blink { + +class V8ContextInfo { +public: + V8ContextInfo(v8::Local<v8::Context> context, int contextGroupId, bool isDefault, const String16& origin, const String16& humanReadableName, const String16& frameId, bool hasMemoryOnConsole) + : context(context) + , contextGroupId(contextGroupId) + , isDefault(isDefault) + , origin(origin) + , humanReadableName(humanReadableName) + , frameId(frameId) + , hasMemoryOnConsole(hasMemoryOnConsole) + { + } + + v8::Local<v8::Context> context; + // Each v8::Context is a part of a group. The group id is used to find appropriate + // V8DebuggerAgent to notify about events in the context. + // |contextGroupId| must be non-0. + int contextGroupId; + bool isDefault; + const String16 origin; + const String16 humanReadableName; + const String16 frameId; + bool hasMemoryOnConsole; +}; + +} // namespace blink + +#endif // V8ContextInfo_h diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8Debugger.h b/deps/v8_inspector/platform/v8_inspector/public/V8Debugger.h new file mode 100644 index 00000000000000..c4fa6cfd7823a5 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/public/V8Debugger.h @@ -0,0 +1,65 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8Debugger_h +#define V8Debugger_h + +#include "platform/PlatformExport.h" +#include "platform/inspector_protocol/Frontend.h" +#include "wtf/PtrUtil.h" + +#include <v8.h> + +namespace blink { + +class V8ContextInfo; +class V8DebuggerClient; +class V8InspectorSession; +class V8StackTrace; + +namespace protocol { +class DictionaryValue; +} + +class PLATFORM_EXPORT V8Debugger { +public: + template <typename T> + class Agent { + public: + virtual void setInspectorState(protocol::DictionaryValue*) = 0; + virtual void setFrontend(T*) = 0; + virtual void clearFrontend() = 0; + virtual void restore() = 0; + }; + + static std::unique_ptr<V8Debugger> create(v8::Isolate*, V8DebuggerClient*); + virtual ~V8Debugger() { } + + // TODO(dgozman): make this non-static? + static int contextId(v8::Local<v8::Context>); + + virtual void contextCreated(const V8ContextInfo&) = 0; + virtual void contextDestroyed(v8::Local<v8::Context>) = 0; + // TODO(dgozman): remove this one. + virtual void resetContextGroup(int contextGroupId) = 0; + virtual void willExecuteScript(v8::Local<v8::Context>, int scriptId) = 0; + virtual void didExecuteScript(v8::Local<v8::Context>) = 0; + virtual void idleStarted() = 0; + virtual void idleFinished() = 0; + + virtual std::unique_ptr<V8InspectorSession> connect(int contextGroupId) = 0; + virtual bool isPaused() = 0; + + static v8::Local<v8::Private> scopeExtensionPrivate(v8::Isolate*); + static bool isCommandLineAPIMethod(const String16& name); + static bool isCommandLineAPIGetter(const String16& name); + + virtual std::unique_ptr<V8StackTrace> createStackTrace(v8::Local<v8::StackTrace>, size_t maxStackSize) = 0; + virtual std::unique_ptr<V8StackTrace> captureStackTrace(size_t maxStackSize) = 0; +}; + +} // namespace blink + + +#endif // V8Debugger_h diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8DebuggerAgent.h b/deps/v8_inspector/platform/v8_inspector/public/V8DebuggerAgent.h new file mode 100644 index 00000000000000..6a18e62fca9a4c --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/public/V8DebuggerAgent.h @@ -0,0 +1,24 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8DebuggerAgent_h +#define V8DebuggerAgent_h + +#include "platform/PlatformExport.h" +#include "platform/inspector_protocol/Dispatcher.h" +#include "platform/v8_inspector/public/V8Debugger.h" + +namespace blink { + +class V8RuntimeAgent; + +class PLATFORM_EXPORT V8DebuggerAgent : public protocol::Backend::Debugger, public V8Debugger::Agent<protocol::Frontend::Debugger> { +public: + virtual ~V8DebuggerAgent() { } +}; + +} // namespace blink + + +#endif // V8DebuggerAgent_h diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8DebuggerClient.h b/deps/v8_inspector/platform/v8_inspector/public/V8DebuggerClient.h new file mode 100644 index 00000000000000..29b041ce293ca8 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/public/V8DebuggerClient.h @@ -0,0 +1,54 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8DebuggerClient_h +#define V8DebuggerClient_h + +#include "platform/PlatformExport.h" +#include "platform/v8_inspector/public/ConsoleAPITypes.h" +#include "platform/v8_inspector/public/ConsoleTypes.h" +#include "platform/v8_inspector/public/V8ContextInfo.h" +#include "platform/v8_inspector/public/V8EventListenerInfo.h" + +#include <v8.h> + +namespace blink { + +class PLATFORM_EXPORT V8DebuggerClient { +public: + virtual ~V8DebuggerClient() { } + virtual void runMessageLoopOnPause(int contextGroupId) = 0; + virtual void quitMessageLoopOnPause() = 0; + virtual void muteWarningsAndDeprecations() = 0; + virtual void unmuteWarningsAndDeprecations() = 0; + virtual void muteConsole() = 0; + virtual void unmuteConsole() = 0; + virtual void beginUserGesture() = 0; + virtual void endUserGesture() = 0; + virtual void eventListeners(v8::Local<v8::Value>, V8EventListenerInfoList&) = 0; + virtual bool callingContextCanAccessContext(v8::Local<v8::Context> calling, v8::Local<v8::Context> target) = 0; + virtual String16 valueSubtype(v8::Local<v8::Value>) = 0; + virtual bool formatAccessorsAsProperties(v8::Local<v8::Value>) = 0; + virtual bool isExecutionAllowed() = 0; + virtual double currentTimeMS() = 0; + virtual int ensureDefaultContextInGroup(int contextGroupId) = 0; + virtual bool isInspectableHeapObject(v8::Local<v8::Object>) = 0; + + virtual void reportMessageToConsole(v8::Local<v8::Context>, MessageType, MessageLevel, const String16& message, const v8::FunctionCallbackInfo<v8::Value>* arguments, unsigned skipArgumentCount, int maxStackSize) = 0; + + virtual void consoleTime(const String16& title) = 0; + virtual void consoleTimeEnd(const String16& title) = 0; + virtual void consoleTimeStamp(const String16& title) = 0; + + virtual v8::MaybeLocal<v8::Value> memoryInfo(v8::Isolate*, v8::Local<v8::Context>, v8::Local<v8::Object> creationContext) = 0; + + typedef void (*TimerCallback)(void*); + virtual void startRepeatingTimer(double, TimerCallback, void* data) = 0; + virtual void cancelTimer(void* data) = 0; +}; + +} // namespace blink + + +#endif // V8DebuggerClient_h diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8EventListenerInfo.h b/deps/v8_inspector/platform/v8_inspector/public/V8EventListenerInfo.h new file mode 100644 index 00000000000000..03370be8da27a0 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/public/V8EventListenerInfo.h @@ -0,0 +1,36 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8EventListenerInfo_h +#define V8EventListenerInfo_h + +#include "platform/inspector_protocol/Collections.h" +#include "platform/inspector_protocol/String16.h" + +#include <v8.h> + +namespace blink { + +class V8EventListenerInfo { +public: + V8EventListenerInfo(const String16& eventType, bool useCapture, bool passive, v8::Local<v8::Object> handler) + : eventType(eventType) + , useCapture(useCapture) + , passive(passive) + , handler(handler) + { + } + + const String16 eventType; + bool useCapture; + bool passive; + v8::Local<v8::Object> handler; + +}; + +using V8EventListenerInfoList = protocol::Vector<V8EventListenerInfo>; + +} // namespace blink + +#endif // V8EventListenerInfo_h diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8HeapProfilerAgent.h b/deps/v8_inspector/platform/v8_inspector/public/V8HeapProfilerAgent.h new file mode 100644 index 00000000000000..ac3f522a3fe562 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/public/V8HeapProfilerAgent.h @@ -0,0 +1,23 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8HeapProfilerAgent_h +#define V8HeapProfilerAgent_h + +#include "platform/PlatformExport.h" +#include "platform/inspector_protocol/Dispatcher.h" +#include "platform/v8_inspector/public/V8Debugger.h" + +namespace blink { + +class V8RuntimeAgent; + +class PLATFORM_EXPORT V8HeapProfilerAgent : public protocol::Backend::HeapProfiler, public V8Debugger::Agent<protocol::Frontend::HeapProfiler> { +public: + virtual ~V8HeapProfilerAgent() { } +}; + +} // namespace blink + +#endif // !defined(V8HeapProfilerAgent_h) diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8Inspector.cpp b/deps/v8_inspector/platform/v8_inspector/public/V8Inspector.cpp new file mode 100644 index 00000000000000..0ab85c7340e974 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/public/V8Inspector.cpp @@ -0,0 +1,125 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +#include "platform/v8_inspector/public/V8Inspector.h" + +#include "platform/inspector_protocol/Dispatcher.h" +#include "platform/v8_inspector/V8StringUtil.h" +#include "platform/v8_inspector/public/V8Debugger.h" +#include "platform/v8_inspector/public/V8DebuggerAgent.h" +#include "platform/v8_inspector/public/V8DebuggerClient.h" +#include "platform/v8_inspector/public/V8HeapProfilerAgent.h" +#include "platform/v8_inspector/public/V8ProfilerAgent.h" +#include "platform/v8_inspector/public/V8RuntimeAgent.h" + +namespace blink { + +V8Inspector::V8Inspector(v8::Isolate* isolate, v8::Local<v8::Context> context) +{ + m_debugger = V8Debugger::create(isolate, this); + m_session = m_debugger->connect(1); + m_session->setClient(this); + m_state = protocol::DictionaryValue::create(); + m_state->setValue("runtime", protocol::DictionaryValue::create()); + m_state->setValue("debugger", protocol::DictionaryValue::create()); + m_state->setValue("profiler", protocol::DictionaryValue::create()); + m_state->setValue("heapProfiler", protocol::DictionaryValue::create()); + + m_session->runtimeAgent()->setInspectorState(m_state->getObject("runtime")); + m_session->debuggerAgent()->setInspectorState(m_state->getObject("debugger")); + m_session->profilerAgent()->setInspectorState(m_state->getObject("profiler")); + m_session->heapProfilerAgent()->setInspectorState(m_state->getObject("heapProfiler")); + + m_debugger->contextCreated(V8ContextInfo(context, 1, true, "", + "NodeJS Main Context", "", false)); +} + +V8Inspector::~V8Inspector() +{ + disconnectFrontend(); +} + +void V8Inspector::eventListeners(v8::Local<v8::Value> value, V8EventListenerInfoList& result) +{ +} + +bool V8Inspector::callingContextCanAccessContext(v8::Local<v8::Context> calling, v8::Local<v8::Context> target) +{ + return true; +} + +String16 V8Inspector::valueSubtype(v8::Local<v8::Value> value) +{ + return String16(); +} + +bool V8Inspector::formatAccessorsAsProperties(v8::Local<v8::Value> value) +{ + return false; +} + +void V8Inspector::connectFrontend(protocol::FrontendChannel* channel) +{ + DCHECK(!m_frontend); + m_frontend = wrapUnique(new protocol::Frontend(channel)); + m_dispatcher = protocol::Dispatcher::create(channel); + + m_dispatcher->registerAgent((protocol::Backend::Debugger*)m_session->debuggerAgent()); + m_dispatcher->registerAgent(m_session->heapProfilerAgent()); + m_dispatcher->registerAgent(m_session->profilerAgent()); + m_dispatcher->registerAgent(m_session->runtimeAgent()); + + m_session->debuggerAgent()->setFrontend( + protocol::Frontend::Debugger::from(m_frontend.get())); + m_session->heapProfilerAgent()->setFrontend( + protocol::Frontend::HeapProfiler::from(m_frontend.get())); + m_session->profilerAgent()->setFrontend( + protocol::Frontend::Profiler::from(m_frontend.get())); + m_session->runtimeAgent()->setFrontend( + protocol::Frontend::Runtime::from(m_frontend.get())); +} + +void V8Inspector::disconnectFrontend() +{ + if (!m_frontend) + return; + m_dispatcher->clearFrontend(); + m_dispatcher.reset(); + + m_session->debuggerAgent()->clearFrontend(); + m_session->heapProfilerAgent()->clearFrontend(); + m_session->profilerAgent()->clearFrontend(); + m_session->runtimeAgent()->clearFrontend(); + + m_frontend.reset(); +} + +void V8Inspector::dispatchMessageFromFrontend(const String16& message) +{ + if (m_dispatcher) + m_dispatcher->dispatch(1, message); +} + +int V8Inspector::ensureDefaultContextInGroup(int contextGroupId) +{ + return 1; +} + +void V8Inspector::muteConsole() +{ + +} + +void V8Inspector::unmuteConsole() +{ + +} + +bool V8Inspector::isExecutionAllowed() +{ + return true; +} + +} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8Inspector.h b/deps/v8_inspector/platform/v8_inspector/public/V8Inspector.h new file mode 100644 index 00000000000000..33763014a86bae --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/public/V8Inspector.h @@ -0,0 +1,81 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8Inspector_h +#define V8Inspector_h + +#include "platform/v8_inspector/public/V8DebuggerClient.h" +#include "platform/v8_inspector/public/V8InspectorSessionClient.h" +#include "platform/v8_inspector/public/V8InspectorSession.h" + +#include "wtf/PtrUtil.h" +#include <v8.h> + +namespace blink { + +namespace protocol { +class Dispatcher; +class Frontend; +class FrontendChannel; +} + +class V8Debugger; +class V8HeapProfilerAgent; +class V8ProfilerAgent; + +class V8Inspector : public V8DebuggerClient, V8InspectorSessionClient { +public: + V8Inspector(v8::Isolate*, v8::Local<v8::Context>); + ~V8Inspector(); + + // Transport interface. + void connectFrontend(protocol::FrontendChannel*); + void disconnectFrontend(); + void dispatchMessageFromFrontend(const String16& message); + +private: + void eventListeners(v8::Local<v8::Value>, V8EventListenerInfoList&) override; + bool callingContextCanAccessContext(v8::Local<v8::Context> calling, v8::Local<v8::Context> target) override; + String16 valueSubtype(v8::Local<v8::Value>) override; + bool formatAccessorsAsProperties(v8::Local<v8::Value>) override; + void muteWarningsAndDeprecations() override { } + void unmuteWarningsAndDeprecations() override { } + double currentTimeMS() override { return 0; }; + + void muteConsole() override; + void unmuteConsole() override; + bool isExecutionAllowed() override; + int ensureDefaultContextInGroup(int contextGroupId) override; + void beginUserGesture() override { } + void endUserGesture() override { } + bool isInspectableHeapObject(v8::Local<v8::Object>) override { return true; } + void consoleTime(const String16& title) override { } + void consoleTimeEnd(const String16& title) override { } + void consoleTimeStamp(const String16& title) override { } + v8::MaybeLocal<v8::Value> memoryInfo(v8::Isolate*, v8::Local<v8::Context>, v8::Local<v8::Object> creationContext) override + { + return v8::MaybeLocal<v8::Value>(); + } + void reportMessageToConsole(v8::Local<v8::Context>, MessageType, MessageLevel, const String16& message, const v8::FunctionCallbackInfo<v8::Value>* arguments, unsigned skipArgumentCount, int maxStackSize) override { } + + // V8InspectorSessionClient + void startInstrumenting() override { } + void stopInstrumenting() override { } + void resumeStartup() override { } + bool canExecuteScripts() override { return true; } + void profilingStarted() override { } + void profilingStopped() override { } + void startRepeatingTimer(double, TimerCallback, void* data) override { } + void cancelTimer(void* data) override { } + + std::unique_ptr<V8Debugger> m_debugger; + std::unique_ptr<V8InspectorSession> m_session; + std::unique_ptr<protocol::Dispatcher> m_dispatcher; + std::unique_ptr<protocol::Frontend> m_frontend; + std::unique_ptr<protocol::DictionaryValue> m_state; +}; + +} + +#endif // V8Inspector_h diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8InspectorSession.h b/deps/v8_inspector/platform/v8_inspector/public/V8InspectorSession.h new file mode 100644 index 00000000000000..dfb9ff6386a9b7 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/public/V8InspectorSession.h @@ -0,0 +1,67 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8InspectorSession_h +#define V8InspectorSession_h + +#include "platform/PlatformExport.h" +#include "platform/inspector_protocol/TypeBuilder.h" +#include "wtf/PtrUtil.h" + +#include <v8.h> + +namespace blink { + +class V8DebuggerAgent; +class V8HeapProfilerAgent; +class V8InspectorSessionClient; +class V8ProfilerAgent; +class V8RuntimeAgent; + +class PLATFORM_EXPORT V8InspectorSession { +public: + static const char backtraceObjectGroup[]; + + // Cross-context inspectable values (DOM nodes in different worlds, etc.). + class Inspectable { + public: + virtual v8::Local<v8::Value> get(v8::Local<v8::Context>) = 0; + virtual ~Inspectable() { } + }; + + virtual ~V8InspectorSession() { } + + virtual void setClient(V8InspectorSessionClient*) = 0; + virtual void addInspectedObject(std::unique_ptr<Inspectable>) = 0; + + // API for the embedder to report native activities. + virtual void schedulePauseOnNextStatement(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data) = 0; + virtual void cancelPauseOnNextStatement() = 0; + virtual void breakProgram(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data) = 0; + virtual void breakProgramOnException(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data) = 0; + virtual void setSkipAllPauses(bool) = 0; + + // API to report async call stacks. + virtual void asyncTaskScheduled(const String16& taskName, void* task, bool recurring) = 0; + virtual void asyncTaskCanceled(void* task) = 0; + virtual void asyncTaskStarted(void* task) = 0; + virtual void asyncTaskFinished(void* task) = 0; + virtual void allAsyncTasksCanceled() = 0; + + // API to work with remote objects. + virtual std::unique_ptr<protocol::Runtime::RemoteObject> wrapObject(v8::Local<v8::Context>, v8::Local<v8::Value>, const String16& groupName, bool generatePreview = false) = 0; + // FIXME: remove when InspectorConsoleAgent moves into V8 inspector. + virtual std::unique_ptr<protocol::Runtime::RemoteObject> wrapTable(v8::Local<v8::Context>, v8::Local<v8::Value> table, v8::Local<v8::Value> columns) = 0; + virtual v8::Local<v8::Value> findObject(ErrorString*, const String16& objectId, v8::Local<v8::Context>* = nullptr, String16* objectGroup = nullptr) = 0; + virtual void releaseObjectGroup(const String16&) = 0; + + virtual V8DebuggerAgent* debuggerAgent() = 0; + virtual V8HeapProfilerAgent* heapProfilerAgent() = 0; + virtual V8ProfilerAgent* profilerAgent() = 0; + virtual V8RuntimeAgent* runtimeAgent() = 0; +}; + +} // namespace blink + +#endif // V8InspectorSession_h diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8InspectorSessionClient.h b/deps/v8_inspector/platform/v8_inspector/public/V8InspectorSessionClient.h new file mode 100644 index 00000000000000..e4775ffc16085c --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/public/V8InspectorSessionClient.h @@ -0,0 +1,28 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8InspectorSessionClient_h +#define V8InspectorSessionClient_h + +#include "platform/PlatformExport.h" + +#include <v8.h> + +namespace blink { + +class PLATFORM_EXPORT V8InspectorSessionClient { +public: + virtual ~V8InspectorSessionClient() { } + virtual void startInstrumenting() = 0; + virtual void stopInstrumenting() = 0; + virtual void resumeStartup() = 0; + virtual bool canExecuteScripts() = 0; + virtual void profilingStarted() = 0; + virtual void profilingStopped() = 0; +}; + +} // namespace blink + + +#endif // V8InspectorSessionClient_h diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8ProfilerAgent.h b/deps/v8_inspector/platform/v8_inspector/public/V8ProfilerAgent.h new file mode 100644 index 00000000000000..dacf8a0bc35933 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/public/V8ProfilerAgent.h @@ -0,0 +1,22 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8ProfilerAgent_h +#define V8ProfilerAgent_h + +#include "platform/PlatformExport.h" +#include "platform/inspector_protocol/Dispatcher.h" +#include "platform/v8_inspector/public/V8Debugger.h" + +namespace blink { + +class PLATFORM_EXPORT V8ProfilerAgent : public protocol::Backend::Profiler, public V8Debugger::Agent<protocol::Frontend::Profiler> { +public: + virtual ~V8ProfilerAgent() { } +}; + +} // namespace blink + + +#endif // !defined(V8ProfilerAgent_h) diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8RuntimeAgent.h b/deps/v8_inspector/platform/v8_inspector/public/V8RuntimeAgent.h new file mode 100644 index 00000000000000..917114f42bcfee --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/public/V8RuntimeAgent.h @@ -0,0 +1,23 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8RuntimeAgent_h +#define V8RuntimeAgent_h + +#include "platform/PlatformExport.h" +#include "platform/inspector_protocol/Dispatcher.h" +#include "platform/v8_inspector/public/V8Debugger.h" + +namespace blink { + +class InjectedScriptManager; + +class PLATFORM_EXPORT V8RuntimeAgent : public protocol::Backend::Runtime, public V8Debugger::Agent<protocol::Frontend::Runtime> { +public: + virtual ~V8RuntimeAgent() { } +}; + +} // namespace blink + +#endif // V8RuntimeAgent_h diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8StackTrace.h b/deps/v8_inspector/platform/v8_inspector/public/V8StackTrace.h new file mode 100644 index 00000000000000..da716c99bf3ec9 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/public/V8StackTrace.h @@ -0,0 +1,42 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8StackTrace_h +#define V8StackTrace_h + +#include "platform/inspector_protocol/TypeBuilder.h" +#include "wtf/PtrUtil.h" + +#include <v8.h> + +namespace blink { + +class TracedValue; + +const v8::StackTrace::StackTraceOptions stackTraceOptions = static_cast<v8::StackTrace::StackTraceOptions>( + v8::StackTrace::kLineNumber | + v8::StackTrace::kColumnOffset | + v8::StackTrace::kScriptId | + v8::StackTrace::kScriptNameOrSourceURL | + v8::StackTrace::kFunctionName); + +class V8StackTrace { +public: + static const size_t maxCallStackSizeToCapture = 200; + + virtual bool isEmpty() const = 0; + virtual String16 topSourceURL() const = 0; + virtual int topLineNumber() const = 0; + virtual int topColumnNumber() const = 0; + virtual String16 topScriptId() const = 0; + virtual String16 topFunctionName() const = 0; + + virtual ~V8StackTrace() { } + virtual std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObject() const = 0; + virtual String16 toString() const = 0; +}; + +} // namespace blink + +#endif // V8StackTrace_h diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8ToProtocolValue.h b/deps/v8_inspector/platform/v8_inspector/public/V8ToProtocolValue.h new file mode 100644 index 00000000000000..c3c692f9959236 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/public/V8ToProtocolValue.h @@ -0,0 +1,17 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8ToProtocolValue_h +#define V8ToProtocolValue_h + +#include "platform/inspector_protocol/Values.h" +#include <v8.h> + +namespace blink { + +PLATFORM_EXPORT std::unique_ptr<protocol::Value> toProtocolValue(v8::Local<v8::Context>, v8::Local<v8::Value>, int maxDepth = protocol::Value::maxDepth); + +} // namespace blink + +#endif // V8ToProtocolValue_h diff --git a/deps/v8_inspector/platform/v8_inspector/v8_inspector.gyp b/deps/v8_inspector/platform/v8_inspector/v8_inspector.gyp new file mode 100644 index 00000000000000..20df5e8bc3d635 --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/v8_inspector.gyp @@ -0,0 +1,48 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + # GN version: //third_party/WebKit/Source/platform:inspector_injected_script + 'target_name': 'inspector_injected_script', + 'type': 'none', + 'actions': [ + { + 'action_name': 'ConvertFileToHeaderWithCharacterArray', + 'inputs': [ + 'build/xxd.py', + 'InjectedScriptSource.js', + ], + 'outputs': [ '<(SHARED_INTERMEDIATE_DIR)/blink/platform/v8_inspector/InjectedScriptSource.h', ], + 'action': [ + 'python', 'build/xxd.py', 'InjectedScriptSource_js', 'InjectedScriptSource.js', '<@(_outputs)' + ], + }, + ], + # Since this target generates header files, it needs to be a hard dependency. + 'hard_dependency': 1, + }, + { + # GN version: //third_party/WebKit/Source/platform:inspector_debugger_script + 'target_name': 'inspector_debugger_script', + 'type': 'none', + 'actions': [ + { + 'action_name': 'ConvertFileToHeaderWithCharacterArray', + 'inputs': [ + 'build/xxd.py', + 'DebuggerScript.js', + ], + 'outputs': [ '<(SHARED_INTERMEDIATE_DIR)/blink/platform/v8_inspector/DebuggerScript.h', ], + 'action': [ + 'python', 'build/xxd.py', 'DebuggerScript_js', 'DebuggerScript.js', '<@(_outputs)' + ], + }, + ], + # Since this target generates header files, it needs to be a hard dependency. + 'hard_dependency': 1, + }, + ], # targets +} diff --git a/deps/v8_inspector/v8_inspector.gyp b/deps/v8_inspector/v8_inspector.gyp new file mode 100644 index 00000000000000..59d8843b8e1a39 --- /dev/null +++ b/deps/v8_inspector/v8_inspector.gyp @@ -0,0 +1,35 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'includes': [ + 'v8_inspector.gypi', + ], + + 'targets': [ + { + 'target_name': 'v8_inspector', + 'type': '<(component)', + + 'dependencies': [ + 'platform/inspector_protocol/protocol.gyp:protocol_sources', + 'platform/v8_inspector/v8_inspector.gyp:inspector_injected_script', + 'platform/v8_inspector/v8_inspector.gyp:inspector_debugger_script', + ], + 'defines': [ + 'V8_INSPECTOR_USE_STL=1' + ], + 'include_dirs': [ + '.', + 'deps/wtf', + '../v8/include', + '../v8', + '<(SHARED_INTERMEDIATE_DIR)/blink', + ], + 'sources': [ + '<@(v8_inspector_files)', + ], + }, + ] # end targets +} diff --git a/deps/v8_inspector/v8_inspector.gypi b/deps/v8_inspector/v8_inspector.gypi new file mode 100644 index 00000000000000..aad781b6842021 --- /dev/null +++ b/deps/v8_inspector/v8_inspector.gypi @@ -0,0 +1,94 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'v8_inspector_files': [ + + '<(SHARED_INTERMEDIATE_DIR)/blink/platform/inspector_protocol/Backend.h', + '<(SHARED_INTERMEDIATE_DIR)/blink/platform/inspector_protocol/Dispatcher.cpp', + '<(SHARED_INTERMEDIATE_DIR)/blink/platform/inspector_protocol/Dispatcher.h', + '<(SHARED_INTERMEDIATE_DIR)/blink/platform/inspector_protocol/Frontend.cpp', + '<(SHARED_INTERMEDIATE_DIR)/blink/platform/inspector_protocol/Frontend.h', + '<(SHARED_INTERMEDIATE_DIR)/blink/platform/inspector_protocol/TypeBuilder.cpp', + '<(SHARED_INTERMEDIATE_DIR)/blink/platform/inspector_protocol/TypeBuilder.h', + 'platform/v8_inspector/public/V8Inspector.cpp', + 'platform/v8_inspector/public/V8Inspector.h', + + 'platform/inspector_protocol/Allocator.h', + 'platform/inspector_protocol/Array.h', + 'platform/inspector_protocol/Collections.h', + 'platform/inspector_protocol/CollectionsSTL.h', + 'platform/inspector_protocol/ErrorSupport.cpp', + 'platform/inspector_protocol/ErrorSupport.h', + 'platform/inspector_protocol/Maybe.h', + 'platform/inspector_protocol/Parser.cpp', + 'platform/inspector_protocol/Parser.h', + 'platform/inspector_protocol/FrontendChannel.h', + 'platform/inspector_protocol/String16.h', + 'platform/inspector_protocol/String16STL.cpp', + 'platform/inspector_protocol/String16STL.h', + 'platform/inspector_protocol/Values.cpp', + 'platform/inspector_protocol/Values.h', + 'platform/inspector_protocol/ValueConversions.cpp', + 'platform/inspector_protocol/ValueConversions.h', + 'platform/v8_inspector/Atomics.h', + 'platform/v8_inspector/InspectorWrapper.cpp', + 'platform/v8_inspector/InspectorWrapper.h', + 'platform/v8_inspector/IgnoreExceptionsScope.h', + 'platform/v8_inspector/InjectedScript.cpp', + 'platform/v8_inspector/InjectedScript.h', + 'platform/v8_inspector/InjectedScriptNative.cpp', + 'platform/v8_inspector/InjectedScriptNative.h', + 'platform/v8_inspector/InspectedContext.cpp', + 'platform/v8_inspector/InspectedContext.h', + 'platform/v8_inspector/JavaScriptCallFrame.cpp', + 'platform/v8_inspector/JavaScriptCallFrame.h', + 'platform/v8_inspector/MuteConsoleScope.h', + 'platform/v8_inspector/ScriptBreakpoint.h', + 'platform/v8_inspector/RemoteObjectId.cpp', + 'platform/v8_inspector/RemoteObjectId.h', + 'platform/v8_inspector/V8Console.cpp', + 'platform/v8_inspector/V8Console.h', + 'platform/v8_inspector/V8DebuggerAgentImpl.cpp', + 'platform/v8_inspector/V8DebuggerAgentImpl.h', + 'platform/v8_inspector/V8DebuggerImpl.cpp', + 'platform/v8_inspector/V8DebuggerImpl.h', + 'platform/v8_inspector/V8DebuggerScript.cpp', + 'platform/v8_inspector/V8DebuggerScript.h', + 'platform/v8_inspector/V8FunctionCall.cpp', + 'platform/v8_inspector/V8FunctionCall.h', + 'platform/v8_inspector/V8HeapProfilerAgentImpl.cpp', + 'platform/v8_inspector/V8HeapProfilerAgentImpl.h', + 'platform/v8_inspector/V8InjectedScriptHost.cpp', + 'platform/v8_inspector/V8InjectedScriptHost.h', + 'platform/v8_inspector/V8InspectorSessionImpl.cpp', + 'platform/v8_inspector/V8InspectorSessionImpl.h', + 'platform/v8_inspector/V8ProfilerAgentImpl.cpp', + 'platform/v8_inspector/V8ProfilerAgentImpl.h', + 'platform/v8_inspector/V8Regex.cpp', + 'platform/v8_inspector/V8Regex.h', + 'platform/v8_inspector/V8RuntimeAgentImpl.cpp', + 'platform/v8_inspector/V8RuntimeAgentImpl.h', + 'platform/v8_inspector/V8StackTraceImpl.cpp', + 'platform/v8_inspector/V8StackTraceImpl.h', + 'platform/v8_inspector/V8StringUtil.cpp', + 'platform/v8_inspector/V8StringUtil.h', + 'platform/v8_inspector/public/V8EventListenerInfo.h', + 'platform/v8_inspector/public/V8ContentSearchUtil.h', + 'platform/v8_inspector/public/V8ContextInfo.h', + 'platform/v8_inspector/public/V8Debugger.h', + 'platform/v8_inspector/public/V8DebuggerAgent.h', + 'platform/v8_inspector/public/V8DebuggerClient.h', + 'platform/v8_inspector/public/V8HeapProfilerAgent.h', + 'platform/v8_inspector/public/V8InspectorSession.h', + 'platform/v8_inspector/public/V8ProfilerAgent.h', + 'platform/v8_inspector/public/V8RuntimeAgent.h', + 'platform/v8_inspector/public/V8StackTrace.h', + 'platform/v8_inspector/public/V8ToProtocolValue.h', + '<(SHARED_INTERMEDIATE_DIR)/blink/platform/v8_inspector/DebuggerScript.h', + '<(SHARED_INTERMEDIATE_DIR)/blink/platform/v8_inspector/InjectedScriptSource.h', + ], + } +} From 7d4f038a7875d24262902feb0bf44da47a75f162 Mon Sep 17 00:00:00 2001 From: Pavel Feldman <pfeldman@chromium.org> Date: Sun, 7 Feb 2016 08:47:14 -0800 Subject: [PATCH 004/131] src,lib: v8-inspector support This change introduces experimental v8-inspector support. This brings the DevTools debug protocol allowing Node.js to be debugged with Chrome DevTools native, or through other debuggers supporting that protocol. Partial WebSocket support, to the extent required by DevTools, is included. This is derived from the implementation in Blink. v8-inspector support can be disabled by the --without-inspector configure flag. PR-URL: https://github.com/nodejs/node/pull/6792 Reviewed-By: jasnell - James M Snell <jasnell@gmail.com> Reviewed-By: addaleax - Anna Henningsen <anna@addaleax.net> Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl> --- configure | 6 + doc/api/debugger.md | 14 + lib/internal/bootstrap_node.js | 4 + node.gyp | 39 ++ src/env-inl.h | 3 + src/env.h | 12 + src/inspector_agent.cc | 506 ++++++++++++++++ src/inspector_agent.h | 97 +++ src/inspector_socket.cc | 679 +++++++++++++++++++++ src/inspector_socket.h | 57 ++ src/node.cc | 67 ++- src/node_internals.h | 6 +- src/signal_wrap.cc | 9 + test/cctest/test_inspector_socket.cc | 864 +++++++++++++++++++++++++++ 14 files changed, 2358 insertions(+), 5 deletions(-) create mode 100644 src/inspector_agent.cc create mode 100644 src/inspector_agent.h create mode 100644 src/inspector_socket.cc create mode 100644 src/inspector_socket.h create mode 100644 test/cctest/test_inspector_socket.cc diff --git a/configure b/configure index 10fd1b5fffa596..f7a3f41ae02821 100755 --- a/configure +++ b/configure @@ -415,6 +415,11 @@ parser.add_option('--no-browser-globals', help='do not export browser globals like setTimeout, console, etc. ' + '(This mode is not officially supported for regular applications)') +parser.add_option('--without-inspector', + action='store_true', + dest='without_inspector', + help='disable experimental V8 inspector support') + (options, args) = parser.parse_args() # Expand ~ in the install prefix now, it gets written to multiple files. @@ -810,6 +815,7 @@ def configure_node(o): o['variables']['library_files'] = options.linked_module o['variables']['asan'] = int(options.enable_asan or 0) + o['variables']['v8_inspector'] = b(not options.without_inspector) if options.use_xcode and options.use_ninja: raise Exception('--xcode and --ninja cannot be used together.') diff --git a/doc/api/debugger.md b/doc/api/debugger.md index a966ee2b01183e..6a31212d9c220b 100644 --- a/doc/api/debugger.md +++ b/doc/api/debugger.md @@ -179,4 +179,18 @@ process or via URI reference to the listening debugger: * `node debug <URI>` - Connects to the process via the URI such as localhost:5858 +## V8 Inspector Integration for Node.js + +__NOTE: This is an experimental feature.__ + +V8 Inspector integration allows attaching Chrome DevTools to Node.js +instances for debugging and profiling. + +V8 Inspector can be enabled by passing the `--inspect` flag when starting a +Node.js application. It is also possible to supply a custom port with that flag, +e.g. `--inspect=9222` will accept DevTools connections on port 9222. + +To break on the first line of the application code, provide the `--debug-brk` +flag in addition to `--inspect`. + [TCP-based protocol]: https://github.com/v8/v8/wiki/Debugging-Protocol diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index d66126969057e0..8c675185691595 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -81,6 +81,10 @@ // Start the debugger agent NativeModule.require('_debugger').start(); + } else if (process.argv[1] == '--remote_debugging_server') { + // Start the debugging server + NativeModule.require('internal/inspector/remote_debugging_server'); + } else if (process.argv[1] == '--debug-agent') { // Start the debugger agent NativeModule.require('_debug_agent').start(); diff --git a/node.gyp b/node.gyp index 0d32905b6ce120..05a5530a2b148e 100644 --- a/node.gyp +++ b/node.gyp @@ -250,6 +250,28 @@ 'deps/v8/src/third_party/vtune/v8vtune.gyp:v8_vtune' ], }], + [ 'v8_inspector=="true"', { + 'defines': [ + 'HAVE_INSPECTOR=1', + 'V8_INSPECTOR_USE_STL=1', + ], + 'sources': [ + 'src/inspector_agent.cc', + 'src/inspector_socket.cc', + 'src/inspector_socket.h', + 'src/inspector-agent.h', + ], + 'dependencies': [ + 'deps/v8_inspector/v8_inspector.gyp:v8_inspector', + ], + 'include_dirs': [ + 'deps/v8_inspector', + 'deps/v8_inspector/deps/wtf', # temporary + '<(SHARED_INTERMEDIATE_DIR)/blink', # for inspector + ], + }, { + 'defines': [ 'HAVE_INSPECTOR=0' ] + }], [ 'node_use_openssl=="true"', { 'defines': [ 'HAVE_OPENSSL=1' ], 'sources': [ @@ -690,7 +712,10 @@ 'target_name': 'cctest', 'type': 'executable', 'dependencies': [ + 'deps/openssl/openssl.gyp:openssl', + 'deps/http_parser/http_parser.gyp:http_parser', 'deps/gtest/gtest.gyp:gtest', + 'deps/uv/uv.gyp:libuv', 'deps/v8/tools/gyp/v8.gyp:v8', 'deps/v8/tools/gyp/v8.gyp:v8_libplatform' ], @@ -711,6 +736,20 @@ 'sources': [ 'test/cctest/util.cc', ], + + 'conditions': [ + ['v8_inspector=="true"', { + 'dependencies': [ + 'deps/openssl/openssl.gyp:openssl', + 'deps/http_parser/http_parser.gyp:http_parser', + 'deps/uv/uv.gyp:libuv' + ], + 'sources': [ + 'src/inspector_socket.cc', + 'test/cctest/test_inspector_socket.cc' + ] + }] + ] } ], # end targets diff --git a/src/env-inl.h b/src/env-inl.h index 34f9bf7d72da42..97e1ba8f764ac8 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -225,6 +225,9 @@ inline Environment::Environment(v8::Local<v8::Context> context, makecallback_cntr_(0), async_wrap_uid_(0), debugger_agent_(this), +#if HAVE_INSPECTOR + inspector_agent_(this), +#endif http_parser_buffer_(nullptr), context_(context->GetIsolate(), context) { // We'll be creating new objects so make sure we've entered the context. diff --git a/src/env.h b/src/env.h index 0c95abd56cb564..4c310c8831fcf8 100644 --- a/src/env.h +++ b/src/env.h @@ -5,6 +5,9 @@ #include "ares.h" #include "debug-agent.h" +#if HAVE_INSPECTOR +#include "inspector_agent.h" +#endif #include "handle_wrap.h" #include "req-wrap.h" #include "tree.h" @@ -549,6 +552,12 @@ class Environment { return &debugger_agent_; } +#if HAVE_INSPECTOR + inline inspector::Agent* inspector_agent() { + return &inspector_agent_; + } +#endif + typedef ListHead<HandleWrap, &HandleWrap::handle_wrap_queue_> HandleWrapQueue; typedef ListHead<ReqWrap<uv_req_t>, &ReqWrap<uv_req_t>::req_wrap_queue_> ReqWrapQueue; @@ -586,6 +595,9 @@ class Environment { size_t makecallback_cntr_; int64_t async_wrap_uid_; debugger::Agent debugger_agent_; +#if HAVE_INSPECTOR + inspector::Agent inspector_agent_; +#endif HandleWrapQueue handle_wrap_queue_; ReqWrapQueue req_wrap_queue_; diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc new file mode 100644 index 00000000000000..cd2ae83b19be59 --- /dev/null +++ b/src/inspector_agent.cc @@ -0,0 +1,506 @@ +#include "inspector_agent.h" + +#include "node.h" +#include "env.h" +#include "env-inl.h" +#include "node_version.h" +#include "v8-platform.h" +#include "util.h" + +#include "platform/v8_inspector/public/V8Inspector.h" +#include "platform/inspector_protocol/FrontendChannel.h" +#include "platform/inspector_protocol/String16.h" +#include "platform/inspector_protocol/Values.h" + +#include "libplatform/libplatform.h" + +#include <string.h> + +// We need pid to use as ID with Chrome +#if defined(_MSC_VER) +#include <direct.h> +#include <io.h> +#define getpid GetCurrentProcessId +#else +#include <unistd.h> // setuid, getuid +#endif + +namespace node { +namespace { + +const char DEVTOOLS_PATH[] = "/node"; + +void PrintDebuggerReadyMessage(int port) { + fprintf(stderr, "Debugger listening on port %d. " + "To start debugging, open the following URL in Chrome:\n" + " chrome-devtools://devtools/remote/serve_file/" + "@521e5b7e2b7cc66b4006a8a54cb9c4e57494a5ef/inspector.html?" + "experiments=true&v8only=true&ws=localhost:%d/node\n", port, port); +} + +bool AcceptsConnection(inspector_socket_t* socket, const char* path) { + return strncmp(DEVTOOLS_PATH, path, sizeof(DEVTOOLS_PATH)) == 0; +} + +void DisposeInspector(inspector_socket_t* socket, int status) { + free(socket); +} + +void DisconnectAndDisposeIO(inspector_socket_t* socket) { + if (socket) { + inspector_close(socket, DisposeInspector); + } +} + +void OnBufferAlloc(uv_handle_t* handle, size_t len, uv_buf_t* buf) { + if (len > 0) { + buf->base = static_cast<char*>(malloc(len)); + CHECK_NE(buf->base, nullptr); + } + buf->len = len; +} + +void SendHttpResponse(inspector_socket_t* socket, const char* response, + size_t len) { + const char HEADERS[] = "HTTP/1.0 200 OK\r\n" + "Content-Type: application/json; charset=UTF-8\r\n" + "Cache-Control: no-cache\r\n" + "Content-Length: %ld\r\n" + "\r\n"; + char header[sizeof(HEADERS) + 20]; + int header_len = snprintf(header, sizeof(header), HEADERS, len); + inspector_write(socket, header, header_len); + inspector_write(socket, response, len); +} + +void SendVersionResponse(inspector_socket_t* socket) { + const char VERSION_RESPONSE_TEMPLATE[] = + "[ {" + " \"Browser\": \"node.js/%s\"," + " \"Protocol-Version\": \"1.1\"," + " \"User-Agent\": \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36" + "(KHTML, like Gecko) Chrome/45.0.2446.0 Safari/537.36\"," + " \"WebKit-Version\": \"537.36 (@198122)\"" + "} ]"; + char buffer[sizeof(VERSION_RESPONSE_TEMPLATE) + 128]; + size_t len = snprintf(buffer, sizeof(buffer), VERSION_RESPONSE_TEMPLATE, + NODE_VERSION); + ASSERT_LT(len, sizeof(buffer)); + SendHttpResponse(socket, buffer, len); +} + +void SendTargentsListResponse(inspector_socket_t* socket) { + const char LIST_RESPONSE_TEMPLATE[] = + "[ {" + " \"description\": \"node.js instance\"," + " \"devtoolsFrontendUrl\": " + "\"https://chrome-devtools-frontend.appspot.com/serve_file/" + "@4604d24a75168768584760ba56d175507941852f/inspector.html\"," + " \"faviconUrl\": \"https://nodejs.org/static/favicon.ico\"," + " \"id\": \"%d\"," + " \"title\": \"%s\"," + " \"type\": \"node\"," + " \"webSocketDebuggerUrl\": \"ws://%s\"" + "} ]"; + char buffer[sizeof(LIST_RESPONSE_TEMPLATE) + 4096]; + char title[2048]; // uv_get_process_title trims the title if too long + int err = uv_get_process_title(title, sizeof(title)); + ASSERT_EQ(0, err); + char* c = title; + while (!c) { + if (*c < ' ' || *c == '\"') { + *c = '_'; + } + c++; + } + size_t len = snprintf(buffer, sizeof(buffer), LIST_RESPONSE_TEMPLATE, + getpid(), title, DEVTOOLS_PATH); + ASSERT_LT(len, sizeof(buffer)); + SendHttpResponse(socket, buffer, len); +} + +bool RespondToGet(inspector_socket_t* socket, const char* path) { + const char PATH[] = "/json"; + const char PATH_LIST[] = "/json/list"; + const char PATH_VERSION[] = "/json/version"; + const char PATH_ACTIVATE[] = "/json/activate/"; + if (!strncmp(PATH_VERSION, path, sizeof(PATH_VERSION))) { + SendVersionResponse(socket); + } else if (!strncmp(PATH_LIST, path, sizeof(PATH_LIST)) || + !strncmp(PATH, path, sizeof(PATH))) { + SendTargentsListResponse(socket); + } else if (!strncmp(path, PATH_ACTIVATE, sizeof(PATH_ACTIVATE) - 1) && + atoi(path + (sizeof(PATH_ACTIVATE) - 1)) == getpid()) { + const char TARGET_ACTIVATED[] = "Target activated"; + SendHttpResponse(socket, TARGET_ACTIVATED, sizeof(TARGET_ACTIVATED) - 1); + } else { + return false; + } + return true; +} + +} // namespace + +namespace inspector { + +using blink::protocol::DictionaryValue; +using blink::protocol::String16; + +void InterruptCallback(v8::Isolate*, void* agent) { + static_cast<Agent*>(agent)->PostMessages(); +} + +class DispatchOnInspectorBackendTask : public v8::Task { + public: + explicit DispatchOnInspectorBackendTask(Agent* agent) : agent_(agent) {} + + void Run() override { + agent_->PostMessages(); + } + + private: + Agent* agent_; +}; + +class ChannelImpl final : public blink::protocol::FrontendChannel { + public: + explicit ChannelImpl(Agent* agent): agent_(agent) {} + virtual ~ChannelImpl() {} + private: + virtual void sendProtocolResponse(int sessionId, int callId, + std::unique_ptr<DictionaryValue> message) + override { + sendMessageToFrontend(std::move(message)); + } + + virtual void sendProtocolNotification( + std::unique_ptr<DictionaryValue> message) override { + sendMessageToFrontend(std::move(message)); + } + + virtual void flush() override { } + + void sendMessageToFrontend(std::unique_ptr<DictionaryValue> message) { + agent_->Write(message->toJSONString().utf8()); + } + + Agent* const agent_; +}; + +class SetConnectedTask : public v8::Task { + public: + SetConnectedTask(Agent* agent, bool connected) + : agent_(agent), + connected_(connected) {} + + void Run() override { + agent_->SetConnected(connected_); + } + + private: + Agent* agent_; + bool connected_; +}; + +class V8NodeInspector : public blink::V8Inspector { + public: + V8NodeInspector(Agent* agent, node::Environment* env, v8::Platform* platform) + : blink::V8Inspector(env->isolate(), env->context()), + agent_(agent), + isolate_(env->isolate()), + platform_(platform), + terminated_(false), + running_nested_loop_(false) {} + + void runMessageLoopOnPause(int context_group_id) override { + if (running_nested_loop_) + return; + terminated_ = false; + running_nested_loop_ = true; + do { + uv_mutex_lock(&agent_->pause_lock_); + uv_cond_wait(&agent_->pause_cond_, &agent_->pause_lock_); + uv_mutex_unlock(&agent_->pause_lock_); + while (v8::platform::PumpMessageLoop(platform_, isolate_)) + {} + } while (!terminated_); + terminated_ = false; + running_nested_loop_ = false; + } + + void quitMessageLoopOnPause() override { + terminated_ = true; + } + + private: + Agent* agent_; + v8::Isolate* isolate_; + v8::Platform* platform_; + bool terminated_; + bool running_nested_loop_; +}; + +Agent::Agent(Environment* env) : port_(9229), + wait_(false), + connected_(false), + shutting_down_(false), + parent_env_(env), + client_socket_(nullptr), + inspector_(nullptr), + platform_(nullptr), + dispatching_messages_(false) { + int err; + err = uv_sem_init(&start_sem_, 0); + CHECK_EQ(err, 0); +} + +Agent::~Agent() { + if (!inspector_) + return; + uv_mutex_destroy(&queue_lock_); + uv_mutex_destroy(&pause_lock_); + uv_cond_destroy(&pause_cond_); + uv_close(reinterpret_cast<uv_handle_t*>(&data_written_), nullptr); +} + +void Agent::Start(v8::Platform* platform, int port, bool wait) { + auto env = parent_env_; + inspector_ = new V8NodeInspector(this, env, platform); + + int err; + + platform_ = platform; + + err = uv_loop_init(&child_loop_); + CHECK_EQ(err, 0); + err = uv_async_init(env->event_loop(), &data_written_, nullptr); + CHECK_EQ(err, 0); + err = uv_mutex_init(&queue_lock_); + CHECK_EQ(err, 0); + err = uv_mutex_init(&pause_lock_); + CHECK_EQ(err, 0); + err = uv_cond_init(&pause_cond_); + CHECK_EQ(err, 0); + + uv_unref(reinterpret_cast<uv_handle_t*>(&data_written_)); + + port_ = port; + wait_ = wait; + + err = uv_thread_create(&thread_, Agent::ThreadCbIO, this); + CHECK_EQ(err, 0); + uv_sem_wait(&start_sem_); + + if (wait) { + // Flush messages in case of wait to connect, see OnRemoteDataIO on how it + // should be fixed. + SetConnected(true); + PostMessages(); + } +} + +void Agent::Stop() { + // TODO(repenaxa): hop on the right thread. + DisconnectAndDisposeIO(client_socket_); + int err = uv_thread_join(&thread_); + CHECK_EQ(err, 0); + + uv_run(&child_loop_, UV_RUN_NOWAIT); + + err = uv_loop_close(&child_loop_); + CHECK_EQ(err, 0); + delete inspector_; +} + +bool Agent::IsStarted() { + return !!platform_; +} + +void Agent::WaitForDisconnect() { + shutting_down_ = true; + fprintf(stderr, "Waiting for the debugger to disconnect...\n"); + inspector_->runMessageLoopOnPause(0); +} + +// static +void Agent::ThreadCbIO(void* agent) { + static_cast<Agent*>(agent)->WorkerRunIO(); +} + +// static +void Agent::OnSocketConnectionIO(uv_stream_t* server, int status) { + if (status == 0) { + inspector_socket_t* socket = + static_cast<inspector_socket_t*>(malloc(sizeof(*socket))); + ASSERT_NE(nullptr, socket); + memset(socket, 0, sizeof(*socket)); + socket->data = server->data; + if (inspector_accept(server, socket, Agent::OnInspectorHandshakeIO) != 0) { + free(socket); + } + } +} + +// static +bool Agent::OnInspectorHandshakeIO(inspector_socket_t* socket, + enum inspector_handshake_event state, + const char* path) { + Agent* agent = static_cast<Agent*>(socket->data); + switch (state) { + case kInspectorHandshakeHttpGet: + return RespondToGet(socket, path); + case kInspectorHandshakeUpgrading: + return AcceptsConnection(socket, path); + case kInspectorHandshakeUpgraded: + agent->OnInspectorConnectionIO(socket); + return true; + case kInspectorHandshakeFailed: + return false; + default: + UNREACHABLE(); + } +} + +// static +void Agent::OnRemoteDataIO(uv_stream_t* stream, + ssize_t read, + const uv_buf_t* b) { + inspector_socket_t* socket = static_cast<inspector_socket_t*>(stream->data); + Agent* agent = static_cast<Agent*>(socket->data); + if (read > 0) { + std::string str(b->base, read); + agent->PushPendingMessage(&agent->message_queue_, str); + free(b->base); + + // TODO(pfeldman): Instead of blocking execution while debugger + // engages, node should wait for the run callback from the remote client + // and initiate its startup. This is a change to node.cc that should be + // upstreamed separately. + if (agent->wait_ && str.find("\"Runtime.run\"") != std::string::npos) { + agent->wait_ = false; + uv_sem_post(&agent->start_sem_); + } + + agent->platform_->CallOnForegroundThread(agent->parent_env_->isolate(), + new DispatchOnInspectorBackendTask(agent)); + agent->parent_env_->isolate() + ->RequestInterrupt(InterruptCallback, agent); + uv_async_send(&agent->data_written_); + } else if (read < 0) { + if (agent->client_socket_ == socket) { + agent->client_socket_ = nullptr; + } + DisconnectAndDisposeIO(socket); + } else { + // EOF + if (agent->client_socket_ == socket) { + agent->client_socket_ = nullptr; + agent->platform_->CallOnForegroundThread(agent->parent_env_->isolate(), + new SetConnectedTask(agent, false)); + uv_async_send(&agent->data_written_); + } + } + uv_cond_broadcast(&agent->pause_cond_); +} + +void Agent::PushPendingMessage(std::vector<std::string>* queue, + const std::string& message) { + uv_mutex_lock(&queue_lock_); + queue->push_back(message); + uv_mutex_unlock(&queue_lock_); +} + +void Agent::SwapBehindLock(std::vector<std::string> Agent::*queue, + std::vector<std::string>* output) { + uv_mutex_lock(&queue_lock_); + (this->*queue).swap(*output); + uv_mutex_unlock(&queue_lock_); +} + +// static +void Agent::WriteCbIO(uv_async_t* async) { + Agent* agent = static_cast<Agent*>(async->data); + inspector_socket_t* socket = agent->client_socket_; + if (socket) { + std::vector<std::string> outgoing_messages; + agent->SwapBehindLock(&Agent::outgoing_message_queue_, &outgoing_messages); + for (auto const& message : outgoing_messages) + inspector_write(socket, message.c_str(), message.length()); + } +} + +void Agent::WorkerRunIO() { + sockaddr_in addr; + uv_tcp_t server; + int err = uv_async_init(&child_loop_, &io_thread_req_, Agent::WriteCbIO); + CHECK_EQ(0, err); + io_thread_req_.data = this; + uv_tcp_init(&child_loop_, &server); + uv_ip4_addr("0.0.0.0", port_, &addr); + server.data = this; + err = uv_tcp_bind(&server, + reinterpret_cast<const struct sockaddr*>(&addr), 0); + if (err == 0) { + err = uv_listen(reinterpret_cast<uv_stream_t*>(&server), 1, + OnSocketConnectionIO); + } + if (err == 0) { + PrintDebuggerReadyMessage(port_); + } else { + fprintf(stderr, "Unable to open devtools socket: %s\n", uv_strerror(err)); + ABORT(); + } + if (!wait_) { + uv_sem_post(&start_sem_); + } + uv_run(&child_loop_, UV_RUN_DEFAULT); + uv_close(reinterpret_cast<uv_handle_t*>(&io_thread_req_), nullptr); + uv_close(reinterpret_cast<uv_handle_t*>(&server), nullptr); + uv_run(&child_loop_, UV_RUN_DEFAULT); +} + +void Agent::OnInspectorConnectionIO(inspector_socket_t* socket) { + if (client_socket_) { + return; + } + client_socket_ = socket; + inspector_read_start(socket, OnBufferAlloc, Agent::OnRemoteDataIO); + platform_->CallOnForegroundThread(parent_env_->isolate(), + new SetConnectedTask(this, true)); +} + +void Agent::PostMessages() { + if (dispatching_messages_) + return; + dispatching_messages_ = true; + std::vector<std::string> messages; + SwapBehindLock(&Agent::message_queue_, &messages); + for (auto const& message : messages) + inspector_->dispatchMessageFromFrontend( + String16::fromUTF8(message.c_str(), message.length())); + uv_async_send(&data_written_); + dispatching_messages_ = false; +} + +void Agent::SetConnected(bool connected) { + if (connected_ == connected) + return; + + connected_ = connected; + if (connected) { + fprintf(stderr, "Debugger attached.\n"); + inspector_->connectFrontend(new ChannelImpl(this)); + } else { + if (!shutting_down_) + PrintDebuggerReadyMessage(port_); + inspector_->quitMessageLoopOnPause(); + inspector_->disconnectFrontend(); + } +} + +void Agent::Write(const std::string& message) { + PushPendingMessage(&outgoing_message_queue_, message); + ASSERT_EQ(0, uv_async_send(&io_thread_req_)); +} +} // namespace debugger +} // namespace node diff --git a/src/inspector_agent.h b/src/inspector_agent.h new file mode 100644 index 00000000000000..65a4abeff7db54 --- /dev/null +++ b/src/inspector_agent.h @@ -0,0 +1,97 @@ +#ifndef SRC_INSPECTOR_AGENT_H_ +#define SRC_INSPECTOR_AGENT_H_ + +#if !HAVE_INSPECTOR +#error("This header can only be used when inspector is enabled") +#endif + +#include "inspector_socket.h" +#include "uv.h" +#include "v8.h" +#include "util.h" + +#include <string> +#include <vector> + +namespace blink { +class V8Inspector; +} + +// Forward declaration to break recursive dependency chain with src/env.h. +namespace node { +class Environment; +} // namespace node + +namespace node { +namespace inspector { + +class ChannelImpl; + +class Agent { + public: + explicit Agent(node::Environment* env); + ~Agent(); + + // Start the inspector agent thread + void Start(v8::Platform* platform, int port, bool wait); + // Stop the inspector agent + void Stop(); + + bool IsStarted(); + bool connected() { return connected_; } + void WaitForDisconnect(); + + protected: + static void ThreadCbIO(void* agent); + static void OnSocketConnectionIO(uv_stream_t* server, int status); + static bool OnInspectorHandshakeIO(inspector_socket_t* socket, + enum inspector_handshake_event state, + const char* path); + static void OnRemoteDataIO(uv_stream_t* stream, ssize_t read, + const uv_buf_t* b); + static void WriteCbIO(uv_async_t* async); + + void WorkerRunIO(); + void OnInspectorConnectionIO(inspector_socket_t* socket); + void PushPendingMessage(std::vector<std::string>* queue, + const std::string& message); + void SwapBehindLock(std::vector<std::string> Agent::*queue, + std::vector<std::string>* output); + void PostMessages(); + void SetConnected(bool connected); + void Write(const std::string& message); + + uv_sem_t start_sem_; + uv_cond_t pause_cond_; + uv_mutex_t queue_lock_; + uv_mutex_t pause_lock_; + uv_thread_t thread_; + uv_loop_t child_loop_; + uv_tcp_t server_; + + int port_; + bool wait_; + bool connected_; + bool shutting_down_; + node::Environment* parent_env_; + + uv_async_t data_written_; + uv_async_t io_thread_req_; + inspector_socket_t* client_socket_; + blink::V8Inspector* inspector_; + v8::Platform* platform_; + std::vector<std::string> message_queue_; + std::vector<std::string> outgoing_message_queue_; + bool dispatching_messages_; + + friend class ChannelImpl; + friend class DispatchOnInspectorBackendTask; + friend class SetConnectedTask; + friend class V8NodeInspector; + friend void InterruptCallback(v8::Isolate*, void* agent); +}; + +} // namespace inspector +} // namespace node + +#endif // SRC_INSPECTOR_AGENT_H_ diff --git a/src/inspector_socket.cc b/src/inspector_socket.cc new file mode 100644 index 00000000000000..cb248ec59feb1a --- /dev/null +++ b/src/inspector_socket.cc @@ -0,0 +1,679 @@ +#include "inspector_socket.h" + +#define NODE_WANT_INTERNALS 1 +#include "base64.h" + +#include "openssl/sha.h" // Sha-1 hash + +#include <string.h> +#include <vector> + +#define ACCEPT_KEY_LENGTH base64_encoded_size(20) +#define BUFFER_GROWTH_CHUNK_SIZE 1024 + +#define DUMP_READS 0 +#define DUMP_WRITES 0 + +static const char CLOSE_FRAME[] = {'\x88', '\x00'}; + +struct http_parsing_state_s { + http_parser parser; + http_parser_settings parser_settings; + handshake_cb callback; + bool parsing_value; + char* ws_key; + char* path; + char* current_header; +}; + +struct ws_state_s { + uv_alloc_cb alloc_cb; + uv_read_cb read_cb; + inspector_cb close_cb; + bool close_sent; + bool received_close; +}; + +enum ws_decode_result { + FRAME_OK, FRAME_INCOMPLETE, FRAME_CLOSE, FRAME_ERROR +}; + +#if DUMP_READS || DUMP_WRITES +static void dump_hex(const char* buf, size_t len) { + const char* ptr = buf; + const char* end = ptr + len; + const char* cptr; + char c; + int i; + + while (ptr < end) { + cptr = ptr; + for (i = 0; i < 16 && ptr < end; i++) { + printf("%2.2X ", *(ptr++)); + } + for (i = 72 - (i * 4); i > 0; i--) { + printf(" "); + } + for (i = 0; i < 16 && cptr < end; i++) { + c = *(cptr++); + printf("%c", (c > 0x19) ? c : '.'); + } + printf("\n"); + } + printf("\n\n"); +} +#endif + +static void dispose_inspector(uv_handle_t* handle) { + inspector_socket_t* inspector = + reinterpret_cast<inspector_socket_t*>(handle->data); + inspector_cb close = + inspector->ws_mode ? inspector->ws_state->close_cb : nullptr; + free(inspector->buffer); + free(inspector->ws_state); + inspector->ws_state = nullptr; + inspector->buffer = nullptr; + inspector->buffer_size = 0; + inspector->data_len = 0; + inspector->last_read_end = 0; + if (close) { + close(inspector, 0); + } +} + +static void close_connection(inspector_socket_t* inspector) { + uv_handle_t* socket = reinterpret_cast<uv_handle_t*>(&inspector->client); + if (!uv_is_closing(socket)) { + uv_read_stop(reinterpret_cast<uv_stream_t*>(socket)); + uv_close(socket, dispose_inspector); + } else if (inspector->ws_state->close_cb) { + inspector->ws_state->close_cb(inspector, 0); + } +} + +// Cleanup +static void write_request_cleanup(uv_write_t* req, int status) { + free((reinterpret_cast<uv_buf_t*>(req->data))->base); + free(req->data); + free(req); +} + +static int write_to_client(inspector_socket_t* inspector, + const char* msg, + size_t len, + uv_write_cb write_cb = write_request_cleanup) { +#if DUMP_WRITES + printf("%s (%ld bytes):\n", __FUNCTION__, len); + dump_hex(msg, len); +#endif + + // Freed in write_request_cleanup + uv_buf_t* buf = reinterpret_cast<uv_buf_t*>(malloc(sizeof(uv_buf_t))); + uv_write_t* req = reinterpret_cast<uv_write_t*>(malloc(sizeof(uv_write_t))); + CHECK_NE(buf, nullptr); + CHECK_NE(req, nullptr); + memset(req, 0, sizeof(*req)); + buf->base = reinterpret_cast<char*>(malloc(len)); + + CHECK_NE(buf->base, nullptr); + + memcpy(buf->base, msg, len); + buf->len = len; + req->data = buf; + + uv_stream_t* stream = reinterpret_cast<uv_stream_t*>(&inspector->client); + return uv_write(req, stream, buf, 1, write_cb) < 0; +} + +// Constants for hybi-10 frame format. + +typedef int OpCode; + +const OpCode kOpCodeContinuation = 0x0; +const OpCode kOpCodeText = 0x1; +const OpCode kOpCodeBinary = 0x2; +const OpCode kOpCodeClose = 0x8; +const OpCode kOpCodePing = 0x9; +const OpCode kOpCodePong = 0xA; + +const unsigned char kFinalBit = 0x80; +const unsigned char kReserved1Bit = 0x40; +const unsigned char kReserved2Bit = 0x20; +const unsigned char kReserved3Bit = 0x10; +const unsigned char kOpCodeMask = 0xF; +const unsigned char kMaskBit = 0x80; +const unsigned char kPayloadLengthMask = 0x7F; + +const size_t kMaxSingleBytePayloadLength = 125; +const size_t kTwoBytePayloadLengthField = 126; +const size_t kEightBytePayloadLengthField = 127; +const size_t kMaskingKeyWidthInBytes = 4; + +static std::vector<char> encode_frame_hybi17(const char* message, + size_t data_length) { + std::vector<char> frame; + OpCode op_code = kOpCodeText; + frame.push_back(kFinalBit | op_code); + if (data_length <= kMaxSingleBytePayloadLength) { + frame.push_back(static_cast<char>(data_length)); + } else if (data_length <= 0xFFFF) { + frame.push_back(kTwoBytePayloadLengthField); + frame.push_back((data_length & 0xFF00) >> 8); + frame.push_back(data_length & 0xFF); + } else { + frame.push_back(kEightBytePayloadLengthField); + char extended_payload_length[8]; + size_t remaining = data_length; + // Fill the length into extended_payload_length in the network byte order. + for (int i = 0; i < 8; ++i) { + extended_payload_length[7 - i] = remaining & 0xFF; + remaining >>= 8; + } + frame.insert(frame.end(), extended_payload_length, + extended_payload_length + 8); + ASSERT_EQ(0, remaining); + } + frame.insert(frame.end(), message, message + data_length); + return frame; +} + +static ws_decode_result decode_frame_hybi17(const char* buffer_begin, + size_t data_length, + bool client_frame, + int* bytes_consumed, + std::vector<char>* output, + bool* compressed) { + *bytes_consumed = 0; + if (data_length < 2) + return FRAME_INCOMPLETE; + + const char* p = buffer_begin; + const char* buffer_end = p + data_length; + + unsigned char first_byte = *p++; + unsigned char second_byte = *p++; + + bool final = (first_byte & kFinalBit) != 0; + bool reserved1 = (first_byte & kReserved1Bit) != 0; + bool reserved2 = (first_byte & kReserved2Bit) != 0; + bool reserved3 = (first_byte & kReserved3Bit) != 0; + int op_code = first_byte & kOpCodeMask; + bool masked = (second_byte & kMaskBit) != 0; + *compressed = reserved1; + if (!final || reserved2 || reserved3) + return FRAME_ERROR; // Only compression extension is supported. + + bool closed = false; + switch (op_code) { + case kOpCodeClose: + closed = true; + break; + case kOpCodeText: + break; + case kOpCodeBinary: // We don't support binary frames yet. + case kOpCodeContinuation: // We don't support binary frames yet. + case kOpCodePing: // We don't support binary frames yet. + case kOpCodePong: // We don't support binary frames yet. + default: + return FRAME_ERROR; + } + + // In Hybi-17 spec client MUST mask its frame. + if (client_frame && !masked) { + return FRAME_ERROR; + } + + uint64_t payload_length64 = second_byte & kPayloadLengthMask; + if (payload_length64 > kMaxSingleBytePayloadLength) { + int extended_payload_length_size; + if (payload_length64 == kTwoBytePayloadLengthField) { + extended_payload_length_size = 2; + } else if (payload_length64 == kEightBytePayloadLengthField) { + extended_payload_length_size = 8; + } else { + return FRAME_ERROR; + } + if (buffer_end - p < extended_payload_length_size) + return FRAME_INCOMPLETE; + payload_length64 = 0; + for (int i = 0; i < extended_payload_length_size; ++i) { + payload_length64 <<= 8; + payload_length64 |= static_cast<unsigned char>(*p++); + } + } + + static const uint64_t max_payload_length = 0x7FFFFFFFFFFFFFFFull; + static const size_t max_length = SIZE_MAX; + if (payload_length64 > max_payload_length || + payload_length64 > max_length - kMaskingKeyWidthInBytes) { + // WebSocket frame length too large. + return FRAME_ERROR; + } + size_t payload_length = static_cast<size_t>(payload_length64); + + if (data_length - kMaskingKeyWidthInBytes < payload_length) + return FRAME_INCOMPLETE; + + const char* masking_key = p; + const char* payload = p + kMaskingKeyWidthInBytes; + for (size_t i = 0; i < payload_length; ++i) // Unmask the payload. + output->insert(output->end(), + payload[i] ^ masking_key[i % kMaskingKeyWidthInBytes]); + + size_t pos = p + kMaskingKeyWidthInBytes + payload_length - buffer_begin; + *bytes_consumed = pos; + return closed ? FRAME_CLOSE : FRAME_OK; +} + +static void invoke_read_callback(inspector_socket_t* inspector, + int status, const uv_buf_t* buf) { + if (inspector->ws_state->read_cb) { + inspector->ws_state->read_cb( + reinterpret_cast<uv_stream_t*>(&inspector->client), status, buf); + } +} + +static void shutdown_complete(inspector_socket_t* inspector) { + if (inspector->ws_state->close_cb) { + inspector->ws_state->close_cb(inspector, 0); + } + close_connection(inspector); +} + +static void on_close_frame_written(uv_write_t* write, int status) { + inspector_socket_t* inspector = + reinterpret_cast<inspector_socket_t*>(write->handle->data); + write_request_cleanup(write, status); + inspector->ws_state->close_sent = true; + if (inspector->ws_state->received_close) { + shutdown_complete(inspector); + } +} + +static void close_frame_received(inspector_socket_t* inspector) { + inspector->ws_state->received_close = true; + if (!inspector->ws_state->close_sent) { + invoke_read_callback(inspector, 0, 0); + write_to_client(inspector, CLOSE_FRAME, sizeof(CLOSE_FRAME), + on_close_frame_written); + } else { + shutdown_complete(inspector); + } +} + +static int parse_ws_frames(inspector_socket_t* inspector, size_t len) { + int bytes_consumed = 0; + std::vector<char> output; + bool compressed = false; + + ws_decode_result r = decode_frame_hybi17(inspector->buffer, + len, true /* client_frame */, + &bytes_consumed, &output, + &compressed); + // Compressed frame means client is ignoring the headers and misbehaves + if (compressed || r == FRAME_ERROR) { + invoke_read_callback(inspector, UV_EPROTO, nullptr); + close_connection(inspector); + bytes_consumed = 0; + } else if (r == FRAME_CLOSE) { + close_frame_received(inspector); + bytes_consumed = 0; + } else if (r == FRAME_OK && inspector->ws_state->alloc_cb + && inspector->ws_state->read_cb) { + uv_buf_t buffer; + size_t len = output.size(); + inspector->ws_state->alloc_cb( + reinterpret_cast<uv_handle_t*>(&inspector->client), + len, &buffer); + CHECK_GE(buffer.len, len); + memcpy(buffer.base, &output[0], len); + invoke_read_callback(inspector, len, &buffer); + } + return bytes_consumed; +} + +static void prepare_buffer(uv_handle_t* stream, size_t len, uv_buf_t* buf) { + inspector_socket_t* inspector = + reinterpret_cast<inspector_socket_t*>(stream->data); + + if (len > (inspector->buffer_size - inspector->data_len)) { + int new_size = (inspector->data_len + len + BUFFER_GROWTH_CHUNK_SIZE - 1) / + BUFFER_GROWTH_CHUNK_SIZE * + BUFFER_GROWTH_CHUNK_SIZE; + inspector->buffer_size = new_size; + inspector->buffer = reinterpret_cast<char*>(realloc(inspector->buffer, + inspector->buffer_size)); + ASSERT_NE(inspector->buffer, nullptr); + } + buf->base = inspector->buffer + inspector->data_len; + buf->len = len; + inspector->data_len += len; +} + +static void websockets_data_cb(uv_stream_t* stream, ssize_t nread, + const uv_buf_t* buf) { + inspector_socket_t* inspector = + reinterpret_cast<inspector_socket_t*>(stream->data); + if (nread < 0 || nread == UV_EOF) { + inspector->connection_eof = true; + if (!inspector->shutting_down && inspector->ws_state->read_cb) { + inspector->ws_state->read_cb(stream, nread, nullptr); + } + } else { + #if DUMP_READS + printf("%s read %ld bytes\n", __FUNCTION__, nread); + if (nread > 0) { + dump_hex(buf->base, nread); + } + #endif + // 1. Move read bytes to continue the buffer + // Should be same as this is supposedly last buffer + ASSERT_EQ(buf->base + buf->len, inspector->buffer + inspector->data_len); + + // Should be noop... + memmove(inspector->buffer + inspector->last_read_end, buf->base, nread); + inspector->last_read_end += nread; + + // 2. Parse. + int processed = 0; + do { + processed = parse_ws_frames(inspector, inspector->last_read_end); + // 3. Fix the buffer size & length + if (processed > 0) { + memmove(inspector->buffer, inspector->buffer + processed, + inspector->last_read_end - processed); + inspector->last_read_end -= processed; + inspector->data_len = inspector->last_read_end; + } + } while (processed > 0 && inspector->data_len > 0); + } +} + +int inspector_read_start(inspector_socket_t* inspector, + uv_alloc_cb alloc_cb, uv_read_cb read_cb) { + ASSERT(inspector->ws_mode); + ASSERT(!inspector->shutting_down || read_cb == nullptr); + inspector->ws_state->close_sent = false; + inspector->ws_state->alloc_cb = alloc_cb; + inspector->ws_state->read_cb = read_cb; + int err = + uv_read_start(reinterpret_cast<uv_stream_t*>(&inspector->client), + prepare_buffer, + websockets_data_cb); + if (err < 0) { + close_connection(inspector); + } + return err; +} + +void inspector_read_stop(inspector_socket_t* inspector) { + uv_read_stop(reinterpret_cast<uv_stream_t*>(&inspector->client)); + inspector->ws_state->alloc_cb = nullptr; + inspector->ws_state->read_cb = nullptr; +} + +static void generate_accept_string(const char* client_key, char* buffer) { + // Magic string from websockets spec. + const char ws_magic[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + size_t key_len = strlen(client_key); + size_t magic_len = sizeof(ws_magic) - 1; + + char* buf = reinterpret_cast<char*>(malloc(key_len + magic_len)); + CHECK_NE(buf, nullptr); + memcpy(buf, client_key, key_len); + memcpy(buf + key_len, ws_magic, magic_len); + char hash[20]; + SHA1((unsigned char*) buf, key_len + magic_len, (unsigned char*) hash); + free(buf); + node::base64_encode(hash, 20, buffer, ACCEPT_KEY_LENGTH); + buffer[ACCEPT_KEY_LENGTH] = '\0'; +} + +static void append(char** value, const char* string, size_t length) { + const size_t INCREMENT = 500; // There should never be more then 1 chunk... + + int current_len = *value ? strlen(*value) : 0; + int new_len = current_len + length; + int adjusted = (new_len / INCREMENT + 1) * INCREMENT; + *value = reinterpret_cast<char*>(realloc(*value, adjusted)); + memcpy(*value + current_len, string, length); + (*value)[new_len] = '\0'; +} + +static int header_value_cb(http_parser* parser, const char* at, size_t length) { + char SEC_WEBSOCKET_KEY_HEADER[] = "Sec-WebSocket-Key"; + struct http_parsing_state_s* state = (struct http_parsing_state_s*) + (reinterpret_cast<inspector_socket_t*>(parser->data))->http_parsing_state; + state->parsing_value = true; + if (state->current_header && strncmp(state->current_header, + SEC_WEBSOCKET_KEY_HEADER, + sizeof(SEC_WEBSOCKET_KEY_HEADER)) == 0) { + append(&state->ws_key, at, length); + } + return 0; +} + +static int header_field_cb(http_parser* parser, const char* at, size_t length) { + struct http_parsing_state_s* state = (struct http_parsing_state_s*) + (reinterpret_cast<inspector_socket_t*>(parser->data))->http_parsing_state; + if (state->parsing_value) { + state->parsing_value = false; + if (state->current_header) + state->current_header[0] = '\0'; + } + append(&state->current_header, at, length); + return 0; +} + +static int path_cb(http_parser* parser, const char* at, size_t length) { + struct http_parsing_state_s* state = (struct http_parsing_state_s*) + (reinterpret_cast<inspector_socket_t*>(parser->data))->http_parsing_state; + append(&state->path, at, length); + return 0; +} + +static void handshake_complete(inspector_socket_t* inspector) { + uv_read_stop(reinterpret_cast<uv_stream_t*>(&inspector->client)); + handshake_cb callback = inspector->http_parsing_state->callback; + inspector->ws_state = (struct ws_state_s*) malloc(sizeof(struct ws_state_s)); + ASSERT_NE(nullptr, inspector->ws_state); + memset(inspector->ws_state, 0, sizeof(struct ws_state_s)); + inspector->last_read_end = 0; + inspector->ws_mode = true; + callback(inspector, kInspectorHandshakeUpgraded, + inspector->http_parsing_state->path); +} + +static void cleanup_http_parsing_state(struct http_parsing_state_s* state) { + free(state->current_header); + free(state->path); + free(state->ws_key); + free(state); +} + +static void handshake_failed(inspector_socket_t* inspector) { + http_parsing_state_s* state = inspector->http_parsing_state; + const char HANDSHAKE_FAILED_RESPONSE[] = + "HTTP/1.0 400 Bad Request\r\n" + "Content-Type: text/html; charset=UTF-8\r\n\r\n" + "WebSockets request was expected\r\n"; + write_to_client(inspector, HANDSHAKE_FAILED_RESPONSE, + sizeof(HANDSHAKE_FAILED_RESPONSE) - 1); + close_connection(inspector); + inspector->http_parsing_state = nullptr; + state->callback(inspector, kInspectorHandshakeFailed, state->path); +} + +// init_handshake references message_complete_cb +static void init_handshake(inspector_socket_t* inspector); + +static int message_complete_cb(http_parser* parser) { + inspector_socket_t* inspector = + reinterpret_cast<inspector_socket_t*>(parser->data); + struct http_parsing_state_s* state = + (struct http_parsing_state_s*) inspector->http_parsing_state; + if (parser->method != HTTP_GET) { + handshake_failed(inspector); + } else if (!parser->upgrade) { + if (state->callback(inspector, kInspectorHandshakeHttpGet, state->path)) { + init_handshake(inspector); + } else { + handshake_failed(inspector); + } + } else if (!state->ws_key) { + handshake_failed(inspector); + } else if (state->callback(inspector, kInspectorHandshakeUpgrading, + state->path)) { + char accept_string[ACCEPT_KEY_LENGTH + 1]; + generate_accept_string(state->ws_key, accept_string); + + const char accept_ws_prefix[] = "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: "; + const char accept_ws_suffix[] = "\r\n\r\n"; + // Format has two chars (%s) that are replaced with actual key + char accept_response[sizeof(accept_ws_prefix) - 1 + + sizeof(accept_ws_suffix) - 1 + + ACCEPT_KEY_LENGTH]; + memcpy(accept_response, accept_ws_prefix, sizeof(accept_ws_prefix) - 1); + memcpy(accept_response + sizeof(accept_ws_prefix) - 1, + accept_string, ACCEPT_KEY_LENGTH); + memcpy(accept_response + sizeof(accept_ws_prefix) - 1 + ACCEPT_KEY_LENGTH, + accept_ws_suffix, sizeof(accept_ws_suffix) - 1); + int len = sizeof(accept_response); + if (write_to_client(inspector, accept_response, len) >= 0) { + handshake_complete(inspector); + } else { + state->callback(inspector, kInspectorHandshakeFailed, nullptr); + close_connection(inspector); + } + inspector->http_parsing_state = nullptr; + } else { + handshake_failed(inspector); + } + return 0; +} + +static void data_received_cb(uv_stream_s* client, ssize_t nread, + const uv_buf_t* buf) { +#if DUMP_READS + if (nread >= 0) { + printf("%s (%ld bytes)\n", __FUNCTION__, nread); + dump_hex(buf->base, nread); + } else { + printf("[%s:%d] %s\n", __FUNCTION__, __LINE__, uv_err_name(nread)); + } +#endif + inspector_socket_t* inspector = + reinterpret_cast<inspector_socket_t*>((client->data)); + http_parsing_state_s* state = inspector->http_parsing_state; + if (nread < 0 || nread == UV_EOF) { + inspector->http_parsing_state->callback(inspector, + kInspectorHandshakeFailed, + nullptr); + close_connection(inspector); + inspector->http_parsing_state = nullptr; + } else { + http_parser* parser = &state->parser; + ssize_t parsed = http_parser_execute(parser, &state->parser_settings, + inspector->buffer, + nread); + if (parsed < nread) { + handshake_failed(inspector); + } + inspector->data_len = 0; + } + + if (inspector->http_parsing_state == nullptr) { + cleanup_http_parsing_state(state); + } +} + +static void init_handshake(inspector_socket_t* inspector) { + http_parsing_state_s* state = inspector->http_parsing_state; + CHECK_NE(state, nullptr); + if (state->current_header) { + state->current_header[0] = '\0'; + } + if (state->ws_key) { + state->ws_key[0] = '\0'; + } + if (state->path) { + state->path[0] = '\0'; + } + http_parser_init(&state->parser, HTTP_REQUEST); + state->parser.data = inspector; + http_parser_settings* settings = &state->parser_settings; + http_parser_settings_init(settings); + settings->on_header_field = header_field_cb; + settings->on_header_value = header_value_cb; + settings->on_message_complete = message_complete_cb; + settings->on_url = path_cb; +} + +int inspector_accept(uv_stream_t* server, inspector_socket_t* inspector, + handshake_cb callback) { + ASSERT_NE(callback, nullptr); + // The only field that users should care about. + void* data = inspector->data; + memset(inspector, 0, sizeof(*inspector)); + inspector->data = data; + + inspector->http_parsing_state = (struct http_parsing_state_s*) + malloc(sizeof(struct http_parsing_state_s)); + ASSERT_NE(nullptr, inspector->http_parsing_state); + memset(inspector->http_parsing_state, 0, sizeof(struct http_parsing_state_s)); + uv_stream_t* client = reinterpret_cast<uv_stream_t*>(&inspector->client); + CHECK_NE(client, nullptr); + int err = uv_tcp_init(server->loop, &inspector->client); + + if (err == 0) { + err = uv_accept(server, client); + } + if (err == 0) { + client->data = inspector; + init_handshake(inspector); + inspector->http_parsing_state->callback = callback; + err = uv_read_start(client, prepare_buffer, + data_received_cb); + } + if (err != 0) { + uv_close(reinterpret_cast<uv_handle_t*>(client), NULL); + } + return err; +} + +void inspector_write(inspector_socket_t* inspector, const char* data, + size_t len) { + if (inspector->ws_mode) { + std::vector<char> output = encode_frame_hybi17(data, len); + write_to_client(inspector, &output[0], output.size()); + } else { + write_to_client(inspector, data, len); + } +} + +void inspector_close(inspector_socket_t* inspector, + inspector_cb callback) { + // libuv throws assertions when closing stream that's already closed - we + // need to do the same. + ASSERT(!uv_is_closing(reinterpret_cast<uv_handle_t*>(&inspector->client))); + ASSERT(!inspector->shutting_down); + inspector->shutting_down = true; + inspector->ws_state->close_cb = callback; + if (inspector->connection_eof) { + close_connection(inspector); + } else { + inspector_read_stop(inspector); + write_to_client(inspector, CLOSE_FRAME, sizeof(CLOSE_FRAME), + on_close_frame_written); + inspector_read_start(inspector, nullptr, nullptr); + } +} + +bool inspector_is_active(const struct inspector_socket_s* inspector) { + const uv_handle_t* client = + reinterpret_cast<const uv_handle_t*>(&inspector->client); + return !inspector->shutting_down && !uv_is_closing(client); +} diff --git a/src/inspector_socket.h b/src/inspector_socket.h new file mode 100644 index 00000000000000..3e52762e715de5 --- /dev/null +++ b/src/inspector_socket.h @@ -0,0 +1,57 @@ +#ifndef SRC_INSPECTOR_SOCKET_H_ +#define SRC_INSPECTOR_SOCKET_H_ + +#include "http_parser.h" +#include "uv.h" + +enum inspector_handshake_event { + kInspectorHandshakeUpgrading, + kInspectorHandshakeUpgraded, + kInspectorHandshakeHttpGet, + kInspectorHandshakeFailed +}; + +struct inspector_socket_s; + +typedef void (*inspector_cb)(struct inspector_socket_s*, int); +// Notifies as handshake is progressing. Returning false as a response to +// kInspectorHandshakeUpgrading or kInspectorHandshakeHttpGet event will abort +// the connection. inspector_write can be used from the callback. +typedef bool (*handshake_cb)(struct inspector_socket_s*, + enum inspector_handshake_event state, + const char* path); + +struct http_parsing_state_s; +struct ws_state_s; + +struct inspector_socket_s { + void* data; + struct http_parsing_state_s* http_parsing_state; + struct ws_state_s* ws_state; + char* buffer; + size_t buffer_size; + size_t data_len; + size_t last_read_end; + uv_tcp_t client; + bool ws_mode; + bool shutting_down; + bool connection_eof; +}; + +typedef struct inspector_socket_s inspector_socket_t; + +int inspector_accept(uv_stream_t* server, struct inspector_socket_s* inspector, + handshake_cb callback); + +void inspector_close(struct inspector_socket_s* inspector, + inspector_cb callback); + +// Callbacks will receive handles that has inspector in data field... +int inspector_read_start(struct inspector_socket_s* inspector, uv_alloc_cb, + uv_read_cb); +void inspector_read_stop(struct inspector_socket_s* inspector); +void inspector_write(struct inspector_socket_s* inspector, + const char* data, size_t len); +bool inspector_is_active(const struct inspector_socket_s* inspector); + +#endif // SRC_INSPECTOR_SOCKET_H_ diff --git a/src/node.cc b/src/node.cc index f71c2714a830d8..cbb7e6433fbf29 100644 --- a/src/node.cc +++ b/src/node.cc @@ -137,6 +137,9 @@ static bool track_heap_objects = false; static const char* eval_string = nullptr; static unsigned int preload_module_count = 0; static const char** preload_modules = nullptr; +#if HAVE_INSPECTOR +static bool use_inspector = false; +#endif static bool use_debug_agent = false; static bool debug_wait_connect = false; static int debug_port = 5858; @@ -3412,6 +3415,22 @@ static bool ParseDebugOpt(const char* arg) { port = arg + sizeof("--debug-brk=") - 1; } else if (!strncmp(arg, "--debug-port=", sizeof("--debug-port=") - 1)) { port = arg + sizeof("--debug-port=") - 1; +#if HAVE_INSPECTOR + // Specifying both --inspect and --debug means debugging is on, using Chromium + // inspector. + } else if (!strcmp(arg, "--inspect")) { + use_debug_agent = true; + use_inspector = true; + } else if (!strncmp(arg, "--inspect=", sizeof("--inspect=") - 1)) { + use_debug_agent = true; + use_inspector = true; + port = arg + sizeof("--inspect=") - 1; +#else + } else if (!strncmp(arg, "--inspect", sizeof("--inspect") - 1)) { + fprintf(stderr, + "Inspector support is not available with this Node.js build\n"); + return false; +#endif } else { return false; } @@ -3682,10 +3701,19 @@ static void DispatchMessagesDebugAgentCallback(Environment* env) { static void StartDebug(Environment* env, bool wait) { CHECK(!debugger_running); +#if HAVE_INSPECTOR + if (use_inspector) { + env->inspector_agent()->Start(default_platform, debug_port, wait); + debugger_running = true; + } else { +#endif + env->debugger_agent()->set_dispatch_handler( + DispatchMessagesDebugAgentCallback); + debugger_running = env->debugger_agent()->Start(debug_port, wait); +#if HAVE_INSPECTOR + } +#endif - env->debugger_agent()->set_dispatch_handler( - DispatchMessagesDebugAgentCallback); - debugger_running = env->debugger_agent()->Start(debug_port, wait); if (debugger_running == false) { fprintf(stderr, "Starting debugger on port %d failed\n", debug_port); fflush(stderr); @@ -3697,6 +3725,11 @@ static void StartDebug(Environment* env, bool wait) { // Called from the main thread. static void EnableDebug(Environment* env) { CHECK(debugger_running); +#if HAVE_INSPECTOR + if (use_inspector) { + return; + } +#endif // Send message to enable debug in workers HandleScope handle_scope(env->isolate()); @@ -3991,7 +4024,15 @@ static void DebugPause(const FunctionCallbackInfo<Value>& args) { static void DebugEnd(const FunctionCallbackInfo<Value>& args) { if (debugger_running) { Environment* env = Environment::GetCurrent(args); - env->debugger_agent()->Stop(); +#if HAVE_INSPECTOR + if (use_inspector) { + env->inspector_agent()->Stop(); + } else { +#endif + env->debugger_agent()->Stop(); +#if HAVE_INSPECTOR + } +#endif debugger_running = false; } } @@ -4420,6 +4461,24 @@ static void StartNodeInstance(void* arg) { instance_data->set_exit_code(exit_code); RunAtExit(env); +#if HAVE_INSPECTOR + if (env->inspector_agent()->connected()) { + // Restore signal dispositions, the app is done and is no longer + // capable of handling signals. +#ifdef __POSIX__ + struct sigaction act; + memset(&act, 0, sizeof(act)); + for (unsigned nr = 1; nr < 32; nr += 1) { + if (nr == SIGKILL || nr == SIGSTOP || nr == SIGPROF) + continue; + act.sa_handler = (nr == SIGPIPE) ? SIG_IGN : SIG_DFL; + CHECK_EQ(0, sigaction(nr, &act, nullptr)); + } +#endif + env->inspector_agent()->WaitForDisconnect(); + } +#endif + #if defined(LEAK_SANITIZER) __lsan_do_leak_check(); #endif diff --git a/src/node_internals.h b/src/node_internals.h index 2875f5ac798291..64134d9ab8d9b7 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -221,7 +221,7 @@ class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { // by clearing all callbacks that could handle the error. void ClearFatalExceptionHandlers(Environment* env); -enum NodeInstanceType { MAIN, WORKER }; +enum NodeInstanceType { MAIN, WORKER, REMOTE_DEBUG_SERVER }; class NodeInstanceData { public: @@ -265,6 +265,10 @@ class NodeInstanceData { return node_instance_type_ == WORKER; } + bool is_remote_debug_server() { + return node_instance_type_ == REMOTE_DEBUG_SERVER; + } + int argc() { return argc_; } diff --git a/src/signal_wrap.cc b/src/signal_wrap.cc index 3ee0251f9b260e..8d31dbf62330ae 100644 --- a/src/signal_wrap.cc +++ b/src/signal_wrap.cc @@ -65,6 +65,15 @@ class SignalWrap : public HandleWrap { SignalWrap* wrap; ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); int signum = args[0]->Int32Value(); +#if defined(__POSIX__) && defined(HAVE_INSPECTOR) + if (signum == SIGPROF) { + Environment* env = Environment::GetCurrent(args); + if (env->inspector_agent()->IsStarted()) { + fprintf(stderr, "process.on(SIGPROF) is reserved while debugging\n"); + return; + } + } +#endif int err = uv_signal_start(&wrap->handle_, OnSignal, signum); args.GetReturnValue().Set(err); } diff --git a/test/cctest/test_inspector_socket.cc b/test/cctest/test_inspector_socket.cc new file mode 100644 index 00000000000000..ebe4215af5eca4 --- /dev/null +++ b/test/cctest/test_inspector_socket.cc @@ -0,0 +1,864 @@ +#include "inspector_socket.h" + +#include "gtest/gtest.h" + +#define PORT 9444 + +static const int MAX_LOOP_ITERATIONS = 10000; + +#define SPIN_WHILE(condition) \ + { \ + bool timed_out = false; \ + timeout_timer.data = &timed_out; \ + uv_timer_start(&timeout_timer, set_timeout_flag, 5000, 0); \ + while (((condition)) && !timed_out) { \ + uv_run(&loop, UV_RUN_NOWAIT); \ + } \ + ASSERT_FALSE((condition)); \ + uv_timer_stop(&timeout_timer); \ + } + +static uv_timer_t timeout_timer; +static bool connected = false; +static bool inspector_ready = false; +static int handshake_events = 0; +static enum inspector_handshake_event last_event = kInspectorHandshakeHttpGet; +static uv_loop_t loop; +static uv_tcp_t server, client_socket; +static inspector_socket_t inspector; +static char last_path[100]; +static void (*handshake_delegate)(enum inspector_handshake_event state, + const char* path, bool* should_continue); + +struct read_expects { + const char* expected; + size_t expected_len; + size_t pos; + bool read_expected; + bool callback_called; +}; + +static const char HANDSHAKE_REQ[] = "GET /ws/path HTTP/1.1\r\n" + "Host: localhost:9222\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Key: aaa==\r\n" + "Sec-WebSocket-Version: 13\r\n\r\n"; + +static void set_timeout_flag(uv_timer_t* timer) { + *(static_cast<bool*>(timer->data)) = true; +} + +static void stop_if_stop_path(enum inspector_handshake_event state, + const char* path, bool* cont) { + *cont = path == nullptr || strcmp(path, "/close") != 0; +} + +static bool connected_cb(inspector_socket_t* socket, + enum inspector_handshake_event state, + const char* path) { + inspector_ready = state == kInspectorHandshakeUpgraded; + last_event = state; + if (!path) { + strcpy(last_path, "@@@ Nothing Recieved @@@"); + } else { + strncpy(last_path, path, sizeof(last_path) - 1); + } + handshake_events++; + bool should_continue = true; + handshake_delegate(state, path, &should_continue); + return should_continue; +} + +static void on_new_connection(uv_stream_t* server, int status) { + GTEST_ASSERT_EQ(0, status); + connected = true; + inspector_accept(server, reinterpret_cast<inspector_socket_t*>(server->data), + connected_cb); +} + +void write_done(uv_write_t* req, int status) { req->data = nullptr; } + +static void do_write(const char* data, int len) { + uv_write_t req; + bool done = false; + req.data = &done; + uv_buf_t buf[1]; + buf[0].base = const_cast<char *>(data); + buf[0].len = len; + uv_write(&req, reinterpret_cast<uv_stream_t *>(&client_socket), buf, 1, + write_done); + SPIN_WHILE(req.data); +} + +static void buffer_alloc_cb(uv_handle_t* stream, size_t len, uv_buf_t* buf) { + buf->base = static_cast<char *>(malloc(len)); + buf->len = len; +} + +static void check_data_cb(read_expects* expectation, ssize_t nread, + const uv_buf_t* buf, bool* retval) { + *retval = false; + EXPECT_TRUE(nread >= 0 && nread != UV_EOF); + ssize_t i; + char c, actual; + ASSERT_TRUE(expectation->expected_len > 0); + for (i = 0; i < nread && expectation->pos <= expectation->expected_len; i++) { + c = expectation->expected[expectation->pos++]; + actual = buf->base[i]; + if (c != actual) { + fprintf(stderr, "Unexpected character at position %ld\n", + expectation->pos - 1); + GTEST_ASSERT_EQ(c, actual); + } + } + GTEST_ASSERT_EQ(i, nread); + free(buf->base); + if (expectation->pos == expectation->expected_len) { + expectation->read_expected = true; + *retval = true; + } +} + +static void check_data_cb(uv_stream_t* stream, ssize_t nread, + const uv_buf_t* buf) { + bool retval = false; + read_expects* expects = static_cast<read_expects *>(stream->data); + expects->callback_called = true; + check_data_cb(expects, nread, buf, &retval); + if (retval) { + stream->data = nullptr; + uv_read_stop(stream); + } +} + +static read_expects prepare_expects(const char* data, size_t len) { + read_expects expectation; + expectation.expected = data; + expectation.expected_len = len; + expectation.pos = 0; + expectation.read_expected = false; + expectation.callback_called = false; + return expectation; +} + +static void fail_callback(uv_stream_t* stream, ssize_t nread, + const uv_buf_t* buf) { + if (nread < 0) { + fprintf(stderr, "IO error: %s\n", uv_strerror(nread)); + } else { + fprintf(stderr, "Read %ld bytes\n", nread); + } + ASSERT_TRUE(false); // Shouldn't have been called +} + +static void expect_nothing_on_client() { + int err = uv_read_start(reinterpret_cast<uv_stream_t *>(&client_socket), + buffer_alloc_cb, fail_callback); + GTEST_ASSERT_EQ(0, err); + for (int i = 0; i < MAX_LOOP_ITERATIONS; i++) + uv_run(&loop, UV_RUN_NOWAIT); +} + +static void expect_on_client(const char* data, size_t len) { + read_expects expectation = prepare_expects(data, len); + client_socket.data = ℰ + uv_read_start(reinterpret_cast<uv_stream_t *>(&client_socket), + buffer_alloc_cb, check_data_cb); + SPIN_WHILE(!expectation.read_expected); +} + +struct expectations { + char* actual_data; + size_t actual_offset; + size_t actual_end; + int err_code; +}; + +static void grow_expects_buffer(uv_handle_t* stream, size_t size, uv_buf_t* b) { + expectations* expects = static_cast<expectations*>( + (static_cast<inspector_socket_t*>(stream->data))->data); + size_t end = expects->actual_end; + // Grow the buffer in chunks of 64k. + size_t new_length = (end + size + 65535) & ~((size_t) 0xFFFF); + expects->actual_data = + static_cast<char*>(realloc(expects->actual_data, new_length)); + *b = uv_buf_init(expects->actual_data + end, new_length - end); +} + +// static void dump_hex(const char* buf, size_t len) { +// const char* ptr = buf; +// const char* end = ptr + len; +// const char* cptr; +// char c; +// int i; + +// while (ptr < end) { +// cptr = ptr; +// for (i = 0; i < 16 && ptr < end; i++) { +// printf("%2.2X ", *(ptr++)); +// } +// for (i = 72 - (i * 4); i > 0; i--) { +// printf(" "); +// } +// for (i = 0; i < 16 && cptr < end; i++) { +// c = *(cptr++); +// printf("%c", (c > 0x19) ? c : '.'); +// } +// printf("\n"); +// } +// printf("\n\n"); +// } + +static void save_read_data(uv_stream_t* stream, ssize_t nread, + const uv_buf_t* buf) { + expectations* expects =static_cast<expectations*>( + (static_cast<inspector_socket_t*>(stream->data))->data); + expects->err_code = nread < 0 ? nread : 0; + if (nread > 0) { + expects->actual_end += nread; + } +} + +static void setup_inspector_expecting() { + if (inspector.data) { + return; + } + expectations* expects = static_cast<expectations*>(malloc(sizeof(*expects))); + memset(expects, 0, sizeof(*expects)); + inspector.data = expects; + inspector_read_start(&inspector, grow_expects_buffer, save_read_data); +} + +static void expect_on_server(const char* data, size_t len) { + setup_inspector_expecting(); + expectations* expects = static_cast<expectations*>(inspector.data); + for (size_t i = 0; i < len;) { + SPIN_WHILE(expects->actual_offset == expects->actual_end); + for (; i < len && expects->actual_offset < expects->actual_end; i++) { + char actual = expects->actual_data[expects->actual_offset++]; + char expected = data[i]; + if (expected != actual) { + fprintf(stderr, "Character %ld:\n", i); + GTEST_ASSERT_EQ(expected, actual); + } + } + } + expects->actual_end -= expects->actual_offset; + if (!expects->actual_end) { + memmove(expects->actual_data, + expects->actual_data + expects->actual_offset, + expects->actual_end); + } + expects->actual_offset = 0; +} + +static void inspector_record_error_code(uv_stream_t* stream, ssize_t nread, + const uv_buf_t* buf) { + inspector_socket_t *inspector = + reinterpret_cast<inspector_socket_t*>(stream->data); + // Increment instead of assign is to ensure the function is only called once + *(static_cast<int *>(inspector->data)) += nread; +} + +static void expect_server_read_error() { + setup_inspector_expecting(); + expectations* expects = static_cast<expectations*>(inspector.data); + SPIN_WHILE(expects->err_code != UV_EPROTO); +} + +static void expect_handshake() { + const char UPGRADE_RESPONSE[] = + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: Dt87H1OULVZnSJo/KgMUYI7xPCg=\r\n\r\n"; + expect_on_client(UPGRADE_RESPONSE, sizeof(UPGRADE_RESPONSE) - 1); +} + +static void expect_handshake_failure() { + const char UPGRADE_RESPONSE[] = + "HTTP/1.0 400 Bad Request\r\n" + "Content-Type: text/html; charset=UTF-8\r\n\r\n" + "WebSockets request was expected\r\n"; + expect_on_client(UPGRADE_RESPONSE, sizeof(UPGRADE_RESPONSE) - 1); +} + +static bool waiting_to_close = true; + +void handle_closed(uv_handle_t* handle) { waiting_to_close = false; } + +static void really_close(uv_tcp_t* socket) { + waiting_to_close = true; + if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(socket))) { + uv_close(reinterpret_cast<uv_handle_t*>(socket), handle_closed); + SPIN_WHILE(waiting_to_close); + } +} + +// Called when the test leaves inspector socket in active state +static void manual_inspector_socket_cleanup() { + EXPECT_EQ(0, uv_is_active( + reinterpret_cast<uv_handle_t*>(&inspector.client))); + free(inspector.ws_state); + free(inspector.http_parsing_state); + free(inspector.buffer); + inspector.buffer = nullptr; +} + +static void on_connection(uv_connect_t* connect, int status) { + GTEST_ASSERT_EQ(0, status); + connect->data = connect; +} + +class InspectorSocketTest : public ::testing::Test { +protected: + virtual void SetUp() { + handshake_delegate = stop_if_stop_path; + handshake_events = 0; + connected = false; + inspector_ready = false; + last_event = kInspectorHandshakeHttpGet; + uv_loop_init(&loop); + memset(&inspector, 0, sizeof(inspector)); + memset(&server, 0, sizeof(server)); + memset(&client_socket, 0, sizeof(client_socket)); + server.data = &inspector; + sockaddr_in addr; + uv_timer_init(&loop, &timeout_timer); + uv_tcp_init(&loop, &server); + uv_tcp_init(&loop, &client_socket); + uv_ip4_addr("localhost", PORT, &addr); + uv_tcp_bind(&server, reinterpret_cast<const struct sockaddr *>(&addr), 0); + int err = uv_listen(reinterpret_cast<uv_stream_t *>(&server), + 0, on_new_connection); + GTEST_ASSERT_EQ(0, err); + uv_connect_t connect; + connect.data = nullptr; + uv_tcp_connect(&connect, &client_socket, + reinterpret_cast<const sockaddr *>(&addr), on_connection); + uv_tcp_nodelay(&client_socket, 1); // The buffering messes up the test + SPIN_WHILE(!connect.data || !connected); + really_close(&server); + uv_unref(reinterpret_cast<uv_handle_t*>(&server)); + } + + virtual void TearDown() { + really_close(&client_socket); + for (int i = 0; i < MAX_LOOP_ITERATIONS; i++) + uv_run(&loop, UV_RUN_NOWAIT); + EXPECT_EQ(nullptr, inspector.buffer); + uv_stop(&loop); + int err = uv_run(&loop, UV_RUN_ONCE); + if (err != 0) { + uv_print_active_handles(&loop, stderr); + } + EXPECT_EQ(0, err); + expectations* expects = static_cast<expectations*>(inspector.data); + if (expects != nullptr) { + GTEST_ASSERT_EQ(expects->actual_end, expects->actual_offset); + free(expects->actual_data); + expects->actual_data = nullptr; + free(expects); + inspector.data = nullptr; + } + uv_loop_close(&loop); + } +}; + +TEST_F(InspectorSocketTest, ReadsAndWritesInspectorMessage) { + ASSERT_TRUE(connected); + ASSERT_FALSE(inspector_ready); + do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + SPIN_WHILE(!inspector_ready); + expect_handshake(); + + // 2. Brief exchange + const char SERVER_MESSAGE[] = "abcd"; + const char CLIENT_FRAME[] = {'\x81', '\x04', 'a', 'b', 'c', 'd'}; + inspector_write(&inspector, SERVER_MESSAGE, sizeof(SERVER_MESSAGE) - 1); + expect_on_client(CLIENT_FRAME, sizeof(CLIENT_FRAME)); + + const char SERVER_FRAME[] = {'\x81', '\x84', '\x7F', '\xC2', '\x66', + '\x31', '\x4E', '\xF0', '\x55', '\x05'}; + const char CLIENT_MESSAGE[] = "1234"; + do_write(SERVER_FRAME, sizeof(SERVER_FRAME)); + expect_on_server(CLIENT_MESSAGE, sizeof(CLIENT_MESSAGE) - 1); + + // 3. Close + const char CLIENT_CLOSE_FRAME[] = {'\x88', '\x80', '\x2D', + '\x0E', '\x1E', '\xFA'}; + const char SERVER_CLOSE_FRAME[] = {'\x88', '\x00'}; + do_write(CLIENT_CLOSE_FRAME, sizeof(CLIENT_CLOSE_FRAME)); + expect_on_client(SERVER_CLOSE_FRAME, sizeof(SERVER_CLOSE_FRAME)); + GTEST_ASSERT_EQ(0, uv_is_active( + reinterpret_cast<uv_handle_t*>(&client_socket))); +} + +TEST_F(InspectorSocketTest, BufferEdgeCases) { + + do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + expect_handshake(); + + const char MULTIPLE_REQUESTS[] = { + '\x81', '\xCB', '\x76', '\xCA', '\x06', '\x0C', '\x0D', '\xE8', '\x6F', + '\x68', '\x54', '\xF0', '\x37', '\x3E', '\x5A', '\xE8', '\x6B', '\x69', + '\x02', '\xA2', '\x69', '\x68', '\x54', '\xF0', '\x24', '\x5B', '\x19', + '\xB8', '\x6D', '\x69', '\x04', '\xE4', '\x75', '\x69', '\x02', '\x8B', + '\x73', '\x78', '\x19', '\xA9', '\x69', '\x62', '\x18', '\xAF', '\x65', + '\x78', '\x22', '\xA5', '\x51', '\x63', '\x04', '\xA1', '\x63', '\x7E', + '\x05', '\xE8', '\x2A', '\x2E', '\x06', '\xAB', '\x74', '\x6D', '\x1B', + '\xB9', '\x24', '\x36', '\x0D', '\xE8', '\x70', '\x6D', '\x1A', '\xBF', + '\x63', '\x2E', '\x4C', '\xBE', '\x74', '\x79', '\x13', '\xB7', '\x7B', + '\x81', '\xA2', '\xFC', '\x9E', '\x0D', '\x15', '\x87', '\xBC', '\x64', + '\x71', '\xDE', '\xA4', '\x3C', '\x26', '\xD0', '\xBC', '\x60', '\x70', + '\x88', '\xF6', '\x62', '\x71', '\xDE', '\xA4', '\x2F', '\x42', '\x93', + '\xEC', '\x66', '\x70', '\x8E', '\xB0', '\x68', '\x7B', '\x9D', '\xFC', + '\x61', '\x70', '\xDE', '\xE3', '\x81', '\xA4', '\x4E', '\x37', '\xB0', + '\x22', '\x35', '\x15', '\xD9', '\x46', '\x6C', '\x0D', '\x81', '\x16', + '\x62', '\x15', '\xDD', '\x47', '\x3A', '\x5F', '\xDF', '\x46', '\x6C', + '\x0D', '\x92', '\x72', '\x3C', '\x58', '\xD6', '\x4B', '\x22', '\x52', + '\xC2', '\x0C', '\x2B', '\x59', '\xD1', '\x40', '\x22', '\x52', '\x92', + '\x5F', '\x81', '\xCB', '\xCD', '\xF0', '\x30', '\xC5', '\xB6', '\xD2', + '\x59', '\xA1', '\xEF', '\xCA', '\x01', '\xF0', '\xE1', '\xD2', '\x5D', + '\xA0', '\xB9', '\x98', '\x5F', '\xA1', '\xEF', '\xCA', '\x12', '\x95', + '\xBF', '\x9F', '\x56', '\xAC', '\xA1', '\x95', '\x42', '\xEB', '\xBE', + '\x95', '\x44', '\x96', '\xAC', '\x9D', '\x40', '\xA9', '\xA4', '\x9E', + '\x57', '\x8C', '\xA3', '\x84', '\x55', '\xB7', '\xBB', '\x91', '\x5C', + '\xE7', '\xE1', '\xD2', '\x40', '\xA4', '\xBF', '\x91', '\x5D', '\xB6', + '\xEF', '\xCA', '\x4B', '\xE7', '\xA4', '\x9E', '\x44', '\xA0', '\xBF', + '\x86', '\x51', '\xA9', '\xEF', '\xCA', '\x01', '\xF5', '\xFD', '\x8D', + '\x4D', '\x81', '\xA9', '\x74', '\x6B', '\x72', '\x43', '\x0F', '\x49', + '\x1B', '\x27', '\x56', '\x51', '\x43', '\x75', '\x58', '\x49', '\x1F', + '\x26', '\x00', '\x03', '\x1D', '\x27', '\x56', '\x51', '\x50', '\x10', + '\x11', '\x19', '\x04', '\x2A', '\x17', '\x0E', '\x25', '\x2C', '\x06', + '\x00', '\x17', '\x31', '\x5A', '\x0E', '\x1C', '\x22', '\x16', '\x07', + '\x17', '\x61', '\x09', '\x81', '\xB8', '\x7C', '\x1A', '\xEA', '\xEB', + '\x07', '\x38', '\x83', '\x8F', '\x5E', '\x20', '\xDB', '\xDC', '\x50', + '\x38', '\x87', '\x8E', '\x08', '\x72', '\x85', '\x8F', '\x5E', '\x20', + '\xC8', '\xA5', '\x19', '\x6E', '\x9D', '\x84', '\x0E', '\x71', '\xC4', + '\x88', '\x1D', '\x74', '\xAF', '\x86', '\x09', '\x76', '\x8B', '\x9F', + '\x19', '\x54', '\x8F', '\x9F', '\x0B', '\x75', '\x98', '\x80', '\x3F', + '\x75', '\x84', '\x8F', '\x15', '\x6E', '\x83', '\x84', '\x12', '\x69', + '\xC8', '\x96'}; + + const char EXPECT[] = { + "{\"id\":12,\"method\":\"Worker.setAutoconnectToWorkers\"," + "\"params\":{\"value\":true}}" + "{\"id\":13,\"method\":\"Worker.enable\"}" + "{\"id\":14,\"method\":\"Profiler.enable\"}" + "{\"id\":15,\"method\":\"Profiler.setSamplingInterval\"," + "\"params\":{\"interval\":100}}" + "{\"id\":16,\"method\":\"ServiceWorker.enable\"}" + "{\"id\":17,\"method\":\"Network.canEmulateNetworkConditions\"}"}; + + do_write(MULTIPLE_REQUESTS, sizeof(MULTIPLE_REQUESTS)); + expect_on_server(EXPECT, sizeof(EXPECT) - 1); + inspector_read_stop(&inspector); + manual_inspector_socket_cleanup(); +} + +TEST_F(InspectorSocketTest, AcceptsRequestInSeveralWrites) { + ASSERT_TRUE(connected); + ASSERT_FALSE(inspector_ready); + // Specifically, break up the request in the "Sec-WebSocket-Key" header name + // and value + const int write1 = 95; + const int write2 = 5; + const int write3 = sizeof(HANDSHAKE_REQ) - write1 - write2 - 1; + do_write(const_cast<char *>(HANDSHAKE_REQ), write1); + ASSERT_FALSE(inspector_ready); + do_write(const_cast<char *>(HANDSHAKE_REQ) + write1, write2); + ASSERT_FALSE(inspector_ready); + do_write(const_cast<char *>(HANDSHAKE_REQ) + write1 + write2, write3); + SPIN_WHILE(!inspector_ready); + expect_handshake(); + inspector_read_stop(&inspector); + GTEST_ASSERT_EQ(uv_is_active(reinterpret_cast<uv_handle_t*>(&client_socket)), 0); + manual_inspector_socket_cleanup(); +} + +TEST_F(InspectorSocketTest, ExtraTextBeforeRequest) { + last_event = kInspectorHandshakeUpgraded; + char UNCOOL_BRO[] = "Uncool, bro: Text before the first req\r\n"; + do_write(const_cast<char *>(UNCOOL_BRO), sizeof(UNCOOL_BRO) - 1); + + ASSERT_FALSE(inspector_ready); + do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + SPIN_WHILE(last_event != kInspectorHandshakeFailed); + expect_handshake_failure(); + EXPECT_EQ(uv_is_active(reinterpret_cast<uv_handle_t*>(&client_socket)), 0); + EXPECT_EQ(uv_is_active(reinterpret_cast<uv_handle_t*>(&socket)), 0); +} + +TEST_F(InspectorSocketTest, ExtraLettersBeforeRequest) { + char UNCOOL_BRO[] = "Uncool!!"; + do_write(const_cast<char *>(UNCOOL_BRO), sizeof(UNCOOL_BRO) - 1); + + ASSERT_FALSE(inspector_ready); + do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + SPIN_WHILE(last_event != kInspectorHandshakeFailed); + expect_handshake_failure(); + EXPECT_EQ(uv_is_active(reinterpret_cast<uv_handle_t*>(&client_socket)), 0); + EXPECT_EQ(uv_is_active(reinterpret_cast<uv_handle_t*>(&socket)), 0); +} + +TEST_F(InspectorSocketTest, RequestWithoutKey) { + const char BROKEN_REQUEST[] = "GET / HTTP/1.1\r\n" + "Host: localhost:9222\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Version: 13\r\n\r\n"; + ; + + do_write(const_cast<char *>(BROKEN_REQUEST), sizeof(BROKEN_REQUEST) - 1); + SPIN_WHILE(last_event != kInspectorHandshakeFailed); + expect_handshake_failure(); + EXPECT_EQ(uv_is_active(reinterpret_cast<uv_handle_t*>(&client_socket)), 0); + EXPECT_EQ(uv_is_active(reinterpret_cast<uv_handle_t*>(&socket)), 0); +} + +TEST_F(InspectorSocketTest, KillsConnectionOnProtocolViolation) { + ASSERT_TRUE(connected); + ASSERT_FALSE(inspector_ready); + do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + SPIN_WHILE(!inspector_ready); + ASSERT_TRUE(inspector_ready); + expect_handshake(); + const char SERVER_FRAME[] = "I'm not a good WS frame. Nope!"; + do_write(SERVER_FRAME, sizeof(SERVER_FRAME)); + expect_server_read_error(); + GTEST_ASSERT_EQ(uv_is_active(reinterpret_cast<uv_handle_t*>(&client_socket)), 0); +} + +TEST_F(InspectorSocketTest, CanStopReadingFromInspector) { + ASSERT_TRUE(connected); + ASSERT_FALSE(inspector_ready); + do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + expect_handshake(); + ASSERT_TRUE(inspector_ready); + + // 2. Brief exchange + const char SERVER_FRAME[] = {'\x81', '\x84', '\x7F', '\xC2', '\x66', + '\x31', '\x4E', '\xF0', '\x55', '\x05'}; + const char CLIENT_MESSAGE[] = "1234"; + do_write(SERVER_FRAME, sizeof(SERVER_FRAME)); + expect_on_server(CLIENT_MESSAGE, sizeof(CLIENT_MESSAGE) - 1); + + inspector_read_stop(&inspector); + do_write(SERVER_FRAME, sizeof(SERVER_FRAME)); + GTEST_ASSERT_EQ(uv_is_active( + reinterpret_cast<uv_handle_t*>(&client_socket)), 0); + manual_inspector_socket_cleanup(); +} + +static bool inspector_closed; + +void inspector_closed_cb(inspector_socket_t *inspector, int code) { + inspector_closed = true; +} + +TEST_F(InspectorSocketTest, CloseDoesNotNotifyReadCallback) { + inspector_closed = false; + do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + expect_handshake(); + + int error_code = 0; + inspector.data = &error_code; + inspector_read_start(&inspector, buffer_alloc_cb, + inspector_record_error_code); + inspector_close(&inspector, inspector_closed_cb); + char CLOSE_FRAME[] = {'\x88', '\x00'}; + expect_on_client(CLOSE_FRAME, sizeof(CLOSE_FRAME)); + ASSERT_FALSE(inspector_closed); + const char CLIENT_CLOSE_FRAME[] = {'\x88', '\x80', '\x2D', + '\x0E', '\x1E', '\xFA'}; + do_write(CLIENT_CLOSE_FRAME, sizeof(CLIENT_CLOSE_FRAME)); + EXPECT_NE(UV_EOF, error_code); + SPIN_WHILE(!inspector_closed); + inspector.data = nullptr; +} + +TEST_F(InspectorSocketTest, CloseWorksWithoutReadEnabled) { + inspector_closed = false; + do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + expect_handshake(); + inspector_close(&inspector, inspector_closed_cb); + char CLOSE_FRAME[] = {'\x88', '\x00'}; + expect_on_client(CLOSE_FRAME, sizeof(CLOSE_FRAME)); + ASSERT_FALSE(inspector_closed); + const char CLIENT_CLOSE_FRAME[] = {'\x88', '\x80', '\x2D', + '\x0E', '\x1E', '\xFA'}; + do_write(CLIENT_CLOSE_FRAME, sizeof(CLIENT_CLOSE_FRAME)); + SPIN_WHILE(!inspector_closed); +} + +// Make sure buffering works +static void send_in_chunks(const char* data, size_t len) { + const int step = 7; + size_t i = 0; + // Do not send it all at once - test the buffering! + for (; i < len - step; i += step) { + do_write(data + i, step); + } + if (i < len) { + do_write(data + i, len - i); + } +} + +static const char TEST_SUCCESS[] = "Test Success\n\n"; + +static void ReportsHttpGet_handshake(enum inspector_handshake_event state, + const char* path, bool* cont) { + *cont = true; + enum inspector_handshake_event expected_state = kInspectorHandshakeHttpGet; + const char* expected_path; + switch (handshake_events) { + case 1: + expected_path = "/some/path"; + break; + case 2: + expected_path = "/respond/withtext"; + inspector_write(&inspector, TEST_SUCCESS, sizeof(TEST_SUCCESS) - 1); + break; + case 3: + expected_path = "/some/path2"; + break; + case 5: + expected_state = kInspectorHandshakeFailed; + case 4: + expected_path = "/close"; + *cont = false; + break; + default: + expected_path = nullptr; + ASSERT_TRUE(false); + } + EXPECT_EQ(expected_state, state); + EXPECT_STREQ(expected_path, path); +} + +TEST_F(InspectorSocketTest, ReportsHttpGet) { + handshake_delegate = ReportsHttpGet_handshake; + + const char GET_REQ[] = "GET /some/path HTTP/1.1\r\n" + "Host: localhost:9222\r\n" + "Sec-WebSocket-Key: aaa==\r\n" + "Sec-WebSocket-Version: 13\r\n\r\n"; + send_in_chunks(GET_REQ, sizeof(GET_REQ) - 1); + + expect_nothing_on_client(); + + const char WRITE_REQUEST[] = "GET /respond/withtext HTTP/1.1\r\n" + "Host: localhost:9222\r\n\r\n"; + send_in_chunks(WRITE_REQUEST, sizeof(WRITE_REQUEST) - 1); + + expect_on_client(TEST_SUCCESS, sizeof(TEST_SUCCESS) - 1); + + const char GET_REQS[] = "GET /some/path2 HTTP/1.1\r\n" + "Host: localhost:9222\r\n" + "Sec-WebSocket-Key: aaa==\r\n" + "Sec-WebSocket-Version: 13\r\n\r\n" + "GET /close HTTP/1.1\r\n" + "Host: localhost:9222\r\n" + "Sec-WebSocket-Key: aaa==\r\n" + "Sec-WebSocket-Version: 13\r\n\r\n"; + send_in_chunks(GET_REQS, sizeof(GET_REQS) - 1); + + expect_handshake_failure(); + EXPECT_EQ(5, handshake_events); +} + +static void +HandshakeCanBeCanceled_handshake(enum inspector_handshake_event state, + const char* path, bool* cont) { + switch (handshake_events - 1) { + case 0: + EXPECT_EQ(kInspectorHandshakeUpgrading, state); + break; + case 1: + EXPECT_EQ(kInspectorHandshakeFailed, state); + break; + default: + EXPECT_TRUE(false); + break; + } + EXPECT_STREQ("/ws/path", path); + *cont = false; +} + +TEST_F(InspectorSocketTest, HandshakeCanBeCanceled) { + handshake_delegate = HandshakeCanBeCanceled_handshake; + + do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + + expect_handshake_failure(); + EXPECT_EQ(2, handshake_events); +} + +static void GetThenHandshake_handshake(enum inspector_handshake_event state, + const char* path, bool* cont) { + *cont = true; + const char* expected_path = "/ws/path"; + switch (handshake_events - 1) { + case 0: + EXPECT_EQ(kInspectorHandshakeHttpGet, state); + expected_path = "/respond/withtext"; + inspector_write(&inspector, TEST_SUCCESS, sizeof(TEST_SUCCESS) - 1); + break; + case 1: + EXPECT_EQ(kInspectorHandshakeUpgrading, state); + break; + case 2: + EXPECT_EQ(kInspectorHandshakeUpgraded, state); + break; + default: + EXPECT_TRUE(false); + break; + } + EXPECT_STREQ(expected_path, path); +} + +TEST_F(InspectorSocketTest, GetThenHandshake) { + handshake_delegate = GetThenHandshake_handshake; + const char WRITE_REQUEST[] = "GET /respond/withtext HTTP/1.1\r\n" + "Host: localhost:9222\r\n\r\n"; + send_in_chunks(WRITE_REQUEST, sizeof(WRITE_REQUEST) - 1); + + expect_on_client(TEST_SUCCESS, sizeof(TEST_SUCCESS) - 1); + + do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + expect_handshake(); + EXPECT_EQ(3, handshake_events); + manual_inspector_socket_cleanup(); +} + +static void WriteBeforeHandshake_close_cb(uv_handle_t* handle) { + *(static_cast<bool *>(handle->data)) = true; +} + +TEST_F(InspectorSocketTest, WriteBeforeHandshake) { + const char MESSAGE1[] = "Message 1"; + const char MESSAGE2[] = "Message 2"; + const char EXPECTED[] = "Message 1Message 2"; + + inspector_write(&inspector, MESSAGE1, sizeof(MESSAGE1) - 1); + inspector_write(&inspector, MESSAGE2, sizeof(MESSAGE2) - 1); + expect_on_client(EXPECTED, sizeof(EXPECTED) - 1); + bool flag = false; + client_socket.data = &flag; + uv_close(reinterpret_cast<uv_handle_t*>(&client_socket), + WriteBeforeHandshake_close_cb); + SPIN_WHILE(!flag); +} + +static void CleanupSocketAfterEOF_close_cb(inspector_socket_t* inspector, + int status) { + *(static_cast<bool *>(inspector->data)) = true; +} + +static void CleanupSocketAfterEOF_read_cb(uv_stream_t* stream, ssize_t nread, + const uv_buf_t* buf) { + EXPECT_EQ(UV_EOF, nread); + inspector_socket_t* insp = + reinterpret_cast<inspector_socket_t*>(stream->data); + inspector_close(insp, CleanupSocketAfterEOF_close_cb); +} + +TEST_F(InspectorSocketTest, CleanupSocketAfterEOF) { + do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + expect_handshake(); + + inspector_read_start(&inspector, buffer_alloc_cb, + CleanupSocketAfterEOF_read_cb); + + for (int i = 0; i < MAX_LOOP_ITERATIONS; ++i) { + uv_run(&loop, UV_RUN_NOWAIT); + } + + uv_close(reinterpret_cast<uv_handle_t*>(&client_socket), nullptr); + bool flag = false; + inspector.data = &flag; + SPIN_WHILE(!flag); + inspector.data = nullptr; +} + +TEST_F(InspectorSocketTest, EOFBeforeHandshake) { + const char MESSAGE[] = "We'll send EOF afterwards"; + inspector_write(&inspector, MESSAGE, sizeof(MESSAGE) - 1); + expect_on_client(MESSAGE, sizeof(MESSAGE) - 1); + uv_close(reinterpret_cast<uv_handle_t*>(&client_socket), nullptr); + SPIN_WHILE(last_event != kInspectorHandshakeFailed); +} + +static void fill_message(char* buffer, size_t len) { + buffer[len - 1] = '\0'; + for (size_t i = 0; i < len - 1; i++) { + buffer[i] = 'a' + (i % ('z' - 'a')); + } +} + +static void mask_message(const char* message, + char* buffer, const char mask[]) { + const size_t mask_len = 4; + int i = 0; + while (*message != '\0') { + *buffer++ = *message++ ^ mask[i++ % mask_len]; + } +} + +TEST_F(InspectorSocketTest, Send1Mb) { + ASSERT_TRUE(connected); + ASSERT_FALSE(inspector_ready); + do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + SPIN_WHILE(!inspector_ready); + expect_handshake(); + + const size_t message_len = 1000000; + + // 2. Brief exchange + char* message = static_cast<char*>(malloc(message_len + 1)); + fill_message(message, message_len + 1); + + // 1000000 is 0xF4240 hex + const char EXPECTED_FRAME_HEADER[] = { + '\x81', '\x7f', '\x00', '\x00', '\x00', '\x00', '\x00', '\x0F', + '\x42', '\x40' + }; + char* expected = + static_cast<char*>(malloc(sizeof(EXPECTED_FRAME_HEADER) + message_len)); + + memcpy(expected, EXPECTED_FRAME_HEADER, sizeof(EXPECTED_FRAME_HEADER)); + memcpy(expected + sizeof(EXPECTED_FRAME_HEADER), message, message_len); + + inspector_write(&inspector, message, message_len); + expect_on_client(expected, sizeof(EXPECTED_FRAME_HEADER) + message_len); + + char MASK[4] = {'W', 'h', 'O', 'a'}; + + const char FRAME_TO_SERVER_HEADER[] = { + '\x81', '\xff', '\x00', '\x00', '\x00', '\x00', '\x00', '\x0F', + '\x42', '\x40', MASK[0], MASK[1], MASK[2], MASK[3] + }; + + const size_t outgoing_len = sizeof(FRAME_TO_SERVER_HEADER) + message_len; + char* outgoing = static_cast<char*>(malloc(outgoing_len)); + memcpy(outgoing, FRAME_TO_SERVER_HEADER, sizeof(FRAME_TO_SERVER_HEADER)); + mask_message(message, outgoing + sizeof(FRAME_TO_SERVER_HEADER), MASK); + + setup_inspector_expecting(); // Buffer on the client side. + do_write(outgoing, outgoing_len); + expect_on_server(message, message_len); + + // 3. Close + const char CLIENT_CLOSE_FRAME[] = {'\x88', '\x80', '\x2D', + '\x0E', '\x1E', '\xFA'}; + const char SERVER_CLOSE_FRAME[] = {'\x88', '\x00'}; + do_write(CLIENT_CLOSE_FRAME, sizeof(CLIENT_CLOSE_FRAME)); + expect_on_client(SERVER_CLOSE_FRAME, sizeof(SERVER_CLOSE_FRAME)); + GTEST_ASSERT_EQ(0, uv_is_active( + reinterpret_cast<uv_handle_t*>(&client_socket))); + free(outgoing); + free(expected); + free(message); +} From c1bd3fe14c49c46c44b3b62bf32aeaad32066314 Mon Sep 17 00:00:00 2001 From: cjihrig <cjihrig@gmail.com> Date: Fri, 20 May 2016 13:33:18 -0400 Subject: [PATCH 005/131] cluster: work with v8_inspector PR-URL: https://github.com/nodejs/node/pull/6792 Reviewed-By: jasnell - James M Snell <jasnell@gmail.com> Reviewed-By: addaleax - Anna Henningsen <anna@addaleax.net> Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl> --- lib/cluster.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/cluster.js b/lib/cluster.js index 7e65fe222b835e..8462ec34fc9aeb 100644 --- a/lib/cluster.js +++ b/lib/cluster.js @@ -305,7 +305,9 @@ function masterInit() { workerEnv.NODE_UNIQUE_ID = '' + id; for (var i = 0; i < execArgv.length; i++) { - var match = execArgv[i].match(/^(--debug|--debug-(brk|port))(=\d+)?$/); + var match = execArgv[i].match( + /^(--inspect|--debug|--debug-(brk|port))(=\d+)?$/ + ); if (match) { if (debugPort === 0) { From ea2d6614476eb303bf7e145d5006505ac10cc3a8 Mon Sep 17 00:00:00 2001 From: Anna Henningsen <anna@addaleax.net> Date: Tue, 31 May 2016 19:28:05 +0200 Subject: [PATCH 006/131] src: fix --without-inspector build Use `HAVE_INSPECTOR` as the 0/1 boolean macro that it is, as opposed to a defined/not-defined boolean. PR-URL: https://github.com/nodejs/node/pull/7078 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> --- src/signal_wrap.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/signal_wrap.cc b/src/signal_wrap.cc index 8d31dbf62330ae..280c9e3eb91779 100644 --- a/src/signal_wrap.cc +++ b/src/signal_wrap.cc @@ -65,7 +65,7 @@ class SignalWrap : public HandleWrap { SignalWrap* wrap; ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); int signum = args[0]->Int32Value(); -#if defined(__POSIX__) && defined(HAVE_INSPECTOR) +#if defined(__POSIX__) && HAVE_INSPECTOR if (signum == SIGPROF) { Environment* env = Environment::GetCurrent(args); if (env->inspector_agent()->IsStarted()) { From 643b33b4979eb1d3acff40743c775908396b5624 Mon Sep 17 00:00:00 2001 From: Nicolas Romer <nicolasromer@gmail.com> Date: Mon, 30 May 2016 16:31:03 -0400 Subject: [PATCH 007/131] src: add linebreak to inspector message Add linebreak to inspector ready message to wrap properly in 80 column (default) terminal windows. Fixes: https://github.com/nodejs/node/issues/7067 PR-URL: https://github.com/nodejs/node/pull/7070 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Claudio Rodriguez <cjrodr@yahoo.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- src/inspector_agent.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index cd2ae83b19be59..451ab46e7ab845 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -31,7 +31,7 @@ namespace { const char DEVTOOLS_PATH[] = "/node"; void PrintDebuggerReadyMessage(int port) { - fprintf(stderr, "Debugger listening on port %d. " + fprintf(stderr, "Debugger listening on port %d.\n" "To start debugging, open the following URL in Chrome:\n" " chrome-devtools://devtools/remote/serve_file/" "@521e5b7e2b7cc66b4006a8a54cb9c4e57494a5ef/inspector.html?" From f4777c77ebe3dd13ff4af5eecae2b6a4de3687fe Mon Sep 17 00:00:00 2001 From: Ali Ijaz Sheikh <ofrobots@google.com> Date: Thu, 2 Jun 2016 16:38:03 -0700 Subject: [PATCH 008/131] deps: update v8_inspector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pick the latest v8_inspector [1] with: * V8 5.1 compatibility * Modify parse builder templates to make coverity happy * The whitespace differences in the jinja2 sub-dependency do exist upstream. I am not sure how I missed them in the original import (ed2eac). [1] https://github.com/pavelfeldman/v8_inspector/commit/3b56732 PR-URL: https://github.com/nodejs/node/pull/7118 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Myles Borins <myles.borins@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Michaël Zasso <mic.besace@gmail.com> --- .../platform/inspector_protocol/CodeGenerator.py | 6 ++++++ .../platform/inspector_protocol/TypeBuilder_h.template | 9 ++++++++- deps/v8_inspector/platform/v8_inspector/V8Compat.h | 4 ++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/deps/v8_inspector/platform/inspector_protocol/CodeGenerator.py b/deps/v8_inspector/platform/inspector_protocol/CodeGenerator.py index 5107feb680860a..62f77a783a5b7e 100644 --- a/deps/v8_inspector/platform/inspector_protocol/CodeGenerator.py +++ b/deps/v8_inspector/platform/inspector_protocol/CodeGenerator.py @@ -180,6 +180,11 @@ def create_primitive_type_definition(type): "integer": "int", "boolean": "bool" } + defaults = { + "number": "0", + "integer": "0", + "boolean": "false" + } jsontypes = { "number": "TypeNumber", "integer": "TypeNumber", @@ -195,6 +200,7 @@ def create_primitive_type_definition(type): "raw_type": typedefs[type], "raw_pass_type": typedefs[type], "raw_return_type": typedefs[type], + "default_value": defaults[type] } type_definitions = {} diff --git a/deps/v8_inspector/platform/inspector_protocol/TypeBuilder_h.template b/deps/v8_inspector/platform/inspector_protocol/TypeBuilder_h.template index 75f67043282b63..144fcb149ffe97 100644 --- a/deps/v8_inspector/platform/inspector_protocol/TypeBuilder_h.template +++ b/deps/v8_inspector/platform/inspector_protocol/TypeBuilder_h.template @@ -187,7 +187,14 @@ public: } private: - {{type.id}}() { } + {{type.id}}() + { + {% for property in type.properties %} + {% if not(property.optional) and "default_value" in resolve_type(property) %} + m_{{property.name}} = {{resolve_type(property).default_value}}; + {%endif %} + {% endfor %} + } {% for property in type.properties %} {% if property.optional %} diff --git a/deps/v8_inspector/platform/v8_inspector/V8Compat.h b/deps/v8_inspector/platform/v8_inspector/V8Compat.h index 17d104f7736ea7..0f5b12cbb6cb07 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8Compat.h +++ b/deps/v8_inspector/platform/v8_inspector/V8Compat.h @@ -7,7 +7,7 @@ #include <v8.h> -#if V8_MAJOR_VERSION < 5 || (V8_MAJOR_VERSION == 5 && V8_MINOR_VERSION < 2) +#if V8_MAJOR_VERSION < 5 || (V8_MAJOR_VERSION == 5 && V8_MINOR_VERSION < 1) namespace v8 { // In standalone V8 inspector this is expected to be noop anyways... @@ -23,6 +23,6 @@ class V8_EXPORT MicrotasksScope { } // namespace v8 -#endif // V8_MAJOR_VERSION < 5 || (V8_MAJOR_VERSION == 5 && V8_MINOR_VERSION < 2) +#endif // V8_MAJOR_VERSION < 5 || (V8_MAJOR_VERSION == 5 && V8_MINOR_VERSION < 1) #endif // V8Compat_h From 2fd140b949efa7042fd13a076af25f4d6f524589 Mon Sep 17 00:00:00 2001 From: Myles Borins <mborins@us.ibm.com> Date: Wed, 8 Jun 2016 13:09:01 -0700 Subject: [PATCH 009/131] src: fix json payload from inspector Fix the `webSocketDebuggerUrl` and `devtoolsFrontendUrl` returned by v8_inspector in /json HTTP endpoint to work with 3rd party clients. Fixes: https://github.com/nodejs/node/issues/7227 PR-URL: https://github.com/nodejs/node/pull/7232 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Ali Ijaz Sheikh <ofrobots@google.com> --- src/inspector_agent.cc | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index 451ab46e7ab845..821b1317346e6d 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -29,13 +29,15 @@ namespace node { namespace { const char DEVTOOLS_PATH[] = "/node"; +const char DEVTOOLS_HASH[] = "521e5b7e2b7cc66b4006a8a54cb9c4e57494a5ef"; void PrintDebuggerReadyMessage(int port) { fprintf(stderr, "Debugger listening on port %d.\n" "To start debugging, open the following URL in Chrome:\n" " chrome-devtools://devtools/remote/serve_file/" - "@521e5b7e2b7cc66b4006a8a54cb9c4e57494a5ef/inspector.html?" - "experiments=true&v8only=true&ws=localhost:%d/node\n", port, port); + "@%s/inspector.html?" + "experiments=true&v8only=true&ws=localhost:%d/node\n", + port, DEVTOOLS_HASH, port); } bool AcceptsConnection(inspector_socket_t* socket, const char* path) { @@ -89,18 +91,19 @@ void SendVersionResponse(inspector_socket_t* socket) { SendHttpResponse(socket, buffer, len); } -void SendTargentsListResponse(inspector_socket_t* socket) { +void SendTargentsListResponse(inspector_socket_t* socket, int port) { const char LIST_RESPONSE_TEMPLATE[] = "[ {" " \"description\": \"node.js instance\"," " \"devtoolsFrontendUrl\": " "\"https://chrome-devtools-frontend.appspot.com/serve_file/" - "@4604d24a75168768584760ba56d175507941852f/inspector.html\"," + "@%s/inspector.html?experiments=true&v8only=true" + "&ws=localhost:%d%s\"," " \"faviconUrl\": \"https://nodejs.org/static/favicon.ico\"," " \"id\": \"%d\"," " \"title\": \"%s\"," " \"type\": \"node\"," - " \"webSocketDebuggerUrl\": \"ws://%s\"" + " \"webSocketDebuggerUrl\": \"ws://localhost:%d%s\"" "} ]"; char buffer[sizeof(LIST_RESPONSE_TEMPLATE) + 4096]; char title[2048]; // uv_get_process_title trims the title if too long @@ -114,12 +117,13 @@ void SendTargentsListResponse(inspector_socket_t* socket) { c++; } size_t len = snprintf(buffer, sizeof(buffer), LIST_RESPONSE_TEMPLATE, - getpid(), title, DEVTOOLS_PATH); + DEVTOOLS_HASH, port, DEVTOOLS_PATH, getpid(), + title, port, DEVTOOLS_PATH); ASSERT_LT(len, sizeof(buffer)); SendHttpResponse(socket, buffer, len); } -bool RespondToGet(inspector_socket_t* socket, const char* path) { +bool RespondToGet(inspector_socket_t* socket, const char* path, int port) { const char PATH[] = "/json"; const char PATH_LIST[] = "/json/list"; const char PATH_VERSION[] = "/json/version"; @@ -128,7 +132,7 @@ bool RespondToGet(inspector_socket_t* socket, const char* path) { SendVersionResponse(socket); } else if (!strncmp(PATH_LIST, path, sizeof(PATH_LIST)) || !strncmp(PATH, path, sizeof(PATH))) { - SendTargentsListResponse(socket); + SendTargentsListResponse(socket, port); } else if (!strncmp(path, PATH_ACTIVATE, sizeof(PATH_ACTIVATE) - 1) && atoi(path + (sizeof(PATH_ACTIVATE) - 1)) == getpid()) { const char TARGET_ACTIVATED[] = "Target activated"; @@ -348,7 +352,7 @@ bool Agent::OnInspectorHandshakeIO(inspector_socket_t* socket, Agent* agent = static_cast<Agent*>(socket->data); switch (state) { case kInspectorHandshakeHttpGet: - return RespondToGet(socket, path); + return RespondToGet(socket, path, agent->port_); case kInspectorHandshakeUpgrading: return AcceptsConnection(socket, path); case kInspectorHandshakeUpgraded: From ec90a7a92e03aa86f210707580668ccb02037924 Mon Sep 17 00:00:00 2001 From: Ali Ijaz Sheikh <ofrobots@google.com> Date: Tue, 7 Jun 2016 18:02:03 -0700 Subject: [PATCH 010/131] inspector: change default port We should use a different default port number for the new debug protocol. This makes it easier for debuggers to guess which protocol they are expected to use to talk to a node process with a debug server. PR-URL: https://github.com/nodejs/node/pull/7212 Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: cjihrig - Colin Ihrig <cjihrig@gmail.com> --- src/inspector_agent.cc | 2 +- src/node.cc | 23 ++++++++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index 821b1317346e6d..848fcf320fea68 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -244,7 +244,7 @@ class V8NodeInspector : public blink::V8Inspector { bool running_nested_loop_; }; -Agent::Agent(Environment* env) : port_(9229), +Agent::Agent(Environment* env) : port_(0), wait_(false), connected_(false), shutting_down_(false), diff --git a/src/node.cc b/src/node.cc index cbb7e6433fbf29..1544938cbe42e2 100644 --- a/src/node.cc +++ b/src/node.cc @@ -143,6 +143,7 @@ static bool use_inspector = false; static bool use_debug_agent = false; static bool debug_wait_connect = false; static int debug_port = 5858; +static int inspector_port = 9229; static const int v8_default_thread_pool_size = 4; static int v8_thread_pool_size = v8_default_thread_pool_size; static bool prof_process = false; @@ -3436,12 +3437,17 @@ static bool ParseDebugOpt(const char* arg) { } if (port != nullptr) { - debug_port = atoi(port); - if (debug_port < 1024 || debug_port > 65535) { + int port_int = atoi(port); + if (port_int < 1024 || port_int > 65535) { fprintf(stderr, "Debug port must be in range 1024 to 65535.\n"); PrintHelp(); exit(12); } + if (use_inspector) { + inspector_port = port_int; + } else { + debug_port = port_int; + } } return true; @@ -3703,22 +3709,21 @@ static void StartDebug(Environment* env, bool wait) { CHECK(!debugger_running); #if HAVE_INSPECTOR if (use_inspector) { - env->inspector_agent()->Start(default_platform, debug_port, wait); + env->inspector_agent()->Start(default_platform, inspector_port, wait); debugger_running = true; } else { #endif env->debugger_agent()->set_dispatch_handler( DispatchMessagesDebugAgentCallback); debugger_running = env->debugger_agent()->Start(debug_port, wait); + if (debugger_running == false) { + fprintf(stderr, "Starting debugger on port %d failed\n", debug_port); + fflush(stderr); + return; + } #if HAVE_INSPECTOR } #endif - - if (debugger_running == false) { - fprintf(stderr, "Starting debugger on port %d failed\n", debug_port); - fflush(stderr); - return; - } } From 7da8a413f6c35f953d8a32dcec3ec54796e1a6f4 Mon Sep 17 00:00:00 2001 From: Eugene Ostroukhov <eostroukhov@chromium.org> Date: Tue, 7 Jun 2016 15:21:36 -0700 Subject: [PATCH 011/131] inspector: reduce implementation in header This is needed to reduce the coupling between node files that use node::Environment and inspector class. Fixes: https://github.com/nodejs/node/issues/7080 PR-URL: https://github.com/nodejs/node/pull/7228 Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl> Conflicts: src/node.cc --- src/inspector_agent.cc | 203 ++++++++++++++++++++++++++++++----------- src/inspector_agent.h | 71 ++------------ src/node.cc | 2 +- 3 files changed, 159 insertions(+), 117 deletions(-) diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index 848fcf320fea68..bcccff4acb008f 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -1,8 +1,9 @@ #include "inspector_agent.h" -#include "node.h" +#include "inspector_socket.h" #include "env.h" #include "env-inl.h" +#include "node.h" #include "node_version.h" #include "v8-platform.h" #include "util.h" @@ -15,6 +16,7 @@ #include "libplatform/libplatform.h" #include <string.h> +#include <vector> // We need pid to use as ID with Chrome #if defined(_MSC_VER) @@ -150,25 +152,89 @@ namespace inspector { using blink::protocol::DictionaryValue; using blink::protocol::String16; +class AgentImpl { + public: + explicit AgentImpl(node::Environment* env); + ~AgentImpl(); + + // Start the inspector agent thread + void Start(v8::Platform* platform, int port, bool wait); + // Stop the inspector agent + void Stop(); + + bool IsStarted(); + bool IsConnected() { return connected_; } + void WaitForDisconnect(); + + private: + static void ThreadCbIO(void* agent); + static void OnSocketConnectionIO(uv_stream_t* server, int status); + static bool OnInspectorHandshakeIO(inspector_socket_t* socket, + enum inspector_handshake_event state, + const char* path); + static void OnRemoteDataIO(uv_stream_t* stream, ssize_t read, + const uv_buf_t* b); + static void WriteCbIO(uv_async_t* async); + + void WorkerRunIO(); + void OnInspectorConnectionIO(inspector_socket_t* socket); + void PushPendingMessage(std::vector<std::string>* queue, + const std::string& message); + void SwapBehindLock(std::vector<std::string> AgentImpl::*queue, + std::vector<std::string>* output); + void PostMessages(); + void SetConnected(bool connected); + void Write(const std::string& message); + + uv_sem_t start_sem_; + uv_cond_t pause_cond_; + uv_mutex_t queue_lock_; + uv_mutex_t pause_lock_; + uv_thread_t thread_; + uv_loop_t child_loop_; + uv_tcp_t server_; + + int port_; + bool wait_; + bool connected_; + bool shutting_down_; + node::Environment* parent_env_; + + uv_async_t data_written_; + uv_async_t io_thread_req_; + inspector_socket_t* client_socket_; + blink::V8Inspector* inspector_; + v8::Platform* platform_; + std::vector<std::string> message_queue_; + std::vector<std::string> outgoing_message_queue_; + bool dispatching_messages_; + + friend class ChannelImpl; + friend class DispatchOnInspectorBackendTask; + friend class SetConnectedTask; + friend class V8NodeInspector; + friend void InterruptCallback(v8::Isolate*, void* agent); +}; + void InterruptCallback(v8::Isolate*, void* agent) { - static_cast<Agent*>(agent)->PostMessages(); + static_cast<AgentImpl*>(agent)->PostMessages(); } class DispatchOnInspectorBackendTask : public v8::Task { public: - explicit DispatchOnInspectorBackendTask(Agent* agent) : agent_(agent) {} + explicit DispatchOnInspectorBackendTask(AgentImpl* agent) : agent_(agent) {} void Run() override { agent_->PostMessages(); } private: - Agent* agent_; + AgentImpl* agent_; }; class ChannelImpl final : public blink::protocol::FrontendChannel { public: - explicit ChannelImpl(Agent* agent): agent_(agent) {} + explicit ChannelImpl(AgentImpl* agent): agent_(agent) {} virtual ~ChannelImpl() {} private: virtual void sendProtocolResponse(int sessionId, int callId, @@ -188,12 +254,12 @@ class ChannelImpl final : public blink::protocol::FrontendChannel { agent_->Write(message->toJSONString().utf8()); } - Agent* const agent_; + AgentImpl* const agent_; }; class SetConnectedTask : public v8::Task { public: - SetConnectedTask(Agent* agent, bool connected) + SetConnectedTask(AgentImpl* agent, bool connected) : agent_(agent), connected_(connected) {} @@ -202,19 +268,20 @@ class SetConnectedTask : public v8::Task { } private: - Agent* agent_; + AgentImpl* agent_; bool connected_; }; class V8NodeInspector : public blink::V8Inspector { public: - V8NodeInspector(Agent* agent, node::Environment* env, v8::Platform* platform) - : blink::V8Inspector(env->isolate(), env->context()), - agent_(agent), - isolate_(env->isolate()), - platform_(platform), - terminated_(false), - running_nested_loop_(false) {} + V8NodeInspector(AgentImpl* agent, node::Environment* env, + v8::Platform* platform) + : blink::V8Inspector(env->isolate(), env->context()), + agent_(agent), + isolate_(env->isolate()), + platform_(platform), + terminated_(false), + running_nested_loop_(false) {} void runMessageLoopOnPause(int context_group_id) override { if (running_nested_loop_) @@ -237,28 +304,28 @@ class V8NodeInspector : public blink::V8Inspector { } private: - Agent* agent_; + AgentImpl* agent_; v8::Isolate* isolate_; v8::Platform* platform_; bool terminated_; bool running_nested_loop_; }; -Agent::Agent(Environment* env) : port_(0), - wait_(false), - connected_(false), - shutting_down_(false), - parent_env_(env), - client_socket_(nullptr), - inspector_(nullptr), - platform_(nullptr), - dispatching_messages_(false) { +AgentImpl::AgentImpl(Environment* env) : port_(0), + wait_(false), + connected_(false), + shutting_down_(false), + parent_env_(env), + client_socket_(nullptr), + inspector_(nullptr), + platform_(nullptr), + dispatching_messages_(false) { int err; err = uv_sem_init(&start_sem_, 0); CHECK_EQ(err, 0); } -Agent::~Agent() { +AgentImpl::~AgentImpl() { if (!inspector_) return; uv_mutex_destroy(&queue_lock_); @@ -267,7 +334,7 @@ Agent::~Agent() { uv_close(reinterpret_cast<uv_handle_t*>(&data_written_), nullptr); } -void Agent::Start(v8::Platform* platform, int port, bool wait) { +void AgentImpl::Start(v8::Platform* platform, int port, bool wait) { auto env = parent_env_; inspector_ = new V8NodeInspector(this, env, platform); @@ -291,7 +358,7 @@ void Agent::Start(v8::Platform* platform, int port, bool wait) { port_ = port; wait_ = wait; - err = uv_thread_create(&thread_, Agent::ThreadCbIO, this); + err = uv_thread_create(&thread_, AgentImpl::ThreadCbIO, this); CHECK_EQ(err, 0); uv_sem_wait(&start_sem_); @@ -303,7 +370,7 @@ void Agent::Start(v8::Platform* platform, int port, bool wait) { } } -void Agent::Stop() { +void AgentImpl::Stop() { // TODO(repenaxa): hop on the right thread. DisconnectAndDisposeIO(client_socket_); int err = uv_thread_join(&thread_); @@ -316,40 +383,41 @@ void Agent::Stop() { delete inspector_; } -bool Agent::IsStarted() { +bool AgentImpl::IsStarted() { return !!platform_; } -void Agent::WaitForDisconnect() { +void AgentImpl::WaitForDisconnect() { shutting_down_ = true; fprintf(stderr, "Waiting for the debugger to disconnect...\n"); inspector_->runMessageLoopOnPause(0); } // static -void Agent::ThreadCbIO(void* agent) { - static_cast<Agent*>(agent)->WorkerRunIO(); +void AgentImpl::ThreadCbIO(void* agent) { + static_cast<AgentImpl*>(agent)->WorkerRunIO(); } // static -void Agent::OnSocketConnectionIO(uv_stream_t* server, int status) { +void AgentImpl::OnSocketConnectionIO(uv_stream_t* server, int status) { if (status == 0) { inspector_socket_t* socket = static_cast<inspector_socket_t*>(malloc(sizeof(*socket))); ASSERT_NE(nullptr, socket); memset(socket, 0, sizeof(*socket)); socket->data = server->data; - if (inspector_accept(server, socket, Agent::OnInspectorHandshakeIO) != 0) { + if (inspector_accept(server, socket, + AgentImpl::OnInspectorHandshakeIO) != 0) { free(socket); } } } // static -bool Agent::OnInspectorHandshakeIO(inspector_socket_t* socket, +bool AgentImpl::OnInspectorHandshakeIO(inspector_socket_t* socket, enum inspector_handshake_event state, const char* path) { - Agent* agent = static_cast<Agent*>(socket->data); + AgentImpl* agent = static_cast<AgentImpl*>(socket->data); switch (state) { case kInspectorHandshakeHttpGet: return RespondToGet(socket, path, agent->port_); @@ -366,11 +434,11 @@ bool Agent::OnInspectorHandshakeIO(inspector_socket_t* socket, } // static -void Agent::OnRemoteDataIO(uv_stream_t* stream, +void AgentImpl::OnRemoteDataIO(uv_stream_t* stream, ssize_t read, const uv_buf_t* b) { inspector_socket_t* socket = static_cast<inspector_socket_t*>(stream->data); - Agent* agent = static_cast<Agent*>(socket->data); + AgentImpl* agent = static_cast<AgentImpl*>(socket->data); if (read > 0) { std::string str(b->base, read); agent->PushPendingMessage(&agent->message_queue_, str); @@ -407,14 +475,14 @@ void Agent::OnRemoteDataIO(uv_stream_t* stream, uv_cond_broadcast(&agent->pause_cond_); } -void Agent::PushPendingMessage(std::vector<std::string>* queue, +void AgentImpl::PushPendingMessage(std::vector<std::string>* queue, const std::string& message) { uv_mutex_lock(&queue_lock_); queue->push_back(message); uv_mutex_unlock(&queue_lock_); } -void Agent::SwapBehindLock(std::vector<std::string> Agent::*queue, +void AgentImpl::SwapBehindLock(std::vector<std::string> AgentImpl::*queue, std::vector<std::string>* output) { uv_mutex_lock(&queue_lock_); (this->*queue).swap(*output); @@ -422,21 +490,22 @@ void Agent::SwapBehindLock(std::vector<std::string> Agent::*queue, } // static -void Agent::WriteCbIO(uv_async_t* async) { - Agent* agent = static_cast<Agent*>(async->data); +void AgentImpl::WriteCbIO(uv_async_t* async) { + AgentImpl* agent = static_cast<AgentImpl*>(async->data); inspector_socket_t* socket = agent->client_socket_; if (socket) { std::vector<std::string> outgoing_messages; - agent->SwapBehindLock(&Agent::outgoing_message_queue_, &outgoing_messages); + agent->SwapBehindLock(&AgentImpl::outgoing_message_queue_, + &outgoing_messages); for (auto const& message : outgoing_messages) inspector_write(socket, message.c_str(), message.length()); } } -void Agent::WorkerRunIO() { +void AgentImpl::WorkerRunIO() { sockaddr_in addr; uv_tcp_t server; - int err = uv_async_init(&child_loop_, &io_thread_req_, Agent::WriteCbIO); + int err = uv_async_init(&child_loop_, &io_thread_req_, AgentImpl::WriteCbIO); CHECK_EQ(0, err); io_thread_req_.data = this; uv_tcp_init(&child_loop_, &server); @@ -463,22 +532,22 @@ void Agent::WorkerRunIO() { uv_run(&child_loop_, UV_RUN_DEFAULT); } -void Agent::OnInspectorConnectionIO(inspector_socket_t* socket) { +void AgentImpl::OnInspectorConnectionIO(inspector_socket_t* socket) { if (client_socket_) { return; } client_socket_ = socket; - inspector_read_start(socket, OnBufferAlloc, Agent::OnRemoteDataIO); + inspector_read_start(socket, OnBufferAlloc, AgentImpl::OnRemoteDataIO); platform_->CallOnForegroundThread(parent_env_->isolate(), new SetConnectedTask(this, true)); } -void Agent::PostMessages() { +void AgentImpl::PostMessages() { if (dispatching_messages_) return; dispatching_messages_ = true; std::vector<std::string> messages; - SwapBehindLock(&Agent::message_queue_, &messages); + SwapBehindLock(&AgentImpl::message_queue_, &messages); for (auto const& message : messages) inspector_->dispatchMessageFromFrontend( String16::fromUTF8(message.c_str(), message.length())); @@ -486,7 +555,7 @@ void Agent::PostMessages() { dispatching_messages_ = false; } -void Agent::SetConnected(bool connected) { +void AgentImpl::SetConnected(bool connected) { if (connected_ == connected) return; @@ -502,9 +571,37 @@ void Agent::SetConnected(bool connected) { } } -void Agent::Write(const std::string& message) { +void AgentImpl::Write(const std::string& message) { PushPendingMessage(&outgoing_message_queue_, message); ASSERT_EQ(0, uv_async_send(&io_thread_req_)); } -} // namespace debugger + +// Exported class Agent +Agent::Agent(node::Environment* env) : impl(new AgentImpl(env)) {} + +Agent::~Agent() { + delete impl; +} + +void Agent::Start(v8::Platform* platform, int port, bool wait) { + impl->Start(platform, port, wait); +}; + +void Agent::Stop() { + impl->Stop(); +}; + +bool Agent::IsStarted() { + return impl->IsStarted(); +}; + +bool Agent::IsConnected() { + return impl->IsConnected(); +}; + +void Agent::WaitForDisconnect() { + impl->WaitForDisconnect(); +}; + +} // namespace inspector } // namespace node diff --git a/src/inspector_agent.h b/src/inspector_agent.h index 65a4abeff7db54..863f1c30c33b63 100644 --- a/src/inspector_agent.h +++ b/src/inspector_agent.h @@ -5,27 +5,19 @@ #error("This header can only be used when inspector is enabled") #endif -#include "inspector_socket.h" -#include "uv.h" -#include "v8.h" -#include "util.h" - -#include <string> -#include <vector> - -namespace blink { -class V8Inspector; -} - // Forward declaration to break recursive dependency chain with src/env.h. namespace node { class Environment; } // namespace node +namespace v8 { +class Platform; +} // namespace v8 + namespace node { namespace inspector { -class ChannelImpl; +class AgentImpl; class Agent { public: @@ -38,57 +30,10 @@ class Agent { void Stop(); bool IsStarted(); - bool connected() { return connected_; } + bool IsConnected(); void WaitForDisconnect(); - - protected: - static void ThreadCbIO(void* agent); - static void OnSocketConnectionIO(uv_stream_t* server, int status); - static bool OnInspectorHandshakeIO(inspector_socket_t* socket, - enum inspector_handshake_event state, - const char* path); - static void OnRemoteDataIO(uv_stream_t* stream, ssize_t read, - const uv_buf_t* b); - static void WriteCbIO(uv_async_t* async); - - void WorkerRunIO(); - void OnInspectorConnectionIO(inspector_socket_t* socket); - void PushPendingMessage(std::vector<std::string>* queue, - const std::string& message); - void SwapBehindLock(std::vector<std::string> Agent::*queue, - std::vector<std::string>* output); - void PostMessages(); - void SetConnected(bool connected); - void Write(const std::string& message); - - uv_sem_t start_sem_; - uv_cond_t pause_cond_; - uv_mutex_t queue_lock_; - uv_mutex_t pause_lock_; - uv_thread_t thread_; - uv_loop_t child_loop_; - uv_tcp_t server_; - - int port_; - bool wait_; - bool connected_; - bool shutting_down_; - node::Environment* parent_env_; - - uv_async_t data_written_; - uv_async_t io_thread_req_; - inspector_socket_t* client_socket_; - blink::V8Inspector* inspector_; - v8::Platform* platform_; - std::vector<std::string> message_queue_; - std::vector<std::string> outgoing_message_queue_; - bool dispatching_messages_; - - friend class ChannelImpl; - friend class DispatchOnInspectorBackendTask; - friend class SetConnectedTask; - friend class V8NodeInspector; - friend void InterruptCallback(v8::Isolate*, void* agent); + private: + AgentImpl* impl; }; } // namespace inspector diff --git a/src/node.cc b/src/node.cc index 1544938cbe42e2..680fd1add188a0 100644 --- a/src/node.cc +++ b/src/node.cc @@ -4467,7 +4467,7 @@ static void StartNodeInstance(void* arg) { RunAtExit(env); #if HAVE_INSPECTOR - if (env->inspector_agent()->connected()) { + if (env->inspector_agent()->IsConnected()) { // Restore signal dispositions, the app is done and is no longer // capable of handling signals. #ifdef __POSIX__ From 38d36e32853aee8472257e7d205b30607fe1efa6 Mon Sep 17 00:00:00 2001 From: Anna Henningsen <anna@addaleax.net> Date: Tue, 31 May 2016 19:28:05 +0200 Subject: [PATCH 012/131] src: fix --without-inspector build `use_inspector` is not available if `HAVE_INSPECTOR` is false. Before this commit, one usage of it would show up as an undeclared identifier (introduced in a766ebf). PR-URL: https://github.com/nodejs/node/pull/7258 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- src/node.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/node.cc b/src/node.cc index 680fd1add188a0..a78293e67a1477 100644 --- a/src/node.cc +++ b/src/node.cc @@ -3443,11 +3443,15 @@ static bool ParseDebugOpt(const char* arg) { PrintHelp(); exit(12); } +#if HAVE_INSPECTOR if (use_inspector) { inspector_port = port_int; } else { +#endif debug_port = port_int; +#if HAVE_INSPECTOR } +#endif } return true; From 88b2aa3ce6c399e3eedfe5e02b3f515a543b06f2 Mon Sep 17 00:00:00 2001 From: Eugene Ostroukhov <eostroukhov@chromium.org> Date: Wed, 8 Jun 2016 14:09:28 -0700 Subject: [PATCH 013/131] inspector: process.exit should wait for inspector Fixes: https://github.com/nodejs/node/pull/7088 PR-URL: https://github.com/nodejs/node/pull/7252 Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl> Conflicts: src/node.cc --- src/node.cc | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/node.cc b/src/node.cc index a78293e67a1477..f67ca740700e9b 100644 --- a/src/node.cc +++ b/src/node.cc @@ -188,6 +188,7 @@ static v8::Platform* default_platform; #ifdef __POSIX__ static uv_sem_t debug_semaphore; +static const unsigned kMaxSignal = 32; #endif static void PrintErrorString(const char* format, ...) { @@ -2149,7 +2150,29 @@ static void InitGroups(const FunctionCallbackInfo<Value>& args) { #endif // __POSIX__ && !defined(__ANDROID__) +static void WaitForInspectorDisconnect(Environment* env) { +#if HAVE_INSPECTOR + if (env->inspector_agent()->IsConnected()) { + // Restore signal dispositions, the app is done and is no longer + // capable of handling signals. +#ifdef __POSIX__ + struct sigaction act; + memset(&act, 0, sizeof(act)); + for (unsigned nr = 1; nr < kMaxSignal; nr += 1) { + if (nr == SIGKILL || nr == SIGSTOP || nr == SIGPROF) + continue; + act.sa_handler = (nr == SIGPIPE) ? SIG_IGN : SIG_DFL; + CHECK_EQ(0, sigaction(nr, &act, nullptr)); + } +#endif + env->inspector_agent()->WaitForDisconnect(); + } +#endif +} + + void Exit(const FunctionCallbackInfo<Value>& args) { + WaitForInspectorDisconnect(Environment::GetCurrent(args)); exit(args[0]->Int32Value()); } @@ -4076,7 +4099,7 @@ inline void PlatformInit() { // The hard-coded upper limit is because NSIG is not very reliable; on Linux, // it evaluates to 32, 34 or 64, depending on whether RT signals are enabled. // Counting up to SIGRTMIN doesn't work for the same reason. - for (unsigned nr = 1; nr < 32; nr += 1) { + for (unsigned nr = 1; nr < kMaxSignal; nr += 1) { if (nr == SIGKILL || nr == SIGSTOP) continue; act.sa_handler = (nr == SIGPIPE) ? SIG_IGN : SIG_DFL; @@ -4470,24 +4493,7 @@ static void StartNodeInstance(void* arg) { instance_data->set_exit_code(exit_code); RunAtExit(env); -#if HAVE_INSPECTOR - if (env->inspector_agent()->IsConnected()) { - // Restore signal dispositions, the app is done and is no longer - // capable of handling signals. -#ifdef __POSIX__ - struct sigaction act; - memset(&act, 0, sizeof(act)); - for (unsigned nr = 1; nr < 32; nr += 1) { - if (nr == SIGKILL || nr == SIGSTOP || nr == SIGPROF) - continue; - act.sa_handler = (nr == SIGPIPE) ? SIG_IGN : SIG_DFL; - CHECK_EQ(0, sigaction(nr, &act, nullptr)); - } -#endif - env->inspector_agent()->WaitForDisconnect(); - } -#endif - + WaitForInspectorDisconnect(env); #if defined(LEAK_SANITIZER) __lsan_do_leak_check(); #endif From 3d69ad1cf3f2d4bed9ee9e9338e0a02f68716256 Mon Sep 17 00:00:00 2001 From: Myles Borins <myles.borins@gmail.com> Date: Thu, 9 Jun 2016 09:31:45 -0700 Subject: [PATCH 014/131] src: make Sec-WebSocket-Key check case-insensitive Current case sensitive comparison is breaking netty-based WS clients. replace strncmp with strncasecmp Fixes: https://github.com/nodejs/node/issues/7247 PR-URL: https://github.com/nodejs/node/pull/7248 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Ali Ijaz Sheikh <ofrobots@google.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> --- src/inspector_socket.cc | 9 ++++++--- src/util-inl.h | 10 ++++++++++ src/util.h | 3 +++ test/cctest/util.cc | 15 +++++++++++++++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/inspector_socket.cc b/src/inspector_socket.cc index cb248ec59feb1a..fa2b6734e9e186 100644 --- a/src/inspector_socket.cc +++ b/src/inspector_socket.cc @@ -1,4 +1,6 @@ #include "inspector_socket.h" +#include "util.h" +#include "util-inl.h" #define NODE_WANT_INTERNALS 1 #include "base64.h" @@ -445,9 +447,10 @@ static int header_value_cb(http_parser* parser, const char* at, size_t length) { struct http_parsing_state_s* state = (struct http_parsing_state_s*) (reinterpret_cast<inspector_socket_t*>(parser->data))->http_parsing_state; state->parsing_value = true; - if (state->current_header && strncmp(state->current_header, - SEC_WEBSOCKET_KEY_HEADER, - sizeof(SEC_WEBSOCKET_KEY_HEADER)) == 0) { + if (state->current_header && + node::StringEqualNoCaseN(state->current_header, + SEC_WEBSOCKET_KEY_HEADER, + sizeof(SEC_WEBSOCKET_KEY_HEADER))) { append(&state->ws_key, at, length); } return 0; diff --git a/src/util-inl.h b/src/util-inl.h index 67fe0bd26c3f16..a167a57c5ab67d 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -219,6 +219,16 @@ bool StringEqualNoCase(const char* a, const char* b) { return false; } +bool StringEqualNoCaseN(const char* a, const char* b, size_t length) { + for (size_t i = 0; i < length; i++) { + if (ToLower(a[i]) != ToLower(b[i])) + return false; + if (a[i] == '\0') + return true; + } + return true; +} + } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/util.h b/src/util.h index 077b7beb4d4ac3..9ad0f6c5ed0aae 100644 --- a/src/util.h +++ b/src/util.h @@ -206,6 +206,9 @@ inline char ToLower(char c); // strcasecmp() is locale-sensitive. Use StringEqualNoCase() instead. inline bool StringEqualNoCase(const char* a, const char* b); +// strncasecmp() is locale-sensitive. Use StringEqualNoCaseN() instead. +inline bool StringEqualNoCaseN(const char* a, const char* b, size_t length); + // Allocates an array of member type T. For up to kStackStorageSize items, // the stack is used, otherwise malloc(). template <typename T, size_t kStackStorageSize = 1024> diff --git a/test/cctest/util.cc b/test/cctest/util.cc index 37133aca562b72..862024aff33d6f 100644 --- a/test/cctest/util.cc +++ b/test/cctest/util.cc @@ -68,6 +68,21 @@ TEST(UtilTest, StringEqualNoCase) { EXPECT_FALSE(StringEqualNoCase("equals", "equal")); } +TEST(UtilTest, StringEqualNoCaseN) { + using node::StringEqualNoCaseN; + EXPECT_FALSE(StringEqualNoCaseN("a", "b", strlen("a"))); + EXPECT_TRUE(StringEqualNoCaseN("", "", strlen(""))); + EXPECT_TRUE(StringEqualNoCaseN("equal", "equal", strlen("equal"))); + EXPECT_TRUE(StringEqualNoCaseN("equal", "EQUAL", strlen("equal"))); + EXPECT_TRUE(StringEqualNoCaseN("EQUAL", "EQUAL", strlen("equal"))); + EXPECT_TRUE(StringEqualNoCaseN("equal", "equals", strlen("equal"))); + EXPECT_FALSE(StringEqualNoCaseN("equal", "equals", strlen("equals"))); + EXPECT_TRUE(StringEqualNoCaseN("equals", "equal", strlen("equal"))); + EXPECT_FALSE(StringEqualNoCaseN("equals", "equal", strlen("equals"))); + EXPECT_TRUE(StringEqualNoCaseN("abc\0abc", "abc\0efg", strlen("abcdefgh"))); + EXPECT_FALSE(StringEqualNoCaseN("abc\0abc", "abcd\0efg", strlen("abcdefgh"))); +} + TEST(UtilTest, ToLower) { using node::ToLower; EXPECT_EQ('0', ToLower('0')); From e1d6bd9e3095aefd370aea8c19a9baa307583418 Mon Sep 17 00:00:00 2001 From: Brian White <mscdex@mscdex.net> Date: Tue, 31 May 2016 13:03:59 -0400 Subject: [PATCH 015/131] stream: improve Readable.read() performance read() performance is improved most by switching from an array to a linked list for storing buffered data. However, other changes that also contribute include: making some hot functions inlinable, faster read() argument checking, and misc code rearrangement to avoid unnecessary code execution. PR-URL: https://github.com/nodejs/node/pull/7077 Reviewed-By: Calvin Metcalf <calvin.metcalf@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> --- benchmark/streams/readable-bigread.js | 32 ++ benchmark/streams/readable-bigunevenread.js | 32 ++ benchmark/streams/readable-boundaryread.js | 33 ++ benchmark/streams/readable-readall.js | 32 ++ benchmark/streams/readable-unevenread.js | 32 ++ lib/_stream_readable.js | 305 ++++++++++-------- lib/internal/streams/BufferList.js | 72 +++++ node.gyp | 1 + test/parallel/test-stream-push-order.js | 3 +- .../test-stream2-readable-from-list.js | 15 +- 10 files changed, 420 insertions(+), 137 deletions(-) create mode 100644 benchmark/streams/readable-bigread.js create mode 100644 benchmark/streams/readable-bigunevenread.js create mode 100644 benchmark/streams/readable-boundaryread.js create mode 100644 benchmark/streams/readable-readall.js create mode 100644 benchmark/streams/readable-unevenread.js create mode 100644 lib/internal/streams/BufferList.js diff --git a/benchmark/streams/readable-bigread.js b/benchmark/streams/readable-bigread.js new file mode 100644 index 00000000000000..50f10c44119141 --- /dev/null +++ b/benchmark/streams/readable-bigread.js @@ -0,0 +1,32 @@ +'use strict'; + +const common = require('../common'); +const v8 = require('v8'); +const Readable = require('stream').Readable; + +const bench = common.createBenchmark(main, { + n: [100e1] +}); + +function main(conf) { + const n = +conf.n; + const b = new Buffer(32); + const s = new Readable(); + function noop() {} + s._read = noop; + + // Force optimization before starting the benchmark + s.push(b); + v8.setFlagsFromString('--allow_natives_syntax'); + eval('%OptimizeFunctionOnNextCall(s.read)'); + s.push(b); + while (s.read(128)); + + bench.start(); + for (var k = 0; k < n; ++k) { + for (var i = 0; i < 1e4; ++i) + s.push(b); + while (s.read(128)); + } + bench.end(n); +} diff --git a/benchmark/streams/readable-bigunevenread.js b/benchmark/streams/readable-bigunevenread.js new file mode 100644 index 00000000000000..ce6e41c5d30eac --- /dev/null +++ b/benchmark/streams/readable-bigunevenread.js @@ -0,0 +1,32 @@ +'use strict'; + +const common = require('../common'); +const v8 = require('v8'); +const Readable = require('stream').Readable; + +const bench = common.createBenchmark(main, { + n: [100e1] +}); + +function main(conf) { + const n = +conf.n; + const b = new Buffer(32); + const s = new Readable(); + function noop() {} + s._read = noop; + + // Force optimization before starting the benchmark + s.push(b); + v8.setFlagsFromString('--allow_natives_syntax'); + eval('%OptimizeFunctionOnNextCall(s.read)'); + s.push(b); + while (s.read(106)); + + bench.start(); + for (var k = 0; k < n; ++k) { + for (var i = 0; i < 1e4; ++i) + s.push(b); + while (s.read(106)); + } + bench.end(n); +} diff --git a/benchmark/streams/readable-boundaryread.js b/benchmark/streams/readable-boundaryread.js new file mode 100644 index 00000000000000..93a6616361ba18 --- /dev/null +++ b/benchmark/streams/readable-boundaryread.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +const v8 = require('v8'); +const Readable = require('stream').Readable; + +const bench = common.createBenchmark(main, { + n: [200e1] +}); + +function main(conf) { + const n = +conf.n; + const b = new Buffer(32); + const s = new Readable(); + function noop() {} + s._read = noop; + + // Force optimization before starting the benchmark + s.push(b); + v8.setFlagsFromString('--allow_natives_syntax'); + eval('%OptimizeFunctionOnNextCall(s.push)'); + eval('%OptimizeFunctionOnNextCall(s.read)'); + s.push(b); + while (s.read(32)); + + bench.start(); + for (var k = 0; k < n; ++k) { + for (var i = 0; i < 1e4; ++i) + s.push(b); + while (s.read(32)); + } + bench.end(n); +} diff --git a/benchmark/streams/readable-readall.js b/benchmark/streams/readable-readall.js new file mode 100644 index 00000000000000..07626fd7978b9a --- /dev/null +++ b/benchmark/streams/readable-readall.js @@ -0,0 +1,32 @@ +'use strict'; + +const common = require('../common'); +const v8 = require('v8'); +const Readable = require('stream').Readable; + +const bench = common.createBenchmark(main, { + n: [50e2] +}); + +function main(conf) { + const n = +conf.n; + const b = new Buffer(32); + const s = new Readable(); + function noop() {} + s._read = noop; + + // Force optimization before starting the benchmark + s.push(b); + v8.setFlagsFromString('--allow_natives_syntax'); + eval('%OptimizeFunctionOnNextCall(s.read)'); + s.push(b); + while (s.read()); + + bench.start(); + for (var k = 0; k < n; ++k) { + for (var i = 0; i < 1e4; ++i) + s.push(b); + while (s.read()); + } + bench.end(n); +} diff --git a/benchmark/streams/readable-unevenread.js b/benchmark/streams/readable-unevenread.js new file mode 100644 index 00000000000000..4a69bd97a94bcf --- /dev/null +++ b/benchmark/streams/readable-unevenread.js @@ -0,0 +1,32 @@ +'use strict'; + +const common = require('../common'); +const v8 = require('v8'); +const Readable = require('stream').Readable; + +const bench = common.createBenchmark(main, { + n: [100e1] +}); + +function main(conf) { + const n = +conf.n; + const b = new Buffer(32); + const s = new Readable(); + function noop() {} + s._read = noop; + + // Force optimization before starting the benchmark + s.push(b); + v8.setFlagsFromString('--allow_natives_syntax'); + eval('%OptimizeFunctionOnNextCall(s.read)'); + s.push(b); + while (s.read(12)); + + bench.start(); + for (var k = 0; k < n; ++k) { + for (var i = 0; i < 1e4; ++i) + s.push(b); + while (s.read(12)); + } + bench.end(n); +} diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js index fc3e32b64e0734..0b55f2ea8e4ac8 100644 --- a/lib/_stream_readable.js +++ b/lib/_stream_readable.js @@ -8,27 +8,29 @@ const Stream = require('stream'); const Buffer = require('buffer').Buffer; const util = require('util'); const debug = util.debuglog('stream'); +const BufferList = require('internal/streams/BufferList'); var StringDecoder; util.inherits(Readable, Stream); -const hasPrependListener = typeof EE.prototype.prependListener === 'function'; - -function prependListener(emitter, event, fn) { - if (hasPrependListener) +var prependListener; +if (typeof EE.prototype.prependListener === 'function') { + prependListener = function prependListener(emitter, event, fn) { return emitter.prependListener(event, fn); - - // This is a brutally ugly hack to make sure that our error handler - // is attached before any userland ones. NEVER DO THIS. This is here - // only because this code needs to continue to work with older versions - // of Node.js that do not include the prependListener() method. The goal - // is to eventually remove this hack. - if (!emitter._events || !emitter._events[event]) - emitter.on(event, fn); - else if (Array.isArray(emitter._events[event])) - emitter._events[event].unshift(fn); - else - emitter._events[event] = [fn, emitter._events[event]]; + }; +} else { + prependListener = function prependListener(emitter, event, fn) { + // This is a hack to make sure that our error handler is attached before any + // userland ones. NEVER DO THIS. This is here only because this code needs + // to continue to work with older versions of Node.js that do not include + // the prependListener() method. The goal is to eventually remove this hack. + if (!emitter._events || !emitter._events[event]) + emitter.on(event, fn); + else if (Array.isArray(emitter._events[event])) + emitter._events[event].unshift(fn); + else + emitter._events[event] = [fn, emitter._events[event]]; + }; } function ReadableState(options, stream) { @@ -50,7 +52,10 @@ function ReadableState(options, stream) { // cast to ints. this.highWaterMark = ~~this.highWaterMark; - this.buffer = []; + // A linked list is used to store data chunks instead of an array because the + // linked list can remove elements from the beginning faster than + // array.shift() + this.buffer = new BufferList(); this.length = 0; this.pipes = null; this.pipesCount = 0; @@ -223,7 +228,8 @@ function computeNewHighWaterMark(n) { if (n >= MAX_HWM) { n = MAX_HWM; } else { - // Get the next highest power of 2 + // Get the next highest power of 2 to prevent increasing hwm excessively in + // tiny amounts n--; n |= n >>> 1; n |= n >>> 2; @@ -235,51 +241,41 @@ function computeNewHighWaterMark(n) { return n; } +// This function is designed to be inlinable, so please take care when making +// changes to the function body. function howMuchToRead(n, state) { - if (state.length === 0 && state.ended) + if (n <= 0 || (state.length === 0 && state.ended)) return 0; - if (state.objectMode) - return n === 0 ? 0 : 1; - - if (n === null || isNaN(n)) { - // only flow one buffer at a time - if (state.flowing && state.buffer.length) - return state.buffer[0].length; + return 1; + if (n !== n) { + // Only flow one buffer at a time + if (state.flowing && state.length) + return state.buffer.head.data.length; else return state.length; } - - if (n <= 0) - return 0; - - // If we're asking for more than the target buffer level, - // then raise the water mark. Bump up to the next highest - // power of 2, to prevent increasing it excessively in tiny - // amounts. + // If we're asking for more than the current hwm, then raise the hwm. if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n); - - // don't have that much. return null, unless we've ended. - if (n > state.length) { - if (!state.ended) { - state.needReadable = true; - return 0; - } else { - return state.length; - } + if (n <= state.length) + return n; + // Don't have enough + if (!state.ended) { + state.needReadable = true; + return 0; } - - return n; + return state.length; } // you can override either this method, or the async _read(n) below. Readable.prototype.read = function(n) { debug('read', n); + n = parseInt(n, 10); var state = this._readableState; var nOrig = n; - if (typeof n !== 'number' || n > 0) + if (n !== 0) state.emittedReadable = false; // if we're doing read(0) to trigger a readable event, but we @@ -342,9 +338,7 @@ Readable.prototype.read = function(n) { if (state.ended || state.reading) { doRead = false; debug('reading or ended', doRead); - } - - if (doRead) { + } else if (doRead) { debug('do read'); state.reading = true; state.sync = true; @@ -354,13 +348,12 @@ Readable.prototype.read = function(n) { // call internal read method this._read(state.highWaterMark); state.sync = false; + // If _read pushed data synchronously, then `reading` will be false, + // and we need to re-evaluate how much data we can return to the user. + if (!state.reading) + n = howMuchToRead(nOrig, state); } - // If _read pushed data synchronously, then `reading` will be false, - // and we need to re-evaluate how much data we can return to the user. - if (doRead && !state.reading) - n = howMuchToRead(nOrig, state); - var ret; if (n > 0) ret = fromList(n, state); @@ -370,18 +363,20 @@ Readable.prototype.read = function(n) { if (ret === null) { state.needReadable = true; n = 0; + } else { + state.length -= n; } - state.length -= n; - - // If we have nothing in the buffer, then we want to know - // as soon as we *do* get something into the buffer. - if (state.length === 0 && !state.ended) - state.needReadable = true; + if (state.length === 0) { + // If we have nothing in the buffer, then we want to know + // as soon as we *do* get something into the buffer. + if (!state.ended) + state.needReadable = true; - // If we tried to read() past the EOF, then emit end on the next tick. - if (nOrig !== n && state.ended && state.length === 0) - endReadable(this); + // If we tried to read() past the EOF, then emit end on the next tick. + if (nOrig !== n && state.ended) + endReadable(this); + } if (ret !== null) this.emit('data', ret); @@ -690,20 +685,17 @@ Readable.prototype.unpipe = function(dest) { // set up data events if they are asked for // Ensure readable listeners eventually get something Readable.prototype.on = function(ev, fn) { - var res = Stream.prototype.on.call(this, ev, fn); - - // If listening to data, and it has not explicitly been paused, - // then call resume to start the flow of data on the next tick. - if (ev === 'data' && false !== this._readableState.flowing) { - this.resume(); - } - - if (ev === 'readable' && !this._readableState.endEmitted) { - var state = this._readableState; - if (!state.readableListening) { - state.readableListening = true; + const res = Stream.prototype.on.call(this, ev, fn); + + if (ev === 'data') { + // Start flowing on next tick if stream isn't explicitly paused + if (this._readableState.flowing !== false) + this.resume(); + } else if (ev === 'readable') { + const state = this._readableState; + if (!state.endEmitted && !state.readableListening) { + state.readableListening = state.needReadable = true; state.emittedReadable = false; - state.needReadable = true; if (!state.reading) { process.nextTick(nReadingNextTick, this); } else if (state.length) { @@ -765,13 +757,9 @@ Readable.prototype.pause = function() { }; function flow(stream) { - var state = stream._readableState; + const state = stream._readableState; debug('flow', state.flowing); - if (state.flowing) { - do { - var chunk = stream.read(); - } while (null !== chunk && state.flowing); - } + while (state.flowing && stream.read() !== null); } // wrap an old-style stream as the async data source. @@ -846,69 +834,120 @@ Readable._fromList = fromList; // Pluck off n bytes from an array of buffers. // Length is the combined lengths of all the buffers in the list. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. function fromList(n, state) { - var list = state.buffer; - var length = state.length; - var stringMode = !!state.decoder; - var objectMode = !!state.objectMode; + // nothing buffered + if (state.length === 0) + return null; + var ret; + if (state.objectMode) + ret = state.buffer.shift(); + else if (!n || n >= state.length) { + // read it all, truncate the list + if (state.decoder) + ret = state.buffer.join(''); + else if (state.buffer.length === 1) + ret = state.buffer.head.data; + else + ret = state.buffer.concat(state.length); + state.buffer.clear(); + } else { + // read part of list + ret = fromListPartial(n, state.buffer, state.decoder); + } - // nothing in the list, definitely empty. - if (list.length === 0) - return null; + return ret; +} - if (length === 0) - ret = null; - else if (objectMode) +// Extracts only enough buffered data to satisfy the amount requested. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function fromListPartial(n, list, hasStrings) { + var ret; + if (n < list.head.data.length) { + // slice is the same for buffers and strings + ret = list.head.data.slice(0, n); + list.head.data = list.head.data.slice(n); + } else if (n === list.head.data.length) { + // first chunk is a perfect match ret = list.shift(); - else if (!n || n >= length) { - // read it all, truncate the array. - if (stringMode) - ret = list.join(''); - else if (list.length === 1) - ret = list[0]; - else - ret = Buffer.concat(list, length); - list.length = 0; } else { - // read just some of it. - if (n < list[0].length) { - // just take a part of the first list item. - // slice is the same for buffers and strings. - const buf = list[0]; - ret = buf.slice(0, n); - list[0] = buf.slice(n); - } else if (n === list[0].length) { - // first list is a perfect match - ret = list.shift(); - } else { - // complex case. - // we have enough to cover it, but it spans past the first buffer. - if (stringMode) - ret = ''; - else - ret = Buffer.allocUnsafe(n); - - var c = 0; - for (var i = 0, l = list.length; i < l && c < n; i++) { - const buf = list[0]; - var cpy = Math.min(n - c, buf.length); - - if (stringMode) - ret += buf.slice(0, cpy); - else - buf.copy(ret, c, 0, cpy); + // result spans more than one buffer + ret = (hasStrings + ? copyFromBufferString(n, list) + : copyFromBuffer(n, list)); + } + return ret; +} - if (cpy < buf.length) - list[0] = buf.slice(cpy); +// Copies a specified amount of characters from the list of buffered data +// chunks. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function copyFromBufferString(n, list) { + var p = list.head; + var c = 1; + var ret = p.data; + n -= ret.length; + while (p = p.next) { + const str = p.data; + const nb = (n > str.length ? str.length : n); + if (nb === str.length) + ret += str; + else + ret += str.slice(0, n); + n -= nb; + if (n === 0) { + if (nb === str.length) { + ++c; + if (p.next) + list.head = p.next; else - list.shift(); - - c += cpy; + list.head = list.tail = null; + } else { + list.head = p; + p.data = str.slice(nb); } + break; } + ++c; } + list.length -= c; + return ret; +} +// Copies a specified amount of bytes from the list of buffered data chunks. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function copyFromBuffer(n, list) { + const ret = Buffer.allocUnsafe(n); + var p = list.head; + var c = 1; + p.data.copy(ret); + n -= p.data.length; + while (p = p.next) { + const buf = p.data; + const nb = (n > buf.length ? buf.length : n); + buf.copy(ret, ret.length - n, 0, nb); + n -= nb; + if (n === 0) { + if (nb === buf.length) { + ++c; + if (p.next) + list.head = p.next; + else + list.head = list.tail = null; + } else { + list.head = p; + p.data = buf.slice(nb); + } + break; + } + ++c; + } + list.length -= c; return ret; } diff --git a/lib/internal/streams/BufferList.js b/lib/internal/streams/BufferList.js new file mode 100644 index 00000000000000..76da94bc83d977 --- /dev/null +++ b/lib/internal/streams/BufferList.js @@ -0,0 +1,72 @@ +'use strict'; + +const Buffer = require('buffer').Buffer; + +module.exports = BufferList; + +function BufferList() { + this.head = null; + this.tail = null; + this.length = 0; +} + +BufferList.prototype.push = function(v) { + const entry = { data: v, next: null }; + if (this.length > 0) + this.tail.next = entry; + else + this.head = entry; + this.tail = entry; + ++this.length; +}; + +BufferList.prototype.unshift = function(v) { + const entry = { data: v, next: this.head }; + if (this.length === 0) + this.tail = entry; + this.head = entry; + ++this.length; +}; + +BufferList.prototype.shift = function() { + if (this.length === 0) + return; + const ret = this.head.data; + if (this.length === 1) + this.head = this.tail = null; + else + this.head = this.head.next; + --this.length; + return ret; +}; + +BufferList.prototype.clear = function() { + this.head = this.tail = null; + this.length = 0; +}; + +BufferList.prototype.join = function(s) { + if (this.length === 0) + return ''; + var p = this.head; + var ret = '' + p.data; + while (p = p.next) + ret += s + p.data; + return ret; +}; + +BufferList.prototype.concat = function(n) { + if (this.length === 0) + return Buffer.alloc(0); + if (this.length === 1) + return this.head.data; + const ret = Buffer.allocUnsafe(n >>> 0); + var p = this.head; + var i = 0; + while (p) { + p.data.copy(ret, i); + i += p.data.length; + p = p.next; + } + return ret; +}; diff --git a/node.gyp b/node.gyp index 05a5530a2b148e..c26a42242d947c 100644 --- a/node.gyp +++ b/node.gyp @@ -88,6 +88,7 @@ 'lib/internal/v8_prof_polyfill.js', 'lib/internal/v8_prof_processor.js', 'lib/internal/streams/lazy_transform.js', + 'lib/internal/streams/BufferList.js', 'deps/v8/tools/splaytree.js', 'deps/v8/tools/codemap.js', 'deps/v8/tools/consarray.js', diff --git a/test/parallel/test-stream-push-order.js b/test/parallel/test-stream-push-order.js index 7c1b2602f6ab51..22a3156f62f502 100644 --- a/test/parallel/test-stream-push-order.js +++ b/test/parallel/test-stream-push-order.js @@ -26,7 +26,6 @@ s.read(0); // ACTUALLY [1, 3, 5, 6, 4, 2] process.on('exit', function() { - assert.deepStrictEqual(s._readableState.buffer, - ['1', '2', '3', '4', '5', '6']); + assert.deepStrictEqual(s._readableState.buffer.join(','), '1,2,3,4,5,6'); console.log('ok'); }); diff --git a/test/parallel/test-stream2-readable-from-list.js b/test/parallel/test-stream2-readable-from-list.js index e22c7a51940966..a823bf9dc10891 100644 --- a/test/parallel/test-stream2-readable-from-list.js +++ b/test/parallel/test-stream2-readable-from-list.js @@ -1,7 +1,9 @@ +// Flags: --expose_internals 'use strict'; require('../common'); var assert = require('assert'); var fromList = require('_stream_readable')._fromList; +var BufferList = require('internal/streams/BufferList'); // tiny node-tap lookalike. var tests = []; @@ -30,6 +32,13 @@ function run() { }); } +function bufferListFromArray(arr) { + const bl = new BufferList(); + for (var i = 0; i < arr.length; ++i) + bl.push(arr[i]); + return bl; +} + // ensure all tests have run process.on('exit', function() { assert.equal(count, 0); @@ -43,6 +52,7 @@ test('buffers', function(t) { Buffer.from('bark'), Buffer.from('bazy'), Buffer.from('kuel') ]; + list = bufferListFromArray(list); // read more than the first element. var ret = fromList(6, { buffer: list, length: 16 }); @@ -61,7 +71,7 @@ test('buffers', function(t) { t.equal(ret.toString(), 'zykuel'); // all consumed. - t.same(list, []); + t.same(list, new BufferList()); t.end(); }); @@ -71,6 +81,7 @@ test('strings', function(t) { 'bark', 'bazy', 'kuel' ]; + list = bufferListFromArray(list); // read more than the first element. var ret = fromList(6, { buffer: list, length: 16, decoder: true }); @@ -89,7 +100,7 @@ test('strings', function(t) { t.equal(ret, 'zykuel'); // all consumed. - t.same(list, []); + t.same(list, new BufferList()); t.end(); }); From fb39025e316adcca88b7905383b22202a356540b Mon Sep 17 00:00:00 2001 From: Mathias Bynens <mathias@qiwi.be> Date: Fri, 10 Jun 2016 18:42:08 +0200 Subject: [PATCH 016/131] punycode: update to v2.0.0 Punycode v2.0.0 drops support for old and non-Node environments. PR-URL: https://github.com/nodejs/node/pull/7267 Fixes: https://github.com/nodejs/node/issues/7224 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Roman Reiss <me@silverwind.io> --- lib/punycode.js | 860 +++++++++++++---------------- test/message/core_line_numbers.out | 8 +- 2 files changed, 389 insertions(+), 479 deletions(-) diff --git a/lib/punycode.js b/lib/punycode.js index 51aa75132937d5..9d788aefb59087 100644 --- a/lib/punycode.js +++ b/lib/punycode.js @@ -1,530 +1,440 @@ -/*! https://mths.be/punycode v1.3.2 by @mathias */ -;(function(root) { - - /** Detect free variables */ - var freeExports = typeof exports == 'object' && exports && - !exports.nodeType && exports; - var freeModule = typeof module == 'object' && module && - !module.nodeType && module; - var freeGlobal = typeof global == 'object' && global; - if ( - freeGlobal.global === freeGlobal || - freeGlobal.window === freeGlobal || - freeGlobal.self === freeGlobal - ) { - root = freeGlobal; +'use strict'; + +/** Highest positive signed 32-bit float value */ +const maxInt = 2147483647; // aka. 0x7FFFFFFF or 2^31-1 + +/** Bootstring parameters */ +const base = 36; +const tMin = 1; +const tMax = 26; +const skew = 38; +const damp = 700; +const initialBias = 72; +const initialN = 128; // 0x80 +const delimiter = '-'; // '\x2D' + +/** Regular expressions */ +const regexPunycode = /^xn--/; +const regexNonASCII = /[^\x20-\x7E]/; // unprintable ASCII chars + non-ASCII chars +const regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g; // RFC 3490 separators + +/** Error messages */ +const errors = { + 'overflow': 'Overflow: input needs wider integers to process', + 'not-basic': 'Illegal input >= 0x80 (not a basic code point)', + 'invalid-input': 'Invalid input' +}; + +/** Convenience shortcuts */ +const baseMinusTMin = base - tMin; +const floor = Math.floor; +const stringFromCharCode = String.fromCharCode; + +/*--------------------------------------------------------------------------*/ + +/** + * A generic error utility function. + * @private + * @param {String} type The error type. + * @returns {Error} Throws a `RangeError` with the applicable error message. + */ +function error(type) { + throw new RangeError(errors[type]); +} + +/** + * A generic `Array#map` utility function. + * @private + * @param {Array} array The array to iterate over. + * @param {Function} callback The function that gets called for every array + * item. + * @returns {Array} A new array of values returned by the callback function. + */ +function map(array, fn) { + const result = []; + let length = array.length; + while (length--) { + result[length] = fn(array[length]); } - - /** - * The `punycode` object. - * @name punycode - * @type Object - */ - var punycode, - - /** Highest positive signed 32-bit float value */ - maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1 - - /** Bootstring parameters */ - base = 36, - tMin = 1, - tMax = 26, - skew = 38, - damp = 700, - initialBias = 72, - initialN = 128, // 0x80 - delimiter = '-', // '\x2D' - - /** Regular expressions */ - regexPunycode = /^xn--/, - regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars - regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators - - /** Error messages */ - errors = { - 'overflow': 'Overflow: input needs wider integers to process', - 'not-basic': 'Illegal input >= 0x80 (not a basic code point)', - 'invalid-input': 'Invalid input' - }, - - /** Convenience shortcuts */ - baseMinusTMin = base - tMin, - floor = Math.floor, - stringFromCharCode = String.fromCharCode, - - /** Temporary variable */ - key; - - /*--------------------------------------------------------------------------*/ - - /** - * A generic error utility function. - * @private - * @param {String} type The error type. - * @returns {Error} Throws a `RangeError` with the applicable error message. - */ - function error(type) { - throw new RangeError(errors[type]); + return result; +} + +/** + * A simple `Array#map`-like wrapper to work with domain name strings or email + * addresses. + * @private + * @param {String} domain The domain name or email address. + * @param {Function} callback The function that gets called for every + * character. + * @returns {Array} A new string of characters returned by the callback + * function. + */ +function mapDomain(string, fn) { + const parts = string.split('@'); + let result = ''; + if (parts.length > 1) { + // In email addresses, only the domain name should be punycoded. Leave + // the local part (i.e. everything up to `@`) intact. + result = parts[0] + '@'; + string = parts[1]; } - - /** - * A generic `Array#map` utility function. - * @private - * @param {Array} array The array to iterate over. - * @param {Function} callback The function that gets called for every array - * item. - * @returns {Array} A new array of values returned by the callback function. - */ - function map(array, fn) { - var length = array.length; - var result = []; - while (length--) { - result[length] = fn(array[length]); - } - return result; - } - - /** - * A simple `Array#map`-like wrapper to work with domain name strings or email - * addresses. - * @private - * @param {String} domain The domain name or email address. - * @param {Function} callback The function that gets called for every - * character. - * @returns {Array} A new string of characters returned by the callback - * function. - */ - function mapDomain(string, fn) { - var parts = string.split('@'); - var result = ''; - if (parts.length > 1) { - // In email addresses, only the domain name should be punycoded. Leave - // the local part (i.e. everything up to `@`) intact. - result = parts[0] + '@'; - string = parts[1]; - } - // Avoid `split(regex)` for IE8 compatibility. See #17. - string = string.replace(regexSeparators, '\x2E'); - var labels = string.split('.'); - var encoded = map(labels, fn).join('.'); - return result + encoded; - } - - /** - * Creates an array containing the numeric code points of each Unicode - * character in the string. While JavaScript uses UCS-2 internally, - * this function will convert a pair of surrogate halves (each of which - * UCS-2 exposes as separate characters) into a single code point, - * matching UTF-16. - * @see `punycode.ucs2.encode` - * @see <https://mathiasbynens.be/notes/javascript-encoding> - * @memberOf punycode.ucs2 - * @name decode - * @param {String} string The Unicode input string (UCS-2). - * @returns {Array} The new array of code points. - */ - function ucs2decode(string) { - var output = [], - counter = 0, - length = string.length, - value, - extra; - while (counter < length) { - value = string.charCodeAt(counter++); - if (value >= 0xD800 && value <= 0xDBFF && counter < length) { - // high surrogate, and there is a next character - extra = string.charCodeAt(counter++); - if ((extra & 0xFC00) == 0xDC00) { // low surrogate - output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); - } else { - // unmatched surrogate; only append this code unit, in case the next - // code unit is the high surrogate of a surrogate pair - output.push(value); - counter--; - } + // Avoid `split(regex)` for IE8 compatibility. See #17. + string = string.replace(regexSeparators, '\x2E'); + const labels = string.split('.'); + const encoded = map(labels, fn).join('.'); + return result + encoded; +} + +/** + * Creates an array containing the numeric code points of each Unicode + * character in the string. While JavaScript uses UCS-2 internally, + * this function will convert a pair of surrogate halves (each of which + * UCS-2 exposes as separate characters) into a single code point, + * matching UTF-16. + * @see `punycode.ucs2.encode` + * @see <https://mathiasbynens.be/notes/javascript-encoding> + * @memberOf punycode.ucs2 + * @name decode + * @param {String} string The Unicode input string (UCS-2). + * @returns {Array} The new array of code points. + */ +function ucs2decode(string) { + const output = []; + let counter = 0; + const length = string.length; + while (counter < length) { + const value = string.charCodeAt(counter++); + if (value >= 0xD800 && value <= 0xDBFF && counter < length) { + // It's a high surrogate, and there is a next character. + const extra = string.charCodeAt(counter++); + if ((extra & 0xFC00) == 0xDC00) { // Low surrogate. + output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); } else { + // It's an unmatched surrogate; only append this code unit, in case the + // next code unit is the high surrogate of a surrogate pair. output.push(value); + counter--; } + } else { + output.push(value); } - return output; } - - /** - * Creates a string based on an array of numeric code points. - * @see `punycode.ucs2.decode` - * @memberOf punycode.ucs2 - * @name encode - * @param {Array} codePoints The array of numeric code points. - * @returns {String} The new Unicode string (UCS-2). - */ - function ucs2encode(array) { - return map(array, function(value) { - var output = ''; - if (value > 0xFFFF) { - value -= 0x10000; - output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); - value = 0xDC00 | value & 0x3FF; - } - output += stringFromCharCode(value); - return output; - }).join(''); + return output; +} + +/** + * Creates a string based on an array of numeric code points. + * @see `punycode.ucs2.decode` + * @memberOf punycode.ucs2 + * @name encode + * @param {Array} codePoints The array of numeric code points. + * @returns {String} The new Unicode string (UCS-2). + */ +const ucs2encode = array => String.fromCodePoint(...array); + +/** + * Converts a basic code point into a digit/integer. + * @see `digitToBasic()` + * @private + * @param {Number} codePoint The basic numeric code point value. + * @returns {Number} The numeric value of a basic code point (for use in + * representing integers) in the range `0` to `base - 1`, or `base` if + * the code point does not represent a value. + */ +const basicToDigit = function(codePoint) { + if (codePoint - 0x30 < 0x0A) { + return codePoint - 0x16; } - - /** - * Converts a basic code point into a digit/integer. - * @see `digitToBasic()` - * @private - * @param {Number} codePoint The basic numeric code point value. - * @returns {Number} The numeric value of a basic code point (for use in - * representing integers) in the range `0` to `base - 1`, or `base` if - * the code point does not represent a value. - */ - function basicToDigit(codePoint) { - if (codePoint - 48 < 10) { - return codePoint - 22; - } - if (codePoint - 65 < 26) { - return codePoint - 65; - } - if (codePoint - 97 < 26) { - return codePoint - 97; - } - return base; + if (codePoint - 0x41 < 0x1A) { + return codePoint - 0x41; } - - /** - * Converts a digit/integer into a basic code point. - * @see `basicToDigit()` - * @private - * @param {Number} digit The numeric value of a basic code point. - * @returns {Number} The basic code point whose value (when used for - * representing integers) is `digit`, which needs to be in the range - * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is - * used; else, the lowercase form is used. The behavior is undefined - * if `flag` is non-zero and `digit` has no uppercase form. - */ - function digitToBasic(digit, flag) { - // 0..25 map to ASCII a..z or A..Z - // 26..35 map to ASCII 0..9 - return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5); + if (codePoint - 0x61 < 0x1A) { + return codePoint - 0x61; } - - /** - * Bias adaptation function as per section 3.4 of RFC 3492. - * https://tools.ietf.org/html/rfc3492#section-3.4 - * @private - */ - function adapt(delta, numPoints, firstTime) { - var k = 0; - delta = firstTime ? floor(delta / damp) : delta >> 1; - delta += floor(delta / numPoints); - for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) { - delta = floor(delta / baseMinusTMin); - } - return floor(k + (baseMinusTMin + 1) * delta / (delta + skew)); + return base; +}; + +/** + * Converts a digit/integer into a basic code point. + * @see `basicToDigit()` + * @private + * @param {Number} digit The numeric value of a basic code point. + * @returns {Number} The basic code point whose value (when used for + * representing integers) is `digit`, which needs to be in the range + * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is + * used; else, the lowercase form is used. The behavior is undefined + * if `flag` is non-zero and `digit` has no uppercase form. + */ +const digitToBasic = function(digit, flag) { + // 0..25 map to ASCII a..z or A..Z + // 26..35 map to ASCII 0..9 + return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5); +}; + +/** + * Bias adaptation function as per section 3.4 of RFC 3492. + * https://tools.ietf.org/html/rfc3492#section-3.4 + * @private + */ +const adapt = function(delta, numPoints, firstTime) { + let k = 0; + delta = firstTime ? floor(delta / damp) : delta >> 1; + delta += floor(delta / numPoints); + for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) { + delta = floor(delta / baseMinusTMin); + } + return floor(k + (baseMinusTMin + 1) * delta / (delta + skew)); +}; + +/** + * Converts a Punycode string of ASCII-only symbols to a string of Unicode + * symbols. + * @memberOf punycode + * @param {String} input The Punycode string of ASCII-only symbols. + * @returns {String} The resulting string of Unicode symbols. + */ +const decode = function(input) { + // Don't use UCS-2. + const output = []; + const inputLength = input.length; + let i = 0; + let n = initialN; + let bias = initialBias; + + // Handle the basic code points: let `basic` be the number of input code + // points before the last delimiter, or `0` if there is none, then copy + // the first basic code points to the output. + + let basic = input.lastIndexOf(delimiter); + if (basic < 0) { + basic = 0; } - /** - * Converts a Punycode string of ASCII-only symbols to a string of Unicode - * symbols. - * @memberOf punycode - * @param {String} input The Punycode string of ASCII-only symbols. - * @returns {String} The resulting string of Unicode symbols. - */ - function decode(input) { - // Don't use UCS-2 - var output = [], - inputLength = input.length, - out, - i = 0, - n = initialN, - bias = initialBias, - basic, - j, - index, - oldi, - w, - k, - digit, - t, - /** Cached calculation results */ - baseMinusT; - - // Handle the basic code points: let `basic` be the number of input code - // points before the last delimiter, or `0` if there is none, then copy - // the first basic code points to the output. - - basic = input.lastIndexOf(delimiter); - if (basic < 0) { - basic = 0; - } - - for (j = 0; j < basic; ++j) { - // if it's not a basic code point - if (input.charCodeAt(j) >= 0x80) { - error('not-basic'); - } - output.push(input.charCodeAt(j)); + for (let j = 0; j < basic; ++j) { + // if it's not a basic code point + if (input.charCodeAt(j) >= 0x80) { + error('not-basic'); } + output.push(input.charCodeAt(j)); + } - // Main decoding loop: start just after the last delimiter if any basic code - // points were copied; start at the beginning otherwise. - - for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) { - - // `index` is the index of the next character to be consumed. - // Decode a generalized variable-length integer into `delta`, - // which gets added to `i`. The overflow checking is easier - // if we increase `i` as we go, then subtract off its starting - // value at the end to obtain `delta`. - for (oldi = i, w = 1, k = base; /* no condition */; k += base) { - - if (index >= inputLength) { - error('invalid-input'); - } + // Main decoding loop: start just after the last delimiter if any basic code + // points were copied; start at the beginning otherwise. - digit = basicToDigit(input.charCodeAt(index++)); + for (let index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) { - if (digit >= base || digit > floor((maxInt - i) / w)) { - error('overflow'); - } + // `index` is the index of the next character to be consumed. + // Decode a generalized variable-length integer into `delta`, + // which gets added to `i`. The overflow checking is easier + // if we increase `i` as we go, then subtract off its starting + // value at the end to obtain `delta`. + let oldi = i; + for (let w = 1, k = base; /* no condition */; k += base) { - i += digit * w; - t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); + if (index >= inputLength) { + error('invalid-input'); + } - if (digit < t) { - break; - } + const digit = basicToDigit(input.charCodeAt(index++)); - baseMinusT = base - t; - if (w > floor(maxInt / baseMinusT)) { - error('overflow'); - } + if (digit >= base || digit > floor((maxInt - i) / w)) { + error('overflow'); + } - w *= baseMinusT; + i += digit * w; + const t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); + if (digit < t) { + break; } - out = output.length + 1; - bias = adapt(i - oldi, out, oldi == 0); - - // `i` was supposed to wrap around from `out` to `0`, - // incrementing `n` each time, so we'll fix that now: - if (floor(i / out) > maxInt - n) { + const baseMinusT = base - t; + if (w > floor(maxInt / baseMinusT)) { error('overflow'); } - n += floor(i / out); - i %= out; - - // Insert `n` at position `i` of the output - output.splice(i++, 0, n); + w *= baseMinusT; } - return ucs2encode(output); - } + const out = output.length + 1; + bias = adapt(i - oldi, out, oldi == 0); - /** - * Converts a string of Unicode symbols (e.g. a domain name label) to a - * Punycode string of ASCII-only symbols. - * @memberOf punycode - * @param {String} input The string of Unicode symbols. - * @returns {String} The resulting Punycode string of ASCII-only symbols. - */ - function encode(input) { - var n, - delta, - handledCPCount, - basicLength, - bias, - j, - m, - q, - k, - t, - currentValue, - output = [], - /** `inputLength` will hold the number of code points in `input`. */ - inputLength, - /** Cached calculation results */ - handledCPCountPlusOne, - baseMinusT, - qMinusT; - - // Convert the input in UCS-2 to Unicode - input = ucs2decode(input); - - // Cache the length - inputLength = input.length; - - // Initialize the state - n = initialN; - delta = 0; - bias = initialBias; - - // Handle the basic code points - for (j = 0; j < inputLength; ++j) { - currentValue = input[j]; - if (currentValue < 0x80) { - output.push(stringFromCharCode(currentValue)); - } + // `i` was supposed to wrap around from `out` to `0`, + // incrementing `n` each time, so we'll fix that now: + if (floor(i / out) > maxInt - n) { + error('overflow'); } - handledCPCount = basicLength = output.length; + n += floor(i / out); + i %= out; - // `handledCPCount` is the number of code points that have been handled; - // `basicLength` is the number of basic code points. + // Insert `n` at position `i` of the output. + output.splice(i++, 0, n); - // Finish the basic string - if it is not empty - with a delimiter - if (basicLength) { - output.push(delimiter); + } + + return String.fromCodePoint(...output); +}; + +/** + * Converts a string of Unicode symbols (e.g. a domain name label) to a + * Punycode string of ASCII-only symbols. + * @memberOf punycode + * @param {String} input The string of Unicode symbols. + * @returns {String} The resulting Punycode string of ASCII-only symbols. + */ +const encode = function(input) { + const output = []; + + // Convert the input in UCS-2 to an array of Unicode code points. + input = ucs2decode(input); + + // Cache the length. + let inputLength = input.length; + + // Initialize the state. + let n = initialN; + let delta = 0; + let bias = initialBias; + + // Handle the basic code points. + for (const currentValue of input) { + if (currentValue < 0x80) { + output.push(stringFromCharCode(currentValue)); } + } - // Main encoding loop: - while (handledCPCount < inputLength) { + let basicLength = output.length; + let handledCPCount = basicLength; - // All non-basic code points < n have been handled already. Find the next - // larger one: - for (m = maxInt, j = 0; j < inputLength; ++j) { - currentValue = input[j]; - if (currentValue >= n && currentValue < m) { - m = currentValue; - } - } + // `handledCPCount` is the number of code points that have been handled; + // `basicLength` is the number of basic code points. - // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>, - // but guard against overflow - handledCPCountPlusOne = handledCPCount + 1; - if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) { - error('overflow'); - } + // Finish the basic string with a delimiter unless it's empty. + if (basicLength) { + output.push(delimiter); + } - delta += (m - n) * handledCPCountPlusOne; - n = m; + // Main encoding loop: + while (handledCPCount < inputLength) { - for (j = 0; j < inputLength; ++j) { - currentValue = input[j]; + // All non-basic code points < n have been handled already. Find the next + // larger one: + let m = maxInt; + for (const currentValue of input) { + if (currentValue >= n && currentValue < m) { + m = currentValue; + } + } - if (currentValue < n && ++delta > maxInt) { - error('overflow'); - } + // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>, + // but guard against overflow. + const handledCPCountPlusOne = handledCPCount + 1; + if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) { + error('overflow'); + } - if (currentValue == n) { - // Represent delta as a generalized variable-length integer - for (q = delta, k = base; /* no condition */; k += base) { - t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); - if (q < t) { - break; - } - qMinusT = q - t; - baseMinusT = base - t; - output.push( - stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0)) - ); - q = floor(qMinusT / baseMinusT); - } + delta += (m - n) * handledCPCountPlusOne; + n = m; - output.push(stringFromCharCode(digitToBasic(q, 0))); - bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength); - delta = 0; - ++handledCPCount; + for (const currentValue of input) { + if (currentValue < n && ++delta > maxInt) { + error('overflow'); + } + if (currentValue == n) { + // Represent delta as a generalized variable-length integer. + let q = delta; + for (let k = base; /* no condition */; k += base) { + const t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); + if (q < t) { + break; + } + const qMinusT = q - t; + const baseMinusT = base - t; + output.push( + stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0)) + ); + q = floor(qMinusT / baseMinusT); } + + output.push(stringFromCharCode(digitToBasic(q, 0))); + bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength); + delta = 0; + ++handledCPCount; } + } - ++delta; - ++n; + ++delta; + ++n; - } - return output.join(''); } - + return output.join(''); +}; + +/** + * Converts a Punycode string representing a domain name or an email address + * to Unicode. Only the Punycoded parts of the input will be converted, i.e. + * it doesn't matter if you call it on a string that has already been + * converted to Unicode. + * @memberOf punycode + * @param {String} input The Punycoded domain name or email address to + * convert to Unicode. + * @returns {String} The Unicode representation of the given Punycode + * string. + */ +const toUnicode = function(input) { + return mapDomain(input, function(string) { + return regexPunycode.test(string) + ? decode(string.slice(4).toLowerCase()) + : string; + }); +}; + +/** + * Converts a Unicode string representing a domain name or an email address to + * Punycode. Only the non-ASCII parts of the domain name will be converted, + * i.e. it doesn't matter if you call it with a domain that's already in + * ASCII. + * @memberOf punycode + * @param {String} input The domain name or email address to convert, as a + * Unicode string. + * @returns {String} The Punycode representation of the given domain name or + * email address. + */ +const toASCII = function(input) { + return mapDomain(input, function(string) { + return regexNonASCII.test(string) + ? 'xn--' + encode(string) + : string; + }); +}; + +/*--------------------------------------------------------------------------*/ + +/** Define the public API */ +const punycode = { /** - * Converts a Punycode string representing a domain name or an email address - * to Unicode. Only the Punycoded parts of the input will be converted, i.e. - * it doesn't matter if you call it on a string that has already been - * converted to Unicode. + * A string representing the current Punycode.js version number. * @memberOf punycode - * @param {String} input The Punycoded domain name or email address to - * convert to Unicode. - * @returns {String} The Unicode representation of the given Punycode - * string. + * @type String */ - function toUnicode(input) { - return mapDomain(input, function(string) { - return regexPunycode.test(string) - ? decode(string.slice(4).toLowerCase()) - : string; - }); - } - + 'version': '2.0.0', /** - * Converts a Unicode string representing a domain name or an email address to - * Punycode. Only the non-ASCII parts of the domain name will be converted, - * i.e. it doesn't matter if you call it with a domain that's already in - * ASCII. + * An object of methods to convert from JavaScript's internal character + * representation (UCS-2) to Unicode code points, and back. + * @see <https://mathiasbynens.be/notes/javascript-encoding> * @memberOf punycode - * @param {String} input The domain name or email address to convert, as a - * Unicode string. - * @returns {String} The Punycode representation of the given domain name or - * email address. + * @type Object */ - function toASCII(input) { - return mapDomain(input, function(string) { - return regexNonASCII.test(string) - ? 'xn--' + encode(string) - : string; - }); - } - - /*--------------------------------------------------------------------------*/ - - /** Define the public API */ - punycode = { - /** - * A string representing the current Punycode.js version number. - * @memberOf punycode - * @type String - */ - 'version': '1.3.2', - /** - * An object of methods to convert from JavaScript's internal character - * representation (UCS-2) to Unicode code points, and back. - * @see <https://mathiasbynens.be/notes/javascript-encoding> - * @memberOf punycode - * @type Object - */ - 'ucs2': { - 'decode': ucs2decode, - 'encode': ucs2encode - }, - 'decode': decode, - 'encode': encode, - 'toASCII': toASCII, - 'toUnicode': toUnicode - }; - - /** Expose `punycode` */ - // Some AMD build optimizers, like r.js, check for specific condition patterns - // like the following: - if ( - typeof define == 'function' && - typeof define.amd == 'object' && - define.amd - ) { - define('punycode', function() { - return punycode; - }); - } else if (freeExports && freeModule) { - if (module.exports == freeExports) { // in Node.js or RingoJS v0.8.0+ - freeModule.exports = punycode; - } else { // in Narwhal or RingoJS v0.7.0- - for (key in punycode) { - punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]); - } - } - } else { // in Rhino or a web browser - root.punycode = punycode; - } + 'ucs2': { + 'decode': ucs2decode, + 'encode': ucs2encode + }, + 'decode': decode, + 'encode': encode, + 'toASCII': toASCII, + 'toUnicode': toUnicode +}; -}(this)); +module.exports = punycode; diff --git a/test/message/core_line_numbers.out b/test/message/core_line_numbers.out index b8394e5c3f2deb..0dd38a3996d8e9 100644 --- a/test/message/core_line_numbers.out +++ b/test/message/core_line_numbers.out @@ -1,9 +1,9 @@ -punycode.js:67 - throw new RangeError(errors[type]); - ^ +punycode.js:42 + throw new RangeError(errors[type]); + ^ RangeError: Invalid input - at error (punycode.js:67:*) + at error (punycode.js:42:*) at Object.decode (punycode.js:*:*) at Object.<anonymous> (*test*message*core_line_numbers.js:*:*) at Module._compile (module.js:*:*) From cbe57479c496049726001581abc5f296b94a6e9f Mon Sep 17 00:00:00 2001 From: Ali Ijaz Sheikh <ofrobots@google.com> Date: Tue, 14 Jun 2016 07:47:54 -0700 Subject: [PATCH 017/131] deps: switch to upstream v8_inspector This change picks up v8_inspector directly from the upstream chromium repository [1]; dropping the intermediate repository. The upstream code has been refactored substantially to make it easy to share code without adaptation with Node.js. The deps/v8_inspector directory is now simply a gathering of dependencies: * platform/v8_inspector: vendored from [2]. * platform/inspector_protocol: vendored from [3]. * deps/jinja2: vendored from [4]. * deps/markupsafe: vendored from [5]. [1]: https://chromium.googlesource.com/chromium/src/third_party/WebKit/Source/platform [2]: https://chromium.googlesource.com/chromium/src/third_party/WebKit/Source/platform/v8_inspector [3]: https://chromium.googlesource.com/chromium/src/third_party/WebKit/Source/platform/inspector_protocol [4]: https://github.com/mitsuhiko/jinja2 [5]: https://github.com/mitsuhiko/markupsafe PR-URL: https://github.com/nodejs/node/pull/7302 Reviewed-By: indutny - Fedor Indutny <fedor.indutny@gmail.com> Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl> --- configure | 1 + deps/v8_inspector/README.md | 12 +- deps/v8_inspector/devtools/Inspector-1.1.json | 3924 ------------- deps/v8_inspector/devtools/protocol.json | 5167 ----------------- deps/v8_inspector/platform/PlatformExport.h | 66 - .../platform/inspector_protocol/Array.h | 4 +- .../inspector_protocol/BackendCallback.h | 23 + .../inspector_protocol/Backend_cpp.template | 0 .../inspector_protocol/Backend_h.template | 78 - .../inspector_protocol/CodeGenerator.py | 177 +- .../platform/inspector_protocol/Collections.h | 9 + .../inspector_protocol/CollectionsSTL.h | 11 +- .../inspector_protocol/CollectionsWTF.h | 14 +- .../inspector_protocol/DispatcherBase.cpp | 173 + .../inspector_protocol/DispatcherBase.h | 97 + .../Dispatcher_cpp.template | 365 -- .../inspector_protocol/Dispatcher_h.template | 65 - .../inspector_protocol/ErrorSupport.cpp | 1 - .../inspector_protocol/ErrorSupport.h | 6 +- .../inspector_protocol/FrontendChannel.h | 35 +- .../inspector_protocol/Frontend_cpp.template | 53 - .../inspector_protocol/Frontend_h.template | 57 - .../platform/inspector_protocol/Maybe.h | 6 +- .../platform/inspector_protocol/Object.cpp | 35 + .../platform/inspector_protocol/Object.h | 30 + .../platform/inspector_protocol/Parser.cpp | 1 - .../platform/inspector_protocol/Parser.h | 3 +- .../platform/inspector_protocol/Platform.h | 14 + .../platform/inspector_protocol/PlatformSTL.h | 281 + .../platform/inspector_protocol/PlatformWTF.h | 14 + .../inspector_protocol/String16STL.cpp | 16 +- .../platform/inspector_protocol/String16STL.h | 15 +- .../platform/inspector_protocol/String16WTF.h | 4 +- .../TypeBuilder_cpp.template | 234 +- .../inspector_protocol/TypeBuilder_h.template | 169 +- .../inspector_protocol/ValueConversions.h | 11 +- .../platform/inspector_protocol/Values.cpp | 3 +- .../platform/inspector_protocol/Values.h | 5 +- .../generate-inspector-protocol-version | 115 +- .../platform/inspector_protocol/protocol.gyp | 89 - .../platform/v8_inspector/Atomics.h | 6 +- .../platform/v8_inspector/InjectedScript.cpp | 36 +- .../platform/v8_inspector/InjectedScript.h | 10 +- .../v8_inspector/InjectedScriptNative.cpp | 1 - .../v8_inspector/InjectedScriptNative.h | 2 +- .../v8_inspector/InjectedScriptSource.js | 221 +- .../v8_inspector/InspectorWrapper.cpp | 70 - .../platform/v8_inspector/InspectorWrapper.h | 88 - .../v8_inspector/JavaScriptCallFrame.h | 3 +- .../v8_inspector/platform/v8_inspector/OWNERS | 6 + .../platform/v8_inspector/RemoteObjectId.cpp | 2 +- .../platform/v8_inspector/RemoteObjectId.h | 4 +- .../platform/v8_inspector/V8Compat.h | 4 +- .../platform/v8_inspector/V8Console.cpp | 161 +- .../platform/v8_inspector/V8Console.h | 22 +- .../v8_inspector/V8DebuggerAgentImpl.cpp | 39 +- .../v8_inspector/V8DebuggerAgentImpl.h | 19 +- .../platform/v8_inspector/V8DebuggerImpl.cpp | 23 +- .../platform/v8_inspector/V8DebuggerImpl.h | 9 +- .../platform/v8_inspector/V8FunctionCall.cpp | 2 +- .../v8_inspector/V8HeapProfilerAgentImpl.cpp | 38 +- .../v8_inspector/V8HeapProfilerAgentImpl.h | 17 +- .../v8_inspector/V8InjectedScriptHost.cpp | 69 +- .../v8_inspector/V8InjectedScriptHost.h | 4 - .../v8_inspector/V8InspectorSessionImpl.cpp | 115 +- .../v8_inspector/V8InspectorSessionImpl.h | 29 +- .../v8_inspector/V8ProfilerAgentImpl.cpp | 20 +- .../v8_inspector/V8ProfilerAgentImpl.h | 15 +- .../platform/v8_inspector/V8Regex.cpp | 1 - .../v8_inspector/V8RuntimeAgentImpl.cpp | 34 +- .../v8_inspector/V8RuntimeAgentImpl.h | 19 +- .../v8_inspector/V8StackTraceImpl.cpp | 33 +- .../platform/v8_inspector/V8StackTraceImpl.h | 8 +- .../v8_inspector/injected_script_externs.js | 22 +- .../v8_inspector/js_protocol-1.1.json | 1866 ++++++ .../platform/v8_inspector/js_protocol.json | 976 ++++ .../v8_inspector/public/V8ContentSearchUtil.h | 3 +- .../platform/v8_inspector/public/V8Debugger.h | 24 +- .../v8_inspector/public/V8DebuggerAgent.h | 24 - .../v8_inspector/public/V8DebuggerClient.h | 9 +- .../v8_inspector/public/V8EventListenerInfo.h | 36 - .../v8_inspector/public/V8HeapProfilerAgent.h | 23 - .../v8_inspector/public/V8Inspector.cpp | 58 +- .../v8_inspector/public/V8Inspector.h | 14 +- .../v8_inspector/public/V8InspectorSession.h | 16 +- .../public/V8InspectorSessionClient.h | 7 +- .../v8_inspector/public/V8ProfilerAgent.h | 22 - .../v8_inspector/public/V8RuntimeAgent.h | 23 - .../v8_inspector/public/V8StackTrace.h | 7 +- .../platform/v8_inspector/v8_inspector.gyp | 189 + deps/v8_inspector/v8_inspector.gyp | 35 - deps/v8_inspector/v8_inspector.gypi | 94 - node.gyp | 2 +- src/inspector_agent.cc | 17 +- 94 files changed, 4713 insertions(+), 11267 deletions(-) delete mode 100644 deps/v8_inspector/devtools/Inspector-1.1.json delete mode 100644 deps/v8_inspector/devtools/protocol.json delete mode 100644 deps/v8_inspector/platform/PlatformExport.h create mode 100644 deps/v8_inspector/platform/inspector_protocol/BackendCallback.h delete mode 100644 deps/v8_inspector/platform/inspector_protocol/Backend_cpp.template delete mode 100644 deps/v8_inspector/platform/inspector_protocol/Backend_h.template create mode 100644 deps/v8_inspector/platform/inspector_protocol/DispatcherBase.cpp create mode 100644 deps/v8_inspector/platform/inspector_protocol/DispatcherBase.h delete mode 100644 deps/v8_inspector/platform/inspector_protocol/Dispatcher_cpp.template delete mode 100644 deps/v8_inspector/platform/inspector_protocol/Dispatcher_h.template delete mode 100644 deps/v8_inspector/platform/inspector_protocol/Frontend_cpp.template delete mode 100644 deps/v8_inspector/platform/inspector_protocol/Frontend_h.template create mode 100644 deps/v8_inspector/platform/inspector_protocol/Object.cpp create mode 100644 deps/v8_inspector/platform/inspector_protocol/Object.h create mode 100644 deps/v8_inspector/platform/inspector_protocol/Platform.h create mode 100644 deps/v8_inspector/platform/inspector_protocol/PlatformSTL.h create mode 100644 deps/v8_inspector/platform/inspector_protocol/PlatformWTF.h delete mode 100644 deps/v8_inspector/platform/inspector_protocol/protocol.gyp delete mode 100644 deps/v8_inspector/platform/v8_inspector/InspectorWrapper.cpp delete mode 100644 deps/v8_inspector/platform/v8_inspector/InspectorWrapper.h create mode 100644 deps/v8_inspector/platform/v8_inspector/js_protocol-1.1.json create mode 100644 deps/v8_inspector/platform/v8_inspector/js_protocol.json delete mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8DebuggerAgent.h delete mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8EventListenerInfo.h delete mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8HeapProfilerAgent.h delete mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8ProfilerAgent.h delete mode 100644 deps/v8_inspector/platform/v8_inspector/public/V8RuntimeAgent.h delete mode 100644 deps/v8_inspector/v8_inspector.gyp delete mode 100644 deps/v8_inspector/v8_inspector.gypi diff --git a/configure b/configure index f7a3f41ae02821..d622a6f0625a18 100755 --- a/configure +++ b/configure @@ -816,6 +816,7 @@ def configure_node(o): o['variables']['asan'] = int(options.enable_asan or 0) o['variables']['v8_inspector'] = b(not options.without_inspector) + o['variables']['debug_devtools'] = 'node' if options.use_xcode and options.use_ninja: raise Exception('--xcode and --ninja cannot be used together.') diff --git a/deps/v8_inspector/README.md b/deps/v8_inspector/README.md index 9892f5fc810b74..e3e5e14cab4a7a 100644 --- a/deps/v8_inspector/README.md +++ b/deps/v8_inspector/README.md @@ -1,2 +1,10 @@ -# v8_inspector -# v8_inspector +V8 Inspector support for Node.js +================================ + +This directory is a gathering of dependencies for Node.js support for the +[Chrome Debug Protocol][https://developer.chrome.com/devtools/docs/debugger-protocol]. + +* platform/v8_inspector: vendored from https://chromium.googlesource.com/chromium/src/third_party/WebKit/Source/platform/v8_inspector +* platform/inspector_protocol: vendored from https://chromium.googlesource.com/chromium/src/third_party/WebKit/Source/platform/inspector_protocol +* deps/jinja2: vendored from https://github.com/mitsuhiko/jinja2 +* deps/markupsafe: vendored from https://github.com/mitsuhiko/markupsafe diff --git a/deps/v8_inspector/devtools/Inspector-1.1.json b/deps/v8_inspector/devtools/Inspector-1.1.json deleted file mode 100644 index 55afa73e9df34b..00000000000000 --- a/deps/v8_inspector/devtools/Inspector-1.1.json +++ /dev/null @@ -1,3924 +0,0 @@ -{ - "version": { "major": "1", "minor": "1" }, - "domains": [{ - "domain": "Inspector", - "hidden": true, - "types": [], - "commands": [ - { - "name": "enable", - "description": "Enables inspector domain notifications." - }, - { - "name": "disable", - "description": "Disables inspector domain notifications." - }, - { - "name": "reset", - "description": "Resets all domains." - } - ], - "events": [ - { - "name": "evaluateForTestInFrontend", - "parameters": [ - { "name": "testCallId", "type": "integer" }, - { "name": "script", "type": "string" } - ] - }, - { - "name": "inspect", - "parameters": [ - { "name": "object", "$ref": "Runtime.RemoteObject" }, - { "name": "hints", "type": "object" } - ] - }, - { - "name": "detached", - "description": "Fired when remote debugging connection is about to be terminated. Contains detach reason.", - "parameters": [ - { "name": "reason", "type": "string", "description": "The reason why connection has been terminated." } - ] - }, - { - "name": "targetCrashed", - "description": "Fired when debugging target has crashed" - } - ] - }, - { - "domain": "Memory", - "hidden": true, - "types": [ - { - "id": "MemoryBlock", - "type": "object", - "properties": [ - { "name": "size", "type": "number", "optional": true, "description": "Size of the block in bytes if available" }, - { "name": "name", "type": "string", "description": "Unique name used to identify the component that allocated this block" }, - { "name": "children", "type": "array", "optional": true, "items": { "$ref": "MemoryBlock" }} - ] - }, - { - "id": "HeapSnapshotChunk", - "type": "object", - "properties": [ - { "name": "strings", "type": "array", "items": { "type": "string" }, "description": "An array of strings that were found since last update." }, - { "name": "nodes", "type": "array", "items": { "type": "integer" }, "description": "An array of nodes that were found since last update." }, - { "name": "edges", "type": "array", "items": { "type": "integer" }, "description": "An array of edges that were found since last update." }, - { "name": "baseToRealNodeId", "type": "array", "items": { "type": "integer" }, "description": "An array of integers for nodeId remapping. Even nodeId has to be mapped to the following odd nodeId." } - ] - } - ], - "commands": [ - { - "name": "getDOMCounters", - "returns": [ - { "name": "documents", "type": "integer" }, - { "name": "nodes", "type": "integer" }, - { "name": "jsEventListeners", "type": "integer" } - ] - } - ], - "events": [ - { - "name": "addNativeSnapshotChunk", - "parameters": [ - { "name": "chunk", "$ref": "HeapSnapshotChunk", "description": "A chunk of the serialized the snapshot." } - ] - } - ] - }, - { - "domain": "Page", - "description": "Actions and events related to the inspected page belong to the page domain.", - "types": [ - { - "id": "ResourceType", - "type": "string", - "enum": ["Document", "Stylesheet", "Image", "Font", "Script", "XHR", "WebSocket", "Other"], - "description": "Resource type as it was perceived by the rendering engine." - }, - { - "id": "FrameId", - "type": "string", - "description": "Unique frame identifier." - }, - { - "id": "Frame", - "type": "object", - "description": "Information about the Frame on the page.", - "properties": [ - { "name": "id", "type": "string", "description": "Frame unique identifier." }, - { "name": "parentId", "type": "string", "optional": true, "description": "Parent frame identifier." }, - { "name": "loaderId", "$ref": "Network.LoaderId", "description": "Identifier of the loader associated with this frame." }, - { "name": "name", "type": "string", "optional": true, "description": "Frame's name as specified in the tag." }, - { "name": "url", "type": "string", "description": "Frame document's URL." }, - { "name": "securityOrigin", "type": "string", "description": "Frame document's security origin." }, - { "name": "mimeType", "type": "string", "description": "Frame document's mimeType as determined by the browser." } - ] - }, - { - "id": "FrameResourceTree", - "type": "object", - "description": "Information about the Frame hierarchy along with their cached resources.", - "properties": [ - { "name": "frame", "$ref": "Frame", "description": "Frame information for this tree item." }, - { "name": "childFrames", "type": "array", "optional": true, "items": { "$ref": "FrameResourceTree" }, "description": "Child frames." }, - { "name": "resources", "type": "array", - "items": { - "type": "object", - "properties": [ - { "name": "url", "type": "string", "description": "Resource URL." }, - { "name": "type", "$ref": "ResourceType", "description": "Type of this resource." }, - { "name": "mimeType", "type": "string", "description": "Resource mimeType as determined by the browser." }, - { "name": "failed", "type": "boolean", "optional": true, "description": "True if the resource failed to load." }, - { "name": "canceled", "type": "boolean", "optional": true, "description": "True if the resource was canceled during loading." } - ] - }, - "description": "Information about frame resources." - } - ], - "hidden": true - }, - { - "id": "SearchMatch", - "type": "object", - "description": "Search match for resource.", - "properties": [ - { "name": "lineNumber", "type": "number", "description": "Line number in resource content." }, - { "name": "lineContent", "type": "string", "description": "Line with match content." } - ], - "hidden": true - }, - { - "id": "SearchResult", - "type": "object", - "description": "Search result for resource.", - "properties": [ - { "name": "url", "type": "string", "description": "Resource URL." }, - { "name": "frameId", "$ref": "FrameId", "description": "Resource frame id." }, - { "name": "matchesCount", "type": "number", "description": "Number of matches in the resource content." } - ], - "hidden": true - }, - { - "id": "Cookie", - "type": "object", - "description": "Cookie object", - "properties": [ - { "name": "name", "type": "string", "description": "Cookie name." }, - { "name": "value", "type": "string", "description": "Cookie value." }, - { "name": "domain", "type": "string", "description": "Cookie domain." }, - { "name": "path", "type": "string", "description": "Cookie path." }, - { "name": "expires", "type": "number", "description": "Cookie expires." }, - { "name": "size", "type": "integer", "description": "Cookie size." }, - { "name": "httpOnly", "type": "boolean", "description": "True if cookie is http-only." }, - { "name": "secure", "type": "boolean", "description": "True if cookie is secure." }, - { "name": "session", "type": "boolean", "description": "True in case of session cookie." } - ], - "hidden": true - }, - { - "id": "ScriptIdentifier", - "type": "string", - "description": "Unique script identifier.", - "hidden": true - }, - { - "id": "NavigationEntry", - "type": "object", - "description": "Navigation history entry.", - "properties": [ - { "name": "id", "type": "integer", "description": "Unique id of the navigation history entry." }, - { "name": "url", "type": "string", "description": "URL of the navigation history entry." }, - { "name": "title", "type": "string", "description": "Title of the navigation history entry." } - ], - "hidden": true - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables page domain notifications." - }, - { - "name": "disable", - "description": "Disables page domain notifications." - }, - { - "name": "addScriptToEvaluateOnLoad", - "parameters": [ - { "name": "scriptSource", "type": "string" } - ], - "returns": [ - { "name": "identifier", "$ref": "ScriptIdentifier", "description": "Identifier of the added script." } - ], - "hidden": true - }, - { - "name": "removeScriptToEvaluateOnLoad", - "parameters": [ - { "name": "identifier", "$ref": "ScriptIdentifier" } - ], - "hidden": true - }, - { - "name": "reload", - "parameters": [ - { "name": "ignoreCache", "type": "boolean", "optional": true, "description": "If true, browser cache is ignored (as if the user pressed Shift+refresh)." }, - { "name": "scriptToEvaluateOnLoad", "type": "string", "optional": true, "description": "If set, the script will be injected into all frames of the inspected page after reload." }, - { "name": "scriptPreprocessor", "type": "string", "optional": true, "description": "Script body that should evaluate to function that will preprocess all the scripts before their compilation.", "hidden": true } - ], - "description": "Reloads given page optionally ignoring the cache." - }, - { - "name": "navigate", - "parameters": [ - { "name": "url", "type": "string", "description": "URL to navigate the page to." } - ], - "description": "Navigates current page to the given URL." - }, - { - "name": "getNavigationHistory", - "parameters": [], - "returns": [ - { "name": "currentIndex", "type": "integer", "description": "Index of the current navigation history entry." }, - { "name": "entries", "type": "array", "items": { "$ref": "NavigationEntry"}, "description": "Array of navigation history entries." } - ], - "description": "Returns navigation history for the current page.", - "hidden": true - }, - { - "name": "navigateToHistoryEntry", - "parameters": [ - { "name": "entryId", "type": "integer", "description": "Unique id of the entry to navigate to." } - ], - "description": "Navigates current page to the given history entry.", - "hidden": true - }, - { - "name": "getCookies", - "returns": [ - { "name": "cookies", "type": "array", "items": { "$ref": "Cookie"}, "description": "Array of cookie objects." }, - { "name": "cookiesString", "type": "string", "description": "document.cookie string representation of the cookies." } - ], - "description": "Returns all browser cookies. Depending on the backend support, will either return detailed cookie information in the <code>cookie</code> field or string cookie representation using <code>cookieString</code>.", - "hidden": true - }, - { - "name": "deleteCookie", - "parameters": [ - { "name": "cookieName", "type": "string", "description": "Name of the cookie to remove." }, - { "name": "url", "type": "string", "description": "URL to match cooke domain and path." } - ], - "description": "Deletes browser cookie with given name, domain and path.", - "hidden": true - }, - { - "name": "getResourceTree", - "description": "Returns present frame / resource tree structure.", - "returns": [ - { "name": "frameTree", "$ref": "FrameResourceTree", "description": "Present frame / resource tree structure." } - ], - "hidden": true - }, - { - "name": "getResourceContent", - "description": "Returns content of the given resource.", - "parameters": [ - { "name": "frameId", "$ref": "FrameId", "description": "Frame id to get resource for." }, - { "name": "url", "type": "string", "description": "URL of the resource to get content for." } - ], - "returns": [ - { "name": "content", "type": "string", "description": "Resource content." }, - { "name": "base64Encoded", "type": "boolean", "description": "True, if content was served as base64." } - ], - "hidden": true - }, - { - "name": "searchInResource", - "description": "Searches for given string in resource content.", - "parameters": [ - { "name": "frameId", "$ref": "FrameId", "description": "Frame id for resource to search in." }, - { "name": "url", "type": "string", "description": "URL of the resource to search in." }, - { "name": "query", "type": "string", "description": "String to search for." }, - { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." }, - { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." } - ], - "returns": [ - { "name": "result", "type": "array", "items": { "$ref": "SearchMatch" }, "description": "List of search matches." } - ], - "hidden": true - }, - { - "name": "searchInResources", - "description": "Searches for given string in frame / resource tree structure.", - "parameters": [ - { "name": "text", "type": "string", "description": "String to search for." }, - { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." }, - { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." } - ], - "returns": [ - { "name": "result", "type": "array", "items": { "$ref": "SearchResult" }, "description": "List of search results." } - ], - "hidden": true - }, - { - "name": "setDocumentContent", - "description": "Sets given markup as the document's HTML.", - "parameters": [ - { "name": "frameId", "$ref": "FrameId", "description": "Frame id to set HTML for." }, - { "name": "html", "type": "string", "description": "HTML content to set." } - ], - "hidden": true - }, - { - "name": "setDeviceMetricsOverride", - "description": "Overrides the values of device screen dimensions (window.screen.width, window.screen.height, window.innerWidth, window.innerHeight, and \"device-width\"/\"device-height\"-related CSS media query results) and the font scale factor.", - "parameters": [ - { "name": "width", "type": "integer", "description": "Overriding width value in pixels (minimum 0, maximum 10000000). 0 disables the override." }, - { "name": "height", "type": "integer", "description": "Overriding height value in pixels (minimum 0, maximum 10000000). 0 disables the override." }, - { "name": "fontScaleFactor", "type": "number", "description": "Overriding font scale factor value (must be positive). 1 disables the override." }, - { "name": "fitWindow", "type": "boolean", "description": "Whether a view that exceeds the available browser window area should be scaled down to fit." } - ], - "hidden": true - }, - { - "name": "setShowPaintRects", - "description": "Requests that backend shows paint rectangles", - "parameters": [ - { "name": "result", "type": "boolean", "description": "True for showing paint rectangles" } - ], - "hidden": true - }, - { - "name": "setShowDebugBorders", - "description": "Requests that backend shows debug borders on layers", - "parameters": [ - { "name": "show", "type": "boolean", "description": "True for showing debug borders" } - ], - "hidden": true - }, - { - "name": "setShowFPSCounter", - "description": "Requests that backend shows the FPS counter", - "parameters": [ - { "name": "show", "type": "boolean", "description": "True for showing the FPS counter" } - ], - "hidden": true - }, - { - "name": "setContinuousPaintingEnabled", - "description": "Requests that backend enables continuous painting", - "parameters": [ - { "name": "enabled", "type": "boolean", "description": "True for enabling cointinuous painting" } - ], - "hidden": true - }, - { - "name": "setShowScrollBottleneckRects", - "description": "Requests that backend shows scroll bottleneck rects", - "parameters": [ - { "name": "show", "type": "boolean", "description": "True for showing scroll bottleneck rects" } - ], - "hidden": true - }, - { - "name": "getScriptExecutionStatus", - "description": "Determines if scripts can be executed in the page.", - "returns": [ - { "name": "result", "type": "string", "enum": ["allowed", "disabled", "forbidden"], "description": "Script execution status: \"allowed\" if scripts can be executed, \"disabled\" if script execution has been disabled through page settings, \"forbidden\" if script execution for the given page is not possible for other reasons." } - ], - "hidden": true - }, - { - "name": "setScriptExecutionDisabled", - "description": "Switches script execution in the page.", - "parameters": [ - { "name": "value", "type": "boolean", "description": "Whether script execution should be disabled in the page." } - ], - "hidden": true - }, - { - "name": "setGeolocationOverride", - "description": "Overrides the Geolocation Position or Error.", - "parameters": [ - { "name": "latitude", "type": "number", "optional": true, "description": "Mock longitude"}, - { "name": "longitude", "type": "number", "optional": true, "description": "Mock latitude"}, - { "name": "accuracy", "type": "number", "optional": true, "description": "Mock accuracy"} - ] - }, - { - "name": "clearGeolocationOverride", - "description": "Clears the overriden Geolocation Position and Error." - }, - { - "name": "setDeviceOrientationOverride", - "description": "Overrides the Device Orientation.", - "parameters": [ - { "name": "alpha", "type": "number", "description": "Mock alpha"}, - { "name": "beta", "type": "number", "description": "Mock beta"}, - { "name": "gamma", "type": "number", "description": "Mock gamma"} - ], - "hidden": true - }, - { - "name": "clearDeviceOrientationOverride", - "description": "Clears the overridden Device Orientation.", - "hidden": true - }, - { - "name": "setTouchEmulationEnabled", - "parameters": [ - { "name": "enabled", "type": "boolean", "description": "Whether the touch event emulation should be enabled." } - ], - "description": "Toggles mouse event-based touch event emulation.", - "hidden": true - }, - { - "name": "setEmulatedMedia", - "parameters": [ - { "name": "media", "type": "string", "description": "Media type to emulate. Empty string disables the override." } - ], - "description": "Emulates the given media for CSS media queries.", - "hidden": true - }, - { - "name": "captureScreenshot", - "description": "Capture page screenshot.", - "parameters": [ - { "name": "format", "type": "string", "optional": true, "enum": ["jpeg", "png"], "description": "Image compression format." }, - { "name": "quality", "type": "integer", "hidden": true, "optional": true, "description": "Compression quality from range [0..100]." }, - { "name": "maxWidth", "type": "integer", "hidden": true, "optional": true, "description": "Maximum screenshot width." }, - { "name": "maxHeight", "type": "integer", "hidden": true, "optional": true, "description": "Maximum screenshot height." } - ], - "returns": [ - { "name": "data", "type": "string", "description": "Base64-encoded image data (PNG)." }, - { "name": "deviceScaleFactor", "type": "number", "hidden": true, "description": "Device scale factor." }, - { "name": "pageScaleFactor", "type": "number", "hidden": true, "description": "Page scale factor." }, - { "name": "viewport", "$ref": "DOM.Rect", "hidden": true, "description": "Viewport in CSS pixels." } - ], - "hidden": true - }, - { - "name": "startScreencast", - "description": "Starts sending each frame using the <code>screencastFrame</code> event.", - "parameters": [ - { "name": "format", "type": "string", "optional": true, "enum": ["jpeg", "png"], "description": "Image compression format." }, - { "name": "quality", "type": "integer", "optional": true, "description": "Compression quality from range [0..100]." }, - { "name": "maxWidth", "type": "integer", "optional": true, "description": "Maximum screenshot width." }, - { "name": "maxHeight", "type": "integer", "optional": true, "description": "Maximum screenshot height." } - ], - "hidden": true - }, - { - "name": "stopScreencast", - "description": "Stops sending each frame in the <code>screencastFrame</code>.", - "hidden": true - }, - { - "name": "handleJavaScriptDialog", - "description": "Accepts or dismisses a JavaScript initiated dialog (alert, confirm, prompt, or onbeforeunload).", - "parameters": [ - { "name": "accept", "type": "boolean", "description": "Whether to accept or dismiss the dialog." }, - { "name": "promptText", "type": "string", "optional": true, "description": "The text to enter into the dialog prompt before accepting. Used only if this is a prompt dialog." } - ], - "hidden": true - }, - { - "name": "setShowViewportSizeOnResize", - "description": "Paints viewport size upon main frame resize.", - "parameters": [ - { "name": "show", "type": "boolean", "description": "Whether to paint size or not." }, - { "name": "showGrid", "type": "boolean", "optional": true, "description": "Whether to paint grid as well." } - ], - "hidden": true - }, - { - "name": "setForceCompositingMode", - "description": "Force accelerated compositing mode for inspected page.", - "parameters": [ - { "name": "force", "type": "boolean", "description": "Whether to force accelerated compositing or not." } - ], - "hidden": true - } - ], - "events": [ - { - "name": "domContentEventFired", - "parameters": [ - { "name": "timestamp", "type": "number" } - ] - }, - { - "name": "loadEventFired", - "parameters": [ - { "name": "timestamp", "type": "number" } - ] - }, - { - "name": "frameAttached", - "description": "Fired when frame has been attached to its parent.", - "parameters": [ - { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has been attached." } - ] - }, - { - "name": "frameNavigated", - "description": "Fired once navigation of the frame has completed. Frame is now associated with the new loader.", - "parameters": [ - { "name": "frame", "$ref": "Frame", "description": "Frame object." } - ] - }, - { - "name": "frameDetached", - "description": "Fired when frame has been detached from its parent.", - "parameters": [ - { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has been detached." } - ] - }, - { - "name": "frameStartedLoading", - "description": "Fired when frame has started loading.", - "parameters": [ - { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has started loading." } - ], - "hidden": true - }, - { - "name": "frameStoppedLoading", - "description": "Fired when frame has stopped loading.", - "parameters": [ - { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has stopped loading." } - ], - "hidden": true - }, - { - "name": "frameScheduledNavigation", - "description": "Fired when frame schedules a potential navigation.", - "parameters": [ - { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has scheduled a navigation." }, - { "name": "delay", "type": "number", "description": "Delay (in seconds) until the navigation is scheduled to begin. The navigation is not guaranteed to start." } - ], - "hidden": true - }, - { - "name": "frameClearedScheduledNavigation", - "description": "Fired when frame no longer has a scheduled navigation.", - "parameters": [ - { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has cleared its scheduled navigation." } - ], - "hidden": true - }, - { - "name": "javascriptDialogOpening", - "description": "Fired when a JavaScript initiated dialog (alert, confirm, prompt, or onbeforeunload) is about to open.", - "parameters": [ - { "name": "message", "type": "string", "description": "Message that will be displayed by the dialog." } - ], - "hidden": true - }, - { - "name": "javascriptDialogClosed", - "description": "Fired when a JavaScript initiated dialog (alert, confirm, prompt, or onbeforeunload) has been closed.", - "hidden": true - }, - { - "name": "scriptsEnabled", - "description": "Fired when the JavaScript is enabled/disabled on the page", - "parameters": [ - { "name": "isEnabled", "type": "boolean", "description": "Whether script execution is enabled or disabled on the page." } - ], - "hidden": true - }, - { - "name": "screencastFrame", - "description": "Compressed image data requested by the <code>startScreencast</code>.", - "parameters": [ - { "name": "data", "type": "string", "description": "Base64-encoded compressed image." }, - { "name": "deviceScaleFactor", "type": "number", "hidden": true, "optional": true, "description": "Device scale factor." }, - { "name": "pageScaleFactor", "type": "number", "hidden": true, "optional": true, "description": "Page scale factor." }, - { "name": "viewport", "$ref": "DOM.Rect", "hidden": true, "optional": true, "description": "Viewport in CSS pixels." }, - { "name": "offsetTop", "type": "number", "hidden": true, "optional": true, "description": "Top offset in DIP." }, - { "name": "offsetBottom", "type": "number", "hidden": true, "optional": true, "description": "Bottom offset in DIP." } - ], - "hidden": true - } - ] - }, - { - "domain": "Runtime", - "description": "Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. Evaluation results are returned as mirror object that expose object type, string representation and unique identifier that can be used for further object reference. Original objects are maintained in memory unless they are either explicitly released or are released along with the other objects in their object group.", - "types": [ - { - "id": "RemoteObjectId", - "type": "string", - "description": "Unique object identifier." - }, - { - "id": "RemoteObject", - "type": "object", - "description": "Mirror object referencing original JavaScript object.", - "properties": [ - { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean"], "description": "Object type." }, - { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date"], "description": "Object subtype hint. Specified for <code>object</code> type values only." }, - { "name": "className", "type": "string", "optional": true, "description": "Object class (constructor) name. Specified for <code>object</code> type values only." }, - { "name": "value", "type": "any", "optional": true, "description": "Remote object value (in case of primitive values or JSON values if it was requested)." }, - { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." }, - { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Unique object identifier (for non-primitive values)." }, - { "name": "preview", "$ref": "ObjectPreview", "optional": true, "description": "Preview containing abbreviated property values.", "hidden": true } - ] - }, - { - "id": "ObjectPreview", - "type": "object", - "hidden": true, - "description": "Object containing abbreviated remote object value.", - "properties": [ - { "name": "lossless", "type": "boolean", "description": "Determines whether preview is lossless (contains all information of the original object)." }, - { "name": "overflow", "type": "boolean", "description": "True iff some of the properties of the original did not fit." }, - { "name": "properties", "type": "array", "items": { "$ref": "PropertyPreview" }, "description": "List of the properties." } - ] - }, - { - "id": "PropertyPreview", - "type": "object", - "hidden": true, - "properties": [ - { "name": "name", "type": "string", "description": "Property name." }, - { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean"], "description": "Object type." }, - { "name": "value", "type": "string", "optional": true, "description": "User-friendly property value string." }, - { "name": "valuePreview", "$ref": "ObjectPreview", "optional": true, "description": "Nested value preview." }, - { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date"], "description": "Object subtype hint. Specified for <code>object</code> type values only." } - ] - }, - { - "id": "PropertyDescriptor", - "type": "object", - "description": "Object property descriptor.", - "properties": [ - { "name": "name", "type": "string", "description": "Property name." }, - { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." }, - { "name": "writable", "type": "boolean", "optional": true, "description": "True if the value associated with the property may be changed (data descriptors only)." }, - { "name": "get", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a getter for the property, or <code>undefined</code> if there is no getter (accessor descriptors only)." }, - { "name": "set", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a setter for the property, or <code>undefined</code> if there is no setter (accessor descriptors only)." }, - { "name": "configurable", "type": "boolean", "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." }, - { "name": "enumerable", "type": "boolean", "description": "True if this property shows up during enumeration of the properties on the corresponding object." }, - { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }, - { "name": "isOwn", "optional": true, "type": "boolean", "description": "True if the property is owned for the object.", "hidden": true } - - ] - }, - { - "id": "InternalPropertyDescriptor", - "type": "object", - "description": "Object internal property descriptor. This property isn't normally visible in JavaScript code.", - "properties": [ - { "name": "name", "type": "string", "description": "Conventional property name." }, - { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." } - ], - "hidden": true - }, - { - "id": "CallArgument", - "type": "object", - "description": "Represents function call argument. Either remote object id <code>objectId</code> or primitive <code>value</code> or neither of (for undefined) them should be specified.", - "properties": [ - { "name": "value", "type": "any", "optional": true, "description": "Primitive value." }, - { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Remote object handle." } - ] - }, - { - "id": "ExecutionContextId", - "type": "integer", - "description": "Id of an execution context." - }, - { - "id": "ExecutionContextDescription", - "type": "object", - "description": "Description of an isolated world.", - "properties": [ - { "name": "id", "$ref": "ExecutionContextId", "description": "Unique id of the execution context. It can be used to specify in which execution context script evaluation should be performed." }, - { "name": "isPageContext", "type": "boolean", "description": "True if this is a context where inpspected web page scripts run. False if it is a content script isolated context.", "hidden": true }, - { "name": "name", "type": "string", "description": "Human readable name describing given context.", "hidden": true}, - { "name": "frameId", "$ref": "Page.FrameId", "description": "Id of the owning frame." } - ] - } - - ], - "commands": [ - { - "name": "evaluate", - "parameters": [ - { "name": "expression", "type": "string", "description": "Expression to evaluate." }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, - { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation.", "hidden": true }, - { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether evaluation should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true }, - { "name": "contextId", "$ref": "Runtime.ExecutionContextId", "optional": true, "description": "Specifies in which isolated context to perform evaluation. Each content script lives in an isolated context and this parameter may be used to specify one of those contexts. If the parameter is omitted or 0 the evaluation will be performed in the context of the inspected page." }, - { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, - { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." } - ], - "returns": [ - { "name": "result", "$ref": "RemoteObject", "description": "Evaluation result." }, - { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." } - ], - "description": "Evaluates expression on global object." - }, - { - "name": "callFunctionOn", - "parameters": [ - { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to call function on." }, - { "name": "functionDeclaration", "type": "string", "description": "Declaration of the function to call." }, - { "name": "arguments", "type": "array", "items": { "$ref": "CallArgument", "description": "Call argument." }, "optional": true, "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target object." }, - { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether function call should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true }, - { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." }, - { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." } - ], - "returns": [ - { "name": "result", "$ref": "RemoteObject", "description": "Call result." }, - { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." } - ], - "description": "Calls function with given declaration on the given object. Object group of the result is inherited from the target object." - }, - { - "name": "getProperties", - "parameters": [ - { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." }, - { "name": "ownProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging only to the element itself, not to its prototype chain." }, - { "name": "accessorPropertiesOnly", "optional": true, "type": "boolean", "description": "If true, returns accessor properties (with getter/setter) only; internal properties are not returned either.", "hidden": true } - ], - "returns": [ - { "name": "result", "type": "array", "items": { "$ref": "PropertyDescriptor"}, "description": "Object properties." }, - { "name": "internalProperties", "optional": true, "type": "array", "items": { "$ref": "InternalPropertyDescriptor"}, "description": "Internal object properties (only of the element itself).", "hidden": true } - ], - "description": "Returns properties of a given object. Object group of the result is inherited from the target object." - }, - { - "name": "releaseObject", - "parameters": [ - { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to release." } - ], - "description": "Releases remote object with given id." - }, - { - "name": "releaseObjectGroup", - "parameters": [ - { "name": "objectGroup", "type": "string", "description": "Symbolic object group name." } - ], - "description": "Releases all remote objects that belong to a given group." - }, - { - "name": "run", - "hidden": true, - "description": "Tells inspected instance(worker or page) that it can run in case it was started paused." - }, - { - "name": "enable", - "description": "Enables reporting of execution contexts creation by means of <code>executionContextCreated</code> event. When the reporting gets enabled the event will be sent immediately for each existing execution context." - }, - { - "name": "disable", - "hidden": true, - "description": "Disables reporting of execution contexts creation." - } - ], - "events": [ - { - "name": "executionContextCreated", - "parameters": [ - { "name": "context", "$ref": "ExecutionContextDescription", "description": "A newly created execution contex." } - ], - "description": "Issued when new execution context is created." - } - ] - }, - { - "domain": "Console", - "description": "Console domain defines methods and events for interaction with the JavaScript console. Console collects messages created by means of the <a href='http://getfirebug.com/wiki/index.php/Console_API'>JavaScript Console API</a>. One needs to enable this domain using <code>enable</code> command in order to start receiving the console messages. Browser collects messages issued while console domain is not enabled as well and reports them using <code>messageAdded</code> notification upon enabling.", - "types": [ - { - "id": "Timestamp", - "type": "number", - "description": "Number of seconds since epoch.", - "hidden": true - }, - { - "id": "ConsoleMessage", - "type": "object", - "description": "Console message.", - "properties": [ - { "name": "source", "type": "string", "enum": ["xml", "javascript", "network", "console-api", "storage", "appcache", "rendering", "css", "security", "other", "deprecation"], "description": "Message source." }, - { "name": "level", "type": "string", "enum": ["log", "warning", "error", "debug"], "description": "Message severity." }, - { "name": "text", "type": "string", "description": "Message text." }, - { "name": "type", "type": "string", "optional": true, "enum": ["log", "dir", "dirxml", "table", "trace", "clear", "startGroup", "startGroupCollapsed", "endGroup", "assert", "timing", "profile", "profileEnd"], "description": "Console message type." }, - { "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." }, - { "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message." }, - { "name": "column", "type": "integer", "optional": true, "description": "Column number in the resource that generated this message." }, - { "name": "repeatCount", "type": "integer", "optional": true, "description": "Repeat count for repeated messages." }, - { "name": "parameters", "type": "array", "items": { "$ref": "Runtime.RemoteObject" }, "optional": true, "description": "Message parameters in case of the formatted message." }, - { "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "JavaScript stack trace for assertions and error messages." }, - { "name": "networkRequestId", "$ref": "Network.RequestId", "optional": true, "description": "Identifier of the network request associated with this message." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp, when this message was fired.", "hidden": true } - ] - }, - { - "id": "CallFrame", - "type": "object", - "description": "Stack entry for console errors and assertions.", - "properties": [ - { "name": "functionName", "type": "string", "description": "JavaScript function name." }, - { "name": "scriptId", "type": "string", "description": "JavaScript script id." }, - { "name": "url", "type": "string", "description": "JavaScript script name or url." }, - { "name": "lineNumber", "type": "integer", "description": "JavaScript script line number." }, - { "name": "columnNumber", "type": "integer", "description": "JavaScript script column number." } - ] - }, - { - "id": "StackTrace", - "type": "array", - "items": { "$ref": "CallFrame" }, - "description": "Call frames for assertions or error messages." - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables console domain, sends the messages collected so far to the client by means of the <code>messageAdded</code> notification." - }, - { - "name": "disable", - "description": "Disables console domain, prevents further console messages from being reported to the client." - }, - { - "name": "clearMessages", - "description": "Clears console messages collected in the browser." - }, - { - "name": "setMonitoringXHREnabled", - "parameters": [ - { "name": "enabled", "type": "boolean", "description": "Monitoring enabled state." } - ], - "description": "Toggles monitoring of XMLHttpRequest. If <code>true</code>, console will receive messages upon each XHR issued.", - "hidden": true - }, - { - "name": "addInspectedNode", - "parameters": [ - { "name": "nodeId", "$ref": "DOM.NodeId", "description": "DOM node id to be accessible by means of $x command line API." } - ], - "description": "Enables console to refer to the node with given id via $x (see Command Line API for more details $x functions).", - "hidden": true - }, - { - "name": "addInspectedHeapObject", - "parameters": [ - { "name": "heapObjectId", "type": "integer" } - ], - "hidden": true - } - ], - "events": [ - { - "name": "messageAdded", - "parameters": [ - { "name": "message", "$ref": "ConsoleMessage", "description": "Console message that has been added." } - ], - "description": "Issued when new console message is added." - }, - { - "name": "messageRepeatCountUpdated", - "parameters": [ - { "name": "count", "type": "integer", "description": "New repeat count value." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp of most recent message in batch.", "hidden": true } - ], - "description": "Issued when subsequent message(s) are equal to the previous one(s)." - }, - { - "name": "messagesCleared", - "description": "Issued when console is cleared. This happens either upon <code>clearMessages</code> command or after page navigation." - } - ] - }, - { - "domain": "Network", - "description": "Network domain allows tracking network activities of the page. It exposes information about http, file, data and other requests and responses, their headers, bodies, timing, etc.", - "types": [ - { - "id": "LoaderId", - "type": "string", - "description": "Unique loader identifier." - }, - { - "id": "RequestId", - "type": "string", - "description": "Unique request identifier." - }, - { - "id": "Timestamp", - "type": "number", - "description": "Number of seconds since epoch." - }, - { - "id": "Headers", - "type": "object", - "description": "Request / response headers as keys / values of JSON object." - }, - { - "id": "ResourceTiming", - "type": "object", - "description": "Timing information for the request.", - "properties": [ - { "name": "requestTime", "type": "number", "description": "Timing's requestTime is a baseline in seconds, while the other numbers are ticks in milliseconds relatively to this requestTime." }, - { "name": "proxyStart", "type": "number", "description": "Started resolving proxy." }, - { "name": "proxyEnd", "type": "number", "description": "Finished resolving proxy." }, - { "name": "dnsStart", "type": "number", "description": "Started DNS address resolve." }, - { "name": "dnsEnd", "type": "number", "description": "Finished DNS address resolve." }, - { "name": "connectStart", "type": "number", "description": "Started connecting to the remote host." }, - { "name": "connectEnd", "type": "number", "description": "Connected to the remote host." }, - { "name": "sslStart", "type": "number", "description": "Started SSL handshake." }, - { "name": "sslEnd", "type": "number", "description": "Finished SSL handshake." }, - { "name": "sendStart", "type": "number", "description": "Started sending request." }, - { "name": "sendEnd", "type": "number", "description": "Finished sending request." }, - { "name": "receiveHeadersEnd", "type": "number", "description": "Finished receiving response headers." } - ] - }, - { - "id": "Request", - "type": "object", - "description": "HTTP request data.", - "properties": [ - { "name": "url", "type": "string", "description": "Request URL." }, - { "name": "method", "type": "string", "description": "HTTP request method." }, - { "name": "headers", "$ref": "Headers", "description": "HTTP request headers." }, - { "name": "postData", "type": "string", "optional": true, "description": "HTTP POST request data." } - ] - }, - { - "id": "Response", - "type": "object", - "description": "HTTP response data.", - "properties": [ - { "name": "url", "type": "string", "description": "Response URL. This URL can be different from CachedResource.url in case of redirect." }, - { "name": "status", "type": "number", "description": "HTTP response status code." }, - { "name": "statusText", "type": "string", "description": "HTTP response status text." }, - { "name": "headers", "$ref": "Headers", "description": "HTTP response headers." }, - { "name": "headersText", "type": "string", "optional": true, "description": "HTTP response headers text." }, - { "name": "mimeType", "type": "string", "description": "Resource mimeType as determined by the browser." }, - { "name": "requestHeaders", "$ref": "Headers", "optional": true, "description": "Refined HTTP request headers that were actually transmitted over the network." }, - { "name": "requestHeadersText", "type": "string", "optional": true, "description": "HTTP request headers text." }, - { "name": "connectionReused", "type": "boolean", "description": "Specifies whether physical connection was actually reused for this request." }, - { "name": "connectionId", "type": "number", "description": "Physical connection id that was actually used for this request." }, - { "name": "fromDiskCache", "type": "boolean", "optional": true, "description": "Specifies that the request was served from the disk cache." }, - { "name": "timing", "$ref": "ResourceTiming", "optional": true, "description": "Timing information for the given request." } - ] - }, - { - "id": "WebSocketRequest", - "type": "object", - "description": "WebSocket request data.", - "hidden": true, - "properties": [ - { "name": "headers", "$ref": "Headers", "description": "HTTP response headers." } - ] - }, - { - "id": "WebSocketResponse", - "type": "object", - "description": "WebSocket response data.", - "hidden": true, - "properties": [ - { "name": "status", "type": "number", "description": "HTTP response status code." }, - { "name": "statusText", "type": "string", "description": "HTTP response status text." }, - { "name": "headers", "$ref": "Headers", "description": "HTTP response headers." } - ] - }, - { - "id": "WebSocketFrame", - "type": "object", - "description": "WebSocket frame data.", - "hidden": true, - "properties": [ - { "name": "opcode", "type": "number", "description": "WebSocket frame opcode." }, - { "name": "mask", "type": "boolean", "description": "WebSocke frame mask." }, - { "name": "payloadData", "type": "string", "description": "WebSocke frame payload data." } - ] - }, - { - "id": "CachedResource", - "type": "object", - "description": "Information about the cached resource.", - "properties": [ - { "name": "url", "type": "string", "description": "Resource URL. This is the url of the original network request." }, - { "name": "type", "$ref": "Page.ResourceType", "description": "Type of this resource." }, - { "name": "response", "$ref": "Response", "optional": true, "description": "Cached response data." }, - { "name": "bodySize", "type": "number", "description": "Cached response body size." } - ] - }, - { - "id": "Initiator", - "type": "object", - "description": "Information about the request initiator.", - "properties": [ - { "name": "type", "type": "string", "enum": ["parser", "script", "other"], "description": "Type of this initiator." }, - { "name": "stackTrace", "$ref": "Console.StackTrace", "optional": true, "description": "Initiator JavaScript stack trace, set for Script only." }, - { "name": "url", "type": "string", "optional": true, "description": "Initiator URL, set for Parser type only." }, - { "name": "lineNumber", "type": "number", "optional": true, "description": "Initiator line number, set for Parser type only." } - ] - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables network tracking, network events will now be delivered to the client." - }, - { - "name": "disable", - "description": "Disables network tracking, prevents network events from being sent to the client." - }, - { - "name": "setUserAgentOverride", - "description": "Allows overriding user agent with the given string.", - "parameters": [ - { "name": "userAgent", "type": "string", "description": "User agent to use." } - ] - }, - { - "name": "setExtraHTTPHeaders", - "description": "Specifies whether to always send extra HTTP headers with the requests from this page.", - "parameters": [ - { "name": "headers", "$ref": "Headers", "description": "Map with extra HTTP headers." } - ] - }, - { - "name": "getResponseBody", - "description": "Returns content served for the given request.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Identifier of the network request to get content for." } - ], - "returns": [ - { "name": "body", "type": "string", "description": "Response body." }, - { "name": "base64Encoded", "type": "boolean", "description": "True, if content was sent as base64." } - ] - }, - { - "name": "replayXHR", - "description": "This method sends a new XMLHttpRequest which is identical to the original one. The following parameters should be identical: method, url, async, request body, extra headers, withCredentials attribute, user, password.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Identifier of XHR to replay." } - ], - "hidden": true - }, - { - "name": "canClearBrowserCache", - "description": "Tells whether clearing browser cache is supported.", - "returns": [ - { "name": "result", "type": "boolean", "description": "True if browser cache can be cleared." } - ] - }, - { - "name": "clearBrowserCache", - "description": "Clears browser cache." - }, - { - "name": "canClearBrowserCookies", - "description": "Tells whether clearing browser cookies is supported.", - "returns": [ - { "name": "result", "type": "boolean", "description": "True if browser cookies can be cleared." } - ] - }, - { - "name": "clearBrowserCookies", - "description": "Clears browser cookies." - }, - { - "name": "setCacheDisabled", - "parameters": [ - { "name": "cacheDisabled", "type": "boolean", "description": "Cache disabled state." } - ], - "description": "Toggles ignoring cache for each request. If <code>true</code>, cache will not be used." - }, - { - "name": "loadResourceForFrontend", - "async": true, - "parameters": [ - { "name": "frameId", "$ref": "Page.FrameId", "description": "Frame to load the resource from." }, - { "name": "url", "type": "string", "description": "URL of the resource to load." }, - { "name": "requestHeaders", "$ref": "Network.Headers", "optional": true, "description": "Request headers." } - ], - "returns": [ - { "name": "statusCode", "type": "number", "description": "HTTP status code." }, - { "name": "responseHeaders", "$ref": "Network.Headers", "description": "Response headers." }, - { "name": "content", "type": "string", "description": "Resource content." } - ], - "description": "Loads a resource in the context of a frame on the inspected page without cross origin checks.", - "hidden": true - } - ], - "events": [ - { - "name": "requestWillBeSent", - "description": "Fired when page is about to send HTTP request.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "frameId", "$ref": "Page.FrameId", "description": "Frame identifier.", "hidden": true }, - { "name": "loaderId", "$ref": "LoaderId", "description": "Loader identifier." }, - { "name": "documentURL", "type": "string", "description": "URL of the document this request is loaded for." }, - { "name": "request", "$ref": "Request", "description": "Request data." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "initiator", "$ref": "Initiator", "description": "Request initiator." }, - { "name": "redirectResponse", "optional": true, "$ref": "Response", "description": "Redirect response data." } - ] - }, - { - "name": "requestServedFromCache", - "description": "Fired if request ended up loading from cache.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." } - ] - }, - { - "name": "responseReceived", - "description": "Fired when HTTP response is available.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "frameId", "$ref": "Page.FrameId", "description": "Frame identifier.", "hidden": true }, - { "name": "loaderId", "$ref": "LoaderId", "description": "Loader identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "type", "$ref": "Page.ResourceType", "description": "Resource type." }, - { "name": "response", "$ref": "Response", "description": "Response data." } - ] - }, - { - "name": "dataReceived", - "description": "Fired when data chunk was received over the network.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "dataLength", "type": "integer", "description": "Data chunk length." }, - { "name": "encodedDataLength", "type": "integer", "description": "Actual bytes received (might be less than dataLength for compressed encodings)." } - ] - }, - { - "name": "loadingFinished", - "description": "Fired when HTTP request has finished loading.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." } - ] - }, - { - "name": "loadingFailed", - "description": "Fired when HTTP request has failed to load.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "errorText", "type": "string", "description": "User friendly error message." }, - { "name": "canceled", "type": "boolean", "optional": true, "description": "True if loading was canceled." } - ] - }, - { - "name": "webSocketWillSendHandshakeRequest", - "description": "Fired when WebSocket is about to initiate handshake.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "request", "$ref": "WebSocketRequest", "description": "WebSocket request data." } - ], - "hidden": true - }, - { - "name": "webSocketHandshakeResponseReceived", - "description": "Fired when WebSocket handshake response becomes available.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "response", "$ref": "WebSocketResponse", "description": "WebSocket response data." } - ], - "hidden": true - }, - { - "name": "webSocketCreated", - "description": "Fired upon WebSocket creation.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "url", "type": "string", "description": "WebSocket request URL." } - ], - "hidden": true - }, - { - "name": "webSocketClosed", - "description": "Fired when WebSocket is closed.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." } - ], - "hidden": true - }, - { - "name": "webSocketFrameReceived", - "description": "Fired when WebSocket frame is received.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "response", "$ref": "WebSocketFrame", "description": "WebSocket response data." } - ], - "hidden": true - }, - { - "name": "webSocketFrameError", - "description": "Fired when WebSocket frame error occurs.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "errorMessage", "type": "string", "description": "WebSocket frame error message." } - ], - "hidden": true - }, - { - "name": "webSocketFrameSent", - "description": "Fired when WebSocket frame is sent.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "response", "$ref": "WebSocketFrame", "description": "WebSocket response data." } - ], - "hidden": true - } - ] - }, - { - "domain": "Database", - "hidden": true, - "types": [ - { - "id": "DatabaseId", - "type": "string", - "description": "Unique identifier of Database object.", - "hidden": true - }, - { - "id": "Database", - "type": "object", - "description": "Database object.", - "hidden": true, - "properties": [ - { "name": "id", "$ref": "DatabaseId", "description": "Database ID." }, - { "name": "domain", "type": "string", "description": "Database domain." }, - { "name": "name", "type": "string", "description": "Database name." }, - { "name": "version", "type": "string", "description": "Database version." } - ] - }, - { - "id": "Error", - "type": "object", - "description": "Database error.", - "properties": [ - { "name": "message", "type": "string", "description": "Error message." }, - { "name": "code", "type": "integer", "description": "Error code." } - ] - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables database tracking, database events will now be delivered to the client." - }, - { - "name": "disable", - "description": "Disables database tracking, prevents database events from being sent to the client." - }, - { - "name": "getDatabaseTableNames", - "parameters": [ - { "name": "databaseId", "$ref": "DatabaseId" } - ], - "returns": [ - { "name": "tableNames", "type": "array", "items": { "type": "string" } } - ] - }, - { - "name": "executeSQL", - "async": true, - "parameters": [ - { "name": "databaseId", "$ref": "DatabaseId" }, - { "name": "query", "type": "string" } - ], - "returns": [ - { "name": "columnNames", "type": "array", "optional": true, "items": { "type": "string" } }, - { "name": "values", "type": "array", "optional": true, "items": { "type": "any" }}, - { "name": "sqlError", "$ref": "Error", "optional": true } - ] - } - ], - "events": [ - { - "name": "addDatabase", - "parameters": [ - { "name": "database", "$ref": "Database" } - ] - } - ] - }, - { - "domain": "IndexedDB", - "hidden": true, - "types": [ - { - "id": "DatabaseWithObjectStores", - "type": "object", - "description": "Database with an array of object stores.", - "properties": [ - { "name": "name", "type": "string", "description": "Database name." }, - { "name": "version", "type": "integer", "description": "Database version." }, - { "name": "objectStores", "type": "array", "items": { "$ref": "ObjectStore" }, "description": "Object stores in this database." } - ] - }, - { - "id": "ObjectStore", - "type": "object", - "description": "Object store.", - "properties": [ - { "name": "name", "type": "string", "description": "Object store name." }, - { "name": "keyPath", "$ref": "KeyPath", "description": "Object store key path." }, - { "name": "autoIncrement", "type": "boolean", "description": "If true, object store has auto increment flag set." }, - { "name": "indexes", "type": "array", "items": { "$ref": "ObjectStoreIndex" }, "description": "Indexes in this object store." } - ] - }, - { - "id": "ObjectStoreIndex", - "type": "object", - "description": "Object store index.", - "properties": [ - { "name": "name", "type": "string", "description": "Index name." }, - { "name": "keyPath", "$ref": "KeyPath", "description": "Index key path." }, - { "name": "unique", "type": "boolean", "description": "If true, index is unique." }, - { "name": "multiEntry", "type": "boolean", "description": "If true, index allows multiple entries for a key." } - ] - }, - { - "id": "Key", - "type": "object", - "description": "Key.", - "properties": [ - { "name": "type", "type": "string", "enum": ["number", "string", "date", "array"], "description": "Key type." }, - { "name": "number", "type": "number", "optional": true, "description": "Number value." }, - { "name": "string", "type": "string", "optional": true, "description": "String value." }, - { "name": "date", "type": "number", "optional": true, "description": "Date value." }, - { "name": "array", "type": "array", "optional": true, "items": { "$ref": "Key" }, "description": "Array value." } - ] - }, - { - "id": "KeyRange", - "type": "object", - "description": "Key range.", - "properties": [ - { "name": "lower", "$ref": "Key", "optional": true, "description": "Lower bound." }, - { "name": "upper", "$ref": "Key", "optional": true, "description": "Upper bound." }, - { "name": "lowerOpen", "type": "boolean", "description": "If true lower bound is open." }, - { "name": "upperOpen", "type": "boolean", "description": "If true upper bound is open." } - ] - }, - { - "id": "DataEntry", - "type": "object", - "description": "Data entry.", - "properties": [ - { "name": "key", "$ref": "Runtime.RemoteObject", "description": "Key." }, - { "name": "primaryKey", "$ref": "Runtime.RemoteObject", "description": "Primary key." }, - { "name": "value", "$ref": "Runtime.RemoteObject", "description": "Value." } - ] - }, - { - "id": "KeyPath", - "type": "object", - "description": "Key path.", - "properties": [ - { "name": "type", "type": "string", "enum": ["null", "string", "array"], "description": "Key path type." }, - { "name": "string", "type": "string", "optional": true, "description": "String value." }, - { "name": "array", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Array value." } - ] - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables events from backend." - }, - { - "name": "disable", - "description": "Disables events from backend." - }, - { - "name": "requestDatabaseNames", - "async": true, - "parameters": [ - { "name": "securityOrigin", "type": "string", "description": "Security origin." } - ], - "returns": [ - { "name": "databaseNames", "type": "array", "items": { "type": "string" }, "description": "Database names for origin." } - ], - "description": "Requests database names for given security origin." - }, - { - "name": "requestDatabase", - "async": true, - "parameters": [ - { "name": "securityOrigin", "type": "string", "description": "Security origin." }, - { "name": "databaseName", "type": "string", "description": "Database name." } - ], - "returns": [ - { "name": "databaseWithObjectStores", "$ref": "DatabaseWithObjectStores", "description": "Database with an array of object stores." } - ], - "description": "Requests database with given name in given frame." - }, - { - "name": "requestData", - "async": true, - "parameters": [ - { "name": "securityOrigin", "type": "string", "description": "Security origin." }, - { "name": "databaseName", "type": "string", "description": "Database name." }, - { "name": "objectStoreName", "type": "string", "description": "Object store name." }, - { "name": "indexName", "type": "string", "description": "Index name, empty string for object store data requests." }, - { "name": "skipCount", "type": "integer", "description": "Number of records to skip." }, - { "name": "pageSize", "type": "integer", "description": "Number of records to fetch." }, - { "name": "keyRange", "$ref": "KeyRange", "optional": true, "description": "Key range." } - ], - "returns": [ - { "name": "objectStoreDataEntries", "type": "array", "items": { "$ref": "DataEntry" }, "description": "Array of object store data entries." }, - { "name": "hasMore", "type": "boolean", "description": "If true, there are more entries to fetch in the given range." } - ], - "description": "Requests data from object store or index." - }, - { - "name": "clearObjectStore", - "async": true, - "parameters": [ - { "name": "securityOrigin", "type": "string", "description": "Security origin." }, - { "name": "databaseName", "type": "string", "description": "Database name." }, - { "name": "objectStoreName", "type": "string", "description": "Object store name." } - ], - "returns": [ - ], - "description": "Clears all entries from an object store." - } - ] - }, - { - "domain": "DOMStorage", - "hidden": true, - "description": "Query and modify DOM storage.", - "types": [ - { - "id": "StorageId", - "type": "object", - "description": "DOM Storage identifier.", - "hidden": true, - "properties": [ - { "name": "securityOrigin", "type": "string", "description": "Security origin for the storage." }, - { "name": "isLocalStorage", "type": "boolean", "description": "Whether the storage is local storage (not session storage)." } - ] - }, - { - "id": "Item", - "type": "array", - "description": "DOM Storage item.", - "hidden": true, - "items": { "type": "string" } - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables storage tracking, storage events will now be delivered to the client." - }, - { - "name": "disable", - "description": "Disables storage tracking, prevents storage events from being sent to the client." - }, - { - "name": "getDOMStorageItems", - "parameters": [ - { "name": "storageId", "$ref": "StorageId" } - ], - "returns": [ - { "name": "entries", "type": "array", "items": { "$ref": "Item" } } - ] - }, - { - "name": "setDOMStorageItem", - "parameters": [ - { "name": "storageId", "$ref": "StorageId" }, - { "name": "key", "type": "string" }, - { "name": "value", "type": "string" } - ] - }, - { - "name": "removeDOMStorageItem", - "parameters": [ - { "name": "storageId", "$ref": "StorageId" }, - { "name": "key", "type": "string" } - ] - } - ], - "events": [ - { - "name": "domStorageItemsCleared", - "parameters": [ - { "name": "storageId", "$ref": "StorageId" } - ] - }, - { - "name": "domStorageItemRemoved", - "parameters": [ - { "name": "storageId", "$ref": "StorageId" }, - { "name": "key", "type": "string" } - ] - }, - { - "name": "domStorageItemAdded", - "parameters": [ - { "name": "storageId", "$ref": "StorageId" }, - { "name": "key", "type": "string" }, - { "name": "newValue", "type": "string" } - ] - }, - { - "name": "domStorageItemUpdated", - "parameters": [ - { "name": "storageId", "$ref": "StorageId" }, - { "name": "key", "type": "string" }, - { "name": "oldValue", "type": "string" }, - { "name": "newValue", "type": "string" } - ] - } - ] - }, - { - "domain": "ApplicationCache", - "hidden": true, - "types": [ - { - "id": "ApplicationCacheResource", - "type": "object", - "description": "Detailed application cache resource information.", - "properties": [ - { "name": "url", "type": "string", "description": "Resource url." }, - { "name": "size", "type": "integer", "description": "Resource size." }, - { "name": "type", "type": "string", "description": "Resource type." } - ] - }, - { - "id": "ApplicationCache", - "type": "object", - "description": "Detailed application cache information.", - "properties": [ - { "name": "manifestURL", "type": "string", "description": "Manifest URL." }, - { "name": "size", "type": "number", "description": "Application cache size." }, - { "name": "creationTime", "type": "number", "description": "Application cache creation time." }, - { "name": "updateTime", "type": "number", "description": "Application cache update time." }, - { "name": "resources", "type": "array", "items": { "$ref": "ApplicationCacheResource" }, "description": "Application cache resources." } - ] - }, - { - "id": "FrameWithManifest", - "type": "object", - "description": "Frame identifier - manifest URL pair.", - "properties": [ - { "name": "frameId", "$ref": "Page.FrameId", "description": "Frame identifier." }, - { "name": "manifestURL", "type": "string", "description": "Manifest URL." }, - { "name": "status", "type": "integer", "description": "Application cache status." } - ] - } - ], - "commands": [ - { - "name": "getFramesWithManifests", - "returns": [ - { "name": "frameIds", "type": "array", "items": { "$ref": "FrameWithManifest" }, "description": "Array of frame identifiers with manifest urls for each frame containing a document associated with some application cache." } - ], - "description": "Returns array of frame identifiers with manifest urls for each frame containing a document associated with some application cache." - }, - { - "name": "enable", - "description": "Enables application cache domain notifications." - }, - { - "name": "getManifestForFrame", - "parameters": [ - { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame containing document whose manifest is retrieved." } - ], - "returns": [ - { "name": "manifestURL", "type": "string", "description": "Manifest URL for document in the given frame." } - ], - "description": "Returns manifest URL for document in the given frame." - }, - { - "name": "getApplicationCacheForFrame", - "parameters": [ - { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame containing document whose application cache is retrieved." } - ], - "returns": [ - { "name": "applicationCache", "$ref": "ApplicationCache", "description": "Relevant application cache data for the document in given frame." } - ], - "description": "Returns relevant application cache data for the document in given frame." - } - ], - "events": [ - { - "name": "applicationCacheStatusUpdated", - "parameters": [ - { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame containing document whose application cache updated status." }, - { "name": "manifestURL", "type": "string", "description": "Manifest URL." }, - { "name": "status", "type": "integer", "description": "Updated application cache status." } - ] - }, - { - "name": "networkStateUpdated", - "parameters": [ - { "name": "isNowOnline", "type": "boolean" } - ] - } - ] - }, - { - "domain": "FileSystem", - "hidden": true, - "types": [ - { - "id": "Entry", - "type": "object", - "properties": [ - { "name": "url", "type": "string", "description": "filesystem: URL for the entry." }, - { "name": "name", "type": "string", "description": "The name of the file or directory." }, - { "name": "isDirectory", "type": "boolean", "description": "True if the entry is a directory." }, - { "name": "mimeType", "type": "string", "optional": true, "description": "MIME type of the entry, available for a file only." }, - { "name": "resourceType", "$ref": "Page.ResourceType", "optional": true, "description": "ResourceType of the entry, available for a file only." }, - { "name": "isTextFile", "type": "boolean", "optional": true, "description": "True if the entry is a text file." } - ], - "description": "Represents a browser side file or directory." - }, - { - "id": "Metadata", - "type": "object", - "properties": [ - { "name": "modificationTime", "type": "number", "description": "Modification time." }, - { "name": "size", "type": "number", "description": "File size. This field is always zero for directories." } - ], - "description": "Represents metadata of a file or entry." - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables events from backend." - }, - { - "name": "disable", - "description": "Disables events from backend." - }, - { - "name": "requestFileSystemRoot", - "async": true, - "parameters": [ - { "name": "origin", "type": "string", "description": "Security origin of requesting FileSystem. One of frames in current page needs to have this security origin." }, - { "name": "type", "type": "string", "enum": ["temporary", "persistent"], "description": "FileSystem type of requesting FileSystem." } - ], - "returns": [ - { "name": "errorCode", "type": "integer", "description": "0, if no error. Otherwise, errorCode is set to FileError::ErrorCode value." }, - { "name": "root", "$ref": "Entry", "optional": true, "description": "Contains root of the requested FileSystem if the command completed successfully." } - ], - "description": "Returns root directory of the FileSystem, if exists." - }, - { - "name": "requestDirectoryContent", - "async": true, - "parameters": [ - { "name": "url", "type": "string", "description": "URL of the directory that the frontend is requesting to read from." } - ], - "returns": [ - { "name": "errorCode", "type": "integer", "description": "0, if no error. Otherwise, errorCode is set to FileError::ErrorCode value." }, - { "name": "entries", "type": "array", "items": { "$ref": "Entry" }, "optional": true, "description": "Contains all entries on directory if the command completed successfully." } - ], - "description": "Returns content of the directory." - }, - { - "name": "requestMetadata", - "async": true, - "parameters": [ - { "name": "url", "type": "string", "description": "URL of the entry that the frontend is requesting to get metadata from." } - ], - "returns": [ - { "name": "errorCode", "type": "integer", "description": "0, if no error. Otherwise, errorCode is set to FileError::ErrorCode value." }, - { "name": "metadata", "$ref": "Metadata", "optional": true, "description": "Contains metadata of the entry if the command completed successfully." } - ], - "description": "Returns metadata of the entry." - }, - { - "name": "requestFileContent", - "async": true, - "parameters": [ - { "name": "url", "type": "string", "description": "URL of the file that the frontend is requesting to read from." }, - { "name": "readAsText", "type": "boolean", "description": "True if the content should be read as text, otherwise the result will be returned as base64 encoded text." }, - { "name": "start", "type": "integer", "optional": true, "description": "Specifies the start of range to read." }, - { "name": "end", "type": "integer", "optional": true, "description": "Specifies the end of range to read exclusively." }, - { "name": "charset", "type": "string", "optional": true, "description": "Overrides charset of the content when content is served as text." } - ], - "returns": [ - { "name": "errorCode", "type": "integer", "description": "0, if no error. Otherwise, errorCode is set to FileError::ErrorCode value." }, - { "name": "content", "type": "string", "optional": true, "description": "Content of the file." }, - { "name": "charset", "type": "string", "optional": true, "description": "Charset of the content if it is served as text." } - ], - "description": "Returns content of the file. Result should be sliced into [start, end)." - }, - { - "name": "deleteEntry", - "async": true, - "parameters": [ - { "name": "url", "type": "string", "description": "URL of the entry to delete." } - ], - "returns": [ - { "name": "errorCode", "type": "integer", "description": "0, if no error. Otherwise errorCode is set to FileError::ErrorCode value." } - ], - "description": "Deletes specified entry. If the entry is a directory, the agent deletes children recursively." - } - ] - }, - { - "domain": "DOM", - "description": "This domain exposes DOM read/write operations. Each DOM Node is represented with its mirror object that has an <code>id</code>. This <code>id</code> can be used to get additional information on the Node, resolve it into the JavaScript object wrapper, etc. It is important that client receives DOM events only for the nodes that are known to the client. Backend keeps track of the nodes that were sent to the client and never sends the same node twice. It is client's responsibility to collect information about the nodes that were sent to the client.<p>Note that <code>iframe</code> owner elements will return corresponding document elements as their child nodes.</p>", - "types": [ - { - "id": "NodeId", - "type": "integer", - "description": "Unique DOM node identifier." - }, - { - "id": "BackendNodeId", - "type": "integer", - "description": "Unique DOM node identifier used to reference a node that may not have been pushed to the front-end.", - "hidden": true - }, - { - "id": "Node", - "type": "object", - "properties": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Node identifier that is passed into the rest of the DOM messages as the <code>nodeId</code>. Backend will only push node with given <code>id</code> once. It is aware of all requested nodes and will only fire DOM events for nodes known to the client." }, - { "name": "nodeType", "type": "integer", "description": "<code>Node</code>'s nodeType." }, - { "name": "nodeName", "type": "string", "description": "<code>Node</code>'s nodeName." }, - { "name": "localName", "type": "string", "description": "<code>Node</code>'s localName." }, - { "name": "nodeValue", "type": "string", "description": "<code>Node</code>'s nodeValue." }, - { "name": "childNodeCount", "type": "integer", "optional": true, "description": "Child count for <code>Container</code> nodes." }, - { "name": "children", "type": "array", "optional": true, "items": { "$ref": "Node" }, "description": "Child nodes of this node when requested with children." }, - { "name": "attributes", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Attributes of the <code>Element</code> node in the form of flat array <code>[name1, value1, name2, value2]</code>." }, - { "name": "documentURL", "type": "string", "optional": true, "description": "Document URL that <code>Document</code> or <code>FrameOwner</code> node points to." }, - { "name": "baseURL", "type": "string", "optional": true, "description": "Base URL that <code>Document</code> or <code>FrameOwner</code> node uses for URL completion.", "hidden": true }, - { "name": "publicId", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s publicId." }, - { "name": "systemId", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s systemId." }, - { "name": "internalSubset", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s internalSubset." }, - { "name": "xmlVersion", "type": "string", "optional": true, "description": "<code>Document</code>'s XML version in case of XML documents." }, - { "name": "name", "type": "string", "optional": true, "description": "<code>Attr</code>'s name." }, - { "name": "value", "type": "string", "optional": true, "description": "<code>Attr</code>'s value." }, - { "name": "frameId", "$ref": "Page.FrameId", "optional": true, "description": "Frame ID for frame owner elements.", "hidden": true }, - { "name": "contentDocument", "$ref": "Node", "optional": true, "description": "Content document for frame owner elements." }, - { "name": "shadowRoots", "type": "array", "optional": true, "items": { "$ref": "Node" }, "description": "Shadow root list for given element host.", "hidden": true }, - { "name": "templateContent", "$ref": "Node", "optional": true, "description": "Content document fragment for template elements", "hidden": true } - ], - "description": "DOM interaction is implemented in terms of mirror objects that represent the actual DOM nodes. DOMNode is a base node mirror type." - }, - { - "id": "EventListener", - "type": "object", - "hidden": true, - "properties": [ - { "name": "type", "type": "string", "description": "<code>EventListener</code>'s type." }, - { "name": "useCapture", "type": "boolean", "description": "<code>EventListener</code>'s useCapture." }, - { "name": "isAttribute", "type": "boolean", "description": "<code>EventListener</code>'s isAttribute." }, - { "name": "nodeId", "$ref": "NodeId", "description": "Target <code>DOMNode</code> id." }, - { "name": "handlerBody", "type": "string", "description": "Event handler function body." }, - { "name": "location", "$ref": "Debugger.Location", "optional": true, "description": "Handler code location." }, - { "name": "sourceName", "type": "string", "optional": true, "description": "Source script URL." }, - { "name": "handler", "$ref": "Runtime.RemoteObject", "optional": true, "description": "Event handler function value." } - ], - "description": "DOM interaction is implemented in terms of mirror objects that represent the actual DOM nodes. DOMNode is a base node mirror type." - }, - { - "id": "RGBA", - "type": "object", - "properties": [ - { "name": "r", "type": "integer", "description": "The red component, in the [0-255] range." }, - { "name": "g", "type": "integer", "description": "The green component, in the [0-255] range." }, - { "name": "b", "type": "integer", "description": "The blue component, in the [0-255] range." }, - { "name": "a", "type": "number", "optional": true, "description": "The alpha component, in the [0-1] range (default: 1)." } - ], - "description": "A structure holding an RGBA color." - }, - { - "id": "Quad", - "type": "array", - "items": { "type": "number" }, - "minItems": 8, - "maxItems": 8, - "description": "An array of quad vertices, x immediately followed by y for each point, points clock-wise.", - "hidden": true - }, - { - "id": "BoxModel", - "type": "object", - "hidden": true, - "properties": [ - { "name": "content", "$ref": "Quad", "description": "Content box" }, - { "name": "padding", "$ref": "Quad", "description": "Padding box" }, - { "name": "border", "$ref": "Quad", "description": "Border box" }, - { "name": "margin", "$ref": "Quad", "description": "Margin box" }, - { "name": "width", "type": "integer", "description": "Node width" }, - { "name": "height", "type": "integer", "description": "Node height" }, - { "name": "shapeOutside", "type": "string", "description": "CSS Shape Outside" } - ], - "description": "Box model." - }, - { - "id": "Rect", - "type": "object", - "hidden": true, - "properties": [ - { "name": "x", "type": "number", "description": "X coordinate" }, - { "name": "y", "type": "number", "description": "Y coordinate" }, - { "name": "width", "type": "number", "description": "Rectangle width" }, - { "name": "height", "type": "number", "description": "Rectangle height" } - ], - "description": "Rectangle." - }, - { - "id": "HighlightConfig", - "type": "object", - "properties": [ - { "name": "showInfo", "type": "boolean", "optional": true, "description": "Whether the node info tooltip should be shown (default: false)." }, - { "name": "contentColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight fill color (default: transparent)." }, - { "name": "paddingColor", "$ref": "RGBA", "optional": true, "description": "The padding highlight fill color (default: transparent)." }, - { "name": "borderColor", "$ref": "RGBA", "optional": true, "description": "The border highlight fill color (default: transparent)." }, - { "name": "marginColor", "$ref": "RGBA", "optional": true, "description": "The margin highlight fill color (default: transparent)." }, - { "name": "eventTargetColor", "$ref": "RGBA", "optional": true, "hidden": true, "description": "The event target element highlight fill color (default: transparent)." } - ], - "description": "Configuration data for the highlighting of page elements." - } - ], - "commands": [ - { - "name": "getDocument", - "returns": [ - { "name": "root", "$ref": "Node", "description": "Resulting node." } - ], - "description": "Returns the root DOM node to the caller." - }, - { - "name": "requestChildNodes", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get children for." }, - { "name": "depth", "type": "integer", "optional": true, "description": "The maximum depth at which children should be retrieved, defaults to 1. Use -1 for the entire subtree or provide an integer larger than 0.", "hidden": true } - ], - "description": "Requests that children of the node with given id are returned to the caller in form of <code>setChildNodes</code> events where not only immediate children are retrieved, but all children down to the specified depth." - }, - { - "name": "querySelector", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to query upon." }, - { "name": "selector", "type": "string", "description": "Selector string." } - ], - "returns": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Query selector result." } - ], - "description": "Executes <code>querySelector</code> on a given node." - }, - { - "name": "querySelectorAll", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to query upon." }, - { "name": "selector", "type": "string", "description": "Selector string." } - ], - "returns": [ - { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Query selector result." } - ], - "description": "Executes <code>querySelectorAll</code> on a given node." - }, - { - "name": "setNodeName", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set name for." }, - { "name": "name", "type": "string", "description": "New node's name." } - ], - "returns": [ - { "name": "nodeId", "$ref": "NodeId", "description": "New node's id." } - ], - "description": "Sets node name for a node with given id." - }, - { - "name": "setNodeValue", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set value for." }, - { "name": "value", "type": "string", "description": "New node's value." } - ], - "description": "Sets node value for a node with given id." - }, - { - "name": "removeNode", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to remove." } - ], - "description": "Removes node with given id." - }, - { - "name": "setAttributeValue", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to set attribute for." }, - { "name": "name", "type": "string", "description": "Attribute name." }, - { "name": "value", "type": "string", "description": "Attribute value." } - ], - "description": "Sets attribute for an element with given id." - }, - { - "name": "setAttributesAsText", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to set attributes for." }, - { "name": "text", "type": "string", "description": "Text with a number of attributes. Will parse this text using HTML parser." }, - { "name": "name", "type": "string", "optional": true, "description": "Attribute name to replace with new attributes derived from text in case text parsed successfully." } - ], - "description": "Sets attributes on element with given id. This method is useful when user edits some existing attribute value and types in several attribute name/value pairs." - }, - { - "name": "removeAttribute", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to remove attribute from." }, - { "name": "name", "type": "string", "description": "Name of the attribute to remove." } - ], - "description": "Removes attribute with given name from an element with given id." - }, - { - "name": "getEventListenersForNode", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get listeners for." }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name for handler value. Handler value is not returned without this parameter specified." } - ], - "returns": [ - { "name": "listeners", "type": "array", "items": { "$ref": "EventListener"}, "description": "Array of relevant listeners." } - ], - "description": "Returns event listeners relevant to the node.", - "hidden": true - }, - { - "name": "getOuterHTML", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get markup for." } - ], - "returns": [ - { "name": "outerHTML", "type": "string", "description": "Outer HTML markup." } - ], - "description": "Returns node's HTML markup." - }, - { - "name": "setOuterHTML", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set markup for." }, - { "name": "outerHTML", "type": "string", "description": "Outer HTML markup to set." } - ], - "description": "Sets node HTML markup, returns new node id." - }, - { - "name": "performSearch", - "parameters": [ - { "name": "query", "type": "string", "description": "Plain text or query selector or XPath search query." } - ], - "returns": [ - { "name": "searchId", "type": "string", "description": "Unique search session identifier." }, - { "name": "resultCount", "type": "integer", "description": "Number of search results." } - ], - "description": "Searches for a given string in the DOM tree. Use <code>getSearchResults</code> to access search results or <code>cancelSearch</code> to end this search session.", - "hidden": true - }, - { - "name": "getSearchResults", - "parameters": [ - { "name": "searchId", "type": "string", "description": "Unique search session identifier." }, - { "name": "fromIndex", "type": "integer", "description": "Start index of the search result to be returned." }, - { "name": "toIndex", "type": "integer", "description": "End index of the search result to be returned." } - ], - "returns": [ - { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Ids of the search result nodes." } - ], - "description": "Returns search results from given <code>fromIndex</code> to given <code>toIndex</code> from the sarch with the given identifier.", - "hidden": true - }, - { - "name": "discardSearchResults", - "parameters": [ - { "name": "searchId", "type": "string", "description": "Unique search session identifier." } - ], - "description": "Discards search results from the session with the given id. <code>getSearchResults</code> should no longer be called for that search.", - "hidden": true - }, - { - "name": "requestNode", - "parameters": [ - { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "JavaScript object id to convert into node." } - ], - "returns": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Node id for given object." } - ], - "description": "Requests that the node is sent to the caller given the JavaScript node object reference. All nodes that form the path from the node to the root are also sent to the client as a series of <code>setChildNodes</code> notifications." - }, - { - "name": "setInspectModeEnabled", - "hidden": true, - "parameters": [ - { "name": "enabled", "type": "boolean", "description": "True to enable inspection mode, false to disable it." }, - { "name": "inspectShadowDOM", "type": "boolean", "optional": true, "description": "True to enable inspection mode for shadow DOM." }, - { "name": "highlightConfig", "$ref": "HighlightConfig", "optional": true, "description": "A descriptor for the highlight appearance of hovered-over nodes. May be omitted if <code>enabled == false</code>." } - ], - "description": "Enters the 'inspect' mode. In this mode, elements that user is hovering over are highlighted. Backend then generates 'inspectNodeRequested' event upon element selection." - }, - { - "name": "highlightRect", - "parameters": [ - { "name": "x", "type": "integer", "description": "X coordinate" }, - { "name": "y", "type": "integer", "description": "Y coordinate" }, - { "name": "width", "type": "integer", "description": "Rectangle width" }, - { "name": "height", "type": "integer", "description": "Rectangle height" }, - { "name": "color", "$ref": "RGBA", "optional": true, "description": "The highlight fill color (default: transparent)." }, - { "name": "outlineColor", "$ref": "RGBA", "optional": true, "description": "The highlight outline color (default: transparent)." } - ], - "description": "Highlights given rectangle. Coordinates are absolute with respect to the main frame viewport." - }, - { - "name": "highlightQuad", - "parameters": [ - { "name": "quad", "$ref": "Quad", "description": "Quad to highlight" }, - { "name": "color", "$ref": "RGBA", "optional": true, "description": "The highlight fill color (default: transparent)." }, - { "name": "outlineColor", "$ref": "RGBA", "optional": true, "description": "The highlight outline color (default: transparent)." } - ], - "description": "Highlights given quad. Coordinates are absolute with respect to the main frame viewport.", - "hidden": true - }, - { - "name": "highlightNode", - "parameters": [ - { "name": "highlightConfig", "$ref": "HighlightConfig", "description": "A descriptor for the highlight appearance." }, - { "name": "nodeId", "$ref": "NodeId", "optional": true, "description": "Identifier of the node to highlight." }, - { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "optional": true, "description": "JavaScript object id of the node to be highlighted.", "hidden": true } - ], - "description": "Highlights DOM node with given id or with the given JavaScript object wrapper. Either nodeId or objectId must be specified." - }, - { - "name": "hideHighlight", - "description": "Hides DOM node highlight." - }, - { - "name": "highlightFrame", - "parameters": [ - { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame to highlight." }, - { "name": "contentColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight fill color (default: transparent)." }, - { "name": "contentOutlineColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight outline color (default: transparent)." } - ], - "description": "Highlights owner element of the frame with given id.", - "hidden": true - }, - { - "name": "pushNodeByPathToFrontend", - "parameters": [ - { "name": "path", "type": "string", "description": "Path to node in the proprietary format." } - ], - "returns": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node for given path." } - ], - "description": "Requests that the node is sent to the caller given its path. // FIXME, use XPath", - "hidden": true - }, - { - "name": "pushNodeByBackendIdToFrontend", - "parameters": [ - { "name": "backendNodeId", "$ref": "BackendNodeId", "description": "The backend node id of the node." } - ], - "returns": [ - { "name": "nodeId", "$ref": "NodeId", "description": "The pushed node's id." } - ], - "description": "Requests that the node is sent to the caller given its backend node id.", - "hidden": true - }, - { - "name": "releaseBackendNodeIds", - "parameters": [ - { "name": "nodeGroup", "type": "string", "description": "The backend node ids group name." } - ], - "description": "Requests that group of <code>BackendNodeIds</code> is released.", - "hidden": true - }, - { - "name": "resolveNode", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to resolve." }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." } - ], - "returns": [ - { "name": "object", "$ref": "Runtime.RemoteObject", "description": "JavaScript object wrapper for given node." } - ], - "description": "Resolves JavaScript node object for given node id." - }, - { - "name": "getAttributes", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to retrieve attibutes for." } - ], - "returns": [ - { "name": "attributes", "type": "array", "items": { "type": "string" }, "description": "An interleaved array of node attribute names and values." } - ], - "description": "Returns attributes for the specified node." - }, - { - "name": "moveTo", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to drop." }, - { "name": "targetNodeId", "$ref": "NodeId", "description": "Id of the element to drop into." }, - { "name": "insertBeforeNodeId", "$ref": "NodeId", "optional": true, "description": "Drop node before given one." } - ], - "returns": [ - { "name": "nodeId", "$ref": "NodeId", "description": "New id of the moved node." } - ], - "description": "Moves node into the new container, places it before the given anchor." - }, - { - "name": "undo", - "description": "Undoes the last performed action.", - "hidden": true - }, - { - "name": "redo", - "description": "Re-does the last undone action.", - "hidden": true - }, - { - "name": "markUndoableState", - "description": "Marks last undoable state.", - "hidden": true - }, - { - "name": "focus", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to focus." } - ], - "description": "Focuses the given element.", - "hidden": true - }, - { - "name": "setFileInputFiles", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the file input node to set files for." }, - { "name": "files", "type": "array", "items": { "type": "string" }, "description": "Array of file paths to set." } - ], - "description": "Sets files for the given file input element.", - "hidden": true - }, - { - "name": "getBoxModel", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get box model for." } - ], - "returns": [ - { "name": "model", "$ref": "BoxModel", "description": "Box model for the node." } - ], - "description": "Returns boxes for the currently selected nodes.", - "hidden": true - }, - { - "name": "getNodeForLocation", - "parameters": [ - { "name": "x", "type": "integer", "description": "X coordinate." }, - { "name": "y", "type": "integer", "description": "Y coordinate." } - ], - "returns": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node at given coordinates." } - ], - "description": "Returns node id at given location.", - "hidden": true - } - ], - "events": [ - { - "name": "documentUpdated", - "description": "Fired when <code>Document</code> has been totally updated. Node ids are no longer valid." - }, - { - "name": "inspectNodeRequested", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to inspect." } - ], - "description": "Fired when the node should be inspected. This happens after call to <code>setInspectModeEnabled</code>.", - "hidden" : true - }, - { - "name": "setChildNodes", - "parameters": [ - { "name": "parentId", "$ref": "NodeId", "description": "Parent node id to populate with children." }, - { "name": "nodes", "type": "array", "items": { "$ref": "Node"}, "description": "Child nodes array." } - ], - "description": "Fired when backend wants to provide client with the missing DOM structure. This happens upon most of the calls requesting node ids." - }, - { - "name": "attributeModified", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, - { "name": "name", "type": "string", "description": "Attribute name." }, - { "name": "value", "type": "string", "description": "Attribute value." } - ], - "description": "Fired when <code>Element</code>'s attribute is modified." - }, - { - "name": "attributeRemoved", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, - { "name": "name", "type": "string", "description": "A ttribute name." } - ], - "description": "Fired when <code>Element</code>'s attribute is removed." - }, - { - "name": "inlineStyleInvalidated", - "parameters": [ - { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Ids of the nodes for which the inline styles have been invalidated." } - ], - "description": "Fired when <code>Element</code>'s inline style is modified via a CSS property modification.", - "hidden": true - }, - { - "name": "characterDataModified", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, - { "name": "characterData", "type": "string", "description": "New text value." } - ], - "description": "Mirrors <code>DOMCharacterDataModified</code> event." - }, - { - "name": "childNodeCountUpdated", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, - { "name": "childNodeCount", "type": "integer", "description": "New node count." } - ], - "description": "Fired when <code>Container</code>'s child node count has changed." - }, - { - "name": "childNodeInserted", - "parameters": [ - { "name": "parentNodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, - { "name": "previousNodeId", "$ref": "NodeId", "description": "If of the previous siblint." }, - { "name": "node", "$ref": "Node", "description": "Inserted node data." } - ], - "description": "Mirrors <code>DOMNodeInserted</code> event." - }, - { - "name": "childNodeRemoved", - "parameters": [ - { "name": "parentNodeId", "$ref": "NodeId", "description": "Parent id." }, - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has been removed." } - ], - "description": "Mirrors <code>DOMNodeRemoved</code> event." - }, - { - "name": "shadowRootPushed", - "parameters": [ - { "name": "hostId", "$ref": "NodeId", "description": "Host element id." }, - { "name": "root", "$ref": "Node", "description": "Shadow root." } - ], - "description": "Called when shadow root is pushed into the element.", - "hidden": true - }, - { - "name": "shadowRootPopped", - "parameters": [ - { "name": "hostId", "$ref": "NodeId", "description": "Host element id." }, - { "name": "rootId", "$ref": "NodeId", "description": "Shadow root id." } - ], - "description": "Called when shadow root is popped from the element.", - "hidden": true - } - ] - }, - { - "domain": "CSS", - "hidden": true, - "description": "This domain exposes CSS read/write operations. All CSS objects (stylesheets, rules, and styles) have an associated <code>id</code> used in subsequent operations on the related object. Each object type has a specific <code>id</code> structure, and those are not interchangeable between objects of different kinds. CSS objects can be loaded using the <code>get*ForNode()</code> calls (which accept a DOM node id). A client can also discover all the existing stylesheets with the <code>getAllStyleSheets()</code> method (or keeping track of the <code>styleSheetAdded</code>/<code>styleSheetRemoved</code> events) and subsequently load the required stylesheet contents using the <code>getStyleSheet[Text]()</code> methods.", - "types": [ - { - "id": "StyleSheetId", - "type": "string" - }, - { - "id": "CSSStyleId", - "type": "object", - "properties": [ - { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "Enclosing stylesheet identifier." }, - { "name": "ordinal", "type": "integer", "description": "The style ordinal within the stylesheet." } - ], - "description": "This object identifies a CSS style in a unique way." - }, - { - "id": "StyleSheetOrigin", - "type": "string", - "enum": ["user", "user-agent", "inspector", "regular"], - "description": "Stylesheet type: \"user\" for user stylesheets, \"user-agent\" for user-agent stylesheets, \"inspector\" for stylesheets created by the inspector (i.e. those holding the \"via inspector\" rules), \"regular\" for regular stylesheets." - }, - { - "id": "CSSRuleId", - "type": "object", - "properties": [ - { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "Enclosing stylesheet identifier." }, - { "name": "ordinal", "type": "integer", "description": "The rule ordinal within the stylesheet." } - ], - "description": "This object identifies a CSS rule in a unique way." - }, - { - "id": "PseudoIdMatches", - "type": "object", - "properties": [ - { "name": "pseudoId", "type": "integer", "description": "Pseudo style identifier (see <code>enum PseudoId</code> in <code>ComputedStyleConstants.h</code>)."}, - { "name": "matches", "type": "array", "items": { "$ref": "RuleMatch" }, "description": "Matches of CSS rules applicable to the pseudo style."} - ], - "description": "CSS rule collection for a single pseudo style." - }, - { - "id": "InheritedStyleEntry", - "type": "object", - "properties": [ - { "name": "inlineStyle", "$ref": "CSSStyle", "optional": true, "description": "The ancestor node's inline style, if any, in the style inheritance chain." }, - { "name": "matchedCSSRules", "type": "array", "items": { "$ref": "RuleMatch" }, "description": "Matches of CSS rules matching the ancestor node in the style inheritance chain." } - ], - "description": "CSS rule collection for a single pseudo style." - }, - { - "id": "RuleMatch", - "type": "object", - "properties": [ - { "name": "rule", "$ref": "CSSRule", "description": "CSS rule in the match." }, - { "name": "matchingSelectors", "type": "array", "items": { "type": "integer" }, "description": "Matching selector indices in the rule's selectorList selectors (0-based)." } - ], - "description": "Match data for a CSS rule." - }, - { - "id": "SelectorList", - "type": "object", - "properties": [ - { "name": "selectors", "type": "array", "items": { "type": "string" }, "description": "Selectors in the list." }, - { "name": "text", "type": "string", "description": "Rule selector text." }, - { "name": "range", "$ref": "SourceRange", "optional": true, "description": "Rule selector range in the underlying resource (if available)." } - ], - "description": "Selector list data." - }, - { - "id": "CSSStyleAttribute", - "type": "object", - "properties": [ - { "name": "name", "type": "string", "description": "DOM attribute name (e.g. \"width\")."}, - { "name": "style", "$ref": "CSSStyle", "description": "CSS style generated by the respective DOM attribute."} - ], - "description": "CSS style information for a DOM style attribute." - }, - { - "id": "CSSStyleSheetHeader", - "type": "object", - "properties": [ - { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "The stylesheet identifier."}, - { "name": "frameId", "$ref": "Page.FrameId", "description": "Owner frame identifier."}, - { "name": "sourceURL", "type": "string", "description": "Stylesheet resource URL."}, - { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with the stylesheet (if any)." }, - { "name": "origin", "$ref": "StyleSheetOrigin", "description": "Stylesheet origin."}, - { "name": "title", "type": "string", "description": "Stylesheet title."}, - { "name": "disabled", "type": "boolean", "description": "Denotes whether the stylesheet is disabled."}, - { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "Whether the sourceURL field value comes from the sourceURL comment." }, - { "name": "isInline", "type": "boolean", "description": "Whether this stylesheet is created for STYLE tag by parser. This flag is not set for document.written STYLE tags." }, - { "name": "startLine", "type": "number", "description": "Line offset of the stylesheet within the resource (zero based)." }, - { "name": "startColumn", "type": "number", "description": "Column offset of the stylesheet within the resource (zero based)." } - ], - "description": "CSS stylesheet metainformation." - }, - { - "id": "CSSStyleSheetBody", - "type": "object", - "properties": [ - { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "The stylesheet identifier."}, - { "name": "rules", "type": "array", "items": { "$ref": "CSSRule" }, "description": "Stylesheet resource URL."}, - { "name": "text", "type": "string", "optional": true, "description": "Stylesheet resource contents (if available)."} - ], - "description": "CSS stylesheet contents." - }, - { - "id": "CSSRule", - "type": "object", - "properties": [ - { "name": "ruleId", "$ref": "CSSRuleId", "optional": true, "description": "The CSS rule identifier (absent for user agent stylesheet and user-specified stylesheet rules)."}, - { "name": "selectorList", "$ref": "SelectorList", "description": "Rule selector data." }, - { "name": "sourceURL", "type": "string", "optional": true, "description": "Parent stylesheet resource URL (for regular rules)."}, - { "name": "origin", "$ref": "StyleSheetOrigin", "description": "Parent stylesheet's origin."}, - { "name": "style", "$ref": "CSSStyle", "description": "Associated style declaration." }, - { "name": "media", "type": "array", "items": { "$ref": "CSSMedia" }, "optional": true, "description": "Media list array (for rules involving media queries). The array enumerates media queries starting with the innermost one, going outwards." } - ], - "description": "CSS rule representation." - }, - { - "id": "SourceRange", - "type": "object", - "properties": [ - { "name": "startLine", "type": "integer", "description": "Start line of range." }, - { "name": "startColumn", "type": "integer", "description": "Start column of range (inclusive)." }, - { "name": "endLine", "type": "integer", "description": "End line of range" }, - { "name": "endColumn", "type": "integer", "description": "End column of range (exclusive)." } - ], - "description": "Text range within a resource. All numbers are zero-based." - }, - { - "id": "ShorthandEntry", - "type": "object", - "properties": [ - { "name": "name", "type": "string", "description": "Shorthand name." }, - { "name": "value", "type": "string", "description": "Shorthand value." } - ] - }, - { - "id": "CSSPropertyInfo", - "type": "object", - "properties": [ - { "name": "name", "type": "string", "description": "Property name." }, - { "name": "longhands", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Longhand property names." } - ] - }, - { - "id": "CSSComputedStyleProperty", - "type": "object", - "properties": [ - { "name": "name", "type": "string", "description": "Computed style property name." }, - { "name": "value", "type": "string", "description": "Computed style property value." } - ] - }, - { - "id": "CSSStyle", - "type": "object", - "properties": [ - { "name": "styleId", "$ref": "CSSStyleId", "optional": true, "description": "The CSS style identifier (absent for attribute styles)." }, - { "name": "cssProperties", "type": "array", "items": { "$ref": "CSSProperty" }, "description": "CSS properties in the style." }, - { "name": "shorthandEntries", "type": "array", "items": { "$ref": "ShorthandEntry" }, "description": "Computed values for all shorthands found in the style." }, - { "name": "cssText", "type": "string", "optional": true, "description": "Style declaration text (if available)." }, - { "name": "range", "$ref": "SourceRange", "optional": true, "description": "Style declaration range in the enclosing stylesheet (if available)." }, - { "name": "width", "type": "string", "optional": true, "description": "The effective \"width\" property value from this style." }, - { "name": "height", "type": "string", "optional": true, "description": "The effective \"height\" property value from this style." } - ], - "description": "CSS style representation." - }, - { - "id": "CSSProperty", - "type": "object", - "properties": [ - { "name": "name", "type": "string", "description": "The property name." }, - { "name": "value", "type": "string", "description": "The property value." }, - { "name": "priority", "type": "string", "optional": true, "description": "The property priority (implies \"\" if absent)." }, - { "name": "implicit", "type": "boolean", "optional": true, "description": "Whether the property is implicit (implies <code>false</code> if absent)." }, - { "name": "text", "type": "string", "optional": true, "description": "The full property text as specified in the style." }, - { "name": "parsedOk", "type": "boolean", "optional": true, "description": "Whether the property is understood by the browser (implies <code>true</code> if absent)." }, - { "name": "status", "type": "string", "enum": ["active", "inactive", "disabled", "style"], "optional": true, "description": "The property status: \"active\" if the property is effective in the style, \"inactive\" if the property is overridden by a same-named property in this style later on, \"disabled\" if the property is disabled by the user, \"style\" (implied if absent) if the property is reported by the browser rather than by the CSS source parser." }, - { "name": "range", "$ref": "SourceRange", "optional": true, "description": "The entire property range in the enclosing style declaration (if available)." } - ], - "description": "CSS property declaration data." - }, - { - "id": "CSSMedia", - "type": "object", - "properties": [ - { "name": "text", "type": "string", "description": "Media query text." }, - { "name": "source", "type": "string", "enum": ["mediaRule", "importRule", "linkedSheet", "inlineSheet"], "description": "Source of the media query: \"mediaRule\" if specified by a @media rule, \"importRule\" if specified by an @import rule, \"linkedSheet\" if specified by a \"media\" attribute in a linked stylesheet's LINK tag, \"inlineSheet\" if specified by a \"media\" attribute in an inline stylesheet's STYLE tag." }, - { "name": "sourceURL", "type": "string", "optional": true, "description": "URL of the document containing the media query description." }, - { "name": "range", "$ref": "SourceRange", "optional": true, "description": "The associated rule (@media or @import) header range in the enclosing stylesheet (if available)." }, - { "name": "parentStyleSheetId", "$ref": "StyleSheetId", "optional": true, "description": "Identifier of the stylesheet containing this object (if exists)." } - ], - "description": "CSS media query descriptor." - }, - { - "id": "SelectorProfileEntry", - "type": "object", - "properties": [ - { "name": "selector", "type": "string", "description": "CSS selector of the corresponding rule." }, - { "name": "url", "type": "string", "description": "URL of the resource containing the corresponding rule." }, - { "name": "lineNumber", "type": "integer", "description": "Selector line number in the resource for the corresponding rule." }, - { "name": "time", "type": "number", "description": "Total time this rule handling contributed to the browser running time during profiling (in milliseconds)." }, - { "name": "hitCount", "type": "integer", "description": "Number of times this rule was considered a candidate for matching against DOM elements." }, - { "name": "matchCount", "type": "integer", "description": "Number of times this rule actually matched a DOM element." } - ], - "description": "CSS selector profile entry." - }, - { - "id": "SelectorProfile", - "type": "object", - "properties": [ - { "name": "totalTime", "type": "number", "description": "Total processing time for all selectors in the profile (in milliseconds)." }, - { "name": "data", "type": "array", "items": { "$ref": "SelectorProfileEntry" }, "description": "CSS selector profile entries." } - ] - }, - { - "id": "Region", - "type": "object", - "properties": [ - { "name": "regionOverset", "type": "string", "enum": ["overset", "fit", "empty"], "description": "The \"overset\" attribute of a Named Flow." }, - { "name": "nodeId", "$ref": "DOM.NodeId", "description": "The corresponding DOM node id." } - ], - "description": "This object represents a region that flows from a Named Flow.", - "hidden": true - }, - { - "id": "NamedFlow", - "type": "object", - "properties": [ - { "name": "documentNodeId", "$ref": "DOM.NodeId", "description": "The document node id." }, - { "name": "name", "type": "string", "description": "Named Flow identifier." }, - { "name": "overset", "type": "boolean", "description": "The \"overset\" attribute of a Named Flow." }, - { "name": "content", "type": "array", "items": { "$ref": "DOM.NodeId" }, "description": "An array of nodes that flow into the Named Flow." }, - { "name": "regions", "type": "array", "items": { "$ref": "Region" }, "description": "An array of regions associated with the Named Flow." } - ], - "description": "This object represents a Named Flow.", - "hidden": true - }, - { - "id": "PlatformFontUsage", - "type": "object", - "properties": [ - { "name": "familyName", "type": "string", "description": "Font's family name reported by platform."}, - { "name": "glyphCount", "type": "number", "description": "Amount of glyphs that were rendered with this font."} - ], - "description": "Information about amount of glyphs that were rendered with given font." - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables the CSS agent for the given page. Clients should not assume that the CSS agent has been enabled until the result of this command is received." - }, - { - "name": "disable", - "description": "Disables the CSS agent for the given page." - }, - { - "name": "getMatchedStylesForNode", - "parameters": [ - { "name": "nodeId", "$ref": "DOM.NodeId" }, - { "name": "includePseudo", "type": "boolean", "optional": true, "description": "Whether to include pseudo styles (default: true)." }, - { "name": "includeInherited", "type": "boolean", "optional": true, "description": "Whether to include inherited styles (default: true)." } - ], - "returns": [ - { "name": "matchedCSSRules", "type": "array", "items": { "$ref": "RuleMatch" }, "optional": true, "description": "CSS rules matching this node, from all applicable stylesheets." }, - { "name": "pseudoElements", "type": "array", "items": { "$ref": "PseudoIdMatches" }, "optional": true, "description": "Pseudo style matches for this node." }, - { "name": "inherited", "type": "array", "items": { "$ref": "InheritedStyleEntry" }, "optional": true, "description": "A chain of inherited styles (from the immediate node parent up to the DOM tree root)." } - ], - "description": "Returns requested styles for a DOM node identified by <code>nodeId</code>." - }, - { - "name": "getInlineStylesForNode", - "parameters": [ - { "name": "nodeId", "$ref": "DOM.NodeId" } - ], - "returns": [ - { "name": "inlineStyle", "$ref": "CSSStyle", "optional": true, "description": "Inline style for the specified DOM node." }, - { "name": "attributesStyle", "$ref": "CSSStyle", "optional": true, "description": "Attribute-defined element style (e.g. resulting from \"width=20 height=100%\")."} - ], - "description": "Returns the styles defined inline (explicitly in the \"style\" attribute and implicitly, using DOM attributes) for a DOM node identified by <code>nodeId</code>." - }, - { - "name": "getComputedStyleForNode", - "parameters": [ - { "name": "nodeId", "$ref": "DOM.NodeId" } - ], - "returns": [ - { "name": "computedStyle", "type": "array", "items": { "$ref": "CSSComputedStyleProperty" }, "description": "Computed style for the specified DOM node." } - ], - "description": "Returns the computed style for a DOM node identified by <code>nodeId</code>." - }, - { - "name": "getPlatformFontsForNode", - "parameters": [ - { "name": "nodeId", "$ref": "DOM.NodeId" } - ], - "returns": [ - { "name": "cssFamilyName", "type": "string", "description": "Font family name which is determined by computed style." }, - { "name": "fonts", "type": "array", "items": { "$ref": "PlatformFontUsage"}, "description": "Usage statistics for every employed platform font." } - ], - "description": "Requests information about platform fonts which we used to render child TextNodes in the given node.", - "hidden": true - }, - { - "name": "getAllStyleSheets", - "returns": [ - { "name": "headers", "type": "array", "items": { "$ref": "CSSStyleSheetHeader" }, "description": "Descriptor entries for all available stylesheets." } - ], - "description": "Returns metainfo entries for all known stylesheets." - }, - { - "name": "getStyleSheet", - "parameters": [ - { "name": "styleSheetId", "$ref": "StyleSheetId" } - ], - "returns": [ - { "name": "styleSheet", "$ref": "CSSStyleSheetBody", "description": "Stylesheet contents for the specified <code>styleSheetId</code>." } - ], - "description": "Returns stylesheet data for the specified <code>styleSheetId</code>." - }, - { - "name": "getStyleSheetText", - "parameters": [ - { "name": "styleSheetId", "$ref": "StyleSheetId" } - ], - "returns": [ - { "name": "text", "type": "string", "description": "The stylesheet text." } - ], - "description": "Returns the current textual content and the URL for a stylesheet." - }, - { - "name": "setStyleSheetText", - "parameters": [ - { "name": "styleSheetId", "$ref": "StyleSheetId" }, - { "name": "text", "type": "string" } - ], - "description": "Sets the new stylesheet text, thereby invalidating all existing <code>CSSStyleId</code>'s and <code>CSSRuleId</code>'s contained by this stylesheet." - }, - { - "name": "setStyleText", - "parameters": [ - { "name": "styleId", "$ref": "CSSStyleId" }, - { "name": "text", "type": "string" } - ], - "returns": [ - { "name": "style", "$ref": "CSSStyle", "description": "The resulting style after the text modification." } - ], - "description": "Updates the CSSStyleDeclaration text." - }, - { - "name": "setPropertyText", - "parameters": [ - { "name": "styleId", "$ref": "CSSStyleId" }, - { "name": "propertyIndex", "type": "integer" }, - { "name": "text", "type": "string" }, - { "name": "overwrite", "type": "boolean" } - ], - "returns": [ - { "name": "style", "$ref": "CSSStyle", "description": "The resulting style after the property text modification." } - ], - "description": "Sets the new <code>text</code> for a property in the respective style, at offset <code>propertyIndex</code>. If <code>overwrite</code> is <code>true</code>, a property at the given offset is overwritten, otherwise inserted. <code>text</code> entirely replaces the property <code>name: value</code>." - }, - { - "name": "toggleProperty", - "parameters": [ - { "name": "styleId", "$ref": "CSSStyleId" }, - { "name": "propertyIndex", "type": "integer" }, - { "name": "disable", "type": "boolean" } - ], - "returns": [ - { "name": "style", "$ref": "CSSStyle", "description": "The resulting style after the property toggling." } - ], - "description": "Toggles the property in the respective style, at offset <code>propertyIndex</code>. The <code>disable</code> parameter denotes whether the property should be disabled (i.e. removed from the style declaration). If <code>disable == false</code>, the property gets put back into its original place in the style declaration." - }, - { - "name": "setRuleSelector", - "parameters": [ - { "name": "ruleId", "$ref": "CSSRuleId" }, - { "name": "selector", "type": "string" } - ], - "returns": [ - { "name": "rule", "$ref": "CSSRule", "description": "The resulting rule after the selector modification." } - ], - "description": "Modifies the rule selector." - }, - { - "name": "addRule", - "parameters": [ - { "name": "contextNodeId", "$ref": "DOM.NodeId" }, - { "name": "selector", "type": "string" } - ], - "returns": [ - { "name": "rule", "$ref": "CSSRule", "description": "The newly created rule." } - ], - "description": "Creates a new empty rule with the given <code>selector</code> in a special \"inspector\" stylesheet in the owner document of the context node." - }, - { - "name": "getSupportedCSSProperties", - "returns": [ - { "name": "cssProperties", "type": "array", "items": { "$ref": "CSSPropertyInfo" }, "description": "Supported property metainfo." } - ], - "description": "Returns all supported CSS property names." - }, - { - "name": "forcePseudoState", - "parameters": [ - { "name": "nodeId", "$ref": "DOM.NodeId", "description": "The element id for which to force the pseudo state." }, - { "name": "forcedPseudoClasses", "type": "array", "items": { "type": "string", "enum": ["active", "focus", "hover", "visited"] }, "description": "Element pseudo classes to force when computing the element's style." } - ], - "description": "Ensures that the given node will have specified pseudo-classes whenever its style is computed by the browser." - }, - { - "name": "getNamedFlowCollection", - "parameters": [ - { "name": "documentNodeId", "$ref": "DOM.NodeId", "description": "The document node id for which to get the Named Flow Collection." } - ], - "returns": [ - { "name": "namedFlows", "type": "array", "items": { "$ref": "NamedFlow" }, "description": "An array containing the Named Flows in the document." } - ], - "description": "Returns the Named Flows from the document.", - "hidden": true - } - ], - "events": [ - { - "name": "mediaQueryResultChanged", - "description": "Fires whenever a MediaQuery result changes (for example, after a browser window has been resized.) The current implementation considers only viewport-dependent media features." - }, - { - "name": "styleSheetChanged", - "parameters": [ - { "name": "styleSheetId", "$ref": "StyleSheetId" } - ], - "description": "Fired whenever a stylesheet is changed as a result of the client operation." - }, - { - "name": "styleSheetAdded", - "parameters": [ - { "name": "header", "$ref": "CSSStyleSheetHeader", "description": "Added stylesheet metainfo." } - ], - "description": "Fired whenever an active document stylesheet is added." - }, - { - "name": "styleSheetRemoved", - "parameters": [ - { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "Identifier of the removed stylesheet." } - ], - "description": "Fired whenever an active document stylesheet is removed." - }, - { - "name": "namedFlowCreated", - "parameters": [ - { "name": "namedFlow", "$ref": "NamedFlow", "description": "The new Named Flow." } - ], - "description": "Fires when a Named Flow is created.", - "hidden": true - }, - { - "name": "namedFlowRemoved", - "parameters": [ - { "name": "documentNodeId", "$ref": "DOM.NodeId", "description": "The document node id." }, - { "name": "flowName", "type": "string", "description": "Identifier of the removed Named Flow." } - ], - "description": "Fires when a Named Flow is removed: has no associated content nodes and regions.", - "hidden": true - }, - { - "name": "regionLayoutUpdated", - "parameters": [ - { "name": "namedFlow", "$ref": "NamedFlow", "description": "The Named Flow whose layout may have changed." } - ], - "description": "Fires when a Named Flow's layout may have changed.", - "hidden": true - }, - { - "name": "regionOversetChanged", - "parameters": [ - { "name": "namedFlow", "$ref": "NamedFlow", "description": "The Named Flow containing the regions whose regionOverset values changed." } - ], - "description": "Fires if any of the regionOverset values changed in a Named Flow's region chain.", - "hidden": true - } - ] - }, - { - "domain": "Debugger", - "description": "Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing breakpoints, stepping through execution, exploring stack traces, etc.", - "types": [ - { - "id": "BreakpointId", - "type": "string", - "description": "Breakpoint identifier." - }, - { - "id": "ScriptId", - "type": "string", - "description": "Unique script identifier." - }, - { - "id": "CallFrameId", - "type": "string", - "description": "Call frame identifier." - }, - { - "id": "Location", - "type": "object", - "properties": [ - { "name": "scriptId", "$ref": "ScriptId", "description": "Script identifier as reported in the <code>Debugger.scriptParsed</code>." }, - { "name": "lineNumber", "type": "integer", "description": "Line number in the script (0-based)." }, - { "name": "columnNumber", "type": "integer", "optional": true, "description": "Column number in the script (0-based)." } - ], - "description": "Location in the source code." - }, - { - "id": "FunctionDetails", - "hidden": true, - "type": "object", - "properties": [ - { "name": "location", "$ref": "Location", "description": "Location of the function." }, - { "name": "name", "type": "string", "optional": true, "description": "Name of the function. Not present for anonymous functions." }, - { "name": "displayName", "type": "string", "optional": true, "description": "Display name of the function(specified in 'displayName' property on the function object)." }, - { "name": "inferredName", "type": "string", "optional": true, "description": "Name of the function inferred from its initial assignment." }, - { "name": "scopeChain", "type": "array", "optional": true, "items": { "$ref": "Scope" }, "description": "Scope chain for this closure." } - ], - "description": "Information about the function." - }, - { - "id": "CallFrame", - "type": "object", - "properties": [ - { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier. This identifier is only valid while the virtual machine is paused." }, - { "name": "functionName", "type": "string", "description": "Name of the JavaScript function called on this call frame." }, - { "name": "location", "$ref": "Location", "description": "Location in the source code." }, - { "name": "scopeChain", "type": "array", "items": { "$ref": "Scope" }, "description": "Scope chain for this call frame." }, - { "name": "this", "$ref": "Runtime.RemoteObject", "description": "<code>this</code> object for this call frame." } - ], - "description": "JavaScript call frame. Array of call frames form the call stack." - }, - { - "id": "Scope", - "type": "object", - "properties": [ - { "name": "type", "type": "string", "enum": ["global", "local", "with", "closure", "catch"], "description": "Scope type." }, - { "name": "object", "$ref": "Runtime.RemoteObject", "description": "Object representing the scope. For <code>global</code> and <code>with</code> scopes it represents the actual object; for the rest of the scopes, it is artificial transient object enumerating scope variables as its properties." } - ], - "description": "Scope description." - }, - { - "id": "SetScriptSourceError", - "type": "object", - "properties": [ - { "name": "compileError", "optional": true, "type": "object", "properties": - [ - { "name": "message", "type": "string", "description": "Compiler error message" }, - { "name": "lineNumber", "type": "integer", "description": "Compile error line number (1-based)" }, - { "name": "columnNumber", "type": "integer", "description": "Compile error column number (1-based)" } - ] - } - ], - "description": "Error data for setScriptSource command. compileError is a case type for uncompilable script source error.", - "hidden": true - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables debugger for the given page. Clients should not assume that the debugging has been enabled until the result for this command is received." - }, - { - "name": "disable", - "description": "Disables debugger for given page." - }, - { - "name": "setBreakpointsActive", - "parameters": [ - { "name": "active", "type": "boolean", "description": "New value for breakpoints active state." } - ], - "description": "Activates / deactivates all breakpoints on the page." - }, - { - "name": "setSkipAllPauses", - "hidden": true, - "parameters": [ - { "name": "skipped", "type": "boolean", "description": "New value for skip pauses state." }, - { "name": "untilReload", "type": "boolean", "optional": true, "description": "Whether page reload should set skipped to false." } - ], - "description": "Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc)." - }, - { - "name": "setBreakpointByUrl", - "parameters": [ - { "name": "lineNumber", "type": "integer", "description": "Line number to set breakpoint at." }, - { "name": "url", "type": "string", "optional": true, "description": "URL of the resources to set breakpoint on." }, - { "name": "urlRegex", "type": "string", "optional": true, "description": "Regex pattern for the URLs of the resources to set breakpoints on. Either <code>url</code> or <code>urlRegex</code> must be specified." }, - { "name": "columnNumber", "type": "integer", "optional": true, "description": "Offset in the line to set breakpoint at." }, - { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." }, - { "name": "isAntibreakpoint", "type": "boolean", "optional": true, "hidden": true, "description": "Creates pseudo-breakpoint that prevents debugger from pausing on exception at this location." } - ], - "returns": [ - { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, - { "name": "locations", "type": "array", "items": { "$ref": "Location"}, "description": "List of the locations this breakpoint resolved into upon addition." } - ], - "description": "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this command is issued, all existing parsed scripts will have breakpoints resolved and returned in <code>locations</code> property. Further matching script parsing will result in subsequent <code>breakpointResolved</code> events issued. This logical breakpoint will survive page reloads." - }, - { - "name": "setBreakpoint", - "parameters": [ - { "name": "location", "$ref": "Location", "description": "Location to set breakpoint in." }, - { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." } - ], - "returns": [ - { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, - { "name": "actualLocation", "$ref": "Location", "description": "Location this breakpoint resolved into." } - ], - "description": "Sets JavaScript breakpoint at a given location." - }, - { - "name": "removeBreakpoint", - "parameters": [ - { "name": "breakpointId", "$ref": "BreakpointId" } - ], - "description": "Removes JavaScript breakpoint." - }, - { - "name": "continueToLocation", - "parameters": [ - { "name": "location", "$ref": "Location", "description": "Location to continue to." }, - { "name": "interstatementLocation", "type": "boolean", "optional": true, "hidden": true, "description": "Allows breakpoints at the intemediate positions inside statements." } - ], - "description": "Continues execution until specific location is reached." - }, - { - "name": "stepOver", - "description": "Steps over the statement." - }, - { - "name": "stepInto", - "description": "Steps into the function call." - }, - { - "name": "stepOut", - "description": "Steps out of the function call." - }, - { - "name": "pause", - "description": "Stops on the next JavaScript statement." - }, - { - "name": "resume", - "description": "Resumes JavaScript execution." - }, - { - "name": "searchInContent", - "parameters": [ - { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to search in." }, - { "name": "query", "type": "string", "description": "String to search for." }, - { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." }, - { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." } - ], - "returns": [ - { "name": "result", "type": "array", "items": { "$ref": "Page.SearchMatch" }, "description": "List of search matches." } - ], - "description": "Searches for given string in script content." - }, - { - "name": "canSetScriptSource", - "returns": [ - { "name": "result", "type": "boolean", "description": "True if <code>setScriptSource</code> is supported." } - ], - "description": "Always returns true." - }, - { - "name": "setScriptSource", - "parameters": [ - { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to edit." }, - { "name": "scriptSource", "type": "string", "description": "New content of the script." }, - { "name": "preview", "type": "boolean", "optional": true, "description": " If true the change will not actually be applied. Preview mode may be used to get result description without actually modifying the code.", "hidden": true } - ], - "returns": [ - { "name": "callFrames", "type": "array", "optional": true, "items": { "$ref": "CallFrame"}, "description": "New stack trace in case editing has happened while VM was stopped." }, - { "name": "result", "type": "object", "optional": true, "description": "VM-specific description of the changes applied.", "hidden": true } - ], - "error": { - "$ref": "SetScriptSourceError" - }, - "description": "Edits JavaScript source live." - }, - { - "name": "restartFrame", - "parameters": [ - { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." } - ], - "returns": [ - { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame"}, "description": "New stack trace." }, - { "name": "result", "type": "object", "description": "VM-specific description.", "hidden": true } - ], - "hidden": true, - "description": "Restarts particular call frame from the beginning." - }, - { - "name": "getScriptSource", - "parameters": [ - { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to get source for." } - ], - "returns": [ - { "name": "scriptSource", "type": "string", "description": "Script source." } - ], - "description": "Returns source for the script with given id." - }, - { - "name": "getFunctionDetails", - "hidden": true, - "parameters": [ - { "name": "functionId", "$ref": "Runtime.RemoteObjectId", "description": "Id of the function to get location for." } - ], - "returns": [ - { "name": "details", "$ref": "FunctionDetails", "description": "Information about the function." } - ], - "description": "Returns detailed informtation on given function." - }, - { - "name": "setPauseOnExceptions", - "parameters": [ - { "name": "state", "type": "string", "enum": ["none", "uncaught", "all"], "description": "Pause on exceptions mode." } - ], - "description": "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions or no exceptions. Initial pause on exceptions state is <code>none</code>." - }, - { - "name": "evaluateOnCallFrame", - "parameters": [ - { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." }, - { "name": "expression", "type": "string", "description": "Expression to evaluate." }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "String object group name to put result into (allows rapid releasing resulting object handles using <code>releaseObjectGroup</code>)." }, - { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false.", "hidden": true }, - { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether evaluation should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true }, - { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, - { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." } - ], - "returns": [ - { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." }, - { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." } - ], - "description": "Evaluates expression on a given call frame." - }, - { - "name": "compileScript", - "hidden": true, - "parameters": [ - { "name": "expression", "type": "string", "description": "Expression to compile." }, - { "name": "sourceURL", "type": "string", "description": "Source url to be set for the script." } - ], - "returns": [ - { "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Id of the script." }, - { "name": "syntaxErrorMessage", "type": "string", "optional": true, "description": "Syntax error message if compilation failed." } - ], - "description": "Compiles expression." - }, - { - "name": "runScript", - "hidden": true, - "parameters": [ - { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to run." }, - { "name": "contextId", "$ref": "Runtime.ExecutionContextId", "optional": true, "description": "Specifies in which isolated context to perform script run. Each content script lives in an isolated context and this parameter may be used to specify one of those contexts. If the parameter is omitted or 0 the evaluation will be performed in the context of the inspected page." }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, - { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether script run should stop on exceptions and mute console. Overrides setPauseOnException state." } - ], - "returns": [ - { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Run result." }, - { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the script run." } - ], - "description": "Runs script with given id in a given context." - }, - { - "name": "setOverlayMessage", - "parameters": [ - { "name": "message", "type": "string", "optional": true, "description": "Overlay message to display when paused in debugger." } - ], - "hidden": true, - "description": "Sets overlay message." - }, - { - "name": "setVariableValue", - "parameters": [ - { "name": "scopeNumber", "type": "integer", "description": "0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch' scope types are allowed. Other scopes could be manipulated manually." }, - { "name": "variableName", "type": "string", "description": "Variable name." }, - { "name": "newValue", "$ref": "Runtime.CallArgument", "description": "New variable value." }, - { "name": "callFrameId", "$ref": "CallFrameId", "optional": true, "description": "Id of callframe that holds variable." }, - { "name": "functionObjectId", "$ref": "Runtime.RemoteObjectId", "optional": true, "description": "Object id of closure (function) that holds variable." } - ], - "hidden": true, - "description": "Changes value of variable in a callframe or a closure. Either callframe or function must be specified. Object-based scopes are not supported and must be mutated manually." - }, - { - "name": "getStepInPositions", - "parameters": [ - { "name": "callFrameId", "$ref": "CallFrameId", "description": "Id of a call frame where the current statement should be analized" } - ], - "returns": [ - { "name": "stepInPositions", "type": "array", "items": { "$ref": "Location" }, "optional": true, "description": "experimental" } - ], - "hidden": true, - "description": "Lists all positions where step-in is possible for a current statement in a specified call frame" - }, - { - "name": "getBacktrace", - "returns": [ - { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame"}, "description": "Call stack the virtual machine stopped on." } - ], - "hidden": true, - "description": "Returns call stack including variables changed since VM was paused. VM must be paused." - }, - { - "name": "skipStackFrames", - "parameters": [ - { "name": "script", "optional": true, "type": "string", "description": "Regular expression defining the scripts to ignore while stepping." } - ], - "hidden": true, - "description": "Makes backend skip steps in the sources with names matching given pattern. VM will try leave blacklisted scripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful." - } - ], - "events": [ - { - "name": "globalObjectCleared", - "description": "Called when global has been cleared and debugger client should reset its state. Happens upon navigation or reload." - }, - { - "name": "scriptParsed", - "parameters": [ - { "name": "scriptId", "$ref": "ScriptId", "description": "Identifier of the script parsed." }, - { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, - { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, - { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, - { "name": "endLine", "type": "integer", "description": "Last line of the script." }, - { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, - { "name": "isContentScript", "type": "boolean", "optional": true, "description": "Determines whether this script is a user extension script." }, - { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, - { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "hidden": true } - ], - "description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger." - }, - { - "name": "scriptFailedToParse", - "parameters": [ - { "name": "scriptId", "$ref": "ScriptId", "description": "Identifier of the script parsed." }, - { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, - { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, - { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, - { "name": "endLine", "type": "integer", "description": "Last line of the script." }, - { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, - { "name": "isContentScript", "type": "boolean", "optional": true, "description": "Determines whether this script is a user extension script." }, - { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, - { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "hidden": true } - ], - "description": "Fired when virtual machine fails to parse the script." - }, - { - "name": "breakpointResolved", - "parameters": [ - { "name": "breakpointId", "$ref": "BreakpointId", "description": "Breakpoint unique identifier." }, - { "name": "location", "$ref": "Location", "description": "Actual breakpoint location." } - ], - "description": "Fired when breakpoint is resolved to an actual script and location." - }, - { - "name": "paused", - "parameters": [ - { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." }, - { "name": "reason", "type": "string", "enum": [ "XHR", "DOM", "EventListener", "exception", "assert", "CSPViolation", "debugCommand", "other" ], "description": "Pause reason." }, - { "name": "data", "type": "object", "optional": true, "description": "Object containing break-specific auxiliary properties." }, - { "name": "hitBreakpoints", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Hit breakpoints IDs", "hidden": true } - ], - "description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria." - }, - { - "name": "resumed", - "description": "Fired when the virtual machine resumed execution." - } - ] - }, - { - "domain": "DOMDebugger", - "description": "DOM debugging allows setting breakpoints on particular DOM operations and events. JavaScript execution will stop on these operations as if there was a regular breakpoint set.", - "types": [ - { - "id": "DOMBreakpointType", - "type": "string", - "enum": ["subtree-modified", "attribute-modified", "node-removed"], - "description": "DOM breakpoint type." - } - ], - "commands": [ - { - "name": "setDOMBreakpoint", - "parameters": [ - { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Identifier of the node to set breakpoint on." }, - { "name": "type", "$ref": "DOMBreakpointType", "description": "Type of the operation to stop upon." } - ], - "description": "Sets breakpoint on particular operation with DOM." - }, - { - "name": "removeDOMBreakpoint", - "parameters": [ - { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Identifier of the node to remove breakpoint from." }, - { "name": "type", "$ref": "DOMBreakpointType", "description": "Type of the breakpoint to remove." } - ], - "description": "Removes DOM breakpoint that was set using <code>setDOMBreakpoint</code>." - }, - { - "name": "setEventListenerBreakpoint", - "parameters": [ - { "name": "eventName", "type": "string", "description": "DOM Event name to stop on (any DOM event will do)." } - ], - "description": "Sets breakpoint on particular DOM event." - }, - { - "name": "removeEventListenerBreakpoint", - "parameters": [ - { "name": "eventName", "type": "string", "description": "Event name." } - ], - "description": "Removes breakpoint on particular DOM event." - }, - { - "name": "setInstrumentationBreakpoint", - "parameters": [ - { "name": "eventName", "type": "string", "description": "Instrumentation name to stop on." } - ], - "description": "Sets breakpoint on particular native event.", - "hidden": true - }, - { - "name": "removeInstrumentationBreakpoint", - "parameters": [ - { "name": "eventName", "type": "string", "description": "Instrumentation name to stop on." } - ], - "description": "Removes breakpoint on particular native event.", - "hidden": true - }, - { - "name": "setXHRBreakpoint", - "parameters": [ - { "name": "url", "type": "string", "description": "Resource URL substring. All XHRs having this substring in the URL will get stopped upon." } - ], - "description": "Sets breakpoint on XMLHttpRequest." - }, - { - "name": "removeXHRBreakpoint", - "parameters": [ - { "name": "url", "type": "string", "description": "Resource URL substring." } - ], - "description": "Removes breakpoint from XMLHttpRequest." - } - ] - }, - { - "domain": "Profiler", - "hidden": true, - "types": [ - { - "id": "ProfileHeader", - "type": "object", - "description": "Profile header.", - "properties": [ - { "name": "title", "type": "string", "description": "Profile title." }, - { "name": "uid", "type": "integer", "description": "Unique identifier of the profile." } - ] - }, - { - "id": "CPUProfileNode", - "type": "object", - "description": "CPU Profile node. Holds callsite information, execution statistics and child nodes.", - "properties": [ - { "name": "functionName", "type": "string", "description": "Function name." }, - { "name": "scriptId", "$ref": "Debugger.ScriptId", "description": "Script identifier." }, - { "name": "url", "type": "string", "description": "URL." }, - { "name": "lineNumber", "type": "integer", "description": "Line number." }, - { "name": "hitCount", "type": "integer", "description": "Number of samples where this node was on top of the call stack." }, - { "name": "callUID", "type": "number", "description": "Call UID." }, - { "name": "children", "type": "array", "items": { "$ref": "CPUProfileNode" }, "description": "Child nodes." }, - { "name": "deoptReason", "type": "string", "description": "The reason of being not optimized. The function may be deoptimized or marked as don't optimize."}, - { "name": "id", "optional": true, "type": "integer", "description": "Unique id of the node." } - ] - }, - { - "id": "CPUProfile", - "type": "object", - "description": "Profile.", - "properties": [ - { "name": "head", "$ref": "CPUProfileNode" }, - { "name": "startTime", "type": "number", "description": "Profiling start time in seconds." }, - { "name": "endTime", "type": "number", "description": "Profiling end time in seconds." }, - { "name": "samples", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Ids of samples top nodes." } - ] - }, - { - "id": "HeapSnapshotObjectId", - "type": "string", - "description": "Heap snashot object id." - } - ], - "commands": [ - { - "name": "enable" - }, - { - "name": "disable" - }, - { - "name": "start" - }, - { - "name": "stop", - "returns": [ - { "name": "header", "$ref": "ProfileHeader", "description": "The header of the recorded profile."} - ] - }, - { - "name": "getProfileHeaders", - "returns": [ - { "name": "headers", "type": "array", "items": { "$ref": "ProfileHeader"} } - ] - }, - { - "name": "getCPUProfile", - "parameters": [ - { "name": "uid", "type": "integer" } - ], - "returns": [ - { "name": "profile", "$ref": "CPUProfile" } - ] - }, - { - "name": "removeProfile", - "parameters": [ - { "name": "type", "type": "string" }, - { "name": "uid", "type": "integer" } - ] - }, - { - "name": "clearProfiles" - } - ], - "events": [ - { - "name": "addProfileHeader", - "parameters": [ - { "name": "header", "$ref": "ProfileHeader" } - ] - }, - { - "name": "setRecordingProfile", - "parameters": [ - { "name": "isProfiling", "type": "boolean" } - ] - }, - { - "name": "resetProfiles" - } - ] - }, - { - "domain": "HeapProfiler", - "hidden": true, - "types": [ - { - "id": "ProfileHeader", - "type": "object", - "description": "Profile header.", - "properties": [ - { "name": "title", "type": "string", "description": "Profile title." }, - { "name": "uid", "type": "integer", "description": "Unique identifier of the profile." }, - { "name": "maxJSObjectId", "type": "integer", "optional": true, "description": "Last seen JS object Id." } - ] - }, - { - "id": "HeapSnapshotObjectId", - "type": "string", - "description": "Heap snashot object id." - } - ], - "commands": [ - { - "name": "getProfileHeaders", - "returns": [ - { "name": "headers", "type": "array", "items": { "$ref": "ProfileHeader"} } - ] - }, - { - "name": "startTrackingHeapObjects" - }, - { - "name": "stopTrackingHeapObjects" - }, - { - "name": "getHeapSnapshot", - "parameters": [ - { "name": "uid", "type": "integer" } - ] - }, - { - "name": "removeProfile", - "parameters": [ - { "name": "uid", "type": "integer" } - ] - }, - { - "name": "clearProfiles" - }, - { - "name": "takeHeapSnapshot", - "parameters": [ - { "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken." } - ] - }, - { - "name": "collectGarbage" - }, - { - "name": "getObjectByHeapObjectId", - "parameters": [ - { "name": "objectId", "$ref": "HeapSnapshotObjectId" }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." } - ], - "returns": [ - { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Evaluation result." } - ] - }, - { - "name": "getHeapObjectId", - "parameters": [ - { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Identifier of the object to get heap object id for." } - ], - "returns": [ - { "name": "heapSnapshotObjectId", "$ref": "HeapSnapshotObjectId", "description": "Id of the heap snapshot object corresponding to the passed remote object id." } - ] - } - ], - "events": [ - { - "name": "addProfileHeader", - "parameters": [ - { "name": "header", "$ref": "ProfileHeader" } - ] - }, - { - "name": "addHeapSnapshotChunk", - "parameters": [ - { "name": "uid", "type": "integer" }, - { "name": "chunk", "type": "string" } - ] - }, - { - "name": "finishHeapSnapshot", - "parameters": [ - { "name": "uid", "type": "integer" } - ] - }, - { - "name": "resetProfiles" - }, - { - "name": "reportHeapSnapshotProgress", - "parameters": [ - { "name": "done", "type": "integer" }, - { "name": "total", "type": "integer" } - ] - }, - { - "name": "lastSeenObjectId", - "description": "If heap objects tracking has been started then backend regulary sends a current value for last seen object id and corresponding timestamp. If the were changes in the heap since last event then one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event.", - "parameters": [ - { "name": "lastSeenObjectId", "type": "integer" }, - { "name": "timestamp", "type": "number" } - ] - }, - { - "name": "heapStatsUpdate", - "description": "If heap objects tracking has been started then backend may send update for one or more fragments", - "parameters": [ - { "name": "statsUpdate", "type": "array", "items": { "type": "integer" }, "description": "An array of triplets. Each triplet describes a fragment. The first integer is the fragment index, the second integer is a total count of objects for the fragment, the third integer is a total size of the objects for the fragment."} - ] - } - ] - }, - { - "domain": "Worker", - "hidden": true, - "types": [], - "commands": [ - { - "name": "enable" - }, - { - "name": "disable" - }, - { - "name": "sendMessageToWorker", - "parameters": [ - { "name": "workerId", "type": "integer" }, - { "name": "message", "type": "object" } - ] - }, - { - "name": "canInspectWorkers", - "description": "Tells whether browser supports workers inspection.", - "returns": [ - { "name": "result", "type": "boolean", "description": "True if browser has workers support." } - ] - }, - { - "name": "connectToWorker", - "parameters": [ - { "name": "workerId", "type": "integer" } - ] - }, - { - "name": "disconnectFromWorker", - "parameters": [ - { "name": "workerId", "type": "integer" } - ] - }, - { - "name": "setAutoconnectToWorkers", - "parameters": [ - { "name": "value", "type": "boolean" } - ] - } - ], - "events": [ - { - "name": "workerCreated", - "parameters": [ - { "name": "workerId", "type": "integer" }, - { "name": "url", "type": "string" }, - { "name": "inspectorConnected", "type": "boolean" } - ] - }, - { - "name": "workerTerminated", - "parameters": [ - { "name": "workerId", "type": "integer" } - ] - }, - { - "name": "dispatchMessageFromWorker", - "parameters": [ - { "name": "workerId", "type": "integer" }, - { "name": "message", "type": "object" } - ] - }, - { - "name": "disconnectedFromWorker" - } - ] - }, - { - "domain": "Canvas", - "hidden": true, - "types": [ - { - "id": "ResourceId", - "type": "string", - "description": "Unique resource identifier." - }, - { - "id": "ResourceStateDescriptor", - "type": "object", - "description": "Resource state descriptor.", - "properties": [ - { "name": "name", "type": "string", "description": "State name." }, - { "name": "enumValueForName", "type": "string", "optional": true, "description": "String representation of the enum value, if <code>name</code> stands for an enum." }, - { "name": "value", "$ref": "CallArgument", "optional": true, "description": "The value associated with the particular state." }, - { "name": "values", "type": "array", "items": { "$ref": "ResourceStateDescriptor" }, "optional": true, "description": "Array of values associated with the particular state. Either <code>value</code> or <code>values</code> will be specified." }, - { "name": "isArray", "type": "boolean", "optional": true, "description": "True iff the given <code>values</code> items stand for an array rather than a list of grouped states." } - ] - }, - { - "id": "ResourceState", - "type": "object", - "description": "Resource state.", - "properties": [ - { "name": "id", "$ref": "ResourceId" }, - { "name": "traceLogId", "$ref": "TraceLogId" }, - { "name": "descriptors", "type": "array", "items": { "$ref": "ResourceStateDescriptor" }, "optional": true, "description": "Describes current <code>Resource</code> state." }, - { "name": "imageURL", "type": "string", "optional": true, "description": "Screenshot image data URL." } - ] - }, - { - "id": "CallArgument", - "type": "object", - "properties": [ - { "name": "description", "type": "string", "description": "String representation of the object." }, - { "name": "enumName", "type": "string", "optional": true, "description": "Enum name, if any, that stands for the value (for example, a WebGL enum name)." }, - { "name": "resourceId", "$ref": "ResourceId", "optional": true, "description": "Resource identifier. Specified for <code>Resource</code> objects only." }, - { "name": "type", "type": "string", "optional": true, "enum": ["object", "function", "undefined", "string", "number", "boolean"], "description": "Object type. Specified for non <code>Resource</code> objects only." }, - { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date"], "description": "Object subtype hint. Specified for <code>object</code> type values only." }, - { "name": "remoteObject", "$ref": "Runtime.RemoteObject", "optional": true, "description": "The <code>RemoteObject</code>, if requested." } - ] - }, - { - "id": "Call", - "type": "object", - "properties": [ - { "name": "contextId", "$ref": "ResourceId" }, - { "name": "functionName", "type": "string", "optional": true }, - { "name": "arguments", "type": "array", "items": { "$ref": "CallArgument" }, "optional": true }, - { "name": "result", "$ref": "CallArgument", "optional": true }, - { "name": "isDrawingCall", "type": "boolean", "optional": true }, - { "name": "isFrameEndCall", "type": "boolean", "optional": true }, - { "name": "property", "type": "string", "optional": true }, - { "name": "value", "$ref": "CallArgument", "optional": true }, - { "name": "sourceURL", "type": "string", "optional": true }, - { "name": "lineNumber", "type": "integer", "optional": true }, - { "name": "columnNumber", "type": "integer", "optional": true } - ] - }, - { - "id": "TraceLogId", - "type": "string", - "description": "Unique trace log identifier." - }, - { - "id": "TraceLog", - "type": "object", - "properties": [ - { "name": "id", "$ref": "TraceLogId" }, - { "name": "calls", "type": "array", "items": { "$ref": "Call" } }, - { "name": "contexts", "type": "array", "items": { "$ref": "CallArgument" } }, - { "name": "startOffset", "type": "integer" }, - { "name": "alive", "type": "boolean" }, - { "name": "totalAvailableCalls", "type": "number" } - ] - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables Canvas inspection." - }, - { - "name": "disable", - "description": "Disables Canvas inspection." - }, - { - "name": "dropTraceLog", - "parameters": [ - { "name": "traceLogId", "$ref": "TraceLogId" } - ] - }, - { - "name": "hasUninstrumentedCanvases", - "returns": [ - { "name": "result", "type": "boolean" } - ], - "description": "Checks if there is any uninstrumented canvas in the inspected page." - }, - { - "name": "captureFrame", - "parameters": [ - { "name": "frameId", "$ref": "Page.FrameId", "optional": true, "description": "Identifier of the frame containing document whose canvases are to be captured. If omitted, main frame is assumed." } - ], - "returns": [ - { "name": "traceLogId", "$ref": "TraceLogId", "description": "Identifier of the trace log containing captured canvas calls." } - ], - "description": "Starts (or continues) a canvas frame capturing which will be stopped automatically after the next frame is prepared." - }, - { - "name": "startCapturing", - "parameters": [ - { "name": "frameId", "$ref": "Page.FrameId", "optional": true, "description": "Identifier of the frame containing document whose canvases are to be captured. If omitted, main frame is assumed." } - ], - "returns": [ - { "name": "traceLogId", "$ref": "TraceLogId", "description": "Identifier of the trace log containing captured canvas calls." } - ], - "description": "Starts (or continues) consecutive canvas frames capturing. The capturing is stopped by the corresponding stopCapturing command." - }, - { - "name": "stopCapturing", - "parameters": [ - { "name": "traceLogId", "$ref": "TraceLogId" } - ] - }, - { - "name": "getTraceLog", - "parameters": [ - { "name": "traceLogId", "$ref": "TraceLogId" }, - { "name": "startOffset", "type": "integer", "optional": true }, - { "name": "maxLength", "type": "integer", "optional": true } - ], - "returns": [ - { "name": "traceLog", "$ref": "TraceLog" } - ] - }, - { - "name": "replayTraceLog", - "parameters": [ - { "name": "traceLogId", "$ref": "TraceLogId" }, - { "name": "stepNo", "type": "integer", "description": "Last call index in the trace log to replay (zero based)." } - ], - "returns": [ - { "name": "resourceState", "$ref": "ResourceState" }, - { "name": "replayTime", "type": "number", "description": "Replay time (in milliseconds)." } - ] - }, - { - "name": "getResourceState", - "parameters": [ - { "name": "traceLogId", "$ref": "TraceLogId" }, - { "name": "resourceId", "$ref": "ResourceId" } - ], - "returns": [ - { "name": "resourceState", "$ref": "ResourceState" } - ] - }, - { - "name": "evaluateTraceLogCallArgument", - "parameters": [ - { "name": "traceLogId", "$ref": "TraceLogId" }, - { "name": "callIndex", "type": "integer", "description": "Index of the call to evaluate on (zero based)." }, - { "name": "argumentIndex", "type": "integer", "description": "Index of the argument to evaluate (zero based). Provide <code>-1</code> to evaluate call result." }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "String object group name to put result into (allows rapid releasing resulting object handles using <code>Runtime.releaseObjectGroup</code>)." } - ], - "returns": [ - { "name": "result", "$ref": "Runtime.RemoteObject", "optional": true, "description": "Object wrapper for the evaluation result." }, - { "name": "resourceState", "$ref": "ResourceState", "optional": true, "description": "State of the <code>Resource</code> object." } - ], - "description": "Evaluates a given trace call argument or its result." - } - ], - "events": [ - { - "name": "contextCreated", - "parameters": [ - { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame containing a canvas with a context." } - ], - "description": "Fired when a canvas context has been created in the given frame. The context may not be instrumented (see hasUninstrumentedCanvases command)." - }, - { - "name": "traceLogsRemoved", - "parameters": [ - { "name": "frameId", "$ref": "Page.FrameId", "optional": true, "description": "If given, trace logs from the given frame were removed." }, - { "name": "traceLogId", "$ref": "TraceLogId", "optional": true, "description": "If given, trace log with the given ID was removed." } - ], - "description": "Fired when a set of trace logs were removed from the backend. If no parameters are given, all trace logs were removed." - } - ] - }, - { - "domain": "Input", - "types": [ - { - "id": "TouchPoint", - "type": "object", - "hidden": true, - "properties": [ - { "name": "state", "type": "string", "enum": ["touchPressed", "touchReleased", "touchMoved", "touchStationary", "touchCancelled"], "description": "State of the touch point." }, - { "name": "x", "type": "integer", "description": "X coordinate of the event relative to the main frame's viewport."}, - { "name": "y", "type": "integer", "description": "Y coordinate of the event relative to the main frame's viewport. 0 refers to the top of the viewport and Y increases as it proceeds towards the bottom of the viewport."}, - { "name": "radiusX", "type": "integer", "optional": true, "description": "X radius of the touch area (default: 1)."}, - { "name": "radiusY", "type": "integer", "optional": true, "description": "Y radius of the touch area (default: 1)."}, - { "name": "rotationAngle", "type": "number", "optional": true, "description": "Rotation angle (default: 0.0)."}, - { "name": "force", "type": "number", "optional": true, "description": "Force (default: 1.0)."}, - { "name": "id", "type": "number", "optional": true, "description": "Identifier used to track touch sources between events, must be unique within an event."} - ] - } - ], - "commands": [ - { - "name": "dispatchKeyEvent", - "parameters": [ - { "name": "type", "type": "string", "enum": ["keyDown", "keyUp", "rawKeyDown", "char"], "description": "Type of the key event." }, - { "name": "modifiers", "type": "integer", "optional": true, "description": "Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 (default: 0)." }, - { "name": "timestamp", "type": "number", "optional": true, "description": "Time at which the event occurred. Measured in UTC time in seconds since January 1, 1970 (default: current time)." }, - { "name": "text", "type": "string", "optional": true, "description": "Text as generated by processing a virtual key code with a keyboard layout. Not needed for for <code>keyUp</code> and <code>rawKeyDown</code> events (default: \"\")" }, - { "name": "unmodifiedText", "type": "string", "optional": true, "description": "Text that would have been generated by the keyboard if no modifiers were pressed (except for shift). Useful for shortcut (accelerator) key handling (default: \"\")." }, - { "name": "keyIdentifier", "type": "string", "optional": true, "description": "Unique key identifier (e.g., 'U+0041') (default: \"\")." }, - { "name": "windowsVirtualKeyCode", "type": "integer", "optional": true, "description": "Windows virtual key code (default: 0)." }, - { "name": "nativeVirtualKeyCode", "type": "integer", "optional": true, "description": "Native virtual key code (default: 0)." }, - { "name": "autoRepeat", "type": "boolean", "optional": true, "description": "Whether the event was generated from auto repeat (default: false)." }, - { "name": "isKeypad", "type": "boolean", "optional": true, "description": "Whether the event was generated from the keypad (default: false)." }, - { "name": "isSystemKey", "type": "boolean", "optional": true, "description": "Whether the event was a system key event (default: false)." } - ], - "description": "Dispatches a key event to the page." - }, - { - "name": "dispatchMouseEvent", - "parameters": [ - { "name": "type", "type": "string", "enum": ["mousePressed", "mouseReleased", "mouseMoved"], "description": "Type of the mouse event." }, - { "name": "x", "type": "integer", "description": "X coordinate of the event relative to the main frame's viewport."}, - { "name": "y", "type": "integer", "description": "Y coordinate of the event relative to the main frame's viewport. 0 refers to the top of the viewport and Y increases as it proceeds towards the bottom of the viewport."}, - { "name": "modifiers", "type": "integer", "optional": true, "description": "Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 (default: 0)." }, - { "name": "timestamp", "type": "number", "optional": true, "description": "Time at which the event occurred. Measured in UTC time in seconds since January 1, 1970 (default: current time)." }, - { "name": "button", "type": "string", "enum": ["none", "left", "middle", "right"], "optional": true, "description": "Mouse button (default: \"none\")." }, - { "name": "clickCount", "type": "integer", "optional": true, "description": "Number of times the mouse button was clicked (default: 0)." }, - { "name": "deviceSpace", "type": "boolean", "optional": true, "hidden": true, "description": "If true, x and y are given in dip wrt current viewport." } - ], - "description": "Dispatches a mouse event to the page." - }, - { - "name": "dispatchTouchEvent", - "hidden": true, - "parameters": [ - { "name": "type", "type": "string", "enum": ["touchStart", "touchEnd", "touchMove"], "description": "Type of the touch event." }, - { "name": "touchPoints", "type": "array", "items": { "$ref": "TouchPoint" }, "description": "Touch points." }, - { "name": "modifiers", "type": "integer", "optional": true, "description": "Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 (default: 0)." }, - { "name": "timestamp", "type": "number", "optional": true, "description": "Time at which the event occurred. Measured in UTC time in seconds since January 1, 1970 (default: current time)." } - ], - "description": "Dispatches a touch event to the page." - }, - { - "name": "dispatchGestureEvent", - "hidden": true, - "parameters": [ - { "name": "type", "type": "string", "enum": ["scrollBegin", "scrollEnd", "scrollUpdate", "tapDown", "tap", "pinchBegin", "pinchEnd", "pinchUpdate"], "description": "Type of the gesture event." }, - { "name": "x", "type": "integer", "description": "X coordinate relative to the screen's viewport."}, - { "name": "y", "type": "integer", "description": "Y coordinate relative to the screen's viewport."}, - { "name": "timestamp", "type": "number", "optional": true, "description": "Time at which the event occurred. Measured in UTC time in seconds since January 1, 1970 (default: current time)." }, - { "name": "deltaX", "type": "integer", "optional": true, "description": "Delta X where apllies."}, - { "name": "deltaY", "type": "integer", "optional": true, "description": "Delta Y where apllies."}, - { "name": "pinchScale", "type": "number", "optional": true, "description": "Pinch scale." } - ], - "description": "Dispatches a gesture event to the page." - } - ], - "events": [] - }, - { - "domain": "LayerTree", - "hidden": true, - "types": [ - { - "id": "LayerId", - "type": "string", - "description": "Unique Layer identifier." - }, - { - "id": "Layer", - "type": "object", - "description": "Information about a compositing layer.", - "properties": [ - { "name": "layerId", "$ref": "LayerId", "description": "The unique id for this layer." }, - { "name": "parentLayerId", "$ref": "LayerId", "optional": true, "description": "The id of parent (not present for root)." }, - { "name": "nodeId", "$ref": "DOM.NodeId", "optional": true, "description": "The id for the node associated with this layer." }, - { "name": "offsetX", "type": "number", "description": "Offset from parent layer, X coordinate." }, - { "name": "offsetY", "type": "number", "description": "Offset from parent layer, X coordinate." }, - { "name": "width", "type": "number", "description": "Layer width." }, - { "name": "height", "type": "number", "description": "Layer height." }, - { "name": "transform", "type": "array", "items": { "type": "number" }, "minItems": 16, "maxItems": 16, "optional": true, "description": "Transformation matrix for layer, default is identity matrix" }, - { "name": "anchorX", "type": "number", "optional": true, "description": "Transform anchor point X, absent if no transform specified" }, - { "name": "anchorY", "type": "number", "optional": true, "description": "Transform anchor point Y, absent if no transform specified" }, - { "name": "anchorZ", "type": "number", "optional": true, "description": "Transform anchor point Z, absent if no transform specified" }, - { "name": "paintCount", "type": "integer", "description": "Indicates how many time this layer has painted." }, - { "name": "invisible", "type": "boolean", "optional": true, "description": "Set if layer is not visible." } - ] - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables compositing tree inspection." - }, - { - "name": "disable", - "description": "Disables compositing tree inspection." - }, - { - "name": "getLayers", - "parameters": [ - { "name": "nodeId", "optional": true, "$ref": "DOM.NodeId", "description": "Root of the subtree for which we want to gather layers (return entire tree if not specified)" } - ], - "description": "Returns the layer tree structure of the current page.", - "returns": [ - { "name": "layers", "type": "array", "items": { "$ref": "Layer" }, "description": "Child layers." } - ] - }, - { - "name": "compositingReasons", - "parameters": [ - { "name": "layerId", "$ref": "LayerId", "description": "The id of the layer for which we want to get the reasons it was composited." } - ], - "description": "Provides the reasons why the given layer was composited.", - "returns": [ - { "name": "compositingReasons", "type": "array", "items": { "type": "string" }, "description": "A list of strings specifying reasons for the given layer to become composited." } - ] - } - ], - "events": [ - { - "name": "layerTreeDidChange" - } - ] - }, - { - "domain": "Tracing", - "hidden": true, - "commands": [ - { - "name": "start", - "description": "Strart trace events collection.", - "parameters": [ - { "name": "categories", "type": "string", "description": "Category/tag filter" } - ] - }, - { - "name": "end", - "description": "Stop trace events collection." - } - ], - "events": [ - { - "name": "dataCollected", - "parameters": [ - { "name": "value", "type": "array", "items": { "type": "object" } } - ] - }, - { - "name": "tracingComplete" - } - ] - }] -} diff --git a/deps/v8_inspector/devtools/protocol.json b/deps/v8_inspector/devtools/protocol.json deleted file mode 100644 index 213274bd7c8adc..00000000000000 --- a/deps/v8_inspector/devtools/protocol.json +++ /dev/null @@ -1,5167 +0,0 @@ -{ - "version": { "major": "1", "minor": "1" }, - "domains": [{ - "domain": "Inspector", - "hidden": true, - "types": [], - "commands": [ - { - "name": "enable", - "description": "Enables inspector domain notifications.", - "handlers": ["browser"] - }, - { - "name": "disable", - "description": "Disables inspector domain notifications.", - "handlers": ["browser"] - } - ], - "events": [ - { - "name": "detached", - "description": "Fired when remote debugging connection is about to be terminated. Contains detach reason.", - "parameters": [ - { "name": "reason", "type": "string", "description": "The reason why connection has been terminated." } - ], - "handlers": ["browser"] - }, - { - "name": "targetCrashed", - "description": "Fired when debugging target has crashed", - "handlers": ["browser"] - } - ] - }, - { - "domain": "Memory", - "hidden": true, - "types": [ - { - "id": "PressureLevel", - "type": "string", - "enum": ["moderate", "critical"], - "description": "Memory pressure level." - } - ], - "commands": [ - { - "name": "getDOMCounters", - "returns": [ - { "name": "documents", "type": "integer" }, - { "name": "nodes", "type": "integer" }, - { "name": "jsEventListeners", "type": "integer" } - ] - }, - { - "name": "setPressureNotificationsSuppressed", - "description": "Enable/disable suppressing memory pressure notifications in all processes.", - "parameters": [ - { "name": "suppressed", "type": "boolean", "description": "If true, memory pressure notifications will be suppressed."} - ], - "handlers": ["browser"] - }, - { - "name": "simulatePressureNotification", - "description": "Simulate a memory pressure notification in all processes.", - "parameters": [ - { "name": "level", "$ref": "PressureLevel", "description": "Memory pressure level of the notification." } - ], - "handlers": ["browser"] - } - ] - }, - { - "domain": "Page", - "description": "Actions and events related to the inspected page belong to the page domain.", - "types": [ - { - "id": "ResourceType", - "type": "string", - "enum": ["Document", "Stylesheet", "Image", "Media", "Font", "Script", "TextTrack", "XHR", "Fetch", "EventSource", "WebSocket", "Manifest", "Other"], - "description": "Resource type as it was perceived by the rendering engine." - }, - { - "id": "FrameId", - "type": "string", - "description": "Unique frame identifier." - }, - { - "id": "Frame", - "type": "object", - "description": "Information about the Frame on the page.", - "properties": [ - { "name": "id", "type": "string", "description": "Frame unique identifier." }, - { "name": "parentId", "type": "string", "optional": true, "description": "Parent frame identifier." }, - { "name": "loaderId", "$ref": "Network.LoaderId", "description": "Identifier of the loader associated with this frame." }, - { "name": "name", "type": "string", "optional": true, "description": "Frame's name as specified in the tag." }, - { "name": "url", "type": "string", "description": "Frame document's URL." }, - { "name": "securityOrigin", "type": "string", "description": "Frame document's security origin." }, - { "name": "mimeType", "type": "string", "description": "Frame document's mimeType as determined by the browser." } - ] - }, - { - "id": "FrameResource", - "type": "object", - "description": "Information about the Resource on the page.", - "properties": [ - { "name": "url", "type": "string", "description": "Resource URL." }, - { "name": "type", "$ref": "ResourceType", "description": "Type of this resource." }, - { "name": "mimeType", "type": "string", "description": "Resource mimeType as determined by the browser." }, - { "name": "failed", "type": "boolean", "optional": true, "description": "True if the resource failed to load." }, - { "name": "canceled", "type": "boolean", "optional": true, "description": "True if the resource was canceled during loading." } - ], - "hidden": true - }, - { - "id": "FrameResourceTree", - "type": "object", - "description": "Information about the Frame hierarchy along with their cached resources.", - "properties": [ - { "name": "frame", "$ref": "Frame", "description": "Frame information for this tree item." }, - { "name": "childFrames", "type": "array", "optional": true, "items": { "$ref": "FrameResourceTree" }, "description": "Child frames." }, - { "name": "resources", "type": "array", "items": { "$ref": "FrameResource" }, "description": "Information about frame resources." } - ], - "hidden": true - }, - { - "id": "ScriptIdentifier", - "type": "string", - "description": "Unique script identifier.", - "hidden": true - }, - { - "id": "NavigationEntry", - "type": "object", - "description": "Navigation history entry.", - "properties": [ - { "name": "id", "type": "integer", "description": "Unique id of the navigation history entry." }, - { "name": "url", "type": "string", "description": "URL of the navigation history entry." }, - { "name": "title", "type": "string", "description": "Title of the navigation history entry." } - ], - "hidden": true - }, - { - "id": "ScreencastFrameMetadata", - "type": "object", - "description": "Screencast frame metadata.", - "properties": [ - { "name": "offsetTop", "type": "number", "hidden": true, "description": "Top offset in DIP." }, - { "name": "pageScaleFactor", "type": "number", "hidden": true, "description": "Page scale factor." }, - { "name": "deviceWidth", "type": "number", "hidden": true, "description": "Device screen width in DIP." }, - { "name": "deviceHeight", "type": "number", "hidden": true, "description": "Device screen height in DIP." }, - { "name": "scrollOffsetX", "type": "number", "hidden": true, "description": "Position of horizontal scroll in CSS pixels." }, - { "name": "scrollOffsetY", "type": "number", "hidden": true, "description": "Position of vertical scroll in CSS pixels." }, - { "name": "timestamp", "type": "number", "optional": true, "hidden": true, "description": "Frame swap timestamp." } - ], - "hidden": true - }, - { - "id": "DialogType", - "description": "Javascript dialog type.", - "type": "string", - "enum": ["alert", "confirm", "prompt", "beforeunload"], - "hidden": true - }, - { - "id": "AppManifestError", - "description": "Error while paring app manifest.", - "type": "object", - "properties": [ - { "name": "message", "type": "string", "description": "Error message." }, - { "name": "critical", "type": "integer", "description": "If criticial, this is a non-recoverable parse error." }, - { "name": "line", "type": "integer", "description": "Error line." }, - { "name": "column", "type": "integer", "description": "Error column." } - ], - "hidden": true - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables page domain notifications.", - "handlers": ["browser", "renderer"] - }, - { - "name": "disable", - "description": "Disables page domain notifications.", - "handlers": ["browser", "renderer"] - }, - { - "name": "addScriptToEvaluateOnLoad", - "parameters": [ - { "name": "scriptSource", "type": "string" } - ], - "returns": [ - { "name": "identifier", "$ref": "ScriptIdentifier", "description": "Identifier of the added script." } - ], - "hidden": true - }, - { - "name": "removeScriptToEvaluateOnLoad", - "parameters": [ - { "name": "identifier", "$ref": "ScriptIdentifier" } - ], - "hidden": true - }, - { - "name": "setAutoAttachToCreatedPages", - "parameters": [ - { "name": "autoAttach", "type": "boolean", "description": "If true, browser will open a new inspector window for every page created from this one." } - ], - "description": "Controls whether browser will open a new inspector window for connected pages.", - "hidden": true - }, - { - "name": "reload", - "parameters": [ - { "name": "ignoreCache", "type": "boolean", "optional": true, "description": "If true, browser cache is ignored (as if the user pressed Shift+refresh)." }, - { "name": "scriptToEvaluateOnLoad", "type": "string", "optional": true, "description": "If set, the script will be injected into all frames of the inspected page after reload." } - ], - "description": "Reloads given page optionally ignoring the cache.", - "handlers": ["browser", "renderer"] - }, - { - "name": "navigate", - "parameters": [ - { "name": "url", "type": "string", "description": "URL to navigate the page to." } - ], - "returns": [ - { "name": "frameId", "$ref": "FrameId", "hidden": true, "description": "Frame id that will be navigated." } - ], - "description": "Navigates current page to the given URL.", - "handlers": ["browser", "renderer"] - }, - { - "name": "getNavigationHistory", - "returns": [ - { "name": "currentIndex", "type": "integer", "description": "Index of the current navigation history entry." }, - { "name": "entries", "type": "array", "items": { "$ref": "NavigationEntry" }, "description": "Array of navigation history entries." } - ], - "description": "Returns navigation history for the current page.", - "hidden": true, - "handlers": ["browser"] - }, - { - "name": "navigateToHistoryEntry", - "parameters": [ - { "name": "entryId", "type": "integer", "description": "Unique id of the entry to navigate to." } - ], - "description": "Navigates current page to the given history entry.", - "hidden": true, - "handlers": ["browser"] - }, - { - "name": "getCookies", - "returns": [ - { "name": "cookies", "type": "array", "items": { "$ref": "Network.Cookie" }, "description": "Array of cookie objects." } - ], - "description": "Returns all browser cookies. Depending on the backend support, will return detailed cookie information in the <code>cookies</code> field.", - "handlers": ["browser"], - "async": true, - "hidden": true, - "redirect": "Network" - }, - { - "name": "deleteCookie", - "parameters": [ - { "name": "cookieName", "type": "string", "description": "Name of the cookie to remove." }, - { "name": "url", "type": "string", "description": "URL to match cooke domain and path." } - ], - "description": "Deletes browser cookie with given name, domain and path.", - "handlers": ["browser"], - "async": true, - "hidden": true, - "redirect": "Network" - }, - { - "name": "getResourceTree", - "description": "Returns present frame / resource tree structure.", - "returns": [ - { "name": "frameTree", "$ref": "FrameResourceTree", "description": "Present frame / resource tree structure." } - ], - "hidden": true - }, - { - "name": "getResourceContent", - "async": true, - "description": "Returns content of the given resource.", - "parameters": [ - { "name": "frameId", "$ref": "FrameId", "description": "Frame id to get resource for." }, - { "name": "url", "type": "string", "description": "URL of the resource to get content for." } - ], - "returns": [ - { "name": "content", "type": "string", "description": "Resource content." }, - { "name": "base64Encoded", "type": "boolean", "description": "True, if content was served as base64." } - ], - "hidden": true - }, - { - "name": "searchInResource", - "async": true, - "description": "Searches for given string in resource content.", - "parameters": [ - { "name": "frameId", "$ref": "FrameId", "description": "Frame id for resource to search in." }, - { "name": "url", "type": "string", "description": "URL of the resource to search in." }, - { "name": "query", "type": "string", "description": "String to search for." }, - { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." }, - { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." } - ], - "returns": [ - { "name": "result", "type": "array", "items": { "$ref": "Debugger.SearchMatch" }, "description": "List of search matches." } - ], - "hidden": true - }, - { - "name": "setDocumentContent", - "description": "Sets given markup as the document's HTML.", - "parameters": [ - { "name": "frameId", "$ref": "FrameId", "description": "Frame id to set HTML for." }, - { "name": "html", "type": "string", "description": "HTML content to set." } - ], - "hidden": true - }, - { - "name": "setDeviceMetricsOverride", - "description": "Overrides the values of device screen dimensions (window.screen.width, window.screen.height, window.innerWidth, window.innerHeight, and \"device-width\"/\"device-height\"-related CSS media query results).", - "parameters": [ - { "name": "width", "type": "integer", "description": "Overriding width value in pixels (minimum 0, maximum 10000000). 0 disables the override." }, - { "name": "height", "type": "integer", "description": "Overriding height value in pixels (minimum 0, maximum 10000000). 0 disables the override." }, - { "name": "deviceScaleFactor", "type": "number", "description": "Overriding device scale factor value. 0 disables the override." }, - { "name": "mobile", "type": "boolean", "description": "Whether to emulate mobile device. This includes viewport meta tag, overlay scrollbars, text autosizing and more." }, - { "name": "fitWindow", "type": "boolean", "description": "Whether a view that exceeds the available browser window area should be scaled down to fit." }, - { "name": "scale", "type": "number", "optional": true, "description": "Scale to apply to resulting view image. Ignored in |fitWindow| mode." }, - { "name": "offsetX", "type": "number", "optional": true, "description": "X offset to shift resulting view image by. Ignored in |fitWindow| mode." }, - { "name": "offsetY", "type": "number", "optional": true, "description": "Y offset to shift resulting view image by. Ignored in |fitWindow| mode." }, - { "name": "screenWidth", "type": "integer", "optional": true, "description": "Overriding screen width value in pixels (minimum 0, maximum 10000000). Only used for |mobile==true|." }, - { "name": "screenHeight", "type": "integer", "optional": true, "description": "Overriding screen height value in pixels (minimum 0, maximum 10000000). Only used for |mobile==true|." }, - { "name": "positionX", "type": "integer", "optional": true, "description": "Overriding view X position on screen in pixels (minimum 0, maximum 10000000). Only used for |mobile==true|." }, - { "name": "positionY", "type": "integer", "optional": true, "description": "Overriding view Y position on screen in pixels (minimum 0, maximum 10000000). Only used for |mobile==true|." }, - { "name": "screenOrientation", "$ref": "Emulation.ScreenOrientation", "optional": true, "description": "Screen orientation override." } - ], - "handlers": ["browser"], - "redirect": "Emulation", - "hidden": true - }, - { - "name": "clearDeviceMetricsOverride", - "description": "Clears the overriden device metrics.", - "handlers": ["browser"], - "redirect": "Emulation", - "hidden": true - }, - { - "name": "setGeolocationOverride", - "description": "Overrides the Geolocation Position or Error. Omitting any of the parameters emulates position unavailable.", - "parameters": [ - { "name": "latitude", "type": "number", "optional": true, "description": "Mock latitude"}, - { "name": "longitude", "type": "number", "optional": true, "description": "Mock longitude"}, - { "name": "accuracy", "type": "number", "optional": true, "description": "Mock accuracy"} - ], - "redirect": "Emulation", - "handlers": ["browser"] - }, - { - "name": "clearGeolocationOverride", - "description": "Clears the overriden Geolocation Position and Error.", - "redirect": "Emulation", - "handlers": ["browser"] - }, - { - "name": "setDeviceOrientationOverride", - "description": "Overrides the Device Orientation.", - "parameters": [ - { "name": "alpha", "type": "number", "description": "Mock alpha"}, - { "name": "beta", "type": "number", "description": "Mock beta"}, - { "name": "gamma", "type": "number", "description": "Mock gamma"} - ], - "redirect": "DeviceOrientation", - "hidden": true - }, - { - "name": "clearDeviceOrientationOverride", - "description": "Clears the overridden Device Orientation.", - "redirect": "DeviceOrientation", - "hidden": true - }, - { - "name": "setTouchEmulationEnabled", - "parameters": [ - { "name": "enabled", "type": "boolean", "description": "Whether the touch event emulation should be enabled." }, - { "name": "configuration", "type": "string", "enum": ["mobile", "desktop"], "optional": true, "description": "Touch/gesture events configuration. Default: current platform." } - ], - "description": "Toggles mouse event-based touch event emulation.", - "hidden": true, - "redirect": "Emulation", - "handlers": ["browser", "renderer"] - }, - { - "name": "captureScreenshot", - "async": true, - "description": "Capture page screenshot.", - "returns": [ - { "name": "data", "type": "string", "description": "Base64-encoded image data (PNG)." } - ], - "hidden": true, - "handlers": ["browser"] - }, - { - "name": "startScreencast", - "description": "Starts sending each frame using the <code>screencastFrame</code> event.", - "parameters": [ - { "name": "format", "type": "string", "optional": true, "enum": ["jpeg", "png"], "description": "Image compression format." }, - { "name": "quality", "type": "integer", "optional": true, "description": "Compression quality from range [0..100]." }, - { "name": "maxWidth", "type": "integer", "optional": true, "description": "Maximum screenshot width." }, - { "name": "maxHeight", "type": "integer", "optional": true, "description": "Maximum screenshot height." }, - { "name": "everyNthFrame", "type": "integer", "optional": true, "description": "Send every n-th frame." } - ], - "hidden": true, - "handlers": ["browser", "renderer"] - }, - { - "name": "stopScreencast", - "description": "Stops sending each frame in the <code>screencastFrame</code>.", - "hidden": true, - "handlers": ["browser", "renderer"] - }, - { - "name": "screencastFrameAck", - "description": "Acknowledges that a screencast frame has been received by the frontend.", - "parameters": [ - { "name": "sessionId", "type": "integer", "description": "Frame number." } - ], - "hidden": true, - "handlers": ["browser"] - }, - { - "name": "handleJavaScriptDialog", - "description": "Accepts or dismisses a JavaScript initiated dialog (alert, confirm, prompt, or onbeforeunload).", - "parameters": [ - { "name": "accept", "type": "boolean", "description": "Whether to accept or dismiss the dialog." }, - { "name": "promptText", "type": "string", "optional": true, "description": "The text to enter into the dialog prompt before accepting. Used only if this is a prompt dialog." } - ], - "hidden": true, - "handlers": ["browser"] - }, - { - "name": "setColorPickerEnabled", - "parameters": [ - { "name": "enabled", "type": "boolean", "description": "Shows / hides color picker" } - ], - "description": "Shows / hides color picker", - "hidden": true, - "handlers": ["browser"] - }, - { - "name": "setOverlayMessage", - "parameters": [ - { "name": "message", "type": "string", "optional": true, "description": "Overlay message to display when paused in debugger." } - ], - "hidden": true, - "description": "Sets overlay message." - }, - { - "name": "getAppManifest", - "hidden": true, - "returns": [ - { "name": "url", "type": "string", "description": "Manifest location." }, - { "name": "errors", "type": "array", "items": { "$ref": "AppManifestError" } }, - { "name": "data", "type": "string", "optional": true, "description": "Manifest content." } - ], - "handlers": ["none"] - }, - { - "name": "requestAppBanner", - "hidden": true, - "handlers": ["browser"] - }, - { - "name": "setBlockedEventsWarningThreshold", - "hidden": true, - "parameters": [ - { "name": "threshold", "type": "number", "description": "If set to a positive number, specifies threshold in seconds for input event latency that will cause a console warning about blocked event to be issued. If zero or less, the warning is disabled." } - ] - } - ], - "events": [ - { - "name": "domContentEventFired", - "parameters": [ - { "name": "timestamp", "type": "number" } - ] - }, - { - "name": "loadEventFired", - "parameters": [ - { "name": "timestamp", "type": "number" } - ] - }, - { - "name": "frameAttached", - "description": "Fired when frame has been attached to its parent.", - "parameters": [ - { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has been attached." }, - { "name": "parentFrameId", "$ref": "FrameId", "description": "Parent frame identifier." } - ] - }, - { - "name": "frameNavigated", - "description": "Fired once navigation of the frame has completed. Frame is now associated with the new loader.", - "parameters": [ - { "name": "frame", "$ref": "Frame", "description": "Frame object." } - ] - }, - { - "name": "frameDetached", - "description": "Fired when frame has been detached from its parent.", - "parameters": [ - { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has been detached." } - ] - }, - { - "name": "frameStartedLoading", - "description": "Fired when frame has started loading.", - "parameters": [ - { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has started loading." } - ], - "hidden": true - }, - { - "name": "frameStoppedLoading", - "description": "Fired when frame has stopped loading.", - "parameters": [ - { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has stopped loading." } - ], - "hidden": true - }, - { - "name": "frameScheduledNavigation", - "description": "Fired when frame schedules a potential navigation.", - "parameters": [ - { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has scheduled a navigation." }, - { "name": "delay", "type": "number", "description": "Delay (in seconds) until the navigation is scheduled to begin. The navigation is not guaranteed to start." } - ], - "hidden": true - }, - { - "name": "frameClearedScheduledNavigation", - "description": "Fired when frame no longer has a scheduled navigation.", - "parameters": [ - { "name": "frameId", "$ref": "FrameId", "description": "Id of the frame that has cleared its scheduled navigation." } - ], - "hidden": true - }, - { - "name": "frameResized", - "hidden": true - }, - { - "name": "javascriptDialogOpening", - "description": "Fired when a JavaScript initiated dialog (alert, confirm, prompt, or onbeforeunload) is about to open.", - "parameters": [ - { "name": "message", "type": "string", "description": "Message that will be displayed by the dialog." }, - { "name": "type", "$ref": "DialogType", "description": "Dialog type." } - ], - "hidden": true - }, - { - "name": "javascriptDialogClosed", - "description": "Fired when a JavaScript initiated dialog (alert, confirm, prompt, or onbeforeunload) has been closed.", - "parameters": [ - { "name": "result", "type": "boolean", "description": "Whether dialog was confirmed." } - ], - "hidden": true - }, - { - "name": "screencastFrame", - "description": "Compressed image data requested by the <code>startScreencast</code>.", - "parameters": [ - { "name": "data", "type": "string", "description": "Base64-encoded compressed image." }, - { "name": "metadata", "$ref": "ScreencastFrameMetadata", "description": "Screencast frame metadata."}, - { "name": "sessionId", "type": "integer", "description": "Frame number."} - ], - "hidden": true, - "handlers": ["browser"] - }, - { - "name": "screencastVisibilityChanged", - "description": "Fired when the page with currently enabled screencast was shown or hidden </code>.", - "parameters": [ - { "name": "visible", "type": "boolean", "description": "True if the page is visible." } - ], - "hidden": true, - "handlers": ["browser"] - }, - { - "name": "colorPicked", - "description": "Fired when a color has been picked.", - "parameters": [ - { "name": "color", "$ref": "DOM.RGBA", "description": "RGBA of the picked color." } - ], - "hidden": true, - "handlers": ["browser"] - }, - { - "name": "interstitialShown", - "description": "Fired when interstitial page was shown", - "hidden": true, - "handlers": ["browser"] - }, - { - "name": "interstitialHidden", - "description": "Fired when interstitial page was hidden", - "hidden": true, - "handlers": ["browser"] - } - ] - }, - { - "domain": "Rendering", - "description": "This domain allows to control rendering of the page.", - "hidden": true, - "commands": [ - { - "name": "setShowPaintRects", - "description": "Requests that backend shows paint rectangles", - "parameters": [ - { "name": "result", "type": "boolean", "description": "True for showing paint rectangles" } - ] - }, - { - "name": "setShowDebugBorders", - "description": "Requests that backend shows debug borders on layers", - "parameters": [ - { "name": "show", "type": "boolean", "description": "True for showing debug borders" } - ] - }, - { - "name": "setShowFPSCounter", - "description": "Requests that backend shows the FPS counter", - "parameters": [ - { "name": "show", "type": "boolean", "description": "True for showing the FPS counter" } - ] - }, - { - "name": "setShowScrollBottleneckRects", - "description": "Requests that backend shows scroll bottleneck rects", - "parameters": [ - { "name": "show", "type": "boolean", "description": "True for showing scroll bottleneck rects" } - ] - }, - { - "name": "setShowViewportSizeOnResize", - "description": "Paints viewport size upon main frame resize.", - "parameters": [ - { "name": "show", "type": "boolean", "description": "Whether to paint size or not." } - ] - } - ] - }, - { - "domain": "Emulation", - "description": "This domain emulates different environments for the page.", - "hidden": true, - "types": [ - { - "id": "ScreenOrientation", - "type": "object", - "description": "Screen orientation.", - "properties": [ - { "name": "type", "type": "string", "enum": ["portraitPrimary", "portraitSecondary", "landscapePrimary", "landscapeSecondary"], "description": "Orientation type." }, - { "name": "angle", "type": "integer", "description": "Orientation angle." } - ] - } - ], - "commands": [ - { - "name": "setDeviceMetricsOverride", - "description": "Overrides the values of device screen dimensions (window.screen.width, window.screen.height, window.innerWidth, window.innerHeight, and \"device-width\"/\"device-height\"-related CSS media query results).", - "parameters": [ - { "name": "width", "type": "integer", "description": "Overriding width value in pixels (minimum 0, maximum 10000000). 0 disables the override." }, - { "name": "height", "type": "integer", "description": "Overriding height value in pixels (minimum 0, maximum 10000000). 0 disables the override." }, - { "name": "deviceScaleFactor", "type": "number", "description": "Overriding device scale factor value. 0 disables the override." }, - { "name": "mobile", "type": "boolean", "description": "Whether to emulate mobile device. This includes viewport meta tag, overlay scrollbars, text autosizing and more." }, - { "name": "fitWindow", "type": "boolean", "description": "Whether a view that exceeds the available browser window area should be scaled down to fit." }, - { "name": "scale", "type": "number", "optional": true, "description": "Scale to apply to resulting view image. Ignored in |fitWindow| mode." }, - { "name": "offsetX", "type": "number", "optional": true, "description": "X offset to shift resulting view image by. Ignored in |fitWindow| mode." }, - { "name": "offsetY", "type": "number", "optional": true, "description": "Y offset to shift resulting view image by. Ignored in |fitWindow| mode." }, - { "name": "screenWidth", "type": "integer", "optional": true, "description": "Overriding screen width value in pixels (minimum 0, maximum 10000000). Only used for |mobile==true|." }, - { "name": "screenHeight", "type": "integer", "optional": true, "description": "Overriding screen height value in pixels (minimum 0, maximum 10000000). Only used for |mobile==true|." }, - { "name": "positionX", "type": "integer", "optional": true, "description": "Overriding view X position on screen in pixels (minimum 0, maximum 10000000). Only used for |mobile==true|." }, - { "name": "positionY", "type": "integer", "optional": true, "description": "Overriding view Y position on screen in pixels (minimum 0, maximum 10000000). Only used for |mobile==true|." }, - { "name": "screenOrientation", "$ref": "ScreenOrientation", "optional": true, "description": "Screen orientation override." } - ], - "handlers": ["browser"] - }, - { - "name": "clearDeviceMetricsOverride", - "description": "Clears the overriden device metrics.", - "handlers": ["browser"] - }, - { - "name": "resetPageScaleFactor", - "description": "Requests that page scale factor is reset to initial values." - }, - { - "name": "setPageScaleFactor", - "description": "Sets a specified page scale factor.", - "parameters": [ - { "name": "pageScaleFactor", "type": "number", "description": "Page scale factor." } - ] - }, - { - "name": "setScriptExecutionDisabled", - "description": "Switches script execution in the page.", - "parameters": [ - { "name": "value", "type": "boolean", "description": "Whether script execution should be disabled in the page." } - ] - }, - { - "name": "setGeolocationOverride", - "description": "Overrides the Geolocation Position or Error. Omitting any of the parameters emulates position unavailable.", - "parameters": [ - { "name": "latitude", "type": "number", "optional": true, "description": "Mock latitude"}, - { "name": "longitude", "type": "number", "optional": true, "description": "Mock longitude"}, - { "name": "accuracy", "type": "number", "optional": true, "description": "Mock accuracy"} - ], - "handlers": ["browser"] - }, - { - "name": "clearGeolocationOverride", - "description": "Clears the overriden Geolocation Position and Error.", - "handlers": ["browser"] - }, - { - "name": "setTouchEmulationEnabled", - "parameters": [ - { "name": "enabled", "type": "boolean", "description": "Whether the touch event emulation should be enabled." }, - { "name": "configuration", "type": "string", "enum": ["mobile", "desktop"], "optional": true, "description": "Touch/gesture events configuration. Default: current platform." } - ], - "description": "Toggles mouse event-based touch event emulation.", - "handlers": ["browser", "renderer"] - }, - { - "name": "setEmulatedMedia", - "parameters": [ - { "name": "media", "type": "string", "description": "Media type to emulate. Empty string disables the override." } - ], - "description": "Emulates the given media for CSS media queries." - }, - { - "name": "setCPUThrottlingRate", - "parameters": [ - { "name": "rate", "type": "number", "description": "Throttling rate as a slowdown factor (1 is no throttle, 2 is 2x slowdown, etc)." } - ], - "description": "Enables CPU throttling to emulate slow CPUs." - }, - { - "name": "canEmulate", - "description": "Tells whether emulation is supported.", - "returns": [ - { "name": "result", "type": "boolean", "description": "True if emulation is supported." } - ], - "handlers": ["browser"] - } - ] - }, - { - "domain": "Runtime", - "description": "Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. Evaluation results are returned as mirror object that expose object type, string representation and unique identifier that can be used for further object reference. Original objects are maintained in memory unless they are either explicitly released or are released along with the other objects in their object group.", - "types": [ - { - "id": "ScriptId", - "type": "string", - "description": "Unique script identifier." - }, - { - "id": "RemoteObjectId", - "type": "string", - "description": "Unique object identifier." - }, - { - "id": "RemoteObject", - "type": "object", - "description": "Mirror object referencing original JavaScript object.", - "properties": [ - { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." }, - { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for <code>object</code> type values only." }, - { "name": "className", "type": "string", "optional": true, "description": "Object class (constructor) name. Specified for <code>object</code> type values only." }, - { "name": "value", "type": "any", "optional": true, "description": "Remote object value in case of primitive values or JSON values (if it was requested), or description string if the value can not be JSON-stringified (like NaN, Infinity, -Infinity, -0)." }, - { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." }, - { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Unique object identifier (for non-primitive values)." }, - { "name": "preview", "$ref": "ObjectPreview", "optional": true, "description": "Preview containing abbreviated property values. Specified for <code>object</code> type values only.", "hidden": true }, - { "name": "customPreview", "$ref": "CustomPreview", "optional": true, "hidden": true} - ] - }, - { - "id": "CustomPreview", - "type": "object", - "hidden": true, - "properties": [ - { "name": "header", "type": "string"}, - { "name": "hasBody", "type": "boolean"}, - { "name": "formatterObjectId", "$ref": "RemoteObjectId"}, - { "name": "bindRemoteObjectFunctionId", "$ref": "RemoteObjectId" }, - { "name": "configObjectId", "$ref": "RemoteObjectId", "optional": true } - ] - }, - { - "id": "ObjectPreview", - "type": "object", - "hidden": true, - "description": "Object containing abbreviated remote object value.", - "properties": [ - { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." }, - { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for <code>object</code> type values only." }, - { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." }, - { "name": "overflow", "type": "boolean", "description": "True iff some of the properties or entries of the original object did not fit." }, - { "name": "properties", "type": "array", "items": { "$ref": "PropertyPreview" }, "description": "List of the properties." }, - { "name": "entries", "type": "array", "items": { "$ref": "EntryPreview" }, "optional": true, "description": "List of the entries. Specified for <code>map</code> and <code>set</code> subtype values only." } - ] - }, - { - "id": "PropertyPreview", - "type": "object", - "hidden": true, - "properties": [ - { "name": "name", "type": "string", "description": "Property name." }, - { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol", "accessor"], "description": "Object type. Accessor means that the property itself is an accessor property." }, - { "name": "value", "type": "string", "optional": true, "description": "User-friendly property value string." }, - { "name": "valuePreview", "$ref": "ObjectPreview", "optional": true, "description": "Nested value preview." }, - { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for <code>object</code> type values only." } - ] - }, - { - "id": "EntryPreview", - "type": "object", - "hidden": true, - "properties": [ - { "name": "key", "$ref": "ObjectPreview", "optional": true, "description": "Preview of the key. Specified for map-like collection entries." }, - { "name": "value", "$ref": "ObjectPreview", "description": "Preview of the value." } - ] - }, - { - "id": "PropertyDescriptor", - "type": "object", - "description": "Object property descriptor.", - "properties": [ - { "name": "name", "type": "string", "description": "Property name or symbol description." }, - { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." }, - { "name": "writable", "type": "boolean", "optional": true, "description": "True if the value associated with the property may be changed (data descriptors only)." }, - { "name": "get", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a getter for the property, or <code>undefined</code> if there is no getter (accessor descriptors only)." }, - { "name": "set", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a setter for the property, or <code>undefined</code> if there is no setter (accessor descriptors only)." }, - { "name": "configurable", "type": "boolean", "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." }, - { "name": "enumerable", "type": "boolean", "description": "True if this property shows up during enumeration of the properties on the corresponding object." }, - { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }, - { "name": "isOwn", "optional": true, "type": "boolean", "description": "True if the property is owned for the object.", "hidden": true }, - { "name": "symbol", "$ref": "RemoteObject", "optional": true, "description": "Property symbol object, if the property is of the <code>symbol</code> type.", "hidden": true } - ] - }, - { - "id": "InternalPropertyDescriptor", - "type": "object", - "description": "Object internal property descriptor. This property isn't normally visible in JavaScript code.", - "properties": [ - { "name": "name", "type": "string", "description": "Conventional property name." }, - { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." } - ], - "hidden": true - }, - { - "id": "CallArgument", - "type": "object", - "description": "Represents function call argument. Either remote object id <code>objectId</code> or primitive <code>value</code> or neither of (for undefined) them should be specified.", - "properties": [ - { "name": "value", "type": "any", "optional": true, "description": "Primitive value, or description string if the value can not be JSON-stringified (like NaN, Infinity, -Infinity, -0)." }, - { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Remote object handle." }, - { "name": "type", "optional": true, "hidden": true, "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." } - ] - }, - { - "id": "ExecutionContextId", - "type": "integer", - "description": "Id of an execution context." - }, - { - "id": "ExecutionContextDescription", - "type": "object", - "description": "Description of an isolated world.", - "properties": [ - { "name": "id", "$ref": "ExecutionContextId", "description": "Unique id of the execution context. It can be used to specify in which execution context script evaluation should be performed." }, - { "name": "isDefault", "type": "boolean", "description": "Whether context is the default page context (as opposite to e.g. context of content script).", "hidden": true }, - { "name": "origin", "type": "string", "description": "Execution context origin.", "hidden": true}, - { "name": "name", "type": "string", "description": "Human readable name describing given context.", "hidden": true}, - { "name": "frameId", "type": "string", "description": "Id of the owning frame. May be an empty string if the context is not associated with a frame." } - ] - }, - { - "id": "ExceptionDetails", - "type": "object", - "description": "Detailed information on exception (or error) that was thrown during script compilation or execution.", - "properties": [ - { "name": "text", "type": "string", "description": "Exception text." }, - { "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." }, - { "name": "scriptId", "type": "string", "optional": true, "description": "Script ID of the message origin." }, - { "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message." }, - { "name": "column", "type": "integer", "optional": true, "description": "Column number in the resource that generated this message." }, - { "name": "stack", "$ref": "StackTrace", "optional": true, "description": "JavaScript stack trace for assertions and error messages." } - ] - }, - { - "id": "CallFrame", - "type": "object", - "description": "Stack entry for runtime errors and assertions.", - "properties": [ - { "name": "functionName", "type": "string", "description": "JavaScript function name." }, - { "name": "scriptId", "$ref": "ScriptId", "description": "JavaScript script id." }, - { "name": "url", "type": "string", "description": "JavaScript script name or url." }, - { "name": "lineNumber", "type": "integer", "description": "JavaScript script line number." }, - { "name": "columnNumber", "type": "integer", "description": "JavaScript script column number." } - ] - }, - { - "id": "StackTrace", - "type": "object", - "description": "Call frames for assertions or error messages.", - "properties": [ - { "name": "description", "type": "string", "optional": true, "description": "String label of this stack trace. For async traces this may be a name of the function that initiated the async call." }, - { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "JavaScript function name." }, - { "name": "parent", "$ref": "StackTrace", "optional": true, "hidden": true, "hidden": true, "description": "Asynchronous JavaScript stack trace that preceded this stack, if available." } - ] - } - ], - "commands": [ - { - "name": "evaluate", - "parameters": [ - { "name": "expression", "type": "string", "description": "Expression to evaluate." }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, - { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation.", "hidden": true }, - { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether evaluation should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true }, - { "name": "contextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which isolated context to perform evaluation. Each content script lives in an isolated context and this parameter may be used to specify one of those contexts. If the parameter is omitted or 0 the evaluation will be performed in the context of the inspected page." }, - { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, - { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." }, - { "name": "userGesture", "type": "boolean", "optional": true, "hidden": true, "description": "Whether execution should be treated as initiated by user in the UI." } - ], - "returns": [ - { "name": "result", "$ref": "RemoteObject", "description": "Evaluation result." }, - { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }, - { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "hidden": true, "description": "Exception details."} - ], - "description": "Evaluates expression on global object." - }, - { - "name": "callFunctionOn", - "parameters": [ - { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to call function on." }, - { "name": "functionDeclaration", "type": "string", "description": "Declaration of the function to call." }, - { "name": "arguments", "type": "array", "items": { "$ref": "CallArgument", "description": "Call argument." }, "optional": true, "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target object." }, - { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether function call should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true }, - { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." }, - { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." }, - { "name": "userGesture", "type": "boolean", "optional": true, "hidden": true, "description": "Whether execution should be treated as initiated by user in the UI." } - ], - "returns": [ - { "name": "result", "$ref": "RemoteObject", "description": "Call result." }, - { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." } - ], - "description": "Calls function with given declaration on the given object. Object group of the result is inherited from the target object." - }, - { - "name": "getProperties", - "parameters": [ - { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." }, - { "name": "ownProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging only to the element itself, not to its prototype chain." }, - { "name": "accessorPropertiesOnly", "optional": true, "type": "boolean", "description": "If true, returns accessor properties (with getter/setter) only; internal properties are not returned either.", "hidden": true }, - { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the results." } - ], - "returns": [ - { "name": "result", "type": "array", "items": { "$ref": "PropertyDescriptor" }, "description": "Object properties." }, - { "name": "internalProperties", "optional": true, "type": "array", "items": { "$ref": "InternalPropertyDescriptor" }, "description": "Internal object properties (only of the element itself).", "hidden": true }, - { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "hidden": true, "description": "Exception details."} - ], - "description": "Returns properties of a given object. Object group of the result is inherited from the target object." - }, - { - "name": "releaseObject", - "parameters": [ - { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to release." } - ], - "description": "Releases remote object with given id." - }, - { - "name": "releaseObjectGroup", - "parameters": [ - { "name": "objectGroup", "type": "string", "description": "Symbolic object group name." } - ], - "description": "Releases all remote objects that belong to a given group." - }, - { - "name": "run", - "hidden": true, - "description": "Tells inspected instance(worker or page) that it can run in case it was started paused." - }, - { - "name": "enable", - "description": "Enables reporting of execution contexts creation by means of <code>executionContextCreated</code> event. When the reporting gets enabled the event will be sent immediately for each existing execution context." - }, - { - "name": "disable", - "hidden": true, - "description": "Disables reporting of execution contexts creation." - }, - { - "name": "setCustomObjectFormatterEnabled", - "parameters": [ - { - "name": "enabled", - "type": "boolean" - } - ], - "hidden": true - }, - { - "name": "compileScript", - "hidden": true, - "parameters": [ - { "name": "expression", "type": "string", "description": "Expression to compile." }, - { "name": "sourceURL", "type": "string", "description": "Source url to be set for the script." }, - { "name": "persistScript", "type": "boolean", "description": "Specifies whether the compiled script should be persisted." }, - { "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Specifies in which isolated context to perform script run. Each content script lives in an isolated context and this parameter is used to specify one of those contexts." } - ], - "returns": [ - { "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Id of the script." }, - { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} - ], - "description": "Compiles expression." - }, - { - "name": "runScript", - "hidden": true, - "parameters": [ - { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to run." }, - { "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Specifies in which isolated context to perform script run. Each content script lives in an isolated context and this parameter is used to specify one of those contexts." }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, - { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether script run should stop on exceptions and mute console. Overrides setPauseOnException state." }, - { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." } - ], - "returns": [ - { "name": "result", "$ref": "RemoteObject", "description": "Run result." }, - { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} - ], - "description": "Runs script with given id in a given context." - } - ], - "events": [ - { - "name": "executionContextCreated", - "parameters": [ - { "name": "context", "$ref": "ExecutionContextDescription", "description": "A newly created execution contex." } - ], - "description": "Issued when new execution context is created." - }, - { - "name": "executionContextDestroyed", - "parameters": [ - { "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Id of the destroyed context" } - ], - "description": "Issued when execution context is destroyed." - }, - { - "name": "executionContextsCleared", - "description": "Issued when all executionContexts were cleared in browser" - }, - { - "name": "inspectRequested", - "parameters": [ - { "name": "object", "$ref": "RemoteObject" }, - { "name": "hints", "type": "object" } - ], - "hidden": true - } - ] - }, - { - "domain": "Console", - "description": "Console domain defines methods and events for interaction with the JavaScript console. Console collects messages created by means of the <a href='http://getfirebug.com/wiki/index.php/Console_API'>JavaScript Console API</a>. One needs to enable this domain using <code>enable</code> command in order to start receiving the console messages. Browser collects messages issued while console domain is not enabled as well and reports them using <code>messageAdded</code> notification upon enabling.", - "types": [ - { - "id": "Timestamp", - "type": "number", - "description": "Number of seconds since epoch.", - "hidden": true - }, - { - "id": "ConsoleMessage", - "type": "object", - "description": "Console message.", - "properties": [ - { "name": "source", "type": "string", "enum": ["xml", "javascript", "network", "console-api", "storage", "appcache", "rendering", "security", "other", "deprecation"], "description": "Message source." }, - { "name": "level", "type": "string", "enum": ["log", "warning", "error", "debug", "info", "revokedError"], "description": "Message severity." }, - { "name": "text", "type": "string", "description": "Message text." }, - { "name": "type", "type": "string", "optional": true, "enum": ["log", "dir", "dirxml", "table", "trace", "clear", "startGroup", "startGroupCollapsed", "endGroup", "assert", "profile", "profileEnd"], "description": "Console message type." }, - { "name": "scriptId", "type": "string", "optional": true, "description": "Script ID of the message origin." }, - { "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." }, - { "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message." }, - { "name": "column", "type": "integer", "optional": true, "description": "Column number in the resource that generated this message." }, - { "name": "repeatCount", "type": "integer", "optional": true, "description": "Repeat count for repeated messages." }, - { "name": "parameters", "type": "array", "items": { "$ref": "Runtime.RemoteObject" }, "optional": true, "description": "Message parameters in case of the formatted message." }, - { "name": "stack", "$ref": "Runtime.StackTrace", "optional": true, "description": "JavaScript stack trace for assertions and error messages." }, - { "name": "networkRequestId", "$ref": "Network.RequestId", "optional": true, "description": "Identifier of the network request associated with this message." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp, when this message was fired.", "hidden": true }, - { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "optional": true, "description": "Identifier of the context where this message was created", "hidden": true }, - { "name": "messageId", "type": "integer", "hidden": true, "optional": true, "description": "Message id." }, - { "name": "relatedMessageId", "type": "integer", "hidden": true, "optional": true, "description": "Related message id." } - ] - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables console domain, sends the messages collected so far to the client by means of the <code>messageAdded</code> notification." - }, - { - "name": "disable", - "description": "Disables console domain, prevents further console messages from being reported to the client." - }, - { - "name": "clearMessages", - "description": "Clears console messages collected in the browser." - } - ], - "events": [ - { - "name": "messageAdded", - "parameters": [ - { "name": "message", "$ref": "ConsoleMessage", "description": "Console message that has been added." } - ], - "description": "Issued when new console message is added." - }, - { - "name": "messageRepeatCountUpdated", - "parameters": [ - { "name": "count", "type": "integer", "description": "New repeat count value." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp of most recent message in batch.", "hidden": true } - ], - "description": "Is not issued. Will be gone in the future versions of the protocol.", - "deprecated": true - }, - { - "name": "messagesCleared", - "description": "Issued when console is cleared. This happens either upon <code>clearMessages</code> command or after page navigation." - } - ] - }, - { - "domain": "Security", - "description": "Security", - "hidden": true, - "types": [ - { - "id": "SecurityState", - "type": "string", - "enum": ["unknown", "neutral", "insecure", "warning", "secure", "info"], - "description": "The security level of a page or resource." - }, - { - "id": "SecurityStateExplanation", - "type": "object", - "properties": [ - { "name": "securityState", "$ref": "SecurityState", "description": "Security state representing the severity of the factor being explained." }, - { "name": "summary", "type": "string", "description": "Short phrase describing the type of factor." }, - { "name": "description", "type": "string", "description": "Full text explanation of the factor." }, - { "name": "certificateId", "$ref": "Network.CertificateId", "optional": true, "description": "Associated certificate id." } - ], - "description": "An explanation of an factor contributing to the security state." - }, - { - "id": "MixedContentStatus", - "type": "object", - "properties": [ - { "name": "ranInsecureContent", "type": "boolean", "description": "True if the page ran insecure content such as scripts." }, - { "name": "displayedInsecureContent", "type": "boolean", "description": "True if the page displayed insecure content such as images." }, - { "name": "ranInsecureContentStyle", "$ref": "SecurityState", "description": "Security state representing a page that ran insecure content." }, - { "name": "displayedInsecureContentStyle", "$ref": "SecurityState", "description": "Security state representing a page that displayed insecure content." } - ], - "description": "Information about mixed content on the page." - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables tracking security state changes.", - "handlers": ["browser"] - }, - { - "name": "disable", - "description": "Disables tracking security state changes.", - "handlers": ["browser"] - } - ], - "events": [ - { - "name": "securityStateChanged", - "description": "The security state of the page changed.", - "parameters": [ - { "name": "securityState", "$ref": "SecurityState", "description": "Security state." }, - { "name": "explanations", "type": "array", "items": { "$ref": "SecurityStateExplanation" }, "description": "List of explanations for the security state. If the overall security state is `insecure` or `warning`, at least one corresponding explanation should be included.", "optional": true }, - { "name": "mixedContentStatus", "$ref": "MixedContentStatus", "description": "Information about mixed content on the page.", "optional": true }, - { "name": "schemeIsCryptographic", "type": "boolean", "description": "True if the page was loaded over cryptographic transport such as HTTPS.", "optional": true } - ], - "handlers": ["browser"] - } - ] - }, - { - "domain": "Network", - "description": "Network domain allows tracking network activities of the page. It exposes information about http, file, data and other requests and responses, their headers, bodies, timing, etc.", - "types": [ - { - "id": "LoaderId", - "type": "string", - "description": "Unique loader identifier." - }, - { - "id": "RequestId", - "type": "string", - "description": "Unique request identifier." - }, - { - "id": "Timestamp", - "type": "number", - "description": "Number of seconds since epoch." - }, - { - "id": "Headers", - "type": "object", - "description": "Request / response headers as keys / values of JSON object." - }, - { - "id": "ResourceTiming", - "type": "object", - "description": "Timing information for the request.", - "properties": [ - { "name": "requestTime", "type": "number", "description": "Timing's requestTime is a baseline in seconds, while the other numbers are ticks in milliseconds relatively to this requestTime." }, - { "name": "proxyStart", "type": "number", "description": "Started resolving proxy." }, - { "name": "proxyEnd", "type": "number", "description": "Finished resolving proxy." }, - { "name": "dnsStart", "type": "number", "description": "Started DNS address resolve." }, - { "name": "dnsEnd", "type": "number", "description": "Finished DNS address resolve." }, - { "name": "connectStart", "type": "number", "description": "Started connecting to the remote host." }, - { "name": "connectEnd", "type": "number", "description": "Connected to the remote host." }, - { "name": "sslStart", "type": "number", "description": "Started SSL handshake." }, - { "name": "sslEnd", "type": "number", "description": "Finished SSL handshake." }, - { "name": "workerStart", "type": "number", "description": "Started running ServiceWorker.", "hidden": true }, - { "name": "workerReady", "type": "number", "description": "Finished Starting ServiceWorker.", "hidden": true }, - { "name": "sendStart", "type": "number", "description": "Started sending request." }, - { "name": "sendEnd", "type": "number", "description": "Finished sending request." }, - { "name": "pushStart", "type": "number", "description": "Time the server started pushing request.", "hidden": true }, - { "name": "pushEnd", "type": "number", "description": "Time the server finished pushing request.", "hidden": true }, - { "name": "receiveHeadersEnd", "type": "number", "description": "Finished receiving response headers." } - ] - }, - { - "id": "ResourcePriority", - "type": "string", - "enum": ["VeryLow", "Low", "Medium", "High", "VeryHigh"], - "description": "Loading priority of a resource request." - }, - { - "id": "Request", - "type": "object", - "description": "HTTP request data.", - "properties": [ - { "name": "url", "type": "string", "description": "Request URL." }, - { "name": "method", "type": "string", "description": "HTTP request method." }, - { "name": "headers", "$ref": "Headers", "description": "HTTP request headers." }, - { "name": "postData", "type": "string", "optional": true, "description": "HTTP POST request data." }, - { "name": "mixedContentType", "optional": true, "type": "string", "enum": ["blockable", "optionally-blockable", "none"], "description": "The mixed content status of the request, as defined in http://www.w3.org/TR/mixed-content/" }, - { "name": "initialPriority", "$ref": "ResourcePriority", "description": "Priority of the resource request at the time request is sent."} - ] - }, - { - "id": "CertificateId", - "type": "integer", - "description": "An internal certificate ID value." - }, - { - "id": "CertificateSubject", - "type": "object", - "description": "Subject of a certificate.", - "properties": [ - { "name": "name", "type": "string", "description": "Certificate subject name." }, - { "name": "sanDnsNames", "type": "array", "items": { "type": "string" }, "description": "Subject Alternative Name (SAN) DNS names." }, - { "name": "sanIpAddresses", "type": "array", "items": { "type": "string" }, "description": "Subject Alternative Name (SAN) IP addresses." } - ] - }, - { - "id": "CertificateDetails", - "type": "object", - "description": "Details about a request's certificate.", - "properties": [ - { "name": "subject", "$ref": "CertificateSubject", "description": "Certificate subject." }, - { "name": "issuer", "type": "string", "description": "Name of the issuing CA." }, - { "name": "validFrom", "$ref": "Timestamp", "description": "Certificate valid from date." }, - { "name": "validTo", "$ref": "Timestamp", "description": "Certificate valid to (expiration) date" } - ] - }, - { - "id": "CertificateValidationDetails", - "type": "object", - "description": "Details about the validation status of a request's certificate.", - "properties": [ - { "name": "numUnknownScts", "type": "integer", "description": "The number of SCTs from unknown logs." }, - { "name": "numInvalidScts", "type": "integer", "description": "The number of invalid SCTs." }, - { "name": "numValidScts", "type": "integer", "description": "The number of valid SCTs." } - ] - }, - { - "id": "SecurityDetails", - "type": "object", - "description": "Security details about a request.", - "properties": [ - { "name": "protocol", "type": "string", "description": "Protocol name (e.g. \"TLS 1.2\" or \"QUIC\")." }, - { "name": "keyExchange", "type": "string", "description": "Key Exchange used by the connection." }, - { "name": "cipher", "type": "string", "description": "Cipher name." }, - { "name": "mac", "type": "string", "optional": true, "description": "TLS MAC. Note that AEAD ciphers do not have separate MACs." }, - { "name": "certificateId", "$ref": "CertificateId", "description": "Certificate ID value." }, - { "name": "certificateValidationDetails", "$ref": "CertificateValidationDetails", "optional": true, "description": "Validation details for the request's certficate." } - ] - }, - { - "id": "BlockedReason", - "type": "string", - "description": "The reason why request was blocked.", - "enum": ["csp", "mixed-content", "origin", "inspector", "other"], - "hidden": true - }, - { - "id": "Response", - "type": "object", - "description": "HTTP response data.", - "properties": [ - { "name": "url", "type": "string", "description": "Response URL. This URL can be different from CachedResource.url in case of redirect." }, - { "name": "status", "type": "number", "description": "HTTP response status code." }, - { "name": "statusText", "type": "string", "description": "HTTP response status text." }, - { "name": "headers", "$ref": "Headers", "description": "HTTP response headers." }, - { "name": "headersText", "type": "string", "optional": true, "description": "HTTP response headers text." }, - { "name": "mimeType", "type": "string", "description": "Resource mimeType as determined by the browser." }, - { "name": "requestHeaders", "$ref": "Headers", "optional": true, "description": "Refined HTTP request headers that were actually transmitted over the network." }, - { "name": "requestHeadersText", "type": "string", "optional": true, "description": "HTTP request headers text." }, - { "name": "connectionReused", "type": "boolean", "description": "Specifies whether physical connection was actually reused for this request." }, - { "name": "connectionId", "type": "number", "description": "Physical connection id that was actually used for this request." }, - { "name": "remoteIPAddress", "type": "string", "optional": true, "hidden": true, "description": "Remote IP address." }, - { "name": "remotePort", "type": "integer", "optional": true, "hidden": true, "description": "Remote port."}, - { "name": "fromDiskCache", "type": "boolean", "optional": true, "description": "Specifies that the request was served from the disk cache." }, - { "name": "fromServiceWorker", "type": "boolean", "optional": true, "description": "Specifies that the request was served from the ServiceWorker." }, - { "name": "encodedDataLength", "type": "number", "optional": false, "description": "Total number of bytes received for this request so far." }, - { "name": "timing", "$ref": "ResourceTiming", "optional": true, "description": "Timing information for the given request." }, - { "name": "protocol", "type": "string", "optional": true, "description": "Protocol used to fetch this request." }, - { "name": "securityState", "$ref": "Security.SecurityState", "description": "Security state of the request resource." }, - { "name": "securityDetails", "$ref": "SecurityDetails", "optional": true, "description": "Security details for the request." } - ] - }, - { - "id": "WebSocketRequest", - "type": "object", - "description": "WebSocket request data.", - "hidden": true, - "properties": [ - { "name": "headers", "$ref": "Headers", "description": "HTTP request headers." } - ] - }, - { - "id": "WebSocketResponse", - "type": "object", - "description": "WebSocket response data.", - "hidden": true, - "properties": [ - { "name": "status", "type": "number", "description": "HTTP response status code." }, - { "name": "statusText", "type": "string", "description": "HTTP response status text." }, - { "name": "headers", "$ref": "Headers", "description": "HTTP response headers." }, - { "name": "headersText", "type": "string", "optional": true, "description": "HTTP response headers text." }, - { "name": "requestHeaders", "$ref": "Headers", "optional": true, "description": "HTTP request headers." }, - { "name": "requestHeadersText", "type": "string", "optional": true, "description": "HTTP request headers text." } - ] - }, - { - "id": "WebSocketFrame", - "type": "object", - "description": "WebSocket frame data.", - "hidden": true, - "properties": [ - { "name": "opcode", "type": "number", "description": "WebSocket frame opcode." }, - { "name": "mask", "type": "boolean", "description": "WebSocke frame mask." }, - { "name": "payloadData", "type": "string", "description": "WebSocke frame payload data." } - ] - }, - { - "id": "CachedResource", - "type": "object", - "description": "Information about the cached resource.", - "properties": [ - { "name": "url", "type": "string", "description": "Resource URL. This is the url of the original network request." }, - { "name": "type", "$ref": "Page.ResourceType", "description": "Type of this resource." }, - { "name": "response", "$ref": "Response", "optional": true, "description": "Cached response data." }, - { "name": "bodySize", "type": "number", "description": "Cached response body size." } - ] - }, - { - "id": "Initiator", - "type": "object", - "description": "Information about the request initiator.", - "properties": [ - { "name": "type", "type": "string", "enum": ["parser", "script", "other"], "description": "Type of this initiator." }, - { "name": "stack", "$ref": "Runtime.StackTrace", "optional": true, "description": "Initiator JavaScript stack trace, set for Script only." }, - { "name": "url", "type": "string", "optional": true, "description": "Initiator URL, set for Parser type only." }, - { "name": "lineNumber", "type": "number", "optional": true, "description": "Initiator line number, set for Parser type only." } - ] - }, - { - "id": "Cookie", - "type": "object", - "description": "Cookie object", - "properties": [ - { "name": "name", "type": "string", "description": "Cookie name." }, - { "name": "value", "type": "string", "description": "Cookie value." }, - { "name": "domain", "type": "string", "description": "Cookie domain." }, - { "name": "path", "type": "string", "description": "Cookie path." }, - { "name": "expires", "type": "number", "description": "Cookie expires." }, - { "name": "size", "type": "integer", "description": "Cookie size." }, - { "name": "httpOnly", "type": "boolean", "description": "True if cookie is http-only." }, - { "name": "secure", "type": "boolean", "description": "True if cookie is secure." }, - { "name": "session", "type": "boolean", "description": "True in case of session cookie." }, - { "name": "sameSite", "type": "string", "optional": true, "enum": ["Strict", "Lax"], "description": "Represents the cookies' 'SameSite' status: https://tools.ietf.org/html/draft-west-first-party-cookies" } - ], - "hidden": true - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables network tracking, network events will now be delivered to the client.", - "parameters": [ - { "name": "maxTotalBufferSize", "type": "integer", "optional": true, "hidden": true, "description": "Buffer size in bytes to use when preserving network payloads (XHRs, etc)." }, - { "name": "maxResourceBufferSize", "type": "integer", "optional": true, "hidden": true, "description": "Per-resource buffer size in bytes to use when preserving network payloads (XHRs, etc)." } - ] - }, - { - "name": "disable", - "description": "Disables network tracking, prevents network events from being sent to the client." - }, - { - "name": "setUserAgentOverride", - "description": "Allows overriding user agent with the given string.", - "parameters": [ - { "name": "userAgent", "type": "string", "description": "User agent to use." } - ] - }, - { - "name": "setExtraHTTPHeaders", - "description": "Specifies whether to always send extra HTTP headers with the requests from this page.", - "parameters": [ - { "name": "headers", "$ref": "Headers", "description": "Map with extra HTTP headers." } - ] - }, - { - "name": "getResponseBody", - "async": true, - "description": "Returns content served for the given request.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Identifier of the network request to get content for." } - ], - "returns": [ - { "name": "body", "type": "string", "description": "Response body." }, - { "name": "base64Encoded", "type": "boolean", "description": "True, if content was sent as base64." } - ] - }, - { - "name": "addBlockedURL", - "description": "Blocks specific URL from loading.", - "parameters": [ - { "name": "url", "type": "string", "description": "URL to block." } - ], - "hidden": true - }, - { - "name": "removeBlockedURL", - "description": "Cancels blocking of a specific URL from loading.", - "parameters": [ - { "name": "url", "type": "string", "description": "URL to stop blocking." } - ], - "hidden": true - }, - { - "name": "replayXHR", - "description": "This method sends a new XMLHttpRequest which is identical to the original one. The following parameters should be identical: method, url, async, request body, extra headers, withCredentials attribute, user, password.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Identifier of XHR to replay." } - ], - "hidden": true - }, - { - "name": "setMonitoringXHREnabled", - "parameters": [ - { "name": "enabled", "type": "boolean", "description": "Monitoring enabled state." } - ], - "description": "Toggles monitoring of XMLHttpRequest. If <code>true</code>, console will receive messages upon each XHR issued.", - "hidden": true - }, - { - "name": "canClearBrowserCache", - "description": "Tells whether clearing browser cache is supported.", - "returns": [ - { "name": "result", "type": "boolean", "description": "True if browser cache can be cleared." } - ] - }, - { - "name": "clearBrowserCache", - "description": "Clears browser cache.", - "handlers": ["browser"] - }, - { - "name": "canClearBrowserCookies", - "description": "Tells whether clearing browser cookies is supported.", - "returns": [ - { "name": "result", "type": "boolean", "description": "True if browser cookies can be cleared." } - ] - }, - { - "name": "clearBrowserCookies", - "description": "Clears browser cookies.", - "handlers": ["browser"] - }, - { - "name": "getCookies", - "returns": [ - { "name": "cookies", "type": "array", "items": { "$ref": "Cookie" }, "description": "Array of cookie objects." } - ], - "description": "Returns all browser cookies. Depending on the backend support, will return detailed cookie information in the <code>cookies</code> field.", - "handlers": ["browser"], - "async": true, - "hidden": true - }, - { - "name": "deleteCookie", - "parameters": [ - { "name": "cookieName", "type": "string", "description": "Name of the cookie to remove." }, - { "name": "url", "type": "string", "description": "URL to match cooke domain and path." } - ], - "description": "Deletes browser cookie with given name, domain and path.", - "handlers": ["browser"], - "async": true, - "hidden": true - }, - { - "name": "canEmulateNetworkConditions", - "description": "Tells whether emulation of network conditions is supported.", - "returns": [ - { "name": "result", "type": "boolean", "description": "True if emulation of network conditions is supported." } - ], - "hidden": true, - "handlers": ["browser"] - }, - { - "name": "emulateNetworkConditions", - "description": "Activates emulation of network conditions.", - "parameters": [ - { "name": "offline", "type": "boolean", "description": "True to emulate internet disconnection." }, - { "name": "latency", "type": "number", "description": "Additional latency (ms)." }, - { "name": "downloadThroughput", "type": "number", "description": "Maximal aggregated download throughput." }, - { "name": "uploadThroughput", "type": "number", "description": "Maximal aggregated upload throughput." } - ], - "hidden": true, - "handlers": ["browser", "renderer"] - }, - { - "name": "setCacheDisabled", - "parameters": [ - { "name": "cacheDisabled", "type": "boolean", "description": "Cache disabled state." } - ], - "description": "Toggles ignoring cache for each request. If <code>true</code>, cache will not be used." - }, - { - "name": "setBypassServiceWorker", - "parameters": [ - { "name": "bypass", "type": "boolean", "description": "Bypass service worker and load from network." } - ], - "hidden": true, - "description": "Toggles ignoring of service worker for each request." - }, - { - "name": "setDataSizeLimitsForTest", - "parameters": [ - { "name": "maxTotalSize", "type": "integer", "description": "Maximum total buffer size." }, - { "name": "maxResourceSize", "type": "integer", "description": "Maximum per-resource size." } - ], - "description": "For testing.", - "hidden": true - }, - { - "name": "getCertificateDetails", - "description": "Returns details for the given certificate.", - "parameters": [ - { "name": "certificateId", "$ref": "CertificateId", "description": "ID of the certificate to get details for." } - ], - "returns": [ - { "name": "result", "$ref": "CertificateDetails", "description": "Certificate details." } - ], - "handlers": ["browser"] - }, - { - "name": "showCertificateViewer", - "description": "Displays native dialog with the certificate details.", - "parameters": [ - { "name": "certificateId", "$ref": "CertificateId", "description": "Certificate id." } - ], - "handlers": ["browser"] - } - ], - "events": [ - { - "name": "resourceChangedPriority", - "description": "Fired when resource loading priority is changed", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "newPriority", "$ref": "ResourcePriority", "description": "New priority" }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." } - ], - "hidden": true - }, - { - "name": "requestWillBeSent", - "description": "Fired when page is about to send HTTP request.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "frameId", "$ref": "Page.FrameId", "description": "Frame identifier.", "hidden": true }, - { "name": "loaderId", "$ref": "LoaderId", "description": "Loader identifier." }, - { "name": "documentURL", "type": "string", "description": "URL of the document this request is loaded for." }, - { "name": "request", "$ref": "Request", "description": "Request data." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "wallTime", "$ref": "Timestamp", "hidden": true, "description": "UTC Timestamp." }, - { "name": "initiator", "$ref": "Initiator", "description": "Request initiator." }, - { "name": "redirectResponse", "optional": true, "$ref": "Response", "description": "Redirect response data." }, - { "name": "type", "$ref": "Page.ResourceType", "optional": true, "hidden": true, "description": "Type of this resource." } - ] - }, - { - "name": "requestServedFromCache", - "description": "Fired if request ended up loading from cache.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." } - ] - }, - { - "name": "responseReceived", - "description": "Fired when HTTP response is available.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "frameId", "$ref": "Page.FrameId", "description": "Frame identifier.", "hidden": true }, - { "name": "loaderId", "$ref": "LoaderId", "description": "Loader identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "type", "$ref": "Page.ResourceType", "description": "Resource type." }, - { "name": "response", "$ref": "Response", "description": "Response data." } - ] - }, - { - "name": "dataReceived", - "description": "Fired when data chunk was received over the network.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "dataLength", "type": "integer", "description": "Data chunk length." }, - { "name": "encodedDataLength", "type": "integer", "description": "Actual bytes received (might be less than dataLength for compressed encodings)." } - ] - }, - { - "name": "loadingFinished", - "description": "Fired when HTTP request has finished loading.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "encodedDataLength", "type": "number", "description": "Total number of bytes received for this request." } - ] - }, - { - "name": "loadingFailed", - "description": "Fired when HTTP request has failed to load.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "type", "$ref": "Page.ResourceType", "description": "Resource type." }, - { "name": "errorText", "type": "string", "description": "User friendly error message." }, - { "name": "canceled", "type": "boolean", "optional": true, "description": "True if loading was canceled." }, - { "name": "blockedReason", "$ref": "BlockedReason", "optional": true, "description": "The reason why loading was blocked, if any.", "hidden": true } - ] - }, - { - "name": "webSocketWillSendHandshakeRequest", - "description": "Fired when WebSocket is about to initiate handshake.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "wallTime", "$ref": "Timestamp", "hidden": true, "description": "UTC Timestamp." }, - { "name": "request", "$ref": "WebSocketRequest", "description": "WebSocket request data." } - ], - "hidden": true - }, - { - "name": "webSocketHandshakeResponseReceived", - "description": "Fired when WebSocket handshake response becomes available.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "response", "$ref": "WebSocketResponse", "description": "WebSocket response data." } - ], - "hidden": true - }, - { - "name": "webSocketCreated", - "description": "Fired upon WebSocket creation.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "url", "type": "string", "description": "WebSocket request URL." } - ], - "hidden": true - }, - { - "name": "webSocketClosed", - "description": "Fired when WebSocket is closed.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." } - ], - "hidden": true - }, - { - "name": "webSocketFrameReceived", - "description": "Fired when WebSocket frame is received.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "response", "$ref": "WebSocketFrame", "description": "WebSocket response data." } - ], - "hidden": true - }, - { - "name": "webSocketFrameError", - "description": "Fired when WebSocket frame error occurs.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "errorMessage", "type": "string", "description": "WebSocket frame error message." } - ], - "hidden": true - }, - { - "name": "webSocketFrameSent", - "description": "Fired when WebSocket frame is sent.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "response", "$ref": "WebSocketFrame", "description": "WebSocket response data." } - ], - "hidden": true - }, - { - "name": "eventSourceMessageReceived", - "description": "Fired when EventSource message is received.", - "parameters": [ - { "name": "requestId", "$ref": "RequestId", "description": "Request identifier." }, - { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp." }, - { "name": "eventName", "type": "string", "description": "Message type." }, - { "name": "eventId", "type": "string", "description": "Message identifier." }, - { "name": "data", "type": "string", "description": "Message content." } - ], - "hidden": true - } - ] - }, - { - "domain": "Database", - "hidden": true, - "types": [ - { - "id": "DatabaseId", - "type": "string", - "description": "Unique identifier of Database object.", - "hidden": true - }, - { - "id": "Database", - "type": "object", - "description": "Database object.", - "hidden": true, - "properties": [ - { "name": "id", "$ref": "DatabaseId", "description": "Database ID." }, - { "name": "domain", "type": "string", "description": "Database domain." }, - { "name": "name", "type": "string", "description": "Database name." }, - { "name": "version", "type": "string", "description": "Database version." } - ] - }, - { - "id": "Error", - "type": "object", - "description": "Database error.", - "properties": [ - { "name": "message", "type": "string", "description": "Error message." }, - { "name": "code", "type": "integer", "description": "Error code." } - ] - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables database tracking, database events will now be delivered to the client." - }, - { - "name": "disable", - "description": "Disables database tracking, prevents database events from being sent to the client." - }, - { - "name": "getDatabaseTableNames", - "parameters": [ - { "name": "databaseId", "$ref": "DatabaseId" } - ], - "returns": [ - { "name": "tableNames", "type": "array", "items": { "type": "string" } } - ] - }, - { - "name": "executeSQL", - "async": true, - "parameters": [ - { "name": "databaseId", "$ref": "DatabaseId" }, - { "name": "query", "type": "string" } - ], - "returns": [ - { "name": "columnNames", "type": "array", "optional": true, "items": { "type": "string" } }, - { "name": "values", "type": "array", "optional": true, "items": { "type": "any" }}, - { "name": "sqlError", "$ref": "Error", "optional": true } - ] - } - ], - "events": [ - { - "name": "addDatabase", - "parameters": [ - { "name": "database", "$ref": "Database" } - ] - } - ] - }, - { - "domain": "IndexedDB", - "hidden": true, - "types": [ - { - "id": "DatabaseWithObjectStores", - "type": "object", - "description": "Database with an array of object stores.", - "properties": [ - { "name": "name", "type": "string", "description": "Database name." }, - { "name": "version", "type": "integer", "description": "Database version." }, - { "name": "objectStores", "type": "array", "items": { "$ref": "ObjectStore" }, "description": "Object stores in this database." } - ] - }, - { - "id": "ObjectStore", - "type": "object", - "description": "Object store.", - "properties": [ - { "name": "name", "type": "string", "description": "Object store name." }, - { "name": "keyPath", "$ref": "KeyPath", "description": "Object store key path." }, - { "name": "autoIncrement", "type": "boolean", "description": "If true, object store has auto increment flag set." }, - { "name": "indexes", "type": "array", "items": { "$ref": "ObjectStoreIndex" }, "description": "Indexes in this object store." } - ] - }, - { - "id": "ObjectStoreIndex", - "type": "object", - "description": "Object store index.", - "properties": [ - { "name": "name", "type": "string", "description": "Index name." }, - { "name": "keyPath", "$ref": "KeyPath", "description": "Index key path." }, - { "name": "unique", "type": "boolean", "description": "If true, index is unique." }, - { "name": "multiEntry", "type": "boolean", "description": "If true, index allows multiple entries for a key." } - ] - }, - { - "id": "Key", - "type": "object", - "description": "Key.", - "properties": [ - { "name": "type", "type": "string", "enum": ["number", "string", "date", "array"], "description": "Key type." }, - { "name": "number", "type": "number", "optional": true, "description": "Number value." }, - { "name": "string", "type": "string", "optional": true, "description": "String value." }, - { "name": "date", "type": "number", "optional": true, "description": "Date value." }, - { "name": "array", "type": "array", "optional": true, "items": { "$ref": "Key" }, "description": "Array value." } - ] - }, - { - "id": "KeyRange", - "type": "object", - "description": "Key range.", - "properties": [ - { "name": "lower", "$ref": "Key", "optional": true, "description": "Lower bound." }, - { "name": "upper", "$ref": "Key", "optional": true, "description": "Upper bound." }, - { "name": "lowerOpen", "type": "boolean", "description": "If true lower bound is open." }, - { "name": "upperOpen", "type": "boolean", "description": "If true upper bound is open." } - ] - }, - { - "id": "DataEntry", - "type": "object", - "description": "Data entry.", - "properties": [ - { "name": "key", "type": "string", "description": "JSON-stringified key object." }, - { "name": "primaryKey", "type": "string", "description": "JSON-stringified primary key object." }, - { "name": "value", "type": "string", "description": "JSON-stringified value object." } - ] - }, - { - "id": "KeyPath", - "type": "object", - "description": "Key path.", - "properties": [ - { "name": "type", "type": "string", "enum": ["null", "string", "array"], "description": "Key path type." }, - { "name": "string", "type": "string", "optional": true, "description": "String value." }, - { "name": "array", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Array value." } - ] - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables events from backend." - }, - { - "name": "disable", - "description": "Disables events from backend." - }, - { - "name": "requestDatabaseNames", - "async": true, - "parameters": [ - { "name": "securityOrigin", "type": "string", "description": "Security origin." } - ], - "returns": [ - { "name": "databaseNames", "type": "array", "items": { "type": "string" }, "description": "Database names for origin." } - ], - "description": "Requests database names for given security origin." - }, - { - "name": "requestDatabase", - "async": true, - "parameters": [ - { "name": "securityOrigin", "type": "string", "description": "Security origin." }, - { "name": "databaseName", "type": "string", "description": "Database name." } - ], - "returns": [ - { "name": "databaseWithObjectStores", "$ref": "DatabaseWithObjectStores", "description": "Database with an array of object stores." } - ], - "description": "Requests database with given name in given frame." - }, - { - "name": "requestData", - "async": true, - "parameters": [ - { "name": "securityOrigin", "type": "string", "description": "Security origin." }, - { "name": "databaseName", "type": "string", "description": "Database name." }, - { "name": "objectStoreName", "type": "string", "description": "Object store name." }, - { "name": "indexName", "type": "string", "description": "Index name, empty string for object store data requests." }, - { "name": "skipCount", "type": "integer", "description": "Number of records to skip." }, - { "name": "pageSize", "type": "integer", "description": "Number of records to fetch." }, - { "name": "keyRange", "$ref": "KeyRange", "optional": true, "description": "Key range." } - ], - "returns": [ - { "name": "objectStoreDataEntries", "type": "array", "items": { "$ref": "DataEntry" }, "description": "Array of object store data entries." }, - { "name": "hasMore", "type": "boolean", "description": "If true, there are more entries to fetch in the given range." } - ], - "description": "Requests data from object store or index." - }, - { - "name": "clearObjectStore", - "async": true, - "parameters": [ - { "name": "securityOrigin", "type": "string", "description": "Security origin." }, - { "name": "databaseName", "type": "string", "description": "Database name." }, - { "name": "objectStoreName", "type": "string", "description": "Object store name." } - ], - "returns": [ - ], - "description": "Clears all entries from an object store." - } - ] - }, - { - "domain": "CacheStorage", - "hidden": true, - "types": [ - { - "id": "CacheId", - "type": "string", - "description": "Unique identifier of the Cache object." - }, - { - "id": "DataEntry", - "type": "object", - "description": "Data entry.", - "properties": [ - { "name": "request", "type": "string", "description": "Request url spec." }, - { "name": "response", "type": "string", "description": "Response stataus text." } - ] - }, - { - "id": "Cache", - "type": "object", - "description": "Cache identifier.", - "properties": [ - { "name": "cacheId", "$ref": "CacheId", "description": "An opaque unique id of the cache." }, - { "name": "securityOrigin", "type": "string", "description": "Security origin of the cache." }, - { "name": "cacheName", "type": "string", "description": "The name of the cache." } - ] - } - ], - "commands": [ - { - "name": "requestCacheNames", - "async": true, - "parameters": [ - { "name": "securityOrigin", "type": "string", "description": "Security origin." } - ], - "returns": [ - { "name": "caches", "type": "array", "items": { "$ref": "Cache" }, "description": "Caches for the security origin." } - ], - "description": "Requests cache names." - }, - { - "name": "requestEntries", - "async": true, - "parameters": [ - { "name": "cacheId", "$ref": "CacheId", "description": "ID of cache to get entries from." }, - { "name": "skipCount", "type": "integer", "description": "Number of records to skip." }, - { "name": "pageSize", "type": "integer", "description": "Number of records to fetch." } - ], - "returns": [ - { "name": "cacheDataEntries", "type": "array", "items": { "$ref": "DataEntry" }, "description": "Array of object store data entries." }, - { "name": "hasMore", "type": "boolean", "description": "If true, there are more entries to fetch in the given range." } - ], - "description": "Requests data from cache." - }, - { - "name": "deleteCache", - "async": true, - "parameters": [ - { "name": "cacheId", "$ref": "CacheId", "description": "Id of cache for deletion." } - ], - "description": "Deletes a cache." - }, - { - "name": "deleteEntry", - "async": true, - "parameters": [ - { "name": "cacheId", "$ref": "CacheId", "description": "Id of cache where the entry will be deleted." }, - { "name": "request", "type": "string", "description": "URL spec of the request." } - ], - "description": "Deletes a cache entry." - } - ] - }, - { - "domain": "DOMStorage", - "hidden": true, - "description": "Query and modify DOM storage.", - "types": [ - { - "id": "StorageId", - "type": "object", - "description": "DOM Storage identifier.", - "hidden": true, - "properties": [ - { "name": "securityOrigin", "type": "string", "description": "Security origin for the storage." }, - { "name": "isLocalStorage", "type": "boolean", "description": "Whether the storage is local storage (not session storage)." } - ] - }, - { - "id": "Item", - "type": "array", - "description": "DOM Storage item.", - "hidden": true, - "items": { "type": "string" } - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables storage tracking, storage events will now be delivered to the client." - }, - { - "name": "disable", - "description": "Disables storage tracking, prevents storage events from being sent to the client." - }, - { - "name": "getDOMStorageItems", - "parameters": [ - { "name": "storageId", "$ref": "StorageId" } - ], - "returns": [ - { "name": "entries", "type": "array", "items": { "$ref": "Item" } } - ] - }, - { - "name": "setDOMStorageItem", - "parameters": [ - { "name": "storageId", "$ref": "StorageId" }, - { "name": "key", "type": "string" }, - { "name": "value", "type": "string" } - ] - }, - { - "name": "removeDOMStorageItem", - "parameters": [ - { "name": "storageId", "$ref": "StorageId" }, - { "name": "key", "type": "string" } - ] - } - ], - "events": [ - { - "name": "domStorageItemsCleared", - "parameters": [ - { "name": "storageId", "$ref": "StorageId" } - ] - }, - { - "name": "domStorageItemRemoved", - "parameters": [ - { "name": "storageId", "$ref": "StorageId" }, - { "name": "key", "type": "string" } - ] - }, - { - "name": "domStorageItemAdded", - "parameters": [ - { "name": "storageId", "$ref": "StorageId" }, - { "name": "key", "type": "string" }, - { "name": "newValue", "type": "string" } - ] - }, - { - "name": "domStorageItemUpdated", - "parameters": [ - { "name": "storageId", "$ref": "StorageId" }, - { "name": "key", "type": "string" }, - { "name": "oldValue", "type": "string" }, - { "name": "newValue", "type": "string" } - ] - } - ] - }, - { - "domain": "ApplicationCache", - "hidden": true, - "types": [ - { - "id": "ApplicationCacheResource", - "type": "object", - "description": "Detailed application cache resource information.", - "properties": [ - { "name": "url", "type": "string", "description": "Resource url." }, - { "name": "size", "type": "integer", "description": "Resource size." }, - { "name": "type", "type": "string", "description": "Resource type." } - ] - }, - { - "id": "ApplicationCache", - "type": "object", - "description": "Detailed application cache information.", - "properties": [ - { "name": "manifestURL", "type": "string", "description": "Manifest URL." }, - { "name": "size", "type": "number", "description": "Application cache size." }, - { "name": "creationTime", "type": "number", "description": "Application cache creation time." }, - { "name": "updateTime", "type": "number", "description": "Application cache update time." }, - { "name": "resources", "type": "array", "items": { "$ref": "ApplicationCacheResource" }, "description": "Application cache resources." } - ] - }, - { - "id": "FrameWithManifest", - "type": "object", - "description": "Frame identifier - manifest URL pair.", - "properties": [ - { "name": "frameId", "$ref": "Page.FrameId", "description": "Frame identifier." }, - { "name": "manifestURL", "type": "string", "description": "Manifest URL." }, - { "name": "status", "type": "integer", "description": "Application cache status." } - ] - } - ], - "commands": [ - { - "name": "getFramesWithManifests", - "returns": [ - { "name": "frameIds", "type": "array", "items": { "$ref": "FrameWithManifest" }, "description": "Array of frame identifiers with manifest urls for each frame containing a document associated with some application cache." } - ], - "description": "Returns array of frame identifiers with manifest urls for each frame containing a document associated with some application cache." - }, - { - "name": "enable", - "description": "Enables application cache domain notifications." - }, - { - "name": "getManifestForFrame", - "parameters": [ - { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame containing document whose manifest is retrieved." } - ], - "returns": [ - { "name": "manifestURL", "type": "string", "description": "Manifest URL for document in the given frame." } - ], - "description": "Returns manifest URL for document in the given frame." - }, - { - "name": "getApplicationCacheForFrame", - "parameters": [ - { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame containing document whose application cache is retrieved." } - ], - "returns": [ - { "name": "applicationCache", "$ref": "ApplicationCache", "description": "Relevant application cache data for the document in given frame." } - ], - "description": "Returns relevant application cache data for the document in given frame." - } - ], - "events": [ - { - "name": "applicationCacheStatusUpdated", - "parameters": [ - { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame containing document whose application cache updated status." }, - { "name": "manifestURL", "type": "string", "description": "Manifest URL." }, - { "name": "status", "type": "integer", "description": "Updated application cache status." } - ] - }, - { - "name": "networkStateUpdated", - "parameters": [ - { "name": "isNowOnline", "type": "boolean" } - ] - } - ] - }, - { - "domain": "DOM", - "description": "This domain exposes DOM read/write operations. Each DOM Node is represented with its mirror object that has an <code>id</code>. This <code>id</code> can be used to get additional information on the Node, resolve it into the JavaScript object wrapper, etc. It is important that client receives DOM events only for the nodes that are known to the client. Backend keeps track of the nodes that were sent to the client and never sends the same node twice. It is client's responsibility to collect information about the nodes that were sent to the client.<p>Note that <code>iframe</code> owner elements will return corresponding document elements as their child nodes.</p>", - "types": [ - { - "id": "NodeId", - "type": "integer", - "description": "Unique DOM node identifier." - }, - { - "id": "BackendNodeId", - "type": "integer", - "description": "Unique DOM node identifier used to reference a node that may not have been pushed to the front-end.", - "hidden": true - }, - { - "id": "BackendNode", - "type": "object", - "properties": [ - { "name": "nodeType", "type": "integer", "description": "<code>Node</code>'s nodeType." }, - { "name": "nodeName", "type": "string", "description": "<code>Node</code>'s nodeName." }, - { "name": "backendNodeId", "$ref": "BackendNodeId" } - ], - "hidden": true, - "description": "Backend node with a friendly name." - }, - { - "id": "PseudoType", - "type": "string", - "enum": [ - "first-line", - "first-letter", - "before", - "after", - "backdrop", - "selection", - "first-line-inherited", - "scrollbar", - "scrollbar-thumb", - "scrollbar-button", - "scrollbar-track", - "scrollbar-track-piece", - "scrollbar-corner", - "resizer", - "input-list-button" - ], - "description": "Pseudo element type." - }, - { - "id": "ShadowRootType", - "type": "string", - "enum": ["user-agent", "open", "closed"], - "description": "Shadow root type." - }, - { - "id": "Node", - "type": "object", - "properties": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Node identifier that is passed into the rest of the DOM messages as the <code>nodeId</code>. Backend will only push node with given <code>id</code> once. It is aware of all requested nodes and will only fire DOM events for nodes known to the client." }, - { "name": "nodeType", "type": "integer", "description": "<code>Node</code>'s nodeType." }, - { "name": "nodeName", "type": "string", "description": "<code>Node</code>'s nodeName." }, - { "name": "localName", "type": "string", "description": "<code>Node</code>'s localName." }, - { "name": "nodeValue", "type": "string", "description": "<code>Node</code>'s nodeValue." }, - { "name": "childNodeCount", "type": "integer", "optional": true, "description": "Child count for <code>Container</code> nodes." }, - { "name": "children", "type": "array", "optional": true, "items": { "$ref": "Node" }, "description": "Child nodes of this node when requested with children." }, - { "name": "attributes", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Attributes of the <code>Element</code> node in the form of flat array <code>[name1, value1, name2, value2]</code>." }, - { "name": "documentURL", "type": "string", "optional": true, "description": "Document URL that <code>Document</code> or <code>FrameOwner</code> node points to." }, - { "name": "baseURL", "type": "string", "optional": true, "description": "Base URL that <code>Document</code> or <code>FrameOwner</code> node uses for URL completion.", "hidden": true }, - { "name": "publicId", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s publicId." }, - { "name": "systemId", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s systemId." }, - { "name": "internalSubset", "type": "string", "optional": true, "description": "<code>DocumentType</code>'s internalSubset." }, - { "name": "xmlVersion", "type": "string", "optional": true, "description": "<code>Document</code>'s XML version in case of XML documents." }, - { "name": "name", "type": "string", "optional": true, "description": "<code>Attr</code>'s name." }, - { "name": "value", "type": "string", "optional": true, "description": "<code>Attr</code>'s value." }, - { "name": "pseudoType", "$ref": "PseudoType", "optional": true, "description": "Pseudo element type for this node." }, - { "name": "shadowRootType", "$ref": "ShadowRootType", "optional": true, "description": "Shadow root type." }, - { "name": "frameId", "$ref": "Page.FrameId", "optional": true, "description": "Frame ID for frame owner elements.", "hidden": true }, - { "name": "contentDocument", "$ref": "Node", "optional": true, "description": "Content document for frame owner elements." }, - { "name": "shadowRoots", "type": "array", "optional": true, "items": { "$ref": "Node" }, "description": "Shadow root list for given element host.", "hidden": true }, - { "name": "templateContent", "$ref": "Node", "optional": true, "description": "Content document fragment for template elements.", "hidden": true }, - { "name": "pseudoElements", "type": "array", "items": { "$ref": "Node" }, "optional": true, "description": "Pseudo elements associated with this node.", "hidden": true }, - { "name": "importedDocument", "$ref": "Node", "optional": true, "description": "Import document for the HTMLImport links." }, - { "name": "distributedNodes", "type": "array", "items": { "$ref": "BackendNode" }, "optional": true, "description": "Distributed nodes for given insertion point.", "hidden": true } - ], - "description": "DOM interaction is implemented in terms of mirror objects that represent the actual DOM nodes. DOMNode is a base node mirror type." - }, - { - "id": "RGBA", - "type": "object", - "properties": [ - { "name": "r", "type": "integer", "description": "The red component, in the [0-255] range." }, - { "name": "g", "type": "integer", "description": "The green component, in the [0-255] range." }, - { "name": "b", "type": "integer", "description": "The blue component, in the [0-255] range." }, - { "name": "a", "type": "number", "optional": true, "description": "The alpha component, in the [0-1] range (default: 1)." } - ], - "description": "A structure holding an RGBA color." - }, - { - "id": "Quad", - "type": "array", - "items": { "type": "number" }, - "minItems": 8, - "maxItems": 8, - "description": "An array of quad vertices, x immediately followed by y for each point, points clock-wise.", - "hidden": true - }, - { - "id": "BoxModel", - "type": "object", - "hidden": true, - "properties": [ - { "name": "content", "$ref": "Quad", "description": "Content box" }, - { "name": "padding", "$ref": "Quad", "description": "Padding box" }, - { "name": "border", "$ref": "Quad", "description": "Border box" }, - { "name": "margin", "$ref": "Quad", "description": "Margin box" }, - { "name": "width", "type": "integer", "description": "Node width" }, - { "name": "height", "type": "integer", "description": "Node height" }, - { "name": "shapeOutside", "$ref": "ShapeOutsideInfo", "optional": true, "description": "Shape outside coordinates" } - ], - "description": "Box model." - }, - { - "id": "ShapeOutsideInfo", - "type": "object", - "hidden": true, - "properties": [ - { "name": "bounds", "$ref": "Quad", "description": "Shape bounds" }, - { "name": "shape", "type": "array", "items": { "type": "any"}, "description": "Shape coordinate details" }, - { "name": "marginShape", "type": "array", "items": { "type": "any"}, "description": "Margin shape bounds" } - ], - "description": "CSS Shape Outside details." - }, - { - "id": "Rect", - "type": "object", - "hidden": true, - "properties": [ - { "name": "x", "type": "number", "description": "X coordinate" }, - { "name": "y", "type": "number", "description": "Y coordinate" }, - { "name": "width", "type": "number", "description": "Rectangle width" }, - { "name": "height", "type": "number", "description": "Rectangle height" } - ], - "description": "Rectangle." - }, - { - "id": "HighlightConfig", - "type": "object", - "properties": [ - { "name": "showInfo", "type": "boolean", "optional": true, "description": "Whether the node info tooltip should be shown (default: false)." }, - { "name": "showRulers", "type": "boolean", "optional": true, "description": "Whether the rulers should be shown (default: false)." }, - { "name": "showExtensionLines", "type": "boolean", "optional": true, "description": "Whether the extension lines from node to the rulers should be shown (default: false)." }, - { "name": "displayAsMaterial", "type": "boolean", "optional": true, "hidden": true}, - { "name": "contentColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight fill color (default: transparent)." }, - { "name": "paddingColor", "$ref": "RGBA", "optional": true, "description": "The padding highlight fill color (default: transparent)." }, - { "name": "borderColor", "$ref": "RGBA", "optional": true, "description": "The border highlight fill color (default: transparent)." }, - { "name": "marginColor", "$ref": "RGBA", "optional": true, "description": "The margin highlight fill color (default: transparent)." }, - { "name": "eventTargetColor", "$ref": "RGBA", "optional": true, "hidden": true, "description": "The event target element highlight fill color (default: transparent)." }, - { "name": "shapeColor", "$ref": "RGBA", "optional": true, "hidden": true, "description": "The shape outside fill color (default: transparent)." }, - { "name": "shapeMarginColor", "$ref": "RGBA", "optional": true, "hidden": true, "description": "The shape margin fill color (default: transparent)." }, - { "name": "selectorList", "type": "string", "optional": true, "description": "Selectors to highlight relevant nodes."} - ], - "description": "Configuration data for the highlighting of page elements." - }, - { - "id": "InspectMode", - "type": "string", - "hidden": true, - "enum": [ - "searchForNode", - "searchForUAShadowDOM", - "showLayoutEditor", - "none" - ] - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables DOM agent for the given page." - }, - { - "name": "disable", - "description": "Disables DOM agent for the given page." - }, - { - "name": "getDocument", - "returns": [ - { "name": "root", "$ref": "Node", "description": "Resulting node." } - ], - "description": "Returns the root DOM node to the caller." - }, - { - "name": "requestChildNodes", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get children for." }, - { "name": "depth", "type": "integer", "optional": true, "description": "The maximum depth at which children should be retrieved, defaults to 1. Use -1 for the entire subtree or provide an integer larger than 0.", "hidden": true } - ], - "description": "Requests that children of the node with given id are returned to the caller in form of <code>setChildNodes</code> events where not only immediate children are retrieved, but all children down to the specified depth." - }, - { - "name": "querySelector", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to query upon." }, - { "name": "selector", "type": "string", "description": "Selector string." } - ], - "returns": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Query selector result." } - ], - "description": "Executes <code>querySelector</code> on a given node." - }, - { - "name": "querySelectorAll", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to query upon." }, - { "name": "selector", "type": "string", "description": "Selector string." } - ], - "returns": [ - { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Query selector result." } - ], - "description": "Executes <code>querySelectorAll</code> on a given node." - }, - { - "name": "setNodeName", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set name for." }, - { "name": "name", "type": "string", "description": "New node's name." } - ], - "returns": [ - { "name": "nodeId", "$ref": "NodeId", "description": "New node's id." } - ], - "description": "Sets node name for a node with given id." - }, - { - "name": "setNodeValue", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set value for." }, - { "name": "value", "type": "string", "description": "New node's value." } - ], - "description": "Sets node value for a node with given id." - }, - { - "name": "removeNode", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to remove." } - ], - "description": "Removes node with given id." - }, - { - "name": "setAttributeValue", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to set attribute for." }, - { "name": "name", "type": "string", "description": "Attribute name." }, - { "name": "value", "type": "string", "description": "Attribute value." } - ], - "description": "Sets attribute for an element with given id." - }, - { - "name": "setAttributesAsText", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to set attributes for." }, - { "name": "text", "type": "string", "description": "Text with a number of attributes. Will parse this text using HTML parser." }, - { "name": "name", "type": "string", "optional": true, "description": "Attribute name to replace with new attributes derived from text in case text parsed successfully." } - ], - "description": "Sets attributes on element with given id. This method is useful when user edits some existing attribute value and types in several attribute name/value pairs." - }, - { - "name": "removeAttribute", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the element to remove attribute from." }, - { "name": "name", "type": "string", "description": "Name of the attribute to remove." } - ], - "description": "Removes attribute with given name from an element with given id." - }, - { - "name": "getOuterHTML", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get markup for." } - ], - "returns": [ - { "name": "outerHTML", "type": "string", "description": "Outer HTML markup." } - ], - "description": "Returns node's HTML markup." - }, - { - "name": "setOuterHTML", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to set markup for." }, - { "name": "outerHTML", "type": "string", "description": "Outer HTML markup to set." } - ], - "description": "Sets node HTML markup, returns new node id." - }, - { - "name": "performSearch", - "parameters": [ - { "name": "query", "type": "string", "description": "Plain text or query selector or XPath search query." }, - { "name": "includeUserAgentShadowDOM", "type": "boolean", "optional": true, "description": "True to search in user agent shadow DOM.", "hidden": true } - ], - "returns": [ - { "name": "searchId", "type": "string", "description": "Unique search session identifier." }, - { "name": "resultCount", "type": "integer", "description": "Number of search results." } - ], - "description": "Searches for a given string in the DOM tree. Use <code>getSearchResults</code> to access search results or <code>cancelSearch</code> to end this search session.", - "hidden": true - }, - { - "name": "getSearchResults", - "parameters": [ - { "name": "searchId", "type": "string", "description": "Unique search session identifier." }, - { "name": "fromIndex", "type": "integer", "description": "Start index of the search result to be returned." }, - { "name": "toIndex", "type": "integer", "description": "End index of the search result to be returned." } - ], - "returns": [ - { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Ids of the search result nodes." } - ], - "description": "Returns search results from given <code>fromIndex</code> to given <code>toIndex</code> from the sarch with the given identifier.", - "hidden": true - }, - { - "name": "discardSearchResults", - "parameters": [ - { "name": "searchId", "type": "string", "description": "Unique search session identifier." } - ], - "description": "Discards search results from the session with the given id. <code>getSearchResults</code> should no longer be called for that search.", - "hidden": true - }, - { - "name": "requestNode", - "parameters": [ - { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "JavaScript object id to convert into node." } - ], - "returns": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Node id for given object." } - ], - "description": "Requests that the node is sent to the caller given the JavaScript node object reference. All nodes that form the path from the node to the root are also sent to the client as a series of <code>setChildNodes</code> notifications." - }, - { - "name": "setInspectMode", - "hidden": true, - "parameters": [ - { "name": "mode", "$ref": "InspectMode", "description": "Set an inspection mode." }, - { "name": "highlightConfig", "$ref": "HighlightConfig", "optional": true, "description": "A descriptor for the highlight appearance of hovered-over nodes. May be omitted if <code>enabled == false</code>." } - ], - "description": "Enters the 'inspect' mode. In this mode, elements that user is hovering over are highlighted. Backend then generates 'inspectNodeRequested' event upon element selection." - }, - { - "name": "highlightRect", - "parameters": [ - { "name": "x", "type": "integer", "description": "X coordinate" }, - { "name": "y", "type": "integer", "description": "Y coordinate" }, - { "name": "width", "type": "integer", "description": "Rectangle width" }, - { "name": "height", "type": "integer", "description": "Rectangle height" }, - { "name": "color", "$ref": "RGBA", "optional": true, "description": "The highlight fill color (default: transparent)." }, - { "name": "outlineColor", "$ref": "RGBA", "optional": true, "description": "The highlight outline color (default: transparent)." } - ], - "description": "Highlights given rectangle. Coordinates are absolute with respect to the main frame viewport." - }, - { - "name": "highlightQuad", - "parameters": [ - { "name": "quad", "$ref": "Quad", "description": "Quad to highlight" }, - { "name": "color", "$ref": "RGBA", "optional": true, "description": "The highlight fill color (default: transparent)." }, - { "name": "outlineColor", "$ref": "RGBA", "optional": true, "description": "The highlight outline color (default: transparent)." } - ], - "description": "Highlights given quad. Coordinates are absolute with respect to the main frame viewport.", - "hidden": true - }, - { - "name": "highlightNode", - "parameters": [ - { "name": "highlightConfig", "$ref": "HighlightConfig", "description": "A descriptor for the highlight appearance." }, - { "name": "nodeId", "$ref": "NodeId", "optional": true, "description": "Identifier of the node to highlight." }, - { "name": "backendNodeId", "$ref": "BackendNodeId", "optional": true, "description": "Identifier of the backend node to highlight." }, - { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "optional": true, "description": "JavaScript object id of the node to be highlighted.", "hidden": true } - ], - "description": "Highlights DOM node with given id or with the given JavaScript object wrapper. Either nodeId or objectId must be specified." - }, - { - "name": "hideHighlight", - "description": "Hides DOM node highlight." - }, - { - "name": "highlightFrame", - "parameters": [ - { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame to highlight." }, - { "name": "contentColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight fill color (default: transparent)." }, - { "name": "contentOutlineColor", "$ref": "RGBA", "optional": true, "description": "The content box highlight outline color (default: transparent)." } - ], - "description": "Highlights owner element of the frame with given id.", - "hidden": true - }, - { - "name": "pushNodeByPathToFrontend", - "parameters": [ - { "name": "path", "type": "string", "description": "Path to node in the proprietary format." } - ], - "returns": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node for given path." } - ], - "description": "Requests that the node is sent to the caller given its path. // FIXME, use XPath", - "hidden": true - }, - { - "name": "pushNodesByBackendIdsToFrontend", - "parameters": [ - { "name": "backendNodeIds", "type": "array", "items": {"$ref": "BackendNodeId"}, "description": "The array of backend node ids." } - ], - "returns": [ - { "name": "nodeIds", "type": "array", "items": {"$ref": "NodeId"}, "description": "The array of ids of pushed nodes that correspond to the backend ids specified in backendNodeIds." } - ], - "description": "Requests that a batch of nodes is sent to the caller given their backend node ids.", - "hidden": true - }, - { - "name": "setInspectedNode", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "DOM node id to be accessible by means of $x command line API." } - ], - "description": "Enables console to refer to the node with given id via $x (see Command Line API for more details $x functions).", - "hidden": true - }, - { - "name": "resolveNode", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to resolve." }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." } - ], - "returns": [ - { "name": "object", "$ref": "Runtime.RemoteObject", "description": "JavaScript object wrapper for given node." } - ], - "description": "Resolves JavaScript node object for given node id." - }, - { - "name": "getAttributes", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to retrieve attibutes for." } - ], - "returns": [ - { "name": "attributes", "type": "array", "items": { "type": "string" }, "description": "An interleaved array of node attribute names and values." } - ], - "description": "Returns attributes for the specified node." - }, - { - "name": "copyTo", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to copy." }, - { "name": "targetNodeId", "$ref": "NodeId", "description": "Id of the element to drop the copy into." }, - { "name": "insertBeforeNodeId", "$ref": "NodeId", "optional": true, "description": "Drop the copy before this node (if absent, the copy becomes the last child of <code>targetNodeId</code>)." } - ], - "returns": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node clone." } - ], - "description": "Creates a deep copy of the specified node and places it into the target container before the given anchor.", - "hidden": true - }, - { - "name": "moveTo", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to move." }, - { "name": "targetNodeId", "$ref": "NodeId", "description": "Id of the element to drop the moved node into." }, - { "name": "insertBeforeNodeId", "$ref": "NodeId", "optional": true, "description": "Drop node before this one (if absent, the moved node becomes the last child of <code>targetNodeId</code>)." } - ], - "returns": [ - { "name": "nodeId", "$ref": "NodeId", "description": "New id of the moved node." } - ], - "description": "Moves node into the new container, places it before the given anchor." - }, - { - "name": "undo", - "description": "Undoes the last performed action.", - "hidden": true - }, - { - "name": "redo", - "description": "Re-does the last undone action.", - "hidden": true - }, - { - "name": "markUndoableState", - "description": "Marks last undoable state.", - "hidden": true - }, - { - "name": "focus", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to focus." } - ], - "description": "Focuses the given element.", - "hidden": true - }, - { - "name": "setFileInputFiles", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the file input node to set files for." }, - { "name": "files", "type": "array", "items": { "type": "string" }, "description": "Array of file paths to set." } - ], - "description": "Sets files for the given file input element.", - "hidden": true, - "handlers": ["browser", "renderer"] - }, - { - "name": "getBoxModel", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get box model for." } - ], - "returns": [ - { "name": "model", "$ref": "BoxModel", "description": "Box model for the node." } - ], - "description": "Returns boxes for the currently selected nodes.", - "hidden": true - }, - { - "name": "getNodeForLocation", - "parameters": [ - { "name": "x", "type": "integer", "description": "X coordinate." }, - { "name": "y", "type": "integer", "description": "Y coordinate." } - ], - "returns": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node at given coordinates." } - ], - "description": "Returns node id at given location.", - "hidden": true - }, - { - "name": "getRelayoutBoundary", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node." } - ], - "returns": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Relayout boundary node id for the given node." } - ], - "description": "Returns the id of the nearest ancestor that is a relayout boundary.", - "hidden": true - }, - { - "name": "getHighlightObjectForTest", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node to get highlight object for." } - ], - "returns": [ - { "name": "highlight", "type": "object", "description": "Highlight data for the node." } - ], - "description": "For testing.", - "hidden": true - } - ], - "events": [ - { - "name": "documentUpdated", - "description": "Fired when <code>Document</code> has been totally updated. Node ids are no longer valid." - }, - { - "name": "inspectNodeRequested", - "parameters": [ - { "name": "backendNodeId", "$ref": "BackendNodeId", "description": "Id of the node to inspect." } - ], - "description": "Fired when the node should be inspected. This happens after call to <code>setInspectMode</code>.", - "hidden" : true - }, - { - "name": "setChildNodes", - "parameters": [ - { "name": "parentId", "$ref": "NodeId", "description": "Parent node id to populate with children." }, - { "name": "nodes", "type": "array", "items": { "$ref": "Node" }, "description": "Child nodes array." } - ], - "description": "Fired when backend wants to provide client with the missing DOM structure. This happens upon most of the calls requesting node ids." - }, - { - "name": "attributeModified", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, - { "name": "name", "type": "string", "description": "Attribute name." }, - { "name": "value", "type": "string", "description": "Attribute value." } - ], - "description": "Fired when <code>Element</code>'s attribute is modified." - }, - { - "name": "attributeRemoved", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, - { "name": "name", "type": "string", "description": "A ttribute name." } - ], - "description": "Fired when <code>Element</code>'s attribute is removed." - }, - { - "name": "inlineStyleInvalidated", - "parameters": [ - { "name": "nodeIds", "type": "array", "items": { "$ref": "NodeId" }, "description": "Ids of the nodes for which the inline styles have been invalidated." } - ], - "description": "Fired when <code>Element</code>'s inline style is modified via a CSS property modification.", - "hidden": true - }, - { - "name": "characterDataModified", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, - { "name": "characterData", "type": "string", "description": "New text value." } - ], - "description": "Mirrors <code>DOMCharacterDataModified</code> event." - }, - { - "name": "childNodeCountUpdated", - "parameters": [ - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, - { "name": "childNodeCount", "type": "integer", "description": "New node count." } - ], - "description": "Fired when <code>Container</code>'s child node count has changed." - }, - { - "name": "childNodeInserted", - "parameters": [ - { "name": "parentNodeId", "$ref": "NodeId", "description": "Id of the node that has changed." }, - { "name": "previousNodeId", "$ref": "NodeId", "description": "If of the previous siblint." }, - { "name": "node", "$ref": "Node", "description": "Inserted node data." } - ], - "description": "Mirrors <code>DOMNodeInserted</code> event." - }, - { - "name": "childNodeRemoved", - "parameters": [ - { "name": "parentNodeId", "$ref": "NodeId", "description": "Parent id." }, - { "name": "nodeId", "$ref": "NodeId", "description": "Id of the node that has been removed." } - ], - "description": "Mirrors <code>DOMNodeRemoved</code> event." - }, - { - "name": "shadowRootPushed", - "parameters": [ - { "name": "hostId", "$ref": "NodeId", "description": "Host element id." }, - { "name": "root", "$ref": "Node", "description": "Shadow root." } - ], - "description": "Called when shadow root is pushed into the element.", - "hidden": true - }, - { - "name": "shadowRootPopped", - "parameters": [ - { "name": "hostId", "$ref": "NodeId", "description": "Host element id." }, - { "name": "rootId", "$ref": "NodeId", "description": "Shadow root id." } - ], - "description": "Called when shadow root is popped from the element.", - "hidden": true - }, - { - "name": "pseudoElementAdded", - "parameters": [ - { "name": "parentId", "$ref": "NodeId", "description": "Pseudo element's parent element id." }, - { "name": "pseudoElement", "$ref": "Node", "description": "The added pseudo element." } - ], - "description": "Called when a pseudo element is added to an element.", - "hidden": true - }, - { - "name": "pseudoElementRemoved", - "parameters": [ - { "name": "parentId", "$ref": "NodeId", "description": "Pseudo element's parent element id." }, - { "name": "pseudoElementId", "$ref": "NodeId", "description": "The removed pseudo element id." } - ], - "description": "Called when a pseudo element is removed from an element.", - "hidden": true - }, - { - "name": "distributedNodesUpdated", - "parameters": [ - { "name": "insertionPointId", "$ref": "NodeId", "description": "Insertion point where distrubuted nodes were updated." }, - { "name": "distributedNodes", "type": "array", "items": { "$ref": "BackendNode" }, "description": "Distributed nodes for given insertion point." } - ], - "description": "Called when distrubution is changed.", - "hidden": true - }, - { - "name": "nodeHighlightRequested", - "parameters": [ - {"name": "nodeId", "$ref": "NodeId"} - ], - "hidden": true - } - ] - }, - { - "domain": "CSS", - "hidden": true, - "description": "This domain exposes CSS read/write operations. All CSS objects (stylesheets, rules, and styles) have an associated <code>id</code> used in subsequent operations on the related object. Each object type has a specific <code>id</code> structure, and those are not interchangeable between objects of different kinds. CSS objects can be loaded using the <code>get*ForNode()</code> calls (which accept a DOM node id). A client can also discover all the existing stylesheets with the <code>getAllStyleSheets()</code> method (or keeping track of the <code>styleSheetAdded</code>/<code>styleSheetRemoved</code> events) and subsequently load the required stylesheet contents using the <code>getStyleSheet[Text]()</code> methods.", - "types": [ - { - "id": "StyleSheetId", - "type": "string" - }, - { - "id": "StyleSheetOrigin", - "type": "string", - "enum": ["injected", "user-agent", "inspector", "regular"], - "description": "Stylesheet type: \"injected\" for stylesheets injected via extension, \"user-agent\" for user-agent stylesheets, \"inspector\" for stylesheets created by the inspector (i.e. those holding the \"via inspector\" rules), \"regular\" for regular stylesheets." - }, - { - "id": "PseudoElementMatches", - "type": "object", - "properties": [ - { "name": "pseudoType", "$ref": "DOM.PseudoType", "description": "Pseudo element type."}, - { "name": "matches", "type": "array", "items": { "$ref": "RuleMatch" }, "description": "Matches of CSS rules applicable to the pseudo style."} - ], - "description": "CSS rule collection for a single pseudo style." - }, - { - "id": "InheritedStyleEntry", - "type": "object", - "properties": [ - { "name": "inlineStyle", "$ref": "CSSStyle", "optional": true, "description": "The ancestor node's inline style, if any, in the style inheritance chain." }, - { "name": "matchedCSSRules", "type": "array", "items": { "$ref": "RuleMatch" }, "description": "Matches of CSS rules matching the ancestor node in the style inheritance chain." } - ], - "description": "Inherited CSS rule collection from ancestor node." - }, - { - "id": "RuleMatch", - "type": "object", - "properties": [ - { "name": "rule", "$ref": "CSSRule", "description": "CSS rule in the match." }, - { "name": "matchingSelectors", "type": "array", "items": { "type": "integer" }, "description": "Matching selector indices in the rule's selectorList selectors (0-based)." } - ], - "description": "Match data for a CSS rule." - }, - { - "id": "Value", - "type": "object", - "properties": [ - { "name": "text", "type": "string", "description": "Value text." }, - { "name": "range", "$ref": "SourceRange", "optional": true, "description": "Value range in the underlying resource (if available)." } - ], - "description": "Data for a simple selector (these are delimited by commas in a selector list)." - }, - { - "id": "SelectorList", - "type": "object", - "properties": [ - { "name": "selectors", "type": "array", "items": { "$ref": "Value" }, "description": "Selectors in the list." }, - { "name": "text", "type": "string", "description": "Rule selector text." } - ], - "description": "Selector list data." - }, - { - "id": "CSSStyleSheetHeader", - "type": "object", - "properties": [ - { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "The stylesheet identifier."}, - { "name": "frameId", "$ref": "Page.FrameId", "description": "Owner frame identifier."}, - { "name": "sourceURL", "type": "string", "description": "Stylesheet resource URL."}, - { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with the stylesheet (if any)." }, - { "name": "origin", "$ref": "StyleSheetOrigin", "description": "Stylesheet origin."}, - { "name": "title", "type": "string", "description": "Stylesheet title."}, - { "name": "ownerNode", "$ref": "DOM.BackendNodeId", "optional": true, "description": "The backend id for the owner node of the stylesheet." }, - { "name": "disabled", "type": "boolean", "description": "Denotes whether the stylesheet is disabled."}, - { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "Whether the sourceURL field value comes from the sourceURL comment." }, - { "name": "isInline", "type": "boolean", "description": "Whether this stylesheet is created for STYLE tag by parser. This flag is not set for document.written STYLE tags." }, - { "name": "startLine", "type": "number", "description": "Line offset of the stylesheet within the resource (zero based)." }, - { "name": "startColumn", "type": "number", "description": "Column offset of the stylesheet within the resource (zero based)." } - ], - "description": "CSS stylesheet metainformation." - }, - { - "id": "CSSRule", - "type": "object", - "properties": [ - { "name": "styleSheetId", "$ref": "StyleSheetId", "optional": true, "description": "The css style sheet identifier (absent for user agent stylesheet and user-specified stylesheet rules) this rule came from." }, - { "name": "selectorList", "$ref": "SelectorList", "description": "Rule selector data." }, - { "name": "origin", "$ref": "StyleSheetOrigin", "description": "Parent stylesheet's origin."}, - { "name": "style", "$ref": "CSSStyle", "description": "Associated style declaration." }, - { "name": "media", "type": "array", "items": { "$ref": "CSSMedia" }, "optional": true, "description": "Media list array (for rules involving media queries). The array enumerates media queries starting with the innermost one, going outwards." } - ], - "description": "CSS rule representation." - }, - { - "id": "SourceRange", - "type": "object", - "properties": [ - { "name": "startLine", "type": "integer", "description": "Start line of range." }, - { "name": "startColumn", "type": "integer", "description": "Start column of range (inclusive)." }, - { "name": "endLine", "type": "integer", "description": "End line of range" }, - { "name": "endColumn", "type": "integer", "description": "End column of range (exclusive)." } - ], - "description": "Text range within a resource. All numbers are zero-based." - }, - { - "id": "ShorthandEntry", - "type": "object", - "properties": [ - { "name": "name", "type": "string", "description": "Shorthand name." }, - { "name": "value", "type": "string", "description": "Shorthand value." }, - { "name": "important", "type": "boolean", "optional": true, "description": "Whether the property has \"!important\" annotation (implies <code>false</code> if absent)." } - ] - }, - { - "id": "CSSComputedStyleProperty", - "type": "object", - "properties": [ - { "name": "name", "type": "string", "description": "Computed style property name." }, - { "name": "value", "type": "string", "description": "Computed style property value." } - ] - }, - { - "id": "CSSStyle", - "type": "object", - "properties": [ - { "name": "styleSheetId", "$ref": "StyleSheetId", "optional": true, "description": "The css style sheet identifier (absent for user agent stylesheet and user-specified stylesheet rules) this rule came from." }, - { "name": "cssProperties", "type": "array", "items": { "$ref": "CSSProperty" }, "description": "CSS properties in the style." }, - { "name": "shorthandEntries", "type": "array", "items": { "$ref": "ShorthandEntry" }, "description": "Computed values for all shorthands found in the style." }, - { "name": "cssText", "type": "string", "optional": true, "description": "Style declaration text (if available)." }, - { "name": "range", "$ref": "SourceRange", "optional": true, "description": "Style declaration range in the enclosing stylesheet (if available)." } - ], - "description": "CSS style representation." - }, - { - "id": "CSSProperty", - "type": "object", - "properties": [ - { "name": "name", "type": "string", "description": "The property name." }, - { "name": "value", "type": "string", "description": "The property value." }, - { "name": "important", "type": "boolean", "optional": true, "description": "Whether the property has \"!important\" annotation (implies <code>false</code> if absent)." }, - { "name": "implicit", "type": "boolean", "optional": true, "description": "Whether the property is implicit (implies <code>false</code> if absent)." }, - { "name": "text", "type": "string", "optional": true, "description": "The full property text as specified in the style." }, - { "name": "parsedOk", "type": "boolean", "optional": true, "description": "Whether the property is understood by the browser (implies <code>true</code> if absent)." }, - { "name": "disabled", "type": "boolean", "optional": true, "description": "Whether the property is disabled by the user (present for source-based properties only)." }, - { "name": "range", "$ref": "SourceRange", "optional": true, "description": "The entire property range in the enclosing style declaration (if available)." } - ], - "description": "CSS property declaration data." - }, - { - "id": "CSSMedia", - "type": "object", - "properties": [ - { "name": "text", "type": "string", "description": "Media query text." }, - { "name": "source", "type": "string", "enum": ["mediaRule", "importRule", "linkedSheet", "inlineSheet"], "description": "Source of the media query: \"mediaRule\" if specified by a @media rule, \"importRule\" if specified by an @import rule, \"linkedSheet\" if specified by a \"media\" attribute in a linked stylesheet's LINK tag, \"inlineSheet\" if specified by a \"media\" attribute in an inline stylesheet's STYLE tag." }, - { "name": "sourceURL", "type": "string", "optional": true, "description": "URL of the document containing the media query description." }, - { "name": "range", "$ref": "SourceRange", "optional": true, "description": "The associated rule (@media or @import) header range in the enclosing stylesheet (if available)." }, - { "name": "styleSheetId", "$ref": "StyleSheetId", "optional": true, "description": "Identifier of the stylesheet containing this object (if exists)." }, - { "name": "mediaList", "type": "array", "items": { "$ref": "MediaQuery" }, "optional": true, "hidden": true, "description": "Array of media queries." } - ], - "description": "CSS media rule descriptor." - }, - { - "id": "MediaQuery", - "type": "object", - "properties": [ - { "name": "expressions", "type": "array", "items": { "$ref": "MediaQueryExpression" }, "description": "Array of media query expressions." }, - { "name": "active", "type": "boolean", "description": "Whether the media query condition is satisfied." } - ], - "description": "Media query descriptor.", - "hidden": true - }, - { - "id": "MediaQueryExpression", - "type": "object", - "properties": [ - { "name": "value", "type": "number", "description": "Media query expression value."}, - { "name": "unit", "type": "string", "description": "Media query expression units."}, - { "name": "feature", "type": "string", "description": "Media query expression feature."}, - { "name": "valueRange", "$ref": "SourceRange", "optional": true, "description": "The associated range of the value text in the enclosing stylesheet (if available)." }, - { "name": "computedLength", "type": "number", "optional": true, "description": "Computed length of media query expression (if applicable)."} - ], - "description": "Media query expression descriptor.", - "hidden": true - }, - { - "id": "PlatformFontUsage", - "type": "object", - "properties": [ - { "name": "familyName", "type": "string", "description": "Font's family name reported by platform."}, - { "name": "isCustomFont", "type": "boolean", "description": "Indicates if the font was downloaded or resolved locally."}, - { "name": "glyphCount", "type": "number", "description": "Amount of glyphs that were rendered with this font."} - ], - "description": "Information about amount of glyphs that were rendered with given font.", - "hidden": true - }, - { - "id": "CSSKeyframesRule", - "type": "object", - "properties": [ - { "name": "animationName", "$ref": "Value", "description": "Animation name." }, - { "name": "keyframes", "type": "array", "items": { "$ref": "CSSKeyframeRule" }, "description": "List of keyframes." } - ], - "description": "CSS keyframes rule representation." - }, - { - "id": "CSSKeyframeRule", - "type": "object", - "properties": [ - { "name": "styleSheetId", "$ref": "StyleSheetId", "optional": true, "description": "The css style sheet identifier (absent for user agent stylesheet and user-specified stylesheet rules) this rule came from." }, - { "name": "origin", "$ref": "StyleSheetOrigin", "description": "Parent stylesheet's origin."}, - { "name": "keyText", "$ref": "Value", "description": "Associated key text." }, - { "name": "style", "$ref": "CSSStyle", "description": "Associated style declaration." } - ], - "description": "CSS keyframe rule representation." - }, - { - "id": "StyleDeclarationEdit", - "type": "object", - "properties": [ - { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "The css style sheet identifier." }, - { "name": "range", "$ref": "SourceRange", "description": "The range of the style text in the enclosing stylesheet." }, - { "name": "text", "type": "string", "description": "New style text."} - ], - "description": "A descriptor of operation to mutate style declaration text." - } - ], - "commands": [ - { - "name": "enable", - "async": true, - "description": "Enables the CSS agent for the given page. Clients should not assume that the CSS agent has been enabled until the result of this command is received." - }, - { - "name": "disable", - "description": "Disables the CSS agent for the given page." - }, - { - "name": "getMatchedStylesForNode", - "parameters": [ - { "name": "nodeId", "$ref": "DOM.NodeId" } - ], - "returns": [ - { "name": "inlineStyle", "$ref": "CSSStyle", "optional": true, "description": "Inline style for the specified DOM node." }, - { "name": "attributesStyle", "$ref": "CSSStyle", "optional": true, "description": "Attribute-defined element style (e.g. resulting from \"width=20 height=100%\")."}, - { "name": "matchedCSSRules", "type": "array", "items": { "$ref": "RuleMatch" }, "optional": true, "description": "CSS rules matching this node, from all applicable stylesheets." }, - { "name": "pseudoElements", "type": "array", "items": { "$ref": "PseudoElementMatches" }, "optional": true, "description": "Pseudo style matches for this node." }, - { "name": "inherited", "type": "array", "items": { "$ref": "InheritedStyleEntry" }, "optional": true, "description": "A chain of inherited styles (from the immediate node parent up to the DOM tree root)." }, - { "name": "cssKeyframesRules", "type": "array", "items": { "$ref": "CSSKeyframesRule" }, "optional": true, "description": "A list of CSS keyframed animations matching this node." } - ], - "description": "Returns requested styles for a DOM node identified by <code>nodeId</code>." - }, - { - "name": "getInlineStylesForNode", - "parameters": [ - { "name": "nodeId", "$ref": "DOM.NodeId" } - ], - "returns": [ - { "name": "inlineStyle", "$ref": "CSSStyle", "optional": true, "description": "Inline style for the specified DOM node." }, - { "name": "attributesStyle", "$ref": "CSSStyle", "optional": true, "description": "Attribute-defined element style (e.g. resulting from \"width=20 height=100%\")."} - ], - "description": "Returns the styles defined inline (explicitly in the \"style\" attribute and implicitly, using DOM attributes) for a DOM node identified by <code>nodeId</code>." - }, - { - "name": "getComputedStyleForNode", - "parameters": [ - { "name": "nodeId", "$ref": "DOM.NodeId" } - ], - "returns": [ - { "name": "computedStyle", "type": "array", "items": { "$ref": "CSSComputedStyleProperty" }, "description": "Computed style for the specified DOM node." } - ], - "description": "Returns the computed style for a DOM node identified by <code>nodeId</code>." - }, - { - "name": "getPlatformFontsForNode", - "parameters": [ - { "name": "nodeId", "$ref": "DOM.NodeId" } - ], - "returns": [ - { "name": "fonts", "type": "array", "items": { "$ref": "PlatformFontUsage" }, "description": "Usage statistics for every employed platform font." } - ], - "description": "Requests information about platform fonts which we used to render child TextNodes in the given node.", - "hidden": true - }, - { - "name": "getStyleSheetText", - "parameters": [ - { "name": "styleSheetId", "$ref": "StyleSheetId" } - ], - "returns": [ - { "name": "text", "type": "string", "description": "The stylesheet text." } - ], - "description": "Returns the current textual content and the URL for a stylesheet." - }, - { - "name": "setStyleSheetText", - "parameters": [ - { "name": "styleSheetId", "$ref": "StyleSheetId" }, - { "name": "text", "type": "string" } - ], - "returns": [ - { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." } - ], - "description": "Sets the new stylesheet text." - }, - { - "name": "setRuleSelector", - "parameters": [ - { "name": "styleSheetId", "$ref": "StyleSheetId" }, - { "name": "range", "$ref": "SourceRange" }, - { "name": "selector", "type": "string" } - ], - "returns": [ - { "name": "selectorList", "$ref": "SelectorList", "description": "The resulting selector list after modification." } - ], - "description": "Modifies the rule selector." - }, - { - "name": "setKeyframeKey", - "parameters": [ - { "name": "styleSheetId", "$ref": "StyleSheetId" }, - { "name": "range", "$ref": "SourceRange" }, - { "name": "keyText", "type": "string" } - ], - "returns": [ - { "name": "keyText", "$ref": "Value", "description": "The resulting key text after modification." } - ], - "description": "Modifies the keyframe rule key text." - }, - { - "name": "setStyleTexts", - "parameters": [ - { "name": "edits", "type": "array", "items": { "$ref": "StyleDeclarationEdit" }} - ], - "returns": [ - { "name": "styles", "type": "array", "items": { "$ref": "CSSStyle" }, "description": "The resulting styles after modification." } - ], - "description": "Applies specified style edits one after another in the given order." - }, - { - "name": "setMediaText", - "parameters": [ - { "name": "styleSheetId", "$ref": "StyleSheetId" }, - { "name": "range", "$ref": "SourceRange" }, - { "name": "text", "type": "string" } - ], - "returns": [ - { "name": "media", "$ref": "CSSMedia", "description": "The resulting CSS media rule after modification." } - ], - "description": "Modifies the rule selector." - }, - { - "name": "createStyleSheet", - "parameters": [ - { "name": "frameId", "$ref": "Page.FrameId", "description": "Identifier of the frame where \"via-inspector\" stylesheet should be created."} - ], - "returns": [ - { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "Identifier of the created \"via-inspector\" stylesheet." } - ], - "description": "Creates a new special \"via-inspector\" stylesheet in the frame with given <code>frameId</code>." - }, - { - "name": "addRule", - "parameters": [ - { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "The css style sheet identifier where a new rule should be inserted." }, - { "name": "ruleText", "type": "string", "description": "The text of a new rule." }, - { "name": "location", "$ref": "SourceRange", "description": "Text position of a new rule in the target style sheet." } - ], - "returns": [ - { "name": "rule", "$ref": "CSSRule", "description": "The newly created rule." } - ], - "description": "Inserts a new rule with the given <code>ruleText</code> in a stylesheet with given <code>styleSheetId</code>, at the position specified by <code>location</code>." - }, - { - "name": "forcePseudoState", - "parameters": [ - { "name": "nodeId", "$ref": "DOM.NodeId", "description": "The element id for which to force the pseudo state." }, - { "name": "forcedPseudoClasses", "type": "array", "items": { "type": "string", "enum": ["active", "focus", "hover", "visited"] }, "description": "Element pseudo classes to force when computing the element's style." } - ], - "description": "Ensures that the given node will have specified pseudo-classes whenever its style is computed by the browser." - }, - { - "name": "getMediaQueries", - "returns": [ - { "name": "medias", "type": "array", "items": { "$ref": "CSSMedia" } } - ], - "description": "Returns all media queries parsed by the rendering engine.", - "hidden": true - }, - { - "name": "setEffectivePropertyValueForNode", - "parameters": [ - { "name": "nodeId", "$ref": "DOM.NodeId", "description": "The element id for which to set property." }, - { "name": "propertyName", "type": "string"}, - { "name": "value", "type": "string"} - ], - "description": "Find a rule with the given active property for the given node and set the new value for this property", - "hidden": true - }, - { - "name": "getBackgroundColors", - "parameters": [ - { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Id of the node to get background colors for." } - ], - "returns": [ - { "name": "backgroundColors", "type": "array", "items": { "type": "string" }, "description": "The range of background colors behind this element, if it contains any visible text. If no visible text is present, this will be undefined. In the case of a flat background color, this will consist of simply that color. In the case of a gradient, this will consist of each of the color stops. For anything more complicated, this will be an empty array. Images will be ignored (as if the image had failed to load).", "optional": true } - ], - "hidden": true - } - ], - "events": [ - { - "name": "mediaQueryResultChanged", - "description": "Fires whenever a MediaQuery result changes (for example, after a browser window has been resized.) The current implementation considers only viewport-dependent media features." - }, - { - "name": "styleSheetChanged", - "parameters": [ - { "name": "styleSheetId", "$ref": "StyleSheetId" } - ], - "description": "Fired whenever a stylesheet is changed as a result of the client operation." - }, - { - "name": "styleSheetAdded", - "parameters": [ - { "name": "header", "$ref": "CSSStyleSheetHeader", "description": "Added stylesheet metainfo." } - ], - "description": "Fired whenever an active document stylesheet is added." - }, - { - "name": "styleSheetRemoved", - "parameters": [ - { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "Identifier of the removed stylesheet." } - ], - "description": "Fired whenever an active document stylesheet is removed." - }, - { - "name": "layoutEditorChange", - "parameters": [ - { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "Identifier of the stylesheet where the modification occurred." }, - { "name": "changeRange", "$ref": "SourceRange", "description": "Range where the modification occurred." } - ] - } - ] - }, - { - "domain": "IO", - "description": "Input/Output operations for streams produced by DevTools.", - "hidden": true, - "types": [ - { - "id": "StreamHandle", - "type": "string" - } - ], - "commands": [ - { - "name": "read", - "description": "Read a chunk of the stream", - "async": true, - "parameters": [ - { "name": "handle", "$ref": "StreamHandle", "description": "Handle of the stream to read." }, - { "name": "offset", "type": "integer", "optional": true, "description": "Seek to the specified offset before reading (if not specificed, proceed with offset following the last read)." }, - { "name": "size", "type": "integer", "optional": true, "description": "Maximum number of bytes to read (left upon the agent discretion if not specified)." } - ], - "returns": [ - { "name": "data", "type": "string", "description": "Data that were read." }, - { "name": "eof", "type": "boolean", "description": "Set if the end-of-file condition occured while reading." } - ], - "handlers": ["browser"] - }, - { - "name": "close", - "description": "Close the stream, discard any temporary backing storage.", - "parameters": [ - { "name": "handle", "$ref": "StreamHandle", "description": "Handle of the stream to close." } - ], - "handlers": ["browser"] - } - ] - }, - { - "domain": "Debugger", - "description": "Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing breakpoints, stepping through execution, exploring stack traces, etc.", - "types": [ - { - "id": "BreakpointId", - "type": "string", - "description": "Breakpoint identifier." - }, - { - "id": "CallFrameId", - "type": "string", - "description": "Call frame identifier." - }, - { - "id": "Location", - "type": "object", - "properties": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Script identifier as reported in the <code>Debugger.scriptParsed</code>." }, - { "name": "lineNumber", "type": "integer", "description": "Line number in the script (0-based)." }, - { "name": "columnNumber", "type": "integer", "optional": true, "description": "Column number in the script (0-based)." } - ], - "description": "Location in the source code." - }, - { - "id": "ScriptPosition", - "hidden": true, - "type": "object", - "properties": [ - { "name": "line", "type": "integer" }, - { "name": "column", "type": "integer" } - ], - "description": "Location in the source code." - }, - { - "id": "FunctionDetails", - "hidden": true, - "type": "object", - "properties": [ - { "name": "location", "$ref": "Location", "optional": true, "description": "Location of the function, none for native functions." }, - { "name": "functionName", "type": "string", "description": "Name of the function." }, - { "name": "isGenerator", "type": "boolean", "description": "Whether this is a generator function." }, - { "name": "scopeChain", "type": "array", "optional": true, "items": { "$ref": "Scope" }, "description": "Scope chain for this closure." } - ], - "description": "Information about the function." - }, - { - "id": "GeneratorObjectDetails", - "hidden": true, - "type": "object", - "properties": [ - { "name": "function", "$ref": "Runtime.RemoteObject", "description": "Generator function." }, - { "name": "functionName", "type": "string", "description": "Name of the generator function." }, - { "name": "status", "type": "string", "enum": ["running", "suspended", "closed"], "description": "Current generator object status." }, - { "name": "location", "$ref": "Location", "optional": true, "description": "If suspended, location where generator function was suspended (e.g. location of the last 'yield'). Otherwise, location of the generator function." } - ], - "description": "Information about the generator object." - }, - { - "id": "CollectionEntry", - "hidden": true, - "type": "object", - "properties": [ - { "name": "key", "$ref": "Runtime.RemoteObject", "optional": true, "description": "Entry key of a map-like collection, otherwise not provided." }, - { "name": "value", "$ref": "Runtime.RemoteObject", "description": "Entry value." } - ], - "description": "Collection entry." - }, - { - "id": "CallFrame", - "type": "object", - "properties": [ - { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier. This identifier is only valid while the virtual machine is paused." }, - { "name": "functionName", "type": "string", "description": "Name of the JavaScript function called on this call frame." }, - { "name": "functionLocation", "$ref": "Location", "optional": true, "hidden": true, "description": "Location in the source code." }, - { "name": "location", "$ref": "Location", "description": "Location in the source code." }, - { "name": "scopeChain", "type": "array", "items": { "$ref": "Scope" }, "description": "Scope chain for this call frame." }, - { "name": "this", "$ref": "Runtime.RemoteObject", "description": "<code>this</code> object for this call frame." }, - { "name": "returnValue", "$ref": "Runtime.RemoteObject", "optional": true, "hidden": true, "description": "The value being returned, if the function is at return point." } - ], - "description": "JavaScript call frame. Array of call frames form the call stack." - }, - { - "id": "Scope", - "type": "object", - "properties": [ - { "name": "type", "type": "string", "enum": ["global", "local", "with", "closure", "catch", "block", "script"], "description": "Scope type." }, - { "name": "object", "$ref": "Runtime.RemoteObject", "description": "Object representing the scope. For <code>global</code> and <code>with</code> scopes it represents the actual object; for the rest of the scopes, it is artificial transient object enumerating scope variables as its properties." }, - { "name": "name", "type": "string", "optional": true, "hidden": true }, - { "name": "startLocation", "$ref": "Location", "optional": true, "hidden": true, "description": "Location in the source code where scope starts" }, - { "name": "endLocation", "$ref": "Location", "optional": true, "hidden": true, "description": "Location in the source code where scope ends" } - ], - "description": "Scope description." - }, - { - "id": "SetScriptSourceError", - "type": "object", - "properties": [ - { "name": "message", "type": "string", "description": "Compiler error message" }, - { "name": "lineNumber", "type": "integer", "description": "Compile error line number (1-based)" }, - { "name": "columnNumber", "type": "integer", "description": "Compile error column number (1-based)" } - ], - "description": "Error data for setScriptSource command. Contains uncompilable script source error.", - "hidden": true - }, - { - "id": "SearchMatch", - "type": "object", - "description": "Search match for resource.", - "properties": [ - { "name": "lineNumber", "type": "number", "description": "Line number in resource content." }, - { "name": "lineContent", "type": "string", "description": "Line with match content." } - ], - "hidden": true - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables debugger for the given page. Clients should not assume that the debugging has been enabled until the result for this command is received." - }, - { - "name": "disable", - "description": "Disables debugger for given page." - }, - { - "name": "setBreakpointsActive", - "parameters": [ - { "name": "active", "type": "boolean", "description": "New value for breakpoints active state." } - ], - "description": "Activates / deactivates all breakpoints on the page." - }, - { - "name": "setSkipAllPauses", - "hidden": true, - "parameters": [ - { "name": "skipped", "type": "boolean", "description": "New value for skip pauses state." } - ], - "description": "Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc)." - }, - { - "name": "setBreakpointByUrl", - "parameters": [ - { "name": "lineNumber", "type": "integer", "description": "Line number to set breakpoint at." }, - { "name": "url", "type": "string", "optional": true, "description": "URL of the resources to set breakpoint on." }, - { "name": "urlRegex", "type": "string", "optional": true, "description": "Regex pattern for the URLs of the resources to set breakpoints on. Either <code>url</code> or <code>urlRegex</code> must be specified." }, - { "name": "columnNumber", "type": "integer", "optional": true, "description": "Offset in the line to set breakpoint at." }, - { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." } - ], - "returns": [ - { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, - { "name": "locations", "type": "array", "items": { "$ref": "Location" }, "description": "List of the locations this breakpoint resolved into upon addition." } - ], - "description": "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this command is issued, all existing parsed scripts will have breakpoints resolved and returned in <code>locations</code> property. Further matching script parsing will result in subsequent <code>breakpointResolved</code> events issued. This logical breakpoint will survive page reloads." - }, - { - "name": "setBreakpoint", - "parameters": [ - { "name": "location", "$ref": "Location", "description": "Location to set breakpoint in." }, - { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." } - ], - "returns": [ - { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, - { "name": "actualLocation", "$ref": "Location", "description": "Location this breakpoint resolved into." } - ], - "description": "Sets JavaScript breakpoint at a given location." - }, - { - "name": "removeBreakpoint", - "parameters": [ - { "name": "breakpointId", "$ref": "BreakpointId" } - ], - "description": "Removes JavaScript breakpoint." - }, - { - "name": "continueToLocation", - "parameters": [ - { "name": "location", "$ref": "Location", "description": "Location to continue to." }, - { "name": "interstatementLocation", "type": "boolean", "optional": true, "hidden": true, "description": "Allows breakpoints at the intemediate positions inside statements." } - ], - "description": "Continues execution until specific location is reached." - }, - { - "name": "stepOver", - "description": "Steps over the statement." - }, - { - "name": "stepInto", - "description": "Steps into the function call." - }, - { - "name": "stepOut", - "description": "Steps out of the function call." - }, - { - "name": "pause", - "description": "Stops on the next JavaScript statement." - }, - { - "name": "resume", - "description": "Resumes JavaScript execution." - }, - { - "name": "searchInContent", - "parameters": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to search in." }, - { "name": "query", "type": "string", "description": "String to search for." }, - { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." }, - { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." } - ], - "returns": [ - { "name": "result", "type": "array", "items": { "$ref": "SearchMatch" }, "description": "List of search matches." } - ], - "description": "Searches for given string in script content." - }, - { - "name": "canSetScriptSource", - "returns": [ - { "name": "result", "type": "boolean", "description": "True if <code>setScriptSource</code> is supported." } - ], - "description": "Always returns true." - }, - { - "name": "setScriptSource", - "parameters": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to edit." }, - { "name": "scriptSource", "type": "string", "description": "New content of the script." }, - { "name": "preview", "type": "boolean", "optional": true, "description": " If true the change will not actually be applied. Preview mode may be used to get result description without actually modifying the code.", "hidden": true } - ], - "returns": [ - { "name": "callFrames", "type": "array", "optional": true, "items": { "$ref": "CallFrame" }, "description": "New stack trace in case editing has happened while VM was stopped." }, - { "name": "stackChanged", "type": "boolean", "optional": true, "description": "Whether current call stack was modified after applying the changes.", "hidden": true }, - { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any.", "hidden": true }, - { "name": "compileError", "optional": true, "$ref": "SetScriptSourceError", "description": "Error data if any." } - ], - "description": "Edits JavaScript source live." - }, - { - "name": "restartFrame", - "parameters": [ - { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." } - ], - "returns": [ - { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "New stack trace." }, - { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." } - ], - "hidden": true, - "description": "Restarts particular call frame from the beginning." - }, - { - "name": "getScriptSource", - "parameters": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to get source for." } - ], - "returns": [ - { "name": "scriptSource", "type": "string", "description": "Script source." } - ], - "description": "Returns source for the script with given id." - }, - { - "name": "getFunctionDetails", - "hidden": true, - "parameters": [ - { "name": "functionId", "$ref": "Runtime.RemoteObjectId", "description": "Id of the function to get details for." } - ], - "returns": [ - { "name": "details", "$ref": "FunctionDetails", "description": "Information about the function." } - ], - "description": "Returns detailed information on given function." - }, - { - "name": "getGeneratorObjectDetails", - "hidden": true, - "parameters": [ - { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Id of the generator object to get details for." } - ], - "returns": [ - { "name": "details", "$ref": "GeneratorObjectDetails", "description": "Information about the generator object." } - ], - "description": "Returns detailed information on given generator object." - }, - { - "name": "getCollectionEntries", - "hidden": true, - "parameters": [ - { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Id of the collection to get entries for." } - ], - "returns": [ - { "name": "entries", "type": "array", "items": { "$ref": "CollectionEntry" }, "description": "Array of collection entries." } - ], - "description": "Returns entries of given collection." - }, - { - "name": "setPauseOnExceptions", - "parameters": [ - { "name": "state", "type": "string", "enum": ["none", "uncaught", "all"], "description": "Pause on exceptions mode." } - ], - "description": "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions or no exceptions. Initial pause on exceptions state is <code>none</code>." - }, - { - "name": "evaluateOnCallFrame", - "parameters": [ - { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." }, - { "name": "expression", "type": "string", "description": "Expression to evaluate." }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "String object group name to put result into (allows rapid releasing resulting object handles using <code>releaseObjectGroup</code>)." }, - { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false.", "hidden": true }, - { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether evaluation should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true }, - { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, - { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." } - ], - "returns": [ - { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." }, - { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }, - { "name": "exceptionDetails", "$ref": "Runtime.ExceptionDetails", "optional": true, "hidden": true, "description": "Exception details."} - ], - "description": "Evaluates expression on a given call frame." - }, - { - "name": "setVariableValue", - "parameters": [ - { "name": "scopeNumber", "type": "integer", "description": "0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch' scope types are allowed. Other scopes could be manipulated manually." }, - { "name": "variableName", "type": "string", "description": "Variable name." }, - { "name": "newValue", "$ref": "Runtime.CallArgument", "description": "New variable value." }, - { "name": "callFrameId", "$ref": "CallFrameId", "description": "Id of callframe that holds variable." } - ], - "hidden": true, - "description": "Changes value of variable in a callframe. Object-based scopes are not supported and must be mutated manually." - }, - { - "name": "getBacktrace", - "returns": [ - { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." }, - { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." } - ], - "hidden": true, - "description": "Returns call stack including variables changed since VM was paused. VM must be paused." - }, - { - "name": "setAsyncCallStackDepth", - "parameters": [ - { "name": "maxDepth", "type": "integer", "description": "Maximum depth of async call stacks. Setting to <code>0</code> will effectively disable collecting async call stacks (default)." } - ], - "hidden": true, - "description": "Enables or disables async call stacks tracking." - }, - { - "name": "setBlackboxPatterns", - "parameters": [ - { "name": "patterns", "type": "array", "items": { "type": "string" }, "description": "Array of regexps that will be used to check script url for blackbox state." } - ], - "hidden": true, - "description": "Replace previous blackbox patterns with passed ones. Forces backend to skip stepping/pausing in scripts with url matching one of the patterns. VM will try to leave blackboxed script by performing 'step in' several times, finally resorting to 'step out' if unsuccessful." - }, - { - "name": "setBlackboxedRanges", - "parameters": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script." }, - { "name": "positions", "type": "array", "items": { "$ref": "ScriptPosition" } } - ], - "hidden": true, - "description": "Makes backend skip steps in the script in blackboxed ranges. VM will try leave blacklisted scripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful. Positions array contains positions where blackbox state is changed. First interval isn't blackboxed. Array should be sorted." - } - ], - "events": [ - { - "name": "scriptParsed", - "parameters": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." }, - { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, - { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, - { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, - { "name": "endLine", "type": "integer", "description": "Last line of the script." }, - { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, - { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context.", "hidden": true }, - { "name": "hash", "type": "string", "hidden": true, "description": "Content hash of the script."}, - { "name": "isContentScript", "type": "boolean", "optional": true, "description": "Determines whether this script is a user extension script." }, - { "name": "isInternalScript", "type": "boolean", "optional": true, "description": "Determines whether this script is an internal script.", "hidden": true }, - { "name": "isLiveEdit", "type": "boolean", "optional": true, "description": "True, if this script is generated as a result of the live edit operation.", "hidden": true }, - { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, - { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "hidden": true }, - { "name": "deprecatedCommentWasUsed", "type": "boolean", "optional": true, "hidden": true, "description": "True, if '//@ sourceURL' or '//@ sourceMappingURL' was used."} - ], - "description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger." - }, - { - "name": "scriptFailedToParse", - "parameters": [ - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." }, - { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, - { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, - { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, - { "name": "endLine", "type": "integer", "description": "Last line of the script." }, - { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, - { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context.", "hidden": true }, - { "name": "hash", "type": "string", "hidden": true, "description": "Content hash of the script."}, - { "name": "isContentScript", "type": "boolean", "optional": true, "description": "Determines whether this script is a user extension script." }, - { "name": "isInternalScript", "type": "boolean", "optional": true, "description": "Determines whether this script is an internal script.", "hidden": true }, - { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, - { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "hidden": true }, - { "name": "deprecatedCommentWasUsed", "type": "boolean", "optional": true, "hidden": true, "description": "True, if '//@ sourceURL' or '//@ sourceMappingURL' was used."} - ], - "description": "Fired when virtual machine fails to parse the script." - }, - { - "name": "breakpointResolved", - "parameters": [ - { "name": "breakpointId", "$ref": "BreakpointId", "description": "Breakpoint unique identifier." }, - { "name": "location", "$ref": "Location", "description": "Actual breakpoint location." } - ], - "description": "Fired when breakpoint is resolved to an actual script and location." - }, - { - "name": "paused", - "parameters": [ - { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." }, - { "name": "reason", "type": "string", "enum": [ "XHR", "DOM", "EventListener", "exception", "assert", "CSPViolation", "debugCommand", "promiseRejection", "other" ], "description": "Pause reason." }, - { "name": "data", "type": "object", "optional": true, "description": "Object containing break-specific auxiliary properties." }, - { "name": "hitBreakpoints", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Hit breakpoints IDs", "hidden": true }, - { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any.", "hidden": true } - ], - "description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria." - }, - { - "name": "resumed", - "description": "Fired when the virtual machine resumed execution." - } - ] - }, - { - "domain": "DOMDebugger", - "description": "DOM debugging allows setting breakpoints on particular DOM operations and events. JavaScript execution will stop on these operations as if there was a regular breakpoint set.", - "types": [ - { - "id": "DOMBreakpointType", - "type": "string", - "enum": ["subtree-modified", "attribute-modified", "node-removed"], - "description": "DOM breakpoint type." - }, - { - "id": "EventListener", - "type": "object", - "description": "Object event listener.", - "properties": [ - { "name": "type", "type": "string", "description": "<code>EventListener</code>'s type." }, - { "name": "useCapture", "type": "boolean", "description": "<code>EventListener</code>'s useCapture." }, - { "name": "passive", "type": "boolean", "description": "<code>EventListener</code>'s passive flag." }, - { "name": "location", "$ref": "Debugger.Location", "description": "Handler code location." }, - { "name": "handler", "$ref": "Runtime.RemoteObject", "optional": true, "description": "Event handler function value." }, - { "name": "originalHandler", "$ref": "Runtime.RemoteObject", "optional": true, "description": "Event original handler function value." } - ], - "hidden": true - } - ], - "commands": [ - { - "name": "setDOMBreakpoint", - "parameters": [ - { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Identifier of the node to set breakpoint on." }, - { "name": "type", "$ref": "DOMBreakpointType", "description": "Type of the operation to stop upon." } - ], - "description": "Sets breakpoint on particular operation with DOM." - }, - { - "name": "removeDOMBreakpoint", - "parameters": [ - { "name": "nodeId", "$ref": "DOM.NodeId", "description": "Identifier of the node to remove breakpoint from." }, - { "name": "type", "$ref": "DOMBreakpointType", "description": "Type of the breakpoint to remove." } - ], - "description": "Removes DOM breakpoint that was set using <code>setDOMBreakpoint</code>." - }, - { - "name": "setEventListenerBreakpoint", - "parameters": [ - { "name": "eventName", "type": "string", "description": "DOM Event name to stop on (any DOM event will do)." }, - { "name": "targetName", "type": "string", "optional": true, "description": "EventTarget interface name to stop on. If equal to <code>\"*\"</code> or not provided, will stop on any EventTarget.", "hidden": true } - ], - "description": "Sets breakpoint on particular DOM event." - }, - { - "name": "removeEventListenerBreakpoint", - "parameters": [ - { "name": "eventName", "type": "string", "description": "Event name." }, - { "name": "targetName", "type": "string", "optional": true, "description": "EventTarget interface name.", "hidden": true } - ], - "description": "Removes breakpoint on particular DOM event." - }, - { - "name": "setInstrumentationBreakpoint", - "parameters": [ - { "name": "eventName", "type": "string", "description": "Instrumentation name to stop on." } - ], - "description": "Sets breakpoint on particular native event.", - "hidden": true - }, - { - "name": "removeInstrumentationBreakpoint", - "parameters": [ - { "name": "eventName", "type": "string", "description": "Instrumentation name to stop on." } - ], - "description": "Removes breakpoint on particular native event.", - "hidden": true - }, - { - "name": "setXHRBreakpoint", - "parameters": [ - { "name": "url", "type": "string", "description": "Resource URL substring. All XHRs having this substring in the URL will get stopped upon." } - ], - "description": "Sets breakpoint on XMLHttpRequest." - }, - { - "name": "removeXHRBreakpoint", - "parameters": [ - { "name": "url", "type": "string", "description": "Resource URL substring." } - ], - "description": "Removes breakpoint from XMLHttpRequest." - }, - { - "name": "getEventListeners", - "hidden": true, - "parameters": [ - { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Identifier of the object to return listeners for." } - ], - "returns": [ - { "name": "listeners", "type": "array", "items": { "$ref": "EventListener" }, "description": "Array of relevant listeners." } - ], - "description": "Returns event listeners of the given object." - } - ] - }, - { - "domain": "Profiler", - "hidden": true, - "types": [ - { - "id": "CPUProfileNode", - "type": "object", - "description": "CPU Profile node. Holds callsite information, execution statistics and child nodes.", - "properties": [ - { "name": "functionName", "type": "string", "description": "Function name." }, - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Script identifier." }, - { "name": "url", "type": "string", "description": "URL." }, - { "name": "lineNumber", "type": "integer", "description": "1-based line number of the function start position." }, - { "name": "columnNumber", "type": "integer", "description": "1-based column number of the function start position." }, - { "name": "hitCount", "type": "integer", "description": "Number of samples where this node was on top of the call stack." }, - { "name": "callUID", "type": "number", "description": "Call UID." }, - { "name": "children", "type": "array", "items": { "$ref": "CPUProfileNode" }, "description": "Child nodes." }, - { "name": "deoptReason", "type": "string", "description": "The reason of being not optimized. The function may be deoptimized or marked as don't optimize."}, - { "name": "id", "type": "integer", "description": "Unique id of the node." }, - { "name": "positionTicks", "type": "array", "items": { "$ref": "PositionTickInfo" }, "description": "An array of source position ticks." } - ] - }, - { - "id": "CPUProfile", - "type": "object", - "description": "Profile.", - "properties": [ - { "name": "head", "$ref": "CPUProfileNode" }, - { "name": "startTime", "type": "number", "description": "Profiling start time in seconds." }, - { "name": "endTime", "type": "number", "description": "Profiling end time in seconds." }, - { "name": "samples", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Ids of samples top nodes." }, - { "name": "timestamps", "optional": true, "type": "array", "items": { "type": "number" }, "description": "Timestamps of the samples in microseconds." } - ] - }, - { - "id": "PositionTickInfo", - "type": "object", - "description": "Specifies a number of samples attributed to a certain source position.", - "properties": [ - { "name": "line", "type": "integer", "description": "Source line number (1-based)." }, - { "name": "ticks", "type": "integer", "description": "Number of samples attributed to the source line." } - ] - } - ], - "commands": [ - { - "name": "enable" - }, - { - "name": "disable" - }, - { - "name": "setSamplingInterval", - "parameters": [ - { "name": "interval", "type": "integer", "description": "New sampling interval in microseconds." } - ], - "description": "Changes CPU profiler sampling interval. Must be called before CPU profiles recording started." - }, - { - "name": "start" - }, - { - "name": "stop", - "returns": [ - { "name": "profile", "$ref": "CPUProfile", "description": "Recorded profile." } - ] - } - ], - "events": [ - { - "name": "consoleProfileStarted", - "parameters": [ - { "name": "id", "type": "string" }, - { "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profile()." }, - { "name": "title", "type": "string", "optional": true, "description": "Profile title passed as argument to console.profile()." } - - ], - "description": "Sent when new profile recodring is started using console.profile() call." - }, - { - "name": "consoleProfileFinished", - "parameters": [ - { "name": "id", "type": "string" }, - { "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profileEnd()." }, - { "name": "profile", "$ref": "CPUProfile" }, - { "name": "title", "type": "string", "optional": true, "description": "Profile title passed as argunet to console.profile()." } - ] - } - ] - }, - { - "domain": "HeapProfiler", - "hidden": true, - "types": [ - { - "id": "HeapSnapshotObjectId", - "type": "string", - "description": "Heap snapshot object id." - }, - { - "id": "SamplingHeapProfileNode", - "type": "object", - "description": "Sampling Heap Profile node. Holds callsite information, allocation statistics and child nodes.", - "properties": [ - { "name": "functionName", "type": "string", "description": "Function name." }, - { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Script identifier." }, - { "name": "url", "type": "string", "description": "URL." }, - { "name": "lineNumber", "type": "integer", "description": "1-based line number of the function start position." }, - { "name": "columnNumber", "type": "integer", "description": "1-based column number of the function start position." }, - { "name": "selfSize", "type": "number", "description": "Allocations size in bytes for the node excluding children." }, - { "name": "children", "type": "array", "items": { "$ref": "SamplingHeapProfileNode" }, "description": "Child nodes." } - ] - }, - { - "id": "SamplingHeapProfile", - "type": "object", - "description": "Profile.", - "properties": [ - { "name": "head", "$ref": "SamplingHeapProfileNode" } - ] - } - ], - "commands": [ - { - "name": "enable" - }, - { - "name": "disable" - }, - { - "name": "startTrackingHeapObjects", - "parameters": [ - { "name": "trackAllocations", "type": "boolean", "optional": true } - ] - }, - { - "name": "stopTrackingHeapObjects", - "parameters": [ - { "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken when the tracking is stopped." } - ] - - }, - { - "name": "takeHeapSnapshot", - "parameters": [ - { "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken." } - ] - }, - { - "name": "collectGarbage" - }, - { - "name": "getObjectByHeapObjectId", - "parameters": [ - { "name": "objectId", "$ref": "HeapSnapshotObjectId" }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." } - ], - "returns": [ - { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Evaluation result." } - ] - }, - { - "name": "addInspectedHeapObject", - "parameters": [ - { "name": "heapObjectId", "$ref": "HeapSnapshotObjectId", "description": "Heap snapshot object id to be accessible by means of $x command line API." } - ], - "description": "Enables console to refer to the node with given id via $x (see Command Line API for more details $x functions)." - }, - { - "name": "getHeapObjectId", - "parameters": [ - { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Identifier of the object to get heap object id for." } - ], - "returns": [ - { "name": "heapSnapshotObjectId", "$ref": "HeapSnapshotObjectId", "description": "Id of the heap snapshot object corresponding to the passed remote object id." } - ] - }, - { - "name": "startSampling", - "parameters": [ - { "name": "samplingInterval", "type": "number", "optional": true, "description": "Average sample interval in bytes. Poisson distribution is used for the intervals. The default value is 32768 bytes." } - ] - }, - { - "name": "stopSampling", - "returns": [ - { "name": "profile", "$ref": "SamplingHeapProfile", "description": "Recorded sampling heap profile." } - ] - } - ], - "events": [ - { - "name": "addHeapSnapshotChunk", - "parameters": [ - { "name": "chunk", "type": "string" } - ] - }, - { - "name": "resetProfiles" - }, - { - "name": "reportHeapSnapshotProgress", - "parameters": [ - { "name": "done", "type": "integer" }, - { "name": "total", "type": "integer" }, - { "name": "finished", "type": "boolean", "optional": true } - ] - }, - { - "name": "lastSeenObjectId", - "description": "If heap objects tracking has been started then backend regulary sends a current value for last seen object id and corresponding timestamp. If the were changes in the heap since last event then one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event.", - "parameters": [ - { "name": "lastSeenObjectId", "type": "integer" }, - { "name": "timestamp", "type": "number" } - ] - }, - { - "name": "heapStatsUpdate", - "description": "If heap objects tracking has been started then backend may send update for one or more fragments", - "parameters": [ - { "name": "statsUpdate", "type": "array", "items": { "type": "integer" }, "description": "An array of triplets. Each triplet describes a fragment. The first integer is the fragment index, the second integer is a total count of objects for the fragment, the third integer is a total size of the objects for the fragment."} - ] - } - ] - }, - { - "domain": "Worker", - "hidden": true, - "types": [], - "commands": [ - { - "name": "enable" - }, - { - "name": "disable" - }, - { - "name": "sendMessageToWorker", - "parameters": [ - { "name": "workerId", "type": "string" }, - { "name": "message", "type": "string" } - ] - }, - { - "name": "setWaitForDebuggerOnStart", - "parameters": [ - { "name": "value", "type": "boolean" } - ] - } - ], - "events": [ - { - "name": "workerCreated", - "parameters": [ - { "name": "workerId", "type": "string" }, - { "name": "url", "type": "string" }, - { "name": "waitingForDebugger", "type": "boolean" } - ] - }, - { - "name": "workerTerminated", - "parameters": [ - { "name": "workerId", "type": "string" } - ] - }, - { - "name": "dispatchMessageFromWorker", - "parameters": [ - { "name": "workerId", "type": "string" }, - { "name": "message", "type": "string" } - ] - } - ] - }, - { - "domain": "ServiceWorker", - "hidden": true, - "types": [ - { - "id": "ServiceWorkerRegistration", - "type": "object", - "description": "ServiceWorker registration.", - "properties": [ - { "name": "registrationId", "type": "string" }, - { "name": "scopeURL", "type": "string" }, - { "name": "isDeleted", "type": "boolean" } - ] - }, - { - "id": "ServiceWorkerVersionRunningStatus", - "type": "string", - "enum": ["stopped", "starting", "running", "stopping"] - }, - { - "id": "ServiceWorkerVersionStatus", - "type": "string", - "enum": ["new", "installing", "installed", "activating", "activated", "redundant"] - }, - { - "id": "TargetID", - "type": "string" - }, - { - "id": "ServiceWorkerVersion", - "type": "object", - "description": "ServiceWorker version.", - "properties": [ - { "name": "versionId", "type": "string" }, - { "name": "registrationId", "type": "string" }, - { "name": "scriptURL", "type": "string" }, - { "name": "runningStatus", "$ref": "ServiceWorkerVersionRunningStatus" }, - { "name": "status", "$ref": "ServiceWorkerVersionStatus" }, - { "name": "scriptLastModified", "type": "number", "optional": true, "description": "The Last-Modified header value of the main script." }, - { "name": "scriptResponseTime", "type": "number", "optional": true, "description": "The time at which the response headers of the main script were received from the server. For cached script it is the last time the cache entry was validated." }, - { "name": "controlledClients", "type": "array", "optional": true, "items": { "$ref": "TargetID" } } - ] - }, - { - "id": "ServiceWorkerErrorMessage", - "type": "object", - "description": "ServiceWorker error message.", - "properties": [ - { "name": "errorMessage", "type": "string" }, - { "name": "registrationId", "type": "string" }, - { "name": "versionId", "type": "string" }, - { "name": "sourceURL", "type": "string" }, - { "name": "lineNumber", "type": "integer" }, - { "name": "columnNumber", "type": "integer" } - ] - }, - { - "id": "TargetInfo", - "type": "object", - "properties": [ - { "name": "id", "$ref": "TargetID" }, - { "name": "type", "type": "string" }, - { "name": "title", "type": "string" }, - { "name": "url", "type": "string" } - ] - } - ], - "commands": [ - { - "name": "enable", - "handlers": ["browser"] - }, - { - "name": "disable", - "handlers": ["browser"] - }, - { - "name": "sendMessage", - "parameters": [ - { "name": "workerId", "type": "string" }, - { "name": "message", "type": "string" } - ], - "handlers": ["browser"] - }, - { - "name": "stop", - "parameters": [ - { "name": "workerId", "type": "string" } - ], - "handlers": ["browser"] - }, - { - "name": "unregister", - "parameters": [ - { "name": "scopeURL", "type": "string" } - ], - "handlers": ["browser"] - }, - { - "name": "updateRegistration", - "parameters": [ - { "name": "scopeURL", "type": "string" } - ], - "handlers": ["browser"] - }, - { - "name": "startWorker", - "parameters": [ - { "name": "scopeURL", "type": "string" } - ], - "handlers": ["browser"] - }, - { - "name": "skipWaiting", - "parameters": [ - { "name": "scopeURL", "type": "string" } - ], - "handlers": ["browser"] - }, - { - "name": "stopWorker", - "parameters": [ - { "name": "versionId", "type": "string" } - ], - "handlers": ["browser"] - }, - { - "name": "inspectWorker", - "parameters": [ - { "name": "versionId", "type": "string" } - ], - "handlers": ["browser"] - }, - { - "name": "setForceUpdateOnPageLoad", - "parameters": [ - { "name": "forceUpdateOnPageLoad", "type": "boolean" } - ], - "handlers": ["browser"] - }, - { - "name": "deliverPushMessage", - "parameters": [ - { "name": "origin", "type": "string" }, - { "name": "registrationId", "type": "string" }, - { "name": "data", "type": "string" } - ], - "handlers": ["browser"] - }, - { - "name": "getTargetInfo", - "parameters": [ - { "name": "targetId", "$ref": "TargetID" } - ], - "returns": [ - { "name": "targetInfo","$ref": "TargetInfo" } - ], - "handlers": ["browser"] - }, - { - "name": "activateTarget", - "parameters": [ - { "name": "targetId", "$ref": "TargetID" } - ], - "handlers": ["browser"] - } - ], - "events": [ - { - "name": "workerCreated", - "parameters": [ - { "name": "workerId", "type": "string" }, - { "name": "url", "type": "string" }, - { "name": "versionId", "type": "string" } - ], - "handlers": ["browser"] - }, - { - "name": "workerTerminated", - "parameters": [ - { "name": "workerId", "type": "string" } - ], - "handlers": ["browser"] - }, - { - "name": "dispatchMessage", - "parameters": [ - { "name": "workerId", "type": "string" }, - { "name": "message", "type": "string" } - ], - "handlers": ["browser"] - }, - { - "name": "workerRegistrationUpdated", - "parameters": [ - { "name": "registrations", "type": "array", "items": { "$ref": "ServiceWorkerRegistration" } } - ], - "handlers": ["browser"] - }, - { - "name": "workerVersionUpdated", - "parameters": [ - { "name": "versions", "type": "array", "items": { "$ref": "ServiceWorkerVersion" } } - ], - "handlers": ["browser"] - }, - { - "name": "workerErrorReported", - "parameters": [ - { "name": "errorMessage", "$ref": "ServiceWorkerErrorMessage" } - ], - "handlers": ["browser"] - } - ] - }, - { - "domain": "Input", - "types": [ - { - "id": "TouchPoint", - "type": "object", - "hidden": true, - "properties": [ - { "name": "state", "type": "string", "enum": ["touchPressed", "touchReleased", "touchMoved", "touchStationary", "touchCancelled"], "description": "State of the touch point." }, - { "name": "x", "type": "integer", "description": "X coordinate of the event relative to the main frame's viewport."}, - { "name": "y", "type": "integer", "description": "Y coordinate of the event relative to the main frame's viewport. 0 refers to the top of the viewport and Y increases as it proceeds towards the bottom of the viewport."}, - { "name": "radiusX", "type": "integer", "optional": true, "description": "X radius of the touch area (default: 1)."}, - { "name": "radiusY", "type": "integer", "optional": true, "description": "Y radius of the touch area (default: 1)."}, - { "name": "rotationAngle", "type": "number", "optional": true, "description": "Rotation angle (default: 0.0)."}, - { "name": "force", "type": "number", "optional": true, "description": "Force (default: 1.0)."}, - { "name": "id", "type": "number", "optional": true, "description": "Identifier used to track touch sources between events, must be unique within an event."} - ] - }, - { - "id": "GestureSourceType", - "type": "string", - "hidden": true, - "enum": ["default", "touch", "mouse"] - } - ], - "commands": [ - { - "name": "dispatchKeyEvent", - "parameters": [ - { "name": "type", "type": "string", "enum": ["keyDown", "keyUp", "rawKeyDown", "char"], "description": "Type of the key event." }, - { "name": "modifiers", "type": "integer", "optional": true, "description": "Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 (default: 0)." }, - { "name": "timestamp", "type": "number", "optional": true, "description": "Time at which the event occurred. Measured in UTC time in seconds since January 1, 1970 (default: current time)." }, - { "name": "text", "type": "string", "optional": true, "description": "Text as generated by processing a virtual key code with a keyboard layout. Not needed for for <code>keyUp</code> and <code>rawKeyDown</code> events (default: \"\")" }, - { "name": "unmodifiedText", "type": "string", "optional": true, "description": "Text that would have been generated by the keyboard if no modifiers were pressed (except for shift). Useful for shortcut (accelerator) key handling (default: \"\")." }, - { "name": "keyIdentifier", "type": "string", "optional": true, "description": "Unique key identifier (e.g., 'U+0041') (default: \"\")." }, - { "name": "code", "type": "string", "optional": true, "description": "Unique DOM defined string value for each physical key (e.g., 'KeyA') (default: \"\")." }, - { "name": "key", "type": "string", "optional": true, "description": "Unique DOM defined string value describing the meaning of the key in the context of active modifiers, keyboard layout, etc (e.g., 'AltGr') (default: \"\")." }, - { "name": "windowsVirtualKeyCode", "type": "integer", "optional": true, "description": "Windows virtual key code (default: 0)." }, - { "name": "nativeVirtualKeyCode", "type": "integer", "optional": true, "description": "Native virtual key code (default: 0)." }, - { "name": "autoRepeat", "type": "boolean", "optional": true, "description": "Whether the event was generated from auto repeat (default: false)." }, - { "name": "isKeypad", "type": "boolean", "optional": true, "description": "Whether the event was generated from the keypad (default: false)." }, - { "name": "isSystemKey", "type": "boolean", "optional": true, "description": "Whether the event was a system key event (default: false)." } - ], - "description": "Dispatches a key event to the page.", - "handlers": ["browser"] - }, - { - "name": "dispatchMouseEvent", - "parameters": [ - { "name": "type", "type": "string", "enum": ["mousePressed", "mouseReleased", "mouseMoved"], "description": "Type of the mouse event." }, - { "name": "x", "type": "integer", "description": "X coordinate of the event relative to the main frame's viewport."}, - { "name": "y", "type": "integer", "description": "Y coordinate of the event relative to the main frame's viewport. 0 refers to the top of the viewport and Y increases as it proceeds towards the bottom of the viewport."}, - { "name": "modifiers", "type": "integer", "optional": true, "description": "Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 (default: 0)." }, - { "name": "timestamp", "type": "number", "optional": true, "description": "Time at which the event occurred. Measured in UTC time in seconds since January 1, 1970 (default: current time)." }, - { "name": "button", "type": "string", "enum": ["none", "left", "middle", "right"], "optional": true, "description": "Mouse button (default: \"none\")." }, - { "name": "clickCount", "type": "integer", "optional": true, "description": "Number of times the mouse button was clicked (default: 0)." } - ], - "description": "Dispatches a mouse event to the page.", - "handlers": ["browser"] - }, - { - "name": "dispatchTouchEvent", - "hidden": true, - "parameters": [ - { "name": "type", "type": "string", "enum": ["touchStart", "touchEnd", "touchMove"], "description": "Type of the touch event." }, - { "name": "touchPoints", "type": "array", "items": { "$ref": "TouchPoint" }, "description": "Touch points." }, - { "name": "modifiers", "type": "integer", "optional": true, "description": "Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 (default: 0)." }, - { "name": "timestamp", "type": "number", "optional": true, "description": "Time at which the event occurred. Measured in UTC time in seconds since January 1, 1970 (default: current time)." } - ], - "description": "Dispatches a touch event to the page." - }, - { - "name": "emulateTouchFromMouseEvent", - "hidden": true, - "parameters": [ - { "name": "type", "type": "string", "enum": ["mousePressed", "mouseReleased", "mouseMoved", "mouseWheel"], "description": "Type of the mouse event." }, - { "name": "x", "type": "integer", "description": "X coordinate of the mouse pointer in DIP."}, - { "name": "y", "type": "integer", "description": "Y coordinate of the mouse pointer in DIP."}, - { "name": "timestamp", "type": "number", "description": "Time at which the event occurred. Measured in UTC time in seconds since January 1, 1970." }, - { "name": "button", "type": "string", "enum": ["none", "left", "middle", "right"], "description": "Mouse button." }, - { "name": "deltaX", "type": "number", "optional": true, "description": "X delta in DIP for mouse wheel event (default: 0)."}, - { "name": "deltaY", "type": "number", "optional": true, "description": "Y delta in DIP for mouse wheel event (default: 0)."}, - { "name": "modifiers", "type": "integer", "optional": true, "description": "Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 (default: 0)." }, - { "name": "clickCount", "type": "integer", "optional": true, "description": "Number of times the mouse button was clicked (default: 0)." } - ], - "description": "Emulates touch event from the mouse event parameters.", - "handlers": ["browser"] - }, - { - "name": "synthesizePinchGesture", - "async": true, - "parameters": [ - { "name": "x", "type": "integer", "description": "X coordinate of the start of the gesture in CSS pixels." }, - { "name": "y", "type": "integer", "description": "Y coordinate of the start of the gesture in CSS pixels." }, - { "name": "scaleFactor", "type": "number", "description": "Relative scale factor after zooming (>1.0 zooms in, <1.0 zooms out)." }, - { "name": "relativeSpeed", "type": "integer", "optional": true, "description": "Relative pointer speed in pixels per second (default: 800)." }, - { "name": "gestureSourceType", "$ref": "GestureSourceType", "optional": true, "description": "Which type of input events to be generated (default: 'default', which queries the platform for the preferred input type)." } - ], - "description": "Synthesizes a pinch gesture over a time period by issuing appropriate touch events.", - "hidden": true, - "handlers": ["browser"] - }, - { - "name": "synthesizeScrollGesture", - "async": true, - "parameters": [ - { "name": "x", "type": "integer", "description": "X coordinate of the start of the gesture in CSS pixels." }, - { "name": "y", "type": "integer", "description": "Y coordinate of the start of the gesture in CSS pixels." }, - { "name": "xDistance", "type": "integer", "optional": true, "description": "The distance to scroll along the X axis (positive to scroll left)." }, - { "name": "yDistance", "type": "integer", "optional": true, "description": "The distance to scroll along the Y axis (positive to scroll up)." }, - { "name": "xOverscroll", "type": "integer", "optional": true, "description": "The number of additional pixels to scroll back along the X axis, in addition to the given distance." }, - { "name": "yOverscroll", "type": "integer", "optional": true, "description": "The number of additional pixels to scroll back along the Y axis, in addition to the given distance." }, - { "name": "preventFling", "type": "boolean", "optional": true, "description": "Prevent fling (default: true)." }, - { "name": "speed", "type": "integer", "optional": true, "description": "Swipe speed in pixels per second (default: 800)." }, - { "name": "gestureSourceType", "$ref": "GestureSourceType", "optional": true, "description": "Which type of input events to be generated (default: 'default', which queries the platform for the preferred input type)." }, - { "name": "repeatCount", "type": "integer", "optional": true, "description": "The number of times to repeat the gesture (default: 0)." }, - { "name": "repeatDelayMs", "type": "integer", "optional": true, "description": "The number of milliseconds delay between each repeat. (default: 250)." }, - { "name": "interactionMarkerName", "type": "string", "optional": true, "description": "The name of the interaction markers to generate, if not empty (default: \"\")." } - ], - "description": "Synthesizes a scroll gesture over a time period by issuing appropriate touch events.", - "hidden": true, - "handlers": ["browser"] - }, - { - "name": "synthesizeTapGesture", - "async": true, - "parameters": [ - { "name": "x", "type": "integer", "description": "X coordinate of the start of the gesture in CSS pixels." }, - { "name": "y", "type": "integer", "description": "Y coordinate of the start of the gesture in CSS pixels." }, - { "name": "duration", "type": "integer", "optional": true, "description": "Duration between touchdown and touchup events in ms (default: 50)." }, - { "name": "tapCount", "type": "integer", "optional": true, "description": "Number of times to perform the tap (e.g. 2 for double tap, default: 1)." }, - { "name": "gestureSourceType", "$ref": "GestureSourceType", "optional": true, "description": "Which type of input events to be generated (default: 'default', which queries the platform for the preferred input type)." } - ], - "description": "Synthesizes a tap gesture over a time period by issuing appropriate touch events.", - "hidden": true, - "handlers": ["browser"] - } - ], - "events": [] - }, - { - "domain": "LayerTree", - "hidden": true, - "types": [ - { - "id": "LayerId", - "type": "string", - "description": "Unique Layer identifier." - }, - { - "id": "SnapshotId", - "type": "string", - "description": "Unique snapshot identifier." - }, - { - "id": "ScrollRect", - "type": "object", - "description": "Rectangle where scrolling happens on the main thread.", - "properties": [ - { "name": "rect", "$ref": "DOM.Rect", "description": "Rectangle itself." }, - { "name": "type", "type": "string", "enum": ["RepaintsOnScroll", "TouchEventHandler", "WheelEventHandler"], "description": "Reason for rectangle to force scrolling on the main thread" } - ] - }, - { - "id": "PictureTile", - "type": "object", - "description": "Serialized fragment of layer picture along with its offset within the layer.", - "properties": [ - { "name": "x", "type": "number", "description": "Offset from owning layer left boundary" }, - { "name": "y", "type": "number", "description": "Offset from owning layer top boundary" }, - { "name": "picture", "type": "string", "description": "Base64-encoded snapshot data." } - ] - }, - { - "id": "Layer", - "type": "object", - "description": "Information about a compositing layer.", - "properties": [ - { "name": "layerId", "$ref": "LayerId", "description": "The unique id for this layer." }, - { "name": "parentLayerId", "$ref": "LayerId", "optional": true, "description": "The id of parent (not present for root)." }, - { "name": "backendNodeId", "$ref": "DOM.BackendNodeId", "optional": true, "description": "The backend id for the node associated with this layer." }, - { "name": "offsetX", "type": "number", "description": "Offset from parent layer, X coordinate." }, - { "name": "offsetY", "type": "number", "description": "Offset from parent layer, Y coordinate." }, - { "name": "width", "type": "number", "description": "Layer width." }, - { "name": "height", "type": "number", "description": "Layer height." }, - { "name": "transform", "type": "array", "items": { "type": "number" }, "minItems": 16, "maxItems": 16, "optional": true, "description": "Transformation matrix for layer, default is identity matrix" }, - { "name": "anchorX", "type": "number", "optional": true, "description": "Transform anchor point X, absent if no transform specified" }, - { "name": "anchorY", "type": "number", "optional": true, "description": "Transform anchor point Y, absent if no transform specified" }, - { "name": "anchorZ", "type": "number", "optional": true, "description": "Transform anchor point Z, absent if no transform specified" }, - { "name": "paintCount", "type": "integer", "description": "Indicates how many time this layer has painted." }, - { "name": "drawsContent", "type": "boolean", "description": "Indicates whether this layer hosts any content, rather than being used for transform/scrolling purposes only." }, - { "name": "invisible", "type": "boolean", "optional": true, "description": "Set if layer is not visible." }, - { "name": "scrollRects", "type": "array", "items": { "$ref": "ScrollRect"}, "optional": true, "description": "Rectangles scrolling on main thread only."} - ] - }, - { - "id": "PaintProfile", - "type": "array", - "description": "Array of timings, one per paint step.", - "items": { - "type": "number", - "description": "A time in seconds since the end of previous step (for the first step, time since painting started)" - } - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables compositing tree inspection." - }, - { - "name": "disable", - "description": "Disables compositing tree inspection." - }, - { - "name": "compositingReasons", - "parameters": [ - { "name": "layerId", "$ref": "LayerId", "description": "The id of the layer for which we want to get the reasons it was composited." } - ], - "description": "Provides the reasons why the given layer was composited.", - "returns": [ - { "name": "compositingReasons", "type": "array", "items": { "type": "string" }, "description": "A list of strings specifying reasons for the given layer to become composited." } - ] - }, - { - "name": "makeSnapshot", - "parameters": [ - { "name": "layerId", "$ref": "LayerId", "description": "The id of the layer." } - ], - "description": "Returns the layer snapshot identifier.", - "returns": [ - { "name": "snapshotId", "$ref": "SnapshotId", "description": "The id of the layer snapshot." } - ] - }, - { - "name": "loadSnapshot", - "parameters": [ - { "name": "tiles", "type": "array", "items": { "$ref": "PictureTile" }, "minItems": 1, "description": "An array of tiles composing the snapshot." } - ], - "description": "Returns the snapshot identifier.", - "returns": [ - { "name": "snapshotId", "$ref": "SnapshotId", "description": "The id of the snapshot." } - ] - }, - { - "name": "releaseSnapshot", - "parameters": [ - { "name": "snapshotId", "$ref": "SnapshotId", "description": "The id of the layer snapshot." } - ], - "description": "Releases layer snapshot captured by the back-end." - }, - { - "name": "profileSnapshot", - "parameters": [ - { "name": "snapshotId", "$ref": "SnapshotId", "description": "The id of the layer snapshot." }, - { "name": "minRepeatCount", "type": "integer", "optional": true, "description": "The maximum number of times to replay the snapshot (1, if not specified)." }, - { "name": "minDuration", "type": "number", "optional": true, "description": "The minimum duration (in seconds) to replay the snapshot." }, - { "name": "clipRect", "$ref": "DOM.Rect", "optional": true, "description": "The clip rectangle to apply when replaying the snapshot." } - ], - "returns": [ - { "name": "timings", "type": "array", "items": { "$ref": "PaintProfile" }, "description": "The array of paint profiles, one per run." } - ] - }, - { - "name": "replaySnapshot", - "parameters": [ - { "name": "snapshotId", "$ref": "SnapshotId", "description": "The id of the layer snapshot." }, - { "name": "fromStep", "type": "integer", "optional": true, "description": "The first step to replay from (replay from the very start if not specified)." }, - { "name": "toStep", "type": "integer", "optional": true, "description": "The last step to replay to (replay till the end if not specified)." }, - { "name": "scale", "type": "number", "optional": true, "description": "The scale to apply while replaying (defaults to 1)." } - ], - "description": "Replays the layer snapshot and returns the resulting bitmap.", - "returns": [ - { "name": "dataURL", "type": "string", "description": "A data: URL for resulting image." } - ] - }, - { - "name": "snapshotCommandLog", - "parameters": [ - { "name": "snapshotId", "$ref": "SnapshotId", "description": "The id of the layer snapshot." } - ], - "description": "Replays the layer snapshot and returns canvas log.", - "returns": [ - { "name": "commandLog", "type": "array", "items": { "type": "object" }, "description": "The array of canvas function calls." } - ] - } - ], - "events": [ - { - "name": "layerTreeDidChange", - "parameters": [ - { "name": "layers", "type": "array", "items": { "$ref": "Layer" }, "optional": true, "description": "Layer tree, absent if not in the comspositing mode." } - ] - }, - { - "name": "layerPainted", - "parameters": [ - { "name": "layerId", "$ref": "LayerId", "description": "The id of the painted layer." }, - { "name": "clip", "$ref": "DOM.Rect", "description": "Clip rectangle." } - ] - } - ] - }, - { - "domain": "DeviceOrientation", - "hidden": true, - "commands": [ - { - "name": "setDeviceOrientationOverride", - "description": "Overrides the Device Orientation.", - "parameters": [ - { "name": "alpha", "type": "number", "description": "Mock alpha"}, - { "name": "beta", "type": "number", "description": "Mock beta"}, - { "name": "gamma", "type": "number", "description": "Mock gamma"} - ] - }, - { - "name": "clearDeviceOrientationOverride", - "description": "Clears the overridden Device Orientation." - } - ] - }, - { - "domain": "Tracing", - "types": [ - { - "id": "MemoryDumpConfig", - "type": "object", - "description": "Configuration for memory dump. Used only when \"memory-infra\" category is enabled." - }, - { - "id": "TraceConfig", - "type": "object", - "properties": [ - { "name": "recordMode", "type": "string", "optional": true, "enum": ["recordUntilFull", "recordContinuously", "recordAsMuchAsPossible", "echoToConsole"], "description": "Controls how the trace buffer stores data." }, - { "name": "enableSampling", "type": "boolean", "optional": true, "description": "Turns on JavaScript stack sampling." }, - { "name": "enableSystrace", "type": "boolean", "optional": true, "description": "Turns on system tracing." }, - { "name": "enableArgumentFilter", "type": "boolean", "optional": true, "description": "Turns on argument filter." }, - { "name": "includedCategories", "type": "array", "items": { "type": "string" }, "optional": true, "description": "Included category filters." }, - { "name": "excludedCategories", "type": "array", "items": { "type": "string" }, "optional": true, "description": "Excluded category filters." }, - { "name": "syntheticDelays", "type": "array", "items": { "type": "string" }, "optional": true, "description": "Configuration to synthesize the delays in tracing." }, - { "name": "memoryDumpConfig", "$ref": "MemoryDumpConfig", "optional": true, "description": "Configuration for memory dump triggers. Used only when \"memory-infra\" category is enabled." } - ] - } - ], - "commands": [ - { - "name": "start", - "async": true, - "description": "Start trace events collection.", - "parameters": [ - { "name": "categories", "type": "string", "optional": true, "deprecated": true, "description": "Category/tag filter" }, - { "name": "options", "type": "string", "optional": true, "deprecated": true, "description": "Tracing options" }, - { "name": "bufferUsageReportingInterval", "type": "number", "optional": true, "description": "If set, the agent will issue bufferUsage events at this interval, specified in milliseconds" }, - { "name": "transferMode", "type": "string", "enum": ["ReportEvents", "ReturnAsStream"], "optional": true, "description": "Whether to report trace events as series of dataCollected events or to save trace to a stream (defaults to <code>ReportEvents</code>)." }, - { "name": "traceConfig", "$ref": "TraceConfig", "optional": true, "description": "" } - ], - "handlers": ["browser", "renderer"] - }, - { - "name": "end", - "async": true, - "description": "Stop trace events collection.", - "handlers": ["browser", "renderer"] - }, - { - "name": "getCategories", - "async": true, - "description": "Gets supported tracing categories.", - "returns": [ - { "name": "categories", "type": "array", "items": { "type": "string" }, "description": "A list of supported tracing categories." } - ], - "handlers": ["browser"] - }, - { - "name": "requestMemoryDump", - "async": true, - "description": "Request a global memory dump.", - "returns": [ - { "name": "dumpGuid", "type": "string", "description": "GUID of the resulting global memory dump." }, - { "name": "success", "type": "boolean", "description": "True iff the global memory dump succeeded." } - ], - "handlers": ["browser"] - }, - { - "name": "recordClockSyncMarker", - "description": "Record a clock sync marker in the trace.", - "parameters": [ - { "name": "syncId", "type": "string", "description": "The ID of this clock sync marker" } - ], - "handlers": ["browser"] - } - ], - "events": [ - { - "name": "dataCollected", - "parameters": [ - { "name": "value", "type": "array", "items": { "type": "object" } } - ], - "description": "Contains an bucket of collected trace events. When tracing is stopped collected events will be send as a sequence of dataCollected events followed by tracingComplete event.", - "handlers": ["browser"] - }, - { - "name": "tracingComplete", - "description": "Signals that tracing is stopped and there is no trace buffers pending flush, all data were delivered via dataCollected events.", - "parameters": [ - { "name": "stream", "$ref": "IO.StreamHandle", "optional": true, "description": "A handle of the stream that holds resulting trace data." } - ], - "handlers": ["browser"] - }, - { - "name": "bufferUsage", - "parameters": [ - { "name": "percentFull", "type": "number", "optional": true, "description": "A number in range [0..1] that indicates the used size of event buffer as a fraction of its total size." }, - { "name": "eventCount", "type": "number", "optional": true, "description": "An approximate number of events in the trace log." }, - { "name": "value", "type": "number", "optional": true, "description": "A number in range [0..1] that indicates the used size of event buffer as a fraction of its total size." } - ], - "handlers": ["browser"] - } - ] - }, - { - "domain": "Animation", - "hidden": true, - "types": [ - { - "id": "Animation", - "type": "object", - "hidden": true, - "properties": [ - { "name": "id", "type": "string", "description": "<code>Animation</code>'s id." }, - { "name": "name", "type": "string", "description": "<code>Animation</code>'s name." }, - { "name": "pausedState", "type": "boolean", "hidden": "true", "description": "<code>Animation</code>'s internal paused state." }, - { "name": "playState", "type": "string", "description": "<code>Animation</code>'s play state." }, - { "name": "playbackRate", "type": "number", "description": "<code>Animation</code>'s playback rate." }, - { "name": "startTime", "type": "number", "description": "<code>Animation</code>'s start time." }, - { "name": "currentTime", "type": "number", "description": "<code>Animation</code>'s current time." }, - { "name": "source", "$ref": "AnimationEffect", "description": "<code>Animation</code>'s source animation node." }, - { "name": "type", "type": "string", "enum": ["CSSTransition", "CSSAnimation", "WebAnimation"], "description": "Animation type of <code>Animation</code>." }, - { "name": "cssId", "type": "string", "optional": true, "description": "A unique ID for <code>Animation</code> representing the sources that triggered this CSS animation/transition."} - ], - "description": "Animation instance." - }, - { - "id": "AnimationEffect", - "type": "object", - "hidden": true, - "properties": [ - { "name": "delay", "type": "number", "description": "<code>AnimationEffect</code>'s delay." }, - { "name": "endDelay", "type": "number", "description": "<code>AnimationEffect</code>'s end delay." }, - { "name": "playbackRate", "type": "number", "description": "<code>AnimationEffect</code>'s playbackRate." }, - { "name": "iterationStart", "type": "number", "description": "<code>AnimationEffect</code>'s iteration start." }, - { "name": "iterations", "type": "number", "description": "<code>AnimationEffect</code>'s iterations." }, - { "name": "duration", "type": "number", "description": "<code>AnimationEffect</code>'s iteration duration." }, - { "name": "direction", "type": "string", "description": "<code>AnimationEffect</code>'s playback direction." }, - { "name": "fill", "type": "string", "description": "<code>AnimationEffect</code>'s fill mode." }, - { "name": "backendNodeId", "$ref": "DOM.BackendNodeId", "description": "<code>AnimationEffect</code>'s target node." }, - { "name": "keyframesRule", "$ref": "KeyframesRule", "optional": true, "description": "<code>AnimationEffect</code>'s keyframes." }, - { "name": "easing", "type": "string", "description": "<code>AnimationEffect</code>'s timing function." } - ], - "description": "AnimationEffect instance" - }, - { - "id": "KeyframesRule", - "type": "object", - "properties": [ - { "name": "name", "type": "string", "optional": true, "description": "CSS keyframed animation's name." }, - { "name": "keyframes", "type": "array", "items": { "$ref": "KeyframeStyle" }, "description": "List of animation keyframes." } - ], - "description": "Keyframes Rule" - }, - { - "id": "KeyframeStyle", - "type": "object", - "properties": [ - { "name": "offset", "type": "string", "description": "Keyframe's time offset." }, - { "name": "easing", "type": "string", "description": "<code>AnimationEffect</code>'s timing function." } - ], - "description": "Keyframe Style" - } - ], - "commands": [ - { - "name": "enable", - "description": "Enables animation domain notifications." - }, - { - "name": "disable", - "description": "Disables animation domain notifications." - }, - { - "name": "getPlaybackRate", - "returns": [ - { "name": "playbackRate", "type": "number", "description": "Playback rate for animations on page."} - ], - "description": "Gets the playback rate of the document timeline." - }, - { - "name": "setPlaybackRate", - "parameters": [ - { "name": "playbackRate", "type": "number", "description": "Playback rate for animations on page" } - ], - "description": "Sets the playback rate of the document timeline." - }, - { - "name": "getCurrentTime", - "parameters": [ - { "name": "id", "type": "string", "description": "Id of animation." } - ], - "returns": [ - { "name": "currentTime", "type": "number", "description": "Current time of the page." } - ], - "description": "Returns the current time of the an animation." - }, - { - "name": "setPaused", - "parameters": [ - { "name": "animations", "type": "array", "items": { "type": "string" }, "description": "Animations to set the pause state of." }, - { "name": "paused", "type": "boolean", "description": "Paused state to set to." } - ], - "description": "Sets the paused state of a set of animations." - }, - { - "name": "setTiming", - "parameters": [ - { "name": "animationId", "type": "string", "description": "Animation id." }, - { "name": "duration", "type": "number", "description": "Duration of the animation." }, - { "name": "delay", "type": "number", "description": "Delay of the animation." } - ], - "description": "Sets the timing of an animation node." - }, - { - "name": "seekAnimations", - "parameters": [ - { "name": "animations", "type": "array", "items": { "type": "string" }, "description": "List of animation ids to seek." }, - { "name": "currentTime", "type": "number", "description": "Set the current time of each animation." } - ], - "description": "Seek a set of animations to a particular time within each animation." - }, - { - "name": "releaseAnimations", - "parameters": [ - { "name": "animations", "type": "array", "items": { "type": "string" }, "description": "List of animation ids to seek." } - ], - "description": "Releases a set of animations to no longer be manipulated." - }, - { - "name": "resolveAnimation", - "parameters": [ - { "name": "animationId", "type": "string", "description": "Animation id." } - ], - "returns": [ - { "name": "remoteObject", "$ref": "Runtime.RemoteObject", "description": "Corresponding remote object." } - ], - "description": "Gets the remote object of the Animation." - } - ], - "events": [ - { - "name": "animationCreated", - "parameters": [ - { "name": "id", "type": "string", "description": "Id of the animation that was created." } - ], - "description": "Event for each animation that has been created." - }, - { - "name": "animationStarted", - "parameters": [ - { "name": "animation", "$ref": "Animation", "description": "Animation that was started." } - ], - "description": "Event for animation that has been started." - }, - { - "name": "animationCanceled", - "parameters": [ - { "name": "id", "type": "string", "description": "Id of the animation that was cancelled."} - ], - "description": "Event for when an animation has been cancelled." - } - ] - }, - { - "domain": "Accessibility", - "hidden": true, - "types": [ - { - "id": "AXNodeId", - "type": "string", - "description": "Unique accessibility node identifier." - }, - { - "id": "AXValueType", - "type": "string", - "enum": [ "boolean", "tristate", "booleanOrUndefined", "idref", "idrefList", "integer", "node", "nodeList", "number", "string", "computedString", "token", "tokenList", "domRelation", "role", "internalRole", "valueUndefined" ], - "description": "Enum of possible property types." - }, - { - "id": "AXValueSourceType", - "type": "string", - "enum": [ "attribute", "implicit", "style", "contents", "placeholder", "relatedElement" ], - "description": "Enum of possible property sources." - }, - { "id": "AXValueNativeSourceType", - "type": "string", - "enum": [ "figcaption", "label", "labelfor", "labelwrapped", "legend", "tablecaption", "title", "other" ], - "description": "Enum of possible native property sources (as a subtype of a particular AXValueSourceType)." - }, - { - "id": "AXValueSource", - "type": "object", - "properties": [ - { "name": "type", "$ref": "AXValueSourceType", "description": "What type of source this is." }, - { "name": "value", "$ref": "AXValue", "description": "The value of this property source.", "optional": true }, - { "name": "attribute", "type": "string", "description": "The name of the relevant attribute, if any.", "optional": true }, - { "name": "attributeValue", "$ref": "AXValue", "description": "The value of the relevant attribute, if any.", "optional": true }, - { "name": "superseded", "type": "boolean", "description": "Whether this source is superseded by a higher priority source.", "optional": true }, - { "name": "nativeSource", "$ref": "AXValueNativeSourceType", "description": "The native markup source for this value, e.g. a <label> element.", "optional": true }, - { "name": "nativeSourceValue", "$ref": "AXValue", "description": "The value, such as a node or node list, of the native source.", "optional": true }, - { "name": "invalid", "type": "boolean", "description": "Whether the value for this property is invalid.", "optional": true }, - { "name": "invalidReason", "type": "string", "description": "Reason for the value being invalid, if it is.", "optional": true } - ], - "description": "A single source for a computed AX property." - }, - { - "id": "AXRelatedNode", - "type": "object", - "properties": [ - { "name": "backendNodeId", "$ref": "DOM.BackendNodeId", "description": "The BackendNodeId of the related node." }, - { "name": "idref", "type": "string", "description": "The IDRef value provided, if any.", "optional": true }, - { "name": "text", "type": "string", "description": "The text alternative of this node in the current context.", "optional": true } - ] - }, - { - "id": "AXProperty", - "type": "object", - "properties": [ - { "name": "name", "type": "string", "description": "The name of this property." }, - { "name": "value", "$ref": "AXValue", "description": "The value of this property." } - ] - }, - { - "id": "AXValue", - "type": "object", - "properties": [ - { "name": "type", "$ref": "AXValueType", "description": "The type of this value." }, - - { "name": "value", "type": "any", "description": "The computed value of this property.", "optional": true }, - { "name": "relatedNodes", "type": "array", "items": { "$ref": "AXRelatedNode" }, "description": "One or more related nodes, if applicable.", "optional": true }, - { "name": "sources", "type": "array", "items": { "$ref": "AXValueSource" }, "description": "The sources which contributed to the computation of this property.", "optional": true } - ], - "description": "A single computed AX property." - }, - { - "id": "AXGlobalStates", - "type": "string", - "enum": [ "disabled", "hidden", "hiddenRoot", "invalid" ], - "description": "States which apply to every AX node." - }, - { - "id": "AXLiveRegionAttributes", - "type": "string", - "enum": [ "live", "atomic", "relevant", "busy", "root" ], - "description": "Attributes which apply to nodes in live regions." - }, - { - "id": "AXWidgetAttributes", - "type": "string", - "enum": [ "autocomplete", "haspopup", "level", "multiselectable", "orientation", "multiline", "readonly", "required", "valuemin", "valuemax", "valuetext" ], - "Description": "Attributes which apply to widgets." - }, - { - "id": "AXWidgetStates", - "type": "string", - "enum": [ "checked", "expanded", "pressed", "selected" ], - "description": "States which apply to widgets." - }, - { - "id": "AXRelationshipAttributes", - "type": "string", - "enum": [ "activedescendant", "flowto", "controls", "describedby", "labelledby", "owns" ], - "description": "Relationships between elements other than parent/child/sibling." - }, - { - "id": "AXNode", - "type": "object", - "properties": [ - { "name": "nodeId", "$ref": "AXNodeId", "description": "Unique identifier for this node." }, - { "name": "ignored", "type": "boolean", "description": "Whether this node is ignored for accessibility" }, - { "name": "ignoredReasons", "type": "array", "items": { "$ref": "AXProperty" }, "description": "Collection of reasons why this node is hidden.", "optional": true }, - { "name": "role", "$ref": "AXValue", "description": "This <code>Node</code>'s role, whether explicit or implicit.", "optional": true}, - { "name": "name", "$ref": "AXValue", "description": "The accessible name for this <code>Node</code>.", "optional": true }, - { "name": "description", "$ref": "AXValue", "description": "The accessible description for this <code>Node</code>.", "optional": true }, - { "name": "value", "$ref": "AXValue", "description": "The value for this <code>Node</code>.", "optional": true }, - { "name": "properties", "type": "array", "items": { "$ref": "AXProperty" }, "description": "All other properties", "optional": true } - ], - "description": "A node in the accessibility tree." - } - ], - "commands": [ - { - "name": "getAXNode", - "parameters": [ - { "name": "nodeId", "$ref": "DOM.NodeId", "description": "ID of node to get accessibility node for." } - ], - "returns": [ - { "name": "accessibilityNode", "$ref": "AXNode", "description": "The <code>Accessibility.AXNode</code> for this DOM node, if it exists.", "optional": true } - ], - "description": "Fetches the accessibility node for this DOM node, if it exists.", - "hidden": true - } - ] - }, - { - "domain": "Storage", - "hidden": true, - "types": [ - { - "id": "StorageType", - "type": "string", - "enum": [ - "appcache", - "cookies", - "file_systems", - "indexeddb", - "local_storage", - "shader_cache", - "websql", - "webrtc_indetity", - "service_workers", - "cache_storage", - "all" - ], - "description": "Enum of possible storage types." - } - ], - "commands": [ - { - "name": "clearDataForOrigin", - "parameters": [ - { "name": "origin", "type": "string", "description": "Security origin." }, - { "name": "storageTypes", "type": "string", "description": "Comma separated origin names." } - ], - "description": "Clears storage for origin.", - "handlers": ["browser"] - } - ] - }] -} diff --git a/deps/v8_inspector/platform/PlatformExport.h b/deps/v8_inspector/platform/PlatformExport.h deleted file mode 100644 index 8230fbb80fe30d..00000000000000 --- a/deps/v8_inspector/platform/PlatformExport.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2013 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - -#ifndef PlatformExport_h -#define PlatformExport_h - -#if !defined(BLINK_PLATFORM_IMPLEMENTATION) -#define BLINK_PLATFORM_IMPLEMENTATION 0 -#endif - -#if defined(COMPONENT_BUILD) -#if defined(WIN32) -#if BLINK_PLATFORM_IMPLEMENTATION -#define PLATFORM_EXPORT __declspec(dllexport) -#else -#define PLATFORM_EXPORT __declspec(dllimport) -#endif -#else // defined(WIN32) -#define PLATFORM_EXPORT __attribute__((visibility("default"))) -#endif -#else // defined(COMPONENT_BUILD) -#define PLATFORM_EXPORT -#endif - -#if defined(_MSC_VER) -// MSVC Compiler warning C4275: -// non dll-interface class 'Bar' used as base for dll-interface class 'Foo'. -// Note that this is intended to be used only when no access to the base class' -// static data is done through derived classes or inline methods. For more info, -// see http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx -// -// This pragma will allow exporting a class that inherits from a non-exported -// base class, anywhere in the Blink platform component. This is only -// a problem when using the MSVC compiler on Windows. -#pragma warning(suppress:4275) -#endif - -#endif // PlatformExport_h diff --git a/deps/v8_inspector/platform/inspector_protocol/Array.h b/deps/v8_inspector/platform/inspector_protocol/Array.h index 07a2993d5c3f7f..b75f51742977c7 100644 --- a/deps/v8_inspector/platform/inspector_protocol/Array.h +++ b/deps/v8_inspector/platform/inspector_protocol/Array.h @@ -5,9 +5,9 @@ #ifndef Array_h #define Array_h -#include "platform/PlatformExport.h" #include "platform/inspector_protocol/Collections.h" #include "platform/inspector_protocol/ErrorSupport.h" +#include "platform/inspector_protocol/Platform.h" #include "platform/inspector_protocol/String16.h" #include "platform/inspector_protocol/ValueConversions.h" #include "platform/inspector_protocol/Values.h" @@ -91,7 +91,7 @@ class Array { errors->addError("array expected"); return nullptr; } - std::unique_ptr<Array<T>> result = wrapUnique(new Array<T>()); + std::unique_ptr<Array<T>> result(new Array<T>()); errors->push(); for (size_t i = 0; i < array->size(); ++i) { errors->setName(String16::number(i)); diff --git a/deps/v8_inspector/platform/inspector_protocol/BackendCallback.h b/deps/v8_inspector/platform/inspector_protocol/BackendCallback.h new file mode 100644 index 00000000000000..870fbb5555ba9b --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/BackendCallback.h @@ -0,0 +1,23 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BackendCallback_h +#define BackendCallback_h + +#include "platform/inspector_protocol/ErrorSupport.h" +#include "platform/inspector_protocol/Platform.h" + +namespace blink { +namespace protocol { + +class PLATFORM_EXPORT BackendCallback { +public: + virtual ~BackendCallback() { } + virtual void sendFailure(const ErrorString&) = 0; +}; + +} // namespace platform +} // namespace blink + +#endif // !defined(BackendCallback_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/Backend_cpp.template b/deps/v8_inspector/platform/inspector_protocol/Backend_cpp.template deleted file mode 100644 index e69de29bb2d1d6..00000000000000 diff --git a/deps/v8_inspector/platform/inspector_protocol/Backend_h.template b/deps/v8_inspector/platform/inspector_protocol/Backend_h.template deleted file mode 100644 index 4d6ac711f6dfac..00000000000000 --- a/deps/v8_inspector/platform/inspector_protocol/Backend_h.template +++ /dev/null @@ -1,78 +0,0 @@ -// This file is generated - -// Copyright (c) 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef {{class_name}}_h -#define {{class_name}}_h - -#include "platform/inspector_protocol/TypeBuilder.h" - -namespace blink { -namespace protocol { - -class FrontendChannel; -class BackendImplWeakPtr; - -class PLATFORM_EXPORT Backend { -public: - class PLATFORM_EXPORT CallbackBase { - public: - virtual ~CallbackBase() { } - virtual void sendFailure(const ErrorString&) = 0; - }; -{% for domain in api.domains %} - - class PLATFORM_EXPORT {{domain.domain}} { - public: - {% for command in domain.commands %} - {% if "redirect" in command %}{% continue %}{% endif %} - {% if ("handlers" in command) and not ("renderer" in command["handlers"]) %}{% continue %}{% endif %} - {% if "async" in command %} - class PLATFORM_EXPORT {{command.name | to_title_case}}Callback : public CallbackBase { - public: - virtual void sendSuccess( - {%- for parameter in command.returns -%} - {%- if "optional" in parameter -%} - const Maybe<{{resolve_type(parameter).raw_type}}>& {{parameter.name}} - {%- else -%} - {{resolve_type(parameter).pass_type}} {{parameter.name}} - {%- endif -%} - {%- if not loop.last -%}, {% endif -%} - {%- endfor -%} - ) = 0; - }; - {% endif %} - virtual void {{command.name}}(ErrorString* - {%- for parameter in command.parameters -%} - {%- if "optional" in parameter -%} - , const Maybe<{{resolve_type(parameter).raw_type}}>& in_{{parameter.name}} - {%- else -%} - , {{resolve_type(parameter).pass_type}} in_{{parameter.name}} - {%- endif -%} - {%- endfor -%} - {%- if "async" in command -%} - , std::unique_ptr<{{command.name | to_title_case}}Callback> callback - {%- else -%} - {%- for parameter in command.returns -%} - {%- if "optional" in parameter -%} - , Maybe<{{resolve_type(parameter).raw_type}}>* out_{{parameter.name}} - {%- else -%} - , {{resolve_type(parameter).type}}* out_{{parameter.name}} - {%- endif -%} - {%- endfor -%} - {%- endif -%} - ) = 0; - {% endfor %} - - protected: - virtual ~{{domain.domain}}() { } - }; -{% endfor %} -}; - -} // namespace protocol -} // namespace blink - -#endif // !defined({{class_name}}_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/CodeGenerator.py b/deps/v8_inspector/platform/inspector_protocol/CodeGenerator.py index 62f77a783a5b7e..4c11161a3cad63 100644 --- a/deps/v8_inspector/platform/inspector_protocol/CodeGenerator.py +++ b/deps/v8_inspector/platform/inspector_protocol/CodeGenerator.py @@ -4,9 +4,7 @@ import os.path import sys -import string import optparse -import re try: import json except ImportError: @@ -22,49 +20,92 @@ # since some compile processes will try to read the partially written cache. module_path, module_filename = os.path.split(os.path.realpath(__file__)) templates_dir = module_path + +# In Blink, jinja2 is in chromium's third_party directory. +# Insert at 1 so at front to override system libraries, and +# after path[0] == invoking script dir +third_party_dir = os.path.normpath(os.path.join( + module_path, os.pardir, os.pardir, os.pardir, os.pardir)) +if os.path.isdir(third_party_dir): + sys.path.insert(1, third_party_dir) + +# In Node, it is in deps folder deps_dir = os.path.normpath(os.path.join( - module_path, os.pardir, os.pardir, 'deps')) + module_path, os.pardir, os.pardir, "deps")) +if os.path.isdir(deps_dir): + sys.path.insert(1, os.path.join(deps_dir, "jinja2")) + sys.path.insert(1, os.path.join(deps_dir, "markupsafe")) -sys.path.insert(1, os.path.join(deps_dir, "jinja2")) -sys.path.insert(1, os.path.join(deps_dir, "markupsafe")) import jinja2 cmdline_parser = optparse.OptionParser() +cmdline_parser.add_option("--protocol") +cmdline_parser.add_option("--include") +cmdline_parser.add_option("--string_type") +cmdline_parser.add_option("--export_macro") cmdline_parser.add_option("--output_dir") -cmdline_parser.add_option("--generate_dispatcher") +cmdline_parser.add_option("--output_package") try: arg_options, arg_values = cmdline_parser.parse_args() - if (len(arg_values) == 0): - raise Exception("At least one plain argument expected (found %s)" % len(arg_values)) + protocol_file = arg_options.protocol + if not protocol_file: + raise Exception("Protocol directory must be specified") + include_file = arg_options.include output_dirname = arg_options.output_dir - generate_dispatcher = arg_options.generate_dispatcher if not output_dirname: raise Exception("Output directory must be specified") + output_package = arg_options.output_package + if not output_package: + raise Exception("Output package must be specified") + string_type = arg_options.string_type + if not string_type: + raise Exception("String type must be specified") + export_macro = arg_options.export_macro + if not export_macro: + raise Exception("Export macro must be specified") except Exception: # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html exc = sys.exc_info()[1] sys.stderr.write("Failed to parse command-line arguments: %s\n\n" % exc) - sys.stderr.write("Usage: <script> --output_dir <output_dir> blink_protocol.json v8_protocol.json ...\n") exit(1) -json_api = {"domains": []} -json_timestamp = 0 +input_file = open(protocol_file, "r") +json_string = input_file.read() +parsed_json = json.loads(json_string) + + +# Make gyp / make generatos happy, otherwise make rebuilds world. +def up_to_date(): + template_ts = max( + os.path.getmtime(__file__), + os.path.getmtime(os.path.join(templates_dir, "TypeBuilder_h.template")), + os.path.getmtime(os.path.join(templates_dir, "TypeBuilder_cpp.template")), + os.path.getmtime(protocol_file)) + + for domain in parsed_json["domains"]: + name = domain["domain"] + h_path = os.path.join(output_dirname, name + ".h") + cpp_path = os.path.join(output_dirname, name + ".cpp") + if not os.path.exists(h_path) or not os.path.exists(cpp_path): + return False + generated_ts = max(os.path.getmtime(h_path), os.path.getmtime(cpp_path)) + if generated_ts < template_ts: + return False + return True + + +if up_to_date(): + sys.exit() -for filename in arg_values: - json_timestamp = max(os.path.getmtime(filename), json_timestamp) - input_file = open(filename, "r") - json_string = input_file.read() - parsed_json = json.loads(json_string) - json_api["domains"] += parsed_json["domains"] def to_title_case(name): return name[:1].upper() + name[1:] def dash_to_camelcase(word): - return ''.join(to_title_case(x) or '-' for x in word.split('-')) + return "".join(to_title_case(x) or "-" for x in word.split("-")) def initialize_jinja_env(cache_dir): @@ -77,7 +118,7 @@ def initialize_jinja_env(cache_dir): lstrip_blocks=True, # so can indent control flow tags trim_blocks=True) jinja_env.filters.update({"to_title_case": to_title_case, "dash_to_camelcase": dash_to_camelcase}) - jinja_env.add_extension('jinja2.ext.loopcontrols') + jinja_env.add_extension("jinja2.ext.loopcontrols") return jinja_env @@ -149,28 +190,16 @@ def create_any_type_definition(): def create_string_type_definition(domain): - if domain in ["Runtime", "Debugger", "Profiler", "HeapProfiler"]: - return { - "return_type": "String16", - "pass_type": "const String16&", - "to_pass_type": "%s", - "to_raw_type": "%s", - "to_rvalue": "%s", - "type": "String16", - "raw_type": "String16", - "raw_pass_type": "const String16&", - "raw_return_type": "String16", - } return { - "return_type": "String", - "pass_type": "const String&", + "return_type": string_type, + "pass_type": ("const %s&" % string_type), "to_pass_type": "%s", "to_raw_type": "%s", "to_rvalue": "%s", - "type": "String", - "raw_type": "String", - "raw_pass_type": "const String&", - "raw_return_type": "String", + "type": string_type, + "raw_type": string_type, + "raw_pass_type": ("const %s&" % string_type), + "raw_return_type": string_type, } @@ -203,6 +232,7 @@ def create_primitive_type_definition(type): "default_value": defaults[type] } + type_definitions = {} type_definitions["number"] = create_primitive_type_definition("number") type_definitions["integer"] = create_primitive_type_definition("integer") @@ -210,6 +240,7 @@ def create_primitive_type_definition(type): type_definitions["object"] = create_object_type_definition() type_definitions["any"] = create_any_type_definition() + def wrap_array_definition(type): return { "return_type": "std::unique_ptr<protocol::Array<%s>>" % type["raw_type"], @@ -242,9 +273,6 @@ def create_type_definitions(): else: type_definitions[domain["domain"] + "." + type["id"]] = create_primitive_type_definition(type["type"]) -patch_full_qualified_refs() -create_type_definitions() - def type_definition(name): return type_definitions[name] @@ -266,39 +294,54 @@ def join_arrays(dict, keys): return result -if os.path.exists(__file__): - current_script_timestamp = os.path.getmtime(__file__) -else: - current_script_timestamp = 0 +def has_disable(commands): + for command in commands: + if command["name"] == "disable": + return True + return False -def is_up_to_date(file, template): - if not os.path.exists(file): - return False - timestamp = os.path.getmtime(file) - return timestamp > max(os.path.getmtime(module_path + template), - current_script_timestamp, json_timestamp) +generate_domains = [] +json_api = {} +json_api["domains"] = parsed_json["domains"] +for domain in parsed_json["domains"]: + generate_domains.append(domain["domain"]) -def generate(class_name): - h_template_name = "/%s_h.template" % class_name - cpp_template_name = "/%s_cpp.template" % class_name +if include_file: + input_file = open(include_file, "r") + json_string = input_file.read() + parsed_json = json.loads(json_string) + json_api["domains"] += parsed_json["domains"] + + +patch_full_qualified_refs() +create_type_definitions() + +if not os.path.exists(output_dirname): + os.mkdir(output_dirname) +jinja_env = initialize_jinja_env(output_dirname) + +h_template_name = "/TypeBuilder_h.template" +cpp_template_name = "/TypeBuilder_cpp.template" +h_template = jinja_env.get_template(h_template_name) +cpp_template = jinja_env.get_template(cpp_template_name) + + +def generate(domain): + class_name = domain["domain"] h_file_name = output_dirname + "/" + class_name + ".h" cpp_file_name = output_dirname + "/" + class_name + ".cpp" - if (is_up_to_date(cpp_file_name, cpp_template_name) and - is_up_to_date(h_file_name, h_template_name)): - return - template_context = { - "class_name": class_name, - "api": json_api, + "domain": domain, "join_arrays": join_arrays, "resolve_type": resolve_type, - "type_definition": type_definition + "type_definition": type_definition, + "has_disable": has_disable, + "export_macro": export_macro, + "output_package": output_package, } - h_template = jinja_env.get_template(h_template_name) - cpp_template = jinja_env.get_template(cpp_template_name) h_file = output_file(h_file_name) cpp_file = output_file(cpp_file_name) h_file.write(h_template.render(template_context)) @@ -307,8 +350,6 @@ def generate(class_name): cpp_file.close() -jinja_env = initialize_jinja_env(output_dirname) -generate("Backend") -generate("Dispatcher") -generate("Frontend") -generate("TypeBuilder") +for domain in json_api["domains"]: + if domain["domain"] in generate_domains: + generate(domain) diff --git a/deps/v8_inspector/platform/inspector_protocol/Collections.h b/deps/v8_inspector/platform/inspector_protocol/Collections.h index 960c747bae4c65..6309284488683e 100644 --- a/deps/v8_inspector/platform/inspector_protocol/Collections.h +++ b/deps/v8_inspector/platform/inspector_protocol/Collections.h @@ -11,4 +11,13 @@ #include "platform/inspector_protocol/CollectionsWTF.h" #endif // V8_INSPECTOR_USE_STL + +// Macro that returns a compile time constant with the length of an array, but gives an error if passed a non-array. +template<typename T, size_t Size> char (&ArrayLengthHelperFunction(T (&)[Size]))[Size]; +// GCC needs some help to deduce a 0 length array. +#if defined(__GNUC__) +template<typename T> char (&ArrayLengthHelperFunction(T (&)[0]))[0]; +#endif +#define PROTOCOL_ARRAY_LENGTH(array) sizeof(::ArrayLengthHelperFunction(array)) + #endif // !defined(Collections_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/CollectionsSTL.h b/deps/v8_inspector/platform/inspector_protocol/CollectionsSTL.h index 09a1d399716f36..ee99cfd8bd855b 100644 --- a/deps/v8_inspector/platform/inspector_protocol/CollectionsSTL.h +++ b/deps/v8_inspector/platform/inspector_protocol/CollectionsSTL.h @@ -5,9 +5,8 @@ #ifndef CollectionsSTL_h #define CollectionsSTL_h +#include "platform/inspector_protocol/Platform.h" #include "platform/inspector_protocol/String16.h" -#include "wtf/Compiler.h" -#include "wtf/PtrUtil.h" #include <algorithm> #include <map> @@ -242,12 +241,4 @@ class HashSet : public protocol::HashMap<K, K> { } // namespace platform } // namespace blink -// Macro that returns a compile time constant with the length of an array, but gives an error if passed a non-array. -template<typename T, size_t Size> char (&ArrayLengthHelperFunction(T (&)[Size]))[Size]; -// GCC needs some help to deduce a 0 length array. -#if COMPILER(GCC) -template<typename T> char (&ArrayLengthHelperFunction(T (&)[0]))[0]; -#endif -#define PROTOCOL_ARRAY_LENGTH(array) sizeof(::ArrayLengthHelperFunction(array)) - #endif // !defined(CollectionsSTL_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/CollectionsWTF.h b/deps/v8_inspector/platform/inspector_protocol/CollectionsWTF.h index 4642c610b84217..5d8fbf625f51b2 100644 --- a/deps/v8_inspector/platform/inspector_protocol/CollectionsWTF.h +++ b/deps/v8_inspector/platform/inspector_protocol/CollectionsWTF.h @@ -5,7 +5,7 @@ #ifndef CollectionsWTF_h #define CollectionsWTF_h -#include "wtf/Assertions.h" +#include "wtf/Allocator.h" #include "wtf/HashMap.h" #include "wtf/PtrUtil.h" #include "wtf/Vector.h" @@ -67,12 +67,12 @@ class Vector<std::unique_ptr<T>> { void resize(size_t s) { m_impl.resize(s); } size_t size() const { return m_impl.size(); } bool isEmpty() const { return m_impl.isEmpty(); } - std::unique_ptr<T>& operator[](size_t i) { return m_impl.at(i); } - const std::unique_ptr<T>& operator[](size_t i) const { return m_impl.at(i); } - std::unique_ptr<T>& at(size_t i) { return m_impl.at(i); } - const std::unique_ptr<T>& at(size_t i) const { return m_impl.at(i); } - std::unique_ptr<T>& last() { return m_impl.last(); } - const std::unique_ptr<T>& last() const { return m_impl.last(); } + T* operator[](size_t i) { return m_impl.at(i).get(); } + const T* operator[](size_t i) const { return m_impl.at(i).get(); } + T* at(size_t i) { return m_impl.at(i).get(); } + const T* at(size_t i) const { return m_impl.at(i).get(); } + T* last() { return m_impl.last().get(); } + const T* last() const { return m_impl.last(); } void append(std::unique_ptr<T> t) { m_impl.append(std::move(t)); } void prepend(std::unique_ptr<T> t) { m_impl.prepend(std::move(t)); } void remove(size_t i) { m_impl.remove(i); } diff --git a/deps/v8_inspector/platform/inspector_protocol/DispatcherBase.cpp b/deps/v8_inspector/platform/inspector_protocol/DispatcherBase.cpp new file mode 100644 index 00000000000000..69798726515c11 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/DispatcherBase.cpp @@ -0,0 +1,173 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/inspector_protocol/DispatcherBase.h" + +#include "platform/inspector_protocol/FrontendChannel.h" +#include "platform/inspector_protocol/Parser.h" + +namespace blink { +namespace protocol { + +// static +const char DispatcherBase::kInvalidRequest[] = "Invalid request"; + +DispatcherBase::WeakPtr::WeakPtr(DispatcherBase* dispatcher) : m_dispatcher(dispatcher) { } + +DispatcherBase::WeakPtr::~WeakPtr() +{ + if (m_dispatcher) + m_dispatcher->m_weakPtrs.remove(this); +} + +DispatcherBase::Callback::Callback(std::unique_ptr<DispatcherBase::WeakPtr> backendImpl, int callId) + : m_backendImpl(std::move(backendImpl)) + , m_callId(callId) { } + +DispatcherBase::Callback::~Callback() = default; + +void DispatcherBase::Callback::dispose() +{ + m_backendImpl = nullptr; +} + +void DispatcherBase::Callback::sendIfActive(std::unique_ptr<protocol::DictionaryValue> partialMessage, const ErrorString& invocationError) +{ + if (!m_backendImpl->get()) + return; + m_backendImpl->get()->sendResponse(m_callId, invocationError, nullptr, std::move(partialMessage)); + m_backendImpl = nullptr; +} + +DispatcherBase::DispatcherBase(FrontendChannel* frontendChannel) + : m_frontendChannel(frontendChannel) { } + +DispatcherBase::~DispatcherBase() +{ + clearFrontend(); +} + +// static +bool DispatcherBase::getCommandName(const String16& message, String16* result) +{ + std::unique_ptr<protocol::Value> value = parseJSON(message); + if (!value) + return false; + + protocol::DictionaryValue* object = DictionaryValue::cast(value.get()); + if (!object) + return false; + + if (!object->getString("method", result)) + return false; + + return true; +} + +void DispatcherBase::sendResponse(int callId, const ErrorString& invocationError, ErrorSupport* errors, std::unique_ptr<protocol::DictionaryValue> result) +{ + if (invocationError.length() || (errors && errors->hasErrors())) { + reportProtocolError(callId, ServerError, invocationError, errors); + return; + } + + std::unique_ptr<protocol::DictionaryValue> responseMessage = DictionaryValue::create(); + responseMessage->setNumber("id", callId); + responseMessage->setObject("result", std::move(result)); + if (m_frontendChannel) + m_frontendChannel->sendProtocolResponse(callId, responseMessage->toJSONString()); +} + +void DispatcherBase::sendResponse(int callId, const ErrorString& invocationError, std::unique_ptr<protocol::DictionaryValue> result) +{ + sendResponse(callId, invocationError, nullptr, std::move(result)); +} + +void DispatcherBase::sendResponse(int callId, const ErrorString& invocationError) +{ + sendResponse(callId, invocationError, nullptr, DictionaryValue::create()); +} + +static void reportProtocolError(FrontendChannel* frontendChannel, int callId, DispatcherBase::CommonErrorCode code, const String16& errorMessage, ErrorSupport* errors) +{ + std::unique_ptr<protocol::DictionaryValue> error = DictionaryValue::create(); + error->setNumber("code", code); + error->setString("message", errorMessage); + DCHECK(error); + if (errors && errors->hasErrors()) + error->setString("data", errors->errors()); + std::unique_ptr<protocol::DictionaryValue> message = DictionaryValue::create(); + message->setObject("error", std::move(error)); + message->setNumber("id", callId); + frontendChannel->sendProtocolResponse(callId, message->toJSONString()); +} + +void DispatcherBase::reportProtocolError(int callId, CommonErrorCode code, const String16& errorMessage, ErrorSupport* errors) +{ + if (m_frontendChannel) + ::blink::protocol::reportProtocolError(m_frontendChannel, callId, code, errorMessage, errors); +} + +void DispatcherBase::clearFrontend() +{ + m_frontendChannel = nullptr; + for (auto& weak : m_weakPtrs) + weak.first->dispose(); + m_weakPtrs.clear(); +} + +std::unique_ptr<DispatcherBase::WeakPtr> DispatcherBase::weakPtr() +{ + std::unique_ptr<DispatcherBase::WeakPtr> weak(new DispatcherBase::WeakPtr(this)); + m_weakPtrs.add(weak.get()); + return weak; +} + +UberDispatcher::UberDispatcher(FrontendChannel* frontendChannel) + : m_frontendChannel(frontendChannel) { } + +void UberDispatcher::registerBackend(const String16& name, std::unique_ptr<protocol::DispatcherBase> dispatcher) +{ + m_dispatchers.set(name, std::move(dispatcher)); +} + +void UberDispatcher::dispatch(const String16& message) +{ + std::unique_ptr<protocol::Value> parsedMessage = parseJSON(message); + if (!parsedMessage) + return; + std::unique_ptr<protocol::DictionaryValue> messageObject = DictionaryValue::cast(std::move(parsedMessage)); + if (!messageObject) + return; + + int callId = 0; + protocol::Value* callIdValue = messageObject->get("id"); + bool success = callIdValue->asNumber(&callId); + if (!success) + return; + + protocol::Value* methodValue = messageObject->get("method"); + String16 method; + success = methodValue && methodValue->asString(&method); + if (!success) + return; + + size_t dotIndex = method.find("."); + if (dotIndex == kNotFound) { + reportProtocolError(m_frontendChannel, callId, DispatcherBase::MethodNotFound, "'" + method + "' wasn't found", nullptr); + return; + } + String16 domain = method.substring(0, dotIndex); + auto it = m_dispatchers.find(domain); + if (it == m_dispatchers.end()) { + reportProtocolError(m_frontendChannel, callId, DispatcherBase::MethodNotFound, "'" + method + "' wasn't found", nullptr); + return; + } + it->second->dispatch(callId, method, std::move(messageObject)); +} + +UberDispatcher::~UberDispatcher() = default; + +} // namespace protocol +} // namespace blink diff --git a/deps/v8_inspector/platform/inspector_protocol/DispatcherBase.h b/deps/v8_inspector/platform/inspector_protocol/DispatcherBase.h new file mode 100644 index 00000000000000..6e8ae28f9e5f0d --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/DispatcherBase.h @@ -0,0 +1,97 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DispatcherBase_h +#define DispatcherBase_h + +#include "platform/inspector_protocol/BackendCallback.h" +#include "platform/inspector_protocol/Collections.h" +#include "platform/inspector_protocol/ErrorSupport.h" +#include "platform/inspector_protocol/Platform.h" +#include "platform/inspector_protocol/String16.h" +#include "platform/inspector_protocol/Values.h" + +namespace blink { +namespace protocol { + +class FrontendChannel; +class WeakPtr; + +class PLATFORM_EXPORT DispatcherBase { + PROTOCOL_DISALLOW_COPY(DispatcherBase); +public: + static const char kInvalidRequest[]; + class PLATFORM_EXPORT WeakPtr { + public: + explicit WeakPtr(DispatcherBase*); + ~WeakPtr(); + DispatcherBase* get() { return m_dispatcher; } + void dispose() { m_dispatcher = nullptr; } + + private: + DispatcherBase* m_dispatcher; + }; + + class PLATFORM_EXPORT Callback : public protocol::BackendCallback { + public: + Callback(std::unique_ptr<WeakPtr> backendImpl, int callId); + virtual ~Callback(); + void dispose(); + + protected: + void sendIfActive(std::unique_ptr<protocol::DictionaryValue> partialMessage, const ErrorString& invocationError); + + private: + std::unique_ptr<WeakPtr> m_backendImpl; + int m_callId; + }; + + explicit DispatcherBase(FrontendChannel*); + virtual ~DispatcherBase(); + + enum CommonErrorCode { + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603, + ServerError = -32000, + }; + + static bool getCommandName(const String16& message, String16* result); + + virtual void dispatch(int callId, const String16& method, std::unique_ptr<protocol::DictionaryValue> messageObject) = 0; + + void sendResponse(int callId, const ErrorString&, ErrorSupport*, std::unique_ptr<protocol::DictionaryValue> result); + void sendResponse(int callId, const ErrorString&, std::unique_ptr<protocol::DictionaryValue> result); + void sendResponse(int callId, const ErrorString&); + + void reportProtocolError(int callId, CommonErrorCode, const String16& errorMessage, ErrorSupport* errors); + void clearFrontend(); + + std::unique_ptr<WeakPtr> weakPtr(); + +private: + FrontendChannel* m_frontendChannel; + protocol::HashSet<WeakPtr*> m_weakPtrs; +}; + +class PLATFORM_EXPORT UberDispatcher { + PROTOCOL_DISALLOW_COPY(UberDispatcher); +public: + explicit UberDispatcher(FrontendChannel*); + void registerBackend(const String16& name, std::unique_ptr<protocol::DispatcherBase>); + void dispatch(const String16& message); + FrontendChannel* channel() { return m_frontendChannel; } + virtual ~UberDispatcher(); + +private: + FrontendChannel* m_frontendChannel; + protocol::HashMap<String16, std::unique_ptr<protocol::DispatcherBase>> m_dispatchers; +}; + +} // namespace platform +} // namespace blink + +#endif // !defined(DispatcherBase_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/Dispatcher_cpp.template b/deps/v8_inspector/platform/inspector_protocol/Dispatcher_cpp.template deleted file mode 100644 index bb7c17b5eed33b..00000000000000 --- a/deps/v8_inspector/platform/inspector_protocol/Dispatcher_cpp.template +++ /dev/null @@ -1,365 +0,0 @@ -// This file is generated - -// Copyright (c) 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "platform/inspector_protocol/{{class_name}}.h" - -#include "platform/inspector_protocol/Collections.h" -#include "platform/inspector_protocol/FrontendChannel.h" -#include "platform/inspector_protocol/Parser.h" - -namespace blink { -namespace protocol { - -using protocol::Maybe; - -class DispatcherImpl; - -class DispatcherImplWeakPtr { -public: - DispatcherImplWeakPtr(DispatcherImpl* dispatcher) : m_dispatcher(dispatcher) { } - ~DispatcherImplWeakPtr(); - DispatcherImpl* get() { return m_dispatcher; } - void dispose() { m_dispatcher = nullptr; } -private: - DispatcherImpl* m_dispatcher; -}; - -class DispatcherImpl : public Dispatcher { -public: - DispatcherImpl(FrontendChannel* frontendChannel) - : m_frontendChannel(frontendChannel) -{% for domain in api.domains %} - , m_{{domain.domain | lower}}Agent(0) -{% endfor %} - { -{% for domain in api.domains %} - {% for command in domain.commands %} - {% if "redirect" in command %}{% continue %}{% endif %} - {% if "handlers" in command and not ("renderer" in command["handlers"]) %}{% continue %}{% endif %} - m_dispatchMap.set("{{domain.domain}}.{{command.name}}", &DispatcherImpl::{{domain.domain}}_{{command.name}}); - {% endfor %} -{% endfor %} - - // Initialize common errors. - m_commonErrors.resize(LastEntry); - m_commonErrors[ParseError] = -32700; - m_commonErrors[InvalidRequest] = -32600; - m_commonErrors[MethodNotFound] = -32601; - m_commonErrors[InvalidParams] = -32602; - m_commonErrors[InternalError] = -32603; - m_commonErrors[ServerError] = -32000; - } - - ~DispatcherImpl() { clearFrontend(); } - - virtual void clearFrontend() - { - m_frontendChannel = nullptr; - for (auto& weak : m_weakPtrs) - weak.first->dispose(); - m_weakPtrs.clear(); - } - - std::unique_ptr<DispatcherImplWeakPtr> weakPtr() - { - std::unique_ptr<DispatcherImplWeakPtr> weak(new DispatcherImplWeakPtr(this)); - m_weakPtrs.add(weak.get()); - return weak; - } - - virtual void dispatch(int sessionId, const String16& message); - virtual void reportProtocolError(int sessionId, int callId, CommonErrorCode, const String16& errorMessage, ErrorSupport* errors) const; - using Dispatcher::reportProtocolError; - - void sendResponse(int sessionId, int callId, const ErrorString& invocationError, ErrorSupport* errors, std::unique_ptr<protocol::DictionaryValue> result); - -{% for domain in api.domains %} - virtual void registerAgent(blink::protocol::Backend::{{domain.domain}}* agent) { DCHECK(!m_{{domain.domain | lower}}Agent); m_{{domain.domain | lower}}Agent = agent; } -{% endfor %} - -private: - friend class DispatcherCallbackBase; - friend class DispatcherImplWeakPtr; - using CallHandler = void (DispatcherImpl::*)(int sessionId, int callId, std::unique_ptr<DictionaryValue> messageObject, ErrorSupport* errors); - using DispatchMap = protocol::HashMap<String16, CallHandler>; - -{% for domain in api.domains %} - {% for command in domain.commands %} - {% if "redirect" in command %}{% continue %}{% endif %} - {% if "handlers" in command and not ("renderer" in command["handlers"]) %}{% continue %}{% endif %} - void {{domain.domain}}_{{command.name}}(int sessionId, int callId, std::unique_ptr<DictionaryValue> requestMessageObject, ErrorSupport*); - {% endfor %} -{% endfor %} - - FrontendChannel* m_frontendChannel; - -{% for domain in api.domains %} - Backend::{{domain.domain}}* m_{{domain.domain | lower}}Agent; -{% endfor %} - - void sendResponse(int sessionId, int callId, ErrorString invocationError, std::unique_ptr<protocol::DictionaryValue> result) - { - sendResponse(sessionId, callId, invocationError, nullptr, std::move(result)); - } - - void sendResponse(int sessionId, int callId, ErrorString invocationError) - { - sendResponse(sessionId, callId, invocationError, nullptr, DictionaryValue::create()); - } - - static const char kInvalidRequest[]; - - DispatchMap m_dispatchMap; - protocol::Vector<int> m_commonErrors; - protocol::HashSet<DispatcherImplWeakPtr*> m_weakPtrs; -}; - -class PLATFORM_EXPORT DispatcherCallbackBase : public protocol::Backend::CallbackBase { -public: - DispatcherCallbackBase(std::unique_ptr<DispatcherImplWeakPtr> backendImpl, int sessionId, int id) - : m_backendImpl(std::move(backendImpl)), m_sessionId(sessionId), m_id(id) { } - virtual ~DispatcherCallbackBase() { } - void dispose() { m_backendImpl = nullptr; } - -protected: - void sendIfActive(std::unique_ptr<protocol::DictionaryValue> partialMessage, const ErrorString& invocationError) - { - if (!m_backendImpl->get()) - return; - m_backendImpl->get()->sendResponse(m_sessionId, m_id, invocationError, nullptr, std::move(partialMessage)); - m_backendImpl = nullptr; - } - -private: - std::unique_ptr<DispatcherImplWeakPtr> m_backendImpl; - int m_sessionId; - int m_id; -}; - -DispatcherImplWeakPtr::~DispatcherImplWeakPtr() -{ - if (m_dispatcher) - m_dispatcher->m_weakPtrs.remove(this); -} - -const char DispatcherImpl::kInvalidRequest[] = "Invalid request"; - -{% for domain in api.domains %} - {% for command in domain.commands %} - {% if "redirect" in command %}{% continue %}{% endif %} - {% if "handlers" in command and not ("renderer" in command["handlers"]) %}{% continue %}{% endif %} - - {% if "async" in command %} - -class PLATFORM_EXPORT {{domain.domain}}{{command.name | to_title_case}}Callback : public Backend::{{domain.domain}}::{{command.name | to_title_case}}Callback, public DispatcherCallbackBase { -public: - {{domain.domain}}{{command.name | to_title_case}}Callback(std::unique_ptr<DispatcherImplWeakPtr> backendImpl, int sessionId, int id) - : DispatcherCallbackBase(std::move(backendImpl), sessionId, id) { } - - void sendSuccess( - {%- for parameter in command.returns -%} - {%- if "optional" in parameter -%} - const Maybe<{{resolve_type(parameter).raw_type}}>& {{parameter.name}} - {%- else -%} - {{resolve_type(parameter).pass_type}} {{parameter.name}} - {%- endif -%} - {%- if not loop.last -%}, {% endif -%} - {%- endfor -%}) override - { - std::unique_ptr<protocol::DictionaryValue> resultObject = DictionaryValue::create(); - {% for parameter in command.returns %} - {% if "optional" in parameter %} - if ({{parameter.name}}.isJust()) - resultObject->setValue("{{parameter.name}}", toValue({{parameter.name}}.fromJust())); - {% else %} - resultObject->setValue("{{parameter.name}}", toValue({{resolve_type(parameter).to_raw_type % parameter.name}})); - {% endif %} - {% endfor %} - sendIfActive(std::move(resultObject), ErrorString()); - } - - void sendFailure(const ErrorString& error) override - { - DCHECK(error.length()); - sendIfActive(nullptr, error); - } -}; - {% endif %} - -void DispatcherImpl::{{domain.domain}}_{{command.name}}(int sessionId, int callId, std::unique_ptr<DictionaryValue> requestMessageObject, ErrorSupport* errors) -{ - if (!m_{{domain.domain | lower}}Agent) - errors->addError("{{domain.domain}} handler is not available."); - - if (errors->hasErrors()) { - reportProtocolError(sessionId, callId, InvalidParams, kInvalidRequest, errors); - return; - } - {% if "parameters" in command %} - - // Prepare input parameters. - protocol::DictionaryValue* object = DictionaryValue::cast(requestMessageObject->get("params")); - errors->push(); - {% for property in command.parameters %} - protocol::Value* {{property.name}}Value = object ? object->get("{{property.name}}") : nullptr; - {% if property.optional %} - Maybe<{{resolve_type(property).raw_type}}> in_{{property.name}}; - if ({{property.name}}Value) { - errors->setName("{{property.name}}"); - in_{{property.name}} = FromValue<{{resolve_type(property).raw_type}}>::parse({{property.name}}Value, errors); - } - {% else %} - errors->setName("{{property.name}}"); - {{resolve_type(property).type}} in_{{property.name}} = FromValue<{{resolve_type(property).raw_type}}>::parse({{property.name}}Value, errors); - {% endif %} - {% endfor %} - errors->pop(); - if (errors->hasErrors()) { - reportProtocolError(sessionId, callId, InvalidParams, kInvalidRequest, errors); - return; - } - {% endif %} - - {% if "async" in command %} - std::unique_ptr<{{domain.domain}}{{command.name | to_title_case}}Callback> callback(new {{domain.domain}}{{command.name | to_title_case}}Callback(weakPtr(), sessionId, callId)); - {% elif "returns" in command %} - // Declare output parameters. - std::unique_ptr<protocol::DictionaryValue> result = DictionaryValue::create(); - {% for property in command.returns %} - {% if "optional" in property %} - Maybe<{{resolve_type(property).raw_type}}> out_{{property.name}}; - {% else %} - {{resolve_type(property).type}} out_{{property.name}}; - {% endif %} - {% endfor %} - {% endif %} - - std::unique_ptr<DispatcherImplWeakPtr> weak = weakPtr(); - ErrorString error; - m_{{domain.domain | lower}}Agent->{{command.name}}(&error - {%- for property in command.parameters -%} - {%- if "optional" in property -%} - , in_{{property.name}} - {%- else -%} - , {{resolve_type(property).to_pass_type % ("in_" + property.name)}} - {%- endif -%} - {%- endfor %} - {%- if "async" in command -%} - , std::move(callback) - {%- elif "returns" in command %} - {%- for property in command.returns -%} - , &out_{{property.name}} - {%- endfor %} - {% endif %}); - {% if "returns" in command and not("async" in command) %} - if (!error.length()) { - {% for parameter in command.returns %} - {% if "optional" in parameter %} - if (out_{{parameter.name}}.isJust()) - result->setValue("{{parameter.name}}", toValue(out_{{parameter.name}}.fromJust())); - {% else %} - result->setValue("{{parameter.name}}", toValue({{resolve_type(parameter).to_raw_type % ("out_" + parameter.name)}})); - {% endif %} - {% endfor %} - } - if (weak->get()) - weak->get()->sendResponse(sessionId, callId, error, std::move(result)); - {% elif not("async" in command) %} - if (weak->get()) - weak->get()->sendResponse(sessionId, callId, error); - {% endif %} -} - {% endfor %} -{% endfor %} - -std::unique_ptr<Dispatcher> Dispatcher::create(FrontendChannel* frontendChannel) -{ - return wrapUnique(new DispatcherImpl(frontendChannel)); -} - -void DispatcherImpl::dispatch(int sessionId, const String16& message) -{ - int callId = 0; - std::unique_ptr<protocol::Value> parsedMessage = parseJSON(message); - DCHECK(parsedMessage); - std::unique_ptr<protocol::DictionaryValue> messageObject = DictionaryValue::cast(std::move(parsedMessage)); - DCHECK(messageObject); - - protocol::Value* callIdValue = messageObject->get("id"); - bool success = callIdValue->asNumber(&callId); - DCHECK(success); - - protocol::Value* methodValue = messageObject->get("method"); - String16 method; - success = methodValue && methodValue->asString(&method); - DCHECK(success); - - protocol::HashMap<String16, CallHandler>::iterator it = m_dispatchMap.find(method); - if (it == m_dispatchMap.end()) { - reportProtocolError(sessionId, callId, MethodNotFound, "'" + method + "' wasn't found"); - return; - } - - protocol::ErrorSupport errors; - ((*this).*(*it->second))(sessionId, callId, std::move(messageObject), &errors); -} - -void DispatcherImpl::sendResponse(int sessionId, int callId, const ErrorString& invocationError, ErrorSupport* errors, std::unique_ptr<protocol::DictionaryValue> result) -{ - if (invocationError.length() || (errors && errors->hasErrors())) { - reportProtocolError(sessionId, callId, ServerError, invocationError, errors); - return; - } - - std::unique_ptr<protocol::DictionaryValue> responseMessage = DictionaryValue::create(); - responseMessage->setNumber("id", callId); - responseMessage->setObject("result", std::move(result)); - if (m_frontendChannel) - m_frontendChannel->sendProtocolResponse(sessionId, callId, std::move(responseMessage)); -} - -void Dispatcher::reportProtocolError(int sessionId, int callId, CommonErrorCode code, const String16& errorMessage) const -{ - ErrorSupport errors; - reportProtocolError(sessionId, callId, code, errorMessage, &errors); -} - -void DispatcherImpl::reportProtocolError(int sessionId, int callId, CommonErrorCode code, const String16& errorMessage, ErrorSupport* errors) const -{ - DCHECK(code >=0); - DCHECK((unsigned)code < m_commonErrors.size()); - DCHECK(m_commonErrors[code]); - std::unique_ptr<protocol::DictionaryValue> error = DictionaryValue::create(); - error->setNumber("code", m_commonErrors[code]); - error->setString("message", errorMessage); - DCHECK(error); - if (errors && errors->hasErrors()) - error->setString("data", errors->errors()); - std::unique_ptr<protocol::DictionaryValue> message = DictionaryValue::create(); - message->setObject("error", std::move(error)); - message->setNumber("id", callId); - if (m_frontendChannel) - m_frontendChannel->sendProtocolResponse(sessionId, callId, std::move(message)); -} - -bool Dispatcher::getCommandName(const String16& message, String16* result) -{ - std::unique_ptr<protocol::Value> value = parseJSON(message); - if (!value) - return false; - - protocol::DictionaryValue* object = DictionaryValue::cast(value.get()); - if (!object) - return false; - - if (!object->getString("method", result)) - return false; - - return true; -} - -} // namespace protocol -} // namespace blink diff --git a/deps/v8_inspector/platform/inspector_protocol/Dispatcher_h.template b/deps/v8_inspector/platform/inspector_protocol/Dispatcher_h.template deleted file mode 100644 index 6a809bbff0742a..00000000000000 --- a/deps/v8_inspector/platform/inspector_protocol/Dispatcher_h.template +++ /dev/null @@ -1,65 +0,0 @@ -// This file is generated - -// Copyright (c) 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef {{class_name}}_h -#define {{class_name}}_h - -#include "platform/inspector_protocol/Backend.h" -#include "platform/inspector_protocol/TypeBuilder.h" - -namespace blink { -namespace protocol { - -class FrontendChannel; -class DispatcherImplWeakPtr; - -class PLATFORM_EXPORT Dispatcher { -public: - static std::unique_ptr<Dispatcher> create(FrontendChannel* frontendChannel); - virtual ~Dispatcher() { } - - class PLATFORM_EXPORT CallbackBase { - public: - CallbackBase(std::unique_ptr<DispatcherImplWeakPtr> backendImpl, int sessionId, int id); - virtual ~CallbackBase(); - void sendFailure(const ErrorString&); - void dispose(); - - protected: - void sendIfActive(std::unique_ptr<protocol::DictionaryValue> partialMessage, const ErrorString& invocationError); - - private: - std::unique_ptr<DispatcherImplWeakPtr> m_backendImpl; - int m_sessionId; - int m_id; - }; - -{% for domain in api.domains %} - virtual void registerAgent(blink::protocol::Backend::{{domain.domain}}*) = 0; -{% endfor %} - - virtual void clearFrontend() = 0; - - enum CommonErrorCode { - ParseError = 0, - InvalidRequest, - MethodNotFound, - InvalidParams, - InternalError, - ServerError, - LastEntry, - }; - - void reportProtocolError(int sessionId, int callId, CommonErrorCode, const String16& errorMessage) const; - virtual void reportProtocolError(int sessionId, int callId, CommonErrorCode, const String16& errorMessage, ErrorSupport*) const = 0; - virtual void dispatch(int sessionId, const String16& message) = 0; - static bool getCommandName(const String16& message, String16* result); -}; - -} // namespace protocol -} // namespace blink - -#endif // !defined({{class_name}}_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/ErrorSupport.cpp b/deps/v8_inspector/platform/inspector_protocol/ErrorSupport.cpp index 87603e980b6e9e..dc9e66373ee271 100644 --- a/deps/v8_inspector/platform/inspector_protocol/ErrorSupport.cpp +++ b/deps/v8_inspector/platform/inspector_protocol/ErrorSupport.cpp @@ -5,7 +5,6 @@ #include "platform/inspector_protocol/ErrorSupport.h" #include "platform/inspector_protocol/String16.h" -#include "wtf/Assertions.h" namespace blink { namespace protocol { diff --git a/deps/v8_inspector/platform/inspector_protocol/ErrorSupport.h b/deps/v8_inspector/platform/inspector_protocol/ErrorSupport.h index 98a3340f2c6fde..bb7e064934b01f 100644 --- a/deps/v8_inspector/platform/inspector_protocol/ErrorSupport.h +++ b/deps/v8_inspector/platform/inspector_protocol/ErrorSupport.h @@ -5,13 +5,15 @@ #ifndef ErrorSupport_h #define ErrorSupport_h -#include "platform/PlatformExport.h" #include "platform/inspector_protocol/Collections.h" +#include "platform/inspector_protocol/Platform.h" #include "platform/inspector_protocol/String16.h" namespace blink { namespace protocol { +using ErrorString = String16; + class PLATFORM_EXPORT ErrorSupport { public: ErrorSupport(); @@ -34,4 +36,6 @@ class PLATFORM_EXPORT ErrorSupport { } // namespace platform } // namespace blink +using blink::protocol::ErrorString; + #endif // !defined(ErrorSupport_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/FrontendChannel.h b/deps/v8_inspector/platform/inspector_protocol/FrontendChannel.h index 27ba557c4a29b5..ee81e951ed8d15 100644 --- a/deps/v8_inspector/platform/inspector_protocol/FrontendChannel.h +++ b/deps/v8_inspector/platform/inspector_protocol/FrontendChannel.h @@ -1,27 +1,6 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #ifndef FrontendChannel_h #define FrontendChannel_h @@ -31,12 +10,12 @@ namespace blink { namespace protocol { -class FrontendChannel { +class PLATFORM_EXPORT FrontendChannel { public: virtual ~FrontendChannel() { } - virtual void sendProtocolResponse(int sessionId, int callId, std::unique_ptr<protocol::DictionaryValue> message) = 0; - virtual void sendProtocolNotification(std::unique_ptr<protocol::DictionaryValue> message) = 0; - virtual void flush() = 0; + virtual void sendProtocolResponse(int callId, const String16& message) = 0; + virtual void sendProtocolNotification(const String16& message) = 0; + virtual void flushProtocolNotifications() = 0; }; } // namespace protocol diff --git a/deps/v8_inspector/platform/inspector_protocol/Frontend_cpp.template b/deps/v8_inspector/platform/inspector_protocol/Frontend_cpp.template deleted file mode 100644 index 137dc8e2b1d0d3..00000000000000 --- a/deps/v8_inspector/platform/inspector_protocol/Frontend_cpp.template +++ /dev/null @@ -1,53 +0,0 @@ -// This file is generated - -// Copyright (c) 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "platform/inspector_protocol/{{class_name}}.h" - -#include "platform/inspector_protocol/String16.h" - -namespace blink { -namespace protocol { - -Frontend::Frontend(FrontendChannel* frontendChannel) - : m_frontendChannel(frontendChannel) -{% for domain in api.domains %} - , m_{{domain.domain | lower}}(frontendChannel) -{% endfor %} -{ -} - -{% for domain in api.domains %} - {% for event in domain.events %} - {% if "handlers" in event and not ("renderer" in event["handlers"]) %}{% continue %}{% endif %} -void Frontend::{{domain.domain}}::{{event.name}}( - {%- for parameter in event.parameters %} - {% if "optional" in parameter -%} - const Maybe<{{resolve_type(parameter).raw_type}}>& - {%- else -%} - {{resolve_type(parameter).pass_type}} - {%- endif %} {{parameter.name}}{%- if not loop.last -%}, {% endif -%} - {% endfor -%}) -{ - std::unique_ptr<protocol::DictionaryValue> jsonMessage = DictionaryValue::create(); - jsonMessage->setString("method", "{{domain.domain}}.{{event.name}}"); - std::unique_ptr<protocol::DictionaryValue> paramsObject = DictionaryValue::create(); - {% for parameter in event.parameters %} - {% if "optional" in parameter %} - if ({{parameter.name}}.isJust()) - paramsObject->setValue("{{parameter.name}}", toValue({{parameter.name}}.fromJust())); - {% else %} - paramsObject->setValue("{{parameter.name}}", toValue({{resolve_type(parameter).to_raw_type % parameter.name}})); - {% endif %} - {% endfor %} - jsonMessage->setObject("params", std::move(paramsObject)); - if (m_frontendChannel) - m_frontendChannel->sendProtocolNotification(std::move(jsonMessage)); -} - {% endfor %} -{% endfor %} - -} // namespace protocol -} // namespace blink diff --git a/deps/v8_inspector/platform/inspector_protocol/Frontend_h.template b/deps/v8_inspector/platform/inspector_protocol/Frontend_h.template deleted file mode 100644 index 79cc64b48e2af8..00000000000000 --- a/deps/v8_inspector/platform/inspector_protocol/Frontend_h.template +++ /dev/null @@ -1,57 +0,0 @@ -// This file is generated - -// Copyright (c) 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef {{class_name}}_h -#define {{class_name}}_h - - -#include "platform/inspector_protocol/FrontendChannel.h" -#include "platform/inspector_protocol/TypeBuilder.h" - -namespace blink { -namespace protocol { - -class PLATFORM_EXPORT Frontend { -public: - Frontend(FrontendChannel*); - FrontendChannel* channel() { return m_frontendChannel; } - -{% for domain in api.domains %} - - class PLATFORM_EXPORT {{domain.domain}} { - public: - static {{domain.domain}}* from(Frontend* frontend) { return &(frontend->m_{{domain.domain | lower}}) ;} - {{domain.domain}}(FrontendChannel* frontendChannel) : m_frontendChannel(frontendChannel) { } - {% for event in domain.events %} - {% if "handlers" in event and not ("renderer" in event["handlers"]) %}{% continue %}{% endif %} - void {{event.name}}( - {%- for parameter in event.parameters -%} - {%- if "optional" in parameter -%} - const Maybe<{{resolve_type(parameter).raw_type}}>& {{parameter.name}} = Maybe<{{resolve_type(parameter).raw_type}}>() - {%- else -%} - {{resolve_type(parameter).pass_type}} {{parameter.name}} - {%- endif -%}{%- if not loop.last -%}, {% endif -%} - {%- endfor -%} - ); - {% endfor %} - - void flush() { m_frontendChannel->flush(); } - private: - FrontendChannel* m_frontendChannel; - }; -{% endfor %} - -private: - FrontendChannel* m_frontendChannel; -{% for domain in api.domains %} - {{domain.domain}} m_{{domain.domain | lower}}; -{% endfor %} -}; - -} // namespace protocol -} // namespace blink - -#endif // !defined({{class_name}}_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/Maybe.h b/deps/v8_inspector/platform/inspector_protocol/Maybe.h index 652d5aa5026a9b..3fcd081d89f12a 100644 --- a/deps/v8_inspector/platform/inspector_protocol/Maybe.h +++ b/deps/v8_inspector/platform/inspector_protocol/Maybe.h @@ -5,8 +5,10 @@ #ifndef Maybe_h #define Maybe_h -#include "platform/PlatformExport.h" -#include "wtf/Assertions.h" +#include "platform/inspector_protocol/Platform.h" +#include "platform/inspector_protocol/String16.h" + +#include <memory> namespace blink { namespace protocol { diff --git a/deps/v8_inspector/platform/inspector_protocol/Object.cpp b/deps/v8_inspector/platform/inspector_protocol/Object.cpp new file mode 100644 index 00000000000000..7bb007d4b73f84 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/Object.cpp @@ -0,0 +1,35 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/inspector_protocol/Object.h" + +namespace blink { +namespace protocol { + +std::unique_ptr<Object> Object::parse(protocol::Value* value, ErrorSupport* errors) +{ + protocol::DictionaryValue* object = DictionaryValue::cast(value); + if (!object) { + errors->addError("object expected"); + return nullptr; + } + return wrapUnique(new Object(wrapUnique(static_cast<DictionaryValue*>(object->clone().release())))); +} + +std::unique_ptr<protocol::DictionaryValue> Object::serialize() const +{ + return DictionaryValue::cast(m_object->clone()); +} + +std::unique_ptr<Object> Object::clone() const +{ + return wrapUnique(new Object(DictionaryValue::cast(m_object->clone()))); +} + +Object::Object(std::unique_ptr<protocol::DictionaryValue> object) : m_object(std::move(object)) { } + +Object::~Object() { } + +} // namespace protocol +} // namespace blink diff --git a/deps/v8_inspector/platform/inspector_protocol/Object.h b/deps/v8_inspector/platform/inspector_protocol/Object.h new file mode 100644 index 00000000000000..22506c08d4570d --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/Object.h @@ -0,0 +1,30 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef Object_h +#define Object_h + +#include "platform/inspector_protocol/ErrorSupport.h" +#include "platform/inspector_protocol/Platform.h" +#include "platform/inspector_protocol/Values.h" + +namespace blink { +namespace protocol { + +class PLATFORM_EXPORT Object { +public: + static std::unique_ptr<Object> parse(protocol::Value*, ErrorSupport*); + ~Object(); + + std::unique_ptr<protocol::DictionaryValue> serialize() const; + std::unique_ptr<Object> clone() const; +private: + explicit Object(std::unique_ptr<protocol::DictionaryValue>); + std::unique_ptr<protocol::DictionaryValue> m_object; +}; + +} // namespace platform +} // namespace blink + +#endif // !defined(Object_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/Parser.cpp b/deps/v8_inspector/platform/inspector_protocol/Parser.cpp index 4425660dca44e1..1c267f871a96b2 100644 --- a/deps/v8_inspector/platform/inspector_protocol/Parser.cpp +++ b/deps/v8_inspector/platform/inspector_protocol/Parser.cpp @@ -6,7 +6,6 @@ #include "platform/inspector_protocol/String16.h" #include "platform/inspector_protocol/Values.h" -#include "wtf/Assertions.h" namespace blink { namespace protocol { diff --git a/deps/v8_inspector/platform/inspector_protocol/Parser.h b/deps/v8_inspector/platform/inspector_protocol/Parser.h index 5f2f44bf6490d8..c6f700ee3040d2 100644 --- a/deps/v8_inspector/platform/inspector_protocol/Parser.h +++ b/deps/v8_inspector/platform/inspector_protocol/Parser.h @@ -5,9 +5,8 @@ #ifndef Parser_h #define Parser_h -#include "platform/PlatformExport.h" +#include "platform/inspector_protocol/Platform.h" #include "platform/inspector_protocol/String16.h" -#include "wtf/PtrUtil.h" namespace blink { namespace protocol { diff --git a/deps/v8_inspector/platform/inspector_protocol/Platform.h b/deps/v8_inspector/platform/inspector_protocol/Platform.h new file mode 100644 index 00000000000000..76ba9302abc047 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/Platform.h @@ -0,0 +1,14 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef protocol_Platform_h +#define protocol_Platform_h + +#if V8_INSPECTOR_USE_STL +#include "platform/inspector_protocol/PlatformSTL.h" +#else +#include "platform/inspector_protocol/PlatformWTF.h" +#endif // V8_INSPECTOR_USE_STL + +#endif // !defined(protocol_Platform_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/PlatformSTL.h b/deps/v8_inspector/platform/inspector_protocol/PlatformSTL.h new file mode 100644 index 00000000000000..3e687b994c951e --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/PlatformSTL.h @@ -0,0 +1,281 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PlatformSTL_h +#define PlatformSTL_h + +#include <memory> + +#define PLATFORM_EXPORT +#ifndef CHECK +#define CHECK(condition) ((void) 0) +#endif +#define DCHECK(condition) ((void) 0) +#define NOTREACHED() +#define DCHECK_EQ(i, j) DCHECK(i == j) +#define DCHECK_GE(i, j) DCHECK(i >= j) +#define DCHECK_LT(i, j) DCHECK(i < j) +#define DCHECK_GT(i, j) DCHECK(i > j) +template <typename T> +inline void USE(T) { } + +#define DEFINE_STATIC_LOCAL(type, name, arguments) \ + static type name; + +#if defined(__APPLE__) && !defined(_LIBCPP_VERSION) + +namespace std { + +template <typename T1, typename T2> +struct is_convertible { +private: + struct True_ { + char x[2]; + }; + struct False_ { + }; + + static True_ helper(T2 const &); + static False_ helper(...); + +public: + static bool const value = ( + sizeof(True_) == sizeof(is_convertible::helper(T1())) + ); +}; + +template <bool, class T = void> +struct enable_if { +}; + +template <class T> +struct enable_if<true, T> { + typedef T type; +}; + +template<class T> +struct remove_extent { + typedef T type; +}; + +template<class T> +struct remove_extent<T[]> { + typedef T type; +}; + +template<class T, std::size_t N> +struct remove_extent<T[N]> { + typedef T type; +}; + +typedef decltype(nullptr) nullptr_t; + +template<class T, T v> +struct integral_constant { + static constexpr T value = v; + typedef T value_type; + typedef integral_constant type; + constexpr operator value_type() const noexcept { return value; } + constexpr value_type operator()() const noexcept { return value; } +}; + +typedef integral_constant<bool, true> true_type; +typedef integral_constant<bool, false> false_type; + +template<class T> +struct is_array : false_type {}; + +template<class T> +struct is_array<T[]> : true_type {}; + +template<class T, std::size_t N> +struct is_array<T[N]> : true_type {}; + +template <typename T> +struct OwnedPtrDeleter { + static void deletePtr(T* ptr) + { + static_assert(sizeof(T) > 0, "type must be complete"); + delete ptr; + } +}; + +template <typename T> +struct OwnedPtrDeleter<T[]> { + static void deletePtr(T* ptr) + { + static_assert(sizeof(T) > 0, "type must be complete"); + delete[] ptr; + } +}; + +template <class T, int n> +struct OwnedPtrDeleter<T[n]> { + static_assert(sizeof(T) < 0, "do not use array with size as type"); +}; + +template <typename T> class unique_ptr { +public: + typedef typename remove_extent<T>::type ValueType; + typedef ValueType* PtrType; + + unique_ptr() : m_ptr(nullptr) {} + unique_ptr(std::nullptr_t) : m_ptr(nullptr) {} + unique_ptr(unique_ptr&&); + template <typename U, typename = typename enable_if<is_convertible<U*, T*>::value>::type> unique_ptr(unique_ptr<U>&&); + + ~unique_ptr() + { + OwnedPtrDeleter<T>::deletePtr(m_ptr); + m_ptr = nullptr; + } + + PtrType get() const { return m_ptr; } + + void reset(PtrType = nullptr); + PtrType release(); + + ValueType& operator*() const { DCHECK(m_ptr); return *m_ptr; } + PtrType operator->() const { DCHECK(m_ptr); return m_ptr; } + + ValueType& operator[](std::ptrdiff_t i) const; + + bool operator!() const { return !m_ptr; } + explicit operator bool() const { return m_ptr; } + + unique_ptr& operator=(std::nullptr_t) { reset(); return *this; } + + + unique_ptr& operator=(unique_ptr&&); + template <typename U> unique_ptr& operator=(unique_ptr<U>&&); + + void swap(unique_ptr& o) { std::swap(m_ptr, o.m_ptr); } + + static T* hashTableDeletedValue() { return reinterpret_cast<T*>(-1); } + + explicit unique_ptr(PtrType ptr) : m_ptr(ptr) {} + +private: + + // We should never have two unique_ptrs for the same underlying object + // (otherwise we'll get double-destruction), so these equality operators + // should never be needed. + template <typename U> bool operator==(const unique_ptr<U>&) const + { + static_assert(!sizeof(U*), "unique_ptrs should never be equal"); + return false; + } + template <typename U> bool operator!=(const unique_ptr<U>&) const + { + static_assert(!sizeof(U*), "unique_ptrs should never be equal"); + return false; + } + + PtrType m_ptr; +}; + + +template <typename T> inline void unique_ptr<T>::reset(PtrType ptr) +{ + PtrType p = m_ptr; + m_ptr = ptr; + OwnedPtrDeleter<T>::deletePtr(p); +} + +template <typename T> inline typename unique_ptr<T>::PtrType unique_ptr<T>::release() +{ + PtrType ptr = m_ptr; + m_ptr = nullptr; + return ptr; +} + +template <typename T> inline typename unique_ptr<T>::ValueType& unique_ptr<T>::operator[](std::ptrdiff_t i) const +{ + static_assert(is_array<T>::value, "elements access is possible for arrays only"); + DCHECK(m_ptr); + DCHECK_GE(i, 0); + return m_ptr[i]; +} + +template <typename T> inline unique_ptr<T>::unique_ptr(unique_ptr<T>&& o) + : m_ptr(o.release()) +{ +} + +template <typename T> +template <typename U, typename> inline unique_ptr<T>::unique_ptr(unique_ptr<U>&& o) + : m_ptr(o.release()) +{ + static_assert(!is_array<T>::value, "pointers to array must never be converted"); +} + +template <typename T> inline unique_ptr<T>& unique_ptr<T>::operator=(unique_ptr<T>&& o) +{ + PtrType ptr = m_ptr; + m_ptr = o.release(); + DCHECK(!ptr || m_ptr != ptr); + OwnedPtrDeleter<T>::deletePtr(ptr); + + return *this; +} + +template <typename T> +template <typename U> inline unique_ptr<T>& unique_ptr<T>::operator=(unique_ptr<U>&& o) +{ + static_assert(!is_array<T>::value, "pointers to array must never be converted"); + PtrType ptr = m_ptr; + m_ptr = o.release(); + DCHECK(!ptr || m_ptr != ptr); + OwnedPtrDeleter<T>::deletePtr(ptr); + + return *this; +} + +template <typename T> inline void swap(unique_ptr<T>& a, unique_ptr<T>& b) +{ + a.swap(b); +} + +template <typename T, typename U> inline bool operator==(const unique_ptr<T>& a, U* b) +{ + return a.get() == b; +} + +template <typename T, typename U> inline bool operator==(T* a, const unique_ptr<U>& b) +{ + return a == b.get(); +} + +template <typename T, typename U> inline bool operator!=(const unique_ptr<T>& a, U* b) +{ + return a.get() != b; +} + +template <typename T, typename U> inline bool operator!=(T* a, const unique_ptr<U>& b) +{ + return a != b.get(); +} + +template <typename T> inline typename unique_ptr<T>::PtrType getPtr(const unique_ptr<T>& p) +{ + return p.get(); +} + +template <typename T> +unique_ptr<T> move(unique_ptr<T>& ptr) +{ + return unique_ptr<T>(ptr.release()); +} + +} + +#endif // defined(__APPLE__) && !defined(_LIBCPP_VERSION) + +template <typename T> +std::unique_ptr<T> wrapUnique(T* ptr) +{ + return std::unique_ptr<T>(ptr); +} + +#endif // PlatformSTL_h diff --git a/deps/v8_inspector/platform/inspector_protocol/PlatformWTF.h b/deps/v8_inspector/platform/inspector_protocol/PlatformWTF.h new file mode 100644 index 00000000000000..3cd659e6881e54 --- /dev/null +++ b/deps/v8_inspector/platform/inspector_protocol/PlatformWTF.h @@ -0,0 +1,14 @@ +// Copyright (c) 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PlatformWTF_h +#define PlatformWTF_h + +#include "platform/PlatformExport.h" +#include "wtf/Assertions.h" +#include "wtf/PtrUtil.h" + +#include <memory> + +#endif // PlatformWTF_h diff --git a/deps/v8_inspector/platform/inspector_protocol/String16STL.cpp b/deps/v8_inspector/platform/inspector_protocol/String16STL.cpp index 9f6afbf07578a8..08c3e448c3b294 100644 --- a/deps/v8_inspector/platform/inspector_protocol/String16STL.cpp +++ b/deps/v8_inspector/platform/inspector_protocol/String16STL.cpp @@ -4,14 +4,14 @@ #include "platform/inspector_protocol/String16STL.h" +#include "platform/inspector_protocol/Platform.h" + #include <algorithm> #include <cctype> #include <cstdio> #include <functional> #include <locale> -#define DCHECK(k) - namespace blink { namespace protocol { @@ -283,7 +283,7 @@ ConversionResult convertUTF16ToUTF8( * @return TRUE or FALSE * @stable ICU 2.8 */ -#define U_IS_BMP(c) ((uint32_t)(c)<=0xffff) +#define U_IS_BMP(c) ((uint32_t)(c) <= 0xffff) /** * Is this code point a supplementary code point (U+10000..U+10ffff)? @@ -291,7 +291,7 @@ ConversionResult convertUTF16ToUTF8( * @return TRUE or FALSE * @stable ICU 2.8 */ -#define U_IS_SUPPLEMENTARY(c) ((uint32_t)((c)-0x10000)<=0xfffff) +#define U_IS_SUPPLEMENTARY(c) ((uint32_t)((c) - 0x10000) <= 0xfffff) /** * Is this code point a surrogate (U+d800..U+dfff)? @@ -299,7 +299,7 @@ ConversionResult convertUTF16ToUTF8( * @return TRUE or FALSE * @stable ICU 2.4 */ -#define U_IS_SURROGATE(c) (((c)&0xfffff800)==0xd800) +#define U_IS_SURROGATE(c) (((c) & 0xfffff800) == 0xd800) /** * Get the lead surrogate (0xd800..0xdbff) for a @@ -308,7 +308,7 @@ ConversionResult convertUTF16ToUTF8( * @return lead surrogate (U+d800..U+dbff) for supplementary * @stable ICU 2.4 */ -#define U16_LEAD(supplementary) (UChar)(((supplementary)>>10)+0xd7c0) +#define U16_LEAD(supplementary) (UChar)(((supplementary) >> 10) + 0xd7c0) /** * Get the trail surrogate (0xdc00..0xdfff) for a @@ -317,7 +317,7 @@ ConversionResult convertUTF16ToUTF8( * @return trail surrogate (U+dc00..U+dfff) for supplementary * @stable ICU 2.4 */ -#define U16_TRAIL(supplementary) (UChar)(((supplementary)&0x3ff)|0xdc00) +#define U16_TRAIL(supplementary) (UChar)(((supplementary) & 0x3ff) | 0xdc00) // This must be called with the length pre-determined by the first byte. // If presented with a length > 4, this returns false. The Unicode @@ -480,7 +480,7 @@ ConversionResult convertUTF8ToUTF16( // Helper to write a three-byte UTF-8 code point to the buffer, caller must check room is available. static inline void putUTF8Triple(char*& buffer, UChar ch) { - DCHECK(ch >= 0x0800); + DCHECK_GE(ch, 0x0800); *buffer++ = static_cast<char>(((ch >> 12) & 0x0F) | 0xE0); *buffer++ = static_cast<char>(((ch >> 6) & 0x3F) | 0x80); *buffer++ = static_cast<char>((ch & 0x3F) | 0x80); diff --git a/deps/v8_inspector/platform/inspector_protocol/String16STL.h b/deps/v8_inspector/platform/inspector_protocol/String16STL.h index 2ac0e2ea7fead5..e76e9775fb9ebc 100644 --- a/deps/v8_inspector/platform/inspector_protocol/String16STL.h +++ b/deps/v8_inspector/platform/inspector_protocol/String16STL.h @@ -5,10 +5,9 @@ #ifndef String16STL_h #define String16STL_h -#include <stdint.h> - #include <cstdlib> #include <cstring> +#include <stdint.h> #include <string> #include <vector> @@ -37,6 +36,7 @@ class String16 { m_impl[i] = characters[i]; } String16(const UChar* characters, size_t size) : m_impl(characters, size) { } + String16 isolatedCopy() const { return String16(m_impl); } unsigned sizeInBytes() const { return m_impl.size() * sizeof(UChar); } const UChar* characters16() const { return m_impl.c_str(); } @@ -103,6 +103,13 @@ class String16 { return m_impl.rfind(str.m_impl, start); } + bool startWith(const String16& s) const + { + if (m_impl.length() < s.m_impl.length()) + return false; + return m_impl.substr(0, s.m_impl.length()) == s.m_impl; + } + bool endsWith(UChar character) const { return m_impl.length() && m_impl[m_impl.length() - 1] == character; @@ -124,8 +131,8 @@ class String16 { } private: - static std::string intToString(int i); - static std::string doubleToString(double d); + static std::string intToString(int); + static std::string doubleToString(double); // presubmit: allow wstring wstring m_impl; mutable bool has_hash = false; diff --git a/deps/v8_inspector/platform/inspector_protocol/String16WTF.h b/deps/v8_inspector/platform/inspector_protocol/String16WTF.h index 4ee9c8df52db4e..57a91d82c6f68d 100644 --- a/deps/v8_inspector/platform/inspector_protocol/String16WTF.h +++ b/deps/v8_inspector/platform/inspector_protocol/String16WTF.h @@ -10,6 +10,7 @@ #include "wtf/text/StringBuilder.h" #include "wtf/text/StringConcatenate.h" #include "wtf/text/StringHash.h" +#include "wtf/text/StringToNumber.h" #include "wtf/text/WTFString.h" namespace blink { @@ -35,6 +36,7 @@ class PLATFORM_EXPORT String16 { operator WTF::String() const { return m_impl; } operator WebString() { return m_impl; } const WTF::String& impl() const { return m_impl; } + String16 isolatedCopy() const { return String16(m_impl.isolatedCopy()); } ~String16() { } @@ -73,7 +75,7 @@ class PLATFORM_EXPORT String16 { class String16Builder { public: String16Builder() { } - void append(const String16& str) { m_impl.append(str); }; + void append(const String16& str) { m_impl.append(StringView(str)); }; void append(UChar c) { m_impl.append(c); }; void append(LChar c) { m_impl.append(c); }; void append(char c) { m_impl.append(c); }; diff --git a/deps/v8_inspector/platform/inspector_protocol/TypeBuilder_cpp.template b/deps/v8_inspector/platform/inspector_protocol/TypeBuilder_cpp.template index d80473a9b79f62..1aeef458f8d4ba 100644 --- a/deps/v8_inspector/platform/inspector_protocol/TypeBuilder_cpp.template +++ b/deps/v8_inspector/platform/inspector_protocol/TypeBuilder_cpp.template @@ -4,38 +4,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "platform/inspector_protocol/{{class_name}}.h" +#include "{{output_package}}/{{domain.domain}}.h" + +#include "platform/inspector_protocol/DispatcherBase.h" namespace blink { namespace protocol { - -std::unique_ptr<Object> Object::parse(protocol::Value* value, ErrorSupport* errors) -{ - protocol::DictionaryValue* object = DictionaryValue::cast(value); - if (!object) { - errors->addError("object expected"); - return nullptr; - } - return wrapUnique(new Object(wrapUnique(static_cast<DictionaryValue*>(object->clone().release())))); -} - -std::unique_ptr<protocol::DictionaryValue> Object::serialize() const -{ - return DictionaryValue::cast(m_object->clone()); -} - -std::unique_ptr<Object> Object::clone() const -{ - return wrapUnique(new Object(DictionaryValue::cast(m_object->clone()))); -} - -Object::Object(std::unique_ptr<protocol::DictionaryValue> object) : m_object(std::move(object)) { } -Object::~Object() { } +namespace {{domain.domain}} { // ------------- Enum values from types. -{% for domain in api.domains %} -namespace {{domain.domain}} { +const char Metainfo::domainName[] = "{{domain.domain}}"; {% for type in domain.types %} {% if "enum" in type %} @@ -103,16 +82,13 @@ std::unique_ptr<{{type.id}}> {{type.id}}::clone() const return parse(serialize().get(), &errors); } {% endfor %} -} // {{domain.domain}} -{% endfor %} // ------------- Enum values from params. -{% for domain in api.domains %} + {% for command in join_arrays(domain, ["commands", "events"]) %} {% for param in join_arrays(command, ["parameters", "returns"]) %} {% if "enum" in param %} -namespace {{domain.domain}} { namespace {{command.name | to_title_case}} { namespace {{param.name | to_title_case}}Enum { {% for literal in param.enum %} @@ -120,11 +96,205 @@ const char* {{ literal | to_title_case}} = "{{literal}}"; {% endfor %} } // {{param.name | to_title_case}}Enum } // {{command.name | to_title_case }} -} // {{domain.domain}} {% endif %} {% endfor %} {% endfor %} -{% endfor %} +// ------------- Frontend notifications. + {% for event in domain.events %} + {% if "handlers" in event and not ("renderer" in event["handlers"]) %}{% continue %}{% endif %} + +void Frontend::{{event.name}}( + {%- for parameter in event.parameters %} + {% if "optional" in parameter -%} + const Maybe<{{resolve_type(parameter).raw_type}}>& + {%- else -%} + {{resolve_type(parameter).pass_type}} + {%- endif %} {{parameter.name}}{%- if not loop.last -%}, {% endif -%} + {% endfor -%}) +{ + std::unique_ptr<protocol::DictionaryValue> jsonMessage = DictionaryValue::create(); + jsonMessage->setString("method", "{{domain.domain}}.{{event.name}}"); + std::unique_ptr<protocol::DictionaryValue> paramsObject = DictionaryValue::create(); + {% for parameter in event.parameters %} + {% if "optional" in parameter %} + if ({{parameter.name}}.isJust()) + paramsObject->setValue("{{parameter.name}}", toValue({{parameter.name}}.fromJust())); + {% else %} + paramsObject->setValue("{{parameter.name}}", toValue({{resolve_type(parameter).to_raw_type % parameter.name}})); + {% endif %} + {% endfor %} + jsonMessage->setObject("params", std::move(paramsObject)); + if (m_frontendChannel) + m_frontendChannel->sendProtocolNotification(jsonMessage->toJSONString()); +} + {% endfor %} + +// --------------------- Dispatcher. + +class DispatcherImpl : public protocol::DispatcherBase { +public: + DispatcherImpl(FrontendChannel* frontendChannel, Backend* backend) + : DispatcherBase(frontendChannel) + , m_backend(backend) { + {% for command in domain.commands %} + {% if "redirect" in command %}{% continue %}{% endif %} + {% if "handlers" in command and not ("renderer" in command["handlers"]) %}{% continue %}{% endif %} + m_dispatchMap.set("{{domain.domain}}.{{command.name}}", &DispatcherImpl::{{command.name}}); + {% endfor %} + } + ~DispatcherImpl() override { } + void dispatch(int callId, const String16& method, std::unique_ptr<protocol::DictionaryValue> messageObject) override; + +protected: + using CallHandler = void (DispatcherImpl::*)(int callId, std::unique_ptr<DictionaryValue> messageObject, ErrorSupport* errors); + using DispatchMap = protocol::HashMap<String16, CallHandler>; + DispatchMap m_dispatchMap; + + {% for command in domain.commands %} + {% if "redirect" in command %}{% continue %}{% endif %} + {% if "handlers" in command and not ("renderer" in command["handlers"]) %}{% continue %}{% endif %} + void {{command.name}}(int callId, std::unique_ptr<DictionaryValue> requestMessageObject, ErrorSupport*); + {% endfor %} + + Backend* m_backend; +}; + +void DispatcherImpl::dispatch(int callId, const String16& method, std::unique_ptr<protocol::DictionaryValue> messageObject) +{ + protocol::HashMap<String16, CallHandler>::iterator it = m_dispatchMap.find(method); + if (it == m_dispatchMap.end()) { + reportProtocolError(callId, MethodNotFound, "'" + method + "' wasn't found", nullptr); + return; + } + + protocol::ErrorSupport errors; + ((*this).*(*it->second))(callId, std::move(messageObject), &errors); +} + + {% for command in domain.commands %} + {% if "redirect" in command %}{% continue %}{% endif %} + {% if "handlers" in command and not ("renderer" in command["handlers"]) %}{% continue %}{% endif %} + {% if "async" in command %} + +class {{command.name | to_title_case}}CallbackImpl : public Backend::{{command.name | to_title_case}}Callback, public DispatcherBase::Callback { +public: + {{command.name | to_title_case}}CallbackImpl(std::unique_ptr<DispatcherBase::WeakPtr> backendImpl, int callId) + : DispatcherBase::Callback(std::move(backendImpl), callId) { } + + void sendSuccess( + {%- for parameter in command.returns -%} + {%- if "optional" in parameter -%} + const Maybe<{{resolve_type(parameter).raw_type}}>& {{parameter.name}} + {%- else -%} + {{resolve_type(parameter).pass_type}} {{parameter.name}} + {%- endif -%} + {%- if not loop.last -%}, {% endif -%} + {%- endfor -%}) override + { + std::unique_ptr<protocol::DictionaryValue> resultObject = DictionaryValue::create(); + {% for parameter in command.returns %} + {% if "optional" in parameter %} + if ({{parameter.name}}.isJust()) + resultObject->setValue("{{parameter.name}}", toValue({{parameter.name}}.fromJust())); + {% else %} + resultObject->setValue("{{parameter.name}}", toValue({{resolve_type(parameter).to_raw_type % parameter.name}})); + {% endif %} + {% endfor %} + sendIfActive(std::move(resultObject), ErrorString()); + } + + void sendFailure(const ErrorString& error) override + { + DCHECK(error.length()); + sendIfActive(nullptr, error); + } + +}; + {% endif %} + +void DispatcherImpl::{{command.name}}(int callId, std::unique_ptr<DictionaryValue> requestMessageObject, ErrorSupport* errors) +{ + {% if "parameters" in command %} + // Prepare input parameters. + protocol::DictionaryValue* object = DictionaryValue::cast(requestMessageObject->get("params")); + errors->push(); + {% for property in command.parameters %} + protocol::Value* {{property.name}}Value = object ? object->get("{{property.name}}") : nullptr; + {% if property.optional %} + Maybe<{{resolve_type(property).raw_type}}> in_{{property.name}}; + if ({{property.name}}Value) { + errors->setName("{{property.name}}"); + in_{{property.name}} = FromValue<{{resolve_type(property).raw_type}}>::parse({{property.name}}Value, errors); + } + {% else %} + errors->setName("{{property.name}}"); + {{resolve_type(property).type}} in_{{property.name}} = FromValue<{{resolve_type(property).raw_type}}>::parse({{property.name}}Value, errors); + {% endif %} + {% endfor %} + errors->pop(); + if (errors->hasErrors()) { + reportProtocolError(callId, InvalidParams, kInvalidRequest, errors); + return; + } + {% endif %} + {% if "async" in command %} + std::unique_ptr<{{command.name | to_title_case}}CallbackImpl> callback(new {{command.name | to_title_case}}CallbackImpl(weakPtr(), callId)); + {% elif "returns" in command %} + // Declare output parameters. + std::unique_ptr<protocol::DictionaryValue> result = DictionaryValue::create(); + {% for property in command.returns %} + {% if "optional" in property %} + Maybe<{{resolve_type(property).raw_type}}> out_{{property.name}}; + {% else %} + {{resolve_type(property).type}} out_{{property.name}}; + {% endif %} + {% endfor %} + {% endif %} + + std::unique_ptr<DispatcherBase::WeakPtr> weak = weakPtr(); + ErrorString error; + m_backend->{{command.name}}(&error + {%- for property in command.parameters -%} + {%- if "optional" in property -%} + , in_{{property.name}} + {%- else -%} + , {{resolve_type(property).to_pass_type % ("in_" + property.name)}} + {%- endif -%} + {%- endfor %} + {%- if "async" in command -%} + , std::move(callback) + {%- elif "returns" in command %} + {%- for property in command.returns -%} + , &out_{{property.name}} + {%- endfor %} + {% endif %}); + {% if "returns" in command and not("async" in command) %} + if (!error.length()) { + {% for parameter in command.returns %} + {% if "optional" in parameter %} + if (out_{{parameter.name}}.isJust()) + result->setValue("{{parameter.name}}", toValue(out_{{parameter.name}}.fromJust())); + {% else %} + result->setValue("{{parameter.name}}", toValue({{resolve_type(parameter).to_raw_type % ("out_" + parameter.name)}})); + {% endif %} + {% endfor %} + } + if (weak->get()) + weak->get()->sendResponse(callId, error, std::move(result)); + {% elif not("async" in command) %} + if (weak->get()) + weak->get()->sendResponse(callId, error); + {% endif %} +} + {% endfor %} + +// static +void Dispatcher::wire(UberDispatcher* dispatcher, Backend* backend) +{ + dispatcher->registerBackend("{{domain.domain}}", wrapUnique(new DispatcherImpl(dispatcher->channel(), backend))); +} + +} // {{domain.domain}} } // namespace protocol } // namespace blink diff --git a/deps/v8_inspector/platform/inspector_protocol/TypeBuilder_h.template b/deps/v8_inspector/platform/inspector_protocol/TypeBuilder_h.template index 144fcb149ffe97..05ca81d3a5714e 100644 --- a/deps/v8_inspector/platform/inspector_protocol/TypeBuilder_h.template +++ b/deps/v8_inspector/platform/inspector_protocol/TypeBuilder_h.template @@ -4,41 +4,34 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef {{class_name}}_h -#define {{class_name}}_h - -#include "platform/PlatformExport.h" +#ifndef protocol_{{domain.domain}}_h +#define protocol_{{domain.domain}}_h + +{% if export_macro == "PLATFORM_EXPORT" %} +#include "platform/inspector_protocol/Platform.h" +{% else %} +#include "core/CoreExport.h" +{% endif %} #include "platform/inspector_protocol/Array.h" +#include "platform/inspector_protocol/BackendCallback.h" +#include "platform/inspector_protocol/DispatcherBase.h" #include "platform/inspector_protocol/ErrorSupport.h" +#include "platform/inspector_protocol/FrontendChannel.h" #include "platform/inspector_protocol/Maybe.h" +#include "platform/inspector_protocol/Object.h" +#include "platform/inspector_protocol/Platform.h" #include "platform/inspector_protocol/String16.h" #include "platform/inspector_protocol/Values.h" #include "platform/inspector_protocol/ValueConversions.h" -#include "wtf/Assertions.h" -#include "wtf/PtrUtil.h" +{% for name in domain.dependencies %} +#include "{{output_package}}/{{name}}.h" +{% endfor %} namespace blink { namespace protocol { - -using ErrorString = String16; - -class PLATFORM_EXPORT Object { -public: - static std::unique_ptr<Object> parse(protocol::Value* value, ErrorSupport* errors); - ~Object(); - - std::unique_ptr<protocol::DictionaryValue> serialize() const; - std::unique_ptr<Object> clone() const; -private: - Object(std::unique_ptr<protocol::DictionaryValue> object); - std::unique_ptr<protocol::DictionaryValue> m_object; -}; - -{% for domain in api.domains %} - -// ------------- Forward declarations and typedefs. - namespace {{domain.domain}} { + +// ------------- Forward and enum declarations. {% for type in domain.types %} {% if type.type == "object" %} {% if "properties" in type %} @@ -53,55 +46,38 @@ using {{type.id}} = Object; using {{type.id}} = {{resolve_type(type).type}}; {% endif %} {% endfor %} -} // {{domain.domain}} -{% endfor %} - -// ------------- Enum values from types. -{% for domain in api.domains %} {% for type in domain.types %} {% if "enum" in type %} -namespace {{domain.domain}} { namespace {{type.id}}Enum { {% for literal in type.enum %} -PLATFORM_EXPORT extern const char* {{ literal | dash_to_camelcase}}; +{{export_macro}} extern const char* {{ literal | dash_to_camelcase}}; {% endfor %} } // {{type.id}}Enum -} // {{domain.domain}} {% endif %} {% endfor %} -{% endfor %} - -// ------------- Enum values from params. -{% for domain in api.domains %} {% for command in join_arrays(domain, ["commands", "events"]) %} {% for param in join_arrays(command, ["parameters", "returns"]) %} {% if "enum" in param %} -namespace {{domain.domain}} { namespace {{command.name | to_title_case}} { namespace {{param.name | to_title_case}}Enum { {% for literal in param.enum %} -PLATFORM_EXPORT extern const char* {{ literal | dash_to_camelcase}}; +{{export_macro}} extern const char* {{ literal | dash_to_camelcase}}; {% endfor %} } // {{param.name | to_title_case}}Enum } // {{command.name | to_title_case }} -} // {{domain.domain}} {% endif %} {% endfor %} {% endfor %} -{% endfor %} // ------------- Type and builder declarations. -{% for domain in api.domains %} - -namespace {{domain.domain}} { {% for type in domain.types %} {% if not (type.type == "object") or not ("properties" in type) %}{% continue %}{% endif %} {% set type_def = type_definition(domain.domain + "." + type.id)%} // {{type.description}} -class PLATFORM_EXPORT {{type.id}} { +class {{export_macro}} {{type.id}} { public: static std::unique_ptr<{{type.id}}> parse(protocol::Value* value, ErrorSupport* errors); @@ -109,7 +85,7 @@ public: {% for property in type.properties %} {% if "enum" in property %} - struct PLATFORM_EXPORT {{property.name | to_title_case}}Enum { + struct {{export_macro}} {{property.name | to_title_case}}Enum { {% for literal in property.enum %} static const char* {{ literal | dash_to_camelcase}}; {% endfor %} @@ -207,12 +183,103 @@ private: {% endfor %} -} // {{domain.domain}} -{% endfor %} +// ------------- Backend interface. + +class {{export_macro}} Backend { +public: + {% for command in domain.commands %} + {% if "redirect" in command %}{% continue %}{% endif %} + {% if ("handlers" in command) and not ("renderer" in command["handlers"]) %}{% continue %}{% endif %} + {% if "async" in command %} + class {{export_macro}} {{command.name | to_title_case}}Callback : public BackendCallback { + public: + virtual void sendSuccess( + {%- for parameter in command.returns -%} + {%- if "optional" in parameter -%} + const Maybe<{{resolve_type(parameter).raw_type}}>& {{parameter.name}} + {%- else -%} + {{resolve_type(parameter).pass_type}} {{parameter.name}} + {%- endif -%} + {%- if not loop.last -%}, {% endif -%} + {%- endfor -%} + ) = 0; + }; + {% endif %} + virtual void {{command.name}}(ErrorString* + {%- for parameter in command.parameters -%} + {%- if "optional" in parameter -%} + , const Maybe<{{resolve_type(parameter).raw_type}}>& in_{{parameter.name}} + {%- else -%} + , {{resolve_type(parameter).pass_type}} in_{{parameter.name}} + {%- endif -%} + {%- endfor -%} + {%- if "async" in command -%} + , std::unique_ptr<{{command.name | to_title_case}}Callback> callback + {%- else -%} + {%- for parameter in command.returns -%} + {%- if "optional" in parameter -%} + , Maybe<{{resolve_type(parameter).raw_type}}>* out_{{parameter.name}} + {%- else -%} + , {{resolve_type(parameter).type}}* out_{{parameter.name}} + {%- endif -%} + {%- endfor -%} + {%- endif -%} + ) = 0; + {% endfor %} + + {% if not has_disable(domain.commands) %} + virtual void disable(ErrorString*) { } + {% endif %} + +protected: + virtual ~Backend() { } +}; +// ------------- Frontend interface. + +class {{export_macro}} Frontend { +public: + Frontend(FrontendChannel* frontendChannel) : m_frontendChannel(frontendChannel) { } + {% for event in domain.events %} + {% if "handlers" in event and not ("renderer" in event["handlers"]) %}{% continue %}{% endif %} + void {{event.name}}( + {%- for parameter in event.parameters -%} + {%- if "optional" in parameter -%} + const Maybe<{{resolve_type(parameter).raw_type}}>& {{parameter.name}} = Maybe<{{resolve_type(parameter).raw_type}}>() + {%- else -%} + {{resolve_type(parameter).pass_type}} {{parameter.name}} + {%- endif -%}{%- if not loop.last -%}, {% endif -%} + {%- endfor -%} + ); + {% endfor %} + + void flush() { m_frontendChannel->flushProtocolNotifications(); } +private: + FrontendChannel* m_frontendChannel; +}; + +// ------------- Dispatcher. + +class {{export_macro}} Dispatcher { +public: + static void wire(UberDispatcher*, blink::protocol::{{domain.domain}}::Backend*); + +private: + Dispatcher() { } +}; + +// ------------- Metainfo. + +class {{export_macro}} Metainfo { +public: + using BackendClass = Backend; + using FrontendClass = Frontend; + using DispatcherClass = Dispatcher; + static const char domainName[]; +}; + +} // namespace {{domain.domain}} } // namespace protocol } // namespace blink -using blink::protocol::ErrorString; - -#endif // !defined({{class_name}}_h) +#endif // !defined(protocol_{{domain.domain}}_h) diff --git a/deps/v8_inspector/platform/inspector_protocol/ValueConversions.h b/deps/v8_inspector/platform/inspector_protocol/ValueConversions.h index 4a5066c928661f..b82f04f588f7f1 100644 --- a/deps/v8_inspector/platform/inspector_protocol/ValueConversions.h +++ b/deps/v8_inspector/platform/inspector_protocol/ValueConversions.h @@ -5,8 +5,8 @@ #ifndef ValueConversions_h #define ValueConversions_h -#include "platform/PlatformExport.h" #include "platform/inspector_protocol/ErrorSupport.h" +#include "platform/inspector_protocol/Platform.h" #include "platform/inspector_protocol/String16.h" #include "platform/inspector_protocol/Values.h" @@ -36,14 +36,7 @@ template<typename T> std::unique_ptr<protocol::Value> toValue(T* param) template<typename T> std::unique_ptr<protocol::Value> toValue(const std::unique_ptr<T>& param) { - static_assert(sizeof(T) == 0, "use raw pointer version."); - return nullptr; -} - -template<typename T> std::unique_ptr<protocol::Value> toValue(std::unique_ptr<T> param) -{ - static_assert(sizeof(T) == 0, "use raw pointer version."); - return nullptr; + return toValue(param.get()); } template<typename T> diff --git a/deps/v8_inspector/platform/inspector_protocol/Values.cpp b/deps/v8_inspector/platform/inspector_protocol/Values.cpp index 979e3d8b2b3bba..9cc1ca51bf484a 100644 --- a/deps/v8_inspector/platform/inspector_protocol/Values.cpp +++ b/deps/v8_inspector/platform/inspector_protocol/Values.cpp @@ -6,7 +6,6 @@ #include "platform/inspector_protocol/Parser.h" #include "platform/inspector_protocol/String16.h" -#include "wtf/Assertions.h" #include <cmath> namespace blink { @@ -346,7 +345,7 @@ void ListValue::pushValue(std::unique_ptr<protocol::Value> value) protocol::Value* ListValue::at(size_t index) { - CHECK(index < m_data.size()); + DCHECK_LT(index, m_data.size()); return m_data[index]; } diff --git a/deps/v8_inspector/platform/inspector_protocol/Values.h b/deps/v8_inspector/platform/inspector_protocol/Values.h index 4202d33ec87dba..e4a719286fb67d 100644 --- a/deps/v8_inspector/platform/inspector_protocol/Values.h +++ b/deps/v8_inspector/platform/inspector_protocol/Values.h @@ -5,11 +5,10 @@ #ifndef Values_h #define Values_h -#include "platform/PlatformExport.h" #include "platform/inspector_protocol/Allocator.h" #include "platform/inspector_protocol/Collections.h" +#include "platform/inspector_protocol/Platform.h" #include "platform/inspector_protocol/String16.h" -#include "wtf/PtrUtil.h" namespace blink { namespace protocol { @@ -27,7 +26,7 @@ class PLATFORM_EXPORT Value { static std::unique_ptr<Value> null() { - return std::unique_ptr<Value>(new Value()); + return wrapUnique(new Value()); } enum ValueType { diff --git a/deps/v8_inspector/platform/inspector_protocol/generate-inspector-protocol-version b/deps/v8_inspector/platform/inspector_protocol/generate-inspector-protocol-version index 514b6dfca43d15..65738d6f38be9c 100755 --- a/deps/v8_inspector/platform/inspector_protocol/generate-inspector-protocol-version +++ b/deps/v8_inspector/platform/inspector_protocol/generate-inspector-protocol-version @@ -45,10 +45,18 @@ # # Adding --show_changes to the command line prints out a list of valid public API changes. +import collections +import copy import os.path +import optparse import re import sys +try: + import json +except ImportError: + import simplejson as json + def list_to_map(items, key): result = {} for item in items: @@ -56,28 +64,34 @@ def list_to_map(items, key): result[item[key]] = item return result + def named_list_to_map(container, name, key): if name in container: return list_to_map(container[name], key) return {} + def removed(reverse): if reverse: return "added" return "removed" + def required(reverse): if reverse: return "optional" return "required" -def compare_schemas(schema_1, schema_2, reverse): + +def compare_schemas(d_1, d_2, reverse): errors = [] - types_1 = normalize_types_in_schema(schema_1) - types_2 = normalize_types_in_schema(schema_2) + domains_1 = copy.deepcopy(d_1) + domains_2 = copy.deepcopy(d_2) + types_1 = normalize_types_in_schema(domains_1) + types_2 = normalize_types_in_schema(domains_2) - domains_by_name_1 = list_to_map(schema_1, "domain") - domains_by_name_2 = list_to_map(schema_2, "domain") + domains_by_name_1 = list_to_map(domains_1, "domain") + domains_by_name_2 = list_to_map(domains_2, "domain") for name in domains_by_name_1: domain_1 = domains_by_name_1[name] @@ -87,6 +101,7 @@ def compare_schemas(schema_1, schema_2, reverse): compare_domains(domain_1, domains_by_name_2[name], types_1, types_2, errors, reverse) return errors + def compare_domains(domain_1, domain_2, types_map_1, types_map_2, errors, reverse): domain_name = domain_1["domain"] commands_1 = named_list_to_map(domain_1, "commands", "name") @@ -107,6 +122,7 @@ def compare_domains(domain_1, domain_2, types_map_1, types_map_2, errors, revers continue compare_events(domain_name, event_1, events_2[name], types_map_1, types_map_2, errors, reverse) + def compare_commands(domain_name, command_1, command_2, types_map_1, types_map_2, errors, reverse): context = domain_name + "." + command_1["name"] @@ -119,12 +135,14 @@ def compare_commands(domain_name, command_1, command_2, types_map_1, types_map_2 returns_2 = named_list_to_map(command_2, "returns", "name") compare_params_list(context, "response parameter", returns_1, returns_2, types_map_1, types_map_2, 0, errors, reverse) + def compare_events(domain_name, event_1, event_2, types_map_1, types_map_2, errors, reverse): context = domain_name + "." + event_1["name"] params_1 = named_list_to_map(event_1, "parameters", "name") params_2 = named_list_to_map(event_2, "parameters", "name") compare_params_list(context, "parameter", params_1, params_2, types_map_1, types_map_2, 0, errors, reverse) + def compare_params_list(context, kind, params_1, params_2, types_map_1, types_map_2, depth, errors, reverse): for name in params_1: param_1 = params_1[name] @@ -141,6 +159,7 @@ def compare_params_list(context, kind, params_1, params_2, types_map_1, types_ma type_2 = extract_type(param_2, types_map_2, errors) compare_types(context + "." + name, kind, type_1, type_2, types_map_1, types_map_2, depth, errors, reverse) + def compare_types(context, kind, type_1, type_2, types_map_1, types_map_2, depth, errors, reverse): if depth > 10: return @@ -165,6 +184,7 @@ def compare_types(context, kind, type_1, type_2, types_map_1, types_map_2, depth item_type_2 = extract_type(type_2["items"], types_map_2, errors) compare_types(context, kind, item_type_1, item_type_2, types_map_1, types_map_2, depth + 1, errors, reverse) + def extract_type(typed_object, types_map, errors): if "type" in typed_object: result = { "id": "<transient>", "type": typed_object["type"] } @@ -180,13 +200,15 @@ def extract_type(typed_object, types_map, errors): types_map[ref] = { "id": "<transient>", "type": "object" } return types_map[ref] -def normalize_types_in_schema(schema): + +def normalize_types_in_schema(domains): types = {} - for domain in schema: + for domain in domains: domain_name = domain["domain"] normalize_types(domain, domain_name, types) return types + def normalize_types(obj, domain_name, types): if isinstance(obj, list): for item in obj: @@ -201,12 +223,16 @@ def normalize_types(obj, domain_name, types): else: normalize_types(value, domain_name, types) -def load_json(filename): - input_file = open(filename, "r") + +def load_schema(file, domains): + if not os.path.isfile(file): + return + input_file = open(file, "r") json_string = input_file.read() - json_string = re.sub(":\s*true", ": True", json_string) - json_string = re.sub(":\s*false", ": False", json_string) - return eval(json_string) + parsed_json = json.loads(json_string) + domains += parsed_json["domains"] + return parsed_json["version"] + def self_test(): def create_test_schema_1(): @@ -392,72 +418,61 @@ def self_test(): compare_schemas(create_test_schema_2(), create_test_schema_1(), True))) + +def load_domains_and_baselines(file, domains, baseline_domains): + version = load_schema(os.path.normpath(file), domains) + suffix = "-%s.%s.json" % (version["major"], version["minor"]) + baseline_file = file.replace(".json", suffix) + load_schema(os.path.normpath(baseline_file), baseline_domains) + return version + + def main(): if not self_test(): sys.stderr.write("Self-test failed") return 1 - if len(sys.argv) < 4 or sys.argv[1] != "-o": - sys.stderr.write("Usage: %s -o OUTPUT_FILE INPUT_FILE [--show-changes]\n" % sys.argv[0]) + cmdline_parser = optparse.OptionParser() + cmdline_parser.add_option("--show_changes") + cmdline_parser.add_option("--o") + arg_options, arg_values = cmdline_parser.parse_args() + + if len(arg_values) < 1 or not arg_options.o: + sys.stderr.write("Usage: %s --o OUTPUT_FILE [--show_changes] PROTOCOL_FOLDER1 ?PROTOCOL_FOLDER2 \n" % sys.argv[0]) return 1 - output_path = sys.argv[2] + output_path = arg_options.o output_file = open(output_path, "w") - input_path = sys.argv[3] - dir_name = os.path.dirname(input_path) - schema = load_json(input_path) - - major = schema["version"]["major"] - minor = schema["version"]["minor"] - version = "%s.%s" % (major, minor) - if len(dir_name) == 0: - dir_name = "." - baseline_path = os.path.normpath(dir_name + "/Inspector-" + version + ".json") - baseline_schema = load_json(baseline_path) + domains = [] + baseline_domains = [] + version = load_domains_and_baselines(arg_values[0], domains, baseline_domains) + if len(arg_values) > 1: + load_domains_and_baselines(arg_values[1], domains, baseline_domains) expected_errors = [ "Debugger.globalObjectCleared: event has been removed" ] - errors = compare_schemas(baseline_schema["domains"], schema["domains"], False) + errors = compare_schemas(baseline_domains, domains, False) unexpected_errors = [] for i in range(len(errors)): if errors[i] not in expected_errors: unexpected_errors.append(errors[i]) if len(unexpected_errors) > 0: - sys.stderr.write(" Compatibility with %s: FAILED\n" % version) + sys.stderr.write(" Compatibility checks FAILED\n") for error in unexpected_errors: sys.stderr.write( " %s\n" % error) return 1 - if len(sys.argv) > 4 and sys.argv[4] == "--show-changes": - changes = compare_schemas( - load_json(input_path)["domains"], load_json(baseline_path)["domains"], True) + if arg_options.show_changes: + changes = compare_schemas(domains, baseline_domains, True) if len(changes) > 0: print " Public changes since %s:" % version for change in changes: print " %s" % change - output_file.write(""" -#ifndef InspectorProtocolVersion_h -#define InspectorProtocolVersion_h - -#include "platform/inspector_protocol/String16.h" - -namespace blink { - -String inspectorProtocolVersion() { return "%s"; } - -int inspectorProtocolVersionMajor() { return %s; } - -int inspectorProtocolVersionMinor() { return %s; } - -} - -#endif // !defined(InspectorProtocolVersion_h) -""" % (version, major, minor)) - + json.dump({"version": version, "domains": domains}, output_file, indent=4, sort_keys=False, separators=(',', ': ')) output_file.close() if __name__ == '__main__': diff --git a/deps/v8_inspector/platform/inspector_protocol/protocol.gyp b/deps/v8_inspector/platform/inspector_protocol/protocol.gyp deleted file mode 100644 index e30f531e7ee7ac..00000000000000 --- a/deps/v8_inspector/platform/inspector_protocol/protocol.gyp +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright 2014 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'variables': { - 'blink_platform_output_dir': '<(SHARED_INTERMEDIATE_DIR)/blink/platform', - 'jinja_module_files': [ - # jinja2/__init__.py contains version string, so sufficient for package - '../../deps/jinja2/jinja2/__init__.py', - '../../deps/markupsafe/markupsafe/__init__.py', # jinja2 dep - ], - }, - - 'targets': [ - { - # GN version: //third_party/WebKit/Source/platform/inspector_protocol_sources - 'target_name': 'protocol_sources', - 'type': 'none', - 'dependencies': [ - 'protocol_version' - ], - 'actions': [ - { - 'action_name': 'generateInspectorProtocolBackendSources', - 'inputs': [ - '<@(jinja_module_files)', - # The python script in action below. - 'CodeGenerator.py', - # Input files for the script. - '../../devtools/protocol.json', - 'Backend_h.template', - 'Dispatcher_h.template', - 'Dispatcher_cpp.template', - 'Frontend_h.template', - 'Frontend_cpp.template', - 'TypeBuilder_h.template', - 'TypeBuilder_cpp.template', - ], - 'outputs': [ - '<(blink_platform_output_dir)/inspector_protocol/Backend.h', - '<(blink_platform_output_dir)/inspector_protocol/Dispatcher.cpp', - '<(blink_platform_output_dir)/inspector_protocol/Dispatcher.h', - '<(blink_platform_output_dir)/inspector_protocol/Frontend.cpp', - '<(blink_platform_output_dir)/inspector_protocol/Frontend.h', - '<(blink_platform_output_dir)/inspector_protocol/TypeBuilder.cpp', - '<(blink_platform_output_dir)/inspector_protocol/TypeBuilder.h', - ], - 'action': [ - 'python', - 'CodeGenerator.py', - '../../devtools/protocol.json', - '--output_dir', '<(blink_platform_output_dir)/inspector_protocol', - ], - 'message': 'Generating Inspector protocol backend sources from protocol.json', - }, - ] - }, - { - # GN version: //third_party/WebKit/Source/platform/inspector_protocol_version - 'target_name': 'protocol_version', - 'type': 'none', - 'actions': [ - { - 'action_name': 'generateInspectorProtocolVersion', - 'inputs': [ - 'generate-inspector-protocol-version', - '../../devtools/protocol.json', - ], - 'outputs': [ - '<(blink_platform_output_dir)/inspector_protocol/InspectorProtocolVersion.h', - ], - 'variables': { - 'generator_include_dirs': [ - ], - }, - 'action': [ - 'python', - 'generate-inspector-protocol-version', - '-o', - '<@(_outputs)', - '<@(_inputs)' - ], - 'message': 'Validate inspector protocol for backwards compatibility and generate version file', - } - ] - }, - ], # targets -} diff --git a/deps/v8_inspector/platform/v8_inspector/Atomics.h b/deps/v8_inspector/platform/v8_inspector/Atomics.h index 8076f19588b402..56cb3ab7ab9129 100644 --- a/deps/v8_inspector/platform/v8_inspector/Atomics.h +++ b/deps/v8_inspector/platform/v8_inspector/Atomics.h @@ -7,14 +7,16 @@ #include <stdint.h> -#if COMPILER(MSVC) +#if defined(_MSC_VER) #include <windows.h> #endif namespace blink { -#if COMPILER(MSVC) +#if defined(_MSC_VER) + inline int atomicIncrement(int volatile* addend) { return InterlockedIncrement(reinterpret_cast<long volatile*>(addend)); } + #else inline int atomicAdd(int volatile* addend, int increment) { return __sync_add_and_fetch(addend, increment); } diff --git a/deps/v8_inspector/platform/v8_inspector/InjectedScript.cpp b/deps/v8_inspector/platform/v8_inspector/InjectedScript.cpp index 65ed3bdce9e25c..868fdeabb4a10b 100644 --- a/deps/v8_inspector/platform/v8_inspector/InjectedScript.cpp +++ b/deps/v8_inspector/platform/v8_inspector/InjectedScript.cpp @@ -335,7 +335,7 @@ std::unique_ptr<protocol::Runtime::ExceptionDetails> InjectedScript::createExcep v8::Local<v8::StackTrace> stackTrace = message->GetStackTrace(); if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0) - exceptionDetailsObject->setStack(m_context->debugger()->createStackTrace(stackTrace, stackTrace->GetFrameCount())->buildInspectorObject()); + exceptionDetailsObject->setStack(m_context->debugger()->createStackTrace(stackTrace)->buildInspectorObject()); return exceptionDetailsObject; } @@ -366,18 +366,10 @@ void InjectedScript::wrapEvaluateResult(ErrorString* errorString, v8::MaybeLocal } } -v8::MaybeLocal<v8::Object> InjectedScript::commandLineAPI(ErrorString* errorString) +v8::Local<v8::Object> InjectedScript::commandLineAPI() { - v8::Isolate* isolate = m_context->isolate(); - if (m_commandLineAPI.IsEmpty()) { - V8FunctionCall function(m_context->debugger(), m_context->context(), v8Value(), "installCommandLineAPI"); - function.appendArgument(V8Console::createCommandLineAPI(m_context)); - bool hadException = false; - v8::Local<v8::Value> extension = function.call(hadException, false); - if (hasInternalError(errorString, hadException || extension.IsEmpty() || !extension->IsObject())) - return v8::MaybeLocal<v8::Object>(); - m_commandLineAPI.Reset(isolate, extension.As<v8::Object>()); - } + if (m_commandLineAPI.IsEmpty()) + m_commandLineAPI.Reset(m_context->isolate(), V8Console::createCommandLineAPI(m_context)); return m_commandLineAPI.Get(m_context->isolate()); } @@ -413,17 +405,8 @@ bool InjectedScript::Scope::initialize() bool InjectedScript::Scope::installCommandLineAPI() { - DCHECK(m_injectedScript && !m_context.IsEmpty() && m_global.IsEmpty()); - v8::Local<v8::Object> extensionObject; - if (!m_injectedScript->commandLineAPI(m_errorString).ToLocal(&extensionObject)) - return false; - m_extensionPrivate = V8Debugger::scopeExtensionPrivate(m_debugger->isolate()); - v8::Local<v8::Object> global = m_context->Global(); - if (!global->SetPrivate(m_context, m_extensionPrivate, extensionObject).FromMaybe(false)) { - *m_errorString = "Internal error"; - return false; - } - m_global = global; + DCHECK(m_injectedScript && !m_context.IsEmpty() && !m_commandLineAPIScope.get()); + m_commandLineAPIScope.reset(new V8Console::CommandLineAPIScope(m_context, m_injectedScript->commandLineAPI(), m_context->Global())); return true; } @@ -454,12 +437,7 @@ void InjectedScript::Scope::pretendUserGesture() void InjectedScript::Scope::cleanup() { - v8::Local<v8::Object> global; - if (m_global.ToLocal(&global)) { - DCHECK(!m_context.IsEmpty()); - global->DeletePrivate(m_context, m_extensionPrivate); - m_global = v8::MaybeLocal<v8::Object>(); - } + m_commandLineAPIScope.reset(); if (!m_context.IsEmpty()) { m_context->Exit(); m_context.Clear(); diff --git a/deps/v8_inspector/platform/v8_inspector/InjectedScript.h b/deps/v8_inspector/platform/v8_inspector/InjectedScript.h index 8a33e73f53eac8..7dae28aba92cce 100644 --- a/deps/v8_inspector/platform/v8_inspector/InjectedScript.h +++ b/deps/v8_inspector/platform/v8_inspector/InjectedScript.h @@ -32,11 +32,12 @@ #define InjectedScript_h #include "platform/inspector_protocol/Allocator.h" -#include "platform/inspector_protocol/TypeBuilder.h" +#include "platform/inspector_protocol/Platform.h" #include "platform/v8_inspector/InjectedScriptNative.h" #include "platform/v8_inspector/InspectedContext.h" +#include "platform/v8_inspector/V8Console.h" #include "platform/v8_inspector/V8DebuggerImpl.h" -#include "wtf/PtrUtil.h" +#include "platform/v8_inspector/protocol/Runtime.h" #include <v8.h> @@ -114,8 +115,7 @@ class InjectedScript final { v8::HandleScope m_handleScope; v8::TryCatch m_tryCatch; v8::Local<v8::Context> m_context; - v8::Local<v8::Private> m_extensionPrivate; - v8::MaybeLocal<v8::Object> m_global; + std::unique_ptr<V8Console::CommandLineAPIScope> m_commandLineAPIScope; bool m_ignoreExceptionsAndMuteConsole; V8DebuggerImpl::PauseOnExceptionsState m_previousPauseOnExceptionsState; bool m_userGesture; @@ -162,7 +162,7 @@ class InjectedScript final { bool canAccessInspectedWindow() const; v8::Local<v8::Value> v8Value() const; v8::MaybeLocal<v8::Value> wrapValue(ErrorString*, v8::Local<v8::Value>, const String16& groupName, bool forceValueType, bool generatePreview) const; - v8::MaybeLocal<v8::Object> commandLineAPI(ErrorString*); + v8::Local<v8::Object> commandLineAPI(); InspectedContext* m_context; v8::Global<v8::Value> m_value; diff --git a/deps/v8_inspector/platform/v8_inspector/InjectedScriptNative.cpp b/deps/v8_inspector/platform/v8_inspector/InjectedScriptNative.cpp index 723eb867faa399..89e48fcbf13cc4 100644 --- a/deps/v8_inspector/platform/v8_inspector/InjectedScriptNative.cpp +++ b/deps/v8_inspector/platform/v8_inspector/InjectedScriptNative.cpp @@ -5,7 +5,6 @@ #include "platform/v8_inspector/InjectedScriptNative.h" #include "platform/inspector_protocol/Values.h" -#include "wtf/Assertions.h" namespace blink { diff --git a/deps/v8_inspector/platform/v8_inspector/InjectedScriptNative.h b/deps/v8_inspector/platform/v8_inspector/InjectedScriptNative.h index 435bcdb8b75c52..63d2a30f3c3165 100644 --- a/deps/v8_inspector/platform/v8_inspector/InjectedScriptNative.h +++ b/deps/v8_inspector/platform/v8_inspector/InjectedScriptNative.h @@ -6,8 +6,8 @@ #define InjectedScriptNative_h #include "platform/inspector_protocol/Collections.h" +#include "platform/inspector_protocol/Platform.h" #include "platform/inspector_protocol/String16.h" -#include "wtf/PtrUtil.h" #include <v8.h> namespace blink { diff --git a/deps/v8_inspector/platform/v8_inspector/InjectedScriptSource.js b/deps/v8_inspector/platform/v8_inspector/InjectedScriptSource.js index bf0ed2bfd208b3..0e0c4f8d28065b 100644 --- a/deps/v8_inspector/platform/v8_inspector/InjectedScriptSource.js +++ b/deps/v8_inspector/platform/v8_inspector/InjectedScriptSource.js @@ -53,20 +53,6 @@ function push(array, var_args) array[array.length] = arguments[i]; } -/** - * @param {(!Arguments.<T>|!NodeList)} array - * @param {number=} index - * @return {!Array.<T>} - * @template T - */ -function slice(array, index) -{ - var result = []; - for (var i = index || 0, j = 0; i < array.length; ++i, ++j) - result[j] = array[i]; - return result; -} - /** * @param {*} obj * @return {string} @@ -405,7 +391,7 @@ InjectedScript.prototype = { if (descriptor) { if (accessorPropertiesOnly && !("get" in descriptor || "set" in descriptor)) continue; - if ("get" in descriptor && "set" in descriptor && name != "__proto__" && InjectedScriptHost.formatAccessorsAsProperties(object) && !doesAttributeHaveObservableSideEffectOnGet(object, name)) { + if ("get" in descriptor && "set" in descriptor && name != "__proto__" && InjectedScriptHost.formatAccessorsAsProperties(object, descriptor.get) && !doesAttributeHaveObservableSideEffectOnGet(object, name)) { descriptor.value = InjectedScriptHost.suppressWarningsAndCallFunction(function(attribute) { return this[attribute]; }, object, [property]); descriptor.isOwn = true; delete descriptor.get; @@ -534,30 +520,6 @@ InjectedScript.prototype = { } }, - /** - * @param {!Object} nativeCommandLineAPI - * @return {!Object} - */ - installCommandLineAPI: function(nativeCommandLineAPI) - { - // NOTE: This list contains only not native Command Line API methods. For full list: V8Console. - // NOTE: Argument names of these methods will be printed in the console, so use pretty names! - var members = [ "$", "$$", "$x", "monitorEvents", "unmonitorEvents", "getEventListeners" ]; - for (var member of members) - nativeCommandLineAPI[member] = CommandLineAPIImpl[member]; - var functionToStringMap = new Map([ - ["$", "function $(selector, [startNode]) { [Command Line API] }"], - ["$$", "function $$(selector, [startNode]) { [Command Line API] }"], - ["$x", "function $x(xpath, [startNode]) { [Command Line API] }"], - ["monitorEvents", "function monitorEvents(object, [types]) { [Command Line API] }"], - ["unmonitorEvents", "function unmonitorEvents(object, [types]) { [Command Line API] }"], - ["getEventListeners", "function getEventListeners(node) { [Command Line API] }"] - ]); - for (let entry of functionToStringMap) - nativeCommandLineAPI[entry[0]].toString = (() => entry[1]); - return nativeCommandLineAPI; - }, - /** * @param {*} object * @return {boolean} @@ -618,7 +580,12 @@ InjectedScript.prototype = { return toString(obj); if (subtype === "node") { - var description = obj.nodeName.toLowerCase(); + var description = ""; + if (obj.nodeName) + description = obj.nodeName.toLowerCase(); + else if (obj.constructor) + description = obj.constructor.name.toLowerCase(); + switch (obj.nodeType) { case 1 /* Node.ELEMENT_NODE */: description += obj.id ? "#" + obj.id : ""; @@ -1044,179 +1011,5 @@ InjectedScript.RemoteObject.prototype = { __proto__: null } -var CommandLineAPIImpl = { __proto__: null } - -/** - * @param {string} selector - * @param {!Node=} opt_startNode - * @return {*} - */ -CommandLineAPIImpl.$ = function (selector, opt_startNode) -{ - if (CommandLineAPIImpl._canQuerySelectorOnNode(opt_startNode)) - return opt_startNode.querySelector(selector); - - return inspectedGlobalObject.document.querySelector(selector); -} - -/** - * @param {string} selector - * @param {!Node=} opt_startNode - * @return {*} - */ -CommandLineAPIImpl.$$ = function (selector, opt_startNode) -{ - if (CommandLineAPIImpl._canQuerySelectorOnNode(opt_startNode)) - return slice(opt_startNode.querySelectorAll(selector)); - return slice(inspectedGlobalObject.document.querySelectorAll(selector)); -} - -/** - * @param {!Node=} node - * @return {boolean} - */ -CommandLineAPIImpl._canQuerySelectorOnNode = function(node) -{ - return !!node && InjectedScriptHost.subtype(node) === "node" && (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.DOCUMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE); -} - -/** - * @param {string} xpath - * @param {!Node=} opt_startNode - * @return {*} - */ -CommandLineAPIImpl.$x = function(xpath, opt_startNode) -{ - var doc = (opt_startNode && opt_startNode.ownerDocument) || inspectedGlobalObject.document; - var result = doc.evaluate(xpath, opt_startNode || doc, null, XPathResult.ANY_TYPE, null); - switch (result.resultType) { - case XPathResult.NUMBER_TYPE: - return result.numberValue; - case XPathResult.STRING_TYPE: - return result.stringValue; - case XPathResult.BOOLEAN_TYPE: - return result.booleanValue; - default: - var nodes = []; - var node; - while (node = result.iterateNext()) - push(nodes, node); - return nodes; - } -} - -/** - * @param {!Object} object - * @param {!Array.<string>|string=} opt_types - */ -CommandLineAPIImpl.monitorEvents = function(object, opt_types) -{ - if (!object || !object.addEventListener || !object.removeEventListener) - return; - var types = CommandLineAPIImpl._normalizeEventTypes(opt_types); - for (var i = 0; i < types.length; ++i) { - object.removeEventListener(types[i], CommandLineAPIImpl._logEvent, false); - object.addEventListener(types[i], CommandLineAPIImpl._logEvent, false); - } -} - -/** - * @param {!Object} object - * @param {!Array.<string>|string=} opt_types - */ -CommandLineAPIImpl.unmonitorEvents = function(object, opt_types) -{ - if (!object || !object.addEventListener || !object.removeEventListener) - return; - var types = CommandLineAPIImpl._normalizeEventTypes(opt_types); - for (var i = 0; i < types.length; ++i) - object.removeEventListener(types[i], CommandLineAPIImpl._logEvent, false); -} - -/** - * @param {!Node} node - * @return {!Object|undefined} - */ -CommandLineAPIImpl.getEventListeners = function(node) -{ - var result = nullifyObjectProto(InjectedScriptHost.getEventListeners(node)); - if (!result) - return; - - // TODO(dtapuska): Remove this one closure compiler is updated - // to handle EventListenerOptions and passive event listeners - // has shipped. Don't JSDoc these otherwise it will fail. - // @param {boolean} capture - // @param {boolean} passive - // @return {boolean|undefined|{capture: (boolean|undefined), passive: boolean}} - function eventListenerOptions(capture, passive) - { - return {"capture": capture, "passive": passive}; - } - - /** - * @param {!Node} node - * @param {string} type - * @param {function()} listener - * @param {boolean} capture - * @param {boolean} passive - */ - function removeEventListenerWrapper(node, type, listener, capture, passive) - { - node.removeEventListener(type, listener, eventListenerOptions(capture, passive)); - } - - /** @this {{type: string, listener: function(), useCapture: boolean, passive: boolean}} */ - var removeFunc = function() - { - removeEventListenerWrapper(node, this.type, this.listener, this.useCapture, this.passive); - } - for (var type in result) { - var listeners = result[type]; - for (var i = 0, listener; listener = listeners[i]; ++i) { - listener["type"] = type; - listener["remove"] = removeFunc; - } - } - return result; -} - -/** - * @param {!Array.<string>|string=} types - * @return {!Array.<string>} - */ -CommandLineAPIImpl._normalizeEventTypes = function(types) -{ - if (typeof types === "undefined") - types = ["mouse", "key", "touch", "pointer", "control", "load", "unload", "abort", "error", "select", "input", "change", "submit", "reset", "focus", "blur", "resize", "scroll", "search", "devicemotion", "deviceorientation"]; - else if (typeof types === "string") - types = [types]; - - var result = []; - for (var i = 0; i < types.length; ++i) { - if (types[i] === "mouse") - push(result, "click", "dblclick", "mousedown", "mouseeenter", "mouseleave", "mousemove", "mouseout", "mouseover", "mouseup", "mouseleave", "mousewheel"); - else if (types[i] === "key") - push(result, "keydown", "keyup", "keypress", "textInput"); - else if (types[i] === "touch") - push(result, "touchstart", "touchmove", "touchend", "touchcancel"); - else if (types[i] === "pointer") - push(result, "pointerover", "pointerout", "pointerenter", "pointerleave", "pointerdown", "pointerup", "pointermove", "pointercancel", "gotpointercapture", "lostpointercapture"); - else if (types[i] === "control") - push(result, "resize", "scroll", "zoom", "focus", "blur", "select", "input", "change", "submit", "reset"); - else - push(result, types[i]); - } - return result; -} - -/** - * @param {!Event} event - */ -CommandLineAPIImpl._logEvent = function(event) -{ - inspectedGlobalObject.console.log(event.type, event); -} - return injectedScript; }) diff --git a/deps/v8_inspector/platform/v8_inspector/InspectorWrapper.cpp b/deps/v8_inspector/platform/v8_inspector/InspectorWrapper.cpp deleted file mode 100644 index 4f5f2784177d0e..00000000000000 --- a/deps/v8_inspector/platform/v8_inspector/InspectorWrapper.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "platform/v8_inspector/InspectorWrapper.h" - -#include "platform/v8_inspector/V8Compat.h" -#include "platform/v8_inspector/public/V8DebuggerClient.h" -#include "wtf/Assertions.h" - -#include <v8-debug.h> - -namespace blink { - -v8::Local<v8::FunctionTemplate> InspectorWrapperBase::createWrapperTemplate(v8::Isolate* isolate, const char* className, const protocol::Vector<V8MethodConfiguration>& methods, const protocol::Vector<V8AttributeConfiguration>& attributes) -{ - v8::Local<v8::FunctionTemplate> functionTemplate = v8::FunctionTemplate::New(isolate); - - functionTemplate->SetClassName(v8::String::NewFromUtf8(isolate, className, v8::NewStringType::kInternalized).ToLocalChecked()); - v8::Local<v8::ObjectTemplate> instanceTemplate = functionTemplate->InstanceTemplate(); - - for (auto& config : attributes) { - v8::Local<v8::Name> v8name = v8::String::NewFromUtf8(isolate, config.name, v8::NewStringType::kInternalized).ToLocalChecked(); - instanceTemplate->SetAccessor(v8name, config.callback); - } - - for (auto& config : methods) { - v8::Local<v8::Name> v8name = v8::String::NewFromUtf8(isolate, config.name, v8::NewStringType::kInternalized).ToLocalChecked(); - v8::Local<v8::FunctionTemplate> functionTemplate = v8::FunctionTemplate::New(isolate, config.callback); - functionTemplate->RemovePrototype(); - instanceTemplate->Set(v8name, functionTemplate, static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::DontEnum | v8::ReadOnly)); - } - - return functionTemplate; -} - -v8::Local<v8::Object> InspectorWrapperBase::createWrapper(v8::Local<v8::FunctionTemplate> constructorTemplate, v8::Local<v8::Context> context) -{ - v8::MicrotasksScope microtasks(context->GetIsolate(), v8::MicrotasksScope::kDoNotRunMicrotasks); - v8::Local<v8::Function> function; - if (!constructorTemplate->GetFunction(context).ToLocal(&function)) - return v8::Local<v8::Object>(); - - v8::Local<v8::Object> result; - if (!function->NewInstance(context).ToLocal(&result)) - return v8::Local<v8::Object>(); - return result; -} - -void* InspectorWrapperBase::unwrap(v8::Local<v8::Context> context, v8::Local<v8::Object> object, const char* name) -{ - v8::Isolate* isolate = context->GetIsolate(); - DCHECK(context != v8::Debug::GetDebugContext(isolate)); - - v8::Local<v8::Private> privateKey = v8::Private::ForApi(isolate, v8::String::NewFromUtf8(isolate, name, v8::NewStringType::kInternalized).ToLocalChecked()); - - v8::Local<v8::Value> value; - if (!object->GetPrivate(context, privateKey).ToLocal(&value)) - return nullptr; - if (!value->IsExternal()) - return nullptr; - return value.As<v8::External>()->Value(); -} - -v8::Local<v8::String> InspectorWrapperBase::v8InternalizedString(v8::Isolate* isolate, const char* name) -{ - return v8::String::NewFromUtf8(isolate, name, v8::NewStringType::kInternalized).ToLocalChecked(); -} - -} // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/InspectorWrapper.h b/deps/v8_inspector/platform/v8_inspector/InspectorWrapper.h deleted file mode 100644 index e28697dbd75824..00000000000000 --- a/deps/v8_inspector/platform/v8_inspector/InspectorWrapper.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef InspectorWrapper_h -#define InspectorWrapper_h - -#include "platform/inspector_protocol/Collections.h" -#include <v8.h> - -namespace blink { - -class InspectorWrapperBase { -public: - struct V8MethodConfiguration { - const char* name; - v8::FunctionCallback callback; - }; - - struct V8AttributeConfiguration { - const char* name; - v8::AccessorNameGetterCallback callback; - }; - - static v8::Local<v8::FunctionTemplate> createWrapperTemplate(v8::Isolate*, const char* className, const protocol::Vector<V8MethodConfiguration>& methods, const protocol::Vector<V8AttributeConfiguration>& attributes); - -protected: - static v8::Local<v8::Object> createWrapper(v8::Local<v8::FunctionTemplate>, v8::Local<v8::Context>); - static void* unwrap(v8::Local<v8::Context>, v8::Local<v8::Object>, const char* name); - - static v8::Local<v8::String> v8InternalizedString(v8::Isolate*, const char* name); -}; - -template<class T, char* const hiddenPropertyName, char* const className> -class InspectorWrapper final : public InspectorWrapperBase { -public: - class WeakCallbackData final { - public: - WeakCallbackData(v8::Isolate* isolate, T* impl, v8::Local<v8::Object> wrapper) - : m_impl(impl) - , m_persistent(isolate, wrapper) - { - m_persistent.SetWeak(this, &WeakCallbackData::weakCallback, v8::WeakCallbackType::kParameter); - } - - T* m_impl; - std::unique_ptr<T> m_implOwn; - - private: - static void weakCallback(const v8::WeakCallbackInfo<WeakCallbackData>& info) - { - delete info.GetParameter(); - } - - v8::Global<v8::Object> m_persistent; - }; - - static v8::Local<v8::FunctionTemplate> createWrapperTemplate(v8::Isolate* isolate, const protocol::Vector<V8MethodConfiguration>& methods, const protocol::Vector<V8AttributeConfiguration>& attributes) - { - return InspectorWrapperBase::createWrapperTemplate(isolate, className, methods, attributes); - } - - static v8::Local<v8::Object> wrap(v8::Local<v8::FunctionTemplate> constructorTemplate, v8::Local<v8::Context> context, T* object) - { - v8::Context::Scope contextScope(context); - v8::Local<v8::Object> result = InspectorWrapperBase::createWrapper(constructorTemplate, context); - if (result.IsEmpty()) - return v8::Local<v8::Object>(); - v8::Isolate* isolate = context->GetIsolate(); - v8::Local<v8::External> objectReference = v8::External::New(isolate, new WeakCallbackData(isolate, object, result)); - - v8::Local<v8::Private> privateKey = v8::Private::ForApi(isolate, v8::String::NewFromUtf8(isolate, hiddenPropertyName, v8::NewStringType::kInternalized).ToLocalChecked()); - result->SetPrivate(context, privateKey, objectReference); - return result; - } - - static T* unwrap(v8::Local<v8::Context> context, v8::Local<v8::Object> object) - { - void* data = InspectorWrapperBase::unwrap(context, object, hiddenPropertyName); - if (!data) - return nullptr; - return reinterpret_cast<WeakCallbackData*>(data)->m_impl; - } -}; - -} // namespace blink - -#endif // InspectorWrapper_h diff --git a/deps/v8_inspector/platform/v8_inspector/JavaScriptCallFrame.h b/deps/v8_inspector/platform/v8_inspector/JavaScriptCallFrame.h index e34d1d03ec09a9..6f9693522eaba9 100644 --- a/deps/v8_inspector/platform/v8_inspector/JavaScriptCallFrame.h +++ b/deps/v8_inspector/platform/v8_inspector/JavaScriptCallFrame.h @@ -32,9 +32,8 @@ #define JavaScriptCallFrame_h #include "platform/inspector_protocol/Collections.h" +#include "platform/inspector_protocol/Platform.h" #include "platform/inspector_protocol/String16.h" -#include "wtf/PtrUtil.h" -#include "wtf/PtrUtil.h" #include <v8.h> namespace blink { diff --git a/deps/v8_inspector/platform/v8_inspector/OWNERS b/deps/v8_inspector/platform/v8_inspector/OWNERS index c34ad775ca7e08..8787a9d384ab32 100644 --- a/deps/v8_inspector/platform/v8_inspector/OWNERS +++ b/deps/v8_inspector/platform/v8_inspector/OWNERS @@ -3,3 +3,9 @@ caseq@chromium.org dgozman@chromium.org kozyatinskiy@chromium.org pfeldman@chromium.org + +# Changes to remote debugging protocol require devtools review to +# ensure backwards compatibility and committment to maintain. +per-file js_protocol.json=set noparent +per-file js_protocol.json=dgozman@chromium.org +per-file js_protocol.json=pfeldman@chromium.org diff --git a/deps/v8_inspector/platform/v8_inspector/RemoteObjectId.cpp b/deps/v8_inspector/platform/v8_inspector/RemoteObjectId.cpp index 1f0332b44fb1a0..9a2a6c00886e96 100644 --- a/deps/v8_inspector/platform/v8_inspector/RemoteObjectId.cpp +++ b/deps/v8_inspector/platform/v8_inspector/RemoteObjectId.cpp @@ -5,8 +5,8 @@ #include "platform/v8_inspector/RemoteObjectId.h" #include "platform/inspector_protocol/Parser.h" +#include "platform/inspector_protocol/Platform.h" #include "platform/inspector_protocol/Values.h" -#include "wtf/PtrUtil.h" namespace blink { diff --git a/deps/v8_inspector/platform/v8_inspector/RemoteObjectId.h b/deps/v8_inspector/platform/v8_inspector/RemoteObjectId.h index ae1cf47ca45be1..af3c4b801f12a8 100644 --- a/deps/v8_inspector/platform/v8_inspector/RemoteObjectId.h +++ b/deps/v8_inspector/platform/v8_inspector/RemoteObjectId.h @@ -5,9 +5,9 @@ #ifndef RemoteObjectId_h #define RemoteObjectId_h +#include "platform/inspector_protocol/ErrorSupport.h" +#include "platform/inspector_protocol/Platform.h" #include "platform/inspector_protocol/String16.h" -#include "platform/inspector_protocol/TypeBuilder.h" -#include "wtf/PtrUtil.h" namespace blink { diff --git a/deps/v8_inspector/platform/v8_inspector/V8Compat.h b/deps/v8_inspector/platform/v8_inspector/V8Compat.h index 0f5b12cbb6cb07..17d104f7736ea7 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8Compat.h +++ b/deps/v8_inspector/platform/v8_inspector/V8Compat.h @@ -7,7 +7,7 @@ #include <v8.h> -#if V8_MAJOR_VERSION < 5 || (V8_MAJOR_VERSION == 5 && V8_MINOR_VERSION < 1) +#if V8_MAJOR_VERSION < 5 || (V8_MAJOR_VERSION == 5 && V8_MINOR_VERSION < 2) namespace v8 { // In standalone V8 inspector this is expected to be noop anyways... @@ -23,6 +23,6 @@ class V8_EXPORT MicrotasksScope { } // namespace v8 -#endif // V8_MAJOR_VERSION < 5 || (V8_MAJOR_VERSION == 5 && V8_MINOR_VERSION < 1) +#endif // V8_MAJOR_VERSION < 5 || (V8_MAJOR_VERSION == 5 && V8_MINOR_VERSION < 2) #endif // V8Compat_h diff --git a/deps/v8_inspector/platform/v8_inspector/V8Console.cpp b/deps/v8_inspector/platform/v8_inspector/V8Console.cpp index f3f7fe171e3bf9..d10b326d151f67 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8Console.cpp +++ b/deps/v8_inspector/platform/v8_inspector/V8Console.cpp @@ -4,6 +4,7 @@ #include "platform/v8_inspector/V8Console.h" +#include "platform/inspector_protocol/Platform.h" #include "platform/inspector_protocol/String16.h" #include "platform/v8_inspector/InjectedScript.h" #include "platform/v8_inspector/InspectedContext.h" @@ -76,18 +77,18 @@ class ConsoleHelper { return m_debuggerClient; } - void addMessage(MessageType type, MessageLevel level, bool allowEmptyArguments, int skipArgumentCount) + void addMessage(MessageType type, MessageLevel level, String16 emptyText, int skipArgumentCount) { - if (!allowEmptyArguments && !m_info.Length()) + if (emptyText.isEmpty() && !m_info.Length()) return; if (V8DebuggerClient* debuggerClient = ensureDebuggerClient()) - debuggerClient->reportMessageToConsole(m_context, type, level, String16(), &m_info, skipArgumentCount, -1); + debuggerClient->reportMessageToConsole(m_context, type, level, m_info.Length() <= skipArgumentCount ? emptyText : String16(), &m_info, skipArgumentCount); } void addMessage(MessageType type, MessageLevel level, const String16& message) { if (V8DebuggerClient* debuggerClient = ensureDebuggerClient()) - debuggerClient->reportMessageToConsole(m_context, type, level, message, nullptr, 0 /* skipArgumentsCount */, 1 /* maxStackSize */); + debuggerClient->reportMessageToConsole(m_context, type, level, message, nullptr, 0 /* skipArgumentsCount */); } void addDeprecationMessage(const char* id, const String16& message) @@ -95,7 +96,7 @@ class ConsoleHelper { if (checkAndSetPrivateFlagOnConsole(id, false)) return; if (V8DebuggerClient* debuggerClient = ensureDebuggerClient()) - debuggerClient->reportMessageToConsole(m_context, LogMessageType, WarningMessageLevel, message, nullptr, 0 /* skipArgumentsCount */, 0 /* maxStackSize */); + debuggerClient->reportMessageToConsole(m_context, LogMessageType, WarningMessageLevel, message, nullptr, 0 /* skipArgumentsCount */); } bool firstArgToBoolean(bool defaultValue) @@ -191,8 +192,8 @@ class ConsoleHelper { V8ProfilerAgentImpl* profilerAgent() { if (V8InspectorSessionImpl* session = currentSession()) { - if (session && session->profilerAgentImpl()->enabled()) - return session->profilerAgentImpl(); + if (session && session->profilerAgent()->enabled()) + return session->profilerAgent(); } return nullptr; } @@ -200,8 +201,8 @@ class ConsoleHelper { V8DebuggerAgentImpl* debuggerAgent() { if (V8InspectorSessionImpl* session = currentSession()) { - if (session && session->debuggerAgentImpl()->enabled()) - return session->debuggerAgentImpl(); + if (session && session->debuggerAgent()->enabled()) + return session->debuggerAgent(); } return nullptr; } @@ -266,67 +267,67 @@ void createBoundFunctionProperty(v8::Local<v8::Context> context, v8::Local<v8::O void V8Console::debugCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { - ConsoleHelper(info).addMessage(LogMessageType, DebugMessageLevel, false, 0); + ConsoleHelper(info).addMessage(LogMessageType, DebugMessageLevel, String16(), 0); } void V8Console::errorCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { - ConsoleHelper(info).addMessage(LogMessageType, ErrorMessageLevel, false, 0); + ConsoleHelper(info).addMessage(LogMessageType, ErrorMessageLevel, String16(), 0); } void V8Console::infoCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { - ConsoleHelper(info).addMessage(LogMessageType, InfoMessageLevel, false, 0); + ConsoleHelper(info).addMessage(LogMessageType, InfoMessageLevel, String16(), 0); } void V8Console::logCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { - ConsoleHelper(info).addMessage(LogMessageType, LogMessageLevel, false, 0); + ConsoleHelper(info).addMessage(LogMessageType, LogMessageLevel, String16(), 0); } void V8Console::warnCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { - ConsoleHelper(info).addMessage(LogMessageType, WarningMessageLevel, false, 0); + ConsoleHelper(info).addMessage(LogMessageType, WarningMessageLevel, String16(), 0); } void V8Console::dirCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { - ConsoleHelper(info).addMessage(DirMessageType, LogMessageLevel, false, 0); + ConsoleHelper(info).addMessage(DirMessageType, LogMessageLevel, String16(), 0); } void V8Console::dirxmlCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { - ConsoleHelper(info).addMessage(DirXMLMessageType, LogMessageLevel, false, 0); + ConsoleHelper(info).addMessage(DirXMLMessageType, LogMessageLevel, String16(), 0); } void V8Console::tableCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { - ConsoleHelper(info).addMessage(TableMessageType, LogMessageLevel, false, 0); + ConsoleHelper(info).addMessage(TableMessageType, LogMessageLevel, String16(), 0); } void V8Console::traceCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { - ConsoleHelper(info).addMessage(TraceMessageType, LogMessageLevel, true, 0); + ConsoleHelper(info).addMessage(TraceMessageType, LogMessageLevel, String16("console.trace"), 0); } void V8Console::groupCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { - ConsoleHelper(info).addMessage(StartGroupMessageType, LogMessageLevel, true, 0); + ConsoleHelper(info).addMessage(StartGroupMessageType, LogMessageLevel, String16("console.group"), 0); } void V8Console::groupCollapsedCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { - ConsoleHelper(info).addMessage(StartGroupCollapsedMessageType, LogMessageLevel, true, 0); + ConsoleHelper(info).addMessage(StartGroupCollapsedMessageType, LogMessageLevel, String16("console.groupCollapsed"), 0); } void V8Console::groupEndCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { - ConsoleHelper(info).addMessage(EndGroupMessageType, LogMessageLevel, true, 0); + ConsoleHelper(info).addMessage(EndGroupMessageType, LogMessageLevel, String16("console.groupEnd"), 0); } void V8Console::clearCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { - ConsoleHelper(info).addMessage(ClearMessageType, LogMessageLevel, true, 0); + ConsoleHelper(info).addMessage(ClearMessageType, LogMessageLevel, String16("console.clear"), 0); } void V8Console::countCallback(const v8::FunctionCallbackInfo<v8::Value>& info) @@ -356,7 +357,7 @@ void V8Console::assertCallback(const v8::FunctionCallbackInfo<v8::Value>& info) ConsoleHelper helper(info); if (helper.firstArgToBoolean(false)) return; - helper.addMessage(AssertMessageType, ErrorMessageLevel, true, 1); + helper.addMessage(AssertMessageType, ErrorMessageLevel, String16("console.assert"), 1); if (V8DebuggerAgentImpl* debuggerAgent = helper.debuggerAgent()) debuggerAgent->breakProgramOnException(protocol::Debugger::Paused::ReasonEnum::Assert, nullptr); } @@ -448,7 +449,7 @@ void V8Console::memoryGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& { if (V8DebuggerClient* client = ConsoleHelper(info).ensureDebuggerClient()) { v8::Local<v8::Value> memoryValue; - if (!client->memoryInfo(info.GetIsolate(), info.GetIsolate()->GetCurrentContext(), info.Holder()).ToLocal(&memoryValue)) + if (!client->memoryInfo(info.GetIsolate(), info.GetIsolate()->GetCurrentContext()).ToLocal(&memoryValue)) return; info.GetReturnValue().Set(memoryValue); } @@ -597,7 +598,7 @@ static void inspectImpl(const v8::FunctionCallbackInfo<v8::Value>& info, bool co if (copyToClipboard) hints->setBoolean("copyToClipboard", true); if (V8InspectorSessionImpl* session = helper.currentSession()) - session->runtimeAgentImpl()->inspect(std::move(wrappedObject), std::move(hints)); + session->runtimeAgent()->inspect(std::move(wrappedObject), std::move(hints)); } void V8Console::inspectCallback(const v8::FunctionCallbackInfo<v8::Value>& info) @@ -656,6 +657,9 @@ v8::Local<v8::Object> V8Console::createConsole(InspectedContext* inspectedContex createBoundFunctionProperty(context, console, "timeEnd", V8Console::timeEndCallback); createBoundFunctionProperty(context, console, "timeStamp", V8Console::timeStampCallback); + bool success = console->SetPrototype(context, v8::Object::New(isolate)).FromMaybe(false); + DCHECK(success); + if (hasMemoryAttribute) console->SetAccessorProperty(toV8StringInternalized(isolate, "memory"), v8::Function::New(isolate, V8Console::memoryGetterCallback, console), v8::Function::New(isolate, V8Console::memorySetterCallback), static_cast<v8::PropertyAttribute>(v8::None), v8::DEFAULT); @@ -663,6 +667,12 @@ v8::Local<v8::Object> V8Console::createConsole(InspectedContext* inspectedContex return console; } +void V8Console::clearInspectedContextIfNeeded(v8::Local<v8::Context> context, v8::Local<v8::Object> console) +{ + v8::Isolate* isolate = context->GetIsolate(); + console->SetPrivate(context, inspectedContextPrivateKey(isolate), v8::External::New(isolate, nullptr)); +} + v8::Local<v8::Object> V8Console::createCommandLineAPI(InspectedContext* inspectedContext) { v8::Local<v8::Context> context = inspectedContext->context(); @@ -693,38 +703,101 @@ v8::Local<v8::Object> V8Console::createCommandLineAPI(InspectedContext* inspecte createBoundFunctionProperty(context, commandLineAPI, "$3", V8Console::inspectedObject3); createBoundFunctionProperty(context, commandLineAPI, "$4", V8Console::inspectedObject4); + inspectedContext->debugger()->client()->installAdditionalCommandLineAPI(context, commandLineAPI); + commandLineAPI->SetPrivate(context, inspectedContextPrivateKey(isolate), v8::External::New(isolate, inspectedContext)); return commandLineAPI; } -void V8Console::clearInspectedContextIfNeeded(v8::Local<v8::Context> context, v8::Local<v8::Object> console) +static bool isCommandLineAPIGetter(const String16& name) { - v8::Isolate* isolate = context->GetIsolate(); - console->SetPrivate(context, inspectedContextPrivateKey(isolate), v8::External::New(isolate, nullptr)); + if (name.length() != 2) + return false; + // $0 ... $4, $_ + return name[0] == '$' && ((name[1] >= '0' && name[1] <= '4') || name[1] == '_'); +} + +void V8Console::CommandLineAPIScope::accessorGetterCallback(v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) +{ + CommandLineAPIScope* scope = static_cast<CommandLineAPIScope*>(info.Data().As<v8::External>()->Value()); + DCHECK(scope); + + v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); + if (scope->m_cleanup) { + bool removed = info.Holder()->Delete(context, name).FromMaybe(false); + DCHECK(removed); + return; + } + v8::Local<v8::Object> commandLineAPI = scope->m_commandLineAPI; + + v8::Local<v8::Value> value; + if (!commandLineAPI->Get(context, name).ToLocal(&value)) + return; + if (isCommandLineAPIGetter(toProtocolStringWithTypeCheck(name))) { + DCHECK(value->IsFunction()); + v8::MicrotasksScope microtasks(info.GetIsolate(), v8::MicrotasksScope::kDoNotRunMicrotasks); + if (value.As<v8::Function>()->Call(context, commandLineAPI, 0, nullptr).ToLocal(&value)) + info.GetReturnValue().Set(value); + } else { + info.GetReturnValue().Set(value); + } +} + +void V8Console::CommandLineAPIScope::accessorSetterCallback(v8::Local<v8::Name> name, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info) +{ + CommandLineAPIScope* scope = static_cast<CommandLineAPIScope*>(info.Data().As<v8::External>()->Value()); + v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); + if (!info.Holder()->Delete(context, name).FromMaybe(false)) + return; + if (!info.Holder()->CreateDataProperty(context, name, value).FromMaybe(false)) + return; + bool removed = scope->m_installedMethods->Delete(context, name).FromMaybe(false); + DCHECK(removed); } -bool V8Debugger::isCommandLineAPIMethod(const String16& name) +V8Console::CommandLineAPIScope::CommandLineAPIScope(v8::Local<v8::Context> context, v8::Local<v8::Object> commandLineAPI, v8::Local<v8::Object> global) + : m_context(context) + , m_commandLineAPI(commandLineAPI) + , m_global(global) + , m_installedMethods(v8::Set::New(context->GetIsolate())) + , m_cleanup(false) { - static protocol::HashSet<String16> methods; - if (methods.size() == 0) { - const char* members[] = { "$", "$$", "$x", "dir", "dirxml", "keys", "values", "profile", "profileEnd", - "monitorEvents", "unmonitorEvents", "inspect", "copy", "clear", "getEventListeners", - "debug", "undebug", "monitor", "unmonitor", "table" }; - for (size_t i = 0; i < PROTOCOL_ARRAY_LENGTH(members); ++i) - methods.add(members[i]); + v8::Local<v8::Array> names; + if (!m_commandLineAPI->GetOwnPropertyNames(context).ToLocal(&names)) + return; + v8::Local<v8::External> externalThis = v8::External::New(context->GetIsolate(), this); + for (size_t i = 0; i < names->Length(); ++i) { + v8::Local<v8::Value> name; + if (!names->Get(context, i).ToLocal(&name) || !name->IsName()) + continue; + if (m_global->Has(context, name).FromMaybe(true)) + continue; + if (!m_installedMethods->Add(context, name).ToLocal(&m_installedMethods)) + continue; + if (!m_global->SetAccessor(context, v8::Local<v8::Name>::Cast(name), CommandLineAPIScope::accessorGetterCallback, + CommandLineAPIScope::accessorSetterCallback, externalThis, + v8::DEFAULT, v8::DontEnum).FromMaybe(false)) { + bool removed = m_installedMethods->Delete(context, name).FromMaybe(false); + DCHECK(removed); + continue; + } } - return methods.find(name) != methods.end(); } -bool V8Debugger::isCommandLineAPIGetter(const String16& name) +V8Console::CommandLineAPIScope::~CommandLineAPIScope() { - protocol::HashSet<String16> getters; - if (getters.size() == 0) { - const char* members[] = { "$0", "$1", "$2", "$3", "$4", "$_" }; - for (size_t i = 0; i < PROTOCOL_ARRAY_LENGTH(members); ++i) - getters.add(members[i]); + m_cleanup = true; + v8::Local<v8::Array> names = m_installedMethods->AsArray(); + for (size_t i = 0; i < names->Length(); ++i) { + v8::Local<v8::Value> name; + if (!names->Get(m_context, i).ToLocal(&name) || !name->IsName()) + continue; + if (name->IsString()) { + v8::Local<v8::Value> descriptor; + bool success = m_global->GetOwnPropertyDescriptor(m_context, v8::Local<v8::String>::Cast(name)).ToLocal(&descriptor); + DCHECK(success); + } } - return getters.find(name) != getters.end(); } } // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/V8Console.h b/deps/v8_inspector/platform/v8_inspector/V8Console.h index bd5fb8cd296fe1..b539f4c0ad03ed 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8Console.h +++ b/deps/v8_inspector/platform/v8_inspector/V8Console.h @@ -5,6 +5,7 @@ #ifndef V8Console_h #define V8Console_h +#include "platform/inspector_protocol/Allocator.h" #include <v8.h> namespace blink { @@ -16,8 +17,25 @@ class InspectedContext; class V8Console { public: static v8::Local<v8::Object> createConsole(InspectedContext*, bool hasMemoryAttribute); - static v8::Local<v8::Object> createCommandLineAPI(InspectedContext*); static void clearInspectedContextIfNeeded(v8::Local<v8::Context>, v8::Local<v8::Object> console); + static v8::Local<v8::Object> createCommandLineAPI(InspectedContext*); + + class CommandLineAPIScope { + PROTOCOL_DISALLOW_COPY(CommandLineAPIScope); + public: + CommandLineAPIScope(v8::Local<v8::Context>, v8::Local<v8::Object> commandLineAPI, v8::Local<v8::Object> global); + ~CommandLineAPIScope(); + + private: + static void accessorGetterCallback(v8::Local<v8::Name>, const v8::PropertyCallbackInfo<v8::Value>&); + static void accessorSetterCallback(v8::Local<v8::Name>, v8::Local<v8::Value>, const v8::PropertyCallbackInfo<void>&); + + v8::Local<v8::Context> m_context; + v8::Local<v8::Object> m_commandLineAPI; + v8::Local<v8::Object> m_global; + v8::Local<v8::Set> m_installedMethods; + bool m_cleanup; + }; private: static void debugCallback(const v8::FunctionCallbackInfo<v8::Value>&); @@ -43,7 +61,7 @@ class V8Console { static void timeCallback(const v8::FunctionCallbackInfo<v8::Value>&); static void timeEndCallback(const v8::FunctionCallbackInfo<v8::Value>&); static void timeStampCallback(const v8::FunctionCallbackInfo<v8::Value>&); - // TODO(philipj): There is no spec for the Memory Info API, see blink-dev: + // TODO(foolip): There is no spec for the Memory Info API, see blink-dev: // https://groups.google.com/a/chromium.org/d/msg/blink-dev/g5YRCGpC9vs/b4OJz71NmPwJ static void memoryGetterCallback(const v8::FunctionCallbackInfo<v8::Value>&); static void memorySetterCallback(const v8::FunctionCallbackInfo<v8::Value>&); diff --git a/deps/v8_inspector/platform/v8_inspector/V8DebuggerAgentImpl.cpp b/deps/v8_inspector/platform/v8_inspector/V8DebuggerAgentImpl.cpp index 57cad6bdb36615..e95d4af0f57210 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8DebuggerAgentImpl.cpp +++ b/deps/v8_inspector/platform/v8_inspector/V8DebuggerAgentImpl.cpp @@ -155,12 +155,12 @@ static std::unique_ptr<protocol::Debugger::Location> buildProtocolLocation(const .setColumnNumber(columnNumber).build(); } -V8DebuggerAgentImpl::V8DebuggerAgentImpl(V8InspectorSessionImpl* session) +V8DebuggerAgentImpl::V8DebuggerAgentImpl(V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel, protocol::DictionaryValue* state) : m_debugger(session->debugger()) , m_session(session) , m_enabled(false) - , m_state(nullptr) - , m_frontend(nullptr) + , m_state(state) + , m_frontend(frontendChannel) , m_isolate(m_debugger->isolate()) , m_breakReason(protocol::Debugger::Paused::ReasonEnum::Other) , m_scheduledDebuggerStep(NoStep) @@ -223,7 +223,6 @@ void V8DebuggerAgentImpl::enable(ErrorString* errorString) } enable(); - DCHECK(m_frontend); } void V8DebuggerAgentImpl::disable(ErrorString*) @@ -272,19 +271,6 @@ void V8DebuggerAgentImpl::internalSetAsyncCallStackDepth(int depth) } } -void V8DebuggerAgentImpl::setInspectorState(protocol::DictionaryValue* state) -{ - m_state = state; -} - -void V8DebuggerAgentImpl::clearFrontend() -{ - ErrorString error; - disable(&error); - DCHECK(m_frontend); - m_frontend = nullptr; -} - void V8DebuggerAgentImpl::restore() { DCHECK(!m_enabled); @@ -1027,6 +1013,7 @@ void V8DebuggerAgentImpl::asyncTaskStarted(void* task) if (!m_maxAsyncCallStackDepth) return; + m_currentTasks.append(task); V8StackTraceImpl* stack = m_asyncTaskStacks.get(task); // Needs to support following order of events: // - asyncTaskScheduled @@ -1035,7 +1022,7 @@ void V8DebuggerAgentImpl::asyncTaskStarted(void* task) // - asyncTaskCanceled <-- canceled before finished // <-- async stack requested here --> // - asyncTaskFinished - m_currentStacks.append(stack ? stack->clone() : nullptr); + m_currentStacks.append(stack ? stack->cloneImpl() : nullptr); } void V8DebuggerAgentImpl::asyncTaskFinished(void* task) @@ -1046,6 +1033,9 @@ void V8DebuggerAgentImpl::asyncTaskFinished(void* task) if (!m_currentStacks.size()) return; + DCHECK(m_currentTasks.last() == task); + m_currentTasks.removeLast(); + m_currentStacks.removeLast(); if (!m_recurringTasks.contains(task)) m_asyncTaskStacks.remove(task); @@ -1056,6 +1046,7 @@ void V8DebuggerAgentImpl::allAsyncTasksCanceled() m_asyncTaskStacks.clear(); m_recurringTasks.clear(); m_currentStacks.clear(); + m_currentTasks.clear(); } void V8DebuggerAgentImpl::setBlackboxPatterns(ErrorString* errorString, std::unique_ptr<protocol::Array<String16>> patterns) @@ -1079,7 +1070,7 @@ void V8DebuggerAgentImpl::setBlackboxPatterns(ErrorString* errorString, std::uni bool V8DebuggerAgentImpl::setBlackboxPattern(ErrorString* errorString, const String16& pattern) { - std::unique_ptr<V8Regex> regex = wrapUnique(new V8Regex(m_debugger, pattern, true /** caseSensitive */, false /** multiline */)); + std::unique_ptr<V8Regex> regex(new V8Regex(m_debugger, pattern, true /** caseSensitive */, false /** multiline */)); if (!regex->isValid()) { *errorString = "Pattern parser error: " + regex->errorMessage(); return false; @@ -1283,9 +1274,9 @@ void V8DebuggerAgentImpl::didParseSource(const V8DebuggerParsedScript& parsedScr const bool* hasSourceURLParam = hasSourceURL ? &hasSourceURL : nullptr; const bool* deprecatedCommentWasUsedParam = deprecatedCommentWasUsed ? &deprecatedCommentWasUsed : nullptr; if (parsedScript.success) - m_frontend->scriptParsed(parsedScript.scriptId, scriptURL, script.startLine(), script.startColumn(), script.endLine(), script.endColumn(), executionContextId, script.hash(), isContentScriptParam, isInternalScriptParam, isLiveEditParam, sourceMapURLParam, hasSourceURLParam, deprecatedCommentWasUsedParam); + m_frontend.scriptParsed(parsedScript.scriptId, scriptURL, script.startLine(), script.startColumn(), script.endLine(), script.endColumn(), executionContextId, script.hash(), isContentScriptParam, isInternalScriptParam, isLiveEditParam, sourceMapURLParam, hasSourceURLParam, deprecatedCommentWasUsedParam); else - m_frontend->scriptFailedToParse(parsedScript.scriptId, scriptURL, script.startLine(), script.startColumn(), script.endLine(), script.endColumn(), executionContextId, script.hash(), isContentScriptParam, isInternalScriptParam, sourceMapURLParam, hasSourceURLParam, deprecatedCommentWasUsedParam); + m_frontend.scriptFailedToParse(parsedScript.scriptId, scriptURL, script.startLine(), script.startColumn(), script.endLine(), script.endColumn(), executionContextId, script.hash(), isContentScriptParam, isInternalScriptParam, sourceMapURLParam, hasSourceURLParam, deprecatedCommentWasUsedParam); m_scripts.set(parsedScript.scriptId, script); @@ -1311,7 +1302,7 @@ void V8DebuggerAgentImpl::didParseSource(const V8DebuggerParsedScript& parsedScr breakpointObject->getString(DebuggerAgentState::condition, &breakpoint.condition); std::unique_ptr<protocol::Debugger::Location> location = resolveBreakpoint(cookie.first, parsedScript.scriptId, breakpoint, UserBreakpointSource); if (location) - m_frontend->breakpointResolved(cookie.first, std::move(location)); + m_frontend.breakpointResolved(cookie.first, std::move(location)); } } @@ -1371,7 +1362,7 @@ V8DebuggerAgentImpl::SkipPauseRequest V8DebuggerAgentImpl::didPause(v8::Local<v8 } ErrorString errorString; - m_frontend->paused(currentCallFrames(&errorString), m_breakReason, std::move(m_breakAuxData), std::move(hitBreakpointIds), currentAsyncStackTrace()); + m_frontend.paused(currentCallFrames(&errorString), m_breakReason, std::move(m_breakAuxData), std::move(hitBreakpointIds), currentAsyncStackTrace()); m_scheduledDebuggerStep = NoStep; m_javaScriptPauseScheduled = false; m_steppingFromFramework = false; @@ -1392,7 +1383,7 @@ void V8DebuggerAgentImpl::didContinue() JavaScriptCallFrames emptyCallFrames; m_pausedCallFrames.swap(emptyCallFrames); clearBreakDetails(); - m_frontend->resumed(); + m_frontend.resumed(); } void V8DebuggerAgentImpl::breakProgram(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data) diff --git a/deps/v8_inspector/platform/v8_inspector/V8DebuggerAgentImpl.h b/deps/v8_inspector/platform/v8_inspector/V8DebuggerAgentImpl.h index a2651970b73ab0..ab04bb50a43a11 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8DebuggerAgentImpl.h +++ b/deps/v8_inspector/platform/v8_inspector/V8DebuggerAgentImpl.h @@ -6,11 +6,9 @@ #define V8DebuggerAgentImpl_h #include "platform/inspector_protocol/Collections.h" -#include "platform/inspector_protocol/Dispatcher.h" -#include "platform/inspector_protocol/Frontend.h" #include "platform/inspector_protocol/String16.h" #include "platform/v8_inspector/V8DebuggerImpl.h" -#include "platform/v8_inspector/public/V8DebuggerAgent.h" +#include "platform/v8_inspector/protocol/Debugger.h" namespace blink { @@ -26,7 +24,7 @@ class DictionaryValue; using protocol::Maybe; -class V8DebuggerAgentImpl : public V8DebuggerAgent { +class V8DebuggerAgentImpl : public protocol::Debugger::Backend { PROTOCOL_DISALLOW_COPY(V8DebuggerAgentImpl); public: enum SkipPauseRequest { @@ -43,17 +41,13 @@ class V8DebuggerAgentImpl : public V8DebuggerAgent { MonitorCommandBreakpointSource }; - explicit V8DebuggerAgentImpl(V8InspectorSessionImpl*); + V8DebuggerAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*, protocol::DictionaryValue* state); ~V8DebuggerAgentImpl() override; - - void setInspectorState(protocol::DictionaryValue*) override; - void setFrontend(protocol::Frontend::Debugger* frontend) override { m_frontend = frontend; } - void clearFrontend() override; - void restore() override; - void disable(ErrorString*) override; + void restore(); // Part of the protocol. void enable(ErrorString*) override; + void disable(ErrorString*) override; void setBreakpointsActive(ErrorString*, bool active) override; void setSkipAllPauses(ErrorString*, bool skipped) override; void setBreakpointByUrl(ErrorString*, @@ -212,7 +206,7 @@ class V8DebuggerAgentImpl : public V8DebuggerAgent { V8InspectorSessionImpl* m_session; bool m_enabled; protocol::DictionaryValue* m_state; - protocol::Frontend::Debugger* m_frontend; + protocol::Debugger::Frontend m_frontend; v8::Isolate* m_isolate; v8::Global<v8::Context> m_pausedContext; JavaScriptCallFrames m_pausedCallFrames; @@ -237,6 +231,7 @@ class V8DebuggerAgentImpl : public V8DebuggerAgent { AsyncTaskToStackTrace m_asyncTaskStacks; protocol::HashSet<void*> m_recurringTasks; int m_maxAsyncCallStackDepth; + protocol::Vector<void*> m_currentTasks; protocol::Vector<std::unique_ptr<V8StackTraceImpl>> m_currentStacks; std::unique_ptr<V8Regex> m_blackboxPattern; protocol::HashMap<String16, protocol::Vector<std::pair<int, int>>> m_blackboxedPositions; diff --git a/deps/v8_inspector/platform/v8_inspector/V8DebuggerImpl.cpp b/deps/v8_inspector/platform/v8_inspector/V8DebuggerImpl.cpp index 6cb4371ede20ce..387bc4b86693a1 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8DebuggerImpl.cpp +++ b/deps/v8_inspector/platform/v8_inspector/V8DebuggerImpl.cpp @@ -158,8 +158,8 @@ V8DebuggerAgentImpl* V8DebuggerImpl::findEnabledDebuggerAgent(int contextGroupId if (!contextGroupId) return nullptr; V8InspectorSessionImpl* session = m_sessions.get(contextGroupId); - if (session && session->debuggerAgentImpl()->enabled()) - return session->debuggerAgentImpl(); + if (session && session->debuggerAgent()->enabled()) + return session->debuggerAgent(); return nullptr; } @@ -457,7 +457,7 @@ static V8DebuggerImpl* toV8DebuggerImpl(v8::Local<v8::Value> data) void V8DebuggerImpl::breakProgramCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { - DCHECK(2 == info.Length()); + DCHECK_EQ(info.Length(), 2); V8DebuggerImpl* thisPtr = toV8DebuggerImpl(info.Data()); v8::Local<v8::Context> pausedContext = thisPtr->m_isolate->GetCurrentContext(); v8::Local<v8::Value> exception; @@ -572,12 +572,7 @@ void V8DebuggerImpl::handleV8AsyncTaskEvent(V8DebuggerAgentImpl* agent, v8::Loca String16 type = toProtocolStringWithTypeCheck(callInternalGetterFunction(eventData, "type")); String16 name = toProtocolStringWithTypeCheck(callInternalGetterFunction(eventData, "name")); int id = callInternalGetterFunction(eventData, "id")->ToInteger(m_isolate)->Value(); - - m_pausedContext = context; - m_executionState = executionState; agent->didReceiveV8AsyncTaskEvent(context, type, name, id); - m_pausedContext.Clear(); - m_executionState.Clear(); } V8DebuggerParsedScript V8DebuggerImpl::createParsedScript(v8::Local<v8::Object> object, bool success) @@ -730,16 +725,16 @@ v8::Local<v8::Script> V8DebuggerImpl::compileInternalScript(v8::Local<v8::Contex return script; } -std::unique_ptr<V8StackTrace> V8DebuggerImpl::createStackTrace(v8::Local<v8::StackTrace> stackTrace, size_t maxStackSize) +std::unique_ptr<V8StackTrace> V8DebuggerImpl::createStackTrace(v8::Local<v8::StackTrace> stackTrace) { V8DebuggerAgentImpl* agent = findEnabledDebuggerAgent(m_isolate->GetCurrentContext()); - return V8StackTraceImpl::create(agent, stackTrace, maxStackSize); + return V8StackTraceImpl::create(agent, stackTrace, V8StackTrace::maxCallStackSizeToCapture); } -std::unique_ptr<V8InspectorSession> V8DebuggerImpl::connect(int contextGroupId) +std::unique_ptr<V8InspectorSession> V8DebuggerImpl::connect(int contextGroupId, protocol::FrontendChannel* channel, V8InspectorSessionClient* client, const String16* state) { DCHECK(!m_sessions.contains(contextGroupId)); - std::unique_ptr<V8InspectorSessionImpl> session = V8InspectorSessionImpl::create(this, contextGroupId); + std::unique_ptr<V8InspectorSessionImpl> session = V8InspectorSessionImpl::create(this, contextGroupId, channel, client, state); m_sessions.set(contextGroupId, session.get()); return std::move(session); } @@ -769,7 +764,7 @@ void V8DebuggerImpl::contextCreated(const V8ContextInfo& info) m_contexts.get(info.contextGroupId)->set(contextId, std::move(contextOwner)); if (V8InspectorSessionImpl* session = m_sessions.get(info.contextGroupId)) - session->runtimeAgentImpl()->reportExecutionContextCreated(inspectedContext); + session->runtimeAgent()->reportExecutionContextCreated(inspectedContext); } void V8DebuggerImpl::contextDestroyed(v8::Local<v8::Context> context) @@ -781,7 +776,7 @@ void V8DebuggerImpl::contextDestroyed(v8::Local<v8::Context> context) InspectedContext* inspectedContext = m_contexts.get(contextGroupId)->get(contextId); if (V8InspectorSessionImpl* session = m_sessions.get(contextGroupId)) - session->runtimeAgentImpl()->reportExecutionContextDestroyed(inspectedContext); + session->runtimeAgent()->reportExecutionContextDestroyed(inspectedContext); m_contexts.get(contextGroupId)->remove(contextId); if (m_contexts.get(contextGroupId)->isEmpty()) diff --git a/deps/v8_inspector/platform/v8_inspector/V8DebuggerImpl.h b/deps/v8_inspector/platform/v8_inspector/V8DebuggerImpl.h index 48f335004a093c..b62a2a003a5661 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8DebuggerImpl.h +++ b/deps/v8_inspector/platform/v8_inspector/V8DebuggerImpl.h @@ -31,11 +31,12 @@ #ifndef V8DebuggerImpl_h #define V8DebuggerImpl_h -#include "platform/inspector_protocol/TypeBuilder.h" +#include "platform/inspector_protocol/Maybe.h" +#include "platform/inspector_protocol/Platform.h" #include "platform/v8_inspector/JavaScriptCallFrame.h" #include "platform/v8_inspector/V8DebuggerScript.h" +#include "platform/v8_inspector/protocol/Debugger.h" #include "platform/v8_inspector/public/V8Debugger.h" -#include "wtf/PtrUtil.h" #include <v8-debug.h> #include <v8.h> @@ -106,7 +107,7 @@ class V8DebuggerImpl : public V8Debugger { v8::Local<v8::Context> regexContext(); // V8Debugger implementation - std::unique_ptr<V8InspectorSession> connect(int contextGroupId) override; + std::unique_ptr<V8InspectorSession> connect(int contextGroupId, protocol::FrontendChannel*, V8InspectorSessionClient*, const String16* state) override; void contextCreated(const V8ContextInfo&) override; void contextDestroyed(v8::Local<v8::Context>) override; void resetContextGroup(int contextGroupId) override; @@ -114,7 +115,7 @@ class V8DebuggerImpl : public V8Debugger { void didExecuteScript(v8::Local<v8::Context>) override; void idleStarted() override; void idleFinished() override; - std::unique_ptr<V8StackTrace> createStackTrace(v8::Local<v8::StackTrace>, size_t maxStackSize) override; + std::unique_ptr<V8StackTrace> createStackTrace(v8::Local<v8::StackTrace>) override; std::unique_ptr<V8StackTrace> captureStackTrace(size_t maxStackSize) override; using ContextByIdMap = protocol::HashMap<int, std::unique_ptr<InspectedContext>>; diff --git a/deps/v8_inspector/platform/v8_inspector/V8FunctionCall.cpp b/deps/v8_inspector/platform/v8_inspector/V8FunctionCall.cpp index 872758129d74d9..83e24d79be63fc 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8FunctionCall.cpp +++ b/deps/v8_inspector/platform/v8_inspector/V8FunctionCall.cpp @@ -30,11 +30,11 @@ #include "platform/v8_inspector/V8FunctionCall.h" +#include "platform/inspector_protocol/Platform.h" #include "platform/v8_inspector/V8Compat.h" #include "platform/v8_inspector/V8DebuggerImpl.h" #include "platform/v8_inspector/V8StringUtil.h" #include "platform/v8_inspector/public/V8DebuggerClient.h" -#include "wtf/PtrUtil.h" #include <v8.h> diff --git a/deps/v8_inspector/platform/v8_inspector/V8HeapProfilerAgentImpl.cpp b/deps/v8_inspector/platform/v8_inspector/V8HeapProfilerAgentImpl.cpp index b6f8179cc078e5..3281d1ba1dab48 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8HeapProfilerAgentImpl.cpp +++ b/deps/v8_inspector/platform/v8_inspector/V8HeapProfilerAgentImpl.cpp @@ -28,7 +28,7 @@ static const char samplingHeapProfilerInterval[] = "samplingHeapProfilerInterval class HeapSnapshotProgress final : public v8::ActivityControl { public: - HeapSnapshotProgress(protocol::Frontend::HeapProfiler* frontend) + HeapSnapshotProgress(protocol::HeapProfiler::Frontend* frontend) : m_frontend(frontend) { } ControlOption ReportProgressValue(int done, int total) override { @@ -40,7 +40,7 @@ class HeapSnapshotProgress final : public v8::ActivityControl { return kContinue; } private: - protocol::Frontend::HeapProfiler* m_frontend; + protocol::HeapProfiler::Frontend* m_frontend; }; class GlobalObjectNameResolver final : public v8::HeapProfiler::ObjectNameResolver { @@ -81,7 +81,7 @@ class GlobalObjectNameResolver final : public v8::HeapProfiler::ObjectNameResolv class HeapSnapshotOutputStream final : public v8::OutputStream { public: - HeapSnapshotOutputStream(protocol::Frontend::HeapProfiler* frontend) + HeapSnapshotOutputStream(protocol::HeapProfiler::Frontend* frontend) : m_frontend(frontend) { } void EndOfStream() override { } int GetChunkSize() override { return 102400; } @@ -92,7 +92,7 @@ class HeapSnapshotOutputStream final : public v8::OutputStream { return kContinue; } private: - protocol::Frontend::HeapProfiler* m_frontend; + protocol::HeapProfiler::Frontend* m_frontend; }; v8::Local<v8::Object> objectByHeapObjectId(v8::Isolate* isolate, int id) @@ -117,7 +117,7 @@ class InspectableHeapObject final : public V8InspectorSession::Inspectable { class HeapStatsStream final : public v8::OutputStream { public: - HeapStatsStream(protocol::Frontend::HeapProfiler* frontend) + HeapStatsStream(protocol::HeapProfiler::Frontend* frontend) : m_frontend(frontend) { } @@ -132,7 +132,7 @@ class HeapStatsStream final : public v8::OutputStream { WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* updateData, int count) override { - DCHECK(count > 0); + DCHECK_GT(count, 0); std::unique_ptr<protocol::Array<int>> statsDiff = protocol::Array<int>::create(); for (int i = 0; i < count; ++i) { statsDiff->addItem(updateData[i].index); @@ -144,14 +144,16 @@ class HeapStatsStream final : public v8::OutputStream { } private: - protocol::Frontend::HeapProfiler* m_frontend; + protocol::HeapProfiler::Frontend* m_frontend; }; } // namespace -V8HeapProfilerAgentImpl::V8HeapProfilerAgentImpl(V8InspectorSessionImpl* session) +V8HeapProfilerAgentImpl::V8HeapProfilerAgentImpl(V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel, protocol::DictionaryValue* state) : m_session(session) , m_isolate(session->debugger()->isolate()) + , m_frontend(frontendChannel) + , m_state(state) , m_hasTimer(false) { } @@ -160,18 +162,10 @@ V8HeapProfilerAgentImpl::~V8HeapProfilerAgentImpl() { } -void V8HeapProfilerAgentImpl::clearFrontend() -{ - ErrorString error; - disable(&error); - DCHECK(m_frontend); - m_frontend = nullptr; -} - void V8HeapProfilerAgentImpl::restore() { if (m_state->booleanProperty(HeapProfilerAgentState::heapProfilerEnabled, false)) - m_frontend->resetProfiles(); + m_frontend.resetProfiles(); if (m_state->booleanProperty(HeapProfilerAgentState::heapObjectsTrackingEnabled, false)) startTrackingHeapObjectsInternal(m_state->booleanProperty(HeapProfilerAgentState::allocationTrackingEnabled, false)); #if V8_MAJOR_VERSION >= 5 @@ -232,7 +226,7 @@ void V8HeapProfilerAgentImpl::takeHeapSnapshot(ErrorString* errorString, const p } std::unique_ptr<HeapSnapshotProgress> progress; if (reportProgress.fromMaybe(false)) - progress = wrapUnique(new HeapSnapshotProgress(m_frontend)); + progress = wrapUnique(new HeapSnapshotProgress(&m_frontend)); GlobalObjectNameResolver resolver(m_session); const v8::HeapSnapshot* snapshot = profiler->TakeHeapSnapshot(progress.get(), &resolver); @@ -240,7 +234,7 @@ void V8HeapProfilerAgentImpl::takeHeapSnapshot(ErrorString* errorString, const p *errorString = "Failed to take heap snapshot"; return; } - HeapSnapshotOutputStream stream(m_frontend); + HeapSnapshotOutputStream stream(&m_frontend); snapshot->Serialize(&stream); const_cast<v8::HeapSnapshot*>(snapshot)->Delete(); } @@ -308,11 +302,9 @@ void V8HeapProfilerAgentImpl::getHeapObjectId(ErrorString* errorString, const St void V8HeapProfilerAgentImpl::requestHeapStatsUpdate() { - if (!m_frontend) - return; - HeapStatsStream stream(m_frontend); + HeapStatsStream stream(&m_frontend); v8::SnapshotObjectId lastSeenObjectId = m_isolate->GetHeapProfiler()->GetHeapStats(&stream); - m_frontend->lastSeenObjectId(lastSeenObjectId, m_session->debugger()->client()->currentTimeMS()); + m_frontend.lastSeenObjectId(lastSeenObjectId, m_session->debugger()->client()->currentTimeMS()); } // static diff --git a/deps/v8_inspector/platform/v8_inspector/V8HeapProfilerAgentImpl.h b/deps/v8_inspector/platform/v8_inspector/V8HeapProfilerAgentImpl.h index 082e533d603531..bc201afab9a604 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8HeapProfilerAgentImpl.h +++ b/deps/v8_inspector/platform/v8_inspector/V8HeapProfilerAgentImpl.h @@ -6,7 +6,10 @@ #define V8HeapProfilerAgentImpl_h #include "platform/inspector_protocol/Allocator.h" -#include "platform/v8_inspector/public/V8HeapProfilerAgent.h" +#include "platform/inspector_protocol/String16.h" +#include "platform/v8_inspector/protocol/HeapProfiler.h" + +#include <v8.h> namespace blink { @@ -14,16 +17,12 @@ class V8InspectorSessionImpl; using protocol::Maybe; -class V8HeapProfilerAgentImpl : public V8HeapProfilerAgent { +class V8HeapProfilerAgentImpl : public protocol::HeapProfiler::Backend { PROTOCOL_DISALLOW_COPY(V8HeapProfilerAgentImpl); public: - explicit V8HeapProfilerAgentImpl(V8InspectorSessionImpl*); + V8HeapProfilerAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*, protocol::DictionaryValue* state); ~V8HeapProfilerAgentImpl() override; - - void setInspectorState(protocol::DictionaryValue* state) override { m_state = state; } - void setFrontend(protocol::Frontend::HeapProfiler* frontend) override { m_frontend = frontend; } - void clearFrontend() override; - void restore() override; + void restore(); void collectGarbage(ErrorString*) override; @@ -50,7 +49,7 @@ class V8HeapProfilerAgentImpl : public V8HeapProfilerAgent { V8InspectorSessionImpl* m_session; v8::Isolate* m_isolate; - protocol::Frontend::HeapProfiler* m_frontend; + protocol::HeapProfiler::Frontend m_frontend; protocol::DictionaryValue* m_state; bool m_hasTimer; }; diff --git a/deps/v8_inspector/platform/v8_inspector/V8InjectedScriptHost.cpp b/deps/v8_inspector/platform/v8_inspector/V8InjectedScriptHost.cpp index e25431f3cbc991..e7f9bed382349b 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8InjectedScriptHost.cpp +++ b/deps/v8_inspector/platform/v8_inspector/V8InjectedScriptHost.cpp @@ -10,7 +10,6 @@ #include "platform/v8_inspector/V8DebuggerImpl.h" #include "platform/v8_inspector/V8StringUtil.h" #include "platform/v8_inspector/public/V8DebuggerClient.h" -#include "platform/v8_inspector/public/V8EventListenerInfo.h" namespace blink { @@ -49,9 +48,7 @@ v8::Local<v8::Object> V8InjectedScriptHost::create(v8::Local<v8::Context> contex setFunctionProperty(context, injectedScriptHost, "subtype", V8InjectedScriptHost::subtypeCallback, debuggerExternal); setFunctionProperty(context, injectedScriptHost, "collectionEntries", V8InjectedScriptHost::collectionEntriesCallback, debuggerExternal); setFunctionProperty(context, injectedScriptHost, "getInternalProperties", V8InjectedScriptHost::getInternalPropertiesCallback, debuggerExternal); - setFunctionProperty(context, injectedScriptHost, "getEventListeners", V8InjectedScriptHost::getEventListenersCallback, debuggerExternal); setFunctionProperty(context, injectedScriptHost, "suppressWarningsAndCallFunction", V8InjectedScriptHost::suppressWarningsAndCallFunctionCallback, debuggerExternal); - setFunctionProperty(context, injectedScriptHost, "setNonEnumProperty", V8InjectedScriptHost::setNonEnumPropertyCallback, debuggerExternal); setFunctionProperty(context, injectedScriptHost, "bind", V8InjectedScriptHost::bindCallback, debuggerExternal); setFunctionProperty(context, injectedScriptHost, "proxyTargetValue", V8InjectedScriptHost::proxyTargetValueCallback, debuggerExternal); setFunctionProperty(context, injectedScriptHost, "prototype", V8InjectedScriptHost::prototypeCallback, debuggerExternal); @@ -69,9 +66,13 @@ void V8InjectedScriptHost::internalConstructorNameCallback(const v8::FunctionCal void V8InjectedScriptHost::formatAccessorsAsProperties(const v8::FunctionCallbackInfo<v8::Value>& info) { - if (info.Length() < 1) + DCHECK_EQ(info.Length(), 2); + info.GetReturnValue().Set(false); + if (!info[1]->IsFunction()) + return; + // Check that function is user-defined. + if (info[1].As<v8::Function>()->ScriptId() != v8::UnboundScript::kNoScriptId) return; - info.GetReturnValue().Set(unwrapDebugger(info)->client()->formatAccessorsAsProperties(info[0])); } @@ -153,48 +154,6 @@ void V8InjectedScriptHost::getInternalPropertiesCallback(const v8::FunctionCallb info.GetReturnValue().Set(properties); } -static v8::Local<v8::Array> wrapListenerFunctions(v8::Isolate* isolate, const V8EventListenerInfoList& listeners, const String16& type) -{ - v8::Local<v8::Array> result = v8::Array::New(isolate); - size_t handlersCount = listeners.size(); - for (size_t i = 0, outputIndex = 0; i < handlersCount; ++i) { - if (listeners[i].eventType != type) - continue; - v8::Local<v8::Object> function = listeners[i].handler; - v8::Local<v8::Object> listenerEntry = v8::Object::New(isolate); - listenerEntry->Set(toV8StringInternalized(isolate, "listener"), function); - listenerEntry->Set(toV8StringInternalized(isolate, "useCapture"), v8::Boolean::New(isolate, listeners[i].useCapture)); - listenerEntry->Set(toV8StringInternalized(isolate, "passive"), v8::Boolean::New(isolate, listeners[i].passive)); - result->Set(v8::Number::New(isolate, outputIndex++), listenerEntry); - } - return result; -} - -void V8InjectedScriptHost::getEventListenersCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - if (info.Length() < 1) - return; - - V8DebuggerClient* client = unwrapDebugger(info)->client(); - V8EventListenerInfoList listenerInfo; - // eventListeners call can produce message on ErrorEvent during lazy event listener compilation. - client->muteWarningsAndDeprecations(); - client->eventListeners(info[0], listenerInfo); - client->unmuteWarningsAndDeprecations(); - - v8::Local<v8::Object> result = v8::Object::New(info.GetIsolate()); - protocol::HashSet<String16> types; - for (auto& info : listenerInfo) - types.add(info.eventType); - for (const auto& it : types) { - v8::Local<v8::Array> listeners = wrapListenerFunctions(info.GetIsolate(), listenerInfo, it.first); - if (!listeners->Length()) - continue; - result->Set(toV8String(info.GetIsolate(), it.first), listeners); - } - info.GetReturnValue().Set(result); -} - void V8InjectedScriptHost::suppressWarningsAndCallFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { if (info.Length() < 2 || info.Length() > 3 || !info[0]->IsFunction()) { @@ -235,17 +194,6 @@ void V8InjectedScriptHost::suppressWarningsAndCallFunctionCallback(const v8::Fun client->unmuteWarningsAndDeprecations(); } -void V8InjectedScriptHost::setNonEnumPropertyCallback(const v8::FunctionCallbackInfo<v8::Value>& info) -{ - if (info.Length() < 3 || !info[0]->IsObject() || !info[1]->IsString()) - return; - - v8::Local<v8::Object> object = info[0].As<v8::Object>(); - v8::Maybe<bool> success = object->DefineOwnProperty(info.GetIsolate()->GetCurrentContext(), info[1].As<v8::String>(), info[2], v8::DontEnum); - USE(success); - DCHECK(!success.IsNothing()); -} - void V8InjectedScriptHost::bindCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { if (info.Length() < 2 || !info[1]->IsString()) @@ -278,9 +226,4 @@ void V8InjectedScriptHost::prototypeCallback(const v8::FunctionCallbackInfo<v8:: info.GetReturnValue().Set(info[0].As<v8::Object>()->GetPrototype()); } -v8::Local<v8::Private> V8Debugger::scopeExtensionPrivate(v8::Isolate* isolate) -{ - return v8::Private::ForApi(isolate, toV8StringInternalized(isolate, "V8Debugger#scopeExtension")); -} - } // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/V8InjectedScriptHost.h b/deps/v8_inspector/platform/v8_inspector/V8InjectedScriptHost.h index a05dd29cee3c50..d21bbdd33e3d28 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8InjectedScriptHost.h +++ b/deps/v8_inspector/platform/v8_inspector/V8InjectedScriptHost.h @@ -27,13 +27,9 @@ class V8InjectedScriptHost { static void formatAccessorsAsProperties(const v8::FunctionCallbackInfo<v8::Value>&); static void isTypedArrayCallback(const v8::FunctionCallbackInfo<v8::Value>&); static void subtypeCallback(const v8::FunctionCallbackInfo<v8::Value>&); - static void generatorObjectDetailsCallback(const v8::FunctionCallbackInfo<v8::Value>&); static void collectionEntriesCallback(const v8::FunctionCallbackInfo<v8::Value>&); static void getInternalPropertiesCallback(const v8::FunctionCallbackInfo<v8::Value>&); - static void getEventListenersCallback(const v8::FunctionCallbackInfo<v8::Value>&); static void suppressWarningsAndCallFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>&); - static void setNonEnumPropertyCallback(const v8::FunctionCallbackInfo<v8::Value>&); - static void setFunctionVariableValueCallback(const v8::FunctionCallbackInfo<v8::Value>&); static void bindCallback(const v8::FunctionCallbackInfo<v8::Value>&); static void proxyTargetValueCallback(const v8::FunctionCallbackInfo<v8::Value>&); static void prototypeCallback(const v8::FunctionCallbackInfo<v8::Value>&); diff --git a/deps/v8_inspector/platform/v8_inspector/V8InspectorSessionImpl.cpp b/deps/v8_inspector/platform/v8_inspector/V8InspectorSessionImpl.cpp index 6453ce649ec3b3..1c584e713e07b3 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8InspectorSessionImpl.cpp +++ b/deps/v8_inspector/platform/v8_inspector/V8InspectorSessionImpl.cpp @@ -4,6 +4,7 @@ #include "platform/v8_inspector/V8InspectorSessionImpl.h" +#include "platform/inspector_protocol/Parser.h" #include "platform/v8_inspector/InjectedScript.h" #include "platform/v8_inspector/InspectedContext.h" #include "platform/v8_inspector/RemoteObjectId.h" @@ -19,53 +20,81 @@ namespace blink { const char V8InspectorSession::backtraceObjectGroup[] = "backtrace"; -std::unique_ptr<V8InspectorSessionImpl> V8InspectorSessionImpl::create(V8DebuggerImpl* debugger, int contextGroupId) +// static +bool V8InspectorSession::isV8ProtocolMethod(const String16& method) { - return wrapUnique(new V8InspectorSessionImpl(debugger, contextGroupId)); + return method.startWith("Debugger.") || method.startWith("HeapProfiler.") || method.startWith("Profiler.") || method.startWith("Runtime."); } -V8InspectorSessionImpl::V8InspectorSessionImpl(V8DebuggerImpl* debugger, int contextGroupId) +std::unique_ptr<V8InspectorSessionImpl> V8InspectorSessionImpl::create(V8DebuggerImpl* debugger, int contextGroupId, protocol::FrontendChannel* channel, V8InspectorSessionClient* client, const String16* state) +{ + return wrapUnique(new V8InspectorSessionImpl(debugger, contextGroupId, channel, client, state)); +} + +V8InspectorSessionImpl::V8InspectorSessionImpl(V8DebuggerImpl* debugger, int contextGroupId, protocol::FrontendChannel* channel, V8InspectorSessionClient* client, const String16* savedState) : m_contextGroupId(contextGroupId) , m_debugger(debugger) - , m_client(nullptr) + , m_client(client) , m_customObjectFormatterEnabled(false) , m_instrumentationCounter(0) - , m_runtimeAgent(wrapUnique(new V8RuntimeAgentImpl(this))) - , m_debuggerAgent(wrapUnique(new V8DebuggerAgentImpl(this))) - , m_heapProfilerAgent(wrapUnique(new V8HeapProfilerAgentImpl(this))) - , m_profilerAgent(wrapUnique(new V8ProfilerAgentImpl(this))) + , m_dispatcher(channel) + , m_state(nullptr) + , m_runtimeAgent(nullptr) + , m_debuggerAgent(nullptr) + , m_heapProfilerAgent(nullptr) + , m_profilerAgent(nullptr) { -} + if (savedState) { + std::unique_ptr<protocol::Value> state = protocol::parseJSON(*savedState); + if (state) + m_state = protocol::DictionaryValue::cast(std::move(state)); + if (!m_state) + m_state = protocol::DictionaryValue::create(); + } else { + m_state = protocol::DictionaryValue::create(); + } -V8InspectorSessionImpl::~V8InspectorSessionImpl() -{ - discardInjectedScripts(); - m_debugger->disconnect(this); -} + m_runtimeAgent = wrapUnique(new V8RuntimeAgentImpl(this, channel, agentState(protocol::Runtime::Metainfo::domainName))); + protocol::Runtime::Dispatcher::wire(&m_dispatcher, m_runtimeAgent.get()); -V8DebuggerAgent* V8InspectorSessionImpl::debuggerAgent() -{ - return m_debuggerAgent.get(); -} + m_debuggerAgent = wrapUnique(new V8DebuggerAgentImpl(this, channel, agentState(protocol::Debugger::Metainfo::domainName))); + protocol::Debugger::Dispatcher::wire(&m_dispatcher, m_debuggerAgent.get()); -V8HeapProfilerAgent* V8InspectorSessionImpl::heapProfilerAgent() -{ - return m_heapProfilerAgent.get(); -} + m_profilerAgent = wrapUnique(new V8ProfilerAgentImpl(this, channel, agentState(protocol::Profiler::Metainfo::domainName))); + protocol::Profiler::Dispatcher::wire(&m_dispatcher, m_profilerAgent.get()); -V8ProfilerAgent* V8InspectorSessionImpl::profilerAgent() -{ - return m_profilerAgent.get(); + m_heapProfilerAgent = wrapUnique(new V8HeapProfilerAgentImpl(this, channel, agentState(protocol::HeapProfiler::Metainfo::domainName))); + protocol::HeapProfiler::Dispatcher::wire(&m_dispatcher, m_heapProfilerAgent.get()); + + if (savedState) { + m_runtimeAgent->restore(); + m_debuggerAgent->restore(); + m_heapProfilerAgent->restore(); + m_profilerAgent->restore(); + } } -V8RuntimeAgent* V8InspectorSessionImpl::runtimeAgent() +V8InspectorSessionImpl::~V8InspectorSessionImpl() { - return m_runtimeAgent.get(); + ErrorString errorString; + m_profilerAgent->disable(&errorString); + m_heapProfilerAgent->disable(&errorString); + m_debuggerAgent->disable(&errorString); + m_runtimeAgent->disable(&errorString); + + discardInjectedScripts(); + m_debugger->disconnect(this); } -void V8InspectorSessionImpl::setClient(V8InspectorSessionClient* client) +protocol::DictionaryValue* V8InspectorSessionImpl::agentState(const String16& name) { - m_client = client; + protocol::DictionaryValue* state = m_state->getObject(name); + if (!state) { + std::unique_ptr<protocol::DictionaryValue> newState = protocol::DictionaryValue::create(); + state = newState.get(); + m_state->setObject(name, std::move(newState)); + } + return state; } void V8InspectorSessionImpl::reset() @@ -203,14 +232,24 @@ void V8InspectorSessionImpl::reportAllContexts(V8RuntimeAgentImpl* agent) void V8InspectorSessionImpl::changeInstrumentationCounter(int delta) { - DCHECK(m_instrumentationCounter + delta >= 0); - if (!m_instrumentationCounter && m_client) + DCHECK_GE(m_instrumentationCounter + delta, 0); + if (!m_instrumentationCounter) m_client->startInstrumenting(); m_instrumentationCounter += delta; - if (!m_instrumentationCounter && m_client) + if (!m_instrumentationCounter) m_client->stopInstrumenting(); } +void V8InspectorSessionImpl::dispatchProtocolMessage(const String16& message) +{ + m_dispatcher.dispatch(message); +} + +String16 V8InspectorSessionImpl::stateJSON() +{ + return m_state->toJSONString(); +} + void V8InspectorSessionImpl::addInspectedObject(std::unique_ptr<V8InspectorSession::Inspectable> inspectable) { m_inspectedObjects.prepend(std::move(inspectable)); @@ -251,6 +290,18 @@ void V8InspectorSessionImpl::setSkipAllPauses(bool skip) m_debuggerAgent->setSkipAllPauses(&errorString, skip); } +void V8InspectorSessionImpl::resume() +{ + ErrorString errorString; + m_debuggerAgent->resume(&errorString); +} + +void V8InspectorSessionImpl::stepOver() +{ + ErrorString errorString; + m_debuggerAgent->stepOver(&errorString); +} + void V8InspectorSessionImpl::asyncTaskScheduled(const String16& taskName, void* task, bool recurring) { m_debuggerAgent->asyncTaskScheduled(taskName, task, recurring); diff --git a/deps/v8_inspector/platform/v8_inspector/V8InspectorSessionImpl.h b/deps/v8_inspector/platform/v8_inspector/V8InspectorSessionImpl.h index ecc108760567a2..bf90eb241dafaf 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8InspectorSessionImpl.h +++ b/deps/v8_inspector/platform/v8_inspector/V8InspectorSessionImpl.h @@ -7,12 +7,12 @@ #include "platform/inspector_protocol/Allocator.h" #include "platform/inspector_protocol/Collections.h" +#include "platform/inspector_protocol/DispatcherBase.h" +#include "platform/inspector_protocol/Platform.h" #include "platform/inspector_protocol/String16.h" -#include "platform/inspector_protocol/TypeBuilder.h" +#include "platform/v8_inspector/protocol/Runtime.h" #include "platform/v8_inspector/public/V8InspectorSession.h" #include "platform/v8_inspector/public/V8InspectorSessionClient.h" -#include "platform/v8_inspector/public/V8RuntimeAgent.h" -#include "wtf/PtrUtil.h" #include <v8.h> @@ -29,14 +29,14 @@ class V8RuntimeAgentImpl; class V8InspectorSessionImpl : public V8InspectorSession { PROTOCOL_DISALLOW_COPY(V8InspectorSessionImpl); public: - static std::unique_ptr<V8InspectorSessionImpl> create(V8DebuggerImpl*, int contextGroupId); + static std::unique_ptr<V8InspectorSessionImpl> create(V8DebuggerImpl*, int contextGroupId, protocol::FrontendChannel*, V8InspectorSessionClient*, const String16* state); ~V8InspectorSessionImpl(); V8DebuggerImpl* debugger() const { return m_debugger; } V8InspectorSessionClient* client() const { return m_client; } - V8DebuggerAgentImpl* debuggerAgentImpl() { return m_debuggerAgent.get(); } - V8ProfilerAgentImpl* profilerAgentImpl() { return m_profilerAgent.get(); } - V8RuntimeAgentImpl* runtimeAgentImpl() { return m_runtimeAgent.get(); } + V8DebuggerAgentImpl* debuggerAgent() { return m_debuggerAgent.get(); } + V8ProfilerAgentImpl* profilerAgent() { return m_profilerAgent.get(); } + V8RuntimeAgentImpl* runtimeAgent() { return m_runtimeAgent.get(); } int contextGroupId() const { return m_contextGroupId; } InjectedScript* findInjectedScript(ErrorString*, int contextId); @@ -48,17 +48,16 @@ class V8InspectorSessionImpl : public V8InspectorSession { void changeInstrumentationCounter(int delta); // V8InspectorSession implementation. - void setClient(V8InspectorSessionClient*) override; + void dispatchProtocolMessage(const String16& message) override; + String16 stateJSON() override; void addInspectedObject(std::unique_ptr<V8InspectorSession::Inspectable>) override; - V8DebuggerAgent* debuggerAgent() override; - V8HeapProfilerAgent* heapProfilerAgent() override; - V8ProfilerAgent* profilerAgent() override; - V8RuntimeAgent* runtimeAgent() override; void schedulePauseOnNextStatement(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data) override; void cancelPauseOnNextStatement() override; void breakProgram(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data) override; void breakProgramOnException(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data) override; void setSkipAllPauses(bool) override; + void resume() override; + void stepOver() override; void asyncTaskScheduled(const String16& taskName, void* task, bool recurring) override; void asyncTaskCanceled(void* task) override; void asyncTaskStarted(void* task) override; @@ -73,7 +72,8 @@ class V8InspectorSessionImpl : public V8InspectorSession { static const unsigned kInspectedObjectBufferSize = 5; private: - V8InspectorSessionImpl(V8DebuggerImpl*, int contextGroupId); + V8InspectorSessionImpl(V8DebuggerImpl*, int contextGroupId, protocol::FrontendChannel*, V8InspectorSessionClient*, const String16* state); + protocol::DictionaryValue* agentState(const String16& name); int m_contextGroupId; V8DebuggerImpl* m_debugger; @@ -81,6 +81,9 @@ class V8InspectorSessionImpl : public V8InspectorSession { bool m_customObjectFormatterEnabled; int m_instrumentationCounter; + protocol::UberDispatcher m_dispatcher; + std::unique_ptr<protocol::DictionaryValue> m_state; + std::unique_ptr<V8RuntimeAgentImpl> m_runtimeAgent; std::unique_ptr<V8DebuggerAgentImpl> m_debuggerAgent; std::unique_ptr<V8HeapProfilerAgentImpl> m_heapProfilerAgent; diff --git a/deps/v8_inspector/platform/v8_inspector/V8ProfilerAgentImpl.cpp b/deps/v8_inspector/platform/v8_inspector/V8ProfilerAgentImpl.cpp index dd5c2e58259e53..92762f73dc8e9d 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8ProfilerAgentImpl.cpp +++ b/deps/v8_inspector/platform/v8_inspector/V8ProfilerAgentImpl.cpp @@ -121,11 +121,11 @@ class V8ProfilerAgentImpl::ProfileDescriptor { String16 m_title; }; -V8ProfilerAgentImpl::V8ProfilerAgentImpl(V8InspectorSessionImpl* session) +V8ProfilerAgentImpl::V8ProfilerAgentImpl(V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel, protocol::DictionaryValue* state) : m_session(session) , m_isolate(m_session->debugger()->isolate()) - , m_state(nullptr) - , m_frontend(nullptr) + , m_state(state) + , m_frontend(frontendChannel) , m_enabled(false) , m_recordingCPUProfile(false) { @@ -139,18 +139,16 @@ void V8ProfilerAgentImpl::consoleProfile(const String16& title) { if (!m_enabled) return; - DCHECK(m_frontend); String16 id = nextProfileId(); m_startedProfiles.append(ProfileDescriptor(id, title)); startProfiling(id); - m_frontend->consoleProfileStarted(id, currentDebugLocation(m_session->debugger()), title); + m_frontend.consoleProfileStarted(id, currentDebugLocation(m_session->debugger()), title); } void V8ProfilerAgentImpl::consoleProfileEnd(const String16& title) { if (!m_enabled) return; - DCHECK(m_frontend); String16 id; String16 resolvedTitle; // Take last started profile if no title was passed. @@ -176,7 +174,7 @@ void V8ProfilerAgentImpl::consoleProfileEnd(const String16& title) if (!profile) return; std::unique_ptr<protocol::Debugger::Location> location = currentDebugLocation(m_session->debugger()); - m_frontend->consoleProfileFinished(id, std::move(location), std::move(profile), resolvedTitle); + m_frontend.consoleProfileFinished(id, std::move(location), std::move(profile), resolvedTitle); } void V8ProfilerAgentImpl::enable(ErrorString*) @@ -211,14 +209,6 @@ void V8ProfilerAgentImpl::setSamplingInterval(ErrorString* error, int interval) m_isolate->GetCpuProfiler()->SetSamplingInterval(interval); } -void V8ProfilerAgentImpl::clearFrontend() -{ - ErrorString error; - disable(&error); - DCHECK(m_frontend); - m_frontend = nullptr; -} - void V8ProfilerAgentImpl::restore() { DCHECK(!m_enabled); diff --git a/deps/v8_inspector/platform/v8_inspector/V8ProfilerAgentImpl.h b/deps/v8_inspector/platform/v8_inspector/V8ProfilerAgentImpl.h index 76bbfd4306c064..7fa5f49b4684e1 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8ProfilerAgentImpl.h +++ b/deps/v8_inspector/platform/v8_inspector/V8ProfilerAgentImpl.h @@ -6,9 +6,8 @@ #define V8ProfilerAgentImpl_h #include "platform/inspector_protocol/Allocator.h" -#include "platform/inspector_protocol/Frontend.h" #include "platform/inspector_protocol/String16.h" -#include "platform/v8_inspector/public/V8ProfilerAgent.h" +#include "platform/v8_inspector/protocol/Profiler.h" namespace v8 { class Isolate; @@ -18,18 +17,14 @@ namespace blink { class V8InspectorSessionImpl; -class V8ProfilerAgentImpl : public V8ProfilerAgent { +class V8ProfilerAgentImpl : public protocol::Profiler::Backend { PROTOCOL_DISALLOW_COPY(V8ProfilerAgentImpl); public: - explicit V8ProfilerAgentImpl(V8InspectorSessionImpl*); + V8ProfilerAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*, protocol::DictionaryValue* state); ~V8ProfilerAgentImpl() override; bool enabled() const { return m_enabled; } - - void setInspectorState(protocol::DictionaryValue* state) override { m_state = state; } - void setFrontend(protocol::Frontend::Profiler* frontend) override { m_frontend = frontend; } - void clearFrontend() override; - void restore() override; + void restore(); void enable(ErrorString*) override; void disable(ErrorString*) override; @@ -51,7 +46,7 @@ class V8ProfilerAgentImpl : public V8ProfilerAgent { V8InspectorSessionImpl* m_session; v8::Isolate* m_isolate; protocol::DictionaryValue* m_state; - protocol::Frontend::Profiler* m_frontend; + protocol::Profiler::Frontend m_frontend; bool m_enabled; bool m_recordingCPUProfile; class ProfileDescriptor; diff --git a/deps/v8_inspector/platform/v8_inspector/V8Regex.cpp b/deps/v8_inspector/platform/v8_inspector/V8Regex.cpp index d29c916aa2c842..304b715f797b51 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8Regex.cpp +++ b/deps/v8_inspector/platform/v8_inspector/V8Regex.cpp @@ -4,7 +4,6 @@ #include "platform/v8_inspector/V8Regex.h" -#include "platform/inspector_protocol/Collections.h" #include "platform/v8_inspector/V8Compat.h" #include "platform/v8_inspector/V8DebuggerImpl.h" #include "platform/v8_inspector/V8StringUtil.h" diff --git a/deps/v8_inspector/platform/v8_inspector/V8RuntimeAgentImpl.cpp b/deps/v8_inspector/platform/v8_inspector/V8RuntimeAgentImpl.cpp index 6a2611f87e3a65..dc45b11875ec86 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8RuntimeAgentImpl.cpp +++ b/deps/v8_inspector/platform/v8_inspector/V8RuntimeAgentImpl.cpp @@ -56,10 +56,10 @@ static bool hasInternalError(ErrorString* errorString, bool hasError) return hasError; } -V8RuntimeAgentImpl::V8RuntimeAgentImpl(V8InspectorSessionImpl* session) +V8RuntimeAgentImpl::V8RuntimeAgentImpl(V8InspectorSessionImpl* session, protocol::FrontendChannel* FrontendChannel, protocol::DictionaryValue* state) : m_session(session) - , m_state(nullptr) - , m_frontend(nullptr) + , m_state(state) + , m_frontend(FrontendChannel) , m_debugger(session->debugger()) , m_enabled(false) { @@ -343,29 +343,11 @@ void V8RuntimeAgentImpl::runScript(ErrorString* errorString, scope.injectedScript()->wrapEvaluateResult(errorString, maybeResultValue, scope.tryCatch(), objectGroup.fromMaybe(""), false, false, result, nullptr, exceptionDetails); } -void V8RuntimeAgentImpl::setInspectorState(protocol::DictionaryValue* state) -{ - m_state = state; -} - -void V8RuntimeAgentImpl::setFrontend(protocol::Frontend::Runtime* frontend) -{ - m_frontend = frontend; -} - -void V8RuntimeAgentImpl::clearFrontend() -{ - ErrorString error; - disable(&error); - DCHECK(m_frontend); - m_frontend = nullptr; -} - void V8RuntimeAgentImpl::restore() { if (!m_state->booleanProperty(V8RuntimeAgentImplState::runtimeEnabled, false)) return; - m_frontend->executionContextsCleared(); + m_frontend.executionContextsCleared(); ErrorString error; enable(&error); if (m_state->booleanProperty(V8RuntimeAgentImplState::customObjectFormatterEnabled, false)) @@ -402,7 +384,7 @@ void V8RuntimeAgentImpl::reset() for (auto& idContext : *contexts) idContext.second->setReported(false); } - m_frontend->executionContextsCleared(); + m_frontend.executionContextsCleared(); } } @@ -417,21 +399,21 @@ void V8RuntimeAgentImpl::reportExecutionContextCreated(InspectedContext* context .setName(context->humanReadableName()) .setOrigin(context->origin()) .setFrameId(context->frameId()).build(); - m_frontend->executionContextCreated(std::move(description)); + m_frontend.executionContextCreated(std::move(description)); } void V8RuntimeAgentImpl::reportExecutionContextDestroyed(InspectedContext* context) { if (m_enabled && context->isReported()) { context->setReported(false); - m_frontend->executionContextDestroyed(context->contextId()); + m_frontend.executionContextDestroyed(context->contextId()); } } void V8RuntimeAgentImpl::inspect(std::unique_ptr<protocol::Runtime::RemoteObject> objectToInspect, std::unique_ptr<protocol::DictionaryValue> hints) { if (m_enabled) - m_frontend->inspectRequested(std::move(objectToInspect), std::move(hints)); + m_frontend.inspectRequested(std::move(objectToInspect), std::move(hints)); } } // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/V8RuntimeAgentImpl.h b/deps/v8_inspector/platform/v8_inspector/V8RuntimeAgentImpl.h index 3870106acb1081..7eb2b135450c4c 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8RuntimeAgentImpl.h +++ b/deps/v8_inspector/platform/v8_inspector/V8RuntimeAgentImpl.h @@ -32,8 +32,10 @@ #define V8RuntimeAgentImpl_h #include "platform/inspector_protocol/Allocator.h" -#include "platform/inspector_protocol/Frontend.h" -#include "platform/v8_inspector/public/V8RuntimeAgent.h" +#include "platform/inspector_protocol/String16.h" +#include "platform/v8_inspector/protocol/Runtime.h" + +#include <v8.h> namespace blink { @@ -49,17 +51,12 @@ class DictionaryValue; using protocol::Maybe; -class V8RuntimeAgentImpl : public V8RuntimeAgent { +class V8RuntimeAgentImpl : public protocol::Runtime::Backend { PROTOCOL_DISALLOW_COPY(V8RuntimeAgentImpl); public: - explicit V8RuntimeAgentImpl(V8InspectorSessionImpl*); + V8RuntimeAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*, protocol::DictionaryValue* state); ~V8RuntimeAgentImpl() override; - - // State management methods. - void setInspectorState(protocol::DictionaryValue*) override; - void setFrontend(protocol::Frontend::Runtime*) override; - void clearFrontend() override; - void restore() override; + void restore(); // Part of the protocol. void enable(ErrorString*) override; @@ -122,7 +119,7 @@ class V8RuntimeAgentImpl : public V8RuntimeAgent { private: V8InspectorSessionImpl* m_session; protocol::DictionaryValue* m_state; - protocol::Frontend::Runtime* m_frontend; + protocol::Runtime::Frontend m_frontend; V8DebuggerImpl* m_debugger; bool m_enabled; protocol::HashMap<String16, std::unique_ptr<v8::Global<v8::Script>>> m_compiledScripts; diff --git a/deps/v8_inspector/platform/v8_inspector/V8StackTraceImpl.cpp b/deps/v8_inspector/platform/v8_inspector/V8StackTraceImpl.cpp index 3cba145eaf0015..650c71eb957f20 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8StackTraceImpl.cpp +++ b/deps/v8_inspector/platform/v8_inspector/V8StackTraceImpl.cpp @@ -4,11 +4,11 @@ #include "platform/v8_inspector/V8StackTraceImpl.h" +#include "platform/inspector_protocol/Platform.h" #include "platform/inspector_protocol/String16.h" #include "platform/v8_inspector/V8DebuggerAgentImpl.h" #include "platform/v8_inspector/V8DebuggerImpl.h" #include "platform/v8_inspector/V8StringUtil.h" -#include "wtf/PtrUtil.h" #include <v8-debug.h> #include <v8-profiler.h> @@ -72,7 +72,7 @@ V8StackTraceImpl::Frame::~Frame() { } -// buildInspectorObject() and ScriptCallStack's toTracedValue() should set the same fields. +// buildInspectorObject() and SourceLocation's toTracedValue() should set the same fields. // If either of them is modified, the other should be also modified. std::unique_ptr<protocol::Runtime::CallFrame> V8StackTraceImpl::Frame::buildInspectorObject() const { @@ -85,6 +85,11 @@ std::unique_ptr<protocol::Runtime::CallFrame> V8StackTraceImpl::Frame::buildInsp .build(); } +V8StackTraceImpl::Frame V8StackTraceImpl::Frame::isolatedCopy() const +{ + return Frame(m_functionName.isolatedCopy(), m_scriptId.isolatedCopy(), m_scriptName.isolatedCopy(), m_lineNumber, m_columnNumber); +} + std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(V8DebuggerAgentImpl* agent, v8::Local<v8::StackTrace> stackTrace, size_t maxStackSize, const String16& description) { v8::Isolate* isolate = v8::Isolate::GetCurrent(); @@ -107,7 +112,7 @@ std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(V8DebuggerAgentImpl* if (stackTrace.IsEmpty() && !asyncCallChain) return nullptr; - std::unique_ptr<V8StackTraceImpl> result(new V8StackTraceImpl(description, frames, asyncCallChain ? asyncCallChain->clone() : nullptr)); + std::unique_ptr<V8StackTraceImpl> result(new V8StackTraceImpl(description, frames, asyncCallChain ? asyncCallChain->cloneImpl() : nullptr)); // Crop to not exceed maxAsyncCallChainDepth. V8StackTraceImpl* deepest = result.get(); @@ -135,10 +140,28 @@ std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture(V8DebuggerAgentImpl* return V8StackTraceImpl::create(agent, stackTrace, maxStackSize, description); } -std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::clone() +std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() +{ + return cloneImpl(); +} + +std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::cloneImpl() { protocol::Vector<Frame> framesCopy(m_frames); - return wrapUnique(new V8StackTraceImpl(m_description, framesCopy, m_parent ? m_parent->clone() : nullptr)); + return wrapUnique(new V8StackTraceImpl(m_description, framesCopy, m_parent ? m_parent->cloneImpl() : nullptr)); +} + +std::unique_ptr<V8StackTrace> V8StackTraceImpl::isolatedCopy() +{ + return isolatedCopyImpl(); +} + +std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::isolatedCopyImpl() +{ + protocol::Vector<Frame> frames; + for (size_t i = 0; i < m_frames.size(); i++) + frames.append(m_frames.at(i).isolatedCopy()); + return wrapUnique(new V8StackTraceImpl(m_description.isolatedCopy(), frames, m_parent ? m_parent->isolatedCopyImpl() : nullptr)); } V8StackTraceImpl::V8StackTraceImpl(const String16& description, protocol::Vector<Frame>& frames, std::unique_ptr<V8StackTraceImpl> parent) diff --git a/deps/v8_inspector/platform/v8_inspector/V8StackTraceImpl.h b/deps/v8_inspector/platform/v8_inspector/V8StackTraceImpl.h index 9b9bd1920a114a..721789319bb297 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8StackTraceImpl.h +++ b/deps/v8_inspector/platform/v8_inspector/V8StackTraceImpl.h @@ -6,8 +6,8 @@ #define V8StackTraceImpl_h #include "platform/inspector_protocol/Collections.h" +#include "platform/inspector_protocol/Platform.h" #include "platform/v8_inspector/public/V8StackTrace.h" -#include "wtf/PtrUtil.h" namespace blink { @@ -31,6 +31,7 @@ class V8StackTraceImpl final : public V8StackTrace { const String16& sourceURL() const { return m_scriptName; } int lineNumber() const { return m_lineNumber; } int columnNumber() const { return m_columnNumber; } + Frame isolatedCopy() const; private: friend class V8StackTraceImpl; @@ -47,7 +48,10 @@ class V8StackTraceImpl final : public V8StackTrace { static std::unique_ptr<V8StackTraceImpl> create(V8DebuggerAgentImpl*, v8::Local<v8::StackTrace>, size_t maxStackSize, const String16& description = String16()); static std::unique_ptr<V8StackTraceImpl> capture(V8DebuggerAgentImpl*, size_t maxStackSize, const String16& description = String16()); - std::unique_ptr<V8StackTraceImpl> clone(); + std::unique_ptr<V8StackTrace> clone() override; + std::unique_ptr<V8StackTraceImpl> cloneImpl(); + std::unique_ptr<V8StackTrace> isolatedCopy() override; + std::unique_ptr<V8StackTraceImpl> isolatedCopyImpl(); std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectForTail(V8DebuggerAgentImpl*) const; ~V8StackTraceImpl() override; diff --git a/deps/v8_inspector/platform/v8_inspector/injected_script_externs.js b/deps/v8_inspector/platform/v8_inspector/injected_script_externs.js index a543e5471fce0c..218010938c10e3 100644 --- a/deps/v8_inspector/platform/v8_inspector/injected_script_externs.js +++ b/deps/v8_inspector/platform/v8_inspector/injected_script_externs.js @@ -7,12 +7,6 @@ function InjectedScriptHostClass() { } -/** - * @param {*} objectId - * @param {!Object} hints - */ -InjectedScriptHostClass.prototype.inspect = function(objectId, hints) {} - /** * @param {*} obj * @return {string} @@ -21,9 +15,10 @@ InjectedScriptHostClass.prototype.internalConstructorName = function(obj) {} /** * @param {*} obj + * @param {function()|undefined} func * @return {boolean} */ -InjectedScriptHostClass.prototype.formatAccessorsAsProperties = function(obj) {} +InjectedScriptHostClass.prototype.formatAccessorsAsProperties = function(obj, func) {} /** * @param {*} obj @@ -49,12 +44,6 @@ InjectedScriptHostClass.prototype.collectionEntries = function(obj) {} */ InjectedScriptHostClass.prototype.getInternalProperties = function(obj) {} -/** - * @param {!EventTarget} target - * @return {!Object|undefined} - */ -InjectedScriptHostClass.prototype.getEventListeners = function(target) {} - /** * @param {!Function} fn * @param {*} receiver @@ -63,13 +52,6 @@ InjectedScriptHostClass.prototype.getEventListeners = function(target) {} */ InjectedScriptHostClass.prototype.suppressWarningsAndCallFunction = function(fn, receiver, argv) {} -/** - * @param {!Object} obj - * @param {string} key - * @param {*} value - */ -InjectedScriptHostClass.prototype.setNonEnumProperty = function(obj, key, value) {} - /** * @param {*} value * @param {string} groupName diff --git a/deps/v8_inspector/platform/v8_inspector/js_protocol-1.1.json b/deps/v8_inspector/platform/v8_inspector/js_protocol-1.1.json new file mode 100644 index 00000000000000..8961f3bbd2321e --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/js_protocol-1.1.json @@ -0,0 +1,1866 @@ +{ + "domains": [ + { + "domain": "Runtime", + "description": "Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. Evaluation results are returned as mirror object that expose object type, string representation and unique identifier that can be used for further object reference. Original objects are maintained in memory unless they are either explicitly released or are released along with the other objects in their object group.", + "types": [ + { + "id": "RemoteObjectId", + "type": "string", + "description": "Unique object identifier." + }, + { + "id": "RemoteObject", + "type": "object", + "description": "Mirror object referencing original JavaScript object.", + "properties": [ + { + "name": "type", + "type": "string", + "enum": [ + "object", + "function", + "undefined", + "string", + "number", + "boolean" + ], + "description": "Object type." + }, + { + "name": "subtype", + "type": "string", + "optional": true, + "enum": [ + "array", + "null", + "node", + "regexp", + "date" + ], + "description": "Object subtype hint. Specified for <code>object</code> type values only." + }, + { + "name": "className", + "type": "string", + "optional": true, + "description": "Object class (constructor) name. Specified for <code>object</code> type values only." + }, + { + "name": "value", + "type": "any", + "optional": true, + "description": "Remote object value (in case of primitive values or JSON values if it was requested)." + }, + { + "name": "description", + "type": "string", + "optional": true, + "description": "String representation of the object." + }, + { + "name": "objectId", + "$ref": "RemoteObjectId", + "optional": true, + "description": "Unique object identifier (for non-primitive values)." + }, + { + "name": "preview", + "$ref": "ObjectPreview", + "optional": true, + "description": "Preview containing abbreviated property values.", + "hidden": true + } + ] + }, + { + "id": "ObjectPreview", + "type": "object", + "hidden": true, + "description": "Object containing abbreviated remote object value.", + "properties": [ + { + "name": "lossless", + "type": "boolean", + "description": "Determines whether preview is lossless (contains all information of the original object)." + }, + { + "name": "overflow", + "type": "boolean", + "description": "True iff some of the properties of the original did not fit." + }, + { + "name": "properties", + "type": "array", + "items": { + "$ref": "PropertyPreview" + }, + "description": "List of the properties." + } + ] + }, + { + "id": "PropertyPreview", + "type": "object", + "hidden": true, + "properties": [ + { + "name": "name", + "type": "string", + "description": "Property name." + }, + { + "name": "type", + "type": "string", + "enum": [ + "object", + "function", + "undefined", + "string", + "number", + "boolean" + ], + "description": "Object type." + }, + { + "name": "value", + "type": "string", + "optional": true, + "description": "User-friendly property value string." + }, + { + "name": "valuePreview", + "$ref": "ObjectPreview", + "optional": true, + "description": "Nested value preview." + }, + { + "name": "subtype", + "type": "string", + "optional": true, + "enum": [ + "array", + "null", + "node", + "regexp", + "date" + ], + "description": "Object subtype hint. Specified for <code>object</code> type values only." + } + ] + }, + { + "id": "PropertyDescriptor", + "type": "object", + "description": "Object property descriptor.", + "properties": [ + { + "name": "name", + "type": "string", + "description": "Property name." + }, + { + "name": "value", + "$ref": "RemoteObject", + "optional": true, + "description": "The value associated with the property." + }, + { + "name": "writable", + "type": "boolean", + "optional": true, + "description": "True if the value associated with the property may be changed (data descriptors only)." + }, + { + "name": "get", + "$ref": "RemoteObject", + "optional": true, + "description": "A function which serves as a getter for the property, or <code>undefined</code> if there is no getter (accessor descriptors only)." + }, + { + "name": "set", + "$ref": "RemoteObject", + "optional": true, + "description": "A function which serves as a setter for the property, or <code>undefined</code> if there is no setter (accessor descriptors only)." + }, + { + "name": "configurable", + "type": "boolean", + "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." + }, + { + "name": "enumerable", + "type": "boolean", + "description": "True if this property shows up during enumeration of the properties on the corresponding object." + }, + { + "name": "wasThrown", + "type": "boolean", + "optional": true, + "description": "True if the result was thrown during the evaluation." + }, + { + "name": "isOwn", + "optional": true, + "type": "boolean", + "description": "True if the property is owned for the object.", + "hidden": true + } + ] + }, + { + "id": "InternalPropertyDescriptor", + "type": "object", + "description": "Object internal property descriptor. This property isn't normally visible in JavaScript code.", + "properties": [ + { + "name": "name", + "type": "string", + "description": "Conventional property name." + }, + { + "name": "value", + "$ref": "RemoteObject", + "optional": true, + "description": "The value associated with the property." + } + ], + "hidden": true + }, + { + "id": "CallArgument", + "type": "object", + "description": "Represents function call argument. Either remote object id <code>objectId</code> or primitive <code>value</code> or neither of (for undefined) them should be specified.", + "properties": [ + { + "name": "value", + "type": "any", + "optional": true, + "description": "Primitive value." + }, + { + "name": "objectId", + "$ref": "RemoteObjectId", + "optional": true, + "description": "Remote object handle." + } + ] + }, + { + "id": "ExecutionContextId", + "type": "integer", + "description": "Id of an execution context." + }, + { + "id": "ExecutionContextDescription", + "type": "object", + "description": "Description of an isolated world.", + "properties": [ + { + "name": "id", + "$ref": "ExecutionContextId", + "description": "Unique id of the execution context. It can be used to specify in which execution context script evaluation should be performed." + }, + { + "name": "isPageContext", + "type": "boolean", + "description": "True if this is a context where inpspected web page scripts run. False if it is a content script isolated context.", + "hidden": true + }, + { + "name": "name", + "type": "string", + "description": "Human readable name describing given context.", + "hidden": true + }, + { + "name": "frameId", + "type": "string", + "description": "Id of the owning frame." + } + ] + } + ], + "commands": [ + { + "name": "evaluate", + "parameters": [ + { + "name": "expression", + "type": "string", + "description": "Expression to evaluate." + }, + { + "name": "objectGroup", + "type": "string", + "optional": true, + "description": "Symbolic group name that can be used to release multiple objects." + }, + { + "name": "includeCommandLineAPI", + "type": "boolean", + "optional": true, + "description": "Determines whether Command Line API should be available during the evaluation.", + "hidden": true + }, + { + "name": "doNotPauseOnExceptionsAndMuteConsole", + "type": "boolean", + "optional": true, + "description": "Specifies whether evaluation should stop on exceptions and mute console. Overrides setPauseOnException state.", + "hidden": true + }, + { + "name": "contextId", + "$ref": "Runtime.ExecutionContextId", + "optional": true, + "description": "Specifies in which isolated context to perform evaluation. Each content script lives in an isolated context and this parameter may be used to specify one of those contexts. If the parameter is omitted or 0 the evaluation will be performed in the context of the inspected page." + }, + { + "name": "returnByValue", + "type": "boolean", + "optional": true, + "description": "Whether the result is expected to be a JSON object that should be sent by value." + }, + { + "name": "generatePreview", + "type": "boolean", + "optional": true, + "hidden": true, + "description": "Whether preview should be generated for the result." + } + ], + "returns": [ + { + "name": "result", + "$ref": "RemoteObject", + "description": "Evaluation result." + }, + { + "name": "wasThrown", + "type": "boolean", + "optional": true, + "description": "True if the result was thrown during the evaluation." + } + ], + "description": "Evaluates expression on global object." + }, + { + "name": "callFunctionOn", + "parameters": [ + { + "name": "objectId", + "$ref": "RemoteObjectId", + "description": "Identifier of the object to call function on." + }, + { + "name": "functionDeclaration", + "type": "string", + "description": "Declaration of the function to call." + }, + { + "name": "arguments", + "type": "array", + "items": { + "$ref": "CallArgument", + "description": "Call argument." + }, + "optional": true, + "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target object." + }, + { + "name": "doNotPauseOnExceptionsAndMuteConsole", + "type": "boolean", + "optional": true, + "description": "Specifies whether function call should stop on exceptions and mute console. Overrides setPauseOnException state.", + "hidden": true + }, + { + "name": "returnByValue", + "type": "boolean", + "optional": true, + "description": "Whether the result is expected to be a JSON object which should be sent by value." + }, + { + "name": "generatePreview", + "type": "boolean", + "optional": true, + "hidden": true, + "description": "Whether preview should be generated for the result." + } + ], + "returns": [ + { + "name": "result", + "$ref": "RemoteObject", + "description": "Call result." + }, + { + "name": "wasThrown", + "type": "boolean", + "optional": true, + "description": "True if the result was thrown during the evaluation." + } + ], + "description": "Calls function with given declaration on the given object. Object group of the result is inherited from the target object." + }, + { + "name": "getProperties", + "parameters": [ + { + "name": "objectId", + "$ref": "RemoteObjectId", + "description": "Identifier of the object to return properties for." + }, + { + "name": "ownProperties", + "optional": true, + "type": "boolean", + "description": "If true, returns properties belonging only to the element itself, not to its prototype chain." + }, + { + "name": "accessorPropertiesOnly", + "optional": true, + "type": "boolean", + "description": "If true, returns accessor properties (with getter/setter) only; internal properties are not returned either.", + "hidden": true + } + ], + "returns": [ + { + "name": "result", + "type": "array", + "items": { + "$ref": "PropertyDescriptor" + }, + "description": "Object properties." + }, + { + "name": "internalProperties", + "optional": true, + "type": "array", + "items": { + "$ref": "InternalPropertyDescriptor" + }, + "description": "Internal object properties (only of the element itself).", + "hidden": true + } + ], + "description": "Returns properties of a given object. Object group of the result is inherited from the target object." + }, + { + "name": "releaseObject", + "parameters": [ + { + "name": "objectId", + "$ref": "RemoteObjectId", + "description": "Identifier of the object to release." + } + ], + "description": "Releases remote object with given id." + }, + { + "name": "releaseObjectGroup", + "parameters": [ + { + "name": "objectGroup", + "type": "string", + "description": "Symbolic object group name." + } + ], + "description": "Releases all remote objects that belong to a given group." + }, + { + "name": "run", + "hidden": true, + "description": "Tells inspected instance(worker or page) that it can run in case it was started paused." + }, + { + "name": "enable", + "description": "Enables reporting of execution contexts creation by means of <code>executionContextCreated</code> event. When the reporting gets enabled the event will be sent immediately for each existing execution context." + }, + { + "name": "disable", + "hidden": true, + "description": "Disables reporting of execution contexts creation." + } + ], + "events": [ + { + "name": "executionContextCreated", + "parameters": [ + { + "name": "context", + "$ref": "ExecutionContextDescription", + "description": "A newly created execution contex." + } + ], + "description": "Issued when new execution context is created." + } + ] + }, + { + "domain": "Debugger", + "description": "Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing breakpoints, stepping through execution, exploring stack traces, etc.", + "types": [ + { + "id": "SearchMatch", + "type": "object", + "description": "Search match for resource.", + "properties": [ + { + "name": "lineNumber", + "type": "number", + "description": "Line number in resource content." + }, + { + "name": "lineContent", + "type": "string", + "description": "Line with match content." + } + ], + "hidden": true + }, + { + "id": "BreakpointId", + "type": "string", + "description": "Breakpoint identifier." + }, + { + "id": "ScriptId", + "type": "string", + "description": "Unique script identifier." + }, + { + "id": "CallFrameId", + "type": "string", + "description": "Call frame identifier." + }, + { + "id": "Location", + "type": "object", + "properties": [ + { + "name": "scriptId", + "$ref": "ScriptId", + "description": "Script identifier as reported in the <code>Debugger.scriptParsed</code>." + }, + { + "name": "lineNumber", + "type": "integer", + "description": "Line number in the script (0-based)." + }, + { + "name": "columnNumber", + "type": "integer", + "optional": true, + "description": "Column number in the script (0-based)." + } + ], + "description": "Location in the source code." + }, + { + "id": "FunctionDetails", + "hidden": true, + "type": "object", + "properties": [ + { + "name": "location", + "$ref": "Location", + "description": "Location of the function." + }, + { + "name": "name", + "type": "string", + "optional": true, + "description": "Name of the function. Not present for anonymous functions." + }, + { + "name": "displayName", + "type": "string", + "optional": true, + "description": "Display name of the function(specified in 'displayName' property on the function object)." + }, + { + "name": "inferredName", + "type": "string", + "optional": true, + "description": "Name of the function inferred from its initial assignment." + }, + { + "name": "scopeChain", + "type": "array", + "optional": true, + "items": { + "$ref": "Scope" + }, + "description": "Scope chain for this closure." + } + ], + "description": "Information about the function." + }, + { + "id": "CallFrame", + "type": "object", + "properties": [ + { + "name": "callFrameId", + "$ref": "CallFrameId", + "description": "Call frame identifier. This identifier is only valid while the virtual machine is paused." + }, + { + "name": "functionName", + "type": "string", + "description": "Name of the JavaScript function called on this call frame." + }, + { + "name": "location", + "$ref": "Location", + "description": "Location in the source code." + }, + { + "name": "scopeChain", + "type": "array", + "items": { + "$ref": "Scope" + }, + "description": "Scope chain for this call frame." + }, + { + "name": "this", + "$ref": "Runtime.RemoteObject", + "description": "<code>this</code> object for this call frame." + } + ], + "description": "JavaScript call frame. Array of call frames form the call stack." + }, + { + "id": "Scope", + "type": "object", + "properties": [ + { + "name": "type", + "type": "string", + "enum": [ + "global", + "local", + "with", + "closure", + "catch" + ], + "description": "Scope type." + }, + { + "name": "object", + "$ref": "Runtime.RemoteObject", + "description": "Object representing the scope. For <code>global</code> and <code>with</code> scopes it represents the actual object; for the rest of the scopes, it is artificial transient object enumerating scope variables as its properties." + } + ], + "description": "Scope description." + }, + { + "id": "SetScriptSourceError", + "type": "object", + "properties": [ + { + "name": "compileError", + "optional": true, + "type": "object", + "properties": [ + { + "name": "message", + "type": "string", + "description": "Compiler error message" + }, + { + "name": "lineNumber", + "type": "integer", + "description": "Compile error line number (1-based)" + }, + { + "name": "columnNumber", + "type": "integer", + "description": "Compile error column number (1-based)" + } + ] + } + ], + "description": "Error data for setScriptSource command. compileError is a case type for uncompilable script source error.", + "hidden": true + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables debugger for the given page. Clients should not assume that the debugging has been enabled until the result for this command is received." + }, + { + "name": "disable", + "description": "Disables debugger for given page." + }, + { + "name": "setBreakpointsActive", + "parameters": [ + { + "name": "active", + "type": "boolean", + "description": "New value for breakpoints active state." + } + ], + "description": "Activates / deactivates all breakpoints on the page." + }, + { + "name": "setSkipAllPauses", + "hidden": true, + "parameters": [ + { + "name": "skipped", + "type": "boolean", + "description": "New value for skip pauses state." + }, + { + "name": "untilReload", + "type": "boolean", + "optional": true, + "description": "Whether page reload should set skipped to false." + } + ], + "description": "Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc)." + }, + { + "name": "setBreakpointByUrl", + "parameters": [ + { + "name": "lineNumber", + "type": "integer", + "description": "Line number to set breakpoint at." + }, + { + "name": "url", + "type": "string", + "optional": true, + "description": "URL of the resources to set breakpoint on." + }, + { + "name": "urlRegex", + "type": "string", + "optional": true, + "description": "Regex pattern for the URLs of the resources to set breakpoints on. Either <code>url</code> or <code>urlRegex</code> must be specified." + }, + { + "name": "columnNumber", + "type": "integer", + "optional": true, + "description": "Offset in the line to set breakpoint at." + }, + { + "name": "condition", + "type": "string", + "optional": true, + "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." + }, + { + "name": "isAntibreakpoint", + "type": "boolean", + "optional": true, + "hidden": true, + "description": "Creates pseudo-breakpoint that prevents debugger from pausing on exception at this location." + } + ], + "returns": [ + { + "name": "breakpointId", + "$ref": "BreakpointId", + "description": "Id of the created breakpoint for further reference." + }, + { + "name": "locations", + "type": "array", + "items": { + "$ref": "Location" + }, + "description": "List of the locations this breakpoint resolved into upon addition." + } + ], + "description": "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this command is issued, all existing parsed scripts will have breakpoints resolved and returned in <code>locations</code> property. Further matching script parsing will result in subsequent <code>breakpointResolved</code> events issued. This logical breakpoint will survive page reloads." + }, + { + "name": "setBreakpoint", + "parameters": [ + { + "name": "location", + "$ref": "Location", + "description": "Location to set breakpoint in." + }, + { + "name": "condition", + "type": "string", + "optional": true, + "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." + } + ], + "returns": [ + { + "name": "breakpointId", + "$ref": "BreakpointId", + "description": "Id of the created breakpoint for further reference." + }, + { + "name": "actualLocation", + "$ref": "Location", + "description": "Location this breakpoint resolved into." + } + ], + "description": "Sets JavaScript breakpoint at a given location." + }, + { + "name": "removeBreakpoint", + "parameters": [ + { + "name": "breakpointId", + "$ref": "BreakpointId" + } + ], + "description": "Removes JavaScript breakpoint." + }, + { + "name": "continueToLocation", + "parameters": [ + { + "name": "location", + "$ref": "Location", + "description": "Location to continue to." + }, + { + "name": "interstatementLocation", + "type": "boolean", + "optional": true, + "hidden": true, + "description": "Allows breakpoints at the intemediate positions inside statements." + } + ], + "description": "Continues execution until specific location is reached." + }, + { + "name": "stepOver", + "description": "Steps over the statement." + }, + { + "name": "stepInto", + "description": "Steps into the function call." + }, + { + "name": "stepOut", + "description": "Steps out of the function call." + }, + { + "name": "pause", + "description": "Stops on the next JavaScript statement." + }, + { + "name": "resume", + "description": "Resumes JavaScript execution." + }, + { + "name": "searchInContent", + "parameters": [ + { + "name": "scriptId", + "$ref": "ScriptId", + "description": "Id of the script to search in." + }, + { + "name": "query", + "type": "string", + "description": "String to search for." + }, + { + "name": "caseSensitive", + "type": "boolean", + "optional": true, + "description": "If true, search is case sensitive." + }, + { + "name": "isRegex", + "type": "boolean", + "optional": true, + "description": "If true, treats string parameter as regex." + } + ], + "returns": [ + { + "name": "result", + "type": "array", + "items": { + "$ref": "SearchMatch" + }, + "description": "List of search matches." + } + ], + "description": "Searches for given string in script content." + }, + { + "name": "canSetScriptSource", + "returns": [ + { + "name": "result", + "type": "boolean", + "description": "True if <code>setScriptSource</code> is supported." + } + ], + "description": "Always returns true." + }, + { + "name": "setScriptSource", + "parameters": [ + { + "name": "scriptId", + "$ref": "ScriptId", + "description": "Id of the script to edit." + }, + { + "name": "scriptSource", + "type": "string", + "description": "New content of the script." + }, + { + "name": "preview", + "type": "boolean", + "optional": true, + "description": " If true the change will not actually be applied. Preview mode may be used to get result description without actually modifying the code.", + "hidden": true + } + ], + "returns": [ + { + "name": "callFrames", + "type": "array", + "optional": true, + "items": { + "$ref": "CallFrame" + }, + "description": "New stack trace in case editing has happened while VM was stopped." + }, + { + "name": "result", + "type": "object", + "optional": true, + "description": "VM-specific description of the changes applied.", + "hidden": true + } + ], + "error": { + "$ref": "SetScriptSourceError" + }, + "description": "Edits JavaScript source live." + }, + { + "name": "restartFrame", + "parameters": [ + { + "name": "callFrameId", + "$ref": "CallFrameId", + "description": "Call frame identifier to evaluate on." + } + ], + "returns": [ + { + "name": "callFrames", + "type": "array", + "items": { + "$ref": "CallFrame" + }, + "description": "New stack trace." + }, + { + "name": "result", + "type": "object", + "description": "VM-specific description.", + "hidden": true + } + ], + "hidden": true, + "description": "Restarts particular call frame from the beginning." + }, + { + "name": "getScriptSource", + "parameters": [ + { + "name": "scriptId", + "$ref": "ScriptId", + "description": "Id of the script to get source for." + } + ], + "returns": [ + { + "name": "scriptSource", + "type": "string", + "description": "Script source." + } + ], + "description": "Returns source for the script with given id." + }, + { + "name": "getFunctionDetails", + "hidden": true, + "parameters": [ + { + "name": "functionId", + "$ref": "Runtime.RemoteObjectId", + "description": "Id of the function to get location for." + } + ], + "returns": [ + { + "name": "details", + "$ref": "FunctionDetails", + "description": "Information about the function." + } + ], + "description": "Returns detailed informtation on given function." + }, + { + "name": "setPauseOnExceptions", + "parameters": [ + { + "name": "state", + "type": "string", + "enum": [ + "none", + "uncaught", + "all" + ], + "description": "Pause on exceptions mode." + } + ], + "description": "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions or no exceptions. Initial pause on exceptions state is <code>none</code>." + }, + { + "name": "evaluateOnCallFrame", + "parameters": [ + { + "name": "callFrameId", + "$ref": "CallFrameId", + "description": "Call frame identifier to evaluate on." + }, + { + "name": "expression", + "type": "string", + "description": "Expression to evaluate." + }, + { + "name": "objectGroup", + "type": "string", + "optional": true, + "description": "String object group name to put result into (allows rapid releasing resulting object handles using <code>releaseObjectGroup</code>)." + }, + { + "name": "includeCommandLineAPI", + "type": "boolean", + "optional": true, + "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false.", + "hidden": true + }, + { + "name": "doNotPauseOnExceptionsAndMuteConsole", + "type": "boolean", + "optional": true, + "description": "Specifies whether evaluation should stop on exceptions and mute console. Overrides setPauseOnException state.", + "hidden": true + }, + { + "name": "returnByValue", + "type": "boolean", + "optional": true, + "description": "Whether the result is expected to be a JSON object that should be sent by value." + }, + { + "name": "generatePreview", + "type": "boolean", + "optional": true, + "hidden": true, + "description": "Whether preview should be generated for the result." + } + ], + "returns": [ + { + "name": "result", + "$ref": "Runtime.RemoteObject", + "description": "Object wrapper for the evaluation result." + }, + { + "name": "wasThrown", + "type": "boolean", + "optional": true, + "description": "True if the result was thrown during the evaluation." + } + ], + "description": "Evaluates expression on a given call frame." + }, + { + "name": "compileScript", + "hidden": true, + "parameters": [ + { + "name": "expression", + "type": "string", + "description": "Expression to compile." + }, + { + "name": "sourceURL", + "type": "string", + "description": "Source url to be set for the script." + } + ], + "returns": [ + { + "name": "scriptId", + "$ref": "ScriptId", + "optional": true, + "description": "Id of the script." + }, + { + "name": "syntaxErrorMessage", + "type": "string", + "optional": true, + "description": "Syntax error message if compilation failed." + } + ], + "description": "Compiles expression." + }, + { + "name": "runScript", + "hidden": true, + "parameters": [ + { + "name": "scriptId", + "$ref": "ScriptId", + "description": "Id of the script to run." + }, + { + "name": "contextId", + "$ref": "Runtime.ExecutionContextId", + "optional": true, + "description": "Specifies in which isolated context to perform script run. Each content script lives in an isolated context and this parameter may be used to specify one of those contexts. If the parameter is omitted or 0 the evaluation will be performed in the context of the inspected page." + }, + { + "name": "objectGroup", + "type": "string", + "optional": true, + "description": "Symbolic group name that can be used to release multiple objects." + }, + { + "name": "doNotPauseOnExceptionsAndMuteConsole", + "type": "boolean", + "optional": true, + "description": "Specifies whether script run should stop on exceptions and mute console. Overrides setPauseOnException state." + } + ], + "returns": [ + { + "name": "result", + "$ref": "Runtime.RemoteObject", + "description": "Run result." + }, + { + "name": "wasThrown", + "type": "boolean", + "optional": true, + "description": "True if the result was thrown during the script run." + } + ], + "description": "Runs script with given id in a given context." + }, + { + "name": "setOverlayMessage", + "parameters": [ + { + "name": "message", + "type": "string", + "optional": true, + "description": "Overlay message to display when paused in debugger." + } + ], + "hidden": true, + "description": "Sets overlay message." + }, + { + "name": "setVariableValue", + "parameters": [ + { + "name": "scopeNumber", + "type": "integer", + "description": "0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch' scope types are allowed. Other scopes could be manipulated manually." + }, + { + "name": "variableName", + "type": "string", + "description": "Variable name." + }, + { + "name": "newValue", + "$ref": "Runtime.CallArgument", + "description": "New variable value." + }, + { + "name": "callFrameId", + "$ref": "CallFrameId", + "optional": true, + "description": "Id of callframe that holds variable." + }, + { + "name": "functionObjectId", + "$ref": "Runtime.RemoteObjectId", + "optional": true, + "description": "Object id of closure (function) that holds variable." + } + ], + "hidden": true, + "description": "Changes value of variable in a callframe or a closure. Either callframe or function must be specified. Object-based scopes are not supported and must be mutated manually." + }, + { + "name": "getStepInPositions", + "parameters": [ + { + "name": "callFrameId", + "$ref": "CallFrameId", + "description": "Id of a call frame where the current statement should be analized" + } + ], + "returns": [ + { + "name": "stepInPositions", + "type": "array", + "items": { + "$ref": "Location" + }, + "optional": true, + "description": "experimental" + } + ], + "hidden": true, + "description": "Lists all positions where step-in is possible for a current statement in a specified call frame" + }, + { + "name": "getBacktrace", + "returns": [ + { + "name": "callFrames", + "type": "array", + "items": { + "$ref": "CallFrame" + }, + "description": "Call stack the virtual machine stopped on." + } + ], + "hidden": true, + "description": "Returns call stack including variables changed since VM was paused. VM must be paused." + }, + { + "name": "skipStackFrames", + "parameters": [ + { + "name": "script", + "optional": true, + "type": "string", + "description": "Regular expression defining the scripts to ignore while stepping." + } + ], + "hidden": true, + "description": "Makes backend skip steps in the sources with names matching given pattern. VM will try leave blacklisted scripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful." + } + ], + "events": [ + { + "name": "globalObjectCleared", + "description": "Called when global has been cleared and debugger client should reset its state. Happens upon navigation or reload." + }, + { + "name": "scriptParsed", + "parameters": [ + { + "name": "scriptId", + "$ref": "ScriptId", + "description": "Identifier of the script parsed." + }, + { + "name": "url", + "type": "string", + "description": "URL or name of the script parsed (if any)." + }, + { + "name": "startLine", + "type": "integer", + "description": "Line offset of the script within the resource with given URL (for script tags)." + }, + { + "name": "startColumn", + "type": "integer", + "description": "Column offset of the script within the resource with given URL." + }, + { + "name": "endLine", + "type": "integer", + "description": "Last line of the script." + }, + { + "name": "endColumn", + "type": "integer", + "description": "Length of the last line of the script." + }, + { + "name": "isContentScript", + "type": "boolean", + "optional": true, + "description": "Determines whether this script is a user extension script." + }, + { + "name": "sourceMapURL", + "type": "string", + "optional": true, + "description": "URL of source map associated with script (if any)." + }, + { + "name": "hasSourceURL", + "type": "boolean", + "optional": true, + "description": "True, if this script has sourceURL.", + "hidden": true + } + ], + "description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger." + }, + { + "name": "scriptFailedToParse", + "parameters": [ + { + "name": "scriptId", + "$ref": "ScriptId", + "description": "Identifier of the script parsed." + }, + { + "name": "url", + "type": "string", + "description": "URL or name of the script parsed (if any)." + }, + { + "name": "startLine", + "type": "integer", + "description": "Line offset of the script within the resource with given URL (for script tags)." + }, + { + "name": "startColumn", + "type": "integer", + "description": "Column offset of the script within the resource with given URL." + }, + { + "name": "endLine", + "type": "integer", + "description": "Last line of the script." + }, + { + "name": "endColumn", + "type": "integer", + "description": "Length of the last line of the script." + }, + { + "name": "isContentScript", + "type": "boolean", + "optional": true, + "description": "Determines whether this script is a user extension script." + }, + { + "name": "sourceMapURL", + "type": "string", + "optional": true, + "description": "URL of source map associated with script (if any)." + }, + { + "name": "hasSourceURL", + "type": "boolean", + "optional": true, + "description": "True, if this script has sourceURL.", + "hidden": true + } + ], + "description": "Fired when virtual machine fails to parse the script." + }, + { + "name": "breakpointResolved", + "parameters": [ + { + "name": "breakpointId", + "$ref": "BreakpointId", + "description": "Breakpoint unique identifier." + }, + { + "name": "location", + "$ref": "Location", + "description": "Actual breakpoint location." + } + ], + "description": "Fired when breakpoint is resolved to an actual script and location." + }, + { + "name": "paused", + "parameters": [ + { + "name": "callFrames", + "type": "array", + "items": { + "$ref": "CallFrame" + }, + "description": "Call stack the virtual machine stopped on." + }, + { + "name": "reason", + "type": "string", + "enum": [ + "XHR", + "DOM", + "EventListener", + "exception", + "assert", + "CSPViolation", + "debugCommand", + "other" + ], + "description": "Pause reason." + }, + { + "name": "data", + "type": "object", + "optional": true, + "description": "Object containing break-specific auxiliary properties." + }, + { + "name": "hitBreakpoints", + "type": "array", + "optional": true, + "items": { + "type": "string" + }, + "description": "Hit breakpoints IDs", + "hidden": true + } + ], + "description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria." + }, + { + "name": "resumed", + "description": "Fired when the virtual machine resumed execution." + } + ] + }, + { + "domain": "Profiler", + "hidden": true, + "types": [ + { + "id": "ProfileHeader", + "type": "object", + "description": "Profile header.", + "properties": [ + { + "name": "title", + "type": "string", + "description": "Profile title." + }, + { + "name": "uid", + "type": "integer", + "description": "Unique identifier of the profile." + } + ] + }, + { + "id": "CPUProfileNode", + "type": "object", + "description": "CPU Profile node. Holds callsite information, execution statistics and child nodes.", + "properties": [ + { + "name": "functionName", + "type": "string", + "description": "Function name." + }, + { + "name": "scriptId", + "$ref": "Debugger.ScriptId", + "description": "Script identifier." + }, + { + "name": "url", + "type": "string", + "description": "URL." + }, + { + "name": "lineNumber", + "type": "integer", + "description": "Line number." + }, + { + "name": "hitCount", + "type": "integer", + "description": "Number of samples where this node was on top of the call stack." + }, + { + "name": "callUID", + "type": "number", + "description": "Call UID." + }, + { + "name": "children", + "type": "array", + "items": { + "$ref": "CPUProfileNode" + }, + "description": "Child nodes." + }, + { + "name": "deoptReason", + "type": "string", + "description": "The reason of being not optimized. The function may be deoptimized or marked as don't optimize." + }, + { + "name": "id", + "optional": true, + "type": "integer", + "description": "Unique id of the node." + } + ] + }, + { + "id": "CPUProfile", + "type": "object", + "description": "Profile.", + "properties": [ + { + "name": "head", + "$ref": "CPUProfileNode" + }, + { + "name": "startTime", + "type": "number", + "description": "Profiling start time in seconds." + }, + { + "name": "endTime", + "type": "number", + "description": "Profiling end time in seconds." + }, + { + "name": "samples", + "optional": true, + "type": "array", + "items": { + "type": "integer" + }, + "description": "Ids of samples top nodes." + } + ] + }, + { + "id": "HeapSnapshotObjectId", + "type": "string", + "description": "Heap snashot object id." + } + ], + "commands": [ + { + "name": "enable" + }, + { + "name": "disable" + }, + { + "name": "start" + }, + { + "name": "stop", + "returns": [ + { + "name": "header", + "$ref": "ProfileHeader", + "description": "The header of the recorded profile." + } + ] + }, + { + "name": "getProfileHeaders", + "returns": [ + { + "name": "headers", + "type": "array", + "items": { + "$ref": "ProfileHeader" + } + } + ] + }, + { + "name": "getCPUProfile", + "parameters": [ + { + "name": "uid", + "type": "integer" + } + ], + "returns": [ + { + "name": "profile", + "$ref": "CPUProfile" + } + ] + }, + { + "name": "removeProfile", + "parameters": [ + { + "name": "type", + "type": "string" + }, + { + "name": "uid", + "type": "integer" + } + ] + }, + { + "name": "clearProfiles" + } + ], + "events": [ + { + "name": "addProfileHeader", + "parameters": [ + { + "name": "header", + "$ref": "ProfileHeader" + } + ] + }, + { + "name": "setRecordingProfile", + "parameters": [ + { + "name": "isProfiling", + "type": "boolean" + } + ] + }, + { + "name": "resetProfiles" + } + ] + }, + { + "domain": "HeapProfiler", + "hidden": true, + "types": [ + { + "id": "ProfileHeader", + "type": "object", + "description": "Profile header.", + "properties": [ + { + "name": "title", + "type": "string", + "description": "Profile title." + }, + { + "name": "uid", + "type": "integer", + "description": "Unique identifier of the profile." + }, + { + "name": "maxJSObjectId", + "type": "integer", + "optional": true, + "description": "Last seen JS object Id." + } + ] + }, + { + "id": "HeapSnapshotObjectId", + "type": "string", + "description": "Heap snashot object id." + } + ], + "commands": [ + { + "name": "getProfileHeaders", + "returns": [ + { + "name": "headers", + "type": "array", + "items": { + "$ref": "ProfileHeader" + } + } + ] + }, + { + "name": "startTrackingHeapObjects" + }, + { + "name": "stopTrackingHeapObjects" + }, + { + "name": "getHeapSnapshot", + "parameters": [ + { + "name": "uid", + "type": "integer" + } + ] + }, + { + "name": "removeProfile", + "parameters": [ + { + "name": "uid", + "type": "integer" + } + ] + }, + { + "name": "clearProfiles" + }, + { + "name": "takeHeapSnapshot", + "parameters": [ + { + "name": "reportProgress", + "type": "boolean", + "optional": true, + "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken." + } + ] + }, + { + "name": "collectGarbage" + }, + { + "name": "getObjectByHeapObjectId", + "parameters": [ + { + "name": "objectId", + "$ref": "HeapSnapshotObjectId" + }, + { + "name": "objectGroup", + "type": "string", + "optional": true, + "description": "Symbolic group name that can be used to release multiple objects." + } + ], + "returns": [ + { + "name": "result", + "$ref": "Runtime.RemoteObject", + "description": "Evaluation result." + } + ] + }, + { + "name": "getHeapObjectId", + "parameters": [ + { + "name": "objectId", + "$ref": "Runtime.RemoteObjectId", + "description": "Identifier of the object to get heap object id for." + } + ], + "returns": [ + { + "name": "heapSnapshotObjectId", + "$ref": "HeapSnapshotObjectId", + "description": "Id of the heap snapshot object corresponding to the passed remote object id." + } + ] + } + ], + "events": [ + { + "name": "addProfileHeader", + "parameters": [ + { + "name": "header", + "$ref": "ProfileHeader" + } + ] + }, + { + "name": "addHeapSnapshotChunk", + "parameters": [ + { + "name": "uid", + "type": "integer" + }, + { + "name": "chunk", + "type": "string" + } + ] + }, + { + "name": "finishHeapSnapshot", + "parameters": [ + { + "name": "uid", + "type": "integer" + } + ] + }, + { + "name": "resetProfiles" + }, + { + "name": "reportHeapSnapshotProgress", + "parameters": [ + { + "name": "done", + "type": "integer" + }, + { + "name": "total", + "type": "integer" + } + ] + }, + { + "name": "lastSeenObjectId", + "description": "If heap objects tracking has been started then backend regulary sends a current value for last seen object id and corresponding timestamp. If the were changes in the heap since last event then one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event.", + "parameters": [ + { + "name": "lastSeenObjectId", + "type": "integer" + }, + { + "name": "timestamp", + "type": "number" + } + ] + }, + { + "name": "heapStatsUpdate", + "description": "If heap objects tracking has been started then backend may send update for one or more fragments", + "parameters": [ + { + "name": "statsUpdate", + "type": "array", + "items": { + "type": "integer" + }, + "description": "An array of triplets. Each triplet describes a fragment. The first integer is the fragment index, the second integer is a total count of objects for the fragment, the third integer is a total size of the objects for the fragment." + } + ] + } + ] + } + ], + "version": { + "major": "1", + "minor": "1" + } +} \ No newline at end of file diff --git a/deps/v8_inspector/platform/v8_inspector/js_protocol.json b/deps/v8_inspector/platform/v8_inspector/js_protocol.json new file mode 100644 index 00000000000000..9ae7e60cb025ac --- /dev/null +++ b/deps/v8_inspector/platform/v8_inspector/js_protocol.json @@ -0,0 +1,976 @@ +{ + "version": { "major": "1", "minor": "1" }, + "domains": [{ + "domain": "Runtime", + "description": "Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. Evaluation results are returned as mirror object that expose object type, string representation and unique identifier that can be used for further object reference. Original objects are maintained in memory unless they are either explicitly released or are released along with the other objects in their object group.", + "types": [ + { + "id": "ScriptId", + "type": "string", + "description": "Unique script identifier." + }, + { + "id": "RemoteObjectId", + "type": "string", + "description": "Unique object identifier." + }, + { + "id": "RemoteObject", + "type": "object", + "description": "Mirror object referencing original JavaScript object.", + "properties": [ + { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." }, + { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for <code>object</code> type values only." }, + { "name": "className", "type": "string", "optional": true, "description": "Object class (constructor) name. Specified for <code>object</code> type values only." }, + { "name": "value", "type": "any", "optional": true, "description": "Remote object value in case of primitive values or JSON values (if it was requested), or description string if the value can not be JSON-stringified (like NaN, Infinity, -Infinity, -0)." }, + { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." }, + { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Unique object identifier (for non-primitive values)." }, + { "name": "preview", "$ref": "ObjectPreview", "optional": true, "description": "Preview containing abbreviated property values. Specified for <code>object</code> type values only.", "hidden": true }, + { "name": "customPreview", "$ref": "CustomPreview", "optional": true, "hidden": true} + ] + }, + { + "id": "CustomPreview", + "type": "object", + "hidden": true, + "properties": [ + { "name": "header", "type": "string"}, + { "name": "hasBody", "type": "boolean"}, + { "name": "formatterObjectId", "$ref": "RemoteObjectId"}, + { "name": "bindRemoteObjectFunctionId", "$ref": "RemoteObjectId" }, + { "name": "configObjectId", "$ref": "RemoteObjectId", "optional": true } + ] + }, + { + "id": "ObjectPreview", + "type": "object", + "hidden": true, + "description": "Object containing abbreviated remote object value.", + "properties": [ + { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." }, + { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for <code>object</code> type values only." }, + { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." }, + { "name": "overflow", "type": "boolean", "description": "True iff some of the properties or entries of the original object did not fit." }, + { "name": "properties", "type": "array", "items": { "$ref": "PropertyPreview" }, "description": "List of the properties." }, + { "name": "entries", "type": "array", "items": { "$ref": "EntryPreview" }, "optional": true, "description": "List of the entries. Specified for <code>map</code> and <code>set</code> subtype values only." } + ] + }, + { + "id": "PropertyPreview", + "type": "object", + "hidden": true, + "properties": [ + { "name": "name", "type": "string", "description": "Property name." }, + { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol", "accessor"], "description": "Object type. Accessor means that the property itself is an accessor property." }, + { "name": "value", "type": "string", "optional": true, "description": "User-friendly property value string." }, + { "name": "valuePreview", "$ref": "ObjectPreview", "optional": true, "description": "Nested value preview." }, + { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for <code>object</code> type values only." } + ] + }, + { + "id": "EntryPreview", + "type": "object", + "hidden": true, + "properties": [ + { "name": "key", "$ref": "ObjectPreview", "optional": true, "description": "Preview of the key. Specified for map-like collection entries." }, + { "name": "value", "$ref": "ObjectPreview", "description": "Preview of the value." } + ] + }, + { + "id": "PropertyDescriptor", + "type": "object", + "description": "Object property descriptor.", + "properties": [ + { "name": "name", "type": "string", "description": "Property name or symbol description." }, + { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." }, + { "name": "writable", "type": "boolean", "optional": true, "description": "True if the value associated with the property may be changed (data descriptors only)." }, + { "name": "get", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a getter for the property, or <code>undefined</code> if there is no getter (accessor descriptors only)." }, + { "name": "set", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a setter for the property, or <code>undefined</code> if there is no setter (accessor descriptors only)." }, + { "name": "configurable", "type": "boolean", "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." }, + { "name": "enumerable", "type": "boolean", "description": "True if this property shows up during enumeration of the properties on the corresponding object." }, + { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }, + { "name": "isOwn", "optional": true, "type": "boolean", "description": "True if the property is owned for the object.", "hidden": true }, + { "name": "symbol", "$ref": "RemoteObject", "optional": true, "description": "Property symbol object, if the property is of the <code>symbol</code> type.", "hidden": true } + ] + }, + { + "id": "InternalPropertyDescriptor", + "type": "object", + "description": "Object internal property descriptor. This property isn't normally visible in JavaScript code.", + "properties": [ + { "name": "name", "type": "string", "description": "Conventional property name." }, + { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." } + ], + "hidden": true + }, + { + "id": "CallArgument", + "type": "object", + "description": "Represents function call argument. Either remote object id <code>objectId</code> or primitive <code>value</code> or neither of (for undefined) them should be specified.", + "properties": [ + { "name": "value", "type": "any", "optional": true, "description": "Primitive value, or description string if the value can not be JSON-stringified (like NaN, Infinity, -Infinity, -0)." }, + { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Remote object handle." }, + { "name": "type", "optional": true, "hidden": true, "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." } + ] + }, + { + "id": "ExecutionContextId", + "type": "integer", + "description": "Id of an execution context." + }, + { + "id": "ExecutionContextDescription", + "type": "object", + "description": "Description of an isolated world.", + "properties": [ + { "name": "id", "$ref": "ExecutionContextId", "description": "Unique id of the execution context. It can be used to specify in which execution context script evaluation should be performed." }, + { "name": "isDefault", "type": "boolean", "description": "Whether context is the default page context (as opposite to e.g. context of content script).", "hidden": true }, + { "name": "origin", "type": "string", "description": "Execution context origin.", "hidden": true}, + { "name": "name", "type": "string", "description": "Human readable name describing given context.", "hidden": true}, + { "name": "frameId", "type": "string", "description": "Id of the owning frame. May be an empty string if the context is not associated with a frame." } + ] + }, + { + "id": "ExceptionDetails", + "type": "object", + "description": "Detailed information on exception (or error) that was thrown during script compilation or execution.", + "properties": [ + { "name": "text", "type": "string", "description": "Exception text." }, + { "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." }, + { "name": "scriptId", "type": "string", "optional": true, "description": "Script ID of the message origin." }, + { "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message." }, + { "name": "column", "type": "integer", "optional": true, "description": "Column number in the resource that generated this message." }, + { "name": "stack", "$ref": "StackTrace", "optional": true, "description": "JavaScript stack trace for assertions and error messages." } + ] + }, + { + "id": "CallFrame", + "type": "object", + "description": "Stack entry for runtime errors and assertions.", + "properties": [ + { "name": "functionName", "type": "string", "description": "JavaScript function name." }, + { "name": "scriptId", "$ref": "ScriptId", "description": "JavaScript script id." }, + { "name": "url", "type": "string", "description": "JavaScript script name or url." }, + { "name": "lineNumber", "type": "integer", "description": "JavaScript script line number." }, + { "name": "columnNumber", "type": "integer", "description": "JavaScript script column number." } + ] + }, + { + "id": "StackTrace", + "type": "object", + "description": "Call frames for assertions or error messages.", + "properties": [ + { "name": "description", "type": "string", "optional": true, "description": "String label of this stack trace. For async traces this may be a name of the function that initiated the async call." }, + { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "JavaScript function name." }, + { "name": "parent", "$ref": "StackTrace", "optional": true, "hidden": true, "hidden": true, "description": "Asynchronous JavaScript stack trace that preceded this stack, if available." } + ] + } + ], + "commands": [ + { + "name": "evaluate", + "parameters": [ + { "name": "expression", "type": "string", "description": "Expression to evaluate." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, + { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation.", "hidden": true }, + { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether evaluation should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true }, + { "name": "contextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which isolated context to perform evaluation. Each content script lives in an isolated context and this parameter may be used to specify one of those contexts. If the parameter is omitted or 0 the evaluation will be performed in the context of the inspected page." }, + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." }, + { "name": "userGesture", "type": "boolean", "optional": true, "hidden": true, "description": "Whether execution should be treated as initiated by user in the UI." } + ], + "returns": [ + { "name": "result", "$ref": "RemoteObject", "description": "Evaluation result." }, + { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "hidden": true, "description": "Exception details."} + ], + "description": "Evaluates expression on global object." + }, + { + "name": "callFunctionOn", + "parameters": [ + { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to call function on." }, + { "name": "functionDeclaration", "type": "string", "description": "Declaration of the function to call." }, + { "name": "arguments", "type": "array", "items": { "$ref": "CallArgument", "description": "Call argument." }, "optional": true, "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target object." }, + { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether function call should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true }, + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." }, + { "name": "userGesture", "type": "boolean", "optional": true, "hidden": true, "description": "Whether execution should be treated as initiated by user in the UI." } + ], + "returns": [ + { "name": "result", "$ref": "RemoteObject", "description": "Call result." }, + { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." } + ], + "description": "Calls function with given declaration on the given object. Object group of the result is inherited from the target object." + }, + { + "name": "getProperties", + "parameters": [ + { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." }, + { "name": "ownProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging only to the element itself, not to its prototype chain." }, + { "name": "accessorPropertiesOnly", "optional": true, "type": "boolean", "description": "If true, returns accessor properties (with getter/setter) only; internal properties are not returned either.", "hidden": true }, + { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the results." } + ], + "returns": [ + { "name": "result", "type": "array", "items": { "$ref": "PropertyDescriptor" }, "description": "Object properties." }, + { "name": "internalProperties", "optional": true, "type": "array", "items": { "$ref": "InternalPropertyDescriptor" }, "description": "Internal object properties (only of the element itself).", "hidden": true }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "hidden": true, "description": "Exception details."} + ], + "description": "Returns properties of a given object. Object group of the result is inherited from the target object." + }, + { + "name": "releaseObject", + "parameters": [ + { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to release." } + ], + "description": "Releases remote object with given id." + }, + { + "name": "releaseObjectGroup", + "parameters": [ + { "name": "objectGroup", "type": "string", "description": "Symbolic object group name." } + ], + "description": "Releases all remote objects that belong to a given group." + }, + { + "name": "run", + "hidden": true, + "description": "Tells inspected instance(worker or page) that it can run in case it was started paused." + }, + { + "name": "enable", + "description": "Enables reporting of execution contexts creation by means of <code>executionContextCreated</code> event. When the reporting gets enabled the event will be sent immediately for each existing execution context." + }, + { + "name": "disable", + "hidden": true, + "description": "Disables reporting of execution contexts creation." + }, + { + "name": "setCustomObjectFormatterEnabled", + "parameters": [ + { + "name": "enabled", + "type": "boolean" + } + ], + "hidden": true + }, + { + "name": "compileScript", + "hidden": true, + "parameters": [ + { "name": "expression", "type": "string", "description": "Expression to compile." }, + { "name": "sourceURL", "type": "string", "description": "Source url to be set for the script." }, + { "name": "persistScript", "type": "boolean", "description": "Specifies whether the compiled script should be persisted." }, + { "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Specifies in which isolated context to perform script run. Each content script lives in an isolated context and this parameter is used to specify one of those contexts." } + ], + "returns": [ + { "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Id of the script." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} + ], + "description": "Compiles expression." + }, + { + "name": "runScript", + "hidden": true, + "parameters": [ + { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to run." }, + { "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Specifies in which isolated context to perform script run. Each content script lives in an isolated context and this parameter is used to specify one of those contexts." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, + { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether script run should stop on exceptions and mute console. Overrides setPauseOnException state." }, + { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." } + ], + "returns": [ + { "name": "result", "$ref": "RemoteObject", "description": "Run result." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} + ], + "description": "Runs script with given id in a given context." + } + ], + "events": [ + { + "name": "executionContextCreated", + "parameters": [ + { "name": "context", "$ref": "ExecutionContextDescription", "description": "A newly created execution contex." } + ], + "description": "Issued when new execution context is created." + }, + { + "name": "executionContextDestroyed", + "parameters": [ + { "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Id of the destroyed context" } + ], + "description": "Issued when execution context is destroyed." + }, + { + "name": "executionContextsCleared", + "description": "Issued when all executionContexts were cleared in browser" + }, + { + "name": "inspectRequested", + "parameters": [ + { "name": "object", "$ref": "RemoteObject" }, + { "name": "hints", "type": "object" } + ], + "hidden": true + } + ] + }, + { + "domain": "Debugger", + "description": "Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing breakpoints, stepping through execution, exploring stack traces, etc.", + "dependencies": ["Runtime"], + "types": [ + { + "id": "BreakpointId", + "type": "string", + "description": "Breakpoint identifier." + }, + { + "id": "CallFrameId", + "type": "string", + "description": "Call frame identifier." + }, + { + "id": "Location", + "type": "object", + "properties": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Script identifier as reported in the <code>Debugger.scriptParsed</code>." }, + { "name": "lineNumber", "type": "integer", "description": "Line number in the script (0-based)." }, + { "name": "columnNumber", "type": "integer", "optional": true, "description": "Column number in the script (0-based)." } + ], + "description": "Location in the source code." + }, + { + "id": "ScriptPosition", + "hidden": true, + "type": "object", + "properties": [ + { "name": "line", "type": "integer" }, + { "name": "column", "type": "integer" } + ], + "description": "Location in the source code." + }, + { + "id": "FunctionDetails", + "hidden": true, + "type": "object", + "properties": [ + { "name": "location", "$ref": "Location", "optional": true, "description": "Location of the function, none for native functions." }, + { "name": "functionName", "type": "string", "description": "Name of the function." }, + { "name": "isGenerator", "type": "boolean", "description": "Whether this is a generator function." }, + { "name": "scopeChain", "type": "array", "optional": true, "items": { "$ref": "Scope" }, "description": "Scope chain for this closure." } + ], + "description": "Information about the function." + }, + { + "id": "GeneratorObjectDetails", + "hidden": true, + "type": "object", + "properties": [ + { "name": "function", "$ref": "Runtime.RemoteObject", "description": "Generator function." }, + { "name": "functionName", "type": "string", "description": "Name of the generator function." }, + { "name": "status", "type": "string", "enum": ["running", "suspended", "closed"], "description": "Current generator object status." }, + { "name": "location", "$ref": "Location", "optional": true, "description": "If suspended, location where generator function was suspended (e.g. location of the last 'yield'). Otherwise, location of the generator function." } + ], + "description": "Information about the generator object." + }, + { + "id": "CollectionEntry", + "hidden": true, + "type": "object", + "properties": [ + { "name": "key", "$ref": "Runtime.RemoteObject", "optional": true, "description": "Entry key of a map-like collection, otherwise not provided." }, + { "name": "value", "$ref": "Runtime.RemoteObject", "description": "Entry value." } + ], + "description": "Collection entry." + }, + { + "id": "CallFrame", + "type": "object", + "properties": [ + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier. This identifier is only valid while the virtual machine is paused." }, + { "name": "functionName", "type": "string", "description": "Name of the JavaScript function called on this call frame." }, + { "name": "functionLocation", "$ref": "Location", "optional": true, "hidden": true, "description": "Location in the source code." }, + { "name": "location", "$ref": "Location", "description": "Location in the source code." }, + { "name": "scopeChain", "type": "array", "items": { "$ref": "Scope" }, "description": "Scope chain for this call frame." }, + { "name": "this", "$ref": "Runtime.RemoteObject", "description": "<code>this</code> object for this call frame." }, + { "name": "returnValue", "$ref": "Runtime.RemoteObject", "optional": true, "hidden": true, "description": "The value being returned, if the function is at return point." } + ], + "description": "JavaScript call frame. Array of call frames form the call stack." + }, + { + "id": "Scope", + "type": "object", + "properties": [ + { "name": "type", "type": "string", "enum": ["global", "local", "with", "closure", "catch", "block", "script"], "description": "Scope type." }, + { "name": "object", "$ref": "Runtime.RemoteObject", "description": "Object representing the scope. For <code>global</code> and <code>with</code> scopes it represents the actual object; for the rest of the scopes, it is artificial transient object enumerating scope variables as its properties." }, + { "name": "name", "type": "string", "optional": true, "hidden": true }, + { "name": "startLocation", "$ref": "Location", "optional": true, "hidden": true, "description": "Location in the source code where scope starts" }, + { "name": "endLocation", "$ref": "Location", "optional": true, "hidden": true, "description": "Location in the source code where scope ends" } + ], + "description": "Scope description." + }, + { + "id": "SetScriptSourceError", + "type": "object", + "properties": [ + { "name": "message", "type": "string", "description": "Compiler error message" }, + { "name": "lineNumber", "type": "integer", "description": "Compile error line number (1-based)" }, + { "name": "columnNumber", "type": "integer", "description": "Compile error column number (1-based)" } + ], + "description": "Error data for setScriptSource command. Contains uncompilable script source error.", + "hidden": true + }, + { + "id": "SearchMatch", + "type": "object", + "description": "Search match for resource.", + "properties": [ + { "name": "lineNumber", "type": "number", "description": "Line number in resource content." }, + { "name": "lineContent", "type": "string", "description": "Line with match content." } + ], + "hidden": true + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables debugger for the given page. Clients should not assume that the debugging has been enabled until the result for this command is received." + }, + { + "name": "disable", + "description": "Disables debugger for given page." + }, + { + "name": "setBreakpointsActive", + "parameters": [ + { "name": "active", "type": "boolean", "description": "New value for breakpoints active state." } + ], + "description": "Activates / deactivates all breakpoints on the page." + }, + { + "name": "setSkipAllPauses", + "hidden": true, + "parameters": [ + { "name": "skipped", "type": "boolean", "description": "New value for skip pauses state." } + ], + "description": "Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc)." + }, + { + "name": "setBreakpointByUrl", + "parameters": [ + { "name": "lineNumber", "type": "integer", "description": "Line number to set breakpoint at." }, + { "name": "url", "type": "string", "optional": true, "description": "URL of the resources to set breakpoint on." }, + { "name": "urlRegex", "type": "string", "optional": true, "description": "Regex pattern for the URLs of the resources to set breakpoints on. Either <code>url</code> or <code>urlRegex</code> must be specified." }, + { "name": "columnNumber", "type": "integer", "optional": true, "description": "Offset in the line to set breakpoint at." }, + { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." } + ], + "returns": [ + { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, + { "name": "locations", "type": "array", "items": { "$ref": "Location" }, "description": "List of the locations this breakpoint resolved into upon addition." } + ], + "description": "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this command is issued, all existing parsed scripts will have breakpoints resolved and returned in <code>locations</code> property. Further matching script parsing will result in subsequent <code>breakpointResolved</code> events issued. This logical breakpoint will survive page reloads." + }, + { + "name": "setBreakpoint", + "parameters": [ + { "name": "location", "$ref": "Location", "description": "Location to set breakpoint in." }, + { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." } + ], + "returns": [ + { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, + { "name": "actualLocation", "$ref": "Location", "description": "Location this breakpoint resolved into." } + ], + "description": "Sets JavaScript breakpoint at a given location." + }, + { + "name": "removeBreakpoint", + "parameters": [ + { "name": "breakpointId", "$ref": "BreakpointId" } + ], + "description": "Removes JavaScript breakpoint." + }, + { + "name": "continueToLocation", + "parameters": [ + { "name": "location", "$ref": "Location", "description": "Location to continue to." }, + { "name": "interstatementLocation", "type": "boolean", "optional": true, "hidden": true, "description": "Allows breakpoints at the intemediate positions inside statements." } + ], + "description": "Continues execution until specific location is reached." + }, + { + "name": "stepOver", + "description": "Steps over the statement." + }, + { + "name": "stepInto", + "description": "Steps into the function call." + }, + { + "name": "stepOut", + "description": "Steps out of the function call." + }, + { + "name": "pause", + "description": "Stops on the next JavaScript statement." + }, + { + "name": "resume", + "description": "Resumes JavaScript execution." + }, + { + "name": "searchInContent", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to search in." }, + { "name": "query", "type": "string", "description": "String to search for." }, + { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." }, + { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." } + ], + "returns": [ + { "name": "result", "type": "array", "items": { "$ref": "SearchMatch" }, "description": "List of search matches." } + ], + "description": "Searches for given string in script content." + }, + { + "name": "canSetScriptSource", + "returns": [ + { "name": "result", "type": "boolean", "description": "True if <code>setScriptSource</code> is supported." } + ], + "description": "Always returns true." + }, + { + "name": "setScriptSource", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to edit." }, + { "name": "scriptSource", "type": "string", "description": "New content of the script." }, + { "name": "preview", "type": "boolean", "optional": true, "description": " If true the change will not actually be applied. Preview mode may be used to get result description without actually modifying the code.", "hidden": true } + ], + "returns": [ + { "name": "callFrames", "type": "array", "optional": true, "items": { "$ref": "CallFrame" }, "description": "New stack trace in case editing has happened while VM was stopped." }, + { "name": "stackChanged", "type": "boolean", "optional": true, "description": "Whether current call stack was modified after applying the changes.", "hidden": true }, + { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any.", "hidden": true }, + { "name": "compileError", "optional": true, "$ref": "SetScriptSourceError", "description": "Error data if any." } + ], + "description": "Edits JavaScript source live." + }, + { + "name": "restartFrame", + "parameters": [ + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." } + ], + "returns": [ + { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "New stack trace." }, + { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." } + ], + "hidden": true, + "description": "Restarts particular call frame from the beginning." + }, + { + "name": "getScriptSource", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to get source for." } + ], + "returns": [ + { "name": "scriptSource", "type": "string", "description": "Script source." } + ], + "description": "Returns source for the script with given id." + }, + { + "name": "getFunctionDetails", + "hidden": true, + "parameters": [ + { "name": "functionId", "$ref": "Runtime.RemoteObjectId", "description": "Id of the function to get details for." } + ], + "returns": [ + { "name": "details", "$ref": "FunctionDetails", "description": "Information about the function." } + ], + "description": "Returns detailed information on given function." + }, + { + "name": "getGeneratorObjectDetails", + "hidden": true, + "parameters": [ + { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Id of the generator object to get details for." } + ], + "returns": [ + { "name": "details", "$ref": "GeneratorObjectDetails", "description": "Information about the generator object." } + ], + "description": "Returns detailed information on given generator object." + }, + { + "name": "getCollectionEntries", + "hidden": true, + "parameters": [ + { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Id of the collection to get entries for." } + ], + "returns": [ + { "name": "entries", "type": "array", "items": { "$ref": "CollectionEntry" }, "description": "Array of collection entries." } + ], + "description": "Returns entries of given collection." + }, + { + "name": "setPauseOnExceptions", + "parameters": [ + { "name": "state", "type": "string", "enum": ["none", "uncaught", "all"], "description": "Pause on exceptions mode." } + ], + "description": "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions or no exceptions. Initial pause on exceptions state is <code>none</code>." + }, + { + "name": "evaluateOnCallFrame", + "parameters": [ + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." }, + { "name": "expression", "type": "string", "description": "Expression to evaluate." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "String object group name to put result into (allows rapid releasing resulting object handles using <code>releaseObjectGroup</code>)." }, + { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false.", "hidden": true }, + { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether evaluation should stop on exceptions and mute console. Overrides setPauseOnException state.", "hidden": true }, + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "hidden": true, "description": "Whether preview should be generated for the result." } + ], + "returns": [ + { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." }, + { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }, + { "name": "exceptionDetails", "$ref": "Runtime.ExceptionDetails", "optional": true, "hidden": true, "description": "Exception details."} + ], + "description": "Evaluates expression on a given call frame." + }, + { + "name": "setVariableValue", + "parameters": [ + { "name": "scopeNumber", "type": "integer", "description": "0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch' scope types are allowed. Other scopes could be manipulated manually." }, + { "name": "variableName", "type": "string", "description": "Variable name." }, + { "name": "newValue", "$ref": "Runtime.CallArgument", "description": "New variable value." }, + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Id of callframe that holds variable." } + ], + "hidden": true, + "description": "Changes value of variable in a callframe. Object-based scopes are not supported and must be mutated manually." + }, + { + "name": "getBacktrace", + "returns": [ + { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." }, + { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." } + ], + "hidden": true, + "description": "Returns call stack including variables changed since VM was paused. VM must be paused." + }, + { + "name": "setAsyncCallStackDepth", + "parameters": [ + { "name": "maxDepth", "type": "integer", "description": "Maximum depth of async call stacks. Setting to <code>0</code> will effectively disable collecting async call stacks (default)." } + ], + "hidden": true, + "description": "Enables or disables async call stacks tracking." + }, + { + "name": "setBlackboxPatterns", + "parameters": [ + { "name": "patterns", "type": "array", "items": { "type": "string" }, "description": "Array of regexps that will be used to check script url for blackbox state." } + ], + "hidden": true, + "description": "Replace previous blackbox patterns with passed ones. Forces backend to skip stepping/pausing in scripts with url matching one of the patterns. VM will try to leave blackboxed script by performing 'step in' several times, finally resorting to 'step out' if unsuccessful." + }, + { + "name": "setBlackboxedRanges", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script." }, + { "name": "positions", "type": "array", "items": { "$ref": "ScriptPosition" } } + ], + "hidden": true, + "description": "Makes backend skip steps in the script in blackboxed ranges. VM will try leave blacklisted scripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful. Positions array contains positions where blackbox state is changed. First interval isn't blackboxed. Array should be sorted." + } + ], + "events": [ + { + "name": "scriptParsed", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." }, + { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, + { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, + { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, + { "name": "endLine", "type": "integer", "description": "Last line of the script." }, + { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, + { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context.", "hidden": true }, + { "name": "hash", "type": "string", "hidden": true, "description": "Content hash of the script."}, + { "name": "isContentScript", "type": "boolean", "optional": true, "description": "Determines whether this script is a user extension script." }, + { "name": "isInternalScript", "type": "boolean", "optional": true, "description": "Determines whether this script is an internal script.", "hidden": true }, + { "name": "isLiveEdit", "type": "boolean", "optional": true, "description": "True, if this script is generated as a result of the live edit operation.", "hidden": true }, + { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, + { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "hidden": true }, + { "name": "deprecatedCommentWasUsed", "type": "boolean", "optional": true, "hidden": true, "description": "True, if '//@ sourceURL' or '//@ sourceMappingURL' was used."} + ], + "description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger." + }, + { + "name": "scriptFailedToParse", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." }, + { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, + { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, + { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, + { "name": "endLine", "type": "integer", "description": "Last line of the script." }, + { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, + { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context.", "hidden": true }, + { "name": "hash", "type": "string", "hidden": true, "description": "Content hash of the script."}, + { "name": "isContentScript", "type": "boolean", "optional": true, "description": "Determines whether this script is a user extension script." }, + { "name": "isInternalScript", "type": "boolean", "optional": true, "description": "Determines whether this script is an internal script.", "hidden": true }, + { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, + { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "hidden": true }, + { "name": "deprecatedCommentWasUsed", "type": "boolean", "optional": true, "hidden": true, "description": "True, if '//@ sourceURL' or '//@ sourceMappingURL' was used."} + ], + "description": "Fired when virtual machine fails to parse the script." + }, + { + "name": "breakpointResolved", + "parameters": [ + { "name": "breakpointId", "$ref": "BreakpointId", "description": "Breakpoint unique identifier." }, + { "name": "location", "$ref": "Location", "description": "Actual breakpoint location." } + ], + "description": "Fired when breakpoint is resolved to an actual script and location." + }, + { + "name": "paused", + "parameters": [ + { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." }, + { "name": "reason", "type": "string", "enum": [ "XHR", "DOM", "EventListener", "exception", "assert", "CSPViolation", "debugCommand", "promiseRejection", "other" ], "description": "Pause reason." }, + { "name": "data", "type": "object", "optional": true, "description": "Object containing break-specific auxiliary properties." }, + { "name": "hitBreakpoints", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Hit breakpoints IDs", "hidden": true }, + { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any.", "hidden": true } + ], + "description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria." + }, + { + "name": "resumed", + "description": "Fired when the virtual machine resumed execution." + } + ] + }, + { + "domain": "Profiler", + "dependencies": ["Runtime", "Debugger"], + "hidden": true, + "types": [ + { + "id": "CPUProfileNode", + "type": "object", + "description": "CPU Profile node. Holds callsite information, execution statistics and child nodes.", + "properties": [ + { "name": "functionName", "type": "string", "description": "Function name." }, + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Script identifier." }, + { "name": "url", "type": "string", "description": "URL." }, + { "name": "lineNumber", "type": "integer", "description": "1-based line number of the function start position." }, + { "name": "columnNumber", "type": "integer", "description": "1-based column number of the function start position." }, + { "name": "hitCount", "type": "integer", "description": "Number of samples where this node was on top of the call stack." }, + { "name": "callUID", "type": "number", "description": "Call UID." }, + { "name": "children", "type": "array", "items": { "$ref": "CPUProfileNode" }, "description": "Child nodes." }, + { "name": "deoptReason", "type": "string", "description": "The reason of being not optimized. The function may be deoptimized or marked as don't optimize."}, + { "name": "id", "type": "integer", "description": "Unique id of the node." }, + { "name": "positionTicks", "type": "array", "items": { "$ref": "PositionTickInfo" }, "description": "An array of source position ticks." } + ] + }, + { + "id": "CPUProfile", + "type": "object", + "description": "Profile.", + "properties": [ + { "name": "head", "$ref": "CPUProfileNode" }, + { "name": "startTime", "type": "number", "description": "Profiling start time in seconds." }, + { "name": "endTime", "type": "number", "description": "Profiling end time in seconds." }, + { "name": "samples", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Ids of samples top nodes." }, + { "name": "timestamps", "optional": true, "type": "array", "items": { "type": "number" }, "description": "Timestamps of the samples in microseconds." } + ] + }, + { + "id": "PositionTickInfo", + "type": "object", + "description": "Specifies a number of samples attributed to a certain source position.", + "properties": [ + { "name": "line", "type": "integer", "description": "Source line number (1-based)." }, + { "name": "ticks", "type": "integer", "description": "Number of samples attributed to the source line." } + ] + } + ], + "commands": [ + { + "name": "enable" + }, + { + "name": "disable" + }, + { + "name": "setSamplingInterval", + "parameters": [ + { "name": "interval", "type": "integer", "description": "New sampling interval in microseconds." } + ], + "description": "Changes CPU profiler sampling interval. Must be called before CPU profiles recording started." + }, + { + "name": "start" + }, + { + "name": "stop", + "returns": [ + { "name": "profile", "$ref": "CPUProfile", "description": "Recorded profile." } + ] + } + ], + "events": [ + { + "name": "consoleProfileStarted", + "parameters": [ + { "name": "id", "type": "string" }, + { "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profile()." }, + { "name": "title", "type": "string", "optional": true, "description": "Profile title passed as argument to console.profile()." } + + ], + "description": "Sent when new profile recodring is started using console.profile() call." + }, + { + "name": "consoleProfileFinished", + "parameters": [ + { "name": "id", "type": "string" }, + { "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profileEnd()." }, + { "name": "profile", "$ref": "CPUProfile" }, + { "name": "title", "type": "string", "optional": true, "description": "Profile title passed as argunet to console.profile()." } + ] + } + ] + }, + { + "domain": "HeapProfiler", + "dependencies": ["Runtime"], + "hidden": true, + "types": [ + { + "id": "HeapSnapshotObjectId", + "type": "string", + "description": "Heap snapshot object id." + }, + { + "id": "SamplingHeapProfileNode", + "type": "object", + "description": "Sampling Heap Profile node. Holds callsite information, allocation statistics and child nodes.", + "properties": [ + { "name": "functionName", "type": "string", "description": "Function name." }, + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Script identifier." }, + { "name": "url", "type": "string", "description": "URL." }, + { "name": "lineNumber", "type": "integer", "description": "1-based line number of the function start position." }, + { "name": "columnNumber", "type": "integer", "description": "1-based column number of the function start position." }, + { "name": "selfSize", "type": "number", "description": "Allocations size in bytes for the node excluding children." }, + { "name": "children", "type": "array", "items": { "$ref": "SamplingHeapProfileNode" }, "description": "Child nodes." } + ] + }, + { + "id": "SamplingHeapProfile", + "type": "object", + "description": "Profile.", + "properties": [ + { "name": "head", "$ref": "SamplingHeapProfileNode" } + ] + } + ], + "commands": [ + { + "name": "enable" + }, + { + "name": "disable" + }, + { + "name": "startTrackingHeapObjects", + "parameters": [ + { "name": "trackAllocations", "type": "boolean", "optional": true } + ] + }, + { + "name": "stopTrackingHeapObjects", + "parameters": [ + { "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken when the tracking is stopped." } + ] + + }, + { + "name": "takeHeapSnapshot", + "parameters": [ + { "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken." } + ] + }, + { + "name": "collectGarbage" + }, + { + "name": "getObjectByHeapObjectId", + "parameters": [ + { "name": "objectId", "$ref": "HeapSnapshotObjectId" }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." } + ], + "returns": [ + { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Evaluation result." } + ] + }, + { + "name": "addInspectedHeapObject", + "parameters": [ + { "name": "heapObjectId", "$ref": "HeapSnapshotObjectId", "description": "Heap snapshot object id to be accessible by means of $x command line API." } + ], + "description": "Enables console to refer to the node with given id via $x (see Command Line API for more details $x functions)." + }, + { + "name": "getHeapObjectId", + "parameters": [ + { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Identifier of the object to get heap object id for." } + ], + "returns": [ + { "name": "heapSnapshotObjectId", "$ref": "HeapSnapshotObjectId", "description": "Id of the heap snapshot object corresponding to the passed remote object id." } + ] + }, + { + "name": "startSampling", + "parameters": [ + { "name": "samplingInterval", "type": "number", "optional": true, "description": "Average sample interval in bytes. Poisson distribution is used for the intervals. The default value is 32768 bytes." } + ] + }, + { + "name": "stopSampling", + "returns": [ + { "name": "profile", "$ref": "SamplingHeapProfile", "description": "Recorded sampling heap profile." } + ] + } + ], + "events": [ + { + "name": "addHeapSnapshotChunk", + "parameters": [ + { "name": "chunk", "type": "string" } + ] + }, + { + "name": "resetProfiles" + }, + { + "name": "reportHeapSnapshotProgress", + "parameters": [ + { "name": "done", "type": "integer" }, + { "name": "total", "type": "integer" }, + { "name": "finished", "type": "boolean", "optional": true } + ] + }, + { + "name": "lastSeenObjectId", + "description": "If heap objects tracking has been started then backend regulary sends a current value for last seen object id and corresponding timestamp. If the were changes in the heap since last event then one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event.", + "parameters": [ + { "name": "lastSeenObjectId", "type": "integer" }, + { "name": "timestamp", "type": "number" } + ] + }, + { + "name": "heapStatsUpdate", + "description": "If heap objects tracking has been started then backend may send update for one or more fragments", + "parameters": [ + { "name": "statsUpdate", "type": "array", "items": { "type": "integer" }, "description": "An array of triplets. Each triplet describes a fragment. The first integer is the fragment index, the second integer is a total count of objects for the fragment, the third integer is a total size of the objects for the fragment."} + ] + } + ] + }] +} diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8ContentSearchUtil.h b/deps/v8_inspector/platform/v8_inspector/public/V8ContentSearchUtil.h index 565fc9673de83f..35cf12331f88c9 100644 --- a/deps/v8_inspector/platform/v8_inspector/public/V8ContentSearchUtil.h +++ b/deps/v8_inspector/platform/v8_inspector/public/V8ContentSearchUtil.h @@ -5,9 +5,8 @@ #ifndef V8ContentSearchUtil_h #define V8ContentSearchUtil_h -#include "platform/PlatformExport.h" +#include "platform/inspector_protocol/Platform.h" #include "platform/inspector_protocol/String16.h" -#include "platform/inspector_protocol/TypeBuilder.h" namespace blink { diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8Debugger.h b/deps/v8_inspector/platform/v8_inspector/public/V8Debugger.h index c4fa6cfd7823a5..b605d8a15e9fa4 100644 --- a/deps/v8_inspector/platform/v8_inspector/public/V8Debugger.h +++ b/deps/v8_inspector/platform/v8_inspector/public/V8Debugger.h @@ -5,9 +5,8 @@ #ifndef V8Debugger_h #define V8Debugger_h -#include "platform/PlatformExport.h" -#include "platform/inspector_protocol/Frontend.h" -#include "wtf/PtrUtil.h" +#include "platform/inspector_protocol/Platform.h" +#include "platform/inspector_protocol/String16.h" #include <v8.h> @@ -16,23 +15,16 @@ namespace blink { class V8ContextInfo; class V8DebuggerClient; class V8InspectorSession; +class V8InspectorSessionClient; class V8StackTrace; namespace protocol { class DictionaryValue; +class FrontendChannel; } class PLATFORM_EXPORT V8Debugger { public: - template <typename T> - class Agent { - public: - virtual void setInspectorState(protocol::DictionaryValue*) = 0; - virtual void setFrontend(T*) = 0; - virtual void clearFrontend() = 0; - virtual void restore() = 0; - }; - static std::unique_ptr<V8Debugger> create(v8::Isolate*, V8DebuggerClient*); virtual ~V8Debugger() { } @@ -48,14 +40,10 @@ class PLATFORM_EXPORT V8Debugger { virtual void idleStarted() = 0; virtual void idleFinished() = 0; - virtual std::unique_ptr<V8InspectorSession> connect(int contextGroupId) = 0; + virtual std::unique_ptr<V8InspectorSession> connect(int contextGroupId, protocol::FrontendChannel*, V8InspectorSessionClient*, const String16* state) = 0; virtual bool isPaused() = 0; - static v8::Local<v8::Private> scopeExtensionPrivate(v8::Isolate*); - static bool isCommandLineAPIMethod(const String16& name); - static bool isCommandLineAPIGetter(const String16& name); - - virtual std::unique_ptr<V8StackTrace> createStackTrace(v8::Local<v8::StackTrace>, size_t maxStackSize) = 0; + virtual std::unique_ptr<V8StackTrace> createStackTrace(v8::Local<v8::StackTrace>) = 0; virtual std::unique_ptr<V8StackTrace> captureStackTrace(size_t maxStackSize) = 0; }; diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8DebuggerAgent.h b/deps/v8_inspector/platform/v8_inspector/public/V8DebuggerAgent.h deleted file mode 100644 index 6a18e62fca9a4c..00000000000000 --- a/deps/v8_inspector/platform/v8_inspector/public/V8DebuggerAgent.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef V8DebuggerAgent_h -#define V8DebuggerAgent_h - -#include "platform/PlatformExport.h" -#include "platform/inspector_protocol/Dispatcher.h" -#include "platform/v8_inspector/public/V8Debugger.h" - -namespace blink { - -class V8RuntimeAgent; - -class PLATFORM_EXPORT V8DebuggerAgent : public protocol::Backend::Debugger, public V8Debugger::Agent<protocol::Frontend::Debugger> { -public: - virtual ~V8DebuggerAgent() { } -}; - -} // namespace blink - - -#endif // V8DebuggerAgent_h diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8DebuggerClient.h b/deps/v8_inspector/platform/v8_inspector/public/V8DebuggerClient.h index 29b041ce293ca8..e927d4cc664c81 100644 --- a/deps/v8_inspector/platform/v8_inspector/public/V8DebuggerClient.h +++ b/deps/v8_inspector/platform/v8_inspector/public/V8DebuggerClient.h @@ -5,11 +5,10 @@ #ifndef V8DebuggerClient_h #define V8DebuggerClient_h -#include "platform/PlatformExport.h" +#include "platform/inspector_protocol/Platform.h" #include "platform/v8_inspector/public/ConsoleAPITypes.h" #include "platform/v8_inspector/public/ConsoleTypes.h" #include "platform/v8_inspector/public/V8ContextInfo.h" -#include "platform/v8_inspector/public/V8EventListenerInfo.h" #include <v8.h> @@ -26,7 +25,6 @@ class PLATFORM_EXPORT V8DebuggerClient { virtual void unmuteConsole() = 0; virtual void beginUserGesture() = 0; virtual void endUserGesture() = 0; - virtual void eventListeners(v8::Local<v8::Value>, V8EventListenerInfoList&) = 0; virtual bool callingContextCanAccessContext(v8::Local<v8::Context> calling, v8::Local<v8::Context> target) = 0; virtual String16 valueSubtype(v8::Local<v8::Value>) = 0; virtual bool formatAccessorsAsProperties(v8::Local<v8::Value>) = 0; @@ -35,13 +33,14 @@ class PLATFORM_EXPORT V8DebuggerClient { virtual int ensureDefaultContextInGroup(int contextGroupId) = 0; virtual bool isInspectableHeapObject(v8::Local<v8::Object>) = 0; - virtual void reportMessageToConsole(v8::Local<v8::Context>, MessageType, MessageLevel, const String16& message, const v8::FunctionCallbackInfo<v8::Value>* arguments, unsigned skipArgumentCount, int maxStackSize) = 0; + virtual void installAdditionalCommandLineAPI(v8::Local<v8::Context>, v8::Local<v8::Object>) = 0; + virtual void reportMessageToConsole(v8::Local<v8::Context>, MessageType, MessageLevel, const String16& message, const v8::FunctionCallbackInfo<v8::Value>* arguments, unsigned skipArgumentCount) = 0; virtual void consoleTime(const String16& title) = 0; virtual void consoleTimeEnd(const String16& title) = 0; virtual void consoleTimeStamp(const String16& title) = 0; - virtual v8::MaybeLocal<v8::Value> memoryInfo(v8::Isolate*, v8::Local<v8::Context>, v8::Local<v8::Object> creationContext) = 0; + virtual v8::MaybeLocal<v8::Value> memoryInfo(v8::Isolate*, v8::Local<v8::Context>) = 0; typedef void (*TimerCallback)(void*); virtual void startRepeatingTimer(double, TimerCallback, void* data) = 0; diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8EventListenerInfo.h b/deps/v8_inspector/platform/v8_inspector/public/V8EventListenerInfo.h deleted file mode 100644 index 03370be8da27a0..00000000000000 --- a/deps/v8_inspector/platform/v8_inspector/public/V8EventListenerInfo.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef V8EventListenerInfo_h -#define V8EventListenerInfo_h - -#include "platform/inspector_protocol/Collections.h" -#include "platform/inspector_protocol/String16.h" - -#include <v8.h> - -namespace blink { - -class V8EventListenerInfo { -public: - V8EventListenerInfo(const String16& eventType, bool useCapture, bool passive, v8::Local<v8::Object> handler) - : eventType(eventType) - , useCapture(useCapture) - , passive(passive) - , handler(handler) - { - } - - const String16 eventType; - bool useCapture; - bool passive; - v8::Local<v8::Object> handler; - -}; - -using V8EventListenerInfoList = protocol::Vector<V8EventListenerInfo>; - -} // namespace blink - -#endif // V8EventListenerInfo_h diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8HeapProfilerAgent.h b/deps/v8_inspector/platform/v8_inspector/public/V8HeapProfilerAgent.h deleted file mode 100644 index ac3f522a3fe562..00000000000000 --- a/deps/v8_inspector/platform/v8_inspector/public/V8HeapProfilerAgent.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef V8HeapProfilerAgent_h -#define V8HeapProfilerAgent_h - -#include "platform/PlatformExport.h" -#include "platform/inspector_protocol/Dispatcher.h" -#include "platform/v8_inspector/public/V8Debugger.h" - -namespace blink { - -class V8RuntimeAgent; - -class PLATFORM_EXPORT V8HeapProfilerAgent : public protocol::Backend::HeapProfiler, public V8Debugger::Agent<protocol::Frontend::HeapProfiler> { -public: - virtual ~V8HeapProfilerAgent() { } -}; - -} // namespace blink - -#endif // !defined(V8HeapProfilerAgent_h) diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8Inspector.cpp b/deps/v8_inspector/platform/v8_inspector/public/V8Inspector.cpp index 0ab85c7340e974..fadab16d7ebd1e 100644 --- a/deps/v8_inspector/platform/v8_inspector/public/V8Inspector.cpp +++ b/deps/v8_inspector/platform/v8_inspector/public/V8Inspector.cpp @@ -5,33 +5,16 @@ #include "platform/v8_inspector/public/V8Inspector.h" -#include "platform/inspector_protocol/Dispatcher.h" +#include "platform/inspector_protocol/DispatcherBase.h" #include "platform/v8_inspector/V8StringUtil.h" #include "platform/v8_inspector/public/V8Debugger.h" -#include "platform/v8_inspector/public/V8DebuggerAgent.h" #include "platform/v8_inspector/public/V8DebuggerClient.h" -#include "platform/v8_inspector/public/V8HeapProfilerAgent.h" -#include "platform/v8_inspector/public/V8ProfilerAgent.h" -#include "platform/v8_inspector/public/V8RuntimeAgent.h" namespace blink { V8Inspector::V8Inspector(v8::Isolate* isolate, v8::Local<v8::Context> context) { m_debugger = V8Debugger::create(isolate, this); - m_session = m_debugger->connect(1); - m_session->setClient(this); - m_state = protocol::DictionaryValue::create(); - m_state->setValue("runtime", protocol::DictionaryValue::create()); - m_state->setValue("debugger", protocol::DictionaryValue::create()); - m_state->setValue("profiler", protocol::DictionaryValue::create()); - m_state->setValue("heapProfiler", protocol::DictionaryValue::create()); - - m_session->runtimeAgent()->setInspectorState(m_state->getObject("runtime")); - m_session->debuggerAgent()->setInspectorState(m_state->getObject("debugger")); - m_session->profilerAgent()->setInspectorState(m_state->getObject("profiler")); - m_session->heapProfilerAgent()->setInspectorState(m_state->getObject("heapProfiler")); - m_debugger->contextCreated(V8ContextInfo(context, 1, true, "", "NodeJS Main Context", "", false)); } @@ -41,10 +24,6 @@ V8Inspector::~V8Inspector() disconnectFrontend(); } -void V8Inspector::eventListeners(v8::Local<v8::Value> value, V8EventListenerInfoList& result) -{ -} - bool V8Inspector::callingContextCanAccessContext(v8::Local<v8::Context> calling, v8::Local<v8::Context> target) { return true; @@ -62,44 +41,18 @@ bool V8Inspector::formatAccessorsAsProperties(v8::Local<v8::Value> value) void V8Inspector::connectFrontend(protocol::FrontendChannel* channel) { - DCHECK(!m_frontend); - m_frontend = wrapUnique(new protocol::Frontend(channel)); - m_dispatcher = protocol::Dispatcher::create(channel); - - m_dispatcher->registerAgent((protocol::Backend::Debugger*)m_session->debuggerAgent()); - m_dispatcher->registerAgent(m_session->heapProfilerAgent()); - m_dispatcher->registerAgent(m_session->profilerAgent()); - m_dispatcher->registerAgent(m_session->runtimeAgent()); - - m_session->debuggerAgent()->setFrontend( - protocol::Frontend::Debugger::from(m_frontend.get())); - m_session->heapProfilerAgent()->setFrontend( - protocol::Frontend::HeapProfiler::from(m_frontend.get())); - m_session->profilerAgent()->setFrontend( - protocol::Frontend::Profiler::from(m_frontend.get())); - m_session->runtimeAgent()->setFrontend( - protocol::Frontend::Runtime::from(m_frontend.get())); + m_session = m_debugger->connect(1, channel, this, &m_state); } void V8Inspector::disconnectFrontend() { - if (!m_frontend) - return; - m_dispatcher->clearFrontend(); - m_dispatcher.reset(); - - m_session->debuggerAgent()->clearFrontend(); - m_session->heapProfilerAgent()->clearFrontend(); - m_session->profilerAgent()->clearFrontend(); - m_session->runtimeAgent()->clearFrontend(); - - m_frontend.reset(); + m_session.reset(); } void V8Inspector::dispatchMessageFromFrontend(const String16& message) { - if (m_dispatcher) - m_dispatcher->dispatch(1, message); + if (m_session) + m_session->dispatchProtocolMessage(message); } int V8Inspector::ensureDefaultContextInGroup(int contextGroupId) @@ -109,7 +62,6 @@ int V8Inspector::ensureDefaultContextInGroup(int contextGroupId) void V8Inspector::muteConsole() { - } void V8Inspector::unmuteConsole() diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8Inspector.h b/deps/v8_inspector/platform/v8_inspector/public/V8Inspector.h index 33763014a86bae..38bbc88774cf9c 100644 --- a/deps/v8_inspector/platform/v8_inspector/public/V8Inspector.h +++ b/deps/v8_inspector/platform/v8_inspector/public/V8Inspector.h @@ -5,11 +5,11 @@ #ifndef V8Inspector_h #define V8Inspector_h +#include "platform/inspector_protocol/Platform.h" #include "platform/v8_inspector/public/V8DebuggerClient.h" -#include "platform/v8_inspector/public/V8InspectorSessionClient.h" #include "platform/v8_inspector/public/V8InspectorSession.h" +#include "platform/v8_inspector/public/V8InspectorSessionClient.h" -#include "wtf/PtrUtil.h" #include <v8.h> namespace blink { @@ -35,7 +35,6 @@ class V8Inspector : public V8DebuggerClient, V8InspectorSessionClient { void dispatchMessageFromFrontend(const String16& message); private: - void eventListeners(v8::Local<v8::Value>, V8EventListenerInfoList&) override; bool callingContextCanAccessContext(v8::Local<v8::Context> calling, v8::Local<v8::Context> target) override; String16 valueSubtype(v8::Local<v8::Value>) override; bool formatAccessorsAsProperties(v8::Local<v8::Value>) override; @@ -53,11 +52,12 @@ class V8Inspector : public V8DebuggerClient, V8InspectorSessionClient { void consoleTime(const String16& title) override { } void consoleTimeEnd(const String16& title) override { } void consoleTimeStamp(const String16& title) override { } - v8::MaybeLocal<v8::Value> memoryInfo(v8::Isolate*, v8::Local<v8::Context>, v8::Local<v8::Object> creationContext) override + v8::MaybeLocal<v8::Value> memoryInfo(v8::Isolate*, v8::Local<v8::Context>) override { return v8::MaybeLocal<v8::Value>(); } - void reportMessageToConsole(v8::Local<v8::Context>, MessageType, MessageLevel, const String16& message, const v8::FunctionCallbackInfo<v8::Value>* arguments, unsigned skipArgumentCount, int maxStackSize) override { } + void reportMessageToConsole(v8::Local<v8::Context>, MessageType, MessageLevel, const String16& message, const v8::FunctionCallbackInfo<v8::Value>* arguments, unsigned skipArgumentCount) override { } + void installAdditionalCommandLineAPI(v8::Local<v8::Context>, v8::Local<v8::Object>) override { } // V8InspectorSessionClient void startInstrumenting() override { } @@ -71,9 +71,7 @@ class V8Inspector : public V8DebuggerClient, V8InspectorSessionClient { std::unique_ptr<V8Debugger> m_debugger; std::unique_ptr<V8InspectorSession> m_session; - std::unique_ptr<protocol::Dispatcher> m_dispatcher; - std::unique_ptr<protocol::Frontend> m_frontend; - std::unique_ptr<protocol::DictionaryValue> m_state; + String16 m_state; }; } diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8InspectorSession.h b/deps/v8_inspector/platform/v8_inspector/public/V8InspectorSession.h index dfb9ff6386a9b7..e9ad175afc830b 100644 --- a/deps/v8_inspector/platform/v8_inspector/public/V8InspectorSession.h +++ b/deps/v8_inspector/platform/v8_inspector/public/V8InspectorSession.h @@ -5,9 +5,8 @@ #ifndef V8InspectorSession_h #define V8InspectorSession_h -#include "platform/PlatformExport.h" -#include "platform/inspector_protocol/TypeBuilder.h" -#include "wtf/PtrUtil.h" +#include "platform/inspector_protocol/Platform.h" +#include "platform/v8_inspector/protocol/Runtime.h" #include <v8.h> @@ -32,7 +31,9 @@ class PLATFORM_EXPORT V8InspectorSession { virtual ~V8InspectorSession() { } - virtual void setClient(V8InspectorSessionClient*) = 0; + static bool isV8ProtocolMethod(const String16& method); + virtual void dispatchProtocolMessage(const String16& message) = 0; + virtual String16 stateJSON() = 0; virtual void addInspectedObject(std::unique_ptr<Inspectable>) = 0; // API for the embedder to report native activities. @@ -41,6 +42,8 @@ class PLATFORM_EXPORT V8InspectorSession { virtual void breakProgram(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data) = 0; virtual void breakProgramOnException(const String16& breakReason, std::unique_ptr<protocol::DictionaryValue> data) = 0; virtual void setSkipAllPauses(bool) = 0; + virtual void resume() = 0; + virtual void stepOver() = 0; // API to report async call stacks. virtual void asyncTaskScheduled(const String16& taskName, void* task, bool recurring) = 0; @@ -55,11 +58,6 @@ class PLATFORM_EXPORT V8InspectorSession { virtual std::unique_ptr<protocol::Runtime::RemoteObject> wrapTable(v8::Local<v8::Context>, v8::Local<v8::Value> table, v8::Local<v8::Value> columns) = 0; virtual v8::Local<v8::Value> findObject(ErrorString*, const String16& objectId, v8::Local<v8::Context>* = nullptr, String16* objectGroup = nullptr) = 0; virtual void releaseObjectGroup(const String16&) = 0; - - virtual V8DebuggerAgent* debuggerAgent() = 0; - virtual V8HeapProfilerAgent* heapProfilerAgent() = 0; - virtual V8ProfilerAgent* profilerAgent() = 0; - virtual V8RuntimeAgent* runtimeAgent() = 0; }; } // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8InspectorSessionClient.h b/deps/v8_inspector/platform/v8_inspector/public/V8InspectorSessionClient.h index e4775ffc16085c..87772b3cca241e 100644 --- a/deps/v8_inspector/platform/v8_inspector/public/V8InspectorSessionClient.h +++ b/deps/v8_inspector/platform/v8_inspector/public/V8InspectorSessionClient.h @@ -5,13 +5,15 @@ #ifndef V8InspectorSessionClient_h #define V8InspectorSessionClient_h -#include "platform/PlatformExport.h" +#include "platform/inspector_protocol/FrontendChannel.h" +#include "platform/inspector_protocol/Platform.h" #include <v8.h> namespace blink { -class PLATFORM_EXPORT V8InspectorSessionClient { +class PLATFORM_EXPORT V8InspectorSessionClient +{ public: virtual ~V8InspectorSessionClient() { } virtual void startInstrumenting() = 0; @@ -24,5 +26,4 @@ class PLATFORM_EXPORT V8InspectorSessionClient { } // namespace blink - #endif // V8InspectorSessionClient_h diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8ProfilerAgent.h b/deps/v8_inspector/platform/v8_inspector/public/V8ProfilerAgent.h deleted file mode 100644 index dacf8a0bc35933..00000000000000 --- a/deps/v8_inspector/platform/v8_inspector/public/V8ProfilerAgent.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef V8ProfilerAgent_h -#define V8ProfilerAgent_h - -#include "platform/PlatformExport.h" -#include "platform/inspector_protocol/Dispatcher.h" -#include "platform/v8_inspector/public/V8Debugger.h" - -namespace blink { - -class PLATFORM_EXPORT V8ProfilerAgent : public protocol::Backend::Profiler, public V8Debugger::Agent<protocol::Frontend::Profiler> { -public: - virtual ~V8ProfilerAgent() { } -}; - -} // namespace blink - - -#endif // !defined(V8ProfilerAgent_h) diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8RuntimeAgent.h b/deps/v8_inspector/platform/v8_inspector/public/V8RuntimeAgent.h deleted file mode 100644 index 917114f42bcfee..00000000000000 --- a/deps/v8_inspector/platform/v8_inspector/public/V8RuntimeAgent.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef V8RuntimeAgent_h -#define V8RuntimeAgent_h - -#include "platform/PlatformExport.h" -#include "platform/inspector_protocol/Dispatcher.h" -#include "platform/v8_inspector/public/V8Debugger.h" - -namespace blink { - -class InjectedScriptManager; - -class PLATFORM_EXPORT V8RuntimeAgent : public protocol::Backend::Runtime, public V8Debugger::Agent<protocol::Frontend::Runtime> { -public: - virtual ~V8RuntimeAgent() { } -}; - -} // namespace blink - -#endif // V8RuntimeAgent_h diff --git a/deps/v8_inspector/platform/v8_inspector/public/V8StackTrace.h b/deps/v8_inspector/platform/v8_inspector/public/V8StackTrace.h index da716c99bf3ec9..6d5c4804c4be35 100644 --- a/deps/v8_inspector/platform/v8_inspector/public/V8StackTrace.h +++ b/deps/v8_inspector/platform/v8_inspector/public/V8StackTrace.h @@ -5,8 +5,9 @@ #ifndef V8StackTrace_h #define V8StackTrace_h -#include "platform/inspector_protocol/TypeBuilder.h" -#include "wtf/PtrUtil.h" +#include "platform/inspector_protocol/Platform.h" +#include "platform/inspector_protocol/String16.h" +#include "platform/v8_inspector/protocol/Runtime.h" #include <v8.h> @@ -35,6 +36,8 @@ class V8StackTrace { virtual ~V8StackTrace() { } virtual std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObject() const = 0; virtual String16 toString() const = 0; + virtual std::unique_ptr<V8StackTrace> clone() = 0; + virtual std::unique_ptr<V8StackTrace> isolatedCopy() = 0; // Safe to pass between threads. }; } // namespace blink diff --git a/deps/v8_inspector/platform/v8_inspector/v8_inspector.gyp b/deps/v8_inspector/platform/v8_inspector/v8_inspector.gyp index 20df5e8bc3d635..a9b31dc1d4a4af 100644 --- a/deps/v8_inspector/platform/v8_inspector/v8_inspector.gyp +++ b/deps/v8_inspector/platform/v8_inspector/v8_inspector.gyp @@ -3,6 +3,9 @@ # found in the LICENSE file. { + 'variables': { + 'blink_platform_output_dir': '<(SHARED_INTERMEDIATE_DIR)/blink/platform', + }, 'targets': [ { # GN version: //third_party/WebKit/Source/platform:inspector_injected_script @@ -44,5 +47,191 @@ # Since this target generates header files, it needs to be a hard dependency. 'hard_dependency': 1, }, + { + # GN version: //third_party/WebKit/Source/platform:inspector_protocol_sources + 'target_name': 'protocol_sources', + 'type': 'none', + 'dependencies': ['protocol_version'], + 'variables': { + 'conditions': [ + ['debug_devtools=="node"', { + # Node build + 'jinja_module_files': [ + '../../deps/jinja2/jinja2/__init__.py', + '../../deps/markupsafe/markupsafe/__init__.py', # jinja2 dep + ], + }, { + 'jinja_module_files': [ + '<(DEPTH)/third_party/jinja2/__init__.py', + '<(DEPTH)/third_party/markupsafe/__init__.py', # jinja2 dep + ], + } + ], + ], + }, + 'actions': [ + { + 'action_name': 'generateV8InspectorProtocolBackendSources', + 'inputs': [ + '<@(jinja_module_files)', + # The python script in action below. + '../inspector_protocol/CodeGenerator.py', + # Source code templates. + '../inspector_protocol/TypeBuilder_h.template', + '../inspector_protocol/TypeBuilder_cpp.template', + # Protocol definitions + 'js_protocol.json', + ], + 'outputs': [ + '<(blink_platform_output_dir)/v8_inspector/protocol/Debugger.cpp', + '<(blink_platform_output_dir)/v8_inspector/protocol/Debugger.h', + '<(blink_platform_output_dir)/v8_inspector/protocol/HeapProfiler.cpp', + '<(blink_platform_output_dir)/v8_inspector/protocol/HeapProfiler.h', + '<(blink_platform_output_dir)/v8_inspector/protocol/Profiler.cpp', + '<(blink_platform_output_dir)/v8_inspector/protocol/Profiler.h', + '<(blink_platform_output_dir)/v8_inspector/protocol/Runtime.cpp', + '<(blink_platform_output_dir)/v8_inspector/protocol/Runtime.h', + ], + 'action': [ + 'python', + '../inspector_protocol/CodeGenerator.py', + '--protocol', 'js_protocol.json', + '--string_type', 'String16', + '--export_macro', 'PLATFORM_EXPORT', + '--output_dir', '<(blink_platform_output_dir)/v8_inspector/protocol', + '--output_package', 'platform/v8_inspector/protocol', + ], + 'message': 'Generating protocol backend sources from json definitions.', + }, + ] + }, + { + # GN version: //third_party/WebKit/Source/core/inspector:protocol_version + 'target_name': 'protocol_version', + 'type': 'none', + 'actions': [ + { + 'action_name': 'generateV8InspectorProtocolVersion', + 'inputs': [ + '../inspector_protocol/generate-inspector-protocol-version', + 'js_protocol.json', + ], + 'outputs': [ + '<(blink_platform_output_dir)/v8_inspector/protocol.json', + ], + 'action': [ + 'python', + '../inspector_protocol/generate-inspector-protocol-version', + '--o', + '<@(_outputs)', + 'js_protocol.json', + ], + 'message': 'Validate v8_inspector protocol for backwards compatibility and generate version file', + }, + ] + }, + { + 'target_name': 'v8_inspector_stl', + 'type': '<(component)', + 'dependencies': [ + ':inspector_injected_script', + ':inspector_debugger_script', + ':protocol_sources', + ], + 'defines': [ + 'V8_INSPECTOR_USE_STL=1' + ], + 'include_dirs': [ + '../..', + '../../../v8/include', + '../../../v8', + '<(SHARED_INTERMEDIATE_DIR)/blink', + ], + 'sources': [ + '<(blink_platform_output_dir)/v8_inspector/protocol/Debugger.cpp', + '<(blink_platform_output_dir)/v8_inspector/protocol/Debugger.h', + '<(blink_platform_output_dir)/v8_inspector/protocol/HeapProfiler.cpp', + '<(blink_platform_output_dir)/v8_inspector/protocol/HeapProfiler.h', + '<(blink_platform_output_dir)/v8_inspector/protocol/Profiler.cpp', + '<(blink_platform_output_dir)/v8_inspector/protocol/Profiler.h', + '<(blink_platform_output_dir)/v8_inspector/protocol/Runtime.cpp', + '<(blink_platform_output_dir)/v8_inspector/protocol/Runtime.h', + + '../inspector_protocol/Allocator.h', + '../inspector_protocol/Array.h', + '../inspector_protocol/Collections.h', + '../inspector_protocol/CollectionsSTL.h', + '../inspector_protocol/DispatcherBase.cpp', + '../inspector_protocol/DispatcherBase.h', + '../inspector_protocol/ErrorSupport.cpp', + '../inspector_protocol/ErrorSupport.h', + '../inspector_protocol/Maybe.h', + '../inspector_protocol/Parser.cpp', + '../inspector_protocol/Parser.h', + '../inspector_protocol/FrontendChannel.h', + '../inspector_protocol/String16.h', + '../inspector_protocol/String16STL.cpp', + '../inspector_protocol/String16STL.h', + '../inspector_protocol/Values.cpp', + '../inspector_protocol/Values.h', + '../inspector_protocol/ValueConversions.cpp', + '../inspector_protocol/ValueConversions.h', + + 'Atomics.h', + 'IgnoreExceptionsScope.h', + 'InjectedScript.cpp', + 'InjectedScript.h', + 'InjectedScriptNative.cpp', + 'InjectedScriptNative.h', + 'InspectedContext.cpp', + 'InspectedContext.h', + 'JavaScriptCallFrame.cpp', + 'JavaScriptCallFrame.h', + 'MuteConsoleScope.h', + 'ScriptBreakpoint.h', + 'RemoteObjectId.cpp', + 'RemoteObjectId.h', + 'V8Console.cpp', + 'V8Console.h', + 'V8DebuggerAgentImpl.cpp', + 'V8DebuggerAgentImpl.h', + 'V8DebuggerImpl.cpp', + 'V8DebuggerImpl.h', + 'V8DebuggerScript.cpp', + 'V8DebuggerScript.h', + 'V8FunctionCall.cpp', + 'V8FunctionCall.h', + 'V8HeapProfilerAgentImpl.cpp', + 'V8HeapProfilerAgentImpl.h', + 'V8InjectedScriptHost.cpp', + 'V8InjectedScriptHost.h', + 'V8InspectorSessionImpl.cpp', + 'V8InspectorSessionImpl.h', + 'V8ProfilerAgentImpl.cpp', + 'V8ProfilerAgentImpl.h', + 'V8Regex.cpp', + 'V8Regex.h', + 'V8RuntimeAgentImpl.cpp', + 'V8RuntimeAgentImpl.h', + 'V8StackTraceImpl.cpp', + 'V8StackTraceImpl.h', + 'V8StringUtil.cpp', + 'V8StringUtil.h', + 'public/V8EventListenerInfo.h', + 'public/V8ContentSearchUtil.h', + 'public/V8ContextInfo.h', + 'public/V8Debugger.h', + 'public/V8DebuggerClient.h', + 'public/V8HeapProfilerAgent.h', + 'public/V8Inspector.cpp', + 'public/V8Inspector.h', + 'public/V8InspectorSession.h', + 'public/V8StackTrace.h', + 'public/V8ToProtocolValue.h', + + '<(blink_platform_output_dir/v8_inspector/DebuggerScript.h', + '<(blink_platform_output_dir/v8_inspector/InjectedScriptSource.h', + ], + }, ], # targets } diff --git a/deps/v8_inspector/v8_inspector.gyp b/deps/v8_inspector/v8_inspector.gyp deleted file mode 100644 index 59d8843b8e1a39..00000000000000 --- a/deps/v8_inspector/v8_inspector.gyp +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'includes': [ - 'v8_inspector.gypi', - ], - - 'targets': [ - { - 'target_name': 'v8_inspector', - 'type': '<(component)', - - 'dependencies': [ - 'platform/inspector_protocol/protocol.gyp:protocol_sources', - 'platform/v8_inspector/v8_inspector.gyp:inspector_injected_script', - 'platform/v8_inspector/v8_inspector.gyp:inspector_debugger_script', - ], - 'defines': [ - 'V8_INSPECTOR_USE_STL=1' - ], - 'include_dirs': [ - '.', - 'deps/wtf', - '../v8/include', - '../v8', - '<(SHARED_INTERMEDIATE_DIR)/blink', - ], - 'sources': [ - '<@(v8_inspector_files)', - ], - }, - ] # end targets -} diff --git a/deps/v8_inspector/v8_inspector.gypi b/deps/v8_inspector/v8_inspector.gypi deleted file mode 100644 index aad781b6842021..00000000000000 --- a/deps/v8_inspector/v8_inspector.gypi +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'variables': { - 'v8_inspector_files': [ - - '<(SHARED_INTERMEDIATE_DIR)/blink/platform/inspector_protocol/Backend.h', - '<(SHARED_INTERMEDIATE_DIR)/blink/platform/inspector_protocol/Dispatcher.cpp', - '<(SHARED_INTERMEDIATE_DIR)/blink/platform/inspector_protocol/Dispatcher.h', - '<(SHARED_INTERMEDIATE_DIR)/blink/platform/inspector_protocol/Frontend.cpp', - '<(SHARED_INTERMEDIATE_DIR)/blink/platform/inspector_protocol/Frontend.h', - '<(SHARED_INTERMEDIATE_DIR)/blink/platform/inspector_protocol/TypeBuilder.cpp', - '<(SHARED_INTERMEDIATE_DIR)/blink/platform/inspector_protocol/TypeBuilder.h', - 'platform/v8_inspector/public/V8Inspector.cpp', - 'platform/v8_inspector/public/V8Inspector.h', - - 'platform/inspector_protocol/Allocator.h', - 'platform/inspector_protocol/Array.h', - 'platform/inspector_protocol/Collections.h', - 'platform/inspector_protocol/CollectionsSTL.h', - 'platform/inspector_protocol/ErrorSupport.cpp', - 'platform/inspector_protocol/ErrorSupport.h', - 'platform/inspector_protocol/Maybe.h', - 'platform/inspector_protocol/Parser.cpp', - 'platform/inspector_protocol/Parser.h', - 'platform/inspector_protocol/FrontendChannel.h', - 'platform/inspector_protocol/String16.h', - 'platform/inspector_protocol/String16STL.cpp', - 'platform/inspector_protocol/String16STL.h', - 'platform/inspector_protocol/Values.cpp', - 'platform/inspector_protocol/Values.h', - 'platform/inspector_protocol/ValueConversions.cpp', - 'platform/inspector_protocol/ValueConversions.h', - 'platform/v8_inspector/Atomics.h', - 'platform/v8_inspector/InspectorWrapper.cpp', - 'platform/v8_inspector/InspectorWrapper.h', - 'platform/v8_inspector/IgnoreExceptionsScope.h', - 'platform/v8_inspector/InjectedScript.cpp', - 'platform/v8_inspector/InjectedScript.h', - 'platform/v8_inspector/InjectedScriptNative.cpp', - 'platform/v8_inspector/InjectedScriptNative.h', - 'platform/v8_inspector/InspectedContext.cpp', - 'platform/v8_inspector/InspectedContext.h', - 'platform/v8_inspector/JavaScriptCallFrame.cpp', - 'platform/v8_inspector/JavaScriptCallFrame.h', - 'platform/v8_inspector/MuteConsoleScope.h', - 'platform/v8_inspector/ScriptBreakpoint.h', - 'platform/v8_inspector/RemoteObjectId.cpp', - 'platform/v8_inspector/RemoteObjectId.h', - 'platform/v8_inspector/V8Console.cpp', - 'platform/v8_inspector/V8Console.h', - 'platform/v8_inspector/V8DebuggerAgentImpl.cpp', - 'platform/v8_inspector/V8DebuggerAgentImpl.h', - 'platform/v8_inspector/V8DebuggerImpl.cpp', - 'platform/v8_inspector/V8DebuggerImpl.h', - 'platform/v8_inspector/V8DebuggerScript.cpp', - 'platform/v8_inspector/V8DebuggerScript.h', - 'platform/v8_inspector/V8FunctionCall.cpp', - 'platform/v8_inspector/V8FunctionCall.h', - 'platform/v8_inspector/V8HeapProfilerAgentImpl.cpp', - 'platform/v8_inspector/V8HeapProfilerAgentImpl.h', - 'platform/v8_inspector/V8InjectedScriptHost.cpp', - 'platform/v8_inspector/V8InjectedScriptHost.h', - 'platform/v8_inspector/V8InspectorSessionImpl.cpp', - 'platform/v8_inspector/V8InspectorSessionImpl.h', - 'platform/v8_inspector/V8ProfilerAgentImpl.cpp', - 'platform/v8_inspector/V8ProfilerAgentImpl.h', - 'platform/v8_inspector/V8Regex.cpp', - 'platform/v8_inspector/V8Regex.h', - 'platform/v8_inspector/V8RuntimeAgentImpl.cpp', - 'platform/v8_inspector/V8RuntimeAgentImpl.h', - 'platform/v8_inspector/V8StackTraceImpl.cpp', - 'platform/v8_inspector/V8StackTraceImpl.h', - 'platform/v8_inspector/V8StringUtil.cpp', - 'platform/v8_inspector/V8StringUtil.h', - 'platform/v8_inspector/public/V8EventListenerInfo.h', - 'platform/v8_inspector/public/V8ContentSearchUtil.h', - 'platform/v8_inspector/public/V8ContextInfo.h', - 'platform/v8_inspector/public/V8Debugger.h', - 'platform/v8_inspector/public/V8DebuggerAgent.h', - 'platform/v8_inspector/public/V8DebuggerClient.h', - 'platform/v8_inspector/public/V8HeapProfilerAgent.h', - 'platform/v8_inspector/public/V8InspectorSession.h', - 'platform/v8_inspector/public/V8ProfilerAgent.h', - 'platform/v8_inspector/public/V8RuntimeAgent.h', - 'platform/v8_inspector/public/V8StackTrace.h', - 'platform/v8_inspector/public/V8ToProtocolValue.h', - '<(SHARED_INTERMEDIATE_DIR)/blink/platform/v8_inspector/DebuggerScript.h', - '<(SHARED_INTERMEDIATE_DIR)/blink/platform/v8_inspector/InjectedScriptSource.h', - ], - } -} diff --git a/node.gyp b/node.gyp index c26a42242d947c..0656691c24461a 100644 --- a/node.gyp +++ b/node.gyp @@ -263,7 +263,7 @@ 'src/inspector-agent.h', ], 'dependencies': [ - 'deps/v8_inspector/v8_inspector.gyp:v8_inspector', + 'deps/v8_inspector/platform/v8_inspector/v8_inspector.gyp:v8_inspector_stl', ], 'include_dirs': [ 'deps/v8_inspector', diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index bcccff4acb008f..d8d389f18147dd 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -192,7 +192,6 @@ class AgentImpl { uv_mutex_t pause_lock_; uv_thread_t thread_; uv_loop_t child_loop_; - uv_tcp_t server_; int port_; bool wait_; @@ -237,21 +236,21 @@ class ChannelImpl final : public blink::protocol::FrontendChannel { explicit ChannelImpl(AgentImpl* agent): agent_(agent) {} virtual ~ChannelImpl() {} private: - virtual void sendProtocolResponse(int sessionId, int callId, - std::unique_ptr<DictionaryValue> message) + virtual void sendProtocolResponse(int callId, + const String16& message) override { - sendMessageToFrontend(std::move(message)); + sendMessageToFrontend(message); } virtual void sendProtocolNotification( - std::unique_ptr<DictionaryValue> message) override { - sendMessageToFrontend(std::move(message)); + const String16& message) override { + sendMessageToFrontend(message); } - virtual void flush() override { } + virtual void flushProtocolNotifications() override { } - void sendMessageToFrontend(std::unique_ptr<DictionaryValue> message) { - agent_->Write(message->toJSONString().utf8()); + void sendMessageToFrontend(const String16& message) { + agent_->Write(message.utf8()); } AgentImpl* const agent_; From 09ecd1fb581cbad049b953597f52a0e93df3fab0 Mon Sep 17 00:00:00 2001 From: Eugene Ostroukhov <eostroukhov@chromium.org> Date: Thu, 16 Jun 2016 10:25:22 -0700 Subject: [PATCH 018/131] inspector: fix coverity scan errors PR-URL: https://github.com/nodejs/node/pull/7324 Reviewed-By: cjihrig - Colin Ihrig <cjihrig@gmail.com> Reviewed-By: ofrobots - Ali Ijaz Sheikh <ofrobots@google.com> Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl> --- src/inspector_agent.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index d8d389f18147dd..d9823327904996 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -110,9 +110,11 @@ void SendTargentsListResponse(inspector_socket_t* socket, int port) { char buffer[sizeof(LIST_RESPONSE_TEMPLATE) + 4096]; char title[2048]; // uv_get_process_title trims the title if too long int err = uv_get_process_title(title, sizeof(title)); - ASSERT_EQ(0, err); + if (err != 0) { + snprintf(title, sizeof(title), "Node.js"); + } char* c = title; - while (!c) { + while (*c != '\0') { if (*c < ' ' || *c == '\"') { *c = '_'; } @@ -322,6 +324,8 @@ AgentImpl::AgentImpl(Environment* env) : port_(0), int err; err = uv_sem_init(&start_sem_, 0); CHECK_EQ(err, 0); + memset(&data_written_, 0, sizeof(data_written_)); + memset(&io_thread_req_, 0, sizeof(io_thread_req_)); } AgentImpl::~AgentImpl() { From 8dd48c9251d240b30af53cdbc7f8924b86baaded Mon Sep 17 00:00:00 2001 From: Eugene Ostroukhov <eostroukhov@chromium.org> Date: Wed, 1 Jun 2016 10:53:30 -0700 Subject: [PATCH 019/131] inspector: fix inspector connection cleanup In some cases close callback was called twice, while in some cases the memory was still not released at all. PR-URL: https://github.com/nodejs/node/pull/7268 Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl> --- src/inspector_agent.cc | 18 +++---- src/inspector_socket.cc | 5 -- test/cctest/test_inspector_socket.cc | 78 ++++++++++++++++++---------- 3 files changed, 57 insertions(+), 44 deletions(-) diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index d9823327904996..b0451db75f7dac 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -47,7 +47,7 @@ bool AcceptsConnection(inspector_socket_t* socket, const char* path) { } void DisposeInspector(inspector_socket_t* socket, int status) { - free(socket); + delete socket; } void DisconnectAndDisposeIO(inspector_socket_t* socket) { @@ -404,14 +404,12 @@ void AgentImpl::ThreadCbIO(void* agent) { // static void AgentImpl::OnSocketConnectionIO(uv_stream_t* server, int status) { if (status == 0) { - inspector_socket_t* socket = - static_cast<inspector_socket_t*>(malloc(sizeof(*socket))); - ASSERT_NE(nullptr, socket); + inspector_socket_t* socket = new inspector_socket_t(); memset(socket, 0, sizeof(*socket)); socket->data = server->data; if (inspector_accept(server, socket, AgentImpl::OnInspectorHandshakeIO) != 0) { - free(socket); + delete socket; } } } @@ -430,6 +428,7 @@ bool AgentImpl::OnInspectorHandshakeIO(inspector_socket_t* socket, agent->OnInspectorConnectionIO(socket); return true; case kInspectorHandshakeFailed: + delete socket; return false; default: UNREACHABLE(); @@ -461,12 +460,7 @@ void AgentImpl::OnRemoteDataIO(uv_stream_t* stream, agent->parent_env_->isolate() ->RequestInterrupt(InterruptCallback, agent); uv_async_send(&agent->data_written_); - } else if (read < 0) { - if (agent->client_socket_ == socket) { - agent->client_socket_ = nullptr; - } - DisconnectAndDisposeIO(socket); - } else { + } else if (read <= 0) { // EOF if (agent->client_socket_ == socket) { agent->client_socket_ = nullptr; @@ -474,6 +468,7 @@ void AgentImpl::OnRemoteDataIO(uv_stream_t* stream, new SetConnectedTask(agent, false)); uv_async_send(&agent->data_written_); } + DisconnectAndDisposeIO(socket); } uv_cond_broadcast(&agent->pause_cond_); } @@ -537,6 +532,7 @@ void AgentImpl::WorkerRunIO() { void AgentImpl::OnInspectorConnectionIO(inspector_socket_t* socket) { if (client_socket_) { + DisconnectAndDisposeIO(socket); return; } client_socket_ = socket; diff --git a/src/inspector_socket.cc b/src/inspector_socket.cc index fa2b6734e9e186..b860ef3bb740f7 100644 --- a/src/inspector_socket.cc +++ b/src/inspector_socket.cc @@ -88,8 +88,6 @@ static void close_connection(inspector_socket_t* inspector) { if (!uv_is_closing(socket)) { uv_read_stop(reinterpret_cast<uv_stream_t*>(socket)); uv_close(socket, dispose_inspector); - } else if (inspector->ws_state->close_cb) { - inspector->ws_state->close_cb(inspector, 0); } } @@ -276,9 +274,6 @@ static void invoke_read_callback(inspector_socket_t* inspector, } static void shutdown_complete(inspector_socket_t* inspector) { - if (inspector->ws_state->close_cb) { - inspector->ws_state->close_cb(inspector, 0); - } close_connection(inspector); } diff --git a/test/cctest/test_inspector_socket.cc b/test/cctest/test_inspector_socket.cc index ebe4215af5eca4..eecb35f824f13a 100644 --- a/test/cctest/test_inspector_socket.cc +++ b/test/cctest/test_inspector_socket.cc @@ -84,7 +84,7 @@ static void do_write(const char* data, int len) { bool done = false; req.data = &done; uv_buf_t buf[1]; - buf[0].base = const_cast<char *>(data); + buf[0].base = const_cast<char*>(data); buf[0].len = len; uv_write(&req, reinterpret_cast<uv_stream_t *>(&client_socket), buf, 1, write_done); @@ -92,7 +92,7 @@ static void do_write(const char* data, int len) { } static void buffer_alloc_cb(uv_handle_t* stream, size_t len, uv_buf_t* buf) { - buf->base = static_cast<char *>(malloc(len)); + buf->base = static_cast<char*>(malloc(len)); buf->len = len; } @@ -369,7 +369,7 @@ class InspectorSocketTest : public ::testing::Test { TEST_F(InspectorSocketTest, ReadsAndWritesInspectorMessage) { ASSERT_TRUE(connected); ASSERT_FALSE(inspector_ready); - do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + do_write(const_cast<char*>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); SPIN_WHILE(!inspector_ready); expect_handshake(); @@ -397,7 +397,7 @@ TEST_F(InspectorSocketTest, ReadsAndWritesInspectorMessage) { TEST_F(InspectorSocketTest, BufferEdgeCases) { - do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + do_write(const_cast<char*>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); expect_handshake(); const char MULTIPLE_REQUESTS[] = { @@ -466,11 +466,11 @@ TEST_F(InspectorSocketTest, AcceptsRequestInSeveralWrites) { const int write1 = 95; const int write2 = 5; const int write3 = sizeof(HANDSHAKE_REQ) - write1 - write2 - 1; - do_write(const_cast<char *>(HANDSHAKE_REQ), write1); + do_write(const_cast<char*>(HANDSHAKE_REQ), write1); ASSERT_FALSE(inspector_ready); - do_write(const_cast<char *>(HANDSHAKE_REQ) + write1, write2); + do_write(const_cast<char*>(HANDSHAKE_REQ) + write1, write2); ASSERT_FALSE(inspector_ready); - do_write(const_cast<char *>(HANDSHAKE_REQ) + write1 + write2, write3); + do_write(const_cast<char*>(HANDSHAKE_REQ) + write1 + write2, write3); SPIN_WHILE(!inspector_ready); expect_handshake(); inspector_read_stop(&inspector); @@ -481,10 +481,10 @@ TEST_F(InspectorSocketTest, AcceptsRequestInSeveralWrites) { TEST_F(InspectorSocketTest, ExtraTextBeforeRequest) { last_event = kInspectorHandshakeUpgraded; char UNCOOL_BRO[] = "Uncool, bro: Text before the first req\r\n"; - do_write(const_cast<char *>(UNCOOL_BRO), sizeof(UNCOOL_BRO) - 1); + do_write(const_cast<char*>(UNCOOL_BRO), sizeof(UNCOOL_BRO) - 1); ASSERT_FALSE(inspector_ready); - do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + do_write(const_cast<char*>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); SPIN_WHILE(last_event != kInspectorHandshakeFailed); expect_handshake_failure(); EXPECT_EQ(uv_is_active(reinterpret_cast<uv_handle_t*>(&client_socket)), 0); @@ -493,10 +493,10 @@ TEST_F(InspectorSocketTest, ExtraTextBeforeRequest) { TEST_F(InspectorSocketTest, ExtraLettersBeforeRequest) { char UNCOOL_BRO[] = "Uncool!!"; - do_write(const_cast<char *>(UNCOOL_BRO), sizeof(UNCOOL_BRO) - 1); + do_write(const_cast<char*>(UNCOOL_BRO), sizeof(UNCOOL_BRO) - 1); ASSERT_FALSE(inspector_ready); - do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + do_write(const_cast<char*>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); SPIN_WHILE(last_event != kInspectorHandshakeFailed); expect_handshake_failure(); EXPECT_EQ(uv_is_active(reinterpret_cast<uv_handle_t*>(&client_socket)), 0); @@ -511,7 +511,7 @@ TEST_F(InspectorSocketTest, RequestWithoutKey) { "Sec-WebSocket-Version: 13\r\n\r\n"; ; - do_write(const_cast<char *>(BROKEN_REQUEST), sizeof(BROKEN_REQUEST) - 1); + do_write(const_cast<char*>(BROKEN_REQUEST), sizeof(BROKEN_REQUEST) - 1); SPIN_WHILE(last_event != kInspectorHandshakeFailed); expect_handshake_failure(); EXPECT_EQ(uv_is_active(reinterpret_cast<uv_handle_t*>(&client_socket)), 0); @@ -521,7 +521,7 @@ TEST_F(InspectorSocketTest, RequestWithoutKey) { TEST_F(InspectorSocketTest, KillsConnectionOnProtocolViolation) { ASSERT_TRUE(connected); ASSERT_FALSE(inspector_ready); - do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + do_write(const_cast<char*>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); SPIN_WHILE(!inspector_ready); ASSERT_TRUE(inspector_ready); expect_handshake(); @@ -534,7 +534,7 @@ TEST_F(InspectorSocketTest, KillsConnectionOnProtocolViolation) { TEST_F(InspectorSocketTest, CanStopReadingFromInspector) { ASSERT_TRUE(connected); ASSERT_FALSE(inspector_ready); - do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + do_write(const_cast<char*>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); expect_handshake(); ASSERT_TRUE(inspector_ready); @@ -552,15 +552,15 @@ TEST_F(InspectorSocketTest, CanStopReadingFromInspector) { manual_inspector_socket_cleanup(); } -static bool inspector_closed; +static int inspector_closed = 0; void inspector_closed_cb(inspector_socket_t *inspector, int code) { - inspector_closed = true; + inspector_closed++; } TEST_F(InspectorSocketTest, CloseDoesNotNotifyReadCallback) { - inspector_closed = false; - do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + inspector_closed = 0; + do_write(const_cast<char*>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); expect_handshake(); int error_code = 0; @@ -570,27 +570,29 @@ TEST_F(InspectorSocketTest, CloseDoesNotNotifyReadCallback) { inspector_close(&inspector, inspector_closed_cb); char CLOSE_FRAME[] = {'\x88', '\x00'}; expect_on_client(CLOSE_FRAME, sizeof(CLOSE_FRAME)); - ASSERT_FALSE(inspector_closed); + EXPECT_EQ(0, inspector_closed); const char CLIENT_CLOSE_FRAME[] = {'\x88', '\x80', '\x2D', '\x0E', '\x1E', '\xFA'}; do_write(CLIENT_CLOSE_FRAME, sizeof(CLIENT_CLOSE_FRAME)); EXPECT_NE(UV_EOF, error_code); - SPIN_WHILE(!inspector_closed); + SPIN_WHILE(inspector_closed == 0); + EXPECT_EQ(1, inspector_closed); inspector.data = nullptr; } TEST_F(InspectorSocketTest, CloseWorksWithoutReadEnabled) { - inspector_closed = false; - do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + inspector_closed = 0; + do_write(const_cast<char*>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); expect_handshake(); inspector_close(&inspector, inspector_closed_cb); char CLOSE_FRAME[] = {'\x88', '\x00'}; expect_on_client(CLOSE_FRAME, sizeof(CLOSE_FRAME)); - ASSERT_FALSE(inspector_closed); + EXPECT_EQ(0, inspector_closed); const char CLIENT_CLOSE_FRAME[] = {'\x88', '\x80', '\x2D', '\x0E', '\x1E', '\xFA'}; do_write(CLIENT_CLOSE_FRAME, sizeof(CLIENT_CLOSE_FRAME)); - SPIN_WHILE(!inspector_closed); + SPIN_WHILE(inspector_closed == 0); + EXPECT_EQ(1, inspector_closed); } // Make sure buffering works @@ -690,7 +692,7 @@ HandshakeCanBeCanceled_handshake(enum inspector_handshake_event state, TEST_F(InspectorSocketTest, HandshakeCanBeCanceled) { handshake_delegate = HandshakeCanBeCanceled_handshake; - do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + do_write(const_cast<char*>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); expect_handshake_failure(); EXPECT_EQ(2, handshake_events); @@ -727,7 +729,7 @@ TEST_F(InspectorSocketTest, GetThenHandshake) { expect_on_client(TEST_SUCCESS, sizeof(TEST_SUCCESS) - 1); - do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + do_write(const_cast<char*>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); expect_handshake(); EXPECT_EQ(3, handshake_events); manual_inspector_socket_cleanup(); @@ -766,7 +768,7 @@ static void CleanupSocketAfterEOF_read_cb(uv_stream_t* stream, ssize_t nread, } TEST_F(InspectorSocketTest, CleanupSocketAfterEOF) { - do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + do_write(const_cast<char*>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); expect_handshake(); inspector_read_start(&inspector, buffer_alloc_cb, @@ -810,7 +812,7 @@ static void mask_message(const char* message, TEST_F(InspectorSocketTest, Send1Mb) { ASSERT_TRUE(connected); ASSERT_FALSE(inspector_ready); - do_write(const_cast<char *>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + do_write(const_cast<char*>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); SPIN_WHILE(!inspector_ready); expect_handshake(); @@ -862,3 +864,23 @@ TEST_F(InspectorSocketTest, Send1Mb) { free(expected); free(message); } + +static ssize_t err; + +void ErrorCleansUpTheSocket_cb(uv_stream_t* stream, ssize_t read, + const uv_buf_t* buf) { + err = read; +} + +TEST_F(InspectorSocketTest, ErrorCleansUpTheSocket) { + inspector_closed = 0; + do_write(const_cast<char*>(HANDSHAKE_REQ), sizeof(HANDSHAKE_REQ) - 1); + expect_handshake(); + const char NOT_A_GOOD_FRAME[] = {'H', 'e', 'l', 'l', 'o'}; + err = 42; + inspector_read_start(&inspector, buffer_alloc_cb, + ErrorCleansUpTheSocket_cb); + do_write(NOT_A_GOOD_FRAME, sizeof(NOT_A_GOOD_FRAME)); + SPIN_WHILE(err > 0); + EXPECT_EQ(UV_EPROTO, err); +} From d049919e7dad847c6368f37042e81796748b5e09 Mon Sep 17 00:00:00 2001 From: Anna Henningsen <anna@addaleax.net> Date: Sun, 8 May 2016 03:28:47 +0200 Subject: [PATCH 020/131] vm: add ability to break on sigint/ctrl+c - Adds the `breakEvalOnSigint` option to `vm.runIn(This)Context`. This uses a watchdog thread to wait for SIGINT and generally works just like the existing `timeout` option. - Adds a method to the existing timer-based watchdog to check if it stopped regularly or by running into the timeout. This is used to tell a SIGINT abort from a timer-based one. - Adds (internal) `process._{start,stop}SigintWatchdog` methods to start/stop the watchdog thread used by the above option manually. This will be used in the REPL to set up SIGINT handling before entering terminal raw mode, so that there is no time window in which Ctrl+C fully aborts the process. PR-URL: https://github.com/nodejs/node/pull/6635 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> --- doc/api/vm.md | 6 + lib/vm.js | 47 ++++ src/node.cc | 8 +- src/node_contextify.cc | 42 +++- src/node_internals.h | 7 + src/node_util.cc | 18 ++ src/node_watchdog.cc | 225 ++++++++++++++++++ src/node_watchdog.h | 59 ++++- test/message/eval_messages.out | 3 + test/message/stdin_messages.out | 3 + .../undefined_reference_in_new_context.out | 2 +- test/message/vm_display_runtime_error.out | 2 +- .../message/vm_dont_display_runtime_error.out | 2 +- test/parallel/test-util-sigint-watchdog.js | 53 +++++ .../test-vm-sigint-existing-handler.js | 77 ++++++ test/parallel/test-vm-sigint.js | 39 +++ 16 files changed, 582 insertions(+), 11 deletions(-) create mode 100644 test/parallel/test-util-sigint-watchdog.js create mode 100644 test/parallel/test-vm-sigint-existing-handler.js create mode 100644 test/parallel/test-vm-sigint.js diff --git a/doc/api/vm.md b/doc/api/vm.md index b65826b2d35c0d..0141ad605eecbb 100644 --- a/doc/api/vm.md +++ b/doc/api/vm.md @@ -77,6 +77,12 @@ added: v0.3.1 * `timeout` {number} Specifies the number of milliseconds to execute `code` before terminating execution. If execution is terminated, an [`Error`][] will be thrown. + * `breakOnSigint`: if `true`, the execution will be terminated when + `SIGINT` (Ctrl+C) is received. Existing handlers for the + event that have been attached via `process.on("SIGINT")` will be disabled + during script execution, but will continue to work after that. + If execution is terminated, an [`Error`][] will be thrown. + Runs the compiled code contained by the `vm.Script` object within the given `contextifiedSandbox` and returns the result. Running code does not have access diff --git a/lib/vm.js b/lib/vm.js index b4a2b9999091d2..364a37eacbe7ce 100644 --- a/lib/vm.js +++ b/lib/vm.js @@ -13,6 +13,29 @@ const Script = binding.ContextifyScript; // - isContext(sandbox) // From this we build the entire documented API. +const realRunInThisContext = Script.prototype.runInThisContext; +const realRunInContext = Script.prototype.runInContext; + +Script.prototype.runInThisContext = function(options) { + if (options && options.breakOnSigint) { + return sigintHandlersWrap(() => { + return realRunInThisContext.call(this, options); + }); + } else { + return realRunInThisContext.call(this, options); + } +}; + +Script.prototype.runInContext = function(contextifiedSandbox, options) { + if (options && options.breakOnSigint) { + return sigintHandlersWrap(() => { + return realRunInContext.call(this, contextifiedSandbox, options); + }); + } else { + return realRunInContext.call(this, contextifiedSandbox, options); + } +}; + Script.prototype.runInNewContext = function(sandbox, options) { var context = exports.createContext(sandbox); return this.runInContext(context, options); @@ -55,3 +78,27 @@ exports.runInThisContext = function(code, options) { }; exports.isContext = binding.isContext; + +// Remove all SIGINT listeners and re-attach them after the wrapped function +// has executed, so that caught SIGINT are handled by the listeners again. +function sigintHandlersWrap(fn) { + // Using the internal list here to make sure `.once()` wrappers are used, + // not the original ones. + let sigintListeners = process._events.SIGINT; + if (!Array.isArray(sigintListeners)) + sigintListeners = sigintListeners ? [sigintListeners] : []; + else + sigintListeners = sigintListeners.slice(); + + process.removeAllListeners('SIGINT'); + + try { + return fn(); + } finally { + // Add using the public methods so that the `newListener` handler of + // process can re-attach the listeners. + for (const listener of sigintListeners) { + process.addListener('SIGINT', listener); + } + } +} diff --git a/src/node.cc b/src/node.cc index f67ca740700e9b..bc48feae04e1e5 100644 --- a/src/node.cc +++ b/src/node.cc @@ -3317,7 +3317,7 @@ static void AtExit() { } -static void SignalExit(int signo) { +void SignalExit(int signo) { uv_tty_reset_mode(); #ifdef __FreeBSD__ // FreeBSD has a nasty bug, see RegisterSignalHandler for details @@ -3819,9 +3819,9 @@ static void EnableDebugSignalHandler(int signo) { } -static void RegisterSignalHandler(int signal, - void (*handler)(int signal), - bool reset_handler = false) { +void RegisterSignalHandler(int signal, + void (*handler)(int signal), + bool reset_handler) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = handler; diff --git a/src/node_contextify.cc b/src/node_contextify.cc index 774871b852d021..a4a769359412ef 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -553,6 +553,7 @@ class ContextifyScript : public BaseObject { TryCatch try_catch(args.GetIsolate()); uint64_t timeout = GetTimeoutArg(args, 0); bool display_errors = GetDisplayErrorsArg(args, 0); + bool break_on_sigint = GetBreakOnSigintArg(args, 0); if (try_catch.HasCaught()) { try_catch.ReThrow(); return; @@ -560,7 +561,7 @@ class ContextifyScript : public BaseObject { // Do the eval within this context Environment* env = Environment::GetCurrent(args); - EvalMachine(env, timeout, display_errors, args, try_catch); + EvalMachine(env, timeout, display_errors, break_on_sigint, args, try_catch); } // args: sandbox, [options] @@ -569,6 +570,7 @@ class ContextifyScript : public BaseObject { int64_t timeout; bool display_errors; + bool break_on_sigint; // Assemble arguments if (!args[0]->IsObject()) { @@ -581,6 +583,7 @@ class ContextifyScript : public BaseObject { TryCatch try_catch(env->isolate()); timeout = GetTimeoutArg(args, 1); display_errors = GetDisplayErrorsArg(args, 1); + break_on_sigint = GetBreakOnSigintArg(args, 1); if (try_catch.HasCaught()) { try_catch.ReThrow(); return; @@ -605,6 +608,7 @@ class ContextifyScript : public BaseObject { if (EvalMachine(contextify_context->env(), timeout, display_errors, + break_on_sigint, args, try_catch)) { contextify_context->CopyProperties(); @@ -653,6 +657,23 @@ class ContextifyScript : public BaseObject { True(env->isolate())); } + static bool GetBreakOnSigintArg(const FunctionCallbackInfo<Value>& args, + const int i) { + if (args[i]->IsUndefined() || args[i]->IsString()) { + return false; + } + if (!args[i]->IsObject()) { + Environment::ThrowTypeError(args.GetIsolate(), + "options must be an object"); + return false; + } + + Local<String> key = FIXED_ONE_BYTE_STRING(args.GetIsolate(), + "breakOnSigint"); + Local<Value> value = args[i].As<Object>()->Get(key); + return value->IsTrue(); + } + static int64_t GetTimeoutArg(const FunctionCallbackInfo<Value>& args, const int i) { if (args[i]->IsUndefined() || args[i]->IsString()) { @@ -798,6 +819,7 @@ class ContextifyScript : public BaseObject { static bool EvalMachine(Environment* env, const int64_t timeout, const bool display_errors, + const bool break_on_sigint, const FunctionCallbackInfo<Value>& args, TryCatch& try_catch) { if (!ContextifyScript::InstanceOf(env, args.Holder())) { @@ -813,16 +835,30 @@ class ContextifyScript : public BaseObject { Local<Script> script = unbound_script->BindToCurrentContext(); Local<Value> result; - if (timeout != -1) { + bool timed_out = false; + if (break_on_sigint && timeout != -1) { Watchdog wd(env->isolate(), timeout); + SigintWatchdog swd(env->isolate()); result = script->Run(); + timed_out = wd.HasTimedOut(); + } else if (break_on_sigint) { + SigintWatchdog swd(env->isolate()); + result = script->Run(); + } else if (timeout != -1) { + Watchdog wd(env->isolate(), timeout); + result = script->Run(); + timed_out = wd.HasTimedOut(); } else { result = script->Run(); } if (try_catch.HasCaught() && try_catch.HasTerminated()) { env->isolate()->CancelTerminateExecution(); - env->ThrowError("Script execution timed out."); + if (timed_out) { + env->ThrowError("Script execution timed out."); + } else { + env->ThrowError("Script execution interrupted."); + } try_catch.ReThrow(); return false; } diff --git a/src/node_internals.h b/src/node_internals.h index 64134d9ab8d9b7..7ae587e1bc090c 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -99,6 +99,13 @@ void GetSockOrPeerName(const v8::FunctionCallbackInfo<v8::Value>& args) { args.GetReturnValue().Set(err); } +void SignalExit(int signo); +#ifdef __POSIX__ +void RegisterSignalHandler(int signal, + void (*handler)(int signal), + bool reset_handler = false); +#endif + #ifdef _WIN32 // emulate snprintf() on windows, _snprintf() doesn't zero-terminate the buffer // on overflow... diff --git a/src/node_util.cc b/src/node_util.cc index 9fc94acf094426..18e89b72642b5a 100644 --- a/src/node_util.cc +++ b/src/node_util.cc @@ -1,4 +1,5 @@ #include "node.h" +#include "node_watchdog.h" #include "v8.h" #include "env.h" #include "env-inl.h" @@ -88,6 +89,20 @@ static void SetHiddenValue(const FunctionCallbackInfo<Value>& args) { } +void StartSigintWatchdog(const FunctionCallbackInfo<Value>& args) { + int ret = SigintWatchdogHelper::GetInstance()->Start(); + if (ret != 0) { + Environment* env = Environment::GetCurrent(args); + env->ThrowErrnoException(ret, "StartSigintWatchdog"); + } +} + + +void StopSigintWatchdog(const FunctionCallbackInfo<Value>& args) { + bool had_pending_signals = SigintWatchdogHelper::GetInstance()->Stop(); + args.GetReturnValue().Set(had_pending_signals); +} + void Initialize(Local<Object> target, Local<Value> unused, Local<Context> context) { @@ -100,6 +115,9 @@ void Initialize(Local<Object> target, env->SetMethod(target, "getHiddenValue", GetHiddenValue); env->SetMethod(target, "setHiddenValue", SetHiddenValue); env->SetMethod(target, "getProxyDetails", GetProxyDetails); + + env->SetMethod(target, "startSigintWatchdog", StartSigintWatchdog); + env->SetMethod(target, "stopSigintWatchdog", StopSigintWatchdog); } } // namespace util diff --git a/src/node_watchdog.cc b/src/node_watchdog.cc index dfa7debd9a7b3a..8e94bc8d9d22ac 100644 --- a/src/node_watchdog.cc +++ b/src/node_watchdog.cc @@ -1,10 +1,13 @@ #include "node_watchdog.h" +#include "node_internals.h" #include "util.h" #include "util-inl.h" +#include <algorithm> namespace node { Watchdog::Watchdog(v8::Isolate* isolate, uint64_t ms) : isolate_(isolate), + timed_out_(false), destroyed_(false) { int rc; loop_ = new uv_loop_t; @@ -79,9 +82,231 @@ void Watchdog::Async(uv_async_t* async) { void Watchdog::Timer(uv_timer_t* timer) { Watchdog* w = ContainerOf(&Watchdog::timer_, timer); + w->timed_out_ = true; uv_stop(w->loop_); w->isolate()->TerminateExecution(); } +SigintWatchdog::~SigintWatchdog() { + Destroy(); +} + + +void SigintWatchdog::Dispose() { + Destroy(); +} + + +SigintWatchdog::SigintWatchdog(v8::Isolate* isolate) + : isolate_(isolate), destroyed_(false) { + // Register this watchdog with the global SIGINT/Ctrl+C listener. + SigintWatchdogHelper::GetInstance()->Register(this); + // Start the helper thread, if that has not already happened. + SigintWatchdogHelper::GetInstance()->Start(); +} + + +void SigintWatchdog::Destroy() { + if (destroyed_) { + return; + } + + destroyed_ = true; + + SigintWatchdogHelper::GetInstance()->Unregister(this); + SigintWatchdogHelper::GetInstance()->Stop(); +} + + +void SigintWatchdog::HandleSigint() { + isolate_->TerminateExecution(); +} + +#ifdef __POSIX__ +void* SigintWatchdogHelper::RunSigintWatchdog(void* arg) { + // Inside the helper thread. + bool is_stopping; + + do { + uv_sem_wait(&instance.sem_); + is_stopping = InformWatchdogsAboutSignal(); + } while (!is_stopping); + + return nullptr; +} + + +void SigintWatchdogHelper::HandleSignal(int signum) { + uv_sem_post(&instance.sem_); +} + +#else + +// Windows starts a separate thread for executing the handler, so no extra +// helper thread is required. +BOOL WINAPI SigintWatchdogHelper::WinCtrlCHandlerRoutine(DWORD dwCtrlType) { + if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) { + InformWatchdogsAboutSignal(); + + // Return true because the signal has been handled. + return TRUE; + } else { + return FALSE; + } +} +#endif + + +bool SigintWatchdogHelper::InformWatchdogsAboutSignal() { + uv_mutex_lock(&instance.list_mutex_); + + bool is_stopping = false; +#ifdef __POSIX__ + is_stopping = instance.stopping_; +#endif + + // If there are no listeners and the helper thread has been awoken by a signal + // (= not when stopping it), indicate that by setting has_pending_signal_. + if (instance.watchdogs_.empty() && !is_stopping) { + instance.has_pending_signal_ = true; + } + + for (auto it : instance.watchdogs_) + it->HandleSigint(); + + uv_mutex_unlock(&instance.list_mutex_); + return is_stopping; +} + + +int SigintWatchdogHelper::Start() { + int ret = 0; + uv_mutex_lock(&mutex_); + + if (start_stop_count_++ > 0) { + goto dont_start; + } + +#ifdef __POSIX__ + CHECK_EQ(has_running_thread_, false); + has_pending_signal_ = false; + stopping_ = false; + + sigset_t sigmask; + sigfillset(&sigmask); + CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, &sigmask)); + ret = pthread_create(&thread_, nullptr, RunSigintWatchdog, nullptr); + CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr)); + if (ret != 0) { + goto dont_start; + } + has_running_thread_ = true; + + RegisterSignalHandler(SIGINT, HandleSignal); +#else + SetConsoleCtrlHandler(WinCtrlCHandlerRoutine, TRUE); +#endif + + dont_start: + uv_mutex_unlock(&mutex_); + return ret; +} + + +bool SigintWatchdogHelper::Stop() { + uv_mutex_lock(&mutex_); + uv_mutex_lock(&list_mutex_); + + bool had_pending_signal = has_pending_signal_; + + if (--start_stop_count_ > 0) { + uv_mutex_unlock(&list_mutex_); + goto dont_stop; + } + +#ifdef __POSIX__ + // Set stopping now because it's only protected by list_mutex_. + stopping_ = true; +#endif + + watchdogs_.clear(); + uv_mutex_unlock(&list_mutex_); + +#ifdef __POSIX__ + if (!has_running_thread_) { + goto dont_stop; + } + + // Wake up the helper thread. + uv_sem_post(&sem_); + + // Wait for the helper thread to finish. + CHECK_EQ(0, pthread_join(thread_, nullptr)); + has_running_thread_ = false; + + RegisterSignalHandler(SIGINT, SignalExit, true); +#else + SetConsoleCtrlHandler(WinCtrlCHandlerRoutine, FALSE); +#endif + + had_pending_signal = has_pending_signal_; + dont_stop: + uv_mutex_unlock(&mutex_); + + has_pending_signal_ = false; + return had_pending_signal; +} + + +void SigintWatchdogHelper::Register(SigintWatchdog* wd) { + uv_mutex_lock(&list_mutex_); + + watchdogs_.push_back(wd); + + uv_mutex_unlock(&list_mutex_); +} + + +void SigintWatchdogHelper::Unregister(SigintWatchdog* wd) { + uv_mutex_lock(&list_mutex_); + + auto it = std::find(watchdogs_.begin(), watchdogs_.end(), wd); + + CHECK_NE(it, watchdogs_.end()); + watchdogs_.erase(it); + + uv_mutex_unlock(&list_mutex_); +} + + +SigintWatchdogHelper::SigintWatchdogHelper() + : start_stop_count_(0), + has_pending_signal_(false) { +#ifdef __POSIX__ + has_running_thread_ = false; + stopping_ = false; + CHECK_EQ(0, uv_sem_init(&sem_, 0)); +#endif + + CHECK_EQ(0, uv_mutex_init(&mutex_)); + CHECK_EQ(0, uv_mutex_init(&list_mutex_)); +}; + + +SigintWatchdogHelper::~SigintWatchdogHelper() { + start_stop_count_ = 0; + Stop(); + +#ifdef __POSIX__ + CHECK_EQ(has_running_thread_, false); + uv_sem_destroy(&sem_); +#endif + + uv_mutex_destroy(&mutex_); + uv_mutex_destroy(&list_mutex_); +} + +SigintWatchdogHelper SigintWatchdogHelper::instance; + } // namespace node diff --git a/src/node_watchdog.h b/src/node_watchdog.h index 36125ac0e1f798..65815e2bcddc30 100644 --- a/src/node_watchdog.h +++ b/src/node_watchdog.h @@ -5,6 +5,11 @@ #include "v8.h" #include "uv.h" +#include <vector> + +#ifdef __POSIX__ +#include <pthread.h> +#endif namespace node { @@ -16,7 +21,7 @@ class Watchdog { void Dispose(); v8::Isolate* isolate() { return isolate_; } - + bool HasTimedOut() { return timed_out_; } private: void Destroy(); @@ -29,9 +34,61 @@ class Watchdog { uv_loop_t* loop_; uv_async_t async_; uv_timer_t timer_; + bool timed_out_; bool destroyed_; }; +class SigintWatchdog { + public: + explicit SigintWatchdog(v8::Isolate* isolate); + ~SigintWatchdog(); + + void Dispose(); + + v8::Isolate* isolate() { return isolate_; } + void HandleSigint(); + private: + void Destroy(); + + v8::Isolate* isolate_; + bool destroyed_; +}; + +class SigintWatchdogHelper { + public: + static SigintWatchdogHelper* GetInstance() { return &instance; } + void Register(SigintWatchdog* watchdog); + void Unregister(SigintWatchdog* watchdog); + + int Start(); + bool Stop(); + private: + SigintWatchdogHelper(); + ~SigintWatchdogHelper(); + + static bool InformWatchdogsAboutSignal(); + static SigintWatchdogHelper instance; + + int start_stop_count_; + + uv_mutex_t mutex_; + uv_mutex_t list_mutex_; + std::vector<SigintWatchdog*> watchdogs_; + bool has_pending_signal_; + +#ifdef __POSIX__ + pthread_t thread_; + uv_sem_t sem_; + bool has_running_thread_; + bool stopping_; + + static void* RunSigintWatchdog(void* arg); + static void HandleSignal(int signum); +#else + static BOOL WINAPI WinCtrlCHandlerRoutine(DWORD dwCtrlType); +#endif +}; + } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/test/message/eval_messages.out b/test/message/eval_messages.out index f1b9273342dbc2..7fbf51f7e0f2f7 100644 --- a/test/message/eval_messages.out +++ b/test/message/eval_messages.out @@ -16,6 +16,7 @@ throw new Error("hello") ^ Error: hello at [eval]:1:7 + at ContextifyScript.Script.runInThisContext (vm.js:*) at Object.exports.runInThisContext (vm.js:*) at Object.<anonymous> ([eval]-wrapper:*:*) at Module._compile (module.js:*:*) @@ -27,6 +28,7 @@ throw new Error("hello") ^ Error: hello at [eval]:1:7 + at ContextifyScript.Script.runInThisContext (vm.js:*) at Object.exports.runInThisContext (vm.js:*) at Object.<anonymous> ([eval]-wrapper:*:*) at Module._compile (module.js:*:*) @@ -39,6 +41,7 @@ var x = 100; y = x; ^ ReferenceError: y is not defined at [eval]:1:16 + at ContextifyScript.Script.runInThisContext (vm.js:*) at Object.exports.runInThisContext (vm.js:*) at Object.<anonymous> ([eval]-wrapper:*:*) at Module._compile (module.js:*:*) diff --git a/test/message/stdin_messages.out b/test/message/stdin_messages.out index 1d860918df2fcd..62c43b02a8e81b 100644 --- a/test/message/stdin_messages.out +++ b/test/message/stdin_messages.out @@ -18,6 +18,7 @@ throw new Error("hello") ^ Error: hello at [stdin]:1:* + at ContextifyScript.Script.runInThisContext (vm.js:*) at Object.exports.runInThisContext (vm.js:*) at Object.<anonymous> ([stdin]-wrapper:*:*) at Module._compile (module.js:*:*) @@ -30,6 +31,7 @@ throw new Error("hello") ^ Error: hello at [stdin]:1:* + at ContextifyScript.Script.runInThisContext (vm.js:*) at Object.exports.runInThisContext (vm.js:*) at Object.<anonymous> ([stdin]-wrapper:*:*) at Module._compile (module.js:*:*) @@ -43,6 +45,7 @@ var x = 100; y = x; ^ ReferenceError: y is not defined at [stdin]:1:16 + at ContextifyScript.Script.runInThisContext (vm.js:*) at Object.exports.runInThisContext (vm.js:*) at Object.<anonymous> ([stdin]-wrapper:*:*) at Module._compile (module.js:*:*) diff --git a/test/message/undefined_reference_in_new_context.out b/test/message/undefined_reference_in_new_context.out index d209eb215ca1c3..04b9a21acdeb1f 100644 --- a/test/message/undefined_reference_in_new_context.out +++ b/test/message/undefined_reference_in_new_context.out @@ -5,6 +5,7 @@ foo.bar = 5; ^ ReferenceError: foo is not defined at evalmachine.<anonymous>:1:1 + at ContextifyScript.Script.runInContext (vm.js:*) at ContextifyScript.Script.runInNewContext (vm.js:*) at Object.exports.runInNewContext (vm.js:*) at Object.<anonymous> (*test*message*undefined_reference_in_new_context.js:*) @@ -13,4 +14,3 @@ ReferenceError: foo is not defined at Module.load (module.js:*) at tryModuleLoad (module.js:*:*) at Function.Module._load (module.js:*:*) - at Module.runMain (module.js:*:*) diff --git a/test/message/vm_display_runtime_error.out b/test/message/vm_display_runtime_error.out index 9c14783472b000..b7fe798cd29adb 100644 --- a/test/message/vm_display_runtime_error.out +++ b/test/message/vm_display_runtime_error.out @@ -5,6 +5,7 @@ throw new Error("boo!") ^ Error: boo! at test.vm:1:7 + at ContextifyScript.Script.runInThisContext (vm.js:*) at Object.exports.runInThisContext (vm.js:*) at Object.<anonymous> (*test*message*vm_display_runtime_error.js:*) at Module._compile (module.js:*) @@ -13,4 +14,3 @@ Error: boo! at tryModuleLoad (module.js:*:*) at Function.Module._load (module.js:*) at Module.runMain (module.js:*) - at run (bootstrap_node.js:*) diff --git a/test/message/vm_dont_display_runtime_error.out b/test/message/vm_dont_display_runtime_error.out index 6cc464505f35ba..e0795d1f535e35 100644 --- a/test/message/vm_dont_display_runtime_error.out +++ b/test/message/vm_dont_display_runtime_error.out @@ -5,6 +5,7 @@ throw new Error("boo!") ^ Error: boo! at test.vm:1:7 + at ContextifyScript.Script.runInThisContext (vm.js:*) at Object.exports.runInThisContext (vm.js:*) at Object.<anonymous> (*test*message*vm_dont_display_runtime_error.js:*) at Module._compile (module.js:*) @@ -13,4 +14,3 @@ Error: boo! at tryModuleLoad (module.js:*:*) at Function.Module._load (module.js:*) at Module.runMain (module.js:*) - at run (bootstrap_node.js:*) diff --git a/test/parallel/test-util-sigint-watchdog.js b/test/parallel/test-util-sigint-watchdog.js new file mode 100644 index 00000000000000..2f95286a5e72e0 --- /dev/null +++ b/test/parallel/test-util-sigint-watchdog.js @@ -0,0 +1,53 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const binding = process.binding('util'); + +if (process.platform === 'win32') { + // No way to send CTRL_C_EVENT to processes from JS right now. + common.skip('platform not supported'); + return; +} + +[(next) => { + // Test with no signal observed. + binding.startSigintWatchdog(); + const hadPendingSignals = binding.stopSigintWatchdog(); + assert.strictEqual(hadPendingSignals, false); + next(); +}, +(next) => { + // Test with one call to the watchdog, one signal. + binding.startSigintWatchdog(); + process.kill(process.pid, 'SIGINT'); + setTimeout(common.mustCall(() => { + const hadPendingSignals = binding.stopSigintWatchdog(); + assert.strictEqual(hadPendingSignals, true); + next(); + }), common.platformTimeout(100)); +}, +(next) => { + // Nested calls are okay. + binding.startSigintWatchdog(); + binding.startSigintWatchdog(); + process.kill(process.pid, 'SIGINT'); + setTimeout(common.mustCall(() => { + const hadPendingSignals1 = binding.stopSigintWatchdog(); + const hadPendingSignals2 = binding.stopSigintWatchdog(); + assert.strictEqual(hadPendingSignals1, true); + assert.strictEqual(hadPendingSignals2, false); + next(); + }), common.platformTimeout(100)); +}, +() => { + // Signal comes in after first call to stop. + binding.startSigintWatchdog(); + binding.startSigintWatchdog(); + const hadPendingSignals1 = binding.stopSigintWatchdog(); + process.kill(process.pid, 'SIGINT'); + setTimeout(common.mustCall(() => { + const hadPendingSignals2 = binding.stopSigintWatchdog(); + assert.strictEqual(hadPendingSignals1, false); + assert.strictEqual(hadPendingSignals2, true); + }), common.platformTimeout(100)); +}].reduceRight((a, b) => common.mustCall(b).bind(null, a))(); diff --git a/test/parallel/test-vm-sigint-existing-handler.js b/test/parallel/test-vm-sigint-existing-handler.js new file mode 100644 index 00000000000000..e86bbeec0b5e9b --- /dev/null +++ b/test/parallel/test-vm-sigint-existing-handler.js @@ -0,0 +1,77 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const spawn = require('child_process').spawn; + +if (process.platform === 'win32') { + // No way to send CTRL_C_EVENT to processes from JS right now. + common.skip('platform not supported'); + return; +} + +if (process.argv[2] === 'child') { + const parent = +process.env.REPL_TEST_PPID; + assert.ok(parent); + + let firstHandlerCalled = 0; + process.on('SIGINT', common.mustCall(() => { + firstHandlerCalled++; + // Handler attached _before_ execution. + }, 2)); + + let onceHandlerCalled = 0; + process.once('SIGINT', common.mustCall(() => { + onceHandlerCalled++; + // Handler attached _before_ execution. + })); + + assert.throws(() => { + vm.runInThisContext(`process.kill(${parent}, 'SIGUSR2'); while(true) {}`, { + breakOnSigint: true + }); + }, /Script execution interrupted/); + + assert.strictEqual(firstHandlerCalled, 0); + assert.strictEqual(onceHandlerCalled, 0); + + // Keep the process alive for a while so that the second SIGINT can be caught. + const timeout = setTimeout(() => {}, 1000); + + let afterHandlerCalled = 0; + + process.on('SIGINT', common.mustCall(() => { + // Handler attached _after_ execution. + if (afterHandlerCalled++ == 0) { + // The first time it just bounces back to check that the `once()` + // handler is not called the second time. + process.kill(parent, 'SIGUSR2'); + return; + } + + assert.strictEqual(onceHandlerCalled, 1); + assert.strictEqual(firstHandlerCalled, 2); + timeout.unref(); + }, 2)); + + process.kill(parent, 'SIGUSR2'); + + return; +} + +process.env.REPL_TEST_PPID = process.pid; +const child = spawn(process.execPath, [ __filename, 'child' ], { + stdio: [null, 'inherit', 'inherit'] +}); + +process.on('SIGUSR2', common.mustCall(() => { + // First kill() breaks the while(true) loop, second one invokes the real + // signal handlers. + process.kill(child.pid, 'SIGINT'); +}, 3)); + +child.on('close', function(code, signal) { + assert.strictEqual(signal, null); + assert.strictEqual(code, 0); +}); diff --git a/test/parallel/test-vm-sigint.js b/test/parallel/test-vm-sigint.js new file mode 100644 index 00000000000000..5260a8464f1da8 --- /dev/null +++ b/test/parallel/test-vm-sigint.js @@ -0,0 +1,39 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const spawn = require('child_process').spawn; + +if (process.platform === 'win32') { + // No way to send CTRL_C_EVENT to processes from JS right now. + common.skip('platform not supported'); + return; +} + +if (process.argv[2] === 'child') { + const parent = +process.env.REPL_TEST_PPID; + assert.ok(parent); + + assert.throws(() => { + vm.runInThisContext(`process.kill(${parent}, "SIGUSR2"); while(true) {}`, { + breakOnSigint: true + }); + }, /Script execution interrupted/); + + return; +} + +process.env.REPL_TEST_PPID = process.pid; +const child = spawn(process.execPath, [ __filename, 'child' ], { + stdio: [null, 'pipe', 'inherit'] +}); + +process.on('SIGUSR2', common.mustCall(() => { + process.kill(child.pid, 'SIGINT'); +})); + +child.on('close', function(code, signal) { + assert.strictEqual(signal, null); + assert.strictEqual(code, 0); +}); From 72d659a000917e848f49c81e63ee2b0524b73de0 Mon Sep 17 00:00:00 2001 From: Anna Henningsen <anna@addaleax.net> Date: Sun, 8 May 2016 03:29:43 +0200 Subject: [PATCH 021/131] readline: return old status from _setRawMode Return the previous raw mode setting from the internal `_setRawMode` so that is easier to reset it to its original state later. PR-URL: https://github.com/nodejs/node/pull/6635 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> --- lib/readline.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/readline.js b/lib/readline.js index cd78b75ac31454..46982bca5feaa8 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -183,9 +183,13 @@ Interface.prototype.setPrompt = function(prompt) { Interface.prototype._setRawMode = function(mode) { + const wasInRawMode = this.input.isRaw; + if (typeof this.input.setRawMode === 'function') { - return this.input.setRawMode(mode); + this.input.setRawMode(mode); } + + return wasInRawMode; }; From da8e510ee065fe50ee9a63e1e9e20b2ba7d5a85a Mon Sep 17 00:00:00 2001 From: Anna Henningsen <anna@addaleax.net> Date: Sun, 8 May 2016 03:30:23 +0200 Subject: [PATCH 022/131] repl: break on sigint/ctrl+c Adds the ability to stop execution of the current REPL command when receiving SIGINT. This applies only to the default eval function. Fixes: https://github.com/nodejs/node/issues/6612 PR-URL: https://github.com/nodejs/node/pull/6635 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> --- doc/api/repl.md | 3 ++ lib/internal/repl.js | 3 +- lib/repl.js | 51 +++++++++++++++++-- test/parallel/test-repl-sigint-nested-eval.js | 50 ++++++++++++++++++ test/parallel/test-repl-sigint.js | 50 ++++++++++++++++++ 5 files changed, 151 insertions(+), 6 deletions(-) create mode 100644 test/parallel/test-repl-sigint-nested-eval.js create mode 100644 test/parallel/test-repl-sigint.js diff --git a/doc/api/repl.md b/doc/api/repl.md index 0c3c1b10e9ab1c..7524a373eea366 100644 --- a/doc/api/repl.md +++ b/doc/api/repl.md @@ -390,6 +390,9 @@ added: v0.1.91 equivalent to prefacing every repl statement with `'use strict'`. * `repl.REPL_MODE_MAGIC` - attempt to evaluates expressions in default mode. If expressions fail to parse, re-try in strict mode. + * `breakEvalOnSigint` - Stop evaluating the current piece of code when + `SIGINT` is received, i.e. `Ctrl+C` is pressed. This cannot be used together + with a custom `eval` function. Defaults to `false`. The `repl.start()` method creates and starts a `repl.REPLServer` instance. diff --git a/lib/internal/repl.js b/lib/internal/repl.js index b72741609ba87e..dd14f42fa5273c 100644 --- a/lib/internal/repl.js +++ b/lib/internal/repl.js @@ -22,7 +22,8 @@ function createRepl(env, opts, cb) { opts = opts || { ignoreUndefined: false, terminal: process.stdout.isTTY, - useGlobal: true + useGlobal: true, + breakEvalOnSigint: true }; if (parseInt(env.NODE_NO_READLINE)) { diff --git a/lib/repl.js b/lib/repl.js index 387e3b5446f678..db5754ec041196 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -24,6 +24,7 @@ const internalModule = require('internal/module'); const internalUtil = require('internal/util'); const util = require('util'); +const utilBinding = process.binding('util'); const inherits = util.inherits; const Stream = require('stream'); const vm = require('vm'); @@ -178,7 +179,7 @@ function REPLServer(prompt, replMode); } - var options, input, output, dom; + var options, input, output, dom, breakEvalOnSigint; if (prompt !== null && typeof prompt === 'object') { // an options object was given options = prompt; @@ -191,10 +192,17 @@ function REPLServer(prompt, prompt = options.prompt; dom = options.domain; replMode = options.replMode; + breakEvalOnSigint = options.breakEvalOnSigint; } else { options = {}; } + if (breakEvalOnSigint && eval_) { + // Allowing this would not reflect user expectations. + // breakEvalOnSigint affects only the behaviour of the default eval(). + throw new Error('Cannot specify both breakEvalOnSigint and eval for REPL'); + } + var self = this; self._domain = dom || domain.create(); @@ -204,6 +212,7 @@ function REPLServer(prompt, self.replMode = replMode || exports.REPL_MODE_SLOPPY; self.underscoreAssigned = false; self.last = undefined; + self.breakEvalOnSigint = !!breakEvalOnSigint; self._inTemplateLiteral = false; @@ -267,14 +276,46 @@ function REPLServer(prompt, regExMatcher.test(savedRegExMatches.join(sep)); if (!err) { + // Unset raw mode during evaluation so that Ctrl+C raises a signal. + let previouslyInRawMode; + if (self.breakEvalOnSigint) { + // Start the SIGINT watchdog before entering raw mode so that a very + // quick Ctrl+C doesn’t lead to aborting the process completely. + utilBinding.startSigintWatchdog(); + previouslyInRawMode = self._setRawMode(false); + } + try { - if (self.useGlobal) { - result = script.runInThisContext({ displayErrors: false }); - } else { - result = script.runInContext(context, { displayErrors: false }); + try { + const scriptOptions = { + displayErrors: false, + breakOnSigint: self.breakEvalOnSigint + }; + + if (self.useGlobal) { + result = script.runInThisContext(scriptOptions); + } else { + result = script.runInContext(context, scriptOptions); + } + } finally { + if (self.breakEvalOnSigint) { + // Reset terminal mode to its previous value. + self._setRawMode(previouslyInRawMode); + + // Returns true if there were pending SIGINTs *after* the script + // has terminated without being interrupted itself. + if (utilBinding.stopSigintWatchdog()) { + self.emit('SIGINT'); + } + } } } catch (e) { err = e; + if (err.message === 'Script execution interrupted.') { + // The stack trace for this case is not very useful anyway. + Object.defineProperty(err, 'stack', { value: '' }); + } + if (err && process.domain) { debug('not recoverable, send to domain'); process.domain.emit('error', err); diff --git a/test/parallel/test-repl-sigint-nested-eval.js b/test/parallel/test-repl-sigint-nested-eval.js new file mode 100644 index 00000000000000..288c4bceebcd61 --- /dev/null +++ b/test/parallel/test-repl-sigint-nested-eval.js @@ -0,0 +1,50 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const spawn = require('child_process').spawn; + +if (process.platform === 'win32') { + // No way to send CTRL_C_EVENT to processes from JS right now. + common.skip('platform not supported'); + return; +} + +process.env.REPL_TEST_PPID = process.pid; +const child = spawn(process.execPath, [ '-i' ], { + stdio: [null, null, 2] +}); + +let stdout = ''; +child.stdout.setEncoding('utf8'); +child.stdout.pipe(process.stdout); +child.stdout.on('data', function(c) { + stdout += c; +}); + +child.stdin.write = ((original) => { + return (chunk) => { + process.stderr.write(chunk); + return original.call(child.stdin, chunk); + }; +})(child.stdin.write); + +child.stdout.once('data', common.mustCall(() => { + process.on('SIGUSR2', common.mustCall(() => { + process.kill(child.pid, 'SIGINT'); + child.stdout.once('data', common.mustCall(() => { + // Make sure REPL still works. + child.stdin.end('"foobar"\n'); + })); + })); + + child.stdin.write('process.kill(+process.env.REPL_TEST_PPID, "SIGUSR2");' + + 'vm.runInThisContext("while(true){}", ' + + '{ breakOnSigint: true });\n'); +})); + +child.on('close', function(code) { + assert.strictEqual(code, 0); + assert.notStrictEqual(stdout.indexOf('Script execution interrupted.'), -1); + assert.notStrictEqual(stdout.indexOf('foobar'), -1); +}); diff --git a/test/parallel/test-repl-sigint.js b/test/parallel/test-repl-sigint.js new file mode 100644 index 00000000000000..6b342ce8612d28 --- /dev/null +++ b/test/parallel/test-repl-sigint.js @@ -0,0 +1,50 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const spawn = require('child_process').spawn; + +if (process.platform === 'win32') { + // No way to send CTRL_C_EVENT to processes from JS right now. + common.skip('platform not supported'); + return; +} + +process.env.REPL_TEST_PPID = process.pid; +const child = spawn(process.execPath, [ '-i' ], { + stdio: [null, null, 2] +}); + +let stdout = ''; +child.stdout.setEncoding('utf8'); +child.stdout.pipe(process.stdout); +child.stdout.on('data', function(c) { + stdout += c; +}); + +child.stdin.write = ((original) => { + return (chunk) => { + process.stderr.write(chunk); + return original.call(child.stdin, chunk); + }; +})(child.stdin.write); + +child.stdout.once('data', common.mustCall(() => { + process.on('SIGUSR2', common.mustCall(() => { + process.kill(child.pid, 'SIGINT'); + child.stdout.once('data', common.mustCall(() => { + // Make sure state from before the interruption is still available. + child.stdin.end('a*2*3*7\n'); + })); + })); + + child.stdin.write('a = 1001;' + + 'process.kill(+process.env.REPL_TEST_PPID, "SIGUSR2");' + + 'while(true){}\n'); +})); + +child.on('close', function(code) { + assert.strictEqual(code, 0); + assert.notStrictEqual(stdout.indexOf('Script execution interrupted.\n'), -1); + assert.notStrictEqual(stdout.indexOf('42042\n'), -1); +}); From 059335180dfb29affb2a9ca25667010d5ed6520d Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Sat, 18 Jun 2016 01:39:05 +0200 Subject: [PATCH 023/131] src: use RAII for mutexes and condition variables We will be introducing many more critical sections in the upcoming multi-isolate changes, so let's make manual synchronization a thing of the past. PR-URL: https://github.com/nodejs/node/pull/7334 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- node.gyp | 1 + src/debug-agent.cc | 15 +--- src/debug-agent.h | 3 +- src/inspector_agent.cc | 40 ++++----- src/node.cc | 30 +++---- src/node_crypto.cc | 16 ++-- src/node_mutex.h | 187 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 227 insertions(+), 65 deletions(-) create mode 100644 src/node_mutex.h diff --git a/node.gyp b/node.gyp index 0656691c24461a..c3f591351d52ce 100644 --- a/node.gyp +++ b/node.gyp @@ -175,6 +175,7 @@ 'src/node_http_parser.h', 'src/node_internals.h', 'src/node_javascript.h', + 'src/node_mutex.h', 'src/node_root_certs.h', 'src/node_version.h', 'src/node_watchdog.h', diff --git a/src/debug-agent.cc b/src/debug-agent.cc index e4e0ea061bd30f..a67ce96811e050 100644 --- a/src/debug-agent.cc +++ b/src/debug-agent.cc @@ -55,13 +55,7 @@ Agent::Agent(Environment* env) : state_(kNone), parent_env_(env), child_env_(nullptr), dispatch_handler_(nullptr) { - int err; - - err = uv_sem_init(&start_sem_, 0); - CHECK_EQ(err, 0); - - err = uv_mutex_init(&message_mutex_); - CHECK_EQ(err, 0); + CHECK_EQ(0, uv_sem_init(&start_sem_, 0)); } @@ -69,7 +63,6 @@ Agent::~Agent() { Stop(); uv_sem_destroy(&start_sem_); - uv_mutex_destroy(&message_mutex_); while (AgentMessage* msg = messages_.PopFront()) delete msg; @@ -276,7 +269,7 @@ void Agent::ChildSignalCb(uv_async_t* signal) { HandleScope scope(isolate); Local<Object> api = PersistentToLocal(isolate, a->api_); - uv_mutex_lock(&a->message_mutex_); + Mutex::ScopedLock scoped_lock(a->message_mutex_); while (AgentMessage* msg = a->messages_.PopFront()) { // Time to close everything if (msg->data() == nullptr) { @@ -307,14 +300,12 @@ void Agent::ChildSignalCb(uv_async_t* signal) { argv); delete msg; } - uv_mutex_unlock(&a->message_mutex_); } void Agent::EnqueueMessage(AgentMessage* message) { - uv_mutex_lock(&message_mutex_); + Mutex::ScopedLock scoped_lock(message_mutex_); messages_.PushBack(message); - uv_mutex_unlock(&message_mutex_); uv_async_send(&child_signal_); } diff --git a/src/debug-agent.h b/src/debug-agent.h index 6b151c4587441b..45aa07bf2436b7 100644 --- a/src/debug-agent.h +++ b/src/debug-agent.h @@ -24,6 +24,7 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#include "node_mutex.h" #include "util.h" #include "util-inl.h" #include "uv.h" @@ -117,7 +118,7 @@ class Agent { bool wait_; uv_sem_t start_sem_; - uv_mutex_t message_mutex_; + node::Mutex message_mutex_; uv_async_t child_signal_; uv_thread_t thread_; diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index b0451db75f7dac..e727bf01a98225 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -4,6 +4,7 @@ #include "env.h" #include "env-inl.h" #include "node.h" +#include "node_mutex.h" #include "node_version.h" #include "v8-platform.h" #include "util.h" @@ -189,9 +190,9 @@ class AgentImpl { void Write(const std::string& message); uv_sem_t start_sem_; - uv_cond_t pause_cond_; - uv_mutex_t queue_lock_; - uv_mutex_t pause_lock_; + ConditionVariable pause_cond_; + Mutex pause_lock_; + Mutex queue_lock_; uv_thread_t thread_; uv_loop_t child_loop_; @@ -290,9 +291,10 @@ class V8NodeInspector : public blink::V8Inspector { terminated_ = false; running_nested_loop_ = true; do { - uv_mutex_lock(&agent_->pause_lock_); - uv_cond_wait(&agent_->pause_cond_, &agent_->pause_lock_); - uv_mutex_unlock(&agent_->pause_lock_); + { + Mutex::ScopedLock scoped_lock(agent_->pause_lock_); + agent_->pause_cond_.Wait(scoped_lock); + } while (v8::platform::PumpMessageLoop(platform_, isolate_)) {} } while (!terminated_); @@ -321,9 +323,7 @@ AgentImpl::AgentImpl(Environment* env) : port_(0), inspector_(nullptr), platform_(nullptr), dispatching_messages_(false) { - int err; - err = uv_sem_init(&start_sem_, 0); - CHECK_EQ(err, 0); + CHECK_EQ(0, uv_sem_init(&start_sem_, 0)); memset(&data_written_, 0, sizeof(data_written_)); memset(&io_thread_req_, 0, sizeof(io_thread_req_)); } @@ -331,9 +331,6 @@ AgentImpl::AgentImpl(Environment* env) : port_(0), AgentImpl::~AgentImpl() { if (!inspector_) return; - uv_mutex_destroy(&queue_lock_); - uv_mutex_destroy(&pause_lock_); - uv_cond_destroy(&pause_cond_); uv_close(reinterpret_cast<uv_handle_t*>(&data_written_), nullptr); } @@ -349,12 +346,6 @@ void AgentImpl::Start(v8::Platform* platform, int port, bool wait) { CHECK_EQ(err, 0); err = uv_async_init(env->event_loop(), &data_written_, nullptr); CHECK_EQ(err, 0); - err = uv_mutex_init(&queue_lock_); - CHECK_EQ(err, 0); - err = uv_mutex_init(&pause_lock_); - CHECK_EQ(err, 0); - err = uv_cond_init(&pause_cond_); - CHECK_EQ(err, 0); uv_unref(reinterpret_cast<uv_handle_t*>(&data_written_)); @@ -441,6 +432,7 @@ void AgentImpl::OnRemoteDataIO(uv_stream_t* stream, const uv_buf_t* b) { inspector_socket_t* socket = static_cast<inspector_socket_t*>(stream->data); AgentImpl* agent = static_cast<AgentImpl*>(socket->data); + Mutex::ScopedLock scoped_lock(agent->pause_lock_); if (read > 0) { std::string str(b->base, read); agent->PushPendingMessage(&agent->message_queue_, str); @@ -470,21 +462,19 @@ void AgentImpl::OnRemoteDataIO(uv_stream_t* stream, } DisconnectAndDisposeIO(socket); } - uv_cond_broadcast(&agent->pause_cond_); + agent->pause_cond_.Broadcast(scoped_lock); } void AgentImpl::PushPendingMessage(std::vector<std::string>* queue, - const std::string& message) { - uv_mutex_lock(&queue_lock_); + const std::string& message) { + Mutex::ScopedLock scoped_lock(queue_lock_); queue->push_back(message); - uv_mutex_unlock(&queue_lock_); } void AgentImpl::SwapBehindLock(std::vector<std::string> AgentImpl::*queue, - std::vector<std::string>* output) { - uv_mutex_lock(&queue_lock_); + std::vector<std::string>* output) { + Mutex::ScopedLock scoped_lock(queue_lock_); (this->*queue).swap(*output); - uv_mutex_unlock(&queue_lock_); } // static diff --git a/src/node.cc b/src/node.cc index bc48feae04e1e5..3a31242957a044 100644 --- a/src/node.cc +++ b/src/node.cc @@ -182,7 +182,7 @@ static double prog_start_time; static bool debugger_running; static uv_async_t dispatch_debug_messages_async; -static uv_mutex_t node_isolate_mutex; +static Mutex node_isolate_mutex; static v8::Isolate* node_isolate; static v8::Platform* default_platform; @@ -3782,18 +3782,17 @@ static void EnableDebug(Environment* env) { // Called from an arbitrary thread. static void TryStartDebugger() { - uv_mutex_lock(&node_isolate_mutex); + Mutex::ScopedLock scoped_lock(node_isolate_mutex); if (auto isolate = node_isolate) { v8::Debug::DebugBreak(isolate); uv_async_send(&dispatch_debug_messages_async); } - uv_mutex_unlock(&node_isolate_mutex); } // Called from the main thread. static void DispatchDebugMessagesAsyncCallback(uv_async_t* handle) { - uv_mutex_lock(&node_isolate_mutex); + Mutex::ScopedLock scoped_lock(node_isolate_mutex); if (auto isolate = node_isolate) { if (debugger_running == false) { fprintf(stderr, "Starting debugger agent.\n"); @@ -3809,7 +3808,6 @@ static void DispatchDebugMessagesAsyncCallback(uv_async_t* handle) { Isolate::Scope isolate_scope(isolate); v8::Debug::ProcessDebugMessages(isolate); } - uv_mutex_unlock(&node_isolate_mutex); } @@ -4143,8 +4141,6 @@ void Init(int* argc, // Make inherited handles noninheritable. uv_disable_stdio_inheritance(); - CHECK_EQ(0, uv_mutex_init(&node_isolate_mutex)); - // init async debug messages dispatching // Main thread uses uv_default_loop uv_async_init(uv_default_loop(), @@ -4428,12 +4424,13 @@ static void StartNodeInstance(void* arg) { #endif Isolate* isolate = Isolate::New(params); - uv_mutex_lock(&node_isolate_mutex); - if (instance_data->is_main()) { - CHECK_EQ(node_isolate, nullptr); - node_isolate = isolate; + { + Mutex::ScopedLock scoped_lock(node_isolate_mutex); + if (instance_data->is_main()) { + CHECK_EQ(node_isolate, nullptr); + node_isolate = isolate; + } } - uv_mutex_unlock(&node_isolate_mutex); if (track_heap_objects) { isolate->GetHeapProfiler()->StartTrackingHeapObjects(true); @@ -4503,10 +4500,11 @@ static void StartNodeInstance(void* arg) { env = nullptr; } - uv_mutex_lock(&node_isolate_mutex); - if (node_isolate == isolate) - node_isolate = nullptr; - uv_mutex_unlock(&node_isolate_mutex); + { + Mutex::ScopedLock scoped_lock(node_isolate_mutex); + if (node_isolate == isolate) + node_isolate = nullptr; + } CHECK_NE(isolate, nullptr); isolate->Dispose(); diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 873fbfb410db6f..0c7ecaf3301cfe 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -115,7 +115,7 @@ static X509_NAME *cnnic_ev_name = d2i_X509_NAME(nullptr, &cnnic_ev_p, sizeof(CNNIC_EV_ROOT_CA_SUBJECT_DATA)-1); -static uv_mutex_t* locks; +static Mutex* mutexes; const char* const root_certs[] = { #include "node_root_certs.h" // NOLINT(build/include_order) @@ -182,14 +182,7 @@ static void crypto_threadid_cb(CRYPTO_THREADID* tid) { static void crypto_lock_init(void) { - int i, n; - - n = CRYPTO_num_locks(); - locks = new uv_mutex_t[n]; - - for (i = 0; i < n; i++) - if (uv_mutex_init(locks + i)) - ABORT(); + mutexes = new Mutex[CRYPTO_num_locks()]; } @@ -197,10 +190,11 @@ static void crypto_lock_cb(int mode, int n, const char* file, int line) { CHECK(!(mode & CRYPTO_LOCK) ^ !(mode & CRYPTO_UNLOCK)); CHECK(!(mode & CRYPTO_READ) ^ !(mode & CRYPTO_WRITE)); + auto mutex = &mutexes[n]; if (mode & CRYPTO_LOCK) - uv_mutex_lock(locks + n); + mutex->Lock(); else - uv_mutex_unlock(locks + n); + mutex->Unlock(); } diff --git a/src/node_mutex.h b/src/node_mutex.h new file mode 100644 index 00000000000000..9e1d31654f7dbf --- /dev/null +++ b/src/node_mutex.h @@ -0,0 +1,187 @@ +#ifndef SRC_NODE_MUTEX_H_ +#define SRC_NODE_MUTEX_H_ + +#include "util.h" +#include "uv.h" + +namespace node { + +template <typename Traits> class ConditionVariableBase; +template <typename Traits> class MutexBase; +struct LibuvMutexTraits; + +using ConditionVariable = ConditionVariableBase<LibuvMutexTraits>; +using Mutex = MutexBase<LibuvMutexTraits>; + +template <typename Traits> +class MutexBase { + public: + inline MutexBase(); + inline ~MutexBase(); + inline void Lock(); + inline void Unlock(); + + class ScopedLock; + class ScopedUnlock; + + class ScopedLock { + public: + inline explicit ScopedLock(const MutexBase& mutex); + inline explicit ScopedLock(const ScopedUnlock& scoped_unlock); + inline ~ScopedLock(); + + private: + template <typename> friend class ConditionVariableBase; + friend class ScopedUnlock; + const MutexBase& mutex_; + DISALLOW_COPY_AND_ASSIGN(ScopedLock); + }; + + class ScopedUnlock { + public: + inline explicit ScopedUnlock(const ScopedLock& scoped_lock); + inline ~ScopedUnlock(); + + private: + friend class ScopedLock; + const MutexBase& mutex_; + DISALLOW_COPY_AND_ASSIGN(ScopedUnlock); + }; + + private: + template <typename> friend class ConditionVariableBase; + mutable typename Traits::MutexT mutex_; + DISALLOW_COPY_AND_ASSIGN(MutexBase); +}; + +template <typename Traits> +class ConditionVariableBase { + public: + using ScopedLock = typename MutexBase<Traits>::ScopedLock; + + inline ConditionVariableBase(); + inline ~ConditionVariableBase(); + inline void Broadcast(const ScopedLock&); + inline void Signal(const ScopedLock&); + inline void Wait(const ScopedLock& scoped_lock); + + private: + typename Traits::CondT cond_; + DISALLOW_COPY_AND_ASSIGN(ConditionVariableBase); +}; + +struct LibuvMutexTraits { + using CondT = uv_cond_t; + using MutexT = uv_mutex_t; + + static inline int cond_init(CondT* cond) { + return uv_cond_init(cond); + } + + static inline int mutex_init(MutexT* mutex) { + return uv_mutex_init(mutex); + } + + static inline void cond_broadcast(CondT* cond) { + uv_cond_broadcast(cond); + } + + static inline void cond_destroy(CondT* cond) { + uv_cond_destroy(cond); + } + + static inline void cond_signal(CondT* cond) { + uv_cond_signal(cond); + } + + static inline void cond_wait(CondT* cond, MutexT* mutex) { + uv_cond_wait(cond, mutex); + } + + static inline void mutex_destroy(MutexT* mutex) { + uv_mutex_destroy(mutex); + } + + static inline void mutex_lock(MutexT* mutex) { + uv_mutex_lock(mutex); + } + + static inline void mutex_unlock(MutexT* mutex) { + uv_mutex_unlock(mutex); + } +}; + +template <typename Traits> +ConditionVariableBase<Traits>::ConditionVariableBase() { + CHECK_EQ(0, Traits::cond_init(&cond_)); +} + +template <typename Traits> +ConditionVariableBase<Traits>::~ConditionVariableBase() { + Traits::cond_destroy(&cond_); +} + +template <typename Traits> +void ConditionVariableBase<Traits>::Broadcast(const ScopedLock&) { + Traits::cond_broadcast(&cond_); +} + +template <typename Traits> +void ConditionVariableBase<Traits>::Signal(const ScopedLock&) { + Traits::cond_signal(&cond_); +} + +template <typename Traits> +void ConditionVariableBase<Traits>::Wait(const ScopedLock& scoped_lock) { + Traits::cond_wait(&cond_, &scoped_lock.mutex_.mutex_); +} + +template <typename Traits> +MutexBase<Traits>::MutexBase() { + CHECK_EQ(0, Traits::mutex_init(&mutex_)); +} + +template <typename Traits> +MutexBase<Traits>::~MutexBase() { + Traits::mutex_destroy(&mutex_); +} + +template <typename Traits> +void MutexBase<Traits>::Lock() { + Traits::mutex_lock(&mutex_); +} + +template <typename Traits> +void MutexBase<Traits>::Unlock() { + Traits::mutex_unlock(&mutex_); +} + +template <typename Traits> +MutexBase<Traits>::ScopedLock::ScopedLock(const MutexBase& mutex) + : mutex_(mutex) { + Traits::mutex_lock(&mutex_.mutex_); +} + +template <typename Traits> +MutexBase<Traits>::ScopedLock::ScopedLock(const ScopedUnlock& scoped_unlock) + : MutexBase(scoped_unlock.mutex_) {} + +template <typename Traits> +MutexBase<Traits>::ScopedLock::~ScopedLock() { + Traits::mutex_unlock(&mutex_.mutex_); +} + +template <typename Traits> +MutexBase<Traits>::ScopedUnlock::ScopedUnlock(const ScopedLock& scoped_lock) + : mutex_(scoped_lock.mutex_) { + Traits::mutex_unlock(&mutex_.mutex_); +} + +template <typename Traits> +MutexBase<Traits>::ScopedUnlock::~ScopedUnlock() { + Traits::mutex_lock(&mutex_.mutex_); +} + +} // namespace node + +#endif // SRC_NODE_MUTEX_H_ From 2c7804ad9eedad7f5e996a06af4c59f8a4bd7103 Mon Sep 17 00:00:00 2001 From: James M Snell <jasnell@gmail.com> Date: Wed, 8 Jun 2016 08:18:26 -0700 Subject: [PATCH 024/131] crypto,tls: perf improvements for crypto and tls getCiphers Improve performance of crypto.getCiphers, getHashes, getCurves and tls.getCiphers by consolidating filterDuplicates logic, adding caching of output, and streamlining filterDuplicates implementation. Benchmarks: crypto.getCiphers n=1 v6.2.1 = 2559.3, new = 15890 ...... -83.89% crypto.getCiphers n=5000 v6.2.1 = 3516.3, new = 24203000 ... -99.99% tls.getCiphers n=1 v6.2.1 = 3405.3, new = 14877 ...... -77.11% tls.getCiphers n=5000 v6.2.1 = 6074.4, new = 24202000 ... -99.97% PR-URL: https://github.com/nodejs/node/pull/7225 Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com> Reviewed-By: Brian White <mscdex@mscdex.net> Conflicts: lib/internal/util.js --- benchmark/crypto/get-ciphers.js | 20 ++++++++++++++++++ lib/crypto.js | 36 +++++++++------------------------ lib/internal/util.js | 31 ++++++++++++++++++++++++++++ lib/tls.js | 16 +++++---------- 4 files changed, 65 insertions(+), 38 deletions(-) create mode 100644 benchmark/crypto/get-ciphers.js diff --git a/benchmark/crypto/get-ciphers.js b/benchmark/crypto/get-ciphers.js new file mode 100644 index 00000000000000..257c9af2fd531e --- /dev/null +++ b/benchmark/crypto/get-ciphers.js @@ -0,0 +1,20 @@ +'use strict'; + +const common = require('../common.js'); + +const bench = common.createBenchmark(main, { + n: [1, 5000], + v: ['crypto', 'tls'] +}); + +function main(conf) { + const n = +conf.n; + const v = conf.v; + const method = require(v).getCiphers; + var i = 0; + + common.v8ForceOptimization(method); + bench.start(); + for (; i < n; i++) method(); + bench.end(n); +} diff --git a/lib/crypto.js b/lib/crypto.js index 4bce3717015340..9ffff06f7f18ed 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -632,41 +632,23 @@ exports.randomBytes = exports.pseudoRandomBytes = randomBytes; exports.rng = exports.prng = randomBytes; -exports.getCiphers = function() { - return filterDuplicates(getCiphers()); -}; - - -exports.getHashes = function() { - return filterDuplicates(getHashes()); -}; +exports.getCiphers = internalUtil.cachedResult(() => { + return internalUtil.filterDuplicateStrings(getCiphers()); +}); +exports.getHashes = internalUtil.cachedResult(() => { + return internalUtil.filterDuplicateStrings(getHashes()); +}); -exports.getCurves = function() { - return filterDuplicates(getCurves()); -}; +exports.getCurves = internalUtil.cachedResult(() => { + return internalUtil.filterDuplicateStrings(getCurves()); +}); Object.defineProperty(exports, 'fips', { get: getFipsCrypto, set: setFipsCrypto }); -function filterDuplicates(names) { - // Drop all-caps names in favor of their lowercase aliases, - // for example, 'sha1' instead of 'SHA1'. - var ctx = {}; - names.forEach(function(name) { - var key = name; - if (/^[0-9A-Z\-]+$/.test(key)) key = key.toLowerCase(); - if (!ctx.hasOwnProperty(key) || ctx[key] < name) - ctx[key] = name; - }); - - return Object.getOwnPropertyNames(ctx).map(function(key) { - return ctx[key]; - }).sort(); -} - // Legacy API Object.defineProperty(exports, 'createCredentials', { configurable: true, diff --git a/lib/internal/util.js b/lib/internal/util.js index 9ecdf17ecda571..6f2af0efb42af2 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -89,3 +89,34 @@ exports.assertCrypto = function(exports) { if (noCrypto) throw new Error('Node.js is not compiled with openssl crypto support'); }; + +// Filters duplicate strings. Used to support functions in crypto and tls +// modules. Implemented specifically to maintain existing behaviors in each. +exports.filterDuplicateStrings = function filterDuplicateStrings(items, low) { + if (!Array.isArray(items)) + return []; + const len = items.length; + if (len <= 1) + return items; + const map = new Map(); + for (var i = 0; i < len; i++) { + const item = items[i]; + const key = item.toLowerCase(); + if (low) { + map.set(key, key); + } else { + if (!map.has(key) || map.get(key) <= item) + map.set(key, item); + } + } + return Array.from(map.values()).sort(); +}; + +exports.cachedResult = function cachedResult(fn) { + var result; + return () => { + if (result === undefined) + result = fn(); + return result; + }; +}; diff --git a/lib/tls.js b/lib/tls.js index 80ea0d76977ecc..695edd8c5a6eb0 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -1,6 +1,7 @@ 'use strict'; -require('internal/util').assertCrypto(exports); +const internalUtil = require('internal/util'); +internalUtil.assertCrypto(exports); const net = require('net'); const url = require('url'); @@ -21,16 +22,9 @@ exports.DEFAULT_CIPHERS = exports.DEFAULT_ECDH_CURVE = 'prime256v1'; -exports.getCiphers = function() { - const names = binding.getSSLCiphers(); - // Drop all-caps names in favor of their lowercase aliases, - var ctx = {}; - names.forEach(function(name) { - if (/^[0-9A-Z\-]+$/.test(name)) name = name.toLowerCase(); - ctx[name] = true; - }); - return Object.getOwnPropertyNames(ctx).sort(); -}; +exports.getCiphers = internalUtil.cachedResult(() => { + return internalUtil.filterDuplicateStrings(binding.getSSLCiphers(), true); +}); // Convert protocols array into valid OpenSSL protocols list // ("\x06spdy/2\x08http/1.1\x08http/1.0") From 77331a7c01020be7acebc4e52db8b4aa0400033a Mon Sep 17 00:00:00 2001 From: James M Snell <jasnell@gmail.com> Date: Wed, 8 Jun 2016 15:47:18 -0700 Subject: [PATCH 025/131] tls: avoid calling Buffer.byteLength multiple times There's no reason to be calling Buffer.byteLength() twice. Small perf improvement Run 1: tls/convertprotocols.js n=1 v6.2.1 = 11852, new = 12204 ...... -2.89% tls/convertprotocols.js n=50000 v6.2.1 = 515660, new = 570610 ..... -9.63% Run 2: tls/convertprotocols.js n=1 v6.2.1 = 11729, new = 12045 ...... -2.62% tls/convertprotocols.js n=50000 v6.2.1 = 512080, new = 637730 ..... -19.70% PR-URL: https://github.com/nodejs/node/pull/7236 Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com> --- benchmark/tls/convertprotocols.js | 20 ++++++++++++++++++++ lib/tls.js | 20 +++++++++++--------- 2 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 benchmark/tls/convertprotocols.js diff --git a/benchmark/tls/convertprotocols.js b/benchmark/tls/convertprotocols.js new file mode 100644 index 00000000000000..32da0fe6fde271 --- /dev/null +++ b/benchmark/tls/convertprotocols.js @@ -0,0 +1,20 @@ +'use strict'; + +const common = require('../common.js'); +const tls = require('tls'); + +const bench = common.createBenchmark(main, { + n: [1, 50000] +}); + +function main(conf) { + const n = +conf.n; + + var i = 0; + var m = {}; + common.v8ForceOptimization( + tls.convertNPNProtocols, ['ABC', 'XYZ123', 'FOO'], m); + bench.start(); + for (; i < n; i++) tls.convertNPNProtocols(['ABC', 'XYZ123', 'FOO'], m); + bench.end(n); +} diff --git a/lib/tls.js b/lib/tls.js index 695edd8c5a6eb0..849daeb07f3456 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -29,17 +29,19 @@ exports.getCiphers = internalUtil.cachedResult(() => { // Convert protocols array into valid OpenSSL protocols list // ("\x06spdy/2\x08http/1.1\x08http/1.0") function convertProtocols(protocols) { - var buff = Buffer.allocUnsafe(protocols.reduce(function(p, c) { - return p + 1 + Buffer.byteLength(c); + const lens = Array(protocols.length); + const buff = Buffer.allocUnsafe(protocols.reduce((p, c, i) => { + var len = Buffer.byteLength(c); + lens[i] = len; + return p + 1 + len; }, 0)); - protocols.reduce(function(offset, c) { - var clen = Buffer.byteLength(c); - buff[offset] = clen; - buff.write(c, offset + 1); - - return offset + 1 + clen; - }, 0); + var offset = 0; + for (var i = 0, c = protocols.length; i < c; i++) { + buff[offset++] = lens[i]; + buff.write(protocols[i], offset); + offset += lens[i]; + } return buff; } From 9e9d7b8fba4afdb56d5263ba277ad3cae80fb74e Mon Sep 17 00:00:00 2001 From: James M Snell <jasnell@gmail.com> Date: Thu, 2 Jun 2016 18:04:05 -0700 Subject: [PATCH 026/131] doc: general improvements to os.md copy PR-URL: https://github.com/nodejs/node/pull/7124 Reviewed-By: Brian White <mscdex@mscdex.net> --- doc/api/os.md | 344 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 227 insertions(+), 117 deletions(-) diff --git a/doc/api/os.md b/doc/api/os.md index 59ed110666d203..8a9c00971a15a8 100644 --- a/doc/api/os.md +++ b/doc/api/os.md @@ -2,25 +2,36 @@ Stability: 2 - Stable -Provides a few basic operating-system related utility functions. +The `os` module provides a number of operating system-related utility methods. +It can be accessed using: -Use `require('os')` to access this module. +```js +const os = require('os'); +``` ## os.EOL <!-- YAML added: v0.7.8 --> -A constant defining the appropriate End-of-line marker for the operating -system. +A string constant defining the operating system-specific end-of-line marker: + +* `\n` on POSIX +* `\r\n` on Windows ## os.arch() <!-- YAML added: v0.5.0 --> -Returns the operating system CPU architecture. Possible values are `'x64'`, -`'arm'` and `'ia32'`. Returns the value of [`process.arch`][]. +The `os.arch()` method returns a string identifying the operating system CPU +architecture *for which the Node.js binary was compiled*. + +The current possible values are: `'arm'`, `'arm64'`, `'ia32'`, `'mips'`, +`'mipsel'`, `'ppc'`, `'ppc64'`, `'s390'`, `'s390x'`, `'x32'`, `'x64'`, and +`'x86'`. + +Equivalent to [`process.arch`][]. ## os.constants @@ -33,80 +44,116 @@ defined are described in [OS Constants][]. added: v0.3.3 --> -Returns an array of objects containing information about each CPU/core -installed: model, speed (in MHz), and times (an object containing the number of -milliseconds the CPU/core spent in: user, nice, sys, idle, and irq). +The `os.cpus()` method returns an array of objects containing information about +each CPU/core installed. -Example inspection of os.cpus: +The properties included on each object include: + +* `model` {String} +* `speed` {number} (in MHz) +* `times` {Object} + * `user` {number} The number of milliseconds the CPU has spent in user mode. + * `nice` {number} The number of milliseconds the CPU has spent in nice mode. + * `sys` {number} The number of milliseconds the CPU has spent in sys mode. + * `idle` {number} The number of milliseconds the CPU has spent in idle mode. + * `irq` {number} The number of milliseconds the CPU has spent in irq mode. + +For example: ```js -[ { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', +[ + { + model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', speed: 2926, - times: - { user: 252020, - nice: 0, - sys: 30340, - idle: 1070356870, - irq: 0 } }, - { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + times: { + user: 252020, + nice: 0, + sys: 30340, + idle: 1070356870, + irq: 0 + } + }, + { + model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', speed: 2926, - times: - { user: 306960, - nice: 0, - sys: 26980, - idle: 1071569080, - irq: 0 } }, - { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + times: { + user: 306960, + nice: 0, + sys: 26980, + idle: 1071569080, + irq: 0 + } + }, + { + model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', speed: 2926, - times: - { user: 248450, - nice: 0, - sys: 21750, - idle: 1070919370, - irq: 0 } }, - { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + times: { + user: 248450, + nice: 0, + sys: 21750, + idle: 1070919370, + irq: 0 + } + }, + { + model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', speed: 2926, - times: - { user: 256880, - nice: 0, - sys: 19430, - idle: 1070905480, - irq: 20 } }, - { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + times: { + user: 256880, + nice: 0, + sys: 19430, + idle: 1070905480, + irq: 20 + } + }, + { + model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', speed: 2926, - times: - { user: 511580, - nice: 20, - sys: 40900, - idle: 1070842510, - irq: 0 } }, - { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + times: { + user: 511580, + nice: 20, + sys: 40900, + idle: 1070842510, + irq: 0 + } + }, + { + model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', speed: 2926, - times: - { user: 291660, - nice: 0, - sys: 34360, - idle: 1070888000, - irq: 10 } }, - { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + times: { + user: 291660, + nice: 0, + sys: 34360, + idle: 1070888000, + irq: 10 + } + }, + { + model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', speed: 2926, - times: - { user: 308260, - nice: 0, - sys: 55410, - idle: 1071129970, - irq: 880 } }, - { model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', + times: { + user: 308260, + nice: 0, + sys: 55410, + idle: 1071129970, + irq: 880 + } + }, + { + model: 'Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz', speed: 2926, - times: - { user: 266450, - nice: 1480, - sys: 34920, - idle: 1072572010, - irq: 30 } } ] + times: { + user: 266450, + nice: 1480, + sys: 34920, + idle: 1072572010, + irq: 30 + } + } +] ``` -Note that since `nice` values are UNIX centric in Windows the `nice` values of +*Note*: Because `nice` values are UNIX-specific, on Windows the `nice` values of all processors are always 0. ## os.endianness() @@ -114,124 +161,186 @@ all processors are always 0. added: v0.9.4 --> -Returns the endianness of the CPU. Possible values are `'BE'` for big endian -or `'LE'` for little endian. +The `os.endianness()` method returns a string identifying the endianness of the +CPU *for which the Node.js binary was compiled*. + +Possible values are: + +* `'BE'` for big endian +* `'LE'` for little endian. ## os.freemem() <!-- YAML added: v0.3.3 --> -Returns the amount of free system memory in bytes. +The `os.freemem()` method returns the amount of free system memory in bytes as +an integer. ## os.homedir() <!-- YAML added: v2.3.0 --> -Returns the home directory of the current user. +The `os.homedir()` method returns the home directory of the current user as a +string. ## os.hostname() <!-- YAML added: v0.3.3 --> -Returns the hostname of the operating system. +The `os.hostname()` method returns the hostname of the operating system as a +string. ## os.loadavg() <!-- YAML added: v0.3.3 --> -Returns an array containing the 1, 5, and 15 minute load averages. +The `os.loadavg()` method returns an array containing the 1, 5, and 15 minute +load averages. The load average is a measure of system activity, calculated by the operating system and expressed as a fractional number. As a rule of thumb, the load average should ideally be less than the number of logical CPUs in the system. -The load average is a very UNIX-y concept; there is no real equivalent on -Windows platforms. That is why this function always returns `[0, 0, 0]` on -Windows. +The load average is a UNIX-specific concept with no real equivalent on +Windows platforms. On Windows, the return value is always `[0, 0, 0]`. ## os.networkInterfaces() <!-- YAML added: v0.6.0 --> -Get a list of network interfaces: +The `os.networkInterfaces()` method returns an object containing only network +interfaces that have been assigned a network address. + +Each key on the returned object identifies a network interface. The associated +value is an array of objects that each describe an assigned network address. + +The properties available on the assigned network address object include: + +* `address` {String} The assigned IPv4 or IPv6 address +* `netmask` {String} The IPv4 or IPv6 network mask +* `family` {String} Either `IPv4` or `IPv6` +* `mac` {String} The MAC address of the network interface +* `internal` {boolean} `true` if the network interface is a loopback or + similar interface that is not remotely accessible; otherwise `false` +* `scopeid` {number} The numeric IPv6 scope ID (only specified when `family` + is `IPv6`) ```js -{ lo: - [ { address: '127.0.0.1', - netmask: '255.0.0.0', - family: 'IPv4', - mac: '00:00:00:00:00:00', - internal: true }, - { address: '::1', - netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', - family: 'IPv6', - mac: '00:00:00:00:00:00', - internal: true } ], - eth0: - [ { address: '192.168.1.108', - netmask: '255.255.255.0', - family: 'IPv4', - mac: '01:02:03:0a:0b:0c', - internal: false }, - { address: 'fe80::a00:27ff:fe4e:66a1', - netmask: 'ffff:ffff:ffff:ffff::', - family: 'IPv6', - mac: '01:02:03:0a:0b:0c', - internal: false } ] } +{ + lo: [ + { + address: '127.0.0.1', + netmask: '255.0.0.0', + family: 'IPv4', + mac: '00:00:00:00:00:00', + internal: true + }, + { + address: '::1', + netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', + family: 'IPv6', + mac: '00:00:00:00:00:00', + internal: true + } + ], + eth0: [ + { + address: '192.168.1.108', + netmask: '255.255.255.0', + family: 'IPv4', + mac: '01:02:03:0a:0b:0c', + internal: false + }, + { + address: 'fe80::a00:27ff:fe4e:66a1', + netmask: 'ffff:ffff:ffff:ffff::', + family: 'IPv6', + mac: '01:02:03:0a:0b:0c', + internal: false + } + ] +} ``` -Note that due to the underlying implementation this will only return network -interfaces that have been assigned an address. - ## os.platform() <!-- YAML added: v0.5.0 --> -Returns the operating system platform. Possible values are `'darwin'`, -`'freebsd'`, `'linux'`, `'sunos'` or `'win32'`. Returns the value of -[`process.platform`][]. +The `os.platform()` method returns a string identifying the operating system +platform as set during compile time of Node.js. + +Currently possible values are: + +* `'aix'` +* `'darwin'` +* `'freebsd'` +* `'linux'` +* `'openbsd'` +* `'sunos'` +* `'win32'` + +Equivalent to [`process.platform`][]. + +*Note*: The value `'android'` may also be returned if the Node.js is built on +the Android operating system. However, Android support in Node.js is considered +to be experimental at this time. ## os.release() <!-- YAML added: v0.3.3 --> -Returns the operating system release. +The `os.release()` method returns a string identifying the operating system +release. + +*Note*: On POSIX systems, the operating system release is determined by calling +uname(3). On Windows, `GetVersionExW()` is used. Please see +https://en.wikipedia.org/wiki/Uname#Examples for more information. ## os.tmpdir() <!-- YAML added: v0.9.9 --> -Returns the operating system's default directory for temporary files. +The `os.tmpdir()` method returns a string specifying the operating system's +default directory for temporary files. ## os.totalmem() <!-- YAML added: v0.3.3 --> -Returns the total amount of system memory in bytes. +The `os.totalmem()` method returns the total amount of system memory in bytes +as an integer. ## os.type() <!-- YAML added: v0.3.3 --> -Returns the operating system name. For example `'Linux'` on Linux, `'Darwin'` -on OS X and `'Windows_NT'` on Windows. +The `os.type()` method returns a string identifying the operating system name +as returned by uname(3). For example `'Linux'` on Linux, `'Darwin'` on OS X and +`'Windows_NT'` on Windows. + +Please see https://en.wikipedia.org/wiki/Uname#Examples for additional +information about the output of running uname(3) on various operating systems. ## os.uptime() <!-- YAML added: v0.3.3 --> -Returns the system uptime in seconds. +The `os.uptime()` method returns the system uptime in number of seconds. + +*Note*: Within Node.js' internals, this number is represented as a `double`. +However, fractional seconds are not returned and the value can typically be +treated as an integer. ## os.userInfo([options]) <!-- YAML @@ -243,13 +352,14 @@ added: v6.0.0 If `encoding` is set to `'buffer'`, the `username`, `shell`, and `homedir` values will be `Buffer` instances. (Default: 'utf8') -Returns a subset of the password file entry for the current effective user. The +The `os.userInfo()` method returns information about the currently effective +user -- on POSIX platforms, this is typically a subset of the password file. The returned object includes the `username`, `uid`, `gid`, `shell`, and `homedir`. On Windows, the `uid` and `gid` fields are `-1`, and `shell` is `null`. -The value of `homedir` returned by `userInfo()` comes directly from the -operating system. This differs from the result of `os.homedir()`, which queries -several environment variables for the home directory before falling back to the +The value of `homedir` returned by `os.userInfo()` is provided by the operating +system. This differs from the result of `os.homedir()`, which queries several +environment variables for the home directory before falling back to the operating system response. ## OS Constants @@ -273,7 +383,7 @@ The following signal constants are exported by `os.constants.signals`: </tr> <tr> <td><code>SIGINT</code></td> - <td>Sent to indicate when a user wishes to interrupt a process + <td>Sent to indicate when a user wishes to interrupt a process (`(Ctrl+C)`).</td> </tr> <tr> @@ -304,7 +414,7 @@ The following signal constants are exported by `os.constants.signals`: </tr> <tr> <td><code>SIGFPE</code></td> - <td>Sent to a process to notify that it has performed an illegal arithmetic + <td>Sent to a process to notify that it has performed an illegal arithmetic operation.</td> </tr> <tr> From 7aa2125fae2408c245bed497ff883b262b1862aa Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Wed, 8 Jun 2016 16:31:54 -0700 Subject: [PATCH 027/131] doc: add argument information for socket.destroy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/7238 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com> Reviewed-By: Johan Bergström <bugs@bergstroem.nu> --- doc/api/net.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/api/net.md b/doc/api/net.md index df8712a94e6284..876cf97ccfbcd3 100644 --- a/doc/api/net.md +++ b/doc/api/net.md @@ -512,7 +512,7 @@ If `true` - [`socket.connect(options[, connectListener])`][`socket.connect(optio haven't yet finished. Will be set to `false` before emitting `connect` event and/or calling [`socket.connect(options[, connectListener])`][`socket.connect(options, connectListener)`]'s callback. -### socket.destroy() +### socket.destroy([exception]) <!-- YAML added: v0.1.90 --> @@ -520,6 +520,9 @@ added: v0.1.90 Ensures that no more I/O activity happens on this socket. Only necessary in case of errors (parse error or so). +If `exception` is specified, an [`'error'`][] event will be emitted and any +listeners for that event will receive `exception` as an argument. + ### socket.destroyed A Boolean value that indicates if the connection is destroyed or not. Once a From 580e11026eb0065848e75723dc894644def52ec7 Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Thu, 16 Jun 2016 09:48:21 -0700 Subject: [PATCH 028/131] test: refresh the tmpdir before using MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test fails if tmp dir does not exist when the test is run. Add common.refreshTmpDir() so that doesn't happen. PR-URL: https://github.com/nodejs/node/pull/7327 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Johan Bergström <bugs@bergstroem.nu> Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com> Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: Anna Henningsen <anna@addaleax.net> --- test/parallel/test-fs-watch-encoding.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/parallel/test-fs-watch-encoding.js b/test/parallel/test-fs-watch-encoding.js index 47cb70310825c1..449d0c8bf4050c 100644 --- a/test/parallel/test-fs-watch-encoding.js +++ b/test/parallel/test-fs-watch-encoding.js @@ -10,6 +10,8 @@ if (common.isFreeBSD) { return; } +common.refreshTmpDir(); + const fn = '新建文夹件.txt'; const a = path.join(common.tmpDir, fn); From c236a133415701b56b4ce480baf02357f82cf82c Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Tue, 21 Jun 2016 22:41:39 -0700 Subject: [PATCH 029/131] test: mark test-vm-timeout flaky on windows PR-URL: https://github.com/nodejs/node/pull/7359 Refs: https://github.com/nodejs/node/issues/6727 Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: Joao Reis <reis@janeasystems.com> Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com> --- test/parallel/parallel.status | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/parallel/parallel.status b/test/parallel/parallel.status index e3a8e345f732a7..0a23f0614a1b0c 100644 --- a/test/parallel/parallel.status +++ b/test/parallel/parallel.status @@ -7,7 +7,8 @@ prefix parallel [true] # This section applies to all platforms [$system==win32] -test-tick-processor : PASS,FLAKY +test-tick-processor : PASS,FLAKY +test-vm-timeout : PASS,FLAKY [$system==linux] test-tick-processor : PASS,FLAKY From 0f434fee6e4fffeb2a0589386137be9bfdba5723 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan <me@rreverser.com> Date: Wed, 22 Jun 2016 18:31:02 +0100 Subject: [PATCH 030/131] doc: add RReverser to collaborators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com> Reviewed-By: Myles Borins <myles.borins@gmail.com> PR-URL: https://github.com/nodejs/node/pull/7370 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5098135b307444..50e84e0e7fc37c 100644 --- a/README.md +++ b/README.md @@ -203,6 +203,7 @@ information about the governance of the Node.js project, see * [robertkowalski](https://github.com/robertkowalski) - **Robert Kowalski** <rok@kowalski.gd> * [romankl](https://github.com/romankl) - **Roman Klauke** <romaaan.git@gmail.com> * [ronkorving](https://github.com/ronkorving) - **Ron Korving** <ron@ronkorving.nl> +* [RReverser](https://github.com/RReverser) - **Ingvar Stepanyan** <me@rreverser.com> * [saghul](https://github.com/saghul) - **Saúl Ibarra Corretgé** <saghul@gmail.com> * [sam-github](https://github.com/sam-github) - **Sam Roberts** <vieuxtech@gmail.com> * [santigimeno](https://github.com/santigimeno) - **Santiago Gimeno** <santiago.gimeno@gmail.com> From ec515c5d3bec55d487248b1e148d98ea8919fb72 Mon Sep 17 00:00:00 2001 From: "Kyle E. Mitchell" <kyle@kemitchell.com> Date: Fri, 10 Jun 2016 15:41:36 -0700 Subject: [PATCH 031/131] doc: mention http request "aborted" events Fixes: https://github.com/nodejs/node/issues/6925 PR-URL: https://github.com/nodejs/node/pull/7270 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> --- doc/api/http.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/api/http.md b/doc/api/http.md index 3e3d3fb0cc92fd..493e66b7e7589e 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -219,6 +219,13 @@ The request implements the [Writable Stream][] interface. This is an Emitted when the request has been aborted by the client. This event is only emitted on the first call to `abort()`. +### Event: 'aborted' + +`function () { }` + +Emitted when the request has been aborted by the server and the network +socket has closed. + ### Event: 'checkExpectation' `function (request, response) { }` @@ -903,6 +910,13 @@ headers and data. It implements the [Readable Stream][] interface, as well as the following additional events, methods, and properties. +### Event: 'aborted' + +`function () { }` + +Emitted when the request has been aborted by the client and the network +socket has closed. + ### Event: 'close' `function () { }` From 7a0718bdc65ef0c266cef22d83c11c199842a8d4 Mon Sep 17 00:00:00 2001 From: "Italo A. Casas" <me@italoacasas.com> Date: Fri, 27 May 2016 10:33:17 -0400 Subject: [PATCH 032/131] doc: add `added:` information for tls Ref: https://github.com/nodejs/node/issues/6578 PR-URL: https://github.com/nodejs/node/pull/7018 Reviewed-By: Anna Henningsen <anna@addaleax.net> --- doc/api/tls.md | 137 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/doc/api/tls.md b/doc/api/tls.md index caec8a7b123c57..ca21905757a00d 100644 --- a/doc/api/tls.md +++ b/doc/api/tls.md @@ -186,11 +186,17 @@ of an application. The `--tls-cipher-list` switch should by used only if absolutely necessary. ## Class: tls.Server +<!-- YAML +added: v0.3.2 +--> The `tls.Server` class is a subclass of `net.Server` that accepts encrypted connections using TLS or SSL. ### Event: 'tlsClientError' +<!-- YAML +added: v6.0.0 +--> The `'tlsClientError'` event is emitted when an error occurs before a secure connection is established. The listener callback is passed two arguments when @@ -201,6 +207,9 @@ called: error originated. ### Event: 'newSession' +<!-- YAML +added: v0.9.2 +--> The `'newSession'` event is emitted upon creation of a new TLS session. This may be used to store sessions in external storage. The listener callback is passed @@ -215,6 +224,9 @@ three arguments when called: established after the addition of the event listener. ### Event: 'OCSPRequest' +<!-- YAML +added: v0.11.13 +--> The `'OCSPRequest'` event is emitted when the client sends a certificate status request. The listener callback is passed three arguments when called: @@ -259,6 +271,9 @@ established after the addition of the event listener. *Note*: An npm module like [asn1.js] may be used to parse the certificates. ### Event: 'resumeSession' +<!-- YAML +added: v0.9.2 +--> The `'resumeSession'` event is emitted when the client requests to resume a previous TLS session. The listener callback is passed two arguments when @@ -291,6 +306,9 @@ server.on('resumeSession', (id, cb) => { ``` ### Event: 'secureConnection' +<!-- YAML +added: v0.3.2 +--> The `'secureConnection'` event is emitted after the handshaking process for a new connection has successfully completed. The listener callback is passed a @@ -315,6 +333,9 @@ The `tlsSocket.servername` property is a string containing the server name requested via SNI. ### server.addContext(hostname, context) +<!-- YAML +added: v0.5.3 +--> * `hostname` {string} A SNI hostname or wildcard (e.g. `'*'`) * `context` {Object} An object containing any of the possible properties @@ -325,12 +346,18 @@ The `server.addContext()` method adds a secure context that will be used if the client request's SNS hostname matches the supplied `hostname` (or wildcard). ### server.address() +<!-- YAML +added: v0.6.0 +--> Returns the bound address, the address family name, and port of the server as reported by the operating system. See [`net.Server.address()`][] for more information. ### server.close([callback]) +<!-- YAML +added: v0.3.2 +--> * `callback` {Function} An optional listener callback that will be registered to listen for the server instance's `'close'` event. @@ -341,15 +368,24 @@ This function operates asynchronously. The `'close'` event will be emitted when the the server is finally closed. ### server.connections +<!-- YAML +added: v0.3.2 +--> Returns the current number of concurrent connections on the server. ### server.getTicketKeys() +<!-- YAML +added: v3.0.0 +--> Returns a `Buffer` instance holding the keys currently used for encryption/decryption of the [TLS Session Tickets][] ### server.listen(port[, hostname][, callback]) +<!-- YAML +added: v0.3.2 +--> * `port` {number} The TCP/IP port on which to begin listening for connections. A value of `0` (zero) will assign a random port. @@ -369,6 +405,9 @@ called when the server has started listening. See `net.Server` for more information. ### server.setTicketKeys(keys) +<!-- YAML +added: v3.0.0 +--> * `keys` {Buffer} The keys used for encryption/decryption of the [TLS Session Tickets][]. @@ -385,6 +424,9 @@ previous keys. ## Class: tls.TLSSocket +<!-- YAML +added: v0.11.4 +--> The `tls.TLSSocket` is a subclass of [`net.Socket`][] that performs transparent encryption of written data and all required TLS negotiation. @@ -396,6 +438,9 @@ Instances of `tls.TLSSocket` implement the duplex [Stream][] interface. connection is open. ### new tls.TLSSocket(socket[, options]) +<!-- YAML +added: v0.11.4 +--> * `socket` {net.Socket} An instance of [`net.Socket`][] * `options` {Object} @@ -417,6 +462,9 @@ connection is open. Construct a new `tls.TLSSocket` object from an existing TCP socket. ### Event: 'OCSPResponse' +<!-- YAML +added: v0.11.13 +--> The `'OCSPResponse'` event is emitted if the `requestOCSP` option was set when the `tls.TLSSocket` was created and an OCSP response has been received. @@ -428,6 +476,9 @@ Typically, the `response` is a digitally signed object from the server's CA that contains information about server's certificate revocation status. ### Event: 'secureConnect' +<!-- YAML +added: v0.11.4 +--> The `'secureConnect'` event is emitted after the handshaking process for a new connection has successfully completed. The listener callback will be called @@ -440,6 +491,9 @@ the `tlsSocket.alpnProtocol` or `tlsSocket.npnProtocol` properties can be checked to determine the negotiated protocol. ### tlsSocket.address() +<!-- YAML +added: v0.11.4 +--> Returns the bound address, the address family name, and port of the underlying socket as reported by the operating system. Returns an @@ -447,21 +501,33 @@ object with three properties, e.g., `{ port: 12346, family: 'IPv4', address: '127.0.0.1' }` ### tlsSocket.authorized +<!-- YAML +added: v0.11.4 +--> Returns `true` if the peer certificate was signed by one of the CAs specified when creating the `tls.TLSSocket` instance, otherwise `false`. ### tlsSocket.authorizationError +<!-- YAML +added: v0.11.4 +--> Returns the reason why the peer's certificate was not been verified. This property is set only when `tlsSocket.authorized === false`. ### tlsSocket.encrypted +<!-- YAML +added: v0.11.4 +--> Always returns `true`. This may be used to distinguish TLS sockets from regular `net.Socket` instances. ### tlsSocket.getCipher() +<!-- YAML +added: v0.11.4 +--> Returns an object representing the cipher name and the SSL/TLS protocol version that first defined the cipher. @@ -473,6 +539,9 @@ https://www.openssl.org/docs/manmaster/ssl/SSL_CIPHER_get_name.html for more information. ### tlsSocket.getEphemeralKeyInfo() +<!-- YAML +added: v5.0.0 +--> Returns an object representing the type, name, and size of parameter of an ephemeral key exchange in [Perfect Forward Secrecy][] on a client @@ -484,6 +553,9 @@ if called on a server socket. The supported types are `'DH'` and `'ECDH'`. The For Example: `{ type: 'ECDH', name: 'prime256v1', size: 256 }` ### tlsSocket.getPeerCertificate([ detailed ]) +<!-- YAML +added: v0.11.4 +--> * `detailed` {boolean} Specify `true` to request that the full certificate chain with the `issuer` property be returned; `false` to return only the @@ -522,6 +594,9 @@ If the peer does not provide a certificate, `null` or an empty object will be returned. ### tlsSocket.getProtocol() +<!-- YAML +added: v5.7.0 +--> Returns a string containing the negotiated SSL/TLS protocol version of the current connection. The value `'unknown'` will be returned for connected @@ -540,12 +615,18 @@ See https://www.openssl.org/docs/manmaster/ssl/SSL_get_version.html for more information. ### tlsSocket.getSession() +<!-- YAML +added: v0.11.4 +--> Returns the ASN.1 encoded TLS session or `undefined` if no session was negotiated. Can be used to speed up handshake establishment when reconnecting to the server. ### tlsSocket.getTLSTicket() +<!-- YAML +added: v0.11.4 +--> Returns the TLS session ticket or `undefined` if no session was negotiated. @@ -553,27 +634,45 @@ Returns the TLS session ticket or `undefined` if no session was negotiated. session reuse provide `session` option to [`tls.connect()`][]. ### tlsSocket.localAddress +<!-- YAML +added: v0.11.4 +--> Returns the string representation of the local IP address. ### tlsSocket.localPort +<!-- YAML +added: v0.11.4 +--> Returns the numeric representation of the local port. ### tlsSocket.remoteAddress +<!-- YAML +added: v0.11.4 +--> Returns the string representation of the remote IP address. For example, `'74.125.127.100'` or `'2001:4860:a005::68'`. ### tlsSocket.remoteFamily +<!-- YAML +added: v0.11.4 +--> Returns the string representation of the remote IP family. `'IPv4'` or `'IPv6'`. ### tlsSocket.remotePort +<!-- YAML +added: v0.11.4 +--> Returns the numeric representation of the remote port. For example, `443`. ### tlsSocket.renegotiate(options, callback) +<!-- YAML +added: v0.11.8 +--> * `options` {Object} * `rejectUnauthorized` {boolean} @@ -592,6 +691,9 @@ secure connection has been established. after `handshakeTimeout` timeout. ### tlsSocket.setMaxSendFragment(size) +<!-- YAML +added: v0.11.11 +--> * `size` {number} The maximum TLS fragment size. Defaults to `16384`. The maximum value is `16384`. @@ -608,6 +710,9 @@ decrease overall server throughput. ## tls.connect(options[, callback]) +<!-- YAML +added: v0.11.3 +--> * `options` {Object} * `host` {string} Host the client should connect to. @@ -677,6 +782,9 @@ The `callback` function, if specified, will be added as a listener for the `tls.connect()` returns a [`tls.TLSSocket`][] object. ## tls.connect(port[, host][, options][, callback]) +<!-- YAML +added: v0.11.3 +--> * `port` {number} * `host` {string} @@ -805,6 +913,9 @@ socket.on('end', () => { ## tls.createSecureContext(options) +<!-- YAML +added: v0.11.13 +--> * `options` {Object} * `pfx` {string|Buffer} A string or `Buffer` holding the PFX or PKCS12 encoded @@ -838,6 +949,9 @@ publicly trusted list of CAs as given in ## tls.createServer(options[, secureConnectionListener]) +<!-- YAML +added: v0.3.2 +--> * `options` {Object} * `pfx` {string|Buffer} A string or `Buffer` containing the private key, @@ -1019,6 +1133,9 @@ openssl s_client -connect 127.0.0.1:8000 ``` ## tls.getCiphers() +<!-- YAML +added: v0.10.2 +--> Returns an array with the names of the supported SSL ciphers. @@ -1031,6 +1148,10 @@ console.log(tls.getCiphers()); // ['AES128-SHA', 'AES256-SHA', ...] ## Deprecated APIs ### Class: CryptoStream +<!-- YAML +added: v0.3.4 +deprecated: v0.11.3 +--> Stability: 0 - Deprecated: Use [`tls.TLSSocket`][] instead. @@ -1038,18 +1159,30 @@ The `tls.CryptoStream` class represents a stream of encrypted data. This class has been deprecated and should no longer be used. #### cryptoStream.bytesWritten +<!-- YAML +added: v0.3.4 +deprecated: v0.11.3 +--> The `cryptoStream.bytesWritten` property returns the total number of bytes written to the underlying socket *including* the bytes required for the implementation of the TLS protocol. ### Class: SecurePair +<!-- YAML +added: v0.3.2 +deprecated: v0.11.3 +--> Stability: 0 - Deprecated: Use [`tls.TLSSocket`][] instead. Returned by `tls.createSecurePair()`. #### Event: 'secure' +<!-- YAML +added: v0.3.2 +deprecated: v0.11.3 +--> The `'secure'` event is emitted by the `SecurePair` object once a secure connection has been established. @@ -1059,6 +1192,10 @@ event, `pair.cleartext.authorized` should be inspected to confirm whether the certificate used is properly authorized. ## tls.createSecurePair([context][, isServer][, requestCert][, rejectUnauthorized][, options]) +<!-- YAML +added: v0.3.2 +deprecated: v0.11.3 +--> Stability: 0 - Deprecated: Use [`tls.TLSSocket`][] instead. From 32e068a5d0205720650c7dd9822b14fd294f637c Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Wed, 22 Jun 2016 11:48:58 +0200 Subject: [PATCH 033/131] tools: update certdata.txt This is the certdata.txt[0] that ships in Firefox 47 and NSS 3.23, last updated on 2016-02-26. [0] https://hg.mozilla.org/mozilla-central/raw-file/1f84dea6508d/security/nss/lib/ckfw/builtins/certdata.txt PR-URL: https://github.com/nodejs/node/pull/7363 Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com> --- tools/certdata.txt | 1793 +++++++++----------------------------------- 1 file changed, 337 insertions(+), 1456 deletions(-) diff --git a/tools/certdata.txt b/tools/certdata.txt index d6d4b4fbb7ec8f..8effa748d2eb05 100644 --- a/tools/certdata.txt +++ b/tools/certdata.txt @@ -336,153 +336,6 @@ CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE -# -# Certificate "Verisign Class 1 Public Primary Certification Authority - G2" -# -# Issuer: OU=VeriSign Trust Network,OU="(c) 1998 VeriSign, Inc. - For authorized use only",OU=Class 1 Public Primary Certification Authority - G2,O="VeriSign, Inc.",C=US -# Serial Number:4c:c7:ea:aa:98:3e:71:d3:93:10:f8:3d:3a:89:91:92 -# Subject: OU=VeriSign Trust Network,OU="(c) 1998 VeriSign, Inc. - For authorized use only",OU=Class 1 Public Primary Certification Authority - G2,O="VeriSign, Inc.",C=US -# Not Valid Before: Mon May 18 00:00:00 1998 -# Not Valid After : Tue Aug 01 23:59:59 2028 -# Fingerprint (MD5): DB:23:3D:F9:69:FA:4B:B9:95:80:44:73:5E:7D:41:83 -# Fingerprint (SHA1): 27:3E:E1:24:57:FD:C4:F9:0C:55:E8:2B:56:16:7F:62:F5:32:E5:47 -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "Verisign Class 1 Public Primary Certification Authority - G2" -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\201\301\061\013\060\011\006\003\125\004\006\023\002\125\123 -\061\027\060\025\006\003\125\004\012\023\016\126\145\162\151\123 -\151\147\156\054\040\111\156\143\056\061\074\060\072\006\003\125 -\004\013\023\063\103\154\141\163\163\040\061\040\120\165\142\154 -\151\143\040\120\162\151\155\141\162\171\040\103\145\162\164\151 -\146\151\143\141\164\151\157\156\040\101\165\164\150\157\162\151 -\164\171\040\055\040\107\062\061\072\060\070\006\003\125\004\013 -\023\061\050\143\051\040\061\071\071\070\040\126\145\162\151\123 -\151\147\156\054\040\111\156\143\056\040\055\040\106\157\162\040 -\141\165\164\150\157\162\151\172\145\144\040\165\163\145\040\157 -\156\154\171\061\037\060\035\006\003\125\004\013\023\026\126\145 -\162\151\123\151\147\156\040\124\162\165\163\164\040\116\145\164 -\167\157\162\153 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\201\301\061\013\060\011\006\003\125\004\006\023\002\125\123 -\061\027\060\025\006\003\125\004\012\023\016\126\145\162\151\123 -\151\147\156\054\040\111\156\143\056\061\074\060\072\006\003\125 -\004\013\023\063\103\154\141\163\163\040\061\040\120\165\142\154 -\151\143\040\120\162\151\155\141\162\171\040\103\145\162\164\151 -\146\151\143\141\164\151\157\156\040\101\165\164\150\157\162\151 -\164\171\040\055\040\107\062\061\072\060\070\006\003\125\004\013 -\023\061\050\143\051\040\061\071\071\070\040\126\145\162\151\123 -\151\147\156\054\040\111\156\143\056\040\055\040\106\157\162\040 -\141\165\164\150\157\162\151\172\145\144\040\165\163\145\040\157 -\156\154\171\061\037\060\035\006\003\125\004\013\023\026\126\145 -\162\151\123\151\147\156\040\124\162\165\163\164\040\116\145\164 -\167\157\162\153 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\020\114\307\352\252\230\076\161\323\223\020\370\075\072\211 -\221\222 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\003\002\060\202\002\153\002\020\114\307\352\252\230\076 -\161\323\223\020\370\075\072\211\221\222\060\015\006\011\052\206 -\110\206\367\015\001\001\005\005\000\060\201\301\061\013\060\011 -\006\003\125\004\006\023\002\125\123\061\027\060\025\006\003\125 -\004\012\023\016\126\145\162\151\123\151\147\156\054\040\111\156 -\143\056\061\074\060\072\006\003\125\004\013\023\063\103\154\141 -\163\163\040\061\040\120\165\142\154\151\143\040\120\162\151\155 -\141\162\171\040\103\145\162\164\151\146\151\143\141\164\151\157 -\156\040\101\165\164\150\157\162\151\164\171\040\055\040\107\062 -\061\072\060\070\006\003\125\004\013\023\061\050\143\051\040\061 -\071\071\070\040\126\145\162\151\123\151\147\156\054\040\111\156 -\143\056\040\055\040\106\157\162\040\141\165\164\150\157\162\151 -\172\145\144\040\165\163\145\040\157\156\154\171\061\037\060\035 -\006\003\125\004\013\023\026\126\145\162\151\123\151\147\156\040 -\124\162\165\163\164\040\116\145\164\167\157\162\153\060\036\027 -\015\071\070\060\065\061\070\060\060\060\060\060\060\132\027\015 -\062\070\060\070\060\061\062\063\065\071\065\071\132\060\201\301 -\061\013\060\011\006\003\125\004\006\023\002\125\123\061\027\060 -\025\006\003\125\004\012\023\016\126\145\162\151\123\151\147\156 -\054\040\111\156\143\056\061\074\060\072\006\003\125\004\013\023 -\063\103\154\141\163\163\040\061\040\120\165\142\154\151\143\040 -\120\162\151\155\141\162\171\040\103\145\162\164\151\146\151\143 -\141\164\151\157\156\040\101\165\164\150\157\162\151\164\171\040 -\055\040\107\062\061\072\060\070\006\003\125\004\013\023\061\050 -\143\051\040\061\071\071\070\040\126\145\162\151\123\151\147\156 -\054\040\111\156\143\056\040\055\040\106\157\162\040\141\165\164 -\150\157\162\151\172\145\144\040\165\163\145\040\157\156\154\171 -\061\037\060\035\006\003\125\004\013\023\026\126\145\162\151\123 -\151\147\156\040\124\162\165\163\164\040\116\145\164\167\157\162 -\153\060\201\237\060\015\006\011\052\206\110\206\367\015\001\001 -\001\005\000\003\201\215\000\060\201\211\002\201\201\000\252\320 -\272\276\026\055\270\203\324\312\322\017\274\166\061\312\224\330 -\035\223\214\126\002\274\331\157\032\157\122\066\156\165\126\012 -\125\323\337\103\207\041\021\145\212\176\217\275\041\336\153\062 -\077\033\204\064\225\005\235\101\065\353\222\353\226\335\252\131 -\077\001\123\155\231\117\355\345\342\052\132\220\301\271\304\246 -\025\317\310\105\353\246\135\216\234\076\360\144\044\166\245\315 -\253\032\157\266\330\173\121\141\156\246\177\207\310\342\267\345 -\064\334\101\210\352\011\100\276\163\222\075\153\347\165\002\003 -\001\000\001\060\015\006\011\052\206\110\206\367\015\001\001\005 -\005\000\003\201\201\000\251\117\303\015\307\147\276\054\313\331 -\250\315\055\165\347\176\025\236\073\162\353\176\353\134\055\011 -\207\326\153\155\140\174\345\256\305\220\043\014\134\112\320\257 -\261\135\363\307\266\012\333\340\025\223\015\335\003\274\307\166 -\212\265\335\117\303\233\023\165\270\001\300\346\311\133\153\245 -\270\211\334\254\244\335\162\355\116\241\367\117\274\006\323\352 -\310\144\164\173\302\225\101\234\145\163\130\361\220\232\074\152 -\261\230\311\304\207\274\317\105\155\105\342\156\042\077\376\274 -\017\061\134\350\362\331 -END - -# Trust for Certificate "Verisign Class 1 Public Primary Certification Authority - G2" -# Issuer: OU=VeriSign Trust Network,OU="(c) 1998 VeriSign, Inc. - For authorized use only",OU=Class 1 Public Primary Certification Authority - G2,O="VeriSign, Inc.",C=US -# Serial Number:4c:c7:ea:aa:98:3e:71:d3:93:10:f8:3d:3a:89:91:92 -# Subject: OU=VeriSign Trust Network,OU="(c) 1998 VeriSign, Inc. - For authorized use only",OU=Class 1 Public Primary Certification Authority - G2,O="VeriSign, Inc.",C=US -# Not Valid Before: Mon May 18 00:00:00 1998 -# Not Valid After : Tue Aug 01 23:59:59 2028 -# Fingerprint (MD5): DB:23:3D:F9:69:FA:4B:B9:95:80:44:73:5E:7D:41:83 -# Fingerprint (SHA1): 27:3E:E1:24:57:FD:C4:F9:0C:55:E8:2B:56:16:7F:62:F5:32:E5:47 -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "Verisign Class 1 Public Primary Certification Authority - G2" -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\047\076\341\044\127\375\304\371\014\125\350\053\126\026\177\142 -\365\062\345\107 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\333\043\075\371\151\372\113\271\225\200\104\163\136\175\101\203 -END -CKA_ISSUER MULTILINE_OCTAL -\060\201\301\061\013\060\011\006\003\125\004\006\023\002\125\123 -\061\027\060\025\006\003\125\004\012\023\016\126\145\162\151\123 -\151\147\156\054\040\111\156\143\056\061\074\060\072\006\003\125 -\004\013\023\063\103\154\141\163\163\040\061\040\120\165\142\154 -\151\143\040\120\162\151\155\141\162\171\040\103\145\162\164\151 -\146\151\143\141\164\151\157\156\040\101\165\164\150\157\162\151 -\164\171\040\055\040\107\062\061\072\060\070\006\003\125\004\013 -\023\061\050\143\051\040\061\071\071\070\040\126\145\162\151\123 -\151\147\156\054\040\111\156\143\056\040\055\040\106\157\162\040 -\141\165\164\150\157\162\151\172\145\144\040\165\163\145\040\157 -\156\154\171\061\037\060\035\006\003\125\004\013\023\026\126\145 -\162\151\123\151\147\156\040\124\162\165\163\164\040\116\145\164 -\167\157\162\153 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\020\114\307\352\252\230\076\161\323\223\020\370\075\072\211 -\221\222 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - # # Certificate "Verisign Class 2 Public Primary Certification Authority - G2" # @@ -630,153 +483,6 @@ CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE -# -# Certificate "Verisign Class 3 Public Primary Certification Authority - G2" -# -# Issuer: OU=VeriSign Trust Network,OU="(c) 1998 VeriSign, Inc. - For authorized use only",OU=Class 3 Public Primary Certification Authority - G2,O="VeriSign, Inc.",C=US -# Serial Number:7d:d9:fe:07:cf:a8:1e:b7:10:79:67:fb:a7:89:34:c6 -# Subject: OU=VeriSign Trust Network,OU="(c) 1998 VeriSign, Inc. - For authorized use only",OU=Class 3 Public Primary Certification Authority - G2,O="VeriSign, Inc.",C=US -# Not Valid Before: Mon May 18 00:00:00 1998 -# Not Valid After : Tue Aug 01 23:59:59 2028 -# Fingerprint (MD5): A2:33:9B:4C:74:78:73:D4:6C:E7:C1:F3:8D:CB:5C:E9 -# Fingerprint (SHA1): 85:37:1C:A6:E5:50:14:3D:CE:28:03:47:1B:DE:3A:09:E8:F8:77:0F -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "Verisign Class 3 Public Primary Certification Authority - G2" -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\201\301\061\013\060\011\006\003\125\004\006\023\002\125\123 -\061\027\060\025\006\003\125\004\012\023\016\126\145\162\151\123 -\151\147\156\054\040\111\156\143\056\061\074\060\072\006\003\125 -\004\013\023\063\103\154\141\163\163\040\063\040\120\165\142\154 -\151\143\040\120\162\151\155\141\162\171\040\103\145\162\164\151 -\146\151\143\141\164\151\157\156\040\101\165\164\150\157\162\151 -\164\171\040\055\040\107\062\061\072\060\070\006\003\125\004\013 -\023\061\050\143\051\040\061\071\071\070\040\126\145\162\151\123 -\151\147\156\054\040\111\156\143\056\040\055\040\106\157\162\040 -\141\165\164\150\157\162\151\172\145\144\040\165\163\145\040\157 -\156\154\171\061\037\060\035\006\003\125\004\013\023\026\126\145 -\162\151\123\151\147\156\040\124\162\165\163\164\040\116\145\164 -\167\157\162\153 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\201\301\061\013\060\011\006\003\125\004\006\023\002\125\123 -\061\027\060\025\006\003\125\004\012\023\016\126\145\162\151\123 -\151\147\156\054\040\111\156\143\056\061\074\060\072\006\003\125 -\004\013\023\063\103\154\141\163\163\040\063\040\120\165\142\154 -\151\143\040\120\162\151\155\141\162\171\040\103\145\162\164\151 -\146\151\143\141\164\151\157\156\040\101\165\164\150\157\162\151 -\164\171\040\055\040\107\062\061\072\060\070\006\003\125\004\013 -\023\061\050\143\051\040\061\071\071\070\040\126\145\162\151\123 -\151\147\156\054\040\111\156\143\056\040\055\040\106\157\162\040 -\141\165\164\150\157\162\151\172\145\144\040\165\163\145\040\157 -\156\154\171\061\037\060\035\006\003\125\004\013\023\026\126\145 -\162\151\123\151\147\156\040\124\162\165\163\164\040\116\145\164 -\167\157\162\153 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\020\175\331\376\007\317\250\036\267\020\171\147\373\247\211 -\064\306 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\003\002\060\202\002\153\002\020\175\331\376\007\317\250 -\036\267\020\171\147\373\247\211\064\306\060\015\006\011\052\206 -\110\206\367\015\001\001\005\005\000\060\201\301\061\013\060\011 -\006\003\125\004\006\023\002\125\123\061\027\060\025\006\003\125 -\004\012\023\016\126\145\162\151\123\151\147\156\054\040\111\156 -\143\056\061\074\060\072\006\003\125\004\013\023\063\103\154\141 -\163\163\040\063\040\120\165\142\154\151\143\040\120\162\151\155 -\141\162\171\040\103\145\162\164\151\146\151\143\141\164\151\157 -\156\040\101\165\164\150\157\162\151\164\171\040\055\040\107\062 -\061\072\060\070\006\003\125\004\013\023\061\050\143\051\040\061 -\071\071\070\040\126\145\162\151\123\151\147\156\054\040\111\156 -\143\056\040\055\040\106\157\162\040\141\165\164\150\157\162\151 -\172\145\144\040\165\163\145\040\157\156\154\171\061\037\060\035 -\006\003\125\004\013\023\026\126\145\162\151\123\151\147\156\040 -\124\162\165\163\164\040\116\145\164\167\157\162\153\060\036\027 -\015\071\070\060\065\061\070\060\060\060\060\060\060\132\027\015 -\062\070\060\070\060\061\062\063\065\071\065\071\132\060\201\301 -\061\013\060\011\006\003\125\004\006\023\002\125\123\061\027\060 -\025\006\003\125\004\012\023\016\126\145\162\151\123\151\147\156 -\054\040\111\156\143\056\061\074\060\072\006\003\125\004\013\023 -\063\103\154\141\163\163\040\063\040\120\165\142\154\151\143\040 -\120\162\151\155\141\162\171\040\103\145\162\164\151\146\151\143 -\141\164\151\157\156\040\101\165\164\150\157\162\151\164\171\040 -\055\040\107\062\061\072\060\070\006\003\125\004\013\023\061\050 -\143\051\040\061\071\071\070\040\126\145\162\151\123\151\147\156 -\054\040\111\156\143\056\040\055\040\106\157\162\040\141\165\164 -\150\157\162\151\172\145\144\040\165\163\145\040\157\156\154\171 -\061\037\060\035\006\003\125\004\013\023\026\126\145\162\151\123 -\151\147\156\040\124\162\165\163\164\040\116\145\164\167\157\162 -\153\060\201\237\060\015\006\011\052\206\110\206\367\015\001\001 -\001\005\000\003\201\215\000\060\201\211\002\201\201\000\314\136 -\321\021\135\134\151\320\253\323\271\152\114\231\037\131\230\060 -\216\026\205\040\106\155\107\077\324\205\040\204\341\155\263\370 -\244\355\014\361\027\017\073\371\247\371\045\327\301\317\204\143 -\362\174\143\317\242\107\362\306\133\063\216\144\100\004\150\301 -\200\271\144\034\105\167\307\330\156\365\225\051\074\120\350\064 -\327\170\037\250\272\155\103\221\225\217\105\127\136\176\305\373 -\312\244\004\353\352\227\067\124\060\157\273\001\107\062\063\315 -\334\127\233\144\151\141\370\233\035\034\211\117\134\147\002\003 -\001\000\001\060\015\006\011\052\206\110\206\367\015\001\001\005 -\005\000\003\201\201\000\121\115\315\276\134\313\230\031\234\025 -\262\001\071\170\056\115\017\147\160\160\231\306\020\132\224\244 -\123\115\124\155\053\257\015\135\100\213\144\323\327\356\336\126 -\141\222\137\246\304\035\020\141\066\323\054\047\074\350\051\011 -\271\021\144\164\314\265\163\237\034\110\251\274\141\001\356\342 -\027\246\014\343\100\010\073\016\347\353\104\163\052\232\361\151 -\222\357\161\024\303\071\254\161\247\221\011\157\344\161\006\263 -\272\131\127\046\171\000\366\370\015\242\063\060\050\324\252\130 -\240\235\235\151\221\375 -END - -# Trust for Certificate "Verisign Class 3 Public Primary Certification Authority - G2" -# Issuer: OU=VeriSign Trust Network,OU="(c) 1998 VeriSign, Inc. - For authorized use only",OU=Class 3 Public Primary Certification Authority - G2,O="VeriSign, Inc.",C=US -# Serial Number:7d:d9:fe:07:cf:a8:1e:b7:10:79:67:fb:a7:89:34:c6 -# Subject: OU=VeriSign Trust Network,OU="(c) 1998 VeriSign, Inc. - For authorized use only",OU=Class 3 Public Primary Certification Authority - G2,O="VeriSign, Inc.",C=US -# Not Valid Before: Mon May 18 00:00:00 1998 -# Not Valid After : Tue Aug 01 23:59:59 2028 -# Fingerprint (MD5): A2:33:9B:4C:74:78:73:D4:6C:E7:C1:F3:8D:CB:5C:E9 -# Fingerprint (SHA1): 85:37:1C:A6:E5:50:14:3D:CE:28:03:47:1B:DE:3A:09:E8:F8:77:0F -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "Verisign Class 3 Public Primary Certification Authority - G2" -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\205\067\034\246\345\120\024\075\316\050\003\107\033\336\072\011 -\350\370\167\017 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\242\063\233\114\164\170\163\324\154\347\301\363\215\313\134\351 -END -CKA_ISSUER MULTILINE_OCTAL -\060\201\301\061\013\060\011\006\003\125\004\006\023\002\125\123 -\061\027\060\025\006\003\125\004\012\023\016\126\145\162\151\123 -\151\147\156\054\040\111\156\143\056\061\074\060\072\006\003\125 -\004\013\023\063\103\154\141\163\163\040\063\040\120\165\142\154 -\151\143\040\120\162\151\155\141\162\171\040\103\145\162\164\151 -\146\151\143\141\164\151\157\156\040\101\165\164\150\157\162\151 -\164\171\040\055\040\107\062\061\072\060\070\006\003\125\004\013 -\023\061\050\143\051\040\061\071\071\070\040\126\145\162\151\123 -\151\147\156\054\040\111\156\143\056\040\055\040\106\157\162\040 -\141\165\164\150\157\162\151\172\145\144\040\165\163\145\040\157 -\156\154\171\061\037\060\035\006\003\125\004\013\023\026\126\145 -\162\151\123\151\147\156\040\124\162\165\163\164\040\116\145\164 -\167\157\162\153 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\020\175\331\376\007\317\250\036\267\020\171\147\373\247\211 -\064\306 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - # # Certificate "GlobalSign Root CA" # @@ -5213,141 +4919,6 @@ CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE -# -# Certificate "Staat der Nederlanden Root CA" -# -# Issuer: CN=Staat der Nederlanden Root CA,O=Staat der Nederlanden,C=NL -# Serial Number: 10000010 (0x98968a) -# Subject: CN=Staat der Nederlanden Root CA,O=Staat der Nederlanden,C=NL -# Not Valid Before: Tue Dec 17 09:23:49 2002 -# Not Valid After : Wed Dec 16 09:15:38 2015 -# Fingerprint (MD5): 60:84:7C:5A:CE:DB:0C:D4:CB:A7:E9:FE:02:C6:A9:C0 -# Fingerprint (SHA1): 10:1D:FA:3F:D5:0B:CB:BB:9B:B5:60:0C:19:55:A4:1A:F4:73:3A:04 -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "Staat der Nederlanden Root CA" -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\125\061\013\060\011\006\003\125\004\006\023\002\116\114\061 -\036\060\034\006\003\125\004\012\023\025\123\164\141\141\164\040 -\144\145\162\040\116\145\144\145\162\154\141\156\144\145\156\061 -\046\060\044\006\003\125\004\003\023\035\123\164\141\141\164\040 -\144\145\162\040\116\145\144\145\162\154\141\156\144\145\156\040 -\122\157\157\164\040\103\101 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\125\061\013\060\011\006\003\125\004\006\023\002\116\114\061 -\036\060\034\006\003\125\004\012\023\025\123\164\141\141\164\040 -\144\145\162\040\116\145\144\145\162\154\141\156\144\145\156\061 -\046\060\044\006\003\125\004\003\023\035\123\164\141\141\164\040 -\144\145\162\040\116\145\144\145\162\154\141\156\144\145\156\040 -\122\157\157\164\040\103\101 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\004\000\230\226\212 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\003\272\060\202\002\242\240\003\002\001\002\002\004\000 -\230\226\212\060\015\006\011\052\206\110\206\367\015\001\001\005 -\005\000\060\125\061\013\060\011\006\003\125\004\006\023\002\116 -\114\061\036\060\034\006\003\125\004\012\023\025\123\164\141\141 -\164\040\144\145\162\040\116\145\144\145\162\154\141\156\144\145 -\156\061\046\060\044\006\003\125\004\003\023\035\123\164\141\141 -\164\040\144\145\162\040\116\145\144\145\162\154\141\156\144\145 -\156\040\122\157\157\164\040\103\101\060\036\027\015\060\062\061 -\062\061\067\060\071\062\063\064\071\132\027\015\061\065\061\062 -\061\066\060\071\061\065\063\070\132\060\125\061\013\060\011\006 -\003\125\004\006\023\002\116\114\061\036\060\034\006\003\125\004 -\012\023\025\123\164\141\141\164\040\144\145\162\040\116\145\144 -\145\162\154\141\156\144\145\156\061\046\060\044\006\003\125\004 -\003\023\035\123\164\141\141\164\040\144\145\162\040\116\145\144 -\145\162\154\141\156\144\145\156\040\122\157\157\164\040\103\101 -\060\202\001\042\060\015\006\011\052\206\110\206\367\015\001\001 -\001\005\000\003\202\001\017\000\060\202\001\012\002\202\001\001 -\000\230\322\265\121\021\172\201\246\024\230\161\155\276\314\347 -\023\033\326\047\016\172\263\152\030\034\266\141\132\325\141\011 -\277\336\220\023\307\147\356\335\363\332\305\014\022\236\065\125 -\076\054\047\210\100\153\367\334\335\042\141\365\302\307\016\365 -\366\325\166\123\115\217\214\274\030\166\067\205\235\350\312\111 -\307\322\117\230\023\011\242\076\042\210\234\177\326\362\020\145 -\264\356\137\030\325\027\343\370\305\375\342\235\242\357\123\016 -\205\167\242\017\341\060\107\356\000\347\063\175\104\147\032\013 -\121\350\213\240\236\120\230\150\064\122\037\056\155\001\362\140 -\105\362\061\353\251\061\150\051\273\172\101\236\306\031\177\224 -\264\121\071\003\177\262\336\247\062\233\264\107\216\157\264\112 -\256\345\257\261\334\260\033\141\274\231\162\336\344\211\267\172 -\046\135\332\063\111\133\122\234\016\365\212\255\303\270\075\350 -\006\152\302\325\052\013\154\173\204\275\126\005\313\206\145\222 -\354\104\053\260\216\271\334\160\013\106\332\255\274\143\210\071 -\372\333\152\376\043\372\274\344\110\364\147\053\152\021\020\041 -\111\002\003\001\000\001\243\201\221\060\201\216\060\014\006\003 -\125\035\023\004\005\060\003\001\001\377\060\117\006\003\125\035 -\040\004\110\060\106\060\104\006\004\125\035\040\000\060\074\060 -\072\006\010\053\006\001\005\005\007\002\001\026\056\150\164\164 -\160\072\057\057\167\167\167\056\160\153\151\157\166\145\162\150 -\145\151\144\056\156\154\057\160\157\154\151\143\151\145\163\057 -\162\157\157\164\055\160\157\154\151\143\171\060\016\006\003\125 -\035\017\001\001\377\004\004\003\002\001\006\060\035\006\003\125 -\035\016\004\026\004\024\250\175\353\274\143\244\164\023\164\000 -\354\226\340\323\064\301\054\277\154\370\060\015\006\011\052\206 -\110\206\367\015\001\001\005\005\000\003\202\001\001\000\005\204 -\207\125\164\066\141\301\273\321\324\306\025\250\023\264\237\244 -\376\273\356\025\264\057\006\014\051\362\250\222\244\141\015\374 -\253\134\010\133\121\023\053\115\302\052\141\310\370\011\130\374 -\055\002\262\071\175\231\146\201\277\156\134\225\105\040\154\346 -\171\247\321\330\034\051\374\302\040\047\121\310\361\174\135\064 -\147\151\205\021\060\306\000\322\327\363\323\174\266\360\061\127 -\050\022\202\163\351\063\057\246\125\264\013\221\224\107\234\372 -\273\172\102\062\350\256\176\055\310\274\254\024\277\331\017\331 -\133\374\301\371\172\225\341\175\176\226\374\161\260\302\114\310 -\337\105\064\311\316\015\362\234\144\010\320\073\303\051\305\262 -\355\220\004\301\261\051\221\305\060\157\301\251\162\063\314\376 -\135\026\027\054\021\151\347\176\376\305\203\010\337\274\334\042 -\072\056\040\151\043\071\126\140\147\220\213\056\166\071\373\021 -\210\227\366\174\275\113\270\040\026\147\005\215\342\073\301\162 -\077\224\225\067\307\135\271\236\330\223\241\027\217\377\014\146 -\025\301\044\174\062\174\003\035\073\241\130\105\062\223 -END - -# Trust for Certificate "Staat der Nederlanden Root CA" -# Issuer: CN=Staat der Nederlanden Root CA,O=Staat der Nederlanden,C=NL -# Serial Number: 10000010 (0x98968a) -# Subject: CN=Staat der Nederlanden Root CA,O=Staat der Nederlanden,C=NL -# Not Valid Before: Tue Dec 17 09:23:49 2002 -# Not Valid After : Wed Dec 16 09:15:38 2015 -# Fingerprint (MD5): 60:84:7C:5A:CE:DB:0C:D4:CB:A7:E9:FE:02:C6:A9:C0 -# Fingerprint (SHA1): 10:1D:FA:3F:D5:0B:CB:BB:9B:B5:60:0C:19:55:A4:1A:F4:73:3A:04 -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "Staat der Nederlanden Root CA" -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\020\035\372\077\325\013\313\273\233\265\140\014\031\125\244\032 -\364\163\072\004 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\140\204\174\132\316\333\014\324\313\247\351\376\002\306\251\300 -END -CKA_ISSUER MULTILINE_OCTAL -\060\125\061\013\060\011\006\003\125\004\006\023\002\116\114\061 -\036\060\034\006\003\125\004\012\023\025\123\164\141\141\164\040 -\144\145\162\040\116\145\144\145\162\154\141\156\144\145\156\061 -\046\060\044\006\003\125\004\003\023\035\123\164\141\141\164\040 -\144\145\162\040\116\145\144\145\162\154\141\156\144\145\156\040 -\122\157\157\164\040\103\101 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\004\000\230\226\212 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - # # Certificate "UTN USERFirst Email Root CA" # @@ -6120,791 +5691,42 @@ CKA_VALUE MULTILINE_OCTAL \166\135\165\220\032\365\046\217\360 END -# Trust for Certificate "Camerfirma Global Chambersign Root" -# Issuer: CN=Global Chambersign Root,OU=http://www.chambersign.org,O=AC Camerfirma SA CIF A82743287,C=EU -# Serial Number: 0 (0x0) -# Subject: CN=Global Chambersign Root,OU=http://www.chambersign.org,O=AC Camerfirma SA CIF A82743287,C=EU -# Not Valid Before: Tue Sep 30 16:14:18 2003 -# Not Valid After : Wed Sep 30 16:14:18 2037 -# Fingerprint (MD5): C5:E6:7B:BF:06:D0:4F:43:ED:C4:7A:65:8A:FB:6B:19 -# Fingerprint (SHA1): 33:9B:6B:14:50:24:9B:55:7A:01:87:72:84:D9:E0:2F:C3:D2:D8:E9 -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "Camerfirma Global Chambersign Root" -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\063\233\153\024\120\044\233\125\172\001\207\162\204\331\340\057 -\303\322\330\351 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\305\346\173\277\006\320\117\103\355\304\172\145\212\373\153\031 -END -CKA_ISSUER MULTILINE_OCTAL -\060\175\061\013\060\011\006\003\125\004\006\023\002\105\125\061 -\047\060\045\006\003\125\004\012\023\036\101\103\040\103\141\155 -\145\162\146\151\162\155\141\040\123\101\040\103\111\106\040\101 -\070\062\067\064\063\062\070\067\061\043\060\041\006\003\125\004 -\013\023\032\150\164\164\160\072\057\057\167\167\167\056\143\150 -\141\155\142\145\162\163\151\147\156\056\157\162\147\061\040\060 -\036\006\003\125\004\003\023\027\107\154\157\142\141\154\040\103 -\150\141\155\142\145\162\163\151\147\156\040\122\157\157\164 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\001\000 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - -# -# Certificate "NetLock Qualified (Class QA) Root" -# -# Issuer: E=info@netlock.hu,CN=NetLock Minositett Kozjegyzoi (Class QA) Tanusitvanykiado,OU=Tanusitvanykiadok,O=NetLock Halozatbiztonsagi Kft.,L=Budapest,C=HU -# Serial Number: 123 (0x7b) -# Subject: E=info@netlock.hu,CN=NetLock Minositett Kozjegyzoi (Class QA) Tanusitvanykiado,OU=Tanusitvanykiadok,O=NetLock Halozatbiztonsagi Kft.,L=Budapest,C=HU -# Not Valid Before: Sun Mar 30 01:47:11 2003 -# Not Valid After : Thu Dec 15 01:47:11 2022 -# Fingerprint (MD5): D4:80:65:68:24:F9:89:22:28:DB:F5:A4:9A:17:8F:14 -# Fingerprint (SHA1): 01:68:97:E1:A0:B8:F2:C3:B1:34:66:5C:20:A7:27:B7:A1:58:E2:8F -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "NetLock Qualified (Class QA) Root" -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\201\311\061\013\060\011\006\003\125\004\006\023\002\110\125 -\061\021\060\017\006\003\125\004\007\023\010\102\165\144\141\160 -\145\163\164\061\047\060\045\006\003\125\004\012\023\036\116\145 -\164\114\157\143\153\040\110\141\154\157\172\141\164\142\151\172 -\164\157\156\163\141\147\151\040\113\146\164\056\061\032\060\030 -\006\003\125\004\013\023\021\124\141\156\165\163\151\164\166\141 -\156\171\153\151\141\144\157\153\061\102\060\100\006\003\125\004 -\003\023\071\116\145\164\114\157\143\153\040\115\151\156\157\163 -\151\164\145\164\164\040\113\157\172\152\145\147\171\172\157\151 -\040\050\103\154\141\163\163\040\121\101\051\040\124\141\156\165 -\163\151\164\166\141\156\171\153\151\141\144\157\061\036\060\034 -\006\011\052\206\110\206\367\015\001\011\001\026\017\151\156\146 -\157\100\156\145\164\154\157\143\153\056\150\165 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\201\311\061\013\060\011\006\003\125\004\006\023\002\110\125 -\061\021\060\017\006\003\125\004\007\023\010\102\165\144\141\160 -\145\163\164\061\047\060\045\006\003\125\004\012\023\036\116\145 -\164\114\157\143\153\040\110\141\154\157\172\141\164\142\151\172 -\164\157\156\163\141\147\151\040\113\146\164\056\061\032\060\030 -\006\003\125\004\013\023\021\124\141\156\165\163\151\164\166\141 -\156\171\153\151\141\144\157\153\061\102\060\100\006\003\125\004 -\003\023\071\116\145\164\114\157\143\153\040\115\151\156\157\163 -\151\164\145\164\164\040\113\157\172\152\145\147\171\172\157\151 -\040\050\103\154\141\163\163\040\121\101\051\040\124\141\156\165 -\163\151\164\166\141\156\171\153\151\141\144\157\061\036\060\034 -\006\011\052\206\110\206\367\015\001\011\001\026\017\151\156\146 -\157\100\156\145\164\154\157\143\153\056\150\165 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\001\173 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\006\321\060\202\005\271\240\003\002\001\002\002\001\173 -\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060 -\201\311\061\013\060\011\006\003\125\004\006\023\002\110\125\061 -\021\060\017\006\003\125\004\007\023\010\102\165\144\141\160\145 -\163\164\061\047\060\045\006\003\125\004\012\023\036\116\145\164 -\114\157\143\153\040\110\141\154\157\172\141\164\142\151\172\164 -\157\156\163\141\147\151\040\113\146\164\056\061\032\060\030\006 -\003\125\004\013\023\021\124\141\156\165\163\151\164\166\141\156 -\171\153\151\141\144\157\153\061\102\060\100\006\003\125\004\003 -\023\071\116\145\164\114\157\143\153\040\115\151\156\157\163\151 -\164\145\164\164\040\113\157\172\152\145\147\171\172\157\151\040 -\050\103\154\141\163\163\040\121\101\051\040\124\141\156\165\163 -\151\164\166\141\156\171\153\151\141\144\157\061\036\060\034\006 -\011\052\206\110\206\367\015\001\011\001\026\017\151\156\146\157 -\100\156\145\164\154\157\143\153\056\150\165\060\036\027\015\060 -\063\060\063\063\060\060\061\064\067\061\061\132\027\015\062\062 -\061\062\061\065\060\061\064\067\061\061\132\060\201\311\061\013 -\060\011\006\003\125\004\006\023\002\110\125\061\021\060\017\006 -\003\125\004\007\023\010\102\165\144\141\160\145\163\164\061\047 -\060\045\006\003\125\004\012\023\036\116\145\164\114\157\143\153 -\040\110\141\154\157\172\141\164\142\151\172\164\157\156\163\141 -\147\151\040\113\146\164\056\061\032\060\030\006\003\125\004\013 -\023\021\124\141\156\165\163\151\164\166\141\156\171\153\151\141 -\144\157\153\061\102\060\100\006\003\125\004\003\023\071\116\145 -\164\114\157\143\153\040\115\151\156\157\163\151\164\145\164\164 -\040\113\157\172\152\145\147\171\172\157\151\040\050\103\154\141 -\163\163\040\121\101\051\040\124\141\156\165\163\151\164\166\141 -\156\171\153\151\141\144\157\061\036\060\034\006\011\052\206\110 -\206\367\015\001\011\001\026\017\151\156\146\157\100\156\145\164 -\154\157\143\153\056\150\165\060\202\001\042\060\015\006\011\052 -\206\110\206\367\015\001\001\001\005\000\003\202\001\017\000\060 -\202\001\012\002\202\001\001\000\307\122\045\262\330\075\324\204 -\125\011\247\033\275\154\271\024\364\212\002\333\166\374\152\052 -\170\253\345\167\360\156\340\214\043\147\333\245\144\231\271\335 -\001\076\157\357\055\232\074\042\360\135\311\127\240\125\101\177 -\362\103\136\130\202\123\061\145\316\036\362\046\272\000\124\036 -\257\260\274\034\344\122\214\240\062\257\267\067\261\123\147\150 -\164\147\120\366\055\056\144\336\256\046\171\337\337\231\206\253 -\253\177\205\354\240\373\200\314\364\270\014\036\223\105\143\271 -\334\270\133\233\355\133\071\324\137\142\260\247\216\174\146\070 -\054\252\261\010\143\027\147\175\314\275\263\361\303\077\317\120 -\071\355\321\031\203\025\333\207\022\047\226\267\332\352\345\235 -\274\272\352\071\117\213\357\164\232\347\305\320\322\352\206\121 -\034\344\376\144\010\050\004\171\005\353\312\305\161\016\013\357 -\253\352\354\022\021\241\030\005\062\151\321\014\054\032\075\045 -\231\077\265\174\312\155\260\256\231\231\372\010\140\347\031\302 -\362\275\121\323\314\323\002\254\301\021\014\200\316\253\334\224 -\235\153\243\071\123\072\326\205\002\003\000\305\175\243\202\002 -\300\060\202\002\274\060\022\006\003\125\035\023\001\001\377\004 -\010\060\006\001\001\377\002\001\004\060\016\006\003\125\035\017 -\001\001\377\004\004\003\002\001\006\060\202\002\165\006\011\140 -\206\110\001\206\370\102\001\015\004\202\002\146\026\202\002\142 -\106\111\107\131\105\114\105\115\041\040\105\172\145\156\040\164 -\141\156\165\163\151\164\166\141\156\171\040\141\040\116\145\164 -\114\157\143\153\040\113\146\164\056\040\115\151\156\157\163\151 -\164\145\164\164\040\123\172\157\154\147\141\154\164\141\164\141 -\163\151\040\123\172\141\142\141\154\171\172\141\164\141\142\141 -\156\040\154\145\151\162\164\040\145\154\152\141\162\141\163\157 -\153\040\141\154\141\160\152\141\156\040\153\145\163\172\165\154 -\164\056\040\101\040\155\151\156\157\163\151\164\145\164\164\040 -\145\154\145\153\164\162\157\156\151\153\165\163\040\141\154\141 -\151\162\141\163\040\152\157\147\150\141\164\141\163\040\145\162 -\166\145\156\171\145\163\165\154\145\163\145\156\145\153\054\040 -\166\141\154\141\155\151\156\164\040\145\154\146\157\147\141\144 -\141\163\141\156\141\153\040\146\145\154\164\145\164\145\154\145 -\040\141\040\115\151\156\157\163\151\164\145\164\164\040\123\172 -\157\154\147\141\154\164\141\164\141\163\151\040\123\172\141\142 -\141\154\171\172\141\164\142\141\156\054\040\141\172\040\101\154 -\164\141\154\141\156\157\163\040\123\172\145\162\172\157\144\145 -\163\151\040\106\145\154\164\145\164\145\154\145\153\142\145\156 -\040\145\154\157\151\162\164\040\145\154\154\145\156\157\162\172 -\145\163\151\040\145\154\152\141\162\141\163\040\155\145\147\164 -\145\164\145\154\145\056\040\101\040\144\157\153\165\155\145\156 -\164\165\155\157\153\040\155\145\147\164\141\154\141\154\150\141 -\164\157\153\040\141\040\150\164\164\160\163\072\057\057\167\167 -\167\056\156\145\164\154\157\143\153\056\150\165\057\144\157\143 -\163\057\040\143\151\155\145\156\040\166\141\147\171\040\153\145 -\162\150\145\164\157\153\040\141\172\040\151\156\146\157\100\156 -\145\164\154\157\143\153\056\156\145\164\040\145\055\155\141\151 -\154\040\143\151\155\145\156\056\040\127\101\122\116\111\116\107 -\041\040\124\150\145\040\151\163\163\165\141\156\143\145\040\141 -\156\144\040\164\150\145\040\165\163\145\040\157\146\040\164\150 -\151\163\040\143\145\162\164\151\146\151\143\141\164\145\040\141 -\162\145\040\163\165\142\152\145\143\164\040\164\157\040\164\150 -\145\040\116\145\164\114\157\143\153\040\121\165\141\154\151\146 -\151\145\144\040\103\120\123\040\141\166\141\151\154\141\142\154 -\145\040\141\164\040\150\164\164\160\163\072\057\057\167\167\167 -\056\156\145\164\154\157\143\153\056\150\165\057\144\157\143\163 -\057\040\157\162\040\142\171\040\145\055\155\141\151\154\040\141 -\164\040\151\156\146\157\100\156\145\164\154\157\143\153\056\156 -\145\164\060\035\006\003\125\035\016\004\026\004\024\011\152\142 -\026\222\260\132\273\125\016\313\165\062\072\062\345\262\041\311 -\050\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000 -\003\202\001\001\000\221\152\120\234\333\170\201\233\077\213\102 -\343\073\374\246\303\356\103\340\317\363\342\200\065\111\105\166 -\002\342\343\057\005\305\361\052\347\300\101\063\306\266\233\320 -\063\071\315\300\333\241\255\154\067\002\114\130\101\073\362\227 -\222\306\110\250\315\345\212\071\211\141\371\122\227\351\275\366 -\371\224\164\350\161\016\274\167\206\303\006\314\132\174\112\176 -\064\120\060\056\373\177\062\232\215\075\363\040\133\370\152\312 -\206\363\061\114\054\131\200\002\175\376\070\311\060\165\034\267 -\125\343\274\237\272\250\155\204\050\005\165\263\213\015\300\221 -\124\041\347\246\013\264\231\365\121\101\334\315\243\107\042\331 -\307\001\201\304\334\107\117\046\352\037\355\333\315\015\230\364 -\243\234\264\163\062\112\226\231\376\274\177\310\045\130\370\130 -\363\166\146\211\124\244\246\076\304\120\134\272\211\030\202\165 -\110\041\322\117\023\350\140\176\007\166\333\020\265\121\346\252 -\271\150\252\315\366\235\220\165\022\352\070\032\312\104\350\267 -\231\247\052\150\225\146\225\253\255\357\211\313\140\251\006\022 -\306\224\107\351\050 -END - -# Trust for Certificate "NetLock Qualified (Class QA) Root" -# Issuer: E=info@netlock.hu,CN=NetLock Minositett Kozjegyzoi (Class QA) Tanusitvanykiado,OU=Tanusitvanykiadok,O=NetLock Halozatbiztonsagi Kft.,L=Budapest,C=HU -# Serial Number: 123 (0x7b) -# Subject: E=info@netlock.hu,CN=NetLock Minositett Kozjegyzoi (Class QA) Tanusitvanykiado,OU=Tanusitvanykiadok,O=NetLock Halozatbiztonsagi Kft.,L=Budapest,C=HU -# Not Valid Before: Sun Mar 30 01:47:11 2003 -# Not Valid After : Thu Dec 15 01:47:11 2022 -# Fingerprint (MD5): D4:80:65:68:24:F9:89:22:28:DB:F5:A4:9A:17:8F:14 -# Fingerprint (SHA1): 01:68:97:E1:A0:B8:F2:C3:B1:34:66:5C:20:A7:27:B7:A1:58:E2:8F -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "NetLock Qualified (Class QA) Root" -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\001\150\227\341\240\270\362\303\261\064\146\134\040\247\047\267 -\241\130\342\217 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\324\200\145\150\044\371\211\042\050\333\365\244\232\027\217\024 -END -CKA_ISSUER MULTILINE_OCTAL -\060\201\311\061\013\060\011\006\003\125\004\006\023\002\110\125 -\061\021\060\017\006\003\125\004\007\023\010\102\165\144\141\160 -\145\163\164\061\047\060\045\006\003\125\004\012\023\036\116\145 -\164\114\157\143\153\040\110\141\154\157\172\141\164\142\151\172 -\164\157\156\163\141\147\151\040\113\146\164\056\061\032\060\030 -\006\003\125\004\013\023\021\124\141\156\165\163\151\164\166\141 -\156\171\153\151\141\144\157\153\061\102\060\100\006\003\125\004 -\003\023\071\116\145\164\114\157\143\153\040\115\151\156\157\163 -\151\164\145\164\164\040\113\157\172\152\145\147\171\172\157\151 -\040\050\103\154\141\163\163\040\121\101\051\040\124\141\156\165 -\163\151\164\166\141\156\171\153\151\141\144\157\061\036\060\034 -\006\011\052\206\110\206\367\015\001\011\001\026\017\151\156\146 -\157\100\156\145\164\154\157\143\153\056\150\165 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\001\173 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - -# -# Certificate "NetLock Notary (Class A) Root" -# -# Issuer: CN=NetLock Kozjegyzoi (Class A) Tanusitvanykiado,OU=Tanusitvanykiadok,O=NetLock Halozatbiztonsagi Kft.,L=Budapest,ST=Hungary,C=HU -# Serial Number: 259 (0x103) -# Subject: CN=NetLock Kozjegyzoi (Class A) Tanusitvanykiado,OU=Tanusitvanykiadok,O=NetLock Halozatbiztonsagi Kft.,L=Budapest,ST=Hungary,C=HU -# Not Valid Before: Wed Feb 24 23:14:47 1999 -# Not Valid After : Tue Feb 19 23:14:47 2019 -# Fingerprint (MD5): 86:38:6D:5E:49:63:6C:85:5C:DB:6D:DC:94:B7:D0:F7 -# Fingerprint (SHA1): AC:ED:5F:65:53:FD:25:CE:01:5F:1F:7A:48:3B:6A:74:9F:61:78:C6 -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "NetLock Notary (Class A) Root" -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\201\257\061\013\060\011\006\003\125\004\006\023\002\110\125 -\061\020\060\016\006\003\125\004\010\023\007\110\165\156\147\141 -\162\171\061\021\060\017\006\003\125\004\007\023\010\102\165\144 -\141\160\145\163\164\061\047\060\045\006\003\125\004\012\023\036 -\116\145\164\114\157\143\153\040\110\141\154\157\172\141\164\142 -\151\172\164\157\156\163\141\147\151\040\113\146\164\056\061\032 -\060\030\006\003\125\004\013\023\021\124\141\156\165\163\151\164 -\166\141\156\171\153\151\141\144\157\153\061\066\060\064\006\003 -\125\004\003\023\055\116\145\164\114\157\143\153\040\113\157\172 -\152\145\147\171\172\157\151\040\050\103\154\141\163\163\040\101 -\051\040\124\141\156\165\163\151\164\166\141\156\171\153\151\141 -\144\157 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\201\257\061\013\060\011\006\003\125\004\006\023\002\110\125 -\061\020\060\016\006\003\125\004\010\023\007\110\165\156\147\141 -\162\171\061\021\060\017\006\003\125\004\007\023\010\102\165\144 -\141\160\145\163\164\061\047\060\045\006\003\125\004\012\023\036 -\116\145\164\114\157\143\153\040\110\141\154\157\172\141\164\142 -\151\172\164\157\156\163\141\147\151\040\113\146\164\056\061\032 -\060\030\006\003\125\004\013\023\021\124\141\156\165\163\151\164 -\166\141\156\171\153\151\141\144\157\153\061\066\060\064\006\003 -\125\004\003\023\055\116\145\164\114\157\143\153\040\113\157\172 -\152\145\147\171\172\157\151\040\050\103\154\141\163\163\040\101 -\051\040\124\141\156\165\163\151\164\166\141\156\171\153\151\141 -\144\157 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\002\001\003 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\006\175\060\202\005\145\240\003\002\001\002\002\002\001 -\003\060\015\006\011\052\206\110\206\367\015\001\001\004\005\000 -\060\201\257\061\013\060\011\006\003\125\004\006\023\002\110\125 -\061\020\060\016\006\003\125\004\010\023\007\110\165\156\147\141 -\162\171\061\021\060\017\006\003\125\004\007\023\010\102\165\144 -\141\160\145\163\164\061\047\060\045\006\003\125\004\012\023\036 -\116\145\164\114\157\143\153\040\110\141\154\157\172\141\164\142 -\151\172\164\157\156\163\141\147\151\040\113\146\164\056\061\032 -\060\030\006\003\125\004\013\023\021\124\141\156\165\163\151\164 -\166\141\156\171\153\151\141\144\157\153\061\066\060\064\006\003 -\125\004\003\023\055\116\145\164\114\157\143\153\040\113\157\172 -\152\145\147\171\172\157\151\040\050\103\154\141\163\163\040\101 -\051\040\124\141\156\165\163\151\164\166\141\156\171\153\151\141 -\144\157\060\036\027\015\071\071\060\062\062\064\062\063\061\064 -\064\067\132\027\015\061\071\060\062\061\071\062\063\061\064\064 -\067\132\060\201\257\061\013\060\011\006\003\125\004\006\023\002 -\110\125\061\020\060\016\006\003\125\004\010\023\007\110\165\156 -\147\141\162\171\061\021\060\017\006\003\125\004\007\023\010\102 -\165\144\141\160\145\163\164\061\047\060\045\006\003\125\004\012 -\023\036\116\145\164\114\157\143\153\040\110\141\154\157\172\141 -\164\142\151\172\164\157\156\163\141\147\151\040\113\146\164\056 -\061\032\060\030\006\003\125\004\013\023\021\124\141\156\165\163 -\151\164\166\141\156\171\153\151\141\144\157\153\061\066\060\064 -\006\003\125\004\003\023\055\116\145\164\114\157\143\153\040\113 -\157\172\152\145\147\171\172\157\151\040\050\103\154\141\163\163 -\040\101\051\040\124\141\156\165\163\151\164\166\141\156\171\153 -\151\141\144\157\060\202\001\042\060\015\006\011\052\206\110\206 -\367\015\001\001\001\005\000\003\202\001\017\000\060\202\001\012 -\002\202\001\001\000\274\164\214\017\273\114\364\067\036\251\005 -\202\330\346\341\154\160\352\170\265\156\321\070\104\015\250\203 -\316\135\322\326\325\201\305\324\113\347\133\224\160\046\333\073 -\235\152\114\142\367\161\363\144\326\141\073\075\353\163\243\067 -\331\317\352\214\222\073\315\367\007\334\146\164\227\364\105\042 -\335\364\134\340\277\155\363\276\145\063\344\025\072\277\333\230 -\220\125\070\304\355\246\125\143\013\260\170\004\364\343\156\301 -\077\216\374\121\170\037\222\236\203\302\376\331\260\251\311\274 -\132\000\377\251\250\230\164\373\366\054\076\025\071\015\266\004 -\125\250\016\230\040\102\263\261\045\255\176\232\157\135\123\261 -\253\014\374\353\340\363\172\263\250\263\377\106\366\143\242\330 -\072\230\173\266\254\205\377\260\045\117\164\143\347\023\007\245 -\012\217\005\367\300\144\157\176\247\047\200\226\336\324\056\206 -\140\307\153\053\136\163\173\027\347\221\077\144\014\330\113\042 -\064\053\233\062\362\110\037\237\241\012\204\172\342\302\255\227 -\075\216\325\301\371\126\243\120\351\306\264\372\230\242\356\225 -\346\052\003\214\337\002\003\001\000\001\243\202\002\237\060\202 -\002\233\060\016\006\003\125\035\017\001\001\377\004\004\003\002 -\000\006\060\022\006\003\125\035\023\001\001\377\004\010\060\006 -\001\001\377\002\001\004\060\021\006\011\140\206\110\001\206\370 -\102\001\001\004\004\003\002\000\007\060\202\002\140\006\011\140 -\206\110\001\206\370\102\001\015\004\202\002\121\026\202\002\115 -\106\111\107\131\105\114\105\115\041\040\105\172\145\156\040\164 -\141\156\165\163\151\164\166\141\156\171\040\141\040\116\145\164 -\114\157\143\153\040\113\146\164\056\040\101\154\164\141\154\141 -\156\157\163\040\123\172\157\154\147\141\154\164\141\164\141\163 -\151\040\106\145\154\164\145\164\145\154\145\151\142\145\156\040 -\154\145\151\162\164\040\145\154\152\141\162\141\163\157\153\040 -\141\154\141\160\152\141\156\040\153\145\163\172\165\154\164\056 -\040\101\040\150\151\164\145\154\145\163\151\164\145\163\040\146 -\157\154\171\141\155\141\164\141\164\040\141\040\116\145\164\114 -\157\143\153\040\113\146\164\056\040\164\145\162\155\145\153\146 -\145\154\145\154\157\163\163\145\147\055\142\151\172\164\157\163 -\151\164\141\163\141\040\166\145\144\151\056\040\101\040\144\151 -\147\151\164\141\154\151\163\040\141\154\141\151\162\141\163\040 -\145\154\146\157\147\141\144\141\163\141\156\141\153\040\146\145 -\154\164\145\164\145\154\145\040\141\172\040\145\154\157\151\162 -\164\040\145\154\154\145\156\157\162\172\145\163\151\040\145\154 -\152\141\162\141\163\040\155\145\147\164\145\164\145\154\145\056 -\040\101\172\040\145\154\152\141\162\141\163\040\154\145\151\162 -\141\163\141\040\155\145\147\164\141\154\141\154\150\141\164\157 -\040\141\040\116\145\164\114\157\143\153\040\113\146\164\056\040 -\111\156\164\145\162\156\145\164\040\150\157\156\154\141\160\152 -\141\156\040\141\040\150\164\164\160\163\072\057\057\167\167\167 -\056\156\145\164\154\157\143\153\056\156\145\164\057\144\157\143 -\163\040\143\151\155\145\156\040\166\141\147\171\040\153\145\162 -\150\145\164\157\040\141\172\040\145\154\154\145\156\157\162\172 -\145\163\100\156\145\164\154\157\143\153\056\156\145\164\040\145 -\055\155\141\151\154\040\143\151\155\145\156\056\040\111\115\120 -\117\122\124\101\116\124\041\040\124\150\145\040\151\163\163\165 -\141\156\143\145\040\141\156\144\040\164\150\145\040\165\163\145 -\040\157\146\040\164\150\151\163\040\143\145\162\164\151\146\151 -\143\141\164\145\040\151\163\040\163\165\142\152\145\143\164\040 -\164\157\040\164\150\145\040\116\145\164\114\157\143\153\040\103 -\120\123\040\141\166\141\151\154\141\142\154\145\040\141\164\040 -\150\164\164\160\163\072\057\057\167\167\167\056\156\145\164\154 -\157\143\153\056\156\145\164\057\144\157\143\163\040\157\162\040 -\142\171\040\145\055\155\141\151\154\040\141\164\040\143\160\163 -\100\156\145\164\154\157\143\153\056\156\145\164\056\060\015\006 -\011\052\206\110\206\367\015\001\001\004\005\000\003\202\001\001 -\000\110\044\106\367\272\126\157\372\310\050\003\100\116\345\061 -\071\153\046\153\123\177\333\337\337\363\161\075\046\300\024\016 -\306\147\173\043\250\014\163\335\001\273\306\312\156\067\071\125 -\325\307\214\126\040\016\050\012\016\322\052\244\260\111\122\306 -\070\007\376\276\012\011\214\321\230\317\312\332\024\061\241\117 -\322\071\374\017\021\054\103\303\335\253\223\307\125\076\107\174 -\030\032\000\334\363\173\330\362\177\122\154\040\364\013\137\151 -\122\364\356\370\262\051\140\353\343\111\061\041\015\326\265\020 -\101\342\101\011\154\342\032\232\126\113\167\002\366\240\233\232 -\047\207\350\125\051\161\302\220\237\105\170\032\341\025\144\075 -\320\016\330\240\166\237\256\305\320\056\352\326\017\126\354\144 -\177\132\233\024\130\001\047\176\023\120\307\153\052\346\150\074 -\277\134\240\012\033\341\016\172\351\342\200\303\351\351\366\375 -\154\021\236\320\345\050\047\053\124\062\102\024\202\165\346\112 -\360\053\146\165\143\214\242\373\004\076\203\016\233\066\360\030 -\344\046\040\303\214\360\050\007\255\074\027\146\210\265\375\266 -\210 -END - -# Trust for Certificate "NetLock Notary (Class A) Root" -# Issuer: CN=NetLock Kozjegyzoi (Class A) Tanusitvanykiado,OU=Tanusitvanykiadok,O=NetLock Halozatbiztonsagi Kft.,L=Budapest,ST=Hungary,C=HU -# Serial Number: 259 (0x103) -# Subject: CN=NetLock Kozjegyzoi (Class A) Tanusitvanykiado,OU=Tanusitvanykiadok,O=NetLock Halozatbiztonsagi Kft.,L=Budapest,ST=Hungary,C=HU -# Not Valid Before: Wed Feb 24 23:14:47 1999 -# Not Valid After : Tue Feb 19 23:14:47 2019 -# Fingerprint (MD5): 86:38:6D:5E:49:63:6C:85:5C:DB:6D:DC:94:B7:D0:F7 -# Fingerprint (SHA1): AC:ED:5F:65:53:FD:25:CE:01:5F:1F:7A:48:3B:6A:74:9F:61:78:C6 -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "NetLock Notary (Class A) Root" -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\254\355\137\145\123\375\045\316\001\137\037\172\110\073\152\164 -\237\141\170\306 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\206\070\155\136\111\143\154\205\134\333\155\334\224\267\320\367 -END -CKA_ISSUER MULTILINE_OCTAL -\060\201\257\061\013\060\011\006\003\125\004\006\023\002\110\125 -\061\020\060\016\006\003\125\004\010\023\007\110\165\156\147\141 -\162\171\061\021\060\017\006\003\125\004\007\023\010\102\165\144 -\141\160\145\163\164\061\047\060\045\006\003\125\004\012\023\036 -\116\145\164\114\157\143\153\040\110\141\154\157\172\141\164\142 -\151\172\164\157\156\163\141\147\151\040\113\146\164\056\061\032 -\060\030\006\003\125\004\013\023\021\124\141\156\165\163\151\164 -\166\141\156\171\153\151\141\144\157\153\061\066\060\064\006\003 -\125\004\003\023\055\116\145\164\114\157\143\153\040\113\157\172 -\152\145\147\171\172\157\151\040\050\103\154\141\163\163\040\101 -\051\040\124\141\156\165\163\151\164\166\141\156\171\153\151\141 -\144\157 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\002\001\003 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - -# -# Certificate "NetLock Business (Class B) Root" -# -# Issuer: CN=NetLock Uzleti (Class B) Tanusitvanykiado,OU=Tanusitvanykiadok,O=NetLock Halozatbiztonsagi Kft.,L=Budapest,C=HU -# Serial Number: 105 (0x69) -# Subject: CN=NetLock Uzleti (Class B) Tanusitvanykiado,OU=Tanusitvanykiadok,O=NetLock Halozatbiztonsagi Kft.,L=Budapest,C=HU -# Not Valid Before: Thu Feb 25 14:10:22 1999 -# Not Valid After : Wed Feb 20 14:10:22 2019 -# Fingerprint (MD5): 39:16:AA:B9:6A:41:E1:14:69:DF:9E:6C:3B:72:DC:B6 -# Fingerprint (SHA1): 87:9F:4B:EE:05:DF:98:58:3B:E3:60:D6:33:E7:0D:3F:FE:98:71:AF -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "NetLock Business (Class B) Root" -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\201\231\061\013\060\011\006\003\125\004\006\023\002\110\125 -\061\021\060\017\006\003\125\004\007\023\010\102\165\144\141\160 -\145\163\164\061\047\060\045\006\003\125\004\012\023\036\116\145 -\164\114\157\143\153\040\110\141\154\157\172\141\164\142\151\172 -\164\157\156\163\141\147\151\040\113\146\164\056\061\032\060\030 -\006\003\125\004\013\023\021\124\141\156\165\163\151\164\166\141 -\156\171\153\151\141\144\157\153\061\062\060\060\006\003\125\004 -\003\023\051\116\145\164\114\157\143\153\040\125\172\154\145\164 -\151\040\050\103\154\141\163\163\040\102\051\040\124\141\156\165 -\163\151\164\166\141\156\171\153\151\141\144\157 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\201\231\061\013\060\011\006\003\125\004\006\023\002\110\125 -\061\021\060\017\006\003\125\004\007\023\010\102\165\144\141\160 -\145\163\164\061\047\060\045\006\003\125\004\012\023\036\116\145 -\164\114\157\143\153\040\110\141\154\157\172\141\164\142\151\172 -\164\157\156\163\141\147\151\040\113\146\164\056\061\032\060\030 -\006\003\125\004\013\023\021\124\141\156\165\163\151\164\166\141 -\156\171\153\151\141\144\157\153\061\062\060\060\006\003\125\004 -\003\023\051\116\145\164\114\157\143\153\040\125\172\154\145\164 -\151\040\050\103\154\141\163\163\040\102\051\040\124\141\156\165 -\163\151\164\166\141\156\171\153\151\141\144\157 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\001\151 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\005\113\060\202\004\264\240\003\002\001\002\002\001\151 -\060\015\006\011\052\206\110\206\367\015\001\001\004\005\000\060 -\201\231\061\013\060\011\006\003\125\004\006\023\002\110\125\061 -\021\060\017\006\003\125\004\007\023\010\102\165\144\141\160\145 -\163\164\061\047\060\045\006\003\125\004\012\023\036\116\145\164 -\114\157\143\153\040\110\141\154\157\172\141\164\142\151\172\164 -\157\156\163\141\147\151\040\113\146\164\056\061\032\060\030\006 -\003\125\004\013\023\021\124\141\156\165\163\151\164\166\141\156 -\171\153\151\141\144\157\153\061\062\060\060\006\003\125\004\003 -\023\051\116\145\164\114\157\143\153\040\125\172\154\145\164\151 -\040\050\103\154\141\163\163\040\102\051\040\124\141\156\165\163 -\151\164\166\141\156\171\153\151\141\144\157\060\036\027\015\071 -\071\060\062\062\065\061\064\061\060\062\062\132\027\015\061\071 -\060\062\062\060\061\064\061\060\062\062\132\060\201\231\061\013 -\060\011\006\003\125\004\006\023\002\110\125\061\021\060\017\006 -\003\125\004\007\023\010\102\165\144\141\160\145\163\164\061\047 -\060\045\006\003\125\004\012\023\036\116\145\164\114\157\143\153 -\040\110\141\154\157\172\141\164\142\151\172\164\157\156\163\141 -\147\151\040\113\146\164\056\061\032\060\030\006\003\125\004\013 -\023\021\124\141\156\165\163\151\164\166\141\156\171\153\151\141 -\144\157\153\061\062\060\060\006\003\125\004\003\023\051\116\145 -\164\114\157\143\153\040\125\172\154\145\164\151\040\050\103\154 -\141\163\163\040\102\051\040\124\141\156\165\163\151\164\166\141 -\156\171\153\151\141\144\157\060\201\237\060\015\006\011\052\206 -\110\206\367\015\001\001\001\005\000\003\201\215\000\060\201\211 -\002\201\201\000\261\352\004\354\040\240\043\302\217\070\140\317 -\307\106\263\325\033\376\373\271\231\236\004\334\034\177\214\112 -\201\230\356\244\324\312\212\027\271\042\177\203\012\165\114\233 -\300\151\330\144\071\243\355\222\243\375\133\134\164\032\300\107 -\312\072\151\166\232\272\342\104\027\374\114\243\325\376\270\227 -\210\257\210\003\211\037\244\362\004\076\310\007\013\346\371\263 -\057\172\142\024\011\106\024\312\144\365\213\200\265\142\250\330 -\153\326\161\223\055\263\277\011\124\130\355\006\353\250\173\334 -\103\261\241\151\002\003\001\000\001\243\202\002\237\060\202\002 -\233\060\022\006\003\125\035\023\001\001\377\004\010\060\006\001 -\001\377\002\001\004\060\016\006\003\125\035\017\001\001\377\004 -\004\003\002\000\006\060\021\006\011\140\206\110\001\206\370\102 -\001\001\004\004\003\002\000\007\060\202\002\140\006\011\140\206 -\110\001\206\370\102\001\015\004\202\002\121\026\202\002\115\106 -\111\107\131\105\114\105\115\041\040\105\172\145\156\040\164\141 -\156\165\163\151\164\166\141\156\171\040\141\040\116\145\164\114 -\157\143\153\040\113\146\164\056\040\101\154\164\141\154\141\156 -\157\163\040\123\172\157\154\147\141\154\164\141\164\141\163\151 -\040\106\145\154\164\145\164\145\154\145\151\142\145\156\040\154 -\145\151\162\164\040\145\154\152\141\162\141\163\157\153\040\141 -\154\141\160\152\141\156\040\153\145\163\172\165\154\164\056\040 -\101\040\150\151\164\145\154\145\163\151\164\145\163\040\146\157 -\154\171\141\155\141\164\141\164\040\141\040\116\145\164\114\157 -\143\153\040\113\146\164\056\040\164\145\162\155\145\153\146\145 -\154\145\154\157\163\163\145\147\055\142\151\172\164\157\163\151 -\164\141\163\141\040\166\145\144\151\056\040\101\040\144\151\147 -\151\164\141\154\151\163\040\141\154\141\151\162\141\163\040\145 -\154\146\157\147\141\144\141\163\141\156\141\153\040\146\145\154 -\164\145\164\145\154\145\040\141\172\040\145\154\157\151\162\164 -\040\145\154\154\145\156\157\162\172\145\163\151\040\145\154\152 -\141\162\141\163\040\155\145\147\164\145\164\145\154\145\056\040 -\101\172\040\145\154\152\141\162\141\163\040\154\145\151\162\141 -\163\141\040\155\145\147\164\141\154\141\154\150\141\164\157\040 -\141\040\116\145\164\114\157\143\153\040\113\146\164\056\040\111 -\156\164\145\162\156\145\164\040\150\157\156\154\141\160\152\141 -\156\040\141\040\150\164\164\160\163\072\057\057\167\167\167\056 -\156\145\164\154\157\143\153\056\156\145\164\057\144\157\143\163 -\040\143\151\155\145\156\040\166\141\147\171\040\153\145\162\150 -\145\164\157\040\141\172\040\145\154\154\145\156\157\162\172\145 -\163\100\156\145\164\154\157\143\153\056\156\145\164\040\145\055 -\155\141\151\154\040\143\151\155\145\156\056\040\111\115\120\117 -\122\124\101\116\124\041\040\124\150\145\040\151\163\163\165\141 -\156\143\145\040\141\156\144\040\164\150\145\040\165\163\145\040 -\157\146\040\164\150\151\163\040\143\145\162\164\151\146\151\143 -\141\164\145\040\151\163\040\163\165\142\152\145\143\164\040\164 -\157\040\164\150\145\040\116\145\164\114\157\143\153\040\103\120 -\123\040\141\166\141\151\154\141\142\154\145\040\141\164\040\150 -\164\164\160\163\072\057\057\167\167\167\056\156\145\164\154\157 -\143\153\056\156\145\164\057\144\157\143\163\040\157\162\040\142 -\171\040\145\055\155\141\151\154\040\141\164\040\143\160\163\100 -\156\145\164\154\157\143\153\056\156\145\164\056\060\015\006\011 -\052\206\110\206\367\015\001\001\004\005\000\003\201\201\000\004 -\333\256\214\027\257\370\016\220\061\116\315\076\011\300\155\072 -\260\370\063\114\107\114\343\165\210\020\227\254\260\070\025\221 -\306\051\226\314\041\300\155\074\245\164\317\330\202\245\071\303 -\145\343\102\160\273\042\220\343\175\333\065\166\341\240\265\332 -\237\160\156\223\032\060\071\035\060\333\056\343\174\262\221\262 -\321\067\051\372\271\326\027\134\107\117\343\035\070\353\237\325 -\173\225\250\050\236\025\112\321\321\320\053\000\227\240\342\222 -\066\053\143\254\130\001\153\063\051\120\206\203\361\001\110 -END - -# Trust for Certificate "NetLock Business (Class B) Root" -# Issuer: CN=NetLock Uzleti (Class B) Tanusitvanykiado,OU=Tanusitvanykiadok,O=NetLock Halozatbiztonsagi Kft.,L=Budapest,C=HU -# Serial Number: 105 (0x69) -# Subject: CN=NetLock Uzleti (Class B) Tanusitvanykiado,OU=Tanusitvanykiadok,O=NetLock Halozatbiztonsagi Kft.,L=Budapest,C=HU -# Not Valid Before: Thu Feb 25 14:10:22 1999 -# Not Valid After : Wed Feb 20 14:10:22 2019 -# Fingerprint (MD5): 39:16:AA:B9:6A:41:E1:14:69:DF:9E:6C:3B:72:DC:B6 -# Fingerprint (SHA1): 87:9F:4B:EE:05:DF:98:58:3B:E3:60:D6:33:E7:0D:3F:FE:98:71:AF -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "NetLock Business (Class B) Root" -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\207\237\113\356\005\337\230\130\073\343\140\326\063\347\015\077 -\376\230\161\257 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\071\026\252\271\152\101\341\024\151\337\236\154\073\162\334\266 -END -CKA_ISSUER MULTILINE_OCTAL -\060\201\231\061\013\060\011\006\003\125\004\006\023\002\110\125 -\061\021\060\017\006\003\125\004\007\023\010\102\165\144\141\160 -\145\163\164\061\047\060\045\006\003\125\004\012\023\036\116\145 -\164\114\157\143\153\040\110\141\154\157\172\141\164\142\151\172 -\164\157\156\163\141\147\151\040\113\146\164\056\061\032\060\030 -\006\003\125\004\013\023\021\124\141\156\165\163\151\164\166\141 -\156\171\153\151\141\144\157\153\061\062\060\060\006\003\125\004 -\003\023\051\116\145\164\114\157\143\153\040\125\172\154\145\164 -\151\040\050\103\154\141\163\163\040\102\051\040\124\141\156\165 -\163\151\164\166\141\156\171\153\151\141\144\157 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\001\151 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - -# -# Certificate "NetLock Express (Class C) Root" -# -# Issuer: CN=NetLock Expressz (Class C) Tanusitvanykiado,OU=Tanusitvanykiadok,O=NetLock Halozatbiztonsagi Kft.,L=Budapest,C=HU -# Serial Number: 104 (0x68) -# Subject: CN=NetLock Expressz (Class C) Tanusitvanykiado,OU=Tanusitvanykiadok,O=NetLock Halozatbiztonsagi Kft.,L=Budapest,C=HU -# Not Valid Before: Thu Feb 25 14:08:11 1999 -# Not Valid After : Wed Feb 20 14:08:11 2019 -# Fingerprint (MD5): 4F:EB:F1:F0:70:C2:80:63:5D:58:9F:DA:12:3C:A9:C4 -# Fingerprint (SHA1): E3:92:51:2F:0A:CF:F5:05:DF:F6:DE:06:7F:75:37:E1:65:EA:57:4B -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "NetLock Express (Class C) Root" -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\201\233\061\013\060\011\006\003\125\004\006\023\002\110\125 -\061\021\060\017\006\003\125\004\007\023\010\102\165\144\141\160 -\145\163\164\061\047\060\045\006\003\125\004\012\023\036\116\145 -\164\114\157\143\153\040\110\141\154\157\172\141\164\142\151\172 -\164\157\156\163\141\147\151\040\113\146\164\056\061\032\060\030 -\006\003\125\004\013\023\021\124\141\156\165\163\151\164\166\141 -\156\171\153\151\141\144\157\153\061\064\060\062\006\003\125\004 -\003\023\053\116\145\164\114\157\143\153\040\105\170\160\162\145 -\163\163\172\040\050\103\154\141\163\163\040\103\051\040\124\141 -\156\165\163\151\164\166\141\156\171\153\151\141\144\157 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\201\233\061\013\060\011\006\003\125\004\006\023\002\110\125 -\061\021\060\017\006\003\125\004\007\023\010\102\165\144\141\160 -\145\163\164\061\047\060\045\006\003\125\004\012\023\036\116\145 -\164\114\157\143\153\040\110\141\154\157\172\141\164\142\151\172 -\164\157\156\163\141\147\151\040\113\146\164\056\061\032\060\030 -\006\003\125\004\013\023\021\124\141\156\165\163\151\164\166\141 -\156\171\153\151\141\144\157\153\061\064\060\062\006\003\125\004 -\003\023\053\116\145\164\114\157\143\153\040\105\170\160\162\145 -\163\163\172\040\050\103\154\141\163\163\040\103\051\040\124\141 -\156\165\163\151\164\166\141\156\171\153\151\141\144\157 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\001\150 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\005\117\060\202\004\270\240\003\002\001\002\002\001\150 -\060\015\006\011\052\206\110\206\367\015\001\001\004\005\000\060 -\201\233\061\013\060\011\006\003\125\004\006\023\002\110\125\061 -\021\060\017\006\003\125\004\007\023\010\102\165\144\141\160\145 -\163\164\061\047\060\045\006\003\125\004\012\023\036\116\145\164 -\114\157\143\153\040\110\141\154\157\172\141\164\142\151\172\164 -\157\156\163\141\147\151\040\113\146\164\056\061\032\060\030\006 -\003\125\004\013\023\021\124\141\156\165\163\151\164\166\141\156 -\171\153\151\141\144\157\153\061\064\060\062\006\003\125\004\003 -\023\053\116\145\164\114\157\143\153\040\105\170\160\162\145\163 -\163\172\040\050\103\154\141\163\163\040\103\051\040\124\141\156 -\165\163\151\164\166\141\156\171\153\151\141\144\157\060\036\027 -\015\071\071\060\062\062\065\061\064\060\070\061\061\132\027\015 -\061\071\060\062\062\060\061\064\060\070\061\061\132\060\201\233 -\061\013\060\011\006\003\125\004\006\023\002\110\125\061\021\060 -\017\006\003\125\004\007\023\010\102\165\144\141\160\145\163\164 -\061\047\060\045\006\003\125\004\012\023\036\116\145\164\114\157 -\143\153\040\110\141\154\157\172\141\164\142\151\172\164\157\156 -\163\141\147\151\040\113\146\164\056\061\032\060\030\006\003\125 -\004\013\023\021\124\141\156\165\163\151\164\166\141\156\171\153 -\151\141\144\157\153\061\064\060\062\006\003\125\004\003\023\053 -\116\145\164\114\157\143\153\040\105\170\160\162\145\163\163\172 -\040\050\103\154\141\163\163\040\103\051\040\124\141\156\165\163 -\151\164\166\141\156\171\153\151\141\144\157\060\201\237\060\015 -\006\011\052\206\110\206\367\015\001\001\001\005\000\003\201\215 -\000\060\201\211\002\201\201\000\353\354\260\154\141\212\043\045 -\257\140\040\343\331\237\374\223\013\333\135\215\260\241\263\100 -\072\202\316\375\165\340\170\062\003\206\132\206\225\221\355\123 -\372\235\100\374\346\350\335\331\133\172\003\275\135\363\073\014 -\303\121\171\233\255\125\240\351\320\003\020\257\012\272\024\102 -\331\122\046\021\042\307\322\040\314\202\244\232\251\376\270\201 -\166\235\152\267\322\066\165\076\261\206\011\366\156\155\176\116 -\267\172\354\256\161\204\366\004\063\010\045\062\353\164\254\026 -\104\306\344\100\223\035\177\255\002\003\001\000\001\243\202\002 -\237\060\202\002\233\060\022\006\003\125\035\023\001\001\377\004 -\010\060\006\001\001\377\002\001\004\060\016\006\003\125\035\017 -\001\001\377\004\004\003\002\000\006\060\021\006\011\140\206\110 -\001\206\370\102\001\001\004\004\003\002\000\007\060\202\002\140 -\006\011\140\206\110\001\206\370\102\001\015\004\202\002\121\026 -\202\002\115\106\111\107\131\105\114\105\115\041\040\105\172\145 -\156\040\164\141\156\165\163\151\164\166\141\156\171\040\141\040 -\116\145\164\114\157\143\153\040\113\146\164\056\040\101\154\164 -\141\154\141\156\157\163\040\123\172\157\154\147\141\154\164\141 -\164\141\163\151\040\106\145\154\164\145\164\145\154\145\151\142 -\145\156\040\154\145\151\162\164\040\145\154\152\141\162\141\163 -\157\153\040\141\154\141\160\152\141\156\040\153\145\163\172\165 -\154\164\056\040\101\040\150\151\164\145\154\145\163\151\164\145 -\163\040\146\157\154\171\141\155\141\164\141\164\040\141\040\116 -\145\164\114\157\143\153\040\113\146\164\056\040\164\145\162\155 -\145\153\146\145\154\145\154\157\163\163\145\147\055\142\151\172 -\164\157\163\151\164\141\163\141\040\166\145\144\151\056\040\101 -\040\144\151\147\151\164\141\154\151\163\040\141\154\141\151\162 -\141\163\040\145\154\146\157\147\141\144\141\163\141\156\141\153 -\040\146\145\154\164\145\164\145\154\145\040\141\172\040\145\154 -\157\151\162\164\040\145\154\154\145\156\157\162\172\145\163\151 -\040\145\154\152\141\162\141\163\040\155\145\147\164\145\164\145 -\154\145\056\040\101\172\040\145\154\152\141\162\141\163\040\154 -\145\151\162\141\163\141\040\155\145\147\164\141\154\141\154\150 -\141\164\157\040\141\040\116\145\164\114\157\143\153\040\113\146 -\164\056\040\111\156\164\145\162\156\145\164\040\150\157\156\154 -\141\160\152\141\156\040\141\040\150\164\164\160\163\072\057\057 -\167\167\167\056\156\145\164\154\157\143\153\056\156\145\164\057 -\144\157\143\163\040\143\151\155\145\156\040\166\141\147\171\040 -\153\145\162\150\145\164\157\040\141\172\040\145\154\154\145\156 -\157\162\172\145\163\100\156\145\164\154\157\143\153\056\156\145 -\164\040\145\055\155\141\151\154\040\143\151\155\145\156\056\040 -\111\115\120\117\122\124\101\116\124\041\040\124\150\145\040\151 -\163\163\165\141\156\143\145\040\141\156\144\040\164\150\145\040 -\165\163\145\040\157\146\040\164\150\151\163\040\143\145\162\164 -\151\146\151\143\141\164\145\040\151\163\040\163\165\142\152\145 -\143\164\040\164\157\040\164\150\145\040\116\145\164\114\157\143 -\153\040\103\120\123\040\141\166\141\151\154\141\142\154\145\040 -\141\164\040\150\164\164\160\163\072\057\057\167\167\167\056\156 -\145\164\154\157\143\153\056\156\145\164\057\144\157\143\163\040 -\157\162\040\142\171\040\145\055\155\141\151\154\040\141\164\040 -\143\160\163\100\156\145\164\154\157\143\153\056\156\145\164\056 -\060\015\006\011\052\206\110\206\367\015\001\001\004\005\000\003 -\201\201\000\020\255\177\327\014\062\200\012\330\206\361\171\230 -\265\255\324\315\263\066\304\226\110\301\134\315\232\331\005\056 -\237\276\120\353\364\046\024\020\055\324\146\027\370\236\301\047 -\375\361\355\344\173\113\240\154\265\253\232\127\160\246\355\240 -\244\355\056\365\375\374\275\376\115\067\010\014\274\343\226\203 -\042\365\111\033\177\113\053\264\124\301\200\174\231\116\035\320 -\214\356\320\254\345\222\372\165\126\376\144\240\023\217\270\270 -\026\235\141\005\147\200\310\320\330\245\007\002\064\230\004\215 -\063\004\324 -END - -# Trust for Certificate "NetLock Express (Class C) Root" -# Issuer: CN=NetLock Expressz (Class C) Tanusitvanykiado,OU=Tanusitvanykiadok,O=NetLock Halozatbiztonsagi Kft.,L=Budapest,C=HU -# Serial Number: 104 (0x68) -# Subject: CN=NetLock Expressz (Class C) Tanusitvanykiado,OU=Tanusitvanykiadok,O=NetLock Halozatbiztonsagi Kft.,L=Budapest,C=HU -# Not Valid Before: Thu Feb 25 14:08:11 1999 -# Not Valid After : Wed Feb 20 14:08:11 2019 -# Fingerprint (MD5): 4F:EB:F1:F0:70:C2:80:63:5D:58:9F:DA:12:3C:A9:C4 -# Fingerprint (SHA1): E3:92:51:2F:0A:CF:F5:05:DF:F6:DE:06:7F:75:37:E1:65:EA:57:4B +# Trust for Certificate "Camerfirma Global Chambersign Root" +# Issuer: CN=Global Chambersign Root,OU=http://www.chambersign.org,O=AC Camerfirma SA CIF A82743287,C=EU +# Serial Number: 0 (0x0) +# Subject: CN=Global Chambersign Root,OU=http://www.chambersign.org,O=AC Camerfirma SA CIF A82743287,C=EU +# Not Valid Before: Tue Sep 30 16:14:18 2003 +# Not Valid After : Wed Sep 30 16:14:18 2037 +# Fingerprint (MD5): C5:E6:7B:BF:06:D0:4F:43:ED:C4:7A:65:8A:FB:6B:19 +# Fingerprint (SHA1): 33:9B:6B:14:50:24:9B:55:7A:01:87:72:84:D9:E0:2F:C3:D2:D8:E9 CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST CKA_TOKEN CK_BBOOL CK_TRUE CKA_PRIVATE CK_BBOOL CK_FALSE CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "NetLock Express (Class C) Root" +CKA_LABEL UTF8 "Camerfirma Global Chambersign Root" CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\343\222\121\057\012\317\365\005\337\366\336\006\177\165\067\341 -\145\352\127\113 +\063\233\153\024\120\044\233\125\172\001\207\162\204\331\340\057 +\303\322\330\351 END CKA_CERT_MD5_HASH MULTILINE_OCTAL -\117\353\361\360\160\302\200\143\135\130\237\332\022\074\251\304 +\305\346\173\277\006\320\117\103\355\304\172\145\212\373\153\031 END CKA_ISSUER MULTILINE_OCTAL -\060\201\233\061\013\060\011\006\003\125\004\006\023\002\110\125 -\061\021\060\017\006\003\125\004\007\023\010\102\165\144\141\160 -\145\163\164\061\047\060\045\006\003\125\004\012\023\036\116\145 -\164\114\157\143\153\040\110\141\154\157\172\141\164\142\151\172 -\164\157\156\163\141\147\151\040\113\146\164\056\061\032\060\030 -\006\003\125\004\013\023\021\124\141\156\165\163\151\164\166\141 -\156\171\153\151\141\144\157\153\061\064\060\062\006\003\125\004 -\003\023\053\116\145\164\114\157\143\153\040\105\170\160\162\145 -\163\163\172\040\050\103\154\141\163\163\040\103\051\040\124\141 -\156\165\163\151\164\166\141\156\171\153\151\141\144\157 +\060\175\061\013\060\011\006\003\125\004\006\023\002\105\125\061 +\047\060\045\006\003\125\004\012\023\036\101\103\040\103\141\155 +\145\162\146\151\162\155\141\040\123\101\040\103\111\106\040\101 +\070\062\067\064\063\062\070\067\061\043\060\041\006\003\125\004 +\013\023\032\150\164\164\160\072\057\057\167\167\167\056\143\150 +\141\155\142\145\162\163\151\147\156\056\157\162\147\061\040\060 +\036\006\003\125\004\003\023\027\107\154\157\142\141\154\040\103 +\150\141\155\142\145\162\163\151\147\156\040\122\157\157\164 END CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\001\150 +\002\001\000 END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_MUST_VERIFY_TRUST +CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST +CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_TRUSTED_DELEGATOR CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE # @@ -14767,144 +13589,6 @@ CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_TRUSTED_DELEGATOR CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE -# -# Certificate "CA Disig" -# -# Issuer: CN=CA Disig,O=Disig a.s.,L=Bratislava,C=SK -# Serial Number: 1 (0x1) -# Subject: CN=CA Disig,O=Disig a.s.,L=Bratislava,C=SK -# Not Valid Before: Wed Mar 22 01:39:34 2006 -# Not Valid After : Tue Mar 22 01:39:34 2016 -# Fingerprint (MD5): 3F:45:96:39:E2:50:87:F7:BB:FE:98:0C:3C:20:98:E6 -# Fingerprint (SHA1): 2A:C8:D5:8B:57:CE:BF:2F:49:AF:F2:FC:76:8F:51:14:62:90:7A:41 -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "CA Disig" -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\112\061\013\060\011\006\003\125\004\006\023\002\123\113\061 -\023\060\021\006\003\125\004\007\023\012\102\162\141\164\151\163 -\154\141\166\141\061\023\060\021\006\003\125\004\012\023\012\104 -\151\163\151\147\040\141\056\163\056\061\021\060\017\006\003\125 -\004\003\023\010\103\101\040\104\151\163\151\147 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\112\061\013\060\011\006\003\125\004\006\023\002\123\113\061 -\023\060\021\006\003\125\004\007\023\012\102\162\141\164\151\163 -\154\141\166\141\061\023\060\021\006\003\125\004\012\023\012\104 -\151\163\151\147\040\141\056\163\056\061\021\060\017\006\003\125 -\004\003\023\010\103\101\040\104\151\163\151\147 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\001\001 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\004\017\060\202\002\367\240\003\002\001\002\002\001\001 -\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060 -\112\061\013\060\011\006\003\125\004\006\023\002\123\113\061\023 -\060\021\006\003\125\004\007\023\012\102\162\141\164\151\163\154 -\141\166\141\061\023\060\021\006\003\125\004\012\023\012\104\151 -\163\151\147\040\141\056\163\056\061\021\060\017\006\003\125\004 -\003\023\010\103\101\040\104\151\163\151\147\060\036\027\015\060 -\066\060\063\062\062\060\061\063\071\063\064\132\027\015\061\066 -\060\063\062\062\060\061\063\071\063\064\132\060\112\061\013\060 -\011\006\003\125\004\006\023\002\123\113\061\023\060\021\006\003 -\125\004\007\023\012\102\162\141\164\151\163\154\141\166\141\061 -\023\060\021\006\003\125\004\012\023\012\104\151\163\151\147\040 -\141\056\163\056\061\021\060\017\006\003\125\004\003\023\010\103 -\101\040\104\151\163\151\147\060\202\001\042\060\015\006\011\052 -\206\110\206\367\015\001\001\001\005\000\003\202\001\017\000\060 -\202\001\012\002\202\001\001\000\222\366\061\301\175\210\375\231 -\001\251\330\173\362\161\165\361\061\306\363\165\146\372\121\050 -\106\204\227\170\064\274\154\374\274\105\131\210\046\030\112\304 -\067\037\241\112\104\275\343\161\004\365\104\027\342\077\374\110 -\130\157\134\236\172\011\272\121\067\042\043\146\103\041\260\074 -\144\242\370\152\025\016\077\353\121\341\124\251\335\006\231\327 -\232\074\124\213\071\003\077\017\305\316\306\353\203\162\002\250 -\037\161\363\055\370\165\010\333\142\114\350\372\316\371\347\152 -\037\266\153\065\202\272\342\217\026\222\175\005\014\154\106\003 -\135\300\355\151\277\072\301\212\240\350\216\331\271\105\050\207 -\010\354\264\312\025\276\202\335\265\104\213\055\255\206\014\150 -\142\155\205\126\362\254\024\143\072\306\321\231\254\064\170\126 -\113\317\266\255\077\214\212\327\004\345\343\170\114\365\206\252 -\365\217\372\075\154\161\243\055\312\147\353\150\173\156\063\251 -\014\202\050\250\114\152\041\100\025\040\014\046\133\203\302\251 -\026\025\300\044\202\135\053\026\255\312\143\366\164\000\260\337 -\103\304\020\140\126\147\143\105\002\003\001\000\001\243\201\377 -\060\201\374\060\017\006\003\125\035\023\001\001\377\004\005\060 -\003\001\001\377\060\035\006\003\125\035\016\004\026\004\024\215 -\262\111\150\235\162\010\045\271\300\047\365\120\223\126\110\106 -\161\371\217\060\016\006\003\125\035\017\001\001\377\004\004\003 -\002\001\006\060\066\006\003\125\035\021\004\057\060\055\201\023 -\143\141\157\160\145\162\141\164\157\162\100\144\151\163\151\147 -\056\163\153\206\026\150\164\164\160\072\057\057\167\167\167\056 -\144\151\163\151\147\056\163\153\057\143\141\060\146\006\003\125 -\035\037\004\137\060\135\060\055\240\053\240\051\206\047\150\164 -\164\160\072\057\057\167\167\167\056\144\151\163\151\147\056\163 -\153\057\143\141\057\143\162\154\057\143\141\137\144\151\163\151 -\147\056\143\162\154\060\054\240\052\240\050\206\046\150\164\164 -\160\072\057\057\143\141\056\144\151\163\151\147\056\163\153\057 -\143\141\057\143\162\154\057\143\141\137\144\151\163\151\147\056 -\143\162\154\060\032\006\003\125\035\040\004\023\060\021\060\017 -\006\015\053\201\036\221\223\346\012\000\000\000\001\001\001\060 -\015\006\011\052\206\110\206\367\015\001\001\005\005\000\003\202 -\001\001\000\135\064\164\141\114\257\073\330\377\237\155\130\066 -\034\075\013\201\015\022\053\106\020\200\375\347\074\047\320\172 -\310\251\266\176\164\060\063\243\072\212\173\164\300\171\171\102 -\223\155\377\261\051\024\202\253\041\214\057\027\371\077\046\057 -\365\131\306\357\200\006\267\232\111\051\354\316\176\161\074\152 -\020\101\300\366\323\232\262\174\132\221\234\300\254\133\310\115 -\136\367\341\123\377\103\167\374\236\113\147\154\327\363\203\321 -\240\340\177\045\337\270\230\013\232\062\070\154\060\240\363\377 -\010\025\063\367\120\112\173\076\243\076\040\251\334\057\126\200 -\012\355\101\120\260\311\364\354\262\343\046\104\000\016\157\236 -\006\274\042\226\123\160\145\304\120\012\106\153\244\057\047\201 -\022\047\023\137\020\241\166\316\212\173\067\352\303\071\141\003 -\225\230\072\347\154\210\045\010\374\171\150\015\207\175\142\370 -\264\137\373\305\330\114\275\130\274\077\103\133\324\036\001\115 -\074\143\276\043\357\214\315\132\120\270\150\124\371\012\231\063 -\021\000\341\236\302\106\167\202\365\131\006\214\041\114\207\011 -\315\345\250 -END - -# Trust for Certificate "CA Disig" -# Issuer: CN=CA Disig,O=Disig a.s.,L=Bratislava,C=SK -# Serial Number: 1 (0x1) -# Subject: CN=CA Disig,O=Disig a.s.,L=Bratislava,C=SK -# Not Valid Before: Wed Mar 22 01:39:34 2006 -# Not Valid After : Tue Mar 22 01:39:34 2016 -# Fingerprint (MD5): 3F:45:96:39:E2:50:87:F7:BB:FE:98:0C:3C:20:98:E6 -# Fingerprint (SHA1): 2A:C8:D5:8B:57:CE:BF:2F:49:AF:F2:FC:76:8F:51:14:62:90:7A:41 -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "CA Disig" -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\052\310\325\213\127\316\277\057\111\257\362\374\166\217\121\024 -\142\220\172\101 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\077\105\226\071\342\120\207\367\273\376\230\014\074\040\230\346 -END -CKA_ISSUER MULTILINE_OCTAL -\060\112\061\013\060\011\006\003\125\004\006\023\002\123\113\061 -\023\060\021\006\003\125\004\007\023\012\102\162\141\164\151\163 -\154\141\166\141\061\023\060\021\006\003\125\004\012\023\012\104 -\151\163\151\147\040\141\056\163\056\061\021\060\017\006\003\125 -\004\003\023\010\103\101\040\104\151\163\151\147 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\001\001 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - # # Certificate "Juur-SK" # @@ -15595,122 +14279,6 @@ CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE -# -# Certificate "Verisign Class 3 Public Primary Certification Authority" -# -# Issuer: OU=Class 3 Public Primary Certification Authority,O="VeriSign, Inc.",C=US -# Serial Number:3c:91:31:cb:1f:f6:d0:1b:0e:9a:b8:d0:44:bf:12:be -# Subject: OU=Class 3 Public Primary Certification Authority,O="VeriSign, Inc.",C=US -# Not Valid Before: Mon Jan 29 00:00:00 1996 -# Not Valid After : Wed Aug 02 23:59:59 2028 -# Fingerprint (MD5): EF:5A:F1:33:EF:F1:CD:BB:51:02:EE:12:14:4B:96:C4 -# Fingerprint (SHA1): A1:DB:63:93:91:6F:17:E4:18:55:09:40:04:15:C7:02:40:B0:AE:6B -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "Verisign Class 3 Public Primary Certification Authority" -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\137\061\013\060\011\006\003\125\004\006\023\002\125\123\061 -\027\060\025\006\003\125\004\012\023\016\126\145\162\151\123\151 -\147\156\054\040\111\156\143\056\061\067\060\065\006\003\125\004 -\013\023\056\103\154\141\163\163\040\063\040\120\165\142\154\151 -\143\040\120\162\151\155\141\162\171\040\103\145\162\164\151\146 -\151\143\141\164\151\157\156\040\101\165\164\150\157\162\151\164 -\171 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\137\061\013\060\011\006\003\125\004\006\023\002\125\123\061 -\027\060\025\006\003\125\004\012\023\016\126\145\162\151\123\151 -\147\156\054\040\111\156\143\056\061\067\060\065\006\003\125\004 -\013\023\056\103\154\141\163\163\040\063\040\120\165\142\154\151 -\143\040\120\162\151\155\141\162\171\040\103\145\162\164\151\146 -\151\143\141\164\151\157\156\040\101\165\164\150\157\162\151\164 -\171 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\020\074\221\061\313\037\366\320\033\016\232\270\320\104\277 -\022\276 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\002\074\060\202\001\245\002\020\074\221\061\313\037\366 -\320\033\016\232\270\320\104\277\022\276\060\015\006\011\052\206 -\110\206\367\015\001\001\005\005\000\060\137\061\013\060\011\006 -\003\125\004\006\023\002\125\123\061\027\060\025\006\003\125\004 -\012\023\016\126\145\162\151\123\151\147\156\054\040\111\156\143 -\056\061\067\060\065\006\003\125\004\013\023\056\103\154\141\163 -\163\040\063\040\120\165\142\154\151\143\040\120\162\151\155\141 -\162\171\040\103\145\162\164\151\146\151\143\141\164\151\157\156 -\040\101\165\164\150\157\162\151\164\171\060\036\027\015\071\066 -\060\061\062\071\060\060\060\060\060\060\132\027\015\062\070\060 -\070\060\062\062\063\065\071\065\071\132\060\137\061\013\060\011 -\006\003\125\004\006\023\002\125\123\061\027\060\025\006\003\125 -\004\012\023\016\126\145\162\151\123\151\147\156\054\040\111\156 -\143\056\061\067\060\065\006\003\125\004\013\023\056\103\154\141 -\163\163\040\063\040\120\165\142\154\151\143\040\120\162\151\155 -\141\162\171\040\103\145\162\164\151\146\151\143\141\164\151\157 -\156\040\101\165\164\150\157\162\151\164\171\060\201\237\060\015 -\006\011\052\206\110\206\367\015\001\001\001\005\000\003\201\215 -\000\060\201\211\002\201\201\000\311\134\131\236\362\033\212\001 -\024\264\020\337\004\100\333\343\127\257\152\105\100\217\204\014 -\013\321\063\331\331\021\317\356\002\130\037\045\367\052\250\104 -\005\252\354\003\037\170\177\236\223\271\232\000\252\043\175\326 -\254\205\242\143\105\307\162\047\314\364\114\306\165\161\322\071 -\357\117\102\360\165\337\012\220\306\216\040\157\230\017\370\254 -\043\137\160\051\066\244\311\206\347\261\232\040\313\123\245\205 -\347\075\276\175\232\376\044\105\063\334\166\025\355\017\242\161 -\144\114\145\056\201\150\105\247\002\003\001\000\001\060\015\006 -\011\052\206\110\206\367\015\001\001\005\005\000\003\201\201\000 -\020\162\122\251\005\024\031\062\010\101\360\305\153\012\314\176 -\017\041\031\315\344\147\334\137\251\033\346\312\350\163\235\042 -\330\230\156\163\003\141\221\305\174\260\105\100\156\104\235\215 -\260\261\226\164\141\055\015\251\105\322\244\222\052\326\232\165 -\227\156\077\123\375\105\231\140\035\250\053\114\371\136\247\011 -\330\165\060\327\322\145\140\075\147\326\110\125\165\151\077\221 -\365\110\013\107\151\042\151\202\226\276\311\310\070\206\112\172 -\054\163\031\110\151\116\153\174\145\277\017\374\160\316\210\220 -END - -# Trust for Certificate "Verisign Class 3 Public Primary Certification Authority" -# Issuer: OU=Class 3 Public Primary Certification Authority,O="VeriSign, Inc.",C=US -# Serial Number:3c:91:31:cb:1f:f6:d0:1b:0e:9a:b8:d0:44:bf:12:be -# Subject: OU=Class 3 Public Primary Certification Authority,O="VeriSign, Inc.",C=US -# Not Valid Before: Mon Jan 29 00:00:00 1996 -# Not Valid After : Wed Aug 02 23:59:59 2028 -# Fingerprint (MD5): EF:5A:F1:33:EF:F1:CD:BB:51:02:EE:12:14:4B:96:C4 -# Fingerprint (SHA1): A1:DB:63:93:91:6F:17:E4:18:55:09:40:04:15:C7:02:40:B0:AE:6B -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "Verisign Class 3 Public Primary Certification Authority" -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\241\333\143\223\221\157\027\344\030\125\011\100\004\025\307\002 -\100\260\256\153 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\357\132\361\063\357\361\315\273\121\002\356\022\024\113\226\304 -END -CKA_ISSUER MULTILINE_OCTAL -\060\137\061\013\060\011\006\003\125\004\006\023\002\125\123\061 -\027\060\025\006\003\125\004\012\023\016\126\145\162\151\123\151 -\147\156\054\040\111\156\143\056\061\067\060\065\006\003\125\004 -\013\023\056\103\154\141\163\163\040\063\040\120\165\142\154\151 -\143\040\120\162\151\155\141\162\171\040\103\145\162\164\151\146 -\151\143\141\164\151\157\156\040\101\165\164\150\157\162\151\164 -\171 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\020\074\221\061\313\037\366\320\033\016\232\270\320\104\277 -\022\276 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - # # Certificate "Microsec e-Szigno Root CA 2009" # @@ -22013,7 +20581,7 @@ CKA_SERIAL_NUMBER MULTILINE_OCTAL \002\010\127\012\021\227\102\304\343\314 END CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST +CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_TRUSTED_DELEGATOR CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE @@ -30351,3 +28919,316 @@ CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_TRUSTED_DELEGATOR CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE + +# +# Certificate "SZAFIR ROOT CA2" +# +# Issuer: CN=SZAFIR ROOT CA2,O=Krajowa Izba Rozliczeniowa S.A.,C=PL +# Serial Number:3e:8a:5d:07:ec:55:d2:32:d5:b7:e3:b6:5f:01:eb:2d:dc:e4:d6:e4 +# Subject: CN=SZAFIR ROOT CA2,O=Krajowa Izba Rozliczeniowa S.A.,C=PL +# Not Valid Before: Mon Oct 19 07:43:30 2015 +# Not Valid After : Fri Oct 19 07:43:30 2035 +# Fingerprint (SHA-256): A1:33:9D:33:28:1A:0B:56:E5:57:D3:D3:2B:1C:E7:F9:36:7E:B0:94:BD:5F:A7:2A:7E:50:04:C8:DE:D7:CA:FE +# Fingerprint (SHA1): E2:52:FA:95:3F:ED:DB:24:60:BD:6E:28:F3:9C:CC:CF:5E:B3:3F:DE +CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "SZAFIR ROOT CA2" +CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 +CKA_SUBJECT MULTILINE_OCTAL +\060\121\061\013\060\011\006\003\125\004\006\023\002\120\114\061 +\050\060\046\006\003\125\004\012\014\037\113\162\141\152\157\167 +\141\040\111\172\142\141\040\122\157\172\154\151\143\172\145\156 +\151\157\167\141\040\123\056\101\056\061\030\060\026\006\003\125 +\004\003\014\017\123\132\101\106\111\122\040\122\117\117\124\040 +\103\101\062 +END +CKA_ID UTF8 "0" +CKA_ISSUER MULTILINE_OCTAL +\060\121\061\013\060\011\006\003\125\004\006\023\002\120\114\061 +\050\060\046\006\003\125\004\012\014\037\113\162\141\152\157\167 +\141\040\111\172\142\141\040\122\157\172\154\151\143\172\145\156 +\151\157\167\141\040\123\056\101\056\061\030\060\026\006\003\125 +\004\003\014\017\123\132\101\106\111\122\040\122\117\117\124\040 +\103\101\062 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\024\076\212\135\007\354\125\322\062\325\267\343\266\137\001 +\353\055\334\344\326\344 +END +CKA_VALUE MULTILINE_OCTAL +\060\202\003\162\060\202\002\132\240\003\002\001\002\002\024\076 +\212\135\007\354\125\322\062\325\267\343\266\137\001\353\055\334 +\344\326\344\060\015\006\011\052\206\110\206\367\015\001\001\013 +\005\000\060\121\061\013\060\011\006\003\125\004\006\023\002\120 +\114\061\050\060\046\006\003\125\004\012\014\037\113\162\141\152 +\157\167\141\040\111\172\142\141\040\122\157\172\154\151\143\172 +\145\156\151\157\167\141\040\123\056\101\056\061\030\060\026\006 +\003\125\004\003\014\017\123\132\101\106\111\122\040\122\117\117 +\124\040\103\101\062\060\036\027\015\061\065\061\060\061\071\060 +\067\064\063\063\060\132\027\015\063\065\061\060\061\071\060\067 +\064\063\063\060\132\060\121\061\013\060\011\006\003\125\004\006 +\023\002\120\114\061\050\060\046\006\003\125\004\012\014\037\113 +\162\141\152\157\167\141\040\111\172\142\141\040\122\157\172\154 +\151\143\172\145\156\151\157\167\141\040\123\056\101\056\061\030 +\060\026\006\003\125\004\003\014\017\123\132\101\106\111\122\040 +\122\117\117\124\040\103\101\062\060\202\001\042\060\015\006\011 +\052\206\110\206\367\015\001\001\001\005\000\003\202\001\017\000 +\060\202\001\012\002\202\001\001\000\267\274\076\120\250\113\315 +\100\265\316\141\347\226\312\264\241\332\014\042\260\372\265\173 +\166\000\167\214\013\317\175\250\206\314\046\121\344\040\075\205 +\014\326\130\343\347\364\052\030\235\332\321\256\046\356\353\123 +\334\364\220\326\023\112\014\220\074\303\364\332\322\216\015\222 +\072\334\261\261\377\070\336\303\272\055\137\200\271\002\275\112 +\235\033\017\264\303\302\301\147\003\335\334\033\234\075\263\260 +\336\000\036\250\064\107\273\232\353\376\013\024\275\066\204\332 +\015\040\277\372\133\313\251\026\040\255\071\140\356\057\165\266 +\347\227\234\371\076\375\176\115\157\115\057\357\210\015\152\372 +\335\361\075\156\040\245\240\022\264\115\160\271\316\327\162\073 +\211\223\247\200\204\034\047\111\162\111\265\377\073\225\236\301 +\314\310\001\354\350\016\212\012\226\347\263\246\207\345\326\371 +\005\053\015\227\100\160\074\272\254\165\132\234\325\115\235\002 +\012\322\113\233\146\113\106\007\027\145\255\237\154\210\000\334 +\042\211\340\341\144\324\147\274\061\171\141\074\273\312\101\315 +\134\152\000\310\074\070\216\130\257\002\003\001\000\001\243\102 +\060\100\060\017\006\003\125\035\023\001\001\377\004\005\060\003 +\001\001\377\060\016\006\003\125\035\017\001\001\377\004\004\003 +\002\001\006\060\035\006\003\125\035\016\004\026\004\024\056\026 +\251\112\030\265\313\314\365\157\120\363\043\137\370\135\347\254 +\360\310\060\015\006\011\052\206\110\206\367\015\001\001\013\005 +\000\003\202\001\001\000\265\163\370\003\334\131\133\035\166\351 +\243\052\173\220\050\262\115\300\063\117\252\232\261\324\270\344 +\047\377\251\226\231\316\106\340\155\174\114\242\070\244\006\160 +\360\364\101\021\354\077\107\215\077\162\207\371\073\375\244\157 +\053\123\000\340\377\071\271\152\007\016\353\035\034\366\242\162 +\220\313\202\075\021\202\213\322\273\237\052\257\041\346\143\206 +\235\171\031\357\367\273\014\065\220\303\212\355\117\017\365\314 +\022\331\244\076\273\240\374\040\225\137\117\046\057\021\043\203 +\116\165\007\017\277\233\321\264\035\351\020\004\376\312\140\217 +\242\114\270\255\317\341\220\017\315\256\012\307\135\173\267\120 +\322\324\141\372\325\025\333\327\237\207\121\124\353\245\343\353 +\311\205\240\045\040\067\373\216\316\014\064\204\341\074\201\262 +\167\116\103\245\210\137\206\147\241\075\346\264\134\141\266\076 +\333\376\267\050\305\242\007\256\265\312\312\215\052\022\357\227 +\355\302\060\244\311\052\172\373\363\115\043\033\231\063\064\240 +\056\365\251\013\077\324\135\341\317\204\237\342\031\302\137\212 +\326\040\036\343\163\267 +END + +# Trust for "SZAFIR ROOT CA2" +# Issuer: CN=SZAFIR ROOT CA2,O=Krajowa Izba Rozliczeniowa S.A.,C=PL +# Serial Number:3e:8a:5d:07:ec:55:d2:32:d5:b7:e3:b6:5f:01:eb:2d:dc:e4:d6:e4 +# Subject: CN=SZAFIR ROOT CA2,O=Krajowa Izba Rozliczeniowa S.A.,C=PL +# Not Valid Before: Mon Oct 19 07:43:30 2015 +# Not Valid After : Fri Oct 19 07:43:30 2035 +# Fingerprint (SHA-256): A1:33:9D:33:28:1A:0B:56:E5:57:D3:D3:2B:1C:E7:F9:36:7E:B0:94:BD:5F:A7:2A:7E:50:04:C8:DE:D7:CA:FE +# Fingerprint (SHA1): E2:52:FA:95:3F:ED:DB:24:60:BD:6E:28:F3:9C:CC:CF:5E:B3:3F:DE +CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "SZAFIR ROOT CA2" +CKA_CERT_SHA1_HASH MULTILINE_OCTAL +\342\122\372\225\077\355\333\044\140\275\156\050\363\234\314\317 +\136\263\077\336 +END +CKA_CERT_MD5_HASH MULTILINE_OCTAL +\021\144\301\211\260\044\261\214\261\007\176\211\236\121\236\231 +END +CKA_ISSUER MULTILINE_OCTAL +\060\121\061\013\060\011\006\003\125\004\006\023\002\120\114\061 +\050\060\046\006\003\125\004\012\014\037\113\162\141\152\157\167 +\141\040\111\172\142\141\040\122\157\172\154\151\143\172\145\156 +\151\157\167\141\040\123\056\101\056\061\030\060\026\006\003\125 +\004\003\014\017\123\132\101\106\111\122\040\122\117\117\124\040 +\103\101\062 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\024\076\212\135\007\354\125\322\062\325\267\343\266\137\001 +\353\055\334\344\326\344 +END +CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR +CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR +CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST +CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE + +# +# Certificate "Certum Trusted Network CA 2" +# +# Issuer: CN=Certum Trusted Network CA 2,OU=Certum Certification Authority,O=Unizeto Technologies S.A.,C=PL +# Serial Number:21:d6:d0:4a:4f:25:0f:c9:32:37:fc:aa:5e:12:8d:e9 +# Subject: CN=Certum Trusted Network CA 2,OU=Certum Certification Authority,O=Unizeto Technologies S.A.,C=PL +# Not Valid Before: Thu Oct 06 08:39:56 2011 +# Not Valid After : Sat Oct 06 08:39:56 2046 +# Fingerprint (SHA-256): B6:76:F2:ED:DA:E8:77:5C:D3:6C:B0:F6:3C:D1:D4:60:39:61:F4:9E:62:65:BA:01:3A:2F:03:07:B6:D0:B8:04 +# Fingerprint (SHA1): D3:DD:48:3E:2B:BF:4C:05:E8:AF:10:F5:FA:76:26:CF:D3:DC:30:92 +CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "Certum Trusted Network CA 2" +CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 +CKA_SUBJECT MULTILINE_OCTAL +\060\201\200\061\013\060\011\006\003\125\004\006\023\002\120\114 +\061\042\060\040\006\003\125\004\012\023\031\125\156\151\172\145 +\164\157\040\124\145\143\150\156\157\154\157\147\151\145\163\040 +\123\056\101\056\061\047\060\045\006\003\125\004\013\023\036\103 +\145\162\164\165\155\040\103\145\162\164\151\146\151\143\141\164 +\151\157\156\040\101\165\164\150\157\162\151\164\171\061\044\060 +\042\006\003\125\004\003\023\033\103\145\162\164\165\155\040\124 +\162\165\163\164\145\144\040\116\145\164\167\157\162\153\040\103 +\101\040\062 +END +CKA_ID UTF8 "0" +CKA_ISSUER MULTILINE_OCTAL +\060\201\200\061\013\060\011\006\003\125\004\006\023\002\120\114 +\061\042\060\040\006\003\125\004\012\023\031\125\156\151\172\145 +\164\157\040\124\145\143\150\156\157\154\157\147\151\145\163\040 +\123\056\101\056\061\047\060\045\006\003\125\004\013\023\036\103 +\145\162\164\165\155\040\103\145\162\164\151\146\151\143\141\164 +\151\157\156\040\101\165\164\150\157\162\151\164\171\061\044\060 +\042\006\003\125\004\003\023\033\103\145\162\164\165\155\040\124 +\162\165\163\164\145\144\040\116\145\164\167\157\162\153\040\103 +\101\040\062 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\020\041\326\320\112\117\045\017\311\062\067\374\252\136\022 +\215\351 +END +CKA_VALUE MULTILINE_OCTAL +\060\202\005\322\060\202\003\272\240\003\002\001\002\002\020\041 +\326\320\112\117\045\017\311\062\067\374\252\136\022\215\351\060 +\015\006\011\052\206\110\206\367\015\001\001\015\005\000\060\201 +\200\061\013\060\011\006\003\125\004\006\023\002\120\114\061\042 +\060\040\006\003\125\004\012\023\031\125\156\151\172\145\164\157 +\040\124\145\143\150\156\157\154\157\147\151\145\163\040\123\056 +\101\056\061\047\060\045\006\003\125\004\013\023\036\103\145\162 +\164\165\155\040\103\145\162\164\151\146\151\143\141\164\151\157 +\156\040\101\165\164\150\157\162\151\164\171\061\044\060\042\006 +\003\125\004\003\023\033\103\145\162\164\165\155\040\124\162\165 +\163\164\145\144\040\116\145\164\167\157\162\153\040\103\101\040 +\062\060\042\030\017\062\060\061\061\061\060\060\066\060\070\063 +\071\065\066\132\030\017\062\060\064\066\061\060\060\066\060\070 +\063\071\065\066\132\060\201\200\061\013\060\011\006\003\125\004 +\006\023\002\120\114\061\042\060\040\006\003\125\004\012\023\031 +\125\156\151\172\145\164\157\040\124\145\143\150\156\157\154\157 +\147\151\145\163\040\123\056\101\056\061\047\060\045\006\003\125 +\004\013\023\036\103\145\162\164\165\155\040\103\145\162\164\151 +\146\151\143\141\164\151\157\156\040\101\165\164\150\157\162\151 +\164\171\061\044\060\042\006\003\125\004\003\023\033\103\145\162 +\164\165\155\040\124\162\165\163\164\145\144\040\116\145\164\167 +\157\162\153\040\103\101\040\062\060\202\002\042\060\015\006\011 +\052\206\110\206\367\015\001\001\001\005\000\003\202\002\017\000 +\060\202\002\012\002\202\002\001\000\275\371\170\370\346\325\200 +\014\144\235\206\033\226\144\147\077\042\072\036\165\001\175\357 +\373\134\147\214\311\314\134\153\251\221\346\271\102\345\040\113 +\233\332\233\173\271\231\135\331\233\200\113\327\204\100\053\047 +\323\350\272\060\273\076\011\032\247\111\225\357\053\100\044\302 +\227\307\247\356\233\045\357\250\012\000\227\205\132\252\235\334 +\051\311\342\065\007\353\160\115\112\326\301\263\126\270\241\101 +\070\233\321\373\061\177\217\340\137\341\261\077\017\216\026\111 +\140\327\006\215\030\371\252\046\020\253\052\323\320\321\147\215 +\033\106\276\107\060\325\056\162\321\305\143\332\347\143\171\104 +\176\113\143\044\211\206\056\064\077\051\114\122\213\052\247\300 +\342\221\050\211\271\300\133\371\035\331\347\047\255\377\232\002 +\227\301\306\120\222\233\002\054\275\251\271\064\131\012\277\204 +\112\377\337\376\263\237\353\331\236\340\230\043\354\246\153\167 +\026\052\333\314\255\073\034\244\207\334\106\163\136\031\142\150 +\105\127\344\220\202\102\273\102\326\360\141\340\301\243\075\146 +\243\135\364\030\356\210\311\215\027\105\051\231\062\165\002\061 +\356\051\046\310\153\002\346\265\142\105\177\067\025\132\043\150 +\211\324\076\336\116\047\260\360\100\014\274\115\027\313\115\242 +\263\036\320\006\132\335\366\223\317\127\165\231\365\372\206\032 +\147\170\263\277\226\376\064\334\275\347\122\126\345\263\345\165 +\173\327\101\221\005\334\135\151\343\225\015\103\271\374\203\226 +\071\225\173\154\200\132\117\023\162\306\327\175\051\172\104\272 +\122\244\052\325\101\106\011\040\376\042\240\266\133\060\215\274 +\211\014\325\327\160\370\207\122\375\332\357\254\121\056\007\263 +\116\376\320\011\332\160\357\230\372\126\346\155\333\265\127\113 +\334\345\054\045\025\310\236\056\170\116\370\332\234\236\206\054 +\312\127\363\032\345\310\222\213\032\202\226\172\303\274\120\022 +\151\330\016\132\106\213\072\353\046\372\043\311\266\260\201\276 +\102\000\244\370\326\376\060\056\307\322\106\366\345\216\165\375 +\362\314\271\320\207\133\314\006\020\140\273\203\065\267\136\147 +\336\107\354\231\110\361\244\241\025\376\255\214\142\216\071\125 +\117\071\026\271\261\143\235\377\267\002\003\001\000\001\243\102 +\060\100\060\017\006\003\125\035\023\001\001\377\004\005\060\003 +\001\001\377\060\035\006\003\125\035\016\004\026\004\024\266\241 +\124\071\002\303\240\077\216\212\274\372\324\370\034\246\321\072 +\016\375\060\016\006\003\125\035\017\001\001\377\004\004\003\002 +\001\006\060\015\006\011\052\206\110\206\367\015\001\001\015\005 +\000\003\202\002\001\000\161\245\016\316\344\351\277\077\070\325 +\211\132\304\002\141\373\114\305\024\027\055\213\117\123\153\020 +\027\374\145\204\307\020\111\220\336\333\307\046\223\210\046\157 +\160\326\002\136\071\240\367\217\253\226\265\245\023\134\201\024 +\155\016\201\202\021\033\212\116\306\117\245\335\142\036\104\337 +\011\131\364\133\167\013\067\351\213\040\306\370\012\116\056\130 +\034\353\063\320\317\206\140\311\332\373\200\057\236\114\140\204 +\170\075\041\144\326\373\101\037\030\017\347\311\165\161\275\275 +\134\336\064\207\076\101\260\016\366\271\326\077\011\023\226\024 +\057\336\232\035\132\271\126\316\065\072\260\137\160\115\136\343 +\051\361\043\050\162\131\266\253\302\214\146\046\034\167\054\046 +\166\065\213\050\247\151\240\371\073\365\043\335\205\020\164\311 +\220\003\126\221\347\257\272\107\324\022\227\021\042\343\242\111 +\224\154\347\267\224\113\272\055\244\332\063\213\114\246\104\377 +\132\074\306\035\144\330\265\061\344\246\074\172\250\127\013\333 +\355\141\032\313\361\316\163\167\143\244\207\157\114\121\070\326 +\344\137\307\237\266\201\052\344\205\110\171\130\136\073\370\333 +\002\202\147\301\071\333\303\164\113\075\066\036\371\051\223\210 +\150\133\250\104\031\041\360\247\350\201\015\054\350\223\066\264 +\067\262\312\260\033\046\172\232\045\037\232\232\200\236\113\052 +\077\373\243\232\376\163\062\161\302\236\306\162\341\212\150\047 +\361\344\017\264\304\114\245\141\223\370\227\020\007\052\060\045 +\251\271\310\161\270\357\150\314\055\176\365\340\176\017\202\250 +\157\266\272\154\203\103\167\315\212\222\027\241\236\133\170\026 +\075\105\342\063\162\335\341\146\312\231\323\311\305\046\375\015 +\150\004\106\256\266\331\233\214\276\031\276\261\306\362\031\343 +\134\002\312\054\330\157\112\007\331\311\065\332\100\165\362\304 +\247\031\157\236\102\020\230\165\346\225\213\140\274\355\305\022 +\327\212\316\325\230\134\126\226\003\305\356\167\006\065\377\317 +\344\356\077\023\141\356\333\332\055\205\360\315\256\235\262\030 +\011\105\303\222\241\162\027\374\107\266\240\013\054\361\304\336 +\103\150\010\152\137\073\360\166\143\373\314\006\054\246\306\342 +\016\265\271\276\044\217 +END + +# Trust for "Certum Trusted Network CA 2" +# Issuer: CN=Certum Trusted Network CA 2,OU=Certum Certification Authority,O=Unizeto Technologies S.A.,C=PL +# Serial Number:21:d6:d0:4a:4f:25:0f:c9:32:37:fc:aa:5e:12:8d:e9 +# Subject: CN=Certum Trusted Network CA 2,OU=Certum Certification Authority,O=Unizeto Technologies S.A.,C=PL +# Not Valid Before: Thu Oct 06 08:39:56 2011 +# Not Valid After : Sat Oct 06 08:39:56 2046 +# Fingerprint (SHA-256): B6:76:F2:ED:DA:E8:77:5C:D3:6C:B0:F6:3C:D1:D4:60:39:61:F4:9E:62:65:BA:01:3A:2F:03:07:B6:D0:B8:04 +# Fingerprint (SHA1): D3:DD:48:3E:2B:BF:4C:05:E8:AF:10:F5:FA:76:26:CF:D3:DC:30:92 +CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "Certum Trusted Network CA 2" +CKA_CERT_SHA1_HASH MULTILINE_OCTAL +\323\335\110\076\053\277\114\005\350\257\020\365\372\166\046\317 +\323\334\060\222 +END +CKA_CERT_MD5_HASH MULTILINE_OCTAL +\155\106\236\331\045\155\010\043\133\136\164\175\036\047\333\362 +END +CKA_ISSUER MULTILINE_OCTAL +\060\201\200\061\013\060\011\006\003\125\004\006\023\002\120\114 +\061\042\060\040\006\003\125\004\012\023\031\125\156\151\172\145 +\164\157\040\124\145\143\150\156\157\154\157\147\151\145\163\040 +\123\056\101\056\061\047\060\045\006\003\125\004\013\023\036\103 +\145\162\164\165\155\040\103\145\162\164\151\146\151\143\141\164 +\151\157\156\040\101\165\164\150\157\162\151\164\171\061\044\060 +\042\006\003\125\004\003\023\033\103\145\162\164\165\155\040\124 +\162\165\163\164\145\144\040\116\145\164\167\157\162\153\040\103 +\101\040\062 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\020\041\326\320\112\117\045\017\311\062\067\374\252\136\022 +\215\351 +END +CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR +CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR +CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST +CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE From 3eb02b6743e7f2579baa79e36640dc39da103158 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Wed, 22 Jun 2016 12:11:46 +0200 Subject: [PATCH 034/131] tools: output include guards in mk-ca-bundle.pl Commit eff96d3 ("src: add include guards to internal headers") adds include guards. Update tools/mk-ca-bundle.pl to output them when generating src/node_root_certs.h. PR-URL: https://github.com/nodejs/node/pull/7363 Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com> --- tools/mk-ca-bundle.pl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/mk-ca-bundle.pl b/tools/mk-ca-bundle.pl index 5ea3b937e31c03..30301fff9a305c 100755 --- a/tools/mk-ca-bundle.pl +++ b/tools/mk-ca-bundle.pl @@ -203,6 +203,7 @@ (%) my $start_of_cert = 0; open(TXT,"$txt") or die "Couldn't open $txt: $!\n"; +print CRT "#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS\n"; while (<TXT>) { if (/\*\*\*\*\* BEGIN LICENSE BLOCK \*\*\*\*\*/) { print CRT; @@ -310,6 +311,7 @@ (%) } } } +print CRT "#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS\n"; close(TXT) or die "Couldn't close $txt: $!\n"; close(CRT) or die "Couldn't close $crt.~: $!\n"; unless( $stdout ) { From ef41c8bd8e062301b26adaa66a7f94160e5e1f46 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Wed, 22 Jun 2016 12:16:49 +0200 Subject: [PATCH 035/131] crypto: update root certificates Update the list of root certificates in src/node_root_certs.h with tools/mk-ca-bundle.pl. Certificates added: - Certum Trusted Network CA 2 - SZAFIR ROOT CA2 Certificates removed: - CA Disig - NetLock Notary (Class A) Root - Staat der Nederlanden Root CA PR-URL: https://github.com/nodejs/node/pull/7363 Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com> --- src/node_root_certs.h | 133 +++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 81 deletions(-) diff --git a/src/node_root_certs.h b/src/node_root_certs.h index f00de758b8fbb1..972c1d33248500 100644 --- a/src/node_root_certs.h +++ b/src/node_root_certs.h @@ -611,28 +611,6 @@ "4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M\n" "-----END CERTIFICATE-----\n", -/* Staat der Nederlanden Root CA */ -"-----BEGIN CERTIFICATE-----\n" -"MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJOTDEeMBwG\n" -"A1UEChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFhdCBkZXIgTmVkZXJs\n" -"YW5kZW4gUm9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEyMTYwOTE1MzhaMFUxCzAJBgNV\n" -"BAYTAk5MMR4wHAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0\n" -"IGRlciBOZWRlcmxhbmRlbiBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC\n" -"AQEAmNK1URF6gaYUmHFtvsznExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeI\n" -"QGv33N0iYfXCxw719tV2U02PjLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX9\n" -"4p2i71MOhXeiD+EwR+4A5zN9RGcaC1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+U\n" -"tFE5A3+y3qcym7RHjm+0Sq7lr7HcsBthvJly3uSJt3omXdozSVtSnA71iq3DuD3oBmrC1SoL\n" -"bHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn622r+I/q85Ej0ZytqERAhSQIDAQABo4GRMIGO\n" -"MAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRVHSAAMDwwOgYIKwYBBQUHAgEWLmh0dHA6\n" -"Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMvcm9vdC1wb2xpY3kwDgYDVR0PAQH/BAQD\n" -"AgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA7Jbg0zTBLL9s+DANBgkqhkiG9w0BAQUFAAOCAQEA\n" -"BYSHVXQ2YcG70dTGFagTtJ+k/rvuFbQvBgwp8qiSpGEN/KtcCFtREytNwiphyPgJWPwtArI5\n" -"fZlmgb9uXJVFIGzmeafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbwMVcoEoJz6TMvplW0\n" -"C5GUR5z6u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3ynGQI0DvDKcWy\n" -"7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsRiJf2fL1L\n" -"uCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw==\n" -"-----END CERTIFICATE-----\n", - /* UTN USERFirst Hardware Root CA */ "-----BEGIN CERTIFICATE-----\n" "MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCBlzELMAkG\n" @@ -713,41 +691,6 @@ "oHflCStFREest2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A==\n" "-----END CERTIFICATE-----\n", -/* NetLock Notary (Class A) Root */ -"-----BEGIN CERTIFICATE-----\n" -"MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhVMRAwDgYD\n" -"VQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxv\n" -"emF0Yml6dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UE\n" -"AxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMB4XDTk5\n" -"MDIyNDIzMTQ0N1oXDTE5MDIxOTIzMTQ0N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdI\n" -"dW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6\n" -"dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0\n" -"TG9jayBLb3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG\n" -"9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSMD7tM9DceqQWC2ObhbHDqeLVu0ThEDaiDzl3S1tWB\n" -"xdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZz+qMkjvN9wfcZnSX9EUi3fRc4L9t875lM+QV\n" -"Or/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC/tmwqcm8WgD/qaiYdPv2LD4VOQ22BFWo\n" -"DpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LYOph7tqyF/7AlT3Rj5xMHpQqPBffAZG9+\n" -"pyeAlt7ULoZgx2srXnN7F+eRP2QM2EsiNCubMvJIH5+hCoR64sKtlz2O1cH5VqNQ6ca0+pii\n" -"7pXmKgOM3wIDAQABo4ICnzCCApswDgYDVR0PAQH/BAQDAgAGMBIGA1UdEwEB/wQIMAYBAf8C\n" -"AQQwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZRUxFTSEg\n" -"RXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRhdGFz\n" -"aSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQuIEEgaGl0\n" -"ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJp\n" -"enRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0\n" -"ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGph\n" -"cmFzIGxlaXJhc2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxh\n" -"cGphbiBhIGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRv\n" -"IGF6IGVsbGVub3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRo\n" -"ZSBpc3N1YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3Qg\n" -"dG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5l\n" -"dC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUA\n" -"A4IBAQBIJEb3ulZv+sgoA0BO5TE5ayZrU3/b39/zcT0mwBQOxmd7I6gMc90Bu8bKbjc5VdXH\n" -"jFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjPytoUMaFP0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jy\n" -"f1JsIPQLX2lS9O74silg6+NJMSEN1rUQQeJBCWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEV\n" -"ZD3QDtigdp+uxdAu6tYPVuxkf1qbFFgBJ34TUMdrKuZoPL9coAob4Q566eKAw+np9v1sEZ7Q\n" -"5SgnK1QyQhSCdeZK8CtmdWOMovsEPoMOmzbwGOQmIMOM8CgHrTwXZoi1/baI\n" -"-----END CERTIFICATE-----\n", - /* XRamp Global CA Root */ "-----BEGIN CERTIFICATE-----\n" "MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkG\n" @@ -1861,30 +1804,6 @@ "nR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ==\n" "-----END CERTIFICATE-----\n", -/* CA Disig */ -"-----BEGIN CERTIFICATE-----\n" -"MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzETMBEGA1UE\n" -"BxMKQnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcw\n" -"HhcNMDYwMzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQswCQYDVQQGEwJTSzETMBEGA1UE\n" -"BxMKQnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcw\n" -"ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6\n" -"UShGhJd4NLxs/LxFWYgmGErENx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhq\n" -"FQ4/61HhVKndBpnXmjxUizkDPw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaS\n" -"fQUMbEYDXcDtab86wYqg6I7ZuUUohwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhW\n" -"S8+2rT+MitcE5eN4TPWGqvWP+j1scaMtymfraHtuM6kMgiioTGohQBUgDCZbg8KpFhXAJIJd\n" -"KxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8wgfwwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E\n" -"FgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0PAQH/BAQDAgEGMDYGA1UdEQQvMC2BE2Nh\n" -"b3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cuZGlzaWcuc2svY2EwZgYDVR0fBF8wXTAt\n" -"oCugKYYnaHR0cDovL3d3dy5kaXNpZy5zay9jYS9jcmwvY2FfZGlzaWcuY3JsMCygKqAohiZo\n" -"dHRwOi8vY2EuZGlzaWcuc2svY2EvY3JsL2NhX2Rpc2lnLmNybDAaBgNVHSAEEzARMA8GDSuB\n" -"HpGT5goAAAABAQEwDQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59tWDYcPQuBDRIrRhCA\n" -"/ec8J9B6yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3mkkp7M5+cTxq\n" -"EEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/CBUz91BK\n" -"ez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeBEicTXxChds6Kezfq\n" -"wzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFNPGO+I++MzVpQuGhU+QqZMxEA\n" -"4Z7CRneC9VkGjCFMhwnN5ag=\n" -"-----END CERTIFICATE-----\n", - /* Juur-SK */ "-----BEGIN CERTIFICATE-----\n" "MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcNAQkBFglw\n" @@ -3883,4 +3802,56 @@ "7xMQ0Xk/0f7qO3/eVvSQsRUR2LIiFdAvwyYua/GRspBl9JrmkO5K\n" "-----END CERTIFICATE-----\n", +/* SZAFIR ROOT CA2 */ +"-----BEGIN CERTIFICATE-----\n" +"MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTEL\n" +"MAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4x\n" +"GDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQz\n" +"MzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93\n" +"YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IB\n" +"DwAwggEKAoIBAQC3vD5QqEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj\n" +"5/QqGJ3a0a4m7utT3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd\n" +"3BucPbOw3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6\n" +"3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0Bw\n" +"PLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMB\n" +"AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlK\n" +"GLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOCAQEAtXP4A9xZWx126aMqe5Aosk3A\n" +"M0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2\n" +"onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcP\n" +"v5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4M\n" +"NIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg\n" +"LvWpCz/UXeHPhJ/iGcJfitYgHuNztw==\n" +"-----END CERTIFICATE-----\n", + +/* Certum Trusted Network CA 2 */ +"-----BEGIN CERTIFICATE-----\n" +"MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCBgDELMAkG\n" +"A1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsT\n" +"HkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0\n" +"ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgzOTU2WhgPMjA0NjEwMDYwODM5NTZaMIGA\n" +"MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUG\n" +"A1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0g\n" +"VHJ1c3RlZCBOZXR3b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9\n" +"+Xj45tWADGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn\n" +"0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFBOJvR+zF/\n" +"j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lEfktjJImGLjQ/KUxS\n" +"iyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+ESv/f/rOf69me4Jgj7KZrdxYq\n" +"28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1mo130GO6IyY0XRSmZMnUCMe4pJshrAua1\n" +"YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02isx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz\n" +"5XV710GRBdxdaeOVDUO5/IOWOZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXX\n" +"cPiHUv3a76xRLgezTv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqC\n" +"lnrDvFASadgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n\n" +"3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0G\n" +"A1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcN\n" +"AQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQF/xlhMcQSZDe28cmk4gmb3DW\n" +"Al45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTfCVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ\n" +"2vuAL55MYIR4PSFk1vtBHxgP58l1cb29XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BN\n" +"XuMp8SMoclm2q8KMZiYcdywmdjWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3\n" +"lEu6LaTaM4tMpkT/WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVI\n" +"eVheO/jbAoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq\n" +"P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Kob7a6bIND\n" +"d82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnjXALKLNhvSgfZyTXa\n" +"QHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P5O4/E2Hu29othfDNrp2yGAlF\n" +"w5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbiDrW5viSP\n" +"-----END CERTIFICATE-----\n", #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS From 009858bd0a4a5820b7a8e74f87252b9b608ae892 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Thu, 23 Jun 2016 09:25:50 +0200 Subject: [PATCH 036/131] test: remove internet/test-tls-connnect-cnnic Shigeki Ohtsu points out that the test is unreliable because some of the www1.cnnnic.cn servers are misconfigured. Remove it. PR-URL: https://github.com/nodejs/node/pull/7363 Refs: https://github.com/nodejs/node/pull/7363#issuecomment-227801420 Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com> --- test/internet/test-tls-connnect-cnnic.js | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 test/internet/test-tls-connnect-cnnic.js diff --git a/test/internet/test-tls-connnect-cnnic.js b/test/internet/test-tls-connnect-cnnic.js deleted file mode 100644 index 038e23d2374584..00000000000000 --- a/test/internet/test-tls-connnect-cnnic.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; -// -// The server cert of www1.cnnic.cn is listed in the whitelist of -// { -// { 0x1B, 0xF4, 0x8A, 0x83, 0x3C, 0xE4, 0x05, 0x64, 0x8C, 0xC0, 0xBD, 0xD3, -// 0xB5, 0xB8, 0xC1, 0x8E, 0xB5, 0x13, 0x15, 0x34, 0x29, 0x3A, 0xB2, 0x63, -// 0x44, 0xB5, 0x00, 0x76, 0x48, 0x11, 0x41, 0xED }, -// }, -// in src/CNNICHashWhitelist.inc - -var common = require('../common'); -if (!common.hasCrypto) { - common.skip('missing crypto'); - return; -} - -var tls = require('tls'); -var socket = tls.connect(443, 'www1.cnnic.cn', function() { - socket.resume(); - socket.destroy(); -}); From 029af2c1f645c68743e6fc9ca4bfd612c2ebafab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Reis?= <reis@janeasystems.com> Date: Mon, 13 Jun 2016 11:24:53 +0100 Subject: [PATCH 037/131] doc: update build instructions for Windows The Visual C++ Build Tools are supported to build Node on Windows and already used in CI, so they should be included in the build instructions. Reviewed-By: Rod Vagg <rod@vagg.org> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> PR-URL: https://github.com/nodejs/node/pull/7285 --- BUILDING.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/BUILDING.md b/BUILDING.md index e9bf0bc01337f2..532c478bbb736b 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -99,8 +99,10 @@ $ node -e "console.log('Hello from Node.js ' + process.version)" Prerequisites: * [Python 2.6 or 2.7](https://www.python.org/downloads/) -* Visual Studio 2013 / 2015, all editions including the Community edition, or -* Visual Studio Express 2013 / 2015 for Desktop +* One of: + * [Visual C++ Build Tools](http://landinghub.visualstudio.com/visual-cpp-build-tools) + * [Visual Studio](https://www.visualstudio.com/) 2013 / 2015, all editions including the Community edition + * [Visual Studio](https://www.visualstudio.com/) Express 2013 / 2015 for Desktop * Basic Unix tools required for some tests, [Git for Windows](http://git-scm.com/download/win) includes Git Bash and tools which can be included in the global `PATH`. @@ -117,8 +119,8 @@ To run the tests: To test if Node.js was built correctly: -``` -$ node -e "console.log('Hello from Node.js ' + process.version)" +```text +> Release\node -e "console.log('Hello from Node.js', process.version)" ``` ### Android / Android-based devices (e.g., Firefox OS) From a04cd856678c732389b8c79651be00d35a936c02 Mon Sep 17 00:00:00 2001 From: akki <akki@users.noreply.github.com> Date: Thu, 23 Jun 2016 23:18:59 +0530 Subject: [PATCH 038/131] doc: fix repl defineCommand example Fixes: https://github.com/nodejs/node/issues/7357 PR-URL: https://github.com/nodejs/node/pull/7365 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: James M Snell <jasnell@gmail.com> --- doc/api/repl.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/repl.md b/doc/api/repl.md index 7524a373eea366..9d44a701fc379d 100644 --- a/doc/api/repl.md +++ b/doc/api/repl.md @@ -312,12 +312,12 @@ replServer.defineCommand('sayhello', { action: function(name) { this.lineParser.reset(); this.bufferedCommand = ''; - this.write(`Hello, ${name}!\n`); + console.log(`Hello, ${name}!`); this.displayPrompt(); } }); replServer.defineCommand('saybye', function() { - this.write('Goodbye!\n'); + console.log('Goodbye!'); this.close(); }); ``` From eed65973d52fd23f5638351ee9e05d9540b2db14 Mon Sep 17 00:00:00 2001 From: "Italo A. Casas" <me@italoacasas.com> Date: Mon, 20 Jun 2016 23:50:34 -0400 Subject: [PATCH 039/131] doc: fix link in the stream doc fixing typo fixing broken links PR-URL: https://github.com/nodejs/node/pull/7347 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com> --- doc/api/stream.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/api/stream.md b/doc/api/stream.md index e7047aa20928a2..4be7f3c3f07880 100644 --- a/doc/api/stream.md +++ b/doc/api/stream.md @@ -169,7 +169,7 @@ are not required to implement the stream interfaces directly and will generally have no reason to call `require('stream')`. Developers wishing to implement new types of streams should refer to the -section [API for Stream Implemeters][]. +section [API for Stream Implementers][]. ### Writable Streams @@ -863,7 +863,7 @@ pulled out of the source, so that the data can be passed on to some other party. [`'end'`][] event has been emitted or a runtime error will be thrown. Developers using `stream.unshift()` often should consider switching to -use of a [Transform][] stream instead. See the [API for Stream Implemeters][] +use of a [Transform][] stream instead. See the [API for Stream Implementers][] section for more information. ```js @@ -969,7 +969,7 @@ Examples of Transform streams include: * [crypto streams][crypto] -## API for Stream Implemeters +## API for Stream Implementers <!--type=misc--> @@ -1013,7 +1013,7 @@ on the type of stream being created, as detailed in the chart below: <p>Reading only</p> </td> <td> - <p>[Readable](#stream_class_stream_readable_1)</p> + <p>[Readable](#stream_class_stream_readable)</p> </td> <td> <p><code>[_read][stream-_read]</code></p> @@ -1024,7 +1024,7 @@ on the type of stream being created, as detailed in the chart below: <p>Writing only</p> </td> <td> - <p>[Writable](#stream_class_stream_writable_1)</p> + <p>[Writable](#stream_class_stream_writable)</p> </td> <td> <p><code>[_write][stream-_write]</code>, <code>[_writev][stream-_writev]</code></p> @@ -1035,7 +1035,7 @@ on the type of stream being created, as detailed in the chart below: <p>Reading and writing</p> </td> <td> - <p>[Duplex](#stream_class_stream_duplex_1)</p> + <p>[Duplex](#stream_class_stream_duplex)</p> </td> <td> <p><code>[_read][stream-_read]</code>, <code>[_write][stream-_write]</code>, <code>[_writev][stream-_writev]</code></p> @@ -1046,7 +1046,7 @@ on the type of stream being created, as detailed in the chart below: <p>Operate on written data, then read the result</p> </td> <td> - <p>[Transform](#stream_class_stream_transform_1)</p> + <p>[Transform](#stream_class_stream_transform)</p> </td> <td> <p><code>[_transform][stream-_transform]</code>, <code>[_flush][stream-_flush]</code></p> @@ -1407,7 +1407,7 @@ class SourceWrapper extends Readable { } ``` *Note*: The `readable.push()` method is intended be called only by Readable -Implemeters, and only from within the `readable._read()` method. +Implementers, and only from within the `readable._read()` method. #### Errors While Reading @@ -1893,7 +1893,7 @@ readable buffer so there is nothing for a user to consume. [`stream.wrap()`]: #stream_readable_wrap_stream [`tls.CryptoStream`]: tls.html#tls_class_cryptostream [API for Stream Consumers]: #stream_api_for_stream_consumers -[API for Stream Implemeters]: #stream_api_for_stream_Implemeters +[API for Stream Implementers]: #stream_api_for_stream_implementers [child process stdin]: child_process.html#child_process_child_stdin [child process stdout and stderr]: child_process.html#child_process_child_stdout [Compatibility]: #stream_compatibility_with_older_node_js_versions From 0bbf2ef6ea75e56123ea957ac286bd9106569efa Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Tue, 21 Jun 2016 17:16:19 -0700 Subject: [PATCH 040/131] test: fix flaky test-fs-watch-encoding on OS X PR-URL: https://github.com/nodejs/node/pull/7356 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com> --- test/parallel/test-fs-watch-encoding.js | 65 ++++++++++++++++--------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/test/parallel/test-fs-watch-encoding.js b/test/parallel/test-fs-watch-encoding.js index 449d0c8bf4050c..f17ed7c30a19a4 100644 --- a/test/parallel/test-fs-watch-encoding.js +++ b/test/parallel/test-fs-watch-encoding.js @@ -1,54 +1,75 @@ 'use strict'; +// This test is a bit more complicated than it ideally needs to be to work +// around issues on OS X and SmartOS. +// +// On OS X, watch events are subject to peculiar timing oddities such that an +// event might fire out of order. The synchronous refreshing of the tmp +// directory might trigger an event on the watchers that are instantiated after +// it! +// +// On SmartOS, the watch events fire but the filename is null. + const common = require('../common'); const fs = require('fs'); const path = require('path'); -const assert = require('assert'); - -if (common.isFreeBSD) { - common.skip('Test currently not working on FreeBSD'); - return; -} common.refreshTmpDir(); const fn = '新建文夹件.txt'; const a = path.join(common.tmpDir, fn); +const watchers = new Set(); + +function registerWatcher(watcher) { + watchers.add(watcher); +} + +function unregisterWatcher(watcher) { + watcher.close(); + watchers.delete(watcher); + if (watchers.size === 0) { + clearInterval(interval); + } +} + const watcher1 = fs.watch( common.tmpDir, {encoding: 'hex'}, (event, filename) => { - if (filename) - assert.equal(filename, 'e696b0e5bbbae69687e5a4b9e4bbb62e747874'); - watcher1.close(); + if (['e696b0e5bbbae69687e5a4b9e4bbb62e747874', null].includes(filename)) + done(watcher1); } ); +registerWatcher(watcher1); const watcher2 = fs.watch( common.tmpDir, (event, filename) => { - if (filename) - assert.equal(filename, fn); - watcher2.close(); + if ([fn, null].includes(filename)) + done(watcher2); } ); +registerWatcher(watcher2); const watcher3 = fs.watch( common.tmpDir, {encoding: 'buffer'}, (event, filename) => { - if (filename) { - assert(filename instanceof Buffer); - assert.equal(filename.toString('utf8'), fn); - } - watcher3.close(); + if (filename instanceof Buffer && filename.toString('utf8') === fn) + done(watcher3); + else if (filename === null) + done(watcher3); } ); +registerWatcher(watcher3); -const fd = fs.openSync(a, 'w+'); -fs.closeSync(fd); +const done = common.mustCall(unregisterWatcher, watchers.size); -process.on('exit', () => { - fs.unlink(a); -}); +// OS X and perhaps other systems can have surprising race conditions with +// file events. So repeat the operation in case it is missed the first time. +const interval = setInterval(() => { + const fd = fs.openSync(a, 'w+'); + fs.closeSync(fd); + fs.unlinkSync(a); +}, common.platformTimeout(100)); From 962ac37e1f07f0ce36266a3156dced149b6387d1 Mon Sep 17 00:00:00 2001 From: Brian White <mscdex@mscdex.net> Date: Wed, 15 Jun 2016 12:42:52 -0400 Subject: [PATCH 041/131] string_decoder: fix bad utf8 character handling This commit fixes an issue when extra utf8 continuation bytes appear at the end of a chunk of data, causing miscalculations to be made when checking how many bytes are needed to decode a complete character. Fixes: https://github.com/nodejs/node/issues/7308 PR-URL: https://github.com/nodejs/node/pull/7310 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com> --- lib/string_decoder.js | 75 ++++++++++++++++++++++------ test/parallel/test-string-decoder.js | 2 +- 2 files changed, 62 insertions(+), 15 deletions(-) diff --git a/lib/string_decoder.js b/lib/string_decoder.js index aaadfd89341a8c..2e9b57d6810785 100644 --- a/lib/string_decoder.js +++ b/lib/string_decoder.js @@ -45,8 +45,10 @@ function StringDecoder(encoding) { case 'utf16le': this.text = utf16Text; this.end = utf16End; - // fall through + nb = 4; + break; case 'utf8': + this.fillLast = utf8FillLast; nb = 4; break; case 'base64': @@ -88,7 +90,7 @@ StringDecoder.prototype.end = utf8End; // Returns only complete characters in a Buffer StringDecoder.prototype.text = utf8Text; -// Attempts to complete a partial character using bytes from a Buffer +// Attempts to complete a partial non-UTF-8 character using bytes from a Buffer StringDecoder.prototype.fillLast = function(buf) { if (this.lastNeed <= buf.length) { buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); @@ -112,38 +114,83 @@ function utf8CheckByte(byte) { return -1; } -// Checks at most the last 3 bytes of a Buffer for an incomplete UTF-8 -// character, returning the total number of bytes needed to complete the partial -// character (if applicable). +// Checks at most 3 bytes at the end of a Buffer in order to detect an +// incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) +// needed to complete the UTF-8 character (if applicable) are returned. function utf8CheckIncomplete(self, buf, i) { var j = buf.length - 1; if (j < i) return 0; - var nb = utf8CheckByte(buf[j--]); + var nb = utf8CheckByte(buf[j]); if (nb >= 0) { if (nb > 0) - self.lastNeed = nb + 1 - (buf.length - j); + self.lastNeed = nb - 1; return nb; } - if (j < i) + if (--j < i) return 0; - nb = utf8CheckByte(buf[j--]); + nb = utf8CheckByte(buf[j]); if (nb >= 0) { if (nb > 0) - self.lastNeed = nb + 1 - (buf.length - j); + self.lastNeed = nb - 2; return nb; } - if (j < i) + if (--j < i) return 0; - nb = utf8CheckByte(buf[j--]); + nb = utf8CheckByte(buf[j]); if (nb >= 0) { - if (nb > 0) - self.lastNeed = nb + 1 - (buf.length - j); + if (nb > 0) { + if (nb === 2) + nb = 0; + else + self.lastNeed = nb - 3; + } return nb; } return 0; } +// Validates as many continuation bytes for a multi-byte UTF-8 character as +// needed or are available. If we see a non-continuation byte where we expect +// one, we "replace" the validated continuation bytes we've seen so far with +// UTF-8 replacement characters ('\ufffd'), to match v8's UTF-8 decoding +// behavior. The continuation byte check is included three times in the case +// where all of the continuation bytes for a character exist in the same buffer. +// It is also done this way as a slight performance increase instead of using a +// loop. +function utf8CheckExtraBytes(self, buf, p) { + if ((buf[0] & 0xC0) !== 0x80) { + self.lastNeed = 0; + return '\ufffd'.repeat(p); + } + if (self.lastNeed > 1 && buf.length > 1) { + if ((buf[1] & 0xC0) !== 0x80) { + self.lastNeed = 1; + return '\ufffd'.repeat(p + 1); + } + if (self.lastNeed > 2 && buf.length > 2) { + if ((buf[2] & 0xC0) !== 0x80) { + self.lastNeed = 2; + return '\ufffd'.repeat(p + 2); + } + } + } +} + +// Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. +function utf8FillLast(buf) { + const p = this.lastTotal - this.lastNeed; + var r = utf8CheckExtraBytes(this, buf, p); + if (r !== undefined) + return r; + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, p, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); + } + buf.copy(this.lastChar, p, 0, buf.length); + this.lastNeed -= buf.length; +} + // Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a // partial character, the character's bytes are buffered until the required // number of bytes are available. diff --git a/test/parallel/test-string-decoder.js b/test/parallel/test-string-decoder.js index 14933c46fcd888..7f1a47abcc65d1 100644 --- a/test/parallel/test-string-decoder.js +++ b/test/parallel/test-string-decoder.js @@ -55,7 +55,7 @@ assert.strictEqual(decoder.write(Buffer.from('\ufffd\ufffd\ufffd')), assert.strictEqual(decoder.end(), ''); decoder = new StringDecoder('utf8'); -assert.strictEqual(decoder.write(Buffer.from('efbfbde2', 'hex')), '\ufffd'); +assert.strictEqual(decoder.write(Buffer.from('EFBFBDE2', 'hex')), '\ufffd'); assert.strictEqual(decoder.end(), '\ufffd'); From 8f1733c4e705afc27bbb6d0f0e1a7fbb90fb124c Mon Sep 17 00:00:00 2001 From: Martin von Gagern <Martin.vGagern@gmx.net> Date: Thu, 16 Jun 2016 15:09:12 +0200 Subject: [PATCH 042/131] test: add more UTF-8 StringDecoder tests PR-URL: https://github.com/nodejs/node/pull/7310 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com> --- test/parallel/test-string-decoder.js | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/parallel/test-string-decoder.js b/test/parallel/test-string-decoder.js index 7f1a47abcc65d1..151ae8b4e0b11a 100644 --- a/test/parallel/test-string-decoder.js +++ b/test/parallel/test-string-decoder.js @@ -28,6 +28,30 @@ test( '\u02e4\u0064\u12e4\u0030\u3045' ); +// Some invalid input, known to have caused trouble with chunking +// in https://github.com/nodejs/node/pull/7310#issuecomment-226445923 +// 00: |00000000 ASCII +// 41: |01000001 ASCII +// B8: 10|111000 continuation +// CC: 110|01100 two-byte head +// E2: 1110|0010 three-byte head +// F0: 11110|000 four-byte head +// F1: 11110|001'another four-byte head +// FB: 111110|11 "five-byte head", not UTF-8 +test('utf-8', Buffer.from('C9B5A941', 'hex'), '\u0275\ufffdA'); +test('utf-8', Buffer.from('E2', 'hex'), '\ufffd'); +test('utf-8', Buffer.from('E241', 'hex'), '\ufffdA'); +test('utf-8', Buffer.from('CCCCB8', 'hex'), '\ufffd\u0338'); +test('utf-8', Buffer.from('F0B841', 'hex'), '\ufffd\ufffdA'); +test('utf-8', Buffer.from('F1CCB8', 'hex'), '\ufffd\u0338'); +test('utf-8', Buffer.from('F0FB00', 'hex'), '\ufffd\ufffd\0'); +test('utf-8', Buffer.from('CCE2B8B8', 'hex'), '\ufffd\u2e38'); +test('utf-8', Buffer.from('E2B8CCB8', 'hex'), '\ufffd\ufffd\u0338'); +test('utf-8', Buffer.from('E2FBCC01', 'hex'), '\ufffd\ufffd\ufffd\u0001'); +test('utf-8', Buffer.from('EDA0B5EDB08D', 'hex'), // CESU-8 of U+1D40D + '\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd'); +test('utf-8', Buffer.from('CCB8CDB9', 'hex'), '\u0338\u0379'); + // UCS-2 test('ucs2', Buffer.from('ababc', 'ucs2'), 'ababc'); @@ -58,6 +82,11 @@ decoder = new StringDecoder('utf8'); assert.strictEqual(decoder.write(Buffer.from('EFBFBDE2', 'hex')), '\ufffd'); assert.strictEqual(decoder.end(), '\ufffd'); +decoder = new StringDecoder('utf8'); +assert.strictEqual(decoder.write(Buffer.from('F1', 'hex')), ''); +assert.strictEqual(decoder.write(Buffer.from('41F2', 'hex')), '\ufffdA'); +assert.strictEqual(decoder.end(), '\ufffd'); + // Additional UTF-16LE surrogate pair tests decoder = new StringDecoder('utf16le'); @@ -93,6 +122,7 @@ function test(encoding, input, expected, singleSequence) { sequence.forEach(function(write) { output += decoder.write(input.slice(write[0], write[1])); }); + output += decoder.end(); process.stdout.write('.'); if (output !== expected) { var message = From 09a7c91baa7d2f206a1bac5f2b41e2788b220a6c Mon Sep 17 00:00:00 2001 From: Jeremiah Senkpiel <fishrock123@rocketmail.com> Date: Wed, 11 May 2016 12:38:04 -0400 Subject: [PATCH 043/131] doc: update "who to cc in issues" chart Refs: https://github.com/nodejs/node/issues/6655 PR-URL: https://github.com/nodejs/node/pull/6694 Reviewed-By: Myles Borins <myles.borins@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Ingvar Stepanyan <me@rreverser.com> --- doc/onboarding-extras.md | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/doc/onboarding-extras.md b/doc/onboarding-extras.md index 55db6107350951..be9b48b449e565 100644 --- a/doc/onboarding-extras.md +++ b/doc/onboarding-extras.md @@ -2,23 +2,30 @@ | subsystem | maintainers | | --- | --- | -| `lib/buffer` | @trevnorris | -| `lib/child_process` | @cjihrig, @bnoordhuis | -| `lib/cluster` | @cjihrig, @bnoordhuis | +| `lib/assert` | @nodejs/testing | +| `lib/buffer` | @nodejs/buffer | +| `lib/child_process` | @bnoordhuis, @cjihrig | +| `lib/cluster` | @bnoordhuis, @cjihrig, @mcollina | | `lib/{crypto,tls,https}` | @nodejs/crypto | | `lib/domains` | @misterdjules | -| `lib/{_}http{*}` | @indutny, @bnoordhuis, @mscdex, @nodejs/http | -| `lib/net` | @indutny, @bnoordhuis, @nodejs/streams | +| `lib/fs`, `src/{fs|file}` | @nodejs/fs | +| `lib/{_}http{*}` | @nodejs/http | +| `lib/net` | @bnoordhuis, @indutny, @nodejs/streams | | `lib/{_}stream{s|*}` | @nodejs/streams | -| `lib/repl` | @fishrock123 | +| `lib/repl` | @addaleax, @fishrock123 | | `lib/timers` | @fishrock123, @misterdjules | -| `lib/zlib` | @indutny, @bnoordhuis | +| `lib/util` | @bnoordhuis, @cjihrig, @evanlucas | +| `lib/zlib` | @addaleax, @bnoordhuis, @indutny | +| `bootstrap_node.js` | @fishrock123 | | `src/async-wrap.*` | @trevnorris | | `src/node_crypto.*` | @nodejs/crypto | | `test/*` | @nodejs/testing | | `tools/eslint`, `.eslintrc` | @silverwind, @trott | -| upgrading v8 | @bnoordhuis, @targos, @ofrobots | -| upgrading npm | @thealphanerd, @fishrock123 | +| upgrading V8 | @nodejs/v8, @nodejs/post-mortem | +| upgrading npm | @fishrock123, @thealphanerd | +| upgrading c-ares | @jbergstroem | +| upgrading http-parser | @jbergstroem, @nodejs/http | +| upgrading libuv | @saghul | When things need extra attention, are controversial, or `semver-major`: @nodejs/ctc From 4486ba9ee1ba8de0128c1962087d344d43f24dd5 Mon Sep 17 00:00:00 2001 From: Lance Ball <lball@redhat.com> Date: Fri, 24 Jun 2016 13:04:31 -0400 Subject: [PATCH 044/131] doc: add lance to collaborators Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> PR-URL: https://github.com/nodejs/node/pull/7407 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 50e84e0e7fc37c..baceb16d7fe4c9 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,7 @@ information about the governance of the Node.js project, see * [joaocgreis](https://github.com/joaocgreis) - **João Reis** <reis@janeasystems.com> * [julianduque](https://github.com/julianduque) - **Julian Duque** <julianduquej@gmail.com> * [JungMinu](https://github.com/JungMinu) - **Minwoo Jung** <jmwsoft@gmail.com> +* [lance](https://github.com/lance) - **Lance Ball** <lball@redhat.com> * [lxe](https://github.com/lxe) - **Aleksey Smolenchuk** <lxe@lxe.co> * [matthewloring](https://github.com/matthewloring) - **Matthew Loring** <mattloring@google.com> * [mcollina](https://github.com/mcollina) - **Matteo Collina** <matteo.collina@gmail.com> From 16aff79deea73ea934da2a7109c10271e29961c7 Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Wed, 22 Jun 2016 16:21:21 -0700 Subject: [PATCH 045/131] test: add test for exec() known issue PR-URL: https://github.com/nodejs/node/pull/7375 Refs: https://github.com/nodejs/node/issues/7342 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com> --- ...test-child-process-exec-stdout-data-string.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 test/known_issues/test-child-process-exec-stdout-data-string.js diff --git a/test/known_issues/test-child-process-exec-stdout-data-string.js b/test/known_issues/test-child-process-exec-stdout-data-string.js new file mode 100644 index 00000000000000..b267ff5e98347a --- /dev/null +++ b/test/known_issues/test-child-process-exec-stdout-data-string.js @@ -0,0 +1,16 @@ +'use strict'; +// Refs: https://github.com/nodejs/node/issues/7342 +const common = require('../common'); +const assert = require('assert'); +const exec = require('child_process').exec; + +const expectedCalls = 2; + +const cb = common.mustCall((data) => { + assert.strictEqual(typeof data, 'string'); +}, expectedCalls); + +const command = common.isWindows ? 'dir' : 'ls'; +exec(command).stdout.on('data', cb); + +exec('fhqwhgads').stderr.on('data', cb); From 457d244170c76f5c09994130ec3ad08560ab068e Mon Sep 17 00:00:00 2001 From: Anna Henningsen <anna@addaleax.net> Date: Wed, 22 Jun 2016 23:39:21 +0200 Subject: [PATCH 046/131] test: fix flaky test-vm-timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Likely fix the flaky parallel/test-vm-timeout. Increase the outer timeout in the test checking for nested timeouts with `vm` scripts so that its firing won’t interfere with the inner timeout. Fixes: https://github.com/nodejs/node/issues/6727 PR-URL: https://github.com/nodejs/node/pull/7373 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- test/parallel/test-vm-timeout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/parallel/test-vm-timeout.js b/test/parallel/test-vm-timeout.js index b4dd57bb54d234..d595bac4c32324 100644 --- a/test/parallel/test-vm-timeout.js +++ b/test/parallel/test-vm-timeout.js @@ -29,6 +29,6 @@ assert.throws(function() { vm.runInNewContext('while(true) {}', context, { timeout: timeout }); } }; - vm.runInNewContext('runInVM(10)', context, { timeout: 100 }); + vm.runInNewContext('runInVM(10)', context, { timeout: 10000 }); throw new Error('Test 5 failed'); }, /Script execution timed out./); From 55b87c0238b7cf277d2e872d90bb9729be32ba0e Mon Sep 17 00:00:00 2001 From: Anna Henningsen <anna@addaleax.net> Date: Wed, 22 Jun 2016 23:32:24 +0200 Subject: [PATCH 047/131] vm: test for abort condition of current invocation When a vm script aborted after a timeout/signal interruption, test whether the local timeout/signal watchdog was responsible for terminating the execution. Without this, when a shorter timer from an outer `vm.run*` invocation fires before an inner timeout, the inner timeout would throw an error instead of the outer one, but because it did not witness the timeout itself, it would assume the termination was the result of a signal interruption. PR-URL: https://github.com/nodejs/node/pull/7373 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- src/node_contextify.cc | 21 ++++++++++++++++++--- src/node_watchdog.cc | 3 ++- src/node_watchdog.h | 2 ++ test/parallel/test-vm-timeout.js | 23 +++++++++++++++++++++++ 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/node_contextify.cc b/src/node_contextify.cc index a4a769359412ef..e5cbfd5512cc01 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -836,14 +836,17 @@ class ContextifyScript : public BaseObject { Local<Value> result; bool timed_out = false; + bool received_signal = false; if (break_on_sigint && timeout != -1) { Watchdog wd(env->isolate(), timeout); SigintWatchdog swd(env->isolate()); result = script->Run(); timed_out = wd.HasTimedOut(); + received_signal = swd.HasReceivedSignal(); } else if (break_on_sigint) { SigintWatchdog swd(env->isolate()); result = script->Run(); + received_signal = swd.HasReceivedSignal(); } else if (timeout != -1) { Watchdog wd(env->isolate(), timeout); result = script->Run(); @@ -852,14 +855,26 @@ class ContextifyScript : public BaseObject { result = script->Run(); } - if (try_catch.HasCaught() && try_catch.HasTerminated()) { - env->isolate()->CancelTerminateExecution(); + if (try_catch.HasCaught()) { + if (try_catch.HasTerminated()) + env->isolate()->CancelTerminateExecution(); + + // It is possible that execution was terminated by another timeout in + // which this timeout is nested, so check whether one of the watchdogs + // from this invocation is responsible for termination. if (timed_out) { env->ThrowError("Script execution timed out."); - } else { + } else if (received_signal) { env->ThrowError("Script execution interrupted."); } + + // If there was an exception thrown during script execution, re-throw it. + // If one of the above checks threw, re-throw the exception instead of + // letting try_catch catch it. + // If execution has been terminated, but not by one of the watchdogs from + // this invocation, this will re-throw a `null` value. try_catch.ReThrow(); + return false; } diff --git a/src/node_watchdog.cc b/src/node_watchdog.cc index 8e94bc8d9d22ac..8a067c27f3b6c2 100644 --- a/src/node_watchdog.cc +++ b/src/node_watchdog.cc @@ -99,7 +99,7 @@ void SigintWatchdog::Dispose() { SigintWatchdog::SigintWatchdog(v8::Isolate* isolate) - : isolate_(isolate), destroyed_(false) { + : isolate_(isolate), received_signal_(false), destroyed_(false) { // Register this watchdog with the global SIGINT/Ctrl+C listener. SigintWatchdogHelper::GetInstance()->Register(this); // Start the helper thread, if that has not already happened. @@ -120,6 +120,7 @@ void SigintWatchdog::Destroy() { void SigintWatchdog::HandleSigint() { + received_signal_ = true; isolate_->TerminateExecution(); } diff --git a/src/node_watchdog.h b/src/node_watchdog.h index 65815e2bcddc30..77c2d535340b6f 100644 --- a/src/node_watchdog.h +++ b/src/node_watchdog.h @@ -46,11 +46,13 @@ class SigintWatchdog { void Dispose(); v8::Isolate* isolate() { return isolate_; } + bool HasReceivedSignal() { return received_signal_; } void HandleSigint(); private: void Destroy(); v8::Isolate* isolate_; + bool received_signal_; bool destroyed_; }; diff --git a/test/parallel/test-vm-timeout.js b/test/parallel/test-vm-timeout.js index d595bac4c32324..0536ae37a1b489 100644 --- a/test/parallel/test-vm-timeout.js +++ b/test/parallel/test-vm-timeout.js @@ -32,3 +32,26 @@ assert.throws(function() { vm.runInNewContext('runInVM(10)', context, { timeout: 10000 }); throw new Error('Test 5 failed'); }, /Script execution timed out./); + +// Test 6: Nested vm timeouts, outer timeout is shorter and fires first. +assert.throws(function() { + const context = { + runInVM: function(timeout) { + vm.runInNewContext('while(true) {}', context, { timeout: timeout }); + } + }; + vm.runInNewContext('runInVM(10000)', context, { timeout: 100 }); + throw new Error('Test 6 failed'); +}, /Script execution timed out./); + +// Test 7: Nested vm timeouts, inner script throws an error. +assert.throws(function() { + const context = { + runInVM: function(timeout) { + vm.runInNewContext('throw new Error(\'foobar\')', context, { + timeout: timeout + }); + } + }; + vm.runInNewContext('runInVM(10000)', context, { timeout: 100000 }); +}, /foobar/); From e30f32f003a3a049ec0f62f71edbadc6f3ace7fe Mon Sep 17 00:00:00 2001 From: Anna Henningsen <anna@addaleax.net> Date: Thu, 23 Jun 2016 00:26:47 +0200 Subject: [PATCH 048/131] Revert "test: mark test-vm-timeout flaky on windows" This reverts commit f34caa96d1c6374aa44999f41aa538d1171abd0c. PR-URL: https://github.com/nodejs/node/pull/7373 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- test/parallel/parallel.status | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/parallel/parallel.status b/test/parallel/parallel.status index 0a23f0614a1b0c..e3a8e345f732a7 100644 --- a/test/parallel/parallel.status +++ b/test/parallel/parallel.status @@ -7,8 +7,7 @@ prefix parallel [true] # This section applies to all platforms [$system==win32] -test-tick-processor : PASS,FLAKY -test-vm-timeout : PASS,FLAKY +test-tick-processor : PASS,FLAKY [$system==linux] test-tick-processor : PASS,FLAKY From 6b1fc63dcb0cfab31347d2c92ff1c5be8397f6c8 Mon Sep 17 00:00:00 2001 From: Evan Lucas <evanlucas@me.com> Date: Thu, 2 Jun 2016 20:31:23 -0500 Subject: [PATCH 049/131] readline: allow passing prompt to constructor Previously, one would have to call setPrompt after calling rl.createInterface. Now, the prompt string can be set by passing the prompt property. PR-URL: https://github.com/nodejs/node/pull/7125 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Conflicts: test/parallel/test-readline-interface.js --- doc/api/readline.md | 8 +++-- lib/readline.js | 6 +++- lib/repl.js | 7 ++--- test/parallel/test-readline-interface.js | 37 ++++++++++++++++++++---- 4 files changed, 45 insertions(+), 13 deletions(-) diff --git a/doc/api/readline.md b/doc/api/readline.md index 448b109e4d5579..385540908331c2 100644 --- a/doc/api/readline.md +++ b/doc/api/readline.md @@ -357,6 +357,7 @@ added: v0.1.98 the history set this value to `0`. Defaults to `30`. This option makes sense only if `terminal` is set to `true` by the user or by an internal `output` check, otherwise the history caching mechanism is not initialized at all. + * `prompt` - the prompt string to use. Default: `'> '` The `readline.createInterface()` method creates a new `readline.Interface` instance. @@ -467,9 +468,12 @@ implement a small command-line interface: ```js const readline = require('readline'); -const rl = readline.createInterface(process.stdin, process.stdout); +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + prompt: 'OHAI> ' +}); -rl.setPrompt('OHAI> '); rl.prompt(); rl.on('line', (line) => { diff --git a/lib/readline.js b/lib/readline.js index 46982bca5feaa8..96d7e114891135 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -45,6 +45,7 @@ function Interface(input, output, completer, terminal) { EventEmitter.call(this); var historySize; + let prompt = '> '; if (arguments.length === 1) { // an options object was given @@ -52,6 +53,9 @@ function Interface(input, output, completer, terminal) { completer = input.completer; terminal = input.terminal; historySize = input.historySize; + if (input.prompt !== undefined) { + prompt = input.prompt; + } input = input.input; } @@ -88,7 +92,7 @@ function Interface(input, output, completer, terminal) { }; } - this.setPrompt('> '); + this.setPrompt(prompt); this.terminal = !!terminal; diff --git a/lib/repl.js b/lib/repl.js index db5754ec041196..01a595984d6cfe 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -385,11 +385,10 @@ function REPLServer(prompt, output: self.outputStream, completer: complete, terminal: options.terminal, - historySize: options.historySize + historySize: options.historySize, + prompt }); - self.setPrompt(prompt !== undefined ? prompt : '> '); - this.commands = Object.create(null); defineDefaultCommands(this); @@ -408,8 +407,6 @@ function REPLServer(prompt, }; } - self.setPrompt(self._prompt); - self.on('close', function() { self.emit('exit'); }); diff --git a/test/parallel/test-readline-interface.js b/test/parallel/test-readline-interface.js index b7f906d7c21686..fcda628acec434 100644 --- a/test/parallel/test-readline-interface.js +++ b/test/parallel/test-readline-interface.js @@ -1,9 +1,11 @@ 'use strict'; -require('../common'); -var assert = require('assert'); -var readline = require('readline'); -var EventEmitter = require('events').EventEmitter; -var inherits = require('util').inherits; +const common = require('../common'); +const assert = require('assert'); +const readline = require('readline'); +const EventEmitter = require('events').EventEmitter; +const inherits = require('util').inherits; +const Writable = require('stream').Writable; +const Readable = require('stream').Readable; function FakeInput() { EventEmitter.call(this); @@ -400,4 +402,29 @@ function isWarned(emitter) { }); }); + { + const expected = terminal + ? ['\u001b[1G', '\u001b[0J', '$ ', '\u001b[3G'] + : ['$ ']; + + let counter = 0; + const output = new Writable({ + write: common.mustCall((chunk, enc, cb) => { + assert.strictEqual(chunk.toString(), expected[counter++]); + cb(); + rl.close(); + }, expected.length) + }); + + const rl = readline.createInterface({ + input: new Readable({ read: () => {} }), + output: output, + prompt: '$ ', + terminal: terminal + }); + + rl.prompt(); + + assert.strictEqual(rl._prompt, '$ '); + } }); From b725437c8149f29a89ae9704013d3ee9ccbb4096 Mon Sep 17 00:00:00 2001 From: Jeremiah Senkpiel <fishrock123@rocketmail.com> Date: Fri, 24 Jun 2016 14:33:57 +0200 Subject: [PATCH 050/131] doc: minor rewording to the GitHub issue/pr templates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Templates are now a bit more consistent with phrasing. - Simplified a bit. PR-URL: https://github.com/nodejs/node/pull/7403 Reviewed-By: Claudio Rodriguez <cjrodr@yahoo.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Minwoo Jung <jmwsoft@gmail.com> Reviewed-By: Michaël Zasso <mic.besace@gmail.com> --- .github/ISSUE_TEMPLATE.md | 8 ++++---- .github/PULL_REQUEST_TEMPLATE.md | 13 ++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 3ae4132fbcbdc7..4c115cb593573f 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,9 +1,9 @@ <!-- -Thank you for reporting an issue. Please fill in the template below. If unsure -about something, just do as best as you're able. +Thank you for reporting an issue. +Please fill in as much of the template below as you're able. -Version: usually output of `node -v` -Platform: either `uname -a` output, or if Windows, version and 32 or 64-bit +Version: output of `node -v` +Platform: output of `uname -a` (UNIX), or version and 32 or 64-bit (Windows) Subsystem: if known, please specify affected core module name If possible, please provide code that demonstrates the problem, keeping it as diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 3ce7dac0f7fb20..7b004801207054 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1,7 @@ <!-- Thank you for your pull request. Please review below requirements. -Bug fixes and new features should include tests and possibly a benchmark. +Bug fixes and new features should include tests and possibly benchmarks. Contributors guide: https://github.com/nodejs/node/blob/master/CONTRIBUTING.md --> @@ -9,15 +9,14 @@ Contributors guide: https://github.com/nodejs/node/blob/master/CONTRIBUTING.md ##### Checklist <!-- Remove items that do not apply. For completed items, change [ ] to [x]. --> -- [ ] `make -j4 test` (UNIX) or `vcbuild test nosign` (Windows) passes -- [ ] a test and/or benchmark is included +- [ ] `make -j4 test` (UNIX), or `vcbuild test nosign` (Windows) passes +- [ ] tests and/or benchmarks are included - [ ] documentation is changed or added -- [ ] the commit message follows commit guidelines - +- [ ] commit message follows commit guidelines ##### Affected core subsystem(s) -<!-- provide affected core subsystem(s) (like doc, cluster, crypto, etc) --> +<!-- Provide affected core subsystem(s) (like doc, cluster, crypto, etc). --> ##### Description of change -<!-- provide a description of the change below this comment --> +<!-- Provide a description of the change below this comment. --> From efce335e631f0f8bbbcf29aba0bd3fa6144b8d67 Mon Sep 17 00:00:00 2001 From: Josh Gavant <josh.gavant@outlook.com> Date: Thu, 16 Jun 2016 08:37:11 -0700 Subject: [PATCH 051/131] doc: add CTC meeting minutes 2016-06-15 PR-URL: https://github.com/nodejs/node/pull/7320 Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Rod Vagg <rod@vagg.org> --- doc/ctc-meetings/2016-06-15.md | 175 +++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 doc/ctc-meetings/2016-06-15.md diff --git a/doc/ctc-meetings/2016-06-15.md b/doc/ctc-meetings/2016-06-15.md new file mode 100644 index 00000000000000..3af642b840228f --- /dev/null +++ b/doc/ctc-meetings/2016-06-15.md @@ -0,0 +1,175 @@ +# Node Foundation CTC Meeting 2016-06-15 + +## Links + +* **Audio Recording**: https://www.youtube.com/watch?v=qWX8i9SKatQ +* **GitHub Issue**: https://github.com/nodejs/node/issues/7307 +* **Minutes Google Doc**: <https://docs.google.com/document/d/1e7JdFHVtMtW9_o0Gi3NNz6g7TK50q4u9LYKFzBeHOQ8> +* _Previous Minutes Google Doc: <https://docs.google.com/document/d/1G_sywyzJFPDzLv-KiG21L_4cIAP9_awQnP00ox8Nyiw>_ + +## Present + +* Bradley Meck @bmeck (observer/GoDaddy) +* Сковорода Никита Андреевич @ChALkeR (CTC) +* Chris Dickinson @chrisdickinson (CTC) +* Evan Lucas @evanlucas (CTC) +* Jeremiah Senkpiel @Fishrock123 (CTC) +* John-David Dalton @jdalton (observer/Microsoft) +* Josh Gavant @joshgav (observer/Microsoft) +* Michael Dawson @mhdawson (CTC) +* Brian White @mscdex (CTC) +* Ali Ijaz Sheikh @ofrobots (CTC) +* Alexis Campailla @orangemocha (CTC) +* Rod Vagg @rvagg (CTC) +* Rich Trott @Trott (CTC) +* Trevor Norris @trevnorris (CTC) + +## Agenda + +Extracted from **ctc-agenda** labelled issues and pull requests from the **nodejs org** prior to the meeting. + +### nodejs/node + +* url: return valid file: urls fom url.format() [#7234](https://github.com/nodejs/node/pull/7234) +* http: don't inherit from Object.prototype [#6102](https://github.com/nodejs/node/pull/6102) + +### Standup + +* Bradley Meck @bmeck + * fleshing out a proposal for if we could disambiguate the grammars for Script and Module. + +* Сковорода Никита Андреевич @ChALkeR (CTC) + * some issue/PRs reviews + +* Chris Dickinson @chrisdickinson (CTC) + * NodeConf + * modules.guide + +* Evan Lucas @evanlucas (CTC) + * Preparing for security release + +* Jeremiah Senkpiel @Fishrock123 (CTC) + * (Previous week: fixed the primary OS X stdio bug) + * NodeConf + +* Fedor Indutny @indutny (CTC) + * fixing bugs, reviewing PRs, working on llnode + +* Josh Gavant @joshgav + * debug protocol stuff + +* Michael Dawson @mhdawson (CTC) + * Still chasing PPC machine issues + * AIX/malloc(0) issue + * ABI stable module API work with Stefan/Ian, filling in Nan examples + * Input on some benchmarking related PRs + * Other misc reviews/lands + * Keeping up with issues + +* Brian White @mscdex (CTC) + * Landed some old PRs + * Submitting PRs to fix some regressions + * Reviewed PRs and issues + +* Ali Ijaz Sheikh @ofrobots (CTC) + * More work on v8_inspector + * Starting to look at backporting some V8 fixes for LTS + +* Alexis Campailla @orangemocha (CTC) + * Landed a fix for node-gyp, broke with VS update 3 + +* Rod Vagg @rvagg (CTC) + * Alpine Linux in CI + * Security release hoo haa + * Reviews & discussions + * Electron / Node relationship + * New CTC repo + * Jenkins upkeep + +* Rich Trott @Trott (CTC) + * Setting up the next onboarding + * Facilitated a session on releases at NodeConf. Will share notes with Build WG, LTS WG, and people who can sign releases. + +* Trevor Norris @trevnorris (CTC) + * Finished updating AsyncWrap EP and now investigating proposed implementation. + * Helping identify old issue in Atom editor in regards to writing to disk. + + +### Review of last meeting +* Tracking issue: stdio problems [#6980](https://github.com/nodejs/node/issues/6980) +* module: expose `Module._runInThisContext` [#6288](https://github.com/nodejs/node/pull/6288) + + +## Minutes + + +### url: return valid file: urls from url.format() [#7234](https://github.com/nodejs/node/pull/7234) + +@trott: semver-major change, needs approval from CTC. +Real fix will be @jasnell’s HTTP compliance work. + +In browsers `file:/home/joshgav/myfile.txt` is auto-corrected to `file:///home/joshgav/myfile.txt` (i.e. slashes are prepended to the path and hostname is an empty string). This change institutes the same in Node.js. + +Are there other protocols which require additional slashes (`//`) if hostname isn’t specified? Yes, but hard to heuristically determine if needed. + + +### http: don't inherit from Object.prototype [#6102](https://github.com/nodejs/node/pull/6102) + +Replace headers object ({}) in req/res with StorageObject, which doesn’t delegate to Object.prototype. But this will break anyone using regular `Object` methods on header props. + +@trevnorris: Why don’t we intercept calls to properties with checks to an internal dictionary of actual headers? If the key isn’t there, then try to call Object.prototype. + +How would that effect perf? + +It’s the right decision because otherwise can’t use headers with certain names like `__proto__`. + +Better to do a deprecation cycle. How? Insert something (proxy) between actual call to property, would issue deprecation warning first. This would be temporary, eventually this interceptor/proxy would go away. + + +### ES6 Modules [node-eps/002-es6-modules.md](https://github.com/nodejs/node-eps/blob/master/002-es6-modules.md) + +Need to disambiguate ES6 modules and regular scripts (which include CJS modules). Cannot determine if file is module, script, etc. from code itself. For this reason Node decided to use `.mjs` extension for ES6 modules. + +New proposal: If `import` or `export` keywords are in module code, then use module goal. So no need for extra metadata or file extension. But would have to parse file to check for presence of these keywords. + +https://github.com/bmeck/UnambiguousJavaScriptGrammar +replaces https://github.com/nodejs/node-eps/blob/master/002-es6-modules.md#51-determining-if-source-is-an-es-module + +This would be part of ECMA262 so browsers would do the same, but needs to be ironed out in TC39. On [agenda][TC39 Agenda] for 7/26 TC39 meeting. + +[TC39 Agenda]: https://github.com/tc39/agendas/blob/master/2016/07.md + +What if nothing is imported or exported? Could do `export default null` to export nothing. + +Starting off, preferred goal when preparing code would be CommonJS/script, later on could change to ES6/module. + +Caching is more feasible. + +Provides more seamless flow from CJS to ES6 in the future. + +Will packaging tools need to implement parsing logic too to package properly? Yes, but there are possibilities listed in the repo. + +What other differences between scripts and modules? +- `await` keyword only in modules according to ECMA262 +- `modules.root` in package.json is intended to allow mirrored directory structure for use with ES6; but technically all it does is redirect file system calls and it could be used for other purposes, so it’s not reliable. + +Purpose of modules.root - allows redirection within a module, e.g. `module/file.js` doesn’t necessarily resolve to `./file.js` within the directory, could be redirected to `${module.root}/file.js`. This allows side-by-side CJS and ES6 among other things. + +What about for human reading? How can people differentiate at a glance between CJS and ES6? +- `import`’s are generally at the top, `export`s at the bottom. If you see `import` it’s an ES6 module. + +How are browsers dealing with this? Older browsers which encounter `<script type=”module”>` and don’t recognize the type will skip it. Loading is asynchronous by default. + +Are browsers concerned about transition period from CJS to ES6? How do they load older scripts, e.g. jQuery? + +**Should Node move this forward as alternative to `.mjs` proposal?** + +TBD. What does TC39 think of this? It’s on agenda for next F2F (see above). + + +### Q/A on public fora +None. + +## Next Meeting +2016-06-22 + From ca95a84bc4b565eefbff7e3d1385c3d1d832b9b2 Mon Sep 17 00:00:00 2001 From: Prince J Wesley <princejohnwesley@gmail.com> Date: Wed, 22 Jun 2016 16:24:52 +0530 Subject: [PATCH 052/131] repl: fix tab completion for defined commands PR-URL: https://github.com/nodejs/node/pull/7364 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> --- lib/repl.js | 4 ++-- test/parallel/test-repl-tab-complete.js | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/repl.js b/lib/repl.js index 01a595984d6cfe..18c81c438dee91 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -721,11 +721,11 @@ REPLServer.prototype.complete = function(line, callback) { // REPL commands (e.g. ".break"). var match = null; - match = line.match(/^\s*(\.\w*)$/); + match = line.match(/^\s*\.(\w*)$/); if (match) { completionGroups.push(Object.keys(this.commands)); completeOn = match[1]; - if (match[1].length > 1) { + if (match[1].length) { filter = match[1]; } diff --git a/test/parallel/test-repl-tab-complete.js b/test/parallel/test-repl-tab-complete.js index eb4e68ad18d772..d92377efebf791 100644 --- a/test/parallel/test-repl-tab-complete.js +++ b/test/parallel/test-repl-tab-complete.js @@ -260,3 +260,10 @@ putIn.run(['.clear']); testMe.complete('var log = console.lo', common.mustCall((error, data) => { assert.deepStrictEqual(data, [['console.log'], 'console.lo']); })); + +// tab completion for defined commands +putIn.run(['.clear']); + +testMe.complete('.b', common.mustCall((error, data) => { + assert.deepStrictEqual(data, [['break'], 'b']); +})); From 44bc638cdbc38297e19dda4babd0d2190aeba2c7 Mon Sep 17 00:00:00 2001 From: sartrey <sartrey@163.com> Date: Wed, 22 Jun 2016 14:55:21 +0800 Subject: [PATCH 053/131] doc: clarify child_process stdout/stderr types Clarify how the encoding option interacts with the data type of child process stdout and stderr. Fixes: https://github.com/nodejs/node/issues/6666 PR-URL: https://github.com/nodejs/node/pull/7361 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- doc/api/child_process.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/api/child_process.md b/doc/api/child_process.md index a196f99b282b8d..d519ab4b49ab26 100644 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -164,8 +164,8 @@ The `stdout` and `stderr` arguments passed to the callback will contain the stdout and stderr output of the child process. By default, Node.js will decode the output as UTF-8 and pass strings to the callback. The `encoding` option can be used to specify the character encoding used to decode the stdout and -stderr output. If `encoding` is `'buffer'`, `Buffer` objects will be passed to -the callback instead. +stderr output. If `encoding` is `'buffer'`, or an unrecognized character +encoding, `Buffer` objects will be passed to the callback instead. The `options` argument may be passed as the second argument to customize how the process is spawned. The default options are: @@ -233,8 +233,8 @@ The `stdout` and `stderr` arguments passed to the callback will contain the stdout and stderr output of the child process. By default, Node.js will decode the output as UTF-8 and pass strings to the callback. The `encoding` option can be used to specify the character encoding used to decode the stdout and -stderr output. If `encoding` is `'buffer'`, `Buffer` objects will be passed to -the callback instead. +stderr output. If `encoding` is `'buffer'`, or an unrecognized character +encoding, `Buffer` objects will be passed to the callback instead. ### child_process.fork(modulePath[, args][, options]) <!-- YAML From e7b84007be33e4cbed5ecfc45540a8f6b34a70a2 Mon Sep 17 00:00:00 2001 From: Guy Fraser <guy.fraser1@gmail.com> Date: Thu, 23 Jun 2016 00:35:57 +0100 Subject: [PATCH 054/131] http: replace finish() callback with arrow function Take advantage of arrow function lexical `this` to avoid defining a `self = this` var which was only used once. Code relating to the `finish` event was split in to two areas of the parent function. Gathered it together to clarify association within the script. Fixes: https://github.com/nodejs/node/issues/7295 PR-URL: https://github.com/nodejs/node/pull/7378 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- lib/_http_outgoing.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index 77bfe4ba77d416..c4c40460e6c74e 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -546,14 +546,6 @@ OutgoingMessage.prototype.end = function(data, encoding, callback) { return false; } - var self = this; - function finish() { - self.emit('finish'); - } - - if (typeof callback === 'function') - this.once('finish', callback); - if (!this._header) { if (data) { if (typeof data === 'string') @@ -581,6 +573,13 @@ OutgoingMessage.prototype.end = function(data, encoding, callback) { this.write(data, encoding); } + if (typeof callback === 'function') + this.once('finish', callback); + + const finish = () => { + this.emit('finish'); + }; + if (this._hasBody && this.chunkedEncoding) { ret = this._send('0\r\n' + this._trailer + '\r\n', 'binary', finish); } else { From 35c70b5668cac752b87265445573bf383371a622 Mon Sep 17 00:00:00 2001 From: surya panikkal <surya.com@gmail.com> Date: Thu, 9 Jun 2016 22:10:10 -0400 Subject: [PATCH 055/131] benchmark: `util._extend` vs `object.assign` To copy the values of all enumerable own properties from- a source object to a target object, node still use- `util._extend`, though newer standard `Object.assign` is available. This is because `util._extend` is found to be faster than `Object.assign`. This benchmark test is to keep track of how performance compare. PR-URL: https://github.com/nodejs/node/pull/7255 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Brian White <mscdex@mscdex.net> --- .../misc/util-extend-vs-object-assign.js | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 benchmark/misc/util-extend-vs-object-assign.js diff --git a/benchmark/misc/util-extend-vs-object-assign.js b/benchmark/misc/util-extend-vs-object-assign.js new file mode 100644 index 00000000000000..caea42ce914cf5 --- /dev/null +++ b/benchmark/misc/util-extend-vs-object-assign.js @@ -0,0 +1,40 @@ +'use strict'; + +const common = require('../common.js'); +const util = require('util'); +const v8 = require('v8'); + +const bench = common.createBenchmark(main, { + type: ['extend', 'assign'], + n: [10e4] +}); + +function main(conf) { + let fn; + const n = conf.n | 0; + let v8command; + + if (conf.type === 'extend') { + fn = util._extend; + v8command = '%OptimizeFunctionOnNextCall(util._extend)'; + } else if (conf.type === 'assign') { + fn = Object.assign; + // Object.assign is built-in, cannot be optimized + v8command = ''; + } + + // Force-optimize the method to test so that the benchmark doesn't + // get disrupted by the optimizer kicking in halfway through. + for (var i = 0; i < conf.type.length * 10; i += 1) + fn({}, process.env); + + v8.setFlagsFromString('--allow_natives_syntax'); + eval(v8command); + + var obj = new Proxy({}, { set: function(a, b, c) { return true; } }); + + bench.start(); + for (var j = 0; j < n; j += 1) + fn(obj, process.env); + bench.end(n); +} From 3475591d1c1d9ea7ce8ea96d81119cdaf47d9eeb Mon Sep 17 00:00:00 2001 From: Ruslan Iusupov <rus0000@users.noreply.github.com> Date: Fri, 24 Jun 2016 23:24:09 +0200 Subject: [PATCH 056/131] doc: fix "sign.verify" typo in crypto doc. Fix typo in example PR-URL: https://github.com/nodejs/node/pull/7411 Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- doc/api/crypto.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/crypto.md b/doc/api/crypto.md index e195c98e322746..2738b395e655a7 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -809,7 +809,7 @@ verify.end(); const public_key = getPublicKeySomehow(); const signature = getSignatureToVerify(); -console.log(sign.verify(public_key, signature)); +console.log(verify.verify(public_key, signature)); // Prints true or false ``` From 5f766ad6d0a44b33f21f7a34af04f322d84c5f94 Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Sun, 26 Jun 2016 13:54:19 -0700 Subject: [PATCH 057/131] doc: fix typographic error in process doc An apostrophe was being used where a backtick was called for, resulting in improper rendering. PR-URL: https://github.com/nodejs/node/pull/7431 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: Ingvar Stepanyan <me@rreverser.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- doc/api/process.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/process.md b/doc/api/process.md index 95602d07649d71..34738d76ad3020 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -774,7 +774,7 @@ emitMyWarning(); added: v0.7.7 --> -The `process.execArgv' property returns the set of Node.js-specific command-line +The `process.execArgv` property returns the set of Node.js-specific command-line options passed when the Node.js process was launched. These options do not appear in the array returned by the [`process.argv`][] property, and do not include the Node.js executable, the name of the script, or any options following From a4880b5b109daa441d126d1c679fae46d4f8a27c Mon Sep 17 00:00:00 2001 From: Fedor Indutny <fedor@indutny.com> Date: Sun, 26 Jun 2016 03:54:12 -0400 Subject: [PATCH 058/131] deps: `MASM.UseSafeExceptionHandlers` for OpenSSL Use `msvs_settings.MASM.UseSafeExceptionHandlers` when building OpenSSL assembly code on Windows. This option appends `/safeseh` to the list of assembler flags when building `.asm` files on Windows. Having this option in place, separate rules in `masm_compile.gypi` are no longer needed. Fix: #7426 PR-URL: https://github.com/nodejs/node/pull/7427 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Bert Belder <bertbelder@gmail.com> --- deps/openssl/masm_compile.gypi | 44 ---------------------------------- deps/openssl/openssl.gyp | 7 +++++- 2 files changed, 6 insertions(+), 45 deletions(-) delete mode 100644 deps/openssl/masm_compile.gypi diff --git a/deps/openssl/masm_compile.gypi b/deps/openssl/masm_compile.gypi deleted file mode 100644 index c18e484f73c3b1..00000000000000 --- a/deps/openssl/masm_compile.gypi +++ /dev/null @@ -1,44 +0,0 @@ -{ - 'conditions': [ - ['target_arch=="ia32"', { - 'rules': [ - { - 'rule_name': 'Assemble', - 'extension': 'asm', - 'inputs': [], - 'outputs': [ - '<(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).obj', - ], - 'action': [ - 'ml.exe', - '/Zi', - '/safeseh', - '/Fo', '<(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).obj', - '/c', '<(RULE_INPUT_PATH)', - ], - 'process_outputs_as_sources': 0, - 'message': 'Assembling <(RULE_INPUT_PATH) to <(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).obj.', - } - ], - }, 'target_arch=="x64"', { - 'rules': [ - { - 'rule_name': 'Assemble', - 'extension': 'asm', - 'inputs': [], - 'outputs': [ - '<(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).obj', - ], - 'action': [ - 'ml64.exe', - '/Zi', - '/Fo', '<(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).obj', - '/c', '<(RULE_INPUT_PATH)', - ], - 'process_outputs_as_sources': 0, - 'message': 'Assembling <(RULE_INPUT_PATH) to <(INTERMEDIATE_DIR)/<(RULE_INPUT_ROOT).obj.', - } - ], - }], - ], -} diff --git a/deps/openssl/openssl.gyp b/deps/openssl/openssl.gyp index db57033d54e20e..b25efe60d5e8e6 100644 --- a/deps/openssl/openssl.gyp +++ b/deps/openssl/openssl.gyp @@ -121,7 +121,12 @@ }], # end of conditions of openssl_no_asm ['OS=="win"', { 'defines' : ['<@(openssl_defines_all_win)'], - 'includes': ['masm_compile.gypi',], + 'msvs_settings': { + 'MASM': { + # Use /safeseh, see commit: 01fa5ee + 'UseSafeExceptionHandlers': 'true', + }, + }, }, { 'defines' : ['<@(openssl_defines_all_non_win)'] }] From fa9e6f7463ff886f21ac8efd5a48381f0a895946 Mon Sep 17 00:00:00 2001 From: Michael Wain <michael.wain@skybettingandgaming.com> Date: Mon, 25 Apr 2016 19:04:30 +0100 Subject: [PATCH 059/131] crypto: Allow GCM ciphers to have a longer IV length GCM cipher IV length can be >=1 bytes. When not the default 12 bytes (96 bits) sets the IV length using `EVP_CIPHER_CTX_ctrl` with type `EVP_CTRL_GCM_SET_IVLEN` PR-URL: https://github.com/nodejs/node/pull/6376 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Shigeki Ohtsu <ohtsu@iij.ad.jp> --- src/node_crypto.cc | 14 +- test/parallel/test-crypto-authenticated.js | 325 +++++++++++++++++++-- 2 files changed, 315 insertions(+), 24 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 0c7ecaf3301cfe..2659b0ac66fd39 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3271,12 +3271,24 @@ void CipherBase::InitIv(const char* cipher_type, /* OpenSSL versions up to 0.9.8l failed to return the correct iv_length (0) for ECB ciphers */ if (EVP_CIPHER_iv_length(cipher_) != iv_len && - !(EVP_CIPHER_mode(cipher_) == EVP_CIPH_ECB_MODE && iv_len == 0)) { + !(EVP_CIPHER_mode(cipher_) == EVP_CIPH_ECB_MODE && iv_len == 0) && + !(EVP_CIPHER_mode(cipher_) == EVP_CIPH_GCM_MODE) && iv_len > 0) { return env()->ThrowError("Invalid IV length"); } + EVP_CIPHER_CTX_init(&ctx_); const bool encrypt = (kind_ == kCipher); EVP_CipherInit_ex(&ctx_, cipher_, nullptr, nullptr, nullptr, encrypt); + + /* Set IV length. Only required if GCM cipher and IV is not default iv. */ + if (EVP_CIPHER_mode(cipher_) == EVP_CIPH_GCM_MODE && + iv_len != EVP_CIPHER_iv_length(cipher_)) { + if (!EVP_CIPHER_CTX_ctrl(&ctx_, EVP_CTRL_GCM_SET_IVLEN, iv_len, nullptr)) { + EVP_CIPHER_CTX_cleanup(&ctx_); + return env()->ThrowError("Invalid IV length"); + } + } + if (!EVP_CIPHER_CTX_set_key_length(&ctx_, key_len)) { EVP_CIPHER_CTX_cleanup(&ctx_); return env()->ThrowError("Invalid key length"); diff --git a/test/parallel/test-crypto-authenticated.js b/test/parallel/test-crypto-authenticated.js index 02dab76a91b174..506862d1bd323a 100644 --- a/test/parallel/test-crypto-authenticated.js +++ b/test/parallel/test-crypto-authenticated.js @@ -1,12 +1,12 @@ 'use strict'; -var common = require('../common'); -var assert = require('assert'); +const common = require('../common'); +const assert = require('assert'); if (!common.hasCrypto) { common.skip('missing crypto'); return; } -var crypto = require('crypto'); +const crypto = require('crypto'); crypto.DEFAULT_ENCODING = 'buffer'; @@ -16,38 +16,295 @@ crypto.DEFAULT_ENCODING = 'buffer'; // !NEVER USE STATIC IVs IN REAL LIFE! // -var TEST_CASES = [ +const TEST_CASES = [ { algo: 'aes-128-gcm', key: '6970787039613669314d623455536234', iv: '583673497131313748307652', plain: 'Hello World!', - ct: '4BE13896F64DFA2C2D0F2C76', - tag: '272B422F62EB545EAA15B5FF84092447', tampered: false }, + ct: '4be13896f64dfa2c2d0f2c76', + tag: '272b422f62eb545eaa15b5ff84092447', tampered: false }, { algo: 'aes-128-gcm', key: '6970787039613669314d623455536234', iv: '583673497131313748307652', plain: 'Hello World!', - ct: '4BE13896F64DFA2C2D0F2C76', aad: '000000FF', - tag: 'BA2479F66275665A88CB7B15F43EB005', tampered: false }, + ct: '4be13896f64dfa2c2d0f2c76', aad: '000000FF', + tag: 'ba2479f66275665a88cb7b15f43eb005', tampered: false }, { algo: 'aes-128-gcm', key: '6970787039613669314d623455536234', iv: '583673497131313748307652', plain: 'Hello World!', - ct: '4BE13596F64DFA2C2D0FAC76', - tag: '272B422F62EB545EAA15B5FF84092447', tampered: true }, + ct: '4be13596f64dfa2c2d0fac76', + tag: '272b422f62eb545eaa15b5ff84092447', tampered: true }, { algo: 'aes-256-gcm', key: '337a54767a7233703637564336316a6d56353472495975313534357834546c59', iv: '36306950306836764a6f4561', plain: 'Hello node.js world!', - ct: '58E62CFE7B1D274111A82267EBB93866E72B6C2A', - tag: '9BB44F663BADABACAE9720881FB1EC7A', tampered: false }, + ct: '58e62cfe7b1d274111a82267ebb93866e72b6c2a', + tag: '9bb44f663badabacae9720881fb1ec7a', tampered: false }, { algo: 'aes-256-gcm', key: '337a54767a7233703637564336316a6d56353472495975313534357834546c59', iv: '36306950306836764a6f4561', plain: 'Hello node.js world!', - ct: '58E62CFF7B1D274011A82267EBB93866E72B6C2B', - tag: '9BB44F663BADABACAE9720881FB1EC7A', tampered: true }, + ct: '58e62cff7b1d274011a82267ebb93866e72b6c2b', + tag: '9bb44f663badabacae9720881fb1ec7a', tampered: true }, { algo: 'aes-192-gcm', key: '1ed2233fa2223ef5d7df08546049406c7305220bca40d4c9', iv: '0e1791e9db3bd21a9122c416', plain: 'Hello node.js world!', password: 'very bad password', aad: '63616c76696e', - ct: 'DDA53A4059AA17B88756984995F7BBA3C636CC44', - tag: 'D2A35E5C611E5E3D2258360241C5B045', tampered: false } + ct: 'dda53a4059aa17b88756984995f7bba3c636cc44', + tag: 'd2a35e5c611e5e3d2258360241c5b045', tampered: false }, + + // Following test cases are from + // http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/ + // proposedmodes/gcm/gcm-revised-spec.pdf + + // Test case 1 + { algo: 'aes-128-gcm', + key: '00000000000000000000000000000000', + iv: '000000000000000000000000', + plain: '', + plainIsHex: false, + ct: '', + tag: '58e2fccefa7e3061367f1d57a4e7455a', tampered: false }, + + // Test case 2 + { algo: 'aes-128-gcm', + key: '00000000000000000000000000000000', + iv: '000000000000000000000000', + plain: '00000000000000000000000000000000', + plainIsHex: true, + ct: '0388dace60b6a392f328c2b971b2fe78', + tag: 'ab6e47d42cec13bdf53a67b21257bddf', tampered: false }, + + // Test case 3 + { algo: 'aes-128-gcm', + key: 'feffe9928665731c6d6a8f9467308308', + iv: 'cafebabefacedbaddecaf888', + plain: 'd9313225f88406e5a55909c5aff5269a' + + '86a7a9531534f7da2e4c303d8a318a721' + + 'c3c0c95956809532fcf0e2449a6b525b1' + + '6aedf5aa0de657ba637b391aafd255', + plainIsHex: true, + ct: '42831ec2217774244b7221b784d0d49c' + + 'e3aa212f2c02a4e035c17e2329aca12e2' + + '1d514b25466931c7d8f6a5aac84aa051b' + + 'a30b396a0aac973d58e091473f5985', + tag: '4d5c2af327cd64a62cf35abd2ba6fab4', tampered: false }, + + // Test case 4 + { algo: 'aes-128-gcm', + key: 'feffe9928665731c6d6a8f9467308308', + iv: 'cafebabefacedbaddecaf888', + plain: 'd9313225f88406e5a55909c5aff5269a' + + '86a7a9531534f7da2e4c303d8a318a721' + + 'c3c0c95956809532fcf0e2449a6b525b16' + + 'aedf5aa0de657ba637b39', + aad: 'feedfacedeadbeeffeedfacedeadbeefabaddad2', + plainIsHex: true, + ct: '42831ec2217774244b7221b784d0d49c' + + 'e3aa212f2c02a4e035c17e2329aca12e2' + + '1d514b25466931c7d8f6a5aac84aa051b' + + 'a30b396a0aac973d58e091', + tag: '5bc94fbc3221a5db94fae95ae7121a47', tampered: false }, + + // Test case 5, 8 byte IV + { algo: 'aes-128-gcm', + key: 'feffe9928665731c6d6a8f9467308308', + iv: 'cafebabefacedbad', + plain: 'd9313225f88406e5a55909c5aff5269a' + + '86a7a9531534f7da2e4c303d8a318a72' + + '1c3c0c95956809532fcf0e2449a6b525' + + 'b16aedf5aa0de657ba637b39', + aad: 'feedfacedeadbeeffeedfacedeadbeef' + + 'abaddad2', + plainIsHex: true, + ct: '61353b4c2806934a777ff51fa22a4755' + + '699b2a714fcdc6f83766e5f97b6c7423' + + '73806900e49f24b22b097544d4896b42' + + '4989b5e1ebac0f07c23f4598', + tag: '3612d2e79e3b0785561be14aaca2fccb', tampered: false }, + + // Test case 6, 60 byte IV + { algo: 'aes-128-gcm', + key: 'feffe9928665731c6d6a8f9467308308', + iv: '9313225DF88406E555909C5AFF5269AA' + + '6A7A9538534F7DA1E4C303D2A318A728' + + 'C3C0C95156809539FCF0E2429A6B52541' + + '6AEDBF5A0DE6A57A637B39B', + plain: 'd9313225f88406e5a55909c5aff5269a' + + '86a7a9531534f7da2e4c303d8a318a72' + + '1c3c0c95956809532fcf0e2449a6b525' + + 'b16aedf5aa0de657ba637b39', + aad: 'feedfacedeadbeeffeedfacedeadbeefabaddad2', + plainIsHex: true, + ct: '8ce24998625615b603a033aca13fb894' + + 'be9112a5c3a211a8ba262a3cca7e2ca7' + + '01e4a9a4fba43c90ccdcb281d48c7c6f' + + 'd62875d2aca417034c34aee5', + tag: '619cc5aefffe0bfa462af43c1699d050', tampered: false }, + + // Test case 7 + { algo: 'aes-192-gcm', + key: '000000000000000000000000000000000000000000000000', + iv: '000000000000000000000000', + plain: '', + plainIsHex: false, + ct: '', + tag: 'cd33b28ac773f74ba00ed1f312572435', tampered: false }, + + // Test case 8 + { algo: 'aes-192-gcm', + key: '000000000000000000000000000000000000000000000000', + iv: '000000000000000000000000', + plain: '00000000000000000000000000000000', + plainIsHex: true, + ct: '98e7247c07f0fe411c267e4384b0f600', + tag: '2ff58d80033927ab8ef4d4587514f0fb', tampered: false }, + + // Test case 9 + { algo: 'aes-192-gcm', + key: 'feffe9928665731c6d6a8f9467308308feffe9928665731c', + iv: 'cafebabefacedbaddecaf888', + plain: 'd9313225f88406e5a55909c5aff5269a' + + '86a7a9531534f7da2e4c303d8a318a72' + + '1c3c0c95956809532fcf0e2449a6b525' + + 'b16aedf5aa0de657ba637b391aafd255', + plainIsHex: true, + ct: '3980ca0b3c00e841eb06fac4872a2757' + + '859e1ceaa6efd984628593b40ca1e19c' + + '7d773d00c144c525ac619d18c84a3f47' + + '18e2448b2fe324d9ccda2710acade256', + tag: '9924a7c8587336bfb118024db8674a14', tampered: false }, + + // Test case 10 + { algo: 'aes-192-gcm', + key: 'feffe9928665731c6d6a8f9467308308feffe9928665731c', + iv: 'cafebabefacedbaddecaf888', + plain: 'd9313225f88406e5a55909c5aff5269a' + + '86a7a9531534f7da2e4c303d8a318a72' + + '1c3c0c95956809532fcf0e2449a6b525' + + 'b16aedf5aa0de657ba637b39', + aad: 'feedfacedeadbeeffeedfacedeadbeefabaddad2', + plainIsHex: true, + ct: '3980ca0b3c00e841eb06fac4872a2757' + + '859e1ceaa6efd984628593b40ca1e19c' + + '7d773d00c144c525ac619d18c84a3f47' + + '18e2448b2fe324d9ccda2710', + tag: '2519498e80f1478f37ba55bd6d27618c', tampered: false }, + + // Test case 11 + { algo: 'aes-192-gcm', + key: 'feffe9928665731c6d6a8f9467308308feffe9928665731c', + iv: 'cafebabefacedbad', + plain: 'd9313225f88406e5a55909c5aff5269a' + + '86a7a9531534f7da2e4c303d8a318a72' + + '1c3c0c95956809532fcf0e2449a6b525' + + 'b16aedf5aa0de657ba637b39', + aad: 'feedfacedeadbeeffeedfacedeadbeefabaddad2', + plainIsHex: true, + ct: '0f10f599ae14a154ed24b36e25324db8' + + 'c566632ef2bbb34f8347280fc4507057' + + 'fddc29df9a471f75c66541d4d4dad1c9' + + 'e93a19a58e8b473fa0f062f7', + tag: '65dcc57fcf623a24094fcca40d3533f8', tampered: false }, + + // Test case 12, 60 byte IV + { algo: 'aes-192-gcm', + key: 'feffe9928665731c6d6a8f9467308308feffe9928665731c', + iv: '9313225df88406e555909c5aff5269aa' + + '6a7a9538534f7da1e4c303d2a318a728' + + 'c3c0c95156809539fcf0e2429a6b5254' + + '16aedbf5a0de6a57a637b39b', + plain: 'd9313225f88406e5a55909c5aff5269a' + + '86a7a9531534f7da2e4c303d8a318a72' + + '1c3c0c95956809532fcf0e2449a6b525' + + 'b16aedf5aa0de657ba637b39', + aad: 'feedfacedeadbeeffeedfacedeadbeefabaddad2', + plainIsHex: true, + ct: 'd27e88681ce3243c4830165a8fdcf9ff' + + '1de9a1d8e6b447ef6ef7b79828666e45' + + '81e79012af34ddd9e2f037589b292db3' + + 'e67c036745fa22e7e9b7373b', + tag: 'dcf566ff291c25bbb8568fc3d376a6d9', tampered: false }, + + // Test case 13 + { algo: 'aes-256-gcm', + key: '0000000000000000000000000000000000000000000000000000000000000000', + iv: '000000000000000000000000', + plain: '', + plainIsHex: false, + ct: '', + tag: '530f8afbc74536b9a963b4f1c4cb738b', tampered: false }, + + // Test case 14 + { algo: 'aes-256-gcm', + key: '0000000000000000000000000000000000000000000000000000000000000000', + iv: '000000000000000000000000', + plain: '00000000000000000000000000000000', + plainIsHex: true, + ct: 'cea7403d4d606b6e074ec5d3baf39d18', + tag: 'd0d1c8a799996bf0265b98b5d48ab919', tampered: false }, + + // Test case 15 + { algo: 'aes-256-gcm', + key: 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', + iv: 'cafebabefacedbaddecaf888', + plain: 'd9313225f88406e5a55909c5aff5269a' + + '86a7a9531534f7da2e4c303d8a318a72' + + '1c3c0c95956809532fcf0e2449a6b525' + + 'b16aedf5aa0de657ba637b391aafd255', + plainIsHex: true, + ct: '522dc1f099567d07f47f37a32a84427d' + + '643a8cdcbfe5c0c97598a2bd2555d1aa' + + '8cb08e48590dbb3da7b08b1056828838' + + 'c5f61e6393ba7a0abcc9f662898015ad', + tag: 'b094dac5d93471bdec1a502270e3cc6c', tampered: false }, + + // Test case 16 + { algo: 'aes-256-gcm', + key: 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', + iv: 'cafebabefacedbaddecaf888', + plain: 'd9313225f88406e5a55909c5aff5269a' + + '86a7a9531534f7da2e4c303d8a318a72' + + '1c3c0c95956809532fcf0e2449a6b525' + + 'b16aedf5aa0de657ba637b39', + aad: 'feedfacedeadbeeffeedfacedeadbeefabaddad2', + plainIsHex: true, + ct: '522dc1f099567d07f47f37a32a84427d' + + '643a8cdcbfe5c0c97598a2bd2555d1aa' + + '8cb08e48590dbb3da7b08b1056828838' + + 'c5f61e6393ba7a0abcc9f662', + tag: '76fc6ece0f4e1768cddf8853bb2d551b', tampered: false }, + + // Test case 17, 8 byte IV + { algo: 'aes-256-gcm', + key: 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', + iv: 'cafebabefacedbad', + plain: 'd9313225f88406e5a55909c5aff5269a' + + '86a7a9531534f7da2e4c303d8a318a72' + + '1c3c0c95956809532fcf0e2449a6b525' + + 'b16aedf5aa0de657ba637b39', + aad: 'feedfacedeadbeeffeedfacedeadbeefabaddad2', + plainIsHex: true, + ct: 'c3762df1ca787d32ae47c13bf19844cb' + + 'af1ae14d0b976afac52ff7d79bba9de0' + + 'feb582d33934a4f0954cc2363bc73f78' + + '62ac430e64abe499f47c9b1f', + tag: '3a337dbf46a792c45e454913fe2ea8f2', tampered: false }, + + // Test case 18, 60 byte IV + { algo: 'aes-256-gcm', + key: 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', + iv: '9313225df88406e555909c5aff5269aa' + + '6a7a9538534f7da1e4c303d2a318a728' + + 'c3c0c95156809539fcf0e2429a6b5254' + + '16aedbf5a0de6a57a637b39b', + plain: 'd9313225f88406e5a55909c5aff5269a' + + '86a7a9531534f7da2e4c303d8a318a72' + + '1c3c0c95956809532fcf0e2449a6b525' + + 'b16aedf5aa0de657ba637b39', + aad: 'feedfacedeadbeeffeedfacedeadbeefabaddad2', + plainIsHex: true, + ct: '5a8def2f0c9e53f1f75d7853659e2a20' + + 'eeb2b22aafde6419a058ab4f6f746bf4' + + '0fc0c3b780f244452da3ebf1c5d82cde' + + 'a2418997200ef82e44ae7e3f', + tag: 'a44a8266ee1c8eb0c8b5d4cf5ae9f19a', tampered: false }, ]; var ciphers = crypto.getCiphers(); @@ -60,18 +317,26 @@ for (var i in TEST_CASES) { continue; } + if (common.hasFipsCrypto && test.iv.length < 24) { + console.log('1..0 # Skipped: IV len < 12 bytes unsupported in FIPS mode'); + continue; + } + (function() { var encrypt = crypto.createCipheriv(test.algo, Buffer.from(test.key, 'hex'), Buffer.from(test.iv, 'hex')); if (test.aad) encrypt.setAAD(Buffer.from(test.aad, 'hex')); - var hex = encrypt.update(test.plain, 'ascii', 'hex'); + + var inputEncoding = test.plainIsHex ? 'hex' : 'ascii'; + var hex = encrypt.update(test.plain, inputEncoding, 'hex'); hex += encrypt.final('hex'); + var auth_tag = encrypt.getAuthTag(); // only test basic encryption run if output is marked as tampered. if (!test.tampered) { - assert.equal(hex.toUpperCase(), test.ct); - assert.equal(auth_tag.toString('hex').toUpperCase(), test.tag); + assert.equal(hex, test.ct); + assert.equal(auth_tag.toString('hex'), test.tag); } })(); @@ -81,9 +346,12 @@ for (var i in TEST_CASES) { decrypt.setAuthTag(Buffer.from(test.tag, 'hex')); if (test.aad) decrypt.setAAD(Buffer.from(test.aad, 'hex')); - var msg = decrypt.update(test.ct, 'hex', 'ascii'); + + var outputEncoding = test.plainIsHex ? 'hex' : 'ascii'; + + var msg = decrypt.update(test.ct, 'hex', outputEncoding); if (!test.tampered) { - msg += decrypt.final('ascii'); + msg += decrypt.final(outputEncoding); assert.equal(msg, test.plain); } else { // assert that final throws if input data could not be verified! @@ -106,8 +374,8 @@ for (var i in TEST_CASES) { var auth_tag = encrypt.getAuthTag(); // only test basic encryption run if output is marked as tampered. if (!test.tampered) { - assert.equal(hex.toUpperCase(), test.ct); - assert.equal(auth_tag.toString('hex').toUpperCase(), test.tag); + assert.equal(hex, test.ct); + assert.equal(auth_tag.toString('hex'), test.tag); } } })(); @@ -174,4 +442,15 @@ for (var i in TEST_CASES) { Buffer.from(test.key, 'hex'), Buffer.from(test.iv, 'hex')); assert.throws(function() { decrypt.getAuthTag(); }, / state/); })(); + + (function() { + // trying to create cipher with incorrect IV length + assert.throws(function() { + crypto.createCipheriv( + test.algo, + Buffer.from(test.key, 'hex'), + Buffer.alloc(0) + ); + }, /Invalid IV length/); + })(); } From 84dd526f5110a50b31c364aa6beb5fffea8fc037 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius <daniel.bevenius@gmail.com> Date: Mon, 20 Jun 2016 21:36:32 +0200 Subject: [PATCH 060/131] src: renaming ares_task struct to node_ares_task This commit attempts to fix one of the items in https://github.com/nodejs/node/issues/4641, which was to remove a TODO comment from env.h regarding the naming of the ares_task_t struct. Also, the struct ares_task_list was renamed to node_ares_task_list following the same reasoning that is does not belong to the c-ares API. PR-URL: https://github.com/nodejs/node/pull/7345 Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Conflicts: src/env.h --- src/cares_wrap.cc | 24 ++++++++++++------------ src/env-inl.h | 2 +- src/env.h | 12 +++++------- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index 91d139ac9cd236..88ce802dfec30e 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -91,7 +91,7 @@ static void NewQueryReqWrap(const FunctionCallbackInfo<Value>& args) { } -static int cmp_ares_tasks(const ares_task_t* a, const ares_task_t* b) { +static int cmp_ares_tasks(const node_ares_task* a, const node_ares_task* b) { if (a->sock < b->sock) return -1; if (a->sock > b->sock) @@ -100,7 +100,7 @@ static int cmp_ares_tasks(const ares_task_t* a, const ares_task_t* b) { } -RB_GENERATE_STATIC(ares_task_list, ares_task_t, node, cmp_ares_tasks) +RB_GENERATE_STATIC(node_ares_task_list, node_ares_task, node, cmp_ares_tasks) @@ -114,7 +114,7 @@ static void ares_timeout(uv_timer_t* handle) { static void ares_poll_cb(uv_poll_t* watcher, int status, int events) { - ares_task_t* task = ContainerOf(&ares_task_t::poll_watcher, watcher); + node_ares_task* task = ContainerOf(&node_ares_task::poll_watcher, watcher); Environment* env = task->env; /* Reset the idle timer */ @@ -135,15 +135,15 @@ static void ares_poll_cb(uv_poll_t* watcher, int status, int events) { static void ares_poll_close_cb(uv_handle_t* watcher) { - ares_task_t* task = ContainerOf(&ares_task_t::poll_watcher, + node_ares_task* task = ContainerOf(&node_ares_task::poll_watcher, reinterpret_cast<uv_poll_t*>(watcher)); free(task); } -/* Allocates and returns a new ares_task_t */ -static ares_task_t* ares_task_create(Environment* env, ares_socket_t sock) { - ares_task_t* task = static_cast<ares_task_t*>(malloc(sizeof(*task))); +/* Allocates and returns a new node_ares_task */ +static node_ares_task* ares_task_create(Environment* env, ares_socket_t sock) { + node_ares_task* task = static_cast<node_ares_task*>(malloc(sizeof(*task))); if (task == nullptr) { /* Out of memory. */ @@ -169,11 +169,11 @@ static void ares_sockstate_cb(void* data, int read, int write) { Environment* env = static_cast<Environment*>(data); - ares_task_t* task; + node_ares_task* task; - ares_task_t lookup_task; + node_ares_task lookup_task; lookup_task.sock = sock; - task = RB_FIND(ares_task_list, env->cares_task_list(), &lookup_task); + task = RB_FIND(node_ares_task_list, env->cares_task_list(), &lookup_task); if (read || write) { if (!task) { @@ -194,7 +194,7 @@ static void ares_sockstate_cb(void* data, return; } - RB_INSERT(ares_task_list, env->cares_task_list(), task); + RB_INSERT(node_ares_task_list, env->cares_task_list(), task); } /* This should never fail. If it fails anyway, the query will eventually */ @@ -210,7 +210,7 @@ static void ares_sockstate_cb(void* data, CHECK(task && "When an ares socket is closed we should have a handle for it"); - RB_REMOVE(ares_task_list, env->cares_task_list(), task); + RB_REMOVE(node_ares_task_list, env->cares_task_list(), task); uv_close(reinterpret_cast<uv_handle_t*>(&task->poll_watcher), ares_poll_close_cb); diff --git a/src/env-inl.h b/src/env-inl.h index 97e1ba8f764ac8..db55b9de8a7627 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -428,7 +428,7 @@ inline ares_channel* Environment::cares_channel_ptr() { return &cares_channel_; } -inline ares_task_list* Environment::cares_task_list() { +inline node_ares_task_list* Environment::cares_task_list() { return &cares_task_list_; } diff --git a/src/env.h b/src/env.h index 4c310c8831fcf8..4c38ddefe8cd1d 100644 --- a/src/env.h +++ b/src/env.h @@ -291,16 +291,14 @@ namespace node { class Environment; -// TODO(bnoordhuis) Rename struct, the ares_ prefix implies it's part -// of the c-ares API while the _t suffix implies it's a typedef. -struct ares_task_t { +struct node_ares_task { Environment* env; ares_socket_t sock; uv_poll_t poll_watcher; - RB_ENTRY(ares_task_t) node; + RB_ENTRY(node_ares_task) node; }; -RB_HEAD(ares_task_list, ares_task_t); +RB_HEAD(node_ares_task_list, node_ares_task); class Environment { public: @@ -472,7 +470,7 @@ class Environment { inline uv_timer_t* cares_timer_handle(); inline ares_channel cares_channel(); inline ares_channel* cares_channel_ptr(); - inline ares_task_list* cares_task_list(); + inline node_ares_task_list* cares_task_list(); inline bool using_domains() const; inline void set_using_domains(bool value); @@ -588,7 +586,7 @@ class Environment { const uint64_t timer_base_; uv_timer_t cares_timer_handle_; ares_channel cares_channel_; - ares_task_list cares_task_list_; + node_ares_task_list cares_task_list_; bool using_domains_; bool printed_error_; bool trace_sync_io_; From c39f6c02041cc73fff6386d8b03a0897e9330938 Mon Sep 17 00:00:00 2001 From: Lance Ball <lball@redhat.com> Date: Wed, 22 Jun 2016 13:07:59 -0400 Subject: [PATCH 061/131] repl: Enable tab completion for global properties When `useGlobal` is false, tab completion in the repl does not enumerate global properties. Instead of just setting these properties blindly on the global context, e.g. context[prop] = global[prop] Use `Object.defineProperty` and the property descriptor found on `global` for the new property in `context`. Also addresses a previously unnoticed issue where `console` is writable when `useGlobal` is false. If the binary has been built with `./configure --without-intl` then the `Intl` builtin type will not be available in a repl runtime. Check for this in the test. Fixes: https://github.com/nodejs/node/issues/7353 PR-URL: https://github.com/nodejs/node/pull/7369 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> --- lib/repl.js | 34 +++++++++++++++++-------- test/parallel/test-repl-console.js | 3 +++ test/parallel/test-repl-context.js | 26 +++++++++++++++++++ test/parallel/test-repl-tab-complete.js | 16 ++++++++++++ 4 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 test/parallel/test-repl-context.js diff --git a/lib/repl.js b/lib/repl.js index 18c81c438dee91..b1e56b0fb5b70c 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -39,6 +39,16 @@ const debug = util.debuglog('repl'); const parentModule = module; const replMap = new WeakMap(); +const GLOBAL_OBJECT_PROPERTIES = ['NaN', 'Infinity', 'undefined', + 'eval', 'parseInt', 'parseFloat', 'isNaN', 'isFinite', 'decodeURI', + 'decodeURIComponent', 'encodeURI', 'encodeURIComponent', + 'Object', 'Function', 'Array', 'String', 'Boolean', 'Number', + 'Date', 'RegExp', 'Error', 'EvalError', 'RangeError', + 'ReferenceError', 'SyntaxError', 'TypeError', 'URIError', + 'Math', 'JSON']; +const GLOBAL_OBJECT_PROPERTY_MAP = {}; +GLOBAL_OBJECT_PROPERTIES.forEach((p) => GLOBAL_OBJECT_PROPERTY_MAP[p] = p); + try { // hack for require.resolve("./relative") to work properly. module.filename = path.resolve('repl'); @@ -582,10 +592,20 @@ REPLServer.prototype.createContext = function() { context = global; } else { context = vm.createContext(); - for (var i in global) context[i] = global[i]; - context.console = new Console(this.outputStream); context.global = context; - context.global.global = context; + const _console = new Console(this.outputStream); + Object.defineProperty(context, 'console', { + configurable: true, + enumerable: true, + get: () => _console + }); + Object.getOwnPropertyNames(global).filter((name) => { + if (name === 'console' || name === 'global') return false; + return GLOBAL_OBJECT_PROPERTY_MAP[name] === undefined; + }).forEach((name) => { + Object.defineProperty(context, name, + Object.getOwnPropertyDescriptor(global, name)); + }); } const module = new Module('<repl>'); @@ -1052,13 +1072,7 @@ REPLServer.prototype.memory = function memory(cmd) { function addStandardGlobals(completionGroups, filter) { // Global object properties // (http://www.ecma-international.org/publications/standards/Ecma-262.htm) - completionGroups.push(['NaN', 'Infinity', 'undefined', - 'eval', 'parseInt', 'parseFloat', 'isNaN', 'isFinite', 'decodeURI', - 'decodeURIComponent', 'encodeURI', 'encodeURIComponent', - 'Object', 'Function', 'Array', 'String', 'Boolean', 'Number', - 'Date', 'RegExp', 'Error', 'EvalError', 'RangeError', - 'ReferenceError', 'SyntaxError', 'TypeError', 'URIError', - 'Math', 'JSON']); + completionGroups.push(GLOBAL_OBJECT_PROPERTIES); // Common keywords. Exclude for completion on the empty string, b/c // they just get in the way. if (filter) { diff --git a/test/parallel/test-repl-console.js b/test/parallel/test-repl-console.js index 609822703fef1e..98cb958cac8e68 100644 --- a/test/parallel/test-repl-console.js +++ b/test/parallel/test-repl-console.js @@ -18,3 +18,6 @@ assert(r.context.console); // ensure that the repl console instance is not the global one assert.notStrictEqual(r.context.console, console); + +// ensure that the repl console instance does not have a setter +assert.throws(() => r.context.console = 'foo', TypeError); diff --git a/test/parallel/test-repl-context.js b/test/parallel/test-repl-context.js new file mode 100644 index 00000000000000..1b319a036fa256 --- /dev/null +++ b/test/parallel/test-repl-context.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const repl = require('repl'); + +// Create a dummy stream that does nothing +const stream = new common.ArrayStream(); + +// Test when useGlobal is false +testContext(repl.start({ + input: stream, + output: stream, + useGlobal: false +})); + +function testContext(repl) { + const context = repl.createContext(); + // ensure that the repl context gets its own "console" instance + assert(context.console instanceof require('console').Console); + + // ensure that the repl's global property is the context + assert(context.global === context); + + // ensure that the repl console instance does not have a setter + assert.throws(() => context.console = 'foo'); +} diff --git a/test/parallel/test-repl-tab-complete.js b/test/parallel/test-repl-tab-complete.js index d92377efebf791..3d32d8e0ce0019 100644 --- a/test/parallel/test-repl-tab-complete.js +++ b/test/parallel/test-repl-tab-complete.js @@ -267,3 +267,19 @@ putIn.run(['.clear']); testMe.complete('.b', common.mustCall((error, data) => { assert.deepStrictEqual(data, [['break'], 'b']); })); + +const testNonGlobal = repl.start({ + input: putIn, + output: putIn, + useGlobal: false +}); + +const builtins = [['Infinity', '', 'Int16Array', 'Int32Array', + 'Int8Array'], 'I']; + +if (typeof Intl === 'object') { + builtins[0].push('Intl'); +} +testNonGlobal.complete('I', common.mustCall((error, data) => { + assert.deepStrictEqual(data, builtins); +})); From 40211e80f24697c5282022d5b5e35f9def322bdd Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Fri, 24 Jun 2016 15:37:38 -0700 Subject: [PATCH 062/131] assert: remove unneeded arguments special handling Remove special handling when asserting on a pair of arguments objects. The code being removed will only run if both `expected` and `actual` are arguments objects. Given that situation, the subsequent code for handling everything else works just fine. Tests added to confirm expected behavior. This came about while trying to improve test coverage. The segment of code removed had no test coverage. I was unable to write a test that would both exercise the code and fail if the code was removed. Further examination indicated that this was because the special handling was not needed. PR-URL: https://github.com/nodejs/node/pull/7413 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- lib/assert.js | 6 ------ test/parallel/test-assert.js | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/assert.js b/lib/assert.js index 8955aa8761d7c2..ba316d38ff665f 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -28,7 +28,6 @@ const compare = process.binding('buffer').compare; const util = require('util'); const Buffer = require('buffer').Buffer; -const pSlice = Array.prototype.slice; const pToString = (obj) => Object.prototype.toString.call(obj); // 1. The assert module provides functions that throw @@ -223,11 +222,6 @@ function objEquiv(a, b, strict, actualVisitedObjects) { const bIsArgs = isArguments(b); if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) return false; - if (aIsArgs) { - a = pSlice.call(a); - b = pSlice.call(b); - return _deepEqual(a, b, strict); - } const ka = Object.keys(a); const kb = Object.keys(b); var key, i; diff --git a/test/parallel/test-assert.js b/test/parallel/test-assert.js index b2e2ea4debfbc5..34e7b643442138 100644 --- a/test/parallel/test-assert.js +++ b/test/parallel/test-assert.js @@ -59,7 +59,7 @@ assert.throws(makeBlock(a.strictEqual, null, undefined), assert.doesNotThrow(makeBlock(a.notStrictEqual, 2, '2'), 'notStrictEqual(2, \'2\')'); -// deepEquals joy! +// deepEqual joy! // 7.2 assert.doesNotThrow(makeBlock(a.deepEqual, new Date(2000, 3, 14), new Date(2000, 3, 14)), @@ -409,6 +409,20 @@ var args = (function() { return arguments; })(); a.throws(makeBlock(a.deepEqual, [], args)); a.throws(makeBlock(a.deepEqual, args, [])); +// more checking that arguments objects are handled correctly +{ + const returnArguments = function() { return arguments; }; + + const someArgs = returnArguments('a'); + const sameArgs = returnArguments('a'); + const diffArgs = returnArguments('b'); + + a.throws(makeBlock(a.deepEqual, someArgs, ['a'])); + a.throws(makeBlock(a.deepEqual, ['a'], someArgs)); + a.throws(makeBlock(a.deepEqual, someArgs, {'0': 'a'})); + a.throws(makeBlock(a.deepEqual, someArgs, diffArgs)); + a.doesNotThrow(makeBlock(a.deepEqual, someArgs, sameArgs)); +} var circular = {y: 1}; circular.x = circular; From 225f3b9f346e4c34972ac951ead0031263f3a56c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= <targos@protonmail.com> Date: Thu, 23 Jun 2016 10:19:34 +0200 Subject: [PATCH 063/131] deps: update v8_inspector Pick up latest commit of v8_inspector and inspector_protocol. This brings back compatibility with V8 5.1. PR-URL: https://github.com/nodejs/node/pull/7385 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Ali Ijaz Sheikh <ofrobots@google.com> --- deps/v8_inspector/platform/v8_inspector/V8Compat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/v8_inspector/platform/v8_inspector/V8Compat.h b/deps/v8_inspector/platform/v8_inspector/V8Compat.h index 17d104f7736ea7..30492b3af7a8e9 100644 --- a/deps/v8_inspector/platform/v8_inspector/V8Compat.h +++ b/deps/v8_inspector/platform/v8_inspector/V8Compat.h @@ -7,7 +7,7 @@ #include <v8.h> -#if V8_MAJOR_VERSION < 5 || (V8_MAJOR_VERSION == 5 && V8_MINOR_VERSION < 2) +#if V8_MAJOR_VERSION < 5 || (V8_MAJOR_VERSION == 5 && V8_MINOR_VERSION < 1) namespace v8 { // In standalone V8 inspector this is expected to be noop anyways... From fe580eb578a475593c18c060a13cf4ae059b7987 Mon Sep 17 00:00:00 2001 From: Evan Lucas <evanlucas@me.com> Date: Wed, 22 Jun 2016 23:00:05 -0500 Subject: [PATCH 064/131] inspector: print warning when used This is done to note that this is an experimental feature. PR-URL: https://github.com/nodejs/node/pull/7383 Reviewed-By: Myles Borins <myles.borins@gmail.com> Reviewed-By: Ali Ijaz Sheikh <ofrobots@google.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- src/inspector_agent.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index e727bf01a98225..2d28f96be0b5f2 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -36,6 +36,7 @@ const char DEVTOOLS_HASH[] = "521e5b7e2b7cc66b4006a8a54cb9c4e57494a5ef"; void PrintDebuggerReadyMessage(int port) { fprintf(stderr, "Debugger listening on port %d.\n" + "Warning: This is an experimental feature and could change at any time.\n" "To start debugging, open the following URL in Chrome:\n" " chrome-devtools://devtools/remote/serve_file/" "@%s/inspector.html?" From bc37e6a22dfd02ba8033e233651b4f88b38161e7 Mon Sep 17 00:00:00 2001 From: James M Snell <jasnell@gmail.com> Date: Mon, 23 May 2016 14:12:20 -0700 Subject: [PATCH 065/131] doc: general improvements to timers.md Overall improvements to timers.md documentation, Includes squashed commit from @bengl: doc: add timer classes The timers returned by `setTimeout` and friends are actually instances of `Timeout` and `Immediate`. Documenting them as such, so that the `ref` and `unref` methods can be identified as methods on `Timeout` objects. Sparked by discussion in #5792 PR-URL: https://github.com/nodejs/node/pull/6937 Reviewed-By: Robert Jefe Lindstaedt <robert.lindstaedt@gmail.com> Reviewed-By: Bryan English <bryan@bryanenglish.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> --- doc/api/globals.md | 2 +- doc/api/timers.md | 168 ++++++++++++++++++++++++++++++--------------- 2 files changed, 114 insertions(+), 56 deletions(-) diff --git a/doc/api/globals.md b/doc/api/globals.md index f70c52aae4904e..1bd2dffc5fa4df 100644 --- a/doc/api/globals.md +++ b/doc/api/globals.md @@ -208,7 +208,7 @@ but rather than loading the module, just return the resolved filename. [native addons]: addons.html [timers]: timers.html [`clearImmediate`]: timers.html#timers_clearimmediate_immediateobject -[`clearInterval`]: timers.html#timers_clearinterval_intervalobject +[`clearInterval`]: timers.html#timers_clearinterval_immediateobject [`clearTimeout`]: timers.html#timers_cleartimeout_timeoutobject [`setImmediate`]: timers.html#timers_setimmediate_callback_arg [`setInterval`]: timers.html#timers_setinterval_callback_delay_arg diff --git a/doc/api/timers.md b/doc/api/timers.md index d33bf1dc22b559..29a7a48e5a5a00 100644 --- a/doc/api/timers.md +++ b/doc/api/timers.md @@ -2,90 +2,148 @@ Stability: 3 - Locked -All of the timer functions are globals. You do not need to `require()` -this module in order to use them. +The `timer` module exposes a global API for scheduling functions to +be called at some future period of time. Because the timer functions are +globals, there is no need to call `require('timers')` to use the API. -## clearImmediate(immediateObject) +The timer functions within Node.js implement a similar API as the timers API +provided by Web Browsers but use a different internal implementation that is +built around [the Node.js Event Loop][]. -Stops an `immediateObject`, as created by [`setImmediate`][], from triggering. +## Class: Immediate -## clearInterval(intervalObject) +This object is created internally and is returned from [`setImmediate()`][]. It +can be passed to [`clearImmediate()`][] in order to cancel the scheduled +actions. -Stops an `intervalObject`, as created by [`setInterval`][], from triggering. +## Class: Timeout -## clearTimeout(timeoutObject) +This object is created internally and is returned from [`setTimeout()`][] and +[`setInterval()`][]. It can be passed to [`clearTimeout`][] or +[`clearInterval()`][] (respectively) in order to cancel the scheduled actions. -Prevents a `timeoutObject`, as created by [`setTimeout`][], from triggering. +By default, when a timer is scheduled using either [`setTimeout`] or +[`setInterval()`][], the Node.js event loop will continue running as long as the +timer is active. Each of the `Timeout` objects returned by these functions +export both `timeout.ref()` and `timeout.unref()` functions that can be used to +control this default behavior. -## ref() +### timeout.ref() -If a timer was previously `unref()`d, then `ref()` can be called to explicitly -request the timer hold the program open. If the timer is already `ref`d calling -`ref` again will have no effect. +When called, requests that the Node.js event loop *not* exit so long as the +`Timeout` is active. Calling `timeout.ref()` multiple times will have no effect. -Returns the timer. +*Note*: By default, all `Timeout` objects are "ref'd", making it normally +unnecessary to call `timeout.ref()` unless `timeout.unref()` had been called +previously. -## setImmediate(callback[, arg][, ...]) +Returns a reference to the `Timeout`. -Schedules "immediate" execution of `callback` after I/O events' -callbacks and before timers set by [`setTimeout`][] and [`setInterval`][] are -triggered. Returns an `immediateObject` for possible use with -[`clearImmediate`][]. Additional optional arguments may be passed to the -callback. +### timeout.unref() -Callbacks for immediates are queued in the order in which they were created. -The entire callback queue is processed every event loop iteration. If an -immediate is queued from inside an executing callback, that immediate won't fire -until the next event loop iteration. +When called, the active `Timeout` object will not require the Node.js event loop +to remain active. If there is no other activity keeping the event loop running, +the process may exit before the `Timeout` object's callback is invoked. Calling +`timout.unref()` multiple times will have no effect. -If `callback` is not a function `setImmediate()` will throw immediately. +*Note*: Calling `timout.unref()` creates an internal timer that will wake the +Node.js event loop. Creating too many of these can adversely impact performance +of the Node.js application. -## setInterval(callback, delay[, arg][, ...]) +Returns a reference to the `Timeout`. + +## Scheduling Timers + +A timer in Node.js is an internal construct that calls a given function after +a certain period of time. When a timer's function is called varies depending on +which method was used to create the timer and what other work the Node.js +event loop is doing. + +### setImmediate(callback[, ...arg]) + +* `callback` {Function} The function to call at the end of this turn of + [the Node.js Event Loop] +* `[, ...arg]` Optional arguments to pass when the `callback` is called. + +Schedules the "immediate" execution of the `callback` after I/O events' +callbacks and before timers created using [`setTimeout()`][] and +[`setInterval()`][] are triggered. Returns an `Immediate` for use with +[`clearImmediate()`][]. + +When multiple calls to `setImmediate()` are made, the `callback` functions are +queued for execution in the order in which they are created. The entire callback +queue is processed every event loop iteration. If an immediate timer is queued +from inside an executing callback, that timer will not be triggered until the +next event loop iteration. + +If `callback` is not a function, a [`TypeError`][] will be thrown. + +### setInterval(callback, delay[, ...arg]) + +* `callback` {Function} The function to call when the timer elapses. +* `delay` {number} The number of milliseconds to wait before calling the + `callback`. +* `[, ...arg]` Optional arguments to pass when the `callback` is called. Schedules repeated execution of `callback` every `delay` milliseconds. -Returns a `intervalObject` for possible use with [`clearInterval`][]. Additional -optional arguments may be passed to the callback. +Returns a `Timeout` for use with [`clearInterval()`][]. -To follow browser behavior, when using delays larger than 2147483647 -milliseconds (approximately 25 days) or less than 1, Node.js will use 1 as the -`delay`. +When `delay` is larger than `2147483647` or less than `1`, the `delay` will be +set to `1`. -If `callback` is not a function `setInterval()` will throw immediately. +If `callback` is not a function, a [`TypeError`][] will be thrown. -## setTimeout(callback, delay[, arg][, ...]) +### setTimeout(callback, delay[, ...arg]) + +* `callback` {Function} The function to call when the timer elapses. +* `delay` {number} The number of milliseconds to wait before calling the + `callback`. +* `[, ...arg]` Optional arguments to pass when the `callback` is called. Schedules execution of a one-time `callback` after `delay` milliseconds. -Returns a `timeoutObject` for possible use with [`clearTimeout`][]. Additional -optional arguments may be passed to the callback. +Returns a `Timeout` for use with [`clearTimeout()`][]. -The callback will likely not be invoked in precisely `delay` milliseconds. +The `callback` will likely not be invoked in precisely `delay` milliseconds. Node.js makes no guarantees about the exact timing of when callbacks will fire, nor of their ordering. The callback will be called as close as possible to the time specified. -To follow browser behavior, when using delays larger than 2147483647 -milliseconds (approximately 25 days) or less than 1, the timeout is executed -immediately, as if the `delay` was set to 1. +*Note*: When `delay` is larger than `2147483647` or less than `1`, the `delay` +will be set to `1`. + +If `callback` is not a function, a [`TypeError`][] will be thrown. + +## Cancelling Timers + +The [`setImmediate()`][], [`setInterval()`][], and [`setTimeout()`][] methods +each return objects that represent the scheduled timers. These can be used to +cancel the timer and prevent it from triggering. + +### clearImmediate(immediate) + +* `immediate` {Immediate} An `Immediate` object as returned by + [`setImmediate()`][]. + +Cancels an `Immediate` object created by [`setImmediate()`][]. + +### clearInterval(timeout) + +* `timeout` {Timeout} A `Timeout` object as returned by [`setInterval()`][]. -If `callback` is not a function `setTimeout()` will throw immediately. +Cancels a `Timeout` object created by [`setInterval()`][]. -## unref() +### clearTimeout(timeout) -The opaque value returned by [`setTimeout`][] and [`setInterval`][] also has the -method `timer.unref()` which allows the creation of a timer that is active but -if it is the only item left in the event loop, it won't keep the program -running. If the timer is already `unref`d calling `unref` again will have no -effect. +* `timeout` {Timeout} A `Timeout` object as returned by [`setTimeout()`][]. -In the case of [`setTimeout`][], `unref` creates a separate timer that will -wakeup the event loop, creating too many of these may adversely effect event -loop performance -- use wisely. +Cancels a `Timeout` object created by [`setTimeout()`][]. -Returns the timer. -[`clearImmediate`]: timers.html#timers_clearimmediate_immediateobject -[`clearInterval`]: timers.html#timers_clearinterval_intervalobject -[`clearTimeout`]: timers.html#timers_cleartimeout_timeoutobject -[`setImmediate`]: timers.html#timers_setimmediate_callback_arg -[`setInterval`]: timers.html#timers_setinterval_callback_delay_arg -[`setTimeout`]: timers.html#timers_settimeout_callback_delay_arg +[the Node.js Event Loop]: https://github.com/nodejs/node/blob/master/doc/topics/the-event-loop-timers-and-nexttick.md +[`TypeError`]: errors.html#errors_class_typerror +[`clearImmediate()`]: timers.html#timers_clearimmediate_immediate +[`clearInterval()`]: timers.html#timers_clearinterval_timeout +[`clearTimeout()`]: timers.html#timers_cleartimeout_timeout +[`setImmediate()`]: timers.html#timers_setimmediate_callback_arg +[`setInterval()`]: timers.html#timers_setinterval_callback_delay_arg +[`setTimeout()`]: timers.html#timers_settimeout_callback_delay_arg From 91744aaf00079843c3b079c11798821a83ebd407 Mon Sep 17 00:00:00 2001 From: Stefan Budeanu <stefan@budeanu.com> Date: Sat, 26 Mar 2016 20:17:55 -0400 Subject: [PATCH 066/131] build: configure --shared Add configure flag for building a shared library that can be embedded in other applications (like Electron). Add flags --without-bundled-v8 and --without-v8-platform to control V8 dependencies used. PR-URL: https://github.com/nodejs/node/pull/6994 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Fedor Indutny <fedor@indutny.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Conflicts: src/node.cc --- common.gypi | 8 +++ configure | 28 ++++++++++ node.gyp | 81 ++++++++++++++++++++++------ src/node.cc | 51 +++++++++++++++--- src/node.h | 14 +++-- test/parallel/test-module-version.js | 10 ++++ tools/getmoduleversion.py | 24 +++++++++ tools/getnodeversion.py | 14 +++-- tools/install.py | 18 ++++++- 9 files changed, 212 insertions(+), 36 deletions(-) create mode 100644 test/parallel/test-module-version.js create mode 100644 tools/getmoduleversion.py diff --git a/common.gypi b/common.gypi index 8da603d00f618e..a892c2d6e38f5b 100644 --- a/common.gypi +++ b/common.gypi @@ -11,6 +11,11 @@ 'msvs_multi_core_compile': '0', # we do enable multicore compiles, but not using the V8 way 'python%': 'python', + 'node_shared%': 'false', + 'node_use_v8_platform%': 'true', + 'node_use_bundled_v8%': 'true', + 'node_module_version%': '', + 'node_tag%': '', 'uv_library%': 'static_library', @@ -290,6 +295,9 @@ ], 'ldflags!': [ '-rdynamic' ], }], + [ 'node_shared=="true"', { + 'cflags': [ '-fPIC' ], + }], ], }], [ 'OS=="android"', { diff --git a/configure b/configure index d622a6f0625a18..879c12b496017f 100755 --- a/configure +++ b/configure @@ -24,6 +24,10 @@ from gyp.common import GetFlavor sys.path.insert(0, os.path.join(root_dir, 'tools', 'configure.d')) import nodedownload +# imports in tools/ +sys.path.insert(0, os.path.join(root_dir, 'tools')) +import getmoduleversion + # parse our options parser = optparse.OptionParser() @@ -420,6 +424,26 @@ parser.add_option('--without-inspector', dest='without_inspector', help='disable experimental V8 inspector support') +parser.add_option('--shared', + action='store_true', + dest='shared', + help='compile shared library for embedding node in another project. ' + + '(This mode is not officially supported for regular applications)') + +parser.add_option('--without-v8-platform', + action='store_true', + dest='without_v8_platform', + default=False, + help='do not initialize v8 platform during node.js startup. ' + + '(This mode is not officially supported for regular applications)') + +parser.add_option('--without-bundled-v8', + action='store_true', + dest='without_bundled_v8', + default=False, + help='do not use V8 includes from the bundled deps folder. ' + + '(This mode is not officially supported for regular applications)') + (options, args) = parser.parse_args() # Expand ~ in the install prefix now, it gets written to multiple files. @@ -810,6 +834,10 @@ def configure_node(o): o['variables']['node_target_type'] = 'static_library' o['variables']['node_no_browser_globals'] = b(options.no_browser_globals) + o['variables']['node_shared'] = b(options.shared) + o['variables']['node_use_v8_platform'] = b(not options.without_v8_platform) + o['variables']['node_use_bundled_v8'] = b(not options.without_bundled_v8) + o['variables']['node_module_version'] = int(getmoduleversion.get_version()) if options.linked_module: o['variables']['library_files'] = options.linked_module diff --git a/node.gyp b/node.gyp index c3f591351d52ce..c14f57e94a02f3 100644 --- a/node.gyp +++ b/node.gyp @@ -6,6 +6,10 @@ 'node_use_etw%': 'false', 'node_use_perfctr%': 'false', 'node_no_browser_globals%': 'false', + 'node_use_v8_platform%': 'true', + 'node_use_bundled_v8%': 'true', + 'node_shared%': 'false', + 'node_module_version%': '', 'node_shared_zlib%': 'false', 'node_shared_http_parser%': 'false', 'node_shared_cares%': 'false', @@ -14,7 +18,6 @@ 'node_shared_openssl%': 'false', 'node_v8_options%': '', 'node_enable_v8_vtunejit%': 'false', - 'node_target_type%': 'executable', 'node_core_target_name%': 'node', 'library_files': [ 'lib/internal/bootstrap_node.js', @@ -100,6 +103,13 @@ 'deps/v8/tools/SourceMap.js', 'deps/v8/tools/tickprocessor-driver.js', ], + 'conditions': [ + [ 'node_shared=="true"', { + 'node_target_type%': 'shared_library', + }, { + 'node_target_type%': 'executable', + }], + ], }, 'targets': [ @@ -109,8 +119,6 @@ 'dependencies': [ 'node_js2c#host', - 'deps/v8/tools/gyp/v8.gyp:v8', - 'deps/v8/tools/gyp/v8.gyp:v8_libplatform' ], 'include_dirs': [ @@ -118,7 +126,6 @@ 'tools/msvs/genfiles', 'deps/uv/src/ares', '<(SHARED_INTERMEDIATE_DIR)', # for node_natives.h - 'deps/v8' # include/v8_platform.h ], 'sources': [ @@ -217,6 +224,42 @@ 'conditions': [ + [ 'node_shared=="false"', { + 'msvs_settings': { + 'VCManifestTool': { + 'EmbedManifest': 'true', + 'AdditionalManifestFiles': 'src/res/node.exe.extra.manifest' + } + }, + }, { + 'defines': [ + 'NODE_SHARED_MODE', + ], + 'conditions': [ + [ 'node_module_version!=""', { + 'product_extension': 'so.<(node_module_version)', + }] + ], + }], + [ 'node_use_bundled_v8=="true"', { + 'include_dirs': [ + 'deps/v8', # include/v8_platform.h + ], + + 'dependencies': [ + 'deps/v8/tools/gyp/v8.gyp:v8', + 'deps/v8/tools/gyp/v8.gyp:v8_libplatform' + ], + }], + [ 'node_use_v8_platform=="true"', { + 'defines': [ + 'NODE_USE_V8_PLATFORM=1', + ], + }, { + 'defines': [ + 'NODE_USE_V8_PLATFORM=0', + ], + }], [ 'node_tag!=""', { 'defines': [ 'NODE_TAG="<(node_tag)"' ], }], @@ -245,7 +288,8 @@ 'defines': [ 'NODE_HAVE_SMALL_ICU=1' ], }]], }], - [ 'node_enable_v8_vtunejit=="true" and (target_arch=="x64" or \ + [ 'node_use_bundled_v8=="true" and \ + node_enable_v8_vtunejit=="true" and (target_arch=="x64" or \ target_arch=="ia32" or target_arch=="x32")', { 'defines': [ 'NODE_ENABLE_VTUNE_PROFILING' ], 'dependencies': [ @@ -308,7 +352,7 @@ ], }, 'conditions': [ - ['OS in "linux freebsd"', { + ['OS in "linux freebsd" and node_shared=="false"', { 'ldflags': [ '-Wl,--whole-archive <(PRODUCT_DIR)/<(OPENSSL_PRODUCT)', '-Wl,--no-whole-archive', @@ -395,7 +439,7 @@ [ 'node_no_browser_globals=="true"', { 'defines': [ 'NODE_NO_BROWSER_GLOBALS' ], } ], - [ 'v8_postmortem_support=="true"', { + [ 'node_use_bundled_v8=="true" and v8_postmortem_support=="true"', { 'dependencies': [ 'deps/v8/tools/gyp/v8.gyp:postmortem-metadata' ], 'conditions': [ # -force_load is not applicable for the static library @@ -478,7 +522,7 @@ 'NODE_PLATFORM="sunos"', ], }], - [ 'OS=="freebsd" or OS=="linux"', { + [ '(OS=="freebsd" or OS=="linux") and node_shared=="false"', { 'ldflags': [ '-Wl,-z,noexecstack', '-Wl,--whole-archive <(V8_BASE)', '-Wl,--no-whole-archive' ] @@ -487,12 +531,6 @@ 'ldflags': [ '-Wl,-M,/usr/lib/ld/map.noexstk' ], }], ], - 'msvs_settings': { - 'VCManifestTool': { - 'EmbedManifest': 'true', - 'AdditionalManifestFiles': 'src/res/node.exe.extra.manifest' - } - }, }, # generate ETW header and resource files { @@ -718,8 +756,6 @@ 'deps/http_parser/http_parser.gyp:http_parser', 'deps/gtest/gtest.gyp:gtest', 'deps/uv/uv.gyp:libuv', - 'deps/v8/tools/gyp/v8.gyp:v8', - 'deps/v8/tools/gyp/v8.gyp:v8_libplatform' ], 'include_dirs': [ 'src', @@ -750,7 +786,18 @@ 'src/inspector_socket.cc', 'test/cctest/test_inspector_socket.cc' ] - }] + }], + [ 'node_use_v8_platform=="true"', { + 'dependencies': [ + 'deps/v8/tools/gyp/v8.gyp:v8_libplatform', + ], + }], + [ 'node_use_bundled_v8=="true"', { + 'dependencies': [ + 'deps/v8/tools/gyp/v8.gyp:v8', + 'deps/v8/tools/gyp/v8.gyp:v8_libplatform' + ], + }], ] } ], # end targets diff --git a/src/node.cc b/src/node.cc index 3a31242957a044..8cadf18f8df812 100644 --- a/src/node.cc +++ b/src/node.cc @@ -39,7 +39,9 @@ #include "string_bytes.h" #include "util.h" #include "uv.h" +#if NODE_USE_V8_PLATFORM #include "libplatform/libplatform.h" +#endif // NODE_USE_V8_PLATFORM #include "v8-debug.h" #include "v8-profiler.h" #include "zlib.h" @@ -184,7 +186,42 @@ static uv_async_t dispatch_debug_messages_async; static Mutex node_isolate_mutex; static v8::Isolate* node_isolate; -static v8::Platform* default_platform; + +static struct { +#if NODE_USE_V8_PLATFORM + void Initialize(int thread_pool_size) { + platform_ = v8::platform::CreateDefaultPlatform(thread_pool_size); + V8::InitializePlatform(platform_); + } + + void PumpMessageLoop(Isolate* isolate) { + v8::platform::PumpMessageLoop(platform_, isolate); + } + + void Dispose() { + delete platform_; + platform_ = nullptr; + } + +#if HAVE_INSPECTOR + void StartInspector(Environment *env, int port, bool wait) { + env->inspector_agent()->Start(platform_, port, wait); + } +#endif // HAVE_INSPECTOR + + v8::Platform* platform_; +#else // !NODE_USE_V8_PLATFORM + void Initialize(int thread_pool_size) {} + void PumpMessageLoop(Isolate* isolate) {} + void Dispose() {} +#if HAVE_INSPECTOR + void StartInspector(Environment *env, int port, bool wait) { + env->ThrowError("Node compiled with NODE_USE_V8_PLATFORM=0"); + } +#endif // HAVE_INSPECTOR + +#endif // !NODE_USE_V8_PLATFORM +} v8_platform; #ifdef __POSIX__ static uv_sem_t debug_semaphore; @@ -3736,7 +3773,7 @@ static void StartDebug(Environment* env, bool wait) { CHECK(!debugger_running); #if HAVE_INSPECTOR if (use_inspector) { - env->inspector_agent()->Start(default_platform, inspector_port, wait); + v8_platform.StartInspector(env, inspector_port, wait); debugger_running = true; } else { #endif @@ -4467,11 +4504,11 @@ static void StartNodeInstance(void* arg) { SealHandleScope seal(isolate); bool more; do { - v8::platform::PumpMessageLoop(default_platform, isolate); + v8_platform.PumpMessageLoop(isolate); more = uv_run(env->event_loop(), UV_RUN_ONCE); if (more == false) { - v8::platform::PumpMessageLoop(default_platform, isolate); + v8_platform.PumpMessageLoop(isolate); EmitBeforeExit(env); // Emit `beforeExit` if the loop became alive either after emitting @@ -4537,8 +4574,7 @@ int Start(int argc, char** argv) { V8::SetEntropySource(crypto::EntropySource); #endif - default_platform = v8::platform::CreateDefaultPlatform(v8_thread_pool_size); - V8::InitializePlatform(default_platform); + v8_platform.Initialize(v8_thread_pool_size); V8::Initialize(); int exit_code = 1; @@ -4555,8 +4591,7 @@ int Start(int argc, char** argv) { } V8::Dispose(); - delete default_platform; - default_platform = nullptr; + v8_platform.Dispose(); delete[] exec_argv; exec_argv = nullptr; diff --git a/src/node.h b/src/node.h index 42c5ac59d7ecf2..6d12567f8940a4 100644 --- a/src/node.h +++ b/src/node.h @@ -415,17 +415,23 @@ extern "C" NODE_EXTERN void node_module_register(void* mod); # define NODE_MODULE_EXPORT __attribute__((visibility("default"))) #endif +#ifdef NODE_SHARED_MODE +# define NODE_CTOR_PREFIX +#else +# define NODE_CTOR_PREFIX static +#endif + #if defined(_MSC_VER) #pragma section(".CRT$XCU", read) #define NODE_C_CTOR(fn) \ - static void __cdecl fn(void); \ + NODE_CTOR_PREFIX void __cdecl fn(void); \ __declspec(dllexport, allocate(".CRT$XCU")) \ void (__cdecl*fn ## _)(void) = fn; \ - static void __cdecl fn(void) + NODE_CTOR_PREFIX void __cdecl fn(void) #else #define NODE_C_CTOR(fn) \ - static void fn(void) __attribute__((constructor)); \ - static void fn(void) + NODE_CTOR_PREFIX void fn(void) __attribute__((constructor)); \ + NODE_CTOR_PREFIX void fn(void) #endif #define NODE_MODULE_X(modname, regfunc, priv, flags) \ diff --git a/test/parallel/test-module-version.js b/test/parallel/test-module-version.js new file mode 100644 index 00000000000000..7f96e8bb60f0ac --- /dev/null +++ b/test/parallel/test-module-version.js @@ -0,0 +1,10 @@ +'use strict'; +require('../common'); +var assert = require('assert'); + +// check for existence +assert(process.config.variables.hasOwnProperty('node_module_version')); + +// ensure that `node_module_version` is an Integer > 0 +assert(Number.isInteger(process.config.variables.node_module_version)); +assert(process.config.variables.node_module_version > 0); diff --git a/tools/getmoduleversion.py b/tools/getmoduleversion.py new file mode 100644 index 00000000000000..fb86ba1fa923dd --- /dev/null +++ b/tools/getmoduleversion.py @@ -0,0 +1,24 @@ +from __future__ import print_function +import os +import re + +def get_version(): + node_version_h = os.path.join( + os.path.dirname(__file__), + '..', + 'src', + 'node_version.h') + + f = open(node_version_h) + + regex = '^#define NODE_MODULE_VERSION [0-9]+' + + for line in f: + if re.match(regex, line): + major = line.split()[2] + return major + + raise Exception('Could not find pattern matching %s' % regex) + +if __name__ == '__main__': + print(get_version()) diff --git a/tools/getnodeversion.py b/tools/getnodeversion.py index 766e4f60dc07ad..f2032cccefe936 100644 --- a/tools/getnodeversion.py +++ b/tools/getnodeversion.py @@ -1,16 +1,20 @@ -import os,re +import os +import re -node_version_h = os.path.join(os.path.dirname(__file__), '..', 'src', +node_version_h = os.path.join( + os.path.dirname(__file__), + '..', + 'src', 'node_version.h') f = open(node_version_h) for line in f: - if re.match('#define NODE_MAJOR_VERSION', line): + if re.match('^#define NODE_MAJOR_VERSION', line): major = line.split()[2] - if re.match('#define NODE_MINOR_VERSION', line): + if re.match('^#define NODE_MINOR_VERSION', line): minor = line.split()[2] - if re.match('#define NODE_PATCH_VERSION', line): + if re.match('^#define NODE_PATCH_VERSION', line): patch = line.split()[2] print '%(major)s.%(minor)s.%(patch)s'% locals() diff --git a/tools/install.py b/tools/install.py index f3fa4fe898157b..a3986c5033904d 100755 --- a/tools/install.py +++ b/tools/install.py @@ -6,6 +6,7 @@ import re import shutil import sys +from getmoduleversion import get_version # set at init time node_prefix = '/usr/local' # PREFIX variable from Makefile @@ -107,9 +108,22 @@ def subdir_files(path, dest, action): def files(action): is_windows = sys.platform == 'win32' + output_file = 'node' + output_prefix = 'out/Release/' - exeext = '.exe' if is_windows else '' - action(['out/Release/node' + exeext], 'bin/node' + exeext) + if 'false' == variables.get('node_shared'): + if is_windows: + output_file += '.exe' + else: + if is_windows: + output_file += '.dll' + else: + # GYP will output to lib.target, this is hardcoded in its source, + # see the _InstallableTargetInstallPath function. + output_prefix += 'lib.target/' + output_file = 'lib' + output_file + '.so.' + get_version() + + action([output_prefix + output_file], 'bin/' + output_file) if 'true' == variables.get('node_use_dtrace'): action(['out/Release/node.d'], 'lib/dtrace/node.d') From 1c7b622cfc1e0108f16e703ae20945a9587ba540 Mon Sep 17 00:00:00 2001 From: Anna Henningsen <anna@addaleax.net> Date: Thu, 23 Jun 2016 21:50:36 +0200 Subject: [PATCH 067/131] doc: add `added:` information for http Ref: https://github.com/nodejs/node/issues/6578 PR-URL: https://github.com/nodejs/node/pull/7392 Reviewed-By: James M Snell <jasnell@gmail.com> --- doc/api/http.md | 244 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) diff --git a/doc/api/http.md b/doc/api/http.md index 493e66b7e7589e..35eb23193dbbc7 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -44,6 +44,9 @@ list like the following: ``` ## Class: http.Agent +<!-- YAML +added: v0.3.4 +--> The HTTP Agent is used for pooling sockets used in HTTP client requests. @@ -91,6 +94,9 @@ http.get({ ``` ### new Agent([options]) +<!-- YAML +added: v0.3.4 +--> * `options` {Object} Set of configurable options to set on the agent. Can have the following fields: @@ -118,6 +124,9 @@ http.request(options, onResponseCallback); ``` ### agent.createConnection(options[, callback]) +<!-- YAML +added: v0.11.4 +--> Produces a socket/stream to be used for HTTP requests. @@ -130,6 +139,9 @@ socket/stream from this function, or by passing the socket/stream to `callback`. `callback` has a signature of `(err, stream)`. ### agent.destroy() +<!-- YAML +added: v0.11.4 +--> Destroy any sockets that are currently in use by the agent. @@ -140,11 +152,17 @@ sockets may hang open for quite a long time before the server terminates them. ### agent.freeSockets +<!-- YAML +added: v0.11.4 +--> An object which contains arrays of sockets currently awaiting use by the Agent when HTTP KeepAlive is used. Do not modify. ### agent.getName(options) +<!-- YAML +added: v0.11.4 +--> Get a unique name for a set of request options, to determine whether a connection can be reused. In the http agent, this returns @@ -160,28 +178,43 @@ Options: the request. ### agent.maxFreeSockets +<!-- YAML +added: v0.11.7 +--> By default set to 256. For Agents supporting HTTP KeepAlive, this sets the maximum number of sockets that will be left open in the free state. ### agent.maxSockets +<!-- YAML +added: v0.3.6 +--> By default set to Infinity. Determines how many concurrent sockets the agent can have open per origin. Origin is either a 'host:port' or 'host:port:localAddress' combination. ### agent.requests +<!-- YAML +added: v0.5.9 +--> An object which contains queues of requests that have not yet been assigned to sockets. Do not modify. ### agent.sockets +<!-- YAML +added: v0.3.6 +--> An object which contains arrays of sockets currently in use by the Agent. Do not modify. ## Class: http.ClientRequest +<!-- YAML +added: v0.1.17 +--> This object is created internally and returned from [`http.request()`][]. It represents an _in-progress_ request whose header has already been queued. The @@ -213,6 +246,9 @@ The request implements the [Writable Stream][] interface. This is an [`EventEmitter`][] with the following events: ### Event: 'abort' +<!-- YAML +added: v1.4.1 +--> `function () { }` @@ -220,6 +256,9 @@ Emitted when the request has been aborted by the client. This event is only emitted on the first call to `abort()`. ### Event: 'aborted' +<!-- YAML +added: v0.3.8 +--> `function () { }` @@ -227,6 +266,9 @@ Emitted when the request has been aborted by the server and the network socket has closed. ### Event: 'checkExpectation' +<!-- YAML +added: v5.5.0 +--> `function (request, response) { }` @@ -238,6 +280,9 @@ Note that when this event is emitted and handled, the `request` event will not be emitted. ### Event: 'connect' +<!-- YAML +added: v0.7.0 +--> `function (response, socket, head) { }` @@ -303,6 +348,9 @@ proxy.listen(1337, '127.0.0.1', () => { ``` ### Event: 'continue' +<!-- YAML +added: v0.3.2 +--> `function () { }` @@ -311,6 +359,9 @@ the request contained 'Expect: 100-continue'. This is an instruction that the client should send the request body. ### Event: 'response' +<!-- YAML +added: v0.1.0 +--> `function (response) { }` @@ -318,12 +369,18 @@ Emitted when a response is received to this request. This event is emitted only once. The `response` argument will be an instance of [`http.IncomingMessage`][]. ### Event: 'socket' +<!-- YAML +added: v0.5.3 +--> `function (socket) { }` Emitted after a socket is assigned to this request. ### Event: 'upgrade' +<!-- YAML +added: v0.1.94 +--> `function (response, socket, head) { }` @@ -375,11 +432,17 @@ srv.listen(1337, '127.0.0.1', () => { ``` ### request.abort() +<!-- YAML +added: v0.3.8 +--> Marks the request as aborting. Calling this will cause remaining data in the response to be dropped and the socket to be destroyed. ### request.end([data][, encoding][, callback]) +<!-- YAML +added: v0.1.90 +--> Finishes sending the request. If any parts of the body are unsent, it will flush them to the stream. If the request is @@ -392,6 +455,9 @@ If `callback` is specified, it will be called when the request stream is finished. ### request.flushHeaders() +<!-- YAML +added: v1.6.0 +--> Flush the request headers. @@ -404,16 +470,25 @@ data isn't sent until possibly much later. `request.flushHeaders()` lets you by the optimization and kickstart the request. ### request.setNoDelay([noDelay]) +<!-- YAML +added: v0.5.9 +--> Once a socket is assigned to this request and is connected [`socket.setNoDelay()`][] will be called. ### request.setSocketKeepAlive([enable][, initialDelay]) +<!-- YAML +added: v0.5.9 +--> Once a socket is assigned to this request and is connected [`socket.setKeepAlive()`][] will be called. ### request.setTimeout(timeout[, callback]) +<!-- YAML +added: v0.5.9 +--> Once a socket is assigned to this request and is connected [`socket.setTimeout()`][] will be called. @@ -422,6 +497,9 @@ Once a socket is assigned to this request and is connected * `callback` {Function} Optional function to be called when a timeout occurs. Same as binding to the `timeout` event. ### request.write(chunk[, encoding][, callback]) +<!-- YAML +added: v0.1.29 +--> Sends a chunk of the body. By calling this method many times, the user can stream a request body to a @@ -440,10 +518,16 @@ is flushed. Returns `request`. ## Class: http.Server +<!-- YAML +added: v0.1.17 +--> This class inherits from [`net.Server`][] and has the following additional events: ### Event: 'checkContinue' +<!-- YAML +added: v0.3.0 +--> `function (request, response) { }` @@ -460,6 +544,9 @@ Note that when this event is emitted and handled, the `'request'` event will not be emitted. ### Event: 'clientError' +<!-- YAML +added: v0.1.94 +--> `function (exception, socket) { }` @@ -490,12 +577,18 @@ object, so any HTTP response sent, including response headers and payload, ensure the response is a properly formatted HTTP response message. ### Event: 'close' +<!-- YAML +added: v0.1.4 +--> `function () { }` Emitted when the server closes. ### Event: 'connect' +<!-- YAML +added: v0.7.0 +--> `function (request, socket, head) { }` @@ -514,6 +607,9 @@ event listener, meaning you will need to bind to it in order to handle data sent to the server on that socket. ### Event: 'connection' +<!-- YAML +added: v0.1.0 +--> `function (socket) { }` @@ -524,6 +620,9 @@ the protocol parser attaches to the socket. The `socket` can also be accessed at `request.connection`. ### Event: 'request' +<!-- YAML +added: v0.1.0 +--> `function (request, response) { }` @@ -533,6 +632,9 @@ per connection (in the case of keep-alive connections). an instance of [`http.ServerResponse`][]. ### Event: 'upgrade' +<!-- YAML +added: v0.1.94 +--> `function (request, socket, head) { }` @@ -551,10 +653,16 @@ event listener, meaning you will need to bind to it in order to handle data sent to the server on that socket. ### server.close([callback]) +<!-- YAML +added: v0.1.90 +--> Stops the server from accepting new connections. See [`net.Server.close()`][]. ### server.listen(handle[, callback]) +<!-- YAML +added: v0.5.10 +--> * `handle` {Object} * `callback` {Function} @@ -574,6 +682,9 @@ a listener for the `'listening'` event. See also [`net.Server.listen()`][]. Returns `server`. ### server.listen(path[, callback]) +<!-- YAML +added: v0.1.90 +--> Start a UNIX socket server listening for connections on the given `path`. @@ -581,6 +692,9 @@ This function is asynchronous. The last parameter `callback` will be added as a listener for the `'listening'` event. See also [`net.Server.listen(path)`][]. ### server.listen(port[, hostname][, backlog][, callback]) +<!-- YAML +added: v0.1.90 +--> Begin accepting connections on the specified `port` and `hostname`. If the `hostname` is omitted, the server will accept connections on any IPv6 address @@ -598,16 +712,25 @@ This function is asynchronous. The last parameter `callback` will be added as a listener for the `'listening'` event. See also [`net.Server.listen(port)`][]. ### server.listening +<!-- YAML +added: v5.7.0 +--> A Boolean indicating whether or not the server is listening for connections. ### server.maxHeadersCount +<!-- YAML +added: v0.7.0 +--> Limits maximum incoming headers count, equal to 1000 by default. If set to 0 - no limit will be applied. ### server.setTimeout(msecs, callback) +<!-- YAML +added: v0.9.12 +--> * `msecs` {Number} * `callback` {Function} @@ -627,6 +750,9 @@ for handling socket timeouts. Returns `server`. ### server.timeout +<!-- YAML +added: v0.9.12 +--> * {Number} Default = 120000 (2 minutes) @@ -641,6 +767,9 @@ Set to 0 to disable any kind of automatic timeout behavior on incoming connections. ## Class: http.ServerResponse +<!-- YAML +added: v0.1.17 +--> This object is created internally by a HTTP server--not by the user. It is passed as the second parameter to the `'request'` event. @@ -649,6 +778,9 @@ The response implements, but does not inherit from, the [Writable Stream][] interface. This is an [`EventEmitter`][] with the following events: ### Event: 'close' +<!-- YAML +added: v0.6.7 +--> `function () { }` @@ -656,6 +788,9 @@ Indicates that the underlying connection was terminated before [`response.end()`][] was called or able to flush. ### Event: 'finish' +<!-- YAML +added: v0.3.6 +--> `function () { }` @@ -667,6 +802,9 @@ does not imply that the client has received anything yet. After this event, no more events will be emitted on the response object. ### response.addTrailers(headers) +<!-- YAML +added: v0.3.0 +--> This method adds HTTP trailing headers (a header but at the end of the message) to the response. @@ -690,6 +828,9 @@ Attempting to set a header field name or value that contains invalid characters will result in a [`TypeError`][] being thrown. ### response.end([data][, encoding][, callback]) +<!-- YAML +added: v0.1.90 +--> This method signals to the server that all of the response headers and body have been sent; that server should consider this message complete. @@ -702,11 +843,17 @@ If `callback` is specified, it will be called when the response stream is finished. ### response.finished +<!-- YAML +added: v0.0.2 +--> Boolean value that indicates whether the response has completed. Starts as `false`. After [`response.end()`][] executes, the value will be `true`. ### response.getHeader(name) +<!-- YAML +added: v0.4.0 +--> Reads out a header that's already been queued but not sent to the client. Note that the name is case insensitive. This can only be called before headers get @@ -719,10 +866,16 @@ var contentType = response.getHeader('content-type'); ``` ### response.headersSent +<!-- YAML +added: v0.9.3 +--> Boolean (read-only). True if headers were sent, false otherwise. ### response.removeHeader(name) +<!-- YAML +added: v0.4.0 +--> Removes a header that's queued for implicit sending. @@ -733,6 +886,9 @@ response.removeHeader('Content-Encoding'); ``` ### response.sendDate +<!-- YAML +added: v0.7.5 +--> When true, the Date header will be automatically generated and sent in the response if it is not already present in the headers. Defaults to true. @@ -741,6 +897,9 @@ This should only be disabled for testing; HTTP requires the Date header in responses. ### response.setHeader(name, value) +<!-- YAML +added: v0.4.0 +--> Sets a single header value for implicit headers. If this header already exists in the to-be-sent headers, its value will be replaced. Use an array of strings @@ -776,6 +935,9 @@ const server = http.createServer((req,res) => { ``` ### response.setTimeout(msecs, callback) +<!-- YAML +added: v0.9.12 +--> * `msecs` {Number} * `callback` {Function} @@ -793,6 +955,9 @@ sockets. Returns `response`. ### response.statusCode +<!-- YAML +added: v0.4.0 +--> When using implicit headers (not calling [`response.writeHead()`][] explicitly), this property controls the status code that will be sent to the client when @@ -808,6 +973,9 @@ After response header was sent to the client, this property indicates the status code which was sent out. ### response.statusMessage +<!-- YAML +added: v0.11.8 +--> When using implicit headers (not calling [`response.writeHead()`][] explicitly), this property controls the status message that will be sent to the client when the headers get @@ -824,6 +992,9 @@ After response header was sent to the client, this property indicates the status message which was sent out. ### response.write(chunk[, encoding][, callback]) +<!-- YAML +added: v0.1.29 +--> If this method is called and [`response.writeHead()`][] has not been called, it will switch to implicit header mode and flush the implicit headers. @@ -850,11 +1021,17 @@ buffer. Returns `false` if all or part of the data was queued in user memory. `'drain'` will be emitted when the buffer is free again. ### response.writeContinue() +<!-- YAML +added: v0.3.0 +--> Sends a HTTP/1.1 100 Continue message to the client, indicating that the request body should be sent. See the [`'checkContinue'`][] event on `Server`. ### response.writeHead(statusCode[, statusMessage][, headers]) +<!-- YAML +added: v0.1.30 +--> Sends a response header to the request. The status code is a 3-digit HTTP status code, like `404`. The last argument, `headers`, are the response headers. @@ -901,6 +1078,9 @@ Attempting to set a header field name or value that contains invalid characters will result in a [`TypeError`][] being thrown. ## Class: http.IncomingMessage +<!-- YAML +added: v0.1.17 +--> An `IncomingMessage` object is created by [`http.Server`][] or [`http.ClientRequest`][] and passed as the first argument to the `'request'` @@ -911,6 +1091,9 @@ It implements the [Readable Stream][] interface, as well as the following additional events, methods, and properties. ### Event: 'aborted' +<!-- YAML +added: v0.3.8 +--> `function () { }` @@ -918,6 +1101,9 @@ Emitted when the request has been aborted by the client and the network socket has closed. ### Event: 'close' +<!-- YAML +added: v0.4.2 +--> `function () { }` @@ -925,6 +1111,9 @@ Indicates that the underlying connection was closed. Just like `'end'`, this event occurs only once per response. ### message.destroy([error]) +<!-- YAML +added: v0.3.0 +--> * `error` {Error} @@ -933,6 +1122,9 @@ is provided, an `'error'` event is emitted and `error` is passed as an argument to any listeners on the event. ### message.headers +<!-- YAML +added: v0.1.5 +--> The request/response headers object. @@ -959,6 +1151,9 @@ header name: * For all other headers, the values are joined together with ', '. ### message.httpVersion +<!-- YAML +added: v0.1.1 +--> In case of server request, the HTTP version sent by the client. In the case of client response, the HTTP version of the connected-to server. @@ -968,6 +1163,9 @@ Also `message.httpVersionMajor` is the first integer and `message.httpVersionMinor` is the second. ### message.method +<!-- YAML +added: v0.1.1 +--> **Only valid for request obtained from [`http.Server`][].** @@ -975,6 +1173,9 @@ The request method as a string. Read only. Example: `'GET'`, `'DELETE'`. ### message.rawHeaders +<!-- YAML +added: v0.11.6 +--> The raw request/response headers list exactly as they were received. @@ -999,11 +1200,17 @@ console.log(request.rawHeaders); ``` ### message.rawTrailers +<!-- YAML +added: v0.11.6 +--> The raw request/response trailer keys and values exactly as they were received. Only populated at the `'end'` event. ### message.setTimeout(msecs, callback) +<!-- YAML +added: v0.5.9 +--> * `msecs` {Number} * `callback` {Function} @@ -1013,18 +1220,27 @@ Calls `message.connection.setTimeout(msecs, callback)`. Returns `message`. ### message.statusCode +<!-- YAML +added: v0.1.1 +--> **Only valid for response obtained from [`http.ClientRequest`][].** The 3-digit HTTP response status code. E.G. `404`. ### message.statusMessage +<!-- YAML +added: v0.11.10 +--> **Only valid for response obtained from [`http.ClientRequest`][].** The HTTP response status message (reason phrase). E.G. `OK` or `Internal Server Error`. ### message.socket +<!-- YAML +added: v0.3.0 +--> The [`net.Socket`][] object associated with the connection. @@ -1032,10 +1248,16 @@ With HTTPS support, use [`request.socket.getPeerCertificate()`][] to obtain the client's authentication details. ### message.trailers +<!-- YAML +added: v0.3.0 +--> The request/response trailers object. Only populated at the `'end'` event. ### message.url +<!-- YAML +added: v0.1.90 +--> **Only valid for request obtained from [`http.Server`][].** @@ -1084,12 +1306,18 @@ $ node ``` ## http.METHODS +<!-- YAML +added: v0.11.8 +--> * {Array} A list of the HTTP methods that are supported by the parser. ## http.STATUS_CODES +<!-- YAML +added: v0.1.22 +--> * {Object} @@ -1098,6 +1326,10 @@ short description of each. For example, `http.STATUS_CODES[404] === 'Not Found'`. ## http.createClient([port][, host]) +<!-- YAML +added: v0.1.13 +deprecated: v0.3.6 +--> Stability: 0 - Deprecated: Use [`http.request()`][] instead. @@ -1105,6 +1337,9 @@ Constructs a new HTTP client. `port` and `host` refer to the server to be connected to. ## http.createServer([requestListener]) +<!-- YAML +added: v0.1.13 +--> Returns a new instance of [`http.Server`][]. @@ -1112,6 +1347,9 @@ The `requestListener` is a function which is automatically added to the `'request'` event. ## http.get(options[, callback]) +<!-- YAML +added: v0.3.6 +--> Since most requests are GET requests without bodies, Node.js provides this convenience method. The only difference between this method and [`http.request()`][] @@ -1130,11 +1368,17 @@ http.get('http://www.google.com/index.html', (res) => { ``` ## http.globalAgent +<!-- YAML +added: v0.5.9 +--> Global instance of Agent which is used as the default for all http client requests. ## http.request(options[, callback]) +<!-- YAML +added: v0.3.6 +--> Node.js maintains several connections per server to make HTTP requests. This function allows one to transparently issue requests. From f269c008f2a52a4ee9d81200981be05c458beaf6 Mon Sep 17 00:00:00 2001 From: Anna Henningsen <anna@addaleax.net> Date: Thu, 23 Jun 2016 21:51:12 +0200 Subject: [PATCH 068/131] doc: add `added:` information for https Ref: https://github.com/nodejs/node/issues/6578 PR-URL: https://github.com/nodejs/node/pull/7392 Reviewed-By: James M Snell <jasnell@gmail.com> --- doc/api/https.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/doc/api/https.md b/doc/api/https.md index 09c4b5d2b9775f..e7cbc9969c3ccd 100644 --- a/doc/api/https.md +++ b/doc/api/https.md @@ -6,24 +6,39 @@ HTTPS is the HTTP protocol over TLS/SSL. In Node.js this is implemented as a separate module. ## Class: https.Agent +<!-- YAML +added: v0.4.5 +--> An Agent object for HTTPS similar to [`http.Agent`][]. See [`https.request()`][] for more information. ## Class: https.Server +<!-- YAML +added: v0.3.4 +--> This class is a subclass of `tls.Server` and emits events same as [`http.Server`][]. See [`http.Server`][] for more information. ### server.setTimeout(msecs, callback) +<!-- YAML +added: v0.11.2 +--> See [`http.Server#setTimeout()`][]. ### server.timeout +<!-- YAML +added: v0.11.2 +--> See [`http.Server#timeout`][]. ## https.createServer(options[, requestListener]) +<!-- YAML +added: v0.3.4 +--> Returns a new HTTPS web server object. The `options` is similar to [`tls.createServer()`][]. The `requestListener` is a function which is @@ -64,6 +79,9 @@ https.createServer(options, (req, res) => { ``` ### server.close([callback]) +<!-- YAML +added: v0.1.90 +--> See [`http.close()`][] for details. @@ -74,6 +92,9 @@ See [`http.close()`][] for details. See [`http.listen()`][] for details. ## https.get(options, callback) +<!-- YAML +added: v0.3.6 +--> Like [`http.get()`][] but for HTTPS. @@ -99,10 +120,16 @@ https.get('https://encrypted.google.com/', (res) => { ``` ## https.globalAgent +<!-- YAML +added: v0.5.9 +--> Global instance of [`https.Agent`][] for all HTTPS client requests. ## https.request(options, callback) +<!-- YAML +added: v0.3.6 +--> Makes a request to a secure web server. From 58ae35c34d976f596f15f017d3b41cce2c16e316 Mon Sep 17 00:00:00 2001 From: Anna Henningsen <anna@addaleax.net> Date: Thu, 23 Jun 2016 21:54:34 +0200 Subject: [PATCH 069/131] doc: fix broken refs to url.parse() in http docs PR-URL: https://github.com/nodejs/node/pull/7392 Reviewed-By: James M Snell <jasnell@gmail.com> --- doc/api/http.md | 2 +- doc/api/https.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/http.md b/doc/api/http.md index 35eb23193dbbc7..7accccd92631c9 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -1523,7 +1523,7 @@ There are a few special headers that should be noted. [`socket.setTimeout()`]: net.html#net_socket_settimeout_timeout_callback [`stream.setEncoding()`]: stream.html#stream_stream_setencoding_encoding [`TypeError`]: errors.html#errors_class_typeerror -[`url.parse()`]: url.html#url_url_parse_urlstr_parsequerystring_slashesdenotehost +[`url.parse()`]: url.html#url_url_parse_urlstring_parsequerystring_slashesdenotehost [constructor options]: #http_new_agent_options [Readable Stream]: stream.html#stream_class_stream_readable [Writable Stream]: stream.html#stream_class_stream_writable diff --git a/doc/api/https.md b/doc/api/https.md index e7cbc9969c3ccd..c76f1ef837c9ab 100644 --- a/doc/api/https.md +++ b/doc/api/https.md @@ -270,4 +270,4 @@ var req = https.request(options, (res) => { [`SSL_METHODS`]: https://www.openssl.org/docs/ssl/ssl.html#DEALING-WITH-PROTOCOL-METHODS [`tls.connect()`]: tls.html#tls_tls_connect_options_callback [`tls.createServer()`]: tls.html#tls_tls_createserver_options_secureconnectionlistener -[`url.parse()`]: url.html#url_url_parse_urlstr_parsequerystring_slashesdenotehost +[`url.parse()`]: url.html#url_url_parse_urlstring_parsequerystring_slashesdenotehost From 365f5207b32fc9355c269277584053f28202fceb Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Sat, 25 Jun 2016 15:18:04 -0700 Subject: [PATCH 070/131] test: test isFullWidthCodePoint with invalid input Code coverage information shows that we are only testing the happy path for the internal readline `isFullWidthCodePoint()` function. Test it with invalid input. PR-URL: https://github.com/nodejs/node/pull/7422 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com> --- test/parallel/test-readline-interface.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/parallel/test-readline-interface.js b/test/parallel/test-readline-interface.js index fcda628acec434..603d08aa7aa130 100644 --- a/test/parallel/test-readline-interface.js +++ b/test/parallel/test-readline-interface.js @@ -325,6 +325,11 @@ function isWarned(emitter) { rli.close(); } + // isFullWidthCodePoint() should return false for non-numeric values + [true, false, null, undefined, {}, [], 'あ'].forEach((v) => { + assert.strictEqual(readline.isFullWidthCodePoint('あ'), false); + }); + // wide characters should be treated as two columns. assert.equal(readline.isFullWidthCodePoint('a'.charCodeAt(0)), false); assert.equal(readline.isFullWidthCodePoint('あ'.charCodeAt(0)), true); From 5830ec5d4198c90008509550b14e2d99371a30d5 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 071/131] tools: update cpplint to r456 PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- tools/cpplint.py | 5095 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 4024 insertions(+), 1071 deletions(-) diff --git a/tools/cpplint.py b/tools/cpplint.py index 4f4af6fe337590..94913206f9bf2b 100644 --- a/tools/cpplint.py +++ b/tools/cpplint.py @@ -28,44 +28,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# Here are some issues that I've had people identify in my code during reviews, -# that I think are possible to flag automatically in a lint tool. If these were -# caught by lint, it would save time both for myself and that of my reviewers. -# Most likely, some of these are beyond the scope of the current lint framework, -# but I think it is valuable to retain these wish-list items even if they cannot -# be immediately implemented. -# -# Suggestions -# ----------- -# - Check for no 'explicit' for multi-arg ctor -# - Check for boolean assign RHS in parens -# - Check for ctor initializer-list colon position and spacing -# - Check that if there's a ctor, there should be a dtor -# - Check accessors that return non-pointer member variables are -# declared const -# - Check accessors that return non-const pointer member vars are -# *not* declared const -# - Check for using public includes for testing -# - Check for spaces between brackets in one-line inline method -# - Check for no assert() -# - Check for spaces surrounding operators -# - Check for 0 in pointer context (should be NULL) -# - Check for 0 in char context (should be '\0') -# - Check for camel-case method name conventions for methods -# that are not simple inline getters and setters -# - Check that base classes have virtual destructors -# put " // namespace" after } that closes a namespace, with -# namespace's name after 'namespace' if it is named. -# - Do not indent namespace contents -# - Avoid inlining non-trivial constructors in header files -# include base/basictypes.h if DISALLOW_EVIL_CONSTRUCTORS is used -# - Check for old-school (void) cast for call-sites of functions -# ignored return value -# - Check gUnit usage of anonymous namespace -# - Check for class declaration order (typedefs, consts, enums, -# ctor(s?), dtor, friend declarations, methods, member vars) -# - """Does google-lint on c++ files. The goal of this script is to identify places in the code that *may* @@ -80,6 +42,7 @@ """ import codecs +import copy import getopt import math # for log import os @@ -88,16 +51,16 @@ import string import sys import unicodedata -import logging -logger = logging.getLogger('testrunner') + _USAGE = """ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] - [--counting=total|toplevel|detailed] + [--counting=total|toplevel|detailed] [--root=subdir] + [--linelength=digits] <file> [file] ... The style guidelines this tries to follow are those in - http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml + https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml Every problem is given a confidence score from 1-5, with 5 meaning we are certain of the problem, and 1 meaning it could be a legitimate construct. @@ -108,7 +71,8 @@ suppresses errors of all categories on that line. The files passed in will be linted; at least one file must be provided. - Linted extensions are .cc, .cpp, and .h. Other file types will be ignored. + Default linted extensions are .cc, .cpp, .cu, .cuh and .h. Change the + extensions with the --extensions flag. Flags: @@ -141,128 +105,342 @@ also be printed. If 'detailed' is provided, then a count is provided for each category like 'build/class'. - logfile=filename - Write TAP output to a logfile. + root=subdir + The root directory used for deriving header guard CPP variable. + By default, the header guard CPP variable is calculated as the relative + path to the directory that contains .git, .hg, or .svn. When this flag + is specified, the relative path is calculated from the specified + directory. If the specified directory does not exist, this flag is + ignored. + + Examples: + Assuming that src/.git exists, the header guard CPP variables for + src/chrome/browser/ui/browser.h are: + + No flag => CHROME_BROWSER_UI_BROWSER_H_ + --root=chrome => BROWSER_UI_BROWSER_H_ + --root=chrome/browser => UI_BROWSER_H_ + + linelength=digits + This is the allowed line length for the project. The default value is + 80 characters. + + Examples: + --linelength=120 + + extensions=extension,extension,... + The allowed file extensions that cpplint will check + + Examples: + --extensions=hpp,cpp + + cpplint.py supports per-directory configurations specified in CPPLINT.cfg + files. CPPLINT.cfg file can contain a number of key=value pairs. + Currently the following options are supported: + + set noparent + filter=+filter1,-filter2,... + exclude_files=regex + linelength=80 + + "set noparent" option prevents cpplint from traversing directory tree + upwards looking for more .cfg files in parent directories. This option + is usually placed in the top-level project directory. + + The "filter" option is similar in function to --filter flag. It specifies + message filters in addition to the |_DEFAULT_FILTERS| and those specified + through --filter command-line flag. + + "exclude_files" allows to specify a regular expression to be matched against + a file name. If the expression matches, the file is skipped and not run + through liner. + + "linelength" allows to specify the allowed line length for the project. + + CPPLINT.cfg has an effect on files in the same directory and all + sub-directories, unless overridden by a nested configuration file. + + Example file: + filter=-build/include_order,+build/include_alpha + exclude_files=.*\.cc + + The above example disables build/include_order warning and enables + build/include_alpha as well as excludes all .cc from being + processed by linter, in the current directory (where the .cfg + file is located) and all sub-directories. """ # We categorize each error message we print. Here are the categories. # We want an explicit list so we can list them all in cpplint --filter=. # If you add a new error message with a new category, add it to the list # here! cpplint_unittest.py should tell you if you forget to do this. -# \ used for clearer layout -- pylint: disable-msg=C6013 _ERROR_CATEGORIES = [ - 'build/class', - 'build/deprecated', - 'build/endif_comment', - 'build/forward_decl', - 'build/header_guard', - 'build/include', - 'build/include_alpha', - 'build/include_order', - 'build/include_what_you_use', - 'build/namespaces', - 'build/printf_format', - 'build/storage_class', - 'legal/copyright', - 'readability/braces', - 'readability/casting', - 'readability/check', - 'readability/constructors', - 'readability/fn_size', - 'readability/function', - 'readability/multiline_comment', - 'readability/multiline_string', - 'readability/nolint', - 'readability/streams', - 'readability/todo', - 'readability/utf8', - 'runtime/arrays', - 'runtime/casting', - 'runtime/explicit', - 'runtime/int', - 'runtime/init', - 'runtime/invalid_increment', - 'runtime/member_string_references', - 'runtime/memset', - 'runtime/operator', - 'runtime/printf', - 'runtime/printf_format', - 'runtime/references', - 'runtime/rtti', - 'runtime/sizeof', - 'runtime/string', - 'runtime/threadsafe_fn', - 'runtime/virtual', - 'whitespace/blank_line', - 'whitespace/braces', - 'whitespace/comma', - 'whitespace/comments', - 'whitespace/end_of_line', - 'whitespace/ending_newline', - 'whitespace/if-one-line', - 'whitespace/indent', - 'whitespace/labels', - 'whitespace/line_length', - 'whitespace/newline', - 'whitespace/operators', - 'whitespace/parens', - 'whitespace/semicolon', - 'whitespace/tab', - 'whitespace/todo' - ] - -# The default state of the category filter. This is overrided by the --filter= + 'build/class', + 'build/c++11', + 'build/c++14', + 'build/c++tr1', + 'build/deprecated', + 'build/endif_comment', + 'build/explicit_make_pair', + 'build/forward_decl', + 'build/header_guard', + 'build/include', + 'build/include_alpha', + 'build/include_order', + 'build/include_what_you_use', + 'build/namespaces', + 'build/printf_format', + 'build/storage_class', + 'legal/copyright', + 'readability/alt_tokens', + 'readability/braces', + 'readability/casting', + 'readability/check', + 'readability/constructors', + 'readability/fn_size', + 'readability/inheritance', + 'readability/multiline_comment', + 'readability/multiline_string', + 'readability/namespace', + 'readability/nolint', + 'readability/nul', + 'readability/strings', + 'readability/todo', + 'readability/utf8', + 'runtime/arrays', + 'runtime/casting', + 'runtime/explicit', + 'runtime/int', + 'runtime/init', + 'runtime/invalid_increment', + 'runtime/member_string_references', + 'runtime/memset', + 'runtime/indentation_namespace', + 'runtime/operator', + 'runtime/printf', + 'runtime/printf_format', + 'runtime/references', + 'runtime/string', + 'runtime/threadsafe_fn', + 'runtime/vlog', + 'whitespace/blank_line', + 'whitespace/braces', + 'whitespace/comma', + 'whitespace/comments', + 'whitespace/empty_conditional_body', + 'whitespace/empty_if_body', + 'whitespace/empty_loop_body', + 'whitespace/end_of_line', + 'whitespace/ending_newline', + 'whitespace/forcolon', + 'whitespace/indent', + 'whitespace/line_length', + 'whitespace/newline', + 'whitespace/operators', + 'whitespace/parens', + 'whitespace/semicolon', + 'whitespace/tab', + 'whitespace/todo', + ] + +# These error categories are no longer enforced by cpplint, but for backwards- +# compatibility they may still appear in NOLINT comments. +_LEGACY_ERROR_CATEGORIES = [ + 'readability/streams', + 'readability/function', + ] + +# The default state of the category filter. This is overridden by the --filter= # flag. By default all errors are on, so only add here categories that should be # off by default (i.e., categories that must be enabled by the --filter= flags). # All entries here should start with a '-' or '+', as in the --filter= flag. -_DEFAULT_FILTERS = [ - '-build/include_alpha', - '-legal/copyright', - '-readability/function', - ] +_DEFAULT_FILTERS = ['-build/include_alpha'] + +# The default list of categories suppressed for C (not C++) files. +_DEFAULT_C_SUPPRESSED_CATEGORIES = [ + 'readability/casting', + ] + +# The default list of categories suppressed for Linux Kernel files. +_DEFAULT_KERNEL_SUPPRESSED_CATEGORIES = [ + 'whitespace/tab', + ] # We used to check for high-bit characters, but after much discussion we # decided those were OK, as long as they were in UTF-8 and didn't represent -# hard-coded international strings, which belong in a seperate i18n file. - -# Headers that we consider STL headers. -_STL_HEADERS = frozenset([ - 'algobase.h', 'algorithm', 'alloc.h', 'bitset', 'deque', 'exception', - 'function.h', 'functional', 'hash_map', 'hash_map.h', 'hash_set', - 'hash_set.h', 'iterator', 'list', 'list.h', 'map', 'memory', 'new', - 'pair.h', 'pthread_alloc', 'queue', 'set', 'set.h', 'sstream', 'stack', - 'stl_alloc.h', 'stl_relops.h', 'type_traits.h', - 'utility', 'vector', 'vector.h', - ]) +# hard-coded international strings, which belong in a separate i18n file. - -# Non-STL C++ system headers. +# C++ headers _CPP_HEADERS = frozenset([ - 'algo.h', 'builtinbuf.h', 'bvector.h', 'cassert', 'cctype', - 'cerrno', 'cfloat', 'ciso646', 'climits', 'clocale', 'cmath', - 'complex', 'complex.h', 'csetjmp', 'csignal', 'cstdarg', 'cstddef', - 'cstdio', 'cstdlib', 'cstring', 'ctime', 'cwchar', 'cwctype', - 'defalloc.h', 'deque.h', 'editbuf.h', 'exception', 'fstream', - 'fstream.h', 'hashtable.h', 'heap.h', 'indstream.h', 'iomanip', - 'iomanip.h', 'ios', 'iosfwd', 'iostream', 'iostream.h', 'istream.h', - 'iterator.h', 'limits', 'map.h', 'multimap.h', 'multiset.h', - 'numeric', 'ostream.h', 'parsestream.h', 'pfstream.h', 'PlotFile.h', - 'procbuf.h', 'pthread_alloc.h', 'rope', 'rope.h', 'ropeimpl.h', - 'SFile.h', 'slist', 'slist.h', 'stack.h', 'stdexcept', - 'stdiostream.h', 'streambuf.h', 'stream.h', 'strfile.h', 'string', - 'strstream', 'strstream.h', 'tempbuf.h', 'tree.h', 'typeinfo', 'valarray', + # Legacy + 'algobase.h', + 'algo.h', + 'alloc.h', + 'builtinbuf.h', + 'bvector.h', + 'complex.h', + 'defalloc.h', + 'deque.h', + 'editbuf.h', + 'fstream.h', + 'function.h', + 'hash_map', + 'hash_map.h', + 'hash_set', + 'hash_set.h', + 'hashtable.h', + 'heap.h', + 'indstream.h', + 'iomanip.h', + 'iostream.h', + 'istream.h', + 'iterator.h', + 'list.h', + 'map.h', + 'multimap.h', + 'multiset.h', + 'ostream.h', + 'pair.h', + 'parsestream.h', + 'pfstream.h', + 'procbuf.h', + 'pthread_alloc', + 'pthread_alloc.h', + 'rope', + 'rope.h', + 'ropeimpl.h', + 'set.h', + 'slist', + 'slist.h', + 'stack.h', + 'stdiostream.h', + 'stl_alloc.h', + 'stl_relops.h', + 'streambuf.h', + 'stream.h', + 'strfile.h', + 'strstream.h', + 'tempbuf.h', + 'tree.h', + 'type_traits.h', + 'vector.h', + # 17.6.1.2 C++ library headers + 'algorithm', + 'array', + 'atomic', + 'bitset', + 'chrono', + 'codecvt', + 'complex', + 'condition_variable', + 'deque', + 'exception', + 'forward_list', + 'fstream', + 'functional', + 'future', + 'initializer_list', + 'iomanip', + 'ios', + 'iosfwd', + 'iostream', + 'istream', + 'iterator', + 'limits', + 'list', + 'locale', + 'map', + 'memory', + 'mutex', + 'new', + 'numeric', + 'ostream', + 'queue', + 'random', + 'ratio', + 'regex', + 'scoped_allocator', + 'set', + 'sstream', + 'stack', + 'stdexcept', + 'streambuf', + 'string', + 'strstream', + 'system_error', + 'thread', + 'tuple', + 'typeindex', + 'typeinfo', + 'type_traits', + 'unordered_map', + 'unordered_set', + 'utility', + 'valarray', + 'vector', + # 17.6.1.2 C++ headers for C library facilities + 'cassert', + 'ccomplex', + 'cctype', + 'cerrno', + 'cfenv', + 'cfloat', + 'cinttypes', + 'ciso646', + 'climits', + 'clocale', + 'cmath', + 'csetjmp', + 'csignal', + 'cstdalign', + 'cstdarg', + 'cstdbool', + 'cstddef', + 'cstdint', + 'cstdio', + 'cstdlib', + 'cstring', + 'ctgmath', + 'ctime', + 'cuchar', + 'cwchar', + 'cwctype', ]) +# Type names +_TYPES = re.compile( + r'^(?:' + # [dcl.type.simple] + r'(char(16_t|32_t)?)|wchar_t|' + r'bool|short|int|long|signed|unsigned|float|double|' + # [support.types] + r'(ptrdiff_t|size_t|max_align_t|nullptr_t)|' + # [cstdint.syn] + r'(u?int(_fast|_least)?(8|16|32|64)_t)|' + r'(u?int(max|ptr)_t)|' + r')$') + + +# These headers are excluded from [build/include] and [build/include_order] +# checks: +# - Anything not following google file name conventions (containing an +# uppercase character, such as Python.h or nsStringAPI.h, for example). +# - Lua headers. +_THIRD_PARTY_HEADERS_PATTERN = re.compile( + r'^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$') + +# Pattern for matching FileInfo.BaseName() against test file name +_TEST_FILE_SUFFIX = r'(_test|_unittest|_regtest)$' + +# Pattern that matches only complete whitespace, possibly across multiple lines. +_EMPTY_CONDITIONAL_BODY_PATTERN = re.compile(r'^\s*$', re.DOTALL) # Assertion macros. These are defined in base/logging.h and -# testing/base/gunit.h. Note that the _M versions need to come first -# for substring matching to work. +# testing/base/public/gunit.h. _CHECK_MACROS = [ 'DCHECK', 'CHECK', - 'EXPECT_TRUE_M', 'EXPECT_TRUE', - 'ASSERT_TRUE_M', 'ASSERT_TRUE', - 'EXPECT_FALSE_M', 'EXPECT_FALSE', - 'ASSERT_FALSE_M', 'ASSERT_FALSE', + 'EXPECT_TRUE', 'ASSERT_TRUE', + 'EXPECT_FALSE', 'ASSERT_FALSE', ] # Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE @@ -275,38 +453,92 @@ _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement - _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement - _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), ('>=', 'LT'), ('>', 'LE'), ('<=', 'GT'), ('<', 'GE')]: _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement - _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement - _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement + +# Alternative tokens and their replacements. For full list, see section 2.5 +# Alternative tokens [lex.digraph] in the C++ standard. +# +# Digraphs (such as '%:') are not included here since it's a mess to +# match those on a word boundary. +_ALT_TOKEN_REPLACEMENT = { + 'and': '&&', + 'bitor': '|', + 'or': '||', + 'xor': '^', + 'compl': '~', + 'bitand': '&', + 'and_eq': '&=', + 'or_eq': '|=', + 'xor_eq': '^=', + 'not': '!', + 'not_eq': '!=' + } + +# Compile regular expression that matches all the above keywords. The "[ =()]" +# bit is meant to avoid matching these keywords outside of boolean expressions. +# +# False positives include C-style multi-line comments and multi-line strings +# but those have always been troublesome for cpplint. +_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( + r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)') # These constants define types of headers for use with # _IncludeState.CheckNextIncludeOrder(). -_LIKELY_MY_HEADER = 1 -_POSSIBLE_MY_HEADER = 2 -_OTHER_HEADER = 3 -_C_SYS_HEADER = 4 -_CPP_SYS_HEADER = 5 - +_C_SYS_HEADER = 1 +_CPP_SYS_HEADER = 2 +_LIKELY_MY_HEADER = 3 +_POSSIBLE_MY_HEADER = 4 +_OTHER_HEADER = 5 + +# These constants define the current inline assembly state +_NO_ASM = 0 # Outside of inline assembly block +_INSIDE_ASM = 1 # Inside inline assembly block +_END_ASM = 2 # Last line of inline assembly block +_BLOCK_ASM = 3 # The whole block is an inline assembly block + +# Match start of assembly blocks +_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)' + r'(?:\s+(volatile|__volatile__))?' + r'\s*[{(]') + +# Match strings that indicate we're working on a C (not C++) file. +_SEARCH_C_FILE = re.compile(r'\b(?:LINT_C_FILE|' + r'vim?:\s*.*(\s*|:)filetype=c(\s*|:|$))') + +# Match string that indicates we're working on a Linux Kernel file. +_SEARCH_KERNEL_FILE = re.compile(r'\b(?:LINT_KERNEL_FILE)') _regexp_compile_cache = {} -# Finds occurrences of NOLINT or NOLINT(...). -_RE_SUPPRESSION = re.compile(r'\bNOLINT\b(\([^)]*\))?') - # {str, set(int)}: a map from error categories to sets of linenumbers # on which those errors are expected and should be suppressed. _error_suppressions = {} +# The root directory used for deriving header guard CPP variable. +# This is set by --root flag. +_root = None + +# The allowed line length of files. +# This is set by --linelength flag. +_line_length = 80 + +# The allowed extensions for file names +# This is set by --extensions flag. +_valid_extensions = set(['cc', 'h', 'cpp', 'cu', 'cuh']) + +# {str, bool}: a map from error categories to booleans which indicate if the +# category should be suppressed for every line. +_global_error_suppressions = {} + + def ParseNolintSuppressions(filename, raw_line, linenum, error): - """Updates the global list of error-suppressions. + """Updates the global list of line error-suppressions. Parses any NOLINT comments on the current line, updating the global error_suppressions store. Reports an error if the NOLINT comment @@ -318,64 +550,113 @@ def ParseNolintSuppressions(filename, raw_line, linenum, error): linenum: int, the number of the current line. error: function, an error handler. """ - # FIXME(adonovan): "NOLINT(" is misparsed as NOLINT(*). - m = _RE_SUPPRESSION.search(raw_line) - if m: - category = m.group(1) + matched = Search(r'\bNOLINT(NEXTLINE)?\b(\([^)]+\))?', raw_line) + if matched: + if matched.group(1): + suppressed_line = linenum + 1 + else: + suppressed_line = linenum + category = matched.group(2) if category in (None, '(*)'): # => "suppress all" - _error_suppressions.setdefault(None, set()).add(linenum) + _error_suppressions.setdefault(None, set()).add(suppressed_line) else: if category.startswith('(') and category.endswith(')'): category = category[1:-1] if category in _ERROR_CATEGORIES: - _error_suppressions.setdefault(category, set()).add(linenum) - else: + _error_suppressions.setdefault(category, set()).add(suppressed_line) + elif category not in _LEGACY_ERROR_CATEGORIES: error(filename, linenum, 'readability/nolint', 5, - 'Unknown NOLINT error category: %s' % category) + 'Unknown NOLINT error category: %s' % category) + + +def ProcessGlobalSuppresions(lines): + """Updates the list of global error suppressions. + + Parses any lint directives in the file that have global effect. + + Args: + lines: An array of strings, each representing a line of the file, with the + last element being empty if the file is terminated with a newline. + """ + for line in lines: + if _SEARCH_C_FILE.search(line): + for category in _DEFAULT_C_SUPPRESSED_CATEGORIES: + _global_error_suppressions[category] = True + if _SEARCH_KERNEL_FILE.search(line): + for category in _DEFAULT_KERNEL_SUPPRESSED_CATEGORIES: + _global_error_suppressions[category] = True def ResetNolintSuppressions(): - "Resets the set of NOLINT suppressions to empty." + """Resets the set of NOLINT suppressions to empty.""" _error_suppressions.clear() + _global_error_suppressions.clear() def IsErrorSuppressedByNolint(category, linenum): """Returns true if the specified error category is suppressed on this line. Consults the global error_suppressions map populated by - ParseNolintSuppressions/ResetNolintSuppressions. + ParseNolintSuppressions/ProcessGlobalSuppresions/ResetNolintSuppressions. Args: category: str, the category of the error. linenum: int, the current line number. Returns: - bool, True iff the error should be suppressed due to a NOLINT comment. + bool, True iff the error should be suppressed due to a NOLINT comment or + global suppression. """ - return (linenum in _error_suppressions.get(category, set()) or + return (_global_error_suppressions.get(category, False) or + linenum in _error_suppressions.get(category, set()) or linenum in _error_suppressions.get(None, set())) + def Match(pattern, s): """Matches the string with the pattern, caching the compiled regexp.""" # The regexp compilation caching is inlined in both Match and Search for # performance reasons; factoring it out into a separate function turns out # to be noticeably expensive. - if not pattern in _regexp_compile_cache: + if pattern not in _regexp_compile_cache: _regexp_compile_cache[pattern] = sre_compile.compile(pattern) return _regexp_compile_cache[pattern].match(s) +def ReplaceAll(pattern, rep, s): + """Replaces instances of pattern in a string with a replacement. + + The compiled regex is kept in a cache shared by Match and Search. + + Args: + pattern: regex pattern + rep: replacement text + s: search string + + Returns: + string with replacements made (or original string if no replacements) + """ + if pattern not in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].sub(rep, s) + + def Search(pattern, s): """Searches the string for the pattern, caching the compiled regexp.""" - if not pattern in _regexp_compile_cache: + if pattern not in _regexp_compile_cache: _regexp_compile_cache[pattern] = sre_compile.compile(pattern) return _regexp_compile_cache[pattern].search(s) -class _IncludeState(dict): +def _IsSourceExtension(s): + """File extension (excluding dot) matches a source file extension.""" + return s in ('c', 'cc', 'cpp', 'cxx') + + +class _IncludeState(object): """Tracks line numbers for includes, and the order in which includes appear. - As a dict, an _IncludeState object serves as a mapping between include - filename and line number on which that file was included. + include_list contains list of lists of (header, line number) pairs. + It's a lists of lists rather than just one flat list to make it + easier to update across preprocessor boundaries. Call CheckNextIncludeOrder() once for each header in the file, passing in the type constants defined above. Calls in an illegal order will @@ -386,9 +667,9 @@ class _IncludeState(dict): # needs to move backwards, CheckNextIncludeOrder will raise an error. _INITIAL_SECTION = 0 _MY_H_SECTION = 1 - _OTHER_H_SECTION = 2 - _C_SECTION = 3 - _CPP_SECTION = 4 + _C_SECTION = 2 + _CPP_SECTION = 3 + _OTHER_H_SECTION = 4 _TYPE_NAMES = { _C_SYS_HEADER: 'C system header', @@ -406,14 +687,47 @@ class _IncludeState(dict): } def __init__(self): - dict.__init__(self) + self.include_list = [[]] + self.ResetSection('') + + def FindHeader(self, header): + """Check if a header has already been included. + + Args: + header: header to check. + Returns: + Line number of previous occurrence, or -1 if the header has not + been seen before. + """ + for section_list in self.include_list: + for f in section_list: + if f[0] == header: + return f[1] + return -1 + + def ResetSection(self, directive): + """Reset section checking for preprocessor directive. + + Args: + directive: preprocessor directive (e.g. "if", "else"). + """ # The name of the current section. self._section = self._INITIAL_SECTION # The path of last found header. self._last_header = '' + # Update list of includes. Note that we never pop from the + # include list. + if directive in ('if', 'ifdef', 'ifndef'): + self.include_list.append([]) + elif directive in ('else', 'elif'): + self.include_list[-1] = [] + + def SetLastHeader(self, header_path): + self._last_header = header_path + def CanonicalizeAlphabeticalOrder(self, header_path): - """Returns a path canonicalized for alphabetical comparisson. + """Returns a path canonicalized for alphabetical comparison. - replaces "-" with "_" so they both cmp the same. - removes '-inl' since we don't require them to be after the main header. @@ -427,19 +741,25 @@ def CanonicalizeAlphabeticalOrder(self, header_path): """ return header_path.replace('-inl.h', '.h').replace('-', '_').lower() - def IsInAlphabeticalOrder(self, header_path): + def IsInAlphabeticalOrder(self, clean_lines, linenum, header_path): """Check if a header is in alphabetical order with the previous header. Args: - header_path: Header to be checked. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + header_path: Canonicalized header to be checked. Returns: Returns true if the header is in alphabetical order. """ - canonical_header = self.CanonicalizeAlphabeticalOrder(header_path) - if self._last_header > canonical_header: + # If previous section is different from current section, _last_header will + # be reset to empty string, so it's always less than current header. + # + # If previous line was a blank line, assume that the headers are + # intentionally sorted the way they are. + if (self._last_header > header_path and + Match(r'^\s*#\s*include\b', clean_lines.elided[linenum - 1])): return False - self._last_header = canonical_header return True def CheckNextIncludeOrder(self, header_type): @@ -462,32 +782,33 @@ def CheckNextIncludeOrder(self, header_type): last_section = self._section - if header_type == _LIKELY_MY_HEADER: - if self._section <= self._MY_H_SECTION: - self._section = self._MY_H_SECTION + if header_type == _C_SYS_HEADER: + if self._section <= self._C_SECTION: + self._section = self._C_SECTION else: self._last_header = '' return error_message - elif header_type == _POSSIBLE_MY_HEADER: - if self._section <= self._MY_H_SECTION: - self._section = self._MY_H_SECTION + elif header_type == _CPP_SYS_HEADER: + if self._section <= self._CPP_SECTION: + self._section = self._CPP_SECTION else: self._last_header = '' return error_message - elif header_type == _OTHER_HEADER: - if self._section <= self._OTHER_H_SECTION: - self._section = self._OTHER_H_SECTION + elif header_type == _LIKELY_MY_HEADER: + if self._section <= self._MY_H_SECTION: + self._section = self._MY_H_SECTION else: - self._last_header = '' - return error_message - elif header_type == _C_SYS_HEADER: - if self._section <= self._C_SECTION: - self._section = self._C_SECTION + self._section = self._OTHER_H_SECTION + elif header_type == _POSSIBLE_MY_HEADER: + if self._section <= self._MY_H_SECTION: + self._section = self._MY_H_SECTION else: - self._section = self._CPP_SECTION + # This will always be the fallback because we're not sure + # enough that the header is associated with this file. + self._section = self._OTHER_H_SECTION else: - assert header_type == _CPP_SYS_HEADER - self._section = self._CPP_SECTION + assert header_type == _OTHER_HEADER + self._section = self._OTHER_H_SECTION if last_section != self._section: self._last_header = '' @@ -503,6 +824,8 @@ def __init__(self): self.error_count = 0 # global count of reported errors # filters to apply when emitting error messages self.filters = _DEFAULT_FILTERS[:] + # backup of filter list. Used to restore the state after each file. + self._filters_backup = self.filters[:] self.counting = 'total' # In what way are we counting errors? self.errors_by_category = {} # string to int dict storing error counts @@ -541,6 +864,10 @@ def SetFilters(self, filters): """ # Default filters always have less priority than the flag ones. self.filters = _DEFAULT_FILTERS[:] + self.AddFilters(filters) + + def AddFilters(self, filters): + """ Adds more filters to the existing list of error-message filters. """ for filt in filters.split(','): clean_filt = filt.strip() if clean_filt: @@ -550,10 +877,13 @@ def SetFilters(self, filters): raise ValueError('Every filter in --filters must start with + or -' ' (%s does not)' % filt) - def setOutputFile(self, filename): - """attempts to create a file which we write output to.""" - fh = logging.FileHandler(filename, mode='wb') - logger.addHandler(fh) + def BackupFilters(self): + """ Saves the current filter list to backup storage.""" + self._filters_backup = self.filters[:] + + def RestoreFilters(self): + """ Restores filters previously backed up.""" + self.filters = self._filters_backup[:] def ResetErrorCounts(self): """Sets the module's error statistic back to zero.""" @@ -572,11 +902,10 @@ def IncrementErrorCount(self, category): def PrintErrorCounts(self): """Print a summary of errors by category, and the total.""" - if not _cpplint_state.output_format == 'tap': - for category, count in self.errors_by_category.iteritems(): - sys.stderr.write('Category \'%s\' errors found: %d\n' % - (category, count)) - sys.stderr.write('Total errors found: %d\n' % self.error_count) + for category, count in self.errors_by_category.iteritems(): + sys.stderr.write('Category \'%s\' errors found: %d\n' % + (category, count)) + sys.stderr.write('Total errors found: %d\n' % self.error_count) _cpplint_state = _CppLintState() @@ -623,9 +952,25 @@ def _SetFilters(filters): """ _cpplint_state.SetFilters(filters) -def _setOutputFile(filename): - _cpplint_state.setOutputFile(filename) +def _AddFilters(filters): + """Adds more filter overrides. + Unlike _SetFilters, this function does not reset the current list of filters + available. + + Args: + filters: A string of comma-separated filters (eg "whitespace/indent"). + Each filter should start with + or -; else we die. + """ + _cpplint_state.AddFilters(filters) + +def _BackupFilters(): + """ Saves the current filter list to backup storage.""" + _cpplint_state.BackupFilters() + +def _RestoreFilters(): + """ Restores filters previously backed up.""" + _cpplint_state.RestoreFilters() class _FunctionState(object): """Tracks current function name and the number of lines in its body.""" @@ -661,6 +1006,9 @@ def Check(self, error, filename, linenum): filename: The name of the current file. linenum: The number of the line to check. """ + if not self.in_a_function: + return + if Match(r'T(EST|est)', self.current_function): base_trigger = self._TEST_TRIGGER else: @@ -679,7 +1027,7 @@ def Check(self, error, filename, linenum): self.current_function, self.lines_in_function, trigger)) def End(self): - """Stop analizing function body.""" + """Stop analyzing function body.""" self.in_a_function = False @@ -688,7 +1036,7 @@ class _IncludeError(Exception): pass -class FileInfo: +class FileInfo(object): """Provides utility functions for filenames. FileInfo provides easy access to the components of a file's path @@ -713,10 +1061,40 @@ def RepositoryName(self): locations won't see bogus errors. """ fullname = self.FullName() - # XXX(bnoordhuis) Expects that cpplint.py lives in the tools/ directory. - toplevel = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) - prefix = os.path.commonprefix([fullname, toplevel]) - return fullname[len(prefix) + 1:] + + if os.path.exists(fullname): + project_dir = os.path.dirname(fullname) + + if os.path.exists(os.path.join(project_dir, ".svn")): + # If there's a .svn file in the current directory, we recursively look + # up the directory tree for the top of the SVN checkout + root_dir = project_dir + one_up_dir = os.path.dirname(root_dir) + while os.path.exists(os.path.join(one_up_dir, ".svn")): + root_dir = os.path.dirname(root_dir) + one_up_dir = os.path.dirname(one_up_dir) + + prefix = os.path.commonprefix([root_dir, project_dir]) + return fullname[len(prefix) + 1:] + + # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by + # searching up from the current path. + root_dir = current_dir = os.path.dirname(fullname) + while current_dir != os.path.dirname(current_dir): + if (os.path.exists(os.path.join(current_dir, ".git")) or + os.path.exists(os.path.join(current_dir, ".hg")) or + os.path.exists(os.path.join(current_dir, ".svn"))): + root_dir = current_dir + current_dir = os.path.dirname(current_dir) + + if (os.path.exists(os.path.join(root_dir, ".git")) or + os.path.exists(os.path.join(root_dir, ".hg")) or + os.path.exists(os.path.join(root_dir, ".svn"))): + prefix = os.path.commonprefix([root_dir, project_dir]) + return fullname[len(prefix) + 1:] + + # Don't know what to do; header guard warnings may be wrong... + return fullname def Split(self): """Splits the file into the directory, basename, and extension. @@ -746,18 +1124,18 @@ def NoExtension(self): def IsSource(self): """File has a source file extension.""" - return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx') + return _IsSourceExtension(self.Extension()[1:]) def _ShouldPrintError(category, confidence, linenum): - """Returns true iff confidence >= verbose, category passes - filter and is not NOLINT-suppressed.""" + """If confidence >= verbose, category passes filter and is not suppressed.""" # There are three ways we might decide not to print an error message: # a "NOLINT(category)" comment appears in the source, # the verbosity level isn't high enough, or the filters filter it out. if IsErrorSuppressedByNolint(category, linenum): return False + if confidence < _cpplint_state.verbose_level: return False @@ -804,28 +1182,20 @@ def Error(filename, linenum, category, confidence, message): if _cpplint_state.output_format == 'vs7': sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence)) - elif _cpplint_state.output_format == 'tap': - template = ('not ok %s\n' - ' ---\n' - ' message: %s\n' - ' data:\n' - ' line: %d\n' - ' ruleId: %s\n' - ' ...') - logger.info(template % (filename, message, linenum, category)) + elif _cpplint_state.output_format == 'eclipse': + sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) else: sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence)) -# Matches standard C++ escape esequences per 2.13.2.3 of the C++ standard. +# Matches standard C++ escape sequences per 2.13.2.3 of the C++ standard. _RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') -# Matches strings. Escape codes should already be removed by ESCAPES. -_RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES = re.compile(r'"[^"]*"') -# Matches characters. Escape codes should already be removed by ESCAPES. -_RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES = re.compile(r"'.'") -# Matches multi-line C++ comments. +# Match a single C style comment on the same line. +_RE_PATTERN_C_COMMENTS = r'/\*(?:[^*]|\*(?!/))*\*/' +# Matches multi-line C style comments. # This RE is a little bit more complicated than one might expect, because we # have to take care of space removals tools so we can handle comments inside # statements better. @@ -834,10 +1204,10 @@ def Error(filename, linenum, category, confidence, message): # if this doesn't work we try on left side but only if there's a non-character # on the right. _RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( - r"""(\s*/\*.*\*/\s*$| - /\*.*\*/\s+| - \s+/\*.*\*/(?=\W)| - /\*.*\*/)""", re.VERBOSE) + r'(\s*' + _RE_PATTERN_C_COMMENTS + r'\s*$|' + + _RE_PATTERN_C_COMMENTS + r'\s+|' + + r'\s+' + _RE_PATTERN_C_COMMENTS + r'(?=\W)|' + + _RE_PATTERN_C_COMMENTS + r')') def IsCppString(line): @@ -857,6 +1227,82 @@ def IsCppString(line): return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 +def CleanseRawStrings(raw_lines): + """Removes C++11 raw strings from lines. + + Before: + static const char kData[] = R"( + multi-line string + )"; + + After: + static const char kData[] = "" + (replaced by blank line) + ""; + + Args: + raw_lines: list of raw lines. + + Returns: + list of lines with C++11 raw strings replaced by empty strings. + """ + + delimiter = None + lines_without_raw_strings = [] + for line in raw_lines: + if delimiter: + # Inside a raw string, look for the end + end = line.find(delimiter) + if end >= 0: + # Found the end of the string, match leading space for this + # line and resume copying the original lines, and also insert + # a "" on the last line. + leading_space = Match(r'^(\s*)\S', line) + line = leading_space.group(1) + '""' + line[end + len(delimiter):] + delimiter = None + else: + # Haven't found the end yet, append a blank line. + line = '""' + + # Look for beginning of a raw string, and replace them with + # empty strings. This is done in a loop to handle multiple raw + # strings on the same line. + while delimiter is None: + # Look for beginning of a raw string. + # See 2.14.15 [lex.string] for syntax. + # + # Once we have matched a raw string, we check the prefix of the + # line to make sure that the line is not part of a single line + # comment. It's done this way because we remove raw strings + # before removing comments as opposed to removing comments + # before removing raw strings. This is because there are some + # cpplint checks that requires the comments to be preserved, but + # we don't want to check comments that are inside raw strings. + matched = Match(r'^(.*?)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line) + if (matched and + not Match(r'^([^\'"]|\'(\\.|[^\'])*\'|"(\\.|[^"])*")*//', + matched.group(1))): + delimiter = ')' + matched.group(2) + '"' + + end = matched.group(3).find(delimiter) + if end >= 0: + # Raw string ended on same line + line = (matched.group(1) + '""' + + matched.group(3)[end + len(delimiter):]) + delimiter = None + else: + # Start of a multi-line raw string + line = matched.group(1) + '""' + else: + break + + lines_without_raw_strings.append(line) + + # TODO(unknown): if delimiter is not None here, we might want to + # emit a warning for unterminated string. + return lines_without_raw_strings + + def FindNextMultiLineCommentStart(lines, lineix): """Find the beginning marker for a multiline comment.""" while lineix < len(lines): @@ -882,7 +1328,7 @@ def RemoveMultiLineCommentsFromRange(lines, begin, end): # Having // dummy comments makes the lines non-empty, so we will not get # unnecessary blank line warnings later in the code. for i in range(begin, end): - lines[i] = '// dummy' + lines[i] = '/**/' def RemoveMultiLineComments(filename, lines, error): @@ -912,18 +1358,20 @@ def CleanseComments(line): """ commentpos = line.find('//') if commentpos != -1 and not IsCppString(line[:commentpos]): - line = line[:commentpos] + line = line[:commentpos].rstrip() # get rid of /* ... */ return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) class CleansedLines(object): - """Holds 3 copies of all lines with different preprocessing applied to them. - - 1) elided member contains lines without strings and comments, - 2) lines member contains lines without comments, and - 3) raw member contains all the lines without processing. - All these three members are of <type 'list'>, and of the same length. + """Holds 4 copies of all lines with different preprocessing applied to them. + + 1) elided member contains lines without strings and comments. + 2) lines member contains lines without comments. + 3) raw_lines member contains all the lines without processing. + 4) lines_without_raw_strings member is same as raw_lines, but with C++11 raw + strings removed. + All these members are of <type 'list'>, and of the same length. """ def __init__(self, lines): @@ -931,9 +1379,11 @@ def __init__(self, lines): self.lines = [] self.raw_lines = lines self.num_lines = len(lines) - for linenum in range(len(lines)): - self.lines.append(CleanseComments(lines[linenum])) - elided = self._CollapseStrings(lines[linenum]) + self.lines_without_raw_strings = CleanseRawStrings(lines) + for linenum in range(len(self.lines_without_raw_strings)): + self.lines.append(CleanseComments( + self.lines_without_raw_strings[linenum])) + elided = self._CollapseStrings(self.lines_without_raw_strings[linenum]) self.elided.append(CleanseComments(elided)) def NumLines(self): @@ -952,22 +1402,151 @@ def _CollapseStrings(elided): Returns: The line with collapsed strings. """ - if not _RE_PATTERN_INCLUDE.match(elided): - # Remove escaped characters first to make quote/single quote collapsing - # basic. Things that look like escaped characters shouldn't occur - # outside of strings and chars. - elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) - elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided) - elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided) - return elided + if _RE_PATTERN_INCLUDE.match(elided): + return elided + + # Remove escaped characters first to make quote/single quote collapsing + # basic. Things that look like escaped characters shouldn't occur + # outside of strings and chars. + elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) + + # Replace quoted strings and digit separators. Both single quotes + # and double quotes are processed in the same loop, otherwise + # nested quotes wouldn't work. + collapsed = '' + while True: + # Find the first quote character + match = Match(r'^([^\'"]*)([\'"])(.*)$', elided) + if not match: + collapsed += elided + break + head, quote, tail = match.groups() + + if quote == '"': + # Collapse double quoted strings + second_quote = tail.find('"') + if second_quote >= 0: + collapsed += head + '""' + elided = tail[second_quote + 1:] + else: + # Unmatched double quote, don't bother processing the rest + # of the line since this is probably a multiline string. + collapsed += elided + break + else: + # Found single quote, check nearby text to eliminate digit separators. + # + # There is no special handling for floating point here, because + # the integer/fractional/exponent parts would all be parsed + # correctly as long as there are digits on both sides of the + # separator. So we are fine as long as we don't see something + # like "0.'3" (gcc 4.9.0 will not allow this literal). + if Search(r'\b(?:0[bBxX]?|[1-9])[0-9a-fA-F]*$', head): + match_literal = Match(r'^((?:\'?[0-9a-zA-Z_])*)(.*)$', "'" + tail) + collapsed += head + match_literal.group(1).replace("'", '') + elided = match_literal.group(2) + else: + second_quote = tail.find('\'') + if second_quote >= 0: + collapsed += head + "''" + elided = tail[second_quote + 1:] + else: + # Unmatched single quote + collapsed += elided + break + + return collapsed + + +def FindEndOfExpressionInLine(line, startpos, stack): + """Find the position just after the end of current parenthesized expression. + + Args: + line: a CleansedLines line. + startpos: start searching at this position. + stack: nesting stack at startpos. + + Returns: + On finding matching end: (index just after matching end, None) + On finding an unclosed expression: (-1, None) + Otherwise: (-1, new stack at end of this line) + """ + for i in xrange(startpos, len(line)): + char = line[i] + if char in '([{': + # Found start of parenthesized expression, push to expression stack + stack.append(char) + elif char == '<': + # Found potential start of template argument list + if i > 0 and line[i - 1] == '<': + # Left shift operator + if stack and stack[-1] == '<': + stack.pop() + if not stack: + return (-1, None) + elif i > 0 and Search(r'\boperator\s*$', line[0:i]): + # operator<, don't add to stack + continue + else: + # Tentative start of template argument list + stack.append('<') + elif char in ')]}': + # Found end of parenthesized expression. + # + # If we are currently expecting a matching '>', the pending '<' + # must have been an operator. Remove them from expression stack. + while stack and stack[-1] == '<': + stack.pop() + if not stack: + return (-1, None) + if ((stack[-1] == '(' and char == ')') or + (stack[-1] == '[' and char == ']') or + (stack[-1] == '{' and char == '}')): + stack.pop() + if not stack: + return (i + 1, None) + else: + # Mismatched parentheses + return (-1, None) + elif char == '>': + # Found potential end of template argument list. + + # Ignore "->" and operator functions + if (i > 0 and + (line[i - 1] == '-' or Search(r'\boperator\s*$', line[0:i - 1]))): + continue + + # Pop the stack if there is a matching '<'. Otherwise, ignore + # this '>' since it must be an operator. + if stack: + if stack[-1] == '<': + stack.pop() + if not stack: + return (i + 1, None) + elif char == ';': + # Found something that look like end of statements. If we are currently + # expecting a '>', the matching '<' must have been an operator, since + # template argument list should not contain statements. + while stack and stack[-1] == '<': + stack.pop() + if not stack: + return (-1, None) + + # Did not find end of expression or unbalanced parentheses on this line + return (-1, stack) def CloseExpression(clean_lines, linenum, pos): - """If input points to ( or { or [, finds the position that closes it. + """If input points to ( or { or [ or <, finds the position that closes it. - If lines[linenum][pos] points to a '(' or '{' or '[', finds the the + If lines[linenum][pos] points to a '(' or '{' or '[' or '<', finds the linenum/pos that correspond to the closing of the expression. + TODO(unknown): cpplint spends a fair bit of time matching parentheses. + Ideally we would want to index all opening and closing parentheses once + and have CloseExpression be just a simple lookup, but due to preprocessor + tricks, this is not so easy. + Args: clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. @@ -981,24 +1560,139 @@ def CloseExpression(clean_lines, linenum, pos): """ line = clean_lines.elided[linenum] - startchar = line[pos] - if startchar not in '({[': + if (line[pos] not in '({[<') or Match(r'<[<=]', line[pos:]): return (line, clean_lines.NumLines(), -1) - if startchar == '(': endchar = ')' - if startchar == '[': endchar = ']' - if startchar == '{': endchar = '}' - num_open = line.count(startchar) - line.count(endchar) - while linenum < clean_lines.NumLines() and num_open > 0: + # Check first line + (end_pos, stack) = FindEndOfExpressionInLine(line, pos, []) + if end_pos > -1: + return (line, linenum, end_pos) + + # Continue scanning forward + while stack and linenum < clean_lines.NumLines() - 1: linenum += 1 line = clean_lines.elided[linenum] - num_open += line.count(startchar) - line.count(endchar) - # OK, now find the endchar that actually got us back to even - endpos = len(line) - while num_open >= 0: - endpos = line.rfind(')', 0, endpos) - num_open -= 1 # chopped off another ) - return (line, linenum, endpos + 1) + (end_pos, stack) = FindEndOfExpressionInLine(line, 0, stack) + if end_pos > -1: + return (line, linenum, end_pos) + + # Did not find end of expression before end of file, give up + return (line, clean_lines.NumLines(), -1) + + +def FindStartOfExpressionInLine(line, endpos, stack): + """Find position at the matching start of current expression. + + This is almost the reverse of FindEndOfExpressionInLine, but note + that the input position and returned position differs by 1. + + Args: + line: a CleansedLines line. + endpos: start searching at this position. + stack: nesting stack at endpos. + + Returns: + On finding matching start: (index at matching start, None) + On finding an unclosed expression: (-1, None) + Otherwise: (-1, new stack at beginning of this line) + """ + i = endpos + while i >= 0: + char = line[i] + if char in ')]}': + # Found end of expression, push to expression stack + stack.append(char) + elif char == '>': + # Found potential end of template argument list. + # + # Ignore it if it's a "->" or ">=" or "operator>" + if (i > 0 and + (line[i - 1] == '-' or + Match(r'\s>=\s', line[i - 1:]) or + Search(r'\boperator\s*$', line[0:i]))): + i -= 1 + else: + stack.append('>') + elif char == '<': + # Found potential start of template argument list + if i > 0 and line[i - 1] == '<': + # Left shift operator + i -= 1 + else: + # If there is a matching '>', we can pop the expression stack. + # Otherwise, ignore this '<' since it must be an operator. + if stack and stack[-1] == '>': + stack.pop() + if not stack: + return (i, None) + elif char in '([{': + # Found start of expression. + # + # If there are any unmatched '>' on the stack, they must be + # operators. Remove those. + while stack and stack[-1] == '>': + stack.pop() + if not stack: + return (-1, None) + if ((char == '(' and stack[-1] == ')') or + (char == '[' and stack[-1] == ']') or + (char == '{' and stack[-1] == '}')): + stack.pop() + if not stack: + return (i, None) + else: + # Mismatched parentheses + return (-1, None) + elif char == ';': + # Found something that look like end of statements. If we are currently + # expecting a '<', the matching '>' must have been an operator, since + # template argument list should not contain statements. + while stack and stack[-1] == '>': + stack.pop() + if not stack: + return (-1, None) + + i -= 1 + + return (-1, stack) + + +def ReverseCloseExpression(clean_lines, linenum, pos): + """If input points to ) or } or ] or >, finds the position that opens it. + + If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the + linenum/pos that correspond to the opening of the expression. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + + Returns: + A tuple (line, linenum, pos) pointer *at* the opening brace, or + (line, 0, -1) if we never find the matching opening brace. Note + we ignore strings and comments when matching; and the line we + return is the 'cleansed' line at linenum. + """ + line = clean_lines.elided[linenum] + if line[pos] not in ')}]>': + return (line, 0, -1) + + # Check last line + (start_pos, stack) = FindStartOfExpressionInLine(line, pos, []) + if start_pos > -1: + return (line, linenum, start_pos) + + # Continue scanning backward + while stack and linenum > 0: + linenum -= 1 + line = clean_lines.elided[linenum] + (start_pos, stack) = FindStartOfExpressionInLine(line, len(line) - 1, stack) + if start_pos > -1: + return (line, linenum, start_pos) + + # Did not find start of expression before beginning of file, give up + return (line, 0, -1) def CheckForCopyright(filename, lines, error): @@ -1014,6 +1708,22 @@ def CheckForCopyright(filename, lines, error): 'You should have a line: "Copyright [year] <Copyright Owner>"') +def GetIndentLevel(line): + """Return the number of leading spaces in line. + + Args: + line: A string to check. + + Returns: + An integer count of leading spaces, possibly zero. + """ + indent = Match(r'^( *)\S', line) + if indent: + return len(indent.group(1)) + else: + return 0 + + def GetHeaderGuardCPPVariable(filename): """Returns the CPP variable that should be used as a header guard. @@ -1029,12 +1739,18 @@ def GetHeaderGuardCPPVariable(filename): # Restores original filename in case that cpplint is invoked from Emacs's # flymake. filename = re.sub(r'_flymake\.h$', '.h', filename) + filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename) + # Replace 'c++' with 'cpp'. + filename = filename.replace('C++', 'cpp').replace('c++', 'cpp') fileinfo = FileInfo(filename) - return re.sub(r'[-./\s]', '_', fileinfo.RepositoryName()).upper() + '_' + file_path_from_root = fileinfo.RepositoryName() + if _root: + file_path_from_root = re.sub('^' + _root + os.sep, '', file_path_from_root) + return re.sub(r'[^a-zA-Z0-9]', '_', file_path_from_root).upper() + '_' -def CheckForHeaderGuard(filename, lines, error): +def CheckForHeaderGuard(filename, clean_lines, error): """Checks that the file contains a header guard. Logs an error if no #ifndef header guard is present. For other @@ -1042,18 +1758,29 @@ def CheckForHeaderGuard(filename, lines, error): Args: filename: The name of the C++ header file. - lines: An array of strings, each representing a line of the file. + clean_lines: A CleansedLines instance containing the file. error: The function to call with any errors found. """ + # Don't check for header guards if there are error suppression + # comments somewhere in this file. + # + # Because this is silencing a warning for a nonexistent line, we + # only support the very specific NOLINT(build/header_guard) syntax, + # and not the general NOLINT or NOLINT(*) syntax. + raw_lines = clean_lines.lines_without_raw_strings + for i in raw_lines: + if Search(r'//\s*NOLINT\(build/header_guard\)', i): + return + cppvar = GetHeaderGuardCPPVariable(filename) - ifndef = None + ifndef = '' ifndef_linenum = 0 - define = None - endif = None + define = '' + endif = '' endif_linenum = 0 - for linenum, line in enumerate(lines): + for linenum, line in enumerate(raw_lines): linesplit = line.split() if len(linesplit) >= 2: # find the first occurrence of #ifndef and #define, save arg @@ -1081,31 +1808,82 @@ def CheckForHeaderGuard(filename, lines, error): if ifndef != cppvar + '_': error_level = 5 - ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum, + ParseNolintSuppressions(filename, raw_lines[ifndef_linenum], ifndef_linenum, error) error(filename, ifndef_linenum, 'build/header_guard', error_level, '#ifndef header guard has wrong style, please use: %s' % cppvar) - if (endif != ('#endif // %s' % cppvar) and - endif != ('#endif /* %s */' % cppvar)): - error_level = 0 - if (endif != ('#endif // %s' % (cppvar + '_')) and - endif != ('#endif /* %s */' % (cppvar + '_'))): - error_level = 5 + # Check for "//" comments on endif line. + ParseNolintSuppressions(filename, raw_lines[endif_linenum], endif_linenum, + error) + match = Match(r'#endif\s*//\s*' + cppvar + r'(_)?\b', endif) + if match: + if match.group(1) == '_': + # Issue low severity warning for deprecated double trailing underscore + error(filename, endif_linenum, 'build/header_guard', 0, + '#endif line should be "#endif // %s"' % cppvar) + return - ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum, - error) - error(filename, endif_linenum, 'build/header_guard', error_level, - '#endif line should be "#endif // %s"' % cppvar) + # Didn't find the corresponding "//" comment. If this file does not + # contain any "//" comments at all, it could be that the compiler + # only wants "/**/" comments, look for those instead. + no_single_line_comments = True + for i in xrange(1, len(raw_lines) - 1): + line = raw_lines[i] + if Match(r'^(?:(?:\'(?:\.|[^\'])*\')|(?:"(?:\.|[^"])*")|[^\'"])*//', line): + no_single_line_comments = False + break + + if no_single_line_comments: + match = Match(r'#endif\s*/\*\s*' + cppvar + r'(_)?\s*\*/', endif) + if match: + if match.group(1) == '_': + # Low severity warning for double trailing underscore + error(filename, endif_linenum, 'build/header_guard', 0, + '#endif line should be "#endif /* %s */"' % cppvar) + return + + # Didn't find anything + error(filename, endif_linenum, 'build/header_guard', 5, + '#endif line should be "#endif // %s"' % cppvar) + + +def CheckHeaderFileIncluded(filename, include_state, error): + """Logs an error if a .cc file does not include its header.""" + + # Do not check test files + fileinfo = FileInfo(filename) + if Search(_TEST_FILE_SUFFIX, fileinfo.BaseName()): + return + + headerfile = filename[0:len(filename) - len(fileinfo.Extension())] + '.h' + if not os.path.exists(headerfile): + return + headername = FileInfo(headerfile).RepositoryName() + first_include = 0 + for section_list in include_state.include_list: + for f in section_list: + if headername in f[0] or f[0] in headername: + return + if not first_include: + first_include = f[1] + + error(filename, first_include, 'build/include', 5, + '%s should include its header file %s' % (fileinfo.RepositoryName(), + headername)) + + +def CheckForBadCharacters(filename, lines, error): + """Logs an error for each line containing bad characters. + Two kinds of bad characters: -def CheckForUnicodeReplacementCharacters(filename, lines, error): - """Logs an error for each line containing Unicode replacement characters. + 1. Unicode replacement characters: These indicate that either the file + contained invalid UTF-8 (likely) or Unicode replacement characters (which + it shouldn't). Note that it's possible for this to throw off line + numbering if the invalid UTF-8 occurred adjacent to a newline. - These indicate that either the file contained invalid UTF-8 (likely) - or Unicode replacement characters (which it shouldn't). Note that - it's possible for this to throw off line numbering if the invalid - UTF-8 occurred adjacent to a newline. + 2. NUL bytes. These are problematic for some tools. Args: filename: The name of the current file. @@ -1116,6 +1894,8 @@ def CheckForUnicodeReplacementCharacters(filename, lines, error): if u'\ufffd' in line: error(filename, linenum, 'readability/utf8', 5, 'Line contains invalid UTF-8 (or Unicode replacement character).') + if '\0' in line: + error(filename, linenum, 'readability/nul', 5, 'Line contains NUL byte.') def CheckForNewlineAtEOF(filename, lines, error): @@ -1170,24 +1950,37 @@ def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): if (line.count('"') - line.count('\\"')) % 2: error(filename, linenum, 'readability/multiline_string', 5, 'Multi-line string ("...") found. This lint script doesn\'t ' - 'do well with such strings, and may give bogus warnings. They\'re ' - 'ugly and unnecessary, and you should use concatenation instead".') - - -threading_list = ( - ('asctime(', 'asctime_r('), - ('ctime(', 'ctime_r('), - ('getgrgid(', 'getgrgid_r('), - ('getgrnam(', 'getgrnam_r('), - ('getlogin(', 'getlogin_r('), - ('getpwnam(', 'getpwnam_r('), - ('getpwuid(', 'getpwuid_r('), - ('gmtime(', 'gmtime_r('), - ('localtime(', 'localtime_r('), - ('rand(', 'rand_r('), - ('readdir(', 'readdir_r('), - ('strtok(', 'strtok_r('), - ('ttyname(', 'ttyname_r('), + 'do well with such strings, and may give bogus warnings. ' + 'Use C++11 raw strings or concatenation instead.') + + +# (non-threadsafe name, thread-safe alternative, validation pattern) +# +# The validation pattern is used to eliminate false positives such as: +# _rand(); // false positive due to substring match. +# ->rand(); // some member function rand(). +# ACMRandom rand(seed); // some variable named rand. +# ISAACRandom rand(); // another variable named rand. +# +# Basically we require the return value of these functions to be used +# in some expression context on the same line by matching on some +# operator before the function name. This eliminates constructors and +# member function calls. +_UNSAFE_FUNC_PREFIX = r'(?:[-+*/=%^&|(<]\s*|>\s+)' +_THREADING_LIST = ( + ('asctime(', 'asctime_r(', _UNSAFE_FUNC_PREFIX + r'asctime\([^)]+\)'), + ('ctime(', 'ctime_r(', _UNSAFE_FUNC_PREFIX + r'ctime\([^)]+\)'), + ('getgrgid(', 'getgrgid_r(', _UNSAFE_FUNC_PREFIX + r'getgrgid\([^)]+\)'), + ('getgrnam(', 'getgrnam_r(', _UNSAFE_FUNC_PREFIX + r'getgrnam\([^)]+\)'), + ('getlogin(', 'getlogin_r(', _UNSAFE_FUNC_PREFIX + r'getlogin\(\)'), + ('getpwnam(', 'getpwnam_r(', _UNSAFE_FUNC_PREFIX + r'getpwnam\([^)]+\)'), + ('getpwuid(', 'getpwuid_r(', _UNSAFE_FUNC_PREFIX + r'getpwuid\([^)]+\)'), + ('gmtime(', 'gmtime_r(', _UNSAFE_FUNC_PREFIX + r'gmtime\([^)]+\)'), + ('localtime(', 'localtime_r(', _UNSAFE_FUNC_PREFIX + r'localtime\([^)]+\)'), + ('rand(', 'rand_r(', _UNSAFE_FUNC_PREFIX + r'rand\(\)'), + ('strtok(', 'strtok_r(', + _UNSAFE_FUNC_PREFIX + r'strtok\([^)]+\)'), + ('ttyname(', 'ttyname_r(', _UNSAFE_FUNC_PREFIX + r'ttyname\([^)]+\)'), ) @@ -1207,17 +2000,34 @@ def CheckPosixThreading(filename, clean_lines, linenum, error): error: The function to call with any errors found. """ line = clean_lines.elided[linenum] - for single_thread_function, multithread_safe_function in threading_list: - ix = line.find(single_thread_function) - # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 - if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and - line[ix - 1] not in ('_', '.', '>'))): + for single_thread_func, multithread_safe_func, pattern in _THREADING_LIST: + # Additional pattern matching check to confirm that this is the + # function we are looking for + if Search(pattern, line): error(filename, linenum, 'runtime/threadsafe_fn', 2, - 'Consider using ' + multithread_safe_function + - '...) instead of ' + single_thread_function + + 'Consider using ' + multithread_safe_func + + '...) instead of ' + single_thread_func + '...) for improved thread safety.') +def CheckVlogArguments(filename, clean_lines, linenum, error): + """Checks that VLOG() is only used for defining a logging level. + + For example, VLOG(2) is correct. VLOG(INFO), VLOG(WARNING), VLOG(ERROR), and + VLOG(FATAL) are not. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + if Search(r'\bVLOG\((INFO|ERROR|WARNING|DFATAL|FATAL)\)', line): + error(filename, linenum, 'runtime/vlog', 5, + 'VLOG() should be used with numeric verbosity level. ' + 'Use LOG() if you want symbolic severity levels.') + # Matches invalid increment: *count++, which moves pointer instead of # incrementing a value. _RE_PATTERN_INVALID_INCREMENT = re.compile( @@ -1246,75 +2056,620 @@ def CheckInvalidIncrement(filename, clean_lines, linenum, error): 'Changing pointer instead of value (or unused value of operator*).') -class _ClassInfo(object): - """Stores information about a class.""" +def IsMacroDefinition(clean_lines, linenum): + if Search(r'^#define', clean_lines[linenum]): + return True - def __init__(self, name, linenum): - self.name = name - self.linenum = linenum - self.seen_open_brace = False - self.is_derived = False - self.virtual_method_linenumber = None - self.has_virtual_destructor = False - self.brace_depth = 0 + if linenum > 0 and Search(r'\\$', clean_lines[linenum - 1]): + return True + return False -class _ClassState(object): - """Holds the current state of the parse relating to class declarations. - It maintains a stack of _ClassInfos representing the parser's guess - as to the current nesting of class declarations. The innermost class - is at the top (back) of the stack. Typically, the stack will either - be empty or have exactly one entry. - """ +def IsForwardClassDeclaration(clean_lines, linenum): + return Match(r'^\s*(\btemplate\b)*.*class\s+\w+;\s*$', clean_lines[linenum]) - def __init__(self): - self.classinfo_stack = [] - def CheckFinished(self, filename, error): - """Checks that all classes have been completely parsed. +class _BlockInfo(object): + """Stores information about a generic block of code.""" + + def __init__(self, linenum, seen_open_brace): + self.starting_linenum = linenum + self.seen_open_brace = seen_open_brace + self.open_parentheses = 0 + self.inline_asm = _NO_ASM + self.check_namespace_indentation = False + + def CheckBegin(self, filename, clean_lines, linenum, error): + """Run checks that applies to text up to the opening brace. + + This is mostly for checking the text after the class identifier + and the "{", usually where the base class is specified. For other + blocks, there isn't much to check, so we always pass. - Call this when all lines in a file have been processed. Args: filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. error: The function to call with any errors found. """ - if self.classinfo_stack: - # Note: This test can result in false positives if #ifdef constructs - # get in the way of brace matching. See the testBuildClass test in - # cpplint_unittest.py for an example of this. - error(filename, self.classinfo_stack[0].linenum, 'build/class', 5, - 'Failed to find complete declaration of class %s' % - self.classinfo_stack[0].name) - - -def CheckForNonStandardConstructs(filename, clean_lines, linenum, - class_state, error): - """Logs an error if we see certain non-ANSI constructs ignored by gcc-2. + pass - Complain about several constructs which gcc-2 accepts, but which are - not standard C++. Warning about these in lint is one way to ease the - transition to new compilers. - - put storage class first (e.g. "static const" instead of "const static"). - - "%lld" instead of %qd" in printf-type functions. - - "%1$d" is non-standard in printf-type functions. - - "\%" is an undefined character escape sequence. - - text after #endif is not allowed. - - invalid inner-style forward declaration. - - >? and <? operators, and their >?= and <?= cousins. - - classes with virtual methods need virtual destructors (compiler warning - available, but not turned on yet.) + def CheckEnd(self, filename, clean_lines, linenum, error): + """Run checks that applies to text after the closing brace. - Additionally, check for constructor/destructor style violations and reference - members, as it is very convenient to do so while checking for - gcc-2 compliance. + This is mostly used for checking end of namespace comments. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + pass + + def IsBlockInfo(self): + """Returns true if this block is a _BlockInfo. + + This is convenient for verifying that an object is an instance of + a _BlockInfo, but not an instance of any of the derived classes. + + Returns: + True for this class, False for derived classes. + """ + return self.__class__ == _BlockInfo + + +class _ExternCInfo(_BlockInfo): + """Stores information about an 'extern "C"' block.""" + + def __init__(self, linenum): + _BlockInfo.__init__(self, linenum, True) + + +class _ClassInfo(_BlockInfo): + """Stores information about a class.""" + + def __init__(self, name, class_or_struct, clean_lines, linenum): + _BlockInfo.__init__(self, linenum, False) + self.name = name + self.is_derived = False + self.check_namespace_indentation = True + if class_or_struct == 'struct': + self.access = 'public' + self.is_struct = True + else: + self.access = 'private' + self.is_struct = False + + # Remember initial indentation level for this class. Using raw_lines here + # instead of elided to account for leading comments. + self.class_indent = GetIndentLevel(clean_lines.raw_lines[linenum]) + + # Try to find the end of the class. This will be confused by things like: + # class A { + # } *x = { ... + # + # But it's still good enough for CheckSectionSpacing. + self.last_line = 0 + depth = 0 + for i in range(linenum, clean_lines.NumLines()): + line = clean_lines.elided[i] + depth += line.count('{') - line.count('}') + if not depth: + self.last_line = i + break + + def CheckBegin(self, filename, clean_lines, linenum, error): + # Look for a bare ':' + if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): + self.is_derived = True + + def CheckEnd(self, filename, clean_lines, linenum, error): + # If there is a DISALLOW macro, it should appear near the end of + # the class. + seen_last_thing_in_class = False + for i in xrange(linenum - 1, self.starting_linenum, -1): + match = Search( + r'\b(DISALLOW_COPY_AND_ASSIGN|DISALLOW_IMPLICIT_CONSTRUCTORS)\(' + + self.name + r'\)', + clean_lines.elided[i]) + if match: + if seen_last_thing_in_class: + error(filename, i, 'readability/constructors', 3, + match.group(1) + ' should be the last thing in the class') + break + + if not Match(r'^\s*$', clean_lines.elided[i]): + seen_last_thing_in_class = True + + # Check that closing brace is aligned with beginning of the class. + # Only do this if the closing brace is indented by only whitespaces. + # This means we will not check single-line class definitions. + indent = Match(r'^( *)\}', clean_lines.elided[linenum]) + if indent and len(indent.group(1)) != self.class_indent: + if self.is_struct: + parent = 'struct ' + self.name + else: + parent = 'class ' + self.name + error(filename, linenum, 'whitespace/indent', 3, + 'Closing brace should be aligned with beginning of %s' % parent) + + +class _NamespaceInfo(_BlockInfo): + """Stores information about a namespace.""" + + def __init__(self, name, linenum): + _BlockInfo.__init__(self, linenum, False) + self.name = name or '' + self.check_namespace_indentation = True + + def CheckEnd(self, filename, clean_lines, linenum, error): + """Check end of namespace comments.""" + line = clean_lines.raw_lines[linenum] + + # Check how many lines is enclosed in this namespace. Don't issue + # warning for missing namespace comments if there aren't enough + # lines. However, do apply checks if there is already an end of + # namespace comment and it's incorrect. + # + # TODO(unknown): We always want to check end of namespace comments + # if a namespace is large, but sometimes we also want to apply the + # check if a short namespace contained nontrivial things (something + # other than forward declarations). There is currently no logic on + # deciding what these nontrivial things are, so this check is + # triggered by namespace size only, which works most of the time. + if (linenum - self.starting_linenum < 10 + and not Match(r'^\s*};*\s*(//|/\*).*\bnamespace\b', line)): + return + + # Look for matching comment at end of namespace. + # + # Note that we accept C style "/* */" comments for terminating + # namespaces, so that code that terminate namespaces inside + # preprocessor macros can be cpplint clean. + # + # We also accept stuff like "// end of namespace <name>." with the + # period at the end. + # + # Besides these, we don't accept anything else, otherwise we might + # get false negatives when existing comment is a substring of the + # expected namespace. + if self.name: + # Named namespace + if not Match((r'^\s*};*\s*(//|/\*).*\bnamespace\s+' + + re.escape(self.name) + r'[\*/\.\\\s]*$'), + line): + error(filename, linenum, 'readability/namespace', 5, + 'Namespace should be terminated with "// namespace %s"' % + self.name) + else: + # Anonymous namespace + if not Match(r'^\s*};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line): + # If "// namespace anonymous" or "// anonymous namespace (more text)", + # mention "// anonymous namespace" as an acceptable form + if Match(r'^\s*}.*\b(namespace anonymous|anonymous namespace)\b', line): + error(filename, linenum, 'readability/namespace', 5, + 'Anonymous namespace should be terminated with "// namespace"' + ' or "// anonymous namespace"') + else: + error(filename, linenum, 'readability/namespace', 5, + 'Anonymous namespace should be terminated with "// namespace"') + + +class _PreprocessorInfo(object): + """Stores checkpoints of nesting stacks when #if/#else is seen.""" + + def __init__(self, stack_before_if): + # The entire nesting stack before #if + self.stack_before_if = stack_before_if + + # The entire nesting stack up to #else + self.stack_before_else = [] + + # Whether we have already seen #else or #elif + self.seen_else = False + + +class NestingState(object): + """Holds states related to parsing braces.""" + + def __init__(self): + # Stack for tracking all braces. An object is pushed whenever we + # see a "{", and popped when we see a "}". Only 3 types of + # objects are possible: + # - _ClassInfo: a class or struct. + # - _NamespaceInfo: a namespace. + # - _BlockInfo: some other type of block. + self.stack = [] + + # Top of the previous stack before each Update(). + # + # Because the nesting_stack is updated at the end of each line, we + # had to do some convoluted checks to find out what is the current + # scope at the beginning of the line. This check is simplified by + # saving the previous top of nesting stack. + # + # We could save the full stack, but we only need the top. Copying + # the full nesting stack would slow down cpplint by ~10%. + self.previous_stack_top = [] + + # Stack of _PreprocessorInfo objects. + self.pp_stack = [] + + def SeenOpenBrace(self): + """Check if we have seen the opening brace for the innermost block. + + Returns: + True if we have seen the opening brace, False if the innermost + block is still expecting an opening brace. + """ + return (not self.stack) or self.stack[-1].seen_open_brace + + def InNamespaceBody(self): + """Check if we are currently one level inside a namespace body. + + Returns: + True if top of the stack is a namespace block, False otherwise. + """ + return self.stack and isinstance(self.stack[-1], _NamespaceInfo) + + def InExternC(self): + """Check if we are currently one level inside an 'extern "C"' block. + + Returns: + True if top of the stack is an extern block, False otherwise. + """ + return self.stack and isinstance(self.stack[-1], _ExternCInfo) + + def InClassDeclaration(self): + """Check if we are currently one level inside a class or struct declaration. + + Returns: + True if top of the stack is a class/struct, False otherwise. + """ + return self.stack and isinstance(self.stack[-1], _ClassInfo) + + def InAsmBlock(self): + """Check if we are currently one level inside an inline ASM block. + + Returns: + True if the top of the stack is a block containing inline ASM. + """ + return self.stack and self.stack[-1].inline_asm != _NO_ASM + + def InTemplateArgumentList(self, clean_lines, linenum, pos): + """Check if current position is inside template argument list. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: position just after the suspected template argument. + Returns: + True if (linenum, pos) is inside template arguments. + """ + while linenum < clean_lines.NumLines(): + # Find the earliest character that might indicate a template argument + line = clean_lines.elided[linenum] + match = Match(r'^[^{};=\[\]\.<>]*(.)', line[pos:]) + if not match: + linenum += 1 + pos = 0 + continue + token = match.group(1) + pos += len(match.group(0)) + + # These things do not look like template argument list: + # class Suspect { + # class Suspect x; } + if token in ('{', '}', ';'): return False + + # These things look like template argument list: + # template <class Suspect> + # template <class Suspect = default_value> + # template <class Suspect[]> + # template <class Suspect...> + if token in ('>', '=', '[', ']', '.'): return True + + # Check if token is an unmatched '<'. + # If not, move on to the next character. + if token != '<': + pos += 1 + if pos >= len(line): + linenum += 1 + pos = 0 + continue + + # We can't be sure if we just find a single '<', and need to + # find the matching '>'. + (_, end_line, end_pos) = CloseExpression(clean_lines, linenum, pos - 1) + if end_pos < 0: + # Not sure if template argument list or syntax error in file + return False + linenum = end_line + pos = end_pos + return False + + def UpdatePreprocessor(self, line): + """Update preprocessor stack. + + We need to handle preprocessors due to classes like this: + #ifdef SWIG + struct ResultDetailsPageElementExtensionPoint { + #else + struct ResultDetailsPageElementExtensionPoint : public Extension { + #endif + + We make the following assumptions (good enough for most files): + - Preprocessor condition evaluates to true from #if up to first + #else/#elif/#endif. + + - Preprocessor condition evaluates to false from #else/#elif up + to #endif. We still perform lint checks on these lines, but + these do not affect nesting stack. + + Args: + line: current line to check. + """ + if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): + # Beginning of #if block, save the nesting stack here. The saved + # stack will allow us to restore the parsing state in the #else case. + self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) + elif Match(r'^\s*#\s*(else|elif)\b', line): + # Beginning of #else block + if self.pp_stack: + if not self.pp_stack[-1].seen_else: + # This is the first #else or #elif block. Remember the + # whole nesting stack up to this point. This is what we + # keep after the #endif. + self.pp_stack[-1].seen_else = True + self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack) + + # Restore the stack to how it was before the #if + self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) + else: + # TODO(unknown): unexpected #else, issue warning? + pass + elif Match(r'^\s*#\s*endif\b', line): + # End of #if or #else blocks. + if self.pp_stack: + # If we saw an #else, we will need to restore the nesting + # stack to its former state before the #else, otherwise we + # will just continue from where we left off. + if self.pp_stack[-1].seen_else: + # Here we can just use a shallow copy since we are the last + # reference to it. + self.stack = self.pp_stack[-1].stack_before_else + # Drop the corresponding #if + self.pp_stack.pop() + else: + # TODO(unknown): unexpected #endif, issue warning? + pass + + # TODO(unknown): Update() is too long, but we will refactor later. + def Update(self, filename, clean_lines, linenum, error): + """Update nesting state with current line. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Remember top of the previous nesting stack. + # + # The stack is always pushed/popped and not modified in place, so + # we can just do a shallow copy instead of copy.deepcopy. Using + # deepcopy would slow down cpplint by ~28%. + if self.stack: + self.previous_stack_top = self.stack[-1] + else: + self.previous_stack_top = None + + # Update pp_stack + self.UpdatePreprocessor(line) + + # Count parentheses. This is to avoid adding struct arguments to + # the nesting stack. + if self.stack: + inner_block = self.stack[-1] + depth_change = line.count('(') - line.count(')') + inner_block.open_parentheses += depth_change + + # Also check if we are starting or ending an inline assembly block. + if inner_block.inline_asm in (_NO_ASM, _END_ASM): + if (depth_change != 0 and + inner_block.open_parentheses == 1 and + _MATCH_ASM.match(line)): + # Enter assembly block + inner_block.inline_asm = _INSIDE_ASM + else: + # Not entering assembly block. If previous line was _END_ASM, + # we will now shift to _NO_ASM state. + inner_block.inline_asm = _NO_ASM + elif (inner_block.inline_asm == _INSIDE_ASM and + inner_block.open_parentheses == 0): + # Exit assembly block + inner_block.inline_asm = _END_ASM + + # Consume namespace declaration at the beginning of the line. Do + # this in a loop so that we catch same line declarations like this: + # namespace proto2 { namespace bridge { class MessageSet; } } + while True: + # Match start of namespace. The "\b\s*" below catches namespace + # declarations even if it weren't followed by a whitespace, this + # is so that we don't confuse our namespace checker. The + # missing spaces will be flagged by CheckSpacing. + namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) + if not namespace_decl_match: + break + + new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum) + self.stack.append(new_namespace) + + line = namespace_decl_match.group(2) + if line.find('{') != -1: + new_namespace.seen_open_brace = True + line = line[line.find('{') + 1:] + + # Look for a class declaration in whatever is left of the line + # after parsing namespaces. The regexp accounts for decorated classes + # such as in: + # class LOCKABLE API Object { + # }; + class_decl_match = Match( + r'^(\s*(?:template\s*<[\w\s<>,:]*>\s*)?' + r'(class|struct)\s+(?:[A-Z_]+\s+)*(\w+(?:::\w+)*))' + r'(.*)$', line) + if (class_decl_match and + (not self.stack or self.stack[-1].open_parentheses == 0)): + # We do not want to accept classes that are actually template arguments: + # template <class Ignore1, + # class Ignore2 = Default<Args>, + # template <Args> class Ignore3> + # void Function() {}; + # + # To avoid template argument cases, we scan forward and look for + # an unmatched '>'. If we see one, assume we are inside a + # template argument list. + end_declaration = len(class_decl_match.group(1)) + if not self.InTemplateArgumentList(clean_lines, linenum, end_declaration): + self.stack.append(_ClassInfo( + class_decl_match.group(3), class_decl_match.group(2), + clean_lines, linenum)) + line = class_decl_match.group(4) + + # If we have not yet seen the opening brace for the innermost block, + # run checks here. + if not self.SeenOpenBrace(): + self.stack[-1].CheckBegin(filename, clean_lines, linenum, error) + + # Update access control if we are inside a class/struct + if self.stack and isinstance(self.stack[-1], _ClassInfo): + classinfo = self.stack[-1] + access_match = Match( + r'^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?' + r':(?:[^:]|$)', + line) + if access_match: + classinfo.access = access_match.group(2) + + # Check that access keywords are indented +1 space. Skip this + # check if the keywords are not preceded by whitespaces. + indent = access_match.group(1) + if (len(indent) != classinfo.class_indent + 1 and + Match(r'^\s*$', indent)): + if classinfo.is_struct: + parent = 'struct ' + classinfo.name + else: + parent = 'class ' + classinfo.name + slots = '' + if access_match.group(3): + slots = access_match.group(3) + error(filename, linenum, 'whitespace/indent', 3, + '%s%s: should be indented +1 space inside %s' % ( + access_match.group(2), slots, parent)) + + # Consume braces or semicolons from what's left of the line + while True: + # Match first brace, semicolon, or closed parenthesis. + matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) + if not matched: + break + + token = matched.group(1) + if token == '{': + # If namespace or class hasn't seen a opening brace yet, mark + # namespace/class head as complete. Push a new block onto the + # stack otherwise. + if not self.SeenOpenBrace(): + self.stack[-1].seen_open_brace = True + elif Match(r'^extern\s*"[^"]*"\s*\{', line): + self.stack.append(_ExternCInfo(linenum)) + else: + self.stack.append(_BlockInfo(linenum, True)) + if _MATCH_ASM.match(line): + self.stack[-1].inline_asm = _BLOCK_ASM + + elif token == ';' or token == ')': + # If we haven't seen an opening brace yet, but we already saw + # a semicolon, this is probably a forward declaration. Pop + # the stack for these. + # + # Similarly, if we haven't seen an opening brace yet, but we + # already saw a closing parenthesis, then these are probably + # function arguments with extra "class" or "struct" keywords. + # Also pop these stack for these. + if not self.SeenOpenBrace(): + self.stack.pop() + else: # token == '}' + # Perform end of block checks and pop the stack. + if self.stack: + self.stack[-1].CheckEnd(filename, clean_lines, linenum, error) + self.stack.pop() + line = matched.group(2) + + def InnermostClass(self): + """Get class info on the top of the stack. + + Returns: + A _ClassInfo object if we are inside a class, or None otherwise. + """ + for i in range(len(self.stack), 0, -1): + classinfo = self.stack[i - 1] + if isinstance(classinfo, _ClassInfo): + return classinfo + return None + + def CheckCompletedBlocks(self, filename, error): + """Checks that all classes and namespaces have been completely parsed. + + Call this when all lines in a file have been processed. + Args: + filename: The name of the current file. + error: The function to call with any errors found. + """ + # Note: This test can result in false positives if #ifdef constructs + # get in the way of brace matching. See the testBuildClass test in + # cpplint_unittest.py for an example of this. + for obj in self.stack: + if isinstance(obj, _ClassInfo): + error(filename, obj.starting_linenum, 'build/class', 5, + 'Failed to find complete declaration of class %s' % + obj.name) + elif isinstance(obj, _NamespaceInfo): + error(filename, obj.starting_linenum, 'build/namespaces', 5, + 'Failed to find complete declaration of namespace %s' % + obj.name) + + +def CheckForNonStandardConstructs(filename, clean_lines, linenum, + nesting_state, error): + r"""Logs an error if we see certain non-ANSI constructs ignored by gcc-2. + + Complain about several constructs which gcc-2 accepts, but which are + not standard C++. Warning about these in lint is one way to ease the + transition to new compilers. + - put storage class first (e.g. "static const" instead of "const static"). + - "%lld" instead of %qd" in printf-type functions. + - "%1$d" is non-standard in printf-type functions. + - "\%" is an undefined character escape sequence. + - text after #endif is not allowed. + - invalid inner-style forward declaration. + - >? and <? operators, and their >?= and <?= cousins. + + Additionally, check for constructor/destructor style violations and reference + members, as it is very convenient to do so while checking for + gcc-2 compliance. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. - class_state: A _ClassState instance which maintains information about - the current stack of nested class declarations being parsed. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. error: A callable to which errors are reported, which takes 4 arguments: filename, line number, error level, and message """ @@ -1343,10 +2698,11 @@ def CheckForNonStandardConstructs(filename, clean_lines, linenum, if Search(r'\b(const|volatile|void|char|short|int|long' r'|float|double|signed|unsigned' r'|schar|u?int8|u?int16|u?int32|u?int64)' - r'\s+(auto|register|static|extern|typedef)\b', + r'\s+(register|static|extern|typedef)\b', line): error(filename, linenum, 'build/storage_class', 5, - 'Storage class (static, extern, typedef, etc) should be first.') + 'Storage-class specifier (static, extern, typedef, etc) should be ' + 'at the beginning of the declaration.') if Match(r'\s*#\s*endif\s*[^/\s]+', line): error(filename, linenum, 'build/endif_comment', 5, @@ -1373,96 +2729,91 @@ def CheckForNonStandardConstructs(filename, clean_lines, linenum, 'const string& members are dangerous. It is much better to use ' 'alternatives, such as pointers or simple constants.') - # Track class entry and exit, and attempt to find cases within the - # class declaration that don't meet the C++ style - # guidelines. Tracking is very dependent on the code matching Google - # style guidelines, but it seems to perform well enough in testing - # to be a worthwhile addition to the checks. - classinfo_stack = class_state.classinfo_stack - # Look for a class declaration - class_decl_match = Match( - r'\s*(template\s*<[\w\s<>,:]*>\s*)?(class|struct)\s+' + - r'(?:NODE_EXTERN\s+)?(\w+(::\w+)*)', line) - if class_decl_match: - classinfo_stack.append(_ClassInfo(class_decl_match.group(3), linenum)) - - # Everything else in this function uses the top of the stack if it's - # not empty. - if not classinfo_stack: + # Everything else in this function operates on class declarations. + # Return early if the top of the nesting stack is not a class, or if + # the class head is not completed yet. + classinfo = nesting_state.InnermostClass() + if not classinfo or not classinfo.seen_open_brace: return - classinfo = classinfo_stack[-1] - - # If the opening brace hasn't been seen look for it and also - # parent class declarations. - if not classinfo.seen_open_brace: - # If the line has a ';' in it, assume it's a forward declaration or - # a single-line class declaration, which we won't process. - if line.find(';') != -1: - classinfo_stack.pop() - return - classinfo.seen_open_brace = (line.find('{') != -1) - # Look for a bare ':' - if Search('(^|[^:]):($|[^:])', line): - classinfo.is_derived = True - if not classinfo.seen_open_brace: - return # Everything else in this function is for after open brace - # The class may have been declared with namespace or classname qualifiers. # The constructor and destructor will not have those qualifiers. base_classname = classinfo.name.split('::')[-1] # Look for single-argument constructors that aren't marked explicit. # Technically a valid construct, but against style. - args = Match(r'(?<!explicit)\s+%s\s*\(([^,()]+)\)' - % re.escape(base_classname), - line) - if (args and - args.group(1) != 'void' and - not Match(r'(const\s+)?%s\s*&' % re.escape(base_classname), - args.group(1).strip())): - error(filename, linenum, 'runtime/explicit', 5, - 'Single-argument constructors should be marked explicit.') - - # Look for methods declared virtual. - if Search(r'\bvirtual\b', line): - classinfo.virtual_method_linenumber = linenum - # Only look for a destructor declaration on the same line. It would - # be extremely unlikely for the destructor declaration to occupy - # more than one line. - if Search(r'~%s\s*\(' % base_classname, line): - classinfo.has_virtual_destructor = True - - # Look for class end. - brace_depth = classinfo.brace_depth - brace_depth = brace_depth + line.count('{') - line.count('}') - if brace_depth <= 0: - classinfo = classinfo_stack.pop() - # Try to detect missing virtual destructor declarations. - # For now, only warn if a non-derived class with virtual methods lacks - # a virtual destructor. This is to make it less likely that people will - # declare derived virtual destructors without declaring the base - # destructor virtual. - if ((classinfo.virtual_method_linenumber is not None) and - (not classinfo.has_virtual_destructor) and - (not classinfo.is_derived)): # Only warn for base classes - error(filename, classinfo.linenum, 'runtime/virtual', 4, - 'The class %s probably needs a virtual destructor due to ' - 'having virtual method(s), one declared at line %d.' - % (classinfo.name, classinfo.virtual_method_linenumber)) - else: - classinfo.brace_depth = brace_depth + explicit_constructor_match = Match( + r'\s+(?:inline\s+)?(explicit\s+)?(?:inline\s+)?%s\s*' + r'\(((?:[^()]|\([^()]*\))*)\)' + % re.escape(base_classname), + line) + + if explicit_constructor_match: + is_marked_explicit = explicit_constructor_match.group(1) + if not explicit_constructor_match.group(2): + constructor_args = [] + else: + constructor_args = explicit_constructor_match.group(2).split(',') + + # collapse arguments so that commas in template parameter lists and function + # argument parameter lists don't split arguments in two + i = 0 + while i < len(constructor_args): + constructor_arg = constructor_args[i] + while (constructor_arg.count('<') > constructor_arg.count('>') or + constructor_arg.count('(') > constructor_arg.count(')')): + constructor_arg += ',' + constructor_args[i + 1] + del constructor_args[i + 1] + constructor_args[i] = constructor_arg + i += 1 + + defaulted_args = [arg for arg in constructor_args if '=' in arg] + noarg_constructor = (not constructor_args or # empty arg list + # 'void' arg specifier + (len(constructor_args) == 1 and + constructor_args[0].strip() == 'void')) + onearg_constructor = ((len(constructor_args) == 1 and # exactly one arg + not noarg_constructor) or + # all but at most one arg defaulted + (len(constructor_args) >= 1 and + not noarg_constructor and + len(defaulted_args) >= len(constructor_args) - 1)) + initializer_list_constructor = bool( + onearg_constructor and + Search(r'\bstd\s*::\s*initializer_list\b', constructor_args[0])) + copy_constructor = bool( + onearg_constructor and + Match(r'(const\s+)?%s(\s*<[^>]*>)?(\s+const)?\s*(?:<\w+>\s*)?&' + % re.escape(base_classname), constructor_args[0].strip())) + + if (not is_marked_explicit and + onearg_constructor and + not initializer_list_constructor and + not copy_constructor): + if defaulted_args: + error(filename, linenum, 'runtime/explicit', 5, + 'Constructors callable with one argument ' + 'should be marked explicit.') + else: + error(filename, linenum, 'runtime/explicit', 5, + 'Single-parameter constructors should be marked explicit.') + elif is_marked_explicit and not onearg_constructor: + if noarg_constructor: + error(filename, linenum, 'runtime/explicit', 5, + 'Zero-parameter constructors should not be marked explicit.') -def CheckSpacingForFunctionCall(filename, line, linenum, error): + +def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error): """Checks for the correctness of various spacing around function calls. Args: filename: The name of the current file. - line: The text of the line to check. + clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ + line = clean_lines.elided[linenum] # Since function calls often occur inside if/for/while/switch # expressions - which have their own, more liberal conventions - we @@ -1492,7 +2843,8 @@ def CheckSpacingForFunctionCall(filename, line, linenum, error): # Note that we assume the contents of [] to be short enough that # they'll never need to wrap. if ( # Ignore control structures. - not Search(r'\b(if|for|while|switch|return|delete)\b', fncall) and + not Search(r'\b(if|for|while|switch|return|new|delete|catch|sizeof)\b', + fncall) and # Ignore pointers/references to functions. not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and # Ignore pointers/references to arrays. @@ -1504,14 +2856,29 @@ def CheckSpacingForFunctionCall(filename, line, linenum, error): error(filename, linenum, 'whitespace/parens', 2, 'Extra space after (') if (Search(r'\w\s+\(', fncall) and - not Search(r'#\s*define|typedef', fncall)): - error(filename, linenum, 'whitespace/parens', 4, - 'Extra space before ( in function call') + not Search(r'_{0,2}asm_{0,2}\s+_{0,2}volatile_{0,2}\s+\(', fncall) and + not Search(r'#\s*define|typedef|using\s+\w+\s*=', fncall) and + not Search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall) and + not Search(r'\bcase\s+\(', fncall)): + # TODO(unknown): Space after an operator function seem to be a common + # error, silence those for now by restricting them to highest verbosity. + if Search(r'\boperator_*\b', line): + error(filename, linenum, 'whitespace/parens', 0, + 'Extra space before ( in function call') + else: + error(filename, linenum, 'whitespace/parens', 4, + 'Extra space before ( in function call') # If the ) is followed only by a newline or a { + newline, assume it's # part of a control statement (if/while/etc), and don't complain if Search(r'[^)]\s+\)\s*[^{\s]', fncall): - error(filename, linenum, 'whitespace/parens', 2, - 'Extra space before )') + # If the closing parenthesis is preceded by only whitespaces, + # try to give a more descriptive error message. + if Search(r'^\s+\)', fncall): + error(filename, linenum, 'whitespace/parens', 2, + 'Closing ) should be moved to the previous line') + else: + error(filename, linenum, 'whitespace/parens', 2, + 'Extra space before )') def IsBlankLine(line): @@ -1529,12 +2896,26 @@ def IsBlankLine(line): return not line or line.isspace() +def CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, + error): + is_namespace_indent_item = ( + len(nesting_state.stack) > 1 and + nesting_state.stack[-1].check_namespace_indentation and + isinstance(nesting_state.previous_stack_top, _NamespaceInfo) and + nesting_state.previous_stack_top == nesting_state.stack[-2]) + + if ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, + clean_lines.elided, line): + CheckItemIndentationInNamespace(filename, clean_lines.elided, + line, error) + + def CheckForFunctionLengths(filename, clean_lines, linenum, function_state, error): """Reports for long function bodies. For an overview why this is done, see: - http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions + https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions Uses a simplistic algorithm assuming other style guidelines (especially spacing) are followed. @@ -1542,7 +2923,7 @@ def CheckForFunctionLengths(filename, clean_lines, linenum, Trivial bodies are unchecked, so constructors with huge initializer lists may be missed. Blank/comment lines are not counted so as to avoid encouraging the removal - of vertical space and commments just to get through a lint check. + of vertical space and comments just to get through a lint check. NOLINT *on the last line of a function* disables this check. Args: @@ -1554,8 +2935,6 @@ def CheckForFunctionLengths(filename, clean_lines, linenum, """ lines = clean_lines.lines line = lines[linenum] - raw = clean_lines.raw_lines - raw_line = raw[linenum] joined_line = '' starting_func = False @@ -1602,59 +2981,132 @@ def CheckForFunctionLengths(filename, clean_lines, linenum, _RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?') -def CheckComment(comment, filename, linenum, error): - """Checks for common mistakes in TODO comments. +def CheckComment(line, filename, linenum, next_line_start, error): + """Checks for common mistakes in comments. Args: - comment: The text of the comment from the line in question. + line: The line in question. filename: The name of the current file. linenum: The number of the line to check. + next_line_start: The first non-whitespace column of the next line. error: The function to call with any errors found. """ - match = _RE_PATTERN_TODO.match(comment) - if match: - # One whitespace is correct; zero whitespace is handled elsewhere. - leading_whitespace = match.group(1) - if len(leading_whitespace) > 1: - error(filename, linenum, 'whitespace/todo', 2, - 'Too many spaces before TODO') - - username = match.group(2) - if not username: - error(filename, linenum, 'readability/todo', 2, - 'Missing username in TODO; it should look like ' - '"// TODO(my_username): Stuff."') - - middle_whitespace = match.group(3) - # Comparisons made explicit for correctness -- pylint: disable-msg=C6403 - if middle_whitespace != ' ' and middle_whitespace != '': - error(filename, linenum, 'whitespace/todo', 2, - 'TODO(my_username) should be followed by a space') - - -def CheckSpacing(filename, clean_lines, linenum, error): + commentpos = line.find('//') + if commentpos != -1: + # Check if the // may be in quotes. If so, ignore it + if re.sub(r'\\.', '', line[0:commentpos]).count('"') % 2 == 0: + # Allow one space for new scopes, two spaces otherwise: + if (not (Match(r'^.*{ *//', line) and next_line_start == commentpos) and + ((commentpos >= 1 and + line[commentpos-1] not in string.whitespace) or + (commentpos >= 2 and + line[commentpos-2] not in string.whitespace))): + error(filename, linenum, 'whitespace/comments', 2, + 'At least two spaces is best between code and comments') + + # Checks for common mistakes in TODO comments. + comment = line[commentpos:] + match = _RE_PATTERN_TODO.match(comment) + if match: + # One whitespace is correct; zero whitespace is handled elsewhere. + leading_whitespace = match.group(1) + if len(leading_whitespace) > 1: + error(filename, linenum, 'whitespace/todo', 2, + 'Too many spaces before TODO') + + username = match.group(2) + if not username: + error(filename, linenum, 'readability/todo', 2, + 'Missing username in TODO; it should look like ' + '"// TODO(my_username): Stuff."') + + middle_whitespace = match.group(3) + # Comparisons made explicit for correctness -- pylint: disable=g-explicit-bool-comparison + if middle_whitespace != ' ' and middle_whitespace != '': + error(filename, linenum, 'whitespace/todo', 2, + 'TODO(my_username) should be followed by a space') + + # If the comment contains an alphanumeric character, there + # should be a space somewhere between it and the // unless + # it's a /// or //! Doxygen comment. + if (Match(r'//[^ ]*\w', comment) and + not Match(r'(///|//\!)(\s+|$)', comment)): + error(filename, linenum, 'whitespace/comments', 4, + 'Should have a space between // and comment') + + +def CheckAccess(filename, clean_lines, linenum, nesting_state, error): + """Checks for improper use of DISALLOW* macros. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] # get rid of comments and strings + + matched = Match((r'\s*(DISALLOW_COPY_AND_ASSIGN|' + r'DISALLOW_IMPLICIT_CONSTRUCTORS)'), line) + if not matched: + return + if nesting_state.stack and isinstance(nesting_state.stack[-1], _ClassInfo): + if nesting_state.stack[-1].access != 'private': + error(filename, linenum, 'readability/constructors', 3, + '%s must be in the private: section' % matched.group(1)) + + else: + # Found DISALLOW* macro outside a class declaration, or perhaps it + # was used inside a function when it should have been part of the + # class declaration. We could issue a warning here, but it + # probably resulted in a compiler error already. + pass + + +def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): """Checks for the correctness of various spacing issues in the code. Things we check for: spaces around operators, spaces after if/for/while/switch, no spaces around parens in function calls, two spaces between code and comment, don't start a block with a blank - line, don't end a function with a blank line, don't have too many - blank lines in a row. + line, don't end a function with a blank line, don't add a blank line + after public/protected/private, don't have too many blank lines in a row. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. error: The function to call with any errors found. """ - raw = clean_lines.raw_lines + # Don't use "elided" lines here, otherwise we can't check commented lines. + # Don't want to use "raw" either, because we don't want to check inside C++11 + # raw strings, + raw = clean_lines.lines_without_raw_strings line = raw[linenum] # Before nixing comments, check if the line is blank for no good # reason. This includes the first line after a block is opened, and # blank lines at the end of a function (ie, right before a line like '}' - if IsBlankLine(line): + # + # Skip all the blank line checks if we are immediately inside a + # namespace body. In other words, don't issue blank line warnings + # for this block: + # namespace { + # + # } + # + # A warning about missing end of namespace comments will be issued instead. + # + # Also skip blank line checks for 'extern "C"' blocks, which are formatted + # like namespaces. + if (IsBlankLine(line) and + not nesting_state.InNamespaceBody() and + not nesting_state.InExternC()): elided = clean_lines.elided prev_line = elided[linenum - 1] prevbrace = prev_line.rfind('{') @@ -1662,11 +3114,10 @@ def CheckSpacing(filename, clean_lines, linenum, error): # both start with alnums and are indented the same amount. # This ignores whitespace at the start of a namespace block # because those are not usually indented. - if (prevbrace != -1 and prev_line[prevbrace:].find('}') == -1 - and prev_line[:prevbrace].find('namespace') == -1): + if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1: # OK, we have a blank line at the start of a code block. Before we # complain, we check if it is an exception to the rule: The previous - # non-empty line has the paramters of a function header that are indented + # non-empty line has the parameters of a function header that are indented # 4 spaces (because they did not fit in a 80 column line when placed on # the same line as the function name). We also check for the case where # the previous line is indented 6 spaces, which may happen when the @@ -1694,13 +3145,9 @@ def CheckSpacing(filename, clean_lines, linenum, error): if not exception: error(filename, linenum, 'whitespace/blank_line', 2, - 'Blank line at the start of a code block. Is this needed?') - # This doesn't ignore whitespace at the end of a namespace block - # because that is too hard without pairing open/close braces; - # however, a special exception is made for namespace closing - # brackets which have a comment containing "namespace". - # - # Also, ignore blank lines at the end of a block in a long if-else + 'Redundant blank line at the start of a code block ' + 'should be deleted.') + # Ignore blank lines at the end of a block in a long if-else # chain, like this: # if (condition1) { # // Something followed by a blank line @@ -1712,87 +3159,147 @@ def CheckSpacing(filename, clean_lines, linenum, error): next_line = raw[linenum + 1] if (next_line and Match(r'\s*}', next_line) - and next_line.find('namespace') == -1 - and next_line.find('extern') == -1 and next_line.find('} else ') == -1): error(filename, linenum, 'whitespace/blank_line', 3, - 'Blank line at the end of a code block. Is this needed?') + 'Redundant blank line at the end of a code block ' + 'should be deleted.') - # Next, we complain if there's a comment too near the text - commentpos = line.find('//') - if commentpos != -1: - # Check if the // may be in quotes. If so, ignore it - # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 - if (line.count('"', 0, commentpos) - - line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes - # Allow one space for new scopes, two spaces otherwise: - if (not Match(r'^\s*{ //', line) and - ((commentpos >= 1 and - line[commentpos-1] not in string.whitespace) or - (commentpos >= 2 and - line[commentpos-2] not in string.whitespace))): - error(filename, linenum, 'whitespace/comments', 2, - 'At least two spaces is best between code and comments') - # There should always be a space between the // and the comment - commentend = commentpos + 2 - if commentend < len(line) and not line[commentend] == ' ': - # but some lines are exceptions -- e.g. if they're big - # comment delimiters like: - # //---------------------------------------------------------- - # or are an empty C++ style Doxygen comment, like: - # /// - # or they begin with multiple slashes followed by a space: - # //////// Header comment - match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or - Search(r'^/$', line[commentend:]) or - Search(r'^/+ ', line[commentend:])) - if not match: - error(filename, linenum, 'whitespace/comments', 4, - 'Should have a space between // and comment') - CheckComment(line[commentpos:], filename, linenum, error) + matched = Match(r'\s*(public|protected|private):', prev_line) + if matched: + error(filename, linenum, 'whitespace/blank_line', 3, + 'Do not leave a blank line after "%s:"' % matched.group(1)) - line = clean_lines.elided[linenum] # get rid of comments and strings + # Next, check comments + next_line_start = 0 + if linenum + 1 < clean_lines.NumLines(): + next_line = raw[linenum + 1] + next_line_start = len(next_line) - len(next_line.lstrip()) + CheckComment(line, filename, linenum, next_line_start, error) + + # get rid of comments and strings + line = clean_lines.elided[linenum] + + # You shouldn't have spaces before your brackets, except maybe after + # 'delete []' or 'return []() {};' + if Search(r'\w\s+\[', line) and not Search(r'(?:delete|return)\s+\[', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Extra space before [') + + # In range-based for, we wanted spaces before and after the colon, but + # not around "::" tokens that might appear. + if (Search(r'for *\(.*[^:]:[^: ]', line) or + Search(r'for *\(.*[^: ]:[^:]', line)): + error(filename, linenum, 'whitespace/forcolon', 2, + 'Missing space around colon in range-based for loop') + + +def CheckOperatorSpacing(filename, clean_lines, linenum, error): + """Checks for horizontal spacing around operators. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] - # Don't try to do spacing checks for operator methods - line = re.sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line) + # Don't try to do spacing checks for operator methods. Do this by + # replacing the troublesome characters with something else, + # preserving column position for all other characters. + # + # The replacement is done repeatedly to avoid false positives from + # operators that call operators. + while True: + match = Match(r'^(.*\boperator\b)(\S+)(\s*\(.*)$', line) + if match: + line = match.group(1) + ('_' * len(match.group(2))) + match.group(3) + else: + break # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". # Otherwise not. Note we only check for non-spaces on *both* sides; # sometimes people put non-spaces on one side when aligning ='s among # many lines (not that this is behavior that I approve of...) - if Search(r'[\w.]=[\w.]', line) and not Search(r'\b(if|while) ', line): + if ((Search(r'[\w.]=', line) or + Search(r'=[\w.]', line)) + and not Search(r'\b(if|while|for) ', line) + # Operators taken from [lex.operators] in C++11 standard. + and not Search(r'(>=|<=|==|!=|&=|\^=|\|=|\+=|\*=|\/=|\%=)', line) + and not Search(r'operator=', line)): error(filename, linenum, 'whitespace/operators', 4, 'Missing spaces around =') - if Match(r'^\s*(if|while) .*[^\{,)&|\\]$', line): - error(filename, linenum, 'whitespace/if-one-line', 4, - 'If\'s body on the same line as if itself') - # It's ok not to have spaces around binary operators like + - * /, but if # there's too little whitespace, we get concerned. It's hard to tell, # though, so we punt on this one for now. TODO. # You should always have whitespace around binary operators. - # Alas, we can't test < or > because they're legitimately used sans spaces - # (a->b, vector<int> a). The only time we can tell is a < with no >, and - # only if it's not template params list spilling into the next line. - match = Search(r'[^<>=!\s](==|!=|<=|>=)[^<>=!\s]', line) - if not match: - # Note that while it seems that the '<[^<]*' term in the following - # regexp could be simplified to '<.*', which would indeed match - # the same class of strings, the [^<] means that searching for the - # regexp takes linear rather than quadratic time. - if not Search(r'<[^<]*,\s*$', line): # template params spill - match = Search(r'[^<>=!\s](<)[^<>=!\s]([^>]|->)*$', line) + # + # Check <= and >= first to avoid false positives with < and >, then + # check non-include lines for spacing around < and >. + # + # If the operator is followed by a comma, assume it's be used in a + # macro context and don't do any checks. This avoids false + # positives. + # + # Note that && is not included here. This is because there are too + # many false positives due to RValue references. + match = Search(r'[^<>=!\s](==|!=|<=|>=|\|\|)[^<>=!\s,;\)]', line) if match: error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around %s' % match.group(1)) - # We allow no-spaces around << and >> when used like this: 10<<20, but + elif not Match(r'#.*include', line): + # Look for < that is not surrounded by spaces. This is only + # triggered if both sides are missing spaces, even though + # technically should should flag if at least one side is missing a + # space. This is done to avoid some false positives with shifts. + match = Match(r'^(.*[^\s<])<[^\s=<,]', line) + if match: + (_, _, end_pos) = CloseExpression( + clean_lines, linenum, len(match.group(1))) + if end_pos <= -1: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around <') + + # Look for > that is not surrounded by spaces. Similar to the + # above, we only trigger if both sides are missing spaces to avoid + # false positives with shifts. + match = Match(r'^(.*[^-\s>])>[^\s=>,]', line) + if match: + (_, _, start_pos) = ReverseCloseExpression( + clean_lines, linenum, len(match.group(1))) + if start_pos <= -1: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around >') + + # We allow no-spaces around << when used like this: 10<<20, but # not otherwise (particularly, not when used as streams) - match = Search(r'[^0-9\s](<<|>>)[^0-9\s]', line) + # + # We also allow operators following an opening parenthesis, since + # those tend to be macros that deal with operators. + match = Search(r'(operator|[^\s(<])(?:L|UL|LL|ULL|l|ul|ll|ull)?<<([^\s,=<])', line) + if (match and not (match.group(1).isdigit() and match.group(2).isdigit()) and + not (match.group(1) == 'operator' and match.group(2) == ';')): + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around <<') + + # We allow no-spaces around >> for almost anything. This is because + # C++11 allows ">>" to close nested templates, which accounts for + # most cases when ">>" is not followed by a space. + # + # We still warn on ">>" followed by alpha character, because that is + # likely due to ">>" being used for right shifts, e.g.: + # value >> alpha + # + # When ">>" is used to close templates, the alphanumeric letter that + # follows would be part of an identifier, and there should still be + # a space separating the template type and the identifier. + # type<type<type>> alpha + match = Search(r'>>[a-zA-Z_]', line) if match: error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around %s' % match.group(1)) + 'Missing spaces around >>') # There shouldn't be space around unary operators match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) @@ -1800,7 +3307,19 @@ def CheckSpacing(filename, clean_lines, linenum, error): error(filename, linenum, 'whitespace/operators', 4, 'Extra space for operator %s' % match.group(1)) - # A pet peeve of mine: no spaces after an if, while, switch, or for + +def CheckParenthesisSpacing(filename, clean_lines, linenum, error): + """Checks for horizontal spacing around parentheses. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # No spaces after an if, while, switch, or for match = Search(r' (if\(|for\(|while\(|switch\()', line) if match: error(filename, linenum, 'whitespace/parens', 5, @@ -1821,52 +3340,271 @@ def CheckSpacing(filename, clean_lines, linenum, error): not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): error(filename, linenum, 'whitespace/parens', 5, 'Mismatching spaces inside () in %s' % match.group(1)) - if not len(match.group(2)) in [0, 1]: + if len(match.group(2)) not in [0, 1]: error(filename, linenum, 'whitespace/parens', 5, 'Should have zero or one spaces inside ( and ) in %s' % match.group(1)) - # You should always have a space after a comma (either as fn arg or operator) - if Search(r',[^\s]', line): - error(filename, linenum, 'whitespace/comma', 3, - 'Missing space after ,') - - # Next we will look for issues with function calls. - CheckSpacingForFunctionCall(filename, line, linenum, error) - # Except after an opening paren, you should have spaces before your braces. - # And since you should never have braces at the beginning of a line, this is - # an easy test. - if Search(r'[^ (]{', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Missing space before {') +def CheckCommaSpacing(filename, clean_lines, linenum, error): + """Checks for horizontal spacing near commas and semicolons. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + raw = clean_lines.lines_without_raw_strings + line = clean_lines.elided[linenum] + + # You should always have a space after a comma (either as fn arg or operator) + # + # This does not apply when the non-space character following the + # comma is another comma, since the only time when that happens is + # for empty macro arguments. + # + # We run this check in two passes: first pass on elided lines to + # verify that lines contain missing whitespaces, second pass on raw + # lines to confirm that those missing whitespaces are not due to + # elided comments. + if (Search(r',[^,\s]', ReplaceAll(r'\boperator\s*,\s*\(', 'F(', line)) and + Search(r',[^,\s]', raw[linenum])): + error(filename, linenum, 'whitespace/comma', 3, + 'Missing space after ,') + + # You should always have a space after a semicolon + # except for few corner cases + # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more + # space after ; + if Search(r';[^\s};\\)/]', line): + error(filename, linenum, 'whitespace/semicolon', 3, + 'Missing space after ;') + + +def _IsType(clean_lines, nesting_state, expr): + """Check if expression looks like a type name, returns true if so. + + Args: + clean_lines: A CleansedLines instance containing the file. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + expr: The expression to check. + Returns: + True, if token looks like a type. + """ + # Keep only the last token in the expression + last_word = Match(r'^.*(\b\S+)$', expr) + if last_word: + token = last_word.group(1) + else: + token = expr + + # Match native types and stdint types + if _TYPES.match(token): + return True + + # Try a bit harder to match templated types. Walk up the nesting + # stack until we find something that resembles a typename + # declaration for what we are looking for. + typename_pattern = (r'\b(?:typename|class|struct)\s+' + re.escape(token) + + r'\b') + block_index = len(nesting_state.stack) - 1 + while block_index >= 0: + if isinstance(nesting_state.stack[block_index], _NamespaceInfo): + return False + + # Found where the opening brace is. We want to scan from this + # line up to the beginning of the function, minus a few lines. + # template <typename Type1, // stop scanning here + # ...> + # class C + # : public ... { // start scanning here + last_line = nesting_state.stack[block_index].starting_linenum + + next_block_start = 0 + if block_index > 0: + next_block_start = nesting_state.stack[block_index - 1].starting_linenum + first_line = last_line + while first_line >= next_block_start: + if clean_lines.elided[first_line].find('template') >= 0: + break + first_line -= 1 + if first_line < next_block_start: + # Didn't find any "template" keyword before reaching the next block, + # there are probably no template things to check for this block + block_index -= 1 + continue + + # Look for typename in the specified range + for i in xrange(first_line, last_line + 1, 1): + if Search(typename_pattern, clean_lines.elided[i]): + return True + block_index -= 1 + + return False + + +def CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error): + """Checks for horizontal spacing near commas. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Except after an opening paren, or after another opening brace (in case of + # an initializer list, for instance), you should have spaces before your + # braces when they are delimiting blocks, classes, namespaces etc. + # And since you should never have braces at the beginning of a line, + # this is an easy test. Except that braces used for initialization don't + # follow the same rule; we often don't want spaces before those. + match = Match(r'^(.*[^ ({>]){', line) + + if match: + # Try a bit harder to check for brace initialization. This + # happens in one of the following forms: + # Constructor() : initializer_list_{} { ... } + # Constructor{}.MemberFunction() + # Type variable{}; + # FunctionCall(type{}, ...); + # LastArgument(..., type{}); + # LOG(INFO) << type{} << " ..."; + # map_of_type[{...}] = ...; + # ternary = expr ? new type{} : nullptr; + # OuterTemplate<InnerTemplateConstructor<Type>{}> + # + # We check for the character following the closing brace, and + # silence the warning if it's one of those listed above, i.e. + # "{.;,)<>]:". + # + # To account for nested initializer list, we allow any number of + # closing braces up to "{;,)<". We can't simply silence the + # warning on first sight of closing brace, because that would + # cause false negatives for things that are not initializer lists. + # Silence this: But not this: + # Outer{ if (...) { + # Inner{...} if (...){ // Missing space before { + # }; } + # + # There is a false negative with this approach if people inserted + # spurious semicolons, e.g. "if (cond){};", but we will catch the + # spurious semicolon with a separate check. + leading_text = match.group(1) + (endline, endlinenum, endpos) = CloseExpression( + clean_lines, linenum, len(match.group(1))) + trailing_text = '' + if endpos > -1: + trailing_text = endline[endpos:] + for offset in xrange(endlinenum + 1, + min(endlinenum + 3, clean_lines.NumLines() - 1)): + trailing_text += clean_lines.elided[offset] + # We also suppress warnings for `uint64_t{expression}` etc., as the style + # guide recommends brace initialization for integral types to avoid + # overflow/truncation. + if (not Match(r'^[\s}]*[{.;,)<>\]:]', trailing_text) + and not _IsType(clean_lines, nesting_state, leading_text)): + error(filename, linenum, 'whitespace/braces', 5, + 'Missing space before {') # Make sure '} else {' has spaces. if Search(r'}else', line): error(filename, linenum, 'whitespace/braces', 5, 'Missing space before else') - # You shouldn't have spaces before your brackets, except maybe after - # 'delete []' or 'new char * []'. - if Search(r'\w\s+\[', line) and not Search(r'delete\s+\[', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Extra space before [') - # You shouldn't have a space before a semicolon at the end of the line. # There's a special case for "for" since the style guide allows space before # the semicolon there. if Search(r':\s*;\s*$', line): error(filename, linenum, 'whitespace/semicolon', 5, - 'Semicolon defining empty statement. Use { } instead.') + 'Semicolon defining empty statement. Use {} instead.') elif Search(r'^\s*;\s*$', line): error(filename, linenum, 'whitespace/semicolon', 5, 'Line contains only semicolon. If this should be an empty statement, ' - 'use { } instead.') + 'use {} instead.') elif (Search(r'\s+;\s*$', line) and not Search(r'\bfor\b', line)): error(filename, linenum, 'whitespace/semicolon', 5, 'Extra space before last semicolon. If this should be an empty ' - 'statement, use { } instead.') + 'statement, use {} instead.') + + +def IsDecltype(clean_lines, linenum, column): + """Check if the token ending on (linenum, column) is decltype(). + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: the number of the line to check. + column: end column of the token to check. + Returns: + True if this token is decltype() expression, False otherwise. + """ + (text, _, start_col) = ReverseCloseExpression(clean_lines, linenum, column) + if start_col < 0: + return False + if Search(r'\bdecltype\s*$', text[0:start_col]): + return True + return False + + +def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): + """Checks for additional blank line issues related to sections. + + Currently the only thing checked here is blank line before protected/private. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + class_info: A _ClassInfo objects. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + # Skip checks if the class is small, where small means 25 lines or less. + # 25 lines seems like a good cutoff since that's the usual height of + # terminals, and any class that can't fit in one screen can't really + # be considered "small". + # + # Also skip checks if we are on the first line. This accounts for + # classes that look like + # class Foo { public: ... }; + # + # If we didn't find the end of the class, last_line would be zero, + # and the check will be skipped by the first condition. + if (class_info.last_line - class_info.starting_linenum <= 24 or + linenum <= class_info.starting_linenum): + return + + matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) + if matched: + # Issue warning if the line before public/protected/private was + # not a blank line, but don't do this if the previous line contains + # "class" or "struct". This can happen two ways: + # - We are at the beginning of the class. + # - We are forward-declaring an inner class that is semantically + # private, but needed to be public for implementation reasons. + # Also ignores cases where the previous line ends with a backslash as can be + # common when defining classes in C macros. + prev_line = clean_lines.lines[linenum - 1] + if (not IsBlankLine(prev_line) and + not Search(r'\b(class|struct)\b', prev_line) and + not Search(r'\\$', prev_line)): + # Try a bit harder to find the beginning of the class. This is to + # account for multi-line base-specifier lists, e.g.: + # class Derived + # : public Base { + end_class_head = class_info.starting_linenum + for i in range(class_info.starting_linenum, linenum): + if Search(r'\{\s*$', clean_lines.lines[i]): + end_class_head = i + break + if end_class_head < linenum - 1: + error(filename, linenum, 'whitespace/blank_line', 3, + '"%s:" should be preceded by a blank line' % matched.group(1)) def GetPreviousNonBlankLine(clean_lines, linenum): @@ -1905,19 +3643,24 @@ def CheckBraces(filename, clean_lines, linenum, error): line = clean_lines.elided[linenum] # get rid of comments and strings if Match(r'\s*{\s*$', line): - # We allow an open brace to start a line in the case where someone - # is using braces in a block to explicitly create a new scope, - # which is commonly used to control the lifetime of - # stack-allocated variables. We don't detect this perfectly: we - # just don't complain if the last non-whitespace character on the - # previous non-blank line is ';', ':', '{', or '}'. + # We allow an open brace to start a line in the case where someone is using + # braces in a block to explicitly create a new scope, which is commonly used + # to control the lifetime of stack-allocated variables. Braces are also + # used for brace initializers inside function calls. We don't detect this + # perfectly: we just don't complain if the last non-whitespace character on + # the previous non-blank line is ',', ';', ':', '(', '{', or '}', or if the + # previous line starts a preprocessor block. We also allow a brace on the + # following line if it is part of an array initialization and would not fit + # within the 80 character limit of the preceding line. prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if not Search(r'[;:}{]\s*$', prevline): + if (not Search(r'[,;:}{(]\s*$', prevline) and + not Match(r'\s*#', prevline) and + not (GetLineWidth(prevline) > _line_length - 2 and '[]' in prevline)): error(filename, linenum, 'whitespace/braces', 4, '{ should almost always be at the end of the previous line') # An else clause should be on the same line as the preceding closing brace. - if Match(r'\s*else\s*', line): + if Match(r'\s*else\b\s*(?:if\b|\{|$)', line): prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] if Match(r'\s*}\s*$', prevline): error(filename, linenum, 'whitespace/newline', 4, @@ -1925,19 +3668,20 @@ def CheckBraces(filename, clean_lines, linenum, error): # If braces come on one side of an else, they should be on both. # However, we have to worry about "else if" that spans multiple lines! - if Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): - if Search(r'}\s*else if([^{]*)$', line): # could be multi-line if - # find the ( after the if - pos = line.find('else if') - pos = line.find('(', pos) - if pos > 0: - (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) - if endline[endpos:].find('{') == -1: # must be brace after if - error(filename, linenum, 'readability/braces', 5, - 'If an else has a brace on one side, it should have it on both') - else: # common case: else not followed by a multi-line if - error(filename, linenum, 'readability/braces', 5, - 'If an else has a brace on one side, it should have it on both') + if Search(r'else if\s*\(', line): # could be multi-line if + brace_on_left = bool(Search(r'}\s*else if\s*\(', line)) + # find the ( after the if + pos = line.find('else if') + pos = line.find('(', pos) + if pos > 0: + (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) + brace_on_right = endline[endpos:].find('{') != -1 + if brace_on_left != brace_on_right: # must be brace after if + error(filename, linenum, 'readability/braces', 5, + 'If an else has a brace on one side, it should have it on both') + elif Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): + error(filename, linenum, 'readability/braces', 5, + 'If an else has a brace on one side, it should have it on both') # Likewise, an else should never have the else clause on the same line if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): @@ -1949,56 +3693,326 @@ def CheckBraces(filename, clean_lines, linenum, error): error(filename, linenum, 'whitespace/newline', 4, 'do/while clauses should not be on a single line') - # Braces shouldn't be followed by a ; unless they're defining a struct - # or initializing an array. - # We can't tell in general, but we can for some common cases. - prevlinenum = linenum - while True: - (prevline, prevlinenum) = GetPreviousNonBlankLine(clean_lines, prevlinenum) - if Match(r'\s+{.*}\s*;', line) and not prevline.count(';'): - line = prevline + line - else: - break - if (Search(r'{.*}\s*;', line) and - line.count('{') == line.count('}') and - not Search(r'struct|class|enum|\s*=\s*{', line)): - error(filename, linenum, 'readability/braces', 4, - "You don't need a ; after a }") + # Check single-line if/else bodies. The style guide says 'curly braces are not + # required for single-line statements'. We additionally allow multi-line, + # single statements, but we reject anything with more than one semicolon in + # it. This means that the first semicolon after the if should be at the end of + # its line, and the line after that should have an indent level equal to or + # lower than the if. We also check for ambiguous if/else nesting without + # braces. + if_else_match = Search(r'\b(if\s*\(|else\b)', line) + if if_else_match and not Match(r'\s*#', line): + if_indent = GetIndentLevel(line) + endline, endlinenum, endpos = line, linenum, if_else_match.end() + if_match = Search(r'\bif\s*\(', line) + if if_match: + # This could be a multiline if condition, so find the end first. + pos = if_match.end() - 1 + (endline, endlinenum, endpos) = CloseExpression(clean_lines, linenum, pos) + # Check for an opening brace, either directly after the if or on the next + # line. If found, this isn't a single-statement conditional. + if (not Match(r'\s*{', endline[endpos:]) + and not (Match(r'\s*$', endline[endpos:]) + and endlinenum < (len(clean_lines.elided) - 1) + and Match(r'\s*{', clean_lines.elided[endlinenum + 1]))): + while (endlinenum < len(clean_lines.elided) + and ';' not in clean_lines.elided[endlinenum][endpos:]): + endlinenum += 1 + endpos = 0 + if endlinenum < len(clean_lines.elided): + endline = clean_lines.elided[endlinenum] + # We allow a mix of whitespace and closing braces (e.g. for one-liner + # methods) and a single \ after the semicolon (for macros) + endpos = endline.find(';') + if not Match(r';[\s}]*(\\?)$', endline[endpos:]): + # Semicolon isn't the last character, there's something trailing. + # Output a warning if the semicolon is not contained inside + # a lambda expression. + if not Match(r'^[^{};]*\[[^\[\]]*\][^{}]*\{[^{}]*\}\s*\)*[;,]\s*$', + endline): + error(filename, linenum, 'readability/braces', 4, + 'If/else bodies with multiple statements require braces') + elif endlinenum < len(clean_lines.elided) - 1: + # Make sure the next line is dedented + next_line = clean_lines.elided[endlinenum + 1] + next_indent = GetIndentLevel(next_line) + # With ambiguous nested if statements, this will error out on the + # if that *doesn't* match the else, regardless of whether it's the + # inner one or outer one. + if (if_match and Match(r'\s*else\b', next_line) + and next_indent != if_indent): + error(filename, linenum, 'readability/braces', 4, + 'Else clause should be indented at the same level as if. ' + 'Ambiguous nested if/else chains require braces.') + elif next_indent > if_indent: + error(filename, linenum, 'readability/braces', 4, + 'If/else bodies with multiple statements require braces') + + +def CheckTrailingSemicolon(filename, clean_lines, linenum, error): + """Looks for redundant trailing semicolon. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + line = clean_lines.elided[linenum] + + # Block bodies should not be followed by a semicolon. Due to C++11 + # brace initialization, there are more places where semicolons are + # required than not, so we use a whitelist approach to check these + # rather than a blacklist. These are the places where "};" should + # be replaced by just "}": + # 1. Some flavor of block following closing parenthesis: + # for (;;) {}; + # while (...) {}; + # switch (...) {}; + # Function(...) {}; + # if (...) {}; + # if (...) else if (...) {}; + # + # 2. else block: + # if (...) else {}; + # + # 3. const member function: + # Function(...) const {}; + # + # 4. Block following some statement: + # x = 42; + # {}; + # + # 5. Block at the beginning of a function: + # Function(...) { + # {}; + # } + # + # Note that naively checking for the preceding "{" will also match + # braces inside multi-dimensional arrays, but this is fine since + # that expression will not contain semicolons. + # + # 6. Block following another block: + # while (true) {} + # {}; + # + # 7. End of namespaces: + # namespace {}; + # + # These semicolons seems far more common than other kinds of + # redundant semicolons, possibly due to people converting classes + # to namespaces. For now we do not warn for this case. + # + # Try matching case 1 first. + match = Match(r'^(.*\)\s*)\{', line) + if match: + # Matched closing parenthesis (case 1). Check the token before the + # matching opening parenthesis, and don't warn if it looks like a + # macro. This avoids these false positives: + # - macro that defines a base class + # - multi-line macro that defines a base class + # - macro that defines the whole class-head + # + # But we still issue warnings for macros that we know are safe to + # warn, specifically: + # - TEST, TEST_F, TEST_P, MATCHER, MATCHER_P + # - TYPED_TEST + # - INTERFACE_DEF + # - EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED, LOCKS_EXCLUDED: + # + # We implement a whitelist of safe macros instead of a blacklist of + # unsafe macros, even though the latter appears less frequently in + # google code and would have been easier to implement. This is because + # the downside for getting the whitelist wrong means some extra + # semicolons, while the downside for getting the blacklist wrong + # would result in compile errors. + # + # In addition to macros, we also don't want to warn on + # - Compound literals + # - Lambdas + # - alignas specifier with anonymous structs + # - decltype + closing_brace_pos = match.group(1).rfind(')') + opening_parenthesis = ReverseCloseExpression( + clean_lines, linenum, closing_brace_pos) + if opening_parenthesis[2] > -1: + line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]] + macro = Search(r'\b([A-Z_][A-Z0-9_]*)\s*$', line_prefix) + func = Match(r'^(.*\])\s*$', line_prefix) + if ((macro and + macro.group(1) not in ( + 'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST', + 'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED', + 'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or + (func and not Search(r'\boperator\s*\[\s*\]', func.group(1))) or + Search(r'\b(?:struct|union)\s+alignas\s*$', line_prefix) or + Search(r'\bdecltype$', line_prefix) or + Search(r'\s+=\s*$', line_prefix)): + match = None + if (match and + opening_parenthesis[1] > 1 and + Search(r'\]\s*$', clean_lines.elided[opening_parenthesis[1] - 1])): + # Multi-line lambda-expression + match = None + else: + # Try matching cases 2-3. + match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line) + if not match: + # Try matching cases 4-6. These are always matched on separate lines. + # + # Note that we can't simply concatenate the previous line to the + # current line and do a single match, otherwise we may output + # duplicate warnings for the blank line case: + # if (cond) { + # // blank line + # } + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if prevline and Search(r'[;{}]\s*$', prevline): + match = Match(r'^(\s*)\{', line) + + # Check matching closing brace + if match: + (endline, endlinenum, endpos) = CloseExpression( + clean_lines, linenum, len(match.group(1))) + if endpos > -1 and Match(r'^\s*;', endline[endpos:]): + # Current {} pair is eligible for semicolon check, and we have found + # the redundant semicolon, output warning here. + # + # Note: because we are scanning forward for opening braces, and + # outputting warnings for the matching closing brace, if there are + # nested blocks with trailing semicolons, we will get the error + # messages in reversed order. + error(filename, endlinenum, 'readability/braces', 4, + "You don't need a ; after a }") -def ReplaceableCheck(operator, macro, line): - """Determine whether a basic CHECK can be replaced with a more specific one. - For example suggest using CHECK_EQ instead of CHECK(a == b) and - similarly for CHECK_GE, CHECK_GT, CHECK_LE, CHECK_LT, CHECK_NE. +def CheckEmptyBlockBody(filename, clean_lines, linenum, error): + """Look for empty loop/conditional body with only a single semicolon. Args: - operator: The C++ operator used in the CHECK. - macro: The CHECK or EXPECT macro being called. - line: The current source line. - - Returns: - True if the CHECK can be replaced with a more specific one. + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. """ - # This matches decimal and hex integers, strings, and chars (in that order). - match_constant = r'([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')' - - # Expression to match two sides of the operator with something that - # looks like a literal, since CHECK(x == iterator) won't compile. - # This means we can't catch all the cases where a more specific - # CHECK is possible, but it's less annoying than dealing with - # extraneous warnings. - match_this = (r'\s*' + macro + r'\((\s*' + - match_constant + r'\s*' + operator + r'[^<>].*|' - r'.*[^<>]' + operator + r'\s*' + match_constant + - r'\s*\))') + # Search for loop keywords at the beginning of the line. Because only + # whitespaces are allowed before the keywords, this will also ignore most + # do-while-loops, since those lines should start with closing brace. + # + # We also check "if" blocks here, since an empty conditional block + # is likely an error. + line = clean_lines.elided[linenum] + matched = Match(r'\s*(for|while|if)\s*\(', line) + if matched: + # Find the end of the conditional expression. + (end_line, end_linenum, end_pos) = CloseExpression( + clean_lines, linenum, line.find('(')) + + # Output warning if what follows the condition expression is a semicolon. + # No warning for all other cases, including whitespace or newline, since we + # have a separate check for semicolons preceded by whitespace. + if end_pos >= 0 and Match(r';', end_line[end_pos:]): + if matched.group(1) == 'if': + error(filename, end_linenum, 'whitespace/empty_conditional_body', 5, + 'Empty conditional bodies should use {}') + else: + error(filename, end_linenum, 'whitespace/empty_loop_body', 5, + 'Empty loop bodies should use {} or continue') + + # Check for if statements that have completely empty bodies (no comments) + # and no else clauses. + if end_pos >= 0 and matched.group(1) == 'if': + # Find the position of the opening { for the if statement. + # Return without logging an error if it has no brackets. + opening_linenum = end_linenum + opening_line_fragment = end_line[end_pos:] + # Loop until EOF or find anything that's not whitespace or opening {. + while not Search(r'^\s*\{', opening_line_fragment): + if Search(r'^(?!\s*$)', opening_line_fragment): + # Conditional has no brackets. + return + opening_linenum += 1 + if opening_linenum == len(clean_lines.elided): + # Couldn't find conditional's opening { or any code before EOF. + return + opening_line_fragment = clean_lines.elided[opening_linenum] + # Set opening_line (opening_line_fragment may not be entire opening line). + opening_line = clean_lines.elided[opening_linenum] + + # Find the position of the closing }. + opening_pos = opening_line_fragment.find('{') + if opening_linenum == end_linenum: + # We need to make opening_pos relative to the start of the entire line. + opening_pos += end_pos + (closing_line, closing_linenum, closing_pos) = CloseExpression( + clean_lines, opening_linenum, opening_pos) + if closing_pos < 0: + return + + # Now construct the body of the conditional. This consists of the portion + # of the opening line after the {, all lines until the closing line, + # and the portion of the closing line before the }. + if (clean_lines.raw_lines[opening_linenum] != + CleanseComments(clean_lines.raw_lines[opening_linenum])): + # Opening line ends with a comment, so conditional isn't empty. + return + if closing_linenum > opening_linenum: + # Opening line after the {. Ignore comments here since we checked above. + body = list(opening_line[opening_pos+1:]) + # All lines until closing line, excluding closing line, with comments. + body.extend(clean_lines.raw_lines[opening_linenum+1:closing_linenum]) + # Closing line before the }. Won't (and can't) have comments. + body.append(clean_lines.elided[closing_linenum][:closing_pos-1]) + body = '\n'.join(body) + else: + # If statement has brackets and fits on a single line. + body = opening_line[opening_pos+1:closing_pos-1] + + # Check if the body is empty + if not _EMPTY_CONDITIONAL_BODY_PATTERN.search(body): + return + # The body is empty. Now make sure there's not an else clause. + current_linenum = closing_linenum + current_line_fragment = closing_line[closing_pos:] + # Loop until EOF or find anything that's not whitespace or else clause. + while Search(r'^\s*$|^(?=\s*else)', current_line_fragment): + if Search(r'^(?=\s*else)', current_line_fragment): + # Found an else clause, so don't log an error. + return + current_linenum += 1 + if current_linenum == len(clean_lines.elided): + break + current_line_fragment = clean_lines.elided[current_linenum] + + # The body is empty and there's no else clause until EOF or other code. + error(filename, end_linenum, 'whitespace/empty_if_body', 4, + ('If statement had no body and no else clause')) + + +def FindCheckMacro(line): + """Find a replaceable CHECK-like macro. - # Don't complain about CHECK(x == NULL) or similar because - # CHECK_EQ(x, NULL) won't compile (requires a cast). - # Also, don't complain about more complex boolean expressions - # involving && or || such as CHECK(a == b || c == d). - return Match(match_this, line) and not Search(r'NULL|&&|\|\|', line) + Args: + line: line to search on. + Returns: + (macro name, start position), or (None, -1) if no replaceable + macro is found. + """ + for macro in _CHECK_MACROS: + i = line.find(macro) + if i >= 0: + # Find opening parenthesis. Do a regular expression match here + # to make sure that we are matching the expected CHECK macro, as + # opposed to some other macro that happens to contain the CHECK + # substring. + matched = Match(r'^(.*\b' + macro + r'\s*)\(', line) + if not matched: + continue + return (macro, len(matched.group(1))) + return (None, -1) def CheckCheck(filename, clean_lines, linenum, error): @@ -2012,26 +4026,143 @@ def CheckCheck(filename, clean_lines, linenum, error): """ # Decide the set of replacement macros that should be suggested - raw_lines = clean_lines.raw_lines - current_macro = '' - for macro in _CHECK_MACROS: - if raw_lines[linenum].find(macro) >= 0: - current_macro = macro - break - if not current_macro: - # Don't waste time here if line doesn't contain 'CHECK' or 'EXPECT' + lines = clean_lines.elided + (check_macro, start_pos) = FindCheckMacro(lines[linenum]) + if not check_macro: return - line = clean_lines.elided[linenum] # get rid of comments and strings + # Find end of the boolean expression by matching parentheses + (last_line, end_line, end_pos) = CloseExpression( + clean_lines, linenum, start_pos) + if end_pos < 0: + return - # Encourage replacing plain CHECKs with CHECK_EQ/CHECK_NE/etc. - for operator in ['==', '!=', '>=', '>', '<=', '<']: - if ReplaceableCheck(operator, current_macro, line): - error(filename, linenum, 'readability/check', 2, - 'Consider using %s instead of %s(a %s b)' % ( - _CHECK_REPLACEMENT[current_macro][operator], - current_macro, operator)) - break + # If the check macro is followed by something other than a + # semicolon, assume users will log their own custom error messages + # and don't suggest any replacements. + if not Match(r'\s*;', last_line[end_pos:]): + return + + if linenum == end_line: + expression = lines[linenum][start_pos + 1:end_pos - 1] + else: + expression = lines[linenum][start_pos + 1:] + for i in xrange(linenum + 1, end_line): + expression += lines[i] + expression += last_line[0:end_pos - 1] + + # Parse expression so that we can take parentheses into account. + # This avoids false positives for inputs like "CHECK((a < 4) == b)", + # which is not replaceable by CHECK_LE. + lhs = '' + rhs = '' + operator = None + while expression: + matched = Match(r'^\s*(<<|<<=|>>|>>=|->\*|->|&&|\|\||' + r'==|!=|>=|>|<=|<|\()(.*)$', expression) + if matched: + token = matched.group(1) + if token == '(': + # Parenthesized operand + expression = matched.group(2) + (end, _) = FindEndOfExpressionInLine(expression, 0, ['(']) + if end < 0: + return # Unmatched parenthesis + lhs += '(' + expression[0:end] + expression = expression[end:] + elif token in ('&&', '||'): + # Logical and/or operators. This means the expression + # contains more than one term, for example: + # CHECK(42 < a && a < b); + # + # These are not replaceable with CHECK_LE, so bail out early. + return + elif token in ('<<', '<<=', '>>', '>>=', '->*', '->'): + # Non-relational operator + lhs += token + expression = matched.group(2) + else: + # Relational operator + operator = token + rhs = matched.group(2) + break + else: + # Unparenthesized operand. Instead of appending to lhs one character + # at a time, we do another regular expression match to consume several + # characters at once if possible. Trivial benchmark shows that this + # is more efficient when the operands are longer than a single + # character, which is generally the case. + matched = Match(r'^([^-=!<>()&|]+)(.*)$', expression) + if not matched: + matched = Match(r'^(\s*\S)(.*)$', expression) + if not matched: + break + lhs += matched.group(1) + expression = matched.group(2) + + # Only apply checks if we got all parts of the boolean expression + if not (lhs and operator and rhs): + return + + # Check that rhs do not contain logical operators. We already know + # that lhs is fine since the loop above parses out && and ||. + if rhs.find('&&') > -1 or rhs.find('||') > -1: + return + + # At least one of the operands must be a constant literal. This is + # to avoid suggesting replacements for unprintable things like + # CHECK(variable != iterator) + # + # The following pattern matches decimal, hex integers, strings, and + # characters (in that order). + lhs = lhs.strip() + rhs = rhs.strip() + match_constant = r'^([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')$' + if Match(match_constant, lhs) or Match(match_constant, rhs): + # Note: since we know both lhs and rhs, we can provide a more + # descriptive error message like: + # Consider using CHECK_EQ(x, 42) instead of CHECK(x == 42) + # Instead of: + # Consider using CHECK_EQ instead of CHECK(a == b) + # + # We are still keeping the less descriptive message because if lhs + # or rhs gets long, the error message might become unreadable. + error(filename, linenum, 'readability/check', 2, + 'Consider using %s instead of %s(a %s b)' % ( + _CHECK_REPLACEMENT[check_macro][operator], + check_macro, operator)) + + +def CheckAltTokens(filename, clean_lines, linenum, error): + """Check alternative keywords being used in boolean expressions. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Avoid preprocessor lines + if Match(r'^\s*#', line): + return + + # Last ditch effort to avoid multi-line comments. This will not help + # if the comment started before the current line or ended after the + # current line, but it catches most of the false positives. At least, + # it provides a way to workaround this warning for people who use + # multi-line comments in preprocessor macros. + # + # TODO(unknown): remove this once cpplint has better support for + # multi-line comments. + if line.find('/*') >= 0 or line.find('*/') >= 0: + return + + for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): + error(filename, linenum, 'readability/alt_tokens', 2, + 'Use operator %s instead of %s' % ( + _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1))) def GetLineWidth(line): @@ -2046,17 +4177,18 @@ def GetLineWidth(line): """ if isinstance(line, unicode): width = 0 - for c in unicodedata.normalize('NFC', line): - if unicodedata.east_asian_width(c) in ('W', 'F'): + for uc in unicodedata.normalize('NFC', line): + if unicodedata.east_asian_width(uc) in ('W', 'F'): width += 2 - elif not unicodedata.combining(c): + elif not unicodedata.combining(uc): width += 1 return width else: return len(line) -def CheckStyle(filename, clean_lines, linenum, file_extension, error): +def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, + error): """Checks rules from the 'C++ style rules' section of cppguide.html. Most of these rules are hard to test (naming, comment style), but we @@ -2068,11 +4200,17 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, error): clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. file_extension: The extension (without the dot) of the filename. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. error: The function to call with any errors found. """ - raw_lines = clean_lines.raw_lines + # Don't use "elided" lines here, otherwise we can't check commented lines. + # Don't want to use "raw" either, because we don't want to check inside C++11 + # raw strings, + raw_lines = clean_lines.lines_without_raw_strings line = raw_lines[linenum] + prev = raw_lines[linenum - 1] if linenum > 0 else '' if line.find('\t') != -1: error(filename, linenum, 'whitespace/tab', 1, @@ -2090,31 +4228,29 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, error): # if(match($0, " <<")) complain = 0; # if(match(prev, " +for \\(")) complain = 0; # if(prevodd && match(prevprev, " +for \\(")) complain = 0; + scope_or_label_pattern = r'\s*\w+\s*:\s*\\?$' + classinfo = nesting_state.InnermostClass() initial_spaces = 0 cleansed_line = clean_lines.elided[linenum] while initial_spaces < len(line) and line[initial_spaces] == ' ': initial_spaces += 1 - if line and line[-1].isspace(): - error(filename, linenum, 'whitespace/end_of_line', 4, - 'Line ends in whitespace. Consider deleting these extra spaces.') - # There are certain situations we allow one space, notably for labels - elif ((initial_spaces == 1 or initial_spaces == 3) and - not Match(r'\s*\w+\s*:\s*$', cleansed_line)): + # There are certain situations we allow one space, notably for + # section labels, and also lines containing multi-line raw strings. + # We also don't check for lines that look like continuation lines + # (of lines ending in double quotes, commas, equals, or angle brackets) + # because the rules for how to indent those are non-trivial. + if (not Search(r'[",=><] *$', prev) and + (initial_spaces == 1 or initial_spaces == 3) and + not Match(scope_or_label_pattern, cleansed_line) and + not (clean_lines.raw_lines[linenum] != line and + Match(r'^\s*""', line))): error(filename, linenum, 'whitespace/indent', 3, 'Weird number of spaces at line-start. ' 'Are you using a 2-space indent?') - # Labels should always be indented at least one space. - elif not initial_spaces and line[:2] != '//' and Search(r'[^:]:\s*$', - line): - error(filename, linenum, 'whitespace/labels', 4, - 'Labels should always be indented at least one space. ' - 'If this is a member-initializer list in a constructor or ' - 'the base class list in a class definition, the colon should ' - 'be on the following line.') - - if len(line) > initial_spaces and line[initial_spaces] == ',': - error(filename, linenum, 'whitespace/commafirst', 4, - 'Comma-first style is not allowed') + + if line and line[-1].isspace(): + error(filename, linenum, 'whitespace/end_of_line', 4, + 'Line ends in whitespace. Consider deleting these extra spaces.') # Check if the line is a header guard. is_header_guard = False @@ -2122,23 +4258,24 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, error): cppvar = GetHeaderGuardCPPVariable(filename) if (line.startswith('#ifndef %s' % cppvar) or line.startswith('#define %s' % cppvar) or - line.startswith('#endif // %s' % cppvar) or - line.startswith('#endif /* %s */' % cppvar)): + line.startswith('#endif // %s' % cppvar)): is_header_guard = True # #include lines and header guards can be long, since there's no clean way to # split them. # # URLs can be long too. It's possible to split these, but it makes them # harder to cut&paste. + # + # The "$Id:...$" comment may also get very long without it being the + # developers fault. if (not line.startswith('#include') and not is_header_guard and - not Match(r'^\s*//.*http(s?)://\S*$', line)): + not Match(r'^\s*//.*http(s?)://\S*$', line) and + not Match(r'^\s*//\s*[^\s]*$', line) and + not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): line_width = GetLineWidth(line) - if line_width > 100: - error(filename, linenum, 'whitespace/line_length', 4, - 'Lines should very rarely be longer than 100 characters') - elif line_width > 80: + if line_width > _line_length: error(filename, linenum, 'whitespace/line_length', 2, - 'Lines should be <= 80 characters long') + 'Lines should be <= %i characters long' % _line_length) if (cleansed_line.count(';') > 1 and # for loops are allowed two ;'s (and may run over two lines). @@ -2149,16 +4286,27 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, error): not ((cleansed_line.find('case ') != -1 or cleansed_line.find('default:') != -1) and cleansed_line.find('break;') != -1)): - error(filename, linenum, 'whitespace/newline', 4, + error(filename, linenum, 'whitespace/newline', 0, 'More than one command on the same line') # Some more style checks CheckBraces(filename, clean_lines, linenum, error) - CheckSpacing(filename, clean_lines, linenum, error) + CheckTrailingSemicolon(filename, clean_lines, linenum, error) + CheckEmptyBlockBody(filename, clean_lines, linenum, error) + CheckAccess(filename, clean_lines, linenum, nesting_state, error) + CheckSpacing(filename, clean_lines, linenum, nesting_state, error) + CheckOperatorSpacing(filename, clean_lines, linenum, error) + CheckParenthesisSpacing(filename, clean_lines, linenum, error) + CheckCommaSpacing(filename, clean_lines, linenum, error) + CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error) + CheckSpacingForFunctionCall(filename, clean_lines, linenum, error) CheckCheck(filename, clean_lines, linenum, error) + CheckAltTokens(filename, clean_lines, linenum, error) + classinfo = nesting_state.InnermostClass() + if classinfo: + CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error) -_RE_PATTERN_INCLUDE_NEW_STYLE = re.compile(r'#include +"[^/]+\.h"') _RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') # Matches the first component of a filename delimited by -s and _s. That is: # _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' @@ -2195,23 +4343,6 @@ def _DropCommonSuffixes(filename): return os.path.splitext(filename)[0] -def _IsTestFilename(filename): - """Determines if the given filename has a suffix that identifies it as a test. - - Args: - filename: The input filename. - - Returns: - True if 'filename' looks like a test, False otherwise. - """ - if (filename.endswith('_test.cc') or - filename.endswith('_unittest.cc') or - filename.endswith('_regtest.cc')): - return True - else: - return False - - def _ClassifyInclude(fileinfo, include, is_system): """Figures out what kind of header 'include' is. @@ -2238,8 +4369,7 @@ def _ClassifyInclude(fileinfo, include, is_system): """ # This is a list of all standard c++ header files, except # those already checked for above. - is_stl_h = include in _STL_HEADERS - is_cpp_h = is_stl_h or include in _CPP_HEADERS + is_cpp_h = include in _CPP_HEADERS if is_system: if is_cpp_h: @@ -2288,9 +4418,19 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): error: The function to call with any errors found. """ fileinfo = FileInfo(filename) - line = clean_lines.lines[linenum] - ParseNolintSuppressions(filename, line, linenum, error) + + # "include" should use the new style "foo/bar.h" instead of just "bar.h" + # Only do this check if the included header follows google naming + # conventions. If not, assume that it's a 3rd party API that + # requires special include conventions. + # + # We also make an exception for Lua headers, which follow google + # naming convention but not the include convention. + match = Match(r'#include\s*"([^/]+\.h)"', line) + if match and not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1)): + error(filename, linenum, 'build/include', 4, + 'Include the directory when naming .h files') # we shouldn't include a file more than once. actually, there are a # handful of instances where doing so is okay, but in general it's @@ -2299,13 +4439,17 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): if match: include = match.group(2) is_system = (match.group(1) == '<') - if include in include_state: - if not IsErrorSuppressedByNolint('build/include', linenum): - error(filename, linenum, 'build/include', 4, - '"%s" already included at %s:%s' % - (include, filename, include_state[include])) - else: - include_state[include] = linenum + duplicate_line = include_state.FindHeader(include) + if duplicate_line >= 0: + error(filename, linenum, 'build/include', 4, + '"%s" already included at %s:%s' % + (include, filename, duplicate_line)) + elif (include.endswith('.cc') and + os.path.dirname(fileinfo.RepositoryName()) != os.path.dirname(include)): + error(filename, linenum, 'build/include', 4, + 'Do not include .cc files from other packages') + elif not _THIRD_PARTY_HEADERS_PATTERN.match(include): + include_state.include_list[-1].append((include, linenum)) # We want to ensure that headers appear in the right order: # 1) for foo.cc, foo.h (preferred location) @@ -2318,31 +4462,108 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): # using a number of techniques. The include_state object keeps # track of the highest type seen, and complains if we see a # lower type after that. - if not IsErrorSuppressedByNolint('build/include_order', linenum): - error_message = include_state.CheckNextIncludeOrder( - _ClassifyInclude(fileinfo, include, is_system)) - if error_message: - error(filename, linenum, 'build/include_order', 4, - '%s. Should be: %s.h, c system, c++ system, other.' % - (error_message, fileinfo.BaseName())) - if not include_state.IsInAlphabeticalOrder(include): - if not IsErrorSuppressedByNolint('build/include_alpha', linenum): - error(filename, linenum, 'build/include_alpha', 4, - 'Include "%s" not in alphabetical order' % include) - - # Look for any of the stream classes that are part of standard C++. - match = _RE_PATTERN_INCLUDE.match(line) - if match: - include = match.group(2) - if Match(r'(f|ind|io|i|o|parse|pf|stdio|str|)?stream$', include): - # Many unit tests use cout, so we exempt them. - if not _IsTestFilename(filename): - if not IsErrorSuppressedByNolint('readability/streams', linenum): - error(filename, linenum, 'readability/streams', 3, - 'Streams are highly discouraged.') - -def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, - error): + error_message = include_state.CheckNextIncludeOrder( + _ClassifyInclude(fileinfo, include, is_system)) + if error_message: + error(filename, linenum, 'build/include_order', 4, + '%s. Should be: %s.h, c system, c++ system, other.' % + (error_message, fileinfo.BaseName())) + canonical_include = include_state.CanonicalizeAlphabeticalOrder(include) + if not include_state.IsInAlphabeticalOrder( + clean_lines, linenum, canonical_include): + error(filename, linenum, 'build/include_alpha', 4, + 'Include "%s" not in alphabetical order' % include) + include_state.SetLastHeader(canonical_include) + + + +def _GetTextInside(text, start_pattern): + r"""Retrieves all the text between matching open and close parentheses. + + Given a string of lines and a regular expression string, retrieve all the text + following the expression and between opening punctuation symbols like + (, [, or {, and the matching close-punctuation symbol. This properly nested + occurrences of the punctuations, so for the text like + printf(a(), b(c())); + a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. + start_pattern must match string having an open punctuation symbol at the end. + + Args: + text: The lines to extract text. Its comments and strings must be elided. + It can be single line and can span multiple lines. + start_pattern: The regexp string indicating where to start extracting + the text. + Returns: + The extracted text. + None if either the opening string or ending punctuation could not be found. + """ + # TODO(unknown): Audit cpplint.py to see what places could be profitably + # rewritten to use _GetTextInside (and use inferior regexp matching today). + + # Give opening punctuations to get the matching close-punctuations. + matching_punctuation = {'(': ')', '{': '}', '[': ']'} + closing_punctuation = set(matching_punctuation.itervalues()) + + # Find the position to start extracting text. + match = re.search(start_pattern, text, re.M) + if not match: # start_pattern not found in text. + return None + start_position = match.end(0) + + assert start_position > 0, ( + 'start_pattern must ends with an opening punctuation.') + assert text[start_position - 1] in matching_punctuation, ( + 'start_pattern must ends with an opening punctuation.') + # Stack of closing punctuations we expect to have in text after position. + punctuation_stack = [matching_punctuation[text[start_position - 1]]] + position = start_position + while punctuation_stack and position < len(text): + if text[position] == punctuation_stack[-1]: + punctuation_stack.pop() + elif text[position] in closing_punctuation: + # A closing punctuation without matching opening punctuations. + return None + elif text[position] in matching_punctuation: + punctuation_stack.append(matching_punctuation[text[position]]) + position += 1 + if punctuation_stack: + # Opening punctuations left without matching close-punctuations. + return None + # punctuations match. + return text[start_position:position - 1] + + +# Patterns for matching call-by-reference parameters. +# +# Supports nested templates up to 2 levels deep using this messy pattern: +# < (?: < (?: < [^<>]* +# > +# | [^<>] )* +# > +# | [^<>] )* +# > +_RE_PATTERN_IDENT = r'[_a-zA-Z]\w*' # =~ [[:alpha:]][[:alnum:]]* +_RE_PATTERN_TYPE = ( + r'(?:const\s+)?(?:typename\s+|class\s+|struct\s+|union\s+|enum\s+)?' + r'(?:\w|' + r'\s*<(?:<(?:<[^<>]*>|[^<>])*>|[^<>])*>|' + r'::)+') +# A call-by-reference parameter ends with '& identifier'. +_RE_PATTERN_REF_PARAM = re.compile( + r'(' + _RE_PATTERN_TYPE + r'(?:\s*(?:\bconst\b|[*]))*\s*' + r'&\s*' + _RE_PATTERN_IDENT + r')\s*(?:=[^,()]+)?[,)]') +# A call-by-const-reference parameter either ends with 'const& identifier' +# or looks like 'const type& identifier' when 'type' is atomic. +_RE_PATTERN_CONST_REF_PARAM = ( + r'(?:.*\s*\bconst\s*&\s*' + _RE_PATTERN_IDENT + + r'|const\s+' + _RE_PATTERN_TYPE + r'\s*&\s*' + _RE_PATTERN_IDENT + r')') +# Stream types. +_RE_PATTERN_REF_STREAM_PARAM = ( + r'(?:.*stream\s*&\s*' + _RE_PATTERN_IDENT + r')') + + +def CheckLanguage(filename, clean_lines, linenum, file_extension, + include_state, nesting_state, error): """Checks rules from the 'C++ language rules' section of cppguide.html. Some of these rules are hard to test (function overloading, using @@ -2354,6 +4575,8 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, linenum: The number of the line to check. file_extension: The extension (without the dot) of the filename. include_state: An _IncludeState instance in which the headers are inserted. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. error: The function to call with any errors found. """ # If the line is empty or consists of entirely a comment, no need to @@ -2367,110 +4590,25 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, CheckIncludeLine(filename, clean_lines, linenum, include_state, error) return - # Create an extended_line, which is the concatenation of the current and - # next lines, for more effective checking of code that may span more than one - # line. - if linenum + 1 < clean_lines.NumLines(): - extended_line = line + clean_lines.elided[linenum + 1] - else: - extended_line = line + # Reset include state across preprocessor directives. This is meant + # to silence warnings for conditional includes. + match = Match(r'^\s*#\s*(if|ifdef|ifndef|elif|else|endif)\b', line) + if match: + include_state.ResetSection(match.group(1)) # Make Windows paths like Unix. fullname = os.path.abspath(filename).replace('\\', '/') - # TODO(unknown): figure out if they're using default arguments in fn proto. - - # Check for non-const references in functions. This is tricky because & - # is also used to take the address of something. We allow <> for templates, - # (ignoring whatever is between the braces) and : for classes. - # These are complicated re's. They try to capture the following: - # paren (for fn-prototype start), typename, &, varname. For the const - # version, we're willing for const to be before typename or after - # Don't check the implemention on same line. - fnline = line.split('{', 1)[0] - if (len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) > - len(re.findall(r'\([^()]*\bconst\s+(?:typename\s+)?(?:struct\s+)?' - r'(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) + - len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+\s+const(\s?&|&\s?)[\w]+', - fnline))): - - # We allow non-const references in a few standard places, like functions - # called "swap()" or iostream operators like "<<" or ">>". - if not Search( - r'(swap|Swap|operator[<>][<>])\s*\(\s*(?:[\w:]|<.*>)+\s*&', - fnline): - error(filename, linenum, 'runtime/references', 2, - 'Is this a non-const reference? ' - 'If so, make const or use a pointer.') - - # Check to see if they're using an conversion function cast. - # I just try to capture the most common basic types, though there are more. - # Parameterless conversion functions, such as bool(), are allowed as they are - # probably a member operator declaration or default constructor. - match = Search( - r'(\bnew\s+)?\b' # Grab 'new' operator, if it's there - r'(int|float|double|bool|char|int32|uint32|int64|uint64)\([^)]', line) - if match: - # gMock methods are defined using some variant of MOCK_METHODx(name, type) - # where type may be float(), int(string), etc. Without context they are - # virtually indistinguishable from int(x) casts. - if (match.group(1) is None and # If new operator, then this isn't a cast - not Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line)): - error(filename, linenum, 'readability/casting', 4, - 'Using deprecated casting style. ' - 'Use static_cast<%s>(...) instead' % - match.group(2)) - - CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], - 'static_cast', - r'\((int|float|double|bool|char|u?int(16|32|64))\)', - error) - # This doesn't catch all cases. Consider (const char * const)"hello". - CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], - 'reinterpret_cast', r'\((\w+\s?\*+\s?)\)', error) - - # In addition, we look for people taking the address of a cast. This - # is dangerous -- casts can assign to temporaries, so the pointer doesn't - # point where you think. - if Search( - r'(&\([^)]+\)[\w(])|(&(static|dynamic|reinterpret)_cast\b)', line): - error(filename, linenum, 'runtime/casting', 4, - ('Are you taking an address of a cast? ' - 'This is dangerous: could be a temp var. ' - 'Take the address before doing the cast, rather than after')) - - # Check for people declaring static/global STL strings at the top level. - # This is dangerous because the C++ language does not guarantee that - # globals with constructors are initialized before the first access. - match = Match( - r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)', - line) - # Make sure it's not a function. - # Function template specialization looks like: "string foo<Type>(...". - # Class template definitions look like: "string Foo<Type>::Method(...". - if match and not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)?\s*\(([^"]|$)', - match.group(3)): - error(filename, linenum, 'runtime/string', 4, - 'For a static/global string constant, use a C style string instead: ' - '"%schar %s[]".' % - (match.group(1), match.group(2))) - - # Check that we're not using RTTI outside of testing code. - if Search(r'\bdynamic_cast<', line) and not _IsTestFilename(filename): - error(filename, linenum, 'runtime/rtti', 5, - 'Do not use dynamic_cast<>. If you need to cast within a class ' - "hierarchy, use static_cast<> to upcast. Google doesn't support " - 'RTTI.') - - if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line): - error(filename, linenum, 'runtime/init', 4, - 'You seem to be initializing a member variable with itself.') + # Perform other checks now that we are sure that this is not an include line + CheckCasts(filename, clean_lines, linenum, error) + CheckGlobalStatic(filename, clean_lines, linenum, error) + CheckPrintf(filename, clean_lines, linenum, error) if file_extension == 'h': # TODO(unknown): check that 1-arg constructors are explicit. # How to tell it's a constructor? # (handled in CheckForNonStandardConstructs for now) - # TODO(unknown): check that classes have DISALLOW_EVIL_CONSTRUCTORS + # TODO(unknown): check that classes declare or disable copy/assign # (level 1 error) pass @@ -2480,11 +4618,179 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, if not Search(r'\bunsigned short port\b', line): error(filename, linenum, 'runtime/int', 4, 'Use "unsigned short" for ports, not "short"') -# else: -# match = Search(r'\b(short|long(?! +double)|long long)\b', line) -# if match: -# error(filename, linenum, 'runtime/int', 4, -# 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) + else: + match = Search(r'\b(short|long(?! +double)|long long)\b', line) + if match: + error(filename, linenum, 'runtime/int', 4, + 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) + + # Check if some verboten operator overloading is going on + # TODO(unknown): catch out-of-line unary operator&: + # class X {}; + # int operator&(const X& x) { return 42; } // unary operator& + # The trick is it's hard to tell apart from binary operator&: + # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& + if Search(r'\boperator\s*&\s*\(\s*\)', line): + error(filename, linenum, 'runtime/operator', 4, + 'Unary operator& is dangerous. Do not use it.') + + # Check for suspicious usage of "if" like + # } if (a == b) { + if Search(r'\}\s*if\s*\(', line): + error(filename, linenum, 'readability/braces', 4, + 'Did you mean "else if"? If not, start a new line for "if".') + + # Check for potential format string bugs like printf(foo). + # We constrain the pattern not to pick things like DocidForPrintf(foo). + # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) + # TODO(unknown): Catch the following case. Need to change the calling + # convention of the whole function to process multiple line to handle it. + # printf( + # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); + printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') + if printf_args: + match = Match(r'([\w.\->()]+)$', printf_args) + if match and match.group(1) != '__VA_ARGS__': + function_name = re.search(r'\b((?:string)?printf)\s*\(', + line, re.I).group(1) + error(filename, linenum, 'runtime/printf', 4, + 'Potential format string bug. Do %s("%%s", %s) instead.' + % (function_name, match.group(1))) + + # Check for potential memset bugs like memset(buf, sizeof(buf), 0). + match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) + if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): + error(filename, linenum, 'runtime/memset', 4, + 'Did you mean "memset(%s, 0, %s)"?' + % (match.group(1), match.group(2))) + + if Search(r'\busing namespace\b', line): + error(filename, linenum, 'build/namespaces', 5, + 'Do not use namespace using-directives. ' + 'Use using-declarations instead.') + + # Detect variable-length arrays. + match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) + if (match and match.group(2) != 'return' and match.group(2) != 'delete' and + match.group(3).find(']') == -1): + # Split the size using space and arithmetic operators as delimiters. + # If any of the resulting tokens are not compile time constants then + # report the error. + tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) + is_const = True + skip_next = False + for tok in tokens: + if skip_next: + skip_next = False + continue + + if Search(r'sizeof\(.+\)', tok): continue + if Search(r'arraysize\(\w+\)', tok): continue + + tok = tok.lstrip('(') + tok = tok.rstrip(')') + if not tok: continue + if Match(r'\d+', tok): continue + if Match(r'0[xX][0-9a-fA-F]+', tok): continue + if Match(r'k[A-Z0-9]\w*', tok): continue + if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue + if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue + # A catch all for tricky sizeof cases, including 'sizeof expression', + # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' + # requires skipping the next token because we split on ' ' and '*'. + if tok.startswith('sizeof'): + skip_next = True + continue + is_const = False + break + if not is_const: + error(filename, linenum, 'runtime/arrays', 1, + 'Do not use variable-length arrays. Use an appropriately named ' + "('k' followed by CamelCase) compile-time constant for the size.") + + # Check for use of unnamed namespaces in header files. Registration + # macros are typically OK, so we allow use of "namespace {" on lines + # that end with backslashes. + if (file_extension == 'h' + and Search(r'\bnamespace\s*{', line) + and line[-1] != '\\'): + error(filename, linenum, 'build/namespaces', 4, + 'Do not use unnamed namespaces in header files. See ' + 'https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' + ' for more information.') + + +def CheckGlobalStatic(filename, clean_lines, linenum, error): + """Check for unsafe global or static objects. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Match two lines at a time to support multiline declarations + if linenum + 1 < clean_lines.NumLines() and not Search(r'[;({]', line): + line += clean_lines.elided[linenum + 1].strip() + + # Check for people declaring static/global STL strings at the top level. + # This is dangerous because the C++ language does not guarantee that + # globals with constructors are initialized before the first access, and + # also because globals can be destroyed when some threads are still running. + # TODO(unknown): Generalize this to also find static unique_ptr instances. + # TODO(unknown): File bugs for clang-tidy to find these. + match = Match( + r'((?:|static +)(?:|const +))(?::*std::)?string( +const)? +' + r'([a-zA-Z0-9_:]+)\b(.*)', + line) + + # Remove false positives: + # - String pointers (as opposed to values). + # string *pointer + # const string *pointer + # string const *pointer + # string *const pointer + # + # - Functions and template specializations. + # string Function<Type>(... + # string Class<Type>::Method(... + # + # - Operators. These are matched separately because operator names + # cross non-word boundaries, and trying to match both operators + # and functions at the same time would decrease accuracy of + # matching identifiers. + # string Class::operator*() + if (match and + not Search(r'\bstring\b(\s+const)?\s*[\*\&]\s*(const\s+)?\w', line) and + not Search(r'\boperator\W', line) and + not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)*\s*\(([^"]|$)', match.group(4))): + if Search(r'\bconst\b', line): + error(filename, linenum, 'runtime/string', 4, + 'For a static/global string constant, use a C style string ' + 'instead: "%schar%s %s[]".' % + (match.group(1), match.group(2) or '', match.group(3))) + else: + error(filename, linenum, 'runtime/string', 4, + 'Static/global string variables are not permitted.') + + if (Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line) or + Search(r'\b([A-Za-z0-9_]*_)\(CHECK_NOTNULL\(\1\)\)', line)): + error(filename, linenum, 'runtime/init', 4, + 'You seem to be initializing a member variable with itself.') + + +def CheckPrintf(filename, clean_lines, linenum, error): + """Check for printf related issues. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] # When snprintf is used, the second argument shouldn't be a literal. match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) @@ -2495,174 +4801,427 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, 'to snprintf.' % (match.group(1), match.group(2))) # Check if some verboten C functions are being used. - if Search(r'\bsprintf\b', line): + if Search(r'\bsprintf\s*\(', line): error(filename, linenum, 'runtime/printf', 5, - 'Never use sprintf. Use snprintf instead.') - match = Search(r'\b(strcpy|strcat)\b', line) + 'Never use sprintf. Use snprintf instead.') + match = Search(r'\b(strcpy|strcat)\s*\(', line) if match: error(filename, linenum, 'runtime/printf', 4, 'Almost always, snprintf is better than %s' % match.group(1)) - if Search(r'\bsscanf\b', line): - error(filename, linenum, 'runtime/printf', 1, - 'sscanf can be ok, but is slow and can overflow buffers.') - # Check if some verboten operator overloading is going on - # TODO(unknown): catch out-of-line unary operator&: - # class X {}; - # int operator&(const X& x) { return 42; } // unary operator& - # The trick is it's hard to tell apart from binary operator&: - # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& - if Search(r'\boperator\s*&\s*\(\s*\)', line): - error(filename, linenum, 'runtime/operator', 4, - 'Unary operator& is dangerous. Do not use it.') +def IsDerivedFunction(clean_lines, linenum): + """Check if current line contains an inherited function. - # Check for suspicious usage of "if" like - # } if (a == b) { - if Search(r'\}\s*if\s*\(', line): - error(filename, linenum, 'readability/braces', 4, - 'Did you mean "else if"? If not, start a new line for "if".') + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + Returns: + True if current line contains a function with "override" + virt-specifier. + """ + # Scan back a few lines for start of current function + for i in xrange(linenum, max(-1, linenum - 10), -1): + match = Match(r'^([^()]*\w+)\(', clean_lines.elided[i]) + if match: + # Look for "override" after the matching closing parenthesis + line, _, closing_paren = CloseExpression( + clean_lines, i, len(match.group(1))) + return (closing_paren >= 0 and + Search(r'\boverride\b', line[closing_paren:])) + return False + + +def IsOutOfLineMethodDefinition(clean_lines, linenum): + """Check if current line contains an out-of-line method definition. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + Returns: + True if current line contains an out-of-line method definition. + """ + # Scan back a few lines for start of current function + for i in xrange(linenum, max(-1, linenum - 10), -1): + if Match(r'^([^()]*\w+)\(', clean_lines.elided[i]): + return Match(r'^[^()]*\w+::\w+\(', clean_lines.elided[i]) is not None + return False + + +def IsInitializerList(clean_lines, linenum): + """Check if current line is inside constructor initializer list. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + Returns: + True if current line appears to be inside constructor initializer + list, False otherwise. + """ + for i in xrange(linenum, 1, -1): + line = clean_lines.elided[i] + if i == linenum: + remove_function_body = Match(r'^(.*)\{\s*$', line) + if remove_function_body: + line = remove_function_body.group(1) + + if Search(r'\s:\s*\w+[({]', line): + # A lone colon tend to indicate the start of a constructor + # initializer list. It could also be a ternary operator, which + # also tend to appear in constructor initializer lists as + # opposed to parameter lists. + return True + if Search(r'\}\s*,\s*$', line): + # A closing brace followed by a comma is probably the end of a + # brace-initialized member in constructor initializer list. + return True + if Search(r'[{};]\s*$', line): + # Found one of the following: + # - A closing brace or semicolon, probably the end of the previous + # function. + # - An opening brace, probably the start of current class or namespace. + # + # Current line is probably not inside an initializer list since + # we saw one of those things without seeing the starting colon. + return False + + # Got to the beginning of the file without seeing the start of + # constructor initializer list. + return False + + +def CheckForNonConstReference(filename, clean_lines, linenum, + nesting_state, error): + """Check for non-const references. + + Separate from CheckLanguage since it scans backwards from current + line, instead of scanning forward. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + # Do nothing if there is no '&' on current line. + line = clean_lines.elided[linenum] + if '&' not in line: + return + + # If a function is inherited, current function doesn't have much of + # a choice, so any non-const references should not be blamed on + # derived function. + if IsDerivedFunction(clean_lines, linenum): + return + + # Don't warn on out-of-line method definitions, as we would warn on the + # in-line declaration, if it isn't marked with 'override'. + if IsOutOfLineMethodDefinition(clean_lines, linenum): + return + + # Long type names may be broken across multiple lines, usually in one + # of these forms: + # LongType + # ::LongTypeContinued &identifier + # LongType:: + # LongTypeContinued &identifier + # LongType< + # ...>::LongTypeContinued &identifier + # + # If we detected a type split across two lines, join the previous + # line to current line so that we can match const references + # accordingly. + # + # Note that this only scans back one line, since scanning back + # arbitrary number of lines would be expensive. If you have a type + # that spans more than 2 lines, please use a typedef. + if linenum > 1: + previous = None + if Match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line): + # previous_line\n + ::current_line + previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$', + clean_lines.elided[linenum - 1]) + elif Match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line): + # previous_line::\n + current_line + previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$', + clean_lines.elided[linenum - 1]) + if previous: + line = previous.group(1) + line.lstrip() + else: + # Check for templated parameter that is split across multiple lines + endpos = line.rfind('>') + if endpos > -1: + (_, startline, startpos) = ReverseCloseExpression( + clean_lines, linenum, endpos) + if startpos > -1 and startline < linenum: + # Found the matching < on an earlier line, collect all + # pieces up to current line. + line = '' + for i in xrange(startline, linenum + 1): + line += clean_lines.elided[i].strip() + + # Check for non-const references in function parameters. A single '&' may + # found in the following places: + # inside expression: binary & for bitwise AND + # inside expression: unary & for taking the address of something + # inside declarators: reference parameter + # We will exclude the first two cases by checking that we are not inside a + # function body, including one that was just introduced by a trailing '{'. + # TODO(unknown): Doesn't account for 'catch(Exception& e)' [rare]. + if (nesting_state.previous_stack_top and + not (isinstance(nesting_state.previous_stack_top, _ClassInfo) or + isinstance(nesting_state.previous_stack_top, _NamespaceInfo))): + # Not at toplevel, not within a class, and not within a namespace + return + + # Avoid initializer lists. We only need to scan back from the + # current line for something that starts with ':'. + # + # We don't need to check the current line, since the '&' would + # appear inside the second set of parentheses on the current line as + # opposed to the first set. + if linenum > 0: + for i in xrange(linenum - 1, max(0, linenum - 10), -1): + previous_line = clean_lines.elided[i] + if not Search(r'[),]\s*$', previous_line): + break + if Match(r'^\s*:\s+\S', previous_line): + return + + # Avoid preprocessors + if Search(r'\\\s*$', line): + return + + # Avoid constructor initializer lists + if IsInitializerList(clean_lines, linenum): + return + + # We allow non-const references in a few standard places, like functions + # called "swap()" or iostream operators like "<<" or ">>". Do not check + # those function parameters. + # + # We also accept & in static_assert, which looks like a function but + # it's actually a declaration expression. + whitelisted_functions = (r'(?:[sS]wap(?:<\w:+>)?|' + r'operator\s*[<>][<>]|' + r'static_assert|COMPILE_ASSERT' + r')\s*\(') + if Search(whitelisted_functions, line): + return + elif not Search(r'\S+\([^)]*$', line): + # Don't see a whitelisted function on this line. Actually we + # didn't see any function name on this line, so this is likely a + # multi-line parameter list. Try a bit harder to catch this case. + for i in xrange(2): + if (linenum > i and + Search(whitelisted_functions, clean_lines.elided[linenum - i - 1])): + return + + decls = ReplaceAll(r'{[^}]*}', ' ', line) # exclude function body + for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls): + if (not Match(_RE_PATTERN_CONST_REF_PARAM, parameter) and + not Match(_RE_PATTERN_REF_STREAM_PARAM, parameter)): + error(filename, linenum, 'runtime/references', 2, + 'Is this a non-const reference? ' + 'If so, make const or use a pointer: ' + + ReplaceAll(' *<', '<', parameter)) - # Check for potential format string bugs like printf(foo). - # We constrain the pattern not to pick things like DocidForPrintf(foo). - # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) - match = re.search(r'\b((?:string)?printf)\s*\(([\w.\->()]+)\)', line, re.I) - if match: - error(filename, linenum, 'runtime/printf', 4, - 'Potential format string bug. Do %s("%%s", %s) instead.' - % (match.group(1), match.group(2))) - # Check for potential memset bugs like memset(buf, sizeof(buf), 0). - match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) - if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): - error(filename, linenum, 'runtime/memset', 4, - 'Did you mean "memset(%s, 0, %s)"?' - % (match.group(1), match.group(2))) +def CheckCasts(filename, clean_lines, linenum, error): + """Various cast related checks. - if Search(r'\busing namespace\b', line): - error(filename, linenum, 'build/namespaces', 5, - 'Do not use namespace using-directives. ' - 'Use using-declarations instead.') + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] - # Detect variable-length arrays. - match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) - if (match and match.group(2) != 'return' and match.group(2) != 'delete' and - match.group(3).find(']') == -1): - # Split the size using space and arithmetic operators as delimiters. - # If any of the resulting tokens are not compile time constants then - # report the error. - tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) - is_const = True - skip_next = False - for tok in tokens: - if skip_next: - skip_next = False - continue + # Check to see if they're using an conversion function cast. + # I just try to capture the most common basic types, though there are more. + # Parameterless conversion functions, such as bool(), are allowed as they are + # probably a member operator declaration or default constructor. + match = Search( + r'(\bnew\s+(?:const\s+)?|\S<\s*(?:const\s+)?)?\b' + r'(int|float|double|bool|char|int32|uint32|int64|uint64)' + r'(\([^)].*)', line) + expecting_function = ExpectingFunctionArgs(clean_lines, linenum) + if match and not expecting_function: + matched_type = match.group(2) + + # matched_new_or_template is used to silence two false positives: + # - New operators + # - Template arguments with function types + # + # For template arguments, we match on types immediately following + # an opening bracket without any spaces. This is a fast way to + # silence the common case where the function type is the first + # template argument. False negative with less-than comparison is + # avoided because those operators are usually followed by a space. + # + # function<double(double)> // bracket + no space = false positive + # value < double(42) // bracket + space = true positive + matched_new_or_template = match.group(1) - if Search(r'sizeof\(.+\)', tok): continue - if Search(r'arraysize\(\w+\)', tok): continue + # Avoid arrays by looking for brackets that come after the closing + # parenthesis. + if Match(r'\([^()]+\)\s*\[', match.group(3)): + return - tok = tok.lstrip('(') - tok = tok.rstrip(')') - if not tok: continue - if Match(r'\d+', tok): continue - if Match(r'0[xX][0-9a-fA-F]+', tok): continue - if Match(r'k[A-Z0-9]\w*', tok): continue - if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue - if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue - # A catch all for tricky sizeof cases, including 'sizeof expression', - # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' - # requires skipping the next token becasue we split on ' ' and '*'. - if tok.startswith('sizeof'): - skip_next = True - continue - is_const = False - break - if not is_const: - error(filename, linenum, 'runtime/arrays', 1, - 'Do not use variable-length arrays. Use an appropriately named ' - "('k' followed by CamelCase) compile-time constant for the size.") + # Other things to ignore: + # - Function pointers + # - Casts to pointer types + # - Placement new + # - Alias declarations + matched_funcptr = match.group(3) + if (matched_new_or_template is None and + not (matched_funcptr and + (Match(r'\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(', + matched_funcptr) or + matched_funcptr.startswith('(*)'))) and + not Match(r'\s*using\s+\S+\s*=\s*' + matched_type, line) and + not Search(r'new\(\S+\)\s*' + matched_type, line)): + error(filename, linenum, 'readability/casting', 4, + 'Using deprecated casting style. ' + 'Use static_cast<%s>(...) instead' % + matched_type) - # If DISALLOW_EVIL_CONSTRUCTORS, DISALLOW_COPY_AND_ASSIGN, or - # DISALLOW_IMPLICIT_CONSTRUCTORS is present, then it should be the last thing - # in the class declaration. - match = Match( - (r'\s*' - r'(DISALLOW_(EVIL_CONSTRUCTORS|COPY_AND_ASSIGN|IMPLICIT_CONSTRUCTORS))' - r'\(.*\);$'), - line) - if match and linenum + 1 < clean_lines.NumLines(): - next_line = clean_lines.elided[linenum + 1] - if not Search(r'^\s*};', next_line): - error(filename, linenum, 'readability/constructors', 3, - match.group(1) + ' should be the last thing in the class') + if not expecting_function: + CheckCStyleCast(filename, clean_lines, linenum, 'static_cast', + r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) - # Check for use of unnamed namespaces in header files. Registration - # macros are typically OK, so we allow use of "namespace {" on lines - # that end with backslashes. - if (file_extension == 'h' - and Search(r'\bnamespace\s*{', line) - and line[-1] != '\\'): - error(filename, linenum, 'build/namespaces', 4, - 'Do not use unnamed namespaces in header files. See ' - 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' - ' for more information.') + # This doesn't catch all cases. Consider (const char * const)"hello". + # + # (char *) "foo" should always be a const_cast (reinterpret_cast won't + # compile). + if CheckCStyleCast(filename, clean_lines, linenum, 'const_cast', + r'\((char\s?\*+\s?)\)\s*"', error): + pass + else: + # Check pointer casts for other than string constants + CheckCStyleCast(filename, clean_lines, linenum, 'reinterpret_cast', + r'\((\w+\s?\*+\s?)\)', error) + + # In addition, we look for people taking the address of a cast. This + # is dangerous -- casts can assign to temporaries, so the pointer doesn't + # point where you think. + # + # Some non-identifier character is required before the '&' for the + # expression to be recognized as a cast. These are casts: + # expression = &static_cast<int*>(temporary()); + # function(&(int*)(temporary())); + # + # This is not a cast: + # reference_type&(int* function_param); + match = Search( + r'(?:[^\w]&\(([^)*][^)]*)\)[\w(])|' + r'(?:[^\w]&(static|dynamic|down|reinterpret)_cast\b)', line) + if match: + # Try a better error message when the & is bound to something + # dereferenced by the casted pointer, as opposed to the casted + # pointer itself. + parenthesis_error = False + match = Match(r'^(.*&(?:static|dynamic|down|reinterpret)_cast\b)<', line) + if match: + _, y1, x1 = CloseExpression(clean_lines, linenum, len(match.group(1))) + if x1 >= 0 and clean_lines.elided[y1][x1] == '(': + _, y2, x2 = CloseExpression(clean_lines, y1, x1) + if x2 >= 0: + extended_line = clean_lines.elided[y2][x2:] + if y2 < clean_lines.NumLines() - 1: + extended_line += clean_lines.elided[y2 + 1] + if Match(r'\s*(?:->|\[)', extended_line): + parenthesis_error = True + + if parenthesis_error: + error(filename, linenum, 'readability/casting', 4, + ('Are you taking an address of something dereferenced ' + 'from a cast? Wrapping the dereferenced expression in ' + 'parentheses will make the binding more obvious')) + else: + error(filename, linenum, 'runtime/casting', 4, + ('Are you taking an address of a cast? ' + 'This is dangerous: could be a temp var. ' + 'Take the address before doing the cast, rather than after')) -def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern, - error): +def CheckCStyleCast(filename, clean_lines, linenum, cast_type, pattern, error): """Checks for a C-style cast by looking for the pattern. - This also handles sizeof(type) warnings, due to similarity of content. - Args: filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. - line: The line of code to check. - raw_line: The raw line of code to check, with comments. cast_type: The string for the C++ cast to recommend. This is either - reinterpret_cast or static_cast, depending. + reinterpret_cast, static_cast, or const_cast, depending. pattern: The regular expression used to find C-style casts. error: The function to call with any errors found. + + Returns: + True if an error was emitted. + False otherwise. """ + line = clean_lines.elided[linenum] match = Search(pattern, line) if not match: - return + return False - # e.g., sizeof(int) - sizeof_match = Match(r'.*sizeof\s*$', line[0:match.start(1) - 1]) - if sizeof_match: - error(filename, linenum, 'runtime/sizeof', 1, - 'Using sizeof(type). Use sizeof(varname) instead if possible') - return + # Exclude lines with keywords that tend to look like casts + context = line[0:match.start(1) - 1] + if Match(r'.*\b(?:sizeof|alignof|alignas|[_A-Z][_A-Z0-9]*)\s*$', context): + return False - remainder = line[match.end(0):] + # Try expanding current context to see if we one level of + # parentheses inside a macro. + if linenum > 0: + for i in xrange(linenum - 1, max(0, linenum - 5), -1): + context = clean_lines.elided[i] + context + if Match(r'.*\b[_A-Z][_A-Z0-9]*\s*\((?:\([^()]*\)|[^()])*$', context): + return False - # The close paren is for function pointers as arguments to a function. - # eg, void foo(void (*bar)(int)); - # The semicolon check is a more basic function check; also possibly a - # function pointer typedef. - # eg, void foo(int); or void foo(int) const; - # The equals check is for function pointer assignment. - # eg, void *(*foo)(int) = ... - # - # Right now, this will only catch cases where there's a single argument, and - # it's unnamed. It should probably be expanded to check for multiple - # arguments with some unnamed. - function_match = Match(r'\s*(\)|=|(const)?\s*(;|\{|throw\(\)))', remainder) - if function_match: - if (not function_match.group(3) or - function_match.group(3) == ';' or - raw_line.find('/*') < 0): - error(filename, linenum, 'readability/function', 3, - 'All parameters should be named in a function') - return + # operator++(int) and operator--(int) + if context.endswith(' operator++') or context.endswith(' operator--'): + return False + + # A single unnamed argument for a function tends to look like old style cast. + # If we see those, don't issue warnings for deprecated casts. + remainder = line[match.end(0):] + if Match(r'^\s*(?:;|const\b|throw\b|final\b|override\b|[=>{),]|->)', + remainder): + return False # At this point, all that should be left is actual casts. error(filename, linenum, 'readability/casting', 4, 'Using C-style cast. Use %s<%s>(...) instead' % (cast_type, match.group(1))) + return True + + +def ExpectingFunctionArgs(clean_lines, linenum): + """Checks whether where function type arguments are expected. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + + Returns: + True if the line at 'linenum' is inside something that expects arguments + of function types. + """ + line = clean_lines.elided[linenum] + return (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or + (linenum >= 2 and + (Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\((?:\S+,)?\s*$', + clean_lines.elided[linenum - 1]) or + Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\(\s*$', + clean_lines.elided[linenum - 2]) or + Search(r'\bstd::m?function\s*\<\s*$', + clean_lines.elided[linenum - 1])))) + _HEADERS_CONTAINING_TEMPLATES = ( ('<deque>', ('deque',)), @@ -2691,6 +5250,7 @@ def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern, ('<set>', ('set', 'multiset',)), ('<stack>', ('stack',)), ('<string>', ('char_traits', 'basic_string',)), + ('<tuple>', ('tuple',)), ('<utility>', ('pair',)), ('<vector>', ('vector',)), @@ -2701,23 +5261,26 @@ def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern, ('<slist>', ('slist',)), ) -_HEADERS_ACCEPTED_BUT_NOT_PROMOTED = { - # We can trust with reasonable confidence that map gives us pair<>, too. - 'pair<>': ('map', 'multimap', 'hash_map', 'hash_multimap') -} +_HEADERS_MAYBE_TEMPLATES = ( + ('<algorithm>', ('copy', 'max', 'min', 'min_element', 'sort', + 'transform', + )), + ('<utility>', ('swap',)), + ) _RE_PATTERN_STRING = re.compile(r'\bstring\b') -_re_pattern_algorithm_header = [] -for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap', - 'transform'): - # Match max<type>(..., ...), max(..., ...), but not foo->max, foo.max or - # type::max(). - _re_pattern_algorithm_header.append( - (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), - _template, - '<algorithm>')) - +_re_pattern_headers_maybe_templates = [] +for _header, _templates in _HEADERS_MAYBE_TEMPLATES: + for _template in _templates: + # Match max<type>(..., ...), max(..., ...), but not foo->max, foo.max or + # type::max(). + _re_pattern_headers_maybe_templates.append( + (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), + _template, + _header)) + +# Other scripts may reach in and modify this pattern. _re_pattern_templates = [] for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: for _template in _templates: @@ -2757,13 +5320,13 @@ def FilesBelongToSameModule(filename_cc, filename_h): string: the additional prefix needed to open the header file. """ - if not filename_cc.endswith('.cc'): + fileinfo = FileInfo(filename_cc) + if not fileinfo.IsSource(): return (False, '') - filename_cc = filename_cc[:-len('.cc')] - if filename_cc.endswith('_unittest'): - filename_cc = filename_cc[:-len('_unittest')] - elif filename_cc.endswith('_test'): - filename_cc = filename_cc[:-len('_test')] + filename_cc = filename_cc[:-len(fileinfo.Extension())] + matched_test_suffix = Search(_TEST_FILE_SUFFIX, fileinfo.BaseName()) + if matched_test_suffix: + filename_cc = filename_cc[:-len(matched_test_suffix.group(1))] filename_cc = filename_cc.replace('/public/', '/') filename_cc = filename_cc.replace('/internal/', '/') @@ -2782,16 +5345,16 @@ def FilesBelongToSameModule(filename_cc, filename_h): return files_belong_to_same_module, common_path -def UpdateIncludeState(filename, include_state, io=codecs): - """Fill up the include_state with new includes found from the file. +def UpdateIncludeState(filename, include_dict, io=codecs): + """Fill up the include_dict with new includes found from the file. Args: filename: the name of the header to read. - include_state: an _IncludeState instance in which the headers are inserted. + include_dict: a dictionary in which the headers are inserted. io: The io factory to use to read the file. Provided for testability. Returns: - True if a header was succesfully added. False otherwise. + True if a header was successfully added. False otherwise. """ headerfile = None try: @@ -2805,9 +5368,7 @@ def UpdateIncludeState(filename, include_state, io=codecs): match = _RE_PATTERN_INCLUDE.search(clean_line) if match: include = match.group(2) - # The value formatting is cute, but not really used right now. - # What matters here is that the key is in include_state. - include_state.setdefault(include, '%s:%d' % (filename, linenum)) + include_dict.setdefault(include, linenum) return True @@ -2837,7 +5398,16 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, if not line or line[0] == '#': continue - for pattern, template, header in _re_pattern_algorithm_header: + # String is special -- it is a non-templatized type in STL. + matched = _RE_PATTERN_STRING.search(line) + if matched: + # Don't warn about strings in non-STL namespaces: + # (We check only the first match per line; good enough.) + prefix = line[:matched.start()] + if prefix.endswith('std::') or not prefix.endswith('::'): + required['<string>'] = (linenum, 'string') + + for pattern, template, header in _re_pattern_headers_maybe_templates: if pattern.search(line): required[header] = (linenum, template) @@ -2851,14 +5421,15 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, # The policy is that if you #include something in foo.h you don't need to # include it again in foo.cc. Here, we will look at possible includes. - # Let's copy the include_state so it is only messed up within this function. - include_state = include_state.copy() + # Let's flatten the include_state include_list and copy it into a dictionary. + include_dict = dict([item for sublist in include_state.include_list + for item in sublist]) - # Did we find the header for this file (if any) and succesfully load it? + # Did we find the header for this file (if any) and successfully load it? header_found = False # Use the absolute path so that matching works properly. - abs_filename = os.path.abspath(filename) + abs_filename = FileInfo(filename).FullName() # For Emacs's flymake. # If cpplint is invoked from Emacs's flymake, a temporary file is generated @@ -2869,12 +5440,13 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, # instead of 'foo_flymake.h' abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) - # include_state is modified during iteration, so we iterate over a copy of + # include_dict is modified during iteration, so we iterate over a copy of # the keys. - for header in include_state.keys(): #NOLINT + header_keys = include_dict.keys() + for header in header_keys: (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) fullpath = common_path + header - if same_module and UpdateIncludeState(fullpath, include_state, io): + if same_module and UpdateIncludeState(fullpath, include_dict, io): header_found = True # If we can't find the header file for a .cc, assume it's because we don't @@ -2888,19 +5460,198 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, # All the lines have been processed, report the errors found. for required_header_unstripped in required: template = required[required_header_unstripped][1] - if template in _HEADERS_ACCEPTED_BUT_NOT_PROMOTED: - headers = _HEADERS_ACCEPTED_BUT_NOT_PROMOTED[template] - if [True for header in headers if header in include_state]: - continue - if required_header_unstripped.strip('<>"') not in include_state: + if required_header_unstripped.strip('<>"') not in include_dict: error(filename, required[required_header_unstripped][0], 'build/include_what_you_use', 4, 'Add #include ' + required_header_unstripped + ' for ' + template) -def ProcessLine(filename, file_extension, - clean_lines, line, include_state, function_state, - class_state, error): +_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') + + +def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error): + """Check that make_pair's template arguments are deduced. + + G++ 4.6 in C++11 mode fails badly if make_pair's template arguments are + specified explicitly, and such use isn't intended in any case. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line) + if match: + error(filename, linenum, 'build/explicit_make_pair', + 4, # 4 = high confidence + 'For C++11-compatibility, omit template arguments from make_pair' + ' OR use pair directly OR if appropriate, construct a pair directly') + + +def CheckRedundantVirtual(filename, clean_lines, linenum, error): + """Check if line contains a redundant "virtual" function-specifier. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + # Look for "virtual" on current line. + line = clean_lines.elided[linenum] + virtual = Match(r'^(.*)(\bvirtual\b)(.*)$', line) + if not virtual: return + + # Ignore "virtual" keywords that are near access-specifiers. These + # are only used in class base-specifier and do not apply to member + # functions. + if (Search(r'\b(public|protected|private)\s+$', virtual.group(1)) or + Match(r'^\s+(public|protected|private)\b', virtual.group(3))): + return + + # Ignore the "virtual" keyword from virtual base classes. Usually + # there is a column on the same line in these cases (virtual base + # classes are rare in google3 because multiple inheritance is rare). + if Match(r'^.*[^:]:[^:].*$', line): return + + # Look for the next opening parenthesis. This is the start of the + # parameter list (possibly on the next line shortly after virtual). + # TODO(unknown): doesn't work if there are virtual functions with + # decltype() or other things that use parentheses, but csearch suggests + # that this is rare. + end_col = -1 + end_line = -1 + start_col = len(virtual.group(2)) + for start_line in xrange(linenum, min(linenum + 3, clean_lines.NumLines())): + line = clean_lines.elided[start_line][start_col:] + parameter_list = Match(r'^([^(]*)\(', line) + if parameter_list: + # Match parentheses to find the end of the parameter list + (_, end_line, end_col) = CloseExpression( + clean_lines, start_line, start_col + len(parameter_list.group(1))) + break + start_col = 0 + + if end_col < 0: + return # Couldn't find end of parameter list, give up + + # Look for "override" or "final" after the parameter list + # (possibly on the next few lines). + for i in xrange(end_line, min(end_line + 3, clean_lines.NumLines())): + line = clean_lines.elided[i][end_col:] + match = Search(r'\b(override|final)\b', line) + if match: + error(filename, linenum, 'readability/inheritance', 4, + ('"virtual" is redundant since function is ' + 'already declared as "%s"' % match.group(1))) + + # Set end_col to check whole lines after we are done with the + # first line. + end_col = 0 + if Search(r'[^\w]\s*$', line): + break + + +def CheckRedundantOverrideOrFinal(filename, clean_lines, linenum, error): + """Check if line contains a redundant "override" or "final" virt-specifier. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + # Look for closing parenthesis nearby. We need one to confirm where + # the declarator ends and where the virt-specifier starts to avoid + # false positives. + line = clean_lines.elided[linenum] + declarator_end = line.rfind(')') + if declarator_end >= 0: + fragment = line[declarator_end:] + else: + if linenum > 1 and clean_lines.elided[linenum - 1].rfind(')') >= 0: + fragment = line + else: + return + + # Check that at most one of "override" or "final" is present, not both + if Search(r'\boverride\b', fragment) and Search(r'\bfinal\b', fragment): + error(filename, linenum, 'readability/inheritance', 4, + ('"override" is redundant since function is ' + 'already declared as "final"')) + + + + +# Returns true if we are at a new block, and it is directly +# inside of a namespace. +def IsBlockInNameSpace(nesting_state, is_forward_declaration): + """Checks that the new block is directly in a namespace. + + Args: + nesting_state: The _NestingState object that contains info about our state. + is_forward_declaration: If the class is a forward declared class. + Returns: + Whether or not the new block is directly in a namespace. + """ + if is_forward_declaration: + if len(nesting_state.stack) >= 1 and ( + isinstance(nesting_state.stack[-1], _NamespaceInfo)): + return True + else: + return False + + return (len(nesting_state.stack) > 1 and + nesting_state.stack[-1].check_namespace_indentation and + isinstance(nesting_state.stack[-2], _NamespaceInfo)) + + +def ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, + raw_lines_no_comments, linenum): + """This method determines if we should apply our namespace indentation check. + + Args: + nesting_state: The current nesting state. + is_namespace_indent_item: If we just put a new class on the stack, True. + If the top of the stack is not a class, or we did not recently + add the class, False. + raw_lines_no_comments: The lines without the comments. + linenum: The current line number we are processing. + + Returns: + True if we should apply our namespace indentation check. Currently, it + only works for classes and namespaces inside of a namespace. + """ + + is_forward_declaration = IsForwardClassDeclaration(raw_lines_no_comments, + linenum) + + if not (is_namespace_indent_item or is_forward_declaration): + return False + + # If we are in a macro, we do not want to check the namespace indentation. + if IsMacroDefinition(raw_lines_no_comments, linenum): + return False + + return IsBlockInNameSpace(nesting_state, is_forward_declaration) + + +# Call this method if the line is directly inside of a namespace. +# If the line above is blank (excluding comments) or the start of +# an inner namespace, it cannot be indented. +def CheckItemIndentationInNamespace(filename, raw_lines_no_comments, linenum, + error): + line = raw_lines_no_comments[linenum] + if Match(r'^\s+', line): + error(filename, linenum, 'runtime/indentation_namespace', 4, + 'Do not indent within a namespace') + + +def ProcessLine(filename, file_extension, clean_lines, line, + include_state, function_state, nesting_state, error, + extra_check_functions=[]): """Processes a single line in the file. Args: @@ -2911,65 +5662,236 @@ def ProcessLine(filename, file_extension, line: Number of line being processed. include_state: An _IncludeState instance in which the headers are inserted. function_state: A _FunctionState instance which counts function lines, etc. - class_state: A _ClassState instance which maintains information about - the current stack of nested class declarations being parsed. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. error: A callable to which errors are reported, which takes 4 arguments: filename, line number, error level, and message - + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error """ raw_lines = clean_lines.raw_lines ParseNolintSuppressions(filename, raw_lines[line], line, error) + nesting_state.Update(filename, clean_lines, line, error) + CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, + error) + if nesting_state.InAsmBlock(): return CheckForFunctionLengths(filename, clean_lines, line, function_state, error) CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) - CheckStyle(filename, clean_lines, line, file_extension, error) + CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error) CheckLanguage(filename, clean_lines, line, file_extension, include_state, - error) + nesting_state, error) + CheckForNonConstReference(filename, clean_lines, line, nesting_state, error) CheckForNonStandardConstructs(filename, clean_lines, line, - class_state, error) + nesting_state, error) + CheckVlogArguments(filename, clean_lines, line, error) CheckPosixThreading(filename, clean_lines, line, error) CheckInvalidIncrement(filename, clean_lines, line, error) + CheckMakePairUsesDeduction(filename, clean_lines, line, error) + CheckRedundantVirtual(filename, clean_lines, line, error) + CheckRedundantOverrideOrFinal(filename, clean_lines, line, error) + for check_fn in extra_check_functions: + check_fn(filename, clean_lines, line, error) + +def FlagCxx11Features(filename, clean_lines, linenum, error): + """Flag those c++11 features that we only allow in certain places. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line) + + # Flag unapproved C++ TR1 headers. + if include and include.group(1).startswith('tr1/'): + error(filename, linenum, 'build/c++tr1', 5, + ('C++ TR1 headers such as <%s> are unapproved.') % include.group(1)) + + # Flag unapproved C++11 headers. + if include and include.group(1) in ('cfenv', + 'condition_variable', + 'fenv.h', + 'future', + 'mutex', + 'thread', + 'chrono', + 'ratio', + 'regex', + 'system_error', + ): + error(filename, linenum, 'build/c++11', 5, + ('<%s> is an unapproved C++11 header.') % include.group(1)) + + # The only place where we need to worry about C++11 keywords and library + # features in preprocessor directives is in macro definitions. + if Match(r'\s*#', line) and not Match(r'\s*#\s*define\b', line): return + + # These are classes and free functions. The classes are always + # mentioned as std::*, but we only catch the free functions if + # they're not found by ADL. They're alphabetical by header. + for top_name in ( + # type_traits + 'alignment_of', + 'aligned_union', + ): + if Search(r'\bstd::%s\b' % top_name, line): + error(filename, linenum, 'build/c++11', 5, + ('std::%s is an unapproved C++11 class or function. Send c-style ' + 'an example of where it would make your code more readable, and ' + 'they may let you use it.') % top_name) + + +def FlagCxx14Features(filename, clean_lines, linenum, error): + """Flag those C++14 features that we restrict. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line) + # Flag unapproved C++14 headers. + if include and include.group(1) in ('scoped_allocator', 'shared_mutex'): + error(filename, linenum, 'build/c++14', 5, + ('<%s> is an unapproved C++14 header.') % include.group(1)) -def ProcessFileData(filename, file_extension, lines, error): + +def ProcessFileData(filename, file_extension, lines, error, + extra_check_functions=[]): """Performs lint checks and reports any errors to the given error function. Args: filename: Filename of the file that is being processed. file_extension: The extension (dot not included) of the file. lines: An array of strings, each representing a line of the file, with the - last element being empty if the file is termined with a newline. + last element being empty if the file is terminated with a newline. error: A callable to which errors are reported, which takes 4 arguments: + filename, line number, error level, and message + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error """ lines = (['// marker so line numbers and indices both start at 1'] + lines + ['// marker so line numbers end in a known way']) include_state = _IncludeState() function_state = _FunctionState() - class_state = _ClassState() + nesting_state = NestingState() ResetNolintSuppressions() CheckForCopyright(filename, lines, error) + ProcessGlobalSuppresions(lines) + RemoveMultiLineComments(filename, lines, error) + clean_lines = CleansedLines(lines) if file_extension == 'h': - CheckForHeaderGuard(filename, lines, error) + CheckForHeaderGuard(filename, clean_lines, error) - RemoveMultiLineComments(filename, lines, error) - clean_lines = CleansedLines(lines) for line in xrange(clean_lines.NumLines()): ProcessLine(filename, file_extension, clean_lines, line, - include_state, function_state, class_state, error) - class_state.CheckFinished(filename, error) + include_state, function_state, nesting_state, error, + extra_check_functions) + FlagCxx11Features(filename, clean_lines, line, error) + nesting_state.CheckCompletedBlocks(filename, error) CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) + # Check that the .cc file has included its header if it exists. + if _IsSourceExtension(file_extension): + CheckHeaderFileIncluded(filename, include_state, error) + # We check here rather than inside ProcessLine so that we see raw # lines rather than "cleaned" lines. - CheckForUnicodeReplacementCharacters(filename, lines, error) + CheckForBadCharacters(filename, lines, error) CheckForNewlineAtEOF(filename, lines, error) -def ProcessFile(filename, vlevel): +def ProcessConfigOverrides(filename): + """ Loads the configuration files and processes the config overrides. + + Args: + filename: The name of the file being processed by the linter. + + Returns: + False if the current |filename| should not be processed further. + """ + + abs_filename = os.path.abspath(filename) + cfg_filters = [] + keep_looking = True + while keep_looking: + abs_path, base_name = os.path.split(abs_filename) + if not base_name: + break # Reached the root directory. + + cfg_file = os.path.join(abs_path, "CPPLINT.cfg") + abs_filename = abs_path + if not os.path.isfile(cfg_file): + continue + + try: + with open(cfg_file) as file_handle: + for line in file_handle: + line, _, _ = line.partition('#') # Remove comments. + if not line.strip(): + continue + + name, _, val = line.partition('=') + name = name.strip() + val = val.strip() + if name == 'set noparent': + keep_looking = False + elif name == 'filter': + cfg_filters.append(val) + elif name == 'exclude_files': + # When matching exclude_files pattern, use the base_name of + # the current file name or the directory name we are processing. + # For example, if we are checking for lint errors in /foo/bar/baz.cc + # and we found the .cfg file at /foo/CPPLINT.cfg, then the config + # file's "exclude_files" filter is meant to be checked against "bar" + # and not "baz" nor "bar/baz.cc". + if base_name: + pattern = re.compile(val) + if pattern.match(base_name): + sys.stderr.write('Ignoring "%s": file excluded by "%s". ' + 'File path component "%s" matches ' + 'pattern "%s"\n' % + (filename, cfg_file, base_name, val)) + return False + elif name == 'linelength': + global _line_length + try: + _line_length = int(val) + except ValueError: + sys.stderr.write('Line length must be numeric.') + else: + sys.stderr.write( + 'Invalid configuration option (%s) in file %s\n' % + (name, cfg_file)) + + except IOError: + sys.stderr.write( + "Skipping config file '%s': Can't open for reading\n" % cfg_file) + keep_looking = False + + # Apply all the accumulated filters in reverse order (top-level directory + # config options having the least priority). + for filter in reversed(cfg_filters): + _AddFilters(filter) + + return True + + +def ProcessFile(filename, vlevel, extra_check_functions=[]): """Does google-lint on a single file. Args: @@ -2977,10 +5899,21 @@ def ProcessFile(filename, vlevel): vlevel: The level of errors to report. Every error of confidence >= verbose_level will be reported. 0 is a good default. + + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error """ _SetVerboseLevel(vlevel) + _BackupFilters() + + if not ProcessConfigOverrides(filename): + _RestoreFilters() + return + lf_lines = [] + crlf_lines = [] try: # Support the UNIX convention of using "-" for stdin. Note that # we are not opening the file with universal newline support @@ -2988,10 +5921,7 @@ def ProcessFile(filename, vlevel): # contain trailing '\r' characters if we are reading a file that # has CRLF endings. # If after the split a trailing '\r' is present, it is removed - # below. If it is not expected to be present (i.e. os.linesep != - # '\r\n' as in Windows), a warning is issued below if this file - # is processed. - + # below. if filename == '-': lines = codecs.StreamReaderWriter(sys.stdin, codecs.getreader('utf8'), @@ -3000,16 +5930,19 @@ def ProcessFile(filename, vlevel): else: lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') - carriage_return_found = False # Remove trailing '\r'. - for linenum in range(len(lines)): + # The -1 accounts for the extra trailing blank line we get from split() + for linenum in range(len(lines) - 1): if lines[linenum].endswith('\r'): lines[linenum] = lines[linenum].rstrip('\r') - carriage_return_found = True + crlf_lines.append(linenum + 1) + else: + lf_lines.append(linenum + 1) except IOError: sys.stderr.write( "Skipping input '%s': Can't open for reading\n" % filename) + _RestoreFilters() return # Note, if no dot is found, this will give the entire filename as the ext. @@ -3017,17 +5950,33 @@ def ProcessFile(filename, vlevel): # When reading from stdin, the extension is unknown, so no cpplint tests # should rely on the extension. - if (filename != '-' and file_extension != 'cc' and file_extension != 'h' - and file_extension != 'cpp'): - sys.stderr.write('Ignoring %s; not a .cc or .h file\n' % filename) + if filename != '-' and file_extension not in _valid_extensions: + sys.stderr.write('Ignoring %s; not a valid file name ' + '(%s)\n' % (filename, ', '.join(_valid_extensions))) else: - ProcessFileData(filename, file_extension, lines, Error) - if carriage_return_found and os.linesep != '\r\n': - # Use 0 for linenum since outputing only one error for potentially - # several lines. - Error(filename, 0, 'whitespace/newline', 1, - 'One or more unexpected \\r (^M) found;' - 'better to use only a \\n') + ProcessFileData(filename, file_extension, lines, Error, + extra_check_functions) + + # If end-of-line sequences are a mix of LF and CR-LF, issue + # warnings on the lines with CR. + # + # Don't issue any warnings if all lines are uniformly LF or CR-LF, + # since critique can handle these just fine, and the style guide + # doesn't dictate a particular end of line sequence. + # + # We can't depend on os.linesep to determine what the desired + # end-of-line sequence should be, since that will return the + # server-side end-of-line sequence. + if lf_lines and crlf_lines: + # Warn on every line with CR. An alternative approach might be to + # check whether the file is mostly CRLF or just LF, and warn on the + # minority, we bias toward LF here since most tools prefer LF. + for linenum in crlf_lines: + Error(filename, linenum, 'whitespace/newline', 1, + 'Unexpected \\r (^M) found; better to use only \\n') + + sys.stderr.write('Done processing %s\n' % filename) + _RestoreFilters() def PrintUsage(message): @@ -3067,7 +6016,9 @@ def ParseArguments(args): (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', 'counting=', 'filter=', - 'logfile=']) + 'root=', + 'linelength=', + 'extensions=']) except getopt.GetoptError: PrintUsage('Invalid arguments.') @@ -3075,14 +6026,13 @@ def ParseArguments(args): output_format = _OutputFormat() filters = '' counting_style = '' - output_filename = '' for (opt, val) in opts: if opt == '--help': PrintUsage(None) elif opt == '--output': - if not val in ('emacs', 'vs7', 'tap'): - PrintUsage('The only allowed output formats are emacs, vs7 and tap.') + if val not in ('emacs', 'vs7', 'eclipse'): + PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') output_format = val elif opt == '--verbose': verbosity = int(val) @@ -3094,8 +6044,21 @@ def ParseArguments(args): if val not in ('total', 'toplevel', 'detailed'): PrintUsage('Valid counting options are total, toplevel, and detailed') counting_style = val - elif opt == '--logfile': - output_filename = val + elif opt == '--root': + global _root + _root = val + elif opt == '--linelength': + global _line_length + try: + _line_length = int(val) + except ValueError: + PrintUsage('Line length must be digits.') + elif opt == '--extensions': + global _valid_extensions + try: + _valid_extensions = set(val.split(',')) + except ValueError: + PrintUsage('Extensions must be comma seperated list.') if not filenames: PrintUsage('No files were specified.') @@ -3104,8 +6067,6 @@ def ParseArguments(args): _SetVerboseLevel(verbosity) _SetFilters(filters) _SetCountingStyle(counting_style) - if output_filename: - _setOutputFile(output_filename) return filenames @@ -3120,14 +6081,6 @@ def main(): codecs.getwriter('utf8'), 'replace') - - ch = logging.StreamHandler(sys.stdout) - logger.addHandler(ch) - logger.setLevel(logging.INFO) - - if _cpplint_state.output_format == 'tap': - logger.info('TAP version 13') - _cpplint_state.ResetErrorCounts() for filename in filenames: ProcessFile(filename, _cpplint_state.verbose_level) From e3662a4386d03e55b44c3c6a33b014fe5bb83f0a Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 072/131] tools: disable unwanted cpplint rules again This commit disables the build/include, build/include_alpha, build/include_order and legal/copyright warnings again. PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- tools/cpplint.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/cpplint.py b/tools/cpplint.py index 94913206f9bf2b..0b79864f1a0882 100644 --- a/tools/cpplint.py +++ b/tools/cpplint.py @@ -254,7 +254,12 @@ # flag. By default all errors are on, so only add here categories that should be # off by default (i.e., categories that must be enabled by the --filter= flags). # All entries here should start with a '-' or '+', as in the --filter= flag. -_DEFAULT_FILTERS = ['-build/include_alpha'] +_DEFAULT_FILTERS = [ + '-build/include', + '-build/include_alpha', + '-build/include_order', + '-legal/copyright', + ] # The default list of categories suppressed for C (not C++) files. _DEFAULT_C_SUPPRESSED_CATEGORIES = [ From 01b7b9a2bcdbe4854254e06560e7aadbeb22b7f0 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 073/131] tools: add back --mode=tap to cpplint This commit reimplements commit 7b45163 ("tools: add tap output to cpplint") on top of the upgraded copy of cpplint. PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- tools/cpplint.py | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/tools/cpplint.py b/tools/cpplint.py index 0b79864f1a0882..88bb8ef6a66b99 100644 --- a/tools/cpplint.py +++ b/tools/cpplint.py @@ -44,6 +44,7 @@ import codecs import copy import getopt +import logging import math # for log import os import re @@ -53,10 +54,13 @@ import unicodedata +logger = logging.getLogger('testrunner') + + _USAGE = """ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] [--counting=total|toplevel|detailed] [--root=subdir] - [--linelength=digits] + [--linelength=digits] [--logfile=filename] <file> [file] ... The style guidelines this tries to follow are those in @@ -134,6 +138,9 @@ Examples: --extensions=hpp,cpp + logfile=filename + Write TAP output to a logfile. + cpplint.py supports per-directory configurations specified in CPPLINT.cfg files. CPPLINT.cfg file can contain a number of key=value pairs. Currently the following options are supported: @@ -1190,6 +1197,15 @@ def Error(filename, linenum, category, confidence, message): elif _cpplint_state.output_format == 'eclipse': sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence)) + elif _cpplint_state.output_format == 'tap': + template = ('not ok %(filename)s\n' + ' ---\n' + ' message: %(message)s\n' + ' data:\n' + ' line: %(linenum)d\n' + ' ruleId: %(category)s\n' + ' ...') + logger.info(template % locals()) else: sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence)) @@ -5980,7 +5996,6 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]): Error(filename, linenum, 'whitespace/newline', 1, 'Unexpected \\r (^M) found; better to use only \\n') - sys.stderr.write('Done processing %s\n' % filename) _RestoreFilters() @@ -6021,6 +6036,7 @@ def ParseArguments(args): (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', 'counting=', 'filter=', + 'logfile=', 'root=', 'linelength=', 'extensions=']) @@ -6036,8 +6052,9 @@ def ParseArguments(args): if opt == '--help': PrintUsage(None) elif opt == '--output': - if val not in ('emacs', 'vs7', 'eclipse'): - PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') + if val not in ('emacs', 'vs7', 'eclipse', 'tap'): + PrintUsage( + 'The only allowed output formats are emacs, vs7, eclipse and tap.') output_format = val elif opt == '--verbose': verbosity = int(val) @@ -6064,6 +6081,8 @@ def ParseArguments(args): _valid_extensions = set(val.split(',')) except ValueError: PrintUsage('Extensions must be comma seperated list.') + elif opt == '--logfile': + logger.addHandler(logging.FileHandler(val, mode='wb')) if not filenames: PrintUsage('No files were specified.') @@ -6086,6 +6105,12 @@ def main(): codecs.getwriter('utf8'), 'replace') + logger.addHandler(logging.StreamHandler(sys.stdout)) + logger.setLevel(logging.INFO) + + if _cpplint_state.output_format == 'tap': + logger.info('TAP version 13') + _cpplint_state.ResetErrorCounts() for filename in filenames: ProcessFile(filename, _cpplint_state.verbose_level) From 9d0641e20d3d3d12f33b6af04c4b7a27c3c97eba Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 074/131] tools: allow cpplint to run outside git repo This reapplies commit a493dab ("cpplint: make it possible to run outside git repo") from September 2015, this time with a proper status line. PR-URL: https://github.com/nodejs/node/pull/7462 Refs: https://github.com/nodejs/node/issues/2693 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- tools/cpplint.py | 38 ++++---------------------------------- 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/tools/cpplint.py b/tools/cpplint.py index 88bb8ef6a66b99..b5a05f0af39ba7 100644 --- a/tools/cpplint.py +++ b/tools/cpplint.py @@ -1073,40 +1073,10 @@ def RepositoryName(self): locations won't see bogus errors. """ fullname = self.FullName() - - if os.path.exists(fullname): - project_dir = os.path.dirname(fullname) - - if os.path.exists(os.path.join(project_dir, ".svn")): - # If there's a .svn file in the current directory, we recursively look - # up the directory tree for the top of the SVN checkout - root_dir = project_dir - one_up_dir = os.path.dirname(root_dir) - while os.path.exists(os.path.join(one_up_dir, ".svn")): - root_dir = os.path.dirname(root_dir) - one_up_dir = os.path.dirname(one_up_dir) - - prefix = os.path.commonprefix([root_dir, project_dir]) - return fullname[len(prefix) + 1:] - - # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by - # searching up from the current path. - root_dir = current_dir = os.path.dirname(fullname) - while current_dir != os.path.dirname(current_dir): - if (os.path.exists(os.path.join(current_dir, ".git")) or - os.path.exists(os.path.join(current_dir, ".hg")) or - os.path.exists(os.path.join(current_dir, ".svn"))): - root_dir = current_dir - current_dir = os.path.dirname(current_dir) - - if (os.path.exists(os.path.join(root_dir, ".git")) or - os.path.exists(os.path.join(root_dir, ".hg")) or - os.path.exists(os.path.join(root_dir, ".svn"))): - prefix = os.path.commonprefix([root_dir, project_dir]) - return fullname[len(prefix) + 1:] - - # Don't know what to do; header guard warnings may be wrong... - return fullname + # XXX(bnoordhuis) Expects that cpplint.py lives in the tools/ directory. + toplevel = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) + prefix = os.path.commonprefix([fullname, toplevel]) + return fullname[len(prefix) + 1:] def Split(self): """Splits the file into the directory, basename, and extension. From 5dfa234baefb016886f5421bb2acdd31ccd45f6e Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 075/131] src: fix build/c++tr1 cpplint warnings PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- src/util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.h b/src/util.h index 9ad0f6c5ed0aae..47c1a4999b0a45 100644 --- a/src/util.h +++ b/src/util.h @@ -11,7 +11,7 @@ #include <stdlib.h> #ifdef __APPLE__ -#include <tr1/type_traits> +#include <tr1/type_traits> // NOLINT(build/c++tr1) #else #include <type_traits> // std::remove_reference #endif From 6280ccdaaaf3bc1dc09ad73946881612e240f7db Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 076/131] src: fix build/header_guard cpplint warnings PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- src/node_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node_version.h b/src/node_version.h index d40aadc9bca904..348f1f7ca00346 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -51,4 +51,4 @@ */ #define NODE_MODULE_VERSION 48 /* Node.js v6.0.0 */ -#endif /* SRC_NODE_VERSION_H_ */ +#endif // SRC_NODE_VERSION_H_ From 7fe758de85ab303de65c8bbb5a07e810324ed9ed Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 077/131] src: fix readability/braces cpplint warnings PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- src/cares_wrap.cc | 4 ++-- src/inspector_agent.cc | 10 +++++----- src/node_http_parser.cc | 4 ++-- src/node_watchdog.cc | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index 88ce802dfec30e..f78fe52871fd0d 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -371,11 +371,11 @@ class QueryWrap : public AsyncWrap { // Subclasses should implement the appropriate Parse method. virtual void Parse(unsigned char* buf, int len) { UNREACHABLE(); - }; + } virtual void Parse(struct hostent* host) { UNREACHABLE(); - }; + } }; diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index 2d28f96be0b5f2..7cd7f4e5c944f4 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -575,23 +575,23 @@ Agent::~Agent() { void Agent::Start(v8::Platform* platform, int port, bool wait) { impl->Start(platform, port, wait); -}; +} void Agent::Stop() { impl->Stop(); -}; +} bool Agent::IsStarted() { return impl->IsStarted(); -}; +} bool Agent::IsConnected() { return impl->IsConnected(); -}; +} void Agent::WaitForDisconnect() { impl->WaitForDisconnect(); -}; +} } // namespace inspector } // namespace node diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index fd5e7c8e215cd1..f757cd6797058d 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -107,9 +107,9 @@ struct StringPtr { void Update(const char* str, size_t size) { - if (str_ == nullptr) + if (str_ == nullptr) { str_ = str; - else if (on_heap_ || str_ + size_ != str) { + } else if (on_heap_ || str_ + size_ != str) { // Non-consecutive input, make a copy on the heap. // TODO(bnoordhuis) Use slab allocation, O(n) allocs is bad. char* s = new char[size_ + size]; diff --git a/src/node_watchdog.cc b/src/node_watchdog.cc index 8a067c27f3b6c2..9c776973a2d630 100644 --- a/src/node_watchdog.cc +++ b/src/node_watchdog.cc @@ -292,7 +292,7 @@ SigintWatchdogHelper::SigintWatchdogHelper() CHECK_EQ(0, uv_mutex_init(&mutex_)); CHECK_EQ(0, uv_mutex_init(&list_mutex_)); -}; +} SigintWatchdogHelper::~SigintWatchdogHelper() { From b7e006b489c6d0ffaa41e534128421ad71d5206a Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 078/131] src: fix readability/constructors cpplint warnings PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- src/node_file.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/node_file.cc b/src/node_file.cc index c1dd59b9b710f5..089ead82ea430c 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -327,10 +327,13 @@ static void After(uv_fs_t *req) { // This struct is only used on sync fs calls. // For async calls FSReqWrap is used. -struct fs_req_wrap { +class fs_req_wrap { + public: fs_req_wrap() {} ~fs_req_wrap() { uv_fs_req_cleanup(&req); } uv_fs_t req; + + private: DISALLOW_COPY_AND_ASSIGN(fs_req_wrap); }; From 5fd158568f93c3c9aab517774a98ec8bb5ca22a7 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 079/131] src: fix readability/inheritance cpplint warnings PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- src/async-wrap.cc | 10 +++++----- src/cares_wrap.cc | 2 +- src/fs_event_wrap.cc | 2 +- src/handle_wrap.h | 2 +- src/inspector_agent.cc | 9 +++------ src/node_stat_watcher.h | 2 +- 6 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/async-wrap.cc b/src/async-wrap.cc index 405b9d1701712c..60124e47ad8833 100644 --- a/src/async-wrap.cc +++ b/src/async-wrap.cc @@ -38,11 +38,11 @@ class RetainedAsyncInfo: public RetainedObjectInfo { public: explicit RetainedAsyncInfo(uint16_t class_id, AsyncWrap* wrap); - virtual void Dispose() override; - virtual bool IsEquivalent(RetainedObjectInfo* other) override; - virtual intptr_t GetHash() override; - virtual const char* GetLabel() override; - virtual intptr_t GetSizeInBytes() override; + void Dispose() override; + bool IsEquivalent(RetainedObjectInfo* other) override; + intptr_t GetHash() override; + const char* GetLabel() override; + intptr_t GetSizeInBytes() override; private: const char* label_; diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index f78fe52871fd0d..bdbbd1a46099cc 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -257,7 +257,7 @@ class QueryWrap : public AsyncWrap { req_wrap_obj->Set(env->domain_string(), env->domain_array()->Get(0)); } - virtual ~QueryWrap() override { + ~QueryWrap() override { CHECK_EQ(false, persistent().IsEmpty()); ClearWrap(object()); persistent().Reset(); diff --git a/src/fs_event_wrap.cc b/src/fs_event_wrap.cc index 3f0df1140c0740..10af967def48fd 100644 --- a/src/fs_event_wrap.cc +++ b/src/fs_event_wrap.cc @@ -35,7 +35,7 @@ class FSEventWrap: public HandleWrap { private: FSEventWrap(Environment* env, Local<Object> object); - virtual ~FSEventWrap() override; + ~FSEventWrap() override; static void OnEvent(uv_fs_event_t* handle, const char* filename, int events, int status); diff --git a/src/handle_wrap.h b/src/handle_wrap.h index eaf0faef200ac6..60f30fbd10575b 100644 --- a/src/handle_wrap.h +++ b/src/handle_wrap.h @@ -55,7 +55,7 @@ class HandleWrap : public AsyncWrap { uv_handle_t* handle, AsyncWrap::ProviderType provider, AsyncWrap* parent = nullptr); - virtual ~HandleWrap() override; + ~HandleWrap() override; private: friend class Environment; diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index 7cd7f4e5c944f4..8ef95305a6f3c0 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -240,18 +240,15 @@ class ChannelImpl final : public blink::protocol::FrontendChannel { explicit ChannelImpl(AgentImpl* agent): agent_(agent) {} virtual ~ChannelImpl() {} private: - virtual void sendProtocolResponse(int callId, - const String16& message) - override { + void sendProtocolResponse(int callId, const String16& message) override { sendMessageToFrontend(message); } - virtual void sendProtocolNotification( - const String16& message) override { + void sendProtocolNotification(const String16& message) override { sendMessageToFrontend(message); } - virtual void flushProtocolNotifications() override { } + void flushProtocolNotifications() override { } void sendMessageToFrontend(const String16& message) { agent_->Write(message.utf8()); diff --git a/src/node_stat_watcher.h b/src/node_stat_watcher.h index 6978f602e1b50b..99cae5880351f7 100644 --- a/src/node_stat_watcher.h +++ b/src/node_stat_watcher.h @@ -13,7 +13,7 @@ namespace node { class StatWatcher : public AsyncWrap { public: - virtual ~StatWatcher() override; + ~StatWatcher() override; static void Initialize(Environment* env, v8::Local<v8::Object> target); From 43e83576bdfa90560e24975751f44fe71889ea76 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 080/131] src: fix readability/namespace cpplint warnings PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- src/node_win32_etw_provider.cc | 3 ++- src/spawn_sync.h | 3 ++- src/string_search.cc | 5 +++-- src/string_search.h | 4 ++-- test/addons/make-callback-recurse/binding.cc | 2 +- test/addons/make-callback/binding.cc | 2 +- 6 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/node_win32_etw_provider.cc b/src/node_win32_etw_provider.cc index 6877f1977dae8b..3aa8578db029d3 100644 --- a/src/node_win32_etw_provider.cc +++ b/src/node_win32_etw_provider.cc @@ -187,4 +187,5 @@ void shutdown_etw() { advapi = nullptr; } } -} + +} // namespace node diff --git a/src/spawn_sync.h b/src/spawn_sync.h index 9f53f0a22bcab9..8ddba479f31e1b 100644 --- a/src/spawn_sync.h +++ b/src/spawn_sync.h @@ -222,7 +222,8 @@ class SyncProcessRunner { Environment* env_; }; -} + +} // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/string_search.cc b/src/string_search.cc index 530a0e54684db8..326fba7c4abf05 100644 --- a/src/string_search.cc +++ b/src/string_search.cc @@ -6,5 +6,6 @@ namespace stringsearch { int StringSearchBase::kBadCharShiftTable[kUC16AlphabetSize]; int StringSearchBase::kGoodSuffixShiftTable[kBMMaxShift + 1]; int StringSearchBase::kSuffixTable[kBMMaxShift + 1]; -} -} // namespace node::stringsearch + +} // namespace stringsearch +} // namespace node diff --git a/src/string_search.h b/src/string_search.h index 7827fe153e69c0..b4cc9d6ac9fb16 100644 --- a/src/string_search.h +++ b/src/string_search.h @@ -623,8 +623,8 @@ size_t SearchString(Vector<const Char> subject, StringSearch<Char> search(pattern); return search.Search(subject, start_index); } -} -} // namespace node::stringsearch +} // namespace stringsearch +} // namespace node namespace node { using node::stringsearch::Vector; diff --git a/test/addons/make-callback-recurse/binding.cc b/test/addons/make-callback-recurse/binding.cc index 1c575910ef66f5..1195dbe2ff7e4c 100644 --- a/test/addons/make-callback-recurse/binding.cc +++ b/test/addons/make-callback-recurse/binding.cc @@ -26,6 +26,6 @@ void Initialize(Local<Object> target) { NODE_SET_METHOD(target, "makeCallback", MakeCallback); } -} // namespace anonymous +} // namespace NODE_MODULE(binding, Initialize) diff --git a/test/addons/make-callback/binding.cc b/test/addons/make-callback/binding.cc index 3012a39ff70a73..a1adb997bbf9b9 100644 --- a/test/addons/make-callback/binding.cc +++ b/test/addons/make-callback/binding.cc @@ -35,6 +35,6 @@ void Initialize(v8::Local<v8::Object> target) { NODE_SET_METHOD(target, "makeCallback", MakeCallback); } -} // namespace anonymous +} // namespace NODE_MODULE(binding, Initialize) From 1fa6dba8f28c3e819ad9db76d1e9d9377fb6e1b1 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 081/131] src: fix readability/nolint cpplint warnings PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- src/node_crypto.cc | 4 ++-- src/spawn_sync.cc | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 2659b0ac66fd39..b61568dbf8bb7b 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -175,7 +175,7 @@ template int SSLWrap<TLSWrap>::SelectALPNCallback( #endif // TLSEXT_TYPE_application_layer_protocol_negotiation static void crypto_threadid_cb(CRYPTO_THREADID* tid) { - static_assert(sizeof(uv_thread_t) <= sizeof(void*), // NOLINT(runtime/sizeof) + static_assert(sizeof(uv_thread_t) <= sizeof(void*), "uv_thread_t does not fit in a pointer"); CRYPTO_THREADID_set_pointer(tid, reinterpret_cast<void*>(uv_thread_self())); } @@ -693,7 +693,7 @@ void SecureContext::AddCACert(const FunctionCallbackInfo<Value>& args) { unsigned cert_count = 0; if (BIO* bio = LoadBIO(env, args[0])) { - while (X509* x509 = // NOLINT(whitespace/if-one-line) + while (X509* x509 = PEM_read_bio_X509(bio, nullptr, CryptoPemCallback, nullptr)) { X509_STORE_add_cert(sc->ca_store_, x509); SSL_CTX_add_client_CA(sc->ctx_, x509); diff --git a/src/spawn_sync.cc b/src/spawn_sync.cc index 62fadb4396ce19..2d6012761c8a97 100644 --- a/src/spawn_sync.cc +++ b/src/spawn_sync.cc @@ -992,7 +992,7 @@ int SyncProcessRunner::CopyJsStringArray(Local<Value> js_value, data_size = 0; for (uint32_t i = 0; i < length; i++) { data_size += StringBytes::StorageSize(isolate, js_array->Get(i), UTF8) + 1; - data_size = ROUND_UP(data_size, sizeof(void*)); // NOLINT(runtime/sizeof) + data_size = ROUND_UP(data_size, sizeof(void*)); } buffer = new char[list_size + data_size]; @@ -1008,8 +1008,7 @@ int SyncProcessRunner::CopyJsStringArray(Local<Value> js_value, js_array->Get(i), UTF8); buffer[data_offset++] = '\0'; - data_offset = ROUND_UP(data_offset, - sizeof(void*)); // NOLINT(runtime/sizeof) + data_offset = ROUND_UP(data_offset, sizeof(void*)); } list[length] = nullptr; From 88c5183147e54d78943815193ded064997f38a93 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 082/131] src: fix runtime/indentation_namespace warnings PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- src/tls_wrap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tls_wrap.h b/src/tls_wrap.h index 3e470f9c6d7848..d608ddfcb5b37c 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -20,7 +20,7 @@ namespace node { class NodeBIO; class WriteWrap; namespace crypto { - class SecureContext; +class SecureContext; } class TLSWrap : public AsyncWrap, From be0c575ab41137081498684ff2d00db6f977e800 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 083/131] src: fix runtime/int cpplint warnings PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- src/node_crypto.cc | 48 ++++++++++++++++++++---------------------- src/node_crypto_bio.cc | 5 +++-- src/node_crypto_bio.h | 3 ++- src/tls_wrap.cc | 2 +- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index b61568dbf8bb7b..def23744ef1292 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -212,7 +212,7 @@ static int CryptoPemCallback(char *buf, int size, int rwflag, void *u) { void ThrowCryptoError(Environment* env, - unsigned long err, + unsigned long err, // NOLINT(runtime/int) const char* default_message = nullptr) { HandleScope scope(env->isolate()); if (err != 0 || default_message == nullptr) { @@ -458,7 +458,7 @@ void SecureContext::SetKey(const FunctionCallbackInfo<Value>& args) { if (!key) { BIO_free_all(bio); - unsigned long err = ERR_get_error(); + unsigned long err = ERR_get_error(); // NOLINT(runtime/int) if (!err) { return env->ThrowError("PEM_read_bio_PrivateKey"); } @@ -470,7 +470,7 @@ void SecureContext::SetKey(const FunctionCallbackInfo<Value>& args) { BIO_free_all(bio); if (!rv) { - unsigned long err = ERR_get_error(); + unsigned long err = ERR_get_error(); // NOLINT(runtime/int) if (!err) return env->ThrowError("SSL_CTX_use_PrivateKey"); return ThrowCryptoError(env, err); @@ -588,7 +588,7 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx, X509* extra = nullptr; int ret = 0; - unsigned long err = 0; + unsigned long err = 0; // NOLINT(runtime/int) // Read extra certs STACK_OF(X509)* extra_certs = sk_X509_new_null(); @@ -664,7 +664,7 @@ void SecureContext::SetCert(const FunctionCallbackInfo<Value>& args) { BIO_free_all(bio); if (!rv) { - unsigned long err = ERR_get_error(); + unsigned long err = ERR_get_error(); // NOLINT(runtime/int) if (!err) { return env->ThrowError("SSL_CTX_use_certificate_chain"); } @@ -873,7 +873,9 @@ void SecureContext::SetOptions(const FunctionCallbackInfo<Value>& args) { return sc->env()->ThrowTypeError("Options must be an integer value"); } - SSL_CTX_set_options(sc->ctx_, static_cast<long>(args[0]->IntegerValue())); + SSL_CTX_set_options( + sc->ctx_, + static_cast<long>(args[0]->IntegerValue())); // NOLINT(runtime/int) } @@ -1016,7 +1018,7 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo<Value>& args) { delete[] pass; if (!ret) { - unsigned long err = ERR_get_error(); + unsigned long err = ERR_get_error(); // NOLINT(runtime/int) const char* str = ERR_reason_error_string(err); return env->ThrowError(str); } @@ -1458,7 +1460,7 @@ static Local<Object> X509ToObject(Environment* env, X509* cert) { String::kNormalString, mem->length)); (void) BIO_reset(bio); - unsigned long exponent_word = BN_get_word(rsa->e); + BN_ULONG exponent_word = BN_get_word(rsa->e); BIO_printf(bio, "0x%lx", exponent_word); BIO_get_mem_ptr(bio, &mem); @@ -1928,7 +1930,8 @@ void SSLWrap<Base>::VerifyError(const FunctionCallbackInfo<Value>& args) { // XXX(bnoordhuis) The UNABLE_TO_GET_ISSUER_CERT error when there is no // peer certificate is questionable but it's compatible with what was // here before. - long x509_verify_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; + long x509_verify_error = // NOLINT(runtime/int) + X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; if (X509* peer_cert = SSL_get_peer_certificate(w->ssl_)) { X509_free(peer_cert); x509_verify_error = SSL_get_verify_result(w->ssl_); @@ -2157,11 +2160,6 @@ void SSLWrap<Base>::SetNPNProtocols(const FunctionCallbackInfo<Value>& args) { #endif // OPENSSL_NPN_NEGOTIATED #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation -typedef struct tlsextalpnctx_st { - unsigned char* data; - unsigned short len; -} tlsextalpnctx; - template <class Base> int SSLWrap<Base>::SelectALPNCallback(SSL* s, const unsigned char** out, @@ -2401,7 +2399,7 @@ void SSLWrap<Base>::CertCbDone(const FunctionCallbackInfo<Value>& args) { if (rv) rv = w->SetCACerts(sc); if (!rv) { - unsigned long err = ERR_get_error(); + unsigned long err = ERR_get_error(); // NOLINT(runtime/int) if (!err) return env->ThrowError("CertCbDone"); return ThrowCryptoError(env, err); @@ -2839,7 +2837,7 @@ void Connection::New(const FunctionCallbackInfo<Value>& args) { SSL_set_bio(conn->ssl_, conn->bio_read_, conn->bio_write_); #ifdef SSL_MODE_RELEASE_BUFFERS - long mode = SSL_get_mode(conn->ssl_); + long mode = SSL_get_mode(conn->ssl_); // NOLINT(runtime/int) SSL_set_mode(conn->ssl_, mode | SSL_MODE_RELEASE_BUFFERS); #endif @@ -3838,7 +3836,7 @@ void SignBase::CheckThrow(SignBase::Error error) { case kSignPrivateKey: case kSignPublicKey: { - unsigned long err = ERR_get_error(); + unsigned long err = ERR_get_error(); // NOLINT(runtime/int) if (err) return ThrowCryptoError(env(), err); switch (error) { @@ -5415,11 +5413,11 @@ class RandomBytesRequest : public AsyncWrap { size_ = 0; } - inline unsigned long error() const { + inline unsigned long error() const { // NOLINT(runtime/int) return error_; } - inline void set_error(unsigned long err) { + inline void set_error(unsigned long err) { // NOLINT(runtime/int) error_ = err; } @@ -5428,7 +5426,7 @@ class RandomBytesRequest : public AsyncWrap { uv_work_t work_req_; private: - unsigned long error_; + unsigned long error_; // NOLINT(runtime/int) size_t size_; char* data_; }; @@ -5446,9 +5444,9 @@ void RandomBytesWork(uv_work_t* work_req) { // RAND_bytes() returns 0 on error. if (r == 0) { - req->set_error(ERR_get_error()); + req->set_error(ERR_get_error()); // NOLINT(runtime/int) } else if (r == -1) { - req->set_error(static_cast<unsigned long>(-1)); + req->set_error(static_cast<unsigned long>(-1)); // NOLINT(runtime/int) } } @@ -5458,7 +5456,7 @@ void RandomBytesCheck(RandomBytesRequest* req, Local<Value> argv[2]) { if (req->error()) { char errmsg[256] = "Operation not supported"; - if (req->error() != static_cast<unsigned long>(-1)) + if (req->error() != static_cast<unsigned long>(-1)) // NOLINT(runtime/int) ERR_error_string_n(req->error(), errmsg, sizeof errmsg); argv[0] = Exception::Error(OneByteString(req->env()->isolate(), errmsg)); @@ -5797,7 +5795,7 @@ void InitCryptoOnce() { #ifdef NODE_FIPS_MODE /* Override FIPS settings in cnf file, if needed. */ - unsigned long err = 0; + unsigned long err = 0; // NOLINT(runtime/int) if (enable_fips_crypto || force_fips_crypto) { if (0 == FIPS_mode() && !FIPS_mode_set(1)) { err = ERR_get_error(); @@ -5879,7 +5877,7 @@ void SetFipsCrypto(const FunctionCallbackInfo<Value>& args) { return env->ThrowError( "Cannot set FIPS mode, it was forced with --force-fips at startup."); } else if (!FIPS_mode_set(mode)) { - unsigned long err = ERR_get_error(); + unsigned long err = ERR_get_error(); // NOLINT(runtime/int) return ThrowCryptoError(env, err); } #else diff --git a/src/node_crypto_bio.cc b/src/node_crypto_bio.cc index d155eaf79a6f9c..ec4aa69b073cec 100644 --- a/src/node_crypto_bio.cc +++ b/src/node_crypto_bio.cc @@ -164,9 +164,10 @@ int NodeBIO::Gets(BIO* bio, char* out, int size) { } -long NodeBIO::Ctrl(BIO* bio, int cmd, long num, void* ptr) { +long NodeBIO::Ctrl(BIO* bio, int cmd, long num, // NOLINT(runtime/int) + void* ptr) { NodeBIO* nbio; - long ret; + long ret; // NOLINT(runtime/int) nbio = FromBIO(bio); ret = 1; diff --git a/src/node_crypto_bio.h b/src/node_crypto_bio.h index 7fe0f9b796a023..ed6b46b53237cf 100644 --- a/src/node_crypto_bio.h +++ b/src/node_crypto_bio.h @@ -91,7 +91,8 @@ class NodeBIO { static int Write(BIO* bio, const char* data, int len); static int Puts(BIO* bio, const char* str); static int Gets(BIO* bio, char* out, int size); - static long Ctrl(BIO* bio, int cmd, long num, void* ptr); + static long Ctrl(BIO* bio, int cmd, long num, // NOLINT(runtime/int) + void* ptr); // Enough to handle the most of the client hellos static const size_t kInitialBufferLength = 1024; diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 20bbce50dee5be..ceb3536cd25ce1 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -128,7 +128,7 @@ void TLSWrap::InitSSL() { SSL_set_verify(ssl_, SSL_VERIFY_NONE, crypto::VerifyCallback); #ifdef SSL_MODE_RELEASE_BUFFERS - long mode = SSL_get_mode(ssl_); + long mode = SSL_get_mode(ssl_); // NOLINT(runtime/int) SSL_set_mode(ssl_, mode | SSL_MODE_RELEASE_BUFFERS); #endif // SSL_MODE_RELEASE_BUFFERS From 1b3c1b08a8200700b51dcf72b9d0d50526049a31 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 084/131] src: fix runtime/references cpplint warnings PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- src/node_contextify.cc | 17 +++++++------- tools/icu/iculslocs.cc | 53 +++++++++++++++++++++--------------------- 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/node_contextify.cc b/src/node_contextify.cc index e5cbfd5512cc01..07fe998bd4d375 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -561,7 +561,8 @@ class ContextifyScript : public BaseObject { // Do the eval within this context Environment* env = Environment::GetCurrent(args); - EvalMachine(env, timeout, display_errors, break_on_sigint, args, try_catch); + EvalMachine(env, timeout, display_errors, break_on_sigint, args, + &try_catch); } // args: sandbox, [options] @@ -610,7 +611,7 @@ class ContextifyScript : public BaseObject { display_errors, break_on_sigint, args, - try_catch)) { + &try_catch)) { contextify_context->CopyProperties(); } @@ -821,7 +822,7 @@ class ContextifyScript : public BaseObject { const bool display_errors, const bool break_on_sigint, const FunctionCallbackInfo<Value>& args, - TryCatch& try_catch) { + TryCatch* try_catch) { if (!ContextifyScript::InstanceOf(env, args.Holder())) { env->ThrowTypeError( "Script methods can only be called on script instances."); @@ -855,8 +856,8 @@ class ContextifyScript : public BaseObject { result = script->Run(); } - if (try_catch.HasCaught()) { - if (try_catch.HasTerminated()) + if (try_catch->HasCaught()) { + if (try_catch->HasTerminated()) env->isolate()->CancelTerminateExecution(); // It is possible that execution was terminated by another timeout in @@ -873,7 +874,7 @@ class ContextifyScript : public BaseObject { // letting try_catch catch it. // If execution has been terminated, but not by one of the watchdogs from // this invocation, this will re-throw a `null` value. - try_catch.ReThrow(); + try_catch->ReThrow(); return false; } @@ -881,9 +882,9 @@ class ContextifyScript : public BaseObject { if (result.IsEmpty()) { // Error occurred during execution of the script. if (display_errors) { - DecorateErrorStack(env, try_catch); + DecorateErrorStack(env, *try_catch); } - try_catch.ReThrow(); + try_catch->ReThrow(); return false; } diff --git a/tools/icu/iculslocs.cc b/tools/icu/iculslocs.cc index 66becace0a4a27..d71f9b8e5d702c 100644 --- a/tools/icu/iculslocs.cc +++ b/tools/icu/iculslocs.cc @@ -105,13 +105,13 @@ void usage() { PROG); } -#define ASSERT_SUCCESS(what) \ - if (U_FAILURE(status)) { \ +#define ASSERT_SUCCESS(status, what) \ + if (U_FAILURE(*status)) { \ u_printf("%s:%d: %s: ERROR: %s %s\n", \ __FILE__, \ __LINE__, \ PROG, \ - u_errorName(status), \ + u_errorName(*status), \ what); \ return 1; \ } @@ -177,9 +177,9 @@ int localeExists(const char* loc, UBool* exists) { } } -void printIndent(const LocalUFILEPointer& bf, int indent) { +void printIndent(const LocalUFILEPointer* bf, int indent) { for (int i = 0; i < indent + 1; i++) { - u_fprintf(bf.getAlias(), " "); + u_fprintf(bf->getAlias(), " "); } } @@ -189,15 +189,15 @@ void printIndent(const LocalUFILEPointer& bf, int indent) { * @return 0 for OK, 1 for err */ int dumpAllButInstalledLocales(int lev, - LocalUResourceBundlePointer& bund, - LocalUFILEPointer& bf, - UErrorCode& status) { - ures_resetIterator(bund.getAlias()); - const UBool isTable = (UBool)(ures_getType(bund.getAlias()) == URES_TABLE); + LocalUResourceBundlePointer* bund, + LocalUFILEPointer* bf, + UErrorCode* status) { + ures_resetIterator(bund->getAlias()); + const UBool isTable = (UBool)(ures_getType(bund->getAlias()) == URES_TABLE); LocalUResourceBundlePointer t; - while (U_SUCCESS(status) && ures_hasNext(bund.getAlias())) { - t.adoptInstead(ures_getNextResource(bund.getAlias(), t.orphan(), &status)); - ASSERT_SUCCESS("while processing table"); + while (U_SUCCESS(*status) && ures_hasNext(bund->getAlias())) { + t.adoptInstead(ures_getNextResource(bund->getAlias(), t.orphan(), status)); + ASSERT_SUCCESS(status, "while processing table"); const char* key = ures_getKey(t.getAlias()); if (VERBOSE > 1) { u_printf("dump@%d: got key %s\n", lev, key); @@ -208,22 +208,22 @@ int dumpAllButInstalledLocales(int lev, } } else { printIndent(bf, lev); - u_fprintf(bf.getAlias(), "%s", key); + u_fprintf(bf->getAlias(), "%s", key); switch (ures_getType(t.getAlias())) { case URES_STRING: { int32_t len = 0; - const UChar* s = ures_getString(t.getAlias(), &len, &status); - ASSERT_SUCCESS("getting string"); - u_fprintf(bf.getAlias(), ":string {\""); - u_file_write(s, len, bf.getAlias()); - u_fprintf(bf.getAlias(), "\"}"); + const UChar* s = ures_getString(t.getAlias(), &len, status); + ASSERT_SUCCESS(status, "getting string"); + u_fprintf(bf->getAlias(), ":string {\""); + u_file_write(s, len, bf->getAlias()); + u_fprintf(bf->getAlias(), "\"}"); } break; default: { u_printf("ERROR: unhandled type in dumpAllButInstalledLocales().\n"); return 1; } break; } - u_fprintf(bf.getAlias(), "\n"); + u_fprintf(bf->getAlias(), "\n"); } } return 0; @@ -250,7 +250,7 @@ int list(const char* toBundle) { // first, calculate the bundle name. calculatePackageName(&status); - ASSERT_SUCCESS("calculating package name"); + ASSERT_SUCCESS(&status, "calculating package name"); if (VERBOSE) { u_printf("\"locale\": %s\n", locale); @@ -258,10 +258,10 @@ int list(const char* toBundle) { LocalUResourceBundlePointer bund( ures_openDirect(packageName.data(), locale, &status)); - ASSERT_SUCCESS("while opening the bundle"); + ASSERT_SUCCESS(&status, "while opening the bundle"); LocalUResourceBundlePointer installedLocales( ures_getByKey(bund.getAlias(), INSTALLEDLOCALES, NULL, &status)); - ASSERT_SUCCESS("while fetching installed locales"); + ASSERT_SUCCESS(&status, "while fetching installed locales"); int32_t count = ures_getSize(installedLocales.getAlias()); if (VERBOSE) { @@ -280,11 +280,12 @@ int list(const char* toBundle) { "%s:table(nofallback) {\n" " // First, everything besides InstalledLocales:\n", locale); - if (dumpAllButInstalledLocales(0, bund, bf, status)) { + if (dumpAllButInstalledLocales(0, &bund, &bf, &status)) { u_printf("Error dumping prolog for %s\n", toBundle); return 1; } - ASSERT_SUCCESS("while writing prolog"); // in case an error was missed + // in case an error was missed + ASSERT_SUCCESS(&status, "while writing prolog"); u_fprintf(bf.getAlias(), " %s:table { // %d locales in input %s.res\n", @@ -300,7 +301,7 @@ int list(const char* toBundle) { for (int32_t i = 0; i < count; i++) { subkey.adoptInstead(ures_getByIndex( installedLocales.getAlias(), i, subkey.orphan(), &status)); - ASSERT_SUCCESS("while fetching an installed locale's name"); + ASSERT_SUCCESS(&status, "while fetching an installed locale's name"); const char* key = ures_getKey(subkey.getAlias()); if (VERBOSE > 1) { From f72259aa89783e26a1f6be7d3293de76adf39858 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 085/131] src: fix whitespace/blank_line cpplint warnings PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- src/node_crypto.h | 1 - src/node_watchdog.h | 1 + src/node_zlib.cc | 1 - src/util.h | 1 + 4 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node_crypto.h b/src/node_crypto.h index 360024be88a883..e0d01887ac9db2 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -577,7 +577,6 @@ class SignBase : public BaseObject { class Sign : public SignBase { public: - static void Initialize(Environment* env, v8::Local<v8::Object> target); Error SignInit(const char* sign_type); diff --git a/src/node_watchdog.h b/src/node_watchdog.h index 77c2d535340b6f..56d5af322b0523 100644 --- a/src/node_watchdog.h +++ b/src/node_watchdog.h @@ -64,6 +64,7 @@ class SigintWatchdogHelper { int Start(); bool Stop(); + private: SigintWatchdogHelper(); ~SigintWatchdogHelper(); diff --git a/src/node_zlib.cc b/src/node_zlib.cc index 4de9d7cdc27aa0..785c1a9b33fda7 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -52,7 +52,6 @@ void InitZlib(v8::Local<v8::Object> target); */ class ZCtx : public AsyncWrap { public: - ZCtx(Environment* env, Local<Object> wrap, node_zlib_mode mode) : AsyncWrap(env, wrap, AsyncWrap::PROVIDER_ZLIB), chunk_size_(0), diff --git a/src/util.h b/src/util.h index 47c1a4999b0a45..dd2da89e4e9d7b 100644 --- a/src/util.h +++ b/src/util.h @@ -286,6 +286,7 @@ class MaybeStackBuffer { if (buf_ != buf_st_) free(buf_); } + private: size_t length_; T* buf_; From 3fa643b069c9032b63c5d753b525d6bb75e07484 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 086/131] src: fix whitespace/indent cpplint warnings PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- src/node_internals.h | 152 ++++++++++++++++++++-------------------- src/node_watchdog.h | 71 +++++++++---------- src/string_bytes.cc | 112 +++++++++++++++--------------- src/util.h | 162 +++++++++++++++++++++---------------------- 4 files changed, 249 insertions(+), 248 deletions(-) diff --git a/src/node_internals.h b/src/node_internals.h index 7ae587e1bc090c..f9370c0a29d14e 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -231,82 +231,82 @@ void ClearFatalExceptionHandlers(Environment* env); enum NodeInstanceType { MAIN, WORKER, REMOTE_DEBUG_SERVER }; class NodeInstanceData { - public: - NodeInstanceData(NodeInstanceType node_instance_type, - uv_loop_t* event_loop, - int argc, - const char** argv, - int exec_argc, - const char** exec_argv, - bool use_debug_agent_flag) - : node_instance_type_(node_instance_type), - exit_code_(1), - event_loop_(event_loop), - argc_(argc), - argv_(argv), - exec_argc_(exec_argc), - exec_argv_(exec_argv), - use_debug_agent_flag_(use_debug_agent_flag) { - CHECK_NE(event_loop_, nullptr); - } - - uv_loop_t* event_loop() const { - return event_loop_; - } - - int exit_code() { - CHECK(is_main()); - return exit_code_; - } - - void set_exit_code(int exit_code) { - CHECK(is_main()); - exit_code_ = exit_code; - } - - bool is_main() { - return node_instance_type_ == MAIN; - } - - bool is_worker() { - return node_instance_type_ == WORKER; - } - - bool is_remote_debug_server() { - return node_instance_type_ == REMOTE_DEBUG_SERVER; - } - - int argc() { - return argc_; - } - - const char** argv() { - return argv_; - } - - int exec_argc() { - return exec_argc_; - } - - const char** exec_argv() { - return exec_argv_; - } - - bool use_debug_agent() { - return is_main() && use_debug_agent_flag_; - } - - private: - const NodeInstanceType node_instance_type_; - int exit_code_; - uv_loop_t* const event_loop_; - const int argc_; - const char** argv_; - const int exec_argc_; - const char** exec_argv_; - const bool use_debug_agent_flag_; - - DISALLOW_COPY_AND_ASSIGN(NodeInstanceData); + public: + NodeInstanceData(NodeInstanceType node_instance_type, + uv_loop_t* event_loop, + int argc, + const char** argv, + int exec_argc, + const char** exec_argv, + bool use_debug_agent_flag) + : node_instance_type_(node_instance_type), + exit_code_(1), + event_loop_(event_loop), + argc_(argc), + argv_(argv), + exec_argc_(exec_argc), + exec_argv_(exec_argv), + use_debug_agent_flag_(use_debug_agent_flag) { + CHECK_NE(event_loop_, nullptr); + } + + uv_loop_t* event_loop() const { + return event_loop_; + } + + int exit_code() { + CHECK(is_main()); + return exit_code_; + } + + void set_exit_code(int exit_code) { + CHECK(is_main()); + exit_code_ = exit_code; + } + + bool is_main() { + return node_instance_type_ == MAIN; + } + + bool is_worker() { + return node_instance_type_ == WORKER; + } + + bool is_remote_debug_server() { + return node_instance_type_ == REMOTE_DEBUG_SERVER; + } + + int argc() { + return argc_; + } + + const char** argv() { + return argv_; + } + + int exec_argc() { + return exec_argc_; + } + + const char** exec_argv() { + return exec_argv_; + } + + bool use_debug_agent() { + return is_main() && use_debug_agent_flag_; + } + + private: + const NodeInstanceType node_instance_type_; + int exit_code_; + uv_loop_t* const event_loop_; + const int argc_; + const char** argv_; + const int exec_argc_; + const char** exec_argv_; + const bool use_debug_agent_flag_; + + DISALLOW_COPY_AND_ASSIGN(NodeInstanceData); }; namespace Buffer { diff --git a/src/node_watchdog.h b/src/node_watchdog.h index 56d5af322b0523..d56b7624de9c12 100644 --- a/src/node_watchdog.h +++ b/src/node_watchdog.h @@ -39,56 +39,57 @@ class Watchdog { }; class SigintWatchdog { - public: - explicit SigintWatchdog(v8::Isolate* isolate); - ~SigintWatchdog(); + public: + explicit SigintWatchdog(v8::Isolate* isolate); + ~SigintWatchdog(); - void Dispose(); + void Dispose(); - v8::Isolate* isolate() { return isolate_; } - bool HasReceivedSignal() { return received_signal_; } - void HandleSigint(); - private: - void Destroy(); + v8::Isolate* isolate() { return isolate_; } + bool HasReceivedSignal() { return received_signal_; } + void HandleSigint(); + + private: + void Destroy(); - v8::Isolate* isolate_; - bool received_signal_; - bool destroyed_; + v8::Isolate* isolate_; + bool received_signal_; + bool destroyed_; }; class SigintWatchdogHelper { - public: - static SigintWatchdogHelper* GetInstance() { return &instance; } - void Register(SigintWatchdog* watchdog); - void Unregister(SigintWatchdog* watchdog); + public: + static SigintWatchdogHelper* GetInstance() { return &instance; } + void Register(SigintWatchdog* watchdog); + void Unregister(SigintWatchdog* watchdog); - int Start(); - bool Stop(); + int Start(); + bool Stop(); - private: - SigintWatchdogHelper(); - ~SigintWatchdogHelper(); + private: + SigintWatchdogHelper(); + ~SigintWatchdogHelper(); - static bool InformWatchdogsAboutSignal(); - static SigintWatchdogHelper instance; + static bool InformWatchdogsAboutSignal(); + static SigintWatchdogHelper instance; - int start_stop_count_; + int start_stop_count_; - uv_mutex_t mutex_; - uv_mutex_t list_mutex_; - std::vector<SigintWatchdog*> watchdogs_; - bool has_pending_signal_; + uv_mutex_t mutex_; + uv_mutex_t list_mutex_; + std::vector<SigintWatchdog*> watchdogs_; + bool has_pending_signal_; #ifdef __POSIX__ - pthread_t thread_; - uv_sem_t sem_; - bool has_running_thread_; - bool stopping_; + pthread_t thread_; + uv_sem_t sem_; + bool has_running_thread_; + bool stopping_; - static void* RunSigintWatchdog(void* arg); - static void HandleSignal(int signum); + static void* RunSigintWatchdog(void* arg); + static void HandleSignal(int signum); #else - static BOOL WINAPI WinCtrlCHandlerRoutine(DWORD dwCtrlType); + static BOOL WINAPI WinCtrlCHandlerRoutine(DWORD dwCtrlType); #endif }; diff --git a/src/string_bytes.cc b/src/string_bytes.cc index 8a327deac2f439..882340ae048bf3 100644 --- a/src/string_bytes.cc +++ b/src/string_bytes.cc @@ -27,78 +27,78 @@ using v8::Value; template <typename ResourceType, typename TypeName> class ExternString: public ResourceType { - public: - ~ExternString() override { - free(const_cast<TypeName*>(data_)); - isolate()->AdjustAmountOfExternalAllocatedMemory(-byte_length()); - } - - const TypeName* data() const override { - return data_; - } + public: + ~ExternString() override { + free(const_cast<TypeName*>(data_)); + isolate()->AdjustAmountOfExternalAllocatedMemory(-byte_length()); + } - size_t length() const override { - return length_; - } + const TypeName* data() const override { + return data_; + } - int64_t byte_length() const { - return length() * sizeof(*data()); - } + size_t length() const override { + return length_; + } - static Local<String> NewFromCopy(Isolate* isolate, - const TypeName* data, - size_t length) { - EscapableHandleScope scope(isolate); + int64_t byte_length() const { + return length() * sizeof(*data()); + } - if (length == 0) - return scope.Escape(String::Empty(isolate)); + static Local<String> NewFromCopy(Isolate* isolate, + const TypeName* data, + size_t length) { + EscapableHandleScope scope(isolate); - TypeName* new_data = - static_cast<TypeName*>(malloc(length * sizeof(*new_data))); - if (new_data == nullptr) { - return Local<String>(); - } - memcpy(new_data, data, length * sizeof(*new_data)); + if (length == 0) + return scope.Escape(String::Empty(isolate)); - return scope.Escape(ExternString<ResourceType, TypeName>::New(isolate, - new_data, - length)); + TypeName* new_data = + static_cast<TypeName*>(malloc(length * sizeof(*new_data))); + if (new_data == nullptr) { + return Local<String>(); } + memcpy(new_data, data, length * sizeof(*new_data)); - // uses "data" for external resource, and will be free'd on gc - static Local<String> New(Isolate* isolate, - const TypeName* data, - size_t length) { - EscapableHandleScope scope(isolate); + return scope.Escape(ExternString<ResourceType, TypeName>::New(isolate, + new_data, + length)); + } - if (length == 0) - return scope.Escape(String::Empty(isolate)); + // uses "data" for external resource, and will be free'd on gc + static Local<String> New(Isolate* isolate, + const TypeName* data, + size_t length) { + EscapableHandleScope scope(isolate); - ExternString* h_str = new ExternString<ResourceType, TypeName>(isolate, - data, - length); - MaybeLocal<String> str = NewExternal(isolate, h_str); - isolate->AdjustAmountOfExternalAllocatedMemory(h_str->byte_length()); + if (length == 0) + return scope.Escape(String::Empty(isolate)); - if (str.IsEmpty()) { - delete h_str; - return Local<String>(); - } + ExternString* h_str = new ExternString<ResourceType, TypeName>(isolate, + data, + length); + MaybeLocal<String> str = NewExternal(isolate, h_str); + isolate->AdjustAmountOfExternalAllocatedMemory(h_str->byte_length()); - return scope.Escape(str.ToLocalChecked()); + if (str.IsEmpty()) { + delete h_str; + return Local<String>(); } - inline Isolate* isolate() const { return isolate_; } + return scope.Escape(str.ToLocalChecked()); + } + + inline Isolate* isolate() const { return isolate_; } - private: - ExternString(Isolate* isolate, const TypeName* data, size_t length) - : isolate_(isolate), data_(data), length_(length) { } - static MaybeLocal<String> NewExternal(Isolate* isolate, - ExternString* h_str); + private: + ExternString(Isolate* isolate, const TypeName* data, size_t length) + : isolate_(isolate), data_(data), length_(length) { } + static MaybeLocal<String> NewExternal(Isolate* isolate, + ExternString* h_str); - Isolate* isolate_; - const TypeName* data_; - size_t length_; + Isolate* isolate_; + const TypeName* data_; + size_t length_; }; diff --git a/src/util.h b/src/util.h index dd2da89e4e9d7b..0a6b5e0dbef9c5 100644 --- a/src/util.h +++ b/src/util.h @@ -213,99 +213,99 @@ inline bool StringEqualNoCaseN(const char* a, const char* b, size_t length); // the stack is used, otherwise malloc(). template <typename T, size_t kStackStorageSize = 1024> class MaybeStackBuffer { - public: - const T* out() const { - return buf_; - } - - T* out() { - return buf_; - } - - // operator* for compatibility with `v8::String::(Utf8)Value` - T* operator*() { - return buf_; - } - - const T* operator*() const { - return buf_; - } - - size_t length() const { - return length_; - } - - // Call to make sure enough space for `storage` entries is available. - // There can only be 1 call to AllocateSufficientStorage or Invalidate - // per instance. - void AllocateSufficientStorage(size_t storage) { - if (storage <= kStackStorageSize) { - buf_ = buf_st_; - } else { - // Guard against overflow. - CHECK_LE(storage, sizeof(T) * storage); - - buf_ = static_cast<T*>(malloc(sizeof(T) * storage)); - CHECK_NE(buf_, nullptr); - } - - // Remember how much was allocated to check against that in SetLength(). - length_ = storage; - } - - void SetLength(size_t length) { - // length_ stores how much memory was allocated. - CHECK_LE(length, length_); - length_ = length; - } - - void SetLengthAndZeroTerminate(size_t length) { - // length_ stores how much memory was allocated. - CHECK_LE(length + 1, length_); - SetLength(length); - - // T() is 0 for integer types, nullptr for pointers, etc. - buf_[length] = T(); - } - - // Make derefencing this object return nullptr. - // Calling this is mutually exclusive with calling - // AllocateSufficientStorage. - void Invalidate() { - CHECK_EQ(buf_, buf_st_); - length_ = 0; - buf_ = nullptr; - } - - MaybeStackBuffer() : length_(0), buf_(buf_st_) { - // Default to a zero-length, null-terminated buffer. - buf_[0] = T(); + public: + const T* out() const { + return buf_; + } + + T* out() { + return buf_; + } + + // operator* for compatibility with `v8::String::(Utf8)Value` + T* operator*() { + return buf_; + } + + const T* operator*() const { + return buf_; + } + + size_t length() const { + return length_; + } + + // Call to make sure enough space for `storage` entries is available. + // There can only be 1 call to AllocateSufficientStorage or Invalidate + // per instance. + void AllocateSufficientStorage(size_t storage) { + if (storage <= kStackStorageSize) { + buf_ = buf_st_; + } else { + // Guard against overflow. + CHECK_LE(storage, sizeof(T) * storage); + + buf_ = static_cast<T*>(malloc(sizeof(T) * storage)); + CHECK_NE(buf_, nullptr); } - ~MaybeStackBuffer() { - if (buf_ != buf_st_) - free(buf_); - } + // Remember how much was allocated to check against that in SetLength(). + length_ = storage; + } + + void SetLength(size_t length) { + // length_ stores how much memory was allocated. + CHECK_LE(length, length_); + length_ = length; + } + + void SetLengthAndZeroTerminate(size_t length) { + // length_ stores how much memory was allocated. + CHECK_LE(length + 1, length_); + SetLength(length); + + // T() is 0 for integer types, nullptr for pointers, etc. + buf_[length] = T(); + } + + // Make derefencing this object return nullptr. + // Calling this is mutually exclusive with calling + // AllocateSufficientStorage. + void Invalidate() { + CHECK_EQ(buf_, buf_st_); + length_ = 0; + buf_ = nullptr; + } + + MaybeStackBuffer() : length_(0), buf_(buf_st_) { + // Default to a zero-length, null-terminated buffer. + buf_[0] = T(); + } + + ~MaybeStackBuffer() { + if (buf_ != buf_st_) + free(buf_); + } - private: - size_t length_; - T* buf_; - T buf_st_[kStackStorageSize]; + private: + size_t length_; + T* buf_; + T buf_st_[kStackStorageSize]; }; class Utf8Value : public MaybeStackBuffer<char> { - public: - explicit Utf8Value(v8::Isolate* isolate, v8::Local<v8::Value> value); + public: + explicit Utf8Value(v8::Isolate* isolate, v8::Local<v8::Value> value); }; class TwoByteValue : public MaybeStackBuffer<uint16_t> { - public: - explicit TwoByteValue(v8::Isolate* isolate, v8::Local<v8::Value> value); + public: + explicit TwoByteValue(v8::Isolate* isolate, v8::Local<v8::Value> value); }; class BufferValue : public MaybeStackBuffer<char> { - public: - explicit BufferValue(v8::Isolate* isolate, v8::Local<v8::Value> value); + public: + explicit BufferValue(v8::Isolate* isolate, v8::Local<v8::Value> value); }; } // namespace node From 8a31b234ee6782867228707c8419940a32809b91 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 087/131] build: remove unused files from CPPLINT_FILES PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- Makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Makefile b/Makefile index c44ed7934551d1..07e2decb5ee548 100644 --- a/Makefile +++ b/Makefile @@ -681,8 +681,6 @@ CPPLINT_EXCLUDE += src/v8abbr.h CPPLINT_EXCLUDE += $(wildcard test/addons/??_*/*.cc test/addons/??_*/*.h) CPPLINT_FILES = $(filter-out $(CPPLINT_EXCLUDE), $(wildcard \ - deps/debugger-agent/include/* \ - deps/debugger-agent/src/* \ src/*.c \ src/*.cc \ src/*.h \ From da0ebf62c7ab82c71649af8c7eb224aa3a4cccd7 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 088/131] src: lint node_win32_perfctr_provider.cc PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- Makefile | 1 - src/node_win32_perfctr_provider.cc | 30 ++++++++++++++++++++---------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 07e2decb5ee548..2c45191c1284a0 100644 --- a/Makefile +++ b/Makefile @@ -674,7 +674,6 @@ CPPLINT_EXCLUDE ?= CPPLINT_EXCLUDE += src/node_lttng.cc CPPLINT_EXCLUDE += src/node_root_certs.h CPPLINT_EXCLUDE += src/node_lttng_tp.h -CPPLINT_EXCLUDE += src/node_win32_perfctr_provider.cc CPPLINT_EXCLUDE += src/queue.h CPPLINT_EXCLUDE += src/tree.h CPPLINT_EXCLUDE += src/v8abbr.h diff --git a/src/node_win32_perfctr_provider.cc b/src/node_win32_perfctr_provider.cc index 00491caf50675f..ebb299e4c54758 100644 --- a/src/node_win32_perfctr_provider.cc +++ b/src/node_win32_perfctr_provider.cc @@ -193,7 +193,8 @@ void TermPerfCountersWin32() { void NODE_COUNT_HTTP_SERVER_REQUEST() { - if (NodeCounterProvider != nullptr && perfctr_incrementULongValue != nullptr) { + if (NodeCounterProvider != nullptr && + perfctr_incrementULongValue != nullptr) { perfctr_incrementULongValue(NodeCounterProvider, perfctr_instance, NODE_COUNTER_HTTP_SERVER_REQUEST, @@ -203,7 +204,8 @@ void NODE_COUNT_HTTP_SERVER_REQUEST() { void NODE_COUNT_HTTP_SERVER_RESPONSE() { - if (NodeCounterProvider != nullptr && perfctr_incrementULongValue != nullptr) { + if (NodeCounterProvider != nullptr && + perfctr_incrementULongValue != nullptr) { perfctr_incrementULongValue(NodeCounterProvider, perfctr_instance, NODE_COUNTER_HTTP_SERVER_RESPONSE, @@ -213,7 +215,8 @@ void NODE_COUNT_HTTP_SERVER_RESPONSE() { void NODE_COUNT_HTTP_CLIENT_REQUEST() { - if (NodeCounterProvider != nullptr && perfctr_incrementULongValue != nullptr) { + if (NodeCounterProvider != nullptr && + perfctr_incrementULongValue != nullptr) { perfctr_incrementULongValue(NodeCounterProvider, perfctr_instance, NODE_COUNTER_HTTP_CLIENT_REQUEST, @@ -223,7 +226,8 @@ void NODE_COUNT_HTTP_CLIENT_REQUEST() { void NODE_COUNT_HTTP_CLIENT_RESPONSE() { - if (NodeCounterProvider != nullptr && perfctr_incrementULongValue != nullptr) { + if (NodeCounterProvider != nullptr && + perfctr_incrementULongValue != nullptr) { perfctr_incrementULongValue(NodeCounterProvider, perfctr_instance, NODE_COUNTER_HTTP_CLIENT_RESPONSE, @@ -233,7 +237,8 @@ void NODE_COUNT_HTTP_CLIENT_RESPONSE() { void NODE_COUNT_SERVER_CONN_OPEN() { - if (NodeCounterProvider != nullptr && perfctr_incrementULongValue != nullptr) { + if (NodeCounterProvider != nullptr && + perfctr_incrementULongValue != nullptr) { perfctr_incrementULongValue(NodeCounterProvider, perfctr_instance, NODE_COUNTER_SERVER_CONNS, @@ -243,7 +248,8 @@ void NODE_COUNT_SERVER_CONN_OPEN() { void NODE_COUNT_SERVER_CONN_CLOSE() { - if (NodeCounterProvider != nullptr && perfctr_decrementULongValue != nullptr) { + if (NodeCounterProvider != nullptr && + perfctr_decrementULongValue != nullptr) { perfctr_decrementULongValue(NodeCounterProvider, perfctr_instance, NODE_COUNTER_SERVER_CONNS, @@ -253,7 +259,8 @@ void NODE_COUNT_SERVER_CONN_CLOSE() { void NODE_COUNT_NET_BYTES_SENT(int bytes) { - if (NodeCounterProvider != nullptr && perfctr_incrementULongLongValue != nullptr) { + if (NodeCounterProvider != nullptr && + perfctr_incrementULongLongValue != nullptr) { perfctr_incrementULongLongValue(NodeCounterProvider, perfctr_instance, NODE_COUNTER_NET_BYTES_SENT, @@ -263,7 +270,8 @@ void NODE_COUNT_NET_BYTES_SENT(int bytes) { void NODE_COUNT_NET_BYTES_RECV(int bytes) { - if (NodeCounterProvider != nullptr && perfctr_incrementULongLongValue != nullptr) { + if (NodeCounterProvider != nullptr && + perfctr_incrementULongLongValue != nullptr) { perfctr_incrementULongLongValue(NodeCounterProvider, perfctr_instance, NODE_COUNTER_NET_BYTES_RECV, @@ -293,7 +301,8 @@ void NODE_COUNT_GC_PERCENTTIME(unsigned int percent) { void NODE_COUNT_PIPE_BYTES_SENT(int bytes) { - if (NodeCounterProvider != nullptr && perfctr_incrementULongLongValue != nullptr) { + if (NodeCounterProvider != nullptr && + perfctr_incrementULongLongValue != nullptr) { perfctr_incrementULongLongValue(NodeCounterProvider, perfctr_instance, NODE_COUNTER_PIPE_BYTES_SENT, @@ -303,7 +312,8 @@ void NODE_COUNT_PIPE_BYTES_SENT(int bytes) { void NODE_COUNT_PIPE_BYTES_RECV(int bytes) { - if (NodeCounterProvider != nullptr && perfctr_incrementULongLongValue != nullptr) { + if (NodeCounterProvider != nullptr && + perfctr_incrementULongLongValue != nullptr) { perfctr_incrementULongLongValue(NodeCounterProvider, perfctr_instance, NODE_COUNTER_PIPE_BYTES_RECV, From ca4fb084f64e5491b1e66b57b6ac13f70c6beeac Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 089/131] src: lint node_lttng_tp.h PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- Makefile | 2 -- src/node_lttng_tp.h | 60 ++++++++++++++++----------------------------- 2 files changed, 21 insertions(+), 41 deletions(-) diff --git a/Makefile b/Makefile index 2c45191c1284a0..8899cdf262cb5e 100644 --- a/Makefile +++ b/Makefile @@ -671,9 +671,7 @@ jslint-ci: tools/eslint-rules tools/jslint.js CPPLINT_EXCLUDE ?= -CPPLINT_EXCLUDE += src/node_lttng.cc CPPLINT_EXCLUDE += src/node_root_certs.h -CPPLINT_EXCLUDE += src/node_lttng_tp.h CPPLINT_EXCLUDE += src/queue.h CPPLINT_EXCLUDE += src/tree.h CPPLINT_EXCLUDE += src/v8abbr.h diff --git a/src/node_lttng_tp.h b/src/node_lttng_tp.h index baa3db2ce0c555..b529bfad5e4b58 100644 --- a/src/node_lttng_tp.h +++ b/src/node_lttng_tp.h @@ -1,3 +1,6 @@ +#ifndef SRC_NODE_LTTNG_TP_H_ +#define SRC_NODE_LTTNG_TP_H_ + #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #undef TRACEPOINT_PROVIDER @@ -17,14 +20,11 @@ TRACEPOINT_EVENT( TP_ARGS( const char*, url, const char*, method, - const char*, forwardedFor - ), + const char*, forwardedFor), TP_FIELDS( ctf_string(url, url) ctf_string(method, method) - ctf_string(forwardedFor, forwardedFor) - ) -) + ctf_string(forwardedFor, forwardedFor)) TRACEPOINT_EVENT( node, @@ -32,27 +32,21 @@ TRACEPOINT_EVENT( TP_ARGS( int, port, const char*, remote, - int, fd - ), + int, fd), TP_FIELDS( ctf_integer(int, port, port) ctf_string(remote, remote) - ctf_integer(int, fd, fd) - ) -) + ctf_integer(int, fd, fd)) TRACEPOINT_EVENT( node, http_client_request, TP_ARGS( const char*, url, - const char*, method - ), + const char*, method), TP_FIELDS( ctf_string(url, url) - ctf_string(method, method) - ) -) + ctf_string(method, method)) TRACEPOINT_EVENT( node, @@ -60,14 +54,11 @@ TRACEPOINT_EVENT( TP_ARGS( int, port, const char*, remote, - int, fd - ), + int, fd), TP_FIELDS( ctf_integer(int, port, port) ctf_string(remote, remote) - ctf_integer(int, fd, fd) - ) -) + ctf_integer(int, fd, fd)) TRACEPOINT_EVENT( node, @@ -76,15 +67,12 @@ TRACEPOINT_EVENT( const char*, remote, int, port, int, fd, - int, buffered - ), + int, buffered), TP_FIELDS( ctf_string(remote, remote) ctf_integer(int, port, port) ctf_integer(int, fd, fd) - ctf_integer(int, buffered, buffered) - ) -) + ctf_integer(int, buffered, buffered)) TRACEPOINT_EVENT( node, @@ -92,26 +80,21 @@ TRACEPOINT_EVENT( TP_ARGS( const char*, remote, int, port, - int, fd - ), + int, fd), TP_FIELDS( ctf_string(remote, remote) ctf_integer(int, port, port) - ctf_integer(int, fd, fd) - ) -) + ctf_integer(int, fd, fd)) TRACEPOINT_EVENT( node, gc_start, TP_ARGS( const char*, gctype, - const char*, gcflags - ), + const char*, gcflags), TP_FIELDS( ctf_string(gctype, gctype) - ctf_string(gcflags, gcflags) - ) + ctf_string(gcflags, gcflags)) ) TRACEPOINT_EVENT( @@ -119,16 +102,15 @@ TRACEPOINT_EVENT( gc_done, TP_ARGS( const char*, gctype, - const char*, gcflags - ), + const char*, gcflags), TP_FIELDS( ctf_string(gctype, gctype) - ctf_string(gcflags, gcflags) - ) -) + ctf_string(gcflags, gcflags)) #endif /* __NODE_LTTNG_TP_H */ #include <lttng/tracepoint-event.h> #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_LTTNG_TP_H_ From 001aa06bc0bdf81a0cbcadf50429e53a09ea4546 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 090/131] src: lint v8abbr.h PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- Makefile | 1 - src/v8abbr.h | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8899cdf262cb5e..4427f2d1d7064d 100644 --- a/Makefile +++ b/Makefile @@ -674,7 +674,6 @@ CPPLINT_EXCLUDE ?= CPPLINT_EXCLUDE += src/node_root_certs.h CPPLINT_EXCLUDE += src/queue.h CPPLINT_EXCLUDE += src/tree.h -CPPLINT_EXCLUDE += src/v8abbr.h CPPLINT_EXCLUDE += $(wildcard test/addons/??_*/*.cc test/addons/??_*/*.h) CPPLINT_FILES = $(filter-out $(CPPLINT_EXCLUDE), $(wildcard \ diff --git a/src/v8abbr.h b/src/v8abbr.h index 95a04b40887771..365825e6cf1472 100644 --- a/src/v8abbr.h +++ b/src/v8abbr.h @@ -76,7 +76,8 @@ * SeqTwoByteString class, but it's the same as the one for SeqOneByteString. */ #ifndef V8DBG_CLASS_SEQTWOBYTESTRING__CHARS__CHAR -#define V8DBG_CLASS_SEQTWOBYTESTRING__CHARS__CHAR V8DBG_CLASS_SEQONEBYTESTRING__CHARS__CHAR +#define V8DBG_CLASS_SEQTWOBYTESTRING__CHARS__CHAR \ + V8DBG_CLASS_SEQONEBYTESTRING__CHARS__CHAR #endif /* Heap class->field offsets */ From e727cb50218be486d9ab1887a8971d9926fcc5a4 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Tue, 28 Jun 2016 21:21:21 +0200 Subject: [PATCH 091/131] tools: fix -Wunused-variable warning PR-URL: https://github.com/nodejs/node/pull/7462 Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- tools/icu/iculslocs.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/icu/iculslocs.cc b/tools/icu/iculslocs.cc index d71f9b8e5d702c..2471e3f8583011 100644 --- a/tools/icu/iculslocs.cc +++ b/tools/icu/iculslocs.cc @@ -193,7 +193,6 @@ int dumpAllButInstalledLocales(int lev, LocalUFILEPointer* bf, UErrorCode* status) { ures_resetIterator(bund->getAlias()); - const UBool isTable = (UBool)(ures_getType(bund->getAlias()) == URES_TABLE); LocalUResourceBundlePointer t; while (U_SUCCESS(*status) && ures_hasNext(bund->getAlias())) { t.adoptInstead(ures_getNextResource(bund->getAlias(), t.orphan(), status)); From a5d894590d090d3f69a04eda6e9c52bbbefcf895 Mon Sep 17 00:00:00 2001 From: Andras <andras@kinvey.com> Date: Tue, 26 Apr 2016 10:33:19 -0400 Subject: [PATCH 092/131] timers: optimize linkedlist Now uses a new L.create() factory to create access-optimized linkedlist objects. PR-URL: https://github.com/nodejs/node/pull/6436 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> --- lib/internal/linkedlist.js | 18 ++++++++++++++++-- lib/timers.js | 6 ++---- test/parallel/test-timers-linked-list.js | 5 +++++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/lib/internal/linkedlist.js b/lib/internal/linkedlist.js index 02186cfedcb9f6..33ada550efb62f 100644 --- a/lib/internal/linkedlist.js +++ b/lib/internal/linkedlist.js @@ -6,6 +6,13 @@ function init(list) { } exports.init = init; +// create a new linked list +function create() { + var list = { _idleNext: null, _idlePrev: null }; + init(list); + return list; +} +exports.create = create; // show the most idle item function peek(list) { @@ -42,10 +49,17 @@ exports.remove = remove; // remove a item from its list and place at the end. function append(list, item) { - remove(item); + if (item._idleNext || item._idlePrev) { + remove(item); + } + + // items are linked with _idleNext -> (older) and _idlePrev -> (newer) + // TODO: swap the linkage to match the intuitive older items at "prev" item._idleNext = list._idleNext; - list._idleNext._idlePrev = item; item._idlePrev = list; + + // the list _idleNext points to tail (newest) and _idlePrev to head (oldest) + list._idleNext._idlePrev = item; list._idleNext = item; } exports.append = append; diff --git a/lib/timers.js b/lib/timers.js index dc2506e01e09a4..7ae25f6a32513d 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -502,16 +502,14 @@ Timeout.prototype.close = function() { }; -var immediateQueue = {}; -L.init(immediateQueue); +var immediateQueue = L.create(); function processImmediate() { var queue = immediateQueue; var domain, immediate; - immediateQueue = {}; - L.init(immediateQueue); + immediateQueue = L.create(); while (L.isEmpty(queue) === false) { immediate = L.shift(queue); diff --git a/test/parallel/test-timers-linked-list.js b/test/parallel/test-timers-linked-list.js index b5ff9f56bf0700..cdae64d344a303 100644 --- a/test/parallel/test-timers-linked-list.js +++ b/test/parallel/test-timers-linked-list.js @@ -103,3 +103,8 @@ assert.equal(C, L.shift(list)); // list assert.ok(L.isEmpty(list)); +var list2 = L.create(); +var list3 = L.create(); +assert.ok(L.isEmpty(list2)); +assert.ok(L.isEmpty(list3)); +assert.ok(list2 != list3); From efb7a90fa959d08eee68c80f2617d50659ffb8e3 Mon Sep 17 00:00:00 2001 From: Andras <andras@kinvey.com> Date: Wed, 27 Apr 2016 20:31:59 -0400 Subject: [PATCH 093/131] timers: optimize `setImmediate()` Save the setImmediate() callback arguments into an array instead of a closure, and invoke the callback on the arguments from an optimizable function. 60% faster setImmediate with 0 args (15% if self-recursive) 4x faster setImmediate with 1-3 args, 2x with > 3 seems to be faster with less memory pressure when memory is tight Changes: - use L.create() to build faster lists - use runCallback() from within tryOnImmediate() - save the arguments and do not build closures for the callbacks PR-URL: https://github.com/nodejs/node/pull/6436 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> --- lib/internal/linkedlist.js | 2 +- lib/timers.js | 79 ++++++++++++++---------- test/parallel/test-timers-immediate.js | 6 ++ test/parallel/test-timers-linked-list.js | 4 +- 4 files changed, 56 insertions(+), 35 deletions(-) diff --git a/lib/internal/linkedlist.js b/lib/internal/linkedlist.js index 33ada550efb62f..d50e3415aa4b54 100644 --- a/lib/internal/linkedlist.js +++ b/lib/internal/linkedlist.js @@ -8,7 +8,7 @@ exports.init = init; // create a new linked list function create() { - var list = { _idleNext: null, _idlePrev: null }; + const list = { _idleNext: null, _idlePrev: null }; init(list); return list; } diff --git a/lib/timers.js b/lib/timers.js index 7ae25f6a32513d..9dbae32405cf6e 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -506,7 +506,7 @@ var immediateQueue = L.create(); function processImmediate() { - var queue = immediateQueue; + const queue = immediateQueue; var domain, immediate; immediateQueue = L.create(); @@ -515,9 +515,13 @@ function processImmediate() { immediate = L.shift(queue); domain = immediate.domain; + if (!immediate._onImmediate) + continue; + if (domain) domain.enter(); + immediate._callback = immediate._onImmediate; tryOnImmediate(immediate, queue); if (domain) @@ -538,7 +542,8 @@ function processImmediate() { function tryOnImmediate(immediate, queue) { var threw = true; try { - immediate._onImmediate(); + // make the actual call outside the try/catch to allow it to be optimized + runCallback(immediate); threw = false; } finally { if (threw && !L.isEmpty(queue)) { @@ -552,14 +557,36 @@ function tryOnImmediate(immediate, queue) { } } +function runCallback(timer) { + const argv = timer._argv; + const argc = argv ? argv.length : 0; + switch (argc) { + // fast-path callbacks with 0-3 arguments + case 0: + return timer._callback(); + case 1: + return timer._callback(argv[0]); + case 2: + return timer._callback(argv[0], argv[1]); + case 3: + return timer._callback(argv[0], argv[1], argv[2]); + // more than 3 arguments run slower with .apply + default: + return timer._callback.apply(timer, argv); + } +} -function Immediate() { } - -Immediate.prototype.domain = undefined; -Immediate.prototype._onImmediate = undefined; -Immediate.prototype._idleNext = undefined; -Immediate.prototype._idlePrev = undefined; +function Immediate() { + // assigning the callback here can cause optimize/deoptimize thrashing + // so have caller annotate the object (node v6.0.0, v8 5.0.71.35) + this._idleNext = null; + this._idlePrev = null; + this._callback = null; + this._argv = null; + this._onImmediate = null; + this.domain = process.domain; +} exports.setImmediate = function(callback, arg1, arg2, arg3) { if (typeof callback !== 'function') { @@ -567,52 +594,40 @@ exports.setImmediate = function(callback, arg1, arg2, arg3) { } var i, args; - var len = arguments.length; - var immediate = new Immediate(); - - L.init(immediate); - switch (len) { + switch (arguments.length) { // fast cases case 0: case 1: - immediate._onImmediate = callback; break; case 2: - immediate._onImmediate = function() { - callback.call(immediate, arg1); - }; + args = [arg1]; break; case 3: - immediate._onImmediate = function() { - callback.call(immediate, arg1, arg2); - }; + args = [arg1, arg2]; break; case 4: - immediate._onImmediate = function() { - callback.call(immediate, arg1, arg2, arg3); - }; + args = [arg1, arg2, arg3]; break; // slow case default: - args = new Array(len - 1); - for (i = 1; i < len; i++) + args = [arg1, arg2, arg3]; + for (i = 4; i < arguments.length; i++) + // extend array dynamically, makes .apply run much faster in v6.0.0 args[i - 1] = arguments[i]; - - immediate._onImmediate = function() { - callback.apply(immediate, args); - }; break; } + // declaring it `const immediate` causes v6.0.0 to deoptimize this function + var immediate = new Immediate(); + immediate._callback = callback; + immediate._argv = args; + immediate._onImmediate = callback; if (!process._needImmediateCallback) { process._needImmediateCallback = true; process._immediateCallback = processImmediate; } - if (process.domain) - immediate.domain = process.domain; - L.append(immediateQueue, immediate); return immediate; diff --git a/test/parallel/test-timers-immediate.js b/test/parallel/test-timers-immediate.js index cd0a423f4d963a..d160adc998fe1b 100644 --- a/test/parallel/test-timers-immediate.js +++ b/test/parallel/test-timers-immediate.js @@ -5,6 +5,7 @@ var assert = require('assert'); let immediateA = false; let immediateB = false; let immediateC = []; +let immediateD = []; setImmediate(function() { try { @@ -25,8 +26,13 @@ setImmediate(function(x, y, z) { immediateC = [x, y, z]; }, 1, 2, 3); +setImmediate(function(x, y, z, a, b) { + immediateD = [x, y, z, a, b]; +}, 1, 2, 3, 4, 5); + process.on('exit', function() { assert.ok(immediateA, 'Immediate should happen after normal execution'); assert.notStrictEqual(immediateB, true, 'immediateB should not fire'); assert.deepStrictEqual(immediateC, [1, 2, 3], 'immediateC args should match'); + assert.deepStrictEqual(immediateD, [1, 2, 3, 4, 5], '5 args should match'); }); diff --git a/test/parallel/test-timers-linked-list.js b/test/parallel/test-timers-linked-list.js index cdae64d344a303..4ec7770cfaa8ff 100644 --- a/test/parallel/test-timers-linked-list.js +++ b/test/parallel/test-timers-linked-list.js @@ -103,8 +103,8 @@ assert.equal(C, L.shift(list)); // list assert.ok(L.isEmpty(list)); -var list2 = L.create(); -var list3 = L.create(); +const list2 = L.create(); +const list3 = L.create(); assert.ok(L.isEmpty(list2)); assert.ok(L.isEmpty(list3)); assert.ok(list2 != list3); From 44f0f940c87a637b185d68346d8db30eb38b2f50 Mon Sep 17 00:00:00 2001 From: Andras <andras@kinvey.com> Date: Wed, 27 Apr 2016 19:59:29 -0400 Subject: [PATCH 094/131] benchmark: add `setImmediate()` benchmarks Timings for sequential and concurren setImmediate() with and without arguments, and set + clearImmediate(). PR-URL: https://github.com/nodejs/node/pull/6436 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> --- benchmark/timers/immediate.js | 113 ++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 benchmark/timers/immediate.js diff --git a/benchmark/timers/immediate.js b/benchmark/timers/immediate.js new file mode 100644 index 00000000000000..7d85dc0325a8be --- /dev/null +++ b/benchmark/timers/immediate.js @@ -0,0 +1,113 @@ +'use strict'; +var common = require('../common.js'); + +var bench = common.createBenchmark(main, { + thousands: [2000], + type: ['depth', 'depth1', 'breadth', 'breadth1', 'breadth4', 'clear'] +}); + +function main(conf) { + var N = +conf.thousands * 1e3; + switch (conf.type) { + case 'depth': + depth(N); + break; + case 'depth1': + depth1(N); + break; + case 'breadth': + breadth(N); + break; + case 'breadth1': + breadth1(N); + break; + case 'breadth4': + breadth4(N); + break; + case 'clear': + clear(N); + break; + } +} + +// setImmediate tail recursion, 0 arguments +function depth(N) { + var n = 0; + bench.start(); + setImmediate(cb); + function cb() { + n++; + if (n === N) + bench.end(N / 1e3); + else + setImmediate(cb); + } +} + +// setImmediate tail recursion, 1 argument +function depth1(N) { + var n = 0; + bench.start(); + setImmediate(cb, 1); + function cb(a1) { + n++; + if (n === N) + bench.end(N / 1e3); + else + setImmediate(cb, 1); + } +} + +// concurrent setImmediate, 0 arguments +function breadth(N) { + var n = 0; + bench.start(); + function cb() { + n++; + if (n === N) + bench.end(N / 1e3); + } + for (var i = 0; i < N; i++) { + setImmediate(cb); + } +} + +// concurrent setImmediate, 1 argument +function breadth1(N) { + var n = 0; + bench.start(); + function cb(a1) { + n++; + if (n === N) + bench.end(N / 1e3); + } + for (var i = 0; i < N; i++) { + setImmediate(cb, 1); + } +} + +// concurrent setImmediate, 4 arguments +function breadth4(N) { + var n = 0; + bench.start(); + function cb(a1, a2, a3, a4) { + n++; + if (n === N) + bench.end(N / 1e3); + } + for (var i = 0; i < N; i++) { + setImmediate(cb, 1, 2, 3, 4); + } +} + +function clear(N) { + bench.start(); + function cb(a1) { + if (a1 === 2) + bench.end(N / 1e3); + } + for (var i = 0; i < N; i++) { + clearImmediate(setImmediate(cb, 1)); + } + setImmediate(cb, 2); +} From c795d1ed9b57811adf622398b1368ee2d5c8a4fa Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Wed, 22 Jun 2016 13:03:46 +0200 Subject: [PATCH 095/131] src: check uv_async_init() return value Pointed out by Coverity. PR-URL: https://github.com/nodejs/node/pull/7374 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> --- src/node.cc | 6 +++--- src/node_win32_etw_provider.cc | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/node.cc b/src/node.cc index 8cadf18f8df812..bc328ad88a80f7 100644 --- a/src/node.cc +++ b/src/node.cc @@ -4180,9 +4180,9 @@ void Init(int* argc, // init async debug messages dispatching // Main thread uses uv_default_loop - uv_async_init(uv_default_loop(), - &dispatch_debug_messages_async, - DispatchDebugMessagesAsyncCallback); + CHECK_EQ(0, uv_async_init(uv_default_loop(), + &dispatch_debug_messages_async, + DispatchDebugMessagesAsyncCallback)); uv_unref(reinterpret_cast<uv_handle_t*>(&dispatch_debug_messages_async)); #if defined(NODE_V8_OPTIONS) diff --git a/src/node_win32_etw_provider.cc b/src/node_win32_etw_provider.cc index 3aa8578db029d3..7b766bd2bb99b8 100644 --- a/src/node_win32_etw_provider.cc +++ b/src/node_win32_etw_provider.cc @@ -155,9 +155,9 @@ void init_etw() { event_write = (EventWriteFunc)GetProcAddress(advapi, "EventWrite"); // create async object used to invoke main thread from callback - uv_async_init(uv_default_loop(), - &dispatch_etw_events_change_async, - etw_events_change_async); + CHECK_EQ(0, uv_async_init(uv_default_loop(), + &dispatch_etw_events_change_async, + etw_events_change_async)); uv_unref(reinterpret_cast<uv_handle_t*>(&dispatch_etw_events_change_async)); if (event_register) { From 3b1c19f90a701b3f4729f8d1c2c0896b021e7b0f Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Wed, 22 Jun 2016 13:13:58 +0200 Subject: [PATCH 096/131] src: initialize encoding_ data member Pointed out by Coverity. Not really a bug because it's assigned before use but explicit assignment in the constructor is more obviously correct. PR-URL: https://github.com/nodejs/node/pull/7374 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> --- src/fs_event_wrap.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fs_event_wrap.cc b/src/fs_event_wrap.cc index 10af967def48fd..cf9559df9d0c5a 100644 --- a/src/fs_event_wrap.cc +++ b/src/fs_event_wrap.cc @@ -34,6 +34,8 @@ class FSEventWrap: public HandleWrap { size_t self_size() const override { return sizeof(*this); } private: + static const encoding kDefaultEncoding = UTF8; + FSEventWrap(Environment* env, Local<Object> object); ~FSEventWrap() override; @@ -41,8 +43,8 @@ class FSEventWrap: public HandleWrap { int status); uv_fs_event_t handle_; - bool initialized_; - enum encoding encoding_; + bool initialized_ = false; + enum encoding encoding_ = kDefaultEncoding; }; @@ -50,9 +52,7 @@ FSEventWrap::FSEventWrap(Environment* env, Local<Object> object) : HandleWrap(env, object, reinterpret_cast<uv_handle_t*>(&handle_), - AsyncWrap::PROVIDER_FSEVENTWRAP) { - initialized_ = false; -} + AsyncWrap::PROVIDER_FSEVENTWRAP) {} FSEventWrap::~FSEventWrap() { @@ -101,7 +101,7 @@ void FSEventWrap::Start(const FunctionCallbackInfo<Value>& args) { if (args[2]->IsTrue()) flags |= UV_FS_EVENT_RECURSIVE; - wrap->encoding_ = ParseEncoding(env->isolate(), args[3], UTF8); + wrap->encoding_ = ParseEncoding(env->isolate(), args[3], kDefaultEncoding); int err = uv_fs_event_init(wrap->env()->event_loop(), &wrap->handle_); if (err == 0) { From 9945b4ecd6c3bcb42904c4ed359dab99deb1c2d6 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Wed, 22 Jun 2016 13:17:03 +0200 Subject: [PATCH 097/131] src: guard against starting fs watcher twice This commit adds a CHECK that verifies that the file event watcher is not started twice, which would be indicative of a bug in lib/fs.js. PR-URL: https://github.com/nodejs/node/pull/7374 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> --- src/fs_event_wrap.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fs_event_wrap.cc b/src/fs_event_wrap.cc index cf9559df9d0c5a..2354a1be0cd57b 100644 --- a/src/fs_event_wrap.cc +++ b/src/fs_event_wrap.cc @@ -88,6 +88,7 @@ void FSEventWrap::Start(const FunctionCallbackInfo<Value>& args) { FSEventWrap* wrap; ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); + CHECK_EQ(wrap->initialized_, false); static const char kErrMsg[] = "filename must be a string or Buffer"; if (args.Length() < 1) From 2228a656b07a8c2a357be92786e060f30c00f88e Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Wed, 22 Jun 2016 13:35:01 +0200 Subject: [PATCH 098/131] src: remove unused data member write_queue_size_ Remove TLSWrap::write_queue_size_, it's not used anywhere. PR-URL: https://github.com/nodejs/node/pull/7374 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> --- src/tls_wrap.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tls_wrap.h b/src/tls_wrap.h index d608ddfcb5b37c..f390c9fe9281f7 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -150,7 +150,6 @@ class TLSWrap : public AsyncWrap, BIO* enc_out_; NodeBIO* clear_in_; size_t write_size_; - size_t write_queue_size_; typedef ListHead<WriteItem, &WriteItem::member_> WriteItemList; WriteItemList write_item_queue_; WriteItemList pending_write_items_; From 25bc7fee34ddea9a57fcdace77b8eecb2b22cff0 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Wed, 22 Jun 2016 13:56:59 +0200 Subject: [PATCH 099/131] src: remove unused md_ data members The code assigned the result of EVP_get_digestbyname() to data members called md_ that were not used outside the initialization functions. PR-URL: https://github.com/nodejs/node/pull/7374 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> --- src/node_crypto.cc | 34 +++++++++++++++++----------------- src/node_crypto.h | 6 ------ 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index def23744ef1292..d7dcd6936c4352 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3585,17 +3585,17 @@ void Hmac::New(const FunctionCallbackInfo<Value>& args) { void Hmac::HmacInit(const char* hash_type, const char* key, int key_len) { HandleScope scope(env()->isolate()); - CHECK_EQ(md_, nullptr); - md_ = EVP_get_digestbyname(hash_type); - if (md_ == nullptr) { + CHECK_EQ(initialised_, false); + const EVP_MD* md = EVP_get_digestbyname(hash_type); + if (md == nullptr) { return env()->ThrowError("Unknown message digest"); } HMAC_CTX_init(&ctx_); int result = 0; if (key_len == 0) { - result = HMAC_Init(&ctx_, "", 0, md_); + result = HMAC_Init(&ctx_, "", 0, md); } else { - result = HMAC_Init(&ctx_, key, key_len, md_); + result = HMAC_Init(&ctx_, key, key_len, md); } if (!result) { return ThrowCryptoError(env(), ERR_get_error()); @@ -3730,12 +3730,12 @@ void Hash::New(const FunctionCallbackInfo<Value>& args) { bool Hash::HashInit(const char* hash_type) { - CHECK_EQ(md_, nullptr); - md_ = EVP_get_digestbyname(hash_type); - if (md_ == nullptr) + CHECK_EQ(initialised_, false); + const EVP_MD* md = EVP_get_digestbyname(hash_type); + if (md == nullptr) return false; EVP_MD_CTX_init(&mdctx_); - if (EVP_DigestInit_ex(&mdctx_, md_, nullptr) <= 0) { + if (EVP_DigestInit_ex(&mdctx_, md, nullptr) <= 0) { return false; } initialised_ = true; @@ -3881,13 +3881,13 @@ void Sign::New(const FunctionCallbackInfo<Value>& args) { SignBase::Error Sign::SignInit(const char* sign_type) { - CHECK_EQ(md_, nullptr); - md_ = EVP_get_digestbyname(sign_type); - if (!md_) + CHECK_EQ(initialised_, false); + const EVP_MD* md = EVP_get_digestbyname(sign_type); + if (md == nullptr) return kSignUnknownDigest; EVP_MD_CTX_init(&mdctx_); - if (!EVP_SignInit_ex(&mdctx_, md_, nullptr)) + if (!EVP_SignInit_ex(&mdctx_, md, nullptr)) return kSignInit; initialised_ = true; @@ -4087,13 +4087,13 @@ void Verify::New(const FunctionCallbackInfo<Value>& args) { SignBase::Error Verify::VerifyInit(const char* verify_type) { - CHECK_EQ(md_, nullptr); - md_ = EVP_get_digestbyname(verify_type); - if (md_ == nullptr) + CHECK_EQ(initialised_, false); + const EVP_MD* md = EVP_get_digestbyname(verify_type); + if (md == nullptr) return kSignUnknownDigest; EVP_MD_CTX_init(&mdctx_); - if (!EVP_VerifyInit_ex(&mdctx_, md_, nullptr)) + if (!EVP_VerifyInit_ex(&mdctx_, md, nullptr)) return kSignInit; initialised_ = true; diff --git a/src/node_crypto.h b/src/node_crypto.h index e0d01887ac9db2..24ac77365cf455 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -500,14 +500,12 @@ class Hmac : public BaseObject { Hmac(Environment* env, v8::Local<v8::Object> wrap) : BaseObject(env, wrap), - md_(nullptr), initialised_(false) { MakeWeak<Hmac>(this); } private: HMAC_CTX ctx_; /* coverity[member_decl] */ - const EVP_MD* md_; /* coverity[member_decl] */ bool initialised_; }; @@ -531,14 +529,12 @@ class Hash : public BaseObject { Hash(Environment* env, v8::Local<v8::Object> wrap) : BaseObject(env, wrap), - md_(nullptr), initialised_(false) { MakeWeak<Hash>(this); } private: EVP_MD_CTX mdctx_; /* coverity[member_decl] */ - const EVP_MD* md_; /* coverity[member_decl] */ bool initialised_; bool finalized_; }; @@ -557,7 +553,6 @@ class SignBase : public BaseObject { SignBase(Environment* env, v8::Local<v8::Object> wrap) : BaseObject(env, wrap), - md_(nullptr), initialised_(false) { } @@ -571,7 +566,6 @@ class SignBase : public BaseObject { void CheckThrow(Error error); EVP_MD_CTX mdctx_; /* coverity[member_decl] */ - const EVP_MD* md_; /* coverity[member_decl] */ bool initialised_; }; From b7e661b12c8f78981a7de4cf4177a37cc5b3c68b Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Wed, 22 Jun 2016 14:07:56 +0200 Subject: [PATCH 100/131] src: remove duplicate HMAC_Init calls PR-URL: https://github.com/nodejs/node/pull/7374 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> --- src/node_crypto.cc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index d7dcd6936c4352..eda62489a76107 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3591,13 +3591,10 @@ void Hmac::HmacInit(const char* hash_type, const char* key, int key_len) { return env()->ThrowError("Unknown message digest"); } HMAC_CTX_init(&ctx_); - int result = 0; if (key_len == 0) { - result = HMAC_Init(&ctx_, "", 0, md); - } else { - result = HMAC_Init(&ctx_, key, key_len, md); + key = ""; } - if (!result) { + if (!HMAC_Init(&ctx_, key, key_len, md)) { return ThrowCryptoError(env(), ERR_get_error()); } initialised_ = true; From 2816418c043d9654c7846f91100508ac0e9e6c0e Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Wed, 22 Jun 2016 14:08:25 +0200 Subject: [PATCH 101/131] src: remove deprecated HMAC_Init, use HMAC_Init_ex PR-URL: https://github.com/nodejs/node/pull/7374 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> --- src/node_crypto.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index eda62489a76107..a7964cde94ac54 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3594,7 +3594,7 @@ void Hmac::HmacInit(const char* hash_type, const char* key, int key_len) { if (key_len == 0) { key = ""; } - if (!HMAC_Init(&ctx_, key, key_len, md)) { + if (!HMAC_Init_ex(&ctx_, key, key_len, md, nullptr)) { return ThrowCryptoError(env(), ERR_get_error()); } initialised_ = true; From ce039c32402716fddfd0b42b062af2e9eaaa0384 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Wed, 22 Jun 2016 14:32:56 +0200 Subject: [PATCH 102/131] src: fix use-after-return in zlib bindings Pointed out by Coverity. Introduced in commit 5b8e1dab from September 2011 ("Initial pass at zlib bindings".) The asynchronous version of Write() used a pointer to a stack-allocated buffer on flush. A mitigating factor is that zlib does not dereference the pointer for zero-sized writes but it's still technically UB. PR-URL: https://github.com/nodejs/node/pull/7374 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> --- src/node_zlib.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/node_zlib.cc b/src/node_zlib.cc index 785c1a9b33fda7..a72e1d1f233c01 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -149,8 +149,7 @@ class ZCtx : public AsyncWrap { if (args[1]->IsNull()) { // just a flush - Bytef nada[1] = { 0 }; - in = nada; + in = nullptr; in_len = 0; in_off = 0; } else { From 6fa560dce96238789d2153fe255bf37d106c4b3e Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Wed, 22 Jun 2016 14:56:18 +0200 Subject: [PATCH 103/131] src: fix memory leak in WriteBuffers() error path Pointed out by Coverity. Introduced in commit 05d30d53 from July 2015 ("fs: implemented WriteStream#writev"). WriteBuffers() leaked memory in the synchronous uv_fs_write() error path when trying to write > 1024 buffers. PR-URL: https://github.com/nodejs/node/pull/7374 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> --- src/node_file.cc | 25 +++++-------------------- src/util.h | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/node_file.cc b/src/node_file.cc index 089ead82ea430c..968284788a4b72 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -1079,38 +1079,23 @@ static void WriteBuffers(const FunctionCallbackInfo<Value>& args) { int64_t pos = GET_OFFSET(args[2]); Local<Value> req = args[3]; - uint32_t chunkCount = chunks->Length(); + MaybeStackBuffer<uv_buf_t> iovs(chunks->Length()); - uv_buf_t s_iovs[1024]; // use stack allocation when possible - uv_buf_t* iovs; - - if (chunkCount > arraysize(s_iovs)) - iovs = new uv_buf_t[chunkCount]; - else - iovs = s_iovs; - - for (uint32_t i = 0; i < chunkCount; i++) { + for (uint32_t i = 0; i < iovs.length(); i++) { Local<Value> chunk = chunks->Get(i); - if (!Buffer::HasInstance(chunk)) { - if (iovs != s_iovs) - delete[] iovs; + if (!Buffer::HasInstance(chunk)) return env->ThrowTypeError("Array elements all need to be buffers"); - } iovs[i] = uv_buf_init(Buffer::Data(chunk), Buffer::Length(chunk)); } if (req->IsObject()) { - ASYNC_CALL(write, req, UTF8, fd, iovs, chunkCount, pos) - if (iovs != s_iovs) - delete[] iovs; + ASYNC_CALL(write, req, UTF8, fd, *iovs, iovs.length(), pos) return; } - SYNC_CALL(write, nullptr, fd, iovs, chunkCount, pos) - if (iovs != s_iovs) - delete[] iovs; + SYNC_CALL(write, nullptr, fd, *iovs, iovs.length(), pos) args.GetReturnValue().Set(SYNC_RESULT); } diff --git a/src/util.h b/src/util.h index 0a6b5e0dbef9c5..1570c57f195c66 100644 --- a/src/util.h +++ b/src/util.h @@ -231,6 +231,16 @@ class MaybeStackBuffer { return buf_; } + T& operator[](size_t index) { + CHECK_LT(index, length()); + return buf_[index]; + } + + const T& operator[](size_t index) const { + CHECK_LT(index, length()); + return buf_[index]; + } + size_t length() const { return length_; } @@ -282,6 +292,10 @@ class MaybeStackBuffer { buf_[0] = T(); } + explicit MaybeStackBuffer(size_t storage) : MaybeStackBuffer() { + AllocateSufficientStorage(storage); + } + ~MaybeStackBuffer() { if (buf_ != buf_st_) free(buf_); From 8f7baffee48800be915792d6ab9aff931a71fdc2 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Wed, 22 Jun 2016 21:57:13 +0200 Subject: [PATCH 104/131] src: fix bad logic in uid/gid checks Pointed out by Coverity. Introduced in commits 3546383c ("process_wrap: avoid leaking memory when throwing due to invalid arguments") and fa4eb47c ("bindings: add spawn_sync bindings"). The return statements inside the if blocks were dead code because their guard conditions always evaluated to false. Remove them. PR-URL: https://github.com/nodejs/node/pull/7374 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> --- src/process_wrap.cc | 14 ++++---------- src/spawn_sync.cc | 33 +++++++-------------------------- src/spawn_sync.h | 1 - 3 files changed, 11 insertions(+), 37 deletions(-) diff --git a/src/process_wrap.cc b/src/process_wrap.cc index 2b214d1d47a8bd..d574bf22965407 100644 --- a/src/process_wrap.cc +++ b/src/process_wrap.cc @@ -123,12 +123,9 @@ class ProcessWrap : public HandleWrap { // options.uid Local<Value> uid_v = js_options->Get(env->uid_string()); if (uid_v->IsInt32()) { - int32_t uid = uid_v->Int32Value(); - if (uid & ~((uv_uid_t) ~0)) { - return env->ThrowRangeError("options.uid is out of range"); - } + const int32_t uid = uid_v->Int32Value(env->context()).FromJust(); options.flags |= UV_PROCESS_SETUID; - options.uid = (uv_uid_t) uid; + options.uid = static_cast<uv_uid_t>(uid); } else if (!uid_v->IsUndefined() && !uid_v->IsNull()) { return env->ThrowTypeError("options.uid should be a number"); } @@ -136,12 +133,9 @@ class ProcessWrap : public HandleWrap { // options.gid Local<Value> gid_v = js_options->Get(env->gid_string()); if (gid_v->IsInt32()) { - int32_t gid = gid_v->Int32Value(); - if (gid & ~((uv_gid_t) ~0)) { - return env->ThrowRangeError("options.gid is out of range"); - } + const int32_t gid = gid_v->Int32Value(env->context()).FromJust(); options.flags |= UV_PROCESS_SETGID; - options.gid = (uv_gid_t) gid; + options.gid = static_cast<uv_gid_t>(gid); } else if (!gid_v->IsUndefined() && !gid_v->IsNull()) { return env->ThrowTypeError("options.gid should be a number"); } diff --git a/src/spawn_sync.cc b/src/spawn_sync.cc index 2d6012761c8a97..79f10a0ea2594d 100644 --- a/src/spawn_sync.cc +++ b/src/spawn_sync.cc @@ -729,17 +729,19 @@ int SyncProcessRunner::ParseOptions(Local<Value> js_value) { } Local<Value> js_uid = js_options->Get(env()->uid_string()); if (IsSet(js_uid)) { - if (!CheckRange<uv_uid_t>(js_uid)) + if (!js_uid->IsInt32()) return UV_EINVAL; - uv_process_options_.uid = static_cast<uv_gid_t>(js_uid->Int32Value()); + const int32_t uid = js_uid->Int32Value(env()->context()).FromJust(); + uv_process_options_.uid = static_cast<uv_uid_t>(uid); uv_process_options_.flags |= UV_PROCESS_SETUID; } Local<Value> js_gid = js_options->Get(env()->gid_string()); if (IsSet(js_gid)) { - if (!CheckRange<uv_gid_t>(js_gid)) + if (!js_gid->IsInt32()) return UV_EINVAL; - uv_process_options_.gid = static_cast<uv_gid_t>(js_gid->Int32Value()); + const int32_t gid = js_gid->Int32Value(env()->context()).FromJust(); + uv_process_options_.gid = static_cast<uv_gid_t>(gid); uv_process_options_.flags |= UV_PROCESS_SETGID; } @@ -763,7 +765,7 @@ int SyncProcessRunner::ParseOptions(Local<Value> js_value) { Local<Value> js_max_buffer = js_options->Get(env()->max_buffer_string()); if (IsSet(js_max_buffer)) { - if (!CheckRange<uint32_t>(js_max_buffer)) + if (!js_max_buffer->IsUint32()) return UV_EINVAL; max_buffer_ = js_max_buffer->Uint32Value(); } @@ -915,27 +917,6 @@ bool SyncProcessRunner::IsSet(Local<Value> value) { } -template <typename t> -bool SyncProcessRunner::CheckRange(Local<Value> js_value) { - if ((t) -1 > 0) { - // Unsigned range check. - if (!js_value->IsUint32()) - return false; - if (js_value->Uint32Value() & ~((t) ~0)) - return false; - - } else { - // Signed range check. - if (!js_value->IsInt32()) - return false; - if (js_value->Int32Value() & ~((t) ~0)) - return false; - } - - return true; -} - - int SyncProcessRunner::CopyJsString(Local<Value> js_value, const char** target) { Isolate* isolate = env()->isolate(); diff --git a/src/spawn_sync.h b/src/spawn_sync.h index 8ddba479f31e1b..8f4c05aa5f4768 100644 --- a/src/spawn_sync.h +++ b/src/spawn_sync.h @@ -175,7 +175,6 @@ class SyncProcessRunner { inline int AddStdioInheritFD(uint32_t child_fd, int inherit_fd); static bool IsSet(Local<Value> value); - template <typename t> static bool CheckRange(Local<Value> js_value); int CopyJsString(Local<Value> js_value, const char** target); int CopyJsStringArray(Local<Value> js_value, char** target); From 4b0ab5b30873a3b893ea6f7c74a0bf25792b6043 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Sun, 19 Jun 2016 10:06:33 +0200 Subject: [PATCH 105/131] test: add testcfg.py to test/abort/ `python tools/test.py abort` won't work without one. PR-URL: https://github.com/nodejs/node/pull/6734 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- test/abort/testcfg.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 test/abort/testcfg.py diff --git a/test/abort/testcfg.py b/test/abort/testcfg.py new file mode 100644 index 00000000000000..5e441845681c52 --- /dev/null +++ b/test/abort/testcfg.py @@ -0,0 +1,6 @@ +import sys, os +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +import testpy + +def GetConfiguration(context, root): + return testpy.SimpleTestConfiguration(context, root, 'abort') From c7ab7a31d7610de42b4fff7082cc04a657b03cea Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Sun, 19 Jun 2016 10:14:24 +0200 Subject: [PATCH 106/131] test: fix abort/test-abort-uncaught-exception The --abort-on-uncaught-exception can terminate the process with either a SIGABRT or a SIGILL signal but the test only expected SIGABRT. PR-URL: https://github.com/nodejs/node/pull/6734 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- test/abort/test-abort-uncaught-exception.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/test/abort/test-abort-uncaught-exception.js b/test/abort/test-abort-uncaught-exception.js index 0d9fa6884df9dc..f9847193769d2d 100644 --- a/test/abort/test-abort-uncaught-exception.js +++ b/test/abort/test-abort-uncaught-exception.js @@ -9,23 +9,26 @@ if (process.argv[2] === 'child') { throw new Error('child error'); } else { run('', null); - run('--abort-on-uncaught-exception', 'SIGABRT'); + run('--abort-on-uncaught-exception', ['SIGABRT', 'SIGILL']); } -function run(flags, signal) { +function run(flags, signals) { const args = [__filename, 'child']; if (flags) args.unshift(flags); const child = spawn(node, args); child.on('exit', common.mustCall(function(code, sig) { - if (!common.isWindows) { - assert.strictEqual(sig, signal); - } else { - if (signal) + if (common.isWindows) { + if (signals) assert.strictEqual(code, 3); else assert.strictEqual(code, 1); + } else { + if (signals) + assert.strictEqual(signals.includes(sig), true); + else + assert.strictEqual(sig, null); } })); } From 6de80fcaea7f7026ae1fc84314afafaf62530a8b Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Mon, 20 Jun 2016 10:46:38 +0200 Subject: [PATCH 107/131] test: don't use internal headers in add-on tests There is no real need and it causes endless grief on Windows with some of the upcoming changes. PR-URL: https://github.com/nodejs/node/pull/6734 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- test/addons/buffer-free-callback/binding.cc | 5 +++-- test/addons/buffer-free-callback/binding.gyp | 5 +---- test/addons/make-callback-recurse/binding.cc | 6 +++--- test/addons/make-callback-recurse/binding.gyp | 5 +---- test/addons/make-callback/binding.cc | 9 ++++----- test/addons/make-callback/binding.gyp | 5 +---- test/addons/null-buffer-neuter/binding.cc | 9 +++++---- test/addons/null-buffer-neuter/binding.gyp | 5 +---- 8 files changed, 19 insertions(+), 30 deletions(-) diff --git a/test/addons/buffer-free-callback/binding.cc b/test/addons/buffer-free-callback/binding.cc index 7bfd5e061d8b0c..a72386fdd627ab 100644 --- a/test/addons/buffer-free-callback/binding.cc +++ b/test/addons/buffer-free-callback/binding.cc @@ -1,8 +1,9 @@ #include <node.h> #include <node_buffer.h> -#include <util.h> #include <v8.h> +#include <assert.h> + static int alive; static char buf[1024]; @@ -32,7 +33,7 @@ void Check(const v8::FunctionCallbackInfo<v8::Value>& args) { v8::Isolate* isolate = args.GetIsolate(); isolate->RequestGarbageCollectionForTesting( v8::Isolate::kFullGarbageCollection); - CHECK_GT(alive, 0); + assert(alive > 0); } void init(v8::Local<v8::Object> target) { diff --git a/test/addons/buffer-free-callback/binding.gyp b/test/addons/buffer-free-callback/binding.gyp index 50d094746c474e..7ede63d94a0d77 100644 --- a/test/addons/buffer-free-callback/binding.gyp +++ b/test/addons/buffer-free-callback/binding.gyp @@ -2,10 +2,7 @@ 'targets': [ { 'target_name': 'binding', - 'defines': [ - 'NODE_WANT_INTERNALS=1', - 'V8_DEPRECATION_WARNINGS=1', - ], + 'defines': [ 'V8_DEPRECATION_WARNINGS=1' ], 'sources': [ 'binding.cc' ] } ] diff --git a/test/addons/make-callback-recurse/binding.cc b/test/addons/make-callback-recurse/binding.cc index 1195dbe2ff7e4c..3e3a1464930477 100644 --- a/test/addons/make-callback-recurse/binding.cc +++ b/test/addons/make-callback-recurse/binding.cc @@ -1,7 +1,7 @@ #include "node.h" #include "v8.h" -#include "../../../src/util.h" +#include <assert.h> using v8::Function; using v8::FunctionCallbackInfo; @@ -13,8 +13,8 @@ using v8::Value; namespace { void MakeCallback(const FunctionCallbackInfo<Value>& args) { - CHECK(args[0]->IsObject()); - CHECK(args[1]->IsFunction()); + assert(args[0]->IsObject()); + assert(args[1]->IsFunction()); Isolate* isolate = args.GetIsolate(); Local<Object> recv = args[0].As<Object>(); Local<Function> method = args[1].As<Function>(); diff --git a/test/addons/make-callback-recurse/binding.gyp b/test/addons/make-callback-recurse/binding.gyp index 50d094746c474e..7ede63d94a0d77 100644 --- a/test/addons/make-callback-recurse/binding.gyp +++ b/test/addons/make-callback-recurse/binding.gyp @@ -2,10 +2,7 @@ 'targets': [ { 'target_name': 'binding', - 'defines': [ - 'NODE_WANT_INTERNALS=1', - 'V8_DEPRECATION_WARNINGS=1', - ], + 'defines': [ 'V8_DEPRECATION_WARNINGS=1' ], 'sources': [ 'binding.cc' ] } ] diff --git a/test/addons/make-callback/binding.cc b/test/addons/make-callback/binding.cc index a1adb997bbf9b9..8970e9fb1af76c 100644 --- a/test/addons/make-callback/binding.cc +++ b/test/addons/make-callback/binding.cc @@ -1,15 +1,14 @@ #include "node.h" #include "v8.h" -#include "../../../src/util.h" - +#include <assert.h> #include <vector> namespace { void MakeCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { - CHECK(args[0]->IsObject()); - CHECK(args[1]->IsFunction() || args[1]->IsString()); + assert(args[0]->IsObject()); + assert(args[1]->IsFunction() || args[1]->IsString()); auto isolate = args.GetIsolate(); auto recv = args[0].As<v8::Object>(); std::vector<v8::Local<v8::Value>> argv; @@ -26,7 +25,7 @@ void MakeCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { result = node::MakeCallback(isolate, recv, method, argv.size(), argv.data()); } else { - UNREACHABLE(); + assert(0 && "unreachable"); } args.GetReturnValue().Set(result); } diff --git a/test/addons/make-callback/binding.gyp b/test/addons/make-callback/binding.gyp index 50d094746c474e..7ede63d94a0d77 100644 --- a/test/addons/make-callback/binding.gyp +++ b/test/addons/make-callback/binding.gyp @@ -2,10 +2,7 @@ 'targets': [ { 'target_name': 'binding', - 'defines': [ - 'NODE_WANT_INTERNALS=1', - 'V8_DEPRECATION_WARNINGS=1', - ], + 'defines': [ 'V8_DEPRECATION_WARNINGS=1' ], 'sources': [ 'binding.cc' ] } ] diff --git a/test/addons/null-buffer-neuter/binding.cc b/test/addons/null-buffer-neuter/binding.cc index da75919011f58b..532e22883383dd 100644 --- a/test/addons/null-buffer-neuter/binding.cc +++ b/test/addons/null-buffer-neuter/binding.cc @@ -1,12 +1,13 @@ #include <node.h> #include <node_buffer.h> -#include <util.h> #include <v8.h> +#include <assert.h> + static int alive; static void FreeCallback(char* data, void* hint) { - CHECK_EQ(data, nullptr); + assert(data == nullptr); alive--; } @@ -24,13 +25,13 @@ void Run(const v8::FunctionCallbackInfo<v8::Value>& args) { nullptr).ToLocalChecked(); char* data = node::Buffer::Data(buf); - CHECK_EQ(data, nullptr); + assert(data == nullptr); } isolate->RequestGarbageCollectionForTesting( v8::Isolate::kFullGarbageCollection); - CHECK_EQ(alive, 0); + assert(alive == 0); } void init(v8::Local<v8::Object> target) { diff --git a/test/addons/null-buffer-neuter/binding.gyp b/test/addons/null-buffer-neuter/binding.gyp index 50d094746c474e..7ede63d94a0d77 100644 --- a/test/addons/null-buffer-neuter/binding.gyp +++ b/test/addons/null-buffer-neuter/binding.gyp @@ -2,10 +2,7 @@ 'targets': [ { 'target_name': 'binding', - 'defines': [ - 'NODE_WANT_INTERNALS=1', - 'V8_DEPRECATION_WARNINGS=1', - ], + 'defines': [ 'V8_DEPRECATION_WARNINGS=1' ], 'sources': [ 'binding.cc' ] } ] From 6cec90a611290291b7e360c7a91a103e9338c3fb Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Fri, 13 May 2016 17:36:50 +0200 Subject: [PATCH 108/131] src: print backtrace on fatal error Print a C backtrace on fatal errors to make it easier to debug issues like https://github.com/nodejs/node/issues/6727. PR-URL: https://github.com/nodejs/node/pull/6734 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- node.gyp | 2 ++ src/backtrace_posix.cc | 50 ++++++++++++++++++++++++++++++++++++++++++ src/backtrace_win32.cc | 8 +++++++ src/node.cc | 1 + src/util.h | 3 +++ 5 files changed, 64 insertions(+) create mode 100644 src/backtrace_posix.cc create mode 100644 src/backtrace_win32.cc diff --git a/node.gyp b/node.gyp index c14f57e94a02f3..06496b13b056ec 100644 --- a/node.gyp +++ b/node.gyp @@ -470,6 +470,7 @@ [ 'OS=="win"', { 'sources': [ + 'src/backtrace_win32.cc', 'src/res/node.rc', ], 'defines!': [ @@ -484,6 +485,7 @@ 'libraries': [ '-lpsapi.lib' ] }, { # POSIX 'defines': [ '__POSIX__' ], + 'sources': [ 'src/backtrace_posix.cc' ], }], [ 'OS=="mac"', { # linking Corefoundation is needed since certain OSX debugging tools diff --git a/src/backtrace_posix.cc b/src/backtrace_posix.cc new file mode 100644 index 00000000000000..468d9784528b0f --- /dev/null +++ b/src/backtrace_posix.cc @@ -0,0 +1,50 @@ +#include "node.h" + +#if defined(__linux__) +#include <features.h> +#endif + +#if defined(__linux__) && !defined(__GLIBC__) +#define HAVE_EXECINFO_H 0 +#else +#define HAVE_EXECINFO_H 1 +#endif + +#if HAVE_EXECINFO_H +#include <cxxabi.h> +#include <dlfcn.h> +#include <execinfo.h> +#include <stdio.h> +#endif + +namespace node { + +void DumpBacktrace(FILE* fp) { +#if HAVE_EXECINFO_H + void* frames[256]; + const int size = backtrace(frames, arraysize(frames)); + if (size <= 0) { + return; + } + for (int i = 1; i < size; i += 1) { + void* frame = frames[i]; + fprintf(fp, "%2d: ", i); + Dl_info info; + const bool have_info = dladdr(frame, &info); + if (!have_info || info.dli_sname == nullptr) { + fprintf(fp, "%p", frame); + } else if (char* demangled = abi::__cxa_demangle(info.dli_sname, 0, 0, 0)) { + fprintf(fp, "%s", demangled); + free(demangled); + } else { + fprintf(fp, "%s", info.dli_sname); + } + if (have_info && info.dli_fname != nullptr) { + fprintf(fp, " [%s]", info.dli_fname); + } + fprintf(fp, "\n"); + } +#endif // HAVE_EXECINFO_H +} + +} // namespace node diff --git a/src/backtrace_win32.cc b/src/backtrace_win32.cc new file mode 100644 index 00000000000000..71610fd663bb3c --- /dev/null +++ b/src/backtrace_win32.cc @@ -0,0 +1,8 @@ +#include "node.h" + +namespace node { + +void DumpBacktrace(FILE* fp) { +} + +} // namespace node diff --git a/src/node.cc b/src/node.cc index bc328ad88a80f7..d97c533a56b9c6 100644 --- a/src/node.cc +++ b/src/node.cc @@ -2452,6 +2452,7 @@ static void OnFatalError(const char* location, const char* message) { } else { PrintErrorString("FATAL ERROR: %s\n", message); } + DumpBacktrace(stderr); fflush(stderr); ABORT(); } diff --git a/src/util.h b/src/util.h index 1570c57f195c66..94278e2b9fc5e5 100644 --- a/src/util.h +++ b/src/util.h @@ -8,6 +8,7 @@ #include <assert.h> #include <signal.h> #include <stddef.h> +#include <stdio.h> #include <stdlib.h> #ifdef __APPLE__ @@ -18,6 +19,8 @@ namespace node { +void DumpBacktrace(FILE* fp); + #ifdef __APPLE__ template <typename T> using remove_reference = std::tr1::remove_reference<T>; #else From c96d701769a8ee39bc90a3b542892b4db9031ab8 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Fri, 13 May 2016 17:48:55 +0200 Subject: [PATCH 109/131] src: print backtrace on abort/unreachable code Print a C backtrace on fatal errors to make it easier to debug issues. PR-URL: https://github.com/nodejs/node/pull/6734 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- src/node.cc | 1 - src/util.h | 11 +++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/node.cc b/src/node.cc index d97c533a56b9c6..bc328ad88a80f7 100644 --- a/src/node.cc +++ b/src/node.cc @@ -2452,7 +2452,6 @@ static void OnFatalError(const char* location, const char* message) { } else { PrintErrorString("FATAL ERROR: %s\n", message); } - DumpBacktrace(stderr); fflush(stderr); ABORT(); } diff --git a/src/util.h b/src/util.h index 94278e2b9fc5e5..f3f123c6d1ac31 100644 --- a/src/util.h +++ b/src/util.h @@ -38,11 +38,18 @@ template <typename T> using remove_reference = std::remove_reference<T>; // Windows 8+ does not like abort() in Release mode #ifdef _WIN32 -#define ABORT() raise(SIGABRT) +#define ABORT_NO_BACKTRACE() raise(SIGABRT) #else -#define ABORT() abort() +#define ABORT_NO_BACKTRACE() abort() #endif +#define ABORT() \ + do { \ + node::DumpBacktrace(stderr); \ + fflush(stderr); \ + ABORT_NO_BACKTRACE(); \ + } while (0) + #if defined(NDEBUG) # define ASSERT(expression) # define CHECK(expression) \ From 517e71508ef1d626f0399e6f62e130bb530597cf Mon Sep 17 00:00:00 2001 From: cjihrig <cjihrig@gmail.com> Date: Mon, 16 May 2016 15:41:37 -0400 Subject: [PATCH 110/131] test: add abort test for backtrace validation This commit adds a test that validates backtraces which are printed on fatal errors. PR-URL: https://github.com/nodejs/node/pull/6734 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> --- test/abort/test-abort-backtrace.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 test/abort/test-abort-backtrace.js diff --git a/test/abort/test-abort-backtrace.js b/test/abort/test-abort-backtrace.js new file mode 100644 index 00000000000000..7f72ee8e71c6a5 --- /dev/null +++ b/test/abort/test-abort-backtrace.js @@ -0,0 +1,24 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); + +if (common.isWindows) { + common.skip('Backtraces unimplemented on Windows.'); + return; +} + +if (process.argv[2] === 'child') { + process.abort(); +} else { + const child = cp.spawnSync(`${process.execPath}`, [`${__filename}`, 'child']); + const frames = + child.stderr.toString().trimRight().split('\n').map((s) => s.trim()); + + assert.strictEqual(child.stdout.toString(), ''); + assert.ok(frames.length > 0); + // All frames should start with a frame number. + assert.ok(frames.every((frame, index) => frame.startsWith(`${index + 1}:`))); + // At least some of the frames should include the binary name. + assert.ok(frames.some((frame) => frame.includes(`[${process.execPath}]`))); +} From b8919b1d23403358c265e2d82d6c760446b2f72b Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Sun, 19 Jun 2016 10:44:22 +0200 Subject: [PATCH 111/131] src: move ABORT() logic into node::Abort() Don't inline calls to node::DumpBacktrace() and fflush(), it makes the generated code bigger. A secondary benefit of moving it to a function is that it gives you something to put a breakpoint on. PR-URL: https://github.com/nodejs/node/pull/6734 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- src/node.cc | 9 ++++++++- src/node_internals.h | 4 +--- src/util.h | 14 ++++++++------ 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/node.cc b/src/node.cc index bc328ad88a80f7..fe53b9629e6938 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1788,8 +1788,15 @@ void GetActiveHandles(const FunctionCallbackInfo<Value>& args) { } +NO_RETURN void Abort() { + DumpBacktrace(stderr); + fflush(stderr); + ABORT_NO_BACKTRACE(); +} + + static void Abort(const FunctionCallbackInfo<Value>& args) { - ABORT(); + Abort(); } diff --git a/src/node_internals.h b/src/node_internals.h index f9370c0a29d14e..7d0a37e5d08fc9 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -134,12 +134,10 @@ constexpr size_t arraysize(const T(&)[N]) { return N; } # define ROUND_UP(a, b) ((a) % (b) ? ((a) + (b)) - ((a) % (b)) : (a)) #endif -#if defined(__GNUC__) && __GNUC__ >= 4 +#ifdef __GNUC__ # define MUST_USE_RESULT __attribute__((warn_unused_result)) -# define NO_RETURN __attribute__((noreturn)) #else # define MUST_USE_RESULT -# define NO_RETURN #endif bool IsExceptionDecorated(Environment* env, v8::Local<v8::Value> er); diff --git a/src/util.h b/src/util.h index f3f123c6d1ac31..a02639affb60bc 100644 --- a/src/util.h +++ b/src/util.h @@ -19,6 +19,13 @@ namespace node { +#ifdef __GNUC__ +#define NO_RETURN __attribute__((noreturn)) +#else +#define NO_RETURN +#endif + +NO_RETURN void Abort(); void DumpBacktrace(FILE* fp); #ifdef __APPLE__ @@ -43,12 +50,7 @@ template <typename T> using remove_reference = std::remove_reference<T>; #define ABORT_NO_BACKTRACE() abort() #endif -#define ABORT() \ - do { \ - node::DumpBacktrace(stderr); \ - fflush(stderr); \ - ABORT_NO_BACKTRACE(); \ - } while (0) +#define ABORT() node::Abort() #if defined(NDEBUG) # define ASSERT(expression) From 57cc4e30714f6e102bd2f7d8bb0ef8f04ecc9485 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Sun, 19 Jun 2016 12:10:45 +0200 Subject: [PATCH 112/131] src: print backtrace on failed CHECK/ASSERT Print a C backtrace on fatal errors to make it easier to debug issues. PR-URL: https://github.com/nodejs/node/pull/6734 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- src/node.cc | 25 +++++++++++++++++++++++++ src/util.h | 40 ++++++++++++++++++++++++++++++++++------ 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/src/node.cc b/src/node.cc index fe53b9629e6938..c9485af1716e54 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1795,6 +1795,31 @@ NO_RETURN void Abort() { } +NO_RETURN void Assert(const char* const (*args)[4]) { + auto filename = (*args)[0]; + auto linenum = (*args)[1]; + auto message = (*args)[2]; + auto function = (*args)[3]; + + char exepath[256]; + size_t exepath_size = sizeof(exepath); + if (uv_exepath(exepath, &exepath_size)) + snprintf(exepath, sizeof(exepath), "node"); + + char pid[12] = {0}; +#ifndef _WIN32 + snprintf(pid, sizeof(pid), "[%u]", getpid()); +#endif + + fprintf(stderr, "%s%s: %s:%s:%s%s Assertion `%s' failed.\n", + exepath, pid, filename, linenum, + function, *function ? ":" : "", message); + fflush(stderr); + + Abort(); +} + + static void Abort(const FunctionCallbackInfo<Value>& args) { Abort(); } diff --git a/src/util.h b/src/util.h index a02639affb60bc..e57651c3a6981d 100644 --- a/src/util.h +++ b/src/util.h @@ -25,7 +25,10 @@ namespace node { #define NO_RETURN #endif +// The slightly odd function signature for Assert() is to ease +// instruction cache pressure in calls from ASSERT and CHECK. NO_RETURN void Abort(); +NO_RETURN void Assert(const char* const (*args)[4]); void DumpBacktrace(FILE* fp); #ifdef __APPLE__ @@ -52,15 +55,40 @@ template <typename T> using remove_reference = std::remove_reference<T>; #define ABORT() node::Abort() -#if defined(NDEBUG) -# define ASSERT(expression) -# define CHECK(expression) \ +#ifdef __GNUC__ +#define LIKELY(expr) __builtin_expect(!!(expr), 1) +#define UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#define PRETTY_FUNCTION_NAME __PRETTY_FUNCTION__ +#else +#define LIKELY(expr) expr +#define UNLIKELY(expr) expr +#define PRETTY_FUNCTION_NAME "" +#endif + +#define STRINGIFY_(x) #x +#define STRINGIFY(x) STRINGIFY_(x) + +#define CHECK(expr) \ do { \ - if (!(expression)) ABORT(); \ + if (UNLIKELY(!(expr))) { \ + static const char* const args[] = { __FILE__, STRINGIFY(__LINE__), \ + #expr, PRETTY_FUNCTION_NAME }; \ + node::Assert(&args); \ + } \ } while (0) + +// FIXME(bnoordhuis) cctests don't link in node::Abort() and node::Assert(). +#ifdef GTEST_DONT_DEFINE_ASSERT_EQ +#undef ABORT +#undef CHECK +#define ABORT ABORT_NO_BACKTRACE +#define CHECK assert +#endif + +#ifdef NDEBUG +#define ASSERT(expr) #else -# define ASSERT(expression) assert(expression) -# define CHECK(expression) assert(expression) +#define ASSERT(expr) CHECK(expr) #endif #define ASSERT_EQ(a, b) ASSERT((a) == (b)) From 658ab3d1e6ff10d204355b2d7deb0a97a2f30161 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Thu, 26 May 2016 18:45:18 +0200 Subject: [PATCH 113/131] test: check types for http request and response Add a basic regression test that checks if the map for IncomingMessage and OutgoingMessage objects is stable over time. The test is not exhaustive in that it doesn't try to establish whether the transition path is the same on every request, it just checks that objects in their final states have the same map. To be investigated why the first (and only the first) ServerRequest object ends up with a deprecated map, regardless of the number of iterations. PR-URL: https://github.com/nodejs/node/pull/7003 Refs: https://github.com/nodejs/node/issues/6294 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- test/parallel/test-http-same-map.js | 55 +++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 test/parallel/test-http-same-map.js diff --git a/test/parallel/test-http-same-map.js b/test/parallel/test-http-same-map.js new file mode 100644 index 00000000000000..0adb73222de184 --- /dev/null +++ b/test/parallel/test-http-same-map.js @@ -0,0 +1,55 @@ +// Flags: --allow_natives_syntax +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = + http.createServer(onrequest).listen(0, common.localhostIPv4, () => next(0)); + +function onrequest(req, res) { + res.end('ok'); + onrequest.requests.push(req); + onrequest.responses.push(res); +} +onrequest.requests = []; +onrequest.responses = []; + +function next(n) { + const { address: host, port } = server.address(); + const req = http.get({ host, port }); + req.once('response', (res) => onresponse(n, req, res)); +} + +function onresponse(n, req, res) { + res.resume(); + + if (n < 3) { + res.once('end', () => next(n + 1)); + } else { + server.close(); + } + + onresponse.requests.push(req); + onresponse.responses.push(res); +} +onresponse.requests = []; +onresponse.responses = []; + +function allSame(list) { + assert(list.length >= 2); + // Use |elt| in no-op position to pacify eslint. + for (const elt of list) elt, eval('%DebugPrint(elt)'); + for (const elt of list) elt, assert(eval('%HaveSameMap(list[0], elt)')); +} + +process.on('exit', () => { + eval('%CollectGarbage(0)'); + // TODO(bnoordhuis) Investigate why the first IncomingMessage ends up + // with a deprecated map. The map is stable after the first request. + allSame(onrequest.requests.slice(1)); + allSame(onrequest.responses); + allSame(onresponse.requests); + allSame(onresponse.responses); +}); From d857e9a3e9dcf8e5ce1ffab1e4f0c755d74f293b Mon Sep 17 00:00:00 2001 From: Myles Borins <mborins@us.ibm.com> Date: Tue, 28 Jun 2016 11:35:18 -0700 Subject: [PATCH 114/131] tools: explicit path for V8 test tap output Currently we do not specific an absolute path for the tap output of the V8 test suite. This is proving to be unreliable across release lines. By prepending `$(PWD)` to each path we can guarantee it will always be in the root folder. PR-URL: https://github.com/nodejs/node/pull/7460 Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com> --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 4427f2d1d7064d..3f0c0b584f2c64 100644 --- a/Makefile +++ b/Makefile @@ -19,9 +19,9 @@ ifdef QUICKCHECK endif ifdef ENABLE_V8_TAP - TAP_V8 := --junitout v8-tap.xml - TAP_V8_INTL := --junitout v8-intl-tap.xml - TAP_V8_BENCHMARKS := --junitout v8-benchmarks-tap.xml + TAP_V8 := --junitout $(PWD)/v8-tap.xml + TAP_V8_INTL := --junitout $(PWD)/v8-intl-tap.xml + TAP_V8_BENCHMARKS := --junitout $(PWD)/v8-benchmarks-tap.xml endif V8_TEST_OPTIONS = $(V8_EXTRA_TEST_OPTIONS) From c4aaf47f4d85d43e2ee594efba1b0c87c797a8af Mon Sep 17 00:00:00 2001 From: Eugene Ostroukhov <eostroukhov@chromium.org> Date: Mon, 27 Jun 2016 16:39:26 -0700 Subject: [PATCH 115/131] inspector: Do cleanups before notifying callback Inspector socket implementation was notifying handshake callback before performing the cleanups, which meant that callback could not reclaim resources allocated by the client. New implementation will free all resource not allocated by the client before calling the callback, allowing the client to complete the cleanup. Fixes: https://github.com/nodejs/node/pull/7418 PR-URL: https://github.com/nodejs/node/pull/7450 Reviewed-By: bnoordhuis - Ben Noordhuis <info@bnoordhuis.nl> --- src/inspector_socket.cc | 57 +++++++++++++++++----------- test/cctest/test_inspector_socket.cc | 5 ++- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/inspector_socket.cc b/src/inspector_socket.cc index b860ef3bb740f7..d3a8d8efffc57c 100644 --- a/src/inspector_socket.cc +++ b/src/inspector_socket.cc @@ -22,6 +22,7 @@ struct http_parsing_state_s { http_parser parser; http_parser_settings parser_settings; handshake_cb callback; + bool done; bool parsing_value; char* ws_key; char* path; @@ -482,24 +483,42 @@ static void handshake_complete(inspector_socket_t* inspector) { inspector->http_parsing_state->path); } -static void cleanup_http_parsing_state(struct http_parsing_state_s* state) { +static void cleanup_http_parsing_state(inspector_socket_t* inspector) { + struct http_parsing_state_s* state = inspector->http_parsing_state; free(state->current_header); free(state->path); free(state->ws_key); free(state); + inspector->http_parsing_state = nullptr; +} + +static void report_handshake_failure_cb(uv_handle_t* handle) { + dispose_inspector(handle); + inspector_socket_t* inspector = + static_cast<inspector_socket_t*>(handle->data); + handshake_cb cb = inspector->http_parsing_state->callback; + cleanup_http_parsing_state(inspector); + cb(inspector, kInspectorHandshakeFailed, nullptr); +} + +static void close_and_report_handshake_failure(inspector_socket_t* inspector) { + uv_handle_t* socket = reinterpret_cast<uv_handle_t*>(&inspector->client); + if (uv_is_closing(socket)) { + report_handshake_failure_cb(socket); + } else { + uv_read_stop(reinterpret_cast<uv_stream_t*>(socket)); + uv_close(socket, report_handshake_failure_cb); + } } static void handshake_failed(inspector_socket_t* inspector) { - http_parsing_state_s* state = inspector->http_parsing_state; const char HANDSHAKE_FAILED_RESPONSE[] = "HTTP/1.0 400 Bad Request\r\n" "Content-Type: text/html; charset=UTF-8\r\n\r\n" "WebSockets request was expected\r\n"; write_to_client(inspector, HANDSHAKE_FAILED_RESPONSE, sizeof(HANDSHAKE_FAILED_RESPONSE) - 1); - close_connection(inspector); - inspector->http_parsing_state = nullptr; - state->callback(inspector, kInspectorHandshakeFailed, state->path); + close_and_report_handshake_failure(inspector); } // init_handshake references message_complete_cb @@ -542,11 +561,10 @@ static int message_complete_cb(http_parser* parser) { int len = sizeof(accept_response); if (write_to_client(inspector, accept_response, len) >= 0) { handshake_complete(inspector); + inspector->http_parsing_state->done = true; } else { - state->callback(inspector, kInspectorHandshakeFailed, nullptr); - close_connection(inspector); + close_and_report_handshake_failure(inspector); } - inspector->http_parsing_state = nullptr; } else { handshake_failed(inspector); } @@ -565,27 +583,21 @@ static void data_received_cb(uv_stream_s* client, ssize_t nread, #endif inspector_socket_t* inspector = reinterpret_cast<inspector_socket_t*>((client->data)); - http_parsing_state_s* state = inspector->http_parsing_state; if (nread < 0 || nread == UV_EOF) { - inspector->http_parsing_state->callback(inspector, - kInspectorHandshakeFailed, - nullptr); - close_connection(inspector); - inspector->http_parsing_state = nullptr; + close_and_report_handshake_failure(inspector); } else { + http_parsing_state_s* state = inspector->http_parsing_state; http_parser* parser = &state->parser; - ssize_t parsed = http_parser_execute(parser, &state->parser_settings, - inspector->buffer, - nread); - if (parsed < nread) { + http_parser_execute(parser, &state->parser_settings, inspector->buffer, + nread); + if (parser->http_errno != HPE_OK) { handshake_failed(inspector); } + if (inspector->http_parsing_state->done) { + cleanup_http_parsing_state(inspector); + } inspector->data_len = 0; } - - if (inspector->http_parsing_state == nullptr) { - cleanup_http_parsing_state(state); - } } static void init_handshake(inspector_socket_t* inspector) { @@ -600,6 +612,7 @@ static void init_handshake(inspector_socket_t* inspector) { if (state->path) { state->path[0] = '\0'; } + state->done = false; http_parser_init(&state->parser, HTTP_REQUEST); state->parser.data = inspector; http_parser_settings* settings = &state->parser_settings; diff --git a/test/cctest/test_inspector_socket.cc b/test/cctest/test_inspector_socket.cc index eecb35f824f13a..7290a6e975b958 100644 --- a/test/cctest/test_inspector_socket.cc +++ b/test/cctest/test_inspector_socket.cc @@ -628,6 +628,8 @@ static void ReportsHttpGet_handshake(enum inspector_handshake_event state, break; case 5: expected_state = kInspectorHandshakeFailed; + expected_path = nullptr; + break; case 4: expected_path = "/close"; *cont = false; @@ -677,15 +679,16 @@ HandshakeCanBeCanceled_handshake(enum inspector_handshake_event state, switch (handshake_events - 1) { case 0: EXPECT_EQ(kInspectorHandshakeUpgrading, state); + EXPECT_STREQ("/ws/path", path); break; case 1: EXPECT_EQ(kInspectorHandshakeFailed, state); + EXPECT_STREQ(nullptr, path); break; default: EXPECT_TRUE(false); break; } - EXPECT_STREQ("/ws/path", path); *cont = false; } From 12b199369d08cd4c09120411a173dbfba48521f8 Mon Sep 17 00:00:00 2001 From: James M Snell <jasnell@gmail.com> Date: Tue, 28 Jun 2016 09:37:09 -0700 Subject: [PATCH 116/131] deps: update icu-small to include punycode datafiles PR-URL: https://github.com/nodejs/node/pull/7355 Reviewed-By: Trevor Norris <trev.norris@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> --- deps/icu-small/source/data/in/icudt57l.dat | Bin 1873824 -> 1933056 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/deps/icu-small/source/data/in/icudt57l.dat b/deps/icu-small/source/data/in/icudt57l.dat index e2e8caab68322b4e1d475d0a7df28a18962110da..87131f44e10ac40aaedc96158dffe7a84df9be08 100644 GIT binary patch delta 24820 zcmeHv33wCL7I4}uO)^Vnl1!G#%w(M<>^lfpD`G(eL_nm1vOG2g5fBj(NLfTg#EQru z53%wP0RcggA_7*#iiilvCL$mrqJpBLq7wR_JJU32DX8!1`~Uy@Ue0&t+<Wf5XS>VH zUE1Q8MipMXuc$Dg7z~ECSq4MMY%uf(TnPAnz-IwJ3AnGtU|0_L*lMF;E8s5Y?S_v4 zmk!4aX8{)`2?L%D<pD<l=Vg(Ge88bzq+tl);(?^05OCp2(oh7r!5-4E7;q8b_1T7m zf%=d%>;?jLhBTZ3JOZ%QXfW&=?l1%a_a!JpQ^27Kl%XBqE}dyZZ@{Y=r(qmmD(*Bq zj?&LL4bK4%Mcjr>fC~Zd1>9he+weW$BEU8iSUcEl2ms#ot=n*w>3U$`_8GbZp-VrX zVFcjSfTsXH+um<@1~4_>Z+IDS;Rk-hyMPTX0*224j{tlD(OU*yc|EI{(RSbHdz&<? zH+<CHPkv!~ddh?5haWU&LCA(+gkXYzXch=L5Udbv5bO{z2si`+0tvwZfr3Coa6)iF za6@1qcp!L79yI%&>#fe5K=~5wpEMYJCn0=sX}A{jMWH)*K*kJ)A#q4t1S63l@N0DE zsO|>C0&+cBoaJ-8ne~G4C1Z*44dVghQR5fJZ;d}2%Z)iEhskRSnq*UbQ*+Zbrgo;A zOx;YqO#MxFnMRq$B}|h|GfcBh^G%CP%S|tvHkh`Uwwv~t4w;UdzA}Ah`o(n7WHA$F z#>|^Va~*RNb4zm@bD43cv4gp*`Brlu^I-Em=Aq_$%@3NVm>)IIHqSR700kDob2*fI z*}MUsTg=<cyUYj8$IK_q-<W?gUo@L6xW#SZEQyGvmZg!UrKOFfgXLyRfu)aSu;m`h z{gw%qhb)f)e#-KU<$23W%Ua7u%T|lUyu-5Ba=>`la>8;N@HxwG7Gn;U<H`x-L~{-p z%Z#;i%HY`u;w^J>bHdItV}4F&pvlhq5J%7*QoVEf<_rNmJOPi<IpcFif|g~*sgU|( zFjFh}Sk6;9&*bbeJ`bstIj`irp7WMQ-wj;egV-o@e$K9(gE^l-{PUcz;rRoE{G8vQ zq|u7}l)$W%)n^r~Wkv}?T?kFAt>B3=ueII)v74<0)_&F@))CgR)(O^!tdCirvOZ&d z-n!EIs&$j~ZR`8ieb!H`pIgsZf3zm8W*csE+c;ayR>#)F*4ozA*3s78*4s9~Hq<u4 zR%RS)E3{3w71`$5iXpW$0gqJ>QV<3|ylPu(+hlv&_P%YO?URhWRGI$_AJ;gZ)Sjnp zi4?PyIp=J@+Oq9Ac8A?<&(GoP`%DO8c4$)w4ee`eSJ|(!UkmXv;|=x$#+$WRfxVyo zPWwpv1NKSwN9<1k{b~EN_806g*-Pwi*x#{#U_W3#Y(HT?Z9iv6#$B+RFdTDZ92Uds zU`-NOYpgBS5$lfi#s*-+u+i9fETz(a16SJr-w;-Vj6`M2*O2*N$otdwkJ1L}U#9H; zclQ5fmg|lCKl}e;hyKYi^QZg&U##x`+W#+h=>JRYKMFp=e#g8&-u{K~<ubL#cjI2; zKGRHWj+VCoo{O=4re)X~tOPq?+=Ok#-US-^B7!IO5yU^mKF7ubw=>v}SRx-HYivoa zvI)m=7aqW)xQy4wo8#BuWyW@RbLZa(iNBNiAK;z%8*Tdo;+y}^{=d5cZo+%uyNuU3 z^PLBbogq|(MAc-~RF6xFSIz$mQi*E%`~@uk6vr#t|6fq%|EB$S;DhkQaC{6t5ub+7 z!sp@(@g?{wd>y_SFT;1@2aNmiBlt=D8~i8yB5ol_!b1pzMARdi5xGP@(V6H$+(8T? zh7)6miNrKw1~HqMPb?yq6E71Rh%Ll+Vh?eMI8J;;d{6vJWRrHX27mrJ`#A^a=<QhG zIPBo4-qZr>FvZaw>FM+~+UjiYobKG_w7S~6rn|Pf&bykpN4r<MPq}>#h3U;KU{)}v znNMwDSgtLA1=+_I4*S@Wi`DzsqQgW;R)x*j5sfC`ci<me7J@GXLR~$JQWAF%#aVNS z{qX#hNY@FI>G<CoOH@<zZxvR9ti*p?U^RLFv%i`{YG|W>zeEA)B>iL<2CM$*7B<Jp zIwXoWB3qK3>}|-(ITL+Ebs)P!<^}t0<Q?Q7hz*B|W61GZg%Re-+L$jh&Vck2mE-@? zZ^AXUQRaP>6Zg~P0&+3b$j?~;b=H!vlW$e<DZ^IflqHPsk$cEPnh2!jaftn9J7@cf zJWHNW6*L+g2aHw+?kF?5AyyOQ!zNwLfD{ZkLJq~zz;VC1g(J_=&T+xs33!zmZ*e4U zcMNpgr7<1l7ze~c#};$MGTl*hiJ&s$JV$W`6?VKFt07%xT<<8=@HPkX(XX~$8Kn*w z4+7U?RVW#X>T_n9@v+P5pHcWS7S>$i(uS#tQp1{Yq_*jr>N2M^N2RLn{}U>t>Rb_D zdCkzkR<mt?oO(s!&~%#puU<jS?=e74m!ET1Yu`@x^WZH5l|vEKhkwjdm%cNRz<!SW z90$C#5iryrxAxDMtQ`NBuc~{1ry^9Xe@m6B9z=U?ji{#7)ztOWja0V;WzafUuMF0x z!W!GX72WWF@gLM)+aT(IZ8$ZCnn+EfW>Jq&bE*Bdhbffv5acYRrcnEA)2JoXDrz0I znOa7zp<bijq*hSxQoE_D3RF#1>Hi;5pJu3Wd5`?roJ^5lR+VsR`~O+ysQh=-FH{z7 zqiNbtA25a>R0VWmfRxx{ROmYND036KHQko(KzF9K_P>R`ogPTvolz}Q<IBef!(?r~ zWDm6ic^|FU*#jcWwBs8jegaTukdw4Y{et#9V0<V6LR(fOs_OjLrY>hhmG-|}p+8Gs z1M|rrtaIi0?+-Gm5VojFQ;evRx*Vs!#eQ8_%@fyS^i%XR^z-ye`c--p{dR(WpWa7* zLVr%5p?{>4wAo4g%?nk=nF&Muf3vs)#w8l4Yd$B(s$HI>>@P%y&{>HB>_35CaDtop zyB*T#R2NRk;n`1%4ZsFrL$IM*{BCRnb{)h<VPmjyfR7T%qm?ibQ+KVVLryrs#?sWI zNrOQxzLPF)JQ*rHggu0nCWGeEWCZR5lqN-pDG+L#r(@G02hr+5x*>!n=10|g@1k!{ zJPsKHfG`-sUFI8r&FqSz2E!aJHW!<xVS`~lwg6kG#fz~;P;xQ0q$0;)Sc)wN$_hyD zGU}oXhE-bbYM>+02E)sc{=~QzTc^<xWxaaSP?!%lVF=#TU@Pn(ZdD%`O5cFJqd{r% zMe}y`{h@TXtQ~527~R;nQ>(uV+f&JTFSZ{$h^c3vrafx2iFBTN?1(X<o)|{wW*t(a zchlGV4`WBM<FFBV3j12CwFtW!y8-KleWOmgn=T1`hn>StUi=yR6}yO?EI+Kl$#MiJ z-WOA;r>VR|$}j`28-%(?HpJ1-jOXB~?lJ_k;h!f7{6rE#0agy-V)A10g4u!Vtw#fP zL0~G<i0Xy7A9$d6pn}e8*kCbQEEb#Uy@wXz)`gmT4=rQ`)Z6c&uTJ>!B5>rCBweA? zBT1yC!4Sm5u(2xPweY%l1H3Wb6gF5};#b2a>$UiGcs||%?~LD!chmUa0?%9V0=yS~ zJH+~Ew4=#>75HfK9~H5I6?ib<JGJ;R)jxuMIBTf-+z48=6hOl?wWzD-)2!NaBu!=A zt=>734rbk>J~EPiIJ*E-iyx<(n2?*)?`P9pJqE)_Fc_(eXb7X2I%5Gf+>vr-1y&p0 zO*gNbChCRt3TbMU7^=dkMfcJ^UF3>;>4iC?@%!;{_yqOl(ew>SqXnbse7H7)Dyqju z(_?@(_er`f>Ol%yg&mYg11dAuZgi_7-l9l##j}*tR+)+9)>RuUqpT`Eh7K7a1CfGI zr-@qlJY}*SE=PqyDx%lsDr;s4NYT{J#Z(8ibPV0MX(2uZc8(swXX20Jv+<`YIv&zJ z?F>DpbJwgw5_mOqCr#a{Nz_E7%A;~Q<&c6~Cg~rd)`EV9@FSL)78{g6@k}sWOr_@G z|AaPNn4yhkRozS4s;Zi)>PRfatEu^%)-4uibdDTwSydGghl<)7wrNgH8<~>u0=^ty z3F;MKtASqzAemu4gOHJ_i_+9`A@5nUGF0@W1Xoo^Eq#u1YlWNZ1$2!rbzW7GDJDQg z8u_oSo?Shy8=b0_!l;y<lcA~B=1755DP5VG+q_=(<1mB^u-R}6C*{nvZ0IAEZT<@E zT2|wUR2PS)i>G6{QfUQJ{?I7}SQF@K^D2g5%7m24Iv1S-il^#8T=UgUDKY6DtW%n0 zx72#@KP?L_&sA8H60Da<Ta)4e7%{EwP7_jE!{k8SN&ECpfCh5p)(S4E5*4@rYYlQ2 zgCBK5bw745?*(4e^J)^KX~0nFEIp=4N7GeREj*~V;hHL1lNxffUjR?)&M3h0vX{eE zssfBMtpRFvRWchiWlTyn%~i-543c2+SJ$U9jmk8?1tn8rb=wew{Ok@|8@y^+Z+ZQ9 zW|5Qv>DrnnQo}Pv)^UY6efkv5T3w__92!O&fF`Y9Tw19BvsL!tbdS+H8?vBd_GWMt zW7=XVvsCFHnk#}Sxm7!ot~xwXA=&#{<xDuZd~kWC(@~~NJv)w85|tISet_hr7^mau zGMZ;KQ_``N8`3ykQ@18<`^HLr#*~jilPJ1Hnii=S)T@sx*P$X`Gdc%p@&>*|ePV`@ zSBoB`2Q_}Xl4Dw<ZI!h5Dq}k<W4lppf1|w>gp_fU$|si}#HW@Y#*gC1;ppQ_d@@iF zAPpv$e}k(Xmr#+$-&Hb73p`gz`?)gqYh^5n1hO_FWu3Zl2_@*GH(*bRhv5{OK#vvD zX%sUO>9iT*(5G6e4JOk0`bR|Z397dG!3jzm81^VCs|!gzbyucSTDQ>NA@<alKokwC zKU^10H!-b7s(y1_6>2Y)m+t&^wP~v)nFgdA(mul=AfZ+bAa<m;vU5Vg3_vB~bGK3! zI)kVesH|*-_a?IOk`n21@szrVm&be(y`Hr$Rb3WSJCjSwb2|4_sxOA%avWU5iOg>l zBqgkM+=>b=_Yv(=J|#!j8+?;ntpQU7s$j&<pUF<FZh<%542{*$eY8pK{2^u0T50ZE zR6X>ONNGh5t8YF>@p|*Y2cm$cbn7I#Fq6BCI9S!qlV~o@mQm|0r#ja3QCvR<eHsBE zH8T{EFMWv=PKNEI0$AW^ZILoUGcO@y6R|inX<e*7HXmz@HN~1^S7EJSnRE?yEzqyS za)|4(cG!(rC+ucS#%u&e5JVh9<<YXxK{SR^t)MhbTnD#9CM72&uh&XXNiM~1!lox( z1VeZUKM^3B1D{SnLli%bC<gU=o6(=m6YB0G#@06kiP~70h!PH>Atn+s5hs>li?Q0o zlgYY7ePVvHA<>v<N-W2k6IT%{uvWy?#A@ssVn4PPyOvl7E5Tc_>xk=#^;kRAF_pfh z%2!{>h^chzEc*?_L)tQ_BcZN)i2kO=v7@e^Mt5w4e48FqC9sq$z-F4BGS#y+wzaTb zZEIs|2cQ0ko(gKShv`_N3o*>row&!=lNe>|P26YeON_M*ARe?0(T3wNVk9w^C?uv6 zMZ`R!n5dp!LcCbzT(^4q81XrAnm9*XAWS4qy2<M4i3!^z8%qjgge<_O+GG-$P>1Y7 z2xNVd!RwQa$fj_<luNdO{D*Dr$c|)pq6^uR$R)dxx01cc8Meo4PuTiFZUOd`?P(jT zKZxuN%HK)eO^zg=feXcpZTFK)Z7<qZ*<RN2$CCqy^~nTj!>5qiTW%UTgPa8{*4dsU z=aGGh1>_K71~~+K7HGX{1hhAjdO2;@T1cM(7Llo$Acc`)`dq8W^k-_<<@vhgQmC?m ze5tbHD-c^(nM$#Fjoe7SL8{-`jBH{{wcK9%lmlY4sYer-DXai{hitA<-zDE8-zPsH zcatBI`^W?2N94!kC*-w|KLYL<55j8BFMx4WA2xlu!3b)KwGt}^+x4(BabtzBl$t4g zltd$>PNb?DHKo+EkI-$jR;VdgryVCxlC`m~!0a>RcjQ9w{f{IX`Z}Bk<C=p`R|EZ8 z>{rb>*JDYt9Sq-gT051K*$#`tV7L`hJiL4nZiU$$ajZ7P>SOgoSVJ)68nBUYI2^RY z<!BBWjKl9>(Aakb)ZH`aV1jpq9ipR_qn@LYqnV?nBiGT!k?-i>=<Mj~=;62xD&6kr z>lom;2^$P2fx{j5I=W$F91l3I203+s{qN=L-phPbPXMhe-(FdmI^t3Kply<4Dv+l; z9xb_fCfzT~__*T<_031<`iaVtHFSZ?dH2$Wf6_76@ibhgc-FDV@w{WH<3-0R#~R04 z$9l&`IGx<;*yh;b*yY&kIOzDqaoq8R<Fw;D$4`#m9EN_BnX+FponKC{gK|+`DnJFP zC?!+1srpo7syWq)x`w)rY6k~8H&NZFTd7`DA2^#CObw;(q3)&B(phvUJ4vdmX3>p2 zsEJV*M051b&^vE|3H2SV<JHQ(pF$n5o@G2$2q!uy-ZJ}P4L3@?@0+GyTYh-QI@?}O zQ$$a7>?@T)d1&jXh1y^gP3oU<;V|0gnnBHkvnIqNwKs<_2CoMj5mbM9oDSHEsM(a3 zseU(~_Pg|5Jw&LKh+cr&2bwA4{i@}ooN%=<v?jA^4gCS_mSq0YChFARPwOgHKGnyI z=;l{dYFmTIE9M~^nrfeq*>kjZk`i+-+n451^WmP)LaLZrL@kE1x~0@|IH6lbt)^b4 z*21~ndTIl;5l-()sV&r2s*Ku3ZKrlnJE>jN9{3vFPaUKV!7ZU9)G_Kfb%Hucoua;? zPE+4dXQ}V0bJS1NdFof{LR0D@WuUWZ6K$cbw4KIjlBQ@U?WR4nk7j9(7U&Qip<}c} zD|9V54y#8upiA<epq*JYZpooZzpWGX#ay-H>lEiKqw?W`Qo0qO``q;;y+Zq*wO+Jd z?BML=Je&Qfy^AW(q2I7~bEZ59-L<v02Ld$)%%%sS@3ZZ*=?HbidCYm-dBS<pdCK{f z^R!d7KSf7irv}NG2d<)APM6!|ars=VtC-?kf-B^TxRz>N6O~B`sD||p7IP`CI<5w; zCaxB))~-BPTewx#(bdJ(-PP08*LA0Bl<Ps)RM%s!xvpnjOI>SR8(eR>cDVMrj<`;_ zesmRJ7hDFn$!&GxZp!U;``ny6<c_%&cO7>FcN2FDcWZZ^yREyuyQ8~{ySuxmySKZq zdw_e0dzgC!+}av@yL-I5&^^^X-96Ji%gxb6ZlrPMZ?=1``)T(A_p|Or?&sZ0-7mUV zx!1VYx=Y*}+?(8`?jFwG&i>9p&N0rh&WX;+&Z%%YY=(2DbC$Enspie4xx__x4$Syz zUr}(v<7QS-9;S?{MYo~zpp{S1`zeX`F)YI|0uy2)OpK8jg{j3Hr|K~Em<CKErU}!G zX~DE)bYHh-a+y4)4bzs%XWBCzn2t<mrVG=J>A@7hCA2<Fe`X*vgc-^VPcS2y(ainK zIA#J<$V_3TF^@1ana7#g%u~!fxUE*qEM{I{USw7=FEeYIb<BEZ1GACY%#<=)n5|41 zvyIu#>|k~>yO=%9US>aYkU7L0W{xn&nB&X|<|K29`3mm4eZ!n(zGu!cKQZT-UzrQc zMaJOC_Lw{tPr~Z4dvFg4JBN%X;1NBwJxx3<J-MFto|`<~Jv}|WJ$*d`JwrVsJr8&i zo~fQ$o~JwuJ<oeqcwX_m<|*^M@7d$o?>XZ6%yY`~gXb4d(v$7AdI>M>b$h+ufH&k- zy!E_Iy;pm$^S1YP^%i*hcn5j!@{aV5@jmD+On4vh7J27-pYbm8F7v+ReZ^bi-RLd# zZuM^S?(}}>J?uT{{nq=V_oCP6%kg18%IEckeX_5Cueq<4FV}aiudT10uY<3XuZypn zuZOR|ce}5jZ=ml^-`&2EzWaO+_$K%!`5y8;;(N^ZgzqWe)4m11XMO+jE%7b$t?;e( zCD!^%d>ee5e5Jm(eA|5Q`F8qt`}X<{_zw9#@g4J>@O|k!?fcgEz3)fgdEaloi@t2X z#c%hMe%kN$d;P4R_lNvZzvPem>-Zb^oA{giTluf?U+2&Fckp-iZ}WBY-|Fw>@8kc6 z|4#o1|GoY({s;Wy{R#hM|3m)i{)ZC&NBy(>Px$BfRS5I_&%lmWvHxHGCH`d)TjgKl zU+XXNZ}4vd0j2)8{AK=k{qOq^`F8pD`VT^hkNrpe$NiuCPx-(0f9wCj|C9e0{{?@! zKbtkPRu*GP*2#KUmKE3tE3viMdTb-M8QYT0W!td%YzMY(XSOTbgT0NtgYC}_Vl&59 z4ReDwE2#B~U=`S5B#o=3^J%+knNQzlx{Dpg&Ml^5YS9*os+_e_lL{OS<&A_RFeu+4 zdpN6|hiO|P=yex|%@B3-d|K*q2o`HHF+!V4N3o;Xy2Kb(dqe)tn=K_KjSYtL<XDjN z7S%R$1<T0kBI(y%4Z97<P;}DDUeo%)dQYNsV?ZZSovyOvSh6*nx@yhRiXI}IGn z;l505!YOu4DiT)nwo+34wA4QoL(0}q)L;dupV^>@+3%&R3d}5xy2BOHD)A&;H!+TV zIyr-S8QLrTtIASOjb|sah3sT@Dm#sx&dy+Gva{GCb~ZbQoy*Q+=d%mgg={gqh+WJs zVVAPY*%j<6b~XDlyOv$Yu4gx}8`;flDZ7Q;%9gR)*zN2Nb|<@w-E#xGm)*}EWDl{2 z*(2;R_BeZjJ;|P8zhY0b->_%d@7Z(gPwaX2SM~yXku?Oe1Ezo_U=7#<cz_I00cXG+ z@C1ATHlVG$%$iS;d#mmQbvNozlVw0d739o@uI=FhLSQc)3Pb|2fE37c<~fzX30f_E zhVIH1(c=RX1BHRffvJIMf$4!6fti6>fucafsVWQUCU{w(bzoaydtgUkXFwgk5I%<w z((0^*w2(MXksN$faBj}S`8by2xQ&#+g}4Y8<0MYuYH@YAdRzmp5!Zxk#<k#Da;>>s zE{|)&wdL};_T1?}2d*R6nd`!J<+^h{xSrgOz-?S_?hdXm*Pk1}4dRAycX7kG;oJyr z6gQe1!;R&}apSp(Tp`ywFgd|Z<)(4dxm@QAZYDR2E8=EzbGW(OJZ?U>fLq8Fb53Rv zx0qYPE#;PTE4WqMYVKujS70r-j$6-d;5Krbxl(Qmx0Ng7wsG6J9o%txC%22+!|mnv za|gLY++pqrcZ@sEo#0M#h4d-zEABM+4R@CNo;%0=#3jyizj7D2i=2Va=1shXUq@T{ zjkKM|d6Gx{kAkODi@A9Z@8emX;{`s%NB9^&i#8Y}$WyfZT6`T18w~aM27DvF3Ezxw z!MEgF^SOK;--d6?=kx9P4tz(xGv9?jOLgVD^F54wPhO>pX-|S<M)0Hf(fk-b#*F31 z@#Fc4d?DY2o6Jw;r}5MI8T?FuS$q*co1eqa<>&G9`2~C}ej#7XFX9*TOZa8lao>yl zO8zB&4gU)NDqq6C20~xwH}P-q6#pjw7XLQ?4*xFy9{)c70l%C7kl)82;Ct{N@jVm# z$NVS!QT|i@Gv3J$3VhCg!8fA63C!WX<iF+z@n`rU{I~pf{14!TANim8U-;knB>1D8 z&k~G+S;!G=LLrX{gwTX@2-A65a0!gy75u_%J|HMOFU;Y$1%kp{J}k`Vqk<^NLR_dV zECf&16^i*pePI#bP{?yO7MclH39W@(Ax~%{Trad2ZWKCI*~Y#}=qlVI^b~pveE}{r zfUtiEgM>SUVZuEbB}NHs`F+Azfq=F8IAOdnQ79B93sZ%C<g|qFh%i$q66Of1Fkg5^ zC>H)DED@FoE1>#I!pp*|!g}F#h;N4Qrmz*B?+DxB`M$7A_)yp{d?Xwejta+x&xKRM z*8pcK;9Eh5?*tuw5Onxa(BWr6hr};}4!;RHBn2JH1s$@2Iv9gGn1kkE4uA~+10Vn# z0CWYof;uok9lSvu{6T*(Pys?v2!?f_gV72QgJMttK+{*K+WN32XV(tuP&ZgNSRbGv zz&N~duyL>{Ky!erDxg)cRWKLeI)L^7odCK5+zQZJgML8-11n%i@a{x#6cFwYJ{X)B zoD`fId^k8G_*k$g_+)Ty@af=!;IqL+!RLcZgD(bG1=j@E21|k)f}4V+!MB2C!FPi@ zf*%C;1os6G20soS34R(p5&R<fRq#ykZ19KRPr+Y;7lP%%?2tKR4PhZNM2FlVZ-@=? zp-?Cql0xxNolv5Fs8OhCs70t%C^vL%sBNfSs6(hzs7t6@s7I(E)H~EC)IT&ZG$b@M zG(0piG&*#DXk2JQs4z4oG%YkEG%GYaG&eLq^lWHxXjE`n2$`}nv^q33_(}+wvM%&m zXl`(02$}LmXiI2O@a+&X2dSm0l&I83S7RHf@Lp&q2;Z%VpAy;|Isjr1X@bFF!6P8( zvrx)kUxZTL`U;q*y_D&rGoef`RrOH<ISDx^?Hc43<dw6PK1uoG2hAHlRr=x=O_iy^ zFTh45{zB;8V0kDzYz`wAHv%Jm0M&m9S;Kgk3cJJJFdOE>p>Q-Th2!Bm;rii5iEz_! zi*T!OZur`8+i<&Zhj6EGmvFamk8pOlAly6LC)~g0ihrP9icfjS9R9ST&*(JNQwD|y zh7Is<VJ^42t^BSV5*`LHB0M@gHatFD7@i6+Jv=j96rK~F7hV8R99|5t6ktVoHNe{N zdVq}prQxjr+W>ZicZK(c4+0zx9}Axdp9-G_IGYHc3!e{P2pb|M04o3vKt<dUUxbT< zBC&{~ZI{-GG>9~bw1~8h<N>sew2yR*bOGod=^5!A=^Ggk84?*584(#B85<cNDU3{w zOpnZr6h-Dl=0z4niX)37OCx7N(u&CH$lA#I$i_%1gtd{x*2uQVj>xXaULfoR{SQVC z|AGD4s`JFL$cf0Q$eGCZk)I<MB3V&O6pK2dk!bCx?p^)-tH!6O9gQ`JHj2Vsil`ZH z7Htu28Ep-zOi)+vq|j8A8_kQh0YLlHZKL_o_R$W}j?vE1F43-0*za_7kM@Z6jNS%f z4Tj#)JEH4I9Z;P%QC&&_X2zR<lxES~Dt!7z`$q>v_mP95L!x&@KY~sEVbS5y5z$d8 zT@u*n=>5@gz<dIb3Zqk^)1r?=XGR~7&W=76ofrLQbYb*4fad|00jvaAQvqwEYoqHb zV10CbbVCJfjOwsCszYg1hb>VZwnn!`%K)|kB(_JlM|V`f&ZrK%qB`t}>aaJe!~Uoa z2ctS1it2DUs>6|}4#%Q89FOX7BC5m5s1B#1I(!xVYG4%2@28_Wd>8#LdLE!0z!)>e zP;;S!hGtPqtgq&{{!wcTH577cgXo}WTc<r%(~$yd_95W#yP#1<MYYY`U`?C2y`#gR zN%2^l=oGcza@g9esZiS}8Ox7SF=xyjYwJY)p;6QmBR#$t8{=X^EEJ2xVlgSE#A?O< z!`+hbWwuMKo;tDmu}0t#v<ufdkxX7l{+68Woa3D9oadbHT;M#H{2{r}S?pZoT<l!p zT<ScX{5rYZxx%^1x!U=%bFK44^0VYR=X&P`=h5Vm<R{4{v1YLrv3^*~SnF7BY+o`j z)+W|AmLF>$>k#W0dq24&`Cf8+^4;XN<U7f-<lD)u$+wbQl5c94DK}>)H>nLL(*CT~ z2-VGc3L!aXQW6akZ5<LcLgS$Y39s!jB6{Vlh{%o8TjG2=>d#M&(8|J9#%wD0_b}SE zG;4PXk*sL&%H`n<dKuL`bK&HL1I!9Q479t8c58|T+;h?XF`ik4&$uL&DhRDLVg)^_ z)1Na&_f`6t=BCAvLg5>$5i7L)eL-oqdRcfPh7FNDmP{^)S|2QRUwT=>^*jF57or-+ zOrNz*w{!EW)B2}1(%1Q!Ifvos;AG|TL24gB<6oai6+Bs<1y_<QQtG6aXjjImg+@lU zxKdMYrI9JY=t^^{HWc*Ne91$u+9kwZDe*c^F{?^Vr&9SkPSb0QY5LZyF{?uROY_tb zE1_2wuB5H7J9imCd}VkEWdho(3T1X=QCksa7O4_P4)&nSz(F;#if*r;J$WzfBuuqk ztW|V>y=-l?FJ-O4$-tP=@={Yr7`k7r%4V*r1y~|Nc0INCe##j}dDT6P`ge+^^%!(* z$d_q5az~WNNnc5;xQ>&^6qRD350`7;KhdFF6K2EojW0c&s*!m|E?pDFP>ISDEVyry zx^a@GrFazJ)s>8>ef~w!YW8YcsHtl+)ys!ac|vP|&c>UKJQjg_lKZK)aCieg$b&sW z{PJ$Sgj-#gt-Rt!O<EB0o!3^m_lJ^U^cNX@;SRXPTWLzV0*dLS(9C%S@wHQxQ(jA@ zYru#huMO-hm;`jF+8`B`bjhptQ!zLlx&%-z1D>gKQ#vc%dTH99jv>a?-X(z=hDvY5 z7zxaRxi0ld`=^Cf*p*fs#q`VjWan77SkG9W*r3?mvC%QL!vTuBq$kyVnI4Hh7%PlD z6q^})GB!Vk&JWwDa7W78_}@@QePb3Kt~fyt!OJo8_SAJ4x;CLse1(>p*Yw3DgDvPH z0}|U<d!n0HD0MB|oIrI8)R$(_e%P@`*D0!A%_`8Dql5d@0e%Jcz{Oy6J*!~`Q~f$u z1=DJW{OCGLJVWsU5UzWo>ScfeWGg@SRXSE7h*k4mrLBqQV*k><VI8=B)r-cTkGb%6 z1cSd2TNWFNy%<{=^WuJoA80Sd*2DtZ)vs4#JpO9z_v^eAt6HZdMgyN0Vt#am%mMWi z+WoTsvd|2fiAyaF5Xic!n!Xl$P4iMs-|_zu%~iu)BeX%~i(k1rr$><f-l%)!;_26n zHuaz}@6yj{h8?EtRbD1KBeZPW42!S=>?>G(mBQ-kG_078!z$?{ti%q&YHAm(q_)9o zYy&Liw!l(t53IZnMPHBkgPUS+fU{8j0_>DF^OeH0ruqe#RzCp@3a~v84r#?xwjc&N z=nM+5<3Q0BD8P^chyf~*S>GRoyU&*>pc_ztRW(3YAb~7E3g{LTV4I+N$^)mOX%8R; zsxr9L11Xht4^;KwW$JgV;=(k8nq&HuS#usxQQKCoY|!^as1NMy#<s@ZiEWR)AKMlC zFt$JTQS5Lm&F9~_d~`Hc8}5@W@qZT6W|nZMraAIt>>>Y`v5v&)*ja6+j$uE<e$oa~ zg`(l?Ut*}c*YE}3G;twT9xJQ)Ev$7551lQV#2j%kWfyTV<RV2%bc%(XTl9!Nkrg>n z5bvNvVnmFIlBkHa#CdS#qmI~`@wjrCdg4yHf!IiFA~q9Sh%8f!ZYj1FbHz%>=3#ka zTd}>^QS2gi7ki4m#lGoGO@RU8plX!L$|xVTMBsXzlp3#93TRBK=3k~9V#QP$rm2E8 zeoAC8zMQ}z;$7k}akw~AY{}m%-Y4EKJ|MQ{9~38u2{D(SEKU`tiPObL#mB&vkBhU# zIpW-eI8R*3&llHI&xp^8&x!v6e?2e0AeLyyK(F!3#23ZAft6xg{v~mZ_=@<d1|{MK zaih3dEEV4p-xl8y-xc2z-xog+cZ(m2`@{p{N8-l-oA^(}qvEIHXJUK)bMXuDOYuDS zwRlGSR{T!<LHtqtS^Pyz{3c!$4N|sb7fq5yvPzglN|fZ3+>%G~Nd*`y!O@;1NMR`| z?FfjHEY*_g0R1#oPii2Yr5Z_1q-N4pQY$G}%9GkiZKZsvz0^VKD0P;)NL{7w(ydZ~ z)JwWu8W89sndtsfe`%mJ7~oE6s5Cqwjg;<{?vw799*`cCCP)csk~Br?%0DDcmu5&a zrG5NNX_nN3FOp_UbELUaPrhDYo-|)tAT5-NrA5+W=>=(-^rEy<dP!O%t(DeE>!l6S zMrpHDDs7RrO1JT4(l+Tm>3!(~X}5F-|Dm)`Iv{-{eJp(<9hE+n66o~mGwE~b3+YSg zwDgU1R{CD*%b$~emVS|blUfT&sa(pEjj~y`%61u-Ntu>iG9!ECZvuWfAPaI>j>(c7 zmut&)<@$0%xj#RLYb-aFo6A?pt>s)fPi`ZB&9{~F<s0N1<xX-Jxtn~8e1^YOE|7aA z<lE&wazFVW@*sJLJX9{g?v}sf?~zB!qvbL3SouMDqFg9XmZ!?o0H(_`<RAH&@+`SX zo-NOjRrzUoPhf%kto)q3SbjlXF0YWYgjMnyd97R`Z;&?v7~yPevs@}~k+;h4$kPKk z!n^W&@)&-nyju?OiM{fEc|3nmJ|rKOj{?ASDxZ+QkiV3_me0s%<?rQl@=x-4`Byn9 zm&;j-Q86nyicP^4he9iE#iRI@fHIxum5>ruL`6|*DRq_lN<*cw(o|`#T&1*Du2I@3 z1EuSgcFGOPjY=owCgo<On{tbCt5T3qdMW1ucPM?8{>lJlkTOJ>#owh2Q-&)elu^ok z%2?$=WrC7WCMi>tY07lvQROk^ab>phl%gsV15YdeRGv|uRi0D+r7*(t$_vUer2u<T zS*g6FtWnm}uPE!3^~wfiqq12kRo+s{lx@m(B`Ac2GJ1#dfwEgke5mYG4k(9|Pn4s| zr^;u_NhK<rQodHcQO+vgE9aD-m0y(Kl%!IwWW|kfbKDxY$MHBBr{k_T6ZgjbaV{Q= zhvU(>7+2!8;`N34Lfv@%c%yiec(Ztmc*}U}cy2r|-cYzMeto=M{D%0A@lNqB@viY( z;yvS1!r3cc&v|>iPrP6JAMqB>f!a4pYAx{Z;>%4?DSDP61uC1(aVq%+y*)8Fo(ZX< zSH^>X=aZ@cOP3fN*T44=$HDP8V%6sP#2=VoFI5Kl>vDI}FD-pJ3Yz#!c9zoXJlV-| zb@BoFrji2(Xroab^)X#!i5Tt$D9JudugESbJworzE{PnchnceL8A@iJq}P&ahaYK9 z9sVPITgjdu>9tOE&egQDWP#ObH>xA9qg$4o#GGTY=eDDjk~;0^@!9IC8|c#|3p>!W fvr6W;oa3`i%i;Gh)!zrf4}pacD53guMNR(?N$)u{ delta 458 zcmWm6y)Q#i7{~G7ZF}prx3{;&RU~3zk<`HCZIy@+CJfg=Ow}}z7%FX$RASJ?AQHo6 zM?=J7keG-8e*uF;gy?7?z8${jbIy||r|`GxpJhvhe7B@QgTs_&(Zdyd#2p+BOUHOq z4(b|HKlOS-Ki!~r^nN1>1=z<n>@+M5qvkAmSXi{QhTe&#ZM2^)9byK{0p(TUa>t<Z zY3U6ozbq*zmD5qR;Aq5FFKXS^2&N`tn!!?&qbyc>92KkYBS#0Qu7obp!w0mN68gXl z8X@Tg<5>Pqs4q0c2XR;93{nfOma&8z)rE6c``G#J>J<ISr0&t4O6s*bpH5!f8Uw+- zMss)5H;cY$P=JCIqUtP6HB?J=R8I}mND;CqN;bvFp*SU|iJGb9$~WD^`C@LYm6Fs( L?N_<6j;;QGyH;qH From d0e24923a69326b23ac14cc631724fc537521f9e Mon Sep 17 00:00:00 2001 From: James M Snell <jasnell@gmail.com> Date: Tue, 21 Jun 2016 14:03:05 -0700 Subject: [PATCH 117/131] net: use icu's punycode implementation ICU has a punycode implementation built in. Use it instead of the javascript implementation because it's much faster. PR-URL: https://github.com/nodejs/node/pull/7355 Reviewed-By: Trevor Norris <trev.norris@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> --- benchmark/net/punycode.js | 75 ++++++++++++++++ lib/url.js | 12 ++- src/node_i18n.cc | 132 +++++++++++++++++++++++++++++ test/parallel/test-icu-punycode.js | 72 ++++++++++++++++ tools/icu/icu-generic.gyp | 9 +- tools/icu/icu_small.json | 3 +- 6 files changed, 291 insertions(+), 12 deletions(-) create mode 100644 benchmark/net/punycode.js create mode 100644 test/parallel/test-icu-punycode.js diff --git a/benchmark/net/punycode.js b/benchmark/net/punycode.js new file mode 100644 index 00000000000000..f4d22557ac5d65 --- /dev/null +++ b/benchmark/net/punycode.js @@ -0,0 +1,75 @@ +'use strict'; + +const common = require('../common.js'); +const icu = process.binding('icu'); +const punycode = require('punycode'); + +const bench = common.createBenchmark(main, { + method: ['punycode', 'icu'], + n: [1024], + val: [ + 'افغانستا.icom.museum', + 'الجزائر.icom.museum', + 'österreich.icom.museum', + 'বাংলাদেশ.icom.museum', + 'беларусь.icom.museum', + 'belgië.icom.museum', + 'българия.icom.museum', + 'تشادر.icom.museum', + '中国.icom.museum', + 'القمر.icom.museum', + 'κυπρος.icom.museum', + 'českárepublika.icom.museum', + 'مصر.icom.museum', + 'ελλάδα.icom.museum', + 'magyarország.icom.museum', + 'ísland.icom.museum', + 'भारत.icom.museum', + 'ايران.icom.museum', + 'éire.icom.museum', + 'איקו״ם.ישראל.museum', + '日本.icom.museum', + 'الأردن.icom.museum' + ] +}); + +function usingPunycode(val) { + punycode.toUnicode(punycode.toASCII(val)); +} + +function usingICU(val) { + icu.toUnicode(icu.toASCII(val)); +} + +function runPunycode(n, val) { + common.v8ForceOptimization(usingPunycode, val); + var i = 0; + bench.start(); + for (; i < n; i++) + usingPunycode(val); + bench.end(n); +} + +function runICU(n, val) { + common.v8ForceOptimization(usingICU, val); + var i = 0; + bench.start(); + for (; i < n; i++) + usingICU(val); + bench.end(n); +} + +function main(conf) { + const n = +conf.n; + const val = conf.val; + switch (conf.method) { + case 'punycode': + runPunycode(n, val); + break; + case 'icu': + runICU(n, val); + break; + default: + throw new Error('Unexpected method'); + } +} diff --git a/lib/url.js b/lib/url.js index c4d6ed2e33a5f5..4a2a879bf34890 100644 --- a/lib/url.js +++ b/lib/url.js @@ -1,6 +1,14 @@ 'use strict'; -const punycode = require('punycode'); +function importPunycode() { + try { + return process.binding('icu'); + } catch (e) { + return require('punycode'); + } +} + +const { toASCII } = importPunycode(); exports.parse = urlParse; exports.resolve = urlResolve; @@ -309,7 +317,7 @@ Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) { // It only converts parts of the domain name that // have non-ASCII characters, i.e. it doesn't matter if // you call it with a domain that already is ASCII-only. - this.hostname = punycode.toASCII(this.hostname); + this.hostname = toASCII(this.hostname); } var p = this.port ? ':' + this.port : ''; diff --git a/src/node_i18n.cc b/src/node_i18n.cc index 3e5b3a91298988..0f3b9b76e6959e 100644 --- a/src/node_i18n.cc +++ b/src/node_i18n.cc @@ -23,8 +23,16 @@ #if defined(NODE_HAVE_I18N_SUPPORT) +#include "node.h" +#include "env.h" +#include "env-inl.h" +#include "util.h" +#include "util-inl.h" +#include "v8.h" + #include <unicode/putil.h> #include <unicode/udata.h> +#include <unicode/uidna.h> #ifdef NODE_HAVE_SMALL_ICU /* if this is defined, we have a 'secondary' entry point. @@ -43,6 +51,13 @@ extern "C" const char U_DATA_API SMALL_ICUDATA_ENTRY_POINT[]; namespace node { +using v8::Context; +using v8::FunctionCallbackInfo; +using v8::Local; +using v8::Object; +using v8::String; +using v8::Value; + bool flag_icu_data_dir = false; namespace i18n { @@ -64,7 +79,124 @@ bool InitializeICUDirectory(const char* icu_data_path) { } } +static int32_t ToUnicode(MaybeStackBuffer<char>* buf, + const char* input, + size_t length) { + UErrorCode status = U_ZERO_ERROR; + uint32_t options = UIDNA_DEFAULT; + options |= UIDNA_NONTRANSITIONAL_TO_UNICODE; + UIDNA* uidna = uidna_openUTS46(options, &status); + if (U_FAILURE(status)) + return -1; + UIDNAInfo info = UIDNA_INFO_INITIALIZER; + + int32_t len = uidna_nameToUnicodeUTF8(uidna, + input, length, + **buf, buf->length(), + &info, + &status); + + if (status == U_BUFFER_OVERFLOW_ERROR) { + status = U_ZERO_ERROR; + buf->AllocateSufficientStorage(len); + len = uidna_nameToUnicodeUTF8(uidna, + input, length, + **buf, buf->length(), + &info, + &status); + } + + if (U_FAILURE(status)) + len = -1; + + uidna_close(uidna); + return len; +} + +static int32_t ToASCII(MaybeStackBuffer<char>* buf, + const char* input, + size_t length) { + UErrorCode status = U_ZERO_ERROR; + uint32_t options = UIDNA_DEFAULT; + options |= UIDNA_NONTRANSITIONAL_TO_ASCII; + UIDNA* uidna = uidna_openUTS46(options, &status); + if (U_FAILURE(status)) + return -1; + UIDNAInfo info = UIDNA_INFO_INITIALIZER; + + int32_t len = uidna_nameToASCII_UTF8(uidna, + input, length, + **buf, buf->length(), + &info, + &status); + + if (status == U_BUFFER_OVERFLOW_ERROR) { + status = U_ZERO_ERROR; + buf->AllocateSufficientStorage(len); + len = uidna_nameToASCII_UTF8(uidna, + input, length, + **buf, buf->length(), + &info, + &status); + } + + if (U_FAILURE(status)) + len = -1; + + uidna_close(uidna); + return len; +} + +static void ToUnicode(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args); + CHECK_GE(args.Length(), 1); + CHECK(args[0]->IsString()); + Utf8Value val(env->isolate(), args[0]); + MaybeStackBuffer<char> buf; + int32_t len = ToUnicode(&buf, *val, val.length()); + + if (len < 0) { + return env->ThrowError("Cannot convert name to Unicode"); + } + + args.GetReturnValue().Set( + String::NewFromUtf8(env->isolate(), + *buf, + v8::NewStringType::kNormal, + len).ToLocalChecked()); +} + +static void ToASCII(const FunctionCallbackInfo<Value>& args) { + Environment* env = Environment::GetCurrent(args); + CHECK_GE(args.Length(), 1); + CHECK(args[0]->IsString()); + Utf8Value val(env->isolate(), args[0]); + MaybeStackBuffer<char> buf; + int32_t len = ToASCII(&buf, *val, val.length()); + + if (len < 0) { + return env->ThrowError("Cannot convert name to ASCII"); + } + + args.GetReturnValue().Set( + String::NewFromUtf8(env->isolate(), + *buf, + v8::NewStringType::kNormal, + len).ToLocalChecked()); +} + +void Init(Local<Object> target, + Local<Value> unused, + Local<Context> context, + void* priv) { + Environment* env = Environment::GetCurrent(context); + env->SetMethod(target, "toUnicode", ToUnicode); + env->SetMethod(target, "toASCII", ToASCII); +} + } // namespace i18n } // namespace node +NODE_MODULE_CONTEXT_AWARE_BUILTIN(icu, node::i18n::Init) + #endif // NODE_HAVE_I18N_SUPPORT diff --git a/test/parallel/test-icu-punycode.js b/test/parallel/test-icu-punycode.js new file mode 100644 index 00000000000000..d9b36e7df78882 --- /dev/null +++ b/test/parallel/test-icu-punycode.js @@ -0,0 +1,72 @@ +'use strict'; + +const common = require('../common'); +const icu = getPunycode(); +const assert = require('assert'); + +function getPunycode() { + try { + return process.binding('icu'); + } catch (err) { + return undefined; + } +} + +if (!icu) { + common.skip('icu punycode tests because ICU is not present.'); + return; +} + +// Credit for list: http://www.i18nguy.com/markup/idna-examples.html +const tests = [ + 'افغانستا.icom.museum', + 'الجزائر.icom.museum', + 'österreich.icom.museum', + 'বাংলাদেশ.icom.museum', + 'беларусь.icom.museum', + 'belgië.icom.museum', + 'българия.icom.museum', + 'تشادر.icom.museum', + '中国.icom.museum', + 'القمر.icom.museum', + 'κυπρος.icom.museum', + 'českárepublika.icom.museum', + 'مصر.icom.museum', + 'ελλάδα.icom.museum', + 'magyarország.icom.museum', + 'ísland.icom.museum', + 'भारत.icom.museum', + 'ايران.icom.museum', + 'éire.icom.museum', + 'איקו״ם.ישראל.museum', + '日本.icom.museum', + 'الأردن.icom.museum', + 'қазақстан.icom.museum', + '한국.icom.museum', + 'кыргызстан.icom.museum', + 'ລາວ.icom.museum', + 'لبنان.icom.museum', + 'македонија.icom.museum', + 'موريتانيا.icom.museum', + 'méxico.icom.museum', + 'монголулс.icom.museum', + 'المغرب.icom.museum', + 'नेपाल.icom.museum', + 'عمان.icom.museum', + 'قطر.icom.museum', + 'românia.icom.museum', + 'россия.иком.museum', + 'србијаицрнагора.иком.museum', + 'இலங்கை.icom.museum', + 'españa.icom.museum', + 'ไทย.icom.museum', + 'تونس.icom.museum', + 'türkiye.icom.museum', + 'украина.icom.museum', + 'việtnam.icom.museum' +]; + +// Testing the roundtrip +tests.forEach((i) => { + assert.strictEqual(i, icu.toUnicode(icu.toASCII(i))); +}); diff --git a/tools/icu/icu-generic.gyp b/tools/icu/icu-generic.gyp index a61b294141fc21..9d466ac39227c3 100644 --- a/tools/icu/icu-generic.gyp +++ b/tools/icu/icu-generic.gyp @@ -37,8 +37,7 @@ 'defines': [ # ICU cannot swap the initial data without this. # http://bugs.icu-project.org/trac/ticket/11046 - 'UCONFIG_NO_LEGACY_CONVERSION=1', - 'UCONFIG_NO_IDNA=1', + 'UCONFIG_NO_LEGACY_CONVERSION=1' ], }], ], @@ -428,9 +427,6 @@ #'<(icu_path)/source/common/ubidi_props_data.h', # and the callers '<(icu_path)/source/common/ushape.cpp', - '<(icu_path)/source/common/usprep.cpp', - '<(icu_path)/source/common/uts46.cpp', - '<(icu_path)/source/common/uidna.cpp', ]}], [ 'icu_ver_major == 57', { 'sources!': [ # work around http://bugs.icu-project.org/trac/ticket/12451 @@ -447,9 +443,6 @@ #'<(icu_path)/source/common/ubidi_props_data.h', # and the callers '<(icu_path)/source/common/ushape.cpp', - '<(icu_path)/source/common/usprep.cpp', - '<(icu_path)/source/common/uts46.cpp', - '<(icu_path)/source/common/uidna.cpp', ]}], [ 'OS == "solaris"', { 'defines': [ '_XOPEN_SOURCE_EXTENDED=0', diff --git a/tools/icu/icu_small.json b/tools/icu/icu_small.json index e434794e91c16b..de26e2cbb14b16 100644 --- a/tools/icu/icu_small.json +++ b/tools/icu/icu_small.json @@ -24,7 +24,7 @@ "region": "none", "zone": "locales", "converters": "none", - "stringprep": "none", + "stringprep": "locales", "translit": "none", "brkfiles": "none", "brkdict": "none", @@ -34,7 +34,6 @@ "remove": [ "cnvalias.icu", "postalCodeData.res", - "uts46.nrm", "genderList.res", "brkitr/root.res", "unames.icu" From 8d18aed59e87924f7e7826968fdfbee40b1f4e6f Mon Sep 17 00:00:00 2001 From: Daniel Bevenius <daniel.bevenius@gmail.com> Date: Wed, 29 Jun 2016 20:38:40 +0200 Subject: [PATCH 118/131] doc: fixing minor typo in AtExit hooks section PR-URL: https://github.com/nodejs/node/pull/7485 Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> --- doc/api/addons.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/addons.md b/doc/api/addons.md index 3dacf88064b858..1187eafdc33a63 100644 --- a/doc/api/addons.md +++ b/doc/api/addons.md @@ -1019,7 +1019,7 @@ console.log(result); // 30 ### AtExit hooks An "AtExit" hook is a function that is invoked after the Node.js event loop -has ended by before the JavaScript VM is terminated and Node.js shuts down. +has ended but before the JavaScript VM is terminated and Node.js shuts down. "AtExit" hooks are registered using the `node::AtExit` API. #### void AtExit(callback, args) From 6151544751795d9dba9c04daab1aefbfd1f7a965 Mon Sep 17 00:00:00 2001 From: Anna Henningsen <anna@addaleax.net> Date: Fri, 24 Jun 2016 05:50:00 +0200 Subject: [PATCH 119/131] vm: don't print out arrow message for custom error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In `AppendExceptionLine()`, which is used both by the `vm` module and the uncaught exception handler, don’t print anything to stderr when called from the `vm` module, even if the thrown object is not a native error instance. Fixes: https://github.com/nodejs/node/issues/7397 PR-URL: https://github.com/nodejs/node/pull/7398 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- src/node.cc | 33 +++++++++++-------- src/node_contextify.cc | 2 +- src/node_internals.h | 4 ++- .../message/vm_caught_custom_runtime_error.js | 18 ++++++++++ .../vm_caught_custom_runtime_error.out | 3 ++ 5 files changed, 45 insertions(+), 15 deletions(-) create mode 100644 test/message/vm_caught_custom_runtime_error.js create mode 100644 test/message/vm_caught_custom_runtime_error.out diff --git a/src/node.cc b/src/node.cc index c9485af1716e54..e984a3b7b209ee 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1514,7 +1514,8 @@ bool IsExceptionDecorated(Environment* env, Local<Value> er) { void AppendExceptionLine(Environment* env, Local<Value> er, - Local<Message> message) { + Local<Message> message, + enum ErrorHandlingMode mode) { if (message.IsEmpty()) return; @@ -1601,20 +1602,26 @@ void AppendExceptionLine(Environment* env, Local<String> arrow_str = String::NewFromUtf8(env->isolate(), arrow); - if (!arrow_str.IsEmpty() && !err_obj.IsEmpty() && err_obj->IsNativeError()) { - err_obj->SetPrivate( - env->context(), - env->arrow_message_private_symbol(), - arrow_str); + const bool can_set_arrow = !arrow_str.IsEmpty() && !err_obj.IsEmpty(); + // If allocating arrow_str failed, print it out. There's not much else to do. + // If it's not an error, but something needs to be printed out because + // it's a fatal exception, also print it out from here. + // Otherwise, the arrow property will be attached to the object and handled + // by the caller. + if (!can_set_arrow || (mode == FATAL_ERROR && !err_obj->IsNativeError())) { + if (env->printed_error()) + return; + env->set_printed_error(true); + + uv_tty_reset_mode(); + PrintErrorString("\n%s", arrow); return; } - // Allocation failed, just print it out. - if (env->printed_error()) - return; - env->set_printed_error(true); - uv_tty_reset_mode(); - PrintErrorString("\n%s", arrow); + CHECK(err_obj->SetPrivate( + env->context(), + env->arrow_message_private_symbol(), + arrow_str).FromMaybe(false)); } @@ -1623,7 +1630,7 @@ static void ReportException(Environment* env, Local<Message> message) { HandleScope scope(env->isolate()); - AppendExceptionLine(env, er, message); + AppendExceptionLine(env, er, message, FATAL_ERROR); Local<Value> trace_value; Local<Value> arrow; diff --git a/src/node_contextify.cc b/src/node_contextify.cc index 07fe998bd4d375..7ec03c10f7743a 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -633,7 +633,7 @@ class ContextifyScript : public BaseObject { if (IsExceptionDecorated(env, err_obj)) return; - AppendExceptionLine(env, exception, try_catch.Message()); + AppendExceptionLine(env, exception, try_catch.Message(), CONTEXTIFY_ERROR); Local<Value> stack = err_obj->Get(env->stack_string()); auto maybe_value = err_obj->GetPrivate( diff --git a/src/node_internals.h b/src/node_internals.h index 7d0a37e5d08fc9..d9c2baa7cd67d7 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -142,9 +142,11 @@ constexpr size_t arraysize(const T(&)[N]) { return N; } bool IsExceptionDecorated(Environment* env, v8::Local<v8::Value> er); +enum ErrorHandlingMode { FATAL_ERROR, CONTEXTIFY_ERROR }; void AppendExceptionLine(Environment* env, v8::Local<v8::Value> er, - v8::Local<v8::Message> message); + v8::Local<v8::Message> message, + enum ErrorHandlingMode mode); NO_RETURN void FatalError(const char* location, const char* message); diff --git a/test/message/vm_caught_custom_runtime_error.js b/test/message/vm_caught_custom_runtime_error.js new file mode 100644 index 00000000000000..237e8e3a105436 --- /dev/null +++ b/test/message/vm_caught_custom_runtime_error.js @@ -0,0 +1,18 @@ +'use strict'; +require('../common'); +const vm = require('vm'); + +console.error('beginning'); + +// Regression test for https://github.com/nodejs/node/issues/7397: +// vm.runInThisContext() should not print out anything to stderr by itself. +try { + vm.runInThisContext(`throw ({ + name: 'MyCustomError', + message: 'This is a custom message' + })`, { filename: 'test.vm' }); +} catch (e) { + console.error('received error', e.name); +} + +console.error('end'); diff --git a/test/message/vm_caught_custom_runtime_error.out b/test/message/vm_caught_custom_runtime_error.out new file mode 100644 index 00000000000000..9aa1e6c6480e3b --- /dev/null +++ b/test/message/vm_caught_custom_runtime_error.out @@ -0,0 +1,3 @@ +beginning +received error MyCustomError +end From 1bd6a623a057fd43729a14ecf54ff49bea23f023 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Wed, 29 Jun 2016 21:11:14 +0200 Subject: [PATCH 120/131] build: drop unconditional openssl dep from cctest Don't link in openssl when building `./configure --without-inspector`, it's only used by the inspector cctests. Ditto libuv and http_parser. Fixes unnecessarily building openssl when `--shared-openssl` is also passed to configure. Fixes: https://github.com/nodejs/node/issues/7478 PR-URL: https://github.com/nodejs/node/pull/7486 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- node.gyp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/node.gyp b/node.gyp index 06496b13b056ec..b2a5a1e4a0e6c9 100644 --- a/node.gyp +++ b/node.gyp @@ -753,12 +753,7 @@ { 'target_name': 'cctest', 'type': 'executable', - 'dependencies': [ - 'deps/openssl/openssl.gyp:openssl', - 'deps/http_parser/http_parser.gyp:http_parser', - 'deps/gtest/gtest.gyp:gtest', - 'deps/uv/uv.gyp:libuv', - ], + 'dependencies': [ 'deps/gtest/gtest.gyp:gtest' ], 'include_dirs': [ 'src', 'deps/v8/include' From 4891001d7edff603ab79e1aa736c1d9a64006b3e Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Sat, 10 Oct 2015 15:01:49 +0200 Subject: [PATCH 121/131] debugger: make listen address configurable `--debug=1.2.3.4:5678` and `--debug=example.com:5678` are now accepted, likewise the `--debug-brk` and `--debug-port` switch. The latter is now something of a misnomer but it's undocumented and for internal use only so it shouldn't matter too much. `--inspect=1.2.3.4:5678` and `--inspect=example.com:5678` are also accepted but don't use the host name yet; they still bind to the default address. Fixes: https://github.com/nodejs/node/issues/3306 PR-URL: https://github.com/nodejs/node/pull/3316 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Trevor Norris <trev.norris@gmail.com> --- lib/_debug_agent.js | 7 +- src/debug-agent.cc | 8 +- src/debug-agent.h | 4 +- src/node.cc | 82 ++++++++++++------- .../test-cluster-disconnect-handles.js | 7 +- test/parallel/test-debug-port-cluster.js | 3 +- test/parallel/test-debug-port-from-cmdline.js | 5 +- test/parallel/test-debug-port-numbers.js | 5 +- test/parallel/test-debug-signal-cluster.js | 8 +- test/sequential/test-debug-host-port.js | 47 +++++++++++ 10 files changed, 128 insertions(+), 48 deletions(-) create mode 100644 test/sequential/test-debug-host-port.js diff --git a/lib/_debug_agent.js b/lib/_debug_agent.js index cb4684f0eddfac..03d3c1c472ec09 100644 --- a/lib/_debug_agent.js +++ b/lib/_debug_agent.js @@ -18,9 +18,10 @@ exports.start = function start() { process._rawDebug(err.stack || err); }); - agent.listen(process._debugAPI.port, function() { - var addr = this.address(); - process._rawDebug('Debugger listening on port %d', addr.port); + agent.listen(process._debugAPI.port, process._debugAPI.host, function() { + const addr = this.address(); + const host = net.isIPv6(addr.address) ? `[${addr.address}]` : addr.address; + process._rawDebug('Debugger listening on %s:%d', host, addr.port); process._debugAPI.notifyListen(); }); diff --git a/src/debug-agent.cc b/src/debug-agent.cc index a67ce96811e050..ec6ebb7ad634f6 100644 --- a/src/debug-agent.cc +++ b/src/debug-agent.cc @@ -44,6 +44,7 @@ using v8::Integer; using v8::Isolate; using v8::Local; using v8::Locker; +using v8::NewStringType; using v8::Object; using v8::String; using v8::Value; @@ -69,7 +70,7 @@ Agent::~Agent() { } -bool Agent::Start(int port, bool wait) { +bool Agent::Start(const std::string& host, int port, bool wait) { int err; if (state_ == kRunning) @@ -85,6 +86,7 @@ bool Agent::Start(int port, bool wait) { goto async_init_failed; uv_unref(reinterpret_cast<uv_handle_t*>(&child_signal_)); + host_ = host; port_ = port; wait_ = wait; @@ -213,6 +215,10 @@ void Agent::InitAdaptor(Environment* env) { t->GetFunction()->NewInstance(env->context()).ToLocalChecked(); api->SetAlignedPointerInInternalField(0, this); + api->Set(String::NewFromUtf8(isolate, "host", + NewStringType::kNormal).ToLocalChecked(), + String::NewFromUtf8(isolate, host_.data(), NewStringType::kNormal, + host_.size()).ToLocalChecked()); api->Set(String::NewFromUtf8(isolate, "port"), Integer::New(isolate, port_)); env->process_object()->Set(String::NewFromUtf8(isolate, "_debugAPI"), api); diff --git a/src/debug-agent.h b/src/debug-agent.h index 45aa07bf2436b7..504212653bc635 100644 --- a/src/debug-agent.h +++ b/src/debug-agent.h @@ -32,6 +32,7 @@ #include "v8-debug.h" #include <string.h> +#include <string> // Forward declaration to break recursive dependency chain with src/env.h. namespace node { @@ -75,7 +76,7 @@ class Agent { typedef void (*DispatchHandler)(node::Environment* env); // Start the debugger agent thread - bool Start(int port, bool wait); + bool Start(const std::string& host, int port, bool wait); // Listen for debug events void Enable(); // Stop the debugger agent @@ -114,6 +115,7 @@ class Agent { State state_; + std::string host_; int port_; bool wait_; diff --git a/src/node.cc b/src/node.cc index e984a3b7b209ee..0c261bd56cb154 100644 --- a/src/node.cc +++ b/src/node.cc @@ -58,6 +58,8 @@ #include <stdlib.h> #include <string.h> #include <sys/types.h> + +#include <string> #include <vector> #if defined(NODE_HAVE_I18N_SUPPORT) @@ -141,10 +143,14 @@ static unsigned int preload_module_count = 0; static const char** preload_modules = nullptr; #if HAVE_INSPECTOR static bool use_inspector = false; +#else +static const bool use_inspector = false; #endif static bool use_debug_agent = false; static bool debug_wait_connect = false; +static std::string debug_host; // NOLINT(runtime/string) static int debug_port = 5858; +static std::string inspector_host; // NOLINT(runtime/string) static int inspector_port = 9229; static const int v8_default_thread_pool_size = 4; static int v8_thread_pool_size = v8_default_thread_pool_size; @@ -203,23 +209,20 @@ static struct { platform_ = nullptr; } -#if HAVE_INSPECTOR void StartInspector(Environment *env, int port, bool wait) { +#if HAVE_INSPECTOR env->inspector_agent()->Start(platform_, port, wait); - } #endif // HAVE_INSPECTOR + } v8::Platform* platform_; #else // !NODE_USE_V8_PLATFORM void Initialize(int thread_pool_size) {} void PumpMessageLoop(Isolate* isolate) {} void Dispose() {} -#if HAVE_INSPECTOR void StartInspector(Environment *env, int port, bool wait) { env->ThrowError("Node compiled with NODE_USE_V8_PLATFORM=0"); } -#endif // HAVE_INSPECTOR - #endif // !NODE_USE_V8_PLATFORM } v8_platform; @@ -3514,6 +3517,7 @@ static bool ParseDebugOpt(const char* arg) { debug_wait_connect = true; port = arg + sizeof("--debug-brk=") - 1; } else if (!strncmp(arg, "--debug-port=", sizeof("--debug-port=") - 1)) { + // XXX(bnoordhuis) Misnomer, configures port and listen address. port = arg + sizeof("--debug-port=") - 1; #if HAVE_INSPECTOR // Specifying both --inspect and --debug means debugging is on, using Chromium @@ -3535,24 +3539,49 @@ static bool ParseDebugOpt(const char* arg) { return false; } - if (port != nullptr) { - int port_int = atoi(port); - if (port_int < 1024 || port_int > 65535) { - fprintf(stderr, "Debug port must be in range 1024 to 65535.\n"); - PrintHelp(); - exit(12); - } -#if HAVE_INSPECTOR - if (use_inspector) { - inspector_port = port_int; - } else { -#endif - debug_port = port_int; -#if HAVE_INSPECTOR + if (port == nullptr) { + return true; + } + + std::string* const the_host = use_inspector ? &inspector_host : &debug_host; + int* const the_port = use_inspector ? &inspector_port : &debug_port; + + // FIXME(bnoordhuis) Move IPv6 address parsing logic to lib/net.js. + // It seems reasonable to support [address]:port notation + // in net.Server#listen() and net.Socket#connect(). + const size_t port_len = strlen(port); + if (port[0] == '[' && port[port_len - 1] == ']') { + the_host->assign(port + 1, port_len - 2); + return true; + } + + const char* const colon = strrchr(port, ':'); + if (colon == nullptr) { + // Either a port number or a host name. Assume that + // if it's not all decimal digits, it's a host name. + for (size_t n = 0; port[n] != '\0'; n += 1) { + if (port[n] < '0' || port[n] > '9') { + *the_host = port; + return true; + } } -#endif + } else { + const bool skip = (colon > port && port[0] == '[' && colon[-1] == ']'); + the_host->assign(port + skip, colon - skip); + } + + char* endptr; + errno = 0; + const char* const digits = colon != nullptr ? colon + 1 : port; + const long result = strtol(digits, &endptr, 10); // NOLINT(runtime/int) + if (errno != 0 || *endptr != '\0' || result < 1024 || result > 65535) { + fprintf(stderr, "Debug port must be in range 1024 to 65535.\n"); + PrintHelp(); + exit(12); } + *the_port = static_cast<int>(result); + return true; } @@ -3810,34 +3839,31 @@ static void DispatchMessagesDebugAgentCallback(Environment* env) { static void StartDebug(Environment* env, bool wait) { CHECK(!debugger_running); -#if HAVE_INSPECTOR if (use_inspector) { v8_platform.StartInspector(env, inspector_port, wait); debugger_running = true; } else { -#endif env->debugger_agent()->set_dispatch_handler( DispatchMessagesDebugAgentCallback); - debugger_running = env->debugger_agent()->Start(debug_port, wait); + debugger_running = + env->debugger_agent()->Start(debug_host, debug_port, wait); if (debugger_running == false) { - fprintf(stderr, "Starting debugger on port %d failed\n", debug_port); + fprintf(stderr, "Starting debugger on %s:%d failed\n", + debug_host.c_str(), debug_port); fflush(stderr); return; } -#if HAVE_INSPECTOR } -#endif } // Called from the main thread. static void EnableDebug(Environment* env) { CHECK(debugger_running); -#if HAVE_INSPECTOR + if (use_inspector) { return; } -#endif // Send message to enable debug in workers HandleScope handle_scope(env->isolate()); diff --git a/test/parallel/test-cluster-disconnect-handles.js b/test/parallel/test-cluster-disconnect-handles.js index e05733dbd9e953..49b8218ab9c8d3 100644 --- a/test/parallel/test-cluster-disconnect-handles.js +++ b/test/parallel/test-cluster-disconnect-handles.js @@ -25,11 +25,8 @@ cluster.schedulingPolicy = cluster.SCHED_RR; if (cluster.isMaster) { let isKilling = false; const handles = require('internal/cluster').handles; - // FIXME(bnoordhuis) lib/cluster.js scans the execArgv arguments for - // debugger flags and renumbers any port numbers it sees starting - // from the default port 5858. Add a '.' that circumvents the - // scanner but is ignored by atoi(3). Heinous hack. - cluster.setupMaster({ execArgv: [`--debug=${common.PORT}.`] }); + const address = common.hasIPv6 ? '[::1]' : common.localhostIPv4; + cluster.setupMaster({ execArgv: [`--debug=${address}:${common.PORT}`] }); const worker = cluster.fork(); worker.once('exit', common.mustCall((code, signal) => { assert.strictEqual(code, 0, 'worker did not exit normally'); diff --git a/test/parallel/test-debug-port-cluster.js b/test/parallel/test-debug-port-cluster.js index cc564b3ac1522c..3410aed2b67d27 100644 --- a/test/parallel/test-debug-port-cluster.js +++ b/test/parallel/test-debug-port-cluster.js @@ -16,7 +16,8 @@ child.stderr.setEncoding('utf8'); const checkMessages = common.mustCall(() => { for (let port = PORT_MIN; port <= PORT_MAX; port += 1) { - assert(stderr.includes(`Debugger listening on port ${port}`)); + const re = RegExp(`Debugger listening on (\\[::\\]|0\\.0\\.0\\.0):${port}`); + assert(re.test(stderr)); } }); diff --git a/test/parallel/test-debug-port-from-cmdline.js b/test/parallel/test-debug-port-from-cmdline.js index 71ed71bd63af65..527d72ee74a75f 100644 --- a/test/parallel/test-debug-port-from-cmdline.js +++ b/test/parallel/test-debug-port-from-cmdline.js @@ -39,11 +39,10 @@ function processStderrLine(line) { function assertOutputLines() { var expectedLines = [ 'Starting debugger agent.', - 'Debugger listening on port ' + debugPort + 'Debugger listening on (\\[::\\]|0\\.0\\.0\\.0):' + debugPort, ]; assert.equal(outputLines.length, expectedLines.length); for (var i = 0; i < expectedLines.length; i++) - assert.equal(outputLines[i], expectedLines[i]); - + assert(RegExp(expectedLines[i]).test(outputLines[i])); } diff --git a/test/parallel/test-debug-port-numbers.js b/test/parallel/test-debug-port-numbers.js index 33cdf6035b449a..683287340c6f8d 100644 --- a/test/parallel/test-debug-port-numbers.js +++ b/test/parallel/test-debug-port-numbers.js @@ -52,8 +52,9 @@ function kill(child) { process.on('exit', function() { for (const child of children) { - const one = RegExp(`Debugger listening on port ${child.test.port}`); - const two = RegExp(`connecting to 127.0.0.1:${child.test.port}`); + const port = child.test.port; + const one = RegExp(`Debugger listening on (\\[::\\]|0\.0\.0\.0):${port}`); + const two = RegExp(`connecting to 127.0.0.1:${port}`); assert(one.test(child.test.stdout)); assert(two.test(child.test.stdout)); } diff --git a/test/parallel/test-debug-signal-cluster.js b/test/parallel/test-debug-signal-cluster.js index e51cd9a50ab29f..c78bab9d489212 100644 --- a/test/parallel/test-debug-signal-cluster.js +++ b/test/parallel/test-debug-signal-cluster.js @@ -63,11 +63,11 @@ process.on('exit', function onExit() { var expectedLines = [ 'Starting debugger agent.', - 'Debugger listening on port ' + (port + 0), + 'Debugger listening on (\\[::\\]|0\\.0\\.0\\.0):' + (port + 0), 'Starting debugger agent.', - 'Debugger listening on port ' + (port + 1), + 'Debugger listening on (\\[::\\]|0\\.0\\.0\\.0):' + (port + 1), 'Starting debugger agent.', - 'Debugger listening on port ' + (port + 2), + 'Debugger listening on (\\[::\\]|0\\.0\\.0\\.0):' + (port + 2), ]; function assertOutputLines() { @@ -79,5 +79,5 @@ function assertOutputLines() { assert.equal(outputLines.length, expectedLines.length); for (var i = 0; i < expectedLines.length; i++) - assert.equal(outputLines[i], expectedLines[i]); + assert(RegExp(expectedLines[i]).test(outputLines[i])); } diff --git a/test/sequential/test-debug-host-port.js b/test/sequential/test-debug-host-port.js new file mode 100644 index 00000000000000..be6a0837f920b8 --- /dev/null +++ b/test/sequential/test-debug-host-port.js @@ -0,0 +1,47 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; + +let run = () => {}; +function test(args, re) { + const next = run; + run = () => { + const options = {encoding: 'utf8'}; + const proc = spawn(process.execPath, args.concat(['-e', '0']), options); + let stderr = ''; + proc.stderr.setEncoding('utf8'); + proc.stderr.on('data', (data) => { + stderr += data; + if (re.test(stderr)) proc.kill(); + }); + proc.on('exit', common.mustCall(() => { + assert(re.test(stderr)); + next(); + })); + }; +} + +test(['--debug-brk'], /Debugger listening on (\[::\]|0\.0\.0\.0):5858/); +test(['--debug-brk=1234'], /Debugger listening on (\[::\]|0\.0\.0\.0):1234/); +test(['--debug-brk=127.0.0.1'], /Debugger listening on 127\.0\.0\.1:5858/); +test(['--debug-brk=127.0.0.1:1234'], /Debugger listening on 127\.0\.0\.1:1234/); +test(['--debug-brk=localhost'], + /Debugger listening on (\[::\]|127\.0\.0\.1):5858/); +test(['--debug-brk=localhost:1234'], + /Debugger listening on (\[::\]|127\.0\.0\.1):1234/); + +if (common.hasIPv6) { + test(['--debug-brk=::'], /Debug port must be in range 1024 to 65535/); + test(['--debug-brk=::0'], /Debug port must be in range 1024 to 65535/); + test(['--debug-brk=::1'], /Debug port must be in range 1024 to 65535/); + test(['--debug-brk=[::]'], /Debugger listening on \[::\]:5858/); + test(['--debug-brk=[::0]'], /Debugger listening on \[::\]:5858/); + test(['--debug-brk=[::]:1234'], /Debugger listening on \[::\]:1234/); + test(['--debug-brk=[::0]:1234'], /Debugger listening on \[::\]:1234/); + test(['--debug-brk=[::ffff:127.0.0.1]:1234'], + /Debugger listening on \[::ffff:127\.0\.0\.1\]:1234/); +} + +run(); // Runs tests in reverse order. From 7a7b8f7e67de79aa796ae761fbbcd10647348dfb Mon Sep 17 00:00:00 2001 From: Lance Ball <lball@redhat.com> Date: Mon, 14 Mar 2016 16:08:21 -0400 Subject: [PATCH 122/131] repl: Default `useGlobal` to false in CLI REPL. Documentation for REPL states that the default value of `useGlobal` is `false`. It makes no distinction between a REPL that is created programmatically, and the one a user is dropped into on the command line by executing `node` with no arguments. This change ensures that the CLI REPL uses a default value of `false`. Fixes: https://github.com/nodejs/node/issues/5659 Ref: https://github.com/nodejs/node/issues/6802 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> PR-URL: https://github.com/nodejs/node/pull/5703 --- lib/internal/repl.js | 2 +- test/parallel/test-repl-use-global.js | 85 +++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 test/parallel/test-repl-use-global.js diff --git a/lib/internal/repl.js b/lib/internal/repl.js index dd14f42fa5273c..d59b40eb763bdb 100644 --- a/lib/internal/repl.js +++ b/lib/internal/repl.js @@ -22,7 +22,7 @@ function createRepl(env, opts, cb) { opts = opts || { ignoreUndefined: false, terminal: process.stdout.isTTY, - useGlobal: true, + useGlobal: false, breakEvalOnSigint: true }; diff --git a/test/parallel/test-repl-use-global.js b/test/parallel/test-repl-use-global.js new file mode 100644 index 00000000000000..86c646cc767361 --- /dev/null +++ b/test/parallel/test-repl-use-global.js @@ -0,0 +1,85 @@ +'use strict'; + +// Flags: --expose-internals + +const common = require('../common'); +const stream = require('stream'); +const repl = require('internal/repl'); +const assert = require('assert'); + +common.globalCheck = false; + +// Array of [useGlobal, expectedResult] pairs +const globalTestCases = [ + [false, 'undefined'], + [true, '\'tacos\''], + [undefined, 'undefined'] +]; + +const globalTest = (useGlobal, cb, output) => (err, repl) => { + if (err) + return cb(err); + + let str = ''; + output.on('data', (data) => (str += data)); + global.lunch = 'tacos'; + repl.write('global.lunch;\n'); + repl.close(); + delete global.lunch; + cb(null, str.trim()); +}; + +// Test how the global object behaves in each state for useGlobal +for (const [option, expected] of globalTestCases) { + runRepl(option, globalTest, common.mustCall((err, output) => { + assert.ifError(err); + assert.strictEqual(output, expected); + })); +} + +// Test how shadowing the process object via `let` +// behaves in each useGlobal state. Note: we can't +// actually test the state when useGlobal is true, +// because the exception that's generated is caught +// (see below), but errors are printed, and the test +// suite is aware of it, causing a failure to be flagged. +// +const processTestCases = [false, undefined]; +const processTest = (useGlobal, cb, output) => (err, repl) => { + if (err) + return cb(err); + + let str = ''; + output.on('data', (data) => (str += data)); + + // if useGlobal is false, then `let process` should work + repl.write('let process;\n'); + repl.write('21 * 2;\n'); + repl.close(); + cb(null, str.trim()); +}; + +for (const option of processTestCases) { + runRepl(option, processTest, common.mustCall((err, output) => { + assert.ifError(err); + assert.strictEqual(output, 'undefined\n42'); + })); +} + +function runRepl(useGlobal, testFunc, cb) { + const inputStream = new stream.PassThrough(); + const outputStream = new stream.PassThrough(); + const opts = { + input: inputStream, + output: outputStream, + useGlobal: useGlobal, + useColors: false, + terminal: false, + prompt: '' + }; + + repl.createInternalRepl( + process.env, + opts, + testFunc(useGlobal, cb, opts.output)); +} From 4a0fb6fcb893273b73e548f38e97e012f6907166 Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Wed, 22 Jun 2016 16:21:21 -0700 Subject: [PATCH 123/131] Revert "child_process: measure buffer length in bytes" This reverts commit c9a5990a76ccb15872234948e23bdc12691c2e70. PR-URL: https://github.com/nodejs/node/pull/7391 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Jackson Tian <shyvo1987@gmail.com> --- lib/child_process.js | 71 ++++++++----------- .../test-child-process-max-buffer.js} | 3 +- ...t-child-process-exec-stdout-data-string.js | 1 + .../test-child-process-maxBuffer-stderr.js | 15 ---- 4 files changed, 33 insertions(+), 57 deletions(-) rename test/{parallel/test-child-process-maxBuffer-stdout.js => known_issues/test-child-process-max-buffer.js} (75%) rename test/{known_issues => parallel}/test-child-process-exec-stdout-data-string.js (99%) delete mode 100644 test/parallel/test-child-process-maxBuffer-stderr.js diff --git a/lib/child_process.js b/lib/child_process.js index 5eee5228f040d3..0c6af12bc8e81b 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -146,13 +146,15 @@ exports.execFile = function(file /*, args, options, callback*/) { }); var encoding; - var stdoutState; - var stderrState; - var _stdout = []; - var _stderr = []; + var _stdout; + var _stderr; if (options.encoding !== 'buffer' && Buffer.isEncoding(options.encoding)) { encoding = options.encoding; + _stdout = ''; + _stderr = ''; } else { + _stdout = []; + _stderr = []; encoding = null; } var stdoutLen = 0; @@ -174,23 +176,16 @@ exports.execFile = function(file /*, args, options, callback*/) { if (!callback) return; - var stdout = Buffer.concat(_stdout, stdoutLen); - var stderr = Buffer.concat(_stderr, stderrLen); - - var stdoutEncoding = encoding; - var stderrEncoding = encoding; - - if (stdoutState && stdoutState.decoder) - stdoutEncoding = stdoutState.decoder.encoding; - - if (stderrState && stderrState.decoder) - stderrEncoding = stderrState.decoder.encoding; - - if (stdoutEncoding) - stdout = stdout.toString(stdoutEncoding); - - if (stderrEncoding) - stderr = stderr.toString(stderrEncoding); + // merge chunks + var stdout; + var stderr; + if (!encoding) { + stdout = Buffer.concat(_stdout); + stderr = Buffer.concat(_stderr); + } else { + stdout = _stdout; + stderr = _stderr; + } if (ex) { // Will be handled later @@ -250,45 +245,39 @@ exports.execFile = function(file /*, args, options, callback*/) { } if (child.stdout) { - stdoutState = child.stdout._readableState; + if (encoding) + child.stdout.setEncoding(encoding); child.stdout.addListener('data', function(chunk) { - // If `child.stdout.setEncoding()` happened in userland, convert string to - // Buffer. - if (stdoutState.decoder) { - chunk = Buffer.from(chunk, stdoutState.decoder.encoding); - } - - stdoutLen += chunk.byteLength; + stdoutLen += chunk.length; if (stdoutLen > options.maxBuffer) { ex = new Error('stdout maxBuffer exceeded'); - stdoutLen -= chunk.byteLength; kill(); } else { - _stdout.push(chunk); + if (!encoding) + _stdout.push(chunk); + else + _stdout += chunk; } }); } if (child.stderr) { - stderrState = child.stderr._readableState; + if (encoding) + child.stderr.setEncoding(encoding); child.stderr.addListener('data', function(chunk) { - // If `child.stderr.setEncoding()` happened in userland, convert string to - // Buffer. - if (stderrState.decoder) { - chunk = Buffer.from(chunk, stderrState.decoder.encoding); - } - - stderrLen += chunk.byteLength; + stderrLen += chunk.length; if (stderrLen > options.maxBuffer) { ex = new Error('stderr maxBuffer exceeded'); - stderrLen -= chunk.byteLength; kill(); } else { - _stderr.push(chunk); + if (!encoding) + _stderr.push(chunk); + else + _stderr += chunk; } }); } diff --git a/test/parallel/test-child-process-maxBuffer-stdout.js b/test/known_issues/test-child-process-max-buffer.js similarity index 75% rename from test/parallel/test-child-process-maxBuffer-stdout.js rename to test/known_issues/test-child-process-max-buffer.js index 122dc499f462bf..14a344c7062a5a 100644 --- a/test/parallel/test-child-process-maxBuffer-stdout.js +++ b/test/known_issues/test-child-process-max-buffer.js @@ -1,4 +1,5 @@ 'use strict'; +// Refs: https://github.com/nodejs/node/issues/1901 const common = require('../common'); const assert = require('assert'); const cp = require('child_process'); @@ -9,7 +10,7 @@ if (process.argv[2] === 'child') { } else { const cmd = `${process.execPath} ${__filename} child`; - cp.exec(cmd, {maxBuffer: 10}, common.mustCall((err, stdout, stderr) => { + cp.exec(cmd, { maxBuffer: 10 }, common.mustCall((err, stdout, stderr) => { assert.strictEqual(err.message, 'stdout maxBuffer exceeded'); })); } diff --git a/test/known_issues/test-child-process-exec-stdout-data-string.js b/test/parallel/test-child-process-exec-stdout-data-string.js similarity index 99% rename from test/known_issues/test-child-process-exec-stdout-data-string.js rename to test/parallel/test-child-process-exec-stdout-data-string.js index b267ff5e98347a..a6469189570582 100644 --- a/test/known_issues/test-child-process-exec-stdout-data-string.js +++ b/test/parallel/test-child-process-exec-stdout-data-string.js @@ -14,3 +14,4 @@ const command = common.isWindows ? 'dir' : 'ls'; exec(command).stdout.on('data', cb); exec('fhqwhgads').stderr.on('data', cb); + diff --git a/test/parallel/test-child-process-maxBuffer-stderr.js b/test/parallel/test-child-process-maxBuffer-stderr.js deleted file mode 100644 index ecaea8b152a0ca..00000000000000 --- a/test/parallel/test-child-process-maxBuffer-stderr.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; -const common = require('../common'); -const assert = require('assert'); -const cp = require('child_process'); -const unicode = '中文测试'; // Length = 4, Byte length = 13 - -if (process.argv[2] === 'child') { - console.error(unicode); -} else { - const cmd = `${process.execPath} ${__filename} child`; - - cp.exec(cmd, {maxBuffer: 10}, common.mustCall((err, stdout, stderr) => { - assert.strictEqual(err.message, 'stderr maxBuffer exceeded'); - })); -} From 2aa06b9fa000d172a3c58c060d4a06294e7e416a Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Thu, 23 Jun 2016 10:51:48 -0700 Subject: [PATCH 124/131] child_process: preserve argument type A previous fix for a `maxBuffer` bug resulted in a change to the argument type for the `data` event on `child.stdin` and `child.stdout` when using `child_process.exec()`. This fixes the `maxBuffer` bug in a way that does not have that side effect. PR-URL: https://github.com/nodejs/node/pull/7391 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Jackson Tian <shyvo1987@gmail.com> Fixes: https://github.com/nodejs/node/issues/7342 Refs: https://github.com/nodejs/node/issues/1901 --- lib/child_process.js | 24 +++++++------- .../test-child-process-max-buffer.js | 16 ---------- .../test-child-process-exec-maxBuffer.js | 31 +++++++++++++++++++ ...process-exec-stdout-stderr-data-string.js} | 20 +++++++----- test/parallel/test-exec-max-buffer.js | 11 ------- 5 files changed, 56 insertions(+), 46 deletions(-) delete mode 100644 test/known_issues/test-child-process-max-buffer.js create mode 100644 test/parallel/test-child-process-exec-maxBuffer.js rename test/parallel/{test-child-process-exec-stdout-data-string.js => test-child-process-exec-stdout-stderr-data-string.js} (51%) delete mode 100644 test/parallel/test-exec-max-buffer.js diff --git a/lib/child_process.js b/lib/child_process.js index 0c6af12bc8e81b..3736bc49ae61a5 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -179,12 +179,12 @@ exports.execFile = function(file /*, args, options, callback*/) { // merge chunks var stdout; var stderr; - if (!encoding) { - stdout = Buffer.concat(_stdout); - stderr = Buffer.concat(_stderr); - } else { + if (encoding) { stdout = _stdout; stderr = _stderr; + } else { + stdout = Buffer.concat(_stdout); + stderr = Buffer.concat(_stderr); } if (ex) { @@ -249,16 +249,16 @@ exports.execFile = function(file /*, args, options, callback*/) { child.stdout.setEncoding(encoding); child.stdout.addListener('data', function(chunk) { - stdoutLen += chunk.length; + stdoutLen += encoding ? Buffer.byteLength(chunk, encoding) : chunk.length; if (stdoutLen > options.maxBuffer) { ex = new Error('stdout maxBuffer exceeded'); kill(); } else { - if (!encoding) - _stdout.push(chunk); - else + if (encoding) _stdout += chunk; + else + _stdout.push(chunk); } }); } @@ -268,16 +268,16 @@ exports.execFile = function(file /*, args, options, callback*/) { child.stderr.setEncoding(encoding); child.stderr.addListener('data', function(chunk) { - stderrLen += chunk.length; + stderrLen += encoding ? Buffer.byteLength(chunk, encoding) : chunk.length; if (stderrLen > options.maxBuffer) { ex = new Error('stderr maxBuffer exceeded'); kill(); } else { - if (!encoding) - _stderr.push(chunk); - else + if (encoding) _stderr += chunk; + else + _stderr.push(chunk); } }); } diff --git a/test/known_issues/test-child-process-max-buffer.js b/test/known_issues/test-child-process-max-buffer.js deleted file mode 100644 index 14a344c7062a5a..00000000000000 --- a/test/known_issues/test-child-process-max-buffer.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; -// Refs: https://github.com/nodejs/node/issues/1901 -const common = require('../common'); -const assert = require('assert'); -const cp = require('child_process'); -const unicode = '中文测试'; // Length = 4, Byte length = 13 - -if (process.argv[2] === 'child') { - console.log(unicode); -} else { - const cmd = `${process.execPath} ${__filename} child`; - - cp.exec(cmd, { maxBuffer: 10 }, common.mustCall((err, stdout, stderr) => { - assert.strictEqual(err.message, 'stdout maxBuffer exceeded'); - })); -} diff --git a/test/parallel/test-child-process-exec-maxBuffer.js b/test/parallel/test-child-process-exec-maxBuffer.js new file mode 100644 index 00000000000000..714e029d728b1e --- /dev/null +++ b/test/parallel/test-child-process-exec-maxBuffer.js @@ -0,0 +1,31 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); + +function checkFactory(streamName) { + return common.mustCall((err) => { + const message = `${streamName} maxBuffer exceeded`; + assert.strictEqual(err.message, message); + }); +} + +{ + const cmd = 'echo "hello world"'; + + cp.exec(cmd, { maxBuffer: 5 }, checkFactory('stdout')); +} + +const unicode = '中文测试'; // length = 4, byte length = 12 + +{ + const cmd = `"${process.execPath}" -e "console.log('${unicode}');"`; + + cp.exec(cmd, {maxBuffer: 10}, checkFactory('stdout')); +} + +{ + const cmd = `"${process.execPath}" -e "console.('${unicode}');"`; + + cp.exec(cmd, {maxBuffer: 10}, checkFactory('stderr')); +} diff --git a/test/parallel/test-child-process-exec-stdout-data-string.js b/test/parallel/test-child-process-exec-stdout-stderr-data-string.js similarity index 51% rename from test/parallel/test-child-process-exec-stdout-data-string.js rename to test/parallel/test-child-process-exec-stdout-stderr-data-string.js index a6469189570582..8ab834c98f8d79 100644 --- a/test/parallel/test-child-process-exec-stdout-data-string.js +++ b/test/parallel/test-child-process-exec-stdout-stderr-data-string.js @@ -4,14 +4,20 @@ const common = require('../common'); const assert = require('assert'); const exec = require('child_process').exec; -const expectedCalls = 2; - -const cb = common.mustCall((data) => { - assert.strictEqual(typeof data, 'string'); -}, expectedCalls); +var stdoutCalls = 0; +var stderrCalls = 0; const command = common.isWindows ? 'dir' : 'ls'; -exec(command).stdout.on('data', cb); +exec(command).stdout.on('data', (data) => { + stdoutCalls += 1; +}); -exec('fhqwhgads').stderr.on('data', cb); +exec('fhqwhgads').stderr.on('data', (data) => { + assert.strictEqual(typeof data, 'string'); + stderrCalls += 1; +}); +process.on('exit', () => { + assert(stdoutCalls > 0); + assert(stderrCalls > 0); +}); diff --git a/test/parallel/test-exec-max-buffer.js b/test/parallel/test-exec-max-buffer.js deleted file mode 100644 index 15c6e7025978e2..00000000000000 --- a/test/parallel/test-exec-max-buffer.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; -require('../common'); -var exec = require('child_process').exec; -var assert = require('assert'); - -var cmd = 'echo "hello world"'; - -exec(cmd, { maxBuffer: 5 }, function(err, stdout, stderr) { - assert.ok(err); - assert.ok(/maxBuffer/.test(err.message)); -}); From 7d07a0b68e83deb531cb7a89bbf19131b3e4688c Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Tue, 28 Jun 2016 16:58:58 -0700 Subject: [PATCH 125/131] doc: improve usage of `zero`/`0` PR-URL: https://github.com/nodejs/node/pull/7466 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Bryan English <bryan@bryanenglish.com> Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: James M Snell <jasnell@gmail.com> --- doc/api/http.md | 2 +- doc/api/process.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/http.md b/doc/api/http.md index 7accccd92631c9..93d28a4f35d46f 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -699,7 +699,7 @@ added: v0.1.90 Begin accepting connections on the specified `port` and `hostname`. If the `hostname` is omitted, the server will accept connections on any IPv6 address (`::`) when IPv6 is available, or any IPv4 address (`0.0.0.0`) otherwise. Use a -port value of zero to have the operating system assign an available port. +port value of `0` to have the operating system assign an available port. To listen to a unix socket, supply a filename instead of port and hostname. diff --git a/doc/api/process.md b/doc/api/process.md index 34738d76ad3020..e8ec8a69a63ea6 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -183,7 +183,7 @@ code without properly recovering from the exception can cause additional unforeseen and unpredictable issues. Exceptions thrown from within the event handler will not be caught. Instead the -process will exit with a non zero exit code and the stack trace will be printed. +process will exit with a non-zero exit code and the stack trace will be printed. This is to avoid infinite recursion. Attempting to resume normally after an uncaught exception can be similar to From b383fdde791d08840f01453ddc080228fe65a4c0 Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Tue, 28 Jun 2016 17:14:19 -0700 Subject: [PATCH 126/131] test: remove common.PORT from http tests PR-URL: https://github.com/nodejs/node/pull/7467 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- ...t-http-client-keep-alive-release-before-finish.js | 12 +++++++----- test/parallel/test-http-no-read-no-dump.js | 12 +++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/test/parallel/test-http-client-keep-alive-release-before-finish.js b/test/parallel/test-http-client-keep-alive-release-before-finish.js index 374bacba3e5362..0efedc91e2613d 100644 --- a/test/parallel/test-http-client-keep-alive-release-before-finish.js +++ b/test/parallel/test-http-client-keep-alive-release-before-finish.js @@ -4,16 +4,18 @@ const http = require('http'); const server = http.createServer((req, res) => { res.end(); -}).listen(common.PORT, common.mustCall(() => { +}).listen(0, common.mustCall(() => { const agent = new http.Agent({ maxSockets: 1, keepAlive: true }); + const port = server.address().port; + const post = http.request({ - agent: agent, + agent, method: 'POST', - port: common.PORT, + port, }, common.mustCall((res) => { res.resume(); })); @@ -28,9 +30,9 @@ const server = http.createServer((req, res) => { }, 100); http.request({ - agent: agent, + agent, method: 'GET', - port: common.PORT, + port, }, common.mustCall((res) => { server.close(); res.connection.end(); diff --git a/test/parallel/test-http-no-read-no-dump.js b/test/parallel/test-http-no-read-no-dump.js index c509146c0a29d7..17d36c56b2eeba 100644 --- a/test/parallel/test-http-no-read-no-dump.js +++ b/test/parallel/test-http-no-read-no-dump.js @@ -15,16 +15,18 @@ const server = http.createServer((req, res) => { res.end(); onPause(); }); -}).listen(common.PORT, common.mustCall(() => { +}).listen(0, common.mustCall(() => { const agent = new http.Agent({ maxSockets: 1, keepAlive: true }); + const port = server.address().port; + const post = http.request({ - agent: agent, + agent, method: 'POST', - port: common.PORT, + port, }, common.mustCall((res) => { res.resume(); @@ -41,9 +43,9 @@ const server = http.createServer((req, res) => { post.write('initial'); http.request({ - agent: agent, + agent, method: 'GET', - port: common.PORT, + port, }, common.mustCall((res) => { server.close(); res.connection.end(); From 45f83e59c4b507b84bfed315a842b6d90110e1e9 Mon Sep 17 00:00:00 2001 From: Ryan Lewis <ryanharrisonlewis@gmail.com> Date: Tue, 17 May 2016 17:41:22 -0700 Subject: [PATCH 127/131] doc: add guide for Node.js Timers Refs: https://github.com/nodejs/docs/issues/76 PR-URL: https://github.com/nodejs/node/pull/6825 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> --- doc/guides/timers-in-node.md | 192 ++++++++++++++++++ .../the-event-loop-timers-and-nexttick.md | 44 ++-- 2 files changed, 214 insertions(+), 22 deletions(-) create mode 100644 doc/guides/timers-in-node.md diff --git a/doc/guides/timers-in-node.md b/doc/guides/timers-in-node.md new file mode 100644 index 00000000000000..3be7069e4389ae --- /dev/null +++ b/doc/guides/timers-in-node.md @@ -0,0 +1,192 @@ +--- +title: Timers in Node.js +layout: docs.hbs +--- + +# Timers in Node.js and beyond + +The Timers module in Node.js contains functions that execute code after a set +period of time. Timers do not need to be imported via `require()`, since +all the methods are available globally to emulate the browser JavaScript API. +To fully understand when timer functions will be executed, it's a good idea to +read up on the the Node.js +[Event Loop](../topics/the-event-loop-timers-and-nexttick). + +## Controlling the Time Continuum with Node.js + +The Node.js API provides several ways of scheduling code to execute at +some point after the present moment. The functions below may seem familiar, +since they are available in most browsers, but Node.js actually provides +its own implementation of these methods. Timers integrate very closely +with the system, and despite the fact that the API mirrors the browser +API, there are some differences in implementation. + +### "When I say so" Execution ~ *`setTimeout()`* + +`setTimeout()` can be used to schedule code execution after a designated +amount of milliseconds. This function is similar to +[`window.setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout) +from the browser JavaScript API, however a string of code cannot be passed +to be executed. + +`setTimeout()` accepts a function to execute as its first argument and the +millisecond delay defined as a number as the second argument. Additional +arguments may also be included and these will be passed on to the function. Here +is an example of that: + +```js +function myFunc (arg) { + console.log('arg was => ' + arg); +} + +setTimeout(myFunc, 1500, 'funky'); +``` + +The above function `myFunc()` will execute as close to 1500 +milliseconds (or 1.5 seconds) as possible due to the call of `setTimeout()`. + +The timeout interval that is set cannot be relied upon to execute after +that *exact* number of milliseconds. This is because other executing code that +blocks or holds onto the event loop will push the execution of the timeout +back. The *only* guarantee is that the timeout will not execute *sooner* than +the declared timeout interval. + +`setTimeout()` returns a `Timeout` object that can be used to reference the +timeout that was set. This returned object can be used to cancel the timeout ( +see `clearTimeout()` below) as well as change the execution behavior (see +`unref()` below). + +### "Right after this" Execution ~ *`setImmediate()`* + +`setImmediate()` will execute code at the end of the current event loop cycle. +This code will execute *after* any I/O operations in the current event loop and +*before* any timers scheduled for the next event loop. This code execution +could be thought of as happening "right after this", meaning any code following +the `setImmediate()` function call will execute before the `setImmediate()` +function argument. + +The first argument to `setImmediate()` will be the function to execute. Any +subsequent arguments will be passed to the function when it is executed. +Here's an example: + +```js +console.log('before immediate'); + +setImmediate((arg) => { + console.log(`executing immediate: ${arg}`); +}, 'so immediate'); + +console.log('after immediate'); +``` + +The above function passed to `setImmediate()` will execute after all runnable +code has executed, and the console output will be: + +```shell +before immediate +after immediate +executing immediate: so immediate +``` + +`setImmediate()` returns and `Immediate` object, which can be used to cancel +the scheduled immediate (see `clearImmediate()` below). + +Note: Don't get `setImmediate()` confused with `process.nextTick()`. There are +some major ways they differ. The first is that `process.nextTick()` will run +*before* any `Immediate`s that are set as well as before any scheduled I/O. +The second is that `process.nextTick()` is non-clearable, meaning once +code has been scheduled to execute with `process.nextTick()`, the execution +cannot be stopped, just like with a normal function. Refer to [this guide](../topics/the-event-loop-timers-and-nexttick#processnexttick) +to better understand the operation of `process.nextTick()`. + +### "Infinite Loop" Execution ~ *`setInterval()`* + +If there is a block of code that should execute multiple times, `setInterval()` +can be used to execute that code. `setInterval()` takes a function +argument that will run an infinite number of times with a given millisecond +delay as the second argument. Just like `setTimeout()`, additional arguments +can be added beyond the delay, and these will be passed on to the function call. +Also like `setTimeout()`, the delay cannot be guaranteed because of operations +that may hold on to the event loop, and therefore should be treated as an +approximate delay. See the below example: + +```js +function intervalFunc () { + console.log('Cant stop me now!'); +} + +setInterval(intervalFunc, 1500); +``` +In the above example, `intervalFunc()` will execute about every 1500 +milliseconds, or 1.5 seconds, until it is stopped (see below). + +Just like `setTimeout()`, `setInterval()` also returns a `Timeout` object which +can be used to reference and modify the interval that was set. + +## Clearing the Future + +What can be done if a `Timeout` or `Immediate` object needs to be cancelled? +`setTimeout()`, `setImmediate()`, and `setInterval()` return a timer object +that can be used to reference the set `Timeout` or `Immediate` object. +By passing said object into the respective `clear` function, execution of +that object will be halted completely. The respective functions are +`clearTimeout()`, `clearImmediate()`, and `clearInterval()`. See the example +below for an example of each: + +```js +let timeoutObj = setTimeout(() => { + console.log('timeout beyond time'); +}, 1500); + +let immediateObj = setImmediate(() => { + console.log('immediately executing immediate'); +}); + +let intervalObj = setInterval(() => { + console.log('interviewing the interval'); +}, 500); + +clearTimeout(timeoutObj); +clearImmediate(immediateObj); +clearInterval(intervalObj); +``` + +## Leaving Timeouts Behind + +Remember that `Timeout` objects are returned by `setTimeout` and `setInterval`. +The `Timeout` object provides two functions intended to augment `Timeout` +behavior with `unref()` and `ref()`. If there is a `Timeout` object scheduled +using a `set` function, `unref()` can be called on that object. This will change +the behavior slightly, and not call the `Timeout` object *if it is the last +code to execute*. The `Timeout` object will not keep the process alive, waiting +to execute. + +In similar fashion, a `Timeout` object that has had `unref()` called on it +can remove that behavior by calling `ref()` on that same `Timeout` object, +which will then ensure its execution. Be aware, however, that this does +not *exactly* restore the initial behavior for performance reasons. See +below for examples of both: + +```js +let timerObj = setTimeout(() => { + console.log('will i run?'); +}); + +// if left alone, this statement will keep the above +// timeout from running, since the timeout will be the only +// thing keeping the program from exiting +timerObj.unref(); + +// we can bring it back to life by calling ref() inside +// an immediate +setImmediate(() => { + timerObj.ref(); +}); +``` +## Further Down the Event Loop + +There's much more to the Event Loop and Timers than this guide +has covered. To learn more about the internals of the Node.js +Event Loop and how Timers operate during execution, check out +this Node.js guide: [The Node.js Event Loop, Timers, and +process.nextTick()](../topics/the-event-loop-timers-and-nexttick). diff --git a/doc/topics/the-event-loop-timers-and-nexttick.md b/doc/topics/the-event-loop-timers-and-nexttick.md index 0876edeab4244b..b6499f5168e506 100644 --- a/doc/topics/the-event-loop-timers-and-nexttick.md +++ b/doc/topics/the-event-loop-timers-and-nexttick.md @@ -4,7 +4,7 @@ The event loop is what allows Node.js to perform non-blocking I/O operations — despite the fact that JavaScript is single-threaded — by -offloading operations to the system kernel whenever possible. +offloading operations to the system kernel whenever possible. Since most modern kernels are multi-threaded, they can handle multiple operations executing in the background. When one of these operations @@ -12,7 +12,7 @@ completes, the kernel tells Node.js so that the appropriate callback may added to the `poll` queue to eventually be executed. We'll explain this in further detail later in this topic. -## Event Loop Explained +## Event Loop Explained When Node.js starts, it initializes the event loop, processes the provided input script (or drops into the REPL, which is not covered in @@ -67,7 +67,7 @@ actually uses are those above._ ## Phases Overview: -* `timers`: this phase executes callbacks scheduled by `setTimeout()` +* `timers`: this phase executes callbacks scheduled by `setTimeout()` and `setInterval()`. * `I/O callbacks`: most types of callback except timers, setImmedate, close * `idle, prepare`: only used internally @@ -88,13 +88,13 @@ _may be executed_ rather than the **exact** time a person _wants it to be executed_. Timers callbacks will run as early as they can be scheduled after the specified amount of time has passed; however, Operating System scheduling or the running of other callbacks may delay -them. +them. -_**Note**: Technically, the [`poll` phase](#poll) controls when timers +_**Note**: Technically, the [`poll` phase](#poll) controls when timers are executed._ -For example, say you schedule a timeout to execute after a 100 ms -threshold, then your script starts asynchronously reading a file which +For example, say you schedule a timeout to execute after a 100 ms +threshold, then your script starts asynchronously reading a file which takes 95 ms: ```js @@ -102,8 +102,8 @@ takes 95 ms: var fs = require('fs'); function someAsyncOperation (callback) { - - // let's assume this takes 95ms to complete + + // let's assume this takes 95ms to complete fs.readFile('/path/to/file', callback); } @@ -149,12 +149,12 @@ more events. ### I/O callbacks: -This phase executes callbacks for some system operations such as types +This phase executes callbacks for some system operations such as types of TCP errors. For example if a TCP socket receives `ECONNREFUSED` when attempting to connect, some \*nix systems want to wait to report the error. This will be queued to execute in the `I/O callbacks` phase. -### poll: +### poll: The poll phase has two main functions: @@ -171,7 +171,7 @@ either the queue has been exhausted, or the system-dependent hard limit is reached. * _If the `poll` queue **is empty**_, one of two more things will -happen: +happen: * If scripts have been scheduled by `setImmediate()`, the event loop will end the `poll` phase and continue to the `check` phase to execute those scheduled scripts. @@ -202,7 +202,7 @@ etc. However, after a callback has been scheduled with `setImmediate()`, then the `poll` phase becomes idle, it will end and continue to the `check` phase rather than waiting for `poll` events. -### `close callbacks`: +### `close callbacks`: If a socket or handle is closed abruptly (e.g. `socket.destroy()`), the `'close'` event will be emitted in this phase. Otherwise it will be @@ -211,10 +211,10 @@ emitted via `process.nextTick()`. ## `setImmediate()` vs `setTimeout()` `setImmediate` and `setTimeout()` are similar, but behave in different -ways depending on when they are called. +ways depending on when they are called. * `setImmediate()` is designed to execute a script once the current -`poll` phase completes. +`poll` phase completes. * `setTimeout()` schedules a script to be run after a minimum threshold in ms has elapsed. @@ -379,7 +379,7 @@ We have two calls that are similar as far as users are concerned, but their names are confusing. * `process.nextTick()` fires immediately on the same phase -* `setImmediate()` fires on the following iteration or 'tick' of the +* `setImmediate()` fires on the following iteration or 'tick' of the event loop In essence, the names should be swapped. `process.nextTick()` fires more @@ -393,7 +393,7 @@ While they are confusing, the names themselves won't change. easier to reason about (and it leads to code that's compatible with a wider variety of environments, like browser JS.)* -## Why use `process.nextTick()`? +## Why use `process.nextTick()`? There are two main reasons: @@ -420,7 +420,7 @@ the event loop to proceed it must hit the `poll` phase, which means there is a non-zero chance that a connection could have been received allowing the connection event to be fired before the listening event. -Another example is running a function constructor that was to, say, +Another example is running a function constructor that was to, say, inherit from `EventEmitter` and it wanted to call an event within the constructor: @@ -440,10 +440,10 @@ myEmitter.on('event', function() { }); ``` -You can't emit an event from the constructor immediately -because the script will not have processed to the point where the user -assigns a callback to that event. So, within the constructor itself, -you can use `process.nextTick()` to set a callback to emit the event +You can't emit an event from the constructor immediately +because the script will not have processed to the point where the user +assigns a callback to that event. So, within the constructor itself, +you can use `process.nextTick()` to set a callback to emit the event after the constructor has finished, which provides the expected results: ```js From 9b5be44b01f2cc4c177cacb262949987da4996d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Reis?= <reis@janeasystems.com> Date: Wed, 15 Jun 2016 13:42:21 +0100 Subject: [PATCH 128/131] build: split CI rules in Makefile Some CI jobs compile Node and run the tests on different machines. This change enables collaborators to have finer control over what runs on these jobs, such as the exact suites to run. The test-ci rule was split into js and native, to allow for addons to be compiled only on the machines that are going to run them. Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: Rod Vagg <rod@vagg.org> PR-URL: https://github.com/nodejs/node/pull/7317 --- Makefile | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 3f0c0b584f2c64..317119f989894c 100644 --- a/Makefile +++ b/Makefile @@ -184,11 +184,25 @@ test-all: test-build test/gc/node_modules/weak/build/Release/weakref.node test-all-valgrind: test-build $(PYTHON) tools/test.py --mode=debug,release --valgrind +CI_NATIVE_SUITES := addons +CI_JS_SUITES := doctool known_issues message parallel pseudo-tty sequential + +# Build and test addons without building anything else +test-ci-native: | test/addons/.buildstamp + $(PYTHON) tools/test.py $(PARALLEL_ARGS) -p tap --logfile test.tap \ + --mode=release --flaky-tests=$(FLAKY_TESTS) \ + $(TEST_CI_ARGS) $(CI_NATIVE_SUITES) + +# This target should not use a native compiler at all +test-ci-js: + $(PYTHON) tools/test.py $(PARALLEL_ARGS) -p tap --logfile test.tap \ + --mode=release --flaky-tests=$(FLAKY_TESTS) \ + $(TEST_CI_ARGS) $(CI_JS_SUITES) + test-ci: | build-addons $(PYTHON) tools/test.py $(PARALLEL_ARGS) -p tap --logfile test.tap \ --mode=release --flaky-tests=$(FLAKY_TESTS) \ - $(TEST_CI_ARGS) addons doctool known_issues message pseudo-tty parallel \ - sequential + $(TEST_CI_ARGS) $(CI_NATIVE_SUITES) $(CI_JS_SUITES) test-release: test-build $(PYTHON) tools/test.py --mode=release @@ -299,9 +313,11 @@ docopen: out/doc/api/all.html docclean: -rm -rf out/doc -run-ci: +build-ci: $(PYTHON) ./configure $(CONFIG_FLAGS) $(MAKE) + +run-ci: build-ci $(MAKE) test-ci RAWVER=$(shell $(PYTHON) tools/getnodeversion.py) @@ -709,4 +725,4 @@ endif bench-all bench bench-misc bench-array bench-buffer bench-net \ bench-http bench-fs bench-tls cctest run-ci test-v8 test-v8-intl \ test-v8-benchmarks test-v8-all v8 lint-ci bench-ci jslint-ci doc-only \ - $(TARBALL)-headers + $(TARBALL)-headers test-ci test-ci-native test-ci-js build-ci From 6da49ac1fd0d9f94202edb8bb2ad744ef7e23387 Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Thu, 30 Jun 2016 14:07:07 -0700 Subject: [PATCH 129/131] test: handle SmartOS bug in test-tls-session-cache Sometimes, a SmartOS bug results in ECONNREFUSED when trying to connect to the TLS server that the test starts. Retry in that situation. Fixes: https://github.com/nodejs/node/issues/5111 Refs: https://smartos.org/bugview/OS-2767 PR-URL: https://github.com/nodejs/node/pull/7505 Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com> --- test/parallel/test-tls-session-cache.js | 68 +++++++++++++++---------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/test/parallel/test-tls-session-cache.js b/test/parallel/test-tls-session-cache.js index 1fd921aeef2b3c..60a85701796d72 100644 --- a/test/parallel/test-tls-session-cache.js +++ b/test/parallel/test-tls-session-cache.js @@ -1,5 +1,5 @@ 'use strict'; -var common = require('../common'); +const common = require('../common'); if (!common.opensslCli) { common.skip('node compiled without OpenSSL CLI.'); @@ -18,17 +18,17 @@ doTest({ tickets: false }, function() { }); function doTest(testOptions, callback) { - var assert = require('assert'); - var tls = require('tls'); - var fs = require('fs'); - var join = require('path').join; - var spawn = require('child_process').spawn; + const assert = require('assert'); + const tls = require('tls'); + const fs = require('fs'); + const join = require('path').join; + const spawn = require('child_process').spawn; - var keyFile = join(common.fixturesDir, 'agent.key'); - var certFile = join(common.fixturesDir, 'agent.crt'); - var key = fs.readFileSync(keyFile); - var cert = fs.readFileSync(certFile); - var options = { + const keyFile = join(common.fixturesDir, 'agent.key'); + const certFile = join(common.fixturesDir, 'agent.crt'); + const key = fs.readFileSync(keyFile); + const cert = fs.readFileSync(certFile); + const options = { key: key, cert: cert, ca: [cert], @@ -38,7 +38,7 @@ function doTest(testOptions, callback) { var resumeCount = 0; var session; - var server = tls.createServer(options, function(cleartext) { + const server = tls.createServer(options, function(cleartext) { cleartext.on('error', function(er) { // We're ok with getting ECONNRESET in this test, but it's // timing-dependent, and thus unreliable. Any other errors @@ -72,7 +72,7 @@ function doTest(testOptions, callback) { }); server.listen(0, function() { - var args = [ + const args = [ 's_client', '-tls1', '-connect', `localhost:${this.address().port}`, @@ -86,21 +86,35 @@ function doTest(testOptions, callback) { if (common.isWindows) args.push('-no_rand_screen'); - var client = spawn(common.opensslCli, args, { - stdio: [ 0, 1, 'pipe' ] - }); - var err = ''; - client.stderr.setEncoding('utf8'); - client.stderr.on('data', function(chunk) { - err += chunk; - }); - client.on('exit', function(code) { - console.error('done'); - assert.equal(code, 0); - server.close(function() { - setTimeout(callback, 100); + function spawnClient() { + const client = spawn(common.opensslCli, args, { + stdio: [ 0, 1, 'pipe' ] }); - }); + var err = ''; + client.stderr.setEncoding('utf8'); + client.stderr.on('data', function(chunk) { + err += chunk; + }); + + client.on('exit', common.mustCall(function(code, signal) { + if (code !== 0) { + // If SmartOS and connection refused, then retry. See + // https://github.com/nodejs/node/issues/2663. + if (common.isSunOS && err.includes('Connection refused')) { + requestCount = 0; + spawnClient(); + return; + } + common.fail(`code: ${code}, signal: ${signal}, output: ${err}`); + } + assert.equal(code, 0); + server.close(common.mustCall(function() { + setTimeout(callback, 100); + })); + })); + } + + spawnClient(); }); process.on('exit', function() { From c132e9cc248047a406c80602e7154f95f3beaffa Mon Sep 17 00:00:00 2001 From: Ben Noordhuis <info@bnoordhuis.nl> Date: Sat, 2 Jul 2016 10:08:26 +0200 Subject: [PATCH 130/131] test: listen on and connect to 127.0.0.1 Avoid transient DNS issues in test sequential/test-net-GH-5504 by using the IP address instead of the 'localhost' host name. Fixes: https://github.com/nodejs/node/issues/6611 PR-URL: https://github.com/nodejs/node/pull/7524 Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com> --- test/sequential/test-net-GH-5504.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/sequential/test-net-GH-5504.js b/test/sequential/test-net-GH-5504.js index 0121f3bf25d1d7..e760c26c2944e0 100644 --- a/test/sequential/test-net-GH-5504.js +++ b/test/sequential/test-net-GH-5504.js @@ -29,7 +29,7 @@ function server() { console.error('_socketEnd'); }); socket.write(content); - }).listen(common.PORT, function() { + }).listen(common.PORT, common.localhostIPv4, function() { console.log('listening'); }); } @@ -37,7 +37,7 @@ function server() { function client() { var net = require('net'); var client = net.connect({ - host: 'localhost', + host: common.localhostIPv4, port: common.PORT }, function() { client.destroy(); From 63d361b531895306303c529d66341918920fb72b Mon Sep 17 00:00:00 2001 From: Tarun Garg <tarungarg546@gmail.com> Date: Tue, 28 Jun 2016 01:47:13 +0530 Subject: [PATCH 131/131] doc: fix documentation of process.argv The current documentation states that if run something like `node app.js` then in our process.argv array first elements is `node`, but actually it's `process.execPath` not `node` as documentation currently suggests. Fixes: https://github.com/nodejs/node/issues/7434 PR-URL: https://github.com/nodejs/node/pull/7449 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com> --- doc/api/process.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/api/process.md b/doc/api/process.md index e8ec8a69a63ea6..d2f101419d6d48 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -450,10 +450,11 @@ console.log(`This processor architecture is ${process.arch}`); added: v0.1.27 --> -The `process.argv` property returns a array containing the command line +The `process.argv` property returns an array containing the command line arguments passed when the Node.js process was launched. The first element will -be 'node', the second element will be the name of the JavaScript file. The -remaining elements will be any additional command line arguments. +be [`process.execPath`]. The second element will be the path to the +JavaScript file being executed. The remaining elements will be any additional +command line arguments. For example, assuming the following script for `process-args.js`: @@ -473,7 +474,7 @@ $ node process-2.js one two=three four Would generate the output: ```text -0: node +0: /usr/local/bin/node 1: /Users/mjr/work/node/process-2.js 2: one 3: two=three @@ -1663,6 +1664,7 @@ cases: [`process.argv`]: #process_process_argv [`process.exit()`]: #process_process_exit_code [`process.kill()`]: #process_process_kill_pid_signal +[`process.execPath`]: #process_process_execPath [`promise.catch()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch [`require.main`]: modules.html#modules_accessing_the_main_module [`setTimeout(fn, 0)`]: timers.html#timers_settimeout_callback_delay_arg