| diff --git chrome/browser/ui/browser_command_controller.cc chrome/browser/ui/browser_command_controller.cc |
| index 85dc58159a58a..9b582e691f6d8 100644 |
| --- chrome/browser/ui/browser_command_controller.cc |
| +++ chrome/browser/ui/browser_command_controller.cc |
| @@ -402,6 +402,7 @@ bool BrowserCommandController::ExecuteCommandWithDisposition( |
| // choose to not implement CommandUpdaterDelegate inside this class and |
| // therefore command_updater_ doesn't have the delegate set). |
| if (!SupportsCommand(id) || !IsCommandEnabled(id)) { |
| + LOG(WARNING) << "Invalid/disabled command " << id; |
| return false; |
| } |
| |
| @@ -418,6 +419,13 @@ bool BrowserCommandController::ExecuteCommandWithDisposition( |
| DCHECK(command_updater_.IsCommandEnabled(id)) |
| << "Invalid/disabled command " << id; |
| |
| +#if BUILDFLAG(ENABLE_CEF) |
| + if (browser_->cef_delegate() && |
| + browser_->cef_delegate()->HandleCommand(id, disposition)) { |
| + return true; |
| + } |
| +#endif |
| + |
| // The order of commands in this switch statement must match the function |
| // declaration order in browser.h! |
| switch (id) { |
| @@ -1152,11 +1160,13 @@ void BrowserCommandController::TabRestoreServiceLoaded( |
| // BrowserCommandController, private: |
| |
| bool BrowserCommandController::IsShowingMainUI() { |
| - return browser_->SupportsWindowFeature(Browser::FEATURE_TABSTRIP); |
| + return browser_->SupportsWindowFeature(Browser::FEATURE_TABSTRIP) || |
| + browser_->toolbar_overridden(); |
| } |
| |
| bool BrowserCommandController::IsShowingLocationBar() { |
| - return browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR); |
| + return browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR) || |
| + browser_->toolbar_overridden(); |
| } |
| |
| bool BrowserCommandController::IsWebAppOrCustomTab() const { |
| diff --git chrome/browser/ui/toolbar/app_menu_model.cc chrome/browser/ui/toolbar/app_menu_model.cc |
| index d2b2a605cbba5..afaf26c26a6db 100644 |
| --- chrome/browser/ui/toolbar/app_menu_model.cc |
| +++ chrome/browser/ui/toolbar/app_menu_model.cc |
| @@ -590,6 +590,57 @@ SaveAndShareSubMenuModel::SaveAndShareSubMenuModel( |
| } |
| } |
| |
| +#if BUILDFLAG(ENABLE_CEF) |
| +using IsVisibleCallback = base::RepeatingCallback<bool(int)>; |
| + |
| +void FilterMenuModel(ui::SimpleMenuModel* model, |
| + const IsVisibleCallback& is_visible) { |
| + absl::optional<size_t> last_separator; |
| + size_t visible_ct = 0; |
| + for (size_t i = 0; i < model->GetItemCount(); ++i) { |
| + const auto type = model->GetTypeAt(i); |
| + if (type == ui::MenuModel::TYPE_SEPARATOR) { |
| + if (last_separator) { |
| + // Remove multiple separators in a row. Prefer to remove a NORMAL |
| + // separator if possible (as compared to zoom/edit controls which use |
| + // UPPER/LOWER separators). |
| + if (model->GetSeparatorTypeAt(*last_separator) == |
| + ui::NORMAL_SEPARATOR) { |
| + model->RemoveItemAt(*last_separator); |
| + i--; |
| + last_separator = i; |
| + } else { |
| + model->RemoveItemAt(i); |
| + i--; |
| + } |
| + } else if (visible_ct == 0) { |
| + // Remove leading separator. |
| + model->RemoveItemAt(i); |
| + i--; |
| + } else { |
| + last_separator = i; |
| + } |
| + visible_ct = 0; |
| + } else if (is_visible.Run(model->GetCommandIdAt(i))) { |
| + last_separator = absl::nullopt; |
| + visible_ct++; |
| + |
| + if (type == ui::MenuModel::TYPE_SUBMENU) { |
| + // Filter sub-menu. |
| + auto sub_model = |
| + static_cast<ui::SimpleMenuModel*>(model->GetSubmenuModelAt(i)); |
| + FilterMenuModel(sub_model, is_visible); |
| + } |
| + } |
| + } |
| + |
| + if (last_separator) { |
| + // Remove trailing separator. |
| + model->RemoveItemAt(*last_separator); |
| + } |
| +} |
| +#endif // BUILDFLAG(ENABLE_CEF) |
| + |
| } // namespace |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| @@ -1382,7 +1433,7 @@ bool AppMenuModel::IsCommandIdChecked(int command_id) const { |
| return false; |
| } |
| |
| -bool AppMenuModel::IsCommandIdEnabled(int command_id) const { |
| +bool AppMenuModel::IsCommandIdEnabledInternal(int command_id) const { |
| GlobalError* error = |
| GlobalErrorServiceFactory::GetForProfile(browser_->profile()) |
| ->GetGlobalErrorByMenuItemCommandID(command_id); |
| @@ -1397,6 +1448,30 @@ bool AppMenuModel::IsCommandIdEnabled(int command_id) const { |
| } |
| } |
| |
| +bool AppMenuModel::IsCommandIdEnabled(int command_id) const { |
| + if (!IsCommandIdEnabledInternal(command_id)) { |
| + return false; |
| + } |
| + |
| +#if BUILDFLAG(ENABLE_CEF) |
| + if (browser_->cef_delegate()) { |
| + return browser_->cef_delegate()->IsAppMenuItemEnabled(command_id); |
| + } |
| +#endif |
| + |
| + return true; |
| +} |
| + |
| +bool AppMenuModel::IsCommandIdVisible(int command_id) const { |
| +#if BUILDFLAG(ENABLE_CEF) |
| + if (browser_->cef_delegate()) { |
| + return browser_->cef_delegate()->IsAppMenuItemVisible(command_id); |
| + } |
| +#endif |
| + |
| + return true; |
| +} |
| + |
| bool AppMenuModel::IsCommandIdAlerted(int command_id) const { |
| if ((command_id == IDC_RECENT_TABS_MENU) || |
| (command_id == AppMenuModel::kMinRecentTabsCommandId)) { |
| @@ -1566,11 +1641,15 @@ void AppMenuModel::Build() { |
| kDefaultIconSize)); |
| } |
| |
| - AddSeparator(features::IsChromeRefresh2023() ? ui::NORMAL_SEPARATOR |
| - : ui::LOWER_SEPARATOR); |
| - CreateZoomMenu(); |
| - AddSeparator(features::IsChromeRefresh2023() ? ui::NORMAL_SEPARATOR |
| - : ui::UPPER_SEPARATOR); |
| + if (IsCommandIdVisible(IDC_ZOOM_MENU)) { |
| + AddSeparator(features::IsChromeRefresh2023() ? ui::NORMAL_SEPARATOR |
| + : ui::LOWER_SEPARATOR); |
| + CreateZoomMenu(); |
| + AddSeparator(features::IsChromeRefresh2023() ? ui::NORMAL_SEPARATOR |
| + : ui::UPPER_SEPARATOR); |
| + } else { |
| + AddSeparator(ui::NORMAL_SEPARATOR); |
| + } |
| |
| AddItemWithStringId(IDC_PRINT, IDS_PRINT); |
| |
| @@ -1651,9 +1730,13 @@ void AppMenuModel::Build() { |
| kMoreToolsMenuItem); |
| |
| if (!features::IsChromeRefresh2023()) { |
| - AddSeparator(ui::LOWER_SEPARATOR); |
| - CreateCutCopyPasteMenu(); |
| - AddSeparator(ui::UPPER_SEPARATOR); |
| + if (IsCommandIdVisible(IDC_EDIT_MENU)) { |
| + AddSeparator(ui::LOWER_SEPARATOR); |
| + CreateCutCopyPasteMenu(); |
| + AddSeparator(ui::UPPER_SEPARATOR); |
| + } else { |
| + AddSeparator(ui::NORMAL_SEPARATOR); |
| + } |
| } |
| |
| if (!features::IsChromeRefresh2023()) { |
| @@ -1739,6 +1822,11 @@ void AppMenuModel::Build() { |
| SetCommandIcon(this, IDC_EXIT, kExitMenuIcon); |
| } |
| |
| +#if BUILDFLAG(ENABLE_CEF) |
| + FilterMenuModel(this, base::BindRepeating(&AppMenuModel::IsCommandIdVisible, |
| + base::Unretained(this))); |
| +#endif |
| + |
| uma_action_recorded_ = false; |
| } |
| |
| diff --git chrome/browser/ui/toolbar/app_menu_model.h chrome/browser/ui/toolbar/app_menu_model.h |
| index f06cf1bf08ba4..4e89b522715fc 100644 |
| --- chrome/browser/ui/toolbar/app_menu_model.h |
| +++ chrome/browser/ui/toolbar/app_menu_model.h |
| @@ -204,6 +204,7 @@ class AppMenuModel : public ui::SimpleMenuModel, |
| void ExecuteCommand(int command_id, int event_flags) override; |
| bool IsCommandIdChecked(int command_id) const override; |
| bool IsCommandIdEnabled(int command_id) const override; |
| + bool IsCommandIdVisible(int command_id) const override; |
| bool IsCommandIdAlerted(int command_id) const override; |
| bool GetAcceleratorForCommandId(int command_id, |
| ui::Accelerator* accelerator) const override; |
| @@ -236,6 +237,8 @@ class AppMenuModel : public ui::SimpleMenuModel, |
| // took to select the command. |
| void LogMenuMetrics(int command_id); |
| |
| + bool IsCommandIdEnabledInternal(int command_id) const; |
| + |
| private: |
| // Adds actionable global error menu items to the menu. |
| // Examples: Extension permissions and sign in errors. |
| diff --git chrome/browser/ui/views/find_bar_host.cc chrome/browser/ui/views/find_bar_host.cc |
| index 59024587ef6b7..0c30aa71768cf 100644 |
| --- chrome/browser/ui/views/find_bar_host.cc |
| +++ chrome/browser/ui/views/find_bar_host.cc |
| @@ -412,6 +412,12 @@ void FindBarHost::GetWidgetBounds(gfx::Rect* bounds) { |
| // The BrowserView does Layout for the components that we care about |
| // positioning relative to, so we ask it to tell us where we should go. |
| *bounds = browser_view()->GetFindBarBoundingBox(); |
| + |
| +#if BUILDFLAG(ENABLE_CEF) |
| + if (browser_view()->browser() && browser_view()->browser()->cef_delegate()) { |
| + browser_view()->browser()->cef_delegate()->UpdateFindBarBoundingBox(bounds); |
| + } |
| +#endif |
| } |
| |
| void FindBarHost::RegisterAccelerators() { |
| diff --git chrome/browser/ui/views/frame/browser_frame.cc chrome/browser/ui/views/frame/browser_frame.cc |
| index bb4b3821bcfda..4014f61a7057f 100644 |
| --- chrome/browser/ui/views/frame/browser_frame.cc |
| +++ chrome/browser/ui/views/frame/browser_frame.cc |
| @@ -114,15 +114,23 @@ ui::ColorProviderKey::SchemeVariant GetSchemeVariant( |
| //////////////////////////////////////////////////////////////////////////////// |
| // BrowserFrame, public: |
| |
| +BrowserFrame::BrowserFrame() : BrowserFrame(nullptr) {} |
| + |
| BrowserFrame::BrowserFrame(BrowserView* browser_view) |
| : native_browser_frame_(nullptr), |
| root_view_(nullptr), |
| browser_frame_view_(nullptr), |
| - browser_view_(browser_view) { |
| - browser_view_->set_frame(this); |
| + browser_view_(nullptr) { |
| set_is_secondary_widget(false); |
| // Don't focus anything on creation, selecting a tab will set the focus. |
| set_focus_on_creation(false); |
| + if (browser_view) |
| + InitBrowserView(browser_view); |
| +} |
| + |
| +void BrowserFrame::InitBrowserView(BrowserView* browser_view) { |
| + browser_view_ = browser_view; |
| + browser_view_->set_frame(this); |
| } |
| |
| BrowserFrame::~BrowserFrame() {} |
| @@ -228,10 +236,20 @@ void BrowserFrame::LayoutWebAppWindowTitle( |
| } |
| |
| int BrowserFrame::GetTopInset() const { |
| + if (!browser_frame_view_) { |
| + // With CEF the browser may already be part of a larger Views layout. Zero |
| + // out the adjustment in BrowserView::GetTopInsetInBrowserView() so that |
| + // the browser isn't shifted to the top of the window. |
| + return browser_view_->y(); |
| + } |
| return browser_frame_view_->GetTopInset(false); |
| } |
| |
| void BrowserFrame::UpdateThrobber(bool running) { |
| + if (!browser_frame_view_) { |
| + // Not supported with CEF Views-hosted DevTools windows. |
| + return; |
| + } |
| browser_frame_view_->UpdateThrobber(running); |
| } |
| |
| @@ -240,6 +258,8 @@ BrowserNonClientFrameView* BrowserFrame::GetFrameView() const { |
| } |
| |
| bool BrowserFrame::UseCustomFrame() const { |
| + if (!native_browser_frame_) |
| + return true; |
| return native_browser_frame_->UseCustomFrame(); |
| } |
| |
| @@ -253,20 +273,30 @@ bool BrowserFrame::ShouldDrawFrameHeader() const { |
| |
| void BrowserFrame::GetWindowPlacement(gfx::Rect* bounds, |
| ui::WindowShowState* show_state) const { |
| + if (!native_browser_frame_) { |
| + *show_state = ui::SHOW_STATE_DEFAULT; |
| + return; |
| + } |
| return native_browser_frame_->GetWindowPlacement(bounds, show_state); |
| } |
| |
| content::KeyboardEventProcessingResult BrowserFrame::PreHandleKeyboardEvent( |
| const content::NativeWebKeyboardEvent& event) { |
| + if (!native_browser_frame_) |
| + return content::KeyboardEventProcessingResult::NOT_HANDLED; |
| return native_browser_frame_->PreHandleKeyboardEvent(event); |
| } |
| |
| bool BrowserFrame::HandleKeyboardEvent( |
| const content::NativeWebKeyboardEvent& event) { |
| + if (!native_browser_frame_) |
| + return false; |
| return native_browser_frame_->HandleKeyboardEvent(event); |
| } |
| |
| void BrowserFrame::OnBrowserViewInitViewsComplete() { |
| + if (!browser_frame_view_) |
| + return; |
| browser_frame_view_->OnBrowserViewInitViewsComplete(); |
| } |
| |
| @@ -367,6 +397,8 @@ ui::ColorProviderKey::ThemeInitializerSupplier* BrowserFrame::GetCustomTheme() |
| } |
| |
| void BrowserFrame::OnNativeWidgetWorkspaceChanged() { |
| + if (!browser_view_) |
| + return; |
| chrome::SaveWindowWorkspace(browser_view_->browser(), GetWorkspace()); |
| chrome::SaveWindowVisibleOnAllWorkspaces(browser_view_->browser(), |
| IsVisibleOnAllWorkspaces()); |
| @@ -478,6 +510,8 @@ void BrowserFrame::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) { |
| |
| ui::ColorProviderKey BrowserFrame::GetColorProviderKey() const { |
| auto key = Widget::GetColorProviderKey(); |
| + if (!browser_view_) |
| + return key; |
| |
| key.app_controller = browser_view_->browser()->app_controller(); |
| |
| @@ -632,5 +666,8 @@ bool BrowserFrame::RegenerateFrameOnThemeChange( |
| } |
| |
| bool BrowserFrame::IsIncognitoBrowser() const { |
| + if (!browser_view_) { |
| + return true; |
| + } |
| return browser_view_->browser()->profile()->IsIncognitoProfile(); |
| } |
| diff --git chrome/browser/ui/views/frame/browser_frame.h chrome/browser/ui/views/frame/browser_frame.h |
| index 0c231b6ac5b01..6b5af98e18e42 100644 |
| --- chrome/browser/ui/views/frame/browser_frame.h |
| +++ chrome/browser/ui/views/frame/browser_frame.h |
| @@ -61,7 +61,9 @@ enum class TabDragKind { |
| // This is a virtual interface that allows system specific browser frames. |
| class BrowserFrame : public views::Widget, public views::ContextMenuController { |
| public: |
| + BrowserFrame(); |
| explicit BrowserFrame(BrowserView* browser_view); |
| + void InitBrowserView(BrowserView* browser_view); |
| |
| BrowserFrame(const BrowserFrame&) = delete; |
| BrowserFrame& operator=(const BrowserFrame&) = delete; |
| diff --git chrome/browser/ui/views/frame/browser_view.cc chrome/browser/ui/views/frame/browser_view.cc |
| index 41376bb2d8af1..fa9ede3614aca 100644 |
| --- chrome/browser/ui/views/frame/browser_view.cc |
| +++ chrome/browser/ui/views/frame/browser_view.cc |
| @@ -343,11 +343,10 @@ using content::NativeWebKeyboardEvent; |
| using content::WebContents; |
| using web_modal::WebContentsModalDialogHost; |
| |
| -namespace { |
| +// static |
| +const char BrowserView::kBrowserViewKey[] = "__BROWSER_VIEW__"; |
| |
| -// The name of a key to store on the window handle so that other code can |
| -// locate this object using just the handle. |
| -const char* const kBrowserViewKey = "__BROWSER_VIEW__"; |
| +namespace { |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| // UMA histograms that record animation smoothness for tab loading animation. |
| @@ -843,12 +842,23 @@ class BrowserView::AccessibilityModeObserver : public ui::AXModeObserver { |
| /////////////////////////////////////////////////////////////////////////////// |
| // BrowserView, public: |
| |
| +BrowserView::BrowserView() : BrowserView(nullptr) {} |
| + |
| BrowserView::BrowserView(std::unique_ptr<Browser> browser) |
| : views::ClientView(nullptr, nullptr), |
| - browser_(std::move(browser)), |
| accessibility_mode_observer_( |
| - std::make_unique<AccessibilityModeObserver>(this)), |
| - browser_actions_(*browser_) { |
| + std::make_unique<AccessibilityModeObserver>(this)) { |
| + if (browser) |
| + InitBrowser(std::move(browser)); |
| +} |
| + |
| +void BrowserView::InitBrowser(std::unique_ptr<Browser> browser) { |
| + DCHECK(!browser_); |
| + browser_ = std::move(browser); |
| + |
| + browser_actions_ = std::make_unique<BrowserActions>(*browser_); |
| + immersive_mode_controller_ = chrome::CreateImmersiveModeController(this); |
| + |
| SetShowIcon( |
| ::ShouldShowWindowIcon(browser_.get(), AppUsesWindowControlsOverlay())); |
| |
| @@ -897,7 +907,6 @@ BrowserView::BrowserView(std::unique_ptr<Browser> browser) |
| } |
| |
| browser_->tab_strip_model()->AddObserver(this); |
| - immersive_mode_controller_ = chrome::CreateImmersiveModeController(this); |
| |
| // Top container holds tab strip region and toolbar and lives at the front of |
| // the view hierarchy. |
| @@ -956,8 +965,15 @@ BrowserView::BrowserView(std::unique_ptr<Browser> browser) |
| contents_container->SetLayoutManager(std::make_unique<ContentsLayoutManager>( |
| devtools_web_view_, contents_web_view_)); |
| |
| - toolbar_ = top_container_->AddChildView( |
| - std::make_unique<ToolbarView>(browser_.get(), this)); |
| + toolbar_ = OverrideCreateToolbar(); |
| + if (!toolbar_) { |
| + toolbar_ = new ToolbarView(browser_.get(), this, absl::nullopt); |
| + } else { |
| + browser_->set_toolbar_overridden(true); |
| + // Update state that depends on the above flag. |
| + browser_->command_controller()->FullscreenStateChanged(); |
| + } |
| + top_container_->AddChildView(base::WrapUnique(toolbar_.get())); |
| |
| contents_separator_ = |
| top_container_->AddChildView(std::make_unique<ContentsSeparator>()); |
| @@ -1031,7 +1047,9 @@ BrowserView::~BrowserView() { |
| |
| // All the tabs should have been destroyed already. If we were closed by the |
| // OS with some tabs than the NativeBrowserFrame should have destroyed them. |
| + if (browser_) { |
| DCHECK_EQ(0, browser_->tab_strip_model()->count()); |
| + } |
| |
| // Stop the animation timer explicitly here to avoid running it in a nested |
| // message loop, which may run by Browser destructor. |
| @@ -1045,12 +1063,14 @@ BrowserView::~BrowserView() { |
| // child views and it is an observer for avatar toolbar button if any. |
| autofill_bubble_handler_.reset(); |
| |
| + if (browser_) { |
| auto* global_registry = |
| extensions::ExtensionCommandsGlobalRegistry::Get(browser_->profile()); |
| if (global_registry->registry_for_active_window() == |
| extension_keybinding_registry_.get()) { |
| global_registry->set_registry_for_active_window(nullptr); |
| } |
| + } |
| |
| // The TabStrip attaches a listener to the model. Make sure we shut down the |
| // TabStrip first so that it can cleanly remove the listener. |
| @@ -1068,7 +1088,9 @@ BrowserView::~BrowserView() { |
| |
| // `SidePanelUI::RemoveSidePanelUIForBrowser()` deletes the |
| // SidePanelCoordinator. |
| + if (browser()) { |
| SidePanelUI::RemoveSidePanelUIForBrowser(browser()); |
| + } |
| } |
| |
| // static |
| @@ -1939,9 +1961,14 @@ void BrowserView::OnExclusiveAccessUserInput() { |
| |
| bool BrowserView::ShouldHideUIForFullscreen() const { |
| // Immersive mode needs UI for the slide-down top panel. |
| - if (immersive_mode_controller_->IsEnabled()) |
| + // Avoid callback into |immersive_mode_controller_| during construction. |
| + // See CEF issue #3527. |
| + if (immersive_mode_controller_ && |
| + immersive_mode_controller_->IsEnabled()) |
| return false; |
| |
| + if (!frame_->GetFrameView()) |
| + return false; |
| return frame_->GetFrameView()->ShouldHideTopUIForFullscreen(); |
| } |
| |
| @@ -2956,7 +2983,8 @@ DownloadShelf* BrowserView::GetDownloadShelf() { |
| } |
| |
| DownloadBubbleUIController* BrowserView::GetDownloadBubbleUIController() { |
| - DCHECK(toolbar_button_provider_); |
| + if (!toolbar_button_provider_) |
| + return nullptr; |
| if (auto* download_button = toolbar_button_provider_->GetDownloadButton()) |
| return download_button->bubble_controller(); |
| return nullptr; |
| @@ -3490,7 +3518,8 @@ void BrowserView::ReparentTopContainerForEndOfImmersive() { |
| if (top_container()->parent() == this) |
| return; |
| |
| - overlay_view_->SetVisible(false); |
| + if (overlay_view_) |
| + overlay_view_->SetVisible(false); |
| top_container()->DestroyLayer(); |
| AddChildViewAt(top_container(), 0); |
| EnsureFocusOrder(); |
| @@ -3948,11 +3977,38 @@ void BrowserView::GetAccessiblePanes(std::vector<views::View*>* panes) { |
| bool BrowserView::ShouldDescendIntoChildForEventHandling( |
| gfx::NativeView child, |
| const gfx::Point& location) { |
| +#if BUILDFLAG(ENABLE_CEF) |
| + const bool frameless_pip = |
| + GetIsPictureInPictureType() && |
| + !browser_->SupportsWindowFeature(Browser::FEATURE_TITLEBAR); |
| + if (frameless_pip) { |
| + if (auto frame_view = frame()->GetFrameView()) { |
| + int result = frame_view->NonClientHitTest(location); |
| + if (result == HTTOP || result == HTTOPLEFT || result == HTTOPRIGHT) { |
| + // Allow resize from the top of a frameless window. |
| + return false; |
| + } |
| + } |
| + } |
| +#endif |
| + |
| + absl::optional<SkRegion> draggable_region; |
| + |
| // Window for PWAs with window-controls-overlay display override should claim |
| // mouse events that fall within the draggable region. |
| web_app::AppBrowserController* controller = browser()->app_controller(); |
| - if (AreDraggableRegionsEnabled() && controller && |
| - controller->draggable_region().has_value()) { |
| + if (AreDraggableRegionsEnabled() && controller) { |
| + draggable_region = controller->draggable_region(); |
| + } |
| + |
| +#if BUILDFLAG(ENABLE_CEF) |
| + // Match logic in PictureInPictureBrowserFrameView::NonClientHitTest. |
| + if (!draggable_region.has_value() && frameless_pip) { |
| + draggable_region = browser_->cef_delegate()->GetDraggableRegion(); |
| + } |
| +#endif |
| + |
| + if (draggable_region.has_value()) { |
| // Draggable regions are defined relative to the web contents. |
| gfx::Point point_in_contents_web_view_coords(location); |
| views::View::ConvertPointToTarget(GetWidget()->GetRootView(), |
| @@ -3961,7 +4017,7 @@ bool BrowserView::ShouldDescendIntoChildForEventHandling( |
| |
| // Draggable regions should be ignored for clicks into any browser view's |
| // owned widgets, for example alerts, permission prompts or find bar. |
| - return !controller->draggable_region()->contains( |
| + return !draggable_region->contains( |
| point_in_contents_web_view_coords.x(), |
| point_in_contents_web_view_coords.y()) || |
| WidgetOwnedByAnchorContainsPoint(point_in_contents_web_view_coords); |
| @@ -4069,8 +4125,10 @@ void BrowserView::Layout() { |
| |
| // TODO(jamescook): Why was this in the middle of layout code? |
| toolbar_->location_bar()->omnibox_view()->SetFocusBehavior( |
| - IsToolbarVisible() ? FocusBehavior::ALWAYS : FocusBehavior::NEVER); |
| - frame()->GetFrameView()->UpdateMinimumSize(); |
| + (IsToolbarVisible() || browser_->toolbar_overridden()) ? |
| + FocusBehavior::ALWAYS : FocusBehavior::NEVER); |
| + if (frame()->GetFrameView()) |
| + frame()->GetFrameView()->UpdateMinimumSize(); |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| // In chromeOS ash we round the bottom two corners of the browser frame by |
| @@ -4148,6 +4206,11 @@ void BrowserView::AddedToWidget() { |
| SetThemeProfileForWindow(GetNativeWindow(), browser_->profile()); |
| #endif |
| |
| + // This browser view may already have a custom button provider set (e.g the |
| + // hosted app frame). |
| + if (!toolbar_button_provider_) |
| + SetToolbarButtonProvider(toolbar_); |
| + |
| toolbar_->Init(); |
| |
| // TODO(pbos): Investigate whether the side panels should be creatable when |
| @@ -4196,13 +4259,9 @@ void BrowserView::AddedToWidget() { |
| |
| EnsureFocusOrder(); |
| |
| - // This browser view may already have a custom button provider set (e.g the |
| - // hosted app frame). |
| - if (!toolbar_button_provider_) |
| - SetToolbarButtonProvider(toolbar_); |
| - |
| frame_->OnBrowserViewInitViewsComplete(); |
| - frame_->GetFrameView()->UpdateMinimumSize(); |
| + if (frame_->GetFrameView()) |
| + frame_->GetFrameView()->UpdateMinimumSize(); |
| using_native_frame_ = frame_->ShouldUseNativeFrame(); |
| |
| MaybeInitializeWebUITabStrip(); |
| @@ -4614,7 +4673,8 @@ void BrowserView::ProcessFullscreen(bool fullscreen, |
| // Undo our anti-jankiness hacks and force a re-layout. |
| in_process_fullscreen_ = false; |
| ToolbarSizeChanged(false); |
| - frame_->GetFrameView()->OnFullscreenStateChanged(); |
| + if (frame_->GetFrameView()) |
| + frame_->GetFrameView()->OnFullscreenStateChanged(); |
| } |
| |
| bool BrowserView::ShouldUseImmersiveFullscreenForUrl(const GURL& url) const { |
| @@ -5003,6 +5063,8 @@ Profile* BrowserView::GetProfile() { |
| } |
| |
| void BrowserView::UpdateUIForTabFullscreen() { |
| + if (!frame_->GetFrameView()) |
| + return; |
| frame()->GetFrameView()->UpdateFullscreenTopUI(); |
| } |
| |
| @@ -5025,6 +5087,8 @@ void BrowserView::HideDownloadShelf() { |
| } |
| |
| bool BrowserView::CanUserExitFullscreen() const { |
| + if (!frame_->GetFrameView()) |
| + return true; |
| return frame_->GetFrameView()->CanUserExitFullscreen(); |
| } |
| |
| diff --git chrome/browser/ui/views/frame/browser_view.h chrome/browser/ui/views/frame/browser_view.h |
| index f6251860d8bd3..9756332121fd8 100644 |
| --- chrome/browser/ui/views/frame/browser_view.h |
| +++ chrome/browser/ui/views/frame/browser_view.h |
| @@ -140,11 +140,16 @@ class BrowserView : public BrowserWindow, |
| kUnknown |
| }; |
| |
| + BrowserView(); |
| explicit BrowserView(std::unique_ptr<Browser> browser); |
| + void InitBrowser(std::unique_ptr<Browser> browser); |
| BrowserView(const BrowserView&) = delete; |
| BrowserView& operator=(const BrowserView&) = delete; |
| ~BrowserView() override; |
| |
| + // Key used to bind BrowserView to the Widget with which it is associated. |
| + static const char kBrowserViewKey[]; |
| + |
| void set_frame(BrowserFrame* frame) { |
| frame_ = frame; |
| paint_as_active_subscription_ = |
| @@ -377,7 +382,7 @@ class BrowserView : public BrowserWindow, |
| } |
| |
| actions::ActionItem* root_action_item() const { |
| - return browser_actions_.root_action_item(); |
| + return browser_actions_->root_action_item(); |
| } |
| |
| // Returns true if the view has been initialized. |
| @@ -822,6 +827,9 @@ class BrowserView : public BrowserWindow, |
| // TopContainerBackground::PaintThemeCustomImage for details. |
| gfx::Point GetThemeOffsetFromBrowserView() const; |
| |
| + protected: |
| + virtual ToolbarView* OverrideCreateToolbar() { return nullptr; } |
| + |
| private: |
| // Do not friend BrowserViewLayout. Use the BrowserViewLayoutDelegate |
| // interface to keep these two classes decoupled and testable. |
| @@ -1255,7 +1263,7 @@ class BrowserView : public BrowserWindow, |
| |
| // `browser_actions_` creates the root browser level action along with child |
| // actions. |
| - const BrowserActions browser_actions_; |
| + std::unique_ptr<BrowserActions> browser_actions_; |
| |
| std::unique_ptr<AccessibilityFocusHighlight> accessibility_focus_highlight_; |
| |
| diff --git chrome/browser/ui/views/frame/browser_view_layout.cc chrome/browser/ui/views/frame/browser_view_layout.cc |
| index ab36dd2ae50e2..6dabacc9773fc 100644 |
| --- chrome/browser/ui/views/frame/browser_view_layout.cc |
| +++ chrome/browser/ui/views/frame/browser_view_layout.cc |
| @@ -48,6 +48,10 @@ |
| #include "ui/views/window/client_view.h" |
| #include "ui/views/window/hit_test_utils.h" |
| |
| +#if BUILDFLAG(ENABLE_CEF) |
| +#include "cef/libcef/browser/chrome/views/chrome_views_util.h" |
| +#endif |
| + |
| using views::View; |
| using web_modal::ModalDialogHostObserver; |
| using web_modal::WebContentsModalDialogHost; |
| @@ -578,6 +582,13 @@ int BrowserViewLayout::LayoutWebUITabStrip(int top) { |
| |
| int BrowserViewLayout::LayoutToolbar(int top) { |
| TRACE_EVENT0("ui", "BrowserViewLayout::LayoutToolbar"); |
| +#if BUILDFLAG(ENABLE_CEF) |
| + if (cef::IsCefView(toolbar_)) { |
| + // CEF may take ownership of the toolbar. Early exit to avoid the DCHECK |
| + // in LayoutManager::SetViewVisibility(). |
| + return top; |
| + } |
| +#endif |
| int browser_view_width = vertical_layout_rect_.width(); |
| bool toolbar_visible = delegate_->IsToolbarVisible(); |
| int height = toolbar_visible ? toolbar_->GetPreferredSize().height() : 0; |
| diff --git chrome/browser/ui/views/frame/contents_web_view.cc chrome/browser/ui/views/frame/contents_web_view.cc |
| index 8267a265a8e10..ee08f18e96a34 100644 |
| --- chrome/browser/ui/views/frame/contents_web_view.cc |
| +++ chrome/browser/ui/views/frame/contents_web_view.cc |
| @@ -23,6 +23,11 @@ |
| ContentsWebView::ContentsWebView(content::BrowserContext* browser_context) |
| : views::WebView(browser_context), |
| status_bubble_(nullptr) { |
| + // Mouse events on draggable regions will not be handled by the WebView. |
| + // Avoid the resulting DCHECK in NativeViewHost::OnMousePressed by |
| + // configuring the NativeViewHost not to process events via the view |
| + // hierarchy. |
| + holder()->SetCanProcessEventsWithinSubtree(false); |
| } |
| |
| ContentsWebView::~ContentsWebView() { |
| diff --git chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc |
| index b9b50af047993..70ece4c81ff04 100644 |
| --- chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc |
| +++ chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc |
| @@ -573,6 +573,11 @@ PictureInPictureBrowserFrameView::PictureInPictureBrowserFrameView( |
| frame->GetNativeWindow()->SetEventTargeter( |
| std::make_unique<chromeos::InteriorResizeHandleTargeter>()); |
| #endif |
| + |
| + if (!browser_view->browser()->SupportsWindowFeature( |
| + Browser::FEATURE_TITLEBAR)) { |
| + top_bar_container_view_->SetVisible(false); |
| + } |
| } |
| |
| PictureInPictureBrowserFrameView::~PictureInPictureBrowserFrameView() { |
| @@ -669,18 +674,42 @@ gfx::Rect PictureInPictureBrowserFrameView::GetWindowBoundsForClientBounds( |
| |
| int PictureInPictureBrowserFrameView::NonClientHitTest( |
| const gfx::Point& point) { |
| - // Allow interacting with the buttons. |
| - if (GetLocationIconViewBounds().Contains(point) || |
| - GetBackToTabControlsBounds().Contains(point) || |
| - GetCloseControlsBounds().Contains(point)) { |
| - return HTCLIENT; |
| + const bool frameless = !top_bar_container_view_->GetVisible(); |
| + if (!frameless) { |
| + // Allow interacting with the buttons. |
| + if (GetLocationIconViewBounds().Contains(point) || |
| + GetBackToTabControlsBounds().Contains(point) || |
| + GetCloseControlsBounds().Contains(point)) { |
| + return HTCLIENT; |
| + } |
| + |
| + for (size_t i = 0; i < content_setting_views_.size(); i++) { |
| + if (GetContentSettingViewBounds(i).Contains(point)) { |
| + return HTCLIENT; |
| + } |
| + } |
| } |
| |
| - for (size_t i = 0; i < content_setting_views_.size(); i++) { |
| - if (GetContentSettingViewBounds(i).Contains(point)) { |
| - return HTCLIENT; |
| +#if BUILDFLAG(ENABLE_CEF) |
| + if (frameless) { |
| + // Match logic in BrowserView::ShouldDescendIntoChildForEventHandling. |
| + const auto draggable_region = |
| + browser_view()->browser()->cef_delegate()->GetDraggableRegion(); |
| + if (draggable_region.has_value()) { |
| + // Draggable regions are defined relative to the web contents. |
| + gfx::Point point_in_contents_web_view_coords(point); |
| + views::View::ConvertPointToTarget(GetWidget()->GetRootView(), |
| + browser_view()->contents_web_view(), |
| + &point_in_contents_web_view_coords); |
| + |
| + if (draggable_region->contains( |
| + point_in_contents_web_view_coords.x(), |
| + point_in_contents_web_view_coords.y())) { |
| + return HTCAPTION; |
| + } |
| } |
| } |
| +#endif // BUILDFLAG(ENABLE_CEF) |
| |
| // Allow dragging and resizing the window. |
| int window_component = GetHTComponentForFrame( |
| @@ -747,7 +776,8 @@ void PictureInPictureBrowserFrameView::Layout() { |
| gfx::Rect content_area = GetLocalBounds(); |
| content_area.Inset(FrameBorderInsets()); |
| gfx::Rect top_bar = content_area; |
| - top_bar.set_height(kTopControlsHeight); |
| + top_bar.set_height( |
| + top_bar_container_view_->GetVisible() ? kTopControlsHeight : 0); |
| top_bar_container_view_->SetBoundsRect(top_bar); |
| #if !BUILDFLAG(IS_ANDROID) |
| if (auto_pip_setting_overlay_) { |
| @@ -1207,7 +1237,8 @@ gfx::Insets PictureInPictureBrowserFrameView::ResizeBorderInsets() const { |
| } |
| |
| int PictureInPictureBrowserFrameView::GetTopAreaHeight() const { |
| - return FrameBorderInsets().top() + kTopControlsHeight; |
| + return FrameBorderInsets().top() + |
| + (top_bar_container_view_->GetVisible() ? kTopControlsHeight : 0); |
| } |
| |
| gfx::Size PictureInPictureBrowserFrameView::GetNonClientViewAreaSize() const { |
| diff --git chrome/browser/ui/views/page_action/page_action_icon_controller.cc chrome/browser/ui/views/page_action/page_action_icon_controller.cc |
| index ca602db58de3a..1db448a2526a4 100644 |
| --- chrome/browser/ui/views/page_action/page_action_icon_controller.cc |
| +++ chrome/browser/ui/views/page_action/page_action_icon_controller.cc |
| @@ -97,6 +97,12 @@ void PageActionIconController::Init(const PageActionIconParams& params, |
| }; |
| |
| for (PageActionIconType type : params.types_enabled) { |
| +#if BUILDFLAG(ENABLE_CEF) |
| + if (params.browser && params.browser->cef_delegate() && |
| + !params.browser->cef_delegate()->IsPageActionIconVisible(type)) { |
| + continue; |
| + } |
| +#endif |
| switch (type) { |
| case PageActionIconType::kPaymentsOfferNotification: |
| add_page_action_icon( |
| diff --git chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc |
| index 734c741d1f2fa..739b468cfc0d8 100644 |
| --- chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc |
| +++ chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc |
| @@ -560,29 +560,41 @@ gfx::Range BrowserTabStripController::ListTabsInGroup( |
| } |
| |
| bool BrowserTabStripController::IsFrameCondensed() const { |
| + if (!GetFrameView()) |
| + return false; |
| return GetFrameView()->IsFrameCondensed(); |
| } |
| |
| bool BrowserTabStripController::HasVisibleBackgroundTabShapes() const { |
| + if (!GetFrameView()) |
| + return false; |
| return GetFrameView()->HasVisibleBackgroundTabShapes( |
| BrowserFrameActiveState::kUseCurrent); |
| } |
| |
| bool BrowserTabStripController::EverHasVisibleBackgroundTabShapes() const { |
| + if (!GetFrameView()) |
| + return false; |
| return GetFrameView()->EverHasVisibleBackgroundTabShapes(); |
| } |
| |
| bool BrowserTabStripController::CanDrawStrokes() const { |
| + if (!GetFrameView()) |
| + return false; |
| return GetFrameView()->CanDrawStrokes(); |
| } |
| |
| SkColor BrowserTabStripController::GetFrameColor( |
| BrowserFrameActiveState active_state) const { |
| + if (!GetFrameView()) |
| + return SK_ColorWHITE; |
| return GetFrameView()->GetFrameColor(active_state); |
| } |
| |
| absl::optional<int> BrowserTabStripController::GetCustomBackgroundId( |
| BrowserFrameActiveState active_state) const { |
| + if (!GetFrameView()) |
| + return absl::nullopt; |
| return GetFrameView()->GetCustomBackgroundId(active_state); |
| } |
| |
| diff --git chrome/browser/ui/views/toolbar/toolbar_view.cc chrome/browser/ui/views/toolbar/toolbar_view.cc |
| index 26de912e300e9..c3761d1cb060c 100644 |
| --- chrome/browser/ui/views/toolbar/toolbar_view.cc |
| +++ chrome/browser/ui/views/toolbar/toolbar_view.cc |
| @@ -186,7 +186,7 @@ class TabstripLikeBackground : public views::Background { |
| void Paint(gfx::Canvas* canvas, views::View* view) const override { |
| bool painted = TopContainerBackground::PaintThemeCustomImage(canvas, view, |
| browser_view_); |
| - if (!painted) { |
| + if (!painted && browser_view_->frame()->GetFrameView()) { |
| SkColor frame_color = |
| browser_view_->frame()->GetFrameView()->GetFrameColor( |
| BrowserFrameActiveState::kUseCurrent); |
| @@ -215,12 +215,13 @@ END_METADATA |
| //////////////////////////////////////////////////////////////////////////////// |
| // ToolbarView, public: |
| |
| -ToolbarView::ToolbarView(Browser* browser, BrowserView* browser_view) |
| +ToolbarView::ToolbarView(Browser* browser, BrowserView* browser_view, |
| + absl::optional<DisplayMode> display_mode) |
| : AnimationDelegateViews(this), |
| browser_(browser), |
| browser_view_(browser_view), |
| app_menu_icon_controller_(browser->profile(), this), |
| - display_mode_(GetDisplayMode(browser)) { |
| + display_mode_(display_mode ? *display_mode : GetDisplayMode(browser)) { |
| SetID(VIEW_ID_TOOLBAR); |
| |
| container_view_ = AddChildView(std::make_unique<ContainerView>()); |
| @@ -245,6 +246,19 @@ ToolbarView::~ToolbarView() { |
| } |
| |
| void ToolbarView::Init() { |
| +#if BUILDFLAG(ENABLE_CEF) |
| + using ToolbarButtonType = cef::BrowserDelegate::ToolbarButtonType; |
| + auto button_visible = [this](ToolbarButtonType type) { |
| + if (this->browser_->cef_delegate()) { |
| + return this->browser_->cef_delegate()->IsToolbarButtonVisible(type); |
| + } |
| + return true; |
| + }; |
| + #define BUTTON_VISIBLE(type) button_visible(ToolbarButtonType::type) |
| +#else |
| + #define BUTTON_VISIBLE(type) true |
| +#endif |
| + |
| #if defined(USE_AURA) |
| // Avoid generating too many occlusion tracking calculation events before this |
| // function returns. The occlusion status will be computed only once once this |
| @@ -269,12 +283,13 @@ void ToolbarView::Init() { |
| |
| auto location_bar = std::make_unique<LocationBarView>( |
| browser_, browser_->profile(), browser_->command_controller(), this, |
| - display_mode_ != DisplayMode::NORMAL); |
| + display_mode_ != DisplayMode::NORMAL && !browser_->toolbar_overridden()); |
| // Make sure the toolbar shows by default. |
| size_animation_.Reset(1); |
| |
| std::unique_ptr<DownloadToolbarButtonView> download_button; |
| - if (download::IsDownloadBubbleEnabled(browser_->profile())) { |
| + if (download::IsDownloadBubbleEnabled(browser_->profile()) && |
| + BUTTON_VISIBLE(kDownload)) { |
| download_button = |
| std::make_unique<DownloadToolbarButtonView>(browser_view_); |
| } |
| @@ -356,8 +371,10 @@ void ToolbarView::Init() { |
| } |
| } |
| std::unique_ptr<media_router::CastToolbarButton> cast; |
| - if (media_router::MediaRouterEnabled(browser_->profile())) |
| + if (media_router::MediaRouterEnabled(browser_->profile()) && |
| + BUTTON_VISIBLE(kCast)) { |
| cast = media_router::CastToolbarButton::Create(browser_); |
| + } |
| |
| std::unique_ptr<MediaToolbarButtonView> media_button; |
| if (base::FeatureList::IsEnabled(media::kGlobalMediaControls)) { |
| @@ -367,7 +384,8 @@ void ToolbarView::Init() { |
| |
| std::unique_ptr<send_tab_to_self::SendTabToSelfToolbarIconView> |
| send_tab_to_self_button; |
| - if (!browser_->profile()->IsOffTheRecord()) { |
| + if (!browser_->profile()->IsOffTheRecord() && |
| + BUTTON_VISIBLE(kSendTabToSelf)) { |
| send_tab_to_self_button = |
| std::make_unique<send_tab_to_self::SendTabToSelfToolbarIconView>( |
| browser_view_); |
| @@ -375,7 +393,7 @@ void ToolbarView::Init() { |
| |
| std::unique_ptr<SidePanelToolbarButton> side_panel_button; |
| std::unique_ptr<SidePanelToolbarContainer> side_panel_toolbar_container; |
| - if (browser_view_->unified_side_panel()) { |
| + if (browser_view_->unified_side_panel() && BUTTON_VISIBLE(kSidePanel)) { |
| if (base::FeatureList::IsEnabled(features::kSidePanelPinning)) { |
| // TODO(b:299463334): Use the new SidePanelContainer which supports |
| // ActionItems |
| diff --git chrome/browser/ui/views/toolbar/toolbar_view.h chrome/browser/ui/views/toolbar/toolbar_view.h |
| index 2bca1ba1b72eb..f7b0cf9d09e6e 100644 |
| --- chrome/browser/ui/views/toolbar/toolbar_view.h |
| +++ chrome/browser/ui/views/toolbar/toolbar_view.h |
| @@ -91,7 +91,8 @@ class ToolbarView : public views::AccessiblePaneView, |
| // needs to be displayed. |
| }; |
| |
| - ToolbarView(Browser* browser, BrowserView* browser_view); |
| + ToolbarView(Browser* browser, BrowserView* browser_view, |
| + absl::optional<DisplayMode> display_mode); |
| ToolbarView(const ToolbarView&) = delete; |
| ToolbarView& operator=(const ToolbarView&) = delete; |
| ~ToolbarView() override; |
| diff --git chrome/browser/ui/web_applications/draggable_region_host_impl.cc chrome/browser/ui/web_applications/draggable_region_host_impl.cc |
| index 460a2b08f964b..cb1b67d8b1213 100644 |
| --- chrome/browser/ui/web_applications/draggable_region_host_impl.cc |
| +++ chrome/browser/ui/web_applications/draggable_region_host_impl.cc |
| @@ -11,6 +11,24 @@ |
| #include "chrome/common/chrome_features.h" |
| #include "mojo/public/cpp/bindings/self_owned_receiver.h" |
| |
| +namespace { |
| + |
| +bool IsSupported(Browser* browser) { |
| + if (web_app::AppBrowserController::IsWebApp(browser)) |
| + return true; |
| + |
| +#if BUILDFLAG(ENABLE_CEF) |
| + if (browser->cef_delegate() && |
| + browser->cef_delegate()->SupportsDraggableRegion()) { |
| + return true; |
| + } |
| +#endif |
| + |
| + return false; |
| +} |
| + |
| +} // namespace |
| + |
| DraggableRegionsHostImpl::DraggableRegionsHostImpl( |
| content::RenderFrameHost& render_frame_host, |
| mojo::PendingReceiver<chrome::mojom::DraggableRegions> receiver) |
| @@ -28,7 +46,7 @@ void DraggableRegionsHostImpl::CreateIfAllowed( |
| auto* browser = chrome::FindBrowserWithWebContents(web_contents); |
| |
| // We only want to bind the receiver for PWAs. |
| - if (!web_app::AppBrowserController::IsWebApp(browser)) |
| + if (!IsSupported(browser)) |
| return; |
| |
| // The object is bound to the lifetime of |render_frame_host| and the mojo |
| @@ -43,7 +61,7 @@ void DraggableRegionsHostImpl::UpdateDraggableRegions( |
| auto* browser = chrome::FindBrowserWithWebContents(web_contents); |
| // When a WebApp browser's WebContents is reparented to a tabbed browser, a |
| // draggable regions update may race with the reparenting logic. |
| - if (!web_app::AppBrowserController::IsWebApp(browser)) |
| + if (!IsSupported(browser)) |
| return; |
| |
| SkRegion sk_region; |
| @@ -56,5 +74,12 @@ void DraggableRegionsHostImpl::UpdateDraggableRegions( |
| } |
| |
| auto* app_browser_controller = browser->app_controller(); |
| - app_browser_controller->UpdateDraggableRegion(sk_region); |
| + if (app_browser_controller) { |
| + app_browser_controller->UpdateDraggableRegion(sk_region); |
| + } |
| +#if BUILDFLAG(ENABLE_CEF) |
| + else { |
| + browser->cef_delegate()->UpdateDraggableRegion(sk_region); |
| + } |
| +#endif |
| } |