Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(177)

Side by Side Diff: content/browser/browsing_data/clear_site_data_throttle.cc

Issue 2368923003: Support the Clear-Site-Data header on resource requests (Closed)
Patch Set: Addressed comments. Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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 "content/browser/browsing_data/clear_site_data_throttle.h" 5 #include "content/browser/browsing_data/clear_site_data_throttle.h"
6 6
7 #include "base/command_line.h" 7 #include "base/command_line.h"
8 #include "base/json/json_reader.h" 8 #include "base/json/json_reader.h"
9 #include "base/json/json_string_value_serializer.h" 9 #include "base/json/json_string_value_serializer.h"
10 #include "base/memory/ptr_util.h" 10 #include "base/memory/ptr_util.h"
11 #include "base/metrics/histogram_macros.h" 11 #include "base/metrics/histogram_macros.h"
12 #include "base/scoped_observer.h"
12 #include "base/strings/string_util.h" 13 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h" 14 #include "base/strings/stringprintf.h"
14 #include "base/values.h" 15 #include "base/values.h"
15 #include "content/browser/frame_host/navigation_handle_impl.h" 16 #include "content/browser/service_worker/service_worker_response_info.h"
16 #include "content/public/browser/browser_context.h" 17 #include "content/public/browser/browser_context.h"
17 #include "content/public/browser/content_browser_client.h" 18 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/navigation_handle.h" 19 #include "content/public/browser/browsing_data_filter_builder.h"
20 #include "content/public/browser/browsing_data_remover.h"
21 #include "content/public/browser/render_frame_host.h"
19 #include "content/public/browser/web_contents.h" 22 #include "content/public/browser/web_contents.h"
20 #include "content/public/common/content_client.h"
21 #include "content/public/common/content_switches.h" 23 #include "content/public/common/content_switches.h"
22 #include "content/public/common/origin_util.h" 24 #include "content/public/common/origin_util.h"
25 #include "content/public/common/resource_response_info.h"
26 #include "content/public/common/resource_type.h"
27 #include "net/base/load_flags.h"
28 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
23 #include "net/http/http_response_headers.h" 29 #include "net/http/http_response_headers.h"
30 #include "net/url_request/redirect_info.h"
24 #include "url/gurl.h" 31 #include "url/gurl.h"
25 #include "url/origin.h" 32 #include "url/origin.h"
26 33
27 namespace content { 34 namespace content {
28 35
29 namespace { 36 namespace {
30 37
31 static const char* kClearSiteDataHeader = "Clear-Site-Data"; 38 const char kNameForLogging[] = "ClearSiteDataThrottle";
32 39
33 static const char* kTypesKey = "types"; 40 const char kClearSiteDataHeader[] = "Clear-Site-Data";
41
42 const char kTypesKey[] = "types";
43
44 // Datatypes.
45 const char kDatatypeCookies[] = "cookies";
46 const char kDatatypeStorage[] = "storage";
47 const char kDatatypeCache[] = "cache";
34 48
35 // Pretty-printed log output. 49 // Pretty-printed log output.
36 static const char* kConsoleMessagePrefix = "Clear-Site-Data header on '%s': %s"; 50 const char kConsoleMessagePrefix[] = "Clear-Site-Data header on '%s': %s";
mmenke 2017/06/05 22:39:36 nit: Not actually a prefix
msramek 2017/06/06 18:20:57 Done. Renamed to "template".
37 static const char* kClearingOneType = "Clearing %s."; 51 const char kConsoleMessageCleared[] = "Cleared datatypes: %s.";
38 static const char* kClearingTwoTypes = "Clearing %s and %s."; 52 const char kConsoleMessageDatatypeSeparator[] = ", ";
39 static const char* kClearingThreeTypes = "Clearing %s, %s, and %s.";
40
41 // Console logging. Adds a |text| message with |level| to |messages|.
42 void ConsoleLog(std::vector<ClearSiteDataThrottle::ConsoleMessage>* messages,
43 const GURL& url,
44 const std::string& text,
45 ConsoleMessageLevel level) {
46 messages->push_back({url, text, level});
47 }
48 53
49 bool AreExperimentalFeaturesEnabled() { 54 bool AreExperimentalFeaturesEnabled() {
50 return base::CommandLine::ForCurrentProcess()->HasSwitch( 55 return base::CommandLine::ForCurrentProcess()->HasSwitch(
51 switches::kEnableExperimentalWebPlatformFeatures); 56 switches::kEnableExperimentalWebPlatformFeatures);
52 } 57 }
53 58
59 bool IsNavigationRequest(net::URLRequest* request) {
60 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request);
61 return info && (info->GetResourceType() == RESOURCE_TYPE_MAIN_FRAME ||
62 info->GetResourceType() == RESOURCE_TYPE_SUB_FRAME);
mmenke 2017/06/05 22:39:36 info && ResourceType::IsResourceTypeFrame(info->Ge
msramek 2017/06/06 18:20:57 Done. Good to know :)
63 }
64
54 // Represents the parameters as a single number to be recorded in a histogram. 65 // Represents the parameters as a single number to be recorded in a histogram.
55 int ParametersMask(bool clear_cookies, bool clear_storage, bool clear_cache) { 66 int ParametersMask(bool clear_cookies, bool clear_storage, bool clear_cache) {
56 return static_cast<int>(clear_cookies) * (1 << 0) + 67 return static_cast<int>(clear_cookies) * (1 << 0) +
57 static_cast<int>(clear_storage) * (1 << 1) + 68 static_cast<int>(clear_storage) * (1 << 1) +
58 static_cast<int>(clear_cache) * (1 << 2); 69 static_cast<int>(clear_cache) * (1 << 2);
59 } 70 }
60 71
72 // A helper function to pass an IO thread callback to a method called on
73 // the UI thread.
74 void JumpFromUIToIOThread(base::OnceClosure callback) {
75 DCHECK_CURRENTLY_ON(BrowserThread::UI);
76 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, std::move(callback));
77 }
78
79 // Finds the BrowserContext associated with the request and requests
80 // the actual clearing of data for |origin|. The datatypes to be deleted
mmenke 2017/06/05 22:39:36 nit: data types
msramek 2017/06/06 18:20:57 Done. Actually, we've been using "datatype" everyw
81 // are determined by |clear_cookies|, |clear_storage|, and |clear_cache|.
82 // |web_contents_getter| identifies the WebContents from which the request
83 // originated. Must be run on the UI thread. The |callback| will be executed
84 // on the IO thread.
85 class UIThreadSiteDataClearer : public BrowsingDataRemover::Observer {
86 public:
87 static void Run(
88 const ResourceRequestInfo::WebContentsGetter& web_contents_getter,
89 const url::Origin& origin,
90 bool clear_cookies,
91 bool clear_storage,
92 bool clear_cache,
93 base::OnceClosure callback) {
94 WebContents* web_contents = web_contents_getter.Run();
95 if (!web_contents)
96 return;
97
98 (new UIThreadSiteDataClearer(web_contents, origin, clear_cookies,
99 clear_storage, clear_cache,
100 std::move(callback)))
101 ->RunAndDestroySelfWhenDone();
102 }
103
104 private:
105 UIThreadSiteDataClearer(const WebContents* web_contents,
106 const url::Origin& origin,
107 bool clear_cookies,
108 bool clear_storage,
109 bool clear_cache,
110 base::OnceClosure callback)
111 : origin_(origin),
112 clear_cookies_(clear_cookies),
113 clear_storage_(clear_storage),
114 clear_cache_(clear_cache),
115 callback_(std::move(callback)),
116 pending_task_count_(0),
117 remover_(nullptr),
118 scoped_observer_(this) {
119 DCHECK_CURRENTLY_ON(BrowserThread::UI);
120
121 remover_ = BrowserContext::GetBrowsingDataRemover(
122 web_contents->GetBrowserContext());
123 DCHECK(remover_);
124 scoped_observer_.Add(remover_);
125 }
126
127 ~UIThreadSiteDataClearer() override {}
128
129 void RunAndDestroySelfWhenDone() {
130 DCHECK_CURRENTLY_ON(BrowserThread::UI);
131
132 // Cookies and channel IDs are scoped to
133 // a) eTLD+1 of |origin|'s host if |origin|'s host is a registrable domain
134 // or a subdomain thereof
135 // b) |origin|'s host exactly if it is an IP address or an internal hostname
136 // (e.g. "localhost" or "fileserver").
137 // TODO(msramek): What about plugin data?
138 if (clear_cookies_) {
139 std::string domain = GetDomainAndRegistry(
140 origin_.host(),
141 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
142
143 if (domain.empty())
144 domain = origin_.host(); // IP address or internal hostname.
145
146 std::unique_ptr<BrowsingDataFilterBuilder> domain_filter_builder(
147 BrowsingDataFilterBuilder::Create(
148 BrowsingDataFilterBuilder::WHITELIST));
149 domain_filter_builder->AddRegisterableDomain(domain);
150
151 pending_task_count_++;
152 remover_->RemoveWithFilterAndReply(
153 base::Time(), base::Time::Max(),
154 BrowsingDataRemover::DATA_TYPE_COOKIES |
155 BrowsingDataRemover::DATA_TYPE_CHANNEL_IDS,
156 BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB |
157 BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB,
158 std::move(domain_filter_builder), this);
159 }
160
161 // Delete origin-scoped data.
162 int remove_mask = 0;
163 if (clear_storage_)
164 remove_mask |= BrowsingDataRemover::DATA_TYPE_DOM_STORAGE;
165 if (clear_cache_)
166 remove_mask |= BrowsingDataRemover::DATA_TYPE_CACHE;
167
168 if (remove_mask) {
169 std::unique_ptr<BrowsingDataFilterBuilder> origin_filter_builder(
170 BrowsingDataFilterBuilder::Create(
171 BrowsingDataFilterBuilder::WHITELIST));
172 origin_filter_builder->AddOrigin(origin_);
173
174 pending_task_count_++;
175 remover_->RemoveWithFilterAndReply(
176 base::Time(), base::Time::Max(), remove_mask,
177 BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB |
178 BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB,
179 std::move(origin_filter_builder), this);
180 }
mmenke 2017/06/05 22:39:36 DCHECK_GT(pending_task_count_, 0);
msramek 2017/06/06 18:20:57 Done.
181 }
182
183 // BrowsingDataRemover::Observer;
mmenke 2017/06/05 22:39:36 nit: ; -> : (At least I assume that was a typo)
msramek 2017/06/06 18:20:57 Done. Yes :)
184 void OnBrowsingDataRemoverDone() override {
185 DCHECK(pending_task_count_);
186 if (--pending_task_count_)
187 return;
188
189 JumpFromUIToIOThread(std::move(callback_));
190 delete this;
191 }
192
193 url::Origin origin_;
194 bool clear_cookies_;
195 bool clear_storage_;
196 bool clear_cache_;
197 base::OnceClosure callback_;
198 int pending_task_count_;
199 BrowsingDataRemover* remover_;
200 ScopedObserver<BrowsingDataRemover, BrowsingDataRemover::Observer>
201 scoped_observer_;
202 };
203
204 // Outputs a single |formatted_message| on the UI thread.
205 void OutputFormattedMessage(WebContents* web_contents,
206 ConsoleMessageLevel level,
207 const std::string& formatted_text) {
208 if (web_contents)
209 web_contents->GetMainFrame()->AddMessageToConsole(level, formatted_text);
210 }
211
212 // Outputs |messages| to the console of WebContents retrieved from
213 // |web_contents_getter|. Must be run on the UI thread.
214 void OutputMessagesOnUIThread(
215 const ResourceRequestInfo::WebContentsGetter& web_contents_getter,
216 const std::vector<ClearSiteDataThrottle::ConsoleMessagesDelegate::Message>&
217 messages,
218 const ClearSiteDataThrottle::ConsoleMessagesDelegate::
219 OutputFormattedMessageFunction& output_formatted_message_function) {
220 DCHECK_CURRENTLY_ON(BrowserThread::UI);
221
222 WebContents* web_contents = web_contents_getter.Run();
223
224 for (const auto& message : messages) {
225 // Prefix each message with |kConsoleMessagePrefix|.
226 output_formatted_message_function.Run(
227 web_contents, message.level,
228 base::StringPrintf(kConsoleMessagePrefix, message.url.spec().c_str(),
229 message.text.c_str()));
230 }
231 }
232
61 } // namespace 233 } // namespace
62 234
235 ////////////////////////////////////////////////////////////////////////////////
236 // ConsoleMessagesDelegate
237
238 ClearSiteDataThrottle::ConsoleMessagesDelegate::ConsoleMessagesDelegate()
239 : output_formatted_message_function_(base::Bind(&OutputFormattedMessage)) {}
240
241 ClearSiteDataThrottle::ConsoleMessagesDelegate::~ConsoleMessagesDelegate() {}
242
243 void ClearSiteDataThrottle::ConsoleMessagesDelegate::AddMessage(
244 const GURL& url,
245 const std::string& text,
246 ConsoleMessageLevel level) {
247 messages_.push_back({url, text, level});
248 }
249
250 void ClearSiteDataThrottle::ConsoleMessagesDelegate::OutputMessages(
251 const ResourceRequestInfo::WebContentsGetter& web_contents_getter) {
252 if (messages_.empty())
253 return;
254
255 DCHECK_CURRENTLY_ON(BrowserThread::IO);
256 BrowserThread::PostTask(
257 BrowserThread::UI, FROM_HERE,
258 base::BindOnce(&OutputMessagesOnUIThread, web_contents_getter,
259 std::move(messages_), output_formatted_message_function_));
260
261 messages_.clear();
262 }
263
264 void ClearSiteDataThrottle::ConsoleMessagesDelegate::
265 SetOutputFormattedMessageFunctionForTesting(
266 const OutputFormattedMessageFunction& function) {
267 output_formatted_message_function_ = function;
268 }
269
270 ////////////////////////////////////////////////////////////////////////////////
271 // ClearSiteDataThrottle
272
63 // static 273 // static
64 std::unique_ptr<NavigationThrottle> 274 std::unique_ptr<ResourceThrottle>
65 ClearSiteDataThrottle::CreateThrottleForNavigation(NavigationHandle* handle) { 275 ClearSiteDataThrottle::MaybeCreateThrottleForRequest(net::URLRequest* request) {
66 if (AreExperimentalFeaturesEnabled()) 276 // This is an experimental feature.
67 return base::WrapUnique(new ClearSiteDataThrottle(handle)); 277 if (!AreExperimentalFeaturesEnabled())
68 278 return std::unique_ptr<ResourceThrottle>();
mmenke 2017/06/05 22:39:36 While I'm sure this isn't exactly a huge consumer
msramek 2017/06/06 18:20:57 Done. Sure, I was just trying to contain the knowl
69 return std::unique_ptr<NavigationThrottle>(); 279
280 // The throttle has no purpose if the request has no ResourceRequestInfo,
281 // because we won't be able to determine whose data should be deleted.
282 if (!ResourceRequestInfo::ForRequest(request))
283 return std::unique_ptr<ResourceThrottle>();
mmenke 2017/06/05 22:42:36 These two returns can just be "return nullptr;"
msramek 2017/06/06 18:20:57 Done.
284
285 return base::WrapUnique(new ClearSiteDataThrottle(
286 request, base::MakeUnique<ConsoleMessagesDelegate>()));
287 }
288
289 ClearSiteDataThrottle::~ClearSiteDataThrottle() {
290 // Output the cached console messages. For navigations, we output console
291 // messages when the request is finished rather than in real time, since in
292 // the case of navigations swapping RenderFrameHost would cause the outputs
293 // to disappear.
294 if (IsNavigationRequest(request_))
295 OutputConsoleMessages();
296 }
297
298 const char* ClearSiteDataThrottle::GetNameForLogging() const {
299 return kNameForLogging;
300 }
301
302 void ClearSiteDataThrottle::WillRedirectRequest(
303 const net::RedirectInfo& redirect_info,
304 bool* defer) {
305 *defer = HandleHeader();
306
307 // For subresource requests, console messages are output on every redirect.
308 // If the redirect is deferred, wait until it is resumed.
309 if (!IsNavigationRequest(request_) && !*defer)
310 OutputConsoleMessages();
311 }
312
313 void ClearSiteDataThrottle::WillProcessResponse(bool* defer) {
314 *defer = HandleHeader();
315
316 // For subresource requests, console messages are output on every redirect.
317 // If the redirect is deferred, wait until it is resumed.
318 if (!IsNavigationRequest(request_) && !*defer)
319 OutputConsoleMessages();
320 }
321
322 // static
323 bool ClearSiteDataThrottle::ParseHeaderForTesting(
324 const std::string& header,
325 bool* clear_cookies,
326 bool* clear_storage,
327 bool* clear_cache,
328 ConsoleMessagesDelegate* delegate,
329 const GURL& current_url) {
330 return ClearSiteDataThrottle::ParseHeader(
331 header, clear_cookies, clear_storage, clear_cache, delegate, current_url);
70 } 332 }
71 333
72 ClearSiteDataThrottle::ClearSiteDataThrottle( 334 ClearSiteDataThrottle::ClearSiteDataThrottle(
73 NavigationHandle* navigation_handle) 335 net::URLRequest* request,
74 : NavigationThrottle(navigation_handle), 336 std::unique_ptr<ConsoleMessagesDelegate> delegate)
75 clearing_in_progress_(false), 337 : request_(request),
76 weak_ptr_factory_(this) {} 338 delegate_(std::move(delegate)),
77 339 weak_ptr_factory_(this) {
78 ClearSiteDataThrottle::~ClearSiteDataThrottle() { 340 DCHECK(request_);
79 // At the end of the navigation we finally have access to the correct 341 DCHECK(delegate_);
80 // RenderFrameHost. Output the cached console messages. Prefix each sequence 342 }
81 // of messages belonging to the same URL with |kConsoleMessagePrefix|. 343
82 GURL last_seen_url; 344 const GURL& ClearSiteDataThrottle::GetCurrentURL() const {
83 for (const ConsoleMessage& message : messages_) { 345 return request_->url();
84 if (message.url == last_seen_url) { 346 }
85 navigation_handle()->GetRenderFrameHost()->AddMessageToConsole( 347
86 message.level, message.text); 348 const net::HttpResponseHeaders* ClearSiteDataThrottle::GetResponseHeaders()
87 } else { 349 const {
88 navigation_handle()->GetRenderFrameHost()->AddMessageToConsole( 350 return request_->response_headers();
89 message.level, 351 }
90 base::StringPrintf(kConsoleMessagePrefix, message.url.spec().c_str(), 352
91 message.text.c_str())); 353 bool ClearSiteDataThrottle::HandleHeader() {
354 const net::HttpResponseHeaders* headers = GetResponseHeaders();
355
356 std::string header_value;
357 if (!headers ||
358 !headers->GetNormalizedHeader(kClearSiteDataHeader, &header_value)) {
359 return false;
360 }
361
362 // Only accept the header on secure non-unique origins.
363 if (!IsOriginSecure(GetCurrentURL())) {
364 delegate_->AddMessage(GetCurrentURL(),
365 "Not supported for insecure origins.",
366 CONSOLE_MESSAGE_LEVEL_ERROR);
367 return false;
368 }
369
370 url::Origin origin(GetCurrentURL());
371 if (origin.unique()) {
372 delegate_->AddMessage(GetCurrentURL(), "Not supported for unique origins.",
373 CONSOLE_MESSAGE_LEVEL_ERROR);
374 return false;
375 }
376
377 // The LOAD_DO_NOT_SAVE_COOKIES flag prohibits the request from doing any
378 // modification to cookies. Clear-Site-Data applies this restriction to other
379 // datatypes as well.
380 // TODO(msramek): Consider showing a blocked icon via
381 // TabSpecificContentSettings and reporting the action in the "Blocked"
382 // section of the cookies dialog in OIB.
383 if (request_->load_flags() & net::LOAD_DO_NOT_SAVE_COOKIES) {
384 delegate_->AddMessage(
385 GetCurrentURL(),
386 "The request's credentials mode prohibits modifying cookies "
387 "and other local data.",
388 CONSOLE_MESSAGE_LEVEL_ERROR);
389 return false;
390 }
391
392 // Service workers can handle fetches of third-party resources and inject
393 // arbitrary headers. Ignore responses that came from a service worker,
394 // as supporting Clear-Site-Data would give them the power to delete data from
395 // any website.
396 // See https://w3c.github.io/webappsec-clear-site-data/#service-workers
397 // for more information.
398 const ServiceWorkerResponseInfo* response_info =
399 ServiceWorkerResponseInfo::ForRequest(request_);
400 if (response_info) {
401 ResourceResponseInfo extra_response_info;
402 response_info->GetExtraResponseInfo(&extra_response_info);
403
404 if (extra_response_info.was_fetched_via_service_worker) {
405 delegate_->AddMessage(
406 GetCurrentURL(),
407 "Ignoring, as the response came from a service worker.",
408 CONSOLE_MESSAGE_LEVEL_ERROR);
409 return false;
92 } 410 }
93 411 }
94 last_seen_url = message.url;
95 }
96 }
97
98 ClearSiteDataThrottle::ThrottleCheckResult
99 ClearSiteDataThrottle::WillStartRequest() {
100 current_url_ = navigation_handle()->GetURL();
101 return PROCEED;
102 }
103
104 ClearSiteDataThrottle::ThrottleCheckResult
105 ClearSiteDataThrottle::WillRedirectRequest() {
106 // We are processing a redirect from url1 to url2. GetResponseHeaders()
107 // contains headers from url1, but GetURL() is already equal to url2. Handle
108 // the headers before updating the URL, so that |current_url_| corresponds
109 // to the URL that sent the headers.
110 HandleHeader();
111 current_url_ = navigation_handle()->GetURL();
112
113 return clearing_in_progress_ ? DEFER : PROCEED;
114 }
115
116 ClearSiteDataThrottle::ThrottleCheckResult
117 ClearSiteDataThrottle::WillProcessResponse() {
118 HandleHeader();
119 return clearing_in_progress_ ? DEFER : PROCEED;
120 }
121
122 const char* ClearSiteDataThrottle::GetNameForLogging() {
123 return "ClearSiteDataThrottle";
124 }
125
126 void ClearSiteDataThrottle::HandleHeader() {
127 NavigationHandleImpl* handle =
128 static_cast<NavigationHandleImpl*>(navigation_handle());
129 const net::HttpResponseHeaders* headers = handle->GetResponseHeaders();
130
131 if (!headers || !headers->HasHeader(kClearSiteDataHeader))
132 return;
133
134 // Only accept the header on secure origins.
135 if (!IsOriginSecure(current_url_)) {
136 ConsoleLog(&messages_, current_url_, "Not supported for insecure origins.",
137 CONSOLE_MESSAGE_LEVEL_ERROR);
138 return;
139 }
140
141 std::string header_value;
142 headers->GetNormalizedHeader(kClearSiteDataHeader, &header_value);
143 412
144 bool clear_cookies; 413 bool clear_cookies;
145 bool clear_storage; 414 bool clear_storage;
146 bool clear_cache; 415 bool clear_cache;
147 416
148 if (!ParseHeader(header_value, &clear_cookies, &clear_storage, &clear_cache, 417 if (!ClearSiteDataThrottle::ParseHeader(header_value, &clear_cookies,
149 &messages_)) { 418 &clear_storage, &clear_cache,
150 return; 419 delegate_.get(), GetCurrentURL())) {
151 } 420 return false;
421 }
422
423 // If the header is valid, clear the data for this browser context and origin.
424 clearing_started_ = base::TimeTicks::Now();
152 425
153 // Record the call parameters. 426 // Record the call parameters.
154 UMA_HISTOGRAM_ENUMERATION( 427 UMA_HISTOGRAM_ENUMERATION(
155 "Navigation.ClearSiteData.Parameters", 428 "Navigation.ClearSiteData.Parameters",
156 ParametersMask(clear_cookies, clear_storage, clear_cache), (1 << 3)); 429 ParametersMask(clear_cookies, clear_storage, clear_cache), (1 << 3));
157 430
158 // If the header is valid, clear the data for this browser context and origin. 431 base::WeakPtr<ClearSiteDataThrottle> weak_ptr =
159 BrowserContext* browser_context = 432 weak_ptr_factory_.GetWeakPtr();
160 navigation_handle()->GetWebContents()->GetBrowserContext(); 433
161 url::Origin origin(current_url_); 434 // Immediately bind the weak pointer to the current thread (IO). This will
162 435 // make a potential misuse on the UI thread DCHECK immediately rather than
163 if (origin.unique()) { 436 // later when it's correctly used on the IO thread again.
164 ConsoleLog(&messages_, current_url_, "Not supported for unique origins.", 437 weak_ptr.get();
165 CONSOLE_MESSAGE_LEVEL_ERROR); 438
166 return; 439 ExecuteClearingTask(
167 } 440 origin, clear_cookies, clear_storage, clear_cache,
168 441 base::BindOnce(&ClearSiteDataThrottle::TaskFinished, weak_ptr));
169 clearing_in_progress_ = true; 442
170 clearing_started_ = base::TimeTicks::Now(); 443 return true;
171 GetContentClient()->browser()->ClearSiteData( 444 }
172 browser_context, origin, clear_cookies, clear_storage, clear_cache, 445
173 base::Bind(&ClearSiteDataThrottle::TaskFinished, 446 // static
174 weak_ptr_factory_.GetWeakPtr()));
175 }
176
177 bool ClearSiteDataThrottle::ParseHeader(const std::string& header, 447 bool ClearSiteDataThrottle::ParseHeader(const std::string& header,
178 bool* clear_cookies, 448 bool* clear_cookies,
179 bool* clear_storage, 449 bool* clear_storage,
180 bool* clear_cache, 450 bool* clear_cache,
181 std::vector<ConsoleMessage>* messages) { 451 ConsoleMessagesDelegate* delegate,
452 const GURL& current_url) {
182 if (!base::IsStringASCII(header)) { 453 if (!base::IsStringASCII(header)) {
183 ConsoleLog(messages, current_url_, "Must only contain ASCII characters.", 454 delegate->AddMessage(current_url, "Must only contain ASCII characters.",
184 CONSOLE_MESSAGE_LEVEL_ERROR); 455 CONSOLE_MESSAGE_LEVEL_ERROR);
185 return false; 456 return false;
186 } 457 }
187 458
188 std::unique_ptr<base::Value> parsed_header = base::JSONReader::Read(header); 459 std::unique_ptr<base::Value> parsed_header = base::JSONReader::Read(header);
189 460
190 if (!parsed_header) { 461 if (!parsed_header) {
191 ConsoleLog(messages, current_url_, "Not a valid JSON.", 462 delegate->AddMessage(current_url, "Not a valid JSON.",
mmenke 2017/06/05 22:39:36 "Not valid JSON." or just "Invalid JSON." or, to
msramek 2017/06/06 18:20:57 Done.
192 CONSOLE_MESSAGE_LEVEL_ERROR); 463 CONSOLE_MESSAGE_LEVEL_ERROR);
193 return false; 464 return false;
194 } 465 }
195 466
196 const base::DictionaryValue* dictionary = nullptr; 467 const base::DictionaryValue* dictionary = nullptr;
197 const base::ListValue* types = nullptr; 468 const base::ListValue* types = nullptr;
198 if (!parsed_header->GetAsDictionary(&dictionary) || 469 if (!parsed_header->GetAsDictionary(&dictionary) ||
199 !dictionary->GetListWithoutPathExpansion(kTypesKey, &types)) { 470 !dictionary->GetListWithoutPathExpansion(kTypesKey, &types)) {
200 ConsoleLog(messages, current_url_, 471 delegate->AddMessage(current_url,
201 "Expecting a JSON dictionary with a 'types' field.", 472 "Expecting a JSON dictionary with a 'types' field.",
mmenke 2017/06/05 22:39:36 optional: I think "Expected" may be a little more
msramek 2017/06/06 18:20:57 Done. Yes, I just realized that when I was address
202 CONSOLE_MESSAGE_LEVEL_ERROR); 473 CONSOLE_MESSAGE_LEVEL_ERROR);
203 return false; 474 return false;
204 } 475 }
205 476
206 DCHECK(types); 477 DCHECK(types);
207 478
208 *clear_cookies = false; 479 *clear_cookies = false;
209 *clear_storage = false; 480 *clear_storage = false;
210 *clear_cache = false; 481 *clear_cache = false;
211 482
212 std::vector<std::string> type_names; 483 std::string type_names;
213 for (const base::Value& value : *types) { 484 for (const base::Value& value : *types) {
214 std::string type; 485 std::string type;
215 value.GetAsString(&type); 486 value.GetAsString(&type);
216 487
217 bool* datatype = nullptr; 488 bool* datatype = nullptr;
218 489
219 if (type == "cookies") { 490 if (type == kDatatypeCookies) {
220 datatype = clear_cookies; 491 datatype = clear_cookies;
221 } else if (type == "storage") { 492 } else if (type == kDatatypeStorage) {
222 datatype = clear_storage; 493 datatype = clear_storage;
223 } else if (type == "cache") { 494 } else if (type == kDatatypeCache) {
224 datatype = clear_cache; 495 datatype = clear_cache;
225 } else { 496 } else {
226 std::string serialized_type; 497 std::string serialized_type;
227 JSONStringValueSerializer serializer(&serialized_type); 498 JSONStringValueSerializer serializer(&serialized_type);
228 serializer.Serialize(value); 499 serializer.Serialize(value);
229 ConsoleLog( 500 delegate->AddMessage(
230 messages, current_url_, 501 current_url,
231 base::StringPrintf("Invalid type: %s.", serialized_type.c_str()), 502 base::StringPrintf("Unrecognized type: %s.", serialized_type.c_str()),
232 CONSOLE_MESSAGE_LEVEL_ERROR); 503 CONSOLE_MESSAGE_LEVEL_ERROR);
233 continue; 504 continue;
234 } 505 }
235 506
507 DCHECK(datatype);
508
236 // Each data type should only be processed once. 509 // Each data type should only be processed once.
237 DCHECK(datatype);
238 if (*datatype) 510 if (*datatype)
239 continue; 511 continue;
240 512
241 *datatype = true; 513 *datatype = true;
242 type_names.push_back(type); 514 if (!type_names.empty())
515 type_names += kConsoleMessageDatatypeSeparator;
516 type_names += type;
243 } 517 }
244 518
245 if (!*clear_cookies && !*clear_storage && !*clear_cache) { 519 if (!*clear_cookies && !*clear_storage && !*clear_cache) {
246 ConsoleLog(messages, current_url_, 520 delegate->AddMessage(current_url,
247 "No valid types specified in the 'types' field.", 521 "No recognized types specified in the 'types' field.",
248 CONSOLE_MESSAGE_LEVEL_ERROR); 522 CONSOLE_MESSAGE_LEVEL_ERROR);
249 return false; 523 return false;
250 } 524 }
251 525
252 // Pretty-print which types are to be cleared. 526 // Pretty-print which types are to be cleared.
253 std::string output; 527 delegate->AddMessage(
254 switch (type_names.size()) { 528 current_url,
255 case 1: 529 base::StringPrintf(kConsoleMessageCleared, type_names.c_str()),
256 output = base::StringPrintf(kClearingOneType, type_names[0].c_str()); 530 CONSOLE_MESSAGE_LEVEL_INFO);
257 break;
258 case 2:
259 output = base::StringPrintf(kClearingTwoTypes, type_names[0].c_str(),
260 type_names[1].c_str());
261 break;
262 case 3:
263 output = base::StringPrintf(kClearingThreeTypes, type_names[0].c_str(),
264 type_names[1].c_str(), type_names[2].c_str());
265 break;
266 default:
267 NOTREACHED();
268 }
269 ConsoleLog(messages, current_url_, output, CONSOLE_MESSAGE_LEVEL_INFO);
270 531
271 return true; 532 return true;
272 } 533 }
273 534
535 void ClearSiteDataThrottle::ExecuteClearingTask(const url::Origin& origin,
536 bool clear_cookies,
537 bool clear_storage,
538 bool clear_cache,
539 base::OnceClosure callback) {
540 DCHECK_CURRENTLY_ON(BrowserThread::IO);
541 BrowserThread::PostTask(
542 BrowserThread::UI, FROM_HERE,
543 base::BindOnce(&UIThreadSiteDataClearer::Run,
544 ResourceRequestInfo::ForRequest(request_)
545 ->GetWebContentsGetterForRequest(),
546 origin, clear_cookies, clear_storage, clear_cache,
547 std::move(callback)));
548 }
549
274 void ClearSiteDataThrottle::TaskFinished() { 550 void ClearSiteDataThrottle::TaskFinished() {
275 DCHECK(clearing_in_progress_); 551 DCHECK_CURRENTLY_ON(BrowserThread::IO);
276 clearing_in_progress_ = false; 552 DCHECK(!clearing_started_.is_null());
277 553
278 UMA_HISTOGRAM_CUSTOM_TIMES("Navigation.ClearSiteData.Duration", 554 UMA_HISTOGRAM_CUSTOM_TIMES("Navigation.ClearSiteData.Duration",
279 base::TimeTicks::Now() - clearing_started_, 555 base::TimeTicks::Now() - clearing_started_,
280 base::TimeDelta::FromMilliseconds(1), 556 base::TimeDelta::FromMilliseconds(1),
281 base::TimeDelta::FromSeconds(1), 50); 557 base::TimeDelta::FromSeconds(1), 50);
282 558
283 navigation_handle()->Resume(); 559 // For subresource requests, console messages are output immediately.
560 if (!IsNavigationRequest(request_))
561 OutputConsoleMessages();
mmenke 2017/06/05 22:39:36 optional: Could make this into a utility function
msramek 2017/06/06 18:20:57 OutputConsoleMessages() is called 4 times actually
562
563 Resume();
564 }
565
566 void ClearSiteDataThrottle::OutputConsoleMessages() {
567 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request_);
568 if (info)
569 delegate_->OutputMessages(info->GetWebContentsGetterForRequest());
284 } 570 }
285 571
286 } // namespace content 572 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698