1 // Copyright (c) 2012 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 "content/browser/host_zoom_map_impl.h"
6
7 #include <algorithm>
8 #include <cmath>
9 #include <memory>
10 #include <utility>
11
12 #include "base/strings/string_piece.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/time/default_clock.h"
15 #include "base/values.h"
16 #include "content/browser/renderer_host/navigation_entry_impl.h"
17 #include "content/browser/renderer_host/render_process_host_impl.h"
18 #include "content/browser/renderer_host/render_view_host_impl.h"
19 #include "content/browser/web_contents/web_contents_impl.h"
20 #include "content/public/browser/browser_context.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/render_frame_host.h"
23 #include "content/public/browser/resource_context.h"
24 #include "content/public/browser/site_instance.h"
25 #include "content/public/browser/storage_partition.h"
26 #include "content/public/common/url_constants.h"
27 #include "net/base/url_util.h"
28 #include "third_party/blink/public/common/page/page_zoom.h"
29
30 namespace content {
31
32 namespace {
33
GetHostFromProcessView(int render_process_id,int render_view_id)34 std::string GetHostFromProcessView(int render_process_id, int render_view_id) {
35 DCHECK_CURRENTLY_ON(BrowserThread::UI);
36 RenderViewHost* render_view_host =
37 RenderViewHost::FromID(render_process_id, render_view_id);
38 if (!render_view_host)
39 return std::string();
40
41 WebContents* web_contents = WebContents::FromRenderViewHost(render_view_host);
42
43 NavigationEntry* entry =
44 web_contents->GetController().GetLastCommittedEntry();
45 if (!entry)
46 return std::string();
47
48 return net::GetHostOrSpecFromURL(HostZoomMap::GetURLFromEntry(entry));
49 }
50
51 } // namespace
52
GetURLFromEntry(NavigationEntry * entry)53 GURL HostZoomMap::GetURLFromEntry(NavigationEntry* entry) {
54 DCHECK_CURRENTLY_ON(BrowserThread::UI);
55 switch (entry->GetPageType()) {
56 case PAGE_TYPE_ERROR:
57 return GURL(kUnreachableWebDataURL);
58 // TODO(wjmaclean): In future, give interstitial pages special treatment as
59 // well.
60 default:
61 return entry->GetURL();
62 }
63 }
64
GetDefaultForBrowserContext(BrowserContext * context)65 HostZoomMap* HostZoomMap::GetDefaultForBrowserContext(BrowserContext* context) {
66 DCHECK_CURRENTLY_ON(BrowserThread::UI);
67 StoragePartition* partition =
68 BrowserContext::GetDefaultStoragePartition(context);
69 DCHECK(partition);
70 return partition->GetHostZoomMap();
71 }
72
Get(SiteInstance * instance)73 HostZoomMap* HostZoomMap::Get(SiteInstance* instance) {
74 DCHECK_CURRENTLY_ON(BrowserThread::UI);
75 StoragePartition* partition = BrowserContext::GetStoragePartition(
76 instance->GetBrowserContext(), instance);
77 DCHECK(partition);
78 return partition->GetHostZoomMap();
79 }
80
GetForWebContents(WebContents * contents)81 HostZoomMap* HostZoomMap::GetForWebContents(WebContents* contents) {
82 DCHECK_CURRENTLY_ON(BrowserThread::UI);
83 // TODO(wjmaclean): Update this behaviour to work with OOPIF.
84 // See crbug.com/528407.
85 StoragePartition* partition =
86 BrowserContext::GetStoragePartition(contents->GetBrowserContext(),
87 contents->GetSiteInstance());
88 DCHECK(partition);
89 return partition->GetHostZoomMap();
90 }
91
92 // Helper function for setting/getting zoom levels for WebContents without
93 // having to import HostZoomMapImpl everywhere.
GetZoomLevel(WebContents * web_contents)94 double HostZoomMap::GetZoomLevel(WebContents* web_contents) {
95 DCHECK_CURRENTLY_ON(BrowserThread::UI);
96 HostZoomMapImpl* host_zoom_map = static_cast<HostZoomMapImpl*>(
97 HostZoomMap::GetForWebContents(web_contents));
98 return host_zoom_map->GetZoomLevelForWebContents(
99 static_cast<WebContentsImpl*>(web_contents));
100 }
101
PageScaleFactorIsOne(WebContents * web_contents)102 bool HostZoomMap::PageScaleFactorIsOne(WebContents* web_contents) {
103 DCHECK_CURRENTLY_ON(BrowserThread::UI);
104 HostZoomMapImpl* host_zoom_map = static_cast<HostZoomMapImpl*>(
105 HostZoomMap::GetForWebContents(web_contents));
106 return host_zoom_map->PageScaleFactorIsOneForWebContents(
107 static_cast<WebContentsImpl*>(web_contents));
108 }
109
SetZoomLevel(WebContents * web_contents,double level)110 void HostZoomMap::SetZoomLevel(WebContents* web_contents, double level) {
111 DCHECK_CURRENTLY_ON(BrowserThread::UI);
112 HostZoomMapImpl* host_zoom_map = static_cast<HostZoomMapImpl*>(
113 HostZoomMap::GetForWebContents(web_contents));
114 host_zoom_map->SetZoomLevelForWebContents(
115 static_cast<WebContentsImpl*>(web_contents), level);
116 }
117
SendErrorPageZoomLevelRefresh(WebContents * web_contents)118 void HostZoomMap::SendErrorPageZoomLevelRefresh(WebContents* web_contents) {
119 DCHECK_CURRENTLY_ON(BrowserThread::UI);
120 HostZoomMapImpl* host_zoom_map =
121 static_cast<HostZoomMapImpl*>(HostZoomMap::GetDefaultForBrowserContext(
122 web_contents->GetBrowserContext()));
123 host_zoom_map->SendErrorPageZoomLevelRefresh();
124 }
125
HostZoomMapImpl()126 HostZoomMapImpl::HostZoomMapImpl()
127 : default_zoom_level_(0.0),
128 clock_(base::DefaultClock::GetInstance()) {
129 DCHECK_CURRENTLY_ON(BrowserThread::UI);
130 }
131
CopyFrom(HostZoomMap * copy_interface)132 void HostZoomMapImpl::CopyFrom(HostZoomMap* copy_interface) {
133 DCHECK_CURRENTLY_ON(BrowserThread::UI);
134 HostZoomMapImpl* copy = static_cast<HostZoomMapImpl*>(copy_interface);
135 host_zoom_levels_.insert(copy->host_zoom_levels_.begin(),
136 copy->host_zoom_levels_.end());
137 for (const auto& it : copy->scheme_host_zoom_levels_) {
138 const std::string& host = it.first;
139 scheme_host_zoom_levels_[host] = HostZoomLevels();
140 scheme_host_zoom_levels_[host].insert(it.second.begin(), it.second.end());
141 }
142 default_zoom_level_ = copy->default_zoom_level_;
143 }
144
GetZoomLevelForHost(const std::string & host) const145 double HostZoomMapImpl::GetZoomLevelForHost(const std::string& host) const {
146 DCHECK_CURRENTLY_ON(BrowserThread::UI);
147 const auto it = host_zoom_levels_.find(host);
148 return it != host_zoom_levels_.end() ? it->second.level : default_zoom_level_;
149 }
150
HasZoomLevel(const std::string & scheme,const std::string & host)151 bool HostZoomMapImpl::HasZoomLevel(const std::string& scheme,
152 const std::string& host) {
153 DCHECK_CURRENTLY_ON(BrowserThread::UI);
154 auto scheme_iterator(scheme_host_zoom_levels_.find(scheme));
155
156 const HostZoomLevels& zoom_levels =
157 (scheme_iterator != scheme_host_zoom_levels_.end())
158 ? scheme_iterator->second
159 : host_zoom_levels_;
160
161 return base::Contains(zoom_levels, host);
162 }
163
GetZoomLevelForHostAndScheme(const std::string & scheme,const std::string & host)164 double HostZoomMapImpl::GetZoomLevelForHostAndScheme(const std::string& scheme,
165 const std::string& host) {
166 DCHECK_CURRENTLY_ON(BrowserThread::UI);
167 auto scheme_iterator(scheme_host_zoom_levels_.find(scheme));
168 if (scheme_iterator != scheme_host_zoom_levels_.end()) {
169 auto i(scheme_iterator->second.find(host));
170 if (i != scheme_iterator->second.end())
171 return i->second.level;
172 }
173
174 return GetZoomLevelForHost(host);
175 }
176
GetAllZoomLevels()177 HostZoomMap::ZoomLevelVector HostZoomMapImpl::GetAllZoomLevels() {
178 DCHECK_CURRENTLY_ON(BrowserThread::UI);
179 HostZoomMap::ZoomLevelVector result;
180 result.reserve(host_zoom_levels_.size() + scheme_host_zoom_levels_.size());
181 for (const auto& entry : host_zoom_levels_) {
182 ZoomLevelChange change = {
183 HostZoomMap::ZOOM_CHANGED_FOR_HOST,
184 entry.first, // host
185 std::string(), // scheme
186 entry.second.level, // zoom level
187 entry.second.last_modified // last modified
188 };
189 result.push_back(change);
190 }
191 for (const auto& scheme_entry : scheme_host_zoom_levels_) {
192 const std::string& scheme = scheme_entry.first;
193 const HostZoomLevels& host_zoom_levels = scheme_entry.second;
194 for (const auto& entry : host_zoom_levels) {
195 ZoomLevelChange change = {
196 HostZoomMap::ZOOM_CHANGED_FOR_SCHEME_AND_HOST,
197 entry.first, // host
198 scheme, // scheme
199 entry.second.level, // zoom level
200 entry.second.last_modified // last modified
201 };
202 result.push_back(change);
203 }
204 }
205 return result;
206 }
207
SetZoomLevelForHost(const std::string & host,double level)208 void HostZoomMapImpl::SetZoomLevelForHost(const std::string& host,
209 double level) {
210 DCHECK_CURRENTLY_ON(BrowserThread::UI);
211 base::Time last_modified = clock_->Now();
212 SetZoomLevelForHostInternal(host, level, last_modified);
213 }
214
InitializeZoomLevelForHost(const std::string & host,double level,base::Time last_modified)215 void HostZoomMapImpl::InitializeZoomLevelForHost(const std::string& host,
216 double level,
217 base::Time last_modified) {
218 DCHECK_CURRENTLY_ON(BrowserThread::UI);
219 SetZoomLevelForHostInternal(host, level, last_modified);
220 }
221
SetZoomLevelForHostInternal(const std::string & host,double level,base::Time last_modified)222 void HostZoomMapImpl::SetZoomLevelForHostInternal(const std::string& host,
223 double level,
224 base::Time last_modified) {
225 DCHECK_CURRENTLY_ON(BrowserThread::UI);
226
227 if (blink::PageZoomValuesEqual(level, default_zoom_level_)) {
228 host_zoom_levels_.erase(host);
229 } else {
230 ZoomLevel& zoomLevel = host_zoom_levels_[host];
231 zoomLevel.level = level;
232 zoomLevel.last_modified = last_modified;
233 }
234
235 // TODO(wjmaclean) Should we use a GURL here? crbug.com/384486
236 SendZoomLevelChange(std::string(), host);
237
238 HostZoomMap::ZoomLevelChange change;
239 change.mode = HostZoomMap::ZOOM_CHANGED_FOR_HOST;
240 change.host = host;
241 change.zoom_level = level;
242 change.last_modified = last_modified;
243
244 zoom_level_changed_callbacks_.Notify(change);
245 }
246
SetZoomLevelForHostAndScheme(const std::string & scheme,const std::string & host,double level)247 void HostZoomMapImpl::SetZoomLevelForHostAndScheme(const std::string& scheme,
248 const std::string& host,
249 double level) {
250 DCHECK_CURRENTLY_ON(BrowserThread::UI);
251 // No last_modified timestamp for scheme and host because they are
252 // not persistet and are used for special cases only.
253 scheme_host_zoom_levels_[scheme][host].level = level;
254
255 SendZoomLevelChange(scheme, host);
256
257 HostZoomMap::ZoomLevelChange change;
258 change.mode = HostZoomMap::ZOOM_CHANGED_FOR_SCHEME_AND_HOST;
259 change.host = host;
260 change.scheme = scheme;
261 change.zoom_level = level;
262 change.last_modified = base::Time();
263
264 zoom_level_changed_callbacks_.Notify(change);
265 }
266
GetDefaultZoomLevel()267 double HostZoomMapImpl::GetDefaultZoomLevel() {
268 DCHECK_CURRENTLY_ON(BrowserThread::UI);
269 return default_zoom_level_;
270 }
271
SetDefaultZoomLevel(double level)272 void HostZoomMapImpl::SetDefaultZoomLevel(double level) {
273 DCHECK_CURRENTLY_ON(BrowserThread::UI);
274
275 if (blink::PageZoomValuesEqual(level, default_zoom_level_))
276 return;
277
278 default_zoom_level_ = level;
279
280 // First, remove all entries that match the new default zoom level.
281 for (auto it = host_zoom_levels_.begin(); it != host_zoom_levels_.end();) {
282 if (blink::PageZoomValuesEqual(it->second.level, default_zoom_level_))
283 it = host_zoom_levels_.erase(it);
284 else
285 it++;
286 }
287
288 // Second, update zoom levels for all pages that do not have an overriding
289 // entry.
290 for (auto* web_contents : WebContentsImpl::GetAllWebContents()) {
291 // Only change zoom for WebContents tied to the StoragePartition this
292 // HostZoomMap serves.
293 if (GetForWebContents(web_contents) != this)
294 continue;
295
296 int render_process_id =
297 web_contents->GetRenderViewHost()->GetProcess()->GetID();
298 int render_view_id = web_contents->GetRenderViewHost()->GetRoutingID();
299
300 // Get the url from the navigation controller directly, as calling
301 // WebContentsImpl::GetLastCommittedURL() may give us a virtual url that
302 // is different than the one stored in the map.
303 GURL url;
304 std::string host;
305 std::string scheme;
306
307 NavigationEntry* entry =
308 web_contents->GetController().GetLastCommittedEntry();
309 // It is possible for a WebContent's zoom level to be queried before
310 // a navigation has occurred.
311 if (entry) {
312 url = GetURLFromEntry(entry);
313 scheme = url.scheme();
314 host = net::GetHostOrSpecFromURL(url);
315 }
316
317 bool uses_default_zoom =
318 !HasZoomLevel(scheme, host) &&
319 !UsesTemporaryZoomLevel(render_process_id, render_view_id);
320
321 if (uses_default_zoom) {
322 web_contents->UpdateZoom();
323
324 HostZoomMap::ZoomLevelChange change;
325 change.mode = HostZoomMap::ZOOM_CHANGED_FOR_HOST;
326 change.host = host;
327 change.zoom_level = level;
328
329 zoom_level_changed_callbacks_.Notify(change);
330 }
331 }
332 }
333
334 std::unique_ptr<HostZoomMap::Subscription>
AddZoomLevelChangedCallback(ZoomLevelChangedCallback callback)335 HostZoomMapImpl::AddZoomLevelChangedCallback(
336 ZoomLevelChangedCallback callback) {
337 DCHECK_CURRENTLY_ON(BrowserThread::UI);
338 return zoom_level_changed_callbacks_.Add(std::move(callback));
339 }
340
GetZoomLevelForWebContents(WebContentsImpl * web_contents_impl)341 double HostZoomMapImpl::GetZoomLevelForWebContents(
342 WebContentsImpl* web_contents_impl) {
343 DCHECK_CURRENTLY_ON(BrowserThread::UI);
344 int render_process_id =
345 web_contents_impl->GetRenderViewHost()->GetProcess()->GetID();
346 int routing_id = web_contents_impl->GetRenderViewHost()->GetRoutingID();
347
348 if (UsesTemporaryZoomLevel(render_process_id, routing_id))
349 return GetTemporaryZoomLevel(render_process_id, routing_id);
350
351 // Get the url from the navigation controller directly, as calling
352 // WebContentsImpl::GetLastCommittedURL() may give us a virtual url that
353 // is different than is stored in the map.
354 GURL url;
355 NavigationEntry* entry =
356 web_contents_impl->GetController().GetLastCommittedEntry();
357 // It is possible for a WebContent's zoom level to be queried before
358 // a navigation has occurred.
359 if (entry)
360 url = GetURLFromEntry(entry);
361 return GetZoomLevelForHostAndScheme(url.scheme(),
362 net::GetHostOrSpecFromURL(url));
363 }
364
SetZoomLevelForWebContents(WebContentsImpl * web_contents_impl,double level)365 void HostZoomMapImpl::SetZoomLevelForWebContents(
366 WebContentsImpl* web_contents_impl,
367 double level) {
368 DCHECK_CURRENTLY_ON(BrowserThread::UI);
369 int render_process_id =
370 web_contents_impl->GetRenderViewHost()->GetProcess()->GetID();
371 int render_view_id = web_contents_impl->GetRenderViewHost()->GetRoutingID();
372 if (UsesTemporaryZoomLevel(render_process_id, render_view_id)) {
373 SetTemporaryZoomLevel(render_process_id, render_view_id, level);
374 } else {
375 // Get the url from the navigation controller directly, as calling
376 // WebContentsImpl::GetLastCommittedURL() may give us a virtual url that
377 // is different than what the render view is using. If the two don't match,
378 // the attempt to set the zoom will fail.
379 NavigationEntry* entry =
380 web_contents_impl->GetController().GetLastCommittedEntry();
381 // Tests may invoke this function with a null entry, but we don't
382 // want to save zoom levels in this case.
383 if (!entry)
384 return;
385
386 GURL url = GetURLFromEntry(entry);
387 SetZoomLevelForHost(net::GetHostOrSpecFromURL(url), level);
388 }
389 }
390
SetPageScaleFactorIsOneForView(int render_process_id,int render_view_id,bool is_one)391 void HostZoomMapImpl::SetPageScaleFactorIsOneForView(int render_process_id,
392 int render_view_id,
393 bool is_one) {
394 DCHECK_CURRENTLY_ON(BrowserThread::UI);
395 view_page_scale_factors_are_one_[RenderViewKey(render_process_id,
396 render_view_id)] = is_one;
397 HostZoomMap::ZoomLevelChange change;
398 change.mode = HostZoomMap::PAGE_SCALE_IS_ONE_CHANGED;
399 zoom_level_changed_callbacks_.Notify(change);
400 }
401
PageScaleFactorIsOneForWebContents(WebContentsImpl * web_contents_impl) const402 bool HostZoomMapImpl::PageScaleFactorIsOneForWebContents(
403 WebContentsImpl* web_contents_impl) const {
404 DCHECK_CURRENTLY_ON(BrowserThread::UI);
405 if (!web_contents_impl->GetRenderViewHost()->GetProcess())
406 return true;
407
408 const auto it = view_page_scale_factors_are_one_.find(RenderViewKey(
409 web_contents_impl->GetRenderViewHost()->GetProcess()->GetID(),
410 web_contents_impl->GetRenderViewHost()->GetRoutingID()));
411 return it != view_page_scale_factors_are_one_.end() ? it->second : true;
412 }
413
ClearPageScaleFactorIsOneForView(int render_process_id,int render_view_id)414 void HostZoomMapImpl::ClearPageScaleFactorIsOneForView(int render_process_id,
415 int render_view_id) {
416 DCHECK_CURRENTLY_ON(BrowserThread::UI);
417 view_page_scale_factors_are_one_.erase(
418 RenderViewKey(render_process_id, render_view_id));
419 }
420
UsesTemporaryZoomLevel(int render_process_id,int render_view_id)421 bool HostZoomMapImpl::UsesTemporaryZoomLevel(int render_process_id,
422 int render_view_id) {
423 DCHECK_CURRENTLY_ON(BrowserThread::UI);
424 RenderViewKey key(render_process_id, render_view_id);
425 return base::Contains(temporary_zoom_levels_, key);
426 }
427
GetTemporaryZoomLevel(int render_process_id,int render_view_id) const428 double HostZoomMapImpl::GetTemporaryZoomLevel(int render_process_id,
429 int render_view_id) const {
430 DCHECK_CURRENTLY_ON(BrowserThread::UI);
431 RenderViewKey key(render_process_id, render_view_id);
432 const auto it = temporary_zoom_levels_.find(key);
433 return it != temporary_zoom_levels_.end() ? it->second : 0;
434 }
435
SetTemporaryZoomLevel(int render_process_id,int render_view_id,double level)436 void HostZoomMapImpl::SetTemporaryZoomLevel(int render_process_id,
437 int render_view_id,
438 double level) {
439 DCHECK_CURRENTLY_ON(BrowserThread::UI);
440
441 RenderViewKey key(render_process_id, render_view_id);
442 temporary_zoom_levels_[key] = level;
443
444 WebContentsImpl* web_contents =
445 static_cast<WebContentsImpl*>(WebContents::FromRenderViewHost(
446 RenderViewHost::FromID(render_process_id, render_view_id)));
447 web_contents->UpdateZoom();
448
449 HostZoomMap::ZoomLevelChange change;
450 change.mode = HostZoomMap::ZOOM_CHANGED_TEMPORARY_ZOOM;
451 change.host = GetHostFromProcessView(render_process_id, render_view_id);
452 change.zoom_level = level;
453
454 zoom_level_changed_callbacks_.Notify(change);
455 }
456
ClearZoomLevels(base::Time delete_begin,base::Time delete_end)457 void HostZoomMapImpl::ClearZoomLevels(base::Time delete_begin,
458 base::Time delete_end) {
459 DCHECK_CURRENTLY_ON(BrowserThread::UI);
460 double default_zoom_level = GetDefaultZoomLevel();
461 for (const auto& zoom_level : GetAllZoomLevels()) {
462 if (zoom_level.scheme.empty() && delete_begin <= zoom_level.last_modified &&
463 (delete_end.is_null() || zoom_level.last_modified < delete_end)) {
464 SetZoomLevelForHost(zoom_level.host, default_zoom_level);
465 }
466 }
467 }
468
ClearTemporaryZoomLevel(int render_process_id,int render_view_id)469 void HostZoomMapImpl::ClearTemporaryZoomLevel(int render_process_id,
470 int render_view_id) {
471 DCHECK_CURRENTLY_ON(BrowserThread::UI);
472 RenderViewKey key(render_process_id, render_view_id);
473 auto it = temporary_zoom_levels_.find(key);
474 if (it == temporary_zoom_levels_.end())
475 return;
476
477 temporary_zoom_levels_.erase(it);
478 WebContentsImpl* web_contents =
479 static_cast<WebContentsImpl*>(WebContents::FromRenderViewHost(
480 RenderViewHost::FromID(render_process_id, render_view_id)));
481 web_contents->UpdateZoom();
482 }
483
SendZoomLevelChange(const std::string & scheme,const std::string & host)484 void HostZoomMapImpl::SendZoomLevelChange(const std::string& scheme,
485 const std::string& host) {
486 DCHECK_CURRENTLY_ON(BrowserThread::UI);
487 // We'll only send to WebContents not using temporary zoom levels. The one
488 // other case of interest is where the renderer is hosting a plugin document;
489 // that should be reflected in our temporary zoom level map, but we will
490 // double check on the renderer side to avoid the possibility of any races.
491 for (auto* web_contents : WebContentsImpl::GetAllWebContents()) {
492 // Only send zoom level changes to WebContents that are using this
493 // HostZoomMap.
494 if (GetForWebContents(web_contents) != this)
495 continue;
496
497 int render_process_id =
498 web_contents->GetRenderViewHost()->GetProcess()->GetID();
499 int render_view_id = web_contents->GetRenderViewHost()->GetRoutingID();
500
501 if (!UsesTemporaryZoomLevel(render_process_id, render_view_id))
502 web_contents->UpdateZoomIfNecessary(scheme, host);
503 }
504 }
505
SendErrorPageZoomLevelRefresh()506 void HostZoomMapImpl::SendErrorPageZoomLevelRefresh() {
507 DCHECK_CURRENTLY_ON(BrowserThread::UI);
508 GURL error_url(kUnreachableWebDataURL);
509 std::string host = net::GetHostOrSpecFromURL(error_url);
510
511 SendZoomLevelChange(std::string(), host);
512 }
513
WillCloseRenderView(int render_process_id,int render_view_id)514 void HostZoomMapImpl::WillCloseRenderView(int render_process_id,
515 int render_view_id) {
516 DCHECK_CURRENTLY_ON(BrowserThread::UI);
517 ClearTemporaryZoomLevel(render_process_id, render_view_id);
518 ClearPageScaleFactorIsOneForView(render_process_id, render_view_id);
519 }
520
~HostZoomMapImpl()521 HostZoomMapImpl::~HostZoomMapImpl() {
522 DCHECK_CURRENTLY_ON(BrowserThread::UI);
523 }
524
SetClockForTesting(base::Clock * clock)525 void HostZoomMapImpl::SetClockForTesting(base::Clock* clock) {
526 clock_ = clock;
527 }
528
529 } // namespace content
530