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 "core/storage/StorageArea.h" 29 30 #include "bindings/core/v8/ExceptionState.h" 31 #include "core/dom/Document.h" 32 #include "core/dom/ExceptionCode.h" 33 #include "core/frame/LocalDOMWindow.h" 34 #include "core/frame/LocalFrame.h" 35 #include "core/inspector/InspectorInstrumentation.h" 36 #include "core/page/Page.h" 37 #include "core/page/StorageClient.h" 38 #include "core/storage/Storage.h" 39 #include "core/storage/StorageEvent.h" 40 #include "core/storage/StorageNamespace.h" 41 #include "platform/weborigin/SecurityOrigin.h" 42 #include "public/platform/WebStorageArea.h" 43 #include "public/platform/WebString.h" 44 #include "public/platform/WebURL.h" 45 46 namespace blink { 47 48 PassOwnPtrWillBeRawPtr<StorageArea> StorageArea::create(PassOwnPtr<WebStorageArea> storageArea, StorageType storageType) 49 { 50 return adoptPtrWillBeNoop(new StorageArea(storageArea, storageType)); 51 } 52 53 StorageArea::StorageArea(PassOwnPtr<WebStorageArea> storageArea, StorageType storageType) 54 : FrameDestructionObserver(nullptr) 55 , m_storageArea(storageArea) 56 , m_storageType(storageType) 57 , m_canAccessStorageCachedResult(false) 58 { 59 } 60 61 StorageArea::~StorageArea() 62 { 63 } 64 65 void StorageArea::trace(Visitor* visitor) 66 { 67 FrameDestructionObserver::trace(visitor); 68 } 69 70 unsigned StorageArea::length(ExceptionState& exceptionState, LocalFrame* frame) 71 { 72 if (!canAccessStorage(frame)) { 73 exceptionState.throwSecurityError("access is denied for this document."); 74 return 0; 75 } 76 return m_storageArea->length(); 77 } 78 79 String StorageArea::key(unsigned index, ExceptionState& exceptionState, LocalFrame* frame) 80 { 81 if (!canAccessStorage(frame)) { 82 exceptionState.throwSecurityError("access is denied for this document."); 83 return String(); 84 } 85 return m_storageArea->key(index); 86 } 87 88 String StorageArea::getItem(const String& key, ExceptionState& exceptionState, LocalFrame* frame) 89 { 90 if (!canAccessStorage(frame)) { 91 exceptionState.throwSecurityError("access is denied for this document."); 92 return String(); 93 } 94 return m_storageArea->getItem(key); 95 } 96 97 void StorageArea::setItem(const String& key, const String& value, ExceptionState& exceptionState, LocalFrame* frame) 98 { 99 if (!canAccessStorage(frame)) { 100 exceptionState.throwSecurityError("access is denied for this document."); 101 return; 102 } 103 WebStorageArea::Result result = WebStorageArea::ResultOK; 104 m_storageArea->setItem(key, value, frame->document()->url(), result); 105 if (result != WebStorageArea::ResultOK) 106 exceptionState.throwDOMException(QuotaExceededError, "Setting the value of '" + key + "' exceeded the quota."); 107 } 108 109 void StorageArea::removeItem(const String& key, ExceptionState& exceptionState, LocalFrame* frame) 110 { 111 if (!canAccessStorage(frame)) { 112 exceptionState.throwSecurityError("access is denied for this document."); 113 return; 114 } 115 m_storageArea->removeItem(key, frame->document()->url()); 116 } 117 118 void StorageArea::clear(ExceptionState& exceptionState, LocalFrame* frame) 119 { 120 if (!canAccessStorage(frame)) { 121 exceptionState.throwSecurityError("access is denied for this document."); 122 return; 123 } 124 m_storageArea->clear(frame->document()->url()); 125 } 126 127 bool StorageArea::contains(const String& key, ExceptionState& exceptionState, LocalFrame* frame) 128 { 129 if (!canAccessStorage(frame)) { 130 exceptionState.throwSecurityError("access is denied for this document."); 131 return false; 132 } 133 return !getItem(key, exceptionState, frame).isNull(); 134 } 135 136 bool StorageArea::canAccessStorage(LocalFrame* frame) 137 { 138 if (!frame || !frame->page()) 139 return false; 140 141 // FrameDestructionObserver is used to safely keep the cached 142 // reference to the LocalFrame. Should the LocalFrame die before 143 // this StorageArea does, that cached reference will be cleared. 144 if (m_frame == frame) 145 return m_canAccessStorageCachedResult; 146 bool result = frame->page()->storageClient().canAccessStorage(frame, m_storageType); 147 // Move attention to the new LocalFrame. 148 observeFrame(frame); 149 m_canAccessStorageCachedResult = result; 150 return result; 151 } 152 153 size_t StorageArea::memoryBytesUsedByCache() 154 { 155 return m_storageArea->memoryBytesUsedByCache(); 156 } 157 158 void StorageArea::dispatchLocalStorageEvent(const String& key, const String& oldValue, const String& newValue, SecurityOrigin* securityOrigin, const KURL& pageURL, WebStorageArea* sourceAreaInstance, bool originatedInProcess) 159 { 160 // FIXME: This looks suspicious. Why doesn't this use allPages instead? 161 const HashSet<Page*>& pages = Page::ordinaryPages(); 162 for (HashSet<Page*>::const_iterator it = pages.begin(); it != pages.end(); ++it) { 163 for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree().traverseNext()) { 164 // FIXME: We do not yet have a way to dispatch events to out-of-process frames. 165 if (!frame->isLocalFrame()) 166 continue; 167 Storage* storage = frame->domWindow()->optionalLocalStorage(); 168 if (storage && toLocalFrame(frame)->document()->securityOrigin()->canAccess(securityOrigin) && !isEventSource(storage, sourceAreaInstance)) 169 frame->domWindow()->enqueueWindowEvent(StorageEvent::create(EventTypeNames::storage, key, oldValue, newValue, pageURL, storage)); 170 } 171 InspectorInstrumentation::didDispatchDOMStorageEvent(*it, key, oldValue, newValue, LocalStorage, securityOrigin); 172 } 173 } 174 175 static Page* findPageWithSessionStorageNamespace(const WebStorageNamespace& sessionNamespace) 176 { 177 // FIXME: This looks suspicious. Why doesn't this use allPages instead? 178 const HashSet<Page*>& pages = Page::ordinaryPages(); 179 for (HashSet<Page*>::const_iterator it = pages.begin(); it != pages.end(); ++it) { 180 const bool dontCreateIfMissing = false; 181 StorageNamespace* storageNamespace = (*it)->sessionStorage(dontCreateIfMissing); 182 if (storageNamespace && storageNamespace->isSameNamespace(sessionNamespace)) 183 return *it; 184 } 185 return 0; 186 } 187 188 void StorageArea::dispatchSessionStorageEvent(const String& key, const String& oldValue, const String& newValue, SecurityOrigin* securityOrigin, const KURL& pageURL, const WebStorageNamespace& sessionNamespace, WebStorageArea* sourceAreaInstance, bool originatedInProcess) 189 { 190 Page* page = findPageWithSessionStorageNamespace(sessionNamespace); 191 if (!page) 192 return; 193 194 for (Frame* frame = page->mainFrame(); frame; frame = frame->tree().traverseNext()) { 195 // FIXME: We do not yet have a way to dispatch events to out-of-process frames. 196 if (!frame->isLocalFrame()) 197 continue; 198 Storage* storage = frame->domWindow()->optionalSessionStorage(); 199 if (storage && toLocalFrame(frame)->document()->securityOrigin()->canAccess(securityOrigin) && !isEventSource(storage, sourceAreaInstance)) 200 frame->domWindow()->enqueueWindowEvent(StorageEvent::create(EventTypeNames::storage, key, oldValue, newValue, pageURL, storage)); 201 } 202 InspectorInstrumentation::didDispatchDOMStorageEvent(page, key, oldValue, newValue, SessionStorage, securityOrigin); 203 } 204 205 bool StorageArea::isEventSource(Storage* storage, WebStorageArea* sourceAreaInstance) 206 { 207 ASSERT(storage); 208 StorageArea* area = storage->area(); 209 return area->m_storageArea == sourceAreaInstance; 210 } 211 212 } // namespace blink 213