1/*
2 * Copyright (C) 2013 Igalia S.L.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2,1 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 "WebKitScriptWorld.h"
22
23#include "WebKitMarshal.h"
24#include "WebKitPrivate.h"
25#include "WebKitScriptWorldPrivate.h"
26#include <wtf/HashMap.h>
27#include <wtf/NeverDestroyed.h>
28
29using namespace WebKit;
30using namespace WebCore;
31
32enum {
33    WINDOW_OBJECT_CLEARED,
34
35    LAST_SIGNAL
36};
37
38typedef HashMap<InjectedBundleScriptWorld*, WebKitScriptWorld*> ScriptWorldMap;
39
40static ScriptWorldMap& scriptWorlds()
41{
42    static NeverDestroyed<ScriptWorldMap> map;
43    return map;
44}
45
46struct _WebKitScriptWorldPrivate {
47    ~_WebKitScriptWorldPrivate()
48    {
49        ASSERT(scriptWorlds().contains(scriptWorld.get()));
50        scriptWorlds().remove(scriptWorld.get());
51    }
52
53    RefPtr<InjectedBundleScriptWorld> scriptWorld;
54};
55
56static guint signals[LAST_SIGNAL] = { 0, };
57
58WEBKIT_DEFINE_TYPE(WebKitScriptWorld, webkit_script_world, G_TYPE_OBJECT)
59
60static void webkit_script_world_class_init(WebKitScriptWorldClass* klass)
61{
62    /**
63     * WebKitScriptWorld::window-object-cleared:
64     * @world: the #WebKitScriptWorld on which the signal is emitted
65     * @page: a #WebKitWebPage
66     * @frame: the #WebKitFrame  to which @world belongs
67     *
68     * Emitted when the JavaScript window object in a #WebKitScriptWorld has been
69     * cleared. This is the preferred place to set custom properties on the window
70     * object using the JavaScriptCore API. You can get the window object of @frame
71     * from the JavaScript execution context of @world that is returned by
72     * webkit_frame_get_javascript_context_for_script_world().
73     *
74     * Since: 2.2
75     */
76    signals[WINDOW_OBJECT_CLEARED] = g_signal_new(
77        "window-object-cleared",
78        G_TYPE_FROM_CLASS(klass),
79        G_SIGNAL_RUN_LAST,
80        0, 0, 0,
81        webkit_marshal_VOID__OBJECT_OBJECT,
82        G_TYPE_NONE, 2,
83        WEBKIT_TYPE_WEB_PAGE,
84        WEBKIT_TYPE_FRAME);
85}
86
87WebKitScriptWorld* webkitScriptWorldGet(InjectedBundleScriptWorld* scriptWorld)
88{
89    return scriptWorlds().get(scriptWorld);
90}
91
92InjectedBundleScriptWorld* webkitScriptWorldGetInjectedBundleScriptWorld(WebKitScriptWorld* world)
93{
94    return world->priv->scriptWorld.get();
95}
96
97void webkitScriptWorldWindowObjectCleared(WebKitScriptWorld* world, WebKitWebPage* page, WebKitFrame* frame)
98{
99    g_signal_emit(world, signals[WINDOW_OBJECT_CLEARED], 0, page, frame);
100}
101
102static WebKitScriptWorld* webkitScriptWorldCreate(PassRefPtr<InjectedBundleScriptWorld> scriptWorld)
103{
104    WebKitScriptWorld* world = WEBKIT_SCRIPT_WORLD(g_object_new(WEBKIT_TYPE_SCRIPT_WORLD, NULL));
105    world->priv->scriptWorld = scriptWorld;
106
107    ASSERT(!scriptWorlds().contains(world->priv->scriptWorld.get()));
108    scriptWorlds().add(world->priv->scriptWorld.get(), world);
109
110    return world;
111}
112
113static gpointer createDefaultScriptWorld(gpointer)
114{
115    return webkitScriptWorldCreate(InjectedBundleScriptWorld::normalWorld());
116}
117
118/**
119 * webkit_script_world_get_default:
120 *
121 * Get the default #WebKitScriptWorld. This is the normal script world
122 * where all scripts are executed by default.
123 * You can get the JavaScript execution context of a #WebKitScriptWorld
124 * for a given #WebKitFrame with webkit_frame_get_javascript_context_for_script_world().
125 *
126 * Returns: (transfer none): the default #WebKitScriptWorld
127 *
128 * Since: 2.2
129 */
130WebKitScriptWorld* webkit_script_world_get_default(void)
131{
132    static GOnce onceInit = G_ONCE_INIT;
133    return WEBKIT_SCRIPT_WORLD(g_once(&onceInit, createDefaultScriptWorld, 0));
134}
135
136/**
137 * webkit_script_world_new:
138 *
139 * Creates a new isolated #WebKitScriptWorld. Scripts executed in
140 * isolated worlds have access to the DOM but not to other variable
141 * or functions created by the page.
142 * You can get the JavaScript execution context of a #WebKitScriptWorld
143 * for a given #WebKitFrame with webkit_frame_get_javascript_context_for_script_world().
144 *
145 * Returns: (transfer full): a new isolated #WebKitScriptWorld
146 *
147 * Since: 2.2
148 */
149WebKitScriptWorld* webkit_script_world_new(void)
150{
151    return webkitScriptWorldCreate(InjectedBundleScriptWorld::create());
152}
153