1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef ScriptState_h
6#define ScriptState_h
7
8#include "bindings/core/v8/ScopedPersistent.h"
9#include "bindings/core/v8/V8PerContextData.h"
10#include "wtf/RefCounted.h"
11#include <v8.h>
12
13namespace blink {
14
15class LocalDOMWindow;
16class DOMWrapperWorld;
17class ExecutionContext;
18class LocalFrame;
19class ScriptValue;
20
21// ScriptState is created when v8::Context is created.
22// ScriptState is destroyed when v8::Context is garbage-collected and
23// all V8 proxy objects that have references to the ScriptState are destructed.
24class ScriptState : public RefCounted<ScriptState> {
25    WTF_MAKE_NONCOPYABLE(ScriptState);
26public:
27    class Scope {
28    public:
29        // You need to make sure that scriptState->context() is not empty before creating a Scope.
30        explicit Scope(ScriptState* scriptState)
31            : m_handleScope(scriptState->isolate())
32            , m_context(scriptState->context())
33        {
34            ASSERT(!m_context.IsEmpty());
35            m_context->Enter();
36        }
37
38        ~Scope()
39        {
40            m_context->Exit();
41        }
42
43    private:
44        v8::HandleScope m_handleScope;
45        v8::Handle<v8::Context> m_context;
46    };
47
48    static PassRefPtr<ScriptState> create(v8::Handle<v8::Context>, PassRefPtr<DOMWrapperWorld>);
49    virtual ~ScriptState();
50
51    static ScriptState* current(v8::Isolate* isolate)
52    {
53        return from(isolate->GetCurrentContext());
54    }
55
56    static ScriptState* from(v8::Handle<v8::Context> context)
57    {
58        ASSERT(!context.IsEmpty());
59        ScriptState* scriptState = static_cast<ScriptState*>(context->GetAlignedPointerFromEmbedderData(v8ContextPerContextDataIndex));
60        // ScriptState::from() must not be called for a context that does not have
61        // valid embedder data in the embedder field.
62        RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(scriptState);
63        RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(scriptState->context() == context);
64        return scriptState;
65    }
66
67    static ScriptState* forMainWorld(LocalFrame*);
68
69    v8::Isolate* isolate() const { return m_isolate; }
70    DOMWrapperWorld& world() const { return *m_world; }
71    LocalDOMWindow* domWindow() const;
72    virtual ExecutionContext* executionContext() const;
73    virtual void setExecutionContext(ExecutionContext*);
74
75    // This can return an empty handle if the v8::Context is gone.
76    v8::Handle<v8::Context> context() const { return m_context.newLocal(m_isolate); }
77    bool contextIsValid() const { return m_context.isEmpty() || m_globalObjectDetached; }
78    void detachGlobalObject();
79    void clearContext() { return m_context.clear(); }
80
81    V8PerContextData* perContextData() const { return m_perContextData.get(); }
82    void disposePerContextData() { m_perContextData = nullptr; }
83
84    bool evalEnabled() const;
85    void setEvalEnabled(bool);
86    ScriptValue getFromGlobalObject(const char* name);
87
88protected:
89    ScriptState(v8::Handle<v8::Context>, PassRefPtr<DOMWrapperWorld>);
90
91private:
92    v8::Isolate* m_isolate;
93    // This persistent handle is weak.
94    ScopedPersistent<v8::Context> m_context;
95
96    // This RefPtr doesn't cause a cycle because all persistent handles that DOMWrapperWorld holds are weak.
97    RefPtr<DOMWrapperWorld> m_world;
98
99    // This OwnPtr causes a cycle:
100    // V8PerContextData --(Persistent)--> v8::Context --(RefPtr)--> ScriptState --(OwnPtr)--> V8PerContextData
101    // So you must explicitly clear the OwnPtr by calling disposePerContextData()
102    // once you no longer need V8PerContextData. Otherwise, the v8::Context will leak.
103    OwnPtr<V8PerContextData> m_perContextData;
104
105    bool m_globalObjectDetached;
106};
107
108class ScriptStateForTesting : public ScriptState {
109public:
110    static PassRefPtr<ScriptStateForTesting> create(v8::Handle<v8::Context>, PassRefPtr<DOMWrapperWorld>);
111
112    virtual ExecutionContext* executionContext() const OVERRIDE;
113    virtual void setExecutionContext(ExecutionContext*) OVERRIDE;
114
115private:
116    ScriptStateForTesting(v8::Handle<v8::Context>, PassRefPtr<DOMWrapperWorld>);
117
118    ExecutionContext* m_executionContext;
119};
120
121// ScriptStateProtectingContext keeps the context associated with the ScriptState alive.
122// You need to call clear() once you no longer need the context. Otherwise, the context will leak.
123class ScriptStateProtectingContext {
124    WTF_MAKE_NONCOPYABLE(ScriptStateProtectingContext);
125public:
126    ScriptStateProtectingContext(ScriptState* scriptState)
127        : m_scriptState(scriptState)
128    {
129        if (m_scriptState)
130            m_context.set(m_scriptState->isolate(), m_scriptState->context());
131    }
132
133    ScriptState* operator->() const { return m_scriptState.get(); }
134    ScriptState* get() const { return m_scriptState.get(); }
135    void clear()
136    {
137        m_scriptState = nullptr;
138        m_context.clear();
139    }
140
141private:
142    RefPtr<ScriptState> m_scriptState;
143    ScopedPersistent<v8::Context> m_context;
144};
145
146}
147
148#endif // ScriptState_h
149