diff --git a/AUTHORS b/AUTHORS index c468b7237c..0679037506 100644 --- a/AUTHORS +++ b/AUTHORS @@ -33,4 +33,7 @@ Cong Liu Eric Newport Marco Fabbri Daniel Braun -Chase Willden \ No newline at end of file +Chase Willden +Anton Khlynovskiy +Wu Yuehang +Rick Edgecombe diff --git a/CHANGELOG.md b/CHANGELOG.md index 0269106140..55766cde31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +0.12.3 / 07-31-2015 +=================== +- Support Mac App Store with the 'macappstore' flavor +- [WIN] Screen.DesktopCaptureMonitor API: https://github.com/nwjs/nw.js/wiki/Screen#screendesktopcapturemonitor (Thanks to Rick Edgecombe) +- [HighDPI][WIN] fix for Tray menu is huge on High-DPI Windows machine (#2847) (Thanks to Jefry) + +0.12.2 / 05-22-2015 +=================== +- Fix #2723: [OSX] cpu hog in some cases +- Fix #3361: application cache +- Fix #2720: [Linux] launching sudo hits error: effective uid is not 0 +- Fix #2819: enable cookie support for web sockets +- Fix #2713: crash with 'new-win-policy' and opening window from iframe +- Fix #3123: support no-displaying-insecure-content and allow-running-insecure-content +- [Screen Selection] add application name to the UI; cancelChooseDesktopMedia implementation +- [Notification] [WIN] disable audio for toast notification, better fallback for toast notification +- Change cache backend from "simple" to "blockfile" + +0.12.1 / 04-13-2015 +=================== +- Fix crash dump generation +- [WIN] Fix blurry text with High DPI display +- Fix: Webview : contentWindow not available at this time (#3126) +- More precise RegExp for App.argv filtering (Thanks to Anton Khlynovskiy) +- Fix #3143: remote debugging devtools page blank (Thanks to Yuehang Wu) +- [Notification][Win] fix for missing windows events +- add Window 'progress' event (Thanks to vadim-kudr ) +- nw-headers is built automatically now in buildbot (Thanks to Xue Yang) + 0.12.0 / 03-05-2015 ======================= - Chromium updated to 41.0.2272.76 diff --git a/LICENSE b/LICENSE index 3a4009653d..ba52b7c91d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ -Copyright (c) 2012-2014 Intel Corp -Copyright (c) 2012-2014 The Chromium Authors +Copyright (c) 2012-2015 Intel Corp +Copyright (c) 2012-2015 The Chromium Authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in th diff --git a/README.md b/README.md index 8881261964..5f1c5ba13c 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,16 @@ It was created in the Intel Open Source Technology Center. * Available on Linux, Mac OS X and Windows ## Downloads -* **v0.12.0:** (Mar 5, 2015, based off of IO.js v1.2.0, Chromium 41.0.2272.76): [release notes](https://groups.google.com/d/msg/nwjs-general/NJA-Up4MFug/1jmBPjzklSUJ) - * Linux: [32bit](http://dl.nwjs.io/v0.12.0/nwjs-v0.12.0-linux-ia32.tar.gz) / [64bit](http://dl.nwjs.io/v0.12.0/nwjs-v0.12.0-linux-x64.tar.gz) - * Windows: [32bit](http://dl.nwjs.io/v0.12.0/nwjs-v0.12.0-win-ia32.zip) / [64bit](http://dl.nwjs.io/v0.12.0/nwjs-v0.12.0-win-x64.zip) - * Mac 10.7+: [32bit](http://dl.nwjs.io/v0.12.0/nwjs-v0.12.0-osx-ia32.zip) / [64bit](http://dl.nwjs.io/v0.12.0/nwjs-v0.12.0-osx-x64.zip) +* **v0.12.3:** (Jul 31, 2015, based off of IO.js v1.2.0, Chromium 41.0.2272.76): [release notes](https://groups.google.com/d/msg/nwjs-general/hhXCS4aXGV0/TUQmcu5XDwAJ) + * Linux: [32bit](http://dl.nwjs.io/v0.12.3/nwjs-v0.12.3-linux-ia32.tar.gz) / [64bit](http://dl.nwjs.io/v0.12.3/nwjs-v0.12.3-linux-x64.tar.gz) + * Windows: [32bit](http://dl.nwjs.io/v0.12.3/nwjs-v0.12.3-win-ia32.zip) / [64bit](http://dl.nwjs.io/v0.12.3/nwjs-v0.12.3-win-x64.zip) + * Mac 10.7+: [32bit](http://dl.nwjs.io/v0.12.3/nwjs-v0.12.3-osx-ia32.zip) / [64bit](http://dl.nwjs.io/v0.12.3/nwjs-v0.12.3-osx-x64.zip) + +* **v0.13.0-alpha1:** (Jun 11, 2015, based off of IO.js v1.5.1, Chromium 43.0.2357.45): [release notes](https://groups.google.com/d/msg/nwjs-general/c25l_jGMqj8/rsAtdSQuxeUJ) + **NOTE** You might want the **SDK build**. Please read the release notes + * Linux: [32bit](http://dl.nwjs.io/v0.13.0/alpha1/nwjs-v0.13.0-alpha1-linux-ia32.tar.gz) / [64bit](http://dl.nwjs.io/v0.13.0/alpha1/nwjs-v0.13.0-alpha1-linux-x64.tar.gz) + * Windows: [32bit](http://dl.nwjs.io/v0.13.0/alpha1/nwjs-v0.13.0-alpha1-win-ia32.zip) / [64bit](http://dl.nwjs.io/v0.13.0/alpha1/nwjs-v0.13.0-alpha1-win-x64.zip) + * Mac 10.7+: [32bit](http://dl.nwjs.io/v0.13.0/alpha1/nwjs-v0.13.0-alpha1-osx-ia32.zip) / [64bit](http://dl.nwjs.io/v0.13.0/alpha1/nwjs-v0.13.0-alpha1-osx-x64.zip) * **0.8.6:** (Apr 18, 2014, based off of Node v0.10.22, Chrome 30.0.1599.66) **If your native Node module works only with Node v0.10, then you should use node-webkit v0.8.x, which is also a maintained branch. [More info](https://groups.google.com/d/msg/nwjs-general/2OJ1cEMPLlA/09BvpTagSA0J)** [release notes](https://groups.google.com/d/msg/nwjs-general/CLPkgfV-i7s/hwkkQuJ1kngJ) @@ -38,7 +44,7 @@ It was created in the Intel Open Source Technology Center. * Windows: [win32](http://dl.node-webkit.org/v0.8.6/node-webkit-v0.8.6-win-ia32.zip) * Mac: [32bit, 10.7+](http://dl.node-webkit.org/v0.8.6/node-webkit-v0.8.6-osx-ia32.zip) -* **latest live build**: git tip version; build triggered from every git commit: http://dl.node-webkit.org/live-build/ +* **latest live build**: git tip version; build triggered from every git commit: http://dl.nwjs.io/live-build/ * [Previous versions](https://github.com/rogerwang/node-webkit/wiki/Downloads-of-old-versions) @@ -67,6 +73,7 @@ Create `package.json`: ```json { "name": "nw-demo", + "version": "0.0.1", "main": "index.html" } ``` @@ -78,10 +85,9 @@ $ /path/to/nw . (suppose the current directory contains 'package.json') Note: on Windows, you can drag the folder containing `package.json` to `nw.exe` to open it. -Note: on OSX, the executable binary is in a hidden directory within the .app file. To run node-webkit on OSX, type: -```bash -$ /path/to/node-webkit.app/Contents/MacOS/node-webkit . (suppose the current directory contains 'package.json') -``` +Note: on OSX, the executable binary is in a hidden directory within the .app file. To run node-webkit on OSX, type: +`/path/to/nwjs.app/Contents/MacOS/nwjs .` *(suppose the current directory contains 'package.json')* + ## Documents For more information on how to write/package/run apps, see: diff --git a/nw.gypi b/nw.gypi index da4cfcce2a..0b4bc0f863 100644 --- a/nw.gypi +++ b/nw.gypi @@ -207,6 +207,8 @@ 'src/api/event/event.cc', 'src/api/screen/desktop_capture_api.h', 'src/api/screen/desktop_capture_api.cc', + 'src/api/screen/desktop_capture_monitor.cc', + 'src/api/screen/desktop_capture_monitor.h', '<(DEPTH)/chrome/browser/media/desktop_media_list.h', '<(DEPTH)/chrome/browser/media/desktop_media_list_observer.h', '<(DEPTH)/chrome/browser/media/desktop_media_picker.h', @@ -912,6 +914,15 @@ { 'target_name': 'dist', 'type': 'none', + 'variables': { + 'conditions': [ + ['nwjs_mas==1', { + 'package_mode': 'mas', + }, { + 'package_mode': 'regular', + }], + ], # conditions + }, # variables 'actions': [ { 'action_name': 'package_nw_binaries', @@ -924,7 +935,7 @@ 'outputs':[ '<(PRODUCT_DIR)/new_package.re', ], - 'action': ['python', '<(package_script)', '-p', '<(PRODUCT_DIR)', '-a', '<(target_arch)'], + 'action': ['python', '<(package_script)', '-p', '<(PRODUCT_DIR)', '-a', '<(target_arch)', '-m', '<(package_mode)'], }, ], 'dependencies': [ diff --git a/src/api/app/app.cc b/src/api/app/app.cc index 30f4b6d084..1207db7410 100644 --- a/src/api/app/app.cc +++ b/src/api/app/app.cc @@ -202,9 +202,10 @@ void App::Call(Shell* shell, shortcut->GetAccelerator(), shortcut); return; } else if (method == "SetProxyConfig") { - std::string proxy_config; + std::string proxy_config, pac_url; arguments.GetString(0, &proxy_config); - SetProxyConfig(GetRenderProcessHost(), proxy_config); + arguments.GetString(1, &pac_url); + SetProxyConfig(GetRenderProcessHost(), proxy_config, pac_url); return; } else if (method == "DoneMenuShow") { dispatcher_host->quit_run_loop(); @@ -299,9 +300,18 @@ void App::ClearCache(content::RenderProcessHost* render_process_host) { } void App::SetProxyConfig(content::RenderProcessHost* render_process_host, - const std::string& proxy_config) { + const std::string& proxy_config, + const std::string& pac_url) { net::ProxyConfig config; - config.proxy_rules().ParseFromString(proxy_config); + if (!pac_url.empty()) { + if (pac_url == "") + config = net::ProxyConfig::CreateDirect(); + else if (pac_url == "") + config = net::ProxyConfig::CreateAutoDetect(); + else + config = net::ProxyConfig::CreateFromCustomPacURL(GURL(pac_url)); + } else + config.proxy_rules().ParseFromString(proxy_config); net::URLRequestContextGetter* context_getter = render_process_host->GetBrowserContext()-> GetRequestContextForRenderProcess(render_process_host->GetID()); diff --git a/src/api/app/app.h b/src/api/app/app.h index 47040ff6dc..969b01fadc 100644 --- a/src/api/app/app.h +++ b/src/api/app/app.h @@ -63,7 +63,8 @@ class App { static void ClearCache(content::RenderProcessHost* render_view_host); static void SetProxyConfig(content::RenderProcessHost* render_process_host, - const std::string& proxy_config); + const std::string& proxy_config, + const std::string& pac_url); private: App(); diff --git a/src/api/app/app.js b/src/api/app/app.js index 32c397f448..7ff009ffe3 100644 --- a/src/api/app/app.js +++ b/src/api/app/app.js @@ -26,10 +26,10 @@ function App() { require('util').inherits(App, exports.Base); App.filteredArgv = [ - /--no-toolbar/, - /--url=.*/, - /--remote-debugging-port=.*/, - /--renderer-cmd-prefix.*/, + /^--no-toolbar$/, + /^--url=/, + /^--remote-debugging-port=/, + /^--renderer-cmd-prefix/, ]; App.prototype.quit = function() { @@ -69,8 +69,8 @@ App.prototype.getProxyForURL = function (url) { return nw.callStaticMethodSync('App', 'getProxyForURL', [ url ]); } -App.prototype.setProxyConfig = function (proxy_config) { - return nw.callStaticMethodSync('App', 'SetProxyConfig', [ proxy_config ]); +App.prototype.setProxyConfig = function (proxy_config, pac_url) { + return nw.callStaticMethodSync('App', 'SetProxyConfig', [ proxy_config, pac_url ]); } App.prototype.addOriginAccessWhitelistEntry = function(sourceOrigin, destinationProtocol, destinationHost, allowDestinationSubdomains) { diff --git a/src/api/dispatcher.cc b/src/api/dispatcher.cc index 4238ea03d8..edfc4965ae 100644 --- a/src/api/dispatcher.cc +++ b/src/api/dispatcher.cc @@ -217,8 +217,10 @@ void Dispatcher::willHandleNavigationPolicy( v8::HandleScope handleScope(isolate); v8::Handle id_val; - if (web_view->mainFrame() && !web_view->mainFrame()->mainWorldScriptContext().IsEmpty()) + if (web_view->mainFrame() && !web_view->mainFrame()->mainWorldScriptContext().IsEmpty()) { + v8::Context::Scope cscope (web_view->mainFrame()->mainWorldScriptContext()); id_val = nwapi::Dispatcher::GetWindowId(web_view->mainFrame()); + } if (id_val.IsEmpty()) return; if (id_val->IsUndefined() || id_val->IsNull()) diff --git a/src/api/dispatcher_host.cc b/src/api/dispatcher_host.cc index f5f25e7936..dbbcd7b86c 100644 --- a/src/api/dispatcher_host.cc +++ b/src/api/dispatcher_host.cc @@ -34,6 +34,7 @@ #include "content/nw/src/api/menu/menu.h" #include "content/nw/src/api/menuitem/menuitem.h" #include "content/nw/src/api/screen/screen.h" +#include "content/nw/src/api/screen/desktop_capture_monitor.h" #include "content/nw/src/api/shell/shell.h" #include "content/nw/src/api/shortcut/shortcut.h" #include "content/nw/src/api/tray/tray.h" @@ -171,6 +172,8 @@ void DispatcherHost::OnAllocateObject(int object_id, objects_registry_.AddWithID(new Shortcut(object_id, weak_ptr_factory_.GetWeakPtr(), option), object_id); } else if (type == "Screen") { objects_registry_.AddWithID(new EventListener(object_id, weak_ptr_factory_.GetWeakPtr(), option), object_id); + }else if (type == "DesktopCaptureMonitor") { + objects_registry_.AddWithID(new DesktopCaptureMonitor(object_id, weak_ptr_factory_.GetWeakPtr(), option), object_id); } else { LOG(ERROR) << "Allocate an object of unknown type: " << type; objects_registry_.AddWithID(new Base(object_id, weak_ptr_factory_.GetWeakPtr(), option), object_id); diff --git a/src/api/screen/desktop_capture_api.cc b/src/api/screen/desktop_capture_api.cc index 7a03e36fbb..91339c79b6 100644 --- a/src/api/screen/desktop_capture_api.cc +++ b/src/api/screen/desktop_capture_api.cc @@ -12,6 +12,8 @@ #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" +#include "content/nw/src/nw_package.h" +#include "content/nw/src/nw_shell.h" #include "net/base/net_util.h" #include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h" #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" @@ -68,7 +70,8 @@ bool DesktopCaptureChooseDesktopMediaFunction::RunSync() { // also be used to determine where to show the picker's UI. content::WebContents* web_contents = content::WebContents::FromRenderViewHost(render_view_host()); DCHECK(web_contents); - base::string16 target_name; + content::Shell* shell = content::Shell::windows()[0]; + base::string16 app_name = base::UTF8ToUTF16(shell->GetPackage()->GetName()); // Register to be notified when the tab is closed. Observe(web_contents); @@ -136,8 +139,8 @@ bool DesktopCaptureChooseDesktopMediaFunction::RunSync() { picker_->Show(web_contents, parent_window, parent_window, - target_name, - target_name, + app_name, + app_name, media_list.Pass(), callback); return true; diff --git a/src/api/screen/desktop_capture_monitor.cc b/src/api/screen/desktop_capture_monitor.cc new file mode 100644 index 0000000000..6afa1e291d --- /dev/null +++ b/src/api/screen/desktop_capture_monitor.cc @@ -0,0 +1,169 @@ +// Copyright (c) 2012 Intel Corp +// Copyright (c) 2012 The Chromium Authors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell co +// pies of the Software, and to permit persons to whom the Software is furnished +// to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in al +// l copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM +// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES +// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +// OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH +// ETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#include "content/nw/src/api/screen/desktop_capture_monitor.h" + +#include "base/values.h" +#include "base/strings/utf_string_conversions.h" +#include "base/strings/string16.h" +#include "content/nw/src/api/dispatcher_host.h" +#include "chrome/browser/media/desktop_streams_registry.h" +#include "chrome/browser/media/media_capture_devices_dispatcher.h" + +#include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h" +#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" +#include "third_party/webrtc/modules/desktop_capture/window_capturer.h" + +#include "base/base64.h" +#include "ui/gfx/codec/png_codec.h" +#include "ui/gfx/image/image.h" +#include "ui/gfx/image/image_skia.h" + +#ifdef _WIN32 +#include +#endif + +namespace nwapi { + +DesktopCaptureMonitor::DesktopCaptureMonitor(int id, + const base::WeakPtr& dispatcher_host, + const base::DictionaryValue& option) + : Base(id, dispatcher_host, option) { +} + +DesktopCaptureMonitor::~DesktopCaptureMonitor() {} + +int DesktopCaptureMonitor::GetPrimaryMonitorIndex(){ +#ifdef _WIN32 + int count=0; + for (int i = 0;; ++i) { + DISPLAY_DEVICE device; + device.cb = sizeof(device); + BOOL ret = EnumDisplayDevices(NULL, i, &device, 0); + if(!ret) + break; + if (device.StateFlags & DISPLAY_DEVICE_ACTIVE){ + if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE){ + return count; + } + count++; + } + } +#endif + return -1; +} + +void DesktopCaptureMonitor::CallSync(const std::string& method, const base::ListValue& arguments, base::ListValue* result){ + if (method == "start") { + bool screens, windows; + if (arguments.GetBoolean(0, &screens) && arguments.GetBoolean(1, &windows)) + Start(screens, windows); + }else if (method == "stop") { + Stop(); + } + else { + NOTREACHED() << "Invalid call to DesktopCapture method:" << method + << " arguments:" << arguments; + } +} + +void DesktopCaptureMonitor::Start(bool screens, bool windows) { + webrtc::DesktopCaptureOptions options = webrtc::DesktopCaptureOptions::CreateDefault(); + options.set_disable_effects(false); + scoped_ptr screen_capturer(screens ? webrtc::ScreenCapturer::Create(options) : NULL); + scoped_ptr window_capturer(windows ? webrtc::WindowCapturer::Create(options) : NULL); + + media_list_.reset(new NativeDesktopMediaList(screen_capturer.Pass(), window_capturer.Pass())); + + media_list_->StartUpdating(this); +} + +void DesktopCaptureMonitor::Stop() { + media_list_.reset(); +} + +void DesktopCaptureMonitor::OnSourceAdded(int index){ + DesktopMediaList::Source src = media_list_->GetSource(index); + + std::string type; + if (src.id.type == content::DesktopMediaID::TYPE_AURA_WINDOW || src.id.type == content::DesktopMediaID::TYPE_WINDOW){ + type = "window"; + } + else if (src.id.type == content::DesktopMediaID::TYPE_SCREEN){ + type = "screen"; + } + else if (src.id.type == content::DesktopMediaID::TYPE_NONE){ + type = "none"; + } + else{ + type = "unknown"; + } + + base::ListValue param; + param.AppendString(src.id.ToString()); + param.AppendString(src.name); + param.AppendInteger(index); + param.AppendString(type); + if(src.id.type == content::DesktopMediaID::TYPE_SCREEN){ + param.AppendBoolean(GetPrimaryMonitorIndex()==index); + } + this->dispatcher_host()->SendEvent(this, "__nw_desktop_capture_monitor_listner_added", param); +} +void DesktopCaptureMonitor::OnSourceRemoved(int index){ + base::ListValue param; + param.AppendInteger(index); //pass by index here, because the information about which ID was at that index is lost before the removed callback is called. Its saved in the javascript though, so we can look it up there + this->dispatcher_host()->SendEvent(this, "__nw_desktop_capture_monitor_listner_removed", param); +} +void DesktopCaptureMonitor::OnSourceMoved(int old_index, int new_index){ + DesktopMediaList::Source src = media_list_->GetSource(new_index); + base::ListValue param; + param.AppendString(src.id.ToString()); + param.AppendInteger(new_index); + param.AppendInteger(old_index); + this->dispatcher_host()->SendEvent(this, "__nw_desktop_capture_monitor_listner_moved", param); +} +void DesktopCaptureMonitor::OnSourceNameChanged(int index){ + DesktopMediaList::Source src = media_list_->GetSource(index); + + base::ListValue param; + param.AppendString(src.id.ToString()); + param.AppendString(src.name); + this->dispatcher_host()->SendEvent(this, "__nw_desktop_capture_monitor_listner_namechanged", param); +} + +void DesktopCaptureMonitor::OnSourceThumbnailChanged(int index){ + std::string base64; + + DesktopMediaList::Source src = media_list_->GetSource(index); + SkBitmap bitmap = src.thumbnail.GetRepresentation(1).sk_bitmap(); + SkAutoLockPixels lock_image(bitmap); + std::vector data; + bool success = gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &data); + if (success){ + base::StringPiece raw_str(reinterpret_cast(&data[0]), data.size()); + base::Base64Encode(raw_str, &base64); + } + base::ListValue param; + param.AppendString(src.id.ToString()); + param.AppendString(base64); + this->dispatcher_host()->SendEvent(this, "__nw_desktop_capture_monitor_listner_thumbnailchanged", param); +} + +} // namespace nwapi diff --git a/src/api/screen/desktop_capture_monitor.h b/src/api/screen/desktop_capture_monitor.h new file mode 100644 index 0000000000..5397897425 --- /dev/null +++ b/src/api/screen/desktop_capture_monitor.h @@ -0,0 +1,50 @@ +// Copyright (c) 2012 Intel Corp +// Copyright (c) 2012 The Chromium Authors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell co +// pies of the Software, and to permit persons to whom the Software is furnished +// to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in al +// l copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM +// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES +// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +// OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH +// ETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#ifndef CONTENT_NW_SRC_API_DESKTOPCAPTURE_H_ +#define CONTENT_NW_SRC_API_DESKTOPCAPTURE_H_ +#include "base/compiler_specific.h" +#include "content/nw/src/api/base/base.h" +#include "chrome/browser/media/desktop_media_list_observer.h" +#include "chrome/browser/media/native_desktop_media_list.h" + +namespace nwapi { + class DesktopCaptureMonitor : public Base, public DesktopMediaListObserver { + public: + DesktopCaptureMonitor(int id, + const base::WeakPtr& dispatcher_host, + const base::DictionaryValue& option); + ~DesktopCaptureMonitor() override; + void CallSync(const std::string& method, const base::ListValue& arguments, base::ListValue* result) override; + void OnSourceAdded(int index) override; + void OnSourceRemoved(int index) override; + void OnSourceMoved(int old_index, int new_index) override; + void OnSourceNameChanged(int index) override; + void OnSourceThumbnailChanged(int index) override; + private: + DesktopCaptureMonitor(); + DISALLOW_COPY_AND_ASSIGN(DesktopCaptureMonitor); + int GetPrimaryMonitorIndex(); + void Start(bool screens, bool windows); + void Stop(); + + scoped_ptr media_list_; + }; +} // namespace api +#endif // CONTENT_NW_SRC_API_DESKTOPCAPTURE_H_ diff --git a/src/api/screen/screen.cc b/src/api/screen/screen.cc index 2045ad5105..51ca93028b 100644 --- a/src/api/screen/screen.cc +++ b/src/api/screen/screen.cc @@ -164,6 +164,14 @@ void Screen::Call(DispatcherHost* dispatcher_host, // Screen Picker GUI is still active, return false; result->AppendBoolean(false); } + } else if (method == "CancelChooseDesktopMedia") { + if (gpDCCDMF) { + gpDCCDMF->Cancel(); + gpDCCDMF = NULL; + result->AppendBoolean(true); + } else { + result->AppendBoolean(false); + } } } diff --git a/src/api/screen/screen.js b/src/api/screen/screen.js index 544757e21b..0f2c290fef 100644 --- a/src/api/screen/screen.js +++ b/src/api/screen/screen.js @@ -18,11 +18,91 @@ // ETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +function DesktopCaptureMonitor() { + nw.allocateObject(this, {}); + this.sources = new Array(); + this.started = false; +} +require('util').inherits(DesktopCaptureMonitor, exports.Base); + + +DesktopCaptureMonitor.prototype.start = function (screens, windows) { + if (this.started) + return false; + this.started = true; + nw.callObjectMethodSync(this, 'start', [screens, windows]); + return true; +} + +DesktopCaptureMonitor.prototype.stop = function () { + if (!this.started) + return false; + nw.callObjectMethodSync(this, 'stop', []); + this.started = false; + this.sources = new Array(); + return true; +} + +DesktopCaptureMonitor.prototype.on('__nw_desktop_capture_monitor_listner_added', function (id, name, order, type, primaryindex) { + if(this.sources.indexOf(id)!=-1) + { + //TODO: Find out what this event comes twice on some platforms + return; + } + this.sources.splice(order, 0, id); + this.emit("added", id, name, order, type, primaryindex); + for (var i = order + 1; i <= this.sources.length - 1; i++) { + this.emit("orderchanged", this.sources[i], i, i - 1); + } +}); + + +DesktopCaptureMonitor.prototype.on('__nw_desktop_capture_monitor_listner_removed', function (index) { + var id = this.sources[index]; + if (index != -1) { + this.sources.splice(index, 1); + this.emit("removed", id); + for (var i = index; i <= this.sources.length - 1; i++) { + this.emit("orderchanged", this.sources[i], i, i + 1); + } + } +}); + +DesktopCaptureMonitor.prototype.on('__nw_desktop_capture_monitor_listner_moved', function (id, new_index, old_index) { + var temp = this.sources[old_index]; + this.sources.splice(old_index, 1); + this.sources.splice(new_index, 0, temp); + this.emit("orderchanged", temp, new_index, old_index); + for (var i = new_index; i < old_index; i++) + this.emit("orderchanged", this.sources[i + 1], i + 1, i); +}); + +DesktopCaptureMonitor.prototype.on('__nw_desktop_capture_monitor_listner_namechanged', function (id, name) { + this.emit("namechanged", id, name); +}); + +DesktopCaptureMonitor.prototype.on('__nw_desktop_capture_monitor_listner_thumbnailchanged', function (id, thumbnail) { + this.emit("thumbnailchanged", id, thumbnail); +}); + +var listenerCount=0; +DesktopCaptureMonitor.prototype.on = DesktopCaptureMonitor.prototype.addListener = function (ev, callback) { + //throw except if unsupported event + if (ev != "added" && ev != "removed" && ev != "orderchanged" && ev != "namechanged" && ev != "thumbnailchanged") + throw new String("only following events can be listened: added, removed, moved, namechanged, thumbnailchanged"); + + process.EventEmitter.prototype.addListener.apply(this, arguments); +} + + +exports.DesktopCaptureMonitor=DesktopCaptureMonitor; + var screenInstance = null; function Screen() { nw.allocateObject(this, {}); this._numListener = 0; + this.DesktopCaptureMonitor = new DesktopCaptureMonitor(); } require('util').inherits(Screen, exports.Base); @@ -74,6 +154,9 @@ Screen.prototype.chooseDesktopMedia = function(array, callback) { return false; } +Screen.prototype.cancelChooseDesktopMedia = function() { + return nw.callStaticMethodSync('Screen', 'CancelChooseDesktopMedia', [ this.id ])[0]; +} // ======== Screen functions End ======== // Store App object in node's context. diff --git a/src/browser/app_controller_mac.h b/src/browser/app_controller_mac.h index 6199be75b8..f2158498f1 100644 --- a/src/browser/app_controller_mac.h +++ b/src/browser/app_controller_mac.h @@ -24,7 +24,11 @@ #import @interface AppController : NSObject { + BOOL appReady; } + +@property(nonatomic) BOOL appReady; + @end #endif // CONTENT_NW_SRC_BROWSER_APP_CONTROLLER_MAC_H_ diff --git a/src/browser/app_controller_mac.mm b/src/browser/app_controller_mac.mm index 71497e0ab1..7de1d69375 100644 --- a/src/browser/app_controller_mac.mm +++ b/src/browser/app_controller_mac.mm @@ -31,6 +31,8 @@ @implementation AppController +@synthesize appReady; + - (BOOL)application:(NSApplication*)sender openFile:(NSString*)filename { if (content::Shell::windows().size() == 0) { @@ -52,6 +54,15 @@ - (BOOL)application:(NSApplication*)sender return FALSE; } +- (void) applicationWillFinishLaunching: (NSNotification *) note { + self.appReady = FALSE; + NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager]; + [eventManager setEventHandler:self + andSelector:@selector(handleGetURLEvent:withReplyEvent:) + forEventClass:kInternetEventClass + andEventID:kAEGetURL]; +} + - (void) applicationDidFinishLaunching: (NSNotification *) note { // Initlialize everything here content::ShellContentBrowserClient* browser_client = @@ -66,6 +77,8 @@ - (void) applicationDidFinishLaunching: (NSNotification *) note { [NSApp setMainMenu:[[[NSMenu alloc] init] autorelease]]; [[NSApp mainMenu] addItem:[[[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""] autorelease]]; + + self.appReady = TRUE; #if 0 nw::StandardMenusMac standard_menus( browser_client->shell_browser_main_parts()->package()->GetName()); @@ -92,4 +105,20 @@ - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)app { return NSTerminateCancel; } +- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent +{ + NSString *urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; + if (self.appReady) { + // Immediate handle of get url event + nwapi::App::EmitOpenEvent([urlString UTF8String]); + } else { + // App is not ready yet, add the URL to the command line arguments. + // This happens when the app is started by opening a link with the registered URL. + if (content::Shell::windows().size() == 0) { + base::CommandLine::ForCurrentProcess()->AppendArg([urlString UTF8String]); + base::CommandLine::ForCurrentProcess()->FixOrigArgv4Finder([urlString UTF8String]); + } + } +} + @end diff --git a/src/browser/chrome_crash_reporter_client.cc b/src/browser/chrome_crash_reporter_client.cc index b620fbd977..de5f3ef308 100644 --- a/src/browser/chrome_crash_reporter_client.cc +++ b/src/browser/chrome_crash_reporter_client.cc @@ -284,7 +284,7 @@ bool ChromeCrashReporterClient::ReportingIsEnforcedByPolicy( return true; } #endif - return false; + return true; } #endif // defined(OS_WIN) @@ -326,8 +326,7 @@ size_t ChromeCrashReporterClient::RegisterCrashKeys() { } bool ChromeCrashReporterClient::IsRunningUnattended() { - scoped_ptr env(base::Environment::Create()); - return env->HasVar(env_vars::kHeadless); + return true; } bool ChromeCrashReporterClient::GetCollectStatsConsent() { diff --git a/src/browser/native_window_aura.cc b/src/browser/native_window_aura.cc index f4590345d9..bb3ab1e425 100644 --- a/src/browser/native_window_aura.cc +++ b/src/browser/native_window_aura.cc @@ -36,6 +36,9 @@ #endif #include "chrome/browser/platform_util.h" +#ifdef OS_LINUX +#include "chrome/browser/ui/libgtk2ui/gtk2_ui.h" +#endif #include "content/nw/src/api/menu/menu.h" #include "content/nw/src/browser/browser_view_layout.h" #include "content/nw/src/browser/menubar_view.h" @@ -115,6 +118,24 @@ bool IsParent(gfx::NativeView child, gfx::NativeView possible_parent) { return false; } +#ifdef OS_LINUX +static void SetDeskopEnvironment() { + static bool runOnce = false; + if (runOnce) return; + runOnce = true; + + scoped_ptr env(base::Environment::Create()); + std::string name; + //if (env->GetVar("CHROME_DESKTOP", &name) && !name.empty()) + // return; + + if (!env->GetVar("NW_DESKTOP", &name) || name.empty()) + name = "nw.desktop"; + + env->SetVar("CHROME_DESKTOP", name); +} +#endif + class NativeWindowClientView : public views::ClientView { public: NativeWindowClientView(views::Widget* widget, @@ -357,6 +378,10 @@ NativeWindowAura::NativeWindowAura(const base::WeakPtr& shell, window_->SetInitialFocus(ui::SHOW_STATE_NORMAL); window_->SetNativeWindowProperty("__BROWSER_VIEW__", this); + +#ifdef OS_LINUX + SetDeskopEnvironment(); +#endif } NativeWindowAura::~NativeWindowAura() { @@ -664,6 +689,8 @@ void NativeWindowAura::FlashFrame(int count) { fwi.dwFlags = FLASHW_STOP; } FlashWindowEx(&fwi); +#elif defined(OS_LINUX) + window_->FlashFrame(count); #endif } @@ -715,6 +742,8 @@ void NativeWindowAura::SetBadgeLabel(const std::string& badge) { taskbar->SetOverlayIcon(hWnd, icon, L"Status"); DestroyIcon(icon); +#elif defined(OS_LINUX) + views::LinuxUI::instance()->SetDownloadCount(atoi(badge.c_str())); #endif } @@ -748,6 +777,8 @@ void NativeWindowAura::SetProgressBar(double progress) { } taskbar->SetProgressState(hWnd, tbpFlag); +#elif defined(OS_LINUX) + views::LinuxUI::instance()->SetProgressFraction(progress); #endif } diff --git a/src/browser/shell_devtools_manager_delegate.cc b/src/browser/shell_devtools_manager_delegate.cc index f97aa62ef7..967bd0e410 100644 --- a/src/browser/shell_devtools_manager_delegate.cc +++ b/src/browser/shell_devtools_manager_delegate.cc @@ -23,7 +23,7 @@ #include "content/public/common/url_constants.h" #include "content/public/common/user_agent.h" #include "content/nw/src/nw_shell.h" -#include "grit/shell_resources.h" +#include "grit/nw_resources.h" #include "net/socket/tcp_server_socket.h" #include "ui/base/resource/resource_bundle.h" @@ -202,7 +202,7 @@ std::string ShellDevToolsDelegate::GetDiscoveryPageHTML() { return std::string(); #else return ResourceBundle::GetSharedInstance().GetRawDataResource( - IDR_CONTENT_SHELL_DEVTOOLS_DISCOVERY_PAGE).as_string(); + IDR_NW_DEVTOOLS_DISCOVERY_PAGE).as_string(); #endif } diff --git a/src/mac/app-Info.plist b/src/mac/app-Info.plist index 3cd9e0d3c2..f521c701d6 100644 --- a/src/mac/app-Info.plist +++ b/src/mac/app-Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.11.4 + 0.12.3 NSPrincipalClass NSApplication LSMinimumSystemVersion diff --git a/src/net/shell_url_request_context_getter.cc b/src/net/shell_url_request_context_getter.cc index 16872a7f46..9c9ea85eb4 100644 --- a/src/net/shell_url_request_context_getter.cc +++ b/src/net/shell_url_request_context_getter.cc @@ -60,6 +60,7 @@ #include "net/url_request/static_http_user_agent_settings.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_storage.h" +#include "net/url_request/url_request_intercepting_job_factory.h" #include "net/url_request/url_request_job_factory_impl.h" #include "ui/base/l10n/l10n_util.h" @@ -132,6 +133,7 @@ ShellURLRequestContextGetter::ShellURLRequestContextGetter( MessageLoop* file_loop, ProtocolHandlerMap* protocol_handlers, ShellBrowserContext* browser_context, + URLRequestInterceptorScopedVector request_interceptors, const std::string& auth_schemes, const std::string& auth_server_whitelist, const std::string& auth_delegate_whitelist, @@ -149,6 +151,7 @@ ShellURLRequestContextGetter::ShellURLRequestContextGetter( io_loop_(io_loop), file_loop_(file_loop), browser_context_(browser_context), + request_interceptors_(request_interceptors.Pass()), extension_info_map_(extension_info_map){ // Must first be created on the UI thread. DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -190,8 +193,8 @@ net::URLRequestContext* ShellURLRequestContextGetter::GetURLRequestContext() { cookie_store->GetCookieMonster()->SetPersistSessionCookies(true); storage_->set_cookie_store(cookie_store.get()); - const char* schemes[] = {"http", "https", "file", "app"}; - cookie_store->GetCookieMonster()->SetCookieableSchemes(schemes, 4); + const char* schemes[] = {"http", "https", "ws", "wss", "app", "file"}; + cookie_store->GetCookieMonster()->SetCookieableSchemes(schemes, 6); storage_->set_channel_id_service(new net::ChannelIDService( new net::DefaultChannelIDStore(NULL), @@ -244,7 +247,7 @@ net::URLRequestContext* ShellURLRequestContextGetter::GetURLRequestContext() { net::HttpCache::DefaultBackend* main_backend = new net::HttpCache::DefaultBackend( net::DISK_CACHE, - net::CACHE_BACKEND_SIMPLE, + net::CACHE_BACKEND_BLOCKFILE, cache_path, 10 * 1024 * 1024, // 10M BrowserThread::GetMessageLoopProxyForThread( @@ -292,8 +295,19 @@ net::URLRequestContext* ShellURLRequestContextGetter::GetURLRequestContext() { new net::AppProtocolHandler(root_path_)); job_factory->SetProtocolHandler("nw", new nw::NwProtocolHandler()); - storage_->set_job_factory(job_factory.release()); + // Set up interceptors in the reverse order. + scoped_ptr top_job_factory = + job_factory.Pass(); + for (URLRequestInterceptorScopedVector::reverse_iterator i = + request_interceptors_.rbegin(); + i != request_interceptors_.rend(); + ++i) { + top_job_factory.reset(new net::URLRequestInterceptingJobFactory( + top_job_factory.Pass(), make_scoped_ptr(*i))); + } + request_interceptors_.weak_clear(); + storage_->set_job_factory(top_job_factory.release()); } return url_request_context_.get(); diff --git a/src/net/shell_url_request_context_getter.h b/src/net/shell_url_request_context_getter.h index a594714346..130000da3f 100644 --- a/src/net/shell_url_request_context_getter.h +++ b/src/net/shell_url_request_context_getter.h @@ -62,6 +62,7 @@ class ShellBrowserContext; base::MessageLoop* file_loop, ProtocolHandlerMap* protocol_handlers, ShellBrowserContext*, + URLRequestInterceptorScopedVector request_interceptors, const std::string& auth_schemes, const std::string& auth_server_whitelist, const std::string& auth_delegate_whitelist, @@ -108,6 +109,7 @@ class ShellBrowserContext; scoped_ptr url_security_manager_; ProtocolHandlerMap protocol_handlers_; ShellBrowserContext* browser_context_; + URLRequestInterceptorScopedVector request_interceptors_; extensions::InfoMap* extension_info_map_; DISALLOW_COPY_AND_ASSIGN(ShellURLRequestContextGetter); diff --git a/src/nw_notification_manager_toast_win.cc b/src/nw_notification_manager_toast_win.cc index f37fbb3067..683f809397 100644 --- a/src/nw_notification_manager_toast_win.cc +++ b/src/nw_notification_manager_toast_win.cc @@ -106,7 +106,7 @@ typedef ABI::Windows::Foundation::ITypedEventHandler { public: - ToastEventHandler::ToastEventHandler(const int render_process_id, const int notification_id); + ToastEventHandler::ToastEventHandler(const int render_process_id, const int notification_id, const content::PlatformNotificationData& params, const SkBitmap& icon); ~ToastEventHandler(); // DesktopToastActivatedEventHandler @@ -150,12 +150,15 @@ class ToastEventHandler : private: ULONG _ref; const int _render_process_id, _notification_id; + // _params and _icon is stored for fallback to bubble notification + const content::PlatformNotificationData _params; + const SkBitmap _icon; }; // ============= ToastEventHandler Implementation ============= -ToastEventHandler::ToastEventHandler(const int render_process_id, const int notification_id) : -_ref(0), _render_process_id(render_process_id), _notification_id(notification_id) { +ToastEventHandler::ToastEventHandler(const int render_process_id, const int notification_id, const content::PlatformNotificationData& params, const SkBitmap& icon) : +_ref(0), _render_process_id(render_process_id), _notification_id(notification_id), _params(params), _icon(icon) { } ToastEventHandler::~ToastEventHandler() { @@ -195,6 +198,7 @@ IFACEMETHODIMP ToastEventHandler::Invoke(_In_ IToastNotification* /* sender */, if (fallBack) { NotificationManagerToastWin::ForceDisable = true; delete nmtw; + NotificationManager::getSingleton()->AddDesktopNotification(_params, _render_process_id, _notification_id, _icon); } return succeeded ? S_OK : E_FAIL; } @@ -244,6 +248,30 @@ HRESULT NotificationManagerToastWin::SetTextValues(_In_reads_(textValuesCount) c return hr; } +HRESULT NotificationManagerToastWin::SilentAudio(_In_ IXmlDocument *toastXml) { + ComPtr nodeList; + HRESULT hr = toastXml->GetElementsByTagName(StringReferenceWrapper(L"toast").Get(), &nodeList); + if (SUCCEEDED(hr)) { + ComPtr toastNode; + hr = nodeList->Item(0, &toastNode); + if (SUCCEEDED(hr)) { + ComPtr soundElement; + hr = toastXml->CreateElement(StringReferenceWrapper(L"audio").Get(), &soundElement); + if (SUCCEEDED(hr)) { + hr = soundElement->SetAttribute(StringReferenceWrapper(L"silent").Get(), StringReferenceWrapper(L"true").Get()); + if (SUCCEEDED(hr)) { + ComPtr soundNode, appendedSoundNode; + hr = soundElement.As(&soundNode); + if (SUCCEEDED(hr)) { + hr = toastNode->AppendChild(soundNode.Get(), &appendedSoundNode); + } + } + } + } + } + return hr; +} + HRESULT NotificationManagerToastWin::SetImageSrc(_In_z_ const wchar_t *imagePath, _In_ IXmlDocument *toastXml) { wchar_t imageSrc[MAX_PATH] = L""; HRESULT hr = StringCchCat(imageSrc, ARRAYSIZE(imageSrc), imagePath); @@ -304,13 +332,16 @@ HRESULT NotificationManagerToastWin::CreateToastXml(_In_ IToastNotificationManag }; UINT32 textLengths[] = { params.title.length(), params.body.length() }; hr = SetTextValues(textValues, 2, textLengths, *inputXml); + if (SUCCEEDED(hr)) { + hr = SilentAudio(*inputXml); + } } } return hr; } HRESULT NotificationManagerToastWin::CreateToast(_In_ IToastNotificationManagerStatics *toastManager, _In_ IXmlDocument *xml, - const int render_process_id, const int notification_id) { + const int render_process_id, const int notification_id, const content::PlatformNotificationData& params, const SkBitmap& icon) { ComPtr factory; HRESULT hr = GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &factory); if (SUCCEEDED(hr)) { @@ -319,7 +350,7 @@ HRESULT NotificationManagerToastWin::CreateToast(_In_ IToastNotificationManagerS if (SUCCEEDED(hr)) { // Register the event handlers EventRegistrationToken activatedToken, dismissedToken, failedToken; - ComPtr eventHandler = new ToastEventHandler(render_process_id, notification_id); + ComPtr eventHandler = new ToastEventHandler(render_process_id, notification_id, params, icon); hr = toast->add_Activated(eventHandler.Get(), &activatedToken); if (SUCCEEDED(hr)) { @@ -384,7 +415,7 @@ bool NotificationManagerToastWin::AddDesktopNotification(const content::Platform ComPtr toastXml; HRESULT hr = CreateToastXml(toastStatics_.Get(), params, icon, &toastXml); if (SUCCEEDED(hr)) { - hr = CreateToast(toastStatics_.Get(), toastXml.Get(), render_process_id, notification_id); + hr = CreateToast(toastStatics_.Get(), toastXml.Get(), render_process_id, notification_id, params, icon); if (SUCCEEDED(hr)) DesktopNotificationPostDisplay(render_process_id, notification_id); } diff --git a/src/nw_notification_manager_toast_win.h b/src/nw_notification_manager_toast_win.h index e2dbaad0c2..3aeebeff80 100644 --- a/src/nw_notification_manager_toast_win.h +++ b/src/nw_notification_manager_toast_win.h @@ -39,7 +39,7 @@ class NotificationManagerToastWin : public NotificationManager { static bool ForceDisable; HRESULT CreateToast(_In_ IToastNotificationManagerStatics *toastManager, _In_ IXmlDocument *xml, - const int render_process_id, const int notification_id); + const int render_process_id, const int notification_id, const content::PlatformNotificationData& params, const SkBitmap& icon); // Create the toast XML from a template HRESULT CreateToastXml(_In_ IToastNotificationManagerStatics *toastManager, @@ -48,6 +48,9 @@ class NotificationManagerToastWin : public NotificationManager { // Set the value of the "src" attribute of the "image" node HRESULT SetImageSrc(_In_z_ const wchar_t *imagePath, _In_ IXmlDocument *toastXml); + // Set the value of the "silent" attribute of the "audio" node + HRESULT SilentAudio( _In_ IXmlDocument *toastXml); + // Set the values of each of the text nodes HRESULT SetTextValues(_In_reads_(textValuesCount) const wchar_t **textValues, _In_ UINT32 textValuesCount, _In_reads_(textValuesCount) UINT32 *textValuesLengths, _In_ IXmlDocument *toastXml); diff --git a/src/nw_notification_manager_win.cc b/src/nw_notification_manager_win.cc index 59ee0bc864..5ad90c2356 100644 --- a/src/nw_notification_manager_win.cc +++ b/src/nw_notification_manager_win.cc @@ -132,7 +132,8 @@ NotificationManagerWin::~NotificationManagerWin() { bool NotificationManagerWin::AddDesktopNotification(const content::PlatformNotificationData& params, const int render_process_id, const int notification_id, const SkBitmap& bitmap_icon) { - if (status_tray_ == NULL) Init(); + if (status_tray_ == NULL) + if(!Init()) return false; content::Shell* shell = content::Shell::windows()[0]; @@ -150,7 +151,7 @@ bool NotificationManagerWin::AddDesktopNotification(const content::PlatformNotif if (status_icon == NULL) { nw::Package* package = shell->GetPackage(); status_icon_ = status_tray_->CreateStatusIcon(StatusTray::NOTIFICATION_TRAY_ICON, - *(shell->window()->app_icon().ToImageSkia()), base::UTF8ToUTF16(package->GetName())); + icon.IsEmpty() ? gfx::ImageSkia() : *icon.ToImageSkia(), base::UTF8ToUTF16(package->GetName())); status_icon = status_icon_; status_observer_ = new TrayObserver(this); status_icon->AddObserver(status_observer_); diff --git a/src/nw_shell.cc b/src/nw_shell.cc index df73345c9e..fc2cc0c904 100644 --- a/src/nw_shell.cc +++ b/src/nw_shell.cc @@ -78,6 +78,7 @@ using nw::NativeWindowAura; #endif #include "content/public/browser/media_capture_devices.h" +#include "media/audio/audio_manager_base.h" #include "chrome/browser/printing/print_view_manager_basic.h" #include "extensions/common/extension_messages.h" @@ -449,13 +450,12 @@ void Shell::ShowDevTools(const char* jail_id, bool headless) { DevToolsHttpHandler* http_handler = browser_client->shell_browser_main_parts()->devtools_handler(); - GURL url = http_handler->GetFrontendURL(agent.get()); + GURL url = http_handler->GetFrontendURL("/devtools/devtools.html"); http_handler->EnumerateTargets(); #if 0 if (headless) { DevToolsAgentHost* agent_host = DevToolsAgentHost::GetOrCreateFor(web_contents()).get(); - url = delegate->devtools_http_handler()->GetFrontendURL(agent_host); DevToolsHttpHandlerImpl* http_handler = static_cast(delegate->devtools_http_handler()); http_handler->EnumerateTargets(); @@ -484,7 +484,7 @@ void Shell::ShowDevTools(const char* jail_id, bool headless) { new ShellDevToolsFrontend( shell, - DevToolsAgentHost::GetOrCreateFor(web_contents_.get()).get()); + agent.get()); int rh_id = shell->web_contents_->GetRenderProcessHost()->GetID(); ChildProcessSecurityPolicyImpl::GetInstance()->GrantScheme(rh_id, url::kFileScheme); @@ -728,6 +728,11 @@ void Shell::RequestMediaAccessPermission( devices.push_back(content::MediaStreamDevice( content::MEDIA_DESKTOP_VIDEO_CAPTURE, media_id.ToString(), "Screen")); } +#if defined(OS_WIN) + if (request.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE) { + devices.push_back(content::MediaStreamDevice(content::MEDIA_DESKTOP_AUDIO_CAPTURE, media::AudioManagerBase::kLoopbackInputDeviceId, "System Audio")); + } +#endif // TODO(jamescook): Should we show a recording icon somewhere? If so, where? scoped_ptr ui; callback.Run(devices, diff --git a/src/nw_version.h b/src/nw_version.h index 701c3a8c3b..b7afd76345 100644 --- a/src/nw_version.h +++ b/src/nw_version.h @@ -23,7 +23,7 @@ #define NW_MAJOR_VERSION 0 #define NW_MINOR_VERSION 12 -#define NW_PATCH_VERSION 0 +#define NW_PATCH_VERSION 3 #define NW_VERSION_IS_RELEASE 1 #ifndef NW_STRINGIFY diff --git a/src/shell_browser_context.cc b/src/shell_browser_context.cc index eb52368f8d..a5299d1125 100644 --- a/src/shell_browser_context.cc +++ b/src/shell_browser_context.cc @@ -185,7 +185,7 @@ net::URLRequestContextGetter* ShellBrowserContext::CreateRequestContext( package_->path(), BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::IO), BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::FILE), - protocol_handlers, this, + protocol_handlers, this, protocol_interceptors.Pass(), auth_schemes, auth_server_whitelist, auth_delegate_whitelist, gssapi_library_name, extension_info_map); diff --git a/src/shell_content_browser_client.cc b/src/shell_content_browser_client.cc index dddcc46ebd..b61a1cfab7 100644 --- a/src/shell_content_browser_client.cc +++ b/src/shell_content_browser_client.cc @@ -370,6 +370,7 @@ void ShellContentBrowserClient::OverrideWebkitPrefs( const GURL& url, content::WebPreferences* prefs) { nw::Package* package = shell_browser_main_parts()->package(); + CommandLine* cmd_line = CommandLine::ForCurrentProcess(); // Disable web security. prefs->dom_paste_enabled = true; @@ -384,6 +385,11 @@ void ShellContentBrowserClient::OverrideWebkitPrefs( prefs->plugins_enabled = true; prefs->java_enabled = false; + prefs->allow_displaying_insecure_content = + !(cmd_line->HasSwitch(switches::kNoDisplayingInsecureContent)); + prefs->allow_running_insecure_content = + cmd_line->HasSwitch(switches::kAllowRunningInsecureContent); + base::DictionaryValue* webkit; if (package->root()->GetDictionary(switches::kmWebkit, &webkit)) { webkit->GetBoolean(switches::kmJava, &prefs->java_enabled); diff --git a/src/shell_devtools_frontend.cc b/src/shell_devtools_frontend.cc index 4fdc8c7b22..1e9cb3d24d 100644 --- a/src/shell_devtools_frontend.cc +++ b/src/shell_devtools_frontend.cc @@ -26,6 +26,10 @@ namespace content { +// This constant should be in sync with +// the constant at devtools_ui_bindings.cc. +const size_t kMaxMessageChunkSize = IPC::Channel::kMaximumMessageSize / 4; + #if 0 // static ShellDevToolsFrontend* ShellDevToolsFrontend::Show( @@ -70,17 +74,16 @@ ShellDevToolsFrontend::~ShellDevToolsFrontend() { void ShellDevToolsFrontend::RenderViewCreated( RenderViewHost* render_view_host) { -#if 0 if (!frontend_host_) { - frontend_host_.reset(DevToolsFrontendHost::Create(render_view_host, this)); - DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor( - agent_host_.get(), this); + frontend_host_.reset( + DevToolsFrontendHost::Create(web_contents()->GetMainFrame(), this)); + agent_host_->AttachClient(this); } -#endif } void ShellDevToolsFrontend::WebContentsDestroyed() { - agent_host_->DetachClient(); + if (agent_host_) + agent_host_->DetachClient(); delete this; } @@ -128,12 +131,22 @@ void ShellDevToolsFrontend::HandleMessageFromDevToolsFrontendToBackend( void ShellDevToolsFrontend::DispatchProtocolMessage( DevToolsAgentHost* agent_host, const std::string& message) { - base::StringValue message_value(message); - std::string param; - base::JSONWriter::Write(&message_value, ¶m); - std::string code = "DevToolsAPI.dispatchMessage(" + param + ");"; - base::string16 javascript = base::UTF8ToUTF16(code); - web_contents()->GetMainFrame()->ExecuteJavaScript(javascript); + if (message.length() < kMaxMessageChunkSize) { + base::string16 javascript = base::UTF8ToUTF16( + "DevToolsAPI.dispatchMessage(" + message + ");"); + web_contents()->GetMainFrame()->ExecuteJavaScript(javascript); + return; + } + + base::FundamentalValue total_size(static_cast(message.length())); + for (size_t pos = 0; pos < message.length(); pos += kMaxMessageChunkSize) { + base::StringValue message_value(message.substr(pos, kMaxMessageChunkSize)); + std::string param; + base::JSONWriter::Write(&message_value, ¶m); + std::string code = "DevToolsAPI.dispatchMessageChunk(" + param + ");"; + base::string16 javascript = base::UTF8ToUTF16(code); + web_contents()->GetMainFrame()->ExecuteJavaScript(javascript); + } } void ShellDevToolsFrontend::AgentHostClosed( diff --git a/src/shell_main.cc b/src/shell_main.cc index 1e4a1b0fd5..7b999ebd30 100644 --- a/src/shell_main.cc +++ b/src/shell_main.cc @@ -25,6 +25,9 @@ #include "sandbox/win/src/sandbox_types.h" #if defined(OS_WIN) +#include +#include + #include "base/win/win_util.h" #include "base/win/windows_version.h" #include "content/public/app/startup_helper_win.h" @@ -39,13 +42,49 @@ using base::CommandLine; #if defined(OS_WIN) +// Win8.1 supports monitor-specific DPI scaling. +bool SetProcessDpiAwarenessWrapper(PROCESS_DPI_AWARENESS value) { + typedef HRESULT(WINAPI *SetProcessDpiAwarenessPtr)(PROCESS_DPI_AWARENESS); + SetProcessDpiAwarenessPtr set_process_dpi_awareness_func = + reinterpret_cast( + GetProcAddress(GetModuleHandleA("user32.dll"), + "SetProcessDpiAwarenessInternal")); + if (set_process_dpi_awareness_func) { + HRESULT hr = set_process_dpi_awareness_func(value); + if (SUCCEEDED(hr)) { + VLOG(1) << "SetProcessDpiAwareness succeeded."; + return true; + } else if (hr == E_ACCESSDENIED) { + LOG(ERROR) << "Access denied error from SetProcessDpiAwareness. " + "Function called twice, or manifest was used."; + } + } + return false; +} + +// This function works for Windows Vista through Win8. Win8.1 must use +// SetProcessDpiAwareness[Wrapper]. +BOOL SetProcessDPIAwareWrapper() { + typedef BOOL(WINAPI *SetProcessDPIAwarePtr)(VOID); + SetProcessDPIAwarePtr set_process_dpi_aware_func = + reinterpret_cast( + GetProcAddress(GetModuleHandleA("user32.dll"), + "SetProcessDPIAware")); + return set_process_dpi_aware_func && + set_process_dpi_aware_func(); +} + +void EnableHighDPISupport() { + if (!SetProcessDpiAwarenessWrapper(PROCESS_SYSTEM_DPI_AWARE)) { + SetProcessDPIAwareWrapper(); + } +} + int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t*, int) { CommandLine::Init(0, NULL); -#if 0 //FIXME - if (base::win::GetVersion() > base::win::VERSION_VISTA) - gfx::EnableHighDPISupport(); -#endif + if (base::win::GetVersion() >= base::win::VERSION_WIN7) + EnableHighDPISupport(); sandbox::SandboxInterfaceInfo sandbox_info = {0}; content::InitializeSandboxInfo(&sandbox_info); diff --git a/tests/manual_tests/notification/package.json b/tests/manual_tests/notification/package.json index a504bbc01f..7efd8aac98 100644 --- a/tests/manual_tests/notification/package.json +++ b/tests/manual_tests/notification/package.json @@ -1,5 +1,5 @@ { "name": "nw-notification-test", "main": "index.html", - "app-id": "com.node.webkit.notification.test" + "app-id": "com.node.webkit.notification.test" } diff --git a/tools/make-nw-headers.py b/tools/make-nw-headers.py index 03b0ce0410..27fbde92da 100644 --- a/tools/make-nw-headers.py +++ b/tools/make-nw-headers.py @@ -2,6 +2,7 @@ import os import tarfile import sys +import getnwisrelease import getnwversion import shutil import distutils.core @@ -25,6 +26,8 @@ def update_uvh(tmp_dir, header_files): tmp_dir = os.path.normpath(os.path.join(nw_root, 'tmp')) nw_version = getnwversion.nw_version +if getnwisrelease.release == 0: + nw_version += getnwisrelease.postfix #parse command line arguments ''' diff --git a/tools/package_binaries.py b/tools/package_binaries.py index 9e630b336e..cd1b8540cb 100755 --- a/tools/package_binaries.py +++ b/tools/package_binaries.py @@ -12,12 +12,13 @@ from subprocess import call -steps = ['nw', 'chromedriver', 'symbol', 'others'] +steps = ['nw', 'chromedriver', 'symbol', 'headers', 'others'] ################################ # Parse command line args parser = argparse.ArgumentParser(description='Package nw binaries.') parser.add_argument('-p','--path', help='Where to find the binaries, like out/Release', required=False) parser.add_argument('-a','--arch', help='target arch', required=False) +parser.add_argument('-m','--mode', help='package mode', required=False) group = parser.add_mutually_exclusive_group() group.add_argument('-s','--step', choices=steps, help='Execute specified step.', required=False) group.add_argument('-n','--skip', choices=steps, help='Skip specified step.', required=False) @@ -33,7 +34,10 @@ nw_ver = None # x.xx dist_dir = None # .../out/Release/dist -is_headers_ok = False # record whether nw-headers generated +package_name = 'nwjs' + +if args.mode == 'mas': + package_name = 'nwjs-macappstore' step = args.step skip = args.skip @@ -107,7 +111,7 @@ def generate_target_nw(platform_name, arch, version): target = {} # Output target['output'] = ''.join([ - 'nwjs-', + package_name, '-', 'v', version, '-', platform_name, '-', arch]) @@ -153,6 +157,9 @@ def generate_target_nw(platform_name, arch, version): return target def generate_target_chromedriver(platform_name, arch, version): + if args.mode == 'mas': + return generate_target_empty(platform_name, arch, version) + target = {} # Output target['output'] = ''.join([ @@ -175,7 +182,7 @@ def generate_target_chromedriver(platform_name, arch, version): def generate_target_symbols(platform_name, arch, version): target = {} - target['output'] = ''.join(['nwjs-symbol-', + target['output'] = ''.join([package_name, '-symbol-', 'v', version, '-', platform_name, '-', arch]) @@ -186,7 +193,7 @@ def generate_target_symbols(platform_name, arch, version): elif platform_name == 'win': target['compress'] = None target['input'] = ['nw.sym.7z'] - target['output'] = ''.join(['nwjs-symbol-', + target['output'] = ''.join([package_name, '-symbol-', 'v', version, '-', platform_name, '-', arch, '.7z']) @@ -201,26 +208,60 @@ def generate_target_symbols(platform_name, arch, version): exit(-1) return target -def generate_target_others(platform_name, arch, version): +def generate_target_headers(platform_name, arch, version): + # here, call make_nw_header tool to generate headers + # then, move to binaries_location target = {} target['output'] = '' target['compress'] = None - if platform_name == 'win': - target['input'] = ['nw.exp', 'nw.lib'] - elif platform_name == 'linux' : + if platform_name == 'osx': target['input'] = [] # here , call make-nw-headers.py to generate nw headers - # after generated, move to dist_dir make_nw_header = os.path.join(os.path.dirname(__file__), \ 'make-nw-headers.py') print make_nw_header res = call(['python', make_nw_header]) if res == 0: - global is_headers_ok - is_headers_ok = True print 'nw-headers generated' + nw_headers_name = 'nw-headers-v' + version + '.tar.gz' + nw_headers_path = os.path.join(os.path.dirname(__file__), \ + os.pardir, 'tmp', nw_headers_name) + if os.path.isfile(os.path.join(binaries_location, nw_headers_name)): + os.remove(os.path.join(binaries_location, nw_headers_name)) + shutil.move(nw_headers_path, binaries_location) + target['input'].append(nw_headers_name) else: + #TODO, handle err print 'nw-headers generate failed' + elif platform_name == 'win': + target['input'] = [] + elif platform_name == 'linux': + target['input'] = [] + else: + print 'Unsupported platform: ' + platform_name + exit(-1) + return target + +def generate_target_empty(platform_name, arch, version): + target = {} + target['output'] = '' + target['compress'] = None + if platform_name == 'win': + target['input'] = [] + elif platform_name == 'linux' : + target['input'] = [] + else: + target['input'] = [] + return target + +def generate_target_others(platform_name, arch, version): + target = {} + target['output'] = '' + target['compress'] = None + if platform_name == 'win': + target['input'] = ['nw.exp', 'nw.lib'] + elif platform_name == 'linux' : + target['input'] = [] else: target['input'] = [] return target @@ -282,14 +323,6 @@ def make_packages(targets): # now let's do it os.mkdir(dist_dir) - # here check whether nw headers generated - # if generated, mv to dist_dir - if is_headers_ok: - nw_headers_name = 'nw-headers-v' + nw_ver + '.tar.gz' - nw_headers_path = os.path.join(os.path.dirname(__file__), \ - os.pardir, 'tmp', nw_headers_name) - print 'Moving "' + nw_headers_name + '"' - shutil.move(nw_headers_path, dist_dir) for t in targets: if len(t['input']) == 0: continue @@ -327,6 +360,7 @@ def make_packages(targets): generators['nw'] = generate_target_nw generators['chromedriver'] = generate_target_chromedriver generators['symbol'] = generate_target_symbols +generators['headers'] = generate_target_headers generators['others'] = generate_target_others ################################ # Process targets