Bug 1845134 - Part 4: Update existing ui-icons to use the latest source from acorn...
[gecko.git] / netwerk / base / nsAsyncRedirectVerifyHelper.cpp
blob7a10d4d3f0b6ba5e2bafe9e21c5f36b597c438a5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/Logging.h"
7 #include "mozilla/SpinEventLoopUntil.h"
8 #include "nsAsyncRedirectVerifyHelper.h"
9 #include "nsThreadUtils.h"
10 #include "nsNetUtil.h"
12 #include "nsIOService.h"
13 #include "nsIChannel.h"
14 #include "nsIHttpChannelInternal.h"
15 #include "nsIAsyncVerifyRedirectCallback.h"
16 #include "nsILoadInfo.h"
18 namespace mozilla {
19 namespace net {
21 static LazyLogModule gRedirectLog("nsRedirect");
22 #undef LOG
23 #define LOG(args) MOZ_LOG(gRedirectLog, LogLevel::Debug, args)
25 NS_IMPL_ISUPPORTS(nsAsyncRedirectVerifyHelper, nsIAsyncVerifyRedirectCallback,
26 nsIRunnable, nsINamed)
28 class nsAsyncVerifyRedirectCallbackEvent : public Runnable {
29 public:
30 nsAsyncVerifyRedirectCallbackEvent(nsIAsyncVerifyRedirectCallback* cb,
31 nsresult result)
32 : Runnable("nsAsyncVerifyRedirectCallbackEvent"),
33 mCallback(cb),
34 mResult(result) {}
36 NS_IMETHOD Run() override {
37 LOG(
38 ("nsAsyncVerifyRedirectCallbackEvent::Run() "
39 "callback to %p with result %" PRIx32,
40 mCallback.get(), static_cast<uint32_t>(mResult)));
41 (void)mCallback->OnRedirectVerifyCallback(mResult);
42 return NS_OK;
45 private:
46 nsCOMPtr<nsIAsyncVerifyRedirectCallback> mCallback;
47 nsresult mResult;
50 nsAsyncRedirectVerifyHelper::~nsAsyncRedirectVerifyHelper() {
51 NS_ASSERTION(NS_FAILED(mResult) || mExpectedCallbacks == 0,
52 "Did not receive all required callbacks!");
55 nsresult nsAsyncRedirectVerifyHelper::Init(
56 nsIChannel* oldChan, nsIChannel* newChan, uint32_t flags,
57 nsIEventTarget* mainThreadEventTarget, bool synchronize) {
58 LOG(
59 ("nsAsyncRedirectVerifyHelper::Init() "
60 "oldChan=%p newChan=%p",
61 oldChan, newChan));
62 mOldChan = oldChan;
63 mNewChan = newChan;
64 mFlags = flags;
65 mCallbackEventTarget = NS_IsMainThread() && mainThreadEventTarget
66 ? mainThreadEventTarget
67 : GetCurrentSerialEventTarget();
69 if (!(flags & (nsIChannelEventSink::REDIRECT_INTERNAL |
70 nsIChannelEventSink::REDIRECT_STS_UPGRADE))) {
71 nsCOMPtr<nsILoadInfo> loadInfo = oldChan->LoadInfo();
72 if (loadInfo->GetDontFollowRedirects()) {
73 ExplicitCallback(NS_BINDING_ABORTED);
74 return NS_OK;
78 if (synchronize) mWaitingForRedirectCallback = true;
80 nsCOMPtr<nsIRunnable> runnable = this;
81 nsresult rv;
82 rv = mainThreadEventTarget
83 ? mainThreadEventTarget->Dispatch(runnable.forget())
84 : GetMainThreadSerialEventTarget()->Dispatch(runnable.forget());
85 NS_ENSURE_SUCCESS(rv, rv);
87 if (synchronize) {
88 if (!SpinEventLoopUntil("nsAsyncRedirectVerifyHelper::Init"_ns,
89 [&]() { return !mWaitingForRedirectCallback; })) {
90 return NS_ERROR_UNEXPECTED;
94 return NS_OK;
97 NS_IMETHODIMP
98 nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback(nsresult result) {
99 LOG(
100 ("nsAsyncRedirectVerifyHelper::OnRedirectVerifyCallback() "
101 "result=%" PRIx32 " expectedCBs=%u mResult=%" PRIx32,
102 static_cast<uint32_t>(result), mExpectedCallbacks,
103 static_cast<uint32_t>(mResult)));
105 MOZ_DIAGNOSTIC_ASSERT(
106 mExpectedCallbacks > 0,
107 "OnRedirectVerifyCallback called more times than expected");
108 if (mExpectedCallbacks <= 0) {
109 return NS_ERROR_UNEXPECTED;
112 --mExpectedCallbacks;
114 // If response indicates failure we may call back immediately
115 if (NS_FAILED(result)) {
116 // We chose to store the first failure-value (as opposed to the last)
117 if (NS_SUCCEEDED(mResult)) mResult = result;
119 // If InitCallback() has been called, just invoke the callback and
120 // return. Otherwise it will be invoked from InitCallback()
121 if (mCallbackInitiated) {
122 ExplicitCallback(mResult);
123 return NS_OK;
127 // If the expected-counter is in balance and InitCallback() was called, all
128 // sinks have agreed that the redirect is ok and we can invoke our callback
129 if (mCallbackInitiated && mExpectedCallbacks == 0) {
130 ExplicitCallback(mResult);
133 return NS_OK;
136 nsresult nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect(
137 nsIChannelEventSink* sink, nsIChannel* oldChannel, nsIChannel* newChannel,
138 uint32_t flags) {
139 LOG(
140 ("nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect() "
141 "sink=%p expectedCBs=%u mResult=%" PRIx32,
142 sink, mExpectedCallbacks, static_cast<uint32_t>(mResult)));
144 ++mExpectedCallbacks;
146 if (IsOldChannelCanceled()) {
147 LOG(
148 (" old channel has been canceled, cancel the redirect by "
149 "emulating OnRedirectVerifyCallback..."));
150 (void)OnRedirectVerifyCallback(NS_BINDING_ABORTED);
151 return NS_BINDING_ABORTED;
154 nsresult rv =
155 sink->AsyncOnChannelRedirect(oldChannel, newChannel, flags, this);
157 LOG((" result=%" PRIx32 " expectedCBs=%u", static_cast<uint32_t>(rv),
158 mExpectedCallbacks));
160 // If the sink returns failure from this call the redirect is vetoed. We
161 // emulate a callback from the sink in this case in order to perform all
162 // the necessary logic.
163 if (NS_FAILED(rv)) {
164 LOG((" emulating OnRedirectVerifyCallback..."));
165 (void)OnRedirectVerifyCallback(rv);
168 return rv; // Return the actual status since our caller may need it
171 void nsAsyncRedirectVerifyHelper::ExplicitCallback(nsresult result) {
172 LOG(
173 ("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
174 "result=%" PRIx32
175 " expectedCBs=%u mCallbackInitiated=%u mResult=%" PRIx32,
176 static_cast<uint32_t>(result), mExpectedCallbacks, mCallbackInitiated,
177 static_cast<uint32_t>(mResult)));
179 nsCOMPtr<nsIAsyncVerifyRedirectCallback> callback(
180 do_QueryInterface(mOldChan));
182 if (!callback || !mCallbackEventTarget) {
183 LOG(
184 ("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
185 "callback=%p mCallbackEventTarget=%p",
186 callback.get(), mCallbackEventTarget.get()));
187 return;
190 mCallbackInitiated = false; // reset to ensure only one callback
191 mWaitingForRedirectCallback = false;
193 // Now, dispatch the callback on the event-target which called Init()
194 nsCOMPtr<nsIRunnable> event =
195 new nsAsyncVerifyRedirectCallbackEvent(callback, result);
196 if (!event) {
197 NS_WARNING(
198 "nsAsyncRedirectVerifyHelper::ExplicitCallback() "
199 "failed creating callback event!");
200 return;
202 nsresult rv = mCallbackEventTarget->Dispatch(event, NS_DISPATCH_NORMAL);
203 if (NS_FAILED(rv)) {
204 NS_WARNING(
205 "nsAsyncRedirectVerifyHelper::ExplicitCallback() "
206 "failed dispatching callback event!");
207 } else {
208 LOG(
209 ("nsAsyncRedirectVerifyHelper::ExplicitCallback() "
210 "dispatched callback event=%p",
211 event.get()));
215 void nsAsyncRedirectVerifyHelper::InitCallback() {
216 LOG(
217 ("nsAsyncRedirectVerifyHelper::InitCallback() "
218 "expectedCBs=%d mResult=%" PRIx32,
219 mExpectedCallbacks, static_cast<uint32_t>(mResult)));
221 mCallbackInitiated = true;
223 // Invoke the callback if we are done
224 if (mExpectedCallbacks == 0) ExplicitCallback(mResult);
227 NS_IMETHODIMP
228 nsAsyncRedirectVerifyHelper::GetName(nsACString& aName) {
229 aName.AssignLiteral("nsAsyncRedirectVerifyHelper");
230 return NS_OK;
233 NS_IMETHODIMP
234 nsAsyncRedirectVerifyHelper::Run() {
235 /* If the channel got canceled after it fired AsyncOnChannelRedirect
236 * and before we got here, mostly because docloader load has been canceled,
237 * we must completely ignore this notification and prevent any further
238 * notification.
240 if (IsOldChannelCanceled()) {
241 ExplicitCallback(NS_BINDING_ABORTED);
242 return NS_OK;
245 // First, the global observer
246 NS_ASSERTION(gIOService, "Must have an IO service at this point");
247 LOG(("nsAsyncRedirectVerifyHelper::Run() calling gIOService..."));
248 nsresult rv =
249 gIOService->AsyncOnChannelRedirect(mOldChan, mNewChan, mFlags, this);
250 if (NS_FAILED(rv)) {
251 ExplicitCallback(rv);
252 return NS_OK;
255 // Now, the per-channel observers
256 nsCOMPtr<nsIChannelEventSink> sink;
257 NS_QueryNotificationCallbacks(mOldChan, sink);
258 if (sink) {
259 LOG(("nsAsyncRedirectVerifyHelper::Run() calling sink..."));
260 rv = DelegateOnChannelRedirect(sink, mOldChan, mNewChan, mFlags);
263 // All invocations to AsyncOnChannelRedirect has been done - call
264 // InitCallback() to flag this
265 InitCallback();
266 return NS_OK;
269 bool nsAsyncRedirectVerifyHelper::IsOldChannelCanceled() {
270 if (!mOldChan) {
271 return false;
273 bool canceled;
274 nsresult rv = mOldChan->GetCanceled(&canceled);
275 return NS_SUCCEEDED(rv) && canceled;
278 } // namespace net
279 } // namespace mozilla