WebKit源碼十分龐大,漫無(wú)目的去看代碼,可能會(huì)傻傻搞不清,所以需要帶著問(wèn)題調(diào)試源碼
Q0.WKWebview生命周期是什么
WKWebView的代理是WKNavigationDelegate,查看其接口代碼:
// 決定是否繼續(xù)加載webview
// 可以立即調(diào)用,或者異步調(diào)用decisionHandler(WKNavigationActionPolicyAllow) 允許加載
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler WK_SWIFT_ASYNC(3);
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction preferences:(WKWebpagePreferences *)preferences decisionHandler:(void (^)(WKNavigationActionPolicy, WKWebpagePreferences *))decisionHandler WK_SWIFT_ASYNC(4) WK_API_AVAILABLE(macos(10.15), ios(13.0));
// 獲得http header數(shù)據(jù)后會(huì)回調(diào),是否繼續(xù)加載webview
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler WK_SWIFT_ASYNC(3);
// webview中開(kāi)始加載的時(shí)候,真正發(fā)出請(qǐng)求時(shí)回調(diào)
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation;
// 重定向時(shí)調(diào)用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation;
// 開(kāi)始加載數(shù)據(jù)時(shí)出錯(cuò)
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
// 獲取http body并回調(diào)
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation;
// 頁(yè)面加載完成時(shí)調(diào)用
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation;
// 跳轉(zhuǎn)失敗時(shí)調(diào)用
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
// 證書(shū)校驗(yàn)
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler WK_SWIFT_ASYNC_NAME(webView(_:respondTo:));
// webview進(jìn)程終止時(shí)觸發(fā)
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView WK_API_AVAILABLE(macos(10.11), ios(9.0));
// 使用已棄用的TLS(Transport Layer Security )版本建立網(wǎng)絡(luò)連接時(shí)調(diào)用
- (void)webView:(WKWebView *)webView authenticationChallenge:(NSURLAuthenticationChallenge *)challenge shouldAllowDeprecatedTLS:(void (^)(BOOL))decisionHandler WK_SWIFT_ASYNC_NAME(webView(_:shouldAllowDeprecatedTLSFor:)) WK_SWIFT_ASYNC(3) WK_API_AVAILABLE(macos(11.0), ios(14.0));
// web操作變成下載
- (void)webView:(WKWebView *)webView navigationAction:(WKNavigationAction *)navigationAction didBecomeDownload:(WKDownload *)download WK_API_AVAILABLE(macos(11.3), ios(14.5));
// web響應(yīng)變成下載
- (void)webView:(WKWebView *)webView navigationResponse:(WKNavigationResponse *)navigationResponse didBecomeDownload:(WKDownload *)download WK_API_AVAILABLE(macos(11.3), ios(14.5));
寫(xiě)一個(gè)簡(jiǎn)單的demo,在WKNavigationDelegate回調(diào)上加上日志,結(jié)果如下:
---func---: decidePolicyForNavigationAction decisionHandler
---func---: didStartProvisionalNavigation
---func---: decidePolicyForNavigationResponse decisionHandler
---func---: didCommitNavigation
---func---: didFinishNavigation
主要流程為:
1.判斷是否可以加載
2.開(kāi)始加載
3.判斷回報(bào)是否可以加載
4.接收數(shù)據(jù)
5.接收完成

Q1.WebKit是什么?
WebKit is an open-source Web browser engine. It’s a framework in macOS and iOS, and used by many first party and third party applications including Safari, Mail, Notes, Books, News, and App Store.
The WebKit codebase is mostly written in C++ with bits of C and assembly, primarily in JavaScriptCore, and some Objective-C to integrate with Cocoa platforms.
It primarily consists of the following components, each inside its own directory in Source:
- bmalloc - WebKit’s malloc implementation as a bump pointer allocator. It provides an important security feature, called IsoHeap, which segregates each type of object into its own page to prevent type confusion attacks upon use-after-free.
- WTF - Stands for Web Template Framework. WebKit’s template library. The rest of the WebKit codebase is built using this template library in addition to, and often in place of, similar class templates in the C++ standard library. It contains common container classes such as Vector, HashMap (unordered), HashSet, and smart pointer types such as Ref, RefPtr, and WeakPtr used throughout the rest of WebKit.
-
JavaScriptCore - WebKit’s JavaScript engine; often abbreviated as JSC. JSC parses JavaScript and generates byte code, which is then executed by one of the following four tiers. Many tiers are needed to balance between compilation time and execution time. Also see Phil's blog post about Speculation in JavaScriptCore.
- Interpreter - This tier reads and executes instructions in byte code in C++.
- Baseline JIT - The first Just In Time compiler tier serves as the profiler as well as a significant speed up from the interpreter.
- DFG JIT - Data Flow Graph Just In Time compiler uses the data flow analysis to generate optimized machine code.
-
FTL JIT - Faster than Light Just In Time compiler which uses B3 backend. It’s the fastest tier of JSC. JavaScriptCode also implements JavaScriptCore API for macOS and iOS applications.
JSC
-
WebCore - The largest component of WebKit, this layer implements most of the Web APIs and their behaviors. Most importantly, this component implements HTML, XML, and CSS parsers and implements HTML, SVG, and MathML elements as well as CSS. It also implements CSS JIT, the only Just In Time compiler for CSS in existence. It works with a few tree data structures:
- Document Object Model - This is the tree data structure we create from parsing HTML.
-
Render Tree - This tree represents the visual representation of each element in DOM tree computed from CSS and also stores the geometric layout information of each element.
WebCore結(jié)構(gòu)圖
WebCore/PAL and WebCore/platform - Whilst technically a part of WebCore, this is a platform abstraction layer for WebCore so that the rest of WebCore code can remain platform independent / agnostic across all the platforms WebKit can run on: macOS, iOS, Windows, Linux, etc... Historically, most of this code resided in WebCore/platform. There is an ongoing multi-year project to slowly migrate code to PAL as we remove the reverse dependencies to WebCore.
WebKitLegacy (a.k.a. WebKit1) - This layer interfaces WebCore with the rest of operating systems in single process and implements WebView on macOS and UIWebView on iOS.
-
WebKit (a.k.a. WebKit2) - This layer implements the multi-process architecture of WebKit, and implements WKWebView on macOS and iOS. WebKit’s multi-process architecture consists of the following processes:
- UI process - This is the application process. e.g. Safari and Mail
- WebContent process - This process loads & runs code loaded from websites. Each tab in Safari typically has its own WebContent process. This is important to keep each tab responsive and protect websites from one another.
-
Networking process - This process is responsible for handling network requests as well as storage management. All WebContent processes in a single session (default vs. private browsing) share a single networking session in the networking process.
WebKit2結(jié)構(gòu)模型
WebInspector / WebDriver - WebKit’s developer tool & automation tool for Web developers.
Q2.在safari上輸入now.qq.com到展示NOW直播主頁(yè)的過(guò)程中,到底發(fā)生了些什么?
// 首先是初始化WKWebview
- (WKWebView *)createWebView
{
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.preferences._mockCaptureDevicesEnabled = YES;
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.webViewContainer.bounds configuration:configuration];
webView.navigationDelegate = self;
webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:TitleContext];
return webView;
}
代碼中初始化的時(shí)候引入了一個(gè)WKWebViewConfiguration的類,點(diǎn)進(jìn)去查看,其注釋為WebView屬性的集合,其中有個(gè)WKUserContentController類型的屬性,后面會(huì)用到。
先看wkwebview初始化的時(shí)候做了些什么:
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
{
// @interface WKWebView : NSView
if (!(self = [super initWithFrame:frame]))
return nil;
[self _initializeWithConfiguration:configuration];
return self;
}
- (void)_initializeWithConfiguration:(WKWebViewConfiguration *)configuration
{
// 判斷configuration合法性
if (!configuration)
[NSException raise:NSInvalidArgumentException format:@"Configuration cannot be nil"];
if (!configuration.websiteDataStore)
[NSException raise:NSInvalidArgumentException format:@"Configuration websiteDataStore cannot be nil"];
_configuration = adoptNS([configuration copy]);
// TODO: 這里初始化的時(shí)候是nil,需要看下什么情況下會(huì)賦值
if (WKWebView *relatedWebView = [_configuration _relatedWebView]) {
WKProcessPool *processPool = [_configuration processPool];
WKProcessPool *relatedWebViewProcessPool = [relatedWebView->_configuration processPool];
if (processPool && processPool != relatedWebViewProcessPool)
[NSException raise:NSInvalidArgumentException format:@"Related web view %@ has process pool %@ but configuration specifies a different process pool %@", relatedWebView, relatedWebViewProcessPool, configuration.processPool];
if ([relatedWebView->_configuration websiteDataStore] != [_configuration websiteDataStore] && linkedOnOrAfterSDKWithBehavior(SDKAlignedBehavior::ExceptionsForRelatedWebViewsUsingDifferentDataStores))
[NSException raise:NSInvalidArgumentException format:@"Related web view %@ has data store %@ but configuration specifies a different data store %@", relatedWebView, [relatedWebView->_configuration websiteDataStore], [_configuration websiteDataStore]];
[_configuration setProcessPool:relatedWebViewProcessPool];
}
validate(_configuration.get());
// 這里引入了一個(gè)pageConfiguration
WebKit::WebProcessPool& processPool = *[_configuration processPool]->_processPool;
auto pageConfiguration = [configuration copyPageConfiguration];
pageConfiguration->setProcessPool(&processPool);
[self _setupPageConfiguration:pageConfiguration];
_usePlatformFindUI = YES;
#if PLATFORM(IOS_FAMILY)
// 初始化一些屬性
_obscuredInsetEdgesAffectedBySafeArea = UIRectEdgeTop | UIRectEdgeLeft | UIRectEdgeRight;
_allowsViewportShrinkToFit = defaultAllowsViewportShrinkToFit;
_allowsLinkPreview = linkedOnOrAfterSDKWithBehavior(SDKAlignedBehavior::LinkPreviewEnabledByDefault);
_findInteractionEnabled = NO;
_needsToPresentLockdownModeMessage = YES;
auto fastClickingEnabled = []() {
if (NSNumber *enabledValue = [[NSUserDefaults standardUserDefaults] objectForKey:@"WebKitFastClickingDisabled"])
return enabledValue.boolValue;
return defaultFastClickingEnabled;
};
_fastClickingIsDisabled = fastClickingEnabled();
_dragInteractionPolicy = _WKDragInteractionPolicyDefault;
// 創(chuàng)建WKContentView
_contentView = adoptNS([[WKContentView alloc] initWithFrame:self.bounds processPool:processPool configuration:pageConfiguration.copyRef() webView:self]);
// 創(chuàng)建page
_page = [_contentView page];
// 添加到page管理類
[[_configuration _contentProviderRegistry] addPage:*_page];
// @interface WKWebView (WKViewInternalIOS) 分類實(shí)現(xiàn)
// 初始化WKScrollView
[self _setupScrollAndContentViews];
// 設(shè)置contentView和scrollView的背景色
if (!self.opaque || !pageConfiguration->drawsBackground())
[self _setOpaqueInternal:NO];
else
[self _updateScrollViewBackground];
// frame變化之后更新
[self _frameOrBoundsChanged];
// 注冊(cè)一些常用事件,ex:_keyboardWillShow,_windowDidRotate
[self _registerForNotifications];
_page->contentSizeCategoryDidChange([self _contentSizeCategory]);
auto notificationName = adoptNS([[NSString alloc] initWithCString:kGSEventHardwareKeyboardAvailabilityChangedNotification encoding:NSUTF8StringEncoding]);
auto notificationBehavior = static_cast<CFNotificationSuspensionBehavior>(CFNotificationSuspensionBehaviorCoalesce | _CFNotificationObserverIsObjC);
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), (__bridge const void *)(self), hardwareKeyboardAvailabilityChangedCallback, (__bridge CFStringRef)notificationName.get(), nullptr, notificationBehavior);
#endif // PLATFORM(IOS_FAMILY)
#if ENABLE(META_VIEWPORT)
_page->setForceAlwaysUserScalable([_configuration ignoresViewportScaleLimits]);
#endif
// 下面都是為_(kāi)page設(shè)置一些參數(shù),至此webview初始化完成
// 此時(shí)的操作都還在我們的app進(jìn)程中
if (NSString *applicationNameForUserAgent = configuration.applicationNameForUserAgent)
_page->setApplicationNameForUserAgent(applicationNameForUserAgent);
_page->setApplicationNameForDesktopUserAgent(configuration._applicationNameForDesktopUserAgent);
_navigationState = makeUnique<WebKit::NavigationState>(self);
_page->setNavigationClient(_navigationState->createNavigationClient());
_uiDelegate = makeUnique<WebKit::UIDelegate>(self);
_page->setFindClient(makeUnique<WebKit::FindClient>(self));
_page->setDiagnosticLoggingClient(makeUnique<WebKit::DiagnosticLoggingClient>(self));
_iconLoadingDelegate = makeUnique<WebKit::IconLoadingDelegate>(self);
_resourceLoadDelegate = makeUnique<WebKit::ResourceLoadDelegate>(self);
for (auto& pair : pageConfiguration->urlSchemeHandlers())
_page->setURLSchemeHandlerForScheme(pair.value.get(), pair.key);
_page->setCocoaView(self);
[WebViewVisualIdentificationOverlay installForWebViewIfNeeded:self kind:@"WKWebView" deprecated:NO];
#if PLATFORM(IOS_FAMILY)
auto timeNow = MonotonicTime::now();
_timeOfRequestForVisibleContentRectUpdate = timeNow;
_timeOfLastVisibleContentRectUpdate = timeNow;
_timeOfFirstVisibleContentRectUpdateWithPendingCommit = timeNow;
#endif
}
WKWebview的創(chuàng)建過(guò)程中,會(huì)先創(chuàng)建WKContentView,在WKContentView中通過(guò)WebProcessPool::createWebPage創(chuàng)建出WebProcessProxy對(duì)象,再執(zhí)行process->createWebPage方法創(chuàng)建WebPageProxy(這里創(chuàng)建的process的時(shí)候會(huì)將它添加到WebProcessPool的Vector<Ref<WebProcessProxy>> m_processes變量中去):
1.WKContentView:
2.WebPageProxy
3.WebProcessPool
4.WebProcessProxy
// WKContentView.mm
- (instancetype)initWithFrame:(CGRect)frame processPool:(NakedRef<WebKit::WebProcessPool>)processPool configuration:(Ref<API::PageConfiguration>&&)configuration webView:(WKWebView *)webView
{
if (!(self = [super initWithFrame:frame webView:webView]))
return nil;
WebKit::InitializeWebKit2();
_pageClient = makeUnique<WebKit::PageClientImpl>(self, webView);
_webView = webView;
return [self _commonInitializationWithProcessPool:processPool configuration:WTFMove(configuration)];
}
- (instancetype)_commonInitializationWithProcessPool:(WebKit::WebProcessPool&)processPool configuration:(Ref<API::PageConfiguration>&&)configuration
{
...
// 創(chuàng)建WebPageProxy
_page = processPool.createWebPage(*_pageClient, WTFMove(configuration));
// WebPageProxy初始化參數(shù)的時(shí)候也會(huì)調(diào)用send方法
_page->initializeWebPage();
...
}
void WebPageProxy::initializeWebPage()
{
...
// 這里將create web page的ipc消息拼接到了m_pendingMessages中
send(Messages::WebProcess::CreateWebPage(m_webPageID, creationParameters(m_process, *m_drawingArea)), 0);
...
}
// WebProcess.cpp
void WebProcess::createWebPage(PageIdentifier pageID, WebPageCreationParameters&& parameters)
{
// It is necessary to check for page existence here since during a window.open() (or targeted
// link) the WebPage gets created both in the synchronous handler and through the normal way.
auto result = m_pageMap.add(pageID, nullptr);
if (result.isNewEntry) {
ASSERT(!result.iterator->value);
// 創(chuàng)建webpage
auto page = WebPage::create(pageID, WTFMove(parameters));
result.iterator->value = page.ptr();
#if ENABLE(GPU_PROCESS)
if (m_gpuProcessConnection)
page->gpuProcessConnectionDidBecomeAvailable(*m_gpuProcessConnection);
#endif
// Balanced by an enableTermination in removeWebPage.
disableTermination();
updateCPULimit();
#if OS(LINUX)
RealTimeThreads::singleton().setEnabled(hasVisibleWebPage());
#endif
} else
result.iterator->value->reinitializeWebPage(WTFMove(parameters));
ASSERT(result.iterator->value);
}
Ref<WebPage> WebPage::create(PageIdentifier pageID, WebPageCreationParameters&& parameters)
{
auto page = adoptRef(*new WebPage(pageID, WTFMove(parameters)));
if (WebProcess::singleton().injectedBundle())
WebProcess::singleton().injectedBundle()->didCreatePage(page.ptr());
#if HAVE(SANDBOX_STATE_FLAGS)
// This call is not meant to actually read a preference, but is only here to trigger a sandbox rule in the
// WebContent process, which will toggle a sandbox variable used to determine if the WebContent process
// has finished launching. This call should be replaced with proper API when available.
CFPreferencesGetAppIntegerValue(CFSTR("key"), CFSTR("com.apple.WebKit.WebContent.Launch"), nullptr);
#endif
return page;
}
webpage創(chuàng)建完成之后,回到WebPageProxy的代碼繼續(xù)看
// WebProcessPool.cpp
Ref<WebPageProxy> WebProcessPool::createWebPage(PageClient& pageClient, Ref<API::PageConfiguration>&& pageConfiguration)
{
...
RefPtr<WebProcessProxy> process;
auto captivePortalMode = pageConfiguration->captivePortalModeEnabled() ? WebProcessProxy::CaptivePortalMode::Enabled : WebProcessProxy::CaptivePortalMode::Disabled;
auto* relatedPage = pageConfiguration->relatedPage();
if (relatedPage && !relatedPage->isClosed()) {
// Sharing processes, e.g. when creating the page via window.open().
process = &pageConfiguration->relatedPage()->ensureRunningProcess();
// We do not support several WebsiteDataStores sharing a single process.
ASSERT(process->isDummyProcessProxy() || pageConfiguration->websiteDataStore() == process->websiteDataStore());
ASSERT(&pageConfiguration->relatedPage()->websiteDataStore() == pageConfiguration->websiteDataStore());
} else if (!m_isDelayedWebProcessLaunchDisabled) {
// In the common case, we delay process launch until something is actually loaded in the page.
process = dummyProcessProxy(pageConfiguration->websiteDataStore()->sessionID());
if (!process) {
process = WebProcessProxy::create(*this, pageConfiguration->websiteDataStore(), captivePortalMode, WebProcessProxy::IsPrewarmed::No, CrossOriginMode::Shared, WebProcessProxy::ShouldLaunchProcess::No);
m_dummyProcessProxies.add(pageConfiguration->websiteDataStore()->sessionID(), *process);
m_processes.append(*process);
}
} else
// 關(guān)鍵代碼:當(dāng)進(jìn)程沒(méi)有注冊(cè)的時(shí)候,創(chuàng)建一個(gè)新的
process = processForRegistrableDomain(*pageConfiguration->websiteDataStore(), { }, captivePortalMode);
RefPtr<WebUserContentControllerProxy> userContentController = pageConfiguration->userContentController();
ASSERT(process);
auto page = process->createWebPage(pageClient, WTFMove(pageConfiguration));
if (!m_remoteWorkerPreferences) {
m_remoteWorkerPreferences = page->preferencesStore();
for (auto& workerProcess : remoteWorkerProcesses())
workerProcess.updateRemoteWorkerPreferencesStore(*m_remoteWorkerPreferences);
}
if (userContentController)
m_userContentControllerForRemoteWorkers = userContentController;
bool enableProcessSwapOnCrossSiteNavigation = page->preferences().processSwapOnCrossSiteNavigationEnabled();
#if PLATFORM(IOS_FAMILY)
if (WebCore::IOSApplication::isFirefox() && !linkedOnOrAfterSDKWithBehavior(SDKAlignedBehavior::ProcessSwapOnCrossSiteNavigation))
enableProcessSwapOnCrossSiteNavigation = false;
#endif
bool wasProcessSwappingOnNavigationEnabled = m_configuration->processSwapsOnNavigation();
m_configuration->setProcessSwapsOnNavigationFromExperimentalFeatures(enableProcessSwapOnCrossSiteNavigation);
if (wasProcessSwappingOnNavigationEnabled != m_configuration->processSwapsOnNavigation())
m_webProcessCache->updateCapacity(*this);
#if ENABLE(GPU_PROCESS)
if (auto* gpuProcess = GPUProcessProxy::singletonIfCreated()) {
gpuProcess->updatePreferences(*process);
gpuProcess->updateScreenPropertiesIfNeeded();
}
#endif
return page;
}
Ref<WebProcessProxy> WebProcessPool::processForRegistrableDomain(WebsiteDataStore& websiteDataStore, const RegistrableDomain& registrableDomain, WebProcessProxy::CaptivePortalMode captivePortalMode)
{
// 一些出錯(cuò)分支的條件判斷
...
// 讀取配置,是否使用同一個(gè)web進(jìn)程
if (usesSingleWebProcess()) {
#if PLATFORM(COCOA)
bool mustMatchDataStore = WebKit::WebsiteDataStore::defaultDataStoreExists() && &websiteDataStore != WebKit::WebsiteDataStore::defaultDataStore().ptr();
#else
bool mustMatchDataStore = false;
#endif
for (auto& process : m_processes) {
if (process.ptr() == m_prewarmedProcess.get() || process->isDummyProcessProxy())
continue;
#if ENABLE(SERVICE_WORKER)
if (process->isRunningServiceWorkers())
continue;
#endif
if (mustMatchDataStore && process->websiteDataStore() != &websiteDataStore)
continue;
return process;
}
}
// 創(chuàng)建新的進(jìn)程
return createNewWebProcess(&websiteDataStore, captivePortalMode);
}
Ref<WebProcessProxy> WebProcessPool::createNewWebProcess(WebsiteDataStore* websiteDataStore, WebProcessProxy::CaptivePortalMode captivePortalMode, WebProcessProxy::IsPrewarmed isPrewarmed, CrossOriginMode crossOriginMode)
{
#if PLATFORM(COCOA)
m_tccPreferenceEnabled = doesAppHaveITPEnabled();
// TODO:
if (websiteDataStore && !websiteDataStore->isItpStateExplicitlySet())
websiteDataStore->setResourceLoadStatisticsEnabled(m_tccPreferenceEnabled);
#endif
// 創(chuàng)建新的進(jìn)程,在調(diào)試窗口可以看到,在這行代碼之后,WebContent進(jìn)程被創(chuàng)建
auto processProxy = WebProcessProxy::create(*this, websiteDataStore, captivePortalMode, isPrewarmed, crossOriginMode);
// 使用websiteDataStore初始化它
// 在初始化的時(shí)候會(huì)調(diào)用process.send(Messages::WebProcess::InitializeWebProcess(parameters), 0)
// 此時(shí)webkit處于State::Launching狀態(tài),將初始化的信息加入m_pendingMessages
initializeNewWebProcess(processProxy, websiteDataStore, isPrewarmed);
m_processes.append(processProxy.copyRef());
return processProxy;
}
// WebProcessProxy.cpp
Ref<WebProcessProxy> WebProcessProxy::create(WebProcessPool& processPool, WebsiteDataStore* websiteDataStore, CaptivePortalMode captivePortalMode, IsPrewarmed isPrewarmed, CrossOriginMode crossOriginMode, ShouldLaunchProcess shouldLaunchProcess)
{
auto proxy = adoptRef(*new WebProcessProxy(processPool, websiteDataStore, isPrewarmed, crossOriginMode, captivePortalMode));
// 進(jìn)程池通過(guò)LRU算法管理
if (shouldLaunchProcess == ShouldLaunchProcess::Yes) {
if (liveProcessesLRU().size() >= s_maxProcessCount) {
for (auto& processPool : WebProcessPool::allProcessPools())
processPool->webProcessCache().clear();
if (liveProcessesLRU().size() >= s_maxProcessCount)
liveProcessesLRU().first()->requestTermination(ProcessTerminationReason::ExceededProcessCountLimit);
}
ASSERT(liveProcessesLRU().size() < s_maxProcessCount);
liveProcessesLRU().add(proxy.ptr());
// 執(zhí)行父類AuxiliaryProcessProxy的connect方法
proxy->connect();
}
return proxy;
}
// AuxiliaryProcessProxy.cpp
void AuxiliaryProcessProxy::connect()
{
ASSERT(!m_processLauncher);
m_processStart = MonotonicTime::now();
ProcessLauncher::LaunchOptions launchOptions;
getLaunchOptions(launchOptions);
m_processLauncher = ProcessLauncher::create(this, WTFMove(launchOptions));
}
// ProcessLauncher.cpp
static Ref<ProcessLauncher> create(Client* client, LaunchOptions&& launchOptions)
{
return adoptRef(*new ProcessLauncher(client, WTFMove(launchOptions)));
}
ProcessLauncher::ProcessLauncher(Client* client, LaunchOptions&& launchOptions)
: m_client(client)
, m_launchOptions(WTFMove(launchOptions))
{
tracePoint(ProcessLaunchStart);
launchProcess();
}
// ProcessLauncherCocoa.mm
void ProcessLauncher::launchProcess()
{
ASSERT(!m_xpcConnection);
const char* name;
if (!m_launchOptions.customWebContentServiceBundleIdentifier.isNull())
name = m_launchOptions.customWebContentServiceBundleIdentifier.data();
else
name = serviceName(m_launchOptions, m_client);
m_xpcConnection = adoptOSObject(xpc_connection_create(name, nullptr));
uuid_t uuid;
uuid_generate(uuid);
xpc_connection_set_oneshot_instance(m_xpcConnection.get(), uuid);
// 初始化配置
...
xpc_connection_set_bootstrap(m_xpcConnection.get(), initializationMessage.get());
if (shouldLeakBoost(m_launchOptions)) {
auto preBootstrapMessage = adoptOSObject(xpc_dictionary_create(nullptr, nullptr, 0));
xpc_dictionary_set_string(preBootstrapMessage.get(), "message-name", "pre-bootstrap");
xpc_connection_send_message(m_xpcConnection.get(), preBootstrapMessage.get());
}
// 端口回調(diào)設(shè)置
...
auto bootstrapMessage = adoptOSObject(xpc_dictionary_create(nullptr, nullptr, 0));
// bootstrapMessage參數(shù)設(shè)置
...
auto sdkBehaviors = sdkAlignedBehaviors();
xpc_dictionary_set_data(bootstrapMessage.get(), "client-sdk-aligned-behaviors", sdkBehaviors.storage(), sdkBehaviors.storageLengthInBytes());
auto extraInitializationData = adoptOSObject(xpc_dictionary_create(nullptr, nullptr, 0));
for (const auto& keyValuePair : m_launchOptions.extraInitializationData)
xpc_dictionary_set_string(extraInitializationData.get(), keyValuePair.key.utf8().data(), keyValuePair.value.utf8().data());
xpc_dictionary_set_value(bootstrapMessage.get(), "extra-initialization-data", extraInitializationData.get());
// 出錯(cuò)的回調(diào)
auto errorHandlerImpl = [weakProcessLauncher = WeakPtr { *this }, listeningPort, logName = CString(name)] (xpc_object_t event) {
// 一些錯(cuò)誤判斷,后面是出錯(cuò)之后的處理
...
// We failed to launch. Release the send right.
deallocateSendRightSafely(listeningPort);
// And the receive right.
mach_port_mod_refs(mach_task_self(), listeningPort, MACH_PORT_RIGHT_RECEIVE, -1);
if (processLauncher->m_xpcConnection)
xpc_connection_cancel(processLauncher->m_xpcConnection.get());
processLauncher->m_xpcConnection = nullptr;
processLauncher->didFinishLaunchingProcess(0, IPC::Connection::Identifier());
};
// 事件回調(diào)
auto eventHandler = [errorHandlerImpl = WTFMove(errorHandlerImpl), eventHandler = m_client->xpcEventHandler()] (xpc_object_t event) mutable {
if (!event || xpc_get_type(event) == XPC_TYPE_ERROR) {
RunLoop::main().dispatch([errorHandlerImpl = WTFMove(errorHandlerImpl), event = OSObjectPtr(event)] {
errorHandlerImpl(event.get());
});
return;
}
if (eventHandler) {
RunLoop::main().dispatch([eventHandler = eventHandler, event = OSObjectPtr(event)] {
eventHandler->handleXPCEvent(event.get());
});
}
};
xpc_connection_set_event_handler(m_xpcConnection.get(), eventHandler);
xpc_connection_resume(m_xpcConnection.get());
if (UNLIKELY(m_launchOptions.shouldMakeProcessLaunchFailForTesting)) {
eventHandler(nullptr);
return;
}
// 引入計(jì)數(shù)器
ref();
// xpc發(fā)消息并關(guān)聯(lián)進(jìn)程,這行代碼執(zhí)行后WebKit.WebContent進(jìn)程被創(chuàng)建,并執(zhí)行初始化
xpc_connection_send_message_with_reply(m_xpcConnection.get(), bootstrapMessage.get(), dispatch_get_main_queue(), ^(xpc_object_t reply) {
// Errors are handled in the event handler.
// It is possible for this block to be called after the error event handler, in which case we're no longer
// launching and we already took care of cleaning things up.
if (isLaunching() && xpc_get_type(reply) != XPC_TYPE_ERROR) {
ASSERT(xpc_get_type(reply) == XPC_TYPE_DICTIONARY);
ASSERT(!strcmp(xpc_dictionary_get_string(reply, "message-name"), "process-finished-launching"));
#if ASSERT_ENABLED
mach_port_urefs_t sendRightCount = 0;
mach_port_get_refs(mach_task_self(), listeningPort, MACH_PORT_RIGHT_SEND, &sendRightCount);
ASSERT(sendRightCount >= 1);
#endif
deallocateSendRightSafely(listeningPort);
if (!m_xpcConnection) {
// The process was terminated.
didFinishLaunchingProcess(0, IPC::Connection::Identifier());
return;
}
// The process has finished launching, grab the pid from the connection.
pid_t processIdentifier = xpc_connection_get_pid(m_xpcConnection.get());
didFinishLaunchingProcess(processIdentifier, IPC::Connection::Identifier(listeningPort, m_xpcConnection));
m_xpcConnection = nullptr;
}
deref();
});
}
上面引入了XPC,官方解釋:
The XPC Services API provides a lightweight mechanism for basic interprocess communication at the libSystem level. It allows you to create lightweight helper tools, called XPC services, that perform work on behalf of your app.
在WKMain.mm中執(zhí)行WebKit::XPCServiceMain(argc, argv)方法:
int XPCServiceMain(int, const char**)
{
...
// 調(diào)用WTF初始化主線程
WTF::initializeMainThread();
auto bootstrap = adoptOSObject(xpc_copy_bootstrap());
if (bootstrap) {
#if PLATFORM(IOS_FAMILY)
auto containerEnvironmentVariables = xpc_dictionary_get_value(bootstrap.get(), "ContainerEnvironmentVariables");
xpc_dictionary_apply(containerEnvironmentVariables, ^(const char *key, xpc_object_t value) {
setenv(key, xpc_string_get_string_ptr(value), 1);
return true;
});
#endif
...
}
xpc_main(XPCServiceEventHandler);
return 0;
}
上面的部分后面再補(bǔ)齊,先回到wkwebivew的加載流程,從下面這行代碼開(kāi)始::
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://now.qq.com/"]]];
點(diǎn)擊跳轉(zhuǎn)WebKit源碼中的WKWebView的loadrequest方法,加上斷點(diǎn)。
#define THROW_IF_SUSPENDED if (UNLIKELY(_page && _page->isSuspended())) \
[NSException raise:NSInternalInconsistencyException format:@"The WKWebView is suspended"]
- (WKNavigation *)loadRequest:(NSURLRequest *)request
{
// 這里是判斷是否有page(WebKit::WebPageProxy*)以及page是否停止
THROW_IF_SUSPENDED;
if (_page->isServiceWorkerPage())
[NSException raise:NSInternalInconsistencyException format:@"The WKWebView was used to load a service worker"];
return wrapper(_page->loadRequest(request));
}
// 在WebCore中Page.h
// m_isServiceWorkerPage在執(zhí)行l(wèi)oadData/loadServiceWorker的時(shí)候?yàn)閠rue
bool isServiceWorkerPage() const { return m_isServiceWorkerPage; }
接下來(lái)到WebPageProxy查看loadRequest方法
RefPtr<API::Navigation> WebPageProxy::loadRequest(ResourceRequest&& request, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy, API::Object* userData)
{
if (m_isClosed)
return nullptr;
WEBPAGEPROXY_RELEASE_LOG(Loading, "loadRequest:");
if (!hasRunningProcess())
launchProcess(RegistrableDomain { request.url() }, ProcessLaunchReason::InitialProcess);
// 創(chuàng)建了Ref<API::Navigation>這個(gè),后面看下是干什么用的
auto navigation = m_navigationState->createLoadRequestNavigation(ResourceRequest(request), m_backForwardList->currentItem());
if (shouldForceForegroundPriorityForClientNavigation())
navigation->setClientNavigationActivity(process().throttler().foregroundActivity("Client navigation"_s));
#if PLATFORM(COCOA)
setLastNavigationWasAppInitiated(request);
#endif
loadRequestWithNavigationShared(m_process.copyRef(), m_webPageID, navigation.get(), WTFMove(request), shouldOpenExternalURLsPolicy, userData, ShouldTreatAsContinuingLoad::No, isNavigatingToAppBoundDomain());
return navigation;
}
void WebPageProxy::loadRequestWithNavigationShared(Ref<WebProcessProxy>&& process, WebCore::PageIdentifier webPageID, API::Navigation& navigation, ResourceRequest&& request, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy, API::Object* userData, ShouldTreatAsContinuingLoad shouldTreatAsContinuingLoad, std::optional<NavigatingToAppBoundDomain> isNavigatingToAppBoundDomain, std::optional<WebsitePoliciesData>&& websitePolicies, std::optional<NetworkResourceLoadIdentifier> existingNetworkResourceLoadIdentifierToResume)
{
ASSERT(!m_isClosed);
WEBPAGEPROXY_RELEASE_LOG(Loading, "loadRequestWithNavigationShared:");
auto transaction = m_pageLoadState.transaction();
auto url = request.url();
if (shouldTreatAsContinuingLoad == ShouldTreatAsContinuingLoad::No)
m_pageLoadState.setPendingAPIRequest(transaction, { navigation.navigationID(), url.string() });
// 構(gòu)造用來(lái)IPC通信的結(jié)構(gòu)體,有encode和decode方法,這里賦值
LoadParameters loadParameters;
loadParameters.navigationID = navigation.navigationID();
loadParameters.request = WTFMove(request);
loadParameters.shouldOpenExternalURLsPolicy = shouldOpenExternalURLsPolicy;
loadParameters.userData = UserData(process->transformObjectsToHandles(userData).get());
loadParameters.shouldTreatAsContinuingLoad = shouldTreatAsContinuingLoad;
loadParameters.websitePolicies = WTFMove(websitePolicies);
loadParameters.lockHistory = navigation.lockHistory();
loadParameters.lockBackForwardList = navigation.lockBackForwardList();
loadParameters.clientRedirectSourceForHistory = navigation.clientRedirectSourceForHistory();
loadParameters.effectiveSandboxFlags = navigation.effectiveSandboxFlags();
loadParameters.isNavigatingToAppBoundDomain = isNavigatingToAppBoundDomain;
loadParameters.existingNetworkResourceLoadIdentifierToResume = existingNetworkResourceLoadIdentifierToResume;
#if ENABLE(PUBLIC_SUFFIX_LIST)
loadParameters.topPrivatelyControlledDomain = WebCore::topPrivatelyControlledDomain(loadParameters.request.url().host().toString());
#endif
// 經(jīng)過(guò)一堆判斷之后初始化了sandboxExtensionHandle
maybeInitializeSandboxExtensionHandle(process, url, m_pageLoadState.resourceDirectoryURL(), loadParameters.sandboxExtensionHandle);
addPlatformLoadParameters(process, loadParameters);
if (shouldTreatAsContinuingLoad == ShouldTreatAsContinuingLoad::No)
// 給NetworkProcess發(fā)送ipc消息
preconnectTo(url, predictedUserAgentForRequest(loadParameters.request));
navigation.setIsLoadedWithNavigationShared(true);
process->markProcessAsRecentlyUsed();
// 這里打包數(shù)據(jù)之后發(fā)送出去
if (!process->isLaunching() || !url.isLocalFile())
process->send(Messages::WebPage::LoadRequest(loadParameters), webPageID);
else
process->send(Messages::WebPage::LoadRequestWaitingForProcessLaunch(loadParameters, m_pageLoadState.resourceDirectoryURL(), m_identifier, true), webPageID);
process->startResponsivenessTimer();
}
void NetworkProcessProxy::preconnectTo(PAL::SessionID sessionID, WebPageProxyIdentifier webPageProxyID, WebCore::PageIdentifier webPageID, const URL& url, const String& userAgent, WebCore::StoredCredentialsPolicy storedCredentialsPolicy, std::optional<NavigatingToAppBoundDomain> isNavigatingToAppBoundDomain, LastNavigationWasAppInitiated lastNavigationWasAppInitiated)
{
if (!url.isValid() || !url.protocolIsInHTTPFamily())
return;
send(Messages::NetworkProcess::PreconnectTo(sessionID, webPageProxyID, webPageID, url, userAgent, storedCredentialsPolicy, isNavigatingToAppBoundDomain, lastNavigationWasAppInitiated), 0);
}
// WebProcessProxy中并未實(shí)現(xiàn)send方法,send方法在其父類AuxiliaryProcessProxy中
template<typename T>
bool AuxiliaryProcessProxy::send(T&& message, uint64_t destinationID, OptionSet<IPC::SendOption> sendOptions)
{
static_assert(!T::isSync, "Async message expected");
auto encoder = makeUniqueRef<IPC::Encoder>(T::name(), destinationID);
encoder.get() << message.arguments();
return sendMessage(WTFMove(encoder), sendOptions);
}
bool AuxiliaryProcessProxy::sendMessage(UniqueRef<IPC::Encoder>&& encoder, OptionSet<IPC::SendOption> sendOptions, std::optional<std::pair<CompletionHandler<void(IPC::Decoder*)>, uint64_t>>&& asyncReplyInfo, ShouldStartProcessThrottlerActivity shouldStartProcessThrottlerActivity)
{
// FIXME: We should turn this into a RELEASE_ASSERT().
ASSERT(isMainRunLoop());
if (!isMainRunLoop()) {
callOnMainRunLoop([protectedThis = Ref { *this }, encoder = WTFMove(encoder), sendOptions, asyncReplyInfo = WTFMove(asyncReplyInfo), shouldStartProcessThrottlerActivity]() mutable {
protectedThis->sendMessage(WTFMove(encoder), sendOptions, WTFMove(asyncReplyInfo), shouldStartProcessThrottlerActivity);
});
return true;
}
if (asyncReplyInfo && canSendMessage() && shouldStartProcessThrottlerActivity == ShouldStartProcessThrottlerActivity::Yes) {
auto completionHandler = std::exchange(asyncReplyInfo->first, nullptr);
asyncReplyInfo->first = [activity = throttler().backgroundActivity({ }), completionHandler = WTFMove(completionHandler)](IPC::Decoder* decoder) mutable {
completionHandler(decoder);
};
}
switch (state()) {
// 在此之前,初始化WKContentView,WebPageProxy,WebProcessProxy等等,以及setDelegate,addUserScript,addScriptMessageHandler等等都會(huì)一直往m_pendingMessages里面塞消息
case State::Launching:
// If we're waiting for the child process to launch, we need to stash away the messages so we can send them once we have a connection.
m_pendingMessages.append({ WTFMove(encoder), sendOptions, WTFMove(asyncReplyInfo) });
return true;
case State::Running:
if (asyncReplyInfo)
IPC::addAsyncReplyHandler(*connection(), asyncReplyInfo->second, std::exchange(asyncReplyInfo->first, nullptr));
if (connection()->sendMessage(WTFMove(encoder), sendOptions))
return true;
break;
case State::Terminated:
break;
}
if (asyncReplyInfo && asyncReplyInfo->first) {
RunLoop::current().dispatch([completionHandler = WTFMove(asyncReplyInfo->first)]() mutable {
completionHandler(nullptr);
});
}
return false;
}
AuxiliaryProcessProxy::State AuxiliaryProcessProxy::state() const
{
if (m_processLauncher && m_processLauncher->isLaunching())
return AuxiliaryProcessProxy::State::Launching;
if (!m_connection)
return AuxiliaryProcessProxy::State::Terminated;
return AuxiliaryProcessProxy::State::Running;
}
在loadrequest的最后依舊是在m_pendingMessages中添加消息,從state()方法可以看到,當(dāng)m_processLauncher不在isLaunching狀態(tài)時(shí),且m_connection存在,代表進(jìn)入running狀態(tài),m_connection賦值的地方,看著是不是很眼熟,這個(gè)就是前面xpc調(diào)用xpc_connection_send_message_with_reply方法中回調(diào)的方法,loadrequest之后,等待xpc的回調(diào)成功之后,再將保存的數(shù)據(jù)發(fā)送過(guò)去:
void AuxiliaryProcessProxy::didFinishLaunching(ProcessLauncher*, IPC::Connection::Identifier connectionIdentifier)
{
ASSERT(!m_connection);
ASSERT(isMainRunLoop());
auto launchTime = MonotonicTime::now() - m_processStart;
if (launchTime > 1_s)
RELEASE_LOG_FAULT(Process, "%s process (%p) took %f seconds to launch", processName().characters(), this, launchTime.value());
if (!IPC::Connection::identifierIsValid(connectionIdentifier))
return;
m_connection = IPC::Connection::createServerConnection(connectionIdentifier, *this);
connectionWillOpen(*m_connection);
m_connection->open();
for (auto&& pendingMessage : std::exchange(m_pendingMessages, { })) {
if (!shouldSendPendingMessage(pendingMessage))
continue;
if (pendingMessage.asyncReplyInfo)
IPC::addAsyncReplyHandler(*connection(), pendingMessage.asyncReplyInfo->second, WTFMove(pendingMessage.asyncReplyInfo->first));
m_connection->sendMessage(WTFMove(pendingMessage.encoder), pendingMessage.sendOptions);
}
}
回到WebProcessProxy的send方法上,它發(fā)送了Messages::WebPage::LoadRequest消息,找到接受消息的地方,在WebPage上,此時(shí)來(lái)到了webcontent進(jìn)程
// WebPage.cpp
void WebPage::loadRequest(LoadParameters&& loadParameters)
{
// 初始化
...
corePage()->userInputBridge().loadRequest(WTFMove(frameLoadRequest));
...
}
// UserInputBridge.cpp
void UserInputBridge::loadRequest(FrameLoadRequest&& request, InputSource)
{
#if ENABLE(WEB_AUTHN)
m_page.authenticatorCoordinator().resetUserGestureRequirement();
#endif
Ref(m_page.mainFrame())->loader().load(WTFMove(request));
}
上面引入Frame與FrameLoader類



