Chromium Code Reviews| Index: headless/lib/renderer/headless_content_renderer_client.cc |
| diff --git a/headless/lib/renderer/headless_content_renderer_client.cc b/headless/lib/renderer/headless_content_renderer_client.cc |
| index 5347d6092228090a42332c32e4416f31e25080a8..3613cbff309a4a592325fc351b435160049e4c80 100644 |
| --- a/headless/lib/renderer/headless_content_renderer_client.cc |
| +++ b/headless/lib/renderer/headless_content_renderer_client.cc |
| @@ -4,10 +4,133 @@ |
| #include "headless/lib/renderer/headless_content_renderer_client.h" |
| +#include "base/strings/utf_string_conversions.h" |
| +#include "content/public/renderer/render_frame.h" |
| + |
| namespace headless { |
| HeadlessContentRendererClient::HeadlessContentRendererClient() {} |
| HeadlessContentRendererClient::~HeadlessContentRendererClient() {} |
| +void HeadlessContentRendererClient::RunScriptsAtDocumentStart( |
| + content::RenderFrame* render_frame) { |
| + render_frame->ExecuteJavaScript(base::UTF8ToUTF16(R"( |
| + // Shim to let code use define() instead of mojo.define() |
| + window.define = (function() { |
| + let moduleCache = new Map(); |
| + return function(name, deps, factory) { |
| + let promise = moduleCache.get(name); |
| + if (promise === undefined) { |
| + // This promise must be cached as mojo.define will only call the |
| + // factory function the first time the module is defined. |
| + promise = new Promise(resolve => { |
| + mojo.define(name, deps, (...modules) => { |
| + let result = factory(...modules); |
| + resolve(result); |
| + return result; |
|
Sami
2016/06/20 16:58:16
Did you mention this return could be removed?
alex clarke (OOO till 29th)
2016/06/20 23:19:13
We need the return but the resolve isn't needed.
|
| + }); |
| + }); |
| + moduleCache.set(name, promise); |
| + } |
| + return promise; |
| + } |
| + })(); |
| + |
| + // This code is run before the browser has sent us the mojo bindings so |
| + // we need to get fancy and use nested proxy classes to define a promise |
| + // to an arbitary window.mojo.services.myModule.myInterface. |
| + if (window.hasOwnProperty("mojo")) { |
| + let mojoBindings = new Map(); |
| + let resolvePending = true; |
| + |
| + // Factory for returning the second level dynamic property which |
| + // resolves to a promise to the corresponding mojo inteerface.. |
|
Sami
2016/06/20 16:58:16
typo: interface.
alex clarke (OOO till 29th)
2016/06/20 23:19:13
Done.
alex clarke (OOO till 29th)
2016/06/20 23:19:13
Done.
|
| + let interfaceProxyFactory = function(serviceName) { |
| + return new Proxy({}, { |
| + get: function(target, interfaceName) { |
| + let name = serviceName + "::" + interfaceName; |
| + let binding = mojoBindings.get(name); |
| + if (binding === undefined) { |
| + binding = {}; |
| + binding.promise = new Promise(function(resolve, reject) { |
| + binding.resolve = resolve; |
| + binding.reject = reject; |
| + if (!resolvePending) |
| + reject(); |
| + }); |
| + mojoBindings.set(name, binding); |
| + } |
| + return binding.promise; |
| + }, |
| + set: function(target, name, value) { |
| + return false; |
|
Sami
2016/06/20 16:58:16
Should we throw an Error here?
alex clarke (OOO till 29th)
2016/06/21 09:16:45
Done.
|
| + } |
| + }); |
| + }; |
| + |
| + // Top level dynamic property. |
| + window.mojo.services = new Proxy({}, { |
| + get: function(target, serviceName) { |
| + if (!(serviceName in target)) { |
| + let interfaceProxy = interfaceProxyFactory(serviceName); |
| + target[serviceName] = interfaceProxy; |
| + return interfaceProxy; |
| + } |
| + return target[serviceName]; |
| + }, |
| + set: function(target, name, value) { |
| + return false; |
|
Sami
2016/06/20 16:58:16
Ditto.
alex clarke (OOO till 29th)
2016/06/21 09:16:45
Done.
|
| + } |
| + }); |
| + |
| + // Resolve promises for the listed |serviceNames|. |
| + window.mojo.resolvePromisesForServices = function(serviceNames) { |
|
Sami
2016/06/20 16:58:16
Should we call this resolvePromisesForServices_ si
alex clarke (OOO till 29th)
2016/06/20 23:19:13
Done.
|
| + let numServices = serviceNames.length; |
| + for (let i = 0; i < numServices; ++i) { |
| + let serviceName = serviceNames[i]; |
| + // Use mojo to obtain the module binding. |
| + define([ |
| + serviceName, |
| + "mojo/public/js/core", |
| + "mojo/public/js/router", |
| + "content/public/renderer/frame_service_registry", |
| + ], function(serviceMojom, mojoCore, routerModule, |
| + serviceProvider) { |
| + // A mojom binding may contain bindings for a number of |
| + // interfaces. Iterate through all of them and resolve any |
| + // promises. |
| + for (var m in serviceMojom) { |
|
Sami
2016/06/20 16:58:16
s/var/let/
Also maybe "for..of" would be more app
alex clarke (OOO till 29th)
2016/06/20 23:19:13
Done.
alex clarke (OOO till 29th)
2016/06/20 23:19:13
Done.
Sami
2016/06/21 10:22:26
(Did you mean to leave the "in" here? Sometimes it
alex clarke (OOO till 29th)
2016/06/21 10:38:37
for..of only works for iterables but serviceMojom
|
| + if (typeof serviceMojom[m] == "object") { |
| + let service = serviceMojom[m]; |
| + let binding = mojoBindings.get(service.name); |
| + let interface = new service.proxyClass( |
| + new routerModule.Router( |
| + serviceProvider.connectToService(service.name))); |
| + if (binding === undefined) { |
| + // Store resolved promise in case binding is requested. |
| + mojoBindings.set( |
| + service.name, {"promise": Promise.resolve(interface)}); |
| + } else { |
| + binding.resolve(interface); |
| + // Since this promise has been resolved we don't want to try |
| + // and reject it below! |
| + delete binding.reject; |
| + } |
| + } |
| + } |
| + }); |
| + } |
| + // Reject any remaining promises that didn't get resolved. |
| + for (let [key, value] of mojoBindings) { |
| + if ("reject" in value) { |
| + value.reject(); |
| + } |
| + } |
| + // Reject any subsequent unknown properties. |
| + resolvePending = false; |
| + }; |
| + } )")); |
| +} |
| + |
| } // namespace headless |