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