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