Merge pull request #4080 from atom/insert-text

Add insertText API for webFrame, webContents and webview tag
This commit is contained in:
Cheng Zhao 2016-01-13 16:17:49 +08:00
commit 884a8a753f
16 changed files with 137 additions and 120 deletions

View file

@ -33,7 +33,6 @@
#include "chrome/browser/printing/print_view_manager_basic.h"
#include "chrome/browser/printing/print_preview_message_handler.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_plugin_guest_manager.h"
#include "content/public/browser/favicon_status.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/navigation_details.h"
@ -75,15 +74,6 @@ void SetUserAgentInIO(scoped_refptr<net::URLRequestContextGetter> getter,
user_agent));
}
bool NotifyZoomLevelChanged(
double level, content::WebContents* guest_web_contents) {
guest_web_contents->SendToAllFrames(
new AtomViewMsg_SetZoomLevel(MSG_ROUTING_NONE, level));
// Return false to iterate over all guests.
return false;
}
} // namespace
namespace mate {
@ -290,15 +280,11 @@ WebContents::WebContents(v8::Isolate* isolate,
}
WebContents::~WebContents() {
if (type_ == WEB_VIEW && managed_web_contents()) {
// When force destroying the "destroyed" event is not emitted.
// The webview's lifetime is completely controlled by GuestViewManager, so
// it is always destroyed by calling webview.destroy(), we need to make
// sure the "destroyed" event is emitted manually.
if (type_ == WEB_VIEW && managed_web_contents())
WebContentsDestroyed();
guest_delegate_->Destroy();
Observe(nullptr);
DestroyWebContents();
}
}
bool WebContents::AddMessageToConsole(content::WebContents* source,
@ -625,7 +611,6 @@ bool WebContents::OnMessageReceived(const IPC::Message& message) {
IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message, OnRendererMessage)
IPC_MESSAGE_HANDLER_DELAY_REPLY(AtomViewHostMsg_Message_Sync,
OnRendererMessageSync)
IPC_MESSAGE_HANDLER(AtomViewHostMsg_ZoomLevelChanged, OnZoomLevelChanged)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
@ -635,7 +620,15 @@ bool WebContents::OnMessageReceived(const IPC::Message& message) {
void WebContents::WebContentsDestroyed() {
// The RenderViewDeleted was not called when the WebContents is destroyed.
RenderViewDeleted(web_contents()->GetRenderViewHost());
// This event is only for internal use, which is emitted when WebContents is
// being destroyed.
Emit("will-destroy");
// Cleanup relationships with other parts.
RemoveFromWeakMap();
if (type_ == WEB_VIEW)
guest_delegate_->Destroy();
// We can not call Destroy here because we need to call Emit first, but we
// also do not want any method to be used, so just mark as destroyed here.
@ -756,11 +749,6 @@ bool WebContents::SavePage(const base::FilePath& full_file_path,
return handler->Handle(full_file_path, save_type);
}
void WebContents::ExecuteJavaScript(const base::string16& code,
bool has_user_gesture) {
Send(new AtomViewMsg_ExecuteJavaScript(routing_id(), code, has_user_gesture));
}
void WebContents::OpenDevTools(mate::Arguments* args) {
if (type_ == REMOTE)
return;
@ -1093,7 +1081,6 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
.SetMethod("getUserAgent", &WebContents::GetUserAgent)
.SetMethod("insertCSS", &WebContents::InsertCSS)
.SetMethod("savePage", &WebContents::SavePage)
.SetMethod("_executeJavaScript", &WebContents::ExecuteJavaScript)
.SetMethod("openDevTools", &WebContents::OpenDevTools)
.SetMethod("closeDevTools", &WebContents::CloseDevTools)
.SetMethod("isDevToolsOpened", &WebContents::IsDevToolsOpened)
@ -1160,15 +1147,6 @@ void WebContents::OnRendererMessageSync(const base::string16& channel,
EmitWithSender(base::UTF16ToUTF8(channel), web_contents(), message, args);
}
void WebContents::OnZoomLevelChanged(double level) {
auto manager = web_contents()->GetBrowserContext()->GetGuestManager();
if (!manager)
return;
manager->ForEachGuest(web_contents(),
base::Bind(&NotifyZoomLevelChanged,
level));
}
// static
mate::Handle<WebContents> WebContents::CreateFrom(
v8::Isolate* isolate, content::WebContents* web_contents) {

View file

@ -74,8 +74,6 @@ class WebContents : public mate::TrackableObject<WebContents>,
bool SavePage(const base::FilePath& full_file_path,
const content::SavePageType& save_type,
const SavePageHandler::SavePageCallback& callback);
void ExecuteJavaScript(const base::string16& code,
bool has_user_gesture);
void OpenDevTools(mate::Arguments* args);
void CloseDevTools();
bool IsDevToolsOpened();
@ -265,10 +263,6 @@ class WebContents : public mate::TrackableObject<WebContents>,
const base::ListValue& args,
IPC::Message* message);
// Called when guests need to be notified of
// embedders' zoom level change.
void OnZoomLevelChanged(double level);
v8::Global<v8::Value> session_;
v8::Global<v8::Value> devtools_web_contents_;

View file

@ -1,3 +1,5 @@
'use strict';
var EventEmitter, Menu, NavigationController, PDFPageSize, binding, deprecate, getNextId, ipcMain, nextId, ref, session, wrapWebContents,
slice = [].slice;
@ -53,6 +55,15 @@ PDFPageSize = {
}
};
// Following methods are mapped to webFrame.
const webFrameMethods = [
'executeJavaScript',
'insertText',
'setZoomFactor',
'setZoomLevel',
'setZoomLevelLimits',
];
wrapWebContents = function(webContents) {
/* webContents is an EventEmitter. */
@ -66,21 +77,6 @@ wrapWebContents = function(webContents) {
return this._send(channel, slice.call(args));
};
/*
Make sure webContents.executeJavaScript would run the code only when the
web contents has been loaded.
*/
webContents.executeJavaScript = function(code, hasUserGesture) {
if (hasUserGesture == null) {
hasUserGesture = false;
}
if (this.getURL() && !this.isLoading()) {
return this._executeJavaScript(code, hasUserGesture);
} else {
return webContents.once('did-finish-load', this._executeJavaScript.bind(this, code, hasUserGesture));
}
};
/* The navigation controller. */
controller = new NavigationController(webContents);
ref1 = NavigationController.prototype;
@ -95,6 +91,24 @@ wrapWebContents = function(webContents) {
}
}
// Mapping webFrame methods.
for (let method of webFrameMethods) {
webContents[method] = function() {
let args = Array.prototype.slice.call(arguments);
this.send('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, args);
};
}
// Make sure webContents.executeJavaScript would run the code only when the
// webContents has been loaded.
const executeJavaScript = webContents.executeJavaScript;
webContents.executeJavaScript = function(code, hasUserGesture) {
if (this.getURL() && !this.isLoading())
return executeJavaScript.call(this, code, hasUserGesture);
else
return this.once('did-finish-load', executeJavaScript.bind(this, code, hasUserGesture));
};
/* Dispatch IPC messages to the ipc module. */
webContents.on('ipc-message', function(event, packed) {
var args, channel;

View file

@ -52,7 +52,7 @@ createGuest = function(embedder, params) {
};
/* Destroy guest when the embedder is gone or navigated. */
destroyEvents = ['destroyed', 'crashed', 'did-navigate'];
destroyEvents = ['will-destroy', 'crashed', 'did-navigate'];
destroy = function() {
if (guestInstances[id] != null) {
return destroyGuest(embedder, id);

View file

@ -30,20 +30,10 @@ IPC_SYNC_MESSAGE_ROUTED2_1(AtomViewHostMsg_Message_Sync,
base::ListValue /* arguments */,
base::string16 /* result (in JSON) */)
IPC_MESSAGE_ROUTED1(AtomViewHostMsg_ZoomLevelChanged,
double /* level */)
IPC_MESSAGE_ROUTED1(AtomViewMsg_SetZoomLevel,
double /* level */)
IPC_MESSAGE_ROUTED2(AtomViewMsg_Message,
base::string16 /* channel */,
base::ListValue /* arguments */)
IPC_MESSAGE_ROUTED2(AtomViewMsg_ExecuteJavaScript,
base::string16 /* code */,
bool /* has user gesture */)
// Sent by the renderer when the draggable regions are updated.
IPC_MESSAGE_ROUTED1(AtomViewHostMsg_UpdateDraggableRegions,
std::vector<atom::DraggableRegion> /* regions */)

View file

@ -4,7 +4,7 @@
#include "atom/renderer/api/atom_api_web_frame.h"
#include "atom/common/api/api_messages.h"
#include "atom/common/api/event_emitter_caller.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/gfx_converter.h"
#include "atom/common/native_mate_converters/string16_converter.h"
@ -15,6 +15,8 @@
#include "native_mate/object_template_builder.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebScopedUserGesture.h"
#include "third_party/WebKit/public/web/WebScriptSource.h"
#include "third_party/WebKit/public/web/WebSecurityPolicy.h"
#include "third_party/WebKit/public/web/WebView.h"
@ -36,11 +38,9 @@ void WebFrame::SetName(const std::string& name) {
}
double WebFrame::SetZoomLevel(double level) {
auto render_view = content::RenderView::FromWebView(web_frame_->view());
// Notify guests if any for zoom level change.
render_view->Send(
new AtomViewHostMsg_ZoomLevelChanged(MSG_ROUTING_NONE, level));
return web_frame_->view()->setZoomLevel(level);
double ret = web_frame_->view()->setZoomLevel(level);
mate::EmitEvent(isolate(), GetWrapper(isolate()), "zoom-level-changed", ret);
return ret;
}
double WebFrame::GetZoomLevel() const {
@ -116,6 +116,19 @@ void WebFrame::RegisterURLSchemeAsPrivileged(const std::string& scheme) {
privileged_scheme);
}
void WebFrame::InsertText(const std::string& text) {
web_frame_->insertText(blink::WebString::fromUTF8(text));
}
void WebFrame::ExecuteJavaScript(const base::string16& code,
mate::Arguments* args) {
bool has_user_gesture = false;
args->GetNext(&has_user_gesture);
scoped_ptr<blink::WebScopedUserGesture> gesture(
has_user_gesture ? new blink::WebScopedUserGesture : nullptr);
web_frame_->executeScriptAndReturnValue(blink::WebScriptSource(code));
}
mate::ObjectTemplateBuilder WebFrame::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
return mate::ObjectTemplateBuilder(isolate)
@ -136,7 +149,9 @@ mate::ObjectTemplateBuilder WebFrame::GetObjectTemplateBuilder(
.SetMethod("registerURLSchemeAsBypassingCSP",
&WebFrame::RegisterURLSchemeAsBypassingCSP)
.SetMethod("registerURLSchemeAsPrivileged",
&WebFrame::RegisterURLSchemeAsPrivileged);
&WebFrame::RegisterURLSchemeAsPrivileged)
.SetMethod("insertText", &WebFrame::InsertText)
.SetMethod("executeJavaScript", &WebFrame::ExecuteJavaScript);
}
// static

View file

@ -60,6 +60,12 @@ class WebFrame : public mate::Wrappable {
void RegisterURLSchemeAsBypassingCSP(const std::string& scheme);
void RegisterURLSchemeAsPrivileged(const std::string& scheme);
// Editing.
void InsertText(const std::string& text);
// Excecuting scripts.
void ExecuteJavaScript(const base::string16& code, mate::Arguments* args);
// mate::Wrappable:
virtual mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate);

View file

@ -1,16 +1,19 @@
var deprecate, webFrame;
'use strict';
deprecate = require('electron').deprecate;
const deprecate = require('electron').deprecate;
const EventEmitter = require('events').EventEmitter;
webFrame = process.atomBinding('web_frame').webFrame;
const webFrame = process.atomBinding('web_frame').webFrame;
// webFrame is an EventEmitter.
webFrame.__proto__ = EventEmitter.prototype;
/* Deprecated. */
// Lots of webview would subscribe to webFrame's events.
webFrame.setMaxListeners(0);
// Deprecated.
deprecate.rename(webFrame, 'registerUrlSchemeAsSecure', 'registerURLSchemeAsSecure');
deprecate.rename(webFrame, 'registerUrlSchemeAsBypassingCSP', 'registerURLSchemeAsBypassingCSP');
deprecate.rename(webFrame, 'registerUrlSchemeAsPrivileged', 'registerURLSchemeAsPrivileged');
module.exports = webFrame;

View file

@ -27,8 +27,6 @@
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebKit.h"
#include "third_party/WebKit/public/web/WebScopedUserGesture.h"
#include "third_party/WebKit/public/web/WebScriptSource.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "ui/base/resource/resource_bundle.h"
#include "native_mate/dictionary.h"
@ -115,8 +113,6 @@ bool AtomRenderViewObserver::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(AtomRenderViewObserver, message)
IPC_MESSAGE_HANDLER(AtomViewMsg_Message, OnBrowserMessage)
IPC_MESSAGE_HANDLER(AtomViewMsg_ExecuteJavaScript,
OnJavaScriptExecuteRequest)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
@ -152,22 +148,4 @@ void AtomRenderViewObserver::OnBrowserMessage(const base::string16& channel,
}
}
void AtomRenderViewObserver::OnJavaScriptExecuteRequest(
const base::string16& code, bool has_user_gesture) {
if (!document_created_)
return;
if (!render_view()->GetWebView())
return;
scoped_ptr<blink::WebScopedUserGesture> gesture(
has_user_gesture ? new blink::WebScopedUserGesture : nullptr);
v8::Isolate* isolate = blink::mainThreadIsolate();
v8::HandleScope handle_scope(isolate);
blink::WebFrame* frame = render_view()->GetWebView()->mainFrame();
frame->executeScriptAndReturnValue(blink::WebScriptSource(code));
}
} // namespace atom

View file

@ -32,8 +32,6 @@ class AtomRenderViewObserver : public content::RenderViewObserver {
void OnBrowserMessage(const base::string16& channel,
const base::ListValue& args);
void OnJavaScriptExecuteRequest(const base::string16& code,
bool has_user_gesture);
// Weak reference to renderer client.
AtomRendererClient* renderer_client_;

View file

@ -57,22 +57,6 @@ class AtomRenderFrameObserver : public content::RenderFrameObserver {
render_frame()->GetWebFrame(), context);
}
bool OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(AtomRenderFrameObserver, message)
IPC_MESSAGE_HANDLER(AtomViewMsg_SetZoomLevel, OnSetZoomLevel)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void OnSetZoomLevel(double level) {
auto view = render_frame()->GetWebFrame()->view();
if (view)
view->setZoomLevel(level);
}
private:
AtomRendererClient* renderer_client_;

View file

@ -1,3 +1,5 @@
'user strict';
var Module, arg, error, error1, events, globalPaths, i, len, nodeIntegration, path, pathname, preloadScript, ref, url, v8Util;
events = require('events');
@ -44,6 +46,13 @@ v8Util = process.atomBinding('v8_util');
v8Util.setHiddenValue(global, 'ipc', new events.EventEmitter);
// Use electron module after everything is ready.
const electron = require('electron');
// Call webFrame method.
electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, method, args) => {
electron.webFrame[method].apply(electron.webFrame, args);
});
/* Process command line arguments. */

View file

@ -1,3 +1,5 @@
'user strict';
var WebViewImpl, deprecate, getNextId, guestViewInternal, ipcRenderer, listener, nextId, ref, registerBrowserPluginElement, registerWebViewElement, remote, useCapture, v8Util, webFrame, webViewConstants,
hasProp = {}.hasOwnProperty,
slice = [].slice;
@ -39,6 +41,12 @@ WebViewImpl = (function() {
this.setupFocusPropagation();
this.viewInstanceId = getNextId();
shadowRoot.appendChild(this.browserPluginNode);
// Subscribe to host's zoom level changes.
this.onZoomLevelChanged = (zoomLevel) => {
this.webviewNode.setZoomLevel(zoomLevel);
}
webFrame.on('zoom-level-changed', this.onZoomLevelChanged);
}
WebViewImpl.prototype.createBrowserPluginNode = function() {
@ -57,6 +65,8 @@ WebViewImpl = (function() {
/* Resets some state upon reattaching <webview> element to the DOM. */
WebViewImpl.prototype.reset = function() {
// Unlisten the zoom-level-changed event.
webFrame.removeListener('zoom-level-changed', this.onZoomLevelChanged);
/*
If guestInstanceId is defined then the <webview> has navigated and has
@ -376,7 +386,16 @@ registerWebViewElement = function() {
/* Public-facing API methods. */
methods = ['getURL', 'getTitle', 'isLoading', 'isWaitingForResponse', 'stop', 'reload', 'reloadIgnoringCache', 'canGoBack', 'canGoForward', 'canGoToOffset', 'clearHistory', 'goBack', 'goForward', 'goToIndex', 'goToOffset', 'isCrashed', 'setUserAgent', 'getUserAgent', 'openDevTools', 'closeDevTools', 'isDevToolsOpened', 'isDevToolsFocused', 'inspectElement', 'setAudioMuted', 'isAudioMuted', 'undo', 'redo', 'cut', 'copy', 'paste', 'pasteAndMatchStyle', 'delete', 'selectAll', 'unselect', 'replace', 'replaceMisspelling', 'findInPage', 'stopFindInPage', 'getId', 'downloadURL', 'inspectServiceWorker', 'print', 'printToPDF'];
nonblockMethods = ['send', 'sendInputEvent', 'executeJavaScript', 'insertCSS'];
nonblockMethods = [
'executeJavaScript',
'insertCSS',
'insertText',
'send',
'sendInputEvent',
'setZoomFactor',
'setZoomLevel',
'setZoomLevelLimits',
];
/* Forward proto.foo* method calls to WebViewImpl.foo*. */
createBlockHandler = function(m) {

View file

@ -472,6 +472,12 @@ Executes the editing command `replace` in web page.
Executes the editing command `replaceMisspelling` in web page.
### `webContents.insertText(text)`
* `text` String
Inserts `text` to the focused element.
### `webContents.findInPage(text[, options])`
* `text` String - Content to be searched, must not be empty.

View file

@ -90,4 +90,21 @@ Content Security Policy.
Registers the `scheme` as secure, bypasses content security policy for resources,
allows registering ServiceWorker and supports fetch API.
### `webFrame.insertText(text)`
* `text` String
Inserts `text` to the focused element.
### `webFrame.executeJavaScript(code[, userGesture])`
* `code` String
* `userGesture` Boolean (optional) - Default is `false`.
Evaluates `code` in page.
In the browser window some HTML APIs like `requestFullScreen` can only be
invoked by a gesture from the user. Setting `userGesture` to `true` will remove
this limitation.
[spellchecker]: https://github.com/atom/node-spellchecker

View file

@ -352,6 +352,12 @@ Executes editing command `replace` in page.
Executes editing command `replaceMisspelling` in page.
### `<webview>.insertText(text)`
* `text` String
Inserts `text` to the focused element.
### `<webview>.findInPage(text[, options])`
* `text` String - Content to be searched, must not be empty.