Add SPI to use networking daemon instead of XPC service
authorachristensen@apple.com <achristensen@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 8 Feb 2019 17:18:08 +0000 (17:18 +0000)
committerachristensen@apple.com <achristensen@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 8 Feb 2019 17:18:08 +0000 (17:18 +0000)
https://bugs.webkit.org/show_bug.cgi?id=194427

Reviewed by Geoffrey Garen.

Source/WebKit:

There is still work to be done, but with the proper plist it starts and loads webpages!

* NetworkProcess/EntryPoint/Cocoa/Daemon/DaemonEntryPoint.mm:
(WebKit::DaemonMain):
* Shared/EntryPointUtilities/Cocoa/XPCService/XPCServiceEntryPoint.h:
* Shared/EntryPointUtilities/Cocoa/XPCService/XPCServiceMain.mm:
(WebKit::XPCEventHandler):
(WebKit::XPCInitializationHandler):
(WebKit::XPCServiceMain):
(WebKit::XPCServiceEventHandler): Deleted.
* UIProcess/API/APIProcessPoolConfiguration.h:
* UIProcess/API/Cocoa/_WKProcessPoolConfiguration.h:
* UIProcess/API/Cocoa/_WKProcessPoolConfiguration.mm:
(-[_WKProcessPoolConfiguration usesNetworkingDaemon]):
(-[_WKProcessPoolConfiguration setUsesNetworkingDaemon:]):
* UIProcess/AuxiliaryProcessProxy.cpp:
(WebKit::AuxiliaryProcessProxy::getLaunchOptions):
* UIProcess/Launcher/ProcessLauncher.h:
* UIProcess/Launcher/mac/ProcessLauncherMac.mm:
(WebKit::serviceName):
(WebKit::ProcessLauncher::launchProcess):
* UIProcess/Network/NetworkProcessProxy.cpp:
(WebKit::NetworkProcessProxy::getLaunchOptions):
* UIProcess/WebProcessPool.cpp:
(WebKit::WebProcessPool::usesNetworkingDaemon const):
* UIProcess/WebProcessPool.h:

Source/WTF:

* wtf/spi/darwin/XPCSPI.h:
Instead of using XPC bootstrap SPI, we just send a separate message.
xpc_copy_bootstrap does not seem to work in  daemons.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@241197 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
Source/WTF/ChangeLog
Source/WTF/wtf/spi/darwin/XPCSPI.h
Source/WebKit/ChangeLog
Source/WebKit/NetworkProcess/EntryPoint/Cocoa/Daemon/DaemonEntryPoint.mm
Source/WebKit/Shared/EntryPointUtilities/Cocoa/XPCService/XPCServiceEntryPoint.h
Source/WebKit/Shared/EntryPointUtilities/Cocoa/XPCService/XPCServiceMain.mm
Source/WebKit/UIProcess/API/APIProcessPoolConfiguration.h
Source/WebKit/UIProcess/API/Cocoa/_WKProcessPoolConfiguration.h
Source/WebKit/UIProcess/API/Cocoa/_WKProcessPoolConfiguration.mm
Source/WebKit/UIProcess/AuxiliaryProcessProxy.cpp
Source/WebKit/UIProcess/Launcher/ProcessLauncher.h
Source/WebKit/UIProcess/Launcher/mac/ProcessLauncherMac.mm
Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp
Source/WebKit/UIProcess/WebProcessPool.cpp
Source/WebKit/UIProcess/WebProcessPool.h

index 0d49678..bacb62d 100644 (file)
@@ -1,3 +1,14 @@
+2019-02-08  Alex Christensen  <achristensen@webkit.org>
+
+        Add SPI to use networking daemon instead of XPC service
+        https://bugs.webkit.org/show_bug.cgi?id=194427
+
+        Reviewed by Geoffrey Garen.
+
+        * wtf/spi/darwin/XPCSPI.h:
+        Instead of using XPC bootstrap SPI, we just send a separate message.
+        xpc_copy_bootstrap does not seem to work in  daemons.
+
 2019-02-08  Benjamin Poulain  <benjamin@webkit.org>
 
         clampTo(): do not convert the input to double when dealing with integers
index 8cace5d..f104b90 100644 (file)
@@ -152,8 +152,6 @@ void xpc_connection_set_instance(xpc_connection_t, uuid_t);
 mach_port_t xpc_dictionary_copy_mach_send(xpc_object_t, const char*);
 void xpc_dictionary_set_mach_send(xpc_object_t, const char*, mach_port_t);
 
-void xpc_connection_set_bootstrap(xpc_connection_t, xpc_object_t bootstrap);
-xpc_object_t xpc_copy_bootstrap(void);
 void xpc_connection_set_oneshot_instance(xpc_connection_t, uuid_t instance);
 
 void xpc_array_append_value(xpc_object_t xarray, xpc_object_t value);
@@ -181,3 +179,5 @@ void xpc_release(xpc_object_t);
 #endif
 
 WTF_EXTERN_C_END
+
+#define XPC_CONNECTION_MACH_SERVICE_LISTENER (1 << 0)
index ee5f983..d5a70a1 100644 (file)
@@ -1,3 +1,37 @@
+2019-02-08  Alex Christensen  <achristensen@webkit.org>
+
+        Add SPI to use networking daemon instead of XPC service
+        https://bugs.webkit.org/show_bug.cgi?id=194427
+
+        Reviewed by Geoffrey Garen.
+
+        There is still work to be done, but with the proper plist it starts and loads webpages!
+
+        * NetworkProcess/EntryPoint/Cocoa/Daemon/DaemonEntryPoint.mm:
+        (WebKit::DaemonMain):
+        * Shared/EntryPointUtilities/Cocoa/XPCService/XPCServiceEntryPoint.h:
+        * Shared/EntryPointUtilities/Cocoa/XPCService/XPCServiceMain.mm:
+        (WebKit::XPCEventHandler):
+        (WebKit::XPCInitializationHandler):
+        (WebKit::XPCServiceMain):
+        (WebKit::XPCServiceEventHandler): Deleted.
+        * UIProcess/API/APIProcessPoolConfiguration.h:
+        * UIProcess/API/Cocoa/_WKProcessPoolConfiguration.h:
+        * UIProcess/API/Cocoa/_WKProcessPoolConfiguration.mm:
+        (-[_WKProcessPoolConfiguration usesNetworkingDaemon]):
+        (-[_WKProcessPoolConfiguration setUsesNetworkingDaemon:]):
+        * UIProcess/AuxiliaryProcessProxy.cpp:
+        (WebKit::AuxiliaryProcessProxy::getLaunchOptions):
+        * UIProcess/Launcher/ProcessLauncher.h:
+        * UIProcess/Launcher/mac/ProcessLauncherMac.mm:
+        (WebKit::serviceName):
+        (WebKit::ProcessLauncher::launchProcess):
+        * UIProcess/Network/NetworkProcessProxy.cpp:
+        (WebKit::NetworkProcessProxy::getLaunchOptions):
+        * UIProcess/WebProcessPool.cpp:
+        (WebKit::WebProcessPool::usesNetworkingDaemon const):
+        * UIProcess/WebProcessPool.h:
+
 2019-02-08  Keith Rollin  <krollin@apple.com>
 
         Unreviewed build fix.
index 224defa..1922840 100644 (file)
 #import "config.h"
 #import "DaemonEntryPoint.h"
 
-namespace WebKit {
+#import "XPCServiceEntryPoint.h"
+#import <wtf/spi/darwin/XPCSPI.h>
 
-int DaemonMain(int, const char**)
+namespace WebKit {
+    
+int DaemonMain(int argc, const char** argv)
 {
-    return 0;
+    if (argc < 2 || strcmp(argv[1], "WebKitNetworkingDaemon")) {
+        WTFLogAlways("Unexpected daemon parameters");
+        return EXIT_FAILURE;
+    }
+
+    xpc_connection_t listener = xpc_connection_create_mach_service("com.apple.WebKit.NetworkingDaemon", dispatch_get_main_queue(), XPC_CONNECTION_MACH_SERVICE_LISTENER);
+    
+    xpc_connection_set_event_handler(listener, ^(xpc_object_t peer) {
+        if (!peer || xpc_get_type(peer) != XPC_TYPE_CONNECTION) {
+            WTFLogAlways("Unexpected XPC object");
+            return;
+        }
+
+        XPCEventHandler(peer, AuxiliaryProcessType::Daemon);
+    });
+
+    xpc_connection_resume(listener);
+    CFRunLoopRun();
+
+    return EXIT_SUCCESS;
 }
 
 }
index c563273..0eca717 100644 (file)
@@ -130,6 +130,9 @@ ALLOW_DEPRECATED_DECLARATIONS_END
 
 int XPCServiceMain(int, const char**);
 
+enum class AuxiliaryProcessType { Daemon, XPCService };
+void XPCEventHandler(xpc_connection_t, AuxiliaryProcessType);
+
 void XPCServiceExit(OSObjectPtr<xpc_object_t>&& priorityBoostMessage);
 
 } // namespace WebKit
index d673c32..12c59f5 100644 (file)
@@ -38,7 +38,9 @@
 
 namespace WebKit {
 
-static void XPCServiceEventHandler(xpc_connection_t peer)
+void XPCInitializationHandler(xpc_object_t);
+
+void XPCEventHandler(xpc_connection_t peer, AuxiliaryProcessType processType)
 {
     static xpc_object_t priorityBoostMessage = nullptr;
 
@@ -47,13 +49,20 @@ static void XPCServiceEventHandler(xpc_connection_t peer)
         xpc_type_t type = xpc_get_type(event);
         if (type == XPC_TYPE_ERROR) {
             if (event == XPC_ERROR_CONNECTION_INVALID || event == XPC_ERROR_TERMINATION_IMMINENT) {
-                // FIXME: Handle this case more gracefully.
-                exit(EXIT_FAILURE);
+                if (processType == AuxiliaryProcessType::XPCService) {
+                    // FIXME: Handle this case more gracefully.
+                    exit(EXIT_FAILURE);
+                } else {
+                    // FIXME: Deref the NetworkProcess object associated with this xpc connection
+                    // once we have a container for such objects.
+                }
             }
         } else {
             assert(type == XPC_TYPE_DICTIONARY);
 
             if (!strcmp(xpc_dictionary_get_string(event, "message-name"), "bootstrap")) {
+                XPCInitializationHandler(xpc_dictionary_get_value(event, "initialization-message"));
+                
                 CFBundleRef webKitBundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.WebKit"));
                 CFStringRef entryPointFunctionName = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), CFSTR("WebKitEntryPoint"));
 
@@ -92,8 +101,10 @@ static void XPCServiceEventHandler(xpc_connection_t peer)
     xpc_connection_resume(peer);
 }
 
-int XPCServiceMain(int, const char**)
+void XPCInitializationHandler(xpc_object_t event)
 {
+    ASSERT(event);
+    ASSERT(xpc_get_type(event) == XPC_TYPE_DICTIONARY);
 #if defined(__i386__)
     // FIXME: This should only be done for the 32-bit plug-in XPC service so we rely on the fact that
     // it's the only of the XPC services that are 32-bit. We should come up with a more targeted #if check.
@@ -104,40 +115,37 @@ int XPCServiceMain(int, const char**)
     }
 #endif
 
-    auto bootstrap = adoptOSObject(xpc_copy_bootstrap());
 #if PLATFORM(IOS_FAMILY)
-    auto containerEnvironmentVariables = xpc_dictionary_get_value(bootstrap.get(), "ContainerEnvironmentVariables");
+    auto containerEnvironmentVariables = xpc_dictionary_get_value(event, "ContainerEnvironmentVariables");
     xpc_dictionary_apply(containerEnvironmentVariables, ^(const char *key, xpc_object_t value) {
         setenv(key, xpc_string_get_string_ptr(value), 1);
         return true;
     });
 #endif
 
-    if (bootstrap) {
 #if PLATFORM(MAC)
-        if (const char* webKitBundleVersion = xpc_dictionary_get_string(bootstrap.get(), "WebKitBundleVersion")) {
-            CFBundleRef webKitBundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.WebKit"));
-            NSString *expectedBundleVersion = (NSString *)CFBundleGetValueForInfoDictionaryKey(webKitBundle, kCFBundleVersionKey);
+    if (const char* webKitBundleVersion = xpc_dictionary_get_string(event, "WebKitBundleVersion")) {
+        CFBundleRef webKitBundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.WebKit"));
+        NSString *expectedBundleVersion = (NSString *)CFBundleGetValueForInfoDictionaryKey(webKitBundle, kCFBundleVersionKey);
 
-            if (strcmp(webKitBundleVersion, expectedBundleVersion.UTF8String)) {
-                _WKSetCrashReportApplicationSpecificInformation([NSString stringWithFormat:@"WebKit framework version mismatch: '%s'", webKitBundleVersion]);
-                __builtin_trap();
-            }
+        if (strcmp(webKitBundleVersion, expectedBundleVersion.UTF8String)) {
+            _WKSetCrashReportApplicationSpecificInformation([NSString stringWithFormat:@"WebKit framework version mismatch: '%s'", webKitBundleVersion]);
+            __builtin_trap();
         }
+    }
 #endif
 
-        if (xpc_object_t languages = xpc_dictionary_get_value(bootstrap.get(), "OverrideLanguages")) {
-            @autoreleasepool {
-                NSDictionary *existingArguments = [[NSUserDefaults standardUserDefaults] volatileDomainForName:NSArgumentDomain];
-                NSMutableDictionary *newArguments = [existingArguments mutableCopy];
-                RetainPtr<NSMutableArray> newLanguages = adoptNS([[NSMutableArray alloc] init]);
-                xpc_array_apply(languages, ^(size_t index, xpc_object_t value) {
-                    [newLanguages addObject:[NSString stringWithCString:xpc_string_get_string_ptr(value) encoding:NSUTF8StringEncoding]];
-                    return true;
-                });
-                [newArguments setValue:newLanguages.get() forKey:@"AppleLanguages"];
-                [[NSUserDefaults standardUserDefaults] setVolatileDomain:newArguments forName:NSArgumentDomain];
-            }
+    if (xpc_object_t languages = xpc_dictionary_get_value(event, "OverrideLanguages")) {
+        @autoreleasepool {
+            NSDictionary *existingArguments = [[NSUserDefaults standardUserDefaults] volatileDomainForName:NSArgumentDomain];
+            NSMutableDictionary *newArguments = [existingArguments mutableCopy];
+            RetainPtr<NSMutableArray> newLanguages = adoptNS([[NSMutableArray alloc] init]);
+            xpc_array_apply(languages, ^(size_t index, xpc_object_t value) {
+                [newLanguages addObject:[NSString stringWithCString:xpc_string_get_string_ptr(value) encoding:NSUTF8StringEncoding]];
+                return true;
+            });
+            [newArguments setValue:newLanguages.get() forKey:@"AppleLanguages"];
+            [[NSUserDefaults standardUserDefaults] setVolatileDomain:newArguments forName:NSArgumentDomain];
         }
     }
 
@@ -153,8 +161,13 @@ int XPCServiceMain(int, const char**)
     }
 #endif
 #endif
+}
 
-    xpc_main(XPCServiceEventHandler);
+int XPCServiceMain(int, const char**)
+{
+    xpc_main([] (xpc_connection_t peer) {
+        XPCEventHandler(peer, AuxiliaryProcessType::XPCService);
+    });
     return 0;
 }
 
index ec73ebd..e3a6a77 100644 (file)
@@ -182,6 +182,9 @@ public:
 #if PLATFORM(COCOA)
     bool suppressesConnectionTerminationOnSystemChange() const { return m_suppressesConnectionTerminationOnSystemChange; }
     void setSuppressesConnectionTerminationOnSystemChange(bool suppressesConnectionTerminationOnSystemChange) { m_suppressesConnectionTerminationOnSystemChange = suppressesConnectionTerminationOnSystemChange; }
+
+    bool usesNetworkingDaemon() const { return m_usesNetworkingDaemon; }
+    void setUsesNetworkingDaemon(bool usesNetworkingDaemon) { m_usesNetworkingDaemon = usesNetworkingDaemon; }
 #endif
 
 private:
@@ -235,6 +238,7 @@ private:
 
 #if PLATFORM(COCOA)
     bool m_suppressesConnectionTerminationOnSystemChange { false };
+    bool m_usesNetworkingDaemon { false };
 #endif
 };
 
index 54a56e9..f671d7b 100644 (file)
@@ -70,6 +70,7 @@ WK_CLASS_AVAILABLE(macosx(10.10), ios(8.0))
 @property (nonatomic) BOOL pageCacheEnabled WK_API_AVAILABLE(macosx(10.14), ios(12.0));
 @property (nonatomic) BOOL suppressesConnectionTerminationOnSystemChange WK_API_AVAILABLE(macosx(10.14), ios(12.0));
 @property (nonatomic, getter=isJITEnabled) BOOL JITEnabled WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
+@property (nonatomic) BOOL usesNetworkingDaemon WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 
 @end
 
index 07b13eb..8a36781 100644 (file)
     _processPoolConfiguration->setJITEnabled(enabled);
 }
 
+- (BOOL)usesNetworkingDaemon
+{
+    return _processPoolConfiguration->usesNetworkingDaemon();
+}
+
+- (void)setUsesNetworkingDaemon:(BOOL)enabled
+{
+    _processPoolConfiguration->setUsesNetworkingDaemon(enabled);
+}
+
+
 - (void)setSuppressesConnectionTerminationOnSystemChange:(BOOL)suppressesConnectionTerminationOnSystemChange
 {
     _processPoolConfiguration->setSuppressesConnectionTerminationOnSystemChange(suppressesConnectionTerminationOnSystemChange);
index f50f7fb..0b6bcdc 100644 (file)
@@ -72,6 +72,8 @@ void AuxiliaryProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& lau
     case ProcessLauncher::ProcessType::Network:
         varname = "NETWORK_PROCESS_CMD_PREFIX";
         break;
+    case ProcessLauncher::ProcessType::NetworkDaemon:
+        ASSERT_NOT_REACHED();
     }
     const char* processCmdPrefix = getenv(varname);
     if (processCmdPrefix && *processCmdPrefix)
index e48e6b7..7307155 100644 (file)
@@ -65,6 +65,7 @@ public:
         Plugin64,
 #endif
         Network,
+        NetworkDaemon
     };
 
     struct LaunchOptions {
index 6cbac25..66896f1 100644 (file)
@@ -56,6 +56,9 @@ static const char* serviceName(const ProcessLauncher::LaunchOptions& launchOptio
         return launchOptions.nonValidInjectedCodeAllowed ? "com.apple.WebKit.WebContent.Development" : "com.apple.WebKit.WebContent";
     case ProcessLauncher::ProcessType::Network:
         return "com.apple.WebKit.Networking";
+    case ProcessLauncher::ProcessType::NetworkDaemon:
+        ASSERT_NOT_REACHED();
+        return nullptr;
 #if ENABLE(NETSCAPE_PLUGIN_API)
     case ProcessLauncher::ProcessType::Plugin32:
         return "com.apple.WebKit.Plugin.32";
@@ -95,17 +98,16 @@ void ProcessLauncher::launchProcess()
 {
     ASSERT(!m_xpcConnection);
 
-    const char* name;
-    if (!m_launchOptions.customWebContentServiceBundleIdentifier.isNull())
-        name = m_launchOptions.customWebContentServiceBundleIdentifier.data();
-    else
-        name = serviceName(m_launchOptions);
+    if (m_launchOptions.processType == ProcessLauncher::ProcessType::NetworkDaemon)
+        m_xpcConnection = adoptOSObject(xpc_connection_create_mach_service("com.apple.WebKit.NetworkingDaemon", dispatch_get_main_queue(), 0));
+    else {
+        const char* name = m_launchOptions.customWebContentServiceBundleIdentifier.isNull() ? serviceName(m_launchOptions) : m_launchOptions.customWebContentServiceBundleIdentifier.data();
+        m_xpcConnection = adoptOSObject(xpc_connection_create(name, nullptr));
 
-    m_xpcConnection = adoptOSObject(xpc_connection_create(name, nullptr));
-
-    uuid_t uuid;
-    uuid_generate(uuid);
-    xpc_connection_set_oneshot_instance(m_xpcConnection.get(), uuid);
+        uuid_t uuid;
+        uuid_generate(uuid);
+        xpc_connection_set_oneshot_instance(m_xpcConnection.get(), uuid);
+    }
 
     // Inherit UI process localization. It can be different from child process default localization:
     // 1. When the application and system frameworks simply have different localized resources available, we should match the application.
@@ -136,7 +138,6 @@ void ProcessLauncher::launchProcess()
 #if PLATFORM(MAC)
     xpc_dictionary_set_string(initializationMessage.get(), "WebKitBundleVersion", [[NSBundle bundleWithIdentifier:@"com.apple.WebKit"].infoDictionary[(__bridge NSString *)kCFBundleVersionKey] UTF8String]);
 #endif
-    xpc_connection_set_bootstrap(m_xpcConnection.get(), initializationMessage.get());
 
     if (shouldLeakBoost(m_launchOptions)) {
         auto preBootstrapMessage = adoptOSObject(xpc_dictionary_create(nullptr, nullptr, 0));
@@ -171,8 +172,9 @@ void ProcessLauncher::launchProcess()
     if (clientIdentifier.isNull())
         clientIdentifier = [[NSBundle mainBundle] bundleIdentifier];
 
-    // FIXME: Switch to xpc_connection_set_bootstrap once it's available everywhere we need.
     auto bootstrapMessage = adoptOSObject(xpc_dictionary_create(nullptr, nullptr, 0));
+
+    xpc_dictionary_set_value(bootstrapMessage.get(), "initialization-message", initializationMessage.get());
     
     if (m_client && !m_client->isJITEnabled())
         xpc_dictionary_set_bool(bootstrapMessage.get(), "disable-jit", true);
@@ -201,6 +203,9 @@ void ProcessLauncher::launchProcess()
     auto errorHandlerImpl = [weakProcessLauncher = makeWeakPtr(*this), listeningPort] (xpc_object_t event) {
         ASSERT(!event || xpc_get_type(event) == XPC_TYPE_ERROR);
 
+        if (event)
+            LOG_ERROR("Error launching auxiliary process: %s", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
+
         auto processLauncher = weakProcessLauncher.get();
         if (!processLauncher)
             return;
index c546119..d2a9289 100644 (file)
@@ -102,7 +102,7 @@ NetworkProcessProxy::~NetworkProcessProxy()
 
 void NetworkProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launchOptions)
 {
-    launchOptions.processType = ProcessLauncher::ProcessType::Network;
+    launchOptions.processType = m_processPool.usesNetworkingDaemon() ? ProcessLauncher::ProcessType::NetworkDaemon : ProcessLauncher::ProcessType::Network;
     AuxiliaryProcessProxy::getLaunchOptions(launchOptions);
 
     if (processPool().shouldMakeNextNetworkProcessLaunchFailForTesting()) {
index 85ca6de..b1e1fe7 100644 (file)
@@ -1507,6 +1507,15 @@ void WebProcessPool::updateMaxSuspendedPageCount()
         m_suspendedPages.removeFirst();
 }
 
+bool WebProcessPool::usesNetworkingDaemon() const
+{
+#if PLATFORM(COCOA)
+    return m_configuration->usesNetworkingDaemon();
+#else
+    return false;
+#endif
+}
+    
 void WebProcessPool::setCacheModel(CacheModel cacheModel)
 {
     m_configuration->setCacheModel(cacheModel);
index 3aecd42..79a884c 100644 (file)
@@ -255,6 +255,8 @@ public:
     DownloadProxy* createDownloadProxy(const WebCore::ResourceRequest&, WebPageProxy* originatingPage);
     API::DownloadClient& downloadClient() { return *m_downloadClient; }
 
+    bool usesNetworkingDaemon() const;
+    
     API::LegacyContextHistoryClient& historyClient() { return *m_historyClient; }
     WebContextClient& client() { return m_client; }