1/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2013 Samsung Electronics. All rights reserved.
4 * Copyright (C) 2015 Apple Inc. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
16 * its contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "InspectorDOMStorageAgent.h"
33
34#include "DOMException.h"
35#include "DOMWindow.h"
36#include "Database.h"
37#include "Document.h"
38#include "Frame.h"
39#include "InspectorPageAgent.h"
40#include "InstrumentingAgents.h"
41#include "Page.h"
42#include "SecurityOrigin.h"
43#include "SecurityOriginData.h"
44#include "Storage.h"
45#include "StorageNamespace.h"
46#include "StorageNamespaceProvider.h"
47#include "StorageType.h"
48#include "VoidCallback.h"
49#include <JavaScriptCore/InspectorFrontendDispatchers.h>
50#include <wtf/JSONValues.h>
51
52
53namespace WebCore {
54
55using namespace Inspector;
56
57InspectorDOMStorageAgent::InspectorDOMStorageAgent(PageAgentContext& context)
58 : InspectorAgentBase("DOMStorage"_s, context)
59 , m_frontendDispatcher(std::make_unique<Inspector::DOMStorageFrontendDispatcher>(context.frontendRouter))
60 , m_backendDispatcher(Inspector::DOMStorageBackendDispatcher::create(context.backendDispatcher, this))
61 , m_inspectedPage(context.inspectedPage)
62{
63}
64
65void InspectorDOMStorageAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
66{
67}
68
69void InspectorDOMStorageAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
70{
71 ErrorString unused;
72 disable(unused);
73}
74
75void InspectorDOMStorageAgent::enable(ErrorString&)
76{
77 m_instrumentingAgents.setInspectorDOMStorageAgent(this);
78}
79
80void InspectorDOMStorageAgent::disable(ErrorString&)
81{
82 m_instrumentingAgents.setInspectorDOMStorageAgent(nullptr);
83}
84
85void InspectorDOMStorageAgent::getDOMStorageItems(ErrorString& errorString, const JSON::Object& storageId, RefPtr<JSON::ArrayOf<JSON::ArrayOf<String>>>& items)
86{
87 Frame* frame;
88 RefPtr<StorageArea> storageArea = findStorageArea(errorString, storageId, frame);
89 if (!storageArea) {
90 errorString = "No StorageArea for given storageId"_s;
91 return;
92 }
93
94 auto storageItems = JSON::ArrayOf<JSON::ArrayOf<String>>::create();
95
96 for (unsigned i = 0; i < storageArea->length(); ++i) {
97 String key = storageArea->key(i);
98 String value = storageArea->item(key);
99
100 auto entry = JSON::ArrayOf<String>::create();
101 entry->addItem(key);
102 entry->addItem(value);
103 storageItems->addItem(WTFMove(entry));
104 }
105
106 items = WTFMove(storageItems);
107}
108
109void InspectorDOMStorageAgent::setDOMStorageItem(ErrorString& errorString, const JSON::Object& storageId, const String& key, const String& value)
110{
111 Frame* frame;
112 RefPtr<StorageArea> storageArea = findStorageArea(errorString, storageId, frame);
113 if (!storageArea) {
114 errorString = "Storage not found"_s;
115 return;
116 }
117
118 bool quotaException = false;
119 storageArea->setItem(frame, key, value, quotaException);
120 if (quotaException)
121 errorString = DOMException::name(QuotaExceededError);
122}
123
124void InspectorDOMStorageAgent::removeDOMStorageItem(ErrorString& errorString, const JSON::Object& storageId, const String& key)
125{
126 Frame* frame;
127 RefPtr<StorageArea> storageArea = findStorageArea(errorString, storageId, frame);
128 if (!storageArea) {
129 errorString = "Storage not found"_s;
130 return;
131 }
132
133 storageArea->removeItem(frame, key);
134}
135
136String InspectorDOMStorageAgent::storageId(Storage& storage)
137{
138 Document* document = storage.frame()->document();
139 ASSERT(document);
140 DOMWindow* window = document->domWindow();
141 ASSERT(window);
142 Ref<SecurityOrigin> securityOrigin = document->securityOrigin();
143 bool isLocalStorage = window->optionalLocalStorage() == &storage;
144 return InspectorDOMStorageAgent::storageId(securityOrigin.ptr(), isLocalStorage)->toJSONString();
145}
146
147RefPtr<Inspector::Protocol::DOMStorage::StorageId> InspectorDOMStorageAgent::storageId(SecurityOrigin* securityOrigin, bool isLocalStorage)
148{
149 return Inspector::Protocol::DOMStorage::StorageId::create()
150 .setSecurityOrigin(securityOrigin->toRawString())
151 .setIsLocalStorage(isLocalStorage)
152 .release();
153}
154
155void InspectorDOMStorageAgent::didDispatchDOMStorageEvent(const String& key, const String& oldValue, const String& newValue, StorageType storageType, SecurityOrigin* securityOrigin)
156{
157 RefPtr<Inspector::Protocol::DOMStorage::StorageId> id = InspectorDOMStorageAgent::storageId(securityOrigin, storageType == StorageType::Local);
158
159 if (key.isNull())
160 m_frontendDispatcher->domStorageItemsCleared(id);
161 else if (newValue.isNull())
162 m_frontendDispatcher->domStorageItemRemoved(id, key);
163 else if (oldValue.isNull())
164 m_frontendDispatcher->domStorageItemAdded(id, key, newValue);
165 else
166 m_frontendDispatcher->domStorageItemUpdated(id, key, oldValue, newValue);
167}
168
169RefPtr<StorageArea> InspectorDOMStorageAgent::findStorageArea(ErrorString& errorString, const JSON::Object& storageId, Frame*& targetFrame)
170{
171 String securityOrigin;
172 bool isLocalStorage = false;
173 bool success = storageId.getString("securityOrigin"_s, securityOrigin);
174 if (success)
175 success = storageId.getBoolean("isLocalStorage"_s, isLocalStorage);
176 if (!success) {
177 errorString = "Invalid storageId format"_s;
178 targetFrame = nullptr;
179 return nullptr;
180 }
181
182 targetFrame = InspectorPageAgent::findFrameWithSecurityOrigin(m_inspectedPage, securityOrigin);
183 if (!targetFrame) {
184 errorString = "Frame not found for the given security origin"_s;
185 return nullptr;
186 }
187
188 if (!isLocalStorage)
189 return m_inspectedPage.sessionStorage()->storageArea(targetFrame->document()->securityOrigin().data());
190 return m_inspectedPage.storageNamespaceProvider().localStorageArea(*targetFrame->document());
191}
192
193} // namespace WebCore
194