Merge from Chromium at DEPS revision r207203

This commit was generated by merge_to_master.py.

Change-Id: I5fbb6854d092096c4d39edc2865a48be1b53c418
diff --git a/ppapi/native_client/src/trusted/plugin/file_downloader.cc b/ppapi/native_client/src/trusted/plugin/file_downloader.cc
index 1708124..b66a267 100644
--- a/ppapi/native_client/src/trusted/plugin/file_downloader.cc
+++ b/ppapi/native_client/src/trusted/plugin/file_downloader.cc
@@ -55,6 +55,7 @@
     const nacl::string& url,
     const pp::CompletionCallback& callback,
     StreamCallbackSource* stream_callback_source) {
+  open_and_stream_ = false;
   data_stream_callback_source_ = stream_callback_source;
   return Open(url, DOWNLOAD_STREAM, callback, true, NULL);
 }
@@ -218,31 +219,34 @@
 
 bool FileDownloader::InitialResponseIsValid(int32_t pp_error) {
   if (pp_error != PP_OK) {  // Url loading failed.
-    file_open_notify_callback_.Run(pp_error);
+    file_open_notify_callback_.RunAndClear(pp_error);
     return false;
   }
 
   // Process the response, validating the headers to confirm successful loading.
-  pp::URLResponseInfo url_response(url_loader_.GetResponseInfo());
-  if (url_response.is_null()) {
+  url_response_ = url_loader_.GetResponseInfo();
+  if (url_response_.is_null()) {
     PLUGIN_PRINTF((
-        "FileDownloader::InitialResponseIsValid (url_response=NULL)\n"));
-    file_open_notify_callback_.Run(PP_ERROR_FAILED);
+        "FileDownloader::InitialResponseIsValid (url_response_=NULL)\n"));
+    file_open_notify_callback_.RunAndClear(PP_ERROR_FAILED);
     return false;
   }
-  // Note that URLs in the chrome-extension scheme produce different error
-  // codes than other schemes.  This is because chrome-extension URLs are
-  // really a special kind of file scheme, and therefore do not produce HTTP
-  // status codes.
-  pp::Var full_url = url_response.GetURL();
+
+  pp::Var full_url = url_response_.GetURL();
   if (!full_url.is_string()) {
     PLUGIN_PRINTF((
         "FileDownloader::InitialResponseIsValid (url is not a string)\n"));
-    file_open_notify_callback_.Run(PP_ERROR_FAILED);
+    file_open_notify_callback_.RunAndClear(PP_ERROR_FAILED);
     return false;
   }
+  url_ = full_url.AsString();
+
+  // Note that URLs in the data-URI scheme produce different error
+  // codes than other schemes.  This is because data-URI are really a
+  // special kind of file scheme, and therefore do not produce HTTP
+  // status codes.
   bool status_ok = false;
-  status_code_ = url_response.GetStatusCode();
+  status_code_ = url_response_.GetStatusCode();
   switch (url_scheme_) {
     case SCHEME_CHROME_EXTENSION:
       PLUGIN_PRINTF(("FileDownloader::InitialResponseIsValid (chrome-extension "
@@ -262,7 +266,7 @@
   }
 
   if (!status_ok) {
-    file_open_notify_callback_.Run(PP_ERROR_FAILED);
+    file_open_notify_callback_.RunAndClear(PP_ERROR_FAILED);
     return false;
   }
 
@@ -273,20 +277,65 @@
   PLUGIN_PRINTF(("FileDownloader::URLLoadStartNotify (pp_error=%"
                  NACL_PRId32")\n", pp_error));
 
-  if (!InitialResponseIsValid(pp_error))
+  if (!InitialResponseIsValid(pp_error)) {
+    // InitialResponseIsValid() calls file_open_notify_callback_ on errors.
     return;
+  }
+
+  if (open_and_stream_)
+    return FinishStreaming(file_open_notify_callback_);
+
+  file_open_notify_callback_.RunAndClear(pp_error);
+}
+
+void FileDownloader::URLBufferStartNotify(int32_t pp_error) {
+  PLUGIN_PRINTF(("FileDownloader::URLBufferStartNotify (pp_error=%"
+                 NACL_PRId32")\n", pp_error));
+
+  if (!InitialResponseIsValid(pp_error)) {
+    // InitialResponseIsValid() calls file_open_notify_callback_ on errors.
+    return;
+  }
+
+  if (open_and_stream_)
+    return FinishStreaming(file_open_notify_callback_);
+
+  file_open_notify_callback_.RunAndClear(pp_error);
+}
+
+void FileDownloader::FinishStreaming(
+    const pp::CompletionCallback& callback) {
+  stream_finish_callback_ = callback;
+
   // Finish streaming the body providing an optional callback.
-  pp::CompletionCallback onload_callback =
-      callback_factory_.NewOptionalCallback(
-          &FileDownloader::URLLoadFinishNotify);
-  pp_error = url_loader_.FinishStreamingToFile(onload_callback);
-  bool async_notify_ok = (pp_error == PP_OK_COMPLETIONPENDING);
-  PLUGIN_PRINTF(("FileDownloader::URLLoadStartNotify (async_notify_ok=%d)\n",
-                 async_notify_ok));
-  if (!async_notify_ok) {
-    // Call manually to free allocated memory and report errors.  This calls
-    // |file_open_notify_callback_| with |pp_error| as the parameter.
-    onload_callback.Run(pp_error);
+  if (streaming_to_file()) {
+    pp::CompletionCallback onload_callback =
+        callback_factory_.NewOptionalCallback(
+            &FileDownloader::URLLoadFinishNotify);
+    int32_t pp_error = url_loader_.FinishStreamingToFile(onload_callback);
+    bool async_notify_ok = (pp_error == PP_OK_COMPLETIONPENDING);
+    PLUGIN_PRINTF(("FileDownloader::FinishStreaming (async_notify_ok=%d)\n",
+                   async_notify_ok));
+    if (!async_notify_ok) {
+      // Call manually to free allocated memory and report errors.  This calls
+      // |stream_finish_callback_| with |pp_error| as the parameter.
+      onload_callback.RunAndClear(pp_error);
+    }
+  } else {
+    pp::CompletionCallback onread_callback =
+        callback_factory_.NewOptionalCallback(
+            &FileDownloader::URLReadBodyNotify);
+    int32_t temp_size = static_cast<int32_t>(temp_buffer_.size());
+    int32_t pp_error = url_loader_.ReadResponseBody(&temp_buffer_[0],
+                                                    temp_size,
+                                                    onread_callback);
+    bool async_notify_ok = (pp_error == PP_OK_COMPLETIONPENDING);
+    PLUGIN_PRINTF((
+        "FileDownloader::FinishStreaming (async_notify_ok=%d)\n",
+        async_notify_ok));
+    if (!async_notify_ok) {
+      onread_callback.RunAndClear(pp_error);
+    }
   }
 }
 
@@ -294,66 +343,46 @@
   PLUGIN_PRINTF(("FileDownloader::URLLoadFinishNotify (pp_error=%"
                  NACL_PRId32")\n", pp_error));
   if (pp_error != PP_OK) {  // Streaming failed.
-    file_open_notify_callback_.Run(pp_error);
+    stream_finish_callback_.RunAndClear(pp_error);
     return;
   }
 
-  pp::URLResponseInfo url_response(url_loader_.GetResponseInfo());
-  // Validated on load.
-  CHECK(url_response.GetStatusCode() == NACL_HTTP_STATUS_OK ||
-        url_response.GetStatusCode() == kExtensionUrlRequestStatusOk);
+  // Validate response again on load (though it should be the same
+  // as it was during InitialResponseIsValid?).
+  url_response_ = url_loader_.GetResponseInfo();
+  CHECK(url_response_.GetStatusCode() == NACL_HTTP_STATUS_OK ||
+        url_response_.GetStatusCode() == kExtensionUrlRequestStatusOk);
 
   // Record the full url from the response.
-  pp::Var full_url = url_response.GetURL();
+  pp::Var full_url = url_response_.GetURL();
   PLUGIN_PRINTF(("FileDownloader::URLLoadFinishNotify (full_url=%s)\n",
                  full_url.DebugString().c_str()));
   if (!full_url.is_string()) {
-    file_open_notify_callback_.Run(PP_ERROR_FAILED);
+    stream_finish_callback_.RunAndClear(PP_ERROR_FAILED);
     return;
   }
   url_ = full_url.AsString();
 
   // The file is now fully downloaded.
-  pp::FileRef file(url_response.GetBodyAsFileRef());
+  pp::FileRef file(url_response_.GetBodyAsFileRef());
   if (file.is_null()) {
     PLUGIN_PRINTF(("FileDownloader::URLLoadFinishNotify (file=NULL)\n"));
-    file_open_notify_callback_.Run(PP_ERROR_FAILED);
+    stream_finish_callback_.RunAndClear(PP_ERROR_FAILED);
     return;
   }
 
   // Open the file providing an optional callback.
   pp::CompletionCallback onopen_callback =
-      callback_factory_.NewOptionalCallback(&FileDownloader::FileOpenNotify);
+      callback_factory_.NewOptionalCallback(
+          &FileDownloader::StreamFinishNotify);
   pp_error = file_reader_.Open(file, PP_FILEOPENFLAG_READ, onopen_callback);
   bool async_notify_ok = (pp_error == PP_OK_COMPLETIONPENDING);
   PLUGIN_PRINTF(("FileDownloader::URLLoadFinishNotify (async_notify_ok=%d)\n",
                  async_notify_ok));
   if (!async_notify_ok) {
     // Call manually to free allocated memory and report errors.  This calls
-    // |file_open_notify_callback_| with |pp_error| as the parameter.
-    onopen_callback.Run(pp_error);
-  }
-}
-
-void FileDownloader::URLBufferStartNotify(int32_t pp_error) {
-  PLUGIN_PRINTF(("FileDownloader::URLBufferStartNotify (pp_error=%"
-                 NACL_PRId32")\n", pp_error));
-
-  if (!InitialResponseIsValid(pp_error))
-    return;
-  // Finish streaming the body asynchronously providing a callback.
-  pp::CompletionCallback onread_callback =
-      callback_factory_.NewOptionalCallback(&FileDownloader::URLReadBodyNotify);
-
-  int32_t temp_size = static_cast<int32_t>(temp_buffer_.size());
-  pp_error = url_loader_.ReadResponseBody(&temp_buffer_[0],
-                                          temp_size,
-                                          onread_callback);
-  bool async_notify_ok = (pp_error == PP_OK_COMPLETIONPENDING);
-  PLUGIN_PRINTF(("FileDownloader::URLBufferStartNotify (async_notify_ok=%d)\n",
-                 async_notify_ok));
-  if (!async_notify_ok) {
-    onread_callback.Run(pp_error);
+    // |stream_finish_callback_| with |pp_error| as the parameter.
+    onopen_callback.RunAndClear(pp_error);
   }
 }
 
@@ -361,12 +390,12 @@
   PLUGIN_PRINTF(("FileDownloader::URLReadBodyNotify (pp_error=%"
                  NACL_PRId32")\n", pp_error));
   if (pp_error < PP_OK) {
-    file_open_notify_callback_.Run(pp_error);
+    stream_finish_callback_.RunAndClear(pp_error);
   } else if (pp_error == PP_OK) {
     if (streaming_to_user()) {
-      data_stream_callback_source_->GetCallback().Run(PP_OK);
+      data_stream_callback_source_->GetCallback().RunAndClear(PP_OK);
     }
-    FileOpenNotify(PP_OK);
+    StreamFinishNotify(PP_OK);
   } else {
     if (streaming_to_buffer()) {
       buffer_.insert(buffer_.end(), &temp_buffer_[0], &temp_buffer_[pp_error]);
@@ -375,7 +404,7 @@
                      &temp_buffer_[0]));
       StreamCallback cb = data_stream_callback_source_->GetCallback();
       *(cb.output()) = &temp_buffer_;
-      cb.Run(pp_error);
+      cb.RunAndClear(pp_error);
     }
     pp::CompletionCallback onread_callback =
         callback_factory_.NewOptionalCallback(
@@ -386,7 +415,7 @@
                                             onread_callback);
     bool async_notify_ok = (pp_error == PP_OK_COMPLETIONPENDING);
     if (!async_notify_ok) {
-      onread_callback.Run(pp_error);
+      onread_callback.RunAndClear(pp_error);
     }
   }
 }
@@ -398,10 +427,21 @@
                                          total_bytes_to_be_received);
 }
 
-void FileDownloader::FileOpenNotify(int32_t pp_error) {
-  PLUGIN_PRINTF(("FileDownloader::FileOpenNotify (pp_error=%"NACL_PRId32")\n",
-                 pp_error));
-  file_open_notify_callback_.Run(pp_error);
+nacl::string FileDownloader::GetResponseHeaders() const {
+  pp::Var headers = url_response_.GetHeaders();
+  if (!headers.is_string()) {
+    PLUGIN_PRINTF((
+        "FileDownloader::GetResponseHeaders (headers are not a string)\n"));
+    return nacl::string();
+  }
+  return headers.AsString();
+}
+
+void FileDownloader::StreamFinishNotify(int32_t pp_error) {
+  PLUGIN_PRINTF((
+      "FileDownloader::StreamFinishNotify (pp_error=%"NACL_PRId32")\n",
+      pp_error));
+  stream_finish_callback_.RunAndClear(pp_error);
 }
 
 bool FileDownloader::streaming_to_file() const {
diff --git a/ppapi/native_client/src/trusted/plugin/file_downloader.h b/ppapi/native_client/src/trusted/plugin/file_downloader.h
index 7d87930..f72e717 100644
--- a/ppapi/native_client/src/trusted/plugin/file_downloader.h
+++ b/ppapi/native_client/src/trusted/plugin/file_downloader.h
@@ -15,8 +15,9 @@
 #include "ppapi/c/trusted/ppb_file_io_trusted.h"
 #include "ppapi/c/trusted/ppb_url_loader_trusted.h"
 #include "ppapi/cpp/file_io.h"
-#include "ppapi/cpp/url_loader.h"
 #include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/url_loader.h"
+#include "ppapi/cpp/url_response_info.h"
 #include "ppapi/utility/completion_callback_factory.h"
 
 namespace plugin {
@@ -49,11 +50,13 @@
   FileDownloader()
       : instance_(NULL),
         file_open_notify_callback_(pp::BlockUntilComplete()),
+        stream_finish_callback_(pp::BlockUntilComplete()),
         file_handle_(PP_kInvalidFileHandle),
         file_io_trusted_interface_(NULL),
         url_loader_trusted_interface_(NULL),
         open_time_(-1),
         mode_(DOWNLOAD_NONE),
+        open_and_stream_(true),
         url_scheme_(SCHEME_OTHER),
         data_stream_callback_source_(NULL) {}
   ~FileDownloader() {}
@@ -61,8 +64,9 @@
   // Initialize() can only be called once during the lifetime of this instance.
   void Initialize(Plugin* instance);
 
-  // Issues a GET on |url| downloading the response into a file. The file is
-  // then opened and a file descriptor is made available.
+  // Issues a GET on |url| to start downloading the response into a file,
+  // and finish streaming it. |callback| will be run after streaming is
+  // done or if an error prevents streaming from completing.
   // Returns true when callback is scheduled to be called on success or failure.
   // Returns false if callback is NULL, Initialize() has not been called or if
   // the PPB_FileIO_Trusted interface is not available.
@@ -77,14 +81,22 @@
             bool record_progress,
             PP_URLLoaderTrusted_StatusCallback progress_callback);
 
-  // Same as Open, but used for streaming the file data directly to the
-  // caller without buffering it. The callbacks provided by
+  // Similar to Open(), but used for streaming the |url| data directly to the
+  // caller without writing to a temporary file. The callbacks provided by
   // |stream_callback_source| are expected to copy the data before returning.
-  // |callback| will still be called when the stream is finished.
+  // |callback| is called once the response headers are received,
+  // and streaming must be completed separately via FinishStreaming().
   bool OpenStream(const nacl::string& url,
                   const pp::CompletionCallback& callback,
                   StreamCallbackSource* stream_callback_source);
 
+  // Finish streaming the response body for a URL request started by either
+  // Open() or OpenStream().  If DownloadMode is DOWNLOAD_TO_FILE,
+  // then the response body is streamed to a file, the file is opened and
+  // a file descriptor is made available.  Runs the given |callback| when
+  // streaming is done.
+  void FinishStreaming(const pp::CompletionCallback& callback);
+
   // Bypasses downloading and takes a handle to the open file. To get the fd,
   // call GetFileInfo().
   void OpenFast(const nacl::string& url, PP_FileHandle file_handle,
@@ -135,6 +147,7 @@
   bool not_streaming() const;
 
   int status_code() const { return status_code_; }
+  nacl::string GetResponseHeaders() const;
 
  private:
   NACL_DISALLOW_COPY_AND_ASSIGN(FileDownloader);
@@ -150,17 +163,21 @@
   // through a factory to take advantage of ref-counting.
   // DOWNLOAD_STREAM is similar to DOWNLOAD_TO_BUFFER except the downloaded
   // data is passed directly to the user instead of saved in a buffer.
+  // The public Open*() functions start step 1), and the public FinishStreaming
+  // function proceeds to step 2) and 3).
   bool InitialResponseIsValid(int32_t pp_error);
   void URLLoadStartNotify(int32_t pp_error);
   void URLLoadFinishNotify(int32_t pp_error);
   void URLBufferStartNotify(int32_t pp_error);
   void URLReadBodyNotify(int32_t pp_error);
-  void FileOpenNotify(int32_t pp_error);
+  void StreamFinishNotify(int32_t pp_error);
 
   Plugin* instance_;
   nacl::string url_to_open_;
   nacl::string url_;
+  pp::URLResponseInfo url_response_;
   pp::CompletionCallback file_open_notify_callback_;
+  pp::CompletionCallback stream_finish_callback_;
   pp::FileIO file_reader_;
   PP_FileHandle file_handle_;
   struct NaClFileToken file_token_;
@@ -171,6 +188,7 @@
   int64_t open_time_;
   int32_t status_code_;
   DownloadMode mode_;
+  bool open_and_stream_;
   static const uint32_t kTempBufferSize = 2048;
   std::vector<char> temp_buffer_;
   std::deque<char> buffer_;
diff --git a/ppapi/native_client/src/trusted/plugin/json_manifest.cc b/ppapi/native_client/src/trusted/plugin/json_manifest.cc
index d1f70ff..d696059 100644
--- a/ppapi/native_client/src/trusted/plugin/json_manifest.cc
+++ b/ppapi/native_client/src/trusted/plugin/json_manifest.cc
@@ -41,23 +41,14 @@
 const char* const kUrlKey =            "url";
 
 // Pnacl keys
-const char* const kCacheIdentityKey = "sha256";
 const char* const kOptLevelKey = "-O";
-const char* const kPnaclExperimentalFlags = "experimental_flags";
 
-// Sample manifest file:
+// Sample NaCL manifest file:
 // {
 //   "program": {
 //     "x86-32": {"url": "myprogram_x86-32.nexe"},
 //     "x86-64": {"url": "myprogram_x86-64.nexe"},
-//     "arm": {"url": "myprogram_arm.nexe"},
-//     "portable": {
-//       "pnacl-translate": {
-//         "url": "myprogram.pexe",
-//         "sha256": "...",
-//         "-O": 0
-//       }
-//     }
+//     "arm": {"url": "myprogram_arm.nexe"}
 //   },
 //   "interpreter": {
 //     "x86-32": {"url": "interpreter_x86-32.nexe"},
@@ -73,10 +64,27 @@
 //       "portable": {"url": "bar.txt"}
 //     },
 //     "libfoo.so": {
-//       "x86-64-ivybridge-foo": { "url": "..."},
-//       "x86-64-ivybridge" : { "pnacl-translate": { "url": "..."}},
-//       "x86-64" : { "url": "..." },
-//       "portable": {"pnacl-translate": {"url": "..."}}
+//       "x86-64" : { "url": "..." }
+//     }
+//   }
+// }
+
+// Sample PNaCl manifest file:
+// {
+//   "program": {
+//     "portable": {
+//       "pnacl-translate": {
+//         "url": "myprogram.pexe",
+//         "-O": 0
+//       }
+//     }
+//   },
+//   "files": {
+//     "foo.txt": {
+//       "portable": {"url": "foo.txt"}
+//     },
+//     "bar.txt": {
+//       "portable": {"url": "bar.txt"}
 //     }
 //   }
 // }
@@ -122,7 +130,8 @@
     if (!FindMatchingProperty(property_name,
                               valid_keys,
                               valid_key_count)) {
-      // TODO(jvoung): Should this set error_string and return false?
+      // For forward compatibility, we do not prohibit other keys being in
+      // the dictionary.
       PLUGIN_PRINTF(("WARNING: '%s' property '%s' has unknown key '%s'.\n",
                      parent_key.c_str(),
                      container_key.c_str(), property_name.c_str()));
@@ -147,22 +156,43 @@
 bool IsValidUrlSpec(const Json::Value& url_spec,
                     const nacl::string& container_key,
                     const nacl::string& parent_key,
+                    const nacl::string& sandbox_isa,
                     nacl::string* error_string) {
   static const char* kManifestUrlSpecRequired[] = {
     kUrlKey
   };
-  static const char* kManifestUrlSpecPlusOptional[] = {
-    kUrlKey,
-    kCacheIdentityKey
-  };
+  const char** urlSpecPlusOptional;
+  size_t urlSpecPlusOptionalLength;
+  if (sandbox_isa == kPortableKey) {
+    static const char* kPnaclUrlSpecPlusOptional[] = {
+      kUrlKey,
+      kOptLevelKey,
+    };
+    urlSpecPlusOptional = kPnaclUrlSpecPlusOptional;
+    urlSpecPlusOptionalLength = NACL_ARRAY_SIZE(kPnaclUrlSpecPlusOptional);
+  } else {
+    urlSpecPlusOptional = kManifestUrlSpecRequired;
+    urlSpecPlusOptionalLength = NACL_ARRAY_SIZE(kManifestUrlSpecRequired);
+  }
   if (!IsValidDictionary(url_spec, container_key, parent_key,
-                         kManifestUrlSpecPlusOptional,
-                         NACL_ARRAY_SIZE(kManifestUrlSpecPlusOptional),
+                         urlSpecPlusOptional,
+                         urlSpecPlusOptionalLength,
                          kManifestUrlSpecRequired,
                          NACL_ARRAY_SIZE(kManifestUrlSpecRequired),
                          error_string)) {
     return false;
   }
+  // URL specifications must not contain "pnacl-translate" keys.
+  // This prohibits NaCl clients from invoking PNaCl.
+  Json::Value translate = url_spec[kPnaclTranslateKey];
+  if (!translate.empty()) {
+    nacl::stringstream error_stream;
+    error_stream << parent_key << " property '" << container_key <<
+        "' has '" << kPnaclTranslateKey << "' inside URL spec.";
+    *error_string = error_stream.str();
+    return false;
+  }
+  // Verify the correct types of the fields if they exist.
   Json::Value url = url_spec[kUrlKey];
   if (!url.isString()) {
     nacl::stringstream error_stream;
@@ -172,6 +202,15 @@
     *error_string = error_stream.str();
     return false;
   }
+  Json::Value opt_level = url_spec[kOptLevelKey];
+  if (!opt_level.empty() && !opt_level.isNumeric()) {
+    nacl::stringstream error_stream;
+    error_stream << parent_key << " property '" << container_key <<
+        "' has non-numeric value '" << opt_level.toStyledString() <<
+        "' for key '" << kOptLevelKey << "'.";
+    *error_string = error_stream.str();
+    return false;
+  }
   return true;
 }
 
@@ -180,6 +219,7 @@
 bool IsValidPnaclTranslateSpec(const Json::Value& pnacl_spec,
                                const nacl::string& container_key,
                                const nacl::string& parent_key,
+                               const nacl::string& sandbox_isa,
                                nacl::string* error_string) {
   static const char* kManifestPnaclSpecProperties[] = {
     kPnaclTranslateKey
@@ -194,7 +234,7 @@
   }
   Json::Value url_spec = pnacl_spec[kPnaclTranslateKey];
   if (!IsValidUrlSpec(url_spec, kPnaclTranslateKey,
-                      container_key, error_string)) {
+                      container_key, sandbox_isa, error_string)) {
     return false;
   }
   return true;
@@ -210,6 +250,7 @@
 bool IsValidISADictionary(const Json::Value& dictionary,
                           const nacl::string& parent_key,
                           const nacl::string& sandbox_isa,
+                          bool must_find_matching_entry,
                           ErrorInfo* error_info) {
   if (error_info == NULL) return false;
 
@@ -220,39 +261,86 @@
                           " property is not an ISA to URL dictionary");
     return false;
   }
-  // The keys to the dictionary have to be valid ISA names.
-  Json::Value::Members members = dictionary.getMemberNames();
-  for (size_t i = 0; i < members.size(); ++i) {
-    // The known ISA values for ISA dictionaries in the manifest.
-    static const char* kManifestISAProperties[] = {
+  // Build the set of reserved ISA dictionary keys.
+  const char** isaProperties;
+  size_t isaPropertiesLength;
+  if (sandbox_isa == kPortableKey) {
+    // The known values for PNaCl ISA dictionaries in the manifest.
+    static const char* kPnaclManifestISAProperties[] = {
+      kPortableKey
+    };
+    isaProperties = kPnaclManifestISAProperties;
+    isaPropertiesLength = NACL_ARRAY_SIZE(kPnaclManifestISAProperties);
+  } else {
+    // The known values for NaCl ISA dictionaries in the manifest.
+    static const char* kNaClManifestISAProperties[] = {
       kX8632Key,
       kX8664Key,
       kArmKey,
+      // "portable" is here to allow checking that, if present, it can
+      // only refer to an URL, such as for a data file, and not to
+      // "pnacl-translate", which would cause the creation of a nexe.
       kPortableKey
     };
+    isaProperties = kNaClManifestISAProperties;
+    isaPropertiesLength = NACL_ARRAY_SIZE(kNaClManifestISAProperties);
+  }
+  // Check that entries in the dictionary are structurally correct.
+  Json::Value::Members members = dictionary.getMemberNames();
+  for (size_t i = 0; i < members.size(); ++i) {
     nacl::string property_name = members[i];
-    if (!FindMatchingProperty(property_name,
-                              kManifestISAProperties,
-                              NACL_ARRAY_SIZE(kManifestISAProperties))) {
-      PLUGIN_PRINTF(("IsValidISADictionary: unrecognized ISA '%s'.\n",
-                     property_name.c_str()));
-    }
-    // Could be "arch/portable" : URLSpec, or
-    // it could be "arch/portable" : { "pnacl-translate": URLSpec }
-    // for executables that need to be translated.
     Json::Value property_value = dictionary[property_name];
     nacl::string error_string;
-    if (!IsValidUrlSpec(property_value, property_name, parent_key,
-                        &error_string) &&
-        !IsValidPnaclTranslateSpec(property_value, property_name,
-                                   parent_key, &error_string)) {
-      error_info->SetReport(ERROR_MANIFEST_SCHEMA_VALIDATE,
-                            nacl::string("manifest: ") + error_string);
-      return false;
+    if (FindMatchingProperty(property_name,
+                             isaProperties,
+                             isaPropertiesLength)) {
+      // For NaCl, arch entries can only be
+      //     "arch/portable" : URLSpec
+      // For PNaCl arch in "program" dictionary entries can only be
+      //     "portable" : { "pnacl-translate": URLSpec }
+      // For PNaCl arch elsewhere, dictionary entries can only be
+      //     "portable" : URLSpec
+      if ((sandbox_isa != kPortableKey &&
+           !IsValidUrlSpec(property_value, property_name, parent_key,
+                           sandbox_isa, &error_string)) ||
+          (sandbox_isa == kPortableKey &&
+           parent_key == kProgramKey &&
+           !IsValidPnaclTranslateSpec(property_value, property_name, parent_key,
+                                      sandbox_isa, &error_string)) ||
+          (sandbox_isa == kPortableKey &&
+           parent_key != kProgramKey &&
+           !IsValidUrlSpec(property_value, property_name, parent_key,
+                           sandbox_isa, &error_string))) {
+        error_info->SetReport(ERROR_MANIFEST_SCHEMA_VALIDATE,
+                              nacl::string("manifest: ") + error_string);
+        return false;
+      }
+    } else {
+      // For forward compatibility, we do not prohibit other keys being in
+      // the dictionary, as they may be architectures supported in later
+      // versions.  However, the value of these entries must be an URLSpec.
+      PLUGIN_PRINTF(("IsValidISADictionary: unrecognized key '%s'.\n",
+                     property_name.c_str()));
+      if (!IsValidUrlSpec(property_value, property_name, parent_key,
+                          sandbox_isa, &error_string)) {
+        error_info->SetReport(ERROR_MANIFEST_SCHEMA_VALIDATE,
+                              nacl::string("manifest: ") + error_string);
+        return false;
+      }
     }
   }
 
-  if (!sandbox_isa.empty()) {
+  if (sandbox_isa == kPortableKey) {
+    bool has_portable = dictionary.isMember(kPortableKey);
+
+    if (!has_portable) {
+      error_info->SetReport(
+          ERROR_MANIFEST_PROGRAM_MISSING_ARCH,
+          nacl::string("manifest: no version of ") + parent_key +
+          " given for portable.");
+      return false;
+    }
+  } else if (must_find_matching_entry) {
     // TODO(elijahtaylor) add ISA resolver here if we expand ISAs to include
     // micro-architectures that can resolve to multiple valid sandboxes.
     bool has_isa = dictionary.isMember(sandbox_isa);
@@ -274,9 +362,6 @@
                             nacl::string* url,
                             PnaclOptions* pnacl_options) {
   *url = url_spec[kUrlKey].asString();
-  if (url_spec.isMember(kCacheIdentityKey)) {
-    pnacl_options->set_bitcode_hash(url_spec[kCacheIdentityKey].asString());
-  }
   if (url_spec.isMember(kOptLevelKey)) {
     uint32_t opt_raw = url_spec[kOptLevelKey].asUInt();
     // Clamp the opt value to fit into an int8_t.
@@ -284,23 +369,21 @@
       opt_raw = 3;
     pnacl_options->set_opt_level(static_cast<int8_t>(opt_raw));
   }
-  if (url_spec.isMember(kPnaclExperimentalFlags)) {
-    pnacl_options->set_experimental_flags(
-        url_spec[kPnaclExperimentalFlags].asString());
-  }
 }
 
 bool GetURLFromISADictionary(const Json::Value& dictionary,
                              const nacl::string& parent_key,
                              const nacl::string& sandbox_isa,
-                             bool prefer_portable,
                              nacl::string* url,
                              PnaclOptions* pnacl_options,
                              ErrorInfo* error_info) {
   if (url == NULL || pnacl_options == NULL || error_info == NULL)
     return false;
 
-  if (!IsValidISADictionary(dictionary, parent_key, sandbox_isa, error_info)) {
+  // When the application actually requests a resolved URL, we must have
+  // a matching entry (sandbox_isa or portable) for NaCl.
+  if (!IsValidISADictionary(dictionary, parent_key, sandbox_isa, true,
+                            error_info)) {
     error_info->SetReport(ERROR_MANIFEST_RESOLVE_URL,
                           "architecture " + sandbox_isa +
                           " is not found for file " + parent_key);
@@ -314,7 +397,7 @@
   bool has_portable = dictionary.isMember(kPortableKey);
   bool has_isa = dictionary.isMember(sandbox_isa);
   nacl::string chosen_isa;
-  if ((has_portable && prefer_portable) || !has_isa) {
+  if ((sandbox_isa == kPortableKey) || (has_portable && !has_isa)) {
     chosen_isa = kPortableKey;
   } else {
     chosen_isa = sandbox_isa;
@@ -323,9 +406,11 @@
   // Check if this requires a pnacl-translate, otherwise just grab the URL.
   // We may have pnacl-translate for isa-specific bitcode for CPU tuning.
   if (isa_spec.isMember(kPnaclTranslateKey)) {
+    // PNaCl
     GrabUrlAndPnaclOptions(isa_spec[kPnaclTranslateKey], url, pnacl_options);
     pnacl_options->set_translate(true);
   } else {
+    // NaCl
     *url = isa_spec[kUrlKey].asString();
     pnacl_options->set_translate(false);
   }
@@ -337,7 +422,6 @@
                const nacl::string& key,
                const nacl::string& sandbox_isa,
                const Manifest* manifest,
-               bool prefer_portable,
                nacl::string* full_url,
                PnaclOptions* pnacl_options,
                ErrorInfo* error_info) {
@@ -349,8 +433,8 @@
   }
   const Json::Value& isa_dict = dictionary[key];
   nacl::string relative_url;
-  if (!GetURLFromISADictionary(isa_dict, key, sandbox_isa, prefer_portable,
-                               &relative_url, pnacl_options, error_info)) {
+  if (!GetURLFromISADictionary(isa_dict, key, sandbox_isa, &relative_url,
+                               pnacl_options, error_info)) {
     return false;
   }
   return manifest->ResolveURL(relative_url, full_url, error_info);
@@ -410,24 +494,33 @@
   }
 
   // Validate the program section.
+  // There must be a matching (portable or sandbox_isa_) entry for program for
+  // NaCl.
   if (!IsValidISADictionary(dictionary_[kProgramKey],
                             kProgramKey,
                             sandbox_isa_,
+                            true,
                             error_info)) {
     return false;
   }
 
   // Validate the interpreter section (if given).
+  // There must be a matching (portable or sandbox_isa_) entry for interpreter
+  // for NaCl.
   if (dictionary_.isMember(kInterpreterKey)) {
     if (!IsValidISADictionary(dictionary_[kInterpreterKey],
                               kInterpreterKey,
                               sandbox_isa_,
+                              true,
                               error_info)) {
       return false;
     }
   }
 
   // Validate the file dictionary (if given).
+  // The "files" key does not require a matching (portable or sandbox_isa_)
+  // entry at schema validation time for NaCl.  This allows manifests to specify
+  // resources that are only loaded for a particular sandbox_isa.
   if (dictionary_.isMember(kFilesKey)) {
     const Json::Value& files = dictionary_[kFilesKey];
     if (!files.isObject()) {
@@ -440,7 +533,8 @@
       nacl::string file_name = members[i];
       if (!IsValidISADictionary(files[file_name],
                                 file_name,
-                                nacl::string(),
+                                sandbox_isa_,
+                                false,
                                 error_info)) {
         return false;
       }
@@ -484,7 +578,6 @@
   if (!GetURLFromISADictionary(program,
                                kProgramKey,
                                sandbox_isa_,
-                               prefer_portable_,
                                &nexe_url,
                                pnacl_options,
                                error_info)) {
@@ -519,8 +612,8 @@
     return false;
 
   if (key == kProgramKey) {
-    return GetKeyUrl(dictionary_, key, sandbox_isa_, this, prefer_portable_,
-                     full_url, pnacl_options, error_info);
+    return GetKeyUrl(dictionary_, key, sandbox_isa_, this, full_url,
+                     pnacl_options, error_info);
   }
   nacl::string::const_iterator p = find(key.begin(), key.end(), '/');
   if (p == key.end()) {
@@ -554,8 +647,8 @@
         nacl::string("ResolveKey: no such \"files\" entry: ") + key);
     return false;
   }
-  return GetKeyUrl(files, rest, sandbox_isa_, this, prefer_portable_,
-                   full_url, pnacl_options, error_info);
+  return GetKeyUrl(files, rest, sandbox_isa_, this, full_url, pnacl_options,
+                   error_info);
 }
 
 }  // namespace plugin
diff --git a/ppapi/native_client/src/trusted/plugin/json_manifest.h b/ppapi/native_client/src/trusted/plugin/json_manifest.h
index e2e4fbc..102d055 100644
--- a/ppapi/native_client/src/trusted/plugin/json_manifest.h
+++ b/ppapi/native_client/src/trusted/plugin/json_manifest.h
@@ -31,12 +31,10 @@
  public:
   JsonManifest(const pp::URLUtil_Dev* url_util,
            const nacl::string& manifest_base_url,
-           const nacl::string& sandbox_isa,
-           bool prefer_portable)
+           const nacl::string& sandbox_isa)
       : url_util_(url_util),
         manifest_base_url_(manifest_base_url),
         sandbox_isa_(sandbox_isa),
-        prefer_portable_(prefer_portable),
         dictionary_(Json::nullValue) { }
   virtual ~JsonManifest() { }
 
@@ -81,9 +79,6 @@
   const pp::URLUtil_Dev* url_util_;
   nacl::string manifest_base_url_;
   nacl::string sandbox_isa_;
-  // Determines whether portable programs are chosen in manifest files over
-  // native programs.
-  bool prefer_portable_;
 
   Json::Value dictionary_;
 };
diff --git a/ppapi/native_client/src/trusted/plugin/nacl_entry_points.h b/ppapi/native_client/src/trusted/plugin/nacl_entry_points.h
index 3d29706..6079820 100644
--- a/ppapi/native_client/src/trusted/plugin/nacl_entry_points.h
+++ b/ppapi/native_client/src/trusted/plugin/nacl_entry_points.h
@@ -16,13 +16,15 @@
 #include "ppapi/c/pp_instance.h"
 #include "ppapi/c/private/ppb_nacl_private.h"
 
-typedef PP_NaClResult (*LaunchNaClProcessFunc)(PP_Instance instance,
-                                               const char* alleged_url,
-                                               PP_Bool uses_irt,
-                                               PP_Bool uses_ppapi,
-                                               PP_Bool enable_ppapi_dev,
-                                               PP_Bool enable_dyncode_syscalls,
-                                               NaClHandle* result_socket);
+typedef PP_NaClResult (*LaunchNaClProcessFunc)(
+    PP_Instance instance,
+    const char* alleged_url,
+    PP_Bool uses_irt,
+    PP_Bool uses_ppapi,
+    PP_Bool enable_ppapi_dev,
+    PP_Bool enable_dyncode_syscalls,
+    PP_Bool enable_exception_handling,
+    NaClHandle* result_socket);
 
 
 extern LaunchNaClProcessFunc launch_nacl_process;
diff --git a/ppapi/native_client/src/trusted/plugin/nacl_http_response_headers.cc b/ppapi/native_client/src/trusted/plugin/nacl_http_response_headers.cc
new file mode 100644
index 0000000..03a266e
--- /dev/null
+++ b/ppapi/native_client/src/trusted/plugin/nacl_http_response_headers.cc
@@ -0,0 +1,107 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "native_client/src/trusted/plugin/nacl_http_response_headers.h"
+
+#include <algorithm>
+#include <sstream>
+
+namespace {
+
+// TODO(jvoung) use Tokenize from base/string_util.h when this moves
+// to chromium.
+void SplitString(const std::string& str,
+                 char delim,
+                 std::vector<std::string>* elems) {
+  std::stringstream ss(str);
+  std::string item;
+  while (std::getline(ss, item, delim)) {
+    elems->push_back(item);
+  }
+}
+
+bool SplitOnce(const std::string& str,
+               char delim,
+               std::vector<std::string>* elems) {
+  size_t pos = str.find(delim);
+  if (pos != std::string::npos) {
+    elems->push_back(str.substr(0, pos));
+    elems->push_back(str.substr(pos + 1));
+    return true;
+  }
+  return false;
+}
+
+}  // namespace
+
+namespace plugin {
+
+NaClHttpResponseHeaders::NaClHttpResponseHeaders() {}
+
+NaClHttpResponseHeaders::~NaClHttpResponseHeaders() {}
+
+void NaClHttpResponseHeaders::Parse(const std::string& headers_str) {
+  // PPAPI response headers are \n delimited. Separate out the lines.
+  std::vector<std::string> lines;
+  SplitString(headers_str, '\n', &lines);
+
+  for (size_t i = 0; i < lines.size(); ++i) {
+    std::vector<std::string> tokens;
+    // Split along the key-value pair separator char.
+    if (!SplitOnce(lines[i], ':', &tokens)) {
+      // Ignore funny header lines that don't have the key-value separator.
+      continue;
+    }
+    std::string key = tokens[0];
+    // Also ignore keys that start with white-space (they are invalid).
+    // See: HttpResponseHeadersTest.NormalizeHeadersLeadingWhitespace.
+    if (key[0] == ' ' || key[0] == '\t')
+      continue;
+    // TODO(jvoung): replace some of this with TrimWhitespaceASCII when
+    // we move code to chromium.
+    // Strip trailing whitespace from the key to normalize.
+    size_t pos = key.find_last_not_of(" \t");
+    if (pos != std::string::npos)
+      key.erase(pos + 1);
+    // Strip leading whitespace from the value to normalize.
+    std::string value = tokens[1];
+    value.erase(0, value.find_first_not_of(" \t"));
+    header_entries_.push_back(Entry(key, value));
+  }
+}
+
+std::string NaClHttpResponseHeaders::GetCacheValidators() {
+  std::string result;
+  for (size_t i = 0; i < header_entries_.size(); ++i) {
+    const Entry& entry = header_entries_[i];
+    std::string key = entry.first;
+    // TODO(jvoung): replace with LowerCaseEqualsASCII later.
+    std::transform(key.begin(), key.end(), key.begin(), ::tolower);
+    if (key.compare("etag") == 0 || key.compare("last-modified") == 0) {
+      if (!result.empty())
+        result += "&";
+      // Return the original headers, not the tolower ones.
+      result += entry.first + ":" + entry.second;
+    }
+  }
+  return result;
+}
+
+bool NaClHttpResponseHeaders::CacheControlNoStore() {
+  for (size_t i = 0; i < header_entries_.size(); ++i) {
+    const Entry& entry = header_entries_[i];
+    std::string key = entry.first;
+    // TODO(jvoung): replace with LowerCaseEqualsASCII later.
+    std::transform(key.begin(), key.end(), key.begin(), ::tolower);
+    if (key.compare("cache-control") == 0) {
+      std::string cc = entry.second;
+      std::transform(cc.begin(), cc.end(), cc.begin(), ::tolower);
+      if (entry.second.find("no-store") != std::string::npos)
+        return true;
+    }
+  }
+  return false;
+}
+
+}  // namespace plugin
diff --git a/ppapi/native_client/src/trusted/plugin/nacl_http_response_headers.h b/ppapi/native_client/src/trusted/plugin/nacl_http_response_headers.h
new file mode 100644
index 0000000..181a381
--- /dev/null
+++ b/ppapi/native_client/src/trusted/plugin/nacl_http_response_headers.h
@@ -0,0 +1,57 @@
+// Copyright 2013 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.
+
+// Some code to parse HTTP response headers in the format given by
+// PPAPI's ppb_url_response.
+// Once we move the trusted NaCl plugin code into chrome,
+// we should use the standard net/http/http_response_headers.h code.
+
+// Keep this file very light on dependencies so that it is easy
+// build a unittest for this (see the gyp file). Do not depend on anything
+// other than the standard libraries.
+
+// NOTE when switching over to net/http/http_response_headers.h:
+// There are differences between the "raw" headers that can be parsed by
+// net/http/http_response_headers and the headers returned by ppb_url_response.
+// The ppb_url_response headers are \n delimited, while the
+// http_response_headers are \0 delimited and end in \0\0.
+
+#ifndef NATIVE_CLIENT_SRC_TRUSTED_PLUGIN_NACL_HTTP_RESPONSE_HEADERS_H_
+#define NATIVE_CLIENT_SRC_TRUSTED_PLUGIN_NACL_HTTP_RESPONSE_HEADERS_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "native_client/src/include/nacl_macros.h"
+
+namespace plugin {
+
+class NaClHttpResponseHeaders {
+ public:
+  NaClHttpResponseHeaders();
+  ~NaClHttpResponseHeaders();
+
+  typedef std::pair<std::string, std::string> Entry;
+
+  // Parse and prepare the headers for use with other methods.
+  // Assumes that the headers are \n delimited, which ppb_url_response gives.
+  // Invalid header lines are skipped.
+  void Parse(const std::string& headers_str);
+
+  // Return a concatenated string of HTTP caching validators.
+  // E.g., Last-Modified time and ETags.
+  std::string GetCacheValidators();
+
+  // Return true if the headers indicate that the data should not be stored.
+  bool CacheControlNoStore();
+
+ private:
+  std::vector<Entry> header_entries_;
+  NACL_DISALLOW_COPY_AND_ASSIGN(NaClHttpResponseHeaders);
+};
+
+}  // namespace plugin
+
+#endif  // NATIVE_CLIENT_SRC_TRUSTED_PLUGIN_NACL_HTTP_RESPONSE_HEADERS_H_
diff --git a/ppapi/native_client/src/trusted/plugin/nacl_http_response_headers_unittest.cc b/ppapi/native_client/src/trusted/plugin/nacl_http_response_headers_unittest.cc
new file mode 100644
index 0000000..80582b9
--- /dev/null
+++ b/ppapi/native_client/src/trusted/plugin/nacl_http_response_headers_unittest.cc
@@ -0,0 +1,138 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "native_client/src/trusted/plugin/nacl_http_response_headers.h"
+
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Test that we are able to discover the cache validator headers.
+TEST(NaClHttpResponseHeadersTest, TestGetValidators) {
+  // Test a single (weak) ETag.
+  std::string one_val_headers("Date: Wed, 15 Nov 1995 06:25:24 GMT\n"
+                              "Server: Apache/2.0.52 (CentOS)\n"
+                              "Content-Type: text/plain; charset=UTF-8\n"
+                              "Connection: close\n"
+                              "Accept-Ranges: bytes\n"
+                              "ETag: w\"abcdefg\"\n"
+                              "Content-Length: 2912652\n");
+  std::string one_val_expected("ETag:w\"abcdefg\"");
+  plugin::NaClHttpResponseHeaders parser_1;
+  parser_1.Parse(one_val_headers);
+  EXPECT_EQ(parser_1.GetCacheValidators(), one_val_expected);
+
+  // Test a Last-Modified Header.
+  std::string mod_val_headers("Date: Wed, 15 Nov 1995 06:25:24 GMT\n"
+                              "Server: Apache/2.0.52 (CentOS)\n"
+                              "Content-Type: text/plain; charset=UTF-8\n"
+                              "Connection: close\n"
+                              "Accept-Ranges: bytes\n"
+                              "Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT\n"
+                              "Content-Length: 2912652\n");
+  std::string mod_val_expected("Last-Modified:Wed, 15 Nov 1995 04:58:08 GMT");
+  plugin::NaClHttpResponseHeaders parser_1b;
+  parser_1b.Parse(mod_val_headers);
+  EXPECT_EQ(parser_1b.GetCacheValidators(), mod_val_expected);
+
+  // Test both (strong) ETag and Last-Modified, with some whitespace.
+  std::string two_val_headers("Date: Wed, 15 Nov 1995 06:25:24 GMT\n"
+                              "Last-modified: Wed, 15 Nov 1995 04:58:08 GMT\n"
+                              "Server: Apache/2.0.52 (CentOS)\n"
+                              "etag  \t :\t   \"/abcdefg:A-Z0-9+/==\"\n"
+                              "Content-Type: text/plain; charset=UTF-8\n"
+                              "cache-control: no-cache\n"
+                              "Connection: close\n"
+                              "Accept-Ranges: bytes\n"
+                              "Content-Length: 2912652\n");
+  // Note that the value can still have white-space.
+  std::string two_val_expected("Last-modified:Wed, 15 Nov 1995 04:58:08 GMT&"
+                               "etag:\"/abcdefg:A-Z0-9+/==\"");
+  plugin::NaClHttpResponseHeaders parser_2;
+  parser_2.Parse(two_val_headers);
+  EXPECT_EQ(parser_2.GetCacheValidators(), two_val_expected);
+
+  // Some etag generators like python HTTP server use ' instead of "
+  std::string single_q_headers("Date: Wed, 15 Nov 1995 06:25:24 GMT\n"
+                               "Server: BaseHTTP/0.3 Python/2.7.3\n"
+                               "ETag: '/usr/local/some_file.nmf'\n");
+  std::string single_q_expected("ETag:'/usr/local/some_file.nmf'");
+  plugin::NaClHttpResponseHeaders parser_3;
+  parser_3.Parse(single_q_headers);
+  EXPECT_EQ(parser_3.GetCacheValidators(), single_q_expected);
+
+  // Keys w/ leading whitespace are invalid.
+  // See: HttpResponseHeadersTest.NormalizeHeadersLeadingWhitespace.
+  std::string bad_headers("Date: Wed, 15 Nov 1995 06:25:24 GMT\n"
+                          "Server: BaseHTTP/0.3 Python/2.7.3\n"
+                          "   ETag: '/usr/local/some_file.nmf'\n");
+  std::string bad_expected("");
+  plugin::NaClHttpResponseHeaders parser_4;
+  parser_4.Parse(bad_headers);
+  EXPECT_EQ(parser_4.GetCacheValidators(), bad_expected);
+}
+
+// Test that we are able to determine when there is a no-store
+// Cache-Control header, among all the Cache-Control headers.
+TEST(NaClHttpResponseHeadersTest, TestFindNoStore) {
+  // Say that there isn't one, when there isn't one.
+  std::string headers_0("Date: Wed, 15 Nov 1995 06:25:24 GMT\n"
+                        "Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT\n"
+                        "ETag: '/tmp/blah.nmf'\n"
+                        "Cache-Control: max-age=3600\n");
+  plugin::NaClHttpResponseHeaders parser_0;
+  parser_0.Parse(headers_0);
+  EXPECT_FALSE(parser_0.CacheControlNoStore());
+
+  // Say that there is one, when there is one.
+  std::string headers_1("Date: Wed, 15 Nov 1995 06:25:24 GMT\n"
+                        "Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT\n"
+                        "ETag: \"/abcdefgA-Z0-9+/\"\n"
+                        "Cache-Control: no-store\n");
+  plugin::NaClHttpResponseHeaders parser_1;
+  parser_1.Parse(headers_1);
+  EXPECT_TRUE(parser_1.CacheControlNoStore());
+
+  // Say that there is one, when comma separated.
+  std::string headers_2("Date: Wed, 15 Nov 1995 06:25:24 GMT\n"
+                        "Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT\n"
+                        "ETag: \"/abcdefgA-Z0-9+/\"\n"
+                        "Cache-Control: no-store, no-cache\n");
+  plugin::NaClHttpResponseHeaders parser_2;
+  parser_2.Parse(headers_2);
+  EXPECT_TRUE(parser_2.CacheControlNoStore());
+
+  // Comma separated, in a different position.
+  std::string headers_3("Date: Wed, 15 Nov 1995 06:25:24 GMT\n"
+                        "Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT\n"
+                        "ETag: \"/abcdefgA-Z0-9+/\"\n"
+                        "Cache-control: no-cache, max-age=60, no-store\n");
+  plugin::NaClHttpResponseHeaders parser_3;
+  parser_3.Parse(headers_3);
+  EXPECT_TRUE(parser_3.CacheControlNoStore());
+
+  // Test multiple cache-control lines, plus extra space before colon.
+  std::string headers_4("Date: Wed, 15 Nov 1995 06:25:24 GMT\n"
+                        "Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT\n"
+                        "ETag: \"/abcdefgA-Z0-9+/\"\n"
+                        "cache-control: no-cache\n"
+                        "cache-control \t : max-age=60, no-store, max-stale\n");
+  plugin::NaClHttpResponseHeaders parser_4;
+  parser_4.Parse(headers_4);
+  EXPECT_TRUE(parser_4.CacheControlNoStore());
+
+  // Test with extra whitespace, in the values.
+  std::string headers_5("Date:   Wed, 15 Nov 1995 06:25:24 GMT  \n"
+                        "Last-Modified:   Wed, 15 Nov 1995 04:58:08 GMT  \n"
+                        "ETag:   \"/abcdefgA-Z0-9+/\"  \n"
+                        ": empty  key \n"
+                        ": empty  key2 \n"
+                        "Blank-Header :   \n"
+                        "Connection: close\n"
+                        "cache-control:max-age=60,  no-store  \n"
+                        "cache-control: no-cache\n");
+  plugin::NaClHttpResponseHeaders parser_5;
+  parser_5.Parse(headers_5);
+  EXPECT_TRUE(parser_5.CacheControlNoStore());
+}
diff --git a/ppapi/native_client/src/trusted/plugin/plugin.cc b/ppapi/native_client/src/trusted/plugin/plugin.cc
index cd5dfbf..6b53107 100644
--- a/ppapi/native_client/src/trusted/plugin/plugin.cc
+++ b/ppapi/native_client/src/trusted/plugin/plugin.cc
@@ -76,6 +76,8 @@
 // MIME type because the "src" attribute is used to supply us with the resource
 // of that MIME type that we're supposed to display.
 const char* const kNaClManifestAttribute = "nacl";
+// The pseudo-ISA used to indicate portable native client.
+const char* const kPortableISA = "portable";
 // This is a pretty arbitrary limit on the byte size of the NaCl manfest file.
 // Note that the resulting string object has to have at least one byte extra
 // for the null termination character.
@@ -378,6 +380,7 @@
                                   bool uses_irt,
                                   bool uses_ppapi,
                                   bool enable_dyncode_syscalls,
+                                  bool enable_exception_handling,
                                   ErrorInfo* error_info,
                                   pp::CompletionCallback init_done_cb,
                                   pp::CompletionCallback crash_cb) {
@@ -401,6 +404,7 @@
                                  uses_ppapi,
                                  enable_dev_interfaces_,
                                  enable_dyncode_syscalls,
+                                 enable_exception_handling,
                                  crash_cb);
   PLUGIN_PRINTF(("Plugin::LoadNaClModuleCommon (service_runtime_started=%d)\n",
                  service_runtime_started));
@@ -413,6 +417,7 @@
 bool Plugin::LoadNaClModule(nacl::DescWrapper* wrapper,
                             ErrorInfo* error_info,
                             bool enable_dyncode_syscalls,
+                            bool enable_exception_handling,
                             pp::CompletionCallback init_done_cb,
                             pp::CompletionCallback crash_cb) {
   // Before forking a new sel_ldr process, ensure that we do not leak
@@ -425,6 +430,7 @@
                             true /* uses_irt */,
                             true /* uses_ppapi */,
                             enable_dyncode_syscalls,
+                            enable_exception_handling,
                             error_info, init_done_cb, crash_cb)) {
     return false;
   }
@@ -492,6 +498,7 @@
                             false /* uses_irt */,
                             false /* uses_ppapi */,
                             false /* enable_dyncode_syscalls */,
+                            false /* enable_exception_handling */,
                             error_info,
                             pp::BlockUntilComplete(),
                             pp::BlockUntilComplete())) {
@@ -575,6 +582,7 @@
 };
 
 const char* const Plugin::kNaClMIMEType = "application/x-nacl";
+const char* const Plugin::kPnaclMIMEType = "application/x-pnacl";
 
 bool Plugin::NexeIsContentHandler() const {
   // Tests if the MIME type is not a NaCl MIME type.
@@ -582,7 +590,8 @@
   // type handler rather than directly by an HTML document.
   return
       !mime_type().empty() &&
-      mime_type() != kNaClMIMEType;
+      mime_type() != kNaClMIMEType &&
+      mime_type() != kPnaclMIMEType;
 }
 
 
@@ -861,6 +870,7 @@
   bool was_successful = LoadNaClModule(
       wrapper.get(), &error_info,
       true, /* enable_dyncode_syscalls */
+      true, /* enable_exception_handling */
       callback_factory_.NewCallback(&Plugin::NexeFileDidOpenContinuation),
       callback_factory_.NewCallback(&Plugin::NexeDidCrash));
 
@@ -982,6 +992,7 @@
   bool was_successful = LoadNaClModule(
       wrapper.get(), &error_info,
       false, /* enable_dyncode_syscalls */
+      false, /* enable_exception_handling */
       callback_factory_.NewCallback(&Plugin::BitcodeDidTranslateContinuation),
       callback_factory_.NewCallback(&Plugin::NexeDidCrash));
 
@@ -1252,13 +1263,11 @@
     return false;
   // Determine whether lookups should use portable (i.e., pnacl versions)
   // rather than platform-specific files.
-  bool should_prefer_portable =
-      (getenv("NACL_PREFER_PORTABLE_IN_MANIFEST") != NULL);
+  bool is_pnacl = (mime_type() == kPnaclMIMEType);
   nacl::scoped_ptr<JsonManifest> json_manifest(
       new JsonManifest(url_util_,
                        manifest_base_url(),
-                       GetSandboxISA(),
-                       should_prefer_portable));
+                       (is_pnacl ? kPortableISA : GetSandboxISA())));
   if (!json_manifest->Init(manifest_json, error_info)) {
     return false;
   }
diff --git a/ppapi/native_client/src/trusted/plugin/plugin.gypi b/ppapi/native_client/src/trusted/plugin/plugin.gypi
index 756ebce..1357a37 100644
--- a/ppapi/native_client/src/trusted/plugin/plugin.gypi
+++ b/ppapi/native_client/src/trusted/plugin/plugin.gypi
@@ -11,6 +11,7 @@
       'json_manifest.cc',
       'local_temp_file.cc',
       'module_ppapi.cc',
+      'nacl_http_response_headers.cc',
       'nacl_subprocess.cc',
       'plugin.cc',
       'pnacl_coordinator.cc',
diff --git a/ppapi/native_client/src/trusted/plugin/plugin.h b/ppapi/native_client/src/trusted/plugin/plugin.h
index eecddc6..2134603 100644
--- a/ppapi/native_client/src/trusted/plugin/plugin.h
+++ b/ppapi/native_client/src/trusted/plugin/plugin.h
@@ -104,6 +104,7 @@
   // Updates nacl_module_origin() and nacl_module_url().
   bool LoadNaClModule(nacl::DescWrapper* wrapper, ErrorInfo* error_info,
                       bool enable_dyncode_syscalls,
+                      bool enable_exception_handling,
                       pp::CompletionCallback init_done_cb,
                       pp::CompletionCallback crash_cb);
 
@@ -281,6 +282,8 @@
   const nacl::string& mime_type() const { return mime_type_; }
   // The default MIME type for the NaCl plugin.
   static const char* const kNaClMIMEType;
+  // The MIME type for the plugin when using PNaCl.
+  static const char* const kPnaclMIMEType;
   // Returns true if PPAPI Dev interfaces should be allowed.
   bool enable_dev_interfaces() { return enable_dev_interfaces_; }
 
@@ -330,6 +333,7 @@
                             bool uses_irt,
                             bool uses_ppapi,
                             bool enable_dyncode_syscalls,
+                            bool enable_exception_handling,
                             ErrorInfo* error_info,
                             pp::CompletionCallback init_done_cb,
                             pp::CompletionCallback crash_cb);
diff --git a/ppapi/native_client/src/trusted/plugin/pnacl_coordinator.cc b/ppapi/native_client/src/trusted/plugin/pnacl_coordinator.cc
index 613abd4..02de2e0 100644
--- a/ppapi/native_client/src/trusted/plugin/pnacl_coordinator.cc
+++ b/ppapi/native_client/src/trusted/plugin/pnacl_coordinator.cc
@@ -12,6 +12,7 @@
 #include "native_client/src/shared/platform/nacl_check.h"
 #include "native_client/src/trusted/plugin/local_temp_file.h"
 #include "native_client/src/trusted/plugin/manifest.h"
+#include "native_client/src/trusted/plugin/nacl_http_response_headers.h"
 #include "native_client/src/trusted/plugin/plugin.h"
 #include "native_client/src/trusted/plugin/plugin_error.h"
 #include "native_client/src/trusted/plugin/pnacl_translate_thread.h"
@@ -691,7 +692,7 @@
     }
   } else {
     // We don't have a cache, so do the non-cached codepath.
-    CachedFileDidOpen(PP_ERROR_FAILED);
+    OpenBitcodeStream();
   }
 }
 
@@ -753,35 +754,18 @@
         "PNaCl translation cache directory creation/check failed.");
     return;
   }
-  if (pnacl_options_.HasCacheKey()) {
-    cached_nexe_file_.reset(new LocalTempFile(
-        plugin_, file_system_.get(),
-        nacl::string(kPnaclTempDir),
-        pnacl_options_.GetCacheKey()));
-    pp::CompletionCallback cb =
-        callback_factory_.NewCallback(&PnaclCoordinator::CachedFileDidOpen);
-    cached_nexe_file_->OpenRead(cb);
-  } else {
-    // For now, tolerate lack of cache identity...
-    CachedFileDidOpen(PP_ERROR_FAILED);
-  }
+  OpenBitcodeStream();
 }
 
-void PnaclCoordinator::CachedFileDidOpen(int32_t pp_error) {
-  PLUGIN_PRINTF(("PnaclCoordinator::CachedFileDidOpen (pp_error=%"
-                 NACL_PRId32")\n", pp_error));
-  if (pp_error == PP_OK) {
-    HistogramEnumerateTranslationCache(true);
-    NexeReadDidOpen(PP_OK);
-    return;
-  }
-  // Otherwise, the cache file is missing, or the cache simply
-  // cannot be created (e.g., incognito mode), so we must translate.
-  HistogramEnumerateTranslationCache(false);
+void PnaclCoordinator::OpenBitcodeStream() {
+  // Now open the pexe stream.
+  streaming_downloader_.reset(new FileDownloader());
+  streaming_downloader_->Initialize(plugin_);
 
-  // Create the translation thread object immediately. This ensures that any
-  // pieces of the file that get downloaded before the compilation thread
-  // is accepting SRPCs won't get dropped.
+  // Even though we haven't started downloading, create the translation
+  // thread object immediately. This ensures that any pieces of the file
+  // that get downloaded before the compilation thread is accepting
+  // SRPCs won't get dropped.
   translate_thread_.reset(new PnaclTranslateThread());
   if (translate_thread_ == NULL) {
     ReportNonPpapiError(ERROR_PNACL_THREAD_CREATE,
@@ -795,18 +779,72 @@
     callback_factory_.NewCallback(&PnaclCoordinator::ObjectFileDidOpen);
   obj_file_->Open(obj_cb);
 
-  streaming_downloader_.reset(new FileDownloader());
-  streaming_downloader_->Initialize(plugin_);
   pp::CompletionCallback cb =
-      callback_factory_.NewCallback(
-          &PnaclCoordinator::BitcodeStreamDidFinish);
-
+      callback_factory_.NewCallback(&PnaclCoordinator::BitcodeStreamDidOpen);
   if (!streaming_downloader_->OpenStream(pexe_url_, cb, this)) {
     ReportNonPpapiError(ERROR_PNACL_PEXE_FETCH_OTHER,
                         nacl::string("failed to open stream ") + pexe_url_);
   }
 }
 
+void PnaclCoordinator::BitcodeStreamDidOpen(int32_t pp_error) {
+  if (pp_error != PP_OK) {
+    BitcodeStreamDidFinish(pp_error);
+    return;
+  }
+
+  if (!off_the_record_) {
+    // Get the cache key and try to open an existing entry.
+    nacl::string headers = streaming_downloader_->GetResponseHeaders();
+    NaClHttpResponseHeaders parser;
+    parser.Parse(headers);
+    nacl::string cache_validators = parser.GetCacheValidators();
+    if (parser.CacheControlNoStore() || cache_validators.empty()) {
+      // We can't cache in this case.
+      pnacl_options_.set_cache_validators("");
+      CachedFileDidOpen(PP_ERROR_FAILED);
+      return;
+    } else {
+      nacl::string url = streaming_downloader_->url();
+      // For now, combine the cache_validators + the URL as the key.
+      // When we change the cache backend to be not-origin-specific
+      // we should send the URL separately, and check in the browser's
+      // RenderViewHost / SiteInstance's IsSameWebsite() to prevent
+      // people from forging the URL for a different origin.
+      pnacl_options_.set_cache_validators(cache_validators + url);
+    }
+    cached_nexe_file_.reset(new LocalTempFile(
+        plugin_, file_system_.get(),
+        nacl::string(kPnaclTempDir),
+        pnacl_options_.GetCacheKey()));
+    pp::CompletionCallback cb =
+        callback_factory_.NewCallback(&PnaclCoordinator::CachedFileDidOpen);
+    cached_nexe_file_->OpenRead(cb);
+  } else {
+    // No cache case.
+    CachedFileDidOpen(PP_ERROR_FAILED);
+  }
+}
+
+void PnaclCoordinator::CachedFileDidOpen(int32_t pp_error) {
+  PLUGIN_PRINTF(("PnaclCoordinator::CachedFileDidOpen (pp_error=%"
+                 NACL_PRId32")\n", pp_error));
+  if (pp_error == PP_OK) {
+    // Cache hit -- no need to stream the rest of the file.
+    streaming_downloader_.reset(NULL);
+    HistogramEnumerateTranslationCache(true);
+    NexeReadDidOpen(PP_OK);
+    return;
+  }
+  // Otherwise, the cache file is missing so we must translate.
+  HistogramEnumerateTranslationCache(false);
+
+  // Continue streaming.
+  pp::CompletionCallback cb =
+      callback_factory_.NewCallback(&PnaclCoordinator::BitcodeStreamDidFinish);
+  streaming_downloader_->FinishStreaming(cb);
+}
+
 void PnaclCoordinator::BitcodeStreamDidFinish(int32_t pp_error) {
   PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamDidFinish (pp_error=%"
                  NACL_PRId32")\n", pp_error));
@@ -840,6 +878,7 @@
   PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamGotData (pp_error=%"
                  NACL_PRId32", data=%p)\n", pp_error, data ? &(*data)[0] : 0));
   DCHECK(translate_thread_.get());
+
   translate_thread_->PutBytes(data, pp_error);
   // If pp_error > 0, then it represents the number of bytes received.
   if (data && pp_error > 0) {
diff --git a/ppapi/native_client/src/trusted/plugin/pnacl_coordinator.h b/ppapi/native_client/src/trusted/plugin/pnacl_coordinator.h
index d6ee015..4e89b81 100644
--- a/ppapi/native_client/src/trusted/plugin/pnacl_coordinator.h
+++ b/ppapi/native_client/src/trusted/plugin/pnacl_coordinator.h
@@ -170,6 +170,11 @@
   void FileSystemDidOpen(int32_t pp_error);
   // Invoked after we are sure the PNaCl temporary directory exists.
   void DirectoryWasCreated(int32_t pp_error);
+  // Invoke to issue a GET request for bitcode.
+  void OpenBitcodeStream();
+  // Invoked when we've started an URL fetch for the pexe to check for
+  // caching metadata.
+  void BitcodeStreamDidOpen(int32_t pp_error);
   // Invoked after we have checked the PNaCl cache for a translated version.
   void CachedFileDidOpen(int32_t pp_error);
   // Invoked when a pexe data chunk arrives (when using streaming translation)
diff --git a/ppapi/native_client/src/trusted/plugin/pnacl_options.cc b/ppapi/native_client/src/trusted/plugin/pnacl_options.cc
index 05eecf2..0648b59 100644
--- a/ppapi/native_client/src/trusted/plugin/pnacl_options.cc
+++ b/ppapi/native_client/src/trusted/plugin/pnacl_options.cc
@@ -9,6 +9,21 @@
 
 #include "native_client/src/include/nacl_string.h"
 
+namespace {
+
+nacl::string ReplaceBadFSChars(nacl::string str,
+                               const nacl::string& bad_chars,
+                               const nacl::string& replacement) {
+  size_t replace_pos = str.find_first_of(bad_chars);
+  while (replace_pos != nacl::string::npos) {
+    str = str.replace(replace_pos, 1, replacement);
+    replace_pos = str.find_first_of(bad_chars);
+  }
+  return str;
+}
+
+}  // namespace
+
 namespace plugin {
 
 // Default to -O0 for now.
@@ -17,15 +32,23 @@
 PnaclOptions::~PnaclOptions() {
 }
 
-nacl::string PnaclOptions::GetCacheKey() {
+nacl::string PnaclOptions::GetCacheKey() const {
   // TODO(jvoung): We need to read the PNaCl translator's manifest
   // to grab the NaCl / PNaCl ABI version too.
   nacl::stringstream ss;
   // Cast opt_level_ as int so that it doesn't think it's a char.
   ss << "-O:" << static_cast<int>(opt_level_)
-     << ";flags:" << experimental_flags_
-     <<  ";bitcode_hash:" << bitcode_hash_;
-  return ss.str();
+     <<  ";cache_validators:" << cache_validators_;
+  // HTML5 FileSystem-based cache does not allow some characters which
+  // may appear in URLs, ETags, or Last-Modified times.  Once we move to
+  // our own cache-backend, it will be more tolerant of various cache
+  // key values.
+  // See: http://dev.w3.org/2009/dap/file-system/file-dir-sys.html#naming-restrictions
+  nacl::string key = ss.str();
+  key = ReplaceBadFSChars(key, "/", "_FWDSLASH_");
+  key = ReplaceBadFSChars(key, "\\", "_BCKSLASH_");
+  key = ReplaceBadFSChars(key, "\0", "_NULL_");
+  return key;
 }
 
 void PnaclOptions::set_opt_level(int8_t l) {
@@ -40,41 +63,19 @@
   opt_level_ = l;
 }
 
-std::vector<char> PnaclOptions::GetOptCommandline() {
+std::vector<char> PnaclOptions::GetOptCommandline() const {
   std::vector<char> result;
-  std::vector<nacl::string> tokens;
-
-  // Split the experimental_flags_ + the -On along whitespace.
-  // Mostly a copy of "base/string_util.h", but avoid importing
-  // base into the PPAPI plugin for now.
-  nacl::string delim(" ");
-  nacl::string str = experimental_flags_;
+  nacl::string str;
 
   if (opt_level_ != -1) {
     nacl::stringstream ss;
     // Cast as int so that it doesn't think it's a char.
-    ss << " -O" << static_cast<int>(opt_level_);
-    str += ss.str();
+    ss << "-O" << static_cast<int>(opt_level_);
+    str = ss.str();
   }
 
-  size_t start = str.find_first_not_of(delim);
-  while (start != nacl::string::npos) {
-    size_t end = str.find_first_of(delim, start + 1);
-    if (end == nacl::string::npos) {
-      tokens.push_back(str.substr(start));
-      break;
-    } else {
-      tokens.push_back(str.substr(start, end - start));
-      start = str.find_first_not_of(delim, end + 1);
-    }
-  }
-
-  for (size_t i = 0; i < tokens.size(); ++i) {
-    nacl::string t = tokens[i];
-    result.reserve(result.size() + t.size());
-    std::copy(t.begin(), t.end(), std::back_inserter(result));
-    result.push_back('\x00');
-  }
+  std::copy(str.begin(), str.end(), std::back_inserter(result));
+  result.push_back('\x00');
 
   return result;
 }
diff --git a/ppapi/native_client/src/trusted/plugin/pnacl_options.h b/ppapi/native_client/src/trusted/plugin/pnacl_options.h
index 55bbbb9..3845c87 100644
--- a/ppapi/native_client/src/trusted/plugin/pnacl_options.h
+++ b/ppapi/native_client/src/trusted/plugin/pnacl_options.h
@@ -19,37 +19,33 @@
   PnaclOptions();
   ~PnaclOptions();
 
-  // Return true if we know the hash of the bitcode, for caching.
-  bool HasCacheKey() { return bitcode_hash_ != ""; }
+  // Return |true| if PNaCl is allowed to cache.
+  // PNaCl is allowed to cache if the server sends cache validators
+  // like Last-Modified time or ETags in the HTTP response, and
+  // it does not send "Cache-Control: no-store".
+  bool HasCacheKey() const { return (!cache_validators_.empty()); }
 
   // Return the cache key (which takes into account the bitcode hash,
   // as well as the commandline options).
-  nacl::string GetCacheKey();
+  nacl::string GetCacheKey() const;
 
   // Return true if the manifest did not specify any special options
   // (just using the default).
-  bool HasDefaultOpts() {
-    return opt_level_ == -1 && experimental_flags_ == "";
+  bool HasDefaultOpts() const {
+    return opt_level_ == -1;
   }
 
   // Return a character array of \x00 delimited commandline options.
-  std::vector<char> GetOptCommandline();
+  std::vector<char> GetOptCommandline() const;
 
-  bool translate() { return translate_; }
+  bool translate() const { return translate_; }
   void set_translate(bool t) { translate_ = t; }
 
-  uint8_t opt_level() { return opt_level_; }
+  uint8_t opt_level() const { return opt_level_; }
   void set_opt_level(int8_t l);
 
-  nacl::string experimental_flags() {
-    return experimental_flags_;
-  }
-  void set_experimental_flags(const nacl::string& f) {
-    experimental_flags_ = f;
-  }
-
-  void set_bitcode_hash(const nacl::string& c) {
-    bitcode_hash_ = c;
+  void set_cache_validators(const nacl::string& c) {
+    cache_validators_ = c;
   }
 
  private:
@@ -58,8 +54,7 @@
   // double-check that it is the case when more fields are added.
   bool translate_;
   int8_t opt_level_;
-  nacl::string experimental_flags_;
-  nacl::string bitcode_hash_;
+  nacl::string cache_validators_;
 };
 
 }  // namespace plugin;
diff --git a/ppapi/native_client/src/trusted/plugin/pnacl_resources.cc b/ppapi/native_client/src/trusted/plugin/pnacl_resources.cc
index 119d53a..4b5e4f6 100644
--- a/ppapi/native_client/src/trusted/plugin/pnacl_resources.cc
+++ b/ppapi/native_client/src/trusted/plugin/pnacl_resources.cc
@@ -121,9 +121,11 @@
 
   int32_t fd = GetPnaclFD(plugin_, resource_info_filename.c_str());
   if (fd < 0) {
+    // File-open failed. Assume this means that the file is
+    // not actually installed.
     ReadResourceInfoError(
-        nacl::string("PnaclResources::ReadResourceInfo failed for: ") +
-        resource_info_filename);
+        nacl::string("The Portable Native Client component is not installed"
+                     " or has been disabled."));
     return;
   }
 
@@ -218,10 +220,13 @@
 
     int32_t fd = PnaclResources::GetPnaclFD(plugin_, filename.c_str());
     if (fd < 0) {
+      // File-open failed. Assume this means that the file is
+      // not actually installed. This shouldn't actually occur since
+      // ReadResourceInfo() should happen first, and error out.
       coordinator_->ReportNonPpapiError(
           ERROR_PNACL_RESOURCE_FETCH,
-          nacl::string("PnaclResources::StartLoad failed for: ") +
-          filename + " (PNaCl not installed?  Check chrome://nacl)");
+          nacl::string("The Portable Native Client component is not installed "
+                       "or has been disabled. Cannot open file: ") + filename);
       result = PP_ERROR_FILENOTFOUND;
       break;
     } else {
diff --git a/ppapi/native_client/src/trusted/plugin/sel_ldr_launcher_chrome.cc b/ppapi/native_client/src/trusted/plugin/sel_ldr_launcher_chrome.cc
index cb828dd..47187d9 100644
--- a/ppapi/native_client/src/trusted/plugin/sel_ldr_launcher_chrome.cc
+++ b/ppapi/native_client/src/trusted/plugin/sel_ldr_launcher_chrome.cc
@@ -21,7 +21,8 @@
                                  bool uses_irt,
                                  bool uses_ppapi,
                                  bool enable_ppapi_dev,
-                                 bool enable_dyncode_syscalls) {
+                                 bool enable_dyncode_syscalls,
+                                 bool enable_exception_handling) {
   if (!launch_nacl_process)
     return false;
   // send a synchronous message to the browser process
@@ -31,6 +32,7 @@
                           PP_FromBool(uses_ppapi),
                           PP_FromBool(enable_ppapi_dev),
                           PP_FromBool(enable_dyncode_syscalls),
+                          PP_FromBool(enable_exception_handling),
                           &channel_) != PP_NACL_OK) {
     return false;
   }
diff --git a/ppapi/native_client/src/trusted/plugin/sel_ldr_launcher_chrome.h b/ppapi/native_client/src/trusted/plugin/sel_ldr_launcher_chrome.h
index 8ccf5c1..6861622 100644
--- a/ppapi/native_client/src/trusted/plugin/sel_ldr_launcher_chrome.h
+++ b/ppapi/native_client/src/trusted/plugin/sel_ldr_launcher_chrome.h
@@ -18,7 +18,8 @@
                      bool uses_irt,
                      bool uses_ppapi,
                      bool enable_ppapi_dev,
-                     bool enable_dyncode_syscalls);
+                     bool enable_dyncode_syscalls,
+                     bool enable_exception_handling);
 };
 
 }  // namespace plugin
diff --git a/ppapi/native_client/src/trusted/plugin/service_runtime.cc b/ppapi/native_client/src/trusted/plugin/service_runtime.cc
index 403a9c4..0876dec 100644
--- a/ppapi/native_client/src/trusted/plugin/service_runtime.cc
+++ b/ppapi/native_client/src/trusted/plugin/service_runtime.cc
@@ -721,6 +721,7 @@
                            bool uses_ppapi,
                            bool enable_ppapi_dev,
                            bool enable_dyncode_syscalls,
+                           bool enable_exception_handling,
                            pp::CompletionCallback crash_cb) {
   NaClLog(4, "ServiceRuntime::Start (nacl_desc=%p)\n",
           reinterpret_cast<void*>(nacl_desc));
@@ -738,7 +739,8 @@
                                        uses_irt,
                                        uses_ppapi,
                                        enable_ppapi_dev,
-                                       enable_dyncode_syscalls);
+                                       enable_dyncode_syscalls,
+                                       enable_exception_handling);
   if (!started) {
     NaClLog(LOG_ERROR, "ServiceRuntime::Start (start failed)\n");
     error_info->SetReport(ERROR_SEL_LDR_LAUNCH,
diff --git a/ppapi/native_client/src/trusted/plugin/service_runtime.h b/ppapi/native_client/src/trusted/plugin/service_runtime.h
index 6c6bced..ccec3e0 100644
--- a/ppapi/native_client/src/trusted/plugin/service_runtime.h
+++ b/ppapi/native_client/src/trusted/plugin/service_runtime.h
@@ -243,6 +243,7 @@
              bool uses_ppapi,
              bool enable_ppapi_dev,
              bool enable_dyncode_syscalls,
+             bool enable_exception_handling,
              pp::CompletionCallback crash_cb);
 
   // Starts the application channel to the nexe.