Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: a91d8f8c973025308054dfcc9138d1cfe7b31884
https://github.com/WebKit/WebKit/commit/a91d8f8c973025308054dfcc9138d1cfe7b31884
Author: Rupin Mittal <[email protected]>
Date: 2026-04-27 (Mon, 27 Apr 2026)
Changed paths:
A LayoutTests/http/tests/site-isolation/resources/text-input.html
A
LayoutTests/http/tests/site-isolation/type-in-cross-origin-iframe-expected.txt
A LayoutTests/http/tests/site-isolation/type-in-cross-origin-iframe.html
M Source/WebKit/UIProcess/API/C/WKPage.cpp
M Source/WebKit/UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.mm
M Source/WebKit/UIProcess/RemotePageProxy.h
M Source/WebKit/UIProcess/WebPageProxy.cpp
M Source/WebKit/UIProcess/WebPageProxy.h
M Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm
M Source/WebKit/UIProcess/mac/WebViewImpl.h
Log Message:
-----------
[Site Isolation] Make typing work on iOS
https://bugs.webkit.org/show_bug.cgi?id=312858
rdar://162073012
Reviewed by Charlie Wolfe.
Turns out, one of the things people like to do on their devices is type. Who
knew?
Unfortunately, on an iOS device with site isolation on, you can't type into a
cross-site iframe. When you click the input field, the keyboard does show up,
but
clicking the keys does not result in them being typed. This is a major livablity
blocker.
The way text gets entered from the keyboard is that UIKit tells WebKit to insert
text by calling the insertText API (in WKContentViewInteraction.mm). With site
isolation on, this is never called.
When deciding to insertText in the contentView, UIKit first checks if the
content
is editable (because why try to insert text if the content isn't editable in the
first place). It does this by asking the WebPageProxy if its content is
editable:
[context setDocumentEditable:protect(_page)->editorState().isContentEditable];
This flag is stored on EditorState. This EditorState is created by the WebPage
and
sent to the UIProcess every time the editing state for that web process changes.
This can happen via three different IPC messages:
1. WebPageProxy::editorStateChanged()
2. WebPageProxy::interpretKeyEvent()
3. RemoteLayerTreeDrawingAreaProxy::commitLayerTree()
All three of these will call WebPageProxy::updateEditorState() where
WebPageProxy
will possibly store the new EditorState.
The EditorState has a monotonically increasing identifier. Whenever
WebPageProxy::updateEditorState() receives a new EditorState, it only stores and
uses it if this identifier is greater than the one of the current EditorState it
holds. Otherwise this new EditorState is ignored.
This is the root of our problem.
Before site isolation, each contentView is associated with a single web process.
And each WebPage maps to a single WebPageProxy. All frames on that WebPage share
the same EditorState. Anytime one of them makes a change to it, it is sent to
the
WebPageProxy, stored there, and its data is used to let UIKit make decisions
(like
whether or not to insert text). Let's consider our case of typing in a iframe:
Site Isolation OFF:
1. Main frame's EditorState goes through a series of changes as the user uses
the
page. For each change, the web process sends to the UIProcess a new
EditorState
with an incremented identifier.
Let's say after this, the EditorState in WebPageProxy has identifier = 10 and
since the users's actions didn't result in the content being editable, the
isContentEditable flag is false.
2. The iframe's input element gets tapped on. The iframe gets focused and
selection
changes. This changes the EditorState, so we make a new one (identifier =
11).
Since the input element is capable of taking in text, isContentEditable is
true.
This EditorState is sent to the UIProcess. Since the identifier is greater
than
the current one, this EditorState replaces the old one.
3. User presses a key on the keyboard. UIKit asks WebPageProxy if the content is
editable. WebPageProxy has the new EditorState with isContentEditable = true.
So UIKit calls insertText and the character gets typed.
Site Isolation ON:
1. Same as above. The main frame's EditorState goes through a series of changes
and
WebPageProxy ends up with an EditorState with identifier = 10 and
isContentEditable = false.
2. The iframe's input element gets tapped on. The iframe gets focused and
selection
changes. This changes the EditorState, so we make a new one.
But this iframe is in a different web process than the main frame. It has
not sent
an EditorState before. So its identifier will begin at 1. The EditorState it
sends
has identifier = 1 and isContentEditable = true.
This EditorState is sent to the UIProcess. WebPageProxy has no idea that the
new
EditorState it just got is from a different process than the EditorState it
holds
now. All it sees is that the new EditorState's identifer is less than the
one it
holds currently. So this EditorState is ignored.
3. User presses a key on the keyboard. UIKit asks WebPageProxy if the content is
editable. WebPageProxy still holds the EditorState from the main frame with
isContentEditable = false.
So UIKit DOES NOT call insertText and the character is not typed.
We need to teach WebPageProxy to deal with the fact that with site isolation on,
it may receive EditorStates from different processes.
With site isolation on, a WebPage doesn't map directly to a WebPageProxy if its
in a web process where the main frame is not local. Rather, it maps to a
RemotePageProxy.
So now, we make RemotePageProxy store the EditorState for its associated
WebPage.
WebPageProxy::updateEditorState() will decide whether to store the new
EditorState
on either the WebPageProxy itself (if it came from the web process where the
main
frame is local) or on the correct RemotePageProxy. To do this, we add a field to
EditorState which indicates which web process generated it.
To ensure that we don't store old EditorStates, we will still only replace the
currently held EditorState for a web process if the new one has a greater
identifier.
There are two problems we must address here:
1. UIKit can still only query one EditorState at a time (it doesn't know about
site
isolation in WebKit). Given that WebPageProxy (and the RemotePageProxys) for
a single WebView collectively now each have an EditorStates, WebPageProxy
must
decide which to use when UIKit asks it questions.
WebPageProxy will use the EditorState for whichever web process currently
contains
the focused frame. If no frame is focused, it will use the main frame's
process.
2. When WebPageProxy::editorStateChanged() gets a new EditorState, if it
decides to
use this in favor of the old one it holds, it calls didUpdateEditorState(),
which
makes changes based on the new EditorState.
But we may receive new EditorStates from a process that doesn't currently
hold
the focused frame that have a greater identifier than the current EditorState
from that web process. In this case, we don't want to call
didUpdateEditorState().
Similarly to the first issue, we fix this by only calling
didUpdateEditorState
if the newEditorState is from the web process that currently holds the
focused
frame.
After all of this re-architecting, UIKit will now call insertText. But typing
still
does not work. There's one more issue to fix.
insertText calls WebPageProxy::insertTextAsync(). But this function will only
send the
text to the main frame's web process. To ensure the cross-site iframe gets it,
we amend
insertTextAsync() to send the text to the web process that contains the
currently
focused frame.
Now the flow is:
Site Isolation ON:
1. The main frame's EditorState goes through a series of changes and
WebPageProxy ends
up with an EditorState with identifier = 10 and isContentEditable = false.
This
is keyed by the main frame's web process.
2. The iframe's input element gets tapped on. The iframe gets focused and
selection
changes. This changes the EditorState, so we make a new one. It sends an
EditorState
with identifier = 1 and isContentEditable = true.
WebPageProxy sees that this is for a different web process. So it stores this
EditorState on the RemotePageProxy that is associated with this WebPage.
And, since the iframe is the currently focused frame, we call
didUpdateEditorState()
with the EditorState it sent.
3. User presses a key on the keyboard. UIKit asks WebPageProxy if the content is
editable. WebPageProxy uses the EditorState from the currently focused
iframe's
web process and returns isContentEditable = true.
So UIKit DOES call insertText and WebPageProxy::insertTextAsync() ensures
that
the character is sent to the iframe's web process and is typed.
This is tested by a new layout test: type-in-cross-origin-iframe.html.
* LayoutTests/http/tests/site-isolation/resources/text-input.html: Added.
*
LayoutTests/http/tests/site-isolation/type-in-cross-origin-iframe-expected.txt:
Added.
* LayoutTests/http/tests/site-isolation/type-in-cross-origin-iframe.html: Added.
* Source/WebKit/UIProcess/API/C/WKPage.cpp:
(WKPageCanDelete):
(WKPageHasSelectedRange):
(WKPageIsContentEditable):
* Source/WebKit/UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.mm:
(WebKit::RemoteLayerTreeDrawingAreaProxy::commitLayerTree):
* Source/WebKit/UIProcess/RemotePageProxy.h:
Store the EditorState from the associated WebPage.
(WebKit::RemotePageProxy::editorState):
* Source/WebKit/UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::editorState const):
When asked about the EditorState, WebPageProxy should use the one from the web
process that contains the currently focused frame (or the main frame if no frame
is focused).
(WebKit::WebPageProxy::hasSelectedRange const):
(WebKit::WebPageProxy::isContentEditable const):
(WebKit::WebPageProxy::editorStateChanged):
(WebKit::WebPageProxy::updateEditorState):
If the new editor state comes from the main frame's web process, update the
editor state stored by the WebPageProxy. Otherwise, update the editor state
stored by the RemotePageProxy that is associated with the web page that this
editor state comes from.
(WebKit::WebPageProxy::insertTextAsync):
Send the text to the currently focused frame's web process.
* Source/WebKit/UIProcess/WebPageProxy.h:
* Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm:
(WebKit::WebPageProxy::interpretKeyEvent):
* Source/WebKit/UIProcess/mac/WebViewImpl.h:
Canonical link: https://commits.webkit.org/312115@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications