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

Reply via email to