| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2008 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 #import <Cocoa/Cocoa.h> | |
| 6 | |
| 7 #include "webkit/glue/plugins/webplugin_delegate_impl.h" | |
| 8 | |
| 9 #include <string> | |
| 10 #include <vector> | |
| 11 | |
| 12 #include "base/file_util.h" | |
| 13 #include "base/lazy_instance.h" | |
| 14 #include "base/message_loop.h" | |
| 15 #include "base/stats_counters.h" | |
| 16 #include "base/string_util.h" | |
| 17 #include "webkit/api/public/WebInputEvent.h" | |
| 18 #include "webkit/default_plugin/plugin_impl.h" | |
| 19 #include "webkit/glue/glue_util.h" | |
| 20 #include "webkit/glue/webplugin.h" | |
| 21 #include "webkit/glue/plugins/plugin_constants_win.h" | |
| 22 #include "webkit/glue/plugins/plugin_instance.h" | |
| 23 #include "webkit/glue/plugins/plugin_lib.h" | |
| 24 #include "webkit/glue/plugins/plugin_list.h" | |
| 25 #include "webkit/glue/plugins/plugin_stream_url.h" | |
| 26 #include "webkit/glue/webkit_glue.h" | |
| 27 | |
| 28 using WebKit::WebCursorInfo; | |
| 29 using WebKit::WebKeyboardEvent; | |
| 30 using WebKit::WebInputEvent; | |
| 31 using WebKit::WebMouseEvent; | |
| 32 | |
| 33 // Important implementation notes: The Mac definition of NPAPI, particularly | |
| 34 // the distinction between windowed and windowless modes, differs from the | |
| 35 // Windows and Linux definitions. Most of those differences are | |
| 36 // accomodated by the WebPluginDelegate class. | |
| 37 | |
| 38 namespace { | |
| 39 | |
| 40 // The fastest we are willing to process idle events for plugins. | |
| 41 // Some can easily exceed the limits of our CPU if we don't throttle them. | |
| 42 // The throttle has been chosen by using the same value as Apple's WebKit port. | |
| 43 // | |
| 44 // We'd like to make the throttle delay variable, based on the amount of | |
| 45 // time currently required to paint plugins. There isn't a good | |
| 46 // way to count the time spent in aggregate plugin painting, however, so | |
| 47 // this seems to work well enough. | |
| 48 const int kPluginIdleThrottleDelayMs = 20; // 20ms (50Hz) | |
| 49 | |
| 50 // The most recently seen offset between global and local coordinates. We use | |
| 51 // this to keep the placeholder Carbon WindowRef's origin in sync with the | |
| 52 // actual browser window, without having to pass that geometry over IPC. If we | |
| 53 // end up needing to interpose on Carbon APIs in the plugin process (in order | |
| 54 // to simulate window activation, for example), this could be replaced by | |
| 55 // interposing on GlobalToLocal and/or LocalToGlobal (see related TODO comments | |
| 56 // below in WebPluginDelegateImpl::OnNullEvent()). | |
| 57 | |
| 58 int g_current_x_offset = 0; | |
| 59 int g_current_y_offset = 0; | |
| 60 | |
| 61 WindowRef g_last_front_window = NULL; | |
| 62 ProcessSerialNumber g_saved_front_process; | |
| 63 | |
| 64 } // namespace | |
| 65 | |
| 66 WebPluginDelegate* WebPluginDelegate::Create( | |
| 67 const FilePath& filename, | |
| 68 const std::string& mime_type, | |
| 69 gfx::PluginWindowHandle containing_view) { | |
| 70 scoped_refptr<NPAPI::PluginLib> plugin = | |
| 71 NPAPI::PluginLib::CreatePluginLib(filename); | |
| 72 if (plugin.get() == NULL) | |
| 73 return NULL; | |
| 74 | |
| 75 NPError err = plugin->NP_Initialize(); | |
| 76 if (err != NPERR_NO_ERROR) | |
| 77 return NULL; | |
| 78 | |
| 79 scoped_refptr<NPAPI::PluginInstance> instance = | |
| 80 plugin->CreateInstance(mime_type); | |
| 81 return new WebPluginDelegateImpl(containing_view, instance.get()); | |
| 82 } | |
| 83 | |
| 84 WebPluginDelegateImpl::WebPluginDelegateImpl( | |
| 85 gfx::PluginWindowHandle containing_view, | |
| 86 NPAPI::PluginInstance *instance) | |
| 87 : parent_(containing_view), | |
| 88 instance_(instance), | |
| 89 quirks_(0), | |
| 90 plugin_(NULL), | |
| 91 // all Mac plugins are "windowless" in the Windows/X11 sense | |
| 92 windowless_(true), | |
| 93 windowless_needs_set_window_(true), | |
| 94 handle_event_depth_(0), | |
| 95 user_gesture_message_posted_(this), | |
| 96 user_gesture_msg_factory_(this), | |
| 97 null_event_factory_(this), | |
| 98 last_mouse_x_(0), | |
| 99 last_mouse_y_(0) { | |
| 100 memset(&window_, 0, sizeof(window_)); | |
| 101 } | |
| 102 | |
| 103 WebPluginDelegateImpl::~WebPluginDelegateImpl() { | |
| 104 DestroyInstance(); | |
| 105 | |
| 106 if (cg_context_.window) | |
| 107 DisposeWindow(cg_context_.window); | |
| 108 } | |
| 109 | |
| 110 void WebPluginDelegateImpl::PluginDestroyed() { | |
| 111 delete this; | |
| 112 } | |
| 113 | |
| 114 bool WebPluginDelegateImpl::Initialize(const GURL& url, | |
| 115 char** argn, | |
| 116 char** argv, | |
| 117 int argc, | |
| 118 WebPlugin* plugin, | |
| 119 bool load_manually) { | |
| 120 plugin_ = plugin; | |
| 121 | |
| 122 instance_->set_web_plugin(plugin); | |
| 123 NPAPI::PluginInstance* old_instance = | |
| 124 NPAPI::PluginInstance::SetInitializingInstance(instance_); | |
| 125 | |
| 126 | |
| 127 bool start_result = instance_->Start(url, argn, argv, argc, load_manually); | |
| 128 | |
| 129 NPAPI::PluginInstance::SetInitializingInstance(old_instance); | |
| 130 | |
| 131 if (!start_result) | |
| 132 return false; | |
| 133 | |
| 134 cg_context_.window = NULL; | |
| 135 window_.window = &cg_context_; | |
| 136 window_.type = NPWindowTypeWindow; | |
| 137 | |
| 138 plugin->SetWindow(NULL); | |
| 139 plugin_url_ = url.spec(); | |
| 140 | |
| 141 MessageLoop::current()->PostDelayedTask(FROM_HERE, | |
| 142 null_event_factory_.NewRunnableMethod( | |
| 143 &WebPluginDelegateImpl::OnNullEvent), | |
| 144 kPluginIdleThrottleDelayMs); | |
| 145 return true; | |
| 146 } | |
| 147 | |
| 148 void WebPluginDelegateImpl::DestroyInstance() { | |
| 149 if (instance_ && (instance_->npp()->ndata != NULL)) { | |
| 150 // Shutdown all streams before destroying so that | |
| 151 // no streams are left "in progress". Need to do | |
| 152 // this before calling set_web_plugin(NULL) because the | |
| 153 // instance uses the helper to do the download. | |
| 154 instance_->CloseStreams(); | |
| 155 instance_->NPP_Destroy(); | |
| 156 instance_->set_web_plugin(NULL); | |
| 157 instance_ = 0; | |
| 158 } | |
| 159 } | |
| 160 | |
| 161 void WebPluginDelegateImpl::UpdateGeometry( | |
| 162 const gfx::Rect& window_rect, | |
| 163 const gfx::Rect& clip_rect) { | |
| 164 | |
| 165 DCHECK(windowless_); | |
| 166 WindowlessUpdateGeometry(window_rect, clip_rect); | |
| 167 } | |
| 168 | |
| 169 void WebPluginDelegateImpl::Paint(CGContextRef context, const gfx::Rect& rect) { | |
| 170 DCHECK(windowless_); | |
| 171 WindowlessPaint(context, rect); | |
| 172 } | |
| 173 | |
| 174 void WebPluginDelegateImpl::Print(CGContextRef context) { | |
| 175 // Disabling the call to NPP_Print as it causes a crash in | |
| 176 // flash in some cases. In any case this does not work as expected | |
| 177 // as the EMF meta file dc passed in needs to be created with the | |
| 178 // the plugin window dc as its sibling dc and the window rect | |
| 179 // in .01 mm units. | |
| 180 } | |
| 181 | |
| 182 NPObject* WebPluginDelegateImpl::GetPluginScriptableObject() { | |
| 183 return instance_->GetPluginScriptableObject(); | |
| 184 } | |
| 185 | |
| 186 void WebPluginDelegateImpl::DidFinishLoadWithReason(NPReason reason) { | |
| 187 instance()->DidFinishLoadWithReason(reason); | |
| 188 } | |
| 189 | |
| 190 int WebPluginDelegateImpl::GetProcessId() { | |
| 191 // We are in process, so the plugin pid is this current process pid. | |
| 192 return getpid(); | |
| 193 } | |
| 194 | |
| 195 void WebPluginDelegateImpl::SendJavaScriptStream(const std::string& url, | |
| 196 const std::wstring& result, | |
| 197 bool success, | |
| 198 bool notify_needed, | |
| 199 intptr_t notify_data) { | |
| 200 instance()->SendJavaScriptStream(url, result, success, notify_needed, | |
| 201 notify_data); | |
| 202 } | |
| 203 | |
| 204 void WebPluginDelegateImpl::DidReceiveManualResponse( | |
| 205 const std::string& url, const std::string& mime_type, | |
| 206 const std::string& headers, uint32 expected_length, uint32 last_modified) { | |
| 207 instance()->DidReceiveManualResponse(url, mime_type, headers, | |
| 208 expected_length, last_modified); | |
| 209 } | |
| 210 | |
| 211 void WebPluginDelegateImpl::DidReceiveManualData(const char* buffer, | |
| 212 int length) { | |
| 213 instance()->DidReceiveManualData(buffer, length); | |
| 214 } | |
| 215 | |
| 216 void WebPluginDelegateImpl::DidFinishManualLoading() { | |
| 217 instance()->DidFinishManualLoading(); | |
| 218 } | |
| 219 | |
| 220 void WebPluginDelegateImpl::DidManualLoadFail() { | |
| 221 instance()->DidManualLoadFail(); | |
| 222 } | |
| 223 | |
| 224 FilePath WebPluginDelegateImpl::GetPluginPath() { | |
| 225 return instance()->plugin_lib()->plugin_info().path; | |
| 226 } | |
| 227 | |
| 228 void WebPluginDelegateImpl::InstallMissingPlugin() { | |
| 229 NPEvent evt; | |
| 230 instance()->NPP_HandleEvent(&evt); | |
| 231 } | |
| 232 | |
| 233 void WebPluginDelegateImpl::WindowlessUpdateGeometry( | |
| 234 const gfx::Rect& window_rect, | |
| 235 const gfx::Rect& clip_rect) { | |
| 236 // Only resend to the instance if the geometry has changed. | |
| 237 if (window_rect == window_rect_ && clip_rect == clip_rect_) | |
| 238 return; | |
| 239 | |
| 240 // We will inform the instance of this change when we call NPP_SetWindow. | |
| 241 clip_rect_ = clip_rect; | |
| 242 | |
| 243 if (window_rect_ != window_rect) { | |
| 244 window_rect_ = window_rect; | |
| 245 | |
| 246 WindowlessSetWindow(true); | |
| 247 } | |
| 248 } | |
| 249 | |
| 250 void WebPluginDelegateImpl::WindowlessPaint(gfx::NativeDrawingContext context, | |
| 251 const gfx::Rect& damage_rect) { | |
| 252 static StatsRate plugin_paint("Plugin.Paint"); | |
| 253 StatsScope<StatsRate> scope(plugin_paint); | |
| 254 | |
| 255 // We save and restore the NSGraphicsContext state in case the plugin uses | |
| 256 // Cocoa drawing. | |
| 257 [NSGraphicsContext saveGraphicsState]; | |
| 258 [NSGraphicsContext setCurrentContext:[NSGraphicsContext | |
| 259 graphicsContextWithGraphicsPort:context | |
| 260 flipped:YES]]; | |
| 261 CGContextSaveGState(context); | |
| 262 | |
| 263 cg_context_.context = context; | |
| 264 if (window_.window == NULL) | |
| 265 windowless_needs_set_window_ = true; | |
| 266 | |
| 267 window_.window = &cg_context_; | |
| 268 | |
| 269 if (windowless_needs_set_window_) | |
| 270 WindowlessSetWindow(false); | |
| 271 | |
| 272 NPEvent paint_event; | |
| 273 paint_event.what = updateEvt; | |
| 274 paint_event.message = reinterpret_cast<uint32>(cg_context_.window); | |
| 275 paint_event.when = TickCount(); | |
| 276 paint_event.where.h = 0; | |
| 277 paint_event.where.v = 0; | |
| 278 paint_event.modifiers = 0; | |
| 279 instance()->NPP_HandleEvent(&paint_event); | |
| 280 | |
| 281 CGContextRestoreGState(context); | |
| 282 [NSGraphicsContext restoreGraphicsState]; | |
| 283 } | |
| 284 | |
| 285 // Moves our dummy window to the given offset relative to the last known | |
| 286 // location of the real renderer window's content view. | |
| 287 static void UpdateDummyWindowLocationWithOffset(WindowRef window, | |
| 288 int x_offset, int y_offset) { | |
| 289 int target_x = g_current_x_offset + x_offset; | |
| 290 int target_y = g_current_y_offset + y_offset; | |
| 291 Rect window_bounds; | |
| 292 GetWindowBounds(window, kWindowContentRgn, &window_bounds); | |
| 293 if ((window_bounds.left != target_x) || | |
| 294 (window_bounds.top != target_y)) { | |
| 295 int height = window_bounds.bottom - window_bounds.top; | |
| 296 int width = window_bounds.right - window_bounds.left; | |
| 297 window_bounds.left = target_x; | |
| 298 window_bounds.top = target_y; | |
| 299 window_bounds.right = window_bounds.left + width; | |
| 300 window_bounds.bottom = window_bounds.top + height; | |
| 301 SetWindowBounds(window, kWindowContentRgn, &window_bounds); | |
| 302 } | |
| 303 } | |
| 304 | |
| 305 void WebPluginDelegateImpl::WindowlessSetWindow(bool force_set_window) { | |
| 306 if (!instance()) | |
| 307 return; | |
| 308 | |
| 309 if (window_rect_.IsEmpty()) // wait for geometry to be set. | |
| 310 return; | |
| 311 | |
| 312 window_.clipRect.top = 0; | |
| 313 window_.clipRect.left = 0; | |
| 314 window_.clipRect.bottom = window_rect_.height(); | |
| 315 window_.clipRect.right = window_rect_.width(); | |
| 316 window_.height = window_rect_.height(); | |
| 317 window_.width = window_rect_.width(); | |
| 318 window_.x = 0; | |
| 319 window_.y = 0; | |
| 320 window_.type = NPWindowTypeWindow; | |
| 321 | |
| 322 if (!force_set_window) | |
| 323 // Reset this flag before entering the instance in case of side-effects. | |
| 324 windowless_needs_set_window_ = false; | |
| 325 | |
| 326 if (!cg_context_.window) { | |
| 327 // For all plugins we create a placeholder offscreen window for the use | |
| 328 // of NPWindow. NPAPI on the Mac requires a Carbon WindowRef for the | |
| 329 // "browser window", even if we're not using the Quickdraw drawing model. | |
| 330 // Not having a valid window reference causes subtle bugs with plugins | |
| 331 // which retreive the NPWindow and validate the same. The NPWindow | |
| 332 // can be retreived via NPN_GetValue of NPNVnetscapeWindow. | |
| 333 Rect window_bounds = { 0, 0, window_rect_.height(), window_rect_.width() }; | |
| 334 WindowRef window_ref; | |
| 335 if (CreateNewWindow(kDocumentWindowClass, | |
| 336 kWindowStandardDocumentAttributes, | |
| 337 &window_bounds, | |
| 338 &window_ref) == noErr) { | |
| 339 cg_context_.window = window_ref; | |
| 340 SelectWindow(window_ref); | |
| 341 g_last_front_window = window_ref; | |
| 342 } | |
| 343 } | |
| 344 UpdateDummyWindowLocationWithOffset(cg_context_.window, window_rect_.x(), | |
| 345 window_rect_.y()); | |
| 346 | |
| 347 if (!force_set_window) | |
| 348 windowless_needs_set_window_ = false; | |
| 349 NPError err = instance()->NPP_SetWindow(&window_); | |
| 350 DCHECK(err == NPERR_NO_ERROR); | |
| 351 } | |
| 352 | |
| 353 void WebPluginDelegateImpl::SetFocus() { | |
| 354 NPEvent focus_event = { 0 }; | |
| 355 focus_event.what = NPEventType_GetFocusEvent; | |
| 356 focus_event.when = TickCount(); | |
| 357 instance()->NPP_HandleEvent(&focus_event); | |
| 358 } | |
| 359 | |
| 360 static bool WebInputEventIsWebMouseEvent(const WebInputEvent& event) { | |
| 361 switch (event.type) { | |
| 362 case WebInputEvent::MouseMove: | |
| 363 case WebInputEvent::MouseLeave: | |
| 364 case WebInputEvent::MouseEnter: | |
| 365 case WebInputEvent::MouseDown: | |
| 366 case WebInputEvent::MouseUp: | |
| 367 if (event.size < sizeof(WebMouseEvent)) { | |
| 368 NOTREACHED(); | |
| 369 return false; | |
| 370 } | |
| 371 return true; | |
| 372 default: | |
| 373 return false; | |
| 374 } | |
| 375 } | |
| 376 | |
| 377 static bool WebInputEventIsWebKeyboardEvent(const WebInputEvent& event) { | |
| 378 switch (event.type) { | |
| 379 case WebInputEvent::KeyDown: | |
| 380 case WebInputEvent::KeyUp: | |
| 381 if (event.size < sizeof(WebKeyboardEvent)) { | |
| 382 NOTREACHED(); | |
| 383 return false; | |
| 384 } | |
| 385 return true; | |
| 386 default: | |
| 387 return false; | |
| 388 } | |
| 389 } | |
| 390 | |
| 391 static bool NPEventFromWebMouseEvent(const WebMouseEvent& event, | |
| 392 NPEvent *np_event) { | |
| 393 np_event->where.h = event.globalX; | |
| 394 np_event->where.v = event.globalY; | |
| 395 | |
| 396 if (event.modifiers & WebInputEvent::ControlKey) | |
| 397 np_event->modifiers |= controlKey; | |
| 398 if (event.modifiers & WebInputEvent::ShiftKey) | |
| 399 np_event->modifiers |= shiftKey; | |
| 400 | |
| 401 // default to "button up"; override this for mouse down events below. | |
| 402 np_event->modifiers |= btnState; | |
| 403 | |
| 404 switch (event.button) { | |
| 405 case WebMouseEvent::ButtonLeft: | |
| 406 break; | |
| 407 case WebMouseEvent::ButtonMiddle: | |
| 408 np_event->modifiers |= cmdKey; | |
| 409 break; | |
| 410 case WebMouseEvent::ButtonRight: | |
| 411 np_event->modifiers |= controlKey; | |
| 412 break; | |
| 413 } | |
| 414 switch (event.type) { | |
| 415 case WebInputEvent::MouseMove: | |
| 416 np_event->what = nullEvent; | |
| 417 return true; | |
| 418 case WebInputEvent::MouseLeave: | |
| 419 case WebInputEvent::MouseEnter: | |
| 420 np_event->what = NPEventType_AdjustCursorEvent; | |
| 421 return true; | |
| 422 case WebInputEvent::MouseDown: | |
| 423 np_event->modifiers &= ~btnState; | |
| 424 np_event->what = mouseDown; | |
| 425 return true; | |
| 426 case WebInputEvent::MouseUp: | |
| 427 np_event->what = mouseUp; | |
| 428 return true; | |
| 429 default: | |
| 430 NOTREACHED(); | |
| 431 return false; | |
| 432 } | |
| 433 } | |
| 434 | |
| 435 static bool NPEventFromWebKeyboardEvent(const WebKeyboardEvent& event, | |
| 436 NPEvent *np_event) { | |
| 437 // TODO: figure out how to handle Unicode input to plugins, if that's | |
| 438 // even possible in the NPAPI Carbon event model. | |
| 439 np_event->message = (event.nativeKeyCode << 8) & keyCodeMask; | |
| 440 np_event->message |= event.text[0] & charCodeMask; | |
| 441 np_event->modifiers |= btnState; | |
| 442 if (event.modifiers & WebInputEvent::ControlKey) | |
| 443 np_event->modifiers |= controlKey; | |
| 444 if (event.modifiers & WebInputEvent::ShiftKey) | |
| 445 np_event->modifiers |= shiftKey; | |
| 446 if (event.modifiers & WebInputEvent::AltKey) | |
| 447 np_event->modifiers |= cmdKey; | |
| 448 if (event.modifiers & WebInputEvent::MetaKey) | |
| 449 np_event->modifiers |= optionKey; | |
| 450 | |
| 451 switch (event.type) { | |
| 452 case WebInputEvent::KeyDown: | |
| 453 if (event.modifiers & WebInputEvent::IsAutoRepeat) | |
| 454 np_event->what = autoKey; | |
| 455 else | |
| 456 np_event->what = keyDown; | |
| 457 return true; | |
| 458 case WebInputEvent::KeyUp: | |
| 459 np_event->what = keyUp; | |
| 460 return true; | |
| 461 default: | |
| 462 NOTREACHED(); | |
| 463 return false; | |
| 464 } | |
| 465 } | |
| 466 | |
| 467 static bool NPEventFromWebInputEvent(const WebInputEvent& event, | |
| 468 NPEvent* np_event) { | |
| 469 if (WebInputEventIsWebMouseEvent(event)) { | |
| 470 return NPEventFromWebMouseEvent(*static_cast<const WebMouseEvent*>(&event), | |
| 471 np_event); | |
| 472 } else if (WebInputEventIsWebKeyboardEvent(event)) { | |
| 473 return NPEventFromWebKeyboardEvent( | |
| 474 *static_cast<const WebKeyboardEvent*>(&event), np_event); | |
| 475 } | |
| 476 DLOG(WARNING) << "unknown event type" << event.type; | |
| 477 return false; | |
| 478 } | |
| 479 | |
| 480 static void UpdateWindowLocation(WindowRef window, const WebMouseEvent& event) { | |
| 481 // TODO: figure out where the vertical offset of 22 comes from (and if 22 is | |
| 482 // exactly right) and replace with an appropriate calculation. It feels like | |
| 483 // window structure or the menu bar, but neither should be involved here. | |
| 484 g_current_x_offset = event.globalX - event.windowX; | |
| 485 g_current_y_offset = event.globalY - event.windowY + 22; | |
| 486 | |
| 487 UpdateDummyWindowLocationWithOffset(window, event.windowX - event.x, | |
| 488 event.windowY - event.y); | |
| 489 } | |
| 490 | |
| 491 bool WebPluginDelegateImpl::HandleInputEvent(const WebInputEvent& event, | |
| 492 WebCursorInfo* cursor) { | |
| 493 DCHECK(windowless_) << "events should only be received in windowless mode"; | |
| 494 DCHECK(cursor != NULL); | |
| 495 | |
| 496 NPEvent np_event = {0}; | |
| 497 if (!NPEventFromWebInputEvent(event, &np_event)) { | |
| 498 return false; | |
| 499 } | |
| 500 np_event.when = TickCount(); | |
| 501 if (np_event.what == nullEvent) { | |
| 502 last_mouse_x_ = np_event.where.h; | |
| 503 last_mouse_y_ = np_event.where.v; | |
| 504 return true; // Let the recurring task actually send the event. | |
| 505 } | |
| 506 | |
| 507 // If this is a mouse event, we need to make sure our dummy window has the | |
| 508 // correct location before we send the event to the plugin, so that any | |
| 509 // coordinate conversion the plugin does will work out. | |
| 510 if (WebInputEventIsWebMouseEvent(event)) { | |
| 511 const WebMouseEvent* mouse_event = | |
| 512 static_cast<const WebMouseEvent*>(&event); | |
| 513 UpdateWindowLocation(cg_context_.window, *mouse_event); | |
| 514 } | |
| 515 CGContextSaveGState(cg_context_.context); | |
| 516 bool ret = instance()->NPP_HandleEvent(&np_event) != 0; | |
| 517 CGContextRestoreGState(cg_context_.context); | |
| 518 return ret; | |
| 519 } | |
| 520 | |
| 521 WebPluginResourceClient* WebPluginDelegateImpl::CreateResourceClient( | |
| 522 int resource_id, const std::string &url, bool notify_needed, | |
| 523 intptr_t notify_data, intptr_t existing_stream) { | |
| 524 // Stream already exists. This typically happens for range requests | |
| 525 // initiated via NPN_RequestRead. | |
| 526 if (existing_stream) { | |
| 527 NPAPI::PluginStream* plugin_stream = | |
| 528 reinterpret_cast<NPAPI::PluginStream*>(existing_stream); | |
| 529 | |
| 530 plugin_stream->CancelRequest(); | |
| 531 | |
| 532 return plugin_stream->AsResourceClient(); | |
| 533 } | |
| 534 | |
| 535 if (notify_needed) { | |
| 536 instance()->SetURLLoadData(GURL(url.c_str()), notify_data); | |
| 537 } | |
| 538 std::string mime_type; | |
| 539 NPAPI::PluginStreamUrl *stream = instance()->CreateStream( | |
| 540 resource_id, url, mime_type, notify_needed, | |
| 541 reinterpret_cast<void*>(notify_data)); | |
| 542 return stream; | |
| 543 } | |
| 544 | |
| 545 void WebPluginDelegateImpl::URLRequestRouted(const std::string&url, | |
| 546 bool notify_needed, | |
| 547 intptr_t notify_data) { | |
| 548 if (notify_needed) { | |
| 549 instance()->SetURLLoadData(GURL(url.c_str()), notify_data); | |
| 550 } | |
| 551 } | |
| 552 | |
| 553 void WebPluginDelegateImpl::OnNullEvent() { | |
| 554 NPEvent np_event = {0}; | |
| 555 np_event.what = nullEvent; | |
| 556 np_event.when = TickCount(); | |
| 557 np_event.modifiers = GetCurrentKeyModifiers(); | |
| 558 if (!Button()) | |
| 559 np_event.modifiers |= btnState; | |
| 560 np_event.where.h = last_mouse_x_; | |
| 561 np_event.where.v = last_mouse_y_; | |
| 562 instance()->NPP_HandleEvent(&np_event); | |
| 563 | |
| 564 WindowRef front_window = FrontWindow(); | |
| 565 if (front_window == cg_context_.window) { | |
| 566 if (front_window != g_last_front_window) { | |
| 567 // If our dummy window is now the front window, but was not previously, | |
| 568 // it means that a plugin window has been destroyed. Make sure our fake | |
| 569 // browser window is selected. | |
| 570 // TODO: Use DYLD_INSERT_LIBRARIES to interpose on Carbon window | |
| 571 // APIs within the plugin process. This will allow us to (a) get rid of | |
| 572 // the dummy window, and (b) explicitly track the creation and | |
| 573 // destruction of windows by the plugin. | |
| 574 g_last_front_window = front_window; | |
| 575 SelectWindow(cg_context_.window); | |
| 576 | |
| 577 // If the plugin process is still the front process, bring the prior | |
| 578 // front process (normally this will be the browser process) back to | |
| 579 // the front. | |
| 580 // TODO: make this an IPC message so that the browser can properly | |
| 581 // reactivate the window. | |
| 582 ProcessSerialNumber this_process, front_process; | |
| 583 GetCurrentProcess(&this_process); | |
| 584 GetFrontProcess(&front_process); | |
| 585 Boolean matched = false; | |
| 586 SameProcess(&this_process, &front_process, &matched); | |
| 587 if (matched) | |
| 588 SetFrontProcess(&g_saved_front_process); | |
| 589 g_last_front_window = FrontWindow(); | |
| 590 } | |
| 591 } else if (front_window != g_last_front_window) { | |
| 592 // The plugin has just created a new window and brought it to the front. | |
| 593 // bring the plugin process to the front so that the user can see it (for | |
| 594 // example, an alert or file selection dialog). | |
| 595 // TODO: make this an IPC to order the plugin process above the browser | |
| 596 // process but not necessarily the frontmost. | |
| 597 ProcessSerialNumber this_process; | |
| 598 GetCurrentProcess(&this_process); | |
| 599 GetFrontProcess(&g_saved_front_process); | |
| 600 SetFrontProcess(&this_process); | |
| 601 g_last_front_window = front_window; | |
| 602 SelectWindow(front_window); | |
| 603 } | |
| 604 | |
| 605 MessageLoop::current()->PostDelayedTask(FROM_HERE, | |
| 606 null_event_factory_.NewRunnableMethod( | |
| 607 &WebPluginDelegateImpl::OnNullEvent), | |
| 608 kPluginIdleThrottleDelayMs); | |
| 609 } | |
| OLD | NEW |