GIOStream *stream, GCredentials *credentials, gpointer data);
 static gboolean on_new_connection(GDBusServer *server,
         GDBusConnection *connection, gpointer data);
+static void on_connection_close(GDBusConnection *connection, gboolean
+        remote_peer_vanished, GError *error, gpointer data);
 static void on_proxy_created (GDBusProxy *proxy, GAsyncResult *result,
         gpointer data);
 static void on_vertical_scroll(GDBusConnection *connection,
         const char *sender_name, const char *object_path,
         const char *interface_name, const char *signal_name,
-        GVariant *parameters, Client *c);
+        GVariant *parameters, gpointer data);
 static void dbus_call(Client *c, const char *method, GVariant *param,
         GAsyncReadyCallback callback);
 static GVariant *dbus_call_sync(Client *c, const char *method, GVariant
     /* Create dbus proxy. */
     g_return_val_if_fail(G_IS_DBUS_CONNECTION(connection), FALSE);
 
-    /*g_signal_connect(connection, "closed", G_CALLBACK(connection_closed_cb), NULL);*/
+    g_signal_connect(connection, "closed", G_CALLBACK(on_connection_close), NULL);
 
     g_dbus_proxy_new(connection,
             G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES|G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
     return TRUE;
 }
 
+static void on_connection_close(GDBusConnection *connection, gboolean
+        remote_peer_vanished, GError *error, gpointer data)
+{
+    if (error && !remote_peer_vanished) {
+        g_warning("Unexpected lost connection to web extension: %s", error->message);
+    }
+}
+
 static void on_proxy_created(GDBusProxy *new_proxy, GAsyncResult *result,
         gpointer data)
 {
 static void on_vertical_scroll(GDBusConnection *connection,
         const char *sender_name, const char *object_path,
         const char *interface_name, const char *signal_name,
-        GVariant *parameters, Client *c)
+        GVariant *parameters, gpointer data)
 {
-    g_variant_get(parameters, "(tt)", &c->state.scroll_max, &c->state.scroll_percent);
+    glong max;
+    guint percent;
+    guint64 pageid;
+    Client *c;
+
+    g_variant_get(parameters, "(ttq)", &pageid, &max, &percent);
+    c = vb_get_client_for_page_id(pageid);
+    if (c) {
+        c->state.scroll_max     = max;
+        c->state.scroll_percent = percent;
+    }
 
     vb_statusbar_update(c);
 }
 void ext_proxy_eval_script(Client *c, char *js, GAsyncReadyCallback callback)
 {
        if (callback) {
-        dbus_call(c, "EvalJs", g_variant_new("(s)", js), callback);
+        dbus_call(c, "EvalJs", g_variant_new("(ts)", js, c->page_id), callback);
        } else {
-        dbus_call(c, "EvalJsNoResult", g_variant_new("(s)", js), NULL);
+        dbus_call(c, "EvalJsNoResult", g_variant_new("(ts)", c->page_id, js), NULL);
        }
 }
 
 GVariant *ext_proxy_eval_script_sync(Client *c, char *js)
 {
-    return dbus_call_sync(c, "EvalJs", g_variant_new("(s)", js));
+    return dbus_call_sync(c, "EvalJs", g_variant_new("(ts)", c->page_id, js));
 }
 
 /**
  */
 void ext_proxy_focus_input(Client *c)
 {
-    dbus_call(c, "FocusInput", NULL, NULL);
+    dbus_call(c, "FocusInput", g_variant_new("(t)", c->page_id), NULL);
 }
 
 /**
         const char *interface_name, const char *signal_name,
         GVariant *parameters, gpointer data)
 {
-    Client *p;
-    guint64 page_id;
+    Client *c;
+    guint64 pageid;
 
-    g_variant_get(parameters, "(t)", &page_id);
+    g_variant_get(parameters, "(t)", &pageid);
 
     /* Search for the client with the same page id as returned by the
      * webextension. */
-    for (p = vb.clients; p && p->page_id != page_id; p = p->next);
-
-    if (p) {
+    c = vb_get_client_for_page_id(pageid);
+    if (c) {
         /* Set the dbus proxy on the right client based on page id. */
-        p->dbusproxy = (GDBusProxy*)data;
+        c->dbusproxy = (GDBusProxy*)data;
 
         /* Subscribe to dbus signals here. */
         g_dbus_connection_signal_subscribe(connection, NULL,
                 VB_WEBEXTENSION_INTERFACE, "VerticalScroll",
                 VB_WEBEXTENSION_OBJECT_PATH, NULL, G_DBUS_SIGNAL_FLAGS_NONE,
-                (GDBusSignalCallback)on_vertical_scroll, p, NULL);
+                (GDBusSignalCallback)on_vertical_scroll, NULL, NULL);
     }
 }
 
 static void vimb_setup(void);
 static WebKitWebView *webview_new(Client *c, WebKitWebView *webview);
 static void on_script_message_focus(WebKitUserContentManager *manager,
-        WebKitJavascriptResult *message, Client *c);
+        WebKitJavascriptResult *res, gpointer data);
 static gboolean profileOptionArgFunc(const gchar *option_name,
         const gchar *value, gpointer data, GError **error);
 
     }
 }
 
+/**
+ * Returns the client for given page id.
+ */
+Client *vb_get_client_for_page_id(guint64 pageid)
+{
+    Client *c;
+    /* Search for the client with the same page id. */
+    for (c = vb.clients; c && c->page_id != pageid; c = c->next);
+
+    if (c) {
+        return c;
+    }
+    return NULL;
+}
+
 /**
  * Retrieves the content of the command line.
  * Returned string must be freed with g_free.
 
     /* Setup script message handlers. */
     webkit_user_content_manager_register_script_message_handler(ucm, "focus");
-    g_signal_connect(ucm, "script-message-received::focus", G_CALLBACK(on_script_message_focus), c);
+    g_signal_connect(ucm, "script-message-received::focus", G_CALLBACK(on_script_message_focus), NULL);
 
     return new;
 }
 
 static void on_script_message_focus(WebKitUserContentManager *manager,
-        WebKitJavascriptResult *message, Client *c)
+        WebKitJavascriptResult *res, gpointer data)
 {
+    char *message;
+    GVariant *variant;
+    guint64 pageid;
     gboolean is_focused;
+    Client *c;
+
+    message = util_js_result_as_string(res);
+    variant = g_variant_parse(G_VARIANT_TYPE("(tb)"), message, NULL, NULL, NULL);
+    g_free(message);
+
+    g_variant_get(variant, "(tb)", &pageid, &is_focused);
+    g_variant_unref(variant);
+
+    c = vb_get_client_for_page_id(pageid);
+    if (!c) {
+        return;
+    }
 
-    is_focused = (gboolean)util_js_result_as_number(message);
     /* Don't change the mode if we are in pass through mode. */
     if (c->mode->id == 'n' && is_focused) {
         vb_enter(c, 'i');
 
 static void on_dbus_connection_created(GObject *source_object,
         GAsyncResult *result, gpointer data);
 static void add_onload_event_observers(WebKitDOMDocument *doc,
-        WebKitWebExtension *extension);
+        WebKitWebPage *page);
 static void on_document_scroll(WebKitDOMEventTarget *target, WebKitDOMEvent *event,
-        gpointer data);
+        WebKitWebPage *page);
 static void emit_page_created(GDBusConnection *connection, guint64 pageid);
 static void emit_page_created_pending(GDBusConnection *connection);
 static void queue_page_created_signal(guint64 pageid);
 static void dbus_emit_signal(const char *name, GVariant *data);
+static WebKitWebPage *get_web_page_or_return_dbus_error(GDBusMethodInvocation *invocation,
+        WebKitWebExtension *extension, guint64 pageid);
 static void dbus_handle_method_call(GDBusConnection *conn, const char *sender,
         const char *object_path, const char *interface_name, const char *method,
         GVariant *parameters, GDBusMethodInvocation *invocation, gpointer data);
 static void on_editable_change_focus(WebKitDOMEventTarget *target,
-        WebKitDOMEvent *event, WebKitWebExtension *extension);
+        WebKitDOMEvent *event, WebKitWebPage *page);
 static void on_page_created(WebKitWebExtension *ext, WebKitWebPage *webpage, gpointer data);
 static void on_web_page_document_loaded(WebKitWebPage *webpage, gpointer extension);
 static gboolean on_web_page_send_request(WebKitWebPage *webpage, WebKitURIRequest *request,
     "<node>"
     " <interface name='" VB_WEBEXTENSION_INTERFACE "'>"
     "  <method name='EvalJs'>"
+    "   <arg type='t' name='page_id' direction='in'/>"
     "   <arg type='s' name='js' direction='in'/>"
     "   <arg type='b' name='success' direction='out'/>"
     "   <arg type='s' name='result' direction='out'/>"
     "  </method>"
     "  <method name='EvalJsNoResult'>"
+    "   <arg type='t' name='page_id' direction='in'/>"
     "   <arg type='s' name='js' direction='in'/>"
     "  </method>"
     "  <method name='FocusInput'>"
+    "   <arg type='t' name='page_id' direction='in'/>"
     "  </method>"
     "  <signal name='PageCreated'>"
     "   <arg type='t' name='page_id' direction='out'/>"
     "  </signal>"
     "  <signal name='VerticalScroll'>"
+    "   <arg type='t' name='page_id' direction='out'/>"
     "   <arg type='t' name='max' direction='out'/>"
-    "   <arg type='t' name='percent' direction='out'/>"
+    "   <arg type='q' name='percent' direction='out'/>"
     "  </signal>"
     "  <method name='SetHeaderSetting'>"
     "   <arg type='s' name='headers' direction='in'/>"
 struct Ext {
     guint               regid;
     GDBusConnection     *connection;
-    WebKitWebPage       *webpage;
-    WebKitDOMElement    *active;
     GHashTable          *headers;
     GHashTable          *documents;
-    gboolean            input_focus;
     GArray              *page_created_signals;
 };
 struct Ext ext = {0};
  * too.
  */
 static void add_onload_event_observers(WebKitDOMDocument *doc,
-        WebKitWebExtension *extension)
+        WebKitWebPage *page)
 {
     WebKitDOMEventTarget *target;
 
     target = WEBKIT_DOM_EVENT_TARGET(webkit_dom_document_get_default_view(doc));
 
     webkit_dom_event_target_add_event_listener(target, "focus",
-            G_CALLBACK(on_editable_change_focus), TRUE, extension);
+            G_CALLBACK(on_editable_change_focus), TRUE, page);
     webkit_dom_event_target_add_event_listener(target, "blur",
-            G_CALLBACK(on_editable_change_focus), TRUE, extension);
+            G_CALLBACK(on_editable_change_focus), TRUE, page);
     /* Check for focused editable elements also if they where focused before
      * the event observer where set up. */
     /* TODO this is not needed for strict-focus=on */
-    on_editable_change_focus(target, NULL, extension);
+    on_editable_change_focus(target, NULL, page);
 
     /* Observe scroll events to get current position in the document. */
     webkit_dom_event_target_add_event_listener(target, "scroll",
-            G_CALLBACK(on_document_scroll), false, NULL);
+            G_CALLBACK(on_document_scroll), false, page);
     /* Call the callback explicitly to make sure we have the right position
      * shown in statusbar also in cases the user does not scroll. */
-    on_document_scroll(target, NULL, NULL);
+    on_document_scroll(target, NULL, page);
 }
 
 /**
  * Callback called when the document is scrolled.
  */
 static void on_document_scroll(WebKitDOMEventTarget *target, WebKitDOMEvent *event,
-        gpointer data)
+        WebKitWebPage *page)
 {
     WebKitDOMDocument *doc;
 
         if (scrollTop && max) {
             percent = (guint)(0.5 + (scrollTop * 100 / max));
         }
-        dbus_emit_signal("VerticalScroll", g_variant_new("(tt)", max, percent));
+        dbus_emit_signal("VerticalScroll", g_variant_new("(ttq)",
+                    webkit_web_page_get_id(page), max, percent));
     }
 }
 
     }
 }
 
+static WebKitWebPage *get_web_page_or_return_dbus_error(GDBusMethodInvocation *invocation,
+        WebKitWebExtension *extension, guint64 pageid)
+{
+    WebKitWebPage *page = webkit_web_extension_get_page(extension, pageid);
+    if (!page) {
+        g_warning("invalid page id %lu", pageid);
+        g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
+                G_DBUS_ERROR_INVALID_ARGS, "Invalid page ID: %"G_GUINT64_FORMAT, pageid);
+    }
+
+    return page;
+}
+
 /**
  * Handle dbus method calls.
  */
         GVariant *parameters, GDBusMethodInvocation *invocation, gpointer extension)
 {
     char *value;
+    guint64 pageid;
+    WebKitWebPage *page;
 
     if (g_str_has_prefix(method, "EvalJs")) {
         char *result       = NULL;
         gboolean success;
-        gboolean no_result = !g_strcmp0(method, "EvalJsNoResult");
-               JSValueRef ref     = NULL;
+        gboolean no_result;
+        JSValueRef ref     = NULL;
         JSGlobalContextRef jsContext;
 
-        g_variant_get(parameters, "(s)", &value);
+        g_variant_get(parameters, "(ts)", &pageid, &value);
+        page = get_web_page_or_return_dbus_error(invocation, WEBKIT_WEB_EXTENSION(extension), pageid);
+        if (!page) {
+            return;
+        }
 
+        no_result = !g_strcmp0(method, "EvalJsNoResult");
         jsContext = webkit_frame_get_javascript_context_for_script_world(
-                       webkit_web_page_get_main_frame(ext.webpage),
-                       webkit_script_world_get_default()
-               );
+            webkit_web_page_get_main_frame(page),
+            webkit_script_world_get_default()
+        );
 
-               success = ext_util_js_eval(jsContext, value, &ref);
+        success = ext_util_js_eval(jsContext, value, &ref);
 
         if (no_result) {
             g_dbus_method_invocation_return_value(invocation, NULL);
             g_free(result);
         }
     } else if (!g_strcmp0(method, "FocusInput")) {
-        ext_dom_focus_input(webkit_web_page_get_dom_document(ext.webpage));
+        g_variant_get(parameters, "(t)", &pageid);
+        page = get_web_page_or_return_dbus_error(invocation, WEBKIT_WEB_EXTENSION(extension), pageid);
+        if (!page) {
+            return;
+        }
+        ext_dom_focus_input(webkit_web_page_get_dom_document(page));
         g_dbus_method_invocation_return_value(invocation, NULL);
     } else if (!g_strcmp0(method, "SetHeaderSetting")) {
         g_variant_get(parameters, "(s)", &value);
  * WebKitDOMDOMWindow.
  */
 static void on_editable_change_focus(WebKitDOMEventTarget *target,
-        WebKitDOMEvent *event, WebKitWebExtension *extension)
+        WebKitDOMEvent *event, WebKitWebPage *page)
 {
-    gboolean input_focus;
     WebKitDOMDocument *doc;
     WebKitDOMDOMWindow *dom_window;
     WebKitDOMElement *active;
+    GVariant *variant;
+    char *message;
 
     if (WEBKIT_DOM_IS_DOM_WINDOW(target)) {
         g_object_get(target, "document", &doc, NULL);
     }
 
     active = webkit_dom_document_get_active_element(doc);
-    /* Don't do anything if there is no active element or the active element
-     * is the same as before. */
-    if (!active || active == ext.active) {
+    /* Don't do anything if there is no active element */
+    if (!active) {
         return;
     }
     if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT(active)) {
 
         iframe = WEBKIT_DOM_HTML_IFRAME_ELEMENT(active);
         subdoc = webkit_dom_html_iframe_element_get_content_document(iframe);
-        add_onload_event_observers(subdoc, extension);
+        add_onload_event_observers(subdoc, page);
         return;
     }
 
-    ext.active = active;
-
     /* Check if the active element is an editable element. */
-    input_focus = ext_dom_is_editable(active);
-    if (input_focus != ext.input_focus) {
-        ext.input_focus = input_focus;
-
-        webkit_dom_document_get_default_view(doc);
-        if (!webkit_dom_dom_window_webkit_message_handlers_post_message(dom_window, "focus", input_focus ? "1" : "0")) {
-            g_warning("Error sending focus message");
-            return;
-        }
+    variant = g_variant_new("(tb)", webkit_web_page_get_id(page),
+            ext_dom_is_editable(active));
+    message = g_variant_print(variant, FALSE);
+    g_variant_unref(variant);
+    if (!webkit_dom_dom_window_webkit_message_handlers_post_message(dom_window, "focus", message)) {
+        g_warning("Error sending focus message");
     }
+    g_free(message);
     g_object_unref(dom_window);
 }
 
 {
     guint64 pageid = webkit_web_page_get_id(webpage);
 
-    /* Save the new created page in the extension data for later use. */
-    ext.webpage = webpage;
     if (ext.connection) {
         emit_page_created(ext.connection, pageid);
     } else {
     }
     ext.documents = g_hash_table_new(g_direct_hash, g_direct_equal);
 
-    add_onload_event_observers(webkit_web_page_get_dom_document(webpage),
-            WEBKIT_WEB_EXTENSION(extension));
+    add_onload_event_observers(webkit_web_page_get_dom_document(webpage), webpage);
 }
 
 /**