diff options
author | initial.commit@chromium.org <initial.commit@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-02 02:14:31 +0000 |
---|---|---|
committer | initial.commit@chromium.org <initial.commit@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-02 02:14:31 +0000 |
commit | 5a7bdf208c28c210b39cff63d1cf91302b02821b (patch) | |
tree | 5ff484561562f78b29d2670168a9e3b40b48dcab /ceee/ie/broker/api_dispatcher.h | |
parent | 26a54a5e0f4603c8fda2fe51789d331125993c9a (diff) | |
download | chromium_src-5a7bdf208c28c210b39cff63d1cf91302b02821b.zip chromium_src-5a7bdf208c28c210b39cff63d1cf91302b02821b.tar.gz chromium_src-5a7bdf208c28c210b39cff63d1cf91302b02821b.tar.bz2 |
Checking in the initial version of CEEE (Chrome Extensions Execution
Environment), an optional feature of Chrome Frame that acts as an
adapter layer for a subset of the Chrome Extension APIs. This enables
extensions that stick to the supported subset of APIs to work in the
context of Chrome Frame with minimal or sometimes no changes.
See http://www.chromium.org/developers/design-documents/ceee for an
overview of the design of CEEE.
TEST=unit tests (run ceee/smoke_tests.bat as an administrator on Windows)
BUG=none
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@64712 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ceee/ie/broker/api_dispatcher.h')
-rw-r--r-- | ceee/ie/broker/api_dispatcher.h | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/ceee/ie/broker/api_dispatcher.h b/ceee/ie/broker/api_dispatcher.h new file mode 100644 index 0000000..db13110 --- /dev/null +++ b/ceee/ie/broker/api_dispatcher.h @@ -0,0 +1,377 @@ +// Copyright (c) 2010 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. +// +// Dispatcher and registry for Chrome Extension APIs. + +#ifndef CEEE_IE_BROKER_API_DISPATCHER_H_ +#define CEEE_IE_BROKER_API_DISPATCHER_H_ + +#include <list> +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/singleton.h" +#include "base/values.h" + +#include "broker_lib.h" // NOLINT + +class ExecutorsManager; + +// Keeps a registry of all API Invocation implementations and implements +// the logic needed to deserialize API Invocation requests, dispatch them, +// and serialize and return the response. +class ApiDispatcher { + public: + ApiDispatcher() : thread_id_(0) {} + virtual ~ApiDispatcher() {} + + // Dispatches a Chrome Extensions API request and sends back the response. + // + // @param message_text The raw JSON-encoded text of the API request over + // the automation interface. + // @param response Where to return the JSON-encoded response. + virtual void HandleApiRequest(BSTR message_text, BSTR* response); + + // Fire the given event message to Chrome Frame and potentially use it + // to complete pending extension API execution. + // + // @param event_name The name of the event to fire. + // @param event_args The JSON encoded event arguments. + virtual void FireEvent(BSTR event_name, BSTR event_args); + + // This class holds on the result and can be derived from to generate results + // that are either specific to tabs or windows for example. + class InvocationResult { + public: + static const int kNoRequestId = -1; + explicit InvocationResult(int request_id) + : request_id_(request_id) { + } + virtual ~InvocationResult(); + + // Post the response string from our current result to Chrome Frame. + virtual void PostResult(); + + // Post the given error string to Chrome Frame. + virtual void PostError(const std::string& error); + + // Identifies if we are pending a post (for which we need a request id). + virtual bool Pending() const { return request_id_ != kNoRequestId; } + + // Returns the result of the API invocation as a value object which + // is still owned by this object - the caller does not take ownership. + // May return NULL if execution didn't produce any results yet. + const Value* value() const { + return value_.get(); + } + void set_value(Value* value) { + value_.reset(value); + } + + // Sets a value in a dictionary. The Value pointer is kept in the + // dictionary which takes ownership so the caller should NOT deallocate it. + // Even in case of errors, the value will be deallocated. + // This value can then be retrieved by GetValue below, this can be useful + // to keep information in the InvocationResult during async handling. + virtual void SetValue(const char* name, Value* value); + + // Returns a value set by SetValue above. Note that the value is only + // valid during the lifetime of the InvocationResult object or until another + // value is set for the same name. Also, the returned value should NOT + // be deallocated by the caller, it will get deallocated on destruction + // of the object, or when another value is set for the same name. + // Returns NULL if no value of this name have been previously set. + virtual const Value* GetValue(const char* name); + + protected: + // A unit test seam. + virtual ApiDispatcher* GetDispatcher(); + + // A helper function to post the given response to Chrome Frame. + virtual void PostResponseToChromeFrame(const char* response_key, + const std::string& response_str); + // Where to store temporary values. + scoped_ptr<DictionaryValue> temp_values_; + + // Where to store the result value. + scoped_ptr<Value> value_; + + // Invocation request identifier. + int request_id_; + + DISALLOW_COPY_AND_ASSIGN(InvocationResult); + }; + + // Base class for API Invocation Execution registered with this object. + // A new execution object is created for each API invocation call even though + // the state data is stored in the classes deriving from InvocationResult + // (declared above). This is to allow easier testing of the Invocation + // implementation by having virtual methods like GetDispatcher to be used + // as a test seam, or other specific methods (like + // ApiResultCreator<>::CreateApiResult). Otherwise, Invocation::Execute + // could be a static callback as the event handlers described below. + class Invocation { + public: + Invocation() {} + virtual ~Invocation() {} + + // Called when a request to invoke the execution of an API is received. + // + // @param args The list value object for the arguments passed. + // @param request_id The identifier of the request being executed. + virtual void Execute(const ListValue& args, int request_id) = 0; + + protected: + // Unit test seam. + virtual ApiDispatcher* GetDispatcher(); + + DISALLOW_COPY_AND_ASSIGN(Invocation); + }; + + // The Invocation factory pointers to be stored in the factory map. + typedef Invocation* (*InvocationFactory)(); + + // The permanent event handlers are stored in a specific map and don't get + // removed after they are called. They are also registered without user data. + // The handler can stop the broadcast of the event by returning false. + typedef bool (*PermanentEventHandler)(const std::string& input_args, + std::string* converted_args, + ApiDispatcher* dispatcher); + + // The ephemeral event handlers are stored in a specific map and they get + // removed after a successfully completed call or an error (a successful call + // is one which returns S_OK, S_FALSE is returned to identify that this + // set of arguments is not the one we were waiting for, so we need to wait + // some more, and returning a FAILED HRESULT causes the handler to be removed + // from the queue). + // EphemeralEventHandlers can specify user data to be passed back to them + // when the event occurs. The dynamic data allocation and release is the + // responsibility of the caller/handler pair, the dispatcher only keeps the + // pointer and passes it back to the handler as is without any post-cleanup. + // Also note that the Ephemeral event handlers are called after the Permanent + // handlers which may have had the chance to augment the content of the + // arguments which are passed to the ephemeral handlers. So these are useful + // for asynchronous invocation completion. + // We also pass in the ApiDispatcher for ease of test mocking. + typedef HRESULT (*EphemeralEventHandler)(const std::string& input_args, + InvocationResult* user_data, + ApiDispatcher* dispatcher); + + // Registers a factory for an API Invocation of a given name. Note that + // registering the same name more than once is not permitted. + // + // @param function_name The name of the function handled by the Invocation. + // @param factory The factory function to call to create a new instance of + // the Invocation type to handle this function. + virtual void RegisterInvocation(const char* function_name, + InvocationFactory factory); + + // Registers a permanent handler for an event of a given name. Note that + // registering the same name more than once is not permitted. + // + // @param event_name The name of the event to handle. + // @param event_handler The event handler to call to handle the event. + virtual void RegisterPermanentEventHandler( + const char* event_name, PermanentEventHandler event_handler); + + // Registers an ephemeral handler for an event of a given name. Note that + // registering the same name more than once is permitted. When an event is + // fired, all ephemeral handlers are called after the permanent one if + // one was registered. + // + // @param event_name The name of the event to handle. + // @param event_handler The event handler to call to handle the event. + // @param user_data The data that has to be passed back to the handler. + virtual void RegisterEphemeralEventHandler( + const char* event_name, + EphemeralEventHandler event_handler, + InvocationResult* user_data); + + // Sets the thread id of the ApiInvocation thread so that we can make sure + // that we are only ran from that thread. Only used for debug purposes. + virtual void SetApiInvocationThreadId(DWORD thread_id) { + thread_id_ = thread_id; + } + + // Fetch the appropriate executor to execute code for the given window. + // + // @param window The window for which we want an executor. + // @param iid The identifier of the interface we want to work with. + // @param executor Where to return the requested executor interface pointer. + virtual void GetExecutor(HWND window, REFIID iid, void** executor); + + // Return a tab handle associated with the id. + // + // @param tab_id The tab identifier. + // @return The corresponding HWND (or INVALID_HANDLE_VALUE if tab_id isn't + // found). + virtual HWND GetTabHandleFromId(int tab_id) const; + + // Return a window handle associated with the id. + // + // @param window_id The window identifier. + // @return The corresponding HWND (or INVALID_HANDLE_VALUE if window_id isn't + // found). + virtual HWND GetWindowHandleFromId(int window_id) const; + + // Return a tab id associated with the HWND. + // + // @param tab_handle The tab HWND. + // @return The corresponding tab id (or 0 if tab_handle isn't found). + virtual int GetTabIdFromHandle(HWND tab_handle) const; + + // Return a window id associated with the HWND. + // + // @param window_handle The window HWND. + // @return The corresponding window id (or 0 if window_handle isn't found). + virtual int GetWindowIdFromHandle(HWND window_handle) const; + + // Register the relation between a tab_id and a HWND. + virtual void SetTabIdForHandle(long tab_id, HWND tab_handle); + + // Unregister the HWND and its corresponding tab_id. + virtual void DeleteTabHandle(HWND handle); + + protected: + typedef std::map<std::string, InvocationFactory> FactoryMap; + FactoryMap factories_; + + typedef std::map<std::string, PermanentEventHandler> + PermanentEventHandlersMap; + PermanentEventHandlersMap permanent_event_handlers_; + + // For ephemeral event handlers we also need to keep the user data. + struct EphemeralEventHandlerTuple { + EphemeralEventHandlerTuple( + EphemeralEventHandler handler, + InvocationResult* user_data) + : handler(handler), user_data(user_data) {} + EphemeralEventHandler handler; + InvocationResult* user_data; + }; + + // We can have a list of ephemeral event handlers for a given event name. + typedef std::vector<EphemeralEventHandlerTuple> + EphemeralEventHandlersList; + typedef std::map<std::string, EphemeralEventHandlersList> + EphemeralEventHandlersMap; + EphemeralEventHandlersMap ephemeral_event_handlers_; + + // The thread we are running into. + DWORD thread_id_; + + // Make sure this is always called from the same thread, + bool IsRunningInSingleThread(); + + DISALLOW_COPY_AND_ASSIGN(ApiDispatcher); +}; + +// Convenience InvocationFactory implementation. +template <class InvocationType> +ApiDispatcher::Invocation* NewApiInvocation() { + return new InvocationType(); +} + +// A singleton that initializes and keeps the ApiDispatcher used by production +// code. +class ProductionApiDispatcher : public ApiDispatcher, + public Singleton<ProductionApiDispatcher> { + private: + // This ensures no construction is possible outside of the class itself. + friend struct DefaultSingletonTraits<ProductionApiDispatcher>; + DISALLOW_IMPLICIT_CONSTRUCTORS(ProductionApiDispatcher); +}; + +// A convenience class that can be derived from by API function classes instead +// of ApiDispatcher::Invocation. Mainly benefits ease of testing, so that we +// can specify a mocked result object. +template<class ResultType = ApiDispatcher::InvocationResult> +class ApiResultCreator : public ApiDispatcher::Invocation { + protected: + // Allocates a new instance of ResultType. The caller of this function takes + // ownership of this instance and is responsible for freeing it. + virtual ResultType* CreateApiResult(int request_id) { + return new ResultType(request_id); + } +}; + +template<class InvocationBase> +class IterativeApiResult : public InvocationBase { + public: + explicit IterativeApiResult(int request_id) + : InvocationBase(request_id), result_accumulator_(new ListValue()) { + } + + // Unlike the base class, this hack subverts the process by accumulating + // responses (positive and errors) rather than posting them right away. + virtual void PostResult() { + DCHECK(value() != NULL); + DCHECK(result_accumulator_ != NULL); + + if (result_accumulator_ != NULL) + result_accumulator_->Append(value_.release()); + } + + virtual void PostError(const std::string& error) { + error_accumulator_.push_back(error); + } + + // Finally post whatever was reported as result. + virtual void FlushAllPosts() { + if (AllFailed()) { + CallRealPostError(error_accumulator_.back()); + } else { + // Post success as per base class implementation. Declare all is fine + // even if !AllSucceeded. + // TODO(motek@google.com): Perhaps we should post a different + // message post for 'not quite ok, but almost'? + set_value(result_accumulator_.release()); + CallRealPostResult(); + } + error_accumulator_.clear(); + } + + bool AllSucceeded() const { + DCHECK(result_accumulator_ != NULL); + return result_accumulator_ != NULL && !result_accumulator_->empty() && + error_accumulator_.empty(); + } + + bool AllFailed() const { + DCHECK(result_accumulator_ != NULL); + return !error_accumulator_.empty() && + (result_accumulator_ == NULL || result_accumulator_->empty()); + } + + bool IsEmpty() const { + DCHECK(result_accumulator_ != NULL); + return (result_accumulator_ == NULL || result_accumulator_->empty()) && + error_accumulator_.empty(); + } + + const std::string LastError() const { + if (!error_accumulator_.empty()) + return error_accumulator_.back(); + return std::string(); + } + + private: + // Redirected invocations of base class's 'post function' create test seam. + virtual void CallRealPostResult() { + InvocationBase::PostResult(); + } + virtual void CallRealPostError(const std::string& error) { + InvocationBase::PostError(error); + } + + scoped_ptr<ListValue> result_accumulator_; + std::list<std::string> error_accumulator_; +}; + + +#endif // CEEE_IE_BROKER_API_DISPATCHER_H_ |