1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "js/Array.h" // JS::GetArrayLength, JS::IsArrayObject
8 #include "js/JSON.h"
9 #include "jsapi.h"
10 #include "mozilla/PresShell.h"
11 #include "mozilla/dom/AutocompleteInfoBinding.h"
12 #include "mozilla/dom/CanonicalBrowsingContext.h"
13 #include "mozilla/dom/Document.h"
14 #include "mozilla/dom/DocumentInlines.h"
15 #include "mozilla/dom/HTMLInputElement.h"
16 #include "mozilla/dom/HTMLSelectElement.h"
17 #include "mozilla/dom/HTMLTextAreaElement.h"
18 #include "mozilla/dom/RootedDictionary.h"
19 #include "mozilla/dom/SessionStorageManager.h"
20 #include "mozilla/dom/PBackgroundSessionStorageCache.h"
21 #include "mozilla/dom/SessionStoreUtils.h"
22 #include "mozilla/dom/txIXPathContext.h"
23 #include "mozilla/dom/WindowGlobalParent.h"
24 #include "mozilla/dom/WindowProxyHolder.h"
25 #include "mozilla/dom/XPathResult.h"
26 #include "mozilla/dom/XPathEvaluator.h"
27 #include "mozilla/dom/XPathExpression.h"
28 #include "mozilla/dom/PBackgroundSessionStorageCache.h"
29 #include "mozilla/ipc/BackgroundUtils.h"
30 #include "mozilla/ReverseIterator.h"
31 #include "mozilla/UniquePtr.h"
32 #include "nsCharSeparatedTokenizer.h"
33 #include "nsContentList.h"
34 #include "nsContentUtils.h"
35 #include "nsFocusManager.h"
36 #include "nsGlobalWindowOuter.h"
37 #include "nsIDocShell.h"
38 #include "nsIFormControl.h"
39 #include "nsIScrollableFrame.h"
40 #include "nsISHistory.h"
41 #include "nsIXULRuntime.h"
42 #include "nsPresContext.h"
43 #include "nsPrintfCString.h"
44
45 using namespace mozilla;
46 using namespace mozilla::dom;
47 using namespace mozilla::dom::sessionstore;
48
49 namespace {
50
51 class DynamicFrameEventFilter final : public nsIDOMEventListener {
52 public:
53 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(DynamicFrameEventFilter)54 NS_DECL_CYCLE_COLLECTION_CLASS(DynamicFrameEventFilter)
55
56 explicit DynamicFrameEventFilter(EventListener* aListener)
57 : mListener(aListener) {}
58
HandleEvent(Event * aEvent)59 NS_IMETHODIMP HandleEvent(Event* aEvent) override {
60 if (mListener && TargetInNonDynamicDocShell(aEvent)) {
61 mListener->HandleEvent(*aEvent);
62 }
63
64 return NS_OK;
65 }
66
67 private:
68 ~DynamicFrameEventFilter() = default;
69
TargetInNonDynamicDocShell(Event * aEvent)70 bool TargetInNonDynamicDocShell(Event* aEvent) {
71 EventTarget* target = aEvent->GetTarget();
72 if (!target) {
73 return false;
74 }
75
76 nsPIDOMWindowOuter* outer = target->GetOwnerGlobalForBindingsInternal();
77 if (!outer || !outer->GetDocShell()) {
78 return false;
79 }
80
81 RefPtr<BrowsingContext> context = outer->GetBrowsingContext();
82 return context && !context->CreatedDynamically();
83 }
84
85 RefPtr<EventListener> mListener;
86 };
87
88 NS_IMPL_CYCLE_COLLECTION(DynamicFrameEventFilter, mListener)
89
90 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DynamicFrameEventFilter)
91 NS_INTERFACE_MAP_ENTRY(nsISupports)
92 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
93 NS_INTERFACE_MAP_END
94
95 NS_IMPL_CYCLE_COLLECTING_ADDREF(DynamicFrameEventFilter)
96 NS_IMPL_CYCLE_COLLECTING_RELEASE(DynamicFrameEventFilter)
97
98 } // anonymous namespace
99
100 /* static */
ForEachNonDynamicChildFrame(const GlobalObject & aGlobal,WindowProxyHolder & aWindow,SessionStoreUtilsFrameCallback & aCallback,ErrorResult & aRv)101 void SessionStoreUtils::ForEachNonDynamicChildFrame(
102 const GlobalObject& aGlobal, WindowProxyHolder& aWindow,
103 SessionStoreUtilsFrameCallback& aCallback, ErrorResult& aRv) {
104 if (!aWindow.get()) {
105 aRv.Throw(NS_ERROR_INVALID_ARG);
106 return;
107 }
108
109 nsCOMPtr<nsIDocShell> docShell = aWindow.get()->GetDocShell();
110 if (!docShell) {
111 aRv.Throw(NS_ERROR_FAILURE);
112 return;
113 }
114
115 int32_t length;
116 aRv = docShell->GetInProcessChildCount(&length);
117 if (aRv.Failed()) {
118 return;
119 }
120
121 for (int32_t i = 0; i < length; ++i) {
122 nsCOMPtr<nsIDocShellTreeItem> item;
123 docShell->GetInProcessChildAt(i, getter_AddRefs(item));
124 if (!item) {
125 aRv.Throw(NS_ERROR_FAILURE);
126 return;
127 }
128
129 RefPtr<BrowsingContext> context = item->GetBrowsingContext();
130 if (!context) {
131 aRv.Throw(NS_ERROR_FAILURE);
132 return;
133 }
134
135 if (!context->CreatedDynamically()) {
136 int32_t childOffset = context->ChildOffset();
137 aCallback.Call(WindowProxyHolder(context.forget()), childOffset);
138 }
139 }
140 }
141
142 /* static */
143 already_AddRefed<nsISupports>
AddDynamicFrameFilteredListener(const GlobalObject & aGlobal,EventTarget & aTarget,const nsAString & aType,JS::Handle<JS::Value> aListener,bool aUseCapture,bool aMozSystemGroup,ErrorResult & aRv)144 SessionStoreUtils::AddDynamicFrameFilteredListener(
145 const GlobalObject& aGlobal, EventTarget& aTarget, const nsAString& aType,
146 JS::Handle<JS::Value> aListener, bool aUseCapture, bool aMozSystemGroup,
147 ErrorResult& aRv) {
148 if (NS_WARN_IF(!aListener.isObject())) {
149 aRv.Throw(NS_ERROR_INVALID_ARG);
150 return nullptr;
151 }
152
153 JSContext* cx = aGlobal.Context();
154 JS::Rooted<JSObject*> obj(cx, &aListener.toObject());
155 JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
156 RefPtr<EventListener> listener =
157 new EventListener(cx, obj, global, GetIncumbentGlobal());
158
159 nsCOMPtr<nsIDOMEventListener> filter(new DynamicFrameEventFilter(listener));
160 if (aMozSystemGroup) {
161 aRv = aTarget.AddSystemEventListener(aType, filter, aUseCapture);
162 } else {
163 aRv = aTarget.AddEventListener(aType, filter, aUseCapture);
164 }
165 if (aRv.Failed()) {
166 return nullptr;
167 }
168
169 return filter.forget();
170 }
171
172 /* static */
RemoveDynamicFrameFilteredListener(const GlobalObject & global,EventTarget & aTarget,const nsAString & aType,nsISupports * aListener,bool aUseCapture,bool aMozSystemGroup,ErrorResult & aRv)173 void SessionStoreUtils::RemoveDynamicFrameFilteredListener(
174 const GlobalObject& global, EventTarget& aTarget, const nsAString& aType,
175 nsISupports* aListener, bool aUseCapture, bool aMozSystemGroup,
176 ErrorResult& aRv) {
177 nsCOMPtr<nsIDOMEventListener> listener = do_QueryInterface(aListener);
178 if (!listener) {
179 aRv.Throw(NS_ERROR_NO_INTERFACE);
180 return;
181 }
182
183 if (aMozSystemGroup) {
184 aTarget.RemoveSystemEventListener(aType, listener, aUseCapture);
185 } else {
186 aTarget.RemoveEventListener(aType, listener, aUseCapture);
187 }
188 }
189
190 /* static */
CollectDocShellCapabilities(const GlobalObject & aGlobal,nsIDocShell * aDocShell,nsCString & aRetVal)191 void SessionStoreUtils::CollectDocShellCapabilities(const GlobalObject& aGlobal,
192 nsIDocShell* aDocShell,
193 nsCString& aRetVal) {
194 bool allow;
195
196 #define TRY_ALLOWPROP(y) \
197 PR_BEGIN_MACRO \
198 aDocShell->GetAllow##y(&allow); \
199 if (!allow) { \
200 if (!aRetVal.IsEmpty()) { \
201 aRetVal.Append(','); \
202 } \
203 aRetVal.Append(#y); \
204 } \
205 PR_END_MACRO
206
207 TRY_ALLOWPROP(Plugins);
208 // Bug 1328013 : Don't collect "AllowJavascript" property
209 // TRY_ALLOWPROP(Javascript);
210 TRY_ALLOWPROP(MetaRedirects);
211 TRY_ALLOWPROP(Subframes);
212 TRY_ALLOWPROP(Images);
213 TRY_ALLOWPROP(Media);
214 TRY_ALLOWPROP(DNSPrefetch);
215 TRY_ALLOWPROP(WindowControl);
216 TRY_ALLOWPROP(Auth);
217 TRY_ALLOWPROP(ContentRetargeting);
218 TRY_ALLOWPROP(ContentRetargetingOnChildren);
219 #undef TRY_ALLOWPROP
220 }
221
222 /* static */
RestoreDocShellCapabilities(nsIDocShell * aDocShell,const nsCString & aDisallowCapabilities)223 void SessionStoreUtils::RestoreDocShellCapabilities(
224 nsIDocShell* aDocShell, const nsCString& aDisallowCapabilities) {
225 aDocShell->SetAllowPlugins(true);
226 aDocShell->SetAllowMetaRedirects(true);
227 aDocShell->SetAllowSubframes(true);
228 aDocShell->SetAllowImages(true);
229 aDocShell->SetAllowMedia(true);
230 aDocShell->SetAllowDNSPrefetch(true);
231 aDocShell->SetAllowWindowControl(true);
232 aDocShell->SetAllowContentRetargeting(true);
233 aDocShell->SetAllowContentRetargetingOnChildren(true);
234
235 bool allowJavascript = true;
236 for (const nsACString& token :
237 nsCCharSeparatedTokenizer(aDisallowCapabilities, ',').ToRange()) {
238 if (token.EqualsLiteral("Plugins")) {
239 aDocShell->SetAllowPlugins(false);
240 } else if (token.EqualsLiteral("Javascript")) {
241 allowJavascript = false;
242 } else if (token.EqualsLiteral("MetaRedirects")) {
243 aDocShell->SetAllowMetaRedirects(false);
244 } else if (token.EqualsLiteral("Subframes")) {
245 aDocShell->SetAllowSubframes(false);
246 } else if (token.EqualsLiteral("Images")) {
247 aDocShell->SetAllowImages(false);
248 } else if (token.EqualsLiteral("Media")) {
249 aDocShell->SetAllowMedia(false);
250 } else if (token.EqualsLiteral("DNSPrefetch")) {
251 aDocShell->SetAllowDNSPrefetch(false);
252 } else if (token.EqualsLiteral("WindowControl")) {
253 aDocShell->SetAllowWindowControl(false);
254 } else if (token.EqualsLiteral("ContentRetargeting")) {
255 bool allow;
256 aDocShell->GetAllowContentRetargetingOnChildren(&allow);
257 aDocShell->SetAllowContentRetargeting(
258 false); // will also set AllowContentRetargetingOnChildren
259 aDocShell->SetAllowContentRetargetingOnChildren(
260 allow); // restore the allowProp to original
261 } else if (token.EqualsLiteral("ContentRetargetingOnChildren")) {
262 aDocShell->SetAllowContentRetargetingOnChildren(false);
263 }
264 }
265
266 if (!mozilla::SessionHistoryInParent()) {
267 // With SessionHistoryInParent, this is set from the parent process.
268 BrowsingContext* bc = aDocShell->GetBrowsingContext();
269 Unused << bc->SetAllowJavascript(allowJavascript);
270 }
271 }
272
CollectCurrentScrollPosition(JSContext * aCx,Document & aDocument,Nullable<CollectedData> & aRetVal)273 static void CollectCurrentScrollPosition(JSContext* aCx, Document& aDocument,
274 Nullable<CollectedData>& aRetVal) {
275 PresShell* presShell = aDocument.GetPresShell();
276 if (!presShell) {
277 return;
278 }
279 nsPoint scrollPos = presShell->GetVisualViewportOffset();
280 int scrollX = nsPresContext::AppUnitsToIntCSSPixels(scrollPos.x);
281 int scrollY = nsPresContext::AppUnitsToIntCSSPixels(scrollPos.y);
282
283 if ((scrollX != 0) || (scrollY != 0)) {
284 aRetVal.SetValue().mScroll.Construct() =
285 nsPrintfCString("%d,%d", scrollX, scrollY);
286 }
287 }
288
289 /* static */
RestoreScrollPosition(const GlobalObject & aGlobal,nsGlobalWindowInner & aWindow,const CollectedData & aData)290 void SessionStoreUtils::RestoreScrollPosition(const GlobalObject& aGlobal,
291 nsGlobalWindowInner& aWindow,
292 const CollectedData& aData) {
293 if (aData.mScroll.WasPassed()) {
294 RestoreScrollPosition(aWindow, aData.mScroll.Value());
295 }
296 }
297
298 /* static */
RestoreScrollPosition(nsGlobalWindowInner & aWindow,const nsCString & aScrollPosition)299 void SessionStoreUtils::RestoreScrollPosition(
300 nsGlobalWindowInner& aWindow, const nsCString& aScrollPosition) {
301 nsCCharSeparatedTokenizer tokenizer(aScrollPosition, ',');
302 nsAutoCString token(tokenizer.nextToken());
303 int pos_X = atoi(token.get());
304 token = tokenizer.nextToken();
305 int pos_Y = atoi(token.get());
306
307 aWindow.ScrollTo(pos_X, pos_Y);
308
309 if (nsCOMPtr<Document> doc = aWindow.GetExtantDoc()) {
310 if (nsPresContext* presContext = doc->GetPresContext()) {
311 if (presContext->IsRootContentDocument()) {
312 // Use eMainThread so this takes precedence over session history
313 // (ScrollFrameHelper::ScrollToRestoredPosition()).
314 presContext->PresShell()->ScrollToVisual(
315 CSSPoint::ToAppUnits(CSSPoint(pos_X, pos_Y)),
316 layers::FrameMetrics::eMainThread, ScrollMode::Instant);
317 }
318 }
319 }
320 }
321
322 // Implements the Luhn checksum algorithm as described at
323 // http://wikipedia.org/wiki/Luhn_algorithm
324 // Number digit lengths vary with network, but should fall within 12-19 range.
325 // [2] More details at https://en.wikipedia.org/wiki/Payment_card_number
IsValidCCNumber(nsAString & aValue)326 static bool IsValidCCNumber(nsAString& aValue) {
327 uint32_t total = 0;
328 uint32_t numLength = 0;
329 uint32_t strLen = aValue.Length();
330 for (uint32_t i = 0; i < strLen; ++i) {
331 uint32_t idx = strLen - i - 1;
332 // ignore whitespace and dashes)
333 char16_t chr = aValue[idx];
334 if (IsSpaceCharacter(chr) || chr == '-') {
335 continue;
336 }
337 // If our number is too long, note that fact
338 ++numLength;
339 if (numLength > 19) {
340 return false;
341 }
342 // Try to parse the character as a base-10 integer.
343 nsresult rv = NS_OK;
344 uint32_t val = Substring(aValue, idx, 1).ToInteger(&rv, 10);
345 if (NS_FAILED(rv)) {
346 return false;
347 }
348 if (i % 2 == 1) {
349 val *= 2;
350 if (val > 9) {
351 val -= 9;
352 }
353 }
354 total += val;
355 }
356
357 return numLength >= 12 && total % 10 == 0;
358 }
359
360 // Limit the number of XPath expressions for performance reasons. See bug
361 // 477564.
362 static const uint16_t kMaxTraversedXPaths = 100;
363
364 // A helper function to append a element into mId or mXpath of CollectedData
365 static Record<nsString, OwningStringOrBooleanOrObject>::EntryType*
AppendEntryToCollectedData(nsINode * aNode,const nsAString & aId,uint16_t & aGeneratedCount,Nullable<CollectedData> & aRetVal)366 AppendEntryToCollectedData(nsINode* aNode, const nsAString& aId,
367 uint16_t& aGeneratedCount,
368 Nullable<CollectedData>& aRetVal) {
369 Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry;
370 if (!aId.IsEmpty()) {
371 if (!aRetVal.SetValue().mId.WasPassed()) {
372 aRetVal.SetValue().mId.Construct();
373 }
374 auto& recordEntries = aRetVal.SetValue().mId.Value().Entries();
375 entry = recordEntries.AppendElement();
376 entry->mKey = aId;
377 } else {
378 if (!aRetVal.SetValue().mXpath.WasPassed()) {
379 aRetVal.SetValue().mXpath.Construct();
380 }
381 auto& recordEntries = aRetVal.SetValue().mXpath.Value().Entries();
382 entry = recordEntries.AppendElement();
383 nsAutoString xpath;
384 aNode->GenerateXPath(xpath);
385 aGeneratedCount++;
386 entry->mKey = xpath;
387 }
388 return entry;
389 }
390
391 /* for bool value */
AppendValueToCollectedData(nsINode * aNode,const nsAString & aId,const bool & aValue,uint16_t & aGeneratedCount,JSContext * aCx,Nullable<CollectedData> & aRetVal)392 static void AppendValueToCollectedData(nsINode* aNode, const nsAString& aId,
393 const bool& aValue,
394 uint16_t& aGeneratedCount,
395 JSContext* aCx,
396 Nullable<CollectedData>& aRetVal) {
397 Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
398 AppendEntryToCollectedData(aNode, aId, aGeneratedCount, aRetVal);
399 entry->mValue.SetAsBoolean() = aValue;
400 }
401
402 /* for nsString value */
AppendValueToCollectedData(nsINode * aNode,const nsAString & aId,const nsString & aValue,uint16_t & aGeneratedCount,Nullable<CollectedData> & aRetVal)403 static void AppendValueToCollectedData(nsINode* aNode, const nsAString& aId,
404 const nsString& aValue,
405 uint16_t& aGeneratedCount,
406 Nullable<CollectedData>& aRetVal) {
407 Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
408 AppendEntryToCollectedData(aNode, aId, aGeneratedCount, aRetVal);
409 entry->mValue.SetAsString() = aValue;
410 }
411
412 /* for single select value */
AppendValueToCollectedData(nsINode * aNode,const nsAString & aId,const CollectedNonMultipleSelectValue & aValue,uint16_t & aGeneratedCount,JSContext * aCx,Nullable<CollectedData> & aRetVal)413 static void AppendValueToCollectedData(
414 nsINode* aNode, const nsAString& aId,
415 const CollectedNonMultipleSelectValue& aValue, uint16_t& aGeneratedCount,
416 JSContext* aCx, Nullable<CollectedData>& aRetVal) {
417 JS::Rooted<JS::Value> jsval(aCx);
418 if (!ToJSValue(aCx, aValue, &jsval)) {
419 JS_ClearPendingException(aCx);
420 return;
421 }
422 Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
423 AppendEntryToCollectedData(aNode, aId, aGeneratedCount, aRetVal);
424 entry->mValue.SetAsObject() = &jsval.toObject();
425 }
426
427 /* special handing for input element with string type */
AppendValueToCollectedData(Document & aDocument,nsINode * aNode,const nsAString & aId,const nsString & aValue,uint16_t & aGeneratedCount,JSContext * aCx,Nullable<CollectedData> & aRetVal)428 static void AppendValueToCollectedData(Document& aDocument, nsINode* aNode,
429 const nsAString& aId,
430 const nsString& aValue,
431 uint16_t& aGeneratedCount,
432 JSContext* aCx,
433 Nullable<CollectedData>& aRetVal) {
434 if (!aId.IsEmpty()) {
435 // We want to avoid saving data for about:sessionrestore as a string.
436 // Since it's stored in the form as stringified JSON, stringifying
437 // further causes an explosion of escape characters. cf. bug 467409
438 if (aId.EqualsLiteral("sessionData")) {
439 nsAutoCString url;
440 Unused << aDocument.GetDocumentURI()->GetSpecIgnoringRef(url);
441 if (url.EqualsLiteral("about:sessionrestore") ||
442 url.EqualsLiteral("about:welcomeback")) {
443 JS::Rooted<JS::Value> jsval(aCx);
444 if (JS_ParseJSON(aCx, aValue.get(), aValue.Length(), &jsval) &&
445 jsval.isObject()) {
446 Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
447 AppendEntryToCollectedData(aNode, aId, aGeneratedCount, aRetVal);
448 entry->mValue.SetAsObject() = &jsval.toObject();
449 } else {
450 JS_ClearPendingException(aCx);
451 }
452 return;
453 }
454 }
455 }
456 AppendValueToCollectedData(aNode, aId, aValue, aGeneratedCount, aRetVal);
457 }
458
459 /* for nsTArray<nsString>: file and multipleSelect */
AppendValueToCollectedData(nsINode * aNode,const nsAString & aId,const nsAString & aValueType,nsTArray<nsString> & aValue,uint16_t & aGeneratedCount,JSContext * aCx,Nullable<CollectedData> & aRetVal)460 static void AppendValueToCollectedData(nsINode* aNode, const nsAString& aId,
461 const nsAString& aValueType,
462 nsTArray<nsString>& aValue,
463 uint16_t& aGeneratedCount,
464 JSContext* aCx,
465 Nullable<CollectedData>& aRetVal) {
466 JS::Rooted<JS::Value> jsval(aCx);
467 if (aValueType.EqualsLiteral("file")) {
468 CollectedFileListValue val;
469 val.mType = aValueType;
470 val.mFileList = std::move(aValue);
471 if (!ToJSValue(aCx, val, &jsval)) {
472 JS_ClearPendingException(aCx);
473 return;
474 }
475 } else {
476 if (!ToJSValue(aCx, aValue, &jsval)) {
477 JS_ClearPendingException(aCx);
478 return;
479 }
480 }
481 Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
482 AppendEntryToCollectedData(aNode, aId, aGeneratedCount, aRetVal);
483 entry->mValue.SetAsObject() = &jsval.toObject();
484 }
485
AppendEntry(nsINode * aNode,const nsString & aId,const FormEntryValue & aValue,sessionstore::FormData & aFormData)486 static void AppendEntry(nsINode* aNode, const nsString& aId,
487 const FormEntryValue& aValue,
488 sessionstore::FormData& aFormData) {
489 if (aId.IsEmpty()) {
490 FormEntry* entry = aFormData.xpath().AppendElement();
491 entry->value() = aValue;
492 aNode->GenerateXPath(entry->id());
493 } else {
494 aFormData.id().AppendElement(FormEntry{aId, aValue});
495 }
496 }
497
CollectTextAreaElement(Document * aDocument,sessionstore::FormData & aFormData)498 static void CollectTextAreaElement(Document* aDocument,
499 sessionstore::FormData& aFormData) {
500 RefPtr<nsContentList> textlist =
501 NS_GetContentList(aDocument, kNameSpaceID_XHTML, u"textarea"_ns);
502 uint32_t length = textlist->Length();
503 for (uint32_t i = 0; i < length; ++i) {
504 MOZ_ASSERT(textlist->Item(i), "null item in node list!");
505
506 HTMLTextAreaElement* textArea =
507 HTMLTextAreaElement::FromNodeOrNull(textlist->Item(i));
508 if (!textArea) {
509 continue;
510 }
511 DOMString autocomplete;
512 textArea->GetAutocomplete(autocomplete);
513 if (autocomplete.AsAString().EqualsLiteral("off")) {
514 continue;
515 }
516 nsAutoString id;
517 textArea->GetId(id);
518 if (id.IsEmpty() && (aFormData.xpath().Length() > kMaxTraversedXPaths)) {
519 continue;
520 }
521 nsString value;
522 textArea->GetValue(value);
523 // In order to reduce XPath generation (which is slow), we only save data
524 // for form fields that have been changed. (cf. bug 537289)
525 if (textArea->AttrValueIs(kNameSpaceID_None, nsGkAtoms::value, value,
526 eCaseMatters)) {
527 continue;
528 }
529
530 AppendEntry(textArea, id, TextField{value}, aFormData);
531 }
532 }
533
CollectInputElement(Document * aDocument,sessionstore::FormData & aFormData)534 static void CollectInputElement(Document* aDocument,
535 sessionstore::FormData& aFormData) {
536 RefPtr<nsContentList> inputlist =
537 NS_GetContentList(aDocument, kNameSpaceID_XHTML, u"input"_ns);
538 uint32_t length = inputlist->Length();
539 for (uint32_t i = 0; i < length; ++i) {
540 MOZ_ASSERT(inputlist->Item(i), "null item in node list!");
541 nsCOMPtr<nsIFormControl> formControl =
542 do_QueryInterface(inputlist->Item(i));
543 if (formControl) {
544 auto controlType = formControl->ControlType();
545 if (controlType == FormControlType::InputPassword ||
546 controlType == FormControlType::InputHidden ||
547 controlType == FormControlType::InputButton ||
548 controlType == FormControlType::InputImage ||
549 controlType == FormControlType::InputSubmit ||
550 controlType == FormControlType::InputReset) {
551 continue;
552 }
553 }
554 RefPtr<HTMLInputElement> input =
555 HTMLInputElement::FromNodeOrNull(inputlist->Item(i));
556 if (!input || !nsContentUtils::IsAutocompleteEnabled(input)) {
557 continue;
558 }
559 nsAutoString id;
560 input->GetId(id);
561 if (id.IsEmpty() && (aFormData.xpath().Length() > kMaxTraversedXPaths)) {
562 continue;
563 }
564 Nullable<AutocompleteInfo> aInfo;
565 input->GetAutocompleteInfo(aInfo);
566 if (!aInfo.IsNull() && !aInfo.Value().mCanAutomaticallyPersist) {
567 continue;
568 }
569
570 FormEntryValue value;
571 if (input->ControlType() == FormControlType::InputCheckbox ||
572 input->ControlType() == FormControlType::InputRadio) {
573 bool checked = input->Checked();
574 if (checked == input->DefaultChecked()) {
575 continue;
576 }
577 AppendEntry(input, id, Checkbox{checked}, aFormData);
578 } else if (input->ControlType() == FormControlType::InputFile) {
579 IgnoredErrorResult rv;
580 sessionstore::FileList file;
581 input->MozGetFileNameArray(file.valueList(), rv);
582 if (rv.Failed() || file.valueList().IsEmpty()) {
583 continue;
584 }
585 AppendEntry(input, id, file, aFormData);
586 } else {
587 TextField field;
588 input->GetValue(field.value(), CallerType::System);
589 auto& value = field.value();
590 // In order to reduce XPath generation (which is slow), we only save data
591 // for form fields that have been changed. (cf. bug 537289)
592 // Also, don't want to collect credit card number.
593 if (value.IsEmpty() || IsValidCCNumber(value) ||
594 input->HasBeenTypePassword() ||
595 input->AttrValueIs(kNameSpaceID_None, nsGkAtoms::value, value,
596 eCaseMatters)) {
597 continue;
598 }
599 AppendEntry(input, id, field, aFormData);
600 }
601 }
602 }
603
CollectSelectElement(Document * aDocument,sessionstore::FormData & aFormData)604 static void CollectSelectElement(Document* aDocument,
605 sessionstore::FormData& aFormData) {
606 RefPtr<nsContentList> selectlist =
607 NS_GetContentList(aDocument, kNameSpaceID_XHTML, u"select"_ns);
608 uint32_t length = selectlist->Length();
609 for (uint32_t i = 0; i < length; ++i) {
610 MOZ_ASSERT(selectlist->Item(i), "null item in node list!");
611 RefPtr<HTMLSelectElement> select =
612 HTMLSelectElement::FromNodeOrNull(selectlist->Item(i));
613 if (!select) {
614 continue;
615 }
616 nsAutoString id;
617 select->GetId(id);
618 if (id.IsEmpty() && (aFormData.xpath().Length() > kMaxTraversedXPaths)) {
619 continue;
620 }
621 AutocompleteInfo aInfo;
622 select->GetAutocompleteInfo(aInfo);
623 if (!aInfo.mCanAutomaticallyPersist) {
624 continue;
625 }
626
627 if (!select->Multiple()) {
628 HTMLOptionsCollection* options = select->GetOptions();
629 if (!options) {
630 continue;
631 }
632
633 uint32_t numOptions = options->Length();
634 int32_t defaultIndex = 0;
635 for (uint32_t idx = 0; idx < numOptions; idx++) {
636 HTMLOptionElement* option = options->ItemAsOption(idx);
637 if (option->DefaultSelected()) {
638 defaultIndex = option->Index();
639 }
640 }
641
642 int32_t selectedIndex = select->SelectedIndex();
643 if (selectedIndex == defaultIndex || selectedIndex < 0) {
644 continue;
645 }
646
647 DOMString selectVal;
648 select->GetValue(selectVal);
649 AppendEntry(select, id,
650 SingleSelect{static_cast<uint32_t>(selectedIndex),
651 selectVal.AsAString()},
652 aFormData);
653 } else {
654 HTMLOptionsCollection* options = select->GetOptions();
655 if (!options) {
656 continue;
657 }
658 bool hasDefaultValue = true;
659 nsTArray<nsString> selectslist;
660 uint32_t numOptions = options->Length();
661 for (uint32_t idx = 0; idx < numOptions; idx++) {
662 HTMLOptionElement* option = options->ItemAsOption(idx);
663 bool selected = option->Selected();
664
665 hasDefaultValue =
666 hasDefaultValue && (selected == option->DefaultSelected());
667
668 if (!selected) {
669 continue;
670 }
671 option->GetValue(*selectslist.AppendElement());
672 }
673 // In order to reduce XPath generation (which is slow), we only save data
674 // for form fields that have been changed. (cf. bug 537289)
675 if (hasDefaultValue) {
676 continue;
677 }
678
679 AppendEntry(select, id, MultipleSelect{selectslist}, aFormData);
680 }
681 }
682 }
683
684 /* static */
CollectFormData(Document * aDocument,sessionstore::FormData & aFormData)685 void SessionStoreUtils::CollectFormData(Document* aDocument,
686 sessionstore::FormData& aFormData) {
687 MOZ_DIAGNOSTIC_ASSERT(aDocument);
688 CollectTextAreaElement(aDocument, aFormData);
689 CollectInputElement(aDocument, aFormData);
690 CollectSelectElement(aDocument, aFormData);
691
692 aFormData.hasData() =
693 !aFormData.id().IsEmpty() || !aFormData.xpath().IsEmpty();
694 }
695
696 /* static */
697 template <typename... ArgsT>
CollectFromTextAreaElement(Document & aDocument,uint16_t & aGeneratedCount,ArgsT &&...args)698 void SessionStoreUtils::CollectFromTextAreaElement(Document& aDocument,
699 uint16_t& aGeneratedCount,
700 ArgsT&&... args) {
701 RefPtr<nsContentList> textlist =
702 NS_GetContentList(&aDocument, kNameSpaceID_XHTML, u"textarea"_ns);
703 uint32_t length = textlist->Length(true);
704 for (uint32_t i = 0; i < length; ++i) {
705 MOZ_ASSERT(textlist->Item(i), "null item in node list!");
706
707 HTMLTextAreaElement* textArea =
708 HTMLTextAreaElement::FromNodeOrNull(textlist->Item(i));
709 if (!textArea) {
710 continue;
711 }
712 DOMString autocomplete;
713 textArea->GetAutocomplete(autocomplete);
714 if (autocomplete.AsAString().EqualsLiteral("off")) {
715 continue;
716 }
717 nsAutoString id;
718 textArea->GetId(id);
719 if (id.IsEmpty() && (aGeneratedCount > kMaxTraversedXPaths)) {
720 continue;
721 }
722 nsString value;
723 textArea->GetValue(value);
724 // In order to reduce XPath generation (which is slow), we only save data
725 // for form fields that have been changed. (cf. bug 537289)
726 if (textArea->AttrValueIs(kNameSpaceID_None, nsGkAtoms::value, value,
727 eCaseMatters)) {
728 continue;
729 }
730 AppendValueToCollectedData(textArea, id, value, aGeneratedCount,
731 std::forward<ArgsT>(args)...);
732 }
733 }
734
735 /* static */
736 template <typename... ArgsT>
CollectFromInputElement(Document & aDocument,uint16_t & aGeneratedCount,ArgsT &&...args)737 void SessionStoreUtils::CollectFromInputElement(Document& aDocument,
738 uint16_t& aGeneratedCount,
739 ArgsT&&... args) {
740 RefPtr<nsContentList> inputlist =
741 NS_GetContentList(&aDocument, kNameSpaceID_XHTML, u"input"_ns);
742 uint32_t length = inputlist->Length(true);
743 for (uint32_t i = 0; i < length; ++i) {
744 MOZ_ASSERT(inputlist->Item(i), "null item in node list!");
745 nsCOMPtr<nsIFormControl> formControl =
746 do_QueryInterface(inputlist->Item(i));
747 if (formControl) {
748 auto controlType = formControl->ControlType();
749 if (controlType == FormControlType::InputPassword ||
750 controlType == FormControlType::InputHidden ||
751 controlType == FormControlType::InputButton ||
752 controlType == FormControlType::InputImage ||
753 controlType == FormControlType::InputSubmit ||
754 controlType == FormControlType::InputReset) {
755 continue;
756 }
757 }
758 RefPtr<HTMLInputElement> input =
759 HTMLInputElement::FromNodeOrNull(inputlist->Item(i));
760 if (!input || !nsContentUtils::IsAutocompleteEnabled(input)) {
761 continue;
762 }
763 nsAutoString id;
764 input->GetId(id);
765 if (id.IsEmpty() && (aGeneratedCount > kMaxTraversedXPaths)) {
766 continue;
767 }
768 Nullable<AutocompleteInfo> aInfo;
769 input->GetAutocompleteInfo(aInfo);
770 if (!aInfo.IsNull() && !aInfo.Value().mCanAutomaticallyPersist) {
771 continue;
772 }
773
774 if (input->ControlType() == FormControlType::InputCheckbox ||
775 input->ControlType() == FormControlType::InputRadio) {
776 bool checked = input->Checked();
777 if (checked == input->DefaultChecked()) {
778 continue;
779 }
780 AppendValueToCollectedData(input, id, checked, aGeneratedCount,
781 std::forward<ArgsT>(args)...);
782 } else if (input->ControlType() == FormControlType::InputFile) {
783 IgnoredErrorResult rv;
784 nsTArray<nsString> result;
785 input->MozGetFileNameArray(result, rv);
786 if (rv.Failed() || result.Length() == 0) {
787 continue;
788 }
789 AppendValueToCollectedData(input, id, u"file"_ns, result, aGeneratedCount,
790 std::forward<ArgsT>(args)...);
791 } else {
792 nsString value;
793 input->GetValue(value, CallerType::System);
794 // In order to reduce XPath generation (which is slow), we only save data
795 // for form fields that have been changed. (cf. bug 537289)
796 // Also, don't want to collect credit card number.
797 if (value.IsEmpty() || IsValidCCNumber(value) ||
798 input->HasBeenTypePassword() ||
799 input->AttrValueIs(kNameSpaceID_None, nsGkAtoms::value, value,
800 eCaseMatters)) {
801 continue;
802 }
803 AppendValueToCollectedData(aDocument, input, id, value, aGeneratedCount,
804 std::forward<ArgsT>(args)...);
805 }
806 }
807 }
808
809 /* static */
810 template <typename... ArgsT>
CollectFromSelectElement(Document & aDocument,uint16_t & aGeneratedCount,ArgsT &&...args)811 void SessionStoreUtils::CollectFromSelectElement(Document& aDocument,
812 uint16_t& aGeneratedCount,
813 ArgsT&&... args) {
814 RefPtr<nsContentList> selectlist =
815 NS_GetContentList(&aDocument, kNameSpaceID_XHTML, u"select"_ns);
816 uint32_t length = selectlist->Length(true);
817 for (uint32_t i = 0; i < length; ++i) {
818 MOZ_ASSERT(selectlist->Item(i), "null item in node list!");
819 RefPtr<HTMLSelectElement> select =
820 HTMLSelectElement::FromNodeOrNull(selectlist->Item(i));
821 if (!select) {
822 continue;
823 }
824 nsAutoString id;
825 select->GetId(id);
826 if (id.IsEmpty() && (aGeneratedCount > kMaxTraversedXPaths)) {
827 continue;
828 }
829 AutocompleteInfo aInfo;
830 select->GetAutocompleteInfo(aInfo);
831 if (!aInfo.mCanAutomaticallyPersist) {
832 continue;
833 }
834 nsAutoCString value;
835 if (!select->Multiple()) {
836 // <select>s without the multiple attribute are hard to determine the
837 // default value, so assume we don't have the default.
838 DOMString selectVal;
839 select->GetValue(selectVal);
840 CollectedNonMultipleSelectValue val;
841 val.mSelectedIndex = select->SelectedIndex();
842 val.mValue = selectVal.AsAString();
843 AppendValueToCollectedData(select, id, val, aGeneratedCount,
844 std::forward<ArgsT>(args)...);
845 } else {
846 // <select>s with the multiple attribute are easier to determine the
847 // default value since each <option> has a defaultSelected property
848 HTMLOptionsCollection* options = select->GetOptions();
849 if (!options) {
850 continue;
851 }
852 bool hasDefaultValue = true;
853 nsTArray<nsString> selectslist;
854 uint32_t numOptions = options->Length();
855 for (uint32_t idx = 0; idx < numOptions; idx++) {
856 HTMLOptionElement* option = options->ItemAsOption(idx);
857 bool selected = option->Selected();
858 if (!selected) {
859 continue;
860 }
861 option->GetValue(*selectslist.AppendElement());
862 hasDefaultValue =
863 hasDefaultValue && (selected == option->DefaultSelected());
864 }
865 // In order to reduce XPath generation (which is slow), we only save data
866 // for form fields that have been changed. (cf. bug 537289)
867 if (hasDefaultValue) {
868 continue;
869 }
870
871 AppendValueToCollectedData(select, id, u"multipleSelect"_ns, selectslist,
872 aGeneratedCount, std::forward<ArgsT>(args)...);
873 }
874 }
875 }
876
CollectCurrentFormData(JSContext * aCx,Document & aDocument,Nullable<CollectedData> & aRetVal)877 static void CollectCurrentFormData(JSContext* aCx, Document& aDocument,
878 Nullable<CollectedData>& aRetVal) {
879 uint16_t generatedCount = 0;
880 /* textarea element */
881 SessionStoreUtils::CollectFromTextAreaElement(aDocument, generatedCount,
882 aRetVal);
883 /* input element */
884 SessionStoreUtils::CollectFromInputElement(aDocument, generatedCount, aCx,
885 aRetVal);
886 /* select element */
887 SessionStoreUtils::CollectFromSelectElement(aDocument, generatedCount, aCx,
888 aRetVal);
889
890 Element* bodyElement = aDocument.GetBody();
891 if (aDocument.HasFlag(NODE_IS_EDITABLE) && bodyElement) {
892 bodyElement->GetInnerHTML(aRetVal.SetValue().mInnerHTML.Construct(),
893 IgnoreErrors());
894 }
895
896 if (aRetVal.IsNull()) {
897 return;
898 }
899
900 // Store the frame's current URL with its form data so that we can compare
901 // it when restoring data to not inject form data into the wrong document.
902 nsIURI* uri = aDocument.GetDocumentURI();
903 if (uri) {
904 uri->GetSpecIgnoringRef(aRetVal.SetValue().mUrl.Construct());
905 }
906 }
907
908 MOZ_CAN_RUN_SCRIPT
SetElementAsString(Element * aElement,const nsAString & aValue)909 static void SetElementAsString(Element* aElement, const nsAString& aValue) {
910 IgnoredErrorResult rv;
911 HTMLTextAreaElement* textArea = HTMLTextAreaElement::FromNode(aElement);
912 if (textArea) {
913 textArea->SetValue(aValue, rv);
914 if (!rv.Failed()) {
915 nsContentUtils::DispatchInputEvent(aElement);
916 }
917 return;
918 }
919 HTMLInputElement* input = HTMLInputElement::FromNode(aElement);
920 if (input) {
921 input->SetValue(aValue, CallerType::NonSystem, rv);
922 if (!rv.Failed()) {
923 nsContentUtils::DispatchInputEvent(aElement);
924 return;
925 }
926 }
927 input = HTMLInputElement::FromNodeOrNull(
928 nsFocusManager::GetRedirectedFocus(aElement));
929 if (input) {
930 input->SetValue(aValue, CallerType::NonSystem, rv);
931 if (!rv.Failed()) {
932 nsContentUtils::DispatchInputEvent(aElement);
933 }
934 }
935 }
936
937 MOZ_CAN_RUN_SCRIPT
SetElementAsBool(Element * aElement,bool aValue)938 static void SetElementAsBool(Element* aElement, bool aValue) {
939 HTMLInputElement* input = HTMLInputElement::FromNode(aElement);
940 if (input) {
941 bool checked = input->Checked();
942 if (aValue != checked) {
943 input->SetChecked(aValue);
944 nsContentUtils::DispatchInputEvent(aElement);
945 }
946 }
947 }
948
949 MOZ_CAN_RUN_SCRIPT
SetElementAsFiles(HTMLInputElement * aElement,const CollectedFileListValue & aValue)950 static void SetElementAsFiles(HTMLInputElement* aElement,
951 const CollectedFileListValue& aValue) {
952 IgnoredErrorResult rv;
953 aElement->MozSetFileNameArray(aValue.mFileList, rv);
954 if (rv.Failed()) {
955 return;
956 }
957 nsContentUtils::DispatchInputEvent(aElement);
958 }
959
960 MOZ_CAN_RUN_SCRIPT
SetElementAsSelect(HTMLSelectElement * aElement,const CollectedNonMultipleSelectValue & aValue)961 static void SetElementAsSelect(HTMLSelectElement* aElement,
962 const CollectedNonMultipleSelectValue& aValue) {
963 HTMLOptionsCollection* options = aElement->GetOptions();
964 if (!options) {
965 return;
966 }
967 int32_t selectIdx = options->SelectedIndex();
968 if (selectIdx >= 0) {
969 nsAutoString selectOptionVal;
970 options->ItemAsOption(selectIdx)->GetValue(selectOptionVal);
971 if (aValue.mValue.Equals(selectOptionVal)) {
972 return;
973 }
974 }
975 uint32_t numOptions = options->Length();
976 for (uint32_t idx = 0; idx < numOptions; idx++) {
977 HTMLOptionElement* option = options->ItemAsOption(idx);
978 nsAutoString optionValue;
979 option->GetValue(optionValue);
980 if (aValue.mValue.Equals(optionValue)) {
981 aElement->SetSelectedIndex(idx);
982 nsContentUtils::DispatchInputEvent(aElement);
983 }
984 }
985 }
986
987 MOZ_CAN_RUN_SCRIPT
SetElementAsMultiSelect(HTMLSelectElement * aElement,const nsTArray<nsString> & aValueArray)988 static void SetElementAsMultiSelect(HTMLSelectElement* aElement,
989 const nsTArray<nsString>& aValueArray) {
990 bool fireEvent = false;
991 HTMLOptionsCollection* options = aElement->GetOptions();
992 if (!options) {
993 return;
994 }
995 uint32_t numOptions = options->Length();
996 for (uint32_t idx = 0; idx < numOptions; idx++) {
997 HTMLOptionElement* option = options->ItemAsOption(idx);
998 nsAutoString optionValue;
999 option->GetValue(optionValue);
1000 for (uint32_t i = 0, l = aValueArray.Length(); i < l; ++i) {
1001 if (optionValue.Equals(aValueArray[i])) {
1002 option->SetSelected(true);
1003 if (!option->DefaultSelected()) {
1004 fireEvent = true;
1005 }
1006 }
1007 }
1008 }
1009 if (fireEvent) {
1010 nsContentUtils::DispatchInputEvent(aElement);
1011 }
1012 }
1013
1014 MOZ_CAN_RUN_SCRIPT
SetElementAsObject(JSContext * aCx,Element * aElement,JS::Handle<JS::Value> aObject)1015 static void SetElementAsObject(JSContext* aCx, Element* aElement,
1016 JS::Handle<JS::Value> aObject) {
1017 RefPtr<HTMLInputElement> input = HTMLInputElement::FromNode(aElement);
1018 if (input) {
1019 if (input->ControlType() == FormControlType::InputFile) {
1020 CollectedFileListValue value;
1021 if (value.Init(aCx, aObject)) {
1022 SetElementAsFiles(input, value);
1023 } else {
1024 JS_ClearPendingException(aCx);
1025 }
1026 }
1027 return;
1028 }
1029 RefPtr<HTMLSelectElement> select = HTMLSelectElement::FromNode(aElement);
1030 if (select) {
1031 // For Single Select Element
1032 if (!select->Multiple()) {
1033 CollectedNonMultipleSelectValue value;
1034 if (value.Init(aCx, aObject)) {
1035 SetElementAsSelect(select, value);
1036 } else {
1037 JS_ClearPendingException(aCx);
1038 }
1039 return;
1040 }
1041
1042 // For Multiple Selects Element
1043 bool isArray = false;
1044 JS::IsArrayObject(aCx, aObject, &isArray);
1045 if (!isArray) {
1046 return;
1047 }
1048 JS::Rooted<JSObject*> arrayObj(aCx, &aObject.toObject());
1049 uint32_t arrayLength = 0;
1050 if (!JS::GetArrayLength(aCx, arrayObj, &arrayLength)) {
1051 JS_ClearPendingException(aCx);
1052 return;
1053 }
1054 nsTArray<nsString> array(arrayLength);
1055 for (uint32_t arrayIdx = 0; arrayIdx < arrayLength; arrayIdx++) {
1056 JS::Rooted<JS::Value> element(aCx);
1057 if (!JS_GetElement(aCx, arrayObj, arrayIdx, &element)) {
1058 JS_ClearPendingException(aCx);
1059 return;
1060 }
1061 if (!element.isString()) {
1062 return;
1063 }
1064 nsAutoJSString value;
1065 if (!value.init(aCx, element)) {
1066 JS_ClearPendingException(aCx);
1067 return;
1068 }
1069 array.AppendElement(value);
1070 }
1071 SetElementAsMultiSelect(select, array);
1072 }
1073 }
1074
1075 MOZ_CAN_RUN_SCRIPT
SetSessionData(JSContext * aCx,Element * aElement,JS::MutableHandle<JS::Value> aObject)1076 static void SetSessionData(JSContext* aCx, Element* aElement,
1077 JS::MutableHandle<JS::Value> aObject) {
1078 nsAutoString data;
1079 if (nsContentUtils::StringifyJSON(aCx, aObject, data)) {
1080 SetElementAsString(aElement, data);
1081 } else {
1082 JS_ClearPendingException(aCx);
1083 }
1084 }
1085
1086 MOZ_CAN_RUN_SCRIPT
SetInnerHTML(Document & aDocument,const nsString & aInnerHTML)1087 static void SetInnerHTML(Document& aDocument, const nsString& aInnerHTML) {
1088 RefPtr<Element> bodyElement = aDocument.GetBody();
1089 if (aDocument.HasFlag(NODE_IS_EDITABLE) && bodyElement) {
1090 IgnoredErrorResult rv;
1091 bodyElement->SetInnerHTML(aInnerHTML, aDocument.NodePrincipal(), rv);
1092 if (!rv.Failed()) {
1093 nsContentUtils::DispatchInputEvent(bodyElement);
1094 }
1095 }
1096 }
1097
1098 class FormDataParseContext : public txIParseContext {
1099 public:
FormDataParseContext(bool aCaseInsensitive)1100 explicit FormDataParseContext(bool aCaseInsensitive)
1101 : mIsCaseInsensitive(aCaseInsensitive) {}
1102
resolveNamespacePrefix(nsAtom * aPrefix,int32_t & aID)1103 nsresult resolveNamespacePrefix(nsAtom* aPrefix, int32_t& aID) override {
1104 if (aPrefix == nsGkAtoms::xul) {
1105 aID = kNameSpaceID_XUL;
1106 } else {
1107 MOZ_ASSERT(nsDependentAtomString(aPrefix).EqualsLiteral("xhtml"));
1108 aID = kNameSpaceID_XHTML;
1109 }
1110 return NS_OK;
1111 }
1112
resolveFunctionCall(nsAtom * aName,int32_t aID,FunctionCall ** aFunction)1113 nsresult resolveFunctionCall(nsAtom* aName, int32_t aID,
1114 FunctionCall** aFunction) override {
1115 return NS_ERROR_XPATH_UNKNOWN_FUNCTION;
1116 }
1117
caseInsensitiveNameTests()1118 bool caseInsensitiveNameTests() override { return mIsCaseInsensitive; }
1119
SetErrorOffset(uint32_t aOffset)1120 void SetErrorOffset(uint32_t aOffset) override {}
1121
1122 private:
1123 bool mIsCaseInsensitive;
1124 };
1125
FindNodeByXPath(Document & aDocument,const nsAString & aExpression)1126 static Element* FindNodeByXPath(Document& aDocument,
1127 const nsAString& aExpression) {
1128 FormDataParseContext parsingContext(aDocument.IsHTMLDocument());
1129 IgnoredErrorResult rv;
1130 UniquePtr<XPathExpression> expression(
1131 aDocument.XPathEvaluator()->CreateExpression(aExpression, &parsingContext,
1132 &aDocument, rv));
1133 if (rv.Failed()) {
1134 return nullptr;
1135 }
1136 RefPtr<XPathResult> result = expression->Evaluate(
1137 aDocument, XPathResult::FIRST_ORDERED_NODE_TYPE, nullptr, rv);
1138 if (rv.Failed()) {
1139 return nullptr;
1140 }
1141 return Element::FromNodeOrNull(result->GetSingleNodeValue(rv));
1142 }
1143
1144 MOZ_CAN_RUN_SCRIPT_BOUNDARY
1145 /* static */
RestoreFormData(const GlobalObject & aGlobal,Document & aDocument,const CollectedData & aData)1146 bool SessionStoreUtils::RestoreFormData(const GlobalObject& aGlobal,
1147 Document& aDocument,
1148 const CollectedData& aData) {
1149 if (!aData.mUrl.WasPassed()) {
1150 return true;
1151 }
1152 // Don't restore any data for the given frame if the URL
1153 // stored in the form data doesn't match its current URL.
1154 nsAutoCString url;
1155 Unused << aDocument.GetDocumentURI()->GetSpecIgnoringRef(url);
1156 if (!aData.mUrl.Value().Equals(url)) {
1157 return false;
1158 }
1159 if (aData.mInnerHTML.WasPassed()) {
1160 SetInnerHTML(aDocument, aData.mInnerHTML.Value());
1161 }
1162 if (aData.mId.WasPassed()) {
1163 for (auto& entry : aData.mId.Value().Entries()) {
1164 RefPtr<Element> node = aDocument.GetElementById(entry.mKey);
1165 if (node == nullptr) {
1166 continue;
1167 }
1168 if (entry.mValue.IsString()) {
1169 SetElementAsString(node, entry.mValue.GetAsString());
1170 } else if (entry.mValue.IsBoolean()) {
1171 SetElementAsBool(node, entry.mValue.GetAsBoolean());
1172 } else {
1173 // For about:{sessionrestore,welcomeback} we saved the field as JSON to
1174 // avoid nested instances causing humongous sessionstore.js files.
1175 // cf. bug 467409
1176 JSContext* cx = aGlobal.Context();
1177 if (entry.mKey.EqualsLiteral("sessionData")) {
1178 if (url.EqualsLiteral("about:sessionrestore") ||
1179 url.EqualsLiteral("about:welcomeback")) {
1180 JS::Rooted<JS::Value> object(
1181 cx, JS::ObjectValue(*entry.mValue.GetAsObject()));
1182 SetSessionData(cx, node, &object);
1183 continue;
1184 }
1185 }
1186 JS::Rooted<JS::Value> object(
1187 cx, JS::ObjectValue(*entry.mValue.GetAsObject()));
1188 SetElementAsObject(cx, node, object);
1189 }
1190 }
1191 }
1192
1193 if (aData.mXpath.WasPassed()) {
1194 for (auto& entry : aData.mXpath.Value().Entries()) {
1195 RefPtr<Element> node = FindNodeByXPath(aDocument, entry.mKey);
1196 if (node == nullptr) {
1197 continue;
1198 }
1199 if (entry.mValue.IsString()) {
1200 SetElementAsString(node, entry.mValue.GetAsString());
1201 } else if (entry.mValue.IsBoolean()) {
1202 SetElementAsBool(node, entry.mValue.GetAsBoolean());
1203 } else {
1204 JS::Rooted<JS::Value> object(
1205 aGlobal.Context(), JS::ObjectValue(*entry.mValue.GetAsObject()));
1206 SetElementAsObject(aGlobal.Context(), node, object);
1207 }
1208 }
1209 }
1210
1211 return true;
1212 }
1213
1214 MOZ_CAN_RUN_SCRIPT
RestoreFormEntry(Element * aNode,const FormEntryValue & aValue)1215 void RestoreFormEntry(Element* aNode, const FormEntryValue& aValue) {
1216 using Type = sessionstore::FormEntryValue::Type;
1217 switch (aValue.type()) {
1218 case Type::TCheckbox:
1219 SetElementAsBool(aNode, aValue.get_Checkbox().value());
1220 break;
1221 case Type::TTextField:
1222 SetElementAsString(aNode, aValue.get_TextField().value());
1223 break;
1224 case Type::TFileList: {
1225 if (RefPtr<HTMLInputElement> input = HTMLInputElement::FromNode(aNode);
1226 input && input->ControlType() == FormControlType::InputFile) {
1227 CollectedFileListValue value;
1228 value.mFileList = aValue.get_FileList().valueList().Clone();
1229 SetElementAsFiles(input, value);
1230 }
1231 break;
1232 }
1233 case Type::TSingleSelect: {
1234 if (RefPtr<HTMLSelectElement> select = HTMLSelectElement::FromNode(aNode);
1235 select && !select->Multiple()) {
1236 CollectedNonMultipleSelectValue value;
1237 value.mSelectedIndex = aValue.get_SingleSelect().index();
1238 value.mValue = aValue.get_SingleSelect().value();
1239 SetElementAsSelect(select, value);
1240 }
1241 break;
1242 }
1243 case Type::TMultipleSelect: {
1244 if (RefPtr<HTMLSelectElement> select = HTMLSelectElement::FromNode(aNode);
1245 select && select->Multiple()) {
1246 SetElementAsMultiSelect(select,
1247 aValue.get_MultipleSelect().valueList());
1248 }
1249 break;
1250 }
1251 default:
1252 MOZ_ASSERT_UNREACHABLE();
1253 }
1254 }
1255
1256 MOZ_CAN_RUN_SCRIPT
1257 /* static */
RestoreFormData(Document & aDocument,const nsString & aInnerHTML,const nsTArray<SessionStoreRestoreData::Entry> & aEntries)1258 void SessionStoreUtils::RestoreFormData(
1259 Document& aDocument, const nsString& aInnerHTML,
1260 const nsTArray<SessionStoreRestoreData::Entry>& aEntries) {
1261 if (!aInnerHTML.IsEmpty()) {
1262 SetInnerHTML(aDocument, aInnerHTML);
1263 }
1264
1265 for (const auto& entry : aEntries) {
1266 RefPtr<Element> node = entry.mIsXPath
1267 ? FindNodeByXPath(aDocument, entry.mData.id())
1268 : aDocument.GetElementById(entry.mData.id());
1269 if (node) {
1270 RestoreFormEntry(node, entry.mData.value());
1271 }
1272 }
1273 }
1274
1275 typedef void (*CollectorFunc)(JSContext* aCx, Document& aDocument,
1276 Nullable<CollectedData>& aRetVal);
1277
1278 /**
1279 * A function that will recursively call |CollectorFunc| to collect data for all
1280 * non-dynamic frames in the current frame/docShell tree.
1281 */
CollectFrameTreeData(JSContext * aCx,BrowsingContext * aBrowsingContext,Nullable<CollectedData> & aRetVal,CollectorFunc aFunc)1282 static void CollectFrameTreeData(JSContext* aCx,
1283 BrowsingContext* aBrowsingContext,
1284 Nullable<CollectedData>& aRetVal,
1285 CollectorFunc aFunc) {
1286 if (aBrowsingContext->CreatedDynamically()) {
1287 return;
1288 }
1289
1290 nsPIDOMWindowOuter* window = aBrowsingContext->GetDOMWindow();
1291 if (!window || !window->GetDocShell()) {
1292 return;
1293 }
1294
1295 Document* document = window->GetExtantDoc();
1296 if (!document) {
1297 return;
1298 }
1299
1300 /* Collect data from current frame */
1301 aFunc(aCx, *document, aRetVal);
1302
1303 /* Collect data from all child frame */
1304 nsTArray<JSObject*> childrenData;
1305 SequenceRooter<JSObject*> rooter(aCx, &childrenData);
1306 uint32_t trailingNullCounter = 0;
1307
1308 // This is not going to work for fission. Bug 1572084 for tracking it.
1309 for (auto& child : aBrowsingContext->Children()) {
1310 NullableRootedDictionary<CollectedData> data(aCx);
1311 CollectFrameTreeData(aCx, child, data, aFunc);
1312 if (data.IsNull()) {
1313 childrenData.AppendElement(nullptr);
1314 trailingNullCounter++;
1315 continue;
1316 }
1317 JS::Rooted<JS::Value> jsval(aCx);
1318 if (!ToJSValue(aCx, data.SetValue(), &jsval)) {
1319 JS_ClearPendingException(aCx);
1320 continue;
1321 }
1322 childrenData.AppendElement(&jsval.toObject());
1323 trailingNullCounter = 0;
1324 }
1325
1326 if (trailingNullCounter != childrenData.Length()) {
1327 childrenData.TruncateLength(childrenData.Length() - trailingNullCounter);
1328 aRetVal.SetValue().mChildren.Construct() = std::move(childrenData);
1329 }
1330 }
1331
CollectScrollPosition(const GlobalObject & aGlobal,WindowProxyHolder & aWindow,Nullable<CollectedData> & aRetVal)1332 /* static */ void SessionStoreUtils::CollectScrollPosition(
1333 const GlobalObject& aGlobal, WindowProxyHolder& aWindow,
1334 Nullable<CollectedData>& aRetVal) {
1335 CollectFrameTreeData(aGlobal.Context(), aWindow.get(), aRetVal,
1336 CollectCurrentScrollPosition);
1337 }
1338
CollectFormData(const GlobalObject & aGlobal,WindowProxyHolder & aWindow,Nullable<CollectedData> & aRetVal)1339 /* static */ void SessionStoreUtils::CollectFormData(
1340 const GlobalObject& aGlobal, WindowProxyHolder& aWindow,
1341 Nullable<CollectedData>& aRetVal) {
1342 CollectFrameTreeData(aGlobal.Context(), aWindow.get(), aRetVal,
1343 CollectCurrentFormData);
1344 }
1345
ComposeInputData(const nsTArray<CollectedInputDataValue> & aData,InputElementData & ret)1346 /* static */ void SessionStoreUtils::ComposeInputData(
1347 const nsTArray<CollectedInputDataValue>& aData, InputElementData& ret) {
1348 nsTArray<int> selectedIndex, valueIdx;
1349 nsTArray<nsString> id, selectVal, strVal, type;
1350 nsTArray<bool> boolVal;
1351
1352 for (const CollectedInputDataValue& data : aData) {
1353 id.AppendElement(data.id);
1354 type.AppendElement(data.type);
1355
1356 if (data.value.is<mozilla::dom::CollectedNonMultipleSelectValue>()) {
1357 valueIdx.AppendElement(selectVal.Length());
1358 selectedIndex.AppendElement(
1359 data.value.as<mozilla::dom::CollectedNonMultipleSelectValue>()
1360 .mSelectedIndex);
1361 selectVal.AppendElement(
1362 data.value.as<mozilla::dom::CollectedNonMultipleSelectValue>()
1363 .mValue);
1364 } else if (data.value.is<CopyableTArray<nsString>>()) {
1365 // The first valueIdx is "index of the first string value"
1366 valueIdx.AppendElement(strVal.Length());
1367 strVal.AppendElements(data.value.as<CopyableTArray<nsString>>());
1368 // The second valueIdx is "index of the last string value" + 1
1369 id.AppendElement(data.id);
1370 type.AppendElement(data.type);
1371 valueIdx.AppendElement(strVal.Length());
1372 } else if (data.value.is<nsString>()) {
1373 valueIdx.AppendElement(strVal.Length());
1374 strVal.AppendElement(data.value.as<nsString>());
1375 } else if (data.type.EqualsLiteral("bool")) {
1376 valueIdx.AppendElement(boolVal.Length());
1377 boolVal.AppendElement(data.value.as<bool>());
1378 }
1379 }
1380
1381 if (selectedIndex.Length() != 0) {
1382 ret.mSelectedIndex.Construct(std::move(selectedIndex));
1383 }
1384 if (valueIdx.Length() != 0) {
1385 ret.mValueIdx.Construct(std::move(valueIdx));
1386 }
1387 if (id.Length() != 0) {
1388 ret.mId.Construct(std::move(id));
1389 }
1390 if (selectVal.Length() != 0) {
1391 ret.mSelectVal.Construct(std::move(selectVal));
1392 }
1393 if (strVal.Length() != 0) {
1394 ret.mStrVal.Construct(std::move(strVal));
1395 }
1396 if (type.Length() != 0) {
1397 ret.mType.Construct(std::move(type));
1398 }
1399 if (boolVal.Length() != 0) {
1400 ret.mBoolVal.Construct(std::move(boolVal));
1401 }
1402 }
1403
1404 MOZ_CAN_RUN_SCRIPT
1405 already_AddRefed<nsISessionStoreRestoreData>
ConstructSessionStoreRestoreData(const GlobalObject & aGlobal)1406 SessionStoreUtils::ConstructSessionStoreRestoreData(
1407 const GlobalObject& aGlobal) {
1408 nsCOMPtr<nsISessionStoreRestoreData> data = new SessionStoreRestoreData();
1409 return data.forget();
1410 }
1411
1412 /* static */
1413 MOZ_CAN_RUN_SCRIPT
InitializeRestore(const GlobalObject & aGlobal,CanonicalBrowsingContext & aContext,nsISessionStoreRestoreData * aData,ErrorResult & aError)1414 already_AddRefed<Promise> SessionStoreUtils::InitializeRestore(
1415 const GlobalObject& aGlobal, CanonicalBrowsingContext& aContext,
1416 nsISessionStoreRestoreData* aData, ErrorResult& aError) {
1417 if (!mozilla::SessionHistoryInParent()) {
1418 MOZ_CRASH("why were we called?");
1419 }
1420
1421 MOZ_DIAGNOSTIC_ASSERT(aContext.IsTop());
1422
1423 MOZ_DIAGNOSTIC_ASSERT(aData);
1424 nsCOMPtr<SessionStoreRestoreData> data = do_QueryInterface(aData);
1425 aContext.SetRestoreData(data, aError);
1426 if (aError.Failed()) {
1427 return nullptr;
1428 }
1429
1430 MOZ_DIAGNOSTIC_ASSERT(aContext.GetSessionHistory());
1431 aContext.GetSessionHistory()->ReloadCurrentEntry();
1432
1433 return aContext.GetRestorePromise();
1434 }
1435
1436 /* static */
RestoreDocShellState(nsIDocShell * aDocShell,const DocShellRestoreState & aState)1437 void SessionStoreUtils::RestoreDocShellState(
1438 nsIDocShell* aDocShell, const DocShellRestoreState& aState) {
1439 if (aDocShell) {
1440 if (aState.URI()) {
1441 aDocShell->SetCurrentURI(aState.URI());
1442 }
1443 RestoreDocShellCapabilities(aDocShell, aState.docShellCaps());
1444 }
1445 }
1446
1447 /* static */
RestoreDocShellState(const GlobalObject & aGlobal,CanonicalBrowsingContext & aContext,const nsACString & aURL,const nsCString & aDocShellCaps,ErrorResult & aError)1448 already_AddRefed<Promise> SessionStoreUtils::RestoreDocShellState(
1449 const GlobalObject& aGlobal, CanonicalBrowsingContext& aContext,
1450 const nsACString& aURL, const nsCString& aDocShellCaps,
1451 ErrorResult& aError) {
1452 MOZ_RELEASE_ASSERT(mozilla::SessionHistoryInParent());
1453 MOZ_RELEASE_ASSERT(aContext.IsTop());
1454
1455 if (WindowGlobalParent* wgp = aContext.GetCurrentWindowGlobal()) {
1456 nsCOMPtr<nsIGlobalObject> global =
1457 do_QueryInterface(aGlobal.GetAsSupports());
1458 MOZ_DIAGNOSTIC_ASSERT(global);
1459
1460 RefPtr<Promise> promise = Promise::Create(global, aError);
1461 if (aError.Failed()) {
1462 return nullptr;
1463 }
1464
1465 nsCOMPtr<nsIURI> uri;
1466 if (!aURL.IsEmpty()) {
1467 if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), aURL))) {
1468 aError.Throw(NS_ERROR_FAILURE);
1469 return nullptr;
1470 }
1471 }
1472
1473 bool allowJavascript = true;
1474 for (const nsACString& token :
1475 nsCCharSeparatedTokenizer(aDocShellCaps, ',').ToRange()) {
1476 if (token.EqualsLiteral("Javascript")) {
1477 allowJavascript = false;
1478 }
1479 }
1480
1481 Unused << aContext.SetAllowJavascript(allowJavascript);
1482
1483 DocShellRestoreState state = {uri, aDocShellCaps};
1484
1485 // TODO (anny): Investigate removing this roundtrip.
1486 wgp->SendRestoreDocShellState(state)->Then(
1487 GetMainThreadSerialEventTarget(), __func__,
1488 [promise](void) { promise->MaybeResolveWithUndefined(); },
1489 [promise](void) { promise->MaybeRejectWithUndefined(); });
1490
1491 return promise.forget();
1492 }
1493
1494 return nullptr;
1495 }
1496
1497 /* static */
RestoreSessionStorageFromParent(const GlobalObject & aGlobal,const CanonicalBrowsingContext & aContext,const Record<nsCString,Record<nsString,nsString>> & aSessionStorage)1498 void SessionStoreUtils::RestoreSessionStorageFromParent(
1499 const GlobalObject& aGlobal, const CanonicalBrowsingContext& aContext,
1500 const Record<nsCString, Record<nsString, nsString>>& aSessionStorage) {
1501 nsTArray<SSCacheCopy> cacheInitList;
1502 for (const auto& originEntry : aSessionStorage.Entries()) {
1503 nsCOMPtr<nsIPrincipal> storagePrincipal =
1504 BasePrincipal::CreateContentPrincipal(originEntry.mKey);
1505
1506 nsCString originKey;
1507 nsresult rv = storagePrincipal->GetStorageOriginKey(originKey);
1508 if (NS_FAILED(rv)) {
1509 continue;
1510 }
1511
1512 SSCacheCopy& cacheInit = *cacheInitList.AppendElement();
1513
1514 cacheInit.originKey() = originKey;
1515 storagePrincipal->OriginAttributesRef().CreateSuffix(
1516 cacheInit.originAttributes());
1517
1518 for (const auto& entry : originEntry.mValue.Entries()) {
1519 SSSetItemInfo& setItemInfo = *cacheInit.data().AppendElement();
1520 setItemInfo.key() = entry.mKey;
1521 setItemInfo.value() = entry.mValue;
1522 }
1523 }
1524
1525 BackgroundSessionStorageManager::LoadData(aContext.Id(), cacheInitList);
1526 }
1527
1528 /* static */
ConstructFormDataValues(JSContext * aCx,const nsTArray<sessionstore::FormEntry> & aValues,nsTArray<Record<nsString,OwningStringOrBooleanOrObject>::EntryType> & aEntries,bool aParseSessionData)1529 nsresult SessionStoreUtils::ConstructFormDataValues(
1530 JSContext* aCx, const nsTArray<sessionstore::FormEntry>& aValues,
1531 nsTArray<Record<nsString, OwningStringOrBooleanOrObject>::EntryType>&
1532 aEntries,
1533 bool aParseSessionData) {
1534 using EntryType = Record<nsString, OwningStringOrBooleanOrObject>::EntryType;
1535
1536 if (!aEntries.SetCapacity(aValues.Length(), fallible)) {
1537 return NS_ERROR_FAILURE;
1538 }
1539
1540 for (const auto& value : aValues) {
1541 EntryType* entry = aEntries.AppendElement();
1542
1543 using Type = sessionstore::FormEntryValue::Type;
1544 switch (value.value().type()) {
1545 case Type::TCheckbox:
1546 entry->mValue.SetAsBoolean() = value.value().get_Checkbox().value();
1547 break;
1548 case Type::TTextField: {
1549 if (aParseSessionData && value.id() == u"sessionData"_ns) {
1550 JS::Rooted<JS::Value> jsval(aCx);
1551 const auto& fieldValue = value.value().get_TextField().value();
1552 if (!JS_ParseJSON(aCx, fieldValue.get(), fieldValue.Length(),
1553 &jsval) ||
1554 !jsval.isObject()) {
1555 return NS_ERROR_FAILURE;
1556 }
1557 entry->mValue.SetAsObject() = &jsval.toObject();
1558 } else {
1559 entry->mValue.SetAsString() = value.value().get_TextField().value();
1560 }
1561 break;
1562 }
1563 case Type::TFileList: {
1564 CollectedFileListValue file;
1565 file.mFileList = value.value().get_FileList().valueList().Clone();
1566
1567 JS::Rooted<JS::Value> jsval(aCx);
1568 if (!ToJSValue(aCx, file, &jsval) || !jsval.isObject()) {
1569 return NS_ERROR_FAILURE;
1570 }
1571 entry->mValue.SetAsObject() = &jsval.toObject();
1572 break;
1573 }
1574 case Type::TSingleSelect: {
1575 CollectedNonMultipleSelectValue select;
1576 select.mSelectedIndex = value.value().get_SingleSelect().index();
1577 select.mValue = value.value().get_SingleSelect().value();
1578
1579 JS::Rooted<JS::Value> jsval(aCx);
1580 if (!ToJSValue(aCx, select, &jsval) || !jsval.isObject()) {
1581 return NS_ERROR_FAILURE;
1582 }
1583 entry->mValue.SetAsObject() = &jsval.toObject();
1584 break;
1585 }
1586 case Type::TMultipleSelect: {
1587 JS::Rooted<JS::Value> jsval(aCx);
1588 if (!ToJSValue(aCx, value.value().get_MultipleSelect().valueList(),
1589 &jsval) ||
1590 !jsval.isObject()) {
1591 return NS_ERROR_FAILURE;
1592 }
1593 entry->mValue.SetAsObject() = &jsval.toObject();
1594 break;
1595 }
1596 default:
1597 break;
1598 }
1599
1600 entry->mKey = value.id();
1601 }
1602
1603 return NS_OK;
1604 }
1605
ConstructSessionStorageValue(const nsTArray<SSSetItemInfo> & aValues,Record<nsString,nsString> & aRecord)1606 static nsresult ConstructSessionStorageValue(
1607 const nsTArray<SSSetItemInfo>& aValues,
1608 Record<nsString, nsString>& aRecord) {
1609 auto& entries = aRecord.Entries();
1610 for (const auto& value : aValues) {
1611 auto entry = entries.AppendElement();
1612 entry->mKey = value.key();
1613 entry->mValue = value.value();
1614 }
1615
1616 return NS_OK;
1617 }
1618
1619 /* static */
ConstructSessionStorageValues(CanonicalBrowsingContext * aBrowsingContext,const nsTArray<SSCacheCopy> & aValues,Record<nsCString,Record<nsString,nsString>> & aRecord)1620 nsresult SessionStoreUtils::ConstructSessionStorageValues(
1621 CanonicalBrowsingContext* aBrowsingContext,
1622 const nsTArray<SSCacheCopy>& aValues,
1623 Record<nsCString, Record<nsString, nsString>>& aRecord) {
1624 if (!aRecord.Entries().SetCapacity(aValues.Length(), fallible)) {
1625 return NS_ERROR_FAILURE;
1626 }
1627
1628 // We wish to remove this step of mapping originAttributes+originKey
1629 // to a storage principal in Bug 1711886 by consolidating the
1630 // storage format in SessionStorageManagerBase and Session Store.
1631 nsTHashMap<nsCStringHashKey, nsIPrincipal*> storagePrincipalList;
1632 aBrowsingContext->PreOrderWalk([&storagePrincipalList](
1633 BrowsingContext* aContext) {
1634 WindowGlobalParent* windowParent =
1635 aContext->Canonical()->GetCurrentWindowGlobal();
1636 if (!windowParent) {
1637 return;
1638 }
1639
1640 nsIPrincipal* storagePrincipal = windowParent->DocumentStoragePrincipal();
1641 if (!storagePrincipal) {
1642 return;
1643 }
1644
1645 const OriginAttributes& originAttributes =
1646 storagePrincipal->OriginAttributesRef();
1647 nsAutoCString originAttributesSuffix;
1648 originAttributes.CreateSuffix(originAttributesSuffix);
1649
1650 nsAutoCString originKey;
1651 storagePrincipal->GetStorageOriginKey(originKey);
1652
1653 storagePrincipalList.InsertOrUpdate(originAttributesSuffix + originKey,
1654 storagePrincipal);
1655 });
1656
1657 for (const auto& value : aValues) {
1658 nsIPrincipal* storagePrincipal =
1659 storagePrincipalList.Get(value.originAttributes() + value.originKey());
1660 if (!storagePrincipal) {
1661 continue;
1662 }
1663
1664 auto entry = aRecord.Entries().AppendElement();
1665
1666 if (!entry->mValue.Entries().SetCapacity(value.data().Length(), fallible)) {
1667 return NS_ERROR_FAILURE;
1668 }
1669
1670 if (NS_FAILED(storagePrincipal->GetOrigin(entry->mKey))) {
1671 return NS_ERROR_FAILURE;
1672 }
1673
1674 ConstructSessionStorageValue(value.data(), entry->mValue);
1675 }
1676
1677 return NS_OK;
1678 }
1679
ResetSessionStore(BrowsingContext * aContext)1680 /* static */ void SessionStoreUtils::ResetSessionStore(
1681 BrowsingContext* aContext) {
1682 MOZ_RELEASE_ASSERT(NATIVE_LISTENER);
1683 WindowContext* windowContext = aContext->GetCurrentWindowContext();
1684 if (!windowContext) {
1685 return;
1686 }
1687
1688 WindowGlobalChild* windowChild = windowContext->GetWindowGlobalChild();
1689 if (!windowChild || !windowChild->CanSend()) {
1690 return;
1691 }
1692
1693 uint32_t epoch = aContext->GetSessionStoreEpoch();
1694
1695 Unused << windowChild->SendResetSessionStore(epoch);
1696 }
1697