Compare commits

..

22 Commits

Author SHA1 Message Date
Kevin Sawicki
de625bfb65 Bump v1.4.12 2016-12-09 21:43:27 -08:00
Kevin Sawicki
c751d42d1a Merge pull request #8176 from electron/upgrade-libcc-for-ct-timebomb-patch
Upgrade libcc for CT timebomb patch
2016-12-09 19:24:59 -08:00
Kevin Sawicki
198f5f237f Upgrade libcc for CT timebomb patch 2016-12-09 10:47:09 -08:00
Kevin Sawicki
dc5b27069a Merge pull request #8107 from deepak1556/clear_auth_cache_patch
session: add api to clear http auth cache
2016-12-09 10:08:02 -08:00
Kevin Sawicki
6a829e0179 Throw error when options object is invalid 2016-12-09 10:07:04 -08:00
deepak1556
4085ba309a fix spec 2016-12-09 09:56:16 -08:00
deepak1556
975d677f55 add docs 2016-12-09 09:56:16 -08:00
deepak1556
21be9a3309 add spec 2016-12-09 09:55:55 -08:00
deepak1556
7456b9ae17 net: add api to set request flags for testing 2016-12-09 09:54:30 -08:00
deepak1556
35349643af session: api to clear auth cache 2016-12-09 09:54:30 -08:00
Kevin Sawicki
952e3bac2c Merge pull request #8061 from deepak1556/resume_download_api
session: api to resume canceled downloads from previous session
2016-12-09 09:39:14 -08:00
Kevin Sawicki
d705f4cbac Fix issue where actual/expected was same variable 2016-12-09 09:37:46 -08:00
deepak1556
5d94221c61 fix code style 2016-12-09 09:37:46 -08:00
deepak1556
f124732431 add spec 2016-12-09 09:37:46 -08:00
deepak1556
d944219b28 add docs 2016-12-09 09:37:46 -08:00
deepak1556
86961d0f44 session: add api to create interrupted downloads 2016-12-09 09:37:46 -08:00
Kevin Sawicki
54d27a390b Merge pull request #8155 from salomvary/explain-macos-modal-sheet
Clarify modal windows being sheets on macOS
2016-12-07 10:29:42 -08:00
Kevin Sawicki
ba44dca34a Merge pull request #8158 from jdfwarrior/master
Add detail for the browserWindow argument in dialog docs
2016-12-07 10:28:34 -08:00
Kevin Sawicki
61d91579df Merge pull request #8157 from electron/post-data-example
Add simple postData example to docs
2016-12-07 10:28:00 -08:00
David Ferguson
32ae3a52b8 add detail for the browserWindow argument in dialog docs 2016-12-06 22:23:14 -06:00
Kevin Sawicki
e198b6945c Add simple postData example 2016-12-06 16:49:22 -08:00
Márton Salomváry
9adb232d99 Clarify modal windows being sheets on macOS 2016-12-06 22:47:26 +01:00
23 changed files with 479 additions and 27 deletions

View File

@@ -146,6 +146,10 @@ const GURL& DownloadItem::GetURL() const {
return download_item_->GetURL();
}
const std::vector<GURL>& DownloadItem::GetURLChain() const {
return download_item_->GetUrlChain();
}
content::DownloadItem::DownloadState DownloadItem::GetState() const {
return download_item_->GetState();
}
@@ -162,6 +166,18 @@ base::FilePath DownloadItem::GetSavePath() const {
return save_path_;
}
std::string DownloadItem::GetLastModifiedTime() const {
return download_item_->GetLastModifiedTime();
}
std::string DownloadItem::GetETag() const {
return download_item_->GetETag();
}
double DownloadItem::GetStartTime() const {
return download_item_->GetStartTime().ToDoubleT();
}
// static
void DownloadItem::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype) {
@@ -180,10 +196,14 @@ void DownloadItem::BuildPrototype(v8::Isolate* isolate,
.SetMethod("getFilename", &DownloadItem::GetFilename)
.SetMethod("getContentDisposition", &DownloadItem::GetContentDisposition)
.SetMethod("getURL", &DownloadItem::GetURL)
.SetMethod("getURLChain", &DownloadItem::GetURLChain)
.SetMethod("getState", &DownloadItem::GetState)
.SetMethod("isDone", &DownloadItem::IsDone)
.SetMethod("setSavePath", &DownloadItem::SetSavePath)
.SetMethod("getSavePath", &DownloadItem::GetSavePath);
.SetMethod("getSavePath", &DownloadItem::GetSavePath)
.SetMethod("getLastModifiedTime", &DownloadItem::GetLastModifiedTime)
.SetMethod("getETag", &DownloadItem::GetETag)
.SetMethod("getStartTime", &DownloadItem::GetStartTime);
}
// static

View File

@@ -6,6 +6,7 @@
#define ATOM_BROWSER_API_ATOM_API_DOWNLOAD_ITEM_H_
#include <string>
#include <vector>
#include "atom/browser/api/trackable_object.h"
#include "base/files/file_path.h"
@@ -38,10 +39,14 @@ class DownloadItem : public mate::TrackableObject<DownloadItem>,
std::string GetFilename() const;
std::string GetContentDisposition() const;
const GURL& GetURL() const;
const std::vector<GURL>& GetURLChain() const;
content::DownloadItem::DownloadState GetState() const;
bool IsDone() const;
void SetSavePath(const base::FilePath& path);
base::FilePath GetSavePath() const;
std::string GetLastModifiedTime() const;
std::string GetETag() const;
double GetStartTime() const;
protected:
DownloadItem(v8::Isolate* isolate, content::DownloadItem* download_item);

View File

@@ -34,6 +34,7 @@
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_manager_delegate.h"
#include "content/public/browser/storage_partition.h"
#include "native_mate/dictionary.h"
#include "native_mate/object_template_builder.h"
@@ -61,6 +62,15 @@ struct ClearStorageDataOptions {
uint32_t quota_types = StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL;
};
struct ClearAuthCacheOptions {
std::string type;
GURL origin;
std::string realm;
base::string16 username;
base::string16 password;
net::HttpAuth::Scheme auth_scheme;
};
uint32_t GetStorageMask(const std::vector<std::string>& storage_types) {
uint32_t storage_mask = 0;
for (const auto& it : storage_types) {
@@ -99,6 +109,18 @@ uint32_t GetQuotaMask(const std::vector<std::string>& quota_types) {
return quota_mask;
}
net::HttpAuth::Scheme GetAuthSchemeFromString(const std::string& scheme) {
if (scheme == "basic")
return net::HttpAuth::AUTH_SCHEME_BASIC;
if (scheme == "digest")
return net::HttpAuth::AUTH_SCHEME_DIGEST;
if (scheme == "ntlm")
return net::HttpAuth::AUTH_SCHEME_NTLM;
if (scheme == "negotiate")
return net::HttpAuth::AUTH_SCHEME_NEGOTIATE;
return net::HttpAuth::AUTH_SCHEME_MAX;
}
void SetUserAgentInIO(scoped_refptr<net::URLRequestContextGetter> getter,
const std::string& accept_lang,
const std::string& user_agent) {
@@ -130,7 +152,27 @@ struct Converter<ClearStorageDataOptions> {
}
};
template<>
template <>
struct Converter<ClearAuthCacheOptions> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
ClearAuthCacheOptions* out) {
mate::Dictionary options;
if (!ConvertFromV8(isolate, val, &options))
return false;
options.Get("type", &out->type);
options.Get("origin", &out->origin);
options.Get("realm", &out->realm);
options.Get("username", &out->username);
options.Get("password", &out->password);
std::string scheme;
if (options.Get("scheme", &scheme))
out->auth_scheme = GetAuthSchemeFromString(scheme);
return true;
}
};
template <>
struct Converter<net::ProxyConfig> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
@@ -313,6 +355,33 @@ void ClearHostResolverCacheInIO(
}
}
void ClearAuthCacheInIO(
const scoped_refptr<net::URLRequestContextGetter>& context_getter,
const ClearAuthCacheOptions& options,
const base::Closure& callback) {
auto request_context = context_getter->GetURLRequestContext();
auto network_session =
request_context->http_transaction_factory()->GetSession();
if (network_session) {
if (options.type == "password") {
auto auth_cache = network_session->http_auth_cache();
if (!options.origin.is_empty()) {
auth_cache->Remove(
options.origin, options.realm, options.auth_scheme,
net::AuthCredentials(options.username, options.password));
} else {
auth_cache->Clear();
}
} else if (options.type == "clientCertificate") {
auto client_auth_cache = network_session->ssl_client_auth_cache();
client_auth_cache->Remove(net::HostPortPair::FromURL(options.origin));
}
network_session->CloseAllConnections();
}
if (!callback.is_null())
RunCallbackInUI(callback);
}
void AllowNTLMCredentialsForDomainsInIO(
const scoped_refptr<net::URLRequestContextGetter>& context_getter,
const std::string& domains) {
@@ -331,6 +400,25 @@ void OnClearStorageDataDone(const base::Closure& callback) {
callback.Run();
}
void DownloadIdCallback(content::DownloadManager* download_manager,
const base::FilePath& path,
const std::vector<GURL>& url_chain,
const std::string& mime_type,
int64_t offset,
int64_t length,
const std::string& last_modified,
const std::string& etag,
const base::Time& start_time,
uint32_t id) {
download_manager->CreateDownloadItem(
base::GenerateGUID(), id, path, path, url_chain, GURL(), GURL(), GURL(),
GURL(), mime_type, mime_type, start_time, base::Time(), etag,
last_modified, offset, length, std::string(),
content::DownloadItem::INTERRUPTED,
content::DownloadDangerType::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT, false);
}
} // namespace
Session::Session(v8::Isolate* isolate, AtomBrowserContext* browser_context)
@@ -357,10 +445,10 @@ void Session::OnDownloadCreated(content::DownloadManager* manager,
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
bool prevent_default = Emit(
"will-download",
DownloadItem::Create(isolate(), item),
item->GetWebContents());
auto handle = DownloadItem::Create(isolate(), item);
if (item->GetState() == content::DownloadItem::INTERRUPTED)
handle->SetSavePath(item->GetTargetFilePath());
bool prevent_default = Emit("will-download", handle, item->GetWebContents());
if (prevent_default) {
item->Cancel(true);
item->Remove();
@@ -481,6 +569,22 @@ void Session::ClearHostResolverCache(mate::Arguments* args) {
callback));
}
void Session::ClearAuthCache(mate::Arguments* args) {
ClearAuthCacheOptions options;
if (!args->GetNext(&options)) {
args->ThrowError("Must specify options object");
return;
}
base::Closure callback;
args->GetNext(&callback);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&ClearAuthCacheInIO,
make_scoped_refptr(browser_context_->GetRequestContext()),
options, callback));
}
void Session::AllowNTLMCredentialsForDomains(const std::string& domains) {
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&AllowNTLMCredentialsForDomainsInIO,
@@ -520,6 +624,37 @@ void Session::GetBlobData(
callback));
}
void Session::CreateInterruptedDownload(const mate::Dictionary& options) {
int64_t offset = 0, length = 0;
double start_time = 0.0;
std::string mime_type, last_modified, etag;
base::FilePath path;
std::vector<GURL> url_chain;
options.Get("path", &path);
options.Get("urlChain", &url_chain);
options.Get("mimeType", &mime_type);
options.Get("offset", &offset);
options.Get("length", &length);
options.Get("lastModified", &last_modified);
options.Get("eTag", &etag);
options.Get("startTime", &start_time);
if (path.empty() || url_chain.empty() || length == 0) {
isolate()->ThrowException(v8::Exception::Error(mate::StringToV8(
isolate(), "Must pass non-empty path, urlChain and length.")));
return;
}
if (offset >= length) {
isolate()->ThrowException(v8::Exception::Error(mate::StringToV8(
isolate(), "Must pass an offset value less than length.")));
return;
}
auto download_manager =
content::BrowserContext::GetDownloadManager(browser_context());
download_manager->GetDelegate()->GetNextId(base::Bind(
&DownloadIdCallback, download_manager, path, url_chain, mime_type, offset,
length, last_modified, etag, base::Time::FromDoubleT(start_time)));
}
v8::Local<v8::Value> Session::Cookies(v8::Isolate* isolate) {
if (cookies_.IsEmpty()) {
auto handle = Cookies::Create(isolate, browser_context());
@@ -598,11 +733,14 @@ void Session::BuildPrototype(v8::Isolate* isolate,
.SetMethod("setPermissionRequestHandler",
&Session::SetPermissionRequestHandler)
.SetMethod("clearHostResolverCache", &Session::ClearHostResolverCache)
.SetMethod("clearAuthCache", &Session::ClearAuthCache)
.SetMethod("allowNTLMCredentialsForDomains",
&Session::AllowNTLMCredentialsForDomains)
.SetMethod("setUserAgent", &Session::SetUserAgent)
.SetMethod("getUserAgent", &Session::GetUserAgent)
.SetMethod("getBlobData", &Session::GetBlobData)
.SetMethod("createInterruptedDownload",
&Session::CreateInterruptedDownload)
.SetProperty("cookies", &Session::Cookies)
.SetProperty("protocol", &Session::Protocol)
.SetProperty("webRequest", &Session::WebRequest);

View File

@@ -74,11 +74,13 @@ class Session: public mate::TrackableObject<Session>,
void SetPermissionRequestHandler(v8::Local<v8::Value> val,
mate::Arguments* args);
void ClearHostResolverCache(mate::Arguments* args);
void ClearAuthCache(mate::Arguments* args);
void AllowNTLMCredentialsForDomains(const std::string& domains);
void SetUserAgent(const std::string& user_agent, mate::Arguments* args);
std::string GetUserAgent();
void GetBlobData(const std::string& uuid,
const AtomBlobReader::CompletionCallback& callback);
void CreateInterruptedDownload(const mate::Dictionary& options);
v8::Local<v8::Value> Cookies(v8::Isolate* isolate);
v8::Local<v8::Value> Protocol(v8::Isolate* isolate);
v8::Local<v8::Value> WebRequest(v8::Isolate* isolate);

View File

@@ -176,6 +176,7 @@ void URLRequest::BuildPrototype(v8::Isolate* isolate,
.SetMethod("setExtraHeader", &URLRequest::SetExtraHeader)
.SetMethod("removeExtraHeader", &URLRequest::RemoveExtraHeader)
.SetMethod("setChunkedUpload", &URLRequest::SetChunkedUpload)
.SetMethod("_setLoadFlags", &URLRequest::SetLoadFlags)
.SetProperty("notStarted", &URLRequest::NotStarted)
.SetProperty("finished", &URLRequest::Finished)
// Response APi
@@ -292,6 +293,18 @@ void URLRequest::SetChunkedUpload(bool is_chunked_upload) {
}
}
void URLRequest::SetLoadFlags(int flags) {
// State must be equal to not started.
if (!request_state_.NotStarted()) {
// Cannot change load flags after start.
return;
}
DCHECK(atom_request_);
if (atom_request_) {
atom_request_->SetLoadFlags(flags);
}
}
void URLRequest::OnAuthenticationRequired(
scoped_refptr<const net::AuthChallengeInfo> auth_info) {
if (request_state_.Canceled() || request_state_.Closed()) {

View File

@@ -173,6 +173,7 @@ class URLRequest : public mate::EventEmitter<URLRequest> {
bool SetExtraHeader(const std::string& name, const std::string& value);
void RemoveExtraHeader(const std::string& name);
void SetChunkedUpload(bool is_chunked_upload);
void SetLoadFlags(int flags);
int StatusCode() const;
std::string StatusMessage() const;

View File

@@ -177,6 +177,13 @@ void AtomURLRequest::PassLoginInformation(
}
}
void AtomURLRequest::SetLoadFlags(int flags) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&AtomURLRequest::DoSetLoadFlags, this, flags));
}
void AtomURLRequest::DoWriteBuffer(
scoped_refptr<const net::IOBufferWithSize> buffer,
bool is_last) {
@@ -244,6 +251,7 @@ void AtomURLRequest::DoSetExtraHeader(const std::string& name,
}
request_->SetExtraRequestHeaderByName(name, value, true);
}
void AtomURLRequest::DoRemoveExtraHeader(const std::string& name) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!request_) {
@@ -278,6 +286,14 @@ void AtomURLRequest::DoCancelWithError(const std::string& error,
isRequestError));
}
void AtomURLRequest::DoSetLoadFlags(int flags) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!request_) {
return;
}
request_->SetLoadFlags(request_->load_flags() | flags);
}
void AtomURLRequest::OnAuthRequired(net::URLRequest* request,
net::AuthChallengeInfo* auth_info) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);

View File

@@ -40,6 +40,7 @@ class AtomURLRequest : public base::RefCountedThreadSafe<AtomURLRequest>,
void RemoveExtraHeader(const std::string& name) const;
void PassLoginInformation(const base::string16& username,
const base::string16& password) const;
void SetLoadFlags(int flags) const;
protected:
// Overrides of net::URLRequest::Delegate
@@ -71,6 +72,7 @@ class AtomURLRequest : public base::RefCountedThreadSafe<AtomURLRequest>,
const base::string16& password) const;
void DoCancelAuth() const;
void DoCancelWithError(const std::string& error, bool isRequestError);
void DoSetLoadFlags(int flags) const;
void ReadResponse();
bool CopyAndPostBuffer(int bytes_read);

View File

@@ -17,9 +17,9 @@
<key>CFBundleIconFile</key>
<string>electron.icns</string>
<key>CFBundleVersion</key>
<string>1.4.11</string>
<string>1.4.12</string>
<key>CFBundleShortVersionString</key>
<string>1.4.11</string>
<string>1.4.12</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.developer-tools</string>
<key>LSMinimumSystemVersion</key>

View File

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

View File

@@ -7,7 +7,7 @@
#define ATOM_MAJOR_VERSION 1
#define ATOM_MINOR_VERSION 4
#define ATOM_PATCH_VERSION 11
#define ATOM_PATCH_VERSION 12
#define ATOM_VERSION_IS_RELEASE 1

View File

@@ -100,6 +100,7 @@ child.once('ready-to-show', () => {
### Platform notices
* On macOS modal windows will be displayed as sheets attached to the parent window.
* On macOS the child windows will keep the relative position to parent window
when parent window moves, while on Windows and Linux child windows will not
move.
@@ -1004,6 +1005,19 @@ let url = require('url').format({
win.loadURL(url)
```
You can load a URL using a `POST` request with URL-encoded data by doing
the following:
```javascript
win.loadURL('http://localhost:8000/post', {
postData: [{
type: 'rawData',
bytes: Buffer.from('hello=world')
}],
extraHeaders: 'Content-Type: application/x-www-form-urlencoded'
})
```
#### `win.reload()`
Same as `webContents.reload`.

View File

@@ -41,6 +41,8 @@ The `dialog` module has the following methods:
Returns `String[]`, an array of file paths chosen by the user,
if the callback is provided it returns `undefined`.
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
The `filters` specifies an array of file types that can be displayed or
selected when you want to limit the user to a specific type. For example:
@@ -82,6 +84,8 @@ shown.
Returns `String`, the path of the file chosen by the user,
if a callback is provided it returns `undefined`.
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
The `filters` specifies an array of file types that can be displayed, see
`dialog.showOpenDialog` for an example.
@@ -122,6 +126,8 @@ it returns undefined.
Shows a message box, it will block the process until the message box is closed.
It returns the index of the clicked button.
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
If a `callback` is passed, the API call will be asynchronous and the result
will be passed via `callback(response)`.

View File

@@ -146,3 +146,23 @@ header.
#### `downloadItem.getState()`
Returns `String` - The current state. Can be `progressing`, `completed`, `cancelled` or `interrupted`.
**Note:** The following methods are useful specifically to resume a
`cancelled` item when session is restarted.
#### `downloadItem.getURLChain()`
Returns `String[]` - The complete url chain of the item including any redirects.
#### `downloadItem.getLastModifiedTime()`
Returns `String` - Last-Modified header value.
#### `downloadItem.getETag()`
Returns `String` - ETag header value.
#### `downloadItem.getStartTime()`
Returns `Double` - Number of seconds since the UNIX epoch when the download was
started.

View File

@@ -344,6 +344,32 @@ Returns `String` - The user agent for this session.
Returns `Blob` - The blob data associated with the `identifier`.
#### `ses.createInterruptedDownload(options)`
* `options` Object
* `path` String - Absolute path of the download.
* `urlChain` String[] - Complete URL chain for the download.
* `mimeType` String (optional)
* `offset` Integer - Start range for the download.
* `length` Integer - Total length of the download.
* `lastModified` String - Last-Modified header value.
* `eTag` String - ETag header value.
* `startTime` Double (optional) - Time when download was started in
number of seconds since UNIX epoch.
Allows resuming `cancelled` or `interrupted` downloads from previous `Session`.
The API will generate a [DownloadItem](download-item.md) that can be accessed with the [will-download](#event-will-download)
event. The [DownloadItem](download-item.md) will not have any `WebContents` associated with it and
the initial state will be `interrupted`. The download will start only when the
`resume` API is called on the [DownloadItem](download-item.md).
#### `ses.clearAuthCache(options[, callback])`
* `options` ([RemovePassword](structures/remove-password.md) | [RemoveClientCertificate](structures/remove-client-certificate.md))
* `callback` Function (optional) - Called when operation is done
Clears the sessions HTTP authentication cache.
### Instance Properties
The following properties are available on instances of `Session`:

View File

@@ -0,0 +1,5 @@
# RemoveClientCertificate Object
* `type` String - `clientCertificate`.
* `origin` String - Origin of the server whose associated client certificate
must be removed from the cache.

View File

@@ -0,0 +1,15 @@
# RemovePassword Object
* `type` String - `password`.
* `origin` String (optional) - When provided, the authentication info
related to the origin will only be removed otherwise the entire cache
will be cleared.
* `scheme` String (optional) - Scheme of the authentication.
Can be `basic`, `digest`, `ntlm`, `negotiate`. Must be provided if
removing by `origin`.
* `realm` String (optional) - Realm of the authentication. Must be provided if
removing by `origin`.
* `username` String (optional) - Credentials of the authentication. Must be
provided if removing by `origin`.
* `password` String (optional) - Credentials of the authentication. Must be
provided if removing by `origin`.

View File

@@ -4,7 +4,7 @@
'product_name%': 'Electron',
'company_name%': 'GitHub, Inc',
'company_abbr%': 'github',
'version%': '1.4.11',
'version%': '1.4.12',
'js2c_input_dir': '<(SHARED_INTERMEDIATE_DIR)/js2c',
},
'includes': [

View File

@@ -1,6 +1,6 @@
{
"name": "electron",
"version": "1.4.11",
"version": "1.4.12",
"devDependencies": {
"asar": "^0.11.0",
"browserify": "^13.1.0",

View File

@@ -9,7 +9,7 @@ import sys
BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \
'https://s3.amazonaws.com/github-janky-artifacts/libchromiumcontent'
LIBCHROMIUMCONTENT_COMMIT = os.getenv('LIBCHROMIUMCONTENT_COMMIT') or \
'd59491fbd40f98ae72dfa575fd2b477c061ce613'
'2c8173b64b7fbc50e7190a6982e6db6b3eda0582'
PLATFORM = {
'cygwin': 'win32',

View File

@@ -3,10 +3,12 @@ const http = require('http')
const https = require('https')
const path = require('path')
const fs = require('fs')
const send = require('send')
const auth = require('basic-auth')
const {closeWindow} = require('./window-helpers')
const {ipcRenderer, remote} = require('electron')
const {ipcMain, session, BrowserWindow} = remote
const {ipcMain, session, BrowserWindow, net} = remote
describe('session module', function () {
var fixtures = path.resolve(__dirname, 'fixtures')
@@ -288,7 +290,9 @@ describe('session module', function () {
res.end(mockPDF)
downloadServer.close()
})
var assertDownload = function (event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename, port, savePath) {
var assertDownload = function (event, state, url, mimeType,
receivedBytes, totalBytes, disposition,
filename, port, savePath) {
assert.equal(state, 'completed')
assert.equal(filename, 'mock.pdf')
assert.equal(savePath, path.join(__dirname, 'fixtures', 'mock.pdf'))
@@ -306,8 +310,12 @@ describe('session module', function () {
var port = downloadServer.address().port
ipcRenderer.sendSync('set-download-option', false, false)
w.loadURL(url + ':' + port)
ipcRenderer.once('download-done', function (event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename, savePath) {
assertDownload(event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename, port, savePath)
ipcRenderer.once('download-done', function (event, state, url,
mimeType, receivedBytes,
totalBytes, disposition,
filename, savePath) {
assertDownload(event, state, url, mimeType, receivedBytes,
totalBytes, disposition, filename, port, savePath)
done()
})
})
@@ -322,8 +330,12 @@ describe('session module', function () {
webview.addEventListener('did-finish-load', function () {
webview.downloadURL(url + ':' + port + '/')
})
ipcRenderer.once('download-done', function (event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename, savePath) {
assertDownload(event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename, port, savePath)
ipcRenderer.once('download-done', function (event, state, url,
mimeType, receivedBytes,
totalBytes, disposition,
filename, savePath) {
assertDownload(event, state, url, mimeType, receivedBytes,
totalBytes, disposition, filename, port, savePath)
document.body.removeChild(webview)
done()
})
@@ -336,7 +348,10 @@ describe('session module', function () {
var port = downloadServer.address().port
ipcRenderer.sendSync('set-download-option', true, false)
w.loadURL(url + ':' + port + '/')
ipcRenderer.once('download-done', function (event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) {
ipcRenderer.once('download-done', function (event, state, url,
mimeType, receivedBytes,
totalBytes, disposition,
filename) {
assert.equal(state, 'cancelled')
assert.equal(filename, 'mock.pdf')
assert.equal(mimeType, 'application/pdf')
@@ -356,7 +371,10 @@ describe('session module', function () {
var port = downloadServer.address().port
ipcRenderer.sendSync('set-download-option', true, false)
w.loadURL(url + ':' + port + '/?testFilename')
ipcRenderer.once('download-done', function (event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) {
ipcRenderer.once('download-done', function (event, state, url,
mimeType, receivedBytes,
totalBytes, disposition,
filename) {
assert.equal(state, 'cancelled')
assert.equal(filename, 'download.pdf')
assert.equal(mimeType, 'application/pdf')
@@ -565,4 +583,139 @@ describe('session module', function () {
w.loadURL(url)
})
})
describe('ses.createInterruptedDownload(options)', function () {
it('can create an interrupted download item', function (done) {
ipcRenderer.sendSync('set-download-option', true, false)
const filePath = path.join(__dirname, 'fixtures', 'mock.pdf')
const options = {
path: filePath,
urlChain: ['http://127.0.0.1/'],
mimeType: 'application/pdf',
offset: 0,
length: 5242880
}
w.webContents.session.createInterruptedDownload(options)
ipcRenderer.once('download-created', function (event, state, urlChain,
mimeType, receivedBytes,
totalBytes, filename,
savePath) {
assert.equal(state, 'interrupted')
assert.deepEqual(urlChain, ['http://127.0.0.1/'])
assert.equal(mimeType, 'application/pdf')
assert.equal(receivedBytes, 0)
assert.equal(totalBytes, 5242880)
assert.equal(savePath, filePath)
done()
})
})
it('can be resumed', function (done) {
const fixtures = path.join(__dirname, 'fixtures')
const downloadFilePath = path.join(fixtures, 'logo.png')
const rangeServer = http.createServer(function (req, res) {
let options = {
root: fixtures
}
send(req, req.url, options)
.on('error', function (error) {
done(error)
}).pipe(res)
})
ipcRenderer.sendSync('set-download-option', true, false, downloadFilePath)
rangeServer.listen(0, '127.0.0.1', function () {
const port = rangeServer.address().port
const downloadUrl = `http://127.0.0.1:${port}/assets/logo.png`
const callback = function (event, state, url, mimeType,
receivedBytes, totalBytes, disposition,
filename, savePath, urlChain,
lastModifiedTime, eTag) {
if (state === 'cancelled') {
const options = {
path: savePath,
urlChain: urlChain,
mimeType: mimeType,
offset: receivedBytes,
length: totalBytes,
lastModified: lastModifiedTime,
eTag: eTag
}
ipcRenderer.sendSync('set-download-option', false, false, downloadFilePath)
w.webContents.session.createInterruptedDownload(options)
} else {
assert.equal(state, 'completed')
assert.equal(filename, 'logo.png')
assert.equal(savePath, downloadFilePath)
assert.equal(url, downloadUrl)
assert.equal(mimeType, 'image/png')
assert.equal(receivedBytes, 14022)
assert.equal(totalBytes, 14022)
assert(fs.existsSync(downloadFilePath))
fs.unlinkSync(downloadFilePath)
rangeServer.close()
ipcRenderer.removeListener('download-done', callback)
done()
}
}
ipcRenderer.on('download-done', callback)
w.webContents.downloadURL(downloadUrl)
})
})
})
describe('ses.clearAuthCache(options[, callback])', function () {
it('can clear http auth info from cache', function (done) {
const ses = session.fromPartition('auth-cache')
const server = http.createServer(function (req, res) {
var credentials = auth(req)
if (!credentials || credentials.name !== 'test' || credentials.pass !== 'test') {
res.statusCode = 401
res.setHeader('WWW-Authenticate', 'Basic realm="Restricted"')
res.end()
} else {
res.end('authenticated')
}
})
server.listen(0, '127.0.0.1', function () {
const port = server.address().port
function issueLoginRequest (attempt = 1) {
if (attempt > 2) {
server.close()
return done()
}
const request = net.request({
url: `http://127.0.0.1:${port}`,
session: ses
})
request.on('login', function (info, callback) {
attempt++
assert.equal(info.scheme, 'basic')
assert.equal(info.realm, 'Restricted')
callback('test', 'test')
})
request.on('response', function (response) {
let data = ''
response.pause()
response.on('data', function (chunk) {
data += chunk
})
response.on('end', function () {
assert.equal(data, 'authenticated')
ses.clearAuthCache({type: 'password'}, function () {
issueLoginRequest(attempt)
})
})
response.on('error', function (error) {
done(error)
})
response.resume()
})
// Internal api to bypass cache for testing.
request.urlRequest._setLoadFlags(1 << 1)
request.end()
}
issueLoginRequest()
})
})
})
})

View File

@@ -10,6 +10,7 @@
"mocha": "^3.1.0",
"multiparty": "^4.1.2",
"q": "^1.4.1",
"send": "^0.14.1",
"temp": "^0.8.3",
"walkdir": "0.0.11",
"ws": "^1.1.1",

View File

@@ -137,8 +137,16 @@ app.on('ready', function () {
// 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')
ipcMain.on('set-download-option', function (event, needCancel, preventDefault) {
ipcMain.on('set-download-option', function (event, needCancel, preventDefault, filePath = downloadFilePath) {
window.webContents.session.once('will-download', function (e, item) {
window.webContents.send('download-created',
item.getState(),
item.getURLChain(),
item.getMimeType(),
item.getReceivedBytes(),
item.getTotalBytes(),
item.getFilename(),
item.getSavePath())
if (preventDefault) {
e.preventDefault()
const url = item.getURL()
@@ -151,7 +159,11 @@ app.on('ready', function () {
}
})
} else {
item.setSavePath(downloadFilePath)
if (item.getState() === 'interrupted' && !needCancel) {
item.resume()
} else {
item.setSavePath(filePath)
}
item.on('done', function (e, state) {
window.webContents.send('download-done',
state,
@@ -161,7 +173,10 @@ app.on('ready', function () {
item.getTotalBytes(),
item.getContentDisposition(),
item.getFilename(),
item.getSavePath())
item.getSavePath(),
item.getURLChain(),
item.getLastModifiedTime(),
item.getETag())
})
if (needCancel) item.cancel()
}