/*
 * Copyright (C) 2009 Google Inc. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef BindingSecurity_h
#define BindingSecurity_h

#include "BindingSecurityBase.h"
#include "Element.h"
#include "Frame.h"
#include "GenericBinding.h"
#include "HTMLFrameElementBase.h"
#include "HTMLNames.h"
#include "HTMLParserIdioms.h"
#include "ScriptController.h"
#include "Settings.h"

namespace WebCore {

class DOMWindow;
class Node;

// Security functions shared by various language bindings.
template <class Binding>
class BindingSecurity : public BindingSecurityBase {
public:
    // Check if the active execution context can access the target frame.
    static bool canAccessFrame(State<Binding>*, Frame*, bool reportError);

    // Check if it is safe to access the given node from the
    // current security context.
    static bool checkNodeSecurity(State<Binding>*, Node* target);

    static bool allowPopUp(State<Binding>*);
    static bool allowSettingFrameSrcToJavascriptUrl(State<Binding>*, HTMLFrameElementBase*, const String& value);
    static bool allowSettingSrcToJavascriptURL(State<Binding>*, Element*, const String& name, const String& value);

    static bool shouldAllowNavigation(State<Binding>*, Frame*);

private:
    explicit BindingSecurity() {}
    ~BindingSecurity();

    // Check if the current DOMWindow's security context can access the target
    // DOMWindow.  This function does not report errors, so most callers should
    // use canAccessFrame instead.
    static bool canAccessWindow(State<Binding>*, DOMWindow* target);
};

// Implementations of templated methods must be in this file.

template <class Binding>
bool BindingSecurity<Binding>::canAccessWindow(State<Binding>* state,
                                               DOMWindow* targetWindow)
{
    DOMWindow* activeWindow = state->activeWindow();
    return canAccess(activeWindow, targetWindow);
}

template <class Binding>
bool BindingSecurity<Binding>::canAccessFrame(State<Binding>* state,
                                              Frame* target,
                                              bool reportError)
{
    // The subject is detached from a frame, deny accesses.
    if (!target)
        return false;

    if (!canAccessWindow(state, getDOMWindow(target))) {
        if (reportError)
            state->immediatelyReportUnsafeAccessTo(target);
        return false;
    }
    return true;
}

template <class Binding>
bool BindingSecurity<Binding>::checkNodeSecurity(State<Binding>* state, Node* node)
{
    if (!node)
        return false;

    Frame* target = getFrame(node);

    if (!target)
        return false;

    return canAccessFrame(state, target, true);
}

template <class Binding>
bool BindingSecurity<Binding>::allowPopUp(State<Binding>* state)
{
    if (ScriptController::processingUserGesture())
        return true;

    Frame* frame = state->firstFrame();
    ASSERT(frame);
    Settings* settings = frame->settings();
    return settings && settings->javaScriptCanOpenWindowsAutomatically();
}

template <class Binding>
bool BindingSecurity<Binding>::allowSettingFrameSrcToJavascriptUrl(State<Binding>* state, HTMLFrameElementBase* frame, const String& value)
{
    if (protocolIsJavaScript(stripLeadingAndTrailingHTMLSpaces(value))) {
        Node* contentDoc = frame->contentDocument();
        if (contentDoc && !checkNodeSecurity(state, contentDoc))
            return false;
    }
    return true;
}

template <class Binding>
bool BindingSecurity<Binding>::allowSettingSrcToJavascriptURL(State<Binding>* state, Element* element, const String& name, const String& value)
{
    if ((element->hasTagName(HTMLNames::iframeTag) || element->hasTagName(HTMLNames::frameTag)) && equalIgnoringCase(name, "src"))
        return allowSettingFrameSrcToJavascriptUrl(state, static_cast<HTMLFrameElementBase*>(element), value);
    return true;
}

template <class Binding>
bool BindingSecurity<Binding>::shouldAllowNavigation(State<Binding>* state, Frame* frame)
{
    Frame* activeFrame = state->activeFrame();
    return activeFrame && activeFrame->loader()->shouldAllowNavigation(frame);
}

}

#endif // BindingSecurity_h
