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