1diff --git chrome/browser/ui/browser_command_controller.cc chrome/browser/ui/browser_command_controller.cc 2index 92f10fac6b017..cc53da3a0b32e 100644 3--- chrome/browser/ui/browser_command_controller.cc 4+++ chrome/browser/ui/browser_command_controller.cc 5@@ -368,8 +368,10 @@ bool BrowserCommandController::ExecuteCommandWithDisposition( 6 // CommandUpdaterDelegate and CommandUpdater declare this function so we 7 // choose to not implement CommandUpdaterDelegate inside this class and 8 // therefore command_updater_ doesn't have the delegate set). 9- if (!SupportsCommand(id) || !IsCommandEnabled(id)) 10+ if (!SupportsCommand(id) || !IsCommandEnabled(id)) { 11+ LOG(WARNING) << "Invalid/disabled command " << id; 12 return false; 13+ } 14 15 // No commands are enabled if there is not yet any selected tab. 16 // TODO(pkasting): It seems like we should not need this, because either 17@@ -979,11 +981,13 @@ void BrowserCommandController::TabRestoreServiceLoaded( 18 // BrowserCommandController, private: 19 20 bool BrowserCommandController::IsShowingMainUI() { 21- return browser_->SupportsWindowFeature(Browser::FEATURE_TABSTRIP); 22+ return browser_->SupportsWindowFeature(Browser::FEATURE_TABSTRIP) || 23+ browser_->toolbar_overridden(); 24 } 25 26 bool BrowserCommandController::IsShowingLocationBar() { 27- return browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR); 28+ return browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR) || 29+ browser_->toolbar_overridden(); 30 } 31 32 void BrowserCommandController::InitCommandState() { 33diff --git chrome/browser/ui/views/frame/browser_frame.cc chrome/browser/ui/views/frame/browser_frame.cc 34index 6264562165ced..d20f970ba1254 100644 35--- chrome/browser/ui/views/frame/browser_frame.cc 36+++ chrome/browser/ui/views/frame/browser_frame.cc 37@@ -73,15 +73,23 @@ bool IsUsingGtkTheme(Profile* profile) { 38 //////////////////////////////////////////////////////////////////////////////// 39 // BrowserFrame, public: 40 41+BrowserFrame::BrowserFrame() : BrowserFrame(nullptr) {} 42+ 43 BrowserFrame::BrowserFrame(BrowserView* browser_view) 44 : native_browser_frame_(nullptr), 45 root_view_(nullptr), 46 browser_frame_view_(nullptr), 47- browser_view_(browser_view) { 48- browser_view_->set_frame(this); 49+ browser_view_(nullptr) { 50 set_is_secondary_widget(false); 51 // Don't focus anything on creation, selecting a tab will set the focus. 52 set_focus_on_creation(false); 53+ if (browser_view) 54+ InitBrowserView(browser_view); 55+} 56+ 57+void BrowserFrame::InitBrowserView(BrowserView* browser_view) { 58+ browser_view_ = browser_view; 59+ browser_view_->set_frame(this); 60 } 61 62 BrowserFrame::~BrowserFrame() {} 63@@ -141,6 +149,12 @@ gfx::Rect BrowserFrame::GetBoundsForTabStripRegion( 64 } 65 66 int BrowserFrame::GetTopInset() const { 67+ if (!browser_frame_view_) { 68+ // With CEF the browser may already be part of a larger Views layout. Zero 69+ // out the adjustment in BrowserView::GetTopInsetInBrowserView() so that 70+ // the browser isn't shifted to the top of the window. 71+ return browser_view_->y(); 72+ } 73 return browser_frame_view_->GetTopInset(false); 74 } 75 76@@ -175,15 +189,21 @@ void BrowserFrame::GetWindowPlacement(gfx::Rect* bounds, 77 78 content::KeyboardEventProcessingResult BrowserFrame::PreHandleKeyboardEvent( 79 const content::NativeWebKeyboardEvent& event) { 80+ if (!native_browser_frame_) 81+ return content::KeyboardEventProcessingResult::NOT_HANDLED; 82 return native_browser_frame_->PreHandleKeyboardEvent(event); 83 } 84 85 bool BrowserFrame::HandleKeyboardEvent( 86 const content::NativeWebKeyboardEvent& event) { 87+ if (!native_browser_frame_) 88+ return false; 89 return native_browser_frame_->HandleKeyboardEvent(event); 90 } 91 92 void BrowserFrame::OnBrowserViewInitViewsComplete() { 93+ if (!browser_frame_view_) 94+ return; 95 browser_frame_view_->OnBrowserViewInitViewsComplete(); 96 } 97 98@@ -244,6 +264,8 @@ const ui::ThemeProvider* BrowserFrame::GetThemeProvider() const { 99 100 ui::ColorProviderManager::InitializerSupplier* BrowserFrame::GetCustomTheme() 101 const { 102+ if (!browser_view_) 103+ return nullptr; 104 Browser* browser = browser_view_->browser(); 105 auto* app_controller = browser->app_controller(); 106 // Ignore GTK+ for web apps with window-controls-overlay as the 107@@ -369,7 +391,8 @@ void BrowserFrame::SelectNativeTheme() { 108 // Select between regular, dark and GTK theme. 109 ui::NativeTheme* native_theme = ui::NativeTheme::GetInstanceForNativeUi(); 110 111- if (browser_view_->browser()->profile()->IsIncognitoProfile()) { 112+ if (browser_view_ && 113+ browser_view_->browser()->profile()->IsIncognitoProfile()) { 114 // If the flag is enabled, then no matter if we are using the default theme 115 // or not we always use the dark ui instance. 116 if (base::FeatureList::IsEnabled( 117diff --git chrome/browser/ui/views/frame/browser_frame.h chrome/browser/ui/views/frame/browser_frame.h 118index 6a1e9abbc66aa..aa9e3ebe2599c 100644 119--- chrome/browser/ui/views/frame/browser_frame.h 120+++ chrome/browser/ui/views/frame/browser_frame.h 121@@ -53,7 +53,9 @@ enum class TabDragKind { 122 // This is a virtual interface that allows system specific browser frames. 123 class BrowserFrame : public views::Widget, public views::ContextMenuController { 124 public: 125+ BrowserFrame(); 126 explicit BrowserFrame(BrowserView* browser_view); 127+ void InitBrowserView(BrowserView* browser_view); 128 129 BrowserFrame(const BrowserFrame&) = delete; 130 BrowserFrame& operator=(const BrowserFrame&) = delete; 131diff --git chrome/browser/ui/views/frame/browser_view.cc chrome/browser/ui/views/frame/browser_view.cc 132index 8138e50a55072..e01b2274119b8 100644 133--- chrome/browser/ui/views/frame/browser_view.cc 134+++ chrome/browser/ui/views/frame/browser_view.cc 135@@ -291,11 +291,10 @@ using content::WebContents; 136 using views::ColumnSet; 137 using web_modal::WebContentsModalDialogHost; 138 139-namespace { 140+// static 141+const char BrowserView::kBrowserViewKey[] = "__BROWSER_VIEW__"; 142 143-// The name of a key to store on the window handle so that other code can 144-// locate this object using just the handle. 145-const char* const kBrowserViewKey = "__BROWSER_VIEW__"; 146+namespace { 147 148 #if BUILDFLAG(IS_CHROMEOS_ASH) 149 // UMA histograms that record animation smoothness for tab loading animation. 150@@ -683,11 +682,22 @@ class BrowserView::SidePanelButtonHighlighter : public views::ViewObserver { 151 /////////////////////////////////////////////////////////////////////////////// 152 // BrowserView, public: 153 154+BrowserView::BrowserView() : BrowserView(nullptr) {} 155+ 156 BrowserView::BrowserView(std::unique_ptr<Browser> browser) 157 : views::ClientView(nullptr, nullptr), 158- browser_(std::move(browser)), 159 accessibility_mode_observer_( 160 std::make_unique<AccessibilityModeObserver>(this)) { 161+ if (browser) 162+ InitBrowser(std::move(browser)); 163+} 164+ 165+void BrowserView::InitBrowser(std::unique_ptr<Browser> browser) { 166+ DCHECK(!browser_); 167+ browser_ = std::move(browser); 168+ 169+ immersive_mode_controller_ = chrome::CreateImmersiveModeController(); 170+ 171 SetShowIcon(::ShouldShowWindowIcon(browser_.get())); 172 173 // In forced app mode, all size controls are always disabled. Otherwise, use 174@@ -701,7 +711,6 @@ BrowserView::BrowserView(std::unique_ptr<Browser> browser) 175 } 176 177 browser_->tab_strip_model()->AddObserver(this); 178- immersive_mode_controller_ = chrome::CreateImmersiveModeController(); 179 180 // Top container holds tab strip region and toolbar and lives at the front of 181 // the view hierarchy. 182@@ -749,8 +758,15 @@ BrowserView::BrowserView(std::unique_ptr<Browser> browser) 183 contents_container->SetLayoutManager(std::make_unique<ContentsLayoutManager>( 184 devtools_web_view_, contents_web_view_)); 185 186- toolbar_ = top_container_->AddChildView( 187- std::make_unique<ToolbarView>(browser_.get(), this)); 188+ toolbar_ = OverrideCreateToolbar(browser_.get(), this); 189+ if (!toolbar_) { 190+ toolbar_ = new ToolbarView(browser_.get(), this, absl::nullopt); 191+ } else { 192+ browser_->set_toolbar_overridden(true); 193+ // Update state that depends on the above flag. 194+ browser_->command_controller()->FullscreenStateChanged(); 195+ } 196+ top_container_->AddChildView(base::WrapUnique(toolbar_.get())); 197 198 contents_separator_ = 199 top_container_->AddChildView(std::make_unique<ContentsSeparator>()); 200@@ -1587,6 +1603,8 @@ bool BrowserView::ShouldHideUIForFullscreen() const { 201 if (immersive_mode_controller_->IsEnabled()) 202 return false; 203 204+ if (!frame_->GetFrameView()) 205+ return false; 206 return frame_->GetFrameView()->ShouldHideTopUIForFullscreen(); 207 } 208 209@@ -2731,7 +2749,8 @@ BrowserView::GetNativeViewHostsForTopControlsSlide() const { 210 } 211 212 void BrowserView::ReparentTopContainerForEndOfImmersive() { 213- overlay_view_->SetVisible(false); 214+ if (overlay_view_) 215+ overlay_view_->SetVisible(false); 216 top_container()->DestroyLayer(); 217 AddChildViewAt(top_container(), 0); 218 EnsureFocusOrder(); 219@@ -3220,8 +3239,10 @@ void BrowserView::Layout() { 220 221 // TODO(jamescook): Why was this in the middle of layout code? 222 toolbar_->location_bar()->omnibox_view()->SetFocusBehavior( 223- IsToolbarVisible() ? FocusBehavior::ALWAYS : FocusBehavior::NEVER); 224- frame()->GetFrameView()->UpdateMinimumSize(); 225+ (IsToolbarVisible() || browser_->toolbar_overridden()) ? 226+ FocusBehavior::ALWAYS : FocusBehavior::NEVER); 227+ if (frame()->GetFrameView()) 228+ frame()->GetFrameView()->UpdateMinimumSize(); 229 230 // Some of the situations when the BrowserView is laid out are: 231 // - Enter/exit immersive fullscreen mode. 232@@ -3284,6 +3305,11 @@ void BrowserView::AddedToWidget() { 233 SetThemeProfileForWindow(GetNativeWindow(), browser_->profile()); 234 #endif 235 236+ // This browser view may already have a custom button provider set (e.g the 237+ // hosted app frame). 238+ if (!toolbar_button_provider_) 239+ SetToolbarButtonProvider(toolbar_); 240+ 241 toolbar_->Init(); 242 243 // TODO(pbos): Manage this either inside SidePanel or the corresponding button 244@@ -3336,13 +3362,9 @@ void BrowserView::AddedToWidget() { 245 246 EnsureFocusOrder(); 247 248- // This browser view may already have a custom button provider set (e.g the 249- // hosted app frame). 250- if (!toolbar_button_provider_) 251- SetToolbarButtonProvider(toolbar_); 252- 253 frame_->OnBrowserViewInitViewsComplete(); 254- frame_->GetFrameView()->UpdateMinimumSize(); 255+ if (frame_->GetFrameView()) 256+ frame_->GetFrameView()->UpdateMinimumSize(); 257 using_native_frame_ = frame_->ShouldUseNativeFrame(); 258 259 MaybeInitializeWebUITabStrip(); 260@@ -3761,7 +3783,8 @@ void BrowserView::ProcessFullscreen(bool fullscreen, 261 // Undo our anti-jankiness hacks and force a re-layout. 262 in_process_fullscreen_ = false; 263 ToolbarSizeChanged(false); 264- frame_->GetFrameView()->OnFullscreenStateChanged(); 265+ if (frame_->GetFrameView()) 266+ frame_->GetFrameView()->OnFullscreenStateChanged(); 267 } 268 269 bool BrowserView::ShouldUseImmersiveFullscreenForUrl(const GURL& url) const { 270@@ -4052,6 +4075,8 @@ Profile* BrowserView::GetProfile() { 271 } 272 273 void BrowserView::UpdateUIForTabFullscreen() { 274+ if (!frame_->GetFrameView()) 275+ return; 276 frame()->GetFrameView()->UpdateFullscreenTopUI(); 277 } 278 279@@ -4074,6 +4099,8 @@ void BrowserView::HideDownloadShelf() { 280 } 281 282 bool BrowserView::CanUserExitFullscreen() const { 283+ if (!frame_->GetFrameView()) 284+ return true; 285 return frame_->GetFrameView()->CanUserExitFullscreen(); 286 } 287 288diff --git chrome/browser/ui/views/frame/browser_view.h chrome/browser/ui/views/frame/browser_view.h 289index 02d9793dd5c8d..fe8b0cef88375 100644 290--- chrome/browser/ui/views/frame/browser_view.h 291+++ chrome/browser/ui/views/frame/browser_view.h 292@@ -125,11 +125,16 @@ class BrowserView : public BrowserWindow, 293 public webapps::AppBannerManager::Observer { 294 public: 295 METADATA_HEADER(BrowserView); 296+ BrowserView(); 297 explicit BrowserView(std::unique_ptr<Browser> browser); 298+ void InitBrowser(std::unique_ptr<Browser> browser); 299 BrowserView(const BrowserView&) = delete; 300 BrowserView& operator=(const BrowserView&) = delete; 301 ~BrowserView() override; 302 303+ // Key used to bind BrowserView to the Widget with which it is associated. 304+ static const char kBrowserViewKey[]; 305+ 306 void set_frame(BrowserFrame* frame) { frame_ = frame; } 307 BrowserFrame* frame() const { return frame_; } 308 309@@ -705,6 +710,12 @@ class BrowserView : public BrowserWindow, 310 const std::map<std::string, std::string>& extra_data) override; 311 #endif 312 313+ protected: 314+ virtual ToolbarView* OverrideCreateToolbar(Browser* browser, 315+ BrowserView* browser_view) { 316+ return nullptr; 317+ } 318+ 319 private: 320 // Do not friend BrowserViewLayout. Use the BrowserViewLayoutDelegate 321 // interface to keep these two classes decoupled and testable. 322diff --git chrome/browser/ui/views/frame/browser_view_layout.cc chrome/browser/ui/views/frame/browser_view_layout.cc 323index 1d22e4b057c6c..d73b547f09e0b 100644 324--- chrome/browser/ui/views/frame/browser_view_layout.cc 325+++ chrome/browser/ui/views/frame/browser_view_layout.cc 326@@ -40,6 +40,10 @@ 327 #include "ui/views/widget/widget.h" 328 #include "ui/views/window/client_view.h" 329 330+#if BUILDFLAG(ENABLE_CEF) 331+#include "cef/libcef/browser/chrome/views/chrome_views_util.h" 332+#endif 333+ 334 using views::View; 335 using web_modal::WebContentsModalDialogHost; 336 using web_modal::ModalDialogHostObserver; 337@@ -458,6 +462,11 @@ int BrowserViewLayout::LayoutWebUITabStrip(int top) { 338 339 int BrowserViewLayout::LayoutToolbar(int top) { 340 TRACE_EVENT0("ui", "BrowserViewLayout::LayoutToolbar"); 341+ if (cef::IsCefView(toolbar_)) { 342+ // CEF may take ownership of the toolbar. Early exit to avoid the DCHECK 343+ // in LayoutManager::SetViewVisibility(). 344+ return top; 345+ } 346 int browser_view_width = vertical_layout_rect_.width(); 347 bool toolbar_visible = delegate_->IsToolbarVisible(); 348 int height = toolbar_visible ? toolbar_->GetPreferredSize().height() : 0; 349diff --git chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc 350index b5706099ce923..07cebc2f8b65f 100644 351--- chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc 352+++ chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc 353@@ -559,37 +559,53 @@ gfx::Range BrowserTabStripController::ListTabsInGroup( 354 } 355 356 bool BrowserTabStripController::IsFrameCondensed() const { 357+ if (!GetFrameView()) 358+ return false; 359 return GetFrameView()->IsFrameCondensed(); 360 } 361 362 bool BrowserTabStripController::HasVisibleBackgroundTabShapes() const { 363+ if (!GetFrameView()) 364+ return false; 365 return GetFrameView()->HasVisibleBackgroundTabShapes( 366 BrowserFrameActiveState::kUseCurrent); 367 } 368 369 bool BrowserTabStripController::EverHasVisibleBackgroundTabShapes() const { 370+ if (!GetFrameView()) 371+ return false; 372 return GetFrameView()->EverHasVisibleBackgroundTabShapes(); 373 } 374 375 bool BrowserTabStripController::ShouldPaintAsActiveFrame() const { 376+ if (!GetFrameView()) 377+ return false; 378 return GetFrameView()->ShouldPaintAsActive(); 379 } 380 381 bool BrowserTabStripController::CanDrawStrokes() const { 382+ if (!GetFrameView()) 383+ return false; 384 return GetFrameView()->CanDrawStrokes(); 385 } 386 387 SkColor BrowserTabStripController::GetFrameColor( 388 BrowserFrameActiveState active_state) const { 389+ if (!GetFrameView()) 390+ return SK_ColorWHITE; 391 return GetFrameView()->GetFrameColor(active_state); 392 } 393 394 SkColor BrowserTabStripController::GetToolbarTopSeparatorColor() const { 395+ if (!GetFrameView()) 396+ return SK_ColorWHITE; 397 return GetFrameView()->GetToolbarTopSeparatorColor(); 398 } 399 400 absl::optional<int> BrowserTabStripController::GetCustomBackgroundId( 401 BrowserFrameActiveState active_state) const { 402+ if (!GetFrameView()) 403+ return absl::nullopt; 404 return GetFrameView()->GetCustomBackgroundId(active_state); 405 } 406 407diff --git chrome/browser/ui/views/toolbar/toolbar_view.cc chrome/browser/ui/views/toolbar/toolbar_view.cc 408index b5098154b2f7e..4a3121cedf083 100644 409--- chrome/browser/ui/views/toolbar/toolbar_view.cc 410+++ chrome/browser/ui/views/toolbar/toolbar_view.cc 411@@ -168,12 +168,13 @@ auto& GetViewCommandMap() { 412 //////////////////////////////////////////////////////////////////////////////// 413 // ToolbarView, public: 414 415-ToolbarView::ToolbarView(Browser* browser, BrowserView* browser_view) 416+ToolbarView::ToolbarView(Browser* browser, BrowserView* browser_view, 417+ absl::optional<DisplayMode> display_mode) 418 : AnimationDelegateViews(this), 419 browser_(browser), 420 browser_view_(browser_view), 421 app_menu_icon_controller_(browser->profile(), this), 422- display_mode_(GetDisplayMode(browser)) { 423+ display_mode_(display_mode ? *display_mode : GetDisplayMode(browser)) { 424 SetID(VIEW_ID_TOOLBAR); 425 426 UpgradeDetector::GetInstance()->AddObserver(this); 427@@ -208,7 +209,7 @@ void ToolbarView::Init() { 428 #endif 429 auto location_bar = std::make_unique<LocationBarView>( 430 browser_, browser_->profile(), browser_->command_controller(), this, 431- display_mode_ != DisplayMode::NORMAL); 432+ display_mode_ != DisplayMode::NORMAL && !browser_->toolbar_overridden()); 433 // Make sure the toolbar shows by default. 434 size_animation_.Reset(1); 435 436diff --git chrome/browser/ui/views/toolbar/toolbar_view.h chrome/browser/ui/views/toolbar/toolbar_view.h 437index 79efc97f711f1..6915479340275 100644 438--- chrome/browser/ui/views/toolbar/toolbar_view.h 439+++ chrome/browser/ui/views/toolbar/toolbar_view.h 440@@ -93,7 +93,8 @@ class ToolbarView : public views::AccessiblePaneView, 441 // needs to be displayed. 442 }; 443 444- ToolbarView(Browser* browser, BrowserView* browser_view); 445+ ToolbarView(Browser* browser, BrowserView* browser_view, 446+ absl::optional<DisplayMode> display_mode); 447 ToolbarView(const ToolbarView&) = delete; 448 ToolbarView& operator=(const ToolbarView&) = delete; 449 ~ToolbarView() override; 450