| // Copyright (c) 2012 The Chromium Embedded Framework 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/frame_impl.h" |
| |
| #include "base/compiler_specific.h" |
| |
| // Enable deprecation warnings on Windows. See http://crbug.com/585142. |
| #if defined(OS_WIN) |
| #if defined(__clang__) |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic error "-Wdeprecated-declarations" |
| #else |
| #pragma warning(push) |
| #pragma warning(default : 4996) |
| #endif |
| #endif |
| |
| #include "libcef/common/cef_messages.h" |
| #include "libcef/common/content_client.h" |
| #include "libcef/common/net/http_header_utils.h" |
| #include "libcef/common/process_message_impl.h" |
| #include "libcef/common/request_impl.h" |
| #include "libcef/common/response_manager.h" |
| #include "libcef/renderer/blink_glue.h" |
| #include "libcef/renderer/browser_impl.h" |
| #include "libcef/renderer/dom_document_impl.h" |
| #include "libcef/renderer/render_frame_util.h" |
| #include "libcef/renderer/render_urlrequest_impl.h" |
| #include "libcef/renderer/thread_util.h" |
| #include "libcef/renderer/v8_impl.h" |
| |
| #include "base/strings/utf_string_conversions.h" |
| #include "content/public/renderer/render_view.h" |
| #include "content/renderer/render_frame_impl.h" |
| #include "third_party/blink/public/platform/web_data.h" |
| #include "third_party/blink/public/platform/web_string.h" |
| #include "third_party/blink/public/platform/web_url.h" |
| #include "third_party/blink/public/web/blink.h" |
| #include "third_party/blink/public/web/web_document.h" |
| #include "third_party/blink/public/web/web_document_loader.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_navigation_control.h" |
| #include "third_party/blink/public/web/web_script_source.h" |
| #include "third_party/blink/public/web/web_view.h" |
| |
| CefFrameImpl::CefFrameImpl(CefBrowserImpl* browser, |
| blink::WebLocalFrame* frame, |
| int64_t frame_id) |
| : browser_(browser), |
| frame_(frame), |
| frame_id_(frame_id), |
| response_manager_(new CefResponseManager) {} |
| |
| CefFrameImpl::~CefFrameImpl() {} |
| |
| bool CefFrameImpl::IsValid() { |
| CEF_REQUIRE_RT_RETURN(false); |
| |
| return (frame_ != nullptr); |
| } |
| |
| void CefFrameImpl::Undo() { |
| ExecuteCommand("Undo"); |
| } |
| |
| void CefFrameImpl::Redo() { |
| ExecuteCommand("Redo"); |
| } |
| |
| void CefFrameImpl::Cut() { |
| ExecuteCommand("Cut"); |
| } |
| |
| void CefFrameImpl::Copy() { |
| ExecuteCommand("Copy"); |
| } |
| |
| void CefFrameImpl::Paste() { |
| ExecuteCommand("Paste"); |
| } |
| |
| void CefFrameImpl::Delete() { |
| ExecuteCommand("Delete"); |
| } |
| |
| void CefFrameImpl::SelectAll() { |
| ExecuteCommand("SelectAll"); |
| } |
| |
| void CefFrameImpl::ViewSource() { |
| NOTREACHED() << "ViewSource cannot be called from the renderer process"; |
| } |
| |
| void CefFrameImpl::GetSource(CefRefPtr<CefStringVisitor> visitor) { |
| CEF_REQUIRE_RT_RETURN_VOID(); |
| if (frame_) { |
| const CefString& content = |
| std::string(blink::WebFrameContentDumper::DumpAsMarkup(frame_).Utf8()); |
| visitor->Visit(content); |
| } |
| } |
| |
| void CefFrameImpl::GetText(CefRefPtr<CefStringVisitor> visitor) { |
| CEF_REQUIRE_RT_RETURN_VOID(); |
| if (frame_) { |
| const CefString& content = blink_glue::DumpDocumentText(frame_); |
| visitor->Visit(content); |
| } |
| } |
| |
| void CefFrameImpl::LoadRequest(CefRefPtr<CefRequest> request) { |
| CEF_REQUIRE_RT_RETURN_VOID(); |
| |
| if (!frame_) |
| return; |
| |
| CefMsg_LoadRequest_Params params; |
| params.url = GURL(std::string(request->GetURL())); |
| params.method = request->GetMethod(); |
| params.site_for_cookies = net::SiteForCookies::FromUrl( |
| GURL(request->GetFirstPartyForCookies().ToString())); |
| |
| CefRequest::HeaderMap headerMap; |
| request->GetHeaderMap(headerMap); |
| if (!headerMap.empty()) |
| params.headers = HttpHeaderUtils::GenerateHeaders(headerMap); |
| |
| CefRefPtr<CefPostData> postData = request->GetPostData(); |
| if (postData.get()) { |
| CefPostDataImpl* impl = static_cast<CefPostDataImpl*>(postData.get()); |
| params.upload_data = new net::UploadData(); |
| impl->Get(*params.upload_data.get()); |
| } |
| |
| params.load_flags = request->GetFlags(); |
| |
| OnLoadRequest(params); |
| } |
| |
| void CefFrameImpl::LoadURL(const CefString& url) { |
| CEF_REQUIRE_RT_RETURN_VOID(); |
| |
| if (!frame_) |
| return; |
| |
| CefMsg_LoadRequest_Params params; |
| params.url = GURL(url.ToString()); |
| params.method = "GET"; |
| |
| OnLoadRequest(params); |
| } |
| |
| void CefFrameImpl::ExecuteJavaScript(const CefString& jsCode, |
| const CefString& scriptUrl, |
| int startLine) { |
| CEF_REQUIRE_RT_RETURN_VOID(); |
| |
| if (jsCode.empty()) |
| return; |
| if (startLine < 1) |
| startLine = 1; |
| |
| if (frame_) { |
| GURL gurl = GURL(scriptUrl.ToString()); |
| frame_->ExecuteScript(blink::WebScriptSource( |
| blink::WebString::FromUTF16(jsCode.ToString16()), gurl, startLine)); |
| } |
| } |
| |
| bool CefFrameImpl::IsMain() { |
| CEF_REQUIRE_RT_RETURN(false); |
| |
| if (frame_) |
| return (frame_->Parent() == nullptr); |
| return false; |
| } |
| |
| bool CefFrameImpl::IsFocused() { |
| CEF_REQUIRE_RT_RETURN(false); |
| |
| if (frame_ && frame_->View()) |
| return (frame_->View()->FocusedFrame() == frame_); |
| return false; |
| } |
| |
| CefString CefFrameImpl::GetName() { |
| CefString name; |
| CEF_REQUIRE_RT_RETURN(name); |
| |
| if (frame_) |
| name = render_frame_util::GetName(frame_); |
| return name; |
| } |
| |
| int64 CefFrameImpl::GetIdentifier() { |
| CEF_REQUIRE_RT_RETURN(0); |
| |
| return frame_id_; |
| } |
| |
| CefRefPtr<CefFrame> CefFrameImpl::GetParent() { |
| CEF_REQUIRE_RT_RETURN(nullptr); |
| |
| if (frame_) { |
| blink::WebFrame* parent = frame_->Parent(); |
| if (parent && parent->IsWebLocalFrame()) |
| return browser_->GetWebFrameImpl(parent->ToWebLocalFrame()).get(); |
| } |
| |
| return nullptr; |
| } |
| |
| CefString CefFrameImpl::GetURL() { |
| CefString url; |
| CEF_REQUIRE_RT_RETURN(url); |
| |
| if (frame_) { |
| GURL gurl = frame_->GetDocument().Url(); |
| url = gurl.spec(); |
| } |
| return url; |
| } |
| |
| CefRefPtr<CefBrowser> CefFrameImpl::GetBrowser() { |
| CEF_REQUIRE_RT_RETURN(nullptr); |
| |
| return browser_; |
| } |
| |
| CefRefPtr<CefV8Context> CefFrameImpl::GetV8Context() { |
| CEF_REQUIRE_RT_RETURN(nullptr); |
| |
| if (frame_) { |
| v8::Isolate* isolate = blink::MainThreadIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| return new CefV8ContextImpl(isolate, frame_->MainWorldScriptContext()); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| void CefFrameImpl::VisitDOM(CefRefPtr<CefDOMVisitor> visitor) { |
| CEF_REQUIRE_RT_RETURN_VOID(); |
| |
| if (!frame_) |
| return; |
| |
| // Create a CefDOMDocumentImpl object that is valid only for the scope of this |
| // method. |
| CefRefPtr<CefDOMDocumentImpl> documentImpl; |
| const blink::WebDocument& document = frame_->GetDocument(); |
| if (!document.IsNull()) |
| documentImpl = new CefDOMDocumentImpl(browser_, frame_); |
| |
| visitor->Visit(documentImpl.get()); |
| |
| if (documentImpl.get()) |
| documentImpl->Detach(); |
| } |
| |
| CefRefPtr<CefURLRequest> CefFrameImpl::CreateURLRequest( |
| CefRefPtr<CefRequest> request, |
| CefRefPtr<CefURLRequestClient> client) { |
| CEF_REQUIRE_RT_RETURN(nullptr); |
| |
| if (!request || !client || !frame_) |
| return nullptr; |
| |
| CefRefPtr<CefRenderURLRequest> impl = |
| new CefRenderURLRequest(this, request, client); |
| if (impl->Start()) |
| return impl.get(); |
| return nullptr; |
| } |
| |
| void CefFrameImpl::SendProcessMessage(CefProcessId target_process, |
| CefRefPtr<CefProcessMessage> message) { |
| Cef_Request_Params params; |
| CefProcessMessageImpl* impl = |
| static_cast<CefProcessMessageImpl*>(message.get()); |
| if (impl->CopyTo(params)) { |
| SendProcessMessage(target_process, params.name, ¶ms.arguments, true); |
| } |
| } |
| |
| blink::WebURLLoaderFactory* CefFrameImpl::GetURLLoaderFactory() { |
| CEF_REQUIRE_RT(); |
| if (!url_loader_factory_ && frame_) { |
| auto render_frame = content::RenderFrameImpl::FromWebFrame(frame_); |
| if (render_frame) { |
| url_loader_factory_ = render_frame->CreateURLLoaderFactory(); |
| } |
| } |
| return url_loader_factory_.get(); |
| } |
| |
| void CefFrameImpl::OnAttached() { |
| Send(new CefHostMsg_FrameAttached(MSG_ROUTING_NONE)); |
| } |
| |
| bool CefFrameImpl::OnMessageReceived(const IPC::Message& message) { |
| bool handled = true; |
| IPC_BEGIN_MESSAGE_MAP(CefFrameImpl, message) |
| IPC_MESSAGE_HANDLER(CefMsg_Request, OnRequest) |
| IPC_MESSAGE_HANDLER(CefMsg_Response, OnResponse) |
| IPC_MESSAGE_HANDLER(CefMsg_ResponseAck, OnResponseAck) |
| IPC_MESSAGE_HANDLER(CefMsg_LoadRequest, OnLoadRequest) |
| IPC_MESSAGE_HANDLER(CefMsg_DidStopLoading, OnDidStopLoading) |
| IPC_MESSAGE_UNHANDLED(handled = false) |
| IPC_END_MESSAGE_MAP() |
| return handled; |
| } |
| |
| void CefFrameImpl::OnDidFinishLoad() { |
| // Ignore notifications from the embedded frame hosting a mime-type plugin. |
| // We'll eventually receive a notification from the owner frame. |
| if (blink_glue::HasPluginFrameOwner(frame_)) |
| return; |
| |
| blink::WebDocumentLoader* dl = frame_->GetDocumentLoader(); |
| const int http_status_code = dl->GetResponse().HttpStatusCode(); |
| Send(new CefHostMsg_DidFinishLoad(MSG_ROUTING_NONE, dl->GetUrl(), |
| http_status_code)); |
| |
| CefRefPtr<CefApp> app = CefContentClient::Get()->application(); |
| if (app) { |
| CefRefPtr<CefRenderProcessHandler> handler = app->GetRenderProcessHandler(); |
| if (handler) { |
| CefRefPtr<CefLoadHandler> load_handler = handler->GetLoadHandler(); |
| if (load_handler) { |
| load_handler->OnLoadEnd(browser_, this, http_status_code); |
| } |
| } |
| } |
| } |
| |
| void CefFrameImpl::OnDraggableRegionsChanged() { |
| blink::WebVector<blink::WebDraggableRegion> webregions = |
| frame_->GetDocument().DraggableRegions(); |
| std::vector<Cef_DraggableRegion_Params> regions; |
| for (size_t i = 0; i < webregions.size(); ++i) { |
| Cef_DraggableRegion_Params region; |
| auto render_frame = content::RenderFrameImpl::FromWebFrame(frame_); |
| render_frame->ConvertViewportToWindow(&webregions[i].bounds); |
| region.bounds = webregions[i].bounds; |
| region.draggable = webregions[i].draggable; |
| regions.push_back(region); |
| } |
| Send(new CefHostMsg_UpdateDraggableRegions(MSG_ROUTING_NONE, regions)); |
| } |
| |
| void CefFrameImpl::OnDetached() { |
| // The browser may hold the last reference to |this|. Take a reference here to |
| // keep |this| alive until after this method returns. |
| CefRefPtr<CefFrameImpl> self = this; |
| |
| browser_->FrameDetached(frame_id_); |
| |
| browser_ = nullptr; |
| frame_ = nullptr; |
| url_loader_factory_.reset(); |
| response_manager_.reset(); |
| } |
| |
| void CefFrameImpl::ExecuteCommand(const std::string& command) { |
| CEF_REQUIRE_RT_RETURN_VOID(); |
| if (frame_) |
| frame_->ExecuteCommand(blink::WebString::FromUTF8(command)); |
| } |
| |
| void CefFrameImpl::SendProcessMessage(CefProcessId target_process, |
| const std::string& name, |
| base::ListValue* arguments, |
| bool user_initiated) { |
| DCHECK_EQ(PID_BROWSER, target_process); |
| DCHECK(!name.empty()); |
| |
| if (!frame_) |
| return; |
| |
| Cef_Request_Params params; |
| params.name = name; |
| if (arguments) |
| params.arguments.Swap(arguments); |
| params.user_initiated = user_initiated; |
| params.request_id = -1; |
| params.expect_response = false; |
| |
| Send(new CefHostMsg_Request(MSG_ROUTING_NONE, params)); |
| } |
| |
| void CefFrameImpl::Send(IPC::Message* message) { |
| if (!frame_) { |
| delete message; |
| return; |
| } |
| |
| auto render_frame = content::RenderFrame::FromWebFrame(frame_); |
| message->set_routing_id(render_frame->GetRoutingID()); |
| render_frame->Send(message); |
| } |
| |
| void CefFrameImpl::OnRequest(const Cef_Request_Params& params) { |
| DCHECK(browser_); |
| DCHECK(frame_); |
| |
| bool success = false; |
| std::string response; |
| bool expect_response_ack = false; |
| |
| TRACE_EVENT2("cef", "CefBrowserImpl::OnRequest", "request_id", |
| params.request_id, "expect_response", |
| params.expect_response ? 1 : 0); |
| |
| 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()) { |
| CefRefPtr<CefProcessMessageImpl> message(new CefProcessMessageImpl( |
| const_cast<Cef_Request_Params*>(¶ms), false, true)); |
| success = handler->OnProcessMessageReceived(browser_, this, PID_BROWSER, |
| message.get()); |
| message->Detach(nullptr); |
| } |
| } |
| } else if (params.name == "execute-code") { |
| // Execute code. |
| DCHECK_EQ(params.arguments.GetSize(), (size_t)4); |
| |
| bool is_javascript = false; |
| std::string code, script_url; |
| 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) { |
| frame_->ExecuteScript( |
| blink::WebScriptSource(blink::WebString::FromUTF8(code), |
| GURL(script_url), script_start_line)); |
| success = true; |
| } else { |
| // TODO(cef): implement support for CSS code. |
| NOTIMPLEMENTED(); |
| } |
| } else if (params.name == "execute-command") { |
| // Execute command. |
| DCHECK_EQ(params.arguments.GetSize(), (size_t)1); |
| |
| std::string command; |
| |
| params.arguments.GetString(0, &command); |
| DCHECK(!command.empty()); |
| |
| if (base::LowerCaseEqualsASCII(command, "getsource")) { |
| response = blink::WebFrameContentDumper::DumpAsMarkup(frame_).Utf8(); |
| success = true; |
| } else if (base::LowerCaseEqualsASCII(command, "gettext")) { |
| response = blink_glue::DumpDocumentText(frame_); |
| success = true; |
| } else if (frame_->ExecuteCommand(blink::WebString::FromUTF8(command))) { |
| success = true; |
| } |
| } 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(MSG_ROUTING_NONE, response_params)); |
| } |
| } |
| |
| void CefFrameImpl::OnResponse(const Cef_Response_Params& params) { |
| response_manager_->RunHandler(params); |
| if (params.expect_response_ack) |
| Send(new CefHostMsg_ResponseAck(MSG_ROUTING_NONE, params.request_id)); |
| } |
| |
| void CefFrameImpl::OnResponseAck(int request_id) { |
| response_manager_->RunAckHandler(request_id); |
| } |
| |
| void CefFrameImpl::OnDidStopLoading() { |
| // We should only receive this notification for the highest-level LocalFrame |
| // in this frame's in-process subtree. If there are multiple of these for the |
| // same browser then the other occurrences will be discarded in |
| // OnLoadingStateChange. |
| browser_->OnLoadingStateChange(false); |
| } |
| |
| void CefFrameImpl::OnLoadRequest(const CefMsg_LoadRequest_Params& params) { |
| DCHECK(frame_); |
| |
| blink::WebURLRequest request; |
| CefRequestImpl::Get(params, request); |
| |
| frame_->StartNavigation(request); |
| } |
| |
| // Enable deprecation warnings on Windows. See http://crbug.com/585142. |
| #if defined(OS_WIN) |
| #if defined(__clang__) |
| #pragma GCC diagnostic pop |
| #else |
| #pragma warning(pop) |
| #endif |
| #endif |