Copy NaCl directory into trunk/platform_tools, adjust paths
authorborenet@google.com <borenet@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 6 May 2013 12:50:00 +0000 (12:50 +0000)
committerborenet@google.com <borenet@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 6 May 2013 12:50:00 +0000 (12:50 +0000)
(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

30 files changed:
gyp/debugger.gyp
gyp/nacl.gyp
platform_tools/nacl/SampleApp/SampleApp.nmf [new file with mode: 0644]
platform_tools/nacl/SampleApp/index.html [new file with mode: 0644]
platform_tools/nacl/debugger/debugger.css [new file with mode: 0644]
platform_tools/nacl/debugger/debugger.nmf [new file with mode: 0644]
platform_tools/nacl/debugger/icons/blank.png [new file with mode: 0644]
platform_tools/nacl/debugger/icons/breakpoint.png [new file with mode: 0644]
platform_tools/nacl/debugger/icons/breakpoint_16x16.png [new file with mode: 0644]
platform_tools/nacl/debugger/icons/delete.png [new file with mode: 0644]
platform_tools/nacl/debugger/icons/inspector.png [new file with mode: 0644]
platform_tools/nacl/debugger/icons/next.png [new file with mode: 0644]
platform_tools/nacl/debugger/icons/pause.png [new file with mode: 0644]
platform_tools/nacl/debugger/icons/play.png [new file with mode: 0644]
platform_tools/nacl/debugger/icons/previous.png [new file with mode: 0644]
platform_tools/nacl/debugger/icons/profile.png [new file with mode: 0644]
platform_tools/nacl/debugger/icons/reload.png [new file with mode: 0644]
platform_tools/nacl/debugger/icons/rewind.png [new file with mode: 0644]
platform_tools/nacl/debugger/icons/skia.png [new file with mode: 0644]
platform_tools/nacl/debugger/index.html [new file with mode: 0644]
platform_tools/nacl/favicon.ico [new file with mode: 0644]
platform_tools/nacl/gclient.config [new file with mode: 0644]
platform_tools/nacl/httpd.py [new file with mode: 0755]
platform_tools/nacl/index.html [new file with mode: 0644]
platform_tools/nacl/nacl_make [new file with mode: 0755]
platform_tools/nacl/src/nacl_debugger.cpp [new file with mode: 0644]
platform_tools/nacl/src/nacl_interface.cpp [new file with mode: 0644]
platform_tools/nacl/src/nacl_sample.cpp [new file with mode: 0644]
platform_tools/nacl/tests/index.html [new file with mode: 0644]
platform_tools/nacl/tests/tests.nmf [new file with mode: 0644]

index 40c0f35..b935fb0 100644 (file)
             '../src/utils',
           ],
           'sources': [
-            '../../nacl/src/nacl_debugger.cpp',
+            '../platform_tools/nacl/src/nacl_debugger.cpp',
           ],
         }, { # skia_os != "nacl"
           'include_dirs': [
index 5edb445..484570b 100644 (file)
@@ -12,7 +12,7 @@
         '../src/utils',
       ],
       'sources': [
-        '../../nacl/src/nacl_interface.cpp',
+        '../platform_tools/nacl/src/nacl_interface.cpp',
       ],
     },
   ],
diff --git a/platform_tools/nacl/SampleApp/SampleApp.nmf b/platform_tools/nacl/SampleApp/SampleApp.nmf
new file mode 100644 (file)
index 0000000..7296c07
--- /dev/null
@@ -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 (file)
index 0000000..cbdeefe
--- /dev/null
@@ -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 (file)
index 0000000..a63b7e0
--- /dev/null
@@ -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 (file)
index 0000000..7a53e52
--- /dev/null
@@ -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 (file)
index 0000000..97246fb
Binary files /dev/null and b/platform_tools/nacl/debugger/icons/blank.png differ
diff --git a/platform_tools/nacl/debugger/icons/breakpoint.png b/platform_tools/nacl/debugger/icons/breakpoint.png
new file mode 100644 (file)
index 0000000..b6290b4
Binary files /dev/null and b/platform_tools/nacl/debugger/icons/breakpoint.png 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 (file)
index 0000000..a25ece8
Binary files /dev/null and b/platform_tools/nacl/debugger/icons/breakpoint_16x16.png differ
diff --git a/platform_tools/nacl/debugger/icons/delete.png b/platform_tools/nacl/debugger/icons/delete.png
new file mode 100644 (file)
index 0000000..50b604f
Binary files /dev/null and b/platform_tools/nacl/debugger/icons/delete.png differ
diff --git a/platform_tools/nacl/debugger/icons/inspector.png b/platform_tools/nacl/debugger/icons/inspector.png
new file mode 100644 (file)
index 0000000..360f23e
Binary files /dev/null and b/platform_tools/nacl/debugger/icons/inspector.png differ
diff --git a/platform_tools/nacl/debugger/icons/next.png b/platform_tools/nacl/debugger/icons/next.png
new file mode 100644 (file)
index 0000000..aae8934
Binary files /dev/null and b/platform_tools/nacl/debugger/icons/next.png differ
diff --git a/platform_tools/nacl/debugger/icons/pause.png b/platform_tools/nacl/debugger/icons/pause.png
new file mode 100644 (file)
index 0000000..a026a54
Binary files /dev/null and b/platform_tools/nacl/debugger/icons/pause.png differ
diff --git a/platform_tools/nacl/debugger/icons/play.png b/platform_tools/nacl/debugger/icons/play.png
new file mode 100644 (file)
index 0000000..5528baf
Binary files /dev/null and b/platform_tools/nacl/debugger/icons/play.png differ
diff --git a/platform_tools/nacl/debugger/icons/previous.png b/platform_tools/nacl/debugger/icons/previous.png
new file mode 100644 (file)
index 0000000..3396a3f
Binary files /dev/null and b/platform_tools/nacl/debugger/icons/previous.png differ
diff --git a/platform_tools/nacl/debugger/icons/profile.png b/platform_tools/nacl/debugger/icons/profile.png
new file mode 100644 (file)
index 0000000..d4b8501
Binary files /dev/null and b/platform_tools/nacl/debugger/icons/profile.png differ
diff --git a/platform_tools/nacl/debugger/icons/reload.png b/platform_tools/nacl/debugger/icons/reload.png
new file mode 100644 (file)
index 0000000..316f36c
Binary files /dev/null and b/platform_tools/nacl/debugger/icons/reload.png differ
diff --git a/platform_tools/nacl/debugger/icons/rewind.png b/platform_tools/nacl/debugger/icons/rewind.png
new file mode 100644 (file)
index 0000000..d8d4902
Binary files /dev/null and b/platform_tools/nacl/debugger/icons/rewind.png differ
diff --git a/platform_tools/nacl/debugger/icons/skia.png b/platform_tools/nacl/debugger/icons/skia.png
new file mode 100644 (file)
index 0000000..017ee4f
Binary files /dev/null and b/platform_tools/nacl/debugger/icons/skia.png differ
diff --git a/platform_tools/nacl/debugger/index.html b/platform_tools/nacl/debugger/index.html
new file mode 100644 (file)
index 0000000..9d4644d
--- /dev/null
@@ -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 (file)
index 0000000..e7440c7
Binary files /dev/null and b/platform_tools/nacl/favicon.ico differ
diff --git a/platform_tools/nacl/gclient.config b/platform_tools/nacl/gclient.config
new file mode 100644 (file)
index 0000000..922ae43
--- /dev/null
@@ -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 (executable)
index 0000000..2ad1d49
--- /dev/null
@@ -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 (file)
index 0000000..abe5d1e
--- /dev/null
@@ -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 (executable)
index 0000000..81f6eb2
--- /dev/null
@@ -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 (file)
index 0000000..26e3ed6
--- /dev/null
@@ -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 (file)
index 0000000..09d55c3
--- /dev/null
@@ -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 (file)
index 0000000..aca74f4
--- /dev/null
@@ -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 (file)
index 0000000..3a0bdb9
--- /dev/null
@@ -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 (file)
index 0000000..95db050
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "program": {
+    "x86-64": {"url": "../../out/nacl64/Debug/tests"},
+    "x86-32": {"url": "../../out/nacl32/Debug/tests"}
+  }
+}