• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
3  * Copyright (C) 2008 Collabora Ltd. All rights reserved.
4  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #ifndef __LP64__
29 
30 #include "config.h"
31 #include "PluginView.h"
32 
33 #include <runtime/JSLock.h>
34 #include <runtime/JSValue.h>
35 #include "wtf/RetainPtr.h"
36 
37 #include "Document.h"
38 #include "DocumentLoader.h"
39 #include "Element.h"
40 #include "EventNames.h"
41 #include "FocusController.h"
42 #include "FrameLoader.h"
43 #include "FrameLoadRequest.h"
44 #include "FrameTree.h"
45 #include "Frame.h"
46 #include "FrameView.h"
47 #include "GraphicsContext.h"
48 #include "HTMLNames.h"
49 #include "HTMLPlugInElement.h"
50 #include "Image.h"
51 #include "JSDOMBinding.h"
52 #include "KeyboardEvent.h"
53 #include "MouseEvent.h"
54 #include "NotImplemented.h"
55 #include "npruntime_impl.h"
56 #include "Page.h"
57 #include "PlatformMouseEvent.h"
58 #include "PlatformKeyboardEvent.h"
59 #include "PluginDebug.h"
60 #include "PluginPackage.h"
61 #include "PluginMainThreadScheduler.h"
62 #include "RenderLayer.h"
63 #include "runtime.h"
64 #include "runtime_root.h"
65 #include "ScriptController.h"
66 #include "Settings.h"
67 
68 using JSC::ExecState;
69 using JSC::Interpreter;
70 using JSC::JSLock;
71 using JSC::JSObject;
72 using JSC::JSValue;
73 using JSC::UString;
74 
75 #if PLATFORM(QT)
76 #include <QWidget>
77 #include <QKeyEvent>
78 QT_BEGIN_NAMESPACE
79 extern Q_GUI_EXPORT OSWindowRef qt_mac_window_for(const QWidget *w);
80 QT_END_NAMESPACE
81 #endif
82 
83 using std::min;
84 
85 using namespace WTF;
86 
87 namespace WebCore {
88 
89 using namespace HTMLNames;
90 
91 static int modifiersForEvent(UIEventWithKeyState *event);
92 
nativeWindowFor(PlatformWidget widget)93 static inline WindowRef nativeWindowFor(PlatformWidget widget)
94 {
95 #if PLATFORM(QT)
96     if (widget)
97         return static_cast<WindowRef>(qt_mac_window_for(widget));
98 #endif
99     return 0;
100 }
101 
cgHandleFor(PlatformWidget widget)102 static inline CGContextRef cgHandleFor(PlatformWidget widget)
103 {
104 #if PLATFORM(QT)
105     if (widget)
106         return (CGContextRef)widget->macCGHandle();
107 #endif
108     return 0;
109 }
110 
topLevelOffsetFor(PlatformWidget widget)111 static inline IntPoint topLevelOffsetFor(PlatformWidget widget)
112 {
113 #if PLATFORM(QT)
114     if (widget) {
115         PlatformWidget topLevel = widget->window();
116         return widget->mapTo(topLevel, QPoint(0, 0)) + topLevel->geometry().topLeft() - topLevel->pos();
117     }
118 #endif
119     return IntPoint();
120 }
121 
122 // --------------- Lifetime management -----------------
123 
init()124 void PluginView::init()
125 {
126     if (m_haveInitialized)
127         return;
128     m_haveInitialized = true;
129 
130     if (!m_plugin) {
131         ASSERT(m_status == PluginStatusCanNotFindPlugin);
132         return;
133     }
134 
135     if (!m_plugin->load()) {
136         m_plugin = 0;
137         m_status = PluginStatusCanNotLoadPlugin;
138         return;
139     }
140 
141     if (!start()) {
142         m_status = PluginStatusCanNotLoadPlugin;
143         return;
144     }
145 
146     setPlatformPluginWidget(m_parentFrame->view()->hostWindow()->platformWindow());
147 
148     m_npCgContext.window = 0;
149     m_npCgContext.context = 0;
150     m_npWindow.window = (void*)&m_npCgContext;
151     m_npWindow.type = NPWindowTypeWindow;
152     m_npWindow.x = 0;
153     m_npWindow.y = 0;
154     m_npWindow.width = 0;
155     m_npWindow.height = 0;
156     m_npWindow.clipRect.left = 0;
157     m_npWindow.clipRect.top = 0;
158     m_npWindow.clipRect.right = 0;
159     m_npWindow.clipRect.bottom = 0;
160 
161     setIsNPAPIPlugin(true);
162 
163     show();
164 
165     m_status = PluginStatusLoadedSuccessfully;
166 
167     // TODO: Implement null timer throttling depending on plugin activation
168     m_nullEventTimer.set(new Timer<PluginView>(this, &PluginView::nullEventTimerFired));
169     m_nullEventTimer->startRepeating(0.02);
170 }
171 
~PluginView()172 PluginView::~PluginView()
173 {
174     stop();
175 
176     deleteAllValues(m_requests);
177 
178     freeStringArray(m_paramNames, m_paramCount);
179     freeStringArray(m_paramValues, m_paramCount);
180 
181     m_parentFrame->script()->cleanupScriptObjectsForPlugin(this);
182 
183     if (m_plugin && !(m_plugin->quirks().contains(PluginQuirkDontUnloadPlugin)))
184         m_plugin->unload();
185 
186     m_window = 0;
187 }
188 
stop()189 void PluginView::stop()
190 {
191     if (!m_isStarted)
192         return;
193 
194     HashSet<RefPtr<PluginStream> > streams = m_streams;
195     HashSet<RefPtr<PluginStream> >::iterator end = streams.end();
196     for (HashSet<RefPtr<PluginStream> >::iterator it = streams.begin(); it != end; ++it) {
197         (*it)->stop();
198         disconnectStream((*it).get());
199     }
200 
201     ASSERT(m_streams.isEmpty());
202 
203     m_isStarted = false;
204 
205     JSC::JSLock::DropAllLocks dropAllLocks(false);
206 
207     PluginMainThreadScheduler::scheduler().unregisterPlugin(m_instance);
208 
209     // Destroy the plugin
210     PluginView::setCurrentPluginView(this);
211     setCallingPlugin(true);
212     m_plugin->pluginFuncs()->destroy(m_instance, 0);
213     setCallingPlugin(false);
214     PluginView::setCurrentPluginView(0);
215 
216     m_instance->pdata = 0;
217 }
218 
getValueStatic(NPNVariable variable,void * value)219 NPError PluginView::getValueStatic(NPNVariable variable, void* value)
220 {
221     LOG(Plugin, "PluginView::getValueStatic(%d)", variable);
222 
223     switch (variable) {
224     case NPNVToolkit:
225         *((uint32 *)value) = 0;
226         return NPERR_NO_ERROR;
227 
228     case NPNVjavascriptEnabledBool:
229         *((uint32 *)value) = true;
230         return NPERR_NO_ERROR;
231 
232     default:
233         return NPERR_GENERIC_ERROR;
234     }
235 }
236 
getValue(NPNVariable variable,void * value)237 NPError PluginView::getValue(NPNVariable variable, void* value)
238 {
239     LOG(Plugin, "PluginView::getValue(%d)", variable);
240 
241     switch (variable) {
242     case NPNVWindowNPObject: {
243         if (m_isJavaScriptPaused)
244             return NPERR_GENERIC_ERROR;
245 
246         NPObject* windowScriptObject = m_parentFrame->script()->windowScriptNPObject();
247 
248         // Return value is expected to be retained, as described in
249         // <http://www.mozilla.org/projects/plugin/npruntime.html>
250         if (windowScriptObject)
251             _NPN_RetainObject(windowScriptObject);
252 
253         void** v = (void**)value;
254         *v = windowScriptObject;
255 
256         return NPERR_NO_ERROR;
257     }
258 
259     case NPNVPluginElementNPObject: {
260         if (m_isJavaScriptPaused)
261             return NPERR_GENERIC_ERROR;
262 
263         NPObject* pluginScriptObject = 0;
264 
265         if (m_element->hasTagName(appletTag) || m_element->hasTagName(embedTag) || m_element->hasTagName(objectTag))
266             pluginScriptObject = static_cast<HTMLPlugInElement*>(m_element)->getNPObject();
267 
268         // Return value is expected to be retained, as described in
269         // <http://www.mozilla.org/projects/plugin/npruntime.html>
270         if (pluginScriptObject)
271             _NPN_RetainObject(pluginScriptObject);
272 
273         void** v = (void**)value;
274         *v = pluginScriptObject;
275 
276         return NPERR_NO_ERROR;
277     }
278 
279     case NPNVsupportsCoreGraphicsBool:
280         *((uint32 *)value) = true;
281         return NPERR_NO_ERROR;
282 
283     default:
284         return getValueStatic(variable, value);
285     }
286 
287 }
setParent(ScrollView * parent)288 void PluginView::setParent(ScrollView* parent)
289 {
290     Widget::setParent(parent);
291 
292     if (parent)
293         init();
294 }
295 
296 // -------------- Geometry and painting ----------------
297 
show()298 void PluginView::show()
299 {
300     LOG(Plugin, "PluginView::show()");
301 
302     setSelfVisible(true);
303 
304     if (isParentVisible() && platformPluginWidget())
305         platformPluginWidget()->setVisible(true);
306 
307     Widget::show();
308 }
309 
hide()310 void PluginView::hide()
311 {
312     LOG(Plugin, "PluginView::hide()");
313 
314     setSelfVisible(false);
315 
316     if (isParentVisible() && platformPluginWidget())
317         platformPluginWidget()->setVisible(false);
318 
319     Widget::hide();
320 }
321 
setFocus()322 void PluginView::setFocus()
323 {
324     LOG(Plugin, "PluginView::setFocus()");
325 
326     if (platformPluginWidget())
327        platformPluginWidget()->setFocus(Qt::OtherFocusReason);
328    else
329        Widget::setFocus();
330 
331     // TODO: Also handle and pass on blur events (focus lost)
332 
333     EventRecord record;
334     record.what = getFocusEvent;
335     record.message = 0;
336     record.when = TickCount();
337     record.where = globalMousePosForPlugin();
338     record.modifiers = GetCurrentKeyModifiers();
339 
340     if (!dispatchNPEvent(record))
341         LOG(Events, "PluginView::setFocus(): Get-focus event not accepted");
342 }
343 
setParentVisible(bool visible)344 void PluginView::setParentVisible(bool visible)
345 {
346     if (isParentVisible() == visible)
347         return;
348 
349     Widget::setParentVisible(visible);
350 
351     if (isSelfVisible() && platformPluginWidget())
352         platformPluginWidget()->setVisible(visible);
353 }
354 
setNPWindowRect(const IntRect &)355 void PluginView::setNPWindowRect(const IntRect&)
356 {
357     setNPWindowIfNeeded();
358 }
359 
setNPWindowIfNeeded()360 void PluginView::setNPWindowIfNeeded()
361 {
362     if (!m_isStarted || !parent() || !m_plugin->pluginFuncs()->setwindow)
363         return;
364 
365     CGContextRef newContextRef = cgHandleFor(platformPluginWidget());
366     if (!newContextRef)
367         return;
368 
369     WindowRef newWindowRef = nativeWindowFor(platformPluginWidget());
370     if (!newWindowRef)
371         return;
372 
373     m_npWindow.window = (void*)&m_npCgContext;
374     m_npCgContext.window = newWindowRef;
375     m_npCgContext.context = newContextRef;
376 
377     m_npWindow.x = m_windowRect.x();
378     m_npWindow.y = m_windowRect.y();
379     m_npWindow.width = m_windowRect.width();
380     m_npWindow.height = m_windowRect.height();
381 
382     // TODO: (also clip against scrollbars, etc.)
383     m_npWindow.clipRect.left = 0;
384     m_npWindow.clipRect.top = 0;
385     m_npWindow.clipRect.right = m_windowRect.width();
386     m_npWindow.clipRect.bottom = m_windowRect.height();
387 
388     PluginView::setCurrentPluginView(this);
389     JSC::JSLock::DropAllLocks dropAllLocks(false);
390     setCallingPlugin(true);
391     m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow);
392     setCallingPlugin(false);
393     PluginView::setCurrentPluginView(0);
394 }
395 
updatePluginWidget()396 void PluginView::updatePluginWidget()
397 {
398     if (!parent())
399        return;
400 
401     ASSERT(parent()->isFrameView());
402     FrameView* frameView = static_cast<FrameView*>(parent());
403 
404     IntRect oldWindowRect = m_windowRect;
405     IntRect oldClipRect = m_clipRect;
406 
407     m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size());
408     m_clipRect = windowClipRect();
409     m_clipRect.move(-m_windowRect.x(), -m_windowRect.y());
410 
411     if (platformPluginWidget() && (m_windowRect != oldWindowRect || m_clipRect != oldClipRect))
412         setNPWindowIfNeeded();
413 }
414 
paint(GraphicsContext * context,const IntRect & rect)415 void PluginView::paint(GraphicsContext* context, const IntRect& rect)
416 {
417     if (!m_isStarted) {
418         paintMissingPluginIcon(context, rect);
419         return;
420     }
421 
422     if (context->paintingDisabled())
423         return;
424 
425     setNPWindowIfNeeded();
426 
427     EventRecord event;
428     event.what = updateEvt;
429     event.message = (long unsigned int)m_npCgContext.window;
430     event.when = TickCount();
431     event.where.h = 0;
432     event.where.v = 0;
433     event.modifiers = GetCurrentKeyModifiers();
434 
435     CGContextRef cg = m_npCgContext.context;
436     CGContextSaveGState(cg);
437     IntPoint offset = frameRect().location();
438     CGContextTranslateCTM(cg, offset.x(), offset.y());
439 
440     if (!dispatchNPEvent(event))
441         LOG(Events, "PluginView::paint(): Paint event not accepted");
442 
443     CGContextRestoreGState(cg);
444 }
445 
invalidateRect(const IntRect & rect)446 void PluginView::invalidateRect(const IntRect& rect)
447 {
448     if (platformPluginWidget()) {
449         // TODO: optimize
450         platformPluginWidget()->update();
451         return;
452     }
453 }
454 
invalidateRect(NPRect * rect)455 void PluginView::invalidateRect(NPRect* rect)
456 {
457     // TODO: optimize
458     invalidate();
459 }
460 
invalidateRegion(NPRegion region)461 void PluginView::invalidateRegion(NPRegion region)
462 {
463     // TODO: optimize
464     invalidate();
465 }
466 
forceRedraw()467 void PluginView::forceRedraw()
468 {
469     notImplemented();
470 }
471 
472 
473 // ----------------- Event handling --------------------
474 
handleMouseEvent(MouseEvent * event)475 void PluginView::handleMouseEvent(MouseEvent* event)
476 {
477     EventRecord record;
478 
479     if (event->type() == eventNames().mousemoveEvent) {
480         // Mouse movement is handled by null timer events
481         return;
482     } else if (event->type() == eventNames().mouseoverEvent) {
483         record.what = adjustCursorEvent;
484     } else if (event->type() == eventNames().mouseoutEvent) {
485         record.what = adjustCursorEvent;
486     } else if (event->type() == eventNames().mousedownEvent) {
487         record.what = mouseDown;
488         // The plugin needs focus to receive keyboard events
489         if (Page* page = m_parentFrame->page())
490             page->focusController()->setFocusedFrame(m_parentFrame);
491         m_parentFrame->document()->setFocusedNode(m_element);
492     } else if (event->type() == eventNames().mouseupEvent) {
493         record.what = mouseUp;
494     } else {
495         return;
496     }
497 
498     record.where = globalMousePosForPlugin();
499     record.modifiers = modifiersForEvent(event);
500 
501     if (!event->buttonDown())
502         record.modifiers |= btnState;
503 
504     if (event->button() == 2)
505         record.modifiers |= controlKey;
506 
507     if (!dispatchNPEvent(record)) {
508         if (record.what == adjustCursorEvent)
509             return; // Signals that the plugin wants a normal cursor
510 
511         LOG(Events, "PluginView::handleMouseEvent(): Mouse event type %d at %d,%d not accepted",
512                 record.what, record.where.h, record.where.v);
513     } else {
514         event->setDefaultHandled();
515     }
516 }
517 
handleKeyboardEvent(KeyboardEvent * event)518 void PluginView::handleKeyboardEvent(KeyboardEvent* event)
519 {
520     LOG(Plugin, "PluginView::handleKeyboardEvent() ----------------- ");
521 
522     LOG(Plugin, "PV::hKE(): KE.keyCode: 0x%02X, KE.charCode: %d",
523             event->keyCode(), event->charCode());
524 
525     EventRecord record;
526 
527     if (event->type() == eventNames().keydownEvent) {
528         // This event is the result of a PlatformKeyboardEvent::KeyDown which
529         // was disambiguated into a PlatformKeyboardEvent::RawKeyDown. Since
530         // we don't have access to the text here, we return, and wait for the
531         // corresponding event based on PlatformKeyboardEvent::Char.
532         return;
533     } else if (event->type() == eventNames().keypressEvent) {
534         // Which would be this one. This event was disambiguated from the same
535         // PlatformKeyboardEvent::KeyDown, but to a PlatformKeyboardEvent::Char,
536         // which retains the text from the original event. So, we can safely pass
537         // on the event as a key-down event to the plugin.
538         record.what = keyDown;
539     } else if (event->type() == eventNames().keyupEvent) {
540         // PlatformKeyboardEvent::KeyUp events always have the text, so nothing
541         // fancy here.
542         record.what = keyUp;
543     } else {
544         return;
545     }
546 
547     const PlatformKeyboardEvent* platformEvent = event->keyEvent();
548     int keyCode = platformEvent->nativeVirtualKeyCode();
549 
550     const String text = platformEvent->text();
551     if (text.length() < 1) {
552         event->setDefaultHandled();
553         return;
554     }
555 
556     WTF::RetainPtr<CFStringRef> cfText(WTF::AdoptCF, text.createCFString());
557 
558     LOG(Plugin, "PV::hKE(): PKE.text: %s, PKE.unmodifiedText: %s, PKE.keyIdentifier: %s",
559             text.ascii().data(), platformEvent->unmodifiedText().ascii().data(),
560             platformEvent->keyIdentifier().ascii().data());
561 
562     char charCodes[2] = { 0, 0 };
563     if (!CFStringGetCString(cfText.get(), charCodes, 2, CFStringGetSystemEncoding())) {
564         LOG_ERROR("Could not resolve character code using system encoding.");
565         event->setDefaultHandled();
566         return;
567     }
568 
569     record.where = globalMousePosForPlugin();
570     record.modifiers = modifiersForEvent(event);
571     record.message = ((keyCode & 0xFF) << 8) | (charCodes[0] & 0xFF);
572     record.when = TickCount();
573 
574     LOG(Plugin, "PV::hKE(): record.modifiers: %d", record.modifiers);
575 
576     LOG(Plugin, "PV::hKE(): PKE.qtEvent()->nativeVirtualKey: 0x%02X, charCode: %d",
577                keyCode, int(uchar(charCodes[0])));
578 
579     if (!dispatchNPEvent(record))
580         LOG(Events, "PluginView::handleKeyboardEvent(): Keyboard event type %d not accepted", record.what);
581     else
582         event->setDefaultHandled();
583 }
584 
nullEventTimerFired(Timer<PluginView> *)585 void PluginView::nullEventTimerFired(Timer<PluginView>*)
586 {
587     EventRecord record;
588 
589     record.what = nullEvent;
590     record.message = 0;
591     record.when = TickCount();
592     record.where = globalMousePosForPlugin();
593     record.modifiers = GetCurrentKeyModifiers();
594     if (!Button())
595         record.modifiers |= btnState;
596 
597     if (!dispatchNPEvent(record))
598         LOG(Events, "PluginView::nullEventTimerFired(): Null event not accepted");
599 }
600 
modifiersForEvent(UIEventWithKeyState * event)601 static int modifiersForEvent(UIEventWithKeyState* event)
602 {
603     int modifiers = 0;
604 
605     if (event->ctrlKey())
606         modifiers |= controlKey;
607 
608     if (event->altKey())
609         modifiers |= optionKey;
610 
611     if (event->metaKey())
612         modifiers |= cmdKey;
613 
614     if (event->shiftKey())
615         modifiers |= shiftKey;
616 
617      return modifiers;
618 }
619 
tigerOrBetter()620 static bool tigerOrBetter()
621 {
622     static SInt32 systemVersion = 0;
623 
624     if (!systemVersion) {
625         if (Gestalt(gestaltSystemVersion, &systemVersion) != noErr)
626             return false;
627     }
628 
629     return systemVersion >= 0x1040;
630 }
631 
globalMousePosForPlugin() const632 Point PluginView::globalMousePosForPlugin() const
633 {
634     Point pos;
635     GetGlobalMouse(&pos);
636 
637     IntPoint offset = topLevelOffsetFor(platformPluginWidget());
638     pos.h -= offset.x();
639     pos.v -= offset.y();
640 
641     float scaleFactor = tigerOrBetter() ? HIGetScaleFactor() : 1;
642 
643     pos.h = short(pos.h * scaleFactor);
644     pos.v = short(pos.v * scaleFactor);
645 
646     return pos;
647 }
648 
dispatchNPEvent(NPEvent & event)649 bool PluginView::dispatchNPEvent(NPEvent& event)
650 {
651     PluginView::setCurrentPluginView(this);
652     JSC::JSLock::DropAllLocks dropAllLocks(false);
653     setCallingPlugin(true);
654 
655     bool accepted = m_plugin->pluginFuncs()->event(m_instance, &event);
656 
657     setCallingPlugin(false);
658     PluginView::setCurrentPluginView(0);
659     return accepted;
660 }
661 
662 // ------------------- Miscellaneous  ------------------
663 
664 static const char* MozillaUserAgent = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0";
665 
userAgent()666 const char* PluginView::userAgent()
667 {
668     if (m_plugin->quirks().contains(PluginQuirkWantsMozillaUserAgent))
669         return MozillaUserAgent;
670 
671     if (m_userAgent.isNull())
672         m_userAgent = m_parentFrame->loader()->userAgent(m_url).utf8();
673 
674     return m_userAgent.data();
675 }
676 
userAgentStatic()677 const char* PluginView::userAgentStatic()
678 {
679     return MozillaUserAgent;
680 }
681 
handlePostReadFile(Vector<char> & buffer,uint32 len,const char * buf)682 NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32 len, const char* buf)
683 {
684     String filename(buf, len);
685 
686     if (filename.startsWith("file:///"))
687         filename = filename.substring(8);
688 
689     if (!fileExists(filename))
690         return NPERR_FILE_NOT_FOUND;
691 
692     FILE* fileHandle = fopen((filename.utf8()).data(), "r");
693 
694     if (fileHandle == 0)
695         return NPERR_FILE_NOT_FOUND;
696 
697     int bytesRead = fread(buffer.data(), 1, 0, fileHandle);
698 
699     fclose(fileHandle);
700 
701     if (bytesRead <= 0)
702         return NPERR_FILE_NOT_FOUND;
703 
704     return NPERR_NO_ERROR;
705 }
706 
707 } // namespace WebCore
708 
709 #endif // !__LP64__
710