• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2009, The Android Open Source Project
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  *  * Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  *  * 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 THE COPYRIGHT HOLDERS ``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 COMPUTER, 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 #include "config.h"
27 #include "GeolocationPermissions.h"
28 
29 #include "DOMWindow.h"
30 #include "Frame.h"
31 #include "Geolocation.h"
32 #include "Navigator.h"
33 #include "SQLiteDatabase.h"
34 #include "SQLiteStatement.h"
35 #include "SQLiteTransaction.h"
36 #include "WebViewCore.h"
37 
38 using namespace WebCore;
39 
40 namespace android {
41 
42 GeolocationPermissions::PermissionsMap GeolocationPermissions::s_permanentPermissions;
43 GeolocationPermissions::GeolocationPermissionsVector GeolocationPermissions::s_instances;
44 bool GeolocationPermissions::s_alwaysDeny = false;
45 String GeolocationPermissions::s_databasePath;
46 bool GeolocationPermissions::s_permanentPermissionsLoaded = false;
47 
48 static const char* databaseName = "/GeolocationPermissions.db";
49 
GeolocationPermissions(WebViewCore * webViewCore,Frame * mainFrame)50 GeolocationPermissions::GeolocationPermissions(WebViewCore* webViewCore, Frame* mainFrame)
51     : m_webViewCore(webViewCore)
52     , m_mainFrame(mainFrame)
53     , m_timer(this, &GeolocationPermissions::timerFired)
54 
55 {
56     ASSERT(m_webViewCore);
57     maybeLoadPermanentPermissions();
58     s_instances.append(this);
59 }
60 
~GeolocationPermissions()61 GeolocationPermissions::~GeolocationPermissions()
62 {
63     size_t index = s_instances.find(this);
64     s_instances.remove(index);
65 }
66 
queryPermissionState(Frame * frame)67 void GeolocationPermissions::queryPermissionState(Frame* frame)
68 {
69     ASSERT(s_permanentPermissionsLoaded);
70 
71     // We use SecurityOrigin::toString to key the map. Note that testing
72     // the SecurityOrigin pointer for equality is insufficient.
73     String originString = frame->document()->securityOrigin()->toString();
74 
75     // If we've been told to always deny requests, do so.
76     if (s_alwaysDeny) {
77         makeAsynchronousCallbackToGeolocation(originString, false);
78         return;
79     }
80 
81     // See if we have a record for this origin in the permanent permissions.
82     // These take precedence over temporary permissions so that changes made
83     // from the browser settings work as intended.
84     PermissionsMap::const_iterator iter = s_permanentPermissions.find(originString);
85     PermissionsMap::const_iterator end = s_permanentPermissions.end();
86     if (iter != end) {
87         bool allow = iter->second;
88         makeAsynchronousCallbackToGeolocation(originString, allow);
89         return;
90     }
91 
92     // Check the temporary permisions.
93     iter = m_temporaryPermissions.find(originString);
94     end = m_temporaryPermissions.end();
95     if (iter != end) {
96         bool allow = iter->second;
97         makeAsynchronousCallbackToGeolocation(originString, allow);
98         return;
99     }
100 
101     // If there's no pending request, prompt the user.
102     if (m_originInProgress.isEmpty()) {
103         m_originInProgress = originString;
104 
105         // Although multiple tabs may request permissions for the same origin
106         // simultaneously, the routing in WebViewCore/CallbackProxy ensures that
107         // the result of the request will make it back to this object, so
108         // there's no need for a globally unique ID for the request.
109         m_webViewCore->geolocationPermissionsShowPrompt(m_originInProgress);
110         return;
111     }
112 
113     // If the request in progress is not for this origin, queue this request.
114     if ((m_originInProgress != originString)
115         && (m_queuedOrigins.find(originString) == WTF::notFound))
116         m_queuedOrigins.append(originString);
117 }
118 
makeAsynchronousCallbackToGeolocation(String origin,bool allow)119 void GeolocationPermissions::makeAsynchronousCallbackToGeolocation(String origin, bool allow)
120 {
121     m_callbackData.origin = origin;
122     m_callbackData.allow = allow;
123     m_timer.startOneShot(0);
124 }
125 
providePermissionState(String origin,bool allow,bool remember)126 void GeolocationPermissions::providePermissionState(String origin, bool allow, bool remember)
127 {
128     ASSERT(s_permanentPermissionsLoaded);
129 
130     // It's possible that this method is called with an origin that doesn't
131     // match m_originInProgress. This can occur if this object is reset
132     // while a permission result is in the process of being marshalled back to
133     // the WebCore thread from the browser. In this case, we simply ignore the
134     // call.
135     if (origin != m_originInProgress)
136         return;
137 
138     maybeCallbackFrames(m_originInProgress, allow);
139     recordPermissionState(origin, allow, remember);
140 
141     // If the permissions are set to be remembered, cancel any queued requests
142     // for this domain in other tabs.
143     if (remember)
144         cancelPendingRequestsInOtherTabs(m_originInProgress);
145 
146     // Clear the origin in progress to signal that this request is done.
147     m_originInProgress = "";
148 
149     // If there are other requests queued, start the next one.
150     if (!m_queuedOrigins.isEmpty()) {
151         m_originInProgress = m_queuedOrigins.first();
152         m_queuedOrigins.remove(0);
153         m_webViewCore->geolocationPermissionsShowPrompt(m_originInProgress);
154     }
155 }
156 
recordPermissionState(String origin,bool allow,bool remember)157 void GeolocationPermissions::recordPermissionState(String origin, bool allow, bool remember)
158 {
159     if (remember) {
160         s_permanentPermissions.set(m_originInProgress, allow);
161     } else {
162         // It's possible that another tab recorded a permanent permission for
163         // this origin while our request was in progress, but we record it
164         // anyway.
165         m_temporaryPermissions.set(m_originInProgress, allow);
166     }
167 }
168 
cancelPendingRequestsInOtherTabs(String origin)169 void GeolocationPermissions::cancelPendingRequestsInOtherTabs(String origin)
170 {
171     for (GeolocationPermissionsVector::const_iterator iter = s_instances.begin();
172          iter != s_instances.end();
173          ++iter)
174         (*iter)->cancelPendingRequests(origin);
175 }
176 
cancelPendingRequests(String origin)177 void GeolocationPermissions::cancelPendingRequests(String origin)
178 {
179     size_t index = m_queuedOrigins.find(origin);
180     if (index != WTF::notFound) {
181         // Get the permission from the permanent list.
182         PermissionsMap::const_iterator iter = s_permanentPermissions.find(origin);
183 #ifndef NDEBUG
184         PermissionsMap::const_iterator end = s_permanentPermissions.end();
185         ASSERT(iter != end);
186 #endif
187         bool allow = iter->second;
188 
189         maybeCallbackFrames(origin, allow);
190 
191         m_queuedOrigins.remove(index);
192     }
193 }
194 
timerFired(Timer<GeolocationPermissions> * timer)195 void GeolocationPermissions::timerFired(Timer<GeolocationPermissions>* timer)
196 {
197     ASSERT_UNUSED(timer, timer == &m_timer);
198     maybeCallbackFrames(m_callbackData.origin, m_callbackData.allow);
199 }
200 
resetTemporaryPermissionStates()201 void GeolocationPermissions::resetTemporaryPermissionStates()
202 {
203     ASSERT(s_permanentPermissionsLoaded);
204     m_originInProgress = "";
205     m_queuedOrigins.clear();
206     m_temporaryPermissions.clear();
207     // If any permission results are being marshalled back to this thread, this
208     // will render them inefective.
209     m_timer.stop();
210 
211     m_webViewCore->geolocationPermissionsHidePrompt();
212 }
213 
maybeCallbackFrames(String origin,bool allow)214 void GeolocationPermissions::maybeCallbackFrames(String origin, bool allow)
215 {
216     // We can't track which frame issued the request, as frames can be deleted
217     // or have their contents replaced. Even uniqueChildName is not unique when
218     // frames are dynamically deleted and created. Instead, we simply call back
219     // to the Geolocation object in all frames from the correct origin.
220     for (Frame* frame = m_mainFrame; frame; frame = frame->tree()->traverseNext()) {
221         if (origin == frame->document()->securityOrigin()->toString()) {
222             // If the page has changed, it may no longer have a Geolocation
223             // object.
224             Geolocation* geolocation = frame->domWindow()->navigator()->optionalGeolocation();
225             if (geolocation)
226                 geolocation->setIsAllowed(allow);
227         }
228     }
229 }
230 
getOrigins()231 GeolocationPermissions::OriginSet GeolocationPermissions::getOrigins()
232 {
233     maybeLoadPermanentPermissions();
234     OriginSet origins;
235     PermissionsMap::const_iterator end = s_permanentPermissions.end();
236     for (PermissionsMap::const_iterator iter = s_permanentPermissions.begin(); iter != end; ++iter)
237         origins.add(iter->first);
238     return origins;
239 }
240 
getAllowed(String origin)241 bool GeolocationPermissions::getAllowed(String origin)
242 {
243     maybeLoadPermanentPermissions();
244     bool allowed = false;
245     PermissionsMap::const_iterator iter = s_permanentPermissions.find(origin);
246     PermissionsMap::const_iterator end = s_permanentPermissions.end();
247     if (iter != end)
248         allowed = iter->second;
249     return allowed;
250 }
251 
clear(String origin)252 void GeolocationPermissions::clear(String origin)
253 {
254     maybeLoadPermanentPermissions();
255     PermissionsMap::iterator iter = s_permanentPermissions.find(origin);
256     if (iter != s_permanentPermissions.end())
257         s_permanentPermissions.remove(iter);
258 }
259 
allow(String origin)260 void GeolocationPermissions::allow(String origin)
261 {
262     maybeLoadPermanentPermissions();
263     // We replace any existing permanent permission.
264     s_permanentPermissions.set(origin, true);
265 }
266 
clearAll()267 void GeolocationPermissions::clearAll()
268 {
269     maybeLoadPermanentPermissions();
270     s_permanentPermissions.clear();
271 }
272 
maybeLoadPermanentPermissions()273 void GeolocationPermissions::maybeLoadPermanentPermissions()
274 {
275     if (s_permanentPermissionsLoaded)
276         return;
277     s_permanentPermissionsLoaded = true;
278 
279     SQLiteDatabase database;
280     if (!database.open(s_databasePath + databaseName))
281         return;
282 
283     // Create the table here, such that even if we've just created the DB, the
284     // commands below should succeed.
285     if (!database.executeCommand("CREATE TABLE IF NOT EXISTS Permissions (origin TEXT UNIQUE NOT NULL, allow INTEGER NOT NULL)")) {
286         database.close();
287         return;
288     }
289 
290     SQLiteStatement statement(database, "SELECT * FROM Permissions");
291     if (statement.prepare() != SQLResultOk) {
292         database.close();
293         return;
294     }
295 
296     ASSERT(s_permanentPermissions.size() == 0);
297     while (statement.step() == SQLResultRow)
298         s_permanentPermissions.set(statement.getColumnText(0), statement.getColumnInt64(1));
299 
300     database.close();
301 }
302 
maybeStorePermanentPermissions()303 void GeolocationPermissions::maybeStorePermanentPermissions()
304 {
305     // Protect against the case where we haven't yet loaded permissions, as
306     // saving in this case would overwrite the stored permissions with the empty
307     // set. This is safe as the permissions are always loaded before they are
308     // modified.
309     if (!s_permanentPermissionsLoaded)
310         return;
311 
312     SQLiteDatabase database;
313     if (!database.open(s_databasePath + databaseName))
314         return;
315 
316     SQLiteTransaction transaction(database);
317 
318     // The number of entries should be small enough that it's not worth trying
319     // to perform a diff. Simply clear the table and repopulate it.
320     if (!database.executeCommand("DELETE FROM Permissions")) {
321         database.close();
322         return;
323     }
324 
325     PermissionsMap::const_iterator end = s_permanentPermissions.end();
326     for (PermissionsMap::const_iterator iter = s_permanentPermissions.begin(); iter != end; ++iter) {
327          SQLiteStatement statement(database, "INSERT INTO Permissions (origin, allow) VALUES (?, ?)");
328          if (statement.prepare() != SQLResultOk)
329              continue;
330          statement.bindText(1, iter->first);
331          statement.bindInt64(2, iter->second);
332          statement.executeCommand();
333     }
334 
335     transaction.commit();
336     database.close();
337 }
338 
setDatabasePath(String path)339 void GeolocationPermissions::setDatabasePath(String path)
340 {
341     // Take the first non-empty value.
342     if (s_databasePath.length() > 0)
343         return;
344     s_databasePath = path;
345 }
346 
setAlwaysDeny(bool deny)347 void GeolocationPermissions::setAlwaysDeny(bool deny)
348 {
349     s_alwaysDeny = deny;
350 }
351 
352 }  // namespace android
353