diff options
author | Ben Murdoch <benm@google.com> | 2011-05-13 16:40:46 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2011-05-16 11:35:03 +0100 |
commit | a2c606d1d8312a5d063e4a11e5911d9c8e4a3d19 (patch) | |
tree | 614d69ba96a23bc057e539a3c8a7d4961a68254b /Source/WebKit/android/WebCoreSupport/GeolocationPermissions.cpp | |
parent | 65f03d4f644ce73618e5f4f50dd694b26f55ae12 (diff) | |
download | external_webkit-a2c606d1d8312a5d063e4a11e5911d9c8e4a3d19.zip external_webkit-a2c606d1d8312a5d063e4a11e5911d9c8e4a3d19.tar.gz external_webkit-a2c606d1d8312a5d063e4a11e5911d9c8e4a3d19.tar.bz2 |
Merge WebKit at r75993: Move WebKit/android files to Source
Change-Id: Ifa871f8320bdb3a09fe189fffecc23f702c394b9
Diffstat (limited to 'Source/WebKit/android/WebCoreSupport/GeolocationPermissions.cpp')
-rwxr-xr-x | Source/WebKit/android/WebCoreSupport/GeolocationPermissions.cpp | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/Source/WebKit/android/WebCoreSupport/GeolocationPermissions.cpp b/Source/WebKit/android/WebCoreSupport/GeolocationPermissions.cpp new file mode 100755 index 0000000..36a9b61 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/GeolocationPermissions.cpp @@ -0,0 +1,419 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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. + */ + +#include "config.h" +#include "GeolocationPermissions.h" + +#include "DOMWindow.h" +#include "Frame.h" +#include "Geolocation.h" +#include "Navigator.h" +#include "SQLiteDatabase.h" +#include "SQLiteFileSystem.h" +#include "SQLiteStatement.h" +#include "SQLiteTransaction.h" +#include "WebViewCore.h" + +#include <text/CString.h> + +using namespace WebCore; + +namespace android { + +GeolocationPermissions::PermissionsMap GeolocationPermissions::s_permanentPermissions; +GeolocationPermissions::GeolocationPermissionsVector GeolocationPermissions::s_instances; +bool GeolocationPermissions::s_alwaysDeny = false; +bool GeolocationPermissions::s_permanentPermissionsLoaded = false; +bool GeolocationPermissions::s_permanentPermissionsModified = false; +String GeolocationPermissions::s_databasePath; + +static const char* databaseName = "GeolocationPermissions.db"; + +GeolocationPermissions::GeolocationPermissions(WebViewCore* webViewCore, Frame* mainFrame) + : m_webViewCore(webViewCore) + , m_mainFrame(mainFrame) + , m_timer(this, &GeolocationPermissions::timerFired) + +{ + ASSERT(m_webViewCore); + maybeLoadPermanentPermissions(); + s_instances.append(this); +} + +GeolocationPermissions::~GeolocationPermissions() +{ + size_t index = s_instances.find(this); + s_instances.remove(index); +} + +void GeolocationPermissions::queryPermissionState(Frame* frame) +{ + ASSERT(s_permanentPermissionsLoaded); + + // We use SecurityOrigin::toString to key the map. Note that testing + // the SecurityOrigin pointer for equality is insufficient. + String originString = frame->document()->securityOrigin()->toString(); + + // If we've been told to always deny requests, do so. + if (s_alwaysDeny) { + makeAsynchronousCallbackToGeolocation(originString, false); + return; + } + + // See if we have a record for this origin in the permanent permissions. + // These take precedence over temporary permissions so that changes made + // from the browser settings work as intended. + PermissionsMap::const_iterator iter = s_permanentPermissions.find(originString); + PermissionsMap::const_iterator end = s_permanentPermissions.end(); + if (iter != end) { + bool allow = iter->second; + makeAsynchronousCallbackToGeolocation(originString, allow); + return; + } + + // Check the temporary permisions. + iter = m_temporaryPermissions.find(originString); + end = m_temporaryPermissions.end(); + if (iter != end) { + bool allow = iter->second; + makeAsynchronousCallbackToGeolocation(originString, allow); + return; + } + + // If there's no pending request, prompt the user. + if (nextOriginInQueue().isEmpty()) { + // Although multiple tabs may request permissions for the same origin + // simultaneously, the routing in WebViewCore/CallbackProxy ensures that + // the result of the request will make it back to this object, so + // there's no need for a globally unique ID for the request. + m_webViewCore->geolocationPermissionsShowPrompt(originString); + } + + // Add this request to the queue so we can track which frames requested it. + if (m_queuedOrigins.find(originString) == WTF::notFound) { + m_queuedOrigins.append(originString); + FrameSet frameSet; + frameSet.add(frame); + m_queuedOriginsToFramesMap.add(originString, frameSet); + } else { + ASSERT(m_queuedOriginsToFramesMap.contains(originString)); + m_queuedOriginsToFramesMap.find(originString)->second.add(frame); + } +} + +void GeolocationPermissions::cancelPermissionStateQuery(WebCore::Frame* frame) +{ + // We cancel any queued request for the given frame. There can be at most + // one of these, since each frame maps to a single origin. We only cancel + // the request if this frame is the only one reqesting permission for this + // origin. + // + // We can use the origin string to avoid searching the map. + String originString = frame->document()->securityOrigin()->toString(); + size_t index = m_queuedOrigins.find(originString); + if (index == WTF::notFound) + return; + + ASSERT(m_queuedOriginsToFramesMap.contains(originString)); + OriginToFramesMap::iterator iter = m_queuedOriginsToFramesMap.find(originString); + ASSERT(iter->second.contains(frame)); + iter->second.remove(frame); + if (!iter->second.isEmpty()) + return; + + m_queuedOrigins.remove(index); + m_queuedOriginsToFramesMap.remove(iter); + + // If this is the origin currently being shown, cancel the prompt + // and show the next in the queue, if present. + if (index == 0) { + m_webViewCore->geolocationPermissionsHidePrompt(); + if (!nextOriginInQueue().isEmpty()) + m_webViewCore->geolocationPermissionsShowPrompt(nextOriginInQueue()); + } +} + +void GeolocationPermissions::makeAsynchronousCallbackToGeolocation(String origin, bool allow) +{ + m_callbackData.origin = origin; + m_callbackData.allow = allow; + m_timer.startOneShot(0); +} + +void GeolocationPermissions::providePermissionState(String origin, bool allow, bool remember) +{ + ASSERT(s_permanentPermissionsLoaded); + + // It's possible that this method is called with an origin that doesn't + // match m_originInProgress. This can occur if this object is reset + // while a permission result is in the process of being marshalled back to + // the WebCore thread from the browser. In this case, we simply ignore the + // call. + if (origin != nextOriginInQueue()) + return; + + maybeCallbackFrames(origin, allow); + recordPermissionState(origin, allow, remember); + + // If the permissions are set to be remembered, cancel any queued requests + // for this domain in other tabs. + if (remember) + cancelPendingRequestsInOtherTabs(origin); + + // Clear the origin from the queue. + ASSERT(!m_queuedOrigins.isEmpty()); + m_queuedOrigins.remove(0); + ASSERT(m_queuedOriginsToFramesMap.contains(origin)); + m_queuedOriginsToFramesMap.remove(origin); + + // If there are other requests queued, start the next one. + if (!nextOriginInQueue().isEmpty()) + m_webViewCore->geolocationPermissionsShowPrompt(nextOriginInQueue()); +} + +void GeolocationPermissions::recordPermissionState(String origin, bool allow, bool remember) +{ + if (remember) { + s_permanentPermissions.set(origin, allow); + s_permanentPermissionsModified = true; + } else { + // It's possible that another tab recorded a permanent permission for + // this origin while our request was in progress, but we record it + // anyway. + m_temporaryPermissions.set(origin, allow); + } +} + +void GeolocationPermissions::cancelPendingRequestsInOtherTabs(String origin) +{ + for (GeolocationPermissionsVector::const_iterator iter = s_instances.begin(); + iter != s_instances.end(); + ++iter) + (*iter)->cancelPendingRequests(origin); +} + +void GeolocationPermissions::cancelPendingRequests(String origin) +{ + size_t index = m_queuedOrigins.find(origin); + + // Don't cancel the request if it's currently being shown, in which case + // it's at index 0. + if (index == WTF::notFound || !index) + return; + + // Get the permission from the permanent list. + ASSERT(s_permanentPermissions.contains(origin)); + PermissionsMap::const_iterator iter = s_permanentPermissions.find(origin); + bool allow = iter->second; + + maybeCallbackFrames(origin, allow); + + m_queuedOrigins.remove(index); + ASSERT(m_queuedOriginsToFramesMap.contains(origin)); + m_queuedOriginsToFramesMap.remove(origin); +} + +void GeolocationPermissions::timerFired(Timer<GeolocationPermissions>* timer) +{ + ASSERT_UNUSED(timer, timer == &m_timer); + maybeCallbackFrames(m_callbackData.origin, m_callbackData.allow); +} + +void GeolocationPermissions::resetTemporaryPermissionStates() +{ + ASSERT(s_permanentPermissionsLoaded); + m_queuedOrigins.clear(); + m_queuedOriginsToFramesMap.clear(); + m_temporaryPermissions.clear(); + // If any permission results are being marshalled back to this thread, this + // will render them inefective. + m_timer.stop(); + + m_webViewCore->geolocationPermissionsHidePrompt(); +} + +const WTF::String& GeolocationPermissions::nextOriginInQueue() +{ + static const String emptyString = ""; + return m_queuedOrigins.isEmpty() ? emptyString : m_queuedOrigins[0]; +} + +void GeolocationPermissions::maybeCallbackFrames(String origin, bool allow) +{ + // We can't track which frame issued the request, as frames can be deleted + // or have their contents replaced. Even uniqueChildName is not unique when + // frames are dynamically deleted and created. Instead, we simply call back + // to the Geolocation object in all frames from the correct origin. + for (Frame* frame = m_mainFrame; frame; frame = frame->tree()->traverseNext()) { + if (origin == frame->document()->securityOrigin()->toString()) { + // If the page has changed, it may no longer have a Geolocation + // object. + Geolocation* geolocation = frame->domWindow()->navigator()->optionalGeolocation(); + if (geolocation) + geolocation->setIsAllowed(allow); + } + } +} + +GeolocationPermissions::OriginSet GeolocationPermissions::getOrigins() +{ + maybeLoadPermanentPermissions(); + OriginSet origins; + PermissionsMap::const_iterator end = s_permanentPermissions.end(); + for (PermissionsMap::const_iterator iter = s_permanentPermissions.begin(); iter != end; ++iter) + origins.add(iter->first); + return origins; +} + +bool GeolocationPermissions::getAllowed(String origin) +{ + maybeLoadPermanentPermissions(); + bool allowed = false; + PermissionsMap::const_iterator iter = s_permanentPermissions.find(origin); + PermissionsMap::const_iterator end = s_permanentPermissions.end(); + if (iter != end) + allowed = iter->second; + return allowed; +} + +void GeolocationPermissions::clear(String origin) +{ + maybeLoadPermanentPermissions(); + PermissionsMap::iterator iter = s_permanentPermissions.find(origin); + if (iter != s_permanentPermissions.end()) { + s_permanentPermissions.remove(iter); + s_permanentPermissionsModified = true; + } +} + +void GeolocationPermissions::allow(String origin) +{ + maybeLoadPermanentPermissions(); + // We replace any existing permanent permission. + s_permanentPermissions.set(origin, true); + s_permanentPermissionsModified = true; +} + +void GeolocationPermissions::clearAll() +{ + maybeLoadPermanentPermissions(); + s_permanentPermissions.clear(); + s_permanentPermissionsModified = true; +} + +void GeolocationPermissions::maybeLoadPermanentPermissions() +{ + if (s_permanentPermissionsLoaded) + return; + s_permanentPermissionsLoaded = true; + + SQLiteDatabase database; + if (!openDatabase(&database)) + return; + + // Create the table here, such that even if we've just created the DB, the + // commands below should succeed. + if (!database.executeCommand("CREATE TABLE IF NOT EXISTS Permissions (origin TEXT UNIQUE NOT NULL, allow INTEGER NOT NULL)")) { + database.close(); + return; + } + + SQLiteStatement statement(database, "SELECT * FROM Permissions"); + if (statement.prepare() != SQLResultOk) { + database.close(); + return; + } + + ASSERT(s_permanentPermissions.size() == 0); + while (statement.step() == SQLResultRow) + s_permanentPermissions.set(statement.getColumnText(0), statement.getColumnInt64(1)); + + database.close(); +} + +void GeolocationPermissions::maybeStorePermanentPermissions() +{ + // If the permanent permissions haven't been modified, there's no need to + // save them to the DB. (If we haven't even loaded them, writing them now + // would overwrite the stored permissions with the empty set.) + if (!s_permanentPermissionsModified) + return; + + SQLiteDatabase database; + if (!openDatabase(&database)) + return; + + SQLiteTransaction transaction(database); + + // The number of entries should be small enough that it's not worth trying + // to perform a diff. Simply clear the table and repopulate it. + if (!database.executeCommand("DELETE FROM Permissions")) { + database.close(); + return; + } + + PermissionsMap::const_iterator end = s_permanentPermissions.end(); + for (PermissionsMap::const_iterator iter = s_permanentPermissions.begin(); iter != end; ++iter) { + SQLiteStatement statement(database, "INSERT INTO Permissions (origin, allow) VALUES (?, ?)"); + if (statement.prepare() != SQLResultOk) + continue; + statement.bindText(1, iter->first); + statement.bindInt64(2, iter->second); + statement.executeCommand(); + } + + transaction.commit(); + database.close(); + + s_permanentPermissionsModified = false; +} + +void GeolocationPermissions::setDatabasePath(String path) +{ + // Take the first non-empty value. + if (s_databasePath.length() > 0) + return; + s_databasePath = path; +} + +bool GeolocationPermissions::openDatabase(SQLiteDatabase* database) +{ + ASSERT(database); + String filename = SQLiteFileSystem::appendDatabaseFileNameToPath(s_databasePath, databaseName); + if (!database->open(filename)) + return false; + if (chmod(filename.utf8().data(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) { + database->close(); + return false; + } + return true; +} + +void GeolocationPermissions::setAlwaysDeny(bool deny) +{ + s_alwaysDeny = deny; +} + +} // namespace android |