MacViewsBrowser: Closing a window is über-janky |
||||||
Issue descriptionChrome Version: 67.0.3381.0 OS: macOS 10.13.3 What steps will reproduce the problem? (1) --enable-features=ViewsBrowserWindows (2) Open a new window with lots of tabs (3) Close the window What is the expected result? The window closes quickly. What happens instead? The window cycles through each tab before it actually closes. Please use labels and text to provide additional information. For graphics-related bugs, please copy/paste the contents of the about:gpu page at the end of this report.
,
Mar 29 2018
** Bulk Edit ** FYI: Starting 04/13 M68 will be in canary, M68 Dev promotion will be on 04/26.
,
Mar 30 2018
Grabbed a sample.
Looks like we're doing a lot of unnecessary layout in either InfoBarContainer::ChangeInfoBarManager or BrowserView::ToolbarSizeChanged or BrowserView::MaybeShowBookmarkBar.
"""
+ ! : | + ! : | + ! : 1113 TabStripModel::InternalCloseTabs(base::span<content::WebContents* const>, unsigned int) (in Google Chrome Framework) load address 0x1012c0000 + 0x464a39f [tab_strip_model.cc:1151]
+ ! : | + ! : | + ! : 1040 CloseWebContentses(WebContentsCloseDelegate*, base::span<content::WebContents* const>, unsigned int) (in Google Chrome Framework) load address 0x1012c0000 + 0x4650098 [vector:639]
+ ! : | + ! : | + ! : | 1040 <name omitted> (in Google Chrome Framework) load address 0x1012c0000 + 0xe0aafe [web_contents_impl.cc:546]
+ ! : | + ! : | + ! : | 1030 content::WebContentsImpl::~WebContentsImpl() (in Google Chrome Framework) load address 0x1012c0000 + 0xe0a231 [weak_ptr.h:243]
+ ! : | + ! : | + ! : | + 991 TabStripModel::DetachWebContentsAt(int) (in Google Chrome Framework) load address 0x1012c0000 + 0x4647e26 [tab_strip_model.cc:321]
+ ! : | + ! : | + ! : | + ! 991 TabStripModel::NotifyIfActiveTabChanged(content::WebContents*, TabStripModel::Notify) (in Google Chrome Framework) load address 0x1012c0000 + 0x46494ff [weak_ptr.h:243]
+ ! : | + ! : | + ! : | + ! 882 Browser::ActiveTabChanged(content::WebContents*, content::WebContents*, int, int) (in Google Chrome Framework) load address 0x1012c0000 + 0x460989e [memory:2603]
+ ! : | + ! : | + ! : | + ! : 712 BrowserView::OnActiveTabChanged(content::WebContents*, content::WebContents*, int, int) (in Google Chrome Framework) load address 0x1012c0000 + 0x48b18c7 [browser_view.cc:2437]
+ ! : | + ! : | + ! : | + ! : | 712 infobars::InfoBarContainer::ChangeInfoBarManager(infobars::InfoBarManager*) (in Google Chrome Framework) load address 0x1012c0000 + 0x39e3602 [vector:633]
+ ! : | + ! : | + ! : | + ! : | 698 BrowserView::ToolbarSizeChanged(bool) (in Google Chrome Framework) load address 0x1012c0000 + 0x48b2366 [browser_view.cc:2442]
+ ! : | + ! : | + ! : | + ! : | + 674 BrowserView::MaybeShowBookmarkBar(content::WebContents*) (in Google Chrome Framework) load address 0x1012c0000 + 0x48b12c4 [browser_view.cc:0]
+ ! : | + ! : | + ! : | + ! : | + ! 672 views::View::AddChildViewAt(views::View*, int) (in Google Chrome Framework) load address 0x1012c0000 + 0x38dfdf5 [view.cc:2724]
+ ! : | + ! : | + ! : | + ! : | + ! : 672 views::View::PropagateAddNotifications(views::View::ViewHierarchyChangedDetails const&, bool) (in Google Chrome Framework) load address 0x1012c0000 + 0x38e0da7 [view.cc:2168]
+ ! : | + ! : | + ! : | + ! : | + ! : 672 views::View::ViewHierarchyChangedImpl(bool, views::View::ViewHierarchyChangedDetails const&) (in Google Chrome Framework) load address 0x1012c0000 + 0x38e0ce5 [view.cc:2197]
+ ! : | + ! : | + ! : | + ! : | + ! : 672 BookmarkBarView::ViewHierarchyChanged(views::View::ViewHierarchyChangedDetails const&) (in Google Chrome Framework) load address 0x1012c0000 + 0x4892653 [bookmark_bar_view.cc:1026]
+ ! : | + ! : | + ! : | + ! : | + ! : 672 BookmarkBarView::UpdateAppearanceForTheme() (in Google Chrome Framework) load address 0x1012c0000 + 0x48926f2 [bookmark_bar_view.cc:2052]
+ ! : | + ! : | + ! : | + ! : | + ! : 557 BookmarkBarView::ConfigureButton(bookmarks::BookmarkNode const*, views::LabelButton*) (in Google Chrome Framework) load address 0x1012c0000 + 0x48956c4 [view.h:562]
+ ! : | + ! : | + ! : | + ! : | + ! : | 557 views::View::NotifyAccessibilityEvent(ax::mojom::Event, bool) (in Google Chrome Framework) load address 0x1012c0000 + 0x38e2cb2 [view.cc:1461]
+ ! : | + ! : | + ! : | + ! : | + ! : | 557 views::NativeViewAccessibilityBase::NotifyAccessibilityEvent(ax::mojom::Event) (in Google Chrome Framework) load address 0x1012c0000 + 0x38fe927 [native_view_accessibility_base.cc:141]
+ ! : | + ! : | + ! : | + ! : | + ! : | 557 ui::AXPlatformNodeMac::NotifyAccessibilityEvent(ax::mojom::Event) (in Google Chrome Framework) load address 0x1012c0000 + 0x338c401 [ax_platform_node_mac.mm:870]
+ ! : | + ! : | + ! : | + ! : | + ! : | 557 non-virtual thunk to views::NativeViewAccessibilityBase::GetData() const (in Google Chrome Framework) load address 0x1012c0000 + 0x38fed30 [native_view_accessibility_base.cc:0]
+ ! : | + ! : | + ! : | + ! : | + ! : | 557 views::NativeViewAccessibilityBase::GetData() const (in Google Chrome Framework) load address 0x1012c0000 + 0x38feca8 [view_accessibility.h:67]
+ ! : | + ! : | + ! : | + ! : | + ! : | 555 views::ViewAccessibility::GetAccessibleNodeData(ui::AXNodeData*) const (in Google Chrome Framework) load address 0x1012c0000 + 0x3869ffa [view_accessibility.cc:78]
+ ! : | + ! : | + ! : | + ! : | + ! : | + 555 (anonymous namespace)::BookmarkButton::GetTooltipText(gfx::Point const&, std::__1::basic_string<unsigned short, base::string16_internals::string16_char_traits, std::__1::allocator<unsigned short> >*) const (in Google Chrome Framework) load address 0x1012c0000 + 0x4897a1e [string:2884]
+ ! : | + ! : | + ! : | + ! : | + ! : | + 530 BookmarkBarView::CreateToolTipForURLAndTitle(int, gfx::FontList const&, GURL const&, std::__1::basic_string<unsigned short, base::string16_internals::string16_char_traits, std::__1::allocator<unsigned short> > const&) (in Google Chrome Framework) load address 0x1012c0000 + 0x4891b68 [bookmark_bar_view.cc:788]
+ ! : | + ! : | + ! : | + ! : | + ! : | + ! 115 url_formatter::ElideUrl(GURL const&, gfx::FontList const&, float, gfx::Typesetter) (in Google Chrome Framework) load address 0x1012c0000 + 0x355e363 [elide_url.cc:176]
"""
,
Mar 31 2018
The following revision refers to this bug: https://chromium.googlesource.com/chromium/src.git/+/df82d0ab2a5e7e59a446f3c925d21ce992b18046 commit df82d0ab2a5e7e59a446f3c925d21ce992b18046 Author: erikchen <erikchen@chromium.org> Date: Sat Mar 31 00:44:51 2018 Don't relayout InfobarContainer if there are no changes. When closing a Window on MacViews, this logic would needlessly trigger relayouts for every tab close, causing massive jank. Bug: 826287 Change-Id: I30bac1e8c12eb16305be8c7630082adbeae54c6d Reviewed-on: https://chromium-review.googlesource.com/988853 Reviewed-by: Erik Chen <erikchen@chromium.org> Reviewed-by: Peter Kasting <pkasting@chromium.org> Commit-Queue: Peter Kasting <pkasting@chromium.org> Cr-Commit-Position: refs/heads/master@{#547339} [modify] https://crrev.com/df82d0ab2a5e7e59a446f3c925d21ce992b18046/components/infobars/core/infobar_container.cc
,
Apr 2 2018
We are unable to verify the fix on 67.0.3386.0 Using Mac 10.13.1 i.e., we are able to see window cycling through each tab before it's actually closed. @erikchen: Could you please let us know if there are any fixes to be landed. Thanks!
,
Apr 2 2018
The behavior is better than before but there are still improvements to be made. Here's the relevant sample from latest Canary:
"""
+ 2301 TabStripModel::CloseAllTabs() (in Google Chrome Framework) load address 0x10dda1000 + 0x466f3ef [vector:439]
+ 2301 TabStripModel::InternalCloseTabs(base::span<content::WebContents* const>, unsigned int) (in Google Chrome Framework) load address 0x10dda1000 + 0x466f5df [tab_strip_model.cc:1151]
+ 2301 CloseWebContentses(WebContentsCloseDelegate*, base::span<content::WebContents* const>, unsigned int) (in Google Chrome Framework) load address 0x10dda1000 + 0x46752d8 [vector:639]
+ 2301 <name omitted> (in Google Chrome Framework) load address 0x10dda1000 + 0xe0d23e [web_contents_impl.cc:546]
+ 2225 content::WebContentsImpl::~WebContentsImpl() (in Google Chrome Framework) load address 0x10dda1000 + 0xe0c971 [weak_ptr.h:243]
+ ! 2158 TabStripModel::DetachWebContentsAt(int) (in Google Chrome Framework) load address 0x10dda1000 + 0x466d066 [tab_strip_model.cc:321]
+ ! : 2158 TabStripModel::NotifyIfActiveTabChanged(content::WebContents*, TabStripModel::Notify) (in Google Chrome Framework) load address 0x10dda1000 + 0x466e73f [weak_ptr.h:243]
+ ! : 1965 Browser::ActiveTabChanged(content::WebContents*, content::WebContents*, int, int) (in Google Chrome Framework) load address 0x10dda1000 + 0x46298ce [memory:2603]
+ ! : | 1816 BrowserView::OnActiveTabChanged(content::WebContents*, content::WebContents*, int, int) (in Google Chrome Framework) load address 0x10dda1000 + 0x48c3720 [web_contents_user_data.h:47]
+ ! : | + 1816 views::WebView::SetWebContents(content::WebContents*) (in Google Chrome Framework) load address 0x10dda1000 + 0x495b9eb [webview.cc:366]
+ ! : | + 1809 views::WebView::AttachWebContents() (in Google Chrome Framework) load address 0x10dda1000 + 0x495c499 [webview.cc:319]
+ ! : | + ! 1804 views::NativeViewHost::Attach(NSView*) (in Google Chrome Framework) load address 0x10dda1000 + 0x38d0bdd [native_view_host.cc:42]
+ ! : | + ! : 1804 views::NativeViewHost::Layout() (in Google Chrome Framework) load address 0x10dda1000 + 0x38d0de2 [native_view_host.cc:117]
+ ! : | + ! : 1804 views::NativeViewHostMac::ShowWidget(int, int, int, int, int, int) (in Google Chrome Framework) load address 0x10dda1000 + 0x38d1634 [native_view_host_mac.mm:140]
+ ! : | + ! : 1804 -[NSView(NSInternal) _setHidden:setNeedsDisplay:] (in AppKit) + 326 [0x7fff2c3b6aa1]
+ ! : | + ! : 1803 -[NSView _recursiveLostHiddenAncestor] (in AppKit) + 127 [0x7fff2c43dc59]
+ ! : | + ! : | 1803 content::WebContentsImpl::WasShown() (in Google Chrome Framework) load address 0x10dda1000 + 0xe14535 [web_contents_impl.cc:1502]
+ ! : | + ! : | 1799 content::RenderWidgetHostViewMac::Show() (in Google Chrome Framework) load address 0x10dda1000 + 0xd3a0bd [memory:2603]
+ ! : | + ! : | + 1799 content::RenderWidgetHostImpl::PauseForPendingResizeOrRepaints() (in Google Chrome Framework) load address 0x10dda1000 +
"""
Each time we are closing a tab, the destructor of WebContentsImpl is calling back into TabStripModel, which in turns needs to redraw the entire window + web contents. Updating the web contents synchronously waits for the GPU process to draw + update before returning, thus causing the observed behavior.
,
Apr 2 2018
+ sky, pkasting, ellyjones [as a subset of you will need to review the CL]. The root problem is that TabStripModel::InternalCloseTabs sends the ActiveTabChanged notification for every tab that is going to be closed. This results in a full layout/paint of both the browser and web contents. Painting web contents will synchronously block the browser main thread waiting for the GPU process. Solution 1: When closing multiple tabs, TabStripModel should send the minimal number of notifications necessary for correctness. e.g. If there are 5 tabs [numbered 1 through 5], and if we are trying to close 3, 4, and 5, then there should be a single ActiveTabChanged notification, indicating a switch from 5->2, rather than 3 notifications for: 5->4, 4->3 and 3->2. Solution 2: Add additional state that indicates that TabStripModel is batch closing tabs [or equivalent]. Expose this state to consumers of ActiveTabChanged notifications, and have them do less work. [e.g. redraw tab strip but not web contents]. Solution (2) allows for more custom behaviors at the expense of additional complexity. Solution (1) is simple and gets the job done. I prefer Solution (1).
,
Apr 2 2018
#7: (1) sounds best to me, especially if closing all the tabs means we send zero ActiveTabChanged notifications :). I don't like (2) but I think it's similar to what the existing Cocoa code does with "rapid tab closure".
,
Apr 12 2018
The correct solution will look something like this: 1) Figure out the tabs that are going to be closed. 2) Send the minimal set of notifications that is still correct. 3) Perform the closes [delete WebContents, update internal state of the TabStripModel - WebContentsData]. (3) must follow (2) because the notifications include raw WebContents pointers to the WebContents that is about to be deleted/removed from the tab strip. Unfortunately, the current logic is slightly broken. a) CloseWebContentses deletes the WebContents b) Half way through the destructor of WebContents, TabStripModel gets an observer notification. c) TabStripModel calls TabStripModel::DetachWebContentsAt to update internal state, and send notifications, using a half destructed WebContents. The reason that (b) and (c) are inverted is that TabStripModel is trying to react to someone else deleting the WebContents out from under it. This is due to confusion in ownership semantics - who exactly owns the WebContents in the TabStripModel? The public APIs all claim that the TabStripModel take ownership of the WebContents, so that's a good sign. There are two problems: i) As indicated before, TabStripModel's implementation seems uncertain about ownership semantics. ii) The destructor of TabStripModel does not destroy the WebContents it owns. I've got a CL in progress which checks (i) - specifically, checking that no one is attempting to delete a WebContents out from under the TabStripModel: https://chromium-review.googlesource.com/c/chromium/src/+/1010521 Assuming that works out, I will fix TabStripModel to assume explicit ownership of the WebContents. I guess fixing (ii) in the process.
,
Apr 13 2018
,
Apr 25 2018
Pls mark the bug as fixed if CL is landed in trunk and nothing else is pending. Thank you.
,
May 4 2018
I've updated all non-test WebContents constructors to return unique_ptrs. I've got CLs in progress to use strong ownership semantics for all non-test consumers of WebContents [none of them are doing anything funky, the CLs are all just refactors to use unique_ptr]. GuestViews are the exception. The ownership semantics there are well-understood but difficult to convert to using unique_ptrs. I have a plan to do this, but it's not clear to me how difficult it will be to execute. Regardless, I've confirmed that it has no effect on TabStripModel. Happy to go into details if we deem that appropriate. I have performed the following steps to confirm that there are no unexpected deletions of WebContents 1) On macOS, Linux, CrOS, Windows: 2) Make the destructor of WebContents protected. 3) Add the statement "friend std::unique_ptr<WebContents>::deleter_type" to WebContents. 4) Compile chrome
,
May 5 2018
The following revision refers to this bug: https://chromium.googlesource.com/chromium/src.git/+/88ff8d7f13681a78d0bf927e46d1878af059e629 commit 88ff8d7f13681a78d0bf927e46d1878af059e629 Author: Erik Chen <erikchen@chromium.org> Date: Sat May 05 01:55:31 2018 Move WebContentsCloser back into TabStripModel. This CL is a refactor with no intended behavior change. The logic was pulled out in a refactor [https://chromium-review.googlesource.com/780143] to support the experimental tab strip model. Since then, the experimental tab strip has been deleted. Pulling the logic back into TabStripModel is the first step to improving the performance of TabStripModel::InternalCloseTabs by reducing the number of unnecessary notifications sent to observers. Bug: 826287 Change-Id: I371bd964d8ea6338d50454a0de329a70e5090fae Reviewed-on: https://chromium-review.googlesource.com/1010642 Commit-Queue: Erik Chen <erikchen@chromium.org> Reviewed-by: Scott Violet <sky@chromium.org> Cr-Commit-Position: refs/heads/master@{#556289} [modify] https://crrev.com/88ff8d7f13681a78d0bf927e46d1878af059e629/chrome/browser/ui/BUILD.gn [modify] https://crrev.com/88ff8d7f13681a78d0bf927e46d1878af059e629/chrome/browser/ui/tabs/tab_strip_model.cc [modify] https://crrev.com/88ff8d7f13681a78d0bf927e46d1878af059e629/chrome/browser/ui/tabs/tab_strip_model.h [delete] https://crrev.com/e1b6426ea838e2c0dc2d2b1893c5873cce1e53ac/chrome/browser/ui/tabs/web_contents_closer.cc [delete] https://crrev.com/e1b6426ea838e2c0dc2d2b1893c5873cce1e53ac/chrome/browser/ui/tabs/web_contents_closer.h
,
May 9 2018
Can this be marked as fixed if nothing else is pending?
,
May 9 2018
There are more CLs coming.
,
May 11 2018
The following revision refers to this bug: https://chromium.googlesource.com/chromium/src.git/+/2969739effcde2fdba105d25d8db903bdfdb5db0 commit 2969739effcde2fdba105d25d8db903bdfdb5db0 Author: erikchen <erikchen@chromium.org> Date: Fri May 11 16:54:23 2018 Explicit ownership semantics for TabStripModel. This CL updates TabStripModel's internal implementation to: * Use strong ownership semantics for WebContents. * Batch tab-removal notifications. This cannot be broken into two CLs because non-batched removal notifications required unclear ownership of WebContents. More details to follow. Previously, closing tabs was not safe for two reasons: * TabStripModelObserver callbacks were sending a half-destroyed WebContents instance to obsevers, which would then query internal state of the WebContents instance. * TabStripModelObserver callbacks could cause re-entrancy, which in turn led to internal state changes during observer list iteration! Previous implementation details: * TabStripModel would potentially try to close many tabs at once. * In a loop [whose iterator would be dynamically modified via WebContents observers] * TabStripModel would delete the WebContents instance. * This triggered TabStripModel's own WebContents destruction observer. * Which in turn called TabStripModel::DetachWebContentsAt() * Which then sent callbacks to TabStripModel observers using a half-destroyed WebContents instance. * Some of these observers would then access state on the half-destroyed WebContents instance. * Other observers would call back into TabStripModel and perform state modifications [e.g. remove another tab]. By observing its own WebContents during callbacks to observers, TabStripModel was able to react to some types of re-entrant behaviors during tab closure [e.g. closing a tab]. However, it was not able to correctly react to other types of re-entrant behaviors [e.g. adding a tab]. Furthermore, observers of TabStripModel needed to query internal state of TabStripModel during observer callbacks. This meant that behavior was dependent on observer ordering. e.g. If there are two observers A and B, and A removes tabs on its observer callback, and B checks for the currently selected index, then the ordering between A and B will cause behavior differences for B. This CL fixes both of these issues. Closing tabs now looks like this: * TabStripModel will potentially try to close many tabs at once. * TabStripModel updates its internal state to reflect this change. It is now in an internally consistent state [in case of re-entrancy from observers]. * For each tab that was removed: * TabStripModel dispatches observer callbacks. * TabStripModel deletes the WebContents. WebContents deletion now happens after observer callbacks, so there are no issues with half-destroyed WebContents instances. Tab closure is now re-entrant safe, since internal state is consistent before sending any observer callbacks, and observer callbacks are not affected by changing internal state. This CL discovered several issues: * Several tests suites were failing to set an active tab before running test logic. This meant that they weren't testing real behavior of the TabStripModel. * Several observers of TabStripModel were relying on assumptions about the internal state of the TabStripModel at the time of the observer callback [e.g. if a selected tab was detached, then the index of the detached tab in TabDetachedAt() is *still* the currently active index of the TabStripModel.] This was not robust against earlier observers performing re-entrancy into the TabStripModel]. * Several test suites were deleting WebContents owned by the TabStripModel. * Two tests in DetachToBrowserTabDragControllerTest were testing a condition that could never occur in the wild. They were testing that the TabStripModel behaves correctly if, halfway through a drag when a tab has been detached but not attached, the tab is deleted. With strong ownership semantics, the only way for a tab to be deleted is through TabStripModel APIs. Which means that in the brief period of time when a tab is not attached to any TabStripModel, it is not possible for the tab to be deleted. This behavior was previously true as well, it's just that it wasn't obvious that this condition couldn't occur. * Browser::TabClosingAt was unnecessarily calling SetDelegate(). That is also called by TabDetachedAtImpl(). * There are several places in the code that relied on the assumption that the TabStripModel WebContentsDestroyed() callbacks would occur before their own WebContentsDestroyed() callbacks. Bug: 826287 Change-Id: Ia44ad3a405844174407ffddb66e3728bbba44515 Reviewed-on: https://chromium-review.googlesource.com/1045790 Reviewed-by: Scott Violet <sky@chromium.org> Reviewed-by: Charlie Harrison <csharrison@chromium.org> Commit-Queue: Erik Chen <erikchen@chromium.org> Cr-Commit-Position: refs/heads/master@{#557906} [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/media/webrtc/tab_desktop_media_list_unittest.cc [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/metrics/tab_stats_tracker_browsertest.cc [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/page_load_metrics/metrics_web_contents_observer.h [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/resource_coordinator/tab_activity_watcher_unittest.cc [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/resource_coordinator/tab_manager_unittest.cc [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/ui/browser.cc [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/ui/tabs/tab_strip_model.cc [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/ui/tabs/tab_strip_model.h [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/ui/tabs/tab_strip_model_observer.h [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/ui/tabs/tab_strip_model_unittest.cc [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/ui/views/tabs/tab_strip.cc [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/chrome/browser/ui/views/tabs/tab_strip.h [modify] https://crrev.com/2969739effcde2fdba105d25d8db903bdfdb5db0/components/web_modal/web_contents_modal_dialog_manager.h
,
May 14 2018
|
||||||
►
Sign in to add a comment |
||||||
Comment 1 by ellyjo...@chromium.org
, Mar 29 2018Owner: erikc...@chromium.org
Status: Assigned (was: Untriaged)