• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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