OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/notifications/notification_permission_context.h" | 5 #include "chrome/browser/notifications/notification_permission_context.h" |
6 | 6 |
7 #include <queue> | |
8 | |
9 #include "base/callback.h" | |
10 #include "base/location.h" | |
11 #include "base/rand_util.h" | |
12 #include "base/single_thread_task_runner.h" | |
13 #include "base/thread_task_runner_handle.h" | |
14 #include "base/timer/timer.h" | |
7 #include "chrome/browser/notifications/desktop_notification_profile_util.h" | 15 #include "chrome/browser/notifications/desktop_notification_profile_util.h" |
16 #include "chrome/browser/permissions/permission_request_id.h" | |
17 #include "chrome/browser/profiles/profile.h" | |
8 #include "components/content_settings/core/common/content_settings_pattern.h" | 18 #include "components/content_settings/core/common/content_settings_pattern.h" |
19 #include "content/public/browser/browser_thread.h" | |
9 #include "content/public/browser/permission_type.h" | 20 #include "content/public/browser/permission_type.h" |
21 #include "content/public/browser/render_frame_host.h" | |
22 #include "content/public/browser/web_contents_observer.h" | |
23 #include "content/public/browser/web_contents_user_data.h" | |
10 #include "url/gurl.h" | 24 #include "url/gurl.h" |
11 | 25 |
26 namespace { | |
27 | |
28 // At most one of these is attached to each WebContents. | |
Peter Beverloo
2016/01/13 01:42:10
Please document what its purpose is.
johnme
2016/01/13 20:49:25
Done.
| |
29 class VisibilityTimerTabHelper | |
30 : public content::WebContentsObserver, | |
31 public content::WebContentsUserData<VisibilityTimerTabHelper> { | |
32 public: | |
33 ~VisibilityTimerTabHelper() override {}; | |
Peter Beverloo
2016/01/13 01:42:10
nit: no ;
johnme
2016/01/13 20:49:26
Done.
| |
34 | |
35 // Runs |task| after the WebContents has been visible for a consecutive | |
36 // duration of at least |visible_delay|. | |
37 void PostTaskAfterVisibleDelay(const tracked_objects::Location& from_here, | |
38 const base::Closure& task, | |
39 base::TimeDelta visible_delay); | |
40 | |
41 // WebContentsObserver: | |
42 void WasShown() override; | |
43 void WasHidden() override; | |
44 void WebContentsDestroyed() override; | |
45 | |
46 private: | |
47 friend class content::WebContentsUserData<VisibilityTimerTabHelper>; | |
48 explicit VisibilityTimerTabHelper(content::WebContents* contents); | |
49 | |
50 void RunTask(const base::Closure& task); | |
51 | |
52 bool is_visible_; | |
53 std::queue<scoped_ptr<base::Timer>> task_queue_; | |
54 | |
55 DISALLOW_COPY_AND_ASSIGN(VisibilityTimerTabHelper); | |
56 }; | |
57 | |
58 VisibilityTimerTabHelper::VisibilityTimerTabHelper( | |
59 content::WebContents* contents) | |
60 : content::WebContentsObserver(contents) { | |
61 if (!contents->GetMainFrame()) { | |
62 is_visible_ = false; | |
63 } else { | |
64 switch (contents->GetMainFrame()->GetVisibilityState()) { | |
65 case blink::WebPageVisibilityStateHidden: | |
66 case blink::WebPageVisibilityStatePrerender: | |
67 is_visible_ = false; | |
68 break; | |
69 case blink::WebPageVisibilityStateVisible: | |
70 is_visible_ = true; | |
71 break; | |
72 } | |
73 } | |
74 } | |
75 | |
76 void VisibilityTimerTabHelper::PostTaskAfterVisibleDelay( | |
77 const tracked_objects::Location& from_here, | |
78 const base::Closure& task, | |
79 base::TimeDelta visible_delay) { | |
80 // Safe to use Unretained, as destroying this will destroy task_queue_, hence | |
81 // cancelling all timers. | |
82 task_queue_.push(make_scoped_ptr(new base::Timer( | |
83 from_here, visible_delay, base::Bind(&VisibilityTimerTabHelper::RunTask, | |
84 base::Unretained(this), task), | |
85 false /* is_repeating */))); | |
86 DCHECK(!task_queue_.back()->IsRunning()); | |
87 if (is_visible_ && task_queue_.size() == 1) | |
88 task_queue_.front()->Reset(); | |
89 } | |
90 | |
91 void VisibilityTimerTabHelper::WasShown() { | |
92 if (!is_visible_ && !task_queue_.empty()) | |
93 task_queue_.front()->Reset(); | |
94 is_visible_ = true; | |
95 } | |
96 | |
97 void VisibilityTimerTabHelper::WasHidden() { | |
98 if (is_visible_ && !task_queue_.empty()) | |
99 task_queue_.front()->Stop(); | |
100 is_visible_ = false; | |
101 } | |
102 | |
103 void VisibilityTimerTabHelper::WebContentsDestroyed() { | |
104 // Delete ourselves, to avoid running tasks after WebContents is destroyed. | |
105 web_contents()->RemoveUserData(UserDataKey()); | |
106 // |this| has been deleted now. | |
107 } | |
108 | |
109 void VisibilityTimerTabHelper::RunTask(const base::Closure& task) { | |
110 DCHECK(is_visible_); | |
111 task.Run(); | |
112 task_queue_.pop(); | |
113 if (!task_queue_.empty()) { | |
114 task_queue_.front()->Reset(); | |
115 return; | |
116 } | |
117 web_contents()->RemoveUserData(UserDataKey()); | |
118 // |this| has been deleted now. | |
119 } | |
120 | |
121 } // namespace | |
122 | |
123 DEFINE_WEB_CONTENTS_USER_DATA_KEY(VisibilityTimerTabHelper); | |
124 | |
12 NotificationPermissionContext::NotificationPermissionContext(Profile* profile) | 125 NotificationPermissionContext::NotificationPermissionContext(Profile* profile) |
13 : PermissionContextBase(profile, | 126 : PermissionContextBase(profile, |
14 content::PermissionType::NOTIFICATIONS, | 127 content::PermissionType::NOTIFICATIONS, |
15 CONTENT_SETTINGS_TYPE_NOTIFICATIONS) {} | 128 CONTENT_SETTINGS_TYPE_NOTIFICATIONS), |
129 weak_factory_ui_thread_(this) {} | |
16 | 130 |
17 NotificationPermissionContext::~NotificationPermissionContext() {} | 131 NotificationPermissionContext::~NotificationPermissionContext() {} |
18 | 132 |
19 void NotificationPermissionContext::ResetPermission( | 133 void NotificationPermissionContext::ResetPermission( |
20 const GURL& requesting_origin, | 134 const GURL& requesting_origin, |
21 const GURL& embedder_origin) { | 135 const GURL& embedder_origin) { |
22 DesktopNotificationProfileUtil::ClearSetting( | 136 DesktopNotificationProfileUtil::ClearSetting( |
23 profile(), ContentSettingsPattern::FromURLNoWildcard(requesting_origin)); | 137 profile(), ContentSettingsPattern::FromURLNoWildcard(requesting_origin)); |
24 } | 138 } |
25 | 139 |
140 void NotificationPermissionContext::DecidePermission( | |
141 content::WebContents* web_contents, | |
142 const PermissionRequestID& id, | |
143 const GURL& requesting_origin, | |
144 const GURL& embedding_origin, | |
145 bool user_gesture, | |
146 const BrowserPermissionCallback& callback) { | |
147 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
148 | |
149 // Notifications permission is always denied in incognito. To prevent sites | |
150 // from using that to detect whether incognito mode is active, we deny after a | |
151 // random time delay, to simulate a user clicking a bubble/infobar. See also | |
152 // ContentSettingsRegistry::Init, which marks notifications as | |
153 // INHERIT_IN_INCOGNITO_EXCEPT_ALLOW, and | |
154 // PermissionMenuModel::PermissionMenuModel which prevents users from manually | |
155 // allowing the permission. | |
156 if (profile()->IsOffTheRecord()) { | |
157 // Random number of seconds in the range [1.0, 2.0). | |
158 double delay_seconds = 1.0 + 1.0 * base::RandDouble(); | |
159 VisibilityTimerTabHelper::CreateForWebContents(web_contents); | |
160 VisibilityTimerTabHelper::FromWebContents(web_contents) | |
161 ->PostTaskAfterVisibleDelay( | |
162 FROM_HERE, | |
163 base::Bind(&NotificationPermissionContext::NotifyPermissionSet, | |
164 weak_factory_ui_thread_.GetWeakPtr(), id, | |
165 requesting_origin, embedding_origin, callback, | |
166 true /* persist */, CONTENT_SETTING_BLOCK), | |
167 base::TimeDelta::FromSecondsD(delay_seconds)); | |
168 return; | |
169 } | |
170 | |
171 PermissionContextBase::DecidePermission(web_contents, id, requesting_origin, | |
172 embedding_origin, user_gesture, | |
173 callback); | |
174 } | |
175 | |
26 // Unlike other permission types, granting a notification for a given origin | 176 // Unlike other permission types, granting a notification for a given origin |
27 // will not take into account the |embedder_origin|, it will only be based | 177 // will not take into account the |embedder_origin|, it will only be based |
28 // on the requesting iframe origin. | 178 // on the requesting iframe origin. |
29 // TODO(mukai) Consider why notifications behave differently than | 179 // TODO(mukai) Consider why notifications behave differently than |
30 // other permissions. https://crbug.com/416894 | 180 // other permissions. https://crbug.com/416894 |
31 void NotificationPermissionContext::UpdateContentSetting( | 181 void NotificationPermissionContext::UpdateContentSetting( |
32 const GURL& requesting_origin, | 182 const GURL& requesting_origin, |
33 const GURL& embedder_origin, | 183 const GURL& embedder_origin, |
34 ContentSetting content_setting) { | 184 ContentSetting content_setting) { |
35 DCHECK(content_setting == CONTENT_SETTING_ALLOW || | 185 DCHECK(content_setting == CONTENT_SETTING_ALLOW || |
36 content_setting == CONTENT_SETTING_BLOCK); | 186 content_setting == CONTENT_SETTING_BLOCK); |
37 | 187 |
38 if (content_setting == CONTENT_SETTING_ALLOW) { | 188 if (content_setting == CONTENT_SETTING_ALLOW) { |
39 DesktopNotificationProfileUtil::GrantPermission(profile(), | 189 DesktopNotificationProfileUtil::GrantPermission(profile(), |
40 requesting_origin); | 190 requesting_origin); |
41 } else { | 191 } else { |
42 DesktopNotificationProfileUtil::DenyPermission(profile(), | 192 DesktopNotificationProfileUtil::DenyPermission(profile(), |
43 requesting_origin); | 193 requesting_origin); |
44 } | 194 } |
45 } | 195 } |
46 | 196 |
47 bool NotificationPermissionContext::IsRestrictedToSecureOrigins() const { | 197 bool NotificationPermissionContext::IsRestrictedToSecureOrigins() const { |
48 return false; | 198 return false; |
49 } | 199 } |
OLD | NEW |