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..ce98c4ecc4caa5191add7024f430572472c0b8e0 100644 |
--- a/headless/lib/renderer/headless_content_renderer_client.cc |
+++ b/headless/lib/renderer/headless_content_renderer_client.cc |
@@ -4,10 +4,134 @@ |
#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); |
+ return result; |
+ }); |
+ }); |
+ 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 interface... |
+ 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) { |
+ throw new Error( |
+ 'Assignment to the mojo services proxy is not allowed'); |
+ } |
+ }); |
+ }; |
+ |
+ // 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) { |
+ throw new Error( |
+ 'Assignment to the mojo services proxy is not allowed'); |
+ } |
+ }); |
+ |
+ // Resolve promises for the listed |serviceNames|. |
+ window.mojo.resolvePromisesForServices_ = function(serviceNames) { |
+ 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 (let m in 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 |