diff options
Diffstat (limited to 'src/3rdparty/webkit/WebCore/bindings/js/JSDOMWindowBase.cpp')
-rw-r--r-- | src/3rdparty/webkit/WebCore/bindings/js/JSDOMWindowBase.cpp | 875 |
1 files changed, 875 insertions, 0 deletions
diff --git a/src/3rdparty/webkit/WebCore/bindings/js/JSDOMWindowBase.cpp b/src/3rdparty/webkit/WebCore/bindings/js/JSDOMWindowBase.cpp new file mode 100644 index 0000000..10fc022 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/bindings/js/JSDOMWindowBase.cpp @@ -0,0 +1,875 @@ +/* + * Copyright (C) 2000 Harri Porten (porten@kde.org) + * Copyright (C) 2006 Jon Shier (jshier@iastate.edu) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reseved. + * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#include "config.h" +#include "JSDOMWindowBase.h" + +#include "CString.h" +#include "Console.h" +#include "DOMTimer.h" +#include "DOMWindow.h" +#include "Element.h" +#include "EventListener.h" +#include "ExceptionCode.h" +#include "FloatRect.h" +#include "Frame.h" +#include "FrameLoadRequest.h" +#include "FrameLoader.h" +#include "FrameTree.h" +#include "GCController.h" +#include "HTMLDocument.h" +#include "InspectorController.h" +#include "JSAudioConstructor.h" +#include "JSDOMWindowCustom.h" +#include "JSEvent.h" +#include "JSEventListener.h" +#include "JSHTMLCollection.h" +#include "JSImageConstructor.h" +#include "JSMessageChannelConstructor.h" +#include "JSNode.h" +#include "JSOptionConstructor.h" +#include "JSWorkerConstructor.h" +#include "JSXMLHttpRequestConstructor.h" +#include "JSXSLTProcessorConstructor.h" +#include "Logging.h" +#include "MediaPlayer.h" +#include "Page.h" +#include "PlatformScreen.h" +#include "PluginInfoStore.h" +#include "RenderView.h" +#include "ScheduledAction.h" +#include "ScriptController.h" +#include "SecurityOrigin.h" +#include "Settings.h" +#include "WindowFeatures.h" +#include "htmlediting.h" +#include <runtime/Error.h> +#include <runtime/JSLock.h> +#include <wtf/AlwaysInline.h> +#include <wtf/MathExtras.h> + +using namespace JSC; + +static JSValuePtr windowProtoFuncOpen(ExecState*, JSObject*, JSValuePtr, const ArgList&); +static JSValuePtr windowProtoFuncShowModalDialog(ExecState*, JSObject*, JSValuePtr, const ArgList&); +static JSValuePtr windowProtoFuncNotImplemented(ExecState*, JSObject*, JSValuePtr, const ArgList&); + +static JSValuePtr jsDOMWindowBaseCrypto(ExecState*, const Identifier&, const PropertySlot&); +static JSValuePtr jsDOMWindowBaseEvent(ExecState*, const Identifier&, const PropertySlot&); +static void setJSDOMWindowBaseEvent(ExecState*, JSObject*, JSValuePtr); + +// Constructors +static JSValuePtr jsDOMWindowBaseAudio(ExecState*, const Identifier&, const PropertySlot&); +static void setJSDOMWindowBaseAudio(ExecState*, JSObject*, JSValuePtr); +static JSValuePtr jsDOMWindowBaseImage(ExecState*, const Identifier&, const PropertySlot&); +static void setJSDOMWindowBaseImage(ExecState*, JSObject*, JSValuePtr); +static JSValuePtr jsDOMWindowBaseMessageChannel(ExecState*, const Identifier&, const PropertySlot&); +static void setJSDOMWindowBaseMessageChannel(ExecState*, JSObject*, JSValuePtr); +static JSValuePtr jsDOMWindowBaseWorker(ExecState*, const Identifier&, const PropertySlot&); +static void setJSDOMWindowBaseWorker(ExecState*, JSObject*, JSValuePtr); +static JSValuePtr jsDOMWindowBaseOption(ExecState*, const Identifier&, const PropertySlot&); +static void setJSDOMWindowBaseOption(ExecState*, JSObject*, JSValuePtr); +static JSValuePtr jsDOMWindowBaseXMLHttpRequest(ExecState*, const Identifier&, const PropertySlot&); +static void setJSDOMWindowBaseXMLHttpRequest(ExecState*, JSObject*, JSValuePtr); +static JSValuePtr jsDOMWindowBaseXSLTProcessor(ExecState*, const Identifier&, const PropertySlot&); +static void setJSDOMWindowBaseXSLTProcessor(ExecState*, JSObject*, JSValuePtr); + +#include "JSDOMWindowBase.lut.h" + +namespace WebCore { + +////////////////////// JSDOMWindowBase Object //////////////////////// + +const ClassInfo JSDOMWindowBase::s_info = { "Window", 0, &JSDOMWindowBaseTable, 0 }; + +/* +@begin JSDOMWindowBaseTable +# -- Functions -- + open windowProtoFuncOpen DontDelete|Function 3 + showModalDialog windowProtoFuncShowModalDialog DontDelete|Function 1 +# Not implemented + captureEvents windowProtoFuncNotImplemented DontDelete|Function 0 + releaseEvents windowProtoFuncNotImplemented DontDelete|Function 0 +# -- Attributes -- + crypto jsDOMWindowBaseCrypto DontDelete|ReadOnly + event jsDOMWindowBaseEvent DontDelete +# -- Constructors -- + Audio jsDOMWindowBaseAudio DontDelete + Image jsDOMWindowBaseImage DontDelete + MessageChannel jsDOMWindowBaseMessageChannel DontDelete + Option jsDOMWindowBaseOption DontDelete + Worker jsDOMWindowBaseWorker DontDelete + XMLHttpRequest jsDOMWindowBaseXMLHttpRequest DontDelete + XSLTProcessor jsDOMWindowBaseXSLTProcessor DontDelete +@end +*/ + +JSDOMWindowBase::JSDOMWindowBaseData::JSDOMWindowBaseData(PassRefPtr<DOMWindow> window, JSDOMWindowShell* shell) + : impl(window) + , returnValueSlot(0) + , shell(shell) +{ +} + +JSDOMWindowBase::JSDOMWindowBase(PassRefPtr<Structure> structure, PassRefPtr<DOMWindow> window, JSDOMWindowShell* shell) + : JSDOMGlobalObject(structure, new JSDOMWindowBaseData(window, shell), shell) +{ + // Time in milliseconds before the script timeout handler kicks in. + setTimeoutTime(10000); + + GlobalPropertyInfo staticGlobals[] = { + GlobalPropertyInfo(Identifier(globalExec(), "document"), jsNull(), DontDelete | ReadOnly), + GlobalPropertyInfo(Identifier(globalExec(), "window"), d()->shell, DontDelete | ReadOnly) + }; + + addStaticGlobals(staticGlobals, sizeof(staticGlobals) / sizeof(GlobalPropertyInfo)); +} + +void JSDOMWindowBase::updateDocument() +{ + ASSERT(d()->impl->document()); + ExecState* exec = globalExec(); + symbolTablePutWithAttributes(Identifier(exec, "document"), toJS(exec, d()->impl->document()), DontDelete | ReadOnly); +} + +JSDOMWindowBase::~JSDOMWindowBase() +{ + if (d()->impl->frame()) + d()->impl->frame()->script()->clearFormerWindow(asJSDOMWindow(this)); +} + +ScriptExecutionContext* JSDOMWindowBase::scriptExecutionContext() const +{ + return d()->impl->document(); +} + +static bool allowPopUp(ExecState* exec) +{ + Frame* frame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame(); + + ASSERT(frame); + if (frame->script()->processingUserGesture()) + return true; + Settings* settings = frame->settings(); + return settings && settings->JavaScriptCanOpenWindowsAutomatically(); +} + +static HashMap<String, String> parseModalDialogFeatures(const String& featuresArg) +{ + HashMap<String, String> map; + + Vector<String> features; + featuresArg.split(';', features); + Vector<String>::const_iterator end = features.end(); + for (Vector<String>::const_iterator it = features.begin(); it != end; ++it) { + String s = *it; + int pos = s.find('='); + int colonPos = s.find(':'); + if (pos >= 0 && colonPos >= 0) + continue; // ignore any strings that have both = and : + if (pos < 0) + pos = colonPos; + if (pos < 0) { + // null string for value means key without value + map.set(s.stripWhiteSpace().lower(), String()); + } else { + String key = s.left(pos).stripWhiteSpace().lower(); + String val = s.substring(pos + 1).stripWhiteSpace().lower(); + int spacePos = val.find(' '); + if (spacePos != -1) + val = val.left(spacePos); + map.set(key, val); + } + } + + return map; +} + +static Frame* createWindow(ExecState* exec, Frame* openerFrame, const String& url, + const String& frameName, const WindowFeatures& windowFeatures, JSValuePtr dialogArgs) +{ + Frame* activeFrame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame(); + ASSERT(activeFrame); + + ResourceRequest request; + + request.setHTTPReferrer(activeFrame->loader()->outgoingReferrer()); + FrameLoader::addHTTPOriginIfNeeded(request, activeFrame->loader()->outgoingOrigin()); + FrameLoadRequest frameRequest(request, frameName); + + // FIXME: It's much better for client API if a new window starts with a URL, here where we + // know what URL we are going to open. Unfortunately, this code passes the empty string + // for the URL, but there's a reason for that. Before loading we have to set up the opener, + // openedByDOM, and dialogArguments values. Also, to decide whether to use the URL we currently + // do an allowsAccessFrom call using the window we create, which can't be done before creating it. + // We'd have to resolve all those issues to pass the URL instead of "". + + bool created; + // We pass in the opener frame here so it can be used for looking up the frame name, in case the active frame + // is different from the opener frame, and the name references a frame relative to the opener frame, for example + // "_self" or "_parent". + Frame* newFrame = activeFrame->loader()->createWindow(openerFrame->loader(), frameRequest, windowFeatures, created); + if (!newFrame) + return 0; + + newFrame->loader()->setOpener(openerFrame); + newFrame->loader()->setOpenedByDOM(); + + JSDOMWindow* newWindow = toJSDOMWindow(newFrame); + + if (dialogArgs) + newWindow->putDirect(Identifier(exec, "dialogArguments"), dialogArgs); + + if (!protocolIs(url, "javascript") || newWindow->allowsAccessFrom(exec)) { + KURL completedURL = url.isEmpty() ? KURL("") : activeFrame->document()->completeURL(url); + bool userGesture = activeFrame->script()->processingUserGesture(); + + if (created) + newFrame->loader()->changeLocation(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture); + else if (!url.isEmpty()) + newFrame->loader()->scheduleLocationChange(completedURL.string(), activeFrame->loader()->outgoingReferrer(), false, userGesture); + } + + return newFrame; +} + +static bool canShowModalDialog(const Frame* frame) +{ + if (!frame) + return false; + + Page* page = frame->page(); + if (!page) + return false; + + return page->chrome()->canRunModal(); +} + +static bool canShowModalDialogNow(const Frame* frame) +{ + if (!frame) + return false; + + Page* page = frame->page(); + if (!page) + return false; + + return page->chrome()->canRunModalNow(); +} + +static JSValuePtr showModalDialog(ExecState* exec, Frame* frame, const String& url, JSValuePtr dialogArgs, const String& featureArgs) +{ + if (!canShowModalDialogNow(frame) || !allowPopUp(exec)) + return jsUndefined(); + + const HashMap<String, String> features = parseModalDialogFeatures(featureArgs); + + const bool trusted = false; + + // The following features from Microsoft's documentation are not implemented: + // - default font settings + // - width, height, left, and top specified in units other than "px" + // - edge (sunken or raised, default is raised) + // - dialogHide: trusted && boolFeature(features, "dialoghide"), makes dialog hide when you print + // - help: boolFeature(features, "help", true), makes help icon appear in dialog (what does it do on Windows?) + // - unadorned: trusted && boolFeature(features, "unadorned"); + + if (!frame) + return jsUndefined(); + + FloatRect screenRect = screenAvailableRect(frame->view()); + + WindowFeatures wargs; + wargs.width = WindowFeatures::floatFeature(features, "dialogwidth", 100, screenRect.width(), 620); // default here came from frame size of dialog in MacIE + wargs.widthSet = true; + wargs.height = WindowFeatures::floatFeature(features, "dialogheight", 100, screenRect.height(), 450); // default here came from frame size of dialog in MacIE + wargs.heightSet = true; + + wargs.x = WindowFeatures::floatFeature(features, "dialogleft", screenRect.x(), screenRect.right() - wargs.width, -1); + wargs.xSet = wargs.x > 0; + wargs.y = WindowFeatures::floatFeature(features, "dialogtop", screenRect.y(), screenRect.bottom() - wargs.height, -1); + wargs.ySet = wargs.y > 0; + + if (WindowFeatures::boolFeature(features, "center", true)) { + if (!wargs.xSet) { + wargs.x = screenRect.x() + (screenRect.width() - wargs.width) / 2; + wargs.xSet = true; + } + if (!wargs.ySet) { + wargs.y = screenRect.y() + (screenRect.height() - wargs.height) / 2; + wargs.ySet = true; + } + } + + wargs.dialog = true; + wargs.resizable = WindowFeatures::boolFeature(features, "resizable"); + wargs.scrollbarsVisible = WindowFeatures::boolFeature(features, "scroll", true); + wargs.statusBarVisible = WindowFeatures::boolFeature(features, "status", !trusted); + wargs.menuBarVisible = false; + wargs.toolBarVisible = false; + wargs.locationBarVisible = false; + wargs.fullscreen = false; + + Frame* dialogFrame = createWindow(exec, frame, url, "", wargs, dialogArgs); + if (!dialogFrame) + return jsUndefined(); + + JSDOMWindow* dialogWindow = toJSDOMWindow(dialogFrame); + + // Get the return value either just before clearing the dialog window's + // properties (in JSDOMWindowBase::clear), or when on return from runModal. + JSValuePtr returnValue = noValue(); + dialogWindow->setReturnValueSlot(&returnValue); + dialogFrame->page()->chrome()->runModal(); + dialogWindow->setReturnValueSlot(0); + + // If we don't have a return value, get it now. + // Either JSDOMWindowBase::clear was not called yet, or there was no return value, + // and in that case, there's no harm in trying again (no benefit either). + if (!returnValue) + returnValue = dialogWindow->getDirect(Identifier(exec, "returnValue")); + + return returnValue ? returnValue : jsUndefined(); +} + +} // namespace WebCore + +using namespace WebCore; + +JSValuePtr jsDOMWindowBaseCrypto(ExecState*, const Identifier&, const PropertySlot&) +{ + return jsUndefined(); // FIXME: implement this +} + +JSValuePtr jsDOMWindowBaseEvent(ExecState* exec, const Identifier&, const PropertySlot& slot) +{ + if (!static_cast<JSDOMWindowBase*>(asObject(slot.slotBase()))->allowsAccessFrom(exec)) + return jsUndefined(); + if (!static_cast<JSDOMWindowBase*>(asObject(slot.slotBase()))->currentEvent()) + return jsUndefined(); + return toJS(exec, static_cast<JSDOMWindowBase*>(asObject(slot.slotBase()))->currentEvent()); +} + +JSValuePtr jsDOMWindowBaseImage(ExecState* exec, const Identifier&, const PropertySlot& slot) +{ + if (!static_cast<JSDOMWindowBase*>(asObject(slot.slotBase()))->allowsAccessFrom(exec)) + return jsUndefined(); + return getDOMConstructor<JSImageConstructor>(exec, static_cast<JSDOMWindowBase*>(asObject(slot.slotBase()))); +} + +JSValuePtr jsDOMWindowBaseMessageChannel(ExecState* exec, const Identifier&, const PropertySlot& slot) +{ +#if ENABLE(CHANNEL_MESSAGING) + if (!static_cast<JSDOMWindowBase*>(asObject(slot.slotBase()))->allowsAccessFrom(exec)) + return jsUndefined(); + return getDOMConstructor<JSMessageChannelConstructor>(exec, static_cast<JSDOMWindowBase*>(asObject(slot.slotBase()))); +#else + return jsUndefined(); +#endif +} + +JSValuePtr jsDOMWindowBaseOption(ExecState* exec, const Identifier&, const PropertySlot& slot) +{ + if (!static_cast<JSDOMWindowBase*>(asObject(slot.slotBase()))->allowsAccessFrom(exec)) + return jsUndefined(); + return getDOMConstructor<JSOptionConstructor>(exec, static_cast<JSDOMWindowBase*>(asObject(slot.slotBase()))); +} + +JSValuePtr jsDOMWindowBaseXMLHttpRequest(ExecState* exec, const Identifier&, const PropertySlot& slot) +{ + if (!static_cast<JSDOMWindowBase*>(asObject(slot.slotBase()))->allowsAccessFrom(exec)) + return jsUndefined(); + return getDOMConstructor<JSXMLHttpRequestConstructor>(exec, static_cast<JSDOMWindowBase*>(asObject(slot.slotBase()))); +} + +JSValuePtr jsDOMWindowBaseAudio(ExecState* exec, const Identifier&, const PropertySlot& slot) +{ +#if ENABLE(VIDEO) + if (!static_cast<JSDOMWindowBase*>(asObject(slot.slotBase()))->allowsAccessFrom(exec)) + return jsUndefined(); + if (!MediaPlayer::isAvailable()) + return jsUndefined(); + return getDOMConstructor<JSAudioConstructor>(exec, static_cast<JSDOMWindowBase*>(asObject(slot.slotBase()))); +#else + return jsUndefined(); +#endif +} + +JSValuePtr jsDOMWindowBaseWorker(ExecState* exec, const Identifier&, const PropertySlot& slot) +{ +#if ENABLE(WORKERS) + if (!static_cast<JSDOMWindowBase*>(asObject(slot.slotBase()))->allowsAccessFrom(exec)) + return jsUndefined(); + return getDOMConstructor<JSWorkerConstructor>(exec); +#else + return jsUndefined(); +#endif +} + +JSValuePtr jsDOMWindowBaseXSLTProcessor(ExecState* exec, const Identifier&, const PropertySlot& slot) +{ +#if ENABLE(XSLT) + if (!static_cast<JSDOMWindowBase*>(asObject(slot.slotBase()))->allowsAccessFrom(exec)) + return jsUndefined(); + return getDOMConstructor<JSXSLTProcessorConstructor>(exec); +#else + return jsUndefined(); +#endif +} + +void setJSDOMWindowBaseEvent(ExecState* exec, JSObject* thisObject, JSValuePtr value) +{ + if (!static_cast<JSDOMWindowBase*>(thisObject)->allowsAccessFrom(exec)) + return; + // Shadowing a built-in constructor + static_cast<JSDOMWindowBase*>(thisObject)->putDirect(Identifier(exec, "Event"), value); +} + +void setJSDOMWindowBaseAudio(ExecState* exec, JSObject* thisObject, JSValuePtr value) +{ + if (!static_cast<JSDOMWindowBase*>(thisObject)->allowsAccessFrom(exec)) + return; + // Shadowing a built-in constructor + static_cast<JSDOMWindowBase*>(thisObject)->putDirect(Identifier(exec, "Audio"), value); +} + +void setJSDOMWindowBaseImage(ExecState* exec, JSObject* thisObject, JSValuePtr value) +{ + if (!static_cast<JSDOMWindowBase*>(thisObject)->allowsAccessFrom(exec)) + return; + // Shadowing a built-in constructor + static_cast<JSDOMWindowBase*>(thisObject)->putDirect(Identifier(exec, "Image"), value); +} + +void setJSDOMWindowBaseMessageChannel(ExecState* exec, JSObject* thisObject, JSValuePtr value) +{ + if (!static_cast<JSDOMWindowBase*>(thisObject)->allowsAccessFrom(exec)) + return; + // Shadowing a built-in constructor + static_cast<JSDOMWindowBase*>(thisObject)->putDirect(Identifier(exec, "MessageChannel"), value); +} + +void setJSDOMWindowBaseOption(ExecState* exec, JSObject* thisObject, JSValuePtr value) +{ + if (!static_cast<JSDOMWindowBase*>(thisObject)->allowsAccessFrom(exec)) + return; + // Shadowing a built-in constructor + static_cast<JSDOMWindowBase*>(thisObject)->putDirect(Identifier(exec, "Option"), value); +} + +void setJSDOMWindowBaseWorker(ExecState* exec, JSObject* thisObject, JSValuePtr value) +{ + if (!static_cast<JSDOMWindowBase*>(thisObject)->allowsAccessFrom(exec)) + return; + // Shadowing a built-in constructor + static_cast<JSDOMWindowBase*>(thisObject)->putDirect(Identifier(exec, "Worker"), value); +} + +void setJSDOMWindowBaseXMLHttpRequest(ExecState* exec, JSObject* thisObject, JSValuePtr value) +{ + if (!static_cast<JSDOMWindowBase*>(thisObject)->allowsAccessFrom(exec)) + return; + // Shadowing a built-in constructor + static_cast<JSDOMWindowBase*>(thisObject)->putDirect(Identifier(exec, "XMLHttpRequest"), value); +} + +void setJSDOMWindowBaseXSLTProcessor(ExecState* exec, JSObject* thisObject, JSValuePtr value) +{ + if (!static_cast<JSDOMWindowBase*>(thisObject)->allowsAccessFrom(exec)) + return; + // Shadowing a built-in constructor + static_cast<JSDOMWindowBase*>(thisObject)->putDirect(Identifier(exec, "XSLTProcessor"), value); +} + +namespace WebCore { + +JSValuePtr JSDOMWindowBase::childFrameGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot) +{ + return toJS(exec, static_cast<JSDOMWindowBase*>(asObject(slot.slotBase()))->impl()->frame()->tree()->child(AtomicString(propertyName))->domWindow()); +} + +JSValuePtr JSDOMWindowBase::indexGetter(ExecState* exec, const Identifier&, const PropertySlot& slot) +{ + return toJS(exec, static_cast<JSDOMWindowBase*>(asObject(slot.slotBase()))->impl()->frame()->tree()->child(slot.index())->domWindow()); +} + +JSValuePtr JSDOMWindowBase::namedItemGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot) +{ + JSDOMWindowBase* thisObj = static_cast<JSDOMWindowBase*>(asObject(slot.slotBase())); + Document* doc = thisObj->impl()->frame()->document(); + ASSERT(thisObj->allowsAccessFrom(exec)); + ASSERT(doc); + ASSERT(doc->isHTMLDocument()); + + RefPtr<HTMLCollection> collection = doc->windowNamedItems(propertyName); + if (collection->length() == 1) + return toJS(exec, collection->firstItem()); + return toJS(exec, collection.get()); +} + +bool JSDOMWindowBase::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + // 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 (impl()->frame()->tree()->child(propertyName)) { + slot.setCustom(this, childFrameGetter); + return true; + } + + const HashEntry* entry = JSDOMWindowBaseTable.entry(exec, propertyName); + if (entry) { + if (entry->attributes() & Function) { + if (entry->function() == windowProtoFuncShowModalDialog) { + if (!canShowModalDialog(impl()->frame())) + return false; + } + if (allowsAccessFrom(exec)) + setUpStaticFunctionSlot(exec, entry, this, propertyName, slot); + else + slot.setUndefined(); + } else + slot.setCustom(this, entry->propertyGetter()); + return true; + } + + // Do prototype lookup early so that functions and attributes in the prototype can have + // precedence over the index and name getters. + JSValuePtr proto = prototype(); + if (proto->isObject()) { + if (asObject(proto)->getPropertySlot(exec, propertyName, slot)) { + if (!allowsAccessFrom(exec)) + slot.setUndefined(); + return true; + } + } + + // FIXME: Search the whole frame hierachy somewhere around here. + // We need to test the correct priority order. + + // allow window[1] or parent[1] etc. (#56983) + bool ok; + unsigned i = propertyName.toArrayIndex(&ok); + if (ok && i < impl()->frame()->tree()->childCount()) { + slot.setCustomIndex(this, i, indexGetter); + return true; + } + + if (!allowsAccessFrom(exec)) { + slot.setUndefined(); + return true; + } + + // Allow shortcuts like 'Image1' instead of document.images.Image1 + Document* document = impl()->frame()->document(); + if (document && document->isHTMLDocument()) { + AtomicStringImpl* atomicPropertyName = AtomicString::find(propertyName); + if (atomicPropertyName && (static_cast<HTMLDocument*>(document)->hasNamedItem(atomicPropertyName) || document->hasElementWithId(atomicPropertyName))) { + slot.setCustom(this, namedItemGetter); + return true; + } + } + + return Base::getOwnPropertySlot(exec, propertyName, slot); +} + +void JSDOMWindowBase::put(ExecState* exec, const Identifier& propertyName, JSValuePtr value, PutPropertySlot& slot) +{ + const HashEntry* entry = JSDOMWindowBaseTable.entry(exec, propertyName); + if (entry) { + if (entry->attributes() & Function) { + if (allowsAccessFrom(exec)) + Base::put(exec, propertyName, value, slot); + return; + } + if (entry->attributes() & ReadOnly) + return; + } + + if (allowsAccessFrom(exec)) + Base::put(exec, propertyName, value, slot); +} + +String JSDOMWindowBase::crossDomainAccessErrorMessage(const JSGlobalObject* other) const +{ + KURL originURL = asJSDOMWindow(other)->impl()->url(); + KURL targetURL = impl()->frame()->document()->url(); + if (originURL.isNull() || targetURL.isNull()) + return String(); + + // FIXME: this error message should contain more specifics of why the same origin check has failed. + return String::format("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains, protocols and ports must match.\n", + targetURL.string().utf8().data(), originURL.string().utf8().data()); +} + +void JSDOMWindowBase::printErrorMessage(const String& message) const +{ + if (message.isEmpty()) + return; + + Frame* frame = impl()->frame(); + if (!frame) + return; + + Settings* settings = frame->settings(); + if (!settings) + return; + + if (settings->privateBrowsingEnabled()) + return; + + impl()->console()->addMessage(JSMessageSource, ErrorMessageLevel, message, 1, String()); // FIXME: provide a real line number and source URL. +} + +ExecState* JSDOMWindowBase::globalExec() +{ + // We need to make sure that any script execution happening in this + // frame does not destroy it + if (Frame *frame = impl()->frame()) + frame->keepAlive(); + return Base::globalExec(); +} + +bool JSDOMWindowBase::supportsProfiling() const +{ + Frame* frame = impl()->frame(); + if (!frame) + return false; + + Page* page = frame->page(); + if (!page) + return false; + + return page->inspectorController()->profilerEnabled(); +} + +bool JSDOMWindowBase::shouldInterruptScript() const +{ + ASSERT(impl()->frame()); + Page* page = impl()->frame()->page(); + + // See <rdar://problem/5479443>. We don't think that page can ever be NULL + // in this case, but if it is, we've gotten into a state where we may have + // hung the UI, with no way to ask the client whether to cancel execution. + // For now, our solution is just to cancel execution no matter what, + // ensuring that we never hang. We might want to consider other solutions + // if we discover problems with this one. + ASSERT(page); + if (!page) + return true; + + return page->chrome()->shouldInterruptJavaScript(); +} + +void JSDOMWindowBase::clearHelperObjectProperties() +{ + setCurrentEvent(0); +} + +void JSDOMWindowBase::clear() +{ + JSLock lock(false); + + if (d()->returnValueSlot && !*d()->returnValueSlot) + *d()->returnValueSlot = getDirect(Identifier(globalExec(), "returnValue")); + + clearHelperObjectProperties(); +} + +JSObject* JSDOMWindowBase::toThisObject(ExecState*) const +{ + return shell(); +} + +JSDOMWindowShell* JSDOMWindowBase::shell() const +{ + return d()->shell; +} + +JSGlobalData* JSDOMWindowBase::commonJSGlobalData() +{ + static JSGlobalData* globalData = JSGlobalData::createLeaked().releaseRef(); + return globalData; +} + +} // namespace WebCore + +using namespace WebCore; + +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 +} + +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))); +} + +JSValuePtr windowProtoFuncNotImplemented(ExecState* exec, JSObject*, JSValuePtr thisValue, const ArgList&) +{ + if (!toJSDOMWindow(thisValue)) + return throwError(exec, TypeError); + return jsUndefined(); +} + +namespace WebCore { + +void JSDOMWindowBase::setReturnValueSlot(JSValuePtr* slot) +{ + d()->returnValueSlot = slot; +} + +////////////////////// timeouts //////////////////////// + +int JSDOMWindowBase::installTimeout(ScheduledAction* a, int t, bool singleShot) +{ + return DOMTimer::install(scriptExecutionContext(), a, t, singleShot); +} + +int JSDOMWindowBase::installTimeout(const UString& handler, int t, bool singleShot) +{ + return installTimeout(new ScheduledAction(handler), t, singleShot); +} + +int JSDOMWindowBase::installTimeout(ExecState* exec, JSValuePtr func, const ArgList& args, int t, bool singleShot) +{ + return installTimeout(new ScheduledAction(exec, func, args), t, singleShot); +} + +void JSDOMWindowBase::removeTimeout(int timeoutId) +{ + DOMTimer::removeById(scriptExecutionContext(), timeoutId); +} + +void JSDOMWindowBase::disconnectFrame() +{ +} + +JSValuePtr toJS(ExecState*, DOMWindow* domWindow) +{ + if (!domWindow) + return jsNull(); + Frame* frame = domWindow->frame(); + if (!frame) + return jsNull(); + return frame->script()->windowShell(); +} + +JSDOMWindow* toJSDOMWindow(Frame* frame) +{ + if (!frame) + return 0; + return frame->script()->windowShell()->window(); +} + +JSDOMWindow* toJSDOMWindow(JSValuePtr value) +{ + if (!value->isObject()) + return 0; + const ClassInfo* classInfo = asObject(value)->classInfo(); + if (classInfo == &JSDOMWindow::s_info) + return static_cast<JSDOMWindow*>(asObject(value)); + if (classInfo == &JSDOMWindowShell::s_info) + return static_cast<JSDOMWindowShell*>(asObject(value))->window(); + return 0; +} + +} // namespace WebCore |