finally blocks should not set the exception stack trace when re-throwing the exception.
[WebKit-https.git] / Source / WebCore / bindings / js / JSDOMBinding.cpp
1 /*
2  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013 Apple Inc. All rights reserved.
4  *  Copyright (C) 2007 Samuel Weinig <sam@webkit.org>
5  *  Copyright (C) 2013 Michael Pruett <michael@68k.org>
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include "config.h"
23 #include "JSDOMBinding.h"
24
25 #include "CachedScript.h"
26 #include "DOMConstructorWithDocument.h"
27 #include "DOMStringList.h"
28 #include "ExceptionCode.h"
29 #include "ExceptionCodeDescription.h"
30 #include "ExceptionHeaders.h"
31 #include "ExceptionInterfaces.h"
32 #include "Frame.h"
33 #include "HTMLParserIdioms.h"
34 #include "JSDOMWindowCustom.h"
35 #include "JSExceptionBase.h"
36 #include "SecurityOrigin.h"
37 #include <inspector/ScriptCallStack.h>
38 #include <inspector/ScriptCallStackFactory.h>
39 #include <interpreter/Interpreter.h>
40 #include <runtime/DateInstance.h>
41 #include <runtime/Error.h>
42 #include <runtime/ErrorHandlingScope.h>
43 #include <runtime/Exception.h>
44 #include <runtime/ExceptionHelpers.h>
45 #include <runtime/JSFunction.h>
46 #include <stdarg.h>
47 #include <wtf/MathExtras.h>
48
49 using namespace JSC;
50 using namespace Inspector;
51
52 namespace WebCore {
53
54 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(DOMConstructorObject);
55 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(DOMConstructorWithDocument);
56
57 void addImpureProperty(const AtomicString& propertyName)
58 {
59     JSDOMWindow::commonVM().addImpureProperty(propertyName);
60 }
61
62 JSValue jsStringOrNull(ExecState* exec, const String& s)
63 {
64     if (s.isNull())
65         return jsNull();
66     return jsStringWithCache(exec, s);
67 }
68
69 JSValue jsOwnedStringOrNull(ExecState* exec, const String& s)
70 {
71     if (s.isNull())
72         return jsNull();
73     return jsOwnedString(exec, s);
74 }
75
76 JSValue jsStringOrUndefined(ExecState* exec, const String& s)
77 {
78     if (s.isNull())
79         return jsUndefined();
80     return jsStringWithCache(exec, s);
81 }
82
83 JSValue jsString(ExecState* exec, const URL& url)
84 {
85     return jsStringWithCache(exec, url.string());
86 }
87
88 JSValue jsStringOrNull(ExecState* exec, const URL& url)
89 {
90     if (url.isNull())
91         return jsNull();
92     return jsStringWithCache(exec, url.string());
93 }
94
95 JSValue jsStringOrUndefined(ExecState* exec, const URL& url)
96 {
97     if (url.isNull())
98         return jsUndefined();
99     return jsStringWithCache(exec, url.string());
100 }
101
102 String valueToStringWithNullCheck(ExecState* exec, JSValue value)
103 {
104     if (value.isNull())
105         return String();
106     return value.toString(exec)->value(exec);
107 }
108
109 String valueToStringWithUndefinedOrNullCheck(ExecState* exec, JSValue value)
110 {
111     if (value.isUndefinedOrNull())
112         return String();
113     return value.toString(exec)->value(exec);
114 }
115
116 JSValue jsDateOrNull(ExecState* exec, double value)
117 {
118     if (!std::isfinite(value))
119         return jsNull();
120     return DateInstance::create(exec->vm(), exec->lexicalGlobalObject()->dateStructure(), value);
121 }
122
123 double valueToDate(ExecState* exec, JSValue value)
124 {
125     if (value.isNumber())
126         return value.asNumber();
127     if (!value.inherits(DateInstance::info()))
128         return std::numeric_limits<double>::quiet_NaN();
129     return static_cast<DateInstance*>(value.toObject(exec))->internalNumber();
130 }
131
132 JSC::JSValue jsArray(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, PassRefPtr<DOMStringList> stringList)
133 {
134     JSC::MarkedArgumentBuffer list;
135     if (stringList) {
136         for (unsigned i = 0; i < stringList->length(); ++i)
137             list.append(jsStringWithCache(exec, stringList->item(i)));
138     }
139     return JSC::constructArray(exec, 0, globalObject, list);
140 }
141
142 void reportException(ExecState* exec, Exception* exception, CachedScript* cachedScript)
143 {
144     RELEASE_ASSERT(exec->vm().currentThreadIsHoldingAPILock());
145     if (isTerminatedExecutionException(exception))
146         return;
147
148     ErrorHandlingScope errorScope(exec->vm());
149
150     RefPtr<ScriptCallStack> callStack(createScriptCallStackFromException(exec, exception, ScriptCallStack::maxCallStackSizeToCapture));
151     exec->clearException();
152
153     JSDOMGlobalObject* globalObject = jsCast<JSDOMGlobalObject*>(exec->lexicalGlobalObject());
154     if (JSDOMWindow* window = jsDynamicCast<JSDOMWindow*>(globalObject)) {
155         if (!window->impl().isCurrentlyDisplayedInFrame())
156             return;
157     }
158
159     int lineNumber = 0;
160     int columnNumber = 0;
161     String exceptionSourceURL;
162     if (const ScriptCallFrame* callFrame = callStack->firstNonNativeCallFrame()) {
163         lineNumber = callFrame->lineNumber();
164         columnNumber = callFrame->columnNumber();
165         exceptionSourceURL = callFrame->sourceURL();
166     }
167
168     String errorMessage;
169     if (ExceptionBase* exceptionBase = toExceptionBase(exception->value()))
170         errorMessage = exceptionBase->message() + ": "  + exceptionBase->description();
171     else {
172         // FIXME: <http://webkit.org/b/115087> Web Inspector: WebCore::reportException should not evaluate JavaScript handling exceptions
173         // If this is a custon exception object, call toString on it to try and get a nice string representation for the exception.
174         errorMessage = exception->value().toString(exec)->value(exec);
175         exec->clearException();
176     }
177
178     ScriptExecutionContext* scriptExecutionContext = globalObject->scriptExecutionContext();
179     scriptExecutionContext->reportException(errorMessage, lineNumber, columnNumber, exceptionSourceURL, callStack->size() ? callStack : 0, cachedScript);
180 }
181
182 void reportCurrentException(ExecState* exec)
183 {
184     Exception* exception = exec->exception();
185     exec->clearException();
186     reportException(exec, exception);
187 }
188
189 #define TRY_TO_CREATE_EXCEPTION(interfaceName) \
190     case interfaceName##Type: \
191         errorObject = toJS(exec, globalObject, interfaceName::create(description)); \
192         break;
193
194 JSValue createDOMException(ExecState* exec, ExceptionCode ec)
195 {
196     if (!ec)
197         return jsUndefined();
198
199     // FIXME: Handle other WebIDL exception types.
200     if (ec == TypeError)
201         return createTypeError(exec);
202
203     // FIXME: All callers to setDOMException need to pass in the right global object
204     // for now, we're going to assume the lexicalGlobalObject.  Which is wrong in cases like this:
205     // frames[0].document.createElement(null, null); // throws an exception which should have the subframes prototypes.
206     JSDOMGlobalObject* globalObject = deprecatedGlobalObjectForPrototype(exec);
207
208     ExceptionCodeDescription description(ec);
209
210     JSValue errorObject;
211     switch (description.type) {
212         DOM_EXCEPTION_INTERFACES_FOR_EACH(TRY_TO_CREATE_EXCEPTION)
213     }
214     
215     ASSERT(errorObject);
216     addErrorInfo(exec, asObject(errorObject), true);
217     return errorObject;
218 }
219
220 void setDOMException(ExecState* exec, ExceptionCode ec)
221 {
222     if (!ec || exec->hadException())
223         return;
224
225     exec->vm().throwException(exec, createDOMException(exec, ec));
226 }
227
228 #undef TRY_TO_CREATE_EXCEPTION
229
230 bool shouldAllowAccessToNode(ExecState* exec, Node* node)
231 {
232     return BindingSecurity::shouldAllowAccessToNode(exec, node);
233 }
234
235 bool shouldAllowAccessToFrame(ExecState* exec, Frame* target)
236 {
237     return BindingSecurity::shouldAllowAccessToFrame(exec, target);
238 }
239
240 bool shouldAllowAccessToFrame(ExecState* exec, Frame* frame, String& message)
241 {
242     if (!frame)
243         return false;
244     if (BindingSecurity::shouldAllowAccessToFrame(exec, frame, DoNotReportSecurityError))
245         return true;
246     message = frame->document()->domWindow()->crossDomainAccessErrorMessage(activeDOMWindow(exec));
247     return false;
248 }
249
250 bool shouldAllowAccessToDOMWindow(ExecState* exec, DOMWindow& target, String& message)
251 {
252     if (BindingSecurity::shouldAllowAccessToDOMWindow(exec, target, DoNotReportSecurityError))
253         return true;
254     message = target.crossDomainAccessErrorMessage(activeDOMWindow(exec));
255     return false;
256 }
257
258 void printErrorMessageForFrame(Frame* frame, const String& message)
259 {
260     if (!frame)
261         return;
262     frame->document()->domWindow()->printErrorMessage(message);
263 }
264
265 EncodedJSValue objectToStringFunctionGetter(ExecState* exec, JSObject*, EncodedJSValue, PropertyName propertyName)
266 {
267     return JSValue::encode(JSFunction::create(exec->vm(), exec->lexicalGlobalObject(), 0, propertyName.publicName(), objectProtoFuncToString));
268 }
269
270 Structure* getCachedDOMStructure(JSDOMGlobalObject* globalObject, const ClassInfo* classInfo)
271 {
272     JSDOMStructureMap& structures = globalObject->structures();
273     return structures.get(classInfo).get();
274 }
275
276 Structure* cacheDOMStructure(JSDOMGlobalObject* globalObject, Structure* structure, const ClassInfo* classInfo)
277 {
278     JSDOMStructureMap& structures = globalObject->structures();
279     ASSERT(!structures.contains(classInfo));
280     return structures.set(classInfo, WriteBarrier<Structure>(globalObject->vm(), globalObject, structure)).iterator->value.get();
281 }
282
283 static const int32_t kMaxInt32 = 0x7fffffff;
284 static const int32_t kMinInt32 = -kMaxInt32 - 1;
285 static const uint32_t kMaxUInt32 = 0xffffffffU;
286 static const int64_t kJSMaxInteger = 0x20000000000000LL - 1; // 2^53 - 1, largest integer exactly representable in ECMAScript.
287
288 static double enforceRange(ExecState* exec, double x, double minimum, double maximum)
289 {
290     if (std::isnan(x) || std::isinf(x)) {
291         throwTypeError(exec);
292         return 0;
293     }
294     x = trunc(x);
295     if (x < minimum || x > maximum) {
296         throwTypeError(exec);
297         return 0;
298     }
299     return x;
300 }
301
302 template <typename T>
303 struct IntTypeLimits {
304 };
305
306 template <>
307 struct IntTypeLimits<int8_t> {
308     static const int8_t minValue = -128;
309     static const int8_t maxValue = 127;
310     static const unsigned numberOfValues = 256; // 2^8
311 };
312
313 template <>
314 struct IntTypeLimits<uint8_t> {
315     static const uint8_t maxValue = 255;
316     static const unsigned numberOfValues = 256; // 2^8
317 };
318
319 template <>
320 struct IntTypeLimits<int16_t> {
321     static const short minValue = -32768;
322     static const short maxValue = 32767;
323     static const unsigned numberOfValues = 65536; // 2^16
324 };
325
326 template <>
327 struct IntTypeLimits<uint16_t> {
328     static const unsigned short maxValue = 65535;
329     static const unsigned numberOfValues = 65536; // 2^16
330 };
331
332 template <typename T>
333 static inline T toSmallerInt(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
334 {
335     typedef IntTypeLimits<T> LimitsTrait;
336     // Fast path if the value is already a 32-bit signed integer in the right range.
337     if (value.isInt32()) {
338         int32_t d = value.asInt32();
339         if (d >= LimitsTrait::minValue && d <= LimitsTrait::maxValue)
340             return static_cast<T>(d);
341         if (configuration == EnforceRange) {
342             throwTypeError(exec);
343             return 0;
344         }
345         d %= LimitsTrait::numberOfValues;
346         return static_cast<T>(d > LimitsTrait::maxValue ? d - LimitsTrait::numberOfValues : d);
347     }
348
349     double x = value.toNumber(exec);
350     if (exec->hadException())
351         return 0;
352
353     if (configuration == EnforceRange)
354         return enforceRange(exec, x, LimitsTrait::minValue, LimitsTrait::maxValue);
355
356     if (std::isnan(x) || std::isinf(x) || !x)
357         return 0;
358
359     x = x < 0 ? -floor(fabs(x)) : floor(fabs(x));
360     x = fmod(x, LimitsTrait::numberOfValues);
361
362     return static_cast<T>(x > LimitsTrait::maxValue ? x - LimitsTrait::numberOfValues : x);
363 }
364
365 template <typename T>
366 static inline T toSmallerUInt(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
367 {
368     typedef IntTypeLimits<T> LimitsTrait;
369     // Fast path if the value is already a 32-bit unsigned integer in the right range.
370     if (value.isUInt32()) {
371         uint32_t d = value.asUInt32();
372         if (d <= LimitsTrait::maxValue)
373             return static_cast<T>(d);
374         if (configuration == EnforceRange) {
375             throwTypeError(exec);
376             return 0;
377         }
378         return static_cast<T>(d);
379     }
380
381     double x = value.toNumber(exec);
382     if (exec->hadException())
383         return 0;
384
385     if (configuration == EnforceRange)
386         return enforceRange(exec, x, 0, LimitsTrait::maxValue);
387
388     if (std::isnan(x) || std::isinf(x) || !x)
389         return 0;
390
391     x = x < 0 ? -floor(fabs(x)) : floor(fabs(x));
392     return static_cast<T>(fmod(x, LimitsTrait::numberOfValues));
393 }
394
395 // http://www.w3.org/TR/WebIDL/#es-byte
396 int8_t toInt8(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
397 {
398     return toSmallerInt<int8_t>(exec, value, configuration);
399 }
400
401 // http://www.w3.org/TR/WebIDL/#es-octet
402 uint8_t toUInt8(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
403 {
404     return toSmallerUInt<uint8_t>(exec, value, configuration);
405 }
406
407 // http://www.w3.org/TR/WebIDL/#es-short
408 int16_t toInt16(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
409 {
410     return toSmallerInt<int16_t>(exec, value, configuration);
411 }
412
413 // http://www.w3.org/TR/WebIDL/#es-unsigned-short
414 uint16_t toUInt16(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
415 {
416     return toSmallerUInt<uint16_t>(exec, value, configuration);
417 }
418
419 // http://www.w3.org/TR/WebIDL/#es-long
420 int32_t toInt32EnforceRange(ExecState* exec, JSValue value)
421 {
422     if (value.isInt32())
423         return value.asInt32();
424
425     double x = value.toNumber(exec);
426     if (exec->hadException())
427         return 0;
428     return enforceRange(exec, x, kMinInt32, kMaxInt32);
429 }
430
431 // http://www.w3.org/TR/WebIDL/#es-unsigned-long
432 uint32_t toUInt32EnforceRange(ExecState* exec, JSValue value)
433 {
434     if (value.isUInt32())
435         return value.asUInt32();
436
437     double x = value.toNumber(exec);
438     if (exec->hadException())
439         return 0;
440     return enforceRange(exec, x, 0, kMaxUInt32);
441 }
442
443 // http://www.w3.org/TR/WebIDL/#es-long-long
444 int64_t toInt64(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
445 {
446     if (value.isInt32())
447         return value.asInt32();
448
449     double x = value.toNumber(exec);
450     if (exec->hadException())
451         return 0;
452
453     if (configuration == EnforceRange)
454         return enforceRange(exec, x, -kJSMaxInteger, kJSMaxInteger);
455
456     // Map NaNs and +/-Infinity to 0; convert finite values modulo 2^64.
457     unsigned long long n;
458     doubleToInteger(x, n);
459     return n;
460 }
461
462 // http://www.w3.org/TR/WebIDL/#es-unsigned-long-long
463 uint64_t toUInt64(ExecState* exec, JSValue value, IntegerConversionConfiguration configuration)
464 {
465     if (value.isUInt32())
466         return value.asUInt32();
467
468     double x = value.toNumber(exec);
469     if (exec->hadException())
470         return 0;
471
472     if (configuration == EnforceRange)
473         return enforceRange(exec, x, 0, kJSMaxInteger);
474
475     // Map NaNs and +/-Infinity to 0; convert finite values modulo 2^64.
476     unsigned long long n;
477     doubleToInteger(x, n);
478     return n;
479 }
480
481 DOMWindow& activeDOMWindow(ExecState* exec)
482 {
483     return asJSDOMWindow(exec->lexicalGlobalObject())->impl();
484 }
485
486 DOMWindow& firstDOMWindow(ExecState* exec)
487 {
488     return asJSDOMWindow(exec->vmEntryGlobalObject())->impl();
489 }
490
491 static inline bool canAccessDocument(JSC::ExecState* state, Document* targetDocument, SecurityReportingOption reportingOption = ReportSecurityError)
492 {
493     if (!targetDocument)
494         return false;
495
496     DOMWindow& active = activeDOMWindow(state);
497
498     if (active.document()->securityOrigin()->canAccess(targetDocument->securityOrigin()))
499         return true;
500
501     if (reportingOption == ReportSecurityError)
502         printErrorMessageForFrame(targetDocument->frame(), targetDocument->domWindow()->crossDomainAccessErrorMessage(active));
503
504     return false;
505 }
506
507 bool BindingSecurity::shouldAllowAccessToDOMWindow(JSC::ExecState* state, DOMWindow& target, SecurityReportingOption reportingOption)
508 {
509     return canAccessDocument(state, target.document(), reportingOption);
510 }
511
512 bool BindingSecurity::shouldAllowAccessToFrame(JSC::ExecState* state, Frame* target, SecurityReportingOption reportingOption)
513 {
514     return target && canAccessDocument(state, target->document(), reportingOption);
515 }
516
517 bool BindingSecurity::shouldAllowAccessToNode(JSC::ExecState* state, Node* target)
518 {
519     return target && canAccessDocument(state, &target->document());
520 }
521     
522 static EncodedJSValue throwTypeError(JSC::ExecState& state, const String& errorMessage)
523 {
524     return throwVMError(&state, createTypeError(&state, errorMessage));
525 }
526
527 static void appendArgumentMustBe(StringBuilder& builder, unsigned argumentIndex, const char* argumentName, const char* interfaceName, const char* functionName)
528 {
529     builder.appendLiteral("Argument ");
530     builder.appendNumber(argumentIndex + 1);
531     builder.appendLiteral(" ('");
532     builder.append(argumentName);
533     builder.appendLiteral("') to ");
534     if (!functionName) {
535         builder.appendLiteral("the ");
536         builder.append(interfaceName);
537         builder.appendLiteral(" constructor");
538     } else {
539         builder.append(interfaceName);
540         builder.append('.');
541         builder.append(functionName);
542     }
543     builder.appendLiteral(" must be ");
544 }
545
546 JSC::EncodedJSValue reportDeprecatedGetterError(JSC::ExecState& state, const char* interfaceName, const char* attributeName)
547 {
548     auto& context = *jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject())->scriptExecutionContext();
549     context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, makeString("Deprecated attempt to access property '", attributeName, "' on a non-", interfaceName, " object."));
550     return JSValue::encode(jsUndefined());
551 }
552     
553 void reportDeprecatedSetterError(JSC::ExecState& state, const char* interfaceName, const char* attributeName)
554 {
555     auto& context = *jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject())->scriptExecutionContext();
556     context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, makeString("Deprecated attempt to set property '", attributeName, "' on a non-", interfaceName, " object."));
557 }
558
559 JSC::EncodedJSValue throwArgumentMustBeEnumError(JSC::ExecState& state, unsigned argumentIndex, const char* argumentName, const char* functionInterfaceName, const char* functionName, const char* expectedValues)
560 {
561     StringBuilder builder;
562     appendArgumentMustBe(builder, argumentIndex, argumentName, functionInterfaceName, functionName);
563     builder.appendLiteral("one of: ");
564     builder.append(expectedValues);
565     return throwTypeError(state, builder.toString());
566 }
567
568 JSC::EncodedJSValue throwArgumentMustBeFunctionError(JSC::ExecState& state, unsigned argumentIndex, const char* argumentName, const char* interfaceName, const char* functionName)
569 {
570     StringBuilder builder;
571     appendArgumentMustBe(builder, argumentIndex, argumentName, interfaceName, functionName);
572     builder.appendLiteral("a function");
573     return throwTypeError(state, builder.toString());
574 }
575
576 JSC::EncodedJSValue throwArgumentTypeError(JSC::ExecState& state, unsigned argumentIndex, const char* argumentName, const char* functionInterfaceName, const char* functionName, const char* expectedType)
577 {
578     StringBuilder builder;
579     appendArgumentMustBe(builder, argumentIndex, argumentName, functionInterfaceName, functionName);
580     builder.appendLiteral("an instance of ");
581     builder.append(expectedType);
582     return throwTypeError(state, builder.toString());
583 }
584
585 void throwArrayElementTypeError(JSC::ExecState& state)
586 {
587     throwTypeError(state, "Invalid Array element type");
588 }
589
590 void throwAttributeTypeError(JSC::ExecState& state, const char* interfaceName, const char* attributeName, const char* expectedType)
591 {
592     throwTypeError(state, makeString("The ", interfaceName, '.', attributeName, " attribute must be an instance of ", expectedType));
593 }
594
595 JSC::EncodedJSValue throwConstructorDocumentUnavailableError(JSC::ExecState& state, const char* interfaceName)
596 {
597     // FIXME: This is confusing exception wording. Can we reword to be clearer and more specific?
598     return throwVMError(&state, createReferenceError(&state, makeString(interfaceName, " constructor associated document is unavailable")));
599 }
600
601 JSC::EncodedJSValue throwGetterTypeError(JSC::ExecState& state, const char* interfaceName, const char* attributeName)
602 {
603     return throwTypeError(state, makeString("The ", interfaceName, '.', attributeName, " getter can only be used on instances of ", interfaceName));
604 }
605
606 void throwSequenceTypeError(JSC::ExecState& state)
607 {
608     throwTypeError(state, "Value is not a sequence");
609 }
610
611 void throwSetterTypeError(JSC::ExecState& state, const char* interfaceName, const char* attributeName)
612 {
613     throwTypeError(state, makeString("The ", interfaceName, '.', attributeName, " setter can only be used on instances of ", interfaceName));
614 }
615
616 EncodedJSValue throwThisTypeError(JSC::ExecState& state, const char* interfaceName, const char* functionName)
617 {
618     return throwTypeError(state, makeString("Can only call ", interfaceName, '.', functionName, " on instances of ", interfaceName));
619 }
620
621 } // namespace WebCore