merge changes from master

This commit is contained in:
liusi 2016-11-01 10:20:47 +08:00
commit 7880d37d73
126 changed files with 5549 additions and 896 deletions

View file

@ -0,0 +1,61 @@
// Copyright (c) 2016 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_net.h"
#include "atom/browser/api/atom_api_url_request.h"
#include "atom/common/node_includes.h"
#include "native_mate/dictionary.h"
namespace atom {
namespace api {
Net::Net(v8::Isolate* isolate) {
Init(isolate);
}
Net::~Net() {}
// static
v8::Local<v8::Value> Net::Create(v8::Isolate* isolate) {
return mate::CreateHandle(isolate, new Net(isolate)).ToV8();
}
// static
void Net::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype) {
prototype->SetClassName(mate::StringToV8(isolate, "Net"));
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
.SetProperty("URLRequest", &Net::URLRequest);
}
v8::Local<v8::Value> Net::URLRequest(v8::Isolate* isolate) {
return URLRequest::GetConstructor(isolate)->GetFunction();
}
} // namespace api
} // namespace atom
namespace {
using atom::api::Net;
using atom::api::URLRequest;
void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv) {
v8::Isolate* isolate = context->GetIsolate();
URLRequest::SetConstructor(isolate, base::Bind(URLRequest::New));
mate::Dictionary dict(isolate, exports);
dict.Set("net", Net::Create(isolate));
dict.Set("Net", Net::GetConstructor(isolate)->GetFunction());
}
} // namespace
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_net, Initialize)

View file

@ -0,0 +1,35 @@
// Copyright (c) 2016 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_NET_H_
#define ATOM_BROWSER_API_ATOM_API_NET_H_
#include "atom/browser/api/event_emitter.h"
namespace atom {
namespace api {
class Net : public mate::EventEmitter<Net> {
public:
static v8::Local<v8::Value> Create(v8::Isolate* isolate);
static void BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype);
v8::Local<v8::Value> URLRequest(v8::Isolate* isolate);
protected:
explicit Net(v8::Isolate* isolate);
~Net() override;
private:
DISALLOW_COPY_AND_ASSIGN(Net);
};
} // namespace api
} // namespace atom
#endif // ATOM_BROWSER_API_ATOM_API_NET_H_

View file

@ -0,0 +1,435 @@
// Copyright (c) 2016 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_url_request.h"
#include <string>
#include "atom/browser/api/atom_api_session.h"
#include "atom/browser/net/atom_url_request.h"
#include "atom/common/api/event_emitter_caller.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/net_converter.h"
#include "atom/common/native_mate_converters/string16_converter.h"
#include "atom/common/node_includes.h"
#include "native_mate/dictionary.h"
namespace mate {
template <>
struct Converter<scoped_refptr<const net::IOBufferWithSize>> {
static v8::Local<v8::Value> ToV8(
v8::Isolate* isolate,
scoped_refptr<const net::IOBufferWithSize> buffer) {
return node::Buffer::Copy(isolate, buffer->data(), buffer->size())
.ToLocalChecked();
}
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
scoped_refptr<const net::IOBufferWithSize>* out) {
auto size = node::Buffer::Length(val);
if (size == 0) {
// Support conversion from empty buffer. A use case is
// a GET request without body.
// Since zero-sized IOBuffer(s) are not supported, we set the
// out pointer to null.
*out = nullptr;
return true;
}
auto data = node::Buffer::Data(val);
if (!data) {
// This is an error as size is positive but data is null.
return false;
}
*out = new net::IOBufferWithSize(size);
// We do a deep copy. We could have used Buffer's internal memory
// but that is much more complicated to be properly handled.
memcpy((*out)->data(), data, size);
return true;
}
};
} // namespace mate
namespace atom {
namespace api {
template <typename Flags>
URLRequest::StateBase<Flags>::StateBase(Flags initialState)
: state_(initialState) {}
template <typename Flags>
void URLRequest::StateBase<Flags>::SetFlag(Flags flag) {
state_ =
static_cast<Flags>(static_cast<int>(state_) | static_cast<int>(flag));
}
template <typename Flags>
bool URLRequest::StateBase<Flags>::operator==(Flags flag) const {
return state_ == flag;
}
template <typename Flags>
bool URLRequest::StateBase<Flags>::IsFlagSet(Flags flag) const {
return static_cast<int>(state_) & static_cast<int>(flag);
}
URLRequest::RequestState::RequestState()
: StateBase(RequestStateFlags::kNotStarted) {}
bool URLRequest::RequestState::NotStarted() const {
return *this == RequestStateFlags::kNotStarted;
}
bool URLRequest::RequestState::Started() const {
return IsFlagSet(RequestStateFlags::kStarted);
}
bool URLRequest::RequestState::Finished() const {
return IsFlagSet(RequestStateFlags::kFinished);
}
bool URLRequest::RequestState::Canceled() const {
return IsFlagSet(RequestStateFlags::kCanceled);
}
bool URLRequest::RequestState::Failed() const {
return IsFlagSet(RequestStateFlags::kFailed);
}
bool URLRequest::RequestState::Closed() const {
return IsFlagSet(RequestStateFlags::kClosed);
}
URLRequest::ResponseState::ResponseState()
: StateBase(ResponseStateFlags::kNotStarted) {}
bool URLRequest::ResponseState::NotStarted() const {
return *this == ResponseStateFlags::kNotStarted;
}
bool URLRequest::ResponseState::Started() const {
return IsFlagSet(ResponseStateFlags::kStarted);
}
bool URLRequest::ResponseState::Ended() const {
return IsFlagSet(ResponseStateFlags::kEnded);
}
bool URLRequest::ResponseState::Failed() const {
return IsFlagSet(ResponseStateFlags::kFailed);
}
URLRequest::URLRequest(v8::Isolate* isolate, v8::Local<v8::Object> wrapper) {
InitWith(isolate, wrapper);
}
URLRequest::~URLRequest() {
// A request has been created in JS, it was not used and then
// it got collected, no close event to cleanup, only destructor
// is called.
if (atom_request_) {
atom_request_->Terminate();
}
}
// static
mate::WrappableBase* URLRequest::New(mate::Arguments* args) {
auto isolate = args->isolate();
v8::Local<v8::Object> options;
args->GetNext(&options);
mate::Dictionary dict(isolate, options);
std::string method;
dict.Get("method", &method);
std::string url;
dict.Get("url", &url);
std::string partition;
mate::Handle<api::Session> session;
if (dict.Get("session", &session)) {
} else if (dict.Get("partition", &partition)) {
session = Session::FromPartition(isolate, partition);
} else {
// Use the default session if not specified.
session = Session::FromPartition(isolate, "");
}
auto browser_context = session->browser_context();
auto api_url_request = new URLRequest(args->isolate(), args->GetThis());
auto atom_url_request =
AtomURLRequest::Create(browser_context, method, url, api_url_request);
api_url_request->atom_request_ = atom_url_request;
return api_url_request;
}
// static
void URLRequest::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype) {
prototype->SetClassName(mate::StringToV8(isolate, "URLRequest"));
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
// Request API
.MakeDestroyable()
.SetMethod("write", &URLRequest::Write)
.SetMethod("cancel", &URLRequest::Cancel)
.SetMethod("setExtraHeader", &URLRequest::SetExtraHeader)
.SetMethod("removeExtraHeader", &URLRequest::RemoveExtraHeader)
.SetMethod("setChunkedUpload", &URLRequest::SetChunkedUpload)
.SetProperty("notStarted", &URLRequest::NotStarted)
.SetProperty("finished", &URLRequest::Finished)
// Response APi
.SetProperty("statusCode", &URLRequest::StatusCode)
.SetProperty("statusMessage", &URLRequest::StatusMessage)
.SetProperty("rawResponseHeaders", &URLRequest::RawResponseHeaders)
.SetProperty("httpVersionMajor", &URLRequest::ResponseHttpVersionMajor)
.SetProperty("httpVersionMinor", &URLRequest::ResponseHttpVersionMinor);
}
bool URLRequest::NotStarted() const {
return request_state_.NotStarted();
}
bool URLRequest::Finished() const {
return request_state_.Finished();
}
bool URLRequest::Canceled() const {
return request_state_.Canceled();
}
bool URLRequest::Write(scoped_refptr<const net::IOBufferWithSize> buffer,
bool is_last) {
if (request_state_.Canceled() || request_state_.Failed() ||
request_state_.Finished() || request_state_.Closed()) {
return false;
}
if (request_state_.NotStarted()) {
request_state_.SetFlag(RequestStateFlags::kStarted);
// Pin on first write.
Pin();
}
if (is_last) {
request_state_.SetFlag(RequestStateFlags::kFinished);
EmitRequestEvent(true, "finish");
}
DCHECK(atom_request_);
if (atom_request_) {
return atom_request_->Write(buffer, is_last);
}
return false;
}
void URLRequest::Cancel() {
if (request_state_.Canceled() || request_state_.Closed()) {
// Cancel only once.
return;
}
// Mark as canceled.
request_state_.SetFlag(RequestStateFlags::kCanceled);
DCHECK(atom_request_);
if (atom_request_ && request_state_.Started()) {
// Really cancel if it was started.
atom_request_->Cancel();
}
EmitRequestEvent(true, "abort");
if (response_state_.Started() && !response_state_.Ended()) {
EmitResponseEvent(true, "aborted");
}
Close();
}
bool URLRequest::SetExtraHeader(const std::string& name,
const std::string& value) {
// Request state must be in the initial non started state.
if (!request_state_.NotStarted()) {
// Cannot change headers after send.
return false;
}
if (!net::HttpUtil::IsValidHeaderName(name)) {
return false;
}
if (!net::HttpUtil::IsValidHeaderValue(value)) {
return false;
}
DCHECK(atom_request_);
if (atom_request_) {
atom_request_->SetExtraHeader(name, value);
}
return true;
}
void URLRequest::RemoveExtraHeader(const std::string& name) {
// State must be equal to not started.
if (!request_state_.NotStarted()) {
// Cannot change headers after send.
return;
}
DCHECK(atom_request_);
if (atom_request_) {
atom_request_->RemoveExtraHeader(name);
}
}
void URLRequest::SetChunkedUpload(bool is_chunked_upload) {
// State must be equal to not started.
if (!request_state_.NotStarted()) {
// Cannot change headers after send.
return;
}
DCHECK(atom_request_);
if (atom_request_) {
atom_request_->SetChunkedUpload(is_chunked_upload);
}
}
void URLRequest::OnAuthenticationRequired(
scoped_refptr<const net::AuthChallengeInfo> auth_info) {
if (request_state_.Canceled() || request_state_.Closed()) {
return;
}
DCHECK(atom_request_);
if (!atom_request_) {
return;
}
Emit("login", auth_info.get(),
base::Bind(&AtomURLRequest::PassLoginInformation, atom_request_));
}
void URLRequest::OnResponseStarted(
scoped_refptr<net::HttpResponseHeaders> response_headers) {
if (request_state_.Canceled() || request_state_.Failed() ||
request_state_.Closed()) {
// Don't emit any event after request cancel.
return;
}
response_headers_ = response_headers;
response_state_.SetFlag(ResponseStateFlags::kStarted);
Emit("response");
}
void URLRequest::OnResponseData(
scoped_refptr<const net::IOBufferWithSize> buffer) {
if (request_state_.Canceled() || request_state_.Closed() ||
request_state_.Failed() || response_state_.Failed()) {
// In case we received an unexpected event from Chromium net,
// don't emit any data event after request cancel/error/close.
return;
}
if (!buffer || !buffer->data() || !buffer->size()) {
return;
}
Emit("data", buffer);
}
void URLRequest::OnResponseCompleted() {
if (request_state_.Canceled() || request_state_.Closed() ||
request_state_.Failed() || response_state_.Failed()) {
// In case we received an unexpected event from Chromium net,
// don't emit any data event after request cancel/error/close.
return;
}
response_state_.SetFlag(ResponseStateFlags::kEnded);
Emit("end");
Close();
}
void URLRequest::OnError(const std::string& error, bool isRequestError) {
auto error_object = v8::Exception::Error(mate::StringToV8(isolate(), error));
if (isRequestError) {
request_state_.SetFlag(RequestStateFlags::kFailed);
EmitRequestEvent(false, "error", error_object);
} else {
response_state_.SetFlag(ResponseStateFlags::kFailed);
EmitResponseEvent(false, "error", error_object);
}
Close();
}
int URLRequest::StatusCode() const {
if (response_headers_) {
return response_headers_->response_code();
}
return -1;
}
std::string URLRequest::StatusMessage() const {
std::string result;
if (response_headers_) {
result = response_headers_->GetStatusText();
}
return result;
}
net::HttpResponseHeaders* URLRequest::RawResponseHeaders() const {
return response_headers_.get();
}
uint32_t URLRequest::ResponseHttpVersionMajor() const {
if (response_headers_) {
return response_headers_->GetHttpVersion().major_value();
}
return 0;
}
uint32_t URLRequest::ResponseHttpVersionMinor() const {
if (response_headers_) {
return response_headers_->GetHttpVersion().minor_value();
}
return 0;
}
void URLRequest::Close() {
if (!request_state_.Closed()) {
request_state_.SetFlag(RequestStateFlags::kClosed);
if (response_state_.Started()) {
// Emit a close event if we really have a response object.
EmitResponseEvent(true, "close");
}
EmitRequestEvent(true, "close");
}
Unpin();
if (atom_request_) {
// A request has been created in JS, used and then it ended.
// We release unneeded net resources.
atom_request_->Terminate();
}
atom_request_ = nullptr;
}
void URLRequest::Pin() {
if (wrapper_.IsEmpty()) {
wrapper_.Reset(isolate(), GetWrapper());
}
}
void URLRequest::Unpin() {
wrapper_.Reset();
}
template <typename... Args>
void URLRequest::EmitRequestEvent(Args... args) {
v8::HandleScope handle_scope(isolate());
mate::CustomEmit(isolate(), GetWrapper(), "_emitRequestEvent", args...);
}
template <typename... Args>
void URLRequest::EmitResponseEvent(Args... args) {
v8::HandleScope handle_scope(isolate());
mate::CustomEmit(isolate(), GetWrapper(), "_emitResponseEvent", args...);
}
} // namespace api
} // namespace atom

View file

@ -0,0 +1,206 @@
// Copyright (c) 2016 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_URL_REQUEST_H_
#define ATOM_BROWSER_API_ATOM_API_URL_REQUEST_H_
#include <array>
#include <string>
#include "atom/browser/api/event_emitter.h"
#include "atom/browser/api/trackable_object.h"
#include "base/memory/weak_ptr.h"
#include "native_mate/dictionary.h"
#include "native_mate/handle.h"
#include "native_mate/wrappable_base.h"
#include "net/base/auth.h"
#include "net/base/io_buffer.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request_context.h"
namespace atom {
class AtomURLRequest;
namespace api {
//
// The URLRequest class implements the V8 binding between the JavaScript API
// and Chromium native net library. It is responsible for handling HTTP/HTTPS
// requests.
//
// The current class provides only the binding layer. Two other JavaScript
// classes (ClientRequest and IncomingMessage) in the net module provide the
// final API, including some state management and arguments validation.
//
// URLRequest's methods fall into two main categories: command and event
// methods. They are always executed on the Browser's UI thread.
// Command methods are called directly from JavaScript code via the API defined
// in BuildPrototype. A command method is generally implemented by forwarding
// the call to a corresponding method on AtomURLRequest which does the
// synchronization on the Browser IO thread. The latter then calls into Chromium
// net library. On the other hand, net library events originate on the IO
// thread in AtomURLRequest and are synchronized back on the UI thread, then
// forwarded to a corresponding event method in URLRequest and then to
// JavaScript via the EmitRequestEvent/EmitResponseEvent helpers.
//
// URLRequest lifetime management: we followed the Wrapper/Wrappable pattern
// defined in native_mate. However, we augment that pattern with a pin/unpin
// mechanism. The main reason is that we want the JS API to provide a similar
// lifetime guarantees as the XMLHttpRequest.
// https://xhr.spec.whatwg.org/#garbage-collection
//
// The primary motivation is to not garbage collect a URLInstance as long as the
// object is emitting network events. For instance, in the following JS code
//
// (function() {
// let request = new URLRequest(...);
// request.on('response', (response)=>{
// response.on('data', (data) = > {
// console.log(data.toString());
// });
// });
// })();
//
// we still want data to be logged even if the response/request objects are n
// more referenced in JavaScript.
//
// Binding by simply following the native_mate Wrapper/Wrappable pattern will
// delete the URLRequest object when the corresponding JS object is collected.
// The v8 handle is a private member in WrappableBase and it is always weak,
// there is no way to make it strong without changing native_mate.
// The solution we implement consists of maintaining some kind of state that
// prevents collection of JS wrappers as long as the request is emitting network
// events. At initialization, the object is unpinned. When the request starts,
// it is pinned. When no more events would be emitted, the object is unpinned
// and lifetime is again managed by the standard native mate Wrapper/Wrappable
// pattern.
//
// pin/unpin: are implemented by constructing/reseting a V8 strong persistent
// handle.
//
// The URLRequest/AtmURLRequest interaction could have been implemented in a
// single class. However, it implies that the resulting class lifetime will be
// managed by two conflicting mechanisms: JavaScript garbage collection and
// Chromium reference counting. Reasoning about lifetime issues become much
// more complex.
//
// We chose to split the implementation into two classes linked via a
// reference counted/raw pointers. A URLRequest instance is deleted if it is
// unpinned and the corresponding JS wrapper object is garbage collected. On the
// other hand, an AtmURLRequest instance lifetime is totally governed by
// reference counting.
//
class URLRequest : public mate::EventEmitter<URLRequest> {
public:
static mate::WrappableBase* New(mate::Arguments* args);
static void BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype);
// Methods for reporting events into JavaScript.
void OnAuthenticationRequired(
scoped_refptr<const net::AuthChallengeInfo> auth_info);
void OnResponseStarted(
scoped_refptr<net::HttpResponseHeaders> response_headers);
void OnResponseData(scoped_refptr<const net::IOBufferWithSize> data);
void OnResponseCompleted();
void OnError(const std::string& error, bool isRequestError);
protected:
explicit URLRequest(v8::Isolate* isolate, v8::Local<v8::Object> wrapper);
~URLRequest() override;
private:
template <typename Flags>
class StateBase {
public:
void SetFlag(Flags flag);
protected:
explicit StateBase(Flags initialState);
bool operator==(Flags flag) const;
bool IsFlagSet(Flags flag) const;
private:
Flags state_;
};
enum class RequestStateFlags {
kNotStarted = 0x0,
kStarted = 0x1,
kFinished = 0x2,
kCanceled = 0x4,
kFailed = 0x8,
kClosed = 0x10
};
class RequestState : public StateBase<RequestStateFlags> {
public:
RequestState();
bool NotStarted() const;
bool Started() const;
bool Finished() const;
bool Canceled() const;
bool Failed() const;
bool Closed() const;
};
enum class ResponseStateFlags {
kNotStarted = 0x0,
kStarted = 0x1,
kEnded = 0x2,
kFailed = 0x4
};
class ResponseState : public StateBase<ResponseStateFlags> {
public:
ResponseState();
bool NotStarted() const;
bool Started() const;
bool Ended() const;
bool Canceled() const;
bool Failed() const;
bool Closed() const;
};
bool NotStarted() const;
bool Finished() const;
bool Canceled() const;
bool Failed() const;
bool Write(scoped_refptr<const net::IOBufferWithSize> buffer, bool is_last);
void Cancel();
bool SetExtraHeader(const std::string& name, const std::string& value);
void RemoveExtraHeader(const std::string& name);
void SetChunkedUpload(bool is_chunked_upload);
int StatusCode() const;
std::string StatusMessage() const;
net::HttpResponseHeaders* RawResponseHeaders() const;
uint32_t ResponseHttpVersionMajor() const;
uint32_t ResponseHttpVersionMinor() const;
void Close();
void Pin();
void Unpin();
template <typename... Args>
void EmitRequestEvent(Args... args);
template <typename... Args>
void EmitResponseEvent(Args... args);
scoped_refptr<AtomURLRequest> atom_request_;
RequestState request_state_;
ResponseState response_state_;
// Used to implement pin/unpin.
v8::Global<v8::Object> wrapper_;
scoped_refptr<net::HttpResponseHeaders> response_headers_;
DISALLOW_COPY_AND_ASSIGN(URLRequest);
};
} // namespace api
} // namespace atom
#endif // ATOM_BROWSER_API_ATOM_API_URL_REQUEST_H_

View file

@ -35,10 +35,10 @@
#include "atom/common/native_mate_converters/gfx_converter.h"
#include "atom/common/native_mate_converters/gurl_converter.h"
#include "atom/common/native_mate_converters/image_converter.h"
#include "atom/common/native_mate_converters/net_converter.h"
#include "atom/common/native_mate_converters/string16_converter.h"
#include "atom/common/native_mate_converters/value_converter.h"
#include "atom/common/options_switches.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "brightray/browser/inspectable_web_contents.h"
#include "brightray/browser/inspectable_web_contents_view.h"
@ -66,7 +66,6 @@
#include "content/public/common/context_menu_params.h"
#include "native_mate/dictionary.h"
#include "native_mate/object_template_builder.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request_context.h"
#include "third_party/WebKit/public/web/WebFindOptions.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
@ -141,32 +140,6 @@ 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) {
size_t iter = 0;
std::string key;
std::string value;
while (headers->EnumerateHeaderLines(&iter, &key, &value)) {
key = base::ToLowerASCII(key);
if (response_headers.HasKey(key)) {
base::ListValue* values = nullptr;
if (response_headers.GetList(key, &values))
values->AppendString(value);
} else {
std::unique_ptr<base::ListValue> values(new base::ListValue());
values->AppendString(value);
response_headers.Set(key, std::move(values));
}
}
}
return ConvertToV8(isolate, response_headers);
}
};
template<>
struct Converter<content::SavePageType> {
static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val,

View file

@ -729,6 +729,13 @@ void Window::SetAspectRatio(double aspect_ratio, mate::Arguments* args) {
window_->SetAspectRatio(aspect_ratio, extra_size);
}
void Window::PreviewFile(const std::string& path, mate::Arguments* args) {
std::string display_name;
if (!args->GetNext(&display_name))
display_name = path;
window_->PreviewFile(path, display_name);
}
void Window::SetParentWindow(v8::Local<v8::Value> value,
mate::Arguments* args) {
if (IsModal()) {
@ -825,6 +832,7 @@ void Window::BuildPrototype(v8::Isolate* isolate,
.SetMethod("setFullScreen", &Window::SetFullScreen)
.SetMethod("isFullScreen", &Window::IsFullscreen)
.SetMethod("setAspectRatio", &Window::SetAspectRatio)
.SetMethod("previewFile", &Window::PreviewFile)
#if !defined(OS_WIN)
.SetMethod("setParentWindow", &Window::SetParentWindow)
#endif

View file

@ -170,6 +170,7 @@ class Window : public mate::TrackableObject<Window>,
void SetMenuBarVisibility(bool visible);
bool IsMenuBarVisible();
void SetAspectRatio(double aspect_ratio, mate::Arguments* args);
void PreviewFile(const std::string& path, mate::Arguments* args);
void SetParentWindow(v8::Local<v8::Value> value, mate::Arguments* args);
v8::Local<v8::Value> GetParentWindow() const;
std::vector<v8::Local<v8::Object>> GetChildWindows() const;

View file

@ -17,7 +17,7 @@ using content::BrowserThread;
namespace atom {
namespace {
namespace internal {
// Loads access tokens and other necessary data on the UI thread, and
// calls back to the originator on the originating thread.
@ -27,12 +27,15 @@ class TokenLoadingJob : public base::RefCountedThreadSafe<TokenLoadingJob> {
const content::AccessTokenStore::LoadAccessTokensCallback& callback)
: callback_(callback), request_context_getter_(nullptr) {}
void Run() {
BrowserThread::PostTaskAndReply(
BrowserThread::UI,
FROM_HERE,
base::Bind(&TokenLoadingJob::PerformWorkOnUIThread, this),
base::Bind(&TokenLoadingJob::RespondOnOriginatingThread, this));
void Run(AtomBrowserContext* browser_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
request_context_getter_ = browser_context->GetRequestContext();
std::unique_ptr<base::Environment> env(base::Environment::Create());
if (!env->GetVar("GOOGLE_API_KEY", &api_key_))
api_key_ = GOOGLEAPIS_API_KEY;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&TokenLoadingJob::RespondOnIOThread, this));
}
private:
@ -40,16 +43,7 @@ class TokenLoadingJob : public base::RefCountedThreadSafe<TokenLoadingJob> {
~TokenLoadingJob() {}
void PerformWorkOnUIThread() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto browser_context = AtomBrowserContext::From("", false);
request_context_getter_ = browser_context->GetRequestContext();
std::unique_ptr<base::Environment> env(base::Environment::Create());
if (!env->GetVar("GOOGLE_API_KEY", &api_key_))
api_key_ = GOOGLEAPIS_API_KEY;
}
void RespondOnOriginatingThread() {
void RespondOnIOThread() {
// Equivalent to access_token_map[kGeolocationProviderURL].
// Somehow base::string16 is causing compilation errors when used in a pair
// of std::map on Linux, this can work around it.
@ -66,9 +60,10 @@ class TokenLoadingJob : public base::RefCountedThreadSafe<TokenLoadingJob> {
std::string api_key_;
};
} // namespace
} // namespace internal
AtomAccessTokenStore::AtomAccessTokenStore() {
browser_context_ = AtomBrowserContext::From("", false);
content::GeolocationProvider::GetInstance()->UserDidOptIntoLocationServices();
}
@ -77,8 +72,16 @@ AtomAccessTokenStore::~AtomAccessTokenStore() {
void AtomAccessTokenStore::LoadAccessTokens(
const LoadAccessTokensCallback& callback) {
scoped_refptr<TokenLoadingJob> job(new TokenLoadingJob(callback));
job->Run();
scoped_refptr<internal::TokenLoadingJob> job(
new internal::TokenLoadingJob(callback));
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&AtomAccessTokenStore::RunTokenLoadingJob,
this, base::RetainedRef(job)));
}
void AtomAccessTokenStore::RunTokenLoadingJob(
scoped_refptr<internal::TokenLoadingJob> job) {
job->Run(browser_context_.get());
}
void AtomAccessTokenStore::SaveAccessToken(const GURL& server_url,

View file

@ -9,6 +9,12 @@
namespace atom {
class AtomBrowserContext;
namespace internal {
class TokenLoadingJob;
}
class AtomAccessTokenStore : public content::AccessTokenStore {
public:
AtomAccessTokenStore();
@ -21,6 +27,9 @@ class AtomAccessTokenStore : public content::AccessTokenStore {
const base::string16& access_token) override;
private:
void RunTokenLoadingJob(scoped_refptr<internal::TokenLoadingJob> job);
scoped_refptr<AtomBrowserContext> browser_context_;
DISALLOW_COPY_AND_ASSIGN(AtomAccessTokenStore);
};

View file

@ -12,6 +12,7 @@
#include "atom/browser/browser.h"
#include "atom/browser/net/asar/asar_protocol_handler.h"
#include "atom/browser/net/atom_cert_verifier.h"
#include "atom/browser/net/atom_ct_delegate.h"
#include "atom/browser/net/atom_network_delegate.h"
#include "atom/browser/net/atom_ssl_config_service.h"
#include "atom/browser/net/atom_url_request_job_factory.h"
@ -67,10 +68,11 @@ std::string RemoveWhitespace(const std::string& str) {
} // namespace
AtomBrowserContext::AtomBrowserContext(
const std::string& partition, bool in_memory,
const base::DictionaryValue& options)
AtomBrowserContext::AtomBrowserContext(const std::string& partition,
bool in_memory,
const base::DictionaryValue& options)
: brightray::BrowserContext(partition, in_memory),
ct_delegate_(new AtomCTDelegate),
network_delegate_(new AtomNetworkDelegate),
cookie_delegate_(new AtomCookieDelegate) {
// Construct user agent string.
@ -190,7 +192,7 @@ content::PermissionManager* AtomBrowserContext::GetPermissionManager() {
}
std::unique_ptr<net::CertVerifier> AtomBrowserContext::CreateCertVerifier() {
return base::WrapUnique(new AtomCertVerifier);
return base::WrapUnique(new AtomCertVerifier(ct_delegate_.get()));
}
net::SSLConfigService* AtomBrowserContext::CreateSSLConfigService() {
@ -205,6 +207,11 @@ std::vector<std::string> AtomBrowserContext::GetCookieableSchemes() {
return default_schemes;
}
net::TransportSecurityState::RequireCTDelegate*
AtomBrowserContext::GetRequireCTDelegate() {
return ct_delegate_.get();
}
void AtomBrowserContext::RegisterPrefs(PrefRegistrySimple* pref_registry) {
pref_registry->RegisterFilePathPref(prefs::kSelectFileLastDirectory,
base::FilePath());

View file

@ -15,6 +15,7 @@
namespace atom {
class AtomBlobReader;
class AtomCTDelegate;
class AtomDownloadManagerDelegate;
class AtomNetworkDelegate;
class AtomPermissionManager;
@ -42,6 +43,8 @@ class AtomBrowserContext : public brightray::BrowserContext {
std::unique_ptr<net::CertVerifier> CreateCertVerifier() override;
net::SSLConfigService* CreateSSLConfigService() override;
std::vector<std::string> GetCookieableSchemes() override;
net::TransportSecurityState::RequireCTDelegate* GetRequireCTDelegate()
override;
// content::BrowserContext:
content::DownloadManagerDelegate* GetDownloadManagerDelegate() override;
@ -67,6 +70,7 @@ class AtomBrowserContext : public brightray::BrowserContext {
std::unique_ptr<WebViewManager> guest_manager_;
std::unique_ptr<AtomPermissionManager> permission_manager_;
std::unique_ptr<AtomBlobReader> blob_reader_;
std::unique_ptr<AtomCTDelegate> ct_delegate_;
std::string user_agent_;
bool use_cache_;

View file

@ -374,6 +374,10 @@ void NativeWindow::SetAspectRatio(double aspect_ratio,
aspect_ratio_extraSize_ = extra_size;
}
void NativeWindow::PreviewFile(const std::string& path,
const std::string& display_name) {
}
void NativeWindow::RequestToClosePage() {
bool prevent_default = false;
FOR_EACH_OBSERVER(NativeWindowObserver,

View file

@ -176,6 +176,8 @@ class NativeWindow : public base::SupportsUserData,
double GetAspectRatio();
gfx::Size GetAspectRatioExtraSize();
virtual void SetAspectRatio(double aspect_ratio, const gfx::Size& extra_size);
virtual void PreviewFile(const std::string& path,
const std::string& display_name);
base::WeakPtr<NativeWindow> GetWeakPtr() {
return weak_factory_.GetWeakPtr();

View file

@ -55,6 +55,8 @@ class NativeWindowMac : public NativeWindow,
void SetMovable(bool movable) override;
void SetAspectRatio(double aspect_ratio, const gfx::Size& extra_size)
override;
void PreviewFile(const std::string& path, const std::string& display_name)
override;
bool IsMovable() override;
void SetMinimizable(bool minimizable) override;
bool IsMinimizable() override;

View file

@ -4,6 +4,7 @@
#include "atom/browser/native_window_mac.h"
#include <Quartz/Quartz.h>
#include <string>
#include "atom/browser/window_list.h"
@ -277,7 +278,29 @@ bool ScopedDisableResize::disable_resize_ = false;
@end
@interface AtomNSWindow : EventDispatchingWindow {
@interface AtomPreviewItem : NSObject <QLPreviewItem>
@property (nonatomic, retain) NSURL* previewItemURL;
@property (nonatomic, retain) NSString* previewItemTitle;
- (id)initWithURL:(NSURL*)url title:(NSString*)title;
@end
@implementation AtomPreviewItem
- (id)initWithURL:(NSURL*)url title:(NSString*)title {
self = [super init];
if (self) {
self.previewItemURL = url;
self.previewItemTitle = title;
}
return self;
}
@end
@interface AtomNSWindow : EventDispatchingWindow<QLPreviewPanelDataSource, QLPreviewPanelDelegate> {
@private
atom::NativeWindowMac* shell_;
bool enable_larger_than_screen_;
@ -287,6 +310,7 @@ bool ScopedDisableResize::disable_resize_ = false;
@property BOOL disableAutoHideCursor;
@property BOOL disableKeyOrMainWindow;
@property NSPoint windowButtonsOffset;
@property (nonatomic, retain) AtomPreviewItem* quickLookItem;
- (void)setShell:(atom::NativeWindowMac*)shell;
- (void)setEnableLargerThanScreen:(bool)enable;
@ -444,6 +468,36 @@ bool ScopedDisableResize::disable_resize_ = false;
return [[self contentView] superview];
}
// Quicklook methods
- (BOOL)acceptsPreviewPanelControl:(QLPreviewPanel*)panel {
return YES;
}
- (void)beginPreviewPanelControl:(QLPreviewPanel*)panel {
panel.delegate = self;
panel.dataSource = self;
}
- (void)endPreviewPanelControl:(QLPreviewPanel*)panel {
panel.delegate = nil;
panel.dataSource = nil;
}
- (NSInteger)numberOfPreviewItemsInPreviewPanel:(QLPreviewPanel*)panel {
return 1;
}
- (id <QLPreviewItem>)previewPanel:(QLPreviewPanel*)panel previewItemAtIndex:(NSInteger)index {
return [self quickLookItem];
}
- (void)previewFileAtPath:(NSString*)path withName:(NSString*) fileName {
NSURL* url = [[[NSURL alloc] initFileURLWithPath:path] autorelease];
[self setQuickLookItem:[[[AtomPreviewItem alloc] initWithURL:url title:fileName] autorelease]];
[[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:nil];
}
@end
@interface ControlRegionView : NSView
@ -899,6 +953,13 @@ void NativeWindowMac::SetAspectRatio(double aspect_ratio,
[window_ setResizeIncrements:NSMakeSize(1.0, 1.0)];
}
void NativeWindowMac::PreviewFile(const std::string& path,
const std::string& display_name) {
NSString* path_ns = [NSString stringWithUTF8String:path.c_str()];
NSString* name_ns = [NSString stringWithUTF8String:display_name.c_str()];
[window_ previewFileAtPath:path_ns withName:name_ns];
}
void NativeWindowMac::SetMovable(bool movable) {
[window_ setMovable:movable];
}

View file

@ -327,7 +327,6 @@ NativeWindowViews::NativeWindowViews(
last_window_state_ = ui::SHOW_STATE_FULLSCREEN;
else
last_window_state_ = ui::SHOW_STATE_NORMAL;
last_normal_bounds_ = GetBounds();
#endif
}
@ -848,7 +847,7 @@ void NativeWindowViews::SetMenu(AtomMenuModel* menu_model) {
if (!menu_bar_) {
gfx::Size content_size = GetContentSize();
menu_bar_.reset(new MenuBar);
menu_bar_.reset(new MenuBar(this));
menu_bar_->set_owned_by_client();
if (!menu_bar_autohide_) {

View file

@ -211,22 +211,6 @@ class NativeWindowViews : public NativeWindow,
ui::WindowShowState last_window_state_;
// There's an issue with restore on Windows, that sometimes causes the Window
// to receive the wrong size (#2498). To circumvent that, we keep tabs on the
// size of the window while in the normal state (not maximized, minimized or
// fullscreen), so we restore it correctly.
gfx::Rect last_normal_bounds_;
// last_normal_bounds_ may or may not require update on WM_MOVE. When a
// window is maximized, it is moved (WM_MOVE) to maximum size first and then
// sized (WM_SIZE). In this case, last_normal_bounds_ should not update. We
// keep last_normal_bounds_candidate_ as a candidate which will become valid
// last_normal_bounds_ if the moves are consecutive with no WM_SIZE event in
// between.
gfx::Rect last_normal_bounds_candidate_;
bool consecutive_moves_;
// In charge of running taskbar related APIs.
TaskbarHost taskbar_host_;

View file

@ -125,7 +125,6 @@ bool NativeWindowViews::PreHandleMSG(
return taskbar_host_.HandleThumbarButtonEvent(LOWORD(w_param));
return false;
case WM_SIZE: {
consecutive_moves_ = false;
// Handle window state change.
HandleSizeEvent(w_param, l_param);
return false;
@ -135,15 +134,6 @@ bool NativeWindowViews::PreHandleMSG(
::GetWindowRect(GetAcceleratedWidget(), (LPRECT)l_param);
return false;
}
case WM_MOVE: {
if (last_window_state_ == ui::SHOW_STATE_NORMAL) {
if (consecutive_moves_)
last_normal_bounds_ = last_normal_bounds_candidate_;
last_normal_bounds_candidate_ = GetBounds();
consecutive_moves_ = true;
}
return false;
}
default:
return false;
}
@ -162,35 +152,20 @@ void NativeWindowViews::HandleSizeEvent(WPARAM w_param, LPARAM l_param) {
NotifyWindowMinimize();
break;
case SIZE_RESTORED:
if (last_window_state_ == ui::SHOW_STATE_NORMAL) {
// Window was resized so we save it's new size.
last_normal_bounds_ = GetBounds();
} else {
switch (last_window_state_) {
case ui::SHOW_STATE_MAXIMIZED:
switch (last_window_state_) {
case ui::SHOW_STATE_MAXIMIZED:
last_window_state_ = ui::SHOW_STATE_NORMAL;
NotifyWindowUnmaximize();
break;
case ui::SHOW_STATE_MINIMIZED:
if (IsFullscreen()) {
last_window_state_ = ui::SHOW_STATE_FULLSCREEN;
NotifyWindowEnterFullScreen();
} else {
last_window_state_ = ui::SHOW_STATE_NORMAL;
// When the window is restored we resize it to the previous known
// normal size.
SetBounds(last_normal_bounds_, false);
NotifyWindowUnmaximize();
break;
case ui::SHOW_STATE_MINIMIZED:
if (IsFullscreen()) {
last_window_state_ = ui::SHOW_STATE_FULLSCREEN;
NotifyWindowEnterFullScreen();
} else {
last_window_state_ = ui::SHOW_STATE_NORMAL;
// When the window is restored we resize it to the previous known
// normal size.
SetBounds(last_normal_bounds_, false);
NotifyWindowRestore();
}
break;
}
NotifyWindowRestore();
}
break;
}
break;
}

View file

@ -5,9 +5,11 @@
#include "atom/browser/net/atom_cert_verifier.h"
#include "atom/browser/browser.h"
#include "atom/browser/net/atom_ct_delegate.h"
#include "atom/common/native_mate_converters/net_converter.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/net_errors.h"
#include "net/cert/cert_verify_result.h"
#include "net/cert/crl_set.h"
#include "net/cert/x509_certificate.h"
@ -28,12 +30,11 @@ void OnResult(
} // namespace
AtomCertVerifier::AtomCertVerifier()
: default_cert_verifier_(net::CertVerifier::CreateDefault()) {
}
AtomCertVerifier::AtomCertVerifier(AtomCTDelegate* ct_delegate)
: default_cert_verifier_(net::CertVerifier::CreateDefault()),
ct_delegate_(ct_delegate) {}
AtomCertVerifier::~AtomCertVerifier() {
}
AtomCertVerifier::~AtomCertVerifier() {}
void AtomCertVerifier::SetVerifyProc(const VerifyProc& proc) {
verify_proc_ = proc;
@ -48,9 +49,15 @@ int AtomCertVerifier::Verify(
const net::BoundNetLog& net_log) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (verify_proc_.is_null())
if (verify_proc_.is_null()) {
ct_delegate_->ClearCTExcludedHostsList();
return default_cert_verifier_->Verify(
params, crl_set, verify_result, callback, out_req, net_log);
}
verify_result->Reset();
verify_result->verified_cert = params.certificate();
ct_delegate_->AddCTExcludedHost(params.hostname());
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,

View file

@ -12,9 +12,11 @@
namespace atom {
class AtomCTDelegate;
class AtomCertVerifier : public net::CertVerifier {
public:
AtomCertVerifier();
explicit AtomCertVerifier(AtomCTDelegate* ct_delegate);
virtual ~AtomCertVerifier();
using VerifyProc =
@ -37,6 +39,7 @@ class AtomCertVerifier : public net::CertVerifier {
private:
VerifyProc verify_proc_;
std::unique_ptr<net::CertVerifier> default_cert_verifier_;
AtomCTDelegate* ct_delegate_;
DISALLOW_COPY_AND_ASSIGN(AtomCertVerifier);
};

View file

@ -0,0 +1,34 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/net/atom_ct_delegate.h"
#include "content/public/browser/browser_thread.h"
namespace atom {
AtomCTDelegate::AtomCTDelegate() {}
AtomCTDelegate::~AtomCTDelegate() {}
void AtomCTDelegate::AddCTExcludedHost(const std::string& host) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
ct_excluded_hosts_.insert(host);
}
void AtomCTDelegate::ClearCTExcludedHostsList() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
ct_excluded_hosts_.clear();
}
AtomCTDelegate::CTRequirementLevel AtomCTDelegate::IsCTRequiredForHost(
const std::string& host) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!ct_excluded_hosts_.empty() &&
(ct_excluded_hosts_.find(host) != ct_excluded_hosts_.end()))
return CTRequirementLevel::NOT_REQUIRED;
return CTRequirementLevel::DEFAULT;
}
} // namespace atom

View file

@ -0,0 +1,33 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_NET_ATOM_CT_DELEGATE_H_
#define ATOM_BROWSER_NET_ATOM_CT_DELEGATE_H_
#include <set>
#include <string>
#include "net/http/transport_security_state.h"
namespace atom {
class AtomCTDelegate : public net::TransportSecurityState::RequireCTDelegate {
public:
AtomCTDelegate();
~AtomCTDelegate() override;
void AddCTExcludedHost(const std::string& host);
void ClearCTExcludedHostsList();
// net::TransportSecurityState::RequireCTDelegate:
CTRequirementLevel IsCTRequiredForHost(const std::string& host) override;
private:
std::set<std::string> ct_excluded_hosts_;
DISALLOW_COPY_AND_ASSIGN(AtomCTDelegate);
};
} // namespace atom
#endif // ATOM_BROWSER_NET_ATOM_CT_DELEGATE_H_

View file

@ -0,0 +1,422 @@
// Copyright (c) 2016 GitHub, Inc.
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/net/atom_url_request.h"
#include <string>
#include "atom/browser/api/atom_api_url_request.h"
#include "atom/browser/atom_browser_context.h"
#include "base/callback.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/elements_upload_data_stream.h"
#include "net/base/io_buffer.h"
#include "net/base/upload_bytes_element_reader.h"
namespace {
const int kBufferSize = 4096;
} // namespace
namespace atom {
namespace internal {
class UploadOwnedIOBufferElementReader : public net::UploadBytesElementReader {
public:
explicit UploadOwnedIOBufferElementReader(
scoped_refptr<const net::IOBufferWithSize> buffer)
: net::UploadBytesElementReader(buffer->data(), buffer->size()),
buffer_(buffer) {}
~UploadOwnedIOBufferElementReader() override {}
static UploadOwnedIOBufferElementReader* CreateWithBuffer(
scoped_refptr<const net::IOBufferWithSize> buffer) {
return new UploadOwnedIOBufferElementReader(std::move(buffer));
}
private:
scoped_refptr<const net::IOBuffer> buffer_;
DISALLOW_COPY_AND_ASSIGN(UploadOwnedIOBufferElementReader);
};
} // namespace internal
AtomURLRequest::AtomURLRequest(api::URLRequest* delegate)
: delegate_(delegate),
is_chunked_upload_(false),
response_read_buffer_(new net::IOBuffer(kBufferSize)) {}
AtomURLRequest::~AtomURLRequest() {
DCHECK(!request_context_getter_);
DCHECK(!request_);
}
scoped_refptr<AtomURLRequest> AtomURLRequest::Create(
AtomBrowserContext* browser_context,
const std::string& method,
const std::string& url,
api::URLRequest* delegate) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(browser_context);
DCHECK(!url.empty());
DCHECK(delegate);
if (!browser_context || url.empty() || !delegate) {
return nullptr;
}
auto request_context_getter = browser_context->url_request_context_getter();
DCHECK(request_context_getter);
if (!request_context_getter) {
return nullptr;
}
scoped_refptr<AtomURLRequest> atom_url_request(new AtomURLRequest(delegate));
if (content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&AtomURLRequest::DoInitialize, atom_url_request,
request_context_getter, method, url))) {
return atom_url_request;
}
return nullptr;
}
void AtomURLRequest::Terminate() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
delegate_ = nullptr;
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&AtomURLRequest::DoTerminate, this));
}
void AtomURLRequest::DoInitialize(
scoped_refptr<net::URLRequestContextGetter> request_context_getter,
const std::string& method,
const std::string& url) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
DCHECK(request_context_getter);
request_context_getter_ = request_context_getter;
request_context_getter_->AddObserver(this);
auto context = request_context_getter_->GetURLRequestContext();
if (!context) {
// Called after shutdown.
DoCancelWithError("Cannot start a request after shutdown.", true);
return;
}
DCHECK(context);
request_ = context->CreateRequest(
GURL(url), net::RequestPriority::DEFAULT_PRIORITY, this);
if (!request_) {
DoCancelWithError("Failed to create a net::URLRequest.", true);
return;
}
request_->set_method(method);
}
void AtomURLRequest::DoTerminate() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
request_.reset();
if (request_context_getter_) {
request_context_getter_->RemoveObserver(this);
request_context_getter_ = nullptr;
}
}
bool AtomURLRequest::Write(scoped_refptr<const net::IOBufferWithSize> buffer,
bool is_last) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
return content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&AtomURLRequest::DoWriteBuffer, this, buffer, is_last));
}
void AtomURLRequest::SetChunkedUpload(bool is_chunked_upload) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// The method can be called only before switching to multi-threaded mode,
// i.e. before the first call to write.
// So it is safe to change the object in the UI thread.
is_chunked_upload_ = is_chunked_upload;
}
void AtomURLRequest::Cancel() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
base::Bind(&AtomURLRequest::DoCancel, this));
}
void AtomURLRequest::SetExtraHeader(const std::string& name,
const std::string& value) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&AtomURLRequest::DoSetExtraHeader, this, name, value));
}
void AtomURLRequest::RemoveExtraHeader(const std::string& name) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&AtomURLRequest::DoRemoveExtraHeader, this, name));
}
void AtomURLRequest::PassLoginInformation(
const base::string16& username,
const base::string16& password) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (username.empty() || password.empty()) {
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&AtomURLRequest::DoCancelAuth, this));
} else {
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&AtomURLRequest::DoSetAuth, this, username, password));
}
}
void AtomURLRequest::DoWriteBuffer(
scoped_refptr<const net::IOBufferWithSize> buffer,
bool is_last) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!request_) {
return;
}
if (is_chunked_upload_) {
// Chunked encoding case.
bool first_call = false;
if (!chunked_stream_writer_) {
std::unique_ptr<net::ChunkedUploadDataStream> chunked_stream(
new net::ChunkedUploadDataStream(0));
chunked_stream_writer_ = chunked_stream->CreateWriter();
request_->set_upload(std::move(chunked_stream));
first_call = true;
}
if (buffer)
// Non-empty buffer.
chunked_stream_writer_->AppendData(buffer->data(), buffer->size(),
is_last);
else if (is_last)
// Empty buffer and last chunk, i.e. request.end().
chunked_stream_writer_->AppendData(nullptr, 0, true);
if (first_call) {
request_->Start();
}
} else {
if (buffer) {
// Handling potential empty buffers.
using internal::UploadOwnedIOBufferElementReader;
auto element_reader =
UploadOwnedIOBufferElementReader::CreateWithBuffer(std::move(buffer));
upload_element_readers_.push_back(
std::unique_ptr<net::UploadElementReader>(element_reader));
}
if (is_last) {
auto elements_upload_data_stream = new net::ElementsUploadDataStream(
std::move(upload_element_readers_), 0);
request_->set_upload(
std::unique_ptr<net::UploadDataStream>(elements_upload_data_stream));
request_->Start();
}
}
}
void AtomURLRequest::DoCancel() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (request_) {
request_->Cancel();
}
DoTerminate();
}
void AtomURLRequest::DoSetExtraHeader(const std::string& name,
const std::string& value) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!request_) {
return;
}
request_->SetExtraRequestHeaderByName(name, value, true);
}
void AtomURLRequest::DoRemoveExtraHeader(const std::string& name) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!request_) {
return;
}
request_->RemoveRequestHeaderByName(name);
}
void AtomURLRequest::DoSetAuth(const base::string16& username,
const base::string16& password) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!request_) {
return;
}
request_->SetAuth(net::AuthCredentials(username, password));
}
void AtomURLRequest::DoCancelAuth() const {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!request_) {
return;
}
request_->CancelAuth();
}
void AtomURLRequest::DoCancelWithError(const std::string& error,
bool isRequestError) {
DoCancel();
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
base::Bind(&AtomURLRequest::InformDelegateErrorOccured, this, error,
isRequestError));
}
void AtomURLRequest::OnAuthRequired(net::URLRequest* request,
net::AuthChallengeInfo* auth_info) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
base::Bind(&AtomURLRequest::InformDelegateAuthenticationRequired, this,
scoped_refptr<net::AuthChallengeInfo>(auth_info)));
}
void AtomURLRequest::OnResponseStarted(net::URLRequest* request) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!request_) {
return;
}
DCHECK_EQ(request, request_.get());
scoped_refptr<net::HttpResponseHeaders> response_headers =
request->response_headers();
const auto& status = request_->status();
if (status.is_success()) {
// Success or pending trigger a Read.
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
base::Bind(&AtomURLRequest::InformDelegateResponseStarted, this,
response_headers));
ReadResponse();
} else if (status.status() == net::URLRequestStatus::Status::FAILED) {
// Report error on Start.
DoCancelWithError(net::ErrorToString(status.ToNetError()), true);
}
// We don't report an error is the request is canceled.
}
void AtomURLRequest::ReadResponse() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
int bytes_read = -1;
if (request_->Read(response_read_buffer_.get(), kBufferSize, &bytes_read)) {
OnReadCompleted(request_.get(), bytes_read);
}
}
void AtomURLRequest::OnReadCompleted(net::URLRequest* request, int bytes_read) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!request_) {
return;
}
DCHECK_EQ(request, request_.get());
const auto status = request_->status();
bool response_error = false;
bool data_ended = false;
bool data_transfer_error = false;
do {
if (!status.is_success()) {
response_error = true;
break;
}
if (bytes_read == 0) {
data_ended = true;
break;
}
if (bytes_read < 0 || !CopyAndPostBuffer(bytes_read)) {
data_transfer_error = true;
break;
}
} while (
request_->Read(response_read_buffer_.get(), kBufferSize, &bytes_read));
if (response_error) {
DoCancelWithError(net::ErrorToString(status.ToNetError()), false);
} else if (data_ended) {
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
base::Bind(&AtomURLRequest::InformDelegateResponseCompleted, this));
DoTerminate();
} else if (data_transfer_error) {
// We abort the request on corrupted data transfer.
DoCancelWithError("Failed to transfer data from IO to UI thread.", false);
}
}
void AtomURLRequest::OnContextShuttingDown() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
DoCancel();
}
bool AtomURLRequest::CopyAndPostBuffer(int bytes_read) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
// data is only a wrapper for the asynchronous response_read_buffer_.
// Make a deep copy of payload and transfer ownership to the UI thread.
auto buffer_copy = new net::IOBufferWithSize(bytes_read);
memcpy(buffer_copy->data(), response_read_buffer_->data(), bytes_read);
return content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
base::Bind(&AtomURLRequest::InformDelegateResponseData, this,
buffer_copy));
}
void AtomURLRequest::InformDelegateAuthenticationRequired(
scoped_refptr<net::AuthChallengeInfo> auth_info) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (delegate_)
delegate_->OnAuthenticationRequired(auth_info);
}
void AtomURLRequest::InformDelegateResponseStarted(
scoped_refptr<net::HttpResponseHeaders> response_headers) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (delegate_)
delegate_->OnResponseStarted(response_headers);
}
void AtomURLRequest::InformDelegateResponseData(
scoped_refptr<net::IOBufferWithSize> data) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Transfer ownership of the data buffer, data will be released
// by the delegate's OnResponseData.
if (delegate_)
delegate_->OnResponseData(data);
}
void AtomURLRequest::InformDelegateResponseCompleted() const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (delegate_)
delegate_->OnResponseCompleted();
}
void AtomURLRequest::InformDelegateErrorOccured(const std::string& error,
bool isRequestError) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (delegate_)
delegate_->OnError(error, isRequestError);
}
} // namespace atom

View file

@ -0,0 +1,104 @@
// Copyright (c) 2016 GitHub, Inc.
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_NET_ATOM_URL_REQUEST_H_
#define ATOM_BROWSER_NET_ATOM_URL_REQUEST_H_
#include <string>
#include <vector>
#include "atom/browser/api/atom_api_url_request.h"
#include "atom/browser/atom_browser_context.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "net/base/auth.h"
#include "net/base/chunked_upload_data_stream.h"
#include "net/base/io_buffer.h"
#include "net/base/upload_element_reader.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context_getter_observer.h"
namespace atom {
class AtomURLRequest : public base::RefCountedThreadSafe<AtomURLRequest>,
public net::URLRequest::Delegate,
public net::URLRequestContextGetterObserver {
public:
static scoped_refptr<AtomURLRequest> Create(
AtomBrowserContext* browser_context,
const std::string& method,
const std::string& url,
api::URLRequest* delegate);
void Terminate();
bool Write(scoped_refptr<const net::IOBufferWithSize> buffer, bool is_last);
void SetChunkedUpload(bool is_chunked_upload);
void Cancel();
void SetExtraHeader(const std::string& name, const std::string& value) const;
void RemoveExtraHeader(const std::string& name) const;
void PassLoginInformation(const base::string16& username,
const base::string16& password) const;
protected:
// Overrides of net::URLRequest::Delegate
void OnAuthRequired(net::URLRequest* request,
net::AuthChallengeInfo* auth_info) override;
void OnResponseStarted(net::URLRequest* request) override;
void OnReadCompleted(net::URLRequest* request, int bytes_read) override;
// Overrides of net::URLRequestContextGetterObserver
void OnContextShuttingDown() override;
private:
friend class base::RefCountedThreadSafe<AtomURLRequest>;
explicit AtomURLRequest(api::URLRequest* delegate);
~AtomURLRequest() override;
void DoInitialize(scoped_refptr<net::URLRequestContextGetter>,
const std::string& method,
const std::string& url);
void DoTerminate();
void DoWriteBuffer(scoped_refptr<const net::IOBufferWithSize> buffer,
bool is_last);
void DoCancel();
void DoSetExtraHeader(const std::string& name,
const std::string& value) const;
void DoRemoveExtraHeader(const std::string& name) const;
void DoSetAuth(const base::string16& username,
const base::string16& password) const;
void DoCancelAuth() const;
void DoCancelWithError(const std::string& error, bool isRequestError);
void ReadResponse();
bool CopyAndPostBuffer(int bytes_read);
void InformDelegateAuthenticationRequired(
scoped_refptr<net::AuthChallengeInfo> auth_info) const;
void InformDelegateResponseStarted(
scoped_refptr<net::HttpResponseHeaders>) const;
void InformDelegateResponseData(
scoped_refptr<net::IOBufferWithSize> data) const;
void InformDelegateResponseCompleted() const;
void InformDelegateErrorOccured(const std::string& error,
bool isRequestError) const;
api::URLRequest* delegate_;
std::unique_ptr<net::URLRequest> request_;
scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
bool is_chunked_upload_;
std::unique_ptr<net::ChunkedUploadDataStream> chunked_stream_;
std::unique_ptr<net::ChunkedUploadDataStream::Writer> chunked_stream_writer_;
std::vector<std::unique_ptr<net::UploadElementReader>>
upload_element_readers_;
scoped_refptr<net::IOBuffer> response_read_buffer_;
DISALLOW_COPY_AND_ASSIGN(AtomURLRequest);
};
} // namespace atom
#endif // ATOM_BROWSER_NET_ATOM_URL_REQUEST_H_

View file

@ -47,9 +47,10 @@ void GetMenuBarColor(SkColor* enabled, SkColor* disabled, SkColor* highlight,
} // namespace
MenuBar::MenuBar()
MenuBar::MenuBar(NativeWindow* window)
: background_color_(kDefaultColor),
menu_model_(NULL) {
menu_model_(NULL),
window_(window) {
UpdateMenuBarColor();
SetLayoutManager(new views::BoxLayout(
views::BoxLayout::kHorizontal, 0, 0, 0));
@ -142,6 +143,9 @@ void MenuBar::OnMenuButtonClicked(views::MenuButton* source,
if (!menu_model_)
return;
if (!window_->IsFocused())
window_->Focus(true);
int id = source->tag();
AtomMenuModel::ItemType type = menu_model_->GetTypeAt(id);
if (type != AtomMenuModel::TYPE_SUBMENU) {

View file

@ -5,6 +5,7 @@
#ifndef ATOM_BROWSER_UI_VIEWS_MENU_BAR_H_
#define ATOM_BROWSER_UI_VIEWS_MENU_BAR_H_
#include "atom/browser/native_window.h"
#include "atom/browser/ui/atom_menu_model.h"
#include "ui/views/controls/button/menu_button_listener.h"
#include "ui/views/view.h"
@ -20,7 +21,7 @@ class MenuDelegate;
class MenuBar : public views::View,
public views::MenuButtonListener {
public:
MenuBar();
explicit MenuBar(NativeWindow* window);
virtual ~MenuBar();
// Replaces current menu with a new one.
@ -66,6 +67,7 @@ class MenuBar : public views::View,
SkColor hover_color_;
#endif
NativeWindow* window_;
AtomMenuModel* menu_model_;
DISALLOW_COPY_AND_ASSIGN(MenuBar);

View file

@ -52,10 +52,6 @@ SubmenuButton::SubmenuButton(const base::string16& title,
SetHasInkDrop(true);
set_ink_drop_base_color(
color_utils::BlendTowardOppositeLuma(background_color_, 0x61));
set_request_focus_on_press(true);
SetFocusForPlatform();
SetFocusPainter(nullptr);
}
SubmenuButton::~SubmenuButton() {