#include "config.h"
#include "kjs_window.h"
#include "DOMWindow.h"
#include "Element.h"
#include "EventListener.h"
#include "EventNames.h"
#include "FloatRect.h"
#include "Frame.h"
#include "FrameTree.h"
#include "HTMLDocument.h"
#include "JSCSSRule.h"
#include "JSCSSValue.h"
#include "JSDOMParser.h"
#include "JSDOMWindow.h"
#include "JSEvent.h"
#include "JSHTMLOptionElementConstructor.h"
#include "JSMutationEvent.h"
#include "JSNode.h"
#include "JSNodeFilter.h"
#include "JSRange.h"
#include "JSXMLHttpRequest.h"
#include "JSXMLSerializer.h"
#include "Settings.h"
#include "Logging.h"
#include "Page.h"
#include "PlugInInfoStore.h"
#include "RenderView.h"
#include "Screen.h"
#include "SelectionController.h"
#include "htmlediting.h"
#include "kjs_css.h"
#include "kjs_events.h"
#include "kjs_navigator.h"
#include "kjs_proxy.h"
#include "kjs_traversal.h"
#include <wtf/MathExtras.h>
#if KHTML_XSLT
#include "JSXSLTProcessor.h"
#endif
#include "WKContentObservation.h"
#include "WebCoreThread.h"
using namespace WebCore;
using namespace EventNames;
namespace KJS {
static int lastUsedTimeoutId;
class DOMWindowTimer : public TimerBase {
public:
DOMWindowTimer(int timeoutId, Window* o, ScheduledAction* a)
: m_timeoutId(timeoutId), m_object(o), m_action(a) { }
virtual ~DOMWindowTimer() { delete m_action; }
int timeoutId() const { return m_timeoutId; }
ScheduledAction* action() const { return m_action; }
ScheduledAction* takeAction() { ScheduledAction* a = m_action; m_action = 0; return a; }
private:
virtual void fired();
int m_timeoutId;
Window* m_object;
ScheduledAction* m_action;
};
class PausedTimeout {
public:
int timeoutId;
double nextFireInterval;
double repeatInterval;
ScheduledAction *action;
};
class History : public DOMObject {
friend class HistoryFunc;
public:
History(ExecState *exec, Frame *f)
: m_frame(f)
{
setPrototype(exec->lexicalInterpreter()->builtinObjectPrototype());
}
virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&);
JSValue *getValueProperty(ExecState *exec, int token) const;
virtual const ClassInfo* classInfo() const { return &info; }
static const ClassInfo info;
enum { Back, Forward, Go, Length };
virtual UString toString(ExecState *exec) const;
void disconnectFrame() { m_frame = 0; }
private:
Frame* m_frame;
};
class FrameArray : public DOMObject {
public:
FrameArray(ExecState *exec, Frame *f)
: m_frame(f)
{
setPrototype(exec->lexicalInterpreter()->builtinObjectPrototype());
}
virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&);
JSValue *getValueProperty(ExecState *exec, int token);
virtual UString toString(ExecState *exec) const;
enum { Length, Location };
void disconnectFrame() { m_frame = 0; }
private:
static JSValue *indexGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&);
static JSValue *nameGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&);
virtual const ClassInfo* classInfo() const { return &info; }
static const ClassInfo info;
Frame* m_frame;
};
}
#include "kjs_window.lut.h"
namespace KJS {
const ClassInfo Screen::info = { "Screen", 0, &ScreenTable, 0 };
Screen::Screen(ExecState* exec, Frame* f)
: m_frame(f)
{
setPrototype(exec->lexicalInterpreter()->builtinObjectPrototype());
}
bool Screen::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
{
return getStaticValueSlot<Screen, JSObject>(exec, &ScreenTable, this, propertyName, slot);
}
JSValue* Screen::getValueProperty(ExecState*, int token) const
{
Widget* widget = m_frame ? m_frame->view() : 0;
switch (token) {
case Height:
return jsNumber(screenRect(widget).height());
case Width:
return jsNumber(screenRect(widget).width());
case ColorDepth:
case PixelDepth:
return jsNumber(screenDepth(widget));
case AvailLeft:
return jsNumber(usableScreenRect(widget).x() - screenRect(widget).x());
case AvailTop:
return jsNumber(usableScreenRect(widget).y() - screenRect(widget).y());
case AvailHeight:
return jsNumber(usableScreenRect(widget).height());
case AvailWidth:
return jsNumber(usableScreenRect(widget).width());
default:
return jsUndefined();
}
}
const ClassInfo Window::info = { "Window", 0, &WindowTable, 0 };
KJS_IMPLEMENT_PROTOFUNC(WindowFunc)
Window::Window(DOMWindow* window)
: m_frame(window->frame())
, screen(0)
, history(0)
, frames(0)
, loc(0)
, m_selection(0)
, m_locationbar(0)
, m_menubar(0)
, m_personalbar(0)
, m_scrollbars(0)
, m_statusbar(0)
, m_toolbar(0)
, m_evt(0)
, m_returnValueSlot(0)
{
}
Window::~Window()
{
clearAllTimeouts();
ListenersMap::iterator i2 = jsEventListeners.begin();
ListenersMap::iterator e2 = jsEventListeners.end();
for (; i2 != e2; ++i2)
i2->second->clearWindowObj();
i2 = jsHTMLEventListeners.begin();
e2 = jsHTMLEventListeners.end();
for (; i2 != e2; ++i2)
i2->second->clearWindowObj();
UnprotectedListenersMap::iterator i1 = jsUnprotectedEventListeners.begin();
UnprotectedListenersMap::iterator e1 = jsUnprotectedEventListeners.end();
for (; i1 != e1; ++i1)
i1->second->clearWindowObj();
i1 = jsUnprotectedHTMLEventListeners.begin();
e1 = jsUnprotectedHTMLEventListeners.end();
for (; i1 != e1; ++i1)
i1->second->clearWindowObj();
}
DOMWindow* Window::impl() const
{
return m_frame->domWindow();
}
ScriptInterpreter *Window::interpreter() const
{
return m_frame->jScript()->interpreter();
}
Window *Window::retrieveWindow(Frame *f)
{
JSObject *o = retrieve(f)->getObject();
ASSERT(o || !f->jScriptEnabled());
return static_cast<Window *>(o);
}
Window *Window::retrieveActive(ExecState *exec)
{
JSValue *imp = exec->dynamicInterpreter()->globalObject();
ASSERT(imp);
return static_cast<Window*>(imp);
}
JSValue *Window::retrieve(Frame *p)
{
ASSERT(p);
if (KJSProxy *proxy = p->jScript())
return proxy->interpreter()->globalObject();
return jsUndefined(); }
Location *Window::location() const
{
if (!loc)
loc = new Location(m_frame);
return loc;
}
Selection *Window::selection() const
{
if (!m_selection)
m_selection = new Selection(m_frame);
return m_selection;
}
BarInfo *Window::locationbar(ExecState *exec) const
{
if (!m_locationbar)
m_locationbar = new BarInfo(exec, m_frame, BarInfo::Locationbar);
return m_locationbar;
}
BarInfo *Window::menubar(ExecState *exec) const
{
if (!m_menubar)
m_menubar = new BarInfo(exec, m_frame, BarInfo::Menubar);
return m_menubar;
}
BarInfo *Window::personalbar(ExecState *exec) const
{
if (!m_personalbar)
m_personalbar = new BarInfo(exec, m_frame, BarInfo::Personalbar);
return m_personalbar;
}
BarInfo *Window::statusbar(ExecState *exec) const
{
if (!m_statusbar)
m_statusbar = new BarInfo(exec, m_frame, BarInfo::Statusbar);
return m_statusbar;
}
BarInfo *Window::toolbar(ExecState *exec) const
{
if (!m_toolbar)
m_toolbar = new BarInfo(exec, m_frame, BarInfo::Toolbar);
return m_toolbar;
}
BarInfo *Window::scrollbars(ExecState *exec) const
{
if (!m_scrollbars)
m_scrollbars = new BarInfo(exec, m_frame, BarInfo::Scrollbars);
return m_scrollbars;
}
void Window::mark()
{
JSObject::mark();
if (screen && !screen->marked())
screen->mark();
if (history && !history->marked())
history->mark();
if (frames && !frames->marked())
frames->mark();
if (loc && !loc->marked())
loc->mark();
if (m_selection && !m_selection->marked())
m_selection->mark();
if (m_locationbar && !m_locationbar->marked())
m_locationbar->mark();
if (m_menubar && !m_menubar->marked())
m_menubar->mark();
if (m_personalbar && !m_personalbar->marked())
m_personalbar->mark();
if (m_scrollbars && !m_scrollbars->marked())
m_scrollbars->mark();
if (m_statusbar && !m_statusbar->marked())
m_statusbar->mark();
if (m_toolbar && !m_toolbar->marked())
m_toolbar->mark();
}
UString Window::toString(ExecState *exec) const
{
if (!m_frame || !Window::retrieveWindow(m_frame)->isSafeScript(exec))
return UString();
return "[object Window]";
}
static bool allowPopUp(ExecState *exec, Window *window)
{
return window->frame()
&& (window->frame()->settings()->JavaScriptCanOpenWindowsAutomatically()
|| static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture());
}
static HashMap<String, String> parseModalDialogFeatures(ExecState *exec, JSValue *featuresArg)
{
HashMap<String, String> map;
DeprecatedStringList features = DeprecatedStringList::split(';', featuresArg->toString(exec));
DeprecatedStringList::ConstIterator end = features.end();
for (DeprecatedStringList::ConstIterator it = features.begin(); it != end; ++it) {
DeprecatedString s = *it;
int pos = s.find('=');
int colonPos = s.find(':');
if (pos >= 0 && colonPos >= 0)
continue; if (pos < 0)
pos = colonPos;
if (pos < 0) {
map.set(s.stripWhiteSpace().lower(), String());
} else {
DeprecatedString key = s.left(pos).stripWhiteSpace().lower();
DeprecatedString val = s.mid(pos + 1).stripWhiteSpace().lower();
int spacePos = val.find(' ');
if (spacePos != -1)
val = val.left(spacePos);
map.set(key, val);
}
}
return map;
}
static bool boolFeature(const HashMap<String, String>& features, const char* key, bool defaultValue = false)
{
HashMap<String, String>::const_iterator it = features.find(key);
if (it == features.end())
return defaultValue;
const String& value = it->second;
return value.isNull() || value == "1" || value == "yes" || value == "on";
}
static float floatFeature(const HashMap<String, String> &features, const char *key, float min, float max, float defaultValue)
{
HashMap<String, String>::const_iterator it = features.find(key);
if (it == features.end())
return defaultValue;
DeprecatedString value = it->second.deprecatedString();
bool ok;
double d = value.toDouble(&ok);
if ((d == 0 && !ok) || isnan(d))
return defaultValue;
if (d < min || max <= min)
return min;
if (d > max)
return max;
return static_cast<int>(d);
}
static Frame *createNewWindow(ExecState *exec, Window *openerWindow, const DeprecatedString &URL,
const DeprecatedString &frameName, const WindowArgs &windowArgs, JSValue *dialogArgs)
{
Frame* openerPart = openerWindow->frame();
Frame* activePart = Window::retrieveActive(exec)->frame();
ResourceRequest request(KURL(""));
request.frameName = frameName;
if (activePart)
request.setReferrer(activePart->referrer());
request.m_responseMIMEType = "text/html";
bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
Frame* newFrame = 0;
openerPart->browserExtension()->createNewWindow(request, windowArgs, newFrame, userGesture);
if (!newFrame)
return 0;
Window* newWindow = Window::retrieveWindow(newFrame);
newFrame->setOpener(openerPart);
newFrame->setOpenedByJS(true);
if (dialogArgs)
newWindow->putDirect("dialogArguments", dialogArgs);
Document *activeDoc = activePart ? activePart->document() : 0;
if (!URL.isEmpty() && activeDoc) {
DeprecatedString completedURL = activeDoc->completeURL(URL);
if (!completedURL.startsWith("javascript:", false) || newWindow->isSafeScript(exec)) {
newFrame->changeLocation(completedURL, activePart->referrer(), false, userGesture);
}
}
return newFrame;
}
static bool canShowModalDialog(const Window *window)
{
Frame *frame = window->frame();
return frame && static_cast<BrowserExtension *>(frame->browserExtension())->canRunModal();
}
static bool canShowModalDialogNow(const Window *window)
{
Frame *frame = window->frame();
return frame && static_cast<BrowserExtension *>(frame->browserExtension())->canRunModalNow();
}
static JSValue* showModalDialog(ExecState* exec, Window* openerWindow, const List& args)
{
UString URL = args[0]->toString(exec);
if (!canShowModalDialogNow(openerWindow) || !allowPopUp(exec, openerWindow))
return jsUndefined();
const HashMap<String, String> features = parseModalDialogFeatures(exec, args[2]);
bool trusted = false;
WindowArgs wargs;
FloatRect screenRect = usableScreenRect(openerWindow->frame()->view());
wargs.width = floatFeature(features, "dialogwidth", 100, screenRect.width(), 620); wargs.widthSet = true;
wargs.height = floatFeature(features, "dialogheight", 100, screenRect.height(), 450); wargs.heightSet = true;
wargs.x = floatFeature(features, "dialogleft", screenRect.x(), screenRect.right() - wargs.width, -1);
wargs.xSet = wargs.x > 0;
wargs.y = floatFeature(features, "dialogtop", screenRect.y(), screenRect.bottom() - wargs.height, -1);
wargs.ySet = wargs.y > 0;
if (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 = boolFeature(features, "resizable");
wargs.scrollBarsVisible = boolFeature(features, "scroll", true);
wargs.statusBarVisible = boolFeature(features, "status", !trusted);
wargs.menuBarVisible = false;
wargs.toolBarVisible = false;
wargs.locationBarVisible = false;
wargs.fullscreen = false;
Frame* dialogFrame = createNewWindow(exec, openerWindow, URL, "", wargs, args[1]);
if (!dialogFrame)
return jsUndefined();
Window* dialogWindow = Window::retrieveWindow(dialogFrame);
JSValue* returnValue = 0;
dialogWindow->setReturnValueSlot(&returnValue);
static_cast<BrowserExtension *>(dialogFrame->browserExtension())->runModal();
dialogWindow->setReturnValueSlot(0);
if (!returnValue)
returnValue = dialogWindow->getDirect("returnValue");
return returnValue ? returnValue : jsUndefined();
}
JSValue *Window::getValueProperty(ExecState *exec, int token) const
{
ASSERT(token == Closed || m_frame);
switch (token) {
case Closed:
return jsBoolean(!m_frame);
case Crypto:
if (!isSafeScript(exec))
return jsUndefined();
return jsUndefined(); case DefaultStatus:
if (!isSafeScript(exec))
return jsUndefined();
return jsString(UString(m_frame->jsDefaultStatusBarText()));
case DOMException:
if (!isSafeScript(exec))
return jsUndefined();
return getDOMExceptionConstructor(exec);
case Status:
if (!isSafeScript(exec))
return jsUndefined();
return jsString(UString(m_frame->jsStatusBarText()));
case Frames:
if (!frames)
frames = new FrameArray(exec, m_frame);
return frames;
case History_:
if (!history)
history = new History(exec, m_frame);
return history;
case Event_:
if (!isSafeScript(exec))
return jsUndefined();
if (!m_evt)
return jsUndefined();
return toJS(exec, m_evt);
case InnerHeight:
if (!isSafeScript(exec))
return jsUndefined();
if (!m_frame->view())
return jsUndefined();
return jsNumber(m_frame->view()->actualVisibleHeight());
case InnerWidth:
if (!isSafeScript(exec))
return jsUndefined();
if (!m_frame->view())
return jsUndefined();
return jsNumber(m_frame->view()->actualVisibleWidth());
case Length:
return jsNumber(m_frame->tree()->childCount());
case Location_:
return location();
case Name:
if (!isSafeScript(exec))
return jsUndefined();
return jsString(m_frame->tree()->name());
case Navigator_:
case ClientInformation: {
if (!isSafeScript(exec))
return jsUndefined();
Navigator *n = new Navigator(exec, m_frame);
const_cast<Window *>(this)->putDirect("navigator", n, DontDelete|ReadOnly);
const_cast<Window *>(this)->putDirect("clientInformation", n, DontDelete|ReadOnly);
return n;
}
case Locationbar:
if (!isSafeScript(exec))
return jsUndefined();
return locationbar(exec);
case Menubar:
if (!isSafeScript(exec))
return jsUndefined();
return menubar(exec);
case OffscreenBuffering:
if (!isSafeScript(exec))
return jsUndefined();
return jsBoolean(true);
case Opener:
if (m_frame->opener())
return retrieve(m_frame->opener());
else
return jsNull();
case OuterHeight:
if (!isSafeScript(exec))
return jsUndefined();
return jsNumber(m_frame->page()->windowRect().height());
case OuterWidth:
if (!isSafeScript(exec))
return jsUndefined();
return jsNumber(m_frame->page()->windowRect().width());
case PageXOffset:
if (!isSafeScript(exec))
return jsUndefined();
if (!m_frame->view())
return jsUndefined();
updateLayout();
return jsNumber(m_frame->view()->actualContentsX());
case PageYOffset:
if (!isSafeScript(exec))
return jsUndefined();
if (!m_frame->view())
return jsUndefined();
updateLayout();
return jsNumber(m_frame->view()->actualContentsY());
case Parent:
return retrieve(m_frame->tree()->parent() ? m_frame->tree()->parent() : m_frame);
case Personalbar:
if (!isSafeScript(exec))
return jsUndefined();
return personalbar(exec);
case ScreenLeft:
case ScreenX:
if (!isSafeScript(exec))
return jsUndefined();
return jsNumber(m_frame->page()->windowRect().x());
case ScreenTop:
case ScreenY:
if (!isSafeScript(exec))
return jsUndefined();
return jsNumber(m_frame->page()->windowRect().y());
case Orientation:
if (!isSafeScript(exec))
return jsUndefined();
return jsNumber(m_frame->orientation());
case ScrollX:
if (!isSafeScript(exec))
return jsUndefined();
if (!m_frame->view())
return jsUndefined();
updateLayout();
return jsNumber(m_frame->view()->contentsX());
case ScrollY:
if (!isSafeScript(exec))
return jsUndefined();
if (!m_frame->view())
return jsUndefined();
updateLayout();
return jsNumber(m_frame->view()->contentsY());
case Scrollbars:
if (!isSafeScript(exec))
return jsUndefined();
return scrollbars(exec);
case Statusbar:
if (!isSafeScript(exec))
return jsUndefined();
return statusbar(exec);
case Toolbar:
if (!isSafeScript(exec))
return jsUndefined();
return toolbar(exec);
case Self:
case Window_:
return retrieve(m_frame);
case Top:
return retrieve(m_frame->page()->mainFrame());
case Screen_:
if (!isSafeScript(exec))
return jsUndefined();
if (!screen)
screen = new Screen(exec, m_frame);
return screen;
case Image:
if (!isSafeScript(exec))
return jsUndefined();
return new ImageConstructorImp(exec, m_frame->document());
case Option:
if (!isSafeScript(exec))
return jsUndefined();
return new JSHTMLOptionElementConstructor(exec, m_frame->document());
case XMLHttpRequest:
if (!isSafeScript(exec))
return jsUndefined();
return new JSXMLHttpRequestConstructorImp(exec, m_frame->document());
case XMLSerializer:
if (!isSafeScript(exec))
return jsUndefined();
return new JSXMLSerializerConstructorImp(exec);
case DOMParser_:
if (!isSafeScript(exec))
return jsUndefined();
return new DOMParserConstructorImp(exec, m_frame->document());
#ifdef KHTML_XSLT
case XSLTProcessor_:
if (!isSafeScript(exec))
return jsUndefined();
return new XSLTProcessorConstructorImp(exec);
#else
case XSLTProcessor_:
return jsUndefined();
#endif
case FrameElement:
if (!isSafeScript(exec))
return jsUndefined();
if (Document* doc = m_frame->document())
if (Element* fe = doc->ownerElement())
if (checkNodeSecurity(exec, fe))
return toJS(exec, fe);
return jsUndefined();
}
if (!isSafeScript(exec))
return jsUndefined();
switch (token) {
case Onabort:
return getListener(exec, abortEvent);
case Onblur:
return getListener(exec, blurEvent);
case Onchange:
return getListener(exec, changeEvent);
case Onclick:
return getListener(exec, clickEvent);
case Ondblclick:
return getListener(exec, dblclickEvent);
case Ondragdrop:
return getListener(exec, khtmlDragdropEvent);
case Onerror:
return getListener(exec, errorEvent);
case Onfocus:
return getListener(exec, focusEvent);
case Onkeydown:
return getListener(exec, keydownEvent);
case Onkeypress:
return getListener(exec, keypressEvent);
case Onkeyup:
return getListener(exec, keyupEvent);
case Onload:
return getListener(exec, loadEvent);
case Onmousedown:
return getListener(exec, mousedownEvent);
case Onmousemove:
return getListener(exec, mousemoveEvent);
case Onmouseout:
return getListener(exec, mouseoutEvent);
case Onmouseover:
return getListener(exec, mouseoverEvent);
case Onmouseup:
return getListener(exec, mouseupEvent);
case OnWindowMouseWheel:
return getListener(exec, mousewheelEvent);
case Onmove:
return getListener(exec, khtmlMoveEvent);
case Onreset:
return getListener(exec, resetEvent);
case Onresize:
return getListener(exec,resizeEvent);
case OnOrientationChange:
return getListener(exec,orientationChangeEvent);
case Onscroll:
return getListener(exec,scrollEvent);
case Onsearch:
return getListener(exec,searchEvent);
case Onselect:
return getListener(exec,selectEvent);
case Onsubmit:
return getListener(exec,submitEvent);
case Onbeforeunload:
return getListener(exec, beforeunloadEvent);
case Onunload:
return getListener(exec, unloadEvent);
}
ASSERT(0);
return jsUndefined();
}
JSValue* Window::childFrameGetter(ExecState*, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
{
return retrieve(static_cast<Window*>(slot.slotBase())->m_frame->tree()->child(AtomicString(propertyName)));
}
JSValue* Window::indexGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
{
return retrieve(static_cast<Window*>(slot.slotBase())->m_frame->tree()->child(slot.index()));
}
JSValue *Window::namedItemGetter(ExecState *exec, JSObject *originalObject, const Identifier& propertyName, const PropertySlot& slot)
{
Window *thisObj = static_cast<Window *>(slot.slotBase());
Document *doc = thisObj->m_frame->document();
ASSERT(thisObj->isSafeScript(exec) && doc && doc->isHTMLDocument());
String name = propertyName;
RefPtr<WebCore::HTMLCollection> collection = doc->windowNamedItems(name);
if (collection->length() == 1)
return toJS(exec, collection->firstItem());
else
return getHTMLCollection(exec, collection.get());
}
bool Window::getOverridePropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
{
if (!m_frame) {
if (propertyName == "closed") {
slot.setStaticEntry(this, Lookup::findEntry(&WindowTable, "closed"), staticValueGetter<Window>);
return true;
}
if (propertyName == "close") {
const HashEntry* entry = Lookup::findEntry(&WindowTable, propertyName);
slot.setStaticEntry(this, entry, staticFunctionGetter<WindowFunc>);
return true;
}
slot.setUndefined(this);
return true;
}
JSValue **val = getDirectLocation(propertyName);
if (val) {
if (isSafeScript(exec))
slot.setValueSlot(this, val);
else
slot.setUndefined(this);
return true;
}
return false;
}
bool Window::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
{
AtomicString atomicPropertyName = propertyName;
if (m_frame->tree()->child(atomicPropertyName)) {
slot.setCustom(this, childFrameGetter);
return true;
}
const HashEntry* entry = Lookup::findEntry(&WindowTable, propertyName);
if (entry) {
if (entry->attr & Function) {
switch (entry->value) {
case Focus:
case Blur:
case Close:
slot.setStaticEntry(this, entry, staticFunctionGetter<WindowFunc>);
break;
case ShowModalDialog:
if (!canShowModalDialog(this))
return false;
default:
if (isSafeScript(exec))
slot.setStaticEntry(this, entry, staticFunctionGetter<WindowFunc>);
else
slot.setUndefined(this);
}
} else
slot.setStaticEntry(this, entry, staticValueGetter<Window>);
return true;
}
bool ok;
unsigned i = propertyName.toArrayIndex(&ok);
if (ok && i < m_frame->tree()->childCount()) {
slot.setCustomIndex(this, i, indexGetter);
return true;
}
Document *doc = m_frame->document();
if (isSafeScript(exec) && doc && doc->isHTMLDocument()) {
AtomicString atomicPropertyName = propertyName;
if (static_cast<HTMLDocument*>(doc)->hasNamedItem(atomicPropertyName) || doc->getElementById(atomicPropertyName)) {
slot.setCustom(this, namedItemGetter);
return true;
}
}
if (!isSafeScript(exec)) {
slot.setUndefined(this);
return true;
}
return JSObject::getOwnPropertySlot(exec, propertyName, slot);
}
void Window::put(ExecState* exec, const Identifier &propertyName, JSValue *value, int attr)
{
if ((attr != None && attr != DontDelete)
|| (JSObject::getDirect(propertyName) && isSafeScript(exec))) {
JSObject::put( exec, propertyName, value, attr );
return;
}
const HashEntry* entry = Lookup::findEntry(&WindowTable, propertyName);
if (entry) {
switch(entry->value) {
case Status:
if (isSafeScript(exec))
m_frame->setJSStatusBarText(value->toString(exec));
return;
case DefaultStatus:
if (isSafeScript(exec))
m_frame->setJSDefaultStatusBarText(value->toString(exec));
return;
case Location_: {
Frame* p = Window::retrieveActive(exec)->m_frame;
if (p) {
if (!p->canTargetLoadInFrame(impl()->frame()))
return;
DeprecatedString dstUrl = p->document()->completeURL(DeprecatedString(value->toString(exec)));
if (!dstUrl.startsWith("javascript:", false) || isSafeScript(exec)) {
bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
m_frame->scheduleLocationChange(dstUrl, p->referrer(), !userGesture, userGesture);
}
}
return;
}
case Onabort:
if (isSafeScript(exec))
setListener(exec, abortEvent,value);
return;
case Onblur:
if (isSafeScript(exec))
setListener(exec, blurEvent,value);
return;
case Onchange:
if (isSafeScript(exec))
setListener(exec, changeEvent,value);
return;
case Onclick:
if (isSafeScript(exec))
setListener(exec,clickEvent,value);
return;
case Ondblclick:
if (isSafeScript(exec))
setListener(exec, dblclickEvent,value);
return;
case Ondragdrop:
if (isSafeScript(exec))
setListener(exec,khtmlDragdropEvent,value);
return;
case Onerror:
if (isSafeScript(exec))
setListener(exec, errorEvent, value);
return;
case Onfocus:
if (isSafeScript(exec))
setListener(exec,focusEvent,value);
return;
case Onkeydown:
if (isSafeScript(exec))
setListener(exec,keydownEvent,value);
return;
case Onkeypress:
if (isSafeScript(exec))
setListener(exec,keypressEvent,value);
return;
case Onkeyup:
if (isSafeScript(exec))
setListener(exec,keyupEvent,value);
return;
case Onload:
if (isSafeScript(exec))
setListener(exec,loadEvent,value);
return;
case Onmousedown:
if (isSafeScript(exec))
setListener(exec,mousedownEvent,value);
return;
case Onmousemove:
if (isSafeScript(exec))
setListener(exec,mousemoveEvent,value);
return;
case Onmouseout:
if (isSafeScript(exec))
setListener(exec,mouseoutEvent,value);
return;
case Onmouseover:
if (isSafeScript(exec))
setListener(exec,mouseoverEvent,value);
return;
case Onmouseup:
if (isSafeScript(exec))
setListener(exec,mouseupEvent,value);
return;
case OnWindowMouseWheel:
if (isSafeScript(exec))
setListener(exec, mousewheelEvent,value);
return;
case Onmove:
if (isSafeScript(exec))
setListener(exec,khtmlMoveEvent,value);
return;
case Onreset:
if (isSafeScript(exec))
setListener(exec,resetEvent,value);
return;
case Onresize:
if (isSafeScript(exec))
setListener(exec,resizeEvent,value);
return;
case OnOrientationChange:
if (isSafeScript(exec))
setListener(exec,orientationChangeEvent,value);
return;
case Onscroll:
if (isSafeScript(exec))
setListener(exec,scrollEvent,value);
return;
case Onsearch:
if (isSafeScript(exec))
setListener(exec,searchEvent,value);
return;
case Onselect:
if (isSafeScript(exec))
setListener(exec,selectEvent,value);
return;
case Onsubmit:
if (isSafeScript(exec))
setListener(exec,submitEvent,value);
return;
case Onbeforeunload:
if (isSafeScript(exec))
setListener(exec, beforeunloadEvent, value);
return;
case Onunload:
if (isSafeScript(exec))
setListener(exec, unloadEvent, value);
return;
case Name:
if (isSafeScript(exec))
m_frame->tree()->setName(value->toString(exec));
return;
default:
break;
}
}
if (isSafeScript(exec))
JSObject::put(exec, propertyName, value, attr);
}
bool Window::toBoolean(ExecState *) const
{
return m_frame;
}
void Window::scheduleClose()
{
m_frame->scheduleClose();
}
static bool shouldLoadAsEmptyDocument(const KURL &url)
{
return url.protocol().lower() == "about" || url.isEmpty();
}
bool Window::isSafeScript(const ScriptInterpreter *origin, const ScriptInterpreter *target)
{
if (origin == target)
return true;
Frame *originPart = origin->frame();
Frame *targetPart = target->frame();
if (!targetPart->document())
return true;
WebCore::Document *originDocument = originPart->document();
WebCore::Document *targetDocument = targetPart->document();
if (!targetDocument) {
return false;
}
WebCore::String targetDomain = targetDocument->domain();
if (targetDomain.isNull())
return true;
WebCore::String originDomain = originDocument->domain();
if (shouldLoadAsEmptyDocument(targetPart->url())) {
Frame *ancestorPart = targetPart->opener() ? targetPart->opener() : targetPart->tree()->parent();
while (ancestorPart && shouldLoadAsEmptyDocument(ancestorPart->url())) {
ancestorPart = ancestorPart->tree()->parent();
}
if (ancestorPart)
originDomain = ancestorPart->document()->domain();
}
if ( targetDomain == originDomain )
return true;
if (Interpreter::shouldPrintExceptions()) {
printf("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains must match.\n",
targetDocument->URL().latin1(), originDocument->URL().latin1());
}
String message = String::sprintf("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains must match.\n",
targetDocument->URL().latin1(), originDocument->URL().latin1());
if (Page* page = targetPart->page())
page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, 1, String());
return false;
}
bool Window::isSafeScript(ExecState *exec) const
{
if (!m_frame)
return false;
Frame *activePart = static_cast<ScriptInterpreter *>( exec->dynamicInterpreter() )->frame();
if (!activePart)
return false;
if ( activePart == m_frame )
return true;
WebCore::Document* thisDocument = m_frame->document();
if (!thisDocument)
return true;
WebCore::Document* actDocument = activePart->document();
const KURL& actURL = actDocument->securityPolicyURL();
if (actURL.isLocalFile())
return true;
const KURL& thisURL = thisDocument->securityPolicyURL();
if (equalIgnoringCase(thisURL.protocol(), "data") || equalIgnoringCase(actURL.protocol(), "data"))
return false;
if (thisDocument->domainWasSetInDOM() && actDocument->domainWasSetInDOM()) {
if (thisDocument->domain() == actDocument->domain())
return true;
}
if (equalIgnoringCase(actURL.host(), thisURL.host()) && equalIgnoringCase(actURL.protocol(), thisURL.protocol()) && actURL.port() == thisURL.port())
return true;
if (Interpreter::shouldPrintExceptions()) {
printf("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains, protocols and ports must match.\n",
thisURL.url().latin1(), actURL.url().latin1());
}
String message = String::sprintf("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains, protocols and ports must match.\n",
thisURL.url().latin1(), actURL.url().latin1());
if (Page* page = m_frame->page())
page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, 1, String());
return false;
}
void Window::setListener(ExecState *exec, const AtomicString &eventType, JSValue *func)
{
if (!isSafeScript(exec))
return;
WebCore::Document *doc = m_frame->document();
if (!doc)
return;
doc->setHTMLWindowEventListener(eventType, findOrCreateJSEventListener(func,true));
}
JSValue *Window::getListener(ExecState *exec, const AtomicString &eventType) const
{
if (!isSafeScript(exec))
return jsUndefined();
WebCore::Document *doc = m_frame->document();
if (!doc)
return jsUndefined();
WebCore::EventListener *listener = doc->getHTMLWindowEventListener(eventType);
if (listener && static_cast<JSEventListener*>(listener)->listenerObj())
return static_cast<JSEventListener*>(listener)->listenerObj();
else
return jsNull();
}
JSEventListener* Window::findJSEventListener(JSValue* val, bool html)
{
if (!val->isObject())
return 0;
JSObject* object = static_cast<JSObject*>(val);
ListenersMap& listeners = html ? jsHTMLEventListeners : jsEventListeners;
return listeners.get(object);
}
JSEventListener *Window::findOrCreateJSEventListener(JSValue *val, bool html)
{
JSEventListener *listener = findJSEventListener(val, html);
if (listener)
return listener;
if (!val->isObject())
return 0;
JSObject *object = static_cast<JSObject *>(val);
return new JSEventListener(object, this, html);
}
JSUnprotectedEventListener* Window::findJSUnprotectedEventListener(JSValue* val, bool html)
{
if (!val->isObject())
return 0;
JSObject* object = static_cast<JSObject*>(val);
UnprotectedListenersMap& listeners = html ? jsUnprotectedHTMLEventListeners : jsUnprotectedEventListeners;
return listeners.get(object);
}
JSUnprotectedEventListener *Window::findOrCreateJSUnprotectedEventListener(JSValue *val, bool html)
{
JSUnprotectedEventListener *listener = findJSUnprotectedEventListener(val, html);
if (listener)
return listener;
if (!val->isObject())
return 0;
JSObject *object = static_cast<JSObject *>(val);
return new JSUnprotectedEventListener(object, this, html);
}
void Window::clearHelperObjectProperties()
{
screen = 0;
history = 0;
frames = 0;
loc = 0;
m_selection = 0;
m_locationbar = 0;
m_menubar = 0;
m_personalbar = 0;
m_scrollbars = 0;
m_statusbar = 0;
m_toolbar = 0;
m_evt = 0;
}
void Window::clear()
{
JSLock lock;
if (m_returnValueSlot && !*m_returnValueSlot)
*m_returnValueSlot = getDirect("returnValue");
clearAllTimeouts();
clearProperties();
clearHelperObjectProperties();
setPrototype(JSDOMWindowProto::self());
interpreter()->initGlobalObject();
Collector::collect();
}
void Window::setCurrentEvent(Event *evt)
{
m_evt = evt;
}
static void setWindowFeature(const String& keyString, const String& valueString, WindowArgs& windowArgs)
{
int value;
if (valueString.length() == 0 || valueString == "yes")
value = 1;
else
value = valueString.toInt();
if (keyString == "left" || keyString == "screenx") {
windowArgs.xSet = true;
windowArgs.x = value;
} else if (keyString == "top" || keyString == "screeny") {
windowArgs.ySet = true;
windowArgs.y = value;
} else if (keyString == "width" || keyString == "innerwidth") {
windowArgs.widthSet = true;
windowArgs.width = value;
} else if (keyString == "height" || keyString == "innerheight") {
windowArgs.heightSet = true;
windowArgs.height = value;
} else if (keyString == "menubar")
windowArgs.menuBarVisible = value;
else if (keyString == "toolbar")
windowArgs.toolBarVisible = value;
else if (keyString == "location")
windowArgs.locationBarVisible = value;
else if (keyString == "status")
windowArgs.statusBarVisible = value;
else if (keyString == "resizable")
windowArgs.resizable = value;
else if (keyString == "fullscreen")
windowArgs.fullscreen = value;
else if (keyString == "scrollbars")
windowArgs.scrollBarsVisible = value;
}
static bool isSeparator(::UChar c)
{
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '=' || c == ',' || c == '\0';
}
static void parseWindowFeatures(const String& features, WindowArgs& windowArgs)
{
windowArgs.dialog = false;
windowArgs.fullscreen = false;
windowArgs.xSet = false;
windowArgs.ySet = false;
windowArgs.widthSet = false;
windowArgs.heightSet = false;
if (features.length() == 0) {
windowArgs.menuBarVisible = true;
windowArgs.statusBarVisible = true;
windowArgs.toolBarVisible = true;
windowArgs.locationBarVisible = true;
windowArgs.scrollBarsVisible = true;
windowArgs.resizable = true;
return;
}
windowArgs.menuBarVisible = false;
windowArgs.statusBarVisible = false;
windowArgs.toolBarVisible = false;
windowArgs.locationBarVisible = false;
windowArgs.scrollBarsVisible = false;
windowArgs.resizable = false;
int keyBegin, keyEnd;
int valueBegin, valueEnd;
int i = 0;
int length = features.length();
String buffer = features.lower();
while (i < length) {
while (isSeparator(buffer[i])) {
if (i >= length)
break;
i++;
}
keyBegin = i;
while (!isSeparator(buffer[i]))
i++;
keyEnd = i;
while (buffer[i] != '=') {
if (buffer[i] == ',' || i >= length)
break;
i++;
}
while (isSeparator(buffer[i])) {
if (buffer[i] == ',' || i >= length)
break;
i++;
}
valueBegin = i;
while (!isSeparator(buffer[i]))
i++;
valueEnd = i;
assert(i <= length);
String keyString(buffer.substring(keyBegin, keyEnd - keyBegin));
String valueString(buffer.substring(valueBegin, valueEnd - valueBegin));
setWindowFeature(keyString, valueString, windowArgs);
}
}
static void constrainToVisible(const FloatRect& screen, WindowArgs& windowArgs)
{
windowArgs.heightSet = 0;
}
JSValue *WindowFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
{
if (!thisObj->inherits(&Window::info))
return throwError(exec, TypeError);
Window *window = static_cast<Window *>(thisObj);
Frame *frame = window->m_frame;
if (!frame)
return jsUndefined();
FrameView *widget = frame->view();
JSValue *v = args[0];
UString s = v->toString(exec);
String str = s;
String str2;
switch (id) {
case Window::Alert:
if (frame && frame->document())
frame->document()->updateRendering();
exec->dynamicInterpreter()->pauseTimeoutCheck();
frame->runJavaScriptAlert(str);
exec->dynamicInterpreter()->resumeTimeoutCheck();
return jsUndefined();
case Window::Confirm: {
if (frame && frame->document())
frame->document()->updateRendering();
exec->dynamicInterpreter()->pauseTimeoutCheck();
bool result = frame->runJavaScriptConfirm(str);
exec->dynamicInterpreter()->resumeTimeoutCheck();
return jsBoolean(result);
}
case Window::Prompt:
{
if (frame && frame->document())
frame->document()->updateRendering();
String message = args.size() >= 2 ? args[1]->toString(exec) : UString();
bool ok = frame->runJavaScriptPrompt(str, message, str2);
if (ok)
return jsString(str2);
else
return jsNull();
}
case Window::Open:
{
Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
if (!activeFrame)
return jsUndefined();
AtomicString frameName = args[1]->isUndefinedOrNull()
? "_blank" : AtomicString(args[1]->toString(exec));
if (!allowPopUp(exec, window) && (frameName.isEmpty() || !frame->tree()->find(frameName)))
return jsUndefined();
WindowArgs windowArgs;
String features = args[2]->isUndefinedOrNull() ? UString() : args[2]->toString(exec);
parseWindowFeatures(features, windowArgs);
constrainToVisible(screenRect(widget), windowArgs);
bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
KURL url;
Frame* activePart = Window::retrieveActive(exec)->m_frame;
if (!str.isEmpty() && activePart)
url = activePart->document()->completeURL(str.deprecatedString());
ResourceRequest request;
request.setURL(url);
request.frameName = frameName.deprecatedString();
bool topOrParent = false;
if (request.frameName == "_top") {
while (frame->tree()->parent())
frame = frame->tree()->parent();
topOrParent = true;
} else if (request.frameName == "_parent") {
if (Frame* parent = frame->tree()->parent())
frame = parent;
if (!activeFrame->canTargetLoadInFrame(frame))
return jsUndefined();
topOrParent = true;
}
if (topOrParent) {
const Window* window = Window::retrieveWindow(frame);
if (!url.url().startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
frame->scheduleLocationChange(url.url(), activePart->referrer(), false, userGesture);
}
return Window::retrieve(frame);
}
request.m_responseMIMEType = "text/html";
Frame* newFrame = 0;
request.setReferrer(activePart->referrer());
frame->browserExtension()->createNewWindow(request, windowArgs, newFrame, userGesture);
if (!newFrame)
return jsUndefined();
newFrame->setOpener(frame);
newFrame->setOpenedByJS(true);
if (!newFrame->document()) {
Document* oldDoc = frame->document();
if (oldDoc && oldDoc->baseURL() != 0)
newFrame->begin(oldDoc->baseURL());
else
newFrame->begin();
newFrame->write("<HTML><BODY>");
newFrame->end();
if (oldDoc) {
newFrame->document()->setDomainInternal(oldDoc->domain());
newFrame->document()->setBaseURL(oldDoc->baseURL());
}
}
if (!url.isEmpty()) {
const Window* window = Window::retrieveWindow(newFrame);
if (!url.url().startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
newFrame->scheduleLocationChange(url.url(), activePart->referrer(), false, userGesture);
}
}
return Window::retrieve(newFrame); }
case Window::Print:
frame->print();
return jsUndefined();
case Window::ScrollBy:
window->updateLayout();
if(args.size() >= 2 && widget)
widget->scrollBy(args[0]->toInt32(exec), args[1]->toInt32(exec));
return jsUndefined();
case Window::Scroll:
case Window::ScrollTo:
window->updateLayout();
if (args.size() >= 2 && widget)
widget->setContentsPos(args[0]->toInt32(exec), args[1]->toInt32(exec));
return jsUndefined();
case Window::MoveBy:
if (args.size() >= 2 && widget) {
FloatRect r = frame->page()->windowRect();
r.move(args[0]->toNumber(exec), args[1]->toNumber(exec));
if (screenRect(widget).contains(r))
frame->page()->setWindowRect(r);
}
return jsUndefined();
case Window::MoveTo:
if (args.size() >= 2 && widget) {
FloatRect r = frame->page()->windowRect();
FloatRect sr = screenRect(widget);
r.setLocation(sr.location());
r.move(args[0]->toNumber(exec), args[1]->toNumber(exec));
if (sr.contains(r))
frame->page()->setWindowRect(r);
}
return jsUndefined();
case Window::ResizeBy:
if (args.size() >= 2 && widget) {
FloatRect r = frame->page()->windowRect();
FloatSize dest = r.size() + FloatSize(args[0]->toNumber(exec), args[1]->toNumber(exec));
FloatRect sg = screenRect(widget);
if (r.x() + dest.width() <= sg.right() && r.y() + dest.height() <= sg.bottom()
&& dest.width() >= 100 && dest.height() >= 100)
frame->page()->setWindowRect(FloatRect(r.location(), dest));
}
return jsUndefined();
case Window::ResizeTo:
if (args.size() >= 2 && widget) {
FloatRect r = frame->page()->windowRect();
FloatSize dest = FloatSize(args[0]->toNumber(exec), args[1]->toNumber(exec));
FloatRect sg = screenRect(widget);
if (r.x() + dest.width() <= sg.right() && r.y() + dest.height() <= sg.bottom() &&
dest.width() >= 100 && dest.height() >= 100)
frame->page()->setWindowRect(FloatRect(r.location(), dest));
}
return jsUndefined();
case Window::SetTimeout:
if (!window->isSafeScript(exec))
return jsUndefined();
if (args.size() >= 2 && v->isString()) {
int i = args[1]->toInt32(exec);
int r = (const_cast<Window*>(window))->installTimeout(s, i, true );
return jsNumber(r);
}
else if (args.size() >= 2 && v->isObject() && static_cast<JSObject *>(v)->implementsCall()) {
JSValue *func = args[0];
int i = args[1]->toInt32(exec);
List funcArgs = args.copyTail().copyTail();
int r = (const_cast<Window*>(window))->installTimeout(func, funcArgs, i, true );
return jsNumber(r);
}
else
return jsUndefined();
case Window::SetInterval:
if (!window->isSafeScript(exec))
return jsUndefined();
if (args.size() >= 2 && v->isString()) {
int i = args[1]->toInt32(exec);
int r = (const_cast<Window*>(window))->installTimeout(s, i, false);
return jsNumber(r);
}
else if (args.size() >= 2 && v->isObject() && static_cast<JSObject *>(v)->implementsCall()) {
JSValue *func = args[0];
int i = args[1]->toInt32(exec);
List funcArgs = args.copyTail().copyTail();
int r = (const_cast<Window*>(window))->installTimeout(func, funcArgs, i, false);
return jsNumber(r);
}
else
return jsUndefined();
case Window::ClearTimeout:
case Window::ClearInterval:
if (!window->isSafeScript(exec))
return jsUndefined();
(const_cast<Window*>(window))->clearTimeout(v->toInt32(exec));
return jsUndefined();
case Window::Focus:
frame->focusWindow();
return jsUndefined();
case Window::GetSelection:
if (!window->isSafeScript(exec))
return jsUndefined();
return window->selection();
case Window::Blur:
frame->unfocusWindow();
return jsUndefined();
case Window::Close:
if (!frame->openedByJS())
{
History history(exec, frame);
if ( history.get( exec, lengthPropertyName )->toInt32(exec) <= 1
)
(const_cast<Window*>(window))->scheduleClose();
}
else
{
(const_cast<Window*>(window))->scheduleClose();
}
return jsUndefined();
case Window::CaptureEvents:
case Window::ReleaseEvents:
if (!window->isSafeScript(exec))
return jsUndefined();
break;
case Window::AddEventListener:
if (!window->isSafeScript(exec))
return jsUndefined();
if (JSEventListener* listener = window->findOrCreateJSEventListener(args[1]))
if (Document *doc = frame->document())
doc->addWindowEventListener(AtomicString(args[0]->toString(exec)), listener, args[2]->toBoolean(exec));
return jsUndefined();
case Window::RemoveEventListener:
if (!window->isSafeScript(exec))
return jsUndefined();
if (JSEventListener* listener = window->findJSEventListener(args[1]))
if (Document *doc = frame->document())
doc->removeWindowEventListener(AtomicString(args[0]->toString(exec)), listener, args[2]->toBoolean(exec));
return jsUndefined();
case Window::ShowModalDialog: {
exec->dynamicInterpreter()->pauseTimeoutCheck();
JSValue* result = showModalDialog(exec, window, args);
exec->dynamicInterpreter()->resumeTimeoutCheck();
return result;
}
}
return jsUndefined();
}
void Window::updateLayout() const
{
WebCore::Document* docimpl = m_frame->document();
if (docimpl)
docimpl->updateLayoutIgnorePendingStylesheets();
}
void ScheduledAction::execute(Window *window)
{
RefPtr<Frame> frame = window->m_frame;
if (!frame)
return;
KJSProxy* scriptProxy = frame->jScript();
if (!scriptProxy)
return;
RefPtr<ScriptInterpreter> interpreter = scriptProxy->interpreter();
interpreter->setProcessingTimerCallback(true);
if (JSValue* func = m_func.get()) {
if (func->isObject() && static_cast<JSObject *>(func)->implementsCall()) {
ExecState *exec = interpreter->globalExec();
ASSERT(window == interpreter->globalObject());
JSLock lock;
interpreter->startTimeoutCheck();
static_cast<JSObject *>(func)->call(exec, window, m_args);
interpreter->stopTimeoutCheck();
if (exec->hadException()) {
JSObject* exception = exec->exception()->toObject(exec);
exec->clearException();
String message = exception->get(exec, messagePropertyName)->toString(exec);
int lineNumber = exception->get(exec, "line")->toInt32(exec);
if (Interpreter::shouldPrintExceptions())
printf("(timer):%s\n", message.deprecatedString().utf8().data());
if (Page* page = frame->page())
page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, lineNumber, String());
}
}
} else
frame->executeScript(0, m_code);
if (Document* doc = frame->document())
doc->updateRendering();
interpreter->setProcessingTimerCallback(false);
}
void Window::clearAllTimeouts()
{
deleteAllValues(m_timeouts);
m_timeouts.clear();
deleteAllValues(m_deferredTimeouts);
m_deferredTimeouts.clear();
if (m_frame)
m_frame->clearObservedContentModifiers();
}
int Window::installTimeout(ScheduledAction* a, int t, bool singleShot)
{
int timeoutId = ++lastUsedTimeoutId;
DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, this, a);
ASSERT(!m_timeouts.get(timeoutId));
m_timeouts.set(timeoutId, timer);
bool deferTimeout = (m_frame->document() && m_frame->document()->view() && m_frame->document()->view()->inSuspendedWindow());
if (!deferTimeout) {
if (t <= 100 && singleShot) {
WKSetObservedContentChange(WKContentIndeterminateChange);
WebThreadAddObservedContentModifier(timer); }
}
double interval = t <= 10 ? 0.010 : t * 0.001;
if (singleShot)
timer->startOneShot(interval);
else
timer->startRepeating(interval);
if (deferTimeout) {
PausedTimeout* deferredTimeout = new PausedTimeout;
deferredTimeout->timeoutId = timeoutId;
deferredTimeout->nextFireInterval = timer->nextFireInterval();
deferredTimeout->repeatInterval = timer->repeatInterval();
deferredTimeout->action = timer->takeAction();
m_deferredTimeouts.set(timeoutId, deferredTimeout);
m_timeouts.remove(timeoutId);
delete timer;
}
return timeoutId;
}
int Window::installTimeout(const UString& handler, int t, bool singleShot)
{
return installTimeout(new ScheduledAction(handler), t, singleShot);
}
int Window::installTimeout(JSValue* func, const List& args, int t, bool singleShot)
{
return installTimeout(new ScheduledAction(func, args), t, singleShot);
}
PausedTimeouts* Window::pauseTimeouts()
{
size_t count = m_timeouts.size();
if (count == 0)
return 0;
PausedTimeout* t = new PausedTimeout [count];
PausedTimeouts* result = new PausedTimeouts(t, count);
TimeoutsMap::iterator it = m_timeouts.begin();
for (size_t i = 0; i != count; ++i, ++it) {
int timeoutId = it->first;
DOMWindowTimer* timer = it->second;
t[i].timeoutId = timeoutId;
t[i].nextFireInterval = timer->nextFireInterval();
t[i].repeatInterval = timer->repeatInterval();
t[i].action = timer->takeAction();
}
ASSERT(it == m_timeouts.end());
deleteAllValues(m_timeouts);
m_timeouts.clear();
ASSERT(m_frame);
if (m_frame)
m_frame->clearObservedContentModifiers();
return result;
}
void Window::resumeTimeouts(PausedTimeouts* timeouts)
{
for (DeferredTimeoutsMap::iterator it = m_deferredTimeouts.begin(); it != m_deferredTimeouts.end(); ++it) {
PausedTimeout *timeout = it->second;
int timeoutId = timeout->timeoutId;
DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, this, timeout->action);
m_timeouts.set(timeoutId, timer);
timer->start(timeout->nextFireInterval, timeout->repeatInterval);
}
deleteAllValues(m_deferredTimeouts);
m_deferredTimeouts.clear();
if (!timeouts)
return;
size_t count = timeouts->numTimeouts();
PausedTimeout* array = timeouts->takeTimeouts();
for (size_t i = 0; i != count; ++i) {
int timeoutId = array[i].timeoutId;
DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, this, array[i].action);
m_timeouts.set(timeoutId, timer);
timer->start(array[i].nextFireInterval, array[i].repeatInterval);
}
delete [] array;
}
void Window::clearTimeout(int timeoutId, bool delAction)
{
TimeoutsMap::iterator it = m_timeouts.find(timeoutId);
if (it == m_timeouts.end())
{
DeferredTimeoutsMap::iterator deferredIt = m_deferredTimeouts.find(timeoutId);
if (deferredIt != m_deferredTimeouts.end()) {
PausedTimeout* timeout = deferredIt->second;
m_deferredTimeouts.remove(deferredIt);
delete timeout;
}
return;
}
DOMWindowTimer* timer = it->second;
m_timeouts.remove(it);
if (WebThreadContainsObservedContentModifier(timer)) {
WebThreadRemoveObservedContentModifier(timer);
if (WebThreadCountOfObservedContentModifiers() == 0)
m_frame->deferredContentChangeObserved();
}
delete timer;
}
void Window::timerFired(DOMWindowTimer* timer)
{
ASSERT(!m_frame->document()->view()->inSuspendedWindow());
if (timer->isActive()) {
timer->action()->execute(this);
return;
}
ScheduledAction* action = timer->takeAction();
m_timeouts.remove(timer->timeoutId());
delete timer;
bool shouldReportLackOfChanges = WebThreadCountOfObservedContentModifiers() == 1,
shouldBeginObservingChanges = WebThreadContainsObservedContentModifier(timer);
if (shouldBeginObservingChanges)
WKBeginObservingContentChanges(false);
action->execute(this);
if (shouldBeginObservingChanges) {
WKStopObservingContentChanges();
WebThreadRemoveObservedContentModifier(timer);
if (WKObservedContentChange() == WKContentVisibilityChange || shouldReportLackOfChanges)
m_frame->deferredContentChangeObserved();
}
delete action;
}
void Window::disconnectFrame()
{
clearAllTimeouts();
m_frame = 0;
if (loc)
loc->m_frame = 0;
if (m_selection)
m_selection->m_frame = 0;
if (m_locationbar)
m_locationbar->m_frame = 0;
if (m_menubar)
m_menubar->m_frame = 0;
if (m_personalbar)
m_personalbar->m_frame = 0;
if (m_statusbar)
m_statusbar->m_frame = 0;
if (m_toolbar)
m_toolbar->m_frame = 0;
if (m_scrollbars)
m_scrollbars->m_frame = 0;
if (frames)
frames->disconnectFrame();
if (history)
history->disconnectFrame();
}
const ClassInfo FrameArray::info = { "FrameArray", 0, &FrameArrayTable, 0 };
JSValue *FrameArray::getValueProperty(ExecState *exec, int token)
{
switch (token) {
case Length:
return jsNumber(m_frame->tree()->childCount());
case Location:
if (JSObject *obj = Window::retrieveWindow(m_frame))
return obj->get(exec, "location");
return jsUndefined();
default:
ASSERT(0);
return jsUndefined();
}
}
JSValue* FrameArray::indexGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
{
return Window::retrieve(static_cast<FrameArray*>(slot.slotBase())->m_frame->tree()->child(slot.index()));
}
JSValue* FrameArray::nameGetter(ExecState*, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
{
return Window::retrieve(static_cast<FrameArray*>(slot.slotBase())->m_frame->tree()->child(AtomicString(propertyName)));
}
bool FrameArray::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
{
if (!m_frame) {
slot.setUndefined(this);
return true;
}
const HashEntry* entry = Lookup::findEntry(&FrameArrayTable, propertyName);
if (entry) {
slot.setStaticEntry(this, entry, staticValueGetter<FrameArray>);
return true;
}
if (m_frame->tree()->child(propertyName)) {
slot.setCustom(this, nameGetter);
return true;
}
bool ok;
unsigned i = propertyName.toArrayIndex(&ok);
if (ok && i < m_frame->tree()->childCount()) {
slot.setCustomIndex(this, i, indexGetter);
return true;
}
return JSObject::getOwnPropertySlot(exec, propertyName, slot);
}
UString FrameArray::toString(ExecState *) const
{
return "[object FrameArray]";
}
const ClassInfo Location::info = { "Location", 0, 0, 0 };
KJS_IMPLEMENT_PROTOFUNC(LocationFunc)
Location::Location(Frame *p) : m_frame(p)
{
}
JSValue *Location::getValueProperty(ExecState *exec, int token) const
{
KURL url = m_frame->url();
switch (token) {
case Hash:
return jsString(url.ref().isNull() ? "" : "#" + url.ref());
case Host: {
UString str = url.host();
if (url.port())
str += ":" + String::number((int)url.port());
return jsString(str);
}
case Hostname:
return jsString(url.host());
case Href:
if (!url.hasPath())
return jsString(url.prettyURL() + "/");
else
return jsString(url.prettyURL());
case Pathname:
return jsString(url.path().isEmpty() ? "/" : url.path());
case Port:
return jsString(url.port() ? String::number((int)url.port()) : "");
case Protocol:
return jsString(url.protocol() + ":");
case Search:
return jsString(url.query());
default:
ASSERT(0);
return jsUndefined();
}
}
bool Location::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
{
if (!m_frame)
return false;
const Window* window = Window::retrieveWindow(m_frame);
const HashEntry *entry = Lookup::findEntry(&LocationTable, propertyName);
if (!entry || (entry->value != Replace && entry->value != Reload && entry->value != Assign))
if (!window || !window->isSafeScript(exec)) {
slot.setUndefined(this);
return true;
}
return getStaticPropertySlot<LocationFunc, Location, JSObject>(exec, &LocationTable, this, propertyName, slot);
}
void Location::put(ExecState *exec, const Identifier &p, JSValue *v, int attr)
{
if (!m_frame)
return;
DeprecatedString str = v->toString(exec);
KURL url = m_frame->url();
const Window* window = Window::retrieveWindow(m_frame);
bool sameDomainAccess = window && window->isSafeScript(exec);
const HashEntry *entry = Lookup::findEntry(&LocationTable, p);
if (entry) {
if (entry->value != Href && !sameDomainAccess)
return;
switch (entry->value) {
case Href: {
Frame* frame = Window::retrieveActive(exec)->impl()->frame();
if (!frame)
return;
if (!frame->canTargetLoadInFrame(m_frame))
return;
url = frame->completeURL(str).url();
break;
}
case Hash: {
if (str.startsWith("#"))
str = str.mid(1);
if (url.ref() == str)
return;
url.setRef(str);
break;
}
case Host: {
url.setHostAndPort(str);
break;
}
case Hostname:
url.setHost(str);
break;
case Pathname:
url.setPath(str);
break;
case Port:
url.setPort(str.toUInt());
break;
case Protocol:
url.setProtocol(str);
break;
case Search:
url.setQuery(str);
break;
default:
return;
}
} else {
if (sameDomainAccess)
JSObject::put(exec, p, v, attr);
return;
}
Frame* activePart = Window::retrieveActive(exec)->frame();
if (!url.url().startsWith("javascript:", false) || sameDomainAccess) {
bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
m_frame->scheduleLocationChange(url.url(), activePart->referrer(), !userGesture, userGesture);
}
}
JSValue *Location::toPrimitive(ExecState *exec, JSType) const
{
return jsString(toString(exec));
}
UString Location::toString(ExecState *exec) const
{
if (!m_frame || !Window::retrieveWindow(m_frame)->isSafeScript(exec))
return UString();
if (!m_frame->url().hasPath())
return m_frame->url().prettyURL()+"/";
else
return m_frame->url().prettyURL();
}
JSValue *LocationFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
{
if (!thisObj->inherits(&Location::info))
return throwError(exec, TypeError);
Location *location = static_cast<Location *>(thisObj);
Frame *frame = location->frame();
if (frame) {
Window* window = Window::retrieveWindow(frame);
if (!window->isSafeScript(exec) && id != Location::Replace)
return jsUndefined();
switch (id) {
case Location::Replace:
{
Frame* p = Window::retrieveActive(exec)->frame();
if (p) {
if (!p->canTargetLoadInFrame(frame))
return jsUndefined();
DeprecatedString str = args[0]->toString(exec);
const Window* window = Window::retrieveWindow(frame);
if (!str.startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
frame->scheduleLocationChange(p->document()->completeURL(str), p->referrer(), true , userGesture);
}
}
break;
}
case Location::Reload:
{
const Window* window = Window::retrieveWindow(frame);
if (!frame->url().url().startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
frame->scheduleRefresh(userGesture);
}
break;
}
case Location::Assign:
{
Frame *p = Window::retrieveActive(exec)->frame();
if (p) {
if (!p->canTargetLoadInFrame(frame))
return jsUndefined();
const Window *window = Window::retrieveWindow(frame);
DeprecatedString dstUrl = p->document()->completeURL(DeprecatedString(args[0]->toString(exec)));
if (!dstUrl.startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
frame->scheduleLocationChange(dstUrl, p->referrer(), !userGesture, userGesture);
}
}
break;
}
case Location::ToString:
if (!frame || !Window::retrieveWindow(frame)->isSafeScript(exec))
return jsString();
return jsString(location->toString(exec));
}
}
return jsUndefined();
}
const ClassInfo Selection::info = { "Selection", 0, 0, 0 };
KJS_IMPLEMENT_PROTOFUNC(SelectionFunc)
Selection::Selection(Frame *p) : m_frame(p)
{
}
JSValue *Selection::getValueProperty(ExecState *exec, int token) const
{
SelectionController s = m_frame->selection();
const Window* window = Window::retrieveWindow(m_frame);
if (!window)
return jsUndefined();
switch (token) {
case AnchorNode:
return toJS(exec, s.anchorNode());
case BaseNode:
return toJS(exec, s.baseNode());
case AnchorOffset:
return jsNumber(s.anchorOffset());
case BaseOffset:
return jsNumber(s.baseOffset());
case FocusNode:
return toJS(exec, s.focusNode());
case ExtentNode:
return toJS(exec, s.extentNode());
case FocusOffset:
return jsNumber(s.focusOffset());
case ExtentOffset:
return jsNumber(s.extentOffset());
case IsCollapsed:
return jsBoolean(s.isCollapsed());
case _Type:
return jsString(s.type());
default:
ASSERT(0);
return jsUndefined();
}
}
bool Selection::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
{
if (!m_frame)
return false;
return getStaticPropertySlot<SelectionFunc, Selection, JSObject>(exec, &SelectionTable, this, propertyName, slot);
}
JSValue *Selection::toPrimitive(ExecState *exec, JSType) const
{
return jsString(toString(exec));
}
UString Selection::toString(ExecState *) const
{
return UString((m_frame->selection()).toString());
}
JSValue *SelectionFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
{
if (!thisObj->inherits(&Selection::info))
return throwError(exec, TypeError);
Selection *selection = static_cast<Selection *>(thisObj);
Frame *frame = selection->frame();
if (frame) {
SelectionController s = frame->selection();
switch (id) {
case Selection::Collapse:
s.collapse(toNode(args[0]), args[1]->toInt32(exec));
break;
case Selection::CollapseToEnd:
s.collapseToEnd();
break;
case Selection::CollapseToStart:
s.collapseToStart();
break;
case Selection::Empty:
s.empty();
break;
case Selection::SetBaseAndExtent:
s.setBaseAndExtent(toNode(args[0]), args[1]->toInt32(exec), toNode(args[2]), args[3]->toInt32(exec));
break;
case Selection::SetPosition:
s.setPosition(toNode(args[0]), args[1]->toInt32(exec));
break;
case Selection::Modify:
s.modify(args[0]->toString(exec), args[1]->toString(exec), args[2]->toString(exec));
break;
case Selection::GetRangeAt:
return toJS(exec, s.getRangeAt(args[0]->toInt32(exec)).get());
case Selection::ToString:
return jsString(s.toString());
}
frame->setSelection(s);
}
return jsUndefined();
}
const ClassInfo BarInfo::info = { "BarInfo", 0, 0, 0 };
BarInfo::BarInfo(ExecState *exec, Frame *f, Type barType)
: m_frame(f)
, m_type(barType)
{
setPrototype(exec->lexicalInterpreter()->builtinObjectPrototype());
}
JSValue *BarInfo::getValueProperty(ExecState *exec, int token) const
{
ASSERT(token == Visible);
switch (m_type) {
case Locationbar:
return jsBoolean(m_frame->locationbarVisible());
case Menubar:
return jsBoolean(m_frame->locationbarVisible());
case Personalbar:
return jsBoolean(m_frame->personalbarVisible());
case Scrollbars:
return jsBoolean(m_frame->scrollbarsVisible());
case Statusbar:
return jsBoolean(m_frame->statusbarVisible());
case Toolbar:
return jsBoolean(m_frame->toolbarVisible());
default:
return jsBoolean(false);
}
}
bool BarInfo::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
{
if (!m_frame)
return false;
return getStaticValueSlot<BarInfo, JSObject>(exec, &BarInfoTable, this, propertyName, slot);
}
const ClassInfo History::info = { "History", 0, 0, 0 };
KJS_IMPLEMENT_PROTOFUNC(HistoryFunc)
bool History::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
{
return getStaticPropertySlot<HistoryFunc, History, JSObject>(exec, &HistoryTable, this, propertyName, slot);
}
JSValue *History::getValueProperty(ExecState *, int token) const
{
switch (token) {
case Length:
{
BrowserExtension *ext = m_frame->browserExtension();
if (!ext)
return jsNumber(0);
return jsNumber(ext->getHistoryLength());
}
default:
return jsUndefined();
}
}
UString History::toString(ExecState *exec) const
{
return "[object History]";
}
JSValue *HistoryFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
{
if (!thisObj->inherits(&History::info))
return throwError(exec, TypeError);
History *history = static_cast<History *>(thisObj);
int steps;
switch (id) {
case History::Back:
steps = -1;
break;
case History::Forward:
steps = 1;
break;
case History::Go:
steps = args[0]->toInt32(exec);
break;
default:
return jsUndefined();
}
history->m_frame->scheduleHistoryNavigation(steps);
return jsUndefined();
}
PausedTimeouts::~PausedTimeouts()
{
PausedTimeout *array = m_array;
if (!array)
return;
size_t count = m_length;
for (size_t i = 0; i != count; ++i)
delete array[i].action;
delete [] array;
}
void DOMWindowTimer::fired()
{
m_object->timerFired(this);
}
}
using namespace KJS;
namespace WebCore {
JSValue* toJS(ExecState*, DOMWindow* domWindow)
{
if (!domWindow)
return jsNull();
Frame* frame = domWindow->frame();
if (!frame)
return jsNull();
return Window::retrieve(frame);
}
DOMWindow* toDOMWindow(JSValue* val)
{
return val->isObject(&JSDOMWindow::info) ? static_cast<JSDOMWindow*>(val)->impl() : 0;
}
}