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