diff options
author | miket@chromium.org <miket@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-16 21:51:38 +0000 |
---|---|---|
committer | miket@chromium.org <miket@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-16 21:51:38 +0000 |
commit | 58edca5827ed7b6f0af126fa2456f5cec0ff6ae3 (patch) | |
tree | 54eba06a541e5e9bf7493929e2cc93527095de72 /chrome/browser | |
parent | 4a3f5de339c9048ba220145a111886443d29bc1b (diff) | |
download | chromium_src-58edca5827ed7b6f0af126fa2456f5cec0ff6ae3.zip chromium_src-58edca5827ed7b6f0af126fa2456f5cec0ff6ae3.tar.gz chromium_src-58edca5827ed7b6f0af126fa2456f5cec0ff6ae3.tar.bz2 |
- Simple layer over POSIX open/read/write.
- Very limited one-byte read.
- Large refactoring of socket's controller and event notifier functionality into common classes.
BUG=110241
TEST=Added, to the extent possible (it's hardware, no meaningful way to do loopback)
Review URL: http://codereview.chromium.org/9317065
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@122363 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
32 files changed, 1039 insertions, 739 deletions
diff --git a/chrome/browser/extensions/api/api_function.cc b/chrome/browser/extensions/api/api_function.cc new file mode 100644 index 0000000..8e6278d --- /dev/null +++ b/chrome/browser/extensions/api/api_function.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2012 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 "chrome/browser/extensions/api/api_function.h" + +#include "base/bind.h" +#include "chrome/browser/extensions/api/api_resource_controller.h" +#include "chrome/browser/extensions/api/api_resource_event_notifier.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/profiles/profile.h" +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; + +namespace extensions { + +bool AsyncIOAPIFunction::RunImpl() { + if (!Prepare()) { + return false; + } + bool rv = BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&AsyncIOAPIFunction::WorkOnIOThread, this)); + DCHECK(rv); + return true; +} + +void AsyncIOAPIFunction::WorkOnIOThread() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + Work(); + bool rv = BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&AsyncIOAPIFunction::RespondOnUIThread, this)); + DCHECK(rv); +} + +void AsyncIOAPIFunction::RespondOnUIThread() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + SendResponse(Respond()); +} + +int AsyncIOAPIFunction::ExtractSrcId(size_t argument_position) { + scoped_ptr<DictionaryValue> options(new DictionaryValue()); + if (args_->GetSize() > argument_position) { + DictionaryValue* temp_options = NULL; + if (args_->GetDictionary(argument_position, &temp_options)) + options.reset(temp_options->DeepCopy()); + } + + // If we tacked on a srcId to the options object, pull it out here to provide + // to the Socket. + int src_id = -1; + if (options->HasKey(kSrcIdKey)) { + EXTENSION_FUNCTION_VALIDATE(options->GetInteger(kSrcIdKey, &src_id)); + } + + return src_id; +} + +APIResourceEventNotifier* AsyncIOAPIFunction::CreateEventNotifier(int src_id) { + return new APIResourceEventNotifier( + profile()->GetExtensionEventRouter(), profile(), extension_id(), + src_id, source_url()); +} + +APIResourceController* AsyncIOAPIFunction::controller() { + return profile()->GetExtensionService()->api_resource_controller(); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/api_function.h b/chrome/browser/extensions/api/api_function.h new file mode 100644 index 0000000..09f8e59 --- /dev/null +++ b/chrome/browser/extensions/api/api_function.h @@ -0,0 +1,50 @@ +// Copyright (c) 2012 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 CHROME_BROWSER_EXTENSIONS_API_API_FUNCTION_H_ +#define CHROME_BROWSER_EXTENSIONS_API_API_FUNCTION_H_ +#pragma once + +#include "chrome/browser/extensions/extension_function.h" +#include "chrome/browser/extensions/api/api_resource.h" + +namespace extensions { + +class APIResourceController; +class APIResourceEventNotifier; + +// AsyncIOAPIFunction provides convenient thread management for APIs that +// need to do essentially all their work on the IO thread. +class AsyncIOAPIFunction : public AsyncExtensionFunction { + protected: + // Set up for work (e.g., validate arguments). Guaranteed to happen on UI + // thread. + virtual bool Prepare() = 0; + + // Do actual work. Guaranteed to happen on IO thread. + virtual void Work() = 0; + + // Respond. Guaranteed to happen on UI thread. + virtual bool Respond() = 0; + + // Looks for a kSrcId key that might have been added to a create method's + // options object. + int ExtractSrcId(size_t argument_position); + + // Utility. + APIResourceEventNotifier* CreateEventNotifier(int src_id); + + // Access to the controller singleton. + APIResourceController* controller(); + + virtual bool RunImpl() OVERRIDE; + + private: + void WorkOnIOThread(); + void RespondOnUIThread(); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_API_FUNCTION_H_ diff --git a/chrome/browser/extensions/api/api_resource.cc b/chrome/browser/extensions/api/api_resource.cc new file mode 100644 index 0000000..482aeec --- /dev/null +++ b/chrome/browser/extensions/api/api_resource.cc @@ -0,0 +1,26 @@ +// Copyright (c) 2012 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 "chrome/browser/extensions/api/api_resource.h" +#include "chrome/browser/extensions/api/api_resource_event_notifier.h" + +namespace extensions { + +APIResource::APIResource(APIResource::APIResourceType api_resource_type, + APIResourceEventNotifier* event_notifier) + : api_resource_type_(api_resource_type), event_notifier_(event_notifier) { +} + +APIResource::~APIResource() { +} + +APIResource::APIResourceType APIResource::api_resource_type() const { + return api_resource_type_; +} + +APIResourceEventNotifier* APIResource::event_notifier() const { + return event_notifier_.get(); +} + +} diff --git a/chrome/browser/extensions/api/api_resource.h b/chrome/browser/extensions/api/api_resource.h new file mode 100644 index 0000000..26489d3 --- /dev/null +++ b/chrome/browser/extensions/api/api_resource.h @@ -0,0 +1,44 @@ +// Copyright (c) 2012 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 CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_H_ +#define CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" + +namespace extensions { + +class APIResourceController; +class APIResourceEventNotifier; + +// An APIResource represents something that an extension API manages, such as a +// socket or a serial-port connection. +class APIResource { + public: + virtual ~APIResource(); + + protected: + enum APIResourceType { + SocketResource, + SerialConnectionResource + }; + APIResource(APIResourceType api_resource_type, + APIResourceEventNotifier* event_notifier); + + APIResourceType api_resource_type() const; + APIResourceEventNotifier* event_notifier() const; + + private: + APIResourceType api_resource_type_; + scoped_ptr<APIResourceEventNotifier> event_notifier_; + + friend class APIResourceController; + DISALLOW_COPY_AND_ASSIGN(APIResource); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_H_ diff --git a/chrome/browser/extensions/api/api_resource_controller.cc b/chrome/browser/extensions/api/api_resource_controller.cc new file mode 100644 index 0000000..b101429 --- /dev/null +++ b/chrome/browser/extensions/api/api_resource_controller.cc @@ -0,0 +1,82 @@ +// Copyright (c) 2012 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 "chrome/browser/extensions/api/api_resource_controller.h" +#include "chrome/browser/extensions/api/serial/serial_connection.h" +#include "chrome/browser/extensions/api/socket/socket.h" + +namespace extensions { + +APIResourceController::APIResourceController() : next_api_resource_id_(1) {} + +APIResourceController::~APIResourceController() {} + +APIResource* APIResourceController::GetAPIResource(int api_resource_id) const { + // TODO(miket): verify that the extension asking for the APIResource is the + // same one that created it. + APIResourceMap::const_iterator i = api_resource_map_.find(api_resource_id); + if (i != api_resource_map_.end()) + return i->second.get(); + return NULL; +} + +APIResource* APIResourceController::GetAPIResource( + APIResource::APIResourceType api_resource_type, + int api_resource_id) const { + APIResource* resource = GetAPIResource(api_resource_id); + + // This DCHECK is going to catch some legitimate application-developer + // errors, where someone asks for resource of Type A with the wrong ID that + // happens to belong to a resource of Type B. But in debug mode, it's more + // likely to catch a silly copy/paste coding error while developing a new + // resource type, and unfortunately we can't really tell the difference. + // We'll choose the more informative outcome (loud explosion during debug) + // rather than a mysterious empty resource returned to JavaScript. + DCHECK(!resource || resource->api_resource_type() == api_resource_type); + + if (resource && resource->api_resource_type() != api_resource_type) + resource = NULL; + + return resource; +} + +int APIResourceController::AddAPIResource(APIResource* api_resource) { + int id = GenerateAPIResourceId(); + if (id > 0) { + linked_ptr<APIResource> resource_ptr(api_resource); + api_resource_map_[id] = resource_ptr; + return id; + } + return 0; +} + +bool APIResourceController::RemoveAPIResource(int api_resource_id) { + APIResource* api_resource = GetAPIResource(api_resource_id); + if (!api_resource) + return false; + api_resource_map_.erase(api_resource_id); + return true; +} + +// TODO(miket): consider partitioning the ID space by extension ID +// to make it harder for extensions to peek into each others' resources. +int APIResourceController::GenerateAPIResourceId() { + while (next_api_resource_id_ > 0 && + api_resource_map_.count(next_api_resource_id_) > 0) + ++next_api_resource_id_; + return next_api_resource_id_++; +} + +Socket* APIResourceController::GetSocket(int api_resource_id) const { + return static_cast<Socket*>(GetAPIResource(APIResource::SocketResource, + api_resource_id)); +} + +SerialConnection* APIResourceController::GetSerialConnection( + int api_resource_id) const { + return static_cast<SerialConnection*>( + GetAPIResource(APIResource::SerialConnectionResource, api_resource_id)); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/api_resource_controller.h b/chrome/browser/extensions/api/api_resource_controller.h new file mode 100644 index 0000000..3e79182 --- /dev/null +++ b/chrome/browser/extensions/api/api_resource_controller.h @@ -0,0 +1,62 @@ +// Copyright (c) 2012 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 CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_CONTROLLER_H_ +#define CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_CONTROLLER_H_ +#pragma once + +#include <string> +#include <map> + +#include "base/memory/linked_ptr.h" +#include "chrome/browser/extensions/api/api_resource.h" + +namespace extensions { + +class SerialConnection; +class Socket; + +// kSrcIdKey, or "srcId," binds an APIResource to the onEvent closure that was +// optionally passed to the APIResource's create() method. We generated it in +// schema_generated_bindings.js; the application code is unaware of it. +extern const char kSrcIdKey[]; + +// APIController keeps track of a collection of APIResources and provides a +// convenient set of methods to work with them. +class APIResourceController { + public: + APIResourceController(); + virtual ~APIResourceController(); + + // Takes ownership of api_resource. + int AddAPIResource(APIResource* api_resource); + bool RemoveAPIResource(int api_resource_id); + + // APIResourceController knows about all classes derived from APIResource. + // This is intentional to avoid scattering potentially unsafe static_cast<> + // operations throughout the codebase. + // + // Another alternative we considered and rejected was templatizing + // APIResourceController and scattering specialized versions throughout the + // codebase. + Socket* GetSocket(int api_resource_id) const; + SerialConnection* GetSerialConnection(int api_resource_id) const; + + private: + int next_api_resource_id_; + typedef std::map<int, linked_ptr<APIResource> > APIResourceMap; + APIResourceMap api_resource_map_; + + APIResource* GetAPIResource(APIResource::APIResourceType api_resource_type, + int api_resource_id) const; + APIResource* GetAPIResource(int api_resource_id) const; + + int GenerateAPIResourceId(); + + DISALLOW_COPY_AND_ASSIGN(APIResourceController); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_CONTROLLER_H_ diff --git a/chrome/browser/extensions/api/socket/socket_event_notifier.cc b/chrome/browser/extensions/api/api_resource_event_notifier.cc index dde760a..1c510cf 100644 --- a/chrome/browser/extensions/api/socket/socket_event_notifier.cc +++ b/chrome/browser/extensions/api/api_resource_event_notifier.cc @@ -2,14 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/extensions/api/socket/socket_event_notifier.h" +#include "chrome/browser/extensions/api/api_resource_event_notifier.h" #include "base/json/json_writer.h" #include "chrome/browser/extensions/extension_event_router.h" #include "chrome/browser/profiles/profile.h" namespace events { -const char kOnSocketEvent[] = "experimental.socket.onEvent"; +// TODO(miket): This should be generic, but at the moment only socket sends +// onEvent events. We'll fix this when serial becomes nonblocking. +const char kOnAPIResourceEvent[] = "experimental.socket.onEvent"; }; namespace extensions { @@ -25,66 +27,70 @@ const char kIsFinalEventKey[] = "isFinalEvent"; const char kResultCodeKey[] = "resultCode"; const char kDataKey[] = "data"; -SocketEventNotifier::SocketEventNotifier(ExtensionEventRouter* router, - Profile* profile, - const std::string& src_extension_id, - int src_id, - const GURL& src_url) +APIResourceEventNotifier::APIResourceEventNotifier( + ExtensionEventRouter* router, + Profile* profile, + const std::string& src_extension_id, + int src_id, + const GURL& src_url) : router_(router), profile_(profile), src_extension_id_(src_extension_id), src_id_(src_id), src_url_(src_url) {} -SocketEventNotifier::~SocketEventNotifier() {} +APIResourceEventNotifier::~APIResourceEventNotifier() {} -void SocketEventNotifier::OnConnectComplete(int result_code) { - SendEventWithResultCode(SOCKET_EVENT_CONNECT_COMPLETE, result_code); +void APIResourceEventNotifier::OnConnectComplete(int result_code) { + SendEventWithResultCode(API_RESOURCE_EVENT_CONNECT_COMPLETE, result_code); } -void SocketEventNotifier::OnDataRead(int result_code, - const std::string& data) { +void APIResourceEventNotifier::OnDataRead(int result_code, + const std::string& data) { // Do we have a destination for this event? There will be one if a source id - // was injected by the request handler for socket.create in + // was injected by the request handler for the resource's create method in // schema_generated_bindings.js, which will in turn be the case if the caller - // of socket.create provided an onEvent closure. + // of the create method provided an onEvent closure. if (src_id_ < 0) return; - DictionaryValue* event = CreateSocketEvent(SOCKET_EVENT_DATA_READ); + DictionaryValue* event = CreateAPIResourceEvent( + API_RESOURCE_EVENT_DATA_READ); event->SetInteger(kResultCodeKey, result_code); event->SetString(kDataKey, data); DispatchEvent(event); } -void SocketEventNotifier::OnWriteComplete(int result_code) { - SendEventWithResultCode(SOCKET_EVENT_WRITE_COMPLETE, result_code); +void APIResourceEventNotifier::OnWriteComplete(int result_code) { + SendEventWithResultCode(API_RESOURCE_EVENT_WRITE_COMPLETE, result_code); } -void SocketEventNotifier::SendEventWithResultCode(SocketEventType event_type, - int result_code) { +void APIResourceEventNotifier::SendEventWithResultCode( + APIResourceEventType event_type, + int result_code) { if (src_id_ < 0) return; - DictionaryValue* event = CreateSocketEvent(event_type); + DictionaryValue* event = CreateAPIResourceEvent(event_type); event->SetInteger(kResultCodeKey, result_code); DispatchEvent(event); } -void SocketEventNotifier::DispatchEvent(DictionaryValue* event) { +void APIResourceEventNotifier::DispatchEvent(DictionaryValue* event) { ListValue args; args.Set(0, event); std::string json_args; base::JSONWriter::Write(&args, false, &json_args); - router_->DispatchEventToExtension(src_extension_id_, events::kOnSocketEvent, + router_->DispatchEventToExtension(src_extension_id_, + events::kOnAPIResourceEvent, json_args, profile_, src_url_); } -DictionaryValue* SocketEventNotifier::CreateSocketEvent( - SocketEventType event_type) { +DictionaryValue* APIResourceEventNotifier::CreateAPIResourceEvent( + APIResourceEventType event_type) { DictionaryValue* event = new DictionaryValue(); - event->SetString(kEventTypeKey, SocketEventTypeToString(event_type)); + event->SetString(kEventTypeKey, APIResourceEventTypeToString(event_type)); event->SetInteger(kSrcIdKey, src_id_); // TODO(miket): Signal that it's OK to clean up onEvent listeners. This is @@ -97,14 +103,14 @@ DictionaryValue* SocketEventNotifier::CreateSocketEvent( } // static -std::string SocketEventNotifier::SocketEventTypeToString( - SocketEventType event_type) { +std::string APIResourceEventNotifier::APIResourceEventTypeToString( + APIResourceEventType event_type) { switch (event_type) { - case SOCKET_EVENT_CONNECT_COMPLETE: + case API_RESOURCE_EVENT_CONNECT_COMPLETE: return kEventTypeConnectComplete; - case SOCKET_EVENT_DATA_READ: + case API_RESOURCE_EVENT_DATA_READ: return kEventTypeDataRead; - case SOCKET_EVENT_WRITE_COMPLETE: + case API_RESOURCE_EVENT_WRITE_COMPLETE: return kEventTypeWriteComplete; } diff --git a/chrome/browser/extensions/api/api_resource_event_notifier.h b/chrome/browser/extensions/api/api_resource_event_notifier.h new file mode 100644 index 0000000..28abfd0 --- /dev/null +++ b/chrome/browser/extensions/api/api_resource_event_notifier.h @@ -0,0 +1,72 @@ +// Copyright (c) 2012 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 CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_EVENT_NOTIFIER_H_ +#define CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_EVENT_NOTIFIER_H_ +#pragma once + +#include <string> + +#include "base/basictypes.h" +#include "base/values.h" +#include "googleurl/src/gurl.h" + +class ExtensionEventRouter; +class Profile; + +namespace extensions { + +enum APIResourceEventType { + API_RESOURCE_EVENT_CONNECT_COMPLETE, + API_RESOURCE_EVENT_DATA_READ, + API_RESOURCE_EVENT_WRITE_COMPLETE +}; + +extern const char kSrcIdKey[]; + +// TODO(miket): It's possible that we'll further refactor these new classes in +// light of some changes that mihaip has suggested. The names might change, +// too: +// +// IOResource +// IOResourceExtensionFunction +// IOResourceEventNotifier +// IOResourceController + +// APIResourceEventNotifier knows how to send an event to a specific app's +// onEvent handler. It handles all platform-API events. +class APIResourceEventNotifier { + public: + APIResourceEventNotifier(ExtensionEventRouter* router, + Profile* profile, + const std::string& src_extension_id, int src_id, + const GURL& src_url); + virtual ~APIResourceEventNotifier(); + + virtual void OnConnectComplete(int result_code); + virtual void OnDataRead(int result_code, const std::string& data); + virtual void OnWriteComplete(int result_code); + + static std::string APIResourceEventTypeToString( + APIResourceEventType event_type); + + private: + void DispatchEvent(DictionaryValue* event); + DictionaryValue* CreateAPIResourceEvent(APIResourceEventType event_type); + + void SendEventWithResultCode(APIResourceEventType event_type, + int result_code); + + ExtensionEventRouter* router_; + Profile* profile_; + std::string src_extension_id_; + int src_id_; + GURL src_url_; + + DISALLOW_COPY_AND_ASSIGN(APIResourceEventNotifier); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_EVENT_NOTIFIER_H_ diff --git a/chrome/browser/extensions/api/serial/serial_api.cc b/chrome/browser/extensions/api/serial/serial_api.cc index c8b5bcb..26b523f 100644 --- a/chrome/browser/extensions/api/serial/serial_api.cc +++ b/chrome/browser/extensions/api/serial/serial_api.cc @@ -2,85 +2,124 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "chrome/browser/extensions/api/serial/serial_api.h" + #include <string> -#include "base/bind.h" #include "base/values.h" -#include "chrome/browser/extensions/api/serial/serial_api.h" -#include "content/public/browser/browser_thread.h" - -using content::BrowserThread; +#include "chrome/browser/extensions/api/api_resource_controller.h" +#include "chrome/browser/extensions/api/serial/serial_connection.h" namespace extensions { const char kConnectionIdKey[] = "connectionId"; +const char kMessageKey[] = "message"; +const char kBytesReadKey[] = "bytesRead"; +const char kBytesWrittenKey[] = "bytesWritten"; -SerialOpenFunction::SerialOpenFunction() { +SerialOpenFunction::SerialOpenFunction() + : src_id_(-1) { } -SerialOpenFunction::~SerialOpenFunction() { -} - -bool SerialOpenFunction::RunImpl() { - EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &port_)); - - bool rv = BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&SerialOpenFunction::WorkOnIOThread, this)); - DCHECK(rv); - +bool SerialOpenFunction::Prepare() { + size_t argument_position = 0; + EXTENSION_FUNCTION_VALIDATE(args_->GetString(argument_position++, &port_)); + src_id_ = ExtractSrcId(argument_position); return true; } -void SerialOpenFunction::WorkOnIOThread() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); +void SerialOpenFunction::Work() { + APIResourceEventNotifier* event_notifier = CreateEventNotifier(src_id_); + SerialConnection* serial_connection = new SerialConnection(port_, + event_notifier); + CHECK(serial_connection); + int id = controller()->AddAPIResource(serial_connection); + CHECK(id); + + bool open_result = serial_connection->Open(); + if (!open_result) { + serial_connection->Close(); + controller()->RemoveAPIResource(id); + id = -1; + } DictionaryValue* result = new DictionaryValue(); - result->SetInteger(kConnectionIdKey, 42); + result->SetInteger(kConnectionIdKey, id); result_.reset(result); +} - bool rv = BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&SerialOpenFunction::RespondOnUIThread, this)); - DCHECK(rv); +bool SerialOpenFunction::Respond() { + return true; } -void SerialOpenFunction::RespondOnUIThread() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - SendResponse(true); +bool SerialCloseFunction::Prepare() { + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &connection_id_)); + return true; } -SerialCloseFunction::SerialCloseFunction() : connection_id_(0) { +void SerialCloseFunction::Work() { + bool close_result = false; + SerialConnection* serial_connection = + controller()->GetSerialConnection(connection_id_); + if (serial_connection) { + serial_connection->Close(); + controller()->RemoveAPIResource(connection_id_); + close_result = true; + } + + result_.reset(Value::CreateBooleanValue(close_result)); } -SerialCloseFunction::~SerialCloseFunction() { +bool SerialCloseFunction::Respond() { + return true; } -bool SerialCloseFunction::RunImpl() { +bool SerialReadFunction::Prepare() { EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &connection_id_)); + return true; +} - bool rv = BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&SerialCloseFunction::WorkOnIOThread, this)); - DCHECK(rv); +void SerialReadFunction::Work() { + int bytes_read = -1; + std::string message; + SerialConnection* serial_connection = + controller()->GetSerialConnection(connection_id_); + if (serial_connection) { + unsigned char byte = '\0'; + bytes_read = serial_connection->Read(&byte); + if (bytes_read == 1) + message = byte; + } - return true; + DictionaryValue* result = new DictionaryValue(); + result->SetInteger(kBytesReadKey, bytes_read); + result->SetString(kMessageKey, message); + result_.reset(result); } -void SerialCloseFunction::WorkOnIOThread() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); +bool SerialReadFunction::Respond() { + return true; +} - result_.reset(Value::CreateBooleanValue(true)); +bool SerialWriteFunction::Prepare() { + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &connection_id_)); + EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &data_)); + return true; +} - bool rv = BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&SerialCloseFunction::RespondOnUIThread, this)); - DCHECK(rv); +void SerialWriteFunction::Work() { + int bytes_written = -1; + SerialConnection* serial_connection = + controller()->GetSerialConnection(connection_id_); + if (serial_connection) + bytes_written = serial_connection->Write(data_); + DictionaryValue* result = new DictionaryValue(); + result->SetInteger(kBytesWrittenKey, bytes_written); + result_.reset(result); } -void SerialCloseFunction::RespondOnUIThread() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - SendResponse(true); +bool SerialWriteFunction::Respond() { + return true; } } // namespace extensions diff --git a/chrome/browser/extensions/api/serial/serial_api.h b/chrome/browser/extensions/api/serial/serial_api.h index df4c3e0..f81ad90 100644 --- a/chrome/browser/extensions/api/serial/serial_api.h +++ b/chrome/browser/extensions/api/serial/serial_api.h @@ -8,44 +8,63 @@ #include <string> -#include "chrome/browser/extensions/extension_function.h" +#include "chrome/browser/extensions/api/api_function.h" namespace extensions { extern const char kConnectionIdKey[]; -class SerialOpenFunction : public AsyncExtensionFunction { +class SerialOpenFunction : public AsyncIOAPIFunction { public: SerialOpenFunction(); - virtual ~SerialOpenFunction(); - - virtual bool RunImpl() OVERRIDE; protected: - void WorkOnIOThread(); - void RespondOnUIThread(); + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + virtual bool Respond() OVERRIDE; private: + int src_id_; std::string port_; DECLARE_EXTENSION_FUNCTION_NAME("experimental.serial.open") }; -class SerialCloseFunction : public AsyncExtensionFunction { - public: - SerialCloseFunction(); - virtual ~SerialCloseFunction(); +class SerialCloseFunction : public AsyncIOAPIFunction { + protected: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + virtual bool Respond() OVERRIDE; + + private: + int connection_id_; + + DECLARE_EXTENSION_FUNCTION_NAME("experimental.serial.close") +}; + +class SerialReadFunction : public AsyncIOAPIFunction { + protected: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + virtual bool Respond() OVERRIDE; + + private: + int connection_id_; - virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("experimental.serial.read") +}; +class SerialWriteFunction : public AsyncIOAPIFunction { protected: - void WorkOnIOThread(); - void RespondOnUIThread(); + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + virtual bool Respond() OVERRIDE; private: int connection_id_; + std::string data_; - DECLARE_EXTENSION_FUNCTION_NAME("experimental.serial.close") + DECLARE_EXTENSION_FUNCTION_NAME("experimental.serial.write") }; } // namespace extensions diff --git a/chrome/browser/extensions/api/serial/serial_apitest.cc b/chrome/browser/extensions/api/serial/serial_apitest.cc index 7a58d73..b588047 100644 --- a/chrome/browser/extensions/api/serial/serial_apitest.cc +++ b/chrome/browser/extensions/api/serial/serial_apitest.cc @@ -7,6 +7,8 @@ #include "chrome/browser/extensions/api/serial/serial_api.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_function_test_utils.h" +#include "chrome/browser/extensions/extension_test_message_listener.h" +#include "chrome/browser/ui/browser.h" #include "chrome/common/chrome_switches.h" #include "content/public/browser/browser_thread.h" @@ -30,5 +32,47 @@ class SerialApiTest : public ExtensionApiTest { } // namespace IN_PROC_BROWSER_TEST_F(SerialApiTest, SerialExtension) { - ASSERT_TRUE(RunExtensionTest("serial/api")) << message_; + ResultCatcher catcher; + catcher.RestrictToProfile(browser()->profile()); + + ExtensionTestMessageListener listener("serial_port", true); + + ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("serial/api"))); + EXPECT_TRUE(listener.WaitUntilSatisfied()); + +#if 0 + // Enable this path only if all the following are true: + // + // 1. You're running Linux. + // + // 2. You have an Adafruit ATmega32u4 breakout board attached to your machine + // via USB with the Arduino Leonardo bootloader flashed to the board. Other + // devices will work; this is the only one tested. + // + // 3. Your user has permission to read/write the /dev/ttyACM0 device. + // + // 4. You have uploaded a program to the '32u4 that does a byte-for-byte echo + // on the virtual serial port at 57600 bps. Here is an example (built using + // the Arduino IDE): + // + // void setup() { + // Serial.begin(57600); + // } + // + // void loop() { + // while (true) { + // while (Serial.available() > 0) { + // Serial.print((char)Serial.read()); + // } + // } + // } + // + // TODO(miket): Enable a more forgiving set of test conditions, specifically + // by mocking SerialConnection. + listener.Reply("/dev/ttyACM0"); +#else + listener.Reply("none"); +#endif + + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); } diff --git a/chrome/browser/extensions/api/serial/serial_connection.h b/chrome/browser/extensions/api/serial/serial_connection.h new file mode 100644 index 0000000..971684a --- /dev/null +++ b/chrome/browser/extensions/api/serial/serial_connection.h @@ -0,0 +1,36 @@ +// Copyright (c) 2012 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 CHROME_BROWSER_EXTENSIONS_API_SERIAL_SERIAL_CONNECTION_H_ +#define CHROME_BROWSER_EXTENSIONS_API_SERIAL_SERIAL_CONNECTION_H_ +#pragma once + +#include <string> + +#include "chrome/browser/extensions/api/api_resource.h" + +namespace extensions { + +class APIResourceEventNotifier; + +class SerialConnection : public APIResource { + public: + SerialConnection(const std::string& port, + APIResourceEventNotifier* event_notifier); + virtual ~SerialConnection(); + + bool Open(); + void Close(); + + int Read(unsigned char* byte); + int Write(const std::string& data); + + private: + std::string port_; + int fd_; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_SERIAL_SERIAL_CONNECTION_H_ diff --git a/chrome/browser/extensions/api/serial/serial_connection_posix.cc b/chrome/browser/extensions/api/serial/serial_connection_posix.cc new file mode 100644 index 0000000..f3d2c68 --- /dev/null +++ b/chrome/browser/extensions/api/serial/serial_connection_posix.cc @@ -0,0 +1,45 @@ +// Copyright (c) 2012 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 "chrome/browser/extensions/api/serial/serial_connection.h" + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string> +#include <termios.h> +#include <unistd.h> + +namespace extensions { + +SerialConnection::SerialConnection(const std::string& port, + APIResourceEventNotifier* event_notifier) + : APIResource(APIResource::SerialConnectionResource, event_notifier), + port_(port), fd_(0) { +} + +SerialConnection::~SerialConnection() { +} + +bool SerialConnection::Open() { + fd_ = open(port_.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); + return (fd_ > 0); +} + +void SerialConnection::Close() { + if (fd_ > 0) { + close(fd_); + fd_ = 0; + } +} + +int SerialConnection::Read(unsigned char* byte) { + return read(fd_, byte, 1); +} + +int SerialConnection::Write(const std::string& data) { + return write(fd_, data.c_str(), data.length()); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/serial/serial_connection_win.cc b/chrome/browser/extensions/api/serial/serial_connection_win.cc new file mode 100644 index 0000000..213df5b --- /dev/null +++ b/chrome/browser/extensions/api/serial/serial_connection_win.cc @@ -0,0 +1,39 @@ +// Copyright (c) 2012 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 "chrome/browser/extensions/api/serial/serial_connection.h" + +#include <string> + +namespace extensions { + +SerialConnection::SerialConnection(const std::string& port, + APIResourceEventNotifier* event_notifier) + : APIResource(APIResource::SerialConnectionResource, event_notifier), + port_(port), fd_(0) { +} + +SerialConnection::~SerialConnection() { +} + +bool SerialConnection::Open() { + // TODO(miket): implement + return false; +} + +void SerialConnection::Close() { + // TODO(miket): implement +} + +int SerialConnection::Read(unsigned char* byte) { + // TODO(miket): implement + return -1; +} + +int SerialConnection::Write(const std::string& data) { + // TODO(miket): implement + return -1; +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/socket/socket.cc b/chrome/browser/extensions/api/socket/socket.cc index 7944c56..9d5f65c 100644 --- a/chrome/browser/extensions/api/socket/socket.cc +++ b/chrome/browser/extensions/api/socket/socket.cc @@ -5,7 +5,7 @@ #include "chrome/browser/extensions/api/socket/socket.h" #include "base/bind.h" -#include "chrome/browser/extensions/api/socket/socket_event_notifier.h" +#include "chrome/browser/extensions/api/api_resource_event_notifier.h" #include "net/base/io_buffer.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" @@ -13,8 +13,11 @@ namespace extensions { -Socket::Socket(SocketEventNotifier* event_notifier) - : event_notifier_(event_notifier), +Socket::Socket(const std::string& address, int port, + APIResourceEventNotifier* event_notifier) + : APIResource(APIResource::SocketResource, event_notifier), + address_(address), + port_(port), is_connected_(false), read_buffer_(new net::IOBufferWithSize(kMaxRead)) { } @@ -28,15 +31,16 @@ void Socket::OnDataRead(int result) { std::string message; if (result >= 0) message = std::string(read_buffer_->data(), result); - event_notifier_->OnDataRead(result, message); + event_notifier()->OnDataRead(result, message); } void Socket::OnWriteComplete(int result) { - event_notifier_->OnWriteComplete(result); + event_notifier()->OnWriteComplete(result); } std::string Socket::Read() { - int result = socket()->Read(read_buffer_, kMaxRead, + int result = socket()->Read( + read_buffer_, kMaxRead, base::Bind(&Socket::OnDataRead, base::Unretained(this))); if (result == net::ERR_IO_PENDING) return ""; @@ -54,7 +58,8 @@ int Socket::Write(const std::string message) { int bytes_sent = 0; while (buffer->BytesRemaining()) { - int result = socket()->Write(buffer, buffer->BytesRemaining(), + int result = socket()->Write( + buffer, buffer->BytesRemaining(), base::Bind(&Socket::OnWriteComplete, base::Unretained(this))); if (result <= 0) // We pass all errors, including ERROR_IO_PENDING, back to the caller. diff --git a/chrome/browser/extensions/api/socket/socket.h b/chrome/browser/extensions/api/socket/socket.h index 9168b1e..858a9e8 100644 --- a/chrome/browser/extensions/api/socket/socket.h +++ b/chrome/browser/extensions/api/socket/socket.h @@ -9,7 +9,7 @@ #include <string> #include "base/memory/scoped_ptr.h" -#include "chrome/browser/extensions/api/socket/socket_event_notifier.h" +#include "chrome/browser/extensions/api/api_resource.h" #include "net/base/io_buffer.h" namespace net { @@ -20,10 +20,13 @@ namespace extensions { // A Socket wraps a low-level socket and includes housekeeping information that // we need to manage it in the context of an extension. -class Socket { +class Socket : public APIResource { public: virtual ~Socket(); + // Returns true iff the socket was able to properly initialize itself. + virtual bool IsValid() = 0; + // Returns net::OK if successful, or an error code otherwise. virtual int Connect() = 0; virtual void Disconnect() = 0; @@ -46,10 +49,12 @@ class Socket { virtual void OnWriteComplete(int result); protected: - explicit Socket(SocketEventNotifier* event_notifier); + Socket(const std::string& address, int port, + APIResourceEventNotifier* event_notifier); virtual net::Socket* socket() = 0; - scoped_ptr<SocketEventNotifier> event_notifier_; + const std::string address_; + int port_; bool is_connected_; private: diff --git a/chrome/browser/extensions/api/socket/socket_api.cc b/chrome/browser/extensions/api/socket/socket_api.cc index 5674dc3..25387f3 100644 --- a/chrome/browser/extensions/api/socket/socket_api.cc +++ b/chrome/browser/extensions/api/socket/socket_api.cc @@ -6,13 +6,11 @@ #include "base/bind.h" #include "base/values.h" -#include "chrome/browser/extensions/api/socket/socket_api_controller.h" -#include "chrome/browser/extensions/api/socket/socket_event_notifier.h" +#include "chrome/browser/extensions/api/api_resource_controller.h" +#include "chrome/browser/extensions/api/socket/socket.h" +#include "chrome/browser/extensions/api/socket/tcp_socket.h" +#include "chrome/browser/extensions/api/socket/udp_socket.h" #include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/profiles/profile.h" -#include "content/public/browser/browser_thread.h" - -using content::BrowserThread; namespace extensions { @@ -22,34 +20,7 @@ const char kSocketIdKey[] = "socketId"; const char kTCPOption[] = "tcp"; const char kUDPOption[] = "udp"; -SocketController* SocketApiFunction::controller() { - return profile()->GetExtensionService()->socket_controller(); -} - -bool SocketApiFunction::RunImpl() { - if (!Prepare()) { - return false; - } - bool rv = BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&SocketApiFunction::WorkOnIOThread, this)); - DCHECK(rv); - return true; -} - -void SocketApiFunction::WorkOnIOThread() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - Work(); - bool rv = BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&SocketApiFunction::RespondOnUIThread, this)); - DCHECK(rv); -} - -void SocketApiFunction::RespondOnUIThread() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - SendResponse(Respond()); -} +const char kSocketNotFoundError[] = "Socket not found"; SocketCreateFunction::SocketCreateFunction() : src_id_(-1) { @@ -57,22 +28,14 @@ SocketCreateFunction::SocketCreateFunction() bool SocketCreateFunction::Prepare() { std::string socket_type_string; - EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &socket_type_string)); - EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &address_)); - EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(2, &port_)); - - scoped_ptr<DictionaryValue> options(new DictionaryValue()); - if (args_->GetSize() > 3) { - DictionaryValue* temp_options = NULL; - if (args_->GetDictionary(3, &temp_options)) - options.reset(temp_options->DeepCopy()); - } - - // If we tacked on a srcId to the options object, pull it out here to provide - // to the Socket. - if (options->HasKey(kSrcIdKey)) { - EXTENSION_FUNCTION_VALIDATE(options->GetInteger(kSrcIdKey, &src_id_)); - } + size_t argument_position = 0; + EXTENSION_FUNCTION_VALIDATE(args_->GetString(argument_position++, + &socket_type_string)); + EXTENSION_FUNCTION_VALIDATE(args_->GetString(argument_position++, + &address_)); + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(argument_position++, + &port_)); + src_id_ = ExtractSrcId(argument_position); if (socket_type_string == kTCPOption) socket_type_ = kSocketTypeTCP; @@ -84,15 +47,19 @@ bool SocketCreateFunction::Prepare() { } void SocketCreateFunction::Work() { - SocketEventNotifier* event_notifier(new SocketEventNotifier( - profile()->GetExtensionEventRouter(), profile(), extension_id(), - src_id_, source_url())); - int socket_id = socket_type_ == kSocketTypeTCP ? - controller()->CreateTCPSocket(address_, port_, event_notifier) : - controller()->CreateUDPSocket(address_, port_, event_notifier); + APIResourceEventNotifier* event_notifier = CreateEventNotifier(src_id_); + Socket* socket = NULL; + if (socket_type_ == kSocketTypeTCP) { + socket = new TCPSocket(address_, port_, event_notifier); + } else { + socket = new UDPSocket(address_, port_, event_notifier); + } + DCHECK(socket); + DCHECK(socket->IsValid()); + DictionaryValue* result = new DictionaryValue(); - result->SetInteger(kSocketIdKey, socket_id); + result->SetInteger(kSocketIdKey, controller()->AddAPIResource(socket)); result_.reset(result); } @@ -106,7 +73,7 @@ bool SocketDestroyFunction::Prepare() { } void SocketDestroyFunction::Work() { - controller()->DestroySocket(socket_id_); + controller()->RemoveAPIResource(socket_id_); } bool SocketDestroyFunction::Respond() { @@ -119,7 +86,12 @@ bool SocketConnectFunction::Prepare() { } void SocketConnectFunction::Work() { - int result = controller()->ConnectSocket(socket_id_); + int result = -1; + Socket* socket = controller()->GetSocket(socket_id_); + if (socket) + result = socket->Connect(); + else + error_ = kSocketNotFoundError; result_.reset(Value::CreateIntegerValue(result)); } @@ -133,7 +105,12 @@ bool SocketDisconnectFunction::Prepare() { } void SocketDisconnectFunction::Work() { - controller()->DisconnectSocket(socket_id_); + int result = -1; + Socket* socket = controller()->GetSocket(socket_id_); + if (socket) + result = socket->Connect(); + else + error_ = kSocketNotFoundError; result_.reset(Value::CreateNullValue()); } @@ -147,7 +124,10 @@ bool SocketReadFunction::Prepare() { } void SocketReadFunction::Work() { - std::string message = controller()->ReadSocket(socket_id_); + std::string message; + Socket* socket = controller()->GetSocket(socket_id_); + if (socket) + message = socket->Read(); DictionaryValue* result = new DictionaryValue(); result->SetString(kMessageKey, message); @@ -165,7 +145,12 @@ bool SocketWriteFunction::Prepare() { } void SocketWriteFunction::Work() { - int bytes_written = controller()->WriteSocket(socket_id_, message_); + int bytes_written = -1; + Socket* socket = controller()->GetSocket(socket_id_); + if (socket) + bytes_written = socket->Write(message_); + else + error_ = kSocketNotFoundError; DictionaryValue* result = new DictionaryValue(); result->SetInteger(kBytesWrittenKey, bytes_written); diff --git a/chrome/browser/extensions/api/socket/socket_api.h b/chrome/browser/extensions/api/socket/socket_api.h index 2f1616d..259fffc 100644 --- a/chrome/browser/extensions/api/socket/socket_api.h +++ b/chrome/browser/extensions/api/socket/socket_api.h @@ -6,45 +6,25 @@ #define CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_API_H_ #pragma once -#include "chrome/browser/extensions/extension_function.h" +#include "chrome/browser/extensions/api/api_function.h" #include <string> namespace extensions { -class SocketController; +class APIResourceController; extern const char kBytesWrittenKey[]; extern const char kSocketIdKey[]; extern const char kUdpSocketType[]; -class SocketApiFunction : public AsyncExtensionFunction { - protected: - // Set up for work. Guaranteed to happen on UI thread. - virtual bool Prepare() = 0; - - // Do actual work. Guaranteed to happen on IO thread. - virtual void Work() = 0; - - // Respond. Guaranteed to happen on UI thread. - virtual bool Respond() = 0; - - virtual bool RunImpl() OVERRIDE; - - SocketController* controller(); - - private: - void WorkOnIOThread(); - void RespondOnUIThread(); -}; - // Many of these socket functions are synchronous in the sense that // they don't involve blocking operations, but we've made them all // AsyncExtensionFunctions because the underlying UDPClientSocket // library wants all operations to happen on the same thread as the // one that created the socket. Too bad. -class SocketCreateFunction : public SocketApiFunction { +class SocketCreateFunction : public AsyncIOAPIFunction { public: SocketCreateFunction(); @@ -68,7 +48,7 @@ class SocketCreateFunction : public SocketApiFunction { DECLARE_EXTENSION_FUNCTION_NAME("experimental.socket.create") }; -class SocketDestroyFunction : public SocketApiFunction { +class SocketDestroyFunction : public AsyncIOAPIFunction { protected: virtual bool Prepare() OVERRIDE; virtual void Work() OVERRIDE; @@ -80,7 +60,7 @@ class SocketDestroyFunction : public SocketApiFunction { DECLARE_EXTENSION_FUNCTION_NAME("experimental.socket.destroy") }; -class SocketConnectFunction : public SocketApiFunction { +class SocketConnectFunction : public AsyncIOAPIFunction { protected: virtual bool Prepare() OVERRIDE; virtual void Work() OVERRIDE; @@ -92,7 +72,7 @@ class SocketConnectFunction : public SocketApiFunction { DECLARE_EXTENSION_FUNCTION_NAME("experimental.socket.connect") }; -class SocketDisconnectFunction : public SocketApiFunction { +class SocketDisconnectFunction : public AsyncIOAPIFunction { protected: virtual bool Prepare() OVERRIDE; virtual void Work() OVERRIDE; @@ -104,7 +84,7 @@ class SocketDisconnectFunction : public SocketApiFunction { DECLARE_EXTENSION_FUNCTION_NAME("experimental.socket.disconnect") }; -class SocketReadFunction : public SocketApiFunction { +class SocketReadFunction : public AsyncIOAPIFunction { protected: virtual bool Prepare() OVERRIDE; virtual void Work() OVERRIDE; @@ -116,7 +96,7 @@ class SocketReadFunction : public SocketApiFunction { DECLARE_EXTENSION_FUNCTION_NAME("experimental.socket.read") }; -class SocketWriteFunction : public SocketApiFunction { +class SocketWriteFunction : public AsyncIOAPIFunction { protected: virtual bool Prepare() OVERRIDE; virtual void Work() OVERRIDE; diff --git a/chrome/browser/extensions/api/socket/socket_api_controller.cc b/chrome/browser/extensions/api/socket/socket_api_controller.cc deleted file mode 100644 index 0a628f3..0000000 --- a/chrome/browser/extensions/api/socket/socket_api_controller.cc +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) 2012 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 "chrome/browser/extensions/api/socket/socket_api_controller.h" - -#include "chrome/browser/extensions/api/socket/tcp_socket.h" -#include "chrome/browser/extensions/api/socket/udp_socket.h" -#include "chrome/browser/extensions/api/socket/socket_event_notifier.h" -#include "net/base/address_list.h" -#include "net/base/net_errors.h" -#include "net/base/rand_callback.h" -#include "net/socket/tcp_client_socket.h" -#include "net/udp/datagram_socket.h" -#include "net/udp/udp_client_socket.h" - -namespace extensions { - -SocketController::SocketController() : next_socket_id_(1) {} - -SocketController::~SocketController() {} - -Socket* SocketController::GetSocket(int socket_id) { - // TODO(miket): we should verify that the extension asking for the - // socket is the same one that created it. - SocketMap::iterator i = socket_map_.find(socket_id); - if (i != socket_map_.end()) - return i->second.get(); - return NULL; -} - -// TODO(miket): we should consider partitioning the ID space by extension ID -// to make it harder for extensions to peek into each others' sockets. -int SocketController::GenerateSocketId() { - while (next_socket_id_ > 0 && socket_map_.count(next_socket_id_) > 0) - ++next_socket_id_; - return next_socket_id_++; -} - -int SocketController::CreateTCPSocket(const std::string& address, int port, - SocketEventNotifier* event_notifier) { - net::IPAddressNumber ip_number; - bool result = net::ParseIPLiteralToNumber(address, &ip_number); - if (!result) - return 0; - - net::AddressList address_list = - net::AddressList::CreateFromIPAddress(ip_number, port); - linked_ptr<Socket> socket(new TCPSocket( - new net::TCPClientSocket(address_list, NULL, net::NetLog::Source()), - event_notifier)); - CHECK(socket.get()); - int next_socket_id = GenerateSocketId(); - if (next_socket_id > 0) { - socket_map_[next_socket_id] = socket; - return next_socket_id; - } - return 0; -} - -int SocketController::CreateUDPSocket(const std::string& address, int port, - SocketEventNotifier* event_notifier) { - linked_ptr<Socket> socket(new UDPSocket( - new net::UDPClientSocket(net::DatagramSocket::DEFAULT_BIND, - net::RandIntCallback(), - NULL, - net::NetLog::Source()), - address, port, event_notifier)); - CHECK(socket.get()); - int next_socket_id = GenerateSocketId(); - if (next_socket_id > 0) { - socket_map_[next_socket_id] = socket; - return next_socket_id; - } - return 0; -} - -bool SocketController::DestroySocket(int socket_id) { - Socket* socket = GetSocket(socket_id); - if (!socket) - return false; - delete socket; - socket_map_.erase(socket_id); - return true; -} - -int SocketController::ConnectSocket(int socket_id) { - Socket* socket = GetSocket(socket_id); - if (socket) - return socket->Connect(); - return net::ERR_FILE_NOT_FOUND; -} - -void SocketController::DisconnectSocket(int socket_id) { - Socket* socket = GetSocket(socket_id); - if (socket) - socket->Disconnect(); -} - -std::string SocketController::ReadSocket(int socket_id) { - Socket* socket = GetSocket(socket_id); - if (!socket) - return ""; - return socket->Read(); -} - -int SocketController::WriteSocket(int socket_id, const std::string& message) { - Socket* socket = GetSocket(socket_id); - if (!socket) - return -1; - return socket->Write(message); -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/socket/socket_api_controller.h b/chrome/browser/extensions/api/socket/socket_api_controller.h deleted file mode 100644 index e5effb3..0000000 --- a/chrome/browser/extensions/api/socket/socket_api_controller.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2012 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 CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_API_CONTROLLER_H_ -#define CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_API_CONTROLLER_H_ -#pragma once - -#include <string> -#include <map> - -#include "base/memory/linked_ptr.h" -#include "base/memory/scoped_ptr.h" - -class Profile; - -namespace net { -class IPEndPoint; -} - -namespace extensions { - -// kSrcIdKey, or "srcId," binds a socket to the onEvent closure that was -// optionally passed to the socket.create() method. It's generated by us in -// schema_generated_bindings.js; the application code is unaware of it. -extern const char kSrcIdKey[]; - -class Socket; -class SocketEventNotifier; - -// SocketController keeps track of a collection of Sockets and provides a -// convenient set of methods to manipulate them. -class SocketController { - public: - SocketController(); - virtual ~SocketController(); - - // Create/Destroy are a pair. They represent the allocation and deallocation - // of the Socket object in memory. - // - // TODO(miket): aa's suggestion to track lifetime of callbacks associated - // with each socket, which will then let us clean up when we go out of scope - // rather than requiring that the app developer remember to call Destroy. - // - // Takes ownership of |event_notifier|. - int CreateTCPSocket(const std::string& address, int port, - SocketEventNotifier* event_notifier); - int CreateUDPSocket(const std::string& address, int port, - SocketEventNotifier* event_notifier); - - bool DestroySocket(int socket_id); - - // Connect, Disconnect, Read, and Write map to the equivalent methods in the - // underlying socket. - int ConnectSocket(int socket_id); - void DisconnectSocket(int socket_id); - std::string ReadSocket(int socket_id); - int WriteSocket(int socket_id, const std::string& message); - - private: - int next_socket_id_; - typedef std::map<int, linked_ptr<Socket> > SocketMap; - SocketMap socket_map_; - - // Convenience method for accessing SocketMap. - Socket* GetSocket(int socket_id); - - int GenerateSocketId(); - - DISALLOW_COPY_AND_ASSIGN(SocketController); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_API_CONTROLLER_H_ diff --git a/chrome/browser/extensions/api/socket/socket_api_controller_unittest.cc b/chrome/browser/extensions/api/socket/socket_api_controller_unittest.cc deleted file mode 100644 index 5c52bb1..0000000 --- a/chrome/browser/extensions/api/socket/socket_api_controller_unittest.cc +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2012 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 "testing/gtest/include/gtest/gtest.h" - -#include "base/memory/scoped_ptr.h" -#include "base/values.h" -#include "chrome/browser/extensions/api/socket/socket_api.h" -#include "chrome/browser/extensions/api/socket/socket_api_controller.h" -#include "chrome/browser/extensions/api/socket/socket_event_notifier.h" -#include "chrome/common/extensions/extension.h" - -namespace extensions { - -class SocketApiControllerTest : public testing::Test { -}; - -TEST_F(SocketApiControllerTest, TestSocketControllerLifetime) { - // We want to make sure that killing the controller while a bunch of - // sockets are alive doesn't crash. - scoped_ptr<SocketController> controller(new SocketController()); - const int kPort = 38888; - const std::string address("127.0.0.1"); - - // Create some sockets but don't do anything with them. - Profile* profile = NULL; - std::string extension_id; - EXPECT_TRUE(Extension::GenerateId("e^(iπ)+1=0", &extension_id)); - const GURL url; - for (int i = 0; i < 10; ++i) { - SocketEventNotifier* notifier = - new SocketEventNotifier(NULL, profile, extension_id, -1, url); - int socket_id = controller->CreateUDPSocket(address, kPort, notifier); - ASSERT_TRUE(socket_id != 0); - } - for (int i = 0; i < 10; ++i) { - SocketEventNotifier* notifier = - new SocketEventNotifier(NULL, profile, extension_id, -1, url); - int socket_id = controller->CreateTCPSocket(address, kPort, notifier); - ASSERT_TRUE(socket_id != 0); - } - - // Create some more sockets and connect them. Note that because this is - // UDP, we can happily "connect" a UDP socket without anyone listening. - // We skip TCP sockets here because we've already tested what we wanted - // to test. - for (int i = 0; i < 10; ++i) { - SocketEventNotifier* notifier = - new SocketEventNotifier(NULL, profile, extension_id, -1, url); - int socket_id = controller->CreateUDPSocket(address, kPort, notifier); - ASSERT_TRUE(socket_id != 0); - ASSERT_EQ(0, controller->ConnectSocket(socket_id)); - } -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/socket/socket_event_notifier.h b/chrome/browser/extensions/api/socket/socket_event_notifier.h deleted file mode 100644 index 1a5b9b1..0000000 --- a/chrome/browser/extensions/api/socket/socket_event_notifier.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2012 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 CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_EVENT_NOTIFIER_H_ -#define CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_EVENT_NOTIFIER_H_ -#pragma once - -#include <string> - -#include "base/values.h" -#include "googleurl/src/gurl.h" - -class ExtensionEventRouter; -class Profile; - -namespace events { -extern const char kOnSocketEvent[]; -}; - -namespace extensions { - -enum SocketEventType { - SOCKET_EVENT_CONNECT_COMPLETE, - SOCKET_EVENT_DATA_READ, - SOCKET_EVENT_WRITE_COMPLETE -}; - -extern const char kSrcIdKey[]; - -// Contains the data that a Socket needs to send an event back to the extension -// that instantiated it. -class SocketEventNotifier { - public: - SocketEventNotifier(ExtensionEventRouter* router, - Profile* profile, - const std::string& src_extension_id, int src_id, - const GURL& src_url); - virtual ~SocketEventNotifier(); - - virtual void OnConnectComplete(int result_code); - virtual void OnDataRead(int result_code, const std::string& data); - virtual void OnWriteComplete(int result_code); - - static std::string SocketEventTypeToString(SocketEventType event_type); - - private: - void DispatchEvent(DictionaryValue* event); - DictionaryValue* CreateSocketEvent(SocketEventType event_type); - - void SendEventWithResultCode(SocketEventType event_type, int result_code); - - ExtensionEventRouter* router_; - Profile* profile_; - std::string src_extension_id_; - int src_id_; - GURL src_url_; - - DISALLOW_COPY_AND_ASSIGN(SocketEventNotifier); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_EVENT_NOTIFIER_H_ diff --git a/chrome/browser/extensions/api/socket/socket_event_notifier_unittest.cc b/chrome/browser/extensions/api/socket/socket_event_notifier_unittest.cc deleted file mode 100644 index 780747f..0000000 --- a/chrome/browser/extensions/api/socket/socket_event_notifier_unittest.cc +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) 2012 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 "chrome/browser/extensions/api/socket/socket_event_notifier.h" - -#include "base/json/json_reader.h" -#include "base/memory/scoped_ptr.h" -#include "base/values.h" -#include "chrome/browser/extensions/extension_event_router.h" -#include "chrome/common/extensions/extension.h" -#include "chrome/test/base/testing_profile.h" -#include "testing/gmock/include/gmock/gmock.h" - -using testing::_; -using testing::SaveArg; - -namespace extensions { - -class MockExtensionEventRouter : public ExtensionEventRouter { - public: - explicit MockExtensionEventRouter(Profile* profile) : - ExtensionEventRouter(profile) {} - - MOCK_METHOD5(DispatchEventToExtension, void(const std::string& extension_id, - const std::string& event_name, - const std::string& event_args, - Profile* source_profile, - const GURL& event_url)); - - - private: - DISALLOW_COPY_AND_ASSIGN(MockExtensionEventRouter); -}; - -class MockTestingProfile : public TestingProfile { - public: - MockTestingProfile() {} - MOCK_METHOD0(GetExtensionEventRouter, ExtensionEventRouter*()); - - private: - DISALLOW_COPY_AND_ASSIGN(MockTestingProfile); -}; - -class SocketEventNotifierTest : public testing::Test { - public: - SocketEventNotifierTest() - : mock_event_router_( - new MockExtensionEventRouter(&profile_)), - src_id_(42) { - EXPECT_TRUE(Extension::GenerateId("chrome fast", &extension_id_)); - event_notifier_.reset(new SocketEventNotifier(mock_event_router_.get(), - &profile_, - extension_id_, - src_id_, url_)); - } - - MockTestingProfile profile_; - scoped_ptr<MockExtensionEventRouter> mock_event_router_; - int src_id_; - GURL url_; - std::string extension_id_; - scoped_ptr<SocketEventNotifier> event_notifier_; -}; - -TEST_F(SocketEventNotifierTest, TestOnConnectCompleteEvent) { - std::string event_args; - EXPECT_CALL(*mock_event_router_.get(), - DispatchEventToExtension( - extension_id_, - events::kOnSocketEvent, - _, - &profile_, - url_)) - .Times(1) - .WillOnce(SaveArg<2>(&event_args)); - - const int expected_result_code = 777; - event_notifier_->OnConnectComplete(expected_result_code); - - scoped_ptr<Value> result(base::JSONReader::Read(event_args, true)); - Value* value = result.get(); - ASSERT_TRUE(result.get() != NULL); - ASSERT_EQ(Value::TYPE_LIST, value->GetType()); - ListValue* list = static_cast<ListValue*>(value); - ASSERT_EQ(1u, list->GetSize()); - - DictionaryValue* info; - ASSERT_TRUE(list->GetDictionary(0, &info)); - - int tmp_src_id = 0; - ASSERT_TRUE(info->GetInteger("srcId", &tmp_src_id)); - ASSERT_EQ(src_id_, tmp_src_id); - - int actual_result_code = 0; - ASSERT_TRUE(info->GetInteger("resultCode", &actual_result_code)); - ASSERT_EQ(expected_result_code, actual_result_code); -} - -TEST_F(SocketEventNotifierTest, TestOnWriteCompleteEvent) { - std::string event_args; - EXPECT_CALL(*mock_event_router_.get(), - DispatchEventToExtension( - extension_id_, - events::kOnSocketEvent, - _, - &profile_, - url_)) - .Times(1) - .WillOnce(SaveArg<2>(&event_args)); - - const int expected_result_code = 888; - event_notifier_->OnWriteComplete(expected_result_code); - - scoped_ptr<Value> result(base::JSONReader::Read(event_args, true)); - Value* value = result.get(); - ASSERT_TRUE(result.get() != NULL); - ASSERT_EQ(Value::TYPE_LIST, value->GetType()); - ListValue* list = static_cast<ListValue*>(value); - ASSERT_EQ(1u, list->GetSize()); - - DictionaryValue* info; - ASSERT_TRUE(list->GetDictionary(0, &info)); - - int tmp_src_id = 0; - ASSERT_TRUE(info->GetInteger("srcId", &tmp_src_id)); - ASSERT_EQ(src_id_, tmp_src_id); - - int actual_result_code = 0; - ASSERT_TRUE(info->GetInteger("resultCode", &actual_result_code)); - ASSERT_EQ(expected_result_code, actual_result_code); -} - -TEST_F(SocketEventNotifierTest, TestOnDataReadEvent) { - std::string event_args; - EXPECT_CALL(*mock_event_router_.get(), - DispatchEventToExtension( - extension_id_, - events::kOnSocketEvent, - _, - &profile_, - url_)) - .Times(1) - .WillOnce(SaveArg<2>(&event_args)); - - const int expected_result_code = 888; - const std::string result_data("hi"); - event_notifier_->OnDataRead(expected_result_code, result_data); - - scoped_ptr<Value> result(base::JSONReader::Read(event_args, true)); - Value* value = result.get(); - ASSERT_TRUE(result.get() != NULL); - ASSERT_EQ(Value::TYPE_LIST, value->GetType()); - ListValue* list = static_cast<ListValue*>(value); - ASSERT_EQ(1u, list->GetSize()); - - DictionaryValue* info; - ASSERT_TRUE(list->GetDictionary(0, &info)); - - int tmp_src_id = 0; - ASSERT_TRUE(info->GetInteger("srcId", &tmp_src_id)); - ASSERT_EQ(src_id_, tmp_src_id); - - int actual_result_code = 0; - ASSERT_TRUE(info->GetInteger("resultCode", &actual_result_code)); - ASSERT_EQ(expected_result_code, actual_result_code); - - std::string tmp_result_data; - ASSERT_TRUE(info->GetString("data", &tmp_result_data)); - ASSERT_EQ(result_data, tmp_result_data); -} - -} // namespace extensions diff --git a/chrome/browser/extensions/api/socket/tcp_socket.cc b/chrome/browser/extensions/api/socket/tcp_socket.cc index 44eda1a..8989fd2 100644 --- a/chrome/browser/extensions/api/socket/tcp_socket.cc +++ b/chrome/browser/extensions/api/socket/tcp_socket.cc @@ -4,17 +4,42 @@ #include "chrome/browser/extensions/api/socket/tcp_socket.h" -#include "chrome/browser/extensions/api/socket/socket_event_notifier.h" +#include "chrome/browser/extensions/api/api_resource.h" +#include "chrome/browser/extensions/api/api_resource_event_notifier.h" +#include "net/base/address_list.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" +#include "net/base/rand_callback.h" #include "net/socket/tcp_client_socket.h" namespace extensions { +TCPSocket::TCPSocket(const std::string& address, int port, + APIResourceEventNotifier* event_notifier) + : Socket(address, port, event_notifier) { + net::IPAddressNumber ip_number; + if (net::ParseIPLiteralToNumber(address, &ip_number)) { + net::AddressList address_list = + net::AddressList::CreateFromIPAddress(ip_number, port); + socket_.reset(new net::TCPClientSocket(address_list, NULL, + net::NetLog::Source())); + } +} + +// For testing. TCPSocket::TCPSocket(net::TCPClientSocket* tcp_client_socket, - SocketEventNotifier* event_notifier) -: Socket(event_notifier), - socket_(tcp_client_socket) { + const std::string& address, int port, + APIResourceEventNotifier* event_notifier) + : Socket(address, port, event_notifier), + socket_(tcp_client_socket) { +} + +// static +TCPSocket* TCPSocket::CreateSocketForTesting( + net::TCPClientSocket* tcp_client_socket, + const std::string& address, int port, + APIResourceEventNotifier* event_notifier) { + return new TCPSocket(tcp_client_socket, address, port, event_notifier); } TCPSocket::~TCPSocket() { @@ -23,6 +48,10 @@ TCPSocket::~TCPSocket() { } } +bool TCPSocket::IsValid() { + return socket_ != NULL; +} + net::Socket* TCPSocket::socket() { return socket_.get(); } @@ -41,7 +70,7 @@ void TCPSocket::Disconnect() { void TCPSocket::OnConnect(int result) { is_connected_ = result == net::OK; - event_notifier_->OnConnectComplete(result); + event_notifier()->OnConnectComplete(result); } } // namespace extensions diff --git a/chrome/browser/extensions/api/socket/tcp_socket.h b/chrome/browser/extensions/api/socket/tcp_socket.h index 48dffc5..051abf4 100644 --- a/chrome/browser/extensions/api/socket/tcp_socket.h +++ b/chrome/browser/extensions/api/socket/tcp_socket.h @@ -9,7 +9,6 @@ #include <string> #include "chrome/browser/extensions/api/socket/socket.h" -#include "chrome/browser/extensions/api/socket/socket_event_notifier.h" // This looks like it should be forward-declarable, but it does some tricky // moves that make it easier to just include it. @@ -21,21 +20,34 @@ class Socket; namespace extensions { +class APIResourceEventNotifier; + class TCPSocket : public Socket { public: - TCPSocket(net::TCPClientSocket* tcp_client_socket, - SocketEventNotifier* event_notifier); + TCPSocket(const std::string& address, int port, + APIResourceEventNotifier* event_notifier); virtual ~TCPSocket(); + virtual bool IsValid() OVERRIDE; + virtual int Connect() OVERRIDE; virtual void Disconnect() OVERRIDE; virtual void OnConnect(int result); + static TCPSocket* CreateSocketForTesting( + net::TCPClientSocket* tcp_client_socket, + const std::string& address, int port, + APIResourceEventNotifier* event_notifier); + protected: virtual net::Socket* socket() OVERRIDE; private: + TCPSocket(net::TCPClientSocket* tcp_client_socket, + const std::string& address, int port, + APIResourceEventNotifier* event_notifier); + scoped_ptr<net::TCPClientSocket> socket_; }; diff --git a/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc b/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc new file mode 100644 index 0000000..8132e78 --- /dev/null +++ b/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc @@ -0,0 +1,99 @@ +// Copyright (c) 2012 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 "chrome/browser/extensions/api/socket/tcp_socket.h" + +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/extensions/api/api_resource_event_notifier.h" +#include "net/base/address_list.h" +#include "net/base/completion_callback.h" +#include "net/base/net_errors.h" +#include "net/base/rand_callback.h" +#include "net/socket/tcp_client_socket.h" +#include "testing/gmock/include/gmock/gmock.h" + +using testing::_; +using testing::DoAll; +using testing::Return; +using testing::SaveArg; + +namespace extensions { + +class MockTCPSocket : public net::TCPClientSocket { + public: + explicit MockTCPSocket(const net::AddressList& address_list) + : net::TCPClientSocket(address_list, NULL, net::NetLog::Source()) { + } + + MOCK_METHOD3(Read, int(net::IOBuffer* buf, int buf_len, + const net::CompletionCallback& callback)); + MOCK_METHOD3(Write, int(net::IOBuffer* buf, int buf_len, + const net::CompletionCallback& callback)); + private: + DISALLOW_COPY_AND_ASSIGN(MockTCPSocket); +}; + +class MockAPIResourceEventNotifier : public APIResourceEventNotifier { + public: + MockAPIResourceEventNotifier() : APIResourceEventNotifier(NULL, NULL, + std::string(), + 0, GURL()) {} + + MOCK_METHOD2(OnReadComplete, void(int result_code, + const std::string& message)); + MOCK_METHOD1(OnWriteComplete, void(int result_code)); +}; + +TEST(SocketTest, TestTCPSocketRead) { + net::AddressList address_list; + MockTCPSocket* tcp_client_socket = new MockTCPSocket(address_list); + APIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier(); + + scoped_ptr<TCPSocket> socket(TCPSocket::CreateSocketForTesting( + tcp_client_socket, "1.2.3.4", 1, notifier)); + + EXPECT_CALL(*tcp_client_socket, Read(_, _, _)) + .Times(1); + + std::string message = socket->Read(); +} + +TEST(SocketTest, TestTCPSocketWrite) { + net::AddressList address_list; + MockTCPSocket* tcp_client_socket = new MockTCPSocket(address_list); + APIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier(); + + scoped_ptr<TCPSocket> socket(TCPSocket::CreateSocketForTesting( + tcp_client_socket, "1.2.3.4", 1, notifier)); + + EXPECT_CALL(*tcp_client_socket, Write(_, _, _)) + .Times(1); + + socket->Write("foo"); +} + +TEST(SocketTest, TestTCPSocketBlockedWrite) { + net::AddressList address_list; + MockTCPSocket* tcp_client_socket = new MockTCPSocket(address_list); + MockAPIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier(); + + scoped_ptr<TCPSocket> socket(TCPSocket::CreateSocketForTesting( + tcp_client_socket, "1.2.3.4", 1, notifier)); + + net::CompletionCallback callback; + EXPECT_CALL(*tcp_client_socket, Write(_, _, _)) + .Times(1) + .WillOnce(testing::DoAll(SaveArg<2>(&callback), + Return(net::ERR_IO_PENDING))); + + ASSERT_EQ(net::ERR_IO_PENDING, socket->Write("foo")); + + // Good. Original call came back unable to complete. Now pretend the socket + // finished, and confirm that we passed the error back. + EXPECT_CALL(*notifier, OnWriteComplete(42)) + .Times(1); + callback.Run(42); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/socket/udp_socket.cc b/chrome/browser/extensions/api/socket/udp_socket.cc index 4de8e25..467df66 100644 --- a/chrome/browser/extensions/api/socket/udp_socket.cc +++ b/chrome/browser/extensions/api/socket/udp_socket.cc @@ -4,20 +4,38 @@ #include "chrome/browser/extensions/api/socket/udp_socket.h" -#include "chrome/browser/extensions/api/socket/socket_event_notifier.h" +#include "chrome/browser/extensions/api/api_resource.h" +#include "chrome/browser/extensions/api/api_resource_event_notifier.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" #include "net/udp/datagram_socket.h" +#include "net/udp/udp_client_socket.h" namespace extensions { +UDPSocket::UDPSocket(const std::string& address, int port, + APIResourceEventNotifier* event_notifier) + : Socket(address, port, event_notifier), + socket_(new net::UDPClientSocket(net::DatagramSocket::DEFAULT_BIND, + net::RandIntCallback(), + NULL, + net::NetLog::Source())) { +} + +// For testing. UDPSocket::UDPSocket(net::DatagramClientSocket* datagram_client_socket, const std::string& address, int port, - SocketEventNotifier* event_notifier) -: Socket(event_notifier), - socket_(datagram_client_socket), - address_(address), - port_(port) { + APIResourceEventNotifier* event_notifier) + : Socket(address, port, event_notifier), + socket_(datagram_client_socket) { +} + +// static +UDPSocket* UDPSocket::CreateSocketForTesting( + net::DatagramClientSocket* datagram_client_socket, + const std::string& address, int port, + APIResourceEventNotifier* event_notifier) { + return new UDPSocket(datagram_client_socket, address, port, event_notifier); } UDPSocket::~UDPSocket() { @@ -26,6 +44,10 @@ UDPSocket::~UDPSocket() { } } +bool UDPSocket::IsValid() { + return socket_ != NULL; +} + net::Socket* UDPSocket::socket() { return socket_.get(); } diff --git a/chrome/browser/extensions/api/socket/udp_socket.h b/chrome/browser/extensions/api/socket/udp_socket.h index 6511e5d..4cffa98 100644 --- a/chrome/browser/extensions/api/socket/udp_socket.h +++ b/chrome/browser/extensions/api/socket/udp_socket.h @@ -9,10 +9,6 @@ #include <string> #include "chrome/browser/extensions/api/socket/socket.h" -#include "chrome/browser/extensions/api/socket/socket_event_notifier.h" - -// This looks like it should be forward-declarable, but it does some tricky -// moves that make it easier to just include it. #include "net/udp/datagram_client_socket.h" namespace net { @@ -21,23 +17,34 @@ class Socket; namespace extensions { +class APIResourceEventNotifier; + class UDPSocket : public Socket { public: - UDPSocket(net::DatagramClientSocket* datagram_client_socket, - const std::string& address, int port, - SocketEventNotifier* event_notifier); + UDPSocket(const std::string& address, int port, + APIResourceEventNotifier* event_notifier); virtual ~UDPSocket(); + virtual bool IsValid() OVERRIDE; + virtual int Connect() OVERRIDE; virtual void Disconnect() OVERRIDE; + static UDPSocket* CreateSocketForTesting( + net::DatagramClientSocket* datagram_client_socket, + const std::string& address, int port, + APIResourceEventNotifier* event_notifier); + protected: virtual net::Socket* socket() OVERRIDE; private: + // Special constructor for testing. + UDPSocket(net::DatagramClientSocket* datagram_client_socket, + const std::string& address, int port, + APIResourceEventNotifier* event_notifier); + scoped_ptr<net::DatagramClientSocket> socket_; - const std::string address_; - int port_; }; } // namespace extensions diff --git a/chrome/browser/extensions/api/socket/socket_unittest.cc b/chrome/browser/extensions/api/socket/udp_socket_unittest.cc index 511a355..980e3e8 100644 --- a/chrome/browser/extensions/api/socket/socket_unittest.cc +++ b/chrome/browser/extensions/api/socket/udp_socket_unittest.cc @@ -5,6 +5,7 @@ #include "chrome/browser/extensions/api/socket/udp_socket.h" #include "base/memory/scoped_ptr.h" +#include "chrome/browser/extensions/api/api_resource_event_notifier.h" #include "net/base/completion_callback.h" #include "net/base/net_errors.h" #include "net/base/rand_callback.h" @@ -18,9 +19,9 @@ using testing::SaveArg; namespace extensions { -class MockSocket : public net::UDPClientSocket { +class MockUDPSocket : public net::UDPClientSocket { public: - MockSocket() + MockUDPSocket() : net::UDPClientSocket(net::DatagramSocket::DEFAULT_BIND, net::RandIntCallback(), NULL, @@ -31,25 +32,26 @@ class MockSocket : public net::UDPClientSocket { MOCK_METHOD3(Write, int(net::IOBuffer* buf, int buf_len, const net::CompletionCallback& callback)); private: - DISALLOW_COPY_AND_ASSIGN(MockSocket); + DISALLOW_COPY_AND_ASSIGN(MockUDPSocket); }; -class MockSocketEventNotifier : public SocketEventNotifier { +class MockAPIResourceEventNotifier : public APIResourceEventNotifier { public: - MockSocketEventNotifier() : SocketEventNotifier(NULL, NULL, std::string(), - 0, GURL()) {} + MockAPIResourceEventNotifier() : APIResourceEventNotifier(NULL, NULL, + std::string(), + 0, GURL()) {} MOCK_METHOD2(OnReadComplete, void(int result_code, const std::string& message)); MOCK_METHOD1(OnWriteComplete, void(int result_code)); }; -TEST(SocketTest, TestSocketRead) { - MockSocket* udp_client_socket = new MockSocket(); - SocketEventNotifier* notifier = new MockSocketEventNotifier(); +TEST(SocketTest, TestUDPSocketRead) { + MockUDPSocket* udp_client_socket = new MockUDPSocket(); + APIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier(); - scoped_ptr<UDPSocket> socket(new UDPSocket(udp_client_socket, "1.2.3.4", 1, - notifier)); + scoped_ptr<UDPSocket> socket(UDPSocket::CreateSocketForTesting( + udp_client_socket, "1.2.3.4", 1, notifier)); EXPECT_CALL(*udp_client_socket, Read(_, _, _)) .Times(1); @@ -57,12 +59,12 @@ TEST(SocketTest, TestSocketRead) { std::string message = socket->Read(); } -TEST(SocketTest, TestSocketWrite) { - MockSocket* udp_client_socket = new MockSocket(); - SocketEventNotifier* notifier = new MockSocketEventNotifier(); +TEST(SocketTest, TestUDPSocketWrite) { + MockUDPSocket* udp_client_socket = new MockUDPSocket(); + APIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier(); - scoped_ptr<UDPSocket> socket(new UDPSocket(udp_client_socket, "1.2.3.4", 1, - notifier)); + scoped_ptr<UDPSocket> socket(UDPSocket::CreateSocketForTesting( + udp_client_socket, "1.2.3.4", 1, notifier)); EXPECT_CALL(*udp_client_socket, Write(_, _, _)) .Times(1); @@ -70,12 +72,12 @@ TEST(SocketTest, TestSocketWrite) { socket->Write("foo"); } -TEST(SocketTest, TestSocketBlockedWrite) { - MockSocket* udp_client_socket = new MockSocket(); - MockSocketEventNotifier* notifier = new MockSocketEventNotifier(); +TEST(SocketTest, TestUDPSocketBlockedWrite) { + MockUDPSocket* udp_client_socket = new MockUDPSocket(); + MockAPIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier(); - scoped_ptr<UDPSocket> socket(new UDPSocket(udp_client_socket, "1.2.3.4", 1, - notifier)); + scoped_ptr<UDPSocket> socket(UDPSocket::CreateSocketForTesting( + udp_client_socket, "1.2.3.4", 1, notifier)); net::CompletionCallback callback; EXPECT_CALL(*udp_client_socket, Write(_, _, _)) diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc index 920dc53..c664dc2 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.cc +++ b/chrome/browser/extensions/extension_function_dispatcher.cc @@ -489,6 +489,8 @@ void FactoryRegistry::ResetFunctions() { // Serial RegisterFunction<extensions::SerialOpenFunction>(); RegisterFunction<extensions::SerialCloseFunction>(); + RegisterFunction<extensions::SerialReadFunction>(); + RegisterFunction<extensions::SerialWriteFunction>(); // Sockets RegisterFunction<extensions::SocketCreateFunction>(); diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc index 28a5363..81a6a2f3 100644 --- a/chrome/browser/extensions/extension_service.cc +++ b/chrome/browser/extensions/extension_service.cc @@ -31,7 +31,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_plugin_service_filter.h" #include "chrome/browser/download/download_extension_api.h" -#include "chrome/browser/extensions/api/socket/socket_api_controller.h" +#include "chrome/browser/extensions/api/api_resource_controller.h" #include "chrome/browser/extensions/app_notification_manager.h" #include "chrome/browser/extensions/apps_promo.h" #include "chrome/browser/extensions/component_loader.h" @@ -387,7 +387,7 @@ ExtensionService::ExtensionService(Profile* profile, apps_promo_(profile->GetPrefs()), event_routers_initialized_(false), extension_warnings_(profile), - socket_controller_(NULL), + api_resource_controller_(NULL), app_shortcut_manager_(profile) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -484,15 +484,13 @@ ExtensionService::~ExtensionService() { provider->ServiceShutdown(); } - // TODO(miket): if we find ourselves adding more and more per-API - // controllers, we should manage them all with an - // APIControllerController (still working on that name). - if (socket_controller_) { - // If this check failed, then a unit test was using sockets but didn't - // provide the IO thread message loop needed for those sockets to do their - // job (including destroying themselves at shutdown). + if (api_resource_controller_) { + // If this check failed, then a unit test was using an APIResource but + // didn't provide the IO thread message loop needed for those resources to + // do their job (including destroying themselves at shutdown). DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO)); - BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, socket_controller_); + BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, + api_resource_controller_); } } @@ -2645,18 +2643,20 @@ ExtensionService::NaClModuleInfoList::iterator return nacl_module_list_.end(); } -extensions::SocketController* ExtensionService::socket_controller() { - // TODO(miket): Find a better place for SocketController to live. It needs - // to be scoped such that it can be created and destroyed on the IO thread. +extensions::APIResourceController* +ExtensionService::api_resource_controller() { + // TODO(miket): Find a better place for this thing to live. It needs to be + // scoped such that it can be created and destroyed on the IO thread. // // To coexist with certain unit tests that don't have an IO thread message // loop available at ExtensionService shutdown, we lazy-initialize this - // object so that those cases neither create nor destroy a SocketController. + // object so that those cases neither create nor destroy an + // APIResourceController. CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - if (!socket_controller_) { - socket_controller_ = new extensions::SocketController(); + if (!api_resource_controller_) { + api_resource_controller_ = new extensions::APIResourceController(); } - return socket_controller_; + return api_resource_controller_; } extensions::RulesRegistryService* ExtensionService::GetRulesRegistryService() { diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h index eaea412..819dc19 100644 --- a/chrome/browser/extensions/extension_service.h +++ b/chrome/browser/extensions/extension_service.h @@ -73,10 +73,10 @@ class ExtensionInputMethodEventRouter; } namespace extensions { +class APIResourceController; class ComponentLoader; class RulesRegistryService; class SettingsFrontend; -class SocketController; } // This is an interface class to encapsulate the dependencies that @@ -576,7 +576,7 @@ class ExtensionService } // Call only from IO thread. - extensions::SocketController* socket_controller(); + extensions::APIResourceController* api_resource_controller(); extensions::RulesRegistryService* GetRulesRegistryService(); @@ -833,7 +833,7 @@ class ExtensionService // We need to control destruction of this object (it needs to happen on the // IO thread), so we don't get to use any RAII devices with it. - extensions::SocketController* socket_controller_; + extensions::APIResourceController* api_resource_controller_; extensions::ProcessMap process_map_; |