Skip to content
This repository has been archived by the owner on Dec 11, 2019. It is now read-only.

Fix tab auto-discarding ability #13120

Merged
merged 1 commit into from
Feb 23, 2018
Merged

Fix tab auto-discarding ability #13120

merged 1 commit into from
Feb 23, 2018

Conversation

petemill
Copy link
Member

@petemill petemill commented Feb 13, 2018

Fix #13210 (13120 / 13210 - what are the chances...)
Fix #1796
Depends on #13118 (new Debug menu for manually discarding a tab)

Depends on new muon version with brave/muon#504

Blocking issues

As described in #13210:

What this fixes

Ensure that tab/WebContents objects are added to memory as soon as they are created.

Use muon's new tab-replaced-at event (instead of .tab-id-changed) to ensure state gets updated correctly when a tab is discarded, as well as communicating temporary contents replacement to a window's frame (such as when detaching a tab to a new window, the tab in the old window gets a temporary WebContents that then gets destroyed).

In the case of tabs which get discarded, they are replaced with a fresh, clean WebContents. We were not receiving this new object, so when the renderer asked for this new object's tabId to be made active, the cache did not have that object.

We must also handle updating references to the old TabId to the new TabId in state, in the module which remembers opener tab IDs and the module which handles historical tab 'active' history for a window.

The guestInstanceId for a tab also changes when a discarded tab is reloaded, so make sure we pass that new value in the frame options when a discarded tab is detached to a new window.

Adds more relevant log entries under the --debug-tab-events flag.

Testing

Verification steps specified on #13210

And in addition, some modified tests from #12193, since now a tab ID can change, and we must update history for which tabs were active with the new tab ID.
Here those modified tests are:

NOTE: on Windows, you should disable tab preview

Closing

Parent tab

  • Preferences: default (When closing an active tab, select its parent tab)
  • Visit a site
  • Cmd-click 5 links to open 'child' tabs from that site
  • Switch active focus to the 3rd tab
  • Right-click the 1st tab and choose Discard
  • Close the 3rd (current) tab
  • Expected: Tab active focus switches to the 1st tab (parent)

Parent tab 2

  • Preferences: default (When closing an active tab, select its parent tab)
  • Visit a site
  • Cmd-click 5 links to open 'child' tabs from that site
  • Right-click the 3rd tab and choose Discard
  • Switch active focus to the 3rd tab, so it loads
  • Close the 3rd (current) tab
  • Expected: Tab active focus switches to the 1st tab (parent)

Next tab

  • Select the last tab
  • Right-click the previously-active 1st tab (new tab page) and choose Discard
  • Switch active focus back to that 1st tab, so it loads
  • Right-click -> Discard all the other tabs (except the current active tab)
  • Close the (active) new tab page
  • Expected: Tab active focus switches to the tab that was 2nd, and now is 1st
  • Preferences: When closing an active tab Select the next tab
  • Open the new tab page
  • Cmd-click all the top sites icons to open 'child' tabs
  • Switch active focus to the 3rd tab
  • Close the 3rd (current) tab
  • Expected: Tab active focus switches to the tab that was 4th, and is now 3rd

Last active tab

  • Preferences: When closing an active tab Select the last viewed tab
  • Open at least 5 tabs
  • Make the 1st tab active
  • Make the 3rd tab active
  • Make the last tab active
  • Right-click -> Discard all the tabs (except the current active tab)
  • Close the currently-active tab (the last tab)
  • Expected: Tab active focus switches to the 3rd tab
  • Close the currently-active tab (the 3rd tab)
  • Expected: Tab active focus switches to the 1st tab
  • Close the currently-active tab (the 1st tab)
  • Expected: Tab active focus switches to the 2nd tab

Detaching

Parent tab

  • Preferences: detault (When closing an active tab, select its parent tab)
  • Open the new tab page
  • Cmd-click all the top sites icons to open 'child' tabs
  • Switch active focus to the 3rd tab
  • Right-click / drag -> Detach the 3rd (current) tab
  • Expected: Tab active focus switches to the 1st tab (new tab page)

Next tab

  • Right-click / drag -> Detach the (active) new tab page
  • Expected: Tab active focus switches to the tab that was 2nd, and now is 1st
  • Preferences: When closing an active tab Select the next tab
  • Open the new tab page
  • Cmd-click all the top sites icons to open 'child' tabs
  • Switch active focus to the 3rd tab
  • Right-click / drag -> Detach the 3rd (current) tab
  • Expected: Tab active focus switches to the tab that was 4th, and is now 3rd

Last active tab

  • Preferences: When closing an active tab Select the last viewed tab
  • Open at least 5 tabs
  • Make the 1st tab active
  • Make the 3rd tab active
  • Make the last tab active
  • Right-click / drag -> Detach the currently-active tab (the last tab)
  • Expected: Tab active focus switches to the 3rd tab
  • Right-click / drag -> Detach the currently-active tab (the 3rd tab)
  • Expected: Tab active focus switches to the 1st tab
  • Right-click / drag -> Detach the currently-active tab (the 1st tab)
  • Expected: Tab active focus switches to the 2nd tab

For reference:

  • When a Tab is discarded, it is given a new Tab ID. It will use that new Tab ID when contents are then loaded in the future. The old Tab ID is not used anymore.
  • When a Tab is discarded, its blank replacement contents will cause app.on('web-contents-created') to be fired.
  • We will never receive a process.on('add-new-contents') event for the tab's new contents / tabId. So we rely on muon's new tab-replaced-at event which fires on the original tab.

@codecov-io
Copy link

codecov-io commented Feb 13, 2018

Codecov Report

Merging #13120 into master will decrease coverage by 0.19%.
The diff coverage is 16.8%.

@@            Coverage Diff            @@
##           master   #13120     +/-   ##
=========================================
- Coverage   56.14%   55.95%   -0.2%     
=========================================
  Files         282      282             
  Lines       28074    28190    +116     
  Branches     4615     4639     +24     
=========================================
+ Hits        15763    15774     +11     
- Misses      12311    12416    +105
Flag Coverage Δ
#unittest 55.95% <16.8%> (-0.2%) ⬇️
Impacted Files Coverage Δ
js/constants/appConstants.js 100% <ø> (ø) ⬆️
js/stores/windowStore.js 27.66% <0%> (-0.29%) ⬇️
js/actions/appActions.js 18.8% <0%> (-0.11%) ⬇️
app/browser/webContentsCache.js 54.76% <14.28%> (-20.24%) ⬇️
app/browser/activeTabHistory.js 77.77% <28.57%> (-12.55%) ⬇️
app/browser/tabs.js 23.91% <30.43%> (-0.22%) ⬇️
app/common/state/tabState.js 61.08% <6.06%> (-3.5%) ⬇️
app/browser/reducers/tabsReducer.js 40% <7.14%> (-1.2%) ⬇️

@alexwykoff alexwykoff added the priority/P1 Blocks development or testing. Product cannot run. Must be fixed immediately, shipped next release. label Feb 13, 2018
@NejcZdovc
Copy link
Contributor

#13118 was merged, so removing PR blocked label

@petemill
Copy link
Member Author

This is ready for review but not merge until brave/muon#504 is merged, released and referenced in whichever branch this PR should end up in (currently 0.21.x).

appActions.tabReplaced(tabId, getTabValue(newTabId), getTabValue(tabId).get('windowId'), !isPlaceholder)
if (!isPlaceholder) {
// update in-memory caches
webContentsCache.tabIdChanged(tabId, newTabId)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could introduce a race condition because the webContentsCache and activeTabHistory will be temporarily out-of-sync with the app state. Can we make them reducers instead that update on the tabReplaced action?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

activeTabHistory and webContentsCache[].openerTabId (openerTabId is the only prop changed here) are only called in response to tab events coming from muon, not app state actions. Is that enough to satisfy your concern @bridiver? My thinking was to change them immediately to actually avoid a race condition, in case we're getting the tab 'detached' / 'destroyed' event quicker than the store can update those values - and we need the values updated then so that the next active TabId can be correctly chosen. Or is your concern that the tabReplaced action may rely on that data?

Either way, I should test that detaching a tab correctly sets the appropriate parent tab on detach, because there may be an issue with the 'temporary' contents id, in which case even if isPlaceholder === true we should call webContentsCache.tabIdChanged and activeTabHistory.tabIdChanged since that is window-specific and the info is needed on tab.on('will-destroy') which is fired very soon after tab-id-changed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^ detaching is fine, and we shouldn't change the activehistory / openerTabId tabId for placeholder replacements since tabs.moveTo directly calls tabs.getNextActiveTabId passing in the moved tabId (and not the temporary replacement tabId) - so detaching and setting the appropriate next tab active in the departing window, depending on preference, works nicely.

@petemill
Copy link
Member Author

I added some more test cases

@petemill
Copy link
Member Author

petemill commented Feb 23, 2018

Looks like the 'Parent Tab 2' discard test isn't working, I'm looking in to it
Edit: fixed

Requires brave/muon#504
Ensure that tab/WebContents objects are added to memory as soon as they are created.
Use muon's new `tab-replaced-at` event (instead of <webview>.tab-id-changed) to ensure state gets updated correctly when a tab is discarded, as well as communicating temporary contents replacement to a window's frame (such as when detaching a tab to a new window, the tab in the old window gets a temporary WebContents that then gets destroyed).

In the case of tabs which get discarded, they are replaced with a fresh, clean WebContents. We were not receiving this new object, so when the renderer asked for this new object's tabId to be made active, the cache did not have that object.
We must also handle updating references to the old TabId to the new TabId in state, in the module which remembers opener tab IDs and the module which handles historical tab 'active' history for a window.

The guestInstanceId for a tab also changes when a discarded tab is reloaded, so make sure we pass that new value in the frame options when a discarded tab is detached to a new window.

Adds more relevant log entries under the `--debug-tab-events` flag.
Copy link
Member

@bsclifton bsclifton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ran through entire test plan - everything working great 😄 Love the extra debugging info too

@petemill
Copy link
Member Author

Thanks for the approval @bsclifton. Looking forward to getting it merged and out on beta release to test auto-discarding in the wild.

@bsclifton bsclifton merged commit f5db19e into master Feb 23, 2018
@bsclifton bsclifton deleted the fix/tabs-discard-dead-tab branch February 23, 2018 22:37
bsclifton added a commit that referenced this pull request Feb 23, 2018
@bsclifton
Copy link
Member

bsclifton commented Feb 23, 2018

master f5db19e
0.22.x 73e52ac
0.21.x 541f346

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
perf priority/P1 Blocks development or testing. Product cannot run. Must be fixed immediately, shipped next release.
Projects
None yet
6 participants