| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/bookmarks/bookmark_utils.h" |
| |
| #include <stddef.h> |
| |
| #include "base/check.h" |
| #include "base/notreached.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/bookmarks/bookmark_model_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/search/search.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "components/bookmarks/browser/bookmark_model.h" |
| #include "components/bookmarks/browser/bookmark_node_data.h" |
| #include "components/bookmarks/common/bookmark_pref_names.h" |
| #include "components/dom_distiller/core/url_constants.h" |
| #include "components/dom_distiller/core/url_utils.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/search/search.h" |
| #include "components/url_formatter/url_formatter.h" |
| #include "components/user_prefs/user_prefs.h" |
| #include "components/vector_icons/vector_icons.h" |
| #include "content/public/browser/web_contents.h" |
| #include "ui/base/dragdrop/drag_drop_types.h" |
| #include "ui/base/dragdrop/drop_target_event.h" |
| #include "ui/base/pointer/touch_ui_controller.h" |
| |
| #if defined(TOOLKIT_VIEWS) |
| #include "ui/gfx/canvas.h" |
| #include "ui/gfx/color_utils.h" |
| #include "ui/gfx/image/image_skia_source.h" |
| #include "ui/gfx/paint_vector_icon.h" |
| #include "ui/gfx/scoped_canvas.h" |
| #endif |
| |
| #if defined(OS_WIN) || defined(OS_MAC) |
| #include "chrome/grit/theme_resources.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/resources/grit/ui_resources.h" |
| #endif |
| |
| using bookmarks::BookmarkModel; |
| using bookmarks::BookmarkNode; |
| |
| namespace chrome { |
| |
| namespace { |
| |
| #if defined(TOOLKIT_VIEWS) |
| // Image source that flips the supplied source image in RTL. |
| class RTLFlipSource : public gfx::ImageSkiaSource { |
| public: |
| explicit RTLFlipSource(gfx::ImageSkia source) : source_(std::move(source)) {} |
| ~RTLFlipSource() override = default; |
| |
| // gfx::ImageSkiaSource: |
| gfx::ImageSkiaRep GetImageForScale(float scale) override { |
| gfx::Canvas canvas(source_.size(), scale, false); |
| gfx::ScopedCanvas scoped_canvas(&canvas); |
| scoped_canvas.FlipIfRTL(source_.width()); |
| canvas.DrawImageInt(source_, 0, 0); |
| return gfx::ImageSkiaRep(canvas.GetBitmap(), scale); |
| } |
| |
| private: |
| const gfx::ImageSkia source_; |
| }; |
| |
| #if !defined(OS_WIN) && !defined(OS_MAC) |
| gfx::ImageSkia GetFolderIcon(const gfx::VectorIcon& icon, SkColor text_color) { |
| return gfx::CreateVectorIcon(icon, |
| color_utils::DeriveDefaultIconColor(text_color)); |
| } |
| #endif // !defined(OS_WIN) && !defined(OS_MAC) |
| #endif // defined(TOOLKIT_VIEWS) |
| |
| } // namespace |
| |
| GURL GetURLToBookmark(content::WebContents* web_contents) { |
| DCHECK(web_contents); |
| if (search::IsInstantNTP(web_contents)) |
| return GURL(kChromeUINewTabURL); |
| // Users cannot bookmark Reader Mode pages directly, so the bookmark |
| // interaction is as if it were with the original page. |
| if (dom_distiller::url_utils::IsDistilledPage(web_contents->GetURL())) { |
| return dom_distiller::url_utils::GetOriginalUrlFromDistillerUrl( |
| web_contents->GetURL()); |
| } |
| return web_contents->GetURL(); |
| } |
| |
| void GetURLAndTitleToBookmark(content::WebContents* web_contents, |
| GURL* url, |
| base::string16* title) { |
| *url = GetURLToBookmark(web_contents); |
| if (dom_distiller::url_utils::IsDistilledPage(web_contents->GetURL())) { |
| // Users cannot bookmark Reader Mode pages directly. Instead, a bookmark |
| // is added for the original page and original title. |
| *title = |
| base::UTF8ToUTF16(dom_distiller::url_utils::GetTitleFromDistillerUrl( |
| web_contents->GetURL())); |
| } else { |
| *title = web_contents->GetTitle(); |
| } |
| } |
| |
| void ToggleBookmarkBarWhenVisible(content::BrowserContext* browser_context) { |
| PrefService* prefs = user_prefs::UserPrefs::Get(browser_context); |
| const bool always_show = |
| !prefs->GetBoolean(bookmarks::prefs::kShowBookmarkBar); |
| |
| // The user changed when the bookmark bar is shown, update the preferences. |
| prefs->SetBoolean(bookmarks::prefs::kShowBookmarkBar, always_show); |
| } |
| |
| base::string16 FormatBookmarkURLForDisplay(const GURL& url) { |
| // Because this gets re-parsed by FixupURL(), it's safe to omit the scheme |
| // and trailing slash, and unescape most characters. However, it's |
| // important not to drop any username/password, or unescape anything that |
| // changes the URL's meaning. |
| url_formatter::FormatUrlTypes format_types = |
| url_formatter::kFormatUrlOmitDefaults & |
| ~url_formatter::kFormatUrlOmitUsernamePassword; |
| |
| // If username is present, we must not omit the scheme because FixupURL() will |
| // subsequently interpret the username as a scheme. crbug.com/639126 |
| if (url.has_username()) |
| format_types &= ~url_formatter::kFormatUrlOmitHTTP; |
| |
| return url_formatter::FormatUrl(url, format_types, net::UnescapeRule::SPACES, |
| nullptr, nullptr, nullptr); |
| } |
| |
| bool IsAppsShortcutEnabled(Profile* profile) { |
| // Legacy supervised users can not have apps installed currently so there's no |
| // need to show the apps shortcut. |
| if (profile->IsLegacySupervised()) |
| return false; |
| |
| #if defined(OS_CHROMEOS) |
| // Chrome OS uses the app list / app launcher. |
| return false; |
| #else |
| return search::IsInstantExtendedAPIEnabled() && !profile->IsOffTheRecord(); |
| #endif |
| } |
| |
| bool ShouldShowAppsShortcutInBookmarkBar(Profile* profile) { |
| return IsAppsShortcutEnabled(profile) && |
| profile->GetPrefs()->GetBoolean( |
| bookmarks::prefs::kShowAppsShortcutInBookmarkBar); |
| } |
| |
| int GetBookmarkDragOperation(content::BrowserContext* browser_context, |
| const BookmarkNode* node) { |
| PrefService* prefs = user_prefs::UserPrefs::Get(browser_context); |
| BookmarkModel* model = |
| BookmarkModelFactory::GetForBrowserContext(browser_context); |
| |
| int move = ui::DragDropTypes::DRAG_MOVE; |
| if (!prefs->GetBoolean(bookmarks::prefs::kEditBookmarksEnabled) || |
| !model->client()->CanBeEditedByUser(node)) { |
| move = ui::DragDropTypes::DRAG_NONE; |
| } |
| if (node->is_url()) |
| return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK | move; |
| return ui::DragDropTypes::DRAG_COPY | move; |
| } |
| |
| int GetPreferredBookmarkDropOperation(int source_operations, int operations) { |
| int common_ops = (source_operations & operations); |
| if (!common_ops) |
| return ui::DragDropTypes::DRAG_NONE; |
| if (ui::DragDropTypes::DRAG_COPY & common_ops) |
| return ui::DragDropTypes::DRAG_COPY; |
| if (ui::DragDropTypes::DRAG_LINK & common_ops) |
| return ui::DragDropTypes::DRAG_LINK; |
| if (ui::DragDropTypes::DRAG_MOVE & common_ops) |
| return ui::DragDropTypes::DRAG_MOVE; |
| return ui::DragDropTypes::DRAG_NONE; |
| } |
| |
| int GetBookmarkDropOperation(Profile* profile, |
| const ui::DropTargetEvent& event, |
| const bookmarks::BookmarkNodeData& data, |
| const BookmarkNode* parent, |
| size_t index) { |
| const base::FilePath& profile_path = profile->GetPath(); |
| |
| if (data.IsFromProfilePath(profile_path) && data.size() > 1) |
| // Currently only accept one dragged node at a time. |
| return ui::DragDropTypes::DRAG_NONE; |
| |
| if (!IsValidBookmarkDropLocation(profile, data, parent, index)) |
| return ui::DragDropTypes::DRAG_NONE; |
| |
| BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile); |
| if (!model->client()->CanBeEditedByUser(parent)) |
| return ui::DragDropTypes::DRAG_NONE; |
| |
| const BookmarkNode* dragged_node = |
| data.GetFirstNode(model, profile->GetPath()); |
| if (dragged_node) { |
| // User is dragging from this profile. |
| if (!model->client()->CanBeEditedByUser(dragged_node)) { |
| // Do a copy instead of a move when dragging bookmarks that the user can't |
| // modify. |
| return ui::DragDropTypes::DRAG_COPY; |
| } |
| return ui::DragDropTypes::DRAG_MOVE; |
| } |
| |
| // User is dragging from another app, copy. |
| return GetPreferredBookmarkDropOperation(event.source_operations(), |
| ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK); |
| } |
| |
| bool IsValidBookmarkDropLocation(Profile* profile, |
| const bookmarks::BookmarkNodeData& data, |
| const BookmarkNode* drop_parent, |
| size_t index) { |
| if (!drop_parent->is_folder()) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| if (!data.is_valid()) |
| return false; |
| |
| BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile); |
| if (!model->client()->CanBeEditedByUser(drop_parent)) |
| return false; |
| |
| const base::FilePath& profile_path = profile->GetPath(); |
| if (data.IsFromProfilePath(profile_path)) { |
| std::vector<const BookmarkNode*> nodes = data.GetNodes(model, profile_path); |
| for (size_t i = 0; i < nodes.size(); ++i) { |
| // Don't allow the drop if the user is attempting to drop on one of the |
| // nodes being dragged. |
| const BookmarkNode* node = nodes[i]; |
| int node_index = (drop_parent == node->parent()) ? |
| drop_parent->GetIndexOf(nodes[i]) : -1; |
| if (node_index != -1 && |
| (index == size_t{node_index} || index == size_t{node_index} + 1)) |
| return false; |
| |
| // drop_parent can't accept a child that is an ancestor. |
| if (drop_parent->HasAncestor(node)) |
| return false; |
| } |
| return true; |
| } |
| // From another profile, always accept. |
| return true; |
| } |
| |
| #if defined(TOOLKIT_VIEWS) |
| // TODO(bsep): vectorize the Windows versions: crbug.com/564112 |
| gfx::ImageSkia GetBookmarkFolderIcon(SkColor text_color) { |
| gfx::ImageSkia folder; |
| #if defined(OS_WIN) |
| folder = *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( |
| IDR_FOLDER_CLOSED); |
| #elif defined(OS_MAC) |
| int resource_id = color_utils::IsDark(text_color) ? IDR_FOLDER_CLOSED |
| : IDR_FOLDER_CLOSED_WHITE; |
| folder = *ui::ResourceBundle::GetSharedInstance() |
| .GetNativeImageNamed(resource_id) |
| .ToImageSkia(); |
| #else |
| folder = GetFolderIcon(ui::TouchUiController::Get()->touch_ui() |
| ? vector_icons::kFolderTouchIcon |
| : vector_icons::kFolderIcon, |
| text_color); |
| #endif |
| return gfx::ImageSkia(std::make_unique<RTLFlipSource>(folder), folder.size()); |
| } |
| |
| gfx::ImageSkia GetBookmarkManagedFolderIcon(SkColor text_color) { |
| gfx::ImageSkia folder; |
| #if defined(OS_WIN) |
| folder = *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( |
| IDR_BOOKMARK_BAR_FOLDER_MANAGED); |
| #elif defined(OS_MAC) |
| int resource_id = color_utils::IsDark(text_color) |
| ? IDR_BOOKMARK_BAR_FOLDER_MANAGED |
| : IDR_BOOKMARK_BAR_FOLDER_MANAGED_WHITE; |
| folder = *ui::ResourceBundle::GetSharedInstance() |
| .GetNativeImageNamed(resource_id) |
| .ToImageSkia(); |
| #else |
| folder = GetFolderIcon(ui::TouchUiController::Get()->touch_ui() |
| ? vector_icons::kFolderManagedTouchIcon |
| : vector_icons::kFolderManagedIcon, |
| text_color); |
| #endif |
| return gfx::ImageSkia(std::make_unique<RTLFlipSource>(folder), folder.size()); |
| } |
| #endif |
| |
| } // namespace chrome |