text: // Copyright 2013 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 "components/blocked_content/popup_blocker_tab_helper.h"
  
  #include <iterator>
  #include <string>
  
  #include "base/metrics/histogram_macros.h"
  #include "build/build_config.h"
  #include "components/blocked_content/list_item_position.h"
  #include "components/blocked_content/popup_navigation_delegate.h"
  #include "components/blocked_content/popup_tracker.h"
  #include "components/blocked_content/safe_browsing_triggered_popup_blocker.h"
  #include "components/content_settings/browser/page_specific_content_settings.h"
  #include "content/public/browser/back_forward_cache.h"
  #include "content/public/browser/navigation_controller.h"
  #include "content/public/browser/navigation_handle.h"
  #include "content/public/browser/page_navigator.h"
  #include "content/public/browser/render_frame_host.h"
  #include "content/public/browser/render_view_host.h"
  #include "content/public/browser/web_contents.h"
  #include "third_party/blink/public/mojom/window_features/window_features.mojom.h"
  
  namespace blocked_content {
  const size_t kMaximumNumberOfPopups = 25;
  
  struct PopupBlockerTabHelper::BlockedRequest {
    BlockedRequest(std::unique_ptr<PopupNavigationDelegate> delegate,
                   const blink::mojom::WindowFeatures& window_features,
                   PopupBlockType block_type)
        : delegate(std::move(delegate)),
          window_features(window_features),
          block_type(block_type) {}
  
    std::unique_ptr<PopupNavigationDelegate> delegate;
    blink::mojom::WindowFeatures window_features;
    PopupBlockType block_type;
  };
  
  PopupBlockerTabHelper::PopupBlockerTabHelper(content::WebContents* web_contents)
      : content::WebContentsObserver(web_contents) {
    blocked_content::SafeBrowsingTriggeredPopupBlocker::MaybeCreate(web_contents);
  }
  
  PopupBlockerTabHelper::~PopupBlockerTabHelper() = default;
  
  void PopupBlockerTabHelper::DidFinishNavigation(
      content::NavigationHandle* navigation_handle) {
    // Clear all page actions, blocked content notifications and browser actions
    // for this tab, unless this is an same-document navigation. Also only
    // consider main frame navigations that successfully committed.
    if (!navigation_handle->IsInMainFrame() ||
        !navigation_handle->HasCommitted() ||
        navigation_handle->IsSameDocument()) {
      return;
    }
  
    // Close blocked popups.
    if (!blocked_popups_.empty()) {
      blocked_popups_.clear();
      HidePopupNotification();
  
      // With back-forward cache we can restore the page, but |blocked_popups_|
      // are lost here and can't be restored at the moment.
      // Disable bfcache here to avoid potential loss of the page state.
      web_contents()
          ->GetController()
          .GetBackForwardCache()
          .DisableForRenderFrameHost(
              navigation_handle->GetPreviousRenderFrameHostId(),
              "PopupBlockerTabHelper");
    }
  }
  
  void PopupBlockerTabHelper::HidePopupNotification() {
    auto* pscs = content_settings::PageSpecificContentSettings::GetForFrame(
        web_contents()->GetMainFrame());
    if (pscs)
      pscs->ClearPopupsBlocked();
  }
  
  void PopupBlockerTabHelper::AddBlockedPopup(
      std::unique_ptr<PopupNavigationDelegate> delegate,
      const blink::mojom::WindowFeatures& window_features,
      PopupBlockType block_type) {
    LogAction(Action::kBlocked);
    if (blocked_popups_.size() >= kMaximumNumberOfPopups)
      return;
  
    int id = next_id_;
    next_id_++;
    blocked_popups_[id] = std::make_unique<BlockedRequest>(
        std::move(delegate), window_features, block_type);
  
    auto* content_settings =
        content_settings::PageSpecificContentSettings::GetForFrame(
            web_contents()->GetMainFrame());
    if (content_settings) {
      content_settings->OnContentBlocked(ContentSettingsType::POPUPS);
    }
    auto* raw_delegate = blocked_popups_[id]->delegate.get();
  
    manager_.NotifyObservers(id, raw_delegate->GetURL());
  
    raw_delegate->OnPopupBlocked(web_contents(), GetBlockedPopupsCount());
  }
  
  void PopupBlockerTabHelper::ShowBlockedPopup(
      int32_t id,
      WindowOpenDisposition disposition) {
    auto it = blocked_popups_.find(id);
    if (it == blocked_popups_.end())
      return;
  
    blocked_content::ListItemPosition position =
        blocked_content::GetListItemPositionFromDistance(
            std::distance(blocked_popups_.begin(), it), blocked_popups_.size());
  
    UMA_HISTOGRAM_ENUMERATION("ContentSettings.Popups.ClickThroughPosition",
                              position);
  
    BlockedRequest* popup = it->second.get();
  
    base::Optional<WindowOpenDisposition> updated_disposition;
    if (disposition != WindowOpenDisposition::CURRENT_TAB)
      updated_disposition = disposition;
  
    PopupNavigationDelegate::NavigateResult result =
        popup->delegate->NavigateWithGesture(popup->window_features,
                                             updated_disposition);
    if (result.navigated_or_inserted_contents) {
      auto* tracker = blocked_content::PopupTracker::CreateForWebContents(
          result.navigated_or_inserted_contents, web_contents(),
          result.disposition);
      tracker->set_is_trusted(true);
    }
  
    switch (popup->block_type) {
      case PopupBlockType::kNotBlocked:
        NOTREACHED();
        break;
      case PopupBlockType::kNoGesture:
        LogAction(Action::kClickedThroughNoGesture);
        break;
      case PopupBlockType::kAbusive:
        LogAction(Action::kClickedThroughAbusive);
        break;
    }
  
    blocked_popups_.erase(id);
    if (blocked_popups_.empty())
      HidePopupNotification();
  }
  
  size_t PopupBlockerTabHelper::GetBlockedPopupsCount() const {
    return blocked_popups_.size();
  }
  
  PopupBlockerTabHelper::PopupIdMap
  PopupBlockerTabHelper::GetBlockedPopupRequests() {
    PopupIdMap result;
    for (const auto& it : blocked_popups_) {
      result[it.first] = it.second->delegate->GetURL();
    }
    return result;
  }
  
  // static
  void PopupBlockerTabHelper::LogAction(Action action) {
    UMA_HISTOGRAM_ENUMERATION("ContentSettings.Popups.BlockerActions", action);
  }
  
  WEB_CONTENTS_USER_DATA_KEY_IMPL(PopupBlockerTabHelper)
  
  }  // namespace blocked_content
  