1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/browser/renderer_host/pepper/pepper_network_proxy_host.h" 6 7 #include "base/bind.h" 8 #include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h" 9 #include "content/browser/renderer_host/pepper/pepper_socket_utils.h" 10 #include "content/public/browser/browser_context.h" 11 #include "content/public/browser/browser_thread.h" 12 #include "content/public/browser/render_process_host.h" 13 #include "content/public/common/socket_permission_request.h" 14 #include "net/base/net_errors.h" 15 #include "net/proxy/proxy_info.h" 16 #include "net/url_request/url_request_context.h" 17 #include "net/url_request/url_request_context_getter.h" 18 #include "ppapi/c/pp_errors.h" 19 #include "ppapi/host/dispatch_host_message.h" 20 #include "ppapi/host/ppapi_host.h" 21 #include "ppapi/proxy/ppapi_messages.h" 22 23 namespace content { 24 25 PepperNetworkProxyHost::PepperNetworkProxyHost(BrowserPpapiHostImpl* host, 26 PP_Instance instance, 27 PP_Resource resource) 28 : ResourceHost(host->GetPpapiHost(), instance, resource), 29 proxy_service_(NULL), 30 is_allowed_(false), 31 waiting_for_ui_thread_data_(true), 32 weak_factory_(this) { 33 int render_process_id(0), render_frame_id(0); 34 host->GetRenderFrameIDsForInstance( 35 instance, &render_process_id, &render_frame_id); 36 BrowserThread::PostTaskAndReplyWithResult( 37 BrowserThread::UI, 38 FROM_HERE, 39 base::Bind(&GetUIThreadDataOnUIThread, 40 render_process_id, 41 render_frame_id, 42 host->external_plugin()), 43 base::Bind(&PepperNetworkProxyHost::DidGetUIThreadData, 44 weak_factory_.GetWeakPtr())); 45 } 46 47 PepperNetworkProxyHost::~PepperNetworkProxyHost() { 48 while (!pending_requests_.empty()) { 49 // If the proxy_service_ is NULL, we shouldn't have any outstanding 50 // requests. 51 DCHECK(proxy_service_); 52 net::ProxyService::PacRequest* request = pending_requests_.front(); 53 proxy_service_->CancelPacRequest(request); 54 pending_requests_.pop(); 55 } 56 } 57 58 PepperNetworkProxyHost::UIThreadData::UIThreadData() : is_allowed(false) {} 59 60 PepperNetworkProxyHost::UIThreadData::~UIThreadData() {} 61 62 // static 63 PepperNetworkProxyHost::UIThreadData 64 PepperNetworkProxyHost::GetUIThreadDataOnUIThread(int render_process_id, 65 int render_frame_id, 66 bool is_external_plugin) { 67 DCHECK_CURRENTLY_ON(BrowserThread::UI); 68 PepperNetworkProxyHost::UIThreadData result; 69 RenderProcessHost* render_process_host = 70 RenderProcessHost::FromID(render_process_id); 71 if (render_process_host && render_process_host->GetBrowserContext()) { 72 result.context_getter = 73 render_process_host->GetBrowserContext() 74 ->GetRequestContextForRenderProcess(render_process_id); 75 } 76 77 SocketPermissionRequest request( 78 content::SocketPermissionRequest::RESOLVE_PROXY, std::string(), 0); 79 result.is_allowed = 80 pepper_socket_utils::CanUseSocketAPIs(is_external_plugin, 81 false /* is_private_api */, 82 &request, 83 render_process_id, 84 render_frame_id); 85 return result; 86 } 87 88 void PepperNetworkProxyHost::DidGetUIThreadData( 89 const UIThreadData& ui_thread_data) { 90 is_allowed_ = ui_thread_data.is_allowed; 91 if (ui_thread_data.context_getter.get() && 92 ui_thread_data.context_getter->GetURLRequestContext()) { 93 proxy_service_ = 94 ui_thread_data.context_getter->GetURLRequestContext()->proxy_service(); 95 } 96 waiting_for_ui_thread_data_ = false; 97 if (!proxy_service_) { 98 DLOG_IF(WARNING, proxy_service_) 99 << "Failed to find a ProxyService for Pepper plugin."; 100 } 101 TryToSendUnsentRequests(); 102 } 103 104 int32_t PepperNetworkProxyHost::OnResourceMessageReceived( 105 const IPC::Message& msg, 106 ppapi::host::HostMessageContext* context) { 107 PPAPI_BEGIN_MESSAGE_MAP(PepperNetworkProxyHost, msg) 108 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_NetworkProxy_GetProxyForURL, 109 OnMsgGetProxyForURL) 110 PPAPI_END_MESSAGE_MAP() 111 return PP_ERROR_FAILED; 112 } 113 114 int32_t PepperNetworkProxyHost::OnMsgGetProxyForURL( 115 ppapi::host::HostMessageContext* context, 116 const std::string& url) { 117 GURL gurl(url); 118 if (gurl.is_valid()) { 119 UnsentRequest request = {gurl, context->MakeReplyMessageContext()}; 120 unsent_requests_.push(request); 121 TryToSendUnsentRequests(); 122 } else { 123 SendFailureReply(PP_ERROR_BADARGUMENT, context->MakeReplyMessageContext()); 124 } 125 return PP_OK_COMPLETIONPENDING; 126 } 127 128 void PepperNetworkProxyHost::TryToSendUnsentRequests() { 129 if (waiting_for_ui_thread_data_) 130 return; 131 132 while (!unsent_requests_.empty()) { 133 const UnsentRequest& request = unsent_requests_.front(); 134 if (!proxy_service_) { 135 SendFailureReply(PP_ERROR_FAILED, request.reply_context); 136 } else if (!is_allowed_) { 137 SendFailureReply(PP_ERROR_NOACCESS, request.reply_context); 138 } else { 139 // Everything looks valid, so try to resolve the proxy. 140 net::ProxyInfo* proxy_info = new net::ProxyInfo; 141 net::ProxyService::PacRequest* pending_request = NULL; 142 base::Callback<void(int)> callback = 143 base::Bind(&PepperNetworkProxyHost::OnResolveProxyCompleted, 144 weak_factory_.GetWeakPtr(), 145 request.reply_context, 146 base::Owned(proxy_info)); 147 int result = proxy_service_->ResolveProxy(request.url, 148 proxy_info, 149 callback, 150 &pending_request, 151 net::BoundNetLog()); 152 pending_requests_.push(pending_request); 153 // If it was handled synchronously, we must run the callback now; 154 // proxy_service_ won't run it for us in this case. 155 if (result != net::ERR_IO_PENDING) 156 callback.Run(result); 157 } 158 unsent_requests_.pop(); 159 } 160 } 161 162 void PepperNetworkProxyHost::OnResolveProxyCompleted( 163 ppapi::host::ReplyMessageContext context, 164 net::ProxyInfo* proxy_info, 165 int result) { 166 pending_requests_.pop(); 167 168 if (result != net::OK) { 169 // Currently, the only proxy-specific error we could get is 170 // MANDATORY_PROXY_CONFIGURATION_FAILED. There's really no action a plugin 171 // can take in this case, so there's no need to distinguish it from other 172 // failures. 173 context.params.set_result(PP_ERROR_FAILED); 174 } 175 host()->SendReply(context, 176 PpapiPluginMsg_NetworkProxy_GetProxyForURLReply( 177 proxy_info->ToPacString())); 178 } 179 180 void PepperNetworkProxyHost::SendFailureReply( 181 int32_t error, 182 ppapi::host::ReplyMessageContext context) { 183 context.params.set_result(error); 184 host()->SendReply( 185 context, PpapiPluginMsg_NetworkProxy_GetProxyForURLReply(std::string())); 186 } 187 188 } // namespace content 189