summaryrefslogtreecommitdiffstats
path: root/extensions/renderer
diff options
context:
space:
mode:
authormikhail.pozdnyakov <mikhail.pozdnyakov@intel.com>2015-12-09 04:09:40 -0800
committerCommit bot <commit-bot@chromium.org>2015-12-09 12:10:32 +0000
commit79486f1c04861a577489fa63b1f82342aaee095b (patch)
tree824933762da51d3ad91d77ec8639d6c96b299bf5 /extensions/renderer
parentaae35e589d9df6d5111cf658e69a82faa1ebed3e (diff)
downloadchromium_src-79486f1c04861a577489fa63b1f82342aaee095b.zip
chromium_src-79486f1c04861a577489fa63b1f82342aaee095b.tar.gz
chromium_src-79486f1c04861a577489fa63b1f82342aaee095b.tar.bz2
chrome.displaySource custom bindings
This patch introduces custom bindings for 'chrome.displaySource' API. These are bindings for 'startSession', 'terminateSession' methods and for the 'onSessionStarted', 'onSessionTerminated', 'onSessionErrorOccured' events. The bindings should belong to render process (i.e. be custom) as: 1) they accept dom objects arguments (MediaStreamTrack) 2) for security reasons: to keep all protocols handling within sandbox The abstract 'DisplaySourceSession' class is added to be implemented by the 'chrome.displaySource' API backends. BUG=242107 Review URL: https://codereview.chromium.org/1471243002 Cr-Commit-Position: refs/heads/master@{#364045}
Diffstat (limited to 'extensions/renderer')
-rw-r--r--extensions/renderer/api/display_source/OWNERS2
-rw-r--r--extensions/renderer/api/display_source/display_source_session.cc37
-rw-r--r--extensions/renderer/api/display_source/display_source_session.h94
-rw-r--r--extensions/renderer/dispatcher.cc7
-rw-r--r--extensions/renderer/display_source_custom_bindings.cc258
-rw-r--r--extensions/renderer/display_source_custom_bindings.h56
-rw-r--r--extensions/renderer/resources/display_source_custom_bindings.js41
-rw-r--r--extensions/renderer/resources/extensions_renderer_resources.grd1
8 files changed, 496 insertions, 0 deletions
diff --git a/extensions/renderer/api/display_source/OWNERS b/extensions/renderer/api/display_source/OWNERS
new file mode 100644
index 0000000..f7e4f6f
--- /dev/null
+++ b/extensions/renderer/api/display_source/OWNERS
@@ -0,0 +1,2 @@
+alexander.shalamov@intel.com
+mikhail.pozdnyakov@intel.com
diff --git a/extensions/renderer/api/display_source/display_source_session.cc b/extensions/renderer/api/display_source/display_source_session.cc
new file mode 100644
index 0000000..8e66b6f
--- /dev/null
+++ b/extensions/renderer/api/display_source/display_source_session.cc
@@ -0,0 +1,37 @@
+// 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 "extensions/renderer/api/display_source/display_source_session.h"
+
+namespace extensions {
+
+DisplaySourceSession::DisplaySourceSession()
+ : state_(Idle) {
+}
+
+DisplaySourceSession::~DisplaySourceSession() {
+}
+
+void DisplaySourceSession::SetCallbacks(
+ const SinkIdCallback& started_callback,
+ const SinkIdCallback& terminated_callback,
+ const ErrorCallback& error_callback) {
+ DCHECK(started_callback_.is_null());
+ DCHECK(terminated_callback_.is_null());
+ DCHECK(error_callback_.is_null());
+
+ started_callback_ = started_callback;
+ terminated_callback_ = terminated_callback;
+ error_callback_ = error_callback;
+}
+
+scoped_ptr<DisplaySourceSession> DisplaySourceSessionFactory::CreateSession(
+ int sink_id,
+ const blink::WebMediaStreamTrack& video_track,
+ const blink::WebMediaStreamTrack& audio_track,
+ scoped_ptr<DisplaySourceAuthInfo> auth_info) {
+ return nullptr;
+}
+
+} // namespace extensions
diff --git a/extensions/renderer/api/display_source/display_source_session.h b/extensions/renderer/api/display_source/display_source_session.h
new file mode 100644
index 0000000..b10dce9
--- /dev/null
+++ b/extensions/renderer/api/display_source/display_source_session.h
@@ -0,0 +1,94 @@
+// 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 EXTENSIONS_RENDERER_API_DISPLAY_SOURCE_DISPLAY_SOURCE_SESSION_H_
+#define EXTENSIONS_RENDERER_API_DISPLAY_SOURCE_DISPLAY_SOURCE_SESSION_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "extensions/common/api/display_source.h"
+#include "third_party/WebKit/public/web/WebDOMMediaStreamTrack.h"
+
+namespace extensions {
+
+using DisplaySourceAuthInfo = api::display_source::AuthenticationInfo;
+using DisplaySourceErrorType = api::display_source::ErrorType;
+
+// This class represents a generic display source session interface.
+class DisplaySourceSession {
+ public:
+ using SinkIdCallback = base::Callback<void(int sink_id)>;
+ using ErrorCallback =
+ base::Callback<void(int sink_id,
+ DisplaySourceErrorType error_type,
+ const std::string& error_description)>;
+
+ // State flow is ether:
+ // 'Idle' -> 'Establishing' -> 'Established' -> 'Terminating' -> 'Idle'
+ // (terminated by Terminate() call)
+ // or
+ // 'Idle' -> 'Establishing' -> 'Established' -> 'Idle'
+ // (terminated from sink device or due to an error)
+ enum State {
+ Idle,
+ Establishing,
+ Established,
+ Terminating
+ };
+
+ virtual ~DisplaySourceSession();
+
+ // Starts the session.
+ // The session state should be set to 'Establishing' immediately after this
+ // method is called.
+ virtual void Start() = 0;
+
+ // Terminates the session.
+ // The session state should be set to 'Terminating' immediately after this
+ // method is called.
+ virtual void Terminate() = 0;
+
+ State state() const { return state_; }
+
+ // Sets the callbacks invoked to inform about the session's state changes.
+ // It is required to set the callbacks before the session is started.
+ // |started_callback| : Called when the session was actually started (state
+ // should be set to 'Established')
+ // |terminated_callback| : Called when the session was actually started (state
+ // should be set to 'Idle')
+ // |error_callback| : Called if a fatal error has occured and the session
+ // either cannot be started (if was invoked in
+ // 'Establishing' state) or will be terminated soon for
+ // emergency reasons (if was invoked in 'Established'
+ // state).
+ void SetCallbacks(const SinkIdCallback& started_callback,
+ const SinkIdCallback& terminated_callback,
+ const ErrorCallback& error_callback);
+
+ protected:
+ DisplaySourceSession();
+
+ State state_;
+ SinkIdCallback started_callback_;
+ SinkIdCallback terminated_callback_;
+ ErrorCallback error_callback_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DisplaySourceSession);
+};
+
+class DisplaySourceSessionFactory {
+ public:
+ static scoped_ptr<DisplaySourceSession> CreateSession(
+ int sink_id,
+ const blink::WebMediaStreamTrack& video_track,
+ const blink::WebMediaStreamTrack& audio_track,
+ scoped_ptr<DisplaySourceAuthInfo> auth_info);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DisplaySourceSessionFactory);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_RENDERER_API_DISPLAY_SOURCE_DISPLAY_SOURCE_SESSION_H_
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index 83fd54d..b6e3a1e 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -55,6 +55,7 @@
#include "extensions/renderer/context_menus_custom_bindings.h"
#include "extensions/renderer/css_native_handler.h"
#include "extensions/renderer/dispatcher_delegate.h"
+#include "extensions/renderer/display_source_custom_bindings.h"
#include "extensions/renderer/document_custom_bindings.h"
#include "extensions/renderer/dom_activity_logger.h"
#include "extensions/renderer/event_bindings.h"
@@ -681,6 +682,9 @@ std::vector<std::pair<std::string, int> > Dispatcher::GetJsResources() {
std::make_pair("declarativeWebRequest",
IDR_DECLARATIVE_WEBREQUEST_CUSTOM_BINDINGS_JS));
resources.push_back(
+ std::make_pair("displaySource",
+ IDR_DISPLAY_SOURCE_CUSTOM_BINDINGS_JS));
+ resources.push_back(
std::make_pair("contextMenus", IDR_CONTEXT_MENUS_CUSTOM_BINDINGS_JS));
resources.push_back(
std::make_pair("contextMenusHandlers", IDR_CONTEXT_MENUS_HANDLERS_JS));
@@ -824,6 +828,9 @@ void Dispatcher::RegisterNativeHandlers(ModuleSystem* module_system,
scoped_ptr<NativeHandler>(new IdGeneratorCustomBindings(context)));
module_system->RegisterNativeHandler(
"runtime", scoped_ptr<NativeHandler>(new RuntimeCustomBindings(context)));
+ module_system->RegisterNativeHandler(
+ "display_source",
+ scoped_ptr<NativeHandler>(new DisplaySourceCustomBindings(context)));
}
bool Dispatcher::OnControlMessageReceived(const IPC::Message& message) {
diff --git a/extensions/renderer/display_source_custom_bindings.cc b/extensions/renderer/display_source_custom_bindings.cc
new file mode 100644
index 0000000..059d2bb
--- /dev/null
+++ b/extensions/renderer/display_source_custom_bindings.cc
@@ -0,0 +1,258 @@
+// 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 "extensions/renderer/display_source_custom_bindings.h"
+
+#include "base/bind.h"
+#include "content/public/child/v8_value_converter.h"
+#include "extensions/renderer/script_context.h"
+#include "third_party/WebKit/public/platform/WebMediaStream.h"
+#include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
+#include "third_party/WebKit/public/web/WebDOMMediaStreamTrack.h"
+#include "v8/include/v8.h"
+
+namespace extensions {
+
+using content::V8ValueConverter;
+
+namespace {
+const char kErrorNotSupported[] = "Not supported";
+const char kInvalidStreamArgs[] = "Invalid stream arguments";
+const char kSessionAlreadyStarted[] = "The session has been already started";
+const char kSessionAlreadyTerminating[] = "The session is already terminating";
+const char kSessionNotFound[] = "Session not found";
+} // namespace
+
+DisplaySourceCustomBindings::DisplaySourceCustomBindings(ScriptContext* context)
+ : ObjectBackedNativeHandler(context),
+ weak_factory_(this) {
+ RouteFunction("StartSession",
+ base::Bind(&DisplaySourceCustomBindings::StartSession,
+ weak_factory_.GetWeakPtr()));
+ RouteFunction("TerminateSession",
+ base::Bind(&DisplaySourceCustomBindings::TerminateSession,
+ weak_factory_.GetWeakPtr()));
+}
+
+DisplaySourceCustomBindings::~DisplaySourceCustomBindings() {
+}
+
+void DisplaySourceCustomBindings::Invalidate() {
+ session_map_.clear();
+ weak_factory_.InvalidateWeakPtrs();
+ ObjectBackedNativeHandler::Invalidate();
+}
+
+namespace {
+
+v8::Local<v8::Value> GetChildValue(v8::Local<v8::Object> value,
+ const std::string& key_name,
+ v8::Isolate* isolate) {
+ v8::Local<v8::Array> property_names(value->GetOwnPropertyNames());
+ for (uint32 i = 0; i < property_names->Length(); ++i) {
+ v8::Local<v8::Value> key(property_names->Get(i));
+ if (key_name == *v8::String::Utf8Value(key)) {
+ v8::TryCatch try_catch(isolate);
+ v8::Local<v8::Value> child_v8 = value->Get(key);
+ if (try_catch.HasCaught()) {
+ return v8::Null(isolate);
+ }
+ return child_v8;
+ }
+ }
+
+ return v8::Null(isolate);
+}
+
+} // namespace
+
+void DisplaySourceCustomBindings::StartSession(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ CHECK_EQ(1, args.Length());
+ CHECK(args[0]->IsObject());
+ v8::Isolate* isolate = context()->isolate();
+ v8::Local<v8::Object> start_info = args[0].As<v8::Object>();
+
+ v8::Local<v8::Value> sink_id_val =
+ GetChildValue(start_info, "sinkId", isolate);
+ CHECK(sink_id_val->IsInt32());
+ const int sink_id = sink_id_val->ToInt32(isolate)->Value();
+ if (GetDisplaySession(sink_id)) {
+ isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(
+ isolate, kSessionAlreadyStarted)));
+ return;
+ }
+
+ v8::Local<v8::Value> video_stream_val =
+ GetChildValue(start_info, "videoTrack", isolate);
+ v8::Local<v8::Value> audio_stream_val =
+ GetChildValue(start_info, "audioTrack", isolate);
+
+ if ((video_stream_val->IsNull() || video_stream_val->IsUndefined()) &&
+ (audio_stream_val->IsNull() || audio_stream_val->IsUndefined())) {
+ isolate->ThrowException(v8::Exception::Error(
+ v8::String::NewFromUtf8(isolate, kInvalidStreamArgs)));
+ return;
+ }
+
+ blink::WebMediaStreamTrack audio_track, video_track;
+
+ if (!video_stream_val->IsNull() && !video_stream_val->IsUndefined()) {
+ CHECK(video_stream_val->IsObject());
+ video_track =
+ blink::WebDOMMediaStreamTrack::fromV8Value(
+ video_stream_val).component();
+ if (video_track.isNull()) {
+ isolate->ThrowException(v8::Exception::Error(
+ v8::String::NewFromUtf8(isolate, kInvalidStreamArgs)));
+ return;
+ }
+ }
+ if (!audio_stream_val->IsNull() && !audio_stream_val->IsUndefined()) {
+ CHECK(audio_stream_val->IsObject());
+ audio_track =
+ blink::WebDOMMediaStreamTrack::fromV8Value(
+ audio_stream_val).component();
+ if (audio_track.isNull()) {
+ isolate->ThrowException(v8::Exception::Error(
+ v8::String::NewFromUtf8(isolate, kInvalidStreamArgs)));
+ return;
+ }
+ }
+
+ scoped_ptr<DisplaySourceAuthInfo> auth_info;
+ v8::Local<v8::Value> auth_info_v8_val =
+ GetChildValue(start_info, "authenticationInfo", isolate);
+ if (!auth_info_v8_val->IsNull()) {
+ CHECK(auth_info_v8_val->IsObject());
+ scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
+ scoped_ptr<base::Value> auth_info_val(
+ converter->FromV8Value(auth_info_v8_val, context()->v8_context()));
+ CHECK(auth_info_val);
+ auth_info = DisplaySourceAuthInfo::FromValue(*auth_info_val);
+ }
+
+ scoped_ptr<DisplaySourceSession> session =
+ DisplaySourceSessionFactory::CreateSession(
+ sink_id, video_track, audio_track, std::move(auth_info));
+ if (!session) {
+ isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(
+ isolate, kErrorNotSupported)));
+ return;
+ }
+
+ auto on_started_callback = base::Bind(
+ &DisplaySourceCustomBindings::OnSessionStarted,
+ weak_factory_.GetWeakPtr());
+ auto on_terminated_callback = base::Bind(
+ &DisplaySourceCustomBindings::OnSessionTerminated,
+ weak_factory_.GetWeakPtr());
+ auto on_error_callback = base::Bind(
+ &DisplaySourceCustomBindings::OnSessionError,
+ weak_factory_.GetWeakPtr());
+ session->SetCallbacks(on_started_callback,
+ on_terminated_callback,
+ on_error_callback);
+ session_map_.insert(std::make_pair(sink_id, std::move(session)));
+ session->Start();
+}
+
+void DisplaySourceCustomBindings::TerminateSession(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ CHECK_EQ(1, args.Length());
+ CHECK(args[0]->IsInt32());
+
+ v8::Isolate* isolate = context()->isolate();
+ int sink_id = args[0]->ToInt32(args.GetIsolate())->Value();
+ DisplaySourceSession* session = GetDisplaySession(sink_id);
+ if (!session) {
+ isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(
+ isolate, kSessionNotFound)));
+ return;
+ }
+
+ if (session->state() == DisplaySourceSession::Terminating) {
+ isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(
+ isolate, kSessionAlreadyTerminating)));
+ return;
+ }
+ // The session will get removed from session_map_ in OnSessionTerminated.
+ session->Terminate();
+}
+
+void DisplaySourceCustomBindings::DispatchSessionStarted(int sink_id) const {
+ v8::Isolate* isolate = context()->isolate();
+ v8::HandleScope handle_scope(isolate);
+ v8::Context::Scope context_scope(context()->v8_context());
+ v8::Local<v8::Array> event_args = v8::Array::New(isolate, 1);
+ event_args->Set(0, v8::Integer::New(isolate, sink_id));
+ context()->DispatchEvent("displaySource.onSessionStarted", event_args);
+}
+
+void DisplaySourceCustomBindings::DispatchSessionTerminated(int sink_id) const {
+ v8::Isolate* isolate = context()->isolate();
+ v8::HandleScope handle_scope(isolate);
+ v8::Context::Scope context_scope(context()->v8_context());
+ v8::Local<v8::Array> event_args = v8::Array::New(isolate, 1);
+ event_args->Set(0, v8::Integer::New(isolate, sink_id));
+ context()->DispatchEvent("displaySource.onSessionTerminated", event_args);
+}
+
+void DisplaySourceCustomBindings::DispatchSessionError(
+ int sink_id,
+ DisplaySourceErrorType type,
+ const std::string& message) const {
+ v8::Isolate* isolate = context()->isolate();
+ v8::HandleScope handle_scope(isolate);
+ v8::Context::Scope context_scope(context()->v8_context());
+
+ api::display_source::ErrorInfo error_info;
+ error_info.type = type;
+ if (!message.empty())
+ error_info.description.reset(new std::string(message));
+
+ scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
+ v8::Local<v8::Value> info_arg =
+ converter->ToV8Value(error_info.ToValue().get(),
+ context()->v8_context());
+
+ v8::Local<v8::Array> event_args = v8::Array::New(isolate, 2);
+ event_args->Set(0, v8::Integer::New(isolate, sink_id));
+ event_args->Set(1, info_arg);
+ context()->DispatchEvent("displaySource.onSessionErrorOccured", event_args);
+}
+
+DisplaySourceSession* DisplaySourceCustomBindings::GetDisplaySession(
+ int sink_id) const {
+ auto iter = session_map_.find(sink_id);
+ if (iter != session_map_.end())
+ return iter->second.get();
+ return nullptr;
+}
+
+void DisplaySourceCustomBindings::OnSessionStarted(int sink_id) {
+ DispatchSessionStarted(sink_id);
+}
+
+void DisplaySourceCustomBindings::OnSessionTerminated(int sink_id) {
+ DisplaySourceSession* session = GetDisplaySession(sink_id);
+ CHECK(session);
+ session_map_.erase(sink_id);
+ DispatchSessionTerminated(sink_id);
+}
+
+void DisplaySourceCustomBindings::OnSessionError(int sink_id,
+ DisplaySourceErrorType type,
+ const std::string& message) {
+ DisplaySourceSession* session = GetDisplaySession(sink_id);
+ CHECK(session);
+ if (session->state() == DisplaySourceSession::Establishing) {
+ // Error has occured before the session has actually started.
+ session_map_.erase(sink_id);
+ }
+
+ DispatchSessionError(sink_id, type, message);
+}
+
+} // extensions
diff --git a/extensions/renderer/display_source_custom_bindings.h b/extensions/renderer/display_source_custom_bindings.h
new file mode 100644
index 0000000..199f199
--- /dev/null
+++ b/extensions/renderer/display_source_custom_bindings.h
@@ -0,0 +1,56 @@
+// 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 EXTENSIONS_RENDERER_DISPLAY_SOURCE_CUSTOM_BINDINGS_H_
+#define EXTENSIONS_RENDERER_DISPLAY_SOURCE_CUSTOM_BINDINGS_H_
+
+#include "base/macros.h"
+#include "extensions/common/api/display_source.h"
+#include "extensions/renderer/api/display_source/display_source_session.h"
+#include "extensions/renderer/object_backed_native_handler.h"
+#include "v8/include/v8.h"
+
+namespace extensions {
+class ScriptContext;
+
+// Implements custom bindings for the displaySource API.
+class DisplaySourceCustomBindings : public ObjectBackedNativeHandler {
+ public:
+ explicit DisplaySourceCustomBindings(ScriptContext* context);
+
+ ~DisplaySourceCustomBindings() override;
+
+ private:
+ // ObjectBackedNativeHandler override.
+ void Invalidate() override;
+
+ void StartSession(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+ void TerminateSession(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ void DispatchSessionStarted(int sink_id) const;
+ void DispatchSessionTerminated(int sink_id) const;
+ void DispatchSessionError(int sink_id,
+ DisplaySourceErrorType type,
+ const std::string& message) const;
+
+ // DisplaySession callbacks.
+ void OnSessionStarted(int sink_id);
+ void OnSessionTerminated(int sink_id);
+ void OnSessionError(int sink_id,
+ DisplaySourceErrorType type,
+ const std::string& message);
+
+ DisplaySourceSession* GetDisplaySession(int sink_id) const;
+
+ std::map<int, scoped_ptr<DisplaySourceSession>> session_map_;
+ base::WeakPtrFactory<DisplaySourceCustomBindings> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DisplaySourceCustomBindings);
+};
+
+} // extensions
+
+#endif // EXTENSIONS_RENDERER_DISPLAY_SOURCE_CUSTOM_BINDINGS_H_
diff --git a/extensions/renderer/resources/display_source_custom_bindings.js b/extensions/renderer/resources/display_source_custom_bindings.js
new file mode 100644
index 0000000..13e8167
--- /dev/null
+++ b/extensions/renderer/resources/display_source_custom_bindings.js
@@ -0,0 +1,41 @@
+// 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.
+
+// Custom binding for the Display Source API.
+
+var binding = require('binding').Binding.create('displaySource');
+var chrome = requireNative('chrome').GetChrome();
+var lastError = require('lastError');
+var natives = requireNative('display_source');
+
+binding.registerCustomHook(function(bindingsAPI, extensionId) {
+ var apiFunctions = bindingsAPI.apiFunctions;
+ apiFunctions.setHandleRequest('startSession',
+ function(sessionInfo, callback) {
+ try {
+ natives.StartSession(sessionInfo);
+ } catch (e) {
+ lastError.set('displaySource.startSession', e.message, null, chrome);
+ } finally {
+ if (callback !== undefined)
+ callback();
+ lastError.clear(chrome);
+ }
+ });
+ apiFunctions.setHandleRequest('terminateSession',
+ function(sink_id, callback) {
+ try {
+ natives.TerminateSession(sink_id);
+ } catch (e) {
+ lastError.set(
+ 'displaySource.terminateSession', e.message, null, chrome);
+ } finally {
+ if (callback !== undefined)
+ callback();
+ lastError.clear(chrome);
+ }
+ });
+});
+
+exports.$set('binding', binding.generate());
diff --git a/extensions/renderer/resources/extensions_renderer_resources.grd b/extensions/renderer/resources/extensions_renderer_resources.grd
index 8a08705..bad8e29 100644
--- a/extensions/renderer/resources/extensions_renderer_resources.grd
+++ b/extensions/renderer/resources/extensions_renderer_resources.grd
@@ -72,6 +72,7 @@
<include name="IDR_CONTEXT_MENUS_CUSTOM_BINDINGS_JS" file="context_menus_custom_bindings.js" type="BINDATA" />
<include name="IDR_CONTEXT_MENUS_HANDLERS_JS" file="context_menus_handlers.js" type="BINDATA" />
<include name="IDR_DECLARATIVE_WEBREQUEST_CUSTOM_BINDINGS_JS" file="declarative_webrequest_custom_bindings.js" type="BINDATA" />
+ <include name="IDR_DISPLAY_SOURCE_CUSTOM_BINDINGS_JS" file="display_source_custom_bindings.js" type="BINDATA" />
<include name="IDR_EXTENSION_CUSTOM_BINDINGS_JS" file="extension_custom_bindings.js" type="BINDATA" />
<include name="IDR_GREASEMONKEY_API_JS" file="greasemonkey_api.js" type="BINDATA" />
<include name="IDR_I18N_CUSTOM_BINDINGS_JS" file="i18n_custom_bindings.js" type="BINDATA" />