1

In the mobile version of my web site I have a JavaScript confirm dialog that appears under specific circumstances. Using setTimeout I trigger the confirm dialog. No matter which tab the user is in, he should see the confirm dialog but in iOS 10 loses focus.

In iOS version 8 & 9 works fine when I have two tabs and I am in the 2nd tab, the confirm dialog shows up in front like it should.

Is there any solution or workaround for that?

var cf = confirm("Close?"); 
if (cf){ do that....} else { do this... }
Panos Kalatzantonakis
  • 12,525
  • 8
  • 64
  • 85
AlexCode
  • 655
  • 5
  • 18

1 Answers1

2

SafariDriver is implemented in JS so in order to intercept calls to alert, confirm, and prompt, one must override the functions in the context of the web page.

Change the injected script to be injected as a Start script instead of End script - which means the script is injected once the DOM has been loaded, but before it has been parsed (as opposed to being injected after the onload event):

http://developer.apple.com/library/safari/#documentation/Tools/Conceptual/SafariExtensionGuide/InjectingScripts/InjectingScripts.html#//apple_ref/doc/uid/TP40009977-CH6-SW5

Override the global alert functions in the context of the page under test, not the injected script. This is similar to the requirements of the executeScript command. Therefore, the first thing our injected script should do is add a script tag to DOM that sets up the alert overrides. This script tag should be added as the first child of the documentElement to ensure it is executed before any others in the page. This will ensure we set-up our alert handlers before anything in the page has a chance to fire an alert.

Once an alert fires, we must notify the extension that there was an alert, while simultaneously blocking the current JS thread in the page. Normally, our page scripts communicate with the injected script using window.postMessage. postMessage fires a MessageEvent asynchronously. To maintain synchronicity, we can manually fire a MessageEvent:

Use a MessageEvent instead of some other DOM event so we can include a JSON object describing the alert.

var event = document.createEvent('MessageEvent');
event.initMessageEvent('message', false, false, {
  type: "alert",  // confirm, or prompt
  text: "hello"
}, window.location.origin, '0', window, null);
window.dispatchEvent(event);

The injected script must listen for an respond to the page's alert message. To synchronously send the alert to the extension for processing, we can (ab)use the Safari extension's mechanism for blocking content from loading:

http://developer.apple.com/library/safari/#documentation/Tools/Conceptual/SafariExtensionGuide/MessagesandProxies/MessagesandProxies.html#//apple_ref/doc/uid/TP40009977-CH14-SW9

window.addEventListener('message', function(e) {
  // Create a beforeload event, which is required by the canLoad method
  var e = document.createEvent('Events');
  e.initEvent('beforeload', false, false);

  // canLoad sends and waits for a response synchronously. It is the only
  // synchronous function in the Safari extension messaging API.
  var response = safari.self.tab.canLoad(e, e.data);

  // Send the response back to the page using another MessageEvent.
  var responseEvent = document.createEvent('MessageEvent');
  responseEvent.initMessageEvent('message', false, false, {
    accepted: response.accepted,
    response: response.value
  }, window.location.origin, '0', window, null);
  window.dispatchEvent(responseEvent);
}, true);

Note, the extension's alert response must be communicated back to the page using another message since we are crossing context boundaries. The only other option is to store the response on the DOM to be read on the other side.

The final step, and this is the open-ended question, is how the extension should handle the alert. Since we're maintaining the blocking behavior of alerts, it's not possible to let anymore commands execute (even if they result in unhandled alert errors).

One possibility is to have the WebDriver client participate in the alert handling. In addition to providing a WebSocket server, the WebDriver client will be expected to also provide an XHR end-point. When an alert is detected, the server will send a synchronous POST XHR to this end-point. The client should respond only once the user has accepted or dismissed the alert (or an unhandled alert error was thrown from another command). When the XHR response is received, the extension completes the chain and sends the response back to the injected script.

You can find more here.

Panos Kalatzantonakis
  • 12,525
  • 8
  • 64
  • 85