1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
6
7#include "chrome/browser/chrome_notification_types.h"
8#include "chrome/browser/content_settings/host_content_settings_map.h"
9#include "chrome/browser/content_settings/tab_specific_content_settings.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/browser/ui/blocked_content/blocked_window_params.h"
12#include "chrome/browser/ui/browser_navigator.h"
13#include "chrome/browser/ui/tabs/tab_strip_model.h"
14#include "chrome/common/render_messages.h"
15#include "content/public/browser/navigation_controller.h"
16#include "content/public/browser/navigation_details.h"
17#include "content/public/browser/navigation_entry.h"
18#include "content/public/browser/render_view_host.h"
19#include "content/public/browser/web_contents.h"
20#include "content/public/browser/web_contents_delegate.h"
21#include "third_party/WebKit/public/web/WebWindowFeatures.h"
22
23#if defined(OS_ANDROID)
24#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
25#endif
26
27using blink::WebWindowFeatures;
28
29const size_t kMaximumNumberOfPopups = 25;
30
31DEFINE_WEB_CONTENTS_USER_DATA_KEY(PopupBlockerTabHelper);
32
33struct PopupBlockerTabHelper::BlockedRequest {
34  BlockedRequest(const chrome::NavigateParams& params,
35                 const WebWindowFeatures& window_features)
36      : params(params), window_features(window_features) {}
37
38  chrome::NavigateParams params;
39  WebWindowFeatures window_features;
40};
41
42PopupBlockerTabHelper::PopupBlockerTabHelper(
43    content::WebContents* web_contents)
44    : content::WebContentsObserver(web_contents) {
45}
46
47PopupBlockerTabHelper::~PopupBlockerTabHelper() {
48}
49
50void PopupBlockerTabHelper::DidNavigateMainFrame(
51    const content::LoadCommittedDetails& details,
52    const content::FrameNavigateParams& params) {
53  // Clear all page actions, blocked content notifications and browser actions
54  // for this tab, unless this is an in-page navigation.
55  if (details.is_in_page)
56    return;
57
58  // Close blocked popups.
59  if (!blocked_popups_.IsEmpty()) {
60    blocked_popups_.Clear();
61    PopupNotificationVisibilityChanged(false);
62  }
63}
64
65void PopupBlockerTabHelper::PopupNotificationVisibilityChanged(
66    bool visible) {
67  if (!web_contents()->IsBeingDestroyed()) {
68    TabSpecificContentSettings::FromWebContents(web_contents())->
69        SetPopupsBlocked(visible);
70  }
71}
72
73bool PopupBlockerTabHelper::MaybeBlockPopup(
74    const chrome::NavigateParams& params,
75    const WebWindowFeatures& window_features) {
76  // A page can't spawn popups (or do anything else, either) until its load
77  // commits, so when we reach here, the popup was spawned by the
78  // NavigationController's last committed entry, not the active entry.  For
79  // example, if a page opens a popup in an onunload() handler, then the active
80  // entry is the page to be loaded as we navigate away from the unloading
81  // page.  For this reason, we can't use GetURL() to get the opener URL,
82  // because it returns the active entry.
83  content::NavigationEntry* entry =
84      web_contents()->GetController().GetLastCommittedEntry();
85  GURL creator = entry ? entry->GetVirtualURL() : GURL();
86  Profile* profile =
87      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
88
89  if (creator.is_valid() &&
90      profile->GetHostContentSettingsMap()->GetContentSetting(
91          creator, creator, CONTENT_SETTINGS_TYPE_POPUPS, std::string()) ==
92          CONTENT_SETTING_ALLOW) {
93    return false;
94  } else {
95    if (blocked_popups_.size() < kMaximumNumberOfPopups) {
96      blocked_popups_.Add(new BlockedRequest(params, window_features));
97      TabSpecificContentSettings::FromWebContents(web_contents())->
98          OnContentBlocked(CONTENT_SETTINGS_TYPE_POPUPS);
99    }
100    return true;
101  }
102}
103
104void PopupBlockerTabHelper::AddBlockedPopup(const BlockedWindowParams& params) {
105  chrome::NavigateParams nav_params =
106      params.CreateNavigateParams(web_contents());
107
108  if (blocked_popups_.size() < kMaximumNumberOfPopups) {
109    blocked_popups_.Add(new BlockedRequest(nav_params, params.features()));
110    TabSpecificContentSettings::FromWebContents(web_contents())->
111        OnContentBlocked(CONTENT_SETTINGS_TYPE_POPUPS);
112  }
113}
114
115void PopupBlockerTabHelper::ShowBlockedPopup(int32 id) {
116  BlockedRequest* popup = blocked_popups_.Lookup(id);
117  if (!popup)
118    return;
119  // We set user_gesture to true here, so the new popup gets correctly focused.
120  popup->params.user_gesture = true;
121#if defined(OS_ANDROID)
122  TabModelList::HandlePopupNavigation(&popup->params);
123#else
124  chrome::Navigate(&popup->params);
125#endif
126  if (popup->params.target_contents) {
127    popup->params.target_contents->Send(new ChromeViewMsg_SetWindowFeatures(
128        popup->params.target_contents->GetRoutingID(), popup->window_features));
129  }
130  blocked_popups_.Remove(id);
131  if (blocked_popups_.IsEmpty())
132    PopupNotificationVisibilityChanged(false);
133}
134
135size_t PopupBlockerTabHelper::GetBlockedPopupsCount() const {
136  return blocked_popups_.size();
137}
138
139PopupBlockerTabHelper::PopupIdMap
140    PopupBlockerTabHelper::GetBlockedPopupRequests() {
141  PopupIdMap result;
142  for (IDMap<BlockedRequest, IDMapOwnPointer>::const_iterator iter(
143           &blocked_popups_);
144       !iter.IsAtEnd();
145       iter.Advance()) {
146    result[iter.GetCurrentKey()] = iter.GetCurrentValue()->params.url;
147  }
148  return result;
149}
150