| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/renderer_host/navigation_throttle_runner.h" |
| |
| #include "content/browser/devtools/devtools_instrumentation.h" |
| #include "content/browser/portal/portal_navigation_throttle.h" |
| #include "content/browser/renderer_host/ancestor_throttle.h" |
| #include "content/browser/renderer_host/blocked_scheme_navigation_throttle.h" |
| #include "content/browser/renderer_host/form_submission_throttle.h" |
| #include "content/browser/renderer_host/mixed_content_navigation_throttle.h" |
| #include "content/browser/renderer_host/navigation_request.h" |
| #include "content/browser/renderer_host/navigator_delegate.h" |
| #include "content/browser/renderer_host/origin_policy_throttle.h" |
| #include "content/public/browser/navigation_handle.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| NavigationThrottle::ThrottleCheckResult ExecuteNavigationEvent( |
| NavigationThrottle* throttle, |
| NavigationThrottleRunner::Event event) { |
| switch (event) { |
| case NavigationThrottleRunner::Event::WillStartRequest: |
| return throttle->WillStartRequest(); |
| case NavigationThrottleRunner::Event::WillRedirectRequest: |
| return throttle->WillRedirectRequest(); |
| case NavigationThrottleRunner::Event::WillFailRequest: |
| return throttle->WillFailRequest(); |
| case NavigationThrottleRunner::Event::WillProcessResponse: |
| return throttle->WillProcessResponse(); |
| default: |
| NOTREACHED(); |
| } |
| NOTREACHED(); |
| return NavigationThrottle::CANCEL_AND_IGNORE; |
| } |
| |
| const char* GetEventName(NavigationThrottleRunner::Event event) { |
| switch (event) { |
| case NavigationThrottleRunner::Event::WillStartRequest: |
| return "NavigationThrottle::WillStartRequest"; |
| case NavigationThrottleRunner::Event::WillRedirectRequest: |
| return "NavigationThrottle::WillRedirectRequest"; |
| case NavigationThrottleRunner::Event::WillFailRequest: |
| return "NavigationThrottle::WillFailRequest"; |
| case NavigationThrottleRunner::Event::WillProcessResponse: |
| return "NavigationThrottle::WillProcessResponse"; |
| default: |
| NOTREACHED(); |
| } |
| return ""; |
| } |
| |
| } // namespace |
| |
| NavigationThrottleRunner::NavigationThrottleRunner(Delegate* delegate, |
| int64_t navigation_id) |
| : delegate_(delegate), navigation_id_(navigation_id) {} |
| |
| NavigationThrottleRunner::~NavigationThrottleRunner() = default; |
| |
| void NavigationThrottleRunner::ProcessNavigationEvent(Event event) { |
| DCHECK_NE(Event::NoEvent, event); |
| current_event_ = event; |
| next_index_ = 0; |
| ProcessInternal(); |
| } |
| |
| void NavigationThrottleRunner::ResumeProcessingNavigationEvent( |
| NavigationThrottle* deferring_throttle) { |
| DCHECK_EQ(GetDeferringThrottle(), deferring_throttle); |
| ProcessInternal(); |
| } |
| |
| void NavigationThrottleRunner::CallResumeForTesting() { |
| ProcessInternal(); |
| } |
| |
| void NavigationThrottleRunner::RegisterNavigationThrottles() { |
| // Note: |throttle_| might not be empty. Some NavigationThrottles might have |
| // been registered with RegisterThrottleForTesting. These must reside at the |
| // end of |throttles_|. TestNavigationManagerThrottle expects that the |
| // NavigationThrottles added for test are the last NavigationThrottles to |
| // execute. Take them out while appending the rest of the |
| // NavigationThrottles. |
| std::vector<std::unique_ptr<NavigationThrottle>> testing_throttles = |
| std::move(throttles_); |
| |
| // The NavigationRequest associated with the NavigationThrottles this |
| // NavigationThrottleRunner manages. |
| // Unit tests that do not use NavigationRequest should never call |
| // RegisterNavigationThrottles as this function expects |delegate_| to be a |
| // NavigationRequest. |
| NavigationRequest* request = static_cast<NavigationRequest*>(delegate_); |
| |
| throttles_ = request->GetDelegate()->CreateThrottlesForNavigation(request); |
| |
| // Check for renderer-inititated main frame navigations to blocked URL schemes |
| // (data, filesystem). This is done early as it may block the main frame |
| // navigation altogether. |
| AddThrottle( |
| BlockedSchemeNavigationThrottle::CreateThrottleForNavigation(request)); |
| |
| AddThrottle(AncestorThrottle::MaybeCreateThrottleFor(request)); |
| AddThrottle(FormSubmissionThrottle::MaybeCreateThrottleFor(request)); |
| |
| // Check for mixed content. This is done after the AncestorThrottle and the |
| // FormSubmissionThrottle so that when folks block mixed content with a CSP |
| // policy, they don't get a warning. They'll still get a warning in the |
| // console about CSP blocking the load. |
| AddThrottle( |
| MixedContentNavigationThrottle::CreateThrottleForNavigation(request)); |
| |
| // Handle Origin Policy (if enabled) |
| AddThrottle(OriginPolicyThrottle::MaybeCreateThrottleFor(request)); |
| |
| // Block certain requests that are not permitted for portals. |
| AddThrottle(PortalNavigationThrottle::MaybeCreateThrottleFor(request)); |
| |
| for (auto& throttle : |
| devtools_instrumentation::CreateNavigationThrottles(request)) { |
| AddThrottle(std::move(throttle)); |
| } |
| |
| // Insert all testing NavigationThrottles last. |
| throttles_.insert(throttles_.end(), |
| std::make_move_iterator(testing_throttles.begin()), |
| std::make_move_iterator(testing_throttles.end())); |
| } |
| |
| NavigationThrottle* NavigationThrottleRunner::GetDeferringThrottle() const { |
| if (next_index_ == 0) |
| return nullptr; |
| return throttles_[next_index_ - 1].get(); |
| } |
| |
| void NavigationThrottleRunner::AddThrottle( |
| std::unique_ptr<NavigationThrottle> navigation_throttle) { |
| if (navigation_throttle) |
| throttles_.push_back(std::move(navigation_throttle)); |
| } |
| |
| void NavigationThrottleRunner::ProcessInternal() { |
| DCHECK_NE(Event::NoEvent, current_event_); |
| base::WeakPtr<NavigationThrottleRunner> weak_ref = weak_factory_.GetWeakPtr(); |
| |
| // Capture into a local variable the |navigation_id_| value, since this |
| // object can be freed by any of the throttles being invoked and the trace |
| // events need to be able to use the navigation id safely in such a case. |
| int64_t local_navigation_id = navigation_id_; |
| |
| for (size_t i = next_index_; i < throttles_.size(); ++i) { |
| TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( |
| "navigation", GetEventName(current_event_), local_navigation_id, |
| "throttle", throttles_[i]->GetNameForLogging()); |
| |
| NavigationThrottle::ThrottleCheckResult result = |
| ExecuteNavigationEvent(throttles_[i].get(), current_event_); |
| if (!weak_ref) { |
| // The NavigationThrottle execution has destroyed this |
| // NavigationThrottleRunner. Return immediately. |
| TRACE_EVENT_NESTABLE_ASYNC_END1("navigation", "", local_navigation_id, |
| "result", "deleted"); |
| return; |
| } |
| TRACE_EVENT_NESTABLE_ASYNC_END1("navigation", GetEventName(current_event_), |
| local_navigation_id, "result", |
| result.action()); |
| |
| switch (result.action()) { |
| case NavigationThrottle::PROCEED: |
| continue; |
| |
| case NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE: |
| case NavigationThrottle::BLOCK_REQUEST: |
| case NavigationThrottle::BLOCK_RESPONSE: |
| case NavigationThrottle::CANCEL: |
| case NavigationThrottle::CANCEL_AND_IGNORE: |
| next_index_ = 0; |
| InformDelegate(result); |
| return; |
| |
| case NavigationThrottle::DEFER: |
| next_index_ = i + 1; |
| return; |
| } |
| } |
| |
| next_index_ = 0; |
| InformDelegate(NavigationThrottle::PROCEED); |
| } |
| |
| void NavigationThrottleRunner::InformDelegate( |
| const NavigationThrottle::ThrottleCheckResult& result) { |
| // Now that the event has executed, reset the current event to NoEvent since |
| // we're no longer processing any event. Do it before the call to the |
| // delegate, as it might lead to the deletion of this |
| // NavigationThrottleRunner. |
| Event event = current_event_; |
| current_event_ = Event::NoEvent; |
| delegate_->OnNavigationEventProcessed(event, result); |
| // DO NOT ADD CODE AFTER THIS. The NavigationThrottleRunner might have been |
| // deleted by the previous call. |
| } |
| |
| } // namespace content |