Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't get NaCl C++ module to load file from within packaged app

I have a chrome packaged app that also includes a PNaCl/NaCl C++ module, as well as some data files which the NaCl module needs to read in. However, I am not able to get it to read in the files.

I set it up according to all the documentation and official examples that I could find, as well as the answer to: How to include a data file in a chrome app for a native client module to read

The nacl_io demo that comes with the SDK is able to do this, but it is in C, not C++.

I came up with a simple example, which I'll post below. When you press the button on the page, the NaCl module should load the first character of test.txt and show it. As of now, it always just responds with "-100" (the error value I put in), meaning that it could not open the file, rather than with the first character of the file.

Can anyone suggest some changes that would allow it to work correctly and load the file?

In order to run it, on the Mac at least, I use this command, with all the files in the ./file-test dir: /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --load-and-launch-app=./file-test

Note that if you try to use this, you will most likely need to change the NACL_SDK_ROOT path in the makefile.

file_test.cc

#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"

#include "nacl_io/nacl_io.h"
#include "sys/mount.h"

class FileTestInstance : public pp::Instance {
 public:
  explicit FileTestInstance(PP_Instance instance) : pp::Instance(instance)
  {
    // initialize nacl file system
    nacl_io_init_ppapi(instance, pp::Module::Get()->get_browser_interface());

    // mount the http root at /http
    mount("", "/http", "httpfs", 0, "");
  }
  virtual ~FileTestInstance() {}

  // Receive message from javascript
  virtual void HandleMessage(const pp::Var& var_message) {
    // Open and load from the file  
    int c;
    FILE *file;
    file = fopen("/http/test.txt", "r");
    if (file) {
        c = getc(file);
        fclose(file);
    } else {
        c = -100;
    }

    // Send message to JavaScript
    pp::Var var_reply(c);
    PostMessage(var_reply);
  }
};

class FileTestModule : public pp::Module {
 public:
  FileTestModule() : pp::Module() {}
  virtual ~FileTestModule() {}

  virtual pp::Instance* CreateInstance(PP_Instance instance) {
    return new FileTestInstance(instance);
  }
};

namespace pp {
Module* CreateModule() {
  return new FileTestModule();
}
}  // namespace pp

index.html

<!DOCTYPE html>
<html>
<head>
  <title>File Test</title>
 <script type="text/javascript" src="script.js"></script>
</head>
<body>

  <h1>File Test</h1>

  <input type="button" id="test" name="test" value="Test" />

  <p><b>Output:</b><p>
  <div id="output">
  </div>

  <p>
    <div id="listener">
      <embed id="file_test" width=0 height=0 src="file_test.nmf" type="application/x-pnacl" />
    </div>
  </p>
</body>
</html>

script.js

// outgoing messages
function postMessage(message) {
 var nacl_module = document.getElementById('file_test')
 nacl_module.postMessage(message);
}

// incoming messages
function handleMessage(message_event) {
  var outputDiv = document.getElementById('output');
  outputDiv.textContent = message_event.data;
}

// button action
function buttonClicked() {
    postMessage("file");
}

// set up
function init() {
    // add listener to nacl module
    var listener = document.getElementById('listener');
    listener.addEventListener('message', handleMessage, true);

    // add action to button
    document.getElementById("test").onclick = buttonClicked;
}

window.onload = init;

main.js

/**
 * Listens for the app launching then creates the window
 */
chrome.app.runtime.onLaunched.addListener(function() {
  // Center window on screen.
  var screenWidth = screen.availWidth;
  var screenHeight = screen.availHeight;
  var width = 600;
  var height = 600;

  chrome.app.window.create('index.html', {
    id: "File-TestID",
    bounds: {
      width: width,
      height: height,
      left: Math.round((screenWidth-width)/2),
      top: Math.round((screenHeight-height)/2)
    }
  });
});

file_test.nmf

{
  "program": {
    "portable": {
      "pnacl-translate": {
        "url": "file_test.pexe"
      }
    }
  }
}

Makefile

#
# Get pepper directory for toolchain and includes.
#
# If NACL_SDK_ROOT is not set, then assume where it can be found.
#
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
NACL_SDK_ROOT ?= $(abspath $(dir $(THIS_MAKEFILE))../../nacl_sdk/pepper_33)

# Project Build flags
WARNINGS := -Wno-long-long -Wall -Wswitch-enum -pedantic -Werror
CXXFLAGS := -pthread -std=gnu++98 $(WARNINGS)

#
# Compute tool paths
#
GETOS := python $(NACL_SDK_ROOT)/tools/getos.py
OSHELPERS = python $(NACL_SDK_ROOT)/tools/oshelpers.py
OSNAME := $(shell $(GETOS))
RM := $(OSHELPERS) rm

PNACL_TC_PATH := $(abspath $(NACL_SDK_ROOT)/toolchain/$(OSNAME)_pnacl)
PNACL_CXX := $(PNACL_TC_PATH)/bin/pnacl-clang++
PNACL_FINALIZE := $(PNACL_TC_PATH)/bin/pnacl-finalize
CXXFLAGS := -I$(NACL_SDK_ROOT)/include -I$(NACL_SDK_ROOT)/include/pnacl
LDFLAGS := -L$(NACL_SDK_ROOT)/lib/pnacl/Release -lppapi_cpp -lppapi -lnacl_io

#
# Disable DOS PATH warning when using Cygwin based tools Windows
#
CYGWIN ?= nodosfilewarning
export CYGWIN


# Declare the ALL target first, to make the 'all' target the default build
all: file_test.pexe

clean:
    $(RM) file_test.pexe file_test.bc

file_test.bc: file_test.cc
    $(PNACL_CXX) -o $@ $< -O2 $(CXXFLAGS) $(LDFLAGS)

file_test.pexe: file_test.bc
    $(PNACL_FINALIZE) -o $@ $<

test.txt

AAAA
like image 257
danf Avatar asked Oct 31 '22 23:10

danf


1 Answers

From Sam Clegg on the native-client-discuss list:

"I think the main problem you have is that you are trying to use nacl_io on the main thread. nacl_io, like the blocking PPAPI interfaces on which it is mostly based, will only work on background threads where blocking calls are allowed. See: https://developer.chrome.com/native-client/devguide/coding/nacl_io."

"Try running your code on a separate thread. One easy way to do this is to use the ppapi_simple library."

Using this advice, and also looking at the examples using_ppapi_simple, flock, and earth, that are included with the SDK, I was able to make a working version:

file_test.cc

#include <stdio.h>
#include "sys/mount.h"

#include <ppapi/cpp/var.h>
#include "ppapi_simple/ps_main.h"
#include "ppapi_simple/ps_event.h"
#include "ppapi_simple/ps_interface.h"


int file_test_main(int argc, char* argv[]) {
    PSEventSetFilter(PSE_ALL);

    // mount the http root at /http
    mount("", "/http", "httpfs", 0, "");

    while (true) {
        PSEvent* ps_event;
        // Consume all available events
        while ((ps_event = PSEventWaitAcquire()) != NULL) {
            // handle messages from javascript
            if (ps_event->type == PSE_INSTANCE_HANDLEMESSAGE) {
                // Convert Pepper Simple message to PPAPI C++ vars
                pp::Var var_message(ps_event->as_var);
                // process the message if it is a string
                if (var_message.is_string()) {
                    // get the string message
                    std::string message = var_message.AsString();

                    // handle message
                    if (message == "file") {
                        // Open and load from the file  
                        int c;
                        FILE *file;
                        file = fopen("/http/test.txt", "r");
                        if (file) {
                            c = getc(file);
                            fclose(file);
                        } else {
                            c = -100;
                        }

                        // Send response back to JavaScript
                        pp::Var var_reply(c);
                        PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), var_reply.pp_var());
                    }
                }
            }

            PSEventRelease(ps_event);
        }
    }

    return 0;
}

/*
 * Register the function to call once the Instance Object is initialized.
 * see: pappi_simple/ps_main.h
 */
PPAPI_SIMPLE_REGISTER_MAIN(file_test_main)

In addition, it is necessary to add -lppapi_simple to LDFLAGS in Makefile.

It would also be possible to do this handling the threads oneself, rather than using ppapi_simple, which can be seen in nacl_io_demo which is included with the SDK.

like image 99
danf Avatar answered Jan 04 '23 13:01

danf