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