1a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
24e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
34e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// found in the LICENSE file.
44e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
5a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.h"
64e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
71320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/lazy_instance.h"
8a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "extensions/browser/api/socket/tcp_socket.h"
9f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/browser/event_router.h"
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_system.h"
11a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "extensions/browser/extensions_browser_client.h"
124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "net/base/net_errors.h"
134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)namespace {
154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)int kDefaultBufferSize = 4096;
164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)namespace extensions {
19a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace core_api {
204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)using content::BrowserThread;
224e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
23a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)static base::LazyInstance<
24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    BrowserContextKeyedAPIFactory<TCPSocketEventDispatcher> > g_factory =
25a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    LAZY_INSTANCE_INITIALIZER;
264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// static
28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)BrowserContextKeyedAPIFactory<TCPSocketEventDispatcher>*
29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)TCPSocketEventDispatcher::GetFactoryInstance() {
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return g_factory.Pointer();
314e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// static
34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)TCPSocketEventDispatcher* TCPSocketEventDispatcher::Get(
35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    content::BrowserContext* context) {
36effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(BrowserThread::UI);
374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return BrowserContextKeyedAPIFactory<TCPSocketEventDispatcher>::Get(context);
394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
41a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)TCPSocketEventDispatcher::TCPSocketEventDispatcher(
42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    content::BrowserContext* context)
43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    : thread_id_(Socket::kThreadId), browser_context_(context) {
444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  ApiResourceManager<ResumableTCPSocket>* manager =
45a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      ApiResourceManager<ResumableTCPSocket>::Get(browser_context_);
46a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK(manager)
47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      << "There is no socket manager. "
48a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         "If this assertion is failing during a test, then it is likely that "
49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         "TestExtensionSystem is failing to provide an instance of "
50a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         "ApiResourceManager<ResumableTCPSocket>.";
514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  sockets_ = manager->data_;
524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)TCPSocketEventDispatcher::~TCPSocketEventDispatcher() {}
554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)TCPSocketEventDispatcher::ReadParams::ReadParams() {}
574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)TCPSocketEventDispatcher::ReadParams::~ReadParams() {}
594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void TCPSocketEventDispatcher::OnSocketConnect(const std::string& extension_id,
614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                               int socket_id) {
62effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(thread_id_);
634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  StartSocketRead(extension_id, socket_id);
654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void TCPSocketEventDispatcher::OnSocketResume(const std::string& extension_id,
684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                              int socket_id) {
69effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(thread_id_);
704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
714e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  StartSocketRead(extension_id, socket_id);
724e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void TCPSocketEventDispatcher::StartSocketRead(const std::string& extension_id,
754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                               int socket_id) {
76effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(thread_id_);
774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  ReadParams params;
794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  params.thread_id = thread_id_;
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  params.browser_context_id = browser_context_;
814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  params.extension_id = extension_id;
824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  params.sockets = sockets_;
834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  params.socket_id = socket_id;
844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  StartRead(params);
864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// static
894e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void TCPSocketEventDispatcher::StartRead(const ReadParams& params) {
90effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(params.thread_id);
914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  ResumableTCPSocket* socket =
934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      params.sockets->Get(params.extension_id, params.socket_id);
944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (!socket) {
954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // This can happen if the socket is closed while our callback is active.
964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return;
974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  DCHECK(params.extension_id == socket->owner_extension_id())
99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      << "Socket has wrong owner.";
1004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // Don't start another read if the socket has been paused.
1024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (socket->paused())
1034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return;
1044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  int buffer_size = socket->buffer_size();
1064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (buffer_size <= 0)
1074e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    buffer_size = kDefaultBufferSize;
1084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  socket->Read(buffer_size,
109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)               base::Bind(&TCPSocketEventDispatcher::ReadCallback, params));
1104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
1114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// static
1134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void TCPSocketEventDispatcher::ReadCallback(
1144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const ReadParams& params,
1154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    int bytes_read,
1164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    scoped_refptr<net::IOBuffer> io_buffer) {
117effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(params.thread_id);
1184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1198bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // If |bytes_read| == 0, the connection has been closed by the peer.
1208bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // If |bytes_read| < 0, there was a network error, and |bytes_read| is a value
1218bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // from "net::ERR_".
1224e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1238bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (bytes_read == 0) {
1248bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    bytes_read = net::ERR_CONNECTION_CLOSED;
1258bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  }
1268bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1278bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (bytes_read > 0) {
1284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // Dispatch "onReceive" event.
1294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    sockets_tcp::ReceiveInfo receive_info;
1304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    receive_info.socket_id = params.socket_id;
1314e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    receive_info.data = std::string(io_buffer->data(), bytes_read);
1324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    scoped_ptr<base::ListValue> args =
1334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        sockets_tcp::OnReceive::Create(receive_info);
1344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    scoped_ptr<Event> event(
135a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        new Event(sockets_tcp::OnReceive::kEventName, args.Pass()));
1364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    PostEvent(params, event.Pass());
1374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // Post a task to delay the read until the socket is available, as
1394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // calling StartReceive at this point would error with ERR_IO_PENDING.
1404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    BrowserThread::PostTask(
141a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        params.thread_id,
142a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        FROM_HERE,
1434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        base::Bind(&TCPSocketEventDispatcher::StartRead, params));
1444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  } else if (bytes_read == net::ERR_IO_PENDING) {
1454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // This happens when resuming a socket which already had an
1464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // active "read" callback.
1474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  } else {
1484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // Dispatch "onReceiveError" event but don't start another read to avoid
1494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // potential infinite reads if we have a persistent network error.
1504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    sockets_tcp::ReceiveErrorInfo receive_error_info;
1514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    receive_error_info.socket_id = params.socket_id;
1524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    receive_error_info.result_code = bytes_read;
1534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    scoped_ptr<base::ListValue> args =
1544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        sockets_tcp::OnReceiveError::Create(receive_error_info);
1554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    scoped_ptr<Event> event(
156a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        new Event(sockets_tcp::OnReceiveError::kEventName, args.Pass()));
1574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    PostEvent(params, event.Pass());
1584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // Since we got an error, the socket is now "paused" until the application
1604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // "resumes" it.
1614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    ResumableTCPSocket* socket =
1624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        params.sockets->Get(params.extension_id, params.socket_id);
1634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    if (socket) {
1644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      socket->set_paused(true);
1654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
1664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
1674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
1684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// static
1704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void TCPSocketEventDispatcher::PostEvent(const ReadParams& params,
1714e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                         scoped_ptr<Event> event) {
172effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(params.thread_id);
1734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
174a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  BrowserThread::PostTask(BrowserThread::UI,
175a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                          FROM_HERE,
176a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                          base::Bind(&DispatchEvent,
177a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                     params.browser_context_id,
178a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                     params.extension_id,
179a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                     base::Passed(event.Pass())));
1804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
1814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// static
183a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void TCPSocketEventDispatcher::DispatchEvent(void* browser_context_id,
1844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                             const std::string& extension_id,
1854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                             scoped_ptr<Event> event) {
186effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(BrowserThread::UI);
1874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
188a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  content::BrowserContext* context =
189a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      reinterpret_cast<content::BrowserContext*>(browser_context_id);
190a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(context))
1914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return;
1924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1930529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  EventRouter* event_router = EventRouter::Get(context);
1940529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  if (event_router)
1950529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    event_router->DispatchEventToExtension(extension_id, event.Pass());
1964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
1974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
198a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}  // namespace core_api
1994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}  // namespace extensions
200