Yining Wang | 4c6d1e0 | 2019-04-10 21:07:01 | [diff] [blame] | 1 | // Copyright 2019 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "chrome/browser/ui/manifest_web_app_browser_controller.h" |
| 6 | |
Yining Wang | 40eae8f | 2019-05-02 19:04:15 | [diff] [blame] | 7 | #include "chrome/browser/profiles/profile.h" |
Yining Wang | 40eae8f | 2019-05-02 19:04:15 | [diff] [blame] | 8 | #include "chrome/browser/ssl/security_state_tab_helper.h" |
Yining Wang | 4c6d1e0 | 2019-04-10 21:07:01 | [diff] [blame] | 9 | #include "chrome/browser/ui/browser.h" |
Evan Stade | 2229a56 | 2021-01-12 18:30:07 | [diff] [blame] | 10 | #include "components/webapps/browser/installable/installable_manager.h" |
Yining Wang | 4c6d1e0 | 2019-04-10 21:07:01 | [diff] [blame] | 11 | #include "content/public/browser/navigation_entry.h" |
Yining Wang | 40eae8f | 2019-05-02 19:04:15 | [diff] [blame] | 12 | #include "content/public/common/url_constants.h" |
| 13 | #include "extensions/common/constants.h" |
Frédéric Wang | 073e74a | 2020-12-16 17:43:32 | [diff] [blame] | 14 | #include "services/network/public/cpp/is_potentially_trustworthy.h" |
Dana Fried | 5876c78d | 2019-12-25 00:12:22 | [diff] [blame] | 15 | #include "third_party/blink/public/common/manifest/manifest.h" |
Yining Wang | 4c6d1e0 | 2019-04-10 21:07:01 | [diff] [blame] | 16 | #include "ui/gfx/favicon_size.h" |
| 17 | #include "ui/gfx/image/image_skia.h" |
| 18 | #include "url/gurl.h" |
| 19 | |
| 20 | ManifestWebAppBrowserController::ManifestWebAppBrowserController( |
| 21 | Browser* browser) |
Dana Fried | 5876c78d | 2019-12-25 00:12:22 | [diff] [blame] | 22 | : AppBrowserController(browser, /*app_id=*/base::nullopt) {} |
Yining Wang | 4c6d1e0 | 2019-04-10 21:07:01 | [diff] [blame] | 23 | |
| 24 | ManifestWebAppBrowserController::~ManifestWebAppBrowserController() = default; |
| 25 | |
Eric Willigers | 6aadc64 | 2019-11-02 00:17:33 | [diff] [blame] | 26 | bool ManifestWebAppBrowserController::HasMinimalUiButtons() const { |
| 27 | return false; |
| 28 | } |
| 29 | |
Alan Cutter | a2cb8d7 | 2019-07-28 22:59:13 | [diff] [blame] | 30 | bool ManifestWebAppBrowserController::ShouldShowCustomTabBar() const { |
Yining Wang | 40eae8f | 2019-05-02 19:04:15 | [diff] [blame] | 31 | content::WebContents* web_contents = |
| 32 | browser()->tab_strip_model()->GetActiveWebContents(); |
| 33 | |
Alan Cutter | a2cb8d7 | 2019-07-28 22:59:13 | [diff] [blame] | 34 | // Don't show until a navigation has occurred. |
Yining Wang | 40eae8f | 2019-05-02 19:04:15 | [diff] [blame] | 35 | if (!web_contents || web_contents->GetLastCommittedURL().is_empty()) |
| 36 | return false; |
| 37 | |
Alan Cutter | a2cb8d7 | 2019-07-28 22:59:13 | [diff] [blame] | 38 | // Show if the web_contents is not on a secure origin. |
Frédéric Wang | 073e74a | 2020-12-16 17:43:32 | [diff] [blame] | 39 | if (!network::IsUrlPotentiallyTrustworthy(app_start_url_)) |
Yining Wang | 40eae8f | 2019-05-02 19:04:15 | [diff] [blame] | 40 | return true; |
Yining Wang | 40eae8f | 2019-05-02 19:04:15 | [diff] [blame] | 41 | |
Alan Cutter | a2cb8d7 | 2019-07-28 22:59:13 | [diff] [blame] | 42 | // Show if web_contents is not currently in scope. |
Jay Harris | 4e0a6c7 | 2019-07-08 23:29:01 | [diff] [blame] | 43 | if (!IsUrlInAppScope(web_contents->GetLastCommittedURL()) || |
| 44 | !IsUrlInAppScope(web_contents->GetVisibleURL())) { |
Yining Wang | 40eae8f | 2019-05-02 19:04:15 | [diff] [blame] | 45 | return true; |
| 46 | } |
| 47 | |
Alan Cutter | a2cb8d7 | 2019-07-28 22:59:13 | [diff] [blame] | 48 | // Show if on a insecure external website. This checks the security level, |
| 49 | // different from IsOriginSecure which just checks the origin itself. |
Evan Stade | 57f160f | 2020-12-08 15:59:37 | [diff] [blame] | 50 | if (!webapps::InstallableManager::IsContentSecure(web_contents)) |
Yining Wang | 40eae8f | 2019-05-02 19:04:15 | [diff] [blame] | 51 | return true; |
| 52 | |
Yining Wang | 4c6d1e0 | 2019-04-10 21:07:01 | [diff] [blame] | 53 | return false; |
| 54 | } |
| 55 | |
Yining Wang | 4c6d1e0 | 2019-04-10 21:07:01 | [diff] [blame] | 56 | gfx::ImageSkia ManifestWebAppBrowserController::GetWindowAppIcon() const { |
| 57 | gfx::ImageSkia page_icon = browser()->GetCurrentPageIcon().AsImageSkia(); |
| 58 | if (!page_icon.isNull()) |
| 59 | return page_icon; |
| 60 | |
| 61 | // The extension icon may be loading still. Return a transparent icon rather |
| 62 | // than using a placeholder to avoid flickering. |
| 63 | SkBitmap bitmap; |
| 64 | bitmap.allocN32Pixels(gfx::kFaviconSize, gfx::kFaviconSize); |
| 65 | bitmap.eraseColor(SK_ColorTRANSPARENT); |
| 66 | return gfx::ImageSkia::CreateFrom1xBitmap(bitmap); |
| 67 | } |
| 68 | |
| 69 | gfx::ImageSkia ManifestWebAppBrowserController::GetWindowIcon() const { |
| 70 | return browser()->GetCurrentPageIcon().AsImageSkia(); |
| 71 | } |
| 72 | |
Mugdha Lakhani | 23954ef2 | 2020-06-25 21:56:21 | [diff] [blame] | 73 | base::string16 ManifestWebAppBrowserController::GetAppShortName() const { |
| 74 | return base::string16(); |
Yining Wang | 4c6d1e0 | 2019-04-10 21:07:01 | [diff] [blame] | 75 | } |
| 76 | |
| 77 | base::string16 ManifestWebAppBrowserController::GetFormattedUrlOrigin() const { |
Alan Cutter | c658135b4 | 2020-09-28 23:09:53 | [diff] [blame] | 78 | return FormatUrlOrigin(GetAppStartUrl()); |
Yining Wang | 4c6d1e0 | 2019-04-10 21:07:01 | [diff] [blame] | 79 | } |
| 80 | |
Alan Cutter | c658135b4 | 2020-09-28 23:09:53 | [diff] [blame] | 81 | GURL ManifestWebAppBrowserController::GetAppStartUrl() const { |
| 82 | return app_start_url_; |
Yining Wang | 40eae8f | 2019-05-02 19:04:15 | [diff] [blame] | 83 | } |
| 84 | |
Jay Harris | 4e0a6c7 | 2019-07-08 23:29:01 | [diff] [blame] | 85 | bool ManifestWebAppBrowserController::IsUrlInAppScope(const GURL& url) const { |
Dana Fried | 5876c78d | 2019-12-25 00:12:22 | [diff] [blame] | 86 | // Prefer to use manifest scope URL if available; fall back to app launch URL |
| 87 | // if not available. Manifest fallback is always launch URL minus filename, |
| 88 | // query, and fragment. |
| 89 | const GURL scope_url = !manifest_scope_.is_empty() |
| 90 | ? manifest_scope_ |
Alan Cutter | c658135b4 | 2020-09-28 23:09:53 | [diff] [blame] | 91 | : GetAppStartUrl().GetWithoutFilename(); |
Dana Fried | 5876c78d | 2019-12-25 00:12:22 | [diff] [blame] | 92 | |
| 93 | return IsInScope(url, scope_url); |
Jay Harris | 4e0a6c7 | 2019-07-08 23:29:01 | [diff] [blame] | 94 | } |
| 95 | |
Yining Wang | 40eae8f | 2019-05-02 19:04:15 | [diff] [blame] | 96 | void ManifestWebAppBrowserController::OnTabInserted( |
| 97 | content::WebContents* contents) { |
Dana Fried | 5876c78d | 2019-12-25 00:12:22 | [diff] [blame] | 98 | // Since we are experimenting with multi-tab PWAs, we only try to load the |
| 99 | // manifest if this is the first web contents being loaded in this window. |
| 100 | DCHECK(!browser()->tab_strip_model()->empty()); |
| 101 | if (browser()->tab_strip_model()->count() == 1) { |
Alan Cutter | c658135b4 | 2020-09-28 23:09:53 | [diff] [blame] | 102 | app_start_url_ = contents->GetURL(); |
Dana Fried | 5876c78d | 2019-12-25 00:12:22 | [diff] [blame] | 103 | contents->GetManifest( |
| 104 | base::BindOnce(&ManifestWebAppBrowserController::OnManifestLoaded, |
| 105 | weak_factory_.GetWeakPtr())); |
| 106 | } |
Eric Willigers | 4a5f7a9 | 2019-05-10 19:19:26 | [diff] [blame] | 107 | AppBrowserController::OnTabInserted(contents); |
Alan Cutter | a2cb8d7 | 2019-07-28 22:59:13 | [diff] [blame] | 108 | UpdateCustomTabBarVisibility(false); |
Yining Wang | 4c6d1e0 | 2019-04-10 21:07:01 | [diff] [blame] | 109 | } |
Dana Fried | 5876c78d | 2019-12-25 00:12:22 | [diff] [blame] | 110 | |
| 111 | void ManifestWebAppBrowserController::OnManifestLoaded( |
| 112 | const GURL& manifest_url, |
| 113 | const blink::Manifest& manifest) { |
| 114 | manifest_scope_ = manifest.scope; |
| 115 | } |
| 116 | |
| 117 | // static |
| 118 | bool ManifestWebAppBrowserController::IsInScope(const GURL& url, |
| 119 | const GURL& scope) { |
| 120 | if (!url::IsSameOriginWith(scope, url)) |
| 121 | return false; |
| 122 | |
| 123 | std::string scope_path = scope.path(); |
| 124 | if (base::EndsWith(scope_path, "/", base::CompareCase::SENSITIVE)) |
| 125 | scope_path = scope_path.substr(0, scope_path.length() - 1); |
| 126 | |
| 127 | const std::string url_path = url.path(); |
| 128 | return url_path == scope_path || |
| 129 | base::StartsWith(url_path, scope_path + "/", |
| 130 | base::CompareCase::SENSITIVE); |
| 131 | } |