/
LocationBarModel.java
833 lines (726 loc) · 33 KB
/
LocationBarModel.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.toolbar;
import android.content.Context;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.LruCache;
import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
import org.chromium.base.FeatureList;
import org.chromium.base.ObserverList;
import org.chromium.base.TraceEvent;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.layouts.LayoutStateProvider;
import org.chromium.chrome.browser.omnibox.ChromeAutocompleteSchemeClassifier;
import org.chromium.chrome.browser.omnibox.LocationBarDataProvider;
import org.chromium.chrome.browser.omnibox.NewTabPageDelegate;
import org.chromium.chrome.browser.omnibox.SearchEngineLogoUtils;
import org.chromium.chrome.browser.omnibox.UrlBarData;
import org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider;
import org.chromium.chrome.browser.paint_preview.TabbedPaintPreview;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TrustedCdn;
import org.chromium.chrome.browser.theme.ThemeUtils;
import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
import org.chromium.chrome.features.start_surface.StartSurfaceState;
import org.chromium.components.browser_ui.styles.ChromeColors;
import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
import org.chromium.components.embedder_support.util.UrlConstants;
import org.chromium.components.embedder_support.util.UrlUtilities;
import org.chromium.components.metrics.OmniboxEventProtos.OmniboxEventProto.PageClassification;
import org.chromium.components.omnibox.AutocompleteSchemeClassifier;
import org.chromium.components.omnibox.OmniboxUrlEmphasizer;
import org.chromium.components.omnibox.SecurityStatusIcon;
import org.chromium.components.security_state.ConnectionSecurityLevel;
import org.chromium.components.security_state.SecurityStateModel;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.url.GURL;
import org.chromium.url.URI;
import java.util.Objects;
/**
* Provides a way of accessing toolbar data and state.
*/
public class LocationBarModel implements ToolbarDataProvider, LocationBarDataProvider {
private static final int LRU_CACHE_SIZE = 10;
static class SpannableDisplayTextCacheKey {
@NonNull
private final String mUrl;
@NonNull
private final String mDisplayText;
private final int mSecurityLevel;
private final int mNonEmphasizedColor;
private final int mEmphasizedColor;
private final int mDangerColor;
private final int mSecureColor;
private SpannableDisplayTextCacheKey(@NonNull String url, @NonNull String displayText,
int securityLevel, int nonEmphasizedColor, int emphasizedColor, int dangerColor,
int secureColor) {
mUrl = url;
mDisplayText = displayText;
mSecurityLevel = securityLevel;
mNonEmphasizedColor = nonEmphasizedColor;
mEmphasizedColor = emphasizedColor;
mDangerColor = dangerColor;
mSecureColor = secureColor;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SpannableDisplayTextCacheKey that = (SpannableDisplayTextCacheKey) o;
return mSecurityLevel == that.mSecurityLevel
&& mNonEmphasizedColor == that.mNonEmphasizedColor
&& mEmphasizedColor == that.mEmphasizedColor
&& mDangerColor == that.mDangerColor && mSecureColor == that.mSecureColor
&& mUrl.equals(that.mUrl) && mDisplayText.equals(that.mDisplayText);
}
@Override
public int hashCode() {
return Objects.hash(mUrl, mDisplayText, mSecurityLevel, mNonEmphasizedColor,
mEmphasizedColor, mDangerColor, mSecureColor);
}
}
/**
* Formats the given URL to the original one of a distillation.
*/
@FunctionalInterface
public interface UrlFormatter {
String format(GURL url);
}
/**
* Provides non-primary incognito profile.
*/
@FunctionalInterface
public interface ProfileProvider {
Profile getNonPrimaryOtrProfile(WindowAndroid window);
}
/**
* Offline-related status of a given content.
*/
public interface OfflineStatus {
/**
* Returns whether the WebContents is showing trusted offline page.
*/
default boolean isShowingTrustedOfflinePage(Tab tab) {
return false;
}
/**
* Checks if an offline page is shown for the tab.
*/
default boolean isOfflinePage(Tab tab) {
return false;
}
}
private final Context mContext;
private final NewTabPageDelegate mNtpDelegate;
private final @NonNull UrlFormatter mUrlFormatter;
private final @NonNull ProfileProvider mProfileProvider;
private final @NonNull OfflineStatus mOfflineStatus;
private final SearchEngineLogoUtils mSearchEngineLogoUtils;
// Always null if optimizations are disabled. Otherwise, non-null and unchanging following
// native init. Always tied to the mLastUsedNonOTRProfile which is safe because no underlying
// services have an incognito-specific instance.
@Nullable
private AutocompleteSchemeClassifier mChromeAutocompleteSchemeClassifier;
// Non-null and unchanging following native init. The last used non-OTR (or regular) profile
// can't change after this point because we don't support multi-profile on Android.
@Nullable
private Profile mLastUsedNonOTRProfile;
@Nullable
private LruCache<SpannableDisplayTextCacheKey, SpannableStringBuilder>
mSpannableDisplayTextCache;
private boolean mOptimizationsEnabled;
private Tab mTab;
private int mPrimaryColor;
private LayoutStateProvider mLayoutStateProvider;
private boolean mIsIncognito;
private boolean mIsUsingBrandColor;
private boolean mShouldShowOmniboxInOverviewMode;
private boolean mIsShowingTabSwitcher;
@StartSurfaceState
private int mStartSurfaceState;
private long mNativeLocationBarModelAndroid;
private ObserverList<LocationBarDataProvider.Observer> mLocationBarDataObservers =
new ObserverList<>();
protected GURL mVisibleGurl = GURL.emptyGURL();
protected String mFormattedFullUrl;
protected String mUrlForDisplay;
/**
* Default constructor for this class.
* @param context The Context used for styling the toolbar visuals.
* @param newTabPageDelegate Delegate used to access NTP.
* @param urlFormatter Formatter returning the formatted version of the original version
* of URL of a distillation.
* @param profileProvider Interface returning non-primary OTR profile.
* @param offlineStatus Offline-related status provider.
* @param searchEngineLogoUtils Utils to query the state of the search engine logos feature.
*/
public LocationBarModel(Context context, NewTabPageDelegate newTabPageDelegate,
@NonNull UrlFormatter urlFormatter, @NonNull ProfileProvider profileProvider,
@NonNull OfflineStatus offlineStatus,
@NonNull SearchEngineLogoUtils searchEngineLogoUtils) {
mContext = context;
mNtpDelegate = newTabPageDelegate;
mUrlFormatter = urlFormatter;
mProfileProvider = profileProvider;
mOfflineStatus = offlineStatus;
mPrimaryColor = ChromeColors.getDefaultThemeColor(context, false);
mSearchEngineLogoUtils = searchEngineLogoUtils;
}
/**
* Handle any initialization that must occur after native has been initialized.
*/
public void initializeWithNative() {
mOptimizationsEnabled =
ChromeFeatureList.isEnabled(ChromeFeatureList.ANDROID_SCROLL_OPTIMIZATIONS);
mLastUsedNonOTRProfile = Profile.getLastUsedRegularProfile();
mNativeLocationBarModelAndroid = LocationBarModelJni.get().init(LocationBarModel.this);
if (mOptimizationsEnabled) {
mSpannableDisplayTextCache = new LruCache<>(LRU_CACHE_SIZE);
mChromeAutocompleteSchemeClassifier =
new ChromeAutocompleteSchemeClassifier(getProfile());
recalculateFormattedUrls();
}
}
/**
* Destroys the native LocationBarModel.
*/
public void destroy() {
if (mChromeAutocompleteSchemeClassifier != null) {
mChromeAutocompleteSchemeClassifier.destroy();
mChromeAutocompleteSchemeClassifier = null;
}
if (mNativeLocationBarModelAndroid == 0) return;
LocationBarModelJni.get().destroy(mNativeLocationBarModelAndroid, LocationBarModel.this);
mNativeLocationBarModelAndroid = 0;
}
/**
* @return The currently active WebContents being used by the Toolbar.
*/
@CalledByNative
private WebContents getActiveWebContents() {
if (!hasTab()) return null;
return mTab.getWebContents();
}
/**
* Sets the tab that contains the information to be displayed in the toolbar.
*
* @param tab The tab associated currently with the toolbar.
* @param isIncognito Whether the incognito model is currently selected, which must match the
* passed in tab if non-null.
*/
public void setTab(Tab tab, boolean isIncognito) {
assert tab == null || tab.isIncognito() == isIncognito;
mTab = tab;
if (mIsIncognito != isIncognito) {
mIsIncognito = isIncognito;
notifyIncognitoStateChanged();
}
updateUsingBrandColor();
notifyTitleChanged();
notifyUrlChanged();
notifyPrimaryColorChanged();
notifySecurityStateChanged();
}
@Override
public Tab getTab() {
return hasTab() ? mTab : null;
}
@Override
public boolean hasTab() {
// TODO(https://crbug.com/1147131): Remove the isInitialized() and isDestroyed checks when
// we no longer wait for TAB_CLOSED events to remove this tab. Otherwise there is a chance
// we use this tab after {@link Tab#destroy()} is called.
return mTab != null && mTab.isInitialized() && !mTab.isDestroyed();
}
@Override
public void addObserver(LocationBarDataProvider.Observer observer) {
mLocationBarDataObservers.addObserver(observer);
}
@Override
public void removeObserver(LocationBarDataProvider.Observer observer) {
mLocationBarDataObservers.removeObserver(observer);
}
@Override
// TODO(https://crbug.com/1305374): migrate to GURL.
@Deprecated
public String getCurrentUrl() {
return getCurrentGurl().getSpec().trim();
}
@Override
public GURL getCurrentGurl() {
if (isInOverviewAndShowingOmnibox()) {
return UrlConstants.ntpGurl();
}
if (mOptimizationsEnabled) {
return mVisibleGurl;
}
Tab tab = getTab();
return tab != null && tab.isInitialized() ? tab.getUrl() : GURL.emptyGURL();
}
@VisibleForTesting
void updateVisibleGurl() {
if (!mOptimizationsEnabled) return;
try (TraceEvent te = TraceEvent.scoped("LocationBarModel.updateVisibleGurl")) {
if (isInOverviewAndShowingOmnibox()) {
mFormattedFullUrl = "";
mUrlForDisplay = "";
mVisibleGurl = UrlConstants.ntpGurl();
return;
}
GURL gurl = getUrlOfVisibleNavigationEntry();
if (!gurl.equals(mVisibleGurl)) {
mVisibleGurl = gurl;
recalculateFormattedUrls();
}
}
}
public void notifyUrlChanged() {
updateVisibleGurl();
for (LocationBarDataProvider.Observer observer : mLocationBarDataObservers) {
observer.onUrlChanged();
}
}
public void notifyZeroSuggestRefresh() {
for (LocationBarDataProvider.Observer observer : mLocationBarDataObservers) {
observer.hintZeroSuggestRefresh();
}
}
@Override
public NewTabPageDelegate getNewTabPageDelegate() {
return mNtpDelegate;
}
void notifyNtpStartedLoading() {
for (Observer observer : mLocationBarDataObservers) {
observer.onNtpStartedLoading();
}
}
@Override
public UrlBarData getUrlBarData() {
// Part of scroll jank investigation http://crbug.com/905461. Will remove TraceEvent after
// the investigation is complete.
try (TraceEvent te = TraceEvent.scoped("LocationBarModel.getUrlBarData")) {
if (!hasTab()) {
return UrlBarData.EMPTY;
}
GURL gurl = getCurrentGurl();
if (!UrlBarData.shouldShowUrl(gurl, isIncognito())) {
return UrlBarData.EMPTY;
}
String url = gurl.getSpec().trim();
boolean isOfflinePage = isOfflinePage();
String formattedUrl = getFormattedFullUrl();
if (mTab.isFrozen()) return buildUrlBarData(url, isOfflinePage, formattedUrl);
if (DomDistillerUrlUtils.isDistilledPage(url)) {
GURL originalUrl =
DomDistillerUrlUtils.getOriginalUrlFromDistillerUrl(new GURL(url));
return buildUrlBarData(mUrlFormatter.format(originalUrl), isOfflinePage);
}
if (isOfflinePage) {
GURL originalUrl = mTab.getOriginalUrl();
formattedUrl = UrlUtilities.stripScheme(mUrlFormatter.format(originalUrl));
// Clear the editing text for untrusted offline pages.
if (!mOfflineStatus.isShowingTrustedOfflinePage(mTab)) {
return buildUrlBarData(url, true, formattedUrl, "");
}
return buildUrlBarData(url, true, formattedUrl);
}
String urlForDisplay = getUrlForDisplay();
if (!urlForDisplay.equals(formattedUrl)) {
return buildUrlBarData(url, false, urlForDisplay, formattedUrl);
}
return buildUrlBarData(url, false, formattedUrl);
}
}
private UrlBarData buildUrlBarData(String url, boolean isOfflinePage) {
return buildUrlBarData(url, isOfflinePage, url, url);
}
private UrlBarData buildUrlBarData(String url, boolean isOfflinePage, String displayText) {
return buildUrlBarData(url, isOfflinePage, displayText, displayText);
}
private UrlBarData buildUrlBarData(
String url, boolean isOfflinePage, String displayText, String editingText) {
SpannableStringBuilder spannableDisplayText = new SpannableStringBuilder(displayText);
if (mNativeLocationBarModelAndroid != 0 && spannableDisplayText.length() > 0
&& shouldEmphasizeUrl()) {
final @BrandedColorScheme int brandedColorScheme =
OmniboxResourceProvider.getBrandedColorScheme(
mContext, isIncognito(), getPrimaryColor());
final @ColorInt int nonEmphasizedColor =
OmniboxResourceProvider.getUrlBarSecondaryTextColor(
mContext, brandedColorScheme);
final @ColorInt int emphasizedColor =
OmniboxResourceProvider.getUrlBarPrimaryTextColor(mContext, brandedColorScheme);
final @ColorInt int dangerColor =
OmniboxResourceProvider.getUrlBarDangerColor(mContext, brandedColorScheme);
final @ColorInt int secureColor =
OmniboxResourceProvider.getUrlBarSecureColor(mContext, brandedColorScheme);
AutocompleteSchemeClassifier autocompleteSchemeClassifier;
int securityLevel = getSecurityLevel(getTab(), isOfflinePage);
SpannableDisplayTextCacheKey cacheKey =
new SpannableDisplayTextCacheKey(url, displayText, securityLevel,
nonEmphasizedColor, emphasizedColor, dangerColor, secureColor);
SpannableStringBuilder cachedSpannableDisplayText = null;
if (mOptimizationsEnabled) {
autocompleteSchemeClassifier = mChromeAutocompleteSchemeClassifier;
cachedSpannableDisplayText = mSpannableDisplayTextCache.get(cacheKey);
} else {
autocompleteSchemeClassifier = new ChromeAutocompleteSchemeClassifier(getProfile());
}
try {
if (cachedSpannableDisplayText != null) {
return UrlBarData.forUrlAndText(url, cachedSpannableDisplayText, editingText);
} else {
OmniboxUrlEmphasizer.emphasizeUrl(spannableDisplayText,
autocompleteSchemeClassifier, getSecurityLevel(),
shouldEmphasizeHttpsScheme(), nonEmphasizedColor, emphasizedColor,
dangerColor, secureColor);
if (mOptimizationsEnabled) {
mSpannableDisplayTextCache.put(cacheKey, spannableDisplayText);
}
}
} finally {
if (!mOptimizationsEnabled) {
autocompleteSchemeClassifier.destroy();
}
}
}
return UrlBarData.forUrlAndText(url, spannableDisplayText, editingText);
}
/**
* @return True if the displayed URL should be emphasized, false if the displayed text
* already has formatting for emphasis applied.
*/
private boolean shouldEmphasizeUrl() {
// If the toolbar shows the publisher URL, it applies its own formatting for emphasis.
if (mTab == null) return true;
return TrustedCdn.getPublisherUrl(mTab) == null;
}
@VisibleForTesting
LruCache<SpannableDisplayTextCacheKey, SpannableStringBuilder> getCacheForTesting() {
return mSpannableDisplayTextCache;
}
/**
* @return Whether the light security theme should be used.
*/
@VisibleForTesting
public boolean shouldEmphasizeHttpsScheme() {
return !isUsingBrandColor() && !isIncognito();
}
@Override
public String getTitle() {
if (!hasTab()) return "";
String title = getTab().getTitle();
return TextUtils.isEmpty(title) ? title : title.trim();
}
public void notifyTitleChanged() {
for (LocationBarDataProvider.Observer observer : mLocationBarDataObservers) {
observer.onTitleChanged();
}
}
@Override
public boolean isIncognito() {
return mIsIncognito;
}
private void notifyIncognitoStateChanged() {
for (LocationBarDataProvider.Observer observer : mLocationBarDataObservers) {
observer.onIncognitoStateChanged();
}
}
/**
* Returns whether the location bar is showing and the app is in overview mode. "Overview mode"
* here is a catchall for "UI steady state without a selected tab." In practice, there are only
* two possible scenarios for overview mode: the start surface and the tab switcher, the latter
* of which does not show the omnibox. This effectively means that this method only returns true
* when the start surface homepage is showing.
*/
@Override
public boolean isInOverviewAndShowingOmnibox() {
if (!mShouldShowOmniboxInOverviewMode) return false;
return mLayoutStateProvider != null && mIsShowingTabSwitcher
&& (mStartSurfaceState == StartSurfaceState.SHOWN_HOMEPAGE
|| mStartSurfaceState == StartSurfaceState.SHOWING_HOMEPAGE
|| mStartSurfaceState == StartSurfaceState.SHOWING_START);
}
/**
* @return Whether the location bar should show when in overview mode.
*/
@Override
public boolean shouldShowLocationBarInOverviewMode() {
return mShouldShowOmniboxInOverviewMode;
}
@Override
public Profile getProfile() {
if (mIsIncognito) {
WindowAndroid windowAndroid = (mTab != null) ? mTab.getWindowAndroid() : null;
// If the mTab belongs to a CustomTabActivity then we return the non-primary OTR profile
// which is associated with it. For all other cases we return the primary OTR profile.
Profile nonPrimaryOtrProfile = mProfileProvider.getNonPrimaryOtrProfile(windowAndroid);
if (nonPrimaryOtrProfile != null) return nonPrimaryOtrProfile;
// When in overview mode with no open tabs, there has not been created an
// OTR profile yet.
assert mLastUsedNonOTRProfile.hasPrimaryOTRProfile() || isInOverviewAndShowingOmnibox();
// Return the primary OTR profile.
return mLastUsedNonOTRProfile.getPrimaryOTRProfile(/*createIfNeeded=*/true);
}
return mLastUsedNonOTRProfile;
}
public void setLayoutStateProvider(LayoutStateProvider layoutStateProvider) {
mLayoutStateProvider = layoutStateProvider;
}
public void setShouldShowOmniboxInOverviewMode(boolean shouldShowOmniboxInOverviewMode) {
if (mShouldShowOmniboxInOverviewMode != shouldShowOmniboxInOverviewMode) {
mShouldShowOmniboxInOverviewMode = shouldShowOmniboxInOverviewMode;
notifyPrimaryColorChanged();
}
}
/**
* Sets the primary color and changes the state for isUsingBrandColor.
* @param color The primary color for the current tab.
*/
public void setPrimaryColor(int color) {
mPrimaryColor = color;
updateUsingBrandColor();
notifyPrimaryColorChanged();
}
private void updateUsingBrandColor() {
mIsUsingBrandColor = !isIncognito()
&& mPrimaryColor != ChromeColors.getDefaultThemeColor(mContext, isIncognito())
&& hasTab() && !mTab.isNativePage();
}
@Override
public int getPrimaryColor() {
return isInOverviewAndShowingOmnibox()
? ChromeColors.getDefaultThemeColor(mContext, isIncognito())
: mPrimaryColor;
}
@Override
public boolean isUsingBrandColor() {
// If the overview is visible, force use of primary color, which is also overridden when the
// overview is visible.
return isInOverviewAndShowingOmnibox() || mIsUsingBrandColor;
}
public void notifyPrimaryColorChanged() {
for (LocationBarDataProvider.Observer observer : mLocationBarDataObservers) {
observer.onPrimaryColorChanged();
}
}
@Override
public boolean isOfflinePage() {
// Start Surface homepage is not bond with a tab and mTab is kept as the previous tab if
// homepage is shown. |!isInOverviewAndShowingOmnibox()| is added here to make sure Start
// Surface homepage is not regarded as offline.
return hasTab() && mOfflineStatus.isOfflinePage(mTab) && !isInOverviewAndShowingOmnibox();
}
@Override
public boolean isPaintPreview() {
// Start Surface homepage is not bound with a tab and mTab is kept as the previous tab if
// the homepage is shown. This is added here to make sure Start Surface homepage is not
// regarded as a paint preview.
if (isInOverviewAndShowingOmnibox()) return false;
return hasTab() && TabbedPaintPreview.get(mTab).isShowing();
}
@Override
public int getSecurityLevel() {
return getSecurityLevel(getTab(), isOfflinePage());
}
@Override
public int getPageClassification(boolean isFocusedFromFakebox, boolean isPrefetch) {
if (mNativeLocationBarModelAndroid == 0) return PageClassification.INVALID_SPEC_VALUE;
// Provide NTP as page class in overview mode (when Start Surface is enabled). No call
// to the backend necessary or possible, since there is no tab or navigation entry.
if (isInOverviewAndShowingOmnibox()) return PageClassification.NTP_VALUE;
return LocationBarModelJni.get().getPageClassification(mNativeLocationBarModelAndroid,
LocationBarModel.this, isFocusedFromFakebox, isPrefetch);
}
@Override
public @DrawableRes int getSecurityIconResource(boolean isTablet) {
boolean isOfflinePage = isOfflinePage();
return getSecurityIconResource(getSecurityLevel(getTab(), isOfflinePage), !isTablet,
isOfflinePage, isPaintPreview());
}
@Override
@StringRes
public int getSecurityIconContentDescriptionResourceId() {
return SecurityStatusIcon.getSecurityIconContentDescriptionResourceId(getSecurityLevel());
}
@VisibleForTesting
@ConnectionSecurityLevel
int getSecurityLevel(Tab tab, boolean isOfflinePage) {
if (tab == null || isOfflinePage || isInOverviewAndShowingOmnibox()) {
return ConnectionSecurityLevel.NONE;
}
@Nullable
String publisherUrl = TrustedCdn.getPublisherUrl(tab);
if (publisherUrl != null) {
assert getSecurityLevelFromStateModel(tab.getWebContents())
!= ConnectionSecurityLevel.DANGEROUS;
return (URI.create(publisherUrl).getScheme().equals(UrlConstants.HTTPS_SCHEME))
? ConnectionSecurityLevel.SECURE
: ConnectionSecurityLevel.WARNING;
}
return getSecurityLevelFromStateModel(tab.getWebContents());
}
@VisibleForTesting
@ConnectionSecurityLevel
int getSecurityLevelFromStateModel(WebContents webContents) {
int securityLevel = SecurityStateModel.getSecurityLevelForWebContents(webContents);
return securityLevel;
}
@VisibleForTesting
@DrawableRes
int getSecurityIconResource(int securityLevel, boolean isSmallDevice, boolean isOfflinePage,
boolean isPaintPreview) {
// Paint Preview appears on top of WebContents and shows a visual representation of the page
// that has been previously stored locally.
if (isPaintPreview) return R.drawable.omnibox_info;
// Checking for a preview first because one possible preview type is showing an offline page
// on a slow connection. In this case, the previews UI takes precedence.
if (isOfflinePage) {
return R.drawable.ic_offline_pin_24dp;
}
// Return early if native initialization hasn't been done yet.
if ((securityLevel == ConnectionSecurityLevel.NONE
|| securityLevel == ConnectionSecurityLevel.WARNING)
&& mNativeLocationBarModelAndroid == 0) {
return R.drawable.omnibox_info;
}
boolean skipIconForNeutralState =
!mSearchEngineLogoUtils.shouldShowSearchEngineLogo(isIncognito())
|| mNtpDelegate.isCurrentlyVisible() || isInOverviewAndShowingOmnibox();
boolean useUpdatedConnectionSecurityIndicators = FeatureList.isInitialized()
&& ChromeFeatureList.isEnabled(
ChromeFeatureList.OMNIBOX_UPDATED_CONNECTION_SECURITY_INDICATORS)
&& !(hasTab() && mTab.isCustomTab());
return SecurityStatusIcon.getSecurityIconResource(securityLevel, isSmallDevice,
skipIconForNeutralState, useUpdatedConnectionSecurityIndicators);
}
@Override
public @ColorRes int getSecurityIconColorStateList() {
final @ColorInt int color = getPrimaryColor();
final @BrandedColorScheme int brandedColorScheme =
OmniboxResourceProvider.getBrandedColorScheme(mContext, isIncognito(), color);
// Assign red color to security icon if the page shows security warning.
return getSecurityIconColorWithSecurityLevel(
getSecurityLevel(), brandedColorScheme, isIncognito());
}
/**
* Get the color for the security icon for different security levels.
* If we are using dark background (dark mode or incognito mode), we should return light red.
* If we are using light background (light mode, but not LIGHT_BRANDED_THEME), we should return
* dark red. The default brand color will be returned if no change is needed.
*
* @param connectionSecurityLevel The connection security level for the current website.
* @param brandedColorScheme The branded color scheme for the omnibox.
* @param isIncognito Whether the tab is in Incognito mode.
* @return The color resource for the security icon, returns -1 if doe snot need to change
* color.
*/
@VisibleForTesting
protected @ColorRes int getSecurityIconColorWithSecurityLevel(
@ConnectionSecurityLevel int connectionSecurityLevel,
@BrandedColorScheme int brandedColorScheme, boolean isIncognito) {
// Return regular color scheme if the website does not show warning.
if (connectionSecurityLevel == ConnectionSecurityLevel.DANGEROUS) {
// Assign red color only on light or dark background including Incognito mode.
// We will not change the security icon to red when BrandedColorScheme is
// LIGHT_BRANDED_THEME for the purpose of improving contrast.
if (isIncognito) {
// Use light red for Incognito mode.
return R.color.baseline_error_200;
} else if (brandedColorScheme == BrandedColorScheme.APP_DEFAULT) {
// Use adaptive red for light and dark background.
return R.color.default_red;
}
}
return ThemeUtils.getThemedToolbarIconTintRes(brandedColorScheme);
}
public void notifySecurityStateChanged() {
@ConnectionSecurityLevel
int securityLevel = getSecurityLevel();
if (securityLevel == ConnectionSecurityLevel.DANGEROUS) {
recalculateFormattedUrls();
}
for (LocationBarDataProvider.Observer observer : mLocationBarDataObservers) {
observer.onSecurityStateChanged();
}
}
private void recalculateFormattedUrls() {
mFormattedFullUrl = calculateFormattedFullUrl();
mUrlForDisplay = calculateUrlForDisplay();
}
private String getFormattedFullUrl() {
if (mOptimizationsEnabled) {
return mFormattedFullUrl;
}
return calculateFormattedFullUrl();
}
private String getUrlForDisplay() {
if (mOptimizationsEnabled) {
return mUrlForDisplay;
}
return calculateUrlForDisplay();
}
/** @return The formatted URL suitable for editing. */
protected String calculateFormattedFullUrl() {
if (mNativeLocationBarModelAndroid == 0) return "";
return LocationBarModelJni.get().getFormattedFullURL(
mNativeLocationBarModelAndroid, LocationBarModel.this);
}
/** @return The formatted URL suitable for display only. */
protected String calculateUrlForDisplay() {
if (mNativeLocationBarModelAndroid == 0) return "";
return LocationBarModelJni.get().getURLForDisplay(
mNativeLocationBarModelAndroid, LocationBarModel.this);
}
protected GURL getUrlOfVisibleNavigationEntry() {
if (mNativeLocationBarModelAndroid == 0) return GURL.emptyGURL();
return LocationBarModelJni.get().getUrlOfVisibleNavigationEntry(
mNativeLocationBarModelAndroid, LocationBarModel.this);
}
/**
* Set whether tab switcher is showing or not and notify changes.
* @param isShowingTabSwitcher Whether tab switcher is showing or not.
*/
public void setIsShowingTabSwitcher(boolean isShowingTabSwitcher) {
mIsShowingTabSwitcher = isShowingTabSwitcher;
notifyTitleChanged();
notifyUrlChanged();
notifyPrimaryColorChanged();
notifySecurityStateChanged();
}
/**
* Sets the current start surface state, which can be used to distinguish between e.g. the
* start-based tab switcher and the start surface homepage.
*/
public void setStartSurfaceState(@StartSurfaceState int startSurfaceState) {
mStartSurfaceState = startSurfaceState;
notifyUrlChanged();
}
@NativeMethods
interface Natives {
long init(LocationBarModel caller);
void destroy(long nativeLocationBarModelAndroid, LocationBarModel caller);
String getFormattedFullURL(long nativeLocationBarModelAndroid, LocationBarModel caller);
String getURLForDisplay(long nativeLocationBarModelAndroid, LocationBarModel caller);
GURL getUrlOfVisibleNavigationEntry(
long nativeLocationBarModelAndroid, LocationBarModel caller);
int getPageClassification(long nativeLocationBarModelAndroid, LocationBarModel caller,
boolean isFocusedFromFakebox, boolean isPrefetch);
}
}