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