| // Copyright 2020 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/renderer_context_menu/quick_answers_menu_observer.h" |
| |
| #include <utility> |
| |
| #include "ash/public/cpp/assistant/controller/assistant_interaction_controller.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/time/time.h" |
| #include "build/branding_buildflags.h" |
| #include "chrome/app/vector_icons/vector_icons.h" |
| #include "chrome/browser/ash/profiles/profile_helper.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chromeos/components/quick_answers/public/cpp/controller/quick_answers_controller.h" |
| #include "chromeos/components/quick_answers/public/cpp/quick_answers_state.h" |
| #include "chromeos/components/quick_answers/quick_answers_model.h" |
| #include "chromeos/services/assistant/public/cpp/assistant_service.h" |
| #include "components/language/core/browser/pref_names.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/renderer_context_menu/render_view_context_menu_proxy.h" |
| #include "components/user_manager/user_manager.h" |
| #include "content/public/browser/context_menu_params.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/browser/web_contents.h" |
| #include "google_apis/gaia/gaia_auth_util.h" |
| #include "third_party/blink/public/mojom/context_menu/context_menu.mojom.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/gfx/text_constants.h" |
| #include "ui/gfx/text_elider.h" |
| |
| namespace { |
| |
| using quick_answers::Context; |
| using quick_answers::QuickAnswersExitPoint; |
| |
| constexpr int kMaxSurroundingTextLength = 300; |
| |
| bool IsInternalUser(Profile* profile) { |
| auto* user = ash::ProfileHelper::Get()->GetUserByProfile(profile); |
| // TODO(b/186906279): Add user login support for browser test. |
| if (!user) |
| return false; |
| |
| const std::string email = user->GetAccountId().GetUserEmail(); |
| return gaia::IsGoogleInternalAccountEmail(email); |
| } |
| |
| } // namespace |
| |
| QuickAnswersMenuObserver::QuickAnswersMenuObserver( |
| RenderViewContextMenuProxy* proxy) |
| : proxy_(proxy) {} |
| |
| QuickAnswersMenuObserver::~QuickAnswersMenuObserver() = default; |
| |
| void QuickAnswersMenuObserver::OnContextMenuShown( |
| const content::ContextMenuParams& params, |
| const gfx::Rect& bounds_in_screen) { |
| DCHECK(QuickAnswersController::Get()); |
| menu_shown_time_ = base::TimeTicks::Now(); |
| |
| if (!QuickAnswersState::Get()->is_eligible()) |
| return; |
| |
| // Skip password input field. |
| if (params.input_field_type == |
| blink::mojom::ContextMenuDataInputFieldType::kPassword) { |
| return; |
| } |
| |
| // Skip if no text selected. |
| auto selected_text = base::UTF16ToUTF8(params.selection_text); |
| if (selected_text.empty()) |
| return; |
| |
| bounds_in_screen_ = bounds_in_screen; |
| |
| content::RenderFrameHost* focused_frame = |
| proxy_->GetWebContents()->GetFocusedFrame(); |
| if (focused_frame) { |
| QuickAnswersController::Get()->SetPendingShowQuickAnswers(); |
| focused_frame->RequestTextSurroundingSelection( |
| base::BindOnce( |
| &QuickAnswersMenuObserver::OnTextSurroundingSelectionAvailable, |
| weak_factory_.GetWeakPtr(), selected_text), |
| kMaxSurroundingTextLength); |
| } |
| } |
| |
| void QuickAnswersMenuObserver::OnContextMenuViewBoundsChanged( |
| const gfx::Rect& bounds_in_screen) { |
| bounds_in_screen_ = bounds_in_screen; |
| QuickAnswersController::Get()->UpdateQuickAnswersAnchorBounds( |
| bounds_in_screen); |
| } |
| |
| void QuickAnswersMenuObserver::OnMenuClosed() { |
| const base::TimeDelta time_since_request_sent = |
| base::TimeTicks::Now() - menu_shown_time_; |
| if (is_other_command_executed_) { |
| base::UmaHistogramTimes("QuickAnswers.ContextMenu.Close.DurationWithClick", |
| time_since_request_sent); |
| } else { |
| base::UmaHistogramTimes( |
| "QuickAnswers.ContextMenu.Close.DurationWithoutClick", |
| time_since_request_sent); |
| } |
| |
| base::UmaHistogramBoolean("QuickAnswers.ContextMenu.Close", |
| is_other_command_executed_); |
| |
| QuickAnswersController::Get()->DismissQuickAnswers( |
| is_other_command_executed_ ? QuickAnswersExitPoint::kContextMenuClick |
| : QuickAnswersExitPoint::KContextMenuDismiss); |
| } |
| |
| void QuickAnswersMenuObserver::CommandWillBeExecuted(int command_id) { |
| is_other_command_executed_ = true; |
| } |
| |
| void QuickAnswersMenuObserver::OnTextSurroundingSelectionAvailable( |
| const std::string& selected_text, |
| const std::u16string& surrounding_text, |
| uint32_t start_offset, |
| uint32_t end_offset) { |
| Profile* profile = Profile::FromBrowserContext(proxy_->GetBrowserContext()); |
| PrefService* prefs = profile->GetPrefs(); |
| |
| Context context; |
| context.surrounding_text = base::UTF16ToUTF8(surrounding_text); |
| context.device_properties.language = |
| l10n_util::GetLanguage(g_browser_process->GetApplicationLocale()); |
| context.device_properties.preferred_languages = |
| prefs->GetString(language::prefs::kPreferredLanguages); |
| context.device_properties.is_internal = IsInternalUser(profile); |
| QuickAnswersController::Get()->MaybeShowQuickAnswers(bounds_in_screen_, |
| selected_text, context); |
| } |