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',
'company_name%': 'GitHub, Inc',
'company_abbr%': 'github',
'version%': '0.33.0',
'version%': '0.33.3',
},
'includes': [
'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 "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/api/atom_api_web_contents.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 atom {
@ -245,11 +233,12 @@ Session::~Session() {
}
void Session::OnDownloadCreated(content::DownloadManager* manager,
content::DownloadItem* item) {
content::DownloadItem* item) {
auto web_contents = item->GetWebContents();
bool prevent_default = Emit("will-download", item,
api::WebContents::CreateFrom(isolate(),
web_contents));
bool prevent_default = Emit(
"will-download",
DownloadItem::Create(isolate(), item),
api::WebContents::CreateFrom(isolate(), web_contents));
if (prevent_default) {
item->Cancel(true);
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
@ -131,7 +158,7 @@ namespace {
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>)>;
WrapWebContentsCallback g_wrap_web_contents;
@ -201,7 +228,7 @@ WebContents::WebContents(v8::Isolate* isolate,
AttachAsUserData(web_contents);
InitWithWebContents(web_contents);
// Save the preferences.
// Save the preferences in C++.
base::DictionaryValue web_preferences;
mate::ConvertFromV8(isolate, options.GetHandle(), &web_preferences);
new WebContentsPreferences(web_contents, &web_preferences);
@ -414,30 +441,6 @@ void WebContents::DidStopLoading() {
void WebContents::DidGetResourceResponseStart(
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",
details.socket_address.IsEmpty(),
details.url,
@ -445,7 +448,7 @@ void WebContents::DidGetResourceResponseStart(
details.http_response_code,
details.method,
details.referrer,
response_headers);
details.headers.get());
}
void WebContents::DidGetRedirectForResourceRequest(
@ -454,7 +457,11 @@ void WebContents::DidGetRedirectForResourceRequest(
Emit("did-get-redirect-request",
details.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(
@ -880,6 +887,12 @@ bool WebContents::IsGuest() const {
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(
v8::Isolate* isolate) {
if (template_.IsEmpty())
@ -935,6 +948,7 @@ mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder(
.SetMethod("setSize", &WebContents::SetSize)
.SetMethod("setAllowTransparency", &WebContents::SetAllowTransparency)
.SetMethod("isGuest", &WebContents::IsGuest)
.SetMethod("getWebPreferences", &WebContents::GetWebPreferences)
.SetMethod("hasServiceWorker", &WebContents::HasServiceWorker)
.SetMethod("unregisterServiceWorker",
&WebContents::UnregisterServiceWorker)
@ -988,7 +1002,7 @@ mate::Handle<WebContents> WebContents::CreateFrom(
// static
mate::Handle<WebContents> WebContents::Create(
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());
return handle;
}

View file

@ -132,6 +132,9 @@ class WebContents : public mate::TrackableObject<WebContents>,
void SetAllowTransparency(bool allow);
bool IsGuest() const;
// Returns the web preferences of current WebContents.
v8::Local<v8::Value> GetWebPreferences(v8::Isolate* isolate);
protected:
explicit WebContents(content::WebContents* web_contents);
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());
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.
window_.reset(NativeWindow::Create(web_contents->managed_web_contents(),
options));

View file

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

View file

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

View file

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

View file

@ -6,6 +6,7 @@
#include <string>
#include "atom/browser/api/atom_api_download_item.h"
#include "atom/browser/atom_browser_context.h"
#include "atom/browser/native_window.h"
#include "atom/browser/ui/file_dialog.h"
@ -73,18 +74,19 @@ void AtomDownloadManagerDelegate::OnDownloadPathGenerated(
if (relay)
window = relay->window.get();
file_dialog::Filters filters;
base::FilePath path;
if (!file_dialog::ShowSaveDialog(window, item->GetURL().spec(), default_path,
filters, &path)) {
return;
if (file_dialog::ShowSaveDialog(window, item->GetURL().spec(), default_path,
file_dialog::Filters(), &path)) {
// 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.
AtomBrowserContext* browser_context = static_cast<AtomBrowserContext*>(
download_manager_->GetBrowserContext());
browser_context->prefs()->SetFilePath(prefs::kDownloadDefaultDirectory,
path.DirName());
// Running the DownloadTargetCallback with an empty FilePath signals that the
// download should be cancelled.
// If user cancels the file save dialog, run the callback with empty FilePath.
callback.Run(path,
content::DownloadItem::TARGET_DISPOSITION_PROMPT,
content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, path);
@ -100,6 +102,25 @@ bool AtomDownloadManagerDelegate::DetermineDownloadTarget(
const content::DownloadTargetCallback& callback) {
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*>(
download_manager_->GetBrowserContext());
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"));
}
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 =
base::Bind(&AtomDownloadManagerDelegate::OnDownloadPathGenerated,
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?
@setAllowTransparency params.allowtransparency
guest.allowPopups = params.allowpopups
# Dispatch events to embedder.
for event in supportedWebViewEvents
do (event) ->

View file

@ -4,6 +4,17 @@ BrowserWindow = require 'browser-window'
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|.
createGuest = (embedder, url, frameName, options) ->
guest = frameToGuest[frameName]
@ -40,11 +51,12 @@ createGuest = (embedder, url, frameName, options) ->
# Routed window.open messages.
ipc.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, args...) ->
[url, frameName, options] = args
event.sender.emit 'new-window', event, url, frameName, 'new-window'
if event.sender.isGuest() or event.defaultPrevented
options = mergeBrowserWindowOptions event.sender, options
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
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) ->
BrowserWindow.fromId(guestId)?.destroy()

View file

@ -68,6 +68,7 @@ NativeWindow::NativeWindow(
const mate::Dictionary& options)
: content::WebContentsObserver(inspectable_web_contents->GetWebContents()),
has_frame_(true),
force_using_draggable_region_(false),
transparent_(false),
enable_larger_than_screen_(false),
is_closed_(false),
@ -159,8 +160,13 @@ void NativeWindow::InitFromOptions(const mate::Dictionary& options) {
// Then show it.
bool show = true;
options.Get(switches::kShow, &show);
if (show)
if (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) {
@ -468,7 +474,7 @@ bool NativeWindow::OnMessageReceived(const IPC::Message& message) {
void NativeWindow::UpdateDraggableRegions(
const std::vector<DraggableRegion>& regions) {
// Draggable region is not supported for non-frameless window.
if (has_frame_)
if (has_frame_ && !force_using_draggable_region_)
return;
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_; }
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) {
has_dialog_attached_ = has_dialog_attached;
}
@ -257,6 +264,9 @@ class NativeWindow : public content::WebContentsObserver,
// Whether window has standard frame.
bool has_frame_;
// Force the window to be aware of draggable regions.
bool force_using_draggable_region_;
// Whether window is transparent.
bool transparent_;

View file

@ -351,21 +351,19 @@ NativeWindowMac::NativeWindowMac(
bool useStandardWindow = true;
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 |
NSMiniaturizableWindowMask | NSResizableWindowMask;
if (!useStandardWindow || transparent() || !has_frame()) {
styleMask |= NSTexturedBackgroundWindowMask;
}
std::string titleBarStyle = "default";
options.Get(switches::kTitleBarStyle, &titleBarStyle);
if (base::mac::IsOSYosemiteOrLater()) {
// New title bar styles are available in Yosemite or newer
if ((titleBarStyle == "hidden") || (titleBarStyle == "hidden-inset")) {
styleMask |= NSFullSizeContentViewWindowMask;
styleMask |= NSUnifiedTitleAndToolbarWindowMask;
}
if ((titleBarStyle == "hidden") || (titleBarStyle == "hidden-inset")) {
styleMask |= NSFullSizeContentViewWindowMask;
styleMask |= NSUnifiedTitleAndToolbarWindowMask;
}
window_.reset([[AtomNSWindow alloc]
@ -393,18 +391,18 @@ NativeWindowMac::NativeWindowMac(
// We will manage window's lifetime ourselves.
[window_ setReleasedWhenClosed:NO];
// Configure title bar look on Yosemite or newer
if (base::mac::IsOSYosemiteOrLater()) {
if ((titleBarStyle == "hidden") || (titleBarStyle == "hidden-inset")) {
[window_ setTitlebarAppearsTransparent:YES];
[window_ setTitleVisibility:NSWindowTitleHidden];
if (titleBarStyle == "hidden-inset") {
NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"];
toolbar.showsBaselineSeparator = NO;
[window_ setToolbar:toolbar];
[toolbar release];
}
// Hide the title bar.
if ((titleBarStyle == "hidden") || (titleBarStyle == "hidden-inset")) {
[window_ setTitlebarAppearsTransparent:YES];
[window_ setTitleVisibility:NSWindowTitleHidden];
if (titleBarStyle == "hidden-inset") {
base::scoped_nsobject<NSToolbar> toolbar(
[[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]);
[toolbar setShowsBaselineSeparator:NO];
[window_ setToolbar:toolbar];
}
// 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.
@ -436,6 +434,11 @@ NativeWindowMac::NativeWindowMac(
[view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
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() {
@ -467,6 +470,8 @@ bool NativeWindowMac::IsFocused() {
}
void NativeWindowMac::Show() {
web_contents()->WasShown();
// This method is supposed to put focus on window, however if the app does not
// have focus then "makeKeyAndOrderFront" will only show the window.
[NSApp activateIgnoringOtherApps:YES];
@ -475,11 +480,13 @@ void NativeWindowMac::Show() {
}
void NativeWindowMac::ShowInactive() {
web_contents()->WasShown();
[window_ orderFrontRegardless];
}
void NativeWindowMac::Hide() {
[window_ orderOut:nil];
web_contents()->WasHidden();
}
bool NativeWindowMac::IsVisible() {

View file

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

View file

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

View file

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

View file

@ -4,10 +4,7 @@
#include "atom/browser/ui/win/notify_icon.h"
#include <shobjidl.h>
#include "atom/browser/ui/win/notify_icon_host.h"
#include "base/md5.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/windows_version.h"
@ -28,31 +25,7 @@ NotifyIcon::NotifyIcon(NotifyIconHost* host,
icon_id_(id),
window_(window),
message_id_(message),
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);
}
menu_model_(NULL) {
NOTIFYICONDATA icon_data;
InitIconData(&icon_data);
icon_data.uFlags |= NIF_MESSAGE;
@ -81,10 +54,6 @@ void NotifyIcon::HandleClickEvent(const gfx::Point& cursor_pos,
icon_id.uID = icon_id_;
icon_id.hWnd = window_;
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 };
Shell_NotifyIconGetRect(&icon_id, &rect);
@ -202,13 +171,6 @@ void NotifyIcon::InitIconData(NOTIFYICONDATA* icon_data) {
icon_data->cbSize = sizeof(NOTIFYICONDATA);
icon_data->hWnd = window_;
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

View file

@ -79,10 +79,6 @@ class NotifyIcon : public TrayIcon {
// The context menu.
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);
};

View file

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

View file

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

View file

@ -115,6 +115,14 @@ bool FillFileInfoWithNode(Archive::FileInfo* info,
Archive::Archive(const base::FilePath& path)
: path_(path),
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) {
}
@ -271,17 +279,7 @@ bool Archive::CopyFileOut(const base::FilePath& path, base::FilePath* out) {
}
int Archive::GetFD() const {
if (!file_.IsValid())
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
return fd_;
}
} // namespace asar

View file

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

View file

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

View file

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

View file

@ -1,8 +1,7 @@
process = global.process
fs = require 'fs'
path = require 'path'
timers = require 'timers'
Module = require 'module'
fs = require 'fs'
path = require 'path'
timers = require 'timers'
Module = require 'module'
process.atomBinding = (name) ->
try
@ -37,6 +36,8 @@ wrapWithActivateUvLoop = (func) ->
process.activateUvLoop()
func.apply this, arguments
process.nextTick = wrapWithActivateUvLoop process.nextTick
global.setImmediate = wrapWithActivateUvLoop timers.setImmediate
global.clearImmediate = timers.clearImmediate
if process.type is 'browser'
# 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.
global.setTimeout = wrapWithActivateUvLoop timers.setTimeout
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_content_tracing);
REFERENCE_MODULE(atom_browser_dialog);
REFERENCE_MODULE(atom_browser_download_item);
REFERENCE_MODULE(atom_browser_menu);
REFERENCE_MODULE(atom_browser_power_monitor);
REFERENCE_MODULE(atom_browser_power_save_blocker);

View file

@ -113,6 +113,10 @@ const char kDisableHttpCache[] = "disable-http-cache";
// Register schemes to standard.
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
const char kAppUserModelId[] = "app-user-model-id";

View file

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

View file

@ -98,6 +98,16 @@ void WebFrame::RegisterURLSchemeAsBypassingCsp(const std::string& 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(
v8::Isolate* isolate) {
return mate::ObjectTemplateBuilder(isolate)
@ -116,7 +126,9 @@ mate::ObjectTemplateBuilder WebFrame::GetObjectTemplateBuilder(
.SetMethod("registerUrlSchemeAsSecure",
&WebFrame::RegisterURLSchemeAsSecure)
.SetMethod("registerUrlSchemeAsBypassingCsp",
&WebFrame::RegisterURLSchemeAsBypassingCsp);
&WebFrame::RegisterURLSchemeAsBypassingCsp)
.SetMethod("registerUrlSchemeAsPrivileged",
&WebFrame::RegisterURLSchemeAsPrivileged);
}
// static

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -216,6 +216,7 @@ WebViewImpl::setupWebViewAttributes = ->
@attributes[webViewConstants.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(webViewConstants.ATTRIBUTE_NODEINTEGRATION, this)
@attributes[webViewConstants.ATTRIBUTE_PLUGINS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_PLUGINS, 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)
autosizeAttributes = [

View file

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

View file

@ -665,7 +665,7 @@ Same as `webContents.loadUrl(url[, options])`.
Same as `webContents.reload`.
### `win.setMenu(menu)` _OS X_
### `win.setMenu(menu)` _Linux_ _Windows_
* `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`.
## --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`
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` Event
* `item` Object
* `url` String
* `filename` String
* `mimeType` String
* `hasUserGesture` Boolean
* `item` [DownloadItem](download-item.md)
* `webContents` [WebContents](web-contents.md)
Fired when Electron is about to download `item` in `webContents`.
@ -32,7 +28,7 @@ Calling `event.preventDefault()` will cancel the download.
```javascript
session.on('will-download', function(event, item, webContents) {
event.preventDefault();
require('request')(item.url, function(data) {
require('request')(item.getUrl(), function(data) {
require('fs').writeFileSync('/somewhere', data);
});
});

View file

@ -78,6 +78,10 @@ Returns:
* `oldUrl` String
* `newUrl` String
* `isMainFrame` Boolean
* `httpResponseCode` Integer
* `requestMethod` String
* `referrer` String
* `headers` Object
Emitted when a redirect is received while requesting a resource.
@ -107,6 +111,8 @@ Returns:
* `frameName` String
* `disposition` String - Can be `default`, `foreground-tab`, `background-tab`,
`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
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) {
if (error) throw error;
fs.writeFile("/tmp/print.pdf", data, function(error) {
if (err)
if (error)
throw error;
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
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

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
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
The `webview` tag has the following methods:
@ -496,7 +504,9 @@ Returns:
* `url` String
* `frameName` String
* `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.

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
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])`
* `url` String
@ -16,6 +20,9 @@ you should create a `BrowserWindow` directly.
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)`
* `message` String

View file

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

View file

@ -34,6 +34,19 @@ npm install --save-dev 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
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
where to download the headers. The `--arch=x64` says the module is built for
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_cookies.cc',
'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_global_shortcut.cc',
'atom/browser/api/atom_api_global_shortcut.h',
@ -129,6 +131,8 @@
'atom/browser/atom_quota_permission_context.h',
'atom/browser/atom_speech_recognition_manager_delegate.cc',
'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.h',
'atom/browser/browser.cc',

View file

@ -126,6 +126,8 @@ def copy_chrome_binary(binary):
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)

View file

@ -8,7 +8,7 @@ import sys
BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \
'http://github-janky-artifacts.s3.amazonaws.com/libchromiumcontent'
LIBCHROMIUMCONTENT_COMMIT = '8482fe555913dea3bde8a74f754524e2cfb02bc5'
LIBCHROMIUMCONTENT_COMMIT = '04523758cda2a96d2454f9056fb1fb9a1c1f95f1'
PLATFORM = {
'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
VERSION = 'v0.7.0'
VERSION = 'v0.8.0'
SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
FRAMEWORKS_URL = 'http://github.com/atom/atom-shell-frameworks/releases' \
'/download/' + VERSION

View file

@ -294,17 +294,7 @@ describe 'browser-window module', ->
w.show()
w.minimize()
describe 'will-navigate event', ->
@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', ->
xdescribe 'beginFrameSubscription method', ->
it 'subscribes frame updates', (done) ->
w.loadUrl "file://#{fixtures}/api/blank.html"
w.webContents.beginFrameSubscription (data) ->

View file

@ -2,6 +2,7 @@ assert = require 'assert'
remote = require 'remote'
http = require 'http'
path = require 'path'
fs = require 'fs'
app = remote.require 'app'
BrowserWindow = remote.require 'browser-window'
@ -72,3 +73,51 @@ describe 'session module', ->
quotas: ['persistent'],
w.webContents.session.clearStorageData options, ->
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 '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', ->
file1 = path.join fixtures, 'asar', 'a.asar', 'file1'
assert.equal fs.readFileSync(file1).toString().trim(), 'file1'

View file

@ -23,6 +23,23 @@ describe 'chromium feature', ->
{port} = server.address()
$.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', ->
it 'calls its callbacks', (done) ->
@timeout 5000
@ -35,6 +52,8 @@ describe 'chromium feature', ->
assert.notEqual navigator.language, ''
describe 'window.open', ->
@timeout 10000
it 'returns a BrowserWindowProxy object', ->
b = window.open 'about:blank', '', 'show=no'
assert.equal b.closed, false
@ -50,6 +69,16 @@ describe 'chromium feature', ->
window.addEventListener 'message', listener
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', ->
@timeout 10000
@ -62,7 +91,7 @@ describe 'chromium feature', ->
ipc.removeAllListeners 'opener'
it 'is null for main window', (done) ->
ipc.on 'opener', (event, opener) ->
ipc.once 'opener', (event, opener) ->
assert.equal opener, null
done()
BrowserWindow = remote.require 'browser-window'
@ -70,7 +99,7 @@ describe 'chromium feature', ->
w.loadUrl url
it 'is not null for window opened by window.open', (done) ->
ipc.on 'opener', (event, opener) ->
ipc.once 'opener', (event, opener) ->
b.close()
done(if opener isnt null then undefined else opener)
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'
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', ->
Q = require 'q'

View file

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

View file

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

View file

@ -1,6 +1,7 @@
var app = require('app');
var ipc = require('ipc');
var dialog = require('dialog');
var path = require('path');
var BrowserWindow = require('browser-window');
var window = null;
@ -73,4 +74,27 @@ app.on('ready', function() {
});
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) ->
webview.addEventListener 'console-message', (e) ->
assert.equal e.message, 'object function object'
assert.equal e.message, 'object undefined object'
done()
webview.setAttribute 'preload', "#{fixtures}/module/preload-node-off.js"
webview.src = "file://#{fixtures}/api/blank.html"
@ -201,6 +201,26 @@ describe '<webview> tag', ->
webview.src = "file://#{fixtures}/pages/partition/one.html"
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', ->
it 'emits when window.open is called', (done) ->
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