Skip to content

FB22370432 - Safari extension message from an iframe somehow allows the iframe to give itself keyboard focus #74

@lapcat

Description

@lapcat

Many web pages such as https://www.expedia.com and https://www.yelp.com show a "Sign in with Google" popup, which is an iframe with an accounts.google.com src URL. The iframe's JavaScript calls d.focus(), where the variable d is the "credentials-picker-container" div inside the iframe document. Normally this JavaScript doesn't accomplish anything; my testing suggests that a user gesture is normally required to change the focus successfully. Thus, when the Expedia or Yelp website finishes loading, the keyboard focus normally remains on the body of the top frame. However, for some strange reason, a Safari extension message sent from the iframe can cause the d.focus() call to succeed, thereby making the iframe the activeElement of the top frame document.

Worse, the iframe can become the activeElement of the top frame document even when its parent div has the CSS property display none! My own Safari extension has a feature that hides "Sign in with Google" popups by using CSS to set display: none on the element. Thus, the keyboard focus ends up in an element that is not displayed on the web page, which is obviously bad for the user.

Steps to reproduce:

  1. Build, run, and enabled the attached sample Safari extension Xcode project ExpediaTest
  2. Open a new private window in Safari (which ensures that no previous site data prevents the Sign in with Google popup from appearing)
  3. Open the web inspector console
  4. Open https://www.expedia.com
  5. Wait a few seconds

Expected results: The top frame document activeElement does not change from the body.

Actual results: The top frame document activeElement changes from the body to the iframe element. You can see this in the ExpediaTest log messages.

Discussion:
In the web inspector, you can create a symbolic breakpoint for "focus" to see exactly when the iframe calls "d.focus()". Add a breakpoint action to log a message, and have it automatically continue execution so that the breakpoint itself doesn't change the document focus.

The bug occurs with window.location.hostname === "accounts.google.com" in the extension content script. If you change === to !== then the bug no longer occurs. Only a Safari extension message from the iframe causes the bug.

The bug is caused by a trivial call to safari.extension.dispatchMessage(). In the Safari extension handler, the implementation is also trivial:

- (void)messageReceivedWithName:(NSString *)messageName fromPage:(SFSafariPage *)page userInfo:(NSDictionary *)userInfo {
	[page dispatchMessageToScriptWithName:messageName userInfo:@{}];
}

I can reproduce this bug in Safari 26.4 on macOS Tahoe and Sequoia. I can't reproduce the bug in Safari 18.6 on macOS Ventura.

ExpediaTest.zip

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions