diff --git a/atom/browser/api/atom_api_session.cc b/atom/browser/api/atom_api_session.cc index 7d5f75cac09..7bc1a2153e3 100644 --- a/atom/browser/api/atom_api_session.cc +++ b/atom/browser/api/atom_api_session.cc @@ -19,6 +19,8 @@ #include "base/prefs/pref_service.h" #include "base/strings/string_util.h" #include "base/thread_task_runner_handle.h" +#include "brightray/browser/net/devtools_network_conditions.h" +#include "brightray/browser/net/devtools_network_controller.h" #include "chrome/common/pref_names.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/storage_partition.h" @@ -294,6 +296,43 @@ void Session::SetDownloadPath(const base::FilePath& path) { prefs::kDownloadDefaultDirectory, path); } +void Session::EnableNetworkEmulation(const mate::Dictionary& options) { + scoped_ptr conditions; + bool offline = false; + double latency, download_throughput, upload_throughput; + if (options.Get("offline", &offline) && offline) { + conditions.reset(new brightray::DevToolsNetworkConditions(offline)); + } else { + options.Get("latency", &latency); + options.Get("downloadThroughput", &download_throughput); + options.Get("uploadThroughput", &upload_throughput); + conditions.reset( + new brightray::DevToolsNetworkConditions(false, + latency, + download_throughput, + upload_throughput)); + } + auto controller = browser_context_->GetDevToolsNetworkController(); + + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, + base::Bind(&brightray::DevToolsNetworkController::SetNetworkState, + base::Unretained(controller), + std::string(), + base::Passed(&conditions))); +} + +void Session::DisableNetworkEmulation() { + scoped_ptr conditions( + new brightray::DevToolsNetworkConditions(false)); + auto controller = browser_context_->GetDevToolsNetworkController(); + + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, + base::Bind(&brightray::DevToolsNetworkController::SetNetworkState, + base::Unretained(controller), + std::string(), + base::Passed(&conditions))); +} + v8::Local Session::Cookies(v8::Isolate* isolate) { if (cookies_.IsEmpty()) { auto handle = atom::api::Cookies::Create(isolate, browser_context()); @@ -310,6 +349,8 @@ mate::ObjectTemplateBuilder Session::GetObjectTemplateBuilder( .SetMethod("clearStorageData", &Session::ClearStorageData) .SetMethod("setProxy", &Session::SetProxy) .SetMethod("setDownloadPath", &Session::SetDownloadPath) + .SetMethod("enableNetworkEmulation", &Session::EnableNetworkEmulation) + .SetMethod("disableNetworkEmulation", &Session::DisableNetworkEmulation) .SetProperty("cookies", &Session::Cookies); } diff --git a/atom/browser/api/atom_api_session.h b/atom/browser/api/atom_api_session.h index 14406e57af5..68ee3634e6c 100644 --- a/atom/browser/api/atom_api_session.h +++ b/atom/browser/api/atom_api_session.h @@ -20,6 +20,7 @@ class FilePath; namespace mate { class Arguments; +class Dictionary; } namespace atom { @@ -65,6 +66,8 @@ class Session: public mate::TrackableObject, void ClearStorageData(mate::Arguments* args); void SetProxy(const std::string& proxy, const base::Closure& callback); void SetDownloadPath(const base::FilePath& path); + void EnableNetworkEmulation(const mate::Dictionary& options); + void DisableNetworkEmulation(); v8::Local Cookies(v8::Isolate* isolate); // Cached object for cookies API. diff --git a/atom/browser/default_app/main.js b/atom/browser/default_app/main.js index 4d854dc35cb..1b92685d13a 100644 --- a/atom/browser/default_app/main.js +++ b/atom/browser/default_app/main.js @@ -13,8 +13,8 @@ app.on('window-all-closed', function() { // Parse command line options. var argv = process.argv.slice(1); -var option = { file: null, help: null, version: null, webdriver: null }; -for (var i in argv) { +var option = { file: null, help: null, version: null, webdriver: null, modules: [] }; +for (var i = 0; i < argv.length; i++) { if (argv[i] == '--version' || argv[i] == '-v') { option.version = true; break; @@ -23,6 +23,9 @@ for (var i in argv) { break; } else if (argv[i] == '--test-type=webdriver') { option.webdriver = true; + } else if (argv[i] == '--require' || argv[i] == '-r') { + option.modules.push(argv[++i]); + continue; } else if (argv[i][0] == '-') { continue; } else { @@ -212,6 +215,10 @@ app.once('ready', function() { Menu.setApplicationMenu(menu); }); +if (option.modules.length > 0) { + require('module')._preloadModules(option.modules); +} + // Start the specified app if there is one specified in command line, otherwise // start the default app. if (option.file && !option.webdriver) { @@ -253,6 +260,7 @@ if (option.file && !option.webdriver) { helpMessage += "A path to an Electron application may be specified. The path must be to \n"; helpMessage += "an index.js file or to a folder containing a package.json or index.js file.\n\n"; helpMessage += "Options:\n"; + helpMessage += " -r, --require Module to preload (option can be repeated)"; helpMessage += " -h, --help Print this usage message.\n"; helpMessage += " -v, --version Print the version."; console.log(helpMessage); diff --git a/atom/browser/native_window_views.cc b/atom/browser/native_window_views.cc index 70707219f3f..34c2f622631 100644 --- a/atom/browser/native_window_views.cc +++ b/atom/browser/native_window_views.cc @@ -169,9 +169,6 @@ NativeWindowViews::NativeWindowViews( menu_bar_autohide_(false), menu_bar_visible_(false), menu_bar_alt_pressed_(false), -#if defined(OS_WIN) - is_minimized_(false), -#endif keyboard_event_handler_(new views::UnhandledKeyboardEventHandler), use_content_size_(false), resizable_(true) { @@ -228,6 +225,9 @@ NativeWindowViews::NativeWindowViews( window_->Init(params); + bool fullscreen = false; + options.Get(switches::kFullscreen, &fullscreen); + #if defined(USE_X11) // Start monitoring window states. window_state_watcher_.reset(new WindowStateWatcher(this)); @@ -253,8 +253,7 @@ NativeWindowViews::NativeWindowViews( } // Before the window is mapped, there is no SHOW_FULLSCREEN_STATE. - bool fullscreen = false; - if (options.Get(switches::kFullscreen, & fullscreen) && fullscreen) { + if (fullscreen) { state_atom_list.push_back(GetAtom("_NET_WM_STATE_FULLSCREEN")); } @@ -278,6 +277,12 @@ NativeWindowViews::NativeWindowViews( bounds = ContentBoundsToWindowBounds(bounds); #if defined(OS_WIN) + // Save initial window state. + if (fullscreen) + last_window_state_ = ui::SHOW_STATE_FULLSCREEN; + else + last_window_state_ = ui::SHOW_STATE_NORMAL; + if (!has_frame()) { // Set Window style so that we get a minimize and maximize animation when // frameless. @@ -391,11 +396,16 @@ bool NativeWindowViews::IsMinimized() { void NativeWindowViews::SetFullScreen(bool fullscreen) { #if defined(OS_WIN) // There is no native fullscreen state on Windows. - window_->SetFullscreen(fullscreen); - if (fullscreen) + if (fullscreen) { + last_window_state_ = ui::SHOW_STATE_FULLSCREEN; NotifyWindowEnterFullScreen(); - else + } else { + last_window_state_ = ui::SHOW_STATE_NORMAL; NotifyWindowLeaveFullScreen(); + } + // We set the new value after notifying, so we can handle the size event + // correctly. + window_->SetFullscreen(fullscreen); #else if (IsVisible()) window_->SetFullscreen(fullscreen); @@ -807,24 +817,8 @@ void NativeWindowViews::OnWidgetMove() { #if defined(OS_WIN) bool NativeWindowViews::ExecuteWindowsCommand(int command_id) { - // Windows uses the 4 lower order bits of |command_id| for type-specific - // information so we must exclude this when comparing. - static const int sc_mask = 0xFFF0; - if ((command_id & sc_mask) == SC_MINIMIZE) { - NotifyWindowMinimize(); - is_minimized_ = true; - } else if ((command_id & sc_mask) == SC_RESTORE) { - if (is_minimized_) - NotifyWindowRestore(); - else - NotifyWindowUnmaximize(); - is_minimized_ = false; - } else if ((command_id & sc_mask) == SC_MAXIMIZE) { - NotifyWindowMaximize(); - } else { - std::string command = AppCommandToString(command_id); - NotifyWindowExecuteWindowsCommand(command); - } + std::string command = AppCommandToString(command_id); + NotifyWindowExecuteWindowsCommand(command); return false; } #endif @@ -844,11 +838,54 @@ void NativeWindowViews::GetDevToolsWindowWMClass( #if defined(OS_WIN) bool NativeWindowViews::PreHandleMSG( UINT message, WPARAM w_param, LPARAM l_param, LRESULT* result) { - // Handle thumbar button click message. - if (message == WM_COMMAND && HIWORD(w_param) == THBN_CLICKED) - return taskbar_host_.HandleThumbarButtonEvent(LOWORD(w_param)); - else - return false; + switch (message) { + case WM_COMMAND: + // Handle thumbar button click message. + if (HIWORD(w_param) == THBN_CLICKED) + return taskbar_host_.HandleThumbarButtonEvent(LOWORD(w_param)); + return false; + case WM_SIZE: + // Handle window state change. + HandleSizeEvent(w_param, l_param); + return false; + default: + return false; + } +} + +void NativeWindowViews::HandleSizeEvent(WPARAM w_param, LPARAM l_param) { + // Here we handle the WM_SIZE event in order to figure out what is the current + // window state and notify the user accordingly. + switch (w_param) { + case SIZE_MAXIMIZED: + last_window_state_ = ui::SHOW_STATE_MAXIMIZED; + NotifyWindowMaximize(); + break; + case SIZE_MINIMIZED: + last_window_state_ = ui::SHOW_STATE_MINIMIZED; + NotifyWindowMinimize(); + break; + case SIZE_RESTORED: + if (last_window_state_ == ui::SHOW_STATE_NORMAL) + return; + + 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; + NotifyWindowRestore(); + } + break; + } + break; + } } #endif diff --git a/atom/browser/native_window_views.h b/atom/browser/native_window_views.h index 355f5bd38ef..70c722c7d9c 100644 --- a/atom/browser/native_window_views.h +++ b/atom/browser/native_window_views.h @@ -142,6 +142,8 @@ class NativeWindowViews : public NativeWindow, // MessageHandlerDelegate: bool PreHandleMSG( UINT message, WPARAM w_param, LPARAM l_param, LRESULT* result) override; + + void HandleSizeEvent(WPARAM w_param, LPARAM l_param); #endif // NativeWindow: @@ -178,9 +180,9 @@ class NativeWindowViews : public NativeWindow, #elif defined(OS_WIN) // Weak ref. AtomDesktopWindowTreeHostWin* atom_desktop_window_tree_host_win_; - // Records window was whether restored from minimized state or maximized - // state. - bool is_minimized_; + + ui::WindowShowState last_window_state_; + // In charge of running taskbar related APIs. TaskbarHost taskbar_host_; #endif diff --git a/atom/browser/ui/file_dialog_win.cc b/atom/browser/ui/file_dialog_win.cc index b169471c6e9..da00dc54e2f 100644 --- a/atom/browser/ui/file_dialog_win.cc +++ b/atom/browser/ui/file_dialog_win.cc @@ -79,6 +79,9 @@ class FileDialog { if (!title.empty()) GetPtr()->SetTitle(base::UTF8ToUTF16(title).c_str()); + if (!filterspec.empty()) + GetPtr()->SetDefaultExtension(filterspec.front().pszSpec); + SetDefaultFolder(default_path); } diff --git a/atom/common/native_mate_converters/blink_converter.cc b/atom/common/native_mate_converters/blink_converter.cc index 67c7e7e95fd..0a599bfdf01 100644 --- a/atom/common/native_mate_converters/blink_converter.cc +++ b/atom/common/native_mate_converters/blink_converter.cc @@ -9,6 +9,7 @@ #include "atom/common/keyboad_util.h" #include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" #include "content/public/browser/native_web_keyboard_event.h" #include "native_mate/dictionary.h" #include "third_party/WebKit/public/web/WebDeviceEmulationParams.h" @@ -29,10 +30,10 @@ int VectorToBitArray(const std::vector& vec) { namespace mate { template<> -struct Converter { +struct Converter { static bool FromV8(v8::Isolate* isolate, v8::Handle val, - char* out) { - std::string code = base::StringToLowerASCII(V8ToString(val)); + base::char16* out) { + base::string16 code = base::UTF8ToUTF16(V8ToString(val)); if (code.length() != 1) return false; *out = code[0]; @@ -77,6 +78,21 @@ struct Converter { } }; +template<> +struct Converter { + static bool FromV8(v8::Isolate* isolate, v8::Handle val, + blink::WebMouseEvent::Button* out) { + std::string button = base::StringToLowerASCII(V8ToString(val)); + if (button == "left") + *out = blink::WebMouseEvent::Button::ButtonLeft; + else if (button == "middle") + *out = blink::WebMouseEvent::Button::ButtonMiddle; + else if (button == "right") + *out = blink::WebMouseEvent::Button::ButtonRight; + return true; + } +}; + template<> struct Converter { static bool FromV8(v8::Isolate* isolate, v8::Handle val, @@ -142,16 +158,19 @@ bool Converter::FromV8( return false; if (!ConvertFromV8(isolate, val, static_cast(out))) return false; - char code; + base::char16 code; if (!dict.Get("keyCode", &code)) return false; bool shifted = false; out->windowsKeyCode = atom::KeyboardCodeFromCharCode(code, &shifted); - if (out->windowsKeyCode == ui::VKEY_UNKNOWN) - return false; if (shifted) out->modifiers |= blink::WebInputEvent::ShiftKey; out->setKeyIdentifierFromWindowsKeyCode(); + if (out->type == blink::WebInputEvent::Char || + out->type == blink::WebInputEvent::RawKeyDown) { + out->text[0] = code; + out->unmodifiedText[0] = code; + } return true; } @@ -176,6 +195,7 @@ bool Converter::FromV8( return false; if (!dict.Get("x", &out->x) || !dict.Get("y", &out->y)) return false; + dict.Get("button", &out->button); dict.Get("globalX", &out->globalX); dict.Get("globalY", &out->globalY); dict.Get("movementX", &out->movementX); diff --git a/docs/api/session.md b/docs/api/session.md index 64991ab7530..392b6826414 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -191,3 +191,30 @@ proxy-uri = ["://"][":"] Sets download saving directory. By default, the download directory will be the `Downloads` under the respective app folder. + +### `session.enableNetworkEmulation(options)` + +* `options` Object + * `offline` Boolean - Whether to emulate network outage. + * `latency` Double - RTT in ms + * `downloadThroughput` Double - Download rate in Bps + * `uploadThroughput` Double - Upload rate in Bps + +Emulates network with the given configuration for the `session`. + +```javascript +// To emulate a GPRS connection with 50kbps throughput and 500 ms latency. +window.webContents.session.enableNetworkEmulation({ + latency: 500, + downloadThroughput: 6400, + uploadThroughput: 6400 +}); + +// To emulate a network outage. +window.webContents.session.enableNetworkEmulation({offline: true}); +``` + +### `session.disableNetworkEmulation` + +Disables any network emulation already active for the `session`. Resets to +the original network configuration. diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index c2dccc3a876..7cbf8d93ba2 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -534,13 +534,13 @@ Sends an input `event` to the page. For keyboard events, the `event` object also have following properties: * `keyCode` String (**required**) - A single character that will be sent as - keyboard event. Can be any ASCII character on the keyboard, like `a`, `1` - and `=`. + keyboard event. Can be any UTF-8 character. For mouse events, the `event` object also have following properties: * `x` Integer (**required**) * `y` Integer (**required**) +* `button` String - The button pressed, can be `left`, `middle`, `right` * `globalX` Integer * `globalY` Integer * `movementX` Integer @@ -565,8 +565,11 @@ For the `mouseWheel` event, the `event` object also have following properties: Begin subscribing for presentation events and captured frames, the `callback` will be called with `callback(frameBuffer)` when there is a presentation event. -The `frameBuffer` is a `Buffer` that contains raw pixel data, in the format of -32bit ARGB. +The `frameBuffer` is a `Buffer` that contains raw pixel data. On most machines, +the pixel data is effectively stored in 32bit BGRA format, but the actual +representation depends on the endianness of the processor (most modern +processors are little-endian, on machines with big-endian processors the data +is in 32bit ARGB format). ### `webContents.endFrameSubscription()` diff --git a/docs/tutorial/quick-start.md b/docs/tutorial/quick-start.md index 4ce65a1dc1a..94368df09c1 100644 --- a/docs/tutorial/quick-start.md +++ b/docs/tutorial/quick-start.md @@ -143,8 +143,8 @@ working as expected. ### electron-prebuilt -If you've installed `electron-prebuilt` globally with `npm`, then you need only -run the following in your app's source directory: +If you've installed `electron-prebuilt` globally with `npm`, then you will only need +to run the following in your app's source directory: ```bash electron . @@ -158,7 +158,7 @@ If you've installed it locally, then run: ### Manually Downloaded Electron Binary -If you downloaded Electron manually, you can also just use the included +If you downloaded Electron manually, you can also use the included binary to execute your app directly. #### Windows diff --git a/package.json b/package.json index 101fdeee257..a125d13b68e 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "electron", "devDependencies": { - "asar": "0.8.x", + "asar": "^0.8.0", "coffee-script": "^1.9.2", "coffeelint": "^1.9.4", "request": "*", - "runas": "3.x" + "runas": "^3.0.0" }, "private": true, "scripts": { diff --git a/vendor/brightray b/vendor/brightray index 8e443520e69..75f7d3fd88a 160000 --- a/vendor/brightray +++ b/vendor/brightray @@ -1 +1 @@ -Subproject commit 8e443520e695674fd26585cfa24a0ec0b6140c27 +Subproject commit 75f7d3fd88ae60026a0717b93e3bf7182f827dc3 diff --git a/vendor/node b/vendor/node index ac25693ad1d..f4243f5c84a 160000 --- a/vendor/node +++ b/vendor/node @@ -1 +1 @@ -Subproject commit ac25693ad1d4c248e69a89147fd3995c3bf6c946 +Subproject commit f4243f5c84a371632d8d72a1a2210a0e994afdcc