1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.javascript.host;
16
17 import static org.htmlunit.BrowserVersionFeatures.JS_STORAGE_PRESERVED_INCLUDED;
18
19 import java.util.Arrays;
20 import java.util.HashSet;
21 import java.util.Map;
22
23 import org.htmlunit.corejs.javascript.Scriptable;
24 import org.htmlunit.javascript.HtmlUnitScriptable;
25 import org.htmlunit.javascript.JavaScriptEngine;
26 import org.htmlunit.javascript.configuration.JsxClass;
27 import org.htmlunit.javascript.configuration.JsxConstructor;
28 import org.htmlunit.javascript.configuration.JsxFunction;
29 import org.htmlunit.javascript.configuration.JsxGetter;
30 import org.w3c.dom.DOMException;
31
32
33
34
35
36
37
38
39 @JsxClass
40 public class Storage extends HtmlUnitScriptable {
41
42 private static final HashSet<String> RESERVED_NAMES_ = new HashSet<>(Arrays.asList(
43 "clear", "key", "getItem", "length", "removeItem",
44 "setItem", "constructor", "toString", "toLocaleString", "valueOf", "hasOwnProperty", "propertyIsEnumerable",
45 "isPrototypeOf", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__"));
46
47 private static final long STORE_SIZE_KIMIT = 5_200_000;
48
49 private final Map<String, String> store_;
50 private long storeSize_;
51
52
53
54
55 public Storage() {
56 super();
57 store_ = null;
58 }
59
60
61
62
63 @JsxConstructor
64 public void jsConstructor() {
65
66 }
67
68
69
70
71
72
73 public Storage(final Window window, final Map<String, String> store) {
74 super();
75 store_ = store;
76 storeSize_ = 0L;
77 setParentScope(window);
78 setPrototype(window.getPrototype(Storage.class));
79 }
80
81
82
83
84 @Override
85 public void put(final String name, final Scriptable start, final Object value) {
86 final boolean isReserved = RESERVED_NAMES_.contains(name);
87 if (store_ == null || isReserved) {
88 super.put(name, start, value);
89 }
90 if (store_ != null && (!isReserved || getBrowserVersion().hasFeature(JS_STORAGE_PRESERVED_INCLUDED))) {
91 setItem(name, JavaScriptEngine.toString(value));
92 }
93 }
94
95
96
97
98 @Override
99 public Object get(final String name, final Scriptable start) {
100 if (store_ == null || RESERVED_NAMES_.contains(name)) {
101 return super.get(name, start);
102 }
103 final Object value = getItem(name);
104 if (value != null) {
105 return value;
106 }
107 return super.get(name, start);
108 }
109
110
111
112
113
114 @JsxGetter
115 public int getLength() {
116 return store_.size();
117 }
118
119
120
121
122
123 @JsxFunction
124 public void removeItem(final String key) {
125 final String removed = store_.remove(key);
126 if (removed != null) {
127 storeSize_ -= removed.length();
128 }
129 }
130
131
132
133
134
135
136 @JsxFunction
137 public String key(final int index) {
138 int counter = 0;
139 for (final String key : store_.keySet()) {
140 if (counter++ == index) {
141 return key;
142 }
143 }
144 return null;
145 }
146
147
148
149
150
151
152 @JsxFunction
153 public Object getItem(final String key) {
154 return store_.get(key);
155 }
156
157
158
159
160
161
162 @JsxFunction
163 public void setItem(final String key, final String data) {
164 final long storeSize = storeSize_ + data.length();
165 if (storeSize > STORE_SIZE_KIMIT) {
166 throw JavaScriptEngine.throwAsScriptRuntimeEx(
167 new DOMException((short) 22, "QuotaExceededError: Failed to execute 'setItem' on 'Storage': "
168 + "Setting the value of '" + key + "' exceeded the quota."));
169 }
170 storeSize_ = storeSize;
171 store_.put(key, data);
172 }
173
174
175
176
177 @JsxFunction
178 public void clear() {
179 store_.clear();
180 storeSize_ = 0;
181 }
182 }