1/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2013 Samsung Electronics. All rights reserved.
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 *
9 * 1.  Redistributions of source code must retain the above copyright
10 *     notice, this list of conditions and the following disclaimer.
11 * 2.  Redistributions in binary form must reproduce the above copyright
12 *     notice, this list of conditions and the following disclaimer in the
13 *     documentation and/or other materials provided with the distribution.
14 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 *     its contributors may be used to endorse or promote products derived
16 *     from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include "config.h"
31#include "core/inspector/InspectorDOMStorageAgent.h"
32
33#include "InspectorFrontend.h"
34#include "bindings/v8/ExceptionState.h"
35#include "core/dom/DOMException.h"
36#include "core/dom/Document.h"
37#include "core/dom/ExceptionCode.h"
38#include "core/inspector/InspectorPageAgent.h"
39#include "core/inspector/InspectorState.h"
40#include "core/inspector/InstrumentingAgents.h"
41#include "core/page/DOMWindow.h"
42#include "core/page/Frame.h"
43#include "core/page/Page.h"
44#include "core/page/PageGroup.h"
45#include "core/platform/JSONValues.h"
46#include "core/storage/Storage.h"
47#include "core/storage/StorageArea.h"
48#include "core/storage/StorageNamespace.h"
49#include "weborigin/SecurityOrigin.h"
50
51namespace WebCore {
52
53namespace DOMStorageAgentState {
54static const char domStorageAgentEnabled[] = "domStorageAgentEnabled";
55};
56
57static bool hadException(ExceptionState& es, ErrorString* errorString)
58{
59    if (!es.hadException())
60        return false;
61
62    switch (es.code()) {
63    case SecurityError:
64        *errorString = "Security error";
65        return true;
66    default:
67        *errorString = "Unknown DOM storage error";
68        return true;
69    }
70}
71
72InspectorDOMStorageAgent::InspectorDOMStorageAgent(InstrumentingAgents* instrumentingAgents, InspectorPageAgent* pageAgent, InspectorCompositeState* state)
73    : InspectorBaseAgent<InspectorDOMStorageAgent>("DOMStorage", instrumentingAgents, state)
74    , m_pageAgent(pageAgent)
75    , m_frontend(0)
76{
77}
78
79InspectorDOMStorageAgent::~InspectorDOMStorageAgent()
80{
81}
82
83void InspectorDOMStorageAgent::setFrontend(InspectorFrontend* frontend)
84{
85    m_frontend = frontend;
86}
87
88void InspectorDOMStorageAgent::clearFrontend()
89{
90    m_frontend = 0;
91    disable(0);
92}
93
94bool InspectorDOMStorageAgent::isEnabled() const
95{
96    return m_state->getBoolean(DOMStorageAgentState::domStorageAgentEnabled);
97}
98
99void InspectorDOMStorageAgent::enable(ErrorString*)
100{
101    m_state->setBoolean(DOMStorageAgentState::domStorageAgentEnabled, true);
102    m_instrumentingAgents->setInspectorDOMStorageAgent(this);
103}
104
105void InspectorDOMStorageAgent::disable(ErrorString*)
106{
107    m_instrumentingAgents->setInspectorDOMStorageAgent(0);
108    m_state->setBoolean(DOMStorageAgentState::domStorageAgentEnabled, false);
109}
110
111void InspectorDOMStorageAgent::getValue(ErrorString* errorString, const RefPtr<JSONObject>& storageId, const String& key, TypeBuilder::OptOutput<WTF::String>* value)
112{
113    Frame* frame;
114    OwnPtr<StorageArea> storageArea = findStorageArea(errorString, storageId, frame);
115    if (!storageArea)
116        return;
117
118    TrackExceptionState es;
119    bool keyPresent = storageArea->contains(key, es, frame);
120    if (hadException(es, errorString) || !keyPresent)
121        return;
122
123    *value = storageArea->getItem(key, es, frame);
124    hadException(es, errorString);
125}
126
127void InspectorDOMStorageAgent::getDOMStorageItems(ErrorString* errorString, const RefPtr<JSONObject>& storageId, RefPtr<TypeBuilder::Array<TypeBuilder::Array<String> > >& items)
128{
129    Frame* frame;
130    OwnPtr<StorageArea> storageArea = findStorageArea(errorString, storageId, frame);
131    if (!storageArea)
132        return;
133
134    RefPtr<TypeBuilder::Array<TypeBuilder::Array<String> > > storageItems = TypeBuilder::Array<TypeBuilder::Array<String> >::create();
135
136    TrackExceptionState es;
137    for (unsigned i = 0; i < storageArea->length(es, frame); ++i) {
138        String name(storageArea->key(i, es, frame));
139        if (hadException(es, errorString))
140            return;
141        String value(storageArea->getItem(name, es, frame));
142        if (hadException(es, errorString))
143            return;
144        RefPtr<TypeBuilder::Array<String> > entry = TypeBuilder::Array<String>::create();
145        entry->addItem(name);
146        entry->addItem(value);
147        storageItems->addItem(entry);
148    }
149    items = storageItems.release();
150}
151
152static String toErrorString(ExceptionState& es)
153{
154    if (es.hadException())
155        return DOMException::getErrorName(es.code());
156    return "";
157}
158
159void InspectorDOMStorageAgent::setDOMStorageItem(ErrorString* errorString, const RefPtr<JSONObject>& storageId, const String& key, const String& value)
160{
161    Frame* frame;
162    OwnPtr<StorageArea> storageArea = findStorageArea(0, storageId, frame);
163    if (!storageArea) {
164        *errorString = "Storage not found";
165        return;
166    }
167
168    TrackExceptionState es;
169    storageArea->setItem(key, value, es, frame);
170    *errorString = toErrorString(es);
171}
172
173void InspectorDOMStorageAgent::removeDOMStorageItem(ErrorString* errorString, const RefPtr<JSONObject>& storageId, const String& key)
174{
175    Frame* frame;
176    OwnPtr<StorageArea> storageArea = findStorageArea(0, storageId, frame);
177    if (!storageArea) {
178        *errorString = "Storage not found";
179        return;
180    }
181
182    TrackExceptionState es;
183    storageArea->removeItem(key, es, frame);
184    *errorString = toErrorString(es);
185}
186
187String InspectorDOMStorageAgent::storageId(Storage* storage)
188{
189    ASSERT(storage);
190    Document* document = storage->frame()->document();
191    ASSERT(document);
192    DOMWindow* window = document->domWindow();
193    ASSERT(window);
194    RefPtr<SecurityOrigin> securityOrigin = document->securityOrigin();
195    bool isLocalStorage = window->optionalLocalStorage() == storage;
196    return storageId(securityOrigin.get(), isLocalStorage)->toJSONString();
197}
198
199PassRefPtr<TypeBuilder::DOMStorage::StorageId> InspectorDOMStorageAgent::storageId(SecurityOrigin* securityOrigin, bool isLocalStorage)
200{
201    return TypeBuilder::DOMStorage::StorageId::create()
202        .setSecurityOrigin(securityOrigin->toRawString())
203        .setIsLocalStorage(isLocalStorage).release();
204}
205
206void InspectorDOMStorageAgent::didDispatchDOMStorageEvent(const String& key, const String& oldValue, const String& newValue, StorageType storageType, SecurityOrigin* securityOrigin)
207{
208    if (!m_frontend || !isEnabled())
209        return;
210
211    RefPtr<TypeBuilder::DOMStorage::StorageId> id = storageId(securityOrigin, storageType == LocalStorage);
212
213    if (key.isNull())
214        m_frontend->domstorage()->domStorageItemsCleared(id);
215    else if (newValue.isNull())
216        m_frontend->domstorage()->domStorageItemRemoved(id, key);
217    else if (oldValue.isNull())
218        m_frontend->domstorage()->domStorageItemAdded(id, key, newValue);
219    else
220        m_frontend->domstorage()->domStorageItemUpdated(id, key, oldValue, newValue);
221}
222
223PassOwnPtr<StorageArea> InspectorDOMStorageAgent::findStorageArea(ErrorString* errorString, const RefPtr<JSONObject>& storageId, Frame*& targetFrame)
224{
225    String securityOrigin;
226    bool isLocalStorage = false;
227    bool success = storageId->getString("securityOrigin", &securityOrigin);
228    if (success)
229        success = storageId->getBoolean("isLocalStorage", &isLocalStorage);
230    if (!success) {
231        if (errorString)
232            *errorString = "Invalid storageId format";
233        return nullptr;
234    }
235
236    Frame* frame = m_pageAgent->findFrameWithSecurityOrigin(securityOrigin);
237    if (!frame) {
238        if (errorString)
239            *errorString = "Frame not found for the given security origin";
240        return nullptr;
241    }
242    targetFrame = frame;
243
244    if (isLocalStorage)
245        return StorageNamespace::localStorageArea(frame->document()->securityOrigin());
246    return m_pageAgent->page()->sessionStorage()->storageArea(frame->document()->securityOrigin());
247}
248
249} // namespace WebCore
250
251