提交 f0c80dc8 编写于 作者: A Adam Barth

Add beginnings of an inspector backend for Sky

R=eseidel@chromium.org

Review URL: https://codereview.chromium.org/687673003
上级 70c382c0
......@@ -13,6 +13,7 @@ group("sky") {
"//sky/engine/platform:platform_unittests",
"//sky/engine/web:sky_unittests",
"//sky/engine/wtf:unittests",
"//sky/framework/inspector/server",
"//sky/viewer",
]
if (use_aura) {
......
<script>
function ConsoleAgent() {
}
ConsoleAgent.prototype.enable = function() {
return {
result: true,
};
};
this.exports = ConsoleAgent;
</script>
<script>
function DOMAgent(delegate) {
this.enabled = false;
this.delegate_ = delegate;
this.nextNodeId_ = 1;
this.nodeToId_ = new Map();
this.idToNode_ = new Map();
}
DOMAgent.prototype.getIdForNode_ = function(node) {
if (this.nodeToId_.has(node))
return this.nodeToId_.get(node);
var id = this.nextNodeId_++;
this.nodeToId_.set(node, id);
this.idToNode_.set(id, node);
return id;
};
DOMAgent.prototype.serializeChildren_ = function(node) {
var children = [];
for (var child = node.firstChild; child; child = child.nextSibling) {
var record = this.serializeNode_(child);
if (record)
children.push(record);
}
return children;
};
DOMAgent.prototype.serializeAttributes_ = function(element) {
var attributes = [];
var attrs = element.attributes;
for (var i = 0; i < attrs.length; ++i) {
var attr = attrs[i];
attributes.push(attr.name);
attributes.push(attr.value);
}
return attributes;
};
DOMAgent.prototype.serializeNode_ = function(node) {
var id = this.getIdForNode_(node);
var record = {
nodeId: id,
};
var isContainer = false;
if (node instanceof Element) {
isContainer = true;
record.nodeType = 1;
record.nodeName = node.tagName;
record.localName = node.tagName;
record.nodeValue = "";
record.attributes = this.serializeAttributes_(node);
} else if (node instanceof Text) {
record.nodeType = 3;
record.nodeName = "#text";
var nodeValue = node.data;
if (!nodeValue.trim())
return null;
record.nodeValue = nodeValue;
} else if (node instanceof Comment) {
record.nodeType = 8;
record.nodeName = "#comment";
record.nodeValue = node.data;
} else if (node instanceof Document) {
isContainer = true;
record.nodeType = 9;
record.nodeName = "#document";
record.localName = "";
record.nodeValue = "";
record.documentURL = node.URL;
record.baseURL = node.baseURI;
} else if (node instanceof DocumentFragment) {
isContainer = true;
record.nodeType = 11;
record.nodeName = "#document-fragment";
record.localName = "";
record.nodeValue = "";
} else {
console.log("Unknown node type");
return null;
}
if (isContainer) {
var children = this.serializeChildren_(node);
if (children.length) {
record.childNodeCount = children.length;
record.children = children;
}
}
return record;
};
DOMAgent.prototype.enable = function() {
this.enabled = true;
this.observer_ = new MutationObserver(this.mutationCallback_.bind(this));
this.observer_.observe(document, {
childList: true,
attributes: true,
characterData: true,
subtree : true,
});
};
DOMAgent.prototype.getDocument = function() {
return {
root: this.serializeNode_(document),
};
};
DOMAgent.prototype.hideHighlight = function() {
};
DOMAgent.prototype.highlightNode = function() {
};
DOMAgent.prototype.mutationCallback_ = function(mutationRecords) {
for (var i = 0; i < mutationRecords.length; ++i) {
var record = mutationRecords[i];
var type = record.type;
var target = record.target;
var nodeId = this.getIdForNode_(target);
if (type == "attributes") {
var attributeName = record.attributeName;
if (target.hasAttribute(attributeName)) {
this.delegate_.sendMessage("DOM.attributeModified", {
nodeId: nodeId,
name: attributeName,
value: target.getAttribute(attributeName),
});
} else {
this.delegate_.sendMessage("DOM.attributeRemoved", {
nodeId: nodeId,
name: attributeName,
});
}
} else if (type == "characterData") {
this.delegate_.sendMessage("DOM.characterDataModified", {
nodeId: nodeId,
characterData: target.data,
});
} else if (type == "childList") {
// FIXME: If this subtree isn't expanded, we only need to send across the
// {"method":"DOM.childNodeCountUpdated","params":"nodeId":648,"childNodeCount":2}
Array.prototype.forEach.call(record.removedNodes, function(node) {
this.delegate_.sendMessage("DOM.childNodeRemoved", {
parentNodeId: nodeId,
nodeId: this.getIdForNode_(node),
});
}.bind(this));
Array.prototype.forEach.call(record.addedNodes, function(node) {
var previousNodeId = node.previousSibling ? this.getIdForNode_(node.previousSibling) : 0;
this.delegate_.sendMessage("DOM.childNodeInserted", {
parentNodeId: nodeId,
previousNodeId: previousNodeId,
node: this.serializeNode_(node),
});
}.bind(this));
}
}
};
this.exports = DOMAgent;
</script>
<link rel="import" href="/mojo/public/html/connection.html" as="connection" />
<link rel="import" href="/mojo/public/html/core.html" as="core" />
<link rel="import" href="/mojo/public/html/support.html" as="support" />
<link rel="import" href="/sky/framework/inspector/server/inspector.mojom.html" as="inspector" />
<link rel="import" href="console-agent.sky" as="ConsoleAgent" />
<link rel="import" href="dom-agent.sky" as="DOMAgent" />
<link rel="import" href="page-agent.sky" as="PageAgent" />
<link rel="import" href="worker-agent.sky" as="WorkerAgent" />
<script>
function InspectorBackend(frontend) {
this.frontend = frontend;
this.agents = {
Console: new ConsoleAgent(),
DOM: new DOMAgent(this),
Page: new PageAgent(),
Worker: new WorkerAgent(),
};
}
InspectorBackend.prototype = Object.create(
inspector.InspectorBackend.stubClass.prototype);
InspectorBackend.prototype.onConnect = function() {
};
InspectorBackend.prototype.onDisconnect = function() {
};
InspectorBackend.prototype.dispatch_ = function(descriptor, params) {
var parsed = descriptor.split('.');
var agentName = parsed[0];
var methodName = parsed[1];
if (!(agentName in this.agents)) {
console.log("InspectorBackend cannot find agent " + agentName);
return {};
}
var agent = this.agents[agentName];
if (!(methodName in agent)) {
console.log("InspectorBackend cannot find method " +
methodName + " on agent " + agentName);
return {};
}
try {
return agent[methodName](params);
} catch(ex) {
console.log(descriptor + ": " + ex);
}
};
InspectorBackend.prototype.onMessage = function(data) {
var message = JSON.parse(data);
var result = this.dispatch_(message.method, message.params);
var response = {
id: message.id,
};
if (typeof result !== "undefined")
response.result = result;
this.frontend.sendMessage(JSON.stringify(response));
};
InspectorBackend.prototype.sendMessage = function(method, params) {
var message = JSON.stringify({
method: method,
params: params,
});
this.frontend.sendMessage(message);
};
var frontendHandle = internals.connectToService(
"mojo:sky_inspector_server", inspector.InspectorFrontend.name);
window.frontendConnection = new connection.Connection(
frontendHandle,
InspectorBackend,
inspector.InspectorFrontend.proxyClass);
window.frontend = frontendConnection.remote;
frontend.listen(9898);
</script>
<script>
function PageAgent() {
}
PageAgent.prototype.enable = function() {
};
PageAgent.prototype.canScreencast = function() {
return {
result: false
};
};
PageAgent.prototype.canEmulate = function() {
return {
result: false
};
};
this.exports = PageAgent;
</script>
# Copyright 2014 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.
import("//mojo/public/tools/bindings/mojom.gni")
group("server") {
testonly = true
deps = [
":sky_inspector_server",
]
}
shared_library("sky_inspector_server") {
sources = [
"inspector_frontend_impl.cc",
"inspector_frontend_impl.h",
"server.cc",
]
deps = [
"//base",
"//mojo/application",
"//mojo/public/c/system:for_shared_library",
"//mojo/public/cpp/bindings",
"//mojo/public/cpp/utility",
"//net:http_server",
":bindings",
]
}
mojom("bindings") {
sources = [
"inspector.mojom",
]
}
// Copyright 2014 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.
module sky {
[Client=InspectorBackend]
interface InspectorFrontend {
Listen(int32 port);
SendMessage(string message);
};
[Client=InspectorFrontend]
interface InspectorBackend {
OnConnect();
OnDisconnect();
OnMessage(string message);
};
}
// Copyright 2014 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 "sky/framework/inspector/server/inspector_frontend_impl.h"
#include "base/lazy_instance.h"
#include "net/server/http_server.h"
#include "net/socket/tcp_server_socket.h"
namespace sky {
namespace inspector {
namespace {
const int kNotConnected = -1;
static base::LazyInstance<std::map<int, InspectorFronendImpl*>> g_servers =
LAZY_INSTANCE_INITIALIZER;
}
InspectorFronendImpl::InspectorFronendImpl()
: connection_id_(kNotConnected) {
}
InspectorFronendImpl::~InspectorFronendImpl() {
StopListening();
}
void InspectorFronendImpl::OnConnect(int connection_id) {
}
void InspectorFronendImpl::OnHttpRequest(
int connection_id, const net::HttpServerRequestInfo& info) {
web_server_->Send500(connection_id, "websockets protocol only");
}
void InspectorFronendImpl::OnWebSocketRequest(
int connection_id, const net::HttpServerRequestInfo& info) {
if (connection_id_ != kNotConnected) {
web_server_->Close(connection_id);
return;
}
web_server_->AcceptWebSocket(connection_id, info);
connection_id_ = connection_id;
client()->OnConnect();
}
void InspectorFronendImpl::OnWebSocketMessage(
int connection_id, const std::string& data) {
DCHECK_EQ(connection_id, connection_id_);
client()->OnMessage(data);
}
void InspectorFronendImpl::OnClose(int connection_id) {
if (connection_id != connection_id_)
return;
connection_id_ = kNotConnected;
client()->OnDisconnect();
}
void InspectorFronendImpl::Listen(int32_t port) {
Register(port);
scoped_ptr<net::ServerSocket> server_socket(
new net::TCPServerSocket(NULL, net::NetLog::Source()));
server_socket->ListenWithAddressAndPort("0.0.0.0", port, 1);
web_server_.reset(new net::HttpServer(server_socket.Pass(), this));
}
void InspectorFronendImpl::StopListening() {
if (!web_server_)
return;
web_server_.reset();
Unregister();
}
void InspectorFronendImpl::Register(int port) {
auto& servers = g_servers.Get();
auto iter = servers.find(port);
if (iter != servers.end())
iter->second->StopListening();
DCHECK(servers.find(port) == servers.end());
servers[port] = this;
port_ = port;
}
void InspectorFronendImpl::Unregister() {
DCHECK(g_servers.Get().find(port_)->second == this);
g_servers.Get().erase(port_);
port_ = kNotConnected;
}
void InspectorFronendImpl::SendMessage(const mojo::String& message) {
if (connection_id_ == kNotConnected)
return;
web_server_->SendOverWebSocket(connection_id_, message);
}
} // namespace inspector
} // namespace sky
// Copyright 2014 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.
#ifndef SKY_FRAMEWORK_INSPECTOR_SERVER_INSPECTOR_FRONTEND_IMPL_H_
#define SKY_FRAMEWORK_INSPECTOR_SERVER_INSPECTOR_FRONTEND_IMPL_H_
#include "mojo/public/cpp/application/interface_factory_impl.h"
#include "mojo/public/cpp/bindings/interface_impl.h"
#include "net/server/http_server.h"
#include "net/socket/tcp_server_socket.h"
#include "sky/framework/inspector/server/inspector.mojom.h"
namespace sky {
namespace inspector {
class InspectorFronendImpl : public mojo::InterfaceImpl<InspectorFrontend>,
public net::HttpServer::Delegate {
public:
InspectorFronendImpl();
virtual ~InspectorFronendImpl();
private:
// From net::HttpServer::Delegate
virtual void OnConnect(int connection_id) override;
virtual void OnHttpRequest(
int connection_id, const net::HttpServerRequestInfo& info) override;
virtual void OnWebSocketRequest(
int connection_id, const net::HttpServerRequestInfo& info) override;
virtual void OnWebSocketMessage(
int connection_id, const std::string& data) override;
virtual void OnClose(int connection_id) override;
// From InspectorFronend
virtual void Listen(int32_t port) override;
virtual void SendMessage(const mojo::String&) override;
void StopListening();
void Register(int port);
void Unregister();
int port_;
int connection_id_;
scoped_ptr<net::HttpServer> web_server_;
MOJO_DISALLOW_COPY_AND_ASSIGN(InspectorFronendImpl);
};
typedef mojo::InterfaceFactoryImpl<
InspectorFronendImpl> InspectorFronendFactory;
} // namespace tester
} // namespace sky
#endif // SKY_FRAMEWORK_INSPECTOR_SERVER_INSPECTOR_FRONTEND_IMPL_H_
// Copyright 2014 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 "mojo/application/application_runner_chromium.h"
#include "mojo/public/c/system/main.h"
#include "mojo/public/cpp/application/application_delegate.h"
#include "mojo/public/cpp/application/application_impl.h"
#include "sky/framework/inspector/server/inspector_frontend_impl.h"
namespace sky {
namespace inspector {
class Server : public mojo::ApplicationDelegate {
public:
Server() {}
virtual ~Server() {}
private:
// Overridden from mojo::ApplicationDelegate:
virtual void Initialize(mojo::ApplicationImpl* app) override {
}
virtual bool ConfigureIncomingConnection(
mojo::ApplicationConnection* connection) override {
connection->AddService(&fronend_factory_);
return true;
}
InspectorFronendFactory fronend_factory_;
DISALLOW_COPY_AND_ASSIGN(Server);
};
} // namespace inspector
} // namespace sky
MojoResult MojoMain(MojoHandle shell_handle) {
mojo::ApplicationRunnerChromium runner(new sky::inspector::Server);
runner.set_message_loop_type(base::MessageLoop::TYPE_IO);
return runner.Run(shell_handle);
}
<script>
function WorkerAgent() {
}
WorkerAgent.prototype.canInspectWorkers = function() {
return {
result: false
};
};
this.exports = WorkerAgent;
</script>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册