1 /*
2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Collabora Ltd. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "config.h"
28 #include "PluginView.h"
29
30 #include "Document.h"
31 #include "DocumentLoader.h"
32 #include "Element.h"
33 #include "FrameLoader.h"
34 #include "FrameTree.h"
35 #include "Frame.h"
36 #include "FrameView.h"
37 #include "GraphicsContext.h"
38 #include "Image.h"
39 #include "HTMLNames.h"
40 #include "HTMLPlugInElement.h"
41 #include "KeyboardEvent.h"
42 #include "MIMETypeRegistry.h"
43 #include "MouseEvent.h"
44 #include "NotImplemented.h"
45 #include "Page.h"
46 #include "FocusController.h"
47 #include "PlatformMouseEvent.h"
48 #if PLATFORM(WIN_OS) && !PLATFORM(WX) && ENABLE(NETSCAPE_PLUGIN_API)
49 #include "PluginMessageThrottlerWin.h"
50 #endif
51 #include "PluginPackage.h"
52 #include "ScriptController.h"
53 #include "ScriptValue.h"
54 #include "PluginDatabase.h"
55 #include "PluginDebug.h"
56 #include "PluginMainThreadScheduler.h"
57 #include "PluginPackage.h"
58 #include "RenderBox.h"
59 #include "RenderObject.h"
60 #include "npruntime_impl.h"
61 #include "Settings.h"
62 #if defined(ANDROID_PLUGINS)
63 #include "TouchEvent.h"
64 #endif
65
66 #if USE(JSC)
67 #include "JSDOMWindow.h"
68 #include "JSDOMBinding.h"
69 #include "c_instance.h"
70 #include "runtime_root.h"
71 #include "runtime.h"
72 #include <runtime/JSLock.h>
73 #include <runtime/JSValue.h>
74 #endif
75
76 #include <wtf/ASCIICType.h>
77
78 #if USE(JSC)
79 using JSC::ExecState;
80 using JSC::JSLock;
81 using JSC::JSObject;
82 using JSC::JSValue;
83 using JSC::UString;
84 #endif
85
86 using std::min;
87
88 using namespace WTF;
89
90 namespace WebCore {
91
92 using namespace HTMLNames;
93
94 static int s_callingPlugin;
95
scriptStringIfJavaScriptURL(const KURL & url)96 static String scriptStringIfJavaScriptURL(const KURL& url)
97 {
98 if (!protocolIsJavaScript(url))
99 return String();
100
101 // This returns an unescaped string
102 return decodeURLEscapeSequences(url.string().substring(11));
103 }
104
105 PluginView* PluginView::s_currentPluginView = 0;
106
popPopupsStateTimerFired(Timer<PluginView> *)107 void PluginView::popPopupsStateTimerFired(Timer<PluginView>*)
108 {
109 popPopupsEnabledState();
110 }
111
windowClipRect() const112 IntRect PluginView::windowClipRect() const
113 {
114 // Start by clipping to our bounds.
115 IntRect clipRect(m_windowRect);
116
117 // Take our element and get the clip rect from the enclosing layer and frame view.
118 RenderLayer* layer = m_element->renderer()->enclosingLayer();
119 FrameView* parentView = m_element->document()->view();
120 clipRect.intersect(parentView->windowClipRectForLayer(layer, true));
121
122 return clipRect;
123 }
124
setFrameRect(const IntRect & rect)125 void PluginView::setFrameRect(const IntRect& rect)
126 {
127 if (m_element->document()->printing())
128 return;
129
130 #if defined(ANDROID_PLUGINS)
131 if (rect != frameRect()) {
132 Widget::setFrameRect(rect);
133 setNPWindowRect(rect); // only call when it changes
134 }
135 #else
136 if (rect != frameRect())
137 Widget::setFrameRect(rect);
138 #endif
139
140 updatePluginWidget();
141
142 #if PLATFORM(WIN_OS)
143 // On Windows, always call plugin to change geometry.
144 setNPWindowRect(rect);
145 #elif XP_UNIX
146 // On Unix, only call plugin if it's full-page.
147 if (m_mode == NP_FULL)
148 setNPWindowRect(rect);
149 #endif
150 }
151
frameRectsChanged()152 void PluginView::frameRectsChanged()
153 {
154 updatePluginWidget();
155 }
156
handleEvent(Event * event)157 void PluginView::handleEvent(Event* event)
158 {
159 if (!m_plugin || m_isWindowed)
160 return;
161
162 if (event->isMouseEvent())
163 handleMouseEvent(static_cast<MouseEvent*>(event));
164 else if (event->isKeyboardEvent())
165 handleKeyboardEvent(static_cast<KeyboardEvent*>(event));
166 #if defined(ANDROID_PLUGINS)
167 else if (event->isTouchEvent())
168 handleTouchEvent(static_cast<TouchEvent*>(event));
169 #endif
170 }
171
172
start()173 bool PluginView::start()
174 {
175 if (m_isStarted)
176 return false;
177
178 m_isWaitingToStart = false;
179
180 PluginMainThreadScheduler::scheduler().registerPlugin(m_instance);
181
182 ASSERT(m_plugin);
183 ASSERT(m_plugin->pluginFuncs()->newp);
184
185 NPError npErr;
186 {
187 PluginView::setCurrentPluginView(this);
188 #if USE(JSC)
189 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
190 #endif
191 setCallingPlugin(true);
192 npErr = m_plugin->pluginFuncs()->newp((NPMIMEType)m_mimeType.utf8().data(), m_instance, m_mode, m_paramCount, m_paramNames, m_paramValues, NULL);
193 setCallingPlugin(false);
194 LOG_NPERROR(npErr);
195 PluginView::setCurrentPluginView(0);
196 }
197
198 if (npErr != NPERR_NO_ERROR) {
199 m_status = PluginStatusCanNotLoadPlugin;
200 PluginMainThreadScheduler::scheduler().unregisterPlugin(m_instance);
201 return false;
202 }
203
204 m_isStarted = true;
205
206 if (!m_url.isEmpty() && !m_loadManually) {
207 FrameLoadRequest frameLoadRequest;
208 frameLoadRequest.resourceRequest().setHTTPMethod("GET");
209 frameLoadRequest.resourceRequest().setURL(m_url);
210 load(frameLoadRequest, false, 0);
211 }
212
213 m_status = PluginStatusLoadedSuccessfully;
214
215 platformStart();
216
217 return true;
218 }
219
stop()220 void PluginView::stop()
221 {
222 if (!m_isStarted)
223 return;
224
225 LOG(Plugins, "PluginView::stop(): Stopping plug-in '%s'", m_plugin->name().utf8().data());
226
227 HashSet<RefPtr<PluginStream> > streams = m_streams;
228 HashSet<RefPtr<PluginStream> >::iterator end = streams.end();
229 for (HashSet<RefPtr<PluginStream> >::iterator it = streams.begin(); it != end; ++it) {
230 (*it)->stop();
231 disconnectStream((*it).get());
232 }
233
234 ASSERT(m_streams.isEmpty());
235
236 m_isStarted = false;
237 #if USE(JSC)
238 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
239 #endif
240 #ifdef XP_WIN
241 // Unsubclass the window
242 if (m_isWindowed) {
243 #if PLATFORM(WINCE)
244 WNDPROC currentWndProc = (WNDPROC)GetWindowLong(platformPluginWidget(), GWL_WNDPROC);
245
246 if (currentWndProc == PluginViewWndProc)
247 SetWindowLong(platformPluginWidget(), GWL_WNDPROC, (LONG)m_pluginWndProc);
248 #else
249 WNDPROC currentWndProc = (WNDPROC)GetWindowLongPtr(platformPluginWidget(), GWLP_WNDPROC);
250
251 if (currentWndProc == PluginViewWndProc)
252 SetWindowLongPtr(platformPluginWidget(), GWLP_WNDPROC, (LONG)m_pluginWndProc);
253 #endif
254 }
255 #endif // XP_WIN
256
257 #if !defined(XP_MACOSX)
258 // Clear the window
259 m_npWindow.window = 0;
260
261 if (m_plugin->pluginFuncs()->setwindow && !m_plugin->quirks().contains(PluginQuirkDontSetNullWindowHandleOnDestroy)) {
262 PluginView::setCurrentPluginView(this);
263 setCallingPlugin(true);
264 m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow);
265 setCallingPlugin(false);
266 PluginView::setCurrentPluginView(0);
267 }
268
269 #ifdef XP_UNIX
270 if (m_isWindowed && m_npWindow.ws_info)
271 delete (NPSetWindowCallbackStruct *)m_npWindow.ws_info;
272 m_npWindow.ws_info = 0;
273 #endif
274
275 #endif // !defined(XP_MACOSX)
276
277 PluginMainThreadScheduler::scheduler().unregisterPlugin(m_instance);
278
279 NPSavedData* savedData = 0;
280 PluginView::setCurrentPluginView(this);
281 setCallingPlugin(true);
282 NPError npErr = m_plugin->pluginFuncs()->destroy(m_instance, &savedData);
283 setCallingPlugin(false);
284 LOG_NPERROR(npErr);
285 PluginView::setCurrentPluginView(0);
286
287 #if ENABLE(NETSCAPE_PLUGIN_API)
288 if (savedData) {
289 // TODO: Actually save this data instead of just discarding it
290 if (savedData->buf)
291 NPN_MemFree(savedData->buf);
292 NPN_MemFree(savedData);
293 }
294 #endif
295
296 m_instance->pdata = 0;
297 }
298
setCurrentPluginView(PluginView * pluginView)299 void PluginView::setCurrentPluginView(PluginView* pluginView)
300 {
301 s_currentPluginView = pluginView;
302 }
303
currentPluginView()304 PluginView* PluginView::currentPluginView()
305 {
306 return s_currentPluginView;
307 }
308
createUTF8String(const String & str)309 static char* createUTF8String(const String& str)
310 {
311 CString cstr = str.utf8();
312 char* result = reinterpret_cast<char*>(fastMalloc(cstr.length() + 1));
313
314 strncpy(result, cstr.data(), cstr.length() + 1);
315
316 return result;
317 }
318
319 #if USE(JSC)
getString(ScriptController * proxy,JSValue result,String & string)320 static bool getString(ScriptController* proxy, JSValue result, String& string)
321 {
322 if (!proxy || !result || result.isUndefined())
323 return false;
324 JSLock lock(JSC::SilenceAssertionsOnly);
325
326 ExecState* exec = proxy->globalObject()->globalExec();
327 UString ustring = result.toString(exec);
328 exec->clearException();
329
330 string = ustring;
331 return true;
332 }
333 #endif
334
startOrAddToUnstartedList()335 bool PluginView::startOrAddToUnstartedList()
336 {
337 if (!m_parentFrame->page())
338 return false;
339
340 if (!m_parentFrame->page()->canStartPlugins()) {
341 m_parentFrame->page()->addUnstartedPlugin(this);
342 m_isWaitingToStart = true;
343 return true;
344 }
345
346 return start();
347 }
348
removeFromUnstartedListIfNecessary()349 void PluginView::removeFromUnstartedListIfNecessary()
350 {
351 if (!m_isWaitingToStart)
352 return;
353
354 if (!m_parentFrame->page())
355 return;
356
357 m_parentFrame->page()->removeUnstartedPlugin(this);
358 }
359
performRequest(PluginRequest * request)360 void PluginView::performRequest(PluginRequest* request)
361 {
362 // don't let a plugin start any loads if it is no longer part of a document that is being
363 // displayed unless the loads are in the same frame as the plugin.
364 const String& targetFrameName = request->frameLoadRequest().frameName();
365 if (m_parentFrame->loader()->documentLoader() != m_parentFrame->loader()->activeDocumentLoader() &&
366 (targetFrameName.isNull() || m_parentFrame->tree()->find(targetFrameName) != m_parentFrame))
367 return;
368
369 KURL requestURL = request->frameLoadRequest().resourceRequest().url();
370 String jsString = scriptStringIfJavaScriptURL(requestURL);
371
372 if (jsString.isNull()) {
373 // if this is not a targeted request, create a stream for it. otherwise,
374 // just pass it off to the loader
375 if (targetFrameName.isEmpty()) {
376 RefPtr<PluginStream> stream = PluginStream::create(this, m_parentFrame, request->frameLoadRequest().resourceRequest(), request->sendNotification(), request->notifyData(), plugin()->pluginFuncs(), instance(), m_plugin->quirks());
377 m_streams.add(stream);
378 stream->start();
379 } else {
380 // If the target frame is our frame, we could destroy the
381 // PluginView, so we protect it. <rdar://problem/6991251>
382 RefPtr<PluginView> protect(this);
383
384 m_parentFrame->loader()->load(request->frameLoadRequest().resourceRequest(), targetFrameName, false);
385
386 // FIXME: <rdar://problem/4807469> This should be sent when the document has finished loading
387 if (request->sendNotification()) {
388 PluginView::setCurrentPluginView(this);
389 #if USE(JSC)
390 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
391 #endif
392 setCallingPlugin(true);
393 m_plugin->pluginFuncs()->urlnotify(m_instance, requestURL.string().utf8().data(), NPRES_DONE, request->notifyData());
394 setCallingPlugin(false);
395 PluginView::setCurrentPluginView(0);
396 }
397 }
398 return;
399 }
400
401 // Targeted JavaScript requests are only allowed on the frame that contains the JavaScript plugin
402 // and this has been made sure in ::load.
403 ASSERT(targetFrameName.isEmpty() || m_parentFrame->tree()->find(targetFrameName) == m_parentFrame);
404
405 #if USE(JSC)
406 // Executing a script can cause the plugin view to be destroyed, so we keep a reference to the parent frame.
407 RefPtr<Frame> parentFrame = m_parentFrame;
408 JSValue result = m_parentFrame->loader()->executeScript(jsString, request->shouldAllowPopups()).jsValue();
409
410 if (targetFrameName.isNull()) {
411 String resultString;
412
413 CString cstr;
414 if (getString(parentFrame->script(), result, resultString))
415 cstr = resultString.utf8();
416
417 RefPtr<PluginStream> stream = PluginStream::create(this, m_parentFrame, request->frameLoadRequest().resourceRequest(), request->sendNotification(), request->notifyData(), plugin()->pluginFuncs(), instance(), m_plugin->quirks());
418 m_streams.add(stream);
419 stream->sendJavaScriptStream(requestURL, cstr);
420 }
421 #endif
422 }
423
requestTimerFired(Timer<PluginView> * timer)424 void PluginView::requestTimerFired(Timer<PluginView>* timer)
425 {
426 ASSERT(timer == &m_requestTimer);
427 ASSERT(m_requests.size() > 0);
428 ASSERT(!m_isJavaScriptPaused);
429
430 PluginRequest* request = m_requests[0];
431 m_requests.remove(0);
432
433 // Schedule a new request before calling performRequest since the call to
434 // performRequest can cause the plugin view to be deleted.
435 if (m_requests.size() > 0)
436 m_requestTimer.startOneShot(0);
437
438 performRequest(request);
439 delete request;
440 }
441
scheduleRequest(PluginRequest * request)442 void PluginView::scheduleRequest(PluginRequest* request)
443 {
444 m_requests.append(request);
445
446 if (!m_isJavaScriptPaused)
447 m_requestTimer.startOneShot(0);
448 }
449
load(const FrameLoadRequest & frameLoadRequest,bool sendNotification,void * notifyData)450 NPError PluginView::load(const FrameLoadRequest& frameLoadRequest, bool sendNotification, void* notifyData)
451 {
452 ASSERT(frameLoadRequest.resourceRequest().httpMethod() == "GET" || frameLoadRequest.resourceRequest().httpMethod() == "POST");
453
454 KURL url = frameLoadRequest.resourceRequest().url();
455
456 if (url.isEmpty())
457 return NPERR_INVALID_URL;
458
459 // Don't allow requests to be made when the document loader is stopping all loaders.
460 if (m_parentFrame->loader()->documentLoader()->isStopping())
461 return NPERR_GENERIC_ERROR;
462
463 const String& targetFrameName = frameLoadRequest.frameName();
464 String jsString = scriptStringIfJavaScriptURL(url);
465
466 if (!jsString.isNull()) {
467 Settings* settings = m_parentFrame->settings();
468
469 // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does.
470 if (!settings || !settings->isJavaScriptEnabled())
471 return NPERR_GENERIC_ERROR;
472
473 // For security reasons, only allow JS requests to be made on the frame that contains the plug-in.
474 if (!targetFrameName.isNull() && m_parentFrame->tree()->find(targetFrameName) != m_parentFrame)
475 return NPERR_INVALID_PARAM;
476 } else if (!FrameLoader::canLoad(url, String(), m_parentFrame->document())) {
477 return NPERR_GENERIC_ERROR;
478 }
479
480 PluginRequest* request = new PluginRequest(frameLoadRequest, sendNotification, notifyData, arePopupsAllowed());
481 scheduleRequest(request);
482
483 return NPERR_NO_ERROR;
484 }
485
makeURL(const KURL & baseURL,const char * relativeURLString)486 static KURL makeURL(const KURL& baseURL, const char* relativeURLString)
487 {
488 String urlString = relativeURLString;
489
490 // Strip return characters.
491 urlString.replace('\n', "");
492 urlString.replace('\r', "");
493
494 return KURL(baseURL, urlString);
495 }
496
getURLNotify(const char * url,const char * target,void * notifyData)497 NPError PluginView::getURLNotify(const char* url, const char* target, void* notifyData)
498 {
499 FrameLoadRequest frameLoadRequest;
500
501 frameLoadRequest.setFrameName(target);
502 frameLoadRequest.resourceRequest().setHTTPMethod("GET");
503 frameLoadRequest.resourceRequest().setURL(makeURL(m_baseURL, url));
504
505 return load(frameLoadRequest, true, notifyData);
506 }
507
getURL(const char * url,const char * target)508 NPError PluginView::getURL(const char* url, const char* target)
509 {
510 FrameLoadRequest frameLoadRequest;
511
512 frameLoadRequest.setFrameName(target);
513 frameLoadRequest.resourceRequest().setHTTPMethod("GET");
514 frameLoadRequest.resourceRequest().setURL(makeURL(m_baseURL, url));
515
516 return load(frameLoadRequest, false, 0);
517 }
518
postURLNotify(const char * url,const char * target,uint32 len,const char * buf,NPBool file,void * notifyData)519 NPError PluginView::postURLNotify(const char* url, const char* target, uint32 len, const char* buf, NPBool file, void* notifyData)
520 {
521 return handlePost(url, target, len, buf, file, notifyData, true, true);
522 }
523
postURL(const char * url,const char * target,uint32 len,const char * buf,NPBool file)524 NPError PluginView::postURL(const char* url, const char* target, uint32 len, const char* buf, NPBool file)
525 {
526 // As documented, only allow headers to be specified via NPP_PostURL when using a file.
527 return handlePost(url, target, len, buf, file, 0, false, file);
528 }
529
newStream(NPMIMEType type,const char * target,NPStream ** stream)530 NPError PluginView::newStream(NPMIMEType type, const char* target, NPStream** stream)
531 {
532 notImplemented();
533 // Unsupported
534 return NPERR_GENERIC_ERROR;
535 }
536
write(NPStream * stream,int32 len,void * buffer)537 int32 PluginView::write(NPStream* stream, int32 len, void* buffer)
538 {
539 notImplemented();
540 // Unsupported
541 return -1;
542 }
543
destroyStream(NPStream * stream,NPReason reason)544 NPError PluginView::destroyStream(NPStream* stream, NPReason reason)
545 {
546 if (!stream || PluginStream::ownerForStream(stream) != m_instance)
547 return NPERR_INVALID_INSTANCE_ERROR;
548
549 PluginStream* browserStream = static_cast<PluginStream*>(stream->ndata);
550 browserStream->cancelAndDestroyStream(reason);
551
552 return NPERR_NO_ERROR;
553 }
554
status(const char * message)555 void PluginView::status(const char* message)
556 {
557 if (Page* page = m_parentFrame->page())
558 page->chrome()->setStatusbarText(m_parentFrame, String(message));
559 }
560
setValue(NPPVariable variable,void * value)561 NPError PluginView::setValue(NPPVariable variable, void* value)
562 {
563 LOG(Plugins, "PluginView::setValue(%s): ", prettyNameForNPPVariable(variable, value).data());
564
565 switch (variable) {
566 case NPPVpluginWindowBool:
567 m_isWindowed = value;
568 return NPERR_NO_ERROR;
569 case NPPVpluginTransparentBool:
570 m_isTransparent = value;
571 return NPERR_NO_ERROR;
572 #if defined(XP_MACOSX)
573 case NPPVpluginDrawingModel: {
574 // Can only set drawing model inside NPP_New()
575 if (this != currentPluginView())
576 return NPERR_GENERIC_ERROR;
577
578 NPDrawingModel newDrawingModel = NPDrawingModel(uintptr_t(value));
579 switch (newDrawingModel) {
580 case NPDrawingModelCoreGraphics:
581 m_drawingModel = newDrawingModel;
582 return NPERR_NO_ERROR;
583 #ifndef NP_NO_QUICKDRAW
584 case NPDrawingModelQuickDraw:
585 #endif
586 case NPDrawingModelCoreAnimation:
587 default:
588 LOG(Plugins, "Plugin asked for unsupported drawing model: %s",
589 prettyNameForDrawingModel(newDrawingModel));
590 return NPERR_GENERIC_ERROR;
591 }
592 }
593
594 case NPPVpluginEventModel: {
595 // Can only set event model inside NPP_New()
596 if (this != currentPluginView())
597 return NPERR_GENERIC_ERROR;
598
599 NPEventModel newEventModel = NPEventModel(uintptr_t(value));
600 switch (newEventModel) {
601 #ifndef NP_NO_CARBON
602 case NPEventModelCarbon:
603 #endif
604 case NPEventModelCocoa:
605 m_eventModel = newEventModel;
606 return NPERR_NO_ERROR;
607
608 default:
609 LOG(Plugins, "Plugin asked for unsupported event model: %s",
610 prettyNameForEventModel(newEventModel));
611 return NPERR_GENERIC_ERROR;
612 }
613 }
614 #endif // defined(XP_MACOSX)
615
616 default:
617 #ifdef PLUGIN_PLATFORM_SETVALUE
618 return platformSetValue(variable, value);
619 #else
620 notImplemented();
621 return NPERR_GENERIC_ERROR;
622 #endif
623 }
624 }
625
invalidateTimerFired(Timer<PluginView> * timer)626 void PluginView::invalidateTimerFired(Timer<PluginView>* timer)
627 {
628 ASSERT(timer == &m_invalidateTimer);
629
630 for (unsigned i = 0; i < m_invalidRects.size(); i++)
631 invalidateRect(m_invalidRects[i]);
632 m_invalidRects.clear();
633 }
634
635
pushPopupsEnabledState(bool state)636 void PluginView::pushPopupsEnabledState(bool state)
637 {
638 m_popupStateStack.append(state);
639 }
640
popPopupsEnabledState()641 void PluginView::popPopupsEnabledState()
642 {
643 m_popupStateStack.removeLast();
644 }
645
arePopupsAllowed() const646 bool PluginView::arePopupsAllowed() const
647 {
648 if (!m_popupStateStack.isEmpty())
649 return m_popupStateStack.last();
650
651 return false;
652 }
653
setJavaScriptPaused(bool paused)654 void PluginView::setJavaScriptPaused(bool paused)
655 {
656 if (m_isJavaScriptPaused == paused)
657 return;
658 m_isJavaScriptPaused = paused;
659
660 if (m_isJavaScriptPaused)
661 m_requestTimer.stop();
662 else if (!m_requests.isEmpty())
663 m_requestTimer.startOneShot(0);
664 }
665
666
667 #if USE(JSC)
bindingInstance()668 PassRefPtr<JSC::Bindings::Instance> PluginView::bindingInstance()
669 {
670 #if ENABLE(NETSCAPE_PLUGIN_API)
671 NPObject* object = 0;
672
673 if (!m_isStarted || !m_plugin || !m_plugin->pluginFuncs()->getvalue)
674 return 0;
675
676 // On Windows, calling Java's NPN_GetValue can allow the message loop to
677 // run, allowing loading to take place or JavaScript to run. Protect the
678 // PluginView from destruction. <rdar://problem/6978804>
679 RefPtr<PluginView> protect(this);
680
681 NPError npErr;
682 {
683 PluginView::setCurrentPluginView(this);
684 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
685 setCallingPlugin(true);
686 npErr = m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginScriptableNPObject, &object);
687 setCallingPlugin(false);
688 PluginView::setCurrentPluginView(0);
689 }
690
691 if (hasOneRef()) {
692 // The renderer for the PluginView was destroyed during the above call, and
693 // the PluginView will be destroyed when this function returns, so we
694 // return null.
695 return 0;
696 }
697
698 if (npErr != NPERR_NO_ERROR || !object)
699 return 0;
700
701 RefPtr<JSC::Bindings::RootObject> root = m_parentFrame->script()->createRootObject(this);
702 RefPtr<JSC::Bindings::Instance> instance = JSC::Bindings::CInstance::create(object, root.release());
703
704 _NPN_ReleaseObject(object);
705
706 return instance.release();
707 #else
708 return 0;
709 #endif // NETSCAPE_PLUGIN_API
710 }
711 #endif // JSC
712
713 #if USE(V8)
714 // This is really JS engine independent
getNPObject()715 NPObject* PluginView::getNPObject() {
716 #if ENABLE(NETSCAPE_PLUGIN_API)
717 if (!m_plugin || !m_plugin->pluginFuncs()->getvalue)
718 return 0;
719
720 NPObject* object = 0;
721
722 NPError npErr;
723 {
724 PluginView::setCurrentPluginView(this);
725 setCallingPlugin(true);
726 npErr = m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginScriptableNPObject, &object);
727 setCallingPlugin(false);
728 PluginView::setCurrentPluginView(0);
729 }
730
731 if (npErr != NPERR_NO_ERROR || !object)
732 return 0;
733
734 _NPN_ReleaseObject(object);
735 return object;
736 #else
737 return 0;
738 #endif // NETSCAPE_PLUGIN_API
739 }
740 #endif // V8
741
disconnectStream(PluginStream * stream)742 void PluginView::disconnectStream(PluginStream* stream)
743 {
744 ASSERT(m_streams.contains(stream));
745
746 m_streams.remove(stream);
747 }
748
setParameters(const Vector<String> & paramNames,const Vector<String> & paramValues)749 void PluginView::setParameters(const Vector<String>& paramNames, const Vector<String>& paramValues)
750 {
751 ASSERT(paramNames.size() == paramValues.size());
752
753 unsigned size = paramNames.size();
754 unsigned paramCount = 0;
755
756 m_paramNames = reinterpret_cast<char**>(fastMalloc(sizeof(char*) * size));
757 m_paramValues = reinterpret_cast<char**>(fastMalloc(sizeof(char*) * size));
758
759 for (unsigned i = 0; i < size; i++) {
760 if (m_plugin->quirks().contains(PluginQuirkRemoveWindowlessVideoParam) && equalIgnoringCase(paramNames[i], "windowlessvideo"))
761 continue;
762
763 if (paramNames[i] == "pluginspage")
764 m_pluginsPage = paramValues[i];
765
766 m_paramNames[paramCount] = createUTF8String(paramNames[i]);
767 m_paramValues[paramCount] = createUTF8String(paramValues[i]);
768
769 paramCount++;
770 }
771
772 m_paramCount = paramCount;
773 }
774
PluginView(Frame * parentFrame,const IntSize & size,PluginPackage * plugin,Element * element,const KURL & url,const Vector<String> & paramNames,const Vector<String> & paramValues,const String & mimeType,bool loadManually)775 PluginView::PluginView(Frame* parentFrame, const IntSize& size, PluginPackage* plugin, Element* element, const KURL& url, const Vector<String>& paramNames, const Vector<String>& paramValues, const String& mimeType, bool loadManually)
776 : m_parentFrame(parentFrame)
777 , m_plugin(plugin)
778 , m_element(element)
779 , m_isStarted(false)
780 , m_url(url)
781 , m_baseURL(m_parentFrame->loader()->completeURL(m_parentFrame->document()->baseURL().string()))
782 , m_status(PluginStatusLoadedSuccessfully)
783 , m_requestTimer(this, &PluginView::requestTimerFired)
784 , m_invalidateTimer(this, &PluginView::invalidateTimerFired)
785 , m_popPopupsStateTimer(this, &PluginView::popPopupsStateTimerFired)
786 , m_paramNames(0)
787 , m_paramValues(0)
788 , m_mimeType(mimeType)
789 #if defined(XP_MACOSX)
790 , m_isWindowed(false)
791 #else
792 , m_isWindowed(true)
793 #endif
794 , m_isTransparent(false)
795 , m_haveInitialized(false)
796 , m_isWaitingToStart(false)
797 #if PLATFORM(GTK) || defined(Q_WS_X11)
798 , m_needsXEmbed(false)
799 #endif
800 #if PLATFORM(WIN_OS) && !PLATFORM(WX) && ENABLE(NETSCAPE_PLUGIN_API)
801 , m_pluginWndProc(0)
802 , m_lastMessage(0)
803 , m_isCallingPluginWndProc(false)
804 , m_wmPrintHDC(0)
805 , m_haveUpdatedPluginWidget(false)
806 #endif
807 #if (PLATFORM(QT) && PLATFORM(WIN_OS)) || defined(XP_MACOSX)
808 , m_window(0)
809 #endif
810 #if defined(XP_MACOSX)
811 , m_drawingModel(NPDrawingModel(-1))
812 , m_eventModel(NPEventModel(-1))
813 #endif
814 , m_loadManually(loadManually)
815 , m_manualStream(0)
816 , m_isJavaScriptPaused(false)
817 {
818 #if defined(ANDROID_PLUGINS)
819 platformInit();
820 #endif
821
822 if (!m_plugin) {
823 m_status = PluginStatusCanNotFindPlugin;
824 return;
825 }
826
827 m_instance = &m_instanceStruct;
828 m_instance->ndata = this;
829 m_instance->pdata = 0;
830
831 setParameters(paramNames, paramValues);
832
833 memset(&m_npWindow, 0, sizeof(m_npWindow));
834 #if defined(XP_MACOSX)
835 memset(&m_npCgContext, 0, sizeof(m_npCgContext));
836 #endif
837
838 m_mode = m_loadManually ? NP_FULL : NP_EMBED;
839
840 resize(size);
841 }
842
focusPluginElement()843 void PluginView::focusPluginElement()
844 {
845 // Focus the plugin
846 if (Page* page = m_parentFrame->page())
847 page->focusController()->setFocusedFrame(m_parentFrame);
848 m_parentFrame->document()->setFocusedNode(m_element);
849 }
850
didReceiveResponse(const ResourceResponse & response)851 void PluginView::didReceiveResponse(const ResourceResponse& response)
852 {
853 if (m_status != PluginStatusLoadedSuccessfully)
854 return;
855
856 ASSERT(m_loadManually);
857 ASSERT(!m_manualStream);
858
859 m_manualStream = PluginStream::create(this, m_parentFrame, m_parentFrame->loader()->activeDocumentLoader()->request(), false, 0, plugin()->pluginFuncs(), instance(), m_plugin->quirks());
860 m_manualStream->setLoadManually(true);
861
862 m_manualStream->didReceiveResponse(0, response);
863 }
864
didReceiveData(const char * data,int length)865 void PluginView::didReceiveData(const char* data, int length)
866 {
867 if (m_status != PluginStatusLoadedSuccessfully)
868 return;
869
870 ASSERT(m_loadManually);
871 ASSERT(m_manualStream);
872
873 m_manualStream->didReceiveData(0, data, length);
874 }
875
didFinishLoading()876 void PluginView::didFinishLoading()
877 {
878 if (m_status != PluginStatusLoadedSuccessfully)
879 return;
880
881 ASSERT(m_loadManually);
882 ASSERT(m_manualStream);
883
884 m_manualStream->didFinishLoading(0);
885 }
886
didFail(const ResourceError & error)887 void PluginView::didFail(const ResourceError& error)
888 {
889 if (m_status != PluginStatusLoadedSuccessfully)
890 return;
891
892 ASSERT(m_loadManually);
893 ASSERT(m_manualStream);
894
895 m_manualStream->didFail(0, error);
896 }
897
setCallingPlugin(bool b) const898 void PluginView::setCallingPlugin(bool b) const
899 {
900 if (!m_plugin->quirks().contains(PluginQuirkHasModalMessageLoop))
901 return;
902
903 if (b)
904 ++s_callingPlugin;
905 else
906 --s_callingPlugin;
907
908 ASSERT(s_callingPlugin >= 0);
909 }
910
isCallingPlugin()911 bool PluginView::isCallingPlugin()
912 {
913 return s_callingPlugin > 0;
914 }
915
create(Frame * parentFrame,const IntSize & size,Element * element,const KURL & url,const Vector<String> & paramNames,const Vector<String> & paramValues,const String & mimeType,bool loadManually)916 PassRefPtr<PluginView> PluginView::create(Frame* parentFrame, const IntSize& size, Element* element, const KURL& url, const Vector<String>& paramNames, const Vector<String>& paramValues, const String& mimeType, bool loadManually)
917 {
918 // if we fail to find a plugin for this MIME type, findPlugin will search for
919 // a plugin by the file extension and update the MIME type, so pass a mutable String
920 String mimeTypeCopy = mimeType;
921 PluginPackage* plugin = PluginDatabase::installedPlugins()->findPlugin(url, mimeTypeCopy);
922
923 // No plugin was found, try refreshing the database and searching again
924 if (!plugin && PluginDatabase::installedPlugins()->refresh()) {
925 mimeTypeCopy = mimeType;
926 plugin = PluginDatabase::installedPlugins()->findPlugin(url, mimeTypeCopy);
927 }
928
929 return adoptRef(new PluginView(parentFrame, size, plugin, element, url, paramNames, paramValues, mimeTypeCopy, loadManually));
930 }
931
freeStringArray(char ** stringArray,int length)932 void PluginView::freeStringArray(char** stringArray, int length)
933 {
934 if (!stringArray)
935 return;
936
937 for (int i = 0; i < length; i++)
938 fastFree(stringArray[i]);
939
940 fastFree(stringArray);
941 }
942
startsWithBlankLine(const Vector<char> & buffer)943 static inline bool startsWithBlankLine(const Vector<char>& buffer)
944 {
945 return buffer.size() > 0 && buffer[0] == '\n';
946 }
947
locationAfterFirstBlankLine(const Vector<char> & buffer)948 static inline int locationAfterFirstBlankLine(const Vector<char>& buffer)
949 {
950 const char* bytes = buffer.data();
951 unsigned length = buffer.size();
952
953 for (unsigned i = 0; i < length - 4; i++) {
954 // Support for Acrobat. It sends "\n\n".
955 if (bytes[i] == '\n' && bytes[i + 1] == '\n')
956 return i + 2;
957
958 // Returns the position after 2 CRLF's or 1 CRLF if it is the first line.
959 if (bytes[i] == '\r' && bytes[i + 1] == '\n') {
960 i += 2;
961 if (i == 2)
962 return i;
963 else if (bytes[i] == '\n')
964 // Support for Director. It sends "\r\n\n" (3880387).
965 return i + 1;
966 else if (bytes[i] == '\r' && bytes[i + 1] == '\n')
967 // Support for Flash. It sends "\r\n\r\n" (3758113).
968 return i + 2;
969 }
970 }
971
972 return -1;
973 }
974
findEOL(const char * bytes,unsigned length)975 static inline const char* findEOL(const char* bytes, unsigned length)
976 {
977 // According to the HTTP specification EOL is defined as
978 // a CRLF pair. Unfortunately, some servers will use LF
979 // instead. Worse yet, some servers will use a combination
980 // of both (e.g. <header>CRLFLF<body>), so findEOL needs
981 // to be more forgiving. It will now accept CRLF, LF or
982 // CR.
983 //
984 // It returns NULL if EOLF is not found or it will return
985 // a pointer to the first terminating character.
986 for (unsigned i = 0; i < length; i++) {
987 if (bytes[i] == '\n')
988 return bytes + i;
989 if (bytes[i] == '\r') {
990 // Check to see if spanning buffer bounds
991 // (CRLF is across reads). If so, wait for
992 // next read.
993 if (i + 1 == length)
994 break;
995
996 return bytes + i;
997 }
998 }
999
1000 return 0;
1001 }
1002
capitalizeRFC822HeaderFieldName(const String & name)1003 static inline String capitalizeRFC822HeaderFieldName(const String& name)
1004 {
1005 bool capitalizeCharacter = true;
1006 String result;
1007
1008 for (unsigned i = 0; i < name.length(); i++) {
1009 UChar c;
1010
1011 if (capitalizeCharacter && name[i] >= 'a' && name[i] <= 'z')
1012 c = toASCIIUpper(name[i]);
1013 else if (!capitalizeCharacter && name[i] >= 'A' && name[i] <= 'Z')
1014 c = toASCIILower(name[i]);
1015 else
1016 c = name[i];
1017
1018 if (name[i] == '-')
1019 capitalizeCharacter = true;
1020 else
1021 capitalizeCharacter = false;
1022
1023 result.append(c);
1024 }
1025
1026 return result;
1027 }
1028
parseRFC822HeaderFields(const Vector<char> & buffer,unsigned length)1029 static inline HTTPHeaderMap parseRFC822HeaderFields(const Vector<char>& buffer, unsigned length)
1030 {
1031 const char* bytes = buffer.data();
1032 const char* eol;
1033 String lastKey;
1034 HTTPHeaderMap headerFields;
1035
1036 // Loop ove rlines until we're past the header, or we can't find any more end-of-lines
1037 while ((eol = findEOL(bytes, length))) {
1038 const char* line = bytes;
1039 int lineLength = eol - bytes;
1040
1041 // Move bytes to the character after the terminator as returned by findEOL.
1042 bytes = eol + 1;
1043 if ((*eol == '\r') && (*bytes == '\n'))
1044 bytes++; // Safe since findEOL won't return a spanning CRLF.
1045
1046 length -= (bytes - line);
1047 if (lineLength == 0)
1048 // Blank line; we're at the end of the header
1049 break;
1050 else if (*line == ' ' || *line == '\t') {
1051 // Continuation of the previous header
1052 if (lastKey.isNull()) {
1053 // malformed header; ignore it and continue
1054 continue;
1055 } else {
1056 // Merge the continuation of the previous header
1057 String currentValue = headerFields.get(lastKey);
1058 String newValue(line, lineLength);
1059
1060 headerFields.set(lastKey, currentValue + newValue);
1061 }
1062 } else {
1063 // Brand new header
1064 const char* colon;
1065 for (colon = line; *colon != ':' && colon != eol; colon++) {
1066 // empty loop
1067 }
1068 if (colon == eol)
1069 // malformed header; ignore it and continue
1070 continue;
1071 else {
1072 lastKey = capitalizeRFC822HeaderFieldName(String(line, colon - line));
1073 String value;
1074
1075 for (colon++; colon != eol; colon++) {
1076 if (*colon != ' ' && *colon != '\t')
1077 break;
1078 }
1079 if (colon == eol)
1080 value = "";
1081 else
1082 value = String(colon, eol - colon);
1083
1084 String oldValue = headerFields.get(lastKey);
1085 if (!oldValue.isNull()) {
1086 String tmp = oldValue;
1087 tmp += ", ";
1088 tmp += value;
1089 value = tmp;
1090 }
1091
1092 headerFields.set(lastKey, value);
1093 }
1094 }
1095 }
1096
1097 return headerFields;
1098 }
1099
handlePost(const char * url,const char * target,uint32 len,const char * buf,bool file,void * notifyData,bool sendNotification,bool allowHeaders)1100 NPError PluginView::handlePost(const char* url, const char* target, uint32 len, const char* buf, bool file, void* notifyData, bool sendNotification, bool allowHeaders)
1101 {
1102 if (!url || !len || !buf)
1103 return NPERR_INVALID_PARAM;
1104
1105 FrameLoadRequest frameLoadRequest;
1106
1107 HTTPHeaderMap headerFields;
1108 Vector<char> buffer;
1109
1110 if (file) {
1111 NPError readResult = handlePostReadFile(buffer, len, buf);
1112 if(readResult != NPERR_NO_ERROR)
1113 return readResult;
1114 } else {
1115 buffer.resize(len);
1116 memcpy(buffer.data(), buf, len);
1117 }
1118
1119 const char* postData = buffer.data();
1120 int postDataLength = buffer.size();
1121
1122 if (allowHeaders) {
1123 if (startsWithBlankLine(buffer)) {
1124 postData++;
1125 postDataLength--;
1126 } else {
1127 int location = locationAfterFirstBlankLine(buffer);
1128 if (location != -1) {
1129 // If the blank line is somewhere in the middle of the buffer, everything before is the header
1130 headerFields = parseRFC822HeaderFields(buffer, location);
1131 unsigned dataLength = buffer.size() - location;
1132
1133 // Sometimes plugins like to set Content-Length themselves when they post,
1134 // but WebFoundation does not like that. So we will remove the header
1135 // and instead truncate the data to the requested length.
1136 String contentLength = headerFields.get("Content-Length");
1137
1138 if (!contentLength.isNull())
1139 dataLength = min(contentLength.toInt(), (int)dataLength);
1140 headerFields.remove("Content-Length");
1141
1142 postData += location;
1143 postDataLength = dataLength;
1144 }
1145 }
1146 }
1147
1148 frameLoadRequest.resourceRequest().setHTTPMethod("POST");
1149 frameLoadRequest.resourceRequest().setURL(makeURL(m_baseURL, url));
1150 frameLoadRequest.resourceRequest().addHTTPHeaderFields(headerFields);
1151 frameLoadRequest.resourceRequest().setHTTPBody(FormData::create(postData, postDataLength));
1152 frameLoadRequest.setFrameName(target);
1153
1154 return load(frameLoadRequest, sendNotification, notifyData);
1155 }
1156
1157 #ifdef PLUGIN_SCHEDULE_TIMER
scheduleTimer(NPP instance,uint32 interval,bool repeat,void (* timerFunc)(NPP,uint32 timerID))1158 uint32 PluginView::scheduleTimer(NPP instance, uint32 interval, bool repeat,
1159 void (*timerFunc)(NPP, uint32 timerID))
1160 {
1161 return m_timerList.schedule(instance, interval, repeat, timerFunc);
1162 }
1163
unscheduleTimer(NPP instance,uint32 timerID)1164 void PluginView::unscheduleTimer(NPP instance, uint32 timerID)
1165 {
1166 m_timerList.unschedule(instance, timerID);
1167 }
1168 #endif
1169
invalidateWindowlessPluginRect(const IntRect & rect)1170 void PluginView::invalidateWindowlessPluginRect(const IntRect& rect)
1171 {
1172 if (!isVisible())
1173 return;
1174
1175 if (!m_element->renderer())
1176 return;
1177 RenderBox* renderer = toRenderBox(m_element->renderer());
1178
1179 IntRect dirtyRect = rect;
1180 dirtyRect.move(renderer->borderLeft() + renderer->paddingLeft(), renderer->borderTop() + renderer->paddingTop());
1181 renderer->repaintRectangle(dirtyRect);
1182 }
1183
paintMissingPluginIcon(GraphicsContext * context,const IntRect & rect)1184 void PluginView::paintMissingPluginIcon(GraphicsContext* context, const IntRect& rect)
1185 {
1186 static RefPtr<Image> nullPluginImage;
1187 if (!nullPluginImage) {
1188 nullPluginImage = Image::loadPlatformResource("nullPlugin");
1189 }
1190
1191 IntRect imageRect(frameRect().x(), frameRect().y(), nullPluginImage->width(), nullPluginImage->height());
1192
1193 int xOffset = (frameRect().width() - imageRect.width()) / 2;
1194 int yOffset = (frameRect().height() - imageRect.height()) / 2;
1195
1196 imageRect.move(xOffset, yOffset);
1197
1198 if (!rect.intersects(imageRect)) {
1199 return;
1200 }
1201
1202 context->save();
1203 context->clip(windowClipRect());
1204 context->drawImage(nullPluginImage.get(), imageRect.location());
1205 context->restore();
1206 }
1207
1208 static const char* MozillaUserAgent = "Mozilla/5.0 ("
1209 #if defined(XP_MACOSX)
1210 "Macintosh; U; Intel Mac OS X;"
1211 #elif defined(XP_WIN)
1212 "Windows; U; Windows NT 5.1;"
1213 #elif defined(XP_UNIX)
1214 "X11; U; Linux i686;"
1215 #endif
1216 " en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0";
1217
userAgent()1218 const char* PluginView::userAgent()
1219 {
1220 #if !PLATFORM(ANDROID)
1221 if (m_plugin->quirks().contains(PluginQuirkWantsMozillaUserAgent))
1222 return MozillaUserAgent;
1223 #endif
1224 if (m_userAgent.isNull())
1225 m_userAgent = m_parentFrame->loader()->userAgent(m_url).utf8();
1226 return m_userAgent.data();
1227 }
1228
1229 #if ENABLE(NETSCAPE_PLUGIN_API)
userAgentStatic()1230 const char* PluginView::userAgentStatic()
1231 {
1232 return MozillaUserAgent;
1233 }
1234 #endif
1235
1236 } // namespace WebCore
1237