30fa1df4ec1c2448f61e82390deff48b3e6c1f15
[WebKit-https.git] / WebCore / bindings / js / JSDOMWindowCustom.cpp
1 /*
2  * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21 #include "JSDOMWindow.h"
22
23 #include "AtomicString.h"
24 #include "DOMWindow.h"
25 #include "Document.h"
26 #include "ExceptionCode.h"
27 #include "Frame.h"
28 #include "FrameLoader.h"
29 #include "FrameTree.h"
30 #include "JSDOMWindowWrapper.h"
31 #include "Settings.h"
32 #include "kjs_proxy.h"
33 #include <kjs/object.h>
34
35 using namespace KJS;
36
37 namespace WebCore {
38
39 static void markDOMObjectWrapper(void* object)
40 {
41     if (!object)
42         return;
43     DOMObject* wrapper = ScriptInterpreter::getDOMObject(object);
44     if (!wrapper || wrapper->marked())
45         return;
46     wrapper->mark();
47 }
48
49 void JSDOMWindow::mark()
50 {
51     Base::mark();
52     markDOMObjectWrapper(impl()->optionalConsole());
53     markDOMObjectWrapper(impl()->optionalHistory());
54     markDOMObjectWrapper(impl()->optionalLocationbar());
55     markDOMObjectWrapper(impl()->optionalMenubar());
56     markDOMObjectWrapper(impl()->optionalNavigator());
57     markDOMObjectWrapper(impl()->optionalPersonalbar());
58     markDOMObjectWrapper(impl()->optionalScreen());
59     markDOMObjectWrapper(impl()->optionalScrollbars());
60     markDOMObjectWrapper(impl()->optionalSelection());
61     markDOMObjectWrapper(impl()->optionalStatusbar());
62     markDOMObjectWrapper(impl()->optionalToolbar());
63     markDOMObjectWrapper(impl()->optionalLocation());
64 #if ENABLE(DOM_STORAGE)
65     markDOMObjectWrapper(impl()->optionalSessionStorage());
66     markDOMObjectWrapper(impl()->optionalLocalStorage());
67 #endif
68 }
69
70 bool JSDOMWindow::customGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
71 {
72     // When accessing a Window cross-domain, functions are always the native built-in ones, and they
73     // are not affected by properties changed on the Window or anything in its prototype chain.
74     // This is consistent with the behavior of Firefox.
75
76     const HashEntry* entry;
77
78     // We don't want any properties other than "close" and "closed" on a closed window.
79     if (!impl()->frame()) {
80         // The following code is safe for cross-domain and same domain use.
81         // It ignores any custom properties that might be set on the DOMWindow (including a custom prototype).
82         entry = s_info.propHashTable->entry(propertyName);
83         if (entry && !(entry->attributes & Function) && entry->integerValue == ClosedAttrNum) {
84             slot.setStaticEntry(this, entry, staticValueGetter<JSDOMWindow>);
85             return true;
86         }
87         entry = JSDOMWindowPrototype::s_info.propHashTable->entry(propertyName);
88         if (entry && (entry->attributes & Function) && entry->functionValue == jsDOMWindowPrototypeFunctionClose) {
89             slot.setStaticEntry(this, entry, nonCachingStaticFunctionGetter);
90             return true;
91         }
92
93         // FIXME: We should have a message here that explains why the property access/function call was
94         // not allowed. 
95         slot.setUndefined(this);
96         return true;
97     }
98
99     // We need to check for cross-domain access here without printing the generic warning message
100     // because we always allow access to some function, just different ones depending whether access
101     // is allowed.
102     bool allowsAccess = allowsAccessFromNoErrorMessage(exec);
103
104     // Look for overrides before looking at any of our own properties.
105     if (JSGlobalObject::getOwnPropertySlot(exec, propertyName, slot)) {
106         // But ignore overrides completely if this is cross-domain access.
107         if (allowsAccess)
108             return true;
109     }
110
111     // We need this code here because otherwise KJS::Window will stop the search before we even get to the
112     // prototype due to the blanket same origin (allowsAccessFrom) check at the end of getOwnPropertySlot.
113     // Also, it's important to get the implementation straight out of the DOMWindow prototype regardless of
114     // what prototype is actually set on this object.
115     entry = JSDOMWindowPrototype::s_info.propHashTable->entry(propertyName);
116     if (entry) {
117         if ((entry->attributes & Function)
118                 && (entry->functionValue == jsDOMWindowPrototypeFunctionBlur
119                     || entry->functionValue == jsDOMWindowPrototypeFunctionClose
120                     || entry->functionValue == jsDOMWindowPrototypeFunctionFocus
121 #if ENABLE(CROSS_DOCUMENT_MESSAGING)
122                     || entry->functionValue == jsDOMWindowPrototypeFunctionPostMessage
123 #endif
124                     )) {
125             if (!allowsAccess) {
126                 slot.setStaticEntry(this, entry, nonCachingStaticFunctionGetter);
127                 return true;
128             }
129         }
130     } else {
131         // Allow access to toString() cross-domain, but always Object.prototype.toString.
132         if (propertyName == exec->propertyNames().toString) {
133             if (!allowsAccess) {
134                 slot.setCustom(this, objectToStringFunctionGetter);
135                 return true;
136             }
137         }
138     }
139
140     return false;
141 }
142
143 bool JSDOMWindow::customPut(ExecState* exec, const Identifier& propertyName, JSValue* value)
144 {
145     if (!impl()->frame())
146         return true;
147
148     // We have a local override (e.g. "var location"), save time and jump directly to JSGlobalObject.
149     PropertySlot slot;
150     if (JSGlobalObject::getOwnPropertySlot(exec, propertyName, slot)) {
151         if (allowsAccessFrom(exec))
152             JSGlobalObject::put(exec, propertyName, value);
153         return true;
154     }
155
156     return false;
157 }
158
159 bool JSDOMWindow::deleteProperty(ExecState* exec, const Identifier& propertyName)
160 {
161     // Only allow deleting properties by frames in the same origin.
162     if (!allowsAccessFrom(exec))
163         return false;
164     return Base::deleteProperty(exec, propertyName);
165 }
166
167 bool JSDOMWindow::customGetPropertyNames(ExecState* exec, PropertyNameArray&)
168 {
169     // Only allow the window to enumerated by frames in the same origin.
170     if (!allowsAccessFrom(exec))
171         return true;
172     return false;
173 }
174
175 void JSDOMWindow::setLocation(ExecState* exec, JSValue* value)
176 {
177     Frame* activeFrame = toJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame();
178     if (!activeFrame)
179         return;
180
181     // To avoid breaking old widgets, make "var location =" in a top-level frame create
182     // a property named "location" instead of performing a navigation (<rdar://problem/5688039>).
183     if (Settings* settings = activeFrame->settings()) {
184         if (settings->usesDashboardBackwardCompatibilityMode() && !activeFrame->tree()->parent()) {
185             if (allowsAccessFrom(exec))
186                 putDirect("location", value);
187             return;
188         }
189     }
190
191     if (!activeFrame->loader()->shouldAllowNavigation(impl()->frame()))
192         return;
193     String dstUrl = activeFrame->loader()->completeURL(value->toString(exec)).string();
194     if (!protocolIs(dstUrl, "javascript") || allowsAccessFrom(exec)) {
195         bool userGesture = activeFrame->scriptProxy()->processingUserGesture();
196         // We want a new history item if this JS was called via a user gesture
197         impl()->frame()->loader()->scheduleLocationChange(dstUrl, activeFrame->loader()->outgoingReferrer(), false, userGesture);
198     }
199 }
200
201 #if ENABLE(CROSS_DOCUMENT_MESSAGING)
202 JSValue* JSDOMWindow::postMessage(ExecState* exec, const List& args)
203 {
204     DOMWindow* window = impl();
205
206     DOMWindow* source = toJSDOMWindow(exec->dynamicGlobalObject())->impl();
207     String domain = source->frame()->loader()->url().host();
208     String uri = source->frame()->loader()->url().string();
209     String message = args[0]->toString(exec);
210
211     if (exec->hadException())
212         return jsUndefined();
213
214     window->postMessage(message, domain, uri, source);
215
216     return jsUndefined();
217 }
218 #endif
219
220 DOMWindow* toDOMWindow(JSValue* val)
221 {
222     if (val->isObject(&JSDOMWindow::s_info))
223         return static_cast<JSDOMWindow*>(val)->impl();
224     if (val->isObject(&JSDOMWindowWrapper::s_info))
225         return static_cast<JSDOMWindowWrapper*>(val)->impl();
226     return 0;
227 }
228
229 } // namespace WebCore