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 "bindings/core/v8/ExceptionState.h"
34#include "core/InspectorFrontend.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/frame/LocalDOMWindow.h"
42#include "core/frame/LocalFrame.h"
43#include "core/page/Page.h"
44#include "core/storage/Storage.h"
45#include "core/storage/StorageNamespace.h"
46#include "platform/JSONValues.h"
47#include "platform/weborigin/SecurityOrigin.h"
48
49namespace blink {
50
51namespace DOMStorageAgentState {
52static const char domStorageAgentEnabled[] = "domStorageAgentEnabled";
53};
54
55static bool hadException(ExceptionState& exceptionState, ErrorString* errorString)
56{
57    if (!exceptionState.hadException())
58        return false;
59
60    switch (exceptionState.code()) {
61    case SecurityError:
62        *errorString = "Security error";
63        return true;
64    default:
65        *errorString = "Unknown DOM storage error";
66        return true;
67    }
68}
69
70InspectorDOMStorageAgent::InspectorDOMStorageAgent(InspectorPageAgent* pageAgent)
71    : InspectorBaseAgent<InspectorDOMStorageAgent>("DOMStorage")
72    , m_pageAgent(pageAgent)
73    , m_frontend(0)
74{
75}
76
77InspectorDOMStorageAgent::~InspectorDOMStorageAgent()
78{
79}
80
81void InspectorDOMStorageAgent::trace(Visitor* visitor)
82{
83    visitor->trace(m_pageAgent);
84    InspectorBaseAgent::trace(visitor);
85}
86
87void InspectorDOMStorageAgent::setFrontend(InspectorFrontend* frontend)
88{
89    m_frontend = frontend->domstorage();
90}
91
92void InspectorDOMStorageAgent::clearFrontend()
93{
94    m_frontend = 0;
95    disable(0);
96}
97
98void InspectorDOMStorageAgent::restore()
99{
100    if (isEnabled())
101        enable(0);
102}
103
104bool InspectorDOMStorageAgent::isEnabled() const
105{
106    return m_state->getBoolean(DOMStorageAgentState::domStorageAgentEnabled);
107}
108
109void InspectorDOMStorageAgent::enable(ErrorString*)
110{
111    m_state->setBoolean(DOMStorageAgentState::domStorageAgentEnabled, true);
112    m_instrumentingAgents->setInspectorDOMStorageAgent(this);
113}
114
115void InspectorDOMStorageAgent::disable(ErrorString*)
116{
117    m_instrumentingAgents->setInspectorDOMStorageAgent(0);
118    m_state->setBoolean(DOMStorageAgentState::domStorageAgentEnabled, false);
119}
120
121void InspectorDOMStorageAgent::getDOMStorageItems(ErrorString* errorString, const RefPtr<JSONObject>& storageId, RefPtr<TypeBuilder::Array<TypeBuilder::Array<String> > >& items)
122{
123    LocalFrame* frame;
124    OwnPtrWillBeRawPtr<StorageArea> storageArea = findStorageArea(errorString, storageId, frame);
125    if (!storageArea)
126        return;
127
128    RefPtr<TypeBuilder::Array<TypeBuilder::Array<String> > > storageItems = TypeBuilder::Array<TypeBuilder::Array<String> >::create();
129
130    TrackExceptionState exceptionState;
131    for (unsigned i = 0; i < storageArea->length(exceptionState, frame); ++i) {
132        String name(storageArea->key(i, exceptionState, frame));
133        if (hadException(exceptionState, errorString))
134            return;
135        String value(storageArea->getItem(name, exceptionState, frame));
136        if (hadException(exceptionState, errorString))
137            return;
138        RefPtr<TypeBuilder::Array<String> > entry = TypeBuilder::Array<String>::create();
139        entry->addItem(name);
140        entry->addItem(value);
141        storageItems->addItem(entry);
142    }
143    items = storageItems.release();
144}
145
146static String toErrorString(ExceptionState& exceptionState)
147{
148    if (exceptionState.hadException())
149        return DOMException::getErrorName(exceptionState.code());
150    return "";
151}
152
153void InspectorDOMStorageAgent::setDOMStorageItem(ErrorString* errorString, const RefPtr<JSONObject>& storageId, const String& key, const String& value)
154{
155    LocalFrame* frame;
156    OwnPtrWillBeRawPtr<StorageArea> storageArea = findStorageArea(0, storageId, frame);
157    if (!storageArea) {
158        *errorString = "Storage not found";
159        return;
160    }
161
162    TrackExceptionState exceptionState;
163    storageArea->setItem(key, value, exceptionState, frame);
164    *errorString = toErrorString(exceptionState);
165}
166
167void InspectorDOMStorageAgent::removeDOMStorageItem(ErrorString* errorString, const RefPtr<JSONObject>& storageId, const String& key)
168{
169    LocalFrame* frame;
170    OwnPtrWillBeRawPtr<StorageArea> storageArea = findStorageArea(0, storageId, frame);
171    if (!storageArea) {
172        *errorString = "Storage not found";
173        return;
174    }
175
176    TrackExceptionState exceptionState;
177    storageArea->removeItem(key, exceptionState, frame);
178    *errorString = toErrorString(exceptionState);
179}
180
181PassRefPtr<TypeBuilder::DOMStorage::StorageId> InspectorDOMStorageAgent::storageId(SecurityOrigin* securityOrigin, bool isLocalStorage)
182{
183    return TypeBuilder::DOMStorage::StorageId::create()
184        .setSecurityOrigin(securityOrigin->toRawString())
185        .setIsLocalStorage(isLocalStorage).release();
186}
187
188void InspectorDOMStorageAgent::didDispatchDOMStorageEvent(const String& key, const String& oldValue, const String& newValue, StorageType storageType, SecurityOrigin* securityOrigin)
189{
190    if (!m_frontend || !isEnabled())
191        return;
192
193    RefPtr<TypeBuilder::DOMStorage::StorageId> id = storageId(securityOrigin, storageType == LocalStorage);
194
195    if (key.isNull())
196        m_frontend->domStorageItemsCleared(id);
197    else if (newValue.isNull())
198        m_frontend->domStorageItemRemoved(id, key);
199    else if (oldValue.isNull())
200        m_frontend->domStorageItemAdded(id, key, newValue);
201    else
202        m_frontend->domStorageItemUpdated(id, key, oldValue, newValue);
203}
204
205PassOwnPtrWillBeRawPtr<StorageArea> InspectorDOMStorageAgent::findStorageArea(ErrorString* errorString, const RefPtr<JSONObject>& storageId, LocalFrame*& targetFrame)
206{
207    String securityOrigin;
208    bool isLocalStorage = false;
209    bool success = storageId->getString("securityOrigin", &securityOrigin);
210    if (success)
211        success = storageId->getBoolean("isLocalStorage", &isLocalStorage);
212    if (!success) {
213        if (errorString)
214            *errorString = "Invalid storageId format";
215        return nullptr;
216    }
217
218    LocalFrame* frame = m_pageAgent->findFrameWithSecurityOrigin(securityOrigin);
219    if (!frame) {
220        if (errorString)
221            *errorString = "LocalFrame not found for the given security origin";
222        return nullptr;
223    }
224    targetFrame = frame;
225
226    if (isLocalStorage)
227        return StorageNamespace::localStorageArea(frame->document()->securityOrigin());
228    return m_pageAgent->page()->sessionStorage()->storageArea(frame->document()->securityOrigin());
229}
230
231} // namespace blink
232
233