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