1/*
2 * Copyright (C) 2010 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 "WebEvent.h"
33#include "WebPage.h"
34#include "WebPageProxyMessages.h"
35#include "WebProcess.h"
36#include <WebCore/Chrome.h>
37#include <WebCore/CookieJar.h>
38#include <WebCore/DocumentLoader.h>
39#include <WebCore/Event.h>
40#include <WebCore/FocusController.h>
41#include <WebCore/FrameLoadRequest.h>
42#include <WebCore/FrameLoaderClient.h>
43#include <WebCore/FrameView.h>
44#include <WebCore/GraphicsContext.h>
45#include <WebCore/HTMLPlugInElement.h>
46#include <WebCore/HostWindow.h>
47#include <WebCore/NetscapePlugInStreamLoader.h>
48#include <WebCore/NetworkingContext.h>
49#include <WebCore/ProxyServer.h>
50#include <WebCore/RenderEmbeddedObject.h>
51#include <WebCore/RenderLayer.h>
52#include <WebCore/ResourceLoadScheduler.h>
53#include <WebCore/ScrollView.h>
54#include <WebCore/Settings.h>
55
56using namespace JSC;
57using namespace WebCore;
58
59namespace WebKit {
60
61class PluginView::URLRequest : public RefCounted<URLRequest> {
62public:
63    static PassRefPtr<PluginView::URLRequest> create(uint64_t requestID, const FrameLoadRequest& request, bool allowPopups)
64    {
65        return adoptRef(new URLRequest(requestID, request, allowPopups));
66    }
67
68    uint64_t requestID() const { return m_requestID; }
69    const String& target() const { return m_request.frameName(); }
70    const ResourceRequest & request() const { return m_request.resourceRequest(); }
71    bool allowPopups() const { return m_allowPopups; }
72
73private:
74    URLRequest(uint64_t requestID, const FrameLoadRequest& request, bool allowPopups)
75        : m_requestID(requestID)
76        , m_request(request)
77        , m_allowPopups(allowPopups)
78    {
79    }
80
81    uint64_t m_requestID;
82    FrameLoadRequest m_request;
83    bool m_allowPopups;
84};
85
86class PluginView::Stream : public RefCounted<PluginView::Stream>, NetscapePlugInStreamLoaderClient {
87public:
88    static PassRefPtr<Stream> create(PluginView* pluginView, uint64_t streamID, const ResourceRequest& request)
89    {
90        return adoptRef(new Stream(pluginView, streamID, request));
91    }
92    ~Stream();
93
94    void start();
95    void cancel();
96
97    uint64_t streamID() const { return m_streamID; }
98
99private:
100    Stream(PluginView* pluginView, uint64_t streamID, const ResourceRequest& request)
101        : m_pluginView(pluginView)
102        , m_streamID(streamID)
103        , m_request(request)
104        , m_streamWasCancelled(false)
105    {
106    }
107
108    // NetscapePluginStreamLoaderClient
109    virtual void didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse&);
110    virtual void didReceiveData(NetscapePlugInStreamLoader*, const char*, int);
111    virtual void didFail(NetscapePlugInStreamLoader*, const ResourceError&);
112    virtual void didFinishLoading(NetscapePlugInStreamLoader*);
113
114    PluginView* m_pluginView;
115    uint64_t m_streamID;
116    const ResourceRequest m_request;
117
118    // True if the stream was explicitly cancelled by calling cancel().
119    // (As opposed to being cancelled by the user hitting the stop button for example.
120    bool m_streamWasCancelled;
121
122    RefPtr<NetscapePlugInStreamLoader> m_loader;
123};
124
125PluginView::Stream::~Stream()
126{
127    ASSERT(!m_pluginView);
128}
129
130void PluginView::Stream::start()
131{
132    ASSERT(!m_loader);
133
134    Frame* frame = m_pluginView->m_pluginElement->document()->frame();
135    ASSERT(frame);
136
137    m_loader = resourceLoadScheduler()->schedulePluginStreamLoad(frame, this, m_request);
138}
139
140void PluginView::Stream::cancel()
141{
142    ASSERT(m_loader);
143
144    m_streamWasCancelled = true;
145    m_loader->cancel(m_loader->cancelledError());
146    m_loader = 0;
147}
148
149static String buildHTTPHeaders(const ResourceResponse& response, long long& expectedContentLength)
150{
151    if (!response.isHTTP())
152        return String();
153
154    Vector<UChar> stringBuilder;
155    String separator(": ");
156
157    String statusLine = String::format("HTTP %d ", response.httpStatusCode());
158    stringBuilder.append(statusLine.characters(), statusLine.length());
159    stringBuilder.append(response.httpStatusText().characters(), response.httpStatusText().length());
160    stringBuilder.append('\n');
161
162    HTTPHeaderMap::const_iterator end = response.httpHeaderFields().end();
163    for (HTTPHeaderMap::const_iterator it = response.httpHeaderFields().begin(); it != end; ++it) {
164        stringBuilder.append(it->first.characters(), it->first.length());
165        stringBuilder.append(separator.characters(), separator.length());
166        stringBuilder.append(it->second.characters(), it->second.length());
167        stringBuilder.append('\n');
168    }
169
170    String headers = String::adopt(stringBuilder);
171
172    // If the content is encoded (most likely compressed), then don't send its length to the plugin,
173    // which is only interested in the decoded length, not yet known at the moment.
174    // <rdar://problem/4470599> tracks a request for -[NSURLResponse expectedContentLength] to incorporate this logic.
175    String contentEncoding = response.httpHeaderField("Content-Encoding");
176    if (!contentEncoding.isNull() && contentEncoding != "identity")
177        expectedContentLength = -1;
178
179    return headers;
180}
181
182void PluginView::Stream::didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse& response)
183{
184    // Compute the stream related data from the resource response.
185    const KURL& responseURL = response.url();
186    const String& mimeType = response.mimeType();
187    long long expectedContentLength = response.expectedContentLength();
188
189    String headers = buildHTTPHeaders(response, expectedContentLength);
190
191    uint32_t streamLength = 0;
192    if (expectedContentLength > 0)
193        streamLength = expectedContentLength;
194
195    m_pluginView->m_plugin->streamDidReceiveResponse(m_streamID, responseURL, streamLength, response.lastModifiedDate(), mimeType, headers);
196}
197
198void PluginView::Stream::didReceiveData(NetscapePlugInStreamLoader*, const char* bytes, int length)
199{
200    m_pluginView->m_plugin->streamDidReceiveData(m_streamID, bytes, length);
201}
202
203void PluginView::Stream::didFail(NetscapePlugInStreamLoader*, const ResourceError& error)
204{
205    // Calling streamDidFail could cause us to be deleted, so we hold on to a reference here.
206    RefPtr<Stream> protect(this);
207
208    // We only want to call streamDidFail if the stream was not explicitly cancelled by the plug-in.
209    if (!m_streamWasCancelled)
210        m_pluginView->m_plugin->streamDidFail(m_streamID, error.isCancellation());
211
212    m_pluginView->removeStream(this);
213    m_pluginView = 0;
214}
215
216void PluginView::Stream::didFinishLoading(NetscapePlugInStreamLoader*)
217{
218    // Calling streamDidFinishLoading could cause us to be deleted, so we hold on to a reference here.
219    RefPtr<Stream> protectStream(this);
220
221    // Protect the plug-in while we're calling into it.
222    NPRuntimeObjectMap::PluginProtector pluginProtector(&m_pluginView->m_npRuntimeObjectMap);
223    m_pluginView->m_plugin->streamDidFinishLoading(m_streamID);
224
225    m_pluginView->removeStream(this);
226    m_pluginView = 0;
227}
228
229static inline WebPage* webPage(HTMLPlugInElement* pluginElement)
230{
231    Frame* frame = pluginElement->document()->frame();
232    ASSERT(frame);
233
234    WebPage* webPage = static_cast<WebFrameLoaderClient*>(frame->loader()->client())->webFrame()->page();
235    ASSERT(webPage);
236
237    return webPage;
238}
239
240PassRefPtr<PluginView> PluginView::create(PassRefPtr<HTMLPlugInElement> pluginElement, PassRefPtr<Plugin> plugin, const Plugin::Parameters& parameters)
241{
242    return adoptRef(new PluginView(pluginElement, plugin, parameters));
243}
244
245PluginView::PluginView(PassRefPtr<HTMLPlugInElement> pluginElement, PassRefPtr<Plugin> plugin, const Plugin::Parameters& parameters)
246    : PluginViewBase(0)
247    , m_pluginElement(pluginElement)
248    , m_plugin(plugin)
249    , m_webPage(webPage(m_pluginElement.get()))
250    , m_parameters(parameters)
251    , m_isInitialized(false)
252    , m_isWaitingUntilMediaCanStart(false)
253    , m_isBeingDestroyed(false)
254    , m_pendingURLRequestsTimer(RunLoop::main(), this, &PluginView::pendingURLRequestsTimerFired)
255    , m_npRuntimeObjectMap(this)
256    , m_manualStreamState(StreamStateInitial)
257{
258#if PLATFORM(MAC)
259    m_webPage->addPluginView(this);
260#endif
261}
262
263PluginView::~PluginView()
264{
265#if PLATFORM(MAC)
266    m_webPage->removePluginView(this);
267#endif
268
269    ASSERT(!m_isBeingDestroyed);
270
271    if (m_isWaitingUntilMediaCanStart)
272        m_pluginElement->document()->removeMediaCanStartListener(this);
273
274    // Cancel all pending frame loads.
275    FrameLoadMap::iterator end = m_pendingFrameLoads.end();
276    for (FrameLoadMap::iterator it = m_pendingFrameLoads.begin(), end = m_pendingFrameLoads.end(); it != end; ++it)
277        it->first->setLoadListener(0);
278
279    if (m_plugin && m_isInitialized) {
280        m_isBeingDestroyed = true;
281        m_plugin->destroy();
282        m_isBeingDestroyed = false;
283    }
284
285    // Invalidate the object map.
286    m_npRuntimeObjectMap.invalidate();
287
288    cancelAllStreams();
289
290    // Null out the plug-in element explicitly so we'll crash earlier if we try to use
291    // the plug-in view after it's been destroyed.
292    m_pluginElement = nullptr;
293}
294
295Frame* PluginView::frame()
296{
297    return m_pluginElement->document()->frame();
298}
299
300void PluginView::manualLoadDidReceiveResponse(const ResourceResponse& response)
301{
302    // The plug-in can be null here if it failed to initialize.
303    if (!m_plugin)
304        return;
305
306    if (!m_isInitialized) {
307        ASSERT(m_manualStreamState == StreamStateInitial);
308        m_manualStreamState = StreamStateHasReceivedResponse;
309        m_manualStreamResponse = response;
310        return;
311    }
312
313    // Compute the stream related data from the resource response.
314    const KURL& responseURL = response.url();
315    const String& mimeType = response.mimeType();
316    long long expectedContentLength = response.expectedContentLength();
317
318    String headers = buildHTTPHeaders(response, expectedContentLength);
319
320    uint32_t streamLength = 0;
321    if (expectedContentLength > 0)
322        streamLength = expectedContentLength;
323
324    m_plugin->manualStreamDidReceiveResponse(responseURL, streamLength, response.lastModifiedDate(), mimeType, headers);
325}
326
327void PluginView::manualLoadDidReceiveData(const char* bytes, int length)
328{
329    // The plug-in can be null here if it failed to initialize.
330    if (!m_plugin)
331        return;
332
333    if (!m_isInitialized) {
334        ASSERT(m_manualStreamState == StreamStateHasReceivedResponse);
335        if (!m_manualStreamData)
336            m_manualStreamData = SharedBuffer::create();
337
338        m_manualStreamData->append(bytes, length);
339        return;
340    }
341
342    m_plugin->manualStreamDidReceiveData(bytes, length);
343}
344
345void PluginView::manualLoadDidFinishLoading()
346{
347    // The plug-in can be null here if it failed to initialize.
348    if (!m_plugin)
349        return;
350
351    if (!m_isInitialized) {
352        ASSERT(m_manualStreamState == StreamStateHasReceivedResponse);
353        m_manualStreamState = StreamStateFinished;
354        return;
355    }
356
357    m_plugin->manualStreamDidFinishLoading();
358}
359
360void PluginView::manualLoadDidFail(const ResourceError& error)
361{
362    // The plug-in can be null here if it failed to initialize.
363    if (!m_plugin)
364        return;
365
366    if (!m_isInitialized) {
367        m_manualStreamState = StreamStateFinished;
368        m_manualStreamError = error;
369        m_manualStreamData = nullptr;
370        return;
371    }
372
373    m_plugin->manualStreamDidFail(error.isCancellation());
374}
375
376#if PLATFORM(MAC)
377void PluginView::setWindowIsVisible(bool windowIsVisible)
378{
379    if (!m_plugin)
380        return;
381
382    // FIXME: Implement.
383}
384
385void PluginView::setWindowIsFocused(bool windowIsFocused)
386{
387    if (!m_isInitialized || !m_plugin)
388        return;
389
390    m_plugin->windowFocusChanged(windowIsFocused);
391}
392
393void PluginView::windowAndViewFramesChanged(const IntRect& windowFrameInScreenCoordinates, const IntRect& viewFrameInWindowCoordinates)
394{
395    if (!m_isInitialized || !m_plugin)
396        return;
397
398    m_plugin->windowAndViewFramesChanged(windowFrameInScreenCoordinates, viewFrameInWindowCoordinates);
399}
400
401bool PluginView::sendComplexTextInput(uint64_t pluginComplexTextInputIdentifier, const String& textInput)
402{
403    if (!m_plugin)
404        return false;
405
406    if (m_plugin->pluginComplexTextInputIdentifier() != pluginComplexTextInputIdentifier)
407        return false;
408
409    m_plugin->sendComplexTextInput(textInput);
410    return true;
411}
412
413#endif
414
415void PluginView::initializePlugin()
416{
417    if (m_isInitialized)
418        return;
419
420    if (!m_plugin) {
421        // We've already tried and failed to initialize the plug-in.
422        return;
423    }
424
425    if (Frame* frame = m_pluginElement->document()->frame()) {
426        if (Page* page = frame->page()) {
427
428            // We shouldn't initialize the plug-in right now, add a listener.
429            if (!page->canStartMedia()) {
430                if (m_isWaitingUntilMediaCanStart)
431                    return;
432
433                m_isWaitingUntilMediaCanStart = true;
434                m_pluginElement->document()->addMediaCanStartListener(this);
435                return;
436            }
437        }
438    }
439
440    if (!m_plugin->initialize(this, m_parameters)) {
441        // We failed to initialize the plug-in.
442        m_plugin = 0;
443
444        return;
445    }
446
447    m_isInitialized = true;
448
449    viewGeometryDidChange();
450
451    redeliverManualStream();
452
453#if PLATFORM(MAC)
454    if (m_plugin->pluginLayer()) {
455        if (frame()) {
456            frame()->view()->enterCompositingMode();
457            m_pluginElement->setNeedsStyleRecalc(SyntheticStyleChange);
458        }
459    }
460
461    windowAndViewFramesChanged(m_webPage->windowFrameInScreenCoordinates(), m_webPage->viewFrameInWindowCoordinates());
462    setWindowIsVisible(m_webPage->windowIsVisible());
463    setWindowIsFocused(m_webPage->windowIsFocused());
464#endif
465}
466
467#if PLATFORM(MAC)
468PlatformLayer* PluginView::platformLayer() const
469{
470    // The plug-in can be null here if it failed to initialize.
471    if (!m_isInitialized || !m_plugin)
472        return 0;
473
474    return m_plugin->pluginLayer();
475}
476#endif
477
478JSObject* PluginView::scriptObject(JSGlobalObject* globalObject)
479{
480    // The plug-in can be null here if it failed to initialize.
481    if (!m_isInitialized || !m_plugin)
482        return 0;
483
484    NPObject* scriptableNPObject = m_plugin->pluginScriptableNPObject();
485    if (!scriptableNPObject)
486        return 0;
487
488    JSObject* jsObject = m_npRuntimeObjectMap.getOrCreateJSObject(globalObject, scriptableNPObject);
489    releaseNPObject(scriptableNPObject);
490
491    return jsObject;
492}
493
494void PluginView::privateBrowsingStateChanged(bool privateBrowsingEnabled)
495{
496    // The plug-in can be null here if it failed to initialize.
497    if (!m_isInitialized || !m_plugin)
498        return;
499
500    m_plugin->privateBrowsingStateChanged(privateBrowsingEnabled);
501}
502
503void PluginView::setFrameRect(const WebCore::IntRect& rect)
504{
505    Widget::setFrameRect(rect);
506    viewGeometryDidChange();
507}
508
509void PluginView::setBoundsSize(const WebCore::IntSize& size)
510{
511    Widget::setBoundsSize(size);
512    m_boundsSize = size;
513    viewGeometryDidChange();
514}
515
516void PluginView::paint(GraphicsContext* context, const IntRect& dirtyRect)
517{
518    if (context->paintingDisabled() || !m_plugin || !m_isInitialized)
519        return;
520
521    IntRect dirtyRectInWindowCoordinates = parent()->contentsToWindow(dirtyRect);
522    IntRect paintRectInWindowCoordinates = intersection(dirtyRectInWindowCoordinates, clipRectInWindowCoordinates());
523    if (paintRectInWindowCoordinates.isEmpty())
524        return;
525
526    if (m_snapshot)
527        m_snapshot->paint(*context, frameRect().location(), m_snapshot->bounds());
528    else {
529        // The plugin is given a frame rect which is parent()->contentsToWindow(frameRect()),
530        // and un-translates by the its origin when painting. The current CTM reflects
531        // this widget's frame is its parent (the document), so we have to offset the CTM by
532        // the document's window coordinates.
533        IntPoint documentOriginInWindowCoordinates = parent()->contentsToWindow(IntPoint());
534        context->save();
535        context->translate(-documentOriginInWindowCoordinates.x(), -documentOriginInWindowCoordinates.y());
536        m_plugin->paint(context, paintRectInWindowCoordinates);
537        context->restore();
538    }
539}
540
541void PluginView::frameRectsChanged()
542{
543    Widget::frameRectsChanged();
544    viewGeometryDidChange();
545}
546
547void PluginView::setParent(ScrollView* scrollView)
548{
549    Widget::setParent(scrollView);
550
551    if (scrollView)
552        initializePlugin();
553}
554
555void PluginView::handleEvent(Event* event)
556{
557    if (!m_isInitialized || !m_plugin)
558        return;
559
560    const WebEvent* currentEvent = WebPage::currentEvent();
561    if (!currentEvent)
562        return;
563
564    bool didHandleEvent = false;
565
566    if ((event->type() == eventNames().mousemoveEvent && currentEvent->type() == WebEvent::MouseMove)
567        || (event->type() == eventNames().mousedownEvent && currentEvent->type() == WebEvent::MouseDown)
568        || (event->type() == eventNames().mouseupEvent && currentEvent->type() == WebEvent::MouseUp)) {
569        // We have a mouse event.
570        if (currentEvent->type() == WebEvent::MouseDown)
571            focusPluginElement();
572
573        didHandleEvent = m_plugin->handleMouseEvent(static_cast<const WebMouseEvent&>(*currentEvent));
574    } else if (event->type() == eventNames().mousewheelEvent && currentEvent->type() == WebEvent::Wheel) {
575        // We have a wheel event.
576        didHandleEvent = m_plugin->handleWheelEvent(static_cast<const WebWheelEvent&>(*currentEvent));
577    } else if (event->type() == eventNames().mouseoverEvent && currentEvent->type() == WebEvent::MouseMove) {
578        // We have a mouse enter event.
579        didHandleEvent = m_plugin->handleMouseEnterEvent(static_cast<const WebMouseEvent&>(*currentEvent));
580    } else if (event->type() == eventNames().mouseoutEvent && currentEvent->type() == WebEvent::MouseMove) {
581        // We have a mouse leave event.
582        didHandleEvent = m_plugin->handleMouseLeaveEvent(static_cast<const WebMouseEvent&>(*currentEvent));
583    } else if ((event->type() == eventNames().keydownEvent && currentEvent->type() == WebEvent::KeyDown)
584               || (event->type() == eventNames().keyupEvent && currentEvent->type() == WebEvent::KeyUp)) {
585        // We have a keyboard event.
586        didHandleEvent = m_plugin->handleKeyboardEvent(static_cast<const WebKeyboardEvent&>(*currentEvent));
587    }
588
589    if (didHandleEvent)
590        event->setDefaultHandled();
591}
592
593void PluginView::notifyWidget(WidgetNotification notification)
594{
595    switch (notification) {
596    case WillPaintFlattened:
597        if (m_plugin && m_isInitialized)
598            m_snapshot = m_plugin->snapshot();
599        break;
600    case DidPaintFlattened:
601        m_snapshot = nullptr;
602        break;
603    }
604}
605
606void PluginView::viewGeometryDidChange()
607{
608    if (!m_isInitialized || !m_plugin || !parent())
609        return;
610
611    // Get the frame rect in window coordinates.
612    IntRect frameRectInWindowCoordinates = parent()->contentsToWindow(frameRect());
613    frameRectInWindowCoordinates.setSize(m_boundsSize);
614    m_plugin->geometryDidChange(frameRectInWindowCoordinates, clipRectInWindowCoordinates());
615}
616
617IntRect PluginView::clipRectInWindowCoordinates() const
618{
619    ASSERT(parent());
620
621    // Get the frame rect in window coordinates.
622    IntRect frameRectInWindowCoordinates = parent()->contentsToWindow(frameRect());
623    frameRectInWindowCoordinates.setSize(m_boundsSize);
624
625    // Get the window clip rect for the enclosing layer (in window coordinates).
626    RenderLayer* layer = m_pluginElement->renderer()->enclosingLayer();
627    FrameView* parentView = m_pluginElement->document()->frame()->view();
628    IntRect windowClipRect = parentView->windowClipRectForLayer(layer, true);
629
630    // Intersect the two rects to get the view clip rect in window coordinates.
631    return intersection(frameRectInWindowCoordinates, windowClipRect);
632}
633
634void PluginView::focusPluginElement()
635{
636    ASSERT(frame());
637
638    if (Page* page = frame()->page())
639        page->focusController()->setFocusedFrame(frame());
640    frame()->document()->setFocusedNode(m_pluginElement);
641}
642
643void PluginView::pendingURLRequestsTimerFired()
644{
645    ASSERT(!m_pendingURLRequests.isEmpty());
646
647    RefPtr<URLRequest> urlRequest = m_pendingURLRequests.takeFirst();
648
649    // If there are more requests to perform, reschedule the timer.
650    if (!m_pendingURLRequests.isEmpty())
651        m_pendingURLRequestsTimer.startOneShot(0);
652
653    performURLRequest(urlRequest.get());
654}
655
656void PluginView::performURLRequest(URLRequest* request)
657{
658    // First, check if this is a javascript: url.
659    if (protocolIsJavaScript(request->request().url())) {
660        performJavaScriptURLRequest(request);
661        return;
662    }
663
664    if (!request->target().isNull()) {
665        performFrameLoadURLRequest(request);
666        return;
667    }
668
669    // This request is to load a URL and create a stream.
670    RefPtr<Stream> stream = PluginView::Stream::create(this, request->requestID(), request->request());
671    addStream(stream.get());
672    stream->start();
673}
674
675void PluginView::performFrameLoadURLRequest(URLRequest* request)
676{
677    ASSERT(!request->target().isNull());
678
679    Frame* frame = m_pluginElement->document()->frame();
680    if (!frame)
681        return;
682
683    if (!m_pluginElement->document()->securityOrigin()->canDisplay(request->request().url())) {
684        // We can't load the request, send back a reply to the plug-in.
685        m_plugin->frameDidFail(request->requestID(), false);
686        return;
687    }
688
689    // First, try to find a target frame.
690    Frame* targetFrame = frame->loader()->findFrameForNavigation(request->target());
691    if (!targetFrame) {
692        // We did not find a target frame. Ask our frame to load the page. This may or may not create a popup window.
693        frame->loader()->load(request->request(), request->target(), false);
694
695        // FIXME: We don't know whether the window was successfully created here so we just assume that it worked.
696        // It's better than not telling the plug-in anything.
697        m_plugin->frameDidFinishLoading(request->requestID());
698        return;
699    }
700
701    // Now ask the frame to load the request.
702    targetFrame->loader()->load(request->request(), false);
703
704    WebFrame* targetWebFrame = static_cast<WebFrameLoaderClient*>(targetFrame->loader()->client())->webFrame();
705    if (WebFrame::LoadListener* loadListener = targetWebFrame->loadListener()) {
706        // Check if another plug-in view or even this view is waiting for the frame to load.
707        // If it is, tell it that the load was cancelled because it will be anyway.
708        loadListener->didFailLoad(targetWebFrame, true);
709    }
710
711    m_pendingFrameLoads.set(targetWebFrame, request);
712    targetWebFrame->setLoadListener(this);
713}
714
715void PluginView::performJavaScriptURLRequest(URLRequest* request)
716{
717    ASSERT(protocolIsJavaScript(request->request().url()));
718
719    RefPtr<Frame> frame = m_pluginElement->document()->frame();
720    if (!frame)
721        return;
722
723    String jsString = decodeURLEscapeSequences(request->request().url().string().substring(sizeof("javascript:") - 1));
724
725    if (!request->target().isNull()) {
726        // For security reasons, only allow JS requests to be made on the frame that contains the plug-in.
727        if (frame->tree()->find(request->target()) != frame) {
728            // Let the plug-in know that its frame load failed.
729            m_plugin->frameDidFail(request->requestID(), false);
730            return;
731        }
732    }
733
734    // Evaluate the JavaScript code. Note that running JavaScript here could cause the plug-in to be destroyed, so we
735    // grab references to the plug-in here.
736    RefPtr<Plugin> plugin = m_plugin;
737
738    bool oldAllowPopups = frame->script()->allowPopupsFromPlugin();
739    frame->script()->setAllowPopupsFromPlugin(request->allowPopups());
740
741    ScriptValue result = frame->script()->executeScript(jsString);
742
743    frame->script()->setAllowPopupsFromPlugin(oldAllowPopups);
744
745    // Check if evaluating the JavaScript destroyed the plug-in.
746    if (!plugin->controller())
747        return;
748
749    ScriptState* scriptState = frame->script()->globalObject(pluginWorld())->globalExec();
750    String resultString;
751    result.getString(scriptState, resultString);
752
753    if (!request->target().isNull()) {
754        // Just send back whether the frame load succeeded or not.
755        if (resultString.isNull())
756            m_plugin->frameDidFail(request->requestID(), false);
757        else
758            m_plugin->frameDidFinishLoading(request->requestID());
759        return;
760    }
761
762    // Send the result back to the plug-in.
763    plugin->didEvaluateJavaScript(request->requestID(), decodeURLEscapeSequences(request->request().url()), resultString);
764}
765
766void PluginView::addStream(Stream* stream)
767{
768    ASSERT(!m_streams.contains(stream->streamID()));
769    m_streams.set(stream->streamID(), stream);
770}
771
772void PluginView::removeStream(Stream* stream)
773{
774    ASSERT(m_streams.get(stream->streamID()) == stream);
775
776    m_streams.remove(stream->streamID());
777}
778
779void PluginView::cancelAllStreams()
780{
781    Vector<RefPtr<Stream> > streams;
782    copyValuesToVector(m_streams, streams);
783
784    for (size_t i = 0; i < streams.size(); ++i)
785        streams[i]->cancel();
786
787    // Cancelling a stream removes it from the m_streams map, so if we cancel all streams the map should be empty.
788    ASSERT(m_streams.isEmpty());
789}
790
791void PluginView::redeliverManualStream()
792{
793    if (m_manualStreamState == StreamStateInitial) {
794        // Nothing to do.
795        return;
796    }
797
798    if (m_manualStreamState == StreamStateFailed) {
799        manualLoadDidFail(m_manualStreamError);
800        return;
801    }
802
803    // Deliver the response.
804    manualLoadDidReceiveResponse(m_manualStreamResponse);
805
806    // Deliver the data.
807    if (m_manualStreamData) {
808        const char* data;
809        unsigned position = 0;
810
811        while (unsigned length = m_manualStreamData->getSomeData(data, position)) {
812            manualLoadDidReceiveData(data, length);
813            position += length;
814        }
815
816        m_manualStreamData = nullptr;
817    }
818
819    if (m_manualStreamState == StreamStateFinished)
820        manualLoadDidFinishLoading();
821}
822
823void PluginView::invalidateRect(const IntRect& dirtyRect)
824{
825    if (!parent() || !m_plugin || !m_isInitialized)
826        return;
827
828#if PLATFORM(MAC)
829    if (m_plugin->pluginLayer())
830        return;
831#endif
832
833    IntRect dirtyRectInWindowCoordinates = convertToContainingWindow(dirtyRect);
834
835    parent()->hostWindow()->invalidateContentsAndWindow(intersection(dirtyRectInWindowCoordinates, clipRectInWindowCoordinates()), false);
836}
837
838void PluginView::setFocus(bool hasFocus)
839{
840    Widget::setFocus(hasFocus);
841
842    if (!m_isInitialized || !m_plugin)
843        return;
844
845    m_plugin->setFocus(hasFocus);
846}
847
848void PluginView::mediaCanStart()
849{
850    ASSERT(m_isWaitingUntilMediaCanStart);
851    m_isWaitingUntilMediaCanStart = false;
852
853    initializePlugin();
854}
855
856void PluginView::invalidate(const IntRect& dirtyRect)
857{
858    invalidateRect(dirtyRect);
859}
860
861String PluginView::userAgent()
862{
863    Frame* frame = m_pluginElement->document()->frame();
864    if (!frame)
865        return String();
866
867    return frame->loader()->client()->userAgent(KURL());
868}
869
870void PluginView::loadURL(uint64_t requestID, const String& method, const String& urlString, const String& target,
871                         const HTTPHeaderMap& headerFields, const Vector<uint8_t>& httpBody, bool allowPopups)
872{
873    FrameLoadRequest frameLoadRequest(m_pluginElement->document()->securityOrigin());
874    frameLoadRequest.resourceRequest().setHTTPMethod(method);
875    frameLoadRequest.resourceRequest().setURL(m_pluginElement->document()->completeURL(urlString));
876    frameLoadRequest.resourceRequest().addHTTPHeaderFields(headerFields);
877    frameLoadRequest.resourceRequest().setHTTPBody(FormData::create(httpBody.data(), httpBody.size()));
878    frameLoadRequest.setFrameName(target);
879
880    m_pendingURLRequests.append(URLRequest::create(requestID, frameLoadRequest, allowPopups));
881    m_pendingURLRequestsTimer.startOneShot(0);
882}
883
884void PluginView::cancelStreamLoad(uint64_t streamID)
885{
886    // Keep a reference to the stream. Stream::cancel might remove the stream from the map, and thus
887    // releasing its last reference.
888    RefPtr<Stream> stream = m_streams.get(streamID).get();
889    if (!stream)
890        return;
891
892    // Cancelling the stream here will remove it from the map.
893    stream->cancel();
894    ASSERT(!m_streams.contains(streamID));
895}
896
897void PluginView::cancelManualStreamLoad()
898{
899    if (!frame())
900        return;
901
902    DocumentLoader* documentLoader = frame()->loader()->activeDocumentLoader();
903    ASSERT(documentLoader);
904
905    if (documentLoader->isLoadingMainResource())
906        documentLoader->cancelMainResourceLoad(frame()->loader()->cancelledError(m_parameters.url));
907}
908
909NPObject* PluginView::windowScriptNPObject()
910{
911    if (!frame())
912        return 0;
913
914    // FIXME: Handle JavaScript being disabled.
915    ASSERT(frame()->script()->canExecuteScripts(NotAboutToExecuteScript));
916
917    return m_npRuntimeObjectMap.getOrCreateNPObject(*pluginWorld()->globalData(), frame()->script()->windowShell(pluginWorld())->window());
918}
919
920NPObject* PluginView::pluginElementNPObject()
921{
922    if (!frame())
923        return 0;
924
925    // FIXME: Handle JavaScript being disabled.
926    JSObject* object = frame()->script()->jsObjectForPluginElement(m_pluginElement.get());
927    ASSERT(object);
928
929    return m_npRuntimeObjectMap.getOrCreateNPObject(*pluginWorld()->globalData(), object);
930}
931
932bool PluginView::evaluate(NPObject* npObject, const String& scriptString, NPVariant* result, bool allowPopups)
933{
934    RefPtr<Frame> frame = m_pluginElement->document()->frame();
935    if (!frame)
936        return false;
937
938    bool oldAllowPopups = frame->script()->allowPopupsFromPlugin();
939    frame->script()->setAllowPopupsFromPlugin(allowPopups);
940
941    // Calling evaluate will run JavaScript that can potentially remove the plug-in element, so we need to
942    // protect the plug-in view from destruction.
943    NPRuntimeObjectMap::PluginProtector pluginProtector(&m_npRuntimeObjectMap);
944
945    bool returnValue = m_npRuntimeObjectMap.evaluate(npObject, scriptString, result);
946
947    frame->script()->setAllowPopupsFromPlugin(oldAllowPopups);
948
949    return returnValue;
950}
951
952void PluginView::setStatusbarText(const String& statusbarText)
953{
954    if (!frame())
955        return;
956
957    Page* page = frame()->page();
958    if (!page)
959        return;
960
961    page->chrome()->setStatusbarText(frame(), statusbarText);
962}
963
964bool PluginView::isAcceleratedCompositingEnabled()
965{
966    if (!frame())
967        return false;
968
969    Settings* settings = frame()->settings();
970    if (!settings)
971        return false;
972
973    return settings->acceleratedCompositingEnabled();
974}
975
976void PluginView::pluginProcessCrashed()
977{
978    if (!m_pluginElement->renderer())
979        return;
980
981    // FIXME: The renderer could also be a RenderApplet, we should handle that.
982    if (!m_pluginElement->renderer()->isEmbeddedObject())
983        return;
984
985    RenderEmbeddedObject* renderer = toRenderEmbeddedObject(m_pluginElement->renderer());
986    renderer->setShowsCrashedPluginIndicator();
987
988    invalidateRect(frameRect());
989}
990
991#if PLATFORM(WIN)
992HWND PluginView::nativeParentWindow()
993{
994    return m_webPage->nativeWindow();
995}
996#endif
997
998#if PLATFORM(MAC)
999void PluginView::setComplexTextInputEnabled(bool complexTextInputEnabled)
1000{
1001    m_webPage->send(Messages::WebPageProxy::SetComplexTextInputEnabled(m_plugin->pluginComplexTextInputIdentifier(), complexTextInputEnabled));
1002}
1003
1004mach_port_t PluginView::compositingRenderServerPort()
1005{
1006    return WebProcess::shared().compositingRenderServerPort();
1007}
1008
1009#endif
1010
1011String PluginView::proxiesForURL(const String& urlString)
1012{
1013    const FrameLoader* frameLoader = frame() ? frame()->loader() : 0;
1014    const NetworkingContext* context = frameLoader ? frameLoader->networkingContext() : 0;
1015    Vector<ProxyServer> proxyServers = proxyServersForURL(KURL(KURL(), urlString), context);
1016    return toString(proxyServers);
1017}
1018
1019String PluginView::cookiesForURL(const String& urlString)
1020{
1021    return cookies(m_pluginElement->document(), KURL(KURL(), urlString));
1022}
1023
1024void PluginView::setCookiesForURL(const String& urlString, const String& cookieString)
1025{
1026    setCookies(m_pluginElement->document(), KURL(KURL(), urlString), cookieString);
1027}
1028
1029bool PluginView::isPrivateBrowsingEnabled()
1030{
1031    // If we can't get the real setting, we'll assume that private browsing is enabled.
1032    if (!frame())
1033        return true;
1034
1035    Settings* settings = frame()->settings();
1036    if (!settings)
1037        return true;
1038
1039    return settings->privateBrowsingEnabled();
1040}
1041
1042void PluginView::protectPluginFromDestruction()
1043{
1044    if (!m_isBeingDestroyed)
1045        ref();
1046}
1047
1048void PluginView::unprotectPluginFromDestruction()
1049{
1050    if (!m_isBeingDestroyed)
1051        deref();
1052}
1053
1054void PluginView::didFinishLoad(WebFrame* webFrame)
1055{
1056    RefPtr<URLRequest> request = m_pendingFrameLoads.take(webFrame);
1057    ASSERT(request);
1058    webFrame->setLoadListener(0);
1059
1060    m_plugin->frameDidFinishLoading(request->requestID());
1061}
1062
1063void PluginView::didFailLoad(WebFrame* webFrame, bool wasCancelled)
1064{
1065    RefPtr<URLRequest> request = m_pendingFrameLoads.take(webFrame);
1066    ASSERT(request);
1067    webFrame->setLoadListener(0);
1068
1069    m_plugin->frameDidFail(request->requestID(), wasCancelled);
1070}
1071
1072} // namespace WebKit
1073