diff options
Diffstat (limited to 'chrome/browser/chrome_plugin_host.cc')
-rw-r--r-- | chrome/browser/chrome_plugin_host.cc | 742 |
1 files changed, 742 insertions, 0 deletions
diff --git a/chrome/browser/chrome_plugin_host.cc b/chrome/browser/chrome_plugin_host.cc new file mode 100644 index 0000000..65600b8 --- /dev/null +++ b/chrome/browser/chrome_plugin_host.cc @@ -0,0 +1,742 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/browser/chrome_plugin_host.h" + +#include <set> + +#include "base/file_util.h" +#include "base/histogram.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "base/perftimer.h" +#include "base/singleton.h" +#include "base/string_util.h" +#include "net/base/cookie_monster.h" +#include "net/url_request/url_request_error_job.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/chrome_plugin_browsing_context.h" +#include "chrome/browser/dom_ui/html_dialog_contents.h" +#include "chrome/browser/net/dns_master.h" +#include "chrome/browser/plugin_process_host.h" +#include "chrome/browser/plugin_service.h" +#include "chrome/browser/profile.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_counters.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_plugin_lib.h" +#include "chrome/common/chrome_plugin_util.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/net/url_request_intercept_job.h" +#include "chrome/common/plugin_messages.h" + +#include "base/gfx/png_encoder.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "googleurl/src/gurl.h" +#include "net/base/base64.h" +#include "skia/include/SkBitmap.h" + +// This class manages the interception of network requests. It queries the +// plugin on every request, and creates an intercept job if the plugin can +// intercept the request. +// NOTE: All methods must be called on the IO thread. +class PluginRequestInterceptor + : public PluginHelper, public URLRequest::Interceptor { + public: + static URLRequestJob* UninterceptedProtocolHandler( + URLRequest* request, const std::string& scheme) { + // This will get called if a plugin failed to intercept a request for a + // protocol it has registered. In that case, we return NULL and the request + // will result in an error. + return new URLRequestErrorJob(request, net::ERR_FILE_NOT_FOUND); + } + + explicit PluginRequestInterceptor(ChromePluginLib* plugin) + : PluginHelper(plugin) { + URLRequest::RegisterRequestInterceptor(this); + } + + virtual ~PluginRequestInterceptor() { + URLRequest::UnregisterRequestInterceptor(this); + + // Unregister our protocols. + for (HandledProtocolList::iterator it = registered_protocols_.begin(); + it != registered_protocols_.end(); ++it) { + URLRequest::ProtocolFactory* factory = + URLRequest::RegisterProtocolFactory(*it, NULL); + DCHECK(factory == UninterceptedProtocolHandler); + } + } + + void RegisterProtocol(const std::string& scheme) { +#ifndef NDEBUG + DCHECK(MessageLoop::current() == message_loop_); +#endif + std::string lower_scheme = StringToLowerASCII(scheme); + handled_protocols_.insert(lower_scheme); + + // Only add a protocol factory if the URLRequest doesn't already handle + // it. If we fail to intercept, the request will be treated as an error. + if (!URLRequest::IsHandledProtocol(lower_scheme)) { + registered_protocols_.insert(lower_scheme); + URLRequest::RegisterProtocolFactory(lower_scheme, + &UninterceptedProtocolHandler); + } + } + + // URLRequest::Interceptor + virtual URLRequestJob* MaybeIntercept(URLRequest* request) { +#ifndef NDEBUG + DCHECK(MessageLoop::current() == message_loop_); +#endif + + if (!IsHandledProtocol(request->url().scheme())) + return NULL; + + CPBrowsingContext context = + CPBrowsingContextManager::Instance()->Lookup(request->context()); + scoped_ptr<ScopableCPRequest> cprequest( + new ScopableCPRequest(request->url().spec().c_str(), + request->method().c_str(), + context)); + + PerfTimer timer; + if (!plugin_->functions().should_intercept_request(cprequest.get())) { + LogInterceptMissTime(timer.Elapsed()); + return NULL; + } + LogInterceptHitTime(timer.Elapsed()); + return new URLRequestInterceptJob(request, plugin_, cprequest.release()); + } + + private: + bool IsHandledProtocol(const std::string& scheme) { + return handled_protocols_.find(scheme) != handled_protocols_.end(); + } + + void LogInterceptHitTime(const TimeDelta& time) { + UMA_HISTOGRAM_TIMES(L"Gears.InterceptHit", time); + } + + void LogInterceptMissTime(const TimeDelta& time) { + UMA_HISTOGRAM_TIMES(L"Gears.InterceptMiss", time); + } + + typedef std::set<std::string> HandledProtocolList; + HandledProtocolList handled_protocols_; + HandledProtocolList registered_protocols_; +}; + +// This class manages a network request made by the plugin, also acting as +// the URLRequest delegate. +// NOTE: All methods must be called on the IO thread. +class PluginRequestHandler : public PluginHelper, public URLRequest::Delegate { + public: + static PluginRequestHandler* FromCPRequest(CPRequest* request) { + return ScopableCPRequest::GetData<PluginRequestHandler*>(request); + } + + PluginRequestHandler(ChromePluginLib* plugin, ScopableCPRequest* cprequest) + : PluginHelper(plugin), cprequest_(cprequest) { + cprequest_->data = this; // see FromCPRequest(). + + URLRequestContext* context = CPBrowsingContextManager::Instance()-> + ToURLRequestContext(cprequest_->context); + // TODO(mpcomplete): remove fallback case when Gears support is prevalent. + if (!context) + context = Profile::GetDefaultRequestContext(); + + GURL gurl(cprequest_->url); + request_.reset(new URLRequest(gurl, this)); + request_->set_context(context); + request_->set_method(cprequest_->method); + request_->set_load_flags(PluginResponseUtils::CPLoadFlagsToNetFlags(0)); + } + + URLRequest* request() { return request_.get(); } + + // URLRequest::Delegate + virtual void OnReceivedRedirect(URLRequest* request, const GURL& new_url) { + plugin_->functions().response_funcs->received_redirect( + cprequest_.get(), new_url.spec().c_str()); + } + + virtual void OnResponseStarted(URLRequest* request) { + // TODO(mpcomplete): better error codes + CPError result = + request_->status().is_success() ? CPERR_SUCCESS : CPERR_FAILURE; + plugin_->functions().response_funcs->start_completed( + cprequest_.get(), result); + } + + virtual void OnReadCompleted(URLRequest* request, int bytes_read) { + // TODO(mpcomplete): better error codes + if (bytes_read < 0) + bytes_read = CPERR_FAILURE; + plugin_->functions().response_funcs->read_completed( + cprequest_.get(), bytes_read); + } + + private: + scoped_ptr<ScopableCPRequest> cprequest_; + scoped_ptr<URLRequest> request_; +}; + +// This class manages plugins that want to handle UI commands. Right now, we +// only allow 1 plugin to do this, so there's only ever 1 instance of this +// class at once. +// NOTE: All methods must be called on the IO thread. +class PluginCommandHandler : public PluginHelper { + public: + static void HandleCommand(int command, + CPCommandInterface* data, + int32 context_as_int32) { + CPBrowsingContext context = + static_cast<CPBrowsingContext>(context_as_int32); + // Ensure plugins are loaded before we try to talk to it. This will be a + // noop if plugins are loaded already. + ChromePluginLib::LoadChromePlugins(GetCPBrowserFuncsForBrowser()); + + DCHECK(ChromePluginLib::IsPluginThread()); + CPError rv = CPERR_INVALID_VERSION; + if (instance_ && instance_->plugin_->functions().handle_command) { + rv = instance_->plugin_->functions().handle_command( + context, command, data ? data->GetData() : NULL); + } + if (data) + data->OnCommandInvoked(rv); + } + + static void RegisterPlugin(ChromePluginLib* plugin) { + DCHECK(ChromePluginLib::IsPluginThread()); + // TODO(mpcomplete): We only expect to have Gears register a command handler + // at the moment. We should either add support for other plugins to do + // this, or verify that the plugin is Gears. + DCHECK(!instance_) << + "We only support a single plugin handling UI commands."; + if (instance_) + return; + // Deleted in response to a notification in PluginHelper. + new PluginCommandHandler(plugin); + } + + private: + explicit PluginCommandHandler(ChromePluginLib* plugin) + : PluginHelper(plugin) { + DCHECK(instance_ == NULL); + instance_ = this; + } + + virtual ~PluginCommandHandler() { + instance_ = NULL; + } + + static PluginCommandHandler* instance_; +}; + +PluginCommandHandler* PluginCommandHandler::instance_ = NULL; + +// This class acts as a helper to display the HTML dialog. It is created +// on demand on the plugin thread, and proxies calls to and from the UI thread +// to display the UI. +class ModelessHtmlDialogDelegate : public HtmlDialogContentsDelegate { + public: + ModelessHtmlDialogDelegate(const GURL& url, + int width, int height, + const std::string& json_arguments, + void* plugin_context, + ChromePluginLib* plugin, + MessageLoop* main_message_loop, + HWND parent_hwnd) + : plugin_(plugin), + plugin_context_(plugin_context), + main_message_loop_(main_message_loop), + io_message_loop_(MessageLoop::current()), + parent_hwnd_(parent_hwnd) { + DCHECK(ChromePluginLib::IsPluginThread()); + params_.url = url; + params_.height = height; + params_.width = width; + params_.json_input = json_arguments; + + main_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &ModelessHtmlDialogDelegate::Show)); + } + ~ModelessHtmlDialogDelegate() { + DCHECK(ChromePluginLib::IsPluginThread()); + } + + // The following public methods are called from the UI thread. + + // ChromeViews::WindowDelegate implementation: + virtual bool IsModal() const { return false; } + + // HtmlDialogContentsDelegate implementation: + virtual GURL GetDialogContentURL() const { return params_.url; } + virtual void GetDialogSize(CSize* size) const { + size->cx = params_.width; + size->cy = params_.height; + } + virtual std::string GetDialogArgs() const { return params_.json_input; } + virtual void OnDialogClosed(const std::string& json_retval) { + io_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &ModelessHtmlDialogDelegate::ReportResults, json_retval)); + } + + private: + // Actually shows the dialog on the UI thread. + void Show() { + DCHECK(MessageLoop::current() == main_message_loop_); + Browser* browser = BrowserList::GetLastActive(); + browser->ShowHtmlDialog(this, parent_hwnd_); + } + + // Gives the JSON result string back to the plugin. + void ReportResults(const std::string& json_retval) { + DCHECK(MessageLoop::current() == io_message_loop_); + // The plugin may have unloaded before it was time to report the results. + if (plugin_->is_loaded()) + plugin_->functions().html_dialog_closed(plugin_context_, + json_retval.c_str()); + delete this; + } + + // The parameters needed to display a modal HTML dialog. + HtmlDialogContents::HtmlDialogParams params_; + + // Message loops for sending messages between UI and IO threads. + MessageLoop* main_message_loop_; + MessageLoop* io_message_loop_; + + // The plugin object that requested the dialog. This can only be accessed on + // the IO thread. + scoped_refptr<ChromePluginLib> plugin_; + + // The plugin's context argument to CPB_ShowHtmlDialog. + void* plugin_context_; + + // The window this dialog box should be parented to, or NULL for the last + // active browser window. + HWND parent_hwnd_; + + DISALLOW_EVIL_CONSTRUCTORS(ModelessHtmlDialogDelegate); +}; + +// Allows InvokeLater without adding refcounting. The object is only deleted +// when its last InvokeLater is run anyway. +void RunnableMethodTraits<ModelessHtmlDialogDelegate>::RetainCallee( + ModelessHtmlDialogDelegate* remover) { +} +void RunnableMethodTraits<ModelessHtmlDialogDelegate>::ReleaseCallee( + ModelessHtmlDialogDelegate* remover) { +} + +namespace { + +// +// Generic functions +// + +void STDCALL CPB_SetKeepProcessAlive(CPID id, CPBool keep_alive) { + // This is a no-op in the main browser process +} + +CPError STDCALL CPB_GetCookies(CPID id, CPBrowsingContext bcontext, + const char* url, char** cookies) { + CHECK(ChromePluginLib::IsPluginThread()); + URLRequestContext* context = CPBrowsingContextManager::Instance()-> + ToURLRequestContext(bcontext); + // TODO(mpcomplete): remove fallback case when Gears support is prevalent. + if (!context) { + context = Profile::GetDefaultRequestContext(); + if (!context) + return CPERR_FAILURE; + } + std::string cookies_str = context->cookie_store()->GetCookies(GURL(url)); + *cookies = CPB_StringDup(CPB_Alloc, cookies_str); + return CPERR_SUCCESS; +} + +CPError STDCALL CPB_ShowHtmlDialogModal( + CPID id, CPBrowsingContext context, const char* url, int width, int height, + const char* json_arguments, char** json_retval) { + // Should not be called in browser process. + return CPERR_FAILURE; +} + +CPError STDCALL CPB_ShowHtmlDialog( + CPID id, CPBrowsingContext context, const char* url, int width, int height, + const char* json_arguments, void* plugin_context) { + CHECK(ChromePluginLib::IsPluginThread()); + ChromePluginLib* plugin = ChromePluginLib::FromCPID(id); + CHECK(plugin); + + HWND parent_hwnd = reinterpret_cast<HWND>(static_cast<uintptr_t>(context)); + PluginService* service = PluginService::GetInstance(); + if (!service) + return CPERR_FAILURE; + MessageLoop* main_message_loop = service->main_message_loop(); + ModelessHtmlDialogDelegate* delegate = + new ModelessHtmlDialogDelegate(GURL(url), width, height, json_arguments, + plugin_context, plugin, main_message_loop, + parent_hwnd); + return CPERR_SUCCESS; +} + +CPError STDCALL CPB_GetCommandLineArguments( + CPID id, CPBrowsingContext context, const char* url, char** arguments) { + CHECK(ChromePluginLib::IsPluginThread()); + std::string arguments_str; + CPError rv = CPB_GetCommandLineArgumentsCommon(url, &arguments_str); + if (rv == CPERR_SUCCESS) + *arguments = CPB_StringDup(CPB_Alloc, arguments_str); + return rv; +} + +CPBrowsingContext STDCALL CPB_GetBrowsingContextFromNPP(struct _NPP* npp) { + CHECK(ChromePluginLib::IsPluginThread()); + NOTREACHED() << "NPP does not exist in the browser process."; + return 0; +} + +int STDCALL CPB_GetBrowsingContextInfo( + CPID id, CPBrowsingContext context, CPBrowsingContextInfoType type, + void* buf, uint32 buf_size) { + CHECK(ChromePluginLib::IsPluginThread()); + switch (type) { + case CPBROWSINGCONTEXT_DATA_DIR_PTR: { + if (buf_size < sizeof(char*)) + return sizeof(char*); + + // TODO(mpcomplete): http://b/1143021 - When we support multiple profiles, + // fetch the data dir from the context. + PluginService* service = PluginService::GetInstance(); + if (!service) + return CPERR_FAILURE; + std::wstring wretval = service->GetChromePluginDataDir(); + file_util::AppendToPath(&wretval, chrome::kChromePluginDataDirname); + *static_cast<char**>(buf) = CPB_StringDup(CPB_Alloc, WideToUTF8(wretval)); + return CPERR_SUCCESS; + } + case CPBROWSINGCONTEXT_UI_LOCALE_PTR: { + if (buf_size < sizeof(char*)) + return sizeof(char*); + + PluginService* service = PluginService::GetInstance(); + if (!service) + return CPERR_FAILURE; + const std::wstring& wretval = service->GetUILocale(); + *static_cast<char**>(buf) = CPB_StringDup(CPB_Alloc, WideToUTF8(wretval)); + return CPERR_SUCCESS; + } + } + + return CPERR_FAILURE; +} + +CPError STDCALL CPB_AddUICommand(CPID id, int command) { + CHECK(ChromePluginLib::IsPluginThread()); + ChromePluginLib* plugin = ChromePluginLib::FromCPID(id); + CHECK(plugin); + + PluginCommandHandler::RegisterPlugin(plugin); + return CPERR_SUCCESS; +} + +static void NotifyGearsShortcutsChanged() { + DCHECK(MessageLoop::current() == + PluginService::GetInstance()->main_message_loop()); + + // TODO(michaeln): source should be the original profile, fix this + // when gears provides the correct browser context, and when we + // can relate that to an actual profile. + NotificationService::current()->Notify( + NOTIFY_WEB_APP_INSTALL_CHANGED, + Source<Profile>(NULL), + NotificationService::NoDetails()); +} + +CPError STDCALL CPB_HandleCommand( + CPID id, CPBrowsingContext context, int command, void *data) { + CHECK(ChromePluginLib::IsPluginThread()); + ChromePluginLib* plugin = ChromePluginLib::FromCPID(id); + CHECK(plugin); + + if (command == GEARSBROWSERCOMMAND_CREATE_SHORTCUT_DONE) { + GearsCreateShortcutResult* result = + static_cast<GearsCreateShortcutResult*>(data); + CHECK(result); + + GearsCreateShortcutData* shortcut_data = + static_cast<GearsCreateShortcutData*>(result->shortcut); + shortcut_data->command_interface->OnCommandResponse(result->result); + } else if (command == GEARSBROWSERCOMMAND_NOTIFY_SHORTCUTS_CHANGED) { + PluginService* service = PluginService::GetInstance(); + if (!service) + return CPERR_FAILURE; + service->main_message_loop()->PostTask(FROM_HERE, + NewRunnableFunction(NotifyGearsShortcutsChanged)); + return CPERR_SUCCESS; + } + return CPERR_FAILURE; +} + +// +// Functions related to network interception +// + +void STDCALL CPB_EnableRequestIntercept(CPID id, const char** schemes, + uint32 num_schemes) { + CHECK(ChromePluginLib::IsPluginThread()); + ChromePluginLib* plugin = ChromePluginLib::FromCPID(id); + CHECK(plugin); + + if (schemes && num_schemes > 0) { + PluginRequestInterceptor* interceptor = + new PluginRequestInterceptor(plugin); + for (uint32 i = 0; i < num_schemes; ++i) + interceptor->RegisterProtocol(schemes[i]); + } else { + PluginRequestInterceptor::DestroyAllHelpersForPlugin(plugin); + } +} + +void STDCALL CPRR_ReceivedRedirect(CPRequest* request, const char* new_url) { +} + +void STDCALL CPRR_StartCompleted(CPRequest* request, CPError result) { + CHECK(ChromePluginLib::IsPluginThread()); + URLRequestInterceptJob* job = URLRequestInterceptJob::FromCPRequest(request); + CHECK(job); + job->OnStartCompleted(result); +} + +void STDCALL CPRR_ReadCompleted(CPRequest* request, int bytes_read) { + CHECK(ChromePluginLib::IsPluginThread()); + URLRequestInterceptJob* job = URLRequestInterceptJob::FromCPRequest(request); + CHECK(job); + job->OnReadCompleted(bytes_read); +} + +void STDCALL CPRR_UploadProgress(CPRequest* request, uint64 pos, uint64 size) { + // Does not apply, plugins do not yet intercept uploads +} + +// +// Functions related to serving network requests to the plugin +// + +CPError STDCALL CPB_CreateRequest(CPID id, CPBrowsingContext context, + const char* method, const char* url, + CPRequest** request) { + CHECK(ChromePluginLib::IsPluginThread()); + ChromePluginLib* plugin = ChromePluginLib::FromCPID(id); + CHECK(plugin); + + ScopableCPRequest* cprequest = new ScopableCPRequest(url, method, context); + PluginRequestHandler* handler = new PluginRequestHandler(plugin, cprequest); + + *request = cprequest; + return CPERR_SUCCESS; +} + +CPError STDCALL CPR_StartRequest(CPRequest* request) { + CHECK(ChromePluginLib::IsPluginThread()); + PluginRequestHandler* handler = PluginRequestHandler::FromCPRequest(request); + CHECK(handler); + handler->request()->Start(); + return CPERR_IO_PENDING; +} + +void STDCALL CPR_EndRequest(CPRequest* request, CPError reason) { + CHECK(ChromePluginLib::IsPluginThread()); + PluginRequestHandler* handler = PluginRequestHandler::FromCPRequest(request); + delete handler; +} + +void STDCALL CPR_SetExtraRequestHeaders(CPRequest* request, + const char* headers) { + CHECK(ChromePluginLib::IsPluginThread()); + PluginRequestHandler* handler = PluginRequestHandler::FromCPRequest(request); + CHECK(handler); + handler->request()->SetExtraRequestHeaders(headers); +} + +void STDCALL CPR_SetRequestLoadFlags(CPRequest* request, uint32 flags) { + CHECK(ChromePluginLib::IsPluginThread()); + PluginRequestHandler* handler = PluginRequestHandler::FromCPRequest(request); + CHECK(handler); + uint32 net_flags = PluginResponseUtils::CPLoadFlagsToNetFlags(flags); + handler->request()->set_load_flags(net_flags); +} + +void STDCALL CPR_AppendDataToUpload(CPRequest* request, const char* bytes, + int bytes_len) { + CHECK(ChromePluginLib::IsPluginThread()); + PluginRequestHandler* handler = PluginRequestHandler::FromCPRequest(request); + CHECK(handler); + handler->request()->AppendBytesToUpload(bytes, bytes_len); +} + +CPError STDCALL CPR_AppendFileToUpload(CPRequest* request, const char* filepath, + uint64 offset, uint64 length) { + CHECK(ChromePluginLib::IsPluginThread()); + PluginRequestHandler* handler = PluginRequestHandler::FromCPRequest(request); + CHECK(handler); + + if (!length) length = kuint64max; + std::wstring wfilepath(UTF8ToWide(filepath)); + handler->request()->AppendFileRangeToUpload(wfilepath, offset, length); + return CPERR_SUCCESS; +} + +int STDCALL CPR_GetResponseInfo(CPRequest* request, CPResponseInfoType type, + void* buf, uint32 buf_size) { + CHECK(ChromePluginLib::IsPluginThread()); + PluginRequestHandler* handler = PluginRequestHandler::FromCPRequest(request); + CHECK(handler); + return PluginResponseUtils::GetResponseInfo( + handler->request()->response_headers(), type, buf, buf_size); +} + +int STDCALL CPR_Read(CPRequest* request, void* buf, uint32 buf_size) { + CHECK(ChromePluginLib::IsPluginThread()); + PluginRequestHandler* handler = PluginRequestHandler::FromCPRequest(request); + CHECK(handler); + + int bytes_read; + if (handler->request()->Read(static_cast<char*>(buf), buf_size, &bytes_read)) + return bytes_read; // 0 == CPERR_SUCESS + + if (handler->request()->status().is_io_pending()) + return CPERR_IO_PENDING; + + return CPERR_FAILURE; +} + +CPBool STDCALL CPB_IsPluginProcessRunning(CPID id) { + CHECK(ChromePluginLib::IsPluginThread()); + ChromePluginLib* plugin = ChromePluginLib::FromCPID(id); + CHECK(plugin); + PluginService* service = PluginService::GetInstance(); + if (!service) + return false; + PluginProcessHost *host = service->FindPluginProcess(plugin->filename()); + return host ? true : false; +} + +CPProcessType STDCALL CPB_GetProcessType(CPID id) { + CHECK(ChromePluginLib::IsPluginThread()); + return CP_PROCESS_BROWSER; +} + +CPError STDCALL CPB_SendMessage(CPID id, const void *data, uint32 data_len) { + CHECK(ChromePluginLib::IsPluginThread()); + ChromePluginLib* plugin = ChromePluginLib::FromCPID(id); + CHECK(plugin); + + PluginService* service = PluginService::GetInstance(); + if (!service) + return CPERR_FAILURE; + PluginProcessHost *host = + service->FindOrStartPluginProcess(plugin->filename(), std::string()); + if (!host) + return CPERR_FAILURE; + + const unsigned char* data_ptr = static_cast<const unsigned char*>(data); + std::vector<uint8> v(data_ptr, data_ptr + data_len); + if (!host->Send(new PluginProcessMsg_PluginMessage(v))) + return CPERR_FAILURE; + + return CPERR_SUCCESS; +} + +} + +CPBrowserFuncs* GetCPBrowserFuncsForBrowser() { + static CPBrowserFuncs browser_funcs; + static CPRequestFuncs request_funcs; + static CPResponseFuncs response_funcs; + static bool initialized = false; + if (!initialized) { + initialized = true; + + browser_funcs.size = sizeof(browser_funcs); + browser_funcs.version = CP_VERSION; + browser_funcs.enable_request_intercept = CPB_EnableRequestIntercept; + browser_funcs.create_request = CPB_CreateRequest; + browser_funcs.get_cookies = CPB_GetCookies; + browser_funcs.alloc = CPB_Alloc; + browser_funcs.free = CPB_Free; + browser_funcs.set_keep_process_alive = CPB_SetKeepProcessAlive; + browser_funcs.show_html_dialog_modal = CPB_ShowHtmlDialogModal; + browser_funcs.show_html_dialog = CPB_ShowHtmlDialog; + browser_funcs.is_plugin_process_running = CPB_IsPluginProcessRunning; + browser_funcs.get_process_type = CPB_GetProcessType; + browser_funcs.send_message = CPB_SendMessage; + browser_funcs.get_browsing_context_from_npp = CPB_GetBrowsingContextFromNPP; + browser_funcs.get_browsing_context_info = CPB_GetBrowsingContextInfo; + browser_funcs.get_command_line_arguments = CPB_GetCommandLineArguments; + browser_funcs.add_ui_command = CPB_AddUICommand; + browser_funcs.handle_command = CPB_HandleCommand; + + browser_funcs.request_funcs = &request_funcs; + browser_funcs.response_funcs = &response_funcs; + + request_funcs.size = sizeof(request_funcs); + request_funcs.start_request = CPR_StartRequest; + request_funcs.end_request = CPR_EndRequest; + request_funcs.set_extra_request_headers = CPR_SetExtraRequestHeaders; + request_funcs.set_request_load_flags = CPR_SetRequestLoadFlags; + request_funcs.append_data_to_upload = CPR_AppendDataToUpload; + request_funcs.get_response_info = CPR_GetResponseInfo; + request_funcs.read = CPR_Read; + request_funcs.append_file_to_upload = CPR_AppendFileToUpload; + + response_funcs.size = sizeof(response_funcs); + response_funcs.received_redirect = CPRR_ReceivedRedirect; + response_funcs.start_completed = CPRR_StartCompleted; + response_funcs.read_completed = CPRR_ReadCompleted; + response_funcs.upload_progress = CPRR_UploadProgress; + } + + return &browser_funcs; +} + +void CPHandleCommand(int command, CPCommandInterface* data, + CPBrowsingContext context) { + // Sadly if we try and pass context through, we seem to break cl's little + // brain trying to compile the Tuple3 ctor. This cast works. + int32 context_as_int32 = static_cast<int32>(context); + // Plugins can only be accessed on the IO thread. + g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, + NewRunnableFunction(PluginCommandHandler::HandleCommand, + command, data, context_as_int32)); +} |