Skip to content

Add proposal for modifying split views#1019

Open
Rob--W wants to merge 2 commits into
mainfrom
split-view-mods
Open

Add proposal for modifying split views#1019
Rob--W wants to merge 2 commits into
mainfrom
split-view-mods

Conversation

@Rob--W

@Rob--W Rob--W commented Jun 4, 2026

Copy link
Copy Markdown
Member

Comment thread proposals/split_view_modification.md Outdated
Co-authored-by: Dave Vandyke <kzar@kzar.co.uk>

#### tabs.createSplitView()

Requires a pair of tabIds referring to distinct tabs, rejects otherwise.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

When I « Add Split View » in my browser (firefox or chromium), I have only only tab. The other one is an empty tab (about:opentabs in firefox). Is it possible to have the same behaviour if I pass only one tabId?

And what appends if I pass 3 or more tabs?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

When I « Add Split View » in my browser (firefox or chromium), I have only only tab. The other one is an empty tab (about:opentabs in firefox). Is it possible to have the same behaviour if I pass only one tabId?

With the existing tabs.create API with the new splitWithTabId option, described above. Reason for relying on that method is to avoid duplicating the "create new tab" functionality.

And what appends if I pass 3 or more tabs?

Error, because we cannot create a split view with more than 2 tabs right now.

@carlosjeurissen

Copy link
Copy Markdown
Member

@Rob--W considering your concern about future-proofing the API, would it make sense to allow splitViewId in tabs.update() to attach/detach a specific tab to/from a splitview? For which we then have to determine what should happen to the splitview. It could either resort to the split-view initial state which offers users to do something with the "split", or simply dissolve the splitview, in case it could be a replacement for tabs.separateSplitView().

@Rob--W

Rob--W commented Jun 9, 2026

Copy link
Copy Markdown
Member Author

@Rob--W considering your concern about future-proofing the API, would it make sense to allow splitViewId in tabs.update() to attach/detach a specific tab to/from a splitview? For which we then have to determine what should happen to the splitview. It could either resort to the split-view initial state which offers users to do something with the "split", or simply dissolve the splitview, in case it could be a replacement for tabs.separateSplitView().

With the number of tabs in a split view being limited to 2, adding a tab to a split view automatically implies kicking another tab out of it. The mechanisms by which that should happen are not obvious (should tabs move around? Which one should be kicked out? etc). It may be possible to define something meaningful for limited scenarios, e.g. tabs already adjacent to each other. But then we'd basically duplicate the functionality of createSplitView - and I am trying to not duplicate functionality unless necessary. We can consider adding this later if we'd like, but for now I'm trying to keep the proposal minimal to only the parts that are not already available through existing extension APIs.

@Rob--W Rob--W requested a review from rdcronin June 12, 2026 12:29

@rdcronin rdcronin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks, Rob! Excited to see this!


- Creating a split view from an existing tab, which creates a new split view
consisting of the specified tab, plus a browser-native UI page where the user
can choose the tab to adopt in that split.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

the browser-native UI page here is unique to Firefox, I think (or at least, we don't have this in Chrome). Is this something we should include here?

@Rob--W Rob--W Jun 13, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

By "browser-native UI page" I meant a page potentially different from the default new tab.

In Firefox it is "about:opentabs", in Chrome is is chrome://new-tab-page/ (internally "chrome://tab-search.top-chrome/split_new_tab_page.html", opened at
https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/browser_commands.cc;l=1608-1612;drc=c9ae8a85e83fc4c4cb5531945ade924b940a816f ). If you want to see for yourself, open the context menu on a tab and use the "Add tab to new split view." option.


- Separating split views.

- Reversing tabs in a split view.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
- Reversing tabs in a split view.
- Reversing the order of tabs in a split view.

```javascript
browser.tabs.create({
// ... existing properties, including: url, index, active
splitWithTabId: number, // Existing tab to pair with

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

bikeshed: We could also call this "splitWith", since tabId is more of the type rather than the name of the value. And its shorter. But splitWithTabId does align more with openerTabId. Thoughts? (I don't feel strongly.)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

If the type is the same I prefer splitWithTabId, as it is also consistent with openerTabId like you said.

I initially considered (and actually started with) an object type, e.g. splitWith: { tabId }, but ultimately flattened that to minimize complexity in tabs.create.

E.g. one of the options I considered for the splitWith option was the relative placement, but I dropped that in favor of reusing the existing index property.

splitWithTabId: number, // Existing tab to pair with
}) : Promise<Tab> // Returns new tab in split with given tab

browser.tabs.createSplitView(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

createSplitView() is so long.

My instinct here is to actually just go with chrome.tabs.split(). This has good consistency with e.g. chrome.tabs.group(). That said, I know that it might also sound confusing (like you're "splitting" a single tab in half or something.)

Could also be createSplit()?

Again, I don't feel super strongly here, but it does kinda stand out as a long / clunky name compared to some others.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

createSplitView() is so long.

My instinct here is to actually just go with chrome.tabs.split(). This has good consistency with e.g. chrome.tabs.group(). That said, I know that it might also sound confusing (like you're "splitting" a single tab in half or something.)

At the design phase my mind went through all that you wrote here :)
I started with split() and unsplit(), but decided against "split" because it sounds like the tab would be split in pieces.

Could also be createSplit()?

I'm not fully opposed to it, but given that the ID is called splitViewId I prefer to be consistent and use "splitView" as the word, so "createSplitView".

For tabs.create, I nevertheless went with "splitWithTabId" because "splitViewWithTabId" was a mouthful and could too easily be confused with "splitViewId".

tabIds: Array<number> // Two tabIds.
) : Promise<number> // Returns splitViewId

browser.tabs.separateSplitView(splitViewId) : Promise<void>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

and chrome.tabs.unsplit() : )

(or "removeSplit()". But I kinda like just "split()" and "unsplit()")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I like unsplit, which was my first choice, but dropped it because "split()" was not a good option in my opinion.

I don't like "removeSplit" because it is ambiguous and can be mistaken for removing the split view and all of its tabs.

I went with "separate" because that is unambiguous and already the verb that Chrome and Firefox use in split view context menus.

First, validate the options and ensure that it would create a tab next to the
tab specified by `splitWithTabId`. The method SHOULD reject if it cannot create
a split view (see createSplitView). If a tab was created, the created tab must
be returned as usual, even if the split view cannot be created.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is the expectation then that the developer check splitViewId to see if the split view was actually created?

This sounds like something that would be very easy to get wrong. Do we need to support this case? Or should we instead say that it's a bug if the browser create a tab that was supposed to be split and then didn't split it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Is the expectation then that the developer check splitViewId to see if the split view was actually created?

The expectation is that we reject with an error if a split view cannot be created, expressed as "SHOULD". Since "SHOULD" is not a "MUST", I decided to also specify what the (reasonable) behavior is. tabs.create's primary purpose is to create a tab, and if it creates a tab it should return it, to prevent "leaking" tabs that the extension does not know about. Naturally, if the split view failed to create, then its splitViewId should be -1.

This sounds like something that would be very easy to get wrong. Do we need to support this case? Or should we instead say that it's a bug if the browser create a tab that was supposed to be split and then didn't split it?

I am willing to turn the SHOULD into a MUST, in which case the situation does not occur. This imposes higher requirements om the browser implementers, which I'd be fine with, if you are too.


### Behavior

#### tabs.create() with splitWithTabId option

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

kind of obvious, but we should also say that it has to be in the window with the tab it's being split with.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

That is already covered by:

if it cannot create
a split view (see createSplitView).

And in createSplitView one of the failure conditions is:

mismatching "windowId" states

`tabs.onUpdated` events.

When tabs are not adjacent, or even in order, the implementation MAY try to
move the tabs to the given order and join them.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

it seems like this is something we should specify more than "MAY". Should we move, or should we not?

My $0.02: I don't think we should reorder / move them.

  • We don't allow tabs that aren't in the same window (or other conditions, as above), so to create a new split, an extension may already need to do some tab management.
  • It seems a bit arbitrary to assume the extension wants to do this move, but not make the same assumption that it would want to e.g. change the pin state, etc. And making all those assumptions is a lot of assumptions.
  • Bundling these operations increases the likelihood that the function "half works" -- for instance, it might move the tabs, but not create the split view, violating the "don't change the state if it didn't succeed" bit below.
  • In theory, browsers may not require splits to be next to each other in the future (why should they need to be?). Preemptively moving these means that if we changed that in the future, it causes significant behavior changes for extensions.
  • We could potentially change this behavior in the future to allow the case of moving tabs for the split. We can't go the other direction and stop allowing it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm onboard with rejecting if they are not adjacent. We can always relax later, which I could call out in the future work section.

request of moving a split view.

If the `index` behavior of `tabs.move` weighs more strongly, then the move of
tabs MAY separate tabs automatically.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

MAY here also seems underspecified.

What do each of the browsers do today? If we already have consistent behavior, is it worth specifying? (If we don't, then MAY is fine)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Chrome separates the split, and I don't know if there were much thoughts behind this behavior.

Firefox tightly keeps tabs in a split view together, treating then as one unit unless there are clear indicators that they should not be together. The unity of split views was a Product requirement, and followed from the experience with tab groups, where users were surprised that the tab groups they created were ungrouped. Turns out that there was an extension that was unaware of tab groups and as part of reshuffling, the tabs were taken out of a tab group.

I documented the options (Chrome vs Firefox) along with the decision and expected behaviors for tabs.move at https://bugzilla.mozilla.org/show_bug.cgi?id=2016751

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@rdcronin Is Chrome willing to adopt Firefox's behavior, where tabs in a split view move together by default?

A Chrome extension developer expressed their preference for Firefox's behavior (as part of a larger comment describing their use case, at #967 (comment))

From a consumer's perspective, the most valuable guarantee would be that the "move as a unit" behavior is at least available and discoverable (vs. Chrome's current always-unsplit), so extensions don't have to detect the unsplit after the fact and stitch the split back together.

@Tai-ch0802

Copy link
Copy Markdown

+1 on this proposal — thanks for putting it together, @Rob--W.

I left a detailed real-world use case over at #967 (comment): a Chromium side-panel tab manager that currently has to block drag-reordering of individual split-view tabs because chrome.tabs.move() silently resets splitViewId to SPLIT_VIEW_ID_NONE. Both tabs.createSplitView() and the tabs.move "move as one unit" behavior described here would unblock us.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants