aboutsummaryrefslogtreecommitdiffhomepage
path: root/platform_tools
diff options
context:
space:
mode:
authorGravatar borenet@google.com <borenet@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-05-06 12:50:00 +0000
committerGravatar borenet@google.com <borenet@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-05-06 12:50:00 +0000
commit6a98b8c0b5ffe1a23902cdf7e692f702b703eaeb (patch)
tree7c5b356f6fa4fb71876465f372eedc3d3ee25801 /platform_tools
parentf073b3332d7c9f7dba6bcf7eb93708593a90036c (diff)
Copy NaCl directory into trunk/platform_tools, adjust paths
(SkipBuildbotRuns) R=djsollen@google.com Review URL: https://codereview.chromium.org/14771017 git-svn-id: http://skia.googlecode.com/svn/trunk@9008 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'platform_tools')
-rw-r--r--platform_tools/nacl/SampleApp/SampleApp.nmf6
-rw-r--r--platform_tools/nacl/SampleApp/index.html71
-rw-r--r--platform_tools/nacl/debugger/debugger.css172
-rw-r--r--platform_tools/nacl/debugger/debugger.nmf6
-rw-r--r--platform_tools/nacl/debugger/icons/blank.pngbin0 -> 146 bytes
-rw-r--r--platform_tools/nacl/debugger/icons/breakpoint.pngbin0 -> 2247 bytes
-rw-r--r--platform_tools/nacl/debugger/icons/breakpoint_16x16.pngbin0 -> 1108 bytes
-rw-r--r--platform_tools/nacl/debugger/icons/delete.pngbin0 -> 1415 bytes
-rw-r--r--platform_tools/nacl/debugger/icons/inspector.pngbin0 -> 1960 bytes
-rw-r--r--platform_tools/nacl/debugger/icons/next.pngbin0 -> 938 bytes
-rw-r--r--platform_tools/nacl/debugger/icons/pause.pngbin0 -> 1181 bytes
-rw-r--r--platform_tools/nacl/debugger/icons/play.pngbin0 -> 1073 bytes
-rw-r--r--platform_tools/nacl/debugger/icons/previous.pngbin0 -> 943 bytes
-rw-r--r--platform_tools/nacl/debugger/icons/profile.pngbin0 -> 243 bytes
-rw-r--r--platform_tools/nacl/debugger/icons/reload.pngbin0 -> 1932 bytes
-rw-r--r--platform_tools/nacl/debugger/icons/rewind.pngbin0 -> 1105 bytes
-rw-r--r--platform_tools/nacl/debugger/icons/skia.pngbin0 -> 1054 bytes
-rw-r--r--platform_tools/nacl/debugger/index.html468
-rw-r--r--platform_tools/nacl/favicon.icobin0 -> 318 bytes
-rw-r--r--platform_tools/nacl/gclient.config13
-rwxr-xr-xplatform_tools/nacl/httpd.py212
-rw-r--r--platform_tools/nacl/index.html22
-rwxr-xr-xplatform_tools/nacl/nacl_make104
-rw-r--r--platform_tools/nacl/src/nacl_debugger.cpp221
-rw-r--r--platform_tools/nacl/src/nacl_interface.cpp111
-rw-r--r--platform_tools/nacl/src/nacl_sample.cpp212
-rw-r--r--platform_tools/nacl/tests/index.html78
-rw-r--r--platform_tools/nacl/tests/tests.nmf6
28 files changed, 1702 insertions, 0 deletions
diff --git a/platform_tools/nacl/SampleApp/SampleApp.nmf b/platform_tools/nacl/SampleApp/SampleApp.nmf
new file mode 100644
index 0000000000..7296c07885
--- /dev/null
+++ b/platform_tools/nacl/SampleApp/SampleApp.nmf
@@ -0,0 +1,6 @@
+{
+ "program": {
+ "x86-64": {"url": "../../out/nacl64/Debug/SampleApp"},
+ "x86-32": {"url": "../../out/nacl32/Debug/SampleApp"}
+ }
+}
diff --git a/platform_tools/nacl/SampleApp/index.html b/platform_tools/nacl/SampleApp/index.html
new file mode 100644
index 0000000000..cbdeefe029
--- /dev/null
+++ b/platform_tools/nacl/SampleApp/index.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<html>
+ <!--
+ Copyright 2013 Google Inc.
+
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+<head>
+
+ <title>Skia Sample App</title>
+
+ <script type="text/javascript">
+ "use strict";
+
+ var SkiaModule = null; // Global application object.
+
+ // Force a re-draw of the given element.
+ function refresh(elem) {
+ var old_display_style = elem.style.display;
+ elem.style.display = "none";
+ elem.style.display = old_display_style;
+ }
+
+ // When the module loads, begin running the application.
+ function moduleDidLoad() {
+ SkiaModule = document.getElementById("skia_nacl");
+ run();
+ }
+
+ function handleMessage(message_event) {
+ var skdebugf_cmd = "SkDebugf:";
+ if (message_event.data.indexOf(skdebugf_cmd) == 0) {
+ var msg_contents = message_event.data.slice(skdebugf_cmd.length)
+ console.log("Skia: " + msg_contents);
+ } else {
+ alert(message_event.data);
+ }
+ }
+
+ // Run the application.
+ function run() {
+ if (SkiaModule) {
+ var cmd = "init";
+ SkiaModule.postMessage(cmd);
+ } else {
+ alert("The Skia module has not properly loaded...");
+ }
+ }
+ </script>
+</head>
+<body>
+
+<h1>Skia Sample App</h1>
+<p>
+ <div id="listener">
+ <script type="text/javascript">
+ var listener = document.getElementById('listener');
+ listener.addEventListener('load', moduleDidLoad, true);
+ listener.addEventListener('message', handleMessage, true);
+ </script>
+
+ <embed name="nacl_module"
+ id="skia_nacl"
+ width=0 height=0
+ src="SampleApp.nmf"
+ type="application/x-nacl" />
+ </div>
+</p>
+</body>
+</html>
diff --git a/platform_tools/nacl/debugger/debugger.css b/platform_tools/nacl/debugger/debugger.css
new file mode 100644
index 0000000000..a63b7e0816
--- /dev/null
+++ b/platform_tools/nacl/debugger/debugger.css
@@ -0,0 +1,172 @@
+body {
+ width:100%;
+ height:100%;
+ margin:0px;
+ padding:0px;
+ background-color:#EEE;
+}
+
+div.single-line {
+ clear:both;
+}
+
+div.column-set {
+ width:100%;
+ height:100%;
+ display:table;
+ vertical-align:top;
+ border-collapse:collapse;
+ margin:0px;
+}
+
+div.column {
+ display:table-cell;
+ vertical-align:top;
+ margin:0px;
+}
+
+div.row-set {
+ width:100%;
+ height:100%;
+ display:table;
+ vertical-align:top;
+ border-collapse:collapse;
+ margin:0px;
+}
+
+div.row {
+ display:table-row;
+ vertical-align:top;
+ margin:0px;
+}
+
+#buttons {
+ height:5px;
+ overflow:auto;
+}
+
+#left_column {
+ width:230px;
+}
+
+#command_list_div {
+}
+
+#command_list_form {
+ width:100%;
+ height:100%;
+}
+
+#command_list {
+ width:100%;
+ height:100%;
+}
+
+#bottom_row {
+ height:275px;
+}
+
+#display_pane {
+}
+
+#right_panel {
+ width:230px;
+}
+
+#tabview {
+}
+
+#matrixclip {
+}
+
+#small_window {
+ width:218px;
+ height:218px;
+ background-color:#FFF;
+}
+
+div.thin_outline {
+ border:1px solid;
+ margin:6px;
+ padding:8px;
+}
+
+div.settings_block {
+}
+
+input.matrix {
+ width:50px;
+}
+
+#overviewdetails {
+ width:100%;
+ height:100%;
+ resize:none;
+ padding:10px;
+}
+
+#menu {
+ height:5px;
+ overflow:auto;
+ background-color:#999;
+}
+
+#menu-bar {
+ margin:0px;
+}
+
+ul.dropdown-menu a {
+ display:block;
+ text-decoration:none;
+ color:#000;
+}
+
+ul.dropdown-menu, ul.dropdown-menu li, ul.dropdown-menu ul {
+ list-style:none;
+ margin:0px;
+ padding:0px;
+}
+
+ul.dropdown-menu {
+ position:relative;
+ z-index:597;
+ float:left;
+}
+
+ul.dropdown-menu li {
+ float:left;
+ padding:5px;
+ cursor:pointer;
+}
+
+ul.dropdown-menu li.hover, ul.dropdown-menu li:hover {
+ position:relative;
+ z-index:599;
+ cursor:default;
+ background-color:#FFF;
+}
+
+ul.dropdown-menu ul {
+ visibility:hidden;
+ position:absolute;
+ top:100%;
+ left:0;
+ z-index:598;
+ width:195px;
+ border:1px solid;
+ border-collapse:collapse;
+ background-color:#DDD;
+}
+
+ul.dropdown-menu ul li {
+ float:none;
+}
+
+ul.dropdown-menu ul ul {
+ top:-2px;
+ left:100%;
+}
+
+ul.dropdown-menu li:hover > ul {
+ visibility:visible;
+} \ No newline at end of file
diff --git a/platform_tools/nacl/debugger/debugger.nmf b/platform_tools/nacl/debugger/debugger.nmf
new file mode 100644
index 0000000000..7a53e52b03
--- /dev/null
+++ b/platform_tools/nacl/debugger/debugger.nmf
@@ -0,0 +1,6 @@
+{
+ "program": {
+ "x86-64": {"url": "../../out/nacl64/Debug/debugger"},
+ "x86-32": {"url": "../../out/nacl32/Debug/debugger"}
+ }
+}
diff --git a/platform_tools/nacl/debugger/icons/blank.png b/platform_tools/nacl/debugger/icons/blank.png
new file mode 100644
index 0000000000..97246fb9a0
--- /dev/null
+++ b/platform_tools/nacl/debugger/icons/blank.png
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/breakpoint.png b/platform_tools/nacl/debugger/icons/breakpoint.png
new file mode 100644
index 0000000000..b6290b4f0e
--- /dev/null
+++ b/platform_tools/nacl/debugger/icons/breakpoint.png
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/breakpoint_16x16.png b/platform_tools/nacl/debugger/icons/breakpoint_16x16.png
new file mode 100644
index 0000000000..a25ece8f84
--- /dev/null
+++ b/platform_tools/nacl/debugger/icons/breakpoint_16x16.png
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/delete.png b/platform_tools/nacl/debugger/icons/delete.png
new file mode 100644
index 0000000000..50b604fb9e
--- /dev/null
+++ b/platform_tools/nacl/debugger/icons/delete.png
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/inspector.png b/platform_tools/nacl/debugger/icons/inspector.png
new file mode 100644
index 0000000000..360f23ef47
--- /dev/null
+++ b/platform_tools/nacl/debugger/icons/inspector.png
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/next.png b/platform_tools/nacl/debugger/icons/next.png
new file mode 100644
index 0000000000..aae8934424
--- /dev/null
+++ b/platform_tools/nacl/debugger/icons/next.png
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/pause.png b/platform_tools/nacl/debugger/icons/pause.png
new file mode 100644
index 0000000000..a026a545be
--- /dev/null
+++ b/platform_tools/nacl/debugger/icons/pause.png
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/play.png b/platform_tools/nacl/debugger/icons/play.png
new file mode 100644
index 0000000000..5528bafc06
--- /dev/null
+++ b/platform_tools/nacl/debugger/icons/play.png
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/previous.png b/platform_tools/nacl/debugger/icons/previous.png
new file mode 100644
index 0000000000..3396a3f8bd
--- /dev/null
+++ b/platform_tools/nacl/debugger/icons/previous.png
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/profile.png b/platform_tools/nacl/debugger/icons/profile.png
new file mode 100644
index 0000000000..d4b8501810
--- /dev/null
+++ b/platform_tools/nacl/debugger/icons/profile.png
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/reload.png b/platform_tools/nacl/debugger/icons/reload.png
new file mode 100644
index 0000000000..316f36c704
--- /dev/null
+++ b/platform_tools/nacl/debugger/icons/reload.png
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/rewind.png b/platform_tools/nacl/debugger/icons/rewind.png
new file mode 100644
index 0000000000..d8d4902e56
--- /dev/null
+++ b/platform_tools/nacl/debugger/icons/rewind.png
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/skia.png b/platform_tools/nacl/debugger/icons/skia.png
new file mode 100644
index 0000000000..017ee4f8aa
--- /dev/null
+++ b/platform_tools/nacl/debugger/icons/skia.png
Binary files differ
diff --git a/platform_tools/nacl/debugger/index.html b/platform_tools/nacl/debugger/index.html
new file mode 100644
index 0000000000..9d4644d956
--- /dev/null
+++ b/platform_tools/nacl/debugger/index.html
@@ -0,0 +1,468 @@
+<!DOCTYPE html>
+<html>
+ <!--
+ Copyright 2013 Google Inc.
+
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+<head>
+ <title>Skia Debugger</title>
+ <link rel="stylesheet" type="text/css" href="debugger.css"/>
+ <script type="text/javascript">
+ "use strict";
+
+ var skia_module = null; // Global application object.
+ var display_right_panel = null;
+ var display_bottom_row = null;
+ var overview_text = "";
+ var details_text = "Default details text.";
+ var command_list = [];
+ var command_types = {};
+ var no_filter_text = "--Filter By Available Commands--";
+
+ function openFileDialog() {
+ var event = document.createEvent("MouseEvents");
+ event.initEvent("click", true, false);
+ document.getElementById("file_open").dispatchEvent(event);
+ }
+
+ function updateOverviewDetails() {
+ var radio_buttons = document.getElementsByName("overviewdetails_radio");
+ for (var i = 0; i < radio_buttons.length; ++i) {
+ if (radio_buttons[i].checked) {
+ if (radio_buttons[i].value == "details") {
+ document.getElementById("overviewdetails").innerHTML = details_text;
+ } else {
+ document.getElementById("overviewdetails").innerHTML = overview_text;
+ }
+ return;
+ }
+ }
+ // If no radio button is checked, check the "overview" button.
+ for (var i = 0; i < radio_buttons.length; ++i) {
+ if (radio_buttons[i].value == "overview") {
+ radio_buttons[i].checked = true;
+ document.getElementById("overviewdetails").innerHTML = overview_text;
+ return;
+ }
+ }
+ }
+
+ function makeIndentString(indent_amt) {
+ var indent_str = "";
+ for (var i = 0; i < indent_amt; ++i) {
+ indent_str += "--";
+ }
+ return indent_str;
+ }
+
+ function updateCommandList(filter) {
+ var command_list_display = document.getElementById("command_list");
+ command_list_display.options.length = 0;
+ var indent = 0;
+ var indent_str = "";
+ for (var i = 0; i < command_list.length; ++i) {
+ if (command_list[i] == "Restore") {
+ indent--;
+ indent_str = makeIndentString(indent);
+ }
+ if (!filter || filter == no_filter_text || command_list[i] == filter) {
+ command_list_display.options.add(new Option(indent_str + command_list[i], i));
+ }
+ if (command_list[i] == "Save" || command_list[i] == "Save Layer") {
+ indent++;
+ indent_str = makeIndentString(indent);
+ }
+ }
+ command_list_display.selectedIndex = command_list_display.length - 1;
+
+ // TODO(borenet): Should the SKP re-draw when the command list is updated?
+ //commandSelected();
+ }
+
+ function updateFilterList() {
+ var filter_list_display = document.getElementById("command_filter");
+ filter_list_display.options.length = 0;
+ filter_list_display.options.add(new Option(no_filter_text, no_filter_text));
+ for (var command_type in command_types) {
+ if (command_types.hasOwnProperty(command_type)) {
+ filter_list_display.options.add(new Option(command_type, command_type));
+ }
+ }
+ }
+
+ function openFile(event) {
+ document.getElementById("overviewdetails").innerHTML = "";
+ var files = event.target.files;
+ if (files.length != 1) {
+ return;
+ }
+ var file = files[0];
+ var reader = new FileReader();
+ reader.onload = (function(theFile) {
+ return function(e) {
+ var data_prefix = "data:;base64,";
+ skia_module.postMessage("LoadSKP" + e.target.result.slice(data_prefix.length));
+ };
+ })(file);
+ reader.readAsDataURL(file);
+ }
+
+ function toggleInspector() {
+ var right_panel = document.getElementById("right_panel");
+ var bottom_row = document.getElementById("bottom_row");
+ if (right_panel.style.display == display_right_panel) {
+ right_panel.style.display = "none";
+ bottom_row.style.display = "none";
+ } else {
+ right_panel.style.display = display_right_panel;
+ bottom_row.style.display = display_bottom_row;
+ }
+ }
+
+ function onLoad() {
+ document.getElementById("file_open").addEventListener("change", openFile, false);
+ var right_panel = document.getElementById("right_panel");
+ var bottom_row = document.getElementById("bottom_row");
+ display_right_panel = right_panel.style.display;
+ display_bottom_row = bottom_row.style.display;
+ updateOverviewDetails();
+ updateFilterList();
+ }
+
+ // When the module loads, begin running the application.
+ function moduleDidLoad() {
+ skia_module = document.getElementById("skia_nacl");
+ sendMsg("init");
+ }
+
+ function handleMessage(message_event) {
+ var cmd_skdebugf = "SkDebugf:";
+ var cmd_clear_commands = "ClearCommands";
+ var cmd_add_command = "AddCommand:";
+ var cmd_update_commands = "UpdateCommands";
+ var cmd_set_overview = "SetOverview:";
+ var cmd_add_filter_option = "AddFilterOption";
+ if (message_event.data.indexOf(cmd_skdebugf) == 0) {
+ var msg_contents = message_event.data.slice(cmd_skdebugf.length)
+ console.log("Skia: " + msg_contents);
+ } else if (message_event.data.indexOf(cmd_clear_commands) == 0) {
+ command_list = [];
+ command_types = {};
+ updateCommandList();
+ updateFilterList();
+ } else if (message_event.data.indexOf(cmd_add_command) == 0) {
+ var command = message_event.data.slice(cmd_add_command.length);
+ command_list.push(command);
+ if (command_types[command] == undefined) {
+ command_types[command] = 1;
+ } else {
+ command_types[command]++;
+ }
+ } else if (message_event.data.indexOf(cmd_update_commands) == 0) {
+ updateCommandList();
+ updateFilterList();
+ } else if (message_event.data.indexOf(cmd_set_overview) == 0) {
+ overview_text = message_event.data.slice(cmd_set_overview.length);
+ document.getElementById("overview_radio").checked = true;
+ updateOverviewDetails();
+ } else {
+ alert(message_event.data);
+ }
+ }
+
+ // Send a message to the plugin.
+ function sendMsg(msg) {
+ if (skia_module) {
+ //console.log("Sending msg:" + msg);
+ skia_module.postMessage(msg);
+ } else {
+ alert("The Skia module has not properly loaded...");
+ }
+ }
+
+ function commandSelected() {
+ var command_list = document.getElementById("command_list");
+ var selected_index = command_list.options[command_list.selectedIndex].value;
+ if (selected_index >= 0) {
+ sendMsg("CommandSelected:" + selected_index);
+ }
+ }
+
+ function rewind() {
+ command_list.selectedIndex = 0;
+ sendMsg("Rewind");
+ }
+
+ function stepBack() {
+ if (command_list.selectedIndex > 0) {
+ command_list.selectedIndex = command_list.selectedIndex - 1;
+ }
+ sendMsg("StepBack");
+ }
+
+ function pause() {
+ sendMsg("Pause");
+ }
+
+ function stepForward() {
+ if (command_list.selectedIndex < command_list.length - 1) {
+ command_list.selectedIndex = command_list.selectedIndex + 1;
+ }
+ sendMsg("StepForward");
+ }
+
+ function play() {
+ command_list.selectedIndex = command_list.length - 1;
+ sendMsg("Play");
+ }
+ </script>
+</head>
+<body onLoad="javascript:onLoad()">
+<div id="content" class="row-set">
+ <div id="menu" class="row">
+ <ul id="menu-bar" class="dropdown-menu">
+ <li><a href="#">File</a>
+ <ul>
+ <li><a href="#" onClick="javascript:openFileDialog()">Open</a></li>
+ <li><a href="#">Save</a></li>
+ <li><a href="#">Save As</a></li>
+ <li><a href="#">Exit</a></li>
+ </ul>
+ </li>
+ <li><a href="#">Edit</a>
+ <ul>
+ <li><a href="#">Delete Command</a></li>
+ <li><a href="#">Clear Deletes</a></li>
+ <li><a href="#">Set Breakpoint</a></li>
+ <li><a href="#">Clear Breakpoints</a></li>
+ </ul>
+ </li>
+ <li><a href="#">View</a>
+ <ul>
+ <li><a href="#">Breakpoints</a></li>
+ <li><a href="#">Deleted Commands</a></li>
+ <li><a href="#">Zoom In</a></li>
+ <li><a href="#">Zoom Out</a></li>
+ </ul>
+ </li>
+ <li><a href="#">Navigate</a>
+ <ul>
+ <li><a href="#" onClick="javascript:rewind()">Rewind</a></li>
+ <li><a href="#" onClick="javascript:stepBack()">Step Back</a></li>
+ <li><a href="#" onClick="javascript:stepForward()">Step Forward</a></li>
+ <li><a href="#" onClick="javascript:play()">Play</a></li>
+ <li><a href="#" onClick="javascript:pause()">Pause</a></li>
+ <li><a href="#">Go to Line...</a></li>
+ </ul>
+ </li>
+ <li><a href="#">Window</a>
+ <ul>
+ <li><a href="#">Inspector</a></li>
+ <li><a href="#">Directory</a></li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+ <div id="buttons" class="row">
+ <div class="column-set">
+ <div class="column">
+ <button onClick="javascript:rewind()"><img src="icons/rewind.png"/><br/>Rewind</button>
+ <button onClick="javascript:stepBack()"><img src="icons/previous.png"/><br/>Step Back</button>
+ <button onClick="javascript:pause()"><img src="icons/pause.png"/><br/>Pause</button>
+ <button onClick="javascript:stepForward()"><img src="icons/next.png"/><br/>Step Forward</button>
+ <button onClick="javascript:play()"><img src="icons/play.png"/><br/>Play</button>
+ </div>
+ <div class="column">
+ <button onClick="javascript:toggleInspector()"><img src="icons/inspector.png"/><br/>Inspector</button>
+ </div>
+ <div class="column">
+ <button><img src="icons/profile.png"/><br/>Profile</button>
+ </div>
+ <div class="column" style="text-align:right; vertical-align:middle;">
+ <select id="command_filter" onChange="javascript:updateCommandList(this.options[this.selectedIndex].value)"></select>
+ <button onClick="javascript:updateCommandList()"><img src="icons/reload.png" /><br/>Clear Filter</button>
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="column-set">
+ <div id="left_column" class="column">
+ <div class="row-set">
+ <div id="command_list_div" class="row">
+ <form id="command_list_form">
+ <select id="command_list" size="2" onChange="javascript:commandSelected()">
+ <option value="-1">Commands go here...</option>
+ </select>
+ </form>
+ </div>
+ </div>
+ </div>
+ <div id="right_column" class="row-set">
+ <div id="top_row" class="row">
+ <div id="display_pane" class="column">
+ <div id="listener" style="width:100%; height:100%;">
+ <script type="text/javascript">
+ var listener = document.getElementById('listener');
+ listener.addEventListener('load', moduleDidLoad, true);
+ listener.addEventListener('message', handleMessage, true);
+ </script>
+ <embed name="nacl_module"
+ id="skia_nacl"
+ src="debugger.nmf"
+ type="application/x-nacl"
+ width="100%"
+ height="100%"
+ style="width:100%, height:100%;"/>
+ </div>
+ </div>
+ <div id="right_panel" class="column">
+ <div class="thin_outline">
+ <div id="visibility_filter" class="settings_block">
+ Visibility Filter<br/>
+ <div class="thin_outline">
+ <form id="visibility_filter_form">
+ <input type="radio" name="visibility_filter_radio" value="on">On<br/>
+ <input type="radio" name="visibility_filter_radio" value="off" checked>Off
+ </form>
+ </div>
+ </div>
+ <div id="command_scrolling" class="settings_block">
+ Command Scrolling Preferences<br/>
+ <div class="thin_outline">
+ <div class="row-set">
+ <div class="row">
+ <div class="column-set">
+ <div class="column">
+ Current Command:
+ </div>
+ <div class="column" style="text-align:right; width:35%;">
+ <input type="text" style="width:100%;"/>
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="column-set">
+ <div class="column">
+ Command HitBox:
+ </div>
+ <div class="column" style="text-align:right; width:35%;">
+ <input type="text" style="width:100%;"/>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="render_targets" class="settings_block">
+ Render Targets<br/>
+ <div class="thin_outline">
+ <form id="render_targets_form">
+ <div class="row-set">
+ <div class="row">
+ <div class="column-set">
+ <div class="column">Raster:</div>
+ <div class="column" style="text-align:right;">
+ <input type="checkbox" name="render_targets_checkbox" value="raster" checked/>
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="column-set">
+ <div class="column" style="padding-left:30px;">Overdraw Viz:</div>
+ <div class="column" style="text-align:right;">
+ <input type="checkbox" name="render_targets_checkbox" value="overdraw"/>
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="column-set">
+ <div class="column">OpenGL</div>
+ <div class="column" style="text-align:right;">
+ <input type="checkbox" name="render_targets_checkbox" value="opengl"/>
+ </div>
+ </div>
+ </div>
+ </div>
+ </form>
+ </div>
+ </div>
+ <div id="zoom_level" class="settings_block">
+ <div class="thin_outline">
+ <div class="row-set">
+ <div class="row">
+ <div class="column-set">
+ <div class="column">
+ Zoom Level:
+ </div>
+ <div class="column" style="text-align:right; width:35%;">
+ <input type="text" style="width:100%;"/>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="small_window_wrapper" class="settings_block">
+ <div class="thin_outline" style="padding:0px;">
+ <div id="small_window">
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="bottom_row" class="row">
+ <div id="tabview" class="column">
+ <div class="row-set">
+ <div class="row" style="height:5px; overflow:auto;">
+ <form id="overviewdetails_form">
+ <input type="radio" name="overviewdetails_radio" onChange="javascript:updateOverviewDetails()" id="overview_radio" value="overview" checked>Overview
+ <input type="radio" name="overviewdetails_radio" onChange="javascript:updateOverviewDetails()" id="details_radio" value="details">Details
+ </form>
+ </div>
+ <div class="row">
+ <div id="overviewdetails"></div>
+ </div>
+ </div>
+ </div>
+ <div id="matrixclip" class="column">
+ Current Matrix
+ <table>
+ <tr>
+ <td><input type="text" id="matrix00" class="matrix" /></td>
+ <td><input type="text" id="matrix01" class="matrix" /></td>
+ <td><input type="text" id="matrix02" class="matrix" /></td>
+ </tr>
+ <tr>
+ <td><input type="text" id="matrix10" class="matrix" /></td>
+ <td><input type="text" id="matrix11" class="matrix" /></td>
+ <td><input type="text" id="matrix12" class="matrix" /></td>
+ </tr>
+ <tr>
+ <td><input type="text" id="matrix20" class="matrix" /></td>
+ <td><input type="text" id="matrix21" class="matrix" /></td>
+ <td><input type="text" id="matrix22" class="matrix" /></td>
+ </tr>
+ </table>
+ Current Clip
+ <table>
+ <tr>
+ <td><input type="text" id="clip00" class="matrix" /></td>
+ <td><input type="text" id="clip01" class="matrix" /></td>
+ </tr>
+ <tr>
+ <td><input type="text" id="clip10" class="matrix" /></td>
+ <td><input type="text" id="clip11" class="matrix" /></td>
+ </tr>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<input type="file" id="file_open" style="display:none;"/>
+</body>
+</html>
diff --git a/platform_tools/nacl/favicon.ico b/platform_tools/nacl/favicon.ico
new file mode 100644
index 0000000000..e7440c7512
--- /dev/null
+++ b/platform_tools/nacl/favicon.ico
Binary files differ
diff --git a/platform_tools/nacl/gclient.config b/platform_tools/nacl/gclient.config
new file mode 100644
index 0000000000..922ae43db8
--- /dev/null
+++ b/platform_tools/nacl/gclient.config
@@ -0,0 +1,13 @@
+# To develop Skia targeting NaCl,
+# copy this file to your root development directory as ".gclient"
+# and then run "gclient sync".
+solutions = [
+ {
+ "name" : "nacl",
+ "url" : "https://skia.googlecode.com/svn/nacl",
+ },
+ {
+ "name" : "trunk",
+ "url" : "https://skia.googlecode.com/svn/trunk",
+ },
+]
diff --git a/platform_tools/nacl/httpd.py b/platform_tools/nacl/httpd.py
new file mode 100755
index 0000000000..2ad1d49438
--- /dev/null
+++ b/platform_tools/nacl/httpd.py
@@ -0,0 +1,212 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 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.
+
+"""A tiny web server.
+
+This is intended to be used for testing, and only run from within the examples
+directory.
+"""
+
+import BaseHTTPServer
+import logging
+import optparse
+import os
+import SimpleHTTPServer
+import SocketServer
+import sys
+import urlparse
+
+
+EXAMPLE_PATH=os.path.dirname(os.path.abspath(__file__))
+NACL_SDK_ROOT = os.getenv('NACL_SDK_ROOT', os.path.dirname(EXAMPLE_PATH))
+
+
+if os.path.exists(NACL_SDK_ROOT):
+ sys.path.append(os.path.join(NACL_SDK_ROOT, 'tools'))
+ import decode_dump
+ import getos
+else:
+ NACL_SDK_ROOT=None
+
+last_nexe = None
+last_nmf = None
+
+logging.getLogger().setLevel(logging.INFO)
+
+# Using 'localhost' means that we only accept connections
+# via the loop back interface.
+SERVER_PORT = 5103
+SERVER_HOST = ''
+
+# We only run from the examples directory so that not too much is exposed
+# via this HTTP server. Everything in the directory is served, so there should
+# never be anything potentially sensitive in the serving directory, especially
+# if the machine might be a multi-user machine and not all users are trusted.
+# We only serve via the loopback interface.
+def SanityCheckDirectory():
+ httpd_path = os.path.abspath(os.path.dirname(__file__))
+ serve_path = os.path.abspath(os.getcwd())
+
+ # Verify we are serving from the directory this script came from, or bellow
+ if serve_path[:len(httpd_path)] == httpd_path:
+ return
+ logging.error('For security, httpd.py should only be run from within the')
+ logging.error('example directory tree.')
+ logging.error('We are currently in %s.' % serve_path)
+ sys.exit(1)
+
+
+# An HTTP server that will quit when |is_running| is set to False. We also use
+# SocketServer.ThreadingMixIn in order to handle requests asynchronously for
+# faster responses.
+class QuittableHTTPServer(SocketServer.ThreadingMixIn,
+ BaseHTTPServer.HTTPServer):
+ def serve_forever(self, timeout=0.5):
+ self.is_running = True
+ self.timeout = timeout
+ while self.is_running:
+ self.handle_request()
+
+ def shutdown(self):
+ self.is_running = False
+ return 1
+
+
+# "Safely" split a string at |sep| into a [key, value] pair. If |sep| does not
+# exist in |str|, then the entire |str| is the key and the value is set to an
+# empty string.
+def KeyValuePair(str, sep='='):
+ if sep in str:
+ return str.split(sep)
+ else:
+ return [str, '']
+
+
+# A small handler that looks for '?quit=1' query in the path and shuts itself
+# down if it finds that parameter.
+class QuittableHTTPHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
+ def send_head(self):
+ """Common code for GET and HEAD commands.
+
+ This sends the response code and MIME headers.
+
+ Return value is either a file object (which has to be copied
+ to the outputfile by the caller unless the command was HEAD,
+ and must be closed by the caller under all circumstances), or
+ None, in which case the caller has nothing further to do.
+
+ """
+ path = self.translate_path(self.path)
+ f = None
+ if os.path.isdir(path):
+ if not self.path.endswith('/'):
+ # redirect browser - doing basically what apache does
+ self.send_response(301)
+ self.send_header("Location", self.path + "/")
+ self.end_headers()
+ return None
+ for index in "index.html", "index.htm":
+ index = os.path.join(path, index)
+ if os.path.exists(index):
+ path = index
+ break
+ else:
+ return self.list_directory(path)
+ ctype = self.guess_type(path)
+ try:
+ # Always read in binary mode. Opening files in text mode may cause
+ # newline translations, making the actual size of the content
+ # transmitted *less* than the content-length!
+ f = open(path, 'rb')
+ except IOError:
+ self.send_error(404, "File not found")
+ return None
+ self.send_response(200)
+ self.send_header("Content-type", ctype)
+ fs = os.fstat(f.fileno())
+ self.send_header("Content-Length", str(fs[6]))
+ self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
+ self.send_header('Cache-Control','no-cache, must-revalidate')
+ self.send_header('Expires','-1')
+ self.end_headers()
+ return f
+
+ def do_GET(self):
+ global last_nexe, last_nmf
+ (_, _, path, query, _) = urlparse.urlsplit(self.path)
+ url_params = dict([KeyValuePair(key_value)
+ for key_value in query.split('&')])
+ if 'quit' in url_params and '1' in url_params['quit']:
+ self.send_response(200, 'OK')
+ self.send_header('Content-type', 'text/html')
+ self.send_header('Content-length', '0')
+ self.end_headers()
+ self.server.shutdown()
+ return
+
+ if path.endswith('.nexe'):
+ last_nexe = path
+ if path.endswith('.nmf'):
+ last_nmf = path
+
+ SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
+
+ def do_POST(self):
+ (_, _,path, query, _) = urlparse.urlsplit(self.path)
+ if 'Content-Length' in self.headers:
+ if not NACL_SDK_ROOT:
+ self.wfile('Could not find NACL_SDK_ROOT to decode trace.')
+ return
+ data = self.rfile.read(int(self.headers['Content-Length']))
+ nexe = '.' + last_nexe
+ nmf = '.' + last_nmf
+ addr = os.path.join(NACL_SDK_ROOT, 'toolchain',
+ getos.GetPlatform() + '_x86_newlib',
+ 'bin', 'x86_64-nacl-addr2line')
+ decoder = decode_dump.CoreDecoder(nexe, nmf, addr, None, None)
+ info = decoder.Decode(data)
+ trace = decoder.StackTrace(info)
+ decoder.PrintTrace(trace, sys.stdout)
+ decoder.PrintTrace(trace, self.wfile)
+
+
+def Run(server_address,
+ server_class=QuittableHTTPServer,
+ handler_class=QuittableHTTPHandler):
+ httpd = server_class(server_address, handler_class)
+ logging.info("Starting local server on port %d", server_address[1])
+ logging.info("To shut down send http://localhost:%d?quit=1",
+ server_address[1])
+ try:
+ httpd.serve_forever()
+ except KeyboardInterrupt:
+ logging.info("Received keyboard interrupt.")
+ httpd.server_close()
+
+ logging.info("Shutting down local server on port %d", server_address[1])
+
+
+def main():
+ usage_str = "usage: %prog [options] [optional_portnum]"
+ parser = optparse.OptionParser(usage=usage_str)
+ parser.add_option(
+ '--no_dir_check', dest='do_safe_check',
+ action='store_false', default=True,
+ help='Do not ensure that httpd.py is being run from a safe directory.')
+ (options, args) = parser.parse_args(sys.argv)
+ if options.do_safe_check:
+ SanityCheckDirectory()
+ if len(args) > 2:
+ print 'Too many arguments specified.'
+ parser.print_help()
+ elif len(args) == 2:
+ Run((SERVER_HOST, int(args[1])))
+ else:
+ Run((SERVER_HOST, SERVER_PORT))
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/platform_tools/nacl/index.html b/platform_tools/nacl/index.html
new file mode 100644
index 0000000000..abe5d1e6ef
--- /dev/null
+++ b/platform_tools/nacl/index.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+ <!--
+ Copyright 2013 Google Inc.
+
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+<head>
+ <title>Skia Native Client Apps</title>
+</head>
+<body>
+ <h1>Skia Native Client Apps</h1>
+ <p>
+ <ul>
+ <li><a href="tests">Skia Unit Tests</a></li>
+ <li><a href="debugger">Skia Debugger</a></li>
+ <li><a href="SampleApp">Skia Sample App</a></li>
+ </ul>
+ </p>
+</body>
+</html>
diff --git a/platform_tools/nacl/nacl_make b/platform_tools/nacl/nacl_make
new file mode 100755
index 0000000000..81f6eb23dd
--- /dev/null
+++ b/platform_tools/nacl/nacl_make
@@ -0,0 +1,104 @@
+#!/bin/bash
+
+function setenv {
+ if [ -z "$1" ]; then
+ echo "ERROR: setenv() requires one argument."
+ exit 1
+ fi
+ if [ -z "${NACL_SDK_ROOT}" ]; then
+ echo "ERROR: This script requires NACL_SDK_ROOT to be set."
+ exit 1
+ fi
+
+ ARCH_WIDTH=$1
+ if [ ${ARCH_WIDTH} = "32" ]; then
+ CROSS_ID=i686
+ elif [ ${ARCH_WIDTH} = "64" ]; then
+ CROSS_ID=x86_64
+ else
+ echo "ERROR: Unknown arch width: ${ARCH_WIDTH}"
+ exit 1
+ fi
+
+ OS_NAME=$(uname -s)
+ if [ $OS_NAME = "Darwin" ]; then
+ OS_SUBDIR="mac"
+ OS_SUBDIR_SHORT="mac"
+ OS_JOBS=4
+ elif [ $OS_NAME = "Linux" ]; then
+ OS_SUBDIR="linux"
+ OS_SUBDIR_SHORT="linux"
+ OS_JOBS=4
+ else
+ OS_SUBDIR="windows"
+ OS_SUBDIR_SHORT="win"
+ OS_JOBS=1
+ fi
+
+ NACL_TOOLCHAIN_ROOT=${NACL_SDK_ROOT}/toolchain/${OS_SUBDIR_SHORT}_x86_newlib
+ NACL_BIN_PATH=${NACL_TOOLCHAIN_ROOT}/bin
+ export NACL_CROSS_PREFIX=${CROSS_ID}-nacl
+
+ if [[ -z "$NACL_MAKE_CCACHE" ]]; then
+ export NACLCC=${NACL_BIN_PATH}/${NACL_CROSS_PREFIX}-gcc
+ export NACLCXX=${NACL_BIN_PATH}/${NACL_CROSS_PREFIX}-g++
+ else
+ export NACLCC="${NACL_MAKE_CCACHE} ${NACL_BIN_PATH}/${NACL_CROSS_PREFIX}-gcc"
+ export NACLCXX="${NACL_MAKE_CCACHE} ${NACL_BIN_PATH}/${NACL_CROSS_PREFIX}-g++"
+ fi
+ export NACLAR=${NACL_BIN_PATH}/${NACL_CROSS_PREFIX}-ar
+ export NACLRANLIB=${NACL_BIN_PATH}/${NACL_CROSS_PREFIX}-ranlib
+ export NACLLD=${NACL_BIN_PATH}/${NACL_CROSS_PREFIX}-ld
+ export NACLSTRINGS=${NACL_BIN_PATH}/${NACL_CROSS_PREFIX}-strings
+ export NACLSTRIP=${NACL_BIN_PATH}/${NACL_CROSS_PREFIX}-strip
+
+ export CC=${NACLCC}
+ export CXX=${NACLCXX}
+ export AR=${NACLAR}
+ export RANLIB=${NACLRANLIB}
+ export PATH=${NACL_BIN_PATH}:${PATH};
+
+ export GYP_DEFINES="skia_os=nacl skia_arch_width=${ARCH_WIDTH}"
+}
+
+function build {
+ if [ -z "$1" ]; then
+ echo "ERROR: build() requires one argument."
+ exit 1
+ fi
+ setenv $1
+
+ export SKIA_OUT=out/nacl$1
+ make ${MAKE_ARGS}
+}
+
+MAKE_ARGS=""
+
+while (( "$#" )); do
+ if [[ "$1" == "--use-ccache" ]];
+ then
+ if [[ -z "$NACL_MAKE_CCACHE" ]];
+ then
+ NACL_MAKE_CCACHE=$(which ccache)
+ fi
+ elif [ -z "${MAKE_ARGS}" ]; then
+ MAKE_ARGS="$1"
+ else
+ MAKE_ARGS="${MAKE_ARGS} $1"
+ fi
+ shift
+done
+
+if [[ -n "$NACL_MAKE_CCACHE" ]]; then
+ $NACL_MAKE_CCACHE --version &> /dev/null
+ if [[ "$?" != "0" ]]; then
+ echo "Unable to find ccache!"
+ exit 1
+ fi
+fi
+
+build 32 && \
+build 64 && \
+if ! [ -L platform_tools/nacl/out ]; then
+ ln -s ../../out platform_tools/nacl
+fi \ No newline at end of file
diff --git a/platform_tools/nacl/src/nacl_debugger.cpp b/platform_tools/nacl/src/nacl_debugger.cpp
new file mode 100644
index 0000000000..26e3ed67c7
--- /dev/null
+++ b/platform_tools/nacl/src/nacl_debugger.cpp
@@ -0,0 +1,221 @@
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "ppapi/cpp/completion_callback.h"
+#include "ppapi/cpp/graphics_2d.h"
+#include "ppapi/cpp/image_data.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/cpp/point.h"
+#include "ppapi/cpp/rect.h"
+#include "ppapi/cpp/var.h"
+
+#include "SkBase64.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColor.h"
+#include "SkDebugger.h"
+#include "SkGraphics.h"
+#include "SkStream.h"
+#include "SkString.h"
+
+class SkiaInstance;
+
+// Used by SkDebugf
+SkiaInstance* gPluginInstance;
+
+void FlushCallback(void* data, int32_t result);
+
+// Skia's subclass of pp::Instance, our interface with the browser.
+class SkiaInstance : public pp::Instance {
+public:
+ explicit SkiaInstance(PP_Instance instance)
+ : pp::Instance(instance)
+ , fCanvas(NULL)
+ , fPicture(NULL)
+ , fFlushLoopRunning(false)
+ , fFlushPending(false)
+
+ {
+ gPluginInstance = this;
+ SkGraphics::Init();
+ }
+
+ virtual ~SkiaInstance() {
+ SkGraphics::Term();
+ gPluginInstance = NULL;
+ }
+
+ virtual void HandleMessage(const pp::Var& var_message) {
+ // Receive a message from javascript.
+ if (var_message.is_string()) {
+ SkString msg(var_message.AsString().c_str());
+ if (msg.startsWith("init")) {
+ } else if (msg.startsWith("LoadSKP")) {
+ size_t startIndex = strlen("LoadSKP");
+ size_t dataSize = msg.size()/sizeof(char) - startIndex;
+ SkBase64 decodedData;
+ decodedData.decode(msg.c_str() + startIndex, dataSize);
+ size_t decodedSize = 3 * (dataSize / 4);
+ SkDebugf("Got size: %d\n", decodedSize);
+ if (!decodedData.getData()) {
+ SkDebugf("Failed to decode SKP\n");
+ return;
+ }
+ SkMemoryStream pictureStream(decodedData.getData(), decodedSize);
+ fPicture = new SkPicture(&pictureStream);
+ if (fPicture->width() == 0 || fPicture->height() == 0) {
+ SkDebugf("Failed to create SKP.\n");
+ return;
+ }
+ fDebugger.loadPicture(fPicture);
+
+ // Set up the command list.
+ SkTArray<SkString>* commands = fDebugger.getDrawCommandsAsStrings();
+ PostMessage("ClearCommands");
+ for (int i = 0; i < commands->count(); ++i) {
+ SkString addCommand("AddCommand:");
+ addCommand.append((*commands)[i]);
+ PostMessage(addCommand.c_str());
+ }
+ PostMessage("UpdateCommands");
+
+ // Set the overview text.
+ SkString overviewText;
+ fDebugger.getOverviewText(NULL, 0.0, &overviewText, 1);
+ overviewText.prepend("SetOverview:");
+ PostMessage(overviewText.c_str());
+
+ // Draw the SKP.
+ if (!fFlushLoopRunning) {
+ Paint();
+ }
+ } else if (msg.startsWith("CommandSelected:")) {
+ size_t startIndex = strlen("CommandSelected:");
+ int index = atoi(msg.c_str() + startIndex);
+ fDebugger.setIndex(index);
+ if (!fFlushLoopRunning) {
+ Paint();
+ }
+ } else if (msg.startsWith("Rewind")) {
+ fCanvas->clear(SK_ColorWHITE);
+ fDebugger.setIndex(0);
+ if (!fFlushLoopRunning) {
+ Paint();
+ }
+ } else if (msg.startsWith("StepBack")) {
+ fCanvas->clear(SK_ColorWHITE);
+ int currentIndex = fDebugger.index();
+ if (currentIndex > 1) {
+ fDebugger.setIndex(currentIndex - 1);
+ if (!fFlushLoopRunning) {
+ Paint();
+ }
+ }
+ } else if (msg.startsWith("Pause")) {
+ // TODO(borenet)
+ } else if (msg.startsWith("StepForward")) {
+ int currentIndex = fDebugger.index();
+ if (currentIndex < fDebugger.getSize() -1) {
+ fDebugger.setIndex(currentIndex + 1);
+ if (!fFlushLoopRunning) {
+ Paint();
+ }
+ }
+ } else if (msg.startsWith("Play")) {
+ fDebugger.setIndex(fDebugger.getSize() - 1);
+ if (!fFlushLoopRunning) {
+ Paint();
+ }
+ }
+ }
+ }
+
+ void Paint() {
+ if (!fImage.is_null()) {
+ fDebugger.draw(fCanvas);
+ fDeviceContext.PaintImageData(fImage, pp::Point(0, 0));
+ if (!fFlushPending) {
+ fFlushPending = true;
+ fDeviceContext.Flush(pp::CompletionCallback(&FlushCallback, this));
+ } else {
+ SkDebugf("A flush is pending... Skipping flush.\n");
+ }
+ } else {
+ SkDebugf("No pixels to write to!\n");
+ }
+ }
+
+ virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
+ if (position.size().width() == fWidth &&
+ position.size().height() == fHeight) {
+ return; // We don't care about the position, only the size.
+ }
+ fWidth = position.size().width();
+ fHeight = position.size().height();
+
+ fDeviceContext = pp::Graphics2D(this, pp::Size(fWidth, fHeight), false);
+ if (!BindGraphics(fDeviceContext)) {
+ SkDebugf("Couldn't bind the device context\n");
+ return;
+ }
+ fImage = pp::ImageData(this,
+ PP_IMAGEDATAFORMAT_BGRA_PREMUL,
+ pp::Size(fWidth, fHeight), false);
+ fBitmap.setConfig(SkBitmap::kARGB_8888_Config, fWidth, fHeight);
+ fBitmap.setPixels(fImage.data());
+ if (fCanvas) {
+ delete fCanvas;
+ }
+ fCanvas = new SkCanvas(fBitmap);
+ fCanvas->clear(SK_ColorWHITE);
+ if (!fFlushLoopRunning) {
+ Paint();
+ }
+ }
+
+ void OnFlush() {
+ fFlushLoopRunning = true;
+ fFlushPending = false;
+ Paint();
+ }
+
+private:
+ pp::Graphics2D fDeviceContext;
+ pp::ImageData fImage;
+ int fWidth;
+ int fHeight;
+
+ SkBitmap fBitmap;
+ SkCanvas* fCanvas;
+ SkDebugger fDebugger;
+ SkPicture* fPicture;
+
+ bool fFlushLoopRunning;
+ bool fFlushPending;
+};
+
+void FlushCallback(void* data, int32_t result) {
+ static_cast<SkiaInstance*>(data)->OnFlush();
+}
+
+class SkiaModule : public pp::Module {
+public:
+ SkiaModule() : pp::Module() {}
+ virtual ~SkiaModule() {}
+
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new SkiaInstance(instance);
+ }
+};
+
+namespace pp {
+Module* CreateModule() {
+ return new SkiaModule();
+}
+} // namespace pp
diff --git a/platform_tools/nacl/src/nacl_interface.cpp b/platform_tools/nacl/src/nacl_interface.cpp
new file mode 100644
index 0000000000..09d55c3fec
--- /dev/null
+++ b/platform_tools/nacl/src/nacl_interface.cpp
@@ -0,0 +1,111 @@
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "ppapi/cpp/completion_callback.h"
+#include "ppapi/cpp/graphics_2d.h"
+#include "ppapi/cpp/image_data.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/cpp/var.h"
+
+#include "SkCanvas.h"
+#include "SkBitmap.h"
+#include "SkString.h"
+#include "SkThreadUtils.h"
+
+class SkiaInstance;
+
+// Used by SkDebugf
+SkiaInstance* gPluginInstance;
+
+// Main entry point for the app we're linked into
+extern int tool_main(int, char**);
+
+// Tokenize a command line and store it in argc and argv.
+void SkStringToProgramArgs(const SkString commandLine, int* argc, char*** argv) {
+ int numBreaks = 0;
+ const char* commandChars = commandLine.c_str();
+ for (size_t i = 0; i < strlen(commandChars); i++) {
+ if (isspace(commandChars[i])) {
+ numBreaks++;
+ }
+ }
+ int numArgs;
+ if (strlen(commandChars) > 0) {
+ numArgs = numBreaks + 1;
+ } else {
+ numArgs = 0;
+ }
+ *argc = numArgs;
+ *argv = new char*[numArgs + 1];
+ (*argv)[numArgs] = NULL;
+ char* start = (char*) commandChars;
+ int length = 0;
+ int argIndex = 0;
+ for (size_t i = 0; i < strlen(commandChars) + 1; i++) {
+ if (isspace(commandChars[i]) || '\0' == commandChars[i]) {
+ if (length > 0) {
+ char* argument = new char[length + 1];
+ memcpy(argument, start, length);
+ argument[length] = '\0';
+ (*argv)[argIndex++] = argument;
+ }
+ start = (char*) commandChars + i + 1;
+ length = 0;
+ } else {
+ length++;
+ }
+ }
+}
+
+// Run the program with the given command line.
+void RunProgram(const SkString& commandLine) {
+ int argc;
+ char** argv;
+ SkStringToProgramArgs(commandLine, &argc, &argv);
+ tool_main(argc, argv);
+}
+
+
+// Skia's subclass of pp::Instance, our interface with the browser.
+class SkiaInstance : public pp::Instance {
+public:
+ explicit SkiaInstance(PP_Instance instance) : pp::Instance(instance) {
+ gPluginInstance = this;
+ }
+
+ virtual ~SkiaInstance() {
+ gPluginInstance = NULL;
+ }
+
+ virtual void HandleMessage(const pp::Var& var_message) {
+ // Receive a message from javascript.
+ if (var_message.is_string()) {
+ SkString msg(var_message.AsString().c_str());
+ if (msg.startsWith("init")) {
+ RunProgram(msg);
+ }
+ }
+ }
+};
+
+class SkiaModule : public pp::Module {
+public:
+ SkiaModule() : pp::Module() {}
+ virtual ~SkiaModule() {}
+
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new SkiaInstance(instance);
+ }
+};
+
+namespace pp {
+Module* CreateModule() {
+ return new SkiaModule();
+}
+} // namespace pp
diff --git a/platform_tools/nacl/src/nacl_sample.cpp b/platform_tools/nacl/src/nacl_sample.cpp
new file mode 100644
index 0000000000..aca74f4247
--- /dev/null
+++ b/platform_tools/nacl/src/nacl_sample.cpp
@@ -0,0 +1,212 @@
+#include <cstdio>
+#include <string>
+
+#include "ppapi/cpp/completion_callback.h"
+#include "ppapi/cpp/graphics_2d.h"
+#include "ppapi/cpp/image_data.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/cpp/var.h"
+
+#include "SampleApp.h"
+#include "SkApplication.h"
+#include "SkCanvas.h"
+#include "SkBitmap.h"
+#include "SkEvent.h"
+#include "SkWindow.h"
+
+class SkiaInstance;
+
+namespace {
+void FlushCallback(void* data, int32_t result);
+}
+
+SkiaInstance* gPluginInstance;
+extern int main(int, char**);
+
+class SkiaInstance : public pp::Instance {
+ public:
+ explicit SkiaInstance(PP_Instance instance) : pp::Instance(instance),
+ fFlushPending(false),
+ fGraphics2dContext(NULL),
+ fPixelBuffer(NULL)
+ {
+ gPluginInstance = this;
+ application_init();
+ char* commandName = "SampleApp";
+ fWindow = new SampleWindow(NULL, 0, &commandName, NULL);
+ }
+
+ virtual ~SkiaInstance() {
+ gPluginInstance = NULL;
+ delete fWindow;
+ application_term();
+ }
+
+ virtual void HandleMessage(const pp::Var& var_message) {
+ // Receive a message from javascript. Right now this just signals us to
+ // get started.
+ uint32_t width = 500;
+ uint32_t height = 500;
+ char buffer[2048];
+ sprintf(buffer, "SetSize:%d,%d", width, height);
+ PostMessage(buffer);
+ }
+
+ virtual void DidChangeView(const pp::Rect& position,
+ const pp::Rect& clip) {
+ if (position.size().width() == width() &&
+ position.size().height() == height()) {
+ return; // Size didn't change, no need to update anything.
+ }
+ // Create a new device context with the new size.
+ DestroyContext();
+ CreateContext(position.size());
+ // Delete the old pixel buffer and create a new one.
+ delete fPixelBuffer;
+ fPixelBuffer = NULL;
+ if (fGraphics2dContext != NULL) {
+ fPixelBuffer = new pp::ImageData(this,
+ PP_IMAGEDATAFORMAT_BGRA_PREMUL,
+ fGraphics2dContext->size(),
+ false);
+ fWindow->resize(position.size().width(), position.size().height());
+ fWindow->update(NULL);
+ paint();
+ }
+ }
+
+ // Indicate whether a flush is pending. This can only be called from the
+ // main thread; it is not thread safe.
+ bool flush_pending() const {
+ return fFlushPending;
+ }
+ void set_flush_pending(bool flag) {
+ fFlushPending = flag;
+ }
+
+ void paint() {
+ if (fPixelBuffer) {
+ // Draw some stuff. TODO(borenet): Actually have SampleApp draw into
+ // the plugin area.
+ uint32_t w = fPixelBuffer->size().width();
+ uint32_t h = fPixelBuffer->size().height();
+ uint32_t* data = (uint32_t*) fPixelBuffer->data();
+ // Create a bitmap using the fPixelBuffer pixels
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h);
+ bitmap.setPixels(data);
+ // Create a canvas with the bitmap as the backend
+ SkCanvas canvas(bitmap);
+
+ canvas.drawColor(SK_ColorBLUE);
+ SkRect rect = SkRect::MakeXYWH(10, 10, 80, 80);
+ SkPaint rect_paint;
+ rect_paint.setStyle(SkPaint::kFill_Style);
+ rect_paint.setColor(SK_ColorRED);
+ canvas.drawRect(rect, rect_paint);
+
+ FlushPixelBuffer();
+ }
+ }
+
+private:
+ int width() const {
+ return fPixelBuffer ? fPixelBuffer->size().width() : 0;
+ }
+
+ int height() const {
+ return fPixelBuffer ? fPixelBuffer->size().height() : 0;
+ }
+
+ bool IsContextValid() const {
+ return fGraphics2dContext != NULL;
+ }
+
+ void CreateContext(const pp::Size& size) {
+ if (IsContextValid())
+ return;
+ fGraphics2dContext = new pp::Graphics2D(this, size, false);
+ if (!BindGraphics(*fGraphics2dContext)) {
+ SkDebugf("Couldn't bind the device context");
+ }
+ }
+
+ void DestroyContext() {
+ if (!IsContextValid())
+ return;
+ delete fGraphics2dContext;
+ fGraphics2dContext = NULL;
+ }
+
+ void FlushPixelBuffer() {
+ if (!IsContextValid())
+ return;
+ // Note that the pixel lock is held while the buffer is copied into the
+ // device context and then flushed.
+ fGraphics2dContext->PaintImageData(*fPixelBuffer, pp::Point());
+ if (flush_pending())
+ return;
+ set_flush_pending(true);
+ fGraphics2dContext->Flush(pp::CompletionCallback(&FlushCallback, this));
+ }
+
+ bool fFlushPending;
+ pp::Graphics2D* fGraphics2dContext;
+ pp::ImageData* fPixelBuffer;
+ SampleWindow* fWindow;
+};
+
+class SkiaModule : public pp::Module {
+public:
+ SkiaModule() : pp::Module() {}
+ virtual ~SkiaModule() {}
+
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ gPluginInstance = new SkiaInstance(instance);
+ return gPluginInstance;
+ }
+};
+
+namespace {
+void FlushCallback(void* data, int32_t result) {
+ static_cast<SkiaInstance*>(data)->set_flush_pending(false);
+}
+}
+
+namespace pp {
+Module* CreateModule() {
+ return new SkiaModule();
+}
+} // namespace pp
+
+
+///////////////////////////////////////////
+///////////// SkOSWindow impl /////////////
+///////////////////////////////////////////
+
+void SkOSWindow::onSetTitle(const char title[])
+{
+ char buffer[2048];
+ sprintf(buffer, "SetTitle:%s", title);
+ gPluginInstance->PostMessage(buffer);
+}
+
+void SkOSWindow::onHandleInval(const SkIRect& rect)
+{
+ gPluginInstance->paint();
+}
+
+void SkOSWindow::onPDFSaved(const char title[], const char desc[],
+ const char path[]) {
+}
+
+///////////////////////////////////////////
+/////////////// SkEvent impl //////////////
+///////////////////////////////////////////
+
+void SkEvent::SignalQueueTimer(SkMSec ms) {
+}
+
+void SkEvent::SignalNonEmptyQueue() {
+}
diff --git a/platform_tools/nacl/tests/index.html b/platform_tools/nacl/tests/index.html
new file mode 100644
index 0000000000..3a0bdb9415
--- /dev/null
+++ b/platform_tools/nacl/tests/index.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html>
+ <!--
+ Copyright 2013 Google Inc.
+
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+ -->
+<head>
+
+ <title>Skia Unit Tests</title>
+
+ <script type="text/javascript">
+ "use strict";
+
+ var SkiaModule = null; // Global application object.
+
+ // Force a re-draw of the given element.
+ function refresh(elem) {
+ var old_display_style = elem.style.display;
+ elem.style.display = "none";
+ elem.style.display = old_display_style;
+ }
+
+ // When the module loads, begin running the application.
+ function moduleDidLoad() {
+ SkiaModule = document.getElementById("skia_nacl");
+ run();
+ }
+
+ function handleMessage(message_event) {
+ var skdebugf_cmd = "SkDebugf:";
+ if (message_event.data.indexOf(skdebugf_cmd) == 0) {
+ var msg_contents = message_event.data.slice(skdebugf_cmd.length)
+ //console.log("Skia: " + msg_contents);
+ var log_textarea = document.getElementById("log_textarea")
+ log_textarea.value += msg_contents;
+ log_textarea.scrollTop = log_textarea.scrollHeight;
+ refresh(log_textarea);
+ } else {
+ alert(message_event.data);
+ }
+ }
+
+ // Run the application.
+ function run() {
+ if (SkiaModule) {
+ var cmd = "init";
+ SkiaModule.postMessage(cmd);
+ } else {
+ alert("The Skia module has not properly loaded...");
+ }
+ }
+ </script>
+</head>
+<body>
+
+<h1>Skia Unit Tests</h1>
+<p>
+<textarea id="log_textarea" rows="2" cols="2" readonly style="width:100%; height:500px; resize:none;"></textarea>
+</p>
+<p>
+ <div id="listener">
+ <script type="text/javascript">
+ var listener = document.getElementById('listener');
+ listener.addEventListener('load', moduleDidLoad, true);
+ listener.addEventListener('message', handleMessage, true);
+ </script>
+
+ <embed name="nacl_module"
+ id="skia_nacl"
+ width=0 height=0
+ src="tests.nmf"
+ type="application/x-nacl" />
+ </div>
+</p>
+</body>
+</html>
diff --git a/platform_tools/nacl/tests/tests.nmf b/platform_tools/nacl/tests/tests.nmf
new file mode 100644
index 0000000000..95db050086
--- /dev/null
+++ b/platform_tools/nacl/tests/tests.nmf
@@ -0,0 +1,6 @@
+{
+ "program": {
+ "x86-64": {"url": "../../out/nacl64/Debug/tests"},
+ "x86-32": {"url": "../../out/nacl32/Debug/tests"}
+ }
+}