chromium 55 URL 加載流程

   瀏覽器從界面上看都很簡單,輸入一個地址後就可以跳轉,今天這裏先整理chromium AndroidWebView.apk中從輸入URL,到資源加載的流程,暫不涉及渲染,渲染後續進行介紹。從Java層往c++層逐步分析。

  AndroidWebView.apk啓動後有個地址輸入欄,如果輸入地址按下確定鍵會調用到loadUrl進行url加載。

    private void initializeUrlField() {
        mUrlTextView = (EditText) findViewById(R.id.url);
        mUrlTextView.setOnEditorActionListener(new OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if ((actionId != EditorInfo.IME_ACTION_GO) && (event == null
                        || event.getKeyCode() != KeyEvent.KEYCODE_ENTER
                        || event.getAction() != KeyEvent.ACTION_DOWN)) {
                    return false;
                }
                ......
                mAwTestContainerView.getAwContents().loadUrl(url);
                mUrlTextView.clearFocus();
                setKeyboardVisibilityForUrl(false);
                mAwTestContainerView.requestFocus();
                return true;
            }
        });
      ......
    }

  代碼路徑:./android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java。

  NavigationController.java 只定義了loadUrl接口,實現是在NavigationControllerImpl.java中,其實現是調用jni接口。

    public void loadUrl(LoadUrlParams params) {
        //Log.e("NavigationControllerImpl.java", "loadUrl Url()"+params.getUrl());
        //Exception e = new Exception("Chrome_debug");
        //e.printStackTrace();
        if (mNativeNavigationControllerAndroid != 0) {
            nativeLoadUrl(mNativeNavigationControllerAndroid, params.getUrl(),
                    params.getLoadUrlType(), params.getTransitionType(),
                    params.getReferrer() != null ? params.getReferrer().getUrl() : null,
                    params.getReferrer() != null ? params.getReferrer().getPolicy() : 0,
                    params.getUserAgentOverrideOption(), params.getExtraHeadersString(),
                    params.getPostData(), params.getBaseUrl(), params.getVirtualUrlForDataUrl(),
                    params.getDataUrlAsString(), params.getCanLoadLocalResources(),
                    params.getIsRendererInitiated(), params.getShouldReplaceCurrentEntry());
        }
    }

代碼目錄:./content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java

nativeLoadUrl 調用到的是navigation_controller_android.cc LoadURL函數

void NavigationControllerAndroid::LoadUrl(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj,
    const JavaParamRef<jstring>& url,
    jint load_url_type,
    jint transition_type,
    const JavaParamRef<jstring>& j_referrer_url,
    jint referrer_policy,
    jint ua_override_option,
    const JavaParamRef<jstring>& extra_headers,
    const JavaParamRef<jobject>& j_post_data,
    const JavaParamRef<jstring>& base_url_for_data_url,
    const JavaParamRef<jstring>& virtual_url_for_data_url,
    const JavaParamRef<jstring>& data_url_as_string,
    jboolean can_load_local_resources,
    jboolean is_renderer_initiated,
    jboolean should_replace_current_entry) {
  ......
  navigation_controller_->LoadURLWithParams(params);
}

代碼路徑:./content/browser/frame_host/navigation_controller_android.cc

LoadURLWithParams調用LoadEntry

void NavigationControllerImpl::LoadURLWithParams(const LoadURLParams& params) {
  ......
  LoadEntry(std::move(entry));
}

LoadEntry接着往下的調用關係如下圖,如果有興趣,可以在render_frame_host_impl.cc Navigate函數中添加堆棧打印,也可以打印出想過堆棧,進而獲取調用關係:

接着看看Navigate函數的實現,這裏需要關注的是navigate調用了SendNavigateMessage:

void RenderFrameHostImpl::Navigate(
    const CommonNavigationParams& common_params,
    const StartNavigationParams& start_params,
    const RequestNavigationParams& request_params) {
......
  if (navigations_suspended_) {
    // This may replace an existing set of params, if this is a pending RFH that
    // is navigated twice consecutively.
    suspended_nav_params_.reset(
        new NavigationParams(common_params, start_params, request_params));
  } else {
    // Get back to a clean state, in case we start a new navigation without
    // completing an unload handler.
    ResetWaitingState();
	//YH_LOGD("<render_frame_host_impl.cc>[%s][%d]\n",__FUNCTION__,__LINE__);
    SendNavigateMessage(common_params, start_params, request_params);
  }
......
}

SendNavigateMessage 的實現如下,是發送一個FrameMsg_Navigate msg,是browser線程發送,render線程接收,資源的加載在render線程中進行實現,可以在render線程中添加堆棧,跟蹤資源加載流程:

void RenderFrameHostImpl::SendNavigateMessage(
    const CommonNavigationParams& common_params,
    const StartNavigationParams& start_params,
    const RequestNavigationParams& request_params) {
......   
  RenderFrameDevToolsAgentHost::OnBeforeNavigation(
      frame_tree_node_->current_frame_host(), this);
  Send(new FrameMsg_Navigate(
      routing_id_, common_params, start_params, request_params));
}

看看render_frame_impl.cc文件中監聽了FrameMsg_Navigate消息,收到FrameMsg_Navigate消息執行OnNavigate函數

bool RenderFrameImpl::OnMessageReceived(const IPC::Message& msg) {
......
  IPC_BEGIN_MESSAGE_MAP(RenderFrameImpl, msg)
    IPC_MESSAGE_HANDLER(FrameMsg_Navigate, OnNavigate)
......
  IPC_END_MESSAGE_MAP()

  return handled;
}

 OnNavigate接着往下的調用關係如下圖,

來看看StartAsync的實現,又是發送一個ResourceHostMsg_RequestResource給browser線程進行處理:

int ResourceDispatcher::StartAsync(
    std::unique_ptr<ResourceRequest> request,
    int routing_id,
    scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner,
    const GURL& frame_origin,
    std::unique_ptr<RequestPeer> peer,
    blink::WebURLRequest::LoadingIPCType ipc_type,
    mojom::URLLoaderFactory* url_loader_factory) {
......

  if (ipc_type == blink::WebURLRequest::LoadingIPCType::Mojo) {
    std::unique_ptr<URLLoaderClientImpl> client(
        new URLLoaderClientImpl(request_id, this, main_thread_task_runner_));
    mojom::URLLoaderPtr url_loader;
    url_loader_factory->CreateLoaderAndStart(
        GetProxy(&url_loader), routing_id, request_id, *request,
        client->CreateInterfacePtrAndBind());
    pending_requests_[request_id]->url_loader = std::move(url_loader);
    pending_requests_[request_id]->url_loader_client = std::move(client);
  } else {
    //base::debug::StackTrace();
    message_sender_->Send(
        new ResourceHostMsg_RequestResource(routing_id, request_id, *request));
  }

  return request_id;
}

resource_dispatcher_host_impl.cc監聽ResourceHostMsg_RequestResource,調用OnRequestResource,

void ResourceDispatcherHostImpl::OnRequestResource(
    int routing_id,
    int request_id,
    const ResourceRequest& request_data) {
  OnRequestResourceInternal(routing_id, request_id, request_data, nullptr,
                            nullptr);
}
void ResourceDispatcherHostImpl::OnRequestResourceInternal(
    int routing_id,
    int request_id,
    const ResourceRequest& request_data,
    mojo::InterfaceRequest<mojom::URLLoader> mojo_request,
    mojom::URLLoaderClientPtr url_loader_client) {
......
  BeginRequest(request_id, request_data, NULL, routing_id,
               std::move(mojo_request), std::move(url_loader_client));
}
void ResourceDispatcherHostImpl::BeginRequest(
    int request_id,
    const ResourceRequest& request_data,
    IPC::Message* sync_result,  // only valid for sync
    int route_id,
    mojo::InterfaceRequest<mojom::URLLoader> mojo_request,
    mojom::URLLoaderClientPtr url_loader_client) {
......
  ContinuePendingBeginRequest(request_id, request_data, sync_result, route_id,
                              headers, std::move(mojo_request),
                              std::move(url_loader_client), true, 0);
}
void ResourceDispatcherHostImpl::ContinuePendingBeginRequest(
    int request_id,
    const ResourceRequest& request_data,
    IPC::Message* sync_result,  // only valid for sync
    int route_id,
    const net::HttpRequestHeaders& headers,
    mojo::InterfaceRequest<mojom::URLLoader> mojo_request,
    mojom::URLLoaderClientPtr url_loader_client,
    bool continue_request,
    int error_code) {
......  
    BeginRequestInternal(std::move(new_request), std::move(handler));
}
void ResourceDispatcherHostImpl::BeginRequestInternal(
    std::unique_ptr<net::URLRequest> request,
    std::unique_ptr<ResourceHandler> handler) {
......
  StartLoading(info, std::move(loader));
}
void ResourceDispatcherHostImpl::StartLoading(
......
  loader_ptr->StartRequest();
}
void ResourceLoader::StartRequest() {
......
  if (defer_start) {
    deferred_stage_ = DEFERRED_START;
  } else {
    StartRequestInternal();
  }
}
void ResourceLoader::StartRequestInternal() {
......
  request_->Start();
  delegate_->DidStartRequest(this);
}

Web服務器響應了請求之後,Chromium的Net模塊會調用ResourceLoader類的成員函數OnResponseStarted,如果請求狀態返回的是OK的(eg 200),則調用StartReading

void ResourceLoader::OnResponseStarted(net::URLRequest* unused) {
......
  if (request_->status().is_success())
    StartReading(false);  // Read the first chunk.
  else
    ResponseCompleted();
}

StartReading 調用ReadMore讀取Web服務器返回來的數據,保存在本地變量bytes_read

void ResourceLoader::StartReading(bool is_continuation) {
  int bytes_read = 0;
  ReadMore(&bytes_read);
......
  if (!is_continuation || bytes_read <= 0) {
    OnReadCompleted(request_.get(), bytes_read);
  } else {
    // Else, trigger OnReadCompleted asynchronously to avoid starving the IO
    // thread in case the URLRequest can provide data synchronously.
    base::ThreadTaskRunnerHandle::Get()->PostTask(
        FROM_HERE,
        base::Bind(&ResourceLoader::OnReadCompleted,
                   weak_ptr_factory_.GetWeakPtr(), request_.get(), bytes_read));
  }
}
void ResourceLoader::ReadMore(int* bytes_read) {
......
  request_->Read(buf.get(), buf_size, bytes_read);

  // No need to check the return value here as we'll detect errors by
  // inspecting the URLRequest's status.
}

然後我默默的翻翻羅昇陽老師的博客,在EnsureResourceBufferIsInitialized函數中添加了堆棧打印,因爲EnsureResourceBufferIsInitialized首先檢查成員變量buffer_是否指向了一個ResourceBuffer對象,並且這個ResourceBuffer對象描述的共享內存是否已經創建。URL的加載是Render向Browser發送一個類型爲ResourceHostMsg_RequestResource的IPC消息,Browser收到這個IPC消息之後,就會通過HTTP協議請求Web服務器將網頁的內容返回來。請求得到響應後,Browser就會創建一塊共享內存,並且通過一個類型爲ResourceMsg_SetDataBuffer的IPC消息將這塊共享內存傳遞給Render進程的。

URL加載看着很繞,因爲不通線程IPC通信,你如果沒先了解下它的msg,就會看着很懵,如果只是看net 部分請求都還好。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章