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