Refactor static getter function prototype to include thisValue in addition to the...
[WebKit-https.git] / Source / WebCore / bindings / js / JSHistoryCustom.cpp
1 /*
2  * Copyright (C) 2008 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "JSHistory.h"
31
32 #include "Frame.h"
33 #include "History.h"
34 #include "SerializedScriptValue.h"
35 #include <runtime/JSFunction.h>
36
37 using namespace JSC;
38
39 namespace WebCore {
40
41 static EncodedJSValue nonCachingStaticBackFunctionGetter(ExecState* exec, EncodedJSValue, EncodedJSValue, PropertyName propertyName)
42 {
43     return JSValue::encode(JSFunction::create(exec->vm(), exec->lexicalGlobalObject(), 0, propertyName.publicName(), jsHistoryPrototypeFunctionBack));
44 }
45
46 static EncodedJSValue nonCachingStaticForwardFunctionGetter(ExecState* exec, EncodedJSValue, EncodedJSValue, PropertyName propertyName)
47 {
48     return JSValue::encode(JSFunction::create(exec->vm(), exec->lexicalGlobalObject(), 0, propertyName.publicName(), jsHistoryPrototypeFunctionForward));
49 }
50
51 static EncodedJSValue nonCachingStaticGoFunctionGetter(ExecState* exec, EncodedJSValue, EncodedJSValue, PropertyName propertyName)
52 {
53     return JSValue::encode(JSFunction::create(exec->vm(), exec->lexicalGlobalObject(), 1, propertyName.publicName(), jsHistoryPrototypeFunctionGo));
54 }
55
56 bool JSHistory::getOwnPropertySlotDelegate(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
57 {
58     // When accessing History cross-domain, functions are always the native built-in ones.
59     // See JSDOMWindow::getOwnPropertySlotDelegate for additional details.
60
61     // Our custom code is only needed to implement the Window cross-domain scheme, so if access is
62     // allowed, return false so the normal lookup will take place.
63     String message;
64     if (shouldAllowAccessToFrame(exec, impl().frame(), message))
65         return false;
66
67     // Check for the few functions that we allow, even when called cross-domain.
68     // Make these read-only / non-configurable to prevent writes via defineProperty.
69     const HashEntry* entry = JSHistoryPrototype::info()->propHashTable(exec)->entry(exec, propertyName);
70     if (entry) {
71         // Allow access to back(), forward() and go() from any frame.
72         if (entry->attributes() & JSC::Function) {
73             if (entry->function() == jsHistoryPrototypeFunctionBack) {
74                 slot.setCustom(this, ReadOnly | DontDelete | DontEnum, nonCachingStaticBackFunctionGetter);
75                 return true;
76             } else if (entry->function() == jsHistoryPrototypeFunctionForward) {
77                 slot.setCustom(this, ReadOnly | DontDelete | DontEnum, nonCachingStaticForwardFunctionGetter);
78                 return true;
79             } else if (entry->function() == jsHistoryPrototypeFunctionGo) {
80                 slot.setCustom(this, ReadOnly | DontDelete | DontEnum, nonCachingStaticGoFunctionGetter);
81                 return true;
82             }
83         }
84     } else {
85         // Allow access to toString() cross-domain, but always Object.toString.
86         if (propertyName == exec->propertyNames().toString) {
87             slot.setCustom(this, ReadOnly | DontDelete | DontEnum, objectToStringFunctionGetter);
88             return true;
89         }
90     }
91
92     printErrorMessageForFrame(impl().frame(), message);
93     slot.setUndefined();
94     return true;
95 }
96
97 bool JSHistory::putDelegate(ExecState* exec, PropertyName, JSValue, PutPropertySlot&)
98 {
99     // Only allow putting by frames in the same origin.
100     if (!shouldAllowAccessToFrame(exec, impl().frame()))
101         return true;
102     return false;
103 }
104
105 bool JSHistory::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
106 {
107     JSHistory* thisObject = jsCast<JSHistory*>(cell);
108     // Only allow deleting by frames in the same origin.
109     if (!shouldAllowAccessToFrame(exec, thisObject->impl().frame()))
110         return false;
111     return Base::deleteProperty(thisObject, exec, propertyName);
112 }
113
114 bool JSHistory::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName)
115 {
116     JSHistory* thisObject = jsCast<JSHistory*>(cell);
117     // Only allow deleting by frames in the same origin.
118     if (!shouldAllowAccessToFrame(exec, thisObject->impl().frame()))
119         return false;
120     return Base::deletePropertyByIndex(thisObject, exec, propertyName);
121 }
122
123 void JSHistory::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
124 {
125     JSHistory* thisObject = jsCast<JSHistory*>(object);
126     // Only allow the history object to enumerated by frames in the same origin.
127     if (!shouldAllowAccessToFrame(exec, thisObject->impl().frame()))
128         return;
129     Base::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
130 }
131
132 JSValue JSHistory::state(ExecState *exec) const
133 {
134     History& history = impl();
135
136     JSValue cachedValue = m_state.get();
137     if (!cachedValue.isEmpty() && !history.stateChanged())
138         return cachedValue;
139
140     RefPtr<SerializedScriptValue> serialized = history.state();
141     JSValue result = serialized ? serialized->deserialize(exec, globalObject(), 0) : jsNull();
142     const_cast<JSHistory*>(this)->m_state.set(exec->vm(), this, result);
143     return result;
144 }
145
146 JSValue JSHistory::pushState(ExecState* exec)
147 {
148     RefPtr<SerializedScriptValue> historyState = SerializedScriptValue::create(exec, exec->argument(0), 0, 0);
149     if (exec->hadException())
150         return jsUndefined();
151
152     String title = valueToStringWithUndefinedOrNullCheck(exec, exec->argument(1));
153     if (exec->hadException())
154         return jsUndefined();
155         
156     String url;
157     if (exec->argumentCount() > 2) {
158         url = valueToStringWithUndefinedOrNullCheck(exec, exec->argument(2));
159         if (exec->hadException())
160             return jsUndefined();
161     }
162
163     ExceptionCode ec = 0;
164     impl().stateObjectAdded(historyState.release(), title, url, History::StateObjectPush, ec);
165     setDOMException(exec, ec);
166
167     m_state.clear();
168
169     return jsUndefined();
170 }
171
172 JSValue JSHistory::replaceState(ExecState* exec)
173 {
174     RefPtr<SerializedScriptValue> historyState = SerializedScriptValue::create(exec, exec->argument(0), 0, 0);
175     if (exec->hadException())
176         return jsUndefined();
177
178     String title = valueToStringWithUndefinedOrNullCheck(exec, exec->argument(1));
179     if (exec->hadException())
180         return jsUndefined();
181         
182     String url;
183     if (exec->argumentCount() > 2) {
184         url = valueToStringWithUndefinedOrNullCheck(exec, exec->argument(2));
185         if (exec->hadException())
186             return jsUndefined();
187     }
188
189     ExceptionCode ec = 0;
190     impl().stateObjectAdded(historyState.release(), title, url, History::StateObjectReplace, ec);
191     setDOMException(exec, ec);
192
193     m_state.clear();
194
195     return jsUndefined();
196 }
197
198 } // namespace WebCore