[email protected] | e689367 | 2014-05-01 17:29:13 | [diff] [blame] | 1 | // Copyright 2014 The Chromium Authors. All rights reserved. |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | e689367 | 2014-05-01 17:29:13 | [diff] [blame] | 5 | #include "extensions/renderer/messaging_bindings.h" |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 6 | |
avi | 2d124c0 | 2015-12-23 06:36:42 | [diff] [blame] | 7 | #include <stdint.h> |
| 8 | |
[email protected] | a672cb9 | 2009-12-29 00:28:43 | [diff] [blame] | 9 | #include <map> |
| 10 | #include <string> |
| 11 | |
[email protected] | 09e9d64 | 2013-03-14 17:00:10 | [diff] [blame] | 12 | #include "base/bind.h" |
[email protected] | 204d8ee | 2013-07-24 07:43:06 | [diff] [blame] | 13 | #include "base/bind_helpers.h" |
kalman | 70c00e24 | 2015-05-15 23:42:27 | [diff] [blame] | 14 | #include "base/callback.h" |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 15 | #include "base/callback_helpers.h" |
| 16 | #include "base/lazy_instance.h" |
fdoray | ba12142 | 2016-12-23 19:51:48 | [diff] [blame] | 17 | #include "base/memory/ptr_util.h" |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 18 | #include "base/metrics/histogram_macros.h" |
[email protected] | 686914f | 2013-04-25 04:54:58 | [diff] [blame] | 19 | #include "base/values.h" |
rob | 248d6a8 | 2014-11-21 02:04:48 | [diff] [blame] | 20 | #include "content/public/renderer/render_frame.h" |
[email protected] | 91add6a | 2014-03-21 11:48:22 | [diff] [blame] | 21 | #include "extensions/common/api/messaging/message.h" |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 22 | #include "extensions/common/api/messaging/port_id.h" |
[email protected] | fb820c0 | 2014-03-13 15:07:08 | [diff] [blame] | 23 | #include "extensions/common/extension_messages.h" |
rdevlin.cronin | b05a80f | 2015-10-06 00:58:18 | [diff] [blame] | 24 | #include "extensions/renderer/extension_frame_helper.h" |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 25 | #include "extensions/renderer/extension_port.h" |
kalman | 32d7af2 | 2015-07-24 05:10:59 | [diff] [blame] | 26 | #include "extensions/renderer/gc_callback.h" |
[email protected] | bcd9580f | 2014-04-17 19:17:59 | [diff] [blame] | 27 | #include "extensions/renderer/script_context.h" |
| 28 | #include "extensions/renderer/script_context_set.h" |
bashi | 7b0e3c9 | 2015-06-23 05:19:56 | [diff] [blame] | 29 | #include "extensions/renderer/v8_helpers.h" |
[email protected] | 8865fea | 2013-10-23 01:17:26 | [diff] [blame] | 30 | #include "third_party/WebKit/public/web/WebUserGestureIndicator.h" |
[email protected] | 82fc0f5 | 2011-09-06 23:39:22 | [diff] [blame] | 31 | #include "v8/include/v8.h" |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 32 | |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 33 | // Message passing API example (in a content script): |
[email protected] | 754ea8b7 | 2013-01-08 15:10:31 | [diff] [blame] | 34 | // var port = runtime.connect(); |
[email protected] | a40caa97 | 2009-04-08 18:35:34 | [diff] [blame] | 35 | // port.postMessage('Can you hear me now?'); |
| 36 | // port.onmessage.addListener(function(msg, port) { |
[email protected] | 75e5a87 | 2009-04-02 23:56:11 | [diff] [blame] | 37 | // alert('response=' + msg); |
| 38 | // port.postMessage('I got your reponse'); |
[email protected] | a40caa97 | 2009-04-08 18:35:34 | [diff] [blame] | 39 | // }); |
[email protected] | 75e5a87 | 2009-04-02 23:56:11 | [diff] [blame] | 40 | |
[email protected] | eb7ef5f | 2014-02-06 09:59:19 | [diff] [blame] | 41 | namespace extensions { |
| 42 | |
bashi | 7b0e3c9 | 2015-06-23 05:19:56 | [diff] [blame] | 43 | using v8_helpers::ToV8String; |
bashi | 7b0e3c9 | 2015-06-23 05:19:56 | [diff] [blame] | 44 | |
[email protected] | 75e5a87 | 2009-04-02 23:56:11 | [diff] [blame] | 45 | namespace { |
| 46 | |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 47 | // A global map between ScriptContext and MessagingBindings. |
scottmg | 5e65e3a | 2017-03-08 08:48:46 | [diff] [blame] | 48 | base::LazyInstance<std::map<ScriptContext*, MessagingBindings*>>:: |
| 49 | DestructorAtExit g_messaging_map = LAZY_INSTANCE_INITIALIZER; |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 50 | |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 51 | } // namespace |
| 52 | |
rdevlin.cronin | 4012b3b | 2016-08-31 18:36:47 | [diff] [blame] | 53 | MessagingBindings::MessagingBindings(ScriptContext* context) |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 54 | : ObjectBackedNativeHandler(context), |
| 55 | context_id_(base::UnguessableToken::Create()), |
| 56 | weak_ptr_factory_(this) { |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 57 | g_messaging_map.Get()[context] = this; |
rdevlin.cronin | 4012b3b | 2016-08-31 18:36:47 | [diff] [blame] | 58 | RouteFunction("CloseChannel", base::Bind(&MessagingBindings::CloseChannel, |
| 59 | base::Unretained(this))); |
| 60 | RouteFunction("PostMessage", base::Bind(&MessagingBindings::PostMessage, |
| 61 | base::Unretained(this))); |
| 62 | // TODO(fsamuel, kalman): Move BindToGC out of messaging natives. |
| 63 | RouteFunction("BindToGC", base::Bind(&MessagingBindings::BindToGC, |
| 64 | base::Unretained(this))); |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 65 | RouteFunction("OpenChannelToExtension", "runtime.connect", |
| 66 | base::Bind(&MessagingBindings::OpenChannelToExtension, |
| 67 | base::Unretained(this))); |
| 68 | RouteFunction("OpenChannelToNativeApp", "runtime.connectNative", |
| 69 | base::Bind(&MessagingBindings::OpenChannelToNativeApp, |
| 70 | base::Unretained(this))); |
| 71 | RouteFunction( |
| 72 | "OpenChannelToTab", |
| 73 | base::Bind(&MessagingBindings::OpenChannelToTab, base::Unretained(this))); |
rob | d1db9f6 | 2016-05-25 11:03:10 | [diff] [blame] | 74 | } |
| 75 | |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 76 | MessagingBindings::~MessagingBindings() { |
| 77 | g_messaging_map.Get().erase(context()); |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 78 | if (num_extension_ports_ > 0) { |
rdevlin.cronin | 6942480 | 2016-11-14 22:41:56 | [diff] [blame] | 79 | UMA_HISTOGRAM_COUNTS_1000( |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 80 | "Extensions.Messaging.ExtensionPortsCreated.Total", next_js_id_); |
rdevlin.cronin | 6942480 | 2016-11-14 22:41:56 | [diff] [blame] | 81 | } |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 82 | } |
rdevlin.cronin | 4012b3b | 2016-08-31 18:36:47 | [diff] [blame] | 83 | |
| 84 | // static |
Devlin Cronin | 525e2fd | 2017-09-12 01:19:55 | [diff] [blame] | 85 | MessagingBindings* MessagingBindings::ForContext(ScriptContext* context) { |
| 86 | return g_messaging_map.Get()[context]; |
[email protected] | f9db247 | 2012-04-02 20:42:45 | [diff] [blame] | 87 | } |
| 88 | |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 89 | ExtensionPort* MessagingBindings::GetPortWithId(const PortId& id) { |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 90 | for (const auto& key_value : ports_) { |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 91 | if (key_value.second->id() == id) |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 92 | return key_value.second.get(); |
| 93 | } |
| 94 | return nullptr; |
| 95 | } |
| 96 | |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 97 | ExtensionPort* MessagingBindings::CreateNewPortWithId(const PortId& id) { |
| 98 | int js_id = GetNextJsId(); |
Jeremy Roman | 16529d0e | 2017-08-24 18:13:47 | [diff] [blame] | 99 | auto port = std::make_unique<ExtensionPort>(context(), id, js_id); |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 100 | return ports_.insert(std::make_pair(js_id, std::move(port))) |
| 101 | .first->second.get(); |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 102 | } |
| 103 | |
rdevlin.cronin | 4012b3b | 2016-08-31 18:36:47 | [diff] [blame] | 104 | void MessagingBindings::PostMessage( |
| 105 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 106 | // Arguments are (int32_t port_id, string message). |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 107 | CHECK(args.Length() == 2); |
| 108 | CHECK(args[0]->IsInt32()); |
| 109 | CHECK(args[1]->IsString()); |
rdevlin.cronin | 4012b3b | 2016-08-31 18:36:47 | [diff] [blame] | 110 | |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 111 | int js_port_id = args[0].As<v8::Int32>()->Value(); |
| 112 | auto iter = ports_.find(js_port_id); |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 113 | if (iter != ports_.end()) { |
Jeremy Roman | 16529d0e | 2017-08-24 18:13:47 | [diff] [blame] | 114 | iter->second->PostExtensionMessage(std::make_unique<Message>( |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 115 | *v8::String::Utf8Value(args[1]), |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 116 | blink::WebUserGestureIndicator::IsProcessingUserGesture())); |
rdevlin.cronin | 4012b3b | 2016-08-31 18:36:47 | [diff] [blame] | 117 | } |
| 118 | } |
| 119 | |
| 120 | void MessagingBindings::CloseChannel( |
| 121 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 122 | // Arguments are (int32_t port_id, bool force_close). |
| 123 | CHECK_EQ(2, args.Length()); |
| 124 | CHECK(args[0]->IsInt32()); |
| 125 | CHECK(args[1]->IsBoolean()); |
| 126 | |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 127 | int js_port_id = args[0].As<v8::Int32>()->Value(); |
rdevlin.cronin | 4012b3b | 2016-08-31 18:36:47 | [diff] [blame] | 128 | bool force_close = args[1].As<v8::Boolean>()->Value(); |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 129 | ClosePort(js_port_id, force_close); |
rdevlin.cronin | 4012b3b | 2016-08-31 18:36:47 | [diff] [blame] | 130 | } |
| 131 | |
| 132 | void MessagingBindings::BindToGC( |
| 133 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 134 | CHECK(args.Length() == 3); |
| 135 | CHECK(args[0]->IsObject()); |
| 136 | CHECK(args[1]->IsFunction()); |
| 137 | CHECK(args[2]->IsInt32()); |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 138 | int js_port_id = args[2].As<v8::Int32>()->Value(); |
rdevlin.cronin | 4012b3b | 2016-08-31 18:36:47 | [diff] [blame] | 139 | base::Closure fallback = base::Bind(&base::DoNothing); |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 140 | if (js_port_id >= 0) { |
rdevlin.cronin | 4012b3b | 2016-08-31 18:36:47 | [diff] [blame] | 141 | // TODO(robwu): Falling back to closing the port shouldn't be needed. If |
| 142 | // the script context is destroyed, then the frame has navigated. But that |
| 143 | // is already detected by the browser, so this logic is redundant. Remove |
| 144 | // this fallback (and move BindToGC out of messaging because it is also |
| 145 | // used in other places that have nothing to do with messaging...). |
| 146 | fallback = base::Bind(&MessagingBindings::ClosePort, |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 147 | weak_ptr_factory_.GetWeakPtr(), js_port_id, |
rdevlin.cronin | 4012b3b | 2016-08-31 18:36:47 | [diff] [blame] | 148 | false /* force_close */); |
| 149 | } |
| 150 | // Destroys itself when the object is GC'd or context is invalidated. |
| 151 | new GCCallback(context(), args[0].As<v8::Object>(), |
| 152 | args[1].As<v8::Function>(), fallback); |
| 153 | } |
| 154 | |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 155 | void MessagingBindings::OpenChannelToExtension( |
| 156 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 157 | content::RenderFrame* render_frame = context()->GetRenderFrame(); |
| 158 | if (!render_frame) |
| 159 | return; |
| 160 | |
| 161 | // The Javascript code should validate/fill the arguments. |
| 162 | CHECK_EQ(args.Length(), 3); |
| 163 | CHECK(args[0]->IsString()); |
| 164 | CHECK(args[1]->IsString()); |
| 165 | CHECK(args[2]->IsBoolean()); |
| 166 | |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 167 | int js_id = GetNextJsId(); |
| 168 | PortId port_id(context_id_, js_id, true); |
Jeremy Roman | 16529d0e | 2017-08-24 18:13:47 | [diff] [blame] | 169 | ports_[js_id] = std::make_unique<ExtensionPort>(context(), port_id, js_id); |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 170 | |
| 171 | ExtensionMsg_ExternalConnectionInfo info; |
| 172 | // For messaging APIs, hosted apps should be considered a web page so hide |
| 173 | // its extension ID. |
| 174 | const Extension* extension = context()->extension(); |
| 175 | if (extension && !extension->is_hosted_app()) |
| 176 | info.source_id = extension->id(); |
| 177 | |
| 178 | info.target_id = *v8::String::Utf8Value(args[0]); |
| 179 | info.source_url = context()->url(); |
| 180 | std::string channel_name = *v8::String::Utf8Value(args[1]); |
| 181 | // TODO(devlin): Why is this not part of info? |
| 182 | bool include_tls_channel_id = |
| 183 | args.Length() > 2 ? args[2]->BooleanValue() : false; |
| 184 | |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 185 | { |
| 186 | SCOPED_UMA_HISTOGRAM_TIMER( |
| 187 | "Extensions.Messaging.SetPortIdTime.Extension"); |
| 188 | render_frame->Send(new ExtensionHostMsg_OpenChannelToExtension( |
| 189 | render_frame->GetRoutingID(), info, channel_name, |
| 190 | include_tls_channel_id, port_id)); |
rdevlin.cronin | 6942480 | 2016-11-14 22:41:56 | [diff] [blame] | 191 | } |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 192 | |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 193 | ++num_extension_ports_; |
| 194 | args.GetReturnValue().Set(static_cast<int32_t>(js_id)); |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 195 | } |
| 196 | |
| 197 | void MessagingBindings::OpenChannelToNativeApp( |
| 198 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 199 | // The Javascript code should validate/fill the arguments. |
| 200 | CHECK_EQ(args.Length(), 1); |
| 201 | CHECK(args[0]->IsString()); |
| 202 | // This should be checked by our function routing code. |
| 203 | CHECK(context()->GetAvailability("runtime.connectNative").is_available()); |
| 204 | |
| 205 | content::RenderFrame* render_frame = context()->GetRenderFrame(); |
| 206 | if (!render_frame) |
| 207 | return; |
| 208 | |
| 209 | std::string native_app_name = *v8::String::Utf8Value(args[0]); |
| 210 | |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 211 | int js_id = GetNextJsId(); |
| 212 | PortId port_id(context_id_, js_id, true); |
Jeremy Roman | 16529d0e | 2017-08-24 18:13:47 | [diff] [blame] | 213 | ports_[js_id] = std::make_unique<ExtensionPort>(context(), port_id, js_id); |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 214 | |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 215 | { |
| 216 | SCOPED_UMA_HISTOGRAM_TIMER( |
| 217 | "Extensions.Messaging.SetPortIdTime.NativeApp"); |
| 218 | render_frame->Send(new ExtensionHostMsg_OpenChannelToNativeApp( |
| 219 | render_frame->GetRoutingID(), native_app_name, port_id)); |
| 220 | } |
| 221 | |
| 222 | args.GetReturnValue().Set(static_cast<int32_t>(js_id)); |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 223 | } |
| 224 | |
| 225 | void MessagingBindings::OpenChannelToTab( |
| 226 | const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 227 | content::RenderFrame* render_frame = context()->GetRenderFrame(); |
| 228 | if (!render_frame) |
| 229 | return; |
| 230 | |
| 231 | // tabs_custom_bindings.js unwraps arguments to tabs.connect/sendMessage and |
| 232 | // passes them to OpenChannelToTab, in the following order: |
| 233 | // - |tab_id| - Positive number that specifies the destination of the channel. |
| 234 | // - |frame_id| - Target frame(s) in the tab where onConnect is dispatched: |
| 235 | // -1 for all frames, 0 for the main frame, >0 for a child frame. |
| 236 | // - |extension_id| - ID of the initiating extension. |
| 237 | // - |channel_name| - A user-defined channel name. |
| 238 | CHECK(args.Length() == 4); |
| 239 | CHECK(args[0]->IsInt32()); |
| 240 | CHECK(args[1]->IsInt32()); |
| 241 | CHECK(args[2]->IsString()); |
| 242 | CHECK(args[3]->IsString()); |
| 243 | |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 244 | int js_id = GetNextJsId(); |
| 245 | PortId port_id(context_id_, js_id, true); |
Jeremy Roman | 16529d0e | 2017-08-24 18:13:47 | [diff] [blame] | 246 | ports_[js_id] = std::make_unique<ExtensionPort>(context(), port_id, js_id); |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 247 | |
| 248 | ExtensionMsg_TabTargetConnectionInfo info; |
| 249 | info.tab_id = args[0]->Int32Value(); |
| 250 | info.frame_id = args[1]->Int32Value(); |
rdevlin.cronin | e603762 | 2016-09-13 23:10:43 | [diff] [blame] | 251 | // TODO(devlin): Why is this not part of info? |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 252 | std::string extension_id = *v8::String::Utf8Value(args[2]); |
| 253 | std::string channel_name = *v8::String::Utf8Value(args[3]); |
rdevlin.cronin | e603762 | 2016-09-13 23:10:43 | [diff] [blame] | 254 | |
| 255 | ExtensionFrameHelper* frame_helper = ExtensionFrameHelper::Get(render_frame); |
| 256 | DCHECK(frame_helper); |
rdevlin.cronin | e603762 | 2016-09-13 23:10:43 | [diff] [blame] | 257 | |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 258 | { |
| 259 | SCOPED_UMA_HISTOGRAM_TIMER("Extensions.Messaging.SetPortIdTime.Tab"); |
| 260 | render_frame->Send(new ExtensionHostMsg_OpenChannelToTab( |
| 261 | render_frame->GetRoutingID(), info, extension_id, channel_name, |
| 262 | port_id)); |
| 263 | } |
| 264 | |
| 265 | args.GetReturnValue().Set(static_cast<int32_t>(js_id)); |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 266 | } |
| 267 | |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 268 | void MessagingBindings::ClosePort(int js_port_id, bool force_close) { |
rdevlin.cronin | 4012b3b | 2016-08-31 18:36:47 | [diff] [blame] | 269 | // TODO(robwu): Merge this logic with CloseChannel once the TODO in BindToGC |
| 270 | // has been addressed. |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 271 | auto iter = ports_.find(js_port_id); |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 272 | if (iter != ports_.end()) { |
| 273 | std::unique_ptr<ExtensionPort> port = std::move(iter->second); |
| 274 | ports_.erase(iter); |
| 275 | port->Close(force_close); |
rdevlin.cronin | 4012b3b | 2016-08-31 18:36:47 | [diff] [blame] | 276 | } |
| 277 | } |
| 278 | |
rdevlin.cronin | 8e744a4 | 2016-12-06 18:40:27 | [diff] [blame] | 279 | int MessagingBindings::GetNextJsId() { |
| 280 | return next_js_id_++; |
rdevlin.cronin | dbbe69e7 | 2016-09-10 00:48:02 | [diff] [blame] | 281 | } |
| 282 | |
[email protected] | 8fe74bf | 2012-08-07 21:08:42 | [diff] [blame] | 283 | } // namespace extensions |