electron/atom/renderer/api/atom_api_web_frame.cc

552 lines
20 KiB
C++
Raw Normal View History

// Copyright (c) 2014 GitHub, Inc.
2014-10-24 10:24:12 +00:00
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "atom/common/api/api_messages.h"
#include "atom/common/api/event_emitter_caller.h"
2016-05-12 20:56:46 +00:00
#include "atom/common/native_mate_converters/blink_converter.h"
#include "atom/common/native_mate_converters/callback.h"
2015-05-29 05:47:09 +00:00
#include "atom/common/native_mate_converters/gfx_converter.h"
2014-10-24 10:24:12 +00:00
#include "atom/common/native_mate_converters/string16_converter.h"
2014-12-18 21:54:01 +00:00
#include "atom/renderer/api/atom_api_spell_check_client.h"
2016-05-12 21:36:49 +00:00
#include "base/memory/memory_pressure_listener.h"
2014-12-08 16:05:34 +00:00
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_frame_observer.h"
#include "content/public/renderer/render_frame_visitor.h"
#include "content/public/renderer/render_view.h"
2014-10-24 10:24:12 +00:00
#include "native_mate/dictionary.h"
#include "native_mate/object_template_builder.h"
#include "third_party/blink/public/platform/web_cache.h"
#include "third_party/blink/public/platform/web_isolated_world_info.h"
#include "third_party/blink/public/web/web_custom_element.h"
#include "third_party/blink/public/web/web_document.h"
#include "third_party/blink/public/web/web_element.h"
#include "third_party/blink/public/web/web_frame_widget.h"
#include "third_party/blink/public/web/web_ime_text_span.h"
#include "third_party/blink/public/web/web_input_method_controller.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/public/web/web_script_execution_callback.h"
#include "third_party/blink/public/web/web_script_source.h"
#include "third_party/blink/public/web/web_view.h"
2018-10-27 00:31:56 +00:00
#include "url/url_util.h"
2014-10-24 10:24:12 +00:00
#include "atom/common/node_includes.h"
namespace mate {
template <>
struct Converter<blink::WebLocalFrame::ScriptExecutionType> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
blink::WebLocalFrame::ScriptExecutionType* out) {
std::string execution_type;
if (!ConvertFromV8(isolate, val, &execution_type))
return false;
if (execution_type == "asynchronous") {
*out = blink::WebLocalFrame::kAsynchronous;
2018-04-18 01:55:30 +00:00
} else if (execution_type == "asynchronousBlockingOnload") {
*out = blink::WebLocalFrame::kAsynchronousBlockingOnload;
} else if (execution_type == "synchronous") {
*out = blink::WebLocalFrame::kSynchronous;
} else {
return false;
}
return true;
}
};
} // namespace mate
2014-10-24 10:24:12 +00:00
namespace atom {
namespace api {
namespace {
content::RenderFrame* GetRenderFrame(v8::Local<v8::Value> value) {
v8::Local<v8::Context> context =
v8::Local<v8::Object>::Cast(value)->CreationContext();
if (context.IsEmpty())
return nullptr;
blink::WebLocalFrame* frame = blink::WebLocalFrame::FrameForContext(context);
if (!frame)
return nullptr;
return content::RenderFrame::FromWebFrame(frame);
}
class RenderFrameStatus : public content::RenderFrameObserver {
public:
explicit RenderFrameStatus(content::RenderFrame* render_frame)
: content::RenderFrameObserver(render_frame) {}
~RenderFrameStatus() final {}
bool is_ok() { return render_frame() != nullptr; }
// RenderFrameObserver implementation.
void OnDestruct() final {}
};
class ScriptExecutionCallback : public blink::WebScriptExecutionCallback {
public:
using CompletionCallback =
2018-04-18 01:55:30 +00:00
base::Callback<void(const v8::Local<v8::Value>& result)>;
explicit ScriptExecutionCallback(const CompletionCallback& callback)
: callback_(callback) {}
2016-07-10 11:21:42 +00:00
~ScriptExecutionCallback() override {}
2017-06-16 20:42:33 +00:00
void Completed(
const blink::WebVector<v8::Local<v8::Value>>& result) override {
2017-06-16 20:42:33 +00:00
if (!callback_.is_null() && !result.IsEmpty() && !result[0].IsEmpty())
2016-02-22 14:00:21 +00:00
// Right now only single results per frame is supported.
callback_.Run(result[0]);
delete this;
}
private:
CompletionCallback callback_;
DISALLOW_COPY_AND_ASSIGN(ScriptExecutionCallback);
};
class FrameSetSpellChecker : public content::RenderFrameVisitor {
public:
FrameSetSpellChecker(SpellCheckClient* spell_check_client,
content::RenderFrame* main_frame)
: spell_check_client_(spell_check_client), main_frame_(main_frame) {
content::RenderFrame::ForEach(this);
main_frame->GetWebFrame()->SetSpellCheckPanelHostClient(spell_check_client);
}
bool Visit(content::RenderFrame* render_frame) override {
auto* view = render_frame->GetRenderView();
if (view->GetMainRenderFrame() == main_frame_ ||
(render_frame->IsMainFrame() && render_frame == main_frame_)) {
render_frame->GetWebFrame()->SetTextCheckClient(spell_check_client_);
}
return true;
}
private:
SpellCheckClient* spell_check_client_;
content::RenderFrame* main_frame_;
DISALLOW_COPY_AND_ASSIGN(FrameSetSpellChecker);
};
class SpellCheckerHolder : public content::RenderFrameObserver {
public:
// Find existing holder for the |render_frame|.
static SpellCheckerHolder* FromRenderFrame(
content::RenderFrame* render_frame) {
for (auto* holder : instances_) {
if (holder->render_frame() == render_frame)
return holder;
}
return nullptr;
}
SpellCheckerHolder(content::RenderFrame* render_frame,
std::unique_ptr<SpellCheckClient> spell_check_client)
: content::RenderFrameObserver(render_frame),
spell_check_client_(std::move(spell_check_client)) {
DCHECK(!FromRenderFrame(render_frame));
instances_.insert(this);
}
~SpellCheckerHolder() final { instances_.erase(this); }
void UnsetAndDestroy() {
FrameSetSpellChecker set_spell_checker(nullptr, render_frame());
delete this;
}
// RenderFrameObserver implementation.
void OnDestruct() final {
// Since we delete this in WillReleaseScriptContext, this method is unlikely
// to be called, but override anyway since I'm not sure if there are some
// corner cases.
//
// Note that while there are two "delete this", it is totally fine as the
// observer unsubscribes automatically in destructor and the other one won't
// be called.
//
// Also note that we should not call UnsetAndDestroy here, as the render
// frame is going to be destroyed.
delete this;
}
void WillReleaseScriptContext(v8::Local<v8::Context> context,
int world_id) final {
// Unset spell checker when the script context is going to be released, as
// the spell check implementation lives there.
UnsetAndDestroy();
}
private:
static std::set<SpellCheckerHolder*> instances_;
std::unique_ptr<SpellCheckClient> spell_check_client_;
};
} // namespace
2014-10-24 10:24:12 +00:00
// static
std::set<SpellCheckerHolder*> SpellCheckerHolder::instances_;
2014-10-24 10:24:12 +00:00
void SetName(v8::Local<v8::Value> window, const std::string& name) {
GetRenderFrame(window)->GetWebFrame()->SetName(
blink::WebString::FromUTF8(name));
2014-10-24 10:44:15 +00:00
}
double SetZoomLevel(v8::Local<v8::Value> window, double level) {
2017-01-31 09:25:48 +00:00
double result = 0.0;
content::RenderFrame* render_frame = GetRenderFrame(window);
2018-03-09 09:31:09 +00:00
render_frame->Send(new AtomFrameHostMsg_SetTemporaryZoomLevel(
render_frame->GetRoutingID(), level, &result));
return result;
2014-10-24 10:24:12 +00:00
}
double GetZoomLevel(v8::Local<v8::Value> window) {
2017-01-31 09:25:48 +00:00
double result = 0.0;
content::RenderFrame* render_frame = GetRenderFrame(window);
2018-03-09 09:31:09 +00:00
render_frame->Send(
new AtomFrameHostMsg_GetZoomLevel(render_frame->GetRoutingID(), &result));
return result;
2014-10-24 10:24:12 +00:00
}
double SetZoomFactor(v8::Local<v8::Value> window, double factor) {
2018-04-18 01:55:30 +00:00
return blink::WebView::ZoomLevelToZoomFactor(
SetZoomLevel(window, blink::WebView::ZoomFactorToZoomLevel(factor)));
2014-10-24 10:24:12 +00:00
}
double GetZoomFactor(v8::Local<v8::Value> window) {
return blink::WebView::ZoomLevelToZoomFactor(GetZoomLevel(window));
2014-10-24 10:24:12 +00:00
}
void SetVisualZoomLevelLimits(v8::Local<v8::Value> window,
double min_level,
double max_level) {
blink::WebFrame* web_frame = GetRenderFrame(window)->GetWebFrame();
web_frame->View()->SetDefaultPageScaleLimits(min_level, max_level);
web_frame->View()->SetIgnoreViewportTagScaleLimits(true);
2015-08-27 14:08:25 +00:00
}
void SetLayoutZoomLevelLimits(v8::Local<v8::Value> window,
double min_level,
double max_level) {
blink::WebFrame* web_frame = GetRenderFrame(window)->GetWebFrame();
web_frame->View()->ZoomLimitsChanged(min_level, max_level);
}
void AllowGuestViewElementDefinition(v8::Isolate* isolate,
v8::Local<v8::Value> window,
v8::Local<v8::Object> context,
v8::Local<v8::Function> register_cb) {
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(context->CreationContext());
blink::WebCustomElement::EmbedderNamesAllowedScope embedder_names_scope;
GetRenderFrame(context)->GetWebFrame()->RequestExecuteV8Function(
context->CreationContext(), register_cb, v8::Null(isolate), 0, nullptr,
nullptr);
2014-10-24 10:24:12 +00:00
}
int GetWebFrameId(v8::Local<v8::Value> window,
v8::Local<v8::Value> content_window) {
// Get the WebLocalFrame before (possibly) executing any user-space JS while
// getting the |params|. We track the status of the RenderFrame via an
// observer in case it is deleted during user code execution.
content::RenderFrame* render_frame = GetRenderFrame(content_window);
RenderFrameStatus render_frame_status(render_frame);
2015-05-29 05:47:09 +00:00
if (!render_frame_status.is_ok())
return -1;
blink::WebLocalFrame* frame = render_frame->GetWebFrame();
// Parent must exist.
blink::WebFrame* parent_frame = frame->Parent();
DCHECK(parent_frame);
DCHECK(parent_frame->IsWebLocalFrame());
2014-12-08 16:05:34 +00:00
return render_frame->GetRoutingID();
}
void SetSpellCheckProvider(mate::Arguments* args,
v8::Local<v8::Value> window,
const std::string& language,
v8::Local<v8::Object> provider) {
auto context = args->isolate()->GetCurrentContext();
if (!provider->Has(context, mate::StringToV8(args->isolate(), "spellCheck"))
.ToChecked()) {
args->ThrowError("\"spellCheck\" has to be defined");
return;
}
// Remove the old client.
content::RenderFrame* render_frame = GetRenderFrame(window);
auto* existing = SpellCheckerHolder::FromRenderFrame(render_frame);
if (existing)
existing->UnsetAndDestroy();
// Set spellchecker for all live frames in the same process or
// in the sandbox mode for all live sub frames to this WebFrame.
auto spell_check_client =
std::make_unique<SpellCheckClient>(language, args->isolate(), provider);
FrameSetSpellChecker spell_checker(spell_check_client.get(), render_frame);
// Attach the spell checker to RenderFrame.
new SpellCheckerHolder(render_frame, std::move(spell_check_client));
2014-12-18 21:54:01 +00:00
}
void InsertText(v8::Local<v8::Value> window, const std::string& text) {
blink::WebFrame* web_frame = GetRenderFrame(window)->GetWebFrame();
if (web_frame->IsWebLocalFrame()) {
web_frame->ToWebLocalFrame()
->FrameWidget()
->GetActiveWebInputMethodController()
->CommitText(blink::WebString::FromUTF8(text),
blink::WebVector<blink::WebImeTextSpan>(),
blink::WebRange(), 0);
}
2016-01-13 03:21:16 +00:00
}
void InsertCSS(v8::Local<v8::Value> window, const std::string& css) {
blink::WebFrame* web_frame = GetRenderFrame(window)->GetWebFrame();
if (web_frame->IsWebLocalFrame()) {
web_frame->ToWebLocalFrame()->GetDocument().InsertStyleSheet(
blink::WebString::FromUTF8(css));
}
2016-12-19 23:50:47 +00:00
}
void ExecuteJavaScript(mate::Arguments* args,
v8::Local<v8::Value> window,
const base::string16& code) {
bool has_user_gesture = false;
args->GetNext(&has_user_gesture);
ScriptExecutionCallback::CompletionCallback completion_callback;
args->GetNext(&completion_callback);
2016-05-23 01:59:39 +00:00
std::unique_ptr<blink::WebScriptExecutionCallback> callback(
new ScriptExecutionCallback(completion_callback));
GetRenderFrame(window)->GetWebFrame()->RequestExecuteScriptAndReturnValue(
2017-06-16 20:42:33 +00:00
blink::WebScriptSource(blink::WebString::FromUTF16(code)),
2018-04-18 01:55:30 +00:00
has_user_gesture, callback.release());
2016-01-13 04:11:46 +00:00
}
void ExecuteJavaScriptInIsolatedWorld(
mate::Arguments* args,
v8::Local<v8::Value> window,
2017-12-19 01:42:36 +00:00
int world_id,
const std::vector<mate::Dictionary>& scripts) {
std::vector<blink::WebScriptSource> sources;
for (const auto& script : scripts) {
base::string16 code;
base::string16 url;
int start_line = 1;
script.Get("url", &url);
script.Get("startLine", &start_line);
if (!script.Get("code", &code)) {
args->ThrowError("Invalid 'code'");
return;
}
2018-04-18 01:55:30 +00:00
sources.emplace_back(
blink::WebScriptSource(blink::WebString::FromUTF16(code),
blink::WebURL(GURL(url)), start_line));
}
bool has_user_gesture = false;
args->GetNext(&has_user_gesture);
blink::WebLocalFrame::ScriptExecutionType scriptExecutionType =
2017-12-19 01:42:36 +00:00
blink::WebLocalFrame::kSynchronous;
args->GetNext(&scriptExecutionType);
ScriptExecutionCallback::CompletionCallback completion_callback;
args->GetNext(&completion_callback);
std::unique_ptr<blink::WebScriptExecutionCallback> callback(
new ScriptExecutionCallback(completion_callback));
GetRenderFrame(window)->GetWebFrame()->RequestExecuteScriptInIsolatedWorld(
world_id, &sources.front(), sources.size(), has_user_gesture,
scriptExecutionType, callback.release());
}
void SetIsolatedWorldInfo(v8::Local<v8::Value> window,
int world_id,
const mate::Dictionary& options,
mate::Arguments* args) {
std::string origin_url, security_policy, name;
options.Get("securityOrigin", &origin_url);
options.Get("csp", &security_policy);
options.Get("name", &name);
if (!security_policy.empty() && origin_url.empty()) {
args->ThrowError(
"If csp is specified, securityOrigin should also be specified");
return;
}
blink::WebIsolatedWorldInfo info;
info.security_origin = blink::WebSecurityOrigin::CreateFromString(
blink::WebString::FromUTF8(origin_url));
info.content_security_policy = blink::WebString::FromUTF8(security_policy);
info.human_readable_name = blink::WebString::FromUTF8(name);
GetRenderFrame(window)->GetWebFrame()->SetIsolatedWorldInfo(world_id, info);
}
blink::WebCache::ResourceTypeStats GetResourceUsage(v8::Isolate* isolate) {
2016-05-12 20:56:46 +00:00
blink::WebCache::ResourceTypeStats stats;
2017-06-16 20:42:33 +00:00
blink::WebCache::GetResourceTypeStats(&stats);
2016-05-13 17:43:08 +00:00
return stats;
2016-05-12 20:56:46 +00:00
}
void ClearCache(v8::Isolate* isolate) {
2016-05-12 21:36:49 +00:00
isolate->IdleNotificationDeadline(0.5);
2017-06-16 20:42:33 +00:00
blink::WebCache::Clear();
2016-05-12 21:36:49 +00:00
base::MemoryPressureListener::NotifyMemoryPressure(
2018-04-18 01:55:30 +00:00
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
2016-05-12 21:36:49 +00:00
}
v8::Local<v8::Value> FindFrameByRoutingId(v8::Isolate* isolate,
v8::Local<v8::Value> window,
int routing_id) {
content::RenderFrame* render_frame =
content::RenderFrame::FromRoutingID(routing_id);
if (render_frame)
return render_frame->GetWebFrame()->MainWorldScriptContext()->Global();
2018-01-09 19:41:40 +00:00
else
return v8::Null(isolate);
2018-01-09 19:41:40 +00:00
}
v8::Local<v8::Value> GetOpener(v8::Isolate* isolate,
v8::Local<v8::Value> window) {
blink::WebFrame* frame = GetRenderFrame(window)->GetWebFrame()->Opener();
2018-01-09 19:41:40 +00:00
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
2018-01-09 19:41:40 +00:00
else
return v8::Null(isolate);
2018-01-09 19:41:40 +00:00
}
// Don't name it as GetParent, Windows has API with same name.
v8::Local<v8::Value> GetFrameParent(v8::Isolate* isolate,
v8::Local<v8::Value> window) {
blink::WebFrame* frame = GetRenderFrame(window)->GetWebFrame()->Parent();
2018-01-09 19:41:40 +00:00
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
2018-01-09 19:41:40 +00:00
else
return v8::Null(isolate);
2018-01-09 19:41:40 +00:00
}
v8::Local<v8::Value> GetTop(v8::Isolate* isolate, v8::Local<v8::Value> window) {
blink::WebFrame* frame = GetRenderFrame(window)->GetWebFrame()->Top();
2018-01-09 19:41:40 +00:00
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
2018-01-09 19:41:40 +00:00
else
return v8::Null(isolate);
2018-01-09 19:41:40 +00:00
}
v8::Local<v8::Value> GetFirstChild(v8::Isolate* isolate,
v8::Local<v8::Value> window) {
blink::WebFrame* frame = GetRenderFrame(window)->GetWebFrame()->FirstChild();
2018-01-09 19:41:40 +00:00
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
2018-01-09 19:41:40 +00:00
else
return v8::Null(isolate);
2018-01-09 19:41:40 +00:00
}
v8::Local<v8::Value> GetNextSibling(v8::Isolate* isolate,
v8::Local<v8::Value> window) {
blink::WebFrame* frame = GetRenderFrame(window)->GetWebFrame()->NextSibling();
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
2018-01-09 19:41:40 +00:00
else
return v8::Null(isolate);
2018-01-09 19:41:40 +00:00
}
v8::Local<v8::Value> GetFrameForSelector(v8::Isolate* isolate,
v8::Local<v8::Value> window,
const std::string& selector) {
blink::WebElement element =
GetRenderFrame(window)->GetWebFrame()->GetDocument().QuerySelector(
blink::WebString::FromUTF8(selector));
if (element.IsNull()) // not found
return v8::Null(isolate);
2018-01-09 19:41:40 +00:00
blink::WebFrame* frame = blink::WebFrame::FromFrameOwnerElement(element);
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
else
return v8::Null(isolate);
}
v8::Local<v8::Value> FindFrameByName(v8::Isolate* isolate,
v8::Local<v8::Value> window,
const std::string& name) {
blink::WebFrame* frame =
GetRenderFrame(window)->GetWebFrame()->FindFrameByName(
blink::WebString::FromUTF8(name));
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
else
return v8::Null(isolate);
}
int GetRoutingId(v8::Local<v8::Value> window) {
return GetRenderFrame(window)->GetRoutingID();
2014-10-24 10:24:12 +00:00
}
} // namespace api
} // namespace atom
namespace {
2018-04-18 01:55:30 +00:00
void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv) {
using namespace atom::api; // NOLINT(build/namespaces)
2014-10-24 10:24:12 +00:00
v8::Isolate* isolate = context->GetIsolate();
mate::Dictionary dict(isolate, exports);
dict.SetMethod("setName", &SetName);
dict.SetMethod("setZoomLevel", &SetZoomLevel);
dict.SetMethod("getZoomLevel", &GetZoomLevel);
dict.SetMethod("setZoomFactor", &SetZoomFactor);
dict.SetMethod("getZoomFactor", &GetZoomFactor);
dict.SetMethod("setVisualZoomLevelLimits", &SetVisualZoomLevelLimits);
dict.SetMethod("setLayoutZoomLevelLimits", &SetLayoutZoomLevelLimits);
dict.SetMethod("allowGuestViewElementDefinition",
&AllowGuestViewElementDefinition);
dict.SetMethod("getWebFrameId", &GetWebFrameId);
dict.SetMethod("setSpellCheckProvider", &SetSpellCheckProvider);
dict.SetMethod("insertText", &InsertText);
dict.SetMethod("insertCSS", &InsertCSS);
dict.SetMethod("executeJavaScript", &ExecuteJavaScript);
dict.SetMethod("executeJavaScriptInIsolatedWorld",
&ExecuteJavaScriptInIsolatedWorld);
dict.SetMethod("setIsolatedWorldInfo", &SetIsolatedWorldInfo);
dict.SetMethod("getResourceUsage", &GetResourceUsage);
dict.SetMethod("clearCache", &ClearCache);
dict.SetMethod("_findFrameByRoutingId", &FindFrameByRoutingId);
dict.SetMethod("_getFrameForSelector", &GetFrameForSelector);
dict.SetMethod("_findFrameByName", &FindFrameByName);
dict.SetMethod("_getOpener", &GetOpener);
dict.SetMethod("_getParent", &GetFrameParent);
dict.SetMethod("_getTop", &GetTop);
dict.SetMethod("_getFirstChild", &GetFirstChild);
dict.SetMethod("_getNextSibling", &GetNextSibling);
dict.SetMethod("_getRoutingId", &GetRoutingId);
2014-10-24 10:24:12 +00:00
}
} // namespace
NODE_LINKED_MODULE_CONTEXT_AWARE(atom_renderer_web_frame, Initialize)