1 // Copyright (c) 2012 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/child/npapi/webplugin_delegate_impl.h" 6 7 #include <string> 8 #include <vector> 9 10 #include "base/memory/scoped_ptr.h" 11 #include "base/message_loop/message_loop.h" 12 #include "base/process/process_handle.h" 13 #include "base/strings/string_util.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "content/child/npapi/plugin_instance.h" 16 #include "content/child/npapi/plugin_lib.h" 17 #include "content/child/npapi/plugin_stream_url.h" 18 #include "content/child/npapi/plugin_url_fetcher.h" 19 #include "third_party/WebKit/public/web/WebInputEvent.h" 20 21 using blink::WebCursorInfo; 22 using blink::WebInputEvent; 23 24 namespace content { 25 26 WebPluginDelegateImpl* WebPluginDelegateImpl::Create( 27 WebPlugin* plugin, 28 const base::FilePath& filename, 29 const std::string& mime_type) { 30 scoped_refptr<PluginLib> plugin_lib(PluginLib::CreatePluginLib(filename)); 31 if (plugin_lib.get() == NULL) 32 return NULL; 33 34 NPError err = plugin_lib->NP_Initialize(); 35 if (err != NPERR_NO_ERROR) 36 return NULL; 37 38 scoped_refptr<PluginInstance> instance(plugin_lib->CreateInstance(mime_type)); 39 return new WebPluginDelegateImpl(plugin, instance.get()); 40 } 41 42 void WebPluginDelegateImpl::PluginDestroyed() { 43 if (handle_event_depth_) { 44 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 45 } else { 46 delete this; 47 } 48 } 49 50 bool WebPluginDelegateImpl::Initialize( 51 const GURL& url, 52 const std::vector<std::string>& arg_names, 53 const std::vector<std::string>& arg_values, 54 bool load_manually) { 55 if (instance_->plugin_lib()->plugin_info().name.find( 56 base::ASCIIToUTF16("QuickTime Plug-in")) != std::wstring::npos) { 57 quirks_ |= PLUGIN_QUIRK_COPY_STREAM_DATA; 58 } 59 60 instance_->set_web_plugin(plugin_); 61 if (quirks_ & PLUGIN_QUIRK_DONT_ALLOW_MULTIPLE_INSTANCES) { 62 PluginLib* plugin_lib = instance()->plugin_lib(); 63 if (plugin_lib->instance_count() > 1) { 64 return false; 65 } 66 } 67 68 int argc = 0; 69 scoped_ptr<char*[]> argn(new char*[arg_names.size()]); 70 scoped_ptr<char*[]> argv(new char*[arg_names.size()]); 71 for (size_t i = 0; i < arg_names.size(); ++i) { 72 if (quirks_ & PLUGIN_QUIRK_NO_WINDOWLESS && 73 LowerCaseEqualsASCII(arg_names[i], "windowlessvideo")) { 74 continue; 75 } 76 argn[argc] = const_cast<char*>(arg_names[i].c_str()); 77 argv[argc] = const_cast<char*>(arg_values[i].c_str()); 78 argc++; 79 } 80 81 creation_succeeded_ = instance_->Start( 82 url, argn.get(), argv.get(), argc, load_manually); 83 if (!creation_succeeded_) { 84 VLOG(1) << "Couldn't start plug-in instance"; 85 return false; 86 } 87 88 windowless_ = instance_->windowless(); 89 if (!windowless_) { 90 if (!WindowedCreatePlugin()) { 91 VLOG(1) << "Couldn't create windowed plug-in"; 92 return false; 93 } 94 } 95 96 bool should_load = PlatformInitialize(); 97 98 plugin_url_ = url.spec(); 99 100 return should_load; 101 } 102 103 void WebPluginDelegateImpl::DestroyInstance() { 104 if (instance_.get() && (instance_->npp()->ndata != NULL)) { 105 // Shutdown all streams before destroying so that 106 // no streams are left "in progress". Need to do 107 // this before calling set_web_plugin(NULL) because the 108 // instance uses the helper to do the download. 109 instance_->CloseStreams(); 110 111 window_.window = NULL; 112 if (creation_succeeded_ && 113 !(quirks_ & PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY)) { 114 instance_->NPP_SetWindow(&window_); 115 } 116 117 instance_->NPP_Destroy(); 118 119 instance_->set_web_plugin(NULL); 120 121 PlatformDestroyInstance(); 122 123 instance_ = 0; 124 } 125 } 126 127 void WebPluginDelegateImpl::UpdateGeometry( 128 const gfx::Rect& window_rect, 129 const gfx::Rect& clip_rect) { 130 131 if (first_set_window_call_) { 132 first_set_window_call_ = false; 133 // Plugins like media player on Windows have a bug where in they handle the 134 // first geometry update and ignore the rest resulting in painting issues. 135 // This quirk basically ignores the first set window call sequence for 136 // these plugins and has been tested for Windows plugins only. 137 if (quirks_ & PLUGIN_QUIRK_IGNORE_FIRST_SETWINDOW_CALL) 138 return; 139 } 140 141 if (windowless_) { 142 WindowlessUpdateGeometry(window_rect, clip_rect); 143 } else { 144 WindowedUpdateGeometry(window_rect, clip_rect); 145 } 146 } 147 148 void WebPluginDelegateImpl::SetFocus(bool focused) { 149 DCHECK(windowless_); 150 // This is called when internal WebKit focus (the focused element on the page) 151 // changes, but plugins need to know about OS-level focus, so we have an extra 152 // layer of focus tracking. 153 // 154 // On Windows, historically browsers did not set focus events to windowless 155 // plugins when the toplevel window focus changes. Sending such focus events 156 // breaks full screen mode in Flash because it will come out of full screen 157 // mode when it loses focus, and its full screen window causes the browser to 158 // lose focus. 159 has_webkit_focus_ = focused; 160 #if !defined(OS_WIN) 161 if (containing_view_has_focus_) 162 SetPluginHasFocus(focused); 163 #else 164 SetPluginHasFocus(focused); 165 #endif 166 } 167 168 void WebPluginDelegateImpl::SetPluginHasFocus(bool focused) { 169 if (focused == plugin_has_focus_) 170 return; 171 if (PlatformSetPluginHasFocus(focused)) 172 plugin_has_focus_ = focused; 173 } 174 175 void WebPluginDelegateImpl::SetContentAreaHasFocus(bool has_focus) { 176 containing_view_has_focus_ = has_focus; 177 if (!windowless_) 178 return; 179 #if !defined(OS_WIN) // See SetFocus above. 180 SetPluginHasFocus(containing_view_has_focus_ && has_webkit_focus_); 181 #endif 182 } 183 184 NPObject* WebPluginDelegateImpl::GetPluginScriptableObject() { 185 return instance_->GetPluginScriptableObject(); 186 } 187 188 NPP WebPluginDelegateImpl::GetPluginNPP() { 189 return instance_->npp(); 190 } 191 192 bool WebPluginDelegateImpl::GetFormValue(base::string16* value) { 193 return instance_->GetFormValue(value); 194 } 195 196 void WebPluginDelegateImpl::DidFinishLoadWithReason(const GURL& url, 197 NPReason reason, 198 int notify_id) { 199 if (quirks_ & PLUGIN_QUIRK_ALWAYS_NOTIFY_SUCCESS && 200 reason == NPRES_NETWORK_ERR) { 201 // Flash needs this or otherwise it unloads the launching swf object. 202 reason = NPRES_DONE; 203 } 204 205 instance()->DidFinishLoadWithReason(url, reason, notify_id); 206 } 207 208 int WebPluginDelegateImpl::GetProcessId() { 209 // We are in process, so the plugin pid is this current process pid. 210 return base::GetCurrentProcId(); 211 } 212 213 void WebPluginDelegateImpl::SendJavaScriptStream(const GURL& url, 214 const std::string& result, 215 bool success, 216 int notify_id) { 217 instance()->SendJavaScriptStream(url, result, success, notify_id); 218 } 219 220 void WebPluginDelegateImpl::DidReceiveManualResponse( 221 const GURL& url, const std::string& mime_type, 222 const std::string& headers, uint32 expected_length, uint32 last_modified) { 223 if (!windowless_) { 224 // Calling NPP_WriteReady before NPP_SetWindow causes movies to not load in 225 // Flash. See http://b/issue?id=892174. 226 DCHECK(windowed_did_set_window_); 227 } 228 229 instance()->DidReceiveManualResponse(url, mime_type, headers, 230 expected_length, last_modified); 231 } 232 233 void WebPluginDelegateImpl::DidReceiveManualData(const char* buffer, 234 int length) { 235 instance()->DidReceiveManualData(buffer, length); 236 } 237 238 void WebPluginDelegateImpl::DidFinishManualLoading() { 239 instance()->DidFinishManualLoading(); 240 } 241 242 void WebPluginDelegateImpl::DidManualLoadFail() { 243 instance()->DidManualLoadFail(); 244 } 245 246 base::FilePath WebPluginDelegateImpl::GetPluginPath() { 247 return instance()->plugin_lib()->plugin_info().path; 248 } 249 250 void WebPluginDelegateImpl::WindowedUpdateGeometry( 251 const gfx::Rect& window_rect, 252 const gfx::Rect& clip_rect) { 253 if (WindowedReposition(window_rect, clip_rect) || 254 !windowed_did_set_window_) { 255 // Let the plugin know that it has been moved 256 WindowedSetWindow(); 257 } 258 } 259 260 bool WebPluginDelegateImpl::HandleInputEvent( 261 const WebInputEvent& event, 262 WebCursor::CursorInfo* cursor_info) { 263 DCHECK(windowless_) << "events should only be received in windowless mode"; 264 265 bool pop_user_gesture = false; 266 if (IsUserGesture(event)) { 267 pop_user_gesture = true; 268 instance()->PushPopupsEnabledState(true); 269 } 270 271 bool handled = PlatformHandleInputEvent(event, cursor_info); 272 273 if (pop_user_gesture) { 274 instance()->PopPopupsEnabledState(); 275 } 276 277 return handled; 278 } 279 280 bool WebPluginDelegateImpl::IsUserGesture(const WebInputEvent& event) { 281 switch (event.type) { 282 case WebInputEvent::MouseDown: 283 case WebInputEvent::MouseUp: 284 case WebInputEvent::KeyDown: 285 case WebInputEvent::KeyUp: 286 return true; 287 default: 288 return false; 289 } 290 return false; 291 } 292 293 WebPluginResourceClient* WebPluginDelegateImpl::CreateResourceClient( 294 unsigned long resource_id, const GURL& url, int notify_id) { 295 return instance()->CreateStream( 296 resource_id, url, std::string(), notify_id); 297 } 298 299 WebPluginResourceClient* WebPluginDelegateImpl::CreateSeekableResourceClient( 300 unsigned long resource_id, int range_request_id) { 301 WebPluginResourceClient* resource_client = instance()->GetRangeRequest( 302 range_request_id); 303 if (resource_client) 304 resource_client->AddRangeRequestResourceId(resource_id); 305 return resource_client; 306 } 307 308 void WebPluginDelegateImpl::FetchURL(unsigned long resource_id, 309 int notify_id, 310 const GURL& url, 311 const GURL& first_party_for_cookies, 312 const std::string& method, 313 const char* buf, 314 unsigned int len, 315 const GURL& referrer, 316 bool notify_redirects, 317 bool is_plugin_src_load, 318 int origin_pid, 319 int render_view_id) { 320 // TODO(jam): once we switch over to resource loading always happening in this 321 // code path, remove WebPluginResourceClient abstraction. 322 PluginStreamUrl* plugin_stream = instance()->CreateStream( 323 resource_id, url, std::string(), notify_id); 324 325 bool copy_stream_data = !!(quirks_ & PLUGIN_QUIRK_COPY_STREAM_DATA); 326 plugin_stream->SetPluginURLFetcher(new PluginURLFetcher( 327 plugin_stream, url, first_party_for_cookies, method, buf, len, 328 referrer, notify_redirects, is_plugin_src_load, origin_pid, 329 render_view_id, resource_id, copy_stream_data)); 330 } 331 332 } // namespace content 333