1 /* 2 * Copyright (C) 2009 Google Inc. All Rights Reserved. 3 * (C) 2008 Apple Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include "config.h" 28 #include "StorageAreaProxy.h" 29 30 #include "StorageNamespaceProxy.h" 31 #include "bindings/v8/ExceptionState.h" 32 #include "core/dom/Document.h" 33 #include "core/dom/ExceptionCode.h" 34 #include "core/events/ThreadLocalEventNames.h" 35 #include "core/inspector/InspectorInstrumentation.h" 36 #include "core/frame/DOMWindow.h" 37 #include "core/frame/Frame.h" 38 #include "core/page/Page.h" 39 #include "core/page/PageGroup.h" 40 #include "core/storage/Storage.h" 41 #include "core/storage/StorageEvent.h" 42 #include "platform/weborigin/SecurityOrigin.h" 43 44 #include "WebFrameImpl.h" 45 #include "WebPermissionClient.h" 46 #include "WebViewImpl.h" 47 #include "public/platform/WebStorageArea.h" 48 #include "public/platform/WebString.h" 49 #include "public/platform/WebURL.h" 50 51 namespace WebCore { 52 53 StorageAreaProxy::StorageAreaProxy(PassOwnPtr<blink::WebStorageArea> storageArea, StorageType storageType) 54 : m_storageArea(storageArea) 55 , m_storageType(storageType) 56 , m_canAccessStorageCachedResult(false) 57 , m_canAccessStorageCachedFrame(0) 58 { 59 } 60 61 StorageAreaProxy::~StorageAreaProxy() 62 { 63 } 64 65 unsigned StorageAreaProxy::length(ExceptionState& exceptionState, Frame* frame) 66 { 67 if (!canAccessStorage(frame)) { 68 exceptionState.throwSecurityError("access is denied for this document."); 69 return 0; 70 } 71 return m_storageArea->length(); 72 } 73 74 String StorageAreaProxy::key(unsigned index, ExceptionState& exceptionState, Frame* frame) 75 { 76 if (!canAccessStorage(frame)) { 77 exceptionState.throwSecurityError("access is denied for this document."); 78 return String(); 79 } 80 return m_storageArea->key(index); 81 } 82 83 String StorageAreaProxy::getItem(const String& key, ExceptionState& exceptionState, Frame* frame) 84 { 85 if (!canAccessStorage(frame)) { 86 exceptionState.throwSecurityError("access is denied for this document."); 87 return String(); 88 } 89 return m_storageArea->getItem(key); 90 } 91 92 void StorageAreaProxy::setItem(const String& key, const String& value, ExceptionState& exceptionState, Frame* frame) 93 { 94 if (!canAccessStorage(frame)) { 95 exceptionState.throwSecurityError("access is denied for this document."); 96 return; 97 } 98 blink::WebStorageArea::Result result = blink::WebStorageArea::ResultOK; 99 m_storageArea->setItem(key, value, frame->document()->url(), result); 100 if (result != blink::WebStorageArea::ResultOK) 101 exceptionState.throwDOMException(QuotaExceededError, "Setting the value of '" + key + "' exceeded the quota."); 102 } 103 104 void StorageAreaProxy::removeItem(const String& key, ExceptionState& exceptionState, Frame* frame) 105 { 106 if (!canAccessStorage(frame)) { 107 exceptionState.throwSecurityError("access is denied for this document."); 108 return; 109 } 110 m_storageArea->removeItem(key, frame->document()->url()); 111 } 112 113 void StorageAreaProxy::clear(ExceptionState& exceptionState, Frame* frame) 114 { 115 if (!canAccessStorage(frame)) { 116 exceptionState.throwSecurityError("access is denied for this document."); 117 return; 118 } 119 m_storageArea->clear(frame->document()->url()); 120 } 121 122 bool StorageAreaProxy::contains(const String& key, ExceptionState& exceptionState, Frame* frame) 123 { 124 if (!canAccessStorage(frame)) { 125 exceptionState.throwSecurityError("access is denied for this document."); 126 return false; 127 } 128 return !getItem(key, exceptionState, frame).isNull(); 129 } 130 131 bool StorageAreaProxy::canAccessStorage(Frame* frame) 132 { 133 if (!frame || !frame->page()) 134 return false; 135 if (m_canAccessStorageCachedFrame == frame) 136 return m_canAccessStorageCachedResult; 137 blink::WebFrameImpl* webFrame = blink::WebFrameImpl::fromFrame(frame); 138 bool result; 139 if (webFrame->permissionClient()) { 140 result = webFrame->permissionClient()->allowStorage(webFrame, m_storageType == LocalStorage); 141 } else { 142 blink::WebViewImpl* webView = webFrame->viewImpl(); 143 result = !webView->permissionClient() || webView->permissionClient()->allowStorage(webFrame, m_storageType == LocalStorage); 144 } 145 146 m_canAccessStorageCachedFrame = frame; 147 m_canAccessStorageCachedResult = result; 148 return result; 149 } 150 151 size_t StorageAreaProxy::memoryBytesUsedByCache() 152 { 153 return m_storageArea->memoryBytesUsedByCache(); 154 } 155 156 void StorageAreaProxy::dispatchLocalStorageEvent(const String& key, const String& oldValue, const String& newValue, 157 SecurityOrigin* securityOrigin, const KURL& pageURL, blink::WebStorageArea* sourceAreaInstance, bool originatedInProcess) 158 { 159 // FIXME: This looks suspicious. Why doesn't this use allPages instead? 160 const HashSet<Page*>& pages = PageGroup::sharedGroup()->pages(); 161 for (HashSet<Page*>::const_iterator it = pages.begin(); it != pages.end(); ++it) { 162 for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree().traverseNext()) { 163 Storage* storage = frame->domWindow()->optionalLocalStorage(); 164 if (storage && frame->document()->securityOrigin()->equal(securityOrigin) && !isEventSource(storage, sourceAreaInstance)) 165 frame->domWindow()->enqueueWindowEvent(StorageEvent::create(EventTypeNames::storage, key, oldValue, newValue, pageURL, storage)); 166 } 167 InspectorInstrumentation::didDispatchDOMStorageEvent(*it, key, oldValue, newValue, LocalStorage, securityOrigin); 168 } 169 } 170 171 static Page* findPageWithSessionStorageNamespace(const blink::WebStorageNamespace& sessionNamespace) 172 { 173 // FIXME: This looks suspicious. Why doesn't this use allPages instead? 174 const HashSet<Page*>& pages = PageGroup::sharedGroup()->pages(); 175 for (HashSet<Page*>::const_iterator it = pages.begin(); it != pages.end(); ++it) { 176 const bool dontCreateIfMissing = false; 177 StorageNamespaceProxy* proxy = static_cast<StorageNamespaceProxy*>((*it)->sessionStorage(dontCreateIfMissing)); 178 if (proxy && proxy->isSameNamespace(sessionNamespace)) 179 return *it; 180 } 181 return 0; 182 } 183 184 void StorageAreaProxy::dispatchSessionStorageEvent(const String& key, const String& oldValue, const String& newValue, 185 SecurityOrigin* securityOrigin, const KURL& pageURL, const blink::WebStorageNamespace& sessionNamespace, 186 blink::WebStorageArea* sourceAreaInstance, bool originatedInProcess) 187 { 188 Page* page = findPageWithSessionStorageNamespace(sessionNamespace); 189 if (!page) 190 return; 191 192 for (Frame* frame = page->mainFrame(); frame; frame = frame->tree().traverseNext()) { 193 Storage* storage = frame->domWindow()->optionalSessionStorage(); 194 if (storage && frame->document()->securityOrigin()->equal(securityOrigin) && !isEventSource(storage, sourceAreaInstance)) 195 frame->domWindow()->enqueueWindowEvent(StorageEvent::create(EventTypeNames::storage, key, oldValue, newValue, pageURL, storage)); 196 } 197 InspectorInstrumentation::didDispatchDOMStorageEvent(page, key, oldValue, newValue, SessionStorage, securityOrigin); 198 } 199 200 bool StorageAreaProxy::isEventSource(Storage* storage, blink::WebStorageArea* sourceAreaInstance) 201 { 202 ASSERT(storage); 203 StorageAreaProxy* areaProxy = static_cast<StorageAreaProxy*>(storage->area()); 204 return areaProxy->m_storageArea == sourceAreaInstance; 205 } 206 207 } // namespace WebCore 208