Home | History | Annotate | Download | only in js
      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 "JSHistoryCustom.h"
     31 
     32 #include "Frame.h"
     33 #include "History.h"
     34 #include <runtime/JSFunction.h>
     35 
     36 using namespace JSC;
     37 
     38 namespace WebCore {
     39 
     40 static JSValue nonCachingStaticBackFunctionGetter(ExecState* exec, JSValue, const Identifier& propertyName)
     41 {
     42     return new (exec) JSFunction(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->functionStructure(), 0, propertyName, jsHistoryPrototypeFunctionBack);
     43 }
     44 
     45 static JSValue nonCachingStaticForwardFunctionGetter(ExecState* exec, JSValue, const Identifier& propertyName)
     46 {
     47     return new (exec) JSFunction(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->functionStructure(), 0, propertyName, jsHistoryPrototypeFunctionForward);
     48 }
     49 
     50 static JSValue nonCachingStaticGoFunctionGetter(ExecState* exec, JSValue, const Identifier& propertyName)
     51 {
     52     return new (exec) JSFunction(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->functionStructure(), 1, propertyName, jsHistoryPrototypeFunctionGo);
     53 }
     54 
     55 bool JSHistory::getOwnPropertySlotDelegate(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
     56 {
     57     // When accessing History cross-domain, functions are always the native built-in ones.
     58     // See JSDOMWindow::getOwnPropertySlotDelegate for additional details.
     59 
     60     // Our custom code is only needed to implement the Window cross-domain scheme, so if access is
     61     // allowed, return false so the normal lookup will take place.
     62     String message;
     63     if (allowsAccessFromFrame(exec, impl()->frame(), message))
     64         return false;
     65 
     66     // Check for the few functions that we allow, even when called cross-domain.
     67     const HashEntry* entry = JSHistoryPrototype::s_info.propHashTable(exec)->entry(exec, propertyName);
     68     if (entry) {
     69         // Allow access to back(), forward() and go() from any frame.
     70         if (entry->attributes() & Function) {
     71             if (entry->function() == jsHistoryPrototypeFunctionBack) {
     72                 slot.setCustom(this, nonCachingStaticBackFunctionGetter);
     73                 return true;
     74             } else if (entry->function() == jsHistoryPrototypeFunctionForward) {
     75                 slot.setCustom(this, nonCachingStaticForwardFunctionGetter);
     76                 return true;
     77             } else if (entry->function() == jsHistoryPrototypeFunctionGo) {
     78                 slot.setCustom(this, nonCachingStaticGoFunctionGetter);
     79                 return true;
     80             }
     81         }
     82     } else {
     83         // Allow access to toString() cross-domain, but always Object.toString.
     84         if (propertyName == exec->propertyNames().toString) {
     85             slot.setCustom(this, objectToStringFunctionGetter);
     86             return true;
     87         }
     88     }
     89 
     90     printErrorMessageForFrame(impl()->frame(), message);
     91     slot.setUndefined();
     92     return true;
     93 }
     94 
     95 bool JSHistory::getOwnPropertyDescriptorDelegate(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
     96 {
     97     if (!impl()->frame()) {
     98         descriptor.setUndefined();
     99         return true;
    100     }
    101 
    102     // Throw out all cross domain access
    103     if (!allowsAccessFromFrame(exec, impl()->frame()))
    104         return true;
    105 
    106     // Check for the few functions that we allow, even when called cross-domain.
    107     const HashEntry* entry = JSHistoryPrototype::s_info.propHashTable(exec)->entry(exec, propertyName);
    108     if (entry) {
    109         PropertySlot slot;
    110         // Allow access to back(), forward() and go() from any frame.
    111         if (entry->attributes() & Function) {
    112             if (entry->function() == jsHistoryPrototypeFunctionBack) {
    113                 slot.setCustom(this, nonCachingStaticBackFunctionGetter);
    114                 descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes());
    115                 return true;
    116             } else if (entry->function() == jsHistoryPrototypeFunctionForward) {
    117                 slot.setCustom(this, nonCachingStaticForwardFunctionGetter);
    118                 descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes());
    119                 return true;
    120             } else if (entry->function() == jsHistoryPrototypeFunctionGo) {
    121                 slot.setCustom(this, nonCachingStaticGoFunctionGetter);
    122                 descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes());
    123                 return true;
    124             }
    125         }
    126     } else {
    127         // Allow access to toString() cross-domain, but always Object.toString.
    128         if (propertyName == exec->propertyNames().toString) {
    129             PropertySlot slot;
    130             slot.setCustom(this, objectToStringFunctionGetter);
    131             descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes());
    132             return true;
    133         }
    134     }
    135 
    136     descriptor.setUndefined();
    137     return true;
    138 }
    139 
    140 bool JSHistory::putDelegate(ExecState* exec, const Identifier&, JSValue, PutPropertySlot&)
    141 {
    142     // Only allow putting by frames in the same origin.
    143     if (!allowsAccessFromFrame(exec, impl()->frame()))
    144         return true;
    145     return false;
    146 }
    147 
    148 bool JSHistory::deleteProperty(ExecState* exec, const Identifier& propertyName)
    149 {
    150     // Only allow deleting by frames in the same origin.
    151     if (!allowsAccessFromFrame(exec, impl()->frame()))
    152         return false;
    153     return Base::deleteProperty(exec, propertyName);
    154 }
    155 
    156 void JSHistory::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
    157 {
    158     // Only allow the history object to enumerated by frames in the same origin.
    159     if (!allowsAccessFromFrame(exec, impl()->frame()))
    160         return;
    161     Base::getOwnPropertyNames(exec, propertyNames, mode);
    162 }
    163 
    164 JSValue JSHistory::pushState(ExecState* exec)
    165 {
    166     RefPtr<SerializedScriptValue> historyState = SerializedScriptValue::create(exec, exec->argument(0));
    167     if (exec->hadException())
    168         return jsUndefined();
    169 
    170     String title = valueToStringWithUndefinedOrNullCheck(exec, exec->argument(1));
    171     if (exec->hadException())
    172         return jsUndefined();
    173 
    174     String url;
    175     if (exec->argumentCount() > 2) {
    176         url = valueToStringWithUndefinedOrNullCheck(exec, exec->argument(2));
    177         if (exec->hadException())
    178             return jsUndefined();
    179     }
    180 
    181     ExceptionCode ec = 0;
    182     impl()->stateObjectAdded(historyState.release(), title, url, History::StateObjectPush, ec);
    183     setDOMException(exec, ec);
    184 
    185     return jsUndefined();
    186 }
    187 
    188 JSValue JSHistory::replaceState(ExecState* exec)
    189 {
    190     RefPtr<SerializedScriptValue> historyState = SerializedScriptValue::create(exec, exec->argument(0));
    191     if (exec->hadException())
    192         return jsUndefined();
    193 
    194     String title = valueToStringWithUndefinedOrNullCheck(exec, exec->argument(1));
    195     if (exec->hadException())
    196         return jsUndefined();
    197 
    198     String url;
    199     if (exec->argumentCount() > 2) {
    200         url = valueToStringWithUndefinedOrNullCheck(exec, exec->argument(2));
    201         if (exec->hadException())
    202             return jsUndefined();
    203     }
    204 
    205     ExceptionCode ec = 0;
    206     impl()->stateObjectAdded(historyState.release(), title, url, History::StateObjectReplace, ec);
    207     setDOMException(exec, ec);
    208 
    209     return jsUndefined();
    210 }
    211 
    212 } // namespace WebCore
    213