1 /*
2 * Copyright (C) 2009 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32 #include "WebPluginContainerImpl.h"
33
34 #include "ChromeClientImpl.h"
35 #include "ScrollbarGroup.h"
36 #include "WebDataSourceImpl.h"
37 #include "WebElement.h"
38 #include "WebInputEvent.h"
39 #include "WebInputEventConversion.h"
40 #include "WebPlugin.h"
41 #include "WebViewClient.h"
42 #include "WebViewImpl.h"
43 #include "core/page/Chrome.h"
44 #include "core/page/EventHandler.h"
45 #include "platform/exported/WrappedResourceResponse.h"
46
47 #include "HTMLNames.h"
48 #include "WebPrintParams.h"
49 #include "bindings/v8/ScriptController.h"
50 #include "core/dom/Clipboard.h"
51 #include "core/events/GestureEvent.h"
52 #include "core/events/KeyboardEvent.h"
53 #include "core/events/MouseEvent.h"
54 #include "core/events/ThreadLocalEventNames.h"
55 #include "core/events/TouchEvent.h"
56 #include "core/events/WheelEvent.h"
57 #include "core/html/HTMLFormElement.h"
58 #include "core/html/HTMLPlugInElement.h"
59 #include "core/loader/FormState.h"
60 #include "core/loader/FrameLoadRequest.h"
61 #include "core/page/FocusController.h"
62 #include "core/frame/Frame.h"
63 #include "core/frame/FrameView.h"
64 #include "core/page/Page.h"
65 #include "core/page/scrolling/ScrollingCoordinator.h"
66 #include "core/platform/chromium/ChromiumDataObject.h"
67 #include "core/plugins/PluginOcclusionSupport.h"
68 #include "core/rendering/HitTestResult.h"
69 #include "core/rendering/RenderBox.h"
70 #include "platform/HostWindow.h"
71 #include "platform/KeyboardCodes.h"
72 #include "platform/PlatformGestureEvent.h"
73 #include "platform/UserGestureIndicator.h"
74 #include "platform/graphics/GraphicsContext.h"
75 #include "platform/graphics/GraphicsLayer.h"
76 #include "platform/scroll/ScrollAnimator.h"
77 #include "platform/scroll/ScrollView.h"
78 #include "platform/scroll/ScrollbarTheme.h"
79 #include "public/platform/Platform.h"
80 #include "public/platform/WebClipboard.h"
81 #include "public/platform/WebCompositorSupport.h"
82 #include "public/platform/WebCursorInfo.h"
83 #include "public/platform/WebDragData.h"
84 #include "public/platform/WebExternalTextureLayer.h"
85 #include "public/platform/WebRect.h"
86 #include "public/platform/WebString.h"
87 #include "public/platform/WebURL.h"
88 #include "public/platform/WebURLError.h"
89 #include "public/platform/WebURLRequest.h"
90 #include "public/platform/WebVector.h"
91
92 using namespace WebCore;
93
94 namespace blink {
95
96 // Public methods --------------------------------------------------------------
97
setFrameRect(const IntRect & frameRect)98 void WebPluginContainerImpl::setFrameRect(const IntRect& frameRect)
99 {
100 Widget::setFrameRect(frameRect);
101 reportGeometry();
102 }
103
paint(GraphicsContext * gc,const IntRect & damageRect)104 void WebPluginContainerImpl::paint(GraphicsContext* gc, const IntRect& damageRect)
105 {
106 if (gc->updatingControlTints() && m_scrollbarGroup) {
107 // See comment in FrameView::updateControlTints().
108 if (m_scrollbarGroup->horizontalScrollbar())
109 m_scrollbarGroup->horizontalScrollbar()->invalidate();
110 if (m_scrollbarGroup->verticalScrollbar())
111 m_scrollbarGroup->verticalScrollbar()->invalidate();
112 }
113
114 if (gc->paintingDisabled())
115 return;
116
117 if (!parent())
118 return;
119
120 // Don't paint anything if the plugin doesn't intersect the damage rect.
121 if (!frameRect().intersects(damageRect))
122 return;
123
124 gc->save();
125
126 ASSERT(parent()->isFrameView());
127 ScrollView* view = toScrollView(parent());
128
129 // The plugin is positioned in window coordinates, so it needs to be painted
130 // in window coordinates.
131 IntPoint origin = view->contentsToWindow(IntPoint(0, 0));
132 gc->translate(static_cast<float>(-origin.x()), static_cast<float>(-origin.y()));
133
134 WebCanvas* canvas = gc->canvas();
135
136 IntRect windowRect = view->contentsToWindow(damageRect);
137 m_webPlugin->paint(canvas, windowRect);
138
139 gc->restore();
140 }
141
invalidateRect(const IntRect & rect)142 void WebPluginContainerImpl::invalidateRect(const IntRect& rect)
143 {
144 if (!parent())
145 return;
146
147 RenderBox* renderer = toRenderBox(m_element->renderer());
148
149 IntRect dirtyRect = rect;
150 dirtyRect.move(renderer->borderLeft() + renderer->paddingLeft(),
151 renderer->borderTop() + renderer->paddingTop());
152 renderer->repaintRectangle(dirtyRect);
153 }
154
setFocus(bool focused)155 void WebPluginContainerImpl::setFocus(bool focused)
156 {
157 Widget::setFocus(focused);
158 m_webPlugin->updateFocus(focused);
159 }
160
show()161 void WebPluginContainerImpl::show()
162 {
163 setSelfVisible(true);
164 m_webPlugin->updateVisibility(true);
165
166 Widget::show();
167 }
168
hide()169 void WebPluginContainerImpl::hide()
170 {
171 setSelfVisible(false);
172 m_webPlugin->updateVisibility(false);
173
174 Widget::hide();
175 }
176
handleEvent(Event * event)177 void WebPluginContainerImpl::handleEvent(Event* event)
178 {
179 if (!m_webPlugin->acceptsInputEvents())
180 return;
181
182 const WebInputEvent* currentInputEvent = WebViewImpl::currentInputEvent();
183 UserGestureIndicator gestureIndicator(currentInputEvent && WebInputEvent::isUserGestureEventType(currentInputEvent->type) ? DefinitelyProcessingNewUserGesture : PossiblyProcessingUserGesture);
184
185 RefPtr<WebPluginContainerImpl> protector(this);
186 // The events we pass are defined at:
187 // http://devedge-temp.mozilla.org/library/manuals/2002/plugin/1.0/structures5.html#1000000
188 // Don't take the documentation as truth, however. There are many cases
189 // where mozilla behaves differently than the spec.
190 if (event->isMouseEvent())
191 handleMouseEvent(toMouseEvent(event));
192 else if (event->isWheelEvent())
193 handleWheelEvent(toWheelEvent(event));
194 else if (event->isKeyboardEvent())
195 handleKeyboardEvent(toKeyboardEvent(event));
196 else if (event->isTouchEvent())
197 handleTouchEvent(toTouchEvent(event));
198 else if (event->isGestureEvent())
199 handleGestureEvent(toGestureEvent(event));
200
201 // FIXME: it would be cleaner if Widget::handleEvent returned true/false and
202 // HTMLPluginElement called setDefaultHandled or defaultEventHandler.
203 if (!event->defaultHandled())
204 m_element->Node::defaultEventHandler(event);
205 }
206
frameRectsChanged()207 void WebPluginContainerImpl::frameRectsChanged()
208 {
209 Widget::frameRectsChanged();
210 reportGeometry();
211 }
212
widgetPositionsUpdated()213 void WebPluginContainerImpl::widgetPositionsUpdated()
214 {
215 Widget::widgetPositionsUpdated();
216 reportGeometry();
217 }
218
clipRectChanged()219 void WebPluginContainerImpl::clipRectChanged()
220 {
221 reportGeometry();
222 }
223
eventListenersRemoved()224 void WebPluginContainerImpl::eventListenersRemoved()
225 {
226 // We're no longer registered to receive touch events, so don't try to remove
227 // the touch event handlers in our destructor.
228 m_touchEventRequestType = TouchEventRequestTypeNone;
229 }
230
setParentVisible(bool parentVisible)231 void WebPluginContainerImpl::setParentVisible(bool parentVisible)
232 {
233 // We override this function to make sure that geometry updates are sent
234 // over to the plugin. For e.g. when a plugin is instantiated it does not
235 // have a valid parent. As a result the first geometry update from webkit
236 // is ignored. This function is called when the plugin eventually gets a
237 // parent.
238
239 if (isParentVisible() == parentVisible)
240 return; // No change.
241
242 Widget::setParentVisible(parentVisible);
243 if (!isSelfVisible())
244 return; // This widget has explicitely been marked as not visible.
245
246 m_webPlugin->updateVisibility(isVisible());
247 }
248
setParent(Widget * widget)249 void WebPluginContainerImpl::setParent(Widget* widget)
250 {
251 // We override this function so that if the plugin is windowed, we can call
252 // NPP_SetWindow at the first possible moment. This ensures that
253 // NPP_SetWindow is called before the manual load data is sent to a plugin.
254 // If this order is reversed, Flash won't load videos.
255
256 Widget::setParent(widget);
257 if (widget)
258 reportGeometry();
259 }
260
setPlugin(WebPlugin * plugin)261 void WebPluginContainerImpl::setPlugin(WebPlugin* plugin)
262 {
263 if (plugin != m_webPlugin) {
264 m_element->resetInstance();
265 m_webPlugin = plugin;
266 }
267 }
268
deviceScaleFactor()269 float WebPluginContainerImpl::deviceScaleFactor()
270 {
271 Page* page = m_element->document().page();
272 if (!page)
273 return 1.0;
274 return page->deviceScaleFactor();
275 }
276
pageScaleFactor()277 float WebPluginContainerImpl::pageScaleFactor()
278 {
279 Page* page = m_element->document().page();
280 if (!page)
281 return 1.0;
282 return page->pageScaleFactor();
283 }
284
pageZoomFactor()285 float WebPluginContainerImpl::pageZoomFactor()
286 {
287 Frame* frame = m_element->document().frame();
288 if (!frame)
289 return 1.0;
290 return frame->pageZoomFactor();
291 }
292
setWebLayer(WebLayer * layer)293 void WebPluginContainerImpl::setWebLayer(WebLayer* layer)
294 {
295 if (m_webLayer == layer)
296 return;
297
298 // If anyone of the layers is null we need to switch between hardware
299 // and software compositing.
300 if (!m_webLayer || !layer)
301 m_element->scheduleLayerUpdate();
302 if (m_webLayer)
303 GraphicsLayer::unregisterContentsLayer(m_webLayer);
304 if (layer)
305 GraphicsLayer::registerContentsLayer(layer);
306 m_webLayer = layer;
307 }
308
supportsPaginatedPrint() const309 bool WebPluginContainerImpl::supportsPaginatedPrint() const
310 {
311 return m_webPlugin->supportsPaginatedPrint();
312 }
313
isPrintScalingDisabled() const314 bool WebPluginContainerImpl::isPrintScalingDisabled() const
315 {
316 return m_webPlugin->isPrintScalingDisabled();
317 }
318
printBegin(const WebPrintParams & printParams) const319 int WebPluginContainerImpl::printBegin(const WebPrintParams& printParams) const
320 {
321 return m_webPlugin->printBegin(printParams);
322 }
323
printPage(int pageNumber,WebCore::GraphicsContext * gc)324 bool WebPluginContainerImpl::printPage(int pageNumber,
325 WebCore::GraphicsContext* gc)
326 {
327 gc->save();
328 WebCanvas* canvas = gc->canvas();
329 bool ret = m_webPlugin->printPage(pageNumber, canvas);
330 gc->restore();
331 return ret;
332 }
333
printEnd()334 void WebPluginContainerImpl::printEnd()
335 {
336 m_webPlugin->printEnd();
337 }
338
copy()339 void WebPluginContainerImpl::copy()
340 {
341 if (!m_webPlugin->hasSelection())
342 return;
343
344 blink::Platform::current()->clipboard()->writeHTML(m_webPlugin->selectionAsMarkup(), WebURL(), m_webPlugin->selectionAsText(), false);
345 }
346
executeEditCommand(const WebString & name)347 bool WebPluginContainerImpl::executeEditCommand(const WebString& name)
348 {
349 if (m_webPlugin->executeEditCommand(name))
350 return true;
351
352 if (name != "Copy")
353 return false;
354
355 copy();
356 return true;
357 }
358
executeEditCommand(const WebString & name,const WebString & value)359 bool WebPluginContainerImpl::executeEditCommand(const WebString& name, const WebString& value)
360 {
361 return m_webPlugin->executeEditCommand(name, value);
362 }
363
element()364 WebElement WebPluginContainerImpl::element()
365 {
366 return WebElement(m_element);
367 }
368
invalidate()369 void WebPluginContainerImpl::invalidate()
370 {
371 Widget::invalidate();
372 }
373
invalidateRect(const WebRect & rect)374 void WebPluginContainerImpl::invalidateRect(const WebRect& rect)
375 {
376 invalidateRect(static_cast<IntRect>(rect));
377 }
378
scrollRect(int dx,int dy,const WebRect & rect)379 void WebPluginContainerImpl::scrollRect(int dx, int dy, const WebRect& rect)
380 {
381 Widget* parentWidget = parent();
382 if (parentWidget->isFrameView()) {
383 FrameView* parentFrameView = toFrameView(parentWidget);
384 if (!parentFrameView->isOverlapped()) {
385 IntRect damageRect = convertToContainingWindow(static_cast<IntRect>(rect));
386 IntSize scrollDelta(dx, dy);
387 // scroll() only uses the second rectangle, clipRect, and ignores the first
388 // rectangle.
389 parent()->hostWindow()->scroll(scrollDelta, damageRect, damageRect);
390 return;
391 }
392 }
393
394 // Use slow scrolling instead.
395 invalidateRect(rect);
396 }
397
reportGeometry()398 void WebPluginContainerImpl::reportGeometry()
399 {
400 if (!parent())
401 return;
402
403 IntRect windowRect, clipRect;
404 Vector<IntRect> cutOutRects;
405 calculateGeometry(frameRect(), windowRect, clipRect, cutOutRects);
406
407 m_webPlugin->updateGeometry(windowRect, clipRect, cutOutRects, isVisible());
408
409 if (m_scrollbarGroup) {
410 m_scrollbarGroup->scrollAnimator()->contentsResized();
411 m_scrollbarGroup->setFrameRect(frameRect());
412 }
413 }
414
allowScriptObjects()415 void WebPluginContainerImpl::allowScriptObjects()
416 {
417 }
418
clearScriptObjects()419 void WebPluginContainerImpl::clearScriptObjects()
420 {
421 Frame* frame = m_element->document().frame();
422 if (!frame)
423 return;
424 frame->script().cleanupScriptObjectsForPlugin(this);
425 }
426
scriptableObjectForElement()427 NPObject* WebPluginContainerImpl::scriptableObjectForElement()
428 {
429 return m_element->getNPObject();
430 }
431
executeScriptURL(const WebURL & url,bool popupsAllowed)432 WebString WebPluginContainerImpl::executeScriptURL(const WebURL& url, bool popupsAllowed)
433 {
434 Frame* frame = m_element->document().frame();
435 if (!frame)
436 return WebString();
437
438 const KURL& kurl = url;
439 ASSERT(kurl.protocolIs("javascript"));
440
441 String script = decodeURLEscapeSequences(
442 kurl.string().substring(strlen("javascript:")));
443
444 UserGestureIndicator gestureIndicator(popupsAllowed ? DefinitelyProcessingNewUserGesture : PossiblyProcessingUserGesture);
445 ScriptValue result = frame->script().executeScriptInMainWorldAndReturnValue(ScriptSourceCode(script));
446
447 // Failure is reported as a null string.
448 String resultStr;
449 result.getString(resultStr);
450 return resultStr;
451 }
452
loadFrameRequest(const WebURLRequest & request,const WebString & target,bool notifyNeeded,void * notifyData)453 void WebPluginContainerImpl::loadFrameRequest(const WebURLRequest& request, const WebString& target, bool notifyNeeded, void* notifyData)
454 {
455 Frame* frame = m_element->document().frame();
456 if (!frame || !frame->loader().documentLoader())
457 return; // FIXME: send a notification in this case?
458
459 if (notifyNeeded) {
460 // FIXME: This is a bit of hack to allow us to observe completion of
461 // our frame request. It would be better to evolve FrameLoader to
462 // support a completion callback instead.
463 OwnPtr<WebPluginLoadObserver> observer = adoptPtr(new WebPluginLoadObserver(this, request.url(), notifyData));
464 // FIXME: Calling get here is dangerous! What if observer is freed?
465 m_pluginLoadObservers.append(observer.get());
466 WebDataSourceImpl::setNextPluginLoadObserver(observer.release());
467 }
468
469 FrameLoadRequest frameRequest(frame->document(), request.toResourceRequest(), target);
470 UserGestureIndicator gestureIndicator(request.hasUserGesture() ? DefinitelyProcessingNewUserGesture : PossiblyProcessingUserGesture);
471 frame->loader().load(frameRequest);
472 }
473
zoomLevelChanged(double zoomLevel)474 void WebPluginContainerImpl::zoomLevelChanged(double zoomLevel)
475 {
476 WebViewImpl* view = WebViewImpl::fromPage(m_element->document().frame()->page());
477 view->fullFramePluginZoomLevelChanged(zoomLevel);
478 }
479
isRectTopmost(const WebRect & rect)480 bool WebPluginContainerImpl::isRectTopmost(const WebRect& rect)
481 {
482 Frame* frame = m_element->document().frame();
483 if (!frame)
484 return false;
485
486 // hitTestResultAtPoint() takes a padding rectangle.
487 // FIXME: We'll be off by 1 when the width or height is even.
488 IntRect documentRect(x() + rect.x, y() + rect.y, rect.width, rect.height);
489 LayoutPoint center = documentRect.center();
490 // Make the rect we're checking (the point surrounded by padding rects) contained inside the requested rect. (Note that -1/2 is 0.)
491 LayoutSize padding((documentRect.width() - 1) / 2, (documentRect.height() - 1) / 2);
492 HitTestResult result = frame->eventHandler().hitTestResultAtPoint(center, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent, padding);
493 const HitTestResult::NodeSet& nodes = result.rectBasedTestResult();
494 if (nodes.size() != 1)
495 return false;
496 return nodes.first().get() == m_element;
497 }
498
requestTouchEventType(TouchEventRequestType requestType)499 void WebPluginContainerImpl::requestTouchEventType(TouchEventRequestType requestType)
500 {
501 if (m_touchEventRequestType == requestType)
502 return;
503
504 if (requestType != TouchEventRequestTypeNone && m_touchEventRequestType == TouchEventRequestTypeNone)
505 m_element->document().didAddTouchEventHandler(m_element);
506 else if (requestType == TouchEventRequestTypeNone && m_touchEventRequestType != TouchEventRequestTypeNone)
507 m_element->document().didRemoveTouchEventHandler(m_element);
508 m_touchEventRequestType = requestType;
509 }
510
setWantsWheelEvents(bool wantsWheelEvents)511 void WebPluginContainerImpl::setWantsWheelEvents(bool wantsWheelEvents)
512 {
513 if (m_wantsWheelEvents == wantsWheelEvents)
514 return;
515 m_wantsWheelEvents = wantsWheelEvents;
516 if (Page* page = m_element->document().page()) {
517 if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) {
518 if (parent() && parent()->isFrameView())
519 scrollingCoordinator->notifyLayoutUpdated();
520 }
521 }
522 }
523
windowToLocalPoint(const WebPoint & point)524 WebPoint WebPluginContainerImpl::windowToLocalPoint(const WebPoint& point)
525 {
526 ScrollView* view = toScrollView(parent());
527 if (!view)
528 return point;
529 WebPoint windowPoint = view->windowToContents(point);
530 return roundedIntPoint(m_element->renderer()->absoluteToLocal(LayoutPoint(windowPoint), UseTransforms));
531 }
532
localToWindowPoint(const WebPoint & point)533 WebPoint WebPluginContainerImpl::localToWindowPoint(const WebPoint& point)
534 {
535 ScrollView* view = toScrollView(parent());
536 if (!view)
537 return point;
538 IntPoint absolutePoint = roundedIntPoint(m_element->renderer()->localToAbsolute(LayoutPoint(point), UseTransforms));
539 return view->contentsToWindow(absolutePoint);
540 }
541
didReceiveResponse(const ResourceResponse & response)542 void WebPluginContainerImpl::didReceiveResponse(const ResourceResponse& response)
543 {
544 // Make sure that the plugin receives window geometry before data, or else
545 // plugins misbehave.
546 frameRectsChanged();
547
548 WrappedResourceResponse urlResponse(response);
549 m_webPlugin->didReceiveResponse(urlResponse);
550 }
551
didReceiveData(const char * data,int dataLength)552 void WebPluginContainerImpl::didReceiveData(const char *data, int dataLength)
553 {
554 m_webPlugin->didReceiveData(data, dataLength);
555 }
556
didFinishLoading()557 void WebPluginContainerImpl::didFinishLoading()
558 {
559 m_webPlugin->didFinishLoading();
560 }
561
didFailLoading(const ResourceError & error)562 void WebPluginContainerImpl::didFailLoading(const ResourceError& error)
563 {
564 m_webPlugin->didFailLoading(error);
565 }
566
platformLayer() const567 WebLayer* WebPluginContainerImpl::platformLayer() const
568 {
569 return m_webLayer;
570 }
571
scriptableObject()572 NPObject* WebPluginContainerImpl::scriptableObject()
573 {
574 return m_webPlugin->scriptableObject();
575 }
576
getFormValue(String & value)577 bool WebPluginContainerImpl::getFormValue(String& value)
578 {
579 WebString webValue;
580 if (m_webPlugin->getFormValue(webValue)) {
581 value = webValue;
582 return true;
583 }
584 return false;
585 }
586
supportsKeyboardFocus() const587 bool WebPluginContainerImpl::supportsKeyboardFocus() const
588 {
589 return m_webPlugin->supportsKeyboardFocus();
590 }
591
supportsInputMethod() const592 bool WebPluginContainerImpl::supportsInputMethod() const
593 {
594 return m_webPlugin->supportsInputMethod();
595 }
596
canProcessDrag() const597 bool WebPluginContainerImpl::canProcessDrag() const
598 {
599 return m_webPlugin->canProcessDrag();
600 }
601
wantsWheelEvents()602 bool WebPluginContainerImpl::wantsWheelEvents()
603 {
604 return m_wantsWheelEvents;
605 }
606
willDestroyPluginLoadObserver(WebPluginLoadObserver * observer)607 void WebPluginContainerImpl::willDestroyPluginLoadObserver(WebPluginLoadObserver* observer)
608 {
609 size_t pos = m_pluginLoadObservers.find(observer);
610 if (pos == kNotFound)
611 return;
612 m_pluginLoadObservers.remove(pos);
613 }
614
scrollbarGroup()615 ScrollbarGroup* WebPluginContainerImpl::scrollbarGroup()
616 {
617 if (!m_scrollbarGroup)
618 m_scrollbarGroup = adoptPtr(new ScrollbarGroup(m_element->document().frame()->view(), frameRect()));
619 return m_scrollbarGroup.get();
620 }
621
willStartLiveResize()622 void WebPluginContainerImpl::willStartLiveResize()
623 {
624 if (m_scrollbarGroup)
625 m_scrollbarGroup->willStartLiveResize();
626 }
627
willEndLiveResize()628 void WebPluginContainerImpl::willEndLiveResize()
629 {
630 if (m_scrollbarGroup)
631 m_scrollbarGroup->willEndLiveResize();
632 }
633
paintCustomOverhangArea(GraphicsContext * context,const IntRect & horizontalOverhangArea,const IntRect & verticalOverhangArea,const IntRect & dirtyRect)634 bool WebPluginContainerImpl::paintCustomOverhangArea(GraphicsContext* context, const IntRect& horizontalOverhangArea, const IntRect& verticalOverhangArea, const IntRect& dirtyRect)
635 {
636 context->save();
637 context->setFillColor(Color(0xCC, 0xCC, 0xCC));
638 context->fillRect(intersection(horizontalOverhangArea, dirtyRect));
639 context->fillRect(intersection(verticalOverhangArea, dirtyRect));
640 context->restore();
641 return true;
642 }
643
644 // Private methods -------------------------------------------------------------
645
WebPluginContainerImpl(WebCore::HTMLPlugInElement * element,WebPlugin * webPlugin)646 WebPluginContainerImpl::WebPluginContainerImpl(WebCore::HTMLPlugInElement* element, WebPlugin* webPlugin)
647 : m_element(element)
648 , m_webPlugin(webPlugin)
649 , m_webLayer(0)
650 , m_touchEventRequestType(TouchEventRequestTypeNone)
651 , m_wantsWheelEvents(false)
652 {
653 }
654
~WebPluginContainerImpl()655 WebPluginContainerImpl::~WebPluginContainerImpl()
656 {
657 if (m_touchEventRequestType != TouchEventRequestTypeNone)
658 m_element->document().didRemoveTouchEventHandler(m_element);
659
660 for (size_t i = 0; i < m_pluginLoadObservers.size(); ++i)
661 m_pluginLoadObservers[i]->clearPluginContainer();
662 m_webPlugin->destroy();
663 if (m_webLayer)
664 GraphicsLayer::unregisterContentsLayer(m_webLayer);
665 }
666
handleMouseEvent(MouseEvent * event)667 void WebPluginContainerImpl::handleMouseEvent(MouseEvent* event)
668 {
669 ASSERT(parent()->isFrameView());
670
671 if (event->isDragEvent()) {
672 if (m_webPlugin->canProcessDrag())
673 handleDragEvent(event);
674 return;
675 }
676
677 // We cache the parent FrameView here as the plugin widget could be deleted
678 // in the call to HandleEvent. See http://b/issue?id=1362948
679 FrameView* parentView = toFrameView(parent());
680
681 WebMouseEventBuilder webEvent(this, m_element->renderer(), *event);
682 if (webEvent.type == WebInputEvent::Undefined)
683 return;
684
685 if (event->type() == EventTypeNames::mousedown)
686 focusPlugin();
687
688 if (m_scrollbarGroup) {
689 // This needs to be set before the other callbacks in this scope, since
690 // the scroll animator class might query the position in response.
691 m_scrollbarGroup->setLastMousePosition(IntPoint(event->x(), event->y()));
692 if (event->type() == EventTypeNames::mousemove)
693 m_scrollbarGroup->scrollAnimator()->mouseMovedInContentArea();
694 else if (event->type() == EventTypeNames::mouseover)
695 m_scrollbarGroup->scrollAnimator()->mouseEnteredContentArea();
696 else if (event->type() == EventTypeNames::mouseout)
697 m_scrollbarGroup->scrollAnimator()->mouseExitedContentArea();
698 }
699
700 WebCursorInfo cursorInfo;
701 if (m_webPlugin->handleInputEvent(webEvent, cursorInfo))
702 event->setDefaultHandled();
703
704 // A windowless plugin can change the cursor in response to a mouse move
705 // event. We need to reflect the changed cursor in the frame view as the
706 // mouse is moved in the boundaries of the windowless plugin.
707 Page* page = parentView->frame().page();
708 if (!page)
709 return;
710 ChromeClientImpl* chromeClient = toChromeClientImpl(page->chrome().client());
711 chromeClient->setCursorForPlugin(cursorInfo);
712 }
713
handleDragEvent(MouseEvent * event)714 void WebPluginContainerImpl::handleDragEvent(MouseEvent* event)
715 {
716 ASSERT(event->isDragEvent());
717
718 WebDragStatus dragStatus = WebDragStatusUnknown;
719 if (event->type() == EventTypeNames::dragenter)
720 dragStatus = WebDragStatusEnter;
721 else if (event->type() == EventTypeNames::dragleave)
722 dragStatus = WebDragStatusLeave;
723 else if (event->type() == EventTypeNames::dragover)
724 dragStatus = WebDragStatusOver;
725 else if (event->type() == EventTypeNames::drop)
726 dragStatus = WebDragStatusDrop;
727
728 if (dragStatus == WebDragStatusUnknown)
729 return;
730
731 Clipboard* clipboard = event->dataTransfer();
732 WebDragData dragData = clipboard->dataObject();
733 WebDragOperationsMask dragOperationMask = static_cast<WebDragOperationsMask>(clipboard->sourceOperation());
734 WebPoint dragScreenLocation(event->screenX(), event->screenY());
735 WebPoint dragLocation(event->absoluteLocation().x() - location().x(), event->absoluteLocation().y() - location().y());
736
737 m_webPlugin->handleDragStatusUpdate(dragStatus, dragData, dragOperationMask, dragLocation, dragScreenLocation);
738 }
739
handleWheelEvent(WheelEvent * event)740 void WebPluginContainerImpl::handleWheelEvent(WheelEvent* event)
741 {
742 WebMouseWheelEventBuilder webEvent(this, m_element->renderer(), *event);
743 if (webEvent.type == WebInputEvent::Undefined)
744 return;
745
746 WebCursorInfo cursorInfo;
747 if (m_webPlugin->handleInputEvent(webEvent, cursorInfo))
748 event->setDefaultHandled();
749 }
750
handleKeyboardEvent(KeyboardEvent * event)751 void WebPluginContainerImpl::handleKeyboardEvent(KeyboardEvent* event)
752 {
753 WebKeyboardEventBuilder webEvent(*event);
754 if (webEvent.type == WebInputEvent::Undefined)
755 return;
756
757 if (webEvent.type == WebInputEvent::KeyDown) {
758 #if OS(MACOSX)
759 if (webEvent.modifiers == WebInputEvent::MetaKey
760 #else
761 if (webEvent.modifiers == WebInputEvent::ControlKey
762 #endif
763 && webEvent.windowsKeyCode == VKEY_C
764 // Only copy if there's a selection, so that we only ever do this
765 // for Pepper plugins that support copying. Windowless NPAPI
766 // plugins will get the event as before.
767 && m_webPlugin->hasSelection()) {
768 copy();
769 event->setDefaultHandled();
770 return;
771 }
772 }
773
774 const WebInputEvent* currentInputEvent = WebViewImpl::currentInputEvent();
775
776 // Copy stashed info over, and only copy here in order not to interfere
777 // the ctrl-c logic above.
778 if (currentInputEvent
779 && WebInputEvent::isKeyboardEventType(currentInputEvent->type)) {
780 webEvent.modifiers |= currentInputEvent->modifiers &
781 (WebInputEvent::CapsLockOn | WebInputEvent::NumLockOn);
782 }
783
784 // Give the client a chance to issue edit comamnds.
785 WebViewImpl* view = WebViewImpl::fromPage(m_element->document().frame()->page());
786 if (m_webPlugin->supportsEditCommands() && view->client())
787 view->client()->handleCurrentKeyboardEvent();
788
789 WebCursorInfo cursorInfo;
790 if (m_webPlugin->handleInputEvent(webEvent, cursorInfo))
791 event->setDefaultHandled();
792 }
793
handleTouchEvent(TouchEvent * event)794 void WebPluginContainerImpl::handleTouchEvent(TouchEvent* event)
795 {
796 switch (m_touchEventRequestType) {
797 case TouchEventRequestTypeNone:
798 return;
799 case TouchEventRequestTypeRaw: {
800 WebTouchEventBuilder webEvent(this, m_element->renderer(), *event);
801 if (webEvent.type == WebInputEvent::Undefined)
802 return;
803
804 if (event->type() == EventTypeNames::touchstart)
805 focusPlugin();
806
807 WebCursorInfo cursorInfo;
808 if (m_webPlugin->handleInputEvent(webEvent, cursorInfo))
809 event->setDefaultHandled();
810 // FIXME: Can a plugin change the cursor from a touch-event callback?
811 return;
812 }
813 case TouchEventRequestTypeSynthesizedMouse:
814 synthesizeMouseEventIfPossible(event);
815 return;
816 }
817 }
818
gestureScrollHelper(ScrollbarGroup * scrollbarGroup,ScrollDirection positiveDirection,ScrollDirection negativeDirection,float delta)819 static inline bool gestureScrollHelper(ScrollbarGroup* scrollbarGroup, ScrollDirection positiveDirection, ScrollDirection negativeDirection, float delta)
820 {
821 if (!delta)
822 return false;
823 float absDelta = delta > 0 ? delta : -delta;
824 return scrollbarGroup->scroll(delta < 0 ? negativeDirection : positiveDirection, ScrollByPrecisePixel, absDelta);
825 }
826
handleGestureEvent(GestureEvent * event)827 void WebPluginContainerImpl::handleGestureEvent(GestureEvent* event)
828 {
829 WebGestureEventBuilder webEvent(this, m_element->renderer(), *event);
830 if (webEvent.type == WebInputEvent::Undefined)
831 return;
832 WebCursorInfo cursorInfo;
833 if (m_webPlugin->handleInputEvent(webEvent, cursorInfo)) {
834 event->setDefaultHandled();
835 return;
836 }
837
838 if (webEvent.type == WebInputEvent::GestureScrollUpdate || webEvent.type == WebInputEvent::GestureScrollUpdateWithoutPropagation) {
839 if (!m_scrollbarGroup)
840 return;
841 if (gestureScrollHelper(m_scrollbarGroup.get(), ScrollLeft, ScrollRight, webEvent.data.scrollUpdate.deltaX))
842 event->setDefaultHandled();
843 if (gestureScrollHelper(m_scrollbarGroup.get(), ScrollUp, ScrollDown, webEvent.data.scrollUpdate.deltaY))
844 event->setDefaultHandled();
845 }
846 // FIXME: Can a plugin change the cursor from a touch-event callback?
847 }
848
synthesizeMouseEventIfPossible(TouchEvent * event)849 void WebPluginContainerImpl::synthesizeMouseEventIfPossible(TouchEvent* event)
850 {
851 WebMouseEventBuilder webEvent(this, m_element->renderer(), *event);
852 if (webEvent.type == WebInputEvent::Undefined)
853 return;
854
855 WebCursorInfo cursorInfo;
856 if (m_webPlugin->handleInputEvent(webEvent, cursorInfo))
857 event->setDefaultHandled();
858 }
859
focusPlugin()860 void WebPluginContainerImpl::focusPlugin()
861 {
862 Frame& containingFrame = toFrameView(parent())->frame();
863 if (Page* currentPage = containingFrame.page())
864 currentPage->focusController().setFocusedElement(m_element, &containingFrame);
865 else
866 containingFrame.document()->setFocusedElement(m_element);
867 }
868
calculateGeometry(const IntRect & frameRect,IntRect & windowRect,IntRect & clipRect,Vector<IntRect> & cutOutRects)869 void WebPluginContainerImpl::calculateGeometry(const IntRect& frameRect,
870 IntRect& windowRect,
871 IntRect& clipRect,
872 Vector<IntRect>& cutOutRects)
873 {
874 windowRect = toScrollView(parent())->contentsToWindow(frameRect);
875
876 // Calculate a clip-rect so that we don't overlap the scrollbars, etc.
877 clipRect = windowClipRect();
878 clipRect.move(-windowRect.x(), -windowRect.y());
879
880 getPluginOcclusions(m_element, this->parent(), frameRect, cutOutRects);
881 // Convert to the plugin position.
882 for (size_t i = 0; i < cutOutRects.size(); i++)
883 cutOutRects[i].move(-frameRect.x(), -frameRect.y());
884 }
885
windowClipRect() const886 WebCore::IntRect WebPluginContainerImpl::windowClipRect() const
887 {
888 // Start by clipping to our bounds.
889 IntRect clipRect =
890 convertToContainingWindow(IntRect(0, 0, width(), height()));
891
892 // document().renderer() can be 0 when we receive messages from the
893 // plugins while we are destroying a frame.
894 // FIXME: Can we just check m_element->document().isActive() ?
895 if (m_element->renderer()->document().renderer()) {
896 // Take our element and get the clip rect from the enclosing layer and
897 // frame view.
898 clipRect.intersect(
899 m_element->document().view()->windowClipRectForFrameOwner(m_element, true));
900 }
901
902 return clipRect;
903 }
904
905 } // namespace blink
906