1/*
2 * Copyright (C) 2007,2012 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 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#import "WebDatabaseManagerClient.h"
27
28#if ENABLE(SQL_DATABASE)
29
30#import "WebDatabaseManagerPrivate.h"
31#import "WebSecurityOriginInternal.h"
32#import <wtf/MainThread.h>
33#import <wtf/RetainPtr.h>
34#import <WebCore/DatabaseTracker.h>
35#import <WebCore/SecurityOrigin.h>
36
37#if PLATFORM(IOS)
38#import <WebCore/WebCoreThread.h>
39#endif
40
41using namespace WebCore;
42
43#if PLATFORM(IOS)
44static CFStringRef WebDatabaseOriginWasAddedNotification = CFSTR("com.apple.MobileSafariSettings.WebDatabaseOriginWasAddedNotification");
45static CFStringRef WebDatabaseWasDeletedNotification = CFSTR("com.apple.MobileSafariSettings.WebDatabaseWasDeletedNotification");
46static CFStringRef WebDatabaseOriginWasDeletedNotification = CFSTR("com.apple.MobileSafariSettings.WebDatabaseOriginWasDeletedNotification");
47#endif
48
49WebDatabaseManagerClient* WebDatabaseManagerClient::sharedWebDatabaseManagerClient()
50{
51    static WebDatabaseManagerClient* sharedClient = new WebDatabaseManagerClient();
52    return sharedClient;
53}
54
55#if PLATFORM(IOS)
56static void onNewDatabaseOriginAdded(CFNotificationCenterRef, void* observer, CFStringRef, const void*, CFDictionaryRef)
57{
58    ASSERT(observer);
59
60    WebDatabaseManagerClient* client = reinterpret_cast<WebDatabaseManagerClient*>(observer);
61    client->newDatabaseOriginWasAdded();
62}
63
64static void onDatabaseDeleted(CFNotificationCenterRef, void* observer, CFStringRef, const void*, CFDictionaryRef)
65{
66    ASSERT(observer);
67
68    WebDatabaseManagerClient* client = reinterpret_cast<WebDatabaseManagerClient*>(observer);
69    client->databaseWasDeleted();
70}
71
72static void onDatabaseOriginDeleted(CFNotificationCenterRef, void* observer, CFStringRef, const void*, CFDictionaryRef)
73{
74    ASSERT(observer);
75
76    WebDatabaseManagerClient* client = reinterpret_cast<WebDatabaseManagerClient*>(observer);
77    client->databaseOriginWasDeleted();
78}
79#endif
80
81WebDatabaseManagerClient::WebDatabaseManagerClient()
82#if PLATFORM(IOS)
83    : m_isHandlingNewDatabaseOriginNotification(false)
84    , m_isHandlingDeleteDatabaseNotification(false)
85    , m_isHandlingDeleteDatabaseOriginNotification(false)
86#endif
87{
88#if PLATFORM(IOS)
89    CFNotificationCenterRef center = CFNotificationCenterGetDarwinNotifyCenter();
90    CFNotificationCenterAddObserver(center, this, onNewDatabaseOriginAdded, WebDatabaseOriginWasAddedNotification, 0, CFNotificationSuspensionBehaviorDeliverImmediately);
91    CFNotificationCenterAddObserver(center, this, onDatabaseDeleted, WebDatabaseWasDeletedNotification, 0, CFNotificationSuspensionBehaviorDeliverImmediately);
92    CFNotificationCenterAddObserver(center, this, onDatabaseOriginDeleted, WebDatabaseOriginWasDeletedNotification, 0, CFNotificationSuspensionBehaviorDeliverImmediately);
93#endif
94}
95
96WebDatabaseManagerClient::~WebDatabaseManagerClient()
97{
98#if PLATFORM(IOS)
99    CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(), this, 0, 0);
100#endif
101}
102
103class DidModifyOriginData {
104    WTF_MAKE_NONCOPYABLE(DidModifyOriginData);
105public:
106    static void dispatchToMainThread(WebDatabaseManagerClient* client, SecurityOrigin* origin)
107    {
108        DidModifyOriginData* context = new DidModifyOriginData(client, origin->isolatedCopy());
109        callOnMainThread(&DidModifyOriginData::dispatchDidModifyOriginOnMainThread, context);
110    }
111
112private:
113    DidModifyOriginData(WebDatabaseManagerClient* client, PassRefPtr<SecurityOrigin> origin)
114        : client(client)
115        , origin(origin)
116    {
117    }
118
119    static void dispatchDidModifyOriginOnMainThread(void* context)
120    {
121        ASSERT(isMainThread());
122        DidModifyOriginData* info = static_cast<DidModifyOriginData*>(context);
123        info->client->dispatchDidModifyOrigin(info->origin.get());
124        delete info;
125    }
126
127    WebDatabaseManagerClient* client;
128    RefPtr<SecurityOrigin> origin;
129};
130
131void WebDatabaseManagerClient::dispatchDidModifyOrigin(SecurityOrigin* origin)
132{
133    if (!isMainThread()) {
134        DidModifyOriginData::dispatchToMainThread(this, origin);
135        return;
136    }
137
138    RetainPtr<WebSecurityOrigin> webSecurityOrigin = adoptNS([[WebSecurityOrigin alloc] _initWithWebCoreSecurityOrigin:origin]);
139
140    [[NSNotificationCenter defaultCenter] postNotificationName:WebDatabaseDidModifyOriginNotification
141                                                        object:webSecurityOrigin.get()];
142}
143
144void WebDatabaseManagerClient::dispatchDidModifyDatabase(SecurityOrigin* origin, const String& databaseIdentifier)
145{
146    if (!isMainThread()) {
147        DidModifyOriginData::dispatchToMainThread(this, origin);
148        return;
149    }
150
151    RetainPtr<WebSecurityOrigin> webSecurityOrigin = adoptNS([[WebSecurityOrigin alloc] _initWithWebCoreSecurityOrigin:origin]);
152    RetainPtr<NSDictionary> userInfo = adoptNS([[NSDictionary alloc]
153                                               initWithObjectsAndKeys:(NSString *)databaseIdentifier, WebDatabaseIdentifierKey, nil]);
154
155    [[NSNotificationCenter defaultCenter] postNotificationName:WebDatabaseDidModifyDatabaseNotification
156                                                        object:webSecurityOrigin.get()
157                                                      userInfo:userInfo.get()];
158}
159
160#if PLATFORM(IOS)
161void WebDatabaseManagerClient::dispatchDidAddNewOrigin(SecurityOrigin*)
162{
163    m_isHandlingNewDatabaseOriginNotification = true;
164    // Send a notification to all apps that a new origin has been added, so other apps with opened database can refresh their origin maps.
165    CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), WebDatabaseOriginWasAddedNotification, 0, 0, true);
166}
167
168void WebDatabaseManagerClient::dispatchDidDeleteDatabase()
169{
170    m_isHandlingDeleteDatabaseNotification = true;
171    // Send a notification to all apps that a database has been deleted, so other apps with the deleted database open will close it properly.
172    CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), WebDatabaseWasDeletedNotification, 0, 0, true);
173}
174
175void WebDatabaseManagerClient::dispatchDidDeleteDatabaseOrigin()
176{
177    m_isHandlingDeleteDatabaseOriginNotification = true;
178    // Send a notification to all apps that an origin has been deleted, so other apps can update their origin maps.
179    CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), WebDatabaseOriginWasDeletedNotification, 0, 0, true);
180}
181
182void WebDatabaseManagerClient::newDatabaseOriginWasAdded()
183{
184    // Locks the WebThread for the rest of the run loop.
185    WebThreadLock();
186
187    // If this is the process that added the new origin, its quota map should have been updated
188    // and does not need to be invalidated.
189    if (m_isHandlingNewDatabaseOriginNotification) {
190        m_isHandlingNewDatabaseOriginNotification = false;
191        return;
192    }
193
194    databaseOriginsDidChange();
195}
196
197void WebDatabaseManagerClient::databaseWasDeleted()
198{
199    // Locks the WebThread for the rest of the run loop.
200    WebThreadLock();
201
202    // If this is the process that added the new origin, its quota map should have been updated
203    // and does not need to be invalidated.
204    if (m_isHandlingDeleteDatabaseNotification) {
205        m_isHandlingDeleteDatabaseNotification = false;
206        return;
207    }
208
209    DatabaseTracker::tracker().removeDeletedOpenedDatabases();
210}
211
212void WebDatabaseManagerClient::databaseOriginWasDeleted()
213{
214    // Locks the WebThread for the rest of the run loop.
215    WebThreadLock();
216
217    // If this is the process that added the new origin, its quota map should have been updated
218    // and does not need to be invalidated.
219    if (m_isHandlingDeleteDatabaseOriginNotification) {
220        m_isHandlingDeleteDatabaseOriginNotification = false;
221        return;
222    }
223
224    databaseOriginsDidChange();
225}
226
227void WebDatabaseManagerClient::databaseOriginsDidChange()
228{
229    // Send a notification (within the app) about the origins change.  If an app needs to update its UI when the origins change
230    // (such as Safari Settings), it can listen for that notification.
231    CFNotificationCenterPostNotification(CFNotificationCenterGetLocalCenter(), WebDatabaseOriginsDidChangeNotification, 0, 0, true);
232}
233
234#endif // PLATFORM(IOS)
235
236#endif
237