2012-04-03 03:34:16 +02:00
|
|
|
// Copyright (c) 2012 The Chromium Embedded Framework Authors.
|
|
|
|
// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
#include "libcef/renderer/browser_impl.h"
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "libcef/common/cef_messages.h"
|
|
|
|
#include "libcef/common/content_client.h"
|
|
|
|
#include "libcef/common/process_message_impl.h"
|
2015-12-01 19:22:28 +01:00
|
|
|
#include "libcef/common/request_impl.h"
|
2012-05-18 23:18:01 +02:00
|
|
|
#include "libcef/common/response_manager.h"
|
2018-04-19 17:44:42 +02:00
|
|
|
#include "libcef/renderer/blink_glue.h"
|
2012-04-03 03:34:16 +02:00
|
|
|
#include "libcef/renderer/content_renderer_client.h"
|
2012-06-11 17:52:49 +02:00
|
|
|
#include "libcef/renderer/dom_document_impl.h"
|
2017-07-06 22:39:37 +02:00
|
|
|
#include "libcef/renderer/render_frame_util.h"
|
2012-04-03 03:34:16 +02:00
|
|
|
#include "libcef/renderer/thread_util.h"
|
|
|
|
|
2013-06-22 04:06:32 +02:00
|
|
|
#include "base/strings/string16.h"
|
|
|
|
#include "base/strings/string_util.h"
|
|
|
|
#include "base/strings/utf_string_conversions.h"
|
2012-04-03 03:34:16 +02:00
|
|
|
#include "content/public/renderer/document_state.h"
|
|
|
|
#include "content/public/renderer/navigation_state.h"
|
|
|
|
#include "content/public/renderer/render_view.h"
|
2015-03-19 23:06:16 +01:00
|
|
|
#include "content/renderer/navigation_state_impl.h"
|
2018-04-19 17:44:42 +02:00
|
|
|
#include "third_party/blink/public/platform/web_string.h"
|
|
|
|
#include "third_party/blink/public/platform/web_url.h"
|
|
|
|
#include "third_party/blink/public/platform/web_url_error.h"
|
|
|
|
#include "third_party/blink/public/platform/web_url_response.h"
|
|
|
|
#include "third_party/blink/public/web/web_document.h"
|
|
|
|
#include "third_party/blink/public/web/web_frame.h"
|
|
|
|
#include "third_party/blink/public/web/web_frame_content_dumper.h"
|
|
|
|
#include "third_party/blink/public/web/web_local_frame.h"
|
|
|
|
#include "third_party/blink/public/web/web_node.h"
|
|
|
|
#include "third_party/blink/public/web/web_script_source.h"
|
|
|
|
#include "third_party/blink/public/web/web_security_policy.h"
|
|
|
|
#include "third_party/blink/public/web/web_view.h"
|
2012-04-03 03:34:16 +02:00
|
|
|
|
2013-11-08 22:28:56 +01:00
|
|
|
using blink::WebFrame;
|
2017-07-27 01:19:27 +02:00
|
|
|
using blink::WebLocalFrame;
|
2013-11-08 22:28:56 +01:00
|
|
|
using blink::WebScriptSource;
|
|
|
|
using blink::WebString;
|
|
|
|
using blink::WebURL;
|
|
|
|
using blink::WebView;
|
2012-04-03 03:34:16 +02:00
|
|
|
|
|
|
|
// CefBrowserImpl static methods.
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// static
|
|
|
|
CefRefPtr<CefBrowserImpl> CefBrowserImpl::GetBrowserForView(
|
|
|
|
content::RenderView* view) {
|
|
|
|
return CefContentRendererClient::Get()->GetBrowserForView(view);
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
CefRefPtr<CefBrowserImpl> CefBrowserImpl::GetBrowserForMainFrame(
|
2013-11-08 22:28:56 +01:00
|
|
|
blink::WebFrame* frame) {
|
2012-04-03 03:34:16 +02:00
|
|
|
return CefContentRendererClient::Get()->GetBrowserForMainFrame(frame);
|
|
|
|
}
|
|
|
|
|
|
|
|
// CefBrowser methods.
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
CefRefPtr<CefBrowserHost> CefBrowserImpl::GetHost() {
|
|
|
|
NOTREACHED() << "GetHost cannot be called from the render process";
|
2016-11-23 21:54:29 +01:00
|
|
|
return nullptr;
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CefBrowserImpl::CanGoBack() {
|
|
|
|
CEF_REQUIRE_RT_RETURN(false);
|
|
|
|
|
2018-04-19 17:44:42 +02:00
|
|
|
return blink_glue::CanGoBack(render_view()->GetWebView());
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefBrowserImpl::GoBack() {
|
|
|
|
CEF_REQUIRE_RT_RETURN_VOID();
|
|
|
|
|
2018-04-19 17:44:42 +02:00
|
|
|
blink_glue::GoBack(render_view()->GetWebView());
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CefBrowserImpl::CanGoForward() {
|
|
|
|
CEF_REQUIRE_RT_RETURN(false);
|
|
|
|
|
2018-04-19 17:44:42 +02:00
|
|
|
return blink_glue::CanGoForward(render_view()->GetWebView());
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefBrowserImpl::GoForward() {
|
|
|
|
CEF_REQUIRE_RT_RETURN_VOID();
|
|
|
|
|
2018-04-19 17:44:42 +02:00
|
|
|
blink_glue::GoForward(render_view()->GetWebView());
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CefBrowserImpl::IsLoading() {
|
|
|
|
CEF_REQUIRE_RT_RETURN(false);
|
|
|
|
|
2014-09-27 01:48:19 +02:00
|
|
|
if (render_view()->GetWebView()) {
|
2017-04-20 21:28:17 +02:00
|
|
|
blink::WebFrame* main_frame = render_view()->GetWebView()->MainFrame();
|
2014-09-27 01:48:19 +02:00
|
|
|
if (main_frame)
|
2017-04-20 21:28:17 +02:00
|
|
|
return main_frame->ToWebLocalFrame()->IsLoading();
|
2014-09-27 01:48:19 +02:00
|
|
|
}
|
2012-04-03 03:34:16 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefBrowserImpl::Reload() {
|
|
|
|
CEF_REQUIRE_RT_RETURN_VOID();
|
|
|
|
|
2017-07-27 01:19:27 +02:00
|
|
|
if (render_view()->GetWebView()) {
|
|
|
|
WebFrame* main_frame = render_view()->GetWebView()->MainFrame();
|
|
|
|
if (main_frame && main_frame->IsWebLocalFrame()) {
|
|
|
|
main_frame->ToWebLocalFrame()->Reload(blink::WebFrameLoadType::kReload);
|
|
|
|
}
|
2017-01-23 18:36:54 +01:00
|
|
|
}
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefBrowserImpl::ReloadIgnoreCache() {
|
|
|
|
CEF_REQUIRE_RT_RETURN_VOID();
|
|
|
|
|
2017-07-27 01:19:27 +02:00
|
|
|
if (render_view()->GetWebView()) {
|
|
|
|
WebFrame* main_frame = render_view()->GetWebView()->MainFrame();
|
|
|
|
if (main_frame && main_frame->IsWebLocalFrame()) {
|
|
|
|
main_frame->ToWebLocalFrame()->Reload(
|
|
|
|
blink::WebFrameLoadType::kReloadBypassingCache);
|
|
|
|
}
|
2016-04-27 22:38:52 +02:00
|
|
|
}
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefBrowserImpl::StopLoad() {
|
|
|
|
CEF_REQUIRE_RT_RETURN_VOID();
|
|
|
|
|
2017-07-27 01:19:27 +02:00
|
|
|
if (render_view()->GetWebView()) {
|
|
|
|
WebFrame* main_frame = render_view()->GetWebView()->MainFrame();
|
|
|
|
if (main_frame && main_frame->IsWebLocalFrame()) {
|
|
|
|
main_frame->ToWebLocalFrame()->StopLoading();
|
|
|
|
}
|
|
|
|
}
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int CefBrowserImpl::GetIdentifier() {
|
|
|
|
CEF_REQUIRE_RT_RETURN(0);
|
|
|
|
|
2012-12-30 12:17:49 +01:00
|
|
|
return browser_id();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CefBrowserImpl::IsSame(CefRefPtr<CefBrowser> that) {
|
|
|
|
CEF_REQUIRE_RT_RETURN(false);
|
|
|
|
|
|
|
|
CefBrowserImpl* impl = static_cast<CefBrowserImpl*>(that.get());
|
|
|
|
return (impl == this);
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CefBrowserImpl::IsPopup() {
|
|
|
|
CEF_REQUIRE_RT_RETURN(false);
|
|
|
|
|
2012-12-30 12:17:49 +01:00
|
|
|
return is_popup();
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CefBrowserImpl::HasDocument() {
|
|
|
|
CEF_REQUIRE_RT_RETURN(false);
|
|
|
|
|
2017-07-27 01:19:27 +02:00
|
|
|
if (render_view()->GetWebView()) {
|
|
|
|
WebFrame* main_frame = render_view()->GetWebView()->MainFrame();
|
|
|
|
if (main_frame && main_frame->IsWebLocalFrame()) {
|
|
|
|
return !main_frame->ToWebLocalFrame()->GetDocument().IsNull();
|
|
|
|
}
|
|
|
|
}
|
2012-04-03 03:34:16 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CefRefPtr<CefFrame> CefBrowserImpl::GetMainFrame() {
|
2016-11-23 21:54:29 +01:00
|
|
|
CEF_REQUIRE_RT_RETURN(nullptr);
|
2012-04-03 03:34:16 +02:00
|
|
|
|
2017-07-27 01:19:27 +02:00
|
|
|
if (render_view()->GetWebView()) {
|
|
|
|
WebFrame* main_frame = render_view()->GetWebView()->MainFrame();
|
|
|
|
if (main_frame && main_frame->IsWebLocalFrame()) {
|
|
|
|
return GetWebFrameImpl(main_frame->ToWebLocalFrame()).get();
|
|
|
|
}
|
|
|
|
}
|
2016-11-23 21:54:29 +01:00
|
|
|
return nullptr;
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CefRefPtr<CefFrame> CefBrowserImpl::GetFocusedFrame() {
|
2016-11-23 21:54:29 +01:00
|
|
|
CEF_REQUIRE_RT_RETURN(nullptr);
|
2012-04-03 03:34:16 +02:00
|
|
|
|
|
|
|
if (render_view()->GetWebView() &&
|
2017-04-20 21:28:17 +02:00
|
|
|
render_view()->GetWebView()->FocusedFrame()) {
|
|
|
|
return GetWebFrameImpl(render_view()->GetWebView()->FocusedFrame()).get();
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
2016-11-23 21:54:29 +01:00
|
|
|
return nullptr;
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CefRefPtr<CefFrame> CefBrowserImpl::GetFrame(int64 identifier) {
|
2016-11-23 21:54:29 +01:00
|
|
|
CEF_REQUIRE_RT_RETURN(nullptr);
|
2012-04-03 03:34:16 +02:00
|
|
|
|
|
|
|
return GetWebFrameImpl(identifier).get();
|
|
|
|
}
|
|
|
|
|
|
|
|
CefRefPtr<CefFrame> CefBrowserImpl::GetFrame(const CefString& name) {
|
2016-11-23 21:54:29 +01:00
|
|
|
CEF_REQUIRE_RT_RETURN(nullptr);
|
2012-04-03 03:34:16 +02:00
|
|
|
|
2014-09-24 17:38:11 +02:00
|
|
|
blink::WebView* web_view = render_view()->GetWebView();
|
|
|
|
if (web_view) {
|
2018-03-20 21:15:08 +01:00
|
|
|
const blink::WebString& frame_name =
|
|
|
|
blink::WebString::FromUTF16(name.ToString16());
|
2014-09-24 17:38:11 +02:00
|
|
|
// Search by assigned frame name (Frame::name).
|
2017-07-27 01:19:27 +02:00
|
|
|
WebFrame* frame = web_view->MainFrame();
|
|
|
|
if (frame && frame->IsWebLocalFrame())
|
|
|
|
frame = frame->ToWebLocalFrame()->FindFrameByName(frame_name);
|
2014-09-24 17:38:11 +02:00
|
|
|
if (!frame) {
|
|
|
|
// Search by unique frame name (Frame::uniqueName).
|
2017-04-20 21:28:17 +02:00
|
|
|
const std::string& searchname = name;
|
|
|
|
for (WebFrame* cur_frame = web_view->MainFrame(); cur_frame;
|
|
|
|
cur_frame = cur_frame->TraverseNext()) {
|
2017-07-27 01:19:27 +02:00
|
|
|
if (cur_frame->IsWebLocalFrame() &&
|
2018-03-28 19:30:15 +02:00
|
|
|
render_frame_util::GetName(cur_frame->ToWebLocalFrame()) ==
|
2017-07-27 01:19:27 +02:00
|
|
|
searchname) {
|
2017-04-20 21:28:17 +02:00
|
|
|
frame = cur_frame;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-09-24 17:38:11 +02:00
|
|
|
}
|
2017-07-27 01:19:27 +02:00
|
|
|
if (frame && frame->IsWebLocalFrame())
|
|
|
|
return GetWebFrameImpl(frame->ToWebLocalFrame()).get();
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
2016-11-23 21:54:29 +01:00
|
|
|
return nullptr;
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t CefBrowserImpl::GetFrameCount() {
|
|
|
|
CEF_REQUIRE_RT_RETURN(0);
|
|
|
|
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
if (render_view()->GetWebView()) {
|
2017-04-20 21:28:17 +02:00
|
|
|
for (WebFrame* frame = render_view()->GetWebView()->MainFrame(); frame;
|
|
|
|
frame = frame->TraverseNext()) {
|
2016-11-23 21:54:29 +01:00
|
|
|
count++;
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefBrowserImpl::GetFrameIdentifiers(std::vector<int64>& identifiers) {
|
|
|
|
CEF_REQUIRE_RT_RETURN_VOID();
|
|
|
|
|
2014-09-24 17:38:11 +02:00
|
|
|
if (identifiers.size() > 0)
|
|
|
|
identifiers.clear();
|
|
|
|
|
2012-04-03 03:34:16 +02:00
|
|
|
if (render_view()->GetWebView()) {
|
2017-04-20 21:28:17 +02:00
|
|
|
for (WebFrame* frame = render_view()->GetWebView()->MainFrame(); frame;
|
|
|
|
frame = frame->TraverseNext()) {
|
2017-07-27 01:19:27 +02:00
|
|
|
if (frame->IsWebLocalFrame())
|
|
|
|
identifiers.push_back(
|
|
|
|
render_frame_util::GetIdentifier(frame->ToWebLocalFrame()));
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefBrowserImpl::GetFrameNames(std::vector<CefString>& names) {
|
|
|
|
CEF_REQUIRE_RT_RETURN_VOID();
|
|
|
|
|
2014-09-24 17:38:11 +02:00
|
|
|
if (names.size() > 0)
|
|
|
|
names.clear();
|
|
|
|
|
2012-04-03 03:34:16 +02:00
|
|
|
if (render_view()->GetWebView()) {
|
2017-04-20 21:28:17 +02:00
|
|
|
for (WebFrame* frame = render_view()->GetWebView()->MainFrame(); frame;
|
|
|
|
frame = frame->TraverseNext()) {
|
2017-07-27 01:19:27 +02:00
|
|
|
if (frame->IsWebLocalFrame())
|
2018-03-28 19:30:15 +02:00
|
|
|
names.push_back(render_frame_util::GetName(frame->ToWebLocalFrame()));
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CefBrowserImpl::SendProcessMessage(CefProcessId target_process,
|
|
|
|
CefRefPtr<CefProcessMessage> message) {
|
|
|
|
Cef_Request_Params params;
|
|
|
|
CefProcessMessageImpl* impl =
|
|
|
|
static_cast<CefProcessMessageImpl*>(message.get());
|
|
|
|
if (impl->CopyTo(params)) {
|
2012-10-18 00:45:49 +02:00
|
|
|
return SendProcessMessage(target_process, params.name, ¶ms.arguments,
|
|
|
|
true);
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// CefBrowserImpl public methods.
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
2012-11-20 21:08:36 +01:00
|
|
|
CefBrowserImpl::CefBrowserImpl(content::RenderView* render_view,
|
|
|
|
int browser_id,
|
2014-07-01 00:30:29 +02:00
|
|
|
bool is_popup,
|
|
|
|
bool is_windowless)
|
2012-04-03 03:34:16 +02:00
|
|
|
: content::RenderViewObserver(render_view),
|
2012-12-30 12:17:49 +01:00
|
|
|
browser_id_(browser_id),
|
2012-11-20 21:08:36 +01:00
|
|
|
is_popup_(is_popup),
|
2014-09-19 21:12:44 +02:00
|
|
|
is_windowless_(is_windowless) {
|
2012-05-18 23:18:01 +02:00
|
|
|
response_manager_.reset(new CefResponseManager);
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
2017-05-17 11:29:28 +02:00
|
|
|
CefBrowserImpl::~CefBrowserImpl() {}
|
2012-04-03 03:34:16 +02:00
|
|
|
|
2014-07-16 23:27:25 +02:00
|
|
|
void CefBrowserImpl::LoadRequest(const CefMsg_LoadRequest_Params& params) {
|
|
|
|
CefRefPtr<CefFrameImpl> framePtr = GetWebFrameImpl(params.frame_id);
|
|
|
|
if (!framePtr.get())
|
|
|
|
return;
|
|
|
|
|
2017-07-27 01:19:27 +02:00
|
|
|
WebLocalFrame* web_frame = framePtr->web_frame();
|
2014-07-16 23:27:25 +02:00
|
|
|
|
2015-12-01 19:22:28 +01:00
|
|
|
blink::WebURLRequest request;
|
|
|
|
CefRequestImpl::Get(params, request);
|
2014-07-16 23:27:25 +02:00
|
|
|
|
2017-04-20 21:28:17 +02:00
|
|
|
web_frame->LoadRequest(request);
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
2012-10-18 00:45:49 +02:00
|
|
|
bool CefBrowserImpl::SendProcessMessage(CefProcessId target_process,
|
|
|
|
const std::string& name,
|
|
|
|
base::ListValue* arguments,
|
|
|
|
bool user_initiated) {
|
|
|
|
DCHECK_EQ(PID_BROWSER, target_process);
|
|
|
|
DCHECK(!name.empty());
|
|
|
|
|
|
|
|
Cef_Request_Params params;
|
|
|
|
params.name = name;
|
|
|
|
if (arguments)
|
|
|
|
params.arguments.Swap(arguments);
|
|
|
|
params.frame_id = -1;
|
|
|
|
params.user_initiated = user_initiated;
|
|
|
|
params.request_id = -1;
|
|
|
|
params.expect_response = false;
|
|
|
|
|
|
|
|
return Send(new CefHostMsg_Request(routing_id(), params));
|
|
|
|
}
|
|
|
|
|
2012-04-03 03:34:16 +02:00
|
|
|
CefRefPtr<CefFrameImpl> CefBrowserImpl::GetWebFrameImpl(
|
2017-07-27 01:19:27 +02:00
|
|
|
blink::WebLocalFrame* frame) {
|
2012-04-03 03:34:16 +02:00
|
|
|
DCHECK(frame);
|
2017-07-06 22:39:37 +02:00
|
|
|
int64_t frame_id = render_frame_util::GetIdentifier(frame);
|
2012-04-03 03:34:16 +02:00
|
|
|
|
|
|
|
// Frames are re-used between page loads. Only add the frame to the map once.
|
|
|
|
FrameMap::const_iterator it = frames_.find(frame_id);
|
|
|
|
if (it != frames_.end())
|
|
|
|
return it->second;
|
|
|
|
|
|
|
|
CefRefPtr<CefFrameImpl> framePtr(new CefFrameImpl(this, frame));
|
|
|
|
frames_.insert(std::make_pair(frame_id, framePtr));
|
|
|
|
|
2017-07-27 01:19:27 +02:00
|
|
|
const int64_t parent_id = frame->Parent() == NULL
|
2018-04-19 17:44:42 +02:00
|
|
|
? blink_glue::kInvalidFrameId
|
2017-07-27 01:19:27 +02:00
|
|
|
: frame->Parent()->IsWebLocalFrame()
|
|
|
|
? render_frame_util::GetIdentifier(
|
|
|
|
frame->Parent()->ToWebLocalFrame())
|
2018-04-19 17:44:42 +02:00
|
|
|
: blink_glue::kInvalidFrameId;
|
2017-04-20 21:28:17 +02:00
|
|
|
const base::string16& name =
|
2018-03-28 19:30:15 +02:00
|
|
|
base::UTF8ToUTF16(render_frame_util::GetName(frame));
|
2012-04-03 03:34:16 +02:00
|
|
|
|
|
|
|
// Notify the browser that the frame has been identified.
|
|
|
|
Send(new CefHostMsg_FrameIdentified(routing_id(), frame_id, parent_id, name));
|
|
|
|
|
|
|
|
return framePtr;
|
|
|
|
}
|
|
|
|
|
2016-01-06 20:20:54 +01:00
|
|
|
CefRefPtr<CefFrameImpl> CefBrowserImpl::GetWebFrameImpl(int64_t frame_id) {
|
2018-04-19 17:44:42 +02:00
|
|
|
if (frame_id == blink_glue::kInvalidFrameId) {
|
2017-07-27 01:19:27 +02:00
|
|
|
if (render_view()->GetWebView()) {
|
|
|
|
WebFrame* main_frame = render_view()->GetWebView()->MainFrame();
|
|
|
|
if (main_frame && main_frame->IsWebLocalFrame()) {
|
|
|
|
return GetWebFrameImpl(main_frame->ToWebLocalFrame());
|
|
|
|
}
|
|
|
|
}
|
2016-11-23 21:54:29 +01:00
|
|
|
return nullptr;
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check if we already know about the frame.
|
|
|
|
FrameMap::const_iterator it = frames_.find(frame_id);
|
|
|
|
if (it != frames_.end())
|
|
|
|
return it->second;
|
|
|
|
|
|
|
|
if (render_view()->GetWebView()) {
|
|
|
|
// Check if the frame exists but we don't know about it yet.
|
2017-04-20 21:28:17 +02:00
|
|
|
for (WebFrame* frame = render_view()->GetWebView()->MainFrame(); frame;
|
|
|
|
frame = frame->TraverseNext()) {
|
2017-07-27 01:19:27 +02:00
|
|
|
if (frame->IsWebLocalFrame() &&
|
|
|
|
render_frame_util::GetIdentifier(frame->ToWebLocalFrame()) ==
|
|
|
|
frame_id) {
|
|
|
|
return GetWebFrameImpl(frame->ToWebLocalFrame());
|
|
|
|
}
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-23 21:54:29 +01:00
|
|
|
return nullptr;
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
2016-01-06 20:20:54 +01:00
|
|
|
void CefBrowserImpl::AddFrameObject(int64_t frame_id,
|
2012-04-27 23:19:06 +02:00
|
|
|
CefTrackNode* tracked_object) {
|
|
|
|
CefRefPtr<CefTrackManager> manager;
|
|
|
|
|
|
|
|
if (!frame_objects_.empty()) {
|
|
|
|
FrameObjectMap::const_iterator it = frame_objects_.find(frame_id);
|
|
|
|
if (it != frame_objects_.end())
|
|
|
|
manager = it->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!manager.get()) {
|
|
|
|
manager = new CefTrackManager();
|
|
|
|
frame_objects_.insert(std::make_pair(frame_id, manager));
|
|
|
|
}
|
|
|
|
|
|
|
|
manager->Add(tracked_object);
|
|
|
|
}
|
|
|
|
|
2012-04-03 03:34:16 +02:00
|
|
|
// RenderViewObserver methods.
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void CefBrowserImpl::OnDestruct() {
|
|
|
|
// Notify that the browser window has been destroyed.
|
|
|
|
CefRefPtr<CefApp> app = CefContentClient::Get()->application();
|
|
|
|
if (app.get()) {
|
2017-05-17 11:29:28 +02:00
|
|
|
CefRefPtr<CefRenderProcessHandler> handler = app->GetRenderProcessHandler();
|
2012-04-03 03:34:16 +02:00
|
|
|
if (handler.get())
|
|
|
|
handler->OnBrowserDestroyed(this);
|
|
|
|
}
|
|
|
|
|
2016-11-23 21:54:29 +01:00
|
|
|
response_manager_.reset();
|
2012-05-18 23:18:01 +02:00
|
|
|
|
2012-04-03 03:34:16 +02:00
|
|
|
CefContentRendererClient::Get()->OnBrowserDestroyed(this);
|
|
|
|
}
|
|
|
|
|
2013-09-13 01:36:54 +02:00
|
|
|
void CefBrowserImpl::DidStopLoading() {
|
|
|
|
OnLoadingStateChange(false);
|
|
|
|
}
|
|
|
|
|
2014-04-30 19:14:40 +02:00
|
|
|
void CefBrowserImpl::DidFinishLoad(blink::WebLocalFrame* frame) {
|
2017-09-06 23:40:58 +02:00
|
|
|
blink::WebDocumentLoader* dl = frame->GetDocumentLoader();
|
2017-07-06 22:39:37 +02:00
|
|
|
Send(new CefHostMsg_DidFinishLoad(routing_id(),
|
|
|
|
render_frame_util::GetIdentifier(frame),
|
2017-09-06 23:40:58 +02:00
|
|
|
dl->GetRequest().Url(), !frame->Parent(),
|
|
|
|
dl->GetResponse().HttpStatusCode()));
|
2013-09-13 01:36:54 +02:00
|
|
|
OnLoadEnd(frame);
|
2012-11-21 23:11:22 +01:00
|
|
|
}
|
|
|
|
|
2014-04-30 19:14:40 +02:00
|
|
|
void CefBrowserImpl::DidStartProvisionalLoad(blink::WebLocalFrame* frame) {
|
2012-04-03 03:34:16 +02:00
|
|
|
// Send the frame creation notification if necessary.
|
|
|
|
GetWebFrameImpl(frame);
|
|
|
|
}
|
|
|
|
|
2017-05-17 11:29:28 +02:00
|
|
|
void CefBrowserImpl::DidFailProvisionalLoad(blink::WebLocalFrame* frame,
|
|
|
|
const blink::WebURLError& error) {
|
2013-09-13 01:36:54 +02:00
|
|
|
OnLoadError(frame, error);
|
|
|
|
}
|
|
|
|
|
2014-04-30 19:14:40 +02:00
|
|
|
void CefBrowserImpl::DidCommitProvisionalLoad(blink::WebLocalFrame* frame,
|
2013-09-13 01:36:54 +02:00
|
|
|
bool is_new_navigation) {
|
2018-02-20 19:51:59 +01:00
|
|
|
if (frame->Parent() == nullptr) {
|
|
|
|
OnLoadingStateChange(true);
|
|
|
|
}
|
2013-09-13 01:36:54 +02:00
|
|
|
OnLoadStart(frame);
|
|
|
|
}
|
|
|
|
|
2017-07-27 01:19:27 +02:00
|
|
|
void CefBrowserImpl::FrameDetached(WebLocalFrame* frame) {
|
2017-07-06 22:39:37 +02:00
|
|
|
int64_t frame_id = render_frame_util::GetIdentifier(frame);
|
2012-04-03 03:34:16 +02:00
|
|
|
|
2013-08-16 19:05:26 +02:00
|
|
|
if (!frames_.empty()) {
|
2012-04-27 23:19:06 +02:00
|
|
|
// Remove the frame from the map.
|
|
|
|
FrameMap::iterator it = frames_.find(frame_id);
|
2013-08-16 19:05:26 +02:00
|
|
|
if (it != frames_.end()) {
|
|
|
|
it->second->Detach();
|
|
|
|
frames_.erase(it);
|
|
|
|
}
|
2012-04-27 23:19:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!frame_objects_.empty()) {
|
|
|
|
// Remove any tracked objects associated with the frame.
|
|
|
|
FrameObjectMap::iterator it = frame_objects_.find(frame_id);
|
|
|
|
if (it != frame_objects_.end())
|
|
|
|
frame_objects_.erase(it);
|
|
|
|
}
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
2013-11-08 22:28:56 +01:00
|
|
|
void CefBrowserImpl::FocusedNodeChanged(const blink::WebNode& node) {
|
2012-06-11 17:52:49 +02:00
|
|
|
// Notify the handler.
|
|
|
|
CefRefPtr<CefApp> app = CefContentClient::Get()->application();
|
|
|
|
if (app.get()) {
|
2017-05-17 11:29:28 +02:00
|
|
|
CefRefPtr<CefRenderProcessHandler> handler = app->GetRenderProcessHandler();
|
2012-06-11 17:52:49 +02:00
|
|
|
if (handler.get()) {
|
2017-04-20 21:28:17 +02:00
|
|
|
if (node.IsNull()) {
|
2012-06-11 17:52:49 +02:00
|
|
|
handler->OnFocusedNodeChanged(this, GetFocusedFrame(), NULL);
|
|
|
|
} else {
|
2017-04-20 21:28:17 +02:00
|
|
|
const blink::WebDocument& document = node.GetDocument();
|
|
|
|
if (!document.IsNull()) {
|
|
|
|
blink::WebFrame* frame = document.GetFrame();
|
2017-07-27 01:19:27 +02:00
|
|
|
if (!frame->IsWebLocalFrame())
|
|
|
|
return;
|
2012-06-11 17:52:49 +02:00
|
|
|
CefRefPtr<CefDOMDocumentImpl> documentImpl =
|
2017-07-27 01:19:27 +02:00
|
|
|
new CefDOMDocumentImpl(this, frame->ToWebLocalFrame());
|
|
|
|
handler->OnFocusedNodeChanged(
|
|
|
|
this, GetWebFrameImpl(frame->ToWebLocalFrame()).get(),
|
|
|
|
documentImpl->GetOrCreateNode(node));
|
2012-06-11 17:52:49 +02:00
|
|
|
documentImpl->Detach();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
|
2016-08-08 15:15:20 +02:00
|
|
|
// Based on ExtensionHelper::DraggableRegionsChanged.
|
2017-07-27 01:19:27 +02:00
|
|
|
void CefBrowserImpl::DraggableRegionsChanged(blink::WebLocalFrame* frame) {
|
2015-04-24 15:48:32 +02:00
|
|
|
blink::WebVector<blink::WebDraggableRegion> webregions =
|
2017-04-20 21:28:17 +02:00
|
|
|
frame->GetDocument().DraggableRegions();
|
2015-04-24 15:48:32 +02:00
|
|
|
std::vector<Cef_DraggableRegion_Params> regions;
|
|
|
|
for (size_t i = 0; i < webregions.size(); ++i) {
|
|
|
|
Cef_DraggableRegion_Params region;
|
2016-08-08 15:15:20 +02:00
|
|
|
render_view()->ConvertViewportToWindowViaWidget(&webregions[i].bounds);
|
2015-04-24 15:48:32 +02:00
|
|
|
region.bounds = webregions[i].bounds;
|
|
|
|
region.draggable = webregions[i].draggable;
|
|
|
|
regions.push_back(region);
|
|
|
|
}
|
|
|
|
Send(new CefHostMsg_UpdateDraggableRegions(routing_id(), regions));
|
|
|
|
}
|
|
|
|
|
2012-04-03 03:34:16 +02:00
|
|
|
bool CefBrowserImpl::OnMessageReceived(const IPC::Message& message) {
|
|
|
|
bool handled = true;
|
|
|
|
IPC_BEGIN_MESSAGE_MAP(CefBrowserImpl, message)
|
|
|
|
IPC_MESSAGE_HANDLER(CefMsg_Request, OnRequest)
|
|
|
|
IPC_MESSAGE_HANDLER(CefMsg_Response, OnResponse)
|
|
|
|
IPC_MESSAGE_HANDLER(CefMsg_ResponseAck, OnResponseAck)
|
2014-07-16 23:27:25 +02:00
|
|
|
IPC_MESSAGE_HANDLER(CefMsg_LoadRequest, LoadRequest)
|
2012-04-03 03:34:16 +02:00
|
|
|
IPC_MESSAGE_UNHANDLED(handled = false)
|
|
|
|
IPC_END_MESSAGE_MAP()
|
|
|
|
return handled;
|
|
|
|
}
|
|
|
|
|
|
|
|
// RenderViewObserver::OnMessageReceived message handlers.
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void CefBrowserImpl::OnRequest(const Cef_Request_Params& params) {
|
|
|
|
bool success = false;
|
|
|
|
std::string response;
|
|
|
|
bool expect_response_ack = false;
|
|
|
|
|
2017-05-17 11:29:28 +02:00
|
|
|
TRACE_EVENT2("libcef", "CefBrowserImpl::OnRequest", "request_id",
|
|
|
|
params.request_id, "expect_response",
|
|
|
|
params.expect_response ? 1 : 0);
|
2013-07-11 19:44:48 +02:00
|
|
|
|
2012-04-03 03:34:16 +02:00
|
|
|
if (params.user_initiated) {
|
|
|
|
// Give the user a chance to handle the request.
|
|
|
|
CefRefPtr<CefApp> app = CefContentClient::Get()->application();
|
|
|
|
if (app.get()) {
|
|
|
|
CefRefPtr<CefRenderProcessHandler> handler =
|
|
|
|
app->GetRenderProcessHandler();
|
|
|
|
if (handler.get()) {
|
2017-05-17 11:29:28 +02:00
|
|
|
CefRefPtr<CefProcessMessageImpl> message(new CefProcessMessageImpl(
|
|
|
|
const_cast<Cef_Request_Params*>(¶ms), false, true));
|
|
|
|
success =
|
|
|
|
handler->OnProcessMessageReceived(this, PID_BROWSER, message.get());
|
2016-11-23 21:54:29 +01:00
|
|
|
message->Detach(nullptr);
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (params.name == "execute-code") {
|
|
|
|
// Execute code.
|
|
|
|
CefRefPtr<CefFrameImpl> framePtr = GetWebFrameImpl(params.frame_id);
|
|
|
|
if (framePtr.get()) {
|
2017-07-27 01:19:27 +02:00
|
|
|
WebLocalFrame* web_frame = framePtr->web_frame();
|
2012-04-03 03:34:16 +02:00
|
|
|
if (web_frame) {
|
|
|
|
DCHECK_EQ(params.arguments.GetSize(), (size_t)4);
|
|
|
|
|
|
|
|
bool is_javascript = false;
|
2012-10-18 00:45:49 +02:00
|
|
|
std::string code, script_url;
|
2012-04-03 03:34:16 +02:00
|
|
|
int script_start_line = 0;
|
|
|
|
|
|
|
|
params.arguments.GetBoolean(0, &is_javascript);
|
|
|
|
params.arguments.GetString(1, &code);
|
|
|
|
DCHECK(!code.empty());
|
|
|
|
params.arguments.GetString(2, &script_url);
|
|
|
|
params.arguments.GetInteger(3, &script_start_line);
|
|
|
|
DCHECK_GE(script_start_line, 0);
|
|
|
|
|
|
|
|
if (is_javascript) {
|
2017-04-20 21:28:17 +02:00
|
|
|
web_frame->ExecuteScript(
|
|
|
|
WebScriptSource(blink::WebString::FromUTF8(code),
|
2017-05-17 11:29:28 +02:00
|
|
|
GURL(script_url), script_start_line));
|
2012-04-03 03:34:16 +02:00
|
|
|
success = true;
|
|
|
|
} else {
|
|
|
|
// TODO(cef): implement support for CSS code.
|
|
|
|
NOTIMPLEMENTED();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (params.name == "execute-command") {
|
|
|
|
// Execute command.
|
|
|
|
CefRefPtr<CefFrameImpl> framePtr = GetWebFrameImpl(params.frame_id);
|
|
|
|
if (framePtr.get()) {
|
2017-07-27 01:19:27 +02:00
|
|
|
WebLocalFrame* web_frame = framePtr->web_frame();
|
2012-04-03 03:34:16 +02:00
|
|
|
if (web_frame) {
|
|
|
|
DCHECK_EQ(params.arguments.GetSize(), (size_t)1);
|
|
|
|
|
2012-10-18 00:45:49 +02:00
|
|
|
std::string command;
|
2012-04-03 03:34:16 +02:00
|
|
|
|
|
|
|
params.arguments.GetString(0, &command);
|
|
|
|
DCHECK(!command.empty());
|
|
|
|
|
2015-07-24 02:06:56 +02:00
|
|
|
if (base::LowerCaseEqualsASCII(command, "getsource")) {
|
2017-07-27 01:19:27 +02:00
|
|
|
response =
|
|
|
|
blink::WebFrameContentDumper::DumpAsMarkup(web_frame).Utf8();
|
|
|
|
success = true;
|
2015-07-24 02:06:56 +02:00
|
|
|
} else if (base::LowerCaseEqualsASCII(command, "gettext")) {
|
2018-04-19 17:44:42 +02:00
|
|
|
response = blink_glue::DumpDocumentText(web_frame);
|
2012-04-03 03:34:16 +02:00
|
|
|
success = true;
|
2017-07-27 01:19:27 +02:00
|
|
|
} else if (web_frame->ExecuteCommand(
|
2017-05-17 11:29:28 +02:00
|
|
|
blink::WebString::FromUTF8(command))) {
|
2012-04-03 03:34:16 +02:00
|
|
|
success = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (params.name == "load-string") {
|
|
|
|
// Load a string.
|
|
|
|
CefRefPtr<CefFrameImpl> framePtr = GetWebFrameImpl(params.frame_id);
|
|
|
|
if (framePtr.get()) {
|
2017-07-27 01:19:27 +02:00
|
|
|
WebLocalFrame* web_frame = framePtr->web_frame();
|
2012-04-03 03:34:16 +02:00
|
|
|
if (web_frame) {
|
|
|
|
DCHECK_EQ(params.arguments.GetSize(), (size_t)2);
|
|
|
|
|
2012-10-18 00:45:49 +02:00
|
|
|
std::string string, url;
|
2012-04-03 03:34:16 +02:00
|
|
|
|
|
|
|
params.arguments.GetString(0, &string);
|
|
|
|
params.arguments.GetString(1, &url);
|
|
|
|
|
2017-04-20 21:28:17 +02:00
|
|
|
web_frame->LoadHTMLString(string, GURL(url));
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Invalid request.
|
|
|
|
NOTREACHED();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (params.expect_response) {
|
|
|
|
DCHECK_GE(params.request_id, 0);
|
|
|
|
|
|
|
|
// Send a response to the browser.
|
|
|
|
Cef_Response_Params response_params;
|
|
|
|
response_params.request_id = params.request_id;
|
|
|
|
response_params.success = success;
|
|
|
|
response_params.response = response;
|
|
|
|
response_params.expect_response_ack = expect_response_ack;
|
|
|
|
Send(new CefHostMsg_Response(routing_id(), response_params));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefBrowserImpl::OnResponse(const Cef_Response_Params& params) {
|
2012-05-18 23:18:01 +02:00
|
|
|
response_manager_->RunHandler(params);
|
2012-04-03 03:34:16 +02:00
|
|
|
if (params.expect_response_ack)
|
|
|
|
Send(new CefHostMsg_ResponseAck(routing_id(), params.request_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefBrowserImpl::OnResponseAck(int request_id) {
|
2012-05-18 23:18:01 +02:00
|
|
|
response_manager_->RunAckHandler(request_id);
|
2012-04-03 03:34:16 +02:00
|
|
|
}
|
2013-09-13 01:36:54 +02:00
|
|
|
|
|
|
|
void CefBrowserImpl::OnLoadingStateChange(bool isLoading) {
|
|
|
|
CefRefPtr<CefApp> app = CefContentClient::Get()->application();
|
|
|
|
if (app.get()) {
|
2017-05-17 11:29:28 +02:00
|
|
|
CefRefPtr<CefRenderProcessHandler> handler = app->GetRenderProcessHandler();
|
2013-09-13 01:36:54 +02:00
|
|
|
if (handler.get()) {
|
|
|
|
CefRefPtr<CefLoadHandler> load_handler = handler->GetLoadHandler();
|
|
|
|
if (load_handler.get()) {
|
|
|
|
WebView* web_view = render_view()->GetWebView();
|
2018-04-19 17:44:42 +02:00
|
|
|
const bool canGoBack = blink_glue::CanGoBack(web_view);
|
|
|
|
const bool canGoForward = blink_glue::CanGoForward(web_view);
|
2013-09-13 01:36:54 +02:00
|
|
|
|
|
|
|
load_handler->OnLoadingStateChange(this, isLoading, canGoBack,
|
|
|
|
canGoForward);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-30 19:14:40 +02:00
|
|
|
void CefBrowserImpl::OnLoadStart(blink::WebLocalFrame* frame) {
|
2013-09-13 01:36:54 +02:00
|
|
|
CefRefPtr<CefApp> app = CefContentClient::Get()->application();
|
|
|
|
if (app.get()) {
|
2017-05-17 11:29:28 +02:00
|
|
|
CefRefPtr<CefRenderProcessHandler> handler = app->GetRenderProcessHandler();
|
2013-09-13 01:36:54 +02:00
|
|
|
if (handler.get()) {
|
|
|
|
CefRefPtr<CefLoadHandler> load_handler = handler->GetLoadHandler();
|
|
|
|
if (load_handler.get()) {
|
|
|
|
CefRefPtr<CefFrameImpl> cef_frame = GetWebFrameImpl(frame);
|
2016-07-12 17:59:42 +02:00
|
|
|
load_handler->OnLoadStart(this, cef_frame.get(), TT_EXPLICIT);
|
2013-09-13 01:36:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-30 19:14:40 +02:00
|
|
|
void CefBrowserImpl::OnLoadEnd(blink::WebLocalFrame* frame) {
|
2013-09-13 01:36:54 +02:00
|
|
|
CefRefPtr<CefApp> app = CefContentClient::Get()->application();
|
|
|
|
if (app.get()) {
|
2017-05-17 11:29:28 +02:00
|
|
|
CefRefPtr<CefRenderProcessHandler> handler = app->GetRenderProcessHandler();
|
2013-09-13 01:36:54 +02:00
|
|
|
if (handler.get()) {
|
|
|
|
CefRefPtr<CefLoadHandler> load_handler = handler->GetLoadHandler();
|
|
|
|
if (load_handler.get()) {
|
|
|
|
CefRefPtr<CefFrameImpl> cef_frame = GetWebFrameImpl(frame);
|
2017-04-20 21:28:17 +02:00
|
|
|
int httpStatusCode =
|
2017-09-06 23:40:58 +02:00
|
|
|
frame->GetDocumentLoader()->GetResponse().HttpStatusCode();
|
2013-09-13 01:36:54 +02:00
|
|
|
load_handler->OnLoadEnd(this, cef_frame.get(), httpStatusCode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-30 19:14:40 +02:00
|
|
|
void CefBrowserImpl::OnLoadError(blink::WebLocalFrame* frame,
|
2013-11-08 22:28:56 +01:00
|
|
|
const blink::WebURLError& error) {
|
2013-09-13 01:36:54 +02:00
|
|
|
CefRefPtr<CefApp> app = CefContentClient::Get()->application();
|
|
|
|
if (app.get()) {
|
2017-05-17 11:29:28 +02:00
|
|
|
CefRefPtr<CefRenderProcessHandler> handler = app->GetRenderProcessHandler();
|
2013-09-13 01:36:54 +02:00
|
|
|
if (handler.get()) {
|
|
|
|
CefRefPtr<CefLoadHandler> load_handler = handler->GetLoadHandler();
|
|
|
|
if (load_handler.get()) {
|
|
|
|
CefRefPtr<CefFrameImpl> cef_frame = GetWebFrameImpl(frame);
|
|
|
|
const cef_errorcode_t errorCode =
|
2017-12-07 22:44:24 +01:00
|
|
|
static_cast<cef_errorcode_t>(error.reason());
|
|
|
|
const std::string& errorText = net::ErrorToString(error.reason());
|
|
|
|
const GURL& failedUrl = error.url();
|
2013-09-13 01:36:54 +02:00
|
|
|
load_handler->OnLoadError(this, cef_frame.get(), errorCode, errorText,
|
|
|
|
failedUrl.spec());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|