Merge pull request #665 from atom/asar
Support loading apps in asar format
This commit is contained in:
commit
9d84f139eb
29 changed files with 1793 additions and 31 deletions
|
@ -91,6 +91,10 @@ void App::OnWindowAllClosed() {
|
|||
Emit("window-all-closed");
|
||||
}
|
||||
|
||||
void App::OnQuit() {
|
||||
Emit("quit");
|
||||
}
|
||||
|
||||
void App::OnOpenFile(bool* prevent_default, const std::string& file_path) {
|
||||
base::ListValue args;
|
||||
args.AppendString(file_path);
|
||||
|
|
|
@ -36,6 +36,7 @@ class App : public mate::EventEmitter,
|
|||
// BrowserObserver implementations:
|
||||
virtual void OnWillQuit(bool* prevent_default) OVERRIDE;
|
||||
virtual void OnWindowAllClosed() OVERRIDE;
|
||||
virtual void OnQuit() OVERRIDE;
|
||||
virtual void OnOpenFile(bool* prevent_default,
|
||||
const std::string& file_path) OVERRIDE;
|
||||
virtual void OnOpenURL(const std::string& url) OVERRIDE;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "atom/browser/atom_browser_main_parts.h"
|
||||
#include "atom/browser/net/atom_url_request_job_factory.h"
|
||||
#include "atom/browser/net/asar/asar_protocol_handler.h"
|
||||
#include "base/threading/sequenced_worker_pool.h"
|
||||
#include "base/threading/worker_pool.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
|
@ -20,6 +21,12 @@ using content::BrowserThread;
|
|||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
const char* kAsarScheme = "asar";
|
||||
|
||||
} // namespace
|
||||
|
||||
AtomBrowserContext::AtomBrowserContext()
|
||||
: fake_browser_process_(new BrowserProcess),
|
||||
job_factory_(new AtomURLRequestJobFactory) {
|
||||
|
@ -44,6 +51,10 @@ net::URLRequestJobFactory* AtomBrowserContext::CreateURLRequestJobFactory(
|
|||
url::kFileScheme, new net::FileProtocolHandler(
|
||||
BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior(
|
||||
base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)));
|
||||
job_factory->SetProtocolHandler(
|
||||
kAsarScheme, new asar::AsarProtocolHandler(
|
||||
BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior(
|
||||
base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)));
|
||||
|
||||
// Set up interceptors in the reverse order.
|
||||
scoped_ptr<net::URLRequestJobFactory> top_job_factory =
|
||||
|
|
|
@ -37,6 +37,8 @@ void Browser::Quit() {
|
|||
}
|
||||
|
||||
void Browser::Shutdown() {
|
||||
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnQuit());
|
||||
|
||||
is_quiting_ = true;
|
||||
base::MessageLoop::current()->Quit();
|
||||
}
|
||||
|
|
|
@ -18,6 +18,9 @@ class BrowserObserver {
|
|||
// method will not be called, instead it will call OnWillQuit.
|
||||
virtual void OnWindowAllClosed() {}
|
||||
|
||||
// The browser is quitting.
|
||||
virtual void OnQuit() {}
|
||||
|
||||
// The browser has opened a file by double clicking in Finder or dragging the
|
||||
// file to the Dock icon. (OS X only)
|
||||
virtual void OnOpenFile(bool* prevent_default,
|
||||
|
|
|
@ -52,20 +52,25 @@ setImmediate ->
|
|||
detail: message
|
||||
buttons: ['OK']
|
||||
|
||||
# Emit 'exit' event on quit.
|
||||
require('app').on 'quit', ->
|
||||
process.emit 'exit'
|
||||
|
||||
# Load the RPC server.
|
||||
require './rpc-server.js'
|
||||
|
||||
# Now we try to load app's package.json.
|
||||
packageJson = null
|
||||
|
||||
packagePath = path.join process.resourcesPath, 'app'
|
||||
try
|
||||
# First we try to load process.resourcesPath/app
|
||||
packageJson = JSON.parse(fs.readFileSync(path.join(packagePath, 'package.json')))
|
||||
catch error
|
||||
# If not found then we load browser/default_app
|
||||
packagePath = path.join process.resourcesPath, 'default_app'
|
||||
packageJson = JSON.parse(fs.readFileSync(path.join(packagePath, 'package.json')))
|
||||
searchPaths = [ 'app', 'app.asar', 'default_app' ]
|
||||
for packagePath in searchPaths
|
||||
try
|
||||
packagePath = path.join process.resourcesPath, packagePath
|
||||
packageJson = JSON.parse(fs.readFileSync(path.join(packagePath, 'package.json')))
|
||||
catch e
|
||||
continue
|
||||
|
||||
throw new Error("Unable to find a valid app") unless packageJson?
|
||||
|
||||
# Set application's version.
|
||||
app = require 'app'
|
||||
|
@ -80,7 +85,7 @@ setImmediate ->
|
|||
# Set application's desktop name.
|
||||
if packageJson.desktopName?
|
||||
app.setDesktopName packageJson.desktopName
|
||||
else
|
||||
else
|
||||
app.setDesktopName '#{app.getName()}.desktop'
|
||||
|
||||
# Load the chrome extension support.
|
||||
|
|
91
atom/browser/net/asar/asar_protocol_handler.cc
Normal file
91
atom/browser/net/asar/asar_protocol_handler.cc
Normal file
|
@ -0,0 +1,91 @@
|
|||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/asar/asar_protocol_handler.h"
|
||||
|
||||
#include "atom/browser/net/asar/url_request_asar_job.h"
|
||||
#include "atom/common/asar/archive.h"
|
||||
#include "net/base/filename_util.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/url_request/url_request_error_job.h"
|
||||
#include "net/url_request/url_request_file_job.h"
|
||||
|
||||
namespace asar {
|
||||
|
||||
namespace {
|
||||
|
||||
const base::FilePath::CharType kAsarExtension[] = FILE_PATH_LITERAL(".asar");
|
||||
|
||||
// Get the relative path in asar archive.
|
||||
bool GetAsarPath(const base::FilePath& full_path,
|
||||
base::FilePath* asar_path,
|
||||
base::FilePath* relative_path) {
|
||||
base::FilePath iter = full_path;
|
||||
while (true) {
|
||||
base::FilePath dirname = iter.DirName();
|
||||
if (iter.MatchesExtension(kAsarExtension))
|
||||
break;
|
||||
else if (iter == dirname)
|
||||
return false;
|
||||
iter = dirname;
|
||||
}
|
||||
|
||||
base::FilePath tail;
|
||||
if (!iter.AppendRelativePath(full_path, &tail))
|
||||
return false;
|
||||
|
||||
*asar_path = iter;
|
||||
*relative_path = tail;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AsarProtocolHandler::AsarProtocolHandler(
|
||||
const scoped_refptr<base::TaskRunner>& file_task_runner)
|
||||
: file_task_runner_(file_task_runner) {}
|
||||
|
||||
AsarProtocolHandler::~AsarProtocolHandler() {
|
||||
}
|
||||
|
||||
Archive* AsarProtocolHandler::GetOrCreateAsarArchive(
|
||||
const base::FilePath& path) const {
|
||||
if (!archives_.contains(path)) {
|
||||
scoped_ptr<Archive> archive(new Archive(path));
|
||||
if (!archive->Init())
|
||||
return nullptr;
|
||||
|
||||
archives_.set(path, archive.Pass());
|
||||
}
|
||||
|
||||
return archives_.get(path);
|
||||
}
|
||||
|
||||
net::URLRequestJob* AsarProtocolHandler::MaybeCreateJob(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const {
|
||||
base::FilePath full_path;
|
||||
net::FileURLToFilePath(request->url(), &full_path);
|
||||
|
||||
// Create asar:// job when the path contains "xxx.asar/", otherwise treat the
|
||||
// URL request as file://.
|
||||
base::FilePath asar_path, relative_path;
|
||||
if (!GetAsarPath(full_path, &asar_path, &relative_path))
|
||||
return new net::URLRequestFileJob(request, network_delegate, full_path,
|
||||
file_task_runner_);
|
||||
|
||||
Archive* archive = GetOrCreateAsarArchive(asar_path);
|
||||
if (!archive)
|
||||
return new net::URLRequestErrorJob(request, network_delegate,
|
||||
net::ERR_FILE_NOT_FOUND);
|
||||
|
||||
return new URLRequestAsarJob(request, network_delegate, archive,
|
||||
relative_path, file_task_runner_);
|
||||
}
|
||||
|
||||
bool AsarProtocolHandler::IsSafeRedirectTarget(const GURL& location) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace asar
|
45
atom/browser/net/asar/asar_protocol_handler.h
Normal file
45
atom/browser/net/asar/asar_protocol_handler.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_ASAR_ASAR_PROTOCOL_HANDLER_H_
|
||||
#define ATOM_BROWSER_NET_ASAR_ASAR_PROTOCOL_HANDLER_H_
|
||||
|
||||
#include "base/containers/scoped_ptr_hash_map.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "net/url_request/url_request_job_factory.h"
|
||||
|
||||
namespace base {
|
||||
class TaskRunner;
|
||||
}
|
||||
|
||||
namespace asar {
|
||||
|
||||
class Archive;
|
||||
|
||||
class AsarProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler {
|
||||
public:
|
||||
explicit AsarProtocolHandler(
|
||||
const scoped_refptr<base::TaskRunner>& file_task_runner);
|
||||
virtual ~AsarProtocolHandler();
|
||||
|
||||
Archive* GetOrCreateAsarArchive(const base::FilePath& path) const;
|
||||
|
||||
// net::URLRequestJobFactory::ProtocolHandler:
|
||||
virtual net::URLRequestJob* MaybeCreateJob(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const OVERRIDE;
|
||||
virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE;
|
||||
|
||||
private:
|
||||
const scoped_refptr<base::TaskRunner> file_task_runner_;
|
||||
|
||||
mutable base::ScopedPtrHashMap<base::FilePath, Archive> archives_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AsarProtocolHandler);
|
||||
};
|
||||
|
||||
} // namespace asar
|
||||
|
||||
#endif // ATOM_BROWSER_NET_ASAR_ASAR_PROTOCOL_HANDLER_H_
|
142
atom/browser/net/asar/url_request_asar_job.cc
Normal file
142
atom/browser/net/asar/url_request_asar_job.cc
Normal file
|
@ -0,0 +1,142 @@
|
|||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/asar/url_request_asar_job.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "net/base/file_stream.h"
|
||||
#include "net/base/io_buffer.h"
|
||||
#include "net/base/mime_util.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/url_request/url_request_status.h"
|
||||
|
||||
namespace asar {
|
||||
|
||||
URLRequestAsarJob::URLRequestAsarJob(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate,
|
||||
Archive* archive,
|
||||
const base::FilePath& file_path,
|
||||
const scoped_refptr<base::TaskRunner>& file_task_runner)
|
||||
: net::URLRequestJob(request, network_delegate),
|
||||
archive_(archive),
|
||||
file_path_(file_path),
|
||||
stream_(new net::FileStream(file_task_runner)),
|
||||
remaining_bytes_(0),
|
||||
file_task_runner_(file_task_runner),
|
||||
weak_ptr_factory_(this) {}
|
||||
|
||||
URLRequestAsarJob::~URLRequestAsarJob() {}
|
||||
|
||||
void URLRequestAsarJob::Start() {
|
||||
if (!archive_ || !archive_->GetFileInfo(file_path_, &file_info_)) {
|
||||
NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
|
||||
net::ERR_FILE_NOT_FOUND));
|
||||
return;
|
||||
}
|
||||
|
||||
remaining_bytes_ = static_cast<int64>(file_info_.size);
|
||||
|
||||
int flags = base::File::FLAG_OPEN |
|
||||
base::File::FLAG_READ |
|
||||
base::File::FLAG_ASYNC;
|
||||
int rv = stream_->Open(archive_->path(), flags,
|
||||
base::Bind(&URLRequestAsarJob::DidOpen,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
if (rv != net::ERR_IO_PENDING)
|
||||
DidOpen(rv);
|
||||
}
|
||||
|
||||
void URLRequestAsarJob::Kill() {
|
||||
weak_ptr_factory_.InvalidateWeakPtrs();
|
||||
URLRequestJob::Kill();
|
||||
}
|
||||
|
||||
bool URLRequestAsarJob::ReadRawData(net::IOBuffer* dest,
|
||||
int dest_size,
|
||||
int* bytes_read) {
|
||||
if (remaining_bytes_ < dest_size)
|
||||
dest_size = static_cast<int>(remaining_bytes_);
|
||||
|
||||
// If we should copy zero bytes because |remaining_bytes_| is zero, short
|
||||
// circuit here.
|
||||
if (!dest_size) {
|
||||
*bytes_read = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int rv = stream_->Read(dest,
|
||||
dest_size,
|
||||
base::Bind(&URLRequestAsarJob::DidRead,
|
||||
weak_ptr_factory_.GetWeakPtr(),
|
||||
make_scoped_refptr(dest)));
|
||||
if (rv >= 0) {
|
||||
// Data is immediately available.
|
||||
*bytes_read = rv;
|
||||
remaining_bytes_ -= rv;
|
||||
DCHECK_GE(remaining_bytes_, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, a read error occured. We may just need to wait...
|
||||
if (rv == net::ERR_IO_PENDING) {
|
||||
SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
|
||||
} else {
|
||||
NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, rv));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool URLRequestAsarJob::GetMimeType(std::string* mime_type) const {
|
||||
return net::GetMimeTypeFromFile(file_path_, mime_type);
|
||||
}
|
||||
|
||||
void URLRequestAsarJob::DidOpen(int result) {
|
||||
if (result != net::OK) {
|
||||
NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
|
||||
return;
|
||||
}
|
||||
|
||||
int rv = stream_->Seek(net::FROM_BEGIN,
|
||||
file_info_.offset,
|
||||
base::Bind(&URLRequestAsarJob::DidSeek,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
if (rv != net::ERR_IO_PENDING) {
|
||||
// stream_->Seek() failed, so pass an intentionally erroneous value
|
||||
// into DidSeek().
|
||||
DidSeek(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void URLRequestAsarJob::DidSeek(int64 result) {
|
||||
if (result != static_cast<int64>(file_info_.offset)) {
|
||||
NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
|
||||
net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
|
||||
return;
|
||||
}
|
||||
|
||||
set_expected_content_size(remaining_bytes_);
|
||||
NotifyHeadersComplete();
|
||||
}
|
||||
|
||||
void URLRequestAsarJob::DidRead(scoped_refptr<net::IOBuffer> buf, int result) {
|
||||
if (result > 0) {
|
||||
SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
|
||||
remaining_bytes_ -= result;
|
||||
DCHECK_GE(remaining_bytes_, 0);
|
||||
}
|
||||
|
||||
buf = NULL;
|
||||
|
||||
if (result == 0) {
|
||||
NotifyDone(net::URLRequestStatus());
|
||||
} else if (result < 0) {
|
||||
NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
|
||||
}
|
||||
|
||||
NotifyReadComplete(result);
|
||||
}
|
||||
|
||||
} // namespace asar
|
72
atom/browser/net/asar/url_request_asar_job.h
Normal file
72
atom/browser/net/asar/url_request_asar_job.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_ASAR_URL_REQUEST_ASAR_JOB_H_
|
||||
#define ATOM_BROWSER_NET_ASAR_URL_REQUEST_ASAR_JOB_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "atom/common/asar/archive.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "net/url_request/url_request_job.h"
|
||||
|
||||
namespace base {
|
||||
class TaskRunner;
|
||||
}
|
||||
|
||||
namespace net {
|
||||
class FileStream;
|
||||
}
|
||||
|
||||
namespace asar {
|
||||
|
||||
class URLRequestAsarJob : public net::URLRequestJob {
|
||||
public:
|
||||
URLRequestAsarJob(net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate,
|
||||
Archive* archive,
|
||||
const base::FilePath& file_path,
|
||||
const scoped_refptr<base::TaskRunner>& file_task_runner);
|
||||
|
||||
// net::URLRequestJob:
|
||||
virtual void Start() OVERRIDE;
|
||||
virtual void Kill() OVERRIDE;
|
||||
virtual bool ReadRawData(net::IOBuffer* buf,
|
||||
int buf_size,
|
||||
int* bytes_read) OVERRIDE;
|
||||
virtual bool GetMimeType(std::string* mime_type) const OVERRIDE;
|
||||
|
||||
protected:
|
||||
virtual ~URLRequestAsarJob();
|
||||
|
||||
private:
|
||||
// Callback after opening file on a background thread.
|
||||
void DidOpen(int result);
|
||||
|
||||
// Callback after seeking to the beginning of |byte_range_| in the file
|
||||
// on a background thread.
|
||||
void DidSeek(int64 result);
|
||||
|
||||
// Callback after data is asynchronously read from the file into |buf|.
|
||||
void DidRead(scoped_refptr<net::IOBuffer> buf, int result);
|
||||
|
||||
Archive* archive_;
|
||||
Archive::FileInfo file_info_;
|
||||
base::FilePath file_path_;
|
||||
|
||||
scoped_ptr<net::FileStream> stream_;
|
||||
int64 remaining_bytes_;
|
||||
|
||||
const scoped_refptr<base::TaskRunner> file_task_runner_;
|
||||
|
||||
base::WeakPtrFactory<URLRequestAsarJob> weak_ptr_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(URLRequestAsarJob);
|
||||
};
|
||||
|
||||
} // namespace asar
|
||||
|
||||
#endif // ATOM_BROWSER_NET_ASAR_URL_REQUEST_ASAR_JOB_H_
|
Loading…
Add table
Add a link
Reference in a new issue