void JSDOMWindow::putByIndex(JSCell* cell, ExecState* exec, unsigned index, JSValue value, bool shouldThrow) { JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell); if (!thisObject->impl().frame()) return; PropertyName propertyName = Identifier::from(exec, index); // Optimization: access JavaScript global variables directly before involving the DOM. if (thisObject->JSGlobalObject::hasOwnPropertyForWrite(exec, propertyName)) { if (BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->impl())) JSGlobalObject::putByIndex(thisObject, exec, index, value, shouldThrow); return; } if (BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->impl())) Base::putByIndex(thisObject, exec, index, value, shouldThrow); }
void JSDOMWindow::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) { JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell); if (!thisObject->impl()->frame()) return; // Optimization: access JavaScript global variables directly before involving the DOM. if (thisObject->JSGlobalObject::hasOwnPropertyForWrite(exec, propertyName)) { if (BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->impl())) JSGlobalObject::put(thisObject, exec, propertyName, value, slot); return; } if (lookupPut<JSDOMWindow>(exec, propertyName, value, s_info.propHashTable(exec), thisObject)) return; if (BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->impl())) Base::put(thisObject, exec, propertyName, value, slot); }
void JSLazyEventListener::parseCode() const { if (m_parsed) return; if (m_globalObject->scriptExecutionContext()->isDocument()) { JSDOMWindow* window = static_cast<JSDOMWindow*>(m_globalObject); Frame* frame = window->impl()->frame(); if (!frame) return; // FIXME: Is this check needed for non-Document contexts? ScriptController* script = frame->script(); if (!script->isEnabled() || script->isPaused()) return; } m_parsed = true; ExecState* exec = m_globalObject->globalExec(); MarkedArgumentBuffer args; UString sourceURL(m_globalObject->scriptExecutionContext()->url().string()); args.append(jsNontrivialString(exec, m_eventParameterName)); args.append(jsString(exec, m_code)); // FIXME: Passing the document's URL to construct is not always correct, since this event listener might // have been added with setAttribute from a script, and we should pass String() in that case. m_jsFunction = constructFunction(exec, args, Identifier(exec, m_functionName), sourceURL, m_lineNumber); // FIXME: is globalExec ok? JSFunction* listenerAsFunction = static_cast<JSFunction*>(m_jsFunction); if (exec->hadException()) { exec->clearException(); // failed to parse, so let's just make this listener a no-op m_jsFunction = 0; } else if (m_originalNode) { // Add the event's home element to the scope // (and the document, and the form - see JSHTMLElement::eventHandlerScope) ScopeChain scope = listenerAsFunction->scope(); JSValue thisObj = toJS(exec, m_originalNode); if (thisObj.isObject()) { static_cast<JSNode*>(asObject(thisObj))->pushEventHandlerScope(exec, scope); listenerAsFunction->setScope(scope); } } // Since we only parse once, there's no need to keep data used for parsing around anymore. m_functionName = String(); m_code = String(); m_eventParameterName = String(); }
bool JSDOMWindow::defineOwnProperty(JSC::JSObject* object, JSC::ExecState* exec, JSC::PropertyName propertyName, JSC::PropertyDescriptor& descriptor, bool shouldThrow) { JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object); // Only allow defining properties in this way by frames in the same origin, as it allows setters to be introduced. if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->impl())) return false; // Don't allow shadowing location using accessor properties. if (descriptor.isAccessorDescriptor() && propertyName == Identifier(exec, "location")) return false; return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow); }
JSValuePtr windowProtoFuncShowModalDialog(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args) { JSDOMWindow* window = toJSDOMWindow(thisValue); if (!window) return throwError(exec, TypeError); if (!window->allowsAccessFrom(exec)) return jsUndefined(); Frame* frame = window->impl()->frame(); if (!frame) return jsUndefined(); return showModalDialog(exec, frame, valueToStringWithUndefinedOrNullCheck(exec, args.at(exec, 0)), args.at(exec, 1), valueToStringWithUndefinedOrNullCheck(exec, args.at(exec, 2))); }
void ScheduledAction::execute(Document* document) { JSDOMWindow* window = toJSDOMWindow(document->frame(), m_isolatedWorld.get()); if (!window) return; RefPtr<Frame> frame = window->impl()->frame(); if (!frame || !frame->script()->canExecuteScripts(AboutToExecuteScript)) return; if (m_function) executeFunctionInContext(window, window->shell(), document); else frame->script()->executeScriptInWorld(m_isolatedWorld.get(), m_code); }
ScriptCachedFrameData::ScriptCachedFrameData(Frame* frame) : m_domWindow(0) { JSLock lock(SilenceAssertionsOnly); ScriptController* scriptController = frame->script(); ScriptController::ShellMap& windowShells = scriptController->m_windowShells; ScriptController::ShellMap::iterator windowShellsEnd = windowShells.end(); for (ScriptController::ShellMap::iterator iter = windowShells.begin(); iter != windowShellsEnd; ++iter) { JSDOMWindow* window = iter->second->window(); m_windows.add(iter->first.get(), window); m_domWindow = window->impl(); } scriptController->attachDebugger(0); }
void ScheduledAction::execute(Document* document) { JSDOMWindow* window = toJSDOMWindow(document->frame()); if (!window) return; RefPtr<Frame> frame = window->impl()->frame(); if (!frame || !frame->script()->isEnabled()) return; frame->script()->setProcessingTimerCallback(true); if (m_function) { executeFunctionInContext(window, window->shell()); Document::updateStyleForAllDocuments(); } else frame->loader()->executeScript(m_code); frame->script()->setProcessingTimerCallback(false); }
bool WebFrame::stringByEvaluatingJavaScriptInScriptWorld(WebScriptWorld* world, void* jsGlobalObject, const char* script, const char** evaluationResult) { if (!world || !jsGlobalObject || !evaluationResult) return false; *evaluationResult = 0; Frame* coreFrame = core(this); JSObjectRef globalObjectRef = reinterpret_cast<JSObjectRef>(jsGlobalObject); String string = String(script); // Start off with some guess at a frame and a global object, we'll try to do better...! JSDOMWindow* anyWorldGlobalObject = coreFrame->script()->globalObject(mainThreadNormalWorld()); // The global object is probably a shell object? - if so, we know how to use this! JSC::JSObject* globalObjectObj = toJS(globalObjectRef); if (!strcmp(globalObjectObj->classInfo()->className, "JSDOMWindowShell")) anyWorldGlobalObject = static_cast<JSDOMWindowShell*>(globalObjectObj)->window(); // Get the frame from the global object we've settled on. Frame* frame = anyWorldGlobalObject->impl()->frame(); ASSERT(frame->document()); JSC::JSValue result = frame->script()->executeScriptInWorld(world->world(), string, true).jsValue(); if (!frame) // In case the script removed our frame from the page. return true; // This bizarre set of rules matches behavior from WebKit for Safari 2.0. // If you don't like it, use -[WebScriptObject evaluateWebScript:] or // JSEvaluateScript instead, since they have less surprising semantics. if (!result || !result.isBoolean() && !result.isString() && !result.isNumber()) return true; JSC::JSLock lock(JSC::SilenceAssertionsOnly); String resultString = ustringToString(result.toString(anyWorldGlobalObject->globalExec())); *evaluationResult = strdup(resultString.utf8().data()); return true; }
void JSEventListener::handleEvent(ScriptExecutionContext* scriptExecutionContext, Event* event) { ASSERT(scriptExecutionContext); if (!scriptExecutionContext || scriptExecutionContext->isJSExecutionForbidden()) return; JSLock lock(SilenceAssertionsOnly); JSObject* jsFunction = this->jsFunction(scriptExecutionContext); if (!jsFunction) return; JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(scriptExecutionContext, m_isolatedWorld.get()); if (!globalObject) return; if (scriptExecutionContext->isDocument()) { JSDOMWindow* window = static_cast<JSDOMWindow*>(globalObject); Frame* frame = window->impl()->frame(); if (!frame) return; // The window must still be active in its frame. See <https://bugs.webkit.org/show_bug.cgi?id=21921>. // FIXME: A better fix for this may be to change DOMWindow::frame() to not return a frame the detached window used to be in. if (frame->domWindow() != window->impl()) return; // FIXME: Is this check needed for other contexts? ScriptController* script = frame->script(); if (!script->canExecuteScripts(AboutToExecuteScript) || script->isPaused()) return; } ExecState* exec = globalObject->globalExec(); JSValue handleEventFunction = jsFunction; CallData callData; CallType callType = getCallData(handleEventFunction, callData); // If jsFunction is not actually a function, see if it implements the EventListener interface and use that if (callType == CallTypeNone) { handleEventFunction = jsFunction->get(exec, Identifier(exec, "handleEvent")); callType = getCallData(handleEventFunction, callData); } if (callType != CallTypeNone) { RefPtr<JSEventListener> protect(this); MarkedArgumentBuffer args; args.append(toJS(exec, globalObject, event)); Event* savedEvent = globalObject->currentEvent(); globalObject->setCurrentEvent(event); JSGlobalData& globalData = globalObject->globalData(); DynamicGlobalObjectScope globalObjectScope(globalData, globalData.dynamicGlobalObject ? globalData.dynamicGlobalObject : globalObject); globalData.timeoutChecker.start(); JSValue thisValue = handleEventFunction == jsFunction ? toJS(exec, globalObject, event->currentTarget()) : jsFunction; JSValue retval = scriptExecutionContext->isDocument() ? JSMainThreadExecState::call(exec, handleEventFunction, callType, callData, thisValue, args) : JSC::call(exec, handleEventFunction, callType, callData, thisValue, args); globalData.timeoutChecker.stop(); globalObject->setCurrentEvent(savedEvent); #if ENABLE(WORKERS) if (scriptExecutionContext->isWorkerContext()) { bool terminatorCausedException = (exec->hadException() && isTerminatedExecutionException(exec->exception())); if (terminatorCausedException || globalData.terminator.shouldTerminate()) static_cast<WorkerContext*>(scriptExecutionContext)->script()->forbidExecution(); } #endif if (exec->hadException()) { event->target()->uncaughtExceptionInEventHandler(); reportCurrentException(exec); } else { if (!retval.isUndefinedOrNull() && event->storesResultAsString()) event->storeResult(ustringToString(retval.toString(exec)->value(exec))); if (m_isAttribute) { if (retval.isFalse()) event->preventDefault(); } } } }
void JSEventListener::handleEvent(ScriptExecutionContext* scriptExecutionContext, Event* event) { ASSERT(scriptExecutionContext); if (!scriptExecutionContext || scriptExecutionContext->isJSExecutionForbidden()) return; JSLockHolder lock(scriptExecutionContext->vm()); JSObject* jsFunction = this->jsFunction(scriptExecutionContext); if (!jsFunction) return; JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(scriptExecutionContext, *m_isolatedWorld); if (!globalObject) return; if (scriptExecutionContext->isDocument()) { JSDOMWindow* window = jsCast<JSDOMWindow*>(globalObject); if (!window->impl().isCurrentlyDisplayedInFrame()) return; // FIXME: Is this check needed for other contexts? ScriptController& script = window->impl().frame()->script(); if (!script.canExecuteScripts(AboutToExecuteScript) || script.isPaused()) return; } ExecState* exec = globalObject->globalExec(); JSValue handleEventFunction = jsFunction; CallData callData; CallType callType = getCallData(handleEventFunction, callData); // If jsFunction is not actually a function, see if it implements the EventListener interface and use that if (callType == CallTypeNone) { handleEventFunction = jsFunction->get(exec, Identifier::fromString(exec, "handleEvent")); callType = getCallData(handleEventFunction, callData); } if (callType != CallTypeNone) { Ref<JSEventListener> protect(*this); MarkedArgumentBuffer args; args.append(toJS(exec, globalObject, event)); Event* savedEvent = globalObject->currentEvent(); globalObject->setCurrentEvent(event); VM& vm = globalObject->vm(); VMEntryScope entryScope(vm, vm.entryScope ? vm.entryScope->globalObject() : globalObject); InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionCall(scriptExecutionContext, callType, callData); JSValue thisValue = handleEventFunction == jsFunction ? toJS(exec, globalObject, event->currentTarget()) : jsFunction; NakedPtr<Exception> exception; JSValue retval = scriptExecutionContext->isDocument() ? JSMainThreadExecState::call(exec, handleEventFunction, callType, callData, thisValue, args, exception) : JSC::call(exec, handleEventFunction, callType, callData, thisValue, args, exception); InspectorInstrumentation::didCallFunction(cookie, scriptExecutionContext); globalObject->setCurrentEvent(savedEvent); if (is<WorkerGlobalScope>(*scriptExecutionContext)) { bool terminatorCausedException = (exec->hadException() && isTerminatedExecutionException(exec->exception())); if (terminatorCausedException || (vm.watchdog && vm.watchdog->didFire())) downcast<WorkerGlobalScope>(*scriptExecutionContext).script()->forbidExecution(); } if (exception) { event->target()->uncaughtExceptionInEventHandler(); reportException(exec, exception); } else { if (!retval.isUndefinedOrNull() && is<BeforeUnloadEvent>(*event)) downcast<BeforeUnloadEvent>(*event).setReturnValue(retval.toString(exec)->value(exec)); if (m_isAttribute) { if (retval.isFalse()) event->preventDefault(); } } } }
JSObject* JSLazyEventListener::initializeJSFunction(ScriptExecutionContext* executionContext) const { ASSERT(executionContext); ASSERT(executionContext->isDocument()); if (!executionContext) return 0; Frame* frame = static_cast<Document*>(executionContext)->frame(); if (!frame) return 0; ScriptController* scriptController = frame->script(); if (!scriptController->canExecuteScripts(AboutToExecuteScript)) return 0; JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(executionContext, isolatedWorld()); if (!globalObject) return 0; if (executionContext->isDocument()) { JSDOMWindow* window = static_cast<JSDOMWindow*>(globalObject); Frame* frame = window->impl()->frame(); if (!frame) return 0; // FIXME: Is this check needed for non-Document contexts? ScriptController* script = frame->script(); if (!script->canExecuteScripts(AboutToExecuteScript) || script->isPaused()) return 0; } ExecState* exec = globalObject->globalExec(); MarkedArgumentBuffer args; args.append(jsNontrivialString(exec, stringToUString(m_eventParameterName))); args.append(jsString(exec, m_code)); JSObject* jsFunction = constructFunction(exec, args, Identifier(exec, stringToUString(m_functionName)), stringToUString(m_sourceURL), m_lineNumber); // FIXME: is globalExec ok? if (exec->hadException()) { exec->clearException(); return 0; } JSFunction* listenerAsFunction = static_cast<JSFunction*>(jsFunction); if (m_originalNode) { if (!wrapper()) { // Ensure that 'node' has a JavaScript wrapper to mark the event listener we're creating. JSLock lock(SilenceAssertionsOnly); // FIXME: Should pass the global object associated with the node setWrapper(asObject(toJS(globalObject->globalExec(), globalObject, m_originalNode))); } // Add the event's home element to the scope // (and the document, and the form - see JSHTMLElement::eventHandlerScope) ScopeChain scope = listenerAsFunction->scope(); static_cast<JSNode*>(wrapper())->pushEventHandlerScope(exec, scope); listenerAsFunction->setScope(scope); } // Since we only parse once, there's no need to keep data used for parsing around anymore. m_functionName = String(); m_code = String(); m_eventParameterName = String(); m_sourceURL = String(); return jsFunction; }
JSValuePtr windowProtoFuncOpen(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList& args) { JSDOMWindow* window = toJSDOMWindow(thisValue); if (!window) return throwError(exec, TypeError); if (!window->allowsAccessFrom(exec)) return jsUndefined(); Frame* frame = window->impl()->frame(); if (!frame) return jsUndefined(); Frame* activeFrame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame(); if (!activeFrame) return jsUndefined(); Page* page = frame->page(); String urlString = valueToStringWithUndefinedOrNullCheck(exec, args.at(exec, 0)); AtomicString frameName = args.at(exec, 1)->isUndefinedOrNull() ? "_blank" : AtomicString(args.at(exec, 1)->toString(exec)); // Because FrameTree::find() returns true for empty strings, we must check for empty framenames. // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker. if (!allowPopUp(exec) && (frameName.isEmpty() || !frame->tree()->find(frameName))) return jsUndefined(); // Get the target frame for the special cases of _top and _parent. In those // cases, we can schedule a location change right now and return early. bool topOrParent = false; if (frameName == "_top") { frame = frame->tree()->top(); topOrParent = true; } else if (frameName == "_parent") { if (Frame* parent = frame->tree()->parent()) frame = parent; topOrParent = true; } if (topOrParent) { if (!activeFrame->loader()->shouldAllowNavigation(frame)) return jsUndefined(); String completedURL; if (!urlString.isEmpty()) completedURL = activeFrame->document()->completeURL(urlString).string(); const JSDOMWindow* targetedWindow = toJSDOMWindow(frame); if (!completedURL.isEmpty() && (!protocolIs(completedURL, "javascript") || (targetedWindow && targetedWindow->allowsAccessFrom(exec)))) { bool userGesture = activeFrame->script()->processingUserGesture(); frame->loader()->scheduleLocationChange(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture); } return toJS(exec, frame->domWindow()); } // In the case of a named frame or a new window, we'll use the createWindow() helper WindowFeatures windowFeatures(valueToStringWithUndefinedOrNullCheck(exec, args.at(exec, 2))); FloatRect windowRect(windowFeatures.xSet ? windowFeatures.x : 0, windowFeatures.ySet ? windowFeatures.y : 0, windowFeatures.widthSet ? windowFeatures.width : 0, windowFeatures.heightSet ? windowFeatures.height : 0); DOMWindow::adjustWindowRect(screenAvailableRect(page ? page->mainFrame()->view() : 0), windowRect, windowRect); windowFeatures.x = windowRect.x(); windowFeatures.y = windowRect.y(); windowFeatures.height = windowRect.height(); windowFeatures.width = windowRect.width(); frame = createWindow(exec, frame, urlString, frameName, windowFeatures, noValue()); if (!frame) return jsUndefined(); return toJS(exec, frame->domWindow()); // global object }
void JSEventListener::handleEvent(Event* event, bool isWindowEvent) { JSLock lock(SilenceAssertionsOnly); JSObject* jsFunction = this->jsFunction(); if (!jsFunction) return; JSDOMGlobalObject* globalObject = m_globalObject; // Null check as clearGlobalObject() can clear this and we still get called back by // xmlhttprequest objects. See http://bugs.webkit.org/show_bug.cgi?id=13275 // FIXME: Is this check still necessary? Requests are supposed to be stopped before clearGlobalObject() is called. ASSERT(globalObject); if (!globalObject) return; ScriptExecutionContext* scriptExecutionContext = globalObject->scriptExecutionContext(); if (!scriptExecutionContext) return; if (scriptExecutionContext->isDocument()) { JSDOMWindow* window = static_cast<JSDOMWindow*>(globalObject); Frame* frame = window->impl()->frame(); if (!frame) return; // The window must still be active in its frame. See <https://bugs.webkit.org/show_bug.cgi?id=21921>. // FIXME: A better fix for this may be to change DOMWindow::frame() to not return a frame the detached window used to be in. if (frame->domWindow() != window->impl()) return; // FIXME: Is this check needed for other contexts? ScriptController* script = frame->script(); if (!script->isEnabled() || script->isPaused()) return; } ExecState* exec = globalObject->globalExec(); JSValue handleEventFunction = jsFunction->get(exec, Identifier(exec, "handleEvent")); CallData callData; CallType callType = handleEventFunction.getCallData(callData); if (callType == CallTypeNone) { handleEventFunction = JSValue(); callType = jsFunction->getCallData(callData); } if (callType != CallTypeNone) { ref(); MarkedArgumentBuffer args; args.append(toJS(exec, globalObject, event)); Event* savedEvent = globalObject->currentEvent(); globalObject->setCurrentEvent(event); // If this event handler is the first JavaScript to execute, then the // dynamic global object should be set to the global object of the // window in which the event occurred. JSGlobalData* globalData = globalObject->globalData(); DynamicGlobalObjectScope globalObjectScope(exec, globalData->dynamicGlobalObject ? globalData->dynamicGlobalObject : globalObject); JSValue retval; if (handleEventFunction) { globalObject->globalData()->timeoutChecker.start(); retval = call(exec, handleEventFunction, callType, callData, jsFunction, args); } else { JSValue thisValue; if (isWindowEvent) thisValue = globalObject->toThisObject(exec); else thisValue = toJS(exec, globalObject, event->currentTarget()); globalObject->globalData()->timeoutChecker.start(); retval = call(exec, jsFunction, callType, callData, thisValue, args); } globalObject->globalData()->timeoutChecker.stop(); globalObject->setCurrentEvent(savedEvent); if (exec->hadException()) reportCurrentException(exec); else { if (!retval.isUndefinedOrNull() && event->storesResultAsString()) event->storeResult(retval.toString(exec)); if (m_isAttribute) { bool retvalbool; if (retval.getBoolean(retvalbool) && !retvalbool) event->preventDefault(); } } if (scriptExecutionContext->isDocument()) Document::updateStyleForAllDocuments(); deref(); } }
void JSAbstractEventListener::handleEvent(Event* event, bool isWindowEvent) { JSObject* listener = listenerObj(); if (!listener) return; JSDOMWindow* window = this->window(); // Null check as clearWindow() can clear this and we still get called back by // xmlhttprequest objects. See http://bugs.webkit.org/show_bug.cgi?id=13275 if (!window) return; Frame* frame = window->impl()->frame(); if (!frame) return; ScriptController* script = frame->script(); if (!script->isEnabled() || script->isPaused()) return; JSLock lock(false); ExecState* exec = window->globalExec(); JSValue* handleEventFunction = listener->get(exec, Identifier(exec, "handleEvent")); CallData callData; CallType callType = handleEventFunction->getCallData(callData); if (callType == CallTypeNone) { handleEventFunction = 0; callType = listener->getCallData(callData); } if (callType != CallTypeNone) { ref(); ArgList args; args.append(toJS(exec, event)); Event* savedEvent = window->currentEvent(); window->setCurrentEvent(event); JSValue* retval; if (handleEventFunction) { window->startTimeoutCheck(); retval = call(exec, handleEventFunction, callType, callData, listener, args); } else { JSValue* thisValue; if (isWindowEvent) thisValue = window->shell(); else thisValue = toJS(exec, event->currentTarget()); window->startTimeoutCheck(); retval = call(exec, listener, callType, callData, thisValue, args); } window->stopTimeoutCheck(); window->setCurrentEvent(savedEvent); if (exec->hadException()) frame->domWindow()->console()->reportCurrentException(exec); else { if (!retval->isUndefinedOrNull() && event->storesResultAsString()) event->storeResult(retval->toString(exec)); if (m_isHTML) { bool retvalbool; if (retval->getBoolean(retvalbool) && !retvalbool) event->preventDefault(); } } Document::updateDocumentsRendering(); deref(); } }
void JSEventListener::handleEvent(ScriptExecutionContext* scriptExecutionContext, Event* event) { ASSERT(scriptExecutionContext); if (!scriptExecutionContext || scriptExecutionContext->isJSExecutionTerminated()) return; JSLock lock(SilenceAssertionsOnly); JSObject* jsFunction = this->jsFunction(scriptExecutionContext); if (!jsFunction) return; JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(scriptExecutionContext, m_isolatedWorld.get()); if (!globalObject) return; if (scriptExecutionContext->isDocument()) { JSDOMWindow* window = static_cast<JSDOMWindow*>(globalObject); Frame* frame = window->impl()->frame(); if (!frame) return; // The window must still be active in its frame. See <https://bugs.webkit.org/show_bug.cgi?id=21921>. // FIXME: A better fix for this may be to change DOMWindow::frame() to not return a frame the detached window used to be in. if (frame->domWindow() != window->impl()) return; // FIXME: Is this check needed for other contexts? ScriptController* script = frame->script(); if (!script->canExecuteScripts(AboutToExecuteScript) || script->isPaused()) return; } ExecState* exec = globalObject->globalExec(); JSValue handleEventFunction = jsFunction->get(exec, Identifier(exec, "handleEvent")); CallData callData; CallType callType = handleEventFunction.getCallData(callData); if (callType == CallTypeNone) { handleEventFunction = JSValue(); callType = jsFunction->getCallData(callData); } if (callType != CallTypeNone) { ref(); MarkedArgumentBuffer args; args.append(toJS(exec, globalObject, event)); Event* savedEvent = globalObject->currentEvent(); globalObject->setCurrentEvent(event); JSGlobalData* globalData = globalObject->globalData(); DynamicGlobalObjectScope globalObjectScope(exec, globalData->dynamicGlobalObject ? globalData->dynamicGlobalObject : globalObject); globalData->timeoutChecker.start(); JSValue retval = handleEventFunction ? JSC::call(exec, handleEventFunction, callType, callData, jsFunction, args) : JSC::call(exec, jsFunction, callType, callData, toJS(exec, globalObject, event->currentTarget()), args); globalData->timeoutChecker.stop(); globalObject->setCurrentEvent(savedEvent); if (exec->hadException()) #if PLATFORM(APOLLO) reportApolloException(globalObject); #else reportCurrentException(exec); #endif else { if (!retval.isUndefinedOrNull() && event->storesResultAsString())
void JSLazyEventListener::parseCode(ScriptExecutionContext* executionContext) const { ASSERT(executionContext); ASSERT(executionContext->isDocument()); if (!executionContext) return; if (m_parsed) return; Frame* frame = static_cast<Document*>(executionContext)->frame(); if (!frame) return; ScriptController* scriptController = frame->script(); if (!scriptController->isEnabled()) return; JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(executionContext); if (!globalObject) return; // Ensure that 'node' has a JavaScript wrapper to mark the event listener we're creating. if (m_originalNode) { JSLock lock(SilenceAssertionsOnly); // FIXME: Should pass the global object associated with the node toJS(globalObject->globalExec(), globalObject, m_originalNode); } if (executionContext->isDocument()) { JSDOMWindow* window = static_cast<JSDOMWindow*>(globalObject); Frame* frame = window->impl()->frame(); if (!frame) return; // FIXME: Is this check needed for non-Document contexts? ScriptController* script = frame->script(); if (!script->isEnabled() || script->isPaused()) return; } m_parsed = true; ExecState* exec = globalObject->globalExec(); MarkedArgumentBuffer args; args.append(jsNontrivialString(exec, m_eventParameterName)); args.append(jsString(exec, m_code)); m_jsFunction = constructFunction(exec, args, Identifier(exec, m_functionName), m_sourceURL, m_lineNumber); // FIXME: is globalExec ok? JSFunction* listenerAsFunction = static_cast<JSFunction*>(m_jsFunction); if (exec->hadException()) { exec->clearException(); // failed to parse, so let's just make this listener a no-op m_jsFunction = 0; } else if (m_originalNode) { // Add the event's home element to the scope // (and the document, and the form - see JSHTMLElement::eventHandlerScope) ScopeChain scope = listenerAsFunction->scope(); JSValue thisObj = toJS(exec, globalObject, m_originalNode); if (thisObj.isObject()) { static_cast<JSNode*>(asObject(thisObj))->pushEventHandlerScope(exec, scope); listenerAsFunction->setScope(scope); } } // Since we only parse once, there's no need to keep data used for parsing around anymore. m_functionName = String(); m_code = String(); m_eventParameterName = String(); m_sourceURL = String(); }
bool JSDOMWindow::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) { JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object); // When accessing a Window cross-domain, functions are always the native built-in ones, and they // are not affected by properties changed on the Window or anything in its prototype chain. // This is consistent with the behavior of Firefox. // We don't want any properties other than "close" and "closed" on a frameless window (i.e. one whose page got closed, // or whose iframe got removed). // FIXME: This doesn't fully match Firefox, which allows at least toString in addition to those. if (!thisObject->impl().frame()) { // The following code is safe for cross-domain and same domain use. // It ignores any custom properties that might be set on the DOMWindow (including a custom prototype). if (propertyName == exec->propertyNames().closed) { slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, jsDOMWindowClosed); return true; } if (propertyName == exec->propertyNames().close) { slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>); return true; } // FIXME: We should have a message here that explains why the property access/function call was // not allowed. slot.setUndefined(); return true; } else slot.setWatchpointSet(thisObject->m_windowCloseWatchpoints); // We need to check for cross-domain access here without printing the generic warning message // because we always allow access to some function, just different ones depending whether access // is allowed. String errorMessage; bool allowsAccess = shouldAllowAccessToDOMWindow(exec, thisObject->impl(), errorMessage); // Look for overrides before looking at any of our own properties, but ignore overrides completely // if this is cross-domain access. if (allowsAccess && JSGlobalObject::getOwnPropertySlot(thisObject, exec, propertyName, slot)) return true; // We need this code here because otherwise JSDOMWindowBase will stop the search before we even get to the // prototype due to the blanket same origin (shouldAllowAccessToDOMWindow) check at the end of getOwnPropertySlot. // Also, it's important to get the implementation straight out of the DOMWindow prototype regardless of // what prototype is actually set on this object. if (propertyName == exec->propertyNames().blur) { if (!allowsAccess) { slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionBlur, 0>); return true; } } else if (propertyName == exec->propertyNames().close) { if (!allowsAccess) { slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>); return true; } } else if (propertyName == exec->propertyNames().focus) { if (!allowsAccess) { slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionFocus, 0>); return true; } } else if (propertyName == exec->propertyNames().postMessage) { if (!allowsAccess) { slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionPostMessage, 2>); return true; } } else if (propertyName == exec->propertyNames().showModalDialog) { if (!DOMWindow::canShowModalDialog(thisObject->impl().frame())) { slot.setUndefined(); return true; } } else if (propertyName == exec->propertyNames().toString) { // Allow access to toString() cross-domain, but always Object.prototype.toString. if (!allowsAccess) { slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, objectToStringFunctionGetter); return true; } } const HashTableValue* entry = JSDOMWindow::info()->propHashTable(exec)->entry(exec, propertyName); if (entry) { slot.setCacheableCustom(thisObject, allowsAccess ? entry->attributes() : ReadOnly | DontDelete | DontEnum, entry->propertyGetter()); return true; } #if ENABLE(USER_MESSAGE_HANDLERS) if (propertyName == exec->propertyNames().webkit && thisObject->impl().shouldHaveWebKitNamespaceForWorld(thisObject->world())) { slot.setCacheableCustom(thisObject, allowsAccess ? DontDelete | ReadOnly : ReadOnly | DontDelete | DontEnum, jsDOMWindowWebKit); return true; } #endif // Do prototype lookup early so that functions and attributes in the prototype can have // precedence over the index and name getters. JSValue proto = thisObject->prototype(); if (proto.isObject()) { if (asObject(proto)->getPropertySlot(exec, propertyName, slot)) { if (!allowsAccess) { thisObject->printErrorMessage(errorMessage); slot.setUndefined(); } return true; } } // After this point it is no longer valid to cache any results because of // the impure nature of the property accesses which follow. We can move this // statement further down when we add ways to mitigate these impurities with, // for example, watchpoints. slot.disableCaching(); // Check for child frames by name before built-in properties to // match Mozilla. This does not match IE, but some sites end up // naming frames things that conflict with window properties that // are in Moz but not IE. Since we have some of these, we have to do // it the Moz way. if (thisObject->impl().frame()->tree().scopedChild(propertyNameToAtomicString(propertyName))) { slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, childFrameGetter); return true; } // FIXME: Search the whole frame hierarchy somewhere around here. // We need to test the correct priority order. // allow window[1] or parent[1] etc. (#56983) unsigned i = propertyName.asIndex(); if (i < thisObject->impl().frame()->tree().scopedChildCount()) { ASSERT(i != PropertyName::NotAnIndex); slot.setValue(thisObject, ReadOnly | DontDelete | DontEnum, toJS(exec, thisObject->impl().frame()->tree().scopedChild(i)->document()->domWindow())); return true; } if (!allowsAccess) { thisObject->printErrorMessage(errorMessage); slot.setUndefined(); return true; } // Allow shortcuts like 'Image1' instead of document.images.Image1 Document* document = thisObject->impl().frame()->document(); if (document->isHTMLDocument()) { AtomicStringImpl* atomicPropertyName = findAtomicString(propertyName); if (atomicPropertyName && toHTMLDocument(document)->hasWindowNamedItem(*atomicPropertyName)) { slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, namedItemGetter); return true; } } return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); }
bool JSDOMWindow::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot) { JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell); // When accessing a Window cross-domain, functions are always the native built-in ones, and they // are not affected by properties changed on the Window or anything in its prototype chain. // This is consistent with the behavior of Firefox. const HashEntry* entry; // We don't want any properties other than "close" and "closed" on a frameless window (i.e. one whose page got closed, // or whose iframe got removed). // FIXME: This doesn't fully match Firefox, which allows at least toString in addition to those. if (!thisObject->impl()->frame()) { // The following code is safe for cross-domain and same domain use. // It ignores any custom properties that might be set on the DOMWindow (including a custom prototype). entry = s_info.propHashTable(exec)->entry(exec, propertyName); if (entry && !(entry->attributes() & JSC::Function) && entry->propertyGetter() == jsDOMWindowClosed) { slot.setCustom(thisObject, entry->propertyGetter()); return true; } entry = JSDOMWindowPrototype::s_info.propHashTable(exec)->entry(exec, propertyName); if (entry && (entry->attributes() & JSC::Function) && entry->function() == jsDOMWindowPrototypeFunctionClose) { slot.setCustom(thisObject, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>); return true; } // FIXME: We should have a message here that explains why the property access/function call was // not allowed. slot.setUndefined(); return true; } // We need to check for cross-domain access here without printing the generic warning message // because we always allow access to some function, just different ones depending whether access // is allowed. String errorMessage; bool allowsAccess = shouldAllowAccessToDOMWindow(exec, thisObject->impl(), errorMessage); // Look for overrides before looking at any of our own properties, but ignore overrides completely // if this is cross-domain access. if (allowsAccess && JSGlobalObject::getOwnPropertySlot(thisObject, exec, propertyName, slot)) return true; // We need this code here because otherwise JSDOMWindowBase will stop the search before we even get to the // prototype due to the blanket same origin (shouldAllowAccessToDOMWindow) check at the end of getOwnPropertySlot. // Also, it's important to get the implementation straight out of the DOMWindow prototype regardless of // what prototype is actually set on this object. entry = JSDOMWindowPrototype::s_info.propHashTable(exec)->entry(exec, propertyName); if (entry) { if (entry->attributes() & JSC::Function) { if (entry->function() == jsDOMWindowPrototypeFunctionBlur) { if (!allowsAccess) { slot.setCustom(thisObject, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionBlur, 0>); return true; } } else if (entry->function() == jsDOMWindowPrototypeFunctionClose) { if (!allowsAccess) { slot.setCustom(thisObject, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>); return true; } } else if (entry->function() == jsDOMWindowPrototypeFunctionFocus) { if (!allowsAccess) { slot.setCustom(thisObject, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionFocus, 0>); return true; } } else if (entry->function() == jsDOMWindowPrototypeFunctionPostMessage) { if (!allowsAccess) { slot.setCustom(thisObject, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionPostMessage, 2>); return true; } } else if (entry->function() == jsDOMWindowPrototypeFunctionShowModalDialog) { if (!DOMWindow::canShowModalDialog(thisObject->impl()->frame())) { slot.setUndefined(); return true; } } } } else { // Allow access to toString() cross-domain, but always Object.prototype.toString. if (propertyName == exec->propertyNames().toString) { if (!allowsAccess) { slot.setCustom(thisObject, objectToStringFunctionGetter); return true; } } } entry = JSDOMWindow::s_info.propHashTable(exec)->entry(exec, propertyName); if (entry) { slot.setCustom(thisObject, entry->propertyGetter()); return true; } // Check for child frames by name before built-in properties to // match Mozilla. This does not match IE, but some sites end up // naming frames things that conflict with window properties that // are in Moz but not IE. Since we have some of these, we have to do // it the Moz way. if (thisObject->impl()->frame()->tree()->scopedChild(propertyNameToAtomicString(propertyName))) { slot.setCustom(thisObject, childFrameGetter); return true; } // Do prototype lookup early so that functions and attributes in the prototype can have // precedence over the index and name getters. JSValue proto = thisObject->prototype(); if (proto.isObject()) { if (asObject(proto)->getPropertySlot(exec, propertyName, slot)) { if (!allowsAccess) { thisObject->printErrorMessage(errorMessage); slot.setUndefined(); } return true; } } // FIXME: Search the whole frame hierarchy somewhere around here. // We need to test the correct priority order. // allow window[1] or parent[1] etc. (#56983) unsigned i = propertyName.asIndex(); if (i < thisObject->impl()->frame()->tree()->scopedChildCount()) { ASSERT(i != PropertyName::NotAnIndex); slot.setCustomIndex(thisObject, i, indexGetter); return true; } if (!allowsAccess) { thisObject->printErrorMessage(errorMessage); slot.setUndefined(); return true; } // Allow shortcuts like 'Image1' instead of document.images.Image1 Document* document = thisObject->impl()->frame()->document(); if (document->isHTMLDocument()) { AtomicStringImpl* atomicPropertyName = findAtomicString(propertyName); if (atomicPropertyName && (toHTMLDocument(document)->hasNamedItem(atomicPropertyName) || document->hasElementWithId(atomicPropertyName))) { slot.setCustom(thisObject, namedItemGetter); return true; } } return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); }
bool JSDOMWindow::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned index, PropertySlot& slot) { JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell); if (!thisObject->impl()->frame()) { // FIXME: We should have a message here that explains why the property access/function call was // not allowed. slot.setUndefined(); return true; } // We need to check for cross-domain access here without printing the generic warning message // because we always allow access to some function, just different ones depending whether access // is allowed. String errorMessage; bool allowsAccess = shouldAllowAccessToDOMWindow(exec, thisObject->impl(), errorMessage); // Look for overrides before looking at any of our own properties, but ignore overrides completely // if this is cross-domain access. if (allowsAccess && JSGlobalObject::getOwnPropertySlotByIndex(thisObject, exec, index, slot)) return true; PropertyName propertyName = Identifier::from(exec, index); // Check for child frames by name before built-in properties to // match Mozilla. This does not match IE, but some sites end up // naming frames things that conflict with window properties that // are in Moz but not IE. Since we have some of these, we have to do // it the Moz way. if (thisObject->impl()->frame()->tree()->scopedChild(propertyNameToAtomicString(propertyName))) { slot.setCustom(thisObject, childFrameGetter); return true; } // Do prototype lookup early so that functions and attributes in the prototype can have // precedence over the index and name getters. JSValue proto = thisObject->prototype(); if (proto.isObject()) { if (asObject(proto)->getPropertySlot(exec, index, slot)) { if (!allowsAccess) { thisObject->printErrorMessage(errorMessage); slot.setUndefined(); } return true; } } // FIXME: Search the whole frame hierarchy somewhere around here. // We need to test the correct priority order. // allow window[1] or parent[1] etc. (#56983) if (index < thisObject->impl()->frame()->tree()->scopedChildCount()) { ASSERT(index != PropertyName::NotAnIndex); slot.setCustomIndex(thisObject, index, indexGetter); return true; } if (!allowsAccess) { thisObject->printErrorMessage(errorMessage); slot.setUndefined(); return true; } // Allow shortcuts like 'Image1' instead of document.images.Image1 Document* document = thisObject->impl()->frame()->document(); if (document->isHTMLDocument()) { AtomicStringImpl* atomicPropertyName = findAtomicString(propertyName); if (atomicPropertyName && (toHTMLDocument(document)->hasNamedItem(atomicPropertyName) || document->hasElementWithId(atomicPropertyName))) { slot.setCustom(thisObject, namedItemGetter); return true; } } return Base::getOwnPropertySlotByIndex(thisObject, exec, index, slot); }
bool JSDOMWindow::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor) { JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object); // Never allow cross-domain getOwnPropertyDescriptor if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->impl())) return false; const HashEntry* entry; // We don't want any properties other than "close" and "closed" on a closed window. if (!thisObject->impl()->frame()) { // The following code is safe for cross-domain and same domain use. // It ignores any custom properties that might be set on the DOMWindow (including a custom prototype). entry = s_info.propHashTable(exec)->entry(exec, propertyName); if (entry && !(entry->attributes() & JSC::Function) && entry->propertyGetter() == jsDOMWindowClosed) { descriptor.setDescriptor(jsBoolean(true), ReadOnly | DontDelete | DontEnum); return true; } entry = JSDOMWindowPrototype::s_info.propHashTable(exec)->entry(exec, propertyName); if (entry && (entry->attributes() & JSC::Function) && entry->function() == jsDOMWindowPrototypeFunctionClose) { PropertySlot slot; slot.setCustom(thisObject, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>); descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum); return true; } descriptor.setUndefined(); return true; } entry = JSDOMWindow::s_info.propHashTable(exec)->entry(exec, propertyName); if (entry) { PropertySlot slot; slot.setCustom(thisObject, entry->propertyGetter()); descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes()); return true; } // Check for child frames by name before built-in properties to // match Mozilla. This does not match IE, but some sites end up // naming frames things that conflict with window properties that // are in Moz but not IE. Since we have some of these, we have to do // it the Moz way. if (thisObject->impl()->frame()->tree()->scopedChild(propertyNameToAtomicString(propertyName))) { PropertySlot slot; slot.setCustom(thisObject, childFrameGetter); descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum); return true; } unsigned i = propertyName.asIndex(); if (i < thisObject->impl()->frame()->tree()->scopedChildCount()) { ASSERT(i != PropertyName::NotAnIndex); PropertySlot slot; slot.setCustomIndex(thisObject, i, indexGetter); descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum); return true; } // Allow shortcuts like 'Image1' instead of document.images.Image1 Document* document = thisObject->impl()->frame()->document(); if (document->isHTMLDocument()) { AtomicStringImpl* atomicPropertyName = findAtomicString(propertyName); if (atomicPropertyName && (toHTMLDocument(document)->hasNamedItem(atomicPropertyName) || document->hasElementWithId(atomicPropertyName))) { PropertySlot slot; slot.setCustom(thisObject, namedItemGetter); descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum); return true; } } return Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor); }