Merge remote-tracking branch 'atom/master'

This commit is contained in:
Plusb Preco 2015-09-27 22:20:51 +09:00
commit 7923e19553
69 changed files with 908 additions and 213 deletions

View file

@ -4,7 +4,7 @@
'product_name%': 'Electron', 'product_name%': 'Electron',
'company_name%': 'GitHub, Inc', 'company_name%': 'GitHub, Inc',
'company_abbr%': 'github', 'company_abbr%': 'github',
'version%': '0.33.0', 'version%': '0.33.3',
}, },
'includes': [ 'includes': [
'filenames.gypi', 'filenames.gypi',

View file

@ -0,0 +1,201 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/api/atom_api_download_item.h"
#include <map>
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/native_mate_converters/gurl_converter.h"
#include "atom/common/node_includes.h"
#include "base/memory/linked_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "native_mate/dictionary.h"
#include "net/base/filename_util.h"
namespace mate {
template<>
struct Converter<content::DownloadItem::DownloadState> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
content::DownloadItem::DownloadState state) {
std::string download_state;
switch (state) {
case content::DownloadItem::COMPLETE:
download_state = "completed";
break;
case content::DownloadItem::CANCELLED:
download_state = "cancelled";
break;
case content::DownloadItem::INTERRUPTED:
download_state = "interrupted";
break;
default:
break;
}
return ConvertToV8(isolate, download_state);
}
};
} // namespace mate
namespace atom {
namespace api {
namespace {
// The wrapDownloadItem funtion which is implemented in JavaScript
using WrapDownloadItemCallback = base::Callback<void(v8::Local<v8::Value>)>;
WrapDownloadItemCallback g_wrap_download_item;
char kDownloadItemSavePathKey[] = "DownloadItemSavePathKey";
std::map<uint32, linked_ptr<v8::Global<v8::Value>>> g_download_item_objects;
} // namespace
DownloadItem::SavePathData::SavePathData(const base::FilePath& path) :
path_(path) {
}
const base::FilePath& DownloadItem::SavePathData::path() {
return path_;
}
DownloadItem::DownloadItem(content::DownloadItem* download_item) :
download_item_(download_item) {
download_item_->AddObserver(this);
}
DownloadItem::~DownloadItem() {
Destroy();
}
void DownloadItem::Destroy() {
if (download_item_) {
download_item_->RemoveObserver(this);
auto iter = g_download_item_objects.find(download_item_->GetId());
if (iter != g_download_item_objects.end())
g_download_item_objects.erase(iter);
download_item_ = nullptr;
}
}
bool DownloadItem::IsDestroyed() const {
return download_item_ == nullptr;
}
void DownloadItem::OnDownloadUpdated(content::DownloadItem* item) {
download_item_->IsDone() ? Emit("done", item->GetState()) : Emit("updated");
}
void DownloadItem::OnDownloadDestroyed(content::DownloadItem* download) {
Destroy();
}
int64 DownloadItem::GetReceivedBytes() {
return download_item_->GetReceivedBytes();
}
int64 DownloadItem::GetTotalBytes() {
return download_item_->GetTotalBytes();
}
const GURL& DownloadItem::GetUrl() {
return download_item_->GetURL();
}
std::string DownloadItem::GetMimeType() {
return download_item_->GetMimeType();
}
bool DownloadItem::HasUserGesture() {
return download_item_->HasUserGesture();
}
std::string DownloadItem::GetFilename() {
return base::UTF16ToUTF8(net::GenerateFileName(GetUrl(),
GetContentDisposition(),
std::string(),
download_item_->GetSuggestedFilename(),
GetMimeType(),
std::string()).LossyDisplayName());
}
std::string DownloadItem::GetContentDisposition() {
return download_item_->GetContentDisposition();
}
void DownloadItem::SetSavePath(const base::FilePath& path) {
download_item_->SetUserData(UserDataKey(), new SavePathData(path));
}
void DownloadItem::Pause() {
download_item_->Pause();
}
void DownloadItem::Resume() {
download_item_->Resume();
}
void DownloadItem::Cancel() {
download_item_->Cancel(true);
}
mate::ObjectTemplateBuilder DownloadItem::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
return mate::ObjectTemplateBuilder(isolate)
.SetMethod("pause", &DownloadItem::Pause)
.SetMethod("resume", &DownloadItem::Resume)
.SetMethod("cancel", &DownloadItem::Cancel)
.SetMethod("getReceivedBytes", &DownloadItem::GetReceivedBytes)
.SetMethod("getTotalBytes", &DownloadItem::GetTotalBytes)
.SetMethod("getUrl", &DownloadItem::GetUrl)
.SetMethod("getMimeType", &DownloadItem::GetMimeType)
.SetMethod("hasUserGesture", &DownloadItem::HasUserGesture)
.SetMethod("getFilename", &DownloadItem::GetFilename)
.SetMethod("getContentDisposition", &DownloadItem::GetContentDisposition)
.SetMethod("setSavePath", &DownloadItem::SetSavePath);
}
void SetWrapDownloadItem(const WrapDownloadItemCallback& callback) {
g_wrap_download_item = callback;
}
void ClearWrapDownloadItem() {
g_wrap_download_item.Reset();
}
// static
mate::Handle<DownloadItem> DownloadItem::Create(
v8::Isolate* isolate, content::DownloadItem* item) {
auto handle = mate::CreateHandle(isolate, new DownloadItem(item));
g_wrap_download_item.Run(handle.ToV8());
g_download_item_objects[item->GetId()] = make_linked_ptr(
new v8::Global<v8::Value>(isolate, handle.ToV8()));
return handle;
}
// static
void* DownloadItem::UserDataKey() {
return &kDownloadItemSavePathKey;
}
} // namespace api
} // namespace atom
namespace {
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
v8::Local<v8::Context> context, void* priv) {
v8::Isolate* isolate = context->GetIsolate();
mate::Dictionary dict(isolate, exports);
dict.SetMethod("_setWrapDownloadItem", &atom::api::SetWrapDownloadItem);
dict.SetMethod("_clearWrapDownloadItem", &atom::api::ClearWrapDownloadItem);
}
} // namespace
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_download_item, Initialize);

View file

@ -0,0 +1,72 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_API_ATOM_API_DOWNLOAD_ITEM_H_
#define ATOM_BROWSER_API_ATOM_API_DOWNLOAD_ITEM_H_
#include <string>
#include "atom/browser/api/trackable_object.h"
#include "base/files/file_path.h"
#include "content/public/browser/download_item.h"
#include "native_mate/handle.h"
#include "url/gurl.h"
namespace atom {
namespace api {
class DownloadItem : public mate::EventEmitter,
public content::DownloadItem::Observer {
public:
class SavePathData : public base::SupportsUserData::Data {
public:
explicit SavePathData(const base::FilePath& path);
const base::FilePath& path();
private:
base::FilePath path_;
};
static mate::Handle<DownloadItem> Create(v8::Isolate* isolate,
content::DownloadItem* item);
static void* UserDataKey();
protected:
explicit DownloadItem(content::DownloadItem* download_item);
~DownloadItem();
// Override content::DownloadItem::Observer methods
void OnDownloadUpdated(content::DownloadItem* download) override;
void OnDownloadDestroyed(content::DownloadItem* download) override;
void Pause();
void Resume();
void Cancel();
int64 GetReceivedBytes();
int64 GetTotalBytes();
std::string GetMimeType();
bool HasUserGesture();
std::string GetFilename();
std::string GetContentDisposition();
const GURL& GetUrl();
void SetSavePath(const base::FilePath& path);
private:
// mate::Wrappable:
mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override;
bool IsDestroyed() const override;
void Destroy();
content::DownloadItem* download_item_;
DISALLOW_COPY_AND_ASSIGN(DownloadItem);
};
} // namespace api
} // namespace atom
#endif // ATOM_BROWSER_API_ATOM_API_DOWNLOAD_ITEM_H_

View file

@ -8,6 +8,7 @@
#include <vector> #include <vector>
#include "atom/browser/api/atom_api_cookies.h" #include "atom/browser/api/atom_api_cookies.h"
#include "atom/browser/api/atom_api_download_item.h"
#include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_context.h"
#include "atom/browser/api/atom_api_web_contents.h" #include "atom/browser/api/atom_api_web_contents.h"
#include "atom/common/native_mate_converters/callback.h" #include "atom/common/native_mate_converters/callback.h"
@ -101,19 +102,6 @@ struct Converter<ClearStorageDataOptions> {
} }
}; };
template<>
struct Converter<content::DownloadItem*> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
content::DownloadItem* val) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("url", val->GetURL());
dict.Set("filename", val->GetSuggestedFilename());
dict.Set("mimeType", val->GetMimeType());
dict.Set("hasUserGesture", val->HasUserGesture());
return dict.GetHandle();
}
};
} // namespace mate } // namespace mate
namespace atom { namespace atom {
@ -245,11 +233,12 @@ Session::~Session() {
} }
void Session::OnDownloadCreated(content::DownloadManager* manager, void Session::OnDownloadCreated(content::DownloadManager* manager,
content::DownloadItem* item) { content::DownloadItem* item) {
auto web_contents = item->GetWebContents(); auto web_contents = item->GetWebContents();
bool prevent_default = Emit("will-download", item, bool prevent_default = Emit(
api::WebContents::CreateFrom(isolate(), "will-download",
web_contents)); DownloadItem::Create(isolate(), item),
api::WebContents::CreateFrom(isolate(), web_contents));
if (prevent_default) { if (prevent_default) {
item->Cancel(true); item->Cancel(true);
item->Remove(); item->Remove();

View file

@ -120,6 +120,33 @@ struct Converter<WindowOpenDisposition> {
} }
}; };
template<>
struct Converter<net::HttpResponseHeaders*> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
net::HttpResponseHeaders* headers) {
base::DictionaryValue response_headers;
if (headers) {
void* iter = nullptr;
std::string key;
std::string value;
while (headers->EnumerateHeaderLines(&iter, &key, &value)) {
key = base::StringToLowerASCII(key);
value = base::StringToLowerASCII(value);
if (response_headers.HasKey(key)) {
base::ListValue* values = nullptr;
if (response_headers.GetList(key, &values))
values->AppendString(value);
} else {
scoped_ptr<base::ListValue> values(new base::ListValue());
values->AppendString(value);
response_headers.Set(key, values.Pass());
}
}
}
return ConvertToV8(isolate, response_headers);
}
};
} // namespace mate } // namespace mate
@ -131,7 +158,7 @@ namespace {
v8::Persistent<v8::ObjectTemplate> template_; v8::Persistent<v8::ObjectTemplate> template_;
// The wrapWebContents funtion which is implemented in JavaScript // The wrapWebContents function which is implemented in JavaScript
using WrapWebContentsCallback = base::Callback<void(v8::Local<v8::Value>)>; using WrapWebContentsCallback = base::Callback<void(v8::Local<v8::Value>)>;
WrapWebContentsCallback g_wrap_web_contents; WrapWebContentsCallback g_wrap_web_contents;
@ -201,7 +228,7 @@ WebContents::WebContents(v8::Isolate* isolate,
AttachAsUserData(web_contents); AttachAsUserData(web_contents);
InitWithWebContents(web_contents); InitWithWebContents(web_contents);
// Save the preferences. // Save the preferences in C++.
base::DictionaryValue web_preferences; base::DictionaryValue web_preferences;
mate::ConvertFromV8(isolate, options.GetHandle(), &web_preferences); mate::ConvertFromV8(isolate, options.GetHandle(), &web_preferences);
new WebContentsPreferences(web_contents, &web_preferences); new WebContentsPreferences(web_contents, &web_preferences);
@ -414,30 +441,6 @@ void WebContents::DidStopLoading() {
void WebContents::DidGetResourceResponseStart( void WebContents::DidGetResourceResponseStart(
const content::ResourceRequestDetails& details) { const content::ResourceRequestDetails& details) {
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
base::DictionaryValue response_headers;
net::HttpResponseHeaders* headers = details.headers.get();
if (!headers)
return;
void* iter = nullptr;
std::string key;
std::string value;
while (headers->EnumerateHeaderLines(&iter, &key, &value)) {
key = base::StringToLowerASCII(key);
value = base::StringToLowerASCII(value);
if (response_headers.HasKey(key)) {
base::ListValue* values = nullptr;
if (response_headers.GetList(key, &values))
values->AppendString(value);
} else {
scoped_ptr<base::ListValue> values(new base::ListValue());
values->AppendString(value);
response_headers.Set(key, values.Pass());
}
}
Emit("did-get-response-details", Emit("did-get-response-details",
details.socket_address.IsEmpty(), details.socket_address.IsEmpty(),
details.url, details.url,
@ -445,7 +448,7 @@ void WebContents::DidGetResourceResponseStart(
details.http_response_code, details.http_response_code,
details.method, details.method,
details.referrer, details.referrer,
response_headers); details.headers.get());
} }
void WebContents::DidGetRedirectForResourceRequest( void WebContents::DidGetRedirectForResourceRequest(
@ -454,7 +457,11 @@ void WebContents::DidGetRedirectForResourceRequest(
Emit("did-get-redirect-request", Emit("did-get-redirect-request",
details.url, details.url,
details.new_url, details.new_url,
(details.resource_type == content::RESOURCE_TYPE_MAIN_FRAME)); (details.resource_type == content::RESOURCE_TYPE_MAIN_FRAME),
details.http_response_code,
details.method,
details.referrer,
details.headers.get());
} }
void WebContents::DidNavigateMainFrame( void WebContents::DidNavigateMainFrame(
@ -880,6 +887,12 @@ bool WebContents::IsGuest() const {
return type_ == WEB_VIEW; return type_ == WEB_VIEW;
} }
v8::Local<v8::Value> WebContents::GetWebPreferences(v8::Isolate* isolate) {
WebContentsPreferences* web_preferences =
WebContentsPreferences::FromWebContents(web_contents());
return mate::ConvertToV8(isolate, *web_preferences->web_preferences());
}
mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder( mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder(
v8::Isolate* isolate) { v8::Isolate* isolate) {
if (template_.IsEmpty()) if (template_.IsEmpty())
@ -935,6 +948,7 @@ mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder(
.SetMethod("setSize", &WebContents::SetSize) .SetMethod("setSize", &WebContents::SetSize)
.SetMethod("setAllowTransparency", &WebContents::SetAllowTransparency) .SetMethod("setAllowTransparency", &WebContents::SetAllowTransparency)
.SetMethod("isGuest", &WebContents::IsGuest) .SetMethod("isGuest", &WebContents::IsGuest)
.SetMethod("getWebPreferences", &WebContents::GetWebPreferences)
.SetMethod("hasServiceWorker", &WebContents::HasServiceWorker) .SetMethod("hasServiceWorker", &WebContents::HasServiceWorker)
.SetMethod("unregisterServiceWorker", .SetMethod("unregisterServiceWorker",
&WebContents::UnregisterServiceWorker) &WebContents::UnregisterServiceWorker)
@ -988,7 +1002,7 @@ mate::Handle<WebContents> WebContents::CreateFrom(
// static // static
mate::Handle<WebContents> WebContents::Create( mate::Handle<WebContents> WebContents::Create(
v8::Isolate* isolate, const mate::Dictionary& options) { v8::Isolate* isolate, const mate::Dictionary& options) {
auto handle = mate::CreateHandle(isolate, new WebContents(isolate, options)); auto handle = mate::CreateHandle(isolate, new WebContents(isolate, options));
g_wrap_web_contents.Run(handle.ToV8()); g_wrap_web_contents.Run(handle.ToV8());
return handle; return handle;
} }

View file

@ -132,6 +132,9 @@ class WebContents : public mate::TrackableObject<WebContents>,
void SetAllowTransparency(bool allow); void SetAllowTransparency(bool allow);
bool IsGuest() const; bool IsGuest() const;
// Returns the web preferences of current WebContents.
v8::Local<v8::Value> GetWebPreferences(v8::Isolate* isolate);
protected: protected:
explicit WebContents(content::WebContents* web_contents); explicit WebContents(content::WebContents* web_contents);
WebContents(v8::Isolate* isolate, const mate::Dictionary& options); WebContents(v8::Isolate* isolate, const mate::Dictionary& options);

View file

@ -82,6 +82,10 @@ Window::Window(v8::Isolate* isolate, const mate::Dictionary& options) {
web_contents_.Reset(isolate, web_contents.ToV8()); web_contents_.Reset(isolate, web_contents.ToV8());
api_web_contents_ = web_contents.get(); api_web_contents_ = web_contents.get();
// Keep a copy of the options for later use.
mate::Dictionary(isolate, web_contents->GetWrapper(isolate)).Set(
"browserWindowOptions", options);
// Creates BrowserWindow. // Creates BrowserWindow.
window_.reset(NativeWindow::Create(web_contents->managed_web_contents(), window_.reset(NativeWindow::Create(web_contents->managed_web_contents(),
options)); options));

View file

@ -2,6 +2,7 @@ EventEmitter = require('events').EventEmitter
bindings = process.atomBinding 'app' bindings = process.atomBinding 'app'
sessionBindings = process.atomBinding 'session' sessionBindings = process.atomBinding 'session'
downloadItemBindings = process.atomBinding 'download_item'
app = bindings.app app = bindings.app
app.__proto__ = EventEmitter.prototype app.__proto__ = EventEmitter.prototype
@ -10,6 +11,15 @@ wrapSession = (session) ->
# session is an Event Emitter. # session is an Event Emitter.
session.__proto__ = EventEmitter.prototype session.__proto__ = EventEmitter.prototype
wrapDownloadItem = (download_item) ->
# download_item is an Event Emitter.
download_item.__proto__ = EventEmitter.prototype
# Be compatible with old APIs.
download_item.url = download_item.getUrl()
download_item.filename = download_item.getFilename()
download_item.mimeType = download_item.getMimeType()
download_item.hasUserGesture = download_item.hasUserGesture()
app.setApplicationMenu = (menu) -> app.setApplicationMenu = (menu) ->
require('menu').setApplicationMenu menu require('menu').setApplicationMenu menu
@ -51,5 +61,8 @@ app.on 'activate', (event, hasVisibleWindows) -> @emit 'activate-with-no-open-wi
sessionBindings._setWrapSession wrapSession sessionBindings._setWrapSession wrapSession
process.once 'exit', sessionBindings._clearWrapSession process.once 'exit', sessionBindings._clearWrapSession
downloadItemBindings._setWrapDownloadItem wrapDownloadItem
process.once 'exit', downloadItemBindings._clearWrapDownloadItem
# Only one App object pemitted. # Only one App object pemitted.
module.exports = app module.exports = app

View file

@ -6,6 +6,7 @@
#include "atom/browser/atom_browser_main_parts.h" #include "atom/browser/atom_browser_main_parts.h"
#include "atom/browser/atom_download_manager_delegate.h" #include "atom/browser/atom_download_manager_delegate.h"
#include "atom/browser/atom_ssl_config_service.h"
#include "atom/browser/browser.h" #include "atom/browser/browser.h"
#include "atom/browser/net/atom_url_request_job_factory.h" #include "atom/browser/net/atom_url_request_job_factory.h"
#include "atom/browser/net/asar/asar_protocol_handler.h" #include "atom/browser/net/asar/asar_protocol_handler.h"
@ -156,6 +157,10 @@ content::BrowserPluginGuestManager* AtomBrowserContext::GetGuestManager() {
return guest_manager_.get(); return guest_manager_.get();
} }
net::SSLConfigService* AtomBrowserContext::CreateSSLConfigService() {
return new AtomSSLConfigService;
}
void AtomBrowserContext::RegisterPrefs(PrefRegistrySimple* pref_registry) { void AtomBrowserContext::RegisterPrefs(PrefRegistrySimple* pref_registry) {
pref_registry->RegisterFilePathPref(prefs::kSelectFileLastDirectory, pref_registry->RegisterFilePathPref(prefs::kSelectFileLastDirectory,
base::FilePath()); base::FilePath());

View file

@ -27,6 +27,7 @@ class AtomBrowserContext : public brightray::BrowserContext {
content::URLRequestInterceptorScopedVector* interceptors) override; content::URLRequestInterceptorScopedVector* interceptors) override;
net::HttpCache::BackendFactory* CreateHttpCacheBackendFactory( net::HttpCache::BackendFactory* CreateHttpCacheBackendFactory(
const base::FilePath& base_path) override; const base::FilePath& base_path) override;
net::SSLConfigService* CreateSSLConfigService() override;
// content::BrowserContext: // content::BrowserContext:
content::DownloadManagerDelegate* GetDownloadManagerDelegate() override; content::DownloadManagerDelegate* GetDownloadManagerDelegate() override;

View file

@ -6,6 +6,7 @@
#include <string> #include <string>
#include "atom/browser/api/atom_api_download_item.h"
#include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_context.h"
#include "atom/browser/native_window.h" #include "atom/browser/native_window.h"
#include "atom/browser/ui/file_dialog.h" #include "atom/browser/ui/file_dialog.h"
@ -73,18 +74,19 @@ void AtomDownloadManagerDelegate::OnDownloadPathGenerated(
if (relay) if (relay)
window = relay->window.get(); window = relay->window.get();
file_dialog::Filters filters;
base::FilePath path; base::FilePath path;
if (!file_dialog::ShowSaveDialog(window, item->GetURL().spec(), default_path, if (file_dialog::ShowSaveDialog(window, item->GetURL().spec(), default_path,
filters, &path)) { file_dialog::Filters(), &path)) {
return; // Remember the last selected download directory.
AtomBrowserContext* browser_context = static_cast<AtomBrowserContext*>(
download_manager_->GetBrowserContext());
browser_context->prefs()->SetFilePath(prefs::kDownloadDefaultDirectory,
path.DirName());
} }
// Remeber the last selected download directory. // Running the DownloadTargetCallback with an empty FilePath signals that the
AtomBrowserContext* browser_context = static_cast<AtomBrowserContext*>( // download should be cancelled.
download_manager_->GetBrowserContext()); // If user cancels the file save dialog, run the callback with empty FilePath.
browser_context->prefs()->SetFilePath(prefs::kDownloadDefaultDirectory,
path.DirName());
callback.Run(path, callback.Run(path,
content::DownloadItem::TARGET_DISPOSITION_PROMPT, content::DownloadItem::TARGET_DISPOSITION_PROMPT,
content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, path); content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, path);
@ -100,6 +102,25 @@ bool AtomDownloadManagerDelegate::DetermineDownloadTarget(
const content::DownloadTargetCallback& callback) { const content::DownloadTargetCallback& callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!download->GetForcedFilePath().empty()) {
callback.Run(download->GetForcedFilePath(),
content::DownloadItem::TARGET_DISPOSITION_OVERWRITE,
content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
download->GetForcedFilePath());
return true;
}
base::SupportsUserData::Data* save_path = download->GetUserData(
atom::api::DownloadItem::UserDataKey());
if (save_path) {
const base::FilePath& default_download_path =
static_cast<api::DownloadItem::SavePathData*>(save_path)->path();
callback.Run(default_download_path,
content::DownloadItem::TARGET_DISPOSITION_OVERWRITE,
content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
default_download_path);
return true;
}
AtomBrowserContext* browser_context = static_cast<AtomBrowserContext*>( AtomBrowserContext* browser_context = static_cast<AtomBrowserContext*>(
download_manager_->GetBrowserContext()); download_manager_->GetBrowserContext());
base::FilePath default_download_path = browser_context->prefs()->GetFilePath( base::FilePath default_download_path = browser_context->prefs()->GetFilePath(
@ -110,14 +131,6 @@ bool AtomDownloadManagerDelegate::DetermineDownloadTarget(
default_download_path = path.Append(FILE_PATH_LITERAL("Downloads")); default_download_path = path.Append(FILE_PATH_LITERAL("Downloads"));
} }
if (!download->GetForcedFilePath().empty()) {
callback.Run(download->GetForcedFilePath(),
content::DownloadItem::TARGET_DISPOSITION_OVERWRITE,
content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
download->GetForcedFilePath());
return true;
}
CreateDownloadPathCallback download_path_callback = CreateDownloadPathCallback download_path_callback =
base::Bind(&AtomDownloadManagerDelegate::OnDownloadPathGenerated, base::Bind(&AtomDownloadManagerDelegate::OnDownloadPathGenerated,
weak_ptr_factory_.GetWeakPtr(), weak_ptr_factory_.GetWeakPtr(),

View file

@ -0,0 +1,47 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/atom_ssl_config_service.h"
#include <string>
#include "base/command_line.h"
#include "atom/common/options_switches.h"
#include "content/public/browser/browser_thread.h"
#include "net/socket/ssl_client_socket.h"
namespace atom {
namespace {
uint16 GetSSLProtocolVersion(const std::string& version_string) {
uint16 version = 0; // Invalid
if (version_string == "tls1")
version = net::SSL_PROTOCOL_VERSION_TLS1;
else if (version_string == "tls1.1")
version = net::SSL_PROTOCOL_VERSION_TLS1_1;
else if (version_string == "tls1.2")
version = net::SSL_PROTOCOL_VERSION_TLS1_2;
return version;
}
} // namespace
AtomSSLConfigService::AtomSSLConfigService() {
auto cmd_line = base::CommandLine::ForCurrentProcess();
if (cmd_line->HasSwitch(switches::kSSLVersionFallbackMin)) {
auto version_string =
cmd_line->GetSwitchValueASCII(switches::kSSLVersionFallbackMin);
config_.version_fallback_min = GetSSLProtocolVersion(version_string);
}
}
AtomSSLConfigService::~AtomSSLConfigService() {
}
void AtomSSLConfigService::GetSSLConfig(net::SSLConfig* config) {
*config = config_;
}
} // namespace atom

View file

@ -0,0 +1,28 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_ATOM_SSL_CONFIG_SERVICE_H_
#define ATOM_BROWSER_ATOM_SSL_CONFIG_SERVICE_H_
#include "net/ssl/ssl_config_service.h"
namespace atom {
class AtomSSLConfigService : public net::SSLConfigService {
public:
AtomSSLConfigService();
~AtomSSLConfigService() override;
// net::SSLConfigService:
void GetSSLConfig(net::SSLConfig* config) override;
private:
net::SSLConfig config_;
DISALLOW_COPY_AND_ASSIGN(AtomSSLConfigService);
};
} // namespace atom
#endif // ATOM_BROWSER_ATOM_SSL_CONFIG_SERVICE_H_

View file

@ -84,6 +84,8 @@ createGuest = (embedder, params) ->
if params.allowtransparency? if params.allowtransparency?
@setAllowTransparency params.allowtransparency @setAllowTransparency params.allowtransparency
guest.allowPopups = params.allowpopups
# Dispatch events to embedder. # Dispatch events to embedder.
for event in supportedWebViewEvents for event in supportedWebViewEvents
do (event) -> do (event) ->

View file

@ -4,6 +4,17 @@ BrowserWindow = require 'browser-window'
frameToGuest = {} frameToGuest = {}
# Merge |options| with the |embedder|'s window's options.
mergeBrowserWindowOptions = (embedder, options) ->
if embedder.browserWindowOptions?
# Inherit the original options if it is a BrowserWindow.
options.__proto__ = embedder.browserWindowOptions
else
# Or only inherit web-preferences if it is a webview.
options['web-preferences'] ?= {}
options['web-preferences'].__proto__ = embedder.getWebPreferences()
options
# Create a new guest created by |embedder| with |options|. # Create a new guest created by |embedder| with |options|.
createGuest = (embedder, url, frameName, options) -> createGuest = (embedder, url, frameName, options) ->
guest = frameToGuest[frameName] guest = frameToGuest[frameName]
@ -40,11 +51,12 @@ createGuest = (embedder, url, frameName, options) ->
# Routed window.open messages. # Routed window.open messages.
ipc.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, args...) -> ipc.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, args...) ->
[url, frameName, options] = args [url, frameName, options] = args
event.sender.emit 'new-window', event, url, frameName, 'new-window' options = mergeBrowserWindowOptions event.sender, options
if event.sender.isGuest() or event.defaultPrevented event.sender.emit 'new-window', event, url, frameName, 'new-window', options
if (event.sender.isGuest() and not event.sender.allowPopups) or event.defaultPrevented
event.returnValue = null event.returnValue = null
else else
event.returnValue = createGuest event.sender, args... event.returnValue = createGuest event.sender, url, frameName, options
ipc.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', (event, guestId) -> ipc.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', (event, guestId) ->
BrowserWindow.fromId(guestId)?.destroy() BrowserWindow.fromId(guestId)?.destroy()

View file

@ -68,6 +68,7 @@ NativeWindow::NativeWindow(
const mate::Dictionary& options) const mate::Dictionary& options)
: content::WebContentsObserver(inspectable_web_contents->GetWebContents()), : content::WebContentsObserver(inspectable_web_contents->GetWebContents()),
has_frame_(true), has_frame_(true),
force_using_draggable_region_(false),
transparent_(false), transparent_(false),
enable_larger_than_screen_(false), enable_larger_than_screen_(false),
is_closed_(false), is_closed_(false),
@ -159,8 +160,13 @@ void NativeWindow::InitFromOptions(const mate::Dictionary& options) {
// Then show it. // Then show it.
bool show = true; bool show = true;
options.Get(switches::kShow, &show); options.Get(switches::kShow, &show);
if (show) if (show) {
Show(); Show();
} else {
// When RenderView is created it sets to visible, this is to prevent
// breaking the visibility API.
web_contents()->WasHidden();
}
} }
void NativeWindow::SetSize(const gfx::Size& size) { void NativeWindow::SetSize(const gfx::Size& size) {
@ -468,7 +474,7 @@ bool NativeWindow::OnMessageReceived(const IPC::Message& message) {
void NativeWindow::UpdateDraggableRegions( void NativeWindow::UpdateDraggableRegions(
const std::vector<DraggableRegion>& regions) { const std::vector<DraggableRegion>& regions) {
// Draggable region is not supported for non-frameless window. // Draggable region is not supported for non-frameless window.
if (has_frame_) if (has_frame_ && !force_using_draggable_region_)
return; return;
draggable_region_ = DraggableRegionsToSkRegion(regions); draggable_region_ = DraggableRegionsToSkRegion(regions);
} }

View file

@ -219,6 +219,13 @@ class NativeWindow : public content::WebContentsObserver,
bool enable_larger_than_screen() const { return enable_larger_than_screen_; } bool enable_larger_than_screen() const { return enable_larger_than_screen_; }
gfx::ImageSkia icon() const { return icon_; } gfx::ImageSkia icon() const { return icon_; }
bool force_using_draggable_region() const {
return force_using_draggable_region_;
}
void set_force_using_draggable_region(bool force) {
force_using_draggable_region_ = true;
}
void set_has_dialog_attached(bool has_dialog_attached) { void set_has_dialog_attached(bool has_dialog_attached) {
has_dialog_attached_ = has_dialog_attached; has_dialog_attached_ = has_dialog_attached;
} }
@ -257,6 +264,9 @@ class NativeWindow : public content::WebContentsObserver,
// Whether window has standard frame. // Whether window has standard frame.
bool has_frame_; bool has_frame_;
// Force the window to be aware of draggable regions.
bool force_using_draggable_region_;
// Whether window is transparent. // Whether window is transparent.
bool transparent_; bool transparent_;

View file

@ -351,21 +351,19 @@ NativeWindowMac::NativeWindowMac(
bool useStandardWindow = true; bool useStandardWindow = true;
options.Get(switches::kStandardWindow, &useStandardWindow); options.Get(switches::kStandardWindow, &useStandardWindow);
// New title bar styles are available in Yosemite or newer
std::string titleBarStyle;
if (base::mac::IsOSYosemiteOrLater())
options.Get(switches::kTitleBarStyle, &titleBarStyle);
NSUInteger styleMask = NSTitledWindowMask | NSClosableWindowMask | NSUInteger styleMask = NSTitledWindowMask | NSClosableWindowMask |
NSMiniaturizableWindowMask | NSResizableWindowMask; NSMiniaturizableWindowMask | NSResizableWindowMask;
if (!useStandardWindow || transparent() || !has_frame()) { if (!useStandardWindow || transparent() || !has_frame()) {
styleMask |= NSTexturedBackgroundWindowMask; styleMask |= NSTexturedBackgroundWindowMask;
} }
if ((titleBarStyle == "hidden") || (titleBarStyle == "hidden-inset")) {
std::string titleBarStyle = "default"; styleMask |= NSFullSizeContentViewWindowMask;
options.Get(switches::kTitleBarStyle, &titleBarStyle); styleMask |= NSUnifiedTitleAndToolbarWindowMask;
if (base::mac::IsOSYosemiteOrLater()) {
// New title bar styles are available in Yosemite or newer
if ((titleBarStyle == "hidden") || (titleBarStyle == "hidden-inset")) {
styleMask |= NSFullSizeContentViewWindowMask;
styleMask |= NSUnifiedTitleAndToolbarWindowMask;
}
} }
window_.reset([[AtomNSWindow alloc] window_.reset([[AtomNSWindow alloc]
@ -393,18 +391,18 @@ NativeWindowMac::NativeWindowMac(
// We will manage window's lifetime ourselves. // We will manage window's lifetime ourselves.
[window_ setReleasedWhenClosed:NO]; [window_ setReleasedWhenClosed:NO];
// Configure title bar look on Yosemite or newer // Hide the title bar.
if (base::mac::IsOSYosemiteOrLater()) { if ((titleBarStyle == "hidden") || (titleBarStyle == "hidden-inset")) {
if ((titleBarStyle == "hidden") || (titleBarStyle == "hidden-inset")) { [window_ setTitlebarAppearsTransparent:YES];
[window_ setTitlebarAppearsTransparent:YES]; [window_ setTitleVisibility:NSWindowTitleHidden];
[window_ setTitleVisibility:NSWindowTitleHidden]; if (titleBarStyle == "hidden-inset") {
if (titleBarStyle == "hidden-inset") { base::scoped_nsobject<NSToolbar> toolbar(
NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]; [[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]);
toolbar.showsBaselineSeparator = NO; [toolbar setShowsBaselineSeparator:NO];
[window_ setToolbar:toolbar]; [window_ setToolbar:toolbar];
[toolbar release];
}
} }
// We should be aware of draggable regions when using hidden titlebar.
set_force_using_draggable_region(true);
} }
// On OS X the initial window size doesn't include window frame. // On OS X the initial window size doesn't include window frame.
@ -436,6 +434,11 @@ NativeWindowMac::NativeWindowMac(
[view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
InstallView(); InstallView();
// Install the DraggableRegionView if it is forced to use draggable regions
// for normal window.
if (has_frame() && force_using_draggable_region())
InstallDraggableRegionView();
} }
NativeWindowMac::~NativeWindowMac() { NativeWindowMac::~NativeWindowMac() {
@ -467,6 +470,8 @@ bool NativeWindowMac::IsFocused() {
} }
void NativeWindowMac::Show() { void NativeWindowMac::Show() {
web_contents()->WasShown();
// This method is supposed to put focus on window, however if the app does not // This method is supposed to put focus on window, however if the app does not
// have focus then "makeKeyAndOrderFront" will only show the window. // have focus then "makeKeyAndOrderFront" will only show the window.
[NSApp activateIgnoringOtherApps:YES]; [NSApp activateIgnoringOtherApps:YES];
@ -475,11 +480,13 @@ void NativeWindowMac::Show() {
} }
void NativeWindowMac::ShowInactive() { void NativeWindowMac::ShowInactive() {
web_contents()->WasShown();
[window_ orderFrontRegardless]; [window_ orderFrontRegardless];
} }
void NativeWindowMac::Hide() { void NativeWindowMac::Hide() {
[window_ orderOut:nil]; [window_ orderOut:nil];
web_contents()->WasHidden();
} }
bool NativeWindowMac::IsVisible() { bool NativeWindowMac::IsVisible() {

View file

@ -338,15 +338,18 @@ bool NativeWindowViews::IsFocused() {
} }
void NativeWindowViews::Show() { void NativeWindowViews::Show() {
web_contents()->WasShown();
window_->native_widget_private()->ShowWithWindowState(GetRestoredState()); window_->native_widget_private()->ShowWithWindowState(GetRestoredState());
} }
void NativeWindowViews::ShowInactive() { void NativeWindowViews::ShowInactive() {
web_contents()->WasShown();
window_->ShowInactive(); window_->ShowInactive();
} }
void NativeWindowViews::Hide() { void NativeWindowViews::Hide() {
window_->Hide(); window_->Hide();
web_contents()->WasHidden();
} }
bool NativeWindowViews::IsVisible() { bool NativeWindowViews::IsVisible() {

View file

@ -17,7 +17,7 @@
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>atom.icns</string> <string>atom.icns</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>0.33.0</string> <string>0.33.3</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>10.8.0</string> <string>10.8.0</string>
<key>NSMainNibFile</key> <key>NSMainNibFile</key>

View file

@ -56,8 +56,8 @@ END
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,33,0,0 FILEVERSION 0,33,3,0
PRODUCTVERSION 0,33,0,0 PRODUCTVERSION 0,33,3,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -74,12 +74,12 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "GitHub, Inc." VALUE "CompanyName", "GitHub, Inc."
VALUE "FileDescription", "Electron" VALUE "FileDescription", "Electron"
VALUE "FileVersion", "0.33.0" VALUE "FileVersion", "0.33.3"
VALUE "InternalName", "electron.exe" VALUE "InternalName", "electron.exe"
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
VALUE "OriginalFilename", "electron.exe" VALUE "OriginalFilename", "electron.exe"
VALUE "ProductName", "Electron" VALUE "ProductName", "Electron"
VALUE "ProductVersion", "0.33.0" VALUE "ProductVersion", "0.33.3"
VALUE "SquirrelAwareVersion", "1" VALUE "SquirrelAwareVersion", "1"
END END
END END

View file

@ -4,10 +4,7 @@
#include "atom/browser/ui/win/notify_icon.h" #include "atom/browser/ui/win/notify_icon.h"
#include <shobjidl.h>
#include "atom/browser/ui/win/notify_icon_host.h" #include "atom/browser/ui/win/notify_icon_host.h"
#include "base/md5.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/win/windows_version.h" #include "base/win/windows_version.h"
@ -28,31 +25,7 @@ NotifyIcon::NotifyIcon(NotifyIconHost* host,
icon_id_(id), icon_id_(id),
window_(window), window_(window),
message_id_(message), message_id_(message),
menu_model_(NULL), menu_model_(NULL) {
has_tray_app_id_hash_(false) {
// NB: If we have an App Model ID, we should propagate that to the tray.
// Doing this prevents duplicate items from showing up in the notification
// preferences (i.e. "Always Show / Show notifications only / etc")
PWSTR explicit_app_id;
if (SUCCEEDED(GetCurrentProcessExplicitAppUserModelID(&explicit_app_id))) {
// GUIDs and MD5 hashes are the same length. So convenient!
base::MD5Sum(explicit_app_id,
sizeof(wchar_t) * wcslen(explicit_app_id),
reinterpret_cast<base::MD5Digest*>(&tray_app_id_hash_));
// Set the GUID to version 4 as described in RFC 4122, section 4.4.
// The format of GUID version 4 must be like
// xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx, where y is one of [8, 9, A, B].
tray_app_id_hash_.Data3 &= 0x0fff;
tray_app_id_hash_.Data3 |= 0x4000;
// Set y to one of [8, 9, A, B].
tray_app_id_hash_.Data4[0] = 1;
has_tray_app_id_hash_ = true;
CoTaskMemFree(explicit_app_id);
}
NOTIFYICONDATA icon_data; NOTIFYICONDATA icon_data;
InitIconData(&icon_data); InitIconData(&icon_data);
icon_data.uFlags |= NIF_MESSAGE; icon_data.uFlags |= NIF_MESSAGE;
@ -81,10 +54,6 @@ void NotifyIcon::HandleClickEvent(const gfx::Point& cursor_pos,
icon_id.uID = icon_id_; icon_id.uID = icon_id_;
icon_id.hWnd = window_; icon_id.hWnd = window_;
icon_id.cbSize = sizeof(NOTIFYICONIDENTIFIER); icon_id.cbSize = sizeof(NOTIFYICONIDENTIFIER);
if (has_tray_app_id_hash_)
memcpy(reinterpret_cast<void*>(&icon_id.guidItem),
&tray_app_id_hash_,
sizeof(GUID));
RECT rect = { 0 }; RECT rect = { 0 };
Shell_NotifyIconGetRect(&icon_id, &rect); Shell_NotifyIconGetRect(&icon_id, &rect);
@ -202,13 +171,6 @@ void NotifyIcon::InitIconData(NOTIFYICONDATA* icon_data) {
icon_data->cbSize = sizeof(NOTIFYICONDATA); icon_data->cbSize = sizeof(NOTIFYICONDATA);
icon_data->hWnd = window_; icon_data->hWnd = window_;
icon_data->uID = icon_id_; icon_data->uID = icon_id_;
if (has_tray_app_id_hash_) {
icon_data->uFlags |= NIF_GUID;
memcpy(reinterpret_cast<void*>(&icon_data->guidItem),
&tray_app_id_hash_,
sizeof(GUID));
}
} }
} // namespace atom } // namespace atom

View file

@ -79,10 +79,6 @@ class NotifyIcon : public TrayIcon {
// The context menu. // The context menu.
ui::SimpleMenuModel* menu_model_; ui::SimpleMenuModel* menu_model_;
// A hash of the app model ID
GUID tray_app_id_hash_;
bool has_tray_app_id_hash_;
DISALLOW_COPY_AND_ASSIGN(NotifyIcon); DISALLOW_COPY_AND_ASSIGN(NotifyIcon);
}; };

View file

@ -40,6 +40,9 @@ WebContentsPreferences::WebContentsPreferences(
base::DictionaryValue* web_preferences) { base::DictionaryValue* web_preferences) {
web_preferences_.Swap(web_preferences); web_preferences_.Swap(web_preferences);
web_contents->SetUserData(UserDataKey(), this); web_contents->SetUserData(UserDataKey(), this);
// The "isGuest" is not a preferences field.
web_preferences_.Remove("isGuest", nullptr);
} }
WebContentsPreferences::~WebContentsPreferences() { WebContentsPreferences::~WebContentsPreferences() {
@ -94,7 +97,7 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches(
if (base::FilePath(preload).IsAbsolute()) if (base::FilePath(preload).IsAbsolute())
command_line->AppendSwitchNative(switches::kPreloadScript, preload); command_line->AppendSwitchNative(switches::kPreloadScript, preload);
else else
LOG(ERROR) << "preload script must have abosulute path."; LOG(ERROR) << "preload script must have absolute path.";
} else if (web_preferences.GetString(switches::kPreloadUrl, &preload)) { } else if (web_preferences.GetString(switches::kPreloadUrl, &preload)) {
// Translate to file path if there is "preload-url" option. // Translate to file path if there is "preload-url" option.
base::FilePath preload_path; base::FilePath preload_path;

View file

@ -37,6 +37,9 @@ class WebContentsPreferences
// $.extend(|web_preferences_|, |new_web_preferences|). // $.extend(|web_preferences_|, |new_web_preferences|).
void Merge(const base::DictionaryValue& new_web_preferences); void Merge(const base::DictionaryValue& new_web_preferences);
// Returns the web preferences.
base::DictionaryValue* web_preferences() { return &web_preferences_; }
private: private:
friend class content::WebContentsUserData<WebContentsPreferences>; friend class content::WebContentsUserData<WebContentsPreferences>;

View file

@ -115,6 +115,14 @@ bool FillFileInfoWithNode(Archive::FileInfo* info,
Archive::Archive(const base::FilePath& path) Archive::Archive(const base::FilePath& path)
: path_(path), : path_(path),
file_(path_, base::File::FLAG_OPEN | base::File::FLAG_READ), file_(path_, base::File::FLAG_OPEN | base::File::FLAG_READ),
#if defined(OS_WIN)
fd_(_open_osfhandle(
reinterpret_cast<intptr_t>(file_.GetPlatformFile()), 0)),
#elif defined(OS_POSIX)
fd_(file_.GetPlatformFile()),
#else
fd_(-1),
#endif
header_size_(0) { header_size_(0) {
} }
@ -271,17 +279,7 @@ bool Archive::CopyFileOut(const base::FilePath& path, base::FilePath* out) {
} }
int Archive::GetFD() const { int Archive::GetFD() const {
if (!file_.IsValid()) return fd_;
return -1;
#if defined(OS_WIN)
return
_open_osfhandle(reinterpret_cast<intptr_t>(file_.GetPlatformFile()), 0);
#elif defined(OS_POSIX)
return file_.GetPlatformFile();
#else
return -1;
#endif
} }
} // namespace asar } // namespace asar

View file

@ -69,6 +69,7 @@ class Archive {
private: private:
base::FilePath path_; base::FilePath path_;
base::File file_; base::File file_;
int fd_;
uint32 header_size_; uint32 header_size_;
scoped_ptr<base::DictionaryValue> header_; scoped_ptr<base::DictionaryValue> header_;

View file

@ -7,7 +7,7 @@
#define ATOM_MAJOR_VERSION 0 #define ATOM_MAJOR_VERSION 0
#define ATOM_MINOR_VERSION 33 #define ATOM_MINOR_VERSION 33
#define ATOM_PATCH_VERSION 0 #define ATOM_PATCH_VERSION 3
#define ATOM_VERSION_IS_RELEASE 1 #define ATOM_VERSION_IS_RELEASE 1

View file

@ -329,7 +329,7 @@ exports.wrapFsWithAsar = (fs) ->
buffer = new Buffer(info.size) buffer = new Buffer(info.size)
fd = archive.getFd() fd = archive.getFd()
retrun undefined unless fd >= 0 return undefined unless fd >= 0
fs.readSync fd, buffer, 0, info.size, info.offset fs.readSync fd, buffer, 0, info.size, info.offset
buffer.toString 'utf8' buffer.toString 'utf8'

View file

@ -1,8 +1,7 @@
process = global.process fs = require 'fs'
fs = require 'fs' path = require 'path'
path = require 'path' timers = require 'timers'
timers = require 'timers' Module = require 'module'
Module = require 'module'
process.atomBinding = (name) -> process.atomBinding = (name) ->
try try
@ -37,6 +36,8 @@ wrapWithActivateUvLoop = (func) ->
process.activateUvLoop() process.activateUvLoop()
func.apply this, arguments func.apply this, arguments
process.nextTick = wrapWithActivateUvLoop process.nextTick process.nextTick = wrapWithActivateUvLoop process.nextTick
global.setImmediate = wrapWithActivateUvLoop timers.setImmediate
global.clearImmediate = timers.clearImmediate
if process.type is 'browser' if process.type is 'browser'
# setTimeout needs to update the polling timeout of the event loop, when # setTimeout needs to update the polling timeout of the event loop, when
@ -45,10 +46,3 @@ if process.type is 'browser'
# recalculate the timeout in browser process. # recalculate the timeout in browser process.
global.setTimeout = wrapWithActivateUvLoop timers.setTimeout global.setTimeout = wrapWithActivateUvLoop timers.setTimeout
global.setInterval = wrapWithActivateUvLoop timers.setInterval global.setInterval = wrapWithActivateUvLoop timers.setInterval
global.setImmediate = wrapWithActivateUvLoop timers.setImmediate
global.clearImmediate = wrapWithActivateUvLoop timers.clearImmediate
else
# There are no setImmediate under renderer process by default, so we need to
# manually setup them here.
global.setImmediate = setImmediate
global.clearImmediate = clearImmediate

View file

@ -34,6 +34,7 @@ REFERENCE_MODULE(atom_browser_app);
REFERENCE_MODULE(atom_browser_auto_updater); REFERENCE_MODULE(atom_browser_auto_updater);
REFERENCE_MODULE(atom_browser_content_tracing); REFERENCE_MODULE(atom_browser_content_tracing);
REFERENCE_MODULE(atom_browser_dialog); REFERENCE_MODULE(atom_browser_dialog);
REFERENCE_MODULE(atom_browser_download_item);
REFERENCE_MODULE(atom_browser_menu); REFERENCE_MODULE(atom_browser_menu);
REFERENCE_MODULE(atom_browser_power_monitor); REFERENCE_MODULE(atom_browser_power_monitor);
REFERENCE_MODULE(atom_browser_power_save_blocker); REFERENCE_MODULE(atom_browser_power_save_blocker);

View file

@ -113,6 +113,10 @@ const char kDisableHttpCache[] = "disable-http-cache";
// Register schemes to standard. // Register schemes to standard.
const char kRegisterStandardSchemes[] = "register-standard-schemes"; const char kRegisterStandardSchemes[] = "register-standard-schemes";
// The minimum SSL/TLS version ("tls1", "tls1.1", or "tls1.2") that
// TLS fallback will accept.
const char kSSLVersionFallbackMin[] = "ssl-version-fallback-min";
// The browser process app model ID // The browser process app model ID
const char kAppUserModelId[] = "app-user-model-id"; const char kAppUserModelId[] = "app-user-model-id";

View file

@ -59,6 +59,7 @@ extern const char kPageVisibility[];
extern const char kDisableHttpCache[]; extern const char kDisableHttpCache[];
extern const char kRegisterStandardSchemes[]; extern const char kRegisterStandardSchemes[];
extern const char kSSLVersionFallbackMin[];
extern const char kAppUserModelId[]; extern const char kAppUserModelId[];

View file

@ -98,6 +98,16 @@ void WebFrame::RegisterURLSchemeAsBypassingCsp(const std::string& scheme) {
blink::WebString::fromUTF8(scheme)); blink::WebString::fromUTF8(scheme));
} }
void WebFrame::RegisterURLSchemeAsPrivileged(const std::string& scheme) {
// Register scheme to privileged list (https, wss, data, chrome-extension)
blink::WebString privileged_scheme(blink::WebString::fromUTF8(scheme));
blink::WebSecurityPolicy::registerURLSchemeAsSecure(privileged_scheme);
blink::WebSecurityPolicy::registerURLSchemeAsBypassingContentSecurityPolicy(
privileged_scheme);
blink::WebSecurityPolicy::registerURLSchemeAsAllowingServiceWorkers(
privileged_scheme);
}
mate::ObjectTemplateBuilder WebFrame::GetObjectTemplateBuilder( mate::ObjectTemplateBuilder WebFrame::GetObjectTemplateBuilder(
v8::Isolate* isolate) { v8::Isolate* isolate) {
return mate::ObjectTemplateBuilder(isolate) return mate::ObjectTemplateBuilder(isolate)
@ -116,7 +126,9 @@ mate::ObjectTemplateBuilder WebFrame::GetObjectTemplateBuilder(
.SetMethod("registerUrlSchemeAsSecure", .SetMethod("registerUrlSchemeAsSecure",
&WebFrame::RegisterURLSchemeAsSecure) &WebFrame::RegisterURLSchemeAsSecure)
.SetMethod("registerUrlSchemeAsBypassingCsp", .SetMethod("registerUrlSchemeAsBypassingCsp",
&WebFrame::RegisterURLSchemeAsBypassingCsp); &WebFrame::RegisterURLSchemeAsBypassingCsp)
.SetMethod("registerUrlSchemeAsPrivileged",
&WebFrame::RegisterURLSchemeAsPrivileged);
} }
// static // static

View file

@ -58,6 +58,7 @@ class WebFrame : public mate::Wrappable {
void RegisterURLSchemeAsSecure(const std::string& scheme); void RegisterURLSchemeAsSecure(const std::string& scheme);
void RegisterURLSchemeAsBypassingCsp(const std::string& scheme); void RegisterURLSchemeAsBypassingCsp(const std::string& scheme);
void RegisterURLSchemeAsPrivileged(const std::string& scheme);
// mate::Wrappable: // mate::Wrappable:
virtual mate::ObjectTemplateBuilder GetObjectTemplateBuilder( virtual mate::ObjectTemplateBuilder GetObjectTemplateBuilder(

View file

@ -1,4 +1,3 @@
process = global.process
ipc = require 'ipc' ipc = require 'ipc'
v8Util = process.atomBinding 'v8_util' v8Util = process.atomBinding 'v8_util'
CallbacksRegistry = require 'callbacks-registry' CallbacksRegistry = require 'callbacks-registry'

View file

@ -1,8 +1,7 @@
process = global.process events = require 'events'
events = require 'events' path = require 'path'
path = require 'path' url = require 'url'
url = require 'url' Module = require 'module'
Module = require 'module'
# We modified the original process.argv to let node.js load the # We modified the original process.argv to let node.js load the
# atom-renderer.js, we need to restore it here. # atom-renderer.js, we need to restore it here.

View file

@ -1,4 +1,3 @@
process = global.process
ipc = require 'ipc' ipc = require 'ipc'
remote = require 'remote' remote = require 'remote'
@ -71,7 +70,6 @@ window.open = (url, frameName='', features='') ->
if guestId if guestId
new BrowserWindowProxy(guestId) new BrowserWindowProxy(guestId)
else else
console.error 'It is not allowed to open new window from this WebContents'
null null
# Use the dialog API to implement alert(). # Use the dialog API to implement alert().

View file

@ -16,7 +16,7 @@ WEB_VIEW_EVENTS =
'did-get-redirect-request': ['oldUrl', 'newUrl', 'isMainFrame'] 'did-get-redirect-request': ['oldUrl', 'newUrl', 'isMainFrame']
'dom-ready': [] 'dom-ready': []
'console-message': ['level', 'message', 'line', 'sourceId'] 'console-message': ['level', 'message', 'line', 'sourceId']
'new-window': ['url', 'frameName', 'disposition'] 'new-window': ['url', 'frameName', 'disposition', 'options']
'close': [] 'close': []
'crashed': [] 'crashed': []
'gpu-crashed': [] 'gpu-crashed': []

View file

@ -216,6 +216,7 @@ WebViewImpl::setupWebViewAttributes = ->
@attributes[webViewConstants.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(webViewConstants.ATTRIBUTE_NODEINTEGRATION, this) @attributes[webViewConstants.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(webViewConstants.ATTRIBUTE_NODEINTEGRATION, this)
@attributes[webViewConstants.ATTRIBUTE_PLUGINS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_PLUGINS, this) @attributes[webViewConstants.ATTRIBUTE_PLUGINS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_PLUGINS, this)
@attributes[webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY, this) @attributes[webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY, this)
@attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this)
@attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this) @attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this)
autosizeAttributes = [ autosizeAttributes = [

View file

@ -13,6 +13,7 @@ module.exports =
ATTRIBUTE_NODEINTEGRATION: 'nodeintegration' ATTRIBUTE_NODEINTEGRATION: 'nodeintegration'
ATTRIBUTE_PLUGINS: 'plugins' ATTRIBUTE_PLUGINS: 'plugins'
ATTRIBUTE_DISABLEWEBSECURITY: 'disablewebsecurity' ATTRIBUTE_DISABLEWEBSECURITY: 'disablewebsecurity'
ATTRIBUTE_ALLOWPOPUPS: 'allowpopups'
ATTRIBUTE_PRELOAD: 'preload' ATTRIBUTE_PRELOAD: 'preload'
ATTRIBUTE_USERAGENT: 'useragent' ATTRIBUTE_USERAGENT: 'useragent'

View file

@ -665,7 +665,7 @@ Same as `webContents.loadUrl(url[, options])`.
Same as `webContents.reload`. Same as `webContents.reload`.
### `win.setMenu(menu)` _OS X_ ### `win.setMenu(menu)` _Linux_ _Windows_
* `menu` Menu * `menu` Menu

View file

@ -87,6 +87,11 @@ Sets the `version` of the pepper flash plugin.
Enables net log events to be saved and writes them to `path`. Enables net log events to be saved and writes them to `path`.
## --ssl-version-fallback-min=`version`
Set the minimum SSL/TLS version ("tls1", "tls1.1" or "tls1.2") that TLS
fallback will accept.
## --v=`log_level` ## --v=`log_level`
Gives the default maximal active V-logging level; 0 is the default. Normally Gives the default maximal active V-logging level; 0 is the default. Normally

101
docs/api/download-item.md Normal file
View file

@ -0,0 +1,101 @@
# DownloadItem
`DownloadItem` is an EventEmitter represents a download item in Electron. It
is used in `will-download` event of `Session` module, and allows users to
control the download item.
```javascript
// In the main process.
win.webContents.session.on('will-download', function(event, item, webContents) {
// Set the save path, making Electron not to prompt a save dialog.
item.setSavePath('/tmp/save.pdf');
console.log(item.getMimeType());
console.log(item.getFilename());
console.log(item.getTotalBytes());
item.on('updated', function() {
console.log('Received bytes: ' + item.getReceivedBytes());
});
item.on('done', function(e, state) {
if (state == "completed") {
console.log("Download successfully");
} else {
console.log("Download is cancelled or interrupted that can't be resumed");
}
});
```
## Events
### Event: 'updated'
Emits when the `downloadItem` gets updated.
### Event: 'done'
* `event` Event
* `state` String
* `completed` - The download completed successfully.
* `cancelled` - The download has been cancelled.
* `interrupted` - An error broke the connection with the file server.
Emits when the download is in a terminal state. This includes a completed
download, a cancelled download(via `downloadItem.cancel()`), and interrupted
download that can't be resumed.
## Methods
The `downloadItem` object has the following methods:
### `downloadItem.setSavePath(path)`
* `path` String - Set the save file path of the download item.
The API is only available in session's `will-download` callback function.
If user doesn't set the save path via the API, Electron will use the original
routine to determine the save path(Usually prompts a save dialog).
### `downloadItem.pause()`
Pauses the download.
### `downloadItem.resume()`
Resumes the download that has been paused.
### `downloadItem.cancel()`
Cancels the download operation.
### `downloadItem.getUrl()`
Returns a `String` represents the origin url where the item is downloaded from.
### `downloadItem.getMimeType()`
Returns a `String` represents the mime type.
### `downloadItem.hasUserGesture()`
Returns a `Boolean` indicates whether the download has user gesture.
### `downloadItem.getFilename()`
Returns a `String` represents the file name of the download item.
**Note:** The file name is not always the same as the actual one saved in local
disk. If user changes the file name in a prompted download saving dialog, the
actual name of saved file will be different.
### `downloadItem.getTotalBytes()`
Returns a `Integer` represents the total size in bytes of the download item.
If the size is unknown, it returns 0.
### `downloadItem.getReceivedBytes()`
Returns a `Integer` represents the received bytes of the download item.
### `downloadItem.getContentDisposition()`
Returns a `String` represents the Content-Disposition field from the response
header.

View file

@ -18,11 +18,7 @@ var session = win.webContents.session
### Event: 'will-download' ### Event: 'will-download'
* `event` Event * `event` Event
* `item` Object * `item` [DownloadItem](download-item.md)
* `url` String
* `filename` String
* `mimeType` String
* `hasUserGesture` Boolean
* `webContents` [WebContents](web-contents.md) * `webContents` [WebContents](web-contents.md)
Fired when Electron is about to download `item` in `webContents`. Fired when Electron is about to download `item` in `webContents`.
@ -32,7 +28,7 @@ Calling `event.preventDefault()` will cancel the download.
```javascript ```javascript
session.on('will-download', function(event, item, webContents) { session.on('will-download', function(event, item, webContents) {
event.preventDefault(); event.preventDefault();
require('request')(item.url, function(data) { require('request')(item.getUrl(), function(data) {
require('fs').writeFileSync('/somewhere', data); require('fs').writeFileSync('/somewhere', data);
}); });
}); });

View file

@ -78,6 +78,10 @@ Returns:
* `oldUrl` String * `oldUrl` String
* `newUrl` String * `newUrl` String
* `isMainFrame` Boolean * `isMainFrame` Boolean
* `httpResponseCode` Integer
* `requestMethod` String
* `referrer` String
* `headers` Object
Emitted when a redirect is received while requesting a resource. Emitted when a redirect is received while requesting a resource.
@ -107,6 +111,8 @@ Returns:
* `frameName` String * `frameName` String
* `disposition` String - Can be `default`, `foreground-tab`, `background-tab`, * `disposition` String - Can be `default`, `foreground-tab`, `background-tab`,
`new-window` and `other`. `new-window` and `other`.
* `options` Object - The options which will be used for creating the new
`BrowserWindow`.
Emitted when the page requests to open a new window for a `url`. It could be Emitted when the page requests to open a new window for a `url`. It could be
requested by `window.open` or an external link like `<a target='_blank'>`. requested by `window.open` or an external link like `<a target='_blank'>`.
@ -415,7 +421,7 @@ win.webContents.on("did-finish-load", function() {
win.webContents.printToPDF({}, function(error, data) { win.webContents.printToPDF({}, function(error, data) {
if (error) throw error; if (error) throw error;
fs.writeFile("/tmp/print.pdf", data, function(error) { fs.writeFile("/tmp/print.pdf", data, function(error) {
if (err) if (error)
throw error; throw error;
console.log("Write PDF successfully."); console.log("Write PDF successfully.");
}) })

View file

@ -83,4 +83,11 @@ attackers.
Resources will be loaded from this `scheme` regardless of the current page's Resources will be loaded from this `scheme` regardless of the current page's
Content Security Policy. Content Security Policy.
### `webFrame.registerUrlSchemeAsPrivileged(scheme)`
* `scheme` String
Registers the `scheme` as secure, bypasses content security policy for resources and
allows registering ServiceWorker.
[spellchecker]: https://github.com/atom/node-spellchecker [spellchecker]: https://github.com/atom/node-spellchecker

View file

@ -149,6 +149,14 @@ This value can only be modified before the first navigation, since the session
of an active renderer process cannot change. Subsequent attempts to modify the of an active renderer process cannot change. Subsequent attempts to modify the
value will fail with a DOM exception. value will fail with a DOM exception.
### `allowpopups`
```html
<webview src="https://www.github.com/" allowpopups></webview>
```
If "on", the guest page will be allowed to open new windows.
## Methods ## Methods
The `webview` tag has the following methods: The `webview` tag has the following methods:
@ -496,7 +504,9 @@ Returns:
* `url` String * `url` String
* `frameName` String * `frameName` String
* `disposition` String - Can be `default`, `foreground-tab`, `background-tab`, * `disposition` String - Can be `default`, `foreground-tab`, `background-tab`,
`new-window` and `other` `new-window` and `other`.
* `options` Object - The options which should be used for creating the new
`BrowserWindow`.
Fired when the guest page attempts to open a new browser window. Fired when the guest page attempts to open a new browser window.

View file

@ -8,6 +8,10 @@ The proxy has limited standard functionality implemented to be
compatible with traditional web pages. For full control of the new window compatible with traditional web pages. For full control of the new window
you should create a `BrowserWindow` directly. you should create a `BrowserWindow` directly.
The newly created `BrowserWindow` will inherit parent window's options by
default, to override inherited options you can set them in the `features`
string.
### `window.open(url[, frameName][, features])` ### `window.open(url[, frameName][, features])`
* `url` String * `url` String
@ -16,6 +20,9 @@ you should create a `BrowserWindow` directly.
Creates a new window and returns an instance of `BrowserWindowProxy` class. Creates a new window and returns an instance of `BrowserWindowProxy` class.
The `features` string follows the format of standard browser, but each feature
has to be a field of `BrowserWindow`'s options.
### `window.opener.postMessage(message, targetOrigin)` ### `window.opener.postMessage(message, targetOrigin)`
* `message` String * `message` String

View file

@ -124,6 +124,7 @@ Finally the `index.html` is the web page you want to show:
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8">
<title>Hello World!</title> <title>Hello World!</title>
</head> </head>
<body> <body>

View file

@ -34,6 +34,19 @@ npm install --save-dev electron-rebuild
node ./node_modules/.bin/electron-rebuild node ./node_modules/.bin/electron-rebuild
``` ```
### The npm Way
You can also use `npm` to install modules. The steps are exactly the same with
Node modules, except that you need to setup some environment variables:
```bash
export npm_config_disturl=https://atom.io/download/atom-shell
export npm_config_target=0.33.1
export npm_config_arch=x64
export npm_config_runtime=electron
HOME=~/.electron-gyp npm install module-name
```
### The node-gyp Way ### The node-gyp Way
To build Node modules with headers of Electron, you need to tell `node-gyp` To build Node modules with headers of Electron, you need to tell `node-gyp`
@ -48,15 +61,3 @@ The `HOME=~/.electron-gyp` changes where to find development headers. The
`--target=0.29.1` is version of Electron. The `--dist-url=...` specifies `--target=0.29.1` is version of Electron. The `--dist-url=...` specifies
where to download the headers. The `--arch=x64` says the module is built for where to download the headers. The `--arch=x64` says the module is built for
64bit system. 64bit system.
### The npm Way
You can also use `npm` to install modules. The steps are exactly the same with
Node modules, except that you need to setup some environment variables:
```bash
export npm_config_disturl=https://atom.io/download/atom-shell
export npm_config_target=0.29.1
export npm_config_arch=x64
HOME=~/.electron-gyp npm install module-name
```

View file

@ -71,6 +71,8 @@
'atom/browser/api/atom_api_content_tracing.cc', 'atom/browser/api/atom_api_content_tracing.cc',
'atom/browser/api/atom_api_cookies.cc', 'atom/browser/api/atom_api_cookies.cc',
'atom/browser/api/atom_api_cookies.h', 'atom/browser/api/atom_api_cookies.h',
'atom/browser/api/atom_api_download_item.cc',
'atom/browser/api/atom_api_download_item.h',
'atom/browser/api/atom_api_dialog.cc', 'atom/browser/api/atom_api_dialog.cc',
'atom/browser/api/atom_api_global_shortcut.cc', 'atom/browser/api/atom_api_global_shortcut.cc',
'atom/browser/api/atom_api_global_shortcut.h', 'atom/browser/api/atom_api_global_shortcut.h',
@ -129,6 +131,8 @@
'atom/browser/atom_quota_permission_context.h', 'atom/browser/atom_quota_permission_context.h',
'atom/browser/atom_speech_recognition_manager_delegate.cc', 'atom/browser/atom_speech_recognition_manager_delegate.cc',
'atom/browser/atom_speech_recognition_manager_delegate.h', 'atom/browser/atom_speech_recognition_manager_delegate.h',
'atom/browser/atom_ssl_config_service.cc',
'atom/browser/atom_ssl_config_service.h',
'atom/browser/bridge_task_runner.cc', 'atom/browser/bridge_task_runner.cc',
'atom/browser/bridge_task_runner.h', 'atom/browser/bridge_task_runner.h',
'atom/browser/browser.cc', 'atom/browser/browser.cc',

View file

@ -126,6 +126,8 @@ def copy_chrome_binary(binary):
def copy_license(): def copy_license():
shutil.copy2(os.path.join(CHROMIUM_DIR, '..', 'LICENSES.chromium.html'),
DIST_DIR)
shutil.copy2(os.path.join(SOURCE_ROOT, 'LICENSE'), DIST_DIR) shutil.copy2(os.path.join(SOURCE_ROOT, 'LICENSE'), DIST_DIR)

View file

@ -8,7 +8,7 @@ import sys
BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \ BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \
'http://github-janky-artifacts.s3.amazonaws.com/libchromiumcontent' 'http://github-janky-artifacts.s3.amazonaws.com/libchromiumcontent'
LIBCHROMIUMCONTENT_COMMIT = '8482fe555913dea3bde8a74f754524e2cfb02bc5' LIBCHROMIUMCONTENT_COMMIT = '04523758cda2a96d2454f9056fb1fb9a1c1f95f1'
PLATFORM = { PLATFORM = {
'cygwin': 'win32', 'cygwin': 'win32',

View file

@ -8,7 +8,7 @@ from lib.config import get_target_arch
from lib.util import safe_mkdir, rm_rf, extract_zip, tempdir, download from lib.util import safe_mkdir, rm_rf, extract_zip, tempdir, download
VERSION = 'v0.7.0' VERSION = 'v0.8.0'
SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
FRAMEWORKS_URL = 'http://github.com/atom/atom-shell-frameworks/releases' \ FRAMEWORKS_URL = 'http://github.com/atom/atom-shell-frameworks/releases' \
'/download/' + VERSION '/download/' + VERSION

View file

@ -294,17 +294,7 @@ describe 'browser-window module', ->
w.show() w.show()
w.minimize() w.minimize()
describe 'will-navigate event', -> xdescribe 'beginFrameSubscription method', ->
@timeout 10000
it 'emits when user starts a navigation', (done) ->
url = "file://#{fixtures}/pages/will-navigate.html"
w.webContents.on 'will-navigate', (event, u) ->
event.preventDefault()
assert.equal u, url
done()
w.loadUrl url
describe 'beginFrameSubscription method', ->
it 'subscribes frame updates', (done) -> it 'subscribes frame updates', (done) ->
w.loadUrl "file://#{fixtures}/api/blank.html" w.loadUrl "file://#{fixtures}/api/blank.html"
w.webContents.beginFrameSubscription (data) -> w.webContents.beginFrameSubscription (data) ->

View file

@ -2,6 +2,7 @@ assert = require 'assert'
remote = require 'remote' remote = require 'remote'
http = require 'http' http = require 'http'
path = require 'path' path = require 'path'
fs = require 'fs'
app = remote.require 'app' app = remote.require 'app'
BrowserWindow = remote.require 'browser-window' BrowserWindow = remote.require 'browser-window'
@ -72,3 +73,51 @@ describe 'session module', ->
quotas: ['persistent'], quotas: ['persistent'],
w.webContents.session.clearStorageData options, -> w.webContents.session.clearStorageData options, ->
w.webContents.send 'getcount' w.webContents.send 'getcount'
describe 'DownloadItem', ->
# A 5 MB mock pdf.
mockPDF = new Buffer 1024 * 1024 * 5
contentDisposition = 'inline; filename="mock.pdf"'
ipc = require 'ipc'
downloadFilePath = path.join fixtures, 'mock.pdf'
downloadServer = http.createServer (req, res) ->
res.writeHead 200, {
'Content-Length': mockPDF.length,
'Content-Type': 'application/pdf',
'Content-Disposition': contentDisposition
}
res.end mockPDF
downloadServer.close()
it 'can download successfully', (done) ->
downloadServer.listen 0, '127.0.0.1', ->
{port} = downloadServer.address()
ipc.sendSync 'set-download-option', false
w.loadUrl "#{url}:#{port}"
ipc.once 'download-done', (state, url, mimeType, receivedBytes,
totalBytes, disposition, filename) ->
assert.equal state, 'completed'
assert.equal filename, 'mock.pdf'
assert.equal url, "http://127.0.0.1:#{port}/"
assert.equal mimeType, 'application/pdf'
assert.equal receivedBytes, mockPDF.length
assert.equal totalBytes, mockPDF.length
assert.equal disposition, contentDisposition
assert fs.existsSync downloadFilePath
fs.unlinkSync downloadFilePath
done()
it 'can cancel download', (done) ->
downloadServer.listen 0, '127.0.0.1', ->
{port} = downloadServer.address()
ipc.sendSync 'set-download-option', true
w.loadUrl "#{url}:#{port}/"
ipc.once 'download-done', (state, url, mimeType, receivedBytes,
totalBytes, disposition, filename) ->
assert.equal state, 'cancelled'
assert.equal filename, 'mock.pdf'
assert.equal mimeType, 'application/pdf'
assert.equal receivedBytes, 0
assert.equal totalBytes, mockPDF.length
assert.equal disposition, contentDisposition
done()

View file

@ -8,6 +8,10 @@ describe 'asar package', ->
describe 'node api', -> describe 'node api', ->
describe 'fs.readFileSync', -> describe 'fs.readFileSync', ->
it 'does not leak fd', ->
for i in [1..10000]
fs.readFileSync(path.join(process.resourcesPath, 'atom.asar', 'renderer', 'api', 'lib', 'ipc.js'))
it 'reads a normal file', -> it 'reads a normal file', ->
file1 = path.join fixtures, 'asar', 'a.asar', 'file1' file1 = path.join fixtures, 'asar', 'a.asar', 'file1'
assert.equal fs.readFileSync(file1).toString().trim(), 'file1' assert.equal fs.readFileSync(file1).toString().trim(), 'file1'

View file

@ -23,6 +23,23 @@ describe 'chromium feature', ->
{port} = server.address() {port} = server.address()
$.get "http://127.0.0.1:#{port}" $.get "http://127.0.0.1:#{port}"
describe 'document.hidden', ->
BrowserWindow = remote.require 'browser-window'
ipc = remote.require 'ipc'
url = "file://#{fixtures}/pages/document-hidden.html"
w = null
afterEach ->
w?.destroy()
ipc.removeAllListeners 'hidden'
it 'is set correctly when window is not shown', (done) ->
ipc.once 'hidden', (event, hidden) ->
assert hidden
done()
w = new BrowserWindow(show:false)
w.loadUrl url
describe 'navigator.webkitGetUserMedia', -> describe 'navigator.webkitGetUserMedia', ->
it 'calls its callbacks', (done) -> it 'calls its callbacks', (done) ->
@timeout 5000 @timeout 5000
@ -35,6 +52,8 @@ describe 'chromium feature', ->
assert.notEqual navigator.language, '' assert.notEqual navigator.language, ''
describe 'window.open', -> describe 'window.open', ->
@timeout 10000
it 'returns a BrowserWindowProxy object', -> it 'returns a BrowserWindowProxy object', ->
b = window.open 'about:blank', '', 'show=no' b = window.open 'about:blank', '', 'show=no'
assert.equal b.closed, false assert.equal b.closed, false
@ -50,6 +69,16 @@ describe 'chromium feature', ->
window.addEventListener 'message', listener window.addEventListener 'message', listener
b = window.open "file://#{fixtures}/pages/window-opener-node.html", '', 'node-integration=no,show=no' b = window.open "file://#{fixtures}/pages/window-opener-node.html", '', 'node-integration=no,show=no'
it 'inherit options of parent window', (done) ->
listener = (event) ->
window.removeEventListener 'message', listener
b.close()
size = remote.getCurrentWindow().getSize()
assert.equal event.data, "size: #{size.width} #{size.height}"
done()
window.addEventListener 'message', listener
b = window.open "file://#{fixtures}/pages/window-open-size.html", '', 'show=no'
describe 'window.opener', -> describe 'window.opener', ->
@timeout 10000 @timeout 10000
@ -62,7 +91,7 @@ describe 'chromium feature', ->
ipc.removeAllListeners 'opener' ipc.removeAllListeners 'opener'
it 'is null for main window', (done) -> it 'is null for main window', (done) ->
ipc.on 'opener', (event, opener) -> ipc.once 'opener', (event, opener) ->
assert.equal opener, null assert.equal opener, null
done() done()
BrowserWindow = remote.require 'browser-window' BrowserWindow = remote.require 'browser-window'
@ -70,7 +99,7 @@ describe 'chromium feature', ->
w.loadUrl url w.loadUrl url
it 'is not null for window opened by window.open', (done) -> it 'is not null for window opened by window.open', (done) ->
ipc.on 'opener', (event, opener) -> ipc.once 'opener', (event, opener) ->
b.close() b.close()
done(if opener isnt null then undefined else opener) done(if opener isnt null then undefined else opener)
b = window.open url, '', 'show=no' b = window.open url, '', 'show=no'

View file

@ -0,0 +1,7 @@
<html>
<body>
<script type="text/javascript" charset="utf-8">
require('ipc').send('hidden', document.hidden);
</script>
</body>
</html>

View file

@ -0,0 +1,12 @@
<html>
<body>
<script type="text/javascript" charset="utf-8">
var w = window.open('http://host', '', 'show=no');
if (w == null)
console.log('null');
else
console.log('window');
</script>
</body>
</html>

View file

@ -0,0 +1,8 @@
<html>
<body>
<script type="text/javascript" charset="utf-8">
var size = require('remote').getCurrentWindow().getSize();
window.opener.postMessage('size: ' + size.width + ' ' + size.height, '*')
</script>
</body>
</html>

View file

@ -22,6 +22,12 @@ describe 'third-party module', ->
assert.equal msg, 'ok' assert.equal msg, 'ok'
done() done()
describe 'ffi', ->
it 'does not crash', ->
ffi = require 'ffi'
libm = ffi.Library('libm', ceil: [ 'double', [ 'double' ] ])
assert.equal libm.ceil(1.5), 2
describe 'q', -> describe 'q', ->
Q = require 'q' Q = require 'q'

View file

@ -65,6 +65,7 @@ describe 'node feature', ->
describe 'contexts', -> describe 'contexts', ->
describe 'setTimeout in fs callback', -> describe 'setTimeout in fs callback', ->
return if process.env.TRAVIS is 'true'
it 'does not crash', (done) -> it 'does not crash', (done) ->
fs.readFile __filename, -> fs.readFile __filename, ->
setTimeout done, 0 setTimeout done, 0

View file

@ -5,6 +5,7 @@
"version": "0.1.0", "version": "0.1.0",
"devDependencies": { "devDependencies": {
"basic-auth": "^1.0.0", "basic-auth": "^1.0.0",
"ffi": "2.0.0",
"formidable": "1.0.16", "formidable": "1.0.16",
"graceful-fs": "3.0.5", "graceful-fs": "3.0.5",
"mocha": "2.1.0", "mocha": "2.1.0",

View file

@ -1,6 +1,7 @@
var app = require('app'); var app = require('app');
var ipc = require('ipc'); var ipc = require('ipc');
var dialog = require('dialog'); var dialog = require('dialog');
var path = require('path');
var BrowserWindow = require('browser-window'); var BrowserWindow = require('browser-window');
var window = null; var window = null;
@ -73,4 +74,27 @@ app.on('ready', function() {
}); });
if (chosen == 0) window.destroy(); if (chosen == 0) window.destroy();
}); });
// For session's download test, listen 'will-download' event in browser, and
// reply the result to renderer for verifying
var downloadFilePath = path.join(__dirname, '..', 'fixtures', 'mock.pdf');
require('ipc').on('set-download-option', function(event, need_cancel) {
window.webContents.session.once('will-download',
function(e, item, webContents) {
item.setSavePath(downloadFilePath);
item.on('done', function(e, state) {
window.webContents.send('download-done',
state,
item.getUrl(),
item.getMimeType(),
item.getReceivedBytes(),
item.getTotalBytes(),
item.getContentDisposition(),
item.getFilename());
});
if (need_cancel)
item.cancel();
});
event.returnValue = "done";
});
}); });

View file

@ -86,7 +86,7 @@ describe '<webview> tag', ->
it 'preload script can still use "process" in required modules when nodeintegration is off', (done) -> it 'preload script can still use "process" in required modules when nodeintegration is off', (done) ->
webview.addEventListener 'console-message', (e) -> webview.addEventListener 'console-message', (e) ->
assert.equal e.message, 'object function object' assert.equal e.message, 'object undefined object'
done() done()
webview.setAttribute 'preload', "#{fixtures}/module/preload-node-off.js" webview.setAttribute 'preload', "#{fixtures}/module/preload-node-off.js"
webview.src = "file://#{fixtures}/api/blank.html" webview.src = "file://#{fixtures}/api/blank.html"
@ -201,6 +201,26 @@ describe '<webview> tag', ->
webview.src = "file://#{fixtures}/pages/partition/one.html" webview.src = "file://#{fixtures}/pages/partition/one.html"
document.body.appendChild webview document.body.appendChild webview
describe 'allowpopups attribute', ->
it 'can not open new window when not set', (done) ->
listener = (e) ->
assert.equal e.message, 'null'
webview.removeEventListener 'console-message', listener
done()
webview.addEventListener 'console-message', listener
webview.src = "file://#{fixtures}/pages/window-open-hide.html"
document.body.appendChild webview
it 'can open new window when set', (done) ->
listener = (e) ->
assert.equal e.message, 'window'
webview.removeEventListener 'console-message', listener
done()
webview.addEventListener 'console-message', listener
webview.setAttribute 'allowpopups', 'on'
webview.src = "file://#{fixtures}/pages/window-open-hide.html"
document.body.appendChild webview
describe 'new-window event', -> describe 'new-window event', ->
it 'emits when window.open is called', (done) -> it 'emits when window.open is called', (done) ->
webview.addEventListener 'new-window', (e) -> webview.addEventListener 'new-window', (e) ->

2
vendor/brightray vendored

@ -1 +1 @@
Subproject commit 25f3a9d0a5b73ec170a65f4e2e4c9ad91e23fc8c Subproject commit 8e443520e695674fd26585cfa24a0ec0b6140c27

2
vendor/node vendored

@ -1 +1 @@
Subproject commit aa9c7a2316ba7762f1d04d091585695be3e6be22 Subproject commit ac25693ad1d4c248e69a89147fd3995c3bf6c946