diff options
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_ |