1/*
2 * Copyright (C) 2010, 2012 Apple 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
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "PluginView.h"
28
29#include "NPRuntimeUtilities.h"
30#include "Plugin.h"
31#include "ShareableBitmap.h"
32#include "WebCoreArgumentCoders.h"
33#include "WebEvent.h"
34#include "WebPage.h"
35#include "WebPageProxyMessages.h"
36#include "WebProcess.h"
37#include <WebCore/BitmapImage.h>
38#include <WebCore/Chrome.h>
39#include <WebCore/CookieJar.h>
40#include <WebCore/Credential.h>
41#include <WebCore/CredentialStorage.h>
42#include <WebCore/DocumentLoader.h>
43#include <WebCore/EventHandler.h>
44#include <WebCore/FocusController.h>
45#include <WebCore/Frame.h>
46#include <WebCore/FrameLoadRequest.h>
47#include <WebCore/FrameLoader.h>
48#include <WebCore/FrameLoaderClient.h>
49#include <WebCore/FrameView.h>
50#include <WebCore/GraphicsContext.h>
51#include <WebCore/HTMLPlugInElement.h>
52#include <WebCore/HTMLPlugInImageElement.h>
53#include <WebCore/HostWindow.h>
54#include <WebCore/MIMETypeRegistry.h>
55#include <WebCore/MouseEvent.h>
56#include <WebCore/NetscapePlugInStreamLoader.h>
57#include <WebCore/NetworkingContext.h>
58#include <WebCore/Page.h>
59#include <WebCore/PageThrottler.h>
60#include <WebCore/PlatformMouseEvent.h>
61#include <WebCore/ProtectionSpace.h>
62#include <WebCore/ProxyServer.h>
63#include <WebCore/RenderEmbeddedObject.h>
64#include <WebCore/ResourceLoadScheduler.h>
65#include <WebCore/ScriptController.h>
66#include <WebCore/ScriptValue.h>
67#include <WebCore/ScrollView.h>
68#include <WebCore/SecurityOrigin.h>
69#include <WebCore/SecurityPolicy.h>
70#include <WebCore/Settings.h>
71#include <WebCore/UserGestureIndicator.h>
72#include <wtf/text/StringBuilder.h>
73
74using namespace JSC;
75using namespace WebCore;
76
77namespace WebKit {
78
79// This simulated mouse click delay in HTMLPlugInImageElement.cpp should generally be the same or shorter than this delay.
80static const double pluginSnapshotTimerDelay = 1.1;
81
82class PluginView::URLRequest : public RefCounted<URLRequest> {
83public:
84    static PassRefPtr<PluginView::URLRequest> create(uint64_t requestID, const FrameLoadRequest& request, bool allowPopups)
85    {
86        return adoptRef(new URLRequest(requestID, request, allowPopups));
87    }
88
89    uint64_t requestID() const { return m_requestID; }
90    const String& target() const { return m_request.frameName(); }
91    const ResourceRequest & request() const { return m_request.resourceRequest(); }
92    bool allowPopups() const { return m_allowPopups; }
93
94private:
95    URLRequest(uint64_t requestID, const FrameLoadRequest& request, bool allowPopups)
96        : m_requestID(requestID)
97        , m_request(request)
98        , m_allowPopups(allowPopups)
99    {
100    }
101
102    uint64_t m_requestID;
103    FrameLoadRequest m_request;
104    bool m_allowPopups;
105};
106
107class PluginView::Stream : public RefCounted<PluginView::Stream>, NetscapePlugInStreamLoaderClient {
108public:
109    static PassRefPtr<Stream> create(PluginView* pluginView, uint64_t streamID, const ResourceRequest& request)
110    {
111        return adoptRef(new Stream(pluginView, streamID, request));
112    }
113    ~Stream();
114
115    void start();
116    void cancel();
117
118    uint64_t streamID() const { return m_streamID; }
119
120private:
121    Stream(PluginView* pluginView, uint64_t streamID, const ResourceRequest& request)
122        : m_pluginView(pluginView)
123        , m_streamID(streamID)
124        , m_request(request)
125        , m_streamWasCancelled(false)
126    {
127    }
128
129    // NetscapePluginStreamLoaderClient
130    virtual void didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse&);
131    virtual void didReceiveData(NetscapePlugInStreamLoader*, const char*, int);
132    virtual void didFail(NetscapePlugInStreamLoader*, const ResourceError&);
133    virtual void didFinishLoading(NetscapePlugInStreamLoader*);
134
135    PluginView* m_pluginView;
136    uint64_t m_streamID;
137    const ResourceRequest m_request;
138
139    // True if the stream was explicitly cancelled by calling cancel().
140    // (As opposed to being cancelled by the user hitting the stop button for example.
141    bool m_streamWasCancelled;
142
143    RefPtr<NetscapePlugInStreamLoader> m_loader;
144};
145
146PluginView::Stream::~Stream()
147{
148    ASSERT(!m_pluginView);
149}
150
151void PluginView::Stream::start()
152{
153    ASSERT(m_pluginView->m_plugin);
154    ASSERT(!m_loader);
155
156    Frame* frame = m_pluginView->m_pluginElement->document()->frame();
157    ASSERT(frame);
158
159    m_loader = resourceLoadScheduler()->schedulePluginStreamLoad(frame, this, m_request);
160}
161
162void PluginView::Stream::cancel()
163{
164    ASSERT(m_loader);
165
166    m_streamWasCancelled = true;
167    m_loader->cancel(m_loader->cancelledError());
168    m_loader = 0;
169}
170
171static String buildHTTPHeaders(const ResourceResponse& response, long long& expectedContentLength)
172{
173    if (!response.isHTTP())
174        return String();
175
176    StringBuilder stringBuilder;
177
178    String statusLine = String::format("HTTP %d ", response.httpStatusCode());
179    stringBuilder.append(statusLine);
180    stringBuilder.append(response.httpStatusText());
181    stringBuilder.append('\n');
182
183    HTTPHeaderMap::const_iterator end = response.httpHeaderFields().end();
184    for (HTTPHeaderMap::const_iterator it = response.httpHeaderFields().begin(); it != end; ++it) {
185        stringBuilder.append(it->key);
186        stringBuilder.appendLiteral(": ");
187        stringBuilder.append(it->value);
188        stringBuilder.append('\n');
189    }
190
191    String headers = stringBuilder.toString();
192
193    // If the content is encoded (most likely compressed), then don't send its length to the plugin,
194    // which is only interested in the decoded length, not yet known at the moment.
195    // <rdar://problem/4470599> tracks a request for -[NSURLResponse expectedContentLength] to incorporate this logic.
196    String contentEncoding = response.httpHeaderField("Content-Encoding");
197    if (!contentEncoding.isNull() && contentEncoding != "identity")
198        expectedContentLength = -1;
199
200    return headers;
201}
202
203void PluginView::Stream::didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse& response)
204{
205    // Compute the stream related data from the resource response.
206    const KURL& responseURL = response.url();
207    const String& mimeType = response.mimeType();
208    long long expectedContentLength = response.expectedContentLength();
209
210    String headers = buildHTTPHeaders(response, expectedContentLength);
211
212    uint32_t streamLength = 0;
213    if (expectedContentLength > 0)
214        streamLength = expectedContentLength;
215
216    m_pluginView->m_plugin->streamDidReceiveResponse(m_streamID, responseURL, streamLength, response.lastModifiedDate(), mimeType, headers, response.suggestedFilename());
217}
218
219void PluginView::Stream::didReceiveData(NetscapePlugInStreamLoader*, const char* bytes, int length)
220{
221    m_pluginView->m_plugin->streamDidReceiveData(m_streamID, bytes, length);
222}
223
224void PluginView::Stream::didFail(NetscapePlugInStreamLoader*, const ResourceError& error)
225{
226    // Calling streamDidFail could cause us to be deleted, so we hold on to a reference here.
227    RefPtr<Stream> protect(this);
228
229    // We only want to call streamDidFail if the stream was not explicitly cancelled by the plug-in.
230    if (!m_streamWasCancelled)
231        m_pluginView->m_plugin->streamDidFail(m_streamID, error.isCancellation());
232
233    m_pluginView->removeStream(this);
234    m_pluginView = 0;
235}
236
237void PluginView::Stream::didFinishLoading(NetscapePlugInStreamLoader*)
238{
239    // Calling streamDidFinishLoading could cause us to be deleted, so we hold on to a reference here.
240    RefPtr<Stream> protectStream(this);
241
242#if ENABLE(NETSCAPE_PLUGIN_API)
243    // Protect the plug-in while we're calling into it.
244    NPRuntimeObjectMap::PluginProtector pluginProtector(&m_pluginView->m_npRuntimeObjectMap);
245#endif
246    m_pluginView->m_plugin->streamDidFinishLoading(m_streamID);
247
248    m_pluginView->removeStream(this);
249    m_pluginView = 0;
250}
251
252static inline WebPage* webPage(HTMLPlugInElement* pluginElement)
253{
254    Frame* frame = pluginElement->document()->frame();
255    ASSERT(frame);
256
257    WebFrameLoaderClient* webFrameLoaderClient = toWebFrameLoaderClient(frame->loader()->client());
258    WebPage* webPage = webFrameLoaderClient ? webFrameLoaderClient->webFrame()->page() : 0;
259    ASSERT(webPage);
260
261    return webPage;
262}
263
264PassRefPtr<PluginView> PluginView::create(PassRefPtr<HTMLPlugInElement> pluginElement, PassRefPtr<Plugin> plugin, const Plugin::Parameters& parameters)
265{
266    return adoptRef(new PluginView(pluginElement, plugin, parameters));
267}
268
269PluginView::PluginView(PassRefPtr<HTMLPlugInElement> pluginElement, PassRefPtr<Plugin> plugin, const Plugin::Parameters& parameters)
270    : PluginViewBase(0)
271    , m_pluginElement(pluginElement)
272    , m_plugin(plugin)
273    , m_webPage(webPage(m_pluginElement.get()))
274    , m_parameters(parameters)
275    , m_isInitialized(false)
276    , m_isWaitingForSynchronousInitialization(false)
277    , m_isWaitingUntilMediaCanStart(false)
278    , m_isBeingDestroyed(false)
279    , m_pluginProcessHasCrashed(false)
280    , m_pendingURLRequestsTimer(RunLoop::main(), this, &PluginView::pendingURLRequestsTimerFired)
281#if ENABLE(NETSCAPE_PLUGIN_API)
282    , m_npRuntimeObjectMap(this)
283#endif
284    , m_manualStreamState(StreamStateInitial)
285    , m_pluginSnapshotTimer(this, &PluginView::pluginSnapshotTimerFired, pluginSnapshotTimerDelay)
286    , m_countSnapshotRetries(0)
287    , m_didReceiveUserInteraction(false)
288    , m_pageScaleFactor(1)
289{
290    m_webPage->addPluginView(this);
291}
292
293PluginView::~PluginView()
294{
295    if (m_webPage)
296        m_webPage->removePluginView(this);
297
298    ASSERT(!m_isBeingDestroyed);
299
300    if (m_isWaitingUntilMediaCanStart)
301        m_pluginElement->document()->removeMediaCanStartListener(this);
302
303    destroyPluginAndReset();
304
305    // Null out the plug-in element explicitly so we'll crash earlier if we try to use
306    // the plug-in view after it's been destroyed.
307    m_pluginElement = nullptr;
308}
309
310void PluginView::destroyPluginAndReset()
311{
312    // Cancel all pending frame loads.
313    for (FrameLoadMap::iterator it = m_pendingFrameLoads.begin(), end = m_pendingFrameLoads.end(); it != end; ++it)
314        it->key->setLoadListener(0);
315
316    if (m_plugin) {
317        m_isBeingDestroyed = true;
318        m_plugin->destroyPlugin();
319        m_isBeingDestroyed = false;
320
321        m_pendingURLRequests.clear();
322        m_pendingURLRequestsTimer.stop();
323
324#if PLATFORM(MAC)
325        if (m_webPage)
326            pluginFocusOrWindowFocusChanged(false);
327#endif
328    }
329
330#if ENABLE(NETSCAPE_PLUGIN_API)
331    // Invalidate the object map.
332    m_npRuntimeObjectMap.invalidate();
333#endif
334
335    cancelAllStreams();
336}
337
338void PluginView::recreateAndInitialize(PassRefPtr<Plugin> plugin)
339{
340    if (m_plugin) {
341        if (m_pluginSnapshotTimer.isActive())
342            m_pluginSnapshotTimer.stop();
343        destroyPluginAndReset();
344    }
345
346    // Reset member variables to initial values.
347    m_plugin = plugin;
348    m_isInitialized = false;
349    m_isWaitingForSynchronousInitialization = false;
350    m_isWaitingUntilMediaCanStart = false;
351    m_isBeingDestroyed = false;
352    m_manualStreamState = StreamStateInitial;
353    m_transientPaintingSnapshot = nullptr;
354
355    initializePlugin();
356}
357
358Frame* PluginView::frame() const
359{
360    return m_pluginElement->document()->frame();
361}
362
363void PluginView::manualLoadDidReceiveResponse(const ResourceResponse& response)
364{
365    // The plug-in can be null here if it failed to initialize.
366    if (!m_plugin)
367        return;
368
369    if (!m_isInitialized) {
370        ASSERT(m_manualStreamState == StreamStateInitial);
371        m_manualStreamState = StreamStateHasReceivedResponse;
372        m_manualStreamResponse = response;
373        return;
374    }
375
376    // Compute the stream related data from the resource response.
377    const KURL& responseURL = response.url();
378    const String& mimeType = response.mimeType();
379    long long expectedContentLength = response.expectedContentLength();
380
381    String headers = buildHTTPHeaders(response, expectedContentLength);
382
383    uint32_t streamLength = 0;
384    if (expectedContentLength > 0)
385        streamLength = expectedContentLength;
386
387    m_plugin->manualStreamDidReceiveResponse(responseURL, streamLength, response.lastModifiedDate(), mimeType, headers, response.suggestedFilename());
388}
389
390void PluginView::manualLoadDidReceiveData(const char* bytes, int length)
391{
392    // The plug-in can be null here if it failed to initialize.
393    if (!m_plugin)
394        return;
395
396    if (!m_isInitialized) {
397        ASSERT(m_manualStreamState == StreamStateHasReceivedResponse);
398        if (!m_manualStreamData)
399            m_manualStreamData = SharedBuffer::create();
400
401        m_manualStreamData->append(bytes, length);
402        return;
403    }
404
405    m_plugin->manualStreamDidReceiveData(bytes, length);
406}
407
408void PluginView::manualLoadDidFinishLoading()
409{
410    // The plug-in can be null here if it failed to initialize.
411    if (!m_plugin)
412        return;
413
414    if (!m_isInitialized) {
415        ASSERT(m_manualStreamState == StreamStateHasReceivedResponse);
416        m_manualStreamState = StreamStateFinished;
417        return;
418    }
419
420    m_plugin->manualStreamDidFinishLoading();
421}
422
423void PluginView::manualLoadDidFail(const ResourceError& error)
424{
425    // The plug-in can be null here if it failed to initialize.
426    if (!m_plugin)
427        return;
428
429    if (!m_isInitialized) {
430        m_manualStreamState = StreamStateFinished;
431        m_manualStreamError = error;
432        m_manualStreamData = nullptr;
433        return;
434    }
435
436    m_plugin->manualStreamDidFail(error.isCancellation());
437}
438
439RenderBoxModelObject* PluginView::renderer() const
440{
441    return toRenderBoxModelObject(m_pluginElement->renderer());
442}
443
444void PluginView::pageScaleFactorDidChange()
445{
446    viewGeometryDidChange();
447}
448
449void PluginView::setPageScaleFactor(double scaleFactor, IntPoint)
450{
451    m_pageScaleFactor = scaleFactor;
452    m_webPage->send(Messages::WebPageProxy::PageScaleFactorDidChange(scaleFactor));
453    m_webPage->send(Messages::WebPageProxy::PageZoomFactorDidChange(scaleFactor));
454    pageScaleFactorDidChange();
455}
456
457double PluginView::pageScaleFactor() const
458{
459    return m_pageScaleFactor;
460}
461
462bool PluginView::handlesPageScaleFactor() const
463{
464    if (!m_plugin || !m_isInitialized)
465        return false;
466
467    return m_plugin->handlesPageScaleFactor();
468}
469
470void PluginView::webPageDestroyed()
471{
472    m_webPage = 0;
473}
474
475#if PLATFORM(MAC)
476void PluginView::setWindowIsVisible(bool windowIsVisible)
477{
478    if (!m_isInitialized || !m_plugin)
479        return;
480
481    m_plugin->windowVisibilityChanged(windowIsVisible);
482}
483
484void PluginView::setWindowIsFocused(bool windowIsFocused)
485{
486    if (!m_isInitialized || !m_plugin)
487        return;
488
489    m_plugin->windowFocusChanged(windowIsFocused);
490}
491
492void PluginView::setDeviceScaleFactor(float scaleFactor)
493{
494    if (!m_isInitialized || !m_plugin)
495        return;
496
497    m_plugin->contentsScaleFactorChanged(scaleFactor);
498}
499
500void PluginView::windowAndViewFramesChanged(const FloatRect& windowFrameInScreenCoordinates, const FloatRect& viewFrameInWindowCoordinates)
501{
502    if (!m_isInitialized || !m_plugin)
503        return;
504
505    m_plugin->windowAndViewFramesChanged(enclosingIntRect(windowFrameInScreenCoordinates), enclosingIntRect(viewFrameInWindowCoordinates));
506}
507
508bool PluginView::sendComplexTextInput(uint64_t pluginComplexTextInputIdentifier, const String& textInput)
509{
510    if (!m_plugin)
511        return false;
512
513    if (m_plugin->pluginComplexTextInputIdentifier() != pluginComplexTextInputIdentifier)
514        return false;
515
516    m_plugin->sendComplexTextInput(textInput);
517    return true;
518}
519
520void PluginView::setLayerHostingMode(LayerHostingMode layerHostingMode)
521{
522    if (!m_plugin)
523        return;
524
525    if (!m_isInitialized) {
526        m_parameters.layerHostingMode = layerHostingMode;
527        return;
528    }
529
530    m_plugin->setLayerHostingMode(layerHostingMode);
531}
532
533NSObject *PluginView::accessibilityObject() const
534{
535    if (!m_isInitialized || !m_plugin)
536        return 0;
537
538    return m_plugin->accessibilityObject();
539}
540#endif
541
542void PluginView::initializePlugin()
543{
544    if (m_isInitialized)
545        return;
546
547    if (!m_plugin) {
548        // We've already tried and failed to initialize the plug-in.
549        return;
550    }
551
552    if (Frame* frame = m_pluginElement->document()->frame()) {
553        if (Page* page = frame->page()) {
554
555            // We shouldn't initialize the plug-in right now, add a listener.
556            if (!page->canStartMedia()) {
557                if (m_isWaitingUntilMediaCanStart)
558                    return;
559
560                m_isWaitingUntilMediaCanStart = true;
561                m_pluginElement->document()->addMediaCanStartListener(this);
562                return;
563            }
564        }
565    }
566
567    m_plugin->initialize(this, m_parameters);
568
569    // Plug-in initialization continued in didFailToInitializePlugin() or didInitializePlugin().
570}
571
572void PluginView::didFailToInitializePlugin()
573{
574    m_plugin = 0;
575
576    String frameURLString = frame()->loader()->documentLoader()->responseURL().string();
577    String pageURLString = m_webPage->corePage()->mainFrame()->loader()->documentLoader()->responseURL().string();
578    m_webPage->send(Messages::WebPageProxy::DidFailToInitializePlugin(m_parameters.mimeType, frameURLString, pageURLString));
579}
580
581void PluginView::didInitializePlugin()
582{
583    m_isInitialized = true;
584
585#if PLATFORM(MAC)
586    windowAndViewFramesChanged(m_webPage->windowFrameInScreenCoordinates(), m_webPage->viewFrameInWindowCoordinates());
587#endif
588
589    viewGeometryDidChange();
590
591    if (m_pluginElement->document()->focusedElement() == m_pluginElement)
592        m_plugin->setFocus(true);
593
594    redeliverManualStream();
595
596#if PLATFORM(MAC)
597    if (m_pluginElement->displayState() < HTMLPlugInElement::Restarting) {
598        if (m_plugin->pluginLayer() && frame()) {
599            frame()->view()->enterCompositingMode();
600            m_pluginElement->setNeedsStyleRecalc(SyntheticStyleChange);
601        }
602        if (frame() && !frame()->settings()->maximumPlugInSnapshotAttempts()) {
603            m_pluginElement->setDisplayState(HTMLPlugInElement::DisplayingSnapshot);
604            return;
605        }
606        m_pluginSnapshotTimer.restart();
607    } else {
608        if (m_plugin->pluginLayer() && frame()) {
609            frame()->view()->enterCompositingMode();
610            m_pluginElement->setNeedsStyleRecalc(SyntheticStyleChange);
611        }
612        if (m_pluginElement->displayState() == HTMLPlugInElement::RestartingWithPendingMouseClick)
613            m_pluginElement->dispatchPendingMouseClick();
614    }
615
616    setWindowIsVisible(m_webPage->windowIsVisible());
617    setWindowIsFocused(m_webPage->windowIsFocused());
618#endif
619
620    if (wantsWheelEvents()) {
621        if (Frame* frame = m_pluginElement->document()->frame()) {
622            if (FrameView* frameView = frame->view())
623                frameView->setNeedsLayout();
624        }
625    }
626}
627
628#if PLATFORM(MAC)
629PlatformLayer* PluginView::platformLayer() const
630{
631    // The plug-in can be null here if it failed to initialize.
632    if (!m_isInitialized || !m_plugin || m_pluginProcessHasCrashed)
633        return 0;
634
635    return m_plugin->pluginLayer();
636}
637#endif
638
639JSObject* PluginView::scriptObject(JSGlobalObject* globalObject)
640{
641    // If we're already waiting for synchronous initialization of the plugin,
642    // calls to scriptObject() are from the plug-in itself and need to return 0;
643    if (m_isWaitingForSynchronousInitialization)
644        return 0;
645
646    // We might not have started initialization of the plug-in yet, the plug-in might be in the middle
647    // of being initializing asynchronously, or initialization might have previously failed.
648    if (!m_isInitialized || !m_plugin)
649        return 0;
650
651#if ENABLE(NETSCAPE_PLUGIN_API)
652    NPObject* scriptableNPObject = m_plugin->pluginScriptableNPObject();
653    if (!scriptableNPObject)
654        return 0;
655
656    JSObject* jsObject = m_npRuntimeObjectMap.getOrCreateJSObject(globalObject, scriptableNPObject);
657    releaseNPObject(scriptableNPObject);
658
659    return jsObject;
660#else
661    UNUSED_PARAM(globalObject);
662    return 0;
663#endif
664}
665
666void PluginView::storageBlockingStateChanged()
667{
668    // The plug-in can be null here if it failed to initialize.
669    if (!m_isInitialized || !m_plugin)
670        return;
671
672    bool storageBlockingPolicy = !frame()->document()->securityOrigin()->canAccessPluginStorage(frame()->document()->topOrigin());
673
674    m_plugin->storageBlockingStateChanged(storageBlockingPolicy);
675}
676
677void PluginView::privateBrowsingStateChanged(bool privateBrowsingEnabled)
678{
679    // The plug-in can be null here if it failed to initialize.
680    if (!m_isInitialized || !m_plugin)
681        return;
682
683    m_plugin->privateBrowsingStateChanged(privateBrowsingEnabled);
684}
685
686bool PluginView::getFormValue(String& formValue)
687{
688    // The plug-in can be null here if it failed to initialize.
689    if (!m_isInitialized || !m_plugin)
690        return false;
691
692    return m_plugin->getFormValue(formValue);
693}
694
695bool PluginView::scroll(ScrollDirection direction, ScrollGranularity granularity)
696{
697    // The plug-in can be null here if it failed to initialize.
698    if (!m_isInitialized || !m_plugin)
699        return false;
700
701    return m_plugin->handleScroll(direction, granularity);
702}
703
704Scrollbar* PluginView::horizontalScrollbar()
705{
706    // The plug-in can be null here if it failed to initialize.
707    if (!m_isInitialized || !m_plugin)
708        return 0;
709
710    return m_plugin->horizontalScrollbar();
711}
712
713Scrollbar* PluginView::verticalScrollbar()
714{
715    // The plug-in can be null here if it failed to initialize.
716    if (!m_isInitialized || !m_plugin)
717        return 0;
718
719    return m_plugin->verticalScrollbar();
720}
721
722bool PluginView::wantsWheelEvents()
723{
724    // The plug-in can be null here if it failed to initialize.
725    if (!m_isInitialized || !m_plugin)
726        return 0;
727
728    return m_plugin->wantsWheelEvents();
729}
730
731void PluginView::setFrameRect(const WebCore::IntRect& rect)
732{
733    Widget::setFrameRect(rect);
734    viewGeometryDidChange();
735}
736
737void PluginView::paint(GraphicsContext* context, const IntRect& /*dirtyRect*/)
738{
739    if (!m_plugin || !m_isInitialized || m_pluginElement->displayState() < HTMLPlugInElement::Restarting)
740        return;
741
742    if (context->paintingDisabled()) {
743        if (context->updatingControlTints())
744            m_plugin->updateControlTints(context);
745        return;
746    }
747
748    // FIXME: We should try to intersect the dirty rect with the plug-in's clip rect here.
749    IntRect paintRect = IntRect(IntPoint(), frameRect().size());
750
751    if (paintRect.isEmpty())
752        return;
753
754    if (m_transientPaintingSnapshot) {
755        m_transientPaintingSnapshot->paint(*context, contentsScaleFactor(), frameRect().location(), m_transientPaintingSnapshot->bounds());
756        return;
757    }
758
759    GraphicsContextStateSaver stateSaver(*context);
760
761    // Translate the coordinate system so that the origin is in the top-left corner of the plug-in.
762    context->translate(frameRect().location().x(), frameRect().location().y());
763
764    m_plugin->paint(context, paintRect);
765}
766
767void PluginView::frameRectsChanged()
768{
769    Widget::frameRectsChanged();
770    viewGeometryDidChange();
771}
772
773void PluginView::clipRectChanged()
774{
775    viewGeometryDidChange();
776}
777
778void PluginView::setParent(ScrollView* scrollView)
779{
780    Widget::setParent(scrollView);
781
782    if (scrollView)
783        initializePlugin();
784}
785
786unsigned PluginView::countFindMatches(const String& target, WebCore::FindOptions options, unsigned maxMatchCount)
787{
788    if (!m_isInitialized || !m_plugin)
789        return 0;
790
791    return m_plugin->countFindMatches(target, options, maxMatchCount);
792}
793
794bool PluginView::findString(const String& target, WebCore::FindOptions options, unsigned maxMatchCount)
795{
796    if (!m_isInitialized || !m_plugin)
797        return false;
798
799    return m_plugin->findString(target, options, maxMatchCount);
800}
801
802String PluginView::getSelectionString() const
803{
804    if (!m_isInitialized || !m_plugin)
805        return String();
806
807    return m_plugin->getSelectionString();
808}
809
810PassOwnPtr<WebEvent> PluginView::createWebEvent(MouseEvent* event) const
811{
812    WebEvent::Type type = WebEvent::NoType;
813    unsigned clickCount = 1;
814    if (event->type() == eventNames().mousedownEvent)
815        type = WebEvent::MouseDown;
816    else if (event->type() == eventNames().mouseupEvent)
817        type = WebEvent::MouseUp;
818    else if (event->type() == eventNames().mouseoverEvent) {
819        type = WebEvent::MouseMove;
820        clickCount = 0;
821    } else if (event->type() == eventNames().clickEvent)
822        return nullptr;
823    else
824        ASSERT_NOT_REACHED();
825
826    WebMouseEvent::Button button = WebMouseEvent::NoButton;
827    switch (event->button()) {
828    case WebCore::LeftButton:
829        button = WebMouseEvent::LeftButton;
830        break;
831    case WebCore::MiddleButton:
832        button = WebMouseEvent::MiddleButton;
833        break;
834    case WebCore::RightButton:
835        button = WebMouseEvent::RightButton;
836        break;
837    default:
838        ASSERT_NOT_REACHED();
839        break;
840    }
841
842    unsigned modifiers = 0;
843    if (event->shiftKey())
844        modifiers |= WebEvent::ShiftKey;
845    if (event->ctrlKey())
846        modifiers |= WebEvent::ControlKey;
847    if (event->altKey())
848        modifiers |= WebEvent::AltKey;
849    if (event->metaKey())
850        modifiers |= WebEvent::MetaKey;
851
852    return adoptPtr(new WebMouseEvent(type, button, m_plugin->convertToRootView(IntPoint(event->offsetX(), event->offsetY())), event->screenLocation(), 0, 0, 0, clickCount, static_cast<WebEvent::Modifiers>(modifiers), 0));
853}
854
855void PluginView::handleEvent(Event* event)
856{
857    if (!m_isInitialized || !m_plugin)
858        return;
859
860    const WebEvent* currentEvent = WebPage::currentEvent();
861    OwnPtr<WebEvent> simulatedWebEvent;
862    if (event->isMouseEvent() && toMouseEvent(event)->isSimulated()) {
863        simulatedWebEvent = createWebEvent(toMouseEvent(event));
864        currentEvent = simulatedWebEvent.get();
865    }
866    if (!currentEvent)
867        return;
868
869    bool didHandleEvent = false;
870
871    if ((event->type() == eventNames().mousemoveEvent && currentEvent->type() == WebEvent::MouseMove)
872        || (event->type() == eventNames().mousedownEvent && currentEvent->type() == WebEvent::MouseDown)
873        || (event->type() == eventNames().mouseupEvent && currentEvent->type() == WebEvent::MouseUp)) {
874        // FIXME: Clicking in a scroll bar should not change focus.
875        if (currentEvent->type() == WebEvent::MouseDown) {
876            focusPluginElement();
877            frame()->eventHandler()->setCapturingMouseEventsNode(m_pluginElement.get());
878        } else if (currentEvent->type() == WebEvent::MouseUp)
879            frame()->eventHandler()->setCapturingMouseEventsNode(0);
880
881        didHandleEvent = m_plugin->handleMouseEvent(static_cast<const WebMouseEvent&>(*currentEvent));
882        if (event->type() != eventNames().mousemoveEvent)
883            pluginDidReceiveUserInteraction();
884    } else if (event->type() == eventNames().mousewheelEvent && currentEvent->type() == WebEvent::Wheel && m_plugin->wantsWheelEvents()) {
885        didHandleEvent = m_plugin->handleWheelEvent(static_cast<const WebWheelEvent&>(*currentEvent));
886        pluginDidReceiveUserInteraction();
887    } else if (event->type() == eventNames().mouseoverEvent && currentEvent->type() == WebEvent::MouseMove)
888        didHandleEvent = m_plugin->handleMouseEnterEvent(static_cast<const WebMouseEvent&>(*currentEvent));
889    else if (event->type() == eventNames().mouseoutEvent && currentEvent->type() == WebEvent::MouseMove)
890        didHandleEvent = m_plugin->handleMouseLeaveEvent(static_cast<const WebMouseEvent&>(*currentEvent));
891    else if (event->type() == eventNames().contextmenuEvent && currentEvent->type() == WebEvent::MouseDown) {
892        didHandleEvent = m_plugin->handleContextMenuEvent(static_cast<const WebMouseEvent&>(*currentEvent));
893        pluginDidReceiveUserInteraction();
894    } else if ((event->type() == eventNames().keydownEvent && currentEvent->type() == WebEvent::KeyDown)
895               || (event->type() == eventNames().keyupEvent && currentEvent->type() == WebEvent::KeyUp)) {
896        didHandleEvent = m_plugin->handleKeyboardEvent(static_cast<const WebKeyboardEvent&>(*currentEvent));
897        pluginDidReceiveUserInteraction();
898    }
899
900    if (didHandleEvent)
901        event->setDefaultHandled();
902}
903
904bool PluginView::handleEditingCommand(const String& commandName, const String& argument)
905{
906    if (!m_isInitialized || !m_plugin)
907        return false;
908
909    return m_plugin->handleEditingCommand(commandName, argument);
910}
911
912bool PluginView::isEditingCommandEnabled(const String& commandName)
913{
914    if (!m_isInitialized || !m_plugin)
915        return false;
916
917    return m_plugin->isEditingCommandEnabled(commandName);
918}
919
920bool PluginView::shouldAllowScripting()
921{
922    if (!m_isInitialized || !m_plugin)
923        return false;
924
925    return m_plugin->shouldAllowScripting();
926}
927
928bool PluginView::shouldAllowNavigationFromDrags() const
929{
930    if (!m_isInitialized || !m_plugin)
931        return false;
932
933    return m_plugin->shouldAllowNavigationFromDrags();
934}
935
936bool PluginView::shouldNotAddLayer() const
937{
938    return m_pluginElement->displayState() < HTMLPlugInElement::Restarting && !m_plugin->supportsSnapshotting();
939}
940
941PassRefPtr<SharedBuffer> PluginView::liveResourceData() const
942{
943    if (!m_isInitialized || !m_plugin)
944        return 0;
945
946    return m_plugin->liveResourceData();
947}
948
949bool PluginView::performDictionaryLookupAtLocation(const WebCore::FloatPoint& point)
950{
951    if (!m_isInitialized || !m_plugin)
952        return false;
953
954    return m_plugin->performDictionaryLookupAtLocation(point);
955}
956
957void PluginView::notifyWidget(WidgetNotification notification)
958{
959    switch (notification) {
960    case WillPaintFlattened:
961        if (shouldCreateTransientPaintingSnapshot())
962            m_transientPaintingSnapshot = m_plugin->snapshot();
963        break;
964    case DidPaintFlattened:
965        m_transientPaintingSnapshot = nullptr;
966        break;
967    }
968}
969
970void PluginView::show()
971{
972    bool wasVisible = isVisible();
973
974    setSelfVisible(true);
975
976    if (!wasVisible)
977        viewVisibilityDidChange();
978
979    Widget::show();
980}
981
982void PluginView::hide()
983{
984    bool wasVisible = isVisible();
985
986    setSelfVisible(false);
987
988    if (wasVisible)
989        viewVisibilityDidChange();
990
991    Widget::hide();
992}
993
994bool PluginView::transformsAffectFrameRect()
995{
996    return false;
997}
998
999void PluginView::viewGeometryDidChange()
1000{
1001    if (!m_isInitialized || !m_plugin || !parent())
1002        return;
1003
1004    ASSERT(frame());
1005    float pageScaleFactor = frame()->page() ? frame()->page()->pageScaleFactor() : 1;
1006
1007    IntPoint scaledFrameRectLocation(frameRect().location().x() * pageScaleFactor, frameRect().location().y() * pageScaleFactor);
1008    IntPoint scaledLocationInRootViewCoordinates(parent()->contentsToRootView(scaledFrameRectLocation));
1009
1010    // FIXME: We still don't get the right coordinates for transformed plugins.
1011    AffineTransform transform;
1012    transform.translate(scaledLocationInRootViewCoordinates.x(), scaledLocationInRootViewCoordinates.y());
1013    transform.scale(pageScaleFactor);
1014
1015    // FIXME: The way we calculate this clip rect isn't correct.
1016    // But it is still important to distinguish between empty and non-empty rects so we can notify the plug-in when it becomes invisible.
1017    // Making the rect actually correct is covered by https://bugs.webkit.org/show_bug.cgi?id=95362
1018    IntRect clipRect = boundsRect();
1019
1020    // FIXME: We can only get a semi-reliable answer from clipRectInWindowCoordinates() when the page is not scaled.
1021    // Fixing that is tracked in <rdar://problem/9026611> - Make the Widget hierarchy play nicely with transforms, for zoomed plug-ins and iframes
1022    if (pageScaleFactor == 1) {
1023        clipRect = clipRectInWindowCoordinates();
1024        if (!clipRect.isEmpty())
1025            clipRect = boundsRect();
1026    }
1027
1028    m_plugin->geometryDidChange(size(), clipRect, transform);
1029}
1030
1031void PluginView::viewVisibilityDidChange()
1032{
1033    if (!m_isInitialized || !m_plugin || !parent())
1034        return;
1035
1036    m_plugin->visibilityDidChange();
1037}
1038
1039IntRect PluginView::clipRectInWindowCoordinates() const
1040{
1041    // Get the frame rect in window coordinates.
1042    IntRect frameRectInWindowCoordinates = parent()->contentsToWindow(frameRect());
1043
1044    Frame* frame = this->frame();
1045
1046    // Get the window clip rect for the plugin element (in window coordinates).
1047    IntRect windowClipRect = frame->view()->windowClipRectForFrameOwner(m_pluginElement.get(), true);
1048
1049    // Intersect the two rects to get the view clip rect in window coordinates.
1050    frameRectInWindowCoordinates.intersect(windowClipRect);
1051
1052    return frameRectInWindowCoordinates;
1053}
1054
1055void PluginView::focusPluginElement()
1056{
1057    ASSERT(frame());
1058
1059    if (Page* page = frame()->page())
1060        page->focusController()->setFocusedElement(m_pluginElement.get(), frame());
1061    else
1062        frame()->document()->setFocusedElement(m_pluginElement);
1063}
1064
1065void PluginView::pendingURLRequestsTimerFired()
1066{
1067    ASSERT(!m_pendingURLRequests.isEmpty());
1068
1069    RefPtr<URLRequest> urlRequest = m_pendingURLRequests.takeFirst();
1070
1071    // If there are more requests to perform, reschedule the timer.
1072    if (!m_pendingURLRequests.isEmpty())
1073        m_pendingURLRequestsTimer.startOneShot(0);
1074
1075    performURLRequest(urlRequest.get());
1076}
1077
1078void PluginView::performURLRequest(URLRequest* request)
1079{
1080    // This protector is needed to make sure the PluginView is not destroyed while it is still needed.
1081    RefPtr<PluginView> protect(this);
1082
1083    // First, check if this is a javascript: url.
1084    if (protocolIsJavaScript(request->request().url())) {
1085        performJavaScriptURLRequest(request);
1086        return;
1087    }
1088
1089    if (!request->target().isNull()) {
1090        performFrameLoadURLRequest(request);
1091        return;
1092    }
1093
1094    // This request is to load a URL and create a stream.
1095    RefPtr<Stream> stream = PluginView::Stream::create(this, request->requestID(), request->request());
1096    addStream(stream.get());
1097    stream->start();
1098}
1099
1100void PluginView::performFrameLoadURLRequest(URLRequest* request)
1101{
1102    ASSERT(!request->target().isNull());
1103
1104    Frame* frame = m_pluginElement->document()->frame();
1105    if (!frame)
1106        return;
1107
1108    if (!m_pluginElement->document()->securityOrigin()->canDisplay(request->request().url())) {
1109        // We can't load the request, send back a reply to the plug-in.
1110        m_plugin->frameDidFail(request->requestID(), false);
1111        return;
1112    }
1113
1114    UserGestureIndicator gestureIndicator(request->allowPopups() ? DefinitelyProcessingNewUserGesture : PossiblyProcessingUserGesture);
1115
1116    // First, try to find a target frame.
1117    Frame* targetFrame = frame->loader()->findFrameForNavigation(request->target());
1118    if (!targetFrame) {
1119        // We did not find a target frame. Ask our frame to load the page. This may or may not create a popup window.
1120        FrameLoadRequest frameRequest(frame, request->request());
1121        frameRequest.setFrameName(request->target());
1122        frameRequest.setShouldCheckNewWindowPolicy(true);
1123        frame->loader()->load(frameRequest);
1124
1125        // FIXME: We don't know whether the window was successfully created here so we just assume that it worked.
1126        // It's better than not telling the plug-in anything.
1127        m_plugin->frameDidFinishLoading(request->requestID());
1128        return;
1129    }
1130
1131    // Now ask the frame to load the request.
1132    targetFrame->loader()->load(FrameLoadRequest(targetFrame, request->request()));
1133
1134    WebFrameLoaderClient* webFrameLoaderClient = toWebFrameLoaderClient(targetFrame->loader()->client());
1135    WebFrame* targetWebFrame = webFrameLoaderClient ? webFrameLoaderClient->webFrame() : 0;
1136    ASSERT(targetWebFrame);
1137
1138    if (WebFrame::LoadListener* loadListener = targetWebFrame->loadListener()) {
1139        // Check if another plug-in view or even this view is waiting for the frame to load.
1140        // If it is, tell it that the load was cancelled because it will be anyway.
1141        loadListener->didFailLoad(targetWebFrame, true);
1142    }
1143
1144    m_pendingFrameLoads.set(targetWebFrame, request);
1145    targetWebFrame->setLoadListener(this);
1146}
1147
1148void PluginView::performJavaScriptURLRequest(URLRequest* request)
1149{
1150    ASSERT(protocolIsJavaScript(request->request().url()));
1151
1152    RefPtr<Frame> frame = m_pluginElement->document()->frame();
1153    if (!frame)
1154        return;
1155
1156    String jsString = decodeURLEscapeSequences(request->request().url().string().substring(sizeof("javascript:") - 1));
1157
1158    if (!request->target().isNull()) {
1159        // For security reasons, only allow JS requests to be made on the frame that contains the plug-in.
1160        if (frame->tree()->find(request->target()) != frame) {
1161            // Let the plug-in know that its frame load failed.
1162            m_plugin->frameDidFail(request->requestID(), false);
1163            return;
1164        }
1165    }
1166
1167    // Evaluate the JavaScript code. Note that running JavaScript here could cause the plug-in to be destroyed, so we
1168    // grab references to the plug-in here.
1169    RefPtr<Plugin> plugin = m_plugin;
1170    ScriptValue result = frame->script()->executeScript(jsString, request->allowPopups());
1171
1172    // Check if evaluating the JavaScript destroyed the plug-in.
1173    if (!plugin->controller())
1174        return;
1175
1176    // Don't notify the plug-in at all about targeted javascript: requests. This matches Mozilla and WebKit1.
1177    if (!request->target().isNull())
1178        return;
1179
1180    ScriptState* scriptState = frame->script()->globalObject(pluginWorld())->globalExec();
1181    String resultString;
1182    result.getString(scriptState, resultString);
1183
1184    // Send the result back to the plug-in.
1185    plugin->didEvaluateJavaScript(request->requestID(), resultString);
1186}
1187
1188void PluginView::addStream(Stream* stream)
1189{
1190    ASSERT(!m_streams.contains(stream->streamID()));
1191    m_streams.set(stream->streamID(), stream);
1192}
1193
1194void PluginView::removeStream(Stream* stream)
1195{
1196    ASSERT(m_streams.get(stream->streamID()) == stream);
1197
1198    m_streams.remove(stream->streamID());
1199}
1200
1201void PluginView::cancelAllStreams()
1202{
1203    Vector<RefPtr<Stream>> streams;
1204    copyValuesToVector(m_streams, streams);
1205
1206    for (size_t i = 0; i < streams.size(); ++i)
1207        streams[i]->cancel();
1208
1209    // Cancelling a stream removes it from the m_streams map, so if we cancel all streams the map should be empty.
1210    ASSERT(m_streams.isEmpty());
1211}
1212
1213void PluginView::redeliverManualStream()
1214{
1215    if (m_manualStreamState == StreamStateInitial) {
1216        // Nothing to do.
1217        return;
1218    }
1219
1220    if (m_manualStreamState == StreamStateFailed) {
1221        manualLoadDidFail(m_manualStreamError);
1222        return;
1223    }
1224
1225    // Deliver the response.
1226    manualLoadDidReceiveResponse(m_manualStreamResponse);
1227
1228    // Deliver the data.
1229    if (m_manualStreamData) {
1230        const char* data;
1231        unsigned position = 0;
1232
1233        while (unsigned length = m_manualStreamData->getSomeData(data, position)) {
1234            manualLoadDidReceiveData(data, length);
1235            position += length;
1236        }
1237
1238        m_manualStreamData = nullptr;
1239    }
1240
1241    if (m_manualStreamState == StreamStateFinished)
1242        manualLoadDidFinishLoading();
1243}
1244
1245void PluginView::invalidateRect(const IntRect& dirtyRect)
1246{
1247    if (!parent() || !m_plugin || !m_isInitialized)
1248        return;
1249
1250#if PLATFORM(MAC)
1251    if (m_plugin->pluginLayer())
1252        return;
1253#endif
1254
1255    if (m_pluginElement->displayState() < HTMLPlugInElement::Restarting)
1256        return;
1257
1258    RenderBoxModelObject* renderer = toRenderBoxModelObject(m_pluginElement->renderer());
1259    if (!renderer)
1260        return;
1261
1262    IntRect contentRect(dirtyRect);
1263    contentRect.move(renderer->borderLeft() + renderer->paddingLeft(), renderer->borderTop() + renderer->paddingTop());
1264    renderer->repaintRectangle(contentRect);
1265}
1266
1267void PluginView::setFocus(bool hasFocus)
1268{
1269    Widget::setFocus(hasFocus);
1270
1271    if (!m_isInitialized || !m_plugin)
1272        return;
1273
1274    m_plugin->setFocus(hasFocus);
1275}
1276
1277void PluginView::mediaCanStart()
1278{
1279    ASSERT(m_isWaitingUntilMediaCanStart);
1280    m_isWaitingUntilMediaCanStart = false;
1281
1282    initializePlugin();
1283}
1284
1285bool PluginView::isPluginVisible()
1286{
1287    return isVisible();
1288}
1289
1290void PluginView::invalidate(const IntRect& dirtyRect)
1291{
1292    invalidateRect(dirtyRect);
1293}
1294
1295String PluginView::userAgent()
1296{
1297    Frame* frame = m_pluginElement->document()->frame();
1298    if (!frame)
1299        return String();
1300
1301    return frame->loader()->client()->userAgent(KURL());
1302}
1303
1304void PluginView::loadURL(uint64_t requestID, const String& method, const String& urlString, const String& target,
1305                         const HTTPHeaderMap& headerFields, const Vector<uint8_t>& httpBody, bool allowPopups)
1306{
1307    FrameLoadRequest frameLoadRequest(m_pluginElement->document()->securityOrigin());
1308    frameLoadRequest.resourceRequest().setHTTPMethod(method);
1309    frameLoadRequest.resourceRequest().setURL(m_pluginElement->document()->completeURL(urlString));
1310    frameLoadRequest.resourceRequest().addHTTPHeaderFields(headerFields);
1311    frameLoadRequest.resourceRequest().setHTTPBody(FormData::create(httpBody.data(), httpBody.size()));
1312    frameLoadRequest.setFrameName(target);
1313
1314    String referrer = SecurityPolicy::generateReferrerHeader(frame()->document()->referrerPolicy(), frameLoadRequest.resourceRequest().url(), frame()->loader()->outgoingReferrer());
1315    if (!referrer.isEmpty())
1316        frameLoadRequest.resourceRequest().setHTTPReferrer(referrer);
1317
1318    m_pendingURLRequests.append(URLRequest::create(requestID, frameLoadRequest, allowPopups));
1319    m_pendingURLRequestsTimer.startOneShot(0);
1320}
1321
1322void PluginView::cancelStreamLoad(uint64_t streamID)
1323{
1324    // Keep a reference to the stream. Stream::cancel might remove the stream from the map, and thus
1325    // releasing its last reference.
1326    RefPtr<Stream> stream = m_streams.get(streamID);
1327    if (!stream)
1328        return;
1329
1330    // Cancelling the stream here will remove it from the map.
1331    stream->cancel();
1332    ASSERT(!m_streams.contains(streamID));
1333}
1334
1335void PluginView::cancelManualStreamLoad()
1336{
1337    if (!frame())
1338        return;
1339
1340    DocumentLoader* documentLoader = frame()->loader()->activeDocumentLoader();
1341    ASSERT(documentLoader);
1342
1343    if (documentLoader->isLoadingMainResource())
1344        documentLoader->cancelMainResourceLoad(frame()->loader()->cancelledError(m_parameters.url));
1345}
1346
1347#if ENABLE(NETSCAPE_PLUGIN_API)
1348NPObject* PluginView::windowScriptNPObject()
1349{
1350    if (!frame())
1351        return 0;
1352
1353    if (!frame()->script()->canExecuteScripts(NotAboutToExecuteScript)) {
1354        // FIXME: Investigate if other browsers allow plug-ins to access JavaScript objects even if JavaScript is disabled.
1355        return 0;
1356    }
1357
1358    return m_npRuntimeObjectMap.getOrCreateNPObject(*pluginWorld()->vm(), frame()->script()->windowShell(pluginWorld())->window());
1359}
1360
1361NPObject* PluginView::pluginElementNPObject()
1362{
1363    if (!frame())
1364        return 0;
1365
1366    if (!frame()->script()->canExecuteScripts(NotAboutToExecuteScript)) {
1367        // FIXME: Investigate if other browsers allow plug-ins to access JavaScript objects even if JavaScript is disabled.
1368        return 0;
1369    }
1370
1371    JSObject* object = frame()->script()->jsObjectForPluginElement(m_pluginElement.get());
1372    ASSERT(object);
1373
1374    return m_npRuntimeObjectMap.getOrCreateNPObject(*pluginWorld()->vm(), object);
1375}
1376
1377bool PluginView::evaluate(NPObject* npObject, const String& scriptString, NPVariant* result, bool allowPopups)
1378{
1379    // FIXME: Is this check necessary?
1380    if (!m_pluginElement->document()->frame())
1381        return false;
1382
1383    // Calling evaluate will run JavaScript that can potentially remove the plug-in element, so we need to
1384    // protect the plug-in view from destruction.
1385    NPRuntimeObjectMap::PluginProtector pluginProtector(&m_npRuntimeObjectMap);
1386
1387    UserGestureIndicator gestureIndicator(allowPopups ? DefinitelyProcessingNewUserGesture : PossiblyProcessingUserGesture);
1388    return m_npRuntimeObjectMap.evaluate(npObject, scriptString, result);
1389}
1390#endif
1391
1392void PluginView::setStatusbarText(const String& statusbarText)
1393{
1394    if (!frame())
1395        return;
1396
1397    Page* page = frame()->page();
1398    if (!page)
1399        return;
1400
1401    page->chrome().setStatusbarText(frame(), statusbarText);
1402}
1403
1404bool PluginView::isAcceleratedCompositingEnabled()
1405{
1406    if (!frame())
1407        return false;
1408
1409    Settings* settings = frame()->settings();
1410    if (!settings)
1411        return false;
1412
1413    // We know that some plug-ins can support snapshotting without needing
1414    // accelerated compositing. Since we're trying to snapshot them anyway,
1415    // put them into normal compositing mode. A side benefit is that this might
1416    // allow the entire page to stay in that mode.
1417    if (m_pluginElement->displayState() < HTMLPlugInElement::Restarting && m_parameters.mimeType == "application/x-shockwave-flash")
1418        return false;
1419
1420    return settings->acceleratedCompositingEnabled();
1421}
1422
1423void PluginView::pluginProcessCrashed()
1424{
1425    m_pluginProcessHasCrashed = true;
1426
1427    if (!m_pluginElement->renderer())
1428        return;
1429
1430    // FIXME: The renderer could also be a RenderApplet, we should handle that.
1431    if (!m_pluginElement->renderer()->isEmbeddedObject())
1432        return;
1433
1434    m_pluginElement->setNeedsStyleRecalc(SyntheticStyleChange);
1435
1436    RenderEmbeddedObject* renderer = toRenderEmbeddedObject(m_pluginElement->renderer());
1437    renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginCrashed);
1438
1439    Widget::invalidate();
1440}
1441
1442void PluginView::willSendEventToPlugin()
1443{
1444    // If we're sending an event to a plug-in, we can't control how long the plug-in
1445    // takes to process it (e.g. it may display a context menu), so we tell the UI process
1446    // to stop the responsiveness timer in this case.
1447    m_webPage->send(Messages::WebPageProxy::StopResponsivenessTimer());
1448}
1449
1450#if PLATFORM(MAC)
1451void PluginView::pluginFocusOrWindowFocusChanged(bool pluginHasFocusAndWindowHasFocus)
1452{
1453    if (m_webPage)
1454        m_webPage->send(Messages::WebPageProxy::PluginFocusOrWindowFocusChanged(m_plugin->pluginComplexTextInputIdentifier(), pluginHasFocusAndWindowHasFocus));
1455}
1456
1457void PluginView::setComplexTextInputState(PluginComplexTextInputState pluginComplexTextInputState)
1458{
1459    if (m_webPage)
1460        m_webPage->send(Messages::WebPageProxy::SetPluginComplexTextInputState(m_plugin->pluginComplexTextInputIdentifier(), pluginComplexTextInputState));
1461}
1462
1463mach_port_t PluginView::compositingRenderServerPort()
1464{
1465    return WebProcess::shared().compositingRenderServerPort();
1466}
1467
1468void PluginView::openPluginPreferencePane()
1469{
1470    ASSERT_NOT_REACHED();
1471}
1472
1473#endif
1474
1475float PluginView::contentsScaleFactor()
1476{
1477    if (Page* page = frame() ? frame()->page() : 0)
1478        return page->deviceScaleFactor();
1479
1480    return 1;
1481}
1482
1483String PluginView::proxiesForURL(const String& urlString)
1484{
1485    const FrameLoader* frameLoader = frame() ? frame()->loader() : 0;
1486    const NetworkingContext* context = frameLoader ? frameLoader->networkingContext() : 0;
1487    Vector<ProxyServer> proxyServers = proxyServersForURL(KURL(KURL(), urlString), context);
1488    return toString(proxyServers);
1489}
1490
1491String PluginView::cookiesForURL(const String& urlString)
1492{
1493    return cookies(m_pluginElement->document(), KURL(KURL(), urlString));
1494}
1495
1496void PluginView::setCookiesForURL(const String& urlString, const String& cookieString)
1497{
1498    setCookies(m_pluginElement->document(), KURL(KURL(), urlString), cookieString);
1499}
1500
1501bool PluginView::getAuthenticationInfo(const ProtectionSpace& protectionSpace, String& username, String& password)
1502{
1503    Credential credential = CredentialStorage::get(protectionSpace);
1504    if (credential.isEmpty())
1505        credential = CredentialStorage::getFromPersistentStorage(protectionSpace);
1506
1507    if (!credential.hasPassword())
1508        return false;
1509
1510    username = credential.user();
1511    password = credential.password();
1512
1513    return true;
1514}
1515
1516bool PluginView::isPrivateBrowsingEnabled()
1517{
1518    // If we can't get the real setting, we'll assume that private browsing is enabled.
1519    if (!frame())
1520        return true;
1521
1522    if (!frame()->document()->securityOrigin()->canAccessPluginStorage(frame()->document()->topOrigin()))
1523        return true;
1524
1525    Settings* settings = frame()->settings();
1526    if (!settings)
1527        return true;
1528
1529    return settings->privateBrowsingEnabled();
1530}
1531
1532bool PluginView::asynchronousPluginInitializationEnabled() const
1533{
1534    return m_webPage->asynchronousPluginInitializationEnabled();
1535}
1536
1537bool PluginView::asynchronousPluginInitializationEnabledForAllPlugins() const
1538{
1539    return m_webPage->asynchronousPluginInitializationEnabledForAllPlugins();
1540}
1541
1542bool PluginView::artificialPluginInitializationDelayEnabled() const
1543{
1544    return m_webPage->artificialPluginInitializationDelayEnabled();
1545}
1546
1547void PluginView::protectPluginFromDestruction()
1548{
1549    if (!m_isBeingDestroyed)
1550        ref();
1551}
1552
1553static void derefPluginView(PluginView* pluginView)
1554{
1555    pluginView->deref();
1556}
1557
1558void PluginView::unprotectPluginFromDestruction()
1559{
1560    if (m_isBeingDestroyed)
1561        return;
1562
1563    // A plug-in may ask us to evaluate JavaScript that removes the plug-in from the
1564    // page, but expect the object to still be alive when the call completes. Flash,
1565    // for example, may crash if the plug-in is destroyed and we return to code for
1566    // the destroyed object higher on the stack. To prevent this, if the plug-in has
1567    // only one remaining reference, call deref() asynchronously.
1568    if (hasOneRef())
1569        RunLoop::main()->dispatch(bind(derefPluginView, this));
1570    else
1571        deref();
1572}
1573
1574void PluginView::didFinishLoad(WebFrame* webFrame)
1575{
1576    RefPtr<URLRequest> request = m_pendingFrameLoads.take(webFrame);
1577    ASSERT(request);
1578    webFrame->setLoadListener(0);
1579
1580    m_plugin->frameDidFinishLoading(request->requestID());
1581}
1582
1583void PluginView::didFailLoad(WebFrame* webFrame, bool wasCancelled)
1584{
1585    RefPtr<URLRequest> request = m_pendingFrameLoads.take(webFrame);
1586    ASSERT(request);
1587    webFrame->setLoadListener(0);
1588
1589    m_plugin->frameDidFail(request->requestID(), wasCancelled);
1590}
1591
1592#if PLUGIN_ARCHITECTURE(X11)
1593uint64_t PluginView::createPluginContainer()
1594{
1595    uint64_t windowID = 0;
1596    m_webPage->sendSync(Messages::WebPageProxy::CreatePluginContainer(), Messages::WebPageProxy::CreatePluginContainer::Reply(windowID));
1597    return windowID;
1598}
1599
1600void PluginView::windowedPluginGeometryDidChange(const WebCore::IntRect& frameRect, const WebCore::IntRect& clipRect, uint64_t windowID)
1601{
1602    m_webPage->send(Messages::WebPageProxy::WindowedPluginGeometryDidChange(frameRect, clipRect, windowID));
1603}
1604#endif
1605
1606#if PLATFORM(MAC)
1607static bool isAlmostSolidColor(BitmapImage* bitmap)
1608{
1609    CGImageRef image = bitmap->getCGImageRef();
1610    ASSERT(CGImageGetBitsPerComponent(image) == 8);
1611
1612    CGBitmapInfo imageInfo = CGImageGetBitmapInfo(image);
1613    if (!(imageInfo & kCGBitmapByteOrder32Little) || (imageInfo & kCGBitmapAlphaInfoMask) != kCGImageAlphaPremultipliedFirst) {
1614        // FIXME: Consider being able to handle other pixel formats.
1615        ASSERT_NOT_REACHED();
1616        return false;
1617    }
1618
1619    size_t width = CGImageGetWidth(image);
1620    size_t height = CGImageGetHeight(image);
1621    size_t bytesPerRow = CGImageGetBytesPerRow(image);
1622
1623    RetainPtr<CFDataRef> provider = adoptCF(CGDataProviderCopyData(CGImageGetDataProvider(image)));
1624    const UInt8* data = CFDataGetBytePtr(provider.get());
1625
1626    // Overlay a grid of sampling dots on top of a grayscale version of the image.
1627    // For the interior points, calculate the difference in luminance among the sample point
1628    // and its surrounds points, scaled by transparency.
1629    const unsigned sampleRows = 7;
1630    const unsigned sampleCols = 7;
1631    // FIXME: Refine the proper number of samples, and accommodate different aspect ratios.
1632    if (width < sampleCols || height < sampleRows)
1633        return false;
1634
1635    // Ensure that the last row/column land on the image perimeter.
1636    const float strideWidth = static_cast<float>(width - 1) / (sampleCols - 1);
1637    const float strideHeight = static_cast<float>(height - 1) / (sampleRows - 1);
1638    float samples[sampleRows][sampleCols];
1639
1640    // Find the luminance of the sample points.
1641    float y = 0;
1642    const UInt8* row = data;
1643    for (unsigned i = 0; i < sampleRows; ++i) {
1644        float x = 0;
1645        for (unsigned j = 0; j < sampleCols; ++j) {
1646            const UInt8* p0 = row + (static_cast<int>(x + .5)) * 4;
1647            // R G B A
1648            samples[i][j] = (0.2125 * *p0 + 0.7154 * *(p0+1) + 0.0721 * *(p0+2)) * *(p0+3) / 255;
1649            x += strideWidth;
1650        }
1651        y += strideHeight;
1652        row = data + (static_cast<int>(y + .5)) * bytesPerRow;
1653    }
1654
1655    // Determine the image score.
1656    float accumScore = 0;
1657    for (unsigned i = 1; i < sampleRows - 1; ++i) {
1658        for (unsigned j = 1; j < sampleCols - 1; ++j) {
1659            float diff = samples[i - 1][j] + samples[i + 1][j] + samples[i][j - 1] + samples[i][j + 1] - 4 * samples[i][j];
1660            accumScore += diff * diff;
1661        }
1662    }
1663
1664    // The score for a given sample can be within the range of 0 and 255^2.
1665    return accumScore < 2500 * (sampleRows - 2) * (sampleCols - 2);
1666}
1667#endif
1668
1669void PluginView::pluginSnapshotTimerFired(DeferrableOneShotTimer<PluginView>*)
1670{
1671    ASSERT(m_plugin);
1672
1673    if (m_plugin->supportsSnapshotting()) {
1674        // Snapshot might be 0 if plugin size is 0x0.
1675        RefPtr<ShareableBitmap> snapshot = m_plugin->snapshot();
1676        RefPtr<Image> snapshotImage;
1677        if (snapshot)
1678            snapshotImage = snapshot->createImage();
1679        m_pluginElement->updateSnapshot(snapshotImage.get());
1680
1681#if PLATFORM(MAC)
1682        unsigned maximumSnapshotRetries = frame() ? frame()->settings()->maximumPlugInSnapshotAttempts() : 0;
1683        if (snapshotImage && isAlmostSolidColor(static_cast<BitmapImage*>(snapshotImage.get())) && m_countSnapshotRetries < maximumSnapshotRetries) {
1684            ++m_countSnapshotRetries;
1685            m_pluginSnapshotTimer.restart();
1686            return;
1687        }
1688#endif
1689    }
1690    // Even if there is no snapshot we still set the state to DisplayingSnapshot
1691    // since we just want to display the default empty box.
1692    m_pluginElement->setDisplayState(HTMLPlugInElement::DisplayingSnapshot);
1693}
1694
1695void PluginView::beginSnapshottingRunningPlugin()
1696{
1697    m_pluginSnapshotTimer.restart();
1698}
1699
1700bool PluginView::shouldAlwaysAutoStart() const
1701{
1702    if (!m_plugin)
1703        return PluginViewBase::shouldAlwaysAutoStart();
1704
1705    if (MIMETypeRegistry::isJavaAppletMIMEType(m_parameters.mimeType))
1706        return true;
1707
1708    return m_plugin->shouldAlwaysAutoStart();
1709}
1710
1711void PluginView::pluginDidReceiveUserInteraction()
1712{
1713    if (frame() && !frame()->settings()->plugInSnapshottingEnabled())
1714        return;
1715
1716    if (m_didReceiveUserInteraction)
1717        return;
1718
1719    m_didReceiveUserInteraction = true;
1720
1721    WebCore::HTMLPlugInImageElement* plugInImageElement = toHTMLPlugInImageElement(m_pluginElement.get());
1722    String pageOrigin = plugInImageElement->document()->page()->mainFrame()->document()->baseURL().host();
1723    String pluginOrigin = plugInImageElement->loadedUrl().host();
1724    String mimeType = plugInImageElement->loadedMimeType();
1725
1726    WebProcess::shared().plugInDidReceiveUserInteraction(pageOrigin, pluginOrigin, mimeType);
1727}
1728
1729bool PluginView::shouldCreateTransientPaintingSnapshot() const
1730{
1731    if (!m_plugin)
1732        return false;
1733
1734    if (!m_isInitialized)
1735        return false;
1736
1737    if (FrameView* frameView = frame()->view()) {
1738        if (frameView->paintBehavior() & (PaintBehaviorSelectionOnly | PaintBehaviorForceBlackText)) {
1739            // This paint behavior is used when drawing the find indicator and there's no need to
1740            // snapshot plug-ins, because they can never be painted as part of the find indicator.
1741            return false;
1742        }
1743    }
1744
1745    return true;
1746}
1747
1748} // namespace WebKit
1749