NavigationPrecommitController.cpp (6789B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 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 "NavigationPrecommitController.h" 8 9 #include "Navigation.h" 10 #include "NavigationUtils.h" 11 #include "mozilla/dom/NavigateEvent.h" 12 #include "mozilla/dom/NavigationPrecommitControllerBinding.h" 13 #include "nsNetUtil.h" 14 15 namespace mozilla::dom { 16 17 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(NavigationPrecommitController, 18 mGlobalObject, mEvent) 19 NS_IMPL_CYCLE_COLLECTING_ADDREF(NavigationPrecommitController) 20 NS_IMPL_CYCLE_COLLECTING_RELEASE(NavigationPrecommitController) 21 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NavigationPrecommitController) 22 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 23 NS_INTERFACE_MAP_ENTRY(nsISupports) 24 NS_INTERFACE_MAP_END 25 26 NavigationPrecommitController::NavigationPrecommitController( 27 NavigateEvent* aEvent, nsIGlobalObject* aGlobalObject) 28 : mGlobalObject(aGlobalObject), mEvent(aEvent) { 29 MOZ_DIAGNOSTIC_ASSERT(mEvent); 30 } 31 32 NavigationPrecommitController::~NavigationPrecommitController() {} 33 34 JSObject* NavigationPrecommitController::WrapObject( 35 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { 36 return NavigationPrecommitController_Binding::Wrap(aCx, this, aGivenProto); 37 } 38 39 nsIGlobalObject* NavigationPrecommitController::GetParentObject() const { 40 return mGlobalObject; 41 } 42 43 // https://html.spec.whatwg.org/#dom-navigationprecommitcontroller-redirect 44 void NavigationPrecommitController::Redirect( 45 JSContext* aCx, const nsAString& aUrl, 46 const NavigationNavigateOptions& aOptions, ErrorResult& aRv) { 47 // The redirect(url, options) method steps are: 48 49 // 1. Assert: this's event's interception state is not "none". 50 MOZ_DIAGNOSTIC_ASSERT(mEvent->InterceptionState() != 51 NavigateEvent::InterceptionState::None); 52 53 // 2. Perform shared checks given this's event. 54 mEvent->PerformSharedChecks(aRv); 55 if (aRv.Failed()) { 56 return; 57 } 58 59 // 3. If this's event's interception state is not "intercepted", then throw an 60 // "InvalidStateError" DOMException. 61 if (mEvent->InterceptionState() != 62 NavigateEvent::InterceptionState::Intercepted) { 63 aRv.ThrowInvalidStateError( 64 "Expected interception state to be 'intercepted'"); 65 return; 66 } 67 68 // 4. If this's event's navigationType is neither "push" nor "replace", then 69 // throw an "InvalidStateError" DOMException. 70 if (mEvent->NavigationType() != NavigationType::Push && 71 mEvent->NavigationType() != NavigationType::Replace) { 72 aRv.ThrowInvalidStateError( 73 "Expected navigation type to be 'push' or 'replace'"); 74 return; 75 } 76 77 // 5. Let document be this's relevant global object's associated Document. 78 RefPtr<Document> document = mEvent->GetDocument(); 79 if (!document) { 80 aRv.ThrowInvalidStateError("Document is not available"); 81 return; 82 } 83 // 6. Let destinationURL be the result of parsing url given document. 84 RefPtr<nsIURI> destinationURL; 85 nsresult res = NS_NewURI(getter_AddRefs(destinationURL), aUrl, nullptr, 86 document->GetDocBaseURI()); 87 88 // 7. If destinationURL is failure, then throw a "SyntaxError" DOMException. 89 if (NS_FAILED(res)) { 90 aRv.ThrowSyntaxError("URL given to navigate() is invalid"); 91 return; 92 } 93 94 // 8. If document cannot have its URL rewritten to destinationURL, then throw 95 // a "SecurityError" DOMException. 96 if (!document->CanRewriteURL(destinationURL)) { 97 aRv.ThrowSecurityError("Cannot rewrite URL to the given destination URL"); 98 return; 99 } 100 101 // 9. If options["history"] is "push" or "replace", then set this's event's 102 // navigationType to options["history"]. 103 if (aOptions.mHistory == NavigationHistoryBehavior::Push || 104 aOptions.mHistory == NavigationHistoryBehavior::Replace) { 105 mEvent->SetNavigationType( 106 *NavigationUtils::NavigationTypeFromNavigationHistoryBehavior( 107 aOptions.mHistory)); 108 } 109 RefPtr destination = mEvent->Destination(); 110 111 // 10. If options["state"] exists, then: 112 if (!aOptions.mState.isUndefined()) { 113 // 10.1 Let serializedState be the result of calling 114 // StructuredSerializeForStorage(options["state"]). This may throw an 115 // exception. 116 RefPtr<nsIStructuredCloneContainer> serializedState = 117 new nsStructuredCloneContainer(); 118 JS::Rooted<JS::Value> state(aCx, aOptions.mState); 119 120 const nsresult rv = serializedState->InitFromJSVal(state, aCx); 121 if (NS_FAILED(rv)) { 122 JS::Rooted<JS::Value> exception(aCx); 123 if (JS_GetPendingException(aCx, &exception)) { 124 JS_ClearPendingException(aCx); 125 aRv.ThrowJSException(aCx, exception); 126 } else { 127 // The spec only mentions that this might throw, but the tests expect 128 // the DataCloneError. 129 aRv.ThrowDataCloneError("Failed to serialize value for redirect()."); 130 } 131 return; 132 } 133 134 // 10.2 Set this's event's destination's state to serializedState. 135 destination->SetState(serializedState); 136 137 // 10.3 Set this's event's target's ongoing API method tracker's serialized 138 // state to serializedState. 139 if (Navigation* target = 140 Navigation::FromEventTargetOrNull(mEvent->GetTarget())) { 141 target->SetSerializedStateIntoOngoingAPIMethodTracker(serializedState); 142 } 143 } 144 145 // 11. Set this's event's destination's URL to destinationURL. 146 destination->SetURL(destinationURL); 147 // 12. If options["info"] exists, then set this's event's info to 148 // options["info"]. 149 if (!aOptions.mInfo.isUndefined()) { 150 mEvent->SetInfo(aOptions.mInfo); 151 } 152 } 153 154 // https://html.spec.whatwg.org/#dom-navigationprecommitcontroller-addhandler 155 void NavigationPrecommitController::AddHandler( 156 NavigationInterceptHandler& aHandler, ErrorResult& aRv) { 157 // 1. Assert: this's event's interception state is not "none". 158 MOZ_DIAGNOSTIC_ASSERT(mEvent->InterceptionState() != 159 NavigateEvent::InterceptionState::None); 160 161 // 2. Perform shared checks given this's event. 162 mEvent->PerformSharedChecks(aRv); 163 if (aRv.Failed()) { 164 return; 165 } 166 167 // 3. If this's event's interception state is not "intercepted", then throw 168 // an "InvalidStateError" DOMException. 169 if (mEvent->InterceptionState() != 170 NavigateEvent::InterceptionState::Intercepted) { 171 aRv.ThrowInvalidStateError( 172 "Cannot add handler after navigation has committed"); 173 return; 174 } 175 176 // 4. Append handler to this's event's navigation handler list. 177 mEvent->NavigationHandlerList().AppendElement(&aHandler); 178 } 179 180 } // namespace mozilla::dom