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