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
56 using namespace JSC;
57 using namespace WebCore;
58
59 namespace WebKit {
60
61 class PluginView::URLRequest : public RefCounted<URLRequest> {
62 public:
create(uint64_t requestID,const FrameLoadRequest & request,bool allowPopups)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
requestID() const68 uint64_t requestID() const { return m_requestID; }
target() const69 const String& target() const { return m_request.frameName(); }
request() const70 const ResourceRequest & request() const { return m_request.resourceRequest(); }
allowPopups() const71 bool allowPopups() const { return m_allowPopups; }
72
73 private:
URLRequest(uint64_t requestID,const FrameLoadRequest & request,bool allowPopups)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
86 class PluginView::Stream : public RefCounted<PluginView::Stream>, NetscapePlugInStreamLoaderClient {
87 public:
create(PluginView * pluginView,uint64_t streamID,const ResourceRequest & request)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
streamID() const97 uint64_t streamID() const { return m_streamID; }
98
99 private:
Stream(PluginView * pluginView,uint64_t streamID,const ResourceRequest & request)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
~Stream()125 PluginView::Stream::~Stream()
126 {
127 ASSERT(!m_pluginView);
128 }
129
start()130 void 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
cancel()140 void 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
buildHTTPHeaders(const ResourceResponse & response,long long & expectedContentLength)149 static 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
didReceiveResponse(NetscapePlugInStreamLoader *,const ResourceResponse & response)182 void 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
didReceiveData(NetscapePlugInStreamLoader *,const char * bytes,int length)198 void PluginView::Stream::didReceiveData(NetscapePlugInStreamLoader*, const char* bytes, int length)
199 {
200 m_pluginView->m_plugin->streamDidReceiveData(m_streamID, bytes, length);
201 }
202
didFail(NetscapePlugInStreamLoader *,const ResourceError & error)203 void 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
didFinishLoading(NetscapePlugInStreamLoader *)216 void 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
webPage(HTMLPlugInElement * pluginElement)229 static 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
create(PassRefPtr<HTMLPlugInElement> pluginElement,PassRefPtr<Plugin> plugin,const Plugin::Parameters & parameters)240 PassRefPtr<PluginView> PluginView::create(PassRefPtr<HTMLPlugInElement> pluginElement, PassRefPtr<Plugin> plugin, const Plugin::Parameters& parameters)
241 {
242 return adoptRef(new PluginView(pluginElement, plugin, parameters));
243 }
244
PluginView(PassRefPtr<HTMLPlugInElement> pluginElement,PassRefPtr<Plugin> plugin,const Plugin::Parameters & parameters)245 PluginView::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
~PluginView()263 PluginView::~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
frame()295 Frame* PluginView::frame()
296 {
297 return m_pluginElement->document()->frame();
298 }
299
manualLoadDidReceiveResponse(const ResourceResponse & response)300 void 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
manualLoadDidReceiveData(const char * bytes,int length)327 void 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
manualLoadDidFinishLoading()345 void 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
manualLoadDidFail(const ResourceError & error)360 void 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)
setWindowIsVisible(bool windowIsVisible)377 void PluginView::setWindowIsVisible(bool windowIsVisible)
378 {
379 if (!m_plugin)
380 return;
381
382 // FIXME: Implement.
383 }
384
setWindowIsFocused(bool windowIsFocused)385 void PluginView::setWindowIsFocused(bool windowIsFocused)
386 {
387 if (!m_isInitialized || !m_plugin)
388 return;
389
390 m_plugin->windowFocusChanged(windowIsFocused);
391 }
392
windowAndViewFramesChanged(const IntRect & windowFrameInScreenCoordinates,const IntRect & viewFrameInWindowCoordinates)393 void 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
sendComplexTextInput(uint64_t pluginComplexTextInputIdentifier,const String & textInput)401 bool 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
initializePlugin()415 void 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)
platformLayer() const468 PlatformLayer* 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
scriptObject(JSGlobalObject * globalObject)478 JSObject* 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
privateBrowsingStateChanged(bool privateBrowsingEnabled)494 void 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
setFrameRect(const WebCore::IntRect & rect)503 void PluginView::setFrameRect(const WebCore::IntRect& rect)
504 {
505 Widget::setFrameRect(rect);
506 viewGeometryDidChange();
507 }
508
setBoundsSize(const WebCore::IntSize & size)509 void PluginView::setBoundsSize(const WebCore::IntSize& size)
510 {
511 Widget::setBoundsSize(size);
512 m_boundsSize = size;
513 viewGeometryDidChange();
514 }
515
paint(GraphicsContext * context,const IntRect & dirtyRect)516 void 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
frameRectsChanged()541 void PluginView::frameRectsChanged()
542 {
543 Widget::frameRectsChanged();
544 viewGeometryDidChange();
545 }
546
setParent(ScrollView * scrollView)547 void PluginView::setParent(ScrollView* scrollView)
548 {
549 Widget::setParent(scrollView);
550
551 if (scrollView)
552 initializePlugin();
553 }
554
handleEvent(Event * event)555 void 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
notifyWidget(WidgetNotification notification)593 void 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
viewGeometryDidChange()606 void 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
clipRectInWindowCoordinates() const617 IntRect 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
focusPluginElement()634 void 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
pendingURLRequestsTimerFired()643 void 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
performURLRequest(URLRequest * request)656 void 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
performFrameLoadURLRequest(URLRequest * request)675 void 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
performJavaScriptURLRequest(URLRequest * request)715 void 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
addStream(Stream * stream)766 void PluginView::addStream(Stream* stream)
767 {
768 ASSERT(!m_streams.contains(stream->streamID()));
769 m_streams.set(stream->streamID(), stream);
770 }
771
removeStream(Stream * stream)772 void PluginView::removeStream(Stream* stream)
773 {
774 ASSERT(m_streams.get(stream->streamID()) == stream);
775
776 m_streams.remove(stream->streamID());
777 }
778
cancelAllStreams()779 void 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
redeliverManualStream()791 void 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
invalidateRect(const IntRect & dirtyRect)823 void 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
setFocus(bool hasFocus)838 void 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
mediaCanStart()848 void PluginView::mediaCanStart()
849 {
850 ASSERT(m_isWaitingUntilMediaCanStart);
851 m_isWaitingUntilMediaCanStart = false;
852
853 initializePlugin();
854 }
855
invalidate(const IntRect & dirtyRect)856 void PluginView::invalidate(const IntRect& dirtyRect)
857 {
858 invalidateRect(dirtyRect);
859 }
860
userAgent()861 String 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
loadURL(uint64_t requestID,const String & method,const String & urlString,const String & target,const HTTPHeaderMap & headerFields,const Vector<uint8_t> & httpBody,bool allowPopups)870 void 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
cancelStreamLoad(uint64_t streamID)884 void 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
cancelManualStreamLoad()897 void 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
windowScriptNPObject()909 NPObject* 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
pluginElementNPObject()920 NPObject* 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
evaluate(NPObject * npObject,const String & scriptString,NPVariant * result,bool allowPopups)932 bool 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
setStatusbarText(const String & statusbarText)952 void 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
isAcceleratedCompositingEnabled()964 bool 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
pluginProcessCrashed()976 void 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)
nativeParentWindow()992 HWND PluginView::nativeParentWindow()
993 {
994 return m_webPage->nativeWindow();
995 }
996 #endif
997
998 #if PLATFORM(MAC)
setComplexTextInputEnabled(bool complexTextInputEnabled)999 void PluginView::setComplexTextInputEnabled(bool complexTextInputEnabled)
1000 {
1001 m_webPage->send(Messages::WebPageProxy::SetComplexTextInputEnabled(m_plugin->pluginComplexTextInputIdentifier(), complexTextInputEnabled));
1002 }
1003
compositingRenderServerPort()1004 mach_port_t PluginView::compositingRenderServerPort()
1005 {
1006 return WebProcess::shared().compositingRenderServerPort();
1007 }
1008
1009 #endif
1010
proxiesForURL(const String & urlString)1011 String 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
cookiesForURL(const String & urlString)1019 String PluginView::cookiesForURL(const String& urlString)
1020 {
1021 return cookies(m_pluginElement->document(), KURL(KURL(), urlString));
1022 }
1023
setCookiesForURL(const String & urlString,const String & cookieString)1024 void PluginView::setCookiesForURL(const String& urlString, const String& cookieString)
1025 {
1026 setCookies(m_pluginElement->document(), KURL(KURL(), urlString), cookieString);
1027 }
1028
isPrivateBrowsingEnabled()1029 bool 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
protectPluginFromDestruction()1042 void PluginView::protectPluginFromDestruction()
1043 {
1044 if (!m_isBeingDestroyed)
1045 ref();
1046 }
1047
unprotectPluginFromDestruction()1048 void PluginView::unprotectPluginFromDestruction()
1049 {
1050 if (!m_isBeingDestroyed)
1051 deref();
1052 }
1053
didFinishLoad(WebFrame * webFrame)1054 void 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
didFailLoad(WebFrame * webFrame,bool wasCancelled)1063 void 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