Commit 6604012b authored by Johannes Henkel's avatar Johannes Henkel Committed by Commit Bot

[DevTools] Remove headless Javascript bindings.

Looks like they're not used.

Change-Id: I78acd0704c1d3caff98f54e9124f968ee65cd234
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1845581Reviewed-by: default avatarPeter Kvitek <kvitekp@chromium.org>
Reviewed-by: default avatarAndrey Kosyakov <caseq@chromium.org>
Commit-Queue: Johannes Henkel <johannes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#703534}
parent 88fcf10f
...@@ -154,13 +154,7 @@ devtools_domains = [ ...@@ -154,13 +154,7 @@ devtools_domains = [
] ]
generated_devtools_api = [] generated_devtools_api = []
generated_devtools_api_js = []
generated_devtools_api_js_externs = []
foreach(domain, devtools_domains) { foreach(domain, devtools_domains) {
generated_devtools_api_js +=
[ "$target_gen_dir/public/devtools_js/" + domain + ".js" ]
generated_devtools_api_js_externs +=
[ "$target_gen_dir/public/devtools_js/externs/externs_" + domain + ".js" ]
generated_devtools_api += [ generated_devtools_api += [
"$target_gen_dir/public/devtools/domains/" + domain + ".cc", "$target_gen_dir/public/devtools/domains/" + domain + ".cc",
"$target_gen_dir/public/devtools/domains/" + domain + ".h", "$target_gen_dir/public/devtools/domains/" + domain + ".h",
...@@ -182,14 +176,10 @@ action("gen_devtools_client_api") { ...@@ -182,14 +176,10 @@ action("gen_devtools_client_api") {
"$root_gen_dir/third_party/blink/renderer/core/inspector/protocol.json", "$root_gen_dir/third_party/blink/renderer/core/inspector/protocol.json",
] ]
outputs = generated_devtools_api + generated_devtools_api_js + outputs = generated_devtools_api
generated_devtools_api_js_externs
sources = [ sources = [
"lib/browser/devtools_api/domain_cc.template", "lib/browser/devtools_api/domain_cc.template",
"lib/browser/devtools_api/domain_externs_js.template",
"lib/browser/devtools_api/domain_h.template", "lib/browser/devtools_api/domain_h.template",
"lib/browser/devtools_api/domain_js.template",
"lib/browser/devtools_api/domain_type_conversions_h.template", "lib/browser/devtools_api/domain_type_conversions_h.template",
"lib/browser/devtools_api/domain_types_cc.template", "lib/browser/devtools_api/domain_types_cc.template",
"lib/browser/devtools_api/domain_types_forward_declarations_h.template", "lib/browser/devtools_api/domain_types_forward_declarations_h.template",
...@@ -204,13 +194,6 @@ action("gen_devtools_client_api") { ...@@ -204,13 +194,6 @@ action("gen_devtools_client_api") {
] ]
} }
js_library("js_devtools_bindings_lib") {
sources = [ "lib/browser/devtools_api/devtools_connection.js" ] +
generated_devtools_api_js
deps = []
extra_deps = [ ":gen_devtools_client_api" ]
}
if (headless_fontconfig_utils && !is_fuchsia) { if (headless_fontconfig_utils && !is_fuchsia) {
static_library("headless_fontconfig_utils") { static_library("headless_fontconfig_utils") {
sources = [ sources = [
...@@ -653,63 +636,6 @@ if (is_mac) { ...@@ -653,63 +636,6 @@ if (is_mac) {
} }
} }
# The js_binary rule doesn't currently work on windows. Apparently there are
# problems on mac too where java isn't necessarily installed.
if (is_linux) {
js_binary("js_devtools_bindings_test") {
sources = [
"test/bindings_test.js",
]
deps = [
":js_devtools_bindings_lib",
]
externs_list = [ "lib/tab_socket_externs.js" ]
outputs = [
"$target_gen_dir/devtools_bindings_test.js",
]
config_files = []
closure_flags = [
"jscomp_error=checkTypes",
"dependency_mode=STRICT",
# Currently the bindings do not support property renaming so we can't use
# ADVANCED_OPTIMIZATIONS here. We could add support via either moving all
# the types to externs, or via applying the same renaming to the json
# dictionaries sent between C++ and JS. The closure compiler can produce
# files which contain those mappings with the appropriate flag.
"compilation_level=SIMPLE",
"language_out=ES5_STRICT",
"entry_point=chromium.BindingsTest",
]
}
grit("headless_browsertest_resources_grit") {
source = "headless_browsertest_resources.grd"
outputs = [
"grit/headless_browsertest_resources.h",
"$root_gen_dir/headless/headless_browsertest_resources.pak",
]
grit_flags = [
"-E",
"gen_root=" + rebase_path(root_gen_dir, root_build_dir),
]
deps = [
":js_devtools_bindings_test",
]
resource_ids = "lib/headless_browsertest_resource_ids"
}
repack("headless_browser_tests_pak") {
sources = [
"$root_gen_dir/headless/headless_browsertest_resources.pak",
]
output = "$root_out_dir/headless_browser_tests.pak"
deps = [
":headless_browsertest_resources_grit",
]
}
}
test("headless_browsertests") { test("headless_browsertests") {
configs += [ "//net:net_test_config" ] configs += [ "//net:net_test_config" ]
sources = [ sources = [
...@@ -771,18 +697,6 @@ test("headless_browsertests") { ...@@ -771,18 +697,6 @@ test("headless_browsertests") {
deps += [ "//components/crash/content/browser" ] deps += [ "//components/crash/content/browser" ]
} }
if (is_linux) {
# Only include this if we built the js_binary
data += [ "$root_out_dir/headless_browser_tests.pak" ]
sources += [ "test/headless_js_bindings_browsertest.cc" ]
deps += [
":headless_browser_tests_pak",
"//ui/gfx:geometry_skia",
"//ui/gfx/codec",
"//ui/gfx/geometry",
]
}
if (is_mac) { if (is_mac) {
deps += [ "//third_party/crashpad/crashpad/client" ] deps += [ "//third_party/crashpad/crashpad/client" ]
} }
......
...@@ -63,22 +63,6 @@ def CamelCaseToHackerStyle(name): ...@@ -63,22 +63,6 @@ def CamelCaseToHackerStyle(name):
return name.lower() return name.lower()
def Shorten(js_name, domain_name):
short_name = domain_name + '.'
long_name = 'chromium.DevTools.' + short_name
return js_name.replace(long_name, short_name)
def ShortForm(domain, js_name):
if not 'js_dependencies' in domain:
return js_name
for dependency in domain['js_dependencies']:
js_name = Shorten(js_name, dependency)
js_name = Shorten(js_name, domain['domain'])
return js_name
def SanitizeLiteral(literal): def SanitizeLiteral(literal):
return { return {
# Rename null enumeration values to avoid a clash with the NULL macro. # Rename null enumeration values to avoid a clash with the NULL macro.
...@@ -138,7 +122,6 @@ def PatchFullQualifiedRefs(json_api): ...@@ -138,7 +122,6 @@ def PatchFullQualifiedRefs(json_api):
def CreateUserTypeDefinition(domain, type): def CreateUserTypeDefinition(domain, type):
namespace = CamelCaseToHackerStyle(domain['domain']) namespace = CamelCaseToHackerStyle(domain['domain'])
return { return {
'js_type': '!chromium.DevTools.%s.%s' % (domain['domain'], type['id']),
'return_type': 'std::unique_ptr<::headless::%s::%s>' % ( 'return_type': 'std::unique_ptr<::headless::%s::%s>' % (
namespace, type['id']), namespace, type['id']),
'pass_type': 'std::unique_ptr<::headless::%s::%s>' % ( 'pass_type': 'std::unique_ptr<::headless::%s::%s>' % (
...@@ -156,7 +139,6 @@ def CreateUserTypeDefinition(domain, type): ...@@ -156,7 +139,6 @@ def CreateUserTypeDefinition(domain, type):
def CreateEnumTypeDefinition(domain_name, type): def CreateEnumTypeDefinition(domain_name, type):
namespace = CamelCaseToHackerStyle(domain_name) namespace = CamelCaseToHackerStyle(domain_name)
return { return {
'js_type': '!chromium.DevTools.%s.%s' % (domain_name, type['id']),
'return_type': '::headless::%s::%s' % (namespace, type['id']), 'return_type': '::headless::%s::%s' % (namespace, type['id']),
'pass_type': '::headless::%s::%s' % (namespace, type['id']), 'pass_type': '::headless::%s::%s' % (namespace, type['id']),
'to_raw_type': '%s', 'to_raw_type': '%s',
...@@ -171,7 +153,6 @@ def CreateEnumTypeDefinition(domain_name, type): ...@@ -171,7 +153,6 @@ def CreateEnumTypeDefinition(domain_name, type):
def CreateObjectTypeDefinition(): def CreateObjectTypeDefinition():
return { return {
'js_type': 'Object',
'return_type': 'std::unique_ptr<base::DictionaryValue>', 'return_type': 'std::unique_ptr<base::DictionaryValue>',
'pass_type': 'std::unique_ptr<base::DictionaryValue>', 'pass_type': 'std::unique_ptr<base::DictionaryValue>',
'to_raw_type': '*%s', 'to_raw_type': '*%s',
...@@ -187,7 +168,6 @@ def CreateObjectTypeDefinition(): ...@@ -187,7 +168,6 @@ def CreateObjectTypeDefinition():
def WrapObjectTypeDefinition(type): def WrapObjectTypeDefinition(type):
id = type.get('id', 'base::Value') id = type.get('id', 'base::Value')
return { return {
'js_type': '!Object',
'return_type': 'std::unique_ptr<%s>' % id, 'return_type': 'std::unique_ptr<%s>' % id,
'pass_type': 'std::unique_ptr<%s>' % id, 'pass_type': 'std::unique_ptr<%s>' % id,
'to_raw_type': '*%s', 'to_raw_type': '*%s',
...@@ -202,7 +182,6 @@ def WrapObjectTypeDefinition(type): ...@@ -202,7 +182,6 @@ def WrapObjectTypeDefinition(type):
def CreateAnyTypeDefinition(): def CreateAnyTypeDefinition():
return { return {
'js_type': '*',
'return_type': 'std::unique_ptr<base::Value>', 'return_type': 'std::unique_ptr<base::Value>',
'pass_type': 'std::unique_ptr<base::Value>', 'pass_type': 'std::unique_ptr<base::Value>',
'to_raw_type': '*%s', 'to_raw_type': '*%s',
...@@ -217,7 +196,6 @@ def CreateAnyTypeDefinition(): ...@@ -217,7 +196,6 @@ def CreateAnyTypeDefinition():
def CreateStringTypeDefinition(): def CreateStringTypeDefinition():
return { return {
'js_type': 'string',
'return_type': 'std::string', 'return_type': 'std::string',
'pass_type': 'const std::string&', 'pass_type': 'const std::string&',
'to_pass_type': '%s', 'to_pass_type': '%s',
...@@ -232,7 +210,6 @@ def CreateStringTypeDefinition(): ...@@ -232,7 +210,6 @@ def CreateStringTypeDefinition():
def CreateBinaryTypeDefinition(): def CreateBinaryTypeDefinition():
return { return {
'js_type': 'string',
'return_type': 'protocol::Binary', 'return_type': 'protocol::Binary',
'pass_type': 'const protocol::Binary&', 'pass_type': 'const protocol::Binary&',
'to_pass_type': '%s', 'to_pass_type': '%s',
...@@ -251,13 +228,7 @@ def CreatePrimitiveTypeDefinition(type): ...@@ -251,13 +228,7 @@ def CreatePrimitiveTypeDefinition(type):
'integer': 'int', 'integer': 'int',
'boolean': 'bool', 'boolean': 'bool',
} }
js_typedefs = {
'number': 'number',
'integer': 'number',
'boolean': 'boolean',
}
return { return {
'js_type': js_typedefs[type],
'return_type': typedefs[type], 'return_type': typedefs[type],
'pass_type': typedefs[type], 'pass_type': typedefs[type],
'to_pass_type': '%s', 'to_pass_type': '%s',
...@@ -282,7 +253,6 @@ type_definitions['any'] = CreateAnyTypeDefinition() ...@@ -282,7 +253,6 @@ type_definitions['any'] = CreateAnyTypeDefinition()
def WrapArrayDefinition(type): def WrapArrayDefinition(type):
return { return {
'js_type': '!Array.<%s>' % type['js_type'],
'return_type': 'std::vector<%s>' % type['type'], 'return_type': 'std::vector<%s>' % type['type'],
'pass_type': 'std::vector<%s>' % type['type'], 'pass_type': 'std::vector<%s>' % type['type'],
'to_raw_type': '%s', 'to_raw_type': '%s',
...@@ -465,15 +435,6 @@ def InitializeDomainDependencies(json_api): ...@@ -465,15 +435,6 @@ def InitializeDomainDependencies(json_api):
for domain in json_api['domains']: for domain in json_api['domains']:
domain_deps = set() domain_deps = set()
TraverseDependencies(domain['domain'], domain_deps) TraverseDependencies(domain['domain'], domain_deps)
if 'dependencies' in domain:
domain['js_dependencies'] = domain['dependencies']
else:
domain['js_dependencies'] = []
domain['js_forward_declarations'] = []
for type in types_required[domain['domain']]:
if not type.split('.')[0] in domain['js_dependencies']:
domain['js_forward_declarations'].append(type)
domain['dependencies'] = sorted(domain_deps) domain['dependencies'] = sorted(domain_deps)
...@@ -521,7 +482,6 @@ def GeneratePerDomain(jinja_env, output_dirname, json_api, class_name, ...@@ -521,7 +482,6 @@ def GeneratePerDomain(jinja_env, output_dirname, json_api, class_name,
template_context = { template_context = {
'domain': domain, 'domain': domain,
'resolve_type': ResolveType, 'resolve_type': ResolveType,
'short_form': functools.partial(ShortForm, domain),
} }
domain_name = CamelCaseToHackerStyle(domain['domain']) domain_name = CamelCaseToHackerStyle(domain['domain'])
output_file = '%s/%s.%s' % (output_dirname, output_file = '%s/%s.%s' % (output_dirname,
...@@ -536,14 +496,6 @@ def GenerateDomains(jinja_env, output_dirname, json_api): ...@@ -536,14 +496,6 @@ def GenerateDomains(jinja_env, output_dirname, json_api):
jinja_env, os.path.join(output_dirname, 'devtools', 'domains'), json_api, jinja_env, os.path.join(output_dirname, 'devtools', 'domains'), json_api,
'domain', ['cc', 'h'], 'domain', ['cc', 'h'],
lambda domain_name: domain_name) lambda domain_name: domain_name)
GeneratePerDomain(
jinja_env, os.path.join(output_dirname, 'devtools_js'), json_api,
'domain', ['js'],
lambda domain_name: domain_name)
GeneratePerDomain(
jinja_env, os.path.join(output_dirname, 'devtools_js', 'externs'),
json_api, 'domain_externs', ['js'],
lambda domain_name: 'externs_%s' % (domain_name, ))
def GenerateTypes(jinja_env, output_dirname, json_api): def GenerateTypes(jinja_env, output_dirname, json_api):
......
// Copyright 2017 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.
/**
* @fileoverview Contains a class which marshals DevTools protocol messages over
* a provided low level message transport.
*/
'use strict';
goog.provide('chromium.DevTools.Connection');
/**
* Handles sending and receiving DevTools JSON protocol messages over the
* provided low level message transport.
* @export
*/
chromium.DevTools.Connection = class {
/**
* @param {!Object} transport The API providing transport for devtools
* commands.
* @param {function(string, Object=): undefined =} opt_validator An
* optional function which performs checks before sending any DevTools
* messages. It should throw an error if validation fails.
*/
constructor(transport, opt_validator) {
/** @private {!Object} */
this.transport_ = transport;
/** @private {function(string, Object=): undefined|undefined} */
this.validator_ = opt_validator;
/** @private {number} */
this.commandId_ = 1;
/**
* An object containing pending DevTools protocol commands keyed by id.
*
* @private {!Map<number, !chromium.DevTools.Connection.PendingCommand>}
*/
this.pendingCommands_ = new Map();
/** @private {number} */
this.nextListenerId_ = 1;
/**
* An object containing DevTools protocol events we are listening for keyed
* by name.
*
* @private {!Map<string,
* !Map<number,
* !chromium.DevTools.Connection.EventFunction>>}
*/
this.eventListeners_ = new Map();
/**
* Used for removing event listeners by id.
*
* @private {!Map<number, string>}
*/
this.eventListenerIdToEventName_ = new Map();
/**
* An object containing listeners for all DevTools protocol events keyed
* by listener id.
*
* @private {!Map<number, !chromium.DevTools.Connection.AllEventsFunction>}
*/
this.allEventsListeners_ = new Map();
this.transport_.onmessage = this.onJsonMessage_.bind(this);
}
/**
* Listens for DevTools protocol events of the specified name and issues the
* callback upon reception.
*
* @param {string} eventName Name of the DevTools protocol event to listen
* for.
* @param {!chromium.DevTools.Connection.EventFunction} listener The callback
* issued when we receive a DevTools protocol event corresponding to the
* given name.
* @return {number} The id of this event listener.
*/
addEventListener(eventName, listener) {
if (!this.eventListeners_.has(eventName)) {
this.eventListeners_.set(eventName, new Map());
}
let id = this.nextListenerId_++;
this.eventListeners_.get(eventName).set(id, listener);
this.eventListenerIdToEventName_.set(id, eventName);
return id;
}
/**
* Removes an event listener previously added by
* <code>addEventListener</code>.
*
* @param {number} id The id of the event listener to remove.
* @return {boolean} Whether the event listener was actually removed.
*/
removeEventListener(id) {
if (!this.eventListenerIdToEventName_.has(id)) return false;
let eventName = this.eventListenerIdToEventName_.get(id);
this.eventListenerIdToEventName_.delete(id);
// This shouldn't happen, but lets check anyway.
if (!this.eventListeners_.has(eventName)) return false;
return this.eventListeners_.get(eventName).delete(id);
}
/**
* Listens for all DevTools protocol events and issues the
* callback upon reception.
*
* @param {!chromium.DevTools.Connection.AllEventsFunction} listener The
* callback issued when we receive a DevTools protocol event.
* @return {number} The id of this event listener.
*/
addAllEventsListener(listener) {
let id = this.nextListenerId_++;
this.allEventsListeners_.set(id, listener);
return id;
}
/**
* Removes an event listener previously added by
* <code>addAllEventsListener</code>.
*
* @param {number} id The id of the event listener to remove.
* @return {boolean} Whether the event listener was actually removed.
*/
removeAllEventsListener(id) {
if (!this.allEventsListeners_.has(id)) return false;
return this.allEventsListeners_.delete(id);
}
/**
* Issues a DevTools protocol command and returns a promise for the results.
*
* @param {string} method The name of the DevTools protocol command method.
* @param {!Object=} params An object containing the command parameters if
* any.
* @return {!Promise<!TYPE>} A promise for the results object.
* @template TYPE
*/
sendDevToolsMessage(method, params = {}) {
let id = this.commandId_;
// We increment by two because these bindings are intended to be used in
// conjunction with HeadlessDevToolsClient::RawProtocolListener and using
// odd numbers for js generated IDs lets the implementation of =
// OnProtocolMessage easily distinguish between C++ and JS generated
// commands and route the response accordingly.
this.commandId_ += 2;
if (this.validator_) {
this.validator_(method, params);
}
// Note the names are in quotes to prevent closure compiler name mangling.
this.transport_.send(
JSON.stringify({'method': method, 'id': id, 'params': params}));
return new Promise((resolve, reject) => {
this.pendingCommands_.set(id, {resolve: resolve, reject: reject});
});
}
/**
* @param {string} jsonMessage A string containing a JSON DevTools protocol
* message.
* @private
*/
onJsonMessage_(jsonMessage) {
let message = JSON.parse(jsonMessage);
if (message.hasOwnProperty('id')) {
if (!this.pendingCommands_.has(message.id))
throw new Error('Unrecognized id:' + jsonMessage);
if (message.hasOwnProperty('error'))
this.pendingCommands_.get(message.id).reject(message.error);
else
this.pendingCommands_.get(message.id).resolve(message.result);
this.pendingCommands_.delete(message.id);
} else {
if (!message.hasOwnProperty('method') ||
!message.hasOwnProperty('params')) {
throw new Error('Bad message:' + jsonMessage);
}
const method = message['method'];
const params = message['params'];
this.allEventsListeners_.forEach(function(listener) {
listener({method, params});
});
if (this.eventListeners_.has(method)) {
this.eventListeners_.get(method).forEach(function(listener) {
listener(params);
});
}
}
}
}
/**
* @typedef {function(Object): undefined|function(string): undefined}
*/
chromium.DevTools.Connection.EventFunction;
/**
* @typedef {function(Object): undefined}
*/
chromium.DevTools.Connection.AllEventsFunction;
/**
* @typedef {{
* resolve: function(!Object),
* reject: function(!Object)
* }}
*/
chromium.DevTools.Connection.PendingCommand;
// Copyright 2017 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.
{# Changes to the externs must be reflected in the generated API too. #}
/**
* Experimental bindings for the {{domain.domain}} DevTools Domain. Note these
* are subject to change without warning. Use at your own risk.
* @param {!chromium.DevTools.Connection} connection The DevTools connection.
* @constructor
*/
chromium.DevTools.Experimental{{domain.domain}} = function(connection) {};
/**
* Removes an event listener.
*
* @param {number} id The id of the event listener to remove.
* @return {boolean} Whether the event listener was actually removed.
*/
chromium.DevTools.Experimental{{domain.domain}}.prototype.removeEventListener = function(id) {};
/**
* Bindings for the {{domain.domain}} DevTools Domain.
* @param {!chromium.DevTools.Connection} connection The DevTools connection.
* @constructor
*/
chromium.DevTools.{{domain.domain}} = function(connection) {};
/** @type {!chromium.DevTools.Experimental{{domain.domain}}} */
chromium.DevTools.{{domain.domain}}.prototype.experimental;
/**
* Removes an event listener.
*
* @param {number} id The id of the event listener to remove.
* @return {boolean} Whether the event listener was actually removed.
*/
chromium.DevTools.{{domain.domain}}.prototype.removeEventListener = function(id) {};
{# Generate enums. #}
{% for type in domain.types %}
{% if not "enum" in type %}{% continue %}{% endif %}
/**
{% if type.description %}
* {{type.description.replace('\n', '\n * ')}}
*
{% endif %}
* @enum {string}
*/
chromium.DevTools.{{domain.domain}}.{{type.id}} = {
{% for literal in type.enum %}
{{ literal | sanitize_literal | dash_to_camelcase | camelcase_to_hacker_style | upper }}: "{{ literal }}"{{',' if not loop.last}}
{% endfor %}
};
{% endfor %}
{# Generate types. #}
{% for type in domain.types %}
{% if not (type.type == "object") or not ("properties" in type) %}{% continue %}{% endif %}
/**
{% if type.description %}
* {{type.description.replace('\n', '\n * ')}}
*
{% endif %}
{% for property in type.properties %}
{% if property.description %}
* {{property.name}}: {{property.description.replace('\n', '\n * ')}}
{% endif %}
{% endfor %}
*
{% if type.properties %}
* @typedef {{ '{{' }}
{% for property in type.properties %}
{% if property.optional %}
* {{property.name}}: ({{ resolve_type(property).js_type }}|undefined){{',' if not loop.last}}
{% else %}
* {{property.name}}: {{ resolve_type(property).js_type }}{{',' if not loop.last}}
{% endif %}
{% endfor %}
* {{ '}}' }}
{% else %}
* @typedef {undefined}
{% endif %}
*/
chromium.DevTools.{{domain.domain}}.{{type.id}};
{% endfor %}
{# Generate all commands for Experimental Domain. #}
{% for command in domain.commands %}
{% set method_name = command.name | sanitize_literal %}
{% set title_case_method_name = method_name | to_title_case %}
{% set result_type = '{!Promise<chromium.DevTools.' + domain.domain + '.' + title_case_method_name + 'Result>}' %}
/**
{% if command.description %}
* {{ command.description.replace('\n', '\n * ') }}
*
{% endif %}
{% if command.parameters|length > 0 %}
{% if command.parameters_required %}
* @param {{ '{chromium.DevTools.' + domain.domain + '.' + title_case_method_name + 'Params}' }} params
{% else %}
* @param {{ '{chromium.DevTools.' + domain.domain + '.' + title_case_method_name + 'Params=}' }} opt_params
{% endif %}
{% endif %}
* @return {{result_type}}
*/
{% if command.parameters|length > 0 %}
{% if command.parameters_required %}
chromium.DevTools.Experimental{{domain.domain}}.prototype.{{method_name}} = function(params) {};
{% else %}
chromium.DevTools.Experimental{{domain.domain}}.prototype.{{method_name}} = function(opt_params) {};
{% endif %}
{% else %}
chromium.DevTools.Experimental{{domain.domain}}.prototype.{{method_name}} = function() {};
{% endif %}
{% endfor %}
{# Generate all events Experimental Domain. #}
{% for event in domain.events %}
{% if event.parameters|length > 0 %}
{% set param_type = '{!function(!chromium.DevTools.' + domain.domain + '.' + event.name | to_title_case + 'Params)}' %}
{% else %}
{% set param_type = '{!function()}' %}
{% endif %}
/**
{% if event.description %}
* {{ event.description.replace('\n', '\n * ') }}
*
{% endif %}
* @param {{param_type}} listener
* @return {number} The id of this event listener.
*/
chromium.DevTools.Experimental{{domain.domain}}.prototype.on{{event.name | to_title_case}} = function(listener) {};
{% endfor %}
{# Generate non-Experimental commands. #}
{% for command in domain.commands %}
{% if command.Experimental %}{% continue %}{% endif %}
{% set method_name = command.name | sanitize_literal %}
{% set title_case_method_name = method_name | to_title_case %}
{% set result_type = '{!Promise<chromium.DevTools.' + domain.domain + '.' + title_case_method_name + 'Result>}' %}
/**
{% if command.description %}
* {{ command.description.replace('\n', '\n * ') }}
*
{% endif %}
{% if command.parameters|length > 0 %}
{% if command.parameters_required %}
* @param {{ '{chromium.DevTools.' + domain.domain + '.' + title_case_method_name + 'Params}' }} params
{% else %}
* @param {{ '{chromium.DevTools.' + domain.domain + '.' + title_case_method_name + 'Params=}' }} opt_params
{% endif %}
{% endif %}
* @return {{result_type}}
*/
{% if command.parameters|length > 0 %}
{% if command.parameters_required %}
chromium.DevTools.{{domain.domain}}.prototype.{{method_name}} = function(params) {};
{% else %}
chromium.DevTools.{{domain.domain}}.prototype.{{method_name}} = function(opt_params) {};
{% endif %}
{% else %}
chromium.DevTools.{{domain.domain}}.prototype.{{method_name}} = function() {};
{% endif %}
{% endfor %}
{# Generate non-Experimental events. #}
{% for event in domain.events %}
{% if event.Experimental %}{% continue %}{% endif %}
{% if event.parameters|length > 0 %}
{% set param_type = '{!function(!chromium.DevTools.' + domain.domain + '.' + event.name | to_title_case + 'Params)}' %}
{% else %}
{% set param_type = '{!function()}' %}
{% endif %}
/**
{% if event.description %}
* {{ event.description.replace('\n', '\n * ') }}
*
{% endif %}
* @param {{param_type}} listener
* @return {number} The id of this event listener.
*/
chromium.DevTools.{{domain.domain}}.prototype.on{{event.name | to_title_case}} = function(listener) {};
{% endfor %}
// Copyright 2017 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.
{# Changes to the generated API must be reflected in the externs too. #}
/**
* @fileoverview Generated DevTools bindings for the {{domain.domain}} Domain.
*/
'use strict';
goog.provide('chromium.DevTools.Experimental{{domain.domain}}');
goog.provide('chromium.DevTools.{{domain.domain}}');
goog.require('chromium.DevTools.Connection');
{% for domain_name in domain.js_dependencies %}
goog.require('chromium.DevTools.{{domain_name}}');
{% endfor %}
{% for forward_declaration in domain.js_forward_declarations %}
goog.forwardDeclare('chromium.DevTools.{{forward_declaration}}');
{% endfor %}
goog.scope(function() {
const Connection = chromium.DevTools.Connection;
{% for domain_name in domain.js_dependencies %}
const {{domain_name}} = chromium.DevTools.{{domain_name}};
{% endfor %}
/**
* Experimental bindings for the {{domain.domain}} DevTools Domain. Note these
* are subject to change without warning. Use at your own risk.
* @param {!Connection} connection The DevTools connection.
* @constructor
*/
chromium.DevTools.Experimental{{domain.domain}} = function(connection) {
/** @private {!Connection} */
this.connection_ = connection;
}
const Experimental{{domain.domain}} = chromium.DevTools.Experimental{{domain.domain}};
/**
* Removes an event listener.
*
* @param {number} id The id of the event listener to remove.
* @return {boolean} Whether the event listener was actually removed.
*/
Experimental{{domain.domain}}.prototype.removeEventListener = function(id) {
return this.connection_.removeEventListener(id);
}
/**
* Bindings for the {{domain.domain}} DevTools Domain.
* @param {!Connection} connection The DevTools connection.
* @constructor
*/
chromium.DevTools.{{domain.domain}} = function(connection) {
/** @private {!Connection} */
this.connection_ = connection;
/** @type {!Experimental{{domain.domain}}} */
this.experimental = new Experimental{{domain.domain}}(connection);
}
const {{domain.domain}} = chromium.DevTools.{{domain.domain}};
/**
* Removes an event listener.
*
* @param {number} id The id of the event listener to remove.
* @return {boolean} Whether the event listener was actually removed.
*/
{{domain.domain}}.prototype.removeEventListener = function(id) {
return this.connection_.removeEventListener(id);
}
{# Generate enums. #}
{% for type in domain.types %}
{% if not "enum" in type %}{% continue %}{% endif %}
/**
{% if type.description %}
* {{type.description.replace('\n', '\n * ')}}
*
{% endif %}
* @enum {string}
*/
{{domain.domain}}.{{type.id}} = {
{% for literal in type.enum %}
{{ literal | sanitize_literal | dash_to_camelcase | camelcase_to_hacker_style | upper }}: "{{ literal }}"{{',' if not loop.last}}
{% endfor %}
};
{% endfor %}
{# Generate types. #}
{% for type in domain.types %}
{% if not (type.type == "object") or not ("properties" in type) %}{% continue %}{% endif %}
/**
{% if type.description %}
* {{type.description.replace('\n', '\n * ')}}
*
{% endif %}
{% for property in type.properties %}
{% if property.description %}
* {{property.name}}: {{property.description.replace('\n', '\n * ')}}
{% endif %}
{% endfor %}
*
{% if type.properties %}
* @typedef {{ '{{' }}
{% for property in type.properties %}
{% if property.optional %}
* {{property.name}}: ({{ short_form(resolve_type(property).js_type) }}|undefined){{',' if not loop.last}}
{% else %}
* {{property.name}}: {{ short_form(resolve_type(property).js_type) }}{{',' if not loop.last}}
{% endif %}
{% endfor %}
* {{ '}}' }}
{% else %}
* @typedef {undefined}
{% endif %}
*/
{{domain.domain}}.{{type.id}};
{% endfor %}
{# Generate all commands for Experimental Domain. #}
{% for command in domain.commands %}
{% set method_name = command.name | sanitize_literal %}
{% set title_case_method_name = method_name | to_title_case %}
{% set result_type = '{!Promise<' + domain.domain + '.' + title_case_method_name + 'Result>}' %}
/**
{% if command.description %}
* {{ command.description.replace('\n', '\n * ') }}
*
{% endif %}
{% if command.parameters|length > 0 %}
{% if command.parameters_required %}
* @param {{ '{' + domain.domain + '.' + title_case_method_name + 'Params}' }} params
{% else %}
* @param {{ '{' + domain.domain + '.' + title_case_method_name + 'Params=}' }} opt_params
{% endif %}
{% endif %}
* @return {{result_type}}
*/
{% if command.parameters|length > 0 %}
{% if command.parameters_required %}
Experimental{{domain.domain}}.prototype.{{method_name}} = function(params) {
return this.connection_.sendDevToolsMessage('{{domain.domain}}.{{command.name}}', params);
};
{% else %}
Experimental{{domain.domain}}.prototype.{{method_name}} = function(opt_params) {
return this.connection_.sendDevToolsMessage('{{domain.domain}}.{{command.name}}', opt_params || {});
};
{% endif %}
{% else %}
Experimental{{domain.domain}}.prototype.{{method_name}} = function() {
return this.connection_.sendDevToolsMessage('{{domain.domain}}.{{command.name}}', {});
};
{% endif %}
{% endfor %}
{# Generate all events Experimental Domain. #}
{% for event in domain.events %}
{% if event.parameters|length > 0 %}
{% set param_type = '{!function(!' + domain.domain + '.' + event.name | to_title_case + 'Params)}' %}
{% else %}
{% set param_type = '{!function()}' %}
{% endif %}
/**
{% if event.description %}
* {{ event.description.replace('\n', '\n * ') }}
*
{% endif %}
* @param {{param_type}} listener
* @return {number} The id of this event listener.
*/
Experimental{{domain.domain}}.prototype.on{{event.name | to_title_case}} = function(listener) {
return this.connection_.addEventListener(
'{{domain.domain}}.{{event.name}}', /** @type {!function(!Object): undefined} */ (listener));
};
{% endfor %}
{# Generate non-Experimental commands. #}
{% for command in domain.commands %}
{% if command.Experimental %}{% continue %}{% endif %}
{% set method_name = command.name | sanitize_literal %}
{% set title_case_method_name = method_name | to_title_case %}
{% set result_type = '{!Promise<' + domain.domain + '.' + title_case_method_name + 'Result>}' %}
/**
{% if command.description %}
* {{ command.description.replace('\n', '\n * ') }}
*
{% endif %}
{% if command.parameters|length > 0 %}
{% if command.parameters_required %}
* @param {{ '{' + domain.domain + '.' + title_case_method_name + 'Params}' }} params
{% else %}
* @param {{ '{' + domain.domain + '.' + title_case_method_name + 'Params=}' }} opt_params
{% endif %}
{% endif %}
* @return {{result_type}}
*/
{% if command.parameters|length > 0 %}
{% if command.parameters_required %}
{{domain.domain}}.prototype.{{method_name}} = function(params) {
return this.connection_.sendDevToolsMessage('{{domain.domain}}.{{command.name}}', params);
};
{% else %}
{{domain.domain}}.prototype.{{method_name}} = function(opt_params) {
return this.connection_.sendDevToolsMessage('{{domain.domain}}.{{command.name}}', opt_params || {});
};
{% endif %}
{% else %}
{{domain.domain}}.prototype.{{method_name}} = function() {
return this.connection_.sendDevToolsMessage('{{domain.domain}}.{{command.name}}', {});
};
{% endif %}
{% endfor %}
{# Generate non-Experimental events. #}
{% for event in domain.events %}
{% if event.Experimental %}{% continue %}{% endif %}
{% if event.parameters|length > 0 %}
{% set param_type = '{!function(!' + domain.domain + '.' + event.name | to_title_case + 'Params)}' %}
{% else %}
{% set param_type = '{!function()}' %}
{% endif %}
/**
{% if event.description %}
* {{ event.description.replace('\n', '\n * ') }}
*
{% endif %}
* @param {{param_type}} listener
* @return {number} The id of this event listener.
*/
{{domain.domain}}.prototype.on{{event.name | to_title_case}} = function(listener) {
return this.connection_.addEventListener(
'{{domain.domain}}.{{event.name}}', /** @type {!function(!Object): undefined} */ (listener));
};
{% endfor %}
}); // goog.scope
// Copyright 2017 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.
goog.module('chromium.BindingsTest');
// Needed to let C++ invoke the test.
goog.module.declareLegacyNamespace();
const Connection = goog.require('chromium.DevTools.Connection');
const DOM = goog.require('chromium.DevTools.DOM');
const Page = goog.require('chromium.DevTools.Page');
const Runtime = goog.require('chromium.DevTools.Runtime');
/**
* A trivial test which is invoked from C++ by HeadlessJsBindingsTest.
*/
class BindingsTest {
constructor() {}
/**
* Evaluates 1+1 and returns the result over the chromium.DevTools.Connection.
*/
evalOneAddOne() {
let connection = new Connection(window.TabSocket);
let runtime = new Runtime(connection);
runtime.evaluate({'expression': '1+1'}).then(function(message) {
connection.sendDevToolsMessage(
'__Result',
{'result': JSON.stringify(message.result.value)});
});
}
/**
* Evaluates 1+1 and returns the result over the chromium.DevTools.Connection.
*/
listenForChildNodeCountUpdated() {
let connection = new Connection(window.TabSocket);
let dom = new DOM(connection);
dom.onChildNodeCountUpdated(function(params) {
connection.sendDevToolsMessage('__Result',
{'result': JSON.stringify(params)});
});
dom.enable().then(function() {
return dom.getDocument({});
}).then(function() {
// Create a new div which should trigger the event.
let div = document.createElement('div');
document.body.appendChild(div);
});
}
/**
* Uses experimental commands to create an isolated world.
*/
getIsolatedWorldName() {
let connection = new Connection(window.TabSocket);
let page = new Page(connection);
let runtime = new Runtime(connection);
runtime.enable().then(function() {
runtime.onExecutionContextCreated(function(params) {
if (params.context.auxData['isDefault'] === false) {
connection.sendDevToolsMessage(
'__Result', {'result': 'Created ' + params.context.name});
}
});
page.experimental.getResourceTree().then(function(result) {
page.experimental.createIsolatedWorld({
'frameId': result.frameTree.frame.id,
'worldName': 'Test Isolated World'
});
});
});
}
}
exports = BindingsTest;
// Copyright 2017 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 <memory>
#include <string>
#include <vector>
#include "base/base64.h"
#include "base/bind.h"
#include "base/json/json_reader.h"
#include "base/memory/weak_ptr.h"
#include "base/path_service.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_restrictions.h"
#include "content/public/common/isolated_world_ids.h"
#include "content/public/test/browser_test.h"
#include "headless/grit/headless_browsertest_resources.h"
#include "headless/public/devtools/domains/page.h"
#include "headless/public/devtools/domains/runtime.h"
#include "headless/public/headless_browser.h"
#include "headless/public/headless_devtools_client.h"
#include "headless/public/headless_web_contents.h"
#include "headless/test/headless_browser_test.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/resource/resource_bundle.h"
namespace headless {
namespace {
const char kIndexHtml[] = R"(
<html>
<body>
<script src="bindings.js"></script>
</body>
</html>
)";
const char kTabSocketScript[] = R"(
window.TabSocket = {};
window.TabSocket.onmessage = () => {};
window.TabSocket.send = (json) => console.debug(json);
)";
} // namespace
class HeadlessJsBindingsTest
: public HeadlessAsyncDevTooledBrowserTest,
public HeadlessDevToolsClient::RawProtocolListener,
public headless::runtime::Observer,
public page::ExperimentalObserver {
public:
using ConsoleAPICalledParams = headless::runtime::ConsoleAPICalledParams;
using EvaluateResult = headless::runtime::EvaluateResult;
using RemoteObject = headless::runtime::RemoteObject;
HeadlessJsBindingsTest() {}
void SetUpOnMainThread() override {
base::ThreadRestrictions::SetIOAllowed(true);
base::FilePath pak_path;
ASSERT_TRUE(base::PathService::Get(base::DIR_MODULE, &pak_path));
pak_path = pak_path.AppendASCII("headless_browser_tests.pak");
ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath(
pak_path, ui::SCALE_FACTOR_NONE);
}
void CustomizeHeadlessWebContents(
HeadlessWebContents::Builder& builder) override {
builder.SetWindowSize(gfx::Size(0, 0));
builder.SetInitialURL(GURL("http://test.com/index.html"));
interceptor_->InsertResponse("http://test.com/index.html",
{kIndexHtml, "text/html"});
std::string bindings_js = ui::ResourceBundle::GetSharedInstance()
.GetRawDataResource(DEVTOOLS_BINDINGS_TEST)
.as_string();
interceptor_->InsertResponse(
"http://test.com/bindings.js",
{bindings_js.c_str(), "application/javascript"});
}
void RunDevTooledTest() override {
devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
devtools_client_->GetPage()->Enable();
devtools_client_->GetPage()->AddScriptToEvaluateOnNewDocument(
kTabSocketScript);
devtools_client_->GetRuntime()->AddObserver(this);
devtools_client_->GetRuntime()->Enable();
devtools_client_->GetRuntime()->Evaluate(
kTabSocketScript,
base::BindOnce(&HeadlessJsBindingsTest::ConnectionEstablished,
weak_factory_.GetWeakPtr()));
devtools_client_->SetRawProtocolListener(this);
}
void ConnectionEstablished(std::unique_ptr<EvaluateResult>) {
connection_established_ = true;
}
virtual void RunJsBindingsTest() = 0;
void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
RunJsBindingsTest();
}
virtual void OnResult(const std::string& result) = 0;
void FailOnJsEvaluateException(
std::unique_ptr<runtime::EvaluateResult> result) {
if (!result->HasExceptionDetails())
return;
FinishAsynchronousTest();
const runtime::ExceptionDetails* exception_details =
result->GetExceptionDetails();
FAIL() << exception_details->GetText()
<< (exception_details->HasException()
? exception_details->GetException()->GetDescription().c_str()
: "");
}
void OnConsoleAPICalled(const ConsoleAPICalledParams& params) override {
const std::vector<std::unique_ptr<RemoteObject>>& args = *params.GetArgs();
if (args.empty())
return;
if (params.GetType() != headless::runtime::ConsoleAPICalledType::DEBUG)
return;
RemoteObject* object = args[0].get();
if (object->GetType() != headless::runtime::RemoteObjectType::STRING)
return;
OnMessageFromJS(object->GetValue()->GetString());
}
void SendMessageToJS(const std::string& message) {
std::string encoded;
base::Base64Encode(message, &encoded);
devtools_client_->GetRuntime()->Evaluate(
"window.TabSocket.onmessage(atob(\"" + encoded + "\"))");
}
void OnMessageFromJS(const std::string& json_message) {
std::unique_ptr<base::Value> message =
base::JSONReader::ReadDeprecated(json_message, base::JSON_PARSE_RFC);
const base::Value* method_value = message->FindKey("method");
if (!method_value) {
FinishAsynchronousTest();
FAIL() << "Badly formed message " << json_message;
return;
}
const base::Value* params_value = message->FindKey("params");
if (!params_value) {
FinishAsynchronousTest();
FAIL() << "Badly formed message " << json_message;
return;
}
const base::Value* id_value = message->FindKey("id");
if (!id_value) {
FinishAsynchronousTest();
FAIL() << "Badly formed message " << json_message;
return;
}
if (method_value->GetString() == "__Result") {
OnResult(params_value->FindKey("result")->GetString());
return;
}
devtools_client_->SendRawDevToolsMessage(json_message);
}
bool OnProtocolMessage(const std::string& json_message,
const base::DictionaryValue& parsed_message) override {
if (!connection_established_)
return false;
const base::Value* id_value = parsed_message.FindKey("id");
// If |parsed_message| contains an id we know this is a message reply.
if (id_value) {
int id = id_value->GetInt();
// We are only interested in message replies (ones with an id) where the
// id is odd. The reason is HeadlessDevToolsClientImpl uses even/oddness
// to distinguish between commands send from the C++ bindings and those
// via HeadlessDevToolsClientImpl::SendRawDevToolsMessage.
if ((id % 2) == 0)
return false;
SendMessageToJS(json_message);
return true;
}
const base::Value* method_value = parsed_message.FindKey("method");
if (!method_value)
return false;
if (method_value->GetString() == "Runtime.consoleAPICalled") {
// console.debug is used for transport.
return false;
}
SendMessageToJS(json_message);
// Check which domain the event belongs to, if it's the DOM domain then
// assume js handled it.
std::vector<base::StringPiece> sections =
SplitStringPiece(method_value->GetString(), ".", base::KEEP_WHITESPACE,
base::SPLIT_WANT_ALL);
return sections[0] == "DOM" || sections[0] == "Runtime";
}
protected:
bool connection_established_ = false;
base::WeakPtrFactory<HeadlessJsBindingsTest> weak_factory_{this};
};
class SimpleCommandJsBindingsTest : public HeadlessJsBindingsTest {
public:
void RunJsBindingsTest() override {
devtools_client_->GetRuntime()->Evaluate(
"new chromium.BindingsTest().evalOneAddOne();",
base::BindOnce(&HeadlessJsBindingsTest::FailOnJsEvaluateException,
base::Unretained(this)));
}
void OnResult(const std::string& result) override {
EXPECT_EQ("2", result);
FinishAsynchronousTest();
}
};
HEADLESS_ASYNC_DEVTOOLED_TEST_F(SimpleCommandJsBindingsTest);
class ExperimentalCommandJsBindingsTest : public HeadlessJsBindingsTest {
public:
void RunJsBindingsTest() override {
devtools_client_->GetRuntime()->Evaluate(
"new chromium.BindingsTest().getIsolatedWorldName();",
base::BindOnce(&HeadlessJsBindingsTest::FailOnJsEvaluateException,
base::Unretained(this)));
}
void OnResult(const std::string& result) override {
EXPECT_EQ("Created Test Isolated World", result);
FinishAsynchronousTest();
}
};
HEADLESS_ASYNC_DEVTOOLED_TEST_F(ExperimentalCommandJsBindingsTest);
class SimpleEventJsBindingsTest : public HeadlessJsBindingsTest {
public:
void RunJsBindingsTest() override {
devtools_client_->GetRuntime()->Evaluate(
"new chromium.BindingsTest().listenForChildNodeCountUpdated();",
base::BindOnce(&HeadlessJsBindingsTest::FailOnJsEvaluateException,
base::Unretained(this)));
}
void OnResult(const std::string& result) override {
EXPECT_EQ("{\"nodeId\":4,\"childNodeCount\":2}", result);
FinishAsynchronousTest();
}
};
HEADLESS_ASYNC_DEVTOOLED_TEST_F(SimpleEventJsBindingsTest);
} // namespace headless
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment