diff --git a/AUTHORS b/AUTHORS index 13ca024d2d..0679037506 100644 --- a/AUTHORS +++ b/AUTHORS @@ -27,3 +27,13 @@ Lukas Benes Lithare Emileit Jefry Tedjokusumo Wu Haojian +Bas Wegh +Joachim Bauch +Cong Liu +Eric Newport +Marco Fabbri +Daniel Braun +Chase Willden +Anton Khlynovskiy +Wu Yuehang +Rick Edgecombe diff --git a/BRANDING b/BRANDING deleted file mode 100644 index 4a97f5989f..0000000000 --- a/BRANDING +++ /dev/null @@ -1,7 +0,0 @@ -COMPANY_FULLNAME=node-webkit.org -COMPANY_SHORTNAME=node-webkit.org -PRODUCT_FULLNAME=node-webkit -PRODUCT_SHORTNAME=node-webkit -PRODUCT_INSTALLER_FULLNAME=Chromium Installer -PRODUCT_INSTALLER_SHORTNAME=Chromium Installer -COPYRIGHT=Copyright 2013 The Chromium Authors. All rights reserved. diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b6ac27662..55766cde31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,183 @@ -0.10.0 / 07-22-2014 +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 +- [Screen Selection] OSX and Win implementation (Thanks to Jefry) +- Fix #3165: crash on auth in webview + +0.12.0-rc1 / 02-27-2015 ======================= +- new 'nwjc' tool replaces 'nwsnapshot'; size limit removed +- add Window.evalNWBin() to work with nwjc +- Fix #2923: support pepper flash plugin on Linux +- Fix #2961: nwdirectory file dialog +- Fix #2996: setting breakpoints in Node context in devtools + +0.12.0-alpha3 / 02-13-2015 +========================== +- Chromium updated to 41.0.2272.32 +- io.js updated to 1.2.0 + +0.12.0-alpha2 / 01-18-2015 +========================== +- Fix: -webkit-app-region: drag; stopped working in version 0.12.0-alpha1 #2963 +- Fix: [WIN] ReferrenceError in native module function createWritableDummyStream #2933 +- support bypassing frame-ancestors CSP in Node frame #2967 + +0.12.0-alpha1 / 01-15-2015 +========================== +- renamed NW.js +- Chromium is updated to 41.0.2236.2 +- migrated to io.js 1.0.0 +- new chrome.webrequest API +- new 'webview' tag from Chrome extensions +- new 'bg-script' field in the manifest + +0.11.3 / 12-16-2014 +=================== +- new method in 'new-win-policy' event handler to control the options for new popup windows +- Fix: nw methods cannot be called from normal frames +- Extend Tray click event with position (Thanks to Marco Fabbri) (#1874) +- [OSX] Fix Window.focus() not taking focus (#2724) +- Add API methods and support for styling of icons (Tray, MenuItem) under Mac OS X (Yosemite) Dark Mode (#2775) +- [OSX] Fix alticon property of Tray not being updated properly (#703) +- Add Window.setVisibleOnAllWorkspaces API (#2722) +- Fix #2469: Changed Window.open to ignore slashes in parameters +- fix crash in window.open in some cases + +0.11.2 / 11-26-2014 +=================== +- Support window transparency (#132, Thanks to Jefry Tedjokusumo) +- Fix: [Linux] broken window events (focus, blur, etc, #2631) +- Fix: memory leak on setting tray icon (#2666) +- Fix: child_process.fork() (#2664) +- Fix: bad Buffer created from strings from DOM (#1669, #2439) (Thank to Liu Cong) +- Fix: Segmentation fault by starting nw on command line with parameters #2671 +- Fix: crashes if http.request gets blocked with Little Snitch (mac only) #2585 +- Fix: Windows 7 64/32 - frame doesn't show #2657 +- Fix: AutoFill Crashes NodeWebkit #2653 +- Fix "Cancel Desktop Notification" for all platform. and implement it for win8 (toast notification) + +0.11.1 / 11-20-2014 +=================== +- add nwsnapshot +- Support setting additional root certificates on supported platforms (thanks to Joachim Bauch) +- Support SetProxyConfig API (#916) +- [WIN] Fix startup crash on high DPI systems (#2649) +- Fix #1021: maximize frameless window in windows 8 +- Fix #2590: save as Filetypes not populating +- Fix #2592: zoomLevel +- Fix #2595: Linux MenuBar crash +- Fix #2393: link target with "_blank" opens page in current window +- Fix: Don't activate app unconditionally on window "Show". + +0.11.0 / 11-11-2014 +=================== +- Fix: notification and screen geometry API +- Fix: windows printing crash (#2515) +- Fix: mac: Fix build with 10.9 SDK +- Fix: enable 'high-dpi-support' for windows (#2524) +- Fix: 'resizable' is broken in manifest (#2319) +- Fix: crash on Flash context menu +- Fix: console.log() changes value (#2431) +- Fix: various crash cases (#2545, #2549) +- Fix: 'undefined' network request in devtools (#2529) +- Fix: devtools - breakpoints do not work (#2538) +- Fix: Jailed devtools broken in nw 0.11.0-rc1 (#2569) +- Fix: Window.setResizable(false) twice makes window resizable (#2299) +- Fix: win.setPosition('center') Invalid (#2307) + +0.11.0-rc1 / 10-27-2014 +======================= +- Chromium updated to 38.0.2125.104 +- Fix memory leak on navigation +- Show commit id in 'nw:version' page +- Fix: fullscreen in manifest (Linux) +- Fix: #430: handle event when quit from OSX dock + +0.10.5 / 09-16-2014 +=================== +- Fix: support more Chromium command line args (#1743, Thanks to Joachim Bauch) +- Fix #2171: crash when opening window +- Fix #2326: some websites crashes NW in Windows (fixed with the same file as updating to VS2013 Update 2) +- Fix: win: crash on invalid parameter error (thanks to Mikael Roos) +- Fix #1991 in a better way: [WIN] stalling for seconds under threaded composition on some hardware (#1991) +- Fix: [WIN] single-instance crash +- Fix: autofill crash when filling number in input box (#2310) +- Fix: CSP is not effective (#1672) +- Fix: crash when calling console.log() with a cross-domain window object in some cases (#1573) +- Fix: crash when stepping into a breakpoint set in scripts loaded by require() (#2214) + +0.10.4 / 09-05-2014 +=================== +- Fix: [WIN] child_process.fork() by making nw executable run as node +- Fix: [WIN] stalling for seconds under threaded composition on some hardware (#1991) +- Fix: [OSX] disable File Quarantine (#2294) +- Fix: [OSX] disable sound for notification +- support 'chrome://gpu' diagnosic page + +0.10.3 / 09-01-2014 +=================== +- Fix: child_process.fork() (#213) by making nw executable run as node +- Fix: [OSX] process.nextTick() blocked in some cases (#2170) + +0.10.2 / 08-12-2014 +=================== +- Support screen geometry API (#2178 Thanks to Jefry Tedjokusumo +- Support progress bar (#2175 Thanks to Jefry Tedjokusumo) +- Window.requestAttention() now accepts an integer parameter [4] +- Fix: JS source code snapshot is now working +- Fix: Linux: shift modifier not working for window menu +- Fix: Win: window.navigator.language is empty +- Fix: require works in wrong path in new-instance window (#2167) +- Fix: support for screencapture media requests (Thanks to Joachim Bauch) +- Fix: Win: cursor not loaded in some cases (#2150, #2152) +- Fix: crash on some cases (#2155, #2148) +- Fix: Large combo-box does not scroll properly and values overlaps on each other (#2161; upstream #357480) + +0.10.1 / 07-30-2014 +=================== +- Support Desktop notification (#27 Thanks to Jefry Tedjokusumo) +- Support Fullscreen API (#55) +- Official 64bit binary for Mac OS X (#296) +- Support F-keys in global shortcut (Thanks to Bas Wegh) +- Option to hide "Edit" and "Window" OS X menus (Thanks to Eric Newport) +- Fix #2072: [WIN] context menu popup in wrong (screen) position +- Fix #2136: crash when popup new window in some cases +- Fix: Linux symbol files are incomplete in 0.10.0 +- Fix #1908: allows redirection to App protocol for OAuth usage +- Fix: new-win-policy is fired on each navigation + +0.10.0 / 07-22-2014 +=================== - Fix: [WIN] download dialog - Fix: [WIN] MenuItem.enabled and other properties needs to be called twice to work (#1132) 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 567f55764a..5f1c5ba13c 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,55 @@ +## node-webkit is renamed NW.js + +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/nwjs/nw.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +Official site: http://nwjs.io +[Announcement](https://groups.google.com/d/msg/nwjs-general/V1FhvfaFIzQ/720xKVd0jNkJ) ## Introduction -node-webkit is an app runtime based on `Chromium` and `node.js`. You can -write native apps in HTML and JavaScript with node-webkit. It also lets you +NW.js is an app runtime based on `Chromium` and `node.js`. You can +write native apps in HTML and JavaScript with NW.js. It also lets you call Node.js modules directly from the DOM and enables a new way of writing native applications with all Web technologies. -It's created and developed in the Intel Open Source Technology Center. +It was created in the Intel Open Source Technology Center. [Introduction to node-webkit (slides)](https://speakerdeck.com/u/zcbenz/p/node-webkit-app-runtime-based-on-chromium-and-node-dot-js) [Creating Desktop Applications With node-webkit](http://strongloop.com/strongblog/creating-desktop-applications-with-node-webkit/) -[WebApp to DesktopApp with node-webkit (slides)](http://oldgeeksguide.github.io/presentations/html5devconf2013/wtod.html) +[WebApp to DesktopApp with node-webkit (slides)](http://oldgeeksguide.github.io/presentations/html5devconf2013/wtod.html) +[Essay on the history and internals of the project](http://yedingding.com/2014/08/01/node-webkit-intro-en.html) ## Features * Apps written in modern HTML5, CSS3, JS and WebGL. * Complete support for [Node.js APIs](http://nodejs.org/api/) and all its [third party modules](https://npmjs.org). -* Good performance: Node and WebKit runs in the same thread: Function calls are made straightforward; objects are in the same heap and can just reference each other; +* Good performance: Node and WebKit run in the same thread: Function calls are made straightforward; objects are in the same heap and can just reference each other; * Easy to package and distribute apps. * Available on Linux, Mac OS X and Windows ## Downloads -* **v0.10.0:** (Jul 22, 2014, based off of Node v0.11.13, Chromium 35.0.1916.113): [release notes](https://groups.google.com/d/msg/node-webkit/x7kYuDO0Cj8/cIxoJ6RFiLsJ) +* **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) - * Linux: [32bit](http://dl.node-webkit.org/v0.10.0/node-webkit-v0.10.0-linux-ia32.tar.gz) / [64bit](http://dl.node-webkit.org/v0.10.0/node-webkit-v0.10.0-linux-x64.tar.gz) - * Windows: [win32](http://dl.node-webkit.org/v0.10.0/node-webkit-v0.10.0-win-ia32.zip) - * Mac: [32bit, 10.7+](http://dl.node-webkit.org/v0.10.0/node-webkit-v0.10.0-osx-ia32.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/node-webkit/2OJ1cEMPLlA/09BvpTagSA0J)** -[release notes](https://groups.google.com/d/msg/node-webkit/CLPkgfV-i7s/hwkkQuJ1kngJ) +* **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) * Linux: [32bit](http://dl.node-webkit.org/v0.8.6/node-webkit-v0.8.6-linux-ia32.tar.gz) / [64bit](http://dl.node-webkit.org/v0.8.6/node-webkit-v0.8.6-linux-x64.tar.gz) * 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) ###Demos and real apps -You may also be interested in [our demos repository](https://github.com/zcbenz/nw-sample-apps) and the [List of apps and companies using node-webkit](https://github.com/rogerwang/node-webkit/wiki/List-of-apps-and-companies-using-node-webkit). +You may also be interested in [our demos repository](https://github.com/zcbenz/nw-sample-apps) and the [List of apps and companies using nw.js](https://github.com/nwjs/nw.js/wiki/List-of-apps-and-companies-using-nw.js). ## Quick Start @@ -62,32 +73,20 @@ Create `package.json`: ```json { "name": "nw-demo", + "version": "0.0.1", "main": "index.html" } ``` -Compress `index.html` and `package.json` into a zip archive called `app.nw`: - -````bash -$ zip app.nw index.html package.json -```` - -This should create a structure like this: - -``` -app.nw -|-- package.json -`-- index.html +Run: +```bash +$ /path/to/nw . (suppose the current directory contains 'package.json') ``` -Download the prebuilt binary for your platform and use it to open the -`app.nw` file: +Note: on Windows, you can drag the folder containing `package.json` to `nw.exe` to open it. -````bash -$ ./nw app.nw -```` - -Note: on Windows, you can drag the `app.nw` 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: +`/path/to/nwjs.app/Contents/MacOS/nwjs .` *(suppose the current directory contains 'package.json')* ## Documents @@ -101,11 +100,12 @@ And our [Wiki](https://github.com/rogerwang/node-webkit/wiki) for much more. ## Community -We use the [node-webkit group](http://groups.google.com/group/node-webkit) as -our mailing list (use English only). Subscribe via [node-webkit+subscribe@googlegroups.com](mailto:node-webkit+subscribe@googlegroups.com). -Issues are being tracked here on GitHub. +We use the [google group](https://groups.google.com/d/forum/nwjs-general) as +our mailing list (use English only). Subscribe via [nwjs-general+subscribe@googlegroups.com](mailto:nwjs-general+subscribe@googlegroups.com). -You can chat with us on IRC in the ##node-webkit channel on irc.freenode.net +*NOTE*: Links to the old google group (e.g. `https://groups.google.com/forum/#!msg/node-webkit/doRWZ07LgWQ/4fheV8FF8zsJ`) that are no more working can be fixed by replacing `node-webkit` with `nwjs-general` (e.g `https://groups.google.com/forum/#!msg/nwjs-general/doRWZ07LgWQ/4fheV8FF8zsJ`). + +Issues are being tracked here on GitHub. ## License @@ -116,4 +116,3 @@ You can chat with us on IRC in the ##node-webkit channel on irc.freenode.net The work is being sponsored by: * [Intel](http://www.intel.com) * [Gnor Tech](http://gnor.net) -* [eFounders](http://efounder.co) diff --git a/nw.gypi b/nw.gypi index f6bfe28ee4..0b4bc0f863 100644 --- a/nw.gypi +++ b/nw.gypi @@ -4,10 +4,28 @@ { 'variables': { - 'nw_product_name': 'node-webkit', + 'nw_product_name': 'nwjs', 'mac_strip_release': 1, + 'nw_gen_path': '<(SHARED_INTERMEDIATE_DIR)/nw', + 'nw_id_script_base': 'commit_id.py', + 'nw_id_script': '<(nw_gen_path)/<(nw_id_script_base)', + 'nw_id_header_base': 'commit.h', + 'nw_id_header': '<(nw_gen_path)/id/<(nw_id_header_base)', + 'nw_use_commit_id%': '& rphs) { } } +void SetProxyConfigCallback( + base::WaitableEvent* done, + net::URLRequestContextGetter* url_request_context_getter, + const net::ProxyConfig& proxy_config) { + net::ProxyService* proxy_service = + url_request_context_getter->GetURLRequestContext()->proxy_service(); + proxy_service->ResetConfigService( + new net::ProxyConfigServiceFixed(proxy_config)); + done->Signal(); +} + } // namespace // static @@ -93,7 +122,8 @@ void App::Call(const std::string& method, void App::Call(Shell* shell, const std::string& method, const base::ListValue& arguments, - base::ListValue* result) { + base::ListValue* result, + DispatcherHost* dispatcher_host) { if (method == "GetDataPath") { ShellBrowserContext* browser_context = static_cast(shell->web_contents()->GetBrowserContext()); @@ -120,13 +150,36 @@ void App::Call(Shell* shell, } else if (method == "ClearCache") { ClearCache(GetRenderProcessHost()); return; + } else if (method == "CreateShortcut") { +#if defined(OS_WIN) + base::string16 path; + arguments.GetString(0, &path); + + base::win::ShortcutProperties props; + base::string16 appID; + if (content::Shell::GetPackage()->root()->GetString("app-id", &appID) == false) + content::Shell::GetPackage()->root()->GetString(switches::kmName, &appID); + const std::wstring appName = base::UTF8ToWide(content::Shell::GetPackage()->GetName()); + props.set_app_id(appID); + + base::FilePath processPath; + PathService::Get(base::FILE_EXE, &processPath); + props.set_target(processPath); + + base::FilePath shortcutPath(path); + result->AppendBoolean(base::win::CreateOrUpdateShortcutLink(shortcutPath, props, + base::PathExists(shortcutPath) ? base::win::SHORTCUT_UPDATE_EXISTING : base::win::SHORTCUT_CREATE_ALWAYS)); +#else + result->AppendBoolean(false); +#endif + return; } else if (method == "GetPackage") { result->AppendString(shell->GetPackage()->package_string()); return; } else if (method == "SetCrashDumpDir") { std::string path; arguments.GetString(0, &path); - result->AppendBoolean(SetCrashDumpPath(path.c_str())); + //FIXME: result->AppendBoolean(SetCrashDumpPath(path.c_str())); return; } else if (method == "RegisterGlobalHotKey") { int object_id = -1; @@ -148,6 +201,15 @@ void App::Call(Shell* shell, GlobalShortcutListener::GetInstance()->UnregisterAccelerator( shortcut->GetAccelerator(), shortcut); return; + } else if (method == "SetProxyConfig") { + std::string proxy_config, pac_url; + arguments.GetString(0, &proxy_config); + arguments.GetString(1, &pac_url); + SetProxyConfig(GetRenderProcessHost(), proxy_config, pac_url); + return; + } else if (method == "DoneMenuShow") { + dispatcher_host->quit_run_loop(); + return; } NOTREACHED() << "Calling unknown sync method " << method << " of App"; @@ -237,4 +299,29 @@ void App::ClearCache(content::RenderProcessHost* render_process_host) { render_process_host->GetID()); } +void App::SetProxyConfig(content::RenderProcessHost* render_process_host, + const std::string& proxy_config, + const std::string& pac_url) { + net::ProxyConfig 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()); + + base::WaitableEvent done(false, false); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&SetProxyConfigCallback, &done, + make_scoped_refptr(context_getter), config)); + done.Wait(); + +} } // namespace nwapi diff --git a/src/api/app/app.h b/src/api/app/app.h index 9cee82ab93..969b01fadc 100644 --- a/src/api/app/app.h +++ b/src/api/app/app.h @@ -22,6 +22,7 @@ #define CONTENT_NW_SRC_API_APP_APP_H_ #include "base/basictypes.h" +#include "../dispatcher_host.h" #include @@ -44,7 +45,8 @@ class App { static void Call(content::Shell* shell, const std::string& method, const base::ListValue& arguments, - base::ListValue* result); + base::ListValue* result, + DispatcherHost* dispatcher_host); // Try to close all windows (then will cause whole app to quit). static void CloseAllWindows(bool force = false, bool quit = false); @@ -60,6 +62,9 @@ class App { static void EmitReopenEvent(); static void ClearCache(content::RenderProcessHost* render_view_host); + static void SetProxyConfig(content::RenderProcessHost* render_process_host, + 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 b52e8dc697..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() { @@ -53,14 +53,26 @@ App.prototype.setCrashDumpDir = function(dir) { return nw.callStaticMethodSync('App', 'SetCrashDumpDir', [ dir ]); } +App.prototype.createShortcut = function(dir) { + return nw.callStaticMethodSync('App', 'CreateShortcut', [ dir ]); +} + App.prototype.clearCache = function() { nw.callStaticMethodSync('App', 'ClearCache', [ ]); } +App.prototype.doneMenuShow = function() { + nw.callStaticMethodSync('App', 'DoneMenuShow', [ ]); +} + App.prototype.getProxyForURL = function (url) { return nw.callStaticMethodSync('App', 'getProxyForURL', [ url ]); } +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) { return nw.callStaticMethodSync('App', 'AddOriginAccessWhitelistEntry', sourceOrigin, destinationProtocol, destinationHost, allowDestinationSubdomains); } diff --git a/src/api/bindings_common.cc b/src/api/bindings_common.cc index 48db9777e7..fbe444edc9 100644 --- a/src/api/bindings_common.cc +++ b/src/api/bindings_common.cc @@ -27,19 +27,20 @@ #include "content/public/renderer/render_thread.h" #include "content/public/renderer/v8_value_converter.h" #include "third_party/WebKit/public/web/WebView.h" -#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebLocalFrame.h" #include "ui/base/resource/resource_bundle.h" using content::RenderView; using content::RenderThread; using content::V8ValueConverter; using blink::WebFrame; +using blink::WebLocalFrame; using blink::WebView; namespace { RenderView* GetRenderView(v8::Handle ctx) { - WebFrame* frame = WebFrame::frameForContext(ctx); - if (!frame) + WebLocalFrame* frame = WebLocalFrame::frameForContext(ctx); + if (!frame || !frame->isNodeJS()) return NULL; WebView* view = frame->view(); diff --git a/src/api/clipboard/clipboard.h b/src/api/clipboard/clipboard.h index e0ed72f622..3d8451387f 100644 --- a/src/api/clipboard/clipboard.h +++ b/src/api/clipboard/clipboard.h @@ -31,13 +31,13 @@ class Clipboard : public Base { Clipboard(int id, const base::WeakPtr& dispatcher_host, const base::DictionaryValue& option); - virtual ~Clipboard(); + ~Clipboard() override; - virtual void Call(const std::string& method, - const base::ListValue& arguments) OVERRIDE; - virtual void CallSync(const std::string& method, + void Call(const std::string& method, + const base::ListValue& arguments) override; + void CallSync(const std::string& method, const base::ListValue& arguments, - base::ListValue* result) OVERRIDE; + base::ListValue* result) override; private: void SetText(std::string& text); diff --git a/src/api/clipboard/clipboard.js b/src/api/clipboard/clipboard.js index f4340bdae8..7202d313bb 100644 --- a/src/api/clipboard/clipboard.js +++ b/src/api/clipboard/clipboard.js @@ -30,7 +30,7 @@ Clipboard.prototype.set = function(data, type) { type = 'text'; if (type != 'text') - throw new String("Type of '" + type + "' is not supported"); + throw new TypeError("Type of '" + type + "' is not supported"); nw.callObjectMethod(this, 'Set', [ data, type ]); } @@ -40,7 +40,7 @@ Clipboard.prototype.get = function(type) { type = 'text'; if (type != 'text') - throw new String('Only support getting plain text from Clipboard'); + throw new TypeError('Only support getting plain text from Clipboard'); var result = nw.callObjectMethodSync(this, 'Get', [ type ]); if (type == 'text') diff --git a/src/api/dispatcher.cc b/src/api/dispatcher.cc index f664c4ade1..edfc4965ae 100644 --- a/src/api/dispatcher.cc +++ b/src/api/dispatcher.cc @@ -29,7 +29,7 @@ #undef CHECK #include "third_party/node/src/req_wrap.h" #include "third_party/WebKit/public/web/WebDocument.h" -#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebView.h" #include "v8/include/v8.h" @@ -43,7 +43,7 @@ #endif #include "third_party/WebKit/Source/config.h" #include "third_party/WebKit/Source/core/frame/Frame.h" -#include "third_party/WebKit/Source/web/WebFrameImpl.h" +#include "third_party/WebKit/Source/web/WebLocalFrameImpl.h" #include "V8HTMLElement.h" namespace nwapi { @@ -136,11 +136,10 @@ v8::Handle Dispatcher::GetWindowId(blink::WebFrame* frame) { return val; } -void Dispatcher::ZoomLevelChanged() { +void Dispatcher::ZoomLevelChanged(blink::WebView* web_view) { v8::Isolate* isolate = v8::Isolate::GetCurrent(); v8::HandleScope scope(isolate); - blink::WebView* web_view = render_view()->GetWebView(); float zoom_level = web_view->zoomLevel(); v8::Handle val = GetWindowId(web_view->mainFrame()); @@ -161,15 +160,15 @@ void Dispatcher::ZoomLevelChanged() { node::MakeCallback(isolate, objects_registry, "handleEvent", 3, argv); } -void Dispatcher::DidCreateDocumentElement(blink::WebFrame* frame) { +void Dispatcher::DidCreateDocumentElement(blink::WebLocalFrame* frame) { documentCallback("document-start", frame); } -void Dispatcher::DidFinishDocumentLoad(blink::WebFrame* frame) { +void Dispatcher::DidFinishDocumentLoad(blink::WebLocalFrame* frame) { documentCallback("document-end", frame); } -void Dispatcher::documentCallback(const char* ev, blink::WebFrame* frame) { +void Dispatcher::documentCallback(const char* ev, blink::WebLocalFrame* frame) { v8::Isolate* isolate = v8::Isolate::GetCurrent(); blink::WebView* web_view = render_view()->GetWebView(); v8::HandleScope scope(isolate); @@ -190,9 +189,9 @@ void Dispatcher::documentCallback(const char* ev, blink::WebFrame* frame) { v8::Local args = v8::Array::New(isolate); v8::Handle element = v8::Null(isolate); - WebCore::LocalFrame* core_frame = blink::toWebFrameImpl(frame)->frame(); - if (core_frame->ownerElement()) { - element = WebCore::toV8((WebCore::HTMLElement*)core_frame->ownerElement(), + blink::LocalFrame* core_frame = blink::toWebLocalFrameImpl(frame)->frame(); + if (core_frame->deprecatedLocalOwner()) { + element = blink::toV8((blink::HTMLElement*)core_frame->deprecatedLocalOwner(), frame->mainWorldScriptContext()->Global(), frame->mainWorldScriptContext()->GetIsolate()); } @@ -206,7 +205,8 @@ void Dispatcher::willHandleNavigationPolicy( content::RenderView* rv, blink::WebFrame* frame, const blink::WebURLRequest& request, - blink::WebNavigationPolicy* policy) { + blink::WebNavigationPolicy* policy, + blink::WebString* manifest) { blink::WebView* web_view = rv->GetWebView(); @@ -214,8 +214,13 @@ void Dispatcher::willHandleNavigationPolicy( return; v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::HandleScope handleScope(isolate); - v8::Handle id_val = nwapi::Dispatcher::GetWindowId(web_view->mainFrame()); + v8::Handle id_val; + 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()) @@ -231,9 +236,9 @@ void Dispatcher::willHandleNavigationPolicy( v8::Handle element = v8::Null(isolate); v8::Handle policy_obj = v8::Object::New(isolate); - WebCore::LocalFrame* core_frame = blink::toWebFrameImpl(frame)->frame(); - if (core_frame->ownerElement()) { - element = WebCore::toV8((WebCore::HTMLElement*)core_frame->ownerElement(), + blink::LocalFrame* core_frame = blink::toWebLocalFrameImpl(frame)->frame(); + if (core_frame->deprecatedLocalOwner()) { + element = blink::toV8((blink::HTMLElement*)core_frame->deprecatedLocalOwner(), frame->mainWorldScriptContext()->Global(), frame->mainWorldScriptContext()->GetIsolate()); } @@ -244,6 +249,15 @@ void Dispatcher::willHandleNavigationPolicy( v8::Handle argv[] = {id_val, v8_str("new-win-policy"), args }; node::MakeCallback(isolate, objects_registry, "handleEvent", 3, argv); + v8::Local manifest_val = policy_obj->Get(v8_str("manifest")); + + //TODO: change this to object + if (manifest_val->IsString()) { + v8::String::Utf8Value manifest_str(manifest_val); + if (manifest) + *manifest = blink::WebString::fromUTF8(*manifest_str); + } + v8::Local val = policy_obj->Get(v8_str("val")); if (!val->IsString()) return; diff --git a/src/api/dispatcher.h b/src/api/dispatcher.h index 51be736156..0193f55d66 100644 --- a/src/api/dispatcher.h +++ b/src/api/dispatcher.h @@ -25,6 +25,7 @@ #include "content/public/renderer/render_view_observer.h" #include "third_party/WebKit/public/web/WebNavigationPolicy.h" #include +#include namespace base { class ListValue; @@ -37,6 +38,7 @@ class RenderView; namespace blink { class WebFrame; class WebURLRequest; +class WebView; } namespace nwapi { @@ -44,25 +46,26 @@ namespace nwapi { class Dispatcher : public content::RenderViewObserver { public: explicit Dispatcher(content::RenderView* render_view); - virtual ~Dispatcher(); + ~Dispatcher() final; static v8::Handle GetObjectRegistry(); static v8::Handle GetWindowId(blink::WebFrame* frame); + static void ZoomLevelChanged(blink::WebView* web_view); static void willHandleNavigationPolicy( content::RenderView* rv, blink::WebFrame* frame, const blink::WebURLRequest& request, - blink::WebNavigationPolicy* policy); + blink::WebNavigationPolicy* policy, + blink::WebString* manifest); private: // RenderViewObserver implementation. - virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; - virtual void DraggableRegionsChanged(blink::WebFrame* frame) OVERRIDE; - virtual void ZoomLevelChanged() OVERRIDE; - virtual void DidFinishDocumentLoad(blink::WebFrame* frame) OVERRIDE; - virtual void DidCreateDocumentElement(blink::WebFrame* frame) OVERRIDE; + bool OnMessageReceived(const IPC::Message& message) override; + void DraggableRegionsChanged(blink::WebFrame* frame) override; + void DidFinishDocumentLoad(blink::WebLocalFrame* frame) override; + void DidCreateDocumentElement(blink::WebLocalFrame* frame) override; - void documentCallback(const char* ev, blink::WebFrame* frame); + void documentCallback(const char* ev, blink::WebLocalFrame* frame); void OnEvent(int object_id, std::string event, diff --git a/src/api/dispatcher_bindings.cc b/src/api/dispatcher_bindings.cc index c68a26fef9..44f7c58b3b 100644 --- a/src/api/dispatcher_bindings.cc +++ b/src/api/dispatcher_bindings.cc @@ -21,17 +21,17 @@ #include "content/nw/src/api/dispatcher_bindings.h" #include "base/files/file_path.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/values.h" #include "base/command_line.h" -#include "chrome/renderer/static_v8_external_string_resource.h" #include "content/nw/src/breakpad_linux.h" #include "content/nw/src/api/api_messages.h" #include "content/nw/src/api/bindings_common.h" #include "content/public/renderer/render_view.h" #include "content/public/renderer/render_thread.h" #include "content/public/renderer/v8_value_converter.h" +#include "extensions/renderer/static_v8_external_one_byte_string_resource.h" #include "grit/nw_resources.h" #include "third_party/node/src/node.h" #undef CHECK @@ -69,7 +69,7 @@ void RequireFromResource(v8::Handle root, v8::HandleScope handle_scope(isolate); v8::Handle source = v8::String::NewExternal(isolate, - new StaticV8ExternalAsciiStringResource( + new extensions::StaticV8ExternalOneByteStringResource( GetStringResource(resource_id))); v8::Handle wrapped_source = WrapSource(source); @@ -117,7 +117,7 @@ DispatcherBindings::DispatcherBindings() IDR_NW_API_DISPATCHER_BINDINGS_JS).data(), 0, // num dependencies. NULL, // dependencies array. - GetStringResource( + (int)GetStringResource( IDR_NW_API_DISPATCHER_BINDINGS_JS).size()) { #if defined(OS_MACOSX) InitMsgIDMap(); @@ -216,6 +216,8 @@ DispatcherBindings::RequireNwGui(const v8::FunctionCallbackInfo& args NwGui, global, v8::String::NewFromUtf8(isolate, "app.js"), IDR_NW_API_APP_JS); RequireFromResource(args.This(), NwGui, global, v8::String::NewFromUtf8(isolate, "shortcut.js"), IDR_NW_API_SHORTCUT_JS); + RequireFromResource(args.This(), + NwGui, global, v8::String::NewFromUtf8(isolate, "screen.js"), IDR_NW_API_SCREEN_JS); g_context->Exit(); args.GetReturnValue().Set(handle_scope.Escape(NwGui)); @@ -451,6 +453,13 @@ void DispatcherBindings::CallStaticMethod( // static void DispatcherBindings::CrashRenderer( const v8::FunctionCallbackInfo& args) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + RenderView* render_view = GetCurrentRenderView(); + if (!render_view) { + args.GetReturnValue().Set(isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(isolate, + "Unable to get render view in CallObjectMethod")))); + return; + } int* ptr = NULL; *ptr = 1; } @@ -459,8 +468,8 @@ void DispatcherBindings::CrashRenderer( void DispatcherBindings::SetCrashDumpDir( const v8::FunctionCallbackInfo& args) { #if defined(OS_WIN) || defined(OS_MACOSX) - std::string path = *v8::String::Utf8Value(args[0]); - SetCrashDumpPath(path.c_str()); + //std::string path = *v8::String::Utf8Value(args[0]); + //FIXME: SetCrashDumpPath(path.c_str()); #endif } diff --git a/src/api/dispatcher_bindings.h b/src/api/dispatcher_bindings.h index f44f23aedf..28cf641d51 100644 --- a/src/api/dispatcher_bindings.h +++ b/src/api/dispatcher_bindings.h @@ -30,13 +30,13 @@ namespace nwapi { class DispatcherBindings : public v8::Extension { public: DispatcherBindings(); - virtual ~DispatcherBindings(); + ~DispatcherBindings() final; // v8::Extension implementation. - virtual v8::Handle + v8::Handle GetNativeFunctionTemplate( v8::Isolate* isolate, - v8::Handle name) OVERRIDE; + v8::Handle name) override; private: // Helper functions for bindings. diff --git a/src/api/dispatcher_bindings_mac.mm b/src/api/dispatcher_bindings_mac.mm index 0cedab54e4..e77be987fe 100644 --- a/src/api/dispatcher_bindings_mac.mm +++ b/src/api/dispatcher_bindings_mac.mm @@ -2,6 +2,7 @@ #include +#include "base/containers/hash_tables.h" #include "grit/nw_strings.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util_mac.h" diff --git a/src/api/dispatcher_host.cc b/src/api/dispatcher_host.cc index 350d4899ef..dbbcd7b86c 100644 --- a/src/api/dispatcher_host.cc +++ b/src/api/dispatcher_host.cc @@ -21,6 +21,7 @@ #include "content/nw/src/api/dispatcher_host.h" #include "base/logging.h" +#include "base/run_loop.h" #include "base/threading/thread_restrictions.h" #include "base/values.h" #include "content/browser/child_process_security_policy_impl.h" @@ -29,8 +30,11 @@ #include "content/nw/src/api/app/app.h" #include "content/nw/src/api/base/base.h" #include "content/nw/src/api/clipboard/clipboard.h" +#include "content/nw/src/api/event/event.h" #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" @@ -54,7 +58,8 @@ static std::map g_dispatcher_host_map DispatcherHost::DispatcherHost(content::RenderViewHost* host) : content::WebContentsObserver(content::WebContents::FromRenderViewHost(host)), render_view_host_(host), - weak_ptr_factory_(this) { + weak_ptr_factory_(this), + run_loop_(NULL) { g_dispatcher_host_map[render_view_host_] = this; } @@ -101,10 +106,18 @@ bool DispatcherHost::Send(IPC::Message* message) { return render_view_host_->Send(message); } -bool DispatcherHost::OnMessageReceived(content::RenderViewHost* render_view_host, +void DispatcherHost::quit_run_loop() { + if (run_loop_) + run_loop_->Quit(); + run_loop_ = NULL; +} + +bool DispatcherHost::OnMessageReceived( + content::RenderViewHost* render_view_host, const IPC::Message& message) { if (render_view_host != render_view_host_) return false; + bool handled = true; base::ThreadRestrictions::ScopedAllowIO allow_io; base::ThreadRestrictions::ScopedAllowWait allow_wait; @@ -157,6 +170,10 @@ void DispatcherHost::OnAllocateObject(int object_id, objects_registry_.AddWithID(new Window(object_id, weak_ptr_factory_.GetWeakPtr(), option), object_id); } else if (type == "Shortcut") { 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); @@ -245,7 +262,10 @@ void DispatcherHost::OnCallStaticMethodSync( if (type == "App") { content::Shell* shell = content::Shell::FromRenderViewHost(render_view_host()); - nwapi::App::Call(shell, method, arguments, result); + nwapi::App::Call(shell, method, arguments, result, this); + return; + } else if (type == "Screen") { + nwapi::Screen::Call(this, method, arguments, result); return; } @@ -267,8 +287,7 @@ void DispatcherHost::OnGetShellId(int* id) { void DispatcherHost::OnCreateShell(const std::string& url, const base::DictionaryValue& manifest, int* routing_id) { - WebContents* base_web_contents = - content::Shell::FromRenderViewHost(render_view_host())->web_contents(); + WebContents* base_web_contents = web_contents(); ShellBrowserContext* browser_context = static_cast(base_web_contents->GetBrowserContext()); scoped_ptr new_manifest(manifest.DeepCopy()); diff --git a/src/api/dispatcher_host.h b/src/api/dispatcher_host.h index e9384ca98e..cdd8326875 100644 --- a/src/api/dispatcher_host.h +++ b/src/api/dispatcher_host.h @@ -32,6 +32,7 @@ namespace base { class DictionaryValue; class ListValue; +class RunLoop; } namespace WebKit { @@ -49,7 +50,7 @@ class Base; class DispatcherHost : public content::WebContentsObserver { public: explicit DispatcherHost(content::RenderViewHost* render_view_host); - virtual ~DispatcherHost(); + ~DispatcherHost() final; // Get C++ object from its id. static Base* GetApiObject(int id); @@ -69,13 +70,17 @@ class DispatcherHost : public content::WebContentsObserver { const std::string& event, const base::ListValue& arguments); - virtual bool Send(IPC::Message* message) OVERRIDE; - virtual void RenderViewHostChanged(content::RenderViewHost* old_host, - content::RenderViewHost* new_host) OVERRIDE; + bool Send(IPC::Message* message) override; + void RenderViewHostChanged(content::RenderViewHost* old_host, + content::RenderViewHost* new_host) override; content::RenderViewHost* render_view_host() const { return render_view_host_; } + void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; } + void quit_run_loop(); + base::RunLoop* run_loop() { return run_loop_; } + private: content::RenderViewHost* render_view_host_; friend class content::Shell; @@ -88,9 +93,14 @@ class DispatcherHost : public content::WebContentsObserver { // Factory to generate weak pointer base::WeakPtrFactory weak_ptr_factory_; + base::RunLoop* run_loop_; + // RenderViewHostObserver implementation. // WebContentsObserver implementation: - virtual bool OnMessageReceived(content::RenderViewHost* render_view_host, const IPC::Message& message) OVERRIDE; + bool OnMessageReceived( + content::RenderViewHost* render_view_host, + const IPC::Message& message) override; + void OnAllocateObject(int object_id, const std::string& type, diff --git a/src/api/event/event.cc b/src/api/event/event.cc new file mode 100644 index 0000000000..b42649314e --- /dev/null +++ b/src/api/event/event.cc @@ -0,0 +1,41 @@ +// Copyright (c) 2014 Jefry Tedjokusumo +// +// 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/event/event.h" +#include "base/values.h" +#include "content/nw/src/api/dispatcher_host.h" +#include "ui/gfx/screen.h" + + +namespace nwapi { + +EventListener::EventListener(int id, + const base::WeakPtr& dispatcher_host, + const base::DictionaryValue& option) : Base(id, dispatcher_host, option) { + +} + +EventListener::~EventListener() { + for (std::map::iterator i = listerners_.begin(); i != listerners_.end(); i++) { + delete i->second; + } +} + +} // namespace nwapi diff --git a/src/api/event/event.h b/src/api/event/event.h new file mode 100644 index 0000000000..cfe81a6bf7 --- /dev/null +++ b/src/api/event/event.h @@ -0,0 +1,83 @@ +// Copyright (c) 2014 Jefry Tedjokusumo +// +// 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_EVENT_EVENT_H_ +#define CONTENT_NW_SRC_API_EVENT_EVENT_H_ + + +#include "base/basictypes.h" + +#include "content/nw/src/api/base/base.h" +#include "ui/gfx/display_observer.h" + +#include + +namespace nwapi { + +class BaseEvent { + friend class EventListener; + DISALLOW_COPY_AND_ASSIGN(BaseEvent); + +protected: + BaseEvent(){} + virtual ~BaseEvent(){} +}; + +class EventListener : public Base { + std::map listerners_; + +public: + EventListener(int id, + const base::WeakPtr& dispatcher_host, + const base::DictionaryValue& option); + + ~EventListener() override; + + static int getUID() { + static int id = 0; + return ++id; + } + + template T* AddListener() { + std::map::iterator i = listerners_.find(T::id); + if (i==listerners_.end()) { + T* listener_object = new T(this); + listerners_[T::id] = listener_object; + return listener_object; + } + return NULL; + } + + template bool RemoveListener() { + std::map::iterator i = listerners_.find(T::id); + if (i!=listerners_.end()) { + delete i->second; + listerners_.erase(i); + return true; + } + return false; + } +private: + DISALLOW_COPY_AND_ASSIGN(EventListener); +}; + +} // namespace nwapi + +#endif //CONTENT_NW_SRC_API_EVENT_EVENT_H_ diff --git a/src/api/menu/menu.cc b/src/api/menu/menu.cc index 2a66f8b0dc..688603fc98 100644 --- a/src/api/menu/menu.cc +++ b/src/api/menu/menu.cc @@ -30,7 +30,7 @@ namespace nwapi { Menu::Menu(int id, const base::WeakPtr& dispatcher_host, const base::DictionaryValue& option) - : Base(id, dispatcher_host, option) { + : Base(id, dispatcher_host, option), enable_show_event_(false) { Create(option); } @@ -63,6 +63,8 @@ void Menu::Call(const std::string& method, arguments.GetInteger(1, &y); Popup(x, y, content::Shell::FromRenderViewHost( dispatcher_host()->render_view_host())); + } else if (method == "EnableShowEvent") { + arguments.GetBoolean(0, &enable_show_event_); } else { NOTREACHED() << "Invalid call to Menu method:" << method << " arguments:" << arguments; diff --git a/src/api/menu/menu.h b/src/api/menu/menu.h index e3384d8be7..0b9148d260 100644 --- a/src/api/menu/menu.h +++ b/src/api/menu/menu.h @@ -28,29 +28,29 @@ #include #include +#if defined(OS_WIN) +#include "ui/views/controls/menu/native_menu_win.h" +#endif + #if defined(OS_MACOSX) #if __OBJC__ @class NSMenu; +@class NWMenuDelegate; #else class NSMenu; +class NWMenuDelegate; #endif // __OBJC__ namespace nw { class NativeWindowCocoa; } -#elif defined(TOOLKIT_GTK) -#include -namespace nw { -class NativeWindowGtk; -} -#elif defined(OS_WIN) -#include "content/nw/src/api/menu/menu_delegate_win.h" -#include "ui/views/controls/menu/native_menu_win.h" +#elif defined(OS_WIN) || defined(OS_LINUX) +#include "content/nw/src/api/menu/menu_delegate.h" #include "chrome/browser/status_icons/status_icon_menu_model.h" #include "ui/views/focus/focus_manager.h" namespace nw { -class NativeWindowWin; +class NativeWindowAura; } namespace nwapi { @@ -68,8 +68,8 @@ class NwMenuModel : public SimpleMenuModel { NwMenuModel(Delegate* delegate); // Overridden from MenuModel: - virtual bool HasIcons() const OVERRIDE; - + bool HasIcons() const override; + protected: friend class nwapi::Menu; }; @@ -91,19 +91,20 @@ class Menu : public Base { Menu(int id, const base::WeakPtr& dispatcher_host, const base::DictionaryValue& option); - virtual ~Menu(); + ~Menu() override; - virtual void Call(const std::string& method, - const base::ListValue& arguments) OVERRIDE; + void Call(const std::string& method, + const base::ListValue& arguments) override; -#if defined(OS_LINUX) - void UpdateKeys(GtkAccelGroup *gtk_accel_group); -#endif - -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_LINUX) void UpdateKeys(views::FocusManager *focus_manager); + ui::NwMenuModel* model() { return menu_model_.get(); } #endif + bool enable_show_event() { return enable_show_event_; } + protected: + bool enable_show_event_; + private: friend class MenuItem; friend class Tray; @@ -118,27 +119,38 @@ class Menu : public Base { #if defined(OS_LINUX) std::vector menu_items; - GtkAccelGroup *gtk_accel_group; #endif #if defined(OS_MACOSX) friend class nw::NativeWindowCocoa; NSMenu* menu_; -#elif defined(TOOLKIT_GTK) - friend class nw::NativeWindowGtk; - GtkWidget* menu_; + NWMenuDelegate* menu_delegate_; +#elif defined(OS_LINUX) + friend class nw::NativeWindowAura; + + views::FocusManager *focus_manager_; + std::vector menu_items_; + nw::NativeWindowAura* window_; + // Flag to indicate the menu has been modified since last show, so we should + // rebuild the menu before next show. + bool is_menu_modified_; + + scoped_ptr menu_delegate_; + scoped_ptr menu_model_; + void UpdateStates(); + #elif defined(OS_WIN) - friend class nw::NativeWindowWin; + friend class nw::NativeWindowAura; void Rebuild(const HMENU *parent_menu = NULL); void UpdateStates(); - void SetWindow(nw::NativeWindowWin* win); + void SetWindow(nw::NativeWindowAura* win); //**Never Try to free this pointer** //We get it from top widget views::FocusManager *focus_manager_; std::vector menu_items_; - nw::NativeWindowWin* window_; + nw::NativeWindowAura* window_; // Flag to indicate the menu has been modified since last show, so we should // rebuild the menu before next show. bool is_menu_modified_; diff --git a/src/api/menu/menu.js b/src/api/menu/menu.js index c20a98033f..82b284e36b 100644 --- a/src/api/menu/menu.js +++ b/src/api/menu/menu.js @@ -19,13 +19,15 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. var v8_util = process.binding('v8_util'); +var EventEmitter = process.EventEmitter; + function Menu(option) { if (typeof option != 'object') option = { type: 'contextmenu' }; if (option.type != 'contextmenu' && option.type != 'menubar') - throw new String('Invalid menu type: ' + option.type); + throw new TypeError('Invalid menu type: ' + option.type); this.type = option.type; v8_util.setHiddenValue(this, 'items', []); @@ -38,12 +40,12 @@ Menu.prototype.__defineGetter__('items', function() { }); Menu.prototype.__defineSetter__('items', function(val) { - throw new String('Menu.items is immutable'); + throw new Error('Menu.items is immutable'); }); Menu.prototype.append = function(menu_item) { if (v8_util.getConstructorName(menu_item) != 'MenuItem') - throw new String("Menu.append() requires a valid MenuItem"); + throw new TypeError("Menu.append() requires a valid MenuItem"); this.items.push(menu_item); nw.callObjectMethod(this, 'Append', [ menu_item.id ]); @@ -70,8 +72,26 @@ Menu.prototype.popup = function(x, y) { } if (require('os').platform() === 'darwin'){ - Menu.prototype.createMacBuiltin = function (app_name) { - var appleMenu = new Menu(); + Menu.prototype.on = Menu.prototype.addListener = function(ev, callback) { + if (ev == 'show') { + nw.callObjectMethod(this, 'EnableShowEvent', [ true ]); + } + // Call parent. + EventEmitter.prototype.addListener.apply(this, arguments); + } + + Menu.prototype.removeListener = function(ev, callback) { + // Call parent. + EventEmitter.prototype.removeListener.apply(this, arguments); + if (ev == 'show' && EventEmitter.listenerCount(this, 'show') === 0) { + nw.callObjectMethod(this, 'EnableShowEvent', [ false ]); + } + } + + Menu.prototype.createMacBuiltin = function (app_name, options) { + var appleMenu = new Menu(), + options = options || {}; + appleMenu.append(new exports.MenuItem({ label: nw.getNSStringFWithFixup("IDS_ABOUT_MAC", app_name), selector: "orderFrontStandardAboutPanel:" @@ -85,7 +105,7 @@ if (require('os').platform() === 'darwin'){ key: "h" })); appleMenu.append(new exports.MenuItem({ - label: nw.getNSStringFWithFixup("IDS_HIDE_OTHERS_MAC", app_name), + label: nw.getNSStringWithFixup("IDS_HIDE_OTHERS_MAC"), selector: "hideOtherApplications:", key: "h", modifiers: "cmd-alt" @@ -104,69 +124,73 @@ if (require('os').platform() === 'darwin'){ })); this.append(new exports.MenuItem({ label:'', submenu: appleMenu})); - var editMenu = new Menu(); - editMenu.append(new exports.MenuItem({ - label: nw.getNSStringWithFixup("IDS_EDIT_UNDO_MAC"), - selector: "undo:", - key: "z" - })); - editMenu.append(new exports.MenuItem({ - label: nw.getNSStringWithFixup("IDS_EDIT_REDO_MAC"), - selector: "redo:", - key: "z", - modifiers: "cmd-shift" - })); - editMenu.append(new exports.MenuItem({ - type: "separator" - })); - editMenu.append(new exports.MenuItem({ - label: nw.getNSStringWithFixup("IDS_CUT_MAC"), - selector: "cut:", - key: "x" - })); - editMenu.append(new exports.MenuItem({ - label: nw.getNSStringWithFixup("IDS_COPY_MAC"), - selector: "copy:", - key: "c" - })); - editMenu.append(new exports.MenuItem({ - label: nw.getNSStringWithFixup("IDS_PASTE_MAC"), - selector: "paste:", - key: "v" - })); - editMenu.append(new exports.MenuItem({ - label: nw.getNSStringWithFixup("IDS_EDIT_DELETE_MAC"), - selector: "delete:", - key: "" - })); - editMenu.append(new exports.MenuItem({ - label: nw.getNSStringWithFixup("IDS_EDIT_SELECT_ALL_MAC"), - selector: "selectAll:", - key: "a" - })); - this.append(new exports.MenuItem({ label: nw.getNSStringWithFixup("IDS_EDIT_MENU_MAC"), - submenu: editMenu})); - - var winMenu = new Menu(); - winMenu.append(new exports.MenuItem({ - label: nw.getNSStringWithFixup("IDS_MINIMIZE_WINDOW_MAC"), - selector: "performMiniaturize:", - key: "m" - })); - winMenu.append(new exports.MenuItem({ - label: nw.getNSStringWithFixup("IDS_CLOSE_WINDOW_MAC"), - selector: "performClose:", - key: "w" - })); - winMenu.append(new exports.MenuItem({ - type: "separator" - })); - winMenu.append(new exports.MenuItem({ - label: nw.getNSStringWithFixup("IDS_ALL_WINDOWS_FRONT_MAC"), - selector: "arrangeInFront:", - })); - this.append(new exports.MenuItem({ label: nw.getNSStringWithFixup("IDS_WINDOW_MENU_MAC"), - submenu: winMenu})); + if (!options.hideEdit) { + var editMenu = new Menu(); + editMenu.append(new exports.MenuItem({ + label: nw.getNSStringWithFixup("IDS_EDIT_UNDO_MAC"), + selector: "undo:", + key: "z" + })); + editMenu.append(new exports.MenuItem({ + label: nw.getNSStringWithFixup("IDS_EDIT_REDO_MAC"), + selector: "redo:", + key: "z", + modifiers: "cmd-shift" + })); + editMenu.append(new exports.MenuItem({ + type: "separator" + })); + editMenu.append(new exports.MenuItem({ + label: nw.getNSStringWithFixup("IDS_CUT_MAC"), + selector: "cut:", + key: "x" + })); + editMenu.append(new exports.MenuItem({ + label: nw.getNSStringWithFixup("IDS_COPY_MAC"), + selector: "copy:", + key: "c" + })); + editMenu.append(new exports.MenuItem({ + label: nw.getNSStringWithFixup("IDS_PASTE_MAC"), + selector: "paste:", + key: "v" + })); + editMenu.append(new exports.MenuItem({ + label: nw.getNSStringWithFixup("IDS_EDIT_DELETE_MAC"), + selector: "delete:", + key: "" + })); + editMenu.append(new exports.MenuItem({ + label: nw.getNSStringWithFixup("IDS_EDIT_SELECT_ALL_MAC"), + selector: "selectAll:", + key: "a" + })); + this.append(new exports.MenuItem({ label: nw.getNSStringWithFixup("IDS_EDIT_MENU_MAC"), + submenu: editMenu})); + } + + if (!options.hideWindow) { + var winMenu = new Menu(); + winMenu.append(new exports.MenuItem({ + label: nw.getNSStringWithFixup("IDS_MINIMIZE_WINDOW_MAC"), + selector: "performMiniaturize:", + key: "m" + })); + winMenu.append(new exports.MenuItem({ + label: nw.getNSStringWithFixup("IDS_CLOSE_WINDOW_MAC"), + selector: "performClose:", + key: "w" + })); + winMenu.append(new exports.MenuItem({ + type: "separator" + })); + winMenu.append(new exports.MenuItem({ + label: nw.getNSStringWithFixup("IDS_ALL_WINDOWS_FRONT_MAC"), + selector: "arrangeInFront:", + })); + this.append(new exports.MenuItem({ label: nw.getNSStringWithFixup("IDS_WINDOW_MENU_MAC"), + submenu: winMenu})); + } } } exports.Menu = Menu; diff --git a/src/api/menu/menu_delegate_win.cc b/src/api/menu/menu_delegate.cc similarity index 94% rename from src/api/menu/menu_delegate_win.cc rename to src/api/menu/menu_delegate.cc index 37941590df..a094211020 100644 --- a/src/api/menu/menu_delegate_win.cc +++ b/src/api/menu/menu_delegate.cc @@ -1,102 +1,109 @@ -// 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/menu/menu_delegate_win.h" - -#include "base/logging.h" -#include "base/strings/string16.h" -#include "content/nw/src/api/dispatcher_host.h" -#include "content/nw/src/api/menuitem/menuitem.h" - -namespace nwapi { - -MenuDelegate::MenuDelegate(DispatcherHost* dispatcher_host) - : dispatcher_host_(dispatcher_host) { -} - -MenuDelegate::~MenuDelegate() { -} - -bool MenuDelegate::IsCommandIdChecked(int command_id) const { - if (command_id < 0) - return false; - - MenuItem* item = dispatcher_host_->GetApiObject(command_id); - return item->is_checked_; -} - -bool MenuDelegate::IsCommandIdEnabled(int command_id) const { - if (command_id < 0) - return false; - - MenuItem* item = dispatcher_host_->GetApiObject(command_id); - if (!item) - return false; - return item->is_enabled_; -} - -bool MenuDelegate::IsItemForCommandIdDynamic(int command_id) const { - if (command_id < 0) - return false; - - MenuItem* item = dispatcher_host_->GetApiObject(command_id); - if (!item) - return false; - return item->is_modified_; -} - +// 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/menu/menu_delegate.h" + +#include "base/logging.h" +#include "base/strings/string16.h" +#include "content/nw/src/api/dispatcher_host.h" +#include "content/nw/src/api/menuitem/menuitem.h" + +namespace nwapi { + +MenuDelegate::MenuDelegate(DispatcherHost* dispatcher_host) + : dispatcher_host_(dispatcher_host) { +} + +MenuDelegate::~MenuDelegate() { +} + +bool MenuDelegate::IsCommandIdChecked(int command_id) const { + if (command_id < 0) + return false; + + MenuItem* item = dispatcher_host_->GetApiObject(command_id); + return item->is_checked_; +} + +bool MenuDelegate::IsCommandIdEnabled(int command_id) const { + if (command_id < 0) + return false; + + MenuItem* item = dispatcher_host_->GetApiObject(command_id); + if (!item) + return false; + return item->is_enabled_; +} + +bool MenuDelegate::IsItemForCommandIdDynamic(int command_id) const { + if (command_id < 0) + return false; + + MenuItem* item = dispatcher_host_->GetApiObject(command_id); + if (!item) + return false; + return item->is_modified_; +} + base::string16 MenuDelegate::GetLabelForCommandId(int command_id) const { - MenuItem* item = dispatcher_host_->GetApiObject(command_id); - return item->label_; -} - -bool MenuDelegate::GetIconForCommandId(int command_id, - gfx::Image* icon) const { - MenuItem* item = dispatcher_host_->GetApiObject(command_id); - if (!item) - return false; - if (item->icon_.IsEmpty()) - return false; - - *icon = item->icon_; - return true; -} - -void MenuDelegate::ExecuteCommand(int command_id, int event_flags) { - if (command_id < 0) - return; - - MenuItem* item = dispatcher_host_->GetApiObject(command_id); - if (!item) - return; - item->OnClick(); -} - -bool MenuDelegate::HasIcon(int command_id) { - if (command_id < 0) - return false; - - MenuItem* item = dispatcher_host_->GetApiObject(command_id); - if (!item) - return false; - return !item->icon_.IsEmpty(); -} - -} // namespace nwapi + MenuItem* item = dispatcher_host_->GetApiObject(command_id); + return item->label_; +} + + +bool MenuDelegate::GetAcceleratorForCommandId( + int command_id, + ui::Accelerator* accelerator) { + return false; +} + +bool MenuDelegate::GetIconForCommandId(int command_id, + gfx::Image* icon) const { + MenuItem* item = dispatcher_host_->GetApiObject(command_id); + if (!item) + return false; + if (item->icon_.IsEmpty()) + return false; + + *icon = item->icon_; + return true; +} + +void MenuDelegate::ExecuteCommand(int command_id, int event_flags) { + if (command_id < 0) + return; + + MenuItem* item = dispatcher_host_->GetApiObject(command_id); + if (!item) + return; + item->OnClick(); +} + +bool MenuDelegate::HasIcon(int command_id) { + if (command_id < 0) + return false; + + MenuItem* item = dispatcher_host_->GetApiObject(command_id); + if (!item) + return false; + return !item->icon_.IsEmpty(); +} + +} // namespace nwapi diff --git a/src/api/menu/menu_delegate_win.h b/src/api/menu/menu_delegate.h similarity index 70% rename from src/api/menu/menu_delegate_win.h rename to src/api/menu/menu_delegate.h index bda6a80075..5723cfda8b 100644 --- a/src/api/menu/menu_delegate_win.h +++ b/src/api/menu/menu_delegate.h @@ -1,59 +1,59 @@ -// 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_MENU_MENU_DELEGATE_H_ -#define CONTENT_NW_SRC_API_MENU_MENU_DELEGATE_H_ - -#include "ui/base/models/simple_menu_model.h" - -namespace nwapi { - -class DispatcherHost; - -class MenuDelegate : public ui::SimpleMenuModel::Delegate { - public: - MenuDelegate(DispatcherHost* dispatcher_host); - virtual ~MenuDelegate(); - - virtual bool IsCommandIdChecked(int command_id) const OVERRIDE; - virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; - - virtual bool GetAcceleratorForCommandId( - int command_id, - ui::Accelerator* accelerator) { return false; } - - virtual bool IsItemForCommandIdDynamic(int command_id) const OVERRIDE; - virtual base::string16 GetLabelForCommandId(int command_id) const OVERRIDE; - virtual bool GetIconForCommandId(int command_id, - gfx::Image* icon) const OVERRIDE; - - virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE; - - virtual bool HasIcon(int command_id) OVERRIDE; - - private: - DispatcherHost* dispatcher_host_; - - DISALLOW_COPY_AND_ASSIGN(MenuDelegate); -}; - -} // namespace nwapi - -#endif // CONTENT_NW_SRC_API_MENU_MENU_DELEGATE_H_ +// 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_MENU_MENU_DELEGATE_H_ +#define CONTENT_NW_SRC_API_MENU_MENU_DELEGATE_H_ + +#include "ui/base/models/simple_menu_model.h" + +namespace nwapi { + +class DispatcherHost; + +class MenuDelegate : public ui::SimpleMenuModel::Delegate { + public: + MenuDelegate(DispatcherHost* dispatcher_host); + ~MenuDelegate() override; + + bool IsCommandIdChecked(int command_id) const override; + bool IsCommandIdEnabled(int command_id) const override; + + bool GetAcceleratorForCommandId( + int command_id, + ui::Accelerator* accelerator) override; + + bool IsItemForCommandIdDynamic(int command_id) const override; + base::string16 GetLabelForCommandId(int command_id) const override; + bool GetIconForCommandId(int command_id, + gfx::Image* icon) const override; + + void ExecuteCommand(int command_id, int event_flags) override; + + bool HasIcon(int command_id) override; + + private: + DispatcherHost* dispatcher_host_; + + DISALLOW_COPY_AND_ASSIGN(MenuDelegate); +}; + +} // namespace nwapi + +#endif // CONTENT_NW_SRC_API_MENU_MENU_DELEGATE_H_ diff --git a/src/common/gpu_internals.h b/src/api/menu/menu_delegate_mac.h similarity index 76% rename from src/common/gpu_internals.h rename to src/api/menu/menu_delegate_mac.h index a8a1f27774..9421784bd4 100644 --- a/src/common/gpu_internals.h +++ b/src/api/menu/menu_delegate_mac.h @@ -18,10 +18,21 @@ // 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_COMMON_GPU_INTERNALS_H_ -#define CONTENT_NW_SRC_COMMON_GPU_INTERNALS_H_ +#ifndef CONTENT_NW_SRC_API_MENU_MENU_DELEGATE_MAC_H_ +#define CONTENT_NW_SRC_API_MENU_MENU_DELEGATE_MAC_H_ -void PrintGpuInfo(); -void PrintClientInfo(); +#import -#endif // CONTENT_NW_SRC_COMMON_GPU_INTERNALS_H_ +namespace nwapi { +class Menu; +} + +@interface NWMenuDelegate : NSObject { + @private + nwapi::Menu* nwmenu_; +} + +- (id)initWithMenu:(nwapi::Menu*)menu; + +@end +#endif // CONTENT_NW_SRC_API_MENU_MENU_DELEGATE_MAC_H_ diff --git a/src/api/menu/menu_delegate_mac.mm b/src/api/menu/menu_delegate_mac.mm new file mode 100644 index 0000000000..88ed52c903 --- /dev/null +++ b/src/api/menu/menu_delegate_mac.mm @@ -0,0 +1,64 @@ +// 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 "base/run_loop.h" +#include "content/nw/src/api/dispatcher_host.h" +#include "content/nw/src/api/menu/menu.h" +#include "content/nw/src/api/menu/menu_delegate_mac.h" +#include "content/nw/src/browser/native_window.h" +#include "content/nw/src/nw_shell.h" + +@implementation NWMenuDelegate + +- (id)initWithMenu:(nwapi::Menu*) menu { + if ((self = [super init])) { + nwmenu_ = menu; + } + return self; +} + +- (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action { + return NO; +} + +- (void)menuNeedsUpdate:(NSMenu*)menu { + + if (!nwmenu_->enable_show_event() || nwmenu_->dispatcher_host()->run_loop()) + return; + + // NSEvent* event = [NSApp currentEvent]; + // NSLog (@"%@\n", event); + // Cocoa will try to populate menu on every keystoke of the key equivlants, + // which is slow. The following bypassed it + + // if ([event type] != NSSystemDefined || [event subtype] == 8) + // return; + + if (!nwmenu_->enable_show_event()) + return; + + base::ListValue args; + base::RunLoop run_loop; + nwmenu_->dispatcher_host()->set_run_loop(&run_loop); + nwmenu_->dispatcher_host()->SendEvent(nwmenu_, "show", args); + run_loop.Run(); +} + +@end diff --git a/src/api/menu/menu_gtk.cc b/src/api/menu/menu_gtk.cc index 4adadc04e1..893c0ab45d 100644 --- a/src/api/menu/menu_gtk.cc +++ b/src/api/menu/menu_gtk.cc @@ -128,8 +128,7 @@ void Menu::Remove(MenuItem* menu_item, int pos) { } void Menu::Popup(int x, int y, content::Shell* shell) { - GdkEventButton* event = shell->web_contents()->GetRenderWidgetHostView()-> - GetLastMouseDown(); + GdkEventButton* event = NULL; //FIXME: shell->web_contents()->GetRenderWidgetHostView()->GetLastMouseDown(); uint32_t triggering_event_time = event ? event->time : GDK_CURRENT_TIME; gfx::Point point; if (!event) { diff --git a/src/api/menu/menu_mac.mm b/src/api/menu/menu_mac.mm index 1274bad0c0..34a27a1079 100644 --- a/src/api/menu/menu_mac.mm +++ b/src/api/menu/menu_mac.mm @@ -25,7 +25,8 @@ #include "base/values.h" #import #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_view.h" +#include "content/nw/src/api/dispatcher_host.h" +#include "content/nw/src/api/menu/menu_delegate_mac.h" #include "content/nw/src/api/menuitem/menuitem.h" #include "content/nw/src/browser/native_window_mac.h" #include "content/nw/src/nw_shell.h" @@ -35,10 +36,13 @@ void Menu::Create(const base::DictionaryValue& option) { menu_ = [[NSMenu alloc] initWithTitle:@"NW Menu"]; [menu_ setAutoenablesItems:NO]; + menu_delegate_ = [[NWMenuDelegate alloc] initWithMenu:this]; + [menu_ setDelegate:menu_delegate_]; } void Menu::Destroy() { [menu_ release]; + [menu_delegate_ release]; } void Menu::Append(MenuItem* menu_item) { @@ -58,7 +62,7 @@ NSWindow* window = static_cast(shell->window())->window(); NSEvent* currentEvent = [NSApp currentEvent]; - NSView* web_view = shell->web_contents()->GetView()->GetNativeView(); + NSView* web_view = shell->web_contents()->GetNativeView(); NSPoint position = { x, web_view.bounds.size.height - y }; NSTimeInterval eventTime = [currentEvent timestamp]; NSEvent* clickEvent = [NSEvent mouseEventWithType:NSRightMouseDown diff --git a/src/api/menu/menu_win.cc b/src/api/menu/menu_views.cc similarity index 81% rename from src/api/menu/menu_win.cc rename to src/api/menu/menu_views.cc index 9d67e93d5a..11d8d9ae97 100644 --- a/src/api/menu/menu_win.cc +++ b/src/api/menu/menu_views.cc @@ -21,23 +21,31 @@ #include "content/nw/src/api/menu/menu.h" #include "base/values.h" +#include "base/strings/utf_string_conversions.h" #include "content/nw/src/api/dispatcher_host.h" #include "content/nw/src/api/menuitem/menuitem.h" -#include "content/nw/src/browser/native_window_win.h" +#include "content/nw/src/browser/native_window_aura.h" #include "content/nw/src/nw_shell.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_view.h" #include "skia/ext/image_operations.h" -#include "ui/gfx/gdi_util.h" -#include "ui/gfx/icon_util.h" -#include "ui/views/controls/menu/menu_2.h" +#include "ui/aura/client/screen_position_client.h" +#include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" +#include "ui/views/controls/menu/menu_runner.h" #include "ui/views/widget/widget.h" - #include "ui/views/focus/focus_manager.h" #include "vector" +#if defined(OS_WIN) +#include "ui/gfx/gdi_util.h" +#include "ui/gfx/icon_util.h" +#include "ui/views/controls/menu/menu_2.h" +#endif + namespace { +#if defined(OS_WIN) + HBITMAP GetNativeBitmapFromSkBitmap(const SkBitmap& bitmap) { int width = bitmap.width(); int height = bitmap.height(); @@ -59,6 +67,7 @@ HBITMAP GetNativeBitmapFromSkBitmap(const SkBitmap& bitmap) { return native_bitmap; } +#endif } // namespace @@ -76,30 +85,39 @@ bool NwMenuModel::HasIcons() const { namespace nwapi { +#if defined(OS_WIN) // The width of the icon for the menuitem static const int kIconWidth = 16; // The height of the icon for the menuitem static const int kIconHeight = 16; +#endif void Menu::Create(const base::DictionaryValue& option) { is_menu_modified_ = true; menu_delegate_.reset(new MenuDelegate(dispatcher_host())); menu_model_.reset(new ui::NwMenuModel(menu_delegate_.get())); +#if defined(OS_WIN) menu_.reset(new views::NativeMenuWin(menu_model_.get(), NULL)); +#endif focus_manager_ = NULL; window_ = NULL; std::string type; + +#if defined(OS_WIN) if (option.GetString("type", &type) && type == "menubar") menu_->set_is_popup_menu(false); +#endif menu_items_.empty(); } void Menu::Destroy() { +#if defined(OS_WIN) for (size_t index = 0; index < icon_bitmaps_.size(); ++index) { ::DeleteObject(icon_bitmaps_[index]); } +#endif } void Menu::Append(MenuItem* menu_item) { @@ -131,7 +149,7 @@ void Menu::Insert(MenuItem* menu_item, int pos) { is_menu_modified_ = true; menu_item->menu_ = this; - + } void Menu::Remove(MenuItem* menu_item, int pos) { @@ -141,17 +159,34 @@ void Menu::Remove(MenuItem* menu_item, int pos) { } void Menu::Popup(int x, int y, content::Shell* shell) { - Rebuild(); + // Rebuild(); // Map point from document to screen. - POINT screen_point = { x, y }; - ClientToScreen((HWND)shell->web_contents()->GetView()->GetNativeView(), - &screen_point); - - menu_->RunMenuAt(gfx::Point(screen_point.x, screen_point.y), - views::Menu2::ALIGN_TOPLEFT); + gfx::Point screen_point(x, y); + + // Convert from content coordinates to window coordinates. + // This code copied from chrome_web_contents_view_delegate_views.cc + aura::Window* web_contents_window = + shell->web_contents()->GetNativeView(); + aura::Window* root_window = web_contents_window->GetRootWindow(); + aura::client::ScreenPositionClient* screen_position_client = + aura::client::GetScreenPositionClient(root_window); + if (screen_position_client) { + screen_position_client->ConvertPointToScreen(web_contents_window, + &screen_point); + } + views::MenuRunner runner(menu_model_.get(), views::MenuRunner::CONTEXT_MENU); + if (views::MenuRunner::MENU_DELETED == + runner.RunMenuAt(static_cast(shell->window())->window(), + NULL, + gfx::Rect(screen_point, gfx::Size()), + views::MENU_ANCHOR_TOPRIGHT, + ui::MENU_SOURCE_NONE)) + return; + // menu_->RunMenuAt(screen_point, views::Menu2::ALIGN_TOPLEFT); } +#if defined(OS_WIN) void Menu::Rebuild(const HMENU *parent_menu) { if (is_menu_modified_) { // Refresh menu before show. @@ -196,6 +231,7 @@ void Menu::Rebuild(const HMENU *parent_menu) { is_menu_modified_ = false; } } +#endif void Menu::UpdateKeys(views::FocusManager *focus_manager){ if (focus_manager == NULL){ @@ -211,11 +247,14 @@ void Menu::UpdateKeys(views::FocusManager *focus_manager){ } void Menu::UpdateStates() { +#if defined(OS_WIN) if (window_) window_->menu_->menu_->UpdateStates(); +#endif } -void Menu::SetWindow(nw::NativeWindowWin* win) { +#if defined(OS_WIN) +void Menu::SetWindow(nw::NativeWindowAura* win) { window_ = win; for (int model_index = 0; model_index < menu_model_->GetItemCount(); @@ -227,5 +266,6 @@ void Menu::SetWindow(nw::NativeWindowWin* win) { } } } +#endif } // namespace nwapi diff --git a/src/api/menuitem/menuitem.cc b/src/api/menuitem/menuitem.cc index 2765e4ac0c..e706617f66 100644 --- a/src/api/menuitem/menuitem.cc +++ b/src/api/menuitem/menuitem.cc @@ -50,6 +50,10 @@ void MenuItem::Call(const std::string& method, std::string icon; arguments.GetString(0, &icon); SetIcon(icon); + } else if (method == "SetIconIsTemplate") { + bool isTemplate; + arguments.GetBoolean(0, &isTemplate); + SetIconIsTemplate(isTemplate); } else if (method == "SetTooltip") { std::string tooltip; arguments.GetString(0, &tooltip); @@ -66,6 +70,16 @@ void MenuItem::Call(const std::string& method, int object_id = 0; arguments.GetInteger(0, &object_id); SetSubmenu(dispatcher_host()->GetApiObject(object_id)); +#if defined(OS_MACOSX) + } else if (method == "SetKey") { + std::string key; + arguments.GetString(0, &key); + SetKey(key); + } else if (method == "SetModifiers") { + std::string mod; + arguments.GetString(0, &mod); + SetModifiers(mod); +#endif } else { NOTREACHED() << "Invalid call to MenuItem method:" << method << " arguments:" << arguments; diff --git a/src/api/menuitem/menuitem.h b/src/api/menuitem/menuitem.h index f80af28b91..84af31c199 100644 --- a/src/api/menuitem/menuitem.h +++ b/src/api/menuitem/menuitem.h @@ -34,10 +34,7 @@ class NSMenuItem; class MenuItemDelegate; #endif // __OBJC__ -#elif defined(TOOLKIT_GTK) -#include -#include "ui/base/gtk/gtk_signal.h" -#elif defined(OS_WIN) +#elif defined(OS_WIN) || defined(OS_LINUX) #include "base/strings/string16.h" #include "ui/gfx/image/image.h" #include "ui/base/accelerators/accelerator.h" @@ -48,8 +45,8 @@ namespace nwapi { class Menu; -#if defined(OS_WIN) -class MenuItem : public Base , +#if defined(OS_WIN) || defined(OS_LINUX) +class MenuItem : public Base , public ui::AcceleratorTarget { #else class MenuItem : public Base { @@ -58,40 +55,18 @@ class MenuItem : public Base { MenuItem(int id, const base::WeakPtr& dispatcher_host, const base::DictionaryValue& option); - virtual ~MenuItem(); + ~MenuItem() override; - virtual void Call(const std::string& method, - const base::ListValue& arguments) OVERRIDE; + void Call(const std::string& method, + const base::ListValue& arguments) override; -#if defined(OS_LINUX) - void UpdateKeys(GtkAccelGroup *gtk_accel_group); -#endif - -#if defined(OS_WIN) - virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE{ - if (super_down_flag_){ - if ( ( (::GetKeyState(VK_LWIN) & 0x8000) != 0x8000) - || ( (::GetKeyState(VK_LWIN) & 0x8000) != 0x8000) ){ - return true; - } - } - if (meta_down_flag_){ - if ( (::GetKeyState(VK_APPS) & 0x8000) != 0x8000 ){ - return true; - } - } - OnClick(); - return true; - } - virtual bool CanHandleAccelerators() const OVERRIDE { - return true; - } +#if defined(OS_WIN) || defined(OS_LINUX) + bool AcceleratorPressed(const ui::Accelerator& accelerator) override; + bool CanHandleAccelerators() const override; void UpdateKeys(views::FocusManager *focus_manager); #endif -#if defined(OS_MACOSX) || defined(OS_WIN) void OnClick(); -#endif private: friend class Menu; @@ -103,40 +78,29 @@ class MenuItem : public Base { void SetIcon(const std::string& icon); void SetTooltip(const std::string& tooltip); void SetKey(const std::string& key); + void SetModifiers(const std::string& modifiers); void SetEnabled(bool enabled); void SetChecked(bool checked); void SetSubmenu(Menu* sub_menu); -#if defined(OS_LINUX) - GtkAccelGroup *gtk_accel_group; - GdkModifierType modifiers_mask; - guint keyval; - bool enable_shortcut; - Menu* submenu_; - std::string label_; -#endif + // Template icon works only on Mac OS X + void SetIconIsTemplate(bool isTemplate); #if defined(OS_MACOSX) std::string type_; NSMenuItem* menu_item_; MenuItemDelegate* delegate_; -#elif defined(TOOLKIT_GTK) - GtkWidget* menu_item_; - - // Don't send click event on active. - bool block_active_; + bool iconIsTemplate; - // Callback invoked when user left-clicks on the menu item. - CHROMEGTK_CALLBACK_0(MenuItem, void, OnClick); -#elif defined(OS_WIN) +#elif defined(OS_WIN) || defined(OS_LINUX) friend class MenuDelegate; Menu* menu_; //**Never Try to free this pointer** //We get it from top widget views::FocusManager *focus_manager_; - + ui::Accelerator accelerator_; // Flag to indicate we need refresh. diff --git a/src/api/menuitem/menuitem.js b/src/api/menuitem/menuitem.js index bacff28344..afb7e8aef9 100644 --- a/src/api/menuitem/menuitem.js +++ b/src/api/menuitem/menuitem.js @@ -22,7 +22,7 @@ var v8_util = process.binding('v8_util'); function MenuItem(option) { if (typeof option != 'object') - throw new String('Invalid option.'); + throw new TypeError('Invalid option.'); if (!option.hasOwnProperty('type')) option.type = 'normal'; @@ -30,14 +30,14 @@ function MenuItem(option) { if (option.type != 'normal' && option.type != 'checkbox' && option.type != 'separator') - throw new String('Invalid MenuItem type: ' + option.type); + throw new TypeError('Invalid MenuItem type: ' + option.type); if (option.type == 'normal' || option.type == 'checkbox') { if (option.type == 'checkbox') option.checked = Boolean(option.checked); if (!option.hasOwnProperty('label')) - throw new String('A normal MenuItem must have a label'); + throw new TypeError('A normal MenuItem must have a label'); else option.label = String(option.label); @@ -46,6 +46,11 @@ function MenuItem(option) { option.icon = nw.getAbsolutePath(option.icon); } + if (option.hasOwnProperty('iconIsTemplate')) + option.iconIsTemplate = Boolean(option.iconIsTemplate); + else + option.iconIsTemplate = true; + if (option.hasOwnProperty('tooltip')) option.tooltip = String(option.tooltip); @@ -54,7 +59,7 @@ function MenuItem(option) { if (option.hasOwnProperty('submenu')) { if (v8_util.getConstructorName(option.submenu) != 'Menu') - throw new String("'submenu' must be a valid Menu"); + throw new TypeError("'submenu' must be a valid Menu"); // Transfer only object id v8_util.setHiddenValue(this, 'submenu', option.submenu); @@ -63,7 +68,7 @@ function MenuItem(option) { if (option.hasOwnProperty('click')) { if (typeof option.click != 'function') - throw new String("'click' must be a valid Function"); + throw new TypeError("'click' must be a valid Function"); else this.click = option.click; } @@ -95,7 +100,7 @@ MenuItem.prototype.__defineGetter__('type', function() { }); MenuItem.prototype.__defineSetter__('type', function() { - throw new String("'type' is immutable at runtime"); + throw new Error("'type' is immutable at runtime"); }); MenuItem.prototype.__defineGetter__('label', function() { @@ -116,6 +121,14 @@ MenuItem.prototype.__defineSetter__('icon', function(val) { this.handleSetter('icon', 'SetIcon', String, real_path); }); +MenuItem.prototype.__defineGetter__('iconIsTemplate', function() { + return this.handleGetter('iconIsTemplate'); +}); + +MenuItem.prototype.__defineSetter__('iconIsTemplate', function(val) { + this.handleSetter('iconIsTemplate', 'SetIconIsTemplate', Boolean, val); +}); + MenuItem.prototype.__defineGetter__('tooltip', function() { return this.handleGetter('tooltip'); }); @@ -124,6 +137,22 @@ MenuItem.prototype.__defineSetter__('tooltip', function(val) { this.handleSetter('tooltip', 'SetTooltip', String, val); }); +MenuItem.prototype.__defineGetter__('key', function() { + return this.handleGetter('key'); +}); + +MenuItem.prototype.__defineSetter__('key', function(val) { + this.handleSetter('key', 'SetKey', String, val); +}); + +MenuItem.prototype.__defineGetter__('modifiers', function() { + return this.handleGetter('modifiers'); +}); + +MenuItem.prototype.__defineSetter__('modifiers', function(val) { + this.handleSetter('modifiers', 'SetModifiers', String, val); +}); + MenuItem.prototype.__defineGetter__('checked', function() { if (this.type != 'checkbox') return undefined; @@ -133,7 +162,7 @@ MenuItem.prototype.__defineGetter__('checked', function() { MenuItem.prototype.__defineSetter__('checked', function(val) { if (this.type != 'checkbox') - throw new String("'checked' property is only available for checkbox"); + throw new TypeError("'checked' property is only available for checkbox"); this.handleSetter('checked', 'SetChecked', Boolean, val); }); @@ -152,7 +181,7 @@ MenuItem.prototype.__defineGetter__('submenu', function() { MenuItem.prototype.__defineSetter__('submenu', function(val) { if (v8_util.getConstructorName(val) != 'Menu') - throw new String("'submenu' property requries a valid Menu"); + throw new TypeError("'submenu' property requries a valid Menu"); v8_util.setHiddenValue(this, 'submenu', val); nw.callObjectMethod(this, 'SetSubmenu', [ val.id ]); diff --git a/src/api/menuitem/menuitem_gtk.cc b/src/api/menuitem/menuitem_gtk.cc index f01651273f..91955e2924 100644 --- a/src/api/menuitem/menuitem_gtk.cc +++ b/src/api/menuitem/menuitem_gtk.cc @@ -82,6 +82,11 @@ void MenuItem::Create(const base::DictionaryValue& option) { if (modifiers.find("meta") != std::string::npos){ modifiers_mask = GdkModifierType(modifiers_mask|GDK_META_MASK); } + + if (modifiers.find("shift") != std::string::npos){ + modifiers_mask = GdkModifierType(modifiers_mask|GDK_SHIFT_MASK); + } + } keyval = gdk_keyval_from_name(key.c_str()); @@ -119,6 +124,9 @@ void MenuItem::SetIcon(const std::string& icon) { } } +void MenuItem::SetIconIsTemplate(bool isTemplate) { +} + void MenuItem::SetTooltip(const std::string& tooltip) { gtk_widget_set_tooltip_text(menu_item_, tooltip.c_str()); } diff --git a/src/api/menuitem/menuitem_mac.mm b/src/api/menuitem/menuitem_mac.mm index 36c8f6c55c..ed4ebf7fb5 100644 --- a/src/api/menuitem/menuitem_mac.mm +++ b/src/api/menuitem/menuitem_mac.mm @@ -71,6 +71,10 @@ if (option.GetBoolean("enabled", &enabled)) SetEnabled(enabled); + bool isTemplate; + if (option.GetBoolean("iconIsTemplate", &isTemplate)) + SetIconIsTemplate(isTemplate); + std::string icon; if (option.GetString("icon", &icon) && !icon.empty()) SetIcon(icon); @@ -85,17 +89,7 @@ std::string modifiers; if (option.GetString("modifiers", &modifiers)) { - NSUInteger mask = 0; - NSString* nsmodifiers = [NSString stringWithUTF8String:modifiers.c_str()]; - if([nsmodifiers rangeOfString:@"shift"].location != NSNotFound) - mask = mask|NSShiftKeyMask; - if([nsmodifiers rangeOfString:@"cmd"].location != NSNotFound) - mask = mask|NSCommandKeyMask; - if([nsmodifiers rangeOfString:@"alt"].location != NSNotFound) - mask = mask|NSAlternateKeyMask; - if([nsmodifiers rangeOfString:@"ctrl"].location != NSNotFound) - mask = mask|NSControlKeyMask; - [menu_item_ setKeyEquivalentModifierMask:mask]; + SetModifiers(modifiers); } int menu_id; @@ -125,12 +119,28 @@ void MenuItem::SetKey(const std::string& key) { [menu_item_ setKeyEquivalent:[NSString stringWithUTF8String:key.c_str()]]; + VLOG(1) << "setkey: " << key; +} + +void MenuItem::SetModifiers(const std::string& modifiers) { + NSUInteger mask = 0; + NSString* nsmodifiers = [NSString stringWithUTF8String:modifiers.c_str()]; + if([nsmodifiers rangeOfString:@"shift"].location != NSNotFound) + mask = mask|NSShiftKeyMask; + if([nsmodifiers rangeOfString:@"cmd"].location != NSNotFound) + mask = mask|NSCommandKeyMask; + if([nsmodifiers rangeOfString:@"alt"].location != NSNotFound) + mask = mask|NSAlternateKeyMask; + if([nsmodifiers rangeOfString:@"ctrl"].location != NSNotFound) + mask = mask|NSControlKeyMask; + [menu_item_ setKeyEquivalentModifierMask:mask]; } void MenuItem::SetIcon(const std::string& icon) { if (!icon.empty()) { NSImage* image = [[NSImage alloc] initWithContentsOfFile:[NSString stringWithUTF8String:icon.c_str()]]; + [image setTemplate:iconIsTemplate]; [menu_item_ setImage:image]; [image release]; } else { @@ -138,6 +148,12 @@ } } +void MenuItem::SetIconIsTemplate(bool isTemplate) { + iconIsTemplate = isTemplate; + if ([menu_item_ image] != nil) + [[menu_item_ image] setTemplate:isTemplate]; +} + void MenuItem::SetTooltip(const std::string& tooltip) { [menu_item_ setToolTip:[NSString stringWithUTF8String:tooltip.c_str()]]; } diff --git a/src/api/menuitem/menuitem_win.cc b/src/api/menuitem/menuitem_views.cc similarity index 92% rename from src/api/menuitem/menuitem_win.cc rename to src/api/menuitem/menuitem_views.cc index c28dc5b25b..238a7c46fc 100644 --- a/src/api/menuitem/menuitem_win.cc +++ b/src/api/menuitem/menuitem_views.cc @@ -1,16 +1,16 @@ // Copyright (c) 2012 Intel Corp // Copyright (c) 2012 The Chromium Authors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy +// +// 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 @@ -96,7 +96,7 @@ void MenuItem::Create(const base::DictionaryValue& option) { int menu_id; if (option.GetInteger("submenu", &menu_id)) SetSubmenu(dispatcher_host()->GetApiObject(menu_id)); -} +} void MenuItem::Destroy() { } @@ -114,8 +114,11 @@ void MenuItem::OnClick() { void MenuItem::SetLabel(const std::string& label) { is_modified_ = true; label_ = base::UTF8ToUTF16(label); + +#if 0//FIXME if (menu_) menu_->UpdateStates(); +#endif } void MenuItem::SetIcon(const std::string& icon) { @@ -131,6 +134,9 @@ void MenuItem::SetIcon(const std::string& icon) { package->GetImage(base::FilePath::FromUTF8Unsafe(icon), &icon_); } +void MenuItem::SetIconIsTemplate(bool isTemplate) { +} + void MenuItem::SetTooltip(const std::string& tooltip) { tooltip_ = base::UTF8ToUTF16(tooltip); if (menu_) @@ -162,7 +168,7 @@ void MenuItem::UpdateKeys(views::FocusManager *focus_manager){ focus_manager->RegisterAccelerator( accelerator_, ui::AcceleratorManager::kHighPriority, - this); + this); } if (submenu_ != NULL){ submenu_->UpdateKeys(focus_manager); @@ -170,6 +176,31 @@ void MenuItem::UpdateKeys(views::FocusManager *focus_manager){ } } +#if defined(OS_WIN) || defined(OS_LINUX) +bool MenuItem::AcceleratorPressed(const ui::Accelerator& accelerator) { + +#if defined(OS_WIN) + if (super_down_flag_){ + if ( ( (::GetKeyState(VK_LWIN) & 0x8000) != 0x8000) + || ( (::GetKeyState(VK_LWIN) & 0x8000) != 0x8000) ){ + return true; + } + } + if (meta_down_flag_){ + if ( (::GetKeyState(VK_APPS) & 0x8000) != 0x8000 ){ + return true; + } + } +#endif + OnClick(); + return true; +} + +bool MenuItem::CanHandleAccelerators() const { + return true; +} + +#endif } // namespace nwapi diff --git a/src/api/screen/desktop_capture_api.cc b/src/api/screen/desktop_capture_api.cc new file mode 100644 index 0000000000..91339c79b6 --- /dev/null +++ b/src/api/screen/desktop_capture_api.cc @@ -0,0 +1,224 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/api/screen/desktop_capture_api.h" + +#include "base/command_line.h" +#include "base/compiler_specific.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/media/native_desktop_media_list.h" +#include "content/public/browser/render_frame_host.h" +#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" +#include "third_party/webrtc/modules/desktop_capture/window_capturer.h" + +namespace nwapi { + +namespace { + +const char kEmptySourcesListError[] = + "At least one source type must be specified."; + +DesktopCaptureChooseDesktopMediaFunction::PickerFactory* g_picker_factory = + NULL; + +} // namespace + +// static +void DesktopCaptureChooseDesktopMediaFunction::SetPickerFactoryForTests( + PickerFactory* factory) { + g_picker_factory = factory; +} + +DesktopCaptureChooseDesktopMediaFunction:: + DesktopCaptureChooseDesktopMediaFunction() { +} + +DesktopCaptureChooseDesktopMediaFunction:: + ~DesktopCaptureChooseDesktopMediaFunction() { + // RenderViewHost may be already destroyed. + if (render_view_host()) { + DesktopCaptureRequestsRegistry::GetInstance()->RemoveRequest( + render_view_host()->GetProcess()->GetID(), request_id_); + } +} + +void DesktopCaptureChooseDesktopMediaFunction::Cancel() { + // Keep reference to |this| to ensure the object doesn't get destroyed before + // we return. + scoped_refptr self(this); + if (picker_) { + picker_.reset(); + SetResult(new base::StringValue(std::string())); + SendResponse(true); + } +} + +bool DesktopCaptureChooseDesktopMediaFunction::RunSync() { + + DesktopCaptureRequestsRegistry::GetInstance()->AddRequest( + render_view_host()->GetProcess()->GetID(), request_id_, this); + + // |web_contents| is the WebContents for which the stream is created, and will + // 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); + 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); + + bool show_screens = false; + bool show_windows = false; + + const base::ListValue* capture_param = NULL; + if(args_->GetList(1,&capture_param)) { + for(base::ListValue::const_iterator i = capture_param->begin(); i != capture_param->end(); i++) { + const base::Value* val = static_cast(*i); + std::string str; + if(val->GetAsString(&str)) { + if(!str.compare("window")) { + show_windows = true; + continue; + } + + if(!str.compare("screen")) { + show_screens = true; + continue; + } + } + } + } + + if (!show_screens && !show_windows) { + error_ = kEmptySourcesListError; + return false; + } + + const gfx::NativeWindow parent_window = + web_contents->GetTopLevelNativeWindow(); + scoped_ptr media_list; + if (g_picker_factory) { + media_list = g_picker_factory->CreateModel( + show_screens, show_windows); + picker_ = g_picker_factory->CreatePicker(); + } else { + { + webrtc::DesktopCaptureOptions options = + webrtc::DesktopCaptureOptions::CreateDefault(); + options.set_disable_effects(false); + scoped_ptr screen_capturer( + show_screens ? webrtc::ScreenCapturer::Create(options) : NULL); + scoped_ptr window_capturer( + show_windows ? webrtc::WindowCapturer::Create(options) : NULL); + + media_list.reset(new NativeDesktopMediaList( + screen_capturer.Pass(), window_capturer.Pass())); + } + + // DesktopMediaPicker is implemented only for Windows, OSX and + // Aura Linux builds. +#if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX) + picker_ = DesktopMediaPicker::Create(); +#else + error_ = "Desktop Capture API is not yet implemented for this platform."; + return false; +#endif + } + DesktopMediaPicker::DoneCallback callback = base::Bind( + &DesktopCaptureChooseDesktopMediaFunction::OnPickerDialogResults, this); + + picker_->Show(web_contents, + parent_window, + parent_window, + app_name, + app_name, + media_list.Pass(), + callback); + return true; +} + +void DesktopCaptureChooseDesktopMediaFunction::WebContentsDestroyed() { + Cancel(); +} + +void DesktopCaptureChooseDesktopMediaFunction::OnPickerDialogResults( + content::DesktopMediaID source) { + std::string result; + if (source.type != content::DesktopMediaID::TYPE_NONE && + web_contents()) { + result = source.ToString(); + } + + SetResult(new base::StringValue(result)); + SendResponse(true); +} + +DesktopCaptureRequestsRegistry::RequestId::RequestId(int process_id, + int request_id) + : process_id(process_id), + request_id(request_id) { +} + +bool DesktopCaptureRequestsRegistry::RequestId::operator<( + const RequestId& other) const { + if (process_id != other.process_id) { + return process_id < other.process_id; + } else { + return request_id < other.request_id; + } +} + +DesktopCaptureCancelChooseDesktopMediaFunction:: + DesktopCaptureCancelChooseDesktopMediaFunction() {} + +DesktopCaptureCancelChooseDesktopMediaFunction:: + ~DesktopCaptureCancelChooseDesktopMediaFunction() {} + +bool DesktopCaptureCancelChooseDesktopMediaFunction::RunSync() { + int request_id; + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &request_id)); + + DesktopCaptureRequestsRegistry::GetInstance()->CancelRequest( + render_view_host()->GetProcess()->GetID(), request_id); + return true; +} + +DesktopCaptureRequestsRegistry::DesktopCaptureRequestsRegistry() {} +DesktopCaptureRequestsRegistry::~DesktopCaptureRequestsRegistry() {} + +// static +DesktopCaptureRequestsRegistry* DesktopCaptureRequestsRegistry::GetInstance() { + return Singleton::get(); +} + +void DesktopCaptureRequestsRegistry::AddRequest( + int process_id, + int request_id, + DesktopCaptureChooseDesktopMediaFunction* handler) { + requests_.insert( + RequestsMap::value_type(RequestId(process_id, request_id), handler)); +} + +void DesktopCaptureRequestsRegistry::RemoveRequest(int process_id, + int request_id) { + requests_.erase(RequestId(process_id, request_id)); +} + +void DesktopCaptureRequestsRegistry::CancelRequest(int process_id, + int request_id) { + RequestsMap::iterator it = requests_.find(RequestId(process_id, request_id)); + if (it != requests_.end()) + it->second->Cancel(); +} + + +} // namespace extensions diff --git a/src/api/screen/desktop_capture_api.h b/src/api/screen/desktop_capture_api.h new file mode 100644 index 0000000000..89b8360873 --- /dev/null +++ b/src/api/screen/desktop_capture_api.h @@ -0,0 +1,115 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NWAPI_DESKTOP_CAPTURE_DESKTOP_CAPTURE_API_H_ +#define NWAPI_DESKTOP_CAPTURE_DESKTOP_CAPTURE_API_H_ + +#include + +#include "base/memory/singleton.h" +#include "chrome/browser/media/desktop_media_list.h" +#include "chrome/browser/media/desktop_media_picker.h" +#include "chrome/browser/media/native_desktop_media_list.h" +#include "extensions/browser/extension_function.h" +#include "content/public/browser/web_contents_observer.h" +#include "url/gurl.h" + +namespace nwapi { + +class DesktopCaptureChooseDesktopMediaFunction + : public SyncExtensionFunction, + public content::WebContentsObserver { + public: + + // Factory creating DesktopMediaList and DesktopMediaPicker instances. + // Used for tests to supply fake picker. + class PickerFactory { + public: + virtual scoped_ptr CreateModel(bool show_screens, + bool show_windows) = 0; + virtual scoped_ptr CreatePicker() = 0; + protected: + virtual ~PickerFactory() {} + }; + + // Used to set PickerFactory used to create mock DesktopMediaPicker instances + // for tests. Calling tests keep ownership of the factory. Can be called with + // |factory| set to NULL at the end of the test. + static void SetPickerFactoryForTests(PickerFactory* factory); + + DesktopCaptureChooseDesktopMediaFunction(); + + void Cancel(); + + // ExtensionFunction overrides. + bool RunSync() override; + + private: + ~DesktopCaptureChooseDesktopMediaFunction() override; + + // content::WebContentsObserver overrides. + void WebContentsDestroyed() override; + + void OnPickerDialogResults(content::DesktopMediaID source); + + int request_id_; + + // URL of page that desktop capture was requested for. + GURL origin_; + + scoped_ptr picker_; +}; + +// this api is not exposed in nwjs yet +class DesktopCaptureCancelChooseDesktopMediaFunction + : public SyncExtensionFunction { + public: + DesktopCaptureCancelChooseDesktopMediaFunction(); + + private: + ~DesktopCaptureCancelChooseDesktopMediaFunction() override; + + // ExtensionFunction overrides. + bool RunSync() override; +}; + +// this class is only needed if we want the ability to cancel "choose desktop media" +// currently not used +class DesktopCaptureRequestsRegistry { + public: + DesktopCaptureRequestsRegistry(); + ~DesktopCaptureRequestsRegistry(); + + static DesktopCaptureRequestsRegistry* GetInstance(); + + void AddRequest(int process_id, + int request_id, + DesktopCaptureChooseDesktopMediaFunction* handler); + void RemoveRequest(int process_id, int request_id); + void CancelRequest(int process_id, int request_id); + + private: + friend struct DefaultSingletonTraits; + + struct RequestId { + RequestId(int process_id, int request_id); + + // Need to use RequestId as a key in std::map<>. + bool operator<(const RequestId& other) const; + + int process_id; + int request_id; + }; + + typedef std::map RequestsMap; + + RequestsMap requests_; + + DISALLOW_COPY_AND_ASSIGN(DesktopCaptureRequestsRegistry); +}; + +} // namespace extensions + +#endif // NWAPI_DESKTOP_CAPTURE_DESKTOP_CAPTURE_API_H_ 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 new file mode 100644 index 0000000000..51ca93028b --- /dev/null +++ b/src/api/screen/screen.cc @@ -0,0 +1,179 @@ +// Copyright (c) 2014 Jefry Tedjokusumo +// +// 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 "base/values.h" +#include "content/nw/src/api/screen/screen.h" +#include "content/nw/src/api/screen/desktop_capture_api.h" +#include "content/nw/src/api/dispatcher_host.h" +#include "content/nw/src/api/event/event.h" +#include "content/nw/src/nw_shell.h" +#include "ui/gfx/display_observer.h" +#include "ui/gfx/screen.h" + +namespace nwapi { +std::string DisplayToJSON(const gfx::Display& display) { + std::stringstream ret; + gfx::Rect rect = display.bounds(); + + ret << "{\"id\":" << display.id(); + + ret << ",\"bounds\":{\"x\":" << rect.x() + << ", \"y\":" << rect.y() + << ", \"width\":" << rect.width() + << ", \"height\":" << rect.height() << "}"; + + rect = display.work_area(); + ret << ",\"work_area\":{\"x\":" << rect.x() + << ", \"y\":" << rect.y() + << ", \"width\":" << rect.width() + << ", \"height\":" << rect.height() << "}"; + + ret << ",\"scaleFactor\":" << display.device_scale_factor(); + ret << ",\"isBuiltIn\":" << (display.IsInternal() ? "true" : "false"); + ret << ",\"rotation\":" << display.RotationAsDegree(); + ret << ",\"touchSupport\":" << display.touch_support(); + ret << "}"; + + return ret.str(); +} + +class JavaScriptDisplayObserver : BaseEvent, public gfx::DisplayObserver { + friend class EventListener; + EventListener* object_; + gfx::Screen* screen_; + + // Called when the |display|'s bound has changed. + void OnDisplayMetricsChanged(const gfx::Display& display, uint32_t changed_metrics) override { + base::ListValue arguments; + arguments.AppendString(DisplayToJSON(display)); + arguments.AppendInteger(changed_metrics); + object_->dispatcher_host()->SendEvent(object_, "displayBoundsChanged", arguments); + } + + // Called when |new_display| has been added. + void OnDisplayAdded(const gfx::Display& new_display) override { + base::ListValue arguments; + arguments.AppendString(DisplayToJSON(new_display)); + object_->dispatcher_host()->SendEvent(object_, "displayAdded", arguments); + + } + + // Called when |old_display| has been removed. + void OnDisplayRemoved(const gfx::Display& old_display) override { + base::ListValue arguments; + arguments.AppendString(DisplayToJSON(old_display)); + object_->dispatcher_host()->SendEvent(object_, "displayRemoved", arguments); + } + + static const int id; + + JavaScriptDisplayObserver(EventListener* object) : object_(object), screen_(NULL){ + } + + ~JavaScriptDisplayObserver() override { + if(screen_) + screen_->RemoveObserver(this); + } + +public: + void setScreen(gfx::Screen* screen) { + if(screen_) screen_->RemoveObserver(this); + screen_ = screen; + if(screen_) screen_->AddObserver(this); + } +}; + +const int JavaScriptDisplayObserver::id = EventListener::getUID(); + +scoped_refptr gpDCCDMF; + +void ChooseDesktopMediaCallback(EventListener* event_listener, + ExtensionFunction::ResponseType type, + const base::ListValue& results, + const std::string& error) { + + event_listener->dispatcher_host()->SendEvent(event_listener, "chooseDesktopMedia", results); + gpDCCDMF = NULL; +} + // static +void Screen::Call(DispatcherHost* dispatcher_host, + const std::string& method, + const base::ListValue& arguments, + base::ListValue* result) { + + if (method == "GetScreens") { + std::stringstream ret; + const std::vector& displays = gfx::Screen::GetNativeScreen()->GetAllDisplays(); + + if (displays.size() == 0) { + result->AppendString("{}"); + return; + } + + for (size_t i=0; iAppendString("["+ret.str()+"]"); + return; + } else if (method == "AddScreenChangeCallback") { + int object_id = 0; + arguments.GetInteger(0, &object_id); + EventListener* event_listener = dispatcher_host->GetApiObject(object_id); + JavaScriptDisplayObserver* listener = event_listener->AddListener(); + if (listener) listener->setScreen(gfx::Screen::GetNativeScreen()); + result->AppendBoolean(listener != NULL); + return; + } else if (method == "RemoveScreenChangeCallback") { + int object_id = 0; + arguments.GetInteger(0, &object_id); + EventListener* event_listener = dispatcher_host->GetApiObject(object_id); + bool res = event_listener->RemoveListener(); + result->AppendBoolean(res); + return; + } else if (method == "ChooseDesktopMedia") { + if (gpDCCDMF == NULL) { + gpDCCDMF = new DesktopCaptureChooseDesktopMediaFunction(); + gpDCCDMF->SetArgs(&arguments); + gpDCCDMF->SetRenderViewHost(dispatcher_host->render_view_host()); + + int object_id = 0; + arguments.GetInteger(0, &object_id); + EventListener* event_listener = dispatcher_host->GetApiObject(object_id); + ExtensionFunction::ResponseCallback callback = base::Bind(&ChooseDesktopMediaCallback, event_listener); + gpDCCDMF->set_response_callback(callback); + result->AppendBoolean(gpDCCDMF->RunSync()); + } else { + // 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); + } + } + +} + +} // namespace nwapi diff --git a/src/api/screen/screen.h b/src/api/screen/screen.h new file mode 100644 index 0000000000..9499a8a7f5 --- /dev/null +++ b/src/api/screen/screen.h @@ -0,0 +1,48 @@ +// Copyright (c) 2014 Jefry Tedjokusumo +// +// 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_SCREEN_SCREEN_H_ +#define CONTENT_NW_SRC_API_SCREEN_SCREEN_H_ + +#include "base/basictypes.h" + +#include + +namespace nwapi { + +class DispatcherHost; +class Screen { +public: + + static void Call(DispatcherHost* dispatcher_host, + const std::string& method, + const base::ListValue& arguments, + base::ListValue* result); + +private: + Screen(); + DISALLOW_COPY_AND_ASSIGN(Screen); +}; + +} // namespace nwapi + + + +#endif //CONTENT_NW_SRC_API_SCREEN_SCREEN_H_ diff --git a/src/api/screen/screen.js b/src/api/screen/screen.js new file mode 100644 index 0000000000..0f2c290fef --- /dev/null +++ b/src/api/screen/screen.js @@ -0,0 +1,172 @@ +// Copyright (c) 2012 Intel Corp +// Copyright (c) 2014 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. + +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); + +// Override the addListener method. +Screen.prototype.on = Screen.prototype.addListener = function(ev, callback) { + if ( ev != "displayBoundsChanged" && ev != "displayAdded" && ev != "displayRemoved" && ev != "chooseDesktopMedia") + throw new TypeError('only following event can be listened: displayBoundsChanged, displayAdded, displayRemoved'); + + var onRemoveListener = function (type, listener) { + if (this._numListener > 0) { + this._numListener--; + if (this._numListener == 0) { + process.EventEmitter.prototype.removeListener.apply(this, ["removeListener", onRemoveListener]); + nw.callStaticMethodSync('Screen', 'RemoveScreenChangeCallback', [ this.id ]); + } + } + }; + + if(this._numListener == 0) { + if (nw.callStaticMethodSync('Screen', 'AddScreenChangeCallback', [ this.id ])[0] == false ) { + throw new Error('nw.callStaticMethodSync(Screen, AddScreenChangeCallback) fails'); + return; + } + process.EventEmitter.prototype.addListener.apply(this, ["removeListener", onRemoveListener]); + } + + // Call parent. + process.EventEmitter.prototype.addListener.apply(this, arguments); + this._numListener++; +} + +// Route events. +Screen.prototype.handleEvent = function(ev) { + if (ev != "chooseDesktopMedia") + arguments[1] = JSON.parse(arguments[1]); + // Call parent. + this.emit.apply(this, arguments); +} + +Screen.prototype.__defineGetter__('screens', function() { + return JSON.parse(nw.callStaticMethodSync('Screen', 'GetScreens', [ ])); +}); + +Screen.prototype.chooseDesktopMedia = function(array, callback) { + if(nw.callStaticMethodSync('Screen', 'ChooseDesktopMedia', [ this.id, array ])[0]) { + this.once('chooseDesktopMedia', callback); + return true; + } + return false; +} + +Screen.prototype.cancelChooseDesktopMedia = function() { + return nw.callStaticMethodSync('Screen', 'CancelChooseDesktopMedia', [ this.id ])[0]; +} +// ======== Screen functions End ======== + +// Store App object in node's context. +exports.Screen = { +Init: function() { + if (screenInstance == null) { + screenInstance = new Screen(); + } + exports.Screen = screenInstance; + return screenInstance; +} +}; + diff --git a/src/api/shortcut/global_shortcut_listener_mac.h b/src/api/shortcut/global_shortcut_listener_mac.h index 2f1f336df5..8502d8b871 100644 --- a/src/api/shortcut/global_shortcut_listener_mac.h +++ b/src/api/shortcut/global_shortcut_listener_mac.h @@ -57,12 +57,12 @@ class GlobalShortcutListenerMac : public GlobalShortcutListener { bool OnMediaKeyEvent(int key_code); // GlobalShortcutListener implementation. - virtual void StartListening() OVERRIDE; - virtual void StopListening() OVERRIDE; + virtual void StartListening() override; + virtual void StopListening() override; virtual bool RegisterAcceleratorImpl( - const ui::Accelerator& accelerator) OVERRIDE; + const ui::Accelerator& accelerator) override; virtual void UnregisterAcceleratorImpl( - const ui::Accelerator& accelerator) OVERRIDE; + const ui::Accelerator& accelerator) override; // Mac-specific functions for registering hot keys with modifiers. bool RegisterHotKey(const ui::Accelerator& accelerator, KeyId hot_key_id); diff --git a/src/api/shortcut/global_shortcut_listener_win.h b/src/api/shortcut/global_shortcut_listener_win.h index 9993bf5138..563e94bf44 100644 --- a/src/api/shortcut/global_shortcut_listener_win.h +++ b/src/api/shortcut/global_shortcut_listener_win.h @@ -42,15 +42,15 @@ class GlobalShortcutListenerWin : public GlobalShortcutListener, virtual void OnWndProc(HWND hwnd, UINT message, WPARAM wparam, - LPARAM lparam) OVERRIDE; + LPARAM lparam) override; // GlobalShortcutListener implementation. - virtual void StartListening() OVERRIDE; - virtual void StopListening() OVERRIDE; + virtual void StartListening() override; + virtual void StopListening() override; virtual bool RegisterAcceleratorImpl( - const ui::Accelerator& accelerator) OVERRIDE; + const ui::Accelerator& accelerator) override; virtual void UnregisterAcceleratorImpl( - const ui::Accelerator& accelerator) OVERRIDE; + const ui::Accelerator& accelerator) override; // Whether this object is listening for global shortcuts. bool is_listening_; diff --git a/src/api/shortcut/global_shortcut_listener_x11.cc b/src/api/shortcut/global_shortcut_listener_x11.cc index 4064d389e1..11d514248e 100644 --- a/src/api/shortcut/global_shortcut_listener_x11.cc +++ b/src/api/shortcut/global_shortcut_listener_x11.cc @@ -26,10 +26,8 @@ #include "ui/gfx/x/x11_error_tracker.h" #include "ui/gfx/x/x11_types.h" -#if defined(TOOLKIT_GTK) +#if defined(OS_LINUX) #include -#else -#include "base/message_loop/message_pump_x11.h" #endif using content::BrowserThread; @@ -88,7 +86,7 @@ void GlobalShortcutListenerX11::StartListening() { DCHECK(!is_listening_); // Don't start twice. DCHECK(!registered_hot_keys_.empty()); // Also don't start if no hotkey is // registered. -#if defined(TOOLKIT_GTK) +#if defined(OS_LINUX) gdk_window_add_filter(gdk_get_default_root_window(), &GlobalShortcutListenerX11::OnXEventThunk, this); @@ -104,7 +102,7 @@ void GlobalShortcutListenerX11::StopListening() { DCHECK(registered_hot_keys_.empty()); // Make sure the set is clean before // ending. -#if defined(TOOLKIT_GTK) +#if defined(OS_LINUX) gdk_window_remove_filter(NULL, &GlobalShortcutListenerX11::OnXEventThunk, this); @@ -115,14 +113,12 @@ void GlobalShortcutListenerX11::StopListening() { is_listening_ = false; } -#if !defined(TOOLKIT_GTK) uint32_t GlobalShortcutListenerX11::Dispatch(const base::NativeEvent& event) { if (event->type == KeyPress) OnXKeyPressEvent(event); return POST_DISPATCH_NONE; } -#endif bool GlobalShortcutListenerX11::RegisterAcceleratorImpl( const ui::Accelerator& accelerator) { @@ -170,7 +166,7 @@ void GlobalShortcutListenerX11::UnregisterAcceleratorImpl( registered_hot_keys_.erase(accelerator); } -#if defined(TOOLKIT_GTK) +#if defined(OS_LINUX) GdkFilterReturn GlobalShortcutListenerX11::OnXEvent(GdkXEvent* gdk_x_event, GdkEvent* gdk_event) { XEvent* x_event = static_cast(gdk_x_event); diff --git a/src/api/shortcut/global_shortcut_listener_x11.h b/src/api/shortcut/global_shortcut_listener_x11.h index 72d16bc517..633bb716fa 100644 --- a/src/api/shortcut/global_shortcut_listener_x11.h +++ b/src/api/shortcut/global_shortcut_listener_x11.h @@ -28,9 +28,9 @@ #include "base/message_loop/message_pump_dispatcher.h" #include "content/nw/src/api/shortcut/global_shortcut_listener.h" -#if defined(TOOLKIT_GTK) +#if defined(OS_LINUX) #include -#include "ui/base/gtk/gtk_signal.h" +#include "chrome/browser/ui/libgtk2ui/gtk2_signal.h" #endif // defined(TOOLKIT_GTK) namespace nwapi { @@ -46,23 +46,21 @@ class GlobalShortcutListenerX11 public GlobalShortcutListener { public: GlobalShortcutListenerX11(); - virtual ~GlobalShortcutListenerX11(); + ~GlobalShortcutListenerX11() final; -#if !defined(TOOLKIT_GTK) // base::MessagePumpDispatcher implementation. - virtual uint32_t Dispatch(const base::NativeEvent& event) OVERRIDE; -#endif + uint32_t Dispatch(const base::NativeEvent& event) override; private: // GlobalShortcutListener implementation. - virtual void StartListening() OVERRIDE; - virtual void StopListening() OVERRIDE; - virtual bool RegisterAcceleratorImpl( - const ui::Accelerator& accelerator) OVERRIDE; - virtual void UnregisterAcceleratorImpl( - const ui::Accelerator& accelerator) OVERRIDE; - -#if defined(TOOLKIT_GTK) + void StartListening() override; + void StopListening() override; + bool RegisterAcceleratorImpl( + const ui::Accelerator& accelerator) override; + void UnregisterAcceleratorImpl( + const ui::Accelerator& accelerator) override; + +#if defined(OS_LINUX) // Callback for XEvents of the default root window. CHROMEG_CALLBACK_1(GlobalShortcutListenerX11, GdkFilterReturn, OnXEvent, GdkXEvent*, GdkEvent*); diff --git a/src/api/shortcut/shorcut.js b/src/api/shortcut/shorcut.js index 653842208b..f51ec49a8f 100644 --- a/src/api/shortcut/shorcut.js +++ b/src/api/shortcut/shorcut.js @@ -22,7 +22,7 @@ var v8_util = process.binding('v8_util'); function Shortcut(option) { if (typeof option != 'object') - throw new String('Invalid option.'); + throw new TypeError('Invalid option.'); if (!option.hasOwnProperty('key')) throw new TypeError("Shortcut requires 'key' to specify key combinations."); diff --git a/src/api/shortcut/shortcut.cc b/src/api/shortcut/shortcut.cc index dc5b0b2229..16dea75e38 100644 --- a/src/api/shortcut/shortcut.cc +++ b/src/api/shortcut/shortcut.cc @@ -34,7 +34,7 @@ namespace nwapi { ui::Accelerator Parse(const std::string& shortcut) { // Convert to lower case, see // https://github.com/rogerwang/node-webkit/pull/1735. - std::string lower_shortcut = StringToLowerASCII(shortcut); + std::string lower_shortcut = base::StringToLowerASCII(shortcut); std::vector tokens; base::SplitString(lower_shortcut, '+', &tokens); @@ -68,6 +68,30 @@ ui::Accelerator Parse(const std::string& shortcut) { tokens[i] == kKeyPgUp || tokens[i] == kKeyPgDwn || tokens[i] == kKeyTab || + tokens[i] == kKeyF1 || + tokens[i] == kKeyF2 || + tokens[i] == kKeyF3 || + tokens[i] == kKeyF4 || + tokens[i] == kKeyF5 || + tokens[i] == kKeyF6 || + tokens[i] == kKeyF7 || + tokens[i] == kKeyF8 || + tokens[i] == kKeyF9 || + tokens[i] == kKeyF10 || + tokens[i] == kKeyF11 || + tokens[i] == kKeyF12 || + tokens[i] == kKeyF13 || + tokens[i] == kKeyF14 || + tokens[i] == kKeyF15 || + tokens[i] == kKeyF16 || + tokens[i] == kKeyF17 || + tokens[i] == kKeyF18 || + tokens[i] == kKeyF19 || + tokens[i] == kKeyF20 || + tokens[i] == kKeyF21 || + tokens[i] == kKeyF22 || + tokens[i] == kKeyF23 || + tokens[i] == kKeyF24 || tokens[i] == kKeyMediaNextTrack || tokens[i] == kKeyMediaPlayPause || tokens[i] == kKeyMediaPrevTrack || @@ -104,6 +128,54 @@ ui::Accelerator Parse(const std::string& shortcut) { key = ui::VKEY_NEXT; } else if (tokens[i] == kKeyTab) { key = ui::VKEY_TAB; + } else if (tokens[i] == kKeyF1) { + key = ui::VKEY_F1; + } else if (tokens[i] == kKeyF2) { + key = ui::VKEY_F2; + } else if (tokens[i] == kKeyF3) { + key = ui::VKEY_F3; + } else if (tokens[i] == kKeyF4) { + key = ui::VKEY_F4; + } else if (tokens[i] == kKeyF5) { + key = ui::VKEY_F5; + } else if (tokens[i] == kKeyF6) { + key = ui::VKEY_F6; + } else if (tokens[i] == kKeyF7) { + key = ui::VKEY_F7; + } else if (tokens[i] == kKeyF8) { + key = ui::VKEY_F8; + } else if (tokens[i] == kKeyF9) { + key = ui::VKEY_F9; + } else if (tokens[i] == kKeyF10) { + key = ui::VKEY_F10; + } else if (tokens[i] == kKeyF11) { + key = ui::VKEY_F11; + } else if (tokens[i] == kKeyF12) { + key = ui::VKEY_F12; + } else if (tokens[i] == kKeyF13) { + key = ui::VKEY_F13; + } else if (tokens[i] == kKeyF14) { + key = ui::VKEY_F14; + } else if (tokens[i] == kKeyF15) { + key = ui::VKEY_F15; + } else if (tokens[i] == kKeyF16) { + key = ui::VKEY_F16; + } else if (tokens[i] == kKeyF17) { + key = ui::VKEY_F17; + } else if (tokens[i] == kKeyF18) { + key = ui::VKEY_F18; + } else if (tokens[i] == kKeyF19) { + key = ui::VKEY_F19; + } else if (tokens[i] == kKeyF20) { + key = ui::VKEY_F20; + } else if (tokens[i] == kKeyF21) { + key = ui::VKEY_F21; + } else if (tokens[i] == kKeyF22) { + key = ui::VKEY_F22; + } else if (tokens[i] == kKeyF23) { + key = ui::VKEY_F23; + } else if (tokens[i] == kKeyF24) { + key = ui::VKEY_F24; } else if (tokens[i] == kKeyMediaNextTrack) { key = ui::VKEY_MEDIA_NEXT_TRACK; } else if (tokens[i] == kKeyMediaPlayPause) { diff --git a/src/api/shortcut/shortcut.h b/src/api/shortcut/shortcut.h index 9bff68602f..60dc428aa6 100644 --- a/src/api/shortcut/shortcut.h +++ b/src/api/shortcut/shortcut.h @@ -34,7 +34,7 @@ class Shortcut : public Base, public GlobalShortcutListener::Observer { Shortcut(int id, const base::WeakPtr& dispatcher_host, const base::DictionaryValue& option); - virtual ~Shortcut(); + ~Shortcut() override; const ui::Accelerator& GetAccelerator() const { return accelerator_; @@ -44,7 +44,7 @@ class Shortcut : public Base, public GlobalShortcutListener::Observer { void OnFailed(const std::string failed_msg); // GlobalShortcutListener::Observer implementation. - virtual void OnKeyPressed(const ui::Accelerator& accelerator); + void OnKeyPressed(const ui::Accelerator& accelerator) override; private: ui::Accelerator accelerator_; diff --git a/src/api/shortcut/shortcut_constants.cc b/src/api/shortcut/shortcut_constants.cc index 6d404f9eea..0759889822 100644 --- a/src/api/shortcut/shortcut_constants.cc +++ b/src/api/shortcut/shortcut_constants.cc @@ -44,5 +44,29 @@ const char kKeySeparator[] = "+"; const char kKeyShift[] = "shift"; const char kKeyTab[] = "tab"; const char kKeyUp[] = "up"; +const char kKeyF1[] = "f1"; +const char kKeyF2[] = "f2"; +const char kKeyF3[] = "f3"; +const char kKeyF4[] = "f4"; +const char kKeyF5[] = "f5"; +const char kKeyF6[] = "f6"; +const char kKeyF7[] = "f7"; +const char kKeyF8[] = "f8"; +const char kKeyF9[] = "f9"; +const char kKeyF10[] = "f10"; +const char kKeyF11[] = "f11"; +const char kKeyF12[] = "f12"; +const char kKeyF13[] = "f13"; +const char kKeyF14[] = "f14"; +const char kKeyF15[] = "f15"; +const char kKeyF16[] = "f16"; +const char kKeyF17[] = "f17"; +const char kKeyF18[] = "f18"; +const char kKeyF19[] = "f19"; +const char kKeyF20[] = "f20"; +const char kKeyF21[] = "f21"; +const char kKeyF22[] = "f22"; +const char kKeyF23[] = "f23"; +const char kKeyF24[] = "f24"; } // namespace nwapi diff --git a/src/api/shortcut/shortcut_constants.h b/src/api/shortcut/shortcut_constants.h index f0f0b88021..db6f0f6ebb 100644 --- a/src/api/shortcut/shortcut_constants.h +++ b/src/api/shortcut/shortcut_constants.h @@ -45,6 +45,31 @@ extern const char kKeySeparator[]; extern const char kKeyShift[]; extern const char kKeyTab[]; extern const char kKeyUp[]; +extern const char kKeyF1[]; +extern const char kKeyF2[]; +extern const char kKeyF3[]; +extern const char kKeyF4[]; +extern const char kKeyF5[]; +extern const char kKeyF6[]; +extern const char kKeyF7[]; +extern const char kKeyF8[]; +extern const char kKeyF9[]; +extern const char kKeyF10[]; +extern const char kKeyF11[]; +extern const char kKeyF12[]; +extern const char kKeyF13[]; +extern const char kKeyF14[]; +extern const char kKeyF15[]; +extern const char kKeyF16[]; +extern const char kKeyF17[]; +extern const char kKeyF18[]; +extern const char kKeyF19[]; +extern const char kKeyF20[]; +extern const char kKeyF21[]; +extern const char kKeyF22[]; +extern const char kKeyF23[]; +extern const char kKeyF24[]; + } // namespace nwapi diff --git a/src/api/tray/tray.cc b/src/api/tray/tray.cc index 2371b10cc6..0259984c6b 100644 --- a/src/api/tray/tray.cc +++ b/src/api/tray/tray.cc @@ -37,6 +37,10 @@ Tray::Tray(int id, if (option.GetString("title", &title)) SetTitle(title); + bool areTemplates; + if (option.GetBoolean("iconsAreTemplates", &areTemplates)) + SetIconsAreTemplates(areTemplates); + std::string icon; if (option.GetString("icon", &icon) && !icon.empty()) SetIcon(icon); @@ -47,7 +51,7 @@ Tray::Tray(int id, std::string tooltip; if (option.GetString("tooltip", &tooltip)) - SetTitle(tooltip); + SetTooltip(tooltip); int menu_id; if (option.GetInteger("menu", &menu_id)) @@ -74,6 +78,10 @@ void Tray::Call(const std::string& method, std::string alticon; arguments.GetString(0, &alticon); SetAltIcon(alticon); + } else if (method == "SetIconsAreTemplates") { + bool areTemplates; + arguments.GetBoolean(0, &areTemplates); + SetIconsAreTemplates(areTemplates); } else if (method == "SetTooltip") { std::string tooltip; arguments.GetString(0, &tooltip); diff --git a/src/api/tray/tray.h b/src/api/tray/tray.h index a0303edb14..095d9e7d34 100644 --- a/src/api/tray/tray.h +++ b/src/api/tray/tray.h @@ -34,10 +34,10 @@ class NSStatusItem; class MacTrayObserver; #endif // __OBJC__ -#elif defined(TOOLKIT_GTK) +#elif 0 #include -#include "ui/base/gtk/gtk_signal.h" -#elif defined(OS_WIN) +#include "chrome/browser/ui/libgtk2ui/gtk2_signal.h" +#elif defined(OS_WIN) || defined(OS_LINUX) class StatusIcon; class StatusTray; #endif // defined(OS_MACOSX) @@ -52,10 +52,10 @@ class Tray : public Base { Tray(int id, const base::WeakPtr& dispatcher_host, const base::DictionaryValue& option); - virtual ~Tray(); + ~Tray() override; - virtual void Call(const std::string& method, - const base::ListValue& arguments) OVERRIDE; + void Call(const std::string& method, + const base::ListValue& arguments) override; private: // Platform-independent implementations @@ -64,16 +64,19 @@ class Tray : public Base { void Destroy(); void SetTitle(const std::string& title); void SetIcon(const std::string& icon_path); - void SetTooltip(const std::string& title); + void SetTooltip(const std::string& tooltip); void SetMenu(Menu* menu); void Remove(); // Alternate icons only work with Macs void SetAltIcon(const std::string& alticon_path); + // Template icons only work with Macs + void SetIconsAreTemplates(bool areTemplates); #if defined(OS_MACOSX) __block NSStatusItem* status_item_; MacTrayObserver* status_observer_; -#elif defined(TOOLKIT_GTK) + bool iconsAreTemplates; +#elif 0 GtkStatusIcon* status_item_; // Reference to the associated menu. @@ -83,7 +86,7 @@ class Tray : public Base { CHROMEGTK_CALLBACK_0(Tray, void, OnClick); // Callback invoked when user right-clicks on the status icon. CHROMEGTK_CALLBACK_2(Tray, void, OnPopupMenu, guint, guint); -#elif defined(OS_WIN) +#elif defined(OS_WIN) || defined(OS_LINUX) // The global presentation of system tray. static StatusTray* status_tray_; diff --git a/src/api/tray/tray.js b/src/api/tray/tray.js index 4f55cbfd94..2721ede410 100644 --- a/src/api/tray/tray.js +++ b/src/api/tray/tray.js @@ -22,10 +22,10 @@ var v8_util = process.binding('v8_util'); function Tray(option) { if (typeof option != 'object') - throw new String('Invalid option'); + throw new TypeError('Invalid option'); if (!option.hasOwnProperty('title') && !option.hasOwnProperty('icon')) - throw new String("Must set 'title' or 'icon' field in option"); + throw new TypeError("Must set 'title' or 'icon' field in option"); if (!option.hasOwnProperty('title')) option.title = ''; @@ -42,12 +42,17 @@ function Tray(option) { option.alticon = nw.getAbsolutePath(option.alticon); } + if (option.hasOwnProperty('iconsAreTemplates')) + option.iconsAreTemplates = Boolean(option.iconsAreTemplates); + else + option.iconsAreTemplates = true; + if (option.hasOwnProperty('tooltip')) option.tooltip = String(option.tooltip); if (option.hasOwnProperty('click')) { if (typeof option.click != 'function') { - throw new String("'click' must be a valid Function"); + throw new TypeError("'click' must be a valid Function"); } else { this.click = option.click; } @@ -55,7 +60,7 @@ function Tray(option) { if (option.hasOwnProperty('menu')) { if (v8_util.getConstructorName(option.menu) != 'Menu') - throw new String("'menu' must be a valid Menu"); + throw new TypeError("'menu' must be a valid Menu"); // Transfer only object id v8_util.setHiddenValue(this, 'menu', option.menu); @@ -100,7 +105,15 @@ Tray.prototype.__defineSetter__('icon', function(val) { Tray.prototype.__defineSetter__('alticon', function(val) { v8_util.getHiddenValue(this, 'option').shadowAlticon = String(val); var real_path = val == '' ? '' : nw.getAbsolutePath(val); - this.handleSetter('alticon', 'SetAlticon', String, real_path); + this.handleSetter('alticon', 'SetAltIcon', String, real_path); +}); + +Tray.prototype.__defineGetter__('iconsAreTemplates', function() { + return this.handleGetter('iconsAreTemplates'); +}); + +Tray.prototype.__defineSetter__('iconsAreTemplates', function(val) { + this.handleSetter('iconsAreTemplates', 'SetIconsAreTemplates', Boolean, val); }); Tray.prototype.__defineGetter__('tooltip', function() { @@ -117,7 +130,7 @@ Tray.prototype.__defineGetter__('menu', function() { Tray.prototype.__defineSetter__('menu', function(val) { if (v8_util.getConstructorName(val) != 'Menu') - throw new String("'menu' property requries a valid Menu"); + throw new TypeError("'menu' property requries a valid Menu"); v8_util.setHiddenValue(this, 'menu', val); nw.callObjectMethod(this, 'SetMenu', [ val.id ]); diff --git a/src/api/tray/tray_win.cc b/src/api/tray/tray_aura.cc similarity index 87% rename from src/api/tray/tray_win.cc rename to src/api/tray/tray_aura.cc index a90e65bb83..7bd9623fe2 100644 --- a/src/api/tray/tray_win.cc +++ b/src/api/tray/tray_aura.cc @@ -30,6 +30,7 @@ #include "content/nw/src/api/menu/menu.h" #include "content/nw/src/nw_package.h" #include "content/nw/src/nw_shell.h" +#include "ui/gfx/screen.h" #include "ui/gfx/image/image.h" namespace nwapi { @@ -42,11 +43,17 @@ class TrayObserver : public StatusIconObserver { : tray_(tray) { } - virtual ~TrayObserver() { + ~TrayObserver() final { } - virtual void OnStatusIconClicked() OVERRIDE { + void OnStatusIconClicked() override { base::ListValue args; + base::DictionaryValue* data = new base::DictionaryValue; + gfx::Point cursor_pos( + gfx::Screen::GetNativeScreen()->GetCursorScreenPoint()); + data->SetInteger("x", cursor_pos.x()); + data->SetInteger("y", cursor_pos.y()); + args.Append(data); tray_->dispatcher_host()->SendEvent(tray_, "click", args); } @@ -56,7 +63,7 @@ class TrayObserver : public StatusIconObserver { void Tray::Create(const base::DictionaryValue& option) { if (!status_tray_) - status_tray_ = StatusTray::Create(); + status_tray_ = StatusTray::GetSingleton(); status_icon_ = status_tray_->CreateStatusIcon(StatusTray::NOTIFICATION_TRAY_ICON, gfx::ImageSkia(), base::string16()); @@ -107,4 +114,7 @@ void Tray::Remove() { void Tray::SetAltIcon(const std::string& alticon_path) { } +void Tray::SetIconsAreTemplates(bool areTemplates) { +} + } // namespace nwapi diff --git a/src/api/tray/tray_gtk.cc b/src/api/tray/tray_gtk.cc index cc86c9f86b..12b5990ef2 100644 --- a/src/api/tray/tray_gtk.cc +++ b/src/api/tray/tray_gtk.cc @@ -71,14 +71,19 @@ void Tray::OnClick(GtkWidget* widget) { } void Tray::OnPopupMenu(GtkWidget* widget, guint button, guint time) { +#if 0//FIXME if (menu_) { gtk_menu_popup(GTK_MENU(menu_->menu_), NULL, NULL, gtk_status_icon_position_menu, status_item_, button, time); } +#endif } void Tray::SetAltIcon(const std::string& alticon_path) { } +void Tray::SetIconsAreTemplates(bool areTemplates) { +} + } // namespace nwapi diff --git a/src/api/tray/tray_mac.mm b/src/api/tray/tray_mac.mm index c983063883..8c8575306d 100644 --- a/src/api/tray/tray_mac.mm +++ b/src/api/tray/tray_mac.mm @@ -22,6 +22,7 @@ #include "base/values.h" #import +#include "ui/gfx/screen.h" #include "content/nw/src/api/dispatcher_host.h" #include "content/nw/src/api/menu/menu.h" @@ -40,6 +41,15 @@ - (void)setBacking:(nwapi::Tray*)newTray { } - (void)onClick:(id)sender { base::ListValue args; + base::DictionaryValue* data = new base::DictionaryValue; + // Get the position of the frame of the NSStatusItem + NSPoint pos = ([[[NSApp currentEvent] window] frame]).origin; + // Flip coordinates to gfx (0,0 in top-left corner) using primary screen. + NSScreen* screen = [[NSScreen screens] objectAtIndex:0]; + pos.y = NSMaxY([screen frame]) - pos.y; + data->SetInteger("x", pos.x); + data->SetInteger("y", pos.y); + args.Append(data); tray_->dispatcher_host()->SendEvent(tray_,"click",args); } @end @@ -65,6 +75,11 @@ - (void)onClick:(id)sender { } void Tray::SetTitle(const std::string& title) { + // note: this is kind of mad but the first time we set the title property + // we have to call setTitle twice or it won't get the right dimensions + if ([status_item_ title] != nil) { + [status_item_ setTitle:[NSString stringWithUTF8String:title.c_str()]]; + } [status_item_ setTitle:[NSString stringWithUTF8String:title.c_str()]]; } @@ -72,6 +87,7 @@ - (void)onClick:(id)sender { if (!icon.empty()) { NSImage* image = [[NSImage alloc] initWithContentsOfFile:[NSString stringWithUTF8String:icon.c_str()]]; + [image setTemplate:iconsAreTemplates]; [status_item_ setImage:image]; [image release]; } else { @@ -83,6 +99,7 @@ - (void)onClick:(id)sender { if (!alticon.empty()) { NSImage* image = [[NSImage alloc] initWithContentsOfFile:[NSString stringWithUTF8String:alticon.c_str()]]; + [image setTemplate:iconsAreTemplates]; [status_item_ setAlternateImage:image]; [image release]; } else { @@ -90,6 +107,16 @@ - (void)onClick:(id)sender { } } +void Tray::SetIconsAreTemplates(bool areTemplates) { + iconsAreTemplates = areTemplates; + if ([status_item_ image] != nil) { + [[status_item_ image] setTemplate:areTemplates]; + } + if ([status_item_ alternateImage] != nil) { + [[status_item_ alternateImage] setTemplate:areTemplates]; + } +} + void Tray::SetTooltip(const std::string& tooltip) { [status_item_ setToolTip:[NSString stringWithUTF8String:tooltip.c_str()]]; } diff --git a/src/api/v8_internal_helper.cc b/src/api/v8_internal_helper.cc new file mode 100644 index 0000000000..5d2961e47a --- /dev/null +++ b/src/api/v8_internal_helper.cc @@ -0,0 +1,13 @@ +#include "v8/src/v8.h" + +using namespace v8; + + +void FixSourceNWBin(Isolate* v8_isolate, Handle script) { + i::Isolate* isolate = reinterpret_cast(v8_isolate); + i::Handle obj = + i::Handle::cast(v8::Utils::OpenHandle(*script)); + i::Handle + function_info(i::SharedFunctionInfo::cast(*obj), obj->GetIsolate()); + reinterpret_cast(function_info->script())->set_source(isolate->heap()->undefined_value()); +} diff --git a/src/api/window/window.cc b/src/api/window/window.cc index 3d475b269f..cb6f3d9fd1 100644 --- a/src/api/window/window.cc +++ b/src/api/window/window.cc @@ -47,10 +47,10 @@ namespace { const char kCauseKey[] = "cause"; const char kCookieKey[] = "cookie"; -const char kDomainKey[] = "domain"; -const char kIdKey[] = "id"; +//const char kDomainKey[] = "domain"; +//const char kIdKey[] = "id"; const char kRemovedKey[] = "removed"; -const char kTabIdsKey[] = "tabIds"; +//const char kTabIdsKey[] = "tabIds"; // Cause Constants const char kEvictedChangeCause[] = "evicted"; @@ -65,7 +65,7 @@ GURL GetURLFromCanonicalCookie(const net::CanonicalCookie& cookie) { cookie.IsSecure() ? "https" : "http"; const std::string host = domain_key.find('.') != 0 ? domain_key : domain_key.substr(1); - return GURL(scheme + content::kStandardSchemeSeparator + host + "/"); + return GURL(scheme + url::kStandardSchemeSeparator + host + "/"); } void GetCookieListFromStore( @@ -145,7 +145,7 @@ PopulateCookieObject(const net::CanonicalCookie& canonical_cookie) { result->SetBoolean("host_only", net::cookie_util::DomainIsHostOnly( canonical_cookie.Domain())); // A non-UTF8 path is invalid, so we just replace it with an empty string. - result->SetString("path", IsStringUTF8(canonical_cookie.Path()) ? canonical_cookie.Path() + result->SetString("path", base::IsStringUTF8(canonical_cookie.Path()) ? canonical_cookie.Path() : std::string()); result->SetBoolean("secure", canonical_cookie.IsSecure()); result->SetBoolean("http_only", canonical_cookie.IsHttpOnly()); @@ -161,6 +161,8 @@ PopulateCookieObject(const net::CanonicalCookie& canonical_cookie) { namespace nwapi { +CookieAPIContext::~CookieAPIContext() {} + Window::Window(int id, const base::WeakPtr& dispatcher_host, const base::DictionaryValue& option) @@ -221,7 +223,12 @@ void Window::Call(const std::string& method, shell_->window()->SetKiosk(!shell_->window()->IsKiosk()); } else if (method == "CloseDevTools") { shell_->CloseDevTools(); - }else if (method == "ResizeTo") { + } else if (method == "SetPosition") { + std::string position; + if (arguments.GetString(0, &position)){ + shell_->window()->SetPosition(position); + } + } else if (method == "ResizeTo") { int width, height; if (arguments.GetInteger(0, &width) && arguments.GetInteger(1, &height)) @@ -248,19 +255,31 @@ void Window::Call(const std::string& method, bool show; if (arguments.GetBoolean(0, &show)) shell_->window()->SetShowInTaskbar(show); + } else if (method == "SetVisibleOnAllWorkspaces") { + bool all_workspaces; + if (arguments.GetBoolean(0, &all_workspaces)) + shell_->window()->SetVisibleOnAllWorkspaces(all_workspaces); } else if (method == "MoveTo") { int x, y; if (arguments.GetInteger(0, &x) && arguments.GetInteger(1, &y)) shell_->window()->SetPosition(gfx::Point(x, y)); } else if (method == "RequestAttention") { - bool flash; - if (arguments.GetBoolean(0, &flash)) - shell_->window()->FlashFrame(flash); + int count; + if (arguments.GetInteger(0, &count)) + shell_->window()->FlashFrame(count); } else if (method == "SetBadgeLabel") { std::string label; if (arguments.GetString(0, &label)) shell_->window()->SetBadgeLabel(label); + } else if (method == "SetTransparent") { + bool transparent; + if (arguments.GetBoolean(0, &transparent)) + shell_->window()->SetTransparent(transparent); + } else if (method == "SetProgressBar") { + double progress; + if (arguments.GetDouble(0, &progress)) + shell_->window()->SetProgressBar(progress); } else if (method == "SetMenu") { int id; if (arguments.GetInteger(0, &id)) @@ -304,6 +323,9 @@ void Window::CallSync(const std::string& method, gfx::Point position = shell_->window()->GetPosition(); result->AppendInteger(position.x()); result->AppendInteger(position.y()); + } else if (method == "IsTransparent") { + bool transparent = shell_->window()->IsTransparent(); + result->AppendBoolean(transparent); } else if (method == "IsDevToolsOpen") { result->AppendBoolean(shell_->devToolsOpen()); } else if (method == "ShowDevTools") { diff --git a/src/api/window/window.h b/src/api/window/window.h index fa9295e834..3e5e9f8c25 100644 --- a/src/api/window/window.h +++ b/src/api/window/window.h @@ -51,6 +51,9 @@ class CookieAPIContext : public base::RefCountedThreadSafe { GURL url_; int req_id_; bool success_; +private: + friend class base::RefCountedThreadSafe; + ~CookieAPIContext(); }; @@ -59,13 +62,13 @@ class Window : public Base, public content::NotificationObserver { Window(int id, const base::WeakPtr& dispatcher_host, const base::DictionaryValue& option); - virtual ~Window(); + ~Window() override; - virtual void Call(const std::string& method, - const base::ListValue& arguments) OVERRIDE; - virtual void CallSync(const std::string& method, + void Call(const std::string& method, + const base::ListValue& arguments) override; + void CallSync(const std::string& method, const base::ListValue& arguments, - base::ListValue* result) OVERRIDE; + base::ListValue* result) override; void CookieGet(const base::ListValue& arguments, bool get_all = false); void GetCookieOnIOThread(CookieAPIContext*); @@ -84,9 +87,9 @@ class Window : public Base, public content::NotificationObserver { private: // content::NotificationObserver implementation. - virtual void Observe(int type, + void Observe(int type, const content::NotificationSource& source, - const content::NotificationDetails& details) OVERRIDE; + const content::NotificationDetails& details) override; // Handler for the COOKIE_CHANGED event. The method takes the details of such // an event and constructs a suitable JSON formatted extension event from it. diff --git a/src/api/window/window.js b/src/api/window/window.js index 73d050a316..00bacf3034 100644 --- a/src/api/window/window.js +++ b/src/api/window/window.js @@ -18,6 +18,9 @@ // 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. +// OS X and Linux only, Windows does not have a concept of workspaces +var canSetVisibleOnAllWorkspaces = /(darwin|linux)/.test(require('os').platform()); + exports.Window = { get: function(other) { // Return other window. @@ -48,7 +51,7 @@ exports.Window = { // Conver relative url to full url. var protocol = url.match(/^[a-z]+:\/\//i); if (protocol == null || protocol.length == 0) { - var href = window.location.href; + var href = window.location.href.split(/\?|#/)[0]; url = href.substring(0, href.lastIndexOf('/') + 1) + url; } @@ -66,5 +69,8 @@ exports.Window = { var routing_id = nw.createShell(url, options); return new global.Window(routing_id, true, id); + }, + canSetVisibleOnAllWorkspaces: function() { + return canSetVisibleOnAllWorkspaces; } }; diff --git a/src/api/window_bindings.cc b/src/api/window_bindings.cc index d6436623f1..00625340ef 100644 --- a/src/api/window_bindings.cc +++ b/src/api/window_bindings.cc @@ -18,15 +18,18 @@ // 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/window_bindings.h" #include "base/values.h" #include "content/child/child_thread.h" #include "content/nw/src/api/bindings_common.h" +#include "content/nw/src/api/dispatcher.h" #include "content/renderer/render_view_impl.h" #include "grit/nw_resources.h" + #undef LOG -using namespace WebCore; +using namespace blink; #if defined(OS_WIN) #define _USE_MATH_DEFINES #include @@ -36,16 +39,29 @@ using namespace WebCore; #include "third_party/WebKit/Source/config.h" #include "third_party/WebKit/Source/core/html/HTMLIFrameElement.h" +#include "third_party/WebKit/Source/core/dom/Document.h" +#include "third_party/WebKit/Source/core/frame/LocalFrame.h" #include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebView.h" -#include "third_party/WebKit/Source/web/WebFrameImpl.h" +#include "third_party/WebKit/Source/web/WebLocalFrameImpl.h" #include "third_party/WebKit/public/web/WebScriptSource.h" +#undef BLINK_IMPLEMENTATION +#define BLINK_IMPLEMENTATION 1 +#include "third_party/WebKit/public/web/WebDocument.h" +#include "third_party/WebKit/Source/platform/heap/Handle.h" +//#include "third_party/WebKit/Source/core/inspector/InspectorInstrumentation.h" +//#include "third_party/WebKit/Source/core/inspector/InspectorResourceAgent.h" + #undef CHECK #include "V8HTMLIFrameElement.h" +extern void FixSourceNWBin(v8::Isolate* v8_isolate, v8::Handle script); + using blink::WebScriptSource; using blink::WebFrame; +//using blink::InstrumentingAgents; +//using blink::InspectorResourceAgent; namespace nwapi { @@ -104,6 +120,8 @@ WindowBindings::AllocateId(const v8::FunctionCallbackInfo& args) { void WindowBindings::CallObjectMethod(const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::EscapableHandleScope scope(isolate); + v8::Local self = args[0]->ToObject(); int routing_id = self->Get(v8::String::NewFromUtf8(isolate, "routing_id"))->Int32Value(); int object_id = self->Get(v8::String::NewFromUtf8(isolate, "id"))->Int32Value(); @@ -127,8 +145,8 @@ WindowBindings::CallObjectMethod(const v8::FunctionCallbackInfo& args if (frm->IsNull()) { web_frame = main_frame; }else{ - WebCore::HTMLIFrameElement* iframe = WebCore::V8HTMLIFrameElement::toNative(frm); - web_frame = blink::WebFrameImpl::fromFrame(iframe->contentFrame()); + blink::HTMLIFrameElement* iframe = blink::V8HTMLIFrameElement::toImpl(frm); + web_frame = blink::WebFrame::fromFrame(iframe->contentFrame()); } #if defined(OS_WIN) base::string16 jscript((WCHAR*)*v8::String::Value(args[3])); @@ -140,16 +158,70 @@ WindowBindings::CallObjectMethod(const v8::FunctionCallbackInfo& args } args.GetReturnValue().Set(result); return; + } else if (method == "EvaluateNWBin") { +#if defined(OS_WIN) + base::FilePath path((WCHAR*)*v8::String::Value(args[3])); +#else + base::FilePath path(*v8::String::Utf8Value(args[3])); +#endif + base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ); + if (file.IsValid()) { + int64 length = file.GetLength(); + if (length > 0 && length < INT_MAX) { + int size = static_cast(length); + std::vector raw_data; + raw_data.resize(size); + uint8_t* data = reinterpret_cast(&(raw_data.front())); + if (file.ReadAtCurrentPos((char*)data, size) == length) { + v8::Handle source_string = v8::String::NewFromUtf8(isolate, ""); + v8::ScriptCompiler::CachedData* cache; + cache = new v8::ScriptCompiler::CachedData( + data, length, v8::ScriptCompiler::CachedData::BufferNotOwned); + v8::ScriptCompiler::Source source(source_string, cache); + v8::Local script; + script = v8::ScriptCompiler::CompileUnbound( + isolate, &source, v8::ScriptCompiler::kConsumeCodeCache); + ASSERT(!cache->rejected); + v8::Handle result; + v8::Handle frm = v8::Handle::Cast(args[2]); + WebFrame* web_frame = NULL; + if (frm->IsNull()) { + web_frame = main_frame; + }else{ + blink::HTMLIFrameElement* iframe = blink::V8HTMLIFrameElement::toImpl(frm); + web_frame = blink::WebFrame::fromFrame(iframe->contentFrame()); + } + v8::Context::Scope cscope (web_frame->mainWorldScriptContext()); + FixSourceNWBin(isolate, script); + result = script->BindToCurrentContext()->Run(); + args.GetReturnValue().Set(result); + } + } + } + return; } else if (method == "setDevToolsJail") { v8::Handle frm = v8::Handle::Cast(args[2]); if (frm->IsNull()) { main_frame->setDevtoolsJail(NULL); }else{ - WebCore::HTMLIFrameElement* iframe = WebCore::V8HTMLIFrameElement::toNative(frm); - main_frame->setDevtoolsJail(blink::WebFrameImpl::fromFrame(iframe->contentFrame())); + blink::HTMLIFrameElement* iframe = blink::V8HTMLIFrameElement::toImpl(frm); + main_frame->setDevtoolsJail(blink::WebFrame::fromFrame(iframe->contentFrame())); } args.GetReturnValue().Set(v8::Undefined(isolate)); return; + } else if (method == "setCacheDisabled") { +#if 0 //FIXME + RefPtrWillBePersistent document = static_cast >(main_frame->document()); + InstrumentingAgents* instrumentingAgents = instrumentationForPage(document->page()); + if (instrumentingAgents) { + bool disable = args[2]->ToBoolean()->Value(); + InspectorResourceAgent* resAgent = instrumentingAgents->inspectorResourceAgent(); + resAgent->setCacheDisabled(NULL, disable); + args.GetReturnValue().Set(true); + } else + args.GetReturnValue().Set(false); + return; +#endif } args.GetReturnValue().Set(remote::CallObjectMethod(render_view->GetRoutingID(), @@ -184,7 +256,8 @@ WindowBindings::CallObjectMethodSync(const v8::FunctionCallbackInfo& return; }else if (method == "SetZoomLevel") { double zoom_level = args[2]->ToNumber()->Value(); - render_view->OnSetZoomLevel(zoom_level); + render_view->GetWebView()->setZoomLevel(zoom_level); + nwapi::Dispatcher::ZoomLevelChanged(render_view->GetWebView()); args.GetReturnValue().Set(v8::Undefined(isolate)); return; } diff --git a/src/api/window_bindings.h b/src/api/window_bindings.h index 4e2fddca32..4bb99d6d87 100644 --- a/src/api/window_bindings.h +++ b/src/api/window_bindings.h @@ -30,13 +30,13 @@ namespace nwapi { class WindowBindings : public v8::Extension { public: WindowBindings(); - virtual ~WindowBindings(); + ~WindowBindings() override; // v8::Extension implementation. - virtual v8::Handle + v8::Handle GetNativeFunctionTemplate( v8::Isolate* isolate, - v8::Handle name) OVERRIDE; + v8::Handle name) override; private: static void AllocateId(const v8::FunctionCallbackInfo& args); diff --git a/src/api/window_bindings.js b/src/api/window_bindings.js index 7d54bf2f10..c5e987b523 100644 --- a/src/api/window_bindings.js +++ b/src/api/window_bindings.js @@ -146,6 +146,7 @@ Window.prototype.handleEvent = function(ev) { policy.forceDownload = function () { this.val = 'download'; }; policy.forceNewWindow = function () { this.val = 'new-window'; }; policy.forceNewPopup = function () { this.val = 'new-popup'; }; + policy.setNewWindowManifest = function (m) { this.manifest = JSON.stringify(m); }; } // Route events to EventEmitter. this.emit.apply(this, arguments); @@ -220,10 +221,10 @@ Window.prototype.__defineSetter__('menu', function(menu) { return; } if (v8_util.getConstructorName(menu) != 'Menu') - throw new String("'menu' property requries a valid Menu"); + throw new TypeError("'menu' property requries a valid Menu"); if (menu.type != 'menubar') - throw new String('Only menu of type "menubar" can be used as this.window menu'); + throw new TypeError('Only menu of type "menubar" can be used as this.window menu'); v8_util.setHiddenValue(this, 'menu', menu); CallObjectMethod(this, 'SetMenu', [ menu.id ]); @@ -249,6 +250,11 @@ Window.prototype.__defineGetter__('isFullscreen', function() { var result = CallObjectMethodSync(this, 'IsFullscreen', []); return Boolean(result[0]); }); + +Window.prototype.__defineGetter__('isTransparent', function() { + var result = CallObjectMethodSync(this, 'IsTransparent', []); + return Boolean(result[0]); +}); Window.prototype.__defineSetter__('isKioskMode', function(flag) { if (flag) @@ -407,8 +413,15 @@ Window.prototype.setShowInTaskbar = function(flag) { CallObjectMethod(this, 'SetShowInTaskbar', [ flag ]); } +Window.prototype.setVisibleOnAllWorkspaces = function(flag) { + CallObjectMethod(this, 'SetVisibleOnAllWorkspaces', [ Boolean(flag) ]); +} + Window.prototype.requestAttention = function(flash) { - flash = Boolean(flash); + if (typeof flash == 'boolean') { + // boolean true is redirected as -1 value + flash = flash ? -1 : 0; + } CallObjectMethod(this, 'RequestAttention', [ flash ]); } @@ -417,9 +430,19 @@ Window.prototype.setBadgeLabel = function(label) { CallObjectMethod(this, 'SetBadgeLabel', [ label ]); } +Window.prototype.setProgressBar = function(progress) { + if (typeof progress != "number") + throw new TypeError('progress must be a number'); + CallObjectMethod(this, 'SetProgressBar', [ progress ]); +} + +Window.prototype.setTransparent = function(transparent) { + CallObjectMethod(this, 'SetTransparent', [ transparent ]); +} + Window.prototype.setPosition = function(position) { if (position != 'center' && position != 'mouse') - throw new String('Invalid postion'); + throw new TypeError('Invalid postion'); CallObjectMethod(this, 'SetPosition', [ position ]); } @@ -482,8 +505,17 @@ Window.prototype.capturePage = function(callback, image_format_options) { CallObjectMethod(this, 'CapturePage', [options.format]); }; - Window.prototype.eval = function(frame, script) { - return CallObjectMethod(this, 'EvaluateScript', frame, script); - }; +Window.prototype.eval = function(frame, script) { + return CallObjectMethod(this, 'EvaluateScript', frame, script); +}; + +Window.prototype.evalNWBin = function(frame, script) { + return CallObjectMethod(this, 'EvaluateNWBin', frame, script); +}; + +Window.prototype.disableCache = function(flag) { + return CallObjectMethod(this, 'setCacheDisabled', flag); +}; + } // function Window.init diff --git a/src/breakpad_linux.cc b/src/breakpad_linux.cc index d9d7a3e339..2f1d8fcb49 100644 --- a/src/breakpad_linux.cc +++ b/src/breakpad_linux.cc @@ -29,7 +29,6 @@ #include "base/files/file_path.h" #include "base/linux_util.h" #include "base/path_service.h" -#include "base/platform_file.h" #include "base/posix/eintr_wrapper.h" #include "base/posix/global_descriptors.h" #include "base/process/memory.h" @@ -197,27 +196,11 @@ size_t LengthWithoutTrailingSpaces(const char* str, size_t len) { return len; } -// Populates the passed in allocated string and its size with the distro of -// the crashing process. -// The passed string is expected to be at least kDistroSize bytes long. -void PopulateDistro(char* distro, size_t* distro_len_param) { - size_t distro_len = std::min(my_strlen(base::g_linux_distro), kDistroSize); - memcpy(distro, base::g_linux_distro, distro_len); - if (distro_len_param) - *distro_len_param = distro_len; -} - void SetClientIdFromCommandLine(const CommandLine& command_line) { // Get the guid and linux distro from the command line switch. std::string switch_value = command_line.GetSwitchValueASCII(switches::kEnableCrashReporter); - size_t separator = switch_value.find(","); - if (separator != std::string::npos) { - GetBreakpadClient()->SetClientID(switch_value.substr(0, separator)); - base::SetLinuxDistro(switch_value.substr(separator + 1)); - } else { - GetBreakpadClient()->SetClientID(switch_value); - } + GetBreakpadClient()->SetBreakpadClientIdFromGUID(switch_value); } // MIME substrings. @@ -690,11 +673,8 @@ bool CrashDoneInProcessNoUpload( // Start constructing the message to send to the browser. char guid[kGuidSize + 1] = {0}; char crash_url[kMaxActiveURLSize + 1] = {0}; - char distro[kDistroSize + 1] = {0}; size_t guid_length = 0; size_t crash_url_length = 0; - size_t distro_length = 0; - PopulateDistro(distro, &distro_length); BreakpadInfo info = {0}; info.filename = NULL; info.fd = descriptor.fd(); @@ -702,8 +682,8 @@ bool CrashDoneInProcessNoUpload( info.process_type_length = my_strlen(g_process_type); info.crash_url = crash_url; info.crash_url_length = crash_url_length; - info.distro = distro; - info.distro_length = distro_length; + info.distro = base::g_linux_distro; + info.distro_length = my_strlen(base::g_linux_distro); info.upload = false; info.process_start_time = g_process_start_time; HandleCrashDump(info); diff --git a/src/breakpad_mac.mm b/src/breakpad_mac.mm index 48dc09e729..25b0282831 100644 --- a/src/breakpad_mac.mm +++ b/src/breakpad_mac.mm @@ -195,7 +195,7 @@ void InitCrashReporter() { [resource_path stringByAppendingPathComponent:@"crash_report_sender.app"]; NSString *reporter_location = [[NSBundle bundleWithPath:reporter_bundle_location] executablePath]; -#endif +#endif VLOG(1) << "resource_path: " << [resource_path UTF8String]; VLOG(1) << "inspector_location: " << [inspector_location UTF8String]; @@ -235,7 +235,7 @@ void InitCrashReporter() { // Initialize Breakpad. gBreakpadRef = BreakpadCreate(breakpad_config); if (!gBreakpadRef) { - LOG_IF(ERROR, base::mac::AmIBundled()) << "Breakpad initializaiton failed"; + LOG_IF(ERROR, base::mac::AmIBundled()) << "Breakpad initialization failed"; return; } diff --git a/src/breakpad_win.cc b/src/breakpad_win.cc index 88c15f5d31..65ddf4016b 100644 --- a/src/breakpad_win.cc +++ b/src/breakpad_win.cc @@ -739,9 +739,11 @@ void InitCrashReporter() { else if (GetBreakpadClient()->GetShouldDumpLargerDumps(is_per_user_install)) dump_type = kLargerDumpType; + int handler_types = google_breakpad::ExceptionHandler::HANDLER_EXCEPTION | + google_breakpad::ExceptionHandler::HANDLER_PURECALL; g_breakpad = new google_breakpad::ExceptionHandler(temp_dir, &FilterCallback, callback, NULL, - google_breakpad::ExceptionHandler::HANDLER_ALL, + handler_types, dump_type, pipe_name.c_str(), custom_info); // Now initialize the non crash dump handler. 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 e774be4178..7de1d69375 100644 --- a/src/browser/app_controller_mac.mm +++ b/src/browser/app_controller_mac.mm @@ -31,11 +31,13 @@ @implementation AppController +@synthesize appReady; + - (BOOL)application:(NSApplication*)sender openFile:(NSString*)filename { if (content::Shell::windows().size() == 0) { - CommandLine::ForCurrentProcess()->AppendArg([filename UTF8String]); - CommandLine::ForCurrentProcess()->FixOrigArgv4Finder([filename UTF8String]); + base::CommandLine::ForCurrentProcess()->AppendArg([filename UTF8String]); + base::CommandLine::ForCurrentProcess()->FixOrigArgv4Finder([filename UTF8String]); return TRUE; } @@ -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()); @@ -82,4 +95,30 @@ - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication return YES; } +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)app { + // The termination procedure is completely and gracefully handled by node-webkit + // (triggered by CloseAllWindows, app exits when last window closes) so we + // don't need Cocoa to terminate the application immediately (NSTerminateNow) + // neither run a special event loop (NSTerminateLater) waiting for a termination + // reply + nwapi::App::CloseAllWindows(false, true); + 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/autofill_popup_base_view.cc b/src/browser/autofill_popup_base_view.cc index ef87bcb065..69901380ef 100644 --- a/src/browser/autofill_popup_base_view.cc +++ b/src/browser/autofill_popup_base_view.cc @@ -8,16 +8,9 @@ #include "base/location.h" #include "base/message_loop/message_loop.h" #include "chrome/browser/ui/autofill/popup_constants.h" -#include "ui/gfx/point.h" -#include "ui/gfx/screen.h" #include "ui/views/border.h" -#include "ui/views/event_utils.h" #include "ui/views/widget/widget.h" -#if defined(USE_AURA) -#include "ui/wm/core/window_animations.h" -#endif - namespace autofill { const SkColor AutofillPopupBaseView::kBorderColor = @@ -49,7 +42,8 @@ AutofillPopupBaseView::~AutofillPopupBaseView() { } void AutofillPopupBaseView::DoShow() { - if (!GetWidget()) { + const bool initialize_widget = !GetWidget(); + if (initialize_widget) { observing_widget_->AddObserver(this); views::FocusManager* focus_manager = observing_widget_->GetFocusManager(); @@ -71,11 +65,9 @@ void AutofillPopupBaseView::DoShow() { params.parent = container_view(); widget->Init(params); widget->SetContentsView(this); -#if defined(USE_AURA) + // No animation for popup appearance (too distracting). - wm::SetWindowVisibilityAnimationTransition( - widget->GetNativeView(), wm::ANIMATE_HIDE); -#endif + widget->SetVisibilityAnimationTransition(views::Widget::ANIMATE_HIDE); } SetBorder(views::Border::CreateSolidBorder(kPopupBorderThickness, @@ -84,8 +76,10 @@ void AutofillPopupBaseView::DoShow() { DoUpdateBoundsAndRedrawPopup(); GetWidget()->Show(); - if (ShouldHideOnOutsideClick()) - GetWidget()->SetCapture(this); + // Showing the widget can change native focus (which would result in an + // immediate hiding of the popup). Only start observing after shown. + if (initialize_widget) + views::WidgetFocusManager::GetInstance()->AddFocusChangeListener(this); } void AutofillPopupBaseView::DoHide() { @@ -108,6 +102,7 @@ void AutofillPopupBaseView::DoHide() { void AutofillPopupBaseView::RemoveObserver() { observing_widget_->GetFocusManager()->UnregisterAccelerators(this); observing_widget_->RemoveObserver(this); + views::WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(this); } void AutofillPopupBaseView::DoUpdateBoundsAndRedrawPopup() { @@ -115,6 +110,13 @@ void AutofillPopupBaseView::DoUpdateBoundsAndRedrawPopup() { SchedulePaint(); } +void AutofillPopupBaseView::OnNativeFocusChange( + gfx::NativeView focused_before, + gfx::NativeView focused_now) { + if (GetWidget() && GetWidget()->GetNativeView() != focused_now) + HideController(); +} + void AutofillPopupBaseView::OnWidgetBoundsChanged(views::Widget* widget, const gfx::Rect& new_bounds) { DCHECK_EQ(widget, observing_widget_); @@ -157,39 +159,10 @@ void AutofillPopupBaseView::OnMouseMoved(const ui::MouseEvent& event) { } bool AutofillPopupBaseView::OnMousePressed(const ui::MouseEvent& event) { - if (HitTestPoint(event.location())) - return true; - - if (ShouldHideOnOutsideClick()) { - GetWidget()->ReleaseCapture(); - - gfx::Point screen_loc = event.location(); - views::View::ConvertPointToScreen(this, &screen_loc); - - ui::MouseEvent mouse_event = event; - mouse_event.set_location(screen_loc); - - if (ShouldRepostEvent(mouse_event)) { - gfx::NativeView native_view = GetWidget()->GetNativeView(); - gfx::Screen* screen = gfx::Screen::GetScreenFor(native_view); - gfx::NativeWindow window = screen->GetWindowAtScreenPoint(screen_loc); - views::RepostLocatedEvent(window, mouse_event); - } - - HideController(); - // |this| is now deleted. - } - - return false; + return event.GetClickCount() == 1; } void AutofillPopupBaseView::OnMouseReleased(const ui::MouseEvent& event) { - // Because this view can can be shown in response to a mouse press, it can - // receive an OnMouseReleased event just after showing. This breaks the mouse - // capture, so restart capturing here. - if (ShouldHideOnOutsideClick() && GetWidget()) - GetWidget()->SetCapture(this); - // We only care about the left click. if (event.IsOnlyLeftMouseButton() && HitTestPoint(event.location())) AcceptSelection(event.location()); @@ -256,27 +229,13 @@ void AutofillPopupBaseView::ClearSelection() { delegate_->SelectionCleared(); } -bool AutofillPopupBaseView::ShouldHideOnOutsideClick() { - if (delegate_) - return delegate_->ShouldHideOnOutsideClick(); - - // |this| instance should be in the process of being destroyed, so the return - // value shouldn't matter. - return false; -} - void AutofillPopupBaseView::HideController() { if (delegate_) delegate_->Hide(); } -bool AutofillPopupBaseView::ShouldRepostEvent(const ui::MouseEvent& event) { - return delegate_->ShouldRepostEvent(event); -} - gfx::NativeView AutofillPopupBaseView::container_view() { return delegate_->container_view(); } - } // namespace autofill diff --git a/src/browser/autofill_popup_base_view.h b/src/browser/autofill_popup_base_view.h index e379f3d98b..ef4ce08abb 100644 --- a/src/browser/autofill_popup_base_view.h +++ b/src/browser/autofill_popup_base_view.h @@ -7,6 +7,7 @@ #include "base/memory/weak_ptr.h" #include "content/nw/src/browser/autofill_popup_view_delegate.h" +#include "ui/views/focus/widget_focus_manager.h" #include "ui/views/widget/widget_delegate.h" #include "ui/views/widget/widget_observer.h" @@ -23,11 +24,20 @@ namespace autofill { // Class that deals with the event handling for Autofill-style popups. This // class should only be instantiated by sub-classes. class AutofillPopupBaseView : public views::WidgetDelegateView, + public views::WidgetFocusChangeListener, public views::WidgetObserver { + public: + static const SkColor kBorderColor; + static const SkColor kHoveredBackgroundColor; + static const SkColor kItemTextColor; + static const SkColor kPopupBackground; + static const SkColor kValueTextColor; + static const SkColor kWarningTextColor; + protected: explicit AutofillPopupBaseView(AutofillPopupViewDelegate* delegate, views::Widget* observing_widget); - virtual ~AutofillPopupBaseView(); + ~AutofillPopupBaseView() override; // Show this popup. Idempotent. void DoShow(); @@ -38,29 +48,26 @@ class AutofillPopupBaseView : public views::WidgetDelegateView, // Update size of popup and paint. void DoUpdateBoundsAndRedrawPopup(); - static const SkColor kBorderColor; - static const SkColor kHoveredBackgroundColor; - static const SkColor kItemTextColor; - static const SkColor kPopupBackground; - static const SkColor kValueTextColor; - static const SkColor kWarningTextColor; - private: friend class AutofillPopupBaseViewTest; // views::Views implementation. - virtual void OnMouseCaptureLost() OVERRIDE; - virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE; - virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE; - virtual void OnMouseMoved(const ui::MouseEvent& event) OVERRIDE; - virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE; - virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE; - virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; - virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE; + void OnMouseCaptureLost() override; + bool OnMouseDragged(const ui::MouseEvent& event) override; + void OnMouseExited(const ui::MouseEvent& event) override; + void OnMouseMoved(const ui::MouseEvent& event) override; + bool OnMousePressed(const ui::MouseEvent& event) override; + void OnMouseReleased(const ui::MouseEvent& event) override; + void OnGestureEvent(ui::GestureEvent* event) override; + bool AcceleratorPressed(const ui::Accelerator& accelerator) override; + + // views::WidgetFocusChangeListener implementation. + void OnNativeFocusChange(gfx::NativeView focused_before, + gfx::NativeView focused_now) override; // views::WidgetObserver implementation. - virtual void OnWidgetBoundsChanged(views::Widget* widget, - const gfx::Rect& new_bounds) OVERRIDE; + void OnWidgetBoundsChanged(views::Widget* widget, + const gfx::Rect& new_bounds) override; // Stop observing the |observing_widget_|. void RemoveObserver(); @@ -69,16 +76,10 @@ class AutofillPopupBaseView : public views::WidgetDelegateView, void AcceptSelection(const gfx::Point& point); void ClearSelection(); - // If the popup should be hidden if the user clicks outside it's bounds. - bool ShouldHideOnOutsideClick(); - // Hide the controller of this view. This assumes that doing so will // eventually hide this view in the process. void HideController(); - // Returns true if this event should be passed along. - bool ShouldRepostEvent(const ui::MouseEvent& event); - // Must return the container view for this popup. gfx::NativeView container_view(); diff --git a/src/browser/autofill_popup_base_view_cocoa.h b/src/browser/autofill_popup_base_view_cocoa.h new file mode 100644 index 0000000000..24212726c4 --- /dev/null +++ b/src/browser/autofill_popup_base_view_cocoa.h @@ -0,0 +1,48 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_COCOA_AUTOFILL_AUTOFILL_POPUP_BASE_VIEW_COCOA_H_ +#define CHROME_BROWSER_UI_COCOA_AUTOFILL_AUTOFILL_POPUP_BASE_VIEW_COCOA_H_ + +#import + +#import "ui/base/cocoa/base_view.h" + +namespace autofill { +class AutofillPopupViewDelegate; +} + +@interface AutofillPopupBaseViewCocoa : BaseView { + @private + __weak autofill::AutofillPopupViewDelegate* delegate_; +} + +- (NSColor*)backgroundColor; +- (NSColor*)borderColor; +- (NSColor*)highlightColor; +- (NSColor*)nameColor; +- (NSColor*)separatorColor; +- (NSColor*)subtextColor; +- (NSColor*)warningColor; + +- (id)initWithDelegate:(autofill::AutofillPopupViewDelegate*)delegate + frame:(NSRect)frame; + +// Informs the view that its delegate has been (or will imminently be) +// destroyed. +- (void)delegateDestroyed; + +// Draw the popup's background and border. +- (void)drawBackgroundAndBorder; + +// Draws a thin separator in the popup UI. +- (void)drawSeparatorWithBounds:(NSRect)bounds; + +// Messages from AutofillPopupViewBridge: +- (void)updateBoundsAndRedrawPopup; +- (void)showPopup; +- (void)hidePopup; +@end + +#endif // CHROME_BROWSER_UI_COCOA_AUTOFILL_AUTOFILL_POPUP_BASE_VIEW_COCOA_H_ diff --git a/src/browser/autofill_popup_base_view_cocoa.mm b/src/browser/autofill_popup_base_view_cocoa.mm new file mode 100644 index 0000000000..7a8743c802 --- /dev/null +++ b/src/browser/autofill_popup_base_view_cocoa.mm @@ -0,0 +1,175 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "chrome/browser/ui/cocoa/autofill/autofill_popup_base_view_cocoa.h" + +#include "chrome/browser/ui/autofill/autofill_popup_view_delegate.h" +#include "chrome/browser/ui/autofill/popup_constants.h" +#include "ui/base/cocoa/window_size_constants.h" + +@implementation AutofillPopupBaseViewCocoa + +#pragma mark - +#pragma mark Colors + +- (NSColor*)backgroundColor { + return [NSColor whiteColor]; +} + +- (NSColor*)borderColor { + return [NSColor colorForControlTint:[NSColor currentControlTint]]; +} + +- (NSColor*)highlightColor { + return [NSColor selectedControlColor]; +} + +- (NSColor*)nameColor { + return [NSColor blackColor]; +} + +- (NSColor*)separatorColor { + return [NSColor colorWithCalibratedWhite:220 / 255.0 alpha:1]; +} + +- (NSColor*)subtextColor { + return [NSColor grayColor]; +} + +- (NSColor*)warningColor { + return [NSColor grayColor]; +} + +#pragma mark - +#pragma mark Public methods + +- (id)initWithDelegate:(autofill::AutofillPopupViewDelegate*)delegate + frame:(NSRect)frame { + self = [super initWithFrame:frame]; + if (self) + delegate_ = delegate; + + return self; +} + +- (void)delegateDestroyed { + delegate_ = NULL; +} + +- (void)drawSeparatorWithBounds:(NSRect)bounds { + [[self separatorColor] set]; + [NSBezierPath fillRect:bounds]; +} + +// A slight optimization for drawing: +// https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaViewsGuide/Optimizing/Optimizing.html +- (BOOL)isOpaque { + return YES; +} + +- (BOOL)isFlipped { + // Flipped so that it's easier to share controller logic with other OSes. + return YES; +} + +- (void)drawBackgroundAndBorder { + // The inset is needed since the border is centered on the |path|. + // TODO(isherman): We should consider using asset-based drawing for the + // border, creating simple bitmaps for the view's border and background, and + // drawing them using NSDrawNinePartImage(). + CGFloat inset = autofill::kPopupBorderThickness / 2.0; + NSRect borderRect = NSInsetRect([self bounds], inset, inset); + NSBezierPath* path = [NSBezierPath bezierPathWithRect:borderRect]; + [[self backgroundColor] setFill]; + [path fill]; + [path setLineWidth:autofill::kPopupBorderThickness]; + [[self borderColor] setStroke]; + [path stroke]; +} + +- (void)mouseUp:(NSEvent*)theEvent { + // If the view is in the process of being destroyed, abort. + if (!delegate_) + return; + + // Only accept single-click. + if ([theEvent clickCount] > 1) + return; + + NSPoint location = [self convertPoint:[theEvent locationInWindow] + fromView:nil]; + + if (NSPointInRect(location, [self bounds])) { + delegate_->SetSelectionAtPoint(gfx::Point(NSPointToCGPoint(location))); + delegate_->AcceptSelectedLine(); + } +} + +- (void)mouseMoved:(NSEvent*)theEvent { + // If the view is in the process of being destroyed, abort. + if (!delegate_) + return; + + NSPoint location = [self convertPoint:[theEvent locationInWindow] + fromView:nil]; + + delegate_->SetSelectionAtPoint(gfx::Point(NSPointToCGPoint(location))); +} + +- (void)mouseDragged:(NSEvent*)theEvent { + [self mouseMoved:theEvent]; +} + +- (void)mouseExited:(NSEvent*)theEvent { + // If the view is in the process of being destroyed, abort. + if (!delegate_) + return; + + delegate_->SelectionCleared(); +} + +#pragma mark - +#pragma mark Messages from AutofillPopupViewBridge: + +- (void)updateBoundsAndRedrawPopup { + NSRect frame = NSRectFromCGRect(delegate_->popup_bounds().ToCGRect()); + + // Flip coordinates back into Cocoa-land. The controller's platform-neutral + // coordinate space places the origin at the top-left of the first screen, + // whereas Cocoa's coordinate space expects the origin to be at the + // bottom-left of this same screen. + NSScreen* screen = [[NSScreen screens] objectAtIndex:0]; + frame.origin.y = NSMaxY([screen frame]) - NSMaxY(frame); + + // TODO(isherman): The view should support scrolling if the popup gets too + // big to fit on the screen. + [[self window] setFrame:frame display:YES]; + [self setNeedsDisplay:YES]; +} + +- (void)showPopup { + NSWindow* window = + [[NSWindow alloc] initWithContentRect:ui::kWindowSizeDeterminedLater + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:YES]; + [window setContentView:self]; + + // Telling Cocoa that the window is opaque enables some drawing optimizations. + [window setOpaque:YES]; + + [self updateBoundsAndRedrawPopup]; + [[delegate_->container_view() window] addChildWindow:window + ordered:NSWindowAbove]; +} + +- (void)hidePopup { + // Remove the child window before closing, otherwise it can mess up + // display ordering. + NSWindow* window = [self window]; + [[window parentWindow] removeChildWindow:window]; + [window close]; +} + +@end diff --git a/src/browser/autofill_popup_controller.h b/src/browser/autofill_popup_controller.h index da66f3e987..a5a723ca83 100644 --- a/src/browser/autofill_popup_controller.h +++ b/src/browser/autofill_popup_controller.h @@ -9,17 +9,18 @@ #include "base/compiler_specific.h" #include "base/strings/string16.h" -#include "content/nw/src/browser/autofill_popup_view_delegate.h" +#include "chrome/browser/ui/autofill/autofill_popup_view_delegate.h" namespace gfx { class FontList; class Point; class Rect; -class RectF; } namespace autofill { +struct Suggestion; + // This interface provides data to an AutofillPopupView. class AutofillPopupController : public AutofillPopupViewDelegate { public: @@ -47,32 +48,19 @@ class AutofillPopupController : public AutofillPopupViewDelegate { // the top left of the popup. virtual gfx::Rect GetRowBounds(size_t index) = 0; - // The bounds of the form field element (screen coordinates). - virtual const gfx::RectF& element_bounds() const = 0; - - // If the current popup should be displayed in RTL mode. - virtual bool IsRTL() const = 0; - - // TODO(csharp): The names, subtexts and icon getters can probably be adjusted - // to take in the row index and return a single element, instead of the - // whole vector. - // The main labels for each autofill item. - virtual const std::vector& names() const = 0; - - // Smaller labels for each autofill item. - virtual const std::vector& subtexts() const = 0; - - // A string which identifies the icon to be shown for each autofill item. - virtual const std::vector& icons() const = 0; + // Returns the number of lines of data that there are. + virtual size_t GetLineCount() const = 0; - // Identifier for the row. - virtual const std::vector& identifiers() const = 0; + // Returns the suggestion or pre-elided string at the given row index. + virtual const autofill::Suggestion& GetSuggestionAt(size_t row) const = 0; + virtual const base::string16& GetElidedValueAt(size_t row) const = 0; + virtual const base::string16& GetElidedLabelAt(size_t row) const = 0; #if !defined(OS_ANDROID) // The same font can vary based on the type of data it is showing, // so we need to know the row. - virtual const gfx::FontList& GetNameFontListForRow(size_t index) const = 0; - virtual const gfx::FontList& subtext_font_list() const = 0; + virtual const gfx::FontList& GetValueFontListForRow(size_t index) const = 0; + virtual const gfx::FontList& GetLabelFontList() const = 0; #endif // Returns the index of the selected line. A line is "selected" when it is @@ -80,7 +68,7 @@ class AutofillPopupController : public AutofillPopupViewDelegate { virtual int selected_line() const = 0; protected: - virtual ~AutofillPopupController() {} + ~AutofillPopupController() override {} }; } // namespace autofill diff --git a/src/browser/autofill_popup_controller_impl.cc b/src/browser/autofill_popup_controller_impl.cc index c85210e31b..a8331e7b7e 100644 --- a/src/browser/autofill_popup_controller_impl.cc +++ b/src/browser/autofill_popup_controller_impl.cc @@ -2,26 +2,27 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/nw/src/browser/autofill_popup_controller_impl.h" +#include "chrome/browser/ui/autofill/autofill_popup_controller_impl.h" #include #include #include "base/logging.h" #include "base/strings/utf_string_conversions.h" -#include "content/nw/src/browser/autofill_popup_view.h" +#include "chrome/browser/ui/autofill/autofill_popup_view.h" #include "chrome/browser/ui/autofill/popup_constants.h" #include "components/autofill/core/browser/autofill_popup_delegate.h" #include "components/autofill/core/browser/popup_item_ids.h" +#include "components/autofill/core/browser/suggestion.h" #include "content/public/browser/native_web_keyboard_event.h" -#include "grit/component_scaled_resources.h" +#include "grit/components_scaled_resources.h" #include "ui/base/resource/resource_bundle.h" #include "ui/events/event.h" -#include "ui/gfx/rect_conversions.h" +#include "ui/gfx/geometry/rect_conversions.h" +#include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/screen.h" #include "ui/gfx/text_elider.h" #include "ui/gfx/text_utils.h" -#include "ui/gfx/vector2d.h" using base::WeakPtr; @@ -38,7 +39,7 @@ const size_t kRowHeight = 24; const size_t kSeparatorHeight = 1; #if !defined(OS_ANDROID) -// Size difference between name and subtext in pixels. +// Size difference between name and label in pixels. const int kLabelFontSizeDelta = -2; const size_t kNamePadding = AutofillPopupView::kNamePadding; @@ -53,12 +54,16 @@ struct DataResource { const DataResource kDataResources[] = { { "americanExpressCC", IDR_AUTOFILL_CC_AMEX }, - { "dinersCC", IDR_AUTOFILL_CC_DINERS }, + { "dinersCC", IDR_AUTOFILL_CC_GENERIC }, { "discoverCC", IDR_AUTOFILL_CC_DISCOVER }, { "genericCC", IDR_AUTOFILL_CC_GENERIC }, - { "jcbCC", IDR_AUTOFILL_CC_JCB }, + { "jcbCC", IDR_AUTOFILL_CC_GENERIC }, { "masterCardCC", IDR_AUTOFILL_CC_MASTERCARD }, { "visaCC", IDR_AUTOFILL_CC_VISA }, + { "scanCreditCardIcon", IDR_AUTOFILL_CC_SCAN_NEW }, +#if defined(OS_MACOSX) && !defined(OS_IOS) + { "macContactsIcon", IDR_AUTOFILL_MAC_CONTACTS_ICON }, +#endif // defined(OS_MACOSX) && !defined(OS_IOS) }; } // namespace @@ -71,9 +76,8 @@ WeakPtr AutofillPopupControllerImpl::GetOrCreate( gfx::NativeView container_view, const gfx::RectF& element_bounds, base::i18n::TextDirection text_direction) { - DCHECK(!previous.get() || previous->delegate_.get() == delegate.get()); - if (previous.get() && previous->web_contents() == web_contents && + previous->delegate_.get() == delegate.get() && previous->container_view() == container_view && previous->element_bounds() == element_bounds) { previous->ClearState(); @@ -102,19 +106,19 @@ AutofillPopupControllerImpl::AutofillPopupControllerImpl( view_(NULL), delegate_(delegate), text_direction_(text_direction), - hide_on_outside_click_(false), weak_ptr_factory_(this) { ClearState(); controller_common_->SetKeyPressCallback( base::Bind(&AutofillPopupControllerImpl::HandleKeyPressEvent, base::Unretained(this))); #if !defined(OS_ANDROID) - subtext_font_list_ = name_font_list_.DeriveWithSizeDelta(kLabelFontSizeDelta); + label_font_list_ = value_font_list_.DeriveWithSizeDelta(kLabelFontSizeDelta); + title_font_list_ = value_font_list_.DeriveWithStyle(gfx::Font::BOLD); #if defined(OS_MACOSX) // There is no italic version of the system font. - warning_font_list_ = name_font_list_; + warning_font_list_ = value_font_list_; #else - warning_font_list_ = name_font_list_.DeriveWithStyle(gfx::Font::ITALIC); + warning_font_list_ = value_font_list_.DeriveWithStyle(gfx::Font::ITALIC); #endif #endif } @@ -122,11 +126,10 @@ AutofillPopupControllerImpl::AutofillPopupControllerImpl( AutofillPopupControllerImpl::~AutofillPopupControllerImpl() {} void AutofillPopupControllerImpl::Show( - const std::vector& names, - const std::vector& subtexts, - const std::vector& icons, - const std::vector& identifiers) { - SetValues(names, subtexts, icons, identifiers); + const std::vector& suggestions) { + SetValues(suggestions); + DCHECK_EQ(suggestions_.size(), elided_values_.size()); + DCHECK_EQ(suggestions_.size(), elided_labels_.size()); #if !defined(OS_ANDROID) // Android displays the long text with ellipsis using the view attributes. @@ -134,12 +137,14 @@ void AutofillPopupControllerImpl::Show( UpdatePopupBounds(); int popup_width = popup_bounds().width(); - // Elide the name and subtext strings so that the popup fits in the available + // Elide the name and label strings so that the popup fits in the available // space. - for (size_t i = 0; i < names_.size(); ++i) { - int name_width = gfx::GetStringWidth(names_[i], GetNameFontListForRow(i)); - int subtext_width = gfx::GetStringWidth(subtexts_[i], subtext_font_list()); - int total_text_length = name_width + subtext_width; + for (size_t i = 0; i < suggestions_.size(); ++i) { + int value_width = + gfx::GetStringWidth(suggestions_[i].value, GetValueFontListForRow(i)); + int label_width = + gfx::GetStringWidth(suggestions_[i].label, GetLabelFontList()); + int total_text_length = value_width + label_width; // The line can have no strings if it represents a UI element, such as // a separator line. @@ -148,18 +153,16 @@ void AutofillPopupControllerImpl::Show( int available_width = popup_width - RowWidthWithoutText(i); - // Each field recieves space in proportion to its length. - int name_size = available_width * name_width / total_text_length; - names_[i] = gfx::ElideText(names_[i], - GetNameFontListForRow(i), - name_size, - gfx::ELIDE_AT_END); - - int subtext_size = available_width * subtext_width / total_text_length; - subtexts_[i] = gfx::ElideText(subtexts_[i], - subtext_font_list(), - subtext_size, - gfx::ELIDE_AT_END); + // Each field receives space in proportion to its length. + int value_size = available_width * value_width / total_text_length; + elided_values_[i] = gfx::ElideText(suggestions_[i].value, + GetValueFontListForRow(i), + value_size, gfx::ELIDE_TAIL); + + int label_size = available_width * label_width / total_text_length; + elided_labels_[i] = gfx::ElideText(suggestions_[i].label, + GetLabelFontList(), + label_size, gfx::ELIDE_TAIL); } #endif @@ -178,31 +181,36 @@ void AutofillPopupControllerImpl::Show( UpdateBoundsAndRedrawPopup(); } - delegate_->OnPopupShown(); controller_common_->RegisterKeyPressCallback(); + delegate_->OnPopupShown(); + + DCHECK_EQ(suggestions_.size(), elided_values_.size()); + DCHECK_EQ(suggestions_.size(), elided_labels_.size()); } void AutofillPopupControllerImpl::UpdateDataListValues( const std::vector& values, const std::vector& labels) { + DCHECK_EQ(suggestions_.size(), elided_values_.size()); + DCHECK_EQ(suggestions_.size(), elided_labels_.size()); + // Remove all the old data list values, which should always be at the top of // the list if they are present. - while (!identifiers_.empty() && - identifiers_[0] == POPUP_ITEM_ID_DATALIST_ENTRY) { - names_.erase(names_.begin()); - subtexts_.erase(subtexts_.begin()); - icons_.erase(icons_.begin()); - identifiers_.erase(identifiers_.begin()); + while (!suggestions_.empty() && + suggestions_[0].frontend_id == POPUP_ITEM_ID_DATALIST_ENTRY) { + suggestions_.erase(suggestions_.begin()); + elided_values_.erase(elided_values_.begin()); + elided_labels_.erase(elided_labels_.begin()); } // If there are no new data list values, exit (clearing the separator if there // is one). if (values.empty()) { - if (!identifiers_.empty() && identifiers_[0] == POPUP_ITEM_ID_SEPARATOR) { - names_.erase(names_.begin()); - subtexts_.erase(subtexts_.begin()); - icons_.erase(icons_.begin()); - identifiers_.erase(identifiers_.begin()); + if (!suggestions_.empty() && + suggestions_[0].frontend_id == POPUP_ITEM_ID_SEPARATOR) { + suggestions_.erase(suggestions_.begin()); + elided_values_.erase(elided_values_.begin()); + elided_labels_.erase(elided_labels_.begin()); } // The popup contents have changed, so either update the bounds or hide it. @@ -215,28 +223,38 @@ void AutofillPopupControllerImpl::UpdateDataListValues( } // Add a separator if there are any other values. - if (!identifiers_.empty() && identifiers_[0] != POPUP_ITEM_ID_SEPARATOR) { - names_.insert(names_.begin(), base::string16()); - subtexts_.insert(subtexts_.begin(), base::string16()); - icons_.insert(icons_.begin(), base::string16()); - identifiers_.insert(identifiers_.begin(), POPUP_ITEM_ID_SEPARATOR); + if (!suggestions_.empty() && + suggestions_[0].frontend_id != POPUP_ITEM_ID_SEPARATOR) { + suggestions_.insert(suggestions_.begin(), autofill::Suggestion()); + suggestions_[0].frontend_id = POPUP_ITEM_ID_SEPARATOR; + elided_values_.insert(elided_values_.begin(), base::string16()); + elided_labels_.insert(elided_labels_.begin(), base::string16()); } - - names_.insert(names_.begin(), values.begin(), values.end()); - subtexts_.insert(subtexts_.begin(), labels.begin(), labels.end()); - - // Add the values that are the same for all data list elements. - icons_.insert(icons_.begin(), values.size(), base::string16()); - identifiers_.insert( - identifiers_.begin(), values.size(), POPUP_ITEM_ID_DATALIST_ENTRY); + // Prepend the parameters to the suggestions we already have. + suggestions_.insert(suggestions_.begin(), values.size(), Suggestion()); + elided_values_.insert(elided_values_.begin(), values.size(), + base::string16()); + elided_labels_.insert(elided_labels_.begin(), values.size(), + base::string16()); + for (size_t i = 0; i < values.size(); i++) { + suggestions_[i].value = values[i]; + suggestions_[i].label = labels[i]; + suggestions_[i].frontend_id = POPUP_ITEM_ID_DATALIST_ENTRY; + + // TODO(brettw) it looks like these should be elided. + elided_values_[i] = values[i]; + elided_labels_[i] = labels[i]; + } UpdateBoundsAndRedrawPopup(); + DCHECK_EQ(suggestions_.size(), elided_values_.size()); + DCHECK_EQ(suggestions_.size(), elided_labels_.size()); } void AutofillPopupControllerImpl::Hide() { controller_common_->RemoveKeyPressCallback(); - if (delegate_.get()) + if (delegate_) delegate_->OnPopupHidden(); if (view_) @@ -262,10 +280,13 @@ bool AutofillPopupControllerImpl::HandleKeyPressEvent( SelectNextLine(); return true; case ui::VKEY_PRIOR: // Page up. - SetSelectedLine(0); + // Set no line and then select the next line in case the first line is not + // selectable. + SetSelectedLine(kNoSelection); + SelectNextLine(); return true; case ui::VKEY_NEXT: // Page down. - SetSelectedLine(names().size() - 1); + SetSelectedLine(GetLineCount() - 1); return true; case ui::VKEY_ESCAPE: Hide(); @@ -307,9 +328,9 @@ bool AutofillPopupControllerImpl::AcceptSelectedLine() { return false; DCHECK_GE(selected_line_, 0); - DCHECK_LT(selected_line_, static_cast(names_.size())); + DCHECK_LT(selected_line_, static_cast(GetLineCount())); - if (!CanAccept(identifiers_[selected_line_])) + if (!CanAccept(suggestions_[selected_line_].frontend_id)) return false; AcceptSuggestion(selected_line_); @@ -320,17 +341,9 @@ void AutofillPopupControllerImpl::SelectionCleared() { SetSelectedLine(kNoSelection); } -bool AutofillPopupControllerImpl::ShouldRepostEvent( - const ui::MouseEvent& event) { - return delegate_->ShouldRepostEvent(event); -} - -bool AutofillPopupControllerImpl::ShouldHideOnOutsideClick() const { - return hide_on_outside_click_; -} - void AutofillPopupControllerImpl::AcceptSuggestion(size_t index) { - delegate_->DidAcceptSuggestion(full_names_[index], identifiers_[index]); + const autofill::Suggestion& suggestion = suggestions_[index]; + delegate_->DidAcceptSuggestion(suggestion.value, suggestion.frontend_id); } int AutofillPopupControllerImpl::GetIconResourceID( @@ -346,26 +359,26 @@ int AutofillPopupControllerImpl::GetIconResourceID( bool AutofillPopupControllerImpl::CanDelete(size_t index) const { // TODO(isherman): Native AddressBook suggestions on Mac and Android should // not be considered to be deleteable. - int id = identifiers_[index]; + int id = suggestions_[index].frontend_id; return id > 0 || id == POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY || id == POPUP_ITEM_ID_PASSWORD_ENTRY; } bool AutofillPopupControllerImpl::IsWarning(size_t index) const { - return identifiers_[index] == POPUP_ITEM_ID_WARNING_MESSAGE; + return suggestions_[index].frontend_id == POPUP_ITEM_ID_WARNING_MESSAGE; } gfx::Rect AutofillPopupControllerImpl::GetRowBounds(size_t index) { int top = kPopupBorderThickness; for (size_t i = 0; i < index; ++i) { - top += GetRowHeightFromId(identifiers()[i]); + top += GetRowHeightFromId(suggestions_[i].frontend_id); } return gfx::Rect( kPopupBorderThickness, top, popup_bounds_.width() - 2 * kPopupBorderThickness, - GetRowHeightFromId(identifiers()[index])); + GetRowHeightFromId(suggestions_[index].frontend_id)); } void AutofillPopupControllerImpl::SetPopupBounds(const gfx::Rect& bounds) { @@ -393,34 +406,39 @@ bool AutofillPopupControllerImpl::IsRTL() const { return text_direction_ == base::i18n::RIGHT_TO_LEFT; } -const std::vector& AutofillPopupControllerImpl::names() const { - return names_; +size_t AutofillPopupControllerImpl::GetLineCount() const { + return suggestions_.size(); } -const std::vector& AutofillPopupControllerImpl::subtexts() - const { - return subtexts_; +const autofill::Suggestion& AutofillPopupControllerImpl::GetSuggestionAt( + size_t row) const { + return suggestions_[row]; } -const std::vector& AutofillPopupControllerImpl::icons() const { - return icons_; +const base::string16& AutofillPopupControllerImpl::GetElidedValueAt( + size_t row) const { + return elided_values_[row]; } -const std::vector& AutofillPopupControllerImpl::identifiers() const { - return identifiers_; +const base::string16& AutofillPopupControllerImpl::GetElidedLabelAt( + size_t row) const { + return elided_labels_[row]; } #if !defined(OS_ANDROID) -const gfx::FontList& AutofillPopupControllerImpl::GetNameFontListForRow( +const gfx::FontList& AutofillPopupControllerImpl::GetValueFontListForRow( size_t index) const { - if (identifiers_[index] == POPUP_ITEM_ID_WARNING_MESSAGE) + if (suggestions_[index].frontend_id == POPUP_ITEM_ID_WARNING_MESSAGE) return warning_font_list_; - return name_font_list_; + if (suggestions_[index].frontend_id == POPUP_ITEM_ID_TITLE) + return title_font_list_; + + return value_font_list_; } -const gfx::FontList& AutofillPopupControllerImpl::subtext_font_list() const { - return subtext_font_list_; +const gfx::FontList& AutofillPopupControllerImpl::GetLabelFontList() const { + return label_font_list_; } #endif @@ -428,27 +446,26 @@ int AutofillPopupControllerImpl::selected_line() const { return selected_line_; } -void AutofillPopupControllerImpl::set_hide_on_outside_click( - bool hide_on_outside_click) { - hide_on_outside_click_ = hide_on_outside_click; -} - void AutofillPopupControllerImpl::SetSelectedLine(int selected_line) { if (selected_line_ == selected_line) return; if (selected_line_ != kNoSelection && - static_cast(selected_line_) < identifiers_.size()) + static_cast(selected_line_) < suggestions_.size()) InvalidateRow(selected_line_); - if (selected_line != kNoSelection) + if (selected_line != kNoSelection) { InvalidateRow(selected_line); + if (!CanAccept(suggestions_[selected_line].frontend_id)) + selected_line = kNoSelection; + } + selected_line_ = selected_line; if (selected_line_ != kNoSelection) { - delegate_->DidSelectSuggestion(names_[selected_line_], - identifiers_[selected_line_]); + delegate_->DidSelectSuggestion(elided_values_[selected_line_], + suggestions_[selected_line_].frontend_id); } else { delegate_->ClearPreviewedForm(); } @@ -458,12 +475,12 @@ void AutofillPopupControllerImpl::SelectNextLine() { int new_selected_line = selected_line_ + 1; // Skip over any lines that can't be selected. - while (static_cast(new_selected_line) < names_.size() && - !CanAccept(identifiers()[new_selected_line])) { + while (static_cast(new_selected_line) < GetLineCount() && + !CanAccept(suggestions_[new_selected_line].frontend_id)) { ++new_selected_line; } - if (new_selected_line >= static_cast(names_.size())) + if (new_selected_line >= static_cast(GetLineCount())) new_selected_line = 0; SetSelectedLine(new_selected_line); @@ -474,12 +491,12 @@ void AutofillPopupControllerImpl::SelectPreviousLine() { // Skip over any lines that can't be selected. while (new_selected_line > kNoSelection && - !CanAccept(identifiers()[new_selected_line])) { + !CanAccept(GetSuggestionAt(new_selected_line).frontend_id)) { --new_selected_line; } if (new_selected_line <= kNoSelection) - new_selected_line = names_.size() - 1; + new_selected_line = GetLineCount() - 1; SetSelectedLine(new_selected_line); } @@ -489,20 +506,18 @@ bool AutofillPopupControllerImpl::RemoveSelectedLine() { return false; DCHECK_GE(selected_line_, 0); - DCHECK_LT(selected_line_, static_cast(names_.size())); + DCHECK_LT(selected_line_, static_cast(GetLineCount())); if (!CanDelete(selected_line_)) return false; - delegate_->RemoveSuggestion(full_names_[selected_line_], - identifiers_[selected_line_]); + delegate_->RemoveSuggestion(suggestions_[selected_line_].value, + suggestions_[selected_line_].frontend_id); // Remove the deleted element. - names_.erase(names_.begin() + selected_line_); - full_names_.erase(full_names_.begin() + selected_line_); - subtexts_.erase(subtexts_.begin() + selected_line_); - icons_.erase(icons_.begin() + selected_line_); - identifiers_.erase(identifiers_.begin() + selected_line_); + suggestions_.erase(suggestions_.begin() + selected_line_); + elided_values_.erase(elided_values_.begin() + selected_line_); + elided_labels_.erase(elided_labels_.begin() + selected_line_); SetSelectedLine(kNoSelection); @@ -519,15 +534,15 @@ bool AutofillPopupControllerImpl::RemoveSelectedLine() { int AutofillPopupControllerImpl::LineFromY(int y) { int current_height = kPopupBorderThickness; - for (size_t i = 0; i < identifiers().size(); ++i) { - current_height += GetRowHeightFromId(identifiers()[i]); + for (size_t i = 0; i < suggestions_.size(); ++i) { + current_height += GetRowHeightFromId(suggestions_[i].frontend_id); if (y <= current_height) return i; } // The y value goes beyond the popup so stop the selection at the last line. - return identifiers().size() - 1; + return GetLineCount() - 1; } int AutofillPopupControllerImpl::GetRowHeightFromId(int identifier) const { @@ -538,27 +553,31 @@ int AutofillPopupControllerImpl::GetRowHeightFromId(int identifier) const { } bool AutofillPopupControllerImpl::CanAccept(int id) { - return id != POPUP_ITEM_ID_SEPARATOR && id != POPUP_ITEM_ID_WARNING_MESSAGE; + return id != POPUP_ITEM_ID_SEPARATOR && id != POPUP_ITEM_ID_WARNING_MESSAGE && + id != POPUP_ITEM_ID_TITLE; } bool AutofillPopupControllerImpl::HasSuggestions() { - return identifiers_.size() != 0 && - (identifiers_[0] > 0 || - identifiers_[0] == POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY || - identifiers_[0] == POPUP_ITEM_ID_PASSWORD_ENTRY || - identifiers_[0] == POPUP_ITEM_ID_DATALIST_ENTRY); + if (suggestions_.empty()) + return false; + int id = suggestions_[0].frontend_id; + return id > 0 || + id == POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY || + id == POPUP_ITEM_ID_PASSWORD_ENTRY || + id == POPUP_ITEM_ID_DATALIST_ENTRY || + id == POPUP_ITEM_ID_MAC_ACCESS_CONTACTS || + id == POPUP_ITEM_ID_SCAN_CREDIT_CARD; } void AutofillPopupControllerImpl::SetValues( - const std::vector& names, - const std::vector& subtexts, - const std::vector& icons, - const std::vector& identifiers) { - names_ = names; - full_names_ = names; - subtexts_ = subtexts; - icons_ = icons; - identifiers_ = identifiers; + const std::vector& suggestions) { + suggestions_ = suggestions; + elided_values_.resize(suggestions.size()); + elided_labels_.resize(suggestions.size()); + for (size_t i = 0; i < suggestions.size(); i++) { + elided_values_[i] = suggestions[i].value; + elided_labels_[i] = suggestions[i].label; + } } void AutofillPopupControllerImpl::ShowView() { @@ -567,18 +586,17 @@ void AutofillPopupControllerImpl::ShowView() { void AutofillPopupControllerImpl::InvalidateRow(size_t row) { DCHECK(0 <= row); - DCHECK(row < identifiers_.size()); + DCHECK(row < suggestions_.size()); view_->InvalidateRow(row); } #if !defined(OS_ANDROID) int AutofillPopupControllerImpl::GetDesiredPopupWidth() const { int popup_width = controller_common_->RoundedElementBounds().width(); - DCHECK_EQ(names().size(), subtexts().size()); - for (size_t i = 0; i < names().size(); ++i) { + for (size_t i = 0; i < GetLineCount(); ++i) { int row_size = - gfx::GetStringWidth(names()[i], name_font_list_) + - gfx::GetStringWidth(subtexts()[i], subtext_font_list_) + + gfx::GetStringWidth(GetElidedValueAt(i), value_font_list_) + + gfx::GetStringWidth(GetElidedLabelAt(i), label_font_list_) + RowWidthWithoutText(i); popup_width = std::max(popup_width, row_size); @@ -590,8 +608,8 @@ int AutofillPopupControllerImpl::GetDesiredPopupWidth() const { int AutofillPopupControllerImpl::GetDesiredPopupHeight() const { int popup_height = 2 * kPopupBorderThickness; - for (size_t i = 0; i < identifiers().size(); ++i) { - popup_height += GetRowHeightFromId(identifiers()[i]); + for (size_t i = 0; i < suggestions_.size(); ++i) { + popup_height += GetRowHeightFromId(suggestions_[i].frontend_id); } return popup_height; @@ -600,13 +618,14 @@ int AutofillPopupControllerImpl::GetDesiredPopupHeight() const { int AutofillPopupControllerImpl::RowWidthWithoutText(int row) const { int row_size = kEndPadding; - if (!subtexts_[row].empty()) + if (!elided_labels_[row].empty()) row_size += kNamePadding; // Add the Autofill icon size, if required. - if (!icons_[row].empty()) { + const base::string16& icon = suggestions_[row].icon; + if (!icon.empty()) { int icon_width = ui::ResourceBundle::GetSharedInstance().GetImageNamed( - GetIconResourceID(icons_[row])).Width(); + GetIconResourceID(icon)).Width(); row_size += icon_width + kIconPadding; } @@ -620,11 +639,10 @@ int AutofillPopupControllerImpl::RowWidthWithoutText(int row) const { } void AutofillPopupControllerImpl::UpdatePopupBounds() { - int popup_required_width = GetDesiredPopupWidth(); + int popup_width = GetDesiredPopupWidth(); int popup_height = GetDesiredPopupHeight(); - popup_bounds_ = controller_common_->GetPopupBounds(popup_height, - popup_required_width); + popup_bounds_ = controller_common_->GetPopupBounds(popup_width, popup_height); } #endif // !defined(OS_ANDROID) @@ -638,11 +656,9 @@ void AutofillPopupControllerImpl::ClearState() { popup_bounds_ = gfx::Rect(); - names_.clear(); - subtexts_.clear(); - icons_.clear(); - identifiers_.clear(); - full_names_.clear(); + suggestions_.clear(); + elided_values_.clear(); + elided_labels_.clear(); selected_line_ = kNoSelection; } diff --git a/src/browser/autofill_popup_controller_impl.h b/src/browser/autofill_popup_controller_impl.h index 899c50b145..8e84b6cd6e 100644 --- a/src/browser/autofill_popup_controller_impl.h +++ b/src/browser/autofill_popup_controller_impl.h @@ -12,8 +12,8 @@ #include "chrome/browser/ui/autofill/autofill_popup_controller.h" #include "chrome/browser/ui/autofill/popup_controller_common.h" #include "ui/gfx/font_list.h" -#include "ui/gfx/rect.h" -#include "ui/gfx/rect_f.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rect_f.h" namespace autofill { @@ -38,10 +38,7 @@ class AutofillPopupControllerImpl : public AutofillPopupController { base::i18n::TextDirection text_direction); // Shows the popup, or updates the existing popup with the given values. - void Show(const std::vector& names, - const std::vector& subtexts, - const std::vector& icons, - const std::vector& identifiers); + void Show(const std::vector& suggestions); // Updates the data list values currently shown with the popup. void UpdateDataListValues(const std::vector& values, @@ -49,10 +46,10 @@ class AutofillPopupControllerImpl : public AutofillPopupController { // Hides the popup and destroys the controller. This also invalidates // |delegate_|. - virtual void Hide() OVERRIDE; + void Hide() override; // Invoked when the view was destroyed by by someone other than this class. - virtual void ViewDestroyed() OVERRIDE; + void ViewDestroyed() override; bool HandleKeyPressEvent(const content::NativeWebKeyboardEvent& event); @@ -70,37 +67,33 @@ class AutofillPopupControllerImpl : public AutofillPopupController { gfx::NativeView container_view, const gfx::RectF& element_bounds, base::i18n::TextDirection text_direction); - virtual ~AutofillPopupControllerImpl(); + ~AutofillPopupControllerImpl() override; // AutofillPopupController implementation. - virtual void UpdateBoundsAndRedrawPopup() OVERRIDE; - virtual void SetSelectionAtPoint(const gfx::Point& point) OVERRIDE; - virtual bool AcceptSelectedLine() OVERRIDE; - virtual void SelectionCleared() OVERRIDE; - virtual bool ShouldRepostEvent(const ui::MouseEvent& event) OVERRIDE; - virtual bool ShouldHideOnOutsideClick() const OVERRIDE; - virtual void AcceptSuggestion(size_t index) OVERRIDE; - virtual int GetIconResourceID( - const base::string16& resource_name) const OVERRIDE; - virtual bool CanDelete(size_t index) const OVERRIDE; - virtual bool IsWarning(size_t index) const OVERRIDE; - virtual gfx::Rect GetRowBounds(size_t index) OVERRIDE; - virtual void SetPopupBounds(const gfx::Rect& bounds) OVERRIDE; - virtual const gfx::Rect& popup_bounds() const OVERRIDE; - virtual gfx::NativeView container_view() OVERRIDE; - virtual const gfx::RectF& element_bounds() const OVERRIDE; - virtual bool IsRTL() const OVERRIDE; - - virtual const std::vector& names() const OVERRIDE; - virtual const std::vector& subtexts() const OVERRIDE; - virtual const std::vector& icons() const OVERRIDE; - virtual const std::vector& identifiers() const OVERRIDE; + void UpdateBoundsAndRedrawPopup() override; + void SetSelectionAtPoint(const gfx::Point& point) override; + bool AcceptSelectedLine() override; + void SelectionCleared() override; + void AcceptSuggestion(size_t index) override; + int GetIconResourceID(const base::string16& resource_name) const override; + bool CanDelete(size_t index) const override; + bool IsWarning(size_t index) const override; + gfx::Rect GetRowBounds(size_t index) override; + void SetPopupBounds(const gfx::Rect& bounds) override; + const gfx::Rect& popup_bounds() const override; + gfx::NativeView container_view() override; + const gfx::RectF& element_bounds() const override; + bool IsRTL() const override; + + size_t GetLineCount() const override; + const autofill::Suggestion& GetSuggestionAt(size_t row) const override; + const base::string16& GetElidedValueAt(size_t row) const override; + const base::string16& GetElidedLabelAt(size_t row) const override; #if !defined(OS_ANDROID) - virtual const gfx::FontList& GetNameFontListForRow( - size_t index) const OVERRIDE; - virtual const gfx::FontList& subtext_font_list() const OVERRIDE; + const gfx::FontList& GetValueFontListForRow(size_t index) const override; + const gfx::FontList& GetLabelFontList() const override; #endif - virtual int selected_line() const OVERRIDE; + int selected_line() const override; content::WebContents* web_contents(); @@ -130,10 +123,7 @@ class AutofillPopupControllerImpl : public AutofillPopupController { // Set the Autofill entry values. Exposed to allow tests to set these values // without showing the popup. - void SetValues(const std::vector& names, - const std::vector& subtexts, - const std::vector& icons, - const std::vector& identifier); + void SetValues(const std::vector& suggestions); AutofillPopupView* view() { return view_; } @@ -182,29 +172,25 @@ class AutofillPopupControllerImpl : public AutofillPopupController { base::i18n::TextDirection text_direction_; // The current Autofill query values. - std::vector names_; - std::vector subtexts_; - std::vector icons_; - std::vector identifiers_; + std::vector suggestions_; - // Since names_ can be elided to ensure that it fits on the screen, we need to - // keep an unelided copy of the names to be able to pass to the delegate. - std::vector full_names_; + // Elided values and labels corresponding to the suggestions_ vector to + // ensure that it fits on the screen. + std::vector elided_values_; + std::vector elided_labels_; #if !defined(OS_ANDROID) // The fonts for the popup text. - gfx::FontList name_font_list_; - gfx::FontList subtext_font_list_; + gfx::FontList value_font_list_; + gfx::FontList label_font_list_; gfx::FontList warning_font_list_; + gfx::FontList title_font_list_; #endif // The line that is currently selected by the user. // |kNoSelection| indicates that no line is currently selected. int selected_line_; - // Whether the popup view should hide on mouse presses outside of it. - bool hide_on_outside_click_; - base::WeakPtrFactory weak_ptr_factory_; }; diff --git a/src/browser/autofill_popup_view_bridge.h b/src/browser/autofill_popup_view_bridge.h index 09119f4a97..3cf9c36f74 100644 --- a/src/browser/autofill_popup_view_bridge.h +++ b/src/browser/autofill_popup_view_bridge.h @@ -9,41 +9,40 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/mac/scoped_nsobject.h" #include "chrome/browser/ui/autofill/autofill_popup_view.h" +#include "chrome/browser/ui/cocoa/autofill/autofill_popup_view_cocoa.h" @class AutofillPopupViewCocoa; @class NSWindow; namespace autofill { -class AutofillPopupController; +class AutofillPopupViewDelegate; -// Mac implementation for AutofillPopupView interface. -// Serves as a bridge to the Objective-C class AutofillPopupViewCocoa which -// actually implements the view. +// Mac implementation of the AutofillPopupView interface. +// Serves as a bridge to an instance of the Objective-C class which actually +// implements the view. class AutofillPopupViewBridge : public AutofillPopupView { public: explicit AutofillPopupViewBridge(AutofillPopupController* controller); private: - virtual ~AutofillPopupViewBridge(); + ~AutofillPopupViewBridge() override; // AutofillPopupView implementation. - virtual void Hide() OVERRIDE; - virtual void Show() OVERRIDE; - virtual void InvalidateRow(size_t row) OVERRIDE; - virtual void UpdateBoundsAndRedrawPopup() OVERRIDE; + void Hide() override; + void Show() override; + void InvalidateRow(size_t row) override; + void UpdateBoundsAndRedrawPopup() override; - // Set the initial bounds of the popup to show, including the placement - // of it. + // Set the initial bounds of the popup, including its placement. void SetInitialBounds(); - // The controller for this view. - AutofillPopupController* controller_; // Weak reference. + // The native Cocoa view. + base::scoped_nsobject view_; - // The native Cocoa window and view. - NSWindow* window_; // Weak reference, owns itself. - AutofillPopupViewCocoa* view_; // Weak reference, owned by the |window_|. + AutofillPopupController* controller_; // Weak. DISALLOW_COPY_AND_ASSIGN(AutofillPopupViewBridge); }; diff --git a/src/browser/autofill_popup_view_bridge.mm b/src/browser/autofill_popup_view_bridge.mm index dc6e59a5fe..da5d8d927e 100644 --- a/src/browser/autofill_popup_view_bridge.mm +++ b/src/browser/autofill_popup_view_bridge.mm @@ -4,41 +4,26 @@ #import -#include "content/nw/src/browser/autofill_popup_view_bridge.h" +#include "chrome/browser/ui/cocoa/autofill/autofill_popup_view_bridge.h" #include "base/logging.h" -#include "content/nw/src/browser/autofill_popup_controller.h" -#import "content/nw/src/browser/autofill_popup_view_cocoa.h" -#include "ui/base/cocoa/window_size_constants.h" -#include "ui/gfx/rect.h" +#include "chrome/browser/ui/autofill/autofill_popup_controller.h" +#include "chrome/browser/ui/autofill/autofill_popup_view_delegate.h" +#import "chrome/browser/ui/cocoa/autofill/autofill_popup_view_cocoa.h" namespace autofill { AutofillPopupViewBridge::AutofillPopupViewBridge( AutofillPopupController* controller) : controller_(controller) { - window_ = - [[NSWindow alloc] initWithContentRect:ui::kWindowSizeDeterminedLater - styleMask:NSBorderlessWindowMask - backing:NSBackingStoreBuffered - defer:YES]; - // Telling Cocoa that the window is opaque enables some drawing optimizations. - [window_ setOpaque:YES]; - - view_ = [[[AutofillPopupViewCocoa alloc] - initWithController:controller_ - frame:NSZeroRect] autorelease]; - [window_ setContentView:view_]; + view_.reset( + [[AutofillPopupViewCocoa alloc] initWithController:controller + frame:NSZeroRect]); } AutofillPopupViewBridge::~AutofillPopupViewBridge() { [view_ controllerDestroyed]; - - // Remove the child window before closing, otherwise it can mess up - // display ordering. - [[window_ parentWindow] removeChildWindow:window_]; - - [window_ close]; + [view_ hidePopup]; } void AutofillPopupViewBridge::Hide() { @@ -46,31 +31,15 @@ } void AutofillPopupViewBridge::Show() { - UpdateBoundsAndRedrawPopup(); - [[controller_->container_view() window] addChildWindow:window_ - ordered:NSWindowAbove]; + [view_ showPopup]; } void AutofillPopupViewBridge::InvalidateRow(size_t row) { - NSRect dirty_rect = - NSRectFromCGRect(controller_->GetRowBounds(row).ToCGRect()); - [view_ setNeedsDisplayInRect:dirty_rect]; + [view_ invalidateRow:row]; } void AutofillPopupViewBridge::UpdateBoundsAndRedrawPopup() { - NSRect frame = NSRectFromCGRect(controller_->popup_bounds().ToCGRect()); - - // Flip coordinates back into Cocoa-land. The controller's platform-neutral - // coordinate space places the origin at the top-left of the first screen, - // whereas Cocoa's coordinate space expects the origin to be at the - // bottom-left of this same screen. - NSScreen* screen = [[NSScreen screens] objectAtIndex:0]; - frame.origin.y = NSMaxY([screen frame]) - NSMaxY(frame); - - // TODO(isherman): The view should support scrolling if the popup gets too - // big to fit on the screen. - [window_ setFrame:frame display:YES]; - [view_ setNeedsDisplay:YES]; + [view_ updateBoundsAndRedrawPopup]; } AutofillPopupView* AutofillPopupView::Create( diff --git a/src/browser/autofill_popup_view_cocoa.h b/src/browser/autofill_popup_view_cocoa.h index c51a6ed8d0..b140d05471 100644 --- a/src/browser/autofill_popup_view_cocoa.h +++ b/src/browser/autofill_popup_view_cocoa.h @@ -2,19 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_UI_COCOA_AUTOFILL_AUTOFILL_POPUP_CONTENT_VIEW_H_ -#define CHROME_BROWSER_UI_COCOA_AUTOFILL_AUTOFILL_POPUP_CONTENT_VIEW_H_ +#ifndef CHROME_BROWSER_UI_COCOA_AUTOFILL_AUTOFILL_POPUP_VIEW_COCOA_H_ +#define CHROME_BROWSER_UI_COCOA_AUTOFILL_AUTOFILL_POPUP_VIEW_COCOA_H_ #import -#import "ui/base/cocoa/base_view.h" +#import "chrome/browser/ui/cocoa/autofill/autofill_popup_base_view_cocoa.h" namespace autofill { class AutofillPopupController; } // namespace autofill // Draws the native Autofill popup view on Mac. -@interface AutofillPopupViewCocoa : BaseView { +@interface AutofillPopupViewCocoa : AutofillPopupBaseViewCocoa { @private // The cross-platform controller for this view. __weak autofill::AutofillPopupController* controller_; @@ -28,6 +28,8 @@ class AutofillPopupController; // destroyed. - (void)controllerDestroyed; +- (void)invalidateRow:(size_t)row; + @end -#endif // CHROME_BROWSER_UI_COCOA_AUTOFILL_AUTOFILL_POPUP_CONTENT_VIEW_H_ +#endif // CHROME_BROWSER_UI_COCOA_AUTOFILL_AUTOFILL_POPUP_VIEW_COCOA_H_ diff --git a/src/browser/autofill_popup_view_cocoa.mm b/src/browser/autofill_popup_view_cocoa.mm index 387e6ef82e..a8b0e9559d 100644 --- a/src/browser/autofill_popup_view_cocoa.mm +++ b/src/browser/autofill_popup_view_cocoa.mm @@ -2,73 +2,64 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "content/nw/src/browser/autofill_popup_view_cocoa.h" +#import "chrome/browser/ui/cocoa/autofill/autofill_popup_view_cocoa.h" #include "base/logging.h" #include "base/strings/sys_string_conversions.h" -#include "content/nw/src/browser/autofill_popup_controller.h" +#include "chrome/browser/ui/autofill/autofill_popup_controller.h" #include "chrome/browser/ui/autofill/popup_constants.h" -#include "content/nw/src/browser/autofill_popup_view_bridge.h" +#include "chrome/browser/ui/cocoa/autofill/autofill_popup_view_bridge.h" #include "components/autofill/core/browser/popup_item_ids.h" -#include "grit/ui_resources.h" +#include "components/autofill/core/browser/suggestion.h" +#include "ui/base/cocoa/window_size_constants.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/font_list.h" #include "ui/gfx/image/image.h" -#include "ui/gfx/point.h" -#include "ui/gfx/rect.h" using autofill::AutofillPopupView; -namespace { - -NSColor* BackgroundColor() { - return [NSColor whiteColor]; -} - -// The color of the border around the popup. -NSColor* BorderColor() { - return [NSColor colorForControlTint:[NSColor currentControlTint]]; -} - -NSColor* SeparatorColor() { - return [NSColor colorWithCalibratedWhite:220 / 255.0 alpha:1]; -} - -NSColor* HighlightColor() { - return [NSColor selectedControlColor]; -} - -NSColor* NameColor() { - return [NSColor blackColor]; -} - -NSColor* WarningColor() { - return [NSColor grayColor]; -} - -NSColor* SubtextColor() { - return [NSColor grayColor]; -} - -} // namespace +@interface AutofillPopupViewCocoa () #pragma mark - #pragma mark Private methods -@interface AutofillPopupViewCocoa () - -// Draws a thin separator in the popup UI. -- (void)drawSeparatorWithBounds:(NSRect)bounds; - // Draws an Autofill suggestion in the given |bounds|, labeled with the given // |name| and |subtext| hint. If the suggestion |isSelected|, then it is drawn // with a highlight. |index| determines the font to use, as well as the icon, -// if the row requires it -- such as for credit cards. +// if the row requires it -- such as for credit cards. |imageFirst| indicates +// whether the image should be drawn before the name, and with the same +// alignment, or whether it should be drawn afterwards, with the opposite +// alignment. - (void)drawSuggestionWithName:(NSString*)name subtext:(NSString*)subtext index:(size_t)index bounds:(NSRect)bounds - selected:(BOOL)isSelected; + selected:(BOOL)isSelected + imageFirst:(BOOL)imageFirst + textYOffset:(CGFloat)textYOffset; + +// This comment block applies to all three draw* methods that follow. +// If |rightAlign| == YES. +// Draws the widget with right border aligned to |x|. +// Returns the x value of left border of the widget. +// If |rightAlign| == NO. +// Draws the widget with left border aligned to |x|. +// Returns the x value of right border of the widget. +- (CGFloat)drawName:(NSString*)name + atX:(CGFloat)x + index:(size_t)index + rightAlign:(BOOL)rightAlign + bounds:(NSRect)bounds + textYOffset:(CGFloat)textYOffset; +- (CGFloat)drawIconAtIndex:(size_t)index + atX:(CGFloat)x + rightAlign:(BOOL)rightAlign + bounds:(NSRect)bounds; +- (CGFloat)drawSubtext:(NSString*)subtext + atX:(CGFloat)x + rightAlign:(BOOL)rightAlign + bounds:(NSRect)bounds + textYOffset:(CGFloat)textYOffset; // Returns the icon for the row with the given |index|, or |nil| if there is // none. @@ -88,7 +79,7 @@ - (id)initWithFrame:(NSRect)frame { - (id)initWithController:(autofill::AutofillPopupController*)controller frame:(NSRect)frame { - self = [super initWithFrame:frame]; + self = [super initWithDelegate:controller frame:frame]; if (self) controller_ = controller; @@ -98,95 +89,49 @@ - (id)initWithController:(autofill::AutofillPopupController*)controller #pragma mark - #pragma mark NSView implementation: -// A slight optimization for drawing: -// https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaViewsGuide/Optimizing/Optimizing.html -- (BOOL)isOpaque { - return YES; -} - -- (BOOL)isFlipped { - // Flipped so that it's easier to share controller logic with other OSes. - return YES; -} - - (void)drawRect:(NSRect)dirtyRect { // If the view is in the process of being destroyed, don't bother drawing. if (!controller_) return; - // Draw the popup's background and border. - // The inset is needed since the border is centered on the |path|. - // TODO(isherman): We should consider using asset-based drawing for the - // border, creating simple bitmaps for the view's border and background, and - // drawing them using NSDrawNinePartImage(). - CGFloat inset = autofill::kPopupBorderThickness / 2.0; - NSRect borderRect = NSInsetRect([self bounds], inset, inset); - NSBezierPath* path = [NSBezierPath bezierPathWithRect:borderRect]; - [BackgroundColor() setFill]; - [path fill]; - [path setLineWidth:autofill::kPopupBorderThickness]; - [BorderColor() setStroke]; - [path stroke]; - - for (size_t i = 0; i < controller_->names().size(); ++i) { + [self drawBackgroundAndBorder]; + + for (size_t i = 0; i < controller_->GetLineCount(); ++i) { // Skip rows outside of the dirty rect. NSRect rowBounds = NSRectFromCGRect(controller_->GetRowBounds(i).ToCGRect()); if (!NSIntersectsRect(rowBounds, dirtyRect)) continue; - if (controller_->identifiers()[i] == autofill::POPUP_ITEM_ID_SEPARATOR) { + if (controller_->GetSuggestionAt(i).frontend_id == autofill::POPUP_ITEM_ID_SEPARATOR) { [self drawSeparatorWithBounds:rowBounds]; - } else { - NSString* name = SysUTF16ToNSString(controller_->names()[i]); - NSString* subtext = SysUTF16ToNSString(controller_->subtexts()[i]); - BOOL isSelected = static_cast(i) == controller_->selected_line(); - [self drawSuggestionWithName:name - subtext:subtext - index:i - bounds:rowBounds - selected:isSelected]; + continue; } - } -} -- (void)mouseUp:(NSEvent*)theEvent { - // If the view is in the process of being destroyed, abort. - if (!controller_) - return; - - NSPoint location = [self convertPoint:[theEvent locationInWindow] - fromView:nil]; + // Additional offset applied to the text in the vertical direction. + CGFloat textYOffset = 0; + BOOL imageFirst = NO; + if (controller_->GetSuggestionAt(i).frontend_id == + autofill::POPUP_ITEM_ID_MAC_ACCESS_CONTACTS) { + // Due to the weighting of the asset used for this autofill entry, the + // text needs to be bumped up by 1 pt to make it look vertically aligned. + textYOffset = -1; + imageFirst = YES; + } - if (NSPointInRect(location, [self bounds])) { - controller_->SetSelectionAtPoint(gfx::Point(NSPointToCGPoint(location))); - controller_->AcceptSelectedLine(); + NSString* name = SysUTF16ToNSString(controller_->GetElidedValueAt(i)); + NSString* subtext = SysUTF16ToNSString(controller_->GetElidedLabelAt(i)); + BOOL isSelected = static_cast(i) == controller_->selected_line(); + [self drawSuggestionWithName:name + subtext:subtext + index:i + bounds:rowBounds + selected:isSelected + imageFirst:imageFirst + textYOffset:textYOffset]; } } -- (void)mouseMoved:(NSEvent*)theEvent { - // If the view is in the process of being destroyed, abort. - if (!controller_) - return; - - NSPoint location = [self convertPoint:[theEvent locationInWindow] - fromView:nil]; - - controller_->SetSelectionAtPoint(gfx::Point(NSPointToCGPoint(location))); -} - -- (void)mouseDragged:(NSEvent*)theEvent { - [self mouseMoved:theEvent]; -} - -- (void)mouseExited:(NSEvent*)theEvent { - // If the view is in the process of being destroyed, abort. - if (!controller_) - return; - - controller_->SelectionCleared(); -} - #pragma mark - #pragma mark Public API: @@ -194,58 +139,94 @@ - (void)controllerDestroyed { // Since the |controller_| either already has been destroyed or is about to // be, about the only thing we can safely do with it is to null it out. controller_ = NULL; + [super delegateDestroyed]; +} + +- (void)invalidateRow:(size_t)row { + NSRect dirty_rect = + NSRectFromCGRect(controller_->GetRowBounds(row).ToCGRect()); + [self setNeedsDisplayInRect:dirty_rect]; } #pragma mark - #pragma mark Private API: -- (void)drawSeparatorWithBounds:(NSRect)bounds { - [SeparatorColor() set]; - [NSBezierPath fillRect:bounds]; -} - - (void)drawSuggestionWithName:(NSString*)name subtext:(NSString*)subtext index:(size_t)index bounds:(NSRect)bounds - selected:(BOOL)isSelected { + selected:(BOOL)isSelected + imageFirst:(BOOL)imageFirst + textYOffset:(CGFloat)textYOffset { // If this row is selected, highlight it. if (isSelected) { - [HighlightColor() set]; + [[self highlightColor] set]; [NSBezierPath fillRect:bounds]; } BOOL isRTL = controller_->IsRTL(); + // The X values of the left and right borders of the autofill widget. + CGFloat leftX = NSMinX(bounds) + AutofillPopupView::kEndPadding; + CGFloat rightX = NSMaxX(bounds) - AutofillPopupView::kEndPadding; + + // Draw left side if isRTL == NO, right side if isRTL == YES. + CGFloat x = isRTL ? rightX : leftX; + if (imageFirst) + x = [self drawIconAtIndex:index atX:x rightAlign:isRTL bounds:bounds]; + [self drawName:name + atX:x + index:index + rightAlign:isRTL + bounds:bounds + textYOffset:textYOffset]; + + // Draw right side if isRTL == NO, left side if isRTL == YES. + x = isRTL ? leftX : rightX; + if (!imageFirst) + x = [self drawIconAtIndex:index atX:x rightAlign:!isRTL bounds:bounds]; + [self drawSubtext:subtext + atX:x + rightAlign:!isRTL + bounds:bounds + textYOffset:textYOffset]; +} + +- (CGFloat)drawName:(NSString*)name + atX:(CGFloat)x + index:(size_t)index + rightAlign:(BOOL)rightAlign + bounds:(NSRect)bounds + textYOffset:(CGFloat)textYOffset { NSColor* nameColor = - controller_->IsWarning(index) ? WarningColor() : NameColor(); + controller_->IsWarning(index) ? [self warningColor] : [self nameColor]; NSDictionary* nameAttributes = [NSDictionary dictionaryWithObjectsAndKeys: - controller_->GetNameFontListForRow(index).GetPrimaryFont(). + controller_->GetValueFontListForRow(index).GetPrimaryFont(). GetNativeFont(), NSFontAttributeName, nameColor, NSForegroundColorAttributeName, nil]; NSSize nameSize = [name sizeWithAttributes:nameAttributes]; - CGFloat x = bounds.origin.x + - (isRTL ? - bounds.size.width - AutofillPopupView::kEndPadding - nameSize.width : - AutofillPopupView::kEndPadding); + x -= rightAlign ? nameSize.width : 0; CGFloat y = bounds.origin.y + (bounds.size.height - nameSize.height) / 2; + y += textYOffset; [name drawAtPoint:NSMakePoint(x, y) withAttributes:nameAttributes]; - // The x-coordinate will be updated as each element is drawn. - x = bounds.origin.x + - (isRTL ? - AutofillPopupView::kEndPadding : - bounds.size.width - AutofillPopupView::kEndPadding); + x += rightAlign ? 0 : nameSize.width; + return x; +} - // Draw the Autofill icon, if one exists. +- (CGFloat)drawIconAtIndex:(size_t)index + atX:(CGFloat)x + rightAlign:(BOOL)rightAlign + bounds:(NSRect)bounds { NSImage* icon = [self iconAtIndex:index]; - if (icon) { - NSSize iconSize = [icon size]; - x += isRTL ? 0 : -iconSize.width; - y = bounds.origin.y + (bounds.size.height - iconSize.height) / 2; + if (!icon) + return x; + NSSize iconSize = [icon size]; + x -= rightAlign ? iconSize.width : 0; + CGFloat y = bounds.origin.y + (bounds.size.height - iconSize.height) / 2; [icon drawInRect:NSMakeRect(x, y, iconSize.width, iconSize.height) fromRect:NSZeroRect operation:NSCompositeSourceOver @@ -253,29 +234,38 @@ - (void)drawSuggestionWithName:(NSString*)name respectFlipped:YES hints:nil]; - x += isRTL ? - iconSize.width + AutofillPopupView::kIconPadding : - -AutofillPopupView::kIconPadding; - } + x += rightAlign ? -AutofillPopupView::kIconPadding + : iconSize.width + AutofillPopupView::kIconPadding; + return x; +} - // Draw the subtext. +- (CGFloat)drawSubtext:(NSString*)subtext + atX:(CGFloat)x + rightAlign:(BOOL)rightAlign + bounds:(NSRect)bounds + textYOffset:(CGFloat)textYOffset { NSDictionary* subtextAttributes = [NSDictionary dictionaryWithObjectsAndKeys: - controller_->subtext_font_list().GetPrimaryFont().GetNativeFont(), - NSFontAttributeName, SubtextColor(), NSForegroundColorAttributeName, + controller_->GetLabelFontList().GetPrimaryFont().GetNativeFont(), + NSFontAttributeName, + [self subtextColor], + NSForegroundColorAttributeName, nil]; NSSize subtextSize = [subtext sizeWithAttributes:subtextAttributes]; - x += isRTL ? 0 : -subtextSize.width; - y = bounds.origin.y + (bounds.size.height - subtextSize.height) / 2; + x -= rightAlign ? subtextSize.width : 0; + CGFloat y = bounds.origin.y + (bounds.size.height - subtextSize.height) / 2; + y += textYOffset; [subtext drawAtPoint:NSMakePoint(x, y) withAttributes:subtextAttributes]; + x += rightAlign ? 0 : subtextSize.width; + return x; } - (NSImage*)iconAtIndex:(size_t)index { - if (controller_->icons()[index].empty()) + if (controller_->GetSuggestionAt(index).icon.empty()) return nil; - int iconId = controller_->GetIconResourceID(controller_->icons()[index]); + int iconId = controller_->GetIconResourceID(controller_->GetSuggestionAt(index).icon); DCHECK_NE(-1, iconId); ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); diff --git a/src/browser/autofill_popup_view_delegate.h b/src/browser/autofill_popup_view_delegate.h index 15836cb260..e4320fd3f6 100644 --- a/src/browser/autofill_popup_view_delegate.h +++ b/src/browser/autofill_popup_view_delegate.h @@ -10,15 +10,12 @@ namespace gfx { class Point; class Rect; -} - -namespace ui { -class MouseEvent; +class RectF; } namespace autofill { -// Base class for Controllers of Autofill style popups. This interface is +// Base class for Controllers of Autofill-style popups. This interface is // used by the relevant views to communicate with the controller. class AutofillPopupViewDelegate { public: @@ -41,18 +38,18 @@ class AutofillPopupViewDelegate { // out of the popup bounds. virtual void SelectionCleared() = 0; - // Whether |event| should be reposted to the native window management. - virtual bool ShouldRepostEvent(const ui::MouseEvent& event) = 0; - - // Whether the view should be hidden on outside mouse presses. - virtual bool ShouldHideOnOutsideClick() const = 0; - // The actual bounds of the popup. virtual const gfx::Rect& popup_bounds() const = 0; // The view that the form field element sits in. virtual gfx::NativeView container_view() = 0; + // The bounds of the form field element (screen coordinates). + virtual const gfx::RectF& element_bounds() const = 0; + + // If the current popup should be displayed in RTL mode. + virtual bool IsRTL() const = 0; + protected: virtual ~AutofillPopupViewDelegate() {} }; diff --git a/src/browser/autofill_popup_view_views.cc b/src/browser/autofill_popup_view_views.cc index b92c9e92fb..f3cb3507fd 100644 --- a/src/browser/autofill_popup_view_views.cc +++ b/src/browser/autofill_popup_view_views.cc @@ -2,18 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/nw/src/browser/autofill_popup_view_views.h" +#include "chrome/browser/ui/views/autofill/autofill_popup_view_views.h" -#include "content/nw/src/browser/autofill_popup_controller.h" +#include "chrome/browser/ui/autofill/autofill_popup_controller.h" #include "components/autofill/core/browser/popup_item_ids.h" -#include "grit/ui_resources.h" +#include "components/autofill/core/browser/suggestion.h" #include "ui/base/resource/resource_bundle.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/rect.h" #include "ui/gfx/image/image.h" #include "ui/gfx/native_widget_types.h" -#include "ui/gfx/point.h" -#include "ui/gfx/rect.h" #include "ui/gfx/text_utils.h" #include "ui/views/border.h" #include "ui/views/widget/widget.h" @@ -49,11 +49,12 @@ void AutofillPopupViewViews::OnPaint(gfx::Canvas* canvas) { canvas->DrawColor(kPopupBackground); OnPaintBorder(canvas); - for (size_t i = 0; i < controller_->names().size(); ++i) { + for (size_t i = 0; i < controller_->GetLineCount(); ++i) { gfx::Rect line_rect = controller_->GetRowBounds(i); - if (controller_->identifiers()[i] == POPUP_ITEM_ID_SEPARATOR) { - canvas->DrawRect(line_rect, kItemTextColor); + if (controller_->GetSuggestionAt(i).frontend_id == + POPUP_ITEM_ID_SEPARATOR) { + canvas->FillRect(line_rect, kItemTextColor); } else { DrawAutofillEntry(canvas, i, line_rect); } @@ -72,14 +73,14 @@ void AutofillPopupViewViews::DrawAutofillEntry(gfx::Canvas* canvas, const bool is_rtl = controller_->IsRTL(); const int value_text_width = - gfx::GetStringWidth(controller_->names()[index], - controller_->GetNameFontListForRow(index)); + gfx::GetStringWidth(controller_->GetElidedValueAt(index), + controller_->GetValueFontListForRow(index)); const int value_content_x = is_rtl ? entry_rect.width() - value_text_width - kEndPadding : kEndPadding; canvas->DrawStringRectWithFlags( - controller_->names()[index], - controller_->GetNameFontListForRow(index), + controller_->GetElidedValueAt(index), + controller_->GetValueFontListForRow(index), controller_->IsWarning(index) ? kWarningTextColor : kValueTextColor, gfx::Rect(value_content_x, entry_rect.y(), @@ -93,8 +94,9 @@ void AutofillPopupViewViews::DrawAutofillEntry(gfx::Canvas* canvas, // Draw the Autofill icon, if one exists ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); int row_height = controller_->GetRowBounds(index).height(); - if (!controller_->icons()[index].empty()) { - int icon = controller_->GetIconResourceID(controller_->icons()[index]); + if (!controller_->GetSuggestionAt(index).icon.empty()) { + int icon = controller_->GetIconResourceID( + controller_->GetSuggestionAt(index).icon); DCHECK_NE(-1, icon); const gfx::ImageSkia* image = rb.GetImageSkiaNamed(icon); int icon_y = entry_rect.y() + (row_height - image->height()) / 2; @@ -106,20 +108,20 @@ void AutofillPopupViewViews::DrawAutofillEntry(gfx::Canvas* canvas, x_align_left += is_rtl ? image->width() + kIconPadding : -kIconPadding; } - // Draw the name text. - const int subtext_width = - gfx::GetStringWidth(controller_->subtexts()[index], - controller_->subtext_font_list()); + // Draw the label text. + const int label_width = + gfx::GetStringWidth(controller_->GetElidedLabelAt(index), + controller_->GetLabelFontList()); if (!is_rtl) - x_align_left -= subtext_width; + x_align_left -= label_width; canvas->DrawStringRectWithFlags( - controller_->subtexts()[index], - controller_->subtext_font_list(), + controller_->GetElidedLabelAt(index), + controller_->GetLabelFontList(), kItemTextColor, gfx::Rect(x_align_left, entry_rect.y(), - subtext_width, + label_width, entry_rect.height()), gfx::Canvas::TEXT_ALIGN_CENTER); } diff --git a/src/browser/autofill_popup_view_views.h b/src/browser/autofill_popup_view_views.h index a10f9d1a3f..cd2d95e35d 100644 --- a/src/browser/autofill_popup_view_views.h +++ b/src/browser/autofill_popup_view_views.h @@ -5,8 +5,8 @@ #ifndef CHROME_BROWSER_UI_VIEWS_AUTOFILL_AUTOFILL_POPUP_VIEW_VIEWS_H_ #define CHROME_BROWSER_UI_VIEWS_AUTOFILL_AUTOFILL_POPUP_VIEW_VIEWS_H_ -#include "content/nw/src/browser/autofill_popup_view.h" -#include "content/nw/src/browser/autofill_popup_base_view.h" +#include "chrome/browser/ui/autofill/autofill_popup_view.h" +#include "chrome/browser/ui/views/autofill/autofill_popup_base_view.h" class AutofillPopupController; @@ -23,16 +23,16 @@ class AutofillPopupViewViews : public AutofillPopupBaseView, views::Widget* observing_widget); private: - virtual ~AutofillPopupViewViews(); + ~AutofillPopupViewViews() override; // AutofillPopupView implementation. - virtual void Show() OVERRIDE; - virtual void Hide() OVERRIDE; - virtual void InvalidateRow(size_t row) OVERRIDE; - virtual void UpdateBoundsAndRedrawPopup() OVERRIDE; + void Show() override; + void Hide() override; + void InvalidateRow(size_t row) override; + void UpdateBoundsAndRedrawPopup() override; // views::Views implementation - virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; + void OnPaint(gfx::Canvas* canvas) override; // Draw the given autofill entry in |entry_rect|. void DrawAutofillEntry(gfx::Canvas* canvas, diff --git a/src/browser/browser_view_layout.cc b/src/browser/browser_view_layout.cc new file mode 100644 index 0000000000..d30d49f22e --- /dev/null +++ b/src/browser/browser_view_layout.cc @@ -0,0 +1,74 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/browser/browser_view_layout.h" +#include "content/nw/src/common/shell_switches.h" + +#include "base/logging.h" + +using views::View; + +namespace nw { + +BrowserViewLayout::BrowserViewLayout() + : menu_bar_(NULL), web_view_(NULL), tool_bar_(NULL) +{ +} + +BrowserViewLayout::~BrowserViewLayout() {} + +void BrowserViewLayout::Layout(View* host) { + if (!host->has_children()) + return; + + int y = 0; + gfx::Size host_size = host->GetContentsBounds().size(); + + if (menu_bar_) { + menu_bar_->SetBounds(0, y, host_size.width(), kMenuHeight); + y += kMenuHeight; + } + + if (tool_bar_) { + int height = tool_bar_->GetPreferredSize().height(); + tool_bar_->SetBounds(0, y, host_size.width(), height); + y += height; + } + + web_view_->SetBounds(0, y, host_size.width(), host_size.height() - y); +} + +gfx::Size BrowserViewLayout::GetPreferredSize(const View* host) const { + if (!host->has_children()) + return gfx::Size(); + + gfx::Rect rect(web_view_->GetPreferredSize()); + rect.Inset(-host->GetInsets()); + if (menu_bar_) + rect.Inset(0, 0, 0, -kMenuHeight); + + if (tool_bar_) + rect.Inset(0, 0, 0, -tool_bar_->GetPreferredSize().height()); + + return rect.size(); +} + +int BrowserViewLayout::GetPreferredHeightForWidth(const View* host, int width) const { + if (!host->has_children()) + return 0; + + const gfx::Insets insets = host->GetInsets(); + int ret = web_view_->GetHeightForWidth(width - insets.width()) + + insets.height(); + + if (menu_bar_) + ret += kMenuHeight; + + if (tool_bar_) + ret += tool_bar_->GetHeightForWidth(width - insets.width()); + + return ret; +} + +} // namespace views diff --git a/src/browser/browser_view_layout.h b/src/browser/browser_view_layout.h new file mode 100644 index 0000000000..e70a25cbc0 --- /dev/null +++ b/src/browser/browser_view_layout.h @@ -0,0 +1,49 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NW_BROWSER_VIEW_LAYOUT_H_ +#define NW_BROWSER_VIEW_LAYOUT_H_ + +#include "base/compiler_specific.h" +#include "ui/views/layout/layout_manager.h" +#include "ui/views/view.h" + +namespace nw { + +/////////////////////////////////////////////////////////////////////////////// +// +// copied from ui/views/layout/FillLayout +// +/////////////////////////////////////////////////////////////////////////////// +class BrowserViewLayout : public views::LayoutManager { + public: + BrowserViewLayout(); + ~BrowserViewLayout() override; + + // Overridden from LayoutManager: + void Layout(views::View* host) override; + gfx::Size GetPreferredSize(const views::View* host) const override; + int GetPreferredHeightForWidth(const views::View* host, + int width) const override; + + void set_menu_bar(views::View* menu_bar) { menu_bar_ = menu_bar; } + views::View* menu_bar() { return menu_bar_; } + + void set_web_view(views::View* web_view) { web_view_ = web_view; } + views::View* web_view() { return web_view_; } + + void set_tool_bar(views::View* tool_bar) { tool_bar_ = tool_bar; } + views::View* tool_bar() { return tool_bar_; } + + private: + views::View* menu_bar_; + views::View* web_view_; + views::View* tool_bar_; + + DISALLOW_COPY_AND_ASSIGN(BrowserViewLayout); +}; + +} // namespace nw + +#endif // NW_BROWSER_VIEW_LAYOUT_H_ diff --git a/src/browser/capture_page_helper.cc b/src/browser/capture_page_helper.cc index a62634c1a1..97943a84d8 100644 --- a/src/browser/capture_page_helper.cc +++ b/src/browser/capture_page_helper.cc @@ -35,7 +35,7 @@ #include "skia/ext/platform_canvas.h" #include "ui/gfx/codec/jpeg_codec.h" #include "ui/gfx/codec/png_codec.h" -#include "ui/gfx/rect.h" +#include "ui/gfx/geometry/rect.h" namespace nw { @@ -88,13 +88,11 @@ void CapturePageHelper::StartCapturePage(const std::string& image_format_str) { gfx::Rect(), view->GetViewBounds().size(), base::Bind(&CapturePageHelper::CopyFromBackingStoreComplete, - this), SkBitmap::kARGB_8888_Config); + this), kN32_SkColorType); } -void CapturePageHelper::CopyFromBackingStoreComplete( - bool succeeded, - const SkBitmap& bitmap) { - if (succeeded) { +void CapturePageHelper::CopyFromBackingStoreComplete(const SkBitmap& bitmap, content::ReadbackResponse response) { + if (response == content::READBACK_SUCCESS) { // Get image from backing store. SendResultFromBitmap(bitmap); return; diff --git a/src/browser/capture_page_helper.h b/src/browser/capture_page_helper.h index 0c76d9e2bc..7b82b225c0 100644 --- a/src/browser/capture_page_helper.h +++ b/src/browser/capture_page_helper.h @@ -24,6 +24,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/readback_types.h" namespace content { class Shell; @@ -64,20 +65,18 @@ class CapturePageHelper : public base::RefCountedThreadSafe, private: CapturePageHelper(const base::WeakPtr& shell); - virtual ~CapturePageHelper(); + ~CapturePageHelper() override; // Internal helpers ---------------------------------------------------------- // Message handler. void OnSnapshot(const SkBitmap& bitmap); - void CopyFromBackingStoreComplete( - bool succeeded, - const SkBitmap& bitmap); + void CopyFromBackingStoreComplete(const SkBitmap& bitmap, content::ReadbackResponse response); void SendResultFromBitmap(const SkBitmap& screen_capture); // content::WebContentsObserver overrides: - virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + bool OnMessageReceived(const IPC::Message& message) override; base::WeakPtr shell_; diff --git a/src/browser/chrome_crash_reporter_client.cc b/src/browser/chrome_crash_reporter_client.cc new file mode 100644 index 0000000000..de5f3ef308 --- /dev/null +++ b/src/browser/chrome_crash_reporter_client.cc @@ -0,0 +1,374 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/app/chrome_crash_reporter_client.h" + +#include "base/atomicops.h" +#include "base/command_line.h" +#include "base/environment.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/path_service.h" +#include "base/strings/safe_sprintf.h" +#include "base/strings/string_split.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_result_codes.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/crash_keys.h" +#include "chrome/common/env_vars.h" +#include "chrome/installer/util/google_update_settings.h" + +#include "content/nw/src/nw_version.h" + +#if defined(OS_WIN) +#include + +#include "base/file_version_info.h" +#include "base/win/registry.h" +#include "chrome/installer/util/google_chrome_sxs_distribution.h" +#include "chrome/installer/util/install_util.h" +#include "chrome/installer/util/util_constants.h" +//#include "policy/policy_constants.h" +#endif + +#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS) +#include "chrome/browser/crash_upload_list.h" +//#include "chrome/common/chrome_version_info_values.h" +#endif + +#if defined(OS_POSIX) +#include "base/debug/dump_without_crashing.h" +#endif + +#if defined(OS_ANDROID) +#include "chrome/common/descriptors_android.h" +#endif + +#if defined(OS_CHROMEOS) +#include "chrome/common/chrome_version_info.h" +#include "chromeos/chromeos_switches.h" +#endif + +namespace chrome { + +namespace { + +#if defined(OS_WIN) +// This is the minimum version of google update that is required for deferred +// crash uploads to work. +const char kMinUpdateVersion[] = "1.3.21.115"; + +// The value name prefix will be of the form {chrome-version}-{pid}-{timestamp} +// (i.e., "#####.#####.#####.#####-########-########") which easily fits into a +// 63 character buffer. +const char kBrowserCrashDumpPrefixTemplate[] = "%s-%08x-%08x"; +const size_t kBrowserCrashDumpPrefixLength = 63; +char g_browser_crash_dump_prefix[kBrowserCrashDumpPrefixLength + 1] = {}; + +// These registry key to which we'll write a value for each crash dump attempt. +HKEY g_browser_crash_dump_regkey = NULL; + +// A atomic counter to make each crash dump value name unique. +base::subtle::Atomic32 g_browser_crash_dump_count = 0; +#endif + +} // namespace + +ChromeCrashReporterClient::ChromeCrashReporterClient() {} + +ChromeCrashReporterClient::~ChromeCrashReporterClient() {} + +void ChromeCrashReporterClient::SetCrashReporterClientIdFromGUID( + const std::string& client_guid) { + crash_keys::SetCrashClientIdFromGUID(client_guid); +} + +#if defined(OS_WIN) +bool ChromeCrashReporterClient::GetAlternativeCrashDumpLocation( + base::FilePath* crash_dir) { + // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate + // location to write breakpad crash dumps can be set. + scoped_ptr env(base::Environment::Create()); + std::string alternate_crash_dump_location; + if (env->GetVar("BREAKPAD_DUMP_LOCATION", &alternate_crash_dump_location)) { + *crash_dir = base::FilePath::FromUTF8Unsafe(alternate_crash_dump_location); + return true; + } + + return false; +} + +void ChromeCrashReporterClient::GetProductNameAndVersion( + const base::FilePath& exe_path, + base::string16* product_name, + base::string16* version, + base::string16* special_build, + base::string16* channel_name) { + DCHECK(product_name); + DCHECK(version); + DCHECK(special_build); + DCHECK(channel_name); + + scoped_ptr version_info( + FileVersionInfo::CreateFileVersionInfo(exe_path)); + + if (version_info.get()) { + // Get the information from the file. + *version = version_info->product_version(); + if (!version_info->is_official_build()) + version->append(base::ASCIIToUTF16("-devel")); + + *product_name = version_info->product_short_name(); + *special_build = version_info->special_build(); + } else { + // No version info found. Make up the values. + *product_name = base::ASCIIToUTF16("Chrome"); + *version = base::ASCIIToUTF16("0.0.0.0-devel"); + } + +#if 0 + GoogleUpdateSettings::GetChromeChannelAndModifiers( + !GetIsPerUserInstall(exe_path), channel_name); +#endif +} + +bool ChromeCrashReporterClient::ShouldShowRestartDialog(base::string16* title, + base::string16* message, + bool* is_rtl_locale) { + scoped_ptr env(base::Environment::Create()); + if (!env->HasVar(env_vars::kShowRestart) || + !env->HasVar(env_vars::kRestartInfo) || + env->HasVar(env_vars::kMetroConnected)) { + return false; + } + + std::string restart_info; + env->GetVar(env_vars::kRestartInfo, &restart_info); + + // The CHROME_RESTART var contains the dialog strings separated by '|'. + // See ChromeBrowserMainPartsWin::PrepareRestartOnCrashEnviroment() + // for details. + std::vector dlg_strings; + base::SplitString(restart_info, '|', &dlg_strings); + + if (dlg_strings.size() < 3) + return false; + + *title = base::UTF8ToUTF16(dlg_strings[0]); + *message = base::UTF8ToUTF16(dlg_strings[1]); + *is_rtl_locale = dlg_strings[2] == env_vars::kRtlLocale; + return true; +} + +bool ChromeCrashReporterClient::AboutToRestart() { + scoped_ptr env(base::Environment::Create()); + if (!env->HasVar(env_vars::kRestartInfo)) + return false; + + env->SetVar(env_vars::kShowRestart, "1"); + return true; +} + +bool ChromeCrashReporterClient::GetDeferredUploadsSupported( + bool is_per_user_install) { +#if 0 + Version update_version = GoogleUpdateSettings::GetGoogleUpdateVersion( + !is_per_user_install); + if (!update_version.IsValid() || + update_version.IsOlderThan(std::string(kMinUpdateVersion))) + return false; +#endif + return true; +} + +bool ChromeCrashReporterClient::GetIsPerUserInstall( + const base::FilePath& exe_path) { + return true; +} + +bool ChromeCrashReporterClient::GetShouldDumpLargerDumps( + bool is_per_user_install) { + return true; +#if 0 + base::string16 channel_name = + GoogleUpdateSettings::GetChromeChannel(!is_per_user_install); + + // Capture more detail in crash dumps for beta and dev channel builds. + return (channel_name == installer::kChromeChannelDev || + channel_name == installer::kChromeChannelBeta || + channel_name == GoogleChromeSxSDistribution::ChannelName()); +#endif +} + +int ChromeCrashReporterClient::GetResultCodeRespawnFailed() { + return chrome::RESULT_CODE_RESPAWN_FAILED; +} + +void ChromeCrashReporterClient::InitBrowserCrashDumpsRegKey() { + DCHECK(g_browser_crash_dump_regkey == NULL); + + base::win::RegKey regkey; + if (regkey.Create(HKEY_CURRENT_USER, + chrome::kBrowserCrashDumpAttemptsRegistryPath, + KEY_ALL_ACCESS) != ERROR_SUCCESS) { + return; + } + + // We use the current process id and the current tick count as a (hopefully) + // unique combination for the crash dump value. There's a small chance that + // across a reboot we might have a crash dump signal written, and the next + // browser process might have the same process id and tick count, but crash + // before consuming the signal (overwriting the signal with an identical one). + // For now, we're willing to live with that risk. + if (base::strings::SafeSPrintf(g_browser_crash_dump_prefix, + kBrowserCrashDumpPrefixTemplate, + "version", + ::GetCurrentProcessId(), + ::GetTickCount()) <= 0) { + NOTREACHED(); + g_browser_crash_dump_prefix[0] = '\0'; + return; + } + + // Hold the registry key in a global for update on crash dump. + g_browser_crash_dump_regkey = regkey.Take(); +} + +void ChromeCrashReporterClient::RecordCrashDumpAttempt(bool is_real_crash) { + // If we're not a browser (or the registry is unavailable to us for some + // reason) then there's nothing to do. + if (g_browser_crash_dump_regkey == NULL) + return; + + // Generate the final value name we'll use (appends the crash number to the + // base value name). + const size_t kMaxValueSize = 2 * kBrowserCrashDumpPrefixLength; + char value_name[kMaxValueSize + 1] = {}; + if (base::strings::SafeSPrintf( + value_name, "%s-%x", g_browser_crash_dump_prefix, + base::subtle::NoBarrier_AtomicIncrement(&g_browser_crash_dump_count, + 1)) > 0) { + DWORD value_dword = is_real_crash ? 1 : 0; + ::RegSetValueExA(g_browser_crash_dump_regkey, value_name, 0, REG_DWORD, + reinterpret_cast(&value_dword), + sizeof(value_dword)); + } +} + +bool ChromeCrashReporterClient::ReportingIsEnforcedByPolicy( + bool* breakpad_enabled) { +// Determine whether configuration management allows loading the crash reporter. +// Since the configuration management infrastructure is not initialized at this +// point, we read the corresponding registry key directly. The return status +// indicates whether policy data was successfully read. If it is true, +// |breakpad_enabled| contains the value set by policy. +#if 0 + base::string16 key_name = + base::UTF8ToUTF16(policy::key::kMetricsReportingEnabled); + DWORD value = 0; + base::win::RegKey hklm_policy_key(HKEY_LOCAL_MACHINE, + policy::kRegistryChromePolicyKey, KEY_READ); + if (hklm_policy_key.ReadValueDW(key_name.c_str(), &value) == ERROR_SUCCESS) { + *breakpad_enabled = value != 0; + return true; + } + + base::win::RegKey hkcu_policy_key(HKEY_CURRENT_USER, + policy::kRegistryChromePolicyKey, KEY_READ); + if (hkcu_policy_key.ReadValueDW(key_name.c_str(), &value) == ERROR_SUCCESS) { + *breakpad_enabled = value != 0; + return true; + } +#endif + return true; +} +#endif // defined(OS_WIN) + +#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS) +void ChromeCrashReporterClient::GetProductNameAndVersion( + const char** product_name, + const char** version) { + DCHECK(product_name); + DCHECK(version); + *product_name = "NW.JS"; + *version = NW_VERSION_STRING; +} + +base::FilePath ChromeCrashReporterClient::GetReporterLogFilename() { + return base::FilePath(CrashUploadList::kReporterLogFilename); +} +#endif + +bool ChromeCrashReporterClient::GetCrashDumpLocation( + base::FilePath* crash_dir) { + // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate + // location to write breakpad crash dumps can be set. + scoped_ptr env(base::Environment::Create()); + std::string alternate_crash_dump_location; + if (env->GetVar("BREAKPAD_DUMP_LOCATION", &alternate_crash_dump_location)) { + base::FilePath crash_dumps_dir_path = + base::FilePath::FromUTF8Unsafe(alternate_crash_dump_location); + PathService::Override(chrome::DIR_CRASH_DUMPS, crash_dumps_dir_path); + } + + return PathService::Get(chrome::DIR_CRASH_DUMPS, crash_dir); +} + +size_t ChromeCrashReporterClient::RegisterCrashKeys() { + // Note: This is not called on Windows because Breakpad is initialized in the + // EXE module, but code that uses crash keys is in the DLL module. + // RegisterChromeCrashKeys() will be called after the DLL is loaded. + return crash_keys::RegisterChromeCrashKeys(); +} + +bool ChromeCrashReporterClient::IsRunningUnattended() { + return true; +} + +bool ChromeCrashReporterClient::GetCollectStatsConsent() { +#if defined(GOOGLE_CHROME_BUILD) + bool is_official_chrome_build = true; +#else + // bool is_official_chrome_build = false; +#endif + +#if defined(OS_CHROMEOS) + bool is_guest_session = CommandLine::ForCurrentProcess()->HasSwitch( + chromeos::switches::kGuestSession); + bool is_stable_channel = + chrome::VersionInfo::GetChannel() == chrome::VersionInfo::CHANNEL_STABLE; + + if (is_guest_session && is_stable_channel) + return false; +#endif // defined(OS_CHROMEOS) + +#if defined(OS_ANDROID) + // TODO(jcivelli): we should not initialize the crash-reporter when it was not + // enabled. Right now if it is disabled we still generate the minidumps but we + // do not upload them. + return is_official_chrome_build; +#else // !defined(OS_ANDROID) + return false; +#endif // defined(OS_ANDROID) +} + +#if defined(OS_ANDROID) +int ChromeCrashReporterClient::GetAndroidMinidumpDescriptor() { + return kAndroidMinidumpDescriptor; +} +#endif + +bool ChromeCrashReporterClient::EnableBreakpadForProcess( + const std::string& process_type) { + return process_type == switches::kRendererProcess || + process_type == switches::kPluginProcess || + process_type == switches::kPpapiPluginProcess || + process_type == switches::kZygoteProcess || + process_type == switches::kGpuProcess; +} + +} // namespace chrome diff --git a/src/browser/chrome_crash_reporter_client.h b/src/browser/chrome_crash_reporter_client.h new file mode 100644 index 0000000000..d3eb42f1f3 --- /dev/null +++ b/src/browser/chrome_crash_reporter_client.h @@ -0,0 +1,76 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_APP_CHROME_CRASH_REPORTER_CLIENT_H_ +#define CHROME_APP_CHROME_CRASH_REPORTER_CLIENT_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "components/crash/app/crash_reporter_client.h" + +namespace chrome { + +class ChromeCrashReporterClient : public crash_reporter::CrashReporterClient { + public: + ChromeCrashReporterClient(); + ~ChromeCrashReporterClient() override; + + // crash_reporter::CrashReporterClient implementation. + void SetCrashReporterClientIdFromGUID( + const std::string& client_guid) override; +#if defined(OS_WIN) + virtual bool GetAlternativeCrashDumpLocation(base::FilePath* crash_dir) + override; + virtual void GetProductNameAndVersion(const base::FilePath& exe_path, + base::string16* product_name, + base::string16* version, + base::string16* special_build, + base::string16* channel_name) override; + virtual bool ShouldShowRestartDialog(base::string16* title, + base::string16* message, + bool* is_rtl_locale) override; + virtual bool AboutToRestart() override; + virtual bool GetDeferredUploadsSupported(bool is_per_user_install) override; + virtual bool GetIsPerUserInstall(const base::FilePath& exe_path) override; + virtual bool GetShouldDumpLargerDumps(bool is_per_user_install) override; + virtual int GetResultCodeRespawnFailed() override; + virtual void InitBrowserCrashDumpsRegKey() override; + virtual void RecordCrashDumpAttempt(bool is_real_crash) override; +#endif + +#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS) + void GetProductNameAndVersion(const char** product_name, + const char** version) override; + base::FilePath GetReporterLogFilename() override; +#endif + + bool GetCrashDumpLocation(base::FilePath* crash_dir) override; + + size_t RegisterCrashKeys() override; + + bool IsRunningUnattended() override; + + bool GetCollectStatsConsent() override; + +#if defined(OS_WIN) || defined(OS_MACOSX) + bool ReportingIsEnforcedByPolicy(bool* breakpad_enabled) override; +#endif + +#if defined(OS_ANDROID) + virtual int GetAndroidMinidumpDescriptor() override; +#endif + +#if defined(OS_MACOSX) + void InstallAdditionalFilters(BreakpadRef breakpad) override; +#endif + + bool EnableBreakpadForProcess(const std::string& process_type) override; + + private: + DISALLOW_COPY_AND_ASSIGN(ChromeCrashReporterClient); +}; + +} // namespace chrome + +#endif // CHROME_APP_CHROME_CRASH_REPORTER_CLIENT_H_ diff --git a/src/browser/chrome_crash_reporter_client_mac.mm b/src/browser/chrome_crash_reporter_client_mac.mm new file mode 100644 index 0000000000..d7927559ea --- /dev/null +++ b/src/browser/chrome_crash_reporter_client_mac.mm @@ -0,0 +1,62 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/app/chrome_crash_reporter_client.h" + +#include + +#include "base/mac/scoped_cftyperef.h" +#include "base/strings/sys_string_conversions.h" +//#include "policy/policy_constants.h" + +#if !defined(DISABLE_NACL) +#include "base/command_line.h" +#import "breakpad/src/client/mac/Framework/Breakpad.h" +#include "chrome/common/chrome_switches.h" +#include "components/nacl/common/nacl_switches.h" +#include "native_client/src/trusted/service_runtime/osx/crash_filter.h" +#endif + +namespace chrome { + +namespace { + +#if !defined(DISABLE_NACL) +bool NaClBreakpadCrashFilter(int exception_type, + int exception_code, + mach_port_t crashing_thread, + void* context) { + return !NaClMachThreadIsInUntrusted(crashing_thread); +} +#endif + +} // namespace + +void ChromeCrashReporterClient::InstallAdditionalFilters(BreakpadRef breakpad) { +#if !defined(DISABLE_NACL) + if (CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kProcessType) == switches::kNaClLoaderProcess) { + BreakpadSetFilterCallback(breakpad, NaClBreakpadCrashFilter, NULL); + } +#endif +} + +bool ChromeCrashReporterClient::ReportingIsEnforcedByPolicy( + bool* breakpad_enabled) { +#if 0 + base::ScopedCFTypeRef key( + base::SysUTF8ToCFStringRef(policy::key::kMetricsReportingEnabled)); + Boolean key_valid; + Boolean metrics_reporting_enabled = CFPreferencesGetAppBooleanValue(key, + kCFPreferencesCurrentApplication, &key_valid); + if (key_valid && + CFPreferencesAppValueIsForced(key, kCFPreferencesCurrentApplication)) { + *breakpad_enabled = metrics_reporting_enabled; + return true; + } +#endif + return false; +} + +} // namespace chrome diff --git a/src/browser/color_chooser_aura.cc b/src/browser/color_chooser_aura.cc new file mode 100644 index 0000000000..1c4c5012c3 --- /dev/null +++ b/src/browser/color_chooser_aura.cc @@ -0,0 +1,68 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/browser/color_chooser_aura.h" + +#include "content/public/browser/web_contents.h" +#include "ui/views/color_chooser/color_chooser_view.h" +#include "ui/views/widget/widget.h" + +ColorChooserAura::ColorChooserAura(content::WebContents* web_contents, + SkColor initial_color) + : web_contents_(web_contents) { + view_ = new views::ColorChooserView(this, initial_color); + widget_ = views::Widget::CreateWindowWithParent( + view_, web_contents->GetTopLevelNativeWindow()); + widget_->Show(); +} + +void ColorChooserAura::OnColorChosen(SkColor color) { + if (web_contents_) + web_contents_->DidChooseColorInColorChooser(color); +} + +void ColorChooserAura::OnColorChooserDialogClosed() { + view_ = NULL; + widget_ = NULL; + DidEndColorChooser(); +} + +void ColorChooserAura::End() { + if (widget_) { + view_->set_listener(NULL); + widget_->Close(); + view_ = NULL; + widget_ = NULL; + // DidEndColorChooser will invoke Browser::DidEndColorChooser, which deletes + // this. Take care of the call order. + DidEndColorChooser(); + } +} + +void ColorChooserAura::DidEndColorChooser() { + if (web_contents_) + web_contents_->DidEndColorChooser(); +} + +void ColorChooserAura::SetSelectedColor(SkColor color) { + if (view_) + view_->OnColorChanged(color); +} + +// static +ColorChooserAura* ColorChooserAura::Open( + content::WebContents* web_contents, SkColor initial_color) { + return new ColorChooserAura(web_contents, initial_color); +} + +#if !defined(OS_WIN) +namespace nw { + +content::ColorChooser* ShowColorChooser(content::WebContents* web_contents, + SkColor initial_color) { + return ColorChooserAura::Open(web_contents, initial_color); +} + +} // namespace nw +#endif // OS_WIN diff --git a/src/browser/color_chooser_aura.h b/src/browser/color_chooser_aura.h new file mode 100644 index 0000000000..05a2f6f832 --- /dev/null +++ b/src/browser/color_chooser_aura.h @@ -0,0 +1,58 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NW_BROWSER_UI_VIEWS_COLOR_CHOOSER_AURA_H_ +#define NW_BROWSER_UI_VIEWS_COLOR_CHOOSER_AURA_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "content/public/browser/color_chooser.h" +#include "ui/views/color_chooser/color_chooser_listener.h" + +namespace content { +class WebContents; +} + +namespace views { +class ColorChooserView; +class Widget; +} + +// TODO(mukai): rename this as -Ash and move to c/b/ui/ash after Linux-aura +// switches to its native color chooser. +class ColorChooserAura : public content::ColorChooser, + public views::ColorChooserListener { + public: + static ColorChooserAura* Open(content::WebContents* web_contents, + SkColor initial_color); + + private: + ColorChooserAura(content::WebContents* web_contents, SkColor initial_color); + + // content::ColorChooser overrides: + void End() override; + void SetSelectedColor(SkColor color) override; + + // views::ColorChooserListener overrides: + void OnColorChosen(SkColor color) override; + void OnColorChooserDialogClosed() override; + + void DidEndColorChooser(); + + // The actual view of the color chooser. No ownership because its parent + // view will take care of its lifetime. + views::ColorChooserView* view_; + + // The widget for the color chooser. No ownership because it's released + // automatically when closed. + views::Widget* widget_; + + // The web contents invoking the color chooser. No ownership because it will + // outlive this class. + content::WebContents* web_contents_; + + DISALLOW_COPY_AND_ASSIGN(ColorChooserAura); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_COLOR_CHOOSER_AURA_H_ diff --git a/src/browser/color_chooser_dialog.h b/src/browser/color_chooser_dialog.h index 79a7fa09a8..530c9a3331 100644 --- a/src/browser/color_chooser_dialog.h +++ b/src/browser/color_chooser_dialog.h @@ -26,8 +26,8 @@ class ColorChooserDialog virtual ~ColorChooserDialog(); // BaseShellDialog: - virtual bool IsRunning(gfx::NativeWindow owning_window) const OVERRIDE; - virtual void ListenerDestroyed() OVERRIDE; + virtual bool IsRunning(gfx::NativeWindow owning_window) const override; + virtual void ListenerDestroyed() override; private: struct ExecuteOpenParams { diff --git a/src/browser/color_chooser_gtk.cc b/src/browser/color_chooser_gtk.cc deleted file mode 100644 index 9a33769d1e..0000000000 --- a/src/browser/color_chooser_gtk.cc +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include "content/public/browser/color_chooser.h" -#include "content/public/browser/web_contents.h" -#include "grit/nw_resources.h" -#include "ui/base/gtk/gtk_signal.h" -#include "ui/base/l10n/l10n_util.h" -#include "ui/gfx/skia_utils_gtk.h" - -class ColorChooserGtk : public content::ColorChooser { - public: - static ColorChooserGtk* Open(content::WebContents* web_contents, - SkColor initial_color); - - ColorChooserGtk(content::WebContents* web_contents, SkColor initial_color); - virtual ~ColorChooserGtk(); - - virtual void End() OVERRIDE; - virtual void SetSelectedColor(SkColor color) OVERRIDE; - - private: - static ColorChooserGtk* current_color_chooser_; - - CHROMEGTK_CALLBACK_0(ColorChooserGtk, void, OnColorChooserOk); - CHROMEGTK_CALLBACK_0(ColorChooserGtk, void, OnColorChooserCancel); - CHROMEGTK_CALLBACK_0(ColorChooserGtk, void, OnColorChooserDestroy); - - // The web contents invoking the color chooser. No ownership because it will - // outlive this class. - content::WebContents* web_contents_; - GtkWidget* color_selection_dialog_; -}; - -ColorChooserGtk* ColorChooserGtk::current_color_chooser_ = NULL; - -ColorChooserGtk* ColorChooserGtk::Open(content::WebContents* web_contents, - SkColor initial_color) { - if (current_color_chooser_) - current_color_chooser_->End(); - DCHECK(!current_color_chooser_); - current_color_chooser_ = new ColorChooserGtk(web_contents, initial_color); - return current_color_chooser_; -} - -ColorChooserGtk::ColorChooserGtk(content::WebContents* web_contents, - SkColor initial_color) - : web_contents_(web_contents) { - color_selection_dialog_ = gtk_color_selection_dialog_new( - l10n_util::GetStringUTF8(IDS_SELECT_COLOR_DIALOG_TITLE).c_str()); - GtkWidget* cancel_button; - GtkColorSelection* color_selection; - GtkWidget* ok_button; - g_object_get(color_selection_dialog_, - "cancel-button", &cancel_button, - "color-selection", &color_selection, - "ok-button", &ok_button, - NULL); - gtk_color_selection_set_has_opacity_control(color_selection, FALSE); - g_signal_connect(ok_button, "clicked", - G_CALLBACK(OnColorChooserOkThunk), this); - g_signal_connect(cancel_button, "clicked", - G_CALLBACK(OnColorChooserCancelThunk), this); - g_signal_connect(color_selection_dialog_, "destroy", - G_CALLBACK(OnColorChooserDestroyThunk), this); - GdkColor gdk_color = gfx::SkColorToGdkColor(initial_color); - gtk_color_selection_set_previous_color(color_selection, &gdk_color); - gtk_color_selection_set_current_color(color_selection, &gdk_color); - gtk_window_present(GTK_WINDOW(color_selection_dialog_)); - g_object_unref(cancel_button); - g_object_unref(color_selection); - g_object_unref(ok_button); -} - -ColorChooserGtk::~ColorChooserGtk() { - // Always call End() before destroying. - DCHECK(!color_selection_dialog_); -} - -void ColorChooserGtk::OnColorChooserOk(GtkWidget* widget) { - GdkColor color; - GtkColorSelection* color_selection; - g_object_get(color_selection_dialog_, - "color-selection", &color_selection, NULL); - gtk_color_selection_get_current_color(color_selection, &color); - if (web_contents_) - web_contents_->DidChooseColorInColorChooser(gfx::GdkColorToSkColor(color)); - g_object_unref(color_selection); - gtk_widget_destroy(color_selection_dialog_); -} - -void ColorChooserGtk::OnColorChooserCancel(GtkWidget* widget) { - gtk_widget_destroy(color_selection_dialog_); -} - -void ColorChooserGtk::OnColorChooserDestroy(GtkWidget* widget) { - color_selection_dialog_ = NULL; - DCHECK(current_color_chooser_ == this); - current_color_chooser_ = NULL; - if (web_contents_) - web_contents_->DidEndColorChooser(); -} - -void ColorChooserGtk::End() { - if (!color_selection_dialog_) - return; - - gtk_widget_destroy(color_selection_dialog_); -} - -void ColorChooserGtk::SetSelectedColor(SkColor color) { - if (!color_selection_dialog_) - return; - - GdkColor gdk_color = gfx::SkColorToGdkColor(color); - GtkColorSelection* color_selection; - g_object_get(color_selection_dialog_, - "color-selection", &color_selection, NULL); - gtk_color_selection_set_previous_color(color_selection, &gdk_color); - gtk_color_selection_set_current_color(color_selection, &gdk_color); - g_object_unref(color_selection); -} - -namespace nw { - -content::ColorChooser* ShowColorChooser(content::WebContents* web_contents, - SkColor initial_color) { - return ColorChooserGtk::Open(web_contents, initial_color); -} - -} // namespace chrome diff --git a/src/browser/color_chooser_mac.mm b/src/browser/color_chooser_mac.mm index 6ec46f1e2b..38b6b2101e 100644 --- a/src/browser/color_chooser_mac.mm +++ b/src/browser/color_chooser_mac.mm @@ -45,8 +45,8 @@ - (void)setColor:(NSColor*)color; void DidChooseColorInColorPanel(SkColor color); void DidCloseColorPabel(); - virtual void End() OVERRIDE; - virtual void SetSelectedColor(SkColor color) OVERRIDE; + virtual void End() override; + virtual void SetSelectedColor(SkColor color) override; private: static ColorChooserMac* current_color_chooser_; diff --git a/src/browser/color_chooser_win.cc b/src/browser/color_chooser_win.cc index fc426679a7..6ebc3be0a0 100644 --- a/src/browser/color_chooser_win.cc +++ b/src/browser/color_chooser_win.cc @@ -24,8 +24,8 @@ class ColorChooserWin : public content::ColorChooser, ~ColorChooserWin(); // content::ColorChooser overrides: - virtual void End() OVERRIDE {} - virtual void SetSelectedColor(SkColor color) OVERRIDE {} + virtual void End() override {} + virtual void SetSelectedColor(SkColor color) override {} // views::ColorChooserListener overrides: virtual void OnColorChosen(SkColor color); diff --git a/src/browser/file_select_helper.cc b/src/browser/file_select_helper.cc index c41b131658..3f34eca45b 100644 --- a/src/browser/file_select_helper.cc +++ b/src/browser/file_select_helper.cc @@ -1,35 +1,26 @@ -// 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. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "content/nw/src/browser/file_select_helper.h" #include +#include #include "base/bind.h" -#include "base/file_util.h" -#include "base/platform_file.h" -#include "base/logging.h" -#include "base/strings/string_util.h" +#include "base/files/file_enumerator.h" +#include "base/files/file_util.h" #include "base/strings/string_split.h" +#include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +//#include "chrome/browser/browser_process.h" #include "chrome/browser/platform_util.h" +//#include "chrome/browser/profiles/profile.h" +//#include "chrome/browser/profiles/profile_manager.h" +// #include "chrome/browser/ui/browser.h" +// #include "chrome/browser/ui/browser_list.h" +// #include "chrome/browser/ui/chrome_select_file_policy.h" +#include "grit/nw_resources.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_details.h" #include "content/public/browser/notification_source.h" @@ -37,18 +28,22 @@ #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/web_contents.h" +#include "content/public/common/file_chooser_file_info.h" #include "content/public/common/file_chooser_params.h" -#include "grit/nw_resources.h" #include "net/base/mime_util.h" -#include "ui/shell_dialogs/selected_file_info.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/shell_dialogs/selected_file_info.h" + +#if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/file_manager/fileapi_util.h" +#include "content/public/browser/site_instance.h" +#endif using content::BrowserThread; using content::FileChooserParams; using content::RenderViewHost; using content::RenderWidgetHost; using content::WebContents; -using base::FilePath; namespace { @@ -57,15 +52,9 @@ namespace { // the renderer must start at 0 and increase. const int kFileSelectEnumerationId = -1; -void NotifyRenderViewHost(RenderViewHost* render_view_host, - const std::vector& files, - FileChooserParams::Mode dialog_mode) { - render_view_host->FilesSelectedInChooser(files, dialog_mode); -} - // Converts a list of FilePaths to a list of ui::SelectedFileInfo. std::vector FilePathListToSelectedFileInfoList( - const std::vector& paths) { + const std::vector& paths) { std::vector selected_files; for (size_t i = 0; i < paths.size(); ++i) { selected_files.push_back( @@ -74,6 +63,11 @@ std::vector FilePathListToSelectedFileInfoList( return selected_files; } +void DeleteFiles(const std::vector& paths) { + for (auto& file_path : paths) + base::DeleteFile(file_path, false); +} + } // namespace struct FileSelectHelper::ActiveDirectoryEnumeration { @@ -82,17 +76,17 @@ struct FileSelectHelper::ActiveDirectoryEnumeration { scoped_ptr delegate_; scoped_ptr lister_; RenderViewHost* rvh_; - std::vector results_; + std::vector results_; }; FileSelectHelper::FileSelectHelper() - : render_view_host_(NULL), + : + render_view_host_(NULL), web_contents_(NULL), select_file_dialog_(), select_file_types_(), dialog_type_(ui::SelectFileDialog::SELECT_OPEN_FILE), - dialog_mode_(FileChooserParams::Open) -{ + dialog_mode_(FileChooserParams::Open) { } FileSelectHelper::~FileSelectHelper() { @@ -121,7 +115,7 @@ void FileSelectHelper::DirectoryListerDispatchDelegate::OnListDone(int error) { parent_->OnListDone(id_, error); } -void FileSelectHelper::FileSelected(const FilePath& path, +void FileSelectHelper::FileSelected(const base::FilePath& path, int index, void* params) { FileSelectedWithExtraInfo(ui::SelectedFileInfo(path, path), index, params); } @@ -130,10 +124,13 @@ void FileSelectHelper::FileSelectedWithExtraInfo( const ui::SelectedFileInfo& file, int index, void* params) { - if (!render_view_host_) + + if (!render_view_host_) { + RunFileChooserEnd(); return; + } - const FilePath& path = file.local_path; + const base::FilePath& path = file.local_path; if (dialog_type_ == ui::SelectFileDialog::SELECT_UPLOAD_FOLDER && extract_directory_) { StartNewEnumeration(path, kFileSelectEnumerationId, render_view_host_); @@ -142,14 +139,20 @@ void FileSelectHelper::FileSelectedWithExtraInfo( std::vector files; files.push_back(file); - NotifyRenderViewHost(render_view_host_, files, dialog_mode_); - // No members should be accessed from here on. - RunFileChooserEnd(); +#if defined(OS_MACOSX) && !defined(OS_IOS) + content::BrowserThread::PostTask( + content::BrowserThread::FILE_USER_BLOCKING, + FROM_HERE, + base::Bind(&FileSelectHelper::ProcessSelectedFilesMac, this, files)); +#else + NotifyRenderViewHostAndEnd(files); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) } -void FileSelectHelper::MultiFilesSelected(const std::vector& files, - void* params) { +void FileSelectHelper::MultiFilesSelected( + const std::vector& files, + void* params) { std::vector selected_files = FilePathListToSelectedFileInfoList(files); @@ -159,30 +162,22 @@ void FileSelectHelper::MultiFilesSelected(const std::vector& files, void FileSelectHelper::MultiFilesSelectedWithExtraInfo( const std::vector& files, void* params) { - if (!render_view_host_) - return; - NotifyRenderViewHost(render_view_host_, files, dialog_mode_); - - // No members should be accessed from here on. - RunFileChooserEnd(); +#if defined(OS_MACOSX) && !defined(OS_IOS) + content::BrowserThread::PostTask( + content::BrowserThread::FILE_USER_BLOCKING, + FROM_HERE, + base::Bind(&FileSelectHelper::ProcessSelectedFilesMac, this, files)); +#else + NotifyRenderViewHostAndEnd(files); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) } void FileSelectHelper::FileSelectionCanceled(void* params) { - if (!render_view_host_) - return; - - // If the user cancels choosing a file to upload we pass back an - // empty vector. - NotifyRenderViewHost( - render_view_host_, std::vector(), - dialog_mode_); - - // No members should be accessed from here on. - RunFileChooserEnd(); + NotifyRenderViewHostAndEnd(std::vector()); } -void FileSelectHelper::StartNewEnumeration(const FilePath& path, +void FileSelectHelper::StartNewEnumeration(const base::FilePath& path, int request_id, RenderViewHost* render_view_host) { scoped_ptr entry(new ActiveDirectoryEnumeration); @@ -208,13 +203,11 @@ void FileSelectHelper::OnListFile( const net::DirectoryLister::DirectoryListerData& data) { ActiveDirectoryEnumeration* entry = directory_enumerations_[id]; - // Directory upload returns directories via a "." file, so that - // empty directories are included. This util call just checks - // the flags in the structure; there's no file I/O going on. + // Directory upload only cares about files. if (data.info.IsDirectory()) - entry->results_.push_back(data.path.Append(FILE_PATH_LITERAL("."))); - else - entry->results_.push_back(data.path); + return; + + entry->results_.push_back(data.path); } void FileSelectHelper::OnListDone(int id, int error) { @@ -231,17 +224,71 @@ void FileSelectHelper::OnListDone(int id, int error) { std::vector selected_files = FilePathListToSelectedFileInfoList(entry->results_); - if (id == kFileSelectEnumerationId) - NotifyRenderViewHost(entry->rvh_, selected_files, dialog_mode_); - else + if (id == kFileSelectEnumerationId) { + NotifyRenderViewHostAndEnd(selected_files); + } else { entry->rvh_->DirectoryEnumerationFinished(id, entry->results_); + EnumerateDirectoryEnd(); + } +} + +void FileSelectHelper::NotifyRenderViewHostAndEnd( + const std::vector& files) { + if (!render_view_host_) { + RunFileChooserEnd(); + return; + } + +#if defined(OS_CHROMEOS) + if (!files.empty()) { + if (!IsValidProfile(profile_)) { + RunFileChooserEnd(); + return; + } + // Converts |files| into FileChooserFileInfo with handling of non-native + // files. + file_manager::util::ConvertSelectedFileInfoListToFileChooserFileInfoList( + file_manager::util::GetFileSystemContextForRenderViewHost( + profile_, render_view_host_), + web_contents_->GetSiteInstance()->GetSiteURL(), + files, + base::Bind( + &FileSelectHelper::NotifyRenderViewHostAndEndAfterConversion, + this)); + return; + } +#endif // defined(OS_CHROMEOS) + + std::vector chooser_files; + for (const auto& file : files) { + content::FileChooserFileInfo chooser_file; + chooser_file.file_path = file.local_path; + chooser_file.display_name = file.display_name; + chooser_files.push_back(chooser_file); + } + + NotifyRenderViewHostAndEndAfterConversion(chooser_files); +} + +void FileSelectHelper::NotifyRenderViewHostAndEndAfterConversion( + const std::vector& list) { + if (render_view_host_) + render_view_host_->FilesSelectedInChooser(list, dialog_mode_); - EnumerateDirectoryEnd(); + // No members should be accessed from here on. + RunFileChooserEnd(); +} + +void FileSelectHelper::DeleteTemporaryFiles() { + BrowserThread::PostTask(BrowserThread::FILE, + FROM_HERE, + base::Bind(&DeleteFiles, temporary_files_)); + temporary_files_.clear(); } scoped_ptr FileSelectHelper::GetFileTypesFromAcceptType( - const std::vector& accept_types) { + const std::vector& accept_types) { scoped_ptr base_file_type( new ui::SelectFileDialog::FileTypeInfo()); if (accept_types.empty()) @@ -252,7 +299,8 @@ FileSelectHelper::GetFileTypesFromAcceptType( new ui::SelectFileDialog::FileTypeInfo(*base_file_type)); file_type->include_all_files = true; file_type->extensions.resize(1); - std::vector* extensions = &file_type->extensions.back(); + std::vector* extensions = + &file_type->extensions.back(); // Find the corresponding extensions. int valid_type_count = 0; @@ -295,7 +343,7 @@ FileSelectHelper::GetFileTypesFromAcceptType( // dialog uses the first extension in the list to form the description, // like "EHTML Files". This is not what we want. if (valid_type_count > 1 || - (valid_type_count == 1 && !description_id == 0 && extensions->size() > 1)) + (valid_type_count == 1 && description_id == 0 && extensions->size() > 1)) description_id = IDS_CUSTOM_FILES; if (description_id) { @@ -319,7 +367,7 @@ void FileSelectHelper::RunFileChooser(content::WebContents* tab, // static void FileSelectHelper::EnumerateDirectory(content::WebContents* tab, int request_id, - const FilePath& path) { + const base::FilePath& path) { // FileSelectHelper will keep itself alive until it sends the result message. scoped_refptr file_select_helper( new FileSelectHelper()); @@ -335,8 +383,12 @@ void FileSelectHelper::RunFileChooser(RenderViewHost* render_view_host, render_view_host_ = render_view_host; web_contents_ = web_contents; notification_registrar_.RemoveAll(); + notification_registrar_.Add(this, + content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED, + content::Source(web_contents_)); notification_registrar_.Add( - this, content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED, + this, + content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED, content::Source(render_view_host_)); notification_registrar_.Add( this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, @@ -356,8 +408,8 @@ void FileSelectHelper::RunFileChooser(RenderViewHost* render_view_host, void FileSelectHelper::RunFileChooserOnFileThread( const FileChooserParams& params) { - select_file_types_ = - GetFileTypesFromAcceptType(params.accept_types); + select_file_types_ = GetFileTypesFromAcceptType(params.accept_types); + select_file_types_->support_drive = !params.need_local_path; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, @@ -373,7 +425,10 @@ void FileSelectHelper::RunFileChooserOnUIThread( return; } - select_file_dialog_ = ui::SelectFileDialog::Create(this, NULL); + select_file_dialog_ = ui::SelectFileDialog::Create( + this, NULL); + if (!select_file_dialog_.get()) + return; dialog_mode_ = params.mode; switch (params.mode) { @@ -395,15 +450,16 @@ void FileSelectHelper::RunFileChooserOnUIThread( NOTREACHED(); } - FilePath default_file_name = params.default_file_name; - FilePath working_path = params.initial_path; + base::FilePath default_file_name = params.default_file_name; + base::FilePath working_path = params.initial_path; -#if defined(OS_WIN) - gfx::NativeWindow owning_window = - (gfx::NativeWindow)::GetAncestor((HWND)render_view_host_->GetView()->GetNativeView(), GA_ROOT); -#else gfx::NativeWindow owning_window = platform_util::GetTopLevel(render_view_host_->GetView()->GetNativeView()); + +#if defined(OS_ANDROID) + // Android needs the original MIME types and an additional capture value. + std::pair, bool> accept_types = + std::make_pair(params.accept_types, params.capture); #endif select_file_dialog_->SelectFile( @@ -411,11 +467,17 @@ void FileSelectHelper::RunFileChooserOnUIThread( params.title, default_file_name, select_file_types_.get(), - select_file_types_.get() ? 1 : 0, // 1-based index. - FILE_PATH_LITERAL(""), + select_file_types_.get() && !select_file_types_->extensions.empty() + ? 1 + : 0, // 1-based index of default extension to show. + base::FilePath::StringType(), owning_window, - const_cast(¶ms), - working_path); +#if defined(OS_ANDROID) + &accept_types); +#else + NULL, + working_path); +#endif select_file_types_.reset(); } @@ -424,6 +486,12 @@ void FileSelectHelper::RunFileChooserOnUIThread( // chooser dialog. Perform any cleanup and release the reference we added // in RunFileChooser(). void FileSelectHelper::RunFileChooserEnd() { + // If there are temporary files, then this instance needs to stick around + // until web_contents_ is destroyed, so that this instance can delete the + // temporary files. + if (!temporary_files_.empty()) + return; + render_view_host_ = NULL; web_contents_ = NULL; Release(); @@ -431,7 +499,7 @@ void FileSelectHelper::RunFileChooserEnd() { void FileSelectHelper::EnumerateDirectory(int request_id, RenderViewHost* render_view_host, - const FilePath& path) { + const base::FilePath& path) { // Because this class returns notifications to the RenderViewHost, it is // difficult for callers to know how long to keep a reference to this @@ -463,9 +531,20 @@ void FileSelectHelper::Observe(int type, case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: { DCHECK(content::Source(source).ptr() == web_contents_); web_contents_ = NULL; - break; } + // Intentional fall through. + case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED: + if (!temporary_files_.empty()) { + DeleteTemporaryFiles(); + + // Now that the temporary files have been scheduled for deletion, there + // is no longer any reason to keep this instance around. + Release(); + } + + break; + default: NOTREACHED(); } @@ -478,8 +557,9 @@ bool FileSelectHelper::IsAcceptTypeValid(const std::string& accept_type) { // of an extension or a "/" in the case of a MIME type). std::string unused; if (accept_type.length() <= 1 || - StringToLowerASCII(accept_type) != accept_type || - base::TrimWhitespaceASCII(accept_type, base::TRIM_ALL, &unused) != base::TRIM_NONE) { + base::StringToLowerASCII(accept_type) != accept_type || + base::TrimWhitespaceASCII(accept_type, base::TRIM_ALL, &unused) != + base::TRIM_NONE) { return false; } return true; diff --git a/src/browser/file_select_helper.h b/src/browser/file_select_helper.h index e488a4ad1b..35036d1ff4 100644 --- a/src/browser/file_select_helper.h +++ b/src/browser/file_select_helper.h @@ -1,25 +1,9 @@ -// 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_BROWSER_FILE_SELECT_HELPER_H_ -#define CONTENT_NW_SRC_BROWSER_FILE_SELECT_HELPER_H_ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_FILE_SELECT_HELPER_H_ +#define CHROME_BROWSER_FILE_SELECT_HELPER_H_ #include #include @@ -32,7 +16,10 @@ #include "net/base/directory_lister.h" #include "ui/shell_dialogs/select_file_dialog.h" +class Profile; + namespace content { +struct FileChooserFileInfo; class RenderViewHost; class WebContents; } @@ -41,9 +28,6 @@ namespace ui { struct SelectedFileInfo; } -namespace base { -class FilePath; -} // This class handles file-selection requests coming from WebUI elements // (via the extensions::ExtensionHost class). It implements both the // initialisation and listener functions for file-selection dialogs. @@ -65,8 +49,9 @@ class FileSelectHelper private: friend class base::RefCountedThreadSafe; FRIEND_TEST_ALL_PREFIXES(FileSelectHelperTest, IsAcceptTypeValid); + FRIEND_TEST_ALL_PREFIXES(FileSelectHelperTest, ZipPackage); explicit FileSelectHelper(); - virtual ~FileSelectHelper(); + ~FileSelectHelper() override; // Utility class which can listen for directory lister events and relay // them to the main object with the correct tracking id. @@ -76,10 +61,11 @@ class FileSelectHelper DirectoryListerDispatchDelegate(FileSelectHelper* parent, int id) : parent_(parent), id_(id) {} - virtual ~DirectoryListerDispatchDelegate() {} - virtual void OnListFile( - const net::DirectoryLister::DirectoryListerData& data) OVERRIDE; - virtual void OnListDone(int error) OVERRIDE; + ~DirectoryListerDispatchDelegate() override {} + void OnListFile( + const net::DirectoryLister::DirectoryListerData& data) override; + void OnListDone(int error) override; + private: // This FileSelectHelper owns this object. FileSelectHelper* parent_; @@ -89,7 +75,7 @@ class FileSelectHelper }; void RunFileChooser(content::RenderViewHost* render_view_host, - content::WebContents* tab_contents, + content::WebContents* web_contents, const content::FileChooserParams& params); void RunFileChooserOnFileThread( const content::FileChooserParams& params); @@ -101,23 +87,23 @@ class FileSelectHelper void RunFileChooserEnd(); // SelectFileDialog::Listener overrides. - virtual void FileSelected( - const base::FilePath& path, int index, void* params) OVERRIDE; - virtual void FileSelectedWithExtraInfo( - const ui::SelectedFileInfo& file, - int index, - void* params) OVERRIDE; - virtual void MultiFilesSelected(const std::vector& files, - void* params) OVERRIDE; - virtual void MultiFilesSelectedWithExtraInfo( + void FileSelected(const base::FilePath& path, + int index, + void* params) override; + void FileSelectedWithExtraInfo(const ui::SelectedFileInfo& file, + int index, + void* params) override; + void MultiFilesSelected(const std::vector& files, + void* params) override; + void MultiFilesSelectedWithExtraInfo( const std::vector& files, - void* params) OVERRIDE; - virtual void FileSelectionCanceled(void* params) OVERRIDE; + void* params) override; + void FileSelectionCanceled(void* params) override; // content::NotificationObserver overrides. - virtual void Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) OVERRIDE; + void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) override; void EnumerateDirectory(int request_id, content::RenderViewHost* render_view_host, @@ -138,15 +124,45 @@ class FileSelectHelper // callback is received from the enumeration code. void EnumerateDirectoryEnd(); - bool extract_directory_; +#if defined(OS_MACOSX) && !defined(OS_IOS) + // Must be called on the FILE_USER_BLOCKING thread. Each selected file that is + // a package will be zipped, and the zip will be passed to the render view + // host in place of the package. + void ProcessSelectedFilesMac(const std::vector& files); + + // Saves the paths of |zipped_files| for later deletion. Passes |files| to the + // render view host. + void ProcessSelectedFilesMacOnUIThread( + const std::vector& files, + const std::vector& zipped_files); + + // Zips the package at |path| into a temporary destination. Returns the + // temporary destination, if the zip was successful. Otherwise returns an + // empty path. + static base::FilePath ZipPackage(const base::FilePath& path); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + + // Utility method that passes |files| to the render view host, and ends the + // file chooser. + void NotifyRenderViewHostAndEnd( + const std::vector& files); + + // Sends the result to the render process, and call |RunFileChooserEnd|. + void NotifyRenderViewHostAndEndAfterConversion( + const std::vector& list); + + // Schedules the deletion of the files in |temporary_files_| and clears the + // vector. + void DeleteTemporaryFiles(); // Helper method to get allowed extensions for select file dialog from // the specified accept types as defined in the spec: // http://whatwg.org/html/number-state.html#attr-input-accept // |accept_types| contains only valid lowercased MIME types or file extensions // beginning with a period (.). - scoped_ptr GetFileTypesFromAcceptType( - const std::vector& accept_types); + static scoped_ptr + GetFileTypesFromAcceptType( + const std::vector& accept_types); // Check the accept type is valid. It is expected to be all lower case with // no whitespace. @@ -176,7 +192,13 @@ class FileSelectHelper // Registrar for notifications regarding our RenderViewHost. content::NotificationRegistrar notification_registrar_; + // Temporary files only used on OSX. This class is responsible for deleting + // these files when they are no longer needed. + std::vector temporary_files_; + + bool extract_directory_; + DISALLOW_COPY_AND_ASSIGN(FileSelectHelper); }; -#endif // CONTENT_NW_SRC_BROWSER_FILE_SELECT_HELPER_H_ +#endif // CHROME_BROWSER_FILE_SELECT_HELPER_H_ diff --git a/src/browser/file_select_helper_mac.mm b/src/browser/file_select_helper_mac.mm new file mode 100644 index 0000000000..4f0f4aad8c --- /dev/null +++ b/src/browser/file_select_helper_mac.mm @@ -0,0 +1,142 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/file_select_helper.h" + +#include +#include + +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/mac/foundation_util.h" +#include "content/public/browser/browser_thread.h" +#include "third_party/zlib/google/zip.h" +#include "ui/shell_dialogs/selected_file_info.h" + +namespace { + +// Given the |path| of a package, returns the destination that the package +// should be zipped to. Returns an empty path on any errors. +base::FilePath ZipDestination(const base::FilePath& path) { + NSMutableString* dest = + [NSMutableString stringWithString:NSTemporaryDirectory()]; + + // Couldn't get the temporary directory. + if (!dest) + return base::FilePath(); + + [dest appendFormat:@"%@/zip_cache/%@", + [[NSBundle mainBundle] bundleIdentifier], + [[NSProcessInfo processInfo] globallyUniqueString]]; + + return base::mac::NSStringToFilePath(dest); +} + +// Returns the path of the package and its components relative to the package's +// parent directory. +std::vector RelativePathsForPackage( + const base::FilePath& package) { + // Get the base directory. + base::FilePath base_dir = package.DirName(); + + // Add the package as the first relative path. + std::vector relative_paths; + relative_paths.push_back(package.BaseName()); + + // Add the components of the package as relative paths. + base::FileEnumerator file_enumerator( + package, + true /* recursive */, + base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); + for (base::FilePath path = file_enumerator.Next(); !path.empty(); + path = file_enumerator.Next()) { + base::FilePath relative_path; + bool success = base_dir.AppendRelativePath(path, &relative_path); + if (success) + relative_paths.push_back(relative_path); + } + + return relative_paths; +} + +} // namespace + +base::FilePath FileSelectHelper::ZipPackage(const base::FilePath& path) { + base::FilePath dest(ZipDestination(path)); + if (dest.empty()) + return dest; + + if (!base::CreateDirectory(dest.DirName())) + return base::FilePath(); + + base::File file(dest, base::File::FLAG_CREATE | base::File::FLAG_WRITE); + if (!file.IsValid()) + return base::FilePath(); + + std::vector files_to_zip(RelativePathsForPackage(path)); + base::FilePath base_dir = path.DirName(); + bool success = zip::ZipFiles(base_dir, files_to_zip, file.GetPlatformFile()); + + int result = -1; + if (success) + result = fchmod(file.GetPlatformFile(), S_IRUSR); + + return result >= 0 ? dest : base::FilePath(); +} + +void FileSelectHelper::ProcessSelectedFilesMac( + const std::vector& files) { + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE_USER_BLOCKING); + + // Make a mutable copy of the input files. + std::vector files_out(files); + std::vector temporary_files; + + for (auto& file_info : files_out) { + NSString* filename = base::mac::FilePathToNSString(file_info.local_path); + BOOL isPackage = + [[NSWorkspace sharedWorkspace] isFilePackageAtPath:filename]; + if (isPackage && base::DirectoryExists(file_info.local_path)) { + base::FilePath result = ZipPackage(file_info.local_path); + + if (!result.empty()) { + temporary_files.push_back(result); + file_info.local_path = result; + file_info.file_path = result; + file_info.display_name.append(".zip"); + } + } + } + + content::BrowserThread::PostTask( + content::BrowserThread::UI, + FROM_HERE, + base::Bind(&FileSelectHelper::ProcessSelectedFilesMacOnUIThread, + base::Unretained(this), + files_out, + temporary_files)); +} + +void FileSelectHelper::ProcessSelectedFilesMacOnUIThread( + const std::vector& files, + const std::vector& temporary_files) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + if (!temporary_files.empty()) { + temporary_files_.insert( + temporary_files_.end(), temporary_files.begin(), temporary_files.end()); + + // Typically, |temporary_files| are deleted after |web_contents_| is + // destroyed. If |web_contents_| is already NULL, then the temporary files + // need to be deleted now. + if (!web_contents_) { + DeleteTemporaryFiles(); + RunFileChooserEnd(); + return; + } + } + + NotifyRenderViewHostAndEnd(files); +} diff --git a/src/browser/login_view.cc b/src/browser/login_view.cc index 5220882397..75fb4ba1af 100644 --- a/src/browser/login_view.cc +++ b/src/browser/login_view.cc @@ -5,7 +5,6 @@ #include "login_view.h" #include "base/strings/utf_string_conversions.h" -#include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" #include "ui/views/controls/label.h" #include "ui/views/controls/textfield/textfield.h" diff --git a/src/browser/login_view.h b/src/browser/login_view.h index b254f1e5ee..2d43e9c9d3 100644 --- a/src/browser/login_view.h +++ b/src/browser/login_view.h @@ -20,7 +20,7 @@ class LoginView : public views::View { // |model| is observed for the entire lifetime of the LoginView. // Therefore |model| should not be destroyed before the LoginView object. LoginView(const base::string16& explanation); - virtual ~LoginView(); + ~LoginView() final; // Access the data in the username/password text fields. const base::string16& GetUsername() const; diff --git a/src/browser/media_capture_util.cc b/src/browser/media_capture_util.cc new file mode 100644 index 0000000000..30e3fb63c7 --- /dev/null +++ b/src/browser/media_capture_util.cc @@ -0,0 +1,88 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/shell/browser/media_capture_util.h" + +#include + +#include "base/callback.h" +#include "base/logging.h" +#include "content/public/browser/media_capture_devices.h" +#include "extensions/common/permissions/permissions_data.h" + +using content::MediaCaptureDevices; +using content::MediaStreamDevice; +using content::MediaStreamDevices; +using content::MediaStreamUI; + +namespace extensions { + +const MediaStreamDevice* GetRequestedDeviceOrDefault( + const MediaStreamDevices& devices, + const std::string& requested_device_id) { + if (!requested_device_id.empty()) + return devices.FindById(requested_device_id); + + if (!devices.empty()) + return &devices[0]; + + return NULL; +} + +namespace media_capture_util { + +// See also Chrome's MediaCaptureDevicesDispatcher. +void GrantMediaStreamRequest(content::WebContents* web_contents, + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback, + const Extension* extension) { + // app_shell only supports audio and video capture, not tab or screen capture. + DCHECK(request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE || + request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE); + + MediaStreamDevices devices; + + if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE) { + VerifyMediaAccessPermission(request.audio_type, extension); + const MediaStreamDevice* device = GetRequestedDeviceOrDefault( + MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices(), + request.requested_audio_device_id); + if (device) + devices.push_back(*device); + } + + if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) { + VerifyMediaAccessPermission(request.video_type, extension); + const MediaStreamDevice* device = GetRequestedDeviceOrDefault( + MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices(), + request.requested_video_device_id); + if (device) + devices.push_back(*device); + } + + // TODO(jamescook): Should we show a recording icon somewhere? If so, where? + scoped_ptr ui; + callback.Run(devices, + devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE + : content::MEDIA_DEVICE_OK, + ui.Pass()); +} + +void VerifyMediaAccessPermission(content::MediaStreamType type, + const Extension* extension) { + const PermissionsData* permissions_data = extension->permissions_data(); + if (type == content::MEDIA_DEVICE_AUDIO_CAPTURE) { + // app_shell has no UI surface to show an error, and on an embedded device + // it's better to crash than to have a feature not work. + CHECK(permissions_data->HasAPIPermission(APIPermission::kAudioCapture)) + << "Audio capture request but no audioCapture permission in manifest."; + } else { + DCHECK(type == content::MEDIA_DEVICE_VIDEO_CAPTURE); + CHECK(permissions_data->HasAPIPermission(APIPermission::kVideoCapture)) + << "Video capture request but no videoCapture permission in manifest."; + } +} + +} // namespace media_capture_util +} // namespace extensions diff --git a/src/browser/media_capture_util.h b/src/browser/media_capture_util.h new file mode 100644 index 0000000000..b1b15ba98c --- /dev/null +++ b/src/browser/media_capture_util.h @@ -0,0 +1,38 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_SHELL_BROWSER_MEDIA_CAPTURE_UTIL_H_ +#define EXTENSIONS_SHELL_BROWSER_MEDIA_CAPTURE_UTIL_H_ + +#include "base/macros.h" +#include "content/public/common/media_stream_request.h" + +namespace content { +class WebContents; +} + +namespace extensions { + +class Extension; + +namespace media_capture_util { + +// Grants access to audio and video capture devices. +// * If the caller requests specific device ids, grants access to those. +// * If the caller does not request specific ids, grants access to the first +// available device. +// Usually used as a helper for media capture ProcessMediaAccessRequest(). +void GrantMediaStreamRequest(content::WebContents* web_contents, + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback, + const Extension* extension); + +// Verifies that the extension has permission for |type|. If not, crash. +void VerifyMediaAccessPermission(content::MediaStreamType type, + const Extension* extension); + +} // namespace media_capture_util +} // namespace extensions + +#endif // EXTENSIONS_SHELL_BROWSER_MEDIA_CAPTURE_UTIL_H_ diff --git a/src/browser/menubar_controller.cc b/src/browser/menubar_controller.cc new file mode 100644 index 0000000000..b661117872 --- /dev/null +++ b/src/browser/menubar_controller.cc @@ -0,0 +1,76 @@ +#include "content/nw/src/browser/menubar_controller.h" + +#include "base/stl_util.h" +#include "content/nw/src/browser/menubar_view.h" +#include "ui/views/controls/button/menu_button.h" +#include "ui/views/controls/menu/menu_item_view.h" +#include "ui/views/widget/widget.h" + +namespace nw { + +MenuBarController::ModelToMenuMap MenuBarController::model_to_menu_map_; +MenuBarController* MenuBarController::master_; + +MenuBarController::MenuBarController(MenuBarView* menubar, ui::MenuModel* menu_model, MenuBarController* master) + :MenuModelAdapter(menu_model), menubar_(menubar) { + + views::MenuItemView* menu = MenuBarController::CreateMenu(menubar, menu_model, this); + if (!master) { + master_ = this; + menu_runner_.reset(new views::MenuRunner(menu, views::MenuRunner::HAS_MNEMONICS)); + } +} + +MenuBarController::~MenuBarController() { + if (master_ == this) { + STLDeleteElements(&controllers_); + model_to_menu_map_.clear(); + } +} + +views::MenuItemView* MenuBarController::GetSiblingMenu( + views::MenuItemView* menu, + const gfx::Point& screen_point, + views::MenuAnchorPosition* anchor, + bool* has_mnemonics, + views::MenuButton** button) { + if (!menubar_) + return NULL; + gfx::Point menubar_loc(screen_point); + views::View::ConvertPointFromScreen(menubar_, &menubar_loc); + ui::MenuModel* model; + if (!menubar_->GetMenuButtonAtLocation(menubar_loc, &model, button)) + return NULL; + + *has_mnemonics = false; + *anchor = views::MENU_ANCHOR_TOPLEFT; + if (!model_to_menu_map_[model]) { + MenuBarController* controller = new MenuBarController(menubar_, model, master_); + CreateMenu(menubar_, model, controller); + controllers_.push_back(controller); + } + + return model_to_menu_map_[model]; +} + +views::MenuItemView* MenuBarController::CreateMenu(MenuBarView* menubar, + ui::MenuModel* model, + MenuBarController* controller) { + views::MenuItemView* menu = new views::MenuItemView(controller); + controller->BuildMenu(menu); + model_to_menu_map_[model] = menu; + + return menu; +} + +void MenuBarController::RunMenuAt(views::View* view, const gfx::Point& point) { + + ignore_result(menu_runner_->RunMenuAt(view->GetWidget()->GetTopLevelWidget(), + static_cast(view), + gfx::Rect(point, gfx::Size()), + views::MENU_ANCHOR_TOPRIGHT, + ui::MENU_SOURCE_NONE)); + delete this; +} + +} //namespace nw diff --git a/src/browser/menubar_controller.h b/src/browser/menubar_controller.h new file mode 100644 index 0000000000..b7ecf0dcdf --- /dev/null +++ b/src/browser/menubar_controller.h @@ -0,0 +1,45 @@ +#ifndef NW_BROWSER_MENUBAR_CONTROLLER_H +#define NW_BROWSER_MENUBAR_CONTROLLER_H + +#include "ui/views/controls/menu/menu_model_adapter.h" +#include "ui/views/controls/menu/menu_runner.h" +#include "ui/views/view.h" + +#include + +namespace ui { +class MenuModel; +} + +namespace nw { +class MenuBarView; + +class MenuBarController : public views::MenuModelAdapter { + public: + MenuBarController(MenuBarView* menubar, ui::MenuModel* menu_model, MenuBarController* master); + ~MenuBarController() override; + + static views::MenuItemView* CreateMenu(MenuBarView* menubar, ui::MenuModel* model, MenuBarController* controller); + void RunMenuAt(views::View* view, const gfx::Point& point); + + views::MenuItemView* GetSiblingMenu( + views::MenuItemView* menu, + const gfx::Point& screen_point, + views::MenuAnchorPosition* anchor, + bool* has_mnemonics, + views::MenuButton** button) override; + + private: + typedef std::map ModelToMenuMap; + + MenuBarView* menubar_; + scoped_ptr menu_runner_; + std::vector controllers_; + static ModelToMenuMap model_to_menu_map_; + static MenuBarController* master_; + + DISALLOW_COPY_AND_ASSIGN(MenuBarController); +}; + +} //namespace nw +#endif diff --git a/src/browser/menubar_view.cc b/src/browser/menubar_view.cc new file mode 100644 index 0000000000..3592efcb00 --- /dev/null +++ b/src/browser/menubar_view.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/browser/menubar_view.h" + + +#include "content/nw/src/browser/menubar_controller.h" +#include "ui/base/models/menu_model.h" +#include "ui/base/window_open_disposition.h" +#include "ui/gfx/text_elider.h" +#include "ui/views/controls/button/menu_button.h" +#include "ui/views/layout/box_layout.h" +#include "ui/views/widget/widget.h" + +using views::MenuRunner; + +#if !defined(OS_WIN) +static const gfx::ElideBehavior kElideBehavior = gfx::FADE_TAIL; +#else +// Windows fade eliding causes text to darken; see http://crbug.com/388084 +static const gfx::ElideBehavior kElideBehavior = gfx::ELIDE_TAIL; +#endif + +namespace nw { + +const char MenuBarView::kViewClassName[] = "BookmarkBarView"; + +// MenuBarButton ------------------------------------------------------- + +// Buttons used on the menu bar. Copied from BookmarkFolderButton + +class MenuBarButton : public views::MenuButton { + public: + MenuBarButton(views::ButtonListener* listener, + const base::string16& title, + views::MenuButtonListener* menu_button_listener, + bool show_menu_marker) + : MenuButton(listener, title, menu_button_listener, show_menu_marker) { + SetElideBehavior(kElideBehavior); + } + + bool GetTooltipText(const gfx::Point& p, + base::string16* tooltip) const override { + if (label()->GetPreferredSize().width() > label()->size().width()) + *tooltip = GetText(); + return !tooltip->empty(); + } + + bool IsTriggerableEvent(const ui::Event& e) override { + // Left clicks and taps should show the menu contents and right clicks + // should show the context menu. They should not trigger the opening of + // underlying urls. + if (e.type() == ui::ET_GESTURE_TAP || + (e.IsMouseEvent() && (e.flags() & + (ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON)))) + return false; + + if (e.IsMouseEvent()) + return ui::DispositionFromEventFlags(e.flags()) != CURRENT_TAB; + return false; + } + + private: + + DISALLOW_COPY_AND_ASSIGN(MenuBarButton); +}; + +MenuBarView::MenuBarView() { + SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0)); +} + +MenuBarView::~MenuBarView() { +} + +void MenuBarView::UpdateMenu(ui::MenuModel* model) { + RemoveAllChildViews(true); + InitView(model); + Layout(); + PreferredSizeChanged(); + SchedulePaint(); +} + +void MenuBarView::InitView(ui::MenuModel* model) { + model_ = model; + for (int i = 0; i < model_->GetItemCount(); i++) { + AddChildView(new MenuBarButton(this, model_->GetLabelAt(i), this, false)); + } +} + +bool MenuBarView::GetMenuButtonAtLocation(const gfx::Point& loc, ui::MenuModel** model, views::MenuButton** button) { + if (!model_) + return false; + if (loc.x() < 0 || loc.x() >= width() || loc.y() < 0 || loc.y() >= height()) + return false; + for (int i = 0; i < model_->GetItemCount(); i++) { + views::View* child = child_at(i); + if (child->bounds().Contains(loc) && + (model_->GetTypeAt(i) == ui::MenuModel::TYPE_SUBMENU)) { + *model = model_->GetSubmenuModelAt(i); + *button = static_cast(child); + return true; + } + } + return false; +} + +void MenuBarView::OnMenuButtonClicked(views::View* view, + const gfx::Point& point) { + int button_index = GetIndexOf(view); + DCHECK_NE(-1, button_index); + ui::MenuModel::ItemType type = model_->GetTypeAt(button_index); + if (type == ui::MenuModel::TYPE_SUBMENU) { + MenuBarController* controller = new MenuBarController(this, model_->GetSubmenuModelAt(button_index), NULL); + controller->RunMenuAt(view, point); + } +} + +void MenuBarView::ButtonPressed(views::Button* sender, + const ui::Event& event) { +} + +void MenuBarView::OnNativeThemeChanged(const ui::NativeTheme* theme) { + set_background(views::Background::CreateSolidBackground(GetNativeTheme()-> + GetSystemColor(ui::NativeTheme::kColorId_MenuBackgroundColor))); +} + +} //namespace nw diff --git a/src/browser/menubar_view.h b/src/browser/menubar_view.h new file mode 100644 index 0000000000..00318f0170 --- /dev/null +++ b/src/browser/menubar_view.h @@ -0,0 +1,65 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NW_BROWSER_MENUBAR_VIEWS_H_ +#define NW_BROWSER_MENUBAR_VIEWS_H_ + +#include "base/strings/string16.h" +#include "ui/views/accessible_pane_view.h" +#include "ui/views/controls/button/button.h" +#include "ui/views/controls/button/menu_button_listener.h" + +namespace views { + class MenuRunner; + class MenuButton; +} + +namespace ui { + class MenuModel; +} + +namespace nw { + +/////////////////////////////////////////////////////////////////////////////// +// +// copied from chrome/browser/ui/views/bookmarks/bookmark_bar_view.h +// +/////////////////////////////////////////////////////////////////////////////// + +class MenuBarView : + public views::AccessiblePaneView, + public views::MenuButtonListener, + public views::ButtonListener { + + public: + // The internal view class name. + static const char kViewClassName[]; + + // Maximum size of buttons + static const int kMaxButtonWidth; + + // |browser_view| can be NULL during tests. + MenuBarView(); + ~MenuBarView() override; + + void UpdateMenu(ui::MenuModel* model); + void InitView(ui::MenuModel* model); + + bool GetMenuButtonAtLocation(const gfx::Point& loc, ui::MenuModel** model, views::MenuButton** button); + + // views::MenuButtonListener: + void OnMenuButtonClicked(views::View* view, + const gfx::Point& point) override; + + // views::ButtonListener: + void ButtonPressed(views::Button* sender, + const ui::Event& event) override; + void OnNativeThemeChanged(const ui::NativeTheme* theme) override; + + private: + ui::MenuModel* model_; + DISALLOW_COPY_AND_ASSIGN(MenuBarView); +}; +} //namespace nw +#endif diff --git a/src/browser/native_window.cc b/src/browser/native_window.cc index 51f4ae5e8d..997d513c3b 100644 --- a/src/browser/native_window.cc +++ b/src/browser/native_window.cc @@ -22,22 +22,28 @@ #include "base/memory/weak_ptr.h" #include "base/values.h" +#include "base/command_line.h" #include "content/nw/src/browser/capture_page_helper.h" #include "content/nw/src/common/shell_switches.h" #include "content/nw/src/nw_package.h" #include "content/nw/src/nw_shell.h" +#include "content/public/common/content_switches.h" #include "grit/nw_resources.h" #include "ui/base/resource/resource_bundle.h" -#include "ui/gfx/rect.h" +#include "ui/gfx/geometry/rect.h" #if defined(OS_MACOSX) #include "content/nw/src/browser/native_window_helper_mac.h" -#elif defined(TOOLKIT_GTK) -#include "content/nw/src/browser/native_window_gtk.h" +#elif defined(OS_LINUX) +#include "content/nw/src/browser/native_window_aura.h" #elif defined(OS_WIN) -#include "content/nw/src/browser/native_window_win.h" +#include "content/nw/src/browser/native_window_aura.h" #endif +namespace content { + extern bool g_support_transparency; + extern bool g_force_cpu_draw; +} namespace nw { @@ -52,12 +58,12 @@ NativeWindow* NativeWindow::Create(const base::WeakPtr& shell, // Create window. NativeWindow* window = -#if defined(TOOLKIT_GTK) - new NativeWindowGtk(shell, manifest); +#if defined(OS_LINUX) + new NativeWindowAura(shell, manifest); #elif defined(OS_MACOSX) CreateNativeWindowCocoa(shell, manifest); #elif defined(OS_WIN) - new NativeWindowWin(shell, manifest); + new NativeWindowAura(shell, manifest); #else NULL; NOTREACHED() << "Cannot create native window on unsupported platform."; @@ -69,10 +75,21 @@ NativeWindow* NativeWindow::Create(const base::WeakPtr& shell, NativeWindow::NativeWindow(const base::WeakPtr& shell, base::DictionaryValue* manifest) : shell_(shell), + transparent_(false), has_frame_(true), capture_page_helper_(NULL) { manifest->GetBoolean(switches::kmFrame, &has_frame_); - + content::g_support_transparency = !base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kmDisableTransparency); + if (content::g_support_transparency) { + content::g_force_cpu_draw = base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kForceCpuDraw); + if (content::g_force_cpu_draw) { + if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpu)) { + content::g_force_cpu_draw = false; + LOG(WARNING) << "switch " << switches::kForceCpuDraw << " must be used with switch " << switches::kDisableGpu; + } + } + manifest->GetBoolean(switches::kmTransparent, &transparent_); + } LoadAppIconFromPackage(manifest); } @@ -85,7 +102,7 @@ content::WebContents* NativeWindow::web_contents() const { void NativeWindow::InitFromManifest(base::DictionaryValue* manifest) { // Setup window from manifest. - int x, y; + int x, y = 0; std::string position; if (manifest->GetInteger(switches::kmX, &x) && manifest->GetInteger(switches::kmY, &y)) { @@ -122,6 +139,11 @@ void NativeWindow::InitFromManifest(base::DictionaryValue* manifest) { !showInTaskbar) { SetShowInTaskbar(false); } + bool all_workspaces; + if (manifest->GetBoolean(switches::kmVisibleOnAllWorkspaces, &all_workspaces) + && all_workspaces) { + SetVisibleOnAllWorkspaces(true); + } bool fullscreen; if (manifest->GetBoolean(switches::kmFullscreen, &fullscreen) && fullscreen) { SetFullscreen(true); diff --git a/src/browser/native_window.h b/src/browser/native_window.h index 6cd8236ef6..578f74fc6c 100644 --- a/src/browser/native_window.h +++ b/src/browser/native_window.h @@ -31,10 +31,10 @@ #include "content/nw/src/nw_shell.h" #include "ui/gfx/image/image.h" #include "ui/gfx/native_widget_types.h" -#include "ui/gfx/point.h" -#include "ui/gfx/size.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/size.h" -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_LINUX) #include "components/web_modal/web_contents_modal_dialog_host.h" #endif @@ -64,13 +64,15 @@ namespace nw { class CapturePageHelper; -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_LINUX) class NativeWindow : public web_modal::WebContentsModalDialogHost { +public: + ~NativeWindow() override; #else class NativeWindow { -#endif - public: +public: virtual ~NativeWindow(); +#endif static NativeWindow* Create(const base::WeakPtr& shell, base::DictionaryValue* manifest); @@ -88,6 +90,7 @@ class NativeWindow { virtual void Restore() = 0; virtual void SetFullscreen(bool fullscreen) = 0; virtual bool IsFullscreen() = 0; + virtual void SetTransparent(bool transparent) = 0; virtual void SetSize(const gfx::Size& size) = 0; virtual gfx::Size GetSize() = 0; virtual void SetMinimumSize(int width, int height) = 0; @@ -95,12 +98,14 @@ class NativeWindow { virtual void SetResizable(bool resizable) = 0; virtual void SetAlwaysOnTop(bool top) = 0; virtual void SetShowInTaskbar(bool show = true) = 0; + virtual void SetVisibleOnAllWorkspaces(bool all_workspaces) = 0; virtual void SetPosition(const std::string& position) = 0; virtual void SetPosition(const gfx::Point& position) = 0; virtual gfx::Point GetPosition() = 0; virtual void SetTitle(const std::string& title) = 0; - virtual void FlashFrame(bool flash) = 0; + virtual void FlashFrame(int count) = 0; virtual void SetBadgeLabel(const std::string& badge) = 0; + virtual void SetProgressBar(double progress) = 0; virtual void SetKiosk(bool kiosk) = 0; virtual bool IsKiosk() = 0; virtual void SetMenu(nwapi::Menu* menu) = 0; @@ -128,17 +133,18 @@ class NativeWindow { const content::NativeWebKeyboardEvent& event) = 0; #if defined(OS_WIN) - virtual gfx::NativeView GetHostView() const OVERRIDE = 0; - virtual gfx::Point GetDialogPosition(const gfx::Size& size) OVERRIDE = 0; - virtual void AddObserver(web_modal::ModalDialogHostObserver* observer) OVERRIDE = 0; - virtual void RemoveObserver(web_modal::ModalDialogHostObserver* observer) OVERRIDE = 0; - virtual gfx::Size GetMaximumDialogSize() OVERRIDE = 0; + virtual gfx::NativeView GetHostView() const override = 0; + virtual gfx::Point GetDialogPosition(const gfx::Size& size) override = 0; + virtual void AddObserver(web_modal::ModalDialogHostObserver* observer) override = 0; + virtual void RemoveObserver(web_modal::ModalDialogHostObserver* observer) override = 0; + virtual gfx::Size GetMaximumDialogSize() override = 0; #endif content::Shell* shell() const { return shell_.get(); } content::WebContents* web_contents() const; bool has_frame() const { return has_frame_; } const gfx::Image& app_icon() const { return app_icon_; } void CapturePage(const std::string& image_format); + bool IsTransparent() { return transparent_; } protected: void OnNativeWindowDestory() { @@ -150,6 +156,8 @@ class NativeWindow { // Weak reference to parent. base::WeakPtr shell_; + + bool transparent_; bool has_frame_; diff --git a/src/browser/native_window_win.cc b/src/browser/native_window_aura.cc similarity index 59% rename from src/browser/native_window_win.cc rename to src/browser/native_window_aura.cc index 003c366133..bb3ab1e425 100644 --- a/src/browser/native_window_win.cc +++ b/src/browser/native_window_aura.cc @@ -1,16 +1,16 @@ // Copyright (c) 2012 Intel Corp // Copyright (c) 2012 The Chromium Authors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy +// +// 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 @@ -18,42 +18,60 @@ // 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/browser/native_window_win.h" +#include "content/nw/src/browser/native_window_aura.h" +#if defined(OS_WIN) #include +#include +#endif #include "base/strings/utf_string_conversions.h" #include "base/values.h" +#include "base/command_line.h" + +#if defined(OS_WIN) #include "base/win/scoped_comptr.h" #include "base/win/windows_version.h" #include "base/win/wrapped_window_proc.h" +#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/native_window_toolbar_win.h" +#include "content/nw/src/browser/browser_view_layout.h" +#include "content/nw/src/browser/menubar_view.h" +#include "content/nw/src/browser/native_window_toolbar_aura.h" #include "content/nw/src/common/shell_switches.h" #include "content/nw/src/nw_shell.h" #include "content/public/browser/native_web_keyboard_event.h" #include "content/public/browser/render_view_host.h" +#include "content/browser/renderer_host/render_view_host_impl.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_view.h" +#include "content/public/common/content_switches.h" #include "extensions/common/draggable_region.h" #include "third_party/skia/include/core/SkPaint.h" #include "ui/base/hit_test.h" #include "ui/gfx/native_widget_types.h" +#if defined(OS_WIN) #include "ui/gfx/win/hwnd_util.h" +#include "ui/gfx/icon_util.h" +#include "ui/views/win/hwnd_util.h" +#endif #include "ui/gfx/path.h" #include "ui/gfx/canvas.h" -#include "ui/gfx/icon_util.h" #include "ui/gfx/font_list.h" #include "ui/gfx/platform_font.h" #include "ui/gfx/image/image_skia_operations.h" +#include "ui/views/background.h" #include "ui/views/controls/webview/webview.h" #include "ui/views/layout/box_layout.h" #include "ui/views/views_delegate.h" +#include "ui/views/views_switches.h" #include "ui/views/widget/widget.h" #include "ui/views/window/native_frame_view.h" -#include "ui/views/win/hwnd_util.h" #include "ui/views/widget/native_widget_private.h" #include "ui/events/event_handler.h" #include "ui/wm/core/easy_resize_window_targeter.h" @@ -68,6 +86,12 @@ #include "ash/accelerators/accelerator_table.h" #endif +using base::CommandLine; + +namespace content { + extern bool g_support_transparency; + extern bool g_force_cpu_draw; +} namespace nw { @@ -86,7 +110,7 @@ bool IsParent(gfx::NativeView child, gfx::NativeView possible_parent) { return true; #endif gfx::NativeView parent = child; - while ((parent = (gfx::NativeView)::GetParent((HWND)parent))) { + while ((parent = (gfx::NativeView)platform_util::GetParent(parent)) != NULL) { if (possible_parent == parent) return true; } @@ -94,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, @@ -102,9 +144,9 @@ class NativeWindowClientView : public views::ClientView { : views::ClientView(widget, contents_view), shell_(shell) { } - virtual ~NativeWindowClientView() {} + ~NativeWindowClientView() override {} - virtual bool CanClose() OVERRIDE { + bool CanClose() override { if (shell_) return shell_->ShouldCloseWindow(); else @@ -119,32 +161,33 @@ class NativeWindowFrameView : public views::NonClientFrameView { public: static const char kViewClassName[]; - explicit NativeWindowFrameView(NativeWindowWin* window); - virtual ~NativeWindowFrameView(); + explicit NativeWindowFrameView(NativeWindowAura* window); + ~NativeWindowFrameView() override; void Init(views::Widget* frame); // views::NonClientFrameView implementation. - virtual gfx::Rect GetBoundsForClientView() const OVERRIDE; - virtual gfx::Rect GetWindowBoundsForClientBounds( - const gfx::Rect& client_bounds) const OVERRIDE; - virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE; - virtual void GetWindowMask(const gfx::Size& size, - gfx::Path* window_mask) OVERRIDE; - virtual void ResetWindowControls() OVERRIDE {} - virtual void UpdateWindowIcon() OVERRIDE {} - virtual void UpdateWindowTitle() OVERRIDE {} + gfx::Rect GetBoundsForClientView() const override; + gfx::Rect GetWindowBoundsForClientBounds( + const gfx::Rect& client_bounds) const override; + int NonClientHitTest(const gfx::Point& point) override; + void GetWindowMask(const gfx::Size& size, + gfx::Path* window_mask) override; + void ResetWindowControls() override {} + void UpdateWindowIcon() override {} + void UpdateWindowTitle() override {} + void SizeConstraintsChanged() override {} // views::View implementation. - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual void Layout() OVERRIDE; - virtual const char* GetClassName() const OVERRIDE; - virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; - virtual gfx::Size GetMinimumSize() OVERRIDE; - virtual gfx::Size GetMaximumSize() OVERRIDE; + gfx::Size GetPreferredSize() const override; + void Layout() override; + const char* GetClassName() const override; + void OnPaint(gfx::Canvas* canvas) override; + gfx::Size GetMinimumSize() const override; + gfx::Size GetMaximumSize() const override; private: - NativeWindowWin* window_; + NativeWindowAura* window_; views::Widget* frame_; DISALLOW_COPY_AND_ASSIGN(NativeWindowFrameView); @@ -153,7 +196,7 @@ class NativeWindowFrameView : public views::NonClientFrameView { const char NativeWindowFrameView::kViewClassName[] = "content/nw/src/browser/NativeWindowFrameView"; -NativeWindowFrameView::NativeWindowFrameView(NativeWindowWin* window) +NativeWindowFrameView::NativeWindowFrameView(NativeWindowAura* window) : window_(window), frame_(NULL) { } @@ -230,7 +273,7 @@ void NativeWindowFrameView::GetWindowMask(const gfx::Size& size, // We got nothing to say about no window mask. } -gfx::Size NativeWindowFrameView::GetPreferredSize() { +gfx::Size NativeWindowFrameView::GetPreferredSize() const { gfx::Size pref = frame_->client_view()->GetPreferredSize(); gfx::Rect bounds(0, 0, pref.width(), pref.height()); return frame_->non_client_view()->GetWindowBoundsForClientBounds( @@ -247,22 +290,31 @@ const char* NativeWindowFrameView::GetClassName() const { return kViewClassName; } -gfx::Size NativeWindowFrameView::GetMinimumSize() { +gfx::Size NativeWindowFrameView::GetMinimumSize() const { return frame_->client_view()->GetMinimumSize(); } -gfx::Size NativeWindowFrameView::GetMaximumSize() { +gfx::Size NativeWindowFrameView::GetMaximumSize() const { return frame_->client_view()->GetMaximumSize(); } } // namespace -NativeWindowWin::NativeWindowWin(const base::WeakPtr& shell, +NativeWindowAura* NativeWindowAura::GetBrowserViewForNativeWindow( + gfx::NativeWindow window) { + views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window); + return widget ? + reinterpret_cast(widget->GetNativeWindowProperty( + "__BROWSER_VIEW__")) : nullptr; +} + +NativeWindowAura::NativeWindowAura(const base::WeakPtr& shell, base::DictionaryValue* manifest) : NativeWindow(shell, manifest), - web_view_(NULL), toolbar_(NULL), + web_view_(NULL), is_fullscreen_(false), + is_visible_on_all_workspaces_(false), is_minimized_(false), is_maximized_(false), is_focus_(false), @@ -274,14 +326,34 @@ NativeWindowWin::NativeWindowWin(const base::WeakPtr& shell, initial_focus_(true), last_width_(-1), last_height_(-1) { manifest->GetBoolean("focus", &initial_focus_); + manifest->GetBoolean("fullscreen", &is_fullscreen_); + manifest->GetBoolean("resizable", &resizable_); + manifest->GetBoolean("visible-on-all-workspaces", &is_visible_on_all_workspaces_); window_ = new views::Widget; views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW); params.delegate = this; - params.top_level = true; params.remove_standard_frame = !has_frame(); params.use_system_default_icon = true; + if (content::g_support_transparency && transparent_) + params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; + if (is_fullscreen_) + params.show_state = ui::SHOW_STATE_FULLSCREEN; + params.visible_on_all_workspaces = is_visible_on_all_workspaces_; +#if defined(OS_WIN) + if (has_frame()) + window_->set_frame_type(views::Widget::FRAME_TYPE_FORCE_NATIVE); +#endif window_->Init(params); + + // WS_CAPTION is needed or the size will be miscalculated on maximizing + // see upstream issue #224924 +#if defined(OS_WIN) + HWND hwnd = views::HWNDForWidget(window_->GetTopLevelWidget()); + int current_style = ::GetWindowLong(hwnd, GWL_STYLE); + ::SetWindowLong(hwnd, GWL_STYLE, current_style | WS_CAPTION); +#endif + if (!has_frame()) InstallEasyResizeTargeterOnContainer(); @@ -290,12 +362,13 @@ NativeWindowWin::NativeWindowWin(const base::WeakPtr& shell, int width, height; manifest->GetInteger(switches::kmWidth, &width); manifest->GetInteger(switches::kmHeight, &height); - gfx::Rect window_bounds = + gfx::Rect window_bounds = window_->non_client_view()->GetWindowBoundsForClientBounds( gfx::Rect(width,height)); last_width_ = width; last_height_ = height; window_->AddObserver(this); + window_->SetSize(window_bounds.size()); window_->CenterWindow(window_bounds.size()); @@ -303,59 +376,69 @@ NativeWindowWin::NativeWindowWin(const base::WeakPtr& shell, OnViewWasResized(); window_->SetInitialFocus(ui::SHOW_STATE_NORMAL); + + window_->SetNativeWindowProperty("__BROWSER_VIEW__", this); + +#ifdef OS_LINUX + SetDeskopEnvironment(); +#endif } -NativeWindowWin::~NativeWindowWin() { +NativeWindowAura::~NativeWindowAura() { +#if defined(OS_WIN) FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver, observer_list_, OnHostDestroying()); +#endif views::WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(this); } -void NativeWindowWin::Close() { +void NativeWindowAura::Close() { window_->Close(); } -void NativeWindowWin::Move(const gfx::Rect& bounds) { +void NativeWindowAura::Move(const gfx::Rect& bounds) { window_->SetBounds(bounds); } -void NativeWindowWin::Focus(bool focus) { +void NativeWindowAura::Focus(bool focus) { window_->Activate(); } -void NativeWindowWin::Show() { - VLOG(1) << "NativeWindowWin::Show(); initial_focus = " << initial_focus_; +void NativeWindowAura::Show() { + VLOG(1) << "NativeWindowAura::Show(); initial_focus = " << initial_focus_; if (is_maximized_) window_->native_widget_private()->ShowWithWindowState(ui::SHOW_STATE_MAXIMIZED); else if (!initial_focus_) { window_->set_focus_on_creation(false); window_->native_widget_private()->ShowWithWindowState(ui::SHOW_STATE_INACTIVE); + } else if (is_fullscreen_) { + window_->native_widget_private()->ShowWithWindowState(ui::SHOW_STATE_FULLSCREEN); } else window_->native_widget_private()->Show(); } -void NativeWindowWin::Hide() { +void NativeWindowAura::Hide() { window_->Hide(); } -void NativeWindowWin::Maximize() { +void NativeWindowAura::Maximize() { window_->Maximize(); } -void NativeWindowWin::Unmaximize() { +void NativeWindowAura::Unmaximize() { window_->Restore(); } -void NativeWindowWin::Minimize() { +void NativeWindowAura::Minimize() { window_->Minimize(); } -void NativeWindowWin::Restore() { +void NativeWindowAura::Restore() { window_->Restore(); } -void NativeWindowWin::SetFullscreen(bool fullscreen) { +void NativeWindowAura::SetFullscreen(bool fullscreen) { is_fullscreen_ = fullscreen; window_->SetFullscreen(fullscreen); if (shell()) { @@ -366,31 +449,120 @@ void NativeWindowWin::SetFullscreen(bool fullscreen) { } } -bool NativeWindowWin::IsFullscreen() { +bool NativeWindowAura::IsFullscreen() { return is_fullscreen_; } -void NativeWindowWin::SetSize(const gfx::Size& size) { +void NativeWindowAura::SetTransparent(bool transparent) { + if (!content::g_support_transparency) + return; +#if defined(OS_WIN) + // Check for Windows Vista or higher, transparency isn't supported in + // anything lower. + if (base::win::GetVersion() < base::win::VERSION_VISTA) { + NOTREACHED() << "The operating system does not support transparency."; + transparent_ = false; + return; + } + + // Check to see if composition is disabled, if so we have to throw an + // error, there's no graceful recovery, yet. TODO: Graceful recovery. + BOOL enabled = FALSE; + HRESULT result = ::DwmIsCompositionEnabled(&enabled); + if (!enabled || !SUCCEEDED(result)) { + NOTREACHED() << "Windows DWM composition is not enabled, transparency is not supported."; + transparent_ = false; + return; + } + + HWND hWnd = views::HWNDForWidget(window_); + + const int marginVal = transparent ? -1 : 0; + MARGINS mgMarInset = { marginVal, marginVal, marginVal, marginVal }; + if (DwmExtendFrameIntoClientArea(hWnd, &mgMarInset) != S_OK) { + NOTREACHED() << "Windows DWM extending to client area failed, transparency is not supported."; + transparent_ = false; + return; + } + + // this is needed, or transparency will fail if it defined on startup + bool change_window_style = false; + const LONG lastExStyle = GetWindowLong(hWnd, GWL_EXSTYLE); + const LONG exStyle = WS_EX_COMPOSITED; + const LONG newExStyle = transparent ? lastExStyle | exStyle : lastExStyle & ~exStyle; + SetWindowLong(hWnd, GWL_EXSTYLE, newExStyle); + change_window_style |= lastExStyle != newExStyle; + + if (change_window_style) { + window_->FrameTypeChanged(); + } + + if (content::g_force_cpu_draw && transparent) { + // Quick FIX where window content is not updated + Minimize(); + Restore(); + } + +#elif defined(USE_X11) && !defined(OS_CHROMEOS) + + static char cachedRes = -1; + if ( cachedRes<0 ) { + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + cachedRes = !command_line.HasSwitch(switches::kDisableGpu) || + !command_line.HasSwitch(views::switches::kEnableTransparentVisuals); + } + + if (cachedRes && transparent) { + LOG(INFO) << "if transparency does not work, try with --enable-transparent-visuals --disable-gpu"; + } + + #endif + + if (toolbar_) { + toolbar_->set_background(transparent ? views::Background::CreateSolidBackground(SK_ColorTRANSPARENT) : + views::Background::CreateStandardPanelBackground()); + toolbar_->SchedulePaint(); + } + + // this is needed to prevent white popup on startup + content::RenderWidgetHostView* rwhv = shell_->web_contents()->GetRenderWidgetHostView(); + if (rwhv) { + if (transparent) + rwhv->SetBackgroundColor(SK_ColorTRANSPARENT); + else + rwhv->SetBackgroundColorToDefault(); + } + + content::RenderViewHostImpl* rvh = static_cast(shell_->web_contents()->GetRenderViewHost()); + if (rvh) { + rvh->SetBackgroundOpaque(!transparent); + } + + transparent_ = transparent; +} + +void NativeWindowAura::SetSize(const gfx::Size& size) { window_->SetSize(size); } -gfx::Size NativeWindowWin::GetSize() { +gfx::Size NativeWindowAura::GetSize() { return window_->GetWindowBoundsInScreen().size(); } -void NativeWindowWin::SetMinimumSize(int width, int height) { +void NativeWindowAura::SetMinimumSize(int width, int height) { minimum_size_.set_width(width); minimum_size_.set_height(height); } -void NativeWindowWin::SetMaximumSize(int width, int height) { +void NativeWindowAura::SetMaximumSize(int width, int height) { maximum_size_.set_width(width); maximum_size_.set_height(height); } -void NativeWindowWin::SetResizable(bool resizable) { +void NativeWindowAura::SetResizable(bool resizable) { resizable_ = resizable; +#if 0 //FIXME // Show/Hide the maximize button. DWORD style = ::GetWindowLong(views::HWNDForWidget(window_), GWL_STYLE); if (resizable) @@ -398,13 +570,15 @@ void NativeWindowWin::SetResizable(bool resizable) { else style &= ~WS_MAXIMIZEBOX; ::SetWindowLong(views::HWNDForWidget(window_), GWL_STYLE, style); +#endif } -void NativeWindowWin::SetShowInTaskbar(bool show) { +void NativeWindowAura::SetShowInTaskbar(bool show) { +#if defined(OS_WIN) if (show == false && base::win::GetVersion() < base::win::VERSION_VISTA) { // Change the owner of native window. Only needed on Windows XP. ::SetWindowLong(views::HWNDForWidget(window_), - GWL_HWNDPARENT, + GWLP_HWNDPARENT, (LONG)ui::GetHiddenWindow()); } @@ -431,16 +605,36 @@ void NativeWindowWin::SetShowInTaskbar(bool show) { LOG(ERROR) << "Failed to change the show in taskbar attribute"; return; } +#endif } -void NativeWindowWin::SetAlwaysOnTop(bool top) { +void NativeWindowAura::SetAlwaysOnTop(bool top) { window_->StackAtTop(); // SetAlwaysOnTop should be called after StackAtTop because otherwise // the top-most flag will be removed. window_->SetAlwaysOnTop(top); } -void NativeWindowWin::OnWidgetBoundsChanged(views::Widget* widget, const gfx::Rect& new_bounds) { +void NativeWindowAura::SetVisibleOnAllWorkspaces(bool all_workspaces) { + is_visible_on_all_workspaces_ = all_workspaces; + window_->SetVisibleOnAllWorkspaces(all_workspaces); +} + +void NativeWindowAura::OnWidgetActivationChanged(views::Widget* widget, bool active) { + if (active) { + if (shell()) + shell()->SendEvent("focus"); + is_focus_ = true; + is_blur_ = false; + }else{ + if (shell()) + shell()->SendEvent("blur"); + is_focus_ = false; + is_blur_ = true; + } +} + +void NativeWindowAura::OnWidgetBoundsChanged(views::Widget* widget, const gfx::Rect& new_bounds) { int w = new_bounds.width(); int h = new_bounds.height(); if (shell() && (w != last_width_ || h != last_height_)) { @@ -453,11 +647,12 @@ void NativeWindowWin::OnWidgetBoundsChanged(views::Widget* widget, const gfx::Re } } -void NativeWindowWin::SetPosition(const std::string& position) { +void NativeWindowAura::SetPosition(const std::string& position) { if (position == "center") { gfx::Rect bounds = window_->GetWindowBoundsInScreen(); window_->CenterWindow(gfx::Size(bounds.width(), bounds.height())); } else if (position == "mouse") { +#if defined(OS_WIN) //FIXME gfx::Rect bounds = window_->GetWindowBoundsInScreen(); POINT pt; if (::GetCursorPos(&pt)) { @@ -467,22 +662,39 @@ void NativeWindowWin::SetPosition(const std::string& position) { bounds.set_y(y > 0 ? y : 0); window_->SetBoundsConstrained(bounds); } +#endif } } -void NativeWindowWin::SetPosition(const gfx::Point& position) { +void NativeWindowAura::SetPosition(const gfx::Point& position) { gfx::Rect bounds = window_->GetWindowBoundsInScreen(); window_->SetBounds(gfx::Rect(position, bounds.size())); } -gfx::Point NativeWindowWin::GetPosition() { +gfx::Point NativeWindowAura::GetPosition() { return window_->GetWindowBoundsInScreen().origin(); } -void NativeWindowWin::FlashFrame(bool flash) { - window_->FlashFrame(flash); +void NativeWindowAura::FlashFrame(int count) { +#if defined(OS_WIN) //FIXME + FLASHWINFO fwi; + fwi.cbSize = sizeof(fwi); + fwi.hwnd = views::HWNDForWidget(window_); + if (count != 0) { + fwi.dwFlags = FLASHW_ALL; + fwi.uCount = count < 0 ? 4 : count; + fwi.dwTimeout = 0; + } + else { + fwi.dwFlags = FLASHW_STOP; + } + FlashWindowEx(&fwi); +#elif defined(OS_LINUX) + window_->FlashFrame(count); +#endif } +#if defined(OS_WIN) HICON createBadgeIcon(const HWND hWnd, const TCHAR *value, const int sizeX, const int sizeY) { // canvas for the overlay icon gfx::Canvas canvas(gfx::Size(sizeX, sizeY), 1, false); @@ -504,8 +716,10 @@ HICON createBadgeIcon(const HWND hWnd, const TCHAR *value, const int sizeX, cons // return the canvas as windows native icon handle return IconUtil::CreateHICONFromSkBitmap(canvas.ExtractImageRep().sk_bitmap()); } +#endif -void NativeWindowWin::SetBadgeLabel(const std::string& badge) { +void NativeWindowAura::SetBadgeLabel(const std::string& badge) { +#if defined(OS_WIN) base::win::ScopedComPtr taskbar; HRESULT result = taskbar.CreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER); @@ -528,65 +742,117 @@ void NativeWindowWin::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 } -void NativeWindowWin::SetKiosk(bool kiosk) { +void NativeWindowAura::SetProgressBar(double progress) { +#if defined(OS_WIN) + base::win::ScopedComPtr taskbar; + HRESULT result = taskbar.CreateInstance(CLSID_TaskbarList, NULL, + CLSCTX_INPROC_SERVER); + + if (FAILED(result)) { + VLOG(1) << "Failed creating a TaskbarList3 object: " << result; + return; + } + + result = taskbar->HrInit(); + if (FAILED(result)) { + LOG(ERROR) << "Failed initializing an ITaskbarList3 interface."; + return; + } + + HWND hWnd = views::HWNDForWidget(window_); + + TBPFLAG tbpFlag = TBPF_NOPROGRESS; + + if (progress > 1) { + tbpFlag = TBPF_INDETERMINATE; + } + else if (progress >= 0) { + tbpFlag = TBPF_NORMAL; + taskbar->SetProgressValue(hWnd, progress * 100, 100); + } + + taskbar->SetProgressState(hWnd, tbpFlag); +#elif defined(OS_LINUX) + views::LinuxUI::instance()->SetProgressFraction(progress); +#endif +} + +void NativeWindowAura::SetKiosk(bool kiosk) { SetFullscreen(kiosk); } -bool NativeWindowWin::IsKiosk() { +bool NativeWindowAura::IsKiosk() { return IsFullscreen(); } -void NativeWindowWin::SetMenu(nwapi::Menu* menu) { +void NativeWindowAura::SetMenu(nwapi::Menu* menu) { window_->set_has_menu_bar(true); menu_ = menu; - +#if defined(OS_LINUX) + MenuBarView* menubar = new MenuBarView(); + GetBrowserViewLayout()->set_menu_bar(menubar); + AddChildView(menubar); + menubar->UpdateMenu(menu->model()); + Layout(); + SchedulePaint(); +#endif // The menu is lazily built. +#if defined(OS_WIN) //FIXME menu->Rebuild(); menu->SetWindow(this); // menu is nwapi::Menu, menu->menu_ is NativeMenuWin, ::SetMenu(views::HWNDForWidget(window_), menu->menu_->GetNativeMenu()); +#endif menu->UpdateKeys( window_->GetFocusManager() ); } -void NativeWindowWin::SetTitle(const std::string& title) { +BrowserViewLayout* NativeWindowAura::GetBrowserViewLayout() const { + return static_cast(GetLayoutManager()); +} + +void NativeWindowAura::SetTitle(const std::string& title) { title_ = title; window_->UpdateWindowTitle(); } -void NativeWindowWin::AddToolbar() { - toolbar_ = new NativeWindowToolbarWin(shell()); +void NativeWindowAura::AddToolbar() { + toolbar_ = new NativeWindowToolbarAura(shell()); + GetBrowserViewLayout()->set_tool_bar(toolbar_); AddChildViewAt(toolbar_, 0); } -void NativeWindowWin::SetToolbarButtonEnabled(TOOLBAR_BUTTON button, +void NativeWindowAura::SetToolbarButtonEnabled(TOOLBAR_BUTTON button, bool enabled) { if (toolbar_) toolbar_->SetButtonEnabled(button, enabled); } -void NativeWindowWin::SetToolbarUrlEntry(const std::string& url) { +void NativeWindowAura::SetToolbarUrlEntry(const std::string& url) { if (toolbar_) toolbar_->SetUrlEntry(url); } - -void NativeWindowWin::SetToolbarIsLoading(bool loading) { + +void NativeWindowAura::SetToolbarIsLoading(bool loading) { if (toolbar_) toolbar_->SetIsLoading(loading); } -views::View* NativeWindowWin::GetContentsView() { +views::View* NativeWindowAura::GetContentsView() { return this; } -views::ClientView* NativeWindowWin::CreateClientView(views::Widget* widget) { +views::ClientView* NativeWindowAura::CreateClientView(views::Widget* widget) { return new NativeWindowClientView(widget, GetContentsView(), shell_); } -views::NonClientFrameView* NativeWindowWin::CreateNonClientFrameView( +views::NonClientFrameView* NativeWindowAura::CreateNonClientFrameView( views::Widget* widget) { if (has_frame()) return new views::NativeFrameView(GetWidget()); @@ -596,7 +862,7 @@ views::NonClientFrameView* NativeWindowWin::CreateNonClientFrameView( return frame_view; } -void NativeWindowWin::OnWidgetMove() { +void NativeWindowAura::OnWidgetMove() { gfx::Point origin = GetPosition(); if (shell()) { base::ListValue args; @@ -606,39 +872,43 @@ void NativeWindowWin::OnWidgetMove() { } } -bool NativeWindowWin::CanResize() const { +bool NativeWindowAura::CanResize() const { return resizable_; } -bool NativeWindowWin::CanMaximize() const { +bool NativeWindowAura::CanMaximize() const { return resizable_; } -views::Widget* NativeWindowWin::GetWidget() { +bool NativeWindowAura::CanMinimize() const { + return true; +} + +views::Widget* NativeWindowAura::GetWidget() { return window_; } -const views::Widget* NativeWindowWin::GetWidget() const { +const views::Widget* NativeWindowAura::GetWidget() const { return window_; } -base::string16 NativeWindowWin::GetWindowTitle() const { +base::string16 NativeWindowAura::GetWindowTitle() const { return base::UTF8ToUTF16(title_); } -void NativeWindowWin::DeleteDelegate() { +void NativeWindowAura::DeleteDelegate() { OnNativeWindowDestory(); } -bool NativeWindowWin::ShouldShowWindowTitle() const { +bool NativeWindowAura::ShouldShowWindowTitle() const { return has_frame(); } -bool NativeWindowWin::ShouldHandleOnSize() const { +bool NativeWindowAura::ShouldHandleOnSize() const { return true; } -void NativeWindowWin::OnNativeFocusChange(gfx::NativeView focused_before, +void NativeWindowAura::OnNativeFocusChange(gfx::NativeView focused_before, gfx::NativeView focused_now) { gfx::NativeView this_window = GetWidget()->GetNativeView(); if (IsParent(focused_now, this_window) || @@ -658,7 +928,7 @@ void NativeWindowWin::OnNativeFocusChange(gfx::NativeView focused_before, } } -gfx::ImageSkia NativeWindowWin::GetWindowAppIcon() { +gfx::ImageSkia NativeWindowAura::GetWindowAppIcon() { gfx::Image icon = app_icon(); if (icon.IsEmpty()) return gfx::ImageSkia(); @@ -674,7 +944,7 @@ gfx::ImageSkia NativeWindowWin::GetWindowAppIcon() { #endif } -gfx::ImageSkia NativeWindowWin::GetWindowIcon() { +gfx::ImageSkia NativeWindowAura::GetWindowIcon() { gfx::Image icon = app_icon(); if (icon.IsEmpty()) return gfx::ImageSkia(); @@ -682,11 +952,11 @@ gfx::ImageSkia NativeWindowWin::GetWindowIcon() { return *icon.ToImageSkia(); } -views::View* NativeWindowWin::GetInitiallyFocusedView() { +views::View* NativeWindowAura::GetInitiallyFocusedView() { return web_view_; } -void NativeWindowWin::UpdateDraggableRegions( +void NativeWindowAura::UpdateDraggableRegions( const std::vector& regions) { // Draggable region is not supported for non-frameless window. if (has_frame()) @@ -712,7 +982,7 @@ void NativeWindowWin::UpdateDraggableRegions( OnViewWasResized(); } -void NativeWindowWin::HandleKeyboardEvent( +void NativeWindowAura::HandleKeyboardEvent( const content::NativeWebKeyboardEvent& event) { unhandled_keyboard_event_handler_.HandleKeyboardEvent(event, GetFocusManager()); @@ -723,7 +993,8 @@ void NativeWindowWin::HandleKeyboardEvent( // event.os_event.wParam, event.os_event.lParam); } -void NativeWindowWin::Layout() { +#if 0 +void NativeWindowAura::Layout() { DCHECK(web_view_); if (toolbar_) { toolbar_->SetBounds(0, 0, width(), 34); @@ -733,37 +1004,51 @@ void NativeWindowWin::Layout() { } OnViewWasResized(); } +#endif -void NativeWindowWin::ViewHierarchyChanged( +void NativeWindowAura::ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) { if (details.is_add && details.child == this) { - views::BoxLayout* layout = new views::BoxLayout( - views::BoxLayout::kVertical, 0, 0, 0); + BrowserViewLayout* layout = new BrowserViewLayout(); SetLayoutManager(layout); web_view_ = new views::WebView(NULL); web_view_->SetWebContents(web_contents()); + layout->set_web_view(web_view_); AddChildView(web_view_); } } -gfx::Size NativeWindowWin::GetMinimumSize() { +gfx::Size NativeWindowAura::GetMinimumSize() const { return minimum_size_; } -gfx::Size NativeWindowWin::GetMaximumSize() { +gfx::Size NativeWindowAura::GetMaximumSize() const { return maximum_size_; } -void NativeWindowWin::OnFocus() { +void NativeWindowAura::OnFocus() { web_view_->RequestFocus(); } -void NativeWindowWin::SetInitialFocus(bool initial_focus) { +bool NativeWindowAura::InitialFocus() { + return initial_focus_; +} + +bool NativeWindowAura::AcceleratorPressed(const ui::Accelerator& accelerator) { + return true; +} + +bool NativeWindowAura::CanHandleAccelerators() const { + return true; +} + +void NativeWindowAura::SetInitialFocus(bool initial_focus) { initial_focus_ = initial_focus; } -bool NativeWindowWin::ExecuteWindowsCommand(int command_id) { +bool NativeWindowAura::ExecuteWindowsCommand(int command_id) { +#if defined(OS_WIN) // 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; @@ -781,10 +1066,34 @@ bool NativeWindowWin::ExecuteWindowsCommand(int command_id) { if (shell()) shell()->SendEvent("unmaximize"); } +#endif return false; } -bool NativeWindowWin::HandleSize(unsigned int param, const gfx::Size& size) { +void NativeWindowAura::HandleWMStateUpdate() { + if (window_->IsMaximized()) { + if (!is_maximized_ && shell()) + shell()->SendEvent("maximize"); + is_maximized_ = true; + }else if (is_maximized_) { + if (shell()) + shell()->SendEvent("unmaximize"); + is_maximized_ = false; + } + + if (window_->IsMinimized()) { + if (!is_minimized_ && shell()) + shell()->SendEvent("minimize"); + is_minimized_ = true; + }else if (is_minimized_) { + if (shell()) + shell()->SendEvent("restore"); + is_minimized_ = false; + } +} + +bool NativeWindowAura::HandleSize(unsigned int param, const gfx::Size& size) { +#if defined(OS_WIN) if (param == SIZE_MAXIMIZED) { is_maximized_ = true; if (shell()) @@ -794,32 +1103,35 @@ bool NativeWindowWin::HandleSize(unsigned int param, const gfx::Size& size) { if (shell()) shell()->SendEvent("unmaximize"); } +#endif return false; } -bool NativeWindowWin::ExecuteAppCommand(int command_id) { +bool NativeWindowAura::ExecuteAppCommand(int command_id) { +#if defined(OS_WIN) if (menu_) { menu_->menu_delegate_->ExecuteCommand(command_id, 0); menu_->menu_->UpdateStates(); } - +#endif return false; } -void NativeWindowWin::SaveWindowPlacement(const gfx::Rect& bounds, +void NativeWindowAura::SaveWindowPlacement(const gfx::Rect& bounds, ui::WindowShowState show_state) { // views::WidgetDelegate::SaveWindowPlacement(bounds, show_state); } -void NativeWindowWin::OnViewWasResized() { +void NativeWindowAura::OnViewWasResized() { // Set the window shape of the RWHV. +#if defined(OS_WIN) DCHECK(window_); DCHECK(web_view_); gfx::Size sz = web_view_->size(); int height = sz.height(), width = sz.width(); gfx::Path path; path.addRect(0, 0, width, height); - SetWindowRgn((HWND)web_contents()->GetView()->GetNativeView(), + SetWindowRgn((HWND)web_contents()->GetNativeView(), (HRGN)path.CreateNativeRegion(), 1); @@ -841,9 +1153,10 @@ void NativeWindowWin::OnViewWasResized() { if (web_contents()->GetRenderViewHost()->GetView()) web_contents()->GetRenderViewHost()->GetView()->SetClickthroughRegion(rgn); #endif +#endif } -void NativeWindowWin::InstallEasyResizeTargeterOnContainer() { +void NativeWindowAura::InstallEasyResizeTargeterOnContainer() { aura::Window* window = window_->GetNativeWindow(); gfx::Insets inset(kResizeInsideBoundsSize, kResizeInsideBoundsSize, kResizeInsideBoundsSize, kResizeInsideBoundsSize); @@ -854,11 +1167,11 @@ void NativeWindowWin::InstallEasyResizeTargeterOnContainer() { new wm::EasyResizeWindowTargeter(window, inset, inset))); } -bool NativeWindowWin::ShouldDescendIntoChildForEventHandling( +bool NativeWindowAura::ShouldDescendIntoChildForEventHandling( gfx::NativeView child, const gfx::Point& location) { #if defined(USE_AURA) - if (child->Contains(web_view_->web_contents()->GetView()->GetNativeView())) { + if (child->Contains(web_view_->web_contents()->GetNativeView())) { // App window should claim mouse events that fall within the draggable // region. return !draggable_region_.get() || @@ -869,26 +1182,26 @@ bool NativeWindowWin::ShouldDescendIntoChildForEventHandling( return true; } -gfx::NativeView NativeWindowWin::GetHostView() const { +gfx::NativeView NativeWindowAura::GetHostView() const { return window_->GetNativeView(); } -gfx::Size NativeWindowWin::GetMaximumDialogSize() { +gfx::Size NativeWindowAura::GetMaximumDialogSize() { return window_->GetWindowBoundsInScreen().size(); } -gfx::Point NativeWindowWin::GetDialogPosition(const gfx::Size& size) { +gfx::Point NativeWindowAura::GetDialogPosition(const gfx::Size& size) { gfx::Size app_window_size = window_->GetWindowBoundsInScreen().size(); return gfx::Point(app_window_size.width() / 2 - size.width() / 2, app_window_size.height() / 2 - size.height() / 2); } -void NativeWindowWin::AddObserver(web_modal::ModalDialogHostObserver* observer) { +void NativeWindowAura::AddObserver(web_modal::ModalDialogHostObserver* observer) { if (observer && !observer_list_.HasObserver(observer)) observer_list_.AddObserver(observer); } -void NativeWindowWin::RemoveObserver(web_modal::ModalDialogHostObserver* observer) { +void NativeWindowAura::RemoveObserver(web_modal::ModalDialogHostObserver* observer) { observer_list_.RemoveObserver(observer); } diff --git a/src/browser/native_window_aura.h b/src/browser/native_window_aura.h new file mode 100644 index 0000000000..ea71601df8 --- /dev/null +++ b/src/browser/native_window_aura.h @@ -0,0 +1,217 @@ +// 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_BROWSER_NATIVE_WINDOW_WIN_H_ +#define CONTENT_NW_SRC_BROWSER_NATIVE_WINDOW_WIN_H_ + +#include "content/nw/src/browser/native_window.h" + +#include "third_party/skia/include/core/SkRegion.h" +#if defined(OS_WIN) +#include "ui/base/win/hidden_window.h" +#endif +#include "ui/gfx/image/image_skia.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/views/focus/widget_focus_manager.h" +#include "ui/views/widget/widget_delegate.h" +#include "ui/views/widget/widget_observer.h" + +#include "ui/base/accelerators/accelerator.h" +#include "ui/base/accelerators/accelerator_manager.h" +#include "ui/views/controls/webview/unhandled_keyboard_event_handler.h" +#include "ui/events/keycodes/keyboard_codes.h" + +namespace views { +class WebView; +} + +namespace nwapi { +class Menu; +} + +namespace nw { + +class BrowserViewLayout; +class NativeWindowToolbarAura; + +class NativeWindowAura : public NativeWindow, + public views::WidgetFocusChangeListener, + public views::WidgetDelegateView, + public views::WidgetObserver { + public: + explicit NativeWindowAura(const base::WeakPtr& shell, + base::DictionaryValue* manifest); + ~NativeWindowAura() final; + static NativeWindowAura* GetBrowserViewForNativeWindow(gfx::NativeWindow window); + + SkRegion* draggable_region() { return draggable_region_.get(); } + NativeWindowToolbarAura* toolbar() { return toolbar_; } + views::Widget* window() { return window_; } + + BrowserViewLayout* GetBrowserViewLayout() const; + + // NativeWindow implementation. + void Close() override; + void Move(const gfx::Rect& pos) override; + void Focus(bool focus) override; + void Show() override; + void Hide() override; + void Maximize() override; + void Unmaximize() override; + void Minimize() override; + void Restore() override; + void SetFullscreen(bool fullscreen) override; + bool IsFullscreen() override; + void SetTransparent(bool transparent) override; + void SetSize(const gfx::Size& size) override; + gfx::Size GetSize() override; + void SetMinimumSize(int width, int height) override; + void SetMaximumSize(int width, int height) override; + void SetResizable(bool resizable) override; + void SetAlwaysOnTop(bool top) override; + void SetShowInTaskbar(bool show = true) override; + void SetVisibleOnAllWorkspaces(bool all_workspaces) override; + void SetPosition(const std::string& position) override; + void SetPosition(const gfx::Point& position) override; + gfx::Point GetPosition() override; + void SetTitle(const std::string& title) override; + void FlashFrame(int count) override; + void SetKiosk(bool kiosk) override; + void SetBadgeLabel(const std::string& badge) override; + void SetProgressBar(double progress) override; + bool IsKiosk() override; + void SetMenu(nwapi::Menu* menu) override; + void SetToolbarButtonEnabled(TOOLBAR_BUTTON button, + bool enabled) override; + void SetToolbarUrlEntry(const std::string& url) override; + void SetToolbarIsLoading(bool loading) override; + void SetInitialFocus(bool initial_focus) override; + bool InitialFocus() override; + + // WidgetDelegate implementation. + void OnWidgetMove() override; + views::View* GetContentsView() override; + views::ClientView* CreateClientView(views::Widget*) override; + views::NonClientFrameView* CreateNonClientFrameView( + views::Widget* widget) override; + bool CanResize() const override; + bool CanMaximize() const override; + bool CanMinimize() const override; + views::Widget* GetWidget() override; + const views::Widget* GetWidget() const override; + base::string16 GetWindowTitle() const override; + void DeleteDelegate() override; + views::View* GetInitiallyFocusedView() override; + gfx::ImageSkia GetWindowAppIcon() override; + gfx::ImageSkia GetWindowIcon() override; + bool ShouldShowWindowTitle() const override; + bool ShouldHandleOnSize() const override; + void HandleWMStateUpdate() override; + + views::UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_; + + + + // WidgetFocusChangeListener implementation. + void OnNativeFocusChange(gfx::NativeView focused_before, + gfx::NativeView focused_now) override; + + // WidgetObserver implementation + void OnWidgetBoundsChanged(views::Widget* widget, const gfx::Rect& new_bounds) override; + void OnWidgetActivationChanged(views::Widget* widget, + bool active) override; + + bool AcceleratorPressed(const ui::Accelerator& accelerator) override; + + bool CanHandleAccelerators() const override; + + gfx::NativeView GetHostView() const override; + gfx::Point GetDialogPosition(const gfx::Size& size) override; + void AddObserver(web_modal::ModalDialogHostObserver* observer) override; + void RemoveObserver(web_modal::ModalDialogHostObserver* observer) override; + gfx::Size GetMaximumDialogSize() override; + + protected: + // NativeWindow implementation. + void AddToolbar() override; + void UpdateDraggableRegions( + const std::vector& regions) override; + void HandleKeyboardEvent( + const content::NativeWebKeyboardEvent& event) override; + + // views::View implementation. + // virtual void Layout() override; + void ViewHierarchyChanged( + const ViewHierarchyChangedDetails& details) override; + gfx::Size GetMinimumSize() const override; + gfx::Size GetMaximumSize() const override; + void OnFocus() override; + + // views::WidgetDelegate implementation. + bool ExecuteWindowsCommand(int command_id) override; + bool HandleSize(unsigned int param, const gfx::Size& size) override; + bool ExecuteAppCommand(int command_id) override; + void SaveWindowPlacement(const gfx::Rect& bounds, + ui::WindowShowState show_state) override; + bool ShouldDescendIntoChildForEventHandling( + gfx::NativeView child, + const gfx::Point& location) override; + private: + friend class content::Shell; + friend class nwapi::Menu; + + void OnViewWasResized(); + void InstallEasyResizeTargeterOnContainer(); + + NativeWindowToolbarAura* toolbar_; + views::WebView* web_view_; + views::Widget* window_; + bool is_fullscreen_; + bool is_visible_on_all_workspaces_; + + // Flags used to prevent sending extra events. + bool is_minimized_; + bool is_maximized_; + bool is_focus_; + bool is_blur_; + + scoped_ptr draggable_region_; + // The window's menubar. + nwapi::Menu* menu_; + + bool resizable_; + std::string title_; + gfx::Size minimum_size_; + gfx::Size maximum_size_; + + bool initial_focus_; + + int last_width_; + int last_height_; + + bool super_down_; + bool meta_down_; + ObserverList observer_list_; + DISALLOW_COPY_AND_ASSIGN(NativeWindowAura); +}; + +} // namespace nw + +#endif // CONTENT_NW_SRC_BROWSER_NATIVE_WINDOW_WIN_H_ diff --git a/src/browser/native_window_gtk.cc b/src/browser/native_window_gtk.cc index 50d24b4bfd..08e93fa86a 100644 --- a/src/browser/native_window_gtk.cc +++ b/src/browser/native_window_gtk.cc @@ -23,19 +23,28 @@ #include #include "base/values.h" -#include "chrome/browser/ui/gtk/gtk_window_util.h" +#include "base/environment.h" +//#include "chrome/browser/ui/gtk/gtk_window_util.h" +//#include "chrome/browser/ui/gtk/unity_service.h" #include "extensions/common/draggable_region.h" #include "content/nw/src/api/menu/menu.h" #include "content/nw/src/common/shell_switches.h" #include "content/nw/src/nw_shell.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_view.h" #include "content/public/common/renderer_preferences.h" #include "ui/base/x/x11_util.h" -#include "ui/gfx/gtk_util.h" #include "ui/gfx/rect.h" -#include "ui/gfx/skia_utils_gtk.h" +//#include "ui/gfx/skia_utils_gtk.h" + +namespace ShellIntegrationLinux { +std::string GetDesktopName(base::Environment* env) { + std::string name; + if (env->GetVar("NW_DESKTOP", &name) && !name.empty()) + return name; + return "nw.desktop"; +} +} namespace nw { @@ -67,10 +76,12 @@ NativeWindowGtk::NativeWindowGtk(const base::WeakPtr& shell, gtk_widget_show(vbox_); gtk_container_add(GTK_CONTAINER(window_), vbox_); +#if 0 //FIXME // Set window icon. gfx::Image icon = app_icon(); if (!icon.IsEmpty()) gtk_window_set_icon(window_, icon.ToGdkPixbuf()); +#endif // Always create toolbar since we need to create a url entry. CreateToolbar(); @@ -82,7 +93,7 @@ NativeWindowGtk::NativeWindowGtk(const base::WeakPtr& shell, gtk_box_pack_start(GTK_BOX(vbox_), toolbar_, FALSE, FALSE, 0); gfx::NativeView native_view = - web_contents()->GetView()->GetNativeView(); + web_contents()->GetNativeView(); gtk_widget_show(native_view); gtk_container_add(GTK_CONTAINER(vbox_), native_view); @@ -294,12 +305,20 @@ void NativeWindowGtk::SetTitle(const std::string& title) { gtk_window_set_title(GTK_WINDOW(window_), title.c_str()); } -void NativeWindowGtk::FlashFrame(bool flash) { - gtk_window_set_urgency_hint(window_, flash); +void NativeWindowGtk::FlashFrame(int count) { + gtk_window_set_urgency_hint(window_, count); } void NativeWindowGtk::SetBadgeLabel(const std::string& badge) { - // TODO + if (unity::IsRunning()) { + unity::SetDownloadCount(atoi(badge.c_str())); + } +} + +void NativeWindowGtk::SetProgressBar(double progress) { + if (unity::IsRunning()) { + unity::SetProgressFraction(progress); + } } void NativeWindowGtk::SetKiosk(bool kiosk) { diff --git a/src/browser/native_window_gtk.h b/src/browser/native_window_gtk.h index ed1c89159e..c0a9111519 100644 --- a/src/browser/native_window_gtk.h +++ b/src/browser/native_window_gtk.h @@ -26,7 +26,7 @@ #include "content/nw/src/browser/native_window.h" #include "third_party/skia/include/core/SkRegion.h" -#include "ui/base/gtk/gtk_signal.h" +#include "chrome/browser/ui/libgtk2ui/gtk2_signal.h" namespace nw { @@ -48,6 +48,7 @@ class NativeWindowGtk : public NativeWindow { virtual void Restore() OVERRIDE; virtual void SetFullscreen(bool fullscreen) OVERRIDE; virtual bool IsFullscreen() OVERRIDE; + virtual void SetTransparent(bool transparent) OVERRIDE; virtual void SetSize(const gfx::Size& size) OVERRIDE; virtual gfx::Size GetSize() OVERRIDE; virtual void SetMinimumSize(int width, int height) OVERRIDE; @@ -59,8 +60,9 @@ class NativeWindowGtk : public NativeWindow { virtual void SetPosition(const gfx::Point& position) OVERRIDE; virtual gfx::Point GetPosition() OVERRIDE; virtual void SetTitle(const std::string& title) OVERRIDE; - virtual void FlashFrame(bool flash) OVERRIDE; + virtual void FlashFrame(int count) OVERRIDE; virtual void SetBadgeLabel(const std::string& badge) OVERRIDE; + virtual void SetProgressBar(double progress) OVERRIDE; virtual void SetKiosk(bool kiosk) OVERRIDE; virtual bool IsKiosk() OVERRIDE; virtual void SetMenu(nwapi::Menu* menu) OVERRIDE; diff --git a/src/browser/native_window_mac.h b/src/browser/native_window_mac.h index b5a5817b63..f91240d7ff 100644 --- a/src/browser/native_window_mac.h +++ b/src/browser/native_window_mac.h @@ -30,6 +30,7 @@ @class ShellNSWindow; @class ShellToolbarDelegate; +@class NWMenuDelegate; class SkRegion; namespace nw { @@ -41,40 +42,43 @@ class NativeWindowCocoa : public NativeWindow { virtual ~NativeWindowCocoa(); // NativeWindow implementation. - virtual void Close() OVERRIDE; - virtual void Move(const gfx::Rect& pos) OVERRIDE; - virtual void Focus(bool focus) OVERRIDE; - virtual void Show() OVERRIDE; - virtual void Hide() OVERRIDE; - virtual void Maximize() OVERRIDE; - virtual void Unmaximize() OVERRIDE; - virtual void Minimize() OVERRIDE; - virtual void Restore() OVERRIDE; - virtual void SetFullscreen(bool fullscreen) OVERRIDE; - virtual bool IsFullscreen() OVERRIDE; - virtual void SetSize(const gfx::Size& size) OVERRIDE; - virtual gfx::Size GetSize() OVERRIDE; - virtual void SetMinimumSize(int width, int height) OVERRIDE; - virtual void SetMaximumSize(int width, int height) OVERRIDE; - virtual void SetResizable(bool resizable) OVERRIDE; - virtual void SetAlwaysOnTop(bool top) OVERRIDE; - virtual void SetShowInTaskbar(bool show = true) OVERRIDE; - virtual void SetPosition(const std::string& position) OVERRIDE; - virtual void SetPosition(const gfx::Point& position) OVERRIDE; - virtual gfx::Point GetPosition() OVERRIDE; - virtual void SetTitle(const std::string& title) OVERRIDE; - virtual void FlashFrame(bool flash) OVERRIDE; - virtual void SetBadgeLabel(const std::string& badge) OVERRIDE; - virtual void SetKiosk(bool kiosk) OVERRIDE; - virtual bool IsKiosk() OVERRIDE; - virtual void SetMenu(nwapi::Menu* menu) OVERRIDE; - virtual void ClearMenu() OVERRIDE; + virtual void Close() override; + virtual void Move(const gfx::Rect& pos) override; + virtual void Focus(bool focus) override; + virtual void Show() override; + virtual void Hide() override; + virtual void Maximize() override; + virtual void Unmaximize() override; + virtual void Minimize() override; + virtual void Restore() override; + virtual void SetFullscreen(bool fullscreen) override; + virtual bool IsFullscreen() override; + virtual void SetTransparent(bool transparent) override; + virtual void SetSize(const gfx::Size& size) override; + virtual gfx::Size GetSize() override; + virtual void SetMinimumSize(int width, int height) override; + virtual void SetMaximumSize(int width, int height) override; + virtual void SetResizable(bool resizable) override; + virtual void SetAlwaysOnTop(bool top) override; + virtual void SetShowInTaskbar(bool show = true) override; + virtual void SetVisibleOnAllWorkspaces(bool all_workspaces) override; + virtual void SetPosition(const std::string& position) override; + virtual void SetPosition(const gfx::Point& position) override; + virtual gfx::Point GetPosition() override; + virtual void SetTitle(const std::string& title) override; + virtual void FlashFrame(int count) override; + virtual void SetBadgeLabel(const std::string& badge) override; + virtual void SetProgressBar(double progress) override; + virtual void SetKiosk(bool kiosk) override; + virtual bool IsKiosk() override; + virtual void SetMenu(nwapi::Menu* menu) override; + virtual void ClearMenu() override; virtual void SetToolbarButtonEnabled(TOOLBAR_BUTTON button, - bool enabled) OVERRIDE; - virtual void SetToolbarUrlEntry(const std::string& url) OVERRIDE; - virtual void SetToolbarIsLoading(bool loading) OVERRIDE; - virtual void SetInitialFocus(bool accept_focus) OVERRIDE; - virtual bool InitialFocus() OVERRIDE; + bool enabled) override; + virtual void SetToolbarUrlEntry(const std::string& url) override; + virtual void SetToolbarIsLoading(bool loading) override; + virtual void SetInitialFocus(bool accept_focus) override; + virtual bool InitialFocus() override; // Called to handle a mouse event. void HandleMouseEvent(NSEvent* event); @@ -88,11 +92,11 @@ class NativeWindowCocoa : public NativeWindow { protected: // NativeWindow implementation. - virtual void AddToolbar() OVERRIDE; + virtual void AddToolbar() override; virtual void UpdateDraggableRegions( - const std::vector& regions) OVERRIDE; + const std::vector& regions) override; virtual void HandleKeyboardEvent( - const content::NativeWebKeyboardEvent& event) OVERRIDE; + const content::NativeWebKeyboardEvent& event) override; void SetNonLionFullscreen(bool fullscreen); @@ -110,6 +114,9 @@ class NativeWindowCocoa : public NativeWindow { // Delegate to the toolbar. base::scoped_nsobject toolbar_delegate_; + + // Data for transparency + NSColor *opaque_color_; bool is_fullscreen_; bool is_kiosk_; diff --git a/src/browser/native_window_mac.mm b/src/browser/native_window_mac.mm index 4a20b86a14..44ca8ac355 100644 --- a/src/browser/native_window_mac.mm +++ b/src/browser/native_window_mac.mm @@ -24,8 +24,9 @@ #include "base/strings/sys_string_conversions.h" #include "base/values.h" #import "chrome/browser/ui/cocoa/custom_frame_view.h" -#import "chrome/browser/ui/cocoa/nsview_additions.h" +#import "ui/base/cocoa/nsview_additions.h" #include "content/nw/src/api/menu/menu.h" +#include "content/nw/src/api/menu/menu_delegate_mac.h" #include "content/nw/src/api/app/app.h" #include "content/nw/src/browser/chrome_event_processing_window.h" #include "content/nw/src/browser/native_window_helper_mac.h" @@ -35,13 +36,18 @@ #include "content/nw/src/nw_package.h" #include "content/nw/src/nw_shell.h" #include "content/public/browser/native_web_keyboard_event.h" -#include "content/public/browser/render_widget_host_view.h" +#include "content/browser/renderer_host/render_widget_host_view_mac.h" +#include "content/browser/renderer_host/render_view_host_impl.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_view.h" #include "extensions/common/draggable_region.h" #include "third_party/skia/include/core/SkRegion.h" #import "ui/base/cocoa/underlay_opengl_hosting_window.h" +namespace content { + extern bool g_support_transparency; + extern bool g_force_cpu_draw; +} + @interface NSWindow (NSPrivateApis) - (void)setBottomCornerRounded:(BOOL)rounded; @end @@ -120,7 +126,7 @@ - (void)windowWillExitFullScreen:(NSNotification*)notification { - (void)windowDidBecomeKey:(NSNotification *)notification { if (shell_) { - shell_->web_contents()->GetView()->Focus(); + shell_->web_contents()->Focus(); shell_->SendEvent("focus"); } } @@ -283,7 +289,11 @@ - (void)drawCustomFrameRect:(NSRect)rect forView:(NSView*)view { [[NSBezierPath bezierPathWithRoundedRect:[view bounds] xRadius:cornerRadius yRadius:cornerRadius] addClip]; - [[NSColor whiteColor] set]; + if ([self isOpaque] || !content::g_support_transparency) + [[NSColor whiteColor] set]; + else + [[NSColor clearColor] set]; + NSRectFill(rect); } @@ -307,6 +317,41 @@ - (NSRect)contentRectForFrameRect:(NSRect)frameRect { @end +@interface NWProgressBar : NSProgressIndicator +@end + +@implementation NWProgressBar + +// override the drawing, so we can give color to the progress bar +- (void)drawRect:(NSRect)dirtyRect { + + [super drawRect:dirtyRect]; + + if(self.style != NSProgressIndicatorBarStyle) + return; + + NSRect sliceRect, remainderRect; + double progressFraction = ([self doubleValue] - [self minValue]) / + ([self maxValue] - [self minValue]); + + NSDivideRect(dirtyRect, &sliceRect, &remainderRect, + NSWidth(dirtyRect) * progressFraction, NSMinXEdge); + + const int kProgressBarCornerRadius = 3; + + if (progressFraction == 0.0) + return; + + NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:sliceRect + xRadius:kProgressBarCornerRadius + yRadius:kProgressBarCornerRadius]; + // blue color with alpha blend 0.5 + [[NSColor colorWithCalibratedRed:0 green:0 blue:1 alpha:0.5] set]; + [path fill]; +} +@end + + namespace nw { NativeWindowCocoa::NativeWindowCocoa( @@ -351,10 +396,13 @@ - (NSRect)contentRectForFrameRect:(NSRect)frameRect { defer:NO]; } window_ = shell_window; + opaque_color_ = [window() backgroundColor]; [shell_window setShell:shell]; - [[window() contentView] cr_setWantsLayer:YES]; + [[window() contentView] setWantsLayer:!content::g_force_cpu_draw]; [window() setDelegate:[[NativeWindowDelegate alloc] initWithShell:shell]]; + SetTransparent(transparent_); + // Disable fullscreen button when 'fullscreen' is specified to false. bool fullscreen; if (!(manifest->GetBoolean(switches::kmFullscreen, &fullscreen) && @@ -373,7 +421,7 @@ - (NSRect)contentRectForFrameRect:(NSRect)frameRect { [window() respondsToSelector:@selector(setBottomCornerRounded:)]) [window() setBottomCornerRounded:NO]; - NSView* view = web_contents()->GetView()->GetNativeView(); + NSView* view = web_contents()->GetNativeView(); [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; // By default, the whole frameless window is not draggable. @@ -390,7 +438,7 @@ - (NSRect)contentRectForFrameRect:(NSRect)frameRect { } void NativeWindowCocoa::InstallView() { - NSView* view = web_contents()->GetView()->GetNativeView(); + NSView* view = web_contents()->GetNativeView(); if (has_frame_) { [view setFrame:[[window() contentView] bounds]]; [[window() contentView] addSubview:view]; @@ -415,7 +463,7 @@ - (NSRect)contentRectForFrameRect:(NSRect)frameRect { } void NativeWindowCocoa::UninstallView() { - NSView* view = web_contents()->GetView()->GetNativeView(); + NSView* view = web_contents()->GetNativeView(); [view removeFromSuperview]; } @@ -436,6 +484,8 @@ - (NSRect)contentRectForFrameRect:(NSRect)frameRect { } void NativeWindowCocoa::Focus(bool focus) { + NSApplication *myApp = [NSApplication sharedApplication]; + [myApp activateIgnoringOtherApps:YES]; if (focus && [window() isVisible]) [window() makeKeyAndOrderFront:nil]; else @@ -444,9 +494,7 @@ - (NSRect)contentRectForFrameRect:(NSRect)frameRect { void NativeWindowCocoa::Show() { NSApplication *myApp = [NSApplication sharedApplication]; - [myApp activateIgnoringOtherApps:YES]; - content::RenderWidgetHostView* rwhv = - shell_->web_contents()->GetRenderWidgetHostView(); + [myApp activateIgnoringOtherApps:NO]; if (first_show_ && initial_focus_) { [window() makeKeyAndOrderFront:nil]; @@ -457,11 +505,12 @@ - (NSRect)contentRectForFrameRect:(NSRect)frameRect { // Chrome assumption is that becoming the first responder = you have focus // so we use this trick to refuse to become first responder during orderFrontRegardless - if (rwhv) - rwhv->SetTakesFocusOnlyOnMouseDown(true); + // TODO(roger) + // if (rwhv) + // rwhv->SetTakesFocusOnlyOnMouseDown(true); [window() orderFrontRegardless]; - if (rwhv) - rwhv->SetTakesFocusOnlyOnMouseDown(false); + // if (rwhv) + // rwhv->SetTakesFocusOnlyOnMouseDown(false); } first_show_ = false; } @@ -505,6 +554,51 @@ - (NSRect)contentRectForFrameRect:(NSRect)frameRect { return is_fullscreen_; } +//debug function to iterate all the sublayers +void SetTransparent (CALayer* layer, const bool transparent) { + if (layer == NULL) return; + [layer setBackgroundColor:CGColorGetConstantColor(transparent ? kCGColorClear : kCGColorWhite)]; + for (CALayer* l in layer.sublayers) { + SetTransparent(l, transparent); + } +} + +//debug function to iterate all the subviews +void SetTransparent (NSView * view, const bool transparent) { + if (view == NULL) return; + SetTransparent(view.layer, transparent); + for (NSView* v in view.subviews) + SetTransparent(v, transparent); +} + +void NativeWindowCocoa::SetTransparent(bool transparent) { + + if (!content::g_support_transparency) return; + if (!transparent_ && transparent) { + opaque_color_ = [window() backgroundColor]; + } + + [window() setHasShadow:transparent ? NO : YES]; + [window() setOpaque:transparent ? NO : YES]; + [window() setBackgroundColor:transparent ? [NSColor clearColor] : opaque_color_]; + + content::RenderWidgetHostViewMac* rwhv = static_cast(shell_->web_contents()->GetRenderWidgetHostView()); + content::RenderViewHostImpl* rvh = static_cast(shell_->web_contents()->GetRenderViewHost()); + + if (rwhv) { + [rwhv->background_layer_ setBackgroundColor:CGColorGetConstantColor(transparent ? kCGColorClear : kCGColorWhite)]; + } + + if (rvh) { + rvh->SetBackgroundOpaque(!transparent); + } + + //this is for debugging, iterate all the subviews / sublayers + //nw::SetTransparent((NSView*)[window()contentView], transparent); + + transparent_ = transparent; + +} void NativeWindowCocoa::SetNonLionFullscreen(bool fullscreen) { if (fullscreen == is_fullscreen_) return; @@ -591,7 +685,7 @@ - (NSRect)contentRectForFrameRect:(NSRect)frameRect { [window() setStyleMask:window().styleMask | NSResizableWindowMask]; } else { [[window() standardWindowButton:NSWindowZoomButton] setEnabled:NO]; - [window() setStyleMask:window().styleMask ^ NSResizableWindowMask]; + [window() setStyleMask:window().styleMask & ~NSResizableWindowMask]; } } @@ -599,6 +693,16 @@ - (NSRect)contentRectForFrameRect:(NSRect)frameRect { [window() setLevel:(top ? NSFloatingWindowLevel : NSNormalWindowLevel)]; } +void NativeWindowCocoa::SetVisibleOnAllWorkspaces(bool all_workspaces) { + NSUInteger collectionBehavior = [window() collectionBehavior]; + if (all_workspaces) { + collectionBehavior |= NSWindowCollectionBehaviorCanJoinAllSpaces; + } else { + collectionBehavior &= ~NSWindowCollectionBehaviorCanJoinAllSpaces; + } + [window() setCollectionBehavior:collectionBehavior]; +} + void NativeWindowCocoa::SetShowInTaskbar(bool show) { ProcessSerialNumber psn = { 0, kCurrentProcess }; if (!show) { @@ -636,9 +740,9 @@ - (NSRect)contentRectForFrameRect:(NSRect)frameRect { [window() setTitle:base::SysUTF8ToNSString(title)]; } -void NativeWindowCocoa::FlashFrame(bool flash) { - if (flash) { - attention_request_id_ = [NSApp requestUserAttention:NSInformationalRequest]; +void NativeWindowCocoa::FlashFrame(int count) { + if (count != 0) { + attention_request_id_ = count < 0 ? [NSApp requestUserAttention:NSInformationalRequest] : [NSApp requestUserAttention:NSCriticalRequest]; } else { [NSApp cancelUserAttentionRequest:attention_request_id_]; attention_request_id_ = 0; @@ -649,6 +753,60 @@ - (NSRect)contentRectForFrameRect:(NSRect)frameRect { [[NSApp dockTile] setBadgeLabel:base::SysUTF8ToNSString(badge)]; } + +void NativeWindowCocoa::SetProgressBar(double progress){ + NSDockTile *dockTile = [NSApp dockTile]; + NWProgressBar *progressIndicator = NULL; + + if (dockTile.contentView == NULL && progress >= 0) { + + // create image view to draw application icon + NSImageView *iv = [[NSImageView alloc] init]; + [iv setImage:[NSApp applicationIconImage]]; + + // set dockTile content view to app icon + [dockTile setContentView:iv]; + + progressIndicator = [[NWProgressBar alloc] + initWithFrame:NSMakeRect(0.0f, 0.0f, dockTile.size.width, 15.)]; + + [progressIndicator setStyle:NSProgressIndicatorBarStyle]; + + [progressIndicator setBezeled:YES]; + [progressIndicator setMinValue:0]; + [progressIndicator setMaxValue:1]; + [progressIndicator setHidden:NO]; + [progressIndicator setUsesThreadedAnimation:false]; + + // add progress indicator to image view + [iv addSubview:progressIndicator]; + } + + progressIndicator = (NWProgressBar*)[dockTile.contentView.subviews objectAtIndex:0]; + + if(progress >= 0) { + [progressIndicator setIndeterminate:progress > 1]; + if(progress > 1) { + // progress Indicator is indeterminate + // [progressIndicator startAnimation:window_]; + [progressIndicator setDoubleValue:1]; + } + else { + //[progressIndicator stopAnimation:window_]; + [progressIndicator setDoubleValue:progress]; + } + } + else { + // progress indicator < 0, destroy it + [[dockTile.contentView.subviews objectAtIndex:0]release]; + [dockTile.contentView release]; + dockTile.contentView = NULL; + } + + [dockTile display]; + +} + void NativeWindowCocoa::SetKiosk(bool kiosk) { if (kiosk) { NSApplicationPresentationOptions options = @@ -812,7 +970,7 @@ - (NSRect)contentRectForFrameRect:(NSRect)frameRect { void NativeWindowCocoa::UpdateDraggableRegionsForSystemDrag( const std::vector& regions, const extensions::DraggableRegion* draggable_area) { - NSView* web_view = web_contents()->GetView()->GetNativeView(); + NSView* web_view = web_contents()->GetNativeView(); NSInteger web_view_width = NSWidth([web_view bounds]); NSInteger web_view_height = NSHeight([web_view bounds]); @@ -882,7 +1040,7 @@ - (NSRect)contentRectForFrameRect:(NSRect)frameRect { const std::vector& regions) { // We still need one ControlRegionView to cover the whole window such that // mouse events could be captured. - NSView* web_view = web_contents()->GetView()->GetNativeView(); + NSView* web_view = web_contents()->GetNativeView(); gfx::Rect window_bounds( 0, 0, NSWidth([web_view bounds]), NSHeight([web_view bounds])); system_drag_exclude_areas_.clear(); @@ -912,7 +1070,7 @@ - (NSRect)contentRectForFrameRect:(NSRect)frameRect { // All ControlRegionViews should be added as children of the WebContentsView, // because WebContentsView will be removed and re-added when entering and // leaving fullscreen mode. - NSView* webView = web_contents()->GetView()->GetNativeView(); + NSView* webView = web_contents()->GetNativeView(); NSInteger webViewHeight = NSHeight([webView bounds]); // Remove all ControlRegionViews that are added last time. diff --git a/src/browser/native_window_toolbar_aura.cc b/src/browser/native_window_toolbar_aura.cc new file mode 100644 index 0000000000..cddc19b6f4 --- /dev/null +++ b/src/browser/native_window_toolbar_aura.cc @@ -0,0 +1,254 @@ +// 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/browser/native_window_toolbar_aura.h" + +#include "base/logging.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "content/nw/src/nw_shell.h" +#include "grit/nw_resources.h" +#include "grit/ui_resources.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/views/background.h" +#include "ui/views/controls/button/image_button.h" +#include "ui/views/controls/textfield/textfield.h" +#include "ui/views/layout/box_layout.h" + +namespace nw { + +const int kButtonMargin = 2; + +NativeWindowToolbarAura::NativeWindowToolbarAura(content::Shell* shell) + : shell_(shell) { +} + +NativeWindowToolbarAura::~NativeWindowToolbarAura() { +} + +views::View* NativeWindowToolbarAura::GetContentsView() { + return this; +} + +void NativeWindowToolbarAura::Layout() { + int panel_width = width(); + int x = kButtonMargin; + + // Place three left buttons. + gfx::Size sz = back_button_->GetPreferredSize(); + back_button_->SetBounds(x, (height() - sz.height()) / 2, + sz.width(), sz.height()); + x += sz.width() + kButtonMargin; + + sz = forward_button_->GetPreferredSize(); + forward_button_->SetBounds(x, back_button_->y(), + sz.width(), sz.height()); + x += sz.width() + kButtonMargin; + + sz = stop_or_refresh_button_->GetPreferredSize(); + stop_or_refresh_button_->SetBounds(x, back_button_->y(), + sz.width(), sz.height()); + x += sz.width() + kButtonMargin; + + // And place dev reload button as far as possible. + sz = dev_reload_button_->GetPreferredSize(); + dev_reload_button_->SetBounds(panel_width - sz.width() - kButtonMargin, + back_button_->y(), + sz.width(), + sz.height()); + + sz = devtools_button_->GetPreferredSize(); + devtools_button_->SetBounds(dev_reload_button_->x() - sz.width() - kButtonMargin, + back_button_->y(), + sz.width(), + sz.height()); + + // Stretch url entry. + url_entry_->SetBounds(x, + (height() - 24) / 2, + devtools_button_->x() - kButtonMargin - x, + 24); +} + +void NativeWindowToolbarAura::ViewHierarchyChanged( + const ViewHierarchyChangedDetails& details) { + if (details.is_add && details.child == this) + InitToolbar(); +} + +void NativeWindowToolbarAura::ContentsChanged( + views::Textfield* sender, + const base::string16& new_contents) { +} + +bool NativeWindowToolbarAura::HandleKeyEvent(views::Textfield* sender, + const ui::KeyEvent& key_event) { + if (key_event.key_code() == ui::VKEY_RETURN) { + base::string16 url_string = url_entry_->text(); + if (!url_string.empty()) { + GURL url(http://github.com/url_string); + if (!url.has_scheme()) +#if defined(OS_WIN) + url = GURL(L"http://" + url_string); +#else + url = GURL(base::UTF8ToUTF16("http://") + url_string); +#endif + shell_->LoadURL(url); + } + } + + return false; +} + +void NativeWindowToolbarAura::ButtonPressed(views::Button* sender, + const ui::Event& event) { + if (sender == back_button_) + shell_->GoBackOrForward(-1); + else if (sender == forward_button_) + shell_->GoBackOrForward(1); + else if (sender == stop_or_refresh_button_) + shell_->ReloadOrStop(); + else if (sender == devtools_button_) + shell_->ShowDevTools(); + else if (sender == dev_reload_button_) + shell_->Reload(content::Shell::RELOAD_DEV); + else + NOTREACHED() << "Click on unkown toolbar button."; +} + +void NativeWindowToolbarAura::InitToolbar() { + set_background(views::Background::CreateStandardPanelBackground()); + + views::BoxLayout* layout = new views::BoxLayout( + views::BoxLayout::kHorizontal, 5, 5, 10); + SetLayoutManager(layout); + + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + back_button_ = new views::ImageButton(this); + back_button_->SetImage(views::CustomButton::STATE_NORMAL, + rb.GetNativeImageNamed(IDR_NW_BACK).ToImageSkia()); + back_button_->SetImage(views::CustomButton::STATE_HOVERED, + rb.GetNativeImageNamed(IDR_NW_BACK_H).ToImageSkia()); + back_button_->SetImage(views::CustomButton::STATE_PRESSED, + rb.GetNativeImageNamed(IDR_NW_BACK_P).ToImageSkia()); + back_button_->SetImage(views::CustomButton::STATE_DISABLED, + rb.GetNativeImageNamed(IDR_NW_BACK_D).ToImageSkia()); + back_button_->SetAccessibleName(base::UTF8ToUTF16("Back")); + AddChildView(back_button_); + + forward_button_ = new views::ImageButton(this); + forward_button_->SetImage(views::CustomButton::STATE_NORMAL, + rb.GetNativeImageNamed(IDR_NW_FORWARD).ToImageSkia()); + forward_button_->SetImage(views::CustomButton::STATE_HOVERED, + rb.GetNativeImageNamed(IDR_NW_FORWARD_H).ToImageSkia()); + forward_button_->SetImage(views::CustomButton::STATE_PRESSED, + rb.GetNativeImageNamed(IDR_NW_FORWARD_P).ToImageSkia()); + forward_button_->SetImage(views::CustomButton::STATE_DISABLED, + rb.GetNativeImageNamed(IDR_NW_FORWARD_D).ToImageSkia()); + forward_button_->SetAccessibleName(base::UTF8ToUTF16("Forward")); + AddChildView(forward_button_); + + stop_or_refresh_button_ = new views::ImageButton(this); + SetIsLoading(true); + AddChildView(stop_or_refresh_button_); + + url_entry_ = new views::Textfield(); + url_entry_->set_controller(this); + AddChildView(url_entry_); + + devtools_button_ = new views::ImageButton(this); + devtools_button_->SetImage(views::CustomButton::STATE_NORMAL, + rb.GetNativeImageNamed(IDR_NW_TOOLS).ToImageSkia()); + devtools_button_->SetImage(views::CustomButton::STATE_HOVERED, + rb.GetNativeImageNamed(IDR_NW_TOOLS_H).ToImageSkia()); + devtools_button_->SetImage(views::CustomButton::STATE_PRESSED, + rb.GetNativeImageNamed(IDR_NW_TOOLS_P).ToImageSkia()); + devtools_button_->SetAccessibleName(base::UTF8ToUTF16("Devtools")); + AddChildView(devtools_button_); + + dev_reload_button_ = new views::ImageButton(this); + dev_reload_button_->SetImage(views::CustomButton::STATE_NORMAL, + rb.GetNativeImageNamed(IDR_NW_RELOAD).ToImageSkia()); + dev_reload_button_->SetImage(views::CustomButton::STATE_HOVERED, + rb.GetNativeImageNamed(IDR_NW_RELOAD_H).ToImageSkia()); + dev_reload_button_->SetImage(views::CustomButton::STATE_PRESSED, + rb.GetNativeImageNamed(IDR_NW_RELOAD_P).ToImageSkia()); + dev_reload_button_->SetImage(views::CustomButton::STATE_DISABLED, + rb.GetNativeImageNamed(IDR_NW_RELOAD_D).ToImageSkia()); + dev_reload_button_->SetAccessibleName(base::UTF8ToUTF16("Reload render process")); + AddChildView(dev_reload_button_); +} + +void NativeWindowToolbarAura::SetButtonEnabled( + NativeWindow::TOOLBAR_BUTTON button, + bool enabled) { + switch (button) { + case nw::NativeWindow::BUTTON_BACK: + back_button_->SetEnabled(enabled); + break; + case nw::NativeWindow::BUTTON_FORWARD: + forward_button_->SetEnabled(enabled); + break; + case nw::NativeWindow::BUTTON_REFRESH_OR_STOP: + stop_or_refresh_button_->SetEnabled(enabled); + break; + case nw::NativeWindow::BUTTON_DEVTOOLS: + devtools_button_->SetEnabled(enabled); + break; + case nw::NativeWindow::BUTTON_REFRESH_DEV: + dev_reload_button_->SetEnabled(enabled); + break; + } +} + +void NativeWindowToolbarAura::SetUrlEntry(const std::string& url) { + url_entry_->SetText(base::UTF8ToUTF16(url)); +} + +void NativeWindowToolbarAura::SetIsLoading(bool loading) { + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + + if (loading) { + stop_or_refresh_button_->SetImage(views::CustomButton::STATE_NORMAL, + rb.GetNativeImageNamed(IDR_NW_STOP).ToImageSkia()); + stop_or_refresh_button_->SetImage(views::CustomButton::STATE_HOVERED, + rb.GetNativeImageNamed(IDR_NW_STOP_H).ToImageSkia()); + stop_or_refresh_button_->SetImage(views::CustomButton::STATE_PRESSED, + rb.GetNativeImageNamed(IDR_NW_STOP_P).ToImageSkia()); + stop_or_refresh_button_->SetImage(views::CustomButton::STATE_DISABLED, + rb.GetNativeImageNamed(IDR_NW_STOP_D).ToImageSkia()); + stop_or_refresh_button_->SetAccessibleName(base::UTF8ToUTF16("Stop")); + } else { + stop_or_refresh_button_->SetImage(views::CustomButton::STATE_NORMAL, + rb.GetNativeImageNamed(IDR_NW_RELOAD).ToImageSkia()); + stop_or_refresh_button_->SetImage(views::CustomButton::STATE_HOVERED, + rb.GetNativeImageNamed(IDR_NW_RELOAD_H).ToImageSkia()); + stop_or_refresh_button_->SetImage(views::CustomButton::STATE_PRESSED, + rb.GetNativeImageNamed(IDR_NW_RELOAD_P).ToImageSkia()); + stop_or_refresh_button_->SetImage(views::CustomButton::STATE_DISABLED, + rb.GetNativeImageNamed(IDR_NW_RELOAD_D).ToImageSkia()); + stop_or_refresh_button_->SetAccessibleName(base::UTF8ToUTF16("Reload")); + } + + // Force refresh + stop_or_refresh_button_->SchedulePaint(); +} + +} // namespace nw diff --git a/src/browser/native_window_toolbar_win.h b/src/browser/native_window_toolbar_aura.h similarity index 72% rename from src/browser/native_window_toolbar_win.h rename to src/browser/native_window_toolbar_aura.h index a3358170f8..5cec67572d 100644 --- a/src/browser/native_window_toolbar_win.h +++ b/src/browser/native_window_toolbar_aura.h @@ -18,8 +18,8 @@ // 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_BROWSER_NATIVE_WINDOW_TOOLBAR_WIN_H_ -#define CONTENT_NW_SRC_BROWSER_NATIVE_WINDOW_TOOLBAR_WIN_H_ +#ifndef CONTENT_NW_SRC_BROWSER_NATIVE_WINDOW_TOOLBAR_AURA_H_ +#define CONTENT_NW_SRC_BROWSER_NATIVE_WINDOW_TOOLBAR_AURA_H_ #include "base/basictypes.h" #include "content/nw/src/browser/native_window.h" @@ -37,13 +37,13 @@ class Textfield; } namespace nw { - -class NativeWindowToolbarWin : public views::WidgetDelegateView, + +class NativeWindowToolbarAura : public views::WidgetDelegateView, public views::TextfieldController, public views::ButtonListener { public: - explicit NativeWindowToolbarWin(content::Shell* shell); - ~NativeWindowToolbarWin(); + explicit NativeWindowToolbarAura(content::Shell* shell); + ~NativeWindowToolbarAura() override; void SetButtonEnabled(NativeWindow::TOOLBAR_BUTTON button, bool enabled); @@ -52,22 +52,22 @@ class NativeWindowToolbarWin : public views::WidgetDelegateView, protected: // Overridden from WidgetDelegateView: - virtual views::View* GetContentsView() OVERRIDE; + views::View* GetContentsView() override; // Overridden from View: - virtual void Layout() OVERRIDE; - virtual void ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) OVERRIDE; + void Layout() override; + void ViewHierarchyChanged( + const ViewHierarchyChangedDetails& details) override; // Overridden from TextfieldController: - virtual void ContentsChanged(views::Textfield* sender, - const base::string16& new_contents) OVERRIDE; - virtual bool HandleKeyEvent(views::Textfield* sender, - const ui::KeyEvent& key_event) OVERRIDE; + void ContentsChanged(views::Textfield* sender, + const base::string16& new_contents) override; + bool HandleKeyEvent(views::Textfield* sender, + const ui::KeyEvent& key_event) override; // Overridden from ButtonListener: - virtual void ButtonPressed(views::Button* sender, - const ui::Event& event) OVERRIDE; + void ButtonPressed(views::Button* sender, + const ui::Event& event) override; private: void InitToolbar(); @@ -81,9 +81,9 @@ class NativeWindowToolbarWin : public views::WidgetDelegateView, views::ImageButton* devtools_button_; views::ImageButton* dev_reload_button_; - DISALLOW_COPY_AND_ASSIGN(NativeWindowToolbarWin); + DISALLOW_COPY_AND_ASSIGN(NativeWindowToolbarAura); }; } // namespace nw -#endif // CONTENT_NW_SRC_BROWSER_NATIVE_WINDOW_TOOLBAR_WIN_H_ +#endif // CONTENT_NW_SRC_BROWSER_NATIVE_WINDOW_TOOLBAR_AURA_H_ diff --git a/src/browser/native_window_toolbar_win.cc b/src/browser/native_window_toolbar_win.cc index 985169a031..270f4ef466 100644 --- a/src/browser/native_window_toolbar_win.cc +++ b/src/browser/native_window_toolbar_win.cc @@ -18,7 +18,7 @@ // 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/browser/native_window_toolbar_win.h" +#include "content/nw/src/browser/native_window_toolbar_aura.h" #include "base/logging.h" #include "base/strings/string16.h" @@ -36,18 +36,18 @@ namespace nw { const int kButtonMargin = 2; -NativeWindowToolbarWin::NativeWindowToolbarWin(content::Shell* shell) +NativeWindowToolbarAura::NativeWindowToolbarAura(content::Shell* shell) : shell_(shell) { } -NativeWindowToolbarWin::~NativeWindowToolbarWin() { +NativeWindowToolbarAura::~NativeWindowToolbarAura() { } -views::View* NativeWindowToolbarWin::GetContentsView() { +views::View* NativeWindowToolbarAura::GetContentsView() { return this; } -void NativeWindowToolbarWin::Layout() { +void NativeWindowToolbarAura::Layout() { int panel_width = width(); int x = kButtonMargin; @@ -87,18 +87,18 @@ void NativeWindowToolbarWin::Layout() { 24); } -void NativeWindowToolbarWin::ViewHierarchyChanged( +void NativeWindowToolbarAura::ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) { if (details.is_add && details.child == this) InitToolbar(); } -void NativeWindowToolbarWin::ContentsChanged( +void NativeWindowToolbarAura::ContentsChanged( views::Textfield* sender, const base::string16& new_contents) { } -bool NativeWindowToolbarWin::HandleKeyEvent(views::Textfield* sender, +bool NativeWindowToolbarAura::HandleKeyEvent(views::Textfield* sender, const ui::KeyEvent& key_event) { if (key_event.key_code() == ui::VKEY_RETURN) { base::string16 url_string = url_entry_->text(); @@ -113,7 +113,7 @@ bool NativeWindowToolbarWin::HandleKeyEvent(views::Textfield* sender, return false; } -void NativeWindowToolbarWin::ButtonPressed(views::Button* sender, +void NativeWindowToolbarAura::ButtonPressed(views::Button* sender, const ui::Event& event) { if (sender == back_button_) shell_->GoBackOrForward(-1); @@ -129,7 +129,7 @@ void NativeWindowToolbarWin::ButtonPressed(views::Button* sender, NOTREACHED() << "Click on unkown toolbar button."; } -void NativeWindowToolbarWin::InitToolbar() { +void NativeWindowToolbarAura::InitToolbar() { set_background(views::Background::CreateStandardPanelBackground()); views::BoxLayout* layout = new views::BoxLayout( @@ -192,7 +192,7 @@ void NativeWindowToolbarWin::InitToolbar() { AddChildView(dev_reload_button_); } -void NativeWindowToolbarWin::SetButtonEnabled( +void NativeWindowToolbarAura::SetButtonEnabled( NativeWindow::TOOLBAR_BUTTON button, bool enabled) { switch (button) { @@ -214,11 +214,11 @@ void NativeWindowToolbarWin::SetButtonEnabled( } } -void NativeWindowToolbarWin::SetUrlEntry(const std::string& url) { +void NativeWindowToolbarAura::SetUrlEntry(const std::string& url) { url_entry_->SetText(base::UTF8ToUTF16(url)); } -void NativeWindowToolbarWin::SetIsLoading(bool loading) { +void NativeWindowToolbarAura::SetIsLoading(bool loading) { ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); if (loading) { diff --git a/src/browser/native_window_win.h b/src/browser/native_window_win.h deleted file mode 100644 index c85bc0c2e6..0000000000 --- a/src/browser/native_window_win.h +++ /dev/null @@ -1,204 +0,0 @@ -// 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_BROWSER_NATIVE_WINDOW_WIN_H_ -#define CONTENT_NW_SRC_BROWSER_NATIVE_WINDOW_WIN_H_ - -#include "content/nw/src/browser/native_window.h" - -#include "third_party/skia/include/core/SkRegion.h" -#include "ui/base/win/hidden_window.h" -#include "ui/gfx/image/image_skia.h" -#include "ui/gfx/rect.h" -#include "ui/views/focus/widget_focus_manager.h" -#include "ui/views/widget/widget_delegate.h" -#include "ui/views/widget/widget_observer.h" - -#include "ui/base/accelerators/accelerator.h" -#include "ui/base/accelerators/accelerator_manager.h" -#include "ui/views/controls/webview/unhandled_keyboard_event_handler.h" -#include "ui/events/keycodes/keyboard_codes.h" - -namespace views { -class WebView; -} - -namespace nwapi { -class Menu; -} - -namespace nw { - -class NativeWindowToolbarWin; - -class NativeWindowWin : public NativeWindow, - public views::WidgetFocusChangeListener, - public views::WidgetDelegateView , - public views::WidgetObserver { - public: - explicit NativeWindowWin(const base::WeakPtr& shell, - base::DictionaryValue* manifest); - virtual ~NativeWindowWin(); - - SkRegion* draggable_region() { return draggable_region_.get(); } - NativeWindowToolbarWin* toolbar() { return toolbar_; } - views::Widget* window() { return window_; } - - // NativeWindow implementation. - virtual void Close() OVERRIDE; - virtual void Move(const gfx::Rect& pos) OVERRIDE; - virtual void Focus(bool focus) OVERRIDE; - virtual void Show() OVERRIDE; - virtual void Hide() OVERRIDE; - virtual void Maximize() OVERRIDE; - virtual void Unmaximize() OVERRIDE; - virtual void Minimize() OVERRIDE; - virtual void Restore() OVERRIDE; - virtual void SetFullscreen(bool fullscreen) OVERRIDE; - virtual bool IsFullscreen() OVERRIDE; - virtual void SetSize(const gfx::Size& size) OVERRIDE; - virtual gfx::Size GetSize() OVERRIDE; - virtual void SetMinimumSize(int width, int height) OVERRIDE; - virtual void SetMaximumSize(int width, int height) OVERRIDE; - virtual void SetResizable(bool resizable) OVERRIDE; - virtual void SetAlwaysOnTop(bool top) OVERRIDE; - virtual void SetShowInTaskbar(bool show = true) OVERRIDE; - virtual void SetPosition(const std::string& position) OVERRIDE; - virtual void SetPosition(const gfx::Point& position) OVERRIDE; - virtual gfx::Point GetPosition() OVERRIDE; - virtual void SetTitle(const std::string& title) OVERRIDE; - virtual void FlashFrame(bool flash) OVERRIDE; - virtual void SetKiosk(bool kiosk) OVERRIDE; - virtual void SetBadgeLabel(const std::string& badge) OVERRIDE; - virtual bool IsKiosk() OVERRIDE; - virtual void SetMenu(nwapi::Menu* menu) OVERRIDE; - virtual void SetToolbarButtonEnabled(TOOLBAR_BUTTON button, - bool enabled) OVERRIDE; - virtual void SetToolbarUrlEntry(const std::string& url) OVERRIDE; - virtual void SetToolbarIsLoading(bool loading) OVERRIDE; - virtual void SetInitialFocus(bool initial_focus) OVERRIDE; - virtual bool InitialFocus() OVERRIDE { return initial_focus_; } - - // WidgetDelegate implementation. - virtual void OnWidgetMove() OVERRIDE; - virtual views::View* GetContentsView() OVERRIDE; - virtual views::ClientView* CreateClientView(views::Widget*) OVERRIDE; - virtual views::NonClientFrameView* CreateNonClientFrameView( - views::Widget* widget) OVERRIDE; - virtual bool CanResize() const OVERRIDE; - virtual bool CanMaximize() const OVERRIDE; - virtual views::Widget* GetWidget() OVERRIDE; - virtual const views::Widget* GetWidget() const OVERRIDE; - virtual base::string16 GetWindowTitle() const OVERRIDE; - virtual void DeleteDelegate() OVERRIDE; - virtual views::View* GetInitiallyFocusedView() OVERRIDE; - virtual gfx::ImageSkia GetWindowAppIcon() OVERRIDE; - virtual gfx::ImageSkia GetWindowIcon() OVERRIDE; - virtual bool ShouldShowWindowTitle() const OVERRIDE; - virtual bool ShouldHandleOnSize() const OVERRIDE; - views::UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_; - - - - // WidgetFocusChangeListener implementation. - virtual void OnNativeFocusChange(gfx::NativeView focused_before, - gfx::NativeView focused_now) OVERRIDE; - - // WidgetObserver implementation - virtual void OnWidgetBoundsChanged(views::Widget* widget, const gfx::Rect& new_bounds) OVERRIDE; - - virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE{ - return true; - } - virtual bool CanHandleAccelerators() const OVERRIDE{ - return true; - } - virtual gfx::NativeView GetHostView() const OVERRIDE; - virtual gfx::Point GetDialogPosition(const gfx::Size& size) OVERRIDE; - virtual void AddObserver(web_modal::ModalDialogHostObserver* observer) OVERRIDE; - virtual void RemoveObserver(web_modal::ModalDialogHostObserver* observer) OVERRIDE; - virtual gfx::Size GetMaximumDialogSize() OVERRIDE; - - protected: - // NativeWindow implementation. - virtual void AddToolbar() OVERRIDE; - virtual void UpdateDraggableRegions( - const std::vector& regions) OVERRIDE; - virtual void HandleKeyboardEvent( - const content::NativeWebKeyboardEvent& event) OVERRIDE; - - // views::View implementation. - virtual void Layout() OVERRIDE; - virtual void ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) OVERRIDE; - virtual gfx::Size GetMinimumSize() OVERRIDE; - virtual gfx::Size GetMaximumSize() OVERRIDE; - virtual void OnFocus() OVERRIDE; - - // views::WidgetDelegate implementation. - virtual bool ExecuteWindowsCommand(int command_id) OVERRIDE; - virtual bool HandleSize(unsigned int param, const gfx::Size& size) OVERRIDE; - virtual bool ExecuteAppCommand(int command_id) OVERRIDE; - virtual void SaveWindowPlacement(const gfx::Rect& bounds, - ui::WindowShowState show_state) OVERRIDE; - virtual bool ShouldDescendIntoChildForEventHandling( - gfx::NativeView child, - const gfx::Point& location) OVERRIDE; - private: - friend class content::Shell; - friend class nwapi::Menu; - - void OnViewWasResized(); - void InstallEasyResizeTargeterOnContainer(); - - NativeWindowToolbarWin* toolbar_; - views::WebView* web_view_; - views::Widget* window_; - bool is_fullscreen_; - - // Flags used to prevent sending extra events. - bool is_minimized_; - bool is_maximized_; - bool is_focus_; - bool is_blur_; - - scoped_ptr draggable_region_; - // The window's menubar. - nwapi::Menu* menu_; - - bool resizable_; - std::string title_; - gfx::Size minimum_size_; - gfx::Size maximum_size_; - - bool initial_focus_; - - int last_width_; - int last_height_; - - bool super_down_; - bool meta_down_; - ObserverList observer_list_; - DISALLOW_COPY_AND_ASSIGN(NativeWindowWin); -}; - -} // namespace nw - -#endif // CONTENT_NW_SRC_BROWSER_NATIVE_WINDOW_WIN_H_ diff --git a/src/browser/nw_autofill_client.cc b/src/browser/nw_autofill_client.cc new file mode 100644 index 0000000000..b91a1ec7e5 --- /dev/null +++ b/src/browser/nw_autofill_client.cc @@ -0,0 +1,158 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/browser/nw_autofill_client.h" + +#include "base/logging.h" +#include "base/prefs/pref_service.h" +#include "components/autofill/content/browser/content_autofill_driver.h" +#include "components/autofill/content/common/autofill_messages.h" +#include "components/autofill/core/common/autofill_pref_names.h" +#include "content/nw/src/browser/autofill_popup_controller_impl.h" +#include "content/nw/src/browser/nw_form_database_service.h" +#include "content/nw/src/shell_browser_context.h" +#include "content/public/browser/render_view_host.h" +#include "ui/gfx/geometry/rect.h" + +#if defined(OS_ANDROID) +#include "chrome/browser/ui/android/autofill/autofill_logger_android.h" +#endif + +DEFINE_WEB_CONTENTS_USER_DATA_KEY(autofill::NWAutofillClient); + +namespace autofill { + +NWAutofillClient::NWAutofillClient(content::WebContents* web_contents) + : content::WebContentsObserver(web_contents), web_contents_(web_contents) { + DCHECK(web_contents); +#if defined(OS_MACOSX) && !defined(OS_IOS) + RegisterForKeystoneNotifications(); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) +} + +NWAutofillClient::~NWAutofillClient() { + // NOTE: It is too late to clean up the autofill popup; that cleanup process + // requires that the WebContents instance still be valid and it is not at + // this point (in particular, the WebContentsImpl destructor has already + // finished running and we are now in the base class destructor). + DCHECK(!popup_controller_); +#if defined(OS_MACOSX) && !defined(OS_IOS) + UnregisterFromKeystoneNotifications(); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) +} + +void NWAutofillClient::TabActivated() { +} + +PersonalDataManager* NWAutofillClient::GetPersonalDataManager() { + return NULL; +} + +scoped_refptr NWAutofillClient::GetDatabase() { + nw::NwFormDatabaseService* service = + static_cast( + web_contents_->GetBrowserContext())->GetFormDatabaseService(); + return service->get_autofill_webdata_service(); +} + +PrefService* NWAutofillClient::GetPrefs() { + return NULL; +} + +void NWAutofillClient::ShowAutofillSettings() { + NOTIMPLEMENTED(); +} + +void NWAutofillClient::ConfirmSaveCreditCard( + const base::Closure& save_card_callback) { + NOTIMPLEMENTED(); +} + +void NWAutofillClient::ShowRequestAutocompleteDialog( + const FormData& form, + content::RenderFrameHost* render_frame_host, + const ResultCallback& callback) { + NOTIMPLEMENTED(); +} + +void NWAutofillClient::ShowAutofillPopup( + const gfx::RectF& element_bounds, + base::i18n::TextDirection text_direction, + const std::vector& suggestions, + base::WeakPtr delegate) { + // Convert element_bounds to be in screen space. + gfx::Rect client_area = web_contents_->GetContainerBounds(); + gfx::RectF element_bounds_in_screen_space = + element_bounds + client_area.OffsetFromOrigin(); + + // Will delete or reuse the old |popup_controller_|. + popup_controller_ = + AutofillPopupControllerImpl::GetOrCreate(popup_controller_, + delegate, + web_contents(), + web_contents()->GetNativeView(), + element_bounds_in_screen_space, + text_direction); + + popup_controller_->Show(suggestions); +} + +void NWAutofillClient::UpdateAutofillPopupDataListValues( + const std::vector& values, + const std::vector& labels) { + if (popup_controller_.get()) + popup_controller_->UpdateDataListValues(values, labels); +} + +void NWAutofillClient::HideAutofillPopup() { + if (popup_controller_.get()) + popup_controller_->Hide(); +} + +bool NWAutofillClient::IsAutocompleteEnabled() { + return true; +} + +void NWAutofillClient::HideRequestAutocompleteDialog() { + NOTIMPLEMENTED(); +} + +void NWAutofillClient::WebContentsDestroyed() { + HideAutofillPopup(); +} + +void NWAutofillClient::DetectAccountCreationForms( + content::RenderFrameHost* rfh, + const std::vector& forms) { + NOTIMPLEMENTED(); +} + +void NWAutofillClient::DidFillOrPreviewField( + const base::string16& autofilled_value, + const base::string16& profile_full_name) { +} + +bool NWAutofillClient::HasCreditCardScanFeature() { + return false; +} + +void NWAutofillClient::ScanCreditCard(const CreditCardScanCallback& callback) { + NOTIMPLEMENTED(); +} + +void NWAutofillClient::ShowUnmaskPrompt( + const autofill::CreditCard& card, + base::WeakPtr delegate) { + NOTIMPLEMENTED(); +} + +void NWAutofillClient::OnUnmaskVerificationResult(bool success) { + NOTIMPLEMENTED(); +} + +void NWAutofillClient::OnFirstUserGestureObserved() { + //NOTIMPLEMENTED(); +} + +} // namespace autofill diff --git a/src/browser/nw_autofill_client.h b/src/browser/nw_autofill_client.h new file mode 100644 index 0000000000..4a41bc4aa7 --- /dev/null +++ b/src/browser/nw_autofill_client.h @@ -0,0 +1,112 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NW_BROWSER_UI_AUTOFILL_CHROME_AUTOFILL_CLIENT_H_ +#define NW_BROWSER_UI_AUTOFILL_CHROME_AUTOFILL_CLIENT_H_ + +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/i18n/rtl.h" +#include "base/memory/weak_ptr.h" +#include "components/autofill/core/browser/autofill_client.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_contents_user_data.h" + +namespace content { +struct FrameNavigateParams; +struct LoadCommittedDetails; +class WebContents; +} + +namespace autofill { + +class AutofillDialogController; +class AutofillKeystoneBridgeWrapper; +class AutofillPopupControllerImpl; +struct FormData; + +// Chrome implementation of AutofillClient. +class NWAutofillClient + : public AutofillClient, + public content::WebContentsUserData, + public content::WebContentsObserver { + public: + ~NWAutofillClient() final; + + // Called when the tab corresponding to |this| instance is activated. + void TabActivated(); + + // AutofillClient: + PersonalDataManager* GetPersonalDataManager() override; + scoped_refptr GetDatabase() override; + PrefService* GetPrefs() override; + void HideRequestAutocompleteDialog() override; + void ShowAutofillSettings() override; + bool HasCreditCardScanFeature() override; + void ScanCreditCard(const CreditCardScanCallback& callback) override; + void ConfirmSaveCreditCard( + const base::Closure& save_card_callback) override; + void ShowRequestAutocompleteDialog( + const FormData& form, + content::RenderFrameHost* render_frame_host, + const ResultCallback& callback) override; + void ShowAutofillPopup( + const gfx::RectF& element_bounds, + base::i18n::TextDirection text_direction, + const std::vector& suggestions, + base::WeakPtr delegate) override; + void UpdateAutofillPopupDataListValues( + const std::vector& values, + const std::vector& labels) override; + void HideAutofillPopup() override; + bool IsAutocompleteEnabled() override; + void DetectAccountCreationForms( + content::RenderFrameHost* rfh, + const std::vector& forms) override; + void DidFillOrPreviewField( + const base::string16& autofilled_value, + const base::string16& profile_full_name) override; + + // content::WebContentsObserver implementation. + void WebContentsDestroyed() override; + void ShowUnmaskPrompt( + const autofill::CreditCard& card, + base::WeakPtr delegate) override; + void OnUnmaskVerificationResult(bool success) override; + void OnFirstUserGestureObserved() override; + + private: +#if defined(OS_MACOSX) && !defined(OS_IOS) + // Creates |bridge_wrapper_|, which is responsible for dealing with Keystone + // notifications. + void RegisterForKeystoneNotifications(); + + // Deletes |bridge_wrapper_|. + void UnregisterFromKeystoneNotifications(); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + + explicit NWAutofillClient(content::WebContents* web_contents); + friend class content::WebContentsUserData; + + content::WebContents* const web_contents_; + // base::WeakPtr dialog_controller_; + base::WeakPtr popup_controller_; + +#if defined(OS_MACOSX) && !defined(OS_IOS) + // Listens to Keystone notifications and passes relevant ones on to the + // PersonalDataManager. + // + // The class of this member must remain a forward declaration, even in the + // .cc implementation file, since the class is defined in a Mac-only + // implementation file. This means that the pointer cannot be wrapped in a + // scoped_ptr. + // AutofillKeystoneBridgeWrapper* bridge_wrapper_; +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + + DISALLOW_COPY_AND_ASSIGN(NWAutofillClient); +}; + +} // namespace autofill + +#endif // CHROME_BROWSER_UI_AUTOFILL_CHROME_AUTOFILL_CLIENT_H_ diff --git a/src/browser/nw_autofill_client_mac.mm b/src/browser/nw_autofill_client_mac.mm new file mode 100644 index 0000000000..783572495b --- /dev/null +++ b/src/browser/nw_autofill_client_mac.mm @@ -0,0 +1,21 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/browser/nw_autofill_client.h" + +#import + +#include "base/logging.h" +#include "base/mac/scoped_nsobject.h" +#include "components/autofill/core/browser/personal_data_manager.h" + +namespace autofill { + +void NWAutofillClient::RegisterForKeystoneNotifications() { +} + +void NWAutofillClient::UnregisterFromKeystoneNotifications() { +} + +} // namespace autofill diff --git a/src/browser/nw_constrained_window_views_client.cc b/src/browser/nw_constrained_window_views_client.cc new file mode 100644 index 0000000000..b332316cdf --- /dev/null +++ b/src/browser/nw_constrained_window_views_client.cc @@ -0,0 +1,139 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/browser/nw_constrained_window_views_client.h" + +#include "base/memory/scoped_ptr.h" +#include "base/observer_list.h" +#include "components/constrained_window/constrained_window_views.h" +#include "components/constrained_window/constrained_window_views_client.h" +#include "components/web_modal/web_contents_modal_dialog_host.h" +#include "extensions/browser/guest_view/guest_view_base.h" +#include "ui/aura/window.h" +#include "ui/aura/window_observer.h" +#include "ui/aura/window_property.h" + +DECLARE_WINDOW_PROPERTY_TYPE(web_modal::ModalDialogHost*); + +namespace nw { +namespace { + +// Provides the host environment for web modal dialogs. See +// web_modal::WebContentsModalDialogHost, and ModalDialogHost for more +// details. +class ModalDialogHostImpl : public web_modal::WebContentsModalDialogHost, + public aura::WindowObserver { + public: + // Returns a modal dialog host for |window|. If it doesn't exist it creates + // one and stores it as owned property. + static ModalDialogHost* Get(aura::Window* window); + + private: + explicit ModalDialogHostImpl(aura::Window* host_window) + : host_window_(host_window) { + host_window_->AddObserver(this); + } + ~ModalDialogHostImpl() override {} + + // web_modal::ModalDialogHost: + gfx::NativeView GetHostView() const override { + return host_window_; + } + gfx::Point GetDialogPosition(const gfx::Size& size) override { + gfx::Size app_window_size = host_window_->GetBoundsInScreen().size(); + return gfx::Point(app_window_size.width() / 2 - size.width() / 2, + app_window_size.height() / 2 - size.height() / 2); + } + void AddObserver(web_modal::ModalDialogHostObserver* observer) override { + observer_list_.AddObserver(observer); + } + void RemoveObserver(web_modal::ModalDialogHostObserver* observer) override { + observer_list_.RemoveObserver(observer); + } + + // web_modal::WebContensModalDialogHost: + gfx::Size GetMaximumDialogSize() override { + return host_window_->bounds().size(); + } + + // aura::WindowObserver: + void OnWindowDestroying(aura::Window* window) override { + if (window != host_window_) + return; + host_window_->RemoveObserver(this); + FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver, + observer_list_, + OnHostDestroying()); + } + void OnWindowBoundsChanged(aura::Window* window, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) override { + if (window != host_window_) + return; + FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver, + observer_list_, + OnPositionRequiresUpdate()); + } + + aura::Window* host_window_; + ObserverList observer_list_; + + DISALLOW_COPY_AND_ASSIGN(ModalDialogHostImpl); +}; + +// A window property key to store the modal dialog host for +// dialogs created with the window as its parent. +DEFINE_OWNED_WINDOW_PROPERTY_KEY(web_modal::ModalDialogHost, + kModalDialogHostKey, + nullptr); + +// static +web_modal::ModalDialogHost* ModalDialogHostImpl::Get( + aura::Window* window) { + web_modal::ModalDialogHost* host = window->GetProperty(kModalDialogHostKey); + if (!host) { + host = new ModalDialogHostImpl(window); + window->SetProperty(kModalDialogHostKey, host); + } + return host; +} + +class NWConstrainedWindowViewsClient + : public constrained_window::ConstrainedWindowViewsClient { + public: + NWConstrainedWindowViewsClient() {} + ~NWConstrainedWindowViewsClient() override {} + + private: + // ConstrainedWindowViewsClient: + content::WebContents* GetEmbedderWebContents( + content::WebContents* initiator_web_contents) override { + extensions::GuestViewBase* guest_view = + extensions::GuestViewBase::FromWebContents(initiator_web_contents); + return guest_view && guest_view->embedder_web_contents() ? + guest_view->embedder_web_contents() : initiator_web_contents; + } + web_modal::ModalDialogHost* GetModalDialogHost( + gfx::NativeWindow parent) override { + return ModalDialogHostImpl::Get(parent); + } + gfx::NativeView GetDialogHostView(gfx::NativeWindow parent) override { + return parent; + } + + DISALLOW_COPY_AND_ASSIGN(NWConstrainedWindowViewsClient); +}; + +} // namespace + +void InstallConstrainedWindowViewsClient() { + constrained_window::SetConstrainedWindowViewsClient( + make_scoped_ptr(new NWConstrainedWindowViewsClient)); +} + +void UninstallConstrainedWindowViewsClient() { + constrained_window::SetConstrainedWindowViewsClient(nullptr); +} + +} // namespace athena diff --git a/src/browser/nw_constrained_window_views_client.h b/src/browser/nw_constrained_window_views_client.h new file mode 100644 index 0000000000..3c7d37ff01 --- /dev/null +++ b/src/browser/nw_constrained_window_views_client.h @@ -0,0 +1,15 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NW_CONSTRAINED_WINDOW_VIEWS_CLIENT_H_ +#define NW_CONSTRAINED_WINDOW_VIEWS_CLIENT_H_ + +namespace nw { + +void InstallConstrainedWindowViewsClient(); +void UninstallConstrainedWindowViewsClient(); + +} + +#endif diff --git a/src/browser/nw_form_database_service.h b/src/browser/nw_form_database_service.h index ce7b132ce2..62a9338d18 100644 --- a/src/browser/nw_form_database_service.h +++ b/src/browser/nw_form_database_service.h @@ -25,7 +25,7 @@ class NwFormDatabaseService : public WebDataServiceConsumer { public: NwFormDatabaseService(const base::FilePath path); - virtual ~NwFormDatabaseService(); + ~NwFormDatabaseService() final; void Shutdown(); @@ -40,9 +40,9 @@ class NwFormDatabaseService : public WebDataServiceConsumer { get_autofill_webdata_service(); // WebDataServiceConsumer implementation. - virtual void OnWebDataServiceRequestDone( + void OnWebDataServiceRequestDone( WebDataServiceBase::Handle h, - const WDTypedResult* result) OVERRIDE; + const WDTypedResult* result) override; private: struct PendingQuery { diff --git a/src/browser/pepper/chrome_browser_pepper_host_factory.cc b/src/browser/pepper/chrome_browser_pepper_host_factory.cc new file mode 100644 index 0000000000..bc7bd41f73 --- /dev/null +++ b/src/browser/pepper/chrome_browser_pepper_host_factory.cc @@ -0,0 +1,94 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/browser/pepper/chrome_browser_pepper_host_factory.h" + +#include "build/build_config.h" +#include "content/nw/src/browser/pepper/pepper_broker_message_filter.h" +#include "content/nw/src/browser/pepper/pepper_flash_browser_host.h" +#include "content/nw/src/browser/pepper/pepper_flash_clipboard_message_filter.h" +#include "content/nw/src/browser/pepper/pepper_isolated_file_system_message_filter.h" +#include "content/public/browser/browser_ppapi_host.h" +#include "ppapi/host/message_filter_host.h" +#include "ppapi/host/ppapi_host.h" +#include "ppapi/host/resource_host.h" +#include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/shared_impl/ppapi_permissions.h" + +using ppapi::host::MessageFilterHost; +using ppapi::host::ResourceHost; +using ppapi::host::ResourceMessageFilter; + +namespace chrome { + +ChromeBrowserPepperHostFactory::ChromeBrowserPepperHostFactory( + content::BrowserPpapiHost* host) + : host_(host) {} + +ChromeBrowserPepperHostFactory::~ChromeBrowserPepperHostFactory() {} + +scoped_ptr ChromeBrowserPepperHostFactory::CreateResourceHost( + ppapi::host::PpapiHost* host, + PP_Resource resource, + PP_Instance instance, + const IPC::Message& message) { + DCHECK(host == host_->GetPpapiHost()); + + // Make sure the plugin is giving us a valid instance for this resource. + if (!host_->IsValidInstance(instance)) + return scoped_ptr(); + + // Private interfaces. + if (host_->GetPpapiHost()->permissions().HasPermission( + ppapi::PERMISSION_PRIVATE)) { + switch (message.type()) { + case PpapiHostMsg_Broker_Create::ID: { + scoped_refptr broker_filter( + new PepperBrokerMessageFilter(instance, host_)); + return scoped_ptr(new MessageFilterHost( + host_->GetPpapiHost(), instance, resource, broker_filter)); + } + } + } + + // Flash interfaces. + if (host_->GetPpapiHost()->permissions().HasPermission( + ppapi::PERMISSION_FLASH)) { + switch (message.type()) { + case PpapiHostMsg_Flash_Create::ID: + return scoped_ptr( + new PepperFlashBrowserHost(host_, instance, resource)); + case PpapiHostMsg_FlashClipboard_Create::ID: { + scoped_refptr clipboard_filter( + new PepperFlashClipboardMessageFilter); + return scoped_ptr(new MessageFilterHost( + host_->GetPpapiHost(), instance, resource, clipboard_filter)); + } +#if 0 + case PpapiHostMsg_FlashDRM_Create::ID: + return scoped_ptr( + new PepperFlashDRMHost(host_, instance, resource)); +#endif + } + } + + // Permissions for the following interfaces will be checked at the + // time of the corresponding instance's methods calls (because + // permission check can be performed only on the UI + // thread). Currently these interfaces are available only for + // whitelisted apps which may not have access to the other private + // interfaces. + if (message.type() == PpapiHostMsg_IsolatedFileSystem_Create::ID) { + PepperIsolatedFileSystemMessageFilter* isolated_fs_filter = + PepperIsolatedFileSystemMessageFilter::Create(instance, host_); + if (!isolated_fs_filter) + return scoped_ptr(); + return scoped_ptr( + new MessageFilterHost(host, instance, resource, isolated_fs_filter)); + } + + return scoped_ptr(); +} + +} // namespace chrome diff --git a/src/browser/pepper/chrome_browser_pepper_host_factory.h b/src/browser/pepper/chrome_browser_pepper_host_factory.h new file mode 100644 index 0000000000..b817953b54 --- /dev/null +++ b/src/browser/pepper/chrome_browser_pepper_host_factory.h @@ -0,0 +1,38 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_RENDERER_HOST_PEPPER_CHROME_BROWSER_PEPPER_HOST_FACTORY_H_ +#define CHROME_BROWSER_RENDERER_HOST_PEPPER_CHROME_BROWSER_PEPPER_HOST_FACTORY_H_ + +#include "base/compiler_specific.h" +#include "ppapi/host/host_factory.h" + +namespace content { +class BrowserPpapiHost; +} // namespace content + +namespace chrome { + +class ChromeBrowserPepperHostFactory : public ppapi::host::HostFactory { + public: + // Non-owning pointer to the filter must outlive this class. + explicit ChromeBrowserPepperHostFactory(content::BrowserPpapiHost* host); + ~ChromeBrowserPepperHostFactory() override; + + scoped_ptr CreateResourceHost( + ppapi::host::PpapiHost* host, + PP_Resource resource, + PP_Instance instance, + const IPC::Message& message) override; + + private: + // Non-owning pointer. + content::BrowserPpapiHost* host_; + + DISALLOW_COPY_AND_ASSIGN(ChromeBrowserPepperHostFactory); +}; + +} // namespace chrome + +#endif // CHROME_BROWSER_RENDERER_HOST_PEPPER_CHROME_BROWSER_PEPPER_HOST_FACTORY_H_ diff --git a/src/browser/pepper/pepper_broker_message_filter.cc b/src/browser/pepper/pepper_broker_message_filter.cc new file mode 100644 index 0000000000..79f7106b09 --- /dev/null +++ b/src/browser/pepper/pepper_broker_message_filter.cc @@ -0,0 +1,56 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/browser/pepper/pepper_broker_message_filter.h" + +#include + +#include "components/content_settings/core/browser/host_content_settings_map.h" +#include "components/content_settings/core/common/content_settings.h" +#include "content/public/browser/browser_ppapi_host.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_process_host.h" +#include "ipc/ipc_message_macros.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/host/dispatch_host_message.h" +#include "ppapi/proxy/ppapi_messages.h" +#include "url/gurl.h" + +using content::BrowserPpapiHost; +using content::BrowserThread; +using content::RenderProcessHost; + +namespace chrome { + +PepperBrokerMessageFilter::PepperBrokerMessageFilter(PP_Instance instance, + BrowserPpapiHost* host) + : document_url_(host->GetDocumentURLForInstance(instance)) { + int unused; + host->GetRenderFrameIDsForInstance(instance, &render_process_id_, &unused); +} + +PepperBrokerMessageFilter::~PepperBrokerMessageFilter() {} + +scoped_refptr +PepperBrokerMessageFilter::OverrideTaskRunnerForMessage( + const IPC::Message& message) { + return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); +} + +int32_t PepperBrokerMessageFilter::OnResourceMessageReceived( + const IPC::Message& msg, + ppapi::host::HostMessageContext* context) { + PPAPI_BEGIN_MESSAGE_MAP(PepperBrokerMessageFilter, msg) + PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_Broker_IsAllowed, + OnIsAllowed) + PPAPI_END_MESSAGE_MAP() + return PP_ERROR_FAILED; +} + +int32_t PepperBrokerMessageFilter::OnIsAllowed( + ppapi::host::HostMessageContext* context) { + return PP_OK; +} + +} // namespace chrome diff --git a/src/browser/pepper/pepper_broker_message_filter.h b/src/browser/pepper/pepper_broker_message_filter.h new file mode 100644 index 0000000000..44627c6a35 --- /dev/null +++ b/src/browser/pepper/pepper_broker_message_filter.h @@ -0,0 +1,51 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_BROKER_MESSAGE_FILTER_H_ +#define CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_BROKER_MESSAGE_FILTER_H_ + +#include "base/compiler_specific.h" +#include "ppapi/c/pp_instance.h" +#include "ppapi/host/resource_message_filter.h" +#include "url/gurl.h" + +namespace content { +class BrowserPpapiHost; +} + +namespace ppapi { +namespace host { +struct HostMessageContext; +} +} + +namespace chrome { + +// This filter handles messages for the PepperBrokerHost on the UI thread. +class PepperBrokerMessageFilter : public ppapi::host::ResourceMessageFilter { + public: + PepperBrokerMessageFilter(PP_Instance instance, + content::BrowserPpapiHost* host); + + private: + ~PepperBrokerMessageFilter() override; + + // ppapi::host::ResourceMessageFilter overrides. + scoped_refptr OverrideTaskRunnerForMessage( + const IPC::Message& message) override; + int32_t OnResourceMessageReceived( + const IPC::Message& msg, + ppapi::host::HostMessageContext* context) override; + + int32_t OnIsAllowed(ppapi::host::HostMessageContext* context); + + int render_process_id_; + GURL document_url_; + + DISALLOW_COPY_AND_ASSIGN(PepperBrokerMessageFilter); +}; + +} // namespace chrome + +#endif // CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_BROKER_MESSAGE_FILTER_H_ diff --git a/src/browser/pepper/pepper_flash_browser_host.cc b/src/browser/pepper/pepper_flash_browser_host.cc new file mode 100644 index 0000000000..ddd4305951 --- /dev/null +++ b/src/browser/pepper/pepper_flash_browser_host.cc @@ -0,0 +1,133 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/browser/pepper/pepper_flash_browser_host.h" + +#include "base/time/time.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_ppapi_host.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_process_host.h" +#include "ipc/ipc_message_macros.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/c/private/ppb_flash.h" +#include "ppapi/host/dispatch_host_message.h" +#include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/proxy/resource_message_params.h" +#include "ppapi/shared_impl/time_conversion.h" +#include "url/gurl.h" + +#if defined(OS_WIN) +#include +#elif defined(OS_MACOSX) +#include +#endif + +using content::BrowserPpapiHost; +using content::BrowserThread; +using content::RenderProcessHost; + +namespace chrome { + +#if 0 +namespace { + +// Get the CookieSettings on the UI thread for the given render process ID. +scoped_refptr GetCookieSettings(int render_process_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + RenderProcessHost* render_process_host = + RenderProcessHost::FromID(render_process_id); + if (render_process_host && render_process_host->GetBrowserContext()) { + Profile* profile = + Profile::FromBrowserContext(render_process_host->GetBrowserContext()); + return CookieSettings::Factory::GetForProfile(profile); + } + return NULL; +} + +} // namespace +#endif + +PepperFlashBrowserHost::PepperFlashBrowserHost(BrowserPpapiHost* host, + PP_Instance instance, + PP_Resource resource) + : ResourceHost(host->GetPpapiHost(), instance, resource), + host_(host), + weak_factory_(this) { + int unused; + host->GetRenderFrameIDsForInstance(instance, &render_process_id_, &unused); +} + +PepperFlashBrowserHost::~PepperFlashBrowserHost() {} + +int32_t PepperFlashBrowserHost::OnResourceMessageReceived( + const IPC::Message& msg, + ppapi::host::HostMessageContext* context) { + PPAPI_BEGIN_MESSAGE_MAP(PepperFlashBrowserHost, msg) + PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_Flash_UpdateActivity, + OnUpdateActivity) + PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_Flash_GetLocalTimeZoneOffset, + OnGetLocalTimeZoneOffset) + PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( + PpapiHostMsg_Flash_GetLocalDataRestrictions, OnGetLocalDataRestrictions) + PPAPI_END_MESSAGE_MAP() + return PP_ERROR_FAILED; +} + +int32_t PepperFlashBrowserHost::OnUpdateActivity( + ppapi::host::HostMessageContext* host_context) { +#if defined(OS_WIN) + // Reading then writing back the same value to the screensaver timeout system + // setting resets the countdown which prevents the screensaver from turning + // on "for a while". As long as the plugin pings us with this message faster + // than the screensaver timeout, it won't go on. + int value = 0; + if (SystemParametersInfo(SPI_GETSCREENSAVETIMEOUT, 0, &value, 0)) + SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, value, NULL, 0); +#elif defined(OS_MACOSX) + UpdateSystemActivity(OverallAct); +#else +// TODO(brettw) implement this for other platforms. +#endif + return PP_OK; +} + +int32_t PepperFlashBrowserHost::OnGetLocalTimeZoneOffset( + ppapi::host::HostMessageContext* host_context, + const base::Time& t) { + // The reason for this processing being in the browser process is that on + // Linux, the localtime calls require filesystem access prohibited by the + // sandbox. + host_context->reply_msg = PpapiPluginMsg_Flash_GetLocalTimeZoneOffsetReply( + ppapi::PPGetLocalTimeZoneOffset(t)); + return PP_OK; +} + +int32_t PepperFlashBrowserHost::OnGetLocalDataRestrictions( + ppapi::host::HostMessageContext* context) { + // Getting the Flash LSO settings requires using the CookieSettings which + // belong to the profile which lives on the UI thread. We lazily initialize + // |cookie_settings_| by grabbing the reference from the UI thread and then + // call |GetLocalDataRestrictions| with it. + GURL document_url = host_->GetDocumentURLForInstance(pp_instance()); + GURL plugin_url = host_->GetPluginURLForInstance(pp_instance()); + GetLocalDataRestrictions(context->MakeReplyMessageContext(), + document_url, + plugin_url); + return PP_OK_COMPLETIONPENDING; +} + +void PepperFlashBrowserHost::GetLocalDataRestrictions( + ppapi::host::ReplyMessageContext reply_context, + const GURL& document_url, + const GURL& plugin_url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + PP_FlashLSORestrictions restrictions = PP_FLASHLSORESTRICTIONS_NONE; + SendReply(reply_context, + PpapiPluginMsg_Flash_GetLocalDataRestrictionsReply( + static_cast(restrictions))); +} + +} // namespace chrome diff --git a/src/browser/pepper/pepper_flash_browser_host.h b/src/browser/pepper/pepper_flash_browser_host.h new file mode 100644 index 0000000000..6fb4aced18 --- /dev/null +++ b/src/browser/pepper/pepper_flash_browser_host.h @@ -0,0 +1,60 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_FLASH_BROWSER_HOST_H_ +#define CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_FLASH_BROWSER_HOST_H_ + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "ppapi/host/host_message_context.h" +#include "ppapi/host/resource_host.h" + +namespace base { +class Time; +} + +namespace content { +class BrowserPpapiHost; +class ResourceContext; +} + +class GURL; + +namespace chrome { + +class PepperFlashBrowserHost : public ppapi::host::ResourceHost { + public: + PepperFlashBrowserHost(content::BrowserPpapiHost* host, + PP_Instance instance, + PP_Resource resource); + ~PepperFlashBrowserHost() override; + + // ppapi::host::ResourceHost override. + int32_t OnResourceMessageReceived( + const IPC::Message& msg, + ppapi::host::HostMessageContext* context) override; + + private: + int32_t OnUpdateActivity(ppapi::host::HostMessageContext* host_context); + int32_t OnGetLocalTimeZoneOffset( + ppapi::host::HostMessageContext* host_context, + const base::Time& t); + int32_t OnGetLocalDataRestrictions(ppapi::host::HostMessageContext* context); + + void GetLocalDataRestrictions(ppapi::host::ReplyMessageContext reply_context, + const GURL& document_url, + const GURL& plugin_url); + + content::BrowserPpapiHost* host_; + int render_process_id_; + // For fetching the Flash LSO settings. + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(PepperFlashBrowserHost); +}; + +} // namespace chrome + +#endif // CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_FLASH_BROWSER_HOST_H_ diff --git a/src/browser/pepper/pepper_flash_clipboard_message_filter.cc b/src/browser/pepper/pepper_flash_clipboard_message_filter.cc new file mode 100644 index 0000000000..dc0cb3a341 --- /dev/null +++ b/src/browser/pepper/pepper_flash_clipboard_message_filter.cc @@ -0,0 +1,376 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/browser/pepper/pepper_flash_clipboard_message_filter.h" + +#include "base/pickle.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/browser/browser_thread.h" +#include "ipc/ipc_message.h" +#include "ipc/ipc_message_macros.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/c/private/ppb_flash_clipboard.h" +#include "ppapi/host/dispatch_host_message.h" +#include "ppapi/host/host_message_context.h" +#include "ppapi/host/ppapi_host.h" +#include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/proxy/resource_message_params.h" +#include "ui/base/clipboard/scoped_clipboard_writer.h" + +using content::BrowserThread; + +namespace chrome { + +namespace { + +const size_t kMaxClipboardWriteSize = 1000000; + +ui::ClipboardType ConvertClipboardType(uint32_t type) { + switch (type) { + case PP_FLASH_CLIPBOARD_TYPE_STANDARD: + return ui::CLIPBOARD_TYPE_COPY_PASTE; + case PP_FLASH_CLIPBOARD_TYPE_SELECTION: + return ui::CLIPBOARD_TYPE_SELECTION; + } + NOTREACHED(); + return ui::CLIPBOARD_TYPE_COPY_PASTE; +} + +// Functions to pack/unpack custom data from a pickle. See the header file for +// more detail on custom formats in Pepper. +// TODO(raymes): Currently pepper custom formats are stored in their own +// native format type. However we should be able to store them in the same way +// as "Web Custom" formats are. This would allow clipboard data to be shared +// between pepper applications and web applications. However currently web apps +// assume all data that is placed on the clipboard is UTF16 and pepper allows +// arbitrary data so this change would require some reworking of the chrome +// clipboard interface for custom data. +bool JumpToFormatInPickle(const base::string16& format, PickleIterator* iter) { + size_t size = 0; + if (!iter->ReadSizeT(&size)) + return false; + for (size_t i = 0; i < size; ++i) { + base::string16 stored_format; + if (!iter->ReadString16(&stored_format)) + return false; + if (stored_format == format) + return true; + int skip_length; + if (!iter->ReadLength(&skip_length)) + return false; + if (!iter->SkipBytes(skip_length)) + return false; + } + return false; +} + +bool IsFormatAvailableInPickle(const base::string16& format, + const Pickle& pickle) { + PickleIterator iter(pickle); + return JumpToFormatInPickle(format, &iter); +} + +std::string ReadDataFromPickle(const base::string16& format, + const Pickle& pickle) { + std::string result; + PickleIterator iter(pickle); + if (!JumpToFormatInPickle(format, &iter) || !iter.ReadString(&result)) + return std::string(); + return result; +} + +bool WriteDataToPickle(const std::map& data, + Pickle* pickle) { + pickle->WriteSizeT(data.size()); + for (std::map::const_iterator it = data.begin(); + it != data.end(); + ++it) { + if (!pickle->WriteString16(it->first)) + return false; + if (!pickle->WriteString(it->second)) + return false; + } + return true; +} + +} // namespace + +PepperFlashClipboardMessageFilter::PepperFlashClipboardMessageFilter() {} + +PepperFlashClipboardMessageFilter::~PepperFlashClipboardMessageFilter() {} + +scoped_refptr +PepperFlashClipboardMessageFilter::OverrideTaskRunnerForMessage( + const IPC::Message& msg) { + // Clipboard writes should always occur on the UI thread due to the + // restrictions of various platform APIs. In general, the clipboard is not + // thread-safe, so all clipboard calls should be serviced from the UI thread. + if (msg.type() == PpapiHostMsg_FlashClipboard_WriteData::ID) + return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); + +// Windows needs clipboard reads to be serviced from the IO thread because +// these are sync IPCs which can result in deadlocks with plugins if serviced +// from the UI thread. Note that Windows clipboard calls ARE thread-safe so it +// is ok for reads and writes to be serviced from different threads. +#if !defined(OS_WIN) + return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); +#else + return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); +#endif +} + +int32_t PepperFlashClipboardMessageFilter::OnResourceMessageReceived( + const IPC::Message& msg, + ppapi::host::HostMessageContext* context) { + PPAPI_BEGIN_MESSAGE_MAP(PepperFlashClipboardMessageFilter, msg) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_FlashClipboard_RegisterCustomFormat, + OnMsgRegisterCustomFormat) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_FlashClipboard_IsFormatAvailable, OnMsgIsFormatAvailable) + PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashClipboard_ReadData, + OnMsgReadData) + PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashClipboard_WriteData, + OnMsgWriteData) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_FlashClipboard_GetSequenceNumber, OnMsgGetSequenceNumber) + PPAPI_END_MESSAGE_MAP() + return PP_ERROR_FAILED; +} + +int32_t PepperFlashClipboardMessageFilter::OnMsgRegisterCustomFormat( + ppapi::host::HostMessageContext* host_context, + const std::string& format_name) { + uint32_t format = custom_formats_.RegisterFormat(format_name); + if (format == PP_FLASH_CLIPBOARD_FORMAT_INVALID) + return PP_ERROR_FAILED; + host_context->reply_msg = + PpapiPluginMsg_FlashClipboard_RegisterCustomFormatReply(format); + return PP_OK; +} + +int32_t PepperFlashClipboardMessageFilter::OnMsgIsFormatAvailable( + ppapi::host::HostMessageContext* host_context, + uint32_t clipboard_type, + uint32_t format) { + if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { + NOTIMPLEMENTED(); + return PP_ERROR_FAILED; + } + + ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); + ui::ClipboardType type = ConvertClipboardType(clipboard_type); + bool available = false; + switch (format) { + case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: { + bool plain = clipboard->IsFormatAvailable( + ui::Clipboard::GetPlainTextFormatType(), type); + bool plainw = clipboard->IsFormatAvailable( + ui::Clipboard::GetPlainTextWFormatType(), type); + available = plain || plainw; + break; + } + case PP_FLASH_CLIPBOARD_FORMAT_HTML: + available = clipboard->IsFormatAvailable( + ui::Clipboard::GetHtmlFormatType(), type); + break; + case PP_FLASH_CLIPBOARD_FORMAT_RTF: + available = + clipboard->IsFormatAvailable(ui::Clipboard::GetRtfFormatType(), type); + break; + case PP_FLASH_CLIPBOARD_FORMAT_INVALID: + break; + default: + if (custom_formats_.IsFormatRegistered(format)) { + std::string format_name = custom_formats_.GetFormatName(format); + std::string clipboard_data; + clipboard->ReadData(ui::Clipboard::GetPepperCustomDataFormatType(), + &clipboard_data); + Pickle pickle(clipboard_data.data(), clipboard_data.size()); + available = + IsFormatAvailableInPickle(base::UTF8ToUTF16(format_name), pickle); + } + break; + } + + return available ? PP_OK : PP_ERROR_FAILED; +} + +int32_t PepperFlashClipboardMessageFilter::OnMsgReadData( + ppapi::host::HostMessageContext* host_context, + uint32_t clipboard_type, + uint32_t format) { + if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { + NOTIMPLEMENTED(); + return PP_ERROR_FAILED; + } + + ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); + ui::ClipboardType type = ConvertClipboardType(clipboard_type); + std::string clipboard_string; + int32_t result = PP_ERROR_FAILED; + switch (format) { + case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: { + if (clipboard->IsFormatAvailable(ui::Clipboard::GetPlainTextWFormatType(), + type)) { + base::string16 text; + clipboard->ReadText(type, &text); + if (!text.empty()) { + result = PP_OK; + clipboard_string = base::UTF16ToUTF8(text); + break; + } + } + // If the PlainTextW format isn't available or is empty, take the + // ASCII text format. + if (clipboard->IsFormatAvailable(ui::Clipboard::GetPlainTextFormatType(), + type)) { + result = PP_OK; + clipboard->ReadAsciiText(type, &clipboard_string); + } + break; + } + case PP_FLASH_CLIPBOARD_FORMAT_HTML: { + if (!clipboard->IsFormatAvailable(ui::Clipboard::GetHtmlFormatType(), + type)) { + break; + } + + base::string16 html; + std::string url; + uint32 fragment_start; + uint32 fragment_end; + clipboard->ReadHTML(type, &html, &url, &fragment_start, &fragment_end); + result = PP_OK; + clipboard_string = base::UTF16ToUTF8( + html.substr(fragment_start, fragment_end - fragment_start)); + break; + } + case PP_FLASH_CLIPBOARD_FORMAT_RTF: { + if (!clipboard->IsFormatAvailable(ui::Clipboard::GetRtfFormatType(), + type)) { + break; + } + result = PP_OK; + clipboard->ReadRTF(type, &clipboard_string); + break; + } + case PP_FLASH_CLIPBOARD_FORMAT_INVALID: + break; + default: { + if (custom_formats_.IsFormatRegistered(format)) { + base::string16 format_name = + base::UTF8ToUTF16(custom_formats_.GetFormatName(format)); + std::string clipboard_data; + clipboard->ReadData(ui::Clipboard::GetPepperCustomDataFormatType(), + &clipboard_data); + Pickle pickle(clipboard_data.data(), clipboard_data.size()); + if (IsFormatAvailableInPickle(format_name, pickle)) { + result = PP_OK; + clipboard_string = ReadDataFromPickle(format_name, pickle); + } + } + break; + } + } + + if (result == PP_OK) { + host_context->reply_msg = + PpapiPluginMsg_FlashClipboard_ReadDataReply(clipboard_string); + } + return result; +} + +int32_t PepperFlashClipboardMessageFilter::OnMsgWriteData( + ppapi::host::HostMessageContext* host_context, + uint32_t clipboard_type, + const std::vector& formats, + const std::vector& data) { + if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { + NOTIMPLEMENTED(); + return PP_ERROR_FAILED; + } + if (formats.size() != data.size()) + return PP_ERROR_FAILED; + + ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); + ui::ClipboardType type = ConvertClipboardType(clipboard_type); + // If no formats are passed in clear the clipboard. + if (formats.size() == 0) { + clipboard->Clear(type); + return PP_OK; + } + + ui::ScopedClipboardWriter scw(type); + std::map custom_data_map; + int32_t res = PP_OK; + for (uint32_t i = 0; i < formats.size(); ++i) { + if (data[i].length() > kMaxClipboardWriteSize) { + res = PP_ERROR_NOSPACE; + break; + } + + switch (formats[i]) { + case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: + scw.WriteText(base::UTF8ToUTF16(data[i])); + break; + case PP_FLASH_CLIPBOARD_FORMAT_HTML: + scw.WriteHTML(base::UTF8ToUTF16(data[i]), std::string()); + break; + case PP_FLASH_CLIPBOARD_FORMAT_RTF: + scw.WriteRTF(data[i]); + break; + case PP_FLASH_CLIPBOARD_FORMAT_INVALID: + res = PP_ERROR_BADARGUMENT; + break; + default: + if (custom_formats_.IsFormatRegistered(formats[i])) { + std::string format_name = custom_formats_.GetFormatName(formats[i]); + custom_data_map[base::UTF8ToUTF16(format_name)] = data[i]; + } else { + // Invalid format. + res = PP_ERROR_BADARGUMENT; + break; + } + } + + if (res != PP_OK) + break; + } + + if (custom_data_map.size() > 0) { + Pickle pickle; + if (WriteDataToPickle(custom_data_map, &pickle)) { + scw.WritePickledData(pickle, + ui::Clipboard::GetPepperCustomDataFormatType()); + } else { + res = PP_ERROR_BADARGUMENT; + } + } + + if (res != PP_OK) { + // Need to clear the objects so nothing is written. + scw.Reset(); + } + + return res; +} + +int32_t PepperFlashClipboardMessageFilter::OnMsgGetSequenceNumber( + ppapi::host::HostMessageContext* host_context, + uint32_t clipboard_type) { + if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { + NOTIMPLEMENTED(); + return PP_ERROR_FAILED; + } + + ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); + ui::ClipboardType type = ConvertClipboardType(clipboard_type); + int64_t sequence_number = clipboard->GetSequenceNumber(type); + host_context->reply_msg = + PpapiPluginMsg_FlashClipboard_GetSequenceNumberReply(sequence_number); + return PP_OK; +} + +} // namespace chrome diff --git a/src/browser/pepper/pepper_flash_clipboard_message_filter.h b/src/browser/pepper/pepper_flash_clipboard_message_filter.h new file mode 100644 index 0000000000..ff07eb7375 --- /dev/null +++ b/src/browser/pepper/pepper_flash_clipboard_message_filter.h @@ -0,0 +1,78 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_FLASH_CLIPBOARD_MESSAGE_FILTER_H_ +#define CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_FLASH_CLIPBOARD_MESSAGE_FILTER_H_ + +#include +#include + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "ppapi/host/resource_message_filter.h" +#include "ppapi/shared_impl/flash_clipboard_format_registry.h" + +namespace ppapi { +namespace host { +struct HostMessageContext; +} +} + +namespace ui { +class ScopedClipboardWriter; +} + +namespace chrome { + +// Resource message filter for accessing the clipboard in Pepper. Pepper +// supports reading/writing custom formats from the clipboard. Currently, all +// custom formats that are read/written from the clipboard through pepper are +// stored in a single real clipboard format (in the same way the "web custom" +// clipboard formats are). This is done so that we don't have to have use real +// clipboard types for each custom clipboard format which may be a limited +// resource on a particular platform. +class PepperFlashClipboardMessageFilter + : public ppapi::host::ResourceMessageFilter { + public: + PepperFlashClipboardMessageFilter(); + + protected: + // ppapi::host::ResourceMessageFilter overrides. + scoped_refptr OverrideTaskRunnerForMessage( + const IPC::Message& msg) override; + int32_t OnResourceMessageReceived( + const IPC::Message& msg, + ppapi::host::HostMessageContext* context) override; + + private: + ~PepperFlashClipboardMessageFilter() override; + + int32_t OnMsgRegisterCustomFormat( + ppapi::host::HostMessageContext* host_context, + const std::string& format_name); + int32_t OnMsgIsFormatAvailable(ppapi::host::HostMessageContext* host_context, + uint32_t clipboard_type, + uint32_t format); + int32_t OnMsgReadData(ppapi::host::HostMessageContext* host_context, + uint32_t clipoard_type, + uint32_t format); + int32_t OnMsgWriteData(ppapi::host::HostMessageContext* host_context, + uint32_t clipboard_type, + const std::vector& formats, + const std::vector& data); + int32_t OnMsgGetSequenceNumber(ppapi::host::HostMessageContext* host_context, + uint32_t clipboard_type); + + int32_t WriteClipboardDataItem(uint32_t format, + const std::string& data, + ui::ScopedClipboardWriter* scw); + + ppapi::FlashClipboardFormatRegistry custom_formats_; + + DISALLOW_COPY_AND_ASSIGN(PepperFlashClipboardMessageFilter); +}; + +} // namespace chrome + +#endif // CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_FLASH_CLIPBOARD_MESSAGE_FILTER_H_ diff --git a/src/browser/pepper/pepper_isolated_file_system_message_filter.cc b/src/browser/pepper/pepper_isolated_file_system_message_filter.cc new file mode 100644 index 0000000000..7cf809148f --- /dev/null +++ b/src/browser/pepper/pepper_isolated_file_system_message_filter.cc @@ -0,0 +1,202 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/browser/pepper/pepper_isolated_file_system_message_filter.h" + +// #include "chrome/browser/browser_process.h" +// #include "chrome/browser/profiles/profile.h" +// #include "chrome/browser/profiles/profile_manager.h" +// #include "chrome/common/chrome_switches.h" +// #include "chrome/common/pepper_permission_util.h" +#include "content/public/browser/browser_ppapi_host.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/child_process_security_policy.h" +#include "content/public/browser/render_view_host.h" +#if defined(ENABLE_EXTENSIONS) +#include "extensions/browser/extension_registry.h" +#include "extensions/common/constants.h" +#include "extensions/common/extension.h" +#include "extensions/common/extension_set.h" +#endif +#include "ppapi/c/pp_errors.h" +#include "ppapi/host/dispatch_host_message.h" +#include "ppapi/host/host_message_context.h" +#include "ppapi/host/ppapi_host.h" +#include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/shared_impl/file_system_util.h" +#include "storage/browser/fileapi/isolated_context.h" + +namespace chrome { + +namespace { + +const char* kPredefinedAllowedCrxFsOrigins[] = { + "6EAED1924DB611B6EEF2A664BD077BE7EAD33B8F", // see crbug.com/234789 + "4EB74897CB187C7633357C2FE832E0AD6A44883A" // see crbug.com/234789 +}; + +} // namespace + +// static +PepperIsolatedFileSystemMessageFilter* +PepperIsolatedFileSystemMessageFilter::Create(PP_Instance instance, + content::BrowserPpapiHost* host) { + int render_process_id; + int unused_render_frame_id; + if (!host->GetRenderFrameIDsForInstance( + instance, &render_process_id, &unused_render_frame_id)) { + return NULL; + } + return new PepperIsolatedFileSystemMessageFilter( + render_process_id, + host->GetProfileDataDirectory(), + host->GetDocumentURLForInstance(instance), + host->GetPpapiHost()); +} + +PepperIsolatedFileSystemMessageFilter::PepperIsolatedFileSystemMessageFilter( + int render_process_id, + const base::FilePath& profile_directory, + const GURL& document_url, + ppapi::host::PpapiHost* ppapi_host) + : render_process_id_(render_process_id), + profile_directory_(profile_directory), + document_url_(document_url), + ppapi_host_(ppapi_host) { + for (size_t i = 0; i < arraysize(kPredefinedAllowedCrxFsOrigins); ++i) + allowed_crxfs_origins_.insert(kPredefinedAllowedCrxFsOrigins[i]); +} + +PepperIsolatedFileSystemMessageFilter:: + ~PepperIsolatedFileSystemMessageFilter() {} + +scoped_refptr +PepperIsolatedFileSystemMessageFilter::OverrideTaskRunnerForMessage( + const IPC::Message& msg) { + // In order to reach ExtensionSystem, we need to get ProfileManager first. + // ProfileManager lives in UI thread, so we need to do this in UI thread. + return content::BrowserThread::GetMessageLoopProxyForThread( + content::BrowserThread::UI); +} + +int32_t PepperIsolatedFileSystemMessageFilter::OnResourceMessageReceived( + const IPC::Message& msg, + ppapi::host::HostMessageContext* context) { + PPAPI_BEGIN_MESSAGE_MAP(PepperIsolatedFileSystemMessageFilter, msg) + PPAPI_DISPATCH_HOST_RESOURCE_CALL( + PpapiHostMsg_IsolatedFileSystem_BrowserOpen, + OnOpenFileSystem) + PPAPI_END_MESSAGE_MAP() + return PP_ERROR_FAILED; +} + +#if 0 +Profile* PepperIsolatedFileSystemMessageFilter::GetProfile() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + ProfileManager* profile_manager = g_browser_process->profile_manager(); + return profile_manager->GetProfile(profile_directory_); +} + +std::string PepperIsolatedFileSystemMessageFilter::CreateCrxFileSystem( + Profile* profile) { +#if defined(ENABLE_EXTENSIONS) + const extensions::Extension* extension = + extensions::ExtensionRegistry::Get(profile)->enabled_extensions().GetByID( + document_url_.host()); + if (!extension) + return std::string(); + + // First level directory for isolated filesystem to lookup. + std::string kFirstLevelDirectory("crxfs"); + return storage::IsolatedContext::GetInstance()->RegisterFileSystemForPath( + storage::kFileSystemTypeNativeLocal, + std::string(), + extension->path(), + &kFirstLevelDirectory); +#else + return std::string(); +#endif +} +#endif + +int32_t PepperIsolatedFileSystemMessageFilter::OnOpenFileSystem( + ppapi::host::HostMessageContext* context, + PP_IsolatedFileSystemType_Private type) { + switch (type) { + case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_INVALID: + case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_CRX: + break; + case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_PLUGINPRIVATE: + return OpenPluginPrivateFileSystem(context); + } + NOTREACHED(); + context->reply_msg = + PpapiPluginMsg_IsolatedFileSystem_BrowserOpenReply(std::string()); + return PP_ERROR_FAILED; +} + +#if 0 +int32_t PepperIsolatedFileSystemMessageFilter::OpenCrxFileSystem( + ppapi::host::HostMessageContext* context) { +#if defined(ENABLE_EXTENSIONS) + Profile* profile = GetProfile(); + const extensions::ExtensionSet* extension_set = NULL; + if (profile) { + extension_set = + &extensions::ExtensionRegistry::Get(profile)->enabled_extensions(); + } + if (!IsExtensionOrSharedModuleWhitelisted( + document_url_, extension_set, allowed_crxfs_origins_) && + !IsHostAllowedByCommandLine( + document_url_, extension_set, switches::kAllowNaClCrxFsAPI)) { + LOG(ERROR) << "Host " << document_url_.host() << " cannot use CrxFs API."; + return PP_ERROR_NOACCESS; + } + + // TODO(raymes): When we remove FileSystem from the renderer, we should create + // a pending PepperFileSystemBrowserHost here with the fsid and send the + // pending host ID back to the plugin. + const std::string fsid = CreateCrxFileSystem(profile); + if (fsid.empty()) { + context->reply_msg = + PpapiPluginMsg_IsolatedFileSystem_BrowserOpenReply(std::string()); + return PP_ERROR_NOTSUPPORTED; + } + + // Grant readonly access of isolated filesystem to renderer process. + content::ChildProcessSecurityPolicy* policy = + content::ChildProcessSecurityPolicy::GetInstance(); + policy->GrantReadFileSystem(render_process_id_, fsid); + + context->reply_msg = PpapiPluginMsg_IsolatedFileSystem_BrowserOpenReply(fsid); + return PP_OK; +#else + return PP_ERROR_NOTSUPPORTED; +#endif +} +#endif + +int32_t PepperIsolatedFileSystemMessageFilter::OpenPluginPrivateFileSystem( + ppapi::host::HostMessageContext* context) { + DCHECK(ppapi_host_); + // Only plugins with private permission can open the filesystem. + if (!ppapi_host_->permissions().HasPermission(ppapi::PERMISSION_PRIVATE)) + return PP_ERROR_NOACCESS; + + const std::string& root_name = ppapi::IsolatedFileSystemTypeToRootName( + PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_PLUGINPRIVATE); + const std::string& fsid = + storage::IsolatedContext::GetInstance()->RegisterFileSystemForVirtualPath( + storage::kFileSystemTypePluginPrivate, root_name, base::FilePath()); + + // Grant full access of isolated filesystem to renderer process. + content::ChildProcessSecurityPolicy* policy = + content::ChildProcessSecurityPolicy::GetInstance(); + policy->GrantCreateReadWriteFileSystem(render_process_id_, fsid); + + context->reply_msg = PpapiPluginMsg_IsolatedFileSystem_BrowserOpenReply(fsid); + return PP_OK; +} + +} // namespace chrome diff --git a/src/browser/pepper/pepper_isolated_file_system_message_filter.h b/src/browser/pepper/pepper_isolated_file_system_message_filter.h new file mode 100644 index 0000000000..bc2996b983 --- /dev/null +++ b/src/browser/pepper/pepper_isolated_file_system_message_filter.h @@ -0,0 +1,81 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_ISOLATED_FILE_SYSTEM_MESSAGE_FILTER_H_ +#define CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_ISOLATED_FILE_SYSTEM_MESSAGE_FILTER_H_ + +#include +#include + +#include "base/files/file_path.h" +#include "ppapi/c/pp_instance.h" +#include "ppapi/c/pp_resource.h" +#include "ppapi/c/private/ppb_isolated_file_system_private.h" +#include "ppapi/host/resource_host.h" +#include "ppapi/host/resource_message_filter.h" +#include "url/gurl.h" + +class Profile; + +namespace content { +class BrowserPpapiHost; +} + +namespace ppapi { +namespace host { +struct HostMessageContext; +} // namespace host +} // namespace ppapi + +namespace chrome { + +class PepperIsolatedFileSystemMessageFilter + : public ppapi::host::ResourceMessageFilter { + public: + static PepperIsolatedFileSystemMessageFilter* Create( + PP_Instance instance, + content::BrowserPpapiHost* host); + + // ppapi::host::ResourceMessageFilter implementation. + scoped_refptr OverrideTaskRunnerForMessage( + const IPC::Message& msg) override; + int32_t OnResourceMessageReceived( + const IPC::Message& msg, + ppapi::host::HostMessageContext* context) override; + + private: + PepperIsolatedFileSystemMessageFilter(int render_process_id, + const base::FilePath& profile_directory, + const GURL& document_url, + ppapi::host::PpapiHost* ppapi_host_); + + ~PepperIsolatedFileSystemMessageFilter() override; + + Profile* GetProfile(); + + // Returns filesystem id of isolated filesystem if valid, or empty string + // otherwise. This must run on the UI thread because ProfileManager only + // allows access on that thread. + + int32_t OnOpenFileSystem(ppapi::host::HostMessageContext* context, + PP_IsolatedFileSystemType_Private type); + int32_t OpenPluginPrivateFileSystem(ppapi::host::HostMessageContext* context); + + const int render_process_id_; + // Keep a copy from original thread. + const base::FilePath profile_directory_; + const GURL document_url_; + + // Not owned by this object. + ppapi::host::PpapiHost* ppapi_host_; + + // Set of origins that can use CrxFs private APIs from NaCl. + std::set allowed_crxfs_origins_; + + DISALLOW_COPY_AND_ASSIGN(PepperIsolatedFileSystemMessageFilter); +}; + +} // namespace chrome + +#endif // CHROME_BROWSER_RENDERER_HOST_PEPPER_PEPPER_ISOLATED_FILE_SYSTEM_MESSAGE_FILTER_H_ diff --git a/src/browser/popup_controller_common.cc b/src/browser/popup_controller_common.cc index 9b6796e6eb..e8bfa2029a 100644 --- a/src/browser/popup_controller_common.cc +++ b/src/browser/popup_controller_common.cc @@ -10,9 +10,9 @@ #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "ui/gfx/display.h" -#include "ui/gfx/rect_conversions.h" +#include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/screen.h" -#include "ui/gfx/vector2d.h" +#include "ui/gfx/geometry/vector2d.h" namespace autofill { @@ -122,22 +122,21 @@ std::pair PopupControllerCommon::CalculatePopupYAndHeight( } } -gfx::Rect PopupControllerCommon::GetPopupBounds( - int popup_required_height, - int popup_required_width) const { +gfx::Rect PopupControllerCommon::GetPopupBounds(int desired_width, + int desired_height) const { // This is the top left point of the popup if the popup is above the element // and grows to the left (since that is the highest and furthest left the // popup go could). gfx::Point top_left_corner_of_popup = RoundedElementBounds().origin() + - gfx::Vector2d(RoundedElementBounds().width() - popup_required_width, - -popup_required_height); + gfx::Vector2d(RoundedElementBounds().width() - desired_width, + -desired_height); // This is the bottom right point of the popup if the popup is below the // element and grows to the right (since the is the lowest and furthest right // the popup could go). gfx::Point bottom_right_corner_of_popup = RoundedElementBounds().origin() + - gfx::Vector2d(popup_required_width, - RoundedElementBounds().height() + popup_required_height); + gfx::Vector2d(desired_width, + RoundedElementBounds().height() + desired_height); gfx::Display top_left_display = GetDisplayNearestPoint( top_left_corner_of_popup); @@ -147,11 +146,11 @@ gfx::Rect PopupControllerCommon::GetPopupBounds( std::pair popup_x_and_width = CalculatePopupXAndWidth(top_left_display, bottom_right_display, - popup_required_width); + desired_width); std::pair popup_y_and_height = CalculatePopupYAndHeight(top_left_display, bottom_right_display, - popup_required_height); + desired_height); return gfx::Rect(popup_x_and_width.first, popup_y_and_height.first, diff --git a/src/browser/popup_controller_common.h b/src/browser/popup_controller_common.h index da1ad40c35..c111670010 100644 --- a/src/browser/popup_controller_common.h +++ b/src/browser/popup_controller_common.h @@ -7,8 +7,8 @@ #include "content/public/browser/render_widget_host.h" #include "ui/gfx/native_widget_types.h" -#include "ui/gfx/rect.h" -#include "ui/gfx/rect_f.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rect_f.h" namespace content { struct NativeWebKeyboardEvent; @@ -42,7 +42,7 @@ class PopupControllerCommon { // Returns the bounds that the popup should be placed at, given the desired // width and height. By default this places the popup below |element_bounds| // but it will be placed above if there isn't enough space. - gfx::Rect GetPopupBounds(int desired_height, int desired_width) const; + gfx::Rect GetPopupBounds(int desired_width, int desired_height) const; // Callback used to register with RenderViewHost. This can only be set once, // or else a callback may be registered that will not be removed diff --git a/src/browser/printing/print_dialog_gtk.h b/src/browser/printing/print_dialog_gtk.h index 1eb385cc30..e6fe453e41 100644 --- a/src/browser/printing/print_dialog_gtk.h +++ b/src/browser/printing/print_dialog_gtk.h @@ -38,7 +38,7 @@ class PrintDialogGtk virtual void UseDefaultSettings() OVERRIDE; virtual bool UpdateSettings(const base::DictionaryValue& job_settings, const printing::PageRanges& ranges, - printing::PrintSettings* settings) OVERRIDE; + printing::PrintSettings* settings) ; //FIXME: override virtual void ShowDialog( gfx::NativeView parent_view, bool has_selection, diff --git a/src/browser/printing/print_job.cc b/src/browser/printing/print_job.cc index d7dfbedc2d..7485983945 100644 --- a/src/browser/printing/print_job.cc +++ b/src/browser/printing/print_job.cc @@ -10,6 +10,7 @@ #include "base/threading/thread_restrictions.h" #include "base/threading/worker_pool.h" #include "base/timer/timer.h" +#include "content/public/browser/browser_thread.h" #include "content/nw/src/browser/printing/print_job_worker.h" #include "content/public/browser/notification_types.h" #include "content/public/browser/notification_service.h" @@ -70,7 +71,8 @@ void PrintJob::Initialize(PrintJobWorkerOwner* job, settings_ = job->settings(); PrintedDocument* new_doc = - new PrintedDocument(settings_, source_, job->cookie()); + new PrintedDocument(settings_, source_, job->cookie(), + content::BrowserThread::GetBlockingPool()); new_doc->set_page_count(page_count); UpdatePrintedDocument(new_doc); diff --git a/src/browser/printing/print_job_worker.cc b/src/browser/printing/print_job_worker.cc index 5e4ed1ed15..7da21ca269 100644 --- a/src/browser/printing/print_job_worker.cc +++ b/src/browser/printing/print_job_worker.cc @@ -130,31 +130,8 @@ void PrintJobWorker::SetSettings(const base::DictionaryValue* const new_settings void PrintJobWorker::UpdatePrintSettings( const base::DictionaryValue* const new_settings) { - // Create new PageRanges based on |new_settings|. - PageRanges new_ranges; - const base::ListValue* page_range_array; - if (new_settings->GetList(kSettingPageRange, &page_range_array)) { - for (size_t index = 0; index < page_range_array->GetSize(); ++index) { - const base::DictionaryValue* dict; - if (!page_range_array->GetDictionary(index, &dict)) - continue; - - PageRange range; - if (!dict->GetInteger(kSettingPageRangeFrom, &range.from) || - !dict->GetInteger(kSettingPageRangeTo, &range.to)) { - continue; - } - - // Page numbers are 1-based in the dictionary. - // Page numbers are 0-based for the printing context. - range.from--; - range.to--; - new_ranges.push_back(range); - } - } PrintingContext::Result result = - printing_context_->UpdatePrintSettings(*new_settings, new_ranges); - delete new_settings; + printing_context_->UpdatePrintSettings(*new_settings); GetSettingsDone(result); } @@ -274,8 +251,8 @@ void PrintJobWorker::OnNewPage() { while (true) { // Is the page available? - scoped_refptr page; - if (!document_->GetPage(page_number_.ToInt(), &page)) { + scoped_refptr page = document_->GetPage(page_number_.ToInt()); + if (!page) { // We need to wait for the page to be available. MessageLoop::current()->PostDelayedTask( FROM_HERE, diff --git a/src/browser/printing/print_job_worker_owner.h b/src/browser/printing/print_job_worker_owner.h index 8f9f58509f..00a561a39b 100644 --- a/src/browser/printing/print_job_worker_owner.h +++ b/src/browser/printing/print_job_worker_owner.h @@ -10,8 +10,12 @@ namespace base { class MessageLoop; +class SequencedTaskRunner; } +namespace tracked_objects { +class Location; +} namespace printing { @@ -21,6 +25,8 @@ class PrintSettings; class PrintJobWorkerOwner : public base::RefCountedThreadSafe { public: + PrintJobWorkerOwner(); + // Finishes the initialization began by PrintJobWorker::GetSettings(). // Creates a new PrintedDocument if necessary. Solely meant to be called by // PrintJobWorker. @@ -30,19 +36,29 @@ class PrintJobWorkerOwner // Detach the PrintJobWorker associated to this object. virtual PrintJobWorker* DetachWorker(PrintJobWorkerOwner* new_owner) = 0; - // Retrieves the message loop that is expected to process GetSettingsDone. - virtual base::MessageLoop* message_loop() = 0; - // Access the current settings. virtual const PrintSettings& settings() const = 0; // Cookie uniquely identifying the PrintedDocument and/or loaded settings. virtual int cookie() const = 0; + // Returns true if the current thread is a thread on which a task + // may be run, and false if no task will be run on the current + // thread. + bool RunsTasksOnCurrentThread() const; + + // Posts the given task to be run. + bool PostTask(const tracked_objects::Location& from_here, + const base::Closure& task); + protected: friend class base::RefCountedThreadSafe; - virtual ~PrintJobWorkerOwner() {} + virtual ~PrintJobWorkerOwner(); + + // Task runner reference. Used to send notifications in the right + // thread. + scoped_refptr task_runner_; }; } // namespace printing diff --git a/src/browser/printing/print_view_manager.cc b/src/browser/printing/print_view_manager.cc index 836da15c00..60a7c451fb 100644 --- a/src/browser/printing/print_view_manager.cc +++ b/src/browser/printing/print_view_manager.cc @@ -35,7 +35,6 @@ #include "content/public/browser/notification_source.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_view.h" #include "grit/nw_resources.h" #include "printing/metafile.h" #include "printing/metafile_impl.h" @@ -52,8 +51,10 @@ DEFINE_WEB_CONTENTS_USER_DATA_KEY(printing::PrintViewManager); namespace { +#if defined(OS_WIN) && !defined(WIN_PDF_METAFILE_FOR_PRINTING) // Limits memory usage by raster to 64 MiB. const int kMaxRasterSizeInPixels = 16*1024*1024; +#endif } // namespace @@ -196,7 +197,7 @@ void PrintViewManager::OnDidPrintPage( } } -#if defined(OS_WIN) +#if defined(OS_WIN) && !defined(WIN_PDF_METAFILE_FOR_PRINTING) bool big_emf = (params.data_size && params.data_size >= kMetafileMaxSize); const CommandLine* cmdline = CommandLine::ForCurrentProcess(); int raster_size = std::min(params.page_size.GetArea(), @@ -219,7 +220,9 @@ void PrintViewManager::OnDidPrintPage( // Update the rendered document. It will send notifications to the listener. document->SetPage(params.page_number, metafile.release(), +#if defined(OS_WIN) params.actual_shrink, +#endif params.page_size, params.content_area); diff --git a/src/browser/printing/print_view_manager.h b/src/browser/printing/print_view_manager.h index 6d2898f060..f4640bcb95 100644 --- a/src/browser/printing/print_view_manager.h +++ b/src/browser/printing/print_view_manager.h @@ -59,25 +59,25 @@ class PrintViewManager : public content::NotificationObserver, void set_observer(PrintViewManagerObserver* observer); // PrintedPagesSource implementation. - virtual base::string16 RenderSourceName() OVERRIDE; + virtual base::string16 RenderSourceName() override; // content::NotificationObserver implementation. virtual void Observe(int type, const content::NotificationSource& source, - const content::NotificationDetails& details) OVERRIDE; + const content::NotificationDetails& details) override; // content::WebContentsObserver implementation. virtual void DidStartLoading( - content::RenderViewHost* render_view_host) OVERRIDE; + content::RenderViewHost* render_view_host) override; // content::WebContentsObserver implementation. - virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + virtual bool OnMessageReceived(const IPC::Message& message) override; // Terminates or cancels the print job if one was pending. - virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE; + virtual void RenderProcessGone(base::TerminationStatus status) override; // Cancels the print job. - virtual void NavigationStopped() OVERRIDE; + virtual void NavigationStopped() override; private: explicit PrintViewManager(content::WebContents* web_contents); diff --git a/src/browser/printing/printing_message_filter.cc b/src/browser/printing/printing_message_filter.cc index 08606ac886..fae9f34a7f 100644 --- a/src/browser/printing/printing_message_filter.cc +++ b/src/browser/printing/printing_message_filter.cc @@ -2,33 +2,46 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/nw/src/browser/printing/printing_message_filter.h" +#include "chrome/browser/printing/printing_message_filter.h" #include #include "base/bind.h" -#include "content/nw/src/browser/printing/printer_query.h" -#include "content/nw/src/browser/printing/print_job_manager.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/printing/print_job_manager.h" +#include "chrome/browser/printing/printer_query.h" #include "content/nw/src/common/print_messages.h" -#include "content/nw/src/shell_content_browser_client.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_view.h" -#include "content/public/common/content_client.h" +#include "content/public/common/child_process_host.h" +#include "content/nw/src/shell_content_browser_client.h" + +#if defined(ENABLE_PRINT_PREVIEW) +#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h" +#endif #if defined(OS_CHROMEOS) #include #include -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/lazy_instance.h" #include "chrome/browser/printing/print_dialog_cloud.h" #endif +#if defined(OS_ANDROID) +#include "base/strings/string_number_conversions.h" +#include "chrome/browser/android/tab_android.h" +#include "chrome/browser/printing/print_view_manager_basic.h" +#include "printing/printing_context_android.h" +#endif + using content::BrowserThread; +namespace printing { + namespace { #if defined(OS_CHROMEOS) @@ -44,7 +57,7 @@ static base::LazyInstance g_printing_file_descriptor_map = LAZY_INSTANCE_INITIALIZER; #endif -void RenderParamsFromPrintSettings(const printing::PrintSettings& settings, +void RenderParamsFromPrintSettings(const PrintSettings& settings, PrintMsg_Print_Params* params) { params->page_size = settings.page_setup_device_units().physical_size(); params->content_size.SetSize( @@ -77,12 +90,13 @@ void RenderParamsFromPrintSettings(const printing::PrintSettings& settings, } // namespace PrintingMessageFilter::PrintingMessageFilter(int render_process_id) - : BrowserMessageFilter(PrintMsgStart), print_job_manager_(NULL), - render_process_id_(render_process_id) { - + : BrowserMessageFilter(PrintMsgStart), + render_process_id_(render_process_id) { content::ShellContentBrowserClient* browser_client = static_cast(content::GetContentClient()->browser()); - print_job_manager_ = browser_client->print_job_manager(); + queue_ = browser_client->print_job_manager()->queue(); + + DCHECK(queue_.get()); } PrintingMessageFilter::~PrintingMessageFilter() { @@ -95,17 +109,21 @@ void PrintingMessageFilter::OverrideThreadForMessage( message.type() == PrintHostMsg_TempFileForPrintingWritten::ID) { *thread = BrowserThread::FILE; } +#elif defined(OS_ANDROID) + if (message.type() == PrintHostMsg_AllocateTempFileForPrinting::ID || + message.type() == PrintHostMsg_TempFileForPrintingWritten::ID) { + *thread = BrowserThread::UI; + } #endif } -bool PrintingMessageFilter::OnMessageReceived(const IPC::Message& message, - bool* message_was_ok) { +bool PrintingMessageFilter::OnMessageReceived(const IPC::Message& message) { bool handled = true; - IPC_BEGIN_MESSAGE_MAP_EX(PrintingMessageFilter, message, *message_was_ok) + IPC_BEGIN_MESSAGE_MAP(PrintingMessageFilter, message) #if defined(OS_WIN) IPC_MESSAGE_HANDLER(PrintHostMsg_DuplicateSection, OnDuplicateSection) #endif -#if defined(OS_CHROMEOS) +#if defined(OS_CHROMEOS) || defined(OS_ANDROID) IPC_MESSAGE_HANDLER(PrintHostMsg_AllocateTempFileForPrinting, OnAllocateTempFileForPrinting) IPC_MESSAGE_HANDLER(PrintHostMsg_TempFileForPrintingWritten, @@ -117,7 +135,9 @@ bool PrintingMessageFilter::OnMessageReceived(const IPC::Message& message, IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_ScriptedPrint, OnScriptedPrint) IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_UpdatePrintSettings, OnUpdatePrintSettings) +#if defined(ENABLE_PRINT_PREVIEW) IPC_MESSAGE_HANDLER(PrintHostMsg_CheckForCancel, OnCheckForCancel) +#endif IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; @@ -134,10 +154,14 @@ void PrintingMessageFilter::OnDuplicateSection( } #endif -#if defined(OS_CHROMEOS) +#if defined(OS_CHROMEOS) || defined(OS_ANDROID) void PrintingMessageFilter::OnAllocateTempFileForPrinting( - base::FileDescriptor* temp_file_fd, int* sequence_number) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + int render_view_id, + base::FileDescriptor* temp_file_fd, + int* sequence_number) { +#if defined(OS_CHROMEOS) + // TODO(thestig): Use |render_view_id| for Chrome OS. + DCHECK_CURRENTLY_ON(BrowserThread::FILE); temp_file_fd->fd = *sequence_number = -1; temp_file_fd->auto_close = false; @@ -145,7 +169,7 @@ void PrintingMessageFilter::OnAllocateTempFileForPrinting( *sequence_number = g_printing_file_descriptor_map.Get().sequence++; base::FilePath path; - if (file_util::CreateTemporaryFile(&path)) { + if (base::CreateTemporaryFile(&path)) { int fd = open(path.value().c_str(), O_WRONLY); if (fd >= 0) { SequenceToPathMap::iterator it = map->find(*sequence_number); @@ -159,11 +183,26 @@ void PrintingMessageFilter::OnAllocateTempFileForPrinting( } } } +#elif defined(OS_ANDROID) + DCHECK_CURRENTLY_ON(BrowserThread::UI); + content::WebContents* wc = GetWebContentsForRenderView(render_view_id); + if (!wc) + return; + PrintViewManagerBasic* print_view_manager = + PrintViewManagerBasic::FromWebContents(wc); + // The file descriptor is originally created in & passed from the Android + // side, and it will handle the closing. + const base::FileDescriptor& file_descriptor = + print_view_manager->file_descriptor(); + temp_file_fd->fd = file_descriptor.fd; + temp_file_fd->auto_close = false; +#endif } void PrintingMessageFilter::OnTempFileForPrintingWritten(int render_view_id, int sequence_number) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); +#if defined(OS_CHROMEOS) + DCHECK_CURRENTLY_ON(BrowserThread::FILE); SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map; SequenceToPathMap::iterator it = map->find(sequence_number); if (it == map->end()) { @@ -178,107 +217,81 @@ void PrintingMessageFilter::OnTempFileForPrintingWritten(int render_view_id, // Erase the entry in the map. map->erase(it); +#elif defined(OS_ANDROID) + DCHECK_CURRENTLY_ON(BrowserThread::UI); + content::WebContents* wc = GetWebContentsForRenderView(render_view_id); + if (!wc) + return; + PrintViewManagerBasic* print_view_manager = + PrintViewManagerBasic::FromWebContents(wc); + const base::FileDescriptor& file_descriptor = + print_view_manager->file_descriptor(); + PrintingContextAndroid::PdfWritingDone(file_descriptor.fd, true); + // Invalidate the file descriptor so it doesn't accidentally get reused. + print_view_manager->set_file_descriptor(base::FileDescriptor(-1, false)); +#endif } +#endif // defined(OS_CHROMEOS) || defined(OS_ANDROID) +#if defined(OS_CHROMEOS) void PrintingMessageFilter::CreatePrintDialogForFile( int render_view_id, const base::FilePath& path) { content::WebContents* wc = GetWebContentsForRenderView(render_view_id); + if (!wc) + return; print_dialog_cloud::CreatePrintDialogForFile( wc->GetBrowserContext(), - wc->GetView()->GetTopLevelNativeWindow(), + wc->GetTopLevelNativeWindow(), path, - string16(), - string16(), - std::string("application/pdf"), - false); + wc->GetTitle(), + base::string16(), + std::string("application/pdf")); } #endif // defined(OS_CHROMEOS) content::WebContents* PrintingMessageFilter::GetWebContentsForRenderView( int render_view_id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_CURRENTLY_ON(BrowserThread::UI); content::RenderViewHost* view = content::RenderViewHost::FromID( render_process_id_, render_view_id); - return content::WebContents::FromRenderViewHost(view); -} - -struct PrintingMessageFilter::GetPrintSettingsForRenderViewParams { - printing::PrinterQuery::GetSettingsAskParam ask_user_for_settings; - int expected_page_count; - bool has_selection; - printing::MarginType margin_type; -}; - -void PrintingMessageFilter::GetPrintSettingsForRenderView( - int render_view_id, - GetPrintSettingsForRenderViewParams params, - const base::Closure& callback, - scoped_refptr printer_query) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - content::WebContents* wc = GetWebContentsForRenderView(render_view_id); - if (wc) { - scoped_ptr wc_observer( - new PrintingUIWebContentsObserver(wc)); - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&printing::PrinterQuery::GetSettings, printer_query, - params.ask_user_for_settings, base::Passed(&wc_observer), - params.expected_page_count, params.has_selection, - params.margin_type, callback)); - } else { - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&PrintingMessageFilter::OnGetPrintSettingsFailed, this, - callback, printer_query)); - } + return view ? content::WebContents::FromRenderViewHost(view) : NULL; } void PrintingMessageFilter::OnIsPrintingEnabled(bool* is_enabled) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CURRENTLY_ON(BrowserThread::IO); *is_enabled = true; } -void PrintingMessageFilter::OnGetPrintSettingsFailed( - const base::Closure& callback, - scoped_refptr printer_query) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - printer_query->GetSettingsDone(printing::PrintSettings(), - printing::PrintingContext::FAILED); - callback.Run(); -} - void PrintingMessageFilter::OnGetDefaultPrintSettings(IPC::Message* reply_msg) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - scoped_refptr printer_query; - print_job_manager_->PopPrinterQuery(0, &printer_query); + DCHECK_CURRENTLY_ON(BrowserThread::IO); + scoped_refptr printer_query; + printer_query = queue_->PopPrinterQuery(0); if (!printer_query.get()) { - printer_query = new printing::PrinterQuery; - printer_query->SetWorkerDestination(print_job_manager_->destination()); + printer_query = + queue_->CreatePrinterQuery(render_process_id_, reply_msg->routing_id()); } // Loads default settings. This is asynchronous, only the IPC message sender // will hang until the settings are retrieved. - GetPrintSettingsForRenderViewParams params; - params.ask_user_for_settings = printing::PrinterQuery::DEFAULTS; - params.expected_page_count = 0; - params.has_selection = false; - params.margin_type = printing::DEFAULT_MARGINS; - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&PrintingMessageFilter::GetPrintSettingsForRenderView, this, - reply_msg->routing_id(), params, - base::Bind(&PrintingMessageFilter::OnGetDefaultPrintSettingsReply, - this, printer_query, reply_msg), - printer_query)); + printer_query->GetSettings( + PrinterQuery::GetSettingsAskParam::DEFAULTS, + 0, + false, + DEFAULT_MARGINS, + false, + base::Bind(&PrintingMessageFilter::OnGetDefaultPrintSettingsReply, + this, + printer_query, + reply_msg)); } void PrintingMessageFilter::OnGetDefaultPrintSettingsReply( - scoped_refptr printer_query, + scoped_refptr printer_query, IPC::Message* reply_msg) { PrintMsg_Print_Params params; if (!printer_query.get() || - printer_query->last_status() != printing::PrintingContext::OK) { + printer_query->last_status() != PrintingContext::OK) { params.Reset(); } else { RenderParamsFromPrintSettings(printer_query->settings(), ¶ms); @@ -290,7 +303,7 @@ void PrintingMessageFilter::OnGetDefaultPrintSettingsReply( if (printer_query.get()) { // If user hasn't cancelled. if (printer_query->cookie() && printer_query->settings().dpi()) { - print_job_manager_->QueuePrinterQuery(printer_query.get()); + queue_->QueuePrinterQuery(printer_query.get()); } else { printer_query->StopWorker(); } @@ -300,90 +313,134 @@ void PrintingMessageFilter::OnGetDefaultPrintSettingsReply( void PrintingMessageFilter::OnScriptedPrint( const PrintHostMsg_ScriptedPrint_Params& params, IPC::Message* reply_msg) { - scoped_refptr printer_query; - print_job_manager_->PopPrinterQuery(params.cookie, &printer_query); + scoped_refptr printer_query = + queue_->PopPrinterQuery(params.cookie); if (!printer_query.get()) { - printer_query = new printing::PrinterQuery; - printer_query->SetWorkerDestination(print_job_manager_->destination()); + printer_query = + queue_->CreatePrinterQuery(render_process_id_, reply_msg->routing_id()); } - GetPrintSettingsForRenderViewParams settings_params; - settings_params.ask_user_for_settings = printing::PrinterQuery::ASK_USER; - settings_params.expected_page_count = params.expected_pages_count; - settings_params.has_selection = params.has_selection; - settings_params.margin_type = params.margin_type; - - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&PrintingMessageFilter::GetPrintSettingsForRenderView, this, - reply_msg->routing_id(), settings_params, - base::Bind(&PrintingMessageFilter::OnScriptedPrintReply, this, - printer_query, reply_msg), - printer_query)); + printer_query->GetSettings( + PrinterQuery::GetSettingsAskParam::ASK_USER, + params.expected_pages_count, + params.has_selection, + params.margin_type, + params.is_scripted, + base::Bind(&PrintingMessageFilter::OnScriptedPrintReply, + this, + printer_query, + reply_msg)); } void PrintingMessageFilter::OnScriptedPrintReply( - scoped_refptr printer_query, + scoped_refptr printer_query, IPC::Message* reply_msg) { PrintMsg_PrintPages_Params params; - if (printer_query->last_status() != printing::PrintingContext::OK || +#if defined(OS_ANDROID) + // We need to save the routing ID here because Send method below deletes the + // |reply_msg| before we can get the routing ID for the Android code. + int routing_id = reply_msg->routing_id(); +#endif + if (printer_query->last_status() != PrintingContext::OK || !printer_query->settings().dpi()) { params.Reset(); } else { RenderParamsFromPrintSettings(printer_query->settings(), ¶ms.params); params.params.document_cookie = printer_query->cookie(); - params.pages = - printing::PageRange::GetPages(printer_query->settings().ranges()); + params.pages = PageRange::GetPages(printer_query->settings().ranges()); } PrintHostMsg_ScriptedPrint::WriteReplyParams(reply_msg, params); Send(reply_msg); if (params.params.dpi && params.params.document_cookie) { - print_job_manager_->QueuePrinterQuery(printer_query.get()); +#if defined(OS_ANDROID) + int file_descriptor; + const base::string16& device_name = printer_query->settings().device_name(); + if (base::StringToInt(device_name, &file_descriptor)) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&PrintingMessageFilter::UpdateFileDescriptor, this, + routing_id, file_descriptor)); + } +#endif + queue_->QueuePrinterQuery(printer_query.get()); } else { printer_query->StopWorker(); } } +#if defined(OS_ANDROID) +void PrintingMessageFilter::UpdateFileDescriptor(int render_view_id, int fd) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + content::WebContents* wc = GetWebContentsForRenderView(render_view_id); + if (!wc) + return; + PrintViewManagerBasic* print_view_manager = + PrintViewManagerBasic::FromWebContents(wc); + print_view_manager->set_file_descriptor(base::FileDescriptor(fd, false)); +} +#endif + void PrintingMessageFilter::OnUpdatePrintSettings( - int document_cookie, const base::DictionaryValue& job_settings, - IPC::Message* reply_msg) { - scoped_refptr printer_query; + int document_cookie, const base::DictionaryValue& job_settings, + IPC::Message* reply_msg) { + scoped_ptr new_settings(job_settings.DeepCopy()); - print_job_manager_->PopPrinterQuery(document_cookie, &printer_query); + scoped_refptr printer_query; + + printer_query = queue_->PopPrinterQuery(document_cookie); if (!printer_query.get()) { - printer_query = new printing::PrinterQuery; - printer_query->SetWorkerDestination(print_job_manager_->destination()); + int host_id = render_process_id_; + int routing_id = reply_msg->routing_id(); + if (!new_settings->GetInteger(printing::kPreviewInitiatorHostId, + &host_id) || + !new_settings->GetInteger(printing::kPreviewInitiatorRoutingId, + &routing_id)) { + host_id = content::ChildProcessHost::kInvalidUniqueID; + routing_id = content::ChildProcessHost::kInvalidUniqueID; + } + printer_query = queue_->CreatePrinterQuery(host_id, routing_id); } printer_query->SetSettings( - job_settings, + new_settings.Pass(), base::Bind(&PrintingMessageFilter::OnUpdatePrintSettingsReply, this, printer_query, reply_msg)); } void PrintingMessageFilter::OnUpdatePrintSettingsReply( - scoped_refptr printer_query, + scoped_refptr printer_query, IPC::Message* reply_msg) { PrintMsg_PrintPages_Params params; if (!printer_query.get() || - printer_query->last_status() != printing::PrintingContext::OK) { + printer_query->last_status() != PrintingContext::OK) { params.Reset(); } else { RenderParamsFromPrintSettings(printer_query->settings(), ¶ms.params); params.params.document_cookie = printer_query->cookie(); - params.pages = - printing::PageRange::GetPages(printer_query->settings().ranges()); + params.pages = PageRange::GetPages(printer_query->settings().ranges()); } - PrintHostMsg_UpdatePrintSettings::WriteReplyParams(reply_msg, params); + PrintHostMsg_UpdatePrintSettings::WriteReplyParams( + reply_msg, + params, + printer_query.get() && + (printer_query->last_status() == printing::PrintingContext::CANCEL)); Send(reply_msg); // If user hasn't cancelled. if (printer_query.get()) { - if (printer_query->cookie() && printer_query->settings().dpi()) - print_job_manager_->QueuePrinterQuery(printer_query.get()); - else + if (printer_query->cookie() && printer_query->settings().dpi()) { + queue_->QueuePrinterQuery(printer_query.get()); + } else { printer_query->StopWorker(); + } } } +#if defined(ENABLE_PRINT_PREVIEW) void PrintingMessageFilter::OnCheckForCancel(int32 preview_ui_id, int preview_request_id, bool* cancel) { + PrintPreviewUI::GetCurrentPrintPreviewStatus(preview_ui_id, + preview_request_id, + cancel); } +#endif + +} // namespace printing diff --git a/src/browser/printing/printing_message_filter.h b/src/browser/printing/printing_message_filter.h index b74f7cdcab..0a12d59a07 100644 --- a/src/browser/printing/printing_message_filter.h +++ b/src/browser/printing/printing_message_filter.h @@ -2,12 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef NW_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_ -#define NW_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_ +#ifndef CHROME_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_ +#define CHROME_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_ #include #include "base/compiler_specific.h" +#include "base/prefs/pref_member.h" #include "content/public/browser/browser_message_filter.h" #if defined(OS_WIN) @@ -15,6 +16,8 @@ #endif struct PrintHostMsg_ScriptedPrint_Params; +class Profile; +class ProfileIOData; namespace base { class DictionaryValue; @@ -26,9 +29,10 @@ class WebContents; } namespace printing { -class PrinterQuery; + class PrintJobManager; -} +class PrintQueriesQueue; +class PrinterQuery; // This class filters out incoming printing related IPC messages for the // renderer process on the IPC thread. @@ -37,14 +41,12 @@ class PrintingMessageFilter : public content::BrowserMessageFilter { PrintingMessageFilter(int render_process_id); // content::BrowserMessageFilter methods. - virtual void OverrideThreadForMessage( - const IPC::Message& message, - content::BrowserThread::ID* thread) OVERRIDE; - virtual bool OnMessageReceived(const IPC::Message& message, - bool* message_was_ok) OVERRIDE; + void OverrideThreadForMessage(const IPC::Message& message, + content::BrowserThread::ID* thread) override; + bool OnMessageReceived(const IPC::Message& message) override; private: - virtual ~PrintingMessageFilter(); + ~PrintingMessageFilter() override; #if defined(OS_WIN) // Used to pass resulting EMF from renderer to browser in printing. @@ -52,15 +54,25 @@ class PrintingMessageFilter : public content::BrowserMessageFilter { base::SharedMemoryHandle* browser_handle); #endif -#if defined(OS_CHROMEOS) +#if defined(OS_CHROMEOS) || defined(OS_ANDROID) // Used to ask the browser allocate a temporary file for the renderer // to fill in resulting PDF in renderer. - void OnAllocateTempFileForPrinting(base::FileDescriptor* temp_file_fd, + void OnAllocateTempFileForPrinting(int render_view_id, + base::FileDescriptor* temp_file_fd, int* sequence_number); void OnTempFileForPrintingWritten(int render_view_id, int sequence_number); +#endif + +#if defined(OS_CHROMEOS) void CreatePrintDialogForFile(int render_view_id, const base::FilePath& path); #endif +#if defined(OS_ANDROID) + // Updates the file descriptor for the PrintViewManagerBasic of a given + // render_view_id. + void UpdateFileDescriptor(int render_view_id, int fd); +#endif + // Given a render_view_id get the corresponding WebContents. // Must be called on the UI thread. content::WebContents* GetWebContentsForRenderView(int render_view_id); @@ -71,35 +83,21 @@ class PrintingMessageFilter : public content::BrowserMessageFilter { // to base::Bind. struct GetPrintSettingsForRenderViewParams; - // Retrieve print settings. Uses |render_view_id| to get a parent - // for any UI created if needed. - void GetPrintSettingsForRenderView( - int render_view_id, - GetPrintSettingsForRenderViewParams params, - const base::Closure& callback, - scoped_refptr printer_query); - - void OnGetPrintSettingsFailed( - const base::Closure& callback, - scoped_refptr printer_query); - // Checks if printing is enabled. void OnIsPrintingEnabled(bool* is_enabled); // Get the default print setting. void OnGetDefaultPrintSettings(IPC::Message* reply_msg); - void OnGetDefaultPrintSettingsReply( - scoped_refptr printer_query, - IPC::Message* reply_msg); + void OnGetDefaultPrintSettingsReply(scoped_refptr printer_query, + IPC::Message* reply_msg); // The renderer host have to show to the user the print dialog and returns // the selected print settings. The task is handled by the print worker // thread and the UI thread. The reply occurs on the IO thread. void OnScriptedPrint(const PrintHostMsg_ScriptedPrint_Params& params, IPC::Message* reply_msg); - void OnScriptedPrintReply( - scoped_refptr printer_query, - IPC::Message* reply_msg); + void OnScriptedPrintReply(scoped_refptr printer_query, + IPC::Message* reply_msg); // Modify the current print settings based on |job_settings|. The task is // handled by the print worker thread and the UI thread. The reply occurs on @@ -107,20 +105,23 @@ class PrintingMessageFilter : public content::BrowserMessageFilter { void OnUpdatePrintSettings(int document_cookie, const base::DictionaryValue& job_settings, IPC::Message* reply_msg); - void OnUpdatePrintSettingsReply( - scoped_refptr printer_query, - IPC::Message* reply_msg); + void OnUpdatePrintSettingsReply(scoped_refptr printer_query, + IPC::Message* reply_msg); +#if defined(ENABLE_PRINT_PREVIEW) // Check to see if print preview has been cancelled. void OnCheckForCancel(int32 preview_ui_id, int preview_request_id, bool* cancel); +#endif - printing::PrintJobManager* print_job_manager_; + const int render_process_id_; - int render_process_id_; + scoped_refptr queue_; DISALLOW_COPY_AND_ASSIGN(PrintingMessageFilter); }; +} // namespace printing + #endif // CHROME_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_ diff --git a/src/browser/printing/printing_ui_web_contents_observer.cc b/src/browser/printing/printing_ui_web_contents_observer.cc index 6353c5dccb..23be554307 100644 --- a/src/browser/printing/printing_ui_web_contents_observer.cc +++ b/src/browser/printing/printing_ui_web_contents_observer.cc @@ -6,7 +6,6 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_view.h" PrintingUIWebContentsObserver::PrintingUIWebContentsObserver( content::WebContents* web_contents) @@ -16,5 +15,5 @@ PrintingUIWebContentsObserver::PrintingUIWebContentsObserver( gfx::NativeView PrintingUIWebContentsObserver::GetParentView() { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - return web_contents() ? web_contents()->GetView()->GetNativeView() : NULL; + return web_contents() ? web_contents()->GetNativeView() : NULL; } diff --git a/src/browser/printing_handler.cc b/src/browser/printing_handler.cc new file mode 100644 index 0000000000..9d354d4aa5 --- /dev/null +++ b/src/browser/printing_handler.cc @@ -0,0 +1,540 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/utility/printing_handler.h" + +#include "base/files/file_util.h" +#include "base/lazy_instance.h" +#include "base/path_service.h" +#include "base/scoped_native_library.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_utility_printing_messages.h" +#include "chrome/utility/cloud_print/bitmap_image.h" +#include "chrome/utility/cloud_print/pwg_encoder.h" +#include "content/public/utility/utility_thread.h" +#include "printing/page_range.h" +#include "printing/pdf_render_settings.h" + +#if defined(OS_WIN) +#include "base/win/iat_patch_function.h" +#include "printing/emf_win.h" +#include "ui/gfx/gdi_util.h" +#endif + +#if defined(ENABLE_PRINT_PREVIEW) +#include "chrome/common/crash_keys.h" +#include "printing/backend/print_backend.h" +#endif + +namespace { + // File name of the internal PDF plugin on different platforms. +const base::FilePath::CharType kInternalPDFPluginFileName[] = +#if defined(OS_WIN) + FILE_PATH_LITERAL("pdf.dll"); +#elif defined(OS_MACOSX) + FILE_PATH_LITERAL("PDF.plugin"); +#else // Linux and Chrome OS + FILE_PATH_LITERAL("libpdf.so"); +#endif + +bool Send(IPC::Message* message) { + return content::UtilityThread::Get()->Send(message); +} + +void ReleaseProcessIfNeeded() { + content::UtilityThread::Get()->ReleaseProcessIfNeeded(); +} + +class PdfFunctionsBase { + public: + PdfFunctionsBase() : render_pdf_to_bitmap_func_(NULL), + get_pdf_doc_info_func_(NULL) {} + + bool Init() { + base::FilePath pdf_module_path; + if (!PathService::Get(base::DIR_MODULE, &pdf_module_path)) + return false; + pdf_module_path = pdf_module_path.Append(kInternalPDFPluginFileName); + if (!base::PathExists(pdf_module_path)) { + return false; + } + + pdf_lib_.Reset(base::LoadNativeLibrary(pdf_module_path, NULL)); + if (!pdf_lib_.is_valid()) { + LOG(WARNING) << "Couldn't load PDF plugin"; + return false; + } + + render_pdf_to_bitmap_func_ = + reinterpret_cast( + pdf_lib_.GetFunctionPointer("RenderPDFPageToBitmap")); + LOG_IF(WARNING, !render_pdf_to_bitmap_func_) << + "Missing RenderPDFPageToBitmap"; + + get_pdf_doc_info_func_ = + reinterpret_cast( + pdf_lib_.GetFunctionPointer("GetPDFDocInfo")); + LOG_IF(WARNING, !get_pdf_doc_info_func_) << "Missing GetPDFDocInfo"; + + if (!render_pdf_to_bitmap_func_ || !get_pdf_doc_info_func_ || + !PlatformInit(pdf_module_path, pdf_lib_)) { + Reset(); + } + + return IsValid(); + } + + bool IsValid() const { + return pdf_lib_.is_valid(); + } + + void Reset() { + pdf_lib_.Reset(NULL); + } + + bool RenderPDFPageToBitmap(const void* pdf_buffer, + int pdf_buffer_size, + int page_number, + void* bitmap_buffer, + int bitmap_width, + int bitmap_height, + int dpi_x, + int dpi_y, + bool autorotate) { + if (!render_pdf_to_bitmap_func_) + return false; + return render_pdf_to_bitmap_func_(pdf_buffer, pdf_buffer_size, page_number, + bitmap_buffer, bitmap_width, + bitmap_height, dpi_x, dpi_y, autorotate); + } + + bool GetPDFDocInfo(const void* pdf_buffer, + int buffer_size, + int* page_count, + double* max_page_width) { + if (!get_pdf_doc_info_func_) + return false; + return get_pdf_doc_info_func_(pdf_buffer, buffer_size, page_count, + max_page_width); + } + + protected: + virtual bool PlatformInit( + const base::FilePath& pdf_module_path, + const base::ScopedNativeLibrary& pdf_lib) { + return true; + } + + private: + // Exported by PDF plugin. + typedef bool (*RenderPDFPageToBitmapProc)(const void* pdf_buffer, + int pdf_buffer_size, + int page_number, + void* bitmap_buffer, + int bitmap_width, + int bitmap_height, + int dpi_x, + int dpi_y, + bool autorotate); + typedef bool (*GetPDFDocInfoProc)(const void* pdf_buffer, + int buffer_size, int* page_count, + double* max_page_width); + + RenderPDFPageToBitmapProc render_pdf_to_bitmap_func_; + GetPDFDocInfoProc get_pdf_doc_info_func_; + + base::ScopedNativeLibrary pdf_lib_; + DISALLOW_COPY_AND_ASSIGN(PdfFunctionsBase); +}; + +#if defined(OS_WIN) +// The 2 below IAT patch functions are almost identical to the code in +// render_process_impl.cc. This is needed to work around specific Windows APIs +// used by the Chrome PDF plugin that will fail in the sandbox. +static base::win::IATPatchFunction g_iat_patch_createdca; +HDC WINAPI UtilityProcess_CreateDCAPatch(LPCSTR driver_name, + LPCSTR device_name, + LPCSTR output, + const DEVMODEA* init_data) { + if (driver_name && (std::string("DISPLAY") == driver_name)) { + // CreateDC fails behind the sandbox, but not CreateCompatibleDC. + return CreateCompatibleDC(NULL); + } + + NOTREACHED(); + return CreateDCA(driver_name, device_name, output, init_data); +} + +static base::win::IATPatchFunction g_iat_patch_get_font_data; +DWORD WINAPI UtilityProcess_GetFontDataPatch( + HDC hdc, DWORD table, DWORD offset, LPVOID buffer, DWORD length) { + int rv = GetFontData(hdc, table, offset, buffer, length); + if (rv == GDI_ERROR && hdc) { + HFONT font = static_cast(GetCurrentObject(hdc, OBJ_FONT)); + + LOGFONT logfont; + if (GetObject(font, sizeof(LOGFONT), &logfont)) { + content::UtilityThread::Get()->PreCacheFont(logfont); + rv = GetFontData(hdc, table, offset, buffer, length); + content::UtilityThread::Get()->ReleaseCachedFonts(); + } + } + return rv; +} + +class PdfFunctionsWin : public PdfFunctionsBase { + public: + PdfFunctionsWin() : render_pdf_to_dc_func_(NULL) { + } + + bool PlatformInit( + const base::FilePath& pdf_module_path, + const base::ScopedNativeLibrary& pdf_lib) override { + // Patch the IAT for handling specific APIs known to fail in the sandbox. + if (!g_iat_patch_createdca.is_patched()) { + g_iat_patch_createdca.Patch(pdf_module_path.value().c_str(), + "gdi32.dll", "CreateDCA", + UtilityProcess_CreateDCAPatch); + } + + if (!g_iat_patch_get_font_data.is_patched()) { + g_iat_patch_get_font_data.Patch(pdf_module_path.value().c_str(), + "gdi32.dll", "GetFontData", + UtilityProcess_GetFontDataPatch); + } + render_pdf_to_dc_func_ = + reinterpret_cast( + pdf_lib.GetFunctionPointer("RenderPDFPageToDC")); + LOG_IF(WARNING, !render_pdf_to_dc_func_) << "Missing RenderPDFPageToDC"; + + return render_pdf_to_dc_func_ != NULL; + } + + bool RenderPDFPageToDC(const void* pdf_buffer, + int buffer_size, + int page_number, + HDC dc, + int dpi_x, + int dpi_y, + int bounds_origin_x, + int bounds_origin_y, + int bounds_width, + int bounds_height, + bool fit_to_bounds, + bool stretch_to_bounds, + bool keep_aspect_ratio, + bool center_in_bounds, + bool autorotate) { + if (!render_pdf_to_dc_func_) + return false; + return render_pdf_to_dc_func_(pdf_buffer, buffer_size, page_number, + dc, dpi_x, dpi_y, bounds_origin_x, + bounds_origin_y, bounds_width, bounds_height, + fit_to_bounds, stretch_to_bounds, + keep_aspect_ratio, center_in_bounds, + autorotate); + } + + private: + // Exported by PDF plugin. + typedef bool (*RenderPDFPageToDCProc)( + const void* pdf_buffer, int buffer_size, int page_number, HDC dc, + int dpi_x, int dpi_y, int bounds_origin_x, int bounds_origin_y, + int bounds_width, int bounds_height, bool fit_to_bounds, + bool stretch_to_bounds, bool keep_aspect_ratio, bool center_in_bounds, + bool autorotate); + RenderPDFPageToDCProc render_pdf_to_dc_func_; + + DISALLOW_COPY_AND_ASSIGN(PdfFunctionsWin); +}; + +typedef PdfFunctionsWin PdfFunctions; +#else // OS_WIN +typedef PdfFunctionsBase PdfFunctions; +#endif // OS_WIN + +base::LazyInstance g_pdf_lib = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +PrintingHandler::PrintingHandler() {} + +PrintingHandler::~PrintingHandler() {} + +// static +void PrintingHandler::PreSandboxStartup() { + g_pdf_lib.Get().Init(); +} + +bool PrintingHandler::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(PrintingHandler, message) +#if defined(OS_WIN) + IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RenderPDFPagesToMetafiles, + OnRenderPDFPagesToMetafile) + IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage, + OnRenderPDFPagesToMetafileGetPage) + IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop, + OnRenderPDFPagesToMetafileStop) +#endif // OS_WIN +#if defined(ENABLE_PRINT_PREVIEW) + IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RenderPDFPagesToPWGRaster, + OnRenderPDFPagesToPWGRaster) + IPC_MESSAGE_HANDLER(ChromeUtilityMsg_GetPrinterCapsAndDefaults, + OnGetPrinterCapsAndDefaults) + IPC_MESSAGE_HANDLER(ChromeUtilityMsg_GetPrinterSemanticCapsAndDefaults, + OnGetPrinterSemanticCapsAndDefaults) +#endif // ENABLE_PRINT_PREVIEW + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +#if defined(OS_WIN) +void PrintingHandler::OnRenderPDFPagesToMetafile( + IPC::PlatformFileForTransit pdf_transit, + const printing::PdfRenderSettings& settings) { + pdf_rendering_settings_ = settings; + base::File pdf_file = IPC::PlatformFileForTransitToFile(pdf_transit); + int page_count = LoadPDF(pdf_file.Pass()); + Send( + new ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount(page_count)); +} + +void PrintingHandler::OnRenderPDFPagesToMetafileGetPage( + int page_number, + IPC::PlatformFileForTransit output_file) { + base::File emf_file = IPC::PlatformFileForTransitToFile(output_file); + float scale_factor = 1.0f; + bool success = + RenderPdfPageToMetafile(page_number, emf_file.Pass(), &scale_factor); + Send(new ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone( + success, scale_factor)); +} + +void PrintingHandler::OnRenderPDFPagesToMetafileStop() { + ReleaseProcessIfNeeded(); +} + +#endif // OS_WIN + +#if defined(ENABLE_PRINT_PREVIEW) +void PrintingHandler::OnRenderPDFPagesToPWGRaster( + IPC::PlatformFileForTransit pdf_transit, + const printing::PdfRenderSettings& settings, + const printing::PwgRasterSettings& bitmap_settings, + IPC::PlatformFileForTransit bitmap_transit) { + base::File pdf = IPC::PlatformFileForTransitToFile(pdf_transit); + base::File bitmap = IPC::PlatformFileForTransitToFile(bitmap_transit); + if (RenderPDFPagesToPWGRaster(pdf.Pass(), settings, bitmap_settings, + bitmap.Pass())) { + Send(new ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Succeeded()); + } else { + Send(new ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Failed()); + } + ReleaseProcessIfNeeded(); +} +#endif // ENABLE_PRINT_PREVIEW + +#if defined(OS_WIN) +int PrintingHandler::LoadPDF(base::File pdf_file) { + if (!g_pdf_lib.Get().IsValid()) + return 0; + + int64 length64 = pdf_file.GetLength(); + if (length64 <= 0 || length64 > std::numeric_limits::max()) + return 0; + int length = static_cast(length64); + + pdf_data_.resize(length); + if (length != pdf_file.Read(0, pdf_data_.data(), pdf_data_.size())) + return 0; + + int total_page_count = 0; + if (!g_pdf_lib.Get().GetPDFDocInfo( + &pdf_data_.front(), pdf_data_.size(), &total_page_count, NULL)) { + return 0; + } + return total_page_count; +} + +bool PrintingHandler::RenderPdfPageToMetafile(int page_number, + base::File output_file, + float* scale_factor) { + printing::Emf metafile; + metafile.Init(); + + // We need to scale down DC to fit an entire page into DC available area. + // Current metafile is based on screen DC and have current screen size. + // Writing outside of those boundaries will result in the cut-off output. + // On metafiles (this is the case here), scaling down will still record + // original coordinates and we'll be able to print in full resolution. + // Before playback we'll need to counter the scaling up that will happen + // in the service (print_system_win.cc). + *scale_factor = + gfx::CalculatePageScale(metafile.context(), + pdf_rendering_settings_.area().right(), + pdf_rendering_settings_.area().bottom()); + gfx::ScaleDC(metafile.context(), *scale_factor); + + // The underlying metafile is of type Emf and ignores the arguments passed + // to StartPage. + metafile.StartPage(gfx::Size(), gfx::Rect(), 1); + if (!g_pdf_lib.Get().RenderPDFPageToDC( + &pdf_data_.front(), + pdf_data_.size(), + page_number, + metafile.context(), + pdf_rendering_settings_.dpi(), + pdf_rendering_settings_.dpi(), + pdf_rendering_settings_.area().x(), + pdf_rendering_settings_.area().y(), + pdf_rendering_settings_.area().width(), + pdf_rendering_settings_.area().height(), + true, + false, + true, + true, + pdf_rendering_settings_.autorotate())) { + return false; + } + metafile.FinishPage(); + metafile.FinishDocument(); + return metafile.SaveTo(&output_file); +} + +#endif // OS_WIN + +#if defined(ENABLE_PRINT_PREVIEW) +bool PrintingHandler::RenderPDFPagesToPWGRaster( + base::File pdf_file, + const printing::PdfRenderSettings& settings, + const printing::PwgRasterSettings& bitmap_settings, + base::File bitmap_file) { + bool autoupdate = true; + if (!g_pdf_lib.Get().IsValid()) + return false; + + base::File::Info info; + if (!pdf_file.GetInfo(&info) || info.size <= 0 || + info.size > std::numeric_limits::max()) + return false; + int data_size = static_cast(info.size); + + std::string data(data_size, 0); + if (pdf_file.Read(0, &data[0], data_size) != data_size) + return false; + + int total_page_count = 0; + if (!g_pdf_lib.Get().GetPDFDocInfo(data.data(), data_size, + &total_page_count, NULL)) { + return false; + } + + cloud_print::PwgEncoder encoder; + std::string pwg_header; + encoder.EncodeDocumentHeader(&pwg_header); + int bytes_written = bitmap_file.WriteAtCurrentPos(pwg_header.data(), + pwg_header.size()); + if (bytes_written != static_cast(pwg_header.size())) + return false; + + cloud_print::BitmapImage image(settings.area().size(), + cloud_print::BitmapImage::BGRA); + for (int i = 0; i < total_page_count; ++i) { + int page_number = i; + + if (bitmap_settings.reverse_page_order) { + page_number = total_page_count - 1 - page_number; + } + + if (!g_pdf_lib.Get().RenderPDFPageToBitmap(data.data(), + data_size, + page_number, + image.pixel_data(), + image.size().width(), + image.size().height(), + settings.dpi(), + settings.dpi(), + autoupdate)) { + return false; + } + + cloud_print::PwgHeaderInfo header_info; + header_info.dpi = settings.dpi(); + header_info.total_pages = total_page_count; + + // Transform odd pages. + if (page_number % 2) { + switch (bitmap_settings.odd_page_transform) { + case printing::TRANSFORM_NORMAL: + break; + case printing::TRANSFORM_ROTATE_180: + header_info.flipx = true; + header_info.flipy = true; + break; + case printing::TRANSFORM_FLIP_HORIZONTAL: + header_info.flipx = true; + break; + case printing::TRANSFORM_FLIP_VERTICAL: + header_info.flipy = true; + break; + } + } + + if (bitmap_settings.rotate_all_pages) { + header_info.flipx = !header_info.flipx; + header_info.flipy = !header_info.flipy; + } + + std::string pwg_page; + if (!encoder.EncodePage(image, header_info, &pwg_page)) + return false; + bytes_written = bitmap_file.WriteAtCurrentPos(pwg_page.data(), + pwg_page.size()); + if (bytes_written != static_cast(pwg_page.size())) + return false; + } + return true; +} + +void PrintingHandler::OnGetPrinterCapsAndDefaults( + const std::string& printer_name) { + scoped_refptr print_backend = + printing::PrintBackend::CreateInstance(NULL); + printing::PrinterCapsAndDefaults printer_info; + + crash_keys::ScopedPrinterInfo crash_key( + print_backend->GetPrinterDriverInfo(printer_name)); + + if (print_backend->GetPrinterCapsAndDefaults(printer_name, &printer_info)) { + Send(new ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Succeeded( + printer_name, printer_info)); + } else { + Send(new ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Failed( + printer_name)); + } + ReleaseProcessIfNeeded(); +} + +void PrintingHandler::OnGetPrinterSemanticCapsAndDefaults( + const std::string& printer_name) { + scoped_refptr print_backend = + printing::PrintBackend::CreateInstance(NULL); + printing::PrinterSemanticCapsAndDefaults printer_info; + + crash_keys::ScopedPrinterInfo crash_key( + print_backend->GetPrinterDriverInfo(printer_name)); + + if (print_backend->GetPrinterSemanticCapsAndDefaults(printer_name, + &printer_info)) { + Send(new ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Succeeded( + printer_name, printer_info)); + } else { + Send(new ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Failed( + printer_name)); + } + ReleaseProcessIfNeeded(); +} +#endif // ENABLE_PRINT_PREVIEW diff --git a/src/browser/printing_handler.h b/src/browser/printing_handler.h new file mode 100644 index 0000000000..a42c9bb65c --- /dev/null +++ b/src/browser/printing_handler.h @@ -0,0 +1,78 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NW_UTILITY_PRINTING_HANDLER_H_ +#define NW_UTILITY_PRINTING_HANDLER_H_ + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "chrome/utility/utility_message_handler.h" +#include "ipc/ipc_platform_file.h" +#include "printing/pdf_render_settings.h" + +#if !defined(ENABLE_PRINT_PREVIEW) && !defined(OS_WIN) +#error "Windows or full printing must be enabled" +#endif + +namespace printing { +class PdfRenderSettings; +struct PwgRasterSettings; +struct PageRange; +} + +// Dispatches IPCs for printing. +class PrintingHandler : public UtilityMessageHandler { + public: + PrintingHandler(); + ~PrintingHandler() override; + + static void PreSandboxStartup(); + + // IPC::Listener: + bool OnMessageReceived(const IPC::Message& message) override; + + private: + // IPC message handlers. +#if defined(OS_WIN) + void OnRenderPDFPagesToMetafile(IPC::PlatformFileForTransit pdf_transit, + const printing::PdfRenderSettings& settings); + void OnRenderPDFPagesToMetafileGetPage( + int page_number, + IPC::PlatformFileForTransit output_file); + void OnRenderPDFPagesToMetafileStop(); +#endif // OS_WIN +#if defined(ENABLE_PRINT_PREVIEW) + void OnRenderPDFPagesToPWGRaster( + IPC::PlatformFileForTransit pdf_transit, + const printing::PdfRenderSettings& settings, + const printing::PwgRasterSettings& bitmap_settings, + IPC::PlatformFileForTransit bitmap_transit); +#endif // ENABLE_PRINT_PREVIEW + +#if defined(OS_WIN) + int LoadPDF(base::File pdf_file); + bool RenderPdfPageToMetafile(int page_number, + base::File output_file, + float* scale_factor); +#endif // OS_WIN +#if defined(ENABLE_PRINT_PREVIEW) + bool RenderPDFPagesToPWGRaster( + base::File pdf_file, + const printing::PdfRenderSettings& settings, + const printing::PwgRasterSettings& bitmap_settings, + base::File bitmap_file); + + void OnGetPrinterCapsAndDefaults(const std::string& printer_name); + void OnGetPrinterSemanticCapsAndDefaults(const std::string& printer_name); +#endif // ENABLE_PRINT_PREVIEW + +#if defined(OS_WIN) + std::vector pdf_data_; + printing::PdfRenderSettings pdf_rendering_settings_; +#endif + + DISALLOW_COPY_AND_ASSIGN(PrintingHandler); +}; + +#endif // CHROME_UTILITY_PRINTING_HANDLER_H_ diff --git a/src/browser/shell_component_extension_resource_manager.cc b/src/browser/shell_component_extension_resource_manager.cc new file mode 100644 index 0000000000..e700fbc529 --- /dev/null +++ b/src/browser/shell_component_extension_resource_manager.cc @@ -0,0 +1,61 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/browser/shell_component_extension_resource_manager.h" + +#include "base/logging.h" +#include "base/path_service.h" +#include "grit/nw_component_resources_map.h" + +namespace extensions { + +ShellComponentExtensionResourceManager:: +ShellComponentExtensionResourceManager() { + + AddComponentResourceEntries( + kNwComponentResources, kNwComponentResourcesSize); +} + +ShellComponentExtensionResourceManager:: +~ShellComponentExtensionResourceManager() {} + +bool ShellComponentExtensionResourceManager::IsComponentExtensionResource( + const base::FilePath& extension_path, + const base::FilePath& resource_path, + int* resource_id) const { + base::FilePath directory_path = extension_path; + base::FilePath resources_dir; + base::FilePath relative_path; +#if 0 + if (!PathService::Get(chrome::DIR_RESOURCES, &resources_dir) || + !resources_dir.AppendRelativePath(directory_path, &relative_path)) { + return false; + } +#endif + relative_path = relative_path.Append(resource_path); + relative_path = relative_path.NormalizePathSeparators(); + + std::map::const_iterator entry = + path_to_resource_id_.find(relative_path); + if (entry != path_to_resource_id_.end()) + *resource_id = entry->second; + + return entry != path_to_resource_id_.end(); +} + +void ShellComponentExtensionResourceManager::AddComponentResourceEntries( + const GritResourceMap* entries, + size_t size) { + for (size_t i = 0; i < size; ++i) { + base::FilePath resource_path = base::FilePath().AppendASCII( + entries[i].name); + resource_path = resource_path.NormalizePathSeparators(); + + DCHECK(path_to_resource_id_.find(resource_path) == + path_to_resource_id_.end()); + path_to_resource_id_[resource_path] = entries[i].value; + } +} + +} // namespace extensions diff --git a/src/browser/shell_component_extension_resource_manager.h b/src/browser/shell_component_extension_resource_manager.h new file mode 100644 index 0000000000..2b3614b578 --- /dev/null +++ b/src/browser/shell_component_extension_resource_manager.h @@ -0,0 +1,41 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NW_BROWSER_EXTENSIONS_CHROME_COMPONENT_EXTENSION_RESOURCE_MANAGER_H_ +#define NW_BROWSER_EXTENSIONS_CHROME_COMPONENT_EXTENSION_RESOURCE_MANAGER_H_ + +#include + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "extensions/browser/component_extension_resource_manager.h" + +struct GritResourceMap; + +namespace extensions { + +class ShellComponentExtensionResourceManager + : public ComponentExtensionResourceManager { + public: + ShellComponentExtensionResourceManager(); + ~ShellComponentExtensionResourceManager() override; + + // Overridden from ComponentExtensionResourceManager: + bool IsComponentExtensionResource(const base::FilePath& extension_path, + const base::FilePath& resource_path, + int* resource_id) const override; + + private: + void AddComponentResourceEntries(const GritResourceMap* entries, size_t size); + + // A map from a resource path to the resource ID. Used by + // IsComponentExtensionResource. + std::map path_to_resource_id_; + + DISALLOW_COPY_AND_ASSIGN(ShellComponentExtensionResourceManager); +}; + +} // namespace extensions + +#endif // NW_BROWSER_EXTENSIONS_CHROME_COMPONENT_EXTENSION_RESOURCE_MANAGER_H_ diff --git a/src/browser/shell_content_utility_client.cc b/src/browser/shell_content_utility_client.cc new file mode 100644 index 0000000000..c9c158670f --- /dev/null +++ b/src/browser/shell_content_utility_client.cc @@ -0,0 +1,99 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/browser/shell_content_utility_client.h" + +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/time/time.h" +#include "chrome/common/chrome_utility_messages.h" +#include "chrome/utility/chrome_content_utility_ipc_whitelist.h" +#include "chrome/utility/utility_message_handler.h" +//#include "chrome/utility/web_resource_unpacker.h" +#include "content/public/child/image_decoder_utils.h" +#include "content/public/common/content_switches.h" +#include "content/public/utility/utility_thread.h" +#include "courgette/courgette.h" +#include "courgette/third_party/bsdiff.h" +#include "ipc/ipc_channel.h" +#include "skia/ext/image_operations.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/zlib/google/zip.h" +#include "ui/gfx/codec/jpeg_codec.h" +#include "ui/gfx/geometry/size.h" + +#include "base/debug/debugger.h" + +#if defined(ENABLE_PRINT_PREVIEW) || defined(OS_WIN) +#include "printing_handler.h" +#endif + +namespace { + +bool Send(IPC::Message* message) { + return content::UtilityThread::Get()->Send(message); +} + +#if 0 +void ReleaseProcessIfNeeded() { + content::UtilityThread::Get()->ReleaseProcessIfNeeded(); +} +#endif + +} // namespace + +int64_t ShellContentUtilityClient::max_ipc_message_size_ = + IPC::Channel::kMaximumMessageSize; + +ShellContentUtilityClient::ShellContentUtilityClient() + : filter_messages_(false) { + +#if defined(ENABLE_PRINT_PREVIEW) || defined(OS_WIN) + handlers_.push_back(new PrintingHandler()); +#endif + +} + +ShellContentUtilityClient::~ShellContentUtilityClient() { +} + +void ShellContentUtilityClient::UtilityThreadStarted() { + +} + +bool ShellContentUtilityClient::OnMessageReceived( + const IPC::Message& message) { + if (filter_messages_ && !ContainsKey(message_id_whitelist_, message.type())) + return false; + + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(ShellContentUtilityClient, message) + IPC_MESSAGE_HANDLER(ChromeUtilityMsg_StartupPing, OnStartupPing) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + for (Handlers::iterator it = handlers_.begin(); + !handled && it != handlers_.end(); ++it) { + handled = (*it)->OnMessageReceived(message); + } + + return handled; +} + +// static +void ShellContentUtilityClient::PreSandboxStartup() { + //base::debug::WaitForDebugger(120, false); +#if defined(ENABLE_PRINT_PREVIEW) || defined(OS_WIN) + PrintingHandler::PreSandboxStartup(); +#endif + +} + +void ShellContentUtilityClient::OnStartupPing() { + Send(new ChromeUtilityHostMsg_ProcessStarted); + // Don't release the process, we assume further messages are on the way. +} + + diff --git a/src/browser/shell_content_utility_client.h b/src/browser/shell_content_utility_client.h new file mode 100644 index 0000000000..3f18659888 --- /dev/null +++ b/src/browser/shell_content_utility_client.h @@ -0,0 +1,55 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NW_UTILITY_CHROME_CONTENT_UTILITY_CLIENT_H_ +#define NW_UTILITY_CHROME_CONTENT_UTILITY_CLIENT_H_ + +#include +#include +#include + +#include "base/compiler_specific.h" +#include "base/memory/scoped_vector.h" +#include "content/public/utility/content_utility_client.h" +#include "ipc/ipc_platform_file.h" + +namespace base { +class FilePath; +struct FileDescriptor; +} + +class UtilityMessageHandler; + +class ShellContentUtilityClient : public content::ContentUtilityClient { + public: + ShellContentUtilityClient(); + virtual ~ShellContentUtilityClient(); + + void UtilityThreadStarted() override; + bool OnMessageReceived(const IPC::Message& message) override; + + static void PreSandboxStartup(); + + static void set_max_ipc_message_size_for_test(int64_t max_message_size) { + max_ipc_message_size_ = max_message_size; + } + + private: + // IPC message handlers. + void OnStartupPing(); + + typedef ScopedVector Handlers; + Handlers handlers_; + + // Flag to enable whitelisting. + bool filter_messages_; + // A list of message_ids to filter. + std::set message_id_whitelist_; + // Maximum IPC msg size (default to kMaximumMessageSize; override for testing) + static int64_t max_ipc_message_size_; + + DISALLOW_COPY_AND_ASSIGN(ShellContentUtilityClient); +}; + +#endif // CHROME_UTILITY_CHROME_CONTENT_UTILITY_CLIENT_H_ diff --git a/src/browser/shell_devtools_delegate.cc b/src/browser/shell_devtools_delegate.cc index bf58db0f0a..4f08cc67b6 100644 --- a/src/browser/shell_devtools_delegate.cc +++ b/src/browser/shell_devtools_delegate.cc @@ -40,7 +40,7 @@ ShellDevToolsDelegate::ShellDevToolsDelegate(BrowserContext* browser_context, devtools_http_handler_ = DevToolsHttpHandler::Start( new net::TCPListenSocketFactory("127.0.0.1", port), "", - this); + this, base::FilePath()); } ShellDevToolsDelegate::~ShellDevToolsDelegate() { @@ -82,11 +82,12 @@ class Target : public content::DevToolsTarget { explicit Target(WebContents* web_contents); virtual std::string GetId() const OVERRIDE { return id_; } + virtual std::string GetParentId() const OVERRIDE { return std::string(); } virtual std::string GetType() const OVERRIDE { return kTargetTypePage; } virtual std::string GetTitle() const OVERRIDE { return title_; } virtual std::string GetDescription() const OVERRIDE { return description_; } - virtual GURL GetUrl() const OVERRIDE { return url_; } - virtual GURL GetFaviconUrl() const OVERRIDE { return GURL(); } + virtual GURL GetURL() const OVERRIDE { return url_; } + virtual GURL GetFaviconURL() const OVERRIDE { return GURL(); } virtual base::TimeTicks GetLastActivityTime() const OVERRIDE { return last_activity_time_; } @@ -110,7 +111,7 @@ class Target : public content::DevToolsTarget { Target::Target(WebContents* web_contents) { agent_host_ = - DevToolsAgentHost::GetOrCreateFor(web_contents->GetRenderViewHost()); + DevToolsAgentHost::GetOrCreateFor(web_contents); id_ = agent_host_->GetId(); title_ = base::UTF16ToUTF8(web_contents->GetTitle()); url_ = web_contents->GetURL(); @@ -118,10 +119,7 @@ Target::Target(WebContents* web_contents) { } bool Target::Activate() const { - RenderViewHost* rvh = agent_host_->GetRenderViewHost(); - if (!rvh) - return false; - WebContents* web_contents = WebContents::FromRenderViewHost(rvh); + WebContents* web_contents = agent_host_->GetWebContents(); if (!web_contents) return false; web_contents->GetDelegate()->ActivateContents(web_contents); @@ -129,22 +127,21 @@ bool Target::Activate() const { } bool Target::Close() const { - RenderViewHost* rvh = agent_host_->GetRenderViewHost(); - if (!rvh) + WebContents* web_contents = agent_host_->GetWebContents(); + if (!web_contents) return false; - rvh->ClosePage(); + web_contents->GetRenderViewHost()->ClosePage(); return true; } void ShellDevToolsDelegate::EnumerateTargets(TargetCallback callback) { TargetList targets; - std::vector rvh_list = - content::DevToolsAgentHost::GetValidRenderViewHosts(); - for (std::vector::iterator it = rvh_list.begin(); - it != rvh_list.end(); ++it) { - WebContents* web_contents = WebContents::FromRenderViewHost(*it); - if (web_contents) - targets.push_back(new Target(web_contents)); + std::vector wc_list = + content::DevToolsAgentHost::GetInspectableWebContents(); + for (std::vector::iterator it = wc_list.begin(); + it != wc_list.end(); + ++it) { + targets.push_back(new Target(*it)); } callback.Run(targets); } diff --git a/src/browser/shell_devtools_delegate.h b/src/browser/shell_devtools_delegate.h index 111e28e55d..cdde5b7f7f 100644 --- a/src/browser/shell_devtools_delegate.h +++ b/src/browser/shell_devtools_delegate.h @@ -48,15 +48,15 @@ class ShellDevToolsDelegate : public DevToolsHttpHandlerDelegate { void Stop(); // DevToolsHttpProtocolHandler::Delegate overrides. - virtual std::string GetDiscoveryPageHTML() OVERRIDE; - virtual bool BundlesFrontendResources() OVERRIDE; - virtual base::FilePath GetDebugFrontendDir() OVERRIDE; - virtual std::string GetPageThumbnailData(const GURL& url) OVERRIDE; - virtual scoped_ptr CreateNewTarget(const GURL& url) OVERRIDE; - virtual void EnumerateTargets(TargetCallback callback) OVERRIDE; + virtual std::string GetDiscoveryPageHTML() override; + virtual bool BundlesFrontendResources() override; + virtual base::FilePath GetDebugFrontendDir() override; + virtual std::string GetPageThumbnailData(const GURL& url) override; + virtual scoped_ptr CreateNewTarget(const GURL& url) override; + virtual void EnumerateTargets(TargetCallback callback) override; virtual scoped_ptr CreateSocketForTethering( net::StreamListenSocket::Delegate* delegate, - std::string* name) OVERRIDE; + std::string* name) override; DevToolsHttpHandler* devtools_http_handler() { return devtools_http_handler_; diff --git a/src/browser/shell_devtools_manager_delegate.cc b/src/browser/shell_devtools_manager_delegate.cc new file mode 100644 index 0000000000..967bd0e410 --- /dev/null +++ b/src/browser/shell_devtools_manager_delegate.cc @@ -0,0 +1,282 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/shell/browser/shell_devtools_manager_delegate.h" + +#include + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/browser/devtools_agent_host.h" +#include "content/public/browser/devtools_http_handler.h" +#include "content/public/browser/devtools_target.h" +#include "content/public/browser/favicon_status.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/content_switches.h" +#include "content/public/common/url_constants.h" +#include "content/public/common/user_agent.h" +#include "content/nw/src/nw_shell.h" +#include "grit/nw_resources.h" +#include "net/socket/tcp_server_socket.h" +#include "ui/base/resource/resource_bundle.h" + +#if defined(OS_ANDROID) +#include "content/public/browser/android/devtools_auth.h" +#include "net/socket/unix_domain_server_socket_posix.h" +#endif + +using base::CommandLine; + +namespace content { + +namespace { + +#if defined(OS_ANDROID) +const char kFrontEndURL[] = + "http://chrome-devtools-frontend.appspot.com/serve_rev/%s/inspector.html"; +#endif +const char kTargetTypePage[] = "page"; +const char kTargetTypeServiceWorker[] = "service_worker"; +const char kTargetTypeOther[] = "other"; + +#if defined(OS_ANDROID) +class UnixDomainServerSocketFactory + : public DevToolsHttpHandler::ServerSocketFactory { + public: + explicit UnixDomainServerSocketFactory(const std::string& socket_name) + : DevToolsHttpHandler::ServerSocketFactory(socket_name, 0, 1) {} + + private: + // DevToolsHttpHandler::ServerSocketFactory. + virtual scoped_ptr Create() const override { + return scoped_ptr( + new net::UnixDomainServerSocket( + base::Bind(&CanUserConnectToDevTools), + true /* use_abstract_namespace */)); + } + + DISALLOW_COPY_AND_ASSIGN(UnixDomainServerSocketFactory); +}; +#else +class TCPServerSocketFactory + : public DevToolsHttpHandler::ServerSocketFactory { + public: + TCPServerSocketFactory(const std::string& address, uint16 port, int backlog) + : DevToolsHttpHandler::ServerSocketFactory( + address, port, backlog) {} + + private: + // DevToolsHttpHandler::ServerSocketFactory. + scoped_ptr Create() const override { + return scoped_ptr( + new net::TCPServerSocket(NULL, net::NetLog::Source())); + } + + DISALLOW_COPY_AND_ASSIGN(TCPServerSocketFactory); +}; +#endif + +scoped_ptr +CreateSocketFactory() { + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); +#if defined(OS_ANDROID) + std::string socket_name = "content_shell_devtools_remote"; + if (command_line.HasSwitch(switches::kRemoteDebuggingSocketName)) { + socket_name = command_line.GetSwitchValueASCII( + switches::kRemoteDebuggingSocketName); + } + return scoped_ptr( + new UnixDomainServerSocketFactory(socket_name)); +#else + // See if the user specified a port on the command line (useful for + // automation). If not, use an ephemeral port by specifying 0. + uint16 port = 0; + if (command_line.HasSwitch(switches::kRemoteDebuggingPort)) { + int temp_port; + std::string port_str = + command_line.GetSwitchValueASCII(switches::kRemoteDebuggingPort); + if (base::StringToInt(port_str, &temp_port) && + temp_port > 0 && temp_port < 65535) { + port = static_cast(temp_port); + } else { + DLOG(WARNING) << "Invalid http debugger port number " << temp_port; + } + } + return scoped_ptr( + new TCPServerSocketFactory("127.0.0.1", port, 1)); +#endif +} + +class Target : public DevToolsTarget { + public: + explicit Target(scoped_refptr agent_host); + + std::string GetId() const override { return agent_host_->GetId(); } + std::string GetParentId() const override { return std::string(); } + std::string GetType() const override { + switch (agent_host_->GetType()) { + case DevToolsAgentHost::TYPE_WEB_CONTENTS: + return kTargetTypePage; + case DevToolsAgentHost::TYPE_SERVICE_WORKER: + return kTargetTypeServiceWorker; + default: + break; + } + return kTargetTypeOther; + } + std::string GetTitle() const override { return agent_host_->GetTitle(); } + std::string GetDescription() const override { return std::string(); } + GURL GetURL() const override { return agent_host_->GetURL(); } + GURL GetFaviconURL() const override { return favicon_url_; } + base::TimeTicks GetLastActivityTime() const override { + return last_activity_time_; + } + bool IsAttached() const override { return agent_host_->IsAttached(); } + scoped_refptr GetAgentHost() const override { + return agent_host_; + } + bool Activate() const override; + bool Close() const override; + + private: + scoped_refptr agent_host_; + GURL favicon_url_; + base::TimeTicks last_activity_time_; +}; + +Target::Target(scoped_refptr agent_host) + : agent_host_(agent_host) { + if (WebContents* web_contents = agent_host_->GetWebContents()) { + NavigationController& controller = web_contents->GetController(); + NavigationEntry* entry = controller.GetActiveEntry(); + if (entry != NULL && entry->GetURL().is_valid()) + favicon_url_ = entry->GetFavicon().url; + last_activity_time_ = web_contents->GetLastActiveTime(); + } +} + +bool Target::Activate() const { + return agent_host_->Activate(); +} + +bool Target::Close() const { + return agent_host_->Close(); +} + +// ShellDevToolsDelegate ---------------------------------------------------- + +class ShellDevToolsDelegate : public DevToolsHttpHandlerDelegate { + public: + explicit ShellDevToolsDelegate(BrowserContext* browser_context); + ~ShellDevToolsDelegate() override; + + // DevToolsHttpHandlerDelegate implementation. + std::string GetDiscoveryPageHTML() override; + bool BundlesFrontendResources() override; + base::FilePath GetDebugFrontendDir() override; + scoped_ptr CreateSocketForTethering( + std::string* name) override; + + private: + BrowserContext* browser_context_; + + DISALLOW_COPY_AND_ASSIGN(ShellDevToolsDelegate); +}; + +ShellDevToolsDelegate::ShellDevToolsDelegate(BrowserContext* browser_context) + : browser_context_(browser_context) { +} + +ShellDevToolsDelegate::~ShellDevToolsDelegate() { +} + +std::string ShellDevToolsDelegate::GetDiscoveryPageHTML() { +#if defined(OS_ANDROID) + return std::string(); +#else + return ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_NW_DEVTOOLS_DISCOVERY_PAGE).as_string(); +#endif +} + +bool ShellDevToolsDelegate::BundlesFrontendResources() { +#if defined(OS_ANDROID) + return false; +#else + return true; +#endif +} + +base::FilePath ShellDevToolsDelegate::GetDebugFrontendDir() { + return base::FilePath(); +} + +scoped_ptr +ShellDevToolsDelegate::CreateSocketForTethering(std::string* name) { + return scoped_ptr(); +} + +} // namespace + +// ShellDevToolsManagerDelegate ---------------------------------------------- + +// static +DevToolsHttpHandler* +ShellDevToolsManagerDelegate::CreateHttpHandler( + BrowserContext* browser_context) { + std::string frontend_url; +#if defined(OS_ANDROID) + frontend_url = base::StringPrintf(kFrontEndURL, GetWebKitRevision().c_str()); +#endif + return DevToolsHttpHandler::Start(CreateSocketFactory(), + frontend_url, + new ShellDevToolsDelegate(browser_context), + base::FilePath()); +} + +ShellDevToolsManagerDelegate::ShellDevToolsManagerDelegate( + BrowserContext* browser_context) + : browser_context_(browser_context) { +} + +ShellDevToolsManagerDelegate::~ShellDevToolsManagerDelegate() { +} + +base::DictionaryValue* ShellDevToolsManagerDelegate::HandleCommand( + DevToolsAgentHost* agent_host, + base::DictionaryValue* command) { + return NULL; +} + +std::string ShellDevToolsManagerDelegate::GetPageThumbnailData( + const GURL& url) { + return std::string(); +} + +scoped_ptr +ShellDevToolsManagerDelegate::CreateNewTarget(const GURL& url) { + Shell* shell = Shell::Create(browser_context_, + url, + NULL, + MSG_ROUTING_NONE, + NULL); + return scoped_ptr( + new Target(DevToolsAgentHost::GetOrCreateFor(shell->web_contents()))); +} + +void ShellDevToolsManagerDelegate::EnumerateTargets(TargetCallback callback) { + TargetList targets; + for (const auto& agent_host : DevToolsAgentHost::GetOrCreateAll()) { + targets.push_back(new Target(agent_host)); + } + callback.Run(targets); +} + +} // namespace content diff --git a/src/browser/shell_devtools_manager_delegate.h b/src/browser/shell_devtools_manager_delegate.h new file mode 100644 index 0000000000..061562d9de --- /dev/null +++ b/src/browser/shell_devtools_manager_delegate.h @@ -0,0 +1,45 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_SHELL_BROWSER_SHELL_DEVTOOLS_MANAGER_DELEGATE_H_ +#define CONTENT_SHELL_BROWSER_SHELL_DEVTOOLS_MANAGER_DELEGATE_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "content/public/browser/devtools_http_handler_delegate.h" +#include "content/public/browser/devtools_manager_delegate.h" + +namespace content { + +class BrowserContext; +class DevToolsHttpHandler; + +class ShellDevToolsManagerDelegate : public DevToolsManagerDelegate { + public: + static DevToolsHttpHandler* CreateHttpHandler( + BrowserContext* browser_context); + + explicit ShellDevToolsManagerDelegate(BrowserContext* browser_context); + ~ShellDevToolsManagerDelegate() override; + + // DevToolsManagerDelegate implementation. + void Inspect(BrowserContext* browser_context, + DevToolsAgentHost* agent_host) override {} + void DevToolsAgentStateChanged(DevToolsAgentHost* agent_host, + bool attached) override {} + base::DictionaryValue* HandleCommand(DevToolsAgentHost* agent_host, + base::DictionaryValue* command) override; + scoped_ptr CreateNewTarget(const GURL& url) override; + void EnumerateTargets(TargetCallback callback) override; + std::string GetPageThumbnailData(const GURL& url) override; + + private: + BrowserContext* browser_context_; + + DISALLOW_COPY_AND_ASSIGN(ShellDevToolsManagerDelegate); +}; + +} // namespace content + +#endif // CONTENT_SHELL_BROWSER_SHELL_DEVTOOLS_MANAGER_DELEGATE_H_ diff --git a/src/browser/shell_display_info_provider.cc b/src/browser/shell_display_info_provider.cc new file mode 100644 index 0000000000..a86173fa93 --- /dev/null +++ b/src/browser/shell_display_info_provider.cc @@ -0,0 +1,38 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/shell/browser/shell_display_info_provider.h" + +namespace extensions { + +ShellDisplayInfoProvider::ShellDisplayInfoProvider() { +} + +ShellDisplayInfoProvider::~ShellDisplayInfoProvider() { +} + +bool ShellDisplayInfoProvider::SetInfo( + const std::string& display_id, + const core_api::system_display::DisplayProperties& info, + std::string* error) { + *error = "Not implemented"; + return false; +} + +void ShellDisplayInfoProvider::UpdateDisplayUnitInfoForPlatform( + const gfx::Display& display, + extensions::core_api::system_display::DisplayUnitInfo* unit) { + NOTIMPLEMENTED(); +} + +gfx::Screen* ShellDisplayInfoProvider::GetActiveScreen() { + return NULL; +} + +// static +DisplayInfoProvider* DisplayInfoProvider::Create() { + return new ShellDisplayInfoProvider(); +} + +} // namespace extensions diff --git a/src/browser/shell_display_info_provider.h b/src/browser/shell_display_info_provider.h new file mode 100644 index 0000000000..61cea1cc7f --- /dev/null +++ b/src/browser/shell_display_info_provider.h @@ -0,0 +1,32 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_SHELL_BROWSER_SHELL_DISPLAY_INFO_PROVIDER_H_ +#define EXTENSIONS_SHELL_BROWSER_SHELL_DISPLAY_INFO_PROVIDER_H_ + +#include "extensions/browser/api/system_display/display_info_provider.h" + +namespace extensions { + +class ShellDisplayInfoProvider : public DisplayInfoProvider { + public: + ShellDisplayInfoProvider(); + ~ShellDisplayInfoProvider() override; + + // DisplayInfoProvider implementation. + bool SetInfo(const std::string& display_id, + const core_api::system_display::DisplayProperties& info, + std::string* error) override; + void UpdateDisplayUnitInfoForPlatform( + const gfx::Display& display, + extensions::core_api::system_display::DisplayUnitInfo* unit) override; + gfx::Screen* GetActiveScreen() override; + + private: + DISALLOW_COPY_AND_ASSIGN(ShellDisplayInfoProvider); +}; + +} // namespace extensions + +#endif // EXTENSIONS_SHELL_BROWSER_SHELL_DISPLAY_INFO_PROVIDER_H_ diff --git a/src/browser/shell_download_manager_delegate.cc b/src/browser/shell_download_manager_delegate.cc index b40fd7924f..f5dd1b73d0 100644 --- a/src/browser/shell_download_manager_delegate.cc +++ b/src/browser/shell_download_manager_delegate.cc @@ -30,7 +30,7 @@ #endif #include "base/bind.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -38,7 +38,7 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/download_manager.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_view.h" +#include "net/base/filename_util.h" #include "net/base/net_util.h" using base::FilePath; @@ -102,6 +102,12 @@ bool ShellDownloadManagerDelegate::DetermineDownloadTarget( return true; } +bool ShellDownloadManagerDelegate::ShouldOpenDownload( + DownloadItem* item, + const DownloadOpenDelayedCallback& callback) { + return true; +} + void ShellDownloadManagerDelegate::GenerateFilename( int32 download_id, const DownloadTargetCallback& callback, @@ -142,4 +148,10 @@ void ShellDownloadManagerDelegate::SetDownloadBehaviorForTesting( suppress_prompting_ = true; } +void ShellDownloadManagerDelegate::GetNextId( + const DownloadIdCallback& callback) { + static uint32 next_id = DownloadItem::kInvalidId + 1; + callback.Run(next_id++); +} + } // namespace content diff --git a/src/browser/shell_download_manager_delegate.h b/src/browser/shell_download_manager_delegate.h index f9649e0e0f..c1b98c1886 100644 --- a/src/browser/shell_download_manager_delegate.h +++ b/src/browser/shell_download_manager_delegate.h @@ -25,34 +25,52 @@ #include "base/memory/ref_counted.h" #include "content/public/browser/download_manager_delegate.h" +#if defined(OS_WIN) +#include "ui/shell_dialogs/select_file_dialog.h" +#endif + namespace content { class DownloadManager; class ShellDownloadManagerDelegate : public DownloadManagerDelegate, +#if defined(OS_WIN) + public ui::SelectFileDialog::Listener, +#endif public base::RefCountedThreadSafe { public: ShellDownloadManagerDelegate(); void SetDownloadManager(DownloadManager* manager); - virtual void Shutdown() OVERRIDE; - virtual bool DetermineDownloadTarget( + void Shutdown() override; + bool DetermineDownloadTarget( DownloadItem* download, - const DownloadTargetCallback& callback) OVERRIDE; + const DownloadTargetCallback& callback) override; + bool ShouldOpenDownload( + DownloadItem* item, + const DownloadOpenDelayedCallback& callback) override; + void GetNextId(const DownloadIdCallback& callback) override; // Inhibits prompting and sets the default download path. void SetDownloadBehaviorForTesting( const base::FilePath& default_download_path); +#if defined(OS_WIN) + virtual void FileSelected( + const base::FilePath& path, int index, void* params) override; + virtual void FileSelectionCanceled(void* params) override; +#endif protected: // To allow subclasses for testing. - virtual ~ShellDownloadManagerDelegate(); + ~ShellDownloadManagerDelegate() final; private: friend class base::RefCountedThreadSafe; + typedef base::Callback + FilenameDeterminedCallback; void GenerateFilename(int32 download_id, const DownloadTargetCallback& callback, @@ -64,10 +82,15 @@ class ShellDownloadManagerDelegate void ChooseDownloadPath(int32 download_id, const DownloadTargetCallback& callback, const base::FilePath& suggested_path); + void OnFileSelected(const base::FilePath& path); DownloadManager* download_manager_; base::FilePath default_download_path_; bool suppress_prompting_; +#if defined(OS_WIN) + DownloadTargetCallback callback_; + scoped_refptr select_file_dialog_; +#endif DISALLOW_COPY_AND_ASSIGN(ShellDownloadManagerDelegate); }; diff --git a/src/browser/shell_download_manager_delegate_gtk.cc b/src/browser/shell_download_manager_delegate_gtk.cc index 0e8d4fe50e..b782e30546 100644 --- a/src/browser/shell_download_manager_delegate_gtk.cc +++ b/src/browser/shell_download_manager_delegate_gtk.cc @@ -20,12 +20,12 @@ #include "content/nw/src/browser/shell_download_manager_delegate.h" -#if defined(TOOLKIT_GTK) +#if defined(OS_LINUX) #include #endif #include "base/bind.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -33,7 +33,6 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/download_manager.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_view.h" #include "net/base/net_util.h" using base::FilePath; @@ -51,12 +50,10 @@ void ShellDownloadManagerDelegate::ChooseDownloadPath( FilePath result; GtkWidget *dialog; - gfx::NativeWindow parent_window; std::string base_name = FilePath(suggested_path).BaseName().value(); - parent_window = item->GetWebContents()->GetView()->GetTopLevelNativeWindow(); dialog = gtk_file_chooser_dialog_new("Save File", - parent_window, + NULL, GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, diff --git a/src/browser/shell_download_manager_delegate_mac.mm b/src/browser/shell_download_manager_delegate_mac.mm index 195b568fbe..18d21a258a 100644 --- a/src/browser/shell_download_manager_delegate_mac.mm +++ b/src/browser/shell_download_manager_delegate_mac.mm @@ -24,7 +24,7 @@ #include #include "base/bind.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/files/file_path.h" #include "base/logging.h" #include "base/strings/string_util.h" @@ -33,7 +33,6 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/download_manager.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_view.h" #include "net/base/net_util.h" using base::FilePath; diff --git a/src/browser/shell_download_manager_delegate_win.cc b/src/browser/shell_download_manager_delegate_win.cc index 168d4957a0..9d15e6e4fa 100644 --- a/src/browser/shell_download_manager_delegate_win.cc +++ b/src/browser/shell_download_manager_delegate_win.cc @@ -26,15 +26,15 @@ #endif #include "base/bind.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "chrome/browser/platform_util.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/download_manager.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_view.h" #include "net/base/net_util.h" #include "ui/aura/window.h" @@ -51,31 +51,47 @@ void ShellDownloadManagerDelegate::ChooseDownloadPath( if (!item || (item->GetState() != DownloadItem::IN_PROGRESS)) return; - base::FilePath result; + WebContents* web_contents = item->GetWebContents(); + select_file_dialog_ = ui::SelectFileDialog::Create(this, NULL); + ui::SelectFileDialog::FileTypeInfo file_type_info; + // Platform file pickers, notably on Mac and Windows, tend to break + // with double extensions like .tar.gz, so only pass in normal ones. + base::FilePath::StringType extension = suggested_path.FinalExtension(); + if (!extension.empty()) { + extension.erase(extension.begin()); // drop the . + file_type_info.extensions.resize(1); + file_type_info.extensions[0].push_back(extension); + } + file_type_info.include_all_files = true; + file_type_info.support_drive = true; + gfx::NativeWindow owning_window = web_contents ? + platform_util::GetTopLevel(web_contents->GetNativeView()) : NULL; - std::wstring file_part = base::FilePath(suggested_path).BaseName().value(); - wchar_t file_name[MAX_PATH]; - base::wcslcpy(file_name, file_part.c_str(), arraysize(file_name)); - OPENFILENAME save_as; - ZeroMemory(&save_as, sizeof(save_as)); - save_as.lStructSize = sizeof(OPENFILENAME); - save_as.hwndOwner = (HWND)item->GetWebContents()->GetView()->GetNativeView()-> - GetHost()->GetAcceleratedWidget(); - save_as.lpstrFile = file_name; - save_as.nMaxFile = arraysize(file_name); - - std::wstring directory; - if (!suggested_path.empty()) - directory = suggested_path.DirName().value(); + callback_ = callback; + base::FilePath working_path; + select_file_dialog_->SelectFile(ui::SelectFileDialog::SELECT_SAVEAS_FILE, + base::string16(), + suggested_path, + &file_type_info, + 0, + base::FilePath::StringType(), + owning_window, + NULL, working_path); +} - save_as.lpstrInitialDir = directory.c_str(); - save_as.Flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_ENABLESIZING | - OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST; +void ShellDownloadManagerDelegate::OnFileSelected(const base::FilePath& path) { + callback_.Run(path, DownloadItem::TARGET_DISPOSITION_PROMPT, + DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, path); +} - if (GetSaveFileName(&save_as)) - result = base::FilePath(std::wstring(save_as.lpstrFile)); +void ShellDownloadManagerDelegate::FileSelected(const base::FilePath& path, + int index, + void* params) { + OnFileSelected(path); +} - callback.Run(result, DownloadItem::TARGET_DISPOSITION_PROMPT, - DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, result); +void ShellDownloadManagerDelegate::FileSelectionCanceled(void* params) { + OnFileSelected(base::FilePath()); } + } // namespace content diff --git a/src/browser/shell_extension_host_delegate.cc b/src/browser/shell_extension_host_delegate.cc new file mode 100644 index 0000000000..c8df9cee7c --- /dev/null +++ b/src/browser/shell_extension_host_delegate.cc @@ -0,0 +1,67 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/browser/shell_extension_host_delegate.h" + +#include "base/logging.h" +#include "content/nw/src/api/dispatcher_host.h" +#include "extensions/browser/extension_host.h" +#include "extensions/shell/browser/media_capture_util.h" +#include "extensions/shell/browser/shell_extension_web_contents_observer.h" + +namespace extensions { + +ShellExtensionHostDelegate::ShellExtensionHostDelegate() { +} + +ShellExtensionHostDelegate::~ShellExtensionHostDelegate() { +} + +void ShellExtensionHostDelegate::OnExtensionHostCreated( + content::WebContents* web_contents) { + ShellExtensionWebContentsObserver::CreateForWebContents(web_contents); +} + +void ShellExtensionHostDelegate::OnRenderViewCreatedForBackgroundPage( + ExtensionHost* host) { + new nwapi::DispatcherHost(host->render_view_host()); +} + +content::JavaScriptDialogManager* +ShellExtensionHostDelegate::GetJavaScriptDialogManager() { + // TODO(jamescook): Create a JavaScriptDialogManager or reuse the one from + // content_shell. + NOTREACHED(); + return NULL; +} + +void ShellExtensionHostDelegate::CreateTab(content::WebContents* web_contents, + const std::string& extension_id, + WindowOpenDisposition disposition, + const gfx::Rect& initial_pos, + bool user_gesture) { + // TODO(jamescook): Should app_shell support opening popup windows? + NOTREACHED(); +} + +void ShellExtensionHostDelegate::ProcessMediaAccessRequest( + content::WebContents* web_contents, + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback, + const Extension* extension) { + // Allow access to the microphone and/or camera. + media_capture_util::GrantMediaStreamRequest( + web_contents, request, callback, extension); +} + +bool ShellExtensionHostDelegate::CheckMediaAccessPermission( + content::WebContents* web_contents, + const GURL& security_origin, + content::MediaStreamType type, + const Extension* extension) { + media_capture_util::VerifyMediaAccessPermission(type, extension); + return true; +} + +} // namespace extensions diff --git a/src/browser/shell_extension_host_delegate.h b/src/browser/shell_extension_host_delegate.h new file mode 100644 index 0000000000..4f7960163e --- /dev/null +++ b/src/browser/shell_extension_host_delegate.h @@ -0,0 +1,43 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSION_HOST_DELEGATE_H_ +#define EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSION_HOST_DELEGATE_H_ + +#include "base/macros.h" +#include "extensions/browser/extension_host_delegate.h" + +namespace extensions { + +// A minimal ExtensionHostDelegate. +class ShellExtensionHostDelegate : public ExtensionHostDelegate { + public: + ShellExtensionHostDelegate(); + ~ShellExtensionHostDelegate() override; + + // ExtensionHostDelegate implementation. + void OnExtensionHostCreated(content::WebContents* web_contents) override; + void OnRenderViewCreatedForBackgroundPage(ExtensionHost* host) override; + content::JavaScriptDialogManager* GetJavaScriptDialogManager() override; + void CreateTab(content::WebContents* web_contents, + const std::string& extension_id, + WindowOpenDisposition disposition, + const gfx::Rect& initial_pos, + bool user_gesture) override; + void ProcessMediaAccessRequest(content::WebContents* web_contents, + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback, + const Extension* extension) override; + bool CheckMediaAccessPermission(content::WebContents* web_contents, + const GURL& security_origin, + content::MediaStreamType type, + const Extension* extension) override; + + private: + DISALLOW_COPY_AND_ASSIGN(ShellExtensionHostDelegate); +}; + +} // namespace extensions + +#endif // EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSION_HOST_DELEGATE_H_ diff --git a/src/browser/shell_extension_system.cc b/src/browser/shell_extension_system.cc new file mode 100644 index 0000000000..99c428fb9a --- /dev/null +++ b/src/browser/shell_extension_system.cc @@ -0,0 +1,254 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/browser/shell_extension_system.h" + +#include + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/path_service.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/notification_details.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/notification_source.h" +#include "extensions/browser/api/app_runtime/app_runtime_api.h" +#include "extensions/browser/event_router.h" +#include "extensions/browser/extension_prefs.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/info_map.h" +#include "extensions/browser/lazy_background_task_queue.h" +#include "extensions/browser/notification_types.h" +#include "extensions/browser/quota_service.h" +#include "extensions/browser/runtime_data.h" +#include "extensions/common/constants.h" +#include "extensions/common/file_util.h" +#include "extensions/common/manifest_constants.h" + +#include "components/crx_file/id_util.h" + +#include "content/nw/src/nw_shell.h" +#include "content/nw/src/nw_package.h" + +using content::BrowserContext; +using content::BrowserThread; + +namespace extensions { + +ShellExtensionSystem::ShellExtensionSystem(BrowserContext* browser_context) + : browser_context_(browser_context) { +} + +ShellExtensionSystem::~ShellExtensionSystem() { +} + +const Extension* ShellExtensionSystem::LoadInternalApp() { + base::DictionaryValue manifest; + manifest.SetString("name", "node-webkit"); + manifest.SetString("version", "1"); + manifest.SetInteger("manifest_version", 2); + base::ListValue* list = new base::ListValue(); + list->Append(new base::StringValue("nw:*")); + list->Append(new base::StringValue("file://*")); + list->Append(new base::StringValue("app://*")); + + manifest.Set(extensions::manifest_keys::kWebURLs, list); + + scoped_ptr scripts(new base::ListValue); + scripts->AppendString("nwapp/background.js"); + nw::Package* package = content::Shell::GetPackage(); + std::string bg_script; + if (package->root()->GetString("bg-script", &bg_script)) + scripts->AppendString(bg_script); + + manifest.Set(extensions::manifest_keys::kPlatformAppBackgroundScripts, scripts.release()); + + base::ListValue* permission_list = new base::ListValue; + permission_list->Append(new base::StringValue("webview")); + permission_list->Append(new base::StringValue("webRequest")); + permission_list->Append(new base::StringValue("webRequestBlocking")); + permission_list->Append(new base::StringValue("")); + manifest.Set(extensions::manifest_keys::kPermissions, permission_list); + + std::string error; + base::FilePath path = package->path(); + //PathService::Get(base::FILE_EXE, &path); + scoped_refptr extension(Extension::Create( + path, Manifest::INTERNAL, manifest, Extension::NO_FLAGS, + // crx_file::id_util::GenerateId("io-blink"), + &error)); + + if (!extension.get()) { + LOG(ERROR) << "Loading internal extension " + << " failed with: " << error; + return nullptr; + } + + ExtensionRegistry::Get(browser_context_)->AddEnabled(extension.get()); + + RegisterExtensionWithRequestContexts(extension.get()); + + content::NotificationService::current()->Notify( + extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED, + content::Source(browser_context_), + content::Details(extension.get())); + + return extension.get(); +} + +const Extension* ShellExtensionSystem::LoadApp(const base::FilePath& app_dir) { + // app_shell only supports unpacked extensions. + // NOTE: If you add packed extension support consider removing the flag + // FOLLOW_SYMLINKS_ANYWHERE below. Packed extensions should not have symlinks. + CHECK(base::DirectoryExists(app_dir)) << app_dir.AsUTF8Unsafe(); + int load_flags = Extension::FOLLOW_SYMLINKS_ANYWHERE; + std::string load_error; + scoped_refptr extension = file_util::LoadExtension( + app_dir, Manifest::COMMAND_LINE, load_flags, &load_error); + if (!extension.get()) { + LOG(ERROR) << "Loading extension at " << app_dir.value() + << " failed with: " << load_error; + return nullptr; + } + + // TODO(jamescook): We may want to do some of these things here: + // * Create a PermissionsUpdater. + // * Call PermissionsUpdater::GrantActivePermissions(). + // * Call ExtensionService::SatisfyImports(). + // * Call ExtensionPrefs::OnExtensionInstalled(). + // * Send NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED. + + ExtensionRegistry::Get(browser_context_)->AddEnabled(extension.get()); + + RegisterExtensionWithRequestContexts(extension.get()); + + content::NotificationService::current()->Notify( + extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED, + content::Source(browser_context_), + content::Details(extension.get())); + + return extension.get(); +} + +void ShellExtensionSystem::Init() { + // Inform the rest of the extensions system to start. + ready_.Signal(); + content::NotificationService::current()->Notify( + extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, + content::Source(browser_context_), + content::NotificationService::NoDetails()); +} + +void ShellExtensionSystem::LaunchApp(const ExtensionId& extension_id) { + // Send the onLaunched event. + DCHECK(ExtensionRegistry::Get(browser_context_) + ->enabled_extensions() + .Contains(extension_id)); + const Extension* extension = ExtensionRegistry::Get(browser_context_) + ->enabled_extensions() + .GetByID(extension_id); + AppRuntimeEventRouter::DispatchOnLaunchedEvent( + browser_context_, extension, extensions::SOURCE_UNTRACKED); +} + +void ShellExtensionSystem::Shutdown() { +} + +void ShellExtensionSystem::InitForRegularProfile(bool extensions_enabled) { + runtime_data_.reset( + new RuntimeData(ExtensionRegistry::Get(browser_context_))); + lazy_background_task_queue_.reset( + new LazyBackgroundTaskQueue(browser_context_)); + event_router_.reset( + new EventRouter(browser_context_, ExtensionPrefs::Get(browser_context_))); + quota_service_.reset(new QuotaService); +} + +ExtensionService* ShellExtensionSystem::extension_service() { + return NULL; +} + +RuntimeData* ShellExtensionSystem::runtime_data() { + return runtime_data_.get(); +} + +ManagementPolicy* ShellExtensionSystem::management_policy() { + return NULL; +} + +SharedUserScriptMaster* ShellExtensionSystem::shared_user_script_master() { + return NULL; +} + +DeclarativeUserScriptManager* +ShellExtensionSystem::declarative_user_script_manager() { + return nullptr; +} + +StateStore* ShellExtensionSystem::state_store() { + return NULL; +} + +StateStore* ShellExtensionSystem::rules_store() { + return NULL; +} + +InfoMap* ShellExtensionSystem::info_map() { + if (!info_map_.get()) + info_map_ = new InfoMap; + return info_map_.get(); +} + +LazyBackgroundTaskQueue* ShellExtensionSystem::lazy_background_task_queue() { + return lazy_background_task_queue_.get(); +} + +EventRouter* ShellExtensionSystem::event_router() { + return event_router_.get(); +} + +ErrorConsole* ShellExtensionSystem::error_console() { + return NULL; +} + +InstallVerifier* ShellExtensionSystem::install_verifier() { + return NULL; +} + +QuotaService* ShellExtensionSystem::quota_service() { + return quota_service_.get(); +} + +void ShellExtensionSystem::RegisterExtensionWithRequestContexts( + const Extension* extension) { + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + base::Bind(&InfoMap::AddExtension, + info_map(), + make_scoped_refptr(extension), + base::Time::Now(), + false, + false)); +} + +void ShellExtensionSystem::UnregisterExtensionWithRequestContexts( + const std::string& extension_id, + const UnloadedExtensionInfo::Reason reason) { +} + +const OneShotEvent& ShellExtensionSystem::ready() const { + return ready_; +} + +ContentVerifier* ShellExtensionSystem::content_verifier() { + return NULL; +} + +scoped_ptr ShellExtensionSystem::GetDependentExtensions( + const Extension* extension) { + return make_scoped_ptr(new ExtensionSet()); +} + +} // namespace extensions diff --git a/src/browser/shell_extension_system.h b/src/browser/shell_extension_system.h new file mode 100644 index 0000000000..8395576975 --- /dev/null +++ b/src/browser/shell_extension_system.h @@ -0,0 +1,99 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSION_SYSTEM_H_ +#define EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSION_SYSTEM_H_ + +#include + +#include "base/compiler_specific.h" +#include "extensions/browser/extension_system.h" +#include "extensions/common/one_shot_event.h" + +class BrowserContextKeyedServiceFactory; + +namespace base { +class FilePath; +} + +namespace content { +class BrowserContext; +} + +namespace extensions { + +class DeclarativeUserScriptMaster; +class EventRouter; +class InfoMap; +class LazyBackgroundTaskQueue; +class ProcessManager; +class RendererStartupHelper; +class SharedUserScriptMaster; + +// A simplified version of ExtensionSystem for app_shell. Allows +// app_shell to skip initialization of services it doesn't need. +class ShellExtensionSystem : public ExtensionSystem { + public: + ShellExtensionSystem(content::BrowserContext* browser_context); + ~ShellExtensionSystem() override; + + // Loads an unpacked application from a directory. Returns the extension on + // success, or null otherwise. + const Extension* LoadApp(const base::FilePath& app_dir); + const Extension* LoadInternalApp(); + + // Initializes the extension system. + void Init(); + + // Launch the app with id |extension_id|. + void LaunchApp(const std::string& extension_id); + + // KeyedService implementation: + void Shutdown() override; + + // ExtensionSystem implementation: + void InitForRegularProfile(bool extensions_enabled) override; + ExtensionService* extension_service() override; + RuntimeData* runtime_data() override; + ManagementPolicy* management_policy() override; + SharedUserScriptMaster* shared_user_script_master() override; + DeclarativeUserScriptManager* declarative_user_script_manager() override; + StateStore* state_store() override; + StateStore* rules_store() override; + InfoMap* info_map() override; + LazyBackgroundTaskQueue* lazy_background_task_queue() override; + EventRouter* event_router() override; + ErrorConsole* error_console() override; + InstallVerifier* install_verifier() override; + QuotaService* quota_service() override; + void RegisterExtensionWithRequestContexts( + const Extension* extension) override; + void UnregisterExtensionWithRequestContexts( + const std::string& extension_id, + const UnloadedExtensionInfo::Reason reason) override; + const OneShotEvent& ready() const override; + ContentVerifier* content_verifier() override; + scoped_ptr GetDependentExtensions( + const Extension* extension) override; + + private: + content::BrowserContext* browser_context_; // Not owned. + + // Data to be accessed on the IO thread. Must outlive process_manager_. + scoped_refptr info_map_; + + scoped_ptr runtime_data_; + scoped_ptr lazy_background_task_queue_; + scoped_ptr event_router_; + scoped_ptr quota_service_; + + // Signaled when the extension system has completed its startup tasks. + OneShotEvent ready_; + + DISALLOW_COPY_AND_ASSIGN(ShellExtensionSystem); +}; + +} // namespace extensions + +#endif // EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSION_SYSTEM_H_ diff --git a/src/browser/shell_extension_system_factory.cc b/src/browser/shell_extension_system_factory.cc new file mode 100644 index 0000000000..68bdc377a1 --- /dev/null +++ b/src/browser/shell_extension_system_factory.cc @@ -0,0 +1,52 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/browser/shell_extension_system_factory.h" + +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "extensions/browser/extension_prefs_factory.h" +#include "extensions/browser/extension_registry_factory.h" +#include "content/nw/src/browser/shell_extension_system.h" + +using content::BrowserContext; + +namespace extensions { + +ExtensionSystem* ShellExtensionSystemFactory::GetForBrowserContext( + BrowserContext* context) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +ShellExtensionSystemFactory* ShellExtensionSystemFactory::GetInstance() { + return Singleton::get(); +} + +ShellExtensionSystemFactory::ShellExtensionSystemFactory() + : ExtensionSystemProvider("ShellExtensionSystem", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(ExtensionPrefsFactory::GetInstance()); + DependsOn(ExtensionRegistryFactory::GetInstance()); +} + +ShellExtensionSystemFactory::~ShellExtensionSystemFactory() { +} + +KeyedService* ShellExtensionSystemFactory::BuildServiceInstanceFor( + BrowserContext* context) const { + return new ShellExtensionSystem(context); +} + +BrowserContext* ShellExtensionSystemFactory::GetBrowserContextToUse( + BrowserContext* context) const { + // Use a separate instance for incognito. + return context; +} + +bool ShellExtensionSystemFactory::ServiceIsCreatedWithBrowserContext() const { + return true; +} + +} // namespace extensions diff --git a/src/browser/shell_extension_system_factory.h b/src/browser/shell_extension_system_factory.h new file mode 100644 index 0000000000..5478d81628 --- /dev/null +++ b/src/browser/shell_extension_system_factory.h @@ -0,0 +1,40 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSION_SYSTEM_FACTORY_H_ +#define EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSION_SYSTEM_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "extensions/browser/extension_system_provider.h" + +namespace extensions { + +// A factory that provides ShellExtensionSystem for app_shell. +class ShellExtensionSystemFactory : public ExtensionSystemProvider { + public: + // ExtensionSystemProvider implementation: + ExtensionSystem* GetForBrowserContext( + content::BrowserContext* context) override; + + static ShellExtensionSystemFactory* GetInstance(); + + private: + friend struct DefaultSingletonTraits; + + ShellExtensionSystemFactory(); + ~ShellExtensionSystemFactory() override; + + // BrowserContextKeyedServiceFactory implementation: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; + bool ServiceIsCreatedWithBrowserContext() const override; + + DISALLOW_COPY_AND_ASSIGN(ShellExtensionSystemFactory); +}; + +} // namespace extensions + +#endif // EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSION_SYSTEM_FACTORY_H_ diff --git a/src/browser/shell_extension_web_contents_observer.cc b/src/browser/shell_extension_web_contents_observer.cc new file mode 100644 index 0000000000..9dc369103d --- /dev/null +++ b/src/browser/shell_extension_web_contents_observer.cc @@ -0,0 +1,20 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/shell/browser/shell_extension_web_contents_observer.h" + +DEFINE_WEB_CONTENTS_USER_DATA_KEY( + extensions::ShellExtensionWebContentsObserver); + +namespace extensions { + +ShellExtensionWebContentsObserver::ShellExtensionWebContentsObserver( + content::WebContents* web_contents) + : ExtensionWebContentsObserver(web_contents) { +} + +ShellExtensionWebContentsObserver::~ShellExtensionWebContentsObserver() { +} + +} // namespace extensions diff --git a/src/browser/shell_extension_web_contents_observer.h b/src/browser/shell_extension_web_contents_observer.h new file mode 100644 index 0000000000..a2a187fdae --- /dev/null +++ b/src/browser/shell_extension_web_contents_observer.h @@ -0,0 +1,29 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSION_WEB_CONTENTS_OBSERVER_H_ +#define EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSION_WEB_CONTENTS_OBSERVER_H_ + +#include "content/public/browser/web_contents_user_data.h" +#include "extensions/browser/extension_web_contents_observer.h" + +namespace extensions { + +// The app_shell version of ExtensionWebContentsObserver. +class ShellExtensionWebContentsObserver + : public ExtensionWebContentsObserver, + public content::WebContentsUserData { + private: + friend class content::WebContentsUserData; + + explicit ShellExtensionWebContentsObserver( + content::WebContents* web_contents); + ~ShellExtensionWebContentsObserver() override; + + DISALLOW_COPY_AND_ASSIGN(ShellExtensionWebContentsObserver); +}; + +} // namespace extensions + +#endif // EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSION_WEB_CONTENTS_OBSERVER_H_ diff --git a/src/browser/shell_extensions_api_client.cc b/src/browser/shell_extensions_api_client.cc new file mode 100644 index 0000000000..8be9f9e3c1 --- /dev/null +++ b/src/browser/shell_extensions_api_client.cc @@ -0,0 +1,97 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/browser/shell_extensions_api_client.h" + +#include "base/files/file_path.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" +#include "content/nw/src/browser/shell_web_view_guest_delegate.h" +#include "extensions/browser/api/device_permissions_prompt.h" +#include "extensions/browser/api/virtual_keyboard_private/virtual_keyboard_delegate.h" +#include "extensions/browser/api/web_request/web_request_event_router_delegate.h" +#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest_delegate.h" +#include "extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h" +#include "extensions/browser/guest_view/web_view/web_view_guest.h" + +namespace extensions { + +ShellExtensionsAPIClient::ShellExtensionsAPIClient() {} + +ShellExtensionsAPIClient::~ShellExtensionsAPIClient() {} + +void ShellExtensionsAPIClient::AddAdditionalValueStoreCaches( + content::BrowserContext* context, + const scoped_refptr& factory, + const scoped_refptr >& observers, + std::map* caches) { +} + +AppViewGuestDelegate* ShellExtensionsAPIClient::CreateAppViewGuestDelegate() + const { + return NULL; +} + +void ShellWebViewGuestDelegate::OnShowContextMenu( + int request_id, + const MenuItemVector* items) { +} + +bool ShellWebViewGuestDelegate::HandleContextMenu( + const content::ContextMenuParams& params) { + return false; +} + +ExtensionOptionsGuestDelegate* +ShellExtensionsAPIClient::CreateExtensionOptionsGuestDelegate( + ExtensionOptionsGuest* guest) const { + return NULL; +} + +scoped_ptr +ShellExtensionsAPIClient::CreateMimeHandlerViewGuestDelegate( + MimeHandlerViewGuest* guest) const { + return scoped_ptr(); +} + +WebViewGuestDelegate* ShellExtensionsAPIClient::CreateWebViewGuestDelegate( + WebViewGuest* web_view_guest) const { + return new ShellWebViewGuestDelegate(web_view_guest); +} + +WebViewPermissionHelperDelegate* ShellExtensionsAPIClient:: + CreateWebViewPermissionHelperDelegate( + WebViewPermissionHelper* web_view_permission_helper) const { + return new WebViewPermissionHelperDelegate(web_view_permission_helper); +} + +WebRequestEventRouterDelegate* +ShellExtensionsAPIClient::CreateWebRequestEventRouterDelegate() const { + return new WebRequestEventRouterDelegate(); +} + +scoped_refptr +ShellExtensionsAPIClient::CreateContentRulesRegistry( + content::BrowserContext* browser_context, + RulesCacheDelegate* cache_delegate) const { + return scoped_refptr(); +} + +scoped_ptr +ShellExtensionsAPIClient::CreateDevicePermissionsPrompt( + content::WebContents* web_contents) const { + return nullptr; +} + +scoped_ptr +ShellExtensionsAPIClient::CreateVirtualKeyboardDelegate() const { + return nullptr; +} + +ManagementAPIDelegate* ShellExtensionsAPIClient::CreateManagementAPIDelegate() + const { + return nullptr; +} + +} // namespace extensions diff --git a/src/browser/shell_extensions_api_client.h b/src/browser/shell_extensions_api_client.h new file mode 100644 index 0000000000..d30432b7e3 --- /dev/null +++ b/src/browser/shell_extensions_api_client.h @@ -0,0 +1,52 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NW_BROWSER_EXTENSIONS_API_CHROME_EXTENSIONS_API_CLIENT_H_ +#define NW_BROWSER_EXTENSIONS_API_CHROME_EXTENSIONS_API_CLIENT_H_ + +#include "base/compiler_specific.h" +#include "extensions/browser/api/extensions_api_client.h" + +namespace extensions { + +// Extra support for extensions APIs in Chrome. +class ShellExtensionsAPIClient : public ExtensionsAPIClient { + public: + ShellExtensionsAPIClient(); + ~ShellExtensionsAPIClient() override; + + // ExtensionsApiClient implementation. + void AddAdditionalValueStoreCaches( + content::BrowserContext* context, + const scoped_refptr& factory, + const scoped_refptr>& observers, + std::map* caches) + override; + AppViewGuestDelegate* CreateAppViewGuestDelegate() const override; + ExtensionOptionsGuestDelegate* CreateExtensionOptionsGuestDelegate( + ExtensionOptionsGuest* guest) const override; + scoped_ptr CreateMimeHandlerViewGuestDelegate( + MimeHandlerViewGuest* guest) const override; + WebViewGuestDelegate* CreateWebViewGuestDelegate( + WebViewGuest* web_view_guest) const override; + WebViewPermissionHelperDelegate* CreateWebViewPermissionHelperDelegate( + WebViewPermissionHelper* web_view_permission_helper) const override; + WebRequestEventRouterDelegate* CreateWebRequestEventRouterDelegate() + const override; + scoped_refptr CreateContentRulesRegistry( + content::BrowserContext* browser_context, + RulesCacheDelegate* cache_delegate) const override; + scoped_ptr CreateDevicePermissionsPrompt( + content::WebContents* web_contents) const override; + scoped_ptr CreateVirtualKeyboardDelegate() + const override; + ManagementAPIDelegate* CreateManagementAPIDelegate() const override; + + private: + DISALLOW_COPY_AND_ASSIGN(ShellExtensionsAPIClient); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_CHROME_EXTENSIONS_API_CLIENT_H_ diff --git a/src/browser/shell_extensions_browser_client.cc b/src/browser/shell_extensions_browser_client.cc new file mode 100644 index 0000000000..034db57253 --- /dev/null +++ b/src/browser/shell_extensions_browser_client.cc @@ -0,0 +1,363 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/browser/shell_extensions_browser_client.h" + +#include "base/prefs/pref_service.h" +#include "base/prefs/pref_service_factory.h" +#include "base/prefs/testing_pref_store.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "components/pref_registry/pref_registry_syncable.h" +#include "components/user_prefs/user_prefs.h" +#include "content/public/browser/browser_thread.h" +#include "extensions/browser/api/extensions_api_client.h" +#include "extensions/browser/api/generated_api_registration.h" +#include "extensions/browser/app_sorting.h" +#include "extensions/browser/event_router.h" +#include "extensions/browser/extension_function_registry.h" +#include "extensions/browser/extension_prefs.h" +#include "extensions/browser/extension_protocols.h" +#include "extensions/browser/null_app_sorting.h" +#include "extensions/browser/updater/null_extension_cache.h" +#include "extensions/browser/url_request_util.h" +//#include "extensions/shell/browser/api/generated_api_registration.h" +#include "extensions/common/file_util.h" +#include "extensions/shell/browser/shell_extension_host_delegate.h" +#include "extensions/shell/browser/shell_extension_system_factory.h" +#include "extensions/shell/browser/shell_runtime_api_delegate.h" + +#include "content/nw/src/browser/shell_component_extension_resource_manager.h" +#include "content/nw/src/browser/shell_extensions_api_client.h" + +#include "net/base/mime_util.h" +#include "net/base/net_errors.h" +#include "net/http/http_request_headers.h" +#include "net/http/http_response_headers.h" +#include "net/http/http_response_info.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_simple_job.h" +#include "ui/base/resource/resource_bundle.h" + +using content::BrowserContext; +using content::BrowserThread; + +namespace { + +// A request for an extension resource in a Chrome .pak file. These are used +// by component extensions. +class URLRequestResourceBundleJob : public net::URLRequestSimpleJob { + public: + URLRequestResourceBundleJob(net::URLRequest* request, + net::NetworkDelegate* network_delegate, + const base::FilePath& filename, + int resource_id, + const std::string& content_security_policy, + bool send_cors_header) + : net::URLRequestSimpleJob(request, network_delegate), + filename_(filename), + resource_id_(resource_id), + weak_factory_(this) { + // Leave cache headers out of resource bundle requests. + response_info_.headers = extensions::BuildHttpHeaders( + content_security_policy, send_cors_header, base::Time()); + } + + // Overridden from URLRequestSimpleJob: + int GetRefCountedData( + std::string* mime_type, + std::string* charset, + scoped_refptr* data, + const net::CompletionCallback& callback) const override { + const ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + *data = rb.LoadDataResourceBytes(resource_id_); + + // Add the Content-Length header now that we know the resource length. + response_info_.headers->AddHeader( + base::StringPrintf("%s: %s", net::HttpRequestHeaders::kContentLength, + base::UintToString((*data)->size()).c_str())); + + std::string* read_mime_type = new std::string; + bool posted = base::PostTaskAndReplyWithResult( + BrowserThread::GetBlockingPool(), FROM_HERE, + base::Bind(&net::GetMimeTypeFromFile, filename_, + base::Unretained(read_mime_type)), + base::Bind(&URLRequestResourceBundleJob::OnMimeTypeRead, + weak_factory_.GetWeakPtr(), mime_type, charset, *data, + base::Owned(read_mime_type), callback)); + DCHECK(posted); + + return net::ERR_IO_PENDING; + } + + void GetResponseInfo(net::HttpResponseInfo* info) override { + *info = response_info_; + } + + private: + ~URLRequestResourceBundleJob() override {} + + void OnMimeTypeRead(std::string* out_mime_type, + std::string* charset, + scoped_refptr data, + std::string* read_mime_type, + const net::CompletionCallback& callback, + bool read_result) { + *out_mime_type = *read_mime_type; + if (StartsWithASCII(*read_mime_type, "text/", false)) { + // All of our HTML files should be UTF-8 and for other resource types + // (like images), charset doesn't matter. + DCHECK(base::IsStringUTF8(base::StringPiece( + reinterpret_cast(data->front()), data->size()))); + *charset = "utf-8"; + } + int result = read_result ? net::OK : net::ERR_INVALID_URL; + callback.Run(result); + } + + // We need the filename of the resource to determine the mime type. + base::FilePath filename_; + + // The resource bundle id to load. + int resource_id_; + + net::HttpResponseInfo response_info_; + + mutable base::WeakPtrFactory weak_factory_; +}; + +} // namespace + + +namespace extensions { +namespace { + +// See chrome::RegisterProfilePrefs() in chrome/browser/prefs/browser_prefs.cc +void RegisterPrefs(user_prefs::PrefRegistrySyncable* registry) { + ExtensionPrefs::RegisterProfilePrefs(registry); +} + +} // namespace + +ShellExtensionsBrowserClient::ShellExtensionsBrowserClient( + BrowserContext* context) + : browser_context_(context), + api_client_(new ShellExtensionsAPIClient), + extension_cache_(new NullExtensionCache()) { + // Set up the preferences service. + base::PrefServiceFactory factory; + factory.set_user_prefs(new TestingPrefStore); + factory.set_extension_prefs(new TestingPrefStore); + + // TODO(jamescook): Convert this to PrefRegistrySimple. + user_prefs::PrefRegistrySyncable* pref_registry = + new user_prefs::PrefRegistrySyncable; + // Prefs should be registered before the PrefService is created. + RegisterPrefs(pref_registry); + prefs_ = factory.Create(pref_registry).Pass(); + user_prefs::UserPrefs::Set(browser_context_, prefs_.get()); +} + +ShellExtensionsBrowserClient::~ShellExtensionsBrowserClient() { +} + +bool ShellExtensionsBrowserClient::IsShuttingDown() { + return false; +} + +bool ShellExtensionsBrowserClient::AreExtensionsDisabled( + const base::CommandLine& command_line, + BrowserContext* context) { + return false; +} + +bool ShellExtensionsBrowserClient::IsValidContext(BrowserContext* context) { + return context == browser_context_; +} + +bool ShellExtensionsBrowserClient::IsSameContext(BrowserContext* first, + BrowserContext* second) { + return first == second; +} + +bool ShellExtensionsBrowserClient::HasOffTheRecordContext( + BrowserContext* context) { + return false; +} + +BrowserContext* ShellExtensionsBrowserClient::GetOffTheRecordContext( + BrowserContext* context) { + // app_shell only supports a single context. + return NULL; +} + +BrowserContext* ShellExtensionsBrowserClient::GetOriginalContext( + BrowserContext* context) { + return context; +} + +bool ShellExtensionsBrowserClient::IsGuestSession( + BrowserContext* context) const { + return false; +} + +bool ShellExtensionsBrowserClient::IsExtensionIncognitoEnabled( + const std::string& extension_id, + content::BrowserContext* context) const { + return false; +} + +bool ShellExtensionsBrowserClient::CanExtensionCrossIncognito( + const Extension* extension, + content::BrowserContext* context) const { + return false; +} + +net::URLRequestJob* +ShellExtensionsBrowserClient::MaybeCreateResourceBundleRequestJob( + net::URLRequest* request, + net::NetworkDelegate* network_delegate, + const base::FilePath& directory_path, + const std::string& content_security_policy, + bool send_cors_header) { + base::FilePath relative_path; + base::FilePath request_path = + extensions::file_util::ExtensionURLToRelativeFilePath(request->url()); + int resource_id = 0; + if (ExtensionsBrowserClient::Get() + ->GetComponentExtensionResourceManager() + ->IsComponentExtensionResource( + directory_path, request_path, &resource_id)) { + relative_path = relative_path.Append(request_path); + relative_path = relative_path.NormalizePathSeparators(); + return new URLRequestResourceBundleJob(request, + network_delegate, + relative_path, + resource_id, + content_security_policy, + send_cors_header); + } + return NULL; +} + +bool ShellExtensionsBrowserClient::AllowCrossRendererResourceLoad( + net::URLRequest* request, + bool is_incognito, + const Extension* extension, + InfoMap* extension_info_map) { + bool allowed = false; + if (url_request_util::AllowCrossRendererResourceLoad( + request, is_incognito, extension, extension_info_map, &allowed)) { + return allowed; + } + + // Couldn't determine if resource is allowed. Block the load. + return false; +} + +PrefService* ShellExtensionsBrowserClient::GetPrefServiceForContext( + BrowserContext* context) { + return prefs_.get(); +} + +void ShellExtensionsBrowserClient::GetEarlyExtensionPrefsObservers( + content::BrowserContext* context, + std::vector* observers) const { +} + +ProcessManagerDelegate* +ShellExtensionsBrowserClient::GetProcessManagerDelegate() const { + return NULL; +} + +scoped_ptr +ShellExtensionsBrowserClient::CreateExtensionHostDelegate() { + return scoped_ptr(new ShellExtensionHostDelegate); +} + +bool ShellExtensionsBrowserClient::DidVersionUpdate(BrowserContext* context) { + // TODO(jamescook): We might want to tell extensions when app_shell updates. + return false; +} + +void ShellExtensionsBrowserClient::PermitExternalProtocolHandler() { +} + +scoped_ptr ShellExtensionsBrowserClient::CreateAppSorting() { + return scoped_ptr(new NullAppSorting); +} + +bool ShellExtensionsBrowserClient::IsRunningInForcedAppMode() { + return false; +} + +ApiActivityMonitor* ShellExtensionsBrowserClient::GetApiActivityMonitor( + BrowserContext* context) { + // app_shell doesn't monitor API function calls or events. + return NULL; +} + +ExtensionSystemProvider* +ShellExtensionsBrowserClient::GetExtensionSystemFactory() { + return ShellExtensionSystemFactory::GetInstance(); +} + +void ShellExtensionsBrowserClient::RegisterExtensionFunctions( + ExtensionFunctionRegistry* registry) const { + // Register core extension-system APIs. + core_api::GeneratedFunctionRegistry::RegisterAll(registry); + + //shell::api::GeneratedFunctionRegistry::RegisterAll(registry); +} + +scoped_ptr +ShellExtensionsBrowserClient::CreateRuntimeAPIDelegate( + content::BrowserContext* context) const { + return scoped_ptr(new ShellRuntimeAPIDelegate()); +} + +const ComponentExtensionResourceManager* +ShellExtensionsBrowserClient::GetComponentExtensionResourceManager() { + if (!resource_manager_) + resource_manager_.reset(new ShellComponentExtensionResourceManager()); + return resource_manager_.get(); +} + +void ShellExtensionsBrowserClient::BroadcastEventToRenderers( + const std::string& event_name, + scoped_ptr args) { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&ShellExtensionsBrowserClient::BroadcastEventToRenderers, + base::Unretained(this), + event_name, + base::Passed(&args))); + return; + } + + scoped_ptr event(new Event(event_name, args.Pass())); + EventRouter::Get(browser_context_)->BroadcastEvent(event.Pass()); +} + +net::NetLog* ShellExtensionsBrowserClient::GetNetLog() { + return NULL; +} + +ExtensionCache* ShellExtensionsBrowserClient::GetExtensionCache() { + return extension_cache_.get(); +} + +bool ShellExtensionsBrowserClient::IsBackgroundUpdateAllowed() { + return true; +} + +bool ShellExtensionsBrowserClient::IsMinBrowserVersionSupported( + const std::string& min_version) { + return true; +} + +} // namespace extensions diff --git a/src/browser/shell_extensions_browser_client.h b/src/browser/shell_extensions_browser_client.h new file mode 100644 index 0000000000..12fd6d398d --- /dev/null +++ b/src/browser/shell_extensions_browser_client.h @@ -0,0 +1,101 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSIONS_BROWSER_CLIENT_H_ +#define EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSIONS_BROWSER_CLIENT_H_ + +#include "base/compiler_specific.h" +#include "extensions/browser/extensions_browser_client.h" + +class PrefService; + +namespace extensions { + +class ExtensionsAPIClient; +class ShellComponentExtensionResourceManager; +// An ExtensionsBrowserClient that supports a single content::BrowserContent +// with no related incognito context. +class ShellExtensionsBrowserClient : public ExtensionsBrowserClient { + public: + // |context| is the single BrowserContext used for IsValidContext() below. + explicit ShellExtensionsBrowserClient(content::BrowserContext* context); + ~ShellExtensionsBrowserClient() override; + + // ExtensionsBrowserClient overrides: + bool IsShuttingDown() override; + bool AreExtensionsDisabled(const base::CommandLine& command_line, + content::BrowserContext* context) override; + bool IsValidContext(content::BrowserContext* context) override; + bool IsSameContext(content::BrowserContext* first, + content::BrowserContext* second) override; + bool HasOffTheRecordContext(content::BrowserContext* context) override; + content::BrowserContext* GetOffTheRecordContext( + content::BrowserContext* context) override; + content::BrowserContext* GetOriginalContext( + content::BrowserContext* context) override; + bool IsGuestSession(content::BrowserContext* context) const override; + bool IsExtensionIncognitoEnabled( + const std::string& extension_id, + content::BrowserContext* context) const override; + bool CanExtensionCrossIncognito( + const Extension* extension, + content::BrowserContext* context) const override; + net::URLRequestJob* MaybeCreateResourceBundleRequestJob( + net::URLRequest* request, + net::NetworkDelegate* network_delegate, + const base::FilePath& directory_path, + const std::string& content_security_policy, + bool send_cors_header) override; + bool AllowCrossRendererResourceLoad(net::URLRequest* request, + bool is_incognito, + const Extension* extension, + InfoMap* extension_info_map) override; + PrefService* GetPrefServiceForContext( + content::BrowserContext* context) override; + void GetEarlyExtensionPrefsObservers( + content::BrowserContext* context, + std::vector* observers) const override; + ProcessManagerDelegate* GetProcessManagerDelegate() const override; + scoped_ptr CreateExtensionHostDelegate() override; + bool DidVersionUpdate(content::BrowserContext* context) override; + void PermitExternalProtocolHandler() override; + scoped_ptr CreateAppSorting() override; + bool IsRunningInForcedAppMode() override; + ApiActivityMonitor* GetApiActivityMonitor( + content::BrowserContext* context) override; + ExtensionSystemProvider* GetExtensionSystemFactory() override; + void RegisterExtensionFunctions( + ExtensionFunctionRegistry* registry) const override; + scoped_ptr CreateRuntimeAPIDelegate( + content::BrowserContext* context) const override; + const ComponentExtensionResourceManager* + GetComponentExtensionResourceManager() override; + void BroadcastEventToRenderers(const std::string& event_name, + scoped_ptr args) override; + net::NetLog* GetNetLog() override; + ExtensionCache* GetExtensionCache() override; + bool IsBackgroundUpdateAllowed() override; + bool IsMinBrowserVersionSupported(const std::string& min_version) override; + + private: + // The single BrowserContext for app_shell. Not owned. + content::BrowserContext* browser_context_; + + // Support for extension APIs. + scoped_ptr api_client_; + + // The PrefService for |browser_context_|. + scoped_ptr prefs_; + + // The extension cache used for download and installation. + scoped_ptr extension_cache_; + + scoped_ptr resource_manager_; + + DISALLOW_COPY_AND_ASSIGN(ShellExtensionsBrowserClient); +}; + +} // namespace extensions + +#endif // EXTENSIONS_SHELL_BROWSER_SHELL_EXTENSIONS_BROWSER_CLIENT_H_ diff --git a/src/browser/shell_javascript_dialog_creator.cc b/src/browser/shell_javascript_dialog_creator.cc index 4c176b2a1e..8b4b8a649c 100644 --- a/src/browser/shell_javascript_dialog_creator.cc +++ b/src/browser/shell_javascript_dialog_creator.cc @@ -24,7 +24,6 @@ #include "base/logging.h" #include "base/strings/utf_string_conversions.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_view.h" #include "content/nw/src/browser/shell_javascript_dialog.h" #include "content/nw/src/common/shell_switches.h" #include "net/base/net_util.h" @@ -63,7 +62,7 @@ void ShellJavaScriptDialogCreator::RunJavaScriptDialog( } gfx::NativeWindow parent_window = - web_contents->GetView()->GetTopLevelNativeWindow(); + web_contents->GetTopLevelNativeWindow(); dialog_.reset(new ShellJavaScriptDialog(this, parent_window, @@ -102,7 +101,7 @@ void ShellJavaScriptDialogCreator::RunBeforeUnloadDialog( base::ASCIIToUTF16("\n\nIs it OK to leave/reload this page?"); gfx::NativeWindow parent_window = - web_contents->GetView()->GetTopLevelNativeWindow(); + web_contents->GetTopLevelNativeWindow(); dialog_.reset(new ShellJavaScriptDialog(this, parent_window, diff --git a/src/browser/shell_javascript_dialog_creator.h b/src/browser/shell_javascript_dialog_creator.h index cddf0510ad..81c32e5a8c 100644 --- a/src/browser/shell_javascript_dialog_creator.h +++ b/src/browser/shell_javascript_dialog_creator.h @@ -17,10 +17,10 @@ class ShellJavaScriptDialog; class ShellJavaScriptDialogCreator : public JavaScriptDialogManager { public: ShellJavaScriptDialogCreator(); - virtual ~ShellJavaScriptDialogCreator(); + ~ShellJavaScriptDialogCreator() final; // JavaScriptDialogCreator: - virtual void RunJavaScriptDialog( + void RunJavaScriptDialog( WebContents* web_contents, const GURL& origin_url, const std::string& accept_lang, @@ -28,20 +28,20 @@ class ShellJavaScriptDialogCreator : public JavaScriptDialogManager { const base::string16& message_text, const base::string16& default_prompt_text, const DialogClosedCallback& callback, - bool* did_suppress_message) OVERRIDE; + bool* did_suppress_message) override; - virtual void RunBeforeUnloadDialog( + void RunBeforeUnloadDialog( WebContents* web_contents, const base::string16& message_text, bool is_reload, - const DialogClosedCallback& callback) OVERRIDE; + const DialogClosedCallback& callback) override; - virtual void CancelActiveAndPendingDialogs( - WebContents* web_contents) OVERRIDE; + void CancelActiveAndPendingDialogs( + WebContents* web_contents) override; // Called by the ShellJavaScriptDialog when it closes. void DialogClosed(ShellJavaScriptDialog* dialog); - virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE; + void WebContentsDestroyed(WebContents* web_contents) override; // Used for content_browsertests. void set_dialog_request_callback( diff --git a/src/browser/shell_javascript_dialog_win.cc b/src/browser/shell_javascript_dialog_win.cc index 3611e3fd5a..636bf3028e 100644 --- a/src/browser/shell_javascript_dialog_win.cc +++ b/src/browser/shell_javascript_dialog_win.cc @@ -35,7 +35,7 @@ INT_PTR CALLBACK ShellJavaScriptDialog::DialogProc(HWND dialog, LPARAM lparam) { switch (message) { case WM_INITDIALOG: { - SetWindowLongPtr(dialog, DWL_USER, static_cast(lparam)); + SetWindowLongPtr(dialog, DWLP_USER, static_cast(lparam)); ShellJavaScriptDialog* owner = reinterpret_cast(lparam); owner->dialog_win_ = dialog; @@ -47,7 +47,7 @@ INT_PTR CALLBACK ShellJavaScriptDialog::DialogProc(HWND dialog, } case WM_DESTROY: { ShellJavaScriptDialog* owner = reinterpret_cast( - GetWindowLongPtr(dialog, DWL_USER)); + GetWindowLongPtr(dialog, DWLP_USER)); if (owner->dialog_win_) { owner->dialog_win_ = 0; owner->callback_.Run(false, base::string16()); @@ -57,7 +57,7 @@ INT_PTR CALLBACK ShellJavaScriptDialog::DialogProc(HWND dialog, } case WM_COMMAND: { ShellJavaScriptDialog* owner = reinterpret_cast( - GetWindowLongPtr(dialog, DWL_USER)); + GetWindowLongPtr(dialog, DWLP_USER)); base::string16 user_input; bool finish = false; bool result; diff --git a/src/browser/shell_login_dialog.h b/src/browser/shell_login_dialog.h index 10a0181d3f..b4c8d1a3db 100644 --- a/src/browser/shell_login_dialog.h +++ b/src/browser/shell_login_dialog.h @@ -13,7 +13,7 @@ #include "ui/base/gtk/gtk_signal.h" #endif -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_LINUX) #include "ui/views/window/dialog_delegate.h" #include "login_view.h" #endif @@ -35,7 +35,7 @@ namespace content { // This class provides a dialog box to ask the user for credentials. Useful in // ResourceDispatcherHostDelegate::CreateLoginDelegate. -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_LINUX) class ShellLoginDialog : public ResourceDispatcherHostLoginDelegate, public views::DialogDelegate { #else class ShellLoginDialog : public ResourceDispatcherHostLoginDelegate { @@ -46,7 +46,7 @@ class ShellLoginDialog : public ResourceDispatcherHostLoginDelegate { // ResourceDispatcherHostLoginDelegate implementation: // Threading: IO thread. - virtual void OnRequestCancelled() OVERRIDE; + void OnRequestCancelled() override; // Called by the platform specific code when the user responds. Public because // the aforementioned platform specific code may not have access to private @@ -56,24 +56,24 @@ class ShellLoginDialog : public ResourceDispatcherHostLoginDelegate { const base::string16& password); void UserCancelledAuth(); -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_LINUX) // views::DialogDelegate methods: - virtual base::string16 GetDialogButtonLabel(ui::DialogButton button) const OVERRIDE; - virtual base::string16 GetWindowTitle() const OVERRIDE; - virtual void WindowClosing() OVERRIDE; - virtual void DeleteDelegate() OVERRIDE; - virtual ui::ModalType GetModalType() const OVERRIDE; - virtual bool Cancel() OVERRIDE; - virtual bool Accept() OVERRIDE; - virtual views::View* GetInitiallyFocusedView() OVERRIDE; - virtual views::View* GetContentsView() OVERRIDE; - virtual views::Widget* GetWidget() OVERRIDE; - virtual const views::Widget* GetWidget() const OVERRIDE; + base::string16 GetDialogButtonLabel(ui::DialogButton button) const override; + base::string16 GetWindowTitle() const override; + void WindowClosing() override; + void DeleteDelegate() override; + ui::ModalType GetModalType() const override; + bool Cancel() override; + bool Accept() override; + views::View* GetInitiallyFocusedView() override; + views::View* GetContentsView() override; + views::Widget* GetWidget() override; + const views::Widget* GetWidget() const override; #endif protected: // Threading: any - virtual ~ShellLoginDialog(); + ~ShellLoginDialog() final; void ReleaseSoon(); int render_process_id_; @@ -119,7 +119,7 @@ class ShellLoginDialog : public ResourceDispatcherHostLoginDelegate { GtkWidget* root_; CHROMEGTK_CALLBACK_1(ShellLoginDialog, void, OnResponse, int); CHROMEGTK_CALLBACK_0(ShellLoginDialog, void, OnDestroy); -#elif defined(OS_WIN) +#elif defined(OS_WIN) || defined(OS_LINUX) LoginView* login_view_; views::Widget* dialog_; diff --git a/src/browser/shell_login_dialog_win.cc b/src/browser/shell_login_dialog_views.cc similarity index 82% rename from src/browser/shell_login_dialog_win.cc rename to src/browser/shell_login_dialog_views.cc index 156d3af07f..6b3f355cec 100644 --- a/src/browser/shell_login_dialog_win.cc +++ b/src/browser/shell_login_dialog_views.cc @@ -33,20 +33,40 @@ #include "content/public/browser/resource_dispatcher_host.h" #include "content/public/browser/resource_request_info.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_view.h" #include "ui/base/l10n/l10n_util.h" #include "ui/views/widget/widget.h" using web_modal::WebContentsModalDialogManager; using web_modal::WebContentsModalDialogManagerDelegate; +#if 0 +namespace { + +// The captive portal dialog is system-modal, but uses the web-content-modal +// dialog manager (odd) and requires this atypical dialog widget initialization. +views::Widget* CreateWindowAsFramelessChild(views::WidgetDelegate* delegate, + gfx::NativeView parent) { + views::Widget* widget = new views::Widget; + + views::Widget::InitParams params; + params.delegate = delegate; + params.child = true; + params.parent = parent; + params.remove_standard_frame = true; + params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; + + widget->Init(params); + return widget; +} + +} // namespace +#endif + namespace content { void ShellLoginDialog::PlatformCreateDialog(const base::string16& message) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - login_view_ = new LoginView(message); - // Scary thread safety note: This can potentially be called *after* SetAuth // or CancelAuth (say, if the request was cancelled before the UI thread got // control). However, that's OK since any UI interaction in those functions @@ -58,13 +78,22 @@ void ShellLoginDialog::PlatformCreateDialog(const base::string16& message) { WebContents* requesting_contents = WebContents::FromRenderFrameHost(rfh); WebContentsModalDialogManager* web_contents_modal_dialog_manager = WebContentsModalDialogManager::FromWebContents(requesting_contents); + + if (!web_contents_modal_dialog_manager) { + UserCancelledAuth(); + DeleteDelegate(); + return; + } + + login_view_ = new LoginView(message); + WebContentsModalDialogManagerDelegate* modal_delegate = web_contents_modal_dialog_manager->delegate(); CHECK(modal_delegate); - dialog_ = views::Widget::CreateWindowAsFramelessChild( - this, modal_delegate->GetWebContentsModalDialogHost()->GetHostView()); - web_contents_modal_dialog_manager->ShowDialog(dialog_->GetNativeView()); - + dialog_ = views::DialogDelegate::CreateDialogWidget( + this, NULL, modal_delegate->GetWebContentsModalDialogHost()->GetHostView()); + web_modal::WebContentsModalDialogManager::FromWebContents(requesting_contents)-> + ShowModalDialog(dialog_->GetNativeWindow()); } #if 0 @@ -122,7 +151,7 @@ void ShellLoginDialog::DeleteDelegate() { } ui::ModalType ShellLoginDialog::GetModalType() const { - return views::WidgetDelegate::GetModalType(); + return ui::MODAL_TYPE_CHILD; } bool ShellLoginDialog::Cancel() { diff --git a/src/browser/shell_resource_dispatcher_host_delegate.cc b/src/browser/shell_resource_dispatcher_host_delegate.cc index a9cbc7eb81..2de9d40be1 100644 --- a/src/browser/shell_resource_dispatcher_host_delegate.cc +++ b/src/browser/shell_resource_dispatcher_host_delegate.cc @@ -23,6 +23,7 @@ #include "chrome/browser/platform_util.h" #include "content/nw/src/browser/shell_login_dialog.h" #include "content/public/browser/browser_thread.h" +#include "url/gurl.h" namespace content { @@ -54,7 +55,7 @@ bool ShellResourceDispatcherHostDelegate::HandleExternalProtocol( // Otherwise put this work on the file thread. On Windows ShellExecute may // block for a significant amount of time, and it shouldn't hurt on Linux. BrowserThread::PostTask( - BrowserThread::FILE, + BrowserThread::UI, FROM_HERE, base::Bind(&platform_util::OpenExternal2, url)); #endif diff --git a/src/browser/shell_resource_dispatcher_host_delegate.h b/src/browser/shell_resource_dispatcher_host_delegate.h index 3679ba7d40..26ab6fc734 100644 --- a/src/browser/shell_resource_dispatcher_host_delegate.h +++ b/src/browser/shell_resource_dispatcher_host_delegate.h @@ -15,14 +15,14 @@ class ShellResourceDispatcherHostDelegate : public ResourceDispatcherHostDelegate { public: ShellResourceDispatcherHostDelegate(); - virtual ~ShellResourceDispatcherHostDelegate(); + ~ShellResourceDispatcherHostDelegate() final; // ResourceDispatcherHostDelegate implementation. - virtual ResourceDispatcherHostLoginDelegate* CreateLoginDelegate( - net::AuthChallengeInfo* auth_info, net::URLRequest* request) OVERRIDE; - virtual bool HandleExternalProtocol(const GURL& url, - int child_id, - int route_id) OVERRIDE; + ResourceDispatcherHostLoginDelegate* CreateLoginDelegate( + net::AuthChallengeInfo* auth_info, net::URLRequest* request) override; + bool HandleExternalProtocol(const GURL& url, + int child_id, + int route_id) override; // Used for content_browsertests. void set_login_request_callback( diff --git a/src/browser/shell_runtime_api_delegate.cc b/src/browser/shell_runtime_api_delegate.cc new file mode 100644 index 0000000000..3c63da7b43 --- /dev/null +++ b/src/browser/shell_runtime_api_delegate.cc @@ -0,0 +1,66 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/shell/browser/shell_runtime_api_delegate.h" + +#include "extensions/common/api/runtime.h" + +#if defined(OS_CHROMEOS) +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/power_manager_client.h" +#endif + +using extensions::core_api::runtime::PlatformInfo; + +namespace extensions { + +ShellRuntimeAPIDelegate::ShellRuntimeAPIDelegate() { +} + +ShellRuntimeAPIDelegate::~ShellRuntimeAPIDelegate() { +} + +void ShellRuntimeAPIDelegate::AddUpdateObserver(UpdateObserver* observer) { +} + +void ShellRuntimeAPIDelegate::RemoveUpdateObserver(UpdateObserver* observer) { +} + +base::Version ShellRuntimeAPIDelegate::GetPreviousExtensionVersion( + const Extension* extension) { + return base::Version(); +} + +void ShellRuntimeAPIDelegate::ReloadExtension(const std::string& extension_id) { +} + +bool ShellRuntimeAPIDelegate::CheckForUpdates( + const std::string& extension_id, + const UpdateCheckCallback& callback) { + return false; +} + +void ShellRuntimeAPIDelegate::OpenURL(const GURL& uninstall_url) { +} + +bool ShellRuntimeAPIDelegate::GetPlatformInfo(PlatformInfo* info) { +#if defined(OS_CHROMEOS) + info->os = PlatformInfo::OS_CROS_; +#elif defined(OS_LINUX) + info->os = PlatformInfo::OS_LINUX_; +#endif + return true; +} + +bool ShellRuntimeAPIDelegate::RestartDevice(std::string* error_message) { +// We allow chrome.runtime.restart() to request a device restart on ChromeOS. +#if defined(OS_CHROMEOS) + chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart(); + return true; +#endif + *error_message = "Restart is only supported on ChromeOS."; + return false; +} + +} // namespace extensions diff --git a/src/browser/shell_runtime_api_delegate.h b/src/browser/shell_runtime_api_delegate.h new file mode 100644 index 0000000000..816423b097 --- /dev/null +++ b/src/browser/shell_runtime_api_delegate.h @@ -0,0 +1,36 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_SHELL_BROWSER_SHELL_RUNTIME_API_DELEGATE_H_ +#define EXTENSIONS_SHELL_BROWSER_SHELL_RUNTIME_API_DELEGATE_H_ + +#include "base/macros.h" +#include "extensions/browser/api/runtime/runtime_api_delegate.h" + +namespace extensions { + +class ShellRuntimeAPIDelegate : public RuntimeAPIDelegate { + public: + ShellRuntimeAPIDelegate(); + ~ShellRuntimeAPIDelegate() override; + + // RuntimeAPIDelegate implementation. + void AddUpdateObserver(UpdateObserver* observer) override; + void RemoveUpdateObserver(UpdateObserver* observer) override; + base::Version GetPreviousExtensionVersion( + const Extension* extension) override; + void ReloadExtension(const std::string& extension_id) override; + bool CheckForUpdates(const std::string& extension_id, + const UpdateCheckCallback& callback) override; + void OpenURL(const GURL& uninstall_url) override; + bool GetPlatformInfo(core_api::runtime::PlatformInfo* info) override; + bool RestartDevice(std::string* error_message) override; + + private: + DISALLOW_COPY_AND_ASSIGN(ShellRuntimeAPIDelegate); +}; + +} // namespace extensions + +#endif // EXTENSIONS_SHELL_BROWSER_SHELL_RUNTIME_API_DELEGATE_H_ diff --git a/src/browser/shell_speech_recognition_manager_delegate.cc b/src/browser/shell_speech_recognition_manager_delegate.cc new file mode 100644 index 0000000000..1cfe565dd9 --- /dev/null +++ b/src/browser/shell_speech_recognition_manager_delegate.cc @@ -0,0 +1,115 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/browser/shell_speech_recognition_manager_delegate.h" + +#include "base/bind.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/speech_recognition_manager.h" +#include "content/public/browser/speech_recognition_session_context.h" +#include "content/public/browser/web_contents.h" + +using content::BrowserThread; +using content::SpeechRecognitionManager; +using content::WebContents; + +namespace content { +namespace speech { + +ShellSpeechRecognitionManagerDelegate::ShellSpeechRecognitionManagerDelegate() { +} + +ShellSpeechRecognitionManagerDelegate:: +~ShellSpeechRecognitionManagerDelegate() { +} + +void ShellSpeechRecognitionManagerDelegate::OnRecognitionStart(int session_id) { +} + +void ShellSpeechRecognitionManagerDelegate::OnAudioStart(int session_id) { +} + +void ShellSpeechRecognitionManagerDelegate::OnEnvironmentEstimationComplete( + int session_id) { +} + +void ShellSpeechRecognitionManagerDelegate::OnSoundStart(int session_id) { +} + +void ShellSpeechRecognitionManagerDelegate::OnSoundEnd(int session_id) { +} + +void ShellSpeechRecognitionManagerDelegate::OnAudioEnd(int session_id) { +} + +void ShellSpeechRecognitionManagerDelegate::OnRecognitionEnd(int session_id) { +} + +void ShellSpeechRecognitionManagerDelegate::OnRecognitionResults( + int session_id, + const content::SpeechRecognitionResults& result) { +} + +void ShellSpeechRecognitionManagerDelegate::OnRecognitionError( + int session_id, + const content::SpeechRecognitionError& error) { +} + +void ShellSpeechRecognitionManagerDelegate::OnAudioLevelsChange( + int session_id, + float volume, + float noise_volume) { +} + +void ShellSpeechRecognitionManagerDelegate::GetDiagnosticInformation( + bool* can_report_metrics, + std::string* hardware_info) { +} + +void ShellSpeechRecognitionManagerDelegate::CheckRecognitionIsAllowed( + int session_id, + base::Callback callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + const content::SpeechRecognitionSessionContext& context = + SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); + + // Make sure that initiators (extensions/web pages) properly set the + // |render_process_id| field, which is needed later to retrieve the profile. + DCHECK_NE(context.render_process_id, 0); + + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&CheckRenderViewType, + callback, + context.render_process_id, + context.render_view_id)); +} + +content::SpeechRecognitionEventListener* +ShellSpeechRecognitionManagerDelegate::GetEventListener() { + return this; +} + +bool ShellSpeechRecognitionManagerDelegate::FilterProfanities( + int render_process_id) { + // TODO(zork): Determine where this preference should come from. + return true; +} + +// static +void ShellSpeechRecognitionManagerDelegate::CheckRenderViewType( + base::Callback callback, + int render_process_id, + int render_view_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + bool allowed = true; + bool check_permission = false; + + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, + base::Bind(callback, check_permission, allowed)); +} + +} // namespace speech +} // namespace extensions diff --git a/src/browser/shell_speech_recognition_manager_delegate.h b/src/browser/shell_speech_recognition_manager_delegate.h new file mode 100644 index 0000000000..47622e124c --- /dev/null +++ b/src/browser/shell_speech_recognition_manager_delegate.h @@ -0,0 +1,60 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NW_SHELL_BROWSER_SHELL_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_ +#define NW_SHELL_BROWSER_SHELL_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_ + +#include "content/public/browser/speech_recognition_event_listener.h" +#include "content/public/browser/speech_recognition_manager_delegate.h" + +namespace content { +namespace speech { + +class ShellSpeechRecognitionManagerDelegate + : public content::SpeechRecognitionManagerDelegate, + public content::SpeechRecognitionEventListener { + public: + ShellSpeechRecognitionManagerDelegate(); + ~ShellSpeechRecognitionManagerDelegate() override; + + private: + // SpeechRecognitionEventListener methods. + void OnRecognitionStart(int session_id) override; + void OnAudioStart(int session_id) override; + void OnEnvironmentEstimationComplete(int session_id) override; + void OnSoundStart(int session_id) override; + void OnSoundEnd(int session_id) override; + void OnAudioEnd(int session_id) override; + void OnRecognitionEnd(int session_id) override; + void OnRecognitionResults( + int session_id, + const content::SpeechRecognitionResults& result) override; + void OnRecognitionError( + int session_id, + const content::SpeechRecognitionError& error) override; + void OnAudioLevelsChange(int session_id, + float volume, + float noise_volume) override; + + // SpeechRecognitionManagerDelegate methods. + void GetDiagnosticInformation(bool* can_report_metrics, + std::string* hardware_info) override; + void CheckRecognitionIsAllowed( + int session_id, + base::Callback callback) override; + content::SpeechRecognitionEventListener* GetEventListener() override; + bool FilterProfanities(int render_process_id) override; + + static void CheckRenderViewType( + base::Callback callback, + int render_process_id, + int render_view_id); + + DISALLOW_COPY_AND_ASSIGN(ShellSpeechRecognitionManagerDelegate); +}; + +} // namespace speech +} // namespace extensions + +#endif // NW_SHELL_BROWSER_SHELL_CONTENT_BROWSER_CLIENT_H_ diff --git a/src/browser/shell_web_contents_modal_dialog_manager.cc b/src/browser/shell_web_contents_modal_dialog_manager.cc new file mode 100644 index 0000000000..32d44fb670 --- /dev/null +++ b/src/browser/shell_web_contents_modal_dialog_manager.cc @@ -0,0 +1,18 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/web_modal/web_contents_modal_dialog_manager.h" + +namespace web_modal { + +SingleWebContentsDialogManager* +WebContentsModalDialogManager::CreateNativeWebModalManager( + NativeWebContentsModalDialog dialog, + SingleWebContentsDialogManagerDelegate* native_delegate) { + // TODO(oshima): Investigate if we need to implement this. + NOTREACHED(); + return NULL; +} + +} // namespace web_modal diff --git a/src/browser/shell_web_view_guest_delegate.cc b/src/browser/shell_web_view_guest_delegate.cc new file mode 100644 index 0000000000..49c19e4e92 --- /dev/null +++ b/src/browser/shell_web_view_guest_delegate.cc @@ -0,0 +1,54 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +#include "content/nw/src/browser/shell_web_view_guest_delegate.h" + +#include "content/public/browser/child_process_security_policy.h" +#include "content/public/browser/render_process_host.h" +#include "extensions/browser/api/web_request/web_request_api.h" +#include "extensions/browser/guest_view/web_view/web_view_constants.h" + +namespace extensions { + +ShellWebViewGuestDelegate::ShellWebViewGuestDelegate( + WebViewGuest* web_view_guest) + : web_view_guest_(web_view_guest), + nw_disabled_(false), + weak_ptr_factory_(this) { +} + +ShellWebViewGuestDelegate::~ShellWebViewGuestDelegate() { +} + +void ShellWebViewGuestDelegate::OnAttachWebViewHelpers( + content::WebContents* contents) { +} + +void ShellWebViewGuestDelegate::OnDidAttachToEmbedder() { + web_view_guest_->attach_params()->GetBoolean("nwdisable", &nw_disabled_); + if (nw_disabled_) + return; + content::RenderProcessHost* host = + web_view_guest_->web_contents()->GetRenderProcessHost(); + content::ChildProcessSecurityPolicy::GetInstance()->GrantScheme( + host->GetID(), url::kFileScheme); +} + +void ShellWebViewGuestDelegate::OnDidCommitProvisionalLoadForFrame( + bool is_main_frame) { +} + +void ShellWebViewGuestDelegate::OnDidInitialize() { +} + +void ShellWebViewGuestDelegate::OnDocumentLoadedInFrame( + content::RenderFrameHost* render_frame_host) { +} + + +void ShellWebViewGuestDelegate::OnGuestDestroyed() { +} + +} // namespace extensions diff --git a/src/browser/shell_web_view_guest_delegate.h b/src/browser/shell_web_view_guest_delegate.h new file mode 100644 index 0000000000..efbfec1ae8 --- /dev/null +++ b/src/browser/shell_web_view_guest_delegate.h @@ -0,0 +1,50 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NW_BROWSER_GUEST_VIEW_WEB_VIEW_CHROME_WEB_VIEW_GUEST_DELEGATE_H_ +#define NW_BROWSER_GUEST_VIEW_WEB_VIEW_CHROME_WEB_VIEW_GUEST_DELEGATE_H_ + +#include "extensions/browser/guest_view/web_view/web_view_guest.h" +#include "extensions/browser/guest_view/web_view/web_view_guest_delegate.h" + + +namespace extensions { + +class ShellWebViewGuestDelegate : public WebViewGuestDelegate { + public : + explicit ShellWebViewGuestDelegate(WebViewGuest* web_view_guest); + ~ShellWebViewGuestDelegate() override; + + // WebViewGuestDelegate implementation. + bool HandleContextMenu(const content::ContextMenuParams& params) override; + void OnAttachWebViewHelpers(content::WebContents* contents) override; + void OnDidAttachToEmbedder() override; + void OnDidCommitProvisionalLoadForFrame(bool is_main_frame) override; + void OnDidInitialize() override; + void OnDocumentLoadedInFrame( + content::RenderFrameHost* render_frame_host) override; + void OnGuestDestroyed() override; + void OnShowContextMenu(int request_id, const MenuItemVector* items) override; + + WebViewGuest* web_view_guest() const { return web_view_guest_; } + + private: + content::WebContents* guest_web_contents() const { + return web_view_guest()->web_contents(); + } + + WebViewGuest* const web_view_guest_; + + bool nw_disabled_; + // This is used to ensure pending tasks will not fire after this object is + // destroyed. + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(ShellWebViewGuestDelegate); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_GUEST_VIEW_WEB_VIEW_CHROME_WEB_VIEW_GUEST_DELEGATE_H_ + diff --git a/src/browser/tab_autofill_manager_delegate.cc b/src/browser/tab_autofill_manager_delegate.cc deleted file mode 100644 index 143e79f729..0000000000 --- a/src/browser/tab_autofill_manager_delegate.cc +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/ui/autofill/tab_autofill_manager_delegate.h" - -#include "base/logging.h" -#include "base/prefs/pref_service.h" -#include "chrome/browser/ui/autofill/autofill_popup_controller_impl.h" -#include "components/autofill/content/browser/content_autofill_driver.h" -#include "components/autofill/content/common/autofill_messages.h" -#include "components/autofill/core/common/autofill_pref_names.h" -#include "content/public/browser/render_view_host.h" -#include "content/public/browser/web_contents_view.h" -#include "content/nw/src/shell_browser_context.h" -#include "content/nw/src/browser/nw_form_database_service.h" -#include "ui/gfx/rect.h" - -#if defined(OS_ANDROID) -#include "chrome/browser/ui/android/autofill/autofill_logger_android.h" -#endif - -DEFINE_WEB_CONTENTS_USER_DATA_KEY(autofill::TabAutofillManagerDelegate); - -namespace autofill { - -TabAutofillManagerDelegate::TabAutofillManagerDelegate( - content::WebContents* web_contents) - : content::WebContentsObserver(web_contents), - web_contents_(web_contents) { - DCHECK(web_contents); -} - -TabAutofillManagerDelegate::~TabAutofillManagerDelegate() { - // NOTE: It is too late to clean up the autofill popup; that cleanup process - // requires that the WebContents instance still be valid and it is not at - // this point (in particular, the WebContentsImpl destructor has already - // finished running and we are now in the base class destructor). - DCHECK(!popup_controller_); -} - -void TabAutofillManagerDelegate::TabActivated() { -} - -PersonalDataManager* TabAutofillManagerDelegate::GetPersonalDataManager() { - return NULL; -} - -scoped_refptr - TabAutofillManagerDelegate::GetDatabase() { - nw::NwFormDatabaseService* service = - static_cast( - web_contents_->GetBrowserContext())->GetFormDatabaseService(); - return service->get_autofill_webdata_service(); -} - -PrefService* TabAutofillManagerDelegate::GetPrefs() { - return NULL; -} - -void TabAutofillManagerDelegate::ShowAutofillSettings() { - NOTIMPLEMENTED(); -} - -void TabAutofillManagerDelegate::ConfirmSaveCreditCard( - const AutofillMetrics& metric_logger, - const base::Closure& save_card_callback) { - NOTIMPLEMENTED(); -} - -void TabAutofillManagerDelegate::ShowRequestAutocompleteDialog( - const FormData& form, - const GURL& source_url, - const base::Callback& callback) { - NOTIMPLEMENTED(); -} - -void TabAutofillManagerDelegate::ShowAutofillPopup( - const gfx::RectF& element_bounds, - base::i18n::TextDirection text_direction, - const std::vector& values, - const std::vector& labels, - const std::vector& icons, - const std::vector& identifiers, - base::WeakPtr delegate) { - // Convert element_bounds to be in screen space. - gfx::Rect client_area; - web_contents_->GetView()->GetContainerBounds(&client_area); - gfx::RectF element_bounds_in_screen_space = - element_bounds + client_area.OffsetFromOrigin(); - - // Will delete or reuse the old |popup_controller_|. - popup_controller_ = AutofillPopupControllerImpl::GetOrCreate( - popup_controller_, - delegate, - web_contents(), - web_contents()->GetView()->GetNativeView(), - element_bounds_in_screen_space, - text_direction); - - popup_controller_->Show(values, labels, icons, identifiers); -} - -void TabAutofillManagerDelegate::UpdateAutofillPopupDataListValues( - const std::vector& values, - const std::vector& labels) { - if (popup_controller_.get()) - popup_controller_->UpdateDataListValues(values, labels); -} - -void TabAutofillManagerDelegate::HideAutofillPopup() { - if (popup_controller_.get()) - popup_controller_->Hide(); - -} - -bool TabAutofillManagerDelegate::IsAutocompleteEnabled() { - return true; -} - -void TabAutofillManagerDelegate::HideRequestAutocompleteDialog() { - NOTIMPLEMENTED(); -} - -void TabAutofillManagerDelegate::WebContentsDestroyed( - content::WebContents* web_contents) { - HideAutofillPopup(); -} - -void TabAutofillManagerDelegate::DetectAccountCreationForms( - const std::vector& forms) { - NOTIMPLEMENTED(); -} - -void TabAutofillManagerDelegate::DidFillOrPreviewField( - const base::string16& autofilled_value, - const base::string16& profile_full_name) { -} - -} // namespace autofill diff --git a/src/browser/tab_autofill_manager_delegate.h b/src/browser/tab_autofill_manager_delegate.h deleted file mode 100644 index 6830858bbd..0000000000 --- a/src/browser/tab_autofill_manager_delegate.h +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_UI_AUTOFILL_TAB_AUTOFILL_MANAGER_DELEGATE_H_ -#define CHROME_BROWSER_UI_AUTOFILL_TAB_AUTOFILL_MANAGER_DELEGATE_H_ - -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/i18n/rtl.h" -#include "base/memory/weak_ptr.h" -#include "components/autofill/core/browser/autofill_manager_delegate.h" -#include "content/public/browser/web_contents_observer.h" -#include "content/public/browser/web_contents_user_data.h" - -namespace content { -struct FrameNavigateParams; -struct LoadCommittedDetails; -class WebContents; -} - -namespace autofill { - -class AutofillDialogController; -class AutofillPopupControllerImpl; -struct FormData; - -// Chrome implementation of AutofillManagerDelegate. -class TabAutofillManagerDelegate - : public AutofillManagerDelegate, - public content::WebContentsUserData, - public content::WebContentsObserver { - public: - virtual ~TabAutofillManagerDelegate(); - - // Called when the tab corresponding to |this| instance is activated. - void TabActivated(); - - // AutofillManagerDelegate implementation. - virtual PersonalDataManager* GetPersonalDataManager() OVERRIDE; - virtual scoped_refptr - GetDatabase() OVERRIDE; - virtual PrefService* GetPrefs() OVERRIDE; - virtual void HideRequestAutocompleteDialog() OVERRIDE; - virtual void ShowAutofillSettings() OVERRIDE; - virtual void ConfirmSaveCreditCard( - const AutofillMetrics& metric_logger, - const base::Closure& save_card_callback) OVERRIDE; - virtual void ShowRequestAutocompleteDialog( - const FormData& form, - const GURL& source_url, - const base::Callback& callback) OVERRIDE; - virtual void ShowAutofillPopup( - const gfx::RectF& element_bounds, - base::i18n::TextDirection text_direction, - const std::vector& values, - const std::vector& labels, - const std::vector& icons, - const std::vector& identifiers, - base::WeakPtr delegate) OVERRIDE; - virtual void UpdateAutofillPopupDataListValues( - const std::vector& values, - const std::vector& labels) OVERRIDE; - virtual void HideAutofillPopup() OVERRIDE; - virtual bool IsAutocompleteEnabled() OVERRIDE; - virtual void DetectAccountCreationForms( - const std::vector& forms) OVERRIDE; - virtual void DidFillOrPreviewField( - const base::string16& autofilled_value, - const base::string16& profile_full_name) OVERRIDE; - - // content::WebContentsObserver implementation. - virtual void WebContentsDestroyed( - content::WebContents* web_contents) OVERRIDE; - - // Exposed for testing. - AutofillDialogController* GetDialogControllerForTesting() { - return dialog_controller_.get(); - } - void SetDialogControllerForTesting( - const base::WeakPtr& dialog_controller) { - dialog_controller_ = dialog_controller; - } - - private: - explicit TabAutofillManagerDelegate(content::WebContents* web_contents); - friend class content::WebContentsUserData; - - content::WebContents* const web_contents_; - base::WeakPtr dialog_controller_; - base::WeakPtr popup_controller_; - - DISALLOW_COPY_AND_ASSIGN(TabAutofillManagerDelegate); -}; - -} // namespace autofill - -#endif // CHROME_BROWSER_UI_AUTOFILL_TAB_AUTOFILL_MANAGER_DELEGATE_H_ diff --git a/src/chrome_breakpad_client.cc b/src/chrome_breakpad_client.cc index a41ff3e367..d20083d32f 100644 --- a/src/chrome_breakpad_client.cc +++ b/src/chrome_breakpad_client.cc @@ -21,6 +21,8 @@ #include "chrome/common/crash_keys.h" #include "chrome/common/env_vars.h" +#include "id/commit.h" + #if defined(OS_WIN) #include @@ -77,10 +79,10 @@ ChromeBreakpadClient::ChromeBreakpadClient() {} ChromeBreakpadClient::~ChromeBreakpadClient() {} -void ChromeBreakpadClient::SetClientID(const std::string& client_id) { - crash_keys::SetClientID(client_id); +void ChromeBreakpadClient::SetBreakpadClientIdFromGUID( + const std::string& client_guid) { + crash_keys::SetCrashClientIdFromGUID(client_guid); } - #if defined(OS_WIN) bool ChromeBreakpadClient::GetAlternativeCrashDumpLocation( base::FilePath* crash_dir) { @@ -266,19 +268,10 @@ void ChromeBreakpadClient::GetProductNameAndVersion(std::string* product_name, std::string* version) { DCHECK(product_name); DCHECK(version); -#if defined(OS_ANDROID) - *product_name = "Chrome_Android"; -#elif defined(OS_CHROMEOS) - *product_name = "Chrome_ChromeOS"; -#else // OS_LINUX -#if !defined(ADDRESS_SANITIZER) - *product_name = "Chrome_Linux"; -#else - *product_name = "Chrome_Linux_ASan"; -#endif -#endif - *version = NW_VERSION_STRING; + *product_name = "node-webkit"; + + *version = NW_VERSION_STRING " " NW_COMMIT_HASH; } base::FilePath ChromeBreakpadClient::GetReporterLogFilename() { diff --git a/src/chrome_breakpad_client.h b/src/chrome_breakpad_client.h index 489e8fe512..d056f23f95 100644 --- a/src/chrome_breakpad_client.h +++ b/src/chrome_breakpad_client.h @@ -17,7 +17,6 @@ class ChromeBreakpadClient : public breakpad::BreakpadClient { virtual ~ChromeBreakpadClient(); // breakpad::BreakpadClient implementation. - virtual void SetClientID(const std::string& client_id) OVERRIDE; #if defined(OS_WIN) virtual bool GetAlternativeCrashDumpLocation(base::FilePath* crash_dir) OVERRIDE; diff --git a/src/common/common_message_generator.h b/src/common/common_message_generator.h index 1879cedc93..5b837918aa 100644 --- a/src/common/common_message_generator.h +++ b/src/common/common_message_generator.h @@ -1,2 +1,5 @@ +#include "extensions/common/extension_messages.h" #include "content/nw/src/api/api_messages.h" #include "content/nw/src/common/print_messages.h" +#include "chrome/common/chrome_utility_messages.h" +#include "chrome/common/chrome_utility_printing_messages.h" diff --git a/src/common/gpu_internals.cc b/src/common/gpu_internals.cc deleted file mode 100644 index bf67a32d91..0000000000 --- a/src/common/gpu_internals.cc +++ /dev/null @@ -1,446 +0,0 @@ -// 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/common/gpu_internals.h" - -#include "base/callback.h" -#include "base/command_line.h" -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/string_number_conversions.h" -#include "base/stringprintf.h" -#include "base/sys_info.h" -#include "base/values.h" -#include "cc/switches.h" -#include "content/public/browser/compositor_util.h" -#include "content/public/browser/gpu_data_manager.h" -#include "content/public/browser/gpu_data_manager_observer.h" -#include "content/public/common/content_switches.h" -#include "content/public/common/gpu_info.h" -#include "third_party/angle/src/common/version.h" - -using content::GpuDataManager; -using content::GpuFeatureType; - -namespace { - -struct GpuFeatureInfo { - std::string name; - uint32 blocked; - bool disabled; - std::string disabled_description; - bool fallback_to_software; -}; - -DictionaryValue* NewDescriptionValuePair(const std::string& desc, - const std::string& value) { - DictionaryValue* dict = new DictionaryValue(); - dict->SetString("description", desc); - dict->SetString("value", value); - return dict; -} - -DictionaryValue* NewDescriptionValuePair(const std::string& desc, - Value* value) { - DictionaryValue* dict = new DictionaryValue(); - dict->SetString("description", desc); - dict->Set("value", value); - return dict; -} - -Value* NewStatusValue(const char* name, const char* status) { - DictionaryValue* value = new DictionaryValue(); - value->SetString("name", name); - value->SetString("status", status); - return value; -} - -#if defined(OS_WIN) -// Output DxDiagNode tree as nested array of {description,value} pairs -ListValue* DxDiagNodeToList(const content::DxDiagNode& node) { - ListValue* list = new ListValue(); - for (std::map::const_iterator it = - node.values.begin(); - it != node.values.end(); - ++it) { - list->Append(NewDescriptionValuePair(it->first, it->second)); - } - - for (std::map::const_iterator it = - node.children.begin(); - it != node.children.end(); - ++it) { - ListValue* sublist = DxDiagNodeToList(it->second); - list->Append(NewDescriptionValuePair(it->first, sublist)); - } - return list; -} -#endif - -std::string GPUDeviceToString(const content::GPUInfo::GPUDevice& gpu) { - std::string vendor = base::StringPrintf("0x%04x", gpu.vendor_id); - if (!gpu.vendor_string.empty()) - vendor += " [" + gpu.vendor_string + "]"; - std::string device = base::StringPrintf("0x%04x", gpu.device_id); - if (!gpu.device_string.empty()) - device += " [" + gpu.device_string + "]"; - return base::StringPrintf( - "VENDOR = %s, DEVICE= %s", vendor.c_str(), device.c_str()); -} - -DictionaryValue* GpuInfoAsDictionaryValue() { - content::GPUInfo gpu_info = GpuDataManager::GetInstance()->GetGPUInfo(); - ListValue* basic_info = new ListValue(); - basic_info->Append(NewDescriptionValuePair( - "Initialization time", - base::Int64ToString(gpu_info.initialization_time.InMilliseconds()))); - basic_info->Append(NewDescriptionValuePair( - "Sandboxed", - Value::CreateBooleanValue(gpu_info.sandboxed))); - basic_info->Append(NewDescriptionValuePair( - "GPU0", GPUDeviceToString(gpu_info.gpu))); - for (size_t i = 0; i < gpu_info.secondary_gpus.size(); ++i) { - basic_info->Append(NewDescriptionValuePair( - base::StringPrintf("GPU%d", static_cast(i + 1)), - GPUDeviceToString(gpu_info.secondary_gpus[i]))); - } - basic_info->Append(NewDescriptionValuePair( - "Optimus", Value::CreateBooleanValue(gpu_info.optimus))); - basic_info->Append(NewDescriptionValuePair( - "AMD switchable", Value::CreateBooleanValue(gpu_info.amd_switchable))); - basic_info->Append(NewDescriptionValuePair("Driver vendor", - gpu_info.driver_vendor)); - basic_info->Append(NewDescriptionValuePair("Driver version", - gpu_info.driver_version)); - basic_info->Append(NewDescriptionValuePair("Driver date", - gpu_info.driver_date)); - basic_info->Append(NewDescriptionValuePair("Pixel shader version", - gpu_info.pixel_shader_version)); - basic_info->Append(NewDescriptionValuePair("Vertex shader version", - gpu_info.vertex_shader_version)); - basic_info->Append(NewDescriptionValuePair("Machine model", - gpu_info.machine_model)); - basic_info->Append(NewDescriptionValuePair("GL version", - gpu_info.gl_version)); - basic_info->Append(NewDescriptionValuePair("GL_VENDOR", - gpu_info.gl_vendor)); - basic_info->Append(NewDescriptionValuePair("GL_RENDERER", - gpu_info.gl_renderer)); - basic_info->Append(NewDescriptionValuePair("GL_VERSION", - gpu_info.gl_version_string)); - basic_info->Append(NewDescriptionValuePair("GL_EXTENSIONS", - gpu_info.gl_extensions)); - - DictionaryValue* info = new DictionaryValue(); - info->Set("basic_info", basic_info); - -#if defined(OS_WIN) - ListValue* perf_info = new ListValue(); - perf_info->Append(NewDescriptionValuePair( - "Graphics", - base::StringPrintf("%.1f", gpu_info.performance_stats.graphics))); - perf_info->Append(NewDescriptionValuePair( - "Gaming", - base::StringPrintf("%.1f", gpu_info.performance_stats.gaming))); - perf_info->Append(NewDescriptionValuePair( - "Overall", - base::StringPrintf("%.1f", gpu_info.performance_stats.overall))); - info->Set("performance_info", perf_info); - - Value* dx_info; - if (gpu_info.dx_diagnostics.children.size()) - dx_info = DxDiagNodeToList(gpu_info.dx_diagnostics); - else - dx_info = Value::CreateNullValue(); - info->Set("diagnostics", dx_info); -#endif - - return info; -} - -// Determine if accelerated-2d-canvas is supported, which depends on whether -// lose_context could happen and whether skia is the backend. -bool SupportsAccelerated2dCanvas() { - if (GpuDataManager::GetInstance()->GetGPUInfo().can_lose_context) - return false; -#if defined(USE_SKIA) - return true; -#else - return false; -#endif -} - -Value* GetFeatureStatus() { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); - bool gpu_access_blocked = !GpuDataManager::GetInstance()->GpuAccessAllowed(); - - uint32 flags = GpuDataManager::GetInstance()->GetBlacklistedFeatures(); - DictionaryValue* status = new DictionaryValue(); - - const GpuFeatureInfo kGpuFeatureInfo[] = { - { - "2d_canvas", - flags & content::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS, - command_line.HasSwitch(switches::kDisableAccelerated2dCanvas) || - !SupportsAccelerated2dCanvas(), - "Accelerated 2D canvas is unavailable: either disabled at the command" - " line or not supported by the current system.", - true - }, - { - "compositing", - flags & content::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING, - command_line.HasSwitch(switches::kDisableAcceleratedCompositing), - "Accelerated compositing has been disabled, either via about:flags or" - " command line. This adversely affects performance of all hardware" - " accelerated features.", - true - }, - { - "3d_css", - flags & (content::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING | - content::GPU_FEATURE_TYPE_3D_CSS), - command_line.HasSwitch(switches::kDisableAcceleratedLayers), - "Accelerated layers have been disabled at the command line.", - false - }, - { - "css_animation", - flags & (content::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING | - content::GPU_FEATURE_TYPE_3D_CSS), - command_line.HasSwitch(cc::switches::kDisableThreadedAnimation) || - command_line.HasSwitch(switches::kDisableAcceleratedCompositing) || - command_line.HasSwitch(switches::kDisableAcceleratedLayers), - "Accelerated CSS animation has been disabled at the command line.", - true - }, - { - "webgl", - flags & content::GPU_FEATURE_TYPE_WEBGL, -#if defined(OS_ANDROID) - !command_line.HasSwitch(switches::kEnableExperimentalWebGL), -#else - command_line.HasSwitch(switches::kDisableExperimentalWebGL), -#endif - "WebGL has been disabled, either via about:flags or command line.", - false - }, - { - "multisampling", - flags & content::GPU_FEATURE_TYPE_MULTISAMPLING, - command_line.HasSwitch(switches::kDisableGLMultisampling), - "Multisampling has been disabled, either via about:flags or command" - " line.", - false - }, - { - "flash_3d", - flags & content::GPU_FEATURE_TYPE_FLASH3D, - command_line.HasSwitch(switches::kDisableFlash3d), - "Using 3d in flash has been disabled, either via about:flags or" - " command line.", - false - }, - { - "flash_stage3d", - flags & content::GPU_FEATURE_TYPE_FLASH_STAGE3D, - command_line.HasSwitch(switches::kDisableFlashStage3d), - "Using Stage3d in Flash has been disabled, either via about:flags or" - " command line.", - false - }, - { - "texture_sharing", - flags & content::GPU_FEATURE_TYPE_TEXTURE_SHARING, - command_line.HasSwitch(switches::kDisableImageTransportSurface), - "Sharing textures between processes has been disabled, either via" - " about:flags or command line.", - false - }, - { - "video_decode", - flags & content::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE, - command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode), - "Accelerated video decode has been disabled, either via about:flags" - " or command line.", - true - }, - { - "video", - flags & content::GPU_FEATURE_TYPE_ACCELERATED_VIDEO, - command_line.HasSwitch(switches::kDisableAcceleratedVideo) || - command_line.HasSwitch(switches::kDisableAcceleratedCompositing), - "Accelerated video presentation has been disabled, either via" - " about:flags or command line.", - true - }, - { - "panel_fitting", - flags & content::GPU_FEATURE_TYPE_PANEL_FITTING, -#if defined(OS_CHROMEOS) - command_line.HasSwitch(ash::switches::kAshDisablePanelFitting), -#else - true, -#endif - "Panel fitting is unavailable, either disabled at the command" - " line or not supported by the current system.", - false - } - }; - const size_t kNumFeatures = sizeof(kGpuFeatureInfo) / sizeof(GpuFeatureInfo); - - // Build the feature_status field. - { - ListValue* feature_status_list = new ListValue(); - - for (size_t i = 0; i < kNumFeatures; ++i) { - std::string status; - if (kGpuFeatureInfo[i].disabled) { - status = "disabled"; - if (kGpuFeatureInfo[i].name == "css_animation") { - status += "_software_animated"; - } else { - if (kGpuFeatureInfo[i].fallback_to_software) - status += "_software"; - else - status += "_off"; - } - } else if (GpuDataManager::GetInstance()->ShouldUseSoftwareRendering()) { - status = "unavailable_software"; - } else if (kGpuFeatureInfo[i].blocked || - gpu_access_blocked) { - status = "unavailable"; - if (kGpuFeatureInfo[i].fallback_to_software) - status += "_software"; - else - status += "_off"; - } else { - status = "enabled"; - if (kGpuFeatureInfo[i].name == "webgl" && - (command_line.HasSwitch(switches::kDisableAcceleratedCompositing) || - (flags & content::GPU_FEATURE_TYPE_ACCELERATED_COMPOSITING))) - status += "_readback"; - bool has_thread = content::IsThreadedCompositingEnabled(); - if (kGpuFeatureInfo[i].name == "compositing") { - bool force_compositing = - content::IsForceCompositingModeEnabled(); - if (force_compositing) - status += "_force"; - if (has_thread) - status += "_threaded"; - } - if (kGpuFeatureInfo[i].name == "css_animation") { - if (has_thread) - status = "accelerated_threaded"; - else - status = "accelerated"; - } - } - feature_status_list->Append( - NewStatusValue(kGpuFeatureInfo[i].name.c_str(), status.c_str())); - } - content::GPUInfo gpu_info = GpuDataManager::GetInstance()->GetGPUInfo(); - if (gpu_info.secondary_gpus.size() > 0 || - gpu_info.optimus || gpu_info.amd_switchable) { - std::string gpu_switching; - switch (GpuDataManager::GetInstance()->GetGpuSwitchingOption()) { - case content::GPU_SWITCHING_OPTION_AUTOMATIC: - gpu_switching = "gpu_switching_automatic"; - break; - case content::GPU_SWITCHING_OPTION_FORCE_DISCRETE: - gpu_switching = "gpu_switching_force_discrete"; - break; - case content::GPU_SWITCHING_OPTION_FORCE_INTEGRATED: - gpu_switching = "gpu_switching_force_integrated"; - break; - default: - break; - } - feature_status_list->Append( - NewStatusValue("gpu_switching", gpu_switching.c_str())); - } - status->Set("featureStatus", feature_status_list); - } - - // Build the problems list. - { - ListValue* problem_list = - GpuDataManager::GetInstance()->GetBlacklistReasons(); - - if (gpu_access_blocked) { - DictionaryValue* problem = new DictionaryValue(); - problem->SetString("description", - "GPU process was unable to boot. Access to GPU disallowed."); - problem->Set("crBugs", new ListValue()); - problem->Set("webkitBugs", new ListValue()); - problem_list->Append(problem); - } - - for (size_t i = 0; i < kNumFeatures; ++i) { - if (kGpuFeatureInfo[i].disabled) { - DictionaryValue* problem = new DictionaryValue(); - problem->SetString( - "description", kGpuFeatureInfo[i].disabled_description); - problem->Set("crBugs", new ListValue()); - problem->Set("webkitBugs", new ListValue()); - problem_list->Append(problem); - } - } - - status->Set("problems", problem_list); - } - - return status; -} - -} // namespace - -void PrintGpuInfo() { - // Get GPU Info. - scoped_ptr gpu_info_val( - GpuInfoAsDictionaryValue()); - - // Add in blacklisting features - Value* feature_status = GetFeatureStatus(); - if (feature_status) - gpu_info_val->Set("featureStatus", feature_status); - - LOG(ERROR) << *gpu_info_val; -} - -void PrintClientInfo() { - DictionaryValue* dict = new DictionaryValue(); - - dict->SetString("operating_system", - base::SysInfo::OperatingSystemName() + " " + - base::SysInfo::OperatingSystemVersion()); - dict->SetString("angle_revision", base::UintToString(BUILD_REVISION)); -#if defined(USE_SKIA) - dict->SetString("graphics_backend", "Skia"); -#else - dict->SetString("graphics_backend", "Core Graphics"); -#endif - dict->SetString("blacklist_version", - GpuDataManager::GetInstance()->GetBlacklistVersion()); - - LOG(ERROR) << *dict; - - delete dict; -} diff --git a/src/common/print_messages.cc b/src/common/print_messages.cc index f4fae5cc88..33420bcfd8 100644 --- a/src/common/print_messages.cc +++ b/src/common/print_messages.cc @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/nw/src/common/print_messages.h" +#include "chrome/common/print_messages.h" #include "base/basictypes.h" #include "base/strings/string16.h" -#include "ui/gfx/size.h" +#include "ui/gfx/geometry/size.h" PrintMsg_Print_Params::PrintMsg_Print_Params() : page_size(), @@ -79,3 +79,14 @@ PrintHostMsg_RequestPrintPreview_Params:: PrintHostMsg_RequestPrintPreview_Params:: ~PrintHostMsg_RequestPrintPreview_Params() {} + +PrintHostMsg_SetOptionsFromDocument_Params:: + PrintHostMsg_SetOptionsFromDocument_Params() + : is_scaling_disabled(false), + copies(0), + duplex(printing::UNKNOWN_DUPLEX_MODE) { +} + +PrintHostMsg_SetOptionsFromDocument_Params:: + ~PrintHostMsg_SetOptionsFromDocument_Params() { +} diff --git a/src/common/print_messages.h b/src/common/print_messages.h index 4e3f99fab5..bffa44c4fa 100644 --- a/src/common/print_messages.h +++ b/src/common/print_messages.h @@ -11,14 +11,16 @@ #include "base/memory/shared_memory.h" #include "base/values.h" #include "ipc/ipc_message_macros.h" +#include "printing/page_range.h" #include "printing/page_size_margins.h" #include "printing/print_job_constants.h" #include "third_party/WebKit/public/web/WebPrintScalingOption.h" +#include "ui/gfx/ipc/gfx_param_traits.h" #include "ui/gfx/native_widget_types.h" -#include "ui/gfx/rect.h" +#include "ui/gfx/geometry/rect.h" -#ifndef NW_COMMON_PRINT_MESSAGES_H_ -#define NW_COMMON_PRINT_MESSAGES_H_ +#ifndef CHROME_COMMON_PRINT_MESSAGES_H_ +#define CHROME_COMMON_PRINT_MESSAGES_H_ struct PrintMsg_Print_Params { PrintMsg_Print_Params(); @@ -70,12 +72,27 @@ struct PrintHostMsg_RequestPrintPreview_Params { bool selection_only; }; +struct PrintHostMsg_SetOptionsFromDocument_Params { + PrintHostMsg_SetOptionsFromDocument_Params(); + ~PrintHostMsg_SetOptionsFromDocument_Params(); + + bool is_scaling_disabled; + int copies; + printing::DuplexMode duplex; + printing::PageRanges page_ranges; +}; + #endif // CHROME_COMMON_PRINT_MESSAGES_H_ #define IPC_MESSAGE_START PrintMsgStart -IPC_ENUM_TRAITS(printing::MarginType) -IPC_ENUM_TRAITS(blink::WebPrintScalingOption) +IPC_ENUM_TRAITS_MAX_VALUE(printing::MarginType, + printing::MARGIN_TYPE_LAST) +IPC_ENUM_TRAITS_MAX_VALUE(blink::WebPrintScalingOption, + blink::WebPrintScalingOptionLast) +IPC_ENUM_TRAITS_MIN_MAX_VALUE(printing::DuplexMode, + printing::UNKNOWN_DUPLEX_MODE, + printing::SHORT_EDGE) // Parameters for a render request. IPC_STRUCT_TRAITS_BEGIN(PrintMsg_Print_Params) @@ -163,6 +180,25 @@ IPC_STRUCT_TRAITS_BEGIN(PrintHostMsg_RequestPrintPreview_Params) IPC_STRUCT_TRAITS_MEMBER(selection_only) IPC_STRUCT_TRAITS_END() +IPC_STRUCT_TRAITS_BEGIN(printing::PageRange) + IPC_STRUCT_TRAITS_MEMBER(from) + IPC_STRUCT_TRAITS_MEMBER(to) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(PrintHostMsg_SetOptionsFromDocument_Params) + // Specifies whether print scaling is enabled or not. + IPC_STRUCT_TRAITS_MEMBER(is_scaling_disabled) + + // Specifies number of copies to be printed. + IPC_STRUCT_TRAITS_MEMBER(copies) + + // Specifies paper handling option. + IPC_STRUCT_TRAITS_MEMBER(duplex) + + // Specifies page range to be printed. + IPC_STRUCT_TRAITS_MEMBER(page_ranges) +IPC_STRUCT_TRAITS_END() + IPC_STRUCT_TRAITS_BEGIN(printing::PageSizeMargins) IPC_STRUCT_TRAITS_MEMBER(content_width) IPC_STRUCT_TRAITS_MEMBER(content_height) @@ -183,10 +219,6 @@ IPC_STRUCT_TRAITS_END() // Parameters to describe a rendered document. IPC_STRUCT_BEGIN(PrintHostMsg_DidPreviewDocument_Params) - // True when we can reuse existing preview data. |metafile_data_handle| and - // |data_size| should not be used when this is true. - IPC_STRUCT_MEMBER(bool, reuse_existing_data) - // A shared memory handle to metafile data. IPC_STRUCT_MEMBER(base::SharedMemoryHandle, metafile_data_handle) @@ -255,9 +287,6 @@ IPC_STRUCT_BEGIN(PrintHostMsg_DidPrintPage_Params) // Page number. IPC_STRUCT_MEMBER(int, page_number) - // Shrink factor used to render this page. - IPC_STRUCT_MEMBER(double, actual_shrink) - // The size of the page the page author specified. IPC_STRUCT_MEMBER(gfx::Size, page_size) @@ -267,10 +296,11 @@ IPC_STRUCT_END() // Parameters for the IPC message ViewHostMsg_ScriptedPrint IPC_STRUCT_BEGIN(PrintHostMsg_ScriptedPrint_Params) - IPC_STRUCT_MEMBER(int, cookie) - IPC_STRUCT_MEMBER(int, expected_pages_count) - IPC_STRUCT_MEMBER(bool, has_selection) - IPC_STRUCT_MEMBER(printing::MarginType, margin_type) +IPC_STRUCT_MEMBER(int, cookie) +IPC_STRUCT_MEMBER(int, expected_pages_count) +IPC_STRUCT_MEMBER(bool, has_selection) +IPC_STRUCT_MEMBER(bool, is_scripted) +IPC_STRUCT_MEMBER(printing::MarginType, margin_type) IPC_STRUCT_END() @@ -279,8 +309,8 @@ IPC_STRUCT_END() // Tells the render view to initiate print preview for the entire document. IPC_MESSAGE_ROUTED1(PrintMsg_InitiatePrintPreview, bool /* selection_only */) -// Tells the render view to initiate printing or print preview for a particular -// node, depending on which mode the render view is in. +// Tells the render frame to initiate printing or print preview for a particular +// node, depending on which mode the render frame is in. IPC_MESSAGE_ROUTED0(PrintMsg_PrintNodeUnderContextMenu) // Tells the renderer to print the print preview tab's PDF plugin without @@ -289,10 +319,15 @@ IPC_MESSAGE_ROUTED0(PrintMsg_PrintNodeUnderContextMenu) IPC_MESSAGE_ROUTED1(PrintMsg_PrintForPrintPreview, base::DictionaryValue /* settings */) +#if defined(ENABLE_BASIC_PRINTING) // Tells the render view to switch the CSS to print media type, renders every // requested pages and switch back the CSS to display media type. IPC_MESSAGE_ROUTED0(PrintMsg_PrintPages) +// Like PrintMsg_PrintPages, but using the print preview document's frame/node. +IPC_MESSAGE_ROUTED0(PrintMsg_PrintForSystemDialog) +#endif // ENABLE_BASIC_PRINTING + // Tells the render view that printing is done so it can clean up. IPC_MESSAGE_ROUTED1(PrintMsg_PrintingDone, bool /* success */) @@ -307,12 +342,6 @@ IPC_MESSAGE_ROUTED1(PrintMsg_SetScriptedPrintingBlocked, IPC_MESSAGE_ROUTED1(PrintMsg_PrintPreview, base::DictionaryValue /* settings */) -// Like PrintMsg_PrintPages, but using the print preview document's frame/node. -IPC_MESSAGE_ROUTED0(PrintMsg_PrintForSystemDialog) - -// Tells a renderer to stop blocking script initiated printing. -IPC_MESSAGE_ROUTED0(PrintMsg_ResetScriptedPrintCount) - // Messages sent from the renderer to the browser. #if defined(OS_WIN) @@ -323,6 +352,10 @@ IPC_SYNC_MESSAGE_ROUTED1_1(PrintHostMsg_DuplicateSection, base::SharedMemoryHandle /* browser handle */) #endif +// Check if printing is enabled. +IPC_SYNC_MESSAGE_ROUTED0_1(PrintHostMsg_IsPrintingEnabled, + bool /* is_enabled */) + // Tells the browser that the renderer is done calculating the number of // rendered pages according to the specified settings. IPC_MESSAGE_ROUTED2(PrintHostMsg_DidGetPrintedPagesCount, @@ -348,10 +381,11 @@ IPC_SYNC_MESSAGE_ROUTED0_1(PrintHostMsg_GetDefaultPrintSettings, // The renderer wants to update the current print settings with new // |job_settings|. -IPC_SYNC_MESSAGE_ROUTED2_1(PrintHostMsg_UpdatePrintSettings, +IPC_SYNC_MESSAGE_ROUTED2_2(PrintHostMsg_UpdatePrintSettings, int /* document_cookie */, base::DictionaryValue /* job_settings */, - PrintMsg_PrintPages_Params /* current_settings */) + PrintMsg_PrintPages_Params /* current_settings */, + bool /* canceled */) // It's the renderer that controls the printing process when it is generated // by javascript. This step is about showing UI to the user to select the @@ -362,17 +396,6 @@ IPC_SYNC_MESSAGE_ROUTED1_1(PrintHostMsg_ScriptedPrint, PrintMsg_PrintPages_Params /* settings chosen by the user*/) -#if defined(USE_X11) -// Asks the browser to create a temporary file for the renderer to fill -// in resulting NativeMetafile in printing. -IPC_SYNC_MESSAGE_CONTROL0_2(PrintHostMsg_AllocateTempFileForPrinting, - base::FileDescriptor /* temp file fd */, - int /* fd in browser*/) -IPC_MESSAGE_CONTROL2(PrintHostMsg_TempFileForPrintingWritten, - int /* render_view_id */, - int /* fd in browser */) -#endif - // Asks the browser to do print preview. IPC_MESSAGE_ROUTED1(PrintHostMsg_RequestPrintPreview, PrintHostMsg_RequestPrintPreview_Params /* params */) @@ -401,6 +424,9 @@ IPC_SYNC_MESSAGE_ROUTED2_1(PrintHostMsg_CheckForCancel, int /* request id */, bool /* print preview cancelled */) +// This is sent when there are invalid printer settings. +IPC_MESSAGE_ROUTED0(PrintHostMsg_ShowInvalidPrinterSettingsError) + // Sends back to the browser the complete rendered document (non-draft mode, // used for printing) that was requested by a PrintMsg_PrintPreview message. // The memory handle in this message is already valid in the browser process. @@ -427,13 +453,13 @@ IPC_MESSAGE_ROUTED1(PrintHostMsg_PrintPreviewInvalidPrinterSettings, // Run a nested message loop in the renderer until print preview for // window.print() finishes. -IPC_SYNC_MESSAGE_ROUTED1_0(PrintHostMsg_ScriptedPrintPreview, - bool /* is_modifiable */) +IPC_SYNC_MESSAGE_ROUTED0_0(PrintHostMsg_SetupScriptedPrintPreview) -// Notify the browser that the PDF in the initiator renderer has disabled print -// scaling option. -IPC_MESSAGE_ROUTED0(PrintHostMsg_PrintPreviewScalingDisabled) +// Tell the browser to show the print preview, when the document is sufficiently +// loaded such that the renderer can determine whether it is modifiable or not. +IPC_MESSAGE_ROUTED1(PrintHostMsg_ShowScriptedPrintPreview, + bool /* is_modifiable */) -// Check if printing is enabled. -IPC_SYNC_MESSAGE_ROUTED0_1(PrintHostMsg_IsPrintingEnabled, - bool /* is_enabled */) +// Notify the browser to set print presets based on source PDF document. +IPC_MESSAGE_ROUTED1(PrintHostMsg_SetOptionsFromDocument, + PrintHostMsg_SetOptionsFromDocument_Params /* params */) diff --git a/src/common/shell_extensions_client.cc b/src/common/shell_extensions_client.cc new file mode 100644 index 0000000000..8701a2b803 --- /dev/null +++ b/src/common/shell_extensions_client.cc @@ -0,0 +1,228 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/nw/src/common/shell_extensions_client.h" + +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "extensions/common/api/generated_schemas.h" +#include "extensions/common/common_manifest_handlers.h" +#include "extensions/common/extension_urls.h" +#include "extensions/common/features/api_feature.h" +#include "extensions/common/features/base_feature_provider.h" +#include "extensions/common/features/behavior_feature.h" +#include "extensions/common/features/json_feature_provider_source.h" +#include "extensions/common/features/manifest_feature.h" +#include "extensions/common/features/permission_feature.h" +#include "extensions/common/features/simple_feature.h" +#include "extensions/common/manifest_handler.h" +#include "extensions/common/permissions/permission_message_provider.h" +#include "extensions/common/permissions/permissions_info.h" +#include "extensions/common/permissions/permissions_provider.h" +#include "extensions/common/url_pattern_set.h" +//#include "extensions/shell/common/api/generated_schemas.h" +//#include "grit/app_shell_resources.h" +#include "grit/extensions_resources.h" + +namespace extensions { + +namespace { + +template +SimpleFeature* CreateFeature() { + return new FeatureClass; +} + +// TODO(jamescook): Refactor ChromePermissionsMessageProvider so we can share +// code. For now, this implementation does nothing. +class ShellPermissionMessageProvider : public PermissionMessageProvider { + public: + ShellPermissionMessageProvider() {} + ~ShellPermissionMessageProvider() override {} + + // PermissionMessageProvider implementation. + PermissionMessages GetPermissionMessages( + const PermissionSet* permissions, + Manifest::Type extension_type) const override { + return PermissionMessages(); + } + + std::vector GetWarningMessages( + const PermissionSet* permissions, + Manifest::Type extension_type) const override { + return std::vector(); + } + + std::vector GetWarningMessagesDetails( + const PermissionSet* permissions, + Manifest::Type extension_type) const override { + return std::vector(); + } + + bool IsPrivilegeIncrease(const PermissionSet* old_permissions, + const PermissionSet* new_permissions, + Manifest::Type extension_type) const override { + // Ensure we implement this before shipping. + CHECK(false); + return false; + } + + private: + DISALLOW_COPY_AND_ASSIGN(ShellPermissionMessageProvider); +}; + +base::LazyInstance + g_permission_message_provider = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +ShellExtensionsClient::ShellExtensionsClient() + : extensions_api_permissions_(ExtensionsAPIPermissions()) { +} + +ShellExtensionsClient::~ShellExtensionsClient() { +} + +void ShellExtensionsClient::Initialize() { + RegisterCommonManifestHandlers(); + ManifestHandler::FinalizeRegistration(); + // TODO(jamescook): Do we need to whitelist any extensions? + + PermissionsInfo::GetInstance()->AddProvider(extensions_api_permissions_); +} + +const PermissionMessageProvider& +ShellExtensionsClient::GetPermissionMessageProvider() const { + NOTIMPLEMENTED(); + return g_permission_message_provider.Get(); +} + +const std::string ShellExtensionsClient::GetProductName() { + return "app_shell"; +} + +scoped_ptr ShellExtensionsClient::CreateFeatureProvider( + const std::string& name) const { + scoped_ptr provider; + scoped_ptr source( + CreateFeatureProviderSource(name)); + if (name == "api") { + provider.reset(new BaseFeatureProvider(source->dictionary(), + CreateFeature)); + } else if (name == "manifest") { + provider.reset(new BaseFeatureProvider(source->dictionary(), + CreateFeature)); + } else if (name == "permission") { + provider.reset(new BaseFeatureProvider(source->dictionary(), + CreateFeature)); + } else if (name == "behavior") { + provider.reset(new BaseFeatureProvider(source->dictionary(), + CreateFeature)); + } else { + NOTREACHED(); + } + return provider.Pass(); +} + +scoped_ptr +ShellExtensionsClient::CreateFeatureProviderSource( + const std::string& name) const { + scoped_ptr source( + new JSONFeatureProviderSource(name)); + if (name == "api") { + source->LoadJSON(IDR_EXTENSION_API_FEATURES); + //source->LoadJSON(IDR_SHELL_EXTENSION_API_FEATURES); + } else if (name == "manifest") { + source->LoadJSON(IDR_EXTENSION_MANIFEST_FEATURES); + } else if (name == "permission") { + source->LoadJSON(IDR_EXTENSION_PERMISSION_FEATURES); + } else if (name == "behavior") { + source->LoadJSON(IDR_EXTENSION_BEHAVIOR_FEATURES); + } else { + NOTREACHED(); + source.reset(); + } + return source.Pass(); +} + +void ShellExtensionsClient::FilterHostPermissions( + const URLPatternSet& hosts, + URLPatternSet* new_hosts, + std::set* messages) const { + NOTIMPLEMENTED(); +} + +void ShellExtensionsClient::FilterHostPermissions( + const URLPatternSet& hosts, + URLPatternSet* new_hosts, + PermissionIDSet* permissions) const { + NOTIMPLEMENTED(); +} + +void ShellExtensionsClient::SetScriptingWhitelist( + const ScriptingWhitelist& whitelist) { + scripting_whitelist_ = whitelist; +} + +const ExtensionsClient::ScriptingWhitelist& +ShellExtensionsClient::GetScriptingWhitelist() const { + // TODO(jamescook): Real whitelist. + return scripting_whitelist_; +} + +URLPatternSet ShellExtensionsClient::GetPermittedChromeSchemeHosts( + const Extension* extension, + const APIPermissionSet& api_permissions) const { + // NOTIMPLEMENTED(); + return URLPatternSet(); +} + +bool ShellExtensionsClient::IsScriptableURL(const GURL& url, + std::string* error) const { + NOTIMPLEMENTED(); + return true; +} + +bool ShellExtensionsClient::IsAPISchemaGenerated( + const std::string& name) const { + return core_api::GeneratedSchemas::IsGenerated(name); // || + // shell::api::GeneratedSchemas::IsGenerated(name); +} + +base::StringPiece ShellExtensionsClient::GetAPISchema( + const std::string& name) const { + // Schema for app_shell-only APIs. + // if (shell::api::GeneratedSchemas::IsGenerated(name)) + // return shell::api::GeneratedSchemas::Get(name); + + // Core extensions APIs. + return core_api::GeneratedSchemas::Get(name); +} + +void ShellExtensionsClient::RegisterAPISchemaResources( + ExtensionAPI* api) const { +} + +bool ShellExtensionsClient::ShouldSuppressFatalErrors() const { + return true; +} + +std::string ShellExtensionsClient::GetWebstoreBaseURL() const { + return extension_urls::kChromeWebstoreBaseURL; +} + +std::string ShellExtensionsClient::GetWebstoreUpdateURL() const { + return extension_urls::kChromeWebstoreUpdateURL; +} + +bool ShellExtensionsClient::IsBlacklistUpdateURL(const GURL& url) const { + return false; +} + +std::set ShellExtensionsClient::GetBrowserImagePaths( + const Extension* extension) { + return std::set(); +} + +} // namespace extensions diff --git a/src/common/shell_extensions_client.h b/src/common/shell_extensions_client.h new file mode 100644 index 0000000000..bb6fd5535d --- /dev/null +++ b/src/common/shell_extensions_client.h @@ -0,0 +1,63 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_SHELL_COMMON_SHELL_EXTENSIONS_CLIENT_H_ +#define EXTENSIONS_SHELL_COMMON_SHELL_EXTENSIONS_CLIENT_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "extensions/common/extensions_client.h" +#include "extensions/common/permissions/extensions_api_permissions.h" + +namespace extensions { + +// The app_shell implementation of ExtensionsClient. +class ShellExtensionsClient : public ExtensionsClient { + public: + ShellExtensionsClient(); + ~ShellExtensionsClient() override; + + // ExtensionsClient overrides: + void Initialize() override; + const PermissionMessageProvider& GetPermissionMessageProvider() + const override; + const std::string GetProductName() override; + scoped_ptr CreateFeatureProvider( + const std::string& name) const override; + scoped_ptr CreateFeatureProviderSource( + const std::string& name) const override; + void FilterHostPermissions( + const URLPatternSet& hosts, + URLPatternSet* new_hosts, + std::set* messages) const override; + void FilterHostPermissions(const URLPatternSet& hosts, + URLPatternSet* new_hosts, + PermissionIDSet* permissions) const override; + void SetScriptingWhitelist(const ScriptingWhitelist& whitelist) override; + const ScriptingWhitelist& GetScriptingWhitelist() const override; + URLPatternSet GetPermittedChromeSchemeHosts( + const Extension* extension, + const APIPermissionSet& api_permissions) const override; + bool IsScriptableURL(const GURL& url, std::string* error) const override; + bool IsAPISchemaGenerated(const std::string& name) const override; + base::StringPiece GetAPISchema(const std::string& name) const override; + void RegisterAPISchemaResources(ExtensionAPI* api) const override; + bool ShouldSuppressFatalErrors() const override; + std::string GetWebstoreBaseURL() const override; + std::string GetWebstoreUpdateURL() const override; + bool IsBlacklistUpdateURL(const GURL& url) const override; + std::set GetBrowserImagePaths( + const Extension* extension) override; + + private: + const ExtensionsAPIPermissions extensions_api_permissions_; + + ScriptingWhitelist scripting_whitelist_; + + DISALLOW_COPY_AND_ASSIGN(ShellExtensionsClient); +}; + +} // namespace extensions + +#endif // EXTENSIONS_SHELL_COMMON_SHELL_EXTENSIONS_CLIENT_H_ diff --git a/src/common/shell_switches.cc b/src/common/shell_switches.cc index 2d6065b224..9ffbdef8e8 100644 --- a/src/common/shell_switches.cc +++ b/src/common/shell_switches.cc @@ -72,6 +72,8 @@ const char kmResizable[] = "resizable"; const char kmAsDesktop[] = "as_desktop"; const char kmFullscreen[] = "fullscreen"; const char kmInitialFocus[] = "focus"; +const char kmTransparent[] = "transparent"; +const char kmDisableTransparency[] = "disable-transparency"; // Make windows icon hide show or hide in taskbar. const char kmShowInTaskbar[] = "show_in_taskbar"; @@ -83,6 +85,9 @@ const char kmKiosk[] = "kiosk"; // Make windows stays on the top of all other windows. const char kmAlwaysOnTop[] = "always-on-top"; +// Make window visible on all workspaces. +const char kmVisibleOnAllWorkspaces[] = "visible-on-all-workspaces"; + // Whether we should support WebGL. const char kmWebgl[] = "webgl"; @@ -110,4 +115,5 @@ const char kmInjectCSS[] = "inject-css"; const char kPrintRaster[] = "print-raster"; #endif +const char kCrashDumpsDir[] = "crash-dumps-dir"; } // namespace switches diff --git a/src/common/shell_switches.h b/src/common/shell_switches.h index 8b145858e1..4e0b532cbc 100644 --- a/src/common/shell_switches.h +++ b/src/common/shell_switches.h @@ -7,6 +7,11 @@ #ifndef CONTENT_NW_SRC_SHELL_SWITCHES_H_ #define CONTENT_NW_SRC_SHELL_SWITCHES_H_ +namespace nw { + const int kMenuHeight = 25; + const int kToolbarHeight = 34; +} + namespace switches { extern const char kContentShellDataPath[]; @@ -48,7 +53,10 @@ extern const char kmFullscreen[]; extern const char kmShowInTaskbar[]; extern const char kmKiosk[]; extern const char kmAlwaysOnTop[]; +extern const char kmVisibleOnAllWorkspaces[]; extern const char kmInitialFocus[]; +extern const char kmTransparent[]; +extern const char kmDisableTransparency[]; extern const char kmWebgl[]; extern const char kmJava[]; @@ -64,6 +72,9 @@ extern const char kmInjectCSS[]; #if defined(OS_WIN) extern const char kPrintRaster[]; #endif + +extern const char kCrashDumpsDir[]; + } // namespace switches #endif // CONTENT_NW_SRC_SHELL_SWITCHES_H_ diff --git a/src/geolocation/shell_access_token_store.h b/src/geolocation/shell_access_token_store.h index 205d89ffc9..7cc5d33fcb 100644 --- a/src/geolocation/shell_access_token_store.h +++ b/src/geolocation/shell_access_token_store.h @@ -17,18 +17,18 @@ class ShellAccessTokenStore : public content::AccessTokenStore { content::ShellBrowserContext* shell_browser_context); private: - virtual ~ShellAccessTokenStore(); + ~ShellAccessTokenStore() final; void GetRequestContextOnUIThread( content::ShellBrowserContext* shell_browser_context); void RespondOnOriginatingThread(const LoadAccessTokensCallbackType& callback); // AccessTokenStore - virtual void LoadAccessTokens( - const LoadAccessTokensCallbackType& callback) OVERRIDE; + void LoadAccessTokens( + const LoadAccessTokensCallbackType& callback) override; - virtual void SaveAccessToken( - const GURL& server_url, const base::string16& access_token) OVERRIDE; + void SaveAccessToken( + const GURL& server_url, const base::string16& access_token) override; content::ShellBrowserContext* shell_browser_context_; net::URLRequestContextGetter* system_request_context_; diff --git a/src/hard_error_handler_win.cc b/src/hard_error_handler_win.cc index cf93adcc3b..0cd46d5461 100644 --- a/src/hard_error_handler_win.cc +++ b/src/hard_error_handler_win.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/breakpad/app/hard_error_handler_win.h" +#include "components/crash/app/hard_error_handler_win.h" #if defined(_WIN32_WINNT_WIN8) && _MSC_VER < 1700 // The Windows 8 SDK defines FACILITY_VISUALCPP in winerror.h, and in diff --git a/src/mac/app-Info.plist b/src/mac/app-Info.plist index 726cee5584..f521c701d6 100644 --- a/src/mac/app-Info.plist +++ b/src/mac/app-Info.plist @@ -11,7 +11,7 @@ CFBundleIconFile nw.icns CFBundleIdentifier - com.intel.nw + io.nwjs.nw CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -19,13 +19,13 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.10.1 + 0.12.3 NSPrincipalClass NSApplication LSMinimumSystemVersion ${MACOSX_DEPLOYMENT_TARGET}.0 LSFileQuarantineEnabled - + NSSupportsAutomaticGraphicsSwitching CFBundleDocumentTypes @@ -34,14 +34,14 @@ CFBundleTypeIconFile nw.icns CFBundleTypeName - node-webkit App + nwjs App CFBundleTypeRole Viewer LSHandlerRank Owner LSItemContentTypes - com.intel.nw.app + io.nwjs.nw.app @@ -65,23 +65,23 @@ com.pkware.zip-archive UTTypeDescription - node-webkit App + nwjs App UTTypeIconFile nw.icns UTTypeIdentifier - com.intel.nw.app + io.nwjs.nw.app UTTypeReferenceURL https://github.com/rogerwang/node-webkit/wiki/How-to-package-and-distribute-your-apps UTTypeTagSpecification com.apple.ostype - node-webkit + nwjs public.filename-extension nw public.mime-type - application/x-node-webkit-app + application/x-nwjs-app diff --git a/src/mac/helper-Info.plist b/src/mac/helper-Info.plist index a9e9ee3b23..258f659c0e 100644 --- a/src/mac/helper-Info.plist +++ b/src/mac/helper-Info.plist @@ -9,7 +9,7 @@ CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier - com.intel.nw.helper + io.nwjs.nw.helper CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -19,7 +19,7 @@ CFBundleSignature ???? LSFileQuarantineEnabled - + LSMinimumSystemVersion ${MACOSX_DEPLOYMENT_TARGET}.0 LSUIElement diff --git a/src/media/media_capture_devices_dispatcher.h b/src/media/media_capture_devices_dispatcher.h index 2e707570a2..9e834961c4 100644 --- a/src/media/media_capture_devices_dispatcher.h +++ b/src/media/media_capture_devices_dispatcher.h @@ -52,7 +52,7 @@ class MediaCaptureDevicesDispatcher : public content::MediaObserver, // Overridden from content::MediaObserver: virtual void OnAudioCaptureDevicesChanged() OVERRIDE; virtual void OnVideoCaptureDevicesChanged() OVERRIDE; - void OnCreatingAudioStream(int render_process_id, + virtual void OnCreatingAudioStream(int render_process_id, int render_view_id) OVERRIDE; // content::NotificationObserver implementation. @@ -87,12 +87,12 @@ class MediaCaptureDevicesDispatcher : public content::MediaObserver, virtual void OnMediaRequestStateChanged( - int render_process_id, - int render_view_id, - int page_request_id, - const GURL& security_origin, - const content::MediaStreamDevice& device, - content::MediaRequestState state) {} + int render_process_id, + int render_frame_id, + int page_request_id, + const GURL& security_origin, + content::MediaStreamType stream_type, + content::MediaRequestState state) OVERRIDE {} // Returns the first available audio or video device, or NULL if no devices // are available. diff --git a/src/media/media_internals.cc b/src/media/media_internals.cc index e8546ea141..22ddf30191 100644 --- a/src/media/media_internals.cc +++ b/src/media/media_internals.cc @@ -100,12 +100,12 @@ void MediaInternals::OnVideoCaptureDevicesChanged() { } void MediaInternals::OnMediaRequestStateChanged( - int render_process_id, - int render_view_id, - int page_request_id, - const GURL& security_origin, - const content::MediaStreamDevice& device, - content::MediaRequestState state) { + int render_process_id, + int render_frame_id, + int page_request_id, + const GURL& security_origin, + content::MediaStreamType stream_type, + content::MediaRequestState state) { } void MediaInternals::OnCreatingAudioStream( diff --git a/src/media/media_internals.h b/src/media/media_internals.h index 74254297b9..cef21cb575 100644 --- a/src/media/media_internals.h +++ b/src/media/media_internals.h @@ -42,27 +42,28 @@ class MediaInternals : public content::MediaObserver { static MediaInternals* GetInstance(); // Overridden from content::MediaObserver: - virtual void OnAudioCaptureDevicesChanged() OVERRIDE; - virtual void OnVideoCaptureDevicesChanged() OVERRIDE; + virtual void OnAudioCaptureDevicesChanged() override; + virtual void OnVideoCaptureDevicesChanged() override; virtual void OnMediaRequestStateChanged( int render_process_id, - int render_view_id, + int render_frame_id, int page_request_id, const GURL& security_origin, - const content::MediaStreamDevice& device, - content::MediaRequestState state) OVERRIDE; + content::MediaStreamType stream_type, + content::MediaRequestState state) override; + virtual void OnCreatingAudioStream(int render_process_id, - int render_view_id) OVERRIDE; + int render_view_id) override; virtual void OnAudioStreamPlaying( int render_process_id, int render_frame_id, int stream_id, - const ReadPowerAndClipCallback& power_read_callback) OVERRIDE {} + const ReadPowerAndClipCallback& power_read_callback) override {} virtual void OnAudioStreamStopped( int render_process_id, int render_frame_id, - int stream_id) OVERRIDE {} + int stream_id) override {} // Methods for observers. // Observers should add themselves on construction and remove themselves // on destruction. diff --git a/src/media/media_stream_devices_controller.cc b/src/media/media_stream_devices_controller.cc index 29942a0300..1522f034df 100644 --- a/src/media/media_stream_devices_controller.cc +++ b/src/media/media_stream_devices_controller.cc @@ -4,13 +4,17 @@ #include "content/nw/src/media/media_stream_devices_controller.h" +#include "base/command_line.h" #include "base/values.h" +#include "chrome/common/chrome_switches.h" #include "content/nw/src/media/media_capture_devices_dispatcher.h" #include "content/nw/src/media/media_internals.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/desktop_media_id.h" #include "content/public/common/media_stream_request.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h" + using content::BrowserThread; namespace { @@ -26,8 +30,8 @@ bool HasAnyAvailableDevice() { return !audio_devices.empty() || !video_devices.empty(); }; -const char kAudioKey[] = "audio"; -const char kVideoKey[] = "video"; +//const char kAudioKey[] = "audio"; +//const char kVideoKey[] = "video"; } // namespace @@ -37,7 +41,7 @@ MediaStreamDevicesController::MediaStreamDevicesController( : request_(request), callback_(callback), - has_audio_(content::IsAudioMediaType(request.audio_type) && + has_audio_(content::IsAudioInputMediaType(request.audio_type) && !IsAudioDeviceBlockedByPolicy()), has_video_(content::IsVideoMediaType(request.video_type) && !IsVideoDeviceBlockedByPolicy()) { @@ -230,10 +234,26 @@ void MediaStreamDevicesController::HandleTapMediaRequest() { content::MEDIA_TAB_AUDIO_CAPTURE, "", "")); } if (request_.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE) { - content::DesktopMediaID media_id = - content::DesktopMediaID::Parse(request_.requested_video_device_id); - devices.push_back(content::MediaStreamDevice( - content::MEDIA_DESKTOP_VIDEO_CAPTURE, media_id.ToString(), "Screen")); + const bool screen_capture_enabled = + CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableUserMediaScreenCapturing); + + if (screen_capture_enabled) { + content::DesktopMediaID media_id; + // If the device id wasn't specified then this is a screen capture request + // (i.e. chooseDesktopMedia() API wasn't used to generate device id). + if (request_.requested_video_device_id.empty()) { + media_id = + content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN, + webrtc::kFullDesktopScreenId); + } else { + media_id = + content::DesktopMediaID::Parse(request_.requested_video_device_id); + } + + devices.push_back(content::MediaStreamDevice( + content::MEDIA_DESKTOP_VIDEO_CAPTURE, media_id.ToString(), "Screen")); + } } callback_.Run(devices, diff --git a/src/net/app_protocol_handler.cc b/src/net/app_protocol_handler.cc index 2b61c4be16..b3060f2b4d 100644 --- a/src/net/app_protocol_handler.cc +++ b/src/net/app_protocol_handler.cc @@ -5,7 +5,7 @@ #include "content/nw/src/net/app_protocol_handler.h" #include "base/base64.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/files/file_path.h" #include "base/format_macros.h" #include "base/logging.h" @@ -13,6 +13,7 @@ #include "base/strings/stringprintf.h" #include "base/threading/sequenced_worker_pool.h" #include "content/public/browser/browser_thread.h" +#include "net/base/filename_util.h" #include "net/base/mime_util.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" @@ -65,12 +66,6 @@ net::HttpResponseHeaders* BuildHttpHeaders( return new net::HttpResponseHeaders(raw_headers); } -void ReadMimeTypeFromFile(const base::FilePath& filename, - std::string* mime_type, - bool* result) { - *result = net::GetMimeTypeFromFile(filename, mime_type); -} - base::Time GetFileLastModifiedTime(const base::FilePath& filename) { if (base::PathExists(filename)) { base::File::Info info; @@ -80,15 +75,6 @@ base::Time GetFileLastModifiedTime(const base::FilePath& filename) { return base::Time(); } -base::Time GetFileCreationTime(const base::FilePath& filename) { - if (base::PathExists(filename)) { - base::File::Info info; - if (base::GetFileInfo(filename, &info)) - return info.creation_time; - } - return base::Time(); -} - void ReadResourceFilePathAndLastModifiedTime( const base::FilePath& file_path, base::Time* last_modified_time) { @@ -116,11 +102,11 @@ class URLRequestNWAppJob : public net::URLRequestFileJob { // base::Time()); } - virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE { + void GetResponseInfo(net::HttpResponseInfo* info) override { *info = response_info_; } - virtual void Start() OVERRIDE { + void Start() override { base::Time* last_modified_time = new base::Time(); bool posted = content::BrowserThread::PostBlockingPoolTaskAndReply( FROM_HERE, @@ -134,7 +120,7 @@ class URLRequestNWAppJob : public net::URLRequestFileJob { } private: - virtual ~URLRequestNWAppJob() {} + ~URLRequestNWAppJob() override {} void OnFilePathAndLastModifiedTimeRead(base::Time* last_modified_time) { response_info_.headers = BuildHttpHeaders( @@ -161,12 +147,12 @@ URLRequestJob* AppProtocolHandler::MaybeCreateJob( URLRequest* request, NetworkDelegate* network_delegate) const { base::FilePath file_path; GURL url(http://github.com/request-%3Eurl()); - url_canon::Replacements replacements; - replacements.SetScheme("file", url_parse::Component(0, 4)); + url::Replacements replacements; + replacements.SetScheme("file", url::Component(0, 4)); replacements.ClearHost(); url = url.ReplaceComponents(replacements); - const bool is_file = FileURLToFilePath(url, &file_path); + const bool is_file = net::FileURLToFilePath(url, &file_path); file_path = root_path_.Append(file_path); // Check file access permissions. diff --git a/src/net/app_protocol_handler.h b/src/net/app_protocol_handler.h index ed8222bee0..76b7ed9006 100644 --- a/src/net/app_protocol_handler.h +++ b/src/net/app_protocol_handler.h @@ -23,9 +23,9 @@ class AppProtocolHandler : public URLRequestJobFactory::ProtocolHandler { public: AppProtocolHandler(const base::FilePath& root); - virtual URLRequestJob* MaybeCreateJob( - URLRequest* request, NetworkDelegate* network_delegate) const OVERRIDE; - virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE; + URLRequestJob* MaybeCreateJob( + URLRequest* request, NetworkDelegate* network_delegate) const override; + bool IsSafeRedirectTarget(const GURL& location) const override; private: base::FilePath root_path_; diff --git a/src/net/clear_on_exit_policy.cc b/src/net/clear_on_exit_policy.cc index 667f47da3e..0457b4ab22 100644 --- a/src/net/clear_on_exit_policy.cc +++ b/src/net/clear_on_exit_policy.cc @@ -27,9 +27,9 @@ bool ClearOnExitPolicy::ShouldClearOriginOnExit(const std::string& domain, return false; std::string scheme = - scheme_is_secure ? content::kHttpsScheme : content::kHttpScheme; + scheme_is_secure ? url::kHttpsScheme : url::kHttpScheme; std::string host = domain[0] == '.' ? domain.substr(1) : domain; - GURL url(http://github.com/scheme%20+%20content::kStandardSchemeSeparator%20+%20host); + GURL url(http://github.com/scheme%20+%20url::kStandardSchemeSeparator%20+%20host); return special_storage_policy_->IsStorageSessionOnly(url); } diff --git a/src/net/resource_request_job.cc b/src/net/resource_request_job.cc index 191a5ffe53..20ce91a4c6 100644 --- a/src/net/resource_request_job.cc +++ b/src/net/resource_request_job.cc @@ -90,7 +90,7 @@ void ResourceRequestJob::DataAvailable(base::RefCountedMemory* bytes) { int bytes_read; if (pending_buf_.get()) { CHECK(pending_buf_->data()); - CompleteRead(pending_buf_, pending_buf_size_, &bytes_read); + CompleteRead(pending_buf_.get(), pending_buf_size_, &bytes_read); pending_buf_ = NULL; NotifyReadComplete(bytes_read); } diff --git a/src/net/resource_request_job.h b/src/net/resource_request_job.h index 5726a52e00..200265fcee 100644 --- a/src/net/resource_request_job.h +++ b/src/net/resource_request_job.h @@ -39,11 +39,11 @@ class ResourceRequestJob : public net::URLRequestJob { int resource_id); // net::URLRequestJob methods. - virtual void Start() OVERRIDE; - virtual bool ReadRawData(net::IOBuffer* dest, int dest_size, int* bytes_read) - OVERRIDE; - virtual bool GetMimeType(std::string* mime_type) const OVERRIDE; - virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE; + void Start() override; + bool ReadRawData(net::IOBuffer* dest, int dest_size, int* bytes_read) + override; + bool GetMimeType(std::string* mime_type) const override; + void GetResponseInfo(net::HttpResponseInfo* info) override; static ResourceRequestJob* Factory(net::URLRequest* request, net::NetworkDelegate* network_delegate); @@ -53,7 +53,7 @@ class ResourceRequestJob : public net::URLRequestJob { void DataAvailable(base::RefCountedMemory* bytes); private: - virtual ~ResourceRequestJob(); + ~ResourceRequestJob() override; // Helper for Start(), to let us start asynchronously. // (This pattern is shared by most net::URLRequestJob implementations.) diff --git a/src/net/shell_network_delegate.cc b/src/net/shell_network_delegate.cc index 3be947bc1c..bb80384f43 100644 --- a/src/net/shell_network_delegate.cc +++ b/src/net/shell_network_delegate.cc @@ -1,52 +1,55 @@ -// 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. +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "content/nw/src/net/shell_network_delegate.h" +#include "extensions/browser/info_map.h" +#include "extensions/browser/api/web_request/web_request_api.h" #include "net/base/net_errors.h" +#include "net/base/static_cookie_policy.h" +#include "net/url_request/url_request.h" namespace content { -ShellNetworkDelegate::ShellNetworkDelegate() { +namespace { +bool g_accept_all_cookies = true; +} + +ShellNetworkDelegate::ShellNetworkDelegate( + void* browser_context, extensions::InfoMap* extension_info_map) { + browser_context_ = browser_context; + extension_info_map_ = extension_info_map; } ShellNetworkDelegate::~ShellNetworkDelegate() { } +void ShellNetworkDelegate::SetAcceptAllCookies(bool accept) { + g_accept_all_cookies = accept; +} + int ShellNetworkDelegate::OnBeforeURLRequest( net::URLRequest* request, const net::CompletionCallback& callback, GURL* new_url) { - return net::OK; + return ExtensionWebRequestEventRouter::GetInstance()->OnBeforeRequest( + browser_context_, extension_info_map_.get(), request, callback, new_url); } int ShellNetworkDelegate::OnBeforeSendHeaders( net::URLRequest* request, const net::CompletionCallback& callback, net::HttpRequestHeaders* headers) { - return net::OK; + return ExtensionWebRequestEventRouter::GetInstance()->OnBeforeSendHeaders( + browser_context_, extension_info_map_.get(), request, callback, headers); } void ShellNetworkDelegate::OnSendHeaders( net::URLRequest* request, const net::HttpRequestHeaders& headers) { + ExtensionWebRequestEventRouter::GetInstance()->OnSendHeaders( + browser_context_, extension_info_map_.get(), request, headers); } int ShellNetworkDelegate::OnHeadersReceived( @@ -55,14 +58,25 @@ int ShellNetworkDelegate::OnHeadersReceived( const net::HttpResponseHeaders* original_response_headers, scoped_refptr* override_response_headers, GURL* allowed_unsafe_redirect_url) { - return net::OK; + return ExtensionWebRequestEventRouter::GetInstance()->OnHeadersReceived( + browser_context_, + extension_info_map_.get(), + request, + callback, + original_response_headers, + override_response_headers, + allowed_unsafe_redirect_url); } void ShellNetworkDelegate::OnBeforeRedirect(net::URLRequest* request, const GURL& new_location) { + ExtensionWebRequestEventRouter::GetInstance()->OnBeforeRedirect( + browser_context_, extension_info_map_.get(), request, new_location); } void ShellNetworkDelegate::OnResponseStarted(net::URLRequest* request) { + ExtensionWebRequestEventRouter::GetInstance()->OnResponseStarted( + browser_context_, extension_info_map_.get(), request); } void ShellNetworkDelegate::OnRawBytesRead(const net::URLRequest& request, @@ -70,9 +84,30 @@ void ShellNetworkDelegate::OnRawBytesRead(const net::URLRequest& request, } void ShellNetworkDelegate::OnCompleted(net::URLRequest* request, bool started) { + if (request->status().status() == net::URLRequestStatus::SUCCESS) { + bool is_redirect = request->response_headers() && + net::HttpResponseHeaders::IsRedirectResponseCode( + request->response_headers()->response_code()); + if (!is_redirect) { + ExtensionWebRequestEventRouter::GetInstance()->OnCompleted( + browser_context_, extension_info_map_.get(), request); + } + return; + } + + if (request->status().status() == net::URLRequestStatus::FAILED || + request->status().status() == net::URLRequestStatus::CANCELED) { + ExtensionWebRequestEventRouter::GetInstance()->OnErrorOccurred( + browser_context_, extension_info_map_.get(), request, started); + return; + } + + NOTREACHED(); } void ShellNetworkDelegate::OnURLRequestDestroyed(net::URLRequest* request) { + ExtensionWebRequestEventRouter::GetInstance()->OnURLRequestDestroyed( + browser_context_, request); } void ShellNetworkDelegate::OnPACScriptError(int line_number, @@ -84,18 +119,32 @@ ShellNetworkDelegate::AuthRequiredResponse ShellNetworkDelegate::OnAuthRequired( const net::AuthChallengeInfo& auth_info, const AuthCallback& callback, net::AuthCredentials* credentials) { - return AUTH_REQUIRED_RESPONSE_NO_ACTION; + return ExtensionWebRequestEventRouter::GetInstance()->OnAuthRequired( + browser_context_, extension_info_map_.get(), request, auth_info, callback, + credentials); } bool ShellNetworkDelegate::OnCanGetCookies(const net::URLRequest& request, const net::CookieList& cookie_list) { - return true; + net::StaticCookiePolicy::Type policy_type = g_accept_all_cookies ? + net::StaticCookiePolicy::ALLOW_ALL_COOKIES : + net::StaticCookiePolicy::BLOCK_ALL_THIRD_PARTY_COOKIES; + net::StaticCookiePolicy policy(policy_type); + int rv = policy.CanGetCookies( + request.url(), request.first_party_for_cookies()); + return rv == net::OK; } bool ShellNetworkDelegate::OnCanSetCookie(const net::URLRequest& request, const std::string& cookie_line, net::CookieOptions* options) { - return true; + net::StaticCookiePolicy::Type policy_type = g_accept_all_cookies ? + net::StaticCookiePolicy::ALLOW_ALL_COOKIES : + net::StaticCookiePolicy::BLOCK_ALL_THIRD_PARTY_COOKIES; + net::StaticCookiePolicy policy(policy_type); + int rv = policy.CanSetCookie( + request.url(), request.first_party_for_cookies()); + return rv == net::OK; } bool ShellNetworkDelegate::OnCanAccessFile(const net::URLRequest& request, @@ -108,10 +157,4 @@ bool ShellNetworkDelegate::OnCanThrottleRequest( return false; } -int ShellNetworkDelegate::OnBeforeSocketStreamConnect( - net::SocketStream* socket, - const net::CompletionCallback& callback) { - return net::OK; -} - } // namespace content diff --git a/src/net/shell_network_delegate.h b/src/net/shell_network_delegate.h index 6f381d72be..61a551d5a0 100644 --- a/src/net/shell_network_delegate.h +++ b/src/net/shell_network_delegate.h @@ -1,85 +1,72 @@ -// 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. +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. -#ifndef CONTENT_NW_SRC_NET_SHELL_NETWORK_DELEGATE_H_ -#define CONTENT_NW_SRC_NET_SHELL_NETWORK_DELEGATE_H_ +#ifndef CONTENT_SHELL_BROWSER_SHELL_NETWORK_DELEGATE_H_ +#define CONTENT_SHELL_BROWSER_SHELL_NETWORK_DELEGATE_H_ #include "base/basictypes.h" #include "base/compiler_specific.h" -#include "base/files/file_path.h" -#include "net/base/network_delegate.h" +#include "extensions/browser/info_map.h" +#include "net/base/network_delegate_impl.h" +namespace extensions { + +class InfoMap; + +} namespace content { -class ShellNetworkDelegate : public net::NetworkDelegate { +class ShellNetworkDelegate : public net::NetworkDelegateImpl { public: - ShellNetworkDelegate(); - virtual ~ShellNetworkDelegate(); + ShellNetworkDelegate(void* browser_context, extensions::InfoMap* extension_info_map); + ~ShellNetworkDelegate() override; + + static void SetAcceptAllCookies(bool accept); private: // net::NetworkDelegate implementation. - virtual int OnBeforeURLRequest(net::URLRequest* request, - const net::CompletionCallback& callback, - GURL* new_url) OVERRIDE; - virtual int OnBeforeSendHeaders(net::URLRequest* request, - const net::CompletionCallback& callback, - net::HttpRequestHeaders* headers) OVERRIDE; - virtual void OnSendHeaders(net::URLRequest* request, - const net::HttpRequestHeaders& headers) OVERRIDE; - virtual int OnHeadersReceived( + int OnBeforeURLRequest(net::URLRequest* request, + const net::CompletionCallback& callback, + GURL* new_url) override; + int OnBeforeSendHeaders(net::URLRequest* request, + const net::CompletionCallback& callback, + net::HttpRequestHeaders* headers) override; + void OnSendHeaders(net::URLRequest* request, + const net::HttpRequestHeaders& headers) override; + int OnHeadersReceived( net::URLRequest* request, const net::CompletionCallback& callback, const net::HttpResponseHeaders* original_response_headers, - scoped_refptr* - override_response_headers, - GURL* allowed_unsafe_redirect_url) OVERRIDE; - virtual void OnBeforeRedirect(net::URLRequest* request, - const GURL& new_location) OVERRIDE; - virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE; - virtual void OnRawBytesRead(const net::URLRequest& request, - int bytes_read) OVERRIDE; - virtual void OnCompleted(net::URLRequest* request, bool started) OVERRIDE; - virtual void OnURLRequestDestroyed(net::URLRequest* request) OVERRIDE; - virtual void OnPACScriptError(int line_number, - const base::string16& error) OVERRIDE; - virtual AuthRequiredResponse OnAuthRequired( + scoped_refptr* override_response_headers, + GURL* allowed_unsafe_redirect_url) override; + void OnBeforeRedirect(net::URLRequest* request, + const GURL& new_location) override; + void OnResponseStarted(net::URLRequest* request) override; + void OnRawBytesRead(const net::URLRequest& request, int bytes_read) override; + void OnCompleted(net::URLRequest* request, bool started) override; + void OnURLRequestDestroyed(net::URLRequest* request) override; + void OnPACScriptError(int line_number, const base::string16& error) override; + AuthRequiredResponse OnAuthRequired( net::URLRequest* request, const net::AuthChallengeInfo& auth_info, const AuthCallback& callback, - net::AuthCredentials* credentials) OVERRIDE; - virtual bool OnCanGetCookies(const net::URLRequest& request, - const net::CookieList& cookie_list) OVERRIDE; - virtual bool OnCanSetCookie(const net::URLRequest& request, - const std::string& cookie_line, - net::CookieOptions* options) OVERRIDE; - virtual bool OnCanAccessFile(const net::URLRequest& request, - const base::FilePath& path) const OVERRIDE; - virtual bool OnCanThrottleRequest( - const net::URLRequest& request) const OVERRIDE; - virtual int OnBeforeSocketStreamConnect( - net::SocketStream* stream, - const net::CompletionCallback& callback) OVERRIDE; + net::AuthCredentials* credentials) override; + bool OnCanGetCookies(const net::URLRequest& request, + const net::CookieList& cookie_list) override; + bool OnCanSetCookie(const net::URLRequest& request, + const std::string& cookie_line, + net::CookieOptions* options) override; + bool OnCanAccessFile(const net::URLRequest& request, + const base::FilePath& path) const override; + bool OnCanThrottleRequest(const net::URLRequest& request) const override; + + void* browser_context_; + scoped_refptr extension_info_map_; DISALLOW_COPY_AND_ASSIGN(ShellNetworkDelegate); }; } // namespace content -#endif // CONTENT_NW_SRC_NET_SHELL_NETWORK_DELEGATE_H_ +#endif // CONTENT_SHELL_BROWSER_SHELL_NETWORK_DELEGATE_H_ diff --git a/src/net/shell_url_request_context_getter.cc b/src/net/shell_url_request_context_getter.cc index 0595186d07..9c9ea85eb4 100644 --- a/src/net/shell_url_request_context_getter.cc +++ b/src/net/shell_url_request_context_getter.cc @@ -36,11 +36,12 @@ #include "content/nw/src/nw_protocol_handler.h" #include "content/nw/src/nw_shell.h" #include "content/nw/src/shell_content_browser_client.h" +#include "extensions/browser/info_map.h" #include "net/cert/cert_verifier.h" -#include "net/ssl/default_server_bound_cert_store.h" +#include "net/cert/cert_verify_proc.h" +#include "net/cert/multi_threaded_cert_verifier.h" #include "net/dns/host_resolver.h" #include "net/dns/mapped_host_resolver.h" -#include "net/ssl/server_bound_cert_service.h" #include "net/ssl/ssl_config_service_defaults.h" #include "net/cookies/cookie_monster.h" #include "net/http/http_auth_filter.h" @@ -53,11 +54,13 @@ #include "net/proxy/proxy_script_fetcher_impl.h" #include "net/proxy/proxy_service.h" #include "net/proxy/proxy_service_v8.h" +#include "net/ssl/channel_id_service.h" +#include "net/ssl/default_channel_id_store.h" #include "net/url_request/file_protocol_handler.h" -#include "net/url_request/protocol_intercept_job_factory.h" #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" @@ -90,18 +93,20 @@ class NWCookieMonsterDelegate : public net::CookieMonster::Delegate { } // net::CookieMonster::Delegate implementation. - virtual void OnCookieChanged( + void OnCookieChanged( const net::CanonicalCookie& cookie, bool removed, - net::CookieMonster::Delegate::ChangeCause cause) OVERRIDE { + net::CookieMonster::Delegate::ChangeCause cause) override { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&NWCookieMonsterDelegate::OnCookieChangedAsyncHelper, this, cookie, removed, cause)); } + void OnLoaded() override { + } private: - virtual ~NWCookieMonsterDelegate() {} + ~NWCookieMonsterDelegate() final {} void OnCookieChangedAsyncHelper( const net::CanonicalCookie& cookie, @@ -128,10 +133,12 @@ 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, - const std::string& gssapi_library_name) + const std::string& gssapi_library_name, + extensions::InfoMap* extension_info_map) : ignore_certificate_errors_(ignore_certificate_errors), data_path_(data_path), root_path_(root_path), @@ -143,7 +150,9 @@ ShellURLRequestContextGetter::ShellURLRequestContextGetter( gssapi_library_name_(gssapi_library_name), io_loop_(io_loop), file_loop_(file_loop), - browser_context_(browser_context) { + 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)); @@ -154,7 +163,7 @@ ShellURLRequestContextGetter::ShellURLRequestContextGetter( // the URLRequestContextStorage on the IO thread in GetURLRequestContext(). proxy_config_service_.reset( net::ProxyService::CreateSystemProxyConfigService( - io_loop_->message_loop_proxy(), file_loop_)); + io_loop_->message_loop_proxy(), file_loop_->message_loop_proxy())); } ShellURLRequestContextGetter::~ShellURLRequestContextGetter() { @@ -169,9 +178,9 @@ net::URLRequestContext* ShellURLRequestContextGetter::GetURLRequestContext() { GetContentClient()->browser()); url_request_context_.reset(new net::URLRequestContext()); - network_delegate_.reset(new ShellNetworkDelegate); - url_request_context_->set_network_delegate(network_delegate_.get()); - storage_.reset( + network_delegate_.reset(new ShellNetworkDelegate(browser_context_, extension_info_map_)); + url_request_context_->set_network_delegate(network_delegate_.get()); + storage_.reset( new net::URLRequestContextStorage(url_request_context_.get())); FilePath cookie_path = data_path_.Append(FILE_PATH_LITERAL("cookies")); @@ -182,13 +191,13 @@ net::URLRequestContext* ShellURLRequestContextGetter::GetURLRequestContext() { NULL, new NWCookieMonsterDelegate(browser_context_)); cookie_store = content::CreateCookieStore(cookie_config); cookie_store->GetCookieMonster()->SetPersistSessionCookies(true); - storage_->set_cookie_store(cookie_store); + 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_server_bound_cert_service(new net::ServerBoundCertService( - new net::DefaultServerBoundCertStore(NULL), + storage_->set_channel_id_service(new net::ChannelIDService( + new net::DefaultChannelIDStore(NULL), base::WorkerPool::GetTaskRunner(true))); std::string accept_lang = browser_client->GetApplicationLocale(); @@ -204,7 +213,14 @@ net::URLRequestContext* ShellURLRequestContextGetter::GetURLRequestContext() { scoped_ptr host_resolver( net::HostResolver::CreateDefaultResolver(NULL)); - storage_->set_cert_verifier(net::CertVerifier::CreateDefault()); + net::CertVerifyProc *verify_proc = net::CertVerifyProc::CreateDefault(); + if (!verify_proc->SupportsAdditionalTrustAnchors()) { + LOG(WARNING) + << "Additional trust anchors not supported on the current platform!"; + } + net::MultiThreadedCertVerifier *verifier = new net::MultiThreadedCertVerifier(verify_proc); + verifier->SetCertTrustAnchorProvider(this); + storage_->set_cert_verifier(verifier); storage_->set_transport_security_state(new net::TransportSecurityState); net::ProxyService* proxy_service; @@ -231,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( @@ -242,8 +258,8 @@ net::URLRequestContext* ShellURLRequestContextGetter::GetURLRequestContext() { url_request_context_->cert_verifier(); network_session_params.transport_security_state = url_request_context_->transport_security_state(); - network_session_params.server_bound_cert_service = - url_request_context_->server_bound_cert_service(); + network_session_params.channel_id_service = + url_request_context_->channel_id_service(); network_session_params.proxy_service = url_request_context_->proxy_service(); network_session_params.ssl_config_service = @@ -270,7 +286,7 @@ net::URLRequestContext* ShellURLRequestContextGetter::GetURLRequestContext() { new net::URLRequestJobFactoryImpl()); InstallProtocolHandlers(job_factory.get(), &protocol_handlers_); job_factory->SetProtocolHandler( - content::kFileScheme, + url::kFileScheme, new net::FileProtocolHandler( content::BrowserThread::GetBlockingPool()-> GetTaskRunnerWithShutdownBehavior( @@ -279,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(); @@ -295,6 +322,17 @@ net::HostResolver* ShellURLRequestContextGetter::host_resolver() { return url_request_context_->host_resolver(); } +void ShellURLRequestContextGetter::SetAdditionalTrustAnchors(const net::CertificateList& trust_anchors) +{ + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + trust_anchors_ = trust_anchors; +} + +const net::CertificateList& ShellURLRequestContextGetter::GetAdditionalTrustAnchors() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + return trust_anchors_; +} + net::HttpAuthHandlerFactory* ShellURLRequestContextGetter::CreateDefaultAuthHandlerFactory( net::HostResolver* resolver) { net::HttpAuthFilterWhitelist* auth_filter_default_credentials = NULL; diff --git a/src/net/shell_url_request_context_getter.h b/src/net/shell_url_request_context_getter.h index 9b92ab13f6..130000da3f 100644 --- a/src/net/shell_url_request_context_getter.h +++ b/src/net/shell_url_request_context_getter.h @@ -26,6 +26,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "content/public/browser/content_browser_client.h" +#include "net/cert/cert_trust_anchor_provider.h" #include "net/url_request/url_request_context_getter.h" #include "net/url_request/url_request_job_factory.h" @@ -43,11 +44,15 @@ namespace base{ class MessageLoop; } +namespace extensions { + class InfoMap; +} + namespace content { class ShellBrowserContext; - class ShellURLRequestContextGetter : public net::URLRequestContextGetter { + class ShellURLRequestContextGetter : public net::URLRequestContextGetter, public net::CertTrustAnchorProvider { public: ShellURLRequestContextGetter( bool ignore_certificate_errors, @@ -57,20 +62,27 @@ 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, - const std::string& gssapi_library_name); + const std::string& gssapi_library_name, + extensions::InfoMap* extension_info_map); // net::URLRequestContextGetter implementation. - virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE; - virtual scoped_refptr - GetNetworkTaskRunner() const OVERRIDE; + net::URLRequestContext* GetURLRequestContext() override; + scoped_refptr + GetNetworkTaskRunner() const override; net::HostResolver* host_resolver(); + void SetAdditionalTrustAnchors(const net::CertificateList& trust_anchors); + + // net::CertTrustAnchorProvider implementation. + const net::CertificateList& GetAdditionalTrustAnchors() override; + protected: - virtual ~ShellURLRequestContextGetter(); + ~ShellURLRequestContextGetter() final; net::HttpAuthHandlerFactory* CreateDefaultAuthHandlerFactory(net::HostResolver* resolver); private: @@ -85,6 +97,7 @@ class ShellBrowserContext; std::string auth_delegate_whitelist_; std::string gssapi_library_name_; // std::vector spdyproxy_auth_origins_; + net::CertificateList trust_anchors_; base::MessageLoop* io_loop_; base::MessageLoop* file_loop_; @@ -96,6 +109,8 @@ 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.cc b/src/nw_notification_manager.cc new file mode 100644 index 0000000000..c5aa4bde43 --- /dev/null +++ b/src/nw_notification_manager.cc @@ -0,0 +1,142 @@ +// Copyright (c) 2014 Jefry Tedjokusumo +// +// 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 "ui/gfx/image/image.h" +#include "content/public/browser/desktop_notification_delegate.h" +#include "content/public/browser/render_process_host.h" +#include "content/common/platform_notification_messages.h" + + +#include "content/nw/src/nw_notification_manager.h" +#if defined(OS_MACOSX) +#include "content/nw/src/nw_notification_manager_mac.h" +#elif defined(OS_WIN) +#include "content/nw/src/nw_notification_manager_win.h" +#include "content/nw/src/nw_notification_manager_toast_win.h" +#elif defined(OS_LINUX) +#include "content/nw/src/nw_notification_manager_linux.h" +#endif + +namespace nw +{ +NotificationManager* NotificationManager::singleton_ = NULL; + +NotificationManager::NotificationManager() { +} + +NotificationManager::~NotificationManager() { + singleton_ = NULL; +} + +NotificationManager* NotificationManager::getSingleton() { + if (singleton_ == NULL) { +#if defined(OS_MACOSX) + singleton_ = new NotificationManagerMac(); +#elif defined(OS_WIN) + if (NotificationManagerToastWin::IsSupported()) + singleton_ = new NotificationManagerToastWin(); + else + singleton_ = new NotificationManagerWin(); +#elif defined(OS_LINUX) + singleton_ = new NotificationManagerLinux(); +#endif + } + return singleton_; +} + +bool NotificationManager::DesktopNotificationPostClick(int render_process_id, int notification_id) { + content::RenderProcessHost* rfh = content::RenderProcessHost::FromID(render_process_id); + if (!rfh) + return false; + + rfh->Send(new PlatformNotificationMsg_DidClick(notification_id)); + return true; +} + +bool NotificationManager::DesktopNotificationPostClose(int render_process_id, int notification_id, bool by_user) { + content::RenderProcessHost* rfh = content::RenderProcessHost::FromID(render_process_id); + if (!rfh) + return false; + + rfh->Send(new PlatformNotificationMsg_DidClose(notification_id)); + return true; +} + +bool NotificationManager::DesktopNotificationPostDisplay(int render_process_id, int notification_id) { + content::RenderProcessHost* rfh = content::RenderProcessHost::FromID(render_process_id); + if (!rfh) + return false; + + rfh->Send(new PlatformNotificationMsg_DidShow(notification_id)); + return true; +} + +bool NotificationManager::DesktopNotificationPostError(int render_process_id, int notification_id, const base::string16& message) { +#if 0 //FIXME + content::RenderProcessHost* rfh = content::RenderProcessHost::FromID(render_process_id); + if (!rfh) + return false; + + // Google remove the error notification messaging !! + rfh->Send(new PlatformNotificationMsg_DidError(rfh->GetRoutingID(), notification_id)); +#endif + return true; +} + +blink::WebNotificationPermission NotificationManager::CheckPermission(ResourceContext* resource_context, + const GURL& origin, + int render_process_id) { + return blink::WebNotificationPermissionAllowed; +} + +void CancelDesktopNotification(int notification_id) { + nw::NotificationManager *notificationManager = nw::NotificationManager::getSingleton(); + if (notificationManager == NULL) { + NOTIMPLEMENTED(); + return; + } + notificationManager->CancelDesktopNotification(notification_id); +} + +void NotificationManager::DisplayNotification(BrowserContext* browser_context, + const GURL& origin, + const SkBitmap& icon, + const PlatformNotificationData& notification_data, + scoped_ptr delegate, + int render_process_id, + base::Closure* cancel_callback) { + if (AddDesktopNotification(notification_data, render_process_id, delegate->notification_id(), icon)) + *cancel_callback = base::Bind(&nw::CancelDesktopNotification, delegate->notification_id()); +} + +void NotificationManager::DisplayPersistentNotification(BrowserContext* browser_context, + int64 service_worker_registration_id, + const GURL& origin, + const SkBitmap& icon, + const PlatformNotificationData& notification_data, + int render_process_id) { + NOTIMPLEMENTED(); +} + +void NotificationManager::ClosePersistentNotification(BrowserContext* browser_context, + const std::string& persistent_notification_id) { + NOTIMPLEMENTED(); +} + +} // namespace nw diff --git a/src/nw_notification_manager.h b/src/nw_notification_manager.h new file mode 100644 index 0000000000..d4afc93427 --- /dev/null +++ b/src/nw_notification_manager.h @@ -0,0 +1,81 @@ +// Copyright (c) 2014 Jefry Tedjokusumo +// +// 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_NOTIFICATION_MANAGER_H_ +#define CONTENT_NW_NOTIFICATION_MANAGER_H_ + +#include "base/basictypes.h" +#include "content/public/browser/platform_notification_service.h" + +namespace nw { + +using namespace content; + +class NotificationManager : public content::PlatformNotificationService{ +private: + static NotificationManager *singleton_; + +protected: + explicit NotificationManager(); + +public: + ~NotificationManager() override; + static NotificationManager* getSingleton(); + + virtual bool AddDesktopNotification(const content::PlatformNotificationData& params, + const int render_process_id, + const int notification_id, + const SkBitmap& icon) = 0; + + virtual bool CancelDesktopNotification(int notification_id) = 0; + + bool DesktopNotificationPostClick(int render_process_id, int notification_id); + bool DesktopNotificationPostClose(int render_process_id, int notification_id, bool by_user); + bool DesktopNotificationPostDisplay(int render_process_id, int notification_id); + bool DesktopNotificationPostError(int render_process_id, int notification_id, const base::string16& message); + + // PlatformNotificationService functions + blink::WebNotificationPermission CheckPermission(ResourceContext* resource_context, + const GURL& origin, + int render_process_id) override; + + void DisplayNotification(BrowserContext* browser_context, + const GURL& origin, + const SkBitmap& icon, + const PlatformNotificationData& notification_data, + scoped_ptr delegate, + int render_process_id, + base::Closure* cancel_callback) override; + + void DisplayPersistentNotification(BrowserContext* browser_context, + int64 service_worker_registration_id, + const GURL& origin, + const SkBitmap& icon, + const PlatformNotificationData& notification_data, + int render_process_id) override; + + void ClosePersistentNotification(BrowserContext* browser_context, + const std::string& persistent_notification_id) override; + + +}; + +} // namespace nw + +#endif // CONTENT_NW_NOTIFICATION_MANAGER_H_ diff --git a/src/nw_notification_manager_linux.cc b/src/nw_notification_manager_linux.cc new file mode 100644 index 0000000000..21860a20d2 --- /dev/null +++ b/src/nw_notification_manager_linux.cc @@ -0,0 +1,148 @@ +// Copyright (c) 2014 Jefry Tedjokusumo +// +// 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 "chrome/browser/status_icons/status_icon.h" +#include "chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h" + +#include "content/public/browser/web_contents.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/common/platform_notification_data.h" +#include "content/nw/src/browser/native_window.h" +#include "content/nw/src/nw_package.h" +#include "content/nw/src/nw_shell.h" + +#include "ui/gfx/image/image.h" +#include "base/strings/utf_string_conversions.h" + +#include "content/nw/src/nw_notification_manager_linux.h" + + +namespace nw { + +NotificationManagerLinux::NotificationManagerLinux() { + notify_init (content::Shell::GetPackage()->GetName().c_str()); + char* info[4]; + if (notify_get_server_info(&info[0], &info[1], &info[2], &info[3])) + //Ubuntu notify-osd can only show 1 notification, this is the "hack" to do that + mForceOneNotification = strcmp(info[0], "notify-osd") == 0; +} + +NotificationManagerLinux::~NotificationManagerLinux() { + notify_uninit(); +} + +NotificationManagerLinux::NotificationMap::iterator NotificationManagerLinux::getNotification(int id) { + if (mForceOneNotification) { + return mNotificationIDmap.begin(); + } + return mNotificationIDmap.find(id); +} + +void NotificationManagerLinux::onClose(NotifyNotification *notif) +{ + NotificationManagerLinux* singleton = static_cast(NotificationManagerLinux::getSingleton()); + NotificationMap::iterator i; + for (i = singleton->mNotificationIDmap.begin(); i!=singleton->mNotificationIDmap.end(); i++) { + if (i->second.mNotification == notif) + break; + } + //int close_reason = notify_notification_get_closed_reason(notif); + //printf("close reason %d\n", close_reason); + singleton->DesktopNotificationPostClose(i->second.mRenderProcessId, i->first, false); + singleton->mNotificationIDmap.erase(i); + g_object_unref(G_OBJECT(notif)); +}; + +bool NotificationManagerLinux::AddDesktopNotification(const content::PlatformNotificationData& params, + const int render_process_id, + const int notification_id, + const SkBitmap& icon) { + content::Shell* shell = content::Shell::windows()[0]; + SkBitmap bitmap; + if(icon.getSize()) { + bitmap = icon; + } else { + bitmap = shell->window()->app_icon().AsBitmap(); + } + + NotifyNotification * notif; + NotificationMap::iterator i = getNotification(notification_id); + if (i==mNotificationIDmap.end()) { +#ifdef NOTIFY_CHECK_VERSION +#if NOTIFY_CHECK_VERSION(0,7,0) + notif = notify_notification_new ( + base::UTF16ToUTF8(params.title).c_str(), base::UTF16ToUTF8(params.body).c_str(), NULL); +#else + notif = notify_notification_new ( + base::UTF16ToUTF8(params.title).c_str(), base::UTF16ToUTF8(params.body).c_str(), NULL, NULL); +#endif +#else + notif = notify_notification_new ( + base::UTF16ToUTF8(params.title).c_str(), base::UTF16ToUTF8(params.body).c_str(), NULL, NULL); +#endif + NotificationData data; + data.mNotification = notif; + data.mRenderProcessId = render_process_id; + mNotificationIDmap[notification_id] = data; + } + else { + notif = i->second.mNotification; + notify_notification_update(notif, base::UTF16ToUTF8(params.title).c_str(), + base::UTF16ToUTF8(params.body).c_str(), NULL); + + // means this is the notify-osd hack + if (i->first != notification_id) { + NotificationData data; + data.mNotification = notif; + g_object_ref(G_OBJECT(notif)); + onClose(notif); + data.mRenderProcessId = render_process_id; + mNotificationIDmap[notification_id] = data; + } + } + + GdkPixbuf* pixbuf = libgtk2ui::GdkPixbufFromSkBitmap(bitmap); + if (pixbuf) { + notify_notification_set_image_from_pixbuf(notif, pixbuf); + g_object_unref(pixbuf); + } + + NOTIFY_NOTIFICATION_GET_CLASS(notif)->closed = onClose; + + GError* error = NULL; + if (notify_notification_show (notif, &error)) { + DesktopNotificationPostDisplay(render_process_id, notification_id); + } + else { + base::string16 errorMsg = base::UTF8ToUTF16(error->message); + DesktopNotificationPostError(render_process_id, notification_id, errorMsg); + } + return error==NULL; +} + +bool NotificationManagerLinux::CancelDesktopNotification(int notification_id) { + NotificationMap::const_iterator i = getNotification(notification_id); + if (i!=mNotificationIDmap.end()) { + return notify_notification_close(i->second.mNotification, NULL); + } + return false; +} + +} // namespace nw diff --git a/src/nw_notification_manager_linux.h b/src/nw_notification_manager_linux.h new file mode 100644 index 0000000000..830afe8546 --- /dev/null +++ b/src/nw_notification_manager_linux.h @@ -0,0 +1,54 @@ +// Copyright (c) 2014 Jefry Tedjokusumo +// +// 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_NOTIFICATION_MANAGER_LINUX_H_ +#define CONTENT_NW_NOTIFICATION_MANAGER_LINUX_H_ + +#include "content/nw/src/nw_notification_manager.h" +#include + +namespace nw { +class NotificationManagerLinux : public NotificationManager { + + struct NotificationData { + NotifyNotification* mNotification; + int mRenderProcessId; + }; + + typedef std::map NotificationMap; + NotificationMap mNotificationIDmap; + + static void onClose(NotifyNotification *notif); + bool mForceOneNotification; + + NotificationMap::iterator getNotification(int id); + +public: + explicit NotificationManagerLinux(); + ~NotificationManagerLinux() override; + bool AddDesktopNotification(const content::PlatformNotificationData& params, + const int render_process_id, + const int notification_id, + const SkBitmap& icon) override; + bool CancelDesktopNotification(int notification_id) override; +}; + +} // namespace nw + +#endif // CONTENT_NW_NOTIFICATION_MANAGER_LINUX_H_ diff --git a/src/nw_notification_manager_mac.h b/src/nw_notification_manager_mac.h new file mode 100644 index 0000000000..fd1c91eb12 --- /dev/null +++ b/src/nw_notification_manager_mac.h @@ -0,0 +1,41 @@ +// Copyright (c) 2014 Jefry Tedjokusumo +// +// 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_NOTIFICATION_MANAGER_MAC_H_ +#define CONTENT_NW_NOTIFICATION_MANAGER_MAC_H_ + +#include "content/nw/src/nw_notification_manager.h" + +namespace nw { + +class NotificationManagerMac : public NotificationManager { +public: + explicit NotificationManagerMac(); + virtual ~NotificationManagerMac(){} + + virtual bool AddDesktopNotification(const content::PlatformNotificationData& params, + const int render_process_id, const int notification_id, const SkBitmap& icon) override; + + virtual bool CancelDesktopNotification(int notification_id) override; + +}; + +} // namespace nw + +#endif // CONTENT_NW_NOTIFICATION_MANAGER_MAC_H_ diff --git a/src/nw_notification_manager_mac.mm b/src/nw_notification_manager_mac.mm new file mode 100644 index 0000000000..726173417f --- /dev/null +++ b/src/nw_notification_manager_mac.mm @@ -0,0 +1,123 @@ +// Copyright (c) 2014 Jefry Tedjokusumo +// +// 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. + +#import +#include "base/mac/mac_util.h" +#include "base/strings/sys_string_conversions.h" + +#include "content/public/browser/web_contents.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/common/platform_notification_data.h" +#include "content/nw/src/browser/native_window.h" +#include "content/nw/src/nw_package.h" +#include "content/nw/src/nw_shell.h" + +#include "content/nw/src/nw_notification_manager_mac.h" + +#if !defined(MAC_OS_X_VERSION_10_8) || \ + MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8 +@interface NSUserNotificationCenter : NSObject +@end +@interface NSUserNotification : NSObject +@end +@implementation NSUserNotification +@end +#endif + +@interface NWUserNotificationCenterDelegate : NSObject { +} +@end +@implementation NWUserNotificationCenterDelegate + +static NWUserNotificationCenterDelegate *singleton_ = nil; + ++(NWUserNotificationCenterDelegate *)defaultNWUserNotificationCenterDelegate{ + @synchronized(self) { + if (singleton_ == nil) + singleton_ = [[self alloc] init]; + } + return singleton_; +} + +-(BOOL)userNotificationCenter:(NSUserNotificationCenter *)center + shouldPresentNotification : (NSUserNotification *)notification { + + NSNumber *render_process_id = [notification.userInfo objectForKey : @"render_process_id"]; + NSNumber *notification_id = [notification.userInfo objectForKey : @"notification_id"]; + + + nw::NotificationManager::getSingleton()->DesktopNotificationPostDisplay(render_process_id.intValue, + notification_id.intValue); + return YES; +} + +-(void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification : (NSUserNotification *)notification { + NSNumber *render_process_id = [notification.userInfo objectForKey : @"render_process_id"]; + NSNumber *notification_id = [notification.userInfo objectForKey : @"notification_id"]; + + + nw::NotificationManager::getSingleton()->DesktopNotificationPostClick(render_process_id.intValue, + notification_id.intValue); +} +@end + +namespace nw { +NotificationManagerMac::NotificationManagerMac() { +} + +bool NotificationManagerMac::AddDesktopNotification(const content::PlatformNotificationData ¶ms, + const int render_process_id, const int notification_id, const SkBitmap& icon) { + + NSUserNotification *notification = [[NSUserNotification alloc] init]; + [notification setTitle : base::SysUTF16ToNSString(params.title)]; + [notification setInformativeText : base::SysUTF16ToNSString(params.body)]; + notification.hasActionButton = YES; + + if (base::mac::IsOSMavericksOrLater() && icon.getSize()) { + gfx::Image image = gfx::Image::CreateFrom1xBitmap(icon); + // this function only runs on Mavericks or later + [notification setContentImage : image.ToNSImage()]; + } + + notification.userInfo = @{ @"render_process_id" :[NSNumber numberWithInt : render_process_id], + @"notification_id" :[NSNumber numberWithInt : notification_id], + }; + + + [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:[NWUserNotificationCenterDelegate defaultNWUserNotificationCenterDelegate]]; + + [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification]; + + [notification release]; + + return true; +} + +bool NotificationManagerMac::CancelDesktopNotification(int notification_id){ + for (NSUserNotification *notification in[[NSUserNotificationCenter defaultUserNotificationCenter] deliveredNotifications]) { + NSNumber *current_notification_id = [notification.userInfo objectForKey : @"notification_id"]; + if (current_notification_id.intValue == notification_id){ + [[NSUserNotificationCenter defaultUserNotificationCenter] removeDeliveredNotification:notification]; + return true; + } + } + return false; +} +} // namespace nw diff --git a/src/nw_notification_manager_toast_win.cc b/src/nw_notification_manager_toast_win.cc new file mode 100644 index 0000000000..683f809397 --- /dev/null +++ b/src/nw_notification_manager_toast_win.cc @@ -0,0 +1,436 @@ +// Copyright (c) 2014 Jefry Tedjokusumo +// +// 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 "config.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/common/platform_notification_data.h" +#include "content/nw/src/browser/native_window.h" +#include "content/nw/src/nw_package.h" +#include "content/nw/src/nw_shell.h" + +#include "ui/gfx/image/image.h" +#include "base/strings/utf_string_conversions.h" +#include "content/nw/src/common/shell_switches.h" +#include "content/nw/src/nw_notification_manager_toast_win.h" +#include "platform/image-encoders/skia/PNGImageEncoder.h" + +#include +#include +#include +#include + +#include +#include +#include + +using namespace Windows::Foundation; + +namespace nw { + +class StringReferenceWrapper { +public: + // Constructor which takes an existing string buffer and its length as the parameters. + // It fills an HSTRING_HEADER struct with the parameter. + // Warning: The caller must ensure the lifetime of the buffer outlives this + // object as it does not make a copy of the wide string memory. + + static bool isSupported() { + static char cachedRes = -1; + if (cachedRes > -1) return cachedRes; + cachedRes = ::LoadLibrary(L"API-MS-WIN-CORE-WINRT-STRING-L1-1-0.DLL") != 0; + return cachedRes; + } + + StringReferenceWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) throw() { + HRESULT hr = WindowsCreateStringReference(stringRef, length, &_header, &_hstring); + if (FAILED(hr)) { + RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); + } + } + + ~StringReferenceWrapper() { + WindowsDeleteString(_hstring); + } + + template + StringReferenceWrapper(_In_reads_(N) wchar_t const (&stringRef)[N]) throw() { + UINT32 length = N - 1; + HRESULT hr = WindowsCreateStringReference(stringRef, length, &_header, &_hstring); + if (FAILED(hr)) { + RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); + } + } + + template + StringReferenceWrapper(_In_reads_(_) wchar_t(&stringRef)[_]) throw() { + UINT32 length; + HRESULT hr = SizeTToUInt32(wcslen(stringRef), &length); + if (FAILED(hr)) { + RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); + } + WindowsCreateStringReference(stringRef, length, &_header, &_hstring); + } + + HSTRING Get() const throw() { + return _hstring; + } + +private: + HSTRING _hstring; + HSTRING_HEADER _header; +}; + +typedef ABI::Windows::Foundation::ITypedEventHandler DesktopToastActivatedEventHandler; +typedef ABI::Windows::Foundation::ITypedEventHandler DesktopToastDismissedEventHandler; +typedef ABI::Windows::Foundation::ITypedEventHandler DesktopToastFailedEventHandler; + +class ToastEventHandler : + public Microsoft::WRL::Implements { +public: + ToastEventHandler::ToastEventHandler(const int render_process_id, const int notification_id, const content::PlatformNotificationData& params, const SkBitmap& icon); + ~ToastEventHandler(); + + // DesktopToastActivatedEventHandler + IFACEMETHODIMP Invoke(_In_ ABI::Windows::UI::Notifications::IToastNotification *sender, _In_ IInspectable* args); + + // DesktopToastDismissedEventHandler + IFACEMETHODIMP Invoke(_In_ ABI::Windows::UI::Notifications::IToastNotification *sender, _In_ ABI::Windows::UI::Notifications::IToastDismissedEventArgs *e); + + // DesktopToastFailedEventHandler + IFACEMETHODIMP Invoke(_In_ ABI::Windows::UI::Notifications::IToastNotification *sender, _In_ ABI::Windows::UI::Notifications::IToastFailedEventArgs *e); + + // IUnknown + IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&_ref); } + + IFACEMETHODIMP_(ULONG) Release() { + ULONG l = InterlockedDecrement(&_ref); + if (l == 0) + delete this; + return l; + } + + IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _COM_Outptr_ void **ppv) { + if (IsEqualIID(riid, IID_IUnknown)) + *ppv = static_cast(static_cast(this)); + else if (IsEqualIID(riid, __uuidof(DesktopToastActivatedEventHandler))) + *ppv = static_cast(this); + else if (IsEqualIID(riid, __uuidof(DesktopToastDismissedEventHandler))) + *ppv = static_cast(this); + else if (IsEqualIID(riid, __uuidof(DesktopToastFailedEventHandler))) + *ppv = static_cast(this); + else *ppv = nullptr; + + if (*ppv) { + reinterpret_cast(*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + +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, 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() { +} + +// DesktopToastActivatedEventHandler +IFACEMETHODIMP ToastEventHandler::Invoke(_In_ IToastNotification* /* sender */, _In_ IInspectable* /* args */) { + BOOL succeeded = nw::NotificationManager::getSingleton()->DesktopNotificationPostClick(_render_process_id, _notification_id); + return succeeded ? S_OK : E_FAIL; +} + +// DesktopToastDismissedEventHandler +IFACEMETHODIMP ToastEventHandler::Invoke(_In_ IToastNotification* /* sender */, _In_ IToastDismissedEventArgs* e) { + ToastDismissalReason tdr; + HRESULT hr = e->get_Reason(&tdr); + if (SUCCEEDED(hr)) { + BOOL succeeded = nw::NotificationManager::getSingleton()->DesktopNotificationPostClose(_render_process_id, _notification_id, tdr == ToastDismissalReason_UserCanceled); + hr = succeeded ? S_OK : E_FAIL; + } + nw::NotificationManager::getSingleton()->CancelDesktopNotification(_notification_id); + return hr; +} + +// DesktopToastFailedEventHandler +IFACEMETHODIMP ToastEventHandler::Invoke(_In_ IToastNotification* /* sender */, _In_ IToastFailedEventArgs* e) { + HRESULT errCode; + e->get_ErrorCode(&errCode); + nw::NotificationManagerToastWin* nmtw = static_cast(nw::NotificationManager::getSingleton()); + std::wstringstream errMsg; errMsg << L"The toast encountered an error code (0x" << std::hex << errCode <<")."; + const bool fallBack = errCode == 0x80070490; + if (fallBack) + errMsg << " Fallback to balloon notification!"; + + BOOL succeeded = nmtw->DesktopNotificationPostError(_render_process_id, _notification_id, errMsg.str().c_str()); + nmtw->notification_map_.erase(_notification_id); + + if (fallBack) { + NotificationManagerToastWin::ForceDisable = true; + delete nmtw; + NotificationManager::getSingleton()->AddDesktopNotification(_params, _render_process_id, _notification_id, _icon); + } + return succeeded ? S_OK : E_FAIL; +} + +// ============= NotificationManagerToastWin Implementation ============= +bool NotificationManagerToastWin::ForceDisable = false; + +HRESULT NotificationManagerToastWin::SetNodeValueString(_In_ HSTRING inputString, _In_ IXmlNode *node, _In_ IXmlDocument *xml) { + ComPtr inputText; + + HRESULT hr = xml->CreateTextNode(inputString, &inputText); + if (SUCCEEDED(hr)) { + ComPtr inputTextNode; + hr = inputText.As(&inputTextNode); + if (SUCCEEDED(hr)) { + ComPtr pAppendedChild; + hr = node->AppendChild(inputTextNode.Get(), &pAppendedChild); + } + } + + return hr; +} + +HRESULT NotificationManagerToastWin::SetTextValues(_In_reads_(textValuesCount) const wchar_t **textValues, _In_ UINT32 textValuesCount, + _In_reads_(textValuesCount) UINT32 *textValuesLengths, _In_ IXmlDocument *toastXml) { + HRESULT hr = textValues != nullptr && textValuesCount > 0 ? S_OK : E_INVALIDARG; + if (SUCCEEDED(hr)) { + ComPtr nodeList; + hr = toastXml->GetElementsByTagName(StringReferenceWrapper(L"text").Get(), &nodeList); + if (SUCCEEDED(hr)) { + UINT32 nodeListLength; + hr = nodeList->get_Length(&nodeListLength); + if (SUCCEEDED(hr)) { + hr = textValuesCount <= nodeListLength ? S_OK : E_INVALIDARG; + if (SUCCEEDED(hr)) { + for (UINT32 i = 0; i < textValuesCount; i++) { + ComPtr textNode; + hr = nodeList->Item(i, &textNode); + if (SUCCEEDED(hr)) { + hr = SetNodeValueString(StringReferenceWrapper(textValues[i], textValuesLengths[i]).Get(), textNode.Get(), toastXml); + } + } + } + } + } + } + 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); + if (SUCCEEDED(hr)) { + ComPtr nodeList; + hr = toastXml->GetElementsByTagName(StringReferenceWrapper(L"image").Get(), &nodeList); + if (SUCCEEDED(hr)) { + ComPtr imageNode; + hr = nodeList->Item(0, &imageNode); + if (SUCCEEDED(hr)) { + ComPtr attributes; + hr = imageNode->get_Attributes(&attributes); + if (SUCCEEDED(hr)) { + ComPtr srcAttribute; + hr = attributes->GetNamedItem(StringReferenceWrapper(L"src").Get(), &srcAttribute); + if (SUCCEEDED(hr)) { + hr = SetNodeValueString(StringReferenceWrapper(imageSrc).Get(), srcAttribute.Get(), toastXml); + } + } + } + } + } + return hr; +} + +HRESULT NotificationManagerToastWin::CreateToastXml(_In_ IToastNotificationManagerStatics *toastManager, + const content::PlatformNotificationData& params, const SkBitmap& icon, _Outptr_ IXmlDocument** inputXml) { + bool bImage = icon.getSize() > 0; + char tempFileName[MAX_PATH]; + + if (params.icon.SchemeIsFile()) { + strcpy_s(tempFileName, params.icon.spec().data()); + } + else if (bImage) { + + char temp[MAX_PATH]; + GetTempPathA(MAX_PATH, tempFileName); + GetTempFileNameA(tempFileName, "NTF", 0, temp); + + Vector encodedImage; + bImage = blink::PNGImageEncoder::encode(icon, reinterpret_cast*>(&encodedImage)); + + FILE *f = fopen(temp, "wb"); + fwrite(encodedImage.data(), sizeof(char), encodedImage.size(), f); + fclose(f); + + sprintf_s(tempFileName, "file:///%s", temp); + } + + // Retrieve the template XML + HRESULT hr = toastManager->GetTemplateContent(bImage ? ToastTemplateType_ToastImageAndText03 : ToastTemplateType_ToastText03, inputXml); + if (SUCCEEDED(hr)) { + hr = bImage ? SetImageSrc(base::UTF8ToWide(tempFileName).c_str(), *inputXml) : S_OK; + if (SUCCEEDED(hr)) { + const wchar_t* textValues[] = { + params.title.c_str(), + params.body.c_str() + }; + 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 content::PlatformNotificationData& params, const SkBitmap& icon) { + ComPtr factory; + HRESULT hr = GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &factory); + if (SUCCEEDED(hr)) { + ComPtr& toast = notification_map_[notification_id]; + hr = factory->CreateToastNotification(xml, &toast); + if (SUCCEEDED(hr)) { + // Register the event handlers + EventRegistrationToken activatedToken, dismissedToken, failedToken; + ComPtr eventHandler = new ToastEventHandler(render_process_id, notification_id, params, icon); + + hr = toast->add_Activated(eventHandler.Get(), &activatedToken); + if (SUCCEEDED(hr)) { + hr = toast->add_Dismissed(eventHandler.Get(), &dismissedToken); + if (SUCCEEDED(hr)) { + hr = toast->add_Failed(eventHandler.Get(), &failedToken); + if (SUCCEEDED(hr)) { + hr = notifier_->Show(toast.Get()); + } + } + } + } + } + return hr; +} + +bool NotificationManagerToastWin::IsSupported() { + static char cachedRes = -1; + if (ForceDisable) return false; + if (cachedRes > -1) return cachedRes; + cachedRes = 0; + + if (StringReferenceWrapper::isSupported()) { + ComPtr toastStatics; + HRESULT hr = GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &toastStatics); + cachedRes = SUCCEEDED(hr); + } + + return cachedRes; +} + +NotificationManagerToastWin::NotificationManagerToastWin() { + Init(); +} + +bool NotificationManagerToastWin::Init() { + if (!content::BrowserThread::CurrentlyOn(BrowserThread::UI)) + return false; + + DCHECK(toastStatics_.Get() == NULL); + + HRESULT hr = GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &toastStatics_); + if (SUCCEEDED(hr)) { + base::string16 appID; + if (content::Shell::GetPackage()->root()->GetString("app-id", &appID) == false) + content::Shell::GetPackage()->root()->GetString(switches::kmName, &appID); + + HRESULT hr = toastStatics_->CreateToastNotifierWithId(StringReferenceWrapper(appID.c_str(), appID.length()).Get(), ¬ifier_); + } + return SUCCEEDED(hr); +} + +NotificationManagerToastWin::~NotificationManagerToastWin() { +} + +bool NotificationManagerToastWin::AddDesktopNotification(const content::PlatformNotificationData& params, + const int render_process_id, const int notification_id, const SkBitmap& icon) { + + if (toastStatics_ == NULL) + if (!Init()) return false; + + ComPtr toastXml; + HRESULT hr = CreateToastXml(toastStatics_.Get(), params, icon, &toastXml); + if (SUCCEEDED(hr)) { + hr = CreateToast(toastStatics_.Get(), toastXml.Get(), render_process_id, notification_id, params, icon); + if (SUCCEEDED(hr)) + DesktopNotificationPostDisplay(render_process_id, notification_id); + } + + return SUCCEEDED(hr); +} + +bool NotificationManagerToastWin::CancelDesktopNotification(int notification_id) { + std::map>::iterator i = notification_map_.find(notification_id); + if (i == notification_map_.end()) + return false; + + ComPtr toast = i->second; + notification_map_.erase(i); + + return SUCCEEDED(notifier_->Hide(toast.Get())); +} +} // namespace nw diff --git a/src/nw_notification_manager_toast_win.h b/src/nw_notification_manager_toast_win.h new file mode 100644 index 0000000000..3aeebeff80 --- /dev/null +++ b/src/nw_notification_manager_toast_win.h @@ -0,0 +1,73 @@ +// Copyright (c) 2014 Jefry Tedjokusumo +// +// 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_NOTIFICATION_MANAGER_TOAST_WIN_H_ +#define CONTENT_NW_NOTIFICATION_MANAGER_TOAST_WIN_H_ + +#include "content/nw/src/nw_notification_manager.h" +#include +#include + + +namespace nw { + using namespace Microsoft::WRL; + using namespace ABI::Windows::UI::Notifications; + using namespace ABI::Windows::Data::Xml::Dom; + +class NotificationManagerToastWin : public NotificationManager { + friend class ToastEventHandler; + + ComPtr toastStatics_; + ComPtr notifier_; + std::map> notification_map_; + static bool ForceDisable; + + HRESULT CreateToast(_In_ IToastNotificationManagerStatics *toastManager, _In_ IXmlDocument *xml, + 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, + const content::PlatformNotificationData& params, const SkBitmap& icon, _Outptr_ IXmlDocument** inputXml); + + // 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); + + HRESULT SetNodeValueString(_In_ HSTRING inputString, _In_ IXmlNode *node, _In_ IXmlDocument *xml); + + bool Init(); + +public: + static bool IsSupported(); + explicit NotificationManagerToastWin(); + virtual ~NotificationManagerToastWin(); + virtual bool AddDesktopNotification(const content::PlatformNotificationData& params, + const int render_process_id, const int notification_id, const SkBitmap& icon) override; + virtual bool CancelDesktopNotification(int notification_id) override; +}; + +} // namespace nw + +#endif // CONTENT_NW_NOTIFICATION_MANAGER_WIN_H_ diff --git a/src/nw_notification_manager_win.cc b/src/nw_notification_manager_win.cc new file mode 100644 index 0000000000..5ad90c2356 --- /dev/null +++ b/src/nw_notification_manager_win.cc @@ -0,0 +1,184 @@ +// Copyright (c) 2014 Jefry Tedjokusumo +// +// 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 "chrome/browser/status_icons/status_icon.h" +#include "chrome/browser/status_icons/status_icon_observer.h" +#include "chrome/browser/ui/views/status_icons/status_tray_win.h" + +#include "content/public/browser/web_contents.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/common/platform_notification_data.h" +#include "content/nw/src/browser/native_window.h" +#include "content/nw/src/nw_package.h" +#include "content/nw/src/nw_shell.h" + +#include "ui/gfx/image/image.h" +#include "base/strings/utf_string_conversions.h" + +#include "content/nw/src/nw_notification_manager_win.h" + +#include + +namespace nw { +class TrayObserver : public StatusIconObserver { +public: + TrayObserver(NotificationManagerWin* tray) + : tray_(tray) { + } + + virtual ~TrayObserver() { + } + + virtual void OnStatusIconClicked() override { + } + + virtual void OnBalloonEvent(int event) override { + switch (event) { + case NIN_BALLOONHIDE: + tray_->DesktopNotificationPostClose(true); + tray_->ReleaseNotification(); + break; + case NIN_BALLOONTIMEOUT: + tray_->DesktopNotificationPostClose(false); + tray_->ReleaseNotification(); + break; + case NIN_BALLOONSHOW: + tray_->DesktopNotificationPostDisplay(); + break; + } + } + + virtual void OnBalloonClicked() override { + tray_->DesktopNotificationPostClick(); + tray_->ReleaseNotification(); + } +private: + NotificationManagerWin* tray_; +}; + +NotificationManagerWin::NotificationManagerWin() : status_icon_(NULL), status_tray_(NULL) { + Init(); +} + +bool NotificationManagerWin::Init() { + if (!content::BrowserThread::CurrentlyOn(BrowserThread::UI)) + return false; + + status_tray_ = static_cast(StatusTray::GetSingleton()); + + // check if status icon is already created + StatusIcon* status_icon = status_tray_->GetStatusIcon(); + + // if status icon already created, set the notification count to 1 and add the observer + notification_count_ = status_icon ? 1 : 0; + if (status_icon) { + status_observer_ = new TrayObserver(this); + status_icon->AddObserver(status_observer_); + } + return true; +} + +bool NotificationManagerWin::ReleaseNotification() { + if (notification_count_ > 0) { + if (notification_count_ == 1) { + // if I create the status_icon_ I am responsible to delete it + if (status_icon_) { + status_icon_->RemoveObserver(status_observer_); + status_tray_->RemoveStatusIcon(status_icon_); + status_icon_ = NULL; + } + + delete status_observer_; + status_observer_ = NULL; + } + notification_count_--; + return true; + } + return false; +} + + +NotificationManagerWin::~NotificationManagerWin() { + ReleaseNotification(); + + // this is to clean up status_observer_ if it is created by the constructor + if (status_observer_) { + StatusIcon* status_icon = status_tray_->GetStatusIcon(); + if (status_icon) + status_icon->RemoveObserver(status_observer_); + + delete status_observer_; + status_observer_ = NULL; + } +} + +bool NotificationManagerWin::AddDesktopNotification(const content::PlatformNotificationData& params, + const int render_process_id, const int notification_id, const SkBitmap& bitmap_icon) { + + if (status_tray_ == NULL) + if(!Init()) return false; + + content::Shell* shell = content::Shell::windows()[0]; + + // if we reach here, it means the function is called from image download callback + render_process_id_ = render_process_id; + notification_id_ = notification_id; + + // set the default notification icon as the app icon + gfx::Image icon = shell->window()->app_icon(); + + // always check if status icon is exist or not + StatusIcon* status_icon = status_tray_->GetStatusIcon(); + + // status_icon_ is null, it means we need to create and adds it to the tray + if (status_icon == NULL) { + nw::Package* package = shell->GetPackage(); + status_icon_ = status_tray_->CreateStatusIcon(StatusTray::NOTIFICATION_TRAY_ICON, + icon.IsEmpty() ? gfx::ImageSkia() : *icon.ToImageSkia(), base::UTF8ToUTF16(package->GetName())); + status_icon = status_icon_; + status_observer_ = new TrayObserver(this); + status_icon->AddObserver(status_observer_); + } + + // add the counter + notification_count_++; + // try to get the notification icon image given by image download callback + if (bitmap_icon.getSize()) + icon = gfx::Image::CreateFrom1xBitmap(bitmap_icon); + + //if body is empty string, the baloon won't shown + base::string16 body = params.body; + if (body.empty()) body = L" "; + + //show the baloon, this only works if iconsize >= 32x32 + bool result = status_icon->DisplayBalloon(icon.Width() < 32 || icon.Height() < 32 ? gfx::ImageSkia() : *icon.ToImageSkia(), params.title, body); + if (!result) { + DesktopNotificationPostError(L"DisplayBalloon fail"); + ReleaseNotification(); + } + + return result; +} + +bool NotificationManagerWin::CancelDesktopNotification(int notification_id) { + //windows can only have 1 notification, cannot delete existing notification + return true; +} +} // namespace nw diff --git a/src/nw_notification_manager_win.h b/src/nw_notification_manager_win.h new file mode 100644 index 0000000000..a067585dfa --- /dev/null +++ b/src/nw_notification_manager_win.h @@ -0,0 +1,74 @@ +// Copyright (c) 2014 Jefry Tedjokusumo +// +// 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_NOTIFICATION_MANAGER_WIN_H_ +#define CONTENT_NW_NOTIFICATION_MANAGER_WIN_H_ + +#include "content/nw/src/nw_notification_manager.h" +class StatusTrayWin; +class StatusIcon; + +namespace nw { +class NotificationManagerWin : public NotificationManager { + // The global presentation of system tray. + StatusTrayWin* status_tray_; + + // StatusIcon pointer created by ME + StatusIcon* status_icon_; + + // number of notification in the queue + int notification_count_; + + // decrement the status_icon_count_, if the value is 0 remove the status_icon_ from the tray + bool ReleaseNotification(); + + // Click observer. + friend class TrayObserver; + TrayObserver* status_observer_; + + // variable to store the latest notification data, windows can only show 1 notification + int render_process_id_, notification_id_; + + bool Init(); + + // dispatch the events from the latest notification + bool DesktopNotificationPostClick() { + return NotificationManager::DesktopNotificationPostClick(render_process_id_, notification_id_); + } + bool DesktopNotificationPostClose(bool by_user) { + return NotificationManager::DesktopNotificationPostClose(render_process_id_, notification_id_, by_user); + } + bool DesktopNotificationPostDisplay() { + return NotificationManager::DesktopNotificationPostDisplay(render_process_id_, notification_id_); + } + bool DesktopNotificationPostError(const base::string16& message) { + return NotificationManager::DesktopNotificationPostError(render_process_id_, notification_id_, message); + } + +public: + explicit NotificationManagerWin(); + virtual ~NotificationManagerWin(); + virtual bool AddDesktopNotification(const content::PlatformNotificationData& params, + const int render_process_id, const int notification_id, const SkBitmap& icon) override; + virtual bool CancelDesktopNotification(int notification_id) override; +}; + +} // namespace nw + +#endif // CONTENT_NW_NOTIFICATION_MANAGER_WIN_H_ diff --git a/src/nw_package.cc b/src/nw_package.cc index 537ce0df7c..aad9c948d7 100644 --- a/src/nw_package.cc +++ b/src/nw_package.cc @@ -23,7 +23,7 @@ #include #include "base/command_line.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/json/json_file_value_serializer.h" #include "base/json/json_string_value_serializer.h" @@ -86,7 +86,7 @@ bool MakePathAbsolute(FilePath* file_path) { } FilePath GetSelfPath() { - CommandLine* command_line = CommandLine::ForCurrentProcess(); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); FilePath path; @@ -120,10 +120,12 @@ void RelativePathToURI(FilePath root, base::DictionaryValue* manifest) { std::string("file://") + main_path.AsUTF8Unsafe()); } +#if defined(OS_WIN) std::wstring ASCIIToWide(const std::string& ascii) { - DCHECK(IsStringASCII(ascii)) << ascii; + DCHECK(base::IsStringASCII(ascii)) << ascii; return std::wstring(ascii.begin(), ascii.end()); } +#endif } // namespace @@ -149,8 +151,8 @@ Package::Package() return; // Then see if we have arguments and extract it. - CommandLine* command_line = CommandLine::ForCurrentProcess(); - const CommandLine::StringVector& args = command_line->GetArgs(); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + const base::CommandLine::StringVector& args = command_line->GetArgs(); if (args.size() > 0) { self_extract_ = false; path_ = FilePath(args[0]); @@ -202,14 +204,14 @@ bool Package::GetImage(const FilePath& icon_path, gfx::Image* image) { if (decoded->empty()) return false; // Unable to decode. - *image = gfx::Image::CreateFrom1xBitmap(*decoded.release()); + *image = gfx::Image::CreateFrom1xBitmap(*decoded); return true; } GURL Package::GetStartupURL() { std::string url; // Specify URL in --url - CommandLine* command_line = CommandLine::ForCurrentProcess(); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kUrl)) { url = command_line->GetSwitchValueASCII(switches::kUrl); GURL g/service/http://github.com/url(url); @@ -243,6 +245,12 @@ bool Package::GetUseNode() { return use_node; } +bool Package::GetUseExtension() { + bool use_ext = true; + root()->GetBoolean(switches::kChromeExtension, &use_ext); + return use_ext; +} + base::DictionaryValue* Package::window() { base::DictionaryValue* window; root()->GetDictionaryWithoutPathExpansion(switches::kmWindow, &window); @@ -295,7 +303,6 @@ bool Package::InitFromPath() { // Check fields const char* required_fields[] = { - switches::kmMain, switches::kmName }; for (unsigned i = 0; i < arraysize(required_fields); i++) @@ -317,7 +324,7 @@ bool Package::InitFromPath() { if (root_->GetString(switches::kAudioBufferSize, &bufsz_str)) { int buffer_size = 0; if (base::StringToInt(bufsz_str, &buffer_size) && buffer_size > 0) { - CommandLine* command_line = CommandLine::ForCurrentProcess(); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); command_line->AppendSwitchASCII(switches::kAudioBufferSize, bufsz_str); } } @@ -340,7 +347,7 @@ void Package::InitWithDefault() { root()->Set(switches::kmWindow, window); // Hide toolbar if specifed in the command line. - if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoToolbar)) + if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoToolbar)) window->SetBoolean(switches::kmToolbar, false); // Window should show in center by default. @@ -418,10 +425,10 @@ void Package::ReadChromiumArgs() { chromium_args.push_back(token); } - CommandLine* command_line = CommandLine::ForCurrentProcess(); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); for (unsigned i = 0; i < chromium_args.size(); ++i) { - CommandLine::StringType key, value; + base::CommandLine::StringType key, value; #if defined(OS_WIN) // Note:: On Windows, the |CommandLine::StringType| will be |std::wstring|, // so the chromium_args[i] is not compatible. We convert the wstring to @@ -446,7 +453,7 @@ void Package::ReadJsFlags() { if (!root()->GetStringASCII(switches::kmJsFlags, &flags)) return; - CommandLine* command_line = CommandLine::ForCurrentProcess(); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); command_line->AppendSwitchASCII("js-flags", flags); } diff --git a/src/nw_package.h b/src/nw_package.h index d82de1b09b..e211572467 100644 --- a/src/nw_package.h +++ b/src/nw_package.h @@ -66,6 +66,8 @@ class Package { // Return if we enable node.js. bool GetUseNode(); + bool GetUseExtension(); + // Root path of package. FilePath path() const { return path_; } diff --git a/src/nw_protocol_handler.h b/src/nw_protocol_handler.h index 60439e3786..674eb133d7 100644 --- a/src/nw_protocol_handler.h +++ b/src/nw_protocol_handler.h @@ -32,14 +32,14 @@ class URLRequestJob; } namespace nw { - + class NwProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler { public: NwProtocolHandler(); - virtual net::URLRequestJob* MaybeCreateJob( + net::URLRequestJob* MaybeCreateJob( net::URLRequest* request, - net::NetworkDelegate* network_delegate) const OVERRIDE; + net::NetworkDelegate* network_delegate) const override; private: DISALLOW_COPY_AND_ASSIGN(NwProtocolHandler); diff --git a/src/nw_shell.cc b/src/nw_shell.cc index ef9a8032d6..fc2cc0c904 100644 --- a/src/nw_shell.cc +++ b/src/nw_shell.cc @@ -21,17 +21,17 @@ #include "content/nw/src/nw_shell.h" #include "base/command_line.h" +#include "base/json/json_reader.h" #include "base/message_loop/message_loop.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "content/browser/child_process_security_policy_impl.h" -#include "content/browser/devtools/devtools_http_handler_impl.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/desktop_media_id.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/devtools_http_handler.h" -#include "content/public/browser/devtools_manager.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/notification_details.h" @@ -50,9 +50,8 @@ #include "content/nw/src/browser/browser_dialogs.h" #include "content/nw/src/browser/file_select_helper.h" #include "content/nw/src/browser/native_window.h" -#include "content/nw/src/browser/shell_devtools_delegate.h" #include "content/nw/src/browser/shell_javascript_dialog_creator.h" -#include "content/nw/src/browser/tab_autofill_manager_delegate.h" +#include "content/nw/src/browser/nw_autofill_client.h" #include "content/nw/src/common/shell_switches.h" #include "content/nw/src/media/media_stream_devices_controller.h" #include "content/nw/src/nw_package.h" @@ -60,27 +59,59 @@ #include "content/nw/src/shell_browser_main_parts.h" #include "content/nw/src/shell_content_browser_client.h" #include "content/nw/src/shell_devtools_frontend.h" - +//#include "content/nw/src/browser/shell_devtools_delegate.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h" #include "grit/nw_resources.h" #include "net/base/escape.h" #include "ui/base/resource/resource_bundle.h" #include "components/autofill/content/browser/content_autofill_driver.h" +#include "components/autofill/content/browser/content_autofill_driver_factory.h" #include "components/autofill/core/browser/autofill_manager.h" #include "components/web_modal/web_contents_modal_dialog_manager.h" +#include "components/app_modal/javascript_dialog_manager.h" -#if defined(OS_WIN) -#include "content/nw/src/browser/native_window_win.h" +#if defined(OS_WIN) || defined(OS_LINUX) +#include "content/nw/src/browser/native_window_aura.h" #include "ui/views/controls/webview/webview.h" -using nw::NativeWindowWin; +using nw::NativeWindowAura; #endif +#include "content/public/browser/media_capture_devices.h" +#include "media/audio/audio_manager_base.h" -#include "content/nw/src/browser/printing/print_view_manager.h" +#include "chrome/browser/printing/print_view_manager_basic.h" +#include "extensions/common/extension_messages.h" using base::MessageLoop; -using content::DevToolsHttpHandlerImpl; +using content::MediaCaptureDevices; +using content::MediaStreamDevice; +using content::MediaStreamDevices; +using content::MediaStreamUI; + +namespace chrome { + bool IsNativeWindowInAsh(gfx::NativeWindow native_window) { + return false; + } +} + +namespace { + +const MediaStreamDevice* GetRequestedDeviceOrDefault( + const MediaStreamDevices& devices, + const std::string& requested_device_id) { + if (!requested_device_id.empty()) + return devices.FindById(requested_device_id); + + if (!devices.empty()) + return &devices[0]; + + return NULL; +} + +} + namespace content { std::vector Shell::windows_; @@ -112,7 +143,7 @@ Shell* Shell::Create(BrowserContext* browser_context, Shell* shell = new Shell(web_contents, GetPackage()->window()); NavigationController::LoadURLParams params(url); - params.transition_type = PageTransitionFromInt(PAGE_TRANSITION_TYPED); + params.transition_type = PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED); params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE; params.frame_name = std::string(); @@ -130,7 +161,7 @@ Shell* Shell::Create(WebContents* source_contents, if (!target_url.is_empty()) { NavigationController::LoadURLParams params(target_url); - params.transition_type = PageTransitionFromInt(PAGE_TRANSITION_TYPED); + params.transition_type = PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED); params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE; params.frame_name = std::string(); @@ -186,6 +217,8 @@ Shell::Shell(WebContents* web_contents, base::DictionaryValue* manifest) enable_nodejs_ = GetPackage()->GetUseNode(); VLOG(1) << "enable nodejs from manifest: " << enable_nodejs_; + extension_function_dispatcher_.reset( + new extensions::ExtensionFunctionDispatcher(web_contents->GetBrowserContext(), this)); // Add web contents. web_contents_.reset(web_contents); content::WebContentsObserver::Observe(web_contents); @@ -195,23 +228,30 @@ Shell::Shell(WebContents* web_contents, base::DictionaryValue* manifest) window_.reset(nw::NativeWindow::Create(weak_ptr_factory_.GetWeakPtr(), manifest)); #if defined(ENABLE_PRINTING) - printing::PrintViewManager::CreateForWebContents(web_contents); + printing::PrintViewManagerBasic::CreateForWebContents(web_contents); #endif // Initialize window after we set window_, because some operations of // NativeWindow requires the window_ to be non-NULL. window_->InitFromManifest(manifest); -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_LINUX) web_modal::WebContentsModalDialogManager::CreateForWebContents(web_contents); web_modal::WebContentsModalDialogManager::FromWebContents(web_contents)->SetDelegate(this); + + popup_manager_.reset( + new web_modal::PopupManager(GetWebContentsModalDialogHost())); + popup_manager_->RegisterWith(web_contents); #endif - autofill::TabAutofillManagerDelegate::CreateForWebContents(web_contents); - autofill::ContentAutofillDriver::CreateForWebContentsAndDelegate( + +#if 1 + autofill::NWAutofillClient::CreateForWebContents(web_contents); + autofill::ContentAutofillDriverFactory::CreateForWebContentsAndDelegate( web_contents, - autofill::TabAutofillManagerDelegate::FromWebContents(web_contents), + autofill::NWAutofillClient::FromWebContents(web_contents), "", autofill::AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER); +#endif //FIXME } Shell::~Shell() { @@ -293,32 +333,7 @@ bool Shell::ShouldCloseWindow(bool quit) { void Shell::PrintCriticalError(const std::string& title, const std::string& content) { - const base::StringPiece template_html( - ResourceBundle::GetSharedInstance().GetRawDataResource( - IDR_NW_FATAL_ERROR)); - - std::string error_page_url; - - if (template_html.empty()) { - // Print hand written error info if nw.pak doesn't exist. - NOTREACHED() << "Unable to load error template."; - error_page_url = "data:text/html;base64,VW5hYmxlIHRvIGZpbmQgbncucGFrLgo="; - } else { - std::string content_with_no_newline, content_with_no_space; - base::ReplaceChars(net::EscapeForHTML(content), - "\n", "
", &content_with_no_newline); - base::ReplaceChars(content_with_no_newline, - " ", " ", &content_with_no_space); - - std::vector subst; - subst.push_back(title); - subst.push_back(content_with_no_space); - error_page_url = "data:text/html;charset=utf-8," + - net::EscapeQueryParamValue( - ReplaceStringPlaceholders(template_html, subst, NULL), false); - } - - LoadURL(GURL(error_page_url)); + LOG(ERROR) << content; } nw::Package* Shell::GetPackage() { @@ -333,21 +348,21 @@ void Shell::LoadURL(const GURL& url) { return; } NavigationController::LoadURLParams params(url); - params.transition_type = PageTransitionFromInt( - PAGE_TRANSITION_TYPED | PAGE_TRANSITION_FROM_ADDRESS_BAR); + params.transition_type = ui::PageTransitionFromInt( + ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR); web_contents_->GetController().LoadURLWithParams(params); // web_contents_->GetController().LoadURL( // url, // Referrer(), // PAGE_TRANSITION_TYPED, // std::string()); - web_contents_->GetView()->Focus(); + web_contents_->Focus(); window()->SetToolbarButtonEnabled(nw::NativeWindow::BUTTON_FORWARD, false); } void Shell::GoBackOrForward(int offset) { web_contents_->GetController().GoToOffset(offset); - web_contents_->GetView()->Focus(); + web_contents_->Focus(); } void Shell::Reload(ReloadType type) { @@ -373,12 +388,12 @@ void Shell::Reload(ReloadType type) { break; } - web_contents_->GetView()->Focus(); + web_contents_->Focus(); } void Shell::Stop() { web_contents_->Stop(); - web_contents_->GetView()->Focus(); + web_contents_->Focus(); } void Shell::ReloadOrStop() { @@ -419,35 +434,35 @@ void Shell::ShowDevTools(const char* jail_id, bool headless) { return; } - RenderViewHost* inspected_rvh = web_contents()->GetRenderViewHost(); + // RenderViewHost* inspected_rvh = web_contents()->GetRenderViewHost(); if (nodejs()) { std::string jscript = std::string("require('nw.gui').Window.get().__setDevToolsJail('") + (jail_id ? jail_id : "(null)") + "');"; - inspected_rvh->ExecuteJavascriptInWebFrame(base::string16(), base::UTF8ToUTF16(jscript.c_str())); + web_contents()->GetMainFrame()->ExecuteJavaScript(base::UTF8ToUTF16(jscript.c_str())); } - scoped_refptr agent(DevToolsAgentHost::GetOrCreateFor(inspected_rvh)); - DevToolsManager* manager = DevToolsManager::GetInstance(); + scoped_refptr agent(DevToolsAgentHost::GetOrCreateFor(web_contents())); if (agent->IsAttached()) { // Break remote debugging debugging session. - manager->CloseAllClientHosts(); + content::DevToolsAgentHost::DetachAllClients(); } - ShellDevToolsDelegate* delegate = - browser_client->shell_browser_main_parts()->devtools_delegate(); - GURL url = delegate->devtools_http_handler()->GetFrontendURL(); + DevToolsHttpHandler* http_handler = + browser_client->shell_browser_main_parts()->devtools_handler(); + GURL url = http_handler->GetFrontendURL("/devtools/devtools.html"); + http_handler->EnumerateTargets(); +#if 0 if (headless) { - DevToolsAgentHost* agent_host = DevToolsAgentHost::GetOrCreateFor(inspected_rvh).get(); - + 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(); SendEvent("devtools-opened", url.spec()); return; } - +#endif SendEvent("devtools-opened", url.spec()); // Use our minimum set manifest base::DictionaryValue manifest; @@ -469,10 +484,10 @@ void Shell::ShowDevTools(const char* jail_id, bool headless) { new ShellDevToolsFrontend( shell, - DevToolsAgentHost::GetOrCreateFor(inspected_rvh).get()); + agent.get()); int rh_id = shell->web_contents_->GetRenderProcessHost()->GetID(); - ChildProcessSecurityPolicyImpl::GetInstance()->GrantScheme(rh_id, content::kFileScheme); + ChildProcessSecurityPolicyImpl::GetInstance()->GrantScheme(rh_id, url::kFileScheme); ChildProcessSecurityPolicyImpl::GetInstance()->GrantScheme(rh_id, "app"); shell->is_devtools_ = true; shell->devtools_owner_ = weak_ptr_factory_.GetWeakPtr(); @@ -496,6 +511,7 @@ bool Shell::OnMessageReceived(const IPC::Message& message) { IPC_BEGIN_MESSAGE_MAP(Shell, message) IPC_MESSAGE_HANDLER(ShellViewHostMsg_UpdateDraggableRegions, UpdateDraggableRegions) + IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; @@ -529,6 +545,12 @@ void Shell::LoadingStateChanged(WebContents* source, bool to_different_document) SendEvent("loaded"); } +void Shell::LoadProgressChanged(content::WebContents* source, double progress) { + std::ostringstream oss; + oss << progress; + SendEvent("progress", oss.str()); +} + void Shell::ActivateContents(content::WebContents* contents) { window()->Focus(true); } @@ -565,11 +587,18 @@ void Shell::WebContentsCreated(WebContents* source_contents, int source_frame_id, const base::string16& frame_name, const GURL& target_url, - WebContents* new_contents) { + WebContents* new_contents, + const base::string16& nw_window_manifest) { // Create with package's manifest scoped_ptr manifest( GetPackage()->window()->DeepCopy()); + scoped_ptr val; + std::string manifest_str = base::UTF16ToUTF8(nw_window_manifest); + val.reset(base::JSONReader().ReadToValue(manifest_str)); + if (val.get() && val->IsType(base::Value::TYPE_DICTIONARY)) + manifest.reset(static_cast(val.release())); + // Get window features blink::WebWindowFeatures features = new_contents->GetWindowFeatures(); manifest->SetBoolean(switches::kmResizable, features.resizable); @@ -593,24 +622,27 @@ void Shell::WebContentsCreated(WebContents* source_contents, new nwapi::DispatcherHost(new_contents->GetRenderViewHost()); #if defined(ENABLE_PRINTING) - printing::PrintViewManager::CreateForWebContents(new_contents); + printing::PrintViewManagerBasic::CreateForWebContents(new_contents); #endif -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_LINUX) web_modal::WebContentsModalDialogManager::CreateForWebContents(new_contents); web_modal::WebContentsModalDialogManager::FromWebContents(new_contents)->SetDelegate(this); #endif + +#if 0 //FIXME autofill::TabAutofillManagerDelegate::CreateForWebContents(new_contents); autofill::ContentAutofillDriver::CreateForWebContentsAndDelegate( new_contents, autofill::TabAutofillManagerDelegate::FromWebContents(new_contents), "", autofill::AutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER); +#endif } -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_LINUX) void Shell::WebContentsFocused(content::WebContents* web_contents) { - NativeWindowWin* win = static_cast(window_.get()); + NativeWindowAura* win = static_cast(window_.get()); if (win) // on aura this function is called in the middle of window creation win->web_view_->OnWebContentsFocused(web_contents); } @@ -637,10 +669,14 @@ void Shell::DidNavigateMainFramePostCommit(WebContents* web_contents) { window()->SetToolbarUrlEntry(web_contents->GetURL().spec()); } -JavaScriptDialogManager* Shell::GetJavaScriptDialogManager() { +JavaScriptDialogManager* Shell::GetJavaScriptDialogManager(WebContents* source) { +#if defined(OS_LINUX) + return app_modal::JavaScriptDialogManager::GetInstance(); +#else if (!dialog_creator_.get()) dialog_creator_.reset(new ShellJavaScriptDialogCreator()); return dialog_creator_.get(); +#endif } bool Shell::AddMessageToConsole(WebContents* source, @@ -666,10 +702,43 @@ void Shell::RequestMediaAccessPermission( WebContents* web_contents, const MediaStreamRequest& request, const MediaResponseCallback& callback) { - scoped_ptr - controller(new MediaStreamDevicesController(request, - callback)); - controller->DismissInfoBarAndTakeActionOnSettings(); + MediaStreamDevices devices; + + if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE) { + const MediaStreamDevice* device = GetRequestedDeviceOrDefault( + MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices(), + request.requested_audio_device_id); + if (device) + devices.push_back(*device); + } + + if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) { + const MediaStreamDevice* device = GetRequestedDeviceOrDefault( + MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices(), + request.requested_video_device_id); + if (device) + devices.push_back(*device); + } + + if (request.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE && + !request.requested_video_device_id.empty()) { + content::DesktopMediaID media_id = + content::DesktopMediaID::Parse(request.requested_video_device_id); + + 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, + devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE + : content::MEDIA_DEVICE_OK, + ui.Pass()); } void Shell::Observe(int type, @@ -684,14 +753,19 @@ void Shell::Observe(int type, window()->SetTitle(base::UTF16ToUTF8(text)); } } else if (type == NOTIFICATION_RENDERER_PROCESS_CLOSED) { - exit_code_ = - content::Details( - details)->exit_code; + content::RenderProcessHost::RendererClosedDetails* process_details = + content::Details(details).ptr(); + content::RenderProcessHost* host = + content::Source(source).ptr(); + exit_code_ = process_details->exit_code; #if defined(OS_POSIX) if (WIFEXITED(exit_code_)) exit_code_ = WEXITSTATUS(exit_code_); #endif - MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure()); + if (host->GetHandle() == web_contents_->GetRenderProcessHost()->GetHandle()) { + set_force_close(true); + window()->Close(); + } } } @@ -704,9 +778,10 @@ GURL Shell::OverrideDOMStorageOrigin(const GURL& origin) { void Shell::RenderViewCreated(RenderViewHost* render_view_host) { //FIXME: handle removal new nwapi::DispatcherHost(render_view_host); + window()->SetTransparent(window()->IsTransparent()); } -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_LINUX) bool Shell::IsWebContentsVisible(content::WebContents* web_contents) { //FIXME return true; @@ -722,4 +797,37 @@ bool Shell::IsFullscreenForTabOrPending(const WebContents* web_contents) const { return window()->IsFullscreen(); } +#if defined(OS_WIN) || defined(OS_LINUX) +web_modal::WebContentsModalDialogHost* Shell::GetWebContentsModalDialogHost() { + return (web_modal::WebContentsModalDialogHost*)window(); +} +#endif + +void Shell::Cleanup() { + std::vector list = windows(); + for (size_t i = 0; i < list.size(); ++i) { + delete list[i]; + } +} + +extensions::WindowController* Shell::GetExtensionWindowController() const { + return NULL; +} + +content::WebContents* Shell::GetAssociatedWebContents() const { + return web_contents_.get(); +} + +void Shell::OnRequest( + const ExtensionHostMsg_Request_Params& params) { + extension_function_dispatcher_->Dispatch( + params, web_contents_->GetRenderViewHost()); +} + +bool Shell::CheckMediaAccessPermission(WebContents* web_contents, + const GURL& security_origin, + MediaStreamType type) { + return true; +} + } // namespace content diff --git a/src/nw_shell.h b/src/nw_shell.h index be01017f46..120b9e5837 100644 --- a/src/nw_shell.h +++ b/src/nw_shell.h @@ -30,11 +30,14 @@ #include "content/public/browser/notification_observer.h" #include "content/public/browser/web_contents_delegate.h" #include "content/public/browser/web_contents_observer.h" -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_LINUX) +#include "components/web_modal/popup_manager.h" #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h" #endif #include "ipc/ipc_channel.h" +#include "extensions/browser/extension_function_dispatcher.h" + namespace base { class DictionaryValue; class FilePath; @@ -42,6 +45,7 @@ class FilePath; namespace extensions { struct DraggableRegion; +class ExtensionFunctionDispatcher; } class GURL; @@ -64,10 +68,11 @@ using base::FilePath; // This represents one window of the Content Shell, i.e. all the UI including // buttons and url bar, as well as the web content area. class Shell : public WebContentsDelegate, -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_LINUX) public web_modal::WebContentsModalDialogManagerDelegate, #endif public content::WebContentsObserver, + public extensions::ExtensionFunctionDispatcher::Delegate, public NotificationObserver { public: enum ReloadType { @@ -80,7 +85,7 @@ class Shell : public WebContentsDelegate, }; explicit Shell(WebContents* web_contents, base::DictionaryValue* manifest); - virtual ~Shell(); + ~Shell() final; // Create a new shell. static Shell* Create(BrowserContext* browser_context, @@ -97,6 +102,7 @@ class Shell : public WebContentsDelegate, // Returns the Shell object corresponding to the given RenderViewHost. static Shell* FromRenderViewHost(RenderViewHost* rvh); + static void Cleanup(); void LoadURL(const GURL& url); void GoBackOrForward(int offset); @@ -139,80 +145,96 @@ class Shell : public WebContentsDelegate, void set_id(int id) { id_ = id; } int id() const { return id_; } - virtual void RenderViewCreated(RenderViewHost* render_view_host) OVERRIDE; -#if defined(OS_WIN) - virtual void SetWebContentsBlocked(content::WebContents* web_contents, bool) OVERRIDE {} - virtual bool IsWebContentsVisible(content::WebContents* web_contents); - virtual web_modal::WebContentsModalDialogHost* GetWebContentsModalDialogHost() OVERRIDE{ return (web_modal::WebContentsModalDialogHost*)window(); } + void RenderViewCreated(RenderViewHost* render_view_host) override; +#if defined(OS_WIN) || defined(OS_LINUX) + void SetWebContentsBlocked(content::WebContents* web_contents, bool) override {} + bool IsWebContentsVisible(content::WebContents* web_contents) override; + web_modal::WebContentsModalDialogHost* GetWebContentsModalDialogHost() override; #endif - + bool CheckMediaAccessPermission(WebContents* web_contents, + const GURL& security_origin, + MediaStreamType type) override; protected: // content::WebContentsObserver implementation. - virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + bool OnMessageReceived(const IPC::Message& message) override; // content::WebContentsDelegate implementation. - virtual WebContents* OpenURLFromTab(WebContents* source, - const OpenURLParams& params) OVERRIDE; - virtual void LoadingStateChanged(WebContents* source, - bool to_different_document) OVERRIDE; - virtual void ActivateContents(content::WebContents* contents) OVERRIDE; - virtual void DeactivateContents(content::WebContents* contents) OVERRIDE; - virtual void CloseContents(WebContents* source) OVERRIDE; - virtual void MoveContents(WebContents* source, const gfx::Rect& pos) OVERRIDE; - virtual bool IsPopupOrPanel(const WebContents* source) const OVERRIDE; - virtual void WebContentsCreated(WebContents* source_contents, + WebContents* OpenURLFromTab(WebContents* source, + const OpenURLParams& params) override; + void LoadingStateChanged(WebContents* source, + bool to_different_document) override; + void LoadProgressChanged(content::WebContents* source, + double progress) override; + void ActivateContents(content::WebContents* contents) override; + void DeactivateContents(content::WebContents* contents) override; + void CloseContents(WebContents* source) override; + void MoveContents(WebContents* source, const gfx::Rect& pos) override; + bool IsPopupOrPanel(const WebContents* source) const override; + void WebContentsCreated(WebContents* source_contents, int source_frame_id, const base::string16& frame_name, const GURL& target_url, - WebContents* new_contents) OVERRIDE; - virtual void ToggleFullscreenModeForTab(WebContents* web_contents, - bool enter_fullscreen) OVERRIDE; - virtual bool IsFullscreenForTabOrPending( - const WebContents* web_contents) const OVERRIDE; -#if defined(OS_WIN) - virtual void WebContentsFocused(WebContents* contents) OVERRIDE; + WebContents* new_contents, + const base::string16& nw_window_manifest) override; + void ToggleFullscreenModeForTab(WebContents* web_contents, + bool enter_fullscreen) override; + bool IsFullscreenForTabOrPending( + const WebContents* web_contents) const override; +#if defined(OS_WIN) || defined(OS_LINUX) + void WebContentsFocused(WebContents* contents) override; #endif - virtual content::ColorChooser* OpenColorChooser( + content::ColorChooser* OpenColorChooser( content::WebContents* web_contents, SkColor color, - const std::vector& suggestions) OVERRIDE; - virtual void RunFileChooser( + const std::vector& suggestions) override; + void RunFileChooser( content::WebContents* web_contents, - const content::FileChooserParams& params) OVERRIDE; - virtual void EnumerateDirectory(content::WebContents* web_contents, + const content::FileChooserParams& params) override; + void EnumerateDirectory(content::WebContents* web_contents, int request_id, - const FilePath& path) OVERRIDE; - virtual void DidNavigateMainFramePostCommit( - WebContents* web_contents) OVERRIDE; - virtual JavaScriptDialogManager* GetJavaScriptDialogManager() OVERRIDE; - virtual void RequestToLockMouse(WebContents* web_contents, + const FilePath& path) override; + void DidNavigateMainFramePostCommit( + WebContents* web_contents) override; + JavaScriptDialogManager* GetJavaScriptDialogManager(WebContents* source) override; + void RequestToLockMouse(WebContents* web_contents, bool user_gesture, - bool last_unlocked_by_target) OVERRIDE; - virtual void HandleKeyboardEvent( + bool last_unlocked_by_target) override; + void HandleKeyboardEvent( WebContents* source, - const NativeWebKeyboardEvent& event) OVERRIDE; - virtual bool AddMessageToConsole(WebContents* source, + const NativeWebKeyboardEvent& event) override; + bool AddMessageToConsole(WebContents* source, int32 level, const base::string16& message, int32 line_no, - const base::string16& source_id) OVERRIDE; - virtual void RequestMediaAccessPermission( + const base::string16& source_id) override; + void RequestMediaAccessPermission( WebContents* web_contents, const MediaStreamRequest& request, - const MediaResponseCallback& callback) OVERRIDE; + const MediaResponseCallback& callback) override; private: + // ExtensionFunctionDispatcher::Delegate + extensions::WindowController* GetExtensionWindowController() const override; + content::WebContents* GetAssociatedWebContents() const override; + + void OnRequest(const ExtensionHostMsg_Request_Params& params); + void UpdateDraggableRegions( const std::vector& regions); // NotificationObserver - virtual void Observe(int type, + void Observe(int type, const NotificationSource& source, - const NotificationDetails& details) OVERRIDE; + const NotificationDetails& details) override; scoped_ptr dialog_creator_; scoped_ptr web_contents_; scoped_ptr window_; + scoped_ptr extension_function_dispatcher_; + +#if defined(OS_WIN) || defined(OS_LINUX) + scoped_ptr popup_manager_; +#endif // Notification manager. NotificationRegistrar registrar_; diff --git a/src/nw_version.h b/src/nw_version.h index a09902adf8..b7afd76345 100644 --- a/src/nw_version.h +++ b/src/nw_version.h @@ -22,9 +22,9 @@ #define NW_VERSION_H #define NW_MAJOR_VERSION 0 -#define NW_MINOR_VERSION 10 -#define NW_PATCH_VERSION 1 -#define NW_VERSION_IS_RELEASE 0 +#define NW_MINOR_VERSION 12 +#define NW_PATCH_VERSION 3 +#define NW_VERSION_IS_RELEASE 1 #ifndef NW_STRINGIFY #define NW_STRINGIFY(n) NW_STRINGIFY_HELPER(n) @@ -38,12 +38,12 @@ #else # define NW_VERSION_STRING NW_STRINGIFY(NW_MAJOR_VERSION) "." \ NW_STRINGIFY(NW_MINOR_VERSION) "." \ - NW_STRINGIFY(NW_PATCH_VERSION) "-pre" + NW_STRINGIFY(NW_PATCH_VERSION) "-rc2" #endif #define NW_VERSION "v" NW_VERSION_STRING -#define CHROME_VERSION "35.0.1916.113" +#define CHROME_VERSION "41.0.2272.76" #define NW_VERSION_AT_LEAST(major, minor, patch) \ (( (major) < NW_MAJOR_VERSION) \ diff --git a/src/paths_mac.mm b/src/paths_mac.mm index d099c8aa74..354e8a36a2 100644 --- a/src/paths_mac.mm +++ b/src/paths_mac.mm @@ -51,16 +51,16 @@ FilePath GetFrameworksPath() { void OverrideFrameworkBundlePath() { FilePath helper_path = - GetFrameworksPath().Append("node-webkit Framework.framework"); + GetFrameworksPath().Append("nwjs Framework.framework"); base::mac::SetOverrideFrameworkBundlePath(helper_path); } void OverrideChildProcessPath() { - FilePath helper_path = GetFrameworksPath().Append("node-webkit Helper.app") + FilePath helper_path = GetFrameworksPath().Append("nwjs Helper.app") .Append("Contents") .Append("MacOS") - .Append("node-webkit Helper"); + .Append("nwjs Helper"); PathService::Override(content::CHILD_PROCESS_EXE, helper_path); } diff --git a/src/renderer/common/render_messages.h b/src/renderer/common/render_messages.h index 7a1cfc2415..3b9556160e 100644 --- a/src/renderer/common/render_messages.h +++ b/src/renderer/common/render_messages.h @@ -19,7 +19,11 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "content/public/common/common_param_traits.h" +#include "ipc/ipc_message.h" +#include "ipc/ipc_message_macros.h" +#include "ipc/ipc_param_traits.h" #include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/ipc/gfx_param_traits.h" // Singly-included section #ifndef CONTENT_NW_SRC_RENDERER_COMMON_RENDER_MESSAGES_H_ @@ -38,3 +42,25 @@ IPC_MESSAGE_ROUTED0(NwViewMsg_CaptureSnapshot) // Send a snapshot of the tab contents to the render host. IPC_MESSAGE_ROUTED1(NwViewHostMsg_Snapshot, SkBitmap /* bitmap */) + +// Brings up SaveAs... dialog to save specified URL. +IPC_MESSAGE_ROUTED2(ChromeViewHostMsg_PDFSaveURLAs, + GURL /* url */, + content::Referrer /* referrer */) + +// Updates the content restrictions, i.e. to disable print/copy. +IPC_MESSAGE_ROUTED1(ChromeViewHostMsg_PDFUpdateContentRestrictions, + int /* restrictions */) + +// Brings up a Password... dialog for protected documents. +IPC_SYNC_MESSAGE_ROUTED1_1(ChromeViewHostMsg_PDFModalPromptForPassword, + std::string /* prompt */, + std::string /* actual_value */) + +#if defined(ENABLE_PLUGINS) +// Sent by the renderer to check if crash reporting is enabled. +IPC_SYNC_MESSAGE_CONTROL0_1(ChromeViewHostMsg_IsCrashReportingEnabled, + bool /* enabled */) +#endif + + diff --git a/src/renderer/nw_render_view_observer.cc b/src/renderer/nw_render_view_observer.cc index 6e6b2d059a..50c0b66877 100644 --- a/src/renderer/nw_render_view_observer.cc +++ b/src/renderer/nw_render_view_observer.cc @@ -22,7 +22,7 @@ #include -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/strings/utf_string_conversions.h" #include "content/nw/src/renderer/common/render_messages.h" #include "content/public/renderer/render_view.h" @@ -30,7 +30,7 @@ #include "skia/ext/platform_canvas.h" #include "third_party/WebKit/public/platform/WebRect.h" #include "third_party/WebKit/public/platform/WebSize.h" -#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebScriptSource.h" #include "third_party/WebKit/public/web/WebView.h" @@ -79,7 +79,7 @@ void NwRenderViewObserver::OnCaptureSnapshot() { Send(new NwViewHostMsg_Snapshot(routing_id(), snapshot)); } -void NwRenderViewObserver::DidFinishDocumentLoad(blink::WebFrame* frame) { +void NwRenderViewObserver::DidFinishDocumentLoad(blink::WebLocalFrame* frame) { RenderViewImpl* rv = RenderViewImpl::FromWebView(frame->view()); if (!rv) return; @@ -87,7 +87,7 @@ void NwRenderViewObserver::DidFinishDocumentLoad(blink::WebFrame* frame) { OnDocumentCallback(rv, js_fn, frame); } -void NwRenderViewObserver::DidCreateDocumentElement(blink::WebFrame* frame) { +void NwRenderViewObserver::DidCreateDocumentElement(blink::WebLocalFrame* frame) { RenderViewImpl* rv = RenderViewImpl::FromWebView(frame->view()); if (!rv) return; @@ -131,7 +131,7 @@ bool NwRenderViewObserver::CaptureSnapshot(blink::WebView* view, SkBaseDevice* device = skia::GetTopDevice(*canvas); const SkBitmap& bitmap = device->accessBitmap(false); - if (!bitmap.copyTo(snapshot, SkBitmapConfigToColorType(SkBitmap::kARGB_8888_Config))) + if (!bitmap.copyTo(snapshot, kN32_SkColorType)) return false; return true; diff --git a/src/renderer/nw_render_view_observer.h b/src/renderer/nw_render_view_observer.h index 0449c1db08..157b726b41 100644 --- a/src/renderer/nw_render_view_observer.h +++ b/src/renderer/nw_render_view_observer.h @@ -23,6 +23,8 @@ #include "content/public/renderer/render_view_observer.h" +#include + class SkBitmap; namespace content { @@ -38,12 +40,12 @@ namespace nw { class NwRenderViewObserver : public content::RenderViewObserver { public: NwRenderViewObserver(content::RenderView* render_view); - virtual ~NwRenderViewObserver(); + ~NwRenderViewObserver() final; // RenderViewObserver implementation. - virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; - virtual void DidCreateDocumentElement(blink::WebFrame* frame) OVERRIDE; - virtual void DidFinishDocumentLoad(blink::WebFrame* frame) OVERRIDE; + bool OnMessageReceived(const IPC::Message& message) override; + void DidCreateDocumentElement(blink::WebLocalFrame* frame) override; + void DidFinishDocumentLoad(blink::WebLocalFrame* frame) override; private: diff --git a/src/renderer/pepper_uma_host.cc b/src/renderer/pepper_uma_host.cc new file mode 100644 index 0000000000..b07add1447 --- /dev/null +++ b/src/renderer/pepper_uma_host.cc @@ -0,0 +1,208 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/renderer/pepper/pepper_uma_host.h" + +#include "base/metrics/histogram.h" +#include "base/sha1.h" +#include "base/strings/string_number_conversions.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/render_messages.h" +#include "chrome/renderer/chrome_content_renderer_client.h" +#include "content/public/renderer/pepper_plugin_instance.h" +#include "content/public/renderer/render_thread.h" +#include "content/public/renderer/renderer_ppapi_host.h" +#include "extensions/common/constants.h" +#include "extensions/common/extension.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/host/dispatch_host_message.h" +#include "ppapi/host/host_message_context.h" +#include "ppapi/host/ppapi_host.h" +#include "ppapi/proxy/ppapi_messages.h" + +#include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR. + +namespace { + +const char* const kPredefinedAllowedUMAOrigins[] = { + "6EAED1924DB611B6EEF2A664BD077BE7EAD33B8F", // see http://crbug.com/317833 + "4EB74897CB187C7633357C2FE832E0AD6A44883A" // see http://crbug.com/317833 +}; + +const char* const kWhitelistedHistogramPrefixes[] = { + "22F67DA2061FFC4DC9A4974036348D9C38C22919" // see http://crbug.com/390221 +}; + +const char* const kWhitelistedPluginBaseNames[] = { +#if defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS) + kWidevineCdmAdapterFileName, // see http://crbug.com/368743 + // and http://crbug.com/410630 +#endif + "libpdf.so" // see http://crbug.com/405305 +}; + +std::string HashPrefix(const std::string& histogram) { + const std::string id_hash = + base::SHA1HashString(histogram.substr(0, histogram.find('.'))); + DCHECK_EQ(id_hash.length(), base::kSHA1Length); + return base::HexEncode(id_hash.c_str(), id_hash.length()); +} + +} // namespace + +PepperUMAHost::PepperUMAHost(content::RendererPpapiHost* host, + PP_Instance instance, + PP_Resource resource) + : ResourceHost(host->GetPpapiHost(), instance, resource), + document_url_(host->GetDocumentURL(instance)), + is_plugin_in_process_(host->IsRunningInProcess()) { + if (host->GetPluginInstance(instance)) { + plugin_base_name_ = + host->GetPluginInstance(instance)->GetModulePath().BaseName(); + } + + for (size_t i = 0; i < arraysize(kPredefinedAllowedUMAOrigins); ++i) + allowed_origins_.insert(kPredefinedAllowedUMAOrigins[i]); + for (size_t i = 0; i < arraysize(kWhitelistedHistogramPrefixes); ++i) + allowed_histogram_prefixes_.insert(kWhitelistedHistogramPrefixes[i]); + for (size_t i = 0; i < arraysize(kWhitelistedPluginBaseNames); ++i) + allowed_plugin_base_names_.insert(kWhitelistedPluginBaseNames[i]); +} + +PepperUMAHost::~PepperUMAHost() {} + +int32_t PepperUMAHost::OnResourceMessageReceived( + const IPC::Message& msg, + ppapi::host::HostMessageContext* context) { + PPAPI_BEGIN_MESSAGE_MAP(PepperUMAHost, msg) + PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UMA_HistogramCustomTimes, + OnHistogramCustomTimes) + PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UMA_HistogramCustomCounts, + OnHistogramCustomCounts) + PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UMA_HistogramEnumeration, + OnHistogramEnumeration) + PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( + PpapiHostMsg_UMA_IsCrashReportingEnabled, OnIsCrashReportingEnabled) + PPAPI_END_MESSAGE_MAP() + return PP_ERROR_FAILED; +} + +bool PepperUMAHost::IsPluginWhitelisted() { +#if defined(ENABLE_EXTENSIONS) + return true; +#else + return false; +#endif +} + +bool PepperUMAHost::IsHistogramAllowed(const std::string& histogram) { + if (is_plugin_in_process_ && histogram.find("NaCl.") == 0) { + return true; + } + + if (IsPluginWhitelisted() && + allowed_histogram_prefixes_.find(HashPrefix(histogram)) != + allowed_histogram_prefixes_.end()) { + return true; + } + + if (allowed_plugin_base_names_.find(plugin_base_name_.MaybeAsASCII()) != + allowed_plugin_base_names_.end()) { + return true; + } + + LOG(ERROR) << "Host or histogram name is not allowed to use the UMA API."; + return false; +} + +#define RETURN_IF_BAD_ARGS(_min, _max, _buckets) \ + do { \ + if (_min >= _max || _buckets <= 1) \ + return PP_ERROR_BADARGUMENT; \ + } while (0) + +int32_t PepperUMAHost::OnHistogramCustomTimes( + ppapi::host::HostMessageContext* context, + const std::string& name, + int64_t sample, + int64_t min, + int64_t max, + uint32_t bucket_count) { + if (!IsHistogramAllowed(name)) { + return PP_ERROR_NOACCESS; + } + RETURN_IF_BAD_ARGS(min, max, bucket_count); + + base::HistogramBase* counter = base::Histogram::FactoryTimeGet( + name, + base::TimeDelta::FromMilliseconds(min), + base::TimeDelta::FromMilliseconds(max), + bucket_count, + base::HistogramBase::kUmaTargetedHistogramFlag); + // The histogram can be NULL if it is constructed with bad arguments. Ignore + // that data for this API. An error message will be logged. + if (counter) + counter->AddTime(base::TimeDelta::FromMilliseconds(sample)); + return PP_OK; +} + +int32_t PepperUMAHost::OnHistogramCustomCounts( + ppapi::host::HostMessageContext* context, + const std::string& name, + int32_t sample, + int32_t min, + int32_t max, + uint32_t bucket_count) { + if (!IsHistogramAllowed(name)) { + return PP_ERROR_NOACCESS; + } + RETURN_IF_BAD_ARGS(min, max, bucket_count); + + base::HistogramBase* counter = base::Histogram::FactoryGet( + name, + min, + max, + bucket_count, + base::HistogramBase::kUmaTargetedHistogramFlag); + // The histogram can be NULL if it is constructed with bad arguments. Ignore + // that data for this API. An error message will be logged. + if (counter) + counter->Add(sample); + return PP_OK; +} + +int32_t PepperUMAHost::OnHistogramEnumeration( + ppapi::host::HostMessageContext* context, + const std::string& name, + int32_t sample, + int32_t boundary_value) { + if (!IsHistogramAllowed(name)) { + return PP_ERROR_NOACCESS; + } + RETURN_IF_BAD_ARGS(0, boundary_value, boundary_value + 1); + + base::HistogramBase* counter = base::LinearHistogram::FactoryGet( + name, + 1, + boundary_value, + boundary_value + 1, + base::HistogramBase::kUmaTargetedHistogramFlag); + // The histogram can be NULL if it is constructed with bad arguments. Ignore + // that data for this API. An error message will be logged. + if (counter) + counter->Add(sample); + return PP_OK; +} + +int32_t PepperUMAHost::OnIsCrashReportingEnabled( + ppapi::host::HostMessageContext* context) { + if (!IsPluginWhitelisted()) + return PP_ERROR_NOACCESS; + bool enabled = false; + content::RenderThread::Get()->Send( + new ChromeViewHostMsg_IsCrashReportingEnabled(&enabled)); + if (enabled) + return PP_OK; + return PP_ERROR_FAILED; +} diff --git a/src/renderer/prerenderer/prerenderer_client.h b/src/renderer/prerenderer/prerenderer_client.h index 42ed0063b9..ef9b98e99c 100644 --- a/src/renderer/prerenderer/prerenderer_client.h +++ b/src/renderer/prerenderer/prerenderer_client.h @@ -33,10 +33,10 @@ class PrerendererClient : public content::RenderViewObserver, explicit PrerendererClient(content::RenderView* render_view); private: - virtual ~PrerendererClient(); + ~PrerendererClient() final; // Implements blink::WebPrerendererClient - virtual void willAddPrerender(blink::WebPrerender* prerender) OVERRIDE; + virtual void willAddPrerender(blink::WebPrerender* prerender) override; }; } // namespace prerender diff --git a/src/renderer/printing/print_web_view_helper.cc b/src/renderer/printing/print_web_view_helper.cc index 8e5c18b3bc..8e7d48769f 100644 --- a/src/renderer/printing/print_web_view_helper.cc +++ b/src/renderer/printing/print_web_view_helper.cc @@ -2,60 +2,67 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/nw/src/renderer/printing/print_web_view_helper.h" +#include "chrome/renderer/printing/print_web_view_helper.h" #include #include "base/auto_reset.h" -#include "base/command_line.h" #include "base/json/json_writer.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/metrics/histogram.h" -#include "base/strings/stringprintf.h" +#include "base/process/process_handle.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" -#include "content/public/renderer/web_preferences.h" -#include "chrome/common/chrome_switches.h" -#include "chrome/common/print_messages.h" -#include "chrome/common/render_messages.h" -#include "chrome/renderer/prerender/prerender_helper.h" +#include "content/nw/src/common/print_messages.h" +//#include "chrome/grit/browser_resources.h" +#include "content/public/common/web_preferences.h" +#include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_view.h" -#include "grit/nw_resources.h" -//#include "grit/generated_resources.h" #include "net/base/escape.h" -#include "printing/metafile.h" -#include "printing/metafile_impl.h" +#include "printing/pdf_metafile_skia.h" #include "printing/units.h" -#include "skia/ext/vector_platform_device_skia.h" #include "third_party/WebKit/public/platform/WebSize.h" #include "third_party/WebKit/public/platform/WebURLRequest.h" #include "third_party/WebKit/public/web/WebConsoleMessage.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebElement.h" -#include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebFrameClient.h" +#include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebPlugin.h" #include "third_party/WebKit/public/web/WebPluginDocument.h" #include "third_party/WebKit/public/web/WebPrintParams.h" +#include "third_party/WebKit/public/web/WebPrintPresetOptions.h" #include "third_party/WebKit/public/web/WebPrintScalingOption.h" #include "third_party/WebKit/public/web/WebScriptSource.h" #include "third_party/WebKit/public/web/WebSettings.h" #include "third_party/WebKit/public/web/WebView.h" #include "third_party/WebKit/public/web/WebViewClient.h" -#include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" -#include "webkit/common/webpreferences.h" -using base::MessageLoop; +using content::WebPreferences; namespace printing { namespace { +enum PrintPreviewHelperEvents { + PREVIEW_EVENT_REQUESTED, + PREVIEW_EVENT_CACHE_HIT, // Unused + PREVIEW_EVENT_CREATE_DOCUMENT, + PREVIEW_EVENT_NEW_SETTINGS, // Unused + PREVIEW_EVENT_MAX, +}; + const double kMinDpi = 1.0; +#if !defined(ENABLE_PRINT_PREVIEW) +bool g_is_preview_enabled_ = false; +#else +bool g_is_preview_enabled_ = true; + const char kPageLoadScriptFormat[] = "document.open(); document.write(%s); document.close();"; @@ -69,6 +76,7 @@ void ExecuteScript(blink::WebFrame* frame, std::string script = base::StringPrintf(script_format, json.c_str()); frame->executeScript(blink::WebString(base::UTF8ToUTF16(script))); } +#endif // !defined(ENABLE_PRINT_PREVIEW) int GetDPI(const PrintMsg_Print_Params* print_params) { #if defined(OS_MACOSX) @@ -84,10 +92,10 @@ bool PrintMsg_Print_Params_IsValid(const PrintMsg_Print_Params& params) { return !params.content_size.IsEmpty() && !params.page_size.IsEmpty() && !params.printable_area.IsEmpty() && params.document_cookie && params.desired_dpi && params.max_shrink && params.min_shrink && - params.dpi && (params.margin_top >= 0) && (params.margin_left >= 0); + params.dpi && (params.margin_top >= 0) && (params.margin_left >= 0) && + params.dpi > kMinDpi && params.document_cookie != 0; } - PrintMsg_Print_Params GetCssPrintParams( blink::WebFrame* frame, int page_index, @@ -272,14 +280,16 @@ void ComputeWebKitPrintParamsInDesiredDpi( print_params.desired_dpi); } +blink::WebPlugin* GetPlugin(const blink::WebFrame* frame) { + return frame->document().isPluginDocument() ? + frame->document().to().plugin() : NULL; +} + bool PrintingNodeOrPdfFrame(const blink::WebFrame* frame, const blink::WebNode& node) { if (!node.isNull()) return true; - if (!frame->document().isPluginDocument()) - return false; - blink::WebPlugin* plugin = - frame->document().to().plugin(); + blink::WebPlugin* plugin = GetPlugin(frame); return plugin && plugin->supportsPaginatedPrint(); } @@ -313,6 +323,41 @@ bool FitToPageEnabled(const base::DictionaryValue& job_settings) { return fit_to_paper_size; } +// Returns the print scaling option to retain/scale/crop the source page size +// to fit the printable area of the paper. +// +// We retain the source page size when the current destination printer is +// SAVE_AS_PDF. +// +// We crop the source page size to fit the printable area or we print only the +// left top page contents when +// (1) Source is PDF and the user has requested not to fit to printable area +// via |job_settings|. +// (2) Source is PDF. This is the first preview request and print scaling +// option is disabled for initiator renderer plugin. +// +// In all other cases, we scale the source page to fit the printable area. +blink::WebPrintScalingOption GetPrintScalingOption( + blink::WebFrame* frame, + const blink::WebNode& node, + bool source_is_html, + const base::DictionaryValue& job_settings, + const PrintMsg_Print_Params& params) { + if (params.print_to_pdf) + return blink::WebPrintScalingOptionSourceSize; + + if (!source_is_html) { + if (!FitToPageEnabled(job_settings)) + return blink::WebPrintScalingOptionNone; + + bool no_plugin_scaling = frame->isPrintScalingDisabledForPlugin(node); + + if (params.is_first_request && no_plugin_scaling) + return blink::WebPrintScalingOptionNone; + } + return blink::WebPrintScalingOptionFitToPrintableArea; +} + PrintMsg_Print_Params CalculatePrintParamsForCss( blink::WebFrame* frame, int page_index, @@ -356,52 +401,76 @@ PrintMsg_Print_Params CalculatePrintParamsForCss( return result_params; } -bool IsPrintPreviewEnabled() { - return CommandLine::ForCurrentProcess()->HasSwitch( - switches::kRendererPrintPreview); +} // namespace + +FrameReference::FrameReference(blink::WebLocalFrame* frame) { + Reset(frame); } -bool IsPrintThrottlingDisabled() { - return CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableScriptedPrintThrottling); +FrameReference::FrameReference() { + Reset(NULL); } -} // namespace +FrameReference::~FrameReference() { +} +void FrameReference::Reset(blink::WebLocalFrame* frame) { + if (frame) { + view_ = frame->view(); + frame_ = frame; + } else { + view_ = NULL; + frame_ = NULL; + } +} + +blink::WebLocalFrame* FrameReference::GetFrame() { + if (view_ == NULL || frame_ == NULL) + return NULL; + for (blink::WebFrame* frame = view_->mainFrame(); frame != NULL; + frame = frame->traverseNext(false)) { + if (frame == frame_) + return frame_; + } + return NULL; +} + +blink::WebView* FrameReference::view() { + return view_; +} + +#if defined(ENABLE_PRINT_PREVIEW) // static - Not anonymous so that platform implementations can use it. void PrintWebViewHelper::PrintHeaderAndFooter( blink::WebCanvas* canvas, int page_number, int total_pages, + const blink::WebFrame& source_frame, float webkit_scale_factor, const PageSizeMargins& page_layout, - const base::DictionaryValue& header_footer_info, const PrintMsg_Print_Params& params) { - skia::VectorPlatformDeviceSkia* device = - static_cast(canvas->getTopDevice()); - device->setDrawingArea(SkPDFDevice::kMargin_DrawingArea); - SkAutoCanvasRestore auto_restore(canvas, true); canvas->scale(1 / webkit_scale_factor, 1 / webkit_scale_factor); blink::WebSize page_size(page_layout.margin_left + page_layout.margin_right + - page_layout.content_width, - page_layout.margin_top + page_layout.margin_bottom + - page_layout.content_height); + page_layout.content_width, + page_layout.margin_top + page_layout.margin_bottom + + page_layout.content_height); blink::WebView* web_view = blink::WebView::create(NULL); web_view->settings()->setJavaScriptEnabled(true); - blink::WebFrame* frame = blink::WebFrame::create(NULL); + blink::WebLocalFrame* frame = blink::WebLocalFrame::create(NULL); web_view->setMainFrame(frame); - base::StringValue html( - ResourceBundle::GetSharedInstance().GetLocalizedString( - IDR_PRINT_PREVIEW_PAGE)); + base::StringValue html(ResourceBundle::GetSharedInstance().GetLocalizedString( + IDR_PRINT_PREVIEW_PAGE)); // Load page with script to avoid async operations. ExecuteScript(frame, kPageLoadScriptFormat, html); - scoped_ptr options(header_footer_info.DeepCopy()); + scoped_ptr options(new base::DictionaryValue()); + options.reset(new base::DictionaryValue()); + options->SetDouble(kSettingHeaderFooterDate, base::Time::Now().ToJsTime()); options->SetDouble("width", page_size.width); options->SetDouble("height", page_size.height); options->SetDouble("topMargin", page_layout.margin_top); @@ -409,6 +478,12 @@ void PrintWebViewHelper::PrintHeaderAndFooter( options->SetString("pageNumber", base::StringPrintf("%d/%d", page_number, total_pages)); + // Fallback to initiator URL and title if it's empty for printed frame. + base::string16 url = source_frame.document().url().string(); + options->SetString("url", url.empty() ? params.url : url); + base::string16 title = source_frame.document().title(); + options->SetString("title", title.empty() ? params.title : title); + ExecuteScript(frame, kPageSetupScriptFormat, *options); blink::WebPrintParams webkit_params(page_size); @@ -419,9 +494,9 @@ void PrintWebViewHelper::PrintHeaderAndFooter( frame->printEnd(); web_view->close(); - - device->setDrawingArea(SkPDFDevice::kContent_DrawingArea); + frame->close(); } +#endif // defined(ENABLE_PRINT_PREVIEW) // static - Not anonymous so that platform implementations can use it. float PrintWebViewHelper::RenderPageContent(blink::WebFrame* frame, @@ -453,7 +528,7 @@ class PrepareFrameAndViewForPrint : public blink::WebViewClient, public blink::WebFrameClient { public: PrepareFrameAndViewForPrint(const PrintMsg_Print_Params& params, - blink::WebFrame* frame, + blink::WebLocalFrame* frame, const blink::WebNode& node, bool ignore_css_margins); virtual ~PrepareFrameAndViewForPrint(); @@ -466,8 +541,8 @@ class PrepareFrameAndViewForPrint : public blink::WebViewClient, // Prepares frame for printing. void StartPrinting(); - blink::WebFrame* frame() const { - return frame_; + blink::WebLocalFrame* frame() { + return frame_.GetFrame(); } const blink::WebNode& node() const { @@ -478,29 +553,34 @@ class PrepareFrameAndViewForPrint : public blink::WebViewClient, return expected_pages_count_; } - gfx::Size GetPrintCanvasSize() const; - void FinishPrinting(); - bool IsLoadingSelection() const { + bool IsLoadingSelection() { // It's not selection if not |owns_web_view_|. - return owns_web_view_ && frame_ && frame_->isLoading(); + return owns_web_view_ && frame() && frame()->isLoading(); } + // TODO(ojan): Remove this override and have this class use a non-null + // layerTreeView. + // blink::WebViewClient override: + virtual bool allowsBrokenNullLayerTreeView() const; + protected: // blink::WebViewClient override: virtual void didStopLoading(); - virtual void CallOnReady(); + // blink::WebFrameClient override: + virtual blink::WebFrame* createChildFrame(blink::WebLocalFrame* parent, + const blink::WebString& name); + virtual void frameDetached(blink::WebFrame* frame); private: + void CallOnReady(); void ResizeForPrinting(); void RestoreSize(); void CopySelection(const WebPreferences& preferences); - base::WeakPtrFactory weak_ptr_factory_; - - blink::WebFrame* frame_; + FrameReference frame_; blink::WebNode node_to_print_; bool owns_web_view_; blink::WebPrintParams web_print_params_; @@ -512,33 +592,37 @@ class PrepareFrameAndViewForPrint : public blink::WebViewClient, bool should_print_selection_only_; bool is_printing_started_; + base::WeakPtrFactory weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(PrepareFrameAndViewForPrint); }; PrepareFrameAndViewForPrint::PrepareFrameAndViewForPrint( const PrintMsg_Print_Params& params, - blink::WebFrame* frame, + blink::WebLocalFrame* frame, const blink::WebNode& node, bool ignore_css_margins) - : weak_ptr_factory_(this), - frame_(frame), + : frame_(frame), node_to_print_(node), owns_web_view_(false), expected_pages_count_(0), should_print_backgrounds_(params.should_print_backgrounds), should_print_selection_only_(params.selection_only), - is_printing_started_(false) { + is_printing_started_(false), + weak_ptr_factory_(this) { PrintMsg_Print_Params print_params = params; if (!should_print_selection_only_ || - !PrintingNodeOrPdfFrame(frame_, node_to_print_)) { + !PrintingNodeOrPdfFrame(frame, node_to_print_)) { bool fit_to_page = ignore_css_margins && print_params.print_scaling_option == blink::WebPrintScalingOptionFitToPrintableArea; - print_params = CalculatePrintParamsForCss(frame_, 0, print_params, + ComputeWebKitPrintParamsInDesiredDpi(params, &web_print_params_); + frame->printBegin(web_print_params_, node_to_print_); + print_params = CalculatePrintParamsForCss(frame, 0, print_params, ignore_css_margins, fit_to_page, NULL); + frame->printEnd(); } - ComputeWebKitPrintParamsInDesiredDpi(print_params, &web_print_params_); } @@ -557,11 +641,9 @@ void PrepareFrameAndViewForPrint::ResizeForPrinting() { print_layout_size.set_height( static_cast(static_cast(print_layout_size.height()) * 1.25)); - if (!frame_) + if (!frame()) return; - - blink::WebView* web_view = frame_->view(); - + blink::WebView* web_view = frame_.view(); // Backup size and offset. if (blink::WebFrame* web_frame = web_view->mainFrame()) prev_scroll_offset_ = web_frame->scrollOffset(); @@ -573,11 +655,10 @@ void PrepareFrameAndViewForPrint::ResizeForPrinting() { void PrepareFrameAndViewForPrint::StartPrinting() { ResizeForPrinting(); - blink::WebView* web_view = frame_->view(); + blink::WebView* web_view = frame_.view(); web_view->settings()->setShouldPrintBackgrounds(should_print_backgrounds_); - // TODO(vitalybuka): Update call after - // https://bugs.webkit.org/show_bug.cgi?id=107718 is fixed. - expected_pages_count_ = frame_->printBegin(web_print_params_, node_to_print_); + expected_pages_count_ = + frame()->printBegin(web_print_params_, node_to_print_); is_printing_started_ = true; } @@ -585,10 +666,12 @@ void PrepareFrameAndViewForPrint::CopySelectionIfNeeded( const WebPreferences& preferences, const base::Closure& on_ready) { on_ready_ = on_ready; - if (should_print_selection_only_) + if (should_print_selection_only_) { CopySelection(preferences); - else - didStopLoading(); + } else { + // Call immediately, async call crashes scripting printing. + CallOnReady(); + } } void PrepareFrameAndViewForPrint::CopySelection( @@ -607,37 +690,51 @@ void PrepareFrameAndViewForPrint::CopySelection( blink::WebView* web_view = blink::WebView::create(this); owns_web_view_ = true; - content::ApplyWebPreferences(prefs, web_view); - web_view->setMainFrame(blink::WebFrame::create(this)); - frame_ = web_view->mainFrame(); + content::RenderView::ApplyWebPreferences(prefs, web_view); + web_view->setMainFrame(blink::WebLocalFrame::create(this)); + frame_.Reset(web_view->mainFrame()->toWebLocalFrame()); node_to_print_.reset(); // When loading is done this will call didStopLoading() and that will do the // actual printing. - frame_->loadRequest(blink::WebURLRequest(GURL(url_str))); + frame()->loadRequest(blink::WebURLRequest(GURL(url_str))); +} + +bool PrepareFrameAndViewForPrint::allowsBrokenNullLayerTreeView() const { + return true; } void PrepareFrameAndViewForPrint::didStopLoading() { DCHECK(!on_ready_.is_null()); // Don't call callback here, because it can delete |this| and WebView that is // called didStopLoading. - MessageLoop::current()->PostTask(FROM_HERE, + base::MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(&PrepareFrameAndViewForPrint::CallOnReady, weak_ptr_factory_.GetWeakPtr())); } -void PrepareFrameAndViewForPrint::CallOnReady() { - return on_ready_.Run(); // Can delete |this|. +blink::WebFrame* PrepareFrameAndViewForPrint::createChildFrame( + blink::WebLocalFrame* parent, + const blink::WebString& name) { + blink::WebFrame* frame = blink::WebLocalFrame::create(this); + parent->appendChild(frame); + return frame; +} + +void PrepareFrameAndViewForPrint::frameDetached(blink::WebFrame* frame) { + if (frame->parent()) + frame->parent()->removeChild(frame); + frame->close(); } -gfx::Size PrepareFrameAndViewForPrint::GetPrintCanvasSize() const { - return gfx::Size(web_print_params_.printContentArea.width, - web_print_params_.printContentArea.height); +void PrepareFrameAndViewForPrint::CallOnReady() { + return on_ready_.Run(); // Can delete |this|. } void PrepareFrameAndViewForPrint::RestoreSize() { - if (frame_) { - blink::WebView* web_view = frame_->view(); + if (frame()) { + blink::WebView* web_view = frame_.GetFrame()->view(); web_view->resize(prev_view_size_); if (blink::WebFrame* web_frame = web_view->mainFrame()) web_frame->setScrollOffset(prev_scroll_offset_); @@ -645,89 +742,111 @@ void PrepareFrameAndViewForPrint::RestoreSize() { } void PrepareFrameAndViewForPrint::FinishPrinting() { - if (frame_) { - blink::WebView* web_view = frame_->view(); + blink::WebLocalFrame* frame = frame_.GetFrame(); + if (frame) { + blink::WebView* web_view = frame->view(); if (is_printing_started_) { is_printing_started_ = false; - frame_->printEnd(); + frame->printEnd(); if (!owns_web_view_) { web_view->settings()->setShouldPrintBackgrounds(false); RestoreSize(); } } if (owns_web_view_) { - DCHECK(!frame_->isLoading()); + DCHECK(!frame->isLoading()); owns_web_view_ = false; web_view->close(); } } - frame_ = NULL; + frame_.Reset(NULL); on_ready_.Reset(); } -PrintWebViewHelper::PrintWebViewHelper(content::RenderView* render_view) +PrintWebViewHelper::PrintWebViewHelper( + content::RenderView* render_view, + bool out_of_process_pdf_enabled, + bool print_preview_disabled, + scoped_ptr delegate) : content::RenderViewObserver(render_view), content::RenderViewObserverTracker(render_view), reset_prep_frame_view_(false), - is_preview_enabled_(IsPrintPreviewEnabled()), - is_scripted_print_throttling_disabled_(IsPrintThrottlingDisabled()), is_print_ready_metafile_sent_(false), ignore_css_margins_(false), - user_cancelled_scripted_print_count_(0), is_scripted_printing_blocked_(false), notify_browser_of_print_failure_(true), print_for_preview_(false), + out_of_process_pdf_enabled_(out_of_process_pdf_enabled), + delegate_(delegate.Pass()), print_node_in_progress_(false), is_loading_(false), is_scripted_preview_delayed_(false), weak_ptr_factory_(this) { + if (print_preview_disabled) + DisablePreview(); } PrintWebViewHelper::~PrintWebViewHelper() {} +// static +void PrintWebViewHelper::DisablePreview() { + g_is_preview_enabled_ = false; +} + bool PrintWebViewHelper::IsScriptInitiatedPrintAllowed( blink::WebFrame* frame, bool user_initiated) { - if (is_scripted_printing_blocked_) - return false; // If preview is enabled, then the print dialog is tab modal, and the user // can always close the tab on a mis-behaving page (the system print dialog // is app modal). If the print was initiated through user action, don't // throttle. Or, if the command line flag to skip throttling has been set. - if (!is_scripted_print_throttling_disabled_ && - !is_preview_enabled_ && - !user_initiated) - return !IsScriptInitiatedPrintTooFrequent(frame); - return true; + return !is_scripted_printing_blocked_ && + (user_initiated || g_is_preview_enabled_ || + scripting_throttler_.IsAllowed(frame)); +} + +void PrintWebViewHelper::DidStartLoading() { + is_loading_ = true; +} + +void PrintWebViewHelper::DidStopLoading() { + is_loading_ = false; + if (!on_stop_loading_closure_.is_null()) { + on_stop_loading_closure_.Run(); + on_stop_loading_closure_.Reset(); + } } // Prints |frame| which called window.print(). -void PrintWebViewHelper::PrintPage(blink::WebFrame* frame, +void PrintWebViewHelper::PrintPage(blink::WebLocalFrame* frame, bool user_initiated) { DCHECK(frame); + // Allow Prerendering to cancel this print request if necessary. + if (delegate_ && delegate_->CancelPrerender(render_view(), routing_id())) + return; + if (!IsScriptInitiatedPrintAllowed(frame, user_initiated)) return; - IncrementScriptedPrintCount(); - if (is_preview_enabled_) { + if (!g_is_preview_enabled_) { + Print(frame, blink::WebNode(), true); + } else { print_preview_context_.InitWithFrame(frame); RequestPrintPreview(PRINT_PREVIEW_SCRIPTED); - } else { - Print(frame, blink::WebNode()); } } bool PrintWebViewHelper::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(PrintWebViewHelper, message) +#if defined(ENABLE_BASIC_PRINTING) IPC_MESSAGE_HANDLER(PrintMsg_PrintPages, OnPrintPages) IPC_MESSAGE_HANDLER(PrintMsg_PrintForSystemDialog, OnPrintForSystemDialog) +#endif // ENABLE_BASIC_PRINTING IPC_MESSAGE_HANDLER(PrintMsg_InitiatePrintPreview, OnInitiatePrintPreview) IPC_MESSAGE_HANDLER(PrintMsg_PrintPreview, OnPrintPreview) IPC_MESSAGE_HANDLER(PrintMsg_PrintForPrintPreview, OnPrintForPrintPreview) IPC_MESSAGE_HANDLER(PrintMsg_PrintingDone, OnPrintingDone) - IPC_MESSAGE_HANDLER(PrintMsg_ResetScriptedPrintCount, - ResetScriptedPrintCount) IPC_MESSAGE_HANDLER(PrintMsg_SetScriptedPrintingBlocked, SetScriptedPrintBlocked) IPC_MESSAGE_UNHANDLED(handled = false) @@ -736,8 +855,7 @@ bool PrintWebViewHelper::OnMessageReceived(const IPC::Message& message) { } void PrintWebViewHelper::OnPrintForPrintPreview( - const base::DictionaryValue& job_settings) { - DCHECK(is_preview_enabled_); + const base::DictionaryValue& job_settings) { // If still not finished with earlier print request simply ignore. if (prep_frame_view_) return; @@ -749,7 +867,7 @@ void PrintWebViewHelper::OnPrintForPrintPreview( return; blink::WebDocument document = main_frame->document(); - // with id="pdf-viewer" is created in + // / + + + diff --git a/tests/automation/crashonreload/internal/package.json b/tests/automation/crashonreload/internal/package.json new file mode 100644 index 0000000000..c04044da00 --- /dev/null +++ b/tests/automation/crashonreload/internal/package.json @@ -0,0 +1,4 @@ +{ + "name":"dev tools", + "main":"index.html" +} \ No newline at end of file diff --git a/tests/automation/crashonreload/internal/test.html b/tests/automation/crashonreload/internal/test.html new file mode 100644 index 0000000000..c72e1692d8 --- /dev/null +++ b/tests/automation/crashonreload/internal/test.html @@ -0,0 +1,9 @@ + + + +test dev tools + + +hello world + + \ No newline at end of file diff --git a/tests/automation/crashonreload/mocha_test.js b/tests/automation/crashonreload/mocha_test.js new file mode 100644 index 0000000000..a0c4e29b55 --- /dev/null +++ b/tests/automation/crashonreload/mocha_test.js @@ -0,0 +1,31 @@ +var spawn = require('child_process').spawn; +var path = require('path'); +var fs = require('fs'); +var curDir = fs.realpathSync('.'); + +describe('crash on reload',function(){ + it('nw should not crash when reloading with devtool opened',function(done){ + this.timeout(0); + var result = false; + + var child = spawnChildProcess(path.join(curDir, 'internal')); + child.on('exit', function (code){ + result = true; + if (code != 0) { + done('nw crashes'); + child.kill(); + } else { + done(); + } + + }); + + setTimeout(function(){ + if (!result) { + child.kill(); + done(); + } + + }, 4500); + }); +}); diff --git a/tests/automation/crashonreload/package.json b/tests/automation/crashonreload/package.json new file mode 100644 index 0000000000..afb89973c8 --- /dev/null +++ b/tests/automation/crashonreload/package.json @@ -0,0 +1,4 @@ +{ + "name":"crash_onreload_wrapper", + "main":"index.html" +} diff --git a/tests/automation/datapath/datacash-path/index.html b/tests/automation/datapath/datacash-path/index.html new file mode 100644 index 0000000000..3f67831e2a --- /dev/null +++ b/tests/automation/datapath/datacash-path/index.html @@ -0,0 +1,19 @@ + + +testing data path + + +

Testing data path

+the data path should be set as + + + diff --git a/tests/automation/datapath/datacash-path/package.json b/tests/automation/datapath/datacash-path/package.json new file mode 100644 index 0000000000..56305d8d64 --- /dev/null +++ b/tests/automation/datapath/datacash-path/package.json @@ -0,0 +1,5 @@ +{ + "name": "data path", + "main": "index.html", + "chromium-args": "--data-path='./data-cash/dataPath/'" +} diff --git a/tests/automation/datapath/datacash/index.html b/tests/automation/datapath/datacash/index.html new file mode 100644 index 0000000000..332964515e --- /dev/null +++ b/tests/automation/datapath/datacash/index.html @@ -0,0 +1,21 @@ + + +testing data path + + +

Testing data path

+the data path should be set as + + + diff --git a/tests/automation/datapath/datacash/package.json b/tests/automation/datapath/datacash/package.json new file mode 100644 index 0000000000..3551a65874 --- /dev/null +++ b/tests/automation/datapath/datacash/package.json @@ -0,0 +1,5 @@ +{ + "name": "data path", + "main": "index.html", + "chromium-args": "--data-path='./data-cash/'" +} diff --git a/tests/automation/datapath/datapath-cash/index.html b/tests/automation/datapath/datapath-cash/index.html new file mode 100644 index 0000000000..3f67831e2a --- /dev/null +++ b/tests/automation/datapath/datapath-cash/index.html @@ -0,0 +1,19 @@ + + +testing data path + + +

Testing data path

+the data path should be set as + + + diff --git a/tests/automation/datapath/datapath-cash/package.json b/tests/automation/datapath/datapath-cash/package.json new file mode 100644 index 0000000000..93f8b5f9d6 --- /dev/null +++ b/tests/automation/datapath/datapath-cash/package.json @@ -0,0 +1,5 @@ +{ + "name": "data path", + "main": "index.html", + "chromium-args": "--data-path='./dataPath/data-cash/'" +} diff --git a/tests/automation/datapath/datapath/index.html b/tests/automation/datapath/datapath/index.html new file mode 100644 index 0000000000..3f67831e2a --- /dev/null +++ b/tests/automation/datapath/datapath/index.html @@ -0,0 +1,19 @@ + + +testing data path + + +

Testing data path

+the data path should be set as + + + diff --git a/tests/automation/datapath/datapath/package.json b/tests/automation/datapath/datapath/package.json new file mode 100644 index 0000000000..7843ddcba3 --- /dev/null +++ b/tests/automation/datapath/datapath/package.json @@ -0,0 +1,5 @@ +{ + "name": "data path", + "main": "index.html", + "chromium-args": "--data-path='./dataPath/'" +} diff --git a/tests/automation/datapath/index.html b/tests/automation/datapath/index.html new file mode 100644 index 0000000000..2fbfa231bb --- /dev/null +++ b/tests/automation/datapath/index.html @@ -0,0 +1,15 @@ + + + + + Data path test + + +

Data path test

+ + + + + + + diff --git a/tests/automation/datapath/mocha_test.js b/tests/automation/datapath/mocha_test.js new file mode 100644 index 0000000000..c367ff4abd --- /dev/null +++ b/tests/automation/datapath/mocha_test.js @@ -0,0 +1,97 @@ +var path = require('path'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + + +describe('data-path', function() { + + var server; + + before(function(done){ + this.timeout(0); + if (!fs.existsSync(path.join(curDir, 'data-cash'))) + fs.mkdirsSync(path.join(curDir, 'data-cash')); + if (!fs.existsSync(path.join(curDir, 'dataPath'))) + fs.mkdirsSync(path.join(curDir, 'dataPath')); + if (!fs.existsSync(path.join(curDir, 'dataPath', 'data-cash'))) + fs.mkdirsSync(path.join(curDir, 'dataPath', 'data-cash')); + if (!fs.existsSync(path.join(curDir, 'data-cash', 'dataPath'))) + fs.mkdirsSync(path.join(curDir, 'data-cash', 'dataPath')); + + server = createTCPServer(13013); + + done(); + }); + + after(function() { + setTimeout(function() { + server.close(); + fs.remove(path.join(curDir, 'data-cash')); + fs.remove(path.join(curDir, 'dataPath')); + fs.remove(path.join(curDir, 'dataPath', 'data-cash')); + fs.remove(path.join(curDir, 'data-cash', 'dataPath')); + }, 1000); + }); + + + it('setting datapath as ./data-cash/ should pass', + function(done) { + this.timeout(0); + var child = spawnChildProcess(path.join(curDir, 'datacash')); + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + child.kill(); + server.removeAllListeners('connection'); + done(); + });}); + }); + + it('setting datapath as ./dataPath/ should pass', + function(done) { + this.timeout(0); + + var child = spawnChildProcess(path.join(curDir, 'datapath')); + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + child.kill(); + server.removeAllListeners('connection'); + done(); + });}); + }); + + it('setting datapath as ./dataPath/data-cash/ should pass', + function(done) { + this.timeout(0); + var child = spawnChildProcess(path.join(curDir, 'datapath-cash')); + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + child.kill(); + server.removeAllListeners('connection'); + done(); + });}); + + }); + + it('setting datapath as ./data-cash/dataPath/ should pass', + function(done) { + this.timeout(0); + + var child = spawnChildProcess(path.join(curDir, 'datacash-path')); + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + child.kill(); + server.removeAllListeners('connection'); + done(); + });}); + + }); + + +}); + + + diff --git a/tests/automation/datapath/package.json b/tests/automation/datapath/package.json new file mode 100644 index 0000000000..6351f0b9fe --- /dev/null +++ b/tests/automation/datapath/package.json @@ -0,0 +1,4 @@ +{ + "name":"datapath_wrapper", + "main":"index.html" +} diff --git a/tests/automation/document_cookies/app/index.html b/tests/automation/document_cookies/app/index.html new file mode 100644 index 0000000000..3efd0d79e6 --- /dev/null +++ b/tests/automation/document_cookies/app/index.html @@ -0,0 +1,54 @@ + + + + + + + + diff --git a/tests/automation/document_cookies/app/package.json b/tests/automation/document_cookies/app/package.json new file mode 100644 index 0000000000..4a73bfc451 --- /dev/null +++ b/tests/automation/document_cookies/app/package.json @@ -0,0 +1,7 @@ +{ + "name": "nw", + "main": "app://whatever/index.html", + "window": { + "show": false + } +} diff --git a/tests/automation/document_cookies/file/index.html b/tests/automation/document_cookies/file/index.html new file mode 100644 index 0000000000..3efd0d79e6 --- /dev/null +++ b/tests/automation/document_cookies/file/index.html @@ -0,0 +1,54 @@ + + + + + + + + diff --git a/tests/automation/document_cookies/file/package.json b/tests/automation/document_cookies/file/package.json new file mode 100644 index 0000000000..18d312afa6 --- /dev/null +++ b/tests/automation/document_cookies/file/package.json @@ -0,0 +1,7 @@ +{ + "name": "nw", + "main": "index.html", + "window": { + "show": false + } +} diff --git a/tests/automation/document_cookies/index.html b/tests/automation/document_cookies/index.html new file mode 100644 index 0000000000..12d7d8c6c2 --- /dev/null +++ b/tests/automation/document_cookies/index.html @@ -0,0 +1,15 @@ + + + + + Document cookie test + + +

Document cookie test

+ + + + + + + diff --git a/tests/automation/document_cookies/mocha_test.js b/tests/automation/document_cookies/mocha_test.js new file mode 100644 index 0000000000..7764443c0d --- /dev/null +++ b/tests/automation/document_cookies/mocha_test.js @@ -0,0 +1,95 @@ +var path = require('path'); +var assert = require('assert'); +var gui = require('nw.gui'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); +var results = new Array(); +describe('document.cookies', function() { + +var server; + + before(function(done) { + server = createTCPServer(13013); + results.push('dump'); // should be remove when the first test case is ready + done(); + }); + + after(function () { + server.close(); + }); + +/* + describe('http', function() { + before(function(done) { + this.timeout(0); + var url = "http://127.0.0.1:8123/document_cookies.html"; + var win = gui.Window.open(url); + + setTimeout(function() { + results.push(win.window.msg.textContent); + done(); + win.window.close(); + }, 1000); + }); + it('should be set', function() { + assert.equal(results[0], '123'); + }); + }); +*/ + + describe('file', function() { + before(function(done) { + this.timeout(0); + var child = spawnChildProcess(path.join(curDir, 'file')); + + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + setTimeout(function() { + results.push(data); + child.kill(); + done(); + }, 1000); + }); + }); + setTimeout(done, 10000); + }); + + after(function() { + server.removeAllListeners('connection'); + }); + + it ('should be set', function() { + assert.equal(results[1], '123'); + }); + }); + + describe('app', function() { + before(function(done) { + this.timeout(0); + var child = spawnChildProcess(path.join(curDir, 'app')); + + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + setTimeout(function() { + results.push(data); + child.kill(); + done(); + }, 1000); + }); + }); + + setTimeout(done, 10000); + }); + + after(function() { + server.removeAllListeners('connection'); + }); + + it ('should be set', function() { + assert.equal(results[2], '123'); + }); + }); + +}); diff --git a/tests/automation/document_cookies/package.json b/tests/automation/document_cookies/package.json new file mode 100644 index 0000000000..196d53596c --- /dev/null +++ b/tests/automation/document_cookies/package.json @@ -0,0 +1,4 @@ +{ + "name":"documentcookie_wrapper", + "main":"index.html" +} diff --git a/tests/automation/failures.md b/tests/automation/failures.md new file mode 100644 index 0000000000..71d49ff7d2 --- /dev/null +++ b/tests/automation/failures.md @@ -0,0 +1,41 @@ +Failed test cases +======================= + +* app + +* chromedriver2_server + +* chromium-args + +* console + +* cookies_api (bad bad bad) + +* document_cookies + +* node + +* node-remote + +* process + +* reload_application + +* save_devtools_settings + +* single_instance + +* source-maps + +* temp_dir + +* user-agent + +* user-agent-app + +* website + + + + + diff --git a/tests/automation/fast_open_and_close_devtools/index.html b/tests/automation/fast_open_and_close_devtools/index.html new file mode 100644 index 0000000000..7fac4bbbd3 --- /dev/null +++ b/tests/automation/fast_open_and_close_devtools/index.html @@ -0,0 +1,16 @@ + + + + + Fast open and close devtools test + + +

Fast open and close devtools test

+ + + + + + + + diff --git a/tests/automation/fast_open_and_close_devtools/internal/index.html b/tests/automation/fast_open_and_close_devtools/internal/index.html new file mode 100644 index 0000000000..8a54574ce4 --- /dev/null +++ b/tests/automation/fast_open_and_close_devtools/internal/index.html @@ -0,0 +1,17 @@ + + + + + Test Case For Fast Open&Close Devtools Crashes + + + + + diff --git a/tests/automation/fast_open_and_close_devtools/internal/package.json b/tests/automation/fast_open_and_close_devtools/internal/package.json new file mode 100644 index 0000000000..fedeac13b7 --- /dev/null +++ b/tests/automation/fast_open_and_close_devtools/internal/package.json @@ -0,0 +1,4 @@ +{ + "name": "nw", + "main": "index.html" +} diff --git a/tests/automation/fast_open_and_close_devtools/mocha_test.js b/tests/automation/fast_open_and_close_devtools/mocha_test.js new file mode 100644 index 0000000000..2b57063cea --- /dev/null +++ b/tests/automation/fast_open_and_close_devtools/mocha_test.js @@ -0,0 +1,21 @@ +var path = require('path'); +var assert = require('assert'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + +var result = false; +describe('Fast open&close devtools from #1391', function() { + before(function(done) { + this.timeout(0); + var app = spawnChildProcess(path.join(curDir, 'internal')); + app.on('exit', function(code) { + if (code != null) + result = true; + done(); + }); + }); + + it('should not crash', function() { + assert.equal(result, true); + }); +}); diff --git a/tests/automation/fast_open_and_close_devtools/package.json b/tests/automation/fast_open_and_close_devtools/package.json new file mode 100644 index 0000000000..09772c3904 --- /dev/null +++ b/tests/automation/fast_open_and_close_devtools/package.json @@ -0,0 +1,4 @@ +{ + "name":"fast_open_and_close_devtools_wrapper", + "main":"index.html" +} diff --git a/tests/automation/fd-limit/.gitignore b/tests/automation/fd-limit/.gitignore new file mode 100644 index 0000000000..ca38f71d85 --- /dev/null +++ b/tests/automation/fd-limit/.gitignore @@ -0,0 +1,2 @@ +testfiles +fd-limit.xml diff --git a/tests/automation/fd-limit/index.html b/tests/automation/fd-limit/index.html new file mode 100644 index 0000000000..11eb04113e --- /dev/null +++ b/tests/automation/fd-limit/index.html @@ -0,0 +1,13 @@ + + + + + + fd-limit + + + + + + + diff --git a/tests/automation/fd-limit/mocha_test.js b/tests/automation/fd-limit/mocha_test.js new file mode 100644 index 0000000000..7ee726290f --- /dev/null +++ b/tests/automation/fd-limit/mocha_test.js @@ -0,0 +1,82 @@ +var assert = require('assert'); +var fs = require('fs'); +var path = require('path'); +var nw = require('./nw'); + +describe('fd-limit', function() { + + describe('file descriptor limit', function() { + + before(function(done) { + this.timeout(5000); + fs.mkdir('testfiles', function(err) { + if (err) throw err; + var n = 0; + (function next() { + fs.writeFile('testfiles/' + n + '.json', JSON.stringify({i: n}), function(err) { + if (err) throw err; + + if (++n === 2000) { + done(); + } else { + next(); + } + }); + }()); + }); + }); + + after(function(done) { + this.timeout(5000); + var n = 0; + (function next() { + fs.unlink('testfiles/' + n + '.json', function(err) { + if (err) throw err; + + if (++n === 2000) { + fs.rmdir('testfiles', function(err){ + if (err) throw err; + done(); + }); + } else { + next(); + } + }); + }()); + }); + + it('fetches 2k files via XHR should succeed with --file-descriptor-limit=8192', function(done) { + if (process.platform === 'win32') { + assert.equal(true, true); + done(); + } + + this.timeout(5000); + + nw.spawn(['test_app', '--file-descriptor-limit=8192'], function(err, data) { + if (err) throw err; + assert.equal(data.ok, true); + done(); + }); + + }); + + it('fetches 2k files via XHR should fail with --file-descriptor-limit=100', function(done) { + if (process.platform === 'win32') { + assert.equal(true, true); + done(); + } + + this.timeout(5000); + + nw.spawn(['test_app', '--file-descriptor-limit=100'], function(err, data) { + if (err) throw err; + assert.equal(data.ok, false); + done(); + }); + + }); + + }); + +}); diff --git a/tests/automation/fd-limit/nw.js b/tests/automation/fd-limit/nw.js new file mode 100644 index 0000000000..8b8650dab0 --- /dev/null +++ b/tests/automation/fd-limit/nw.js @@ -0,0 +1,40 @@ +var spawn = require('child_process').spawn; +var net = require('net'); + +exports.port = 13030; + +exports.spawn = function(args, cb) { + var shutdown = function (err, data) { + shutdown = function(){}; + server.close(function(){ + if (err) { + cb(err); + } else { + cb(null, JSON.parse(data)); + } + }); + }; + + var server = net.createServer(); + server.on('connection', function(con) { + con.on('data', function(data) { + shutdown(null, data); + }); + con.on('error', function(err){ + shutdown(err); + }); + con.on('close', function(){ + shutdown(new Error('not-receive')); + }); + }); + server.listen(exports.port); + + return spawn(process.execPath, args); + +}; + +exports.send = function(data) { + var client = net.connect(exports.port); + client.end(JSON.stringify(data)); + require('nw.gui').App.quit(); +}; diff --git a/tests/automation/fd-limit/package.json b/tests/automation/fd-limit/package.json new file mode 100644 index 0000000000..7a55b028dd --- /dev/null +++ b/tests/automation/fd-limit/package.json @@ -0,0 +1,5 @@ +{ + "name": "fd-limit", + "main": "index.html", + "single-instance": false +} diff --git a/tests/automation/fd-limit/test_app/index.html b/tests/automation/fd-limit/test_app/index.html new file mode 100644 index 0000000000..7e1056bd45 --- /dev/null +++ b/tests/automation/fd-limit/test_app/index.html @@ -0,0 +1,47 @@ + + + + + + fd-limit + + + + + diff --git a/tests/automation/fd-limit/test_app/package.json b/tests/automation/fd-limit/test_app/package.json new file mode 100644 index 0000000000..b3bf1b62e6 --- /dev/null +++ b/tests/automation/fd-limit/test_app/package.json @@ -0,0 +1,5 @@ +{ + "name": "fd-limit-test-app", + "main": "index.html", + "single-instance": false +} diff --git a/tests/automation/filevalue/index.html b/tests/automation/filevalue/index.html new file mode 100644 index 0000000000..e6c61c7b2a --- /dev/null +++ b/tests/automation/filevalue/index.html @@ -0,0 +1,16 @@ + + + + + file value test + + +

File value test

+ + + + + + + + diff --git a/tests/automation/filevalue/internal/index.html b/tests/automation/filevalue/internal/index.html new file mode 100644 index 0000000000..f13e4c01a2 --- /dev/null +++ b/tests/automation/filevalue/internal/index.html @@ -0,0 +1,36 @@ + + + + + test setting file input value + + + + + + + diff --git a/tests/automation/filevalue/internal/package.json b/tests/automation/filevalue/internal/package.json new file mode 100644 index 0000000000..602dd5d3e4 --- /dev/null +++ b/tests/automation/filevalue/internal/package.json @@ -0,0 +1,4 @@ +{ + "name":"nw-file-input-value", + "main":"index.html" +} \ No newline at end of file diff --git a/tests/automation/filevalue/internal/testfile b/tests/automation/filevalue/internal/testfile new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/automation/filevalue/mocha_test.js b/tests/automation/filevalue/mocha_test.js new file mode 100644 index 0000000000..9994fa8aed --- /dev/null +++ b/tests/automation/filevalue/mocha_test.js @@ -0,0 +1,43 @@ + +var path = require('path'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + +describe('file input',function(){ + describe('set value',function(){ + + var server; + + before(function(done) { + server = createTCPServer(13013); + done(); + }); + + after(function () { + server.close(); + }); + + it('the value should be set without exception',function(done){ + this.timeout(0); + var child = spawnChildProcess(path.join(curDir, 'internal')); + + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + setTimeout(function() { + child.kill(); + if (data == 'mytestfile') + done(); + else + done('the value of the file input is not set correctly'); + }, 2000); + }); + }); + + }); + + }); + +}); + + diff --git a/tests/automation/filevalue/package.json b/tests/automation/filevalue/package.json new file mode 100644 index 0000000000..88c0905e3c --- /dev/null +++ b/tests/automation/filevalue/package.json @@ -0,0 +1,4 @@ +{ + "name":"filevalue_wrapper", + "main":"index.html" +} diff --git a/tests/automation/globals.js b/tests/automation/globals.js new file mode 100644 index 0000000000..24ac6fbad1 --- /dev/null +++ b/tests/automation/globals.js @@ -0,0 +1,5 @@ +var fs = require('fs'); + +exports.tests_dir = fs.realpathSync('.'); + + diff --git a/tests/automation/inject-js/index.html b/tests/automation/inject-js/index.html new file mode 100644 index 0000000000..d2395f274d --- /dev/null +++ b/tests/automation/inject-js/index.html @@ -0,0 +1,16 @@ + + + + + Inject js test + + +

Inject js test

+ + + + + + + + diff --git a/tests/automation/inject-js/internal/end.js b/tests/automation/inject-js/internal/end.js new file mode 100644 index 0000000000..613f42bcdb --- /dev/null +++ b/tests/automation/inject-js/internal/end.js @@ -0,0 +1,2 @@ +var result = result ||[]; +result.push('inject-js-end'); \ No newline at end of file diff --git a/tests/automation/inject-js/internal/index.html b/tests/automation/inject-js/internal/index.html new file mode 100644 index 0000000000..354175ced9 --- /dev/null +++ b/tests/automation/inject-js/internal/index.html @@ -0,0 +1,19 @@ + + + + + + + + + + diff --git a/tests/automation/inject-js/internal/package.json b/tests/automation/inject-js/internal/package.json new file mode 100644 index 0000000000..d36df79c46 --- /dev/null +++ b/tests/automation/inject-js/internal/package.json @@ -0,0 +1,6 @@ +{ + "name":"nw_1403514049", + "main":"index.html", + "inject-js-end":"./end.js", + "inject-js-start":"./start.js" +} \ No newline at end of file diff --git a/tests/automation/inject-js/internal/start.js b/tests/automation/inject-js/internal/start.js new file mode 100644 index 0000000000..5dd62d1045 --- /dev/null +++ b/tests/automation/inject-js/internal/start.js @@ -0,0 +1,6 @@ +var result = result ||[]; +result.push('inject-js-start'); + +window.onload = function(){ + result.push('onload') +}; \ No newline at end of file diff --git a/tests/automation/inject-js/mocha_test.js b/tests/automation/inject-js/mocha_test.js new file mode 100644 index 0000000000..025ea1377b --- /dev/null +++ b/tests/automation/inject-js/mocha_test.js @@ -0,0 +1,54 @@ +var path = require('path'); +var assert = require('assert'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + + +describe('inject-js',function(){ + + var server, child, result; + + before(function(done) { + this.timeout(0); + server = createTCPServer(13013); + child = spawnChildProcess(path.join(curDir, 'internal')); + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + result = JSON.parse(data); + child.kill(); + done(); + }); + }); + + }); + + after(function () { + server.close(); + }); + + + it('result.length should equal 4',function(done){ + assert.notEqual(result,null); + assert.equal(result.length,4); + done(); + }); + it('inject-js-start should run first',function(done){ + assert.equal(result[0],'inject-js-start'); + done(); + }); + it('document script should run after inject-js-start',function(done){ + assert.equal(result[1],'script-start'); + done(); + }); + it('inject-js-end should before window.onload',function(done){ + assert.equal(result[2],'inject-js-end'); + done(); + }); + it('inject-js-start should run last',function(done){ + assert.equal(result[3],'onload'); + done(); + }); +}); + + diff --git a/tests/automation/inject-js/package.json b/tests/automation/inject-js/package.json new file mode 100644 index 0000000000..2d328d1b95 --- /dev/null +++ b/tests/automation/inject-js/package.json @@ -0,0 +1,4 @@ +{ + "name":"injectjs_wrapper", + "main":"index.html" +} diff --git a/tests/automation/loaded_event/index.html b/tests/automation/loaded_event/index.html new file mode 100644 index 0000000000..d31b76187a --- /dev/null +++ b/tests/automation/loaded_event/index.html @@ -0,0 +1,16 @@ + + + + + loaded_event test + + +

Loaded event test

+ + + + + + + + diff --git a/tests/automation/loaded_event/internal/index.html b/tests/automation/loaded_event/internal/index.html new file mode 100644 index 0000000000..132bd7454e --- /dev/null +++ b/tests/automation/loaded_event/internal/index.html @@ -0,0 +1,44 @@ + + + + + + + + + + diff --git a/tests/automation/loaded_event/internal/package.json b/tests/automation/loaded_event/internal/package.json new file mode 100644 index 0000000000..7cd4843f99 --- /dev/null +++ b/tests/automation/loaded_event/internal/package.json @@ -0,0 +1,4 @@ +{ + "name": "nw-demo", + "main": "index.html" +} diff --git a/tests/automation/loaded_event/internal/popup.html b/tests/automation/loaded_event/internal/popup.html new file mode 100644 index 0000000000..c032c597e7 --- /dev/null +++ b/tests/automation/loaded_event/internal/popup.html @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/tests/automation/loaded_event/mocha_test.js b/tests/automation/loaded_event/mocha_test.js new file mode 100644 index 0000000000..571fa4c71d --- /dev/null +++ b/tests/automation/loaded_event/mocha_test.js @@ -0,0 +1,47 @@ +var path = require('path'); +var assert = require('assert'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + +describe('loaded event', function() { + + var server, child, result = false; + + before(function(done) { + server = createTCPServer(13013); + done(); + }); + + after(function () { + server.close(); + }); + + it('loaded event can been fired', + function(done) { + this.timeout(0); + + child = spawnChildProcess(path.join(curDir, 'internal')); + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + result = true; + child.kill(); + done(); + }); + }); + + setTimeout(function(){ + if (!result) { + child.close(); + //child.app.kill(); + //child.removeConnection(); + done('loaded evenet does not been fired'); + } + }, 3000); + //child.app.stderr.on('data', function(d){ console.log ('app' + d);}); + + }) + + +}) + diff --git a/tests/automation/loaded_event/package.json b/tests/automation/loaded_event/package.json new file mode 100644 index 0000000000..beccf58ec0 --- /dev/null +++ b/tests/automation/loaded_event/package.json @@ -0,0 +1,4 @@ +{ + "name":"loaded_event_wrapper", + "main":"index.html" +} diff --git a/tests/automation/menu/index.html b/tests/automation/menu/index.html new file mode 100644 index 0000000000..121999a48b --- /dev/null +++ b/tests/automation/menu/index.html @@ -0,0 +1,16 @@ + + + + + Menu test + + +

Menu test

+ + + + + + + + diff --git a/tests/automation/menu/mocha_test.js b/tests/automation/menu/mocha_test.js new file mode 100644 index 0000000000..780846e5ad --- /dev/null +++ b/tests/automation/menu/mocha_test.js @@ -0,0 +1,83 @@ +var gui = require('nw.gui'); +var assert = require('assert'); + + +describe('Menu', function(){ + + describe('#append()', function(){ + it('should append a value', function(){ + var menu = new gui.Menu(); + menu.append(new gui.MenuItem({ label:'Item 1'})); + assert.equal(menu.items[0].label, 'Item 1'); + }) + + }) + + describe('#.length', function(){ + it('should return correct value', function(){ + var menu = new gui.Menu(); + menu.append(new gui.MenuItem({label : 'Item 1'})); + menu.append(new gui.MenuItem({label : 'Item 2'})); + assert.equal(menu.items.length, 2); + }) + }) + + describe('#insert()', function(){ + var menu = new gui.Menu(); + + it('should adject .length', function(){ + menu.append(new gui.MenuItem({label : 'Item 1'})); + menu.append(new gui.MenuItem({label : 'Item 2'})); + menu.insert(new gui.MenuItem({label : 'Item 0'}), 1); + assert.equal(menu.items.length, 3); + }) + + it('new value should be added in', function(){ + assert.equal(menu.items[1].label, 'Item 0'); + }) + + it('the origin value', function(){ + assert.equal(menu.items[2].label, 'Item 2'); + }) + }) + + describe('#removeAt()', function(){ + var menu = new gui.Menu(); + it('should adject .length', function(){ + menu.append(new gui.MenuItem({label : 'Item 0'})); + menu.append(new gui.MenuItem({label : 'Item 1'})); + menu.append(new gui.MenuItem({label : 'Item 2'})); + menu.removeAt(1); + assert.equal(menu.items.length, 2); + }); + + it('the next value', function(){ + assert.equal(menu.items[1].label, 'Item 2'); + }) + }) + + describe('#remove()', function(){ + var menu = new gui.Menu(); + menu.append(new gui.MenuItem({label : 'Item 0'})); + menu.append(new gui.MenuItem({label : 'Item 1'})); + var removedItem = new gui.MenuItem({label : 'Item Deleted'}); + menu.insert(removedItem, 1); + it('before delete', function(){ + assert.equal(menu.items.length, 3); + assert.equal(menu.items[1].label, 'Item Deleted'); + }) + + it('after delete', function(){ + menu.remove(removedItem); + assert.equal(menu.items.length, 2); + assert.equal(menu.items[1].label, 'Item 1'); + + }) + }) + + describe('#popup(), not implentment',function(){ + }) + +}) + + diff --git a/tests/automation/menu/package.json b/tests/automation/menu/package.json new file mode 100644 index 0000000000..899ddeef90 --- /dev/null +++ b/tests/automation/menu/package.json @@ -0,0 +1,4 @@ +{ + "name":"Menu_wrapper", + "main":"index.html" +} diff --git a/tests/automation/menu_item/icon1.png b/tests/automation/menu_item/icon1.png new file mode 100644 index 0000000000..010fc2dd2b Binary files /dev/null and b/tests/automation/menu_item/icon1.png differ diff --git a/tests/automation/menu_item/index.html b/tests/automation/menu_item/index.html new file mode 100644 index 0000000000..afa4493aa7 --- /dev/null +++ b/tests/automation/menu_item/index.html @@ -0,0 +1,16 @@ + + + + + Menu item test + + +

Menu item test

+ + + + + + + + diff --git a/tests/automation/menu_item/mocha_test.js b/tests/automation/menu_item/mocha_test.js new file mode 100644 index 0000000000..fcac27477d --- /dev/null +++ b/tests/automation/menu_item/mocha_test.js @@ -0,0 +1,148 @@ +var gui = require('nw.gui'); +var assert = require('assert'); + +describe('MenuItem', function(){ + var menu; + beforeEach(function(){ + menu = new gui.Menu(); + }) + + describe('.type', function(){ + + it('should be separator', function(){ + menu.append(new gui.MenuItem({ label : 'Item 1', type : 'separator' })); + assert.equal(menu.items.length, 1); + assert.equal(menu.items[0].type, 'separator'); + }) + + it('should be normal', function(){ + menu.append(new gui.MenuItem({ label : 'Item 1' })); + assert.equal(menu.items.length, 1); + assert.equal(menu.items[0].type, 'normal'); + }) + + it('should be checkbox', function(){ + menu.append(new gui.MenuItem({ label : 'Item 1', type : 'checkbox' })); + assert.equal(menu.items.length, 1); + assert.equal(menu.items[0].type, 'checkbox'); + }) + + }) + + describe('.label', function(){ + it('set label', function(){ + menu.append(new gui.MenuItem({ label : 'Item 1'})); + assert.equal(menu.items.length, 1); + assert.equal(menu.items[0].label, 'Item 1'); + + }) + + it('after change',function(){ + menu.append(new gui.MenuItem({ label : 'Item 1'})); + assert.equal(menu.items.length, 1); + menu.items[0].label = 'Item Mama'; + assert.equal(menu.items[0].label, 'Item Mama'); + }) + + }) + + describe('.icon', function(){ + it('set menu icon', function(){ + menu.append(new gui.MenuItem({ label : 'Item icon' })); + assert.equal(menu.items.length, 1); + menu.items[0].icon = 'icon1.png'; + assert.equal(menu.items[0].icon, 'icon1.png'); + + }) + + it('clear menu icon', function(){ + menu.append(new gui.MenuItem({ label : 'Item icon' })); + menu.items[0].icon = 'icon1.png'; + menu.items[0].icon = ''; + assert.equal(menu.items[0].icon, ''); + }) + }) + + describe('.tooltip', function(){ + it('set tooltip', function(){ + menu.append(new gui.MenuItem({ label : 'Item 0', tooltip : 'tooltip' })); + assert.equal(menu.items[0].tooltip, 'tooltip'); + }) + + it('change tooltip', function(){ + menu.append(new gui.MenuItem({ label : 'Item 0', tooltip : 'tooltip' })); + menu.items[0].tooltip = ''; + assert.equal(menu.items[0].tooltip, ''); + }) + }) + + + describe('.checked', function(){ + it('set enabled', function(){ + menu.append(new gui.MenuItem({ type : 'checkbox', label : 'Item 0', checked : true })); + assert.equal(menu.items[0].checked, true); + }) + + it('change checked', function(){ + menu.append(new gui.MenuItem({ type : 'checkbox', label : 'Item 0', checked: true })); + menu.items[0].checked = false; + assert.equal(menu.items[0].checked, false); + + }) + }) + + + describe('.enabled', function(){ + it('set enabled', function(){ + menu.append(new gui.MenuItem({ type : 'checkbox', label : 'Item 0', enabled : true })); + assert.equal(menu.items[0].enabled, true); + }) + + it('change enabled', function(){ + menu.append(new gui.MenuItem({ type : 'checkbox', label : 'Item 0', enabled : true })); + menu.items[0].enabled = false; + assert.equal(menu.items[0].enabled, false); + + }) + }) + + describe('.submenu', function(){ + it('set submenu', function(){ + var submenu = new gui.Menu(); + submenu.append(new gui.MenuItem({ type: 'checkbox', label: 'Sub 1', checked: true, enabled: false })); + submenu.append(new gui.MenuItem({ type: 'checkbox', label: 'Sub 2', checked: true, enabled: false })); + submenu.append(new gui.MenuItem({ type: 'checkbox', label: 'Sub 3', checked: true, enabled: false })); + submenu.append(new gui.MenuItem({ type: 'checkbox', label: 'Sub 4', checked: true, enabled: false })); + menu.append(new gui.MenuItem({ label: 'I have submenu', submenu: submenu })); + + assert.equal(menu.items.length, 1); + assert.equal(menu.items[0].label, 'I have submenu'); + assert.equal(menu.items[0].submenu, submenu); + assert.equal(submenu.items.length, 4); + }) + }) + + describe('#click()', function(){ + + var menu_item = new gui.MenuItem({label : 'item 1'}); + + + it('before click',function(){ + assert.equal(menu_item.label, 'item 1'); + }) + + it('after click',function(done){ + menu_item.on('click', function(){ + menu_item.label = '2'; + assert.equal(menu_item.label, '2'); + done(); + }) + menu_item.emit('click'); + + }) + + + + }) + +}) diff --git a/tests/automation/menu_item/package.json b/tests/automation/menu_item/package.json new file mode 100644 index 0000000000..b4e9a364b2 --- /dev/null +++ b/tests/automation/menu_item/package.json @@ -0,0 +1,4 @@ +{ + "name":"Menu_item_wrapper", + "main":"index.html" +} diff --git a/tests/automation/mocha_test.js b/tests/automation/mocha_test.js new file mode 100644 index 0000000000..db7f86f779 --- /dev/null +++ b/tests/automation/mocha_test.js @@ -0,0 +1,193 @@ +var fs = require('fs'); +var path = require('path'); +var child_process = require('child_process'); +var colors = require('colors'); +//var toMarkdown = require('to-markdown').toMarkdown; + +var g_quiet = false; +var g_outputDir; +var g_outputLog; +var g_format; + +function print(str, err) { + if (!g_quiet) { + if (err) { + console.error(str); + } else { + console.log(str); + } + } + if (g_outputLog) { + fs.appendFile(g_outputLog, str + '\n'); + } +} + +function preTest() { + print(colors.green('===============')); + print(colors.green('Begin testing')); + print(colors.green('===============')); +} + +function postTest() { + print(colors.green('===============')); + print(colors.green('End testing')); + print(colors.green('===============')); + + if (g_format === 'markdown' && fs.existsSync(g_outputDir)) { + // convert xml to md for better reading + var subFiles = fs.readdirSync(g_outputDir); + for (var i = 0, len = subFiles.length; i < len; i++) { + var filePath = subFiles[i]; + if (path.extname(filePath) === '.xml') { + var conent = fs.readFileSync(path.join(g_outputDir, filePath)); + content = encodeURIComponent(conent); + var mdData = toMarkdown(content); + fs.writeFileSync(path.join(g_outputDir, path.basename(filePath)+'.md'), mdData); + } + } + } +} + +function performTests(config) { + + g_quiet = config.quiet; + + var curPath = fs.realpathSync('.'); + if (!fs.existsSync('output')) + fs.mkdirSync('output'); + g_outputDir = path.join(curPath, 'output', (new Date()).toUTCString()); + if (!fs.existsSync(g_outputDir)) + fs.mkdirSync(g_outputDir); + g_outputLog = path.join(g_outputDir, 'summary.log'); + print('The test results will output to : ' + colors.underline(g_outputDir) + '\n'); + + var subDirs = fs.readdirSync(curPath); + var testSuites = []; + var isExcluded = function(testsuite) { + for (var i = 0, len = config.exclude.length; i < len; i++) { + if (config.exclude[i] === testsuite) + return true; + } + return false; + }; + + if (config.single && config.single !== '' && fs.existsSync(path.join(curPath, config.single))) { + testSuites.push(config.single); + } else { + for (var i = 0, len = subDirs.length; i < len; i++) { + var subDir = subDirs[i]; + var status = fs.statSync(path.join(curPath, subDir)); + if (status && status.isDirectory() && !isExcluded(subDir)) { + testSuites.push(subDir); + } + } + } + + /* ============ running test cases ================ */ + if (testSuites.length === 0) { + print('No test cases are found in ' + curPath); + return; + } + preTest(); + g_format = config.format; + var arg_timeout = config.timeout ? ['--timeout', config.timeout] : []; + var arg_slow = config.slow ? ['--slow', config.slow] : []; + var argv = undefined; + var cmd = undefined; + var child = undefined; + var env = process.env; + var timerId = undefined; + var children = []; + + var timeout = parseInt(config.timeout); + if (Number.isNaN(timeout) || timeout <= 0) + timeout = 5000; + + env['out_dir'] = g_outputDir; + + var spawTestProcessByNW = function(testcase) { + cmd = 'nw'; + argv = [testcase]; + print('Running: ' + colors.underline(testcase)); + child = child_process.spawn(cmd, argv, {env: env}); + child.testcase = testcase; + return child; + }; + + var spawTestProcessByMocha = function(testcase) { + cmd = 'mocha'; + argv = [testcase + path.sep + 'mocha_test'].concat(['-R', 'xunit-file']) + .concat(arg_timeout).concat(arg_slow); + env['XUNIT_FILE'] = path.join(g_outputDir, testcase + '.xml'); + print('Running:' + colors.underline(testcase)); + child = child_process.spawn(cmd, argv, {env: env}); + child.testcase = testcase; + return child; + }; + + var curIdx = -1; + var curChildProcess = undefined; + var prefix; + +// for (var i =0, len = testSuites.length; i < len; i++ ) { +// console.log('Test suite ' + i + ':' + testSuites[i]); +// } + + var runNextTestByNW = function() { + curIdx += 1; + if (curIdx < testSuites.length) { + resetTimeout(curIdx); + curChildProcess = spawTestProcessByNW(testSuites[curIdx]); + children.push(curChildProcess); + curChildProcess.on('exit', function(code, signal) { + (function(p, c) { + prefix = (code === 0 ? colors.green('[Success]') : colors.red('[Failure]')); + print(prefix + ' : ' + colors.underline(p.testcase) + ' with exit code ' + c); + clearTimeout(timerId); + p.removeAllListeners('exit'); + p.kill('SIGKILL'); + runNextTestByNW(); + })(curChildProcess, code); + }); + } else { + if (timerId) { + clearTimeout(timerId); + timerId = undefined; + } + postTest(); + } + }; + + var resetTimeout = function(idx) { + if (timerId) + clearTimeout(timerId); + (function(idx){ + + timerId = setTimeout(function() { + + timerId = undefined; + if (idx || idx >= 0) { + var p = children[idx]; + print(colors.yellow('[Timeout]') + ' : ' + colors.underline(p.testcase)); + p.removeAllListeners('exit'); + p.kill('SIGKILL'); // 'SIGKILL', 'SIGTERM' + p = undefined; + runNextTestByNW(); + } + }, timeout); + + })(idx); + + }; + + runNextTestByNW(); + +} // end of performTests() + +var content = fs.readFileSync('./config.json'); +if (content) + performTests(JSON.parse(content)); +else + print('Failed to read configuration.', true); + + diff --git a/tests/automation/new-instance/index.html b/tests/automation/new-instance/index.html new file mode 100644 index 0000000000..a3fe74b68f --- /dev/null +++ b/tests/automation/new-instance/index.html @@ -0,0 +1,16 @@ + + + + + New Instance test + + +

New Instance test

+ + + + + + + + diff --git a/tests/automation/new-instance/internal/index.html b/tests/automation/new-instance/internal/index.html new file mode 100644 index 0000000000..6ee8e7dec4 --- /dev/null +++ b/tests/automation/new-instance/internal/index.html @@ -0,0 +1,33 @@ + + + + +The pid of main page is: + +
+The pid of new-instance page is: + + + + + + diff --git a/tests/automation/new-instance/internal/package.json b/tests/automation/new-instance/internal/package.json new file mode 100644 index 0000000000..7cd4843f99 --- /dev/null +++ b/tests/automation/new-instance/internal/package.json @@ -0,0 +1,4 @@ +{ + "name": "nw-demo", + "main": "index.html" +} diff --git a/tests/automation/new-instance/internal/popup.html b/tests/automation/new-instance/internal/popup.html new file mode 100644 index 0000000000..b76e8af321 --- /dev/null +++ b/tests/automation/new-instance/internal/popup.html @@ -0,0 +1,21 @@ + + + + +The pid of page is: + + + + + + diff --git a/tests/automation/new-instance/mocha_test.js b/tests/automation/new-instance/mocha_test.js new file mode 100644 index 0000000000..efc6a1783f --- /dev/null +++ b/tests/automation/new-instance/mocha_test.js @@ -0,0 +1,51 @@ +var path = require('path'); +var assert = require('assert'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + +describe('new-instance', function() { + var server, child, result = false; + + before(function(done) { + server = createTCPServer(13013); + done(); + }); + + after(function () { + server.close(); + }); + + it('new window has different pid', function(done) { + this.timeout(0); + var result = false; + var times = 0; + var pid1, pid2; + + child = spawnChildProcess(path.join(curDir, 'internal')); + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + if (times == 0) { + pid1 = data; + times += 1; + } else { + pid2 = data; + + if (pid1 != pid2) { + done(); + } else { + done('they are in the same process'); + } + child.kill(); + } + + }); + }); + + + }); + + +}); + + diff --git a/tests/automation/new-instance/package.json b/tests/automation/new-instance/package.json new file mode 100644 index 0000000000..da9799222d --- /dev/null +++ b/tests/automation/new-instance/package.json @@ -0,0 +1,4 @@ +{ + "name":"New_instance_wrapper", + "main":"index.html" +} diff --git a/tests/automation/node-main/index.html b/tests/automation/node-main/index.html new file mode 100644 index 0000000000..8b479f5b7d --- /dev/null +++ b/tests/automation/node-main/index.html @@ -0,0 +1,16 @@ + + + + + node main test + + +

Node main test

+ + + + + + + + diff --git a/tests/automation/node-main/mocha_test.js b/tests/automation/node-main/mocha_test.js new file mode 100644 index 0000000000..befaedcf6e --- /dev/null +++ b/tests/automation/node-main/mocha_test.js @@ -0,0 +1,98 @@ +var path = require('path'); +var assert = require('assert'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + +describe('node-main', function() { + + var server; + + before(function(done) { + server = createTCPServer(13013); + done(); + }); + + after(function () { + server.close(); + }); + + describe('create http server in node-main', function() { + it('nw should not close by itself after show devtool', + function(done) { + this.timeout(0); + var result = false; + + var child = spawnChildProcess(path.join(curDir, '..', 'show_devtool_after_http_server_created_in_node_main')); + + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + result = JSON.parse(data); + child.kill(); + if (result.success) done(); + else done('error'); + result = true; + server.removeAllListeners('connection'); + }); + }); + + + setTimeout(function(){ + if (!result) { + done('nw close by itself') + } + }, 3000); + //child.app.stderr.on('data', function(d){ console.log ('app' + d);}); + + }); + }); + + + + describe('call require() in app', function() { + it('nw should can require modules', function(done){ + this.timeout(0); + var result = false; + var child = spawnChildProcess(path.join(curDir, '..', 'call_require_with_node-main_set')); + + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + result = JSON.parse(data); + child.kill(); + if (result.ok) done(); + else done('error:'+result.error); + result = true; + server.removeAllListeners('connection'); + }); + }); + + }); + }); + + describe('reference node-main module',function(){ + it('nw should be able to reference node-main module',function(done){ + this.timeout(0); + + var result = false; + var child = spawnChildProcess(path.join(curDir, '..', 'reference-node-main')); + + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + result = JSON.parse(data); + child.kill(); + assert.equal(result,true); + done(); + server.removeAllListeners('connection'); + }); + }); + + }); + }); + + + +}); + + diff --git a/tests/automation/node-main/package.json b/tests/automation/node-main/package.json new file mode 100644 index 0000000000..918bb25bc8 --- /dev/null +++ b/tests/automation/node-main/package.json @@ -0,0 +1,4 @@ +{ + "name":"Node_main_wrapper", + "main":"index.html" +} diff --git a/tests/automation/node-remote/index.html b/tests/automation/node-remote/index.html new file mode 100644 index 0000000000..4ec84c0284 --- /dev/null +++ b/tests/automation/node-remote/index.html @@ -0,0 +1,93 @@ + + + + + + + +
+ Can remote page + http://127.0.0.1:8123/node_remote_test.html + call Node: + +
+ +
+ Can remote page + http://127.0.0.1:8124/node_remote_test.html + call Node: + +
+ + + diff --git a/tests/automation/node-remote/mocha_test.js b/tests/automation/node-remote/mocha_test.js new file mode 100644 index 0000000000..737c129899 --- /dev/null +++ b/tests/automation/node-remote/mocha_test.js @@ -0,0 +1,70 @@ +var spawn = require('child_process').spawn; +var path = require('path'); +var net = require('net'); +var server = global.server; +var cb; + +describe('node-remote', function() { + describe('enable remote site http://127.0.0.1:8123/', function() { + var app; + var exec_argv; + var socket; + before(function(done) { + //this.timeout(0); + exec_argv = [path.join(global.tests_dir, 'node-remote'), + '--port', + global.port]; + + if (global.auto) exec_argv.push('--auto'); + + server.on('connection', cb = function(s){ + socket = s; + s.setEncoding('utf8'); + done(); + socket.on('error', function(e) { + console.log(e); + }); + }); + app = spawn(process.execPath, exec_argv); + }) + + after(function(done) { + this.timeout(0); + app.kill(); + server.removeListener('connection', cb); + done(); + }) + + afterEach(function(){ + socket.removeAllListeners('data'); + }) + + it('http://127.0.0.1:8123/node_remote_test.html%20should%20be%20able%20call%20Node', + function(done) { + this.timeout(0); + socket.on('data', function(data) { + if (data == 'ok'){ + done(); + } else { + done(data); + } + + }); + socket.write('8123'); + }) + + it('http://127.0.0.1:8124/node_remote_test.html%20should%20not%20be%20able%20call%20Node', + function(done) { + this.timeout(0); + socket.on('data', function(data) { + if (data == 'ok'){ + done('should not call node'); + } else { + done(); + } + + }); + socket.write('8124'); + }) + }) +}) diff --git a/tests/automation/node-remote/package.json b/tests/automation/node-remote/package.json new file mode 100644 index 0000000000..e79fa87ac3 --- /dev/null +++ b/tests/automation/node-remote/package.json @@ -0,0 +1,8 @@ +{ + "name": "nw-demo", + "main": "index.html", + "user-agent": "%name-a-b", + "node-remote": "127.0.0.1:8123", + "version": "1.0", + "window": { "show" : false } +} diff --git a/tests/automation/node/index.html b/tests/automation/node/index.html new file mode 100644 index 0000000000..a454c5e92f --- /dev/null +++ b/tests/automation/node/index.html @@ -0,0 +1,16 @@ + + + + + node test + + +

Node test

+ + + + + + + + diff --git a/tests/automation/node/message_channel_test.js b/tests/automation/node/message_channel_test.js new file mode 100644 index 0000000000..2aac0a92a1 --- /dev/null +++ b/tests/automation/node/message_channel_test.js @@ -0,0 +1,7 @@ +exports.run = function(done) { + var mc = new window.MessageChannel() + mc.port1.onmessage = function(m) { + done(); + } + mc.port2.postMessage("HELLO"); +} diff --git a/tests/automation/node/mocha_test.js b/tests/automation/node/mocha_test.js new file mode 100644 index 0000000000..f853a5218f --- /dev/null +++ b/tests/automation/node/mocha_test.js @@ -0,0 +1,258 @@ +var path = require('path'); +var assert = require('assert'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + + +/// 1 +describe('require', function() { + describe('type', function() { + it('is function', function() { + assert.equal(typeof global.require, 'function'); + }); + }); + + describe('members', function() { + it('have resolve', function() { + assert.equal(typeof global.require.resolve, 'function'); + }); + + it('have main', function() { + assert.equal(typeof global.require.main, 'object'); + }); + + it('have extensions', function() { + assert.equal(typeof global.require.extensions, 'object'); + }); + + it('have registerExtension', function() { + assert.equal(typeof global.require.registerExtension, 'function'); + }); + + it('have cache', function() { + assert.equal(typeof global.require.cache, 'object'); + }); + }); +}); + +////////////////// 2 +/* +describe('node', function() { + + var mocha_callback = null; + before(function() { + mocha_callback = process.listeners('uncaughtException')[1]; + process.removeListener('uncaughtException', mocha_callback); + }); + after(function(){ + process.on('uncaughtException',mocha_callback); + }) + + describe('process', function() { + it('uncaughtException should have a default listener', function() { + assert.equal(process.listeners('uncaughtException').length, 1); + }); + + it('throwing uncaughtException should not show error page when having listener', function(done) { + var empty = function(e) { + process.removeListener('uncaughtException', empty); + done(); + }; + process.on('uncaughtException', empty); + process.nextTick(function() { + throw new String('If you see this then uncaughtException test failed'); + }); + }); + }); +}); +*/ +////////////// 3 + +describe('module', function() { + describe('javascript modules', function() { + var path = require('path'); + + it('require by path', function() { + var module1_path = path.join(curDir, 'module1.js'); + assert.equal(require(module1_path).test(), 'module1'); + }); + + it('simple file module', function() { + var module3_path = path.join(curDir, 'node_modules', 'module3'); + assert.equal(require(module3_path).test(), 'module3'); + }); + + it('space in path', function() { + var module4_path = path.join(curDir, 'node_modules', 'module4 space'); + assert.equal(require(module4_path).test(), 'module4'); + }); + }); + + describe('built-in modules', function() { + var fs = require('fs'); + var http = require('http'); + var path = require('path'); + var exec = require('child_process').exec; + + it('execute child process', function(done) { + var child = exec('echo echo back', function (error, stdout, stderr) { + assert.equal(error, null); + assert.equal(stdout, process.platform == 'win32' ? 'echo back\r\n' : 'echo back\n'); + assert.equal(stderr, ''); + done(); + }); + }); + + it('Buffer', function() { + assert.equal(String(Buffer('test')), 'test'); + }); +/* + it('http server and client', function(done) { + var server = http.createServer(function (req, res) { + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end('hello'); + }).listen(10086); + process.nextTick(function() { + var count = 0; + for (var i = 0; i < 5; ++i) { + http.get("http://127.0.0.1:10086/", function(res) { + var data = ''; + res.on('end', function(e) { + if (data == 'hello') + ++count; + }).on('data', function(chunk) { + data += chunk; + }); + }).on('error', function() { + assert.equal(true, false); + }); + } + setTimeout(function() { + assert.equal(count, 5); + server.close(); + done(); + }, 50); + }); + }); +*/ + }); + +/* + describe('native modules (long-to-run)', function() { + it('bignum should work on posix environment', function() { + if (process.platform != 'win32') { + var bignum = require('./node_modules/bignum'); + assert.equal(bignum('782910138827292261791972728324982').sub('182373273283402171237474774728373').div(8), '75067108192986261319312244199576'); + } + }); + + it('native modules without handle scope', function() { + require('./node_modules/nw_test_loop_without_handle'); + }); + + it('native modules should work', function() { + var nativeModules = new Array("dtrace-provider", "ref", "lame"); + for (var i = 0; i < nativeModules.length; i++) + assert.equal((typeof require(nativeModules[i])), "object"); + }); + }); +*/ + +}); + +////////////////////// 4 + +describe('performance', function() { + describe('timers', function() { + it('setTimeout in webkit and node.js should be accurate', function(done) { + var flag1, flag2; + global.setTimeout(function() { flag1 = true; }, 10); + window.setTimeout(function() { flag2 = true; }, 10); + + setTimeout(function() { + assert.equal(flag1, true); + assert.equal(flag2, true); + done(); + }, 50); + }); + + it('setInterval int webkit and node.js should be accurate', function(done) { + var count1 = 0; + var count2 = 0;; + global.setInterval(function() { ++count1; }, 10); + window.setInterval(function() { ++count2; }, 10); + + setTimeout(function() { + assert.equal(count1 > 3, true); + assert.equal(count2 > 3, true); + done(); + }, 50); + }); + }); + + + describe('IO', function() { + var fs = require('fs'); + var path = require('path'); + var exec = require('child_process').exec; + var tar = require('tar'); + var result = false; + + it('file reading should be quick even when we have a child process running', function(done) { + this.timeout(0); + var mochaPath = path.join(curDir, 'mocha_test.js'); + fs.readFile(mochaPath, 'utf8', function(err, data) { + result = true; + done(); + }); + + setTimeout(function() { + if (!result) done('timeout'); + }, 1000); + }); + + it('untar a file should be quick', function(done) { + this.timeout(100); + var testPath = path.join(curDir, 'test.tar'); + fs.createReadStream(testPath) + .pipe(tar.Extract({ path: 'tmp' })) + .on('error', function (er) { + assert.equal(false, true); + }) + .on('end', function () { + var content = fs.readFileSync('tmp/text', 'utf8'); + assert.equal(content, 'I love luyao\n'); // who is luyao ? + fs.unlinkSync('tmp/text'); + fs.rmdirSync('tmp'); + done(); + }); + }); + + }); + + +}); + +///////////////////////////// 5 + +describe('browser', function() { + describe('security', function() { + it('cross domain call in file:// should succeed', function(done) { + window.$.get('http://baidu.com/', function(data, status) { + assert.equal(status, 'success'); + assert.notEqual(data, null); + done(); + }); + }); + }); + + describe('render crashes', function() { + it('MessageChannel should not crash', function(done) { + var testPath = path.join(curDir, 'message_channel_test'); + var test = require(testPath); + test.run(done); // force to run in node context. + }); + }); +}); + + diff --git a/tests/automation/node/module1.js b/tests/automation/node/module1.js new file mode 100644 index 0000000000..fe1099f65c --- /dev/null +++ b/tests/automation/node/module1.js @@ -0,0 +1,3 @@ +exports.test = function() { + return "module1"; +} diff --git a/tests/automation/node/package.json b/tests/automation/node/package.json new file mode 100644 index 0000000000..9244205ca5 --- /dev/null +++ b/tests/automation/node/package.json @@ -0,0 +1,4 @@ +{ + "name":"Node_wrapper", + "main":"index.html" +} diff --git a/tests/automation/node/test.tar b/tests/automation/node/test.tar new file mode 100644 index 0000000000..e087c9cff2 Binary files /dev/null and b/tests/automation/node/test.tar differ diff --git a/tests/automation/nw-in-mem/index.html b/tests/automation/nw-in-mem/index.html new file mode 100644 index 0000000000..a894d0e6c6 --- /dev/null +++ b/tests/automation/nw-in-mem/index.html @@ -0,0 +1,16 @@ + + + + + nw_in_mem test + + +

nw in men test

+ + + + + + + + diff --git a/tests/automation/nw-in-mem/mocha_test.js b/tests/automation/nw-in-mem/mocha_test.js new file mode 100644 index 0000000000..91c87bd3ef --- /dev/null +++ b/tests/automation/nw-in-mem/mocha_test.js @@ -0,0 +1,42 @@ +var os = require('os'); +var path = require('path'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + +var exec = require('child_process').exec; +var spawn = require('child_process').spawn; + +describe('nw in memory after quiting',function(){ + + it('nw.exe should not be in memory after gui.App.quit is called on windows',function(done){ + this.timeout(0); + if(os.platform() == "win32") { + var app = spawn(process.execPath, [path.join(curDir,'package')]) + app.on('close',function(){ + var nwname = "nw.exe"; + var nwcount = 0; + setTimeout(function() { + exec("tasklist", function(err, stdout, stderr) { + if(err){ return console.log(err); } + stdout.split('\n').filter(function(line){ + var p=line.trim().split(/\s+/),pname=p[0]; + if(pname.toLowerCase().indexOf(nwname)>=0) + nwcount++; + }); + + if(nwcount == 2) + done(); + else if(nwcount > 2) + done('nw.exe is still in memory after the quit of app'); + }) + }, 500); + }); + } + else + done(); + }); + + +}); + + diff --git a/tests/automation/nw-in-mem/package.json b/tests/automation/nw-in-mem/package.json new file mode 100644 index 0000000000..c0251d7820 --- /dev/null +++ b/tests/automation/nw-in-mem/package.json @@ -0,0 +1,4 @@ +{ + "name":"nw_in_mem_wrapper", + "main":"index.html" +} diff --git a/tests/automation/nw-in-mem/package/c2.png b/tests/automation/nw-in-mem/package/c2.png new file mode 100644 index 0000000000..ba48f98d2c Binary files /dev/null and b/tests/automation/nw-in-mem/package/c2.png differ diff --git a/tests/automation/nw-in-mem/package/c2runtime.js b/tests/automation/nw-in-mem/package/c2runtime.js new file mode 100644 index 0000000000..1ffdea04c2 --- /dev/null +++ b/tests/automation/nw-in-mem/package/c2runtime.js @@ -0,0 +1,2 @@ + +var cr={plugins_:{},behaviors:{}};"function"!==typeof Object.getPrototypeOf&&(Object.getPrototypeOf="object"===typeof"test".__proto__?function(a){return a.__proto__}:function(a){return a.constructor.prototype});(function(){function a(a,p){this.x=a;this.y=p;cr.seal(this)}function b(a,p,g,u){this.set(a,p,g,u);cr.seal(this)}function d(){this.bly=this.blx=this.bry=this.brx=this.try_=this.trx=this.tly=this.tlx=0;cr.seal(this)}function e(){this.items={};this.item_count=0;this.values_cache=[];this.cache_valid=!0;cr.seal(this)}function f(){this.sum=this.t=this.y=this.c=0;cr.seal(this)}function h(a){this.pts_cache=[];this.set_pts(a);cr.seal(this)}cr.logexport=function(a){window.console&&window.console.log&&window.console.log(a)};cr.seal=function(a){return a};cr.freeze=function(a){return a};cr.is_undefined=function(a){return"undefined"===typeof a};cr.is_number=function(a){return"number"===typeof a};cr.is_string=function(a){return"string"===typeof a};cr.isPOT=function(a){return 0a?-a:a};cr.max=function(a,p){return a>p?a:p};cr.min=function(a,p){return acr.max(c,s)||cr.max(p,u)cr.max(k,m))return!1;var f=c-a+s-g,b=k-p+m-u;a=g-a;p=u-p;c=s-c;k=m-k;m=cr.abs(p*c-k*a);a=a*b-p*f;return cr.abs(c*b-k*f)<=m&&cr.abs(a)<=m};b.prototype.set=function(a,p,g,u){this.left=a;this.top=p;this.right=g;this.bottom=u};b.prototype.width=function(){return this.right-this.left};b.prototype.height=function(){return this.bottom-this.top};b.prototype.offset=function(a,p){this.left+=a;this.top+=p;this.right+=a;this.bottom+=p;return this};b.prototype.intersects_rect=function(a){return!(a.rightthis.right||a.top>this.bottom)};b.prototype.contains_pt=function(a,p){return a>=this.left&&a<=this.right&&p>=this.top&&p<=this.bottom};cr.rect=b;d.prototype.set_from_rect=function(a){this.tlx=a.left;this.tly=a.top;this.trx=a.right;this.try_=a.top;this.brx=a.right;this.bry=a.bottom;this.blx=a.left;this.bly=a.bottom};d.prototype.set_from_rotated_rect=function(a,p){if(0===p)this.set_from_rect(a);else{var g=Math.sin(p),u=Math.cos(p),c=a.left*g,k=a.top*g,s=a.right*g,g=a.bottom*g,m=a.left*u,f=a.top*u,b=a.right*u,u=a.bottom*u;this.tlx=m-k;this.tly=f+c;this.trx=b-k;this.try_=f+s;this.brx=b-g;this.bry=u+s;this.blx=m-g;this.bly=u+c}};d.prototype.offset=function(a,p){this.tlx+=a;this.tly+=p;this.trx+=a;this.try_+=p;this.brx+=a;this.bry+=p;this.blx+=a;this.bly+=p;return this};d.prototype.bounding_box=function(a){a.left=cr.min(cr.min(this.tlx,this.trx),cr.min(this.brx,this.blx));a.top=cr.min(cr.min(this.tly,this.try_),cr.min(this.bry,this.bly));a.right=cr.max(cr.max(this.tlx,this.trx),cr.max(this.brx,this.blx));a.bottom=cr.max(cr.max(this.tly,this.try_),cr.max(this.bry,this.bly))};d.prototype.contains_pt=function(a,p){var g=this.trx-this.tlx,u=this.try_-this.tly,c=this.brx-this.tlx,k=this.bry-this.tly,s=a-this.tlx,m=p-this.tly,f=g*g+u*u,b=g*c+u*k,u=g*s+u*m,l=c*c+k*k,d=c*s+k*m,e=1/(f*l-b*b),g=(l*u-b*d)*e,f=(f*d-b*u)*e;if(0<=g&&0g+f)return!0;g=this.blx-this.tlx;u=this.bly-this.tly;f=g*g+u*u;b=g*c+u*k;u=g*s+u*m;e=1/(f*l-b*b);g=(l*u-b*d)*e;f=(f*d-b*u)*e;return 0<=g&&0g+f};d.prototype.at=function(a,p){switch(a){case 0:return p?this.tlx:this.tly;case 1:return p?this.trx:this.try_;case 2:return p?this.brx:this.bry;case 3:return p?this.blx:this.bly;case 4:return p?this.tlx:this.tly;default:return p?this.tlx:this.tly}};d.prototype.midX=function(){return(this.tlx+this.trx+this.brx+this.blx)/4};d.prototype.midY=function(){return(this.tly+this.try_+this.bry+this.bly)/4};d.prototype.intersects_segment=function(a,p,g,u){if(this.contains_pt(a,p)||this.contains_pt(g,u))return!0;var c,k,s,m,f;for(f=0;4>f;f++)if(c=this.at(f,!0),k=this.at(f,!1),s=this.at(f+1,!0),m=this.at(f+1,!1),cr.segments_intersect(a,p,g,u,c,k,s,m))return!0;return!1};d.prototype.intersects_quad=function(a){var p=a.midX(),g=a.midY();if(this.contains_pt(p,g))return!0;p=this.midX();g=this.midY();if(a.contains_pt(p,g))return!0;var u,c,k,s,m,f,b,l;for(b=0;4>b;b++)for(l=0;4>l;l++)if(p=this.at(b,!0),g=this.at(b,!1),u=this.at(b+1,!0),c=this.at(b+1,!1),k=a.at(l,!0),s=a.at(l,!1),m=a.at(l+1,!0),f=a.at(l+1,!1),cr.segments_intersect(p,g,u,c,k,s,m,f))return!0;return!1};cr.quad=d;cr.RGB=function(a,p,g){return Math.max(Math.min(a,255),0)|Math.max(Math.min(p,255),0)<<8|Math.max(Math.min(g,255),0)<<16};cr.GetRValue=function(a){return a&255};cr.GetGValue=function(a){return(a&65280)>>8};cr.GetBValue=function(a){return(a&16711680)>>16};cr.shallowCopy=function(a,p,g){for(var u in p)p.hasOwnProperty(u)&&(a[u]=p[u]);return a};cr.arrayRemove=function(a,p){var g,u;p=cr.floor(p);if(!(0>p||p>=a.length))if(0===p)a.shift();else if(p===a.length-1)a.pop();else{g=p;for(u=a.length-1;gg?g:a};cr.to_radians=function(a){return a/(180/cr.PI)};cr.to_degrees=function(a){return a*(180/cr.PI)};cr.clamp_angle_degrees=function(a){a%=360;0>a&&(a+=360);return a};cr.clamp_angle=function(a){a%=2*cr.PI;0>a&&(a+=2*cr.PI);return a};cr.to_clamped_degrees=function(a){return cr.clamp_angle_degrees(cr.to_degrees(a))};cr.to_clamped_radians=function(a){return cr.clamp_angle(cr.to_radians(a))};cr.angleTo=function(a,p,g,u){return Math.atan2(u-p,g-a)};cr.angleDiff=function(a,p){if(a===p)return 0;var g=Math.sin(a),u=Math.cos(a),c=Math.sin(p),k=Math.cos(p),g=g*c+u*k;return 1<=g?0:-1>=g?cr.PI:Math.acos(g)};cr.angleRotate=function(a,p,g){var u=Math.sin(a),c=Math.cos(a),k=Math.sin(p),s=Math.cos(p);return Math.acos(u*k+c*s)>g?0=u*c-g*k};cr.rotatePtAround=function(a,p,g,u,c,k){if(0===g)return k?a:p;var s=Math.sin(g);g=Math.cos(g);a-=u;p-=c;var m=a*s;a=a*g-p*s;p=p*g+m;return k?a+u:p+c};cr.distanceTo=function(a,p,g,u){a=g-a;p=u-p;return Math.sqrt(a*a+p*p)};cr.xor=function(a,p){return!a!==!p};cr.lerp=function(a,p,g){return a+(p-a)*g};cr.hasAnyOwnProperty=function(a){for(var p in a)if(a.hasOwnProperty(p))return!0;return!1};cr.wipe=function(a){for(var p in a)a.hasOwnProperty(p)&&delete a[p]};var k=+new Date;cr.performance_now=function(){if("undefined"!==typeof window.performance){var a=window.performance;if("undefined"!==typeof a.now)return a.now();if("undefined"!==typeof a.webkitNow)return a.webkitNow();if("undefined"!==typeof a.msNow)return a.msNow()}return Date.now()-k};e.prototype.contains=function(a){return this.items.hasOwnProperty(a.toString())};e.prototype.add=function(a){var p=a.toString();this.items.hasOwnProperty(p)||(this.items[p]=a,this.item_count++,this.cache_valid=!1);return this};e.prototype.remove=function(a){a=a.toString();this.items.hasOwnProperty(a)&&(delete this.items[a],this.item_count--,this.cache_valid=!1);return this};e.prototype.clear=function(){cr.wipe(this.items);this.item_count=0;this.values_cache.length=0;this.cache_valid=!0;return this};e.prototype.isEmpty=function(){return 0===this.item_count};e.prototype.count=function(){return this.item_count};e.prototype.update_cache=function(){if(!this.cache_valid){this.values_cache.length=this.item_count;var a,p=0;for(a in this.items)this.items.hasOwnProperty(a)&&(this.values_cache[p++]=this.items[a]);this.cache_valid=!0}};e.prototype.values=function(){this.update_cache();return this.values_cache.slice(0)};e.prototype.valuesRef=function(){this.update_cache();return this.values_cache};cr.ObjectSet=e;f.prototype.add=function(a){this.y=a-this.c;this.t=this.sum+this.y;this.c=this.t-this.sum-this.y;this.sum=this.t};f.prototype.reset=function(){this.sum=this.t=this.y=this.c=0};cr.KahanAdder=f;cr.regexp_escape=function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&")};h.prototype.set_pts=function(a){this.pts_array=a;this.pts_count=a.length/2;this.pts_cache.length=a.length;this.cache_height=this.cache_width=-1;this.cache_angle=0};h.prototype.is_empty=function(){return!this.pts_array.length};h.prototype.set_from_quad=function(a,p,g,u,c){this.pts_cache.length=8;this.pts_count=4;var k=this.pts_cache;k[0]=a.tlx-p;k[1]=a.tly-g;k[2]=a.trx-p;k[3]=a.try_-g;k[4]=a.brx-p;k[5]=a.bry-g;k[6]=a.blx-p;k[7]=a.bly-g;this.cache_width=u;this.cache_height=c};h.prototype.set_from_poly=function(a){this.pts_count=a.pts_count;cr.shallowAssignArray(this.pts_cache,a.pts_cache)};h.prototype.cache_poly=function(a,p,g){if(this.cache_width!==a||this.cache_height!==p||this.cache_angle!==g){this.cache_width=a;this.cache_height=p;this.cache_angle=g;var u,c,k,s=0,m=1,f=this.pts_array,b=this.pts_cache;0!==g&&(s=Math.sin(g),m=Math.cos(g));g=0;for(u=this.pts_count;gf&&(f=c),kl&&(l=k);c=m-110;var b=b-101,f=f+131,l=l+120,d,e,r=0,h=0;for(u=0;u=a||11<=a?"source-over":c[a-1]};cr.setGLBlend=function(a,p,g){if(g)switch(a.srcBlend=g.ONE,a.destBlend=g.ONE_MINUS_SRC_ALPHA,p){case 1:a.srcBlend=g.ONE;a.destBlend=g.ONE;break;case 3:a.srcBlend=g.ONE;a.destBlend=g.ZERO;break;case 4:a.srcBlend=g.ONE_MINUS_DST_ALPHA;a.destBlend=g.ONE;break;case 5:a.srcBlend=g.DST_ALPHA;a.destBlend=g.ZERO;break;case 6:a.srcBlend=g.ZERO;a.destBlend=g.SRC_ALPHA;break;case 7:a.srcBlend=g.ONE_MINUS_DST_ALPHA;a.destBlend=g.ZERO;break;case 8:a.srcBlend=g.ZERO;a.destBlend=g.ONE_MINUS_SRC_ALPHA;break;case 9:a.srcBlend=g.DST_ALPHA;a.destBlend=g.ONE_MINUS_SRC_ALPHA;break;case 10:a.srcBlend=g.ONE_MINUS_DST_ALPHA,a.destBlend=g.SRC_ALPHA}};cr.round6dp=function(a){return Math.round(1E6*a)/1E6};var r={usage:"search",sensitivity:"accent"},n=!!"a".localeCompare,l=n&&0==="a".localeCompare("A",void 0,r),q=n&&0!=="a".localeCompare("\u00e1",void 0,r),v=n&&l&&q;cr.equals_nocase=function(a,p){return"string"!==typeof a||"string"!==typeof p||a.length!==p.length?!1:a===p?!0:v?0===a.localeCompare(p,void 0,r):a.toLowerCase()===p.toLowerCase()}})();var MatrixArray="undefined"!==typeof Float32Array?Float32Array:Array,glMatrixArrayType=MatrixArray,vec3={},mat3={},mat4={},quat4={};vec3.create=function(a){var b=new MatrixArray(3);a&&(b[0]=a[0],b[1]=a[1],b[2]=a[2]);return b};vec3.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];return b};vec3.add=function(a,b,d){if(!d||a===d)return a[0]+=b[0],a[1]+=b[1],a[2]+=b[2],a;d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];return d};vec3.subtract=function(a,b,d){if(!d||a===d)return a[0]-=b[0],a[1]-=b[1],a[2]-=b[2],a;d[0]=a[0]-b[0];d[1]=a[1]-b[1];d[2]=a[2]-b[2];return d};vec3.negate=function(a,b){b||(b=a);b[0]=-a[0];b[1]=-a[1];b[2]=-a[2];return b};vec3.scale=function(a,b,d){if(!d||a===d)return a[0]*=b,a[1]*=b,a[2]*=b,a;d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;return d};vec3.normalize=function(a,b){b||(b=a);var d=a[0],e=a[1],f=a[2],h=Math.sqrt(d*d+e*e+f*f);if(h){if(1===h)return b[0]=d,b[1]=e,b[2]=f,b}else return b[0]=0,b[1]=0,b[2]=0,b;h=1/h;b[0]=d*h;b[1]=e*h;b[2]=f*h;return b};vec3.cross=function(a,b,d){d||(d=a);var e=a[0],f=a[1];a=a[2];var h=b[0],k=b[1];b=b[2];d[0]=f*b-a*k;d[1]=a*h-e*b;d[2]=e*k-f*h;return d};vec3.length=function(a){var b=a[0],d=a[1];a=a[2];return Math.sqrt(b*b+d*d+a*a)};vec3.dot=function(a,b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]};vec3.direction=function(a,b,d){d||(d=a);var e=a[0]-b[0],f=a[1]-b[1];a=a[2]-b[2];b=Math.sqrt(e*e+f*f+a*a);if(!b)return d[0]=0,d[1]=0,d[2]=0,d;b=1/b;d[0]=e*b;d[1]=f*b;d[2]=a*b;return d};vec3.lerp=function(a,b,d,e){e||(e=a);e[0]=a[0]+d*(b[0]-a[0]);e[1]=a[1]+d*(b[1]-a[1]);e[2]=a[2]+d*(b[2]-a[2]);return e};vec3.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+"]"};mat3.create=function(a){var b=new MatrixArray(9);a&&(b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b[4]=a[4],b[5]=a[5],b[6]=a[6],b[7]=a[7],b[8]=a[8]);return b};mat3.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];return b};mat3.identity=function(a){a[0]=1;a[1]=0;a[2]=0;a[3]=0;a[4]=1;a[5]=0;a[6]=0;a[7]=0;a[8]=1;return a};mat3.transpose=function(a,b){if(!b||a===b){var d=a[1],e=a[2],f=a[5];a[1]=a[3];a[2]=a[6];a[3]=d;a[5]=a[7];a[6]=e;a[7]=f;return a}b[0]=a[0];b[1]=a[3];b[2]=a[6];b[3]=a[1];b[4]=a[4];b[5]=a[7];b[6]=a[2];b[7]=a[5];b[8]=a[8];return b};mat3.toMat4=function(a,b){b||(b=mat4.create());b[15]=1;b[14]=0;b[13]=0;b[12]=0;b[11]=0;b[10]=a[8];b[9]=a[7];b[8]=a[6];b[7]=0;b[6]=a[5];b[5]=a[4];b[4]=a[3];b[3]=0;b[2]=a[2];b[1]=a[1];b[0]=a[0];return b};mat3.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+", "+a[4]+", "+a[5]+", "+a[6]+", "+a[7]+", "+a[8]+"]"};mat4.create=function(a){var b=new MatrixArray(16);a&&(b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b[4]=a[4],b[5]=a[5],b[6]=a[6],b[7]=a[7],b[8]=a[8],b[9]=a[9],b[10]=a[10],b[11]=a[11],b[12]=a[12],b[13]=a[13],b[14]=a[14],b[15]=a[15]);return b};mat4.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9];b[10]=a[10];b[11]=a[11];b[12]=a[12];b[13]=a[13];b[14]=a[14];b[15]=a[15];return b};mat4.identity=function(a){a[0]=1;a[1]=0;a[2]=0;a[3]=0;a[4]=0;a[5]=1;a[6]=0;a[7]=0;a[8]=0;a[9]=0;a[10]=1;a[11]=0;a[12]=0;a[13]=0;a[14]=0;a[15]=1;return a};mat4.transpose=function(a,b){if(!b||a===b){var d=a[1],e=a[2],f=a[3],h=a[6],k=a[7],c=a[11];a[1]=a[4];a[2]=a[8];a[3]=a[12];a[4]=d;a[6]=a[9];a[7]=a[13];a[8]=e;a[9]=h;a[11]=a[14];a[12]=f;a[13]=k;a[14]=c;return a}b[0]=a[0];b[1]=a[4];b[2]=a[8];b[3]=a[12];b[4]=a[1];b[5]=a[5];b[6]=a[9];b[7]=a[13];b[8]=a[2];b[9]=a[6];b[10]=a[10];b[11]=a[14];b[12]=a[3];b[13]=a[7];b[14]=a[11];b[15]=a[15];return b};mat4.determinant=function(a){var b=a[0],d=a[1],e=a[2],f=a[3],h=a[4],k=a[5],c=a[6],r=a[7],n=a[8],l=a[9],q=a[10],v=a[11],t=a[12],p=a[13],g=a[14];a=a[15];return t*l*c*f-n*p*c*f-t*k*q*f+h*p*q*f+n*k*g*f-h*l*g*f-t*l*e*r+n*p*e*r+t*d*q*r-b*p*q*r-n*d*g*r+b*l*g*r+t*k*e*v-h*p*e*v-t*d*c*v+b*p*c*v+h*d*g*v-b*k*g*v-n*k*e*a+h*l*e*a+n*d*c*a-b*l*c*a-h*d*q*a+b*k*q*a};mat4.inverse=function(a,b){b||(b=a);var d=a[0],e=a[1],f=a[2],h=a[3],k=a[4],c=a[5],r=a[6],n=a[7],l=a[8],q=a[9],v=a[10],t=a[11],p=a[12],g=a[13],u=a[14],D=a[15],B=d*c-e*k,s=d*r-f*k,m=d*n-h*k,P=e*r-f*c,x=e*n-h*c,z=f*n-h*r,G=l*g-q*p,C=l*u-v*p,F=l*D-t*p,y=q*u-v*g,H=q*D-t*g,Q=v*D-t*u,w=1/(B*Q-s*H+m*y+P*F-x*C+z*G);b[0]=(c*Q-r*H+n*y)*w;b[1]=(-e*Q+f*H-h*y)*w;b[2]=(g*z-u*x+D*P)*w;b[3]=(-q*z+v*x-t*P)*w;b[4]=(-k*Q+r*F-n*C)*w;b[5]=(d*Q-f*F+h*C)*w;b[6]=(-p*z+u*m-D*s)*w;b[7]=(l*z-v*m+t*s)*w;b[8]=(k*H-c*F+n*G)*w;b[9]=(-d*H+e*F-h*G)*w;b[10]=(p*x-g*m+D*B)*w;b[11]=(-l*x+q*m-t*B)*w;b[12]=(-k*y+c*C-r*G)*w;b[13]=(d*y-e*C+f*G)*w;b[14]=(-p*P+g*s-u*B)*w;b[15]=(l*P-q*s+v*B)*w;return b};mat4.toRotationMat=function(a,b){b||(b=mat4.create());b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9];b[10]=a[10];b[11]=a[11];b[12]=0;b[13]=0;b[14]=0;b[15]=1;return b};mat4.toMat3=function(a,b){b||(b=mat3.create());b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[4];b[4]=a[5];b[5]=a[6];b[6]=a[8];b[7]=a[9];b[8]=a[10];return b};mat4.toInverseMat3=function(a,b){var d=a[0],e=a[1],f=a[2],h=a[4],k=a[5],c=a[6],r=a[8],n=a[9],l=a[10],q=l*k-c*n,v=-l*h+c*r,t=n*h-k*r,p=d*q+e*v+f*t;if(!p)return null;p=1/p;b||(b=mat3.create());b[0]=q*p;b[1]=(-l*e+f*n)*p;b[2]=(c*e-f*k)*p;b[3]=v*p;b[4]=(l*d-f*r)*p;b[5]=(-c*d+f*h)*p;b[6]=t*p;b[7]=(-n*d+e*r)*p;b[8]=(k*d-e*h)*p;return b};mat4.multiply=function(a,b,d){d||(d=a);var e=a[0],f=a[1],h=a[2],k=a[3],c=a[4],r=a[5],n=a[6],l=a[7],q=a[8],v=a[9],t=a[10],p=a[11],g=a[12],u=a[13],D=a[14];a=a[15];var B=b[0],s=b[1],m=b[2],P=b[3],x=b[4],z=b[5],G=b[6],C=b[7],F=b[8],y=b[9],H=b[10],Q=b[11],w=b[12],N=b[13],R=b[14];b=b[15];d[0]=B*e+s*c+m*q+P*g;d[1]=B*f+s*r+m*v+P*u;d[2]=B*h+s*n+m*t+P*D;d[3]=B*k+s*l+m*p+P*a;d[4]=x*e+z*c+G*q+C*g;d[5]=x*f+z*r+G*v+C*u;d[6]=x*h+z*n+G*t+C*D;d[7]=x*k+z*l+G*p+C*a;d[8]=F*e+y*c+H*q+Q*g;d[9]=F*f+y*r+H*v+Q*u;d[10]=F*h+y*n+H*t+Q*D;d[11]=F*k+y*l+H*p+Q*a;d[12]=w*e+N*c+R*q+b*g;d[13]=w*f+N*r+R*v+b*u;d[14]=w*h+N*n+R*t+b*D;d[15]=w*k+N*l+R*p+b*a;return d};mat4.multiplyVec3=function(a,b,d){d||(d=b);var e=b[0],f=b[1];b=b[2];d[0]=a[0]*e+a[4]*f+a[8]*b+a[12];d[1]=a[1]*e+a[5]*f+a[9]*b+a[13];d[2]=a[2]*e+a[6]*f+a[10]*b+a[14];return d};mat4.multiplyVec4=function(a,b,d){d||(d=b);var e=b[0],f=b[1],h=b[2];b=b[3];d[0]=a[0]*e+a[4]*f+a[8]*h+a[12]*b;d[1]=a[1]*e+a[5]*f+a[9]*h+a[13]*b;d[2]=a[2]*e+a[6]*f+a[10]*h+a[14]*b;d[3]=a[3]*e+a[7]*f+a[11]*h+a[15]*b;return d};mat4.translate=function(a,b,d){var e=b[0],f=b[1];b=b[2];var h,k,c,r,n,l,q,v,t,p,g,u;if(!d||a===d)return a[12]=a[0]*e+a[4]*f+a[8]*b+a[12],a[13]=a[1]*e+a[5]*f+a[9]*b+a[13],a[14]=a[2]*e+a[6]*f+a[10]*b+a[14],a[15]=a[3]*e+a[7]*f+a[11]*b+a[15],a;h=a[0];k=a[1];c=a[2];r=a[3];n=a[4];l=a[5];q=a[6];v=a[7];t=a[8];p=a[9];g=a[10];u=a[11];d[0]=h;d[1]=k;d[2]=c;d[3]=r;d[4]=n;d[5]=l;d[6]=q;d[7]=v;d[8]=t;d[9]=p;d[10]=g;d[11]=u;d[12]=h*e+n*f+t*b+a[12];d[13]=k*e+l*f+p*b+a[13];d[14]=c*e+q*f+g*b+a[14];d[15]=r*e+v*f+u*b+a[15];return d};mat4.scale=function(a,b,d){var e=b[0],f=b[1];b=b[2];if(!d||a===d)return a[0]*=e,a[1]*=e,a[2]*=e,a[3]*=e,a[4]*=f,a[5]*=f,a[6]*=f,a[7]*=f,a[8]*=b,a[9]*=b,a[10]*=b,a[11]*=b,a;d[0]=a[0]*e;d[1]=a[1]*e;d[2]=a[2]*e;d[3]=a[3]*e;d[4]=a[4]*f;d[5]=a[5]*f;d[6]=a[6]*f;d[7]=a[7]*f;d[8]=a[8]*b;d[9]=a[9]*b;d[10]=a[10]*b;d[11]=a[11]*b;d[12]=a[12];d[13]=a[13];d[14]=a[14];d[15]=a[15];return d};mat4.rotate=function(a,b,d,e){var f=d[0],h=d[1];d=d[2];var k=Math.sqrt(f*f+h*h+d*d),c,r,n,l,q,v,t,p,g,u,D,B,s,m,P,x,z,G,C,F;if(!k)return null;1!==k&&(k=1/k,f*=k,h*=k,d*=k);c=Math.sin(b);r=Math.cos(b);n=1-r;b=a[0];k=a[1];l=a[2];q=a[3];v=a[4];t=a[5];p=a[6];g=a[7];u=a[8];D=a[9];B=a[10];s=a[11];m=f*f*n+r;P=h*f*n+d*c;x=d*f*n-h*c;z=f*h*n-d*c;G=h*h*n+r;C=d*h*n+f*c;F=f*d*n+h*c;f=h*d*n-f*c;h=d*d*n+r;e?a!==e&&(e[12]=a[12],e[13]=a[13],e[14]=a[14],e[15]=a[15]):e=a;e[0]=b*m+v*P+u*x;e[1]=k*m+t*P+D*x;e[2]=l*m+p*P+B*x;e[3]=q*m+g*P+s*x;e[4]=b*z+v*G+u*C;e[5]=k*z+t*G+D*C;e[6]=l*z+p*G+B*C;e[7]=q*z+g*G+s*C;e[8]=b*F+v*f+u*h;e[9]=k*F+t*f+D*h;e[10]=l*F+p*f+B*h;e[11]=q*F+g*f+s*h;return e};mat4.rotateX=function(a,b,d){var e=Math.sin(b);b=Math.cos(b);var f=a[4],h=a[5],k=a[6],c=a[7],r=a[8],n=a[9],l=a[10],q=a[11];d?a!==d&&(d[0]=a[0],d[1]=a[1],d[2]=a[2],d[3]=a[3],d[12]=a[12],d[13]=a[13],d[14]=a[14],d[15]=a[15]):d=a;d[4]=f*b+r*e;d[5]=h*b+n*e;d[6]=k*b+l*e;d[7]=c*b+q*e;d[8]=f*-e+r*b;d[9]=h*-e+n*b;d[10]=k*-e+l*b;d[11]=c*-e+q*b;return d};mat4.rotateY=function(a,b,d){var e=Math.sin(b);b=Math.cos(b);var f=a[0],h=a[1],k=a[2],c=a[3],r=a[8],n=a[9],l=a[10],q=a[11];d?a!==d&&(d[4]=a[4],d[5]=a[5],d[6]=a[6],d[7]=a[7],d[12]=a[12],d[13]=a[13],d[14]=a[14],d[15]=a[15]):d=a;d[0]=f*b+r*-e;d[1]=h*b+n*-e;d[2]=k*b+l*-e;d[3]=c*b+q*-e;d[8]=f*e+r*b;d[9]=h*e+n*b;d[10]=k*e+l*b;d[11]=c*e+q*b;return d};mat4.rotateZ=function(a,b,d){var e=Math.sin(b);b=Math.cos(b);var f=a[0],h=a[1],k=a[2],c=a[3],r=a[4],n=a[5],l=a[6],q=a[7];d?a!==d&&(d[8]=a[8],d[9]=a[9],d[10]=a[10],d[11]=a[11],d[12]=a[12],d[13]=a[13],d[14]=a[14],d[15]=a[15]):d=a;d[0]=f*b+r*e;d[1]=h*b+n*e;d[2]=k*b+l*e;d[3]=c*b+q*e;d[4]=f*-e+r*b;d[5]=h*-e+n*b;d[6]=k*-e+l*b;d[7]=c*-e+q*b;return d};mat4.frustum=function(a,b,d,e,f,h,k){k||(k=mat4.create());var c=b-a,r=e-d,n=h-f;k[0]=2*f/c;k[1]=0;k[2]=0;k[3]=0;k[4]=0;k[5]=2*f/r;k[6]=0;k[7]=0;k[8]=(b+a)/c;k[9]=(e+d)/r;k[10]=-(h+f)/n;k[11]=-1;k[12]=0;k[13]=0;k[14]=-(2*h*f)/n;k[15]=0;return k};mat4.perspective=function(a,b,d,e,f){a=d*Math.tan(a*Math.PI/360);b*=a;return mat4.frustum(-b,b,-a,a,d,e,f)};mat4.ortho=function(a,b,d,e,f,h,k){k||(k=mat4.create());var c=b-a,r=e-d,n=h-f;k[0]=2/c;k[1]=0;k[2]=0;k[3]=0;k[4]=0;k[5]=2/r;k[6]=0;k[7]=0;k[8]=0;k[9]=0;k[10]=-2/n;k[11]=0;k[12]=-(a+b)/c;k[13]=-(e+d)/r;k[14]=-(h+f)/n;k[15]=1;return k};mat4.lookAt=function(a,b,d,e){e||(e=mat4.create());var f,h,k,c,r,n,l,q,v=a[0],t=a[1];a=a[2];h=d[0];k=d[1];f=d[2];d=b[1];n=b[2];if(v===b[0]&&t===d&&a===n)return mat4.identity(e);d=v-b[0];n=t-b[1];l=a-b[2];q=1/Math.sqrt(d*d+n*n+l*l);d*=q;n*=q;l*=q;b=k*l-f*n;f=f*d-h*l;h=h*n-k*d;(q=Math.sqrt(b*b+f*f+h*h))?(q=1/q,b*=q,f*=q,h*=q):h=f=b=0;k=n*h-l*f;c=l*b-d*h;r=d*f-n*b;(q=Math.sqrt(k*k+c*c+r*r))?(q=1/q,k*=q,c*=q,r*=q):r=c=k=0;e[0]=b;e[1]=k;e[2]=d;e[3]=0;e[4]=f;e[5]=c;e[6]=n;e[7]=0;e[8]=h;e[9]=r;e[10]=l;e[11]=0;e[12]=-(b*v+f*t+h*a);e[13]=-(k*v+c*t+r*a);e[14]=-(d*v+n*t+l*a);e[15]=1;return e};mat4.fromRotationTranslation=function(a,b,d){d||(d=mat4.create());var e=a[0],f=a[1],h=a[2],k=a[3],c=e+e,r=f+f,n=h+h;a=e*c;var l=e*r,e=e*n,q=f*r,f=f*n,h=h*n,c=c*k,r=r*k,k=k*n;d[0]=1-(q+h);d[1]=l+k;d[2]=e-r;d[3]=0;d[4]=l-k;d[5]=1-(a+h);d[6]=f+c;d[7]=0;d[8]=e+r;d[9]=f-c;d[10]=1-(a+q);d[11]=0;d[12]=b[0];d[13]=b[1];d[14]=b[2];d[15]=1;return d};mat4.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+", "+a[4]+", "+a[5]+", "+a[6]+", "+a[7]+", "+a[8]+", "+a[9]+", "+a[10]+", "+a[11]+", "+a[12]+", "+a[13]+", "+a[14]+", "+a[15]+"]"};quat4.create=function(a){var b=new MatrixArray(4);a&&(b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3]);return b};quat4.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];return b};quat4.calculateW=function(a,b){var d=a[0],e=a[1],f=a[2];if(!b||a===b)return a[3]=-Math.sqrt(Math.abs(1-d*d-e*e-f*f)),a;b[0]=d;b[1]=e;b[2]=f;b[3]=-Math.sqrt(Math.abs(1-d*d-e*e-f*f));return b};quat4.inverse=function(a,b){if(!b||a===b)return a[0]*=-1,a[1]*=-1,a[2]*=-1,a;b[0]=-a[0];b[1]=-a[1];b[2]=-a[2];b[3]=a[3];return b};quat4.length=function(a){var b=a[0],d=a[1],e=a[2];a=a[3];return Math.sqrt(b*b+d*d+e*e+a*a)};quat4.normalize=function(a,b){b||(b=a);var d=a[0],e=a[1],f=a[2],h=a[3],k=Math.sqrt(d*d+e*e+f*f+h*h);if(0===k)return b[0]=0,b[1]=0,b[2]=0,b[3]=0,b;k=1/k;b[0]=d*k;b[1]=e*k;b[2]=f*k;b[3]=h*k;return b};quat4.multiply=function(a,b,d){d||(d=a);var e=a[0],f=a[1],h=a[2];a=a[3];var k=b[0],c=b[1],r=b[2];b=b[3];d[0]=e*b+a*k+f*r-h*c;d[1]=f*b+a*c+h*k-e*r;d[2]=h*b+a*r+e*c-f*k;d[3]=a*b-e*k-f*c-h*r;return d};quat4.multiplyVec3=function(a,b,d){d||(d=b);var e=b[0],f=b[1],h=b[2];b=a[0];var k=a[1],c=a[2];a=a[3];var r=a*e+k*h-c*f,n=a*f+c*e-b*h,l=a*h+b*f-k*e,e=-b*e-k*f-c*h;d[0]=r*a+e*-b+n*-c-l*-k;d[1]=n*a+e*-k+l*-b-r*-c;d[2]=l*a+e*-c+r*-k-n*-b;return d};quat4.toMat3=function(a,b){b||(b=mat3.create());var d=a[0],e=a[1],f=a[2],h=a[3],k=d+d,c=e+e,r=f+f,n=d*k,l=d*c,d=d*r,q=e*c,e=e*r,f=f*r,k=k*h,c=c*h,h=h*r;b[0]=1-(q+f);b[1]=l+h;b[2]=d-c;b[3]=l-h;b[4]=1-(n+f);b[5]=e+k;b[6]=d+c;b[7]=e-k;b[8]=1-(n+q);return b};quat4.toMat4=function(a,b){b||(b=mat4.create());var d=a[0],e=a[1],f=a[2],h=a[3],k=d+d,c=e+e,r=f+f,n=d*k,l=d*c,d=d*r,q=e*c,e=e*r,f=f*r,k=k*h,c=c*h,h=h*r;b[0]=1-(q+f);b[1]=l+h;b[2]=d-c;b[3]=0;b[4]=l-h;b[5]=1-(n+f);b[6]=e+k;b[7]=0;b[8]=d+c;b[9]=e-k;b[10]=1-(n+q);b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return b};quat4.slerp=function(a,b,d,e){e||(e=a);var f=a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3],h,k;if(1<=Math.abs(f))return e!==a&&(e[0]=a[0],e[1]=a[1],e[2]=a[2],e[3]=a[3]),e;h=Math.acos(f);k=Math.sqrt(1-f*f);if(0.001>Math.abs(k))return e[0]=0.5*a[0]+0.5*b[0],e[1]=0.5*a[1]+0.5*b[1],e[2]=0.5*a[2]+0.5*b[2],e[3]=0.5*a[3]+0.5*b[3],e;f=Math.sin((1-d)*h)/k;d=Math.sin(d*h)/k;e[0]=a[0]*f+b[0]*d;e[1]=a[1]*f+b[1]*d;e[2]=a[2]*f+b[2]*d;e[3]=a[3]*f+b[3]*d;return e};quat4.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+"]"};(function(){function a(a,c){this.height=this.width=0;this.cam=vec3.create([0,0,100]);this.look=vec3.create([0,0,0]);this.up=vec3.create([0,1,0]);this.worldScale=vec3.create([1,1,1]);this.matP=mat4.create();this.matMV=mat4.create();this.lastMV=mat4.create();this.currentMV=mat4.create();this.gl=a;this.initState()}function b(a,c,f){this.gl=a;this.shaderProgram=c;this.name=f;this.locAPos=a.getAttribLocation(c,"aPos");this.locATex=a.getAttribLocation(c,"aTex");this.locMatP=a.getUniformLocation(c,"matP");this.locMatMV=a.getUniformLocation(c,"matMV");this.locOpacity=a.getUniformLocation(c,"opacity");this.locSamplerFront=a.getUniformLocation(c,"samplerFront");this.locSamplerBack=a.getUniformLocation(c,"samplerBack");this.locDestStart=a.getUniformLocation(c,"destStart");this.locDestEnd=a.getUniformLocation(c,"destEnd");this.locSeconds=a.getUniformLocation(c,"seconds");this.locPixelWidth=a.getUniformLocation(c,"pixelWidth");this.locPixelHeight=a.getUniformLocation(c,"pixelHeight");this.locLayerScale=a.getUniformLocation(c,"layerScale");this.locOpacity&&a.uniform1f(this.locOpacity,1);this.locSamplerFront&&a.uniform1i(this.locSamplerFront,0);this.locSamplerBack&&a.uniform1i(this.locSamplerBack,1);this.locDestStart&&a.uniform2f(this.locDestStart,0,0);this.locDestEnd&&a.uniform2f(this.locDestEnd,1,1);this.hasCurrentMatMV=!1}function d(a,c){this.type=a;this.glwrap=c;this.gl=c.gl;this.indexCount=this.startIndex=this.opacityParam=0;this.mat4param=this.texParam=null;this.shaderParams=[];cr.seal(this)}function e(a){--a;for(var c=1;32>c;c<<=1)a|=a>>c;return a+1}a.prototype.initState=function(){var a=this.gl,c;this.lastOpacity=1;this.lastTexture=null;this.currentOpacity=1;a.clearColor(0,0,0,0);a.clear(a.COLOR_BUFFER_BIT);a.enable(a.BLEND);a.blendFunc(a.ONE,a.ONE_MINUS_SRC_ALPHA);a.disable(a.CULL_FACE);a.disable(a.DEPTH_TEST);this.maxTextureSize=a.getParameter(a.MAX_TEXTURE_SIZE);this.lastSrcBlend=a.ONE;this.lastDestBlend=a.ONE_MINUS_SRC_ALPHA;this.pointBuffer=a.createBuffer();a.bindBuffer(a.ARRAY_BUFFER,this.pointBuffer);this.vertexBuffers=Array(4);this.texcoordBuffers=Array(4);for(c=0;4>c;c++)this.vertexBuffers[c]=a.createBuffer(),a.bindBuffer(a.ARRAY_BUFFER,this.vertexBuffers[c]),this.texcoordBuffers[c]=a.createBuffer(),a.bindBuffer(a.ARRAY_BUFFER,this.texcoordBuffers[c]);this.curBuffer=0;this.indexBuffer=a.createBuffer();a.bindBuffer(a.ELEMENT_ARRAY_BUFFER,this.indexBuffer);this.vertexData=new Float32Array(16E3);this.texcoordData=new Float32Array(16E3);this.pointData=new Float32Array(32E3);for(var f=new Uint16Array(12E3),b=c=0;12E3>c;)f[c++]=b,f[c++]=b+1,f[c++]=b+2,f[c++]=b,f[c++]=b+2,f[c++]=b+3,b+=4;a.bufferData(a.ELEMENT_ARRAY_BUFFER,f,a.STATIC_DRAW);this.pointPtr=this.vertexPtr=0;this.shaderPrograms=[];c=this.createShaderProgram({src:"varying mediump vec2 vTex;\nuniform lowp float opacity;\nuniform lowp sampler2D samplerFront;\nvoid main(void) {\n\tgl_FragColor = texture2D(samplerFront, vTex);\n\tgl_FragColor *= opacity;\n}"},"attribute highp vec2 aPos;\nattribute mediump vec2 aTex;\nvarying mediump vec2 vTex;\nuniform highp mat4 matP;\nuniform highp mat4 matMV;\nvoid main(void) {\n\tgl_Position = matP * matMV * vec4(aPos.x, aPos.y, 0.0, 1.0);\n\tvTex = aTex;\n}","");this.shaderPrograms.push(c);c=this.createShaderProgram({src:"uniform mediump sampler2D samplerFront;\nvarying lowp float opacity;\nvoid main(void) {\n\tgl_FragColor = texture2D(samplerFront, gl_PointCoord);\n\tgl_FragColor *= opacity;\n}"},"attribute vec4 aPos;\nvarying float opacity;\nuniform mat4 matP;\nuniform mat4 matMV;\nvoid main(void) {\n\tgl_Position = matP * matMV * vec4(aPos.x, aPos.y, 0.0, 1.0);\n\tgl_PointSize = aPos.z;\n\topacity = aPos.w;\n}","");this.shaderPrograms.push(c);for(var l in cr.shaders)cr.shaders.hasOwnProperty(l)&&this.shaderPrograms.push(this.createShaderProgram(cr.shaders[l],"attribute highp vec2 aPos;\nattribute mediump vec2 aTex;\nvarying mediump vec2 vTex;\nuniform highp mat4 matP;\nuniform highp mat4 matMV;\nvoid main(void) {\n\tgl_Position = matP * matMV * vec4(aPos.x, aPos.y, 0.0, 1.0);\n\tvTex = aTex;\n}",l));a.activeTexture(a.TEXTURE0);a.bindTexture(a.TEXTURE_2D,null);this.batch=[];this.batchPtr=0;this.hasPointBatchTop=this.hasQuadBatchTop=!1;this.currentProgram=this.lastProgram=-1;this.currentShader=null;this.fbo=a.createFramebuffer();this.renderToTex=null;this.tmpVec3=vec3.create([0,0,0]);a=a.getParameter(a.ALIASED_POINT_SIZE_RANGE);this.minPointSize=a[0];this.maxPointSize=a[1];this.switchProgram(0);cr.seal(this)};a.prototype.createShaderProgram=function(a,c,f){var d=this.gl,l=d.createShader(d.FRAGMENT_SHADER);d.shaderSource(l,a.src);d.compileShader(l);if(!d.getShaderParameter(l,d.COMPILE_STATUS))return d.deleteShader(l),null;var e=d.createShader(d.VERTEX_SHADER);d.shaderSource(e,c);d.compileShader(e);if(!d.getShaderParameter(e,d.COMPILE_STATUS))return d.deleteShader(l),d.deleteShader(e),null;c=d.createProgram();d.attachShader(c,l);d.attachShader(c,e);d.linkProgram(c);if(!d.getProgramParameter(c,d.LINK_STATUS))return d.deleteShader(l),d.deleteShader(e),d.deleteProgram(c),null;d.useProgram(c);d.validateProgram(c);d.deleteShader(l);d.deleteShader(e);f=new b(d,c,f);f.extendBoxHorizontal=a.extendBoxHorizontal||0;f.extendBoxVertical=a.extendBoxVertical||0;f.crossSampling=!!a.crossSampling;f.animated=!!a.animated;f.parameters=a.parameters||[];a=0;for(l=f.parameters.length;ac;c++)if(this.lastMV[c]!==this.matMV[c]){a=!0;break}a&&(a=this.pushBatch(),a.type=5,a.mat4param?mat4.set(this.matMV,a.mat4param):a.mat4param=mat4.create(this.matMV),mat4.set(this.matMV,this.lastMV),this.hasPointBatchTop=this.hasQuadBatchTop=!1)};d.prototype.doSetTexture=function(){this.gl.bindTexture(this.gl.TEXTURE_2D,this.texParam)};d.prototype.doSetOpacity=function(){var a=this.opacityParam,c=this.glwrap;c.currentOpacity=a;c=c.currentShader;c.locOpacity&&this.gl.uniform1f(c.locOpacity,a)};d.prototype.doQuad=function(){this.gl.drawElements(this.gl.TRIANGLES,this.indexCount,this.gl.UNSIGNED_SHORT,2*this.startIndex)};d.prototype.doSetBlend=function(){this.gl.blendFunc(this.startIndex,this.indexCount)};d.prototype.doUpdateModelView=function(){var a,c,f,b=this.glwrap.shaderPrograms,l=this.glwrap.currentProgram;a=0;for(c=b.length;a"===c.name)&&a.vertexAttribPointer(c.locAPos,4,a.FLOAT,!1,0,0));if(0"!==c.name)&&a.vertexAttribPointer(c.locAPos,2,a.FLOAT,!1,0,0);a.bindBuffer(a.ARRAY_BUFFER,this.texcoordBuffers[this.curBuffer]);a.bufferData(a.ARRAY_BUFFER,this.texcoordData.subarray(0,this.vertexPtr),a.STREAM_DRAW);c&&(0<=c.locATex&&""!==c.name)&&a.vertexAttribPointer(c.locATex,2,a.FLOAT,!1,0,0)}for(var f,a=0,c=this.batchPtr;am?(m*=g,5===e?(e=m/this.original_width,1e&&(e=1/Math.ceil(1/e)),m=this.original_width*e,d=this.original_height*e,b=(a-m)/2,s=(g-d)/2,a=m,g=d):(b=(a-m)/2,a=m)):(d=a/m,5===e?(e=d/this.original_height,1e&&(e=1/Math.ceil(1/e)),m=this.original_width*e,d=this.original_height*e,b=(a-m)/2,s=(g-d)/2,a=m):s=(g-d)/2,g=d),l&&!this.isNodeWebkit&&(s=b=0),b=Math.floor(b),s=Math.floor(s),a=Math.floor(a),g=Math.floor(g)):this.isNodeWebkit&&(this.isNodeFullscreen&&0===this.fullscreen_mode_set)&&(b=Math.floor((a-this.original_width)/2),s=Math.floor((g-this.original_height)/2),a=this.original_width,g=this.original_height);this.isRetina&&(this.isiPad&&1=s)s=5E4;a+=s;this.wait_for_textures[c].complete||this.wait_for_textures[c].loaded?g+=s:f=!1}this.progress=0==a?0:g/a;return f};a.prototype.go=function(){if(this.ctx||this.glwrap){var a=this.ctx||this.overlay_ctx;this.overlay_canvas&&this.positionOverlayCanvas();this.progress=0;this.last_progress=-1;if(this.areAllTexturesLoaded())this.go_textures_done();else{var g=Date.now()-this.start_time,c=1;this.isTizen&&(c=this.devicePixelRatio);if(a){if(3!==this.loaderstyle&&500<=g&&this.last_progress!=this.progress){a.clearRect(0,0,this.width,this.height);var g=this.width/(2*c),c=this.height/(2*c),f=0===this.loaderstyle&&this.loaderlogo.complete,b=40,s=0,m=80;f&&(m=this.loaderlogo.width,b=m/2,s=this.loaderlogo.height/2,a.drawImage(this.loaderlogo,cr.floor(g-b),cr.floor(c-s)));1>=this.loaderstyle?(c+=s+(f?12:0),g=cr.floor(g-b)+0.5,c=cr.floor(c)+0.5,a.fillStyle="DodgerBlue",a.fillRect(g,c,Math.floor(m*this.progress),6),a.strokeStyle="black",a.strokeRect(g,c,m,6),a.strokeStyle="white",a.strokeRect(g-1,c-1,m+2,8)):2===this.loaderstyle&&(a.font="12pt Arial",a.fillStyle="#999",a.textBaseLine="middle",f=Math.round(100*this.progress)+"%",b=a.measureText?a.measureText(f):null,a.fillText(f,g-(b?b.width:0)/2,c))}this.last_progress=this.progress}setTimeout(function(a){return function(){a.go()}}(this),100)}}};a.prototype.go_textures_done=function(){this.overlay_canvas&&(this.canvas.parentNode.removeChild(this.overlay_canvas),this.overlay_canvas=this.overlay_ctx=null);this.start_time=Date.now();this.last_fps_time=cr.performance_now();var a,g,c;if(this.uses_loader_layout)for(a=0,g=this.types_by_index.length;aa||2===this.fullscreen_mode&&gg||2===f&&ca++;)this.doChangeLayout(this.changelayout);a=0;for(g=this.eventsheets_by_index.length;ag.deadCache.length&&g.deadCache.push(a);g.stale_iids=!0}this.deathRow.isEmpty()||(this.redraw=!0);this.deathRow.clear();this.isInClearDeathRow=!1};a.prototype.createInstance=function(a,g,c,f){if(a.is_family){var b=cr.floor(Math.random()*a.members.length);return this.createInstance(a.members[b],g,c,f)}return a.default_instance?this.createInstanceFromInit(a.default_instance,g,!1,c,f,!1):null};var r=[];a.prototype.createInstanceFromInit=function(a,g,c,f,b,s){var m,d,e,l;if(!a)return null;var h=this.types_by_index[a[1]],k=h.plugin.is_world;if(this.isloading&&k&&!h.isOnLoaderLayout||k&&!this.glwrap&&11===a[0][11])return null;var t=g;k||(g=null);var q;h.deadCache.length?(q=h.deadCache.pop(),q.recycled=!0,h.plugin.Instance.call(q,h)):(q=new h.plugin.Instance(h),q.recycled=!1);q.uid=c&&!s?a[2]:this.next_uid++;this.objectsByUid[q.uid.toString()]=q;q.puid=this.next_puid++;q.iid=0;q.get_iid=cr.inst_get_iid;h.stale_iids=!0;e=a[3];if(q.recycled)cr.wipe(q.extra);else{q.extra={};if("undefined"!==typeof cr_is_preview)for(q.instance_var_names=[],q.instance_var_names.length=e.length,m=0,d=e.length;ma&&(a=0);a>=this.running_layout.layers.length&&(a=this.running_layout.layers.length-1);return this.running_layout.layers[a]};a.prototype.getLayer=function(a){return cr.is_number(a)?this.getLayerByNumber(a):this.getLayerByName(a.toString())};a.prototype.clearSol=function(a){var g,c;g=0;for(c=a.length;gk;k++)if(t=e-k*n,a.x=c+Math.cos(t)*m,a.y=f+Math.sin(t)*m,a.set_bbox_changed(),!this.testOverlap(a,h)&&(h=b?null:this.testOverlapSolid(a),!h)){q=t;break}36===k&&(q=cr.clamp_angle(e+cr.PI));h=l;for(k=1;36>k;k++)if(t=e+k*n,a.x=c+Math.cos(t)*m,a.y=f+Math.sin(t)*m,a.set_bbox_changed(),!this.testOverlap(a,h)&&(h=b?null:this.testOverlapSolid(a),!h)){r=t;break}36===k&&(r=cr.clamp_angle(e+cr.PI));a.x=d;a.y=s;a.set_bbox_changed();if(r===q)return r;a=cr.angleDiff(r,q)/2;a=cr.angleClockwise(r,q)?cr.clamp_angle(q+a+cr.PI):cr.clamp_angle(r+a);q=Math.cos(e);e=Math.sin(e);r=Math.cos(a);a=Math.sin(a);c=q*r+e*a;return cr.angleTo(0,0,q-2*c*r,e-2*c*a)};var v=[],t=-1;a.prototype.trigger=function(a,c,f){if(!this.running_layout)return!1;var b=this.running_layout.event_sheet;if(!b)return!1;t++;t===v.length?v.push(new cr.ObjectSet):v[t].clear();a=this.triggerOnSheet(a,c,b,f);t--;return a};a.prototype.triggerOnSheet=function(a,c,f,b){var d=v[t];if(d.contains(f))return!1;d.add(f);var d=f.includes.valuesRef(),s=!1,m,e,l;m=0;for(e=d.length;m=this.localvar_stack.length&&this.localvar_stack.push([])};a.prototype.popLocalVarStack=function(){this.localvar_stack_index--};a.prototype.getCurrentLocalVarStack=function(){return this.localvar_stack[this.localvar_stack_index]};a.prototype.pushEventStack=function(a){this.event_stack_index++;this.event_stack_index>=this.event_stack.length&&this.event_stack.push(new cr.eventStackFrame);var c=this.getCurrentEventStack();c.reset(a);return c};a.prototype.popEventStack=function(){this.event_stack_index--};a.prototype.getCurrentEventStack=function(){return this.event_stack[this.event_stack_index]};a.prototype.pushLoopStack=function(a){this.loop_stack_index++;this.loop_stack_index>=this.loop_stack.length&&this.loop_stack.push(cr.seal({name:a,index:0,stopped:!1}));var c=this.getCurrentLoop();c.name=a;c.index=0;c.stopped=!1;return c};a.prototype.popLoopStack=function(){this.loop_stack_index--};a.prototype.getCurrentLoop=function(){return this.loop_stack[this.loop_stack_index]};a.prototype.getEventVariableByName=function(a,c){for(var f,b,d,s,m,e;c;){f=0;for(b=c.subevents.length;fe||e>=a.instance_vars.length||(a.instance_vars[e]=b[f]));if(d.is_world){e=c.w;a.layer.sid!==e.l&&(b=a.layer,a.layer=this.running_layout.getLayerBySid(e.l),a.layer?(a.layer.instances.push(a),a.layer.zindices_stale=!0,cr.arrayFindRemove(b.instances,a),b.zindices_stale=!0):(a.layer=b,this.DestroyInstance(a)));a.x=e.x;a.y=e.y;a.width=e.w;a.height=e.h;a.zindex=e.zi;a.angle=e.hasOwnProperty("a")?e.a:0;a.opacity=e.hasOwnProperty("o")?e.o:1;a.hotspotX=e.hasOwnProperty("hX")?e.hX:0.5;a.hotspotY=e.hasOwnProperty("hY")?e.hY:0.5;a.visible=e.hasOwnProperty("v")?e.v:!0;a.collisionsEnabled=e.hasOwnProperty("ce")?e.ce:!0;a.my_timescale=e.hasOwnProperty("mts")?e.mts:-1;a.blend_mode=e.hasOwnProperty("bm")?e.bm:0;a.compositeOp=cr.effectToCompositeOp(a.blend_mode);this.gl&&cr.setGLBlend(a,a.blend_mode,this.gl);a.set_bbox_changed();if(e.hasOwnProperty("fx"))for(b=0,d=e.fx.length;bm||(a.active_effect_flags[m]=e.fx[b].active,a.effect_params[m]=e.fx[b].params);a.updateActiveEffects()}if(l=c.behs)for(f in l)l.hasOwnProperty(f)&&(e=this.getBehaviorIndexBySid(a,parseInt(f,10)),0>e||a.behavior_insts[e].loadFromJSON(l[f]));c.data&&a.loadFromJSON(c.data)};cr.runtime=a;cr.createRuntime=function(c){return new a(document.getElementById(c))};cr.createDCRuntime=function(c,f){return new a({dc:!0,width:c,height:f})};window.cr_createRuntime=cr.createRuntime;window.cr_createDCRuntime=cr.createDCRuntime;window.createCocoonJSRuntime=function(){window.c2cocoonjs=!0;var c=document.createElement("screencanvas")||document.createElement("canvas");document.body.appendChild(c);c=new a(c);window.c2runtime=c;window.addEventListener("orientationchange",function(){window.c2runtime.setSize(window.innerWidth,window.innerHeight)});window.c2runtime.setSize(window.innerWidth,window.innerHeight);return c}})();window.cr_getC2Runtime=function(){var a=document.getElementById("c2canvas");return a?a.c2runtime:window.c2runtime?window.c2runtime:null};window.cr_sizeCanvas=function(a,b){if(0!==a&&0!==b){var d=window.cr_getC2Runtime();d&&d.setSize(a,b)}};window.cr_setSuspended=function(a){var b=window.cr_getC2Runtime();b&&b.setSuspended(a)};(function(){function a(a,b){this.runtime=a;this.event_sheet=null;this.scrollX=this.runtime.original_width/2;this.scrollY=this.runtime.original_height/2;this.scale=1;this.angle=0;this.first_visit=!0;this.name=b[0];this.width=b[1];this.height=b[2];this.unbounded_scrolling=b[3];this.sheetname=b[4];this.sid=b[5];var d=b[6],c,e;this.layers=[];this.initial_types=[];c=0;for(e=d.length;c=this.layers.length&&(q=this.layers.length-1);n.layer=this.layers[q];n.layer.instances.push(n);n.layer.zindices_stale=!0}e.length=0;this.boundScrolling();a=0;for(k=this.layers.length;ak?n.siblings.push(l.instances[k]):l.default_instance&&(r=this.runtime.createInstanceFromInit(l.default_instance,n.layer,!0,n.x,n.y,!0),this.runtime.ClearDeathRow(),l.updateIIDs(),n.siblings.push(r),e.push(r)));a=0;for(k=this.initial_nonworld.length;a=this.active_effect_types.length?(1===this.active_effect_types.length?(b=this.active_effect_types[0].index,a.switchProgram(this.active_effect_types[0].shaderindex),a.setProgramParameters(null,1/this.runtime.width,1/this.runtime.height,0,0,1,1,this.scale,this.effect_params[b]),a.programIsAnimated(this.active_effect_types[0].shaderindex)&&(this.runtime.redraw=!0)):a.switchProgram(0),a.setRenderingToTexture(null),a.setOpacity(1),a.setTexture(this.runtime.layout_tex),a.setAlphaBlend(),a.resetModelView(),a.updateModelView(),b=this.runtime.width/2,d=this.runtime.height/2,a.quad(-b,d,b,d,b,-d,-b,-d),a.setTexture(null)):this.renderEffectChain(a,null,null,null));a.present()};a.prototype.getRenderTarget=function(){return 0this.width-b&&(a=this.width-b);athis.height-b&&(a=this.height-b);aG&&(G=0);0>F&&(F=0);H>B&&(H=B);w>s&&(w=s);0>C&&(C=0);0>y&&(y=0);Q>B&&(Q=B);N>s&&(N=s);x.left=G/B;x.top=1-F/s;x.right=H/B;x.bottom=1-w/s}else x.left=z.left=0,x.top=z.top=0,x.right=z.right=1,x.bottom=z.bottom=1;R=d&&((d.angle||W)&&a.programUsesDest(e[0].shaderindex)||0!==t||0!==R||1!==d.opacity||d.type.plugin.must_predraw)||b&&!d&&1!==b.opacity;a.setAlphaBlend();if(R){l[g]||(l[g]=a.createEmptyTexture(B,s,this.runtime.linearSampling));if(l[g].c2width!==B||l[g].c2height!==s)a.deleteTexture(l[g]),l[g]=a.createEmptyTexture(B,s,this.runtime.linearSampling);a.switchProgram(0);a.setRenderingToTexture(l[g]);D=N-y;a.clearRect(C,s-y-D,Q-C,D);d?d.drawGL(a):(a.setTexture(this.runtime.layer_tex),a.setOpacity(b.opacity),a.resetModelView(),a.translate(-m,-P),a.updateModelView(),a.quadTex(G,w,H,w,H,F,G,F,x));z.left=z.top=0;z.right=z.bottom=1;d&&(p=x.top,x.top=x.bottom,x.bottom=p);g=1;u=0}a.setOpacity(1);t=e.length-1;var W=a.programUsesCrossSampling(e[t].shaderindex),U=0;q=0;for(v=e.length;qthis.viewRight||l.top>this.viewBottom)||(d.globalCompositeOperation=n.compositeOp,n.draw(d)));d.restore();this.render_offscreen&&(a.globalCompositeOperation=this.compositeOp,a.globalAlpha=this.opacity,a.drawImage(b,0,0))};b.prototype.rotateViewport=function(a,b,d){var c=this.getScale();this.viewLeft=a;this.viewTop=b;this.viewRight=a+this.runtime.width*(1/c);this.viewBottom=b+this.runtime.height*(1/c);a=this.getAngle();0!==a&&(d&&(d.translate(this.runtime.width/2,this.runtime.height/2),d.rotate(-a),d.translate(this.runtime.width/-2,this.runtime.height/-2)),this.tmprect.set(this.viewLeft,this.viewTop,this.viewRight,this.viewBottom),this.tmprect.offset((this.viewLeft+this.viewRight)/-2,(this.viewTop+this.viewBottom)/-2),this.tmpquad.set_from_rotated_rect(this.tmprect,a),this.tmpquad.bounding_box(this.tmprect),this.tmprect.offset((this.viewLeft+this.viewRight)/2,(this.viewTop+this.viewBottom)/2),this.viewLeft=this.tmprect.left,this.viewTop=this.tmprect.top,this.viewRight=this.tmprect.right,this.viewBottom=this.tmprect.bottom)};b.prototype.drawGL=function(a){var b=this.runtime.width,d=this.runtime.height,c=0,e=0;if(this.render_offscreen=this.forceOwnTexture||1!==this.opacity||0this.viewRight||c.top>this.viewBottom)))if(v.uses_shaders)if(c=v.active_effect_types[0].shaderindex,e=v.active_effect_types[0].index,1!==v.active_effect_types.length||a.programUsesCrossSampling(c)||a.programExtendsBox(c)||(v.angle||v.layer.getAngle())&&a.programUsesDest(c)||1!==v.opacity||v.type.plugin.must_predraw)this.layout.renderEffectChain(a,this,v,this.render_offscreen?this.runtime.layer_tex:this.layout.getRenderTarget()),a.resetModelView(),a.scale(n,n),a.rotateZ(-this.getAngle()),a.translate((this.viewLeft+this.viewRight)/-2,(this.viewTop+this.viewBottom)/-2),a.updateModelView();else{a.switchProgram(c);a.setBlend(v.srcBlend,v.destBlend);a.programIsAnimated(c)&&(this.runtime.redraw=!0);var t=0,p=0,g=0,u=0;a.programUsesDest(c)&&(c=v.bbox,t=this.layerToCanvas(c.left,c.top,!0),p=this.layerToCanvas(c.left,c.top,!1),g=this.layerToCanvas(c.right,c.bottom,!0),c=this.layerToCanvas(c.right,c.bottom,!1),t/=b,p=1-p/d,g/=b,u=1-c/d);a.setProgramParameters(this.render_offscreen?this.runtime.layer_tex:this.layout.getRenderTarget(),1/v.width,1/v.height,t,p,g,u,this.getScale(),v.effect_params[e]);v.drawGL(a)}else a.switchProgram(0),a.setBlend(v.srcBlend,v.destBlend),v.drawGL(a);this.render_offscreen&&(c=this.active_effect_types.length?this.active_effect_types[0].shaderindex:0,e=this.active_effect_types.length?this.active_effect_types[0].index:0,0===this.active_effect_types.length||1===this.active_effect_types.length&&!a.programUsesCrossSampling(c)&&1===this.opacity?(1===this.active_effect_types.length?(a.switchProgram(c),a.setProgramParameters(this.layout.getRenderTarget(),1/this.runtime.width,1/this.runtime.height,0,0,1,1,this.getScale(),this.effect_params[e]),a.programIsAnimated(c)&&(this.runtime.redraw=!0)):a.switchProgram(0),a.setRenderingToTexture(this.layout.getRenderTarget()),a.setOpacity(this.opacity),a.setTexture(this.runtime.layer_tex),a.setBlend(this.srcBlend,this.destBlend),a.resetModelView(),a.updateModelView(),b=this.runtime.width/2,d=this.runtime.height/2,a.quad(-b,d,b,d,b,-d,-b,-d),a.setTexture(null)):this.layout.renderEffectChain(a,this,null,this.layout.getRenderTarget()))};b.prototype.canvasToLayer=function(a,b,d){var c=this.runtime.devicePixelRatio;this.runtime.isRetina&&0c[1].index&&(d=c[0],c[0]=c[1],c[1]=d):2=t.length&&(t.length=c.length+1);t[c.length]||(t[c.length]=[]);m=t[c.length];d=0;for(e=m.length;d=c.length&&(c.length=this.localIndex+1),c[this.localIndex]=a):this.data=a};l.prototype.getValue=function(){var a=this.runtime.getCurrentLocalVarStack();return!this.parent||this.is_static||!a||this.is_constant?this.data:this.localIndex>=a.length||"undefined"===typeof a[this.localIndex]?this.initial:a[this.localIndex]};l.prototype.run=function(){!this.parent||(this.is_static||this.is_constant)||this.setValue(this.initial)};cr.eventvariable=l;q.prototype.toString=function(){return"include:"+this.include_sheet.toString()};q.prototype.postInit=function(){this.include_sheet=this.runtime.eventsheets[this.include_sheet_name];this.sheet.includes.add(this);this.solModifiers=d(this.solModifiers)};q.prototype.run=function(){this.parent&&this.runtime.pushCleanSol(this.runtime.types_by_index);this.include_sheet.hasRun||this.include_sheet.run();this.parent&&this.runtime.popSol(this.runtime.types_by_index)};q.prototype.isActive=function(){for(var a=this.parent;a;){if(a.group&&!this.runtime.activeGroups[a.group_name.toLowerCase()])return!1;a=a.parent}return!0};cr.eventinclude=q;v.prototype.reset=function(a){this.current_event=a;this.actindex=this.cndindex=0;this.temp_parents_arr.length=0;this.else_branch_ran=this.last_event_true=!1};v.prototype.isModifierAfterCnds=function(){return this.current_event.solWriterAfterCnds?!0:this.cndindex=this.type&&(this.first=new cr.expNode(a,b[1]),this.second=new cr.expNode(a,b[2]));if(f){var h,k;h=0;for(k=f.length;hb&&(b+=f.length);f=f[b];-1b&&(b+=f.length);f=f[b].instance_vars[this.varindex];cr.is_string(f)?a.set_string(f):a.set_float(f);this.owner.popTempValue();return}this.owner.popTempValue()}b%=f.length;0>b&&(b+=f.length);f=f[b];b=0;this.object_type.is_family&&(b=f.type.family_var_map[this.object_type.family_index]);f=f.instance_vars[this.varindex+b];cr.is_string(f)?a.set_string(f):a.set_float(f)};a.prototype.eval_int=function(a){a.type=cr.exptype.Integer;a.data=this.value};a.prototype.eval_float=function(a){a.type=cr.exptype.Float;a.data=this.value};a.prototype.eval_string=function(a){a.type=cr.exptype.String;a.data=this.value};a.prototype.eval_unaryminus=function(a){this.first.get(a);a.is_number()&&(a.data=-a.data)};a.prototype.eval_add=function(a){this.first.get(a);var b=this.owner.pushTempValue();this.second.get(b);a.is_number()&&b.is_number()&&(a.data+=b.data,b.is_float()&&a.make_float());this.owner.popTempValue()};a.prototype.eval_subtract=function(a){this.first.get(a);var b=this.owner.pushTempValue();this.second.get(b);a.is_number()&&b.is_number()&&(a.data-=b.data,b.is_float()&&a.make_float());this.owner.popTempValue()};a.prototype.eval_multiply=function(a){this.first.get(a);var b=this.owner.pushTempValue();this.second.get(b);a.is_number()&&b.is_number()&&(a.data*=b.data,b.is_float()&&a.make_float());this.owner.popTempValue()};a.prototype.eval_divide=function(a){this.first.get(a);var b=this.owner.pushTempValue();this.second.get(b);a.is_number()&&b.is_number()&&(a.data/=b.data,a.make_float());this.owner.popTempValue()};a.prototype.eval_mod=function(a){this.first.get(a);var b=this.owner.pushTempValue();this.second.get(b);a.is_number()&&b.is_number()&&(a.data%=b.data,b.is_float()&&a.make_float());this.owner.popTempValue()};a.prototype.eval_power=function(a){this.first.get(a);var b=this.owner.pushTempValue();this.second.get(b);a.is_number()&&b.is_number()&&(a.data=Math.pow(a.data,b.data),b.is_float()&&a.make_float());this.owner.popTempValue()};a.prototype.eval_and=function(a){this.first.get(a);var b=this.owner.pushTempValue();this.second.get(b);a.is_number()?b.is_string()?a.set_string(a.data.toString()+b.data):a.data&&b.data?a.set_int(1):a.set_int(0):a.is_string()&&(b.is_string()?a.data+=b.data:a.data+=(Math.round(1E10*b.data)/1E10).toString());this.owner.popTempValue()};a.prototype.eval_or=function(a){this.first.get(a);var b=this.owner.pushTempValue();this.second.get(b);a.is_number()&&b.is_number()&&(a.data||b.data?a.set_int(1):a.set_int(0));this.owner.popTempValue()};a.prototype.eval_conditional=function(a){this.first.get(a);a.data?this.second.get(a):this.third.get(a)};a.prototype.eval_equal=function(a){this.first.get(a);var b=this.owner.pushTempValue();this.second.get(b);a.set_int(a.data===b.data?1:0);this.owner.popTempValue()};a.prototype.eval_notequal=function(a){this.first.get(a);var b=this.owner.pushTempValue();this.second.get(b);a.set_int(a.data!==b.data?1:0);this.owner.popTempValue()};a.prototype.eval_less=function(a){this.first.get(a);var b=this.owner.pushTempValue();this.second.get(b);a.set_int(a.datab.data?1:0);this.owner.popTempValue()};a.prototype.eval_greaterequal=function(a){this.first.get(a);var b=this.owner.pushTempValue();this.second.get(b);a.set_int(a.data>=b.data?1:0);this.owner.popTempValue()};a.prototype.eval_eventvar_exp=function(a){var b=this.eventvar.getValue();cr.is_number(b)?a.set_float(b):a.set_string(b)};cr.expNode=a;b.prototype.is_int=function(){return this.type===cr.exptype.Integer};b.prototype.is_float=function(){return this.type===cr.exptype.Float};b.prototype.is_number=function(){return this.type===cr.exptype.Integer||this.type===cr.exptype.Float};b.prototype.is_string=function(){return this.type===cr.exptype.String};b.prototype.make_int=function(){this.is_int()||(this.is_float()?this.data=Math.floor(this.data):this.is_string()&&(this.data=parseInt(this.data,10)),this.type=cr.exptype.Integer)};b.prototype.make_float=function(){this.is_float()||(this.is_string()&&(this.data=parseFloat(this.data)),this.type=cr.exptype.Float)};b.prototype.make_string=function(){this.is_string()||(this.data=this.data.toString(),this.type=cr.exptype.String)};b.prototype.set_int=function(a){this.type=cr.exptype.Integer;this.data=Math.floor(a)};b.prototype.set_float=function(a){this.type=cr.exptype.Float;this.data=a};b.prototype.set_string=function(a){this.type=cr.exptype.String;this.data=a};b.prototype.set_any=function(a){cr.is_number(a)?(this.type=cr.exptype.Float,this.data=a):cr.is_string(a)?(this.type=cr.exptype.String,this.data=a.toString()):(this.type=cr.exptype.Integer,this.data=0)};cr.expvalue=b;cr.exptype={Integer:0,Float:1,String:2}})();cr.system_object=function(a){this.runtime=a;this.waits=[]};cr.system_object.prototype.saveToJSON=function(){var a={},b,d,e,f,h,k,c,r;a.waits=[];var n=a.waits,l;b=0;for(d=this.waits.length;bd?1:0}function d(a,b){n&&a===l&&b===q||(n=RegExp(a,b),l=a,q=b);n.lastIndex=0;return n}function e(){}function f(){}function h(a,b,c){if(a!==u||b!==D||c!==B){var e=d(b,c);g=a.match(e);u=a;D=b;B=c}}var k=cr.system_object.prototype;a.prototype.EveryTick=function(){return!0};a.prototype.OnLayoutStart=function(){return!0};a.prototype.OnLayoutEnd=function(){return!0};a.prototype.Compare=function(a,b,c){return cr.do_cmp(a,b,c)};a.prototype.CompareTime=function(a,b){var c=this.runtime.kahanTime.sum;if(0===a){var d=this.runtime.getCurrentCondition();return!d.extra.CompareTime_executed&&c>=b?d.extra.CompareTime_executed=!0:!1}return cr.do_cmp(c,a,b)};a.prototype.LayerVisible=function(a){return a?a.visible:!1};a.prototype.LayerCmpOpacity=function(a,b,c){return a?cr.do_cmp(100*a.opacity,b,c):!1};a.prototype.Repeat=function(a){var b=this.runtime.getCurrentEventStack(),c=b.current_event,d=b.isModifierAfterCnds(),b=this.runtime.pushLoopStack();if(d)for(d=0;d=c&&!a.stopped;--b)this.runtime.pushCopySol(e.solModifiers),a.index=b,e.retrigger(),this.runtime.popSol(e.solModifiers);else for(;b>=c&&!a.stopped;--b)a.index=b,e.retrigger();else if(d)for(;b<=c&&!a.stopped;++b)this.runtime.pushCopySol(e.solModifiers),a.index=b,e.retrigger(),this.runtime.popSol(e.solModifiers);else for(;b<=c&&!a.stopped;++b)a.index=b,e.retrigger();this.runtime.popLoopStack();return!1};var c=[],r=-1;a.prototype.ForEach=function(a){var b=a.getCurrentSol();r++;c.length===r&&c.push([]);var d=c[r];cr.shallowAssignArray(d,b.getObjects());var e=this.runtime.getCurrentEventStack(),f=e.current_event,g=e.isModifierAfterCnds(),e=this.runtime.pushLoopStack(),l,t,p,q,h,k,n=a.is_contained;if(g)for(g=0,l=d.length;g=c+e?(b.extra.Every_lastTime=c+e,d>=b.extra.Every_lastTime+e&&(b.extra.Every_lastTime=d),b.extra.Every_seconds=a,!0):!1};a.prototype.PickNth=function(a,b){if(!a)return!1;var c=a.getCurrentSol(),d=c.getObjects();b=cr.floor(b);if(0>b||b>=d.length)return!1;c.pick_one(d[b]);a.applySolToContainer();return!0};a.prototype.PickRandom=function(a){if(!a)return!1;var b=a.getCurrentSol(),c=b.getObjects(),d=cr.floor(Math.random()*c.length);if(d>=c.length)return!1;b.pick_one(c[d]);a.applySolToContainer();return!0};a.prototype.CompareVar=function(a,b,c){return cr.do_cmp(a.getValue(),b,c)};a.prototype.IsGroupActive=function(a){return this.runtime.activeGroups[a.toLowerCase()]};a.prototype.IsPreview=function(){return"undefined"!==typeof cr_is_preview};a.prototype.PickAll=function(a){if(!a||!a.instances.length)return!1;a.getCurrentSol().select_all=!0;a.applySolToContainer();return!0};a.prototype.IsMobile=function(){return this.runtime.isMobile};a.prototype.CompareBetween=function(a,b,c){return a>=b&&a<=c};a.prototype.Else=function(){var a=this.runtime.getCurrentEventStack();return a.else_branch_ran?!1:!a.last_event_true};a.prototype.OnLoadFinished=function(){return!0};a.prototype.OnCanvasSnapshot=function(){return!0};a.prototype.EffectsSupported=function(){return!!this.runtime.glwrap};a.prototype.OnSaveComplete=function(){return!0};a.prototype.OnLoadComplete=function(){return!0};a.prototype.OnLoadFailed=function(){return!0};a.prototype.ObjectUIDExists=function(a){return!!this.runtime.getObjectByUID(a)};a.prototype.IsOnPlatform=function(a){var b=this.runtime;switch(a){case 0:return!b.isDomFree&&!b.isNodeWebkit&&!b.isPhoneGap&&!b.isWindows8App&&!b.isWindowsPhone8&&!b.isBlackberry10;case 1:return b.isiOS;case 2:return b.isAndroid;case 3:return b.isWindows8App;case 4:return b.isWindowsPhone8;case 5:return b.isBlackberry10;case 6:return b.isTizen;case 7:return b.isNodeWebkit;case 8:return b.isCocoonJs;case 9:return b.isPhoneGap;case 10:return b.isArcade;case 11:return b.isNodeWebkit;default:return!1}};var n=null,l="",q="";a.prototype.RegexTest=function(a,b,c){return d(b,c).test(a)};var v=[];a.prototype.PickOverlappingPoint=function(a,b,c){if(!a)return!1;var d=a.getCurrentSol(),e=d.getObjects(),f=this.runtime.getCurrentEventStack().current_event.orblock,g=this.runtime.getCurrentCondition(),l,t;d.select_all?(cr.shallowAssignArray(v,e),d.else_instances.length=0,d.select_all=!1,d.instances.length=0):f?(cr.shallowAssignArray(v,d.else_instances),d.else_instances.length=0):(cr.shallowAssignArray(v,e),d.instances.length=0);e=0;for(f=v.length;ea&&(a=0);this.runtime.timescale=a};e.prototype.SetObjectTimescale=function(a,b){var c=b;0>c&&(c=0);if(a){var d=a.getCurrentSol().getObjects(),e,f;e=0;for(f=d.length;ea)){var b,c,d,e=this.runtime.getCurrentEventStack(),f;f=t.length?t.pop():{sols:{},solModifiers:[]};f.deleteme=!1;f.time=this.runtime.kahanTime.sum+a;f.ev=e.current_event;f.actindex=e.actindex+1;a=0;for(b=this.runtime.types_by_index.length;athis.runtime.loop_stack_index||(this.runtime.getCurrentLoop().stopped=!0)};e.prototype.GoToLayoutByName=function(a){if(!this.runtime.isloading&&!this.runtime.changelayout)for(var b in this.runtime.layouts)if(this.runtime.layouts.hasOwnProperty(b)&&cr.equals_nocase(b,a)){this.runtime.changelayout=this.runtime.layouts[b];break}};e.prototype.RestartLayout=function(a){if(!this.runtime.isloading&&!this.runtime.changelayout&&this.runtime.running_layout){this.runtime.changelayout=this.runtime.running_layout;var b,c;a=0;for(b=this.runtime.allGroups.length;a=a||0>=b||this.runtime.setSize(a,b)};e.prototype.SetLayoutEffectEnabled=function(a,b){if(this.runtime.running_layout&&this.runtime.glwrap){var c=this.runtime.running_layout.getEffectByName(b);if(c){var d=1===a;c.active!=d&&(c.active=d,this.runtime.running_layout.updateActiveEffects(),this.runtime.redraw=!0)}}};e.prototype.SetLayerEffectEnabled=function(a,b,c){a&&this.runtime.glwrap&&(c=a.getEffectByName(c))&&(b=1===b,c.active!=b&&(c.active=b,a.updateActiveEffects(),this.runtime.redraw=!0))};e.prototype.SetLayoutEffectParam=function(a,b,c){if(this.runtime.running_layout&&this.runtime.glwrap&&(a=this.runtime.running_layout.getEffectByName(a))){var d=this.runtime.running_layout.effect_params[a.index];b=Math.floor(b);0>b||b>=d.length||(1===this.runtime.glwrap.getProgramParameterType(a.shaderindex,b)&&(c/=100),d[b]!==c&&(d[b]=c,a.active&&(this.runtime.redraw=!0)))}};e.prototype.SetLayerEffectParam=function(a,b,c,d){a&&this.runtime.glwrap&&(b=a.getEffectByName(b))&&(a=a.effect_params[b.index],c=Math.floor(c),0>c||c>=a.length||(1===this.runtime.glwrap.getProgramParameterType(b.shaderindex,c)&&(d/=100),a[c]!==d&&(a[c]=d,b.active&&(this.runtime.redraw=!0))))};e.prototype.SaveState=function(a){this.runtime.saveToSlot=a};e.prototype.LoadState=function(a){this.runtime.loadFromSlot=a};e.prototype.LoadStateJSON=function(a){this.runtime.loadFromJson=a};k.acts=new e;f.prototype["int"]=function(a,b){cr.is_string(b)?(a.set_int(parseInt(b,10)),isNaN(a.data)&&(a.data=0)):a.set_int(b)};f.prototype["float"]=function(a,b){cr.is_string(b)?(a.set_float(parseFloat(b)),isNaN(a.data)&&(a.data=0)):a.set_float(b)};f.prototype.str=function(a,b){cr.is_string(b)?a.set_string(b):a.set_string(b.toString())};f.prototype.len=function(a,b){a.set_int(b.length||0)};f.prototype.random=function(a,b,c){void 0===c?a.set_float(Math.random()*b):a.set_float(Math.random()*(c-b)+b)};f.prototype.sqrt=function(a,b){a.set_float(Math.sqrt(b))};f.prototype.abs=function(a,b){a.set_float(Math.abs(b))};f.prototype.round=function(a,b){a.set_int(Math.round(b))};f.prototype.floor=function(a,b){a.set_int(Math.floor(b))};f.prototype.ceil=function(a,b){a.set_int(Math.ceil(b))};f.prototype.sin=function(a,b){a.set_float(Math.sin(cr.to_radians(b)))};f.prototype.cos=function(a,b){a.set_float(Math.cos(cr.to_radians(b)))};f.prototype.tan=function(a,b){a.set_float(Math.tan(cr.to_radians(b)))};f.prototype.asin=function(a,b){a.set_float(cr.to_degrees(Math.asin(b)))};f.prototype.acos=function(a,b){a.set_float(cr.to_degrees(Math.acos(b)))};f.prototype.atan=function(a,b){a.set_float(cr.to_degrees(Math.atan(b)))};f.prototype.exp=function(a,b){a.set_float(Math.exp(b))};f.prototype.ln=function(a,b){a.set_float(Math.log(b))};f.prototype.log10=function(a,b){a.set_float(Math.log(b)/Math.LN10)};f.prototype.max=function(a){var b=arguments[1],c,d;c=2;for(d=arguments.length;carguments[c]&&(b=arguments[c]);a.set_float(b)};f.prototype.dt=function(a){a.set_float(this.runtime.dt)};f.prototype.timescale=function(a){a.set_float(this.runtime.timescale)};f.prototype.wallclocktime=function(a){a.set_float((Date.now()-this.runtime.start_time)/1E3)};f.prototype.time=function(a){a.set_float(this.runtime.kahanTime.sum)};f.prototype.tickcount=function(a){a.set_int(this.runtime.tickcount)};f.prototype.objectcount=function(a){a.set_int(this.runtime.objectcount)};f.prototype.fps=function(a){a.set_int(this.runtime.fps)};f.prototype.loopindex=function(a,b){var c,d,e;if(this.runtime.loop_stack.length)if(b){d=0;for(e=this.runtime.loop_stack.length;dd?a.set_float(d):a.set_float(b)};f.prototype.layerscale=function(a,b){var c=this.runtime.getLayer(b);c?a.set_float(c.scale):a.set_float(0)};f.prototype.layeropacity=function(a,b){var c=this.runtime.getLayer(b);c?a.set_float(100*c.opacity):a.set_float(0)};f.prototype.layerscalerate=function(a,b){var c=this.runtime.getLayer(b);c?a.set_float(c.zoomRate):a.set_float(0)};f.prototype.layerparallaxx=function(a,b){var c=this.runtime.getLayer(b);c?a.set_float(100*c.parallaxX):a.set_float(0)};f.prototype.layerparallaxy=function(a,b){var c=this.runtime.getLayer(b);c?a.set_float(100*c.parallaxY):a.set_float(0)};f.prototype.layoutscale=function(a){this.runtime.running_layout?a.set_float(this.runtime.running_layout.scale):a.set_float(0)};f.prototype.layoutangle=function(a){a.set_float(cr.to_degrees(this.runtime.running_layout.angle))};f.prototype.layerangle=function(a,b){var c=this.runtime.getLayer(b);c?a.set_float(cr.to_degrees(c.angle)):a.set_float(0)};f.prototype.layoutwidth=function(a){a.set_int(this.runtime.running_layout.width)};f.prototype.layoutheight=function(a){a.set_int(this.runtime.running_layout.height)};f.prototype.find=function(a,b,c){cr.is_string(b)&&cr.is_string(c)?a.set_int(b.search(RegExp(cr.regexp_escape(c),"i"))):a.set_int(-1)};f.prototype.left=function(a,b,c){a.set_string(cr.is_string(b)?b.substr(0,c):"")};f.prototype.right=function(a,b,c){a.set_string(cr.is_string(b)?b.substr(b.length-c):"")};f.prototype.mid=function(a,b,c,d){a.set_string(cr.is_string(b)?b.substr(c,d):"")};f.prototype.tokenat=function(a,b,c,d){cr.is_string(b)&&cr.is_string(d)?(b=b.split(d),c=cr.floor(c),0>c||c>=b.length?a.set_string(""):a.set_string(b[c])):a.set_string("")};f.prototype.tokencount=function(a,b,c){cr.is_string(b)&&b.length?a.set_int(b.split(c).length):a.set_int(0)};f.prototype.replace=function(a,b,c,d){cr.is_string(b)&&cr.is_string(c)&&cr.is_string(d)?a.set_string(b.replace(RegExp(cr.regexp_escape(c),"gi"),d)):a.set_string(cr.is_string(b)?b:"")};f.prototype.trim=function(a,b){a.set_string(cr.is_string(b)?b.trim():"")};f.prototype.pi=function(a){a.set_float(cr.PI)};f.prototype.layoutname=function(a){this.runtime.running_layout?a.set_string(this.runtime.running_layout.name):a.set_string("")};f.prototype.renderer=function(a){a.set_string(this.runtime.gl?"webgl":"canvas2d")};f.prototype.anglediff=function(a,b,c){a.set_float(cr.to_degrees(cr.angleDiff(cr.to_radians(b),cr.to_radians(c))))};f.prototype.choose=function(a){var b=cr.floor(Math.random()*(arguments.length-1));a.set_any(arguments[b+1])};f.prototype.rgb=function(a,b,c,d){a.set_int(cr.RGB(b,c,d))};f.prototype.projectversion=function(a){a.set_string(this.runtime.versionstr)};f.prototype.anglelerp=function(a,b,c,d){b=cr.to_radians(b);c=cr.to_radians(c);var e=cr.angleDiff(b,c);cr.angleClockwise(c,b)?a.set_float(cr.to_clamped_degrees(b+e*d)):a.set_float(cr.to_clamped_degrees(b-e*d))};f.prototype.anglerotate=function(a,b,c,d){b=cr.to_radians(b);c=cr.to_radians(c);d=cr.to_radians(d);a.set_float(cr.to_clamped_degrees(cr.angleRotate(b,c,d)))};f.prototype.zeropad=function(a,b,c){var d=0>b?"-":"";0>b&&(b=-b);c-=b.toString().length;for(var e=0;ef||f>=g.length?a.set_string(""):a.set_string(g[f])};f.prototype.infinity=function(a){a.set_float(Infinity)};k.exps=new f;k.runWaits=function(){var a,b,c,d,e,f,g=this.runtime.getCurrentEventStack();a=0;for(c=this.waits.length;athis.runtime.kahanTime.sum)){g.current_event=d.ev;g.actindex=d.actindex;g.cndindex=0;for(b in d.sols)d.sols.hasOwnProperty(b)&&(e=this.runtime.types_by_index[parseInt(b,10)].getCurrentSol(),f=d.sols[b],e.select_all=f.sa,cr.shallowAssignArray(e.instances,f.insts),e=f,e.insts.length=0,p.push(e));d.ev.resume_actions_and_subevents();this.runtime.clearSol(d.solModifiers);d.deleteme=!0}b=a=0;for(c=this.waits.length;aa.viewRight||b.top>a.viewBottom)},r.IsOutsideLayout=function(){this.update_bbox();var a=this.bbox,b=this.runtime.running_layout;return 0>a.right||0>a.bottom||a.left>b.width||a.top>b.height},r.PickDistance=function(a,b,c){var d=this.getCurrentSol(),e=d.getObjects();if(!e.length)return!1;var f=e[0],h=f,k=cr.distanceTo(f.x,f.y,b,c),n,r,m;n=1;for(r=e.length;nk)k=m,h=f;d.pick_one(h);return!0},n.SetX=function(a){this.x!==a&&(this.x=a,this.set_bbox_changed())},n.SetY=function(a){this.y!==a&&(this.y=a,this.set_bbox_changed())},n.SetPos=function(a,b){if(this.x!==a||this.y!==b)this.x=a,this.y=b,this.set_bbox_changed()},n.SetPosToObject=function(a,b){var c=a.getPairedInstance(this);if(c){var d;c.getImagePoint?(d=c.getImagePoint(b,!0),c=c.getImagePoint(b,!1)):(d=c.x,c=c.y);if(this.x!==d||this.y!==c)this.x=d,this.y=c,this.set_bbox_changed()}},n.MoveForward=function(a){0!==a&&(this.x+=Math.cos(this.angle)*a,this.y+=Math.sin(this.angle)*a,this.set_bbox_changed())},n.MoveAtAngle=function(a,b){0!==b&&(this.x+=Math.cos(cr.to_radians(a))*b,this.y+=Math.sin(cr.to_radians(a))*b,this.set_bbox_changed())},b.X=function(a){a.set_float(this.x)},b.Y=function(a){a.set_float(this.y)},b.dt=function(a){a.set_float(this.runtime.getDt(this))});f&&(r.CompareWidth=function(a,b){return cr.do_cmp(this.width,a,b)},r.CompareHeight=function(a,b){return cr.do_cmp(this.height,a,b)},n.SetWidth=function(a){this.width!==a&&(this.width=a,this.set_bbox_changed())},n.SetHeight=function(a){this.height!==a&&(this.height=a,this.set_bbox_changed())},n.SetSize=function(a,b){if(this.width!==a||this.height!==b)this.width=a,this.height=b,this.set_bbox_changed()},b.Width=function(a){a.set_float(this.width)},b.Height=function(a){a.set_float(this.height)},b.BBoxLeft=function(a){this.update_bbox();a.set_float(this.bbox.left)},b.BBoxTop=function(a){this.update_bbox();a.set_float(this.bbox.top)},b.BBoxRight=function(a){this.update_bbox();a.set_float(this.bbox.right)},b.BBoxBottom=function(a){this.update_bbox();a.set_float(this.bbox.bottom)});h&&(r.AngleWithin=function(a,b){return cr.angleDiff(this.angle,cr.to_radians(b))<=cr.to_radians(a)},r.IsClockwiseFrom=function(a){return cr.angleClockwise(this.angle,cr.to_radians(a))},r.IsBetweenAngles=function(a,b){var c=cr.to_clamped_radians(a),d=cr.to_clamped_radians(b),e=cr.clamp_angle(this.angle);return cr.angleClockwise(d,c)?cr.angleClockwise(e,c)&&!cr.angleClockwise(e,d):!(!cr.angleClockwise(e,c)&&cr.angleClockwise(e,d))},n.SetAngle=function(a){a=cr.to_radians(cr.clamp_angle_degrees(a));isNaN(a)||this.angle===a||(this.angle=a,this.set_bbox_changed())},n.RotateClockwise=function(a){0===a||isNaN(a)||(this.angle+=cr.to_radians(a),this.angle=cr.clamp_angle(this.angle),this.set_bbox_changed())},n.RotateCounterclockwise=function(a){0===a||isNaN(a)||(this.angle-=cr.to_radians(a),this.angle=cr.clamp_angle(this.angle),this.set_bbox_changed())},n.RotateTowardAngle=function(a,b){var c=cr.angleRotate(this.angle,cr.to_radians(b),cr.to_radians(a));isNaN(c)||this.angle===c||(this.angle=c,this.set_bbox_changed())},n.RotateTowardPosition=function(a,b,c){b=Math.atan2(c-this.y,b-this.x);a=cr.angleRotate(this.angle,b,cr.to_radians(a));isNaN(a)||this.angle===a||(this.angle=a,this.set_bbox_changed())},n.SetTowardPosition=function(a,b){var c=Math.atan2(b-this.y,a-this.x);isNaN(c)||this.angle===c||(this.angle=c,this.set_bbox_changed())},b.Angle=function(a){a.set_float(cr.to_clamped_degrees(this.angle))});d||(r.CompareInstanceVar=function(a,b,c){return cr.do_cmp(this.instance_vars[a],b,c)},r.IsBoolInstanceVarSet=function(a){return this.instance_vars[a]},r.PickInstVarHiLow=function(a,b){var c=this.getCurrentSol(),d=c.getObjects();if(!d.length)return!1;var e=d[0],f=e,h=e.instance_vars[b],k,n,r;k=1;for(n=d.length;kh)h=r,f=e;c.pick_one(f);return!0},r.PickByUID=function(a){var b,c,d,e,f;if(this.runtime.getCurrentCondition().inverted){f=this.getCurrentSol();if(f.select_all)for(f.select_all=!1,f.instances.length=0,f.else_instances.length=0,d=this.instances,b=0,c=d.length;ba?a=0:1e.layer.index||d.layer.index===e.layer.index&&d.get_zindex()>e.get_zindex())e=d}else if(d.layer.indexc)){var d=1===a;this.active_effect_flags[c]!==d&&(this.active_effect_flags[c]=d,this.updateActiveEffects(),this.runtime.redraw=!0)}}},n.SetEffectParam=function(a,b,c){if(this.runtime.glwrap){var d=this.type.getEffectIndexByName(a);0>d||(a=this.type.effect_types[d],d=this.effect_params[d],b=Math.floor(b),0>b||b>=d.length||(1===this.runtime.glwrap.getProgramParameterType(a.shaderindex,b)&&(c/=100),d[b]!==c&&(d[b]=c,a.active&&(this.runtime.redraw=!0))))}})};cr.set_bbox_changed=function(){this.bbox_changed=!0;this.runtime.redraw=!0;var a,b;a=0;for(b=this.bbox_changed_callbacks.length;athis.bbox.right&&(a=this.bbox.left,this.bbox.left=this.bbox.right,this.bbox.right=a);this.bbox.top>this.bbox.bottom&&(a=this.bbox.top,this.bbox.top=this.bbox.bottom,this.bbox.bottom=a);this.bbox_changed=!1}};cr.inst_contains_pt=function(a,b){return this.bbox.contains_pt(a,b)&&this.bquad.contains_pt(a,b)?this.collision_poly&&!this.collision_poly.is_empty()?(this.collision_poly.cache_poly(this.width,this.height,this.angle),this.collision_poly.contains_pt(a-this.x,b-this.y)):!0:!1};cr.inst_get_iid=function(){this.type.updateIIDs();return this.iid};cr.inst_get_zindex=function(){this.layer.updateZIndices();return this.zindex};cr.inst_updateActiveEffects=function(){this.active_effect_types.length=0;var a,b;a=0;for(b=this.active_effect_flags.length;ad;case 5:return a>=d;default:return!1}};cr.shaders={};cr.shaders.warpripple={src:"varying mediump vec2 vTex;\nuniform lowp sampler2D samplerFront;\nuniform mediump float seconds;\nuniform mediump float pixelWidth;\nuniform mediump float layerScale;\nuniform mediump float freq;\nuniform mediump float amp;\nuniform mediump float speed;\nconst mediump float PI = 3.1415926;\nvoid main(void)\n{\nmediump vec2 p = vTex;\nmediump vec2 tex = vTex * 2.0 - 1.0;\nmediump float d = length(tex);\nmediump float a = atan(tex.y, tex.x);\nd += sin((d * 2.0 * PI) * freq / layerScale / (pixelWidth * 750.0) + (seconds * speed)) * amp * (pixelWidth * 750.0) * layerScale;\ntex.x = cos(a) * d;\ntex.y = sin(a) * d;\ntex = (tex + 1.0) / 2.0;\ngl_FragColor = texture2D(samplerFront, tex);\n}",extendBoxHorizontal:50,extendBoxVertical:50,crossSampling:!1,animated:!0,parameters:[["freq",0,0],["amp",0,1],["speed",0,0]]};cr.plugins_.Audio=function(a){this.runtime=a};(function(){function a(a){a=Math.pow(10,a/20);0>a&&(a=0);1a&&(a=0);1b&&(b=0);1c&&(c=0);1b&&(b=0),1b&&(b=0);1b&&(b=0);1b&&(b=0);1b&&(b=0);1a&&(a=0.01);this.preGain.gain.value=a;this.postGain.gain.value=Math.pow(1/a,0.6)*b};D.prototype.shape=function(a,b,c){var d=1.05*c*b-b;c=0>a?-1:1;a=0>a?-a:a;b=af;++f)e=f/32768,e=this.shape(e,c,d),this.curve[32768+f]=e,this.curve[32768-f-1]=-e};D.prototype.connectTo=function(a){this.wetNode.disconnect();this.wetNode.connect(a);this.dryNode.disconnect();this.dryNode.connect(a)};D.prototype.remove=function(){this.inputNode.disconnect();this.preGain.disconnect();this.waveShaper.disconnect();this.postGain.disconnect();this.wetNode.disconnect();this.dryNode.disconnect()};D.prototype.getInputNode=function(){return this.inputNode};D.prototype.setParam=function(a,b,c,d){switch(a){case 0:b/=100,0>b&&(b=0),1e&&(e=-e),this.peakthis.speeds.length||this.speeds.shift(),this.speeds.push(a),this.lastX=this.obj.x,this.lastY=this.obj.y)};m.prototype.getSpeed=function(){if(!this.speeds.length)return 0;var a,b,c=0;a=0;for(b=this.speeds.length;athis.buffer.bufferObject.duration:this.instanceObject.ended;case V:return this.pgended;case aa:!0}return!0};x.prototype.canBeRecycled=function(){return this.fresh||this.stopped?!0:this.hasEnded()};x.prototype.setPannerEnabled=function(a){J===E&&(!this.pannerEnabled&&a?(this.pannerNode||(this.pannerNode=I.createPanner(),this.pannerNode.panningModel="number"===typeof this.pannerNode.panningModel?ha:["equalpower","HRTF","soundfield"][ha],this.pannerNode.distanceModel="number"===typeof this.pannerNode.distanceModel?ia:["linear","inverse","exponential"][ia],this.pannerNode.refDistance=ka,this.pannerNode.maxDistance=la,this.pannerNode.rolloffFactor=ma),this.gainNode.disconnect(),this.gainNode.connect(this.pannerNode),this.pannerNode.connect(d(this.tag)),this.pannerEnabled=!0):this.pannerEnabled&&!a&&(this.pannerNode.disconnect(),this.gainNode.disconnect(),this.gainNode.connect(d(this.tag)),this.pannerEnabled=!1))};x.prototype.setPan=function(a,b,c,d,e,f){this.pannerEnabled&&J===E&&(this.pannerNode.setPosition(a,b,0),this.pannerNode.setOrientation(Math.cos(cr.to_radians(c)),Math.sin(cr.to_radians(c)),0),this.pannerNode.coneInnerAngle=d,this.pannerNode.coneOuterAngle=e,this.pannerNode.coneOuterGain=f,this.panX=a,this.panY=b,this.panAngle=c,this.panConeInner=d,this.panConeOuter=e,this.panConeOuterGain=f)};x.prototype.setObject=function(a){this.pannerEnabled&&J===E&&(this.objectTracker||(this.objectTracker=new m),this.objectTracker.setObject(a))};x.prototype.tick=function(a){if(this.pannerEnabled&&J===E&&this.objectTracker&&this.objectTracker.hasObject()&&this.isPlaying()){this.objectTracker.tick(a);a=this.objectTracker.obj;var b=cr.rotatePtAround(a.x,a.y,-a.layer.getAngle(),Z,$,!0),c=cr.rotatePtAround(a.x,a.y,-a.layer.getAngle(),Z,$,!1);this.pannerNode.setPosition(b,c,0);b=0;"undefined"!==typeof this.objectTracker.obj.angle&&(b=a.angle-a.layer.getAngle(),this.pannerNode.setOrientation(Math.cos(b),Math.sin(b),0));this.pannerNode.setVelocity(this.objectTracker.getVelocityX(),this.objectTracker.getVelocityY(),0)}};x.prototype.play=function(a,b,c){var d=this.instanceObject;this.looping=a;this.volume=b;c=c||0;switch(this.myapi){case S:1!==d.playbackRate&&(d.playbackRate=1);d.volume!==b*Y&&(d.volume=b*Y);d.loop!==a&&(d.loop=a);d.muted&&(d.muted=!1);if(d.currentTime!==c)try{d.currentTime=c}catch(e){}this.instanceObject.play();break;case E:this.muted=!1;this.mutevol=1;if(this.buffer.myapi===E)this.fresh||(this.instanceObject=I.createBufferSource(),this.instanceObject.buffer=this.buffer.bufferObject,this.instanceObject.connect(this.gainNode)),this.instanceObject.loop=a,this.gainNode.gain.value=b*Y,0===c?h(this.instanceObject):k(this.instanceObject,c,this.getDuration());else{1!==d.playbackRate&&(d.playbackRate=1);d.loop!==a&&(d.loop=a);this.gainNode.gain.value=b*Y;if(d.currentTime!==c)try{d.currentTime=c}catch(f){}d.play()}break;case V:(!this.fresh&&this.stopped||0!==c)&&d.seekTo(c);d.play();this.pgended=!1;break;case aa:N.isDirectCanvas?AppMobi.context.playSound(this.src):AppMobi.player.playSound(this.src)}this.playbackRate=1;this.startTime=N.kahanTime.sum-c;this.is_paused=this.stopped=this.fresh=!1};x.prototype.stop=function(){switch(this.myapi){case S:this.instanceObject.paused||this.instanceObject.pause();break;case E:this.buffer.myapi===E?c(this.instanceObject):this.instanceObject.paused||this.instanceObject.pause();break;case V:this.instanceObject.stop()}this.stopped=!0;this.is_paused=!1};x.prototype.pause=function(){if(!(this.fresh||this.stopped||this.hasEnded()||this.is_paused)){switch(this.myapi){case S:this.instanceObject.paused||this.instanceObject.pause();break;case E:this.buffer.myapi===E?(this.resume_position=this.getPlaybackTime(),this.looping&&(this.resume_position%=this.getDuration()),c(this.instanceObject)):this.instanceObject.paused||this.instanceObject.pause();break;case V:this.instanceObject.pause()}this.is_paused=!0}};x.prototype.resume=function(){if(!this.fresh&&!this.stopped&&!this.hasEnded()&&this.is_paused){switch(this.myapi){case S:this.instanceObject.play();break;case E:this.buffer.myapi===E?(this.instanceObject=I.createBufferSource(),this.instanceObject.buffer=this.buffer.bufferObject,this.instanceObject.connect(this.gainNode),this.instanceObject.loop=this.looping,this.gainNode.gain.value=Y*this.volume*this.mutevol,this.startTime=N.kahanTime.sum-this.resume_position,k(this.instanceObject,this.resume_position,this.getDuration())):this.instanceObject.play();break;case V:this.instanceObject.play()}this.is_paused=!1}};x.prototype.seek=function(a){if(!(this.fresh||this.stopped||this.hasEnded()))switch(this.myapi){case S:try{this.instanceObject.currentTime=a}catch(b){}break;case E:if(this.buffer.myapi===E)this.is_paused?this.resume_position=a:(this.pause(),this.resume_position=a,this.resume());else try{this.instanceObject.currentTime=a}catch(c){}}};x.prototype.reconnect=function(a){this.myapi===E&&(this.pannerEnabled?(this.pannerNode.disconnect(),this.pannerNode.connect(a)):(this.gainNode.disconnect(),this.gainNode.connect(a)))};x.prototype.getDuration=function(){switch(this.myapi){case S:if("undefined"!==typeof this.instanceObject.duration)return this.instanceObject.duration;break;case E:return this.buffer.bufferObject.duration;case V:return this.instanceObject.getDuration()}return 0};x.prototype.getPlaybackTime=function(){var a=this.getDuration(),b=0;switch(this.myapi){case S:"undefined"!==typeof this.instanceObject.currentTime&&(b=this.instanceObject.currentTime);break;case E:if(this.buffer.myapi===E){if(this.is_paused)return this.resume_position;b=N.kahanTime.sum-this.startTime}else"undefined"!==typeof this.instanceObject.currentTime&&(b=this.instanceObject.currentTime)}!this.looping&&b>a&&(b=a);return b};x.prototype.isPlaying=function(){return!this.is_paused&&!this.fresh&&!this.stopped&&!this.hasEnded()};x.prototype.setVolume=function(a){this.volume=a;this.updateVolume()};x.prototype.updateVolume=function(){var a=this.volume*Y;switch(this.myapi){case S:this.instanceObject.volume&&this.instanceObject.volume!==a&&(this.instanceObject.volume=a);break;case E:this.gainNode.gain.value=a*this.mutevol}};x.prototype.getVolume=function(){return this.volume};x.prototype.doSetMuted=function(a){switch(this.myapi){case S:this.instanceObject.muted!==!!a&&(this.instanceObject.muted=!!a);break;case E:this.mutevol=a?0:1,this.gainNode.gain.value=Y*this.volume*this.mutevol}};x.prototype.setMuted=function(a){this.is_muted=!!a;this.doSetMuted(this.is_muted||this.is_silent)};x.prototype.setSilent=function(a){this.is_silent=!!a;this.doSetMuted(this.is_muted||this.is_silent)};x.prototype.setLooping=function(a){this.looping=a;switch(this.myapi){case S:this.instanceObject.loop!==!!a&&(this.instanceObject.loop=!!a);break;case E:this.instanceObject.loop!==!!a&&(this.instanceObject.loop=!!a)}};x.prototype.setPlaybackRate=function(a){this.playbackRate=a;this.updatePlaybackRate()};x.prototype.updatePlaybackRate=function(){var a=this.playbackRate;if(1===fa&&!this.is_music||2===fa)a*=N.timescale;switch(this.myapi){case S:this.instanceObject.playbackRate!==a&&(this.instanceObject.playbackRate=a);break;case E:this.buffer.myapi===E?this.instanceObject.playbackRate.value!==a&&(this.instanceObject.playbackRate.value=a):this.instanceObject.playbackRate!==a&&(this.instanceObject.playbackRate=a)}};x.prototype.setSuspended=function(a){switch(this.myapi){case S:a?this.isPlaying()?(this.instanceObject.pause(),this.resume_me=!0):this.resume_me=!1:this.resume_me&&this.instanceObject.play();break;case E:a?this.isPlaying()?(this.buffer.myapi===E?(this.resume_position=this.getPlaybackTime(),this.looping&&(this.resume_position%=this.getDuration()),c(this.instanceObject)):this.instanceObject.pause(),this.resume_me=!0):this.resume_me=!1:this.resume_me&&(this.buffer.myapi===E?(this.instanceObject=I.createBufferSource(),this.instanceObject.buffer=this.buffer.bufferObject,this.instanceObject.connect(this.gainNode),this.instanceObject.loop=this.looping,this.gainNode.gain.value=Y*this.volume*this.mutevol,this.startTime=N.kahanTime.sum-this.resume_position,k(this.instanceObject,this.resume_position,this.getDuration())):this.instanceObject.play());break;case V:a?this.isPlaying()?(this.instanceObject.pause(),this.resume_me=!0):this.resume_me=!1:this.resume_me&&this.instanceObject.play()}};w.Instance=function(a){this.type=a;N=this.runtime=a.runtime;R=this;this.listenerTracker=null;this.listenerZ=-600;I=null;"undefined"!==typeof AudioContext?(J=E,I=new AudioContext):"undefined"!==typeof webkitAudioContext&&(J=E,I=new webkitAudioContext);this.runtime.isiOS&&J===E&&document.addEventListener("touchstart",function(){if(!na){var a=I.createBuffer(1,1,22050),b=I.createBufferSource();b.buffer=a;b.connect(I.destination);h(b);na=!0}},!0);J!==E&&(this.runtime.isPhoneGap?J=V:this.runtime.isAppMobi&&(J=aa));J===V&&(U=location.href,a=U.lastIndexOf("/"),-1"!==b&&(a.playTagWhenReady=b,a.loopWhenReady=d,a.volumeWhenReady=e),null;l=new x(a,b);K.push(l);return l};var M=[];F.prototype.OnEnded=function(a){return cr.equals_nocase(W,a)};F.prototype.PreloadsComplete=function(){var a,b;a=0;for(b=da.length;a",b,!1)}};y.prototype.PreloadByName=function(a,b){if(!T){var c=1===a,d=this.runtime.files_subfolder+b.toLowerCase()+(X?".ogg":".m4a");J===aa?this.runtime.isDirectCanvas?AppMobi.context.loadSound(d):AppMobi.player.loadSound(d):J!==V&&this.getAudioInstance(d,"",c,!1)}};y.prototype.SetPlaybackRate=function(a,b){z(a);0>b&&(b=0);var c,d;c=0;for(d=M.length;cb||b>=ja.length)||(a=a.toLowerCase(),g/=100,0>g&&(g=0),1e&&(e=0),1f&&(f=0),1g&&(g=0),1d&&(d=0);1c&&(c=0),1c&&(c=0),1f&&(f=0),1b||b>=a.length||a[b].setParam(c,d,e,f)))};y.prototype.SetListenerObject=function(a){a&&J===E&&(a=a.getFirstPicked())&&(this.listenerTracker.setObject(a),Z=a.x,$=a.y)};y.prototype.SetListenerZ=function(a){this.listenerZ=a};w.acts=new y;H.prototype.Duration=function(a,b){z(b);M.length?a.set_float(M[0].getDuration()):a.set_float(0)};H.prototype.PlaybackTime=function(a,b){z(b);M.length?a.set_float(M[0].getPlaybackTime()):a.set_float(0)};H.prototype.Volume=function(a,c){z(c);if(M.length){var d=M[0].getVolume();a.set_float(b(d))}else a.set_float(0)};H.prototype.MasterVolume=function(a){a.set_float(Y)};H.prototype.EffectCount=function(a,b){b=b.toLowerCase();var c=null;L.hasOwnProperty(b)&&(c=L[b]);a.set_int(c?c.length:0)};H.prototype.AnalyserFreqBinCount=function(a,b,c){b=b.toLowerCase();c=Math.floor(c);b=Q(b,c);a.set_int(b?b.node.frequencyBinCount:0)};H.prototype.AnalyserFreqBinAt=function(a,b,c,d){b=b.toLowerCase();c=Math.floor(c);d=Math.floor(d);(b=Q(b,c))?0>d||d>=b.node.frequencyBinCount?a.set_float(0):a.set_float(b.freqBins[d]):a.set_float(0)};H.prototype.AnalyserPeakLevel=function(a,b,c){b=b.toLowerCase();c=Math.floor(c);(b=Q(b,c))?a.set_float(b.peak):a.set_float(0)};H.prototype.AnalyserRMSLevel=function(a,b,c){b=b.toLowerCase();c=Math.floor(c);(b=Q(b,c))?a.set_float(b.rms):a.set_float(0)};w.exps=new H})();cr.plugins_.Browser=function(a){this.runtime=a};(function(){function a(){}function b(){}function d(){"undefined"!==typeof jQuery&&k.setSize(jQuery(window).width(),jQuery(window).height())}function e(){}var f=cr.plugins_.Browser.prototype;f.Type=function(a){this.plugin=a;this.runtime=a.runtime};f.Type.prototype.onCreate=function(){};f.Instance=function(a){this.type=a;this.runtime=a.runtime};f.Instance.prototype.onCreate=function(){var a=this;window.addEventListener("resize",function(){a.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnResize,a)});"undefined"!==typeof navigator.onLine&&(window.addEventListener("online",function(){a.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnOnline,a)}),window.addEventListener("offline",function(){a.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnOffline,a)}));"undefined"!==typeof window.applicationCache&&(window.applicationCache.addEventListener("updateready",function(){a.runtime.loadingprogress=1;a.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnUpdateReady,a)}),window.applicationCache.addEventListener("progress",function(b){a.runtime.loadingprogress=b.loaded/b.total}));this.runtime.isDirectCanvas||(document.addEventListener("appMobi.device.update.available",function(){a.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnUpdateReady,a)}),document.addEventListener("menubutton",function(){a.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnMenuButton,a)}),document.addEventListener("searchbutton",function(){a.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnSearchButton,a)}));this.runtime.addSuspendCallback(function(b){b?a.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnPageHidden,a):a.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnPageVisible,a)});this.is_arcade="undefined"!==typeof window.is_scirra_arcade;this.fullscreenOldMarginCss=""};a.prototype.CookiesEnabled=function(){return navigator?navigator.cookieEnabled:!1};a.prototype.IsOnline=function(){return navigator?navigator.onLine:!1};a.prototype.HasJava=function(){return navigator?navigator.javaEnabled():!1};a.prototype.OnOnline=function(){return!0};a.prototype.OnOffline=function(){return!0};a.prototype.IsDownloadingUpdate=function(){return"undefined"===typeof window.applicationCache?!1:window.applicationCache.status===window.applicationCache.DOWNLOADING};a.prototype.OnUpdateReady=function(){return!0};a.prototype.PageVisible=function(){return!this.runtime.isSuspended};a.prototype.OnPageVisible=function(){return!0};a.prototype.OnPageHidden=function(){return!0};a.prototype.OnResize=function(){return!0};a.prototype.IsFullscreen=function(){return!!(document.mozFullScreen||document.webkitIsFullScreen||document.fullScreen||this.runtime.isNodeFullscreen)};a.prototype.OnMenuButton=function(){return!0};a.prototype.OnSearchButton=function(){return!0};a.prototype.IsMetered=function(){var a=navigator.connection||navigator.mozConnection||navigator.webkitConnection;return a?a.metered:!1};a.prototype.IsCharging=function(){var a=navigator.battery||navigator.mozBattery||navigator.webkitBattery;return a?a.charging:!0};f.cnds=new a;b.prototype.Alert=function(a){this.runtime.isDomFree||alert(a.toString())};b.prototype.Close=function(){this.runtime.isCocoonJs?CocoonJS.App.forceToFinish():this.runtime.isNodeWebkit?window.nwgui.App.quit():this.is_arcade||this.runtime.isDomFree||window.close()};b.prototype.Focus=function(){this.runtime.isNodeWebkit?window.nwgui.Window.get().focus():this.is_arcade||this.runtime.isDomFree||window.focus()};b.prototype.Blur=function(){this.runtime.isNodeWebkit?window.nwgui.Window.get().blur():this.is_arcade||this.runtime.isDomFree||window.blur()};b.prototype.GoBack=function(){this.is_arcade||(this.runtime.isDomFree||!window.back)||window.back()};b.prototype.GoForward=function(){this.is_arcade||(this.runtime.isDomFree||!window.forward)||window.forward()};b.prototype.GoHome=function(){this.is_arcade||(this.runtime.isDomFree||!window.home)||window.home()};b.prototype.GoToURL=function(a){this.runtime.isCocoonJs?CocoonJS.App.openURL(a):this.is_arcade||this.runtime.isDomFree||(window.location=a)};b.prototype.GoToURLWindow=function(a,b){this.runtime.isCocoonJs?CocoonJS.App.openURL(a):this.is_arcade||this.runtime.isDomFree||window.open(a,b)};b.prototype.Reload=function(){this.is_arcade||this.runtime.isDomFree||window.location.reload()};var h=!0,k=null;b.prototype.RequestFullScreen=function(a){this.runtime.isDomFree?cr.logexport("[Construct 2] Requesting fullscreen is not supported on this platform - the request has been ignored"):(2<=a&&(a+=1),6===a&&(a=2),this.runtime.isNodeWebkit?this.runtime.isNodeFullscreen||(window.nwgui.Window.get().enterFullscreen(),this.runtime.isNodeFullscreen=!0):document.mozFullScreen||(document.webkitIsFullScreen||document.fullScreen)||(this.fullscreenOldMarginCss=jQuery(this.runtime.canvasdiv).css("margin"),jQuery(this.runtime.canvasdiv).css("margin","0"),window.c2resizestretchmode=0a||a>=this.keyMap.length?!1:this.keyMap[a]};a.prototype.OnKeyCode=function(a){return a===this.triggerKey};a.prototype.OnKeyCodeReleased=function(a){return a===this.triggerKey};e.cnds=new a;e.acts=new function(){};b.prototype.LastKeyCode=function(a){a.set_int(this.triggerKey)};b.prototype.StringFromKeyCode=function(a,b){a.set_string(d(b))};e.exps=new b})();cr.plugins_.NodeWebkit=function(a){this.runtime=a};(function(){function a(){}function b(){}function d(){}var e=!1,f=null,h=null,k=null,c="",r="",n="\\",l=[],q="",v="",t=cr.plugins_.NodeWebkit.prototype;t.Type=function(a){this.plugin=a;this.runtime=a.runtime};t.Type.prototype.onCreate=function(){};t.Instance=function(a){this.type=a;this.runtime=a.runtime};var p=t.Instance.prototype;p.onCreate=function(){e=this.runtime.isNodeWebkit;var a=this;e&&(f=require("path"),h=require("fs"),k=require("child_process"),"win32"!==process.platform&&(n="/"),c=f.dirname(process.execPath)+n,r=(process.env.HOME||process.env.HOMEPATH||process.env.USERPROFILE)+n,window.ondrop=function(b){b.preventDefault();for(var c=0;cb||b>=l.length?a.set_string(""):a.set_string(l[b])};d.prototype.DroppedFile=function(a){a.set_string(q)};d.prototype.ChosenPath=function(a){a.set_string(v)};t.exps=new d})();cr.plugins_.Particles=function(a){this.runtime=a};(function(){function a(a){this.owner=a;this.active=!1;this.angle=this.speed=this.y=this.x=0;this.opacity=1;this.age=this.gs=this.size=this.grow=0;cr.seal(this)}function b(){}function d(){}function e(){}var f=cr.plugins_.Particles.prototype;f.Type=function(a){this.plugin=a;this.runtime=a.runtime};var h=f.Type.prototype;h.onCreate=function(){this.is_family||(this.texture_img=new Image,this.texture_img.src=this.texture_file,this.texture_img.cr_filesize=this.texture_filesize,this.webGL_texture=null,this.runtime.wait_for_textures.push(this.texture_img))};h.onLostWebGLContext=function(){this.is_family||(this.webGL_texture=null)};h.onRestoreWebGLContext=function(){this.is_family||!this.instances.length||this.webGL_texture||(this.webGL_texture=this.runtime.glwrap.loadTexture(this.texture_img,!0,this.runtime.linearSampling,this.texture_pixelformat))};h.loadTextures=function(){this.is_family||(this.webGL_texture||!this.runtime.glwrap)||(this.webGL_texture=this.runtime.glwrap.loadTexture(this.texture_img,!0,this.runtime.linearSampling,this.texture_pixelformat))};h.unloadTextures=function(){this.is_family||(this.instances.length||!this.webGL_texture)||(this.runtime.glwrap.deleteTexture(this.webGL_texture),this.webGL_texture=null)};h.preloadCanvas2D=function(a){a.drawImage(this.texture_img,0,0)};a.prototype.init=function(){var a=this.owner;this.x=a.x-a.xrandom/2+Math.random()*a.xrandom;this.y=a.y-a.yrandom/2+Math.random()*a.yrandom;this.speed=a.initspeed-a.speedrandom/2+Math.random()*a.speedrandom;this.angle=a.angle-a.spraycone/2+Math.random()*a.spraycone;this.opacity=a.initopacity;this.size=a.initsize-a.sizerandom/2+Math.random()*a.sizerandom;this.grow=a.growrate-a.growrandom/2+Math.random()*a.growrandom;this.age=this.gs=0};a.prototype.tick=function(a){var b=this.owner;this.x+=Math.cos(this.angle)*this.speed*a;this.y+=Math.sin(this.angle)*this.speed*a;this.y+=this.gs*a;this.speed+=b.acc*a;this.size+=this.grow*a;this.gs+=b.g*a;this.age+=a;1>this.size?this.active=!1:(0!==b.lifeanglerandom&&(this.angle+=Math.random()*b.lifeanglerandom*a-b.lifeanglerandom*a/2),0!==b.lifespeedrandom&&(this.speed+=Math.random()*b.lifespeedrandom*a-b.lifespeedrandom*a/2),0!==b.lifeopacityrandom&&(this.opacity+=Math.random()*b.lifeopacityrandom*a-b.lifeopacityrandom*a/2,0>this.opacity?this.opacity=0:1=b.destroymode&&this.age>=b.timeout&&(this.active=!1),2===b.destroymode&&0>=this.speed&&(this.active=!1))};a.prototype.draw=function(a){var b=this.owner.opacity*this.opacity;if(0!==b){0===this.owner.destroymode&&(b*=1-this.age/this.owner.timeout);a.globalAlpha=b;var b=this.x-this.size/2,d=this.y-this.size/2;this.owner.runtime.pixel_rounding&&(b=b+0.5|0,d=d+0.5|0);a.drawImage(this.owner.type.texture_img,b,d,this.size,this.size)}};a.prototype.drawGL=function(a){var b=this.owner.opacity*this.opacity;0===this.owner.destroymode&&(b*=1-this.age/this.owner.timeout);var d=this.size,e=d*this.owner.particlescale,f=this.x-d/2,h=this.y-d/2;this.owner.runtime.pixel_rounding&&(f=f+0.5|0,h=h+0.5|0);1>e||0===b||(ea.maxPointSize?(a.setOpacity(b),a.quad(f,h,f+d,h,f+d,h+d,f,h+d)):a.point(this.x,this.y,e,b))};a.prototype.left=function(){return this.x-this.size/2};a.prototype.right=function(){return this.x+this.size/2};a.prototype.top=function(){return this.y-this.size/2};a.prototype.bottom=function(){return this.y+this.size/2};f.Instance=function(a){this.type=a;this.runtime=a.runtime};var h=f.Instance.prototype,k=[];h.onCreate=function(){var a=this.properties;this.rate=a[0];this.spraycone=cr.to_radians(a[1]);this.spraytype=a[2];this.spraying=!0;this.initspeed=a[3];this.initsize=a[4];this.initopacity=a[5]/100;this.growrate=a[6];this.xrandom=a[7];this.yrandom=a[8];this.speedrandom=a[9];this.sizerandom=a[10];this.growrandom=a[11];this.acc=a[12];this.g=a[13];this.lifeanglerandom=a[14];this.lifespeedrandom=a[15];this.lifeopacityrandom=a[16];this.destroymode=a[17];this.timeout=a[18];this.particleCreateCounter=0;this.particlescale=1;this.particleBoxLeft=this.x;this.particleBoxTop=this.y;this.particleBoxRight=this.x;this.particleBoxBottom=this.y;this.add_bbox_changed_callback(function(a){a.bbox.set(a.particleBoxLeft,a.particleBoxTop,a.particleBoxRight,a.particleBoxBottom);a.bquad.set_from_rect(a.bbox);a.bbox_changed=!1});this.recycled||(this.particles=[]);this.runtime.tickMe(this);this.type.loadTextures();if(1===this.spraytype)for(a=0;athis.particleBoxRight&&(this.particleBoxRight=e.right()),e.top()this.particleBoxBottom&&(this.particleBoxBottom=e.bottom()),f++):k.push(e);this.particles.length=f;this.set_bbox_changed();this.first_tick=!1;1===this.spraytype&&0===this.particles.length&&this.runtime.DestroyInstance(this)};h.draw=function(a){var b,d,e,f=this.layer;b=0;for(d=this.particles.length;b=f.viewLeft&&(e.bottom()>=f.viewTop&&e.left()<=f.viewRight&&e.top()<=f.viewBottom)&&e.draw(a)};h.drawGL=function(a){this.particlescale=this.layer.getScale();a.setTexture(this.type.webGL_texture);var b,d,e,f=this.layer;b=0;for(d=this.particles.length;b=f.viewLeft&&(e.bottom()>=f.viewTop&&e.left()<=f.viewRight&&e.top()<=f.viewBottom)&&e.drawGL(a)};b.prototype.IsSpraying=function(){return this.spraying};f.cnds=new b;d.prototype.SetSpraying=function(a){this.spraying=0!==a};d.prototype.SetEffect=function(a){this.compositeOp=cr.effectToCompositeOp(a);cr.setGLBlend(this,a,this.runtime.gl);this.runtime.redraw=!0};d.prototype.SetRate=function(a){this.rate=a;var b;if(1===this.spraytype&&this.first_tick)if(athis.particles.length)for(a-=this.particles.length,b=0;ba.viewRight||b.top>a.viewBottom)this.runtime.glwrap.deleteTexture(this.mytex),this.mycanvas=this.myctx=this.mytex=null}};k.onDestroy=function(){this.mycanvas=this.myctx=null;this.runtime.glwrap&&this.mytex&&this.runtime.glwrap.deleteTexture(this.mytex);this.mytex=null};k.updateFont=function(){this.font=this.fontstyle+" "+this.ptSize.toString()+"pt "+this.facename;this.text_changed=!0;this.runtime.redraw=!0};k.draw=function(a,b){a.font=this.font;a.textBaseline="top";a.fillStyle=this.color;a.globalAlpha=b?1:this.opacity;var c=1;b&&(c=this.layer.getScale(),a.save(),a.scale(c,c));if(this.text_changed||this.width!==this.lastwrapwidth)this.type.plugin.WordWrap(this.text,this.lines,a,this.width,this.wrapbyword),this.text_changed=!1,this.lastwrapwidth=this.width;this.update_bbox();var c=b?0:this.bquad.tlx,d=b?0:this.bquad.tly;this.runtime.pixel_rounding&&(c=c+0.5|0,d=d+0.5|0);0===this.angle||b||(a.save(),a.translate(c,d),a.rotate(this.angle),d=c=0);var e=d+this.height,f=this.pxHeight,f=f+this.line_height_offset*this.runtime.devicePixelRatio,h,k;1===this.valign?d+=Math.max(this.height/2-this.lines.length*f/2,0):2===this.valign&&(d+=Math.max(this.height-this.lines.length*f-2,0));for(k=0;k=e-f);k++);(0!==this.angle||b)&&a.restore();this.last_render_tick=this.runtime.tickcount};k.drawGL=function(a){if(!(1>this.width||1>this.height)){var b=this.text_changed||this.need_text_redraw;this.need_text_redraw=!1;var c=this.layer.getScale(),d=this.layer.getAngle(),e=this.rcTex,f=c*this.width,h=c*this.height,k=Math.ceil(f),n=Math.ceil(h),s=this.runtime.width/2,m=this.runtime.height/2;this.myctx||(this.mycanvas=document.createElement("canvas"),this.mycanvas.width=k,this.mycanvas.height=n,this.lastwidth=k,this.lastheight=n,b=!0,this.myctx=this.mycanvas.getContext("2d"));if(k!==this.lastwidth||n!==this.lastheight)this.mycanvas.width=k,this.mycanvas.height=n,this.mytex&&(a.deleteTexture(this.mytex),this.mytex=null),b=!0;b&&(this.myctx.clearRect(0,0,k,n),this.draw(this.myctx,!0),this.mytex||(this.mytex=a.createEmptyTexture(k,n,this.runtime.linearSampling,this.runtime.isMobile)),a.videoToTexture(this.mycanvas,this.mytex,this.runtime.isMobile));this.lastwidth=k;this.lastheight=n;a.setTexture(this.mytex);a.setOpacity(this.opacity);a.resetModelView();a.translate(-s,-m);a.updateModelView();var r=this.bquad,b=this.layer.layerToCanvas(r.tlx,r.tly,!0),s=this.layer.layerToCanvas(r.tlx,r.tly,!1),m=this.layer.layerToCanvas(r.trx,r.try_,!0),x=this.layer.layerToCanvas(r.trx,r.try_,!1),z=this.layer.layerToCanvas(r.brx,r.bry,!0),G=this.layer.layerToCanvas(r.brx,r.bry,!1),C=this.layer.layerToCanvas(r.blx,r.bly,!0),r=this.layer.layerToCanvas(r.blx,r.bly,!1);if(this.runtime.pixel_rounding||0===this.angle&&0===d)var F=(b+0.5|0)-b,y=(s+0.5|0)-s,b=b+F,s=s+y,m=m+F,x=x+y,z=z+F,G=G+y,C=C+F,r=r+y;0===this.angle&&0===d?(m=b+k,x=s,z=m,G=s+n,C=b,r=G,e.right=1,e.bottom=1):(e.right=f/k,e.bottom=h/n);a.quadTex(b,s,m,x,z,G,C,r,e);a.resetModelView();a.scale(c,c);a.rotateZ(-this.layer.getAngle());a.translate((this.layer.viewLeft+this.layer.viewRight)/-2,(this.layer.viewTop+this.layer.viewBottom)/-2);a.updateModelView();this.last_render_tick=this.runtime.tickcount}};var r=[];h.TokeniseWords=function(a){r.length=0;for(var b="",c,d=0;d=f)b(d);else{if(100>=c.length&&-1===c.indexOf("\n")){var g=e.measureText(c).width;if(g<=f){b(d);d.push(a());d[0].text=c;d[0].width=g;return}}this.WrapText(c,d,e,f,h)}else b(d)};h.WrapText=function(b,c,d,e,f){f&&(this.TokeniseWords(b),b=r);var g="",h,k,B,s=0;for(B=0;B=c.length&&c.push(a()),k=c[s],k.text=g,k.width=d.measureText(g).width,s++,g=""):(h=g,g+=b[B],k=d.measureText(g).width,k>=e&&(s>=c.length&&c.push(a()),k=c[s],k.text=h,k.width=d.measureText(h).width,s++,g=b[B],f||" "!==g||(g="")));g.length&&(s>=c.length&&c.push(a()),k=c[s],k.text=g,k.width=d.measureText(g).width,s++);for(B=s;Ba&&(a=Math.round(1E10*a)/1E10);a=a.toString();this.text!==a&&(this.text=a,this.text_changed=!0,this.runtime.redraw=!0)};e.prototype.AppendText=function(a){cr.is_number(a)&&(a=Math.round(1E10*a)/1E10);if(a=a.toString())this.text+=a,this.text_changed=!0,this.runtime.redraw=!0};e.prototype.SetFontFace=function(a,b){var c="";switch(b){case 1:c="bold";break;case 2:c="italic";break;case 3:c="bold italic"}if(a!==this.facename||c!==this.fontstyle)this.facename=a,this.fontstyle=c,this.updateFont()};e.prototype.SetFontSize=function(a){this.ptSize!==a&&(this.ptSize=a,this.pxHeight=Math.ceil(96*(this.ptSize/72))+4,this.updateFont())};e.prototype.SetFontColor=function(a){a="rgb("+cr.GetRValue(a).toString()+","+cr.GetGValue(a).toString()+","+cr.GetBValue(a).toString()+")";a!==this.color&&(this.color=a,this.need_text_redraw=!0,this.runtime.redraw=!0)};e.prototype.SetWebFont=function(a,b){if(this.runtime.isDomFree)cr.logexport("[Construct 2] Text plugin: 'Set web font' not supported on this platform - the action has been ignored");else{var d=this,e=function(){d.runtime.redraw=!0;d.text_changed=!0};if(c.hasOwnProperty(b)){var f="'"+a+"'";if(this.facename!==f)for(this.facename=f,this.updateFont(),f=1;10>f;f++)setTimeout(e,100*f),setTimeout(e,1E3*f)}else for(f=document.createElement("link"),f.href=b,f.rel="stylesheet",f.type="text/css",f.onload=e,document.getElementsByTagName("head")[0].appendChild(f),c[b]=!0,this.facename="'"+a+"'",this.updateFont(),f=1;10>f;f++)setTimeout(e,100*f),setTimeout(e,1E3*f)}};e.prototype.SetEffect=function(a){this.compositeOp=cr.effectToCompositeOp(a);cr.setGLBlend(this,a,this.runtime.gl);this.runtime.redraw=!0};h.acts=new e;f.prototype.Text=function(a){a.set_string(this.text)};f.prototype.FaceName=function(a){a.set_string(this.facename)};f.prototype.FaceSize=function(a){a.set_int(this.ptSize)};f.prototype.TextWidth=function(a){var b=0,c,d,e;c=0;for(d=this.lines.length;cc-b.time||(b.lasttime=b.time,b.lastx=b.x,b.lasty=b.y,b.time=c,b.x=a.pageX-d.left,b.y=a.pageY-d.top)}}};h.onPointerStart=function(a){if(a.pointerType!==a.MSPOINTER_TYPE_MOUSE){a.preventDefault&&a.preventDefault();var b=this.runtime.isDomFree?k:jQuery(this.runtime.canvas).offset(),c=a.pageX-b.left,b=a.pageY-b.top,d=cr.performance_now();this.trigger_index=this.touches.length;this.trigger_id=a.pointerId;this.touches.push({time:d,x:c,y:b,lasttime:d,lastx:c,lasty:b,id:a.pointerId,startindex:this.trigger_index});this.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnNthTouchStart,this);this.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnTouchStart,this);this.curTouchX=c;this.curTouchY=b;this.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnTouchObject,this)}};h.onPointerEnd=function(a){a.pointerType!==a.MSPOINTER_TYPE_MOUSE&&(a.preventDefault&&a.preventDefault(),a=this.findTouch(a.pointerId),this.trigger_index=0<=a?this.touches[a].startindex:-1,this.trigger_id=0<=a?this.touches[a].id:-1,this.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnNthTouchEnd,this),this.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnTouchEnd,this),0<=a&&this.touches.splice(a,1))};h.onTouchMove=function(a){a.preventDefault&&a.preventDefault();var b=cr.performance_now(),c,d,e,f;c=0;for(d=a.changedTouches.length;cb-f.time||(f.lasttime=f.time,f.lastx=f.x,f.lasty=f.y,f.time=b,f.x=e.pageX-h.left,f.y=e.pageY-h.top)}};h.onTouchStart=function(a){a.preventDefault&&a.preventDefault();var b=this.runtime.isDomFree?k:jQuery(this.runtime.canvas).offset(),c=cr.performance_now(),d,e,f,h;d=0;for(e=a.changedTouches.length;da||a>=this.touches.length)return!1;var d=this.touches[a];a=cr.distanceTo(d.x,d.y,d.lastx,d.lasty);var d=(d.time-d.lasttime)/1E3,e=0;0=a+1};f.cnds=new d;e.prototype.TouchCount=function(a){a.set_int(this.touches.length)};e.prototype.X=function(a,b){if(this.touches.length){var c,d,e,f,h;cr.is_undefined(b)?(c=this.runtime.getLayerByNumber(0),d=c.scale,e=c.zoomRate,f=c.parallaxX,h=c.angle,c.scale=this.runtime.running_layout.scale,c.zoomRate=1,c.parallaxX=1,c.angle=this.runtime.running_layout.angle,a.set_float(c.canvasToLayer(this.touches[0].x,this.touches[0].y,!0)),c.scale=d,c.zoomRate=e,c.parallaxX=f,c.angle=h):(c=cr.is_number(b)?this.runtime.getLayerByNumber(b):this.runtime.getLayerByName(b))?a.set_float(c.canvasToLayer(this.touches[0].x,this.touches[0].y,!0)):a.set_float(0)}else a.set_float(0)};e.prototype.XAt=function(a,b,c){b=Math.floor(b);if(0>b||b>=this.touches.length)a.set_float(0);else{var d,e,f,h;cr.is_undefined(c)?(c=this.runtime.getLayerByNumber(0),d=c.scale,e=c.zoomRate,f=c.parallaxX,h=c.angle,c.scale=this.runtime.running_layout.scale,c.zoomRate=1,c.parallaxX=1,c.angle=this.runtime.running_layout.angle,a.set_float(c.canvasToLayer(this.touches[b].x,this.touches[b].y,!0)),c.scale=d,c.zoomRate=e,c.parallaxX=f,c.angle=h):(c=cr.is_number(c)?this.runtime.getLayerByNumber(c):this.runtime.getLayerByName(c))?a.set_float(c.canvasToLayer(this.touches[b].x,this.touches[b].y,!0)):a.set_float(0)}};e.prototype.XForID=function(a,b,c){b=this.findTouch(b);if(0>b)a.set_float(0);else{b=this.touches[b];var d,e,f,h;cr.is_undefined(c)?(c=this.runtime.getLayerByNumber(0),d=c.scale,e=c.zoomRate,f=c.parallaxX,h=c.angle,c.scale=this.runtime.running_layout.scale,c.zoomRate=1,c.parallaxX=1,c.angle=this.runtime.running_layout.angle,a.set_float(c.canvasToLayer(b.x,b.y,!0)),c.scale=d,c.zoomRate=e,c.parallaxX=f,c.angle=h):(c=cr.is_number(c)?this.runtime.getLayerByNumber(c):this.runtime.getLayerByName(c))?a.set_float(c.canvasToLayer(b.x,b.y,!0)):a.set_float(0)}};e.prototype.Y=function(a,b){if(this.touches.length){var c,d,e,f,h;cr.is_undefined(b)?(c=this.runtime.getLayerByNumber(0),d=c.scale,e=c.zoomRate,f=c.parallaxY,h=c.angle,c.scale=this.runtime.running_layout.scale,c.zoomRate=1,c.parallaxY=1,c.angle=this.runtime.running_layout.angle,a.set_float(c.canvasToLayer(this.touches[0].x,this.touches[0].y,!1)),c.scale=d,c.zoomRate=e,c.parallaxY=f,c.angle=h):(c=cr.is_number(b)?this.runtime.getLayerByNumber(b):this.runtime.getLayerByName(b))?a.set_float(c.canvasToLayer(this.touches[0].x,this.touches[0].y,!1)):a.set_float(0)}else a.set_float(0)};e.prototype.YAt=function(a,b,c){b=Math.floor(b);if(0>b||b>=this.touches.length)a.set_float(0);else{var d,e,f,h;cr.is_undefined(c)?(c=this.runtime.getLayerByNumber(0),d=c.scale,e=c.zoomRate,f=c.parallaxY,h=c.angle,c.scale=this.runtime.running_layout.scale,c.zoomRate=1,c.parallaxY=1,c.angle=this.runtime.running_layout.angle,a.set_float(c.canvasToLayer(this.touches[b].x,this.touches[b].y,!1)),c.scale=d,c.zoomRate=e,c.parallaxY=f,c.angle=h):(c=cr.is_number(c)?this.runtime.getLayerByNumber(c):this.runtime.getLayerByName(c))?a.set_float(c.canvasToLayer(this.touches[b].x,this.touches[b].y,!1)):a.set_float(0)}};e.prototype.YForID=function(a,b,c){b=this.findTouch(b);if(0>b)a.set_float(0);else{b=this.touches[b];var d,e,f,h;cr.is_undefined(c)?(c=this.runtime.getLayerByNumber(0),d=c.scale,e=c.zoomRate,f=c.parallaxY,h=c.angle,c.scale=this.runtime.running_layout.scale,c.zoomRate=1,c.parallaxY=1,c.angle=this.runtime.running_layout.angle,a.set_float(c.canvasToLayer(b.x,b.y,!1)),c.scale=d,c.zoomRate=e,c.parallaxY=f,c.angle=h):(c=cr.is_number(c)?this.runtime.getLayerByNumber(c):this.runtime.getLayerByName(c))?a.set_float(c.canvasToLayer(b.x,b.y,!1)):a.set_float(0)}};e.prototype.AbsoluteX=function(a){this.touches.length?a.set_float(this.touches[0].x):a.set_float(0)};e.prototype.AbsoluteXAt=function(a,b){b=Math.floor(b);0>b||b>=this.touches.length?a.set_float(0):a.set_float(this.touches[b].x)};e.prototype.AbsoluteXForID=function(a,b){var c=this.findTouch(b);0>c?a.set_float(0):a.set_float(this.touches[c].x)};e.prototype.AbsoluteY=function(a){this.touches.length?a.set_float(this.touches[0].y):a.set_float(0)};e.prototype.AbsoluteYAt=function(a,b){b=Math.floor(b);0>b||b>=this.touches.length?a.set_float(0):a.set_float(this.touches[b].y)};e.prototype.AbsoluteYForID=function(a,b){var c=this.findTouch(b);0>c?a.set_float(0):a.set_float(this.touches[c].y)};e.prototype.SpeedAt=function(a,b){b=Math.floor(b);if(0>b||b>=this.touches.length)a.set_float(0);else{var c=this.touches[b],d=cr.distanceTo(c.x,c.y,c.lastx,c.lasty),c=(c.time-c.lasttime)/1E3;0===c?a.set_float(0):a.set_float(d/c)}};e.prototype.SpeedForID=function(a,b){var c=this.findTouch(b);if(0>c)a.set_float(0);else{var d=this.touches[c],c=cr.distanceTo(d.x,d.y,d.lastx,d.lasty),d=(d.time-d.lasttime)/1E3;0===d?a.set_float(0):a.set_float(c/d)}};e.prototype.AngleAt=function(a,b){b=Math.floor(b);if(0>b||b>=this.touches.length)a.set_float(0);else{var c=this.touches[b];a.set_float(cr.to_degrees(cr.angleTo(c.lastx,c.lasty,c.x,c.y)))}};e.prototype.AngleForID=function(a,b){var c=this.findTouch(b);0>c?a.set_float(0):(c=this.touches[c],a.set_float(cr.to_degrees(cr.angleTo(c.lastx,c.lasty,c.x,c.y))))};e.prototype.Alpha=function(a){a.set_float(this.getAlpha())};e.prototype.Beta=function(a){a.set_float(this.getBeta())};e.prototype.Gamma=function(a){a.set_float(this.getGamma())};e.prototype.AccelerationXWithG=function(a){a.set_float(this.acc_g_x)};e.prototype.AccelerationYWithG=function(a){a.set_float(this.acc_g_y)};e.prototype.AccelerationZWithG=function(a){a.set_float(this.acc_g_z)};e.prototype.AccelerationX=function(a){a.set_float(this.acc_x)};e.prototype.AccelerationY=function(a){a.set_float(this.acc_y)};e.prototype.AccelerationZ=function(a){a.set_float(this.acc_z)};e.prototype.TouchIndex=function(a){a.set_int(this.trigger_index)};e.prototype.TouchID=function(a){a.set_float(this.trigger_id)};f.exps=new e})();cr.getProjectModel=function(){return[null,"Loader",[[cr.plugins_.Browser,!0,!1,!1,!1,!1,!1,!1,!1,!1],[cr.plugins_.Audio,!0,!1,!1,!1,!1,!1,!1,!1,!1],[cr.plugins_.Keyboard,!0,!1,!1,!1,!1,!1,!1,!1,!1],[cr.plugins_.NodeWebkit,!0,!1,!1,!1,!1,!1,!1,!1,!1],[cr.plugins_.Particles,!1,!0,!0,!1,!0,!0,!0,!0,!0],[cr.plugins_.Text,!1,!0,!0,!0,!0,!0,!0,!0,!1],[cr.plugins_.Touch,!0,!1,!1,!1,!1,!1,!1,!1,!1]],[["t0",cr.plugins_.Browser,!1,[],0,0,null,null,[],!1,!1,0xe7508259f1049,[],[]],["t1",cr.plugins_.Keyboard,!1,[],0,0,null,null,[],!1,!1,8268562079040081,[],[]],["t2",cr.plugins_.Particles,!1,[],0,0,["c2.png",29833,0],null,[],!1,!1,723074460476516,[]],["t3",cr.plugins_.Audio,!1,[],0,0,null,null,[],!1,!1,0x87d7a273e6e76,[],[0,1,1,600,600,1E4,1,5E3,1]],["t4",cr.plugins_.NodeWebkit,!1,[],0,0,null,null,[],!1,!1,0x418c17d077bfa,[],[]],["t5",cr.plugins_.Particles,!1,[],0,0,["nw.png",2403,0],null,[],!1,!1,7413958693205721,[]],["t6",cr.plugins_.Text,!1,[],0,0,null,null,[],!1,!1,0xadbe7ef820faf,[]],["t7",cr.plugins_.Touch,!1,[],0,0,null,null,[],!1,!1,4854103537132793,[],[1]]],[],[["Loader",800,600,!1,"Loader_events",9256725879576544,[["Loader",0,8771806647801429,!0,[0,0,0],!1,1,1,1,!1,1,0,0,[],[]]],[],[]],["Veille",800,600,!1,"Veille_events",306290045656015,[["Veille",0,0x9c56068fd6acc,!0,[0,0,0],!1,1,1,1,!1,1,1,0,[[[400,-75,0,32,320,0,1.5708,1,0,0.5,0,0,[]],2,1,[],[],[5,120,0,250,40,0,-2,40,0,0,8,0,-150,100,2,800,2,0,5]],[[400,700,0,32,320,0,-1.5708,1,0,0.5,0,0,[]],5,7,[],[],[5,120,0,250,40,0,-2,40,0,0,8,0,-150,-100,2,800,2,0,5]],[[579,30,0,200,540,0,0,0.5,0,0,1,0,[]],6,4,[],[],["",1,"bold 12pt Arial","rgb(255,255,255)",1,1,0,0,0]]],[["warpripple","WarpRipple",[1,0.01,-5]]]]],[],[]],["Exit",800,600,!1,"Exit_events",6113943557441396,[["Exit",0,0xb124a9f08ea67,!0,[0,0,0],!1,1,1,1,!1,1,0,0,[],[]]],[],[]]],[["Loader_events",[[1,"NumVeille",0,-2,!1,!1,8964089995488567],[1,"NWfile",1,"",!1,!1,0xe4fbfc76f13ab],[0,null,!1,0x4dfd65888a214,[[-1,cr.system_object.prototype.cnds.OnLayoutStart,null,1,!1,!1,!1,9184290607372780]],[[0,cr.plugins_.Browser.prototype.acts.RequestFullScreen,null,7636186985065621,[[3,5]]]]],[0,null,!1,0x5bb13aa690f60,[[-1,cr.system_object.prototype.cnds.OnLoadFinished,null,1,!1,!1,!1,0x82e0442152500]],[[-1,cr.system_object.prototype.acts.Wait,null,9840344659686878,[[0,[1,0.01]]]],[-1,cr.system_object.prototype.acts.GoToLayout,null,0x5200955dfe05a,[[6,"Veille"]]]]]]],["Veille_events",[[1,"RMS",0,0,!1,!1,8272711222301935],[1,"Restart",0,0,!1,!1,5338948241480027],[0,null,!1,9883295321069700,[[-1,cr.system_object.prototype.cnds.OnLayoutStart,null,1,!1,!1,!1,0xd39cedc82dd33]],[[3,cr.plugins_.Audio.prototype.acts.PlayByName,null,5202980257560176,[[3,1],[1,[2,"Evening_Fall_Harp"]],[3,0],[0,[0,0]],[1,[2,""]]]],[2,cr.plugins_.Particles.prototype.acts.SetRate,null,0xc15240e82c978,[[0,[0,5]]]],[5,cr.plugins_.Particles.prototype.acts.SetRate,null,0x6ff8687e80572,[[0,[0,5]]]],[2,cr.plugins_.Particles.prototype.acts.SetSpraying,null,5500294466648475,[[3,1]]],[5,cr.plugins_.Particles.prototype.acts.SetSpraying,null,4830535051445549,[[3,1]]]]],[0,[!1,"Start"],!1,5670715518694284,[[-1,cr.system_object.prototype.cnds.IsGroupActive,null,0,!1,!1,!1,5670715518694284,[[1,[2,"Start"]]]]],[],[[0,null,!1,8365481790965971,[[-1,cr.system_object.prototype.cnds.Compare,null,0,!1,!1,!1,681938087295472,[[7,[19,cr.system_object.prototype.exps.layeropacity,[[2,"Veille"]]]],[8,0],[7,[0,100]]]]],[[-1,cr.system_object.prototype.acts.SetGroupActive,null,0x939cfc17f135c,[[1,[2,"Start"]],[3,0]]]]],[0,null,!1,0xaccce4249a717,[[-1,cr.system_object.prototype.cnds.Else,null,0,!1,!1,!1,9645145373693224],[-1,cr.system_object.prototype.cnds.EveryTick,null,0,!1,!1,!1,0xc1f6300af58e8]],[[-1,cr.system_object.prototype.acts.SetLayerOpacity,null,0x50fa048c371f6,[[5,[2,"Veille"]],[0,[4,[19,cr.system_object.prototype.exps.layeropacity,[[2,"Veille"]]],[0,1]]]]],[2,cr.plugins_.Particles.prototype.acts.SetInitOpacity,null,0xa82f69b81a210,[[0,[5,[19,cr.system_object.prototype.exps.layeropacity,[[2,"Veille"]]],[0,10]]]]],[5,cr.plugins_.Particles.prototype.acts.SetInitOpacity,null,9087421245893464,[[0,[5,[19,cr.system_object.prototype.exps.layeropacity,[[2,"Veille"]]],[0,10]]]]]]]]],[0,[!1,"Body"],!1,7028360535295121,[[-1,cr.system_object.prototype.cnds.IsGroupActive,null,0,!1,!1,!1,7028360535295121,[[1,[2,"Body"]]]]],[],[[0,null,!1,0x51e8a6e11ca45,[[1,cr.plugins_.Keyboard.prototype.cnds.OnKey,null,1,!1,!1,!1,0xaf5a5a8d1a494,[[9,13]]]],[],[[0,null,!1,6545261610976676,[[6,cr.plugins_.Text.prototype.cnds.IsVisible,null,0,!1,!0,!1,57733252997832]],[[6,cr.plugins_.Text.prototype.acts.SetVisible,null,0x8bbeaf0a9536a,[[3,1]]]]],[0,null,!1,7903978118133453,[[-1,cr.system_object.prototype.cnds.Else,null,0,!1,!1,!1,7093124615072028]],[[6,cr.plugins_.Text.prototype.acts.SetVisible,null,9016231935903996,[[3,0]]]]]]],[0,null,!1,0xc3a65bf4f2a22,[[-1,cr.system_object.prototype.cnds.Every,null,0,!1,!1,!1,7317966506217073,[[0,[1,1]]]]],[[2,cr.plugins_.Particles.prototype.acts.SetRate,null,8632633260751814,[[0,[19,cr.system_object.prototype.exps.clamp,[[18,[16,[7,[4,[20,2,cr.plugins_.Particles.prototype.exps.Rate,!1,null],[20,2,cr.plugins_.Particles.prototype.exps.ParticleCount,!1,null]],[7,[19,cr.system_object.prototype.exps.fps],[0,25]]],[19,cr.system_object.prototype.exps.fps]],[5,[20,2,cr.plugins_.Particles.prototype.exps.Rate,!1,null],[19,cr.system_object.prototype.exps.random,[[0,1],[0,2]]]],[4,[20,2,cr.plugins_.Particles.prototype.exps.Rate,!1,null],[19,cr.system_object.prototype.exps.random,[[0,1],[0,2]]]]],[0,5],[0,25]]]]]],[5,cr.plugins_.Particles.prototype.acts.SetRate,null,6062215411365679,[[0,[19,cr.system_object.prototype.exps.clamp,[[18,[16,[7,[4,[20,5,cr.plugins_.Particles.prototype.exps.Rate,!1,null],[20,5,cr.plugins_.Particles.prototype.exps.ParticleCount,!1,null]],[7,[19,cr.system_object.prototype.exps.fps],[0,25]]],[19,cr.system_object.prototype.exps.fps]],[5,[20,5,cr.plugins_.Particles.prototype.exps.Rate,!1,null],[19,cr.system_object.prototype.exps.random,[[0,1],[0,2]]]],[4,[20,5,cr.plugins_.Particles.prototype.exps.Rate,!1,null],[19,cr.system_object.prototype.exps.random,[[0,1],[0,2]]]]],[0,5],[0,25]]]]]],[6,cr.plugins_.Text.prototype.acts.SetText,null,9405966255769602,[[7,[10,[10,[10,[10,[10,[10,[10,[10,[10,[10,[10,[10,[10,[10,[10,[10,[10,[2,"#"],[20,2,cr.plugins_.Particles.prototype.exps.ParticleCount,!1,null]],[2," +"]],[19,cr.system_object.prototype.exps["int"],[[20,2,cr.plugins_.Particles.prototype.exps.Rate,!1,null]]]],[2,"/sec"]],[19,cr.system_object.prototype.exps.newline]],[19,cr.system_object.prototype.exps.newline]],[2,"FPS:"]],[19,cr.system_object.prototype.exps.fps]],[2," RMS:"]],[7,[19,cr.system_object.prototype.exps.floor,[[6,[23,"RMS"],[0,100]]]],[0,100]]],[19,cr.system_object.prototype.exps.newline]],[19,cr.system_object.prototype.exps.newline]],[2,"#"]],[20,5,cr.plugins_.Particles.prototype.exps.ParticleCount,!1,null]],[2," +"]],[19,cr.system_object.prototype.exps["int"],[[20,5,cr.plugins_.Particles.prototype.exps.Rate,!1,null]]]],[2,"/sec"]]]]]]],[0,null,!1,0x928915e9cd3bb,[[-1,cr.system_object.prototype.cnds.EveryTick,null,0,!1,!1,!1,6917607399799475],[3,cr.plugins_.Audio.prototype.cnds.AdvancedAudioSupported,null,0,!1,!1,!1,9849462572130356]],[[-1,cr.system_object.prototype.acts.SetVar,null,9168531653053006,[[11,"RMS"],[7,[20,3,cr.plugins_.Audio.prototype.exps.AnalyserRMSLevel,!1,null,[[2,"a"],[0,0]]]]]],[2,cr.plugins_.Particles.prototype.acts.SetGravity,null,0xcf6eb9b13cfb,[[0,[6,[23,"RMS"],[1,-2.1]]]]],[5,cr.plugins_.Particles.prototype.acts.SetGravity,null,8351761715539176,[[0,[6,[23,"RMS"],[1,2.1]]]]]]],[0,null,!1,8242976808981609,[[-1,cr.system_object.prototype.cnds.Compare,null,0,!1,!1,!1,5987721542830278,[[7,[19,cr.system_object.prototype.exps.time]],[8,5],[7,[0,140]]]],[-1,cr.system_object.prototype.cnds.CompareVar,null,0,!1,!1,!1,8881592830623784,[[11,"Restart"],[8,0],[7,[0,0]]]]],[[-1,cr.system_object.prototype.acts.SetVar,null,8871831640987063,[[11,"Restart"],[7,[0,1]]]]]],[0,null,!0,0xf980861b0c5a4,[[1,cr.plugins_.Keyboard.prototype.cnds.OnKey,null,1,!1,!1,!1,7240018894178457,[[9,27]]],[7,cr.plugins_.Touch.prototype.cnds.IsInTouch,null,0,!1,!1,!1,0xe41159773516],[-1,cr.system_object.prototype.cnds.CompareVar,null,0,!1,!1,!1,0xa522af7e93f2e,[[11,"Restart"],[8,0],[7,[0,1]]]]],[[-1,cr.system_object.prototype.acts.SetVar,null,0x9f9a93e787142,[[11,"Restart"],[7,[3,[23,"Restart"]]]]],[-1,cr.system_object.prototype.acts.SetGroupActive,null,4593935824136996,[[1,[2,"Start"]],[3,0]]],[-1,cr.system_object.prototype.acts.SetGroupActive,null,7749099240835881,[[1,[2,"Body"]],[3,0]]],[2,cr.plugins_.Particles.prototype.acts.SetSpraying,null,6865126891265061,[[3,0]]],[5,cr.plugins_.Particles.prototype.acts.SetSpraying,null,0x44b2c5ab55ee5,[[3,0]]],[-1,cr.system_object.prototype.acts.SetGroupActive,null,9864700388762392,[[1,[2,"End"]],[3,1]]]]]]],[0,[!1,"End"],!1,0x7309bde3e92e,[[-1,cr.system_object.prototype.cnds.IsGroupActive,null,0,!1,!1,!1,0x7309bde3e92e,[[1,[2,"End"]]]]],[],[[0,null,!1,0x7321b49aa09e1,[[-1,cr.system_object.prototype.cnds.Compare,null,0,!1,!1,!1,4511261893892535,[[7,[19,cr.system_object.prototype.exps.layeropacity,[[2,"Veille"]]]],[8,0],[7,[0,0]]]]],[[-1,cr.system_object.prototype.acts.SetGroupActive,null,4772013783097554,[[1,[2,"End"]],[3,0]]],[3,cr.plugins_.Audio.prototype.acts.StopAll,null,832829439830883]],[[0,null,!1,7259102832799663,[[-1,cr.system_object.prototype.cnds.CompareVar,null,0,!1,!1,!1,0x5012013f72140,[[11,"Restart"],[8,0],[7,[0,0]]]]],[[-1,cr.system_object.prototype.acts.GoToLayoutByName,null,0xe7d6f7283c74b,[[1,[2,"exit"]]]]]],[0,null,!1,8363975973112161,[[-1,cr.system_object.prototype.cnds.Else,null,0,!1,!1,!1,0x60cf0d437e8dc]],[[3,cr.plugins_.Audio.prototype.acts.StopAll,null,5893738282654444],[0,cr.plugins_.Browser.prototype.acts.Reload,null,0x62cf4bced8610]]]]],[0,null,!1,7218792687155007,[[-1,cr.system_object.prototype.cnds.Else,null,0,!1,!1,!1,7503399522701357],[-1,cr.system_object.prototype.cnds.EveryTick,null,0,!1,!1,!1,5378603673816071]],[[-1,cr.system_object.prototype.acts.SetLayerOpacity,null,0xccdd38cda519a,[[5,[2,"Veille"]],[0,[5,[19,cr.system_object.prototype.exps.layeropacity,[[2,"Veille"]]],[0,1]]]]],[3,cr.plugins_.Audio.prototype.acts.SetVolume,null,5245460560361351,[[1,[2,"a"]],[0,[5,[20,3,cr.plugins_.Audio.prototype.exps.Volume,!1,null,[[2,"a"]]],[1,0.25]]]]]]]]],[0,null,!1,0xf82f8373efea5,[[7,cr.plugins_.Touch.prototype.cnds.IsInTouch,null,0,!1,!1,!1,0xb4decc1bcddd3]],[[-1,cr.system_object.prototype.acts.GoToLayoutByName,null,0x59bbef9fef967,[[1,[2,"exit"]]]]]]]],["Exit_events",[[0,null,!1,0xe7e976c27bdd5,[[-1,cr.system_object.prototype.cnds.OnLayoutStart,null,1,!1,!1,!1,7777797351584432]],[[-1,cr.system_object.prototype.acts.Wait,null,5810891606874927,[[0,[1,0.01]]]],[0,cr.plugins_.Browser.prototype.acts.Close,null,82255096705349]]]]]],"",!0,800,600,2,!0,!0,!1,"1.1",0,!0,3,!0,8,!1,[]]}; \ No newline at end of file diff --git a/tests/automation/nw-in-mem/package/evening_fall_harp.ogg b/tests/automation/nw-in-mem/package/evening_fall_harp.ogg new file mode 100644 index 0000000000..003803ce2e Binary files /dev/null and b/tests/automation/nw-in-mem/package/evening_fall_harp.ogg differ diff --git a/tests/automation/nw-in-mem/package/icon-114.png b/tests/automation/nw-in-mem/package/icon-114.png new file mode 100644 index 0000000000..bf56364da4 Binary files /dev/null and b/tests/automation/nw-in-mem/package/icon-114.png differ diff --git a/tests/automation/nw-in-mem/package/icon-128.png b/tests/automation/nw-in-mem/package/icon-128.png new file mode 100644 index 0000000000..523ffd0ff7 Binary files /dev/null and b/tests/automation/nw-in-mem/package/icon-128.png differ diff --git a/tests/automation/nw-in-mem/package/icon-16.png b/tests/automation/nw-in-mem/package/icon-16.png new file mode 100644 index 0000000000..e1b785cc71 Binary files /dev/null and b/tests/automation/nw-in-mem/package/icon-16.png differ diff --git a/tests/automation/nw-in-mem/package/icon-32.png b/tests/automation/nw-in-mem/package/icon-32.png new file mode 100644 index 0000000000..02edf8c2b4 Binary files /dev/null and b/tests/automation/nw-in-mem/package/icon-32.png differ diff --git a/tests/automation/nw-in-mem/package/index.html b/tests/automation/nw-in-mem/package/index.html new file mode 100644 index 0000000000..ea89fa8452 --- /dev/null +++ b/tests/automation/nw-in-mem/package/index.html @@ -0,0 +1,107 @@ + + + + + + Veille + + + + + + + + + + + + + + +
+ + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/automation/nw-in-mem/package/jquery-2.0.0.min.js b/tests/automation/nw-in-mem/package/jquery-2.0.0.min.js new file mode 100644 index 0000000000..fbb594e8f3 --- /dev/null +++ b/tests/automation/nw-in-mem/package/jquery-2.0.0.min.js @@ -0,0 +1,6 @@ +/*! jQuery v2.0.0 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license +//@ sourceMappingURL=jquery.min.map +*/ +(function(e,undefined){var t,n,r=typeof undefined,i=e.location,o=e.document,s=o.documentElement,a=e.jQuery,u=e.$,l={},c=[],f="2.0.0",p=c.concat,h=c.push,d=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=f.trim,x=function(e,n){return new x.fn.init(e,n,t)},b=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^-ms-/,N=/-([\da-z])/gi,E=function(e,t){return t.toUpperCase()},S=function(){o.removeEventListener("DOMContentLoaded",S,!1),e.removeEventListener("load",S,!1),x.ready()};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,t,n){var r,i;if(!e)return this;if("string"==typeof e){if(r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:T.exec(e),!r||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof x?t[0]:t,x.merge(this,x.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:o,!0)),C.test(r[1])&&x.isPlainObject(t))for(r in t)x.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return i=o.getElementById(r[2]),i&&i.parentNode&&(this.length=1,this[0]=i),this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?n.ready(e):(e.selector!==undefined&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return d.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,t,n,r,i,o,s=arguments[0]||{},a=1,u=arguments.length,l=!1;for("boolean"==typeof s&&(l=s,s=arguments[1]||{},a=2),"object"==typeof s||x.isFunction(s)||(s={}),u===a&&(s=this,--a);u>a;a++)if(null!=(e=arguments[a]))for(t in e)n=s[t],r=e[t],s!==r&&(l&&r&&(x.isPlainObject(r)||(i=x.isArray(r)))?(i?(i=!1,o=n&&x.isArray(n)?n:[]):o=n&&x.isPlainObject(n)?n:{},s[t]=x.extend(l,o,r)):r!==undefined&&(s[t]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=a),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){(e===!0?--x.readyWait:x.isReady)||(x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(o,[x]),x.fn.trigger&&x(o).trigger("ready").off("ready")))},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray,isWindow:function(e){return null!=e&&e===e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if("object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(t){return!1}return!0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:JSON.parse,parseXML:function(e){var t,n;if(!e||"string"!=typeof e)return null;try{n=new DOMParser,t=n.parseFromString(e,"text/xml")}catch(r){t=undefined}return(!t||t.getElementsByTagName("parsererror").length)&&x.error("Invalid XML: "+e),t},noop:function(){},globalEval:function(e){var t,n=eval;e=x.trim(e),e&&(1===e.indexOf("use strict")?(t=o.createElement("script"),t.text=e,o.head.appendChild(t).parentNode.removeChild(t)):n(e))},camelCase:function(e){return e.replace(k,"ms-").replace(N,E)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,s=j(e);if(n){if(s){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(s){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:function(e){return null==e?"":v.call(e)},makeArray:function(e,t){var n=t||[];return null!=e&&(j(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:g.call(t,e,n)},merge:function(e,t){var n=t.length,r=e.length,i=0;if("number"==typeof n)for(;n>i;i++)e[r++]=t[i];else while(t[i]!==undefined)e[r++]=t[i++];return e.length=r,e},grep:function(e,t,n){var r,i=[],o=0,s=e.length;for(n=!!n;s>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,s=j(e),a=[];if(s)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(a[a.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(a[a.length]=r);return p.apply([],a)},guid:1,proxy:function(e,t){var n,r,i;return"string"==typeof t&&(n=e[t],t=e,e=n),x.isFunction(e)?(r=d.call(arguments,2),i=function(){return e.apply(t||this,r.concat(d.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):undefined},access:function(e,t,n,r,i,o,s){var a=0,u=e.length,l=null==n;if("object"===x.type(n)){i=!0;for(a in n)x.access(e,t,a,n[a],!0,o,s)}else if(r!==undefined&&(i=!0,x.isFunction(r)||(s=!0),l&&(s?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(x(e),n)})),t))for(;u>a;a++)t(e[a],n,s?r:r.call(e[a],a,t(e[a],n)));return i?e:l?t.call(e):u?t(e[0],n):o},now:Date.now,swap:function(e,t,n,r){var i,o,s={};for(o in t)s[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=s[o];return i}}),x.ready.promise=function(t){return n||(n=x.Deferred(),"complete"===o.readyState?setTimeout(x.ready):(o.addEventListener("DOMContentLoaded",S,!1),e.addEventListener("load",S,!1))),n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function j(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}t=x(o),function(e,undefined){var t,n,r,i,o,s,a,u,l,c,f,p,h,d,g,m,y="sizzle"+-new Date,v=e.document,b={},w=0,T=0,C=ot(),k=ot(),N=ot(),E=!1,S=function(){return 0},j=typeof undefined,D=1<<31,A=[],L=A.pop,q=A.push,H=A.push,O=A.slice,F=A.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},P="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",R="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=M.replace("w","w#"),$="\\["+R+"*("+M+")"+R+"*(?:([*^$|!~]?=)"+R+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+R+"*\\]",B=":("+M+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",I=RegExp("^"+R+"+|((?:^|[^\\\\])(?:\\\\.)*)"+R+"+$","g"),z=RegExp("^"+R+"*,"+R+"*"),_=RegExp("^"+R+"*([>+~]|"+R+")"+R+"*"),X=RegExp(R+"*[+~]"),U=RegExp("="+R+"*([^\\]'\"]*)"+R+"*\\]","g"),Y=RegExp(B),V=RegExp("^"+W+"$"),G={ID:RegExp("^#("+M+")"),CLASS:RegExp("^\\.("+M+")"),TAG:RegExp("^("+M.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+B),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+R+"*(even|odd|(([+-]|)(\\d*)n|)"+R+"*(?:([+-]|)"+R+"*(\\d+)|))"+R+"*\\)|)","i"),"boolean":RegExp("^(?:"+P+")$","i"),needsContext:RegExp("^"+R+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+R+"*((?:-\\d)?\\d*)"+R+"*\\)|)(?=[^-]|$)","i")},J=/^[^{]+\{\s*\[native \w/,Q=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,et=/'|\\/g,tt=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,nt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{H.apply(A=O.call(v.childNodes),v.childNodes),A[v.childNodes.length].nodeType}catch(rt){H={apply:A.length?function(e,t){q.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function it(e){return J.test(e+"")}function ot(){var e,t=[];return e=function(n,i){return t.push(n+=" ")>r.cacheLength&&delete e[t.shift()],e[n]=i}}function st(e){return e[y]=!0,e}function at(e){var t=c.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ut(e,t,n,r){var i,o,s,a,u,f,d,g,x,w;if((t?t.ownerDocument||t:v)!==c&&l(t),t=t||c,n=n||[],!e||"string"!=typeof e)return n;if(1!==(a=t.nodeType)&&9!==a)return[];if(p&&!r){if(i=Q.exec(e))if(s=i[1]){if(9===a){if(o=t.getElementById(s),!o||!o.parentNode)return n;if(o.id===s)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(s))&&m(t,o)&&o.id===s)return n.push(o),n}else{if(i[2])return H.apply(n,t.getElementsByTagName(e)),n;if((s=i[3])&&b.getElementsByClassName&&t.getElementsByClassName)return H.apply(n,t.getElementsByClassName(s)),n}if(b.qsa&&(!h||!h.test(e))){if(g=d=y,x=t,w=9===a&&e,1===a&&"object"!==t.nodeName.toLowerCase()){f=gt(e),(d=t.getAttribute("id"))?g=d.replace(et,"\\$&"):t.setAttribute("id",g),g="[id='"+g+"'] ",u=f.length;while(u--)f[u]=g+mt(f[u]);x=X.test(e)&&t.parentNode||t,w=f.join(",")}if(w)try{return H.apply(n,x.querySelectorAll(w)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(I,"$1"),t,n,r)}o=ut.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},l=ut.setDocument=function(e){var t=e?e.ownerDocument||e:v;return t!==c&&9===t.nodeType&&t.documentElement?(c=t,f=t.documentElement,p=!o(t),b.getElementsByTagName=at(function(e){return e.appendChild(t.createComment("")),!e.getElementsByTagName("*").length}),b.attributes=at(function(e){return e.className="i",!e.getAttribute("className")}),b.getElementsByClassName=at(function(e){return e.innerHTML="
",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),b.sortDetached=at(function(e){return 1&e.compareDocumentPosition(c.createElement("div"))}),b.getById=at(function(e){return f.appendChild(e).id=y,!t.getElementsByName||!t.getElementsByName(y).length}),b.getById?(r.find.ID=function(e,t){if(typeof t.getElementById!==j&&p){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},r.filter.ID=function(e){var t=e.replace(tt,nt);return function(e){return e.getAttribute("id")===t}}):(r.find.ID=function(e,t){if(typeof t.getElementById!==j&&p){var n=t.getElementById(e);return n?n.id===e||typeof n.getAttributeNode!==j&&n.getAttributeNode("id").value===e?[n]:undefined:[]}},r.filter.ID=function(e){var t=e.replace(tt,nt);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),r.find.TAG=b.getElementsByTagName?function(e,t){return typeof t.getElementsByTagName!==j?t.getElementsByTagName(e):undefined}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=b.getElementsByClassName&&function(e,t){return typeof t.getElementsByClassName!==j&&p?t.getElementsByClassName(e):undefined},d=[],h=[],(b.qsa=it(t.querySelectorAll))&&(at(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||h.push("\\["+R+"*(?:value|"+P+")"),e.querySelectorAll(":checked").length||h.push(":checked")}),at(function(e){var t=c.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&h.push("[*^$]="+R+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||h.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),h.push(",.*:")})),(b.matchesSelector=it(g=f.webkitMatchesSelector||f.mozMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){b.disconnectedMatch=g.call(e,"div"),g.call(e,"[s!='']:x"),d.push("!=",B)}),h=h.length&&RegExp(h.join("|")),d=d.length&&RegExp(d.join("|")),m=it(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},S=f.compareDocumentPosition?function(e,n){if(e===n)return E=!0,0;var r=n.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(n);return r?1&r||!b.sortDetached&&n.compareDocumentPosition(e)===r?e===t||m(v,e)?-1:n===t||m(v,n)?1:u?F.call(u,e)-F.call(u,n):0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,n){var r,i=0,o=e.parentNode,s=n.parentNode,a=[e],l=[n];if(e===n)return E=!0,0;if(!o||!s)return e===t?-1:n===t?1:o?-1:s?1:u?F.call(u,e)-F.call(u,n):0;if(o===s)return lt(e,n);r=e;while(r=r.parentNode)a.unshift(r);r=n;while(r=r.parentNode)l.unshift(r);while(a[i]===l[i])i++;return i?lt(a[i],l[i]):a[i]===v?-1:l[i]===v?1:0},c):c},ut.matches=function(e,t){return ut(e,null,null,t)},ut.matchesSelector=function(e,t){if((e.ownerDocument||e)!==c&&l(e),t=t.replace(U,"='$1']"),!(!b.matchesSelector||!p||d&&d.test(t)||h&&h.test(t)))try{var n=g.call(e,t);if(n||b.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return ut(t,c,null,[e]).length>0},ut.contains=function(e,t){return(e.ownerDocument||e)!==c&&l(e),m(e,t)},ut.attr=function(e,t){(e.ownerDocument||e)!==c&&l(e);var n=r.attrHandle[t.toLowerCase()],i=n&&n(e,t,!p);return i===undefined?b.attributes||!p?e.getAttribute(t):(i=e.getAttributeNode(t))&&i.specified?i.value:null:i},ut.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},ut.uniqueSort=function(e){var t,n=[],r=0,i=0;if(E=!b.detectDuplicates,u=!b.sortStable&&e.slice(0),e.sort(S),E){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return e};function lt(e,t){var n=t&&e,r=n&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ct(e,t,n){var r;return n?undefined:(r=e.getAttributeNode(t))&&r.specified?r.value:e[t]===!0?t.toLowerCase():null}function ft(e,t,n){var r;return n?undefined:r=e.getAttribute(t,"type"===t.toLowerCase()?1:2)}function pt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ht(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function dt(e){return st(function(t){return t=+t,st(function(n,r){var i,o=e([],n.length,t),s=o.length;while(s--)n[i=o[s]]&&(n[i]=!(r[i]=n[i]))})})}i=ut.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else for(;t=e[r];r++)n+=i(t);return n},r=ut.selectors={cacheLength:50,createPseudo:st,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(tt,nt),e[3]=(e[4]||e[5]||"").replace(tt,nt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||ut.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&ut.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return G.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&Y.test(n)&&(t=gt(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(tt,nt).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=C[e+" "];return t||(t=RegExp("(^|"+R+")"+e+"("+R+"|$)"))&&C(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=ut.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),s="last"!==e.slice(-4),a="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,h,d,g=o!==s?"nextSibling":"previousSibling",m=t.parentNode,v=a&&t.nodeName.toLowerCase(),x=!u&&!a;if(m){if(o){while(g){f=t;while(f=f[g])if(a?f.nodeName.toLowerCase()===v:1===f.nodeType)return!1;d=g="only"===e&&!d&&"nextSibling"}return!0}if(d=[s?m.firstChild:m.lastChild],s&&x){c=m[y]||(m[y]={}),l=c[e]||[],h=l[0]===w&&l[1],p=l[0]===w&&l[2],f=h&&m.childNodes[h];while(f=++h&&f&&f[g]||(p=h=0)||d.pop())if(1===f.nodeType&&++p&&f===t){c[e]=[w,h,p];break}}else if(x&&(l=(t[y]||(t[y]={}))[e])&&l[0]===w)p=l[1];else while(f=++h&&f&&f[g]||(p=h=0)||d.pop())if((a?f.nodeName.toLowerCase()===v:1===f.nodeType)&&++p&&(x&&((f[y]||(f[y]={}))[e]=[w,p]),f===t))break;return p-=i,p===r||0===p%r&&p/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||ut.error("unsupported pseudo: "+e);return i[y]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?st(function(e,n){var r,o=i(e,t),s=o.length;while(s--)r=F.call(e,o[s]),e[r]=!(n[r]=o[s])}):function(e){return i(e,0,n)}):i}},pseudos:{not:st(function(e){var t=[],n=[],r=s(e.replace(I,"$1"));return r[y]?st(function(e,t,n,i){var o,s=r(e,null,i,[]),a=e.length;while(a--)(o=s[a])&&(e[a]=!(t[a]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:st(function(e){return function(t){return ut(e,t).length>0}}),contains:st(function(e){return function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:st(function(e){return V.test(e||"")||ut.error("unsupported lang: "+e),e=e.replace(tt,nt).toLowerCase(),function(t){var n;do if(n=p?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===c.activeElement&&(!c.hasFocus||c.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Z.test(e.nodeName)},input:function(e){return K.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:dt(function(){return[0]}),last:dt(function(e,t){return[t-1]}),eq:dt(function(e,t,n){return[0>n?n+t:n]}),even:dt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:dt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:dt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:dt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(t in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})r.pseudos[t]=pt(t);for(t in{submit:!0,reset:!0})r.pseudos[t]=ht(t);function gt(e,t){var n,i,o,s,a,u,l,c=k[e+" "];if(c)return t?0:c.slice(0);a=e,u=[],l=r.preFilter;while(a){(!n||(i=z.exec(a)))&&(i&&(a=a.slice(i[0].length)||a),u.push(o=[])),n=!1,(i=_.exec(a))&&(n=i.shift(),o.push({value:n,type:i[0].replace(I," ")}),a=a.slice(n.length));for(s in r.filter)!(i=G[s].exec(a))||l[s]&&!(i=l[s](i))||(n=i.shift(),o.push({value:n,type:s,matches:i}),a=a.slice(n.length));if(!n)break}return t?a.length:a?ut.error(e):k(e,u).slice(0)}function mt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function yt(e,t,r){var i=t.dir,o=r&&"parentNode"===i,s=T++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,r,a){var u,l,c,f=w+" "+s;if(a){while(t=t[i])if((1===t.nodeType||o)&&e(t,r,a))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[y]||(t[y]={}),(l=c[i])&&l[0]===f){if((u=l[1])===!0||u===n)return u===!0}else if(l=c[i]=[f],l[1]=e(t,r,a)||n,l[1]===!0)return!0}}function vt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,s=[],a=0,u=e.length,l=null!=t;for(;u>a;a++)(o=e[a])&&(!n||n(o,r,i))&&(s.push(o),l&&t.push(a));return s}function bt(e,t,n,r,i,o){return r&&!r[y]&&(r=bt(r)),i&&!i[y]&&(i=bt(i,o)),st(function(o,s,a,u){var l,c,f,p=[],h=[],d=s.length,g=o||Ct(t||"*",a.nodeType?[a]:a,[]),m=!e||!o&&t?g:xt(g,p,e,a,u),y=n?i||(o?e:d||r)?[]:s:m;if(n&&n(m,y,a,u),r){l=xt(y,h),r(l,[],a,u),c=l.length;while(c--)(f=l[c])&&(y[h[c]]=!(m[h[c]]=f))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(f=y[c])&&l.push(m[c]=f);i(null,y=[],l,u)}c=y.length;while(c--)(f=y[c])&&(l=i?F.call(o,f):p[c])>-1&&(o[l]=!(s[l]=f))}}else y=xt(y===s?y.splice(d,y.length):y),i?i(null,s,y,u):H.apply(s,y)})}function wt(e){var t,n,i,o=e.length,s=r.relative[e[0].type],u=s||r.relative[" "],l=s?1:0,c=yt(function(e){return e===t},u,!0),f=yt(function(e){return F.call(t,e)>-1},u,!0),p=[function(e,n,r){return!s&&(r||n!==a)||((t=n).nodeType?c(e,n,r):f(e,n,r))}];for(;o>l;l++)if(n=r.relative[e[l].type])p=[yt(vt(p),n)];else{if(n=r.filter[e[l].type].apply(null,e[l].matches),n[y]){for(i=++l;o>i;i++)if(r.relative[e[i].type])break;return bt(l>1&&vt(p),l>1&&mt(e.slice(0,l-1)).replace(I,"$1"),n,i>l&&wt(e.slice(l,i)),o>i&&wt(e=e.slice(i)),o>i&&mt(e))}p.push(n)}return vt(p)}function Tt(e,t){var i=0,o=t.length>0,s=e.length>0,u=function(u,l,f,p,h){var d,g,m,y=[],v=0,x="0",b=u&&[],T=null!=h,C=a,k=u||s&&r.find.TAG("*",h&&l.parentNode||l),N=w+=null==C?1:Math.random()||.1;for(T&&(a=l!==c&&l,n=i);null!=(d=k[x]);x++){if(s&&d){g=0;while(m=e[g++])if(m(d,l,f)){p.push(d);break}T&&(w=N,n=++i)}o&&((d=!m&&d)&&v--,u&&b.push(d))}if(v+=x,o&&x!==v){g=0;while(m=t[g++])m(b,y,l,f);if(u){if(v>0)while(x--)b[x]||y[x]||(y[x]=L.call(p));y=xt(y)}H.apply(p,y),T&&!u&&y.length>0&&v+t.length>1&&ut.uniqueSort(p)}return T&&(w=N,a=C),b};return o?st(u):u}s=ut.compile=function(e,t){var n,r=[],i=[],o=N[e+" "];if(!o){t||(t=gt(e)),n=t.length;while(n--)o=wt(t[n]),o[y]?r.push(o):i.push(o);o=N(e,Tt(i,r))}return o};function Ct(e,t,n){var r=0,i=t.length;for(;i>r;r++)ut(e,t[r],n);return n}function kt(e,t,n,i){var o,a,u,l,c,f=gt(e);if(!i&&1===f.length){if(a=f[0]=f[0].slice(0),a.length>2&&"ID"===(u=a[0]).type&&9===t.nodeType&&p&&r.relative[a[1].type]){if(t=(r.find.ID(u.matches[0].replace(tt,nt),t)||[])[0],!t)return n;e=e.slice(a.shift().value.length)}o=G.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],r.relative[l=u.type])break;if((c=r.find[l])&&(i=c(u.matches[0].replace(tt,nt),X.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=i.length&&mt(a),!e)return H.apply(n,i),n;break}}}return s(e,f)(i,t,!p,n,X.test(e)),n}r.pseudos.nth=r.pseudos.eq;function Nt(){}Nt.prototype=r.filters=r.pseudos,r.setFilters=new Nt,b.sortStable=y.split("").sort(S).join("")===y,l(),[0,0].sort(S),b.detectDuplicates=E,at(function(e){if(e.innerHTML="","#"!==e.firstChild.getAttribute("href")){var t="type|href|height|width".split("|"),n=t.length;while(n--)r.attrHandle[t[n]]=ft}}),at(function(e){if(null!=e.getAttribute("disabled")){var t=P.split("|"),n=t.length;while(n--)r.attrHandle[t[n]]=ct}}),x.find=ut,x.expr=ut.selectors,x.expr[":"]=x.expr.pseudos,x.unique=ut.uniqueSort,x.text=ut.getText,x.isXMLDoc=ut.isXML,x.contains=ut.contains}(e);var D={};function A(e){var t=D[e]={};return x.each(e.match(w)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?D[e]||A(e):x.extend({},e);var t,n,r,i,o,s,a=[],u=!e.once&&[],l=function(f){for(t=e.memory&&f,n=!0,s=i||0,i=0,o=a.length,r=!0;a&&o>s;s++)if(a[s].apply(f[0],f[1])===!1&&e.stopOnFalse){t=!1;break}r=!1,a&&(u?u.length&&l(u.shift()):t?a=[]:c.disable())},c={add:function(){if(a){var n=a.length;(function s(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&c.has(n)||a.push(n):n&&n.length&&"string"!==r&&s(n)})})(arguments),r?o=a.length:t&&(i=n,l(t))}return this},remove:function(){return a&&x.each(arguments,function(e,t){var n;while((n=x.inArray(t,a,n))>-1)a.splice(n,1),r&&(o>=n&&o--,s>=n&&s--)}),this},has:function(e){return e?x.inArray(e,a)>-1:!(!a||!a.length)},empty:function(){return a=[],o=0,this},disable:function(){return a=u=t=undefined,this},disabled:function(){return!a},lock:function(){return u=undefined,t||c.disable(),this},locked:function(){return!u},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!a||n&&!u||(r?u.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!n}};return c},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var s=o[0],a=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var s=o[2],a=o[3];r[o[1]]=s.add,a&&s.add(function(){n=a},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=s.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=d.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),s=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?d.call(arguments):r,n===a?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},a,u,l;if(r>1)for(a=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(s(t,l,n)).fail(o.reject).progress(s(t,u,a)):--i;return i||o.resolveWith(l,n),o.promise()}}),x.support=function(t){var n=o.createElement("input"),r=o.createDocumentFragment(),i=o.createElement("div"),s=o.createElement("select"),a=s.appendChild(o.createElement("option"));return n.type?(n.type="checkbox",t.checkOn=""!==n.value,t.optSelected=a.selected,t.reliableMarginRight=!0,t.boxSizingReliable=!0,t.pixelPosition=!1,n.checked=!0,t.noCloneChecked=n.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!a.disabled,n=o.createElement("input"),n.value="t",n.type="radio",t.radioValue="t"===n.value,n.setAttribute("checked","t"),n.setAttribute("name","t"),r.appendChild(n),t.checkClone=r.cloneNode(!0).cloneNode(!0).lastChild.checked,t.focusinBubbles="onfocusin"in e,i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===i.style.backgroundClip,x(function(){var n,r,s="padding:0;margin:0;border:0;display:block;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box",a=o.getElementsByTagName("body")[0];a&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",a.appendChild(n).appendChild(i),i.innerHTML="",i.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%",x.swap(a,null!=a.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===i.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(i,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(i,null)||{width:"4px"}).width,r=i.appendChild(o.createElement("div")),r.style.cssText=i.style.cssText=s,r.style.marginRight=r.style.width="0",i.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),a.removeChild(n))}),t):t}({});var L,q,H=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,O=/([A-Z])/g;function F(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=x.expando+Math.random()}F.uid=1,F.accepts=function(e){return e.nodeType?1===e.nodeType||9===e.nodeType:!0},F.prototype={key:function(e){if(!F.accepts(e))return 0;var t={},n=e[this.expando];if(!n){n=F.uid++;try{t[this.expando]={value:n},Object.defineProperties(e,t)}catch(r){t[this.expando]=n,x.extend(e,t)}}return this.cache[n]||(this.cache[n]={}),n},set:function(e,t,n){var r,i=this.key(e),o=this.cache[i];if("string"==typeof t)o[t]=n;else if(x.isEmptyObject(o))this.cache[i]=t;else for(r in t)o[r]=t[r]},get:function(e,t){var n=this.cache[this.key(e)];return t===undefined?n:n[t]},access:function(e,t,n){return t===undefined||t&&"string"==typeof t&&n===undefined?this.get(e,t):(this.set(e,t,n),n!==undefined?n:t)},remove:function(e,t){var n,r,i=this.key(e),o=this.cache[i];if(t===undefined)this.cache[i]={};else{x.isArray(t)?r=t.concat(t.map(x.camelCase)):t in o?r=[t]:(r=x.camelCase(t),r=r in o?[r]:r.match(w)||[]),n=r.length;while(n--)delete o[r[n]]}},hasData:function(e){return!x.isEmptyObject(this.cache[e[this.expando]]||{})},discard:function(e){delete this.cache[this.key(e)]}},L=new F,q=new F,x.extend({acceptData:F.accepts,hasData:function(e){return L.hasData(e)||q.hasData(e)},data:function(e,t,n){return L.access(e,t,n)},removeData:function(e,t){L.remove(e,t)},_data:function(e,t,n){return q.access(e,t,n)},_removeData:function(e,t){q.remove(e,t)}}),x.fn.extend({data:function(e,t){var n,r,i=this[0],o=0,s=null;if(e===undefined){if(this.length&&(s=L.get(i),1===i.nodeType&&!q.get(i,"hasDataAttrs"))){for(n=i.attributes;n.length>o;o++)r=n[o].name,0===r.indexOf("data-")&&(r=x.camelCase(r.substring(5)),P(i,r,s[r]));q.set(i,"hasDataAttrs",!0)}return s}return"object"==typeof e?this.each(function(){L.set(this,e)}):x.access(this,function(t){var n,r=x.camelCase(e);if(i&&t===undefined){if(n=L.get(i,e),n!==undefined)return n;if(n=L.get(i,r),n!==undefined)return n;if(n=P(i,r,undefined),n!==undefined)return n}else this.each(function(){var n=L.get(this,r);L.set(this,r,t),-1!==e.indexOf("-")&&n!==undefined&&L.set(this,e,t)})},null,t,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){L.remove(this,e)})}});function P(e,t,n){var r;if(n===undefined&&1===e.nodeType)if(r="data-"+t.replace(O,"-$1").toLowerCase(),n=e.getAttribute(r),"string"==typeof n){try{n="true"===n?!0:"false"===n?!1:"null"===n?null:+n+""===n?+n:H.test(n)?JSON.parse(n):n}catch(i){}L.set(e,t,n)}else n=undefined;return n}x.extend({queue:function(e,t,n){var r;return e?(t=(t||"fx")+"queue",r=q.get(e,t),n&&(!r||x.isArray(n)?r=q.access(e,t,x.makeArray(n)):r.push(n)),r||[]):undefined},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),s=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,s,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return q.get(e,n)||q.access(e,n,{empty:x.Callbacks("once memory").add(function(){q.remove(e,[t+"queue",n])})})}}),x.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),n>arguments.length?x.queue(this[0],e):t===undefined?this:this.each(function(){var n=x.queue(this,e,t); +x._queueHooks(this,e),"fx"===e&&"inprogress"!==n[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=x.Deferred(),o=this,s=this.length,a=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=undefined),e=e||"fx";while(s--)n=q.get(o[s],e+"queueHooks"),n&&n.empty&&(r++,n.empty.add(a));return a(),i.promise(t)}});var R,M,W=/[\t\r\n]/g,$=/\r/g,B=/^(?:input|select|textarea|button)$/i;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[x.propFix[e]||e]})},addClass:function(e){var t,n,r,i,o,s=0,a=this.length,u="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];a>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(W," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,s=0,a=this.length,u=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];a>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(W," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,i="boolean"==typeof t;return x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,s=0,a=x(this),u=t,l=e.match(w)||[];while(o=l[s++])u=i?u:!a.hasClass(o),a[u?"addClass":"removeClass"](o)}else(n===r||"boolean"===n)&&(this.className&&q.set(this,"__className__",this.className),this.className=this.className||e===!1?"":q.get(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(W," ").indexOf(t)>=0)return!0;return!1},val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=x.isFunction(e),this.each(function(n){var i,o=x(this);1===this.nodeType&&(i=r?e.call(this,n,o.val()):e,null==i?i="":"number"==typeof i?i+="":x.isArray(i)&&(i=x.map(i,function(e){return null==e?"":e+""})),t=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&t.set(this,i,"value")!==undefined||(this.value=i))});if(i)return t=x.valHooks[i.type]||x.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&(n=t.get(i,"value"))!==undefined?n:(n=i.value,"string"==typeof n?n.replace($,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,s=o?null:[],a=o?i+1:r.length,u=0>i?a:o?i:0;for(;a>u;u++)if(n=r[u],!(!n.selected&&u!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),s=i.length;while(s--)r=i[s],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,t,n){var i,o,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===r?x.prop(e,t,n):(1===s&&x.isXMLDoc(e)||(t=t.toLowerCase(),i=x.attrHooks[t]||(x.expr.match.boolean.test(t)?M:R)),n===undefined?i&&"get"in i&&null!==(o=i.get(e,t))?o:(o=x.find.attr(e,t),null==o?undefined:o):null!==n?i&&"set"in i&&(o=i.set(e,n,t))!==undefined?o:(e.setAttribute(t,n+""),n):(x.removeAttr(e,t),undefined))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.boolean.test(n)&&(e[r]=!1),e.removeAttribute(n)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,t,n){var r,i,o,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return o=1!==s||!x.isXMLDoc(e),o&&(t=x.propFix[t]||t,i=x.propHooks[t]),n!==undefined?i&&"set"in i&&(r=i.set(e,n,t))!==undefined?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){return e.hasAttribute("tabindex")||B.test(e.nodeName)||e.href?e.tabIndex:-1}}}}),M={set:function(e,t,n){return t===!1?x.removeAttr(e,n):e.setAttribute(n,n),n}},x.each(x.expr.match.boolean.source.match(/\w+/g),function(e,t){var n=x.expr.attrHandle[t]||x.find.attr;x.expr.attrHandle[t]=function(e,t,r){var i=x.expr.attrHandle[t],o=r?undefined:(x.expr.attrHandle[t]=undefined)!=n(e,t,r)?t.toLowerCase():null;return x.expr.attrHandle[t]=i,o}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,t){return x.isArray(t)?e.checked=x.inArray(x(e).val(),t)>=0:undefined}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var I=/^key/,z=/^(?:mouse|contextmenu)|click/,_=/^(?:focusinfocus|focusoutblur)$/,X=/^([^.]*)(?:\.(.+)|)$/;function U(){return!0}function Y(){return!1}function V(){try{return o.activeElement}catch(e){}}x.event={global:{},add:function(e,t,n,i,o){var s,a,u,l,c,f,p,h,d,g,m,y=q.get(e);if(y){n.handler&&(s=n,n=s.handler,o=s.selector),n.guid||(n.guid=x.guid++),(l=y.events)||(l=y.events={}),(a=y.handle)||(a=y.handle=function(e){return typeof x===r||e&&x.event.triggered===e.type?undefined:x.event.dispatch.apply(a.elem,arguments)},a.elem=e),t=(t||"").match(w)||[""],c=t.length;while(c--)u=X.exec(t[c])||[],d=m=u[1],g=(u[2]||"").split(".").sort(),d&&(p=x.event.special[d]||{},d=(o?p.delegateType:p.bindType)||d,p=x.event.special[d]||{},f=x.extend({type:d,origType:m,data:i,handler:n,guid:n.guid,selector:o,needsContext:o&&x.expr.match.needsContext.test(o),namespace:g.join(".")},s),(h=l[d])||(h=l[d]=[],h.delegateCount=0,p.setup&&p.setup.call(e,i,g,a)!==!1||e.addEventListener&&e.addEventListener(d,a,!1)),p.add&&(p.add.call(e,f),f.handler.guid||(f.handler.guid=n.guid)),o?h.splice(h.delegateCount++,0,f):h.push(f),x.event.global[d]=!0);e=null}},remove:function(e,t,n,r,i){var o,s,a,u,l,c,f,p,h,d,g,m=q.hasData(e)&&q.get(e);if(m&&(u=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(a=X.exec(t[l])||[],h=g=a[1],d=(a[2]||"").split(".").sort(),h){f=x.event.special[h]||{},h=(r?f.delegateType:f.bindType)||h,p=u[h]||[],a=a[2]&&RegExp("(^|\\.)"+d.join("\\.(?:.*\\.|)")+"(\\.|$)"),s=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||a&&!a.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));s&&!p.length&&(f.teardown&&f.teardown.call(e,d,m.handle)!==!1||x.removeEvent(e,h,m.handle),delete u[h])}else for(h in u)x.event.remove(e,h+t[l],n,r,!0);x.isEmptyObject(u)&&(delete m.handle,q.remove(e,"events"))}},trigger:function(t,n,r,i){var s,a,u,l,c,f,p,h=[r||o],d=y.call(t,"type")?t.type:t,g=y.call(t,"namespace")?t.namespace.split("."):[];if(a=u=r=r||o,3!==r.nodeType&&8!==r.nodeType&&!_.test(d+x.event.triggered)&&(d.indexOf(".")>=0&&(g=d.split("."),d=g.shift(),g.sort()),c=0>d.indexOf(":")&&"on"+d,t=t[x.expando]?t:new x.Event(d,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=g.join("."),t.namespace_re=t.namespace?RegExp("(^|\\.)"+g.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=undefined,t.target||(t.target=r),n=null==n?[t]:x.makeArray(n,[t]),p=x.event.special[d]||{},i||!p.trigger||p.trigger.apply(r,n)!==!1)){if(!i&&!p.noBubble&&!x.isWindow(r)){for(l=p.delegateType||d,_.test(l+d)||(a=a.parentNode);a;a=a.parentNode)h.push(a),u=a;u===(r.ownerDocument||o)&&h.push(u.defaultView||u.parentWindow||e)}s=0;while((a=h[s++])&&!t.isPropagationStopped())t.type=s>1?l:p.bindType||d,f=(q.get(a,"events")||{})[t.type]&&q.get(a,"handle"),f&&f.apply(a,n),f=c&&a[c],f&&x.acceptData(a)&&f.apply&&f.apply(a,n)===!1&&t.preventDefault();return t.type=d,i||t.isDefaultPrevented()||p._default&&p._default.apply(h.pop(),n)!==!1||!x.acceptData(r)||c&&x.isFunction(r[d])&&!x.isWindow(r)&&(u=r[c],u&&(r[c]=null),x.event.triggered=d,r[d](),x.event.triggered=undefined,u&&(r[c]=u)),t.result}},dispatch:function(e){e=x.event.fix(e);var t,n,r,i,o,s=[],a=d.call(arguments),u=(q.get(this,"events")||{})[e.type]||[],l=x.event.special[e.type]||{};if(a[0]=e,e.delegateTarget=this,!l.preDispatch||l.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),t=0;while((i=s[t++])&&!e.isPropagationStopped()){e.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(o.namespace))&&(e.handleObj=o,e.data=o.data,r=((x.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,a),r!==undefined&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return l.postDispatch&&l.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,s=[],a=t.delegateCount,u=e.target;if(a&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!==this;u=u.parentNode||this)if(u.disabled!==!0||"click"!==e.type){for(r=[],n=0;a>n;n++)o=t[n],i=o.selector+" ",r[i]===undefined&&(r[i]=o.needsContext?x(i,this).index(u)>=0:x.find(i,this,null,[u]).length),r[i]&&r.push(o);r.length&&s.push({elem:u,handlers:r})}return t.length>a&&s.push({elem:this,handlers:t.slice(a)}),s},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,t){var n,r,i,s=t.button;return null==e.pageX&&null!=t.clientX&&(n=e.target.ownerDocument||o,r=n.documentElement,i=n.body,e.pageX=t.clientX+(r&&r.scrollLeft||i&&i.scrollLeft||0)-(r&&r.clientLeft||i&&i.clientLeft||0),e.pageY=t.clientY+(r&&r.scrollTop||i&&i.scrollTop||0)-(r&&r.clientTop||i&&i.clientTop||0)),e.which||s===undefined||(e.which=1&s?1:2&s?3:4&s?2:0),e}},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=z.test(i)?this.mouseHooks:I.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return 3===e.target.nodeType&&(e.target=e.target.parentNode),s.filter?s.filter(e,o):e},special:{load:{noBubble:!0},focus:{trigger:function(){return this!==V()&&this.focus?(this.focus(),!1):undefined},delegateType:"focusin"},blur:{trigger:function(){return this===V()&&this.blur?(this.blur(),!1):undefined},delegateType:"focusout"},click:{trigger:function(){return"checkbox"===this.type&&this.click&&x.nodeName(this,"input")?(this.click(),!1):undefined},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==undefined&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)},x.Event=function(e,t){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.getPreventDefault&&e.getPreventDefault()?U:Y):this.type=e,t&&x.extend(this,t),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,undefined):new x.Event(e,t)},x.Event.prototype={isDefaultPrevented:Y,isPropagationStopped:Y,isImmediatePropagationStopped:Y,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=U,e&&e.preventDefault&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=U,e&&e.stopPropagation&&e.stopPropagation()},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=U,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,t,n,r,i){var o,s;if("object"==typeof e){"string"!=typeof t&&(n=n||t,t=undefined);for(s in e)this.on(s,t,n,e[s],i);return this}if(null==n&&null==r?(r=t,n=t=undefined):null==r&&("string"==typeof t?(r=n,n=undefined):(r=n,n=t,t=undefined)),r===!1)r=Y;else if(!r)return this;return 1===i&&(o=r,r=function(e){return x().off(e),o.apply(this,arguments)},r.guid=o.guid||(o.guid=x.guid++)),this.each(function(){x.event.add(this,e,r,n,t)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,x(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return(t===!1||"function"==typeof t)&&(n=t,t=undefined),n===!1&&(n=Y),this.each(function(){x.event.remove(this,e,n,t)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];return n?x.event.trigger(e,t,n,!0):undefined}});var G=/^.[^:#\[\.,]*$/,J=x.expr.match.needsContext,Q={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n,r,i=this.length;if("string"!=typeof e)return t=this,this.pushStack(x(e).filter(function(){for(r=0;i>r;r++)if(x.contains(t[r],this))return!0}));for(n=[],r=0;i>r;r++)x.find(e,this[r],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t=x(e,this),n=t.length;return this.filter(function(){var e=0;for(;n>e;e++)if(x.contains(this,t[e]))return!0})},not:function(e){return this.pushStack(Z(this,e||[],!0))},filter:function(e){return this.pushStack(Z(this,e||[],!1))},is:function(e){return!!e&&("string"==typeof e?J.test(e)?x(e,this.context).index(this[0])>=0:x.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],s=J.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(s?s.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?g.call(x(e),this[0]):g.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function K(e,t){while((e=e[t])&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return K(e,"nextSibling")},prev:function(e){return K(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(Q[e]||x.unique(i),"p"===e[0]&&i.reverse()),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,t,n){var r=[],i=n!==undefined;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&x(e).is(n))break;r.push(e)}return r},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function Z(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(G.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return g.call(t,e)>=0!==n})}var et=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,tt=/<([\w:]+)/,nt=/<|&#?\w+;/,rt=/<(?:script|style|link)/i,it=/^(?:checkbox|radio)$/i,ot=/checked\s*(?:[^=]|=\s*.checked.)/i,st=/^$|\/(?:java|ecma)script/i,at=/^true\/(.*)/,ut=/^\s*\s*$/g,lt={option:[1,""],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};lt.optgroup=lt.option,lt.tbody=lt.tfoot=lt.colgroup=lt.caption=lt.col=lt.thead,lt.th=lt.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===undefined?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=ct(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=ct(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(gt(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&ht(gt(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++)1===e.nodeType&&(x.cleanData(gt(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var t=this[0]||{},n=0,r=this.length;if(e===undefined&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!rt.test(e)&&!lt[(tt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(et,"<$1>");try{for(;r>n;n++)t=this[n]||{},1===t.nodeType&&(x.cleanData(gt(t,!1)),t.innerHTML=e);t=0}catch(i){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=p.apply([],e);var r,i,o,s,a,u,l=0,c=this.length,f=this,h=c-1,d=e[0],g=x.isFunction(d);if(g||!(1>=c||"string"!=typeof d||x.support.checkClone)&&ot.test(d))return this.each(function(r){var i=f.eq(r);g&&(e[0]=d.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(r=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),i=r.firstChild,1===r.childNodes.length&&(r=i),i)){for(o=x.map(gt(r,"script"),ft),s=o.length;c>l;l++)a=r,l!==h&&(a=x.clone(a,!0,!0),s&&x.merge(o,gt(a,"script"))),t.call(this[l],a,l);if(s)for(u=o[o.length-1].ownerDocument,x.map(o,pt),l=0;s>l;l++)a=o[l],st.test(a.type||"")&&!q.access(a,"globalEval")&&x.contains(u,a)&&(a.src?x._evalUrl(a.src):x.globalEval(a.textContent.replace(ut,"")))}return this}}),x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=[],i=x(e),o=i.length-1,s=0;for(;o>=s;s++)n=s===o?this:this.clone(!0),x(i[s])[t](n),h.apply(r,n.get());return this.pushStack(r)}}),x.extend({clone:function(e,t,n){var r,i,o,s,a=e.cloneNode(!0),u=x.contains(e.ownerDocument,e);if(!(x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(s=gt(a),o=gt(e),r=0,i=o.length;i>r;r++)mt(o[r],s[r]);if(t)if(n)for(o=o||gt(e),s=s||gt(a),r=0,i=o.length;i>r;r++)dt(o[r],s[r]);else dt(e,a);return s=gt(a,"script"),s.length>0&&ht(s,!u&>(e,"script")),a},buildFragment:function(e,t,n,r){var i,o,s,a,u,l,c=0,f=e.length,p=t.createDocumentFragment(),h=[];for(;f>c;c++)if(i=e[c],i||0===i)if("object"===x.type(i))x.merge(h,i.nodeType?[i]:i);else if(nt.test(i)){o=o||p.appendChild(t.createElement("div")),s=(tt.exec(i)||["",""])[1].toLowerCase(),a=lt[s]||lt._default,o.innerHTML=a[1]+i.replace(et,"<$1>")+a[2],l=a[0];while(l--)o=o.firstChild;x.merge(h,o.childNodes),o=p.firstChild,o.textContent=""}else h.push(t.createTextNode(i));p.textContent="",c=0;while(i=h[c++])if((!r||-1===x.inArray(i,r))&&(u=x.contains(i.ownerDocument,i),o=gt(p.appendChild(i),"script"),u&&ht(o),n)){l=0;while(i=o[l++])st.test(i.type||"")&&n.push(i)}return p},cleanData:function(e){var t,n,r,i=e.length,o=0,s=x.event.special;for(;i>o;o++){if(n=e[o],x.acceptData(n)&&(t=q.access(n)))for(r in t.events)s[r]?x.event.remove(n,r):x.removeEvent(n,r,t.handle);L.discard(n),q.discard(n)}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"text",async:!1,global:!1,success:x.globalEval})}});function ct(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function ft(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function pt(e){var t=at.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function ht(e,t){var n=e.length,r=0;for(;n>r;r++)q.set(e[r],"globalEval",!t||q.get(t[r],"globalEval"))}function dt(e,t){var n,r,i,o,s,a,u,l;if(1===t.nodeType){if(q.hasData(e)&&(o=q.access(e),s=x.extend({},o),l=o.events,q.set(t,s),l)){delete s.handle,s.events={};for(i in l)for(n=0,r=l[i].length;r>n;n++)x.event.add(t,i,l[i][n])}L.hasData(e)&&(a=L.access(e),u=x.extend({},a),L.set(t,u))}}function gt(e,t){var n=e.getElementsByTagName?e.getElementsByTagName(t||"*"):e.querySelectorAll?e.querySelectorAll(t||"*"):[];return t===undefined||t&&x.nodeName(e,t)?x.merge([e],n):n}function mt(e,t){var n=t.nodeName.toLowerCase();"input"===n&&it.test(e.type)?t.checked=e.checked:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}x.fn.extend({wrapAll:function(e){var t;return x.isFunction(e)?this.each(function(t){x(this).wrapAll(e.call(this,t))}):(this[0]&&(t=x(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this)},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var yt,vt,xt=/^(none|table(?!-c[ea]).+)/,bt=/^margin/,wt=RegExp("^("+b+")(.*)$","i"),Tt=RegExp("^("+b+")(?!px)[a-z%]+$","i"),Ct=RegExp("^([+-])=("+b+")","i"),kt={BODY:"block"},Nt={position:"absolute",visibility:"hidden",display:"block"},Et={letterSpacing:0,fontWeight:400},St=["Top","Right","Bottom","Left"],jt=["Webkit","O","Moz","ms"];function Dt(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=jt.length;while(i--)if(t=jt[i]+n,t in e)return t;return r}function At(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function Lt(t){return e.getComputedStyle(t,null)}function qt(e,t){var n,r,i,o=[],s=0,a=e.length;for(;a>s;s++)r=e[s],r.style&&(o[s]=q.get(r,"olddisplay"),n=r.style.display,t?(o[s]||"none"!==n||(r.style.display=""),""===r.style.display&&At(r)&&(o[s]=q.access(r,"olddisplay",Pt(r.nodeName)))):o[s]||(i=At(r),(n&&"none"!==n||!i)&&q.set(r,"olddisplay",i?n:x.css(r,"display"))));for(s=0;a>s;s++)r=e[s],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[s]||"":"none"));return e}x.fn.extend({css:function(e,t){return x.access(this,function(e,t,n){var r,i,o={},s=0;if(x.isArray(t)){for(r=Lt(e),i=t.length;i>s;s++)o[t[s]]=x.css(e,t[s],!1,r);return o}return n!==undefined?x.style(e,t,n):x.css(e,t)},e,t,arguments.length>1)},show:function(){return qt(this,!0)},hide:function(){return qt(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:At(this))?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=yt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,s,a=x.camelCase(t),u=e.style;return t=x.cssProps[a]||(x.cssProps[a]=Dt(u,a)),s=x.cssHooks[t]||x.cssHooks[a],n===undefined?s&&"get"in s&&(i=s.get(e,!1,r))!==undefined?i:u[t]:(o=typeof n,"string"===o&&(i=Ct.exec(n))&&(n=(i[1]+1)*i[2]+parseFloat(x.css(e,t)),o="number"),null==n||"number"===o&&isNaN(n)||("number"!==o||x.cssNumber[a]||(n+="px"),x.support.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),s&&"set"in s&&(n=s.set(e,n,r))===undefined||(u[t]=n)),undefined)}},css:function(e,t,n,r){var i,o,s,a=x.camelCase(t);return t=x.cssProps[a]||(x.cssProps[a]=Dt(e.style,a)),s=x.cssHooks[t]||x.cssHooks[a],s&&"get"in s&&(i=s.get(e,!0,n)),i===undefined&&(i=yt(e,t,r)),"normal"===i&&t in Et&&(i=Et[t]),""===n||n?(o=parseFloat(i),n===!0||x.isNumeric(o)?o||0:i):i}}),yt=function(e,t,n){var r,i,o,s=n||Lt(e),a=s?s.getPropertyValue(t)||s[t]:undefined,u=e.style;return s&&(""!==a||x.contains(e.ownerDocument,e)||(a=x.style(e,t)),Tt.test(a)&&bt.test(t)&&(r=u.width,i=u.minWidth,o=u.maxWidth,u.minWidth=u.maxWidth=u.width=a,a=s.width,u.width=r,u.minWidth=i,u.maxWidth=o)),a};function Ht(e,t,n){var r=wt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function Ot(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,s=0;for(;4>o;o+=2)"margin"===n&&(s+=x.css(e,n+St[o],!0,i)),r?("content"===n&&(s-=x.css(e,"padding"+St[o],!0,i)),"margin"!==n&&(s-=x.css(e,"border"+St[o]+"Width",!0,i))):(s+=x.css(e,"padding"+St[o],!0,i),"padding"!==n&&(s+=x.css(e,"border"+St[o]+"Width",!0,i)));return s}function Ft(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Lt(e),s=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=yt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Tt.test(i))return i;r=s&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+Ot(e,t,n||(s?"border":"content"),r,o)+"px"}function Pt(e){var t=o,n=kt[e];return n||(n=Rt(e,t),"none"!==n&&n||(vt=(vt||x(" + + + + + diff --git a/tests/automation/nwfaketop/internal/package.json b/tests/automation/nwfaketop/internal/package.json new file mode 100644 index 0000000000..b2bc1566ae --- /dev/null +++ b/tests/automation/nwfaketop/internal/package.json @@ -0,0 +1,11 @@ +{ + "name": "nwfaketop", + "main": "index.html", + "window": { + "width": 600, + "height": 400, + "position": "center", + "toolbar": true, + "resizable": true + } +} \ No newline at end of file diff --git a/tests/automation/nwfaketop/mocha_test.js b/tests/automation/nwfaketop/mocha_test.js new file mode 100644 index 0000000000..fb32f30701 --- /dev/null +++ b/tests/automation/nwfaketop/mocha_test.js @@ -0,0 +1,44 @@ +var path = require('path'); +var assert = require('assert'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + + +describe('nwfaketop',function(){ + + var child, server, result; + + before(function(done){ + this.timeout(0); + server = createTCPServer(13013); + child = spawnChildProcess(path.join(curDir, 'internal')); + + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + result = JSON.parse(data+''); + done(); + }); + }); + + }); + + after(function(done){ + server.close(); + child.kill(); + done(); + }); + + it("nwfaketop attribute set",function(done){ + assert(result[0],true); + done(); + }); + it("nwfaketop attribute default",function(done){ + assert(result[1],true); + done(); + }); + + +}); + + diff --git a/tests/automation/nwfaketop/package.json b/tests/automation/nwfaketop/package.json new file mode 100644 index 0000000000..0877473e9c --- /dev/null +++ b/tests/automation/nwfaketop/package.json @@ -0,0 +1,4 @@ +{ + "name":"nwfaketop_wrapper", + "main":"index.html" +} diff --git a/tests/automation/package.json b/tests/automation/package.json new file mode 100644 index 0000000000..03957725b8 --- /dev/null +++ b/tests/automation/package.json @@ -0,0 +1,13 @@ +{ + "name": "automation", + "version": "0.1.0", + "main": "index.html", + "window": { + "position": "center", + "show": false + }, + "dependencies": { + "xunit-file": "*", + "colors": "*" + } +} diff --git a/tests/automation/plugin/flash/index.html b/tests/automation/plugin/flash/index.html new file mode 100644 index 0000000000..26b23a9fb1 --- /dev/null +++ b/tests/automation/plugin/flash/index.html @@ -0,0 +1,35 @@ + + + + SWFObject - step 1 + + + +
+ + + + + + +

Alternative content

+ +
+ + + + + +
+
+ + diff --git a/tests/automation/plugin/flash/package.json b/tests/automation/plugin/flash/package.json new file mode 100644 index 0000000000..9dede79d65 --- /dev/null +++ b/tests/automation/plugin/flash/package.json @@ -0,0 +1,20 @@ +{ + "main": "index.html", + "name": "nw-demo", + "description": "demo app of node-webkit", + "version": "0.1", + "keywords": [ "demo", "node-webkit" ], + "window": { + "toolbar": true, + "width": 800, + "height": 500, + "position": "mouse", + "min_width": 400, + "min_height": 200, + "max_width": 800, + "max_height": 600 + }, + "webkit": { + "plugin": true + } +} diff --git a/tests/automation/plugin/flash/relog.swf b/tests/automation/plugin/flash/relog.swf new file mode 100644 index 0000000000..db63f88062 Binary files /dev/null and b/tests/automation/plugin/flash/relog.swf differ diff --git a/tests/automation/plugin/index.html b/tests/automation/plugin/index.html new file mode 100644 index 0000000000..655f1d6f72 --- /dev/null +++ b/tests/automation/plugin/index.html @@ -0,0 +1,16 @@ + + + + + plugin test + + +

plugin test

+ + + + + + + + diff --git a/tests/automation/plugin/mocha_test.js b/tests/automation/plugin/mocha_test.js new file mode 100644 index 0000000000..ac053a7e11 --- /dev/null +++ b/tests/automation/plugin/mocha_test.js @@ -0,0 +1,40 @@ +var path = require('path'); +var assert = require('assert'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + +describe('Plugin', function() { + + describe('flash', function(){ + + var server, child, result = false; + + before(function(done) { + server = createTCPServer(13013); + child = spawnChildProcess(path.join(curDir, 'flash')); + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + result = true; + child.kill(); + done(); + }); + }); + + }); + + after(function () { + server.close(); + }); + + it('should not crash', function() { + + assert.equal(result, true); + + }); + + }); + +}); + + diff --git a/tests/automation/plugin/package.json b/tests/automation/plugin/package.json new file mode 100644 index 0000000000..4313392294 --- /dev/null +++ b/tests/automation/plugin/package.json @@ -0,0 +1,5 @@ +{ + "name":"plugin_wrapper", + "main":"index.html" +} + diff --git a/tests/automation/process/exit/index.html b/tests/automation/process/exit/index.html new file mode 100644 index 0000000000..769749b1a4 --- /dev/null +++ b/tests/automation/process/exit/index.html @@ -0,0 +1,20 @@ + + + + + +sdew + + diff --git a/tests/automation/process/exit/package.json b/tests/automation/process/exit/package.json new file mode 100644 index 0000000000..0631d3a337 --- /dev/null +++ b/tests/automation/process/exit/package.json @@ -0,0 +1,5 @@ +{ + "name": "nw-demo", + "main": "index.html", + "window": { "show" : true } +} diff --git a/tests/automation/process/index.html b/tests/automation/process/index.html new file mode 100644 index 0000000000..94e29b0cd3 --- /dev/null +++ b/tests/automation/process/index.html @@ -0,0 +1,16 @@ + + + + + process test + + +

Process test

+ + + + + + + + diff --git a/tests/automation/process/mocha_test.js b/tests/automation/process/mocha_test.js new file mode 100644 index 0000000000..22c1388226 --- /dev/null +++ b/tests/automation/process/mocha_test.js @@ -0,0 +1,32 @@ +var path = require('path'); +var assert = require('assert'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + +describe('process', function() { + describe('exit', function(){ + it('event process.exit should be fired after calling win.close(true)', function(done) { + + + var child = spawnChildProcess(path.join(curDir, 'exit')); + + setTimeout(function() { + var tmpFilePath = path.join(curDir, 'exit', 'a.txt'); + fs.exists(tmpFilePath, function (exists) { + if (exists) { + fs.unlink(tmpFilePath); + done(); + } else { + done('the event `exit` does not been called.'); + } + }); + + }, 1500); + + + }); + + }); + +}); + diff --git a/tests/automation/process/package.json b/tests/automation/process/package.json new file mode 100644 index 0000000000..c83e304e6e --- /dev/null +++ b/tests/automation/process/package.json @@ -0,0 +1,5 @@ +{ + "name":"process_wrapper", + "main":"index.html" +} + diff --git a/tests/automation/proxy/export.bat b/tests/automation/proxy/export.bat new file mode 100644 index 0000000000..fe25b8b4be --- /dev/null +++ b/tests/automation/proxy/export.bat @@ -0,0 +1 @@ +reg export "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" c:\test.reg \ No newline at end of file diff --git a/tests/automation/proxy/index.html b/tests/automation/proxy/index.html new file mode 100644 index 0000000000..1708c25344 --- /dev/null +++ b/tests/automation/proxy/index.html @@ -0,0 +1,16 @@ + + + + + Proxy test + + +

Proxy test

+ + + + + + + + diff --git a/tests/automation/proxy/mocha_test.js b/tests/automation/proxy/mocha_test.js new file mode 100644 index 0000000000..f2b1fda63c --- /dev/null +++ b/tests/automation/proxy/mocha_test.js @@ -0,0 +1,51 @@ +var cp = require('child_process'); +var os = require('os'); +var path = require('path'); +var assert = require('assert'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + +describe('proxy', function(){ + before(function(done){ + if (os.platform() == "win32"){ + this.timeout(0); + var ex = cp.exec(path.join(curDir, 'export.bat'), + function(error, stdout, stderr){ + console.log(stdout); + done(); + }); + } + else + done(); + }) + after(function(){ + this.timeout(0); + if (os.platform() == "win32"){ + fs.unlink('c:/test.reg', function (err) { + if (err) console.log(err); + }); + } + }) + it('the get proxyfor url should work fine', function(done) { + if (os.platform() == "win32"){ + this.timeout(0); + setTimeout(function(){ + var data = fs.readFileSync("c:/test.reg",'utf16le'); + var index = data.indexOf('ProxyServer'); + var right = data.substring(index+14); + var array = right.split('"'); + var gui = require('nw.gui'); + var re = gui.App.getProxyForURL("https://www.google.com.hk/"); + if (re == "PROXY "+array[0]) + done(); + else + done('the method getProxyForURL is not right'); + },1000); + } + else { + done(); + } + }); + +}); + diff --git a/tests/automation/proxy/package.json b/tests/automation/proxy/package.json new file mode 100644 index 0000000000..b395473dc3 --- /dev/null +++ b/tests/automation/proxy/package.json @@ -0,0 +1,5 @@ +{ + "name":"proxy_wrapper", + "main":"index.html" +} + diff --git a/tests/automation/quit_with_secondary_window_on_top/index.html b/tests/automation/quit_with_secondary_window_on_top/index.html new file mode 100644 index 0000000000..70522fdc0c --- /dev/null +++ b/tests/automation/quit_with_secondary_window_on_top/index.html @@ -0,0 +1,16 @@ + + + + + quit with secondary window on top test + + +

quit with secondary window on top test

+ + + + + + + + diff --git a/tests/automation/quit_with_secondary_window_on_top/internal/index.html b/tests/automation/quit_with_secondary_window_on_top/internal/index.html new file mode 100644 index 0000000000..9ce8558db6 --- /dev/null +++ b/tests/automation/quit_with_secondary_window_on_top/internal/index.html @@ -0,0 +1,17 @@ + + + + quit_with_secondary_window_on_top! + + +

quit_with_secondary_window_on_top!

+ with the created new window on top, hit CMD-Q or select Menu > Quit + . + + diff --git a/tests/automation/quit_with_secondary_window_on_top/internal/index1.html b/tests/automation/quit_with_secondary_window_on_top/internal/index1.html new file mode 100644 index 0000000000..a73f7b5476 --- /dev/null +++ b/tests/automation/quit_with_secondary_window_on_top/internal/index1.html @@ -0,0 +1,18 @@ + + + + New Window! + + +

New Window!

+ We are using node.js + + + diff --git a/tests/automation/quit_with_secondary_window_on_top/internal/package.json b/tests/automation/quit_with_secondary_window_on_top/internal/package.json new file mode 100644 index 0000000000..9b415e218a --- /dev/null +++ b/tests/automation/quit_with_secondary_window_on_top/internal/package.json @@ -0,0 +1,4 @@ +{ + "name": "nw-quit_with_secondary_window_on_top", + "main": "index.html" +} diff --git a/tests/automation/quit_with_secondary_window_on_top/mocha_test.js b/tests/automation/quit_with_secondary_window_on_top/mocha_test.js new file mode 100644 index 0000000000..0bc93039a6 --- /dev/null +++ b/tests/automation/quit_with_secondary_window_on_top/mocha_test.js @@ -0,0 +1,40 @@ +var assert = require('assert'); +var path = require('path'); +var exec = require('child_process').exec; +var spawn = require('child_process').spawn; +var os = require('os'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + +var result; + +if (os.platform() === 'darwin') { + +describe('quit_with_secondary_window_on_top', function() { + before(function(done) { + this.timeout(0); + var app_path = path.join(curDir, 'internal'); + var exec_path = process.execPath; + exec_path = exec_path.replace(/ /g, '\\ '); + exec(exec_path + ' ' + app_path, function(error, stdout, stderr) { + result = error; + done(); + }); + }) + it('should quit with secondary window on top without error', function() { + assert.equal(result, null); + }); +}); + +} else { + console.log('This test need to run under darwin'); +describe('quit_with_secondary_window_on_top', function() { + + it('should quit with secondary window on top without error', function() { + // todo + }); +}); + +} + + diff --git a/tests/automation/quit_with_secondary_window_on_top/package.json b/tests/automation/quit_with_secondary_window_on_top/package.json new file mode 100644 index 0000000000..cdb919062f --- /dev/null +++ b/tests/automation/quit_with_secondary_window_on_top/package.json @@ -0,0 +1,5 @@ +{ + "name":"quit_with_secondary_window_on_top_wrapper", + "main":"index.html" +} + diff --git a/tests/automation/reference-node-main/index.html b/tests/automation/reference-node-main/index.html new file mode 100644 index 0000000000..e8fedb870d --- /dev/null +++ b/tests/automation/reference-node-main/index.html @@ -0,0 +1,53 @@ + + + + + test + + + + + + diff --git a/tests/automation/reference-node-main/index.js b/tests/automation/reference-node-main/index.js new file mode 100644 index 0000000000..04b94f07a4 --- /dev/null +++ b/tests/automation/reference-node-main/index.js @@ -0,0 +1,24 @@ +exports.message = "hello world"; +exports.port = 10000; +exports.ready = false; + +var server = require('net').createServer(); +server.on('connection',function(socket){ + socket.on('data',function(data){ + socket.write(data); + }); +}); + +server.on('error',function(){ + try{ + server.close(); + }catch(e){ + exports.port += 1; + setTimeout(function(){ + server.listen(exports.port); + },0); + } +}); +server.listen(exports.port,function(){ + exports.ready = true; +}); diff --git a/tests/automation/reference-node-main/package.json b/tests/automation/reference-node-main/package.json new file mode 100644 index 0000000000..486c567b08 --- /dev/null +++ b/tests/automation/reference-node-main/package.json @@ -0,0 +1,6 @@ +{ + "name":"nw_1403596464", + "main":"index.html", + "dependencies":{}, + "node-main":"./index.js" +} \ No newline at end of file diff --git a/tests/automation/reference_xhr_in_node_context/index.html b/tests/automation/reference_xhr_in_node_context/index.html new file mode 100644 index 0000000000..a7d063e37a --- /dev/null +++ b/tests/automation/reference_xhr_in_node_context/index.html @@ -0,0 +1,16 @@ + + + + + reference xhr in node context test + + +

reference xhr in node context test

+ + + + + + + + diff --git a/tests/automation/reference_xhr_in_node_context/internal/index.html b/tests/automation/reference_xhr_in_node_context/internal/index.html new file mode 100644 index 0000000000..848b42c98f --- /dev/null +++ b/tests/automation/reference_xhr_in_node_context/internal/index.html @@ -0,0 +1,32 @@ + + + + + test + + +

it works!

+ + + + diff --git a/tests/automation/reference_xhr_in_node_context/internal/package.json b/tests/automation/reference_xhr_in_node_context/internal/package.json new file mode 100644 index 0000000000..1f456a50f6 --- /dev/null +++ b/tests/automation/reference_xhr_in_node_context/internal/package.json @@ -0,0 +1,5 @@ +{ + "name":"nw_1404278514", + "main":"index.html", + "dependencies":{} +} \ No newline at end of file diff --git a/tests/automation/reference_xhr_in_node_context/internal/test.js b/tests/automation/reference_xhr_in_node_context/internal/test.js new file mode 100644 index 0000000000..e935f938c6 --- /dev/null +++ b/tests/automation/reference_xhr_in_node_context/internal/test.js @@ -0,0 +1,11 @@ +exports.xhr = function () { + global.xhr.onreadystatechange = function() { + if (global.xhr.readyState === 4 && global.xhr.status === 200) { + } + } + global.xhr.onload = function(e) { + var uInt8Array = new Uint8Array(this.response); // this.response == uInt8Array.buffer + // var byte3 = uInt8Array[4]; // byte at offset 4 + }; + +} \ No newline at end of file diff --git a/tests/automation/reference_xhr_in_node_context/mocha_test.js b/tests/automation/reference_xhr_in_node_context/mocha_test.js new file mode 100644 index 0000000000..c96eb6c7a5 --- /dev/null +++ b/tests/automation/reference_xhr_in_node_context/mocha_test.js @@ -0,0 +1,34 @@ +var path = require('path'); +var assert = require('assert'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + +describe('reference xhr from node context',function(){ + + var server, child, crash = true; + + before(function(done) { + this.timeout(0); + server = createTCPServer(13013); + child = spawnChildProcess(path.join(curDir, 'internal')); + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + crash = false; + child.kill(); + done(); + }); + }); + + }); + + after(function () { + server.close(); + }); + + it("reference xhr from node context should not crash",function(done){ + assert.equal(crash,false); + done(); + }); +}); + diff --git a/tests/automation/reference_xhr_in_node_context/package.json b/tests/automation/reference_xhr_in_node_context/package.json new file mode 100644 index 0000000000..ca5ad75924 --- /dev/null +++ b/tests/automation/reference_xhr_in_node_context/package.json @@ -0,0 +1,5 @@ +{ + "name":"reference_xhr_in_node_context_wrapper", + "main":"index.html" +} + diff --git a/tests/automation/reload_application/index.html b/tests/automation/reload_application/index.html new file mode 100644 index 0000000000..52d2674005 --- /dev/null +++ b/tests/automation/reload_application/index.html @@ -0,0 +1,16 @@ + + + + + reload application test + + +

reload application test

+ + + + + + + + diff --git a/tests/automation/reload_application/internal/index.html b/tests/automation/reload_application/internal/index.html new file mode 100644 index 0000000000..5144305aa3 --- /dev/null +++ b/tests/automation/reload_application/internal/index.html @@ -0,0 +1,96 @@ + + + + + + + + + +
+ +
+ +
+ + + + diff --git a/tests/automation/reload_application/internal/package.json b/tests/automation/reload_application/internal/package.json new file mode 100644 index 0000000000..3323c9b893 --- /dev/null +++ b/tests/automation/reload_application/internal/package.json @@ -0,0 +1,7 @@ +{ + "name": "reload application", + "main": "index.html", + "window": { + "show": false + } +} diff --git a/tests/automation/reload_application/mocha_test.js b/tests/automation/reload_application/mocha_test.js new file mode 100644 index 0000000000..9f0a3c7546 --- /dev/null +++ b/tests/automation/reload_application/mocha_test.js @@ -0,0 +1,189 @@ +var assert = require('assert'); +var spawn = require('child_process').spawn; +var path = require('path'); +var net = require('net'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + +var cb; + +describe('AppTest', function(){ + + describe('reload app (long-to-run)', function(){ + + + var server; + + before(function(done) { + server = createTCPServer(13013); + done(); + }); + + after(function () { + server.close(); + }); + + afterEach(function(){ + try { + server.removeAllListeners('connection'); + } catch (e) { + } + + }); + + + + var spawnChild = function(type) { + return spawn(process.execPath, [path.join(curDir, 'internal'), type]); + }; + + + it('close window after reload', function(done){ + this.timeout(0); + var result = false; + + var app = spawnChild(0); + app.on('exit', function (code){ + if (code != 0) return done('error'); + result = true; + done(); + }); + + setTimeout(function(){ + if (!result) { + app.kill(); + done("Timeout, Can not close window."); + } + }, 30000); + + }); + + + + it('quit app after reload', function(done){ + this.timeout(0); + var result = false; + var app = spawnChild(1); + app.on('exit', function (code){ + if (code) { + done('error: the error code is: ' + code); + } else { + result = true; + done(); + } + }); + + setTimeout(function(){ + if (!result) { + app.kill(); + done("Timeout, Can not quit App."); + } + }, 30000); + + + }); + + + it('close window after reload dev', function(done){ + this.timeout(0); + var times = 0; + var result = false; + + server.on('connection', cb = function(socket){ + + //console.log('client connect in 1'); + socket.setEncoding('utf8'); + socket.on('error', function(er) { + //console.log(er); + }); + socket.on('data', function(data){ + if (data == 'open'){ + if (times == 0){ + times += 1; + socket.write('reload'); + } else if (times == 1){ + socket.write('quit'); + } + + }// if(data == 'open') + + }); + }); + + var app = spawnChild(2); + + app.on('exit', function (code){ + if (code) { + done('error: the error code is: ' + code); + } else { + result = true; + done(); + } + }); + + + setTimeout(function(){ + if (!result) { + app.kill(); + done("Timeout, Can not close window."); + } + }, 30000); + + + }); + + + it('quit app after reload dev', function(done){ + this.timeout(0); + var times = 0; + var result = false; + + server.on('connection', cb = function(socket){ + socket.on('error', function(er) { + //console.log(er); + }); + //console.log('client connect'); + socket.setEncoding('utf8'); + socket.on('data', function(data){ + if (data == 'open'){ + + if (times == 0){ + times += 1; + socket.write('reload'); + } else if (times == 1){ + socket.write('quit'); + } + + }// if(data == 'open') + + }); + }); + + var app = spawnChild(3); + + app.on('exit', function (code){ + if (code != 0) return done('error'); + result = true; + done(); + }); + + + setTimeout(function(){ + if (!result) { + app.kill(); + done("Timeout, Can not quit App."); + } + }, 30000); + + + }); + + + + + }); + +}); + + + diff --git a/tests/automation/reload_application/package.json b/tests/automation/reload_application/package.json new file mode 100644 index 0000000000..37488a02a1 --- /dev/null +++ b/tests/automation/reload_application/package.json @@ -0,0 +1,5 @@ +{ + "name":"reload_application_wrapper", + "main":"index.html" +} + diff --git a/tests/automation/remote-img/imgnotshown/package.json b/tests/automation/remote-img/imgnotshown/package.json new file mode 100644 index 0000000000..d1242b6d18 --- /dev/null +++ b/tests/automation/remote-img/imgnotshown/package.json @@ -0,0 +1,5 @@ +{ + "name": "remote file access", + "main": "http://localhost:8123/index.html", + "node-remote": "localhost" +} \ No newline at end of file diff --git a/tests/automation/remote-img/imgshown/index.html b/tests/automation/remote-img/imgshown/index.html new file mode 100644 index 0000000000..a424b015c2 --- /dev/null +++ b/tests/automation/remote-img/imgshown/index.html @@ -0,0 +1,21 @@ + + + + + remote file access test + + + +
+ + + \ No newline at end of file diff --git a/tests/automation/remote-img/imgshown/package.json b/tests/automation/remote-img/imgshown/package.json new file mode 100644 index 0000000000..d9d91badba --- /dev/null +++ b/tests/automation/remote-img/imgshown/package.json @@ -0,0 +1,4 @@ +{ + "name": "remote file access", + "main": "index.html" +} \ No newline at end of file diff --git a/tests/automation/remote-img/index.html b/tests/automation/remote-img/index.html new file mode 100644 index 0000000000..1800fbd5d1 --- /dev/null +++ b/tests/automation/remote-img/index.html @@ -0,0 +1,16 @@ + + + + + remote img test + + +

remote img test

+ + + + + + + + diff --git a/tests/automation/remote-img/mocha_test.js b/tests/automation/remote-img/mocha_test.js new file mode 100644 index 0000000000..aa5582afba --- /dev/null +++ b/tests/automation/remote-img/mocha_test.js @@ -0,0 +1,93 @@ +var path = require('path'); +var spawn = require('child_process').spawn; +var fs = require('fs-extra'); +var os = require('os'); +var platform = os.platform(); +var curDir = fs.realpathSync('.'); + +var result, app, exec_argv, timerId; + +process.on('exit', function() { + if (app) { + app.kill('SIGKILL'); + } +}); + +describe('remote-file-access', function(){ + + before(function(done){ + this.timeout(0); + if(platform == "win32"){ + fs.copySync(path.join(curDir, 'star.jpg'),'/star.jpg'); + } + else { + fs.copySync(path.join(curDir, 'star.jpg'),'/tmp/star.jpg'); + } + done(); + }); + + after(function(done) { + this.timeout(0); + if (platform == "win32"){ + fs.unlinkSync('/star.jpg'); + } + else { + fs.unlinkSync('/tmp/star.jpg'); + } + done(); + }); + + + it ('remote img should work', function(done){ + this.timeout(0); + exec_argv = [path.join(curDir, 'imgnotshown')]; + result =false; + app = spawn(process.execPath, exec_argv); + app.on('exit', function(code){ + + clearTimeout(timerId); + result = true; + app.kill('SIGKILL'); + app = undefined; + done(); + }); + + timerId = setTimeout(function(){ + if(!result){ + app.removeAllListeners('exit'); + app.kill('SIGKILL'); + app = undefined; + done(); + } + }, 2000); + }); + + + it ('local img should work', function(done){ + this.timeout(0); + exec_argv = [path.join(curDir, 'imgshown')]; + result = false; + app = spawn(process.execPath, exec_argv); + app.on('exit', function(code){ + clearTimeout(timerId); + result = true; + app.kill('SIGKILL'); + app = undefined; + done(); + }); + + timerId=setTimeout(function(){ + if(!result){ + app.removeAllListeners('exit'); + app.kill('SIGKILL'); + app = undefined; + done(); + } + }, 2000); + + }); + + +}); + + diff --git a/tests/automation/remote-img/package.json b/tests/automation/remote-img/package.json new file mode 100644 index 0000000000..849ae2d40b --- /dev/null +++ b/tests/automation/remote-img/package.json @@ -0,0 +1,5 @@ +{ + "name":"remote_img_wrapper", + "main":"index.html" +} + diff --git a/tests/automation/remote-img/star.jpg b/tests/automation/remote-img/star.jpg new file mode 100644 index 0000000000..9d634e2c27 Binary files /dev/null and b/tests/automation/remote-img/star.jpg differ diff --git a/tests/automation/res/assert.js b/tests/automation/res/assert.js new file mode 100644 index 0000000000..73c9fa55ce --- /dev/null +++ b/tests/automation/res/assert.js @@ -0,0 +1,431 @@ +// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 +// +// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! +// +// Copyright (c) 2011 Jxck +// +// Originally from node.js (http://nodejs.org) +// Copyright Joyent, Inc. +// +// 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 copies 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 +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 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(module) { + +if (typeof module.exports === 'undefined') { + module.exports = module; // this case must be browser +} + +// UTILITY + +// Object.create compatible in IE +var create = Object.create || function(p) { + if (!p) throw Error('no type'); + function f() {}; + f.prototype = p; + return new f(); +}; + +// UTILITY +var util = { + inherits: function(ctor, superCtor) { + ctor.super_ = superCtor; + ctor.prototype = create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }, + isArray: function(ar) { + return Array.isArray(ar); + }, + isBoolean: function(arg) { + return typeof arg === 'boolean'; + }, + isNull: function(arg) { + return arg === null; + }, + isNullOrUndefined: function(arg) { + return arg == null; + }, + isNumber: function(arg) { + return typeof arg === 'number'; + }, + isString: function(arg) { + return typeof arg === 'string'; + }, + isSymbol: function(arg) { + return typeof arg === 'symbol'; + }, + isUndefined: function(arg) { + return arg === void 0; + }, + isRegExp: function(re) { + return util.isObject(re) && util.objectToString(re) === '[object RegExp]'; + }, + isObject: function(arg) { + return typeof arg === 'object' && arg !== null; + }, + isDate: function(d) { + return util.isObject(d) && util.objectToString(d) === '[object Date]'; + }, + isError: function(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); + }, + isFunction: function(arg) { + return typeof arg === 'function'; + }, + isPrimitive: function(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; + }, + objectToString: function(o) { + return Object.prototype.toString.call(o); + } +}; + +var pSlice = Array.prototype.slice; + +// from https://github.com/substack/node-deep-equal +var Object_keys = typeof Object.keys === 'function' + ? Object.keys + : function (obj) { + var keys = []; + for (var key in obj) keys.push(key); + return keys; + } +; + +// 1. The assert module provides functions that throw +// AssertionError's when particular conditions are not met. The +// assert module must conform to the following interface. + +var assert = module.exports = ok; + +// 2. The AssertionError is defined in assert. +// new assert.AssertionError({ message: message, +// actual: actual, +// expected: expected }) + +assert.AssertionError = function AssertionError(options) { + this.name = 'AssertionError'; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + if (options.message) { + this.message = options.message; + this.generatedMessage = false; + } else { + this.message = getMessage(this); + this.generatedMessage = true; + } + var stackStartFunction = options.stackStartFunction || fail; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } else { + // try to throw an error now, and from the stack property + // work out the line that called in to assert.js. + try { + this.stack = (new Error).stack.toString(); + } catch (e) {} + } +}; + +// assert.AssertionError instanceof Error +util.inherits(assert.AssertionError, Error); + +function replacer(key, value) { + if (util.isUndefined(value)) { + return '' + value; + } + if (util.isNumber(value) && (isNaN(value) || !isFinite(value))) { + return value.toString(); + } + if (util.isFunction(value) || util.isRegExp(value)) { + return value.toString(); + } + return value; +} + +function truncate(s, n) { + if (util.isString(s)) { + return s.length < n ? s : s.slice(0, n); + } else { + return s; + } +} + +function getMessage(self) { + return truncate(JSON.stringify(self.actual, replacer), 128) + ' ' + + self.operator + ' ' + + truncate(JSON.stringify(self.expected, replacer), 128); +} + +// At present only the three keys mentioned above are used and +// understood by the spec. Implementations or sub modules can pass +// other keys to the AssertionError's constructor - they will be +// ignored. + +// 3. All of the following functions must throw an AssertionError +// when a corresponding condition is not met, with a message that +// may be undefined if not provided. All assertion methods provide +// both the actual and expected values to the assertion error for +// display purposes. + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); +} + +// EXTENSION! allows for well behaved errors defined elsewhere. +assert.fail = fail; + +// 4. Pure assertion tests whether a value is truthy, as determined +// by !!guard. +// assert.ok(guard, message_opt); +// This statement is equivalent to assert.equal(true, !!guard, +// message_opt);. To test strictly for the value true, use +// assert.strictEqual(true, guard, message_opt);. + +function ok(value, message) { + if (!value) fail(value, true, message, '==', assert.ok); +} +assert.ok = ok; + +// 5. The equality assertion tests shallow, coercive equality with +// ==. +// assert.equal(actual, expected, message_opt); + +assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, '==', assert.equal); +}; + +// 6. The non-equality assertion tests for whether two objects are not equal +// with != assert.notEqual(actual, expected, message_opt); + +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, '!=', assert.notEqual); + } +}; + +// 7. The equivalence assertion tests a deep equality relation. +// assert.deepEqual(actual, expected, message_opt); + +assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected)) { + fail(actual, expected, message, 'deepEqual', assert.deepEqual); + } +}; + +function _deepEqual(actual, expected) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + + // } else if (util.isBuffer(actual) && util.isBuffer(expected)) { + // if (actual.length != expected.length) return false; + // + // for (var i = 0; i < actual.length; i++) { + // if (actual[i] !== expected[i]) return false; + // } + // + // return true; + // + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (util.isDate(actual) && util.isDate(expected)) { + return actual.getTime() === expected.getTime(); + + // 7.3 If the expected value is a RegExp object, the actual value is + // equivalent if it is also a RegExp object with the same source and + // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). + } else if (util.isRegExp(actual) && util.isRegExp(expected)) { + return actual.source === expected.source && + actual.global === expected.global && + actual.multiline === expected.multiline && + actual.lastIndex === expected.lastIndex && + actual.ignoreCase === expected.ignoreCase; + + // 7.4. Other pairs that do not both pass typeof value == 'object', + // equivalence is determined by ==. + } else if (!util.isObject(actual) && !util.isObject(expected)) { + return actual == expected; + + // 7.5 For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical 'prototype' property. Note: this + // accounts for both named and indexed properties on Arrays. + } else { + return objEquiv(actual, expected); + } +} + +function isArguments(object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; +} + +function objEquiv(a, b) { + if (util.isNullOrUndefined(a) || util.isNullOrUndefined(b)) + return false; + // an identical 'prototype' property. + if (a.prototype !== b.prototype) return false; + //~~~I've managed to break Object.keys through screwy arguments passing. + // Converting to array solves the problem. + var aIsArgs = isArguments(a), + bIsArgs = isArguments(b); + if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) + return false; + if (aIsArgs) { + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b); + } + try { + var ka = Object.keys(a), + kb = Object.keys(b), + key, i; + } catch (e) {//happens when one is a string literal and the other isn't + return false; + } + // having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length != kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] != kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!_deepEqual(a[key], b[key])) return false; + } + return true; +} + +// 8. The non-equivalence assertion tests for any deep inequality. +// assert.notDeepEqual(actual, expected, message_opt); + +assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected)) { + fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); + } +}; + +// 9. The strict equality assertion tests strict equality, as determined by ===. +// assert.strictEqual(actual, expected, message_opt); + +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, '===', assert.strictEqual); + } +}; + +// 10. The strict non-equality assertion tests for strict inequality, as +// determined by !==. assert.notStrictEqual(actual, expected, message_opt); + +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, '!==', assert.notStrictEqual); + } +}; + +function expectedException(actual, expected) { + if (!actual || !expected) { + return false; + } + + if (Object.prototype.toString.call(expected) == '[object RegExp]') { + return expected.test(actual); + } else if (actual instanceof expected) { + return true; + } else if (expected.call({}, actual) === true) { + return true; + } + + return false; +} + +function _throws(shouldThrow, block, expected, message) { + var actual; + + if (util.isString(expected)) { + message = expected; + expected = null; + } + + try { + block(); + } catch (e) { + actual = e; + } + + message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + + (message ? ' ' + message : '.'); + + if (shouldThrow && !actual) { + fail(actual, expected, 'Missing expected exception' + message); + } + + if (!shouldThrow && expectedException(actual, expected)) { + fail(actual, expected, 'Got unwanted exception' + message); + } + + if ((shouldThrow && actual && expected && + !expectedException(actual, expected)) || (!shouldThrow && actual)) { + throw actual; + } +} + +// 11. Expected to throw an error: +// assert.throws(block, Error_opt, message_opt); + +assert.throws = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [true].concat(pSlice.call(arguments))); +}; + +// EXTENSION! This is annoying to write outside this module. +assert.doesNotThrow = function(block, /*optional*/message) { + _throws.apply(this, [false].concat(pSlice.call(arguments))); +}; + +assert.ifError = function(err) { if (err) {throw err;}}; + +module.assert = module.exports; +delete module.exports; +})(this); diff --git a/tests/automation/res/build.sh b/tests/automation/res/build.sh new file mode 100755 index 0000000000..484afb91fd --- /dev/null +++ b/tests/automation/res/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +uglifyjs mocha.js chai.js assert.js util.js -c -m -o mocha_util.js + diff --git a/tests/automation/res/chai.js b/tests/automation/res/chai.js new file mode 100644 index 0000000000..75eaecbb65 --- /dev/null +++ b/tests/automation/res/chai.js @@ -0,0 +1,4800 @@ + +;(function(){ + +/** + * Require the module at `name`. + * + * @param {String} name + * @return {Object} exports + * @api public + */ + +function require(name) { + var module = require.modules[name]; + if (!module) throw new Error('failed to require "' + name + '"'); + + if (!('exports' in module) && typeof module.definition === 'function') { + module.client = module.component = true; + module.definition.call(this, module.exports = {}, module); + delete module.definition; + } + + return module.exports; +} + +/** + * Meta info, accessible in the global scope unless you use AMD option. + */ + +require.loader = 'component'; + +/** + * Internal helper object, contains a sorting function for semantiv versioning + */ +require.helper = {}; +require.helper.semVerSort = function(a, b) { + var aArray = a.version.split('.'); + var bArray = b.version.split('.'); + for (var i=0; i bLex ? 1 : -1; + continue; + } else if (aInt > bInt) { + return 1; + } else { + return -1; + } + } + return 0; +} + +/** + * Find and require a module which name starts with the provided name. + * If multiple modules exists, the highest semver is used. + * This function can only be used for remote dependencies. + + * @param {String} name - module name: `user~repo` + * @param {Boolean} returnPath - returns the canonical require path if true, + * otherwise it returns the epxorted module + */ +require.latest = function (name, returnPath) { + function showError(name) { + throw new Error('failed to find latest module of "' + name + '"'); + } + // only remotes with semvers, ignore local files conataining a '/' + var versionRegexp = /(.*)~(.*)@v?(\d+\.\d+\.\d+[^\/]*)$/; + var remoteRegexp = /(.*)~(.*)/; + if (!remoteRegexp.test(name)) showError(name); + var moduleNames = Object.keys(require.modules); + var semVerCandidates = []; + var otherCandidates = []; // for instance: name of the git branch + for (var i=0; i 0) { + var module = semVerCandidates.sort(require.helper.semVerSort).pop().name; + if (returnPath === true) { + return module; + } + return require(module); + } + // if the build contains more than one branch of the same module + // you should not use this funciton + var module = otherCandidates.pop().name; + if (returnPath === true) { + return module; + } + return require(module); +} + +/** + * Registered modules. + */ + +require.modules = {}; + +/** + * Register module at `name` with callback `definition`. + * + * @param {String} name + * @param {Function} definition + * @api private + */ + +require.register = function (name, definition) { + require.modules[name] = { + definition: definition + }; +}; + +/** + * Define a module's exports immediately with `exports`. + * + * @param {String} name + * @param {Generic} exports + * @api private + */ + +require.define = function (name, exports) { + require.modules[name] = { + exports: exports + }; +}; +require.register("chaijs~assertion-error@1.0.0", function (exports, module) { +/*! + * assertion-error + * Copyright(c) 2013 Jake Luer + * MIT Licensed + */ + +/*! + * Return a function that will copy properties from + * one object to another excluding any originally + * listed. Returned function will create a new `{}`. + * + * @param {String} excluded properties ... + * @return {Function} + */ + +function exclude () { + var excludes = [].slice.call(arguments); + + function excludeProps (res, obj) { + Object.keys(obj).forEach(function (key) { + if (!~excludes.indexOf(key)) res[key] = obj[key]; + }); + } + + return function extendExclude () { + var args = [].slice.call(arguments) + , i = 0 + , res = {}; + + for (; i < args.length; i++) { + excludeProps(res, args[i]); + } + + return res; + }; +}; + +/*! + * Primary Exports + */ + +module.exports = AssertionError; + +/** + * ### AssertionError + * + * An extension of the JavaScript `Error` constructor for + * assertion and validation scenarios. + * + * @param {String} message + * @param {Object} properties to include (optional) + * @param {callee} start stack function (optional) + */ + +function AssertionError (message, _props, ssf) { + var extend = exclude('name', 'message', 'stack', 'constructor', 'toJSON') + , props = extend(_props || {}); + + // default values + this.message = message || 'Unspecified AssertionError'; + this.showDiff = false; + + // copy from properties + for (var key in props) { + this[key] = props[key]; + } + + // capture stack trace + ssf = ssf || arguments.callee; + if (ssf && Error.captureStackTrace) { + Error.captureStackTrace(this, ssf); + } +} + +/*! + * Inherit from Error.prototype + */ + +AssertionError.prototype = Object.create(Error.prototype); + +/*! + * Statically set name + */ + +AssertionError.prototype.name = 'AssertionError'; + +/*! + * Ensure correct constructor + */ + +AssertionError.prototype.constructor = AssertionError; + +/** + * Allow errors to be converted to JSON for static transfer. + * + * @param {Boolean} include stack (default: `true`) + * @return {Object} object that can be `JSON.stringify` + */ + +AssertionError.prototype.toJSON = function (stack) { + var extend = exclude('constructor', 'toJSON', 'stack') + , props = extend({ name: this.name }, this); + + // include stack if exists and not turned off + if (false !== stack && this.stack) { + props.stack = this.stack; + } + + return props; +}; + +}); + +require.register("chaijs~type-detect@0.1.1", function (exports, module) { +/*! + * type-detect + * Copyright(c) 2013 jake luer + * MIT Licensed + */ + +/*! + * Primary Exports + */ + +var exports = module.exports = getType; + +/*! + * Detectable javascript natives + */ + +var natives = { + '[object Array]': 'array' + , '[object RegExp]': 'regexp' + , '[object Function]': 'function' + , '[object Arguments]': 'arguments' + , '[object Date]': 'date' +}; + +/** + * ### typeOf (obj) + * + * Use several different techniques to determine + * the type of object being tested. + * + * + * @param {Mixed} object + * @return {String} object type + * @api public + */ + +function getType (obj) { + var str = Object.prototype.toString.call(obj); + if (natives[str]) return natives[str]; + if (obj === null) return 'null'; + if (obj === undefined) return 'undefined'; + if (obj === Object(obj)) return 'object'; + return typeof obj; +} + +exports.Library = Library; + +/** + * ### Library + * + * Create a repository for custom type detection. + * + * ```js + * var lib = new type.Library; + * ``` + * + */ + +function Library () { + this.tests = {}; +} + +/** + * #### .of (obj) + * + * Expose replacement `typeof` detection to the library. + * + * ```js + * if ('string' === lib.of('hello world')) { + * // ... + * } + * ``` + * + * @param {Mixed} object to test + * @return {String} type + */ + +Library.prototype.of = getType; + +/** + * #### .define (type, test) + * + * Add a test to for the `.test()` assertion. + * + * Can be defined as a regular expression: + * + * ```js + * lib.define('int', /^[0-9]+$/); + * ``` + * + * ... or as a function: + * + * ```js + * lib.define('bln', function (obj) { + * if ('boolean' === lib.of(obj)) return true; + * var blns = [ 'yes', 'no', 'true', 'false', 1, 0 ]; + * if ('string' === lib.of(obj)) obj = obj.toLowerCase(); + * return !! ~blns.indexOf(obj); + * }); + * ``` + * + * @param {String} type + * @param {RegExp|Function} test + * @api public + */ + +Library.prototype.define = function (type, test) { + if (arguments.length === 1) return this.tests[type]; + this.tests[type] = test; + return this; +}; + +/** + * #### .test (obj, test) + * + * Assert that an object is of type. Will first + * check natives, and if that does not pass it will + * use the user defined custom tests. + * + * ```js + * assert(lib.test('1', 'int')); + * assert(lib.test('yes', 'bln')); + * ``` + * + * @param {Mixed} object + * @param {String} type + * @return {Boolean} result + * @api public + */ + +Library.prototype.test = function (obj, type) { + if (type === getType(obj)) return true; + var test = this.tests[type]; + + if (test && 'regexp' === getType(test)) { + return test.test(obj); + } else if (test && 'function' === getType(test)) { + return test(obj); + } else { + throw new ReferenceError('Type test "' + type + '" not defined or invalid.'); + } +}; + +}); + +require.register("chaijs~deep-eql@0.1.3", function (exports, module) { +/*! + * deep-eql + * Copyright(c) 2013 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependencies + */ + +var type = require('chaijs~type-detect@0.1.1'); + +/*! + * Buffer.isBuffer browser shim + */ + +var Buffer; +try { Buffer = require('buffer').Buffer; } +catch(ex) { + Buffer = {}; + Buffer.isBuffer = function() { return false; } +} + +/*! + * Primary Export + */ + +module.exports = deepEqual; + +/** + * Assert super-strict (egal) equality between + * two objects of any type. + * + * @param {Mixed} a + * @param {Mixed} b + * @param {Array} memoised (optional) + * @return {Boolean} equal match + */ + +function deepEqual(a, b, m) { + if (sameValue(a, b)) { + return true; + } else if ('date' === type(a)) { + return dateEqual(a, b); + } else if ('regexp' === type(a)) { + return regexpEqual(a, b); + } else if (Buffer.isBuffer(a)) { + return bufferEqual(a, b); + } else if ('arguments' === type(a)) { + return argumentsEqual(a, b, m); + } else if (!typeEqual(a, b)) { + return false; + } else if (('object' !== type(a) && 'object' !== type(b)) + && ('array' !== type(a) && 'array' !== type(b))) { + return sameValue(a, b); + } else { + return objectEqual(a, b, m); + } +} + +/*! + * Strict (egal) equality test. Ensures that NaN always + * equals NaN and `-0` does not equal `+0`. + * + * @param {Mixed} a + * @param {Mixed} b + * @return {Boolean} equal match + */ + +function sameValue(a, b) { + if (a === b) return a !== 0 || 1 / a === 1 / b; + return a !== a && b !== b; +} + +/*! + * Compare the types of two given objects and + * return if they are equal. Note that an Array + * has a type of `array` (not `object`) and arguments + * have a type of `arguments` (not `array`/`object`). + * + * @param {Mixed} a + * @param {Mixed} b + * @return {Boolean} result + */ + +function typeEqual(a, b) { + return type(a) === type(b); +} + +/*! + * Compare two Date objects by asserting that + * the time values are equal using `saveValue`. + * + * @param {Date} a + * @param {Date} b + * @return {Boolean} result + */ + +function dateEqual(a, b) { + if ('date' !== type(b)) return false; + return sameValue(a.getTime(), b.getTime()); +} + +/*! + * Compare two regular expressions by converting them + * to string and checking for `sameValue`. + * + * @param {RegExp} a + * @param {RegExp} b + * @return {Boolean} result + */ + +function regexpEqual(a, b) { + if ('regexp' !== type(b)) return false; + return sameValue(a.toString(), b.toString()); +} + +/*! + * Assert deep equality of two `arguments` objects. + * Unfortunately, these must be sliced to arrays + * prior to test to ensure no bad behavior. + * + * @param {Arguments} a + * @param {Arguments} b + * @param {Array} memoize (optional) + * @return {Boolean} result + */ + +function argumentsEqual(a, b, m) { + if ('arguments' !== type(b)) return false; + a = [].slice.call(a); + b = [].slice.call(b); + return deepEqual(a, b, m); +} + +/*! + * Get enumerable properties of a given object. + * + * @param {Object} a + * @return {Array} property names + */ + +function enumerable(a) { + var res = []; + for (var key in a) res.push(key); + return res; +} + +/*! + * Simple equality for flat iterable objects + * such as Arrays or Node.js buffers. + * + * @param {Iterable} a + * @param {Iterable} b + * @return {Boolean} result + */ + +function iterableEqual(a, b) { + if (a.length !== b.length) return false; + + var i = 0; + var match = true; + + for (; i < a.length; i++) { + if (a[i] !== b[i]) { + match = false; + break; + } + } + + return match; +} + +/*! + * Extension to `iterableEqual` specifically + * for Node.js Buffers. + * + * @param {Buffer} a + * @param {Mixed} b + * @return {Boolean} result + */ + +function bufferEqual(a, b) { + if (!Buffer.isBuffer(b)) return false; + return iterableEqual(a, b); +} + +/*! + * Block for `objectEqual` ensuring non-existing + * values don't get in. + * + * @param {Mixed} object + * @return {Boolean} result + */ + +function isValue(a) { + return a !== null && a !== undefined; +} + +/*! + * Recursively check the equality of two objects. + * Once basic sameness has been established it will + * defer to `deepEqual` for each enumerable key + * in the object. + * + * @param {Mixed} a + * @param {Mixed} b + * @return {Boolean} result + */ + +function objectEqual(a, b, m) { + if (!isValue(a) || !isValue(b)) { + return false; + } + + if (a.prototype !== b.prototype) { + return false; + } + + var i; + if (m) { + for (i = 0; i < m.length; i++) { + if ((m[i][0] === a && m[i][1] === b) + || (m[i][0] === b && m[i][1] === a)) { + return true; + } + } + } else { + m = []; + } + + try { + var ka = enumerable(a); + var kb = enumerable(b); + } catch (ex) { + return false; + } + + ka.sort(); + kb.sort(); + + if (!iterableEqual(ka, kb)) { + return false; + } + + m.push([ a, b ]); + + var key; + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!deepEqual(a[key], b[key], m)) { + return false; + } + } + + return true; +} + +}); + +require.register("chai", function (exports, module) { +module.exports = require('chai/lib/chai.js'); + +}); + +require.register("chai/lib/chai.js", function (exports, module) { +/*! + * chai + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +var used = [] + , exports = module.exports = {}; + +/*! + * Chai version + */ + +exports.version = '1.10.0'; + +/*! + * Assertion Error + */ + +exports.AssertionError = require('chaijs~assertion-error@1.0.0'); + +/*! + * Utils for plugins (not exported) + */ + +var util = require('chai/lib/chai/utils/index.js'); + +/** + * # .use(function) + * + * Provides a way to extend the internals of Chai + * + * @param {Function} + * @returns {this} for chaining + * @api public + */ + +exports.use = function (fn) { + if (!~used.indexOf(fn)) { + fn(this, util); + used.push(fn); + } + + return this; +}; + +/*! + * Configuration + */ + +var config = require('chai/lib/chai/config.js'); +exports.config = config; + +/*! + * Primary `Assertion` prototype + */ + +var assertion = require('chai/lib/chai/assertion.js'); +exports.use(assertion); + +/*! + * Core Assertions + */ + +var core = require('chai/lib/chai/core/assertions.js'); +exports.use(core); + +/*! + * Expect interface + */ + +var expect = require('chai/lib/chai/interface/expect.js'); +exports.use(expect); + +/*! + * Should interface + */ + +var should = require('chai/lib/chai/interface/should.js'); +exports.use(should); + +/*! + * Assert interface + */ + +var assert = require('chai/lib/chai/interface/assert.js'); +exports.use(assert); + +}); + +require.register("chai/lib/chai/assertion.js", function (exports, module) { +/*! + * chai + * http://chaijs.com + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +var config = require('chai/lib/chai/config.js'); +var NOOP = function() { }; + +module.exports = function (_chai, util) { + /*! + * Module dependencies. + */ + + var AssertionError = _chai.AssertionError + , flag = util.flag; + + /*! + * Module export. + */ + + _chai.Assertion = Assertion; + + /*! + * Assertion Constructor + * + * Creates object for chaining. + * + * @api private + */ + + function Assertion (obj, msg, stack) { + flag(this, 'ssfi', stack || arguments.callee); + flag(this, 'object', obj); + flag(this, 'message', msg); + } + + Object.defineProperty(Assertion, 'includeStack', { + get: function() { + console.warn('Assertion.includeStack is deprecated, use chai.config.includeStack instead.'); + return config.includeStack; + }, + set: function(value) { + console.warn('Assertion.includeStack is deprecated, use chai.config.includeStack instead.'); + config.includeStack = value; + } + }); + + Object.defineProperty(Assertion, 'showDiff', { + get: function() { + console.warn('Assertion.showDiff is deprecated, use chai.config.showDiff instead.'); + return config.showDiff; + }, + set: function(value) { + console.warn('Assertion.showDiff is deprecated, use chai.config.showDiff instead.'); + config.showDiff = value; + } + }); + + Assertion.addProperty = function (name, fn) { + util.addProperty(this.prototype, name, fn); + }; + + Assertion.addMethod = function (name, fn) { + util.addMethod(this.prototype, name, fn); + }; + + Assertion.addChainableMethod = function (name, fn, chainingBehavior) { + util.addChainableMethod(this.prototype, name, fn, chainingBehavior); + }; + + Assertion.addChainableNoop = function(name, fn) { + util.addChainableMethod(this.prototype, name, NOOP, fn); + }; + + Assertion.overwriteProperty = function (name, fn) { + util.overwriteProperty(this.prototype, name, fn); + }; + + Assertion.overwriteMethod = function (name, fn) { + util.overwriteMethod(this.prototype, name, fn); + }; + + Assertion.overwriteChainableMethod = function (name, fn, chainingBehavior) { + util.overwriteChainableMethod(this.prototype, name, fn, chainingBehavior); + }; + + /*! + * ### .assert(expression, message, negateMessage, expected, actual) + * + * Executes an expression and check expectations. Throws AssertionError for reporting if test doesn't pass. + * + * @name assert + * @param {Philosophical} expression to be tested + * @param {String or Function} message or function that returns message to display if fails + * @param {String or Function} negatedMessage or function that returns negatedMessage to display if negated expression fails + * @param {Mixed} expected value (remember to check for negation) + * @param {Mixed} actual (optional) will default to `this.obj` + * @api private + */ + + Assertion.prototype.assert = function (expr, msg, negateMsg, expected, _actual, showDiff) { + var ok = util.test(this, arguments); + if (true !== showDiff) showDiff = false; + if (true !== config.showDiff) showDiff = false; + + if (!ok) { + var msg = util.getMessage(this, arguments) + , actual = util.getActual(this, arguments); + throw new AssertionError(msg, { + actual: actual + , expected: expected + , showDiff: showDiff + }, (config.includeStack) ? this.assert : flag(this, 'ssfi')); + } + }; + + /*! + * ### ._obj + * + * Quick reference to stored `actual` value for plugin developers. + * + * @api private + */ + + Object.defineProperty(Assertion.prototype, '_obj', + { get: function () { + return flag(this, 'object'); + } + , set: function (val) { + flag(this, 'object', val); + } + }); +}; + +}); + +require.register("chai/lib/chai/config.js", function (exports, module) { +module.exports = { + + /** + * ### config.includeStack + * + * User configurable property, influences whether stack trace + * is included in Assertion error message. Default of false + * suppresses stack trace in the error message. + * + * chai.config.includeStack = true; // enable stack on error + * + * @param {Boolean} + * @api public + */ + + includeStack: false, + + /** + * ### config.showDiff + * + * User configurable property, influences whether or not + * the `showDiff` flag should be included in the thrown + * AssertionErrors. `false` will always be `false`; `true` + * will be true when the assertion has requested a diff + * be shown. + * + * @param {Boolean} + * @api public + */ + + showDiff: true, + + /** + * ### config.truncateThreshold + * + * User configurable property, sets length threshold for actual and + * expected values in assertion errors. If this threshold is exceeded, + * the value is truncated. + * + * Set it to zero if you want to disable truncating altogether. + * + * chai.config.truncateThreshold = 0; // disable truncating + * + * @param {Number} + * @api public + */ + + truncateThreshold: 40 + +}; + +}); + +require.register("chai/lib/chai/core/assertions.js", function (exports, module) { +/*! + * chai + * http://chaijs.com + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +module.exports = function (chai, _) { + var Assertion = chai.Assertion + , toString = Object.prototype.toString + , flag = _.flag; + + /** + * ### Language Chains + * + * The following are provided as chainable getters to + * improve the readability of your assertions. They + * do not provide testing capabilities unless they + * have been overwritten by a plugin. + * + * **Chains** + * + * - to + * - be + * - been + * - is + * - that + * - and + * - has + * - have + * - with + * - at + * - of + * - same + * + * @name language chains + * @api public + */ + + [ 'to', 'be', 'been' + , 'is', 'and', 'has', 'have' + , 'with', 'that', 'at' + , 'of', 'same' ].forEach(function (chain) { + Assertion.addProperty(chain, function () { + return this; + }); + }); + + /** + * ### .not + * + * Negates any of assertions following in the chain. + * + * expect(foo).to.not.equal('bar'); + * expect(goodFn).to.not.throw(Error); + * expect({ foo: 'baz' }).to.have.property('foo') + * .and.not.equal('bar'); + * + * @name not + * @api public + */ + + Assertion.addProperty('not', function () { + flag(this, 'negate', true); + }); + + /** + * ### .deep + * + * Sets the `deep` flag, later used by the `equal` and + * `property` assertions. + * + * expect(foo).to.deep.equal({ bar: 'baz' }); + * expect({ foo: { bar: { baz: 'quux' } } }) + * .to.have.deep.property('foo.bar.baz', 'quux'); + * + * @name deep + * @api public + */ + + Assertion.addProperty('deep', function () { + flag(this, 'deep', true); + }); + + /** + * ### .a(type) + * + * The `a` and `an` assertions are aliases that can be + * used either as language chains or to assert a value's + * type. + * + * // typeof + * expect('test').to.be.a('string'); + * expect({ foo: 'bar' }).to.be.an('object'); + * expect(null).to.be.a('null'); + * expect(undefined).to.be.an('undefined'); + * + * // language chain + * expect(foo).to.be.an.instanceof(Foo); + * + * @name a + * @alias an + * @param {String} type + * @param {String} message _optional_ + * @api public + */ + + function an (type, msg) { + if (msg) flag(this, 'message', msg); + type = type.toLowerCase(); + var obj = flag(this, 'object') + , article = ~[ 'a', 'e', 'i', 'o', 'u' ].indexOf(type.charAt(0)) ? 'an ' : 'a '; + + this.assert( + type === _.type(obj) + , 'expected #{this} to be ' + article + type + , 'expected #{this} not to be ' + article + type + ); + } + + Assertion.addChainableMethod('an', an); + Assertion.addChainableMethod('a', an); + + /** + * ### .include(value) + * + * The `include` and `contain` assertions can be used as either property + * based language chains or as methods to assert the inclusion of an object + * in an array or a substring in a string. When used as language chains, + * they toggle the `contain` flag for the `keys` assertion. + * + * expect([1,2,3]).to.include(2); + * expect('foobar').to.contain('foo'); + * expect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo'); + * + * @name include + * @alias contain + * @param {Object|String|Number} obj + * @param {String} message _optional_ + * @api public + */ + + function includeChainingBehavior () { + flag(this, 'contains', true); + } + + function include (val, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + var expected = false; + if (_.type(obj) === 'array' && _.type(val) === 'object') { + for (var i in obj) { + if (_.eql(obj[i], val)) { + expected = true; + break; + } + } + } else if (_.type(val) === 'object') { + if (!flag(this, 'negate')) { + for (var k in val) new Assertion(obj).property(k, val[k]); + return; + } + var subset = {} + for (var k in val) subset[k] = obj[k] + expected = _.eql(subset, val); + } else { + expected = obj && ~obj.indexOf(val) + } + this.assert( + expected + , 'expected #{this} to include ' + _.inspect(val) + , 'expected #{this} to not include ' + _.inspect(val)); + } + + Assertion.addChainableMethod('include', include, includeChainingBehavior); + Assertion.addChainableMethod('contain', include, includeChainingBehavior); + + /** + * ### .ok + * + * Asserts that the target is truthy. + * + * expect('everthing').to.be.ok; + * expect(1).to.be.ok; + * expect(false).to.not.be.ok; + * expect(undefined).to.not.be.ok; + * expect(null).to.not.be.ok; + * + * Can also be used as a function, which prevents some linter errors. + * + * expect('everthing').to.be.ok(); + * + * @name ok + * @api public + */ + + Assertion.addChainableNoop('ok', function () { + this.assert( + flag(this, 'object') + , 'expected #{this} to be truthy' + , 'expected #{this} to be falsy'); + }); + + /** + * ### .true + * + * Asserts that the target is `true`. + * + * expect(true).to.be.true; + * expect(1).to.not.be.true; + * + * Can also be used as a function, which prevents some linter errors. + * + * expect(true).to.be.true(); + * + * @name true + * @api public + */ + + Assertion.addChainableNoop('true', function () { + this.assert( + true === flag(this, 'object') + , 'expected #{this} to be true' + , 'expected #{this} to be false' + , this.negate ? false : true + ); + }); + + /** + * ### .false + * + * Asserts that the target is `false`. + * + * expect(false).to.be.false; + * expect(0).to.not.be.false; + * + * Can also be used as a function, which prevents some linter errors. + * + * expect(false).to.be.false(); + * + * @name false + * @api public + */ + + Assertion.addChainableNoop('false', function () { + this.assert( + false === flag(this, 'object') + , 'expected #{this} to be false' + , 'expected #{this} to be true' + , this.negate ? true : false + ); + }); + + /** + * ### .null + * + * Asserts that the target is `null`. + * + * expect(null).to.be.null; + * expect(undefined).not.to.be.null; + * + * Can also be used as a function, which prevents some linter errors. + * + * expect(null).to.be.null(); + * + * @name null + * @api public + */ + + Assertion.addChainableNoop('null', function () { + this.assert( + null === flag(this, 'object') + , 'expected #{this} to be null' + , 'expected #{this} not to be null' + ); + }); + + /** + * ### .undefined + * + * Asserts that the target is `undefined`. + * + * expect(undefined).to.be.undefined; + * expect(null).to.not.be.undefined; + * + * Can also be used as a function, which prevents some linter errors. + * + * expect(undefined).to.be.undefined(); + * + * @name undefined + * @api public + */ + + Assertion.addChainableNoop('undefined', function () { + this.assert( + undefined === flag(this, 'object') + , 'expected #{this} to be undefined' + , 'expected #{this} not to be undefined' + ); + }); + + /** + * ### .exist + * + * Asserts that the target is neither `null` nor `undefined`. + * + * var foo = 'hi' + * , bar = null + * , baz; + * + * expect(foo).to.exist; + * expect(bar).to.not.exist; + * expect(baz).to.not.exist; + * + * Can also be used as a function, which prevents some linter errors. + * + * expect(foo).to.exist(); + * + * @name exist + * @api public + */ + + Assertion.addChainableNoop('exist', function () { + this.assert( + null != flag(this, 'object') + , 'expected #{this} to exist' + , 'expected #{this} to not exist' + ); + }); + + + /** + * ### .empty + * + * Asserts that the target's length is `0`. For arrays, it checks + * the `length` property. For objects, it gets the count of + * enumerable keys. + * + * expect([]).to.be.empty; + * expect('').to.be.empty; + * expect({}).to.be.empty; + * + * Can also be used as a function, which prevents some linter errors. + * + * expect([]).to.be.empty(); + * + * @name empty + * @api public + */ + + Assertion.addChainableNoop('empty', function () { + var obj = flag(this, 'object') + , expected = obj; + + if (Array.isArray(obj) || 'string' === typeof object) { + expected = obj.length; + } else if (typeof obj === 'object') { + expected = Object.keys(obj).length; + } + + this.assert( + !expected + , 'expected #{this} to be empty' + , 'expected #{this} not to be empty' + ); + }); + + /** + * ### .arguments + * + * Asserts that the target is an arguments object. + * + * function test () { + * expect(arguments).to.be.arguments; + * } + * + * Can also be used as a function, which prevents some linter errors. + * + * function test () { + * expect(arguments).to.be.arguments(); + * } + * + * @name arguments + * @alias Arguments + * @api public + */ + + function checkArguments () { + var obj = flag(this, 'object') + , type = Object.prototype.toString.call(obj); + this.assert( + '[object Arguments]' === type + , 'expected #{this} to be arguments but got ' + type + , 'expected #{this} to not be arguments' + ); + } + + Assertion.addChainableNoop('arguments', checkArguments); + Assertion.addChainableNoop('Arguments', checkArguments); + + /** + * ### .equal(value) + * + * Asserts that the target is strictly equal (`===`) to `value`. + * Alternately, if the `deep` flag is set, asserts that + * the target is deeply equal to `value`. + * + * expect('hello').to.equal('hello'); + * expect(42).to.equal(42); + * expect(1).to.not.equal(true); + * expect({ foo: 'bar' }).to.not.equal({ foo: 'bar' }); + * expect({ foo: 'bar' }).to.deep.equal({ foo: 'bar' }); + * + * @name equal + * @alias equals + * @alias eq + * @alias deep.equal + * @param {Mixed} value + * @param {String} message _optional_ + * @api public + */ + + function assertEqual (val, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + if (flag(this, 'deep')) { + return this.eql(val); + } else { + this.assert( + val === obj + , 'expected #{this} to equal #{exp}' + , 'expected #{this} to not equal #{exp}' + , val + , this._obj + , true + ); + } + } + + Assertion.addMethod('equal', assertEqual); + Assertion.addMethod('equals', assertEqual); + Assertion.addMethod('eq', assertEqual); + + /** + * ### .eql(value) + * + * Asserts that the target is deeply equal to `value`. + * + * expect({ foo: 'bar' }).to.eql({ foo: 'bar' }); + * expect([ 1, 2, 3 ]).to.eql([ 1, 2, 3 ]); + * + * @name eql + * @alias eqls + * @param {Mixed} value + * @param {String} message _optional_ + * @api public + */ + + function assertEql(obj, msg) { + if (msg) flag(this, 'message', msg); + this.assert( + _.eql(obj, flag(this, 'object')) + , 'expected #{this} to deeply equal #{exp}' + , 'expected #{this} to not deeply equal #{exp}' + , obj + , this._obj + , true + ); + } + + Assertion.addMethod('eql', assertEql); + Assertion.addMethod('eqls', assertEql); + + /** + * ### .above(value) + * + * Asserts that the target is greater than `value`. + * + * expect(10).to.be.above(5); + * + * Can also be used in conjunction with `length` to + * assert a minimum length. The benefit being a + * more informative error message than if the length + * was supplied directly. + * + * expect('foo').to.have.length.above(2); + * expect([ 1, 2, 3 ]).to.have.length.above(2); + * + * @name above + * @alias gt + * @alias greaterThan + * @param {Number} value + * @param {String} message _optional_ + * @api public + */ + + function assertAbove (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + if (flag(this, 'doLength')) { + new Assertion(obj, msg).to.have.property('length'); + var len = obj.length; + this.assert( + len > n + , 'expected #{this} to have a length above #{exp} but got #{act}' + , 'expected #{this} to not have a length above #{exp}' + , n + , len + ); + } else { + this.assert( + obj > n + , 'expected #{this} to be above ' + n + , 'expected #{this} to be at most ' + n + ); + } + } + + Assertion.addMethod('above', assertAbove); + Assertion.addMethod('gt', assertAbove); + Assertion.addMethod('greaterThan', assertAbove); + + /** + * ### .least(value) + * + * Asserts that the target is greater than or equal to `value`. + * + * expect(10).to.be.at.least(10); + * + * Can also be used in conjunction with `length` to + * assert a minimum length. The benefit being a + * more informative error message than if the length + * was supplied directly. + * + * expect('foo').to.have.length.of.at.least(2); + * expect([ 1, 2, 3 ]).to.have.length.of.at.least(3); + * + * @name least + * @alias gte + * @param {Number} value + * @param {String} message _optional_ + * @api public + */ + + function assertLeast (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + if (flag(this, 'doLength')) { + new Assertion(obj, msg).to.have.property('length'); + var len = obj.length; + this.assert( + len >= n + , 'expected #{this} to have a length at least #{exp} but got #{act}' + , 'expected #{this} to have a length below #{exp}' + , n + , len + ); + } else { + this.assert( + obj >= n + , 'expected #{this} to be at least ' + n + , 'expected #{this} to be below ' + n + ); + } + } + + Assertion.addMethod('least', assertLeast); + Assertion.addMethod('gte', assertLeast); + + /** + * ### .below(value) + * + * Asserts that the target is less than `value`. + * + * expect(5).to.be.below(10); + * + * Can also be used in conjunction with `length` to + * assert a maximum length. The benefit being a + * more informative error message than if the length + * was supplied directly. + * + * expect('foo').to.have.length.below(4); + * expect([ 1, 2, 3 ]).to.have.length.below(4); + * + * @name below + * @alias lt + * @alias lessThan + * @param {Number} value + * @param {String} message _optional_ + * @api public + */ + + function assertBelow (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + if (flag(this, 'doLength')) { + new Assertion(obj, msg).to.have.property('length'); + var len = obj.length; + this.assert( + len < n + , 'expected #{this} to have a length below #{exp} but got #{act}' + , 'expected #{this} to not have a length below #{exp}' + , n + , len + ); + } else { + this.assert( + obj < n + , 'expected #{this} to be below ' + n + , 'expected #{this} to be at least ' + n + ); + } + } + + Assertion.addMethod('below', assertBelow); + Assertion.addMethod('lt', assertBelow); + Assertion.addMethod('lessThan', assertBelow); + + /** + * ### .most(value) + * + * Asserts that the target is less than or equal to `value`. + * + * expect(5).to.be.at.most(5); + * + * Can also be used in conjunction with `length` to + * assert a maximum length. The benefit being a + * more informative error message than if the length + * was supplied directly. + * + * expect('foo').to.have.length.of.at.most(4); + * expect([ 1, 2, 3 ]).to.have.length.of.at.most(3); + * + * @name most + * @alias lte + * @param {Number} value + * @param {String} message _optional_ + * @api public + */ + + function assertMost (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + if (flag(this, 'doLength')) { + new Assertion(obj, msg).to.have.property('length'); + var len = obj.length; + this.assert( + len <= n + , 'expected #{this} to have a length at most #{exp} but got #{act}' + , 'expected #{this} to have a length above #{exp}' + , n + , len + ); + } else { + this.assert( + obj <= n + , 'expected #{this} to be at most ' + n + , 'expected #{this} to be above ' + n + ); + } + } + + Assertion.addMethod('most', assertMost); + Assertion.addMethod('lte', assertMost); + + /** + * ### .within(start, finish) + * + * Asserts that the target is within a range. + * + * expect(7).to.be.within(5,10); + * + * Can also be used in conjunction with `length` to + * assert a length range. The benefit being a + * more informative error message than if the length + * was supplied directly. + * + * expect('foo').to.have.length.within(2,4); + * expect([ 1, 2, 3 ]).to.have.length.within(2,4); + * + * @name within + * @param {Number} start lowerbound inclusive + * @param {Number} finish upperbound inclusive + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('within', function (start, finish, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , range = start + '..' + finish; + if (flag(this, 'doLength')) { + new Assertion(obj, msg).to.have.property('length'); + var len = obj.length; + this.assert( + len >= start && len <= finish + , 'expected #{this} to have a length within ' + range + , 'expected #{this} to not have a length within ' + range + ); + } else { + this.assert( + obj >= start && obj <= finish + , 'expected #{this} to be within ' + range + , 'expected #{this} to not be within ' + range + ); + } + }); + + /** + * ### .instanceof(constructor) + * + * Asserts that the target is an instance of `constructor`. + * + * var Tea = function (name) { this.name = name; } + * , Chai = new Tea('chai'); + * + * expect(Chai).to.be.an.instanceof(Tea); + * expect([ 1, 2, 3 ]).to.be.instanceof(Array); + * + * @name instanceof + * @param {Constructor} constructor + * @param {String} message _optional_ + * @alias instanceOf + * @api public + */ + + function assertInstanceOf (constructor, msg) { + if (msg) flag(this, 'message', msg); + var name = _.getName(constructor); + this.assert( + flag(this, 'object') instanceof constructor + , 'expected #{this} to be an instance of ' + name + , 'expected #{this} to not be an instance of ' + name + ); + }; + + Assertion.addMethod('instanceof', assertInstanceOf); + Assertion.addMethod('instanceOf', assertInstanceOf); + + /** + * ### .property(name, [value]) + * + * Asserts that the target has a property `name`, optionally asserting that + * the value of that property is strictly equal to `value`. + * If the `deep` flag is set, you can use dot- and bracket-notation for deep + * references into objects and arrays. + * + * // simple referencing + * var obj = { foo: 'bar' }; + * expect(obj).to.have.property('foo'); + * expect(obj).to.have.property('foo', 'bar'); + * + * // deep referencing + * var deepObj = { + * green: { tea: 'matcha' } + * , teas: [ 'chai', 'matcha', { tea: 'konacha' } ] + * }; + + * expect(deepObj).to.have.deep.property('green.tea', 'matcha'); + * expect(deepObj).to.have.deep.property('teas[1]', 'matcha'); + * expect(deepObj).to.have.deep.property('teas[2].tea', 'konacha'); + * + * You can also use an array as the starting point of a `deep.property` + * assertion, or traverse nested arrays. + * + * var arr = [ + * [ 'chai', 'matcha', 'konacha' ] + * , [ { tea: 'chai' } + * , { tea: 'matcha' } + * , { tea: 'konacha' } ] + * ]; + * + * expect(arr).to.have.deep.property('[0][1]', 'matcha'); + * expect(arr).to.have.deep.property('[1][2].tea', 'konacha'); + * + * Furthermore, `property` changes the subject of the assertion + * to be the value of that property from the original object. This + * permits for further chainable assertions on that property. + * + * expect(obj).to.have.property('foo') + * .that.is.a('string'); + * expect(deepObj).to.have.property('green') + * .that.is.an('object') + * .that.deep.equals({ tea: 'matcha' }); + * expect(deepObj).to.have.property('teas') + * .that.is.an('array') + * .with.deep.property('[2]') + * .that.deep.equals({ tea: 'konacha' }); + * + * @name property + * @alias deep.property + * @param {String} name + * @param {Mixed} value (optional) + * @param {String} message _optional_ + * @returns value of property for chaining + * @api public + */ + + Assertion.addMethod('property', function (name, val, msg) { + if (msg) flag(this, 'message', msg); + + var descriptor = flag(this, 'deep') ? 'deep property ' : 'property ' + , negate = flag(this, 'negate') + , obj = flag(this, 'object') + , value = flag(this, 'deep') + ? _.getPathValue(name, obj) + : obj[name]; + + if (negate && undefined !== val) { + if (undefined === value) { + msg = (msg != null) ? msg + ': ' : ''; + throw new Error(msg + _.inspect(obj) + ' has no ' + descriptor + _.inspect(name)); + } + } else { + this.assert( + undefined !== value + , 'expected #{this} to have a ' + descriptor + _.inspect(name) + , 'expected #{this} to not have ' + descriptor + _.inspect(name)); + } + + if (undefined !== val) { + this.assert( + val === value + , 'expected #{this} to have a ' + descriptor + _.inspect(name) + ' of #{exp}, but got #{act}' + , 'expected #{this} to not have a ' + descriptor + _.inspect(name) + ' of #{act}' + , val + , value + ); + } + + flag(this, 'object', value); + }); + + + /** + * ### .ownProperty(name) + * + * Asserts that the target has an own property `name`. + * + * expect('test').to.have.ownProperty('length'); + * + * @name ownProperty + * @alias haveOwnProperty + * @param {String} name + * @param {String} message _optional_ + * @api public + */ + + function assertOwnProperty (name, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + this.assert( + obj.hasOwnProperty(name) + , 'expected #{this} to have own property ' + _.inspect(name) + , 'expected #{this} to not have own property ' + _.inspect(name) + ); + } + + Assertion.addMethod('ownProperty', assertOwnProperty); + Assertion.addMethod('haveOwnProperty', assertOwnProperty); + + /** + * ### .length(value) + * + * Asserts that the target's `length` property has + * the expected value. + * + * expect([ 1, 2, 3]).to.have.length(3); + * expect('foobar').to.have.length(6); + * + * Can also be used as a chain precursor to a value + * comparison for the length property. + * + * expect('foo').to.have.length.above(2); + * expect([ 1, 2, 3 ]).to.have.length.above(2); + * expect('foo').to.have.length.below(4); + * expect([ 1, 2, 3 ]).to.have.length.below(4); + * expect('foo').to.have.length.within(2,4); + * expect([ 1, 2, 3 ]).to.have.length.within(2,4); + * + * @name length + * @alias lengthOf + * @param {Number} length + * @param {String} message _optional_ + * @api public + */ + + function assertLengthChain () { + flag(this, 'doLength', true); + } + + function assertLength (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + new Assertion(obj, msg).to.have.property('length'); + var len = obj.length; + + this.assert( + len == n + , 'expected #{this} to have a length of #{exp} but got #{act}' + , 'expected #{this} to not have a length of #{act}' + , n + , len + ); + } + + Assertion.addChainableMethod('length', assertLength, assertLengthChain); + Assertion.addMethod('lengthOf', assertLength); + + /** + * ### .match(regexp) + * + * Asserts that the target matches a regular expression. + * + * expect('foobar').to.match(/^foo/); + * + * @name match + * @param {RegExp} RegularExpression + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('match', function (re, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + this.assert( + re.exec(obj) + , 'expected #{this} to match ' + re + , 'expected #{this} not to match ' + re + ); + }); + + /** + * ### .string(string) + * + * Asserts that the string target contains another string. + * + * expect('foobar').to.have.string('bar'); + * + * @name string + * @param {String} string + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('string', function (str, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + new Assertion(obj, msg).is.a('string'); + + this.assert( + ~obj.indexOf(str) + , 'expected #{this} to contain ' + _.inspect(str) + , 'expected #{this} to not contain ' + _.inspect(str) + ); + }); + + + /** + * ### .keys(key1, [key2], [...]) + * + * Asserts that the target has exactly the given keys, or + * asserts the inclusion of some keys when using the + * `include` or `contain` modifiers. + * + * expect({ foo: 1, bar: 2 }).to.have.keys(['foo', 'bar']); + * expect({ foo: 1, bar: 2, baz: 3 }).to.contain.keys('foo', 'bar'); + * + * @name keys + * @alias key + * @param {String...|Array} keys + * @api public + */ + + function assertKeys (keys) { + var obj = flag(this, 'object') + , str + , ok = true; + + keys = keys instanceof Array + ? keys + : Array.prototype.slice.call(arguments); + + if (!keys.length) throw new Error('keys required'); + + var actual = Object.keys(obj) + , expected = keys + , len = keys.length; + + // Inclusion + ok = keys.every(function(key){ + return ~actual.indexOf(key); + }); + + // Strict + if (!flag(this, 'negate') && !flag(this, 'contains')) { + ok = ok && keys.length == actual.length; + } + + // Key string + if (len > 1) { + keys = keys.map(function(key){ + return _.inspect(key); + }); + var last = keys.pop(); + str = keys.join(', ') + ', and ' + last; + } else { + str = _.inspect(keys[0]); + } + + // Form + str = (len > 1 ? 'keys ' : 'key ') + str; + + // Have / include + str = (flag(this, 'contains') ? 'contain ' : 'have ') + str; + + // Assertion + this.assert( + ok + , 'expected #{this} to ' + str + , 'expected #{this} to not ' + str + , expected.sort() + , actual.sort() + , true + ); + } + + Assertion.addMethod('keys', assertKeys); + Assertion.addMethod('key', assertKeys); + + /** + * ### .throw(constructor) + * + * Asserts that the function target will throw a specific error, or specific type of error + * (as determined using `instanceof`), optionally with a RegExp or string inclusion test + * for the error's message. + * + * var err = new ReferenceError('This is a bad function.'); + * var fn = function () { throw err; } + * expect(fn).to.throw(ReferenceError); + * expect(fn).to.throw(Error); + * expect(fn).to.throw(/bad function/); + * expect(fn).to.not.throw('good function'); + * expect(fn).to.throw(ReferenceError, /bad function/); + * expect(fn).to.throw(err); + * expect(fn).to.not.throw(new RangeError('Out of range.')); + * + * Please note that when a throw expectation is negated, it will check each + * parameter independently, starting with error constructor type. The appropriate way + * to check for the existence of a type of error but for a message that does not match + * is to use `and`. + * + * expect(fn).to.throw(ReferenceError) + * .and.not.throw(/good function/); + * + * @name throw + * @alias throws + * @alias Throw + * @param {ErrorConstructor} constructor + * @param {String|RegExp} expected error message + * @param {String} message _optional_ + * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @returns error for chaining (null if no error) + * @api public + */ + + function assertThrows (constructor, errMsg, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + new Assertion(obj, msg).is.a('function'); + + var thrown = false + , desiredError = null + , name = null + , thrownError = null; + + if (arguments.length === 0) { + errMsg = null; + constructor = null; + } else if (constructor && (constructor instanceof RegExp || 'string' === typeof constructor)) { + errMsg = constructor; + constructor = null; + } else if (constructor && constructor instanceof Error) { + desiredError = constructor; + constructor = null; + errMsg = null; + } else if (typeof constructor === 'function') { + name = constructor.prototype.name || constructor.name; + if (name === 'Error' && constructor !== Error) { + name = (new constructor()).name; + } + } else { + constructor = null; + } + + try { + obj(); + } catch (err) { + // first, check desired error + if (desiredError) { + this.assert( + err === desiredError + , 'expected #{this} to throw #{exp} but #{act} was thrown' + , 'expected #{this} to not throw #{exp}' + , (desiredError instanceof Error ? desiredError.toString() : desiredError) + , (err instanceof Error ? err.toString() : err) + ); + + flag(this, 'object', err); + return this; + } + + // next, check constructor + if (constructor) { + this.assert( + err instanceof constructor + , 'expected #{this} to throw #{exp} but #{act} was thrown' + , 'expected #{this} to not throw #{exp} but #{act} was thrown' + , name + , (err instanceof Error ? err.toString() : err) + ); + + if (!errMsg) { + flag(this, 'object', err); + return this; + } + } + + // next, check message + var message = 'object' === _.type(err) && "message" in err + ? err.message + : '' + err; + + if ((message != null) && errMsg && errMsg instanceof RegExp) { + this.assert( + errMsg.exec(message) + , 'expected #{this} to throw error matching #{exp} but got #{act}' + , 'expected #{this} to throw error not matching #{exp}' + , errMsg + , message + ); + + flag(this, 'object', err); + return this; + } else if ((message != null) && errMsg && 'string' === typeof errMsg) { + this.assert( + ~message.indexOf(errMsg) + , 'expected #{this} to throw error including #{exp} but got #{act}' + , 'expected #{this} to throw error not including #{act}' + , errMsg + , message + ); + + flag(this, 'object', err); + return this; + } else { + thrown = true; + thrownError = err; + } + } + + var actuallyGot = '' + , expectedThrown = name !== null + ? name + : desiredError + ? '#{exp}' //_.inspect(desiredError) + : 'an error'; + + if (thrown) { + actuallyGot = ' but #{act} was thrown' + } + + this.assert( + thrown === true + , 'expected #{this} to throw ' + expectedThrown + actuallyGot + , 'expected #{this} to not throw ' + expectedThrown + actuallyGot + , (desiredError instanceof Error ? desiredError.toString() : desiredError) + , (thrownError instanceof Error ? thrownError.toString() : thrownError) + ); + + flag(this, 'object', thrownError); + }; + + Assertion.addMethod('throw', assertThrows); + Assertion.addMethod('throws', assertThrows); + Assertion.addMethod('Throw', assertThrows); + + /** + * ### .respondTo(method) + * + * Asserts that the object or class target will respond to a method. + * + * Klass.prototype.bar = function(){}; + * expect(Klass).to.respondTo('bar'); + * expect(obj).to.respondTo('bar'); + * + * To check if a constructor will respond to a static function, + * set the `itself` flag. + * + * Klass.baz = function(){}; + * expect(Klass).itself.to.respondTo('baz'); + * + * @name respondTo + * @param {String} method + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('respondTo', function (method, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , itself = flag(this, 'itself') + , context = ('function' === _.type(obj) && !itself) + ? obj.prototype[method] + : obj[method]; + + this.assert( + 'function' === typeof context + , 'expected #{this} to respond to ' + _.inspect(method) + , 'expected #{this} to not respond to ' + _.inspect(method) + ); + }); + + /** + * ### .itself + * + * Sets the `itself` flag, later used by the `respondTo` assertion. + * + * function Foo() {} + * Foo.bar = function() {} + * Foo.prototype.baz = function() {} + * + * expect(Foo).itself.to.respondTo('bar'); + * expect(Foo).itself.not.to.respondTo('baz'); + * + * @name itself + * @api public + */ + + Assertion.addProperty('itself', function () { + flag(this, 'itself', true); + }); + + /** + * ### .satisfy(method) + * + * Asserts that the target passes a given truth test. + * + * expect(1).to.satisfy(function(num) { return num > 0; }); + * + * @name satisfy + * @param {Function} matcher + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('satisfy', function (matcher, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + var result = matcher(obj); + this.assert( + result + , 'expected #{this} to satisfy ' + _.objDisplay(matcher) + , 'expected #{this} to not satisfy' + _.objDisplay(matcher) + , this.negate ? false : true + , result + ); + }); + + /** + * ### .closeTo(expected, delta) + * + * Asserts that the target is equal `expected`, to within a +/- `delta` range. + * + * expect(1.5).to.be.closeTo(1, 0.5); + * + * @name closeTo + * @param {Number} expected + * @param {Number} delta + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('closeTo', function (expected, delta, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + + new Assertion(obj, msg).is.a('number'); + if (_.type(expected) !== 'number' || _.type(delta) !== 'number') { + throw new Error('the arguments to closeTo must be numbers'); + } + + this.assert( + Math.abs(obj - expected) <= delta + , 'expected #{this} to be close to ' + expected + ' +/- ' + delta + , 'expected #{this} not to be close to ' + expected + ' +/- ' + delta + ); + }); + + function isSubsetOf(subset, superset, cmp) { + return subset.every(function(elem) { + if (!cmp) return superset.indexOf(elem) !== -1; + + return superset.some(function(elem2) { + return cmp(elem, elem2); + }); + }) + } + + /** + * ### .members(set) + * + * Asserts that the target is a superset of `set`, + * or that the target and `set` have the same strictly-equal (===) members. + * Alternately, if the `deep` flag is set, set members are compared for deep + * equality. + * + * expect([1, 2, 3]).to.include.members([3, 2]); + * expect([1, 2, 3]).to.not.include.members([3, 2, 8]); + * + * expect([4, 2]).to.have.members([2, 4]); + * expect([5, 2]).to.not.have.members([5, 2, 1]); + * + * expect([{ id: 1 }]).to.deep.include.members([{ id: 1 }]); + * + * @name members + * @param {Array} set + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('members', function (subset, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + + new Assertion(obj).to.be.an('array'); + new Assertion(subset).to.be.an('array'); + + var cmp = flag(this, 'deep') ? _.eql : undefined; + + if (flag(this, 'contains')) { + return this.assert( + isSubsetOf(subset, obj, cmp) + , 'expected #{this} to be a superset of #{act}' + , 'expected #{this} to not be a superset of #{act}' + , obj + , subset + ); + } + + this.assert( + isSubsetOf(obj, subset, cmp) && isSubsetOf(subset, obj, cmp) + , 'expected #{this} to have the same members as #{act}' + , 'expected #{this} to not have the same members as #{act}' + , obj + , subset + ); + }); +}; + +}); + +require.register("chai/lib/chai/interface/assert.js", function (exports, module) { +/*! + * chai + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + + +module.exports = function (chai, util) { + + /*! + * Chai dependencies. + */ + + var Assertion = chai.Assertion + , flag = util.flag; + + /*! + * Module export. + */ + + /** + * ### assert(expression, message) + * + * Write your own test expressions. + * + * assert('foo' !== 'bar', 'foo is not bar'); + * assert(Array.isArray([]), 'empty arrays are arrays'); + * + * @param {Mixed} expression to test for truthiness + * @param {String} message to display on error + * @name assert + * @api public + */ + + var assert = chai.assert = function (express, errmsg) { + var test = new Assertion(null, null, chai.assert); + test.assert( + express + , errmsg + , '[ negation message unavailable ]' + ); + }; + + /** + * ### .fail(actual, expected, [message], [operator]) + * + * Throw a failure. Node.js `assert` module-compatible. + * + * @name fail + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @param {String} operator + * @api public + */ + + assert.fail = function (actual, expected, message, operator) { + message = message || 'assert.fail()'; + throw new chai.AssertionError(message, { + actual: actual + , expected: expected + , operator: operator + }, assert.fail); + }; + + /** + * ### .ok(object, [message]) + * + * Asserts that `object` is truthy. + * + * assert.ok('everything', 'everything is ok'); + * assert.ok(false, 'this will fail'); + * + * @name ok + * @param {Mixed} object to test + * @param {String} message + * @api public + */ + + assert.ok = function (val, msg) { + new Assertion(val, msg).is.ok; + }; + + /** + * ### .notOk(object, [message]) + * + * Asserts that `object` is falsy. + * + * assert.notOk('everything', 'this will fail'); + * assert.notOk(false, 'this will pass'); + * + * @name notOk + * @param {Mixed} object to test + * @param {String} message + * @api public + */ + + assert.notOk = function (val, msg) { + new Assertion(val, msg).is.not.ok; + }; + + /** + * ### .equal(actual, expected, [message]) + * + * Asserts non-strict equality (`==`) of `actual` and `expected`. + * + * assert.equal(3, '3', '== coerces values to strings'); + * + * @name equal + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @api public + */ + + assert.equal = function (act, exp, msg) { + var test = new Assertion(act, msg, assert.equal); + + test.assert( + exp == flag(test, 'object') + , 'expected #{this} to equal #{exp}' + , 'expected #{this} to not equal #{act}' + , exp + , act + ); + }; + + /** + * ### .notEqual(actual, expected, [message]) + * + * Asserts non-strict inequality (`!=`) of `actual` and `expected`. + * + * assert.notEqual(3, 4, 'these numbers are not equal'); + * + * @name notEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @api public + */ + + assert.notEqual = function (act, exp, msg) { + var test = new Assertion(act, msg, assert.notEqual); + + test.assert( + exp != flag(test, 'object') + , 'expected #{this} to not equal #{exp}' + , 'expected #{this} to equal #{act}' + , exp + , act + ); + }; + + /** + * ### .strictEqual(actual, expected, [message]) + * + * Asserts strict equality (`===`) of `actual` and `expected`. + * + * assert.strictEqual(true, true, 'these booleans are strictly equal'); + * + * @name strictEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @api public + */ + + assert.strictEqual = function (act, exp, msg) { + new Assertion(act, msg).to.equal(exp); + }; + + /** + * ### .notStrictEqual(actual, expected, [message]) + * + * Asserts strict inequality (`!==`) of `actual` and `expected`. + * + * assert.notStrictEqual(3, '3', 'no coercion for strict equality'); + * + * @name notStrictEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @api public + */ + + assert.notStrictEqual = function (act, exp, msg) { + new Assertion(act, msg).to.not.equal(exp); + }; + + /** + * ### .deepEqual(actual, expected, [message]) + * + * Asserts that `actual` is deeply equal to `expected`. + * + * assert.deepEqual({ tea: 'green' }, { tea: 'green' }); + * + * @name deepEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @api public + */ + + assert.deepEqual = function (act, exp, msg) { + new Assertion(act, msg).to.eql(exp); + }; + + /** + * ### .notDeepEqual(actual, expected, [message]) + * + * Assert that `actual` is not deeply equal to `expected`. + * + * assert.notDeepEqual({ tea: 'green' }, { tea: 'jasmine' }); + * + * @name notDeepEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @api public + */ + + assert.notDeepEqual = function (act, exp, msg) { + new Assertion(act, msg).to.not.eql(exp); + }; + + /** + * ### .isTrue(value, [message]) + * + * Asserts that `value` is true. + * + * var teaServed = true; + * assert.isTrue(teaServed, 'the tea has been served'); + * + * @name isTrue + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isTrue = function (val, msg) { + new Assertion(val, msg).is['true']; + }; + + /** + * ### .isFalse(value, [message]) + * + * Asserts that `value` is false. + * + * var teaServed = false; + * assert.isFalse(teaServed, 'no tea yet? hmm...'); + * + * @name isFalse + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isFalse = function (val, msg) { + new Assertion(val, msg).is['false']; + }; + + /** + * ### .isNull(value, [message]) + * + * Asserts that `value` is null. + * + * assert.isNull(err, 'there was no error'); + * + * @name isNull + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNull = function (val, msg) { + new Assertion(val, msg).to.equal(null); + }; + + /** + * ### .isNotNull(value, [message]) + * + * Asserts that `value` is not null. + * + * var tea = 'tasty chai'; + * assert.isNotNull(tea, 'great, time for tea!'); + * + * @name isNotNull + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotNull = function (val, msg) { + new Assertion(val, msg).to.not.equal(null); + }; + + /** + * ### .isUndefined(value, [message]) + * + * Asserts that `value` is `undefined`. + * + * var tea; + * assert.isUndefined(tea, 'no tea defined'); + * + * @name isUndefined + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isUndefined = function (val, msg) { + new Assertion(val, msg).to.equal(undefined); + }; + + /** + * ### .isDefined(value, [message]) + * + * Asserts that `value` is not `undefined`. + * + * var tea = 'cup of chai'; + * assert.isDefined(tea, 'tea has been defined'); + * + * @name isDefined + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isDefined = function (val, msg) { + new Assertion(val, msg).to.not.equal(undefined); + }; + + /** + * ### .isFunction(value, [message]) + * + * Asserts that `value` is a function. + * + * function serveTea() { return 'cup of tea'; }; + * assert.isFunction(serveTea, 'great, we can have tea now'); + * + * @name isFunction + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isFunction = function (val, msg) { + new Assertion(val, msg).to.be.a('function'); + }; + + /** + * ### .isNotFunction(value, [message]) + * + * Asserts that `value` is _not_ a function. + * + * var serveTea = [ 'heat', 'pour', 'sip' ]; + * assert.isNotFunction(serveTea, 'great, we have listed the steps'); + * + * @name isNotFunction + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotFunction = function (val, msg) { + new Assertion(val, msg).to.not.be.a('function'); + }; + + /** + * ### .isObject(value, [message]) + * + * Asserts that `value` is an object (as revealed by + * `Object.prototype.toString`). + * + * var selection = { name: 'Chai', serve: 'with spices' }; + * assert.isObject(selection, 'tea selection is an object'); + * + * @name isObject + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isObject = function (val, msg) { + new Assertion(val, msg).to.be.a('object'); + }; + + /** + * ### .isNotObject(value, [message]) + * + * Asserts that `value` is _not_ an object. + * + * var selection = 'chai' + * assert.isNotObject(selection, 'tea selection is not an object'); + * assert.isNotObject(null, 'null is not an object'); + * + * @name isNotObject + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotObject = function (val, msg) { + new Assertion(val, msg).to.not.be.a('object'); + }; + + /** + * ### .isArray(value, [message]) + * + * Asserts that `value` is an array. + * + * var menu = [ 'green', 'chai', 'oolong' ]; + * assert.isArray(menu, 'what kind of tea do we want?'); + * + * @name isArray + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isArray = function (val, msg) { + new Assertion(val, msg).to.be.an('array'); + }; + + /** + * ### .isNotArray(value, [message]) + * + * Asserts that `value` is _not_ an array. + * + * var menu = 'green|chai|oolong'; + * assert.isNotArray(menu, 'what kind of tea do we want?'); + * + * @name isNotArray + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotArray = function (val, msg) { + new Assertion(val, msg).to.not.be.an('array'); + }; + + /** + * ### .isString(value, [message]) + * + * Asserts that `value` is a string. + * + * var teaOrder = 'chai'; + * assert.isString(teaOrder, 'order placed'); + * + * @name isString + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isString = function (val, msg) { + new Assertion(val, msg).to.be.a('string'); + }; + + /** + * ### .isNotString(value, [message]) + * + * Asserts that `value` is _not_ a string. + * + * var teaOrder = 4; + * assert.isNotString(teaOrder, 'order placed'); + * + * @name isNotString + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotString = function (val, msg) { + new Assertion(val, msg).to.not.be.a('string'); + }; + + /** + * ### .isNumber(value, [message]) + * + * Asserts that `value` is a number. + * + * var cups = 2; + * assert.isNumber(cups, 'how many cups'); + * + * @name isNumber + * @param {Number} value + * @param {String} message + * @api public + */ + + assert.isNumber = function (val, msg) { + new Assertion(val, msg).to.be.a('number'); + }; + + /** + * ### .isNotNumber(value, [message]) + * + * Asserts that `value` is _not_ a number. + * + * var cups = '2 cups please'; + * assert.isNotNumber(cups, 'how many cups'); + * + * @name isNotNumber + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotNumber = function (val, msg) { + new Assertion(val, msg).to.not.be.a('number'); + }; + + /** + * ### .isBoolean(value, [message]) + * + * Asserts that `value` is a boolean. + * + * var teaReady = true + * , teaServed = false; + * + * assert.isBoolean(teaReady, 'is the tea ready'); + * assert.isBoolean(teaServed, 'has tea been served'); + * + * @name isBoolean + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isBoolean = function (val, msg) { + new Assertion(val, msg).to.be.a('boolean'); + }; + + /** + * ### .isNotBoolean(value, [message]) + * + * Asserts that `value` is _not_ a boolean. + * + * var teaReady = 'yep' + * , teaServed = 'nope'; + * + * assert.isNotBoolean(teaReady, 'is the tea ready'); + * assert.isNotBoolean(teaServed, 'has tea been served'); + * + * @name isNotBoolean + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotBoolean = function (val, msg) { + new Assertion(val, msg).to.not.be.a('boolean'); + }; + + /** + * ### .typeOf(value, name, [message]) + * + * Asserts that `value`'s type is `name`, as determined by + * `Object.prototype.toString`. + * + * assert.typeOf({ tea: 'chai' }, 'object', 'we have an object'); + * assert.typeOf(['chai', 'jasmine'], 'array', 'we have an array'); + * assert.typeOf('tea', 'string', 'we have a string'); + * assert.typeOf(/tea/, 'regexp', 'we have a regular expression'); + * assert.typeOf(null, 'null', 'we have a null'); + * assert.typeOf(undefined, 'undefined', 'we have an undefined'); + * + * @name typeOf + * @param {Mixed} value + * @param {String} name + * @param {String} message + * @api public + */ + + assert.typeOf = function (val, type, msg) { + new Assertion(val, msg).to.be.a(type); + }; + + /** + * ### .notTypeOf(value, name, [message]) + * + * Asserts that `value`'s type is _not_ `name`, as determined by + * `Object.prototype.toString`. + * + * assert.notTypeOf('tea', 'number', 'strings are not numbers'); + * + * @name notTypeOf + * @param {Mixed} value + * @param {String} typeof name + * @param {String} message + * @api public + */ + + assert.notTypeOf = function (val, type, msg) { + new Assertion(val, msg).to.not.be.a(type); + }; + + /** + * ### .instanceOf(object, constructor, [message]) + * + * Asserts that `value` is an instance of `constructor`. + * + * var Tea = function (name) { this.name = name; } + * , chai = new Tea('chai'); + * + * assert.instanceOf(chai, Tea, 'chai is an instance of tea'); + * + * @name instanceOf + * @param {Object} object + * @param {Constructor} constructor + * @param {String} message + * @api public + */ + + assert.instanceOf = function (val, type, msg) { + new Assertion(val, msg).to.be.instanceOf(type); + }; + + /** + * ### .notInstanceOf(object, constructor, [message]) + * + * Asserts `value` is not an instance of `constructor`. + * + * var Tea = function (name) { this.name = name; } + * , chai = new String('chai'); + * + * assert.notInstanceOf(chai, Tea, 'chai is not an instance of tea'); + * + * @name notInstanceOf + * @param {Object} object + * @param {Constructor} constructor + * @param {String} message + * @api public + */ + + assert.notInstanceOf = function (val, type, msg) { + new Assertion(val, msg).to.not.be.instanceOf(type); + }; + + /** + * ### .include(haystack, needle, [message]) + * + * Asserts that `haystack` includes `needle`. Works + * for strings and arrays. + * + * assert.include('foobar', 'bar', 'foobar contains string "bar"'); + * assert.include([ 1, 2, 3 ], 3, 'array contains value'); + * + * @name include + * @param {Array|String} haystack + * @param {Mixed} needle + * @param {String} message + * @api public + */ + + assert.include = function (exp, inc, msg) { + new Assertion(exp, msg, assert.include).include(inc); + }; + + /** + * ### .notInclude(haystack, needle, [message]) + * + * Asserts that `haystack` does not include `needle`. Works + * for strings and arrays. + *i + * assert.notInclude('foobar', 'baz', 'string not include substring'); + * assert.notInclude([ 1, 2, 3 ], 4, 'array not include contain value'); + * + * @name notInclude + * @param {Array|String} haystack + * @param {Mixed} needle + * @param {String} message + * @api public + */ + + assert.notInclude = function (exp, inc, msg) { + new Assertion(exp, msg, assert.notInclude).not.include(inc); + }; + + /** + * ### .match(value, regexp, [message]) + * + * Asserts that `value` matches the regular expression `regexp`. + * + * assert.match('foobar', /^foo/, 'regexp matches'); + * + * @name match + * @param {Mixed} value + * @param {RegExp} regexp + * @param {String} message + * @api public + */ + + assert.match = function (exp, re, msg) { + new Assertion(exp, msg).to.match(re); + }; + + /** + * ### .notMatch(value, regexp, [message]) + * + * Asserts that `value` does not match the regular expression `regexp`. + * + * assert.notMatch('foobar', /^foo/, 'regexp does not match'); + * + * @name notMatch + * @param {Mixed} value + * @param {RegExp} regexp + * @param {String} message + * @api public + */ + + assert.notMatch = function (exp, re, msg) { + new Assertion(exp, msg).to.not.match(re); + }; + + /** + * ### .property(object, property, [message]) + * + * Asserts that `object` has a property named by `property`. + * + * assert.property({ tea: { green: 'matcha' }}, 'tea'); + * + * @name property + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.property = function (obj, prop, msg) { + new Assertion(obj, msg).to.have.property(prop); + }; + + /** + * ### .notProperty(object, property, [message]) + * + * Asserts that `object` does _not_ have a property named by `property`. + * + * assert.notProperty({ tea: { green: 'matcha' }}, 'coffee'); + * + * @name notProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.notProperty = function (obj, prop, msg) { + new Assertion(obj, msg).to.not.have.property(prop); + }; + + /** + * ### .deepProperty(object, property, [message]) + * + * Asserts that `object` has a property named by `property`, which can be a + * string using dot- and bracket-notation for deep reference. + * + * assert.deepProperty({ tea: { green: 'matcha' }}, 'tea.green'); + * + * @name deepProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.deepProperty = function (obj, prop, msg) { + new Assertion(obj, msg).to.have.deep.property(prop); + }; + + /** + * ### .notDeepProperty(object, property, [message]) + * + * Asserts that `object` does _not_ have a property named by `property`, which + * can be a string using dot- and bracket-notation for deep reference. + * + * assert.notDeepProperty({ tea: { green: 'matcha' }}, 'tea.oolong'); + * + * @name notDeepProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.notDeepProperty = function (obj, prop, msg) { + new Assertion(obj, msg).to.not.have.deep.property(prop); + }; + + /** + * ### .propertyVal(object, property, value, [message]) + * + * Asserts that `object` has a property named by `property` with value given + * by `value`. + * + * assert.propertyVal({ tea: 'is good' }, 'tea', 'is good'); + * + * @name propertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.propertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg).to.have.property(prop, val); + }; + + /** + * ### .propertyNotVal(object, property, value, [message]) + * + * Asserts that `object` has a property named by `property`, but with a value + * different from that given by `value`. + * + * assert.propertyNotVal({ tea: 'is good' }, 'tea', 'is bad'); + * + * @name propertyNotVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.propertyNotVal = function (obj, prop, val, msg) { + new Assertion(obj, msg).to.not.have.property(prop, val); + }; + + /** + * ### .deepPropertyVal(object, property, value, [message]) + * + * Asserts that `object` has a property named by `property` with value given + * by `value`. `property` can use dot- and bracket-notation for deep + * reference. + * + * assert.deepPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'matcha'); + * + * @name deepPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.deepPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg).to.have.deep.property(prop, val); + }; + + /** + * ### .deepPropertyNotVal(object, property, value, [message]) + * + * Asserts that `object` has a property named by `property`, but with a value + * different from that given by `value`. `property` can use dot- and + * bracket-notation for deep reference. + * + * assert.deepPropertyNotVal({ tea: { green: 'matcha' }}, 'tea.green', 'konacha'); + * + * @name deepPropertyNotVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.deepPropertyNotVal = function (obj, prop, val, msg) { + new Assertion(obj, msg).to.not.have.deep.property(prop, val); + }; + + /** + * ### .lengthOf(object, length, [message]) + * + * Asserts that `object` has a `length` property with the expected value. + * + * assert.lengthOf([1,2,3], 3, 'array has length of 3'); + * assert.lengthOf('foobar', 5, 'string has length of 6'); + * + * @name lengthOf + * @param {Mixed} object + * @param {Number} length + * @param {String} message + * @api public + */ + + assert.lengthOf = function (exp, len, msg) { + new Assertion(exp, msg).to.have.length(len); + }; + + /** + * ### .throws(function, [constructor/string/regexp], [string/regexp], [message]) + * + * Asserts that `function` will throw an error that is an instance of + * `constructor`, or alternately that it will throw an error with message + * matching `regexp`. + * + * assert.throw(fn, 'function throws a reference error'); + * assert.throw(fn, /function throws a reference error/); + * assert.throw(fn, ReferenceError); + * assert.throw(fn, ReferenceError, 'function throws a reference error'); + * assert.throw(fn, ReferenceError, /function throws a reference error/); + * + * @name throws + * @alias throw + * @alias Throw + * @param {Function} function + * @param {ErrorConstructor} constructor + * @param {RegExp} regexp + * @param {String} message + * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @api public + */ + + assert.Throw = function (fn, errt, errs, msg) { + if ('string' === typeof errt || errt instanceof RegExp) { + errs = errt; + errt = null; + } + + var assertErr = new Assertion(fn, msg).to.Throw(errt, errs); + return flag(assertErr, 'object'); + }; + + /** + * ### .doesNotThrow(function, [constructor/regexp], [message]) + * + * Asserts that `function` will _not_ throw an error that is an instance of + * `constructor`, or alternately that it will not throw an error with message + * matching `regexp`. + * + * assert.doesNotThrow(fn, Error, 'function does not throw'); + * + * @name doesNotThrow + * @param {Function} function + * @param {ErrorConstructor} constructor + * @param {RegExp} regexp + * @param {String} message + * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @api public + */ + + assert.doesNotThrow = function (fn, type, msg) { + if ('string' === typeof type) { + msg = type; + type = null; + } + + new Assertion(fn, msg).to.not.Throw(type); + }; + + /** + * ### .operator(val1, operator, val2, [message]) + * + * Compares two values using `operator`. + * + * assert.operator(1, '<', 2, 'everything is ok'); + * assert.operator(1, '>', 2, 'this will fail'); + * + * @name operator + * @param {Mixed} val1 + * @param {String} operator + * @param {Mixed} val2 + * @param {String} message + * @api public + */ + + assert.operator = function (val, operator, val2, msg) { + if (!~['==', '===', '>', '>=', '<', '<=', '!=', '!=='].indexOf(operator)) { + throw new Error('Invalid operator "' + operator + '"'); + } + var test = new Assertion(eval(val + operator + val2), msg); + test.assert( + true === flag(test, 'object') + , 'expected ' + util.inspect(val) + ' to be ' + operator + ' ' + util.inspect(val2) + , 'expected ' + util.inspect(val) + ' to not be ' + operator + ' ' + util.inspect(val2) ); + }; + + /** + * ### .closeTo(actual, expected, delta, [message]) + * + * Asserts that the target is equal `expected`, to within a +/- `delta` range. + * + * assert.closeTo(1.5, 1, 0.5, 'numbers are close'); + * + * @name closeTo + * @param {Number} actual + * @param {Number} expected + * @param {Number} delta + * @param {String} message + * @api public + */ + + assert.closeTo = function (act, exp, delta, msg) { + new Assertion(act, msg).to.be.closeTo(exp, delta); + }; + + /** + * ### .sameMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` have the same members. + * Order is not taken into account. + * + * assert.sameMembers([ 1, 2, 3 ], [ 2, 1, 3 ], 'same members'); + * + * @name sameMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @api public + */ + + assert.sameMembers = function (set1, set2, msg) { + new Assertion(set1, msg).to.have.same.members(set2); + } + + /** + * ### .includeMembers(superset, subset, [message]) + * + * Asserts that `subset` is included in `superset`. + * Order is not taken into account. + * + * assert.includeMembers([ 1, 2, 3 ], [ 2, 1 ], 'include members'); + * + * @name includeMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @api public + */ + + assert.includeMembers = function (superset, subset, msg) { + new Assertion(superset, msg).to.include.members(subset); + } + + /*! + * Undocumented / untested + */ + + assert.ifError = function (val, msg) { + new Assertion(val, msg).to.not.be.ok; + }; + + /*! + * Aliases. + */ + + (function alias(name, as){ + assert[as] = assert[name]; + return alias; + }) + ('Throw', 'throw') + ('Throw', 'throws'); +}; + +}); + +require.register("chai/lib/chai/interface/expect.js", function (exports, module) { +/*! + * chai + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +module.exports = function (chai, util) { + chai.expect = function (val, message) { + return new chai.Assertion(val, message); + }; +}; + + +}); + +require.register("chai/lib/chai/interface/should.js", function (exports, module) { +/*! + * chai + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +module.exports = function (chai, util) { + var Assertion = chai.Assertion; + + function loadShould () { + // explicitly define this method as function as to have it's name to include as `ssfi` + function shouldGetter() { + if (this instanceof String || this instanceof Number) { + return new Assertion(this.constructor(this), null, shouldGetter); + } else if (this instanceof Boolean) { + return new Assertion(this == true, null, shouldGetter); + } + return new Assertion(this, null, shouldGetter); + } + function shouldSetter(value) { + // See https://github.com/chaijs/chai/issues/86: this makes + // `whatever.should = someValue` actually set `someValue`, which is + // especially useful for `global.should = require('chai').should()`. + // + // Note that we have to use [[DefineProperty]] instead of [[Put]] + // since otherwise we would trigger this very setter! + Object.defineProperty(this, 'should', { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } + // modify Object.prototype to have `should` + Object.defineProperty(Object.prototype, 'should', { + set: shouldSetter + , get: shouldGetter + , configurable: true + }); + + var should = {}; + + should.equal = function (val1, val2, msg) { + new Assertion(val1, msg).to.equal(val2); + }; + + should.Throw = function (fn, errt, errs, msg) { + new Assertion(fn, msg).to.Throw(errt, errs); + }; + + should.exist = function (val, msg) { + new Assertion(val, msg).to.exist; + } + + // negation + should.not = {} + + should.not.equal = function (val1, val2, msg) { + new Assertion(val1, msg).to.not.equal(val2); + }; + + should.not.Throw = function (fn, errt, errs, msg) { + new Assertion(fn, msg).to.not.Throw(errt, errs); + }; + + should.not.exist = function (val, msg) { + new Assertion(val, msg).to.not.exist; + } + + should['throw'] = should['Throw']; + should.not['throw'] = should.not['Throw']; + + return should; + }; + + chai.should = loadShould; + chai.Should = loadShould; +}; + +}); + +require.register("chai/lib/chai/utils/addChainableMethod.js", function (exports, module) { +/*! + * Chai - addChainingMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependencies + */ + +var transferFlags = require('chai/lib/chai/utils/transferFlags.js'); +var flag = require('chai/lib/chai/utils/flag.js'); +var config = require('chai/lib/chai/config.js'); + +/*! + * Module variables + */ + +// Check whether `__proto__` is supported +var hasProtoSupport = '__proto__' in Object; + +// Without `__proto__` support, this module will need to add properties to a function. +// However, some Function.prototype methods cannot be overwritten, +// and there seems no easy cross-platform way to detect them (@see chaijs/chai/issues/69). +var excludeNames = /^(?:length|name|arguments|caller)$/; + +// Cache `Function` properties +var call = Function.prototype.call, + apply = Function.prototype.apply; + +/** + * ### addChainableMethod (ctx, name, method, chainingBehavior) + * + * Adds a method to an object, such that the method can also be chained. + * + * utils.addChainableMethod(chai.Assertion.prototype, 'foo', function (str) { + * var obj = utils.flag(this, 'object'); + * new chai.Assertion(obj).to.be.equal(str); + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.addChainableMethod('foo', fn, chainingBehavior); + * + * The result can then be used as both a method assertion, executing both `method` and + * `chainingBehavior`, or as a language chain, which only executes `chainingBehavior`. + * + * expect(fooStr).to.be.foo('bar'); + * expect(fooStr).to.be.foo.equal('foo'); + * + * @param {Object} ctx object to which the method is added + * @param {String} name of method to add + * @param {Function} method function to be used for `name`, when called + * @param {Function} chainingBehavior function to be called every time the property is accessed + * @name addChainableMethod + * @api public + */ + +module.exports = function (ctx, name, method, chainingBehavior) { + if (typeof chainingBehavior !== 'function') { + chainingBehavior = function () { }; + } + + var chainableBehavior = { + method: method + , chainingBehavior: chainingBehavior + }; + + // save the methods so we can overwrite them later, if we need to. + if (!ctx.__methods) { + ctx.__methods = {}; + } + ctx.__methods[name] = chainableBehavior; + + Object.defineProperty(ctx, name, + { get: function () { + chainableBehavior.chainingBehavior.call(this); + + var assert = function assert() { + var old_ssfi = flag(this, 'ssfi'); + if (old_ssfi && config.includeStack === false) + flag(this, 'ssfi', assert); + var result = chainableBehavior.method.apply(this, arguments); + return result === undefined ? this : result; + }; + + // Use `__proto__` if available + if (hasProtoSupport) { + // Inherit all properties from the object by replacing the `Function` prototype + var prototype = assert.__proto__ = Object.create(this); + // Restore the `call` and `apply` methods from `Function` + prototype.call = call; + prototype.apply = apply; + } + // Otherwise, redefine all properties (slow!) + else { + var asserterNames = Object.getOwnPropertyNames(ctx); + asserterNames.forEach(function (asserterName) { + if (!excludeNames.test(asserterName)) { + var pd = Object.getOwnPropertyDescriptor(ctx, asserterName); + Object.defineProperty(assert, asserterName, pd); + } + }); + } + + transferFlags(this, assert); + return assert; + } + , configurable: true + }); +}; + +}); + +require.register("chai/lib/chai/utils/addMethod.js", function (exports, module) { +/*! + * Chai - addMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +var config = require('chai/lib/chai/config.js'); + +/** + * ### .addMethod (ctx, name, method) + * + * Adds a method to the prototype of an object. + * + * utils.addMethod(chai.Assertion.prototype, 'foo', function (str) { + * var obj = utils.flag(this, 'object'); + * new chai.Assertion(obj).to.be.equal(str); + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.addMethod('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(fooStr).to.be.foo('bar'); + * + * @param {Object} ctx object to which the method is added + * @param {String} name of method to add + * @param {Function} method function to be used for name + * @name addMethod + * @api public + */ +var flag = require('chai/lib/chai/utils/flag.js'); + +module.exports = function (ctx, name, method) { + ctx[name] = function () { + var old_ssfi = flag(this, 'ssfi'); + if (old_ssfi && config.includeStack === false) + flag(this, 'ssfi', ctx[name]); + var result = method.apply(this, arguments); + return result === undefined ? this : result; + }; +}; + +}); + +require.register("chai/lib/chai/utils/addProperty.js", function (exports, module) { +/*! + * Chai - addProperty utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### addProperty (ctx, name, getter) + * + * Adds a property to the prototype of an object. + * + * utils.addProperty(chai.Assertion.prototype, 'foo', function () { + * var obj = utils.flag(this, 'object'); + * new chai.Assertion(obj).to.be.instanceof(Foo); + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.addProperty('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.be.foo; + * + * @param {Object} ctx object to which the property is added + * @param {String} name of property to add + * @param {Function} getter function to be used for name + * @name addProperty + * @api public + */ + +module.exports = function (ctx, name, getter) { + Object.defineProperty(ctx, name, + { get: function () { + var result = getter.call(this); + return result === undefined ? this : result; + } + , configurable: true + }); +}; + +}); + +require.register("chai/lib/chai/utils/flag.js", function (exports, module) { +/*! + * Chai - flag utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### flag(object ,key, [value]) + * + * Get or set a flag value on an object. If a + * value is provided it will be set, else it will + * return the currently set value or `undefined` if + * the value is not set. + * + * utils.flag(this, 'foo', 'bar'); // setter + * utils.flag(this, 'foo'); // getter, returns `bar` + * + * @param {Object} object (constructed Assertion + * @param {String} key + * @param {Mixed} value (optional) + * @name flag + * @api private + */ + +module.exports = function (obj, key, value) { + var flags = obj.__flags || (obj.__flags = Object.create(null)); + if (arguments.length === 3) { + flags[key] = value; + } else { + return flags[key]; + } +}; + +}); + +require.register("chai/lib/chai/utils/getActual.js", function (exports, module) { +/*! + * Chai - getActual utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * # getActual(object, [actual]) + * + * Returns the `actual` value for an Assertion + * + * @param {Object} object (constructed Assertion) + * @param {Arguments} chai.Assertion.prototype.assert arguments + */ + +module.exports = function (obj, args) { + return args.length > 4 ? args[4] : obj._obj; +}; + +}); + +require.register("chai/lib/chai/utils/getEnumerableProperties.js", function (exports, module) { +/*! + * Chai - getEnumerableProperties utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .getEnumerableProperties(object) + * + * This allows the retrieval of enumerable property names of an object, + * inherited or not. + * + * @param {Object} object + * @returns {Array} + * @name getEnumerableProperties + * @api public + */ + +module.exports = function getEnumerableProperties(object) { + var result = []; + for (var name in object) { + result.push(name); + } + return result; +}; + +}); + +require.register("chai/lib/chai/utils/getMessage.js", function (exports, module) { +/*! + * Chai - message composition utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependancies + */ + +var flag = require('chai/lib/chai/utils/flag.js') + , getActual = require('chai/lib/chai/utils/getActual.js') + , inspect = require('chai/lib/chai/utils/inspect.js') + , objDisplay = require('chai/lib/chai/utils/objDisplay.js'); + +/** + * ### .getMessage(object, message, negateMessage) + * + * Construct the error message based on flags + * and template tags. Template tags will return + * a stringified inspection of the object referenced. + * + * Message template tags: + * - `#{this}` current asserted object + * - `#{act}` actual value + * - `#{exp}` expected value + * + * @param {Object} object (constructed Assertion) + * @param {Arguments} chai.Assertion.prototype.assert arguments + * @name getMessage + * @api public + */ + +module.exports = function (obj, args) { + var negate = flag(obj, 'negate') + , val = flag(obj, 'object') + , expected = args[3] + , actual = getActual(obj, args) + , msg = negate ? args[2] : args[1] + , flagMsg = flag(obj, 'message'); + + if(typeof msg === "function") msg = msg(); + msg = msg || ''; + msg = msg + .replace(/#{this}/g, objDisplay(val)) + .replace(/#{act}/g, objDisplay(actual)) + .replace(/#{exp}/g, objDisplay(expected)); + + return flagMsg ? flagMsg + ': ' + msg : msg; +}; + +}); + +require.register("chai/lib/chai/utils/getName.js", function (exports, module) { +/*! + * Chai - getName utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * # getName(func) + * + * Gets the name of a function, in a cross-browser way. + * + * @param {Function} a function (usually a constructor) + */ + +module.exports = function (func) { + if (func.name) return func.name; + + var match = /^\s?function ([^(]*)\(/.exec(func); + return match && match[1] ? match[1] : ""; +}; + +}); + +require.register("chai/lib/chai/utils/getPathValue.js", function (exports, module) { +/*! + * Chai - getPathValue utility + * Copyright(c) 2012-2014 Jake Luer + * @see https://github.com/logicalparadox/filtr + * MIT Licensed + */ + +/** + * ### .getPathValue(path, object) + * + * This allows the retrieval of values in an + * object given a string path. + * + * var obj = { + * prop1: { + * arr: ['a', 'b', 'c'] + * , str: 'Hello' + * } + * , prop2: { + * arr: [ { nested: 'Universe' } ] + * , str: 'Hello again!' + * } + * } + * + * The following would be the results. + * + * getPathValue('prop1.str', obj); // Hello + * getPathValue('prop1.att[2]', obj); // b + * getPathValue('prop2.arr[0].nested', obj); // Universe + * + * @param {String} path + * @param {Object} object + * @returns {Object} value or `undefined` + * @name getPathValue + * @api public + */ + +var getPathValue = module.exports = function (path, obj) { + var parsed = parsePath(path); + return _getPathValue(parsed, obj); +}; + +/*! + * ## parsePath(path) + * + * Helper function used to parse string object + * paths. Use in conjunction with `_getPathValue`. + * + * var parsed = parsePath('myobject.property.subprop'); + * + * ### Paths: + * + * * Can be as near infinitely deep and nested + * * Arrays are also valid using the formal `myobject.document[3].property`. + * + * @param {String} path + * @returns {Object} parsed + * @api private + */ + +function parsePath (path) { + var str = path.replace(/\[/g, '.[') + , parts = str.match(/(\\\.|[^.]+?)+/g); + return parts.map(function (value) { + var re = /\[(\d+)\]$/ + , mArr = re.exec(value) + if (mArr) return { i: parseFloat(mArr[1]) }; + else return { p: value }; + }); +}; + +/*! + * ## _getPathValue(parsed, obj) + * + * Helper companion function for `.parsePath` that returns + * the value located at the parsed address. + * + * var value = getPathValue(parsed, obj); + * + * @param {Object} parsed definition from `parsePath`. + * @param {Object} object to search against + * @returns {Object|Undefined} value + * @api private + */ + +function _getPathValue (parsed, obj) { + var tmp = obj + , res; + for (var i = 0, l = parsed.length; i < l; i++) { + var part = parsed[i]; + if (tmp) { + if ('undefined' !== typeof part.p) + tmp = tmp[part.p]; + else if ('undefined' !== typeof part.i) + tmp = tmp[part.i]; + if (i == (l - 1)) res = tmp; + } else { + res = undefined; + } + } + return res; +}; + +}); + +require.register("chai/lib/chai/utils/getProperties.js", function (exports, module) { +/*! + * Chai - getProperties utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .getProperties(object) + * + * This allows the retrieval of property names of an object, enumerable or not, + * inherited or not. + * + * @param {Object} object + * @returns {Array} + * @name getProperties + * @api public + */ + +module.exports = function getProperties(object) { + var result = Object.getOwnPropertyNames(subject); + + function addProperty(property) { + if (result.indexOf(property) === -1) { + result.push(property); + } + } + + var proto = Object.getPrototypeOf(subject); + while (proto !== null) { + Object.getOwnPropertyNames(proto).forEach(addProperty); + proto = Object.getPrototypeOf(proto); + } + + return result; +}; + +}); + +require.register("chai/lib/chai/utils/index.js", function (exports, module) { +/*! + * chai + * Copyright(c) 2011 Jake Luer + * MIT Licensed + */ + +/*! + * Main exports + */ + +var exports = module.exports = {}; + +/*! + * test utility + */ + +exports.test = require('chai/lib/chai/utils/test.js'); + +/*! + * type utility + */ + +exports.type = require('chai/lib/chai/utils/type.js'); + +/*! + * message utility + */ + +exports.getMessage = require('chai/lib/chai/utils/getMessage.js'); + +/*! + * actual utility + */ + +exports.getActual = require('chai/lib/chai/utils/getActual.js'); + +/*! + * Inspect util + */ + +exports.inspect = require('chai/lib/chai/utils/inspect.js'); + +/*! + * Object Display util + */ + +exports.objDisplay = require('chai/lib/chai/utils/objDisplay.js'); + +/*! + * Flag utility + */ + +exports.flag = require('chai/lib/chai/utils/flag.js'); + +/*! + * Flag transferring utility + */ + +exports.transferFlags = require('chai/lib/chai/utils/transferFlags.js'); + +/*! + * Deep equal utility + */ + +exports.eql = require('chaijs~deep-eql@0.1.3'); + +/*! + * Deep path value + */ + +exports.getPathValue = require('chai/lib/chai/utils/getPathValue.js'); + +/*! + * Function name + */ + +exports.getName = require('chai/lib/chai/utils/getName.js'); + +/*! + * add Property + */ + +exports.addProperty = require('chai/lib/chai/utils/addProperty.js'); + +/*! + * add Method + */ + +exports.addMethod = require('chai/lib/chai/utils/addMethod.js'); + +/*! + * overwrite Property + */ + +exports.overwriteProperty = require('chai/lib/chai/utils/overwriteProperty.js'); + +/*! + * overwrite Method + */ + +exports.overwriteMethod = require('chai/lib/chai/utils/overwriteMethod.js'); + +/*! + * Add a chainable method + */ + +exports.addChainableMethod = require('chai/lib/chai/utils/addChainableMethod.js'); + +/*! + * Overwrite chainable method + */ + +exports.overwriteChainableMethod = require('chai/lib/chai/utils/overwriteChainableMethod.js'); + + +}); + +require.register("chai/lib/chai/utils/inspect.js", function (exports, module) { +// This is (almost) directly from Node.js utils +// https://github.com/joyent/node/blob/f8c335d0caf47f16d31413f89aa28eda3878e3aa/lib/util.js + +var getName = require('chai/lib/chai/utils/getName.js'); +var getProperties = require('chai/lib/chai/utils/getProperties.js'); +var getEnumerableProperties = require('chai/lib/chai/utils/getEnumerableProperties.js'); + +module.exports = inspect; + +/** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Boolean} showHidden Flag that shows hidden (not enumerable) + * properties of objects. + * @param {Number} depth Depth in which to descend in object. Default is 2. + * @param {Boolean} colors Flag to turn on ANSI escape codes to color the + * output. Default is false (no coloring). + */ +function inspect(obj, showHidden, depth, colors) { + var ctx = { + showHidden: showHidden, + seen: [], + stylize: function (str) { return str; } + }; + return formatValue(ctx, obj, (typeof depth === 'undefined' ? 2 : depth)); +} + +// Returns true if object is a DOM element. +var isDOMElement = function (object) { + if (typeof HTMLElement === 'object') { + return object instanceof HTMLElement; + } else { + return object && + typeof object === 'object' && + object.nodeType === 1 && + typeof object.nodeName === 'string'; + } +}; + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (value && typeof value.inspect === 'function' && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes); + if (typeof ret !== 'string') { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // If this is a DOM element, try to get the outer HTML. + if (isDOMElement(value)) { + if ('outerHTML' in value) { + return value.outerHTML; + // This value does not have an outerHTML attribute, + // it could still be an XML element + } else { + // Attempt to serialize it + try { + if (document.xmlVersion) { + var xmlSerializer = new XMLSerializer(); + return xmlSerializer.serializeToString(value); + } else { + // Firefox 11- do not support outerHTML + // It does, however, support innerHTML + // Use the following to render the element + var ns = "http://www.w3.org/1999/xhtml"; + var container = document.createElementNS(ns, '_'); + + container.appendChild(value.cloneNode(false)); + html = container.innerHTML + .replace('><', '>' + value.innerHTML + '<'); + container.innerHTML = ''; + return html; + } + } catch (err) { + // This could be a non-native DOM implementation, + // continue with the normal flow: + // printing the element as if it is an object. + } + } + } + + // Look up the keys of the object. + var visibleKeys = getEnumerableProperties(value); + var keys = ctx.showHidden ? getProperties(value) : visibleKeys; + + // Some type of object without properties can be shortcutted. + // In IE, errors have a single `stack` property, or if they are vanilla `Error`, + // a `stack` plus `description` property; ignore those for consistency. + if (keys.length === 0 || (isError(value) && ( + (keys.length === 1 && keys[0] === 'stack') || + (keys.length === 2 && keys[0] === 'description' && keys[1] === 'stack') + ))) { + if (typeof value === 'function') { + var name = getName(value); + var nameSuffix = name ? ': ' + name : ''; + return ctx.stylize('[Function' + nameSuffix + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toUTCString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (typeof value === 'function') { + var name = getName(value); + var nameSuffix = name ? ': ' + name : ''; + base = ' [Function' + nameSuffix + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + return formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + + +function formatPrimitive(ctx, value) { + switch (typeof value) { + case 'undefined': + return ctx.stylize('undefined', 'undefined'); + + case 'string': + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + + case 'number': + if (value === 0 && (1/value) === -Infinity) { + return ctx.stylize('-0', 'number'); + } + return ctx.stylize('' + value, 'number'); + + case 'boolean': + return ctx.stylize('' + value, 'boolean'); + } + // For some reason typeof null is "object", so special case here. + if (value === null) { + return ctx.stylize('null', 'null'); + } +} + + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (Object.prototype.hasOwnProperty.call(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str; + if (value.__lookupGetter__) { + if (value.__lookupGetter__(key)) { + if (value.__lookupSetter__(key)) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (value.__lookupSetter__(key)) { + str = ctx.stylize('[Setter]', 'special'); + } + } + } + if (visibleKeys.indexOf(key) < 0) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(value[key]) < 0) { + if (recurseTimes === null) { + str = formatValue(ctx, value[key], null); + } else { + str = formatValue(ctx, value[key], recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (typeof name === 'undefined') { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + +function isArray(ar) { + return Array.isArray(ar) || + (typeof ar === 'object' && objectToString(ar) === '[object Array]'); +} + +function isRegExp(re) { + return typeof re === 'object' && objectToString(re) === '[object RegExp]'; +} + +function isDate(d) { + return typeof d === 'object' && objectToString(d) === '[object Date]'; +} + +function isError(e) { + return typeof e === 'object' && objectToString(e) === '[object Error]'; +} + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + +}); + +require.register("chai/lib/chai/utils/objDisplay.js", function (exports, module) { +/*! + * Chai - flag utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependancies + */ + +var inspect = require('chai/lib/chai/utils/inspect.js'); +var config = require('chai/lib/chai/config.js'); + +/** + * ### .objDisplay (object) + * + * Determines if an object or an array matches + * criteria to be inspected in-line for error + * messages or should be truncated. + * + * @param {Mixed} javascript object to inspect + * @name objDisplay + * @api public + */ + +module.exports = function (obj) { + var str = inspect(obj) + , type = Object.prototype.toString.call(obj); + + if (config.truncateThreshold && str.length >= config.truncateThreshold) { + if (type === '[object Function]') { + return !obj.name || obj.name === '' + ? '[Function]' + : '[Function: ' + obj.name + ']'; + } else if (type === '[object Array]') { + return '[ Array(' + obj.length + ') ]'; + } else if (type === '[object Object]') { + var keys = Object.keys(obj) + , kstr = keys.length > 2 + ? keys.splice(0, 2).join(', ') + ', ...' + : keys.join(', '); + return '{ Object (' + kstr + ') }'; + } else { + return str; + } + } else { + return str; + } +}; + +}); + +require.register("chai/lib/chai/utils/overwriteMethod.js", function (exports, module) { +/*! + * Chai - overwriteMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### overwriteMethod (ctx, name, fn) + * + * Overwites an already existing method and provides + * access to previous function. Must return function + * to be used for name. + * + * utils.overwriteMethod(chai.Assertion.prototype, 'equal', function (_super) { + * return function (str) { + * var obj = utils.flag(this, 'object'); + * if (obj instanceof Foo) { + * new chai.Assertion(obj.value).to.equal(str); + * } else { + * _super.apply(this, arguments); + * } + * } + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.overwriteMethod('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.equal('bar'); + * + * @param {Object} ctx object whose method is to be overwritten + * @param {String} name of method to overwrite + * @param {Function} method function that returns a function to be used for name + * @name overwriteMethod + * @api public + */ + +module.exports = function (ctx, name, method) { + var _method = ctx[name] + , _super = function () { return this; }; + + if (_method && 'function' === typeof _method) + _super = _method; + + ctx[name] = function () { + var result = method(_super).apply(this, arguments); + return result === undefined ? this : result; + } +}; + +}); + +require.register("chai/lib/chai/utils/overwriteProperty.js", function (exports, module) { +/*! + * Chai - overwriteProperty utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### overwriteProperty (ctx, name, fn) + * + * Overwites an already existing property getter and provides + * access to previous value. Must return function to use as getter. + * + * utils.overwriteProperty(chai.Assertion.prototype, 'ok', function (_super) { + * return function () { + * var obj = utils.flag(this, 'object'); + * if (obj instanceof Foo) { + * new chai.Assertion(obj.name).to.equal('bar'); + * } else { + * _super.call(this); + * } + * } + * }); + * + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.overwriteProperty('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.be.ok; + * + * @param {Object} ctx object whose property is to be overwritten + * @param {String} name of property to overwrite + * @param {Function} getter function that returns a getter function to be used for name + * @name overwriteProperty + * @api public + */ + +module.exports = function (ctx, name, getter) { + var _get = Object.getOwnPropertyDescriptor(ctx, name) + , _super = function () {}; + + if (_get && 'function' === typeof _get.get) + _super = _get.get + + Object.defineProperty(ctx, name, + { get: function () { + var result = getter(_super).call(this); + return result === undefined ? this : result; + } + , configurable: true + }); +}; + +}); + +require.register("chai/lib/chai/utils/overwriteChainableMethod.js", function (exports, module) { +/*! + * Chai - overwriteChainableMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### overwriteChainableMethod (ctx, name, fn) + * + * Overwites an already existing chainable method + * and provides access to the previous function or + * property. Must return functions to be used for + * name. + * + * utils.overwriteChainableMethod(chai.Assertion.prototype, 'length', + * function (_super) { + * } + * , function (_super) { + * } + * ); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.overwriteChainableMethod('foo', fn, fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.have.length(3); + * expect(myFoo).to.have.length.above(3); + * + * @param {Object} ctx object whose method / property is to be overwritten + * @param {String} name of method / property to overwrite + * @param {Function} method function that returns a function to be used for name + * @param {Function} chainingBehavior function that returns a function to be used for property + * @name overwriteChainableMethod + * @api public + */ + +module.exports = function (ctx, name, method, chainingBehavior) { + var chainableBehavior = ctx.__methods[name]; + + var _chainingBehavior = chainableBehavior.chainingBehavior; + chainableBehavior.chainingBehavior = function () { + var result = chainingBehavior(_chainingBehavior).call(this); + return result === undefined ? this : result; + }; + + var _method = chainableBehavior.method; + chainableBehavior.method = function () { + var result = method(_method).apply(this, arguments); + return result === undefined ? this : result; + }; +}; + +}); + +require.register("chai/lib/chai/utils/test.js", function (exports, module) { +/*! + * Chai - test utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependancies + */ + +var flag = require('chai/lib/chai/utils/flag.js'); + +/** + * # test(object, expression) + * + * Test and object for expression. + * + * @param {Object} object (constructed Assertion) + * @param {Arguments} chai.Assertion.prototype.assert arguments + */ + +module.exports = function (obj, args) { + var negate = flag(obj, 'negate') + , expr = args[0]; + return negate ? !expr : expr; +}; + +}); + +require.register("chai/lib/chai/utils/transferFlags.js", function (exports, module) { +/*! + * Chai - transferFlags utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### transferFlags(assertion, object, includeAll = true) + * + * Transfer all the flags for `assertion` to `object`. If + * `includeAll` is set to `false`, then the base Chai + * assertion flags (namely `object`, `ssfi`, and `message`) + * will not be transferred. + * + * + * var newAssertion = new Assertion(); + * utils.transferFlags(assertion, newAssertion); + * + * var anotherAsseriton = new Assertion(myObj); + * utils.transferFlags(assertion, anotherAssertion, false); + * + * @param {Assertion} assertion the assertion to transfer the flags from + * @param {Object} object the object to transfer the flags too; usually a new assertion + * @param {Boolean} includeAll + * @name getAllFlags + * @api private + */ + +module.exports = function (assertion, object, includeAll) { + var flags = assertion.__flags || (assertion.__flags = Object.create(null)); + + if (!object.__flags) { + object.__flags = Object.create(null); + } + + includeAll = arguments.length === 3 ? includeAll : true; + + for (var flag in flags) { + if (includeAll || + (flag !== 'object' && flag !== 'ssfi' && flag != 'message')) { + object.__flags[flag] = flags[flag]; + } + } +}; + +}); + +require.register("chai/lib/chai/utils/type.js", function (exports, module) { +/*! + * Chai - type utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Detectable javascript natives + */ + +var natives = { + '[object Arguments]': 'arguments' + , '[object Array]': 'array' + , '[object Date]': 'date' + , '[object Function]': 'function' + , '[object Number]': 'number' + , '[object RegExp]': 'regexp' + , '[object String]': 'string' +}; + +/** + * ### type(object) + * + * Better implementation of `typeof` detection that can + * be used cross-browser. Handles the inconsistencies of + * Array, `null`, and `undefined` detection. + * + * utils.type({}) // 'object' + * utils.type(null) // `null' + * utils.type(undefined) // `undefined` + * utils.type([]) // `array` + * + * @param {Mixed} object to detect type of + * @name type + * @api private + */ + +module.exports = function (obj) { + var str = Object.prototype.toString.call(obj); + if (natives[str]) return natives[str]; + if (obj === null) return 'null'; + if (obj === undefined) return 'undefined'; + if (obj === Object(obj)) return 'object'; + return typeof obj; +}; + +}); + +if (typeof exports == "object") { + module.exports = require("chai"); +} else if (typeof define == "function" && define.amd) { + define("chai", [], function(){ return require("chai"); }); +} else { + (this || window)["chai"] = require("chai"); +} +})() diff --git a/tests/mocha/mocha.css b/tests/automation/res/mocha.css similarity index 100% rename from tests/mocha/mocha.css rename to tests/automation/res/mocha.css diff --git a/tests/mocha/mocha.js b/tests/automation/res/mocha.js similarity index 92% rename from tests/mocha/mocha.js rename to tests/automation/res/mocha.js index 6eb96ae6ff..564a4f3184 100644 --- a/tests/mocha/mocha.js +++ b/tests/automation/res/mocha.js @@ -1,4 +1,4 @@ -;(function(global){;(function(){ +;(function(){ // CommonJS require() @@ -48,7 +48,6 @@ require.relative = function (parent) { require.register("browser/debug.js", function(module, exports, require){ - module.exports = function(type){ return function(){ } @@ -414,8 +413,22 @@ if (typeof module !== 'undefined') { }); // module: browser/diff.js -require.register("browser/events.js", function(module, exports, require){ +require.register("browser/escape-string-regexp.js", function(module, exports, require){ +'use strict'; + +var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; + +module.exports = function (str) { + if (typeof str !== 'string') { + throw new TypeError('Expected a string'); + } + + return str.replace(matchOperatorsRe, '\\$&'); +}; + +}); // module: browser/escape-string-regexp.js +require.register("browser/events.js", function(module, exports, require){ /** * Module exports. */ @@ -593,12 +606,17 @@ EventEmitter.prototype.emit = function (name) { return true; }; + }); // module: browser/events.js require.register("browser/fs.js", function(module, exports, require){ }); // module: browser/fs.js +require.register("browser/glob.js", function(module, exports, require){ + +}); // module: browser/glob.js + require.register("browser/path.js", function(module, exports, require){ }); // module: browser/path.js @@ -700,28 +718,28 @@ Progress.prototype.draw = function(ctx){ , y = half , rad = half - 1 , fontSize = this._fontSize; - + ctx.font = fontSize + 'px ' + this._font; - + var angle = Math.PI * 2 * (percent / 100); ctx.clearRect(0, 0, size, size); - + // outer circle ctx.strokeStyle = '#9f9f9f'; ctx.beginPath(); ctx.arc(x, y, rad, 0, angle, false); ctx.stroke(); - + // inner circle ctx.strokeStyle = '#eee'; ctx.beginPath(); ctx.arc(x, y, rad - 1, 0, angle, true); ctx.stroke(); - + // text var text = this._text || (percent | 0) + '%' , w = ctx.measureText(text).width; - + ctx.fillText( text , x - w / 2 + 1 @@ -733,7 +751,6 @@ Progress.prototype.draw = function(ctx){ }); // module: browser/progress.js require.register("browser/tty.js", function(module, exports, require){ - exports.isatty = function(){ return true; }; @@ -750,7 +767,6 @@ exports.getWindowSize = function(){ }); // module: browser/tty.js require.register("context.js", function(module, exports, require){ - /** * Expose `Context`. */ @@ -788,10 +804,25 @@ Context.prototype.runnable = function(runnable){ */ Context.prototype.timeout = function(ms){ + if (arguments.length === 0) return this.runnable().timeout(); this.runnable().timeout(ms); return this; }; +/** + * Set test timeout `enabled`. + * + * @param {Boolean} enabled + * @return {Context} self + * @api private + */ + +Context.prototype.enableTimeouts = function (enabled) { + this.runnable().enableTimeouts(enabled); + return this; +}; + + /** * Set test slowness threshold `ms`. * @@ -823,7 +854,6 @@ Context.prototype.inspect = function(){ }); // module: context.js require.register("hook.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -880,14 +910,14 @@ Hook.prototype.error = function(err){ }); // module: hook.js require.register("interfaces/bdd.js", function(module, exports, require){ - /** * Module dependencies. */ var Suite = require('../suite') , Test = require('../test') - , utils = require('../utils'); + , utils = require('../utils') + , escapeRe = require('browser/escape-string-regexp'); /** * BDD-style interface: @@ -951,6 +981,7 @@ module.exports = function(suite){ context.describe = context.context = function(title, fn){ var suite = Suite.create(suites[0], title); + suite.file = file; suites.unshift(suite); fn.call(suite); suites.shift(); @@ -989,8 +1020,9 @@ module.exports = function(suite){ context.it = context.specify = function(title, fn){ var suite = suites[0]; - if (suite.pending) var fn = null; + if (suite.pending) fn = null; var test = new Test(title, fn); + test.file = file; suite.addTest(test); return test; }; @@ -1001,7 +1033,7 @@ module.exports = function(suite){ context.it.only = function(title, fn){ var test = context.it(title, fn); - var reString = '^' + utils.escapeRegexp(test.fullTitle()) + '$'; + var reString = '^' + escapeRe(test.fullTitle()) + '$'; mocha.grep(new RegExp(reString)); return test; }; @@ -1021,7 +1053,6 @@ module.exports = function(suite){ }); // module: interfaces/bdd.js require.register("interfaces/exports.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -1051,7 +1082,7 @@ module.exports = function(suite){ suite.on('require', visit); - function visit(obj) { + function visit(obj, file) { var suite; for (var key in obj) { if ('function' == typeof obj[key]) { @@ -1070,10 +1101,12 @@ module.exports = function(suite){ suites[0].afterEach(fn); break; default: - suites[0].addTest(new Test(key, fn)); + var test = new Test(key, fn); + test.file = file; + suites[0].addTest(test); } } else { - var suite = Suite.create(suites[0], key); + suite = Suite.create(suites[0], key); suites.unshift(suite); visit(obj[key]); suites.shift(); @@ -1085,7 +1118,6 @@ module.exports = function(suite){ }); // module: interfaces/exports.js require.register("interfaces/index.js", function(module, exports, require){ - exports.bdd = require('./bdd'); exports.tdd = require('./tdd'); exports.qunit = require('./qunit'); @@ -1094,13 +1126,13 @@ exports.exports = require('./exports'); }); // module: interfaces/index.js require.register("interfaces/qunit.js", function(module, exports, require){ - /** * Module dependencies. */ var Suite = require('../suite') , Test = require('../test') + , escapeRe = require('browser/escape-string-regexp') , utils = require('../utils'); /** @@ -1172,6 +1204,7 @@ module.exports = function(suite){ context.suite = function(title){ if (suites.length > 1) suites.shift(); var suite = Suite.create(suites[0], title); + suite.file = file; suites.unshift(suite); return suite; }; @@ -1193,6 +1226,7 @@ module.exports = function(suite){ context.test = function(title, fn){ var test = new Test(title, fn); + test.file = file; suites[0].addTest(test); return test; }; @@ -1203,7 +1237,7 @@ module.exports = function(suite){ context.test.only = function(title, fn){ var test = context.test(title, fn); - var reString = '^' + utils.escapeRegexp(test.fullTitle()) + '$'; + var reString = '^' + escapeRe(test.fullTitle()) + '$'; mocha.grep(new RegExp(reString)); }; @@ -1220,14 +1254,14 @@ module.exports = function(suite){ }); // module: interfaces/qunit.js require.register("interfaces/tdd.js", function(module, exports, require){ - /** * Module dependencies. */ var Suite = require('../suite') , Test = require('../test') - , utils = require('../utils');; + , escapeRe = require('browser/escape-string-regexp') + , utils = require('../utils'); /** * TDD-style interface: @@ -1299,6 +1333,7 @@ module.exports = function(suite){ context.suite = function(title, fn){ var suite = Suite.create(suites[0], title); + suite.file = file; suites.unshift(suite); fn.call(suite); suites.shift(); @@ -1333,8 +1368,9 @@ module.exports = function(suite){ context.test = function(title, fn){ var suite = suites[0]; - if (suite.pending) var fn = null; + if (suite.pending) fn = null; var test = new Test(title, fn); + test.file = file; suite.addTest(test); return test; }; @@ -1345,7 +1381,7 @@ module.exports = function(suite){ context.test.only = function(title, fn){ var test = context.test(title, fn); - var reString = '^' + utils.escapeRegexp(test.fullTitle()) + '$'; + var reString = '^' + escapeRe(test.fullTitle()) + '$'; mocha.grep(new RegExp(reString)); }; @@ -1373,6 +1409,7 @@ require.register("mocha.js", function(module, exports, require){ */ var path = require('browser/path') + , escapeRe = require('browser/escape-string-regexp') , utils = require('./utils'); /** @@ -1381,6 +1418,16 @@ var path = require('browser/path') exports = module.exports = Mocha; +/** + * To require local UIs and reporters when running in node. + */ + +if (typeof process !== 'undefined' && typeof process.cwd === 'function') { + var join = path.join + , cwd = process.cwd(); + module.paths.push(cwd, join(cwd, 'node_modules')); +} + /** * Expose internals. */ @@ -1413,7 +1460,7 @@ function image(name) { * Options: * * - `ui` name "bdd", "tdd", "exports" etc - * - `reporter` reporter instance, defaults to `mocha.reporters.Dot` + * - `reporter` reporter instance, defaults to `mocha.reporters.spec` * - `globals` array of accepted globals * - `timeout` timeout in milliseconds * - `bail` bail on the first test failure @@ -1436,6 +1483,7 @@ function Mocha(options) { this.reporter(options.reporter); if (null != options.timeout) this.timeout(options.timeout); this.useColors(options.useColors) + if (options.enableTimeouts !== null) this.enableTimeouts(options.enableTimeouts); if (options.slow) this.slow(options.slow); this.suite.on('pre-require', function (context) { @@ -1480,7 +1528,7 @@ Mocha.prototype.addFile = function(file){ }; /** - * Set reporter to `reporter`, defaults to "dot". + * Set reporter to `reporter`, defaults to "spec". * * @param {String|Function} reporter name or constructor * @api public @@ -1490,7 +1538,7 @@ Mocha.prototype.reporter = function(reporter){ if ('function' == typeof reporter) { this._reporter = reporter; } else { - reporter = reporter || 'dot'; + reporter = reporter || 'spec'; var _reporter; try { _reporter = require('./reporters/' + reporter); } catch (err) {}; if (!_reporter) try { _reporter = require(reporter); } catch (err) {}; @@ -1573,7 +1621,7 @@ Mocha.prototype._growl = function(runner, reporter) { Mocha.prototype.grep = function(re){ this.options.grep = 'string' == typeof re - ? new RegExp(utils.escapeRegexp(re)) + ? new RegExp(escapeRe(re)) : re; return this; }; @@ -1696,6 +1744,21 @@ Mocha.prototype.slow = function(slow){ return this; }; +/** + * Enable timeouts. + * + * @param {Boolean} enabled + * @return {Mocha} + * @api public + */ + +Mocha.prototype.enableTimeouts = function(enabled) { + this.suite.enableTimeouts(arguments.length && enabled !== undefined + ? enabled + : true); + return this +}; + /** * Makes all tests async (accepting a callback) * @@ -1708,6 +1771,16 @@ Mocha.prototype.asyncOnly = function(){ return this; }; +/** + * Disable syntax highlighting (in browser). + * @returns {Mocha} + * @api public + */ +Mocha.prototype.noHighlighting = function() { + this.options.noHighlighting = true; + return this; +}; + /** * Run tests and invoke `fn()` when complete. * @@ -1762,7 +1835,7 @@ var y = d * 365.25; module.exports = function(val, options){ options = options || {}; if ('string' == typeof val) return parse(val); - return options.long ? longFormat(val) : shortFormat(val); + return options['long'] ? longFormat(val) : shortFormat(val); }; /** @@ -1849,7 +1922,6 @@ function plural(ms, n, name) { }); // module: ms.js require.register("reporters/base.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -2031,12 +2103,12 @@ exports.list = function(failures){ // explicitly show diff if (err.showDiff && sameType(actual, expected)) { escape = false; - err.actual = actual = stringify(canonicalize(actual)); - err.expected = expected = stringify(canonicalize(expected)); + err.actual = actual = utils.stringify(actual); + err.expected = expected = utils.stringify(expected); } // actual / expected diff - if ('string' == typeof actual && 'string' == typeof expected) { + if (err.showDiff && 'string' == typeof actual && 'string' == typeof expected) { fmt = color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n'); var match = message.match(/^([^:]+): expected/); msg = '\n ' + color('error message', match ? match[1] : msg); @@ -2294,53 +2366,6 @@ function colorLines(name, str) { }).join('\n'); } -/** - * Stringify `obj`. - * - * @param {Object} obj - * @return {String} - * @api private - */ - -function stringify(obj) { - if (obj instanceof RegExp) return obj.toString(); - return JSON.stringify(obj, null, 2); -} - -/** - * Return a new object that has the keys in sorted order. - * @param {Object} obj - * @return {Object} - * @api private - */ - - function canonicalize(obj, stack) { - stack = stack || []; - - if (utils.indexOf(stack, obj) !== -1) return obj; - - var canonicalizedObj; - - if ('[object Array]' == {}.toString.call(obj)) { - stack.push(obj); - canonicalizedObj = utils.map(obj, function(item) { - return canonicalize(item, stack); - }); - stack.pop(); - } else if (typeof obj === 'object' && obj !== null) { - stack.push(obj); - canonicalizedObj = {}; - utils.forEach(utils.keys(obj).sort(), function(key) { - canonicalizedObj[key] = canonicalize(obj[key], stack); - }); - stack.pop(); - } else { - canonicalizedObj = obj; - } - - return canonicalizedObj; - } - /** * Check that a / b have the same type. * @@ -2356,11 +2381,9 @@ function sameType(a, b) { return a == b; } - }); // module: reporters/base.js require.register("reporters/doc.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -2415,12 +2438,18 @@ function Doc(runner) { var code = utils.escape(utils.clean(test.fn.toString())); console.log('%s
%s
', indent(), code); }); + + runner.on('fail', function(test, err){ + console.log('%s
%s
', indent(), utils.escape(test.title)); + var code = utils.escape(utils.clean(test.fn.toString())); + console.log('%s
%s
', indent(), code); + console.log('%s
%s
', indent(), utils.escape(err)); + }); } }); // module: reporters/doc.js require.register("reporters/dot.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -2447,13 +2476,14 @@ function Dot(runner) { var self = this , stats = this.stats , width = Base.window.width * .75 | 0 - , n = 0; + , n = -1; runner.on('start', function(){ process.stdout.write('\n '); }); runner.on('pending', function(test){ + if (++n % width == 0) process.stdout.write('\n '); process.stdout.write(color('pending', Base.symbols.dot)); }); @@ -2486,10 +2516,10 @@ F.prototype = Base.prototype; Dot.prototype = new F; Dot.prototype.constructor = Dot; + }); // module: reporters/dot.js require.register("reporters/html-cov.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -2540,10 +2570,10 @@ function coverageClass(n) { if (n >= 25) return 'low'; return 'terrible'; } + }); // module: reporters/html-cov.js require.register("reporters/html.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -2722,14 +2752,23 @@ function HTML(runner) { }); } +/** + * Makes a URL, preserving querystring ("search") parameters. + * @param {string} s + * @returns {string} your new URL + */ +var makeUrl = function makeUrl(s) { + var search = window.location.search; + return (search ? search + '&' : '?' ) + 'grep=' + encodeURIComponent(s); +}; + /** * Provide suite URL * * @param {Object} [suite] */ - HTML.prototype.suiteURL = function(suite){ - return '?grep=' + encodeURIComponent(suite.fullTitle()); + return makeUrl(suite.fullTitle()); }; /** @@ -2739,7 +2778,7 @@ HTML.prototype.suiteURL = function(suite){ */ HTML.prototype.testURL = function(test){ - return '?grep=' + encodeURIComponent(test.fullTitle()); + return makeUrl(test.fullTitle()); }; /** @@ -2820,7 +2859,6 @@ function on(el, event, fn) { }); // module: reporters/html.js require.register("reporters/index.js", function(module, exports, require){ - exports.Base = require('./base'); exports.Dot = require('./dot'); exports.Doc = require('./doc'); @@ -2842,7 +2880,6 @@ exports.JSONStream = require('./json-stream'); }); // module: reporters/index.js require.register("reporters/json-cov.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -2933,7 +2970,7 @@ function map(cov) { } return ret; -}; +} /** * Map jscoverage data for a single source file @@ -2999,7 +3036,6 @@ function clean(test) { }); // module: reporters/json-cov.js require.register("reporters/json-stream.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -3036,7 +3072,9 @@ function List(runner) { }); runner.on('fail', function(test, err){ - console.log(JSON.stringify(['fail', clean(test)])); + test = clean(test); + test.err = err.message; + console.log(JSON.stringify(['fail', test])); }); runner.on('end', function(){ @@ -3060,10 +3098,10 @@ function clean(test) { , duration: test.duration } } + }); // module: reporters/json-stream.js require.register("reporters/json.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -3090,6 +3128,7 @@ function JSONReporter(runner) { Base.call(this, runner); var tests = [] + , pending = [] , failures = [] , passes = []; @@ -3105,14 +3144,21 @@ function JSONReporter(runner) { failures.push(test); }); + runner.on('pending', function(test){ + pending.push(test); + }); + runner.on('end', function(){ var obj = { - stats: self.stats - , tests: tests.map(clean) - , failures: failures.map(clean) - , passes: passes.map(clean) + stats: self.stats, + tests: tests.map(clean), + pending: pending.map(clean), + failures: failures.map(clean), + passes: passes.map(clean) }; + runner.testResults = obj; + process.stdout.write(JSON.stringify(obj, null, 2)); }); } @@ -3128,15 +3174,30 @@ function JSONReporter(runner) { function clean(test) { return { - title: test.title - , fullTitle: test.fullTitle() - , duration: test.duration + title: test.title, + fullTitle: test.fullTitle(), + duration: test.duration, + err: errorJSON(test.err || {}) } } + +/** + * Transform `error` into a JSON object. + * @param {Error} err + * @return {Object} + */ + +function errorJSON(err) { + var res = {}; + Object.getOwnPropertyNames(err).forEach(function(key) { + res[key] = err[key]; + }, err); + return res; +} + }); // module: reporters/json.js require.register("reporters/landing.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -3194,7 +3255,7 @@ function Landing(runner) { } runner.on('start', function(){ - stream.write('\n '); + stream.write('\n\n\n '); cursor.hide(); }); @@ -3211,7 +3272,7 @@ function Landing(runner) { } // render landing strip - stream.write('\u001b[4F\n\n'); + stream.write('\u001b['+(width+1)+'D\u001b[2A'); stream.write(runway()); stream.write('\n '); stream.write(color('runway', Array(col).join('⋅'))); @@ -3237,10 +3298,10 @@ F.prototype = Base.prototype; Landing.prototype = new F; Landing.prototype.constructor = Landing; + }); // module: reporters/landing.js require.register("reporters/list.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -3403,10 +3464,10 @@ function Markdown(runner) { process.stdout.write(buf); }); } + }); // module: reporters/markdown.js require.register("reporters/min.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -3639,7 +3700,7 @@ NyanCat.prototype.face = function() { } else { return '( - .-)'; } -} +}; /** * Move cursor up `n`. @@ -3720,7 +3781,6 @@ NyanCat.prototype.constructor = NyanCat; }); // module: reporters/nyan.js require.register("reporters/progress.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -3758,7 +3818,8 @@ function Progress(runner, options) { , width = Base.window.width * .50 | 0 , total = runner.total , complete = 0 - , max = Math.max; + , max = Math.max + , lastN = -1; // default chars options.open = options.open || '['; @@ -3781,6 +3842,12 @@ function Progress(runner, options) { , n = width * percent | 0 , i = width - n; + if (lastN === n && !options.verbose) { + // Don't re-render the line if it hasn't changed + return; + } + lastN = n; + cursor.CR(); process.stdout.write('\u001b[J'); process.stdout.write(color('progress', ' ' + options.open)); @@ -3814,7 +3881,6 @@ Progress.prototype.constructor = Progress; }); // module: reporters/progress.js require.register("reporters/spec.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -3905,7 +3971,6 @@ Spec.prototype.constructor = Spec; }); // module: reporters/spec.js require.register("reporters/tap.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -3982,7 +4047,6 @@ function title(test) { }); // module: reporters/tap.js require.register("reporters/xunit.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -4071,8 +4135,7 @@ function test(test) { if ('failed' == test.state) { var err = test.err; - attrs.message = escape(err.message); - console.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack)))); + console.log(tag('testcase', attrs, false, tag('failure', {}, false, cdata(escape(err.message) + "\n" + err.stack)))); } else if (test.pending) { console.log(tag('testcase', attrs, false, tag('skipped', {}, true))); } else { @@ -4109,7 +4172,6 @@ function cdata(str) { }); // module: reporters/xunit.js require.register("runnable.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -4155,7 +4217,9 @@ function Runnable(title, fn) { this.sync = ! this.async; this._timeout = 2000; this._slow = 75; + this._enableTimeouts = true; this.timedOut = false; + this._trace = new Error('done() called multiple times') } /** @@ -4178,6 +4242,7 @@ Runnable.prototype.constructor = Runnable; Runnable.prototype.timeout = function(ms){ if (0 == arguments.length) return this._timeout; + if (ms === 0) this._enableTimeouts = false; if ('string' == typeof ms) ms = milliseconds(ms); debug('timeout %d', ms); this._timeout = ms; @@ -4201,6 +4266,21 @@ Runnable.prototype.slow = function(ms){ return this; }; +/** + * Set and & get timeout `enabled`. + * + * @param {Boolean} enabled + * @return {Runnable|Boolean} enabled or self + * @api private + */ + +Runnable.prototype.enableTimeouts = function(enabled){ + if (arguments.length === 0) return this._enableTimeouts; + debug('enableTimeouts %s', enabled); + this._enableTimeouts = enabled; + return this; +}; + /** * Return the full title generated by recursively * concatenating the parent's full title. @@ -4247,17 +4327,12 @@ Runnable.prototype.inspect = function(){ Runnable.prototype.resetTimeout = function(){ var self = this; - var max_timeout = 10000;//default 10s - if (window && window.__mocha_max_timeout){ - max_timeout = window.__mocha_max_timeout; - } - var ms = this.timeout() || max_timeout; - if (ms > max_timeout){ - ms = max_timeout; - } + var ms = this.timeout() || 1e9; + if (!this._enableTimeouts) return; this.clearTimeout(); this.timer = setTimeout(function(){ + if (!self._enableTimeouts) return; self.callback(new Error('timeout of ' + ms + 'ms exceeded')); self.timedOut = true; }, ms); @@ -4282,28 +4357,30 @@ Runnable.prototype.globals = function(arr){ Runnable.prototype.run = function(fn){ var self = this - , ms = this.timeout() , start = new Date , ctx = this.ctx , finished , emitted; - if (ctx) ctx.runnable(this); + // Some times the ctx exists but it is not runnable + if (ctx && ctx.runnable) ctx.runnable(this); // called multiple times function multiple(err) { if (emitted) return; emitted = true; - self.emit('error', err || new Error('done() called multiple times')); + self.emit('error', err || new Error('done() called multiple times; stacktrace may be inaccurate')); } // finished function done(err) { + var ms = self.timeout(); if (self.timedOut) return; - if (finished) return multiple(err); + if (finished) return multiple(err || self._trace); self.clearTimeout(); self.duration = new Date - start; finished = true; + if (!err && self.duration > ms && self._enableTimeouts) err = new Error('timeout of ' + ms + 'ms exceeded'); fn(err); } @@ -4317,7 +4394,13 @@ Runnable.prototype.run = function(fn){ try { this.fn.call(ctx, function(err){ if (err instanceof Error || toString.call(err) === "[object Error]") return done(err); - if (null != err) return done(new Error('done() invoked with non-Error: ' + err)); + if (null != err) { + if (Object.prototype.toString.call(err) === '[object Object]') { + return done(new Error('done() invoked with non-Error: ' + JSON.stringify(err))); + } else { + return done(new Error('done() invoked with non-Error: ' + err)); + } + } done(); }); } catch (err) { @@ -4345,7 +4428,13 @@ Runnable.prototype.run = function(fn){ var result = fn.call(ctx); if (result && typeof result.then === 'function') { self.resetTimeout(); - result.then(function(){ done() }, done); + result + .then(function() { + done() + }, + function(reason) { + done(reason || new Error('Promise rejected with no or falsy reason')) + }); } else { done(); } @@ -4522,7 +4611,6 @@ Runner.prototype.checkGlobals = function(test){ var ok = this._globals; var globals = this.globalProps(); - var isNode = process.kill; var leaks; if (test) { @@ -4894,13 +4982,26 @@ Runner.prototype.runSuite = function(suite, fn){ */ Runner.prototype.uncaught = function(err){ - debug('uncaught exception %s', err.message); - var runnable = this.currentRunnable; - if (!runnable || 'failed' == runnable.state) return; - runnable.clearTimeout(); + if (err) { + debug('uncaught exception %s', err !== function () { + return this; + }.call(err) ? err : ( err.message || err )); + } else { + debug('uncaught undefined exception'); + err = new Error('Caught undefined error, did you throw without specifying what?'); + } err.uncaught = true; + + var runnable = this.currentRunnable; + if (!runnable) return; + + var wasAlreadyDone = runnable.state; this.fail(runnable, err); + runnable.clearTimeout(); + + if (wasAlreadyDone) return; + // recover from test if ('test' == runnable.type) { this.emit('test end', runnable); @@ -4947,7 +5048,6 @@ Runner.prototype.run = function(fn){ // uncaught exception process.on('uncaughtException', uncaught); - window.__uncaught = uncaught; return this; }; @@ -4961,7 +5061,7 @@ Runner.prototype.run = function(fn){ Runner.prototype.abort = function(){ debug('aborting'); this._abort = true; -} +}; /** * Filter leaks with the given globals flagged as `ok`. @@ -5025,7 +5125,6 @@ function filterLeaks(ok, globals) { }); // module: runner.js require.register("suite.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -5073,9 +5172,11 @@ exports.create = function(parent, title){ * @api private */ -function Suite(title, ctx) { +function Suite(title, parentContext) { this.title = title; - this.ctx = ctx; + var context = function() {}; + context.prototype = parentContext; + this.ctx = new context(); this.suites = []; this.tests = []; this.pending = false; @@ -5085,6 +5186,7 @@ function Suite(title, ctx) { this._afterAll = []; this.root = !title; this._timeout = 2000; + this._enableTimeouts = true; this._slow = 75; this._bail = false; } @@ -5111,6 +5213,7 @@ Suite.prototype.clone = function(){ debug('clone'); suite.ctx = this.ctx; suite.timeout(this.timeout()); + suite.enableTimeouts(this.enableTimeouts()); suite.slow(this.slow()); suite.bail(this.bail()); return suite; @@ -5126,12 +5229,28 @@ Suite.prototype.clone = function(){ Suite.prototype.timeout = function(ms){ if (0 == arguments.length) return this._timeout; + if (ms === 0) this._enableTimeouts = false; if ('string' == typeof ms) ms = milliseconds(ms); debug('timeout %d', ms); this._timeout = parseInt(ms, 10); return this; }; +/** + * Set timeout `enabled`. + * + * @param {Boolean} enabled + * @return {Suite|Boolean} self or enabled + * @api private + */ + +Suite.prototype.enableTimeouts = function(enabled){ + if (arguments.length === 0) return this._enableTimeouts; + debug('enableTimeouts %s', enabled); + this._enableTimeouts = enabled; + return this; +}; + /** * Set slow `ms` or short-hand such as "2s". * @@ -5182,6 +5301,7 @@ Suite.prototype.beforeAll = function(title, fn){ var hook = new Hook(title, fn); hook.parent = this; hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); hook.slow(this.slow()); hook.ctx = this.ctx; this._beforeAll.push(hook); @@ -5208,6 +5328,7 @@ Suite.prototype.afterAll = function(title, fn){ var hook = new Hook(title, fn); hook.parent = this; hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); hook.slow(this.slow()); hook.ctx = this.ctx; this._afterAll.push(hook); @@ -5234,6 +5355,7 @@ Suite.prototype.beforeEach = function(title, fn){ var hook = new Hook(title, fn); hook.parent = this; hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); hook.slow(this.slow()); hook.ctx = this.ctx; this._beforeEach.push(hook); @@ -5260,6 +5382,7 @@ Suite.prototype.afterEach = function(title, fn){ var hook = new Hook(title, fn); hook.parent = this; hook.timeout(this.timeout()); + hook.enableTimeouts(this.enableTimeouts()); hook.slow(this.slow()); hook.ctx = this.ctx; this._afterEach.push(hook); @@ -5278,6 +5401,7 @@ Suite.prototype.afterEach = function(title, fn){ Suite.prototype.addSuite = function(suite){ suite.parent = this; suite.timeout(this.timeout()); + suite.enableTimeouts(this.enableTimeouts()); suite.slow(this.slow()); suite.bail(this.bail()); this.suites.push(suite); @@ -5296,6 +5420,7 @@ Suite.prototype.addSuite = function(suite){ Suite.prototype.addTest = function(test){ test.parent = this; test.timeout(this.timeout()); + test.enableTimeouts(this.enableTimeouts()); test.slow(this.slow()); test.ctx = this.ctx; this.tests.push(test); @@ -5353,7 +5478,6 @@ Suite.prototype.eachTest = function(fn){ }); // module: suite.js require.register("test.js", function(module, exports, require){ - /** * Module dependencies. */ @@ -5399,6 +5523,9 @@ require.register("utils.js", function(module, exports, require){ var fs = require('browser/fs') , path = require('browser/path') + , basename = path.basename + , exists = fs.existsSync || path.existsSync + , glob = require('browser/glob') , join = path.join , debug = require('browser/debug')('mocha:watch'); @@ -5564,16 +5691,19 @@ function ignored(path){ * @api private */ -exports.files = function(dir, ret){ +exports.files = function(dir, ext, ret){ ret = ret || []; + ext = ext || ['js']; + + var re = new RegExp('\\.(' + ext.join('|') + ')$'); fs.readdirSync(dir) .filter(ignored) .forEach(function(path){ path = join(dir, path); if (fs.statSync(path).isDirectory()) { - exports.files(path, ret); - } else if (path.match(/\.(js|coffee|litcoffee|coffee.md)$/)) { + exports.files(path, ext, ret); + } else if (path.match(re)) { ret.push(path); } }); @@ -5604,7 +5734,7 @@ exports.slug = function(str){ exports.clean = function(str) { str = str .replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, '') - .replace(/^function *\(.*\) *{/, '') + .replace(/^function *\(.*\) *{|\(.*\) *=> *{?/, '') .replace(/\s+\}$/, ''); var spaces = str.match(/^\n?( *)/)[1].length @@ -5616,18 +5746,6 @@ exports.clean = function(str) { return exports.trim(str); }; -/** - * Escape regular expression characters in `str`. - * - * @param {String} str - * @return {String} - * @api private - */ - -exports.escapeRegexp = function(str){ - return str.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&"); -}; - /** * Trim the given `str`. * @@ -5675,7 +5793,7 @@ function highlight(js) { .replace(/('.*?')/gm, '$1') .replace(/(\d+\.\d+)/gm, '$1') .replace(/(\d+)/gm, '$1') - .replace(/\bnew *(\w+)/gm, 'new $1') + .replace(/\bnew[ \t]+(\w+)/gm, 'new $1') .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '$1') } @@ -5687,15 +5805,110 @@ function highlight(js) { */ exports.highlightTags = function(name) { - var code = document.getElementsByTagName(name); + var code = document.getElementById('mocha').getElementsByTagName(name); for (var i = 0, len = code.length; i < len; ++i) { code[i].innerHTML = highlight(code[i].innerHTML); } }; + +/** + * Stringify `obj`. + * + * @param {Object} obj + * @return {String} + * @api private + */ + +exports.stringify = function(obj) { + if (obj instanceof RegExp) return obj.toString(); + return JSON.stringify(exports.canonicalize(obj), null, 2).replace(/,(\n|$)/g, '$1'); +}; + +/** + * Return a new object that has the keys in sorted order. + * @param {Object} obj + * @param {Array} [stack] + * @return {Object} + * @api private + */ + +exports.canonicalize = function(obj, stack) { + stack = stack || []; + + if (exports.indexOf(stack, obj) !== -1) return '[Circular]'; + + var canonicalizedObj; + + if ({}.toString.call(obj) === '[object Array]') { + stack.push(obj); + canonicalizedObj = exports.map(obj, function (item) { + return exports.canonicalize(item, stack); + }); + stack.pop(); + } else if (typeof obj === 'object' && obj !== null) { + stack.push(obj); + canonicalizedObj = {}; + exports.forEach(exports.keys(obj).sort(), function (key) { + canonicalizedObj[key] = exports.canonicalize(obj[key], stack); + }); + stack.pop(); + } else { + canonicalizedObj = obj; + } + + return canonicalizedObj; + }; + +/** + * Lookup file names at the given `path`. + */ +exports.lookupFiles = function lookupFiles(path, extensions, recursive) { + var files = []; + var re = new RegExp('\\.(' + extensions.join('|') + ')$'); + + if (!exists(path)) { + if (exists(path + '.js')) { + path += '.js'; + } else { + files = glob.sync(path); + if (!files.length) throw new Error("cannot resolve path (or pattern) '" + path + "'"); + return files; + } + } + + try { + var stat = fs.statSync(path); + if (stat.isFile()) return path; + } + catch (ignored) { + return; + } + + fs.readdirSync(path).forEach(function(file){ + file = join(path, file); + try { + var stat = fs.statSync(file); + if (stat.isDirectory()) { + if (recursive) { + files = files.concat(lookupFiles(file, extensions, recursive)); + } + return; + } + } + catch (ignored) { + return; + } + if (!stat.isFile() || !re.test(file) || basename(file)[0] === '.') return; + files.push(file); + }); + + return files; +}; + }); // module: utils.js // The global object is "self" in Web Workers. -global = (function() { return this; })(); +var global = (function() { return this; })(); /** * Save timer references to avoid Sinon interfering (see GH-237). @@ -5722,13 +5935,20 @@ process.stdout = {}; var uncaughtExceptionHandlers = []; +var originalOnerrorHandler = global.onerror; + /** * Remove uncaughtException listener. + * Revert to original onerror handler if previously defined. */ process.removeListener = function(e, fn){ if ('uncaughtException' == e) { - global.onerror = function() {}; + if (originalOnerrorHandler) { + global.onerror = originalOnerrorHandler; + } else { + global.onerror = function() {}; + } var i = Mocha.utils.indexOf(uncaughtExceptionHandlers, fn); if (i != -1) { uncaughtExceptionHandlers.splice(i, 1); } } @@ -5831,12 +6051,13 @@ mocha.run = function(fn){ if (query.grep) mocha.grep(query.grep); if (query.invert) mocha.invert(); - return Mocha.prototype.run.call(mocha, function(){ + return Mocha.prototype.run.call(mocha, function(err){ // The DOM Document is not available in Web Workers. - if (global.document) { + var document = global.document; + if (document && document.getElementById('mocha') && options.noHighlighting !== true) { Mocha.utils.highlightTags('code'); } - if (fn) fn(); + if (fn) fn(err); }); }; @@ -5845,4 +6066,4 @@ mocha.run = function(fn){ */ Mocha.process = process; -})();})(window||global||undefined); \ No newline at end of file +})(); diff --git a/tests/automation/res/mocha_util.js b/tests/automation/res/mocha_util.js new file mode 100644 index 0000000000..eab1459caa --- /dev/null +++ b/tests/automation/res/mocha_util.js @@ -0,0 +1,3 @@ +!function(){function t(e){var n=t.resolve(e),r=t.modules[n];if(!r)throw new Error('failed to require "'+e+'"');return r.exports||(r.exports={},r.call(r.exports,r,r.exports,t.relative(n))),r.exports}function e(){for(var t=(new r).getTime();h.length&&(new r).getTime()-t<100;)h.shift()();l=h.length?i(e,0):null}t.modules={},t.resolve=function(e){var n=e,r=e+".js",i=e+"/index.js";return t.modules[r]&&r||t.modules[i]&&i||n},t.register=function(e,n){t.modules[e]=n},t.relative=function(e){return function(n){if("."!=n.charAt(0))return t(n);var r=e.split("/"),i=n.split("/");r.pop();for(var o=0;o/g,">"),e=e.replace(/"/g,""")}var r=function(t){this.ignoreWhitespace=t};r.prototype={diff:function(e,n){if(n===e)return[{value:n}];if(!n)return[{value:e,removed:!0}];if(!e)return[{value:n,added:!0}];n=this.tokenize(n),e=this.tokenize(e);var r=n.length,i=e.length,o=r+i,s=[{newPos:-1,components:[]}],a=this.extractCommon(s[0],n,e,0);if(s[0].newPos+1>=r&&a+1>=i)return s[0].components;for(var u=1;o>=u;u++)for(var c=-1*u;u>=c;c+=2){var l,h=s[c-1],f=s[c+1];a=(f?f.newPos:0)-c,h&&(s[c-1]=void 0);var p=h&&h.newPos+1=0&&i>a;if(p||d){!p||d&&h.newPos=r&&a+1>=i)return l.components;s[c]=l}else s[c]=void 0}},pushComponent:function(t,e,n,r){var i=t[t.length-1];i&&i.added===n&&i.removed===r?t[t.length-1]={value:this.join(i.value,e),added:n,removed:r}:t.push({value:e,added:n,removed:r})},extractCommon:function(t,e,n,r){for(var i=e.length,o=n.length,s=t.newPos,a=s-r;i>s+1&&o>a+1&&this.equals(e[s+1],n[a+1]);)s++,a++,this.pushComponent(t.components,e[s],void 0,void 0);return t.newPos=s,a},equals:function(t,e){var n=/\S/;return!this.ignoreWhitespace||n.test(t)||n.test(e)?t===e:!0},join:function(t,e){return t+e},tokenize:function(t){return t}};var i=new r,o=new r(!0),s=new r;o.tokenize=s.tokenize=function(t){return e(t.split(/(\s+|\b)/))};var a=new r(!0);a.tokenize=function(t){return e(t.split(/([{}:;,]|\s+)/))};var u=new r;return u.tokenize=function(t){return t.split(/^/m)},{Diff:r,diffChars:function(t,e){return i.diff(t,e)},diffWords:function(t,e){return o.diff(t,e)},diffWordsWithSpace:function(t,e){return s.diff(t,e)},diffLines:function(t,e){return u.diff(t,e)},diffCss:function(t,e){return a.diff(t,e)},createPatch:function(t,e,n,r,i){function o(t){return t.map(function(t){return" "+t})}function s(t,e,n){var r=c[c.length-2],i=e===c.length-2,o=e===c.length-3&&(n.added!==r.added||n.removed!==r.removed);/\n$/.test(n.value)||!i&&!o||t.push("\\ No newline at end of file")}var a=[];a.push("Index: "+t),a.push("==================================================================="),a.push("--- "+t+("undefined"==typeof r?"":" "+r)),a.push("+++ "+t+("undefined"==typeof i?"":" "+i));var c=u.diff(e,n);c[c.length-1].value||c.pop(),c.push({value:"",lines:[]});for(var l=0,h=0,f=[],p=1,d=1,g=0;g=0;s--){for(var c=r[s],l=0;l"):i.removed&&e.push(""),e.push(n(i.value)),i.added?e.push(""):i.removed&&e.push("")}return e.join("")},convertChangesToDMP:function(t){for(var e,n=[],r=0;ro;o++)if(r[o]===e||r[o].listener&&r[o].listener===e){i=o;break}if(0>i)return this;r.splice(i,1),r.length||delete this.$events[t]}else(r===e||r.listener&&r.listener===e)&&delete this.$events[t]}return this},r.prototype.removeAllListeners=function(t){return void 0===t?(this.$events={},this):(this.$events&&this.$events[t]&&(this.$events[t]=null),this)},r.prototype.listeners=function(t){return this.$events||(this.$events={}),this.$events[t]||(this.$events[t]=[]),n(this.$events[t])||(this.$events[t]=[this.$events[t]]),this.$events[t]},r.prototype.emit=function(t){if(!this.$events)return!1;var e=this.$events[t];if(!e)return!1;var r=[].slice.call(arguments,1);if("function"==typeof e)e.apply(this,r);else{if(!n(e))return!1;for(var i=e.slice(),o=0,s=i.length;s>o;o++)i[o].apply(this,r)}return!0}}),t.register("browser/fs.js",function(){}),t.register("browser/glob.js",function(){}),t.register("browser/path.js",function(){}),t.register("browser/progress.js",function(t){function e(){this.percent=0,this.size(0),this.fontSize(11),this.font("helvetica, arial, sans-serif")}t.exports=e,e.prototype.size=function(t){return this._size=t,this},e.prototype.text=function(t){return this._text=t,this},e.prototype.fontSize=function(t){return this._fontSize=t,this},e.prototype.font=function(t){return this._font=t,this},e.prototype.update=function(t){return this.percent=t,this},e.prototype.draw=function(t){try{var e=Math.min(this.percent,100),n=this._size,r=n/2,i=r,o=r,s=r-1,a=this._fontSize;t.font=a+"px "+this._font;var u=2*Math.PI*(e/100);t.clearRect(0,0,n,n),t.strokeStyle="#9f9f9f",t.beginPath(),t.arc(i,o,s,0,u,!1),t.stroke(),t.strokeStyle="#eee",t.beginPath(),t.arc(i,o,s-1,0,u,!0),t.stroke();var c=this._text||(0|e)+"%",l=t.measureText(c).width;t.fillText(c,i-l/2+1,o+a/2-1)}catch(h){}return this}}),t.register("browser/tty.js",function(t,e){e.isatty=function(){return!0},e.getWindowSize=function(){return"innerHeight"in n?[n.innerHeight,n.innerWidth]:[640,480]}}),t.register("context.js",function(t){function e(){}t.exports=e,e.prototype.runnable=function(t){return 0==arguments.length?this._runnable:(this.test=this._runnable=t,this)},e.prototype.timeout=function(t){return 0===arguments.length?this.runnable().timeout():(this.runnable().timeout(t),this)},e.prototype.enableTimeouts=function(t){return this.runnable().enableTimeouts(t),this},e.prototype.slow=function(t){return this.runnable().slow(t),this},e.prototype.inspect=function(){return JSON.stringify(this,function(t,e){return"_runnable"!=t&&"test"!=t?e:void 0},2)}}),t.register("hook.js",function(t,e,n){function r(t,e){o.call(this,t,e),this.type="hook"}function i(){}var o=n("./runnable");t.exports=r,i.prototype=o.prototype,r.prototype=new i,r.prototype.constructor=r,r.prototype.error=function(t){if(0==arguments.length){var t=this._error;return this._error=null,t}this._error=t}}),t.register("interfaces/bdd.js",function(t,e,n){var r=n("../suite"),i=n("../test"),o=(n("../utils"),n("browser/escape-string-regexp"));t.exports=function(t){var e=[t];t.on("pre-require",function(t,n,s){t.before=function(t,n){e[0].beforeAll(t,n)},t.after=function(t,n){e[0].afterAll(t,n)},t.beforeEach=function(t,n){e[0].beforeEach(t,n)},t.afterEach=function(t,n){e[0].afterEach(t,n)},t.describe=t.context=function(t,i){var o=r.create(e[0],t);return o.file=n,e.unshift(o),i.call(o),e.shift(),o},t.xdescribe=t.xcontext=t.describe.skip=function(t,n){var i=r.create(e[0],t);i.pending=!0,e.unshift(i),n.call(i),e.shift()},t.describe.only=function(e,n){var r=t.describe(e,n);return s.grep(r.fullTitle()),r},t.it=t.specify=function(t,r){var o=e[0];o.pending&&(r=null);var s=new i(t,r);return s.file=n,o.addTest(s),s},t.it.only=function(e,n){var r=t.it(e,n),i="^"+o(r.fullTitle())+"$";return s.grep(new RegExp(i)),r},t.xit=t.xspecify=t.it.skip=function(e){t.it(e)}})}}),t.register("interfaces/exports.js",function(t,e,n){var r=n("../suite"),i=n("../test");t.exports=function(t){function e(t,o){var s;for(var a in t)if("function"==typeof t[a]){var u=t[a];switch(a){case"before":n[0].beforeAll(u);break;case"after":n[0].afterAll(u);break;case"beforeEach":n[0].beforeEach(u);break;case"afterEach":n[0].afterEach(u);break;default:var c=new i(a,u);c.file=o,n[0].addTest(c)}}else s=r.create(n[0],a),n.unshift(s),e(t[a]),n.shift()}var n=[t];t.on("require",e)}}),t.register("interfaces/index.js",function(t,e,n){e.bdd=n("./bdd"),e.tdd=n("./tdd"),e.qunit=n("./qunit"),e.exports=n("./exports")}),t.register("interfaces/qunit.js",function(t,e,n){{var r=n("../suite"),i=n("../test"),o=n("browser/escape-string-regexp");n("../utils")}t.exports=function(t){var e=[t];t.on("pre-require",function(t,n,s){t.before=function(t,n){e[0].beforeAll(t,n)},t.after=function(t,n){e[0].afterAll(t,n)},t.beforeEach=function(t,n){e[0].beforeEach(t,n)},t.afterEach=function(t,n){e[0].afterEach(t,n)},t.suite=function(t){e.length>1&&e.shift();var i=r.create(e[0],t);return i.file=n,e.unshift(i),i},t.suite.only=function(e,n){var r=t.suite(e,n);s.grep(r.fullTitle())},t.test=function(t,r){var o=new i(t,r);return o.file=n,e[0].addTest(o),o},t.test.only=function(e,n){var r=t.test(e,n),i="^"+o(r.fullTitle())+"$";s.grep(new RegExp(i))},t.test.skip=function(e){t.test(e)}})}}),t.register("interfaces/tdd.js",function(t,e,n){{var r=n("../suite"),i=n("../test"),o=n("browser/escape-string-regexp");n("../utils")}t.exports=function(t){var e=[t];t.on("pre-require",function(t,n,s){t.setup=function(t,n){e[0].beforeEach(t,n)},t.teardown=function(t,n){e[0].afterEach(t,n)},t.suiteSetup=function(t,n){e[0].beforeAll(t,n)},t.suiteTeardown=function(t,n){e[0].afterAll(t,n)},t.suite=function(t,i){var o=r.create(e[0],t);return o.file=n,e.unshift(o),i.call(o),e.shift(),o},t.suite.skip=function(t,n){var i=r.create(e[0],t);i.pending=!0,e.unshift(i),n.call(i),e.shift()},t.suite.only=function(e,n){var r=t.suite(e,n);s.grep(r.fullTitle())},t.test=function(t,r){var o=e[0];o.pending&&(r=null);var s=new i(t,r);return s.file=n,o.addTest(s),s},t.test.only=function(e,n){var r=t.test(e,n),i="^"+o(r.fullTitle())+"$";s.grep(new RegExp(i))},t.test.skip=function(e){t.test(e)}})}}),t.register("mocha.js",function(t,e,r){function i(t){return __dirname+"/../images/"+t+".png"}function s(t){t=t||{},this.files=[],this.options=t,this.grep(t.grep),this.suite=new e.Suite("",new e.Context),this.ui(t.ui),this.bail(t.bail),this.reporter(t.reporter),null!=t.timeout&&this.timeout(t.timeout),this.useColors(t.useColors),null!==t.enableTimeouts&&this.enableTimeouts(t.enableTimeouts),t.slow&&this.slow(t.slow),this.suite.on("pre-require",function(t){e.afterEach=t.afterEach||t.teardown,e.after=t.after||t.suiteTeardown,e.beforeEach=t.beforeEach||t.setup,e.before=t.before||t.suiteSetup,e.describe=t.describe||t.suite,e.it=t.it||t.test,e.setup=t.setup||t.beforeEach,e.suiteSetup=t.suiteSetup||t.before,e.suiteTeardown=t.suiteTeardown||t.after,e.suite=t.suite||t.describe,e.teardown=t.teardown||t.afterEach,e.test=t.test||t.it})}var a=r("browser/path"),u=r("browser/escape-string-regexp"),c=r("./utils");if(e=t.exports=s,"undefined"!=typeof o&&"function"==typeof o.cwd){var l=a.join,h=o.cwd();t.paths.push(h,l(h,"node_modules"))}e.utils=c,e.interfaces=r("./interfaces"),e.reporters=r("./reporters"),e.Runnable=r("./runnable"),e.Context=r("./context"),e.Runner=r("./runner"),e.Suite=r("./suite"),e.Hook=r("./hook"),e.Test=r("./test"),s.prototype.bail=function(t){return 0==arguments.length&&(t=!0),this.suite.bail(t),this},s.prototype.addFile=function(t){return this.files.push(t),this},s.prototype.reporter=function(t){if("function"==typeof t)this._reporter=t;else{t=t||"spec";var e;try{e=r("./reporters/"+t)}catch(n){}if(!e)try{e=r(t)}catch(n){}if(e||"teamcity"!==t||console.warn("The Teamcity reporter was moved to a package named mocha-teamcity-reporter (https://npmjs.org/package/mocha-teamcity-reporter)."),!e)throw new Error('invalid reporter "'+t+'"');this._reporter=e}return this},s.prototype.ui=function(t){if(t=t||"bdd",this._ui=e.interfaces[t],!this._ui)try{this._ui=r(t)}catch(n){}if(!this._ui)throw new Error('invalid interface "'+t+'"');return this._ui=this._ui(this.suite),this},s.prototype.loadFiles=function(t){var e=this,i=this.suite,o=this.files.length;this.files.forEach(function(s){s=a.resolve(s),i.emit("pre-require",n,s,e),i.emit("require",r(s),s,e),i.emit("post-require",n,s,e),--o||t&&t()})},s.prototype._growl=function(t,e){var n=r("growl");t.on("end",function(){var r=e.stats;if(r.failures){var o=r.failures+" of "+t.total+" tests failed";n(o,{name:"mocha",title:"Failed",image:i("error")})}else n(r.passes+" tests passed in "+r.duration+"ms",{name:"mocha",title:"Passed",image:i("ok")})})},s.prototype.grep=function(t){return this.options.grep="string"==typeof t?new RegExp(u(t)):t,this},s.prototype.invert=function(){return this.options.invert=!0,this},s.prototype.ignoreLeaks=function(t){return this.options.ignoreLeaks=!!t,this},s.prototype.checkLeaks=function(){return this.options.ignoreLeaks=!1,this},s.prototype.growl=function(){return this.options.growl=!0,this},s.prototype.globals=function(t){return this.options.globals=(this.options.globals||[]).concat(t),this},s.prototype.useColors=function(t){return this.options.useColors=arguments.length&&void 0!=t?t:!0,this},s.prototype.useInlineDiffs=function(t){return this.options.useInlineDiffs=arguments.length&&void 0!=t?t:!1,this},s.prototype.timeout=function(t){return this.suite.timeout(t),this},s.prototype.slow=function(t){return this.suite.slow(t),this},s.prototype.enableTimeouts=function(t){return this.suite.enableTimeouts(arguments.length&&void 0!==t?t:!0),this},s.prototype.asyncOnly=function(){return this.options.asyncOnly=!0,this},s.prototype.noHighlighting=function(){return this.options.noHighlighting=!0,this},s.prototype.run=function(t){this.files.length&&this.loadFiles();var n=this.suite,r=this.options;r.files=this.files;var i=new e.Runner(n),o=new this._reporter(i,r);return i.ignoreLeaks=!1!==r.ignoreLeaks,i.asyncOnly=r.asyncOnly,r.grep&&i.grep(r.grep,r.invert),r.globals&&i.globals(r.globals),r.growl&&this._growl(i,o),e.reporters.Base.useColors=r.useColors,e.reporters.Base.inlineDiffs=r.useInlineDiffs,i.run(t)}}),t.register("ms.js",function(t){function e(t){var e=/^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(t);if(e){var n=parseFloat(e[1]),r=(e[2]||"ms").toLowerCase();switch(r){case"years":case"year":case"y":return n*c;case"days":case"day":case"d":return n*u;case"hours":case"hour":case"h":return n*a;case"minutes":case"minute":case"m":return n*s;case"seconds":case"second":case"s":return n*o;case"ms":return n}}}function n(t){return t>=u?Math.round(t/u)+"d":t>=a?Math.round(t/a)+"h":t>=s?Math.round(t/s)+"m":t>=o?Math.round(t/o)+"s":t+"ms"}function r(t){return i(t,u,"day")||i(t,a,"hour")||i(t,s,"minute")||i(t,o,"second")||t+" ms"}function i(t,e,n){return e>t?void 0:1.5*e>t?Math.floor(t/e)+" "+n:Math.ceil(t/e)+" "+n+"s"}var o=1e3,s=60*o,a=60*s,u=24*a,c=365.25*u;t.exports=function(t,i){return i=i||{},"string"==typeof t?e(t):i["long"]?r(t):n(t)}}),t.register("reporters/base.js",function(t,e,r){function i(t){var e=this.stats={suites:0,tests:0,passes:0,pending:0,failures:0},n=this.failures=[];t&&(this.runner=t,t.stats=e,t.on("start",function(){e.start=new m}),t.on("suite",function(t){e.suites=e.suites||0,t.root||e.suites++}),t.on("test end",function(){e.tests=e.tests||0,e.tests++}),t.on("pass",function(t){e.passes=e.passes||0;var n=t.slow()/2;t.speed=t.duration>t.slow()?"slow":t.duration>n?"medium":"fast",e.passes++}),t.on("fail",function(t,r){e.failures=e.failures||0,e.failures++,t.err=r,n.push(t)}),t.on("end",function(){e.end=new m,e.duration=new m-e.start}),t.on("pending",function(){e.pending++}))}function s(t,e){return t=String(t),Array(e-t.length+1).join(" ")+t}function a(t,e){var n=c(t,"WordsWithSpace",e),r=n.split("\n");if(r.length>4){var i=String(r.length).length;n=r.map(function(t,e){return s(++e,i)+" | "+t}).join("\n")}return n="\n"+y("diff removed","actual")+" "+y("diff added","expected")+"\n\n"+n+"\n",n=n.replace(/^/gm," ")}function u(t,e){function n(t){return e&&(t=l(t)),"+"===t[0]?i+h("diff added",t):"-"===t[0]?i+h("diff removed",t):t.match(/\@\@/)?null:t.match(/\\ No newline/)?null:i+t}function r(t){return null!=t}var i=" ";msg=d.createPatch("string",t.actual,t.expected);var o=msg.split("\n").splice(4);return"\n "+h("diff added","+ expected")+" "+h("diff removed","- actual")+"\n\n"+o.map(n).filter(r).join("\n")}function c(t,e,n){var r=n?l(t.actual):t.actual,i=n?l(t.expected):t.expected;return d["diff"+e](r,i).map(function(t){return t.added?h("diff added",t.value):t.removed?h("diff removed",t.value):t.value}).join("")}function l(t){return t.replace(/\t/g,"").replace(/\r/g,"").replace(/\n/g,"\n")}function h(t,e){return e.split("\n").map(function(e){return y(t,e)}).join("\n")}function f(t,e){return t=Object.prototype.toString.call(t),e=Object.prototype.toString.call(e),t==e}var p=r("browser/tty"),d=r("browser/diff"),g=r("../ms"),v=r("../utils"),m=n.Date,b=(n.setTimeout,n.setInterval,n.clearTimeout,n.clearInterval,p.isatty(1)&&p.isatty(2));e=t.exports=i,e.useColors=b||void 0!==o.env.MOCHA_COLORS,e.inlineDiffs=!1,e.colors={pass:90,fail:31,"bright pass":92,"bright fail":91,"bright yellow":93,pending:36,suite:0,"error title":0,"error message":31,"error stack":90,checkmark:32,fast:90,medium:33,slow:31,green:32,light:90,"diff gutter":90,"diff added":42,"diff removed":41},e.symbols={ok:"✓",err:"✖",dot:"․"},"win32"==o.platform&&(e.symbols.ok="√",e.symbols.err="×",e.symbols.dot=".");var y=e.color=function(t,n){return e.useColors?"["+e.colors[t]+"m"+n+"":n};e.window={width:b?o.stdout.getWindowSize?o.stdout.getWindowSize(1)[0]:p.getWindowSize()[1]:75},e.cursor={hide:function(){b&&o.stdout.write("[?25l")},show:function(){b&&o.stdout.write("[?25h")},deleteLine:function(){b&&o.stdout.write("")},beginningOfLine:function(){b&&o.stdout.write("")},CR:function(){b?(e.cursor.deleteLine(),e.cursor.beginningOfLine()):o.stdout.write("\r")}},e.list=function(t){console.error(),t.forEach(function(t,n){var r=y("error title"," %s) %s:\n")+y("error message"," %s")+y("error stack","\n%s\n"),i=t.err,o=i.message||"",s=i.stack||o,c=s.indexOf(o)+o.length,l=s.slice(0,c),h=i.actual,p=i.expected,d=!0;if(i.uncaught&&(l="Uncaught "+l),i.showDiff&&f(h,p)&&(d=!1,i.actual=h=v.stringify(h),i.expected=p=v.stringify(p)),i.showDiff&&"string"==typeof h&&"string"==typeof p){r=y("error title"," %s) %s:\n%s")+y("error stack","\n%s\n");var g=o.match(/^([^:]+): expected/);l="\n "+y("error message",g?g[1]:l),l+=e.inlineDiffs?a(i,d):u(i,d)}s=s.slice(c?c+1:c).replace(/^/gm," "),console.error(r,n+1,t.fullTitle(),l,s)})},i.prototype.epilogue=function(){var t,e=this.stats;console.log(),t=y("bright pass"," ")+y("green"," %d passing")+y("light"," (%s)"),console.log(t,e.passes||0,g(e.duration)),e.pending&&(t=y("pending"," ")+y("pending"," %d pending"),console.log(t,e.pending)),e.failures&&(t=y("fail"," %d failing"),console.error(t,e.failures),i.list(this.failures),console.error()),console.log()}}),t.register("reporters/doc.js",function(t,e,n){function r(t){function e(){return Array(n).join(" ")}i.call(this,t);var n=(this.stats,t.total,2);t.on("suite",function(t){t.root||(++n,console.log('%s
',e()),++n,console.log("%s

%s

",e(),o.escape(t.title)),console.log("%s
",e()))}),t.on("suite end",function(t){t.root||(console.log("%s
",e()),--n,console.log("%s
",e()),--n)}),t.on("pass",function(t){console.log("%s
%s
",e(),o.escape(t.title));var n=o.escape(o.clean(t.fn.toString()));console.log("%s
%s
",e(),n)}),t.on("fail",function(t,n){console.log('%s
%s
',e(),o.escape(t.title));var r=o.escape(o.clean(t.fn.toString()));console.log('%s
%s
',e(),r),console.log('%s
%s
',e(),o.escape(n))})}var i=n("./base"),o=n("../utils");e=t.exports=r}),t.register("reporters/dot.js",function(t,e,n){function r(t){s.call(this,t);var e=this,n=(this.stats,.75*s.window.width|0),r=-1;t.on("start",function(){o.stdout.write("\n ")}),t.on("pending",function(){++r%n==0&&o.stdout.write("\n "),o.stdout.write(a("pending",s.symbols.dot))}),t.on("pass",function(t){++r%n==0&&o.stdout.write("\n "),o.stdout.write("slow"==t.speed?a("bright yellow",s.symbols.dot):a(t.speed,s.symbols.dot))}),t.on("fail",function(){++r%n==0&&o.stdout.write("\n "),o.stdout.write(a("fail",s.symbols.dot))}),t.on("end",function(){console.log(),e.epilogue()})}function i(){}var s=n("./base"),a=s.color;e=t.exports=r,i.prototype=s.prototype,r.prototype=new i,r.prototype.constructor=r}),t.register("reporters/html-cov.js",function(t,e,n){function r(t){var e=n("jade"),r=__dirname+"/templates/coverage.jade",u=a.readFileSync(r,"utf8"),c=e.compile(u,{filename:r}),l=this;s.call(this,t,!1),t.on("end",function(){o.stdout.write(c({cov:l.cov,coverageClass:i}))})}function i(t){return t>=75?"high":t>=50?"medium":t>=25?"low":"terrible"}var s=n("./json-cov"),a=n("browser/fs");e=t.exports=r}),t.register("reporters/html.js",function(t,e,r){function i(t){h.call(this,t);var e,n,r=this,i=this.stats,m=(t.total,s(v)),b=m.getElementsByTagName("li"),y=b[1].getElementsByTagName("em")[0],w=b[1].getElementsByTagName("a")[0],x=b[2].getElementsByTagName("em")[0],j=b[2].getElementsByTagName("a")[0],E=b[3].getElementsByTagName("em")[0],T=m.getElementsByTagName("canvas")[0],k=s('
    '),q=[k],_=document.getElementById("mocha");if(T.getContext){var O=window.devicePixelRatio||1;T.style.width=T.width,T.style.height=T.height,T.width*=O,T.height*=O,n=T.getContext("2d"),n.scale(O,O),e=new p}return _?(l(w,"click",function(){u();var t=/pass/.test(k.className)?"":" pass";k.className=k.className.replace(/fail|pass/g,"")+t,k.className.trim()&&a("test pass")}),l(j,"click",function(){u();var t=/fail/.test(k.className)?"":" fail";k.className=k.className.replace(/fail|pass/g,"")+t,k.className.trim()&&a("test fail")}),_.appendChild(m),_.appendChild(k),e&&e.size(40),t.on("suite",function(t){if(!t.root){var e=r.suiteURL(t),n=s('
  • %s

  • ',e,d(t.title));q[0].appendChild(n),q.unshift(document.createElement("ul")),n.appendChild(q[0])}}),t.on("suite end",function(t){t.root||q.shift()}),t.on("fail",function(e){"hook"==e.type&&t.emit("test end",e)}),void t.on("test end",function(t){var o=i.tests/this.total*100|0;e&&e.update(o).draw(n);var a=new g-i.start;if(c(y,i.passes),c(x,i.failures),c(E,(a/1e3).toFixed(2)),"passed"==t.state)var u=r.testURL(t),h=s('
  • %e%ems

  • ',t.speed,t.title,t.duration,u);else if(t.pending)var h=s('
  • %e

  • ',t.title);else{var h=s('
  • %e

  • ',t.title,encodeURIComponent(t.fullTitle())),p=t.err.stack||t.err.toString();~p.indexOf(t.err.message)||(p=t.err.message+"\n"+p),"[object Error]"==p&&(p=t.err.message),!t.err.stack&&t.err.sourceURL&&void 0!==t.err.line&&(p+="\n("+t.err.sourceURL+":"+t.err.line+")"),h.appendChild(s('
    %e
    ',p))}if(!t.pending){var d=h.getElementsByTagName("h2")[0];l(d,"click",function(){v.style.display="none"==v.style.display?"block":"none"});var v=s("
    %e
    ",f.clean(t.fn.toString()));h.appendChild(v),v.style.display="none"}q[0]&&q[0].appendChild(h)})):o("#mocha div missing, add it to your document")}function o(t){document.body.appendChild(s('
    %s
    ',t))}function s(t){var e=arguments,n=document.createElement("div"),r=1;return n.innerHTML=t.replace(/%([se])/g,function(t,n){switch(n){case"s":return String(e[r++]);case"e":return d(e[r++])}}),n.firstChild}function a(t){for(var e=document.getElementsByClassName("suite"),n=0;n0&&(e.coverage=e.hits/e.sloc*100),e}function a(t,e){var n={filename:t,coverage:0,hits:0,misses:0,sloc:0,source:{}};return e.source.forEach(function(t,r){r++,0===e[r]?(n.misses++,n.sloc++):void 0!==e[r]&&(n.hits++,n.sloc++),n.source[r]={source:t,coverage:void 0===e[r]?"":e[r]}}),n.coverage=n.hits/n.sloc*100,n}function u(t){return{title:t.title,fullTitle:t.fullTitle(),duration:t.duration}}var c=r("./base");e=t.exports=i}),t.register("reporters/json-stream.js",function(t,e,n){function r(t){s.call(this,t);var e=this,n=(this.stats,t.total);t.on("start",function(){console.log(JSON.stringify(["start",{total:n}]))}),t.on("pass",function(t){console.log(JSON.stringify(["pass",i(t)]))}),t.on("fail",function(t,e){t=i(t),t.err=e.message,console.log(JSON.stringify(["fail",t]))}),t.on("end",function(){o.stdout.write(JSON.stringify(["end",e.stats]))})}function i(t){return{title:t.title,fullTitle:t.fullTitle(),duration:t.duration}}{var s=n("./base");s.color}e=t.exports=r}),t.register("reporters/json.js",function(t,e,n){function r(t){var e=this;a.call(this,t);var n=[],r=[],s=[],u=[];t.on("test end",function(t){n.push(t)}),t.on("pass",function(t){u.push(t)}),t.on("fail",function(t){s.push(t)}),t.on("pending",function(t){r.push(t)}),t.on("end",function(){var a={stats:e.stats,tests:n.map(i),pending:r.map(i),failures:s.map(i),passes:u.map(i)};t.testResults=a,o.stdout.write(JSON.stringify(a,null,2))})}function i(t){return{title:t.title,fullTitle:t.fullTitle(),duration:t.duration,err:s(t.err||{})}}function s(t){var e={};return Object.getOwnPropertyNames(t).forEach(function(n){e[n]=t[n]},t),e}{var a=n("./base");a.cursor,a.color}e=t.exports=r}),t.register("reporters/landing.js",function(t,e,n){function r(t){function e(){var t=Array(r).join("-");return" "+u("runway",t)}s.call(this,t);var n=this,r=(this.stats,.75*s.window.width|0),i=t.total,c=o.stdout,l=u("plane","✈"),h=-1,f=0;t.on("start",function(){c.write("\n\n\n "),a.hide()}),t.on("test end",function(t){var n=-1==h?r*++f/i|0:h;"failed"==t.state&&(l=u("plane crash","✈"),h=n),c.write("["+(r+1)+"D"),c.write(e()),c.write("\n "),c.write(u("runway",Array(n).join("⋅"))),c.write(l),c.write(u("runway",Array(r-n).join("⋅")+"\n")),c.write(e()),c.write("")}),t.on("end",function(){a.show(),console.log(),n.epilogue()})}function i(){}var s=n("./base"),a=s.cursor,u=s.color;e=t.exports=r,s.colors.plane=0,s.colors["plane crash"]=31,s.colors.runway=90,i.prototype=s.prototype,r.prototype=new i,r.prototype.constructor=r}),t.register("reporters/list.js",function(t,e,n){function r(t){s.call(this,t);var e=this,n=(this.stats,0);t.on("start",function(){console.log()}),t.on("test",function(t){o.stdout.write(u("pass"," "+t.fullTitle()+": "))}),t.on("pending",function(t){var e=u("checkmark"," -")+u("pending"," %s");console.log(e,t.fullTitle())}),t.on("pass",function(t){var e=u("checkmark"," "+s.symbols.dot)+u("pass"," %s: ")+u(t.speed,"%dms");a.CR(),console.log(e,t.fullTitle(),t.duration)}),t.on("fail",function(t){a.CR(),console.log(u("fail"," %d) %s"),++n,t.fullTitle())}),t.on("end",e.epilogue.bind(e))}function i(){}var s=n("./base"),a=s.cursor,u=s.color;e=t.exports=r,i.prototype=s.prototype,r.prototype=new i,r.prototype.constructor=r}),t.register("reporters/markdown.js",function(t,e,n){function r(t){function e(t){return Array(u).join("#")+" "+t}function n(t,e){var r=e;return e=e[t.title]=e[t.title]||{suite:t},t.suites.forEach(function(t){n(t,e)}),r}function r(t,e){++e;var n,i="";for(var o in t)"suite"!=o&&(o&&(n=" - ["+o+"](#"+s.slug(t[o].suite.fullTitle())+")\n"),o&&(i+=Array(e).join(" ")+n),i+=r(t[o],e));return--e,i}function a(t){var e=n(t,{});return r(e,0)}i.call(this,t);var u=(this.stats,0),c="";a(t.suite),t.on("suite",function(t){++u;var n=s.slug(t.fullTitle());c+='\n',c+=e(t.title)+"\n"}),t.on("suite end",function(){--u}),t.on("pass",function(t){var e=s.clean(t.fn.toString());c+=t.title+".\n",c+="\n```js\n",c+=e+"\n",c+="```\n\n"}),t.on("end",function(){o.stdout.write("# TOC\n"),o.stdout.write(a(t.suite)),o.stdout.write(c) +})}var i=n("./base"),s=n("../utils");e=t.exports=r}),t.register("reporters/min.js",function(t,e,n){function r(t){s.call(this,t),t.on("start",function(){o.stdout.write(""),o.stdout.write("")}),t.on("end",this.epilogue.bind(this))}function i(){}var s=n("./base");e=t.exports=r,i.prototype=s.prototype,r.prototype=new i,r.prototype.constructor=r}),t.register("reporters/nyan.js",function(t,e,n){function r(t){a.call(this,t);{var e=this,n=(this.stats,.75*a.window.width|0),r=(this.rainbowColors=e.generateColors(),this.colorIndex=0,this.numberOfLines=4,this.trajectories=[[],[],[],[]],this.nyanCatWidth=11);this.trajectoryWidthMax=n-r,this.scoreboardWidth=5,this.tick=0}t.on("start",function(){a.cursor.hide(),e.draw()}),t.on("pending",function(){e.draw()}),t.on("pass",function(){e.draw()}),t.on("fail",function(){e.draw()}),t.on("end",function(){a.cursor.show();for(var t=0;t=this.trajectoryWidthMax&&r.shift(),r.push(e)}},r.prototype.drawRainbow=function(){var t=this;this.trajectories.forEach(function(e){i("["+t.scoreboardWidth+"C"),i(e.join("")),i("\n")}),this.cursorUp(this.numberOfLines)},r.prototype.drawNyanCat=function(){var t=this,e=this.scoreboardWidth+this.trajectories[0].length,n="["+e+"C",r="";i(n),i("_,------,"),i("\n"),i(n),r=t.tick?" ":" ",i("_|"+r+"/\\_/\\ "),i("\n"),i(n),r=t.tick?"_":"__";var o=t.tick?"~":"^";i(o+"|"+r+this.face()+" "),i("\n"),i(n),r=t.tick?" ":" ",i(r+'"" "" '),i("\n"),this.cursorUp(this.numberOfLines)},r.prototype.face=function(){var t=this.stats;return t.failures?"( x .x)":t.pending?"( o .o)":t.passes?"( ^ .^)":"( - .-)"},r.prototype.cursorUp=function(t){i("["+t+"A")},r.prototype.cursorDown=function(t){i("["+t+"B")},r.prototype.generateColors=function(){for(var t=[],e=0;42>e;e++){var n=Math.floor(Math.PI/3),r=e*(1/6),i=Math.floor(3*Math.sin(r)+3),o=Math.floor(3*Math.sin(r+2*n)+3),s=Math.floor(3*Math.sin(r+4*n)+3);t.push(36*i+6*o+s+16)}return t},r.prototype.rainbowify=function(t){var e=this.rainbowColors[this.colorIndex%this.rainbowColors.length];return this.colorIndex+=1,"[38;5;"+e+"m"+t+""},s.prototype=a.prototype,r.prototype=new s,r.prototype.constructor=r}),t.register("reporters/progress.js",function(t,e,n){function r(t,e){s.call(this,t);var n=this,e=e||{},r=(this.stats,.5*s.window.width|0),i=t.total,c=0,l=(Math.max,-1);e.open=e.open||"[",e.complete=e.complete||"▬",e.incomplete=e.incomplete||s.symbols.dot,e.close=e.close||"]",e.verbose=!1,t.on("start",function(){console.log(),a.hide()}),t.on("test end",function(){c++;var t=c/i,n=r*t|0,s=r-n;(l!==n||e.verbose)&&(l=n,a.CR(),o.stdout.write(""),o.stdout.write(u("progress"," "+e.open)),o.stdout.write(Array(n).join(e.complete)),o.stdout.write(Array(s).join(e.incomplete)),o.stdout.write(u("progress",e.close)),e.verbose&&o.stdout.write(u("progress"," "+c+" of "+i)))}),t.on("end",function(){a.show(),console.log(),n.epilogue()})}function i(){}var s=n("./base"),a=s.cursor,u=s.color;e=t.exports=r,s.colors.progress=90,i.prototype=s.prototype,r.prototype=new i,r.prototype.constructor=r}),t.register("reporters/spec.js",function(t,e,n){function r(t){function e(){return Array(r).join(" ")}o.call(this,t);var n=this,r=(this.stats,0),i=0;t.on("start",function(){console.log()}),t.on("suite",function(t){++r,console.log(a("suite","%s%s"),e(),t.title)}),t.on("suite end",function(){--r,1==r&&console.log()}),t.on("pending",function(t){var n=e()+a("pending"," - %s");console.log(n,t.title)}),t.on("pass",function(t){if("fast"==t.speed){var n=e()+a("checkmark"," "+o.symbols.ok)+a("pass"," %s ");s.CR(),console.log(n,t.title)}else{var n=e()+a("checkmark"," "+o.symbols.ok)+a("pass"," %s ")+a(t.speed,"(%dms)");s.CR(),console.log(n,t.title,t.duration)}}),t.on("fail",function(t){s.CR(),console.log(e()+a("fail"," %d) %s"),++i,t.title)}),t.on("end",n.epilogue.bind(n))}function i(){}var o=n("./base"),s=o.cursor,a=o.color;e=t.exports=r,i.prototype=o.prototype,r.prototype=new i,r.prototype.constructor=r}),t.register("reporters/tap.js",function(t,e,n){function r(t){o.call(this,t);var e=(this.stats,1),n=0,r=0;t.on("start",function(){var e=t.grepTotal(t.suite);console.log("%d..%d",1,e)}),t.on("test end",function(){++e}),t.on("pending",function(t){console.log("ok %d %s # SKIP -",e,i(t))}),t.on("pass",function(t){n++,console.log("ok %d %s",e,i(t))}),t.on("fail",function(t,n){r++,console.log("not ok %d %s",e,i(t)),n.stack&&console.log(n.stack.replace(/^/gm," "))}),t.on("end",function(){console.log("# tests "+(n+r)),console.log("# pass "+n),console.log("# fail "+r)})}function i(t){return t.fullTitle().replace(/#/g,"")}{var o=n("./base");o.cursor,o.color}e=t.exports=r}),t.register("reporters/xunit.js",function(t,e,r){function i(t){c.call(this,t);var e=this.stats,n=[];t.on("pending",function(t){n.push(t)}),t.on("pass",function(t){n.push(t)}),t.on("fail",function(t){n.push(t)}),t.on("end",function(){console.log(a("testsuite",{name:"Mocha Tests",tests:e.tests,failures:e.failures,errors:e.failures,skipped:e.tests-e.failures-e.passes,timestamp:(new f).toUTCString(),time:e.duration/1e3||0},!1)),n.forEach(s),console.log("")})}function o(){}function s(t){var e={classname:t.parent.fullTitle(),name:t.title,time:t.duration/1e3||0};if("failed"==t.state){var n=t.err;console.log(a("testcase",e,!1,a("failure",{},!1,u(h(n.message)+"\n"+n.stack))))}else console.log(t.pending?a("testcase",e,!1,a("skipped",{},!0)):a("testcase",e,!0))}function a(t,e,n,r){var i,o=n?"/>":">",s=[];for(var a in e)s.push(a+'="'+h(e[a])+'"');return i="<"+t+(s.length?" "+s.join(" "):"")+o,r&&(i+=r+""}{var c=r("./base"),l=r("../utils"),h=l.escape,f=n.Date;n.setTimeout,n.setInterval,n.clearTimeout,n.clearInterval}e=t.exports=i,o.prototype=c.prototype,i.prototype=new o,i.prototype.constructor=i}),t.register("runnable.js",function(t,e,r){function i(t,e){this.title=t,this.fn=e,this.async=e&&e.length,this.sync=!this.async,this._timeout=2e3,this._slow=75,this._enableTimeouts=!0,this.timedOut=!1,this._trace=new Error("done() called multiple times")}function o(){}var s=r("browser/events").EventEmitter,a=r("browser/debug")("mocha:runnable"),u=r("./ms"),c=n.Date,l=n.setTimeout,h=(n.setInterval,n.clearTimeout),f=(n.clearInterval,Object.prototype.toString);t.exports=i,o.prototype=s.prototype,i.prototype=new o,i.prototype.constructor=i,i.prototype.timeout=function(t){return 0==arguments.length?this._timeout:(0===t&&(this._enableTimeouts=!1),"string"==typeof t&&(t=u(t)),a("timeout %d",t),this._timeout=t,this.timer&&this.resetTimeout(),this)},i.prototype.slow=function(t){return 0===arguments.length?this._slow:("string"==typeof t&&(t=u(t)),a("timeout %d",t),this._slow=t,this)},i.prototype.enableTimeouts=function(t){return 0===arguments.length?this._enableTimeouts:(a("enableTimeouts %s",t),this._enableTimeouts=t,this)},i.prototype.fullTitle=function(){return this.parent.fullTitle()+" "+this.title},i.prototype.clearTimeout=function(){h(this.timer)},i.prototype.inspect=function(){return JSON.stringify(this,function(t,e){return"_"!=t[0]?"parent"==t?"#":"ctx"==t?"#":e:void 0},2)},i.prototype.resetTimeout=function(){var t=this,e=this.timeout()||1e9;this._enableTimeouts&&(this.clearTimeout(),this.timer=l(function(){t._enableTimeouts&&(t.callback(new Error("timeout of "+e+"ms exceeded")),t.timedOut=!0)},e))},i.prototype.globals=function(t){this._allowedGlobals=t},i.prototype.run=function(t){function e(t){o||(o=!0,s.emit("error",t||new Error("done() called multiple times; stacktrace may be inaccurate")))}function n(n){var r=s.timeout();if(!s.timedOut){if(i)return e(n||s._trace);s.clearTimeout(),s.duration=new c-a,i=!0,!n&&s.duration>r&&s._enableTimeouts&&(n=new Error("timeout of "+r+"ms exceeded")),t(n)}}function r(t){var e=t.call(u);e&&"function"==typeof e.then?(s.resetTimeout(),e.then(function(){n()},function(t){n(t||new Error("Promise rejected with no or falsy reason"))})):n()}var i,o,s=this,a=new c,u=this.ctx;if(u&&u.runnable&&u.runnable(this),this.callback=n,this.async){this.resetTimeout();try{this.fn.call(u,function(t){return t instanceof Error||"[object Error]"===f.call(t)?n(t):null!=t?n("[object Object]"===Object.prototype.toString.call(t)?new Error("done() invoked with non-Error: "+JSON.stringify(t)):new Error("done() invoked with non-Error: "+t)):void n()})}catch(l){n(l)}}else{if(this.asyncOnly)return n(new Error("--async-only option in use without declaring `done()`"));try{this.pending?n():r(this.fn)}catch(l){n(l)}}}}),t.register("runner.js",function(t,e,r){function i(t){var e=this;this._globals=[],this._abort=!1,this.suite=t,this.total=t.total(),this.failures=0,this.on("test end",function(t){e.checkGlobals(t)}),this.on("hook end",function(t){e.checkGlobals(t)}),this.grep(/.*/),this.globals(this.globalProps().concat(u()))}function s(){}function a(t,e){return f(e,function(e){if(/^d+/.test(e))return!1;if(n.navigator&&/^getInterface/.test(e))return!1;if(n.navigator&&/^\d+/.test(e))return!1;if(/^mocha-/.test(e))return!1;var r=f(t,function(t){return~t.indexOf("*")?0==e.indexOf(t.split("*")[0]):e==t});return 0==r.length&&(!n.navigator||"onerror"!==e)})}function u(){if("object"==typeof o&&"string"==typeof o.version){var t=o.version.split(".").reduce(function(t,e){return t<<8|e});if(2315>t)return["errno"]}return[]}var c=r("browser/events").EventEmitter,l=r("browser/debug")("mocha:runner"),h=(r("./test"),r("./utils")),f=h.filter,p=(h.keys,["setTimeout","clearTimeout","setInterval","clearInterval","XMLHttpRequest","Date"]);t.exports=i,i.immediately=n.setImmediate||o.nextTick,s.prototype=c.prototype,i.prototype=new s,i.prototype.constructor=i,i.prototype.grep=function(t,e){return l("grep %s",t),this._grep=t,this._invert=e,this.total=this.grepTotal(this.suite),this},i.prototype.grepTotal=function(t){var e=this,n=0;return t.eachTest(function(t){var r=e._grep.test(t.fullTitle());e._invert&&(r=!r),r&&n++}),n},i.prototype.globalProps=function(){for(var t=h.keys(n),e=0;e1?this.fail(t,new Error("global leaks detected: "+e.join(", "))):e.length&&this.fail(t,new Error("global leak detected: "+e[0])))}},i.prototype.fail=function(t,e){++this.failures,t.state="failed","string"==typeof e&&(e=new Error('the string "'+e+'" was thrown, throw an Error :)')),this.emit("fail",t,e)},i.prototype.failHook=function(t,e){this.fail(t,e),this.suite.bail()&&this.emit("end")},i.prototype.hook=function(t,e){function n(t){var i=o[t];return i?s.failures&&r.bail()?e():(s.currentRunnable=i,i.ctx.currentTest=s.test,s.emit("hook",i),i.on("error",function(t){s.failHook(i,t)}),void i.run(function(r){i.removeAllListeners("error");var o=i.error();return o&&s.fail(s.test,o),r?(s.failHook(i,r),e(r)):(s.emit("hook end",i),delete i.ctx.currentTest,void n(++t))})):e()}var r=this.suite,o=r["_"+t],s=this;i.immediately(function(){n(0)})},i.prototype.hooks=function(t,e,n){function r(s){return i.suite=s,s?void i.hook(t,function(t){if(t){var s=i.suite;return i.suite=o,n(t,s)}r(e.pop())}):(i.suite=o,n())}var i=this,o=this.suite;r(e.pop())},i.prototype.hookUp=function(t,e){var n=[this.suite].concat(this.parents()).reverse();this.hooks(t,n,e)},i.prototype.hookDown=function(t,e){var n=[this.suite].concat(this.parents());this.hooks(t,n,e)},i.prototype.parents=function(){for(var t=this.suite,e=[];t=t.parent;)e.push(t);return e},i.prototype.runTest=function(t){var e=this.test,n=this;this.asyncOnly&&(e.asyncOnly=!0);try{e.on("error",function(t){n.fail(e,t)}),e.run(t)}catch(r){t(r)}},i.prototype.runTests=function(t,e){function n(t,r,i){var s=o.suite;o.suite=i?r.parent:r,o.suite?o.hookUp("afterEach",function(t,i){return o.suite=s,t?n(t,i,!0):void e(r)}):(o.suite=s,e(r))}function r(a,u){if(o.failures&&t._bail)return e();if(o._abort)return e();if(a)return n(a,u,!0);if(i=s.shift(),!i)return e();var c=o._grep.test(i.fullTitle());return o._invert&&(c=!c),c?i.pending?(o.emit("pending",i),o.emit("test end",i),r()):(o.emit("test",o.test=i),void o.hookDown("beforeEach",function(t,e){return t?n(t,e,!1):(o.currentRunnable=o.test,void o.runTest(function(t){return i=o.test,t?(o.fail(i,t),o.emit("test end",i),o.hookUp("afterEach",r)):(i.state="passed",o.emit("pass",i),o.emit("test end",i),void o.hookUp("afterEach",r))}))})):r()}var i,o=this,s=t.tests.slice();this.next=r,r()},i.prototype.runSuite=function(t,e){function n(e){if(e)return e==t?r():r(e);if(o._abort)return r();var i=t.suites[s++];return i?void o.runSuite(i,n):r()}function r(n){o.suite=t,o.hook("afterAll",function(){o.emit("suite end",t),e(n)})}var i=this.grepTotal(t),o=this,s=0;return l("run suite %s",t.fullTitle()),i?(this.emit("suite",this.suite=t),void this.hook("beforeAll",function(e){return e?r():void o.runTests(t,n)})):e()},i.prototype.uncaught=function(t){t?l("uncaught exception %s",t!==function(){return this}.call(t)?t:t.message||t):(l("uncaught undefined exception"),t=new Error("Caught undefined error, did you throw without specifying what?")),t.uncaught=!0;var e=this.currentRunnable;if(e){var n=e.state;if(this.fail(e,t),e.clearTimeout(),!n)return"test"==e.type?(this.emit("test end",e),void this.hookUp("afterEach",this.next)):void this.emit("end")}},i.prototype.run=function(t){function e(t){n.uncaught(t)}var n=this,t=t||function(){};return l("start"),this.on("end",function(){l("end"),o.removeListener("uncaughtException",e),t(n.failures)}),this.emit("start"),this.runSuite(this.suite,function(){l("finished running"),n.emit("end")}),o.on("uncaughtException",e),this},i.prototype.abort=function(){l("aborting"),this._abort=!0}}),t.register("suite.js",function(t,e,n){function r(t,e){this.title=t;var n=function(){};n.prototype=e,this.ctx=new n,this.suites=[],this.tests=[],this.pending=!1,this._beforeEach=[],this._beforeAll=[],this._afterEach=[],this._afterAll=[],this.root=!t,this._timeout=2e3,this._enableTimeouts=!0,this._slow=75,this._bail=!1}function i(){}var o=n("browser/events").EventEmitter,s=n("browser/debug")("mocha:suite"),a=n("./ms"),u=n("./utils"),c=n("./hook");e=t.exports=r,e.create=function(t,e){var n=new r(e,t.ctx);return n.parent=t,t.pending&&(n.pending=!0),e=n.fullTitle(),t.addSuite(n),n},i.prototype=o.prototype,r.prototype=new i,r.prototype.constructor=r,r.prototype.clone=function(){var t=new r(this.title);return s("clone"),t.ctx=this.ctx,t.timeout(this.timeout()),t.enableTimeouts(this.enableTimeouts()),t.slow(this.slow()),t.bail(this.bail()),t},r.prototype.timeout=function(t){return 0==arguments.length?this._timeout:(0===t&&(this._enableTimeouts=!1),"string"==typeof t&&(t=a(t)),s("timeout %d",t),this._timeout=parseInt(t,10),this)},r.prototype.enableTimeouts=function(t){return 0===arguments.length?this._enableTimeouts:(s("enableTimeouts %s",t),this._enableTimeouts=t,this)},r.prototype.slow=function(t){return 0===arguments.length?this._slow:("string"==typeof t&&(t=a(t)),s("slow %d",t),this._slow=t,this)},r.prototype.bail=function(t){return 0==arguments.length?this._bail:(s("bail %s",t),this._bail=t,this)},r.prototype.beforeAll=function(t,e){if(this.pending)return this;"function"==typeof t&&(e=t,t=e.name),t='"before all" hook'+(t?": "+t:"");var n=new c(t,e);return n.parent=this,n.timeout(this.timeout()),n.enableTimeouts(this.enableTimeouts()),n.slow(this.slow()),n.ctx=this.ctx,this._beforeAll.push(n),this.emit("beforeAll",n),this},r.prototype.afterAll=function(t,e){if(this.pending)return this;"function"==typeof t&&(e=t,t=e.name),t='"after all" hook'+(t?": "+t:"");var n=new c(t,e);return n.parent=this,n.timeout(this.timeout()),n.enableTimeouts(this.enableTimeouts()),n.slow(this.slow()),n.ctx=this.ctx,this._afterAll.push(n),this.emit("afterAll",n),this},r.prototype.beforeEach=function(t,e){if(this.pending)return this;"function"==typeof t&&(e=t,t=e.name),t='"before each" hook'+(t?": "+t:"");var n=new c(t,e);return n.parent=this,n.timeout(this.timeout()),n.enableTimeouts(this.enableTimeouts()),n.slow(this.slow()),n.ctx=this.ctx,this._beforeEach.push(n),this.emit("beforeEach",n),this},r.prototype.afterEach=function(t,e){if(this.pending)return this;"function"==typeof t&&(e=t,t=e.name),t='"after each" hook'+(t?": "+t:"");var n=new c(t,e);return n.parent=this,n.timeout(this.timeout()),n.enableTimeouts(this.enableTimeouts()),n.slow(this.slow()),n.ctx=this.ctx,this._afterEach.push(n),this.emit("afterEach",n),this},r.prototype.addSuite=function(t){return t.parent=this,t.timeout(this.timeout()),t.enableTimeouts(this.enableTimeouts()),t.slow(this.slow()),t.bail(this.bail()),this.suites.push(t),this.emit("suite",t),this},r.prototype.addTest=function(t){return t.parent=this,t.timeout(this.timeout()),t.enableTimeouts(this.enableTimeouts()),t.slow(this.slow()),t.ctx=this.ctx,this.tests.push(t),this.emit("test",t),this},r.prototype.fullTitle=function(){if(this.parent){var t=this.parent.fullTitle();if(t)return t+" "+this.title}return this.title},r.prototype.total=function(){return u.reduce(this.suites,function(t,e){return t+e.total()},0)+this.tests.length},r.prototype.eachTest=function(t){return u.forEach(this.tests,t),u.forEach(this.suites,function(e){e.eachTest(t)}),this}}),t.register("test.js",function(t,e,n){function r(t,e){o.call(this,t,e),this.pending=!e,this.type="test"}function i(){}var o=n("./runnable");t.exports=r,i.prototype=o.prototype,r.prototype=new i,r.prototype.constructor=r}),t.register("utils.js",function(t,e,n){function r(t){return!~f.indexOf(t)}function i(t){return t.replace(//g,">").replace(/\/\/(.*)/gm,'//$1').replace(/('.*?')/gm,'$1').replace(/(\d+\.\d+)/gm,'$1').replace(/(\d+)/gm,'$1').replace(/\bnew[ \t]+(\w+)/gm,'new $1').replace(/\b(function|new|throw|return|var|if|else)\b/gm,'$1')}var o=n("browser/fs"),s=n("browser/path"),a=s.basename,u=o.existsSync||s.existsSync,c=n("browser/glob"),l=s.join,h=n("browser/debug")("mocha:watch"),f=["node_modules",".git"];e.escape=function(t){return String(t).replace(/&/g,"&").replace(/"/g,""").replace(//g,">")},e.forEach=function(t,e,n){for(var r=0,i=t.length;i>r;r++)e.call(n,t[r],r)},e.map=function(t,e,n){for(var r=[],i=0,o=t.length;o>i;i++)r.push(e.call(n,t[i],i));return r},e.indexOf=function(t,e,n){for(var r=n||0,i=t.length;i>r;r++)if(t[r]===e)return r;return-1},e.reduce=function(t,e,n){for(var r=n,i=0,o=t.length;o>i;i++)r=e(r,t[i],i,t);return r},e.filter=function(t,e){for(var n=[],r=0,i=t.length;i>r;r++){var o=t[r];e(o,r,t)&&n.push(o)}return n},e.keys=Object.keys||function(t){var e=[],n=Object.prototype.hasOwnProperty;for(var r in t)n.call(t,r)&&e.push(r);return e},e.watch=function(t,e){var n={interval:100};t.forEach(function(t){h("file %s",t),o.watchFile(t,n,function(n,r){r.mtime *{?/,"").replace(/\s+\}$/,"");var n=t.match(/^\n?( *)/)[1].length,r=t.match(/^\n?(\t*)/)[1].length,i=new RegExp("^\n?"+(r?" ":" ")+"{"+(r?r:n)+"}","gm");return t=t.replace(i,""),e.trim(t)},e.trim=function(t){return t.replace(/^\s+|\s+$/g,"")},e.parseQuery=function(t){return e.reduce(t.replace("?","").split("&"),function(t,e){var n=e.indexOf("="),r=e.slice(0,n),i=e.slice(++n);return t[r]=decodeURIComponent(i),t},{})},e.highlightTags=function(t){for(var e=document.getElementById("mocha").getElementsByTagName(t),n=0,r=e.length;r>n;++n)e[n].innerHTML=i(e[n].innerHTML)},e.stringify=function(t){return t instanceof RegExp?t.toString():JSON.stringify(e.canonicalize(t),null,2).replace(/,(\n|$)/g,"$1")},e.canonicalize=function(t,n){if(n=n||[],-1!==e.indexOf(n,t))return"[Circular]";var r;return"[object Array]"==={}.toString.call(t)?(n.push(t),r=e.map(t,function(t){return e.canonicalize(t,n)}),n.pop()):"object"==typeof t&&null!==t?(n.push(t),r={},e.forEach(e.keys(t).sort(),function(i){r[i]=e.canonicalize(t[i],n)}),n.pop()):r=t,r},e.lookupFiles=function p(t,e,n){var r=[],i=new RegExp("\\.("+e.join("|")+")$");if(!u(t)){if(!u(t+".js")){if(r=c.sync(t),!r.length)throw new Error("cannot resolve path (or pattern) '"+t+"'");return r}t+=".js"}try{var s=o.statSync(t);if(s.isFile())return t}catch(h){return}return o.readdirSync(t).forEach(function(s){s=l(t,s);try{var u=o.statSync(s);if(u.isDirectory())return void(n&&(r=r.concat(p(s,e,n))))}catch(c){return}u.isFile()&&i.test(s)&&"."!==a(s)[0]&&r.push(s)}),r}});var n=function(){return this}(),r=n.Date,i=n.setTimeout,o=(n.setInterval,n.clearTimeout,n.clearInterval,{});o.exit=function(){},o.stdout={};var s=[],a=n.onerror;o.removeListener=function(t,e){if("uncaughtException"==t){n.onerror=a?a:function(){};var r=u.utils.indexOf(s,e);-1!=r&&s.splice(r,1)}},o.on=function(t,e){"uncaughtException"==t&&(n.onerror=function(t,n,r){return e(new Error(t+" ("+n+":"+r+")")),!0},s.push(e))};var u=n.Mocha=t("mocha"),c=n.mocha=new u({reporter:"html"});c.suite.removeAllListeners("pre-require");var l,h=[];u.Runner.immediately=function(t){h.push(t),l||(l=i(e,0))},c.throwError=function(t){throw u.utils.forEach(s,function(e){e(t)}),t},c.ui=function(t){return u.prototype.ui.call(this,t),this.suite.emit("pre-require",n,null,this),this},c.setup=function(t){"string"==typeof t&&(t={ui:t});for(var e in t)this[e](t[e]);return this},c.run=function(t){var e=c.options;c.globals("location");var r=u.utils.parseQuery(n.location.search||"");return r.grep&&c.grep(r.grep),r.invert&&c.invert(),u.prototype.run.call(c,function(r){var i=n.document;i&&i.getElementById("mocha")&&e.noHighlighting!==!0&&u.utils.highlightTags("code"),t&&t(r)})},u.process=o}(),function(){function require(t){var e=require.modules[t];if(!e)throw new Error('failed to require "'+t+'"');return"exports"in e||"function"!=typeof e.definition||(e.client=e.component=!0,e.definition.call(this,e.exports={},e),delete e.definition),e.exports}require.loader="component",require.helper={},require.helper.semVerSort=function(t,e){for(var n=t.version.split("."),r=e.version.split("."),i=0;is?1:-1;var a=n[i].substr((""+o).length),u=r[i].substr((""+s).length);if(""===a&&""!==u)return 1;if(""!==a&&""===u)return-1;if(""!==a&&""!==u)return a>u?1:-1}return 0},require.latest=function(t,e){function n(t){throw new Error('failed to find latest module of "'+t+'"')}var r=/(.*)~(.*)@v?(\d+\.\d+\.\d+[^\/]*)$/,i=/(.*)~(.*)/;i.test(t)||n(t);for(var o=Object.keys(require.modules),s=[],a=[],u=0;u0){var f=s.sort(require.helper.semVerSort).pop().name;return e===!0?f:require(f)}var f=a.pop().name;return e===!0?f:require(f)},require.modules={},require.register=function(t,e){require.modules[t]={definition:e}},require.define=function(t,e){require.modules[t]={exports:e}},require.register("chaijs~assertion-error@1.0.0",function(t,e){function n(){function t(t,n){Object.keys(n).forEach(function(r){~e.indexOf(r)||(t[r]=n[r])})}var e=[].slice.call(arguments);return function(){for(var e=[].slice.call(arguments),n=0,r={};n=0;i--)if(l=o[i],!n(t[l],e[l],r))return!1;return!0}var p,d=require("chaijs~type-detect@0.1.1");try{p=require("buffer").Buffer}catch(g){p={},p.isBuffer=function(){return!1}}e.exports=n}),require.register("chai",function(t,e){e.exports=require("chai/lib/chai.js")}),require.register("chai/lib/chai.js",function(t,e){var n=[],t=e.exports={};t.version="1.10.0",t.AssertionError=require("chaijs~assertion-error@1.0.0");var r=require("chai/lib/chai/utils/index.js");t.use=function(t){return~n.indexOf(t)||(t(this,r),n.push(t)),this};var i=require("chai/lib/chai/config.js");t.config=i;var o=require("chai/lib/chai/assertion.js");t.use(o);var s=require("chai/lib/chai/core/assertions.js");t.use(s);var a=require("chai/lib/chai/interface/expect.js");t.use(a);var u=require("chai/lib/chai/interface/should.js");t.use(u);var c=require("chai/lib/chai/interface/assert.js");t.use(c)}),require.register("chai/lib/chai/assertion.js",function(t,e){var n=require("chai/lib/chai/config.js"),r=function(){};e.exports=function(t,e){function i(t,e,n){s(this,"ssfi",n||arguments.callee),s(this,"object",t),s(this,"message",e)}var o=t.AssertionError,s=e.flag;t.Assertion=i,Object.defineProperty(i,"includeStack",{get:function(){return console.warn("Assertion.includeStack is deprecated, use chai.config.includeStack instead."),n.includeStack},set:function(t){console.warn("Assertion.includeStack is deprecated, use chai.config.includeStack instead."),n.includeStack=t}}),Object.defineProperty(i,"showDiff",{get:function(){return console.warn("Assertion.showDiff is deprecated, use chai.config.showDiff instead."),n.showDiff},set:function(t){console.warn("Assertion.showDiff is deprecated, use chai.config.showDiff instead."),n.showDiff=t}}),i.addProperty=function(t,n){e.addProperty(this.prototype,t,n)},i.addMethod=function(t,n){e.addMethod(this.prototype,t,n)},i.addChainableMethod=function(t,n,r){e.addChainableMethod(this.prototype,t,n,r)},i.addChainableNoop=function(t,n){e.addChainableMethod(this.prototype,t,r,n)},i.overwriteProperty=function(t,n){e.overwriteProperty(this.prototype,t,n)},i.overwriteMethod=function(t,n){e.overwriteMethod(this.prototype,t,n)},i.overwriteChainableMethod=function(t,n,r){e.overwriteChainableMethod(this.prototype,t,n,r)},i.prototype.assert=function(t,r,i,a,u,c){var l=e.test(this,arguments);if(!0!==c&&(c=!1),!0!==n.showDiff&&(c=!1),!l){var r=e.getMessage(this,arguments),h=e.getActual(this,arguments);throw new o(r,{actual:h,expected:a,showDiff:c},n.includeStack?this.assert:s(this,"ssfi"))}},Object.defineProperty(i.prototype,"_obj",{get:function(){return s(this,"object")},set:function(t){s(this,"object",t)}})}}),require.register("chai/lib/chai/config.js",function(t,e){e.exports={includeStack:!1,showDiff:!0,truncateThreshold:40}}),require.register("chai/lib/chai/core/assertions.js",function(t,e){e.exports=function(t,e){function n(t,n){n&&w(this,"message",n),t=t.toLowerCase();var r=w(this,"object"),i=~["a","e","i","o","u"].indexOf(t.charAt(0))?"an ":"a ";this.assert(t===e.type(r),"expected #{this} to be "+i+t,"expected #{this} not to be "+i+t)}function r(){w(this,"contains",!0)}function i(t,n){n&&w(this,"message",n);var r=w(this,"object"),i=!1;if("array"===e.type(r)&&"object"===e.type(t)){for(var o in r)if(e.eql(r[o],t)){i=!0;break}}else if("object"===e.type(t)){if(!w(this,"negate")){for(var s in t)new y(r).property(s,t[s]);return}var a={};for(var s in t)a[s]=r[s];i=e.eql(a,t)}else i=r&&~r.indexOf(t);this.assert(i,"expected #{this} to include "+e.inspect(t),"expected #{this} to not include "+e.inspect(t))}function o(){var t=w(this,"object"),e=Object.prototype.toString.call(t);this.assert("[object Arguments]"===e,"expected #{this} to be arguments but got "+e,"expected #{this} to not be arguments")}function s(t,e){e&&w(this,"message",e);var n=w(this,"object");return w(this,"deep")?this.eql(t):void this.assert(t===n,"expected #{this} to equal #{exp}","expected #{this} to not equal #{exp}",t,this._obj,!0)}function a(t,n){n&&w(this,"message",n),this.assert(e.eql(t,w(this,"object")),"expected #{this} to deeply equal #{exp}","expected #{this} to not deeply equal #{exp}",t,this._obj,!0)}function u(t,e){e&&w(this,"message",e);var n=w(this,"object");if(w(this,"doLength")){new y(n,e).to.have.property("length");var r=n.length;this.assert(r>t,"expected #{this} to have a length above #{exp} but got #{act}","expected #{this} to not have a length above #{exp}",t,r)}else this.assert(n>t,"expected #{this} to be above "+t,"expected #{this} to be at most "+t)}function c(t,e){e&&w(this,"message",e);var n=w(this,"object");if(w(this,"doLength")){new y(n,e).to.have.property("length");var r=n.length;this.assert(r>=t,"expected #{this} to have a length at least #{exp} but got #{act}","expected #{this} to have a length below #{exp}",t,r)}else this.assert(n>=t,"expected #{this} to be at least "+t,"expected #{this} to be below "+t)}function l(t,e){e&&w(this,"message",e);var n=w(this,"object");if(w(this,"doLength")){new y(n,e).to.have.property("length");var r=n.length;this.assert(t>r,"expected #{this} to have a length below #{exp} but got #{act}","expected #{this} to not have a length below #{exp}",t,r) +}else this.assert(t>n,"expected #{this} to be below "+t,"expected #{this} to be at least "+t)}function h(t,e){e&&w(this,"message",e);var n=w(this,"object");if(w(this,"doLength")){new y(n,e).to.have.property("length");var r=n.length;this.assert(t>=r,"expected #{this} to have a length at most #{exp} but got #{act}","expected #{this} to have a length above #{exp}",t,r)}else this.assert(t>=n,"expected #{this} to be at most "+t,"expected #{this} to be above "+t)}function f(t,n){n&&w(this,"message",n);var r=e.getName(t);this.assert(w(this,"object")instanceof t,"expected #{this} to be an instance of "+r,"expected #{this} to not be an instance of "+r)}function p(t,n){n&&w(this,"message",n);var r=w(this,"object");this.assert(r.hasOwnProperty(t),"expected #{this} to have own property "+e.inspect(t),"expected #{this} to not have own property "+e.inspect(t))}function d(){w(this,"doLength",!0)}function g(t,e){e&&w(this,"message",e);var n=w(this,"object");new y(n,e).to.have.property("length");var r=n.length;this.assert(r==t,"expected #{this} to have a length of #{exp} but got #{act}","expected #{this} to not have a length of #{act}",t,r)}function v(t){var n,r=w(this,"object"),i=!0;if(t=t instanceof Array?t:Array.prototype.slice.call(arguments),!t.length)throw new Error("keys required");var o=Object.keys(r),s=t,a=t.length;if(i=t.every(function(t){return~o.indexOf(t)}),w(this,"negate")||w(this,"contains")||(i=i&&t.length==o.length),a>1){t=t.map(function(t){return e.inspect(t)});var u=t.pop();n=t.join(", ")+", and "+u}else n=e.inspect(t[0]);n=(a>1?"keys ":"key ")+n,n=(w(this,"contains")?"contain ":"have ")+n,this.assert(i,"expected #{this} to "+n,"expected #{this} to not "+n,s.sort(),o.sort(),!0)}function m(t,n,r){r&&w(this,"message",r);var i=w(this,"object");new y(i,r).is.a("function");var o=!1,s=null,a=null,u=null;0===arguments.length?(n=null,t=null):t&&(t instanceof RegExp||"string"==typeof t)?(n=t,t=null):t&&t instanceof Error?(s=t,t=null,n=null):"function"==typeof t?(a=t.prototype.name||t.name,"Error"===a&&t!==Error&&(a=(new t).name)):t=null;try{i()}catch(c){if(s)return this.assert(c===s,"expected #{this} to throw #{exp} but #{act} was thrown","expected #{this} to not throw #{exp}",s instanceof Error?s.toString():s,c instanceof Error?c.toString():c),w(this,"object",c),this;if(t&&(this.assert(c instanceof t,"expected #{this} to throw #{exp} but #{act} was thrown","expected #{this} to not throw #{exp} but #{act} was thrown",a,c instanceof Error?c.toString():c),!n))return w(this,"object",c),this;var l="object"===e.type(c)&&"message"in c?c.message:""+c;if(null!=l&&n&&n instanceof RegExp)return this.assert(n.exec(l),"expected #{this} to throw error matching #{exp} but got #{act}","expected #{this} to throw error not matching #{exp}",n,l),w(this,"object",c),this;if(null!=l&&n&&"string"==typeof n)return this.assert(~l.indexOf(n),"expected #{this} to throw error including #{exp} but got #{act}","expected #{this} to throw error not including #{act}",n,l),w(this,"object",c),this;o=!0,u=c}var h="",f=null!==a?a:s?"#{exp}":"an error";o&&(h=" but #{act} was thrown"),this.assert(o===!0,"expected #{this} to throw "+f+h,"expected #{this} to not throw "+f+h,s instanceof Error?s.toString():s,u instanceof Error?u.toString():u),w(this,"object",u)}function b(t,e,n){return t.every(function(t){return n?e.some(function(e){return n(t,e)}):-1!==e.indexOf(t)})}var y=t.Assertion,w=(Object.prototype.toString,e.flag);["to","be","been","is","and","has","have","with","that","at","of","same"].forEach(function(t){y.addProperty(t,function(){return this})}),y.addProperty("not",function(){w(this,"negate",!0)}),y.addProperty("deep",function(){w(this,"deep",!0)}),y.addChainableMethod("an",n),y.addChainableMethod("a",n),y.addChainableMethod("include",i,r),y.addChainableMethod("contain",i,r),y.addChainableNoop("ok",function(){this.assert(w(this,"object"),"expected #{this} to be truthy","expected #{this} to be falsy")}),y.addChainableNoop("true",function(){this.assert(!0===w(this,"object"),"expected #{this} to be true","expected #{this} to be false",this.negate?!1:!0)}),y.addChainableNoop("false",function(){this.assert(!1===w(this,"object"),"expected #{this} to be false","expected #{this} to be true",this.negate?!0:!1)}),y.addChainableNoop("null",function(){this.assert(null===w(this,"object"),"expected #{this} to be null","expected #{this} not to be null")}),y.addChainableNoop("undefined",function(){this.assert(void 0===w(this,"object"),"expected #{this} to be undefined","expected #{this} not to be undefined")}),y.addChainableNoop("exist",function(){this.assert(null!=w(this,"object"),"expected #{this} to exist","expected #{this} to not exist")}),y.addChainableNoop("empty",function(){var t=w(this,"object"),e=t;Array.isArray(t)||"string"==typeof object?e=t.length:"object"==typeof t&&(e=Object.keys(t).length),this.assert(!e,"expected #{this} to be empty","expected #{this} not to be empty")}),y.addChainableNoop("arguments",o),y.addChainableNoop("Arguments",o),y.addMethod("equal",s),y.addMethod("equals",s),y.addMethod("eq",s),y.addMethod("eql",a),y.addMethod("eqls",a),y.addMethod("above",u),y.addMethod("gt",u),y.addMethod("greaterThan",u),y.addMethod("least",c),y.addMethod("gte",c),y.addMethod("below",l),y.addMethod("lt",l),y.addMethod("lessThan",l),y.addMethod("most",h),y.addMethod("lte",h),y.addMethod("within",function(t,e,n){n&&w(this,"message",n);var r=w(this,"object"),i=t+".."+e;if(w(this,"doLength")){new y(r,n).to.have.property("length");var o=r.length;this.assert(o>=t&&e>=o,"expected #{this} to have a length within "+i,"expected #{this} to not have a length within "+i)}else this.assert(r>=t&&e>=r,"expected #{this} to be within "+i,"expected #{this} to not be within "+i)}),y.addMethod("instanceof",f),y.addMethod("instanceOf",f),y.addMethod("property",function(t,n,r){r&&w(this,"message",r);var i=w(this,"deep")?"deep property ":"property ",o=w(this,"negate"),s=w(this,"object"),a=w(this,"deep")?e.getPathValue(t,s):s[t];if(o&&void 0!==n){if(void 0===a)throw r=null!=r?r+": ":"",new Error(r+e.inspect(s)+" has no "+i+e.inspect(t))}else this.assert(void 0!==a,"expected #{this} to have a "+i+e.inspect(t),"expected #{this} to not have "+i+e.inspect(t));void 0!==n&&this.assert(n===a,"expected #{this} to have a "+i+e.inspect(t)+" of #{exp}, but got #{act}","expected #{this} to not have a "+i+e.inspect(t)+" of #{act}",n,a),w(this,"object",a)}),y.addMethod("ownProperty",p),y.addMethod("haveOwnProperty",p),y.addChainableMethod("length",g,d),y.addMethod("lengthOf",g),y.addMethod("match",function(t,e){e&&w(this,"message",e);var n=w(this,"object");this.assert(t.exec(n),"expected #{this} to match "+t,"expected #{this} not to match "+t)}),y.addMethod("string",function(t,n){n&&w(this,"message",n);var r=w(this,"object");new y(r,n).is.a("string"),this.assert(~r.indexOf(t),"expected #{this} to contain "+e.inspect(t),"expected #{this} to not contain "+e.inspect(t))}),y.addMethod("keys",v),y.addMethod("key",v),y.addMethod("throw",m),y.addMethod("throws",m),y.addMethod("Throw",m),y.addMethod("respondTo",function(t,n){n&&w(this,"message",n);var r=w(this,"object"),i=w(this,"itself"),o="function"!==e.type(r)||i?r[t]:r.prototype[t];this.assert("function"==typeof o,"expected #{this} to respond to "+e.inspect(t),"expected #{this} to not respond to "+e.inspect(t))}),y.addProperty("itself",function(){w(this,"itself",!0)}),y.addMethod("satisfy",function(t,n){n&&w(this,"message",n);var r=w(this,"object"),i=t(r);this.assert(i,"expected #{this} to satisfy "+e.objDisplay(t),"expected #{this} to not satisfy"+e.objDisplay(t),this.negate?!1:!0,i)}),y.addMethod("closeTo",function(t,n,r){r&&w(this,"message",r);var i=w(this,"object");if(new y(i,r).is.a("number"),"number"!==e.type(t)||"number"!==e.type(n))throw new Error("the arguments to closeTo must be numbers");this.assert(Math.abs(i-t)<=n,"expected #{this} to be close to "+t+" +/- "+n,"expected #{this} not to be close to "+t+" +/- "+n)}),y.addMethod("members",function(t,n){n&&w(this,"message",n);var r=w(this,"object");new y(r).to.be.an("array"),new y(t).to.be.an("array");var i=w(this,"deep")?e.eql:void 0;return w(this,"contains")?this.assert(b(t,r,i),"expected #{this} to be a superset of #{act}","expected #{this} to not be a superset of #{act}",r,t):void this.assert(b(r,t,i)&&b(t,r,i),"expected #{this} to have the same members as #{act}","expected #{this} to not have the same members as #{act}",r,t)})}}),require.register("chai/lib/chai/interface/assert.js",function(exports,module){module.exports=function(chai,util){var Assertion=chai.Assertion,flag=util.flag,assert=chai.assert=function(t,e){var n=new Assertion(null,null,chai.assert);n.assert(t,e,"[ negation message unavailable ]")};assert.fail=function(t,e,n,r){throw n=n||"assert.fail()",new chai.AssertionError(n,{actual:t,expected:e,operator:r},assert.fail)},assert.ok=function(t,e){new Assertion(t,e).is.ok},assert.notOk=function(t,e){new Assertion(t,e).is.not.ok},assert.equal=function(t,e,n){var r=new Assertion(t,n,assert.equal);r.assert(e==flag(r,"object"),"expected #{this} to equal #{exp}","expected #{this} to not equal #{act}",e,t)},assert.notEqual=function(t,e,n){var r=new Assertion(t,n,assert.notEqual);r.assert(e!=flag(r,"object"),"expected #{this} to not equal #{exp}","expected #{this} to equal #{act}",e,t)},assert.strictEqual=function(t,e,n){new Assertion(t,n).to.equal(e)},assert.notStrictEqual=function(t,e,n){new Assertion(t,n).to.not.equal(e)},assert.deepEqual=function(t,e,n){new Assertion(t,n).to.eql(e)},assert.notDeepEqual=function(t,e,n){new Assertion(t,n).to.not.eql(e)},assert.isTrue=function(t,e){new Assertion(t,e).is["true"]},assert.isFalse=function(t,e){new Assertion(t,e).is["false"]},assert.isNull=function(t,e){new Assertion(t,e).to.equal(null)},assert.isNotNull=function(t,e){new Assertion(t,e).to.not.equal(null)},assert.isUndefined=function(t,e){new Assertion(t,e).to.equal(void 0)},assert.isDefined=function(t,e){new Assertion(t,e).to.not.equal(void 0)},assert.isFunction=function(t,e){new Assertion(t,e).to.be.a("function")},assert.isNotFunction=function(t,e){new Assertion(t,e).to.not.be.a("function")},assert.isObject=function(t,e){new Assertion(t,e).to.be.a("object")},assert.isNotObject=function(t,e){new Assertion(t,e).to.not.be.a("object")},assert.isArray=function(t,e){new Assertion(t,e).to.be.an("array")},assert.isNotArray=function(t,e){new Assertion(t,e).to.not.be.an("array")},assert.isString=function(t,e){new Assertion(t,e).to.be.a("string")},assert.isNotString=function(t,e){new Assertion(t,e).to.not.be.a("string")},assert.isNumber=function(t,e){new Assertion(t,e).to.be.a("number")},assert.isNotNumber=function(t,e){new Assertion(t,e).to.not.be.a("number")},assert.isBoolean=function(t,e){new Assertion(t,e).to.be.a("boolean")},assert.isNotBoolean=function(t,e){new Assertion(t,e).to.not.be.a("boolean")},assert.typeOf=function(t,e,n){new Assertion(t,n).to.be.a(e)},assert.notTypeOf=function(t,e,n){new Assertion(t,n).to.not.be.a(e)},assert.instanceOf=function(t,e,n){new Assertion(t,n).to.be.instanceOf(e)},assert.notInstanceOf=function(t,e,n){new Assertion(t,n).to.not.be.instanceOf(e)},assert.include=function(t,e,n){new Assertion(t,n,assert.include).include(e)},assert.notInclude=function(t,e,n){new Assertion(t,n,assert.notInclude).not.include(e)},assert.match=function(t,e,n){new Assertion(t,n).to.match(e)},assert.notMatch=function(t,e,n){new Assertion(t,n).to.not.match(e)},assert.property=function(t,e,n){new Assertion(t,n).to.have.property(e)},assert.notProperty=function(t,e,n){new Assertion(t,n).to.not.have.property(e)},assert.deepProperty=function(t,e,n){new Assertion(t,n).to.have.deep.property(e)},assert.notDeepProperty=function(t,e,n){new Assertion(t,n).to.not.have.deep.property(e)},assert.propertyVal=function(t,e,n,r){new Assertion(t,r).to.have.property(e,n)},assert.propertyNotVal=function(t,e,n,r){new Assertion(t,r).to.not.have.property(e,n)},assert.deepPropertyVal=function(t,e,n,r){new Assertion(t,r).to.have.deep.property(e,n)},assert.deepPropertyNotVal=function(t,e,n,r){new Assertion(t,r).to.not.have.deep.property(e,n)},assert.lengthOf=function(t,e,n){new Assertion(t,n).to.have.length(e)},assert.Throw=function(t,e,n,r){("string"==typeof e||e instanceof RegExp)&&(n=e,e=null);var i=new Assertion(t,r).to.Throw(e,n);return flag(i,"object")},assert.doesNotThrow=function(t,e,n){"string"==typeof e&&(n=e,e=null),new Assertion(t,n).to.not.Throw(e)},assert.operator=function(val,operator,val2,msg){if(!~["==","===",">",">=","<","<=","!=","!=="].indexOf(operator))throw new Error('Invalid operator "'+operator+'"');var test=new Assertion(eval(val+operator+val2),msg);test.assert(!0===flag(test,"object"),"expected "+util.inspect(val)+" to be "+operator+" "+util.inspect(val2),"expected "+util.inspect(val)+" to not be "+operator+" "+util.inspect(val2))},assert.closeTo=function(t,e,n,r){new Assertion(t,r).to.be.closeTo(e,n)},assert.sameMembers=function(t,e,n){new Assertion(t,n).to.have.same.members(e)},assert.includeMembers=function(t,e,n){new Assertion(t,n).to.include.members(e)},assert.ifError=function(t,e){new Assertion(t,e).to.not.be.ok},function t(e,n){return assert[n]=assert[e],t}("Throw","throw")("Throw","throws")}}),require.register("chai/lib/chai/interface/expect.js",function(t,e){e.exports=function(t){t.expect=function(e,n){return new t.Assertion(e,n)}}}),require.register("chai/lib/chai/interface/should.js",function(t,e){e.exports=function(t){function e(){function t(){return this instanceof String||this instanceof Number?new n(this.constructor(this),null,t):this instanceof Boolean?new n(1==this,null,t):new n(this,null,t)}function e(t){Object.defineProperty(this,"should",{value:t,enumerable:!0,configurable:!0,writable:!0})}Object.defineProperty(Object.prototype,"should",{set:e,get:t,configurable:!0});var r={};return r.equal=function(t,e,r){new n(t,r).to.equal(e)},r.Throw=function(t,e,r,i){new n(t,i).to.Throw(e,r)},r.exist=function(t,e){new n(t,e).to.exist},r.not={},r.not.equal=function(t,e,r){new n(t,r).to.not.equal(e)},r.not.Throw=function(t,e,r,i){new n(t,i).to.not.Throw(e,r)},r.not.exist=function(t,e){new n(t,e).to.not.exist},r["throw"]=r.Throw,r.not["throw"]=r.not.Throw,r}var n=t.Assertion;t.should=e,t.Should=e}}),require.register("chai/lib/chai/utils/addChainableMethod.js",function(t,e){var n=require("chai/lib/chai/utils/transferFlags.js"),r=require("chai/lib/chai/utils/flag.js"),i=require("chai/lib/chai/config.js"),o="__proto__"in Object,s=/^(?:length|name|arguments|caller)$/,a=Function.prototype.call,u=Function.prototype.apply;e.exports=function(t,e,c,l){"function"!=typeof l&&(l=function(){});var h={method:c,chainingBehavior:l};t.__methods||(t.__methods={}),t.__methods[e]=h,Object.defineProperty(t,e,{get:function(){h.chainingBehavior.call(this);var e=function f(){var t=r(this,"ssfi");t&&i.includeStack===!1&&r(this,"ssfi",f);var e=h.method.apply(this,arguments);return void 0===e?this:e};if(o){var c=e.__proto__=Object.create(this);c.call=a,c.apply=u}else{var l=Object.getOwnPropertyNames(t);l.forEach(function(n){if(!s.test(n)){var r=Object.getOwnPropertyDescriptor(t,n);Object.defineProperty(e,n,r)}})}return n(this,e),e},configurable:!0})}}),require.register("chai/lib/chai/utils/addMethod.js",function(t,e){var n=require("chai/lib/chai/config.js"),r=require("chai/lib/chai/utils/flag.js");e.exports=function(t,e,i){t[e]=function(){var o=r(this,"ssfi");o&&n.includeStack===!1&&r(this,"ssfi",t[e]);var s=i.apply(this,arguments);return void 0===s?this:s}}}),require.register("chai/lib/chai/utils/addProperty.js",function(t,e){e.exports=function(t,e,n){Object.defineProperty(t,e,{get:function(){var t=n.call(this);return void 0===t?this:t},configurable:!0})}}),require.register("chai/lib/chai/utils/flag.js",function(t,e){e.exports=function(t,e,n){var r=t.__flags||(t.__flags=Object.create(null));return 3!==arguments.length?r[e]:void(r[e]=n)}}),require.register("chai/lib/chai/utils/getActual.js",function(t,e){e.exports=function(t,e){return e.length>4?e[4]:t._obj}}),require.register("chai/lib/chai/utils/getEnumerableProperties.js",function(t,e){e.exports=function(t){var e=[];for(var n in t)e.push(n);return e}}),require.register("chai/lib/chai/utils/getMessage.js",function(t,e){var n=require("chai/lib/chai/utils/flag.js"),r=require("chai/lib/chai/utils/getActual.js"),i=(require("chai/lib/chai/utils/inspect.js"),require("chai/lib/chai/utils/objDisplay.js"));e.exports=function(t,e){var o=n(t,"negate"),s=n(t,"object"),a=e[3],u=r(t,e),c=o?e[2]:e[1],l=n(t,"message");return"function"==typeof c&&(c=c()),c=c||"",c=c.replace(/#{this}/g,i(s)).replace(/#{act}/g,i(u)).replace(/#{exp}/g,i(a)),l?l+": "+c:c}}),require.register("chai/lib/chai/utils/getName.js",function(t,e){e.exports=function(t){if(t.name)return t.name;var e=/^\s?function ([^(]*)\(/.exec(t);return e&&e[1]?e[1]:""}}),require.register("chai/lib/chai/utils/getPathValue.js",function(t,e){function n(t){var e=t.replace(/\[/g,".["),n=e.match(/(\\\.|[^.]+?)+/g);return n.map(function(t){var e=/\[(\d+)\]$/,n=e.exec(t);return n?{i:parseFloat(n[1])}:{p:t}})}function r(t,e){for(var n,r=e,i=0,o=t.length;o>i;i++){var s=t[i];r?("undefined"!=typeof s.p?r=r[s.p]:"undefined"!=typeof s.i&&(r=r[s.i]),i==o-1&&(n=r)):n=void 0}return n}e.exports=function(t,e){var i=n(t);return r(i,e)}}),require.register("chai/lib/chai/utils/getProperties.js",function(t,e){e.exports=function(){function t(t){-1===e.indexOf(t)&&e.push(t)}for(var e=Object.getOwnPropertyNames(subject),n=Object.getPrototypeOf(subject);null!==n;)Object.getOwnPropertyNames(n).forEach(t),n=Object.getPrototypeOf(n);return e}}),require.register("chai/lib/chai/utils/index.js",function(t,e){var t=e.exports={};t.test=require("chai/lib/chai/utils/test.js"),t.type=require("chai/lib/chai/utils/type.js"),t.getMessage=require("chai/lib/chai/utils/getMessage.js"),t.getActual=require("chai/lib/chai/utils/getActual.js"),t.inspect=require("chai/lib/chai/utils/inspect.js"),t.objDisplay=require("chai/lib/chai/utils/objDisplay.js"),t.flag=require("chai/lib/chai/utils/flag.js"),t.transferFlags=require("chai/lib/chai/utils/transferFlags.js"),t.eql=require("chaijs~deep-eql@0.1.3"),t.getPathValue=require("chai/lib/chai/utils/getPathValue.js"),t.getName=require("chai/lib/chai/utils/getName.js"),t.addProperty=require("chai/lib/chai/utils/addProperty.js"),t.addMethod=require("chai/lib/chai/utils/addMethod.js"),t.overwriteProperty=require("chai/lib/chai/utils/overwriteProperty.js"),t.overwriteMethod=require("chai/lib/chai/utils/overwriteMethod.js"),t.addChainableMethod=require("chai/lib/chai/utils/addChainableMethod.js"),t.overwriteChainableMethod=require("chai/lib/chai/utils/overwriteChainableMethod.js")}),require.register("chai/lib/chai/utils/inspect.js",function(t,e){function n(t,e,n){var i={showHidden:e,seen:[],stylize:function(t){return t}};return r(i,t,"undefined"==typeof n?2:n)}function r(e,n,p){if(n&&"function"==typeof n.inspect&&n.inspect!==t.inspect&&(!n.constructor||n.constructor.prototype!==n)){var b=n.inspect(p);return"string"!=typeof b&&(b=r(e,b,p)),b}var y=i(e,n);if(y)return y;if(m(n)){if("outerHTML"in n)return n.outerHTML;try{if(document.xmlVersion){var w=new XMLSerializer;return w.serializeToString(n)}var x="http://www.w3.org/1999/xhtml",j=document.createElementNS(x,"_");return j.appendChild(n.cloneNode(!1)),html=j.innerHTML.replace("><",">"+n.innerHTML+"<"),j.innerHTML="",html}catch(E){}}var T=v(n),k=e.showHidden?g(n):T;if(0===k.length||f(n)&&(1===k.length&&"stack"===k[0]||2===k.length&&"description"===k[0]&&"stack"===k[1])){if("function"==typeof n){var q=d(n),_=q?": "+q:"";return e.stylize("[Function"+_+"]","special")}if(l(n))return e.stylize(RegExp.prototype.toString.call(n),"regexp");if(h(n))return e.stylize(Date.prototype.toUTCString.call(n),"date");if(f(n))return o(n)}var O="",S=!1,A=["{","}"];if(c(n)&&(S=!0,A=["[","]"]),"function"==typeof n){var q=d(n),_=q?": "+q:"";O=" [Function"+_+"]"}if(l(n)&&(O=" "+RegExp.prototype.toString.call(n)),h(n)&&(O=" "+Date.prototype.toUTCString.call(n)),f(n))return o(n);if(0===k.length&&(!S||0==n.length))return A[0]+O+A[1];if(0>p)return l(n)?e.stylize(RegExp.prototype.toString.call(n),"regexp"):e.stylize("[Object]","special");e.seen.push(n);var M;return M=S?s(e,n,p,T,k):k.map(function(t){return a(e,n,p,T,t,S)}),e.seen.pop(),u(M,O,A)}function i(t,e){switch(typeof e){case"undefined":return t.stylize("undefined","undefined");case"string":var n="'"+JSON.stringify(e).replace(/^"|"$/g,"").replace(/'/g,"\\'").replace(/\\"/g,'"')+"'";return t.stylize(n,"string");case"number":return 0===e&&1/e===-1/0?t.stylize("-0","number"):t.stylize(""+e,"number");case"boolean":return t.stylize(""+e,"boolean")}return null===e?t.stylize("null","null"):void 0}function o(t){return"["+Error.prototype.toString.call(t)+"]"}function s(t,e,n,r,i){for(var o=[],s=0,u=e.length;u>s;++s)o.push(Object.prototype.hasOwnProperty.call(e,String(s))?a(t,e,n,r,String(s),!0):"");return i.forEach(function(i){i.match(/^\d+$/)||o.push(a(t,e,n,r,i,!0))}),o}function a(t,e,n,i,o,s){var a,u;if(e.__lookupGetter__&&(e.__lookupGetter__(o)?u=e.__lookupSetter__(o)?t.stylize("[Getter/Setter]","special"):t.stylize("[Getter]","special"):e.__lookupSetter__(o)&&(u=t.stylize("[Setter]","special"))),i.indexOf(o)<0&&(a="["+o+"]"),u||(t.seen.indexOf(e[o])<0?(u=null===n?r(t,e[o],null):r(t,e[o],n-1),u.indexOf("\n")>-1&&(u=s?u.split("\n").map(function(t){return" "+t}).join("\n").substr(2):"\n"+u.split("\n").map(function(t){return" "+t}).join("\n"))):u=t.stylize("[Circular]","special")),"undefined"==typeof a){if(s&&o.match(/^\d+$/))return u;a=JSON.stringify(""+o),a.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(a=a.substr(1,a.length-2),a=t.stylize(a,"name")):(a=a.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),a=t.stylize(a,"string"))}return a+": "+u}function u(t,e,n){var r=0,i=t.reduce(function(t,e){return r++,e.indexOf("\n")>=0&&r++,t+e.length+1},0);return i>60?n[0]+(""===e?"":e+"\n ")+" "+t.join(",\n ")+" "+n[1]:n[0]+e+" "+t.join(", ")+" "+n[1]}function c(t){return Array.isArray(t)||"object"==typeof t&&"[object Array]"===p(t)}function l(t){return"object"==typeof t&&"[object RegExp]"===p(t)}function h(t){return"object"==typeof t&&"[object Date]"===p(t)}function f(t){return"object"==typeof t&&"[object Error]"===p(t)}function p(t){return Object.prototype.toString.call(t)}var d=require("chai/lib/chai/utils/getName.js"),g=require("chai/lib/chai/utils/getProperties.js"),v=require("chai/lib/chai/utils/getEnumerableProperties.js");e.exports=n;var m=function(t){return"object"==typeof HTMLElement?t instanceof HTMLElement:t&&"object"==typeof t&&1===t.nodeType&&"string"==typeof t.nodeName}}),require.register("chai/lib/chai/utils/objDisplay.js",function(t,e){var n=require("chai/lib/chai/utils/inspect.js"),r=require("chai/lib/chai/config.js");e.exports=function(t){var e=n(t),i=Object.prototype.toString.call(t);if(r.truncateThreshold&&e.length>=r.truncateThreshold){if("[object Function]"===i)return t.name&&""!==t.name?"[Function: "+t.name+"]":"[Function]";if("[object Array]"===i)return"[ Array("+t.length+") ]";if("[object Object]"===i){var o=Object.keys(t),s=o.length>2?o.splice(0,2).join(", ")+", ...":o.join(", ");return"{ Object ("+s+") }"}return e}return e}}),require.register("chai/lib/chai/utils/overwriteMethod.js",function(t,e){e.exports=function(t,e,n){var r=t[e],i=function(){return this};r&&"function"==typeof r&&(i=r),t[e]=function(){var t=n(i).apply(this,arguments);return void 0===t?this:t}}}),require.register("chai/lib/chai/utils/overwriteProperty.js",function(t,e){e.exports=function(t,e,n){var r=Object.getOwnPropertyDescriptor(t,e),i=function(){};r&&"function"==typeof r.get&&(i=r.get),Object.defineProperty(t,e,{get:function(){var t=n(i).call(this);return void 0===t?this:t},configurable:!0})}}),require.register("chai/lib/chai/utils/overwriteChainableMethod.js",function(t,e){e.exports=function(t,e,n,r){var i=t.__methods[e],o=i.chainingBehavior;i.chainingBehavior=function(){var t=r(o).call(this);return void 0===t?this:t};var s=i.method;i.method=function(){var t=n(s).apply(this,arguments);return void 0===t?this:t}}}),require.register("chai/lib/chai/utils/test.js",function(t,e){var n=require("chai/lib/chai/utils/flag.js");e.exports=function(t,e){var r=n(t,"negate"),i=e[0];return r?!i:i}}),require.register("chai/lib/chai/utils/transferFlags.js",function(t,e){e.exports=function(t,e,n){var r=t.__flags||(t.__flags=Object.create(null));e.__flags||(e.__flags=Object.create(null)),n=3===arguments.length?n:!0;for(var i in r)(n||"object"!==i&&"ssfi"!==i&&"message"!=i)&&(e.__flags[i]=r[i])}}),require.register("chai/lib/chai/utils/type.js",function(t,e){var n={"[object Arguments]":"arguments","[object Array]":"array","[object Date]":"date","[object Function]":"function","[object Number]":"number","[object RegExp]":"regexp","[object String]":"string"};e.exports=function(t){var e=Object.prototype.toString.call(t);return n[e]?n[e]:null===t?"null":void 0===t?"undefined":t===Object(t)?"object":typeof t}}),"object"==typeof exports?module.exports=require("chai"):"function"==typeof define&&define.amd?define("chai",[],function(){return require("chai")}):(this||window).chai=require("chai")}(),function(t){function e(t,e){return f.isUndefined(e)?""+e:!f.isNumber(e)||!isNaN(e)&&isFinite(e)?f.isFunction(e)||f.isRegExp(e)?e.toString():e:e.toString()}function n(t,e){return f.isString(t)?t.length=0;o--)if(u[o]!=c[o])return!1;for(o=u.length-1;o>=0;o--)if(i=u[o],!s(t[i],e[i]))return!1;return!0}function c(t,e){return t&&e?"[object RegExp]"==Object.prototype.toString.call(e)?e.test(t):t instanceof e?!0:e.call({},t)===!0?!0:!1:!1}function l(t,e,n,r){var o;f.isString(n)&&(r=n,n=null);try{e()}catch(s){o=s}if(r=(n&&n.name?" ("+n.name+").":".")+(r?" "+r:"."),t&&!o&&i(o,n,"Missing expected exception"+r),!t&&c(o,n)&&i(o,n,"Got unwanted exception"+r),t&&o&&n&&!c(o,n)||!t&&o)throw o}"undefined"==typeof t.exports&&(t.exports=t);var h=Object.create||function(t){function e(){}if(!t)throw Error("no type");return e.prototype=t,new e},f={inherits:function(t,e){t.super_=e,t.prototype=h(e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}})},isArray:function(t){return Array.isArray(t)},isBoolean:function(t){return"boolean"==typeof t},isNull:function(t){return null===t},isNullOrUndefined:function(t){return null==t},isNumber:function(t){return"number"==typeof t},isString:function(t){return"string"==typeof t},isSymbol:function(t){return"symbol"==typeof t},isUndefined:function(t){return void 0===t},isRegExp:function(t){return f.isObject(t)&&"[object RegExp]"===f.objectToString(t)},isObject:function(t){return"object"==typeof t&&null!==t},isDate:function(t){return f.isObject(t)&&"[object Date]"===f.objectToString(t)},isError:function(t){return isObject(t)&&("[object Error]"===objectToString(t)||t instanceof Error)},isFunction:function(t){return"function"==typeof t},isPrimitive:function(t){return null===t||"boolean"==typeof t||"number"==typeof t||"string"==typeof t||"symbol"==typeof t||"undefined"==typeof t},objectToString:function(t){return Object.prototype.toString.call(t)}},p=Array.prototype.slice,d=("function"==typeof Object.keys?Object.keys:function(t){var e=[];for(var n in t)e.push(n);return e},t.exports=o);d.AssertionError=function(t){this.name="AssertionError",this.actual=t.actual,this.expected=t.expected,this.operator=t.operator,t.message?(this.message=t.message,this.generatedMessage=!1):(this.message=r(this),this.generatedMessage=!0);var e=t.stackStartFunction||i;if(Error.captureStackTrace)Error.captureStackTrace(this,e);else try{this.stack=(new Error).stack.toString()}catch(n){}},f.inherits(d.AssertionError,Error),d.fail=i,d.ok=o,d.equal=function(t,e,n){t!=e&&i(t,e,n,"==",d.equal)},d.notEqual=function(t,e,n){t==e&&i(t,e,n,"!=",d.notEqual)},d.deepEqual=function(t,e,n){s(t,e)||i(t,e,n,"deepEqual",d.deepEqual)},d.notDeepEqual=function(t,e,n){s(t,e)&&i(t,e,n,"notDeepEqual",d.notDeepEqual)},d.strictEqual=function(t,e,n){t!==e&&i(t,e,n,"===",d.strictEqual)},d.notStrictEqual=function(t,e,n){t===e&&i(t,e,n,"!==",d.notStrictEqual)},d.throws=function(){l.apply(this,[!0].concat(p.call(arguments)))},d.doesNotThrow=function(){l.apply(this,[!1].concat(p.call(arguments)))},d.ifError=function(t){if(t)throw t},t.assert=t.exports,delete t.exports}(this);var div=document.createElement("div");div.id="mocha",document.body.appendChild(div);var g_quiet=process.env.quiet||!0;if("false"===g_quiet&&(g_quiet=!1),!g_quiet){var link=document.createElement("link");link.setAttribute("rel","stylesheet"),link.setAttribute("type","text/css"),link.setAttribute("href","../res/mocha.css"),document.getElementsByTagName("head")[0].appendChild(link)}var fs=require("fs"),path=require("path"),net=require("net"),spawn=require("child_process").spawn,createTCPServer=function(t,e){var n=net.createServer();return e&&(process.exit=function(){n.close(),process.quit()}),t=t||13013,n.on("error",function(t){console.error("Failed to launch TCP server: "+t.code)}),n.listen(t),n},connectToTCPServer=function(t,e,n){t=t||13013;var r=net.connect({port:t});return r.setEncoding("utf8"),e&&r.write(JSON.stringify(e)),n||(r.end(),r=void 0),r},spawnChildProcess=function(t){var e=spawn(process.execPath,[t]);return e};mocha.setup({ui:"bdd"});var outputAsXML=function(){var t=process.env.out_dir;t||(t=fs.realpathSync("."),console.log("====Test results will be at: "+t));var e=0;if(window.XMLSerializer){var n=new XMLSerializer,r=process.cwd();r=r.substring(r.lastIndexOf(path.sep)+1);var i=path.join(t,r+".xml"),o=(document.getElementById("mocha-report"),document.getElementById("mocha")),s=n.serializeToString(o);s=decodeURIComponent(s),fs.writeFileSync(i,s)}var a=document.getElementById("mocha-stats"),u=a.getElementsByClassName("failures"),c=void 0,l="";return u&&u.length>0&&(c=u[0].getElementsByTagName("em"),c&&c.length>0&&(l=c[0].innerText,"0"!==l&&(e=1))),g_quiet&&process.exit(e),0};window.assertRunningResult=function(t,e){describe("Assert test running result",function(){it("the test should succeed",function(n){t?n():n("Assertion failed: "+e)})})},window.addEventListener("load",function(){mocha.run(),mocha.suite.afterAll(function(){outputAsXML()})}); \ No newline at end of file diff --git a/tests/automation/res/util.js b/tests/automation/res/util.js new file mode 100644 index 0000000000..c916bd17e4 --- /dev/null +++ b/tests/automation/res/util.js @@ -0,0 +1,124 @@ +var div = document.createElement('div'); +div.id = 'mocha'; +//div.style.visibility = 'hidden'; +document.body.appendChild(div); + +// useful when need to keep app stay on window: +// $ quiet=false nw +// +var g_quiet = process.env['quiet'] || true; +if (g_quiet === 'false') g_quiet = false; + +if (!g_quiet) { + var link = document.createElement('link'); + link.setAttribute('rel', 'stylesheet'); + link.setAttribute('type', 'text/css'); + link.setAttribute('href', '../res/mocha.css'); + document.getElementsByTagName('head')[0].appendChild(link); +} + +var fs = require('fs'); +var path = require('path'); +var net = require('net'); +var spawn = require('child_process').spawn; + +var createTCPServer = function(port, autoClose) { + var server = net.createServer(); + if (autoClose) { + process.exit = function() { + server.close(); + process.quit(); + }; + } + port = port || 13013; + server.on('error', function(e) { + console.error('Failed to launch TCP server: '+ e.code); + }); + server.listen(port); + return server; +}; + +var connectToTCPServer = function(port, data, keepOpen) { + port = port || 13013; + var socket = net.connect({port: port}); + socket.setEncoding('utf8'); + if (data) { + socket.write(JSON.stringify(data)); + } + if (!keepOpen) { + socket.end(); + socket = undefined; + } + return socket; +}; + +var spawnChildProcess = function(appPath) { + var child = spawn(process.execPath, [appPath]); + return child; +}; + +mocha.setup({ui:'bdd'}); + +var outputAsXML = function() { + var outputDir = process.env['out_dir']; + if (!outputDir) { + outputDir = fs.realpathSync('.'); + console.log('====Test results will be at: ' + outputDir); + //return; + } + var ret = 0; + if (window.XMLSerializer) { + var xmlParser = new XMLSerializer(); + var appName = process.cwd(); + appName = appName.substring(appName.lastIndexOf(path.sep) + 1); + var outputPath = path.join(outputDir, appName + '.xml'); + var reportNode = document.getElementById('mocha-report'); + var mochaNode = document.getElementById('mocha'); + var data = xmlParser.serializeToString(mochaNode); //(reportNode); + //data = data.replace('/', '//'); + //data = data.replace('%20', ' '); + data = decodeURIComponent(data); + fs.writeFileSync(outputPath, data); + } + + // check failures + var stats = document.getElementById('mocha-stats'); + var failureNodes = stats.getElementsByClassName('failures'); + var failureNumNode = undefined; + var failures = ''; + + if (failureNodes && failureNodes.length > 0) { + failureNumNode = failureNodes[0].getElementsByTagName('em'); + if (failureNumNode && failureNumNode.length > 0) { + failures = failureNumNode[0].innerText; + if (failures !== '0') { + ret = 1; + } + } + } + + if (g_quiet) + process.exit(ret); + + return 0; +}; + +window.assertRunningResult = function(result, errMsg) { + + describe('Assert test running result', function() { + it('the test should succeed', function(done){ + if (result) { + done(); + } else { + done('Assertion failed: ' + errMsg); + } + }); + }); +}; + +window.addEventListener('load', function(){ + mocha.run(); + mocha.suite.afterAll(function(){ + outputAsXML(); + }); +}); diff --git a/tests/automation/run.sh b/tests/automation/run.sh new file mode 100755 index 0000000000..1f39745cfa --- /dev/null +++ b/tests/automation/run.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +node mocha_test.js + diff --git a/tests/automation/sample/index.html b/tests/automation/sample/index.html new file mode 100644 index 0000000000..8f9c77e535 --- /dev/null +++ b/tests/automation/sample/index.html @@ -0,0 +1,18 @@ + + + + +hi + + + + + diff --git a/tests/automation/sample/mocha_test.js b/tests/automation/sample/mocha_test.js new file mode 100644 index 0000000000..2581ad091a --- /dev/null +++ b/tests/automation/sample/mocha_test.js @@ -0,0 +1,32 @@ +var assert = require('assert'); + +describe('sample-test', function() { + + beforeEach(function() { + console.log('before each'); + }); + + before(function() { + console.log('before'); + //assert.ok(false); + }); + + describe('#indexOf()', function() { // unit test group 1 + it('should return -1 when value is not presenttt', function() { // unit test 1.1 + assert.equal(-1, [1, 2, 3].indexOf(5)); + assert.equal(-1, [1, 2, 3].indexOf(6)); + assert.equal(1, [1, 2, 3].indexOf(2)); + }); + }); + + describe('#length', function() { // unit test group 2 + it('should return 3', function() { // unit test 2.1 + assert.equal(3, [1,2,3].length); + }); + it('should return 0', function() { // unit test 2.2 + assert.equal(0, ''.length); + }); + + }); + +}); diff --git a/tests/automation/sample/package.json b/tests/automation/sample/package.json new file mode 100644 index 0000000000..8748842da0 --- /dev/null +++ b/tests/automation/sample/package.json @@ -0,0 +1,4 @@ +{ +"name": "nw-#1236", +"main": "index.html" +} diff --git a/tests/automation/save_devtools_settings/index.html b/tests/automation/save_devtools_settings/index.html new file mode 100644 index 0000000000..ec903a852d --- /dev/null +++ b/tests/automation/save_devtools_settings/index.html @@ -0,0 +1,16 @@ + + + + + save devtools setting test + + +

    save devtools setting test

    + + + + + + + + diff --git a/tests/automation/save_devtools_settings/internal/index.html b/tests/automation/save_devtools_settings/internal/index.html new file mode 100644 index 0000000000..58a0bd79c6 --- /dev/null +++ b/tests/automation/save_devtools_settings/internal/index.html @@ -0,0 +1,183 @@ + + + + + Test case for save devtools settings + + +

    Please wait to be closed.

    + + + + diff --git a/tests/automation/save_devtools_settings/internal/package.json b/tests/automation/save_devtools_settings/internal/package.json new file mode 100644 index 0000000000..095e2476d4 --- /dev/null +++ b/tests/automation/save_devtools_settings/internal/package.json @@ -0,0 +1,4 @@ +{ + "name": "nw-save-devtools-settings-test", + "main": "index.html" +} diff --git a/tests/automation/save_devtools_settings/mocha_test.js b/tests/automation/save_devtools_settings/mocha_test.js new file mode 100644 index 0000000000..e83a72cb4d --- /dev/null +++ b/tests/automation/save_devtools_settings/mocha_test.js @@ -0,0 +1,120 @@ +var path = require('path'); +var assert = require('assert'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + + +var original; +var changed; +var ok = false; +var server, child; + +var spawnChild = function(action) { + return spawn(process.execPath, [path.join(curDir, 'internal'), action]); +}; + +function read_changed(done) { + if (!ok) { + setTimeout(read_changed, 2000, done); + } else { + var tmpChild = spawnChild(0); + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + changed = data; + tmpChild.kill(); + done(); + console.log("secnond"); + }); + }); + + } +} + +describe('save_devtools_settings', function() { + + before(function(done) { + this.timeout(0); + server = createTCPServer(13013); + child = spawnChild(1); + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + result = JSON.parse(data); + original = data; + ok = true; + child.kill(); + done(); + console.log("first"); + }); + }); + + setTimeout(read_changed, 2000, done); + + }); + + after(function () { + server.close(); + }); + + + it("should save devtools' settings", function() { + var i = 0; + // general + assert.notEqual(original[i], changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + assert.equal((original[i] + 1) % 4, changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + + assert.equal((original[i] + 1) % 4, changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + if (!original[i - 1]) + assert.equal((original[i] + 1) % 10, changed[i]); + i++; + assert.notEqual(original[i], changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + + // overrides + assert.notEqual(original[i], changed[i]); i++; + if (!original[i - 1]) + // 0~21 + assert.equal((original[i] + 1) % 22, changed[i]); + i++; + assert.notEqual(original[i], changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + if (!original[i - 1]) { + assert.equal((original[i] + 1) % 10, changed[i]); i++; + assert.equal((original[i] + 1) % 10, changed[i]); i++; + } + assert.notEqual(original[i], changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + if (!original[i - 1]) { + assert.equal((original[i] + 1) % 10, changed[i]); i++; + assert.equal((original[i] + 1) % 10, changed[i]); i++; + assert.equal((original[i] + 1) % 10, changed[i]); i++; + } + assert.notEqual(original[i], changed[i]); i++; + assert.notEqual(original[i], changed[i]); i++; + if (!original[i - 1]) + assert.equal((original[i] + 1) % 9, changed[i]); + i++; + + }); + +}); + + diff --git a/tests/automation/save_devtools_settings/package.json b/tests/automation/save_devtools_settings/package.json new file mode 100644 index 0000000000..9a89f16732 --- /dev/null +++ b/tests/automation/save_devtools_settings/package.json @@ -0,0 +1,5 @@ +{ + "name":"save_devtools_settings_wrapper", + "main":"index.html" +} + diff --git a/tests/automation/shortcut/index.html b/tests/automation/shortcut/index.html new file mode 100644 index 0000000000..168044ffc5 --- /dev/null +++ b/tests/automation/shortcut/index.html @@ -0,0 +1,16 @@ + + + + + shortcut test + + +

    shortcut test

    + + + + + + + + diff --git a/tests/automation/shortcut/mocha_test.js b/tests/automation/shortcut/mocha_test.js new file mode 100644 index 0000000000..57b1a6aa53 --- /dev/null +++ b/tests/automation/shortcut/mocha_test.js @@ -0,0 +1,110 @@ +var gui = require('nw.gui'); +var assert = require('assert'); +require('should'); + +describe('Shortcut', function() { + describe('.key', function() { + it('should be undefined if no key specified.', function() { + var shortcut; + try { + shortcut = new gui.Shortcut(); + } catch (err) {} + assert.equal(shortcut, undefined); + }); + + it('should be an object if key specified.', function() { + var shortcut; + try { + shortcut = new gui.Shortcut({"key": "Alt+Shift+S"}); + } catch (err) {} + + shortcut.should.be.a.Object; + assert.equal(shortcut.key, "Alt+Shift+S"); + }); + }); + + describe('.active', function() { + it('should be undefined if active is not an function object.', function() { + var shortcut; + try { + shortcut = new gui.Shortcut({key: 'Alt+Shift+S', active: "foo"}); + } catch(err) {} + + assert.equal(shortcut, undefined); + }); + + it('should be an object if "key" and "active" specified.', function() { + var onActive = function() {}; + var shortcut = new gui.Shortcut({key: 'Alt+Shift+S', active: onActive}); + + shortcut.should.be.a.Object; + assert.equal(shortcut.active, onActive); + }); + }); + + describe('.failed', function() { + it('should be undefined if "failed" is not a function object.', function() { + var shortcut; + try { + shortcut = new gui.Shortcut({key: 'Alt+Shift+S', failed: "foo"}); + } catch(err) {} + + assert.equal(shortcut, undefined); + }); + + it('should be an object if "key" and "failed" specified.', function() { + var onFailed = function() {}; + var shortcut = new gui.Shortcut({key: 'Alt+Shift+S', failed: onFailed}); + + shortcut.should.be.a.Object; + assert.equal(shortcut.failed, onFailed); + }); + }); +}); + +describe('App.registerGlobalHotKey', function() { + it('should register failed if the parameter is not an Shortcut object.', + function(done) { + var object = new Object(); + try { + gui.App.registerGlobalHotKey(object); + } catch(err) { + done(); + } + } + ); + + it('should register failed if the same key has been registered.', + function(done) { + var shortcut = new gui.Shortcut({key: "Alt+Shift+S"}); + shortcut.failed = function(msg) { + assert.equal(msg, "Register global desktop keyboard shortcut failed."); + done(); + }; + + gui.App.registerGlobalHotKey(shortcut); + gui.App.registerGlobalHotKey(shortcut); + } + ); + + it('should register failed for invalid key.', function(done) { + var shortcut = new gui.Shortcut({key: "foo"}); + shortcut.failed = function(msg) { + done(); + } + gui.App.registerGlobalHotKey(shortcut); + }); +}); + +describe('App.unregisterGlobalHotKey', function() { + it('should unregister failed if the parameter is not an Shortcut object.', + function(done) { + var object = new Object(); + try { + gui.App.unregisterGlobalHotKey(object); + } catch(err) { + done(); + } + } + ); +}); diff --git a/tests/automation/shortcut/package.json b/tests/automation/shortcut/package.json new file mode 100644 index 0000000000..a055ed9650 --- /dev/null +++ b/tests/automation/shortcut/package.json @@ -0,0 +1,5 @@ +{ + "name":"shortcut_wrapper", + "main":"index.html" +} + diff --git a/tests/automation/show_devtool_after_http_server_created_in_node_main/index.html b/tests/automation/show_devtool_after_http_server_created_in_node_main/index.html new file mode 100644 index 0000000000..46a598e9c2 --- /dev/null +++ b/tests/automation/show_devtool_after_http_server_created_in_node_main/index.html @@ -0,0 +1,49 @@ + + + + + +

    nw rocks !

    +

    after launch devtool, nw should not close by itself.

    + + + + diff --git a/tests/automation/show_devtool_after_http_server_created_in_node_main/main.js b/tests/automation/show_devtool_after_http_server_created_in_node_main/main.js new file mode 100644 index 0000000000..ce28ff5e7a --- /dev/null +++ b/tests/automation/show_devtool_after_http_server_created_in_node_main/main.js @@ -0,0 +1,7 @@ +var http = require('http'); +http.createServer(function (req, res) { + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end('Hello World\n'); +}).listen(11337, '127.0.0.1'); + +console.log('Server running at http://127.0.0.1:1337/'); diff --git a/tests/automation/show_devtool_after_http_server_created_in_node_main/package.json b/tests/automation/show_devtool_after_http_server_created_in_node_main/package.json new file mode 100644 index 0000000000..d88d875b8e --- /dev/null +++ b/tests/automation/show_devtool_after_http_server_created_in_node_main/package.json @@ -0,0 +1,14 @@ +{ + "name": "server", + "description": "server", + "version": "0.1", + "nodejs":true, + "node-main": "main.js", + "main": "index.html", + "window": { + "show": true, + "toolbar": true, + "fullscreen":false + + } +} diff --git a/tests/automation/single_instance/index.html b/tests/automation/single_instance/index.html new file mode 100644 index 0000000000..dec432a698 --- /dev/null +++ b/tests/automation/single_instance/index.html @@ -0,0 +1,16 @@ + + + + + single instance test + + +

    single instance test

    + + + + + + + + diff --git a/tests/automation/single_instance/mocha_test.js b/tests/automation/single_instance/mocha_test.js new file mode 100644 index 0000000000..fafc932280 --- /dev/null +++ b/tests/automation/single_instance/mocha_test.js @@ -0,0 +1,188 @@ +var path = require('path'); +var os = require('os'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + +var func = require(path.join(curDir, '..', 'start_app', 'script.js')); +var execPath = func.getExecPath(); + +var spawn = require('child_process').spawn; +var exec = require('child_process').exec; + +var app = new Array(); +var child1, child2; +var mac_app_path = path.join(curDir, 'tmp-nw', 'node-webkit.app'); + +function make_execuable_file(folder_path, done) { + func.copyExecFiles(function() { + func.copySourceFiles(folder_path); + func.zipSourceFiles(function() { + func.makeExecuableFile(); + if (os.platform() == 'darwin') { + var app_path = 'tmp-nw/node-webkit.app/Contents/Resources/app.nw'; + fs.mkdir(app_path, function(err) { + if(err && err.code !== 'EEXIST') throw err + fs.copy('tmp-nw/index.html', path.join(app_path, 'index.html')); + fs.copy('tmp-nw/package.html', path.join(app_path, 'package.html')); + }); + } + done(); + }); + }); +} + +function check_have(i, cmd, options, msg, last, done) { + var result = false; + app[i] = spawn(cmd, options); + app[i].on('exit', function() { + result = true; + }); + + if (last == 2) { + setTimeout(function() { + if (result) { + done(); + } else { + done(msg); + } + app[i].kill(); + app[i - 1].kill(); + }, 3000); + } else { + setTimeout(function() { + if (result) { + done(msg); + } else { + done(); + } + if (last == 1) { + app[i].kill(); + app[i - 1].kill(); + } + }, 3000); + } +} + +describe('single-instance', function() { + this.timeout(0); + +/////// 1 + describe('single-instance false', function() { + before(function(done) { + make_execuable_file(path.join(curDir, 'mul'), done); + }); + + after(function() { + fs.remove(path.join(curDir, 'tmp-nw'), function (er) { + if (er) throw er; + }); + }); + + it('should have a instance', function(done) { + check_have(0, execPath, "", 'not have a instance', 0, done); + }); + + it('should have a second instance', function(done) { + check_have(1, execPath, "", 'not have a second instance', 1, done); + }); + }); + +//////////////// 2 + describe('single-instance default', function() { + + before(function(done) { + setTimeout(function() { + make_execuable_file(path.join(curDir, 'single'), done); + }, 3000); + }); + + after(function() { + fs.remove(path.join(curDir, 'tmp-nw'), function (er) { + if (er) throw er; + }); + }); + + it('should have a instance', function(done) { + check_have(2, execPath, "", 'not have a instance', 0, done); + }); + + it('should not have a second instance', function(done) { + check_have(3, execPath, "", 'have a second instance', 2, done); + }); + }); + +/////////////////////// 3 + if (os.platform() == 'darwin') { + describe('single-instance false(open app)', function(){ + before(function(done) { + setTimeout(function() { + make_execuable_file('single_instance/open_mul', done); + }, 3000); + }); + + after(function() { + fs.remove('tmp-nw', function (er) { + if (er) throw er; + }); + }); + + it('should have a instance (open app)', function(done) { + child1 = exec('open ' + mac_app_path); + setTimeout(function () { + var content = fs.readFileSync('tmp-nw/msg'); + if (content + "" != "") + done(); + else + done("not have a instance"); + }, 6000); + }); + + it('should not have a second instance (open app)', function(done) { + child2 = exec('open ' + mac_app_path); + var content = fs.readFileSync('tmp-nw/msg'); + if (content + "" == "11") + done("have a second instance"); + else + done(); + }); + + }); + + describe('single-instance default(open app)', function(){ + before(function(done) { + setTimeout(function() { + make_execuable_file('single_instance/open_single', done); + }, 3000); + }); + + after(function() { + fs.remove('tmp-nw', function (er) { + if (er) throw er; + }); + }); + + it('should have a instance (open app)', function(done) { + child1 = exec('open ' + mac_app_path); + setTimeout(function () { + var content = fs.readFileSync('tmp-nw/msg_s'); + if (content + "" != "") + done(); + else + done("not have a instance"); + }, 6000); + }); + + it('should not have a second instance (open app)', function(done) { + child2 = exec('open ' + mac_app_path); + var content = fs.readFileSync('tmp-nw/msg_s'); + if (content + "" == "11") + done("have a second instance"); + else + done(); + + }); + }); + } +}); + + diff --git a/tests/automation/single_instance/mul/index.html b/tests/automation/single_instance/mul/index.html new file mode 100644 index 0000000000..c432c370d3 --- /dev/null +++ b/tests/automation/single_instance/mul/index.html @@ -0,0 +1,10 @@ + + + + Mul-instance + + +

    Mul-instance

    + We are using node.js + + diff --git a/tests/automation/single_instance/mul/package.json b/tests/automation/single_instance/mul/package.json new file mode 100644 index 0000000000..0a341d73b3 --- /dev/null +++ b/tests/automation/single_instance/mul/package.json @@ -0,0 +1,6 @@ +{ + "name": "nw-single-sinstance", + "main": "index.html", + "single-instance": false +} + diff --git a/tests/automation/single_instance/open_mul/index.html b/tests/automation/single_instance/open_mul/index.html new file mode 100644 index 0000000000..b365a2557b --- /dev/null +++ b/tests/automation/single_instance/open_mul/index.html @@ -0,0 +1,19 @@ + + + + open Mul-instance + + +

    open Mul-instance

    + We are using node.js + + + diff --git a/tests/automation/single_instance/open_mul/package.json b/tests/automation/single_instance/open_mul/package.json new file mode 100644 index 0000000000..d00691e29a --- /dev/null +++ b/tests/automation/single_instance/open_mul/package.json @@ -0,0 +1,5 @@ +{ + "name": "nw-single-sinstance", + "main": "index.html", + "single-instance": false +} diff --git a/tests/automation/single_instance/open_single/index.html b/tests/automation/single_instance/open_single/index.html new file mode 100644 index 0000000000..fdadd50e9d --- /dev/null +++ b/tests/automation/single_instance/open_single/index.html @@ -0,0 +1,18 @@ + + + + open Single-instance + + +

    open Single-instance

    + We are using node.js + + + diff --git a/tests/automation/single_instance/open_single/package.json b/tests/automation/single_instance/open_single/package.json new file mode 100644 index 0000000000..e5e3247df4 --- /dev/null +++ b/tests/automation/single_instance/open_single/package.json @@ -0,0 +1,4 @@ +{ + "name": "nw-single-sinstance", + "main": "index.html" +} diff --git a/tests/automation/single_instance/package.json b/tests/automation/single_instance/package.json new file mode 100644 index 0000000000..546ab42270 --- /dev/null +++ b/tests/automation/single_instance/package.json @@ -0,0 +1,5 @@ +{ + "name":"single_instance_wrapper", + "main":"index.html" +} + diff --git a/tests/automation/single_instance/single/index.html b/tests/automation/single_instance/single/index.html new file mode 100644 index 0000000000..8281ed49db --- /dev/null +++ b/tests/automation/single_instance/single/index.html @@ -0,0 +1,10 @@ + + + + Single-instance + + +

    Single-instance

    + We are using node.js + + diff --git a/tests/automation/single_instance/single/package.json b/tests/automation/single_instance/single/package.json new file mode 100644 index 0000000000..0fb97dc071 --- /dev/null +++ b/tests/automation/single_instance/single/package.json @@ -0,0 +1,5 @@ +{ + "name": "nw-single-sinstance", + "main": "index.html" +} + diff --git a/tests/automation/snapshot/index.html b/tests/automation/snapshot/index.html new file mode 100644 index 0000000000..07632e0abd --- /dev/null +++ b/tests/automation/snapshot/index.html @@ -0,0 +1,16 @@ + + + + + snapshort test + + +

    snapshort test

    + + + + + + + + diff --git a/tests/automation/snapshot/internal/1266-snapshot-crash-start/file_to_snapshot_to_app.bin.js b/tests/automation/snapshot/internal/1266-snapshot-crash-start/file_to_snapshot_to_app.bin.js new file mode 100755 index 0000000000..cda251772f --- /dev/null +++ b/tests/automation/snapshot/internal/1266-snapshot-crash-start/file_to_snapshot_to_app.bin.js @@ -0,0 +1,11 @@ +var sampleFunction; + +(function() +{ + var privateVar = 'private'; + + sampleFunction = function() + { + return privateVar+'67868'; + }; +})(); diff --git a/tests/automation/snapshot/internal/1266-snapshot-crash-start/index.html b/tests/automation/snapshot/internal/1266-snapshot-crash-start/index.html new file mode 100755 index 0000000000..e6d9329a9e --- /dev/null +++ b/tests/automation/snapshot/internal/1266-snapshot-crash-start/index.html @@ -0,0 +1,26 @@ + + + + + snapshot test + + + +
    Data...
    + + + + + diff --git a/tests/automation/snapshot/internal/1266-snapshot-crash-start/package.json b/tests/automation/snapshot/internal/1266-snapshot-crash-start/package.json new file mode 100755 index 0000000000..10d863b9ea --- /dev/null +++ b/tests/automation/snapshot/internal/1266-snapshot-crash-start/package.json @@ -0,0 +1,5 @@ +{ + "name": "nw-demo", + "main": "index.html", + "snapshot" : "app.bin" +} diff --git a/tests/automation/snapshot/internal/1266-snapshot-crash-start/script.js b/tests/automation/snapshot/internal/1266-snapshot-crash-start/script.js new file mode 100755 index 0000000000..1b9218f143 --- /dev/null +++ b/tests/automation/snapshot/internal/1266-snapshot-crash-start/script.js @@ -0,0 +1,4 @@ +$(document).ready(function() +{ + $('#content').html(sampleFunction()); +}); \ No newline at end of file diff --git a/tests/automation/snapshot/internal/index.html b/tests/automation/snapshot/internal/index.html new file mode 100644 index 0000000000..155bfddea2 --- /dev/null +++ b/tests/automation/snapshot/internal/index.html @@ -0,0 +1,10 @@ + + snapshot demo + + + + + + diff --git a/tests/automation/snapshot/internal/mytest.js b/tests/automation/snapshot/internal/mytest.js new file mode 100644 index 0000000000..df8095f254 --- /dev/null +++ b/tests/automation/snapshot/internal/mytest.js @@ -0,0 +1,12 @@ +function mytest(a) { + document.write(a + 42); + + var gui = require('nw.gui'); + var result = { ok: true }; + + var socket = require('net').connect({port: 13013}); + socket.setEncoding('utf8'); + socket.end(JSON.stringify(result)); + + +} diff --git a/tests/automation/snapshot/internal/package.json b/tests/automation/snapshot/internal/package.json new file mode 100644 index 0000000000..694c4bf933 --- /dev/null +++ b/tests/automation/snapshot/internal/package.json @@ -0,0 +1,5 @@ +{ + "name": "nw-demo", + "main": "index.html", + "snapshot": "mytest.bin" +} \ No newline at end of file diff --git a/tests/automation/snapshot/mocha_test.js b/tests/automation/snapshot/mocha_test.js new file mode 100644 index 0000000000..dcf5cef1dc --- /dev/null +++ b/tests/automation/snapshot/mocha_test.js @@ -0,0 +1,124 @@ +var path = require('path'); +var os = require('os'); +var fs = require('fs-extra'); +var cp = require('child_process'); +var curDir = fs.realpathSync('.'); + +var myTestJSPath = path.join(curDir, 'internal', 'mytest.js'); +var myTestBinPath = path.join(curDir, 'internal', 'mytest.bin'); + +var snapshotPath; +var server; + +describe('snapshot', function() { + +/////////// 1 + + describe('demo should work fine', function() { + before(function(done) { + var snapshotExec; + if (os.platform() == 'darwin') { + snapshotExec = '../../../../../../nwsnapshot'; + } + if (os.platform() == 'linux') { + snapshotExec = 'nwsnapshot'; + } + if (os.platform() == 'win32') { + snapshotExec = 'nwsnapshot.exe'; + } + + snapshotPath = path.join(process.execPath, '..', snapshotExec); + console.log("snapshotPath: " + snapshotPath); + + cp.execFile(snapshotPath, + ['--extra_code', myTestJSPath, myTestBinPath], + {cwd: curDir}, + function (error, stdout, stderr) { + + server = createTCPServer(13013); + + done(); + } + ); + + }); + + after(function() { + fs.unlink(myTestBinPath, function(err) {if(err && err.code !== 'ENOENT') throw err}); + fs.unlink(path.join(curDir, 'internal', 'v8.log'), function(err) {if(err && err.code !== 'ENOENT') throw err}); + server.close(); + }); + + it('the native code could be exectuted', + function(done) { + this.timeout(0); + var result = false; + + + var child = spawnChildProcess(path.join(curDir, 'internal')); + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + result = true; + child.kill(); + done(); + }); + }); + + + setTimeout(function(){ + if (!result) { + done('the native code does not been executed'); + child.close(); + //child.removeConnection(); + //child.app.kill(); + } + }, 3000); + //child.app.stderr.on('data', function(d){ console.log ('app' + d);}); + + }); + }); + + +////////////////// 2 + + + describe('1266-snapshot-crash-start', function() { + before(function(done) { + cp.execFile(snapshotPath, + ['--extra_code', 'file_to_snapshot_to_app.bin.js', 'app.bin'], + {cwd: path.join(curDir, 'internal', '1266-snapshot-crash-start')}, + function (error, stdout, stderr) { + done(); + } + + ); + }); + + after(function() { + fs.unlink(path.join(curDir, 'internal','1266-snapshot-crash-start', 'app.bin'), function(err) {if(err && err.code !== 'ENOENT') throw err}); + fs.unlink(path.join(curDir, 'internal','1266-snapshot-crash-start', 'v8.log'), function(err) {if(err && err.code !== 'ENOENT') throw err}); + fs.unlink(path.join(curDir, 'internal','1266-snapshot-crash-start','tmp'), function(err) {if(err && err.code !== 'ENOENT') throw err}); + }); + + it('another demo should close nomally', function(done) { + this.timeout(0); + var ppath = process.execPath + " " + path.join(curDir, 'internal', '1266-snapshot-crash-start'); + cp.exec(ppath, function(err, stdout, stderr) { + }); + setTimeout(function(){ + fs.exists(path.join(curDir, 'internal', '1266-snapshot-crash-start', 'tmp'), function(exists) { + if (exists) + done(); + else + done('another demo fails'); + }); + }, 3000); + }); + }); + + + +}); + + diff --git a/tests/automation/snapshot/package.json b/tests/automation/snapshot/package.json new file mode 100644 index 0000000000..7fcfad8073 --- /dev/null +++ b/tests/automation/snapshot/package.json @@ -0,0 +1,5 @@ +{ + "name":"snapshort_wrapper", + "main":"index.html" +} + diff --git a/tests/automation/source-maps/index.html b/tests/automation/source-maps/index.html new file mode 100644 index 0000000000..129df6e8bc --- /dev/null +++ b/tests/automation/source-maps/index.html @@ -0,0 +1,16 @@ + + + + + source map test + + +

    source map test

    + + + + + + + + diff --git a/tests/automation/source-maps/internal/index.html b/tests/automation/source-maps/internal/index.html new file mode 100644 index 0000000000..8d22e007a0 --- /dev/null +++ b/tests/automation/source-maps/internal/index.html @@ -0,0 +1,90 @@ + + + + + Test case for source maps + + + +

    Please wait to be closed.

    + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/automation/source-maps/internal/package.json b/tests/automation/source-maps/internal/package.json new file mode 100644 index 0000000000..862ea905fc --- /dev/null +++ b/tests/automation/source-maps/internal/package.json @@ -0,0 +1,5 @@ +{ + "name": "nw-test-source-maps", + "main": "index.html", + "node-remote": "127.0.0.1" +} diff --git a/tests/automation/source-maps/internal/scripts/compiler.jar b/tests/automation/source-maps/internal/scripts/compiler.jar new file mode 100644 index 0000000000..53037b0e8a Binary files /dev/null and b/tests/automation/source-maps/internal/scripts/compiler.jar differ diff --git a/tests/automation/source-maps/internal/scripts/jquery.d.ts b/tests/automation/source-maps/internal/scripts/jquery.d.ts new file mode 100644 index 0000000000..a7c4c53892 --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/jquery.d.ts @@ -0,0 +1,702 @@ +// Typing for the jQuery library, version 1.7.x + +/* + Interface for the AJAX setting that will configure the AJAX request +*/ +interface JQueryAjaxSettings { + accepts?: any; + async?: bool; + beforeSend?(jqXHR: JQueryXHR, settings: JQueryAjaxSettings); + cache?: bool; + complete?(jqXHR: JQueryXHR, textStatus: string); + contents?: { [key: string]: any; }; + contentType?: string; + context?: any; + converters?: { [key: string]: any; }; + crossDomain?: bool; + data?: any; + dataFilter?(data: any, ty: any): any; + dataType?: string; + error?(jqXHR: JQueryXHR, textStatus: string, errorThrow: string): any; + global?: bool; + headers?: { [key: any]: any; }; + ifModified?: bool; + isLocal?: bool; + jsonp?: string; + jsonpCallback?: any; + mimeType?: string; + password?: string; + processData?: bool; + scriptCharset?: string; + statusCode?: { [key: any]: any; }; + success?(data: any, textStatus: string, jqXHR: JQueryXHR); + timeout?: number; + traditional?: bool; + type?: string; + url?: string; + username?: string; + xhr?: any; + xhrFields?: { [key: any]: any; }; +} + +/* + Interface for the jqXHR object +*/ +interface JQueryXHR extends XMLHttpRequest { + overrideMimeType(); +} + +/* + Interface for the JQuery callback +*/ +interface JQueryCallback { + add(...callbacks: any[]): any; + disable(): any; + empty(): any; + fire(...arguments: any[]): any; + fired(): bool; + fireWith(context: any, ...args: any[]): any; + has(callback: any): bool; + lock(): any; + locked(): bool; + removed(...callbacks: any[]): any; +} + +/* + Interface for the JQuery promise, part of callbacks +*/ +interface JQueryPromise { + always(...alwaysCallbacks: any[]): JQueryDeferred; + done(...doneCallbacks: any[]): JQueryDeferred; + fail(...failCallbacks: any[]): JQueryDeferred; + pipe(doneFilter?: (x: any) => any, failFilter?: (x: any) => any, progressFilter?: (x: any) => any): JQueryPromise; + then(doneCallbacks: any, failCallbacks: any, progressCallbacks?: any): JQueryDeferred; +} + +/* + Interface for the JQuery deferred, part of callbacks +*/ +interface JQueryDeferred extends JQueryPromise { + notify(...args: any[]): JQueryDeferred; + notifyWith(context: any, ...args: any[]): JQueryDeferred; + + pipe(doneFilter?: any, failFilter?: any, progressFilter?: any): JQueryPromise; + progress(...progressCallbacks: any[]): JQueryDeferred; + reject(...args: any[]): JQueryDeferred; + rejectWith(context:any, ...args: any[]): JQueryDeferred; + resolve(...args: any[]): JQueryDeferred; + resolveWith(context:any, ...args: any[]): JQueryDeferred; + state(): string; + then(doneCallbacks: any, failCallbacks: any, progressCallbacks?: any): JQueryDeferred; +} + +/* + Interface of the JQuery extension of the W3C event object +*/ +interface JQueryEventObject extends Event { + data: any; + delegateTarget: Element; + isDefaultPrevented(): bool; + isImmediatePropogationStopped(): bool; + isPropogationStopped(): bool; + namespace: string; + preventDefault(): any; + relatedTarget: Element; + result: any; + stopImmediatePropagation(); + stopPropagation(); + pageX: number; + pageY: number; + which: number; + metaKey: any; +} + +/* + Collection of properties of the current browser +*/ +interface JQueryBrowserInfo { + safari:bool; + opera:bool; + msie:bool; + mozilla:bool; + version:string; +} + +interface JQuerySupport { + ajax?: bool; + boxModel?: bool; + changeBubbles?: bool; + checkClone?: bool; + checkOn?: bool; + cors?: bool; + cssFloat?: bool; + hrefNormalized?: bool; + htmlSerialize?: bool; + leadingWhitespace?: bool; + noCloneChecked?: bool; + noCloneEvent?: bool; + opacity?: bool; + optDisabled?: bool; + optSelected?: bool; + scriptEval?(): bool; + style?: bool; + submitBubbles?: bool; + tbody?: bool; +} + +/* + Static members of jQuery (those on $ and jQuery themselves) +*/ +interface JQueryStatic { + + /**** + AJAX + *****/ + ajax(url: string, settings: JQueryAjaxSettings); + + ajaxPrefilter(dataTypes: string, handler: (opts: any, originalOpts: any, jqXHR: JQueryXHR) => any): any; + ajaxPrefilter(handler: (opts: any, originalOpts: any, jqXHR: JQueryXHR) => any): any; + + ajaxSetup(options: any); + + get(url: string, data?: any, success?: any, dataType?: any): JQueryXHR; + getJSON(url: string, data?: any, success?: any): JQueryXHR; + getScript(url: string, success?: any): JQueryXHR; + + param(obj: any): string; + param(obj: any, traditional: bool): string; + + post(url: string, data?: any, success?: any, dataType?: any): JQueryXHR; + + /********* + CALLBACKS + **********/ + Callbacks(flags: any): JQueryCallback; + + /**** + CORE + *****/ + holdReady(hold: bool): any; + + (selector: string, context?: any): JQuery; + (element: Element): JQuery; + (object: { }): JQuery; + (elementArray: Element[]): JQuery; + (object: JQuery): JQuery; + (func: Function): JQuery; + (): JQuery; + + noConflict(removeAll?: bool): Object; + + when(...deferreds: any[]): JQueryPromise; + + /*** + CSS + ****/ + css(e: any, propertyName: string, value?: any); + css(e: any, propertyName: any, value?: any); + cssHooks: { [key: any]: any; }; + + /**** + DATA + *****/ + data(element: Element, key: string, value: any): Object; + + dequeue(element: Element, queueName?: string): any; + + hasData(element: Element): bool; + + queue(element: Element, queueName?: string): any[]; + queue(element: Element, queueName: string, newQueueOrCallback: any): JQuery; + + removeData(element: Element, name?: string): JQuery; + + /******* + EFFECTS + ********/ + fx: { tick: () => void; interval: number; stop: () => void; speeds: { slow: number; fast: number; }; off: bool; step: any; }; + + /****** + EVENTS + *******/ + proxy(context: any, name: any): any; + + /********* + INTERNALS + **********/ + error(message: any); + + /************* + MISCELLANEOUS + **************/ + expr: any; + fn: any; //TODO: Decide how we want to type this + isReady: bool; + + /********** + PROPERTIES + ***********/ + browser: JQueryBrowserInfo; + support: JQuerySupport; + + /********* + UTILITIES + **********/ + contains(container: Element, contained: Element): bool; + + each(collection: any, callback: (indexInArray: any, valueOfElement: any) => any): any; + + extend(target: any, ...objs: any[]): Object; + extend(deep: bool, target: any, ...objs: any[]): Object; + + globalEval(code: string): any; + + grep(array: any[], func: any, invert: bool): any[]; + + inArray(value: any, array: any[], fromIndex?: number): number; + + isArray(obj: any): bool; + isEmptyObject(obj: any): bool; + isFunction(obj: any): bool; + isNumeric(value: any): bool; + isPlainObject(obj: any): bool; + isWindow(obj: any): bool; + isXMLDoc(node: Node): bool; + + makeArray(obj: any): any[]; + + map(array: any[], callback: (elementOfArray: any, indexInArray: any) =>any): JQuery; + + merge(first: any[], second: any[]): any[]; + + noop(): any; + + now(): number; + + parseJSON(json: string): Object; + + //FIXME: This should return an XMLDocument + parseXML(data: string): any; + + queue(element: Element, queueName: string, newQueue: any[]): JQuery; + + trim(str: string): string; + + type(obj: any): string; + + unique(arr: any[]): any[]; +} + +/* + The jQuery instance members +*/ +interface JQuery { + /**** + AJAX + *****/ + ajaxComplete(handler: any): JQuery; + ajaxError(handler: (evt: any, xhr: any, opts: any) => any): JQuery; + ajaxSend(handler: (evt: any, xhr: any, opts: any) => any): JQuery; + ajaxStart(handler: () => any): JQuery; + ajaxStop(handler: () => any): JQuery; + ajaxSuccess(handler: (evt: any, xml: any, opts: any) => any): JQuery; + + load(url: string, data?: any, complete?: any): JQuery; + + serialize(): string; + serializeArray(): any[]; + + /********** + ATTRIBUTES + ***********/ + addClass(classNames: string): JQuery; + addClass(func: (index: any, currentClass: any) => JQuery); + + attr(attributeName: string): string; + attr(attributeName: string, value: any): JQuery; + attr(map: { [key: any]: any; }): JQuery; + attr(attributeName: string, func: (index: any, attr: any) => any): JQuery; + + hasClass(className: string): bool; + + html(htmlString: string): JQuery; + html(): string; + + prop(propertyName: string): string; + prop(propertyName: string, value: any): JQuery; + prop(map: any): JQuery; + prop(propertyName: string, func: (index: any, oldPropertyValue: any) => any): JQuery; + + removeAttr(attributeName: any): JQuery; + + removeClass(className?: any): JQuery; + removeClass(func: (index: any, cls: any) => any): JQuery; + + removeProp(propertyName: any): JQuery; + + toggleClass(className: any, swtch?: bool): JQuery; + toggleClass(swtch?: bool): JQuery; + toggleClass(func: (index: any, cls: any, swtch: any) => any): JQuery; + + val(): any; + val(value: string[]): JQuery; + val(value: string): JQuery; + val(func: (index: any, value: any) => any): JQuery; + + /*** + CSS + ****/ + css(propertyName: string, value?: any); + css(propertyName: any, value?: any); + + height(): number; + height(value: number): JQuery; + height(func: (index: any, height: any) => any): JQuery; + + innerHeight(): number; + innerWidth(): number; + + offset(): Object; + offset(coordinates: any): JQuery; + offset(func: (index: any, coords: any) => any): JQuery; + + outerHeight(includeMargin?: bool): number; + outerWidth(includeMargin?: bool): number; + + position(): { top: number; left: number; }; + + scrollLeft(): number; + scrollLeft(value: number): JQuery; + + scrollTop(): number; + scrollTop(value: number): JQuery; + + width(): number; + width(value: number): JQuery; + width(func: (index: any, height: any) => any): JQuery; + + /**** + DATA + *****/ + clearQueue(queueName?: string): JQuery; + + data(key: string, value: any): JQuery; + data(obj: { [key: string]: any; }): JQuery; + data(key?: string): any; + + dequeue(queueName?: string): JQuery; + + removeData(nameOrList?: any): JQuery; + + /******** + DEFERRED + *********/ + promise(type?: any, target?: any): JQueryPromise; + + /******* + EFFECTS + ********/ + animate(properties: any, duration?: any, easing?: string, complete?: Function): JQuery; + animate(properties: any, options: { duration?: any; easing?: string; complete?: Function; step?: Function; queue?: bool; specialEasing?: any; }); + + delay(duration: number, queueName?: string): JQuery; + + fadeIn(duration?: any, callback?: any): JQuery; + fadeIn(duration?: any, easing?: string, callback?: any): JQuery; + + fadeOut(duration?: any, callback?: any): JQuery; + fadeOut(duration?: any, easing?: string, callback?: any): JQuery; + + fadeTo(duration: any, opacity: number, callback?: any): JQuery; + fadeTo(duration: any, opacity: number, easing?: string, callback?: any): JQuery; + + fadeToggle(duration?: any, easing?: string, callback?: any): JQuery; + + hide(duration?: any, callback?: any): JQuery; + hide(duration?: any, easing?: string, callback?: any): JQuery; + + show(duration?: any, callback?: any): JQuery; + show(duration?: any, easing?: string, callback?: any): JQuery; + + slideDown(duration?: any, callback?: any): JQuery; + slideDown(duration?: any, easing?: string, callback?: any): JQuery; + + slideToggle(duration?: any, callback?: any): JQuery; + slideToggle(duration?: any, easing?: string, callback?: any): JQuery; + + slideUp(duration?: any, callback?: any): JQuery; + slideUp(duration?: any, easing?: string, callback?: any): JQuery; + + stop(clearQueue?: bool, jumpToEnd?: bool): JQuery; + stop(queue?:any, clearQueue?: bool, jumpToEnd?: bool): JQuery; + + toggle(duration?: any, callback?: any): JQuery; + toggle(duration?: any, easing?: string, callback?: any): JQuery; + toggle(showOrHide: bool): JQuery; + + /****** + EVENTS + *******/ + bind(eventType: string, eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery; + bind(eventType: string, eventData: any, preventBubble:bool): JQuery; + bind(eventType: string, preventBubble:bool): JQuery; + bind(...events: any[]); + + blur(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery; + blur(handler: (eventObject: JQueryEventObject) => any): JQuery; + + change(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery; + change(handler: (eventObject: JQueryEventObject) => any): JQuery; + + click(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery; + click(handler: (eventObject: JQueryEventObject) => any): JQuery; + + dblclick(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery; + dblclick(handler: (eventObject: JQueryEventObject) => any): JQuery; + + delegate(selector: any, eventType: string, handler: (eventObject: JQueryEventObject) => any): JQuery; + + + focus(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery; + focus(handler: (eventObject: JQueryEventObject) => any): JQuery; + + focusin(eventData: any, handler: (eventObject: JQueryEventObject) => any): JQuery; + focusin(handler: (eventObject: JQueryEventObject) => any): JQuery; + + focusout(eventData: any, handler: (eventObject: JQueryEventObject) => any): JQuery; + focusout(handler: (eventObject: JQueryEventObject) => any): JQuery; + + hover(handlerIn: (eventObject: JQueryEventObject) => any, handlerOut: (eventObject: JQueryEventObject) => any): JQuery; + hover(handlerInOut: (eventObject: JQueryEventObject) => any): JQuery; + + keydown(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery; + keydown(handler: (eventObject: JQueryEventObject) => any): JQuery; + + keypress(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery; + keypress(handler: (eventObject: JQueryEventObject) => any): JQuery; + + keyup(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery; + keyup(handler: (eventObject: JQueryEventObject) => any): JQuery; + + mousedown(eventData: any, handler: (eventObject: JQueryEventObject) => any): JQuery; + mousedown(handler: (eventObject: JQueryEventObject) => any): JQuery; + + mouseevent(eventData: any, handler: (eventObject: JQueryEventObject) => any): JQuery; + mouseevent(handler: (eventObject: JQueryEventObject) => any): JQuery; + + mouseleave(eventData: any, handler: (eventObject: JQueryEventObject) => any): JQuery; + mouseleave(handler: (eventObject: JQueryEventObject) => any): JQuery; + + mousemove(eventData: any, handler: (eventObject: JQueryEventObject) => any): JQuery; + mousemove(handler: (eventObject: JQueryEventObject) => any): JQuery; + + mouseout(eventData: any, handler: (eventObject: JQueryEventObject) => any): JQuery; + mouseout(handler: (eventObject: JQueryEventObject) => any): JQuery; + + mouseover(eventData: any, handler: (eventObject: JQueryEventObject) => any): JQuery; + mouseover(handler: (eventObject: JQueryEventObject) => any): JQuery; + + mouseup(eventData: any, handler: (eventObject: JQueryEventObject) => any): JQuery; + mouseup(handler: (eventObject: JQueryEventObject) => any): JQuery; + + off(events?: string, selector?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery; + off(eventsMap: { [key: string]: any; }, selector?: any): JQuery; + + on(events: string, selector?: any, data?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery; + on(eventsMap: { [key: string]: any; }, selector?: any, data?: any): JQuery; + + one(events: string, selector?: any, data?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery; + one(eventsMap: { [key: string]: any; }, selector?: any, data?: any): JQuery; + + ready(handler: any): JQuery; + + resize(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery; + resize(handler: (eventObject: JQueryEventObject) => any): JQuery; + + scroll(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery; + scroll(handler: (eventObject: JQueryEventObject) => any): JQuery; + + select(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery; + select(handler: (eventObject: JQueryEventObject) => any): JQuery; + + submit(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery; + submit(handler: (eventObject: JQueryEventObject) => any): JQuery; + + trigger(eventType: string, ...extraParameters: any[]): JQuery; + trigger(event: JQueryEventObject): JQuery; + + triggerHandler(eventType: string, ...extraParameters: any[]): Object; + + unbind(eventType?: string, handler?: (eventObject: JQueryEventObject) => any): JQuery; + unbind(eventType: string, fls: bool): JQuery; + unbind(evt: any): JQuery; + + undelegate(): JQuery; + undelegate(selector: any, eventType: string, handler?: (eventObject: JQueryEventObject) => any): JQuery; + undelegate(selector: any, events: any): JQuery; + undelegate(namespace: string): JQuery; + + /********* + INTERNALS + **********/ + + context: Element; + jquery: string; + pushStack(elements: any[]): JQuery; + pushStack(elements: any[], name: any, arguments: any): JQuery; + + /************ + MANIPULATION + *************/ + after(...content: any[]): JQuery; + after(func: (index: any) => any); + + append(...content: any[]): JQuery; + append(func: (index: any, html: any) => any); + + appendTo(target: any): JQuery; + + before(...content: any[]): JQuery; + before(func: (index: any) => any); + + clone(withDataAndEvents?: bool, deepWithDataAndEvents?: bool): JQuery; + + detach(selector?: any): JQuery; + + empty(): JQuery; + + insertAfter(target: any): JQuery; + insertBefore(target: any): JQuery; + + prepend(...content: any[]): JQuery; + prepend(func: (index: any, html: any) =>any): JQuery; + + prependTo(target: any): JQuery; + + remove(selector?: any): JQuery; + + replaceAll(target: any): JQuery; + + replaceWith(func: any): JQuery; + + text(textString: string): JQuery; + text(): string; + + toArray(): any[]; + + unwrap(): JQuery; + + wrap(wrappingElement: any): JQuery; + wrap(func: (index: any) =>any): JQuery; + + wrapAll(wrappingElement: any): JQuery; + + wrapInner(wrappingElement: any): JQuery; + wrapInner(func: (index: any) =>any): JQuery; + + /************* + MISCELLANEOUS + **************/ + each(func: (index: any, elem: Element) => JQuery); + + get(index?: number): any; + + index(selectorOrElement?: any): number; + + /********** + PROPERTIES + ***********/ + length: number; + [x: string]: HTMLElement; + [x: number]: HTMLElement; + + /********** + TRAVERSING + ***********/ + add(selector: string, context?: any): JQuery; + add(...elements: any[]): JQuery; + add(html: string): JQuery; + add(obj: JQuery): JQuery; + + andSelf(): JQuery; + + children(selector?: any): JQuery; + + closest(selector: string): JQuery; + closest(selector: string, context?: Element): JQuery; + closest(obj: JQuery): JQuery; + closest(element: any): JQuery; + closest(selectors: any, context?: Element): any[]; + + contents(): JQuery; + + end(): JQuery; + + eq(index: number): JQuery; + + filter(selector: string): JQuery; + filter(func: (index: any) =>any): JQuery; + filter(element: any): JQuery; + filter(obj: JQuery): JQuery; + + find(selector: string): JQuery; + find(element: any): JQuery; + find(obj: JQuery): JQuery; + + first(): JQuery; + + has(selector: string): JQuery; + has(contained: Element): JQuery; + + is(selector: string): JQuery; + is(func: (index: any) =>any): JQuery; + is(element: any): JQuery; + is(obj: JQuery): JQuery; + + last(): JQuery; + + map(callback: (index: any, domElement: Element) =>any): JQuery; + + next(selector?: string): JQuery; + + nextAll(selector?: string): JQuery; + + nextUntil(selector?: string, filter?: string): JQuery; + nextUntil(element?: Element, filter?: string): JQuery; + + not(selector: string): JQuery; + not(func: (index: any) =>any): JQuery; + not(element: any): JQuery; + not(obj: JQuery): JQuery; + + offsetParent(): JQuery; + + parent(selector?: string): JQuery; + + parents(selector?: string): JQuery; + + parentsUntil(selector?: string, filter?: string): JQuery; + parentsUntil(element?: Element, filter?: string): JQuery; + + prev(selector?: string): JQuery; + + prevAll(selector?: string): JQuery; + + prevUntil(selector?: string, filter?:string): JQuery; + prevUntil(element?: Element, filter?:string): JQuery; + + siblings(selector?: string): JQuery; + + slice(start: number, end?: number): JQuery; + + /********* + UTILITIES + **********/ + + queue(queueName?: string): any[]; + queue(queueName: string, newQueueOrCallback: any): JQuery; + queue(newQueueOrCallback: any): JQuery; +} + +declare var jQuery: JQueryStatic; +declare var $: JQueryStatic; \ No newline at end of file diff --git a/tests/automation/source-maps/internal/scripts/script.closure.js b/tests/automation/source-maps/internal/scripts/script.closure.js new file mode 100644 index 0000000000..be7ba3131e --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/script.closure.js @@ -0,0 +1,2 @@ +document.getElementById("color").focus();$(document).keydown(function(a){13==a.keyCode&&$("body").css("background-color",$("#color").val())}); +//@ sourceMappingURL=script.closure.js.map \ No newline at end of file diff --git a/tests/automation/source-maps/internal/scripts/script.closure.js.map b/tests/automation/source-maps/internal/scripts/script.closure.js.map new file mode 100644 index 0000000000..a3bb383db0 --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/script.closure.js.map @@ -0,0 +1,8 @@ +{ + "version":3, + "file":"script.closure.js", + "lineCount":1, + "mappings":"AACAA,QAAAC,eAAA,CAAwB,OAAxB,CAAAC,MAAA,EAEAC,EAAA,CAAEH,QAAF,CAAAI,QAAA,CAAoB,QAAQ,CAACC,CAAD,CAAM,CAGb,EAAnB,EAAIA,CAAAC,QAAJ,EAEEH,CAAA,CAAE,MAAF,CAAAI,IAAA,CAAc,kBAAd,CAAkCJ,CAAA,CAAE,QAAF,CAAAK,IAAA,EAAlC,CAL8B,CAAlC;", + "sources":["script1.js"], + "names":["document","getElementById","focus","$","keydown","evt","keyCode","css","val"] +} diff --git a/tests/automation/source-maps/internal/scripts/script.coffee.coffee b/tests/automation/source-maps/internal/scripts/script.coffee.coffee new file mode 100644 index 0000000000..506a5f7a53 --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/script.coffee.coffee @@ -0,0 +1,5 @@ +document.getElementById("color").focus() + +$(document).keydown (evt) -> + if evt.keyCode == 13 + $("body").css("background-color", $("#color").val()) diff --git a/tests/automation/source-maps/internal/scripts/script.coffee.js b/tests/automation/source-maps/internal/scripts/script.coffee.js new file mode 100644 index 0000000000..b7fa109dc5 --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/script.coffee.js @@ -0,0 +1,13 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + + document.getElementById("color").focus(); + + $(document).keydown(function(evt) { + if (evt.keyCode === 13) { + return $("body").css("background-color", $("#color").val()); + } + }); + +}).call(this); +//@ sourceMappingURL=script.coffee.js.map \ No newline at end of file diff --git a/tests/automation/source-maps/internal/scripts/script.coffee.js.map b/tests/automation/source-maps/internal/scripts/script.coffee.js.map new file mode 100644 index 0000000000..26883a4421 --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/script.coffee.js.map @@ -0,0 +1 @@ +{"version":3,"file":"script.coffee.coffee","sources":["script.coffee.coffee"],"names":[],"mappings":"AAAC;AAAA,QAAQ,eAAe,CAAC,OAAD,CAAS,MAAM,CAAA,EAAtC;AAEA,CAAC,CAAC,QAAD,CAAU,QAAX,CAAoB,SAAA,CAAA,GAAA,CAAA;MACd,GAAG,QAAH,CAAA,GAAA,CAAe;WAChB,CAAC,CAAC,MAAD,CAAQ,IAAI,CAAC,kBAAD,EAAqB,CAAC,CAAC,QAAD,CAAU,IAAI,CAAA,CAApC;CAFlB"} diff --git a/tests/automation/source-maps/internal/scripts/script.coffee.min.js b/tests/automation/source-maps/internal/scripts/script.coffee.min.js new file mode 100644 index 0000000000..9c9e11d29e --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/script.coffee.min.js @@ -0,0 +1,2 @@ +(function(){document.getElementById("color").focus();$(document).keydown(function(evt){if(evt.keyCode===13){return $("body").css("background-color",$("#color").val())}})}).call(this); +//@ sourceMappingURL=script.coffee.min.js.map \ No newline at end of file diff --git a/tests/automation/source-maps/internal/scripts/script.coffee.min.js.map b/tests/automation/source-maps/internal/scripts/script.coffee.min.js.map new file mode 100644 index 0000000000..2d70896e3f --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/script.coffee.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"script.coffee.min.js","sources":["script.coffee.coffee"],"names":[],"mappings":"CAAC,WAEoB,SACX,eAAY,SAAA,OADrB,GAAA,UAAA,QAAA,SAAA,KAAA,GAAA,IAAA,UAAA,GAAA,CAAA,MAAA,GAAA,QAAA,IAAA,mBAAA,EAAA,UAAA,YAAA,KAAA"} \ No newline at end of file diff --git a/tests/automation/source-maps/internal/scripts/script.js b/tests/automation/source-maps/internal/scripts/script.js new file mode 100644 index 0000000000..a4aa1db970 --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/script.js @@ -0,0 +1,12 @@ +// on load, put focus on the input +document.getElementById("color").focus(); + +$(document).keydown(function(evt) { + + // only if the user keypress is ENTER + if (evt.keyCode == 13) { + // css color names and hex code + $("body").css("background-color", $("#color").val()); + } + +}); \ No newline at end of file diff --git a/tests/automation/source-maps/internal/scripts/script.jsmin-grunt.js b/tests/automation/source-maps/internal/scripts/script.jsmin-grunt.js new file mode 100644 index 0000000000..e0877b3d38 --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/script.jsmin-grunt.js @@ -0,0 +1,3 @@ + +document.getElementById("color").focus();$(document).keydown(function(evt){if(evt.keyCode==13){$("body").css("background-color",$("#color").val());}}); +//@ sourceMappingURL=script.jsmin-grunt.js.map \ No newline at end of file diff --git a/tests/automation/source-maps/internal/scripts/script.jsmin-grunt.js.map b/tests/automation/source-maps/internal/scripts/script.jsmin-grunt.js.map new file mode 100644 index 0000000000..7377de2425 --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/script.jsmin-grunt.js.map @@ -0,0 +1 @@ +{"version":3,"file":"script.jsmin-grunt.js","sources":["script2.js"],"names":[],"mappings":"AAAkC;AAClC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAExC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAGhC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,CAAE,CAAC,CAAC,CAAE,CAErB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CACtD,CAEF,CAAC,CAAC"} diff --git a/tests/automation/source-maps/internal/scripts/script.typescript.js b/tests/automation/source-maps/internal/scripts/script.typescript.js new file mode 100644 index 0000000000..f4687533d8 --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/script.typescript.js @@ -0,0 +1,7 @@ +document.getElementById("color").focus(); +$(document).keydown(function (evt) { + if(evt.keyCode == 13) { + $("body").css("background-color", $("#color").val()); + } +}); +//@ sourceMappingURL=script.typescript.js.map diff --git a/tests/automation/source-maps/internal/scripts/script.typescript.js.map b/tests/automation/source-maps/internal/scripts/script.typescript.js.map new file mode 100644 index 0000000000..034a30378e --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/script.typescript.js.map @@ -0,0 +1 @@ +{"version":3,"file":"script.typescript.js","sources":["script.typescript.ts"],"names":[""],"mappings":"AAAA,QAGQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE;AAExC,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,UAAS,GAAG;IAG9BA,GAAIA,GAAGA,CAACA,OAAOA,IAAIA,EAAEA,CAACA;QAEpBA,CAACA,CAACA,MAAMA,CAACA,CAACA,GAAGA,CAACA,kBAAkBA,EAAEA,CAACA,CAACA,QAAQA,CAACA,CAACA,GAAGA,EAAEA,CAACA;KACrDA;AAEHA,CAACA,CAAC;AAAC"} \ No newline at end of file diff --git a/tests/automation/source-maps/internal/scripts/script.typescript.min.js b/tests/automation/source-maps/internal/scripts/script.typescript.min.js new file mode 100644 index 0000000000..75ed335420 --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/script.typescript.min.js @@ -0,0 +1,2 @@ +document.getElementById("color").focus();$(document).keydown(function(evt){if(evt.keyCode==13){$("body").css("background-color",$("#color").val())}}); +//@ sourceMappingURL=script.typescript.min.js.map \ No newline at end of file diff --git a/tests/automation/source-maps/internal/scripts/script.typescript.min.js.map b/tests/automation/source-maps/internal/scripts/script.typescript.min.js.map new file mode 100644 index 0000000000..7cd9eaa424 --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/script.typescript.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"script.typescript.min.js","sources":["script.typescript.ts"],"names":[],"mappings":"AAAA,SAGS,eAAe,SAAS,OAEjC,GAAE,UAAU,QAAQ,SAAS,KAG3B,GAAI,IAAI,SAAW,GAAG,CAEpB,EAAE,QAAQ,IAAI,mBAAoB,EAAE,UAAU"} \ No newline at end of file diff --git a/tests/automation/source-maps/internal/scripts/script.typescript.ts b/tests/automation/source-maps/internal/scripts/script.typescript.ts new file mode 100644 index 0000000000..20481fb5fc --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/script.typescript.ts @@ -0,0 +1,14 @@ +/// + +// on load, put focus on the input +document.getElementById("color").focus(); + +$(document).keydown(function(evt) { + + // only if the user keypress is ENTER + if (evt.keyCode == 13) { + // css color names and hex code + $("body").css("background-color", $("#color").val()); + } + +}); \ No newline at end of file diff --git a/tests/automation/source-maps/internal/scripts/script.uglify.js b/tests/automation/source-maps/internal/scripts/script.uglify.js new file mode 100644 index 0000000000..3392f6df36 --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/script.uglify.js @@ -0,0 +1,2 @@ +document.getElementById("color").focus();$(document).keydown(function(evt){if(evt.keyCode==13){$("body").css("background-color",$("#color").val())}}); +//@ sourceMappingURL=script.uglify.js.map \ No newline at end of file diff --git a/tests/automation/source-maps/internal/scripts/script.uglify.js.map b/tests/automation/source-maps/internal/scripts/script.uglify.js.map new file mode 100644 index 0000000000..cd200f92b4 --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/script.uglify.js.map @@ -0,0 +1 @@ +{"version":3,"file":"script.uglify.js","sources":["script.js"],"names":["document","getElementById","focus","$","keydown","evt","keyCode","css","val"],"mappings":"AACAA,SAASC,eAAe,SAASC,OAEjCC,GAAEH,UAAUI,QAAQ,SAASC,KAG3B,GAAIA,IAAIC,SAAW,GAAI,CAErBH,EAAE,QAAQI,IAAI,mBAAoBJ,EAAE,UAAUK"} \ No newline at end of file diff --git a/tests/automation/source-maps/internal/scripts/script1.js b/tests/automation/source-maps/internal/scripts/script1.js new file mode 100644 index 0000000000..a4aa1db970 --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/script1.js @@ -0,0 +1,12 @@ +// on load, put focus on the input +document.getElementById("color").focus(); + +$(document).keydown(function(evt) { + + // only if the user keypress is ENTER + if (evt.keyCode == 13) { + // css color names and hex code + $("body").css("background-color", $("#color").val()); + } + +}); \ No newline at end of file diff --git a/tests/automation/source-maps/internal/scripts/script2.js b/tests/automation/source-maps/internal/scripts/script2.js new file mode 100644 index 0000000000..a4aa1db970 --- /dev/null +++ b/tests/automation/source-maps/internal/scripts/script2.js @@ -0,0 +1,12 @@ +// on load, put focus on the input +document.getElementById("color").focus(); + +$(document).keydown(function(evt) { + + // only if the user keypress is ENTER + if (evt.keyCode == 13) { + // css color names and hex code + $("body").css("background-color", $("#color").val()); + } + +}); \ No newline at end of file diff --git a/tests/automation/source-maps/internal/styles/style.css b/tests/automation/source-maps/internal/styles/style.css new file mode 100644 index 0000000000..ab7318910d --- /dev/null +++ b/tests/automation/source-maps/internal/styles/style.css @@ -0,0 +1,20 @@ +@media -sass-debug-info{filename{font-family:file\:\/start\/styles\/style\.sass}line{font-family:\000034}} +body { + background-color: #d9d9d9; } + +@media -sass-debug-info{filename{font-family:file\:\/start\/styles\/style\.sass}line{font-family:\000037}} +input { + background-color: rgba(255, 255, 255, 0.5); + border: 0; + display: block; + font-size: 3em; + margin: 15% auto; + outline: none; + padding: 0.4em; + width: 20%; } + +@media only screen and (max-width: 600px) { +@media -sass-debug-info{filename{font-family:file\:\/start\/styles\/style\.sass}line{font-family:\0000318}} + input { + font-size: 2em; + width: 40%; } } diff --git a/tests/automation/source-maps/internal/styles/style.sass b/tests/automation/source-maps/internal/styles/style.sass new file mode 100644 index 0000000000..3a024e308e --- /dev/null +++ b/tests/automation/source-maps/internal/styles/style.sass @@ -0,0 +1,20 @@ +$main-color: #000 +$secondary-color: #fff + +body + background-color: lighten($main-color, 85%) + +input + background-color: transparentize($secondary-color, 0.5) + border: 0 + display: block + font-size: 3em + margin: 15% auto + outline: none + padding: 0.4em + width: 20% + +@media only screen and (max-width: 600px) + input + font-size: 2em + width: 40% diff --git a/tests/automation/source-maps/internal/styles/style.scss b/tests/automation/source-maps/internal/styles/style.scss new file mode 100644 index 0000000000..21bf031a23 --- /dev/null +++ b/tests/automation/source-maps/internal/styles/style.scss @@ -0,0 +1,24 @@ +$main-color: #000; +$secondary-color: #fff; + +body{ + background-color: lighten($main-color, 85%); +} + +input{ + background-color: transparentize($secondary-color, 0.5); + border: 0; + display: block; + font-size: 3em; + margin: 15% auto; + outline: none; + padding: 0.4em; + width: 20%; +} + +@media only screen and (max-width: 600px){ + input{ + font-size: 2em; + width: 40%; + } +} diff --git a/tests/automation/source-maps/mocha_test.js b/tests/automation/source-maps/mocha_test.js new file mode 100644 index 0000000000..f7144463c3 --- /dev/null +++ b/tests/automation/source-maps/mocha_test.js @@ -0,0 +1,66 @@ +var path = require('path'); +var assert = require('assert'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + + +describe('source maps', function() { + + var server, child, results, ok = false; + + before(function(done) { + this.timeout(0); + server = createTCPServer(13013); + child = spawnChildProcess(path.join(curDir, 'internal')); + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + ok = true; + results = JSON.parse(data); + child.kill(); + done(); + }); + }); + + setTimeout(function() { + if (!ok) { + child.kill(); + done('timeout'); + } + }, 4500); + + }); + + after(function () { + server.close(); + }); + + it('should support Closure Compile', function() { + assert.equal("script.closure.js", results[0]); + assert.equal("script1.js", results[8]); + }); + + it('should support JSMin and Grunt', function() { + assert.equal("script.jsmin-grunt.js", results[4]); + assert.equal("script2.js", results[9]); + }); + + it('should support Uglifyjs', function() { + assert.equal('script.uglify.js', results[7]); + assert.equal('script.js', results[3]); + }); + + it('should support CoffeeScript', function() { + assert.equal('script.coffee.min.js', results[2]); + assert.equal('script.coffee.coffee', results[1]); + }); + + it('should support TypeScript', function() { + assert.equal('script.typescript.min.js', results[5]); + assert.equal('script.typescript.ts', results[6]); + }); + + +}); + + diff --git a/tests/automation/source-maps/package.json b/tests/automation/source-maps/package.json new file mode 100644 index 0000000000..9c9bc79877 --- /dev/null +++ b/tests/automation/source-maps/package.json @@ -0,0 +1,5 @@ +{ + "name":"source_map_wrapper", + "main":"index.html" +} + diff --git a/tests/automation/start_app/index.html b/tests/automation/start_app/index.html new file mode 100644 index 0000000000..e7d0082e15 --- /dev/null +++ b/tests/automation/start_app/index.html @@ -0,0 +1,12 @@ + + + Hello World + + +

    Start app test

    + + + + + + diff --git a/tests/automation/start_app/internal/index.html b/tests/automation/start_app/internal/index.html new file mode 100644 index 0000000000..043908786a --- /dev/null +++ b/tests/automation/start_app/internal/index.html @@ -0,0 +1,16 @@ + + + Hello World + + + +

    this is node-webkit

    + + + diff --git a/tests/automation/start_app/internal/package.json b/tests/automation/start_app/internal/package.json new file mode 100644 index 0000000000..cc5dc359a1 --- /dev/null +++ b/tests/automation/start_app/internal/package.json @@ -0,0 +1,8 @@ +{ + "name": "nw-demo", + "version": "0.1.0", + "main": "index.html", + "window": { + "show": false + } +} diff --git a/tests/automation/start_app/mocha_test.js b/tests/automation/start_app/mocha_test.js new file mode 100644 index 0000000000..f8a0132516 --- /dev/null +++ b/tests/automation/start_app/mocha_test.js @@ -0,0 +1,138 @@ +var spawn = require('child_process').spawn; +var exec = require('child_process').exec; +var path = require('path'); +var net = require('net'); +var os = require('os'); +var fs = require('fs-extra'); +var curDir = require('fs').realpathSync('.'); +var func = require(path.join(curDir, 'script.js')); +var execPath = path.join(curDir, func.getExecPath()); + +describe('Startup', function() { +describe('different method of starting app (long-to-run)', function() { + + before(function(done){ + this.timeout(10000); + func.copyExecFiles(function() { + func.copySourceFiles('internal'); + func.zipSourceFiles(function() { + func.makeExecuableFile(); + done(); + }); + }); + + }); + + after(function(done) { + setTimeout(function() { + fs.remove(path.join(curDir, 'tmp-nw'), function (er) { + if (er) { + console.log('Failed to remove the temporary folder tmp-nw: ' + er); + throw er; + } + done(); + }) + }, 1000); + }) + + it('start from nw that package.json in the same folder', function(done) { + this.timeout(0); + var result = false; + + if (os.platform() == 'darwin') { + //mac don't have this method + done(); + return; + } + + var app = spawn(execPath); + app.on('exit', function() { + result = true; + done(); + }); + + setTimeout(function() { + if (!result) { + done('timeout'); + app.kill(); + } + }, 10000); + + }) + + it('start from app.nw', function(done) { + this.timeout(0); + var result = false; + + var app = spawn(execPath, [path.join(curDir, 'tmp-nw', 'app.nw')]); + app.on('exit', function() { + result = true; + done(); + }); + setTimeout(function() { + if (!result) { + done('timeout'); + app.kill(); + } + }, 10000); + + }) + + it('start from folder contains `../`', function(done) { + this.timeout(0); + var result = false; + var app = spawn(execPath, [path.join(curDir, '..', '..', 'tmp-nw')]); + + + app.on('exit', function() { + result = true; + done(); + }); + + setTimeout(function() { + if (!result) { + done('timeout'); + app.kill(); + } + }, 10000); + }) + + it('start from an executable file app.exe', function(done) { + this.timeout(0); + var result = false; + function launch(appPath) { + var app = spawn(appPath); + app.on('exit', function() { + result = true; + done(); + }); + + setTimeout(function() { + if (!result) { + done('timeout'); + app.kill(); + } + }, 10000); + } + + if (os.platform() == 'win32') { + launch(path.join(curDir, 'tmp-nw', 'app.exe')); + } + if (os.platform() == 'linux') { + launch(path.join(curDir, 'tmp-nw', 'app')); + } + if (os.platform() == 'darwin') { + var app_path = curDir + 'tmp-nw/node-webkit.app/Contents/Resources/app.nw'; + fs.mkdir(app_path, function(err) { + if(err && err.code !== 'EEXIST') throw err + fs.copy(path.join(curDir, 'tmp-nw', 'internal', 'index.html'), path.join(app_path, 'index.html')); + fs.copy(path.join(curDir, 'tmp-nw', 'internal', 'package.json'), path.join(app_path, 'package.json'), function() { + launch(app_path); + }); + }); + } + + }) + +}) +}) diff --git a/tests/automation/start_app/package.json b/tests/automation/start_app/package.json new file mode 100644 index 0000000000..15ea52e629 --- /dev/null +++ b/tests/automation/start_app/package.json @@ -0,0 +1,5 @@ +{ + "name": "start_app_wrapper", + "version": "0.1.0", + "main": "index.html" +} diff --git a/tests/automation/start_app/script.js b/tests/automation/start_app/script.js new file mode 100644 index 0000000000..9ad4d05d7f --- /dev/null +++ b/tests/automation/start_app/script.js @@ -0,0 +1,91 @@ +var path = require('path'); +var os = require('os'); +var fsextra = require('fs-extra'); +var cp = require('child_process'); +var exec = cp.exec; +var sqawn = cp.sqawn; +var global = {}; +global.tests_dir = fsextra.realpathSync('.'); + +var required_file_win_linux = fsextra.readdirSync(path.dirname(process.execPath)); +var required_file_win = required_file_win_linux; +var required_file_linux = required_file_win_linux; + +var required_file_macox = [ + 'node-webkit.app' +]; + +var source_file = ['index.html', 'package.json']; + +var exec_root = path.dirname(process.execPath); +var required_file; +if (os.platform() == 'win32') { + required_file = required_file_win; +} +if (os.platform() == 'linux') { + required_file = required_file_linux; +} +if (os.platform() == 'darwin') { + required_file = required_file_macox; + if (~exec_root.indexOf("Helper.app")) + exec_root = path.join(exec_root, '..', '..', '..') + exec_root = path.normalize( + path.join(exec_root, '..', '..', '..')); +} + + + +exports.getExecPath = function() { + if (os.platform() == 'win32') { + return path.join('tmp-nw', 'nw.exe'); + } + if (os.platform() == 'linux') { + return path.join('tmp-nw', 'nw'); + } + if (os.platform() == 'darwin') { + return path.join('tmp-nw', 'node-webkit.app', 'Contents', 'MacOS', 'node-webkit'); + } + +} + +function copyExecFiles(done) { + fsextra.mkdir('tmp-nw', function(err) { + if(err && err.code !== 'EEXIST') throw err; + var files_done = 0; + for (var i in required_file) { + var src_file = path.join(exec_root, required_file[i]); + var dst_file = path.join('tmp-nw', required_file[i]); + fsextra.copySync(src_file, dst_file); + } + done(); + }); + +} + +exports.copySourceFiles = function(folder) { + if (folder == undefined) + folder = 'start_app'; + fsextra.createReadStream(global.tests_dir + '/' + folder + '/index.html').pipe( + fsextra.createWriteStream('tmp-nw/index.html')); + fsextra.createReadStream(global.tests_dir + '/' + folder + '/package.json').pipe( + fsextra.createWriteStream('tmp-nw/package.json')); + +} + +exports.zipSourceFiles = function(callback) { + exec('python '+path.join(global.tests_dir, 'zip.py')); + setTimeout(callback, 2000); +} + +exports.makeExecuableFile = function() { + if (os.platform() == 'win32') { + cp.exec('copy /b nw.exe+app.nw app.exe', {cwd: './tmp-nw/'}); + } + if (os.platform() == 'linux') { + cp.exec('cat nw app.nw > app && chmod +x app', {cwd: './tmp-nw/'}); + } + +} + + +exports.copyExecFiles = copyExecFiles; diff --git a/tests/automation/start_app/zip.py b/tests/automation/start_app/zip.py new file mode 100644 index 0000000000..66f228f716 --- /dev/null +++ b/tests/automation/start_app/zip.py @@ -0,0 +1,14 @@ +import zipfile +import os +curDir = os.path.dirname(os.path.abspath(__file__)) +zip = zipfile.ZipFile(os.path.join(curDir, 'tmp-nw', 'app.nw'), 'w', + compression=zipfile.ZIP_DEFLATED) + +source_file = ['index.html', 'package.json'] + +for file in source_file: + path = os.path.join(curDir, 'tmp-nw', file) + zip.write(path, file) + +zip.close(); + diff --git a/tests/automation/temp_dir/index.html b/tests/automation/temp_dir/index.html new file mode 100644 index 0000000000..5146dd6241 --- /dev/null +++ b/tests/automation/temp_dir/index.html @@ -0,0 +1,16 @@ + + + + + temp dir test + + +

    temp dir test

    + + + + + + + + diff --git a/tests/automation/temp_dir/internal/index.html b/tests/automation/temp_dir/internal/index.html new file mode 100644 index 0000000000..4cddf36d08 --- /dev/null +++ b/tests/automation/temp_dir/internal/index.html @@ -0,0 +1,26 @@ + + + + Test CASE FOR TMP DIR SHOULD BE REMOVED AFTER APP EXIT + + +

    Hello World!

    + + + diff --git a/tests/automation/temp_dir/internal/package.json b/tests/automation/temp_dir/internal/package.json new file mode 100644 index 0000000000..b9b7eb06de --- /dev/null +++ b/tests/automation/temp_dir/internal/package.json @@ -0,0 +1,4 @@ +{ + "name": "nw", + "main": "index.html" +} diff --git a/tests/automation/temp_dir/mocha_test.js b/tests/automation/temp_dir/mocha_test.js new file mode 100644 index 0000000000..271b7b55e2 --- /dev/null +++ b/tests/automation/temp_dir/mocha_test.js @@ -0,0 +1,70 @@ +var path = require('path'); +var os = require('os'); +var cp = require('child_process'); +var assert = require('assert'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + +var func = require(path.join(curDir, '..', 'start_app', 'script.js')); +var execPath = func.getExecPath(); + + +var mac_app_path = path.join(curDir, 'tmp-nw', 'node-webkit.app'); + +var temp_path; + +if (os.platform() == 'win32') { + execPath = path.join(curDir, 'tmp-nw', 'app.exe'); +} else if (os.platform() == 'linux') { + execPath = path.join(curDir, 'tmp-nw', 'app'); +} else if (os.platform() == 'darwin') { + execPath = path.join(curDir, 'tmp-nw', 'node-webkit.app', 'Contents', 'MacOS', 'node-webkit'); +} + +function make_execuable_file(folder_path, done) { + func.copyExecFiles(function() { + func.copySourceFiles(folder_path); + func.zipSourceFiles(function() { + func.makeExecuableFile(); + if (os.platform() == 'darwin') { + var app_path = 'tmp-nw/node-webkit.app/Contents/Resources/app.nw'; + fs.mkdir(app_path, function(err) { + if(err && err.code !== 'EEXIST') throw err + fs.copy('tmp-nw/index.html', path.join(app_path, 'index.html')); + fs.copy('tmp-nw/package.html', path.join(app_path, 'package.html')); + setTimeout(done, 3000); + + }); + } else { + setTimeout(function() { + var child = cp.spawn(execPath, curDir); // [path.join(curDir, 'internal')]); + child.on('exit', function() { + temp_path = path.dirname(fs.readFileSync(path.join(curDir, 'tmp-nw','path.org'))).substring(8); + done(); + }); + }, 3000); + } + }); + }); +} + +describe('temp_dir', function() { + this.timeout(0); + + before(function(done) { + make_execuable_file('internal', done); + }); + + after(function() { + fs.remove('tmp-nw', function (er) { + if (er) throw er; + }); + }); + + it('should be removed after app exit', function() { + var exist = fs.existsSync(temp_path); + assert.equal(exist, false); + }); +}); + + diff --git a/tests/automation/temp_dir/package.json b/tests/automation/temp_dir/package.json new file mode 100644 index 0000000000..1d0c39102d --- /dev/null +++ b/tests/automation/temp_dir/package.json @@ -0,0 +1,5 @@ +{ + "name":"temp_dir_wrapper", + "main":"index.html" +} + diff --git a/tests/automation/user-agent-app/index.html b/tests/automation/user-agent-app/index.html new file mode 100644 index 0000000000..31eb0c66d0 --- /dev/null +++ b/tests/automation/user-agent-app/index.html @@ -0,0 +1,16 @@ + + + + + user agent app test + + +

    user agent app test

    + + + + + + + + diff --git a/tests/automation/user-agent-app/internal/index.html b/tests/automation/user-agent-app/internal/index.html new file mode 100644 index 0000000000..517c1a0c48 --- /dev/null +++ b/tests/automation/user-agent-app/internal/index.html @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/tests/automation/user-agent-app/internal/package.json b/tests/automation/user-agent-app/internal/package.json new file mode 100644 index 0000000000..ec487cf9a5 --- /dev/null +++ b/tests/automation/user-agent-app/internal/package.json @@ -0,0 +1,5 @@ +{ + "name": "user agent app", + "main": "index.html", + "user-agent": "%name/%nwver/%ver/%webkit_ver/%osinfo" +} \ No newline at end of file diff --git a/tests/automation/user-agent-app/mocha_test.js b/tests/automation/user-agent-app/mocha_test.js new file mode 100644 index 0000000000..a395d0c7b9 --- /dev/null +++ b/tests/automation/user-agent-app/mocha_test.js @@ -0,0 +1,47 @@ +var path = require('path'); +var assert = require('assert'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + + +describe('user-agent-app', function() { + + var result = false, package_name, package_info; + + before(function(done) { + this.timeout(0); + server = createTCPServer(13013); + child = spawnChildProcess(path.join(curDir, 'internal')); + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + result = true; + package_name = data; + package_info = JSON.parse(fs.readFileSync(path.join(curDir, 'internal', 'package.json'), 'utf8')); + child.kill(); + done(); + }); + }); + + setTimeout(function(){ + if (!result) { + child.kill(); + done('loaded event does not been fired'); + } + }, 7000); + + }); + + after(function () { + server.close(); + }); + + it('user agent shoud be the same when open a new window', + function() { + assert.equal(result, true); + assert.equal(package_name, package_info.name); + }); + + +}); + diff --git a/tests/automation/user-agent-app/package.json b/tests/automation/user-agent-app/package.json new file mode 100644 index 0000000000..9a59564273 --- /dev/null +++ b/tests/automation/user-agent-app/package.json @@ -0,0 +1,5 @@ +{ + "name":"user_agent_app_wrapper", + "main":"index.html" +} + diff --git a/tests/automation/user-agent/index.html b/tests/automation/user-agent/index.html new file mode 100644 index 0000000000..823bfc1920 --- /dev/null +++ b/tests/automation/user-agent/index.html @@ -0,0 +1,16 @@ + + + + + user agent test + + +

    user agent test

    + + + + + + + + diff --git a/tests/automation/user-agent/internal/index.html b/tests/automation/user-agent/internal/index.html new file mode 100644 index 0000000000..6a9e30c610 --- /dev/null +++ b/tests/automation/user-agent/internal/index.html @@ -0,0 +1,58 @@ + + + + Test Case For user-agent! + + +

    Hello User-Agent

    + + + diff --git a/tests/automation/user-agent/internal/index1.html b/tests/automation/user-agent/internal/index1.html new file mode 100644 index 0000000000..829a1c6904 --- /dev/null +++ b/tests/automation/user-agent/internal/index1.html @@ -0,0 +1,14 @@ + + + + Child Window For user-agent! + + +

    Hello Child User-Agent

    + + + diff --git a/tests/automation/user-agent/internal/package.json b/tests/automation/user-agent/internal/package.json new file mode 100644 index 0000000000..e106169070 --- /dev/null +++ b/tests/automation/user-agent/internal/package.json @@ -0,0 +1,5 @@ +{ + "name": "nw-user-agent-test", + "main": "index.html", + "user-agent": "%name/%nwver/%ver/%webkit_ver/%osinfo" +} diff --git a/tests/automation/user-agent/mocha_test.js b/tests/automation/user-agent/mocha_test.js new file mode 100644 index 0000000000..f8cec10e2a --- /dev/null +++ b/tests/automation/user-agent/mocha_test.js @@ -0,0 +1,64 @@ +var path = require('path'); +var assert = require('assert'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + +describe('user-agent', function() { + + var server, child, results; + + before(function(done) { + this.timeout(0); + server = createTCPServer(13013); + child = spawnChildProcess(path.join(curDir, 'internal')); + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + results = JSON.parse(data); + for (var i = 0, len = results.length; i < len; i++) { + console.log('User agent['+i+']:' + results[i]); + } + child.kill(); + done(); + }); + }); + + }); + + after(function () { + server.close(); + }); + + + describe('user-agent with child window opened', function() { + it('should be same to the one in parent window', function() { + assert.equal(results[0], results[1]); + }); + }); + + describe('user-agent with reload', function() { + it('should be same with all reload method', function() { + assert.equal(results[0], results[2]); + assert.equal(results[0], results[3]); + assert.equal(results[0], results[4]); + assert.equal(results[0], results[5]); + }); + }); + + describe('user-agent with package.json', function() { + var user_agent = navigator.userAgent.split('/'); + var package_info = JSON.parse(fs.readFileSync(path.join(curDir, 'internal', 'package.json'), 'utf8')); + it('name should be same to the one in package.json', + function() { + assert.equal(user_agent[0], package_info.name); + }); + + it('version should be same to the one in package.json', + function() { + assert.equal(user_agent[2], package_info.version); + }); + }); + +}); + + diff --git a/tests/automation/user-agent/package.json b/tests/automation/user-agent/package.json new file mode 100644 index 0000000000..26c3b524a8 --- /dev/null +++ b/tests/automation/user-agent/package.json @@ -0,0 +1,5 @@ +{ + "name":"user_agent_wrapper", + "main":"index.html" +} + diff --git a/tests/automation/website/index.html b/tests/automation/website/index.html new file mode 100644 index 0000000000..c742b26e0a --- /dev/null +++ b/tests/automation/website/index.html @@ -0,0 +1,16 @@ + + + + + website test + + +

    website test

    + + + + + + + + diff --git a/tests/automation/website/mocha_test.js b/tests/automation/website/mocha_test.js new file mode 100644 index 0000000000..fcaca4cec5 --- /dev/null +++ b/tests/automation/website/mocha_test.js @@ -0,0 +1,51 @@ +var assert = require('assert'); + +describe('website', function() { + describe('scores', function() { + var gui = require('nw.gui'); + + it('html5test.com should score high (long-to-run)', function(done) { + this.timeout(0); + var win = gui.Window.open('http://html5test.com/', { show: false }); + win.on('loaded', function() { + var results = win.window.document.getElementById('results'); + if (results == null){ + done('Can not connect to the web'); + } else { + try { + var score = results.childNodes[0].childNodes[1].innerHTML; + if (score >= 445) { + done(); + } else { + done('have a low score'); + } + } catch (e) { + done('failed to get score'); + } // try-catch + + } + win.close(); + }); + }); + + + it('should support WebGL at get.webgl.org', function(done) { + this.timeout(0); + var win = gui.Window.open('http://get.webgl.org/', { show: false }); + win.on('loaded', function() { + var results = win.window.document.getElementById('webgl-yes'); + if (results.classList.contains('webgl-hidden')) { + done('do not support WebGL'); + } else { + done(); + } + win.close(); + }); + + }); + + + }); +}); + + diff --git a/tests/automation/website/package.json b/tests/automation/website/package.json new file mode 100644 index 0000000000..7c67f7e726 --- /dev/null +++ b/tests/automation/website/package.json @@ -0,0 +1,5 @@ +{ + "name":"website_wrapper", + "main":"index.html" +} + diff --git a/tests/automation/window-document-event/index.html b/tests/automation/window-document-event/index.html new file mode 100644 index 0000000000..4b8b8ab768 --- /dev/null +++ b/tests/automation/window-document-event/index.html @@ -0,0 +1,16 @@ + + + + + window document event test + + +

    window document event test

    + + + + + + + + diff --git a/tests/automation/window-document-event/internal/iframe.html b/tests/automation/window-document-event/internal/iframe.html new file mode 100644 index 0000000000..bdcdcbb44e --- /dev/null +++ b/tests/automation/window-document-event/internal/iframe.html @@ -0,0 +1,13 @@ + + + + + + + +

    Iframe

    + + + \ No newline at end of file diff --git a/tests/automation/window-document-event/internal/index.html b/tests/automation/window-document-event/internal/index.html new file mode 100644 index 0000000000..e5527cf9e5 --- /dev/null +++ b/tests/automation/window-document-event/internal/index.html @@ -0,0 +1,63 @@ + + + + + test + + +

    it works!

    + + + + diff --git a/tests/automation/window-document-event/internal/new_win.html b/tests/automation/window-document-event/internal/new_win.html new file mode 100644 index 0000000000..aa328ef904 --- /dev/null +++ b/tests/automation/window-document-event/internal/new_win.html @@ -0,0 +1,17 @@ + + + + + + + +

    New Window

    + + + + \ No newline at end of file diff --git a/tests/automation/window-document-event/internal/package.json b/tests/automation/window-document-event/internal/package.json new file mode 100644 index 0000000000..85ea1d2a75 --- /dev/null +++ b/tests/automation/window-document-event/internal/package.json @@ -0,0 +1,5 @@ +{ + "name":"nw_1403587578", + "main":"index.html", + "dependencies":{} +} \ No newline at end of file diff --git a/tests/automation/window-document-event/mocha_test.js b/tests/automation/window-document-event/mocha_test.js new file mode 100644 index 0000000000..7fa0d55613 --- /dev/null +++ b/tests/automation/window-document-event/mocha_test.js @@ -0,0 +1,74 @@ +var path = require('path'); +var assert = require('assert'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + +describe('document-start/end',function(){ + + var server, child, results; + + before(function(done) { + this.timeout(0); + server = createTCPServer(13013); + child = spawnChildProcess(path.join(curDir, 'internal')); + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + results = JSON.parse(''+data); + child.kill(); + done(); + }); + }); + + }); + + after(function () { + server.close(); + }); + + + it('results should not equal undefined',function(done){ + assert.notEqual(results,undefined); + done(); + }) + + it('new window document-start run first',function(done){ + assert.equal(results[0]['flag'],true) + assert.equal(results[0]['name'],'top-window-document-start') + done(); + }); + it('new window script run between document-start and document-end',function(done){ + assert.equal(results[1],'new-window-script'); + done(); + }); + it('new window document-end should run before onload event',function(done){ + assert.equal(results[2]['flag'],true) + assert.equal(results[2]['name'],'top-window-document-end') + done(); + }); + it('new window onload should run last',function(done){ + assert.equal(results[3],'onload-from-new-window'); + done(); + }); + + it('iframe document-start should run first',function(done){ + assert.equal(results[4]['flag'],true) + assert.equal(results[4]['name'],'iframe-document-start') + done(); + }); + it('iframe script run between document-start and document-end',function(done){ + assert.equal(results[5],'iframe-script'); + done(); + }); + it('iframe document-end should run later',function(done){ + assert.equal(results[6]['flag'],true) + assert.equal(results[6]['name'],'iframe-document-end') + done(); + }); + it('iframe onload should run last',function(done){ + assert.equal(results[7],'onload-from-iframe'); + done(); + }); +}); + + diff --git a/tests/automation/window-document-event/package.json b/tests/automation/window-document-event/package.json new file mode 100644 index 0000000000..ca9df08169 --- /dev/null +++ b/tests/automation/window-document-event/package.json @@ -0,0 +1,5 @@ +{ + "name":"window_document_event_wrapper", + "main":"index.html" +} + diff --git a/tests/automation/window-eval/index.html b/tests/automation/window-eval/index.html new file mode 100644 index 0000000000..3548d2e5b5 --- /dev/null +++ b/tests/automation/window-eval/index.html @@ -0,0 +1,16 @@ + + + + + window eval test + + +

    window eval test

    + + + + + + + + diff --git a/tests/automation/window-eval/internal/iframe.html b/tests/automation/window-eval/internal/iframe.html new file mode 100644 index 0000000000..ab1d2c0ffa --- /dev/null +++ b/tests/automation/window-eval/internal/iframe.html @@ -0,0 +1,14 @@ + + + + + test + + +

    iframe

    + + + + \ No newline at end of file diff --git a/tests/automation/window-eval/internal/index.html b/tests/automation/window-eval/internal/index.html new file mode 100644 index 0000000000..4089aa8ba8 --- /dev/null +++ b/tests/automation/window-eval/internal/index.html @@ -0,0 +1,35 @@ + + + + + test + + +

    it works!

    + + + + + diff --git a/tests/automation/window-eval/internal/package.json b/tests/automation/window-eval/internal/package.json new file mode 100644 index 0000000000..a33ab28774 --- /dev/null +++ b/tests/automation/window-eval/internal/package.json @@ -0,0 +1,5 @@ +{ + "name":"nw_1403254112", + "main":"index.html", + "dependencies":{} +} \ No newline at end of file diff --git a/tests/automation/window-eval/mocha_test.js b/tests/automation/window-eval/mocha_test.js new file mode 100644 index 0000000000..de11d3e1e9 --- /dev/null +++ b/tests/automation/window-eval/mocha_test.js @@ -0,0 +1,35 @@ +var path = require('path'); +var assert = require('assert'); +var fs = require('fs-extra'); +var curDir = fs.realpathSync('.'); + + +describe('Window.eval',function(){ + var server, child, result; + + before(function(done) { + this.timeout(0); + server = createTCPServer(13013); + child = spawnChildProcess(path.join(curDir, 'internal')); + server.on('connection', function(socket) { + socket.setEncoding('utf8'); + socket.on('data', function(data) { + result = (data.toString() == "success"); + child.kill(); + done(); + }); + }); + + }); + + after(function () { + server.close(); + }); + + it("Window.eval should works",function(done){ + assert.equal(result,true); + done(); + }); +}); + + diff --git a/tests/automation/window-eval/package.json b/tests/automation/window-eval/package.json new file mode 100644 index 0000000000..3b31df6584 --- /dev/null +++ b/tests/automation/window-eval/package.json @@ -0,0 +1,5 @@ +{ + "name":"window_eval_wrapper", + "main":"index.html" +} + diff --git a/tests/automation/window/index.html b/tests/automation/window/index.html new file mode 100644 index 0000000000..f4cb6778bc --- /dev/null +++ b/tests/automation/window/index.html @@ -0,0 +1,59 @@ + + + + + Test Case for 'Window.focus' + + + +

    For now you should manually click the button to test the case

    + + + + + + + diff --git a/tests/automation/window/index1.html b/tests/automation/window/index1.html new file mode 100644 index 0000000000..0c6385bceb --- /dev/null +++ b/tests/automation/window/index1.html @@ -0,0 +1,19 @@ + + + + + new_win1 + + + +

    Please focus this before click the according button .

    + + diff --git a/tests/automation/window/index2.html b/tests/automation/window/index2.html new file mode 100644 index 0000000000..d4f09e42d1 --- /dev/null +++ b/tests/automation/window/index2.html @@ -0,0 +1,10 @@ + + + + + new_win2 + + +

    Please wait to be closed.

    + + diff --git a/tests/automation/window/mocha_test.js b/tests/automation/window/mocha_test.js new file mode 100644 index 0000000000..983cb6b705 --- /dev/null +++ b/tests/automation/window/mocha_test.js @@ -0,0 +1,30 @@ +var path = require('path'); +var assert = require('assert'); + +describe('window', function() { + this.timeout(0); + before(function(done) { + this.timeout(0); + + + var checkDone = function() { + if (test_done) { + done(); + } else { + setTimeout(function() {checkDone();}, 2000);; + } + }; + + checkDone(); + + }); + + describe('focus()', function() { + it('should focus on the window', function() { + for (var i = 0; i < 3; i++) { + console.log('win_c['+i+'] = '+ win_c[i]); + assert.notEqual(win_c[i], "0"); + } + }); + }); +}); diff --git a/tests/automation/window/package.json b/tests/automation/window/package.json new file mode 100644 index 0000000000..1d6d4d34f4 --- /dev/null +++ b/tests/automation/window/package.json @@ -0,0 +1,4 @@ +{ + "name": "nw-window-test", + "main": "index.html" +} diff --git a/tests/index.html b/tests/index.html index 1e9e538aaa..8a83bc7673 100644 --- a/tests/index.html +++ b/tests/index.html @@ -1,6 +1,6 @@ - + + +
    + +

    A custom tray menu

    +

    +

    + + \ No newline at end of file diff --git a/tests/manual_tests/custom_tray_menu/icon.png b/tests/manual_tests/custom_tray_menu/icon.png new file mode 100644 index 0000000000..50d9c17541 Binary files /dev/null and b/tests/manual_tests/custom_tray_menu/icon.png differ diff --git a/tests/manual_tests/custom_tray_menu/index.html b/tests/manual_tests/custom_tray_menu/index.html new file mode 100644 index 0000000000..82441e8b7c --- /dev/null +++ b/tests/manual_tests/custom_tray_menu/index.html @@ -0,0 +1,48 @@ + + + + + \ No newline at end of file diff --git a/tests/manual_tests/custom_tray_menu/package.json b/tests/manual_tests/custom_tray_menu/package.json new file mode 100644 index 0000000000..a61be4902e --- /dev/null +++ b/tests/manual_tests/custom_tray_menu/package.json @@ -0,0 +1,24 @@ +{ + "main": "index.html", + "name": "nw-test", + "description": "test app", + "window": { + "title": "My Node-Webkit App", + "icon": "icon.png", + "toolbar": false, + "frame": true, + "width": 800, + "height": 500, + "always-on-top": false, + "kiosk": false, + "fullscreen": false, + "show": false, + "show_in_taskbar": false, + "resizable": true + }, + "webkit": { + "plugin": false, + "java": false, + "page-cache": false + } +} \ No newline at end of file diff --git a/tests/manual_tests/menu/index.html b/tests/manual_tests/menu/index.html index e1e772fa4b..d67823ac98 100644 --- a/tests/manual_tests/menu/index.html +++ b/tests/manual_tests/menu/index.html @@ -193,7 +193,7 @@ } })); menubar.append(new gui.MenuItem({ label: 'Sub1', submenu: sub1})); - menubar.append(new gui.MenuItem({ label: 'Sub2', submenu: sub2})); + //menubar.append(new gui.MenuItem({ label: 'Sub2', submenu: sub2})); win.menu = menubar; diff --git a/tests/manual_tests/menu_shortcut/index.html b/tests/manual_tests/menu_shortcut/index.html index ecd349e91e..fdb1f05426 100644 --- a/tests/manual_tests/menu_shortcut/index.html +++ b/tests/manual_tests/menu_shortcut/index.html @@ -18,7 +18,6 @@

    var menubar = new gui.Menu({ type: 'menubar' }); var menubar_file = new gui.MenuItem({"label":"File"}); - menubar.append(menubar_file); var file_menu = new gui.Menu(); var file_quit_item = new gui.MenuItem({ "label":"Quit", @@ -50,9 +49,10 @@

    file_menu.insert(file_about_item,0); menubar_file.submenu = file_menu; + menubar.append(menubar_file); win.menu = menubar; - \ No newline at end of file + diff --git a/tests/manual_tests/notification/index.html b/tests/manual_tests/notification/index.html new file mode 100644 index 0000000000..35434a4cd6 --- /dev/null +++ b/tests/manual_tests/notification/index.html @@ -0,0 +1,68 @@ + + + Notification + + +

    + + + diff --git a/tests/manual_tests/notification/package.json b/tests/manual_tests/notification/package.json new file mode 100644 index 0000000000..7efd8aac98 --- /dev/null +++ b/tests/manual_tests/notification/package.json @@ -0,0 +1,5 @@ +{ + "name": "nw-notification-test", + "main": "index.html", + "app-id": "com.node.webkit.notification.test" +} diff --git a/tests/manual_tests/visible_on_all_workspaces/index.html b/tests/manual_tests/visible_on_all_workspaces/index.html new file mode 100644 index 0000000000..8114b47d52 --- /dev/null +++ b/tests/manual_tests/visible_on_all_workspaces/index.html @@ -0,0 +1,65 @@ + + + Always on Visible Workspace Test - window #1 + + +

    +

    Window #1

    +

    This window is visible only on this workspace

    +
    + Visible on all workspaces +

    Makes the window visible on all workspaces simultaneously.

    +
    +
    + Only on this workspace +

    Makes the window visible only on this workspace.

    +
    +

    +
    + + + diff --git a/tests/manual_tests/visible_on_all_workspaces/index2.html b/tests/manual_tests/visible_on_all_workspaces/index2.html new file mode 100644 index 0000000000..e7ff6d3748 --- /dev/null +++ b/tests/manual_tests/visible_on_all_workspaces/index2.html @@ -0,0 +1,41 @@ + + + Always on Visible Workspace Test - window #2 + + +

    Window #2

    +

    This window is visible only on this workspace

    +
    + Visible on all workspaces +

    Makes the window visible on all workspaces simultaneously.

    +
    +
    + Only on this workspace +

    Makes the window visible only on this workspace.

    +
    +

    + + + diff --git a/tests/manual_tests/visible_on_all_workspaces/package.json b/tests/manual_tests/visible_on_all_workspaces/package.json new file mode 100644 index 0000000000..8c6eadaecd --- /dev/null +++ b/tests/manual_tests/visible_on_all_workspaces/package.json @@ -0,0 +1,8 @@ +{ + "name": "nw-always-on-visible-workspace-test", + "main": "index.html", + "window": { + "visible-on-all-workspaces": true + }, + "dependencies": {} +} diff --git a/tests/manual_tests/webview/index.html b/tests/manual_tests/webview/index.html new file mode 100755 index 0000000000..5820bb835e --- /dev/null +++ b/tests/manual_tests/webview/index.html @@ -0,0 +1,28 @@ + + + + Webview + + + +

    + + + + + + diff --git a/tests/manual_tests/webview/package.json b/tests/manual_tests/webview/package.json new file mode 100755 index 0000000000..61360bc487 --- /dev/null +++ b/tests/manual_tests/webview/package.json @@ -0,0 +1,4 @@ +{ + "name": "WebView", + "main": "index.html" +} diff --git a/tests/mocha/Readme.md b/tests/mocha/Readme.md deleted file mode 100644 index 9340cacfda..0000000000 --- a/tests/mocha/Readme.md +++ /dev/null @@ -1,172 +0,0 @@ - [![Build Status](https://secure.travis-ci.org/visionmedia/mocha.png)](http://travis-ci.org/visionmedia/mocha) - - [![Mocha test framework](http://f.cl.ly/items/3l1k0n2A1U3M1I1L210p/Screen%20Shot%202012-02-24%20at%202.21.43%20PM.png)](http://visionmedia.github.io/mocha) - - Mocha is a simple, flexible, fun JavaScript test framework for node.js and the browser. For more information view the [documentation](http://visionmedia.github.io/mocha). - -## Contributors - -``` - - project : mocha - repo age : 2 years, 4 months ago - commits : 1314 - active : 372 days - files : 141 - authors : - 582 TJ Holowaychuk 44.3% - 389 Tj Holowaychuk 29.6% - 46 Travis Jeffery 3.5% - 31 Guillermo Rauch 2.4% - 13 Attila Domokos 1.0% - 10 John Firebaugh 0.8% - 8 Jo Liss 0.6% - 7 Nathan Rajlich 0.5% - 6 Mike Pennisi 0.5% - 6 James Carr 0.5% - 6 Brendan Nee 0.5% - 5 Aaron Heckmann 0.4% - 5 Ryunosuke SATO 0.4% - 4 hokaccha 0.3% - 4 Joshua Krall 0.3% - 4 Xavier Antoviaque 0.3% - 3 Jesse Dailey 0.2% - 3 Forbes Lindesay 0.2% - 3 Sindre Sorhus 0.2% - 3 Cory Thomas 0.2% - 3 Fredrik Enestad 0.2% - 3 Ben Lindsey 0.2% - 3 Tyson Tate 0.2% - 3 Mathieu Desvé 0.2% - 3 Valentin Agachi 0.2% - 3 Wil Moore III 0.2% - 3 Merrick Christensen 0.2% - 3 eiji.ienaga 0.2% - 3 fool2fish 0.2% - 3 Nathan Bowser 0.2% - 3 Paul Miller 0.2% - 2 Juzer Ali 0.2% - 2 Pete Hawkins 0.2% - 2 Jonas Westerlund 0.2% - 2 Arian Stolwijk 0.2% - 2 Quang Van 0.2% - 2 Glen Mailer 0.2% - 2 Justin DuJardin 0.2% - 2 FARKAS Máté 0.2% - 2 Raynos 0.2% - 2 Michael Riley 0.2% - 2 Michael Schoonmaker 0.2% - 2 Domenic Denicola 0.2% - 2 Simon Gaeremynck 0.2% - 2 Konstantin Käfer 0.2% - 2 domenic 0.2% - 2 Paul Armstrong 0.2% - 2 fcrisci 0.2% - 2 Alexander Early 0.2% - 2 Shawn Krisman 0.2% - 2 Brian Beck 0.2% - 2 Nathan Alderson 0.2% - 2 David Henderson 0.2% - 2 Timo Tijhof 0.2% - 2 Ian Storm Taylor 0.2% - 2 travis jeffery 0.2% - 1 Matt Smith 0.1% - 1 Matthew Shanley 0.1% - 1 Nathan Black 0.1% - 1 Phil Sung 0.1% - 1 R56 0.1% - 1 Refael Ackermann 0.1% - 1 Richard Dingwall 0.1% - 1 Romain Prieto 0.1% - 1 Roman Neuhauser 0.1% - 1 Roman Shtylman 0.1% - 1 Russ Bradberry 0.1% - 1 Russell Munson 0.1% - 1 Rustem Mustafin 0.1% - 1 Salehen Shovon Rahman 0.1% - 1 Sasha Koss 0.1% - 1 Seiya Konno 0.1% - 1 Simon Goumaz 0.1% - 1 Standa Opichal 0.1% - 1 Stephen Mathieson 0.1% - 1 Steve Mason 0.1% - 1 Tapiwa Kelvin 0.1% - 1 Teddy Zeenny 0.1% - 1 Tim Ehat 0.1% - 1 Vadim Nikitin 0.1% - 1 Victor Costan 0.1% - 1 Will Langstroth 0.1% - 1 Yanis Wang 0.1% - 1 Yuest Wang 0.1% - 1 abrkn 0.1% - 1 airportyh 0.1% - 1 badunk 0.1% - 1 fengmk2 0.1% - 1 grasGendarme 0.1% - 1 lodr 0.1% - 1 tgautier@yahoo.com 0.1% - 1 traleig1 0.1% - 1 vlad 0.1% - 1 yuitest 0.1% - 1 Adam Crabtree 0.1% - 1 Andreas Brekken 0.1% - 1 Andreas Lind Petersen 0.1% - 1 Andrew Nesbitt 0.1% - 1 Andrey Popp 0.1% - 1 Arnaud Brousseau 0.1% - 1 Atsuya Takagi 0.1% - 1 Austin Birch 0.1% - 1 Bjørge Næss 0.1% - 1 Brian Lalor 0.1% - 1 Brian M. Carlson 0.1% - 1 Brian Moore 0.1% - 1 Bryan Donovan 0.1% - 1 Casey Foster 0.1% - 1 ChrisWren 0.1% - 1 Corey Butler 0.1% - 1 Daniel Stockman 0.1% - 1 Dave McKenna 0.1% - 1 Di Wu 0.1% - 1 Dmitry Shirokov 0.1% - 1 Fedor Indutny 0.1% - 1 Florian Margaine 0.1% - 1 Frederico Silva 0.1% - 1 Fredrik Lindin 0.1% - 1 Gareth Murphy 0.1% - 1 Gavin Mogan 0.1% - 1 Glen Huang 0.1% - 1 Greg Perkins 0.1% - 1 Harry Brundage 0.1% - 1 Herman Junge 0.1% - 1 Ian Young 0.1% - 1 Ivan 0.1% - 1 JP Bochi 0.1% - 1 Jaakko Salonen 0.1% - 1 Jakub Nešetřil 0.1% - 1 James Bowes 0.1% - 1 James Lal 0.1% - 1 Jason Barry 0.1% - 1 Javier Aranda 0.1% - 1 Jeff Kunkle 0.1% - 1 Jeremy Martin 0.1% - 1 Jimmy Cuadra 0.1% - 1 Jonathan Creamer 0.1% - 1 Jussi Virtanen 0.1% - 1 Katie Gengler 0.1% - 1 Kazuhito Hokamura 0.1% - 1 Kirill Korolyov 0.1% - 1 Koen Punt 0.1% - 1 Laszlo Bacsi 0.1% - 1 Liam Newman 0.1% - 1 László Bácsi 0.1% - 1 Maciej Małecki 0.1% - 1 Mal Graty 0.1% - 1 Marc Kuo 0.1% - 1 Matt Robenolt 0.1% -``` - -## Links - - - [Google Group](http://groups.google.com/group/mochajs) - - [Wiki](https://github.com/visionmedia/mocha/wiki) - - Mocha [Extensions and reporters](https://github.com/visionmedia/mocha/wiki) diff --git a/tests/mocha/bin/_mocha b/tests/mocha/bin/_mocha deleted file mode 100755 index bea6df85f7..0000000000 --- a/tests/mocha/bin/_mocha +++ /dev/null @@ -1,467 +0,0 @@ -#!/usr/bin/env node - -/** - * Module dependencies. - */ - -var program = require('commander') - , sprintf = require('util').format - , path = require('path') - , fs = require('fs') - , glob = require('glob') - , resolve = path.resolve - , exists = fs.existsSync || path.existsSync - , Mocha = require('../') - , utils = Mocha.utils - , interfaces = Mocha.interfaces - , join = path.join - , basename = path.basename - , cwd = process.cwd() - , mocha = new Mocha; - -/** - * Save timer references to avoid Sinon interfering (see GH-237). - */ - -var Date = global.Date - , setTimeout = global.setTimeout - , setInterval = global.setInterval - , clearTimeout = global.clearTimeout - , clearInterval = global.clearInterval; - -/** - * Files. - */ - -var files = []; - -/** - * Globals. - */ - -var globals = []; - -/** - * Requires. - */ - -var requires = []; - -/** - * Images. - */ - -var images = { - fail: __dirname + '/../images/error.png' - , pass: __dirname + '/../images/ok.png' -}; - -// options - -program - .version(JSON.parse(fs.readFileSync(__dirname + '/../package.json', 'utf8')).version) - .usage('[debug] [options] [files]') - .option('-r, --require ', 'require the given module') - .option('-R, --reporter ', 'specify the reporter to use', 'dot') - .option('-u, --ui ', 'specify user-interface (bdd|tdd|exports)', 'bdd') - .option('-g, --grep ', 'only run tests matching ') - .option('-i, --invert', 'inverts --grep matches') - .option('-t, --timeout ', 'set test-case timeout in milliseconds [2000]') - .option('-s, --slow ', '"slow" test threshold in milliseconds [75]') - .option('-w, --watch', 'watch files for changes') - .option('-c, --colors', 'force enabling of colors') - .option('-C, --no-colors', 'force disabling of colors') - .option('-G, --growl', 'enable growl notification support') - .option('-d, --debug', "enable node's debugger, synonym for node --debug") - .option('-b, --bail', "bail after first test failure") - .option('-A, --async-only', "force all tests to take a callback (async)") - .option('-S, --sort', "sort test files") - .option('--recursive', 'include sub directories') - .option('--debug-brk', "enable node's debugger breaking on the first line") - .option('--globals ', 'allow the given comma-delimited global [names]', list, []) - .option('--check-leaks', 'check for global variable leaks') - .option('--interfaces', 'display available interfaces') - .option('--reporters', 'display available reporters') - .option('--compilers :,...', 'use the given module(s) to compile files', list, []) - .option('--inline-diffs', 'display actual/expected differences inline within each string') - .option('--no-exit', 'require a clean shutdown of the event loop: mocha will not call process.exit') - -program.name = 'mocha'; - -// init command - -program - .command('init ') - .description('initialize a client-side mocha setup at ') - .action(function(path){ - var mkdir = require('mkdirp'); - mkdir.sync(path); - var css = fs.readFileSync(join(__dirname, '..', 'mocha.css')); - var js = fs.readFileSync(join(__dirname, '..', 'mocha.js')); - var tmpl = fs.readFileSync(join(__dirname, '..', 'lib/template.html')); - fs.writeFileSync(join(path, 'mocha.css'), css); - fs.writeFileSync(join(path, 'mocha.js'), js); - fs.writeFileSync(join(path, 'tests.js'), ''); - fs.writeFileSync(join(path, 'index.html'), tmpl); - process.exit(0); - }); - -// --globals - -program.on('globals', function(val){ - globals = globals.concat(list(val)); -}); - -// --reporters - -program.on('reporters', function(){ - console.log(); - console.log(' dot - dot matrix'); - console.log(' doc - html documentation'); - console.log(' spec - hierarchical spec list'); - console.log(' json - single json object'); - console.log(' progress - progress bar'); - console.log(' list - spec-style listing'); - console.log(' tap - test-anything-protocol'); - console.log(' landing - unicode landing strip'); - console.log(' xunit - xunit reporter'); - console.log(' html-cov - HTML test coverage'); - console.log(' json-cov - JSON test coverage'); - console.log(' min - minimal reporter (great with --watch)'); - console.log(' json-stream - newline delimited json events'); - console.log(' markdown - markdown documentation (github flavour)'); - console.log(' nyan - nyan cat!'); - console.log(); - process.exit(); -}); - -// --interfaces - -program.on('interfaces', function(){ - console.log(''); - console.log(' bdd'); - console.log(' tdd'); - console.log(' qunit'); - console.log(' exports'); - console.log(''); - process.exit(); -}); - -// -r, --require - -module.paths.push(cwd, join(cwd, 'node_modules')); - -program.on('require', function(mod){ - var abs = exists(mod) || exists(mod + '.js'); - if (abs) mod = resolve(mod); - requires.push(mod); -}); - -// mocha.opts support - -try { - var opts = fs.readFileSync('test/mocha.opts', 'utf8') - .trim() - .split(/\s+/); - - process.argv = process.argv - .slice(0, 2) - .concat(opts.concat(process.argv.slice(2))); -} catch (err) { - // ignore -} - -// parse args - -program.parse(process.argv); - -// infinite stack traces - -Error.stackTraceLimit = Infinity; // TODO: config - -// reporter - -mocha.reporter(program.reporter); - -// interface - -mocha.ui(program.ui); - -// load reporter - -try { - Reporter = require('../lib/reporters/' + program.reporter); -} catch (err) { - try { - Reporter = require(program.reporter); - } catch (err) { - throw new Error('reporter "' + program.reporter + '" does not exist'); - } -} - -// --no-colors - -if (!program.colors) mocha.useColors(false); - -// --colors - -if (~process.argv.indexOf('--colors') || - ~process.argv.indexOf('-c')) { - mocha.useColors(true); -} - -// --inline-diffs - -if (program.inlineDiffs) mocha.useInlineDiffs(true); - -// --slow - -if (program.slow) mocha.suite.slow(program.slow); - -// --timeout - -if (program.timeout) mocha.suite.timeout(program.timeout); - -// --bail - -mocha.suite.bail(program.bail); - -// --grep - -if (program.grep) mocha.grep(new RegExp(program.grep)); - -// --invert - -if (program.invert) mocha.invert(); - -// --check-leaks - -if (program.checkLeaks) mocha.checkLeaks(); - -// --growl - -if (program.growl) mocha.growl(); - -// --async-only - -if (program.asyncOnly) mocha.asyncOnly(); - -// --globals - -mocha.globals(globals); - -// custom compiler support - -var extensions = ['js']; -program.compilers.forEach(function(c) { - var compiler = c.split(':') - , ext = compiler[0] - , mod = compiler[1]; - - if (mod[0] == '.') mod = join(process.cwd(), mod); - require(mod); - extensions.push(ext); -}); - -var re = new RegExp('\\.(' + extensions.join('|') + ')$'); - -// requires - -requires.forEach(function(mod) { - require(mod); -}); - -// files - -var files = [] - , args = program.args; - -// default files to test/*.{js,coffee} - -if (!args.length) args.push('test'); - -args.forEach(function(arg){ - files = files.concat(lookupFiles(arg, program.recursive)); -}); - -// resolve - -files = files.map(function(path){ - return resolve(path); -}); - -if (program.sort) { - files.sort(); -} - -// --watch - -var runner; -if (program.watch) { - console.log(); - hideCursor(); - process.on('SIGINT', function(){ - showCursor(); - console.log('\n'); - process.exit(); - }); - - var watchFiles = utils.files(cwd); - var runAgain = false; - - function loadAndRun() { - try { - mocha.files = files; - runAgain = false; - runner = mocha.run(function(){ - runner = null; - if (runAgain) { - rerun(); - } - }); - } catch(e) { - console.log(e.stack); - } - } - - function purge() { - watchFiles.forEach(function(file){ - delete require.cache[file]; - }); - } - - loadAndRun(); - - function rerun() { - purge(); - stop() - mocha.suite = mocha.suite.clone(); - mocha.suite.ctx = new Mocha.Context; - mocha.ui(program.ui); - loadAndRun(); - } - - utils.watch(watchFiles, function(){ - runAgain = true; - if (runner) { - runner.abort(); - } else { - rerun(); - } - }); - - return; -} - -// load - -mocha.files = files; -runner = mocha.run(program.exit ? process.exit : exitLater); - -function exitLater(code) { - process.on('exit', function() { process.exit(code) }) -} - -process.on('SIGINT', function() { runner.abort(); }) - -// enable growl notifications - -function growl(runner, reporter) { - var notify = require('growl'); - - runner.on('end', function(){ - var stats = reporter.stats; - if (stats.failures) { - var msg = stats.failures + ' of ' + runner.total + ' tests failed'; - notify(msg, { name: 'mocha', title: 'Failed', image: images.fail }); - } else { - notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', { - name: 'mocha' - , title: 'Passed' - , image: images.pass - }); - } - }); -} - -/** - * Parse list. - */ - -function list(str) { - return str.split(/ *, */); -} - -/** - * Hide the cursor. - */ - -function hideCursor(){ - process.stdout.write('\u001b[?25l'); -}; - -/** - * Show the cursor. - */ - -function showCursor(){ - process.stdout.write('\u001b[?25h'); -}; - -/** - * Stop play()ing. - */ - -function stop() { - process.stdout.write('\u001b[2K'); - clearInterval(play.timer); -} - -/** - * Lookup file names at the given `path`. - */ - -function lookupFiles(path, recursive) { - var files = []; - - if (!exists(path)) { - if (exists(path + '.js')) { - path += '.js' - } else { - files = glob.sync(path); - if (!files.length) throw new Error("cannot resolve path (or pattern) '" + path + "'"); - return files; - } - } - - var stat = fs.statSync(path); - if (stat.isFile()) return path; - - fs.readdirSync(path).forEach(function(file){ - file = join(path, file); - var stat = fs.statSync(file); - if (stat.isDirectory()) { - if (recursive) files = files.concat(lookupFiles(file, recursive)); - return - } - if (!stat.isFile() || !re.test(file) || basename(file)[0] == '.') return; - files.push(file); - }); - - return files; -} - -/** - * Play the given array of strings. - */ - -function play(arr, interval) { - var len = arr.length - , interval = interval || 100 - , i = 0; - - play.timer = setInterval(function(){ - var str = arr[i++ % len]; - process.stdout.write('\u001b[0G' + str); - }, interval); -} diff --git a/tests/mocha/bin/mocha b/tests/mocha/bin/mocha deleted file mode 100755 index 742d60779e..0000000000 --- a/tests/mocha/bin/mocha +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env node - -/** - * This tiny wrapper file checks for known node flags and appends them - * when found, before invoking the "real" _mocha(1) executable. - */ - -var spawn = require('child_process').spawn - , args = [ __dirname + '/_mocha' ]; - -process.argv.slice(2).forEach(function(arg){ - var flag = arg.split('=')[0]; - - switch (flag) { - case '-d': - args.unshift('--debug'); - break; - case 'debug': - case '--debug': - case '--debug-brk': - args.unshift(arg); - break; - case '-gc': - case '--expose-gc': - args.unshift('--expose-gc'); - break; - case '--gc-global': - case '--harmony': - case '--harmony-proxies': - case '--harmony-collections': - case '--harmony-generators': - case '--prof': - args.unshift(arg); - break; - default: - if (0 == arg.indexOf('--trace')) args.unshift(arg); - else args.push(arg); - break; - } -}); - -var proc = spawn(process.argv[0], args, { customFds: [0,1,2] }); -proc.on('exit', function (code, signal) { - process.on('exit', function(){ - if (signal) { - process.kill(process.pid, signal); - } else { - process.exit(code); - } - }); -}); diff --git a/tests/mocha/images/error.png b/tests/mocha/images/error.png deleted file mode 100644 index a07a1ba5ef..0000000000 Binary files a/tests/mocha/images/error.png and /dev/null differ diff --git a/tests/mocha/images/ok.png b/tests/mocha/images/ok.png deleted file mode 100644 index b3623a5994..0000000000 Binary files a/tests/mocha/images/ok.png and /dev/null differ diff --git a/tests/mocha/index.js b/tests/mocha/index.js deleted file mode 100644 index 507566fac2..0000000000 --- a/tests/mocha/index.js +++ /dev/null @@ -1,4 +0,0 @@ - -module.exports = process.env.COV - ? require('./lib-cov/mocha') - : require('./lib/mocha'); \ No newline at end of file diff --git a/tests/mocha/lib/browser/debug.js b/tests/mocha/lib/browser/debug.js deleted file mode 100644 index 03cf59234c..0000000000 --- a/tests/mocha/lib/browser/debug.js +++ /dev/null @@ -1,5 +0,0 @@ - -module.exports = function(type){ - return function(){ - } -}; diff --git a/tests/mocha/lib/browser/diff.js b/tests/mocha/lib/browser/diff.js deleted file mode 100644 index a34c22a044..0000000000 --- a/tests/mocha/lib/browser/diff.js +++ /dev/null @@ -1,354 +0,0 @@ -/* See LICENSE file for terms of use */ - -/* - * Text diff implementation. - * - * This library supports the following APIS: - * JsDiff.diffChars: Character by character diff - * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace - * JsDiff.diffLines: Line based diff - * - * JsDiff.diffCss: Diff targeted at CSS content - * - * These methods are based on the implementation proposed in - * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986). - * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 - */ -var JsDiff = (function() { - /*jshint maxparams: 5*/ - function clonePath(path) { - return { newPos: path.newPos, components: path.components.slice(0) }; - } - function removeEmpty(array) { - var ret = []; - for (var i = 0; i < array.length; i++) { - if (array[i]) { - ret.push(array[i]); - } - } - return ret; - } - function escapeHTML(s) { - var n = s; - n = n.replace(/&/g, '&'); - n = n.replace(//g, '>'); - n = n.replace(/"/g, '"'); - - return n; - } - - var Diff = function(ignoreWhitespace) { - this.ignoreWhitespace = ignoreWhitespace; - }; - Diff.prototype = { - diff: function(oldString, newString) { - // Handle the identity case (this is due to unrolling editLength == 0 - if (newString === oldString) { - return [{ value: newString }]; - } - if (!newString) { - return [{ value: oldString, removed: true }]; - } - if (!oldString) { - return [{ value: newString, added: true }]; - } - - newString = this.tokenize(newString); - oldString = this.tokenize(oldString); - - var newLen = newString.length, oldLen = oldString.length; - var maxEditLength = newLen + oldLen; - var bestPath = [{ newPos: -1, components: [] }]; - - // Seed editLength = 0 - var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); - if (bestPath[0].newPos+1 >= newLen && oldPos+1 >= oldLen) { - return bestPath[0].components; - } - - for (var editLength = 1; editLength <= maxEditLength; editLength++) { - for (var diagonalPath = -1*editLength; diagonalPath <= editLength; diagonalPath+=2) { - var basePath; - var addPath = bestPath[diagonalPath-1], - removePath = bestPath[diagonalPath+1]; - oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; - if (addPath) { - // No one else is going to attempt to use this value, clear it - bestPath[diagonalPath-1] = undefined; - } - - var canAdd = addPath && addPath.newPos+1 < newLen; - var canRemove = removePath && 0 <= oldPos && oldPos < oldLen; - if (!canAdd && !canRemove) { - bestPath[diagonalPath] = undefined; - continue; - } - - // Select the diagonal that we want to branch from. We select the prior - // path whose position in the new string is the farthest from the origin - // and does not pass the bounds of the diff graph - if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) { - basePath = clonePath(removePath); - this.pushComponent(basePath.components, oldString[oldPos], undefined, true); - } else { - basePath = clonePath(addPath); - basePath.newPos++; - this.pushComponent(basePath.components, newString[basePath.newPos], true, undefined); - } - - var oldPos = this.extractCommon(basePath, newString, oldString, diagonalPath); - - if (basePath.newPos+1 >= newLen && oldPos+1 >= oldLen) { - return basePath.components; - } else { - bestPath[diagonalPath] = basePath; - } - } - } - }, - - pushComponent: function(components, value, added, removed) { - var last = components[components.length-1]; - if (last && last.added === added && last.removed === removed) { - // We need to clone here as the component clone operation is just - // as shallow array clone - components[components.length-1] = - {value: this.join(last.value, value), added: added, removed: removed }; - } else { - components.push({value: value, added: added, removed: removed }); - } - }, - extractCommon: function(basePath, newString, oldString, diagonalPath) { - var newLen = newString.length, - oldLen = oldString.length, - newPos = basePath.newPos, - oldPos = newPos - diagonalPath; - while (newPos+1 < newLen && oldPos+1 < oldLen && this.equals(newString[newPos+1], oldString[oldPos+1])) { - newPos++; - oldPos++; - - this.pushComponent(basePath.components, newString[newPos], undefined, undefined); - } - basePath.newPos = newPos; - return oldPos; - }, - - equals: function(left, right) { - var reWhitespace = /\S/; - if (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)) { - return true; - } else { - return left === right; - } - }, - join: function(left, right) { - return left + right; - }, - tokenize: function(value) { - return value; - } - }; - - var CharDiff = new Diff(); - - var WordDiff = new Diff(true); - var WordWithSpaceDiff = new Diff(); - WordDiff.tokenize = WordWithSpaceDiff.tokenize = function(value) { - return removeEmpty(value.split(/(\s+|\b)/)); - }; - - var CssDiff = new Diff(true); - CssDiff.tokenize = function(value) { - return removeEmpty(value.split(/([{}:;,]|\s+)/)); - }; - - var LineDiff = new Diff(); - LineDiff.tokenize = function(value) { - return value.split(/^/m); - }; - - return { - Diff: Diff, - - diffChars: function(oldStr, newStr) { return CharDiff.diff(oldStr, newStr); }, - diffWords: function(oldStr, newStr) { return WordDiff.diff(oldStr, newStr); }, - diffWordsWithSpace: function(oldStr, newStr) { return WordWithSpaceDiff.diff(oldStr, newStr); }, - diffLines: function(oldStr, newStr) { return LineDiff.diff(oldStr, newStr); }, - - diffCss: function(oldStr, newStr) { return CssDiff.diff(oldStr, newStr); }, - - createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) { - var ret = []; - - ret.push('Index: ' + fileName); - ret.push('==================================================================='); - ret.push('--- ' + fileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader)); - ret.push('+++ ' + fileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader)); - - var diff = LineDiff.diff(oldStr, newStr); - if (!diff[diff.length-1].value) { - diff.pop(); // Remove trailing newline add - } - diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier - - function contextLines(lines) { - return lines.map(function(entry) { return ' ' + entry; }); - } - function eofNL(curRange, i, current) { - var last = diff[diff.length-2], - isLast = i === diff.length-2, - isLastOfType = i === diff.length-3 && (current.added !== last.added || current.removed !== last.removed); - - // Figure out if this is the last line for the given file and missing NL - if (!/\n$/.test(current.value) && (isLast || isLastOfType)) { - curRange.push('\\ No newline at end of file'); - } - } - - var oldRangeStart = 0, newRangeStart = 0, curRange = [], - oldLine = 1, newLine = 1; - for (var i = 0; i < diff.length; i++) { - var current = diff[i], - lines = current.lines || current.value.replace(/\n$/, '').split('\n'); - current.lines = lines; - - if (current.added || current.removed) { - if (!oldRangeStart) { - var prev = diff[i-1]; - oldRangeStart = oldLine; - newRangeStart = newLine; - - if (prev) { - curRange = contextLines(prev.lines.slice(-4)); - oldRangeStart -= curRange.length; - newRangeStart -= curRange.length; - } - } - curRange.push.apply(curRange, lines.map(function(entry) { return (current.added?'+':'-') + entry; })); - eofNL(curRange, i, current); - - if (current.added) { - newLine += lines.length; - } else { - oldLine += lines.length; - } - } else { - if (oldRangeStart) { - // Close out any changes that have been output (or join overlapping) - if (lines.length <= 8 && i < diff.length-2) { - // Overlapping - curRange.push.apply(curRange, contextLines(lines)); - } else { - // end the range and output - var contextSize = Math.min(lines.length, 4); - ret.push( - '@@ -' + oldRangeStart + ',' + (oldLine-oldRangeStart+contextSize) - + ' +' + newRangeStart + ',' + (newLine-newRangeStart+contextSize) - + ' @@'); - ret.push.apply(ret, curRange); - ret.push.apply(ret, contextLines(lines.slice(0, contextSize))); - if (lines.length <= 4) { - eofNL(ret, i, current); - } - - oldRangeStart = 0; newRangeStart = 0; curRange = []; - } - } - oldLine += lines.length; - newLine += lines.length; - } - } - - return ret.join('\n') + '\n'; - }, - - applyPatch: function(oldStr, uniDiff) { - var diffstr = uniDiff.split('\n'); - var diff = []; - var remEOFNL = false, - addEOFNL = false; - - for (var i = (diffstr[0][0]==='I'?4:0); i < diffstr.length; i++) { - if(diffstr[i][0] === '@') { - var meh = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); - diff.unshift({ - start:meh[3], - oldlength:meh[2], - oldlines:[], - newlength:meh[4], - newlines:[] - }); - } else if(diffstr[i][0] === '+') { - diff[0].newlines.push(diffstr[i].substr(1)); - } else if(diffstr[i][0] === '-') { - diff[0].oldlines.push(diffstr[i].substr(1)); - } else if(diffstr[i][0] === ' ') { - diff[0].newlines.push(diffstr[i].substr(1)); - diff[0].oldlines.push(diffstr[i].substr(1)); - } else if(diffstr[i][0] === '\\') { - if (diffstr[i-1][0] === '+') { - remEOFNL = true; - } else if(diffstr[i-1][0] === '-') { - addEOFNL = true; - } - } - } - - var str = oldStr.split('\n'); - for (var i = diff.length - 1; i >= 0; i--) { - var d = diff[i]; - for (var j = 0; j < d.oldlength; j++) { - if(str[d.start-1+j] !== d.oldlines[j]) { - return false; - } - } - Array.prototype.splice.apply(str,[d.start-1,+d.oldlength].concat(d.newlines)); - } - - if (remEOFNL) { - while (!str[str.length-1]) { - str.pop(); - } - } else if (addEOFNL) { - str.push(''); - } - return str.join('\n'); - }, - - convertChangesToXML: function(changes){ - var ret = []; - for ( var i = 0; i < changes.length; i++) { - var change = changes[i]; - if (change.added) { - ret.push(''); - } else if (change.removed) { - ret.push(''); - } - - ret.push(escapeHTML(change.value)); - - if (change.added) { - ret.push(''); - } else if (change.removed) { - ret.push(''); - } - } - return ret.join(''); - }, - - // See: http://code.google.com/p/google-diff-match-patch/wiki/API - convertChangesToDMP: function(changes){ - var ret = [], change; - for ( var i = 0; i < changes.length; i++) { - change = changes[i]; - ret.push([(change.added ? 1 : change.removed ? -1 : 0), change.value]); - } - return ret; - } - }; -})(); - -if (typeof module !== 'undefined') { - module.exports = JsDiff; -} diff --git a/tests/mocha/lib/browser/events.js b/tests/mocha/lib/browser/events.js deleted file mode 100644 index cfbd072028..0000000000 --- a/tests/mocha/lib/browser/events.js +++ /dev/null @@ -1,178 +0,0 @@ - -/** - * Module exports. - */ - -exports.EventEmitter = EventEmitter; - -/** - * Check if `obj` is an array. - */ - -function isArray(obj) { - return '[object Array]' == {}.toString.call(obj); -} - -/** - * Event emitter constructor. - * - * @api public - */ - -function EventEmitter(){}; - -/** - * Adds a listener. - * - * @api public - */ - -EventEmitter.prototype.on = function (name, fn) { - if (!this.$events) { - this.$events = {}; - } - - if (!this.$events[name]) { - this.$events[name] = fn; - } else if (isArray(this.$events[name])) { - this.$events[name].push(fn); - } else { - this.$events[name] = [this.$events[name], fn]; - } - - return this; -}; - -EventEmitter.prototype.addListener = EventEmitter.prototype.on; - -/** - * Adds a volatile listener. - * - * @api public - */ - -EventEmitter.prototype.once = function (name, fn) { - var self = this; - - function on () { - self.removeListener(name, on); - fn.apply(this, arguments); - }; - - on.listener = fn; - this.on(name, on); - - return this; -}; - -/** - * Removes a listener. - * - * @api public - */ - -EventEmitter.prototype.removeListener = function (name, fn) { - if (this.$events && this.$events[name]) { - var list = this.$events[name]; - - if (isArray(list)) { - var pos = -1; - - for (var i = 0, l = list.length; i < l; i++) { - if (list[i] === fn || (list[i].listener && list[i].listener === fn)) { - pos = i; - break; - } - } - - if (pos < 0) { - return this; - } - - list.splice(pos, 1); - - if (!list.length) { - delete this.$events[name]; - } - } else if (list === fn || (list.listener && list.listener === fn)) { - delete this.$events[name]; - } - } - - return this; -}; - -/** - * Removes all listeners for an event. - * - * @api public - */ - -EventEmitter.prototype.removeAllListeners = function (name) { - if (name === undefined) { - this.$events = {}; - return this; - } - - if (this.$events && this.$events[name]) { - this.$events[name] = null; - } - - return this; -}; - -/** - * Gets all listeners for a certain event. - * - * @api public - */ - -EventEmitter.prototype.listeners = function (name) { - if (!this.$events) { - this.$events = {}; - } - - if (!this.$events[name]) { - this.$events[name] = []; - } - - if (!isArray(this.$events[name])) { - this.$events[name] = [this.$events[name]]; - } - - return this.$events[name]; -}; - -/** - * Emits an event. - * - * @api public - */ - -EventEmitter.prototype.emit = function (name) { - if (!this.$events) { - return false; - } - - var handler = this.$events[name]; - - if (!handler) { - return false; - } - - var args = [].slice.call(arguments, 1); - - if ('function' == typeof handler) { - handler.apply(this, args); - } else if (isArray(handler)) { - var listeners = handler.slice(); - - for (var i = 0, l = listeners.length; i < l; i++) { - listeners[i].apply(this, args); - } - } else { - return false; - } - - return true; -}; \ No newline at end of file diff --git a/tests/mocha/lib/browser/progress.js b/tests/mocha/lib/browser/progress.js deleted file mode 100644 index 90526f72b8..0000000000 --- a/tests/mocha/lib/browser/progress.js +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Expose `Progress`. - */ - -module.exports = Progress; - -/** - * Initialize a new `Progress` indicator. - */ - -function Progress() { - this.percent = 0; - this.size(0); - this.fontSize(11); - this.font('helvetica, arial, sans-serif'); -} - -/** - * Set progress size to `n`. - * - * @param {Number} n - * @return {Progress} for chaining - * @api public - */ - -Progress.prototype.size = function(n){ - this._size = n; - return this; -}; - -/** - * Set text to `str`. - * - * @param {String} str - * @return {Progress} for chaining - * @api public - */ - -Progress.prototype.text = function(str){ - this._text = str; - return this; -}; - -/** - * Set font size to `n`. - * - * @param {Number} n - * @return {Progress} for chaining - * @api public - */ - -Progress.prototype.fontSize = function(n){ - this._fontSize = n; - return this; -}; - -/** - * Set font `family`. - * - * @param {String} family - * @return {Progress} for chaining - */ - -Progress.prototype.font = function(family){ - this._font = family; - return this; -}; - -/** - * Update percentage to `n`. - * - * @param {Number} n - * @return {Progress} for chaining - */ - -Progress.prototype.update = function(n){ - this.percent = n; - return this; -}; - -/** - * Draw on `ctx`. - * - * @param {CanvasRenderingContext2d} ctx - * @return {Progress} for chaining - */ - -Progress.prototype.draw = function(ctx){ - try { - var percent = Math.min(this.percent, 100) - , size = this._size - , half = size / 2 - , x = half - , y = half - , rad = half - 1 - , fontSize = this._fontSize; - - ctx.font = fontSize + 'px ' + this._font; - - var angle = Math.PI * 2 * (percent / 100); - ctx.clearRect(0, 0, size, size); - - // outer circle - ctx.strokeStyle = '#9f9f9f'; - ctx.beginPath(); - ctx.arc(x, y, rad, 0, angle, false); - ctx.stroke(); - - // inner circle - ctx.strokeStyle = '#eee'; - ctx.beginPath(); - ctx.arc(x, y, rad - 1, 0, angle, true); - ctx.stroke(); - - // text - var text = this._text || (percent | 0) + '%' - , w = ctx.measureText(text).width; - - ctx.fillText( - text - , x - w / 2 + 1 - , y + fontSize / 2 - 1); - } catch (ex) {} //don't fail if we can't render progress - return this; -}; diff --git a/tests/mocha/lib/browser/tty.js b/tests/mocha/lib/browser/tty.js deleted file mode 100644 index 6f5f079a15..0000000000 --- a/tests/mocha/lib/browser/tty.js +++ /dev/null @@ -1,13 +0,0 @@ - -exports.isatty = function(){ - return true; -}; - -exports.getWindowSize = function(){ - if ('innerHeight' in global) { - return [global.innerHeight, global.innerWidth]; - } else { - // In a Web Worker, the DOM Window is not available. - return [640, 480]; - } -}; diff --git a/tests/mocha/lib/context.js b/tests/mocha/lib/context.js deleted file mode 100644 index 6d6422a1ff..0000000000 --- a/tests/mocha/lib/context.js +++ /dev/null @@ -1,69 +0,0 @@ - -/** - * Expose `Context`. - */ - -module.exports = Context; - -/** - * Initialize a new `Context`. - * - * @api private - */ - -function Context(){} - -/** - * Set or get the context `Runnable` to `runnable`. - * - * @param {Runnable} runnable - * @return {Context} - * @api private - */ - -Context.prototype.runnable = function(runnable){ - if (0 == arguments.length) return this._runnable; - this.test = this._runnable = runnable; - return this; -}; - -/** - * Set test timeout `ms`. - * - * @param {Number} ms - * @return {Context} self - * @api private - */ - -Context.prototype.timeout = function(ms){ - this.runnable().timeout(ms); - return this; -}; - -/** - * Set test slowness threshold `ms`. - * - * @param {Number} ms - * @return {Context} self - * @api private - */ - -Context.prototype.slow = function(ms){ - this.runnable().slow(ms); - return this; -}; - -/** - * Inspect the context void of `._runnable`. - * - * @return {String} - * @api private - */ - -Context.prototype.inspect = function(){ - return JSON.stringify(this, function(key, val){ - if ('_runnable' == key) return; - if ('test' == key) return; - return val; - }, 2); -}; diff --git a/tests/mocha/lib/hook.js b/tests/mocha/lib/hook.js deleted file mode 100644 index 814e7b6242..0000000000 --- a/tests/mocha/lib/hook.js +++ /dev/null @@ -1,49 +0,0 @@ - -/** - * Module dependencies. - */ - -var Runnable = require('./runnable'); - -/** - * Expose `Hook`. - */ - -module.exports = Hook; - -/** - * Initialize a new `Hook` with the given `title` and callback `fn`. - * - * @param {String} title - * @param {Function} fn - * @api private - */ - -function Hook(title, fn) { - Runnable.call(this, title, fn); - this.type = 'hook'; -} - -/** - * Inherit from `Runnable.prototype`. - */ - -Hook.prototype.__proto__ = Runnable.prototype; - -/** - * Get or set the test `err`. - * - * @param {Error} err - * @return {Error} - * @api public - */ - -Hook.prototype.error = function(err){ - if (0 == arguments.length) { - var err = this._error; - this._error = null; - return err; - } - - this._error = err; -}; diff --git a/tests/mocha/lib/interfaces/bdd.js b/tests/mocha/lib/interfaces/bdd.js deleted file mode 100644 index e1581dabe1..0000000000 --- a/tests/mocha/lib/interfaces/bdd.js +++ /dev/null @@ -1,137 +0,0 @@ - -/** - * Module dependencies. - */ - -var Suite = require('../suite') - , Test = require('../test') - , utils = require('../utils'); - -/** - * BDD-style interface: - * - * describe('Array', function(){ - * describe('#indexOf()', function(){ - * it('should return -1 when not present', function(){ - * - * }); - * - * it('should return the index when present', function(){ - * - * }); - * }); - * }); - * - */ - -module.exports = function(suite){ - var suites = [suite]; - - suite.on('pre-require', function(context, file, mocha){ - - /** - * Execute before running tests. - */ - - context.before = function(name, fn){ - suites[0].beforeAll(name, fn); - }; - - /** - * Execute after running tests. - */ - - context.after = function(name, fn){ - suites[0].afterAll(name, fn); - }; - - /** - * Execute before each test case. - */ - - context.beforeEach = function(name, fn){ - suites[0].beforeEach(name, fn); - }; - - /** - * Execute after each test case. - */ - - context.afterEach = function(name, fn){ - suites[0].afterEach(name, fn); - }; - - /** - * Describe a "suite" with the given `title` - * and callback `fn` containing nested suites - * and/or tests. - */ - - context.describe = context.context = function(title, fn){ - var suite = Suite.create(suites[0], title); - suites.unshift(suite); - fn.call(suite); - suites.shift(); - return suite; - }; - - /** - * Pending describe. - */ - - context.xdescribe = - context.xcontext = - context.describe.skip = function(title, fn){ - var suite = Suite.create(suites[0], title); - suite.pending = true; - suites.unshift(suite); - fn.call(suite); - suites.shift(); - }; - - /** - * Exclusive suite. - */ - - context.describe.only = function(title, fn){ - var suite = context.describe(title, fn); - mocha.grep(suite.fullTitle()); - return suite; - }; - - /** - * Describe a specification or test-case - * with the given `title` and callback `fn` - * acting as a thunk. - */ - - context.it = context.specify = function(title, fn){ - var suite = suites[0]; - if (suite.pending) var fn = null; - var test = new Test(title, fn); - suite.addTest(test); - return test; - }; - - /** - * Exclusive test-case. - */ - - context.it.only = function(title, fn){ - var test = context.it(title, fn); - var reString = '^' + utils.escapeRegexp(test.fullTitle()) + '$'; - mocha.grep(new RegExp(reString)); - return test; - }; - - /** - * Pending test case. - */ - - context.xit = - context.xspecify = - context.it.skip = function(title){ - context.it(title); - }; - }); -}; diff --git a/tests/mocha/lib/interfaces/exports.js b/tests/mocha/lib/interfaces/exports.js deleted file mode 100644 index 6b229c0ff5..0000000000 --- a/tests/mocha/lib/interfaces/exports.js +++ /dev/null @@ -1,60 +0,0 @@ - -/** - * Module dependencies. - */ - -var Suite = require('../suite') - , Test = require('../test'); - -/** - * TDD-style interface: - * - * exports.Array = { - * '#indexOf()': { - * 'should return -1 when the value is not present': function(){ - * - * }, - * - * 'should return the correct index when the value is present': function(){ - * - * } - * } - * }; - * - */ - -module.exports = function(suite){ - var suites = [suite]; - - suite.on('require', visit); - - function visit(obj) { - var suite; - for (var key in obj) { - if ('function' == typeof obj[key]) { - var fn = obj[key]; - switch (key) { - case 'before': - suites[0].beforeAll(fn); - break; - case 'after': - suites[0].afterAll(fn); - break; - case 'beforeEach': - suites[0].beforeEach(fn); - break; - case 'afterEach': - suites[0].afterEach(fn); - break; - default: - suites[0].addTest(new Test(key, fn)); - } - } else { - var suite = Suite.create(suites[0], key); - suites.unshift(suite); - visit(obj[key]); - suites.shift(); - } - } - } -}; diff --git a/tests/mocha/lib/interfaces/index.js b/tests/mocha/lib/interfaces/index.js deleted file mode 100644 index f7b2655e37..0000000000 --- a/tests/mocha/lib/interfaces/index.js +++ /dev/null @@ -1,5 +0,0 @@ - -exports.bdd = require('./bdd'); -exports.tdd = require('./tdd'); -exports.qunit = require('./qunit'); -exports.exports = require('./exports'); diff --git a/tests/mocha/lib/interfaces/qunit.js b/tests/mocha/lib/interfaces/qunit.js deleted file mode 100644 index a1150c362b..0000000000 --- a/tests/mocha/lib/interfaces/qunit.js +++ /dev/null @@ -1,122 +0,0 @@ - -/** - * Module dependencies. - */ - -var Suite = require('../suite') - , Test = require('../test') - , utils = require('../utils'); - -/** - * QUnit-style interface: - * - * suite('Array'); - * - * test('#length', function(){ - * var arr = [1,2,3]; - * ok(arr.length == 3); - * }); - * - * test('#indexOf()', function(){ - * var arr = [1,2,3]; - * ok(arr.indexOf(1) == 0); - * ok(arr.indexOf(2) == 1); - * ok(arr.indexOf(3) == 2); - * }); - * - * suite('String'); - * - * test('#length', function(){ - * ok('foo'.length == 3); - * }); - * - */ - -module.exports = function(suite){ - var suites = [suite]; - - suite.on('pre-require', function(context, file, mocha){ - - /** - * Execute before running tests. - */ - - context.before = function(name, fn){ - suites[0].beforeAll(name, fn); - }; - - /** - * Execute after running tests. - */ - - context.after = function(name, fn){ - suites[0].afterAll(name, fn); - }; - - /** - * Execute before each test case. - */ - - context.beforeEach = function(name, fn){ - suites[0].beforeEach(name, fn); - }; - - /** - * Execute after each test case. - */ - - context.afterEach = function(name, fn){ - suites[0].afterEach(name, fn); - }; - - /** - * Describe a "suite" with the given `title`. - */ - - context.suite = function(title){ - if (suites.length > 1) suites.shift(); - var suite = Suite.create(suites[0], title); - suites.unshift(suite); - return suite; - }; - - /** - * Exclusive test-case. - */ - - context.suite.only = function(title, fn){ - var suite = context.suite(title, fn); - mocha.grep(suite.fullTitle()); - }; - - /** - * Describe a specification or test-case - * with the given `title` and callback `fn` - * acting as a thunk. - */ - - context.test = function(title, fn){ - var test = new Test(title, fn); - suites[0].addTest(test); - return test; - }; - - /** - * Exclusive test-case. - */ - - context.test.only = function(title, fn){ - var test = context.test(title, fn); - var reString = '^' + utils.escapeRegexp(test.fullTitle()) + '$'; - mocha.grep(new RegExp(reString)); - }; - - /** - * Pending test case. - */ - - context.test.skip = function(title){ - context.test(title); - }; - }); -}; diff --git a/tests/mocha/lib/interfaces/tdd.js b/tests/mocha/lib/interfaces/tdd.js deleted file mode 100644 index f5b78e00b6..0000000000 --- a/tests/mocha/lib/interfaces/tdd.js +++ /dev/null @@ -1,138 +0,0 @@ - -/** - * Module dependencies. - */ - -var Suite = require('../suite') - , Test = require('../test') - , utils = require('../utils');; - -/** - * TDD-style interface: - * - * suite('Array', function(){ - * suite('#indexOf()', function(){ - * suiteSetup(function(){ - * - * }); - * - * test('should return -1 when not present', function(){ - * - * }); - * - * test('should return the index when present', function(){ - * - * }); - * - * suiteTeardown(function(){ - * - * }); - * }); - * }); - * - */ - -module.exports = function(suite){ - var suites = [suite]; - - suite.on('pre-require', function(context, file, mocha){ - - /** - * Execute before each test case. - */ - - context.setup = function(name, fn){ - suites[0].beforeEach(name, fn); - }; - - /** - * Execute after each test case. - */ - - context.teardown = function(name, fn){ - suites[0].afterEach(name, fn); - }; - - /** - * Execute before the suite. - */ - - context.suiteSetup = function(name, fn){ - suites[0].beforeAll(name, fn); - }; - - /** - * Execute after the suite. - */ - - context.suiteTeardown = function(name, fn){ - suites[0].afterAll(name, fn); - }; - - /** - * Describe a "suite" with the given `title` - * and callback `fn` containing nested suites - * and/or tests. - */ - - context.suite = function(title, fn){ - var suite = Suite.create(suites[0], title); - suites.unshift(suite); - fn.call(suite); - suites.shift(); - return suite; - }; - - /** - * Pending suite. - */ - context.suite.skip = function(title, fn) { - var suite = Suite.create(suites[0], title); - suite.pending = true; - suites.unshift(suite); - fn.call(suite); - suites.shift(); - }; - - /** - * Exclusive test-case. - */ - - context.suite.only = function(title, fn){ - var suite = context.suite(title, fn); - mocha.grep(suite.fullTitle()); - }; - - /** - * Describe a specification or test-case - * with the given `title` and callback `fn` - * acting as a thunk. - */ - - context.test = function(title, fn){ - var suite = suites[0]; - if (suite.pending) var fn = null; - var test = new Test(title, fn); - suite.addTest(test); - return test; - }; - - /** - * Exclusive test-case. - */ - - context.test.only = function(title, fn){ - var test = context.test(title, fn); - var reString = '^' + utils.escapeRegexp(test.fullTitle()) + '$'; - mocha.grep(new RegExp(reString)); - }; - - /** - * Pending test case. - */ - - context.test.skip = function(title){ - context.test(title); - }; - }); -}; diff --git a/tests/mocha/lib/mocha.js b/tests/mocha/lib/mocha.js deleted file mode 100644 index dabb409651..0000000000 --- a/tests/mocha/lib/mocha.js +++ /dev/null @@ -1,370 +0,0 @@ -/*! - * mocha - * Copyright(c) 2011 TJ Holowaychuk - * MIT Licensed - */ - -/** - * Module dependencies. - */ - -var path = require('path') - , utils = require('./utils'); - -/** - * Expose `Mocha`. - */ - -exports = module.exports = Mocha; - -/** - * Expose internals. - */ - -exports.utils = utils; -exports.interfaces = require('./interfaces'); -exports.reporters = require('./reporters'); -exports.Runnable = require('./runnable'); -exports.Context = require('./context'); -exports.Runner = require('./runner'); -exports.Suite = require('./suite'); -exports.Hook = require('./hook'); -exports.Test = require('./test'); - -/** - * Return image `name` path. - * - * @param {String} name - * @return {String} - * @api private - */ - -function image(name) { - return __dirname + '/../images/' + name + '.png'; -} - -/** - * Setup mocha with `options`. - * - * Options: - * - * - `ui` name "bdd", "tdd", "exports" etc - * - `reporter` reporter instance, defaults to `mocha.reporters.Dot` - * - `globals` array of accepted globals - * - `timeout` timeout in milliseconds - * - `bail` bail on the first test failure - * - `slow` milliseconds to wait before considering a test slow - * - `ignoreLeaks` ignore global leaks - * - `grep` string or regexp to filter tests with - * - * @param {Object} options - * @api public - */ - -function Mocha(options) { - options = options || {}; - this.files = []; - this.options = options; - this.grep(options.grep); - this.suite = new exports.Suite('', new exports.Context); - this.ui(options.ui); - this.bail(options.bail); - this.reporter(options.reporter); - if (null != options.timeout) this.timeout(options.timeout); - this.useColors(options.useColors) - if (options.slow) this.slow(options.slow); - - this.suite.on('pre-require', function (context) { - exports.afterEach = context.afterEach || context.teardown; - exports.after = context.after || context.suiteTeardown; - exports.beforeEach = context.beforeEach || context.setup; - exports.before = context.before || context.suiteSetup; - exports.describe = context.describe || context.suite; - exports.it = context.it || context.test; - exports.setup = context.setup || context.beforeEach; - exports.suiteSetup = context.suiteSetup || context.before; - exports.suiteTeardown = context.suiteTeardown || context.after; - exports.suite = context.suite || context.describe; - exports.teardown = context.teardown || context.afterEach; - exports.test = context.test || context.it; - }); -} - -/** - * Enable or disable bailing on the first failure. - * - * @param {Boolean} [bail] - * @api public - */ - -Mocha.prototype.bail = function(bail){ - if (0 == arguments.length) bail = true; - this.suite.bail(bail); - return this; -}; - -/** - * Add test `file`. - * - * @param {String} file - * @api public - */ - -Mocha.prototype.addFile = function(file){ - this.files.push(file); - return this; -}; - -/** - * Set reporter to `reporter`, defaults to "dot". - * - * @param {String|Function} reporter name or constructor - * @api public - */ - -Mocha.prototype.reporter = function(reporter){ - if ('function' == typeof reporter) { - this._reporter = reporter; - } else { - reporter = reporter || 'dot'; - var _reporter; - try { _reporter = require('./reporters/' + reporter); } catch (err) {}; - if (!_reporter) try { _reporter = require(reporter); } catch (err) {}; - if (!_reporter && reporter === 'teamcity') - console.warn('The Teamcity reporter was moved to a package named ' + - 'mocha-teamcity-reporter ' + - '(https://npmjs.org/package/mocha-teamcity-reporter).'); - if (!_reporter) throw new Error('invalid reporter "' + reporter + '"'); - this._reporter = _reporter; - } - return this; -}; - -/** - * Set test UI `name`, defaults to "bdd". - * - * @param {String} bdd - * @api public - */ - -Mocha.prototype.ui = function(name){ - name = name || 'bdd'; - this._ui = exports.interfaces[name]; - if (!this._ui) try { this._ui = require(name); } catch (err) {}; - if (!this._ui) throw new Error('invalid interface "' + name + '"'); - this._ui = this._ui(this.suite); - return this; -}; - -/** - * Load registered files. - * - * @api private - */ - -Mocha.prototype.loadFiles = function(fn){ - var self = this; - var suite = this.suite; - var pending = this.files.length; - this.files.forEach(function(file){ - file = path.resolve(file); - suite.emit('pre-require', global, file, self); - suite.emit('require', require(file), file, self); - suite.emit('post-require', global, file, self); - --pending || (fn && fn()); - }); -}; - -/** - * Enable growl support. - * - * @api private - */ - -Mocha.prototype._growl = function(runner, reporter) { - var notify = require('growl'); - - runner.on('end', function(){ - var stats = reporter.stats; - if (stats.failures) { - var msg = stats.failures + ' of ' + runner.total + ' tests failed'; - notify(msg, { name: 'mocha', title: 'Failed', image: image('error') }); - } else { - notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', { - name: 'mocha' - , title: 'Passed' - , image: image('ok') - }); - } - }); -}; - -/** - * Add regexp to grep, if `re` is a string it is escaped. - * - * @param {RegExp|String} re - * @return {Mocha} - * @api public - */ - -Mocha.prototype.grep = function(re){ - this.options.grep = 'string' == typeof re - ? new RegExp(utils.escapeRegexp(re)) - : re; - return this; -}; - -/** - * Invert `.grep()` matches. - * - * @return {Mocha} - * @api public - */ - -Mocha.prototype.invert = function(){ - this.options.invert = true; - return this; -}; - -/** - * Ignore global leaks. - * - * @param {Boolean} ignore - * @return {Mocha} - * @api public - */ - -Mocha.prototype.ignoreLeaks = function(ignore){ - this.options.ignoreLeaks = !!ignore; - return this; -}; - -/** - * Enable global leak checking. - * - * @return {Mocha} - * @api public - */ - -Mocha.prototype.checkLeaks = function(){ - this.options.ignoreLeaks = false; - return this; -}; - -/** - * Enable growl support. - * - * @return {Mocha} - * @api public - */ - -Mocha.prototype.growl = function(){ - this.options.growl = true; - return this; -}; - -/** - * Ignore `globals` array or string. - * - * @param {Array|String} globals - * @return {Mocha} - * @api public - */ - -Mocha.prototype.globals = function(globals){ - this.options.globals = (this.options.globals || []).concat(globals); - return this; -}; - -/** - * Emit color output. - * - * @param {Boolean} colors - * @return {Mocha} - * @api public - */ - -Mocha.prototype.useColors = function(colors){ - this.options.useColors = arguments.length && colors != undefined - ? colors - : true; - return this; -}; - -/** - * Use inline diffs rather than +/-. - * - * @param {Boolean} inlineDiffs - * @return {Mocha} - * @api public - */ - -Mocha.prototype.useInlineDiffs = function(inlineDiffs) { - this.options.useInlineDiffs = arguments.length && inlineDiffs != undefined - ? inlineDiffs - : false; - return this; -}; - -/** - * Set the timeout in milliseconds. - * - * @param {Number} timeout - * @return {Mocha} - * @api public - */ - -Mocha.prototype.timeout = function(timeout){ - this.suite.timeout(timeout); - return this; -}; - -/** - * Set slowness threshold in milliseconds. - * - * @param {Number} slow - * @return {Mocha} - * @api public - */ - -Mocha.prototype.slow = function(slow){ - this.suite.slow(slow); - return this; -}; - -/** - * Makes all tests async (accepting a callback) - * - * @return {Mocha} - * @api public - */ - -Mocha.prototype.asyncOnly = function(){ - this.options.asyncOnly = true; - return this; -}; - -/** - * Run tests and invoke `fn()` when complete. - * - * @param {Function} fn - * @return {Runner} - * @api public - */ - -Mocha.prototype.run = function(fn){ - if (this.files.length) this.loadFiles(); - var suite = this.suite; - var options = this.options; - options.files = this.files; - var runner = new exports.Runner(suite); - var reporter = new this._reporter(runner, options); - runner.ignoreLeaks = false !== options.ignoreLeaks; - runner.asyncOnly = options.asyncOnly; - if (options.grep) runner.grep(options.grep, options.invert); - if (options.globals) runner.globals(options.globals); - if (options.growl) this._growl(runner, reporter); - exports.reporters.Base.useColors = options.useColors; - exports.reporters.Base.inlineDiffs = options.useInlineDiffs; - return runner.run(fn); -}; diff --git a/tests/mocha/lib/ms.js b/tests/mocha/lib/ms.js deleted file mode 100644 index 4096637431..0000000000 --- a/tests/mocha/lib/ms.js +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Helpers. - */ - -var s = 1000; -var m = s * 60; -var h = m * 60; -var d = h * 24; -var y = d * 365.25; - -/** - * Parse or format the given `val`. - * - * Options: - * - * - `long` verbose formatting [false] - * - * @param {String|Number} val - * @param {Object} options - * @return {String|Number} - * @api public - */ - -module.exports = function(val, options){ - options = options || {}; - if ('string' == typeof val) return parse(val); - return options.long ? longFormat(val) : shortFormat(val); -}; - -/** - * Parse the given `str` and return milliseconds. - * - * @param {String} str - * @return {Number} - * @api private - */ - -function parse(str) { - var match = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str); - if (!match) return; - var n = parseFloat(match[1]); - var type = (match[2] || 'ms').toLowerCase(); - switch (type) { - case 'years': - case 'year': - case 'y': - return n * y; - case 'days': - case 'day': - case 'd': - return n * d; - case 'hours': - case 'hour': - case 'h': - return n * h; - case 'minutes': - case 'minute': - case 'm': - return n * m; - case 'seconds': - case 'second': - case 's': - return n * s; - case 'ms': - return n; - } -} - -/** - * Short format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ - -function shortFormat(ms) { - if (ms >= d) return Math.round(ms / d) + 'd'; - if (ms >= h) return Math.round(ms / h) + 'h'; - if (ms >= m) return Math.round(ms / m) + 'm'; - if (ms >= s) return Math.round(ms / s) + 's'; - return ms + 'ms'; -} - -/** - * Long format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ - -function longFormat(ms) { - return plural(ms, d, 'day') - || plural(ms, h, 'hour') - || plural(ms, m, 'minute') - || plural(ms, s, 'second') - || ms + ' ms'; -} - -/** - * Pluralization helper. - */ - -function plural(ms, n, name) { - if (ms < n) return; - if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; - return Math.ceil(ms / n) + ' ' + name + 's'; -} diff --git a/tests/mocha/lib/reporters/base.js b/tests/mocha/lib/reporters/base.js deleted file mode 100644 index 0754fe1a37..0000000000 --- a/tests/mocha/lib/reporters/base.js +++ /dev/null @@ -1,507 +0,0 @@ - -/** - * Module dependencies. - */ - -var tty = require('tty') - , diff = require('diff') - , ms = require('../ms') - , utils = require('../utils'); - -/** - * Save timer references to avoid Sinon interfering (see GH-237). - */ - -var Date = global.Date - , setTimeout = global.setTimeout - , setInterval = global.setInterval - , clearTimeout = global.clearTimeout - , clearInterval = global.clearInterval; - -/** - * Check if both stdio streams are associated with a tty. - */ - -var isatty = tty.isatty(1) && tty.isatty(2); - -/** - * Expose `Base`. - */ - -exports = module.exports = Base; - -/** - * Enable coloring by default. - */ - -exports.useColors = isatty || (process.env.MOCHA_COLORS !== undefined); - -/** - * Inline diffs instead of +/- - */ - -exports.inlineDiffs = false; - -/** - * Default color map. - */ - -exports.colors = { - 'pass': 90 - , 'fail': 31 - , 'bright pass': 92 - , 'bright fail': 91 - , 'bright yellow': 93 - , 'pending': 36 - , 'suite': 0 - , 'error title': 0 - , 'error message': 31 - , 'error stack': 90 - , 'checkmark': 32 - , 'fast': 90 - , 'medium': 33 - , 'slow': 31 - , 'green': 32 - , 'light': 90 - , 'diff gutter': 90 - , 'diff added': 42 - , 'diff removed': 41 -}; - -/** - * Default symbol map. - */ - -exports.symbols = { - ok: '✓', - err: '✖', - dot: '․' -}; - -// With node.js on Windows: use symbols available in terminal default fonts -if ('win32' == process.platform) { - exports.symbols.ok = '\u221A'; - exports.symbols.err = '\u00D7'; - exports.symbols.dot = '.'; -} - -/** - * Color `str` with the given `type`, - * allowing colors to be disabled, - * as well as user-defined color - * schemes. - * - * @param {String} type - * @param {String} str - * @return {String} - * @api private - */ - -var color = exports.color = function(type, str) { - if (!exports.useColors) return str; - return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m'; -}; - -/** - * Expose term window size, with some - * defaults for when stderr is not a tty. - */ - -exports.window = { - width: isatty - ? process.stdout.getWindowSize - ? process.stdout.getWindowSize(1)[0] - : tty.getWindowSize()[1] - : 75 -}; - -/** - * Expose some basic cursor interactions - * that are common among reporters. - */ - -exports.cursor = { - hide: function(){ - isatty && process.stdout.write('\u001b[?25l'); - }, - - show: function(){ - isatty && process.stdout.write('\u001b[?25h'); - }, - - deleteLine: function(){ - isatty && process.stdout.write('\u001b[2K'); - }, - - beginningOfLine: function(){ - isatty && process.stdout.write('\u001b[0G'); - }, - - CR: function(){ - if (isatty) { - exports.cursor.deleteLine(); - exports.cursor.beginningOfLine(); - } else { - process.stdout.write('\r'); - } - } -}; - -/** - * Outut the given `failures` as a list. - * - * @param {Array} failures - * @api public - */ - -exports.list = function(failures){ - console.error(); - failures.forEach(function(test, i){ - // format - var fmt = color('error title', ' %s) %s:\n') - + color('error message', ' %s') - + color('error stack', '\n%s\n'); - - // msg - var err = test.err - , message = err.message || '' - , stack = err.stack || message - , index = stack.indexOf(message) + message.length - , msg = stack.slice(0, index) - , actual = err.actual - , expected = err.expected - , escape = true; - - // uncaught - if (err.uncaught) { - msg = 'Uncaught ' + msg; - } - - // explicitly show diff - if (err.showDiff && sameType(actual, expected)) { - escape = false; - err.actual = actual = stringify(canonicalize(actual)); - err.expected = expected = stringify(canonicalize(expected)); - } - - // actual / expected diff - if ('string' == typeof actual && 'string' == typeof expected) { - fmt = color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n'); - var match = message.match(/^([^:]+): expected/); - msg = '\n ' + color('error message', match ? match[1] : msg); - - if (exports.inlineDiffs) { - msg += inlineDiff(err, escape); - } else { - msg += unifiedDiff(err, escape); - } - } - - // indent stack trace without msg - stack = stack.slice(index ? index + 1 : index) - .replace(/^/gm, ' '); - - console.error(fmt, (i + 1), test.fullTitle(), msg, stack); - }); -}; - -/** - * Initialize a new `Base` reporter. - * - * All other reporters generally - * inherit from this reporter, providing - * stats such as test duration, number - * of tests passed / failed etc. - * - * @param {Runner} runner - * @api public - */ - -function Base(runner) { - var self = this - , stats = this.stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 } - , failures = this.failures = []; - - if (!runner) return; - this.runner = runner; - - runner.stats = stats; - - runner.on('start', function(){ - stats.start = new Date; - }); - - runner.on('suite', function(suite){ - stats.suites = stats.suites || 0; - suite.root || stats.suites++; - }); - - runner.on('test end', function(test){ - stats.tests = stats.tests || 0; - stats.tests++; - }); - - runner.on('pass', function(test){ - stats.passes = stats.passes || 0; - - var medium = test.slow() / 2; - test.speed = test.duration > test.slow() - ? 'slow' - : test.duration > medium - ? 'medium' - : 'fast'; - - stats.passes++; - }); - - runner.on('fail', function(test, err){ - stats.failures = stats.failures || 0; - stats.failures++; - test.err = err; - failures.push(test); - }); - - runner.on('end', function(){ - stats.end = new Date; - stats.duration = new Date - stats.start; - }); - - runner.on('pending', function(){ - stats.pending++; - }); -} - -/** - * Output common epilogue used by many of - * the bundled reporters. - * - * @api public - */ - -Base.prototype.epilogue = function(){ - var stats = this.stats; - var tests; - var fmt; - - console.log(); - - // passes - fmt = color('bright pass', ' ') - + color('green', ' %d passing') - + color('light', ' (%s)'); - - console.log(fmt, - stats.passes || 0, - ms(stats.duration)); - - // pending - if (stats.pending) { - fmt = color('pending', ' ') - + color('pending', ' %d pending'); - - console.log(fmt, stats.pending); - } - - // failures - if (stats.failures) { - fmt = color('fail', ' %d failing'); - - console.error(fmt, - stats.failures); - - Base.list(this.failures); - console.error(); - } - - console.log(); -}; - -/** - * Pad the given `str` to `len`. - * - * @param {String} str - * @param {String} len - * @return {String} - * @api private - */ - -function pad(str, len) { - str = String(str); - return Array(len - str.length + 1).join(' ') + str; -} - - -/** - * Returns an inline diff between 2 strings with coloured ANSI output - * - * @param {Error} Error with actual/expected - * @return {String} Diff - * @api private - */ - -function inlineDiff(err, escape) { - var msg = errorDiff(err, 'WordsWithSpace', escape); - - // linenos - var lines = msg.split('\n'); - if (lines.length > 4) { - var width = String(lines.length).length; - msg = lines.map(function(str, i){ - return pad(++i, width) + ' |' + ' ' + str; - }).join('\n'); - } - - // legend - msg = '\n' - + color('diff removed', 'actual') - + ' ' - + color('diff added', 'expected') - + '\n\n' - + msg - + '\n'; - - // indent - msg = msg.replace(/^/gm, ' '); - return msg; -} - -/** - * Returns a unified diff between 2 strings - * - * @param {Error} Error with actual/expected - * @return {String} Diff - * @api private - */ - -function unifiedDiff(err, escape) { - var indent = ' '; - function cleanUp(line) { - if (escape) { - line = escapeInvisibles(line); - } - if (line[0] === '+') return indent + colorLines('diff added', line); - if (line[0] === '-') return indent + colorLines('diff removed', line); - if (line.match(/\@\@/)) return null; - if (line.match(/\\ No newline/)) return null; - else return indent + line; - } - function notBlank(line) { - return line != null; - } - msg = diff.createPatch('string', err.actual, err.expected); - var lines = msg.split('\n').splice(4); - return '\n ' - + colorLines('diff added', '+ expected') + ' ' - + colorLines('diff removed', '- actual') - + '\n\n' - + lines.map(cleanUp).filter(notBlank).join('\n'); -} - -/** - * Return a character diff for `err`. - * - * @param {Error} err - * @return {String} - * @api private - */ - -function errorDiff(err, type, escape) { - var actual = escape ? escapeInvisibles(err.actual) : err.actual; - var expected = escape ? escapeInvisibles(err.expected) : err.expected; - return diff['diff' + type](actual, expected).map(function(str){ - if (str.added) return colorLines('diff added', str.value); - if (str.removed) return colorLines('diff removed', str.value); - return str.value; - }).join(''); -} - -/** - * Returns a string with all invisible characters in plain text - * - * @param {String} line - * @return {String} - * @api private - */ -function escapeInvisibles(line) { - return line.replace(/\t/g, '') - .replace(/\r/g, '') - .replace(/\n/g, '\n'); -} - -/** - * Color lines for `str`, using the color `name`. - * - * @param {String} name - * @param {String} str - * @return {String} - * @api private - */ - -function colorLines(name, str) { - return str.split('\n').map(function(str){ - return color(name, str); - }).join('\n'); -} - -/** - * Stringify `obj`. - * - * @param {Object} obj - * @return {String} - * @api private - */ - -function stringify(obj) { - if (obj instanceof RegExp) return obj.toString(); - return JSON.stringify(obj, null, 2); -} - -/** - * Return a new object that has the keys in sorted order. - * @param {Object} obj - * @return {Object} - * @api private - */ - - function canonicalize(obj, stack) { - stack = stack || []; - - if (utils.indexOf(stack, obj) !== -1) return obj; - - var canonicalizedObj; - - if ('[object Array]' == {}.toString.call(obj)) { - stack.push(obj); - canonicalizedObj = utils.map(obj, function(item) { - return canonicalize(item, stack); - }); - stack.pop(); - } else if (typeof obj === 'object' && obj !== null) { - stack.push(obj); - canonicalizedObj = {}; - utils.forEach(utils.keys(obj).sort(), function(key) { - canonicalizedObj[key] = canonicalize(obj[key], stack); - }); - stack.pop(); - } else { - canonicalizedObj = obj; - } - - return canonicalizedObj; - } - -/** - * Check that a / b have the same type. - * - * @param {Object} a - * @param {Object} b - * @return {Boolean} - * @api private - */ - -function sameType(a, b) { - a = Object.prototype.toString.call(a); - b = Object.prototype.toString.call(b); - return a == b; -} - diff --git a/tests/mocha/lib/reporters/doc.js b/tests/mocha/lib/reporters/doc.js deleted file mode 100644 index 2e5bf57fc4..0000000000 --- a/tests/mocha/lib/reporters/doc.js +++ /dev/null @@ -1,56 +0,0 @@ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , utils = require('../utils'); - -/** - * Expose `Doc`. - */ - -exports = module.exports = Doc; - -/** - * Initialize a new `Doc` reporter. - * - * @param {Runner} runner - * @api public - */ - -function Doc(runner) { - Base.call(this, runner); - - var self = this - , stats = this.stats - , total = runner.total - , indents = 2; - - function indent() { - return Array(indents).join(' '); - } - - runner.on('suite', function(suite){ - if (suite.root) return; - ++indents; - console.log('%s
    ', indent()); - ++indents; - console.log('%s

    %s

    ', indent(), utils.escape(suite.title)); - console.log('%s
    ', indent()); - }); - - runner.on('suite end', function(suite){ - if (suite.root) return; - console.log('%s
    ', indent()); - --indents; - console.log('%s
    ', indent()); - --indents; - }); - - runner.on('pass', function(test){ - console.log('%s
    %s
    ', indent(), utils.escape(test.title)); - var code = utils.escape(utils.clean(test.fn.toString())); - console.log('%s
    %s
    ', indent(), code); - }); -} diff --git a/tests/mocha/lib/reporters/dot.js b/tests/mocha/lib/reporters/dot.js deleted file mode 100644 index 0c298ba71d..0000000000 --- a/tests/mocha/lib/reporters/dot.js +++ /dev/null @@ -1,62 +0,0 @@ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , color = Base.color; - -/** - * Expose `Dot`. - */ - -exports = module.exports = Dot; - -/** - * Initialize a new `Dot` matrix test reporter. - * - * @param {Runner} runner - * @api public - */ - -function Dot(runner) { - Base.call(this, runner); - - var self = this - , stats = this.stats - , width = Base.window.width * .75 | 0 - , n = 0; - - runner.on('start', function(){ - process.stdout.write('\n '); - }); - - runner.on('pending', function(test){ - process.stdout.write(color('pending', Base.symbols.dot)); - }); - - runner.on('pass', function(test){ - if (++n % width == 0) process.stdout.write('\n '); - if ('slow' == test.speed) { - process.stdout.write(color('bright yellow', Base.symbols.dot)); - } else { - process.stdout.write(color(test.speed, Base.symbols.dot)); - } - }); - - runner.on('fail', function(test, err){ - if (++n % width == 0) process.stdout.write('\n '); - process.stdout.write(color('fail', Base.symbols.dot)); - }); - - runner.on('end', function(){ - console.log(); - self.epilogue(); - }); -} - -/** - * Inherit from `Base.prototype`. - */ - -Dot.prototype.__proto__ = Base.prototype; \ No newline at end of file diff --git a/tests/mocha/lib/reporters/html-cov.js b/tests/mocha/lib/reporters/html-cov.js deleted file mode 100644 index bfb27ffdf6..0000000000 --- a/tests/mocha/lib/reporters/html-cov.js +++ /dev/null @@ -1,51 +0,0 @@ - -/** - * Module dependencies. - */ - -var JSONCov = require('./json-cov') - , fs = require('fs'); - -/** - * Expose `HTMLCov`. - */ - -exports = module.exports = HTMLCov; - -/** - * Initialize a new `JsCoverage` reporter. - * - * @param {Runner} runner - * @api public - */ - -function HTMLCov(runner) { - var jade = require('jade') - , file = __dirname + '/templates/coverage.jade' - , str = fs.readFileSync(file, 'utf8') - , fn = jade.compile(str, { filename: file }) - , self = this; - - JSONCov.call(this, runner, false); - - runner.on('end', function(){ - process.stdout.write(fn({ - cov: self.cov - , coverageClass: coverageClass - })); - }); -} - -/** - * Return coverage class for `n`. - * - * @return {String} - * @api private - */ - -function coverageClass(n) { - if (n >= 75) return 'high'; - if (n >= 50) return 'medium'; - if (n >= 25) return 'low'; - return 'terrible'; -} \ No newline at end of file diff --git a/tests/mocha/lib/reporters/html.js b/tests/mocha/lib/reporters/html.js deleted file mode 100644 index 8b44e3ed50..0000000000 --- a/tests/mocha/lib/reporters/html.js +++ /dev/null @@ -1,273 +0,0 @@ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , utils = require('../utils') - , Progress = require('../browser/progress') - , escape = utils.escape; - -/** - * Save timer references to avoid Sinon interfering (see GH-237). - */ - -var Date = global.Date - , setTimeout = global.setTimeout - , setInterval = global.setInterval - , clearTimeout = global.clearTimeout - , clearInterval = global.clearInterval; - -/** - * Expose `HTML`. - */ - -exports = module.exports = HTML; - -/** - * Stats template. - */ - -var statsTemplate = ''; - -/** - * Initialize a new `HTML` reporter. - * - * @param {Runner} runner - * @api public - */ - -function HTML(runner) { - Base.call(this, runner); - - var self = this - , stats = this.stats - , total = runner.total - , stat = fragment(statsTemplate) - , items = stat.getElementsByTagName('li') - , passes = items[1].getElementsByTagName('em')[0] - , passesLink = items[1].getElementsByTagName('a')[0] - , failures = items[2].getElementsByTagName('em')[0] - , failuresLink = items[2].getElementsByTagName('a')[0] - , duration = items[3].getElementsByTagName('em')[0] - , canvas = stat.getElementsByTagName('canvas')[0] - , report = fragment('
      ') - , stack = [report] - , progress - , ctx - , root = document.getElementById('mocha'); - - if (canvas.getContext) { - var ratio = window.devicePixelRatio || 1; - canvas.style.width = canvas.width; - canvas.style.height = canvas.height; - canvas.width *= ratio; - canvas.height *= ratio; - ctx = canvas.getContext('2d'); - ctx.scale(ratio, ratio); - progress = new Progress; - } - - if (!root) return error('#mocha div missing, add it to your document'); - - // pass toggle - on(passesLink, 'click', function(){ - unhide(); - var name = /pass/.test(report.className) ? '' : ' pass'; - report.className = report.className.replace(/fail|pass/g, '') + name; - if (report.className.trim()) hideSuitesWithout('test pass'); - }); - - // failure toggle - on(failuresLink, 'click', function(){ - unhide(); - var name = /fail/.test(report.className) ? '' : ' fail'; - report.className = report.className.replace(/fail|pass/g, '') + name; - if (report.className.trim()) hideSuitesWithout('test fail'); - }); - - root.appendChild(stat); - root.appendChild(report); - - if (progress) progress.size(40); - - runner.on('suite', function(suite){ - if (suite.root) return; - - // suite - var url = self.suiteURL(suite); - var el = fragment('
    • %s

    • ', url, escape(suite.title)); - - // container - stack[0].appendChild(el); - stack.unshift(document.createElement('ul')); - el.appendChild(stack[0]); - }); - - runner.on('suite end', function(suite){ - if (suite.root) return; - stack.shift(); - }); - - runner.on('fail', function(test, err){ - if ('hook' == test.type) runner.emit('test end', test); - }); - - runner.on('test end', function(test){ - // TODO: add to stats - var percent = stats.tests / this.total * 100 | 0; - if (progress) progress.update(percent).draw(ctx); - - // update stats - var ms = new Date - stats.start; - text(passes, stats.passes); - text(failures, stats.failures); - text(duration, (ms / 1000).toFixed(2)); - - // test - if ('passed' == test.state) { - var url = self.testURL(test); - var el = fragment('
    • %e%ems

    • ', test.speed, test.title, test.duration, url); - } else if (test.pending) { - var el = fragment('
    • %e

    • ', test.title); - } else { - var el = fragment('
    • %e

    • ', test.title, encodeURIComponent(test.fullTitle())); - var str = test.err.stack || test.err.toString(); - - // FF / Opera do not add the message - if (!~str.indexOf(test.err.message)) { - str = test.err.message + '\n' + str; - } - - // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we - // check for the result of the stringifying. - if ('[object Error]' == str) str = test.err.message; - - // Safari doesn't give you a stack. Let's at least provide a source line. - if (!test.err.stack && test.err.sourceURL && test.err.line !== undefined) { - str += "\n(" + test.err.sourceURL + ":" + test.err.line + ")"; - } - - el.appendChild(fragment('
      %e
      ', str)); - } - - // toggle code - // TODO: defer - if (!test.pending) { - var h2 = el.getElementsByTagName('h2')[0]; - - on(h2, 'click', function(){ - pre.style.display = 'none' == pre.style.display - ? 'block' - : 'none'; - }); - - var pre = fragment('
      %e
      ', utils.clean(test.fn.toString())); - el.appendChild(pre); - pre.style.display = 'none'; - } - - // Don't call .appendChild if #mocha-report was already .shift()'ed off the stack. - if (stack[0]) stack[0].appendChild(el); - }); -} - -/** - * Provide suite URL - * - * @param {Object} [suite] - */ - -HTML.prototype.suiteURL = function(suite){ - return '?grep=' + encodeURIComponent(suite.fullTitle()); -}; - -/** - * Provide test URL - * - * @param {Object} [test] - */ - -HTML.prototype.testURL = function(test){ - return '?grep=' + encodeURIComponent(test.fullTitle()); -}; - -/** - * Display error `msg`. - */ - -function error(msg) { - document.body.appendChild(fragment('
      %s
      ', msg)); -} - -/** - * Return a DOM fragment from `html`. - */ - -function fragment(html) { - var args = arguments - , div = document.createElement('div') - , i = 1; - - div.innerHTML = html.replace(/%([se])/g, function(_, type){ - switch (type) { - case 's': return String(args[i++]); - case 'e': return escape(args[i++]); - } - }); - - return div.firstChild; -} - -/** - * Check for suites that do not have elements - * with `classname`, and hide them. - */ - -function hideSuitesWithout(classname) { - var suites = document.getElementsByClassName('suite'); - for (var i = 0; i < suites.length; i++) { - var els = suites[i].getElementsByClassName(classname); - if (0 == els.length) suites[i].className += ' hidden'; - } -} - -/** - * Unhide .hidden suites. - */ - -function unhide() { - var els = document.getElementsByClassName('suite hidden'); - for (var i = 0; i < els.length; ++i) { - els[i].className = els[i].className.replace('suite hidden', 'suite'); - } -} - -/** - * Set `el` text to `str`. - */ - -function text(el, str) { - if (el.textContent) { - el.textContent = str; - } else { - el.innerText = str; - } -} - -/** - * Listen on `event` with callback `fn`. - */ - -function on(el, event, fn) { - if (el.addEventListener) { - el.addEventListener(event, fn, false); - } else { - el.attachEvent('on' + event, fn); - } -} diff --git a/tests/mocha/lib/reporters/index.js b/tests/mocha/lib/reporters/index.js deleted file mode 100644 index 1c4fccf060..0000000000 --- a/tests/mocha/lib/reporters/index.js +++ /dev/null @@ -1,18 +0,0 @@ - -exports.Base = require('./base'); -exports.Dot = require('./dot'); -exports.Doc = require('./doc'); -exports.TAP = require('./tap'); -exports.JSON = require('./json'); -exports.HTML = require('./html'); -exports.List = require('./list'); -exports.Min = require('./min'); -exports.Spec = require('./spec'); -exports.Nyan = require('./nyan'); -exports.XUnit = require('./xunit'); -exports.Markdown = require('./markdown'); -exports.Progress = require('./progress'); -exports.Landing = require('./landing'); -exports.JSONCov = require('./json-cov'); -exports.HTMLCov = require('./html-cov'); -exports.JSONStream = require('./json-stream'); diff --git a/tests/mocha/lib/reporters/json-cov.js b/tests/mocha/lib/reporters/json-cov.js deleted file mode 100644 index 73d0009377..0000000000 --- a/tests/mocha/lib/reporters/json-cov.js +++ /dev/null @@ -1,153 +0,0 @@ - -/** - * Module dependencies. - */ - -var Base = require('./base'); - -/** - * Expose `JSONCov`. - */ - -exports = module.exports = JSONCov; - -/** - * Initialize a new `JsCoverage` reporter. - * - * @param {Runner} runner - * @param {Boolean} output - * @api public - */ - -function JSONCov(runner, output) { - var self = this - , output = 1 == arguments.length ? true : output; - - Base.call(this, runner); - - var tests = [] - , failures = [] - , passes = []; - - runner.on('test end', function(test){ - tests.push(test); - }); - - runner.on('pass', function(test){ - passes.push(test); - }); - - runner.on('fail', function(test){ - failures.push(test); - }); - - runner.on('end', function(){ - var cov = global._$jscoverage || {}; - var result = self.cov = map(cov); - result.stats = self.stats; - result.tests = tests.map(clean); - result.failures = failures.map(clean); - result.passes = passes.map(clean); - if (!output) return; - process.stdout.write(JSON.stringify(result, null, 2 )); - }); -} - -/** - * Map jscoverage data to a JSON structure - * suitable for reporting. - * - * @param {Object} cov - * @return {Object} - * @api private - */ - -function map(cov) { - var ret = { - instrumentation: 'node-jscoverage' - , sloc: 0 - , hits: 0 - , misses: 0 - , coverage: 0 - , files: [] - }; - - for (var filename in cov) { - var data = coverage(filename, cov[filename]); - ret.files.push(data); - ret.hits += data.hits; - ret.misses += data.misses; - ret.sloc += data.sloc; - } - - ret.files.sort(function(a, b) { - return a.filename.localeCompare(b.filename); - }); - - if (ret.sloc > 0) { - ret.coverage = (ret.hits / ret.sloc) * 100; - } - - return ret; -}; - -/** - * Map jscoverage data for a single source file - * to a JSON structure suitable for reporting. - * - * @param {String} filename name of the source file - * @param {Object} data jscoverage coverage data - * @return {Object} - * @api private - */ - -function coverage(filename, data) { - var ret = { - filename: filename, - coverage: 0, - hits: 0, - misses: 0, - sloc: 0, - source: {} - }; - - data.source.forEach(function(line, num){ - num++; - - if (data[num] === 0) { - ret.misses++; - ret.sloc++; - } else if (data[num] !== undefined) { - ret.hits++; - ret.sloc++; - } - - ret.source[num] = { - source: line - , coverage: data[num] === undefined - ? '' - : data[num] - }; - }); - - ret.coverage = ret.hits / ret.sloc * 100; - - return ret; -} - -/** - * Return a plain-object representation of `test` - * free of cyclic properties etc. - * - * @param {Object} test - * @return {Object} - * @api private - */ - -function clean(test) { - return { - title: test.title - , fullTitle: test.fullTitle() - , duration: test.duration - } -} diff --git a/tests/mocha/lib/reporters/json-stream.js b/tests/mocha/lib/reporters/json-stream.js deleted file mode 100644 index 7cb8fbede7..0000000000 --- a/tests/mocha/lib/reporters/json-stream.js +++ /dev/null @@ -1,61 +0,0 @@ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , color = Base.color; - -/** - * Expose `List`. - */ - -exports = module.exports = List; - -/** - * Initialize a new `List` test reporter. - * - * @param {Runner} runner - * @api public - */ - -function List(runner) { - Base.call(this, runner); - - var self = this - , stats = this.stats - , total = runner.total; - - runner.on('start', function(){ - console.log(JSON.stringify(['start', { total: total }])); - }); - - runner.on('pass', function(test){ - console.log(JSON.stringify(['pass', clean(test)])); - }); - - runner.on('fail', function(test, err){ - console.log(JSON.stringify(['fail', clean(test)])); - }); - - runner.on('end', function(){ - process.stdout.write(JSON.stringify(['end', self.stats])); - }); -} - -/** - * Return a plain-object representation of `test` - * free of cyclic properties etc. - * - * @param {Object} test - * @return {Object} - * @api private - */ - -function clean(test) { - return { - title: test.title - , fullTitle: test.fullTitle() - , duration: test.duration - } -} \ No newline at end of file diff --git a/tests/mocha/lib/reporters/json.js b/tests/mocha/lib/reporters/json.js deleted file mode 100644 index a699f5014a..0000000000 --- a/tests/mocha/lib/reporters/json.js +++ /dev/null @@ -1,70 +0,0 @@ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , cursor = Base.cursor - , color = Base.color; - -/** - * Expose `JSON`. - */ - -exports = module.exports = JSONReporter; - -/** - * Initialize a new `JSON` reporter. - * - * @param {Runner} runner - * @api public - */ - -function JSONReporter(runner) { - var self = this; - Base.call(this, runner); - - var tests = [] - , failures = [] - , passes = []; - - runner.on('test end', function(test){ - tests.push(test); - }); - - runner.on('pass', function(test){ - passes.push(test); - }); - - runner.on('fail', function(test){ - failures.push(test); - }); - - runner.on('end', function(){ - var obj = { - stats: self.stats - , tests: tests.map(clean) - , failures: failures.map(clean) - , passes: passes.map(clean) - }; - - process.stdout.write(JSON.stringify(obj, null, 2)); - }); -} - -/** - * Return a plain-object representation of `test` - * free of cyclic properties etc. - * - * @param {Object} test - * @return {Object} - * @api private - */ - -function clean(test) { - return { - title: test.title - , fullTitle: test.fullTitle() - , duration: test.duration - } -} \ No newline at end of file diff --git a/tests/mocha/lib/reporters/landing.js b/tests/mocha/lib/reporters/landing.js deleted file mode 100644 index bf064f64b2..0000000000 --- a/tests/mocha/lib/reporters/landing.js +++ /dev/null @@ -1,97 +0,0 @@ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , cursor = Base.cursor - , color = Base.color; - -/** - * Expose `Landing`. - */ - -exports = module.exports = Landing; - -/** - * Airplane color. - */ - -Base.colors.plane = 0; - -/** - * Airplane crash color. - */ - -Base.colors['plane crash'] = 31; - -/** - * Runway color. - */ - -Base.colors.runway = 90; - -/** - * Initialize a new `Landing` reporter. - * - * @param {Runner} runner - * @api public - */ - -function Landing(runner) { - Base.call(this, runner); - - var self = this - , stats = this.stats - , width = Base.window.width * .75 | 0 - , total = runner.total - , stream = process.stdout - , plane = color('plane', '✈') - , crashed = -1 - , n = 0; - - function runway() { - var buf = Array(width).join('-'); - return ' ' + color('runway', buf); - } - - runner.on('start', function(){ - stream.write('\n '); - cursor.hide(); - }); - - runner.on('test end', function(test){ - // check if the plane crashed - var col = -1 == crashed - ? width * ++n / total | 0 - : crashed; - - // show the crash - if ('failed' == test.state) { - plane = color('plane crash', '✈'); - crashed = col; - } - - // render landing strip - stream.write('\u001b[4F\n\n'); - stream.write(runway()); - stream.write('\n '); - stream.write(color('runway', Array(col).join('⋅'))); - stream.write(plane) - stream.write(color('runway', Array(width - col).join('⋅') + '\n')); - stream.write(runway()); - stream.write('\u001b[0m'); - }); - - runner.on('end', function(){ - cursor.show(); - console.log(); - self.epilogue(); - }); -} - -/** - * Inherit from `Base.prototype`. - */ - -Landing.prototype.__proto__ = Base.prototype; \ No newline at end of file diff --git a/tests/mocha/lib/reporters/list.js b/tests/mocha/lib/reporters/list.js deleted file mode 100644 index 3328e157a8..0000000000 --- a/tests/mocha/lib/reporters/list.js +++ /dev/null @@ -1,64 +0,0 @@ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , cursor = Base.cursor - , color = Base.color; - -/** - * Expose `List`. - */ - -exports = module.exports = List; - -/** - * Initialize a new `List` test reporter. - * - * @param {Runner} runner - * @api public - */ - -function List(runner) { - Base.call(this, runner); - - var self = this - , stats = this.stats - , n = 0; - - runner.on('start', function(){ - console.log(); - }); - - runner.on('test', function(test){ - process.stdout.write(color('pass', ' ' + test.fullTitle() + ': ')); - }); - - runner.on('pending', function(test){ - var fmt = color('checkmark', ' -') - + color('pending', ' %s'); - console.log(fmt, test.fullTitle()); - }); - - runner.on('pass', function(test){ - var fmt = color('checkmark', ' '+Base.symbols.dot) - + color('pass', ' %s: ') - + color(test.speed, '%dms'); - cursor.CR(); - console.log(fmt, test.fullTitle(), test.duration); - }); - - runner.on('fail', function(test, err){ - cursor.CR(); - console.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); - }); - - runner.on('end', self.epilogue.bind(self)); -} - -/** - * Inherit from `Base.prototype`. - */ - -List.prototype.__proto__ = Base.prototype; diff --git a/tests/mocha/lib/reporters/markdown.js b/tests/mocha/lib/reporters/markdown.js deleted file mode 100644 index 6383a64248..0000000000 --- a/tests/mocha/lib/reporters/markdown.js +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Module dependencies. - */ - -var Base = require('./base') - , utils = require('../utils'); - -/** - * Expose `Markdown`. - */ - -exports = module.exports = Markdown; - -/** - * Initialize a new `Markdown` reporter. - * - * @param {Runner} runner - * @api public - */ - -function Markdown(runner) { - Base.call(this, runner); - - var self = this - , stats = this.stats - , level = 0 - , buf = ''; - - function title(str) { - return Array(level).join('#') + ' ' + str; - } - - function indent() { - return Array(level).join(' '); - } - - function mapTOC(suite, obj) { - var ret = obj; - obj = obj[suite.title] = obj[suite.title] || { suite: suite }; - suite.suites.forEach(function(suite){ - mapTOC(suite, obj); - }); - return ret; - } - - function stringifyTOC(obj, level) { - ++level; - var buf = ''; - var link; - for (var key in obj) { - if ('suite' == key) continue; - if (key) link = ' - [' + key + '](#' + utils.slug(obj[key].suite.fullTitle()) + ')\n'; - if (key) buf += Array(level).join(' ') + link; - buf += stringifyTOC(obj[key], level); - } - --level; - return buf; - } - - function generateTOC(suite) { - var obj = mapTOC(suite, {}); - return stringifyTOC(obj, 0); - } - - generateTOC(runner.suite); - - runner.on('suite', function(suite){ - ++level; - var slug = utils.slug(suite.fullTitle()); - buf += '' + '\n'; - buf += title(suite.title) + '\n'; - }); - - runner.on('suite end', function(suite){ - --level; - }); - - runner.on('pass', function(test){ - var code = utils.clean(test.fn.toString()); - buf += test.title + '.\n'; - buf += '\n```js\n'; - buf += code + '\n'; - buf += '```\n\n'; - }); - - runner.on('end', function(){ - process.stdout.write('# TOC\n'); - process.stdout.write(generateTOC(runner.suite)); - process.stdout.write(buf); - }); -} \ No newline at end of file diff --git a/tests/mocha/lib/reporters/min.js b/tests/mocha/lib/reporters/min.js deleted file mode 100644 index 1b6117d065..0000000000 --- a/tests/mocha/lib/reporters/min.js +++ /dev/null @@ -1,38 +0,0 @@ - -/** - * Module dependencies. - */ - -var Base = require('./base'); - -/** - * Expose `Min`. - */ - -exports = module.exports = Min; - -/** - * Initialize a new `Min` minimal test reporter (best used with --watch). - * - * @param {Runner} runner - * @api public - */ - -function Min(runner) { - Base.call(this, runner); - - runner.on('start', function(){ - // clear screen - process.stdout.write('\u001b[2J'); - // set cursor position - process.stdout.write('\u001b[1;3H'); - }); - - runner.on('end', this.epilogue.bind(this)); -} - -/** - * Inherit from `Base.prototype`. - */ - -Min.prototype.__proto__ = Base.prototype; diff --git a/tests/mocha/lib/reporters/nyan.js b/tests/mocha/lib/reporters/nyan.js deleted file mode 100644 index 4501f6b264..0000000000 --- a/tests/mocha/lib/reporters/nyan.js +++ /dev/null @@ -1,260 +0,0 @@ -/** - * Module dependencies. - */ - -var Base = require('./base') - , color = Base.color; - -/** - * Expose `Dot`. - */ - -exports = module.exports = NyanCat; - -/** - * Initialize a new `Dot` matrix test reporter. - * - * @param {Runner} runner - * @api public - */ - -function NyanCat(runner) { - Base.call(this, runner); - var self = this - , stats = this.stats - , width = Base.window.width * .75 | 0 - , rainbowColors = this.rainbowColors = self.generateColors() - , colorIndex = this.colorIndex = 0 - , numerOfLines = this.numberOfLines = 4 - , trajectories = this.trajectories = [[], [], [], []] - , nyanCatWidth = this.nyanCatWidth = 11 - , trajectoryWidthMax = this.trajectoryWidthMax = (width - nyanCatWidth) - , scoreboardWidth = this.scoreboardWidth = 5 - , tick = this.tick = 0 - , n = 0; - - runner.on('start', function(){ - Base.cursor.hide(); - self.draw(); - }); - - runner.on('pending', function(test){ - self.draw(); - }); - - runner.on('pass', function(test){ - self.draw(); - }); - - runner.on('fail', function(test, err){ - self.draw(); - }); - - runner.on('end', function(){ - Base.cursor.show(); - for (var i = 0; i < self.numberOfLines; i++) write('\n'); - self.epilogue(); - }); -} - -/** - * Draw the nyan cat - * - * @api private - */ - -NyanCat.prototype.draw = function(){ - this.appendRainbow(); - this.drawScoreboard(); - this.drawRainbow(); - this.drawNyanCat(); - this.tick = !this.tick; -}; - -/** - * Draw the "scoreboard" showing the number - * of passes, failures and pending tests. - * - * @api private - */ - -NyanCat.prototype.drawScoreboard = function(){ - var stats = this.stats; - var colors = Base.colors; - - function draw(color, n) { - write(' '); - write('\u001b[' + color + 'm' + n + '\u001b[0m'); - write('\n'); - } - - draw(colors.green, stats.passes); - draw(colors.fail, stats.failures); - draw(colors.pending, stats.pending); - write('\n'); - - this.cursorUp(this.numberOfLines); -}; - -/** - * Append the rainbow. - * - * @api private - */ - -NyanCat.prototype.appendRainbow = function(){ - var segment = this.tick ? '_' : '-'; - var rainbowified = this.rainbowify(segment); - - for (var index = 0; index < this.numberOfLines; index++) { - var trajectory = this.trajectories[index]; - if (trajectory.length >= this.trajectoryWidthMax) trajectory.shift(); - trajectory.push(rainbowified); - } -}; - -/** - * Draw the rainbow. - * - * @api private - */ - -NyanCat.prototype.drawRainbow = function(){ - var self = this; - - this.trajectories.forEach(function(line, index) { - write('\u001b[' + self.scoreboardWidth + 'C'); - write(line.join('')); - write('\n'); - }); - - this.cursorUp(this.numberOfLines); -}; - -/** - * Draw the nyan cat - * - * @api private - */ - -NyanCat.prototype.drawNyanCat = function() { - var self = this; - var startWidth = this.scoreboardWidth + this.trajectories[0].length; - var color = '\u001b[' + startWidth + 'C'; - var padding = ''; - - write(color); - write('_,------,'); - write('\n'); - - write(color); - padding = self.tick ? ' ' : ' '; - write('_|' + padding + '/\\_/\\ '); - write('\n'); - - write(color); - padding = self.tick ? '_' : '__'; - var tail = self.tick ? '~' : '^'; - var face; - write(tail + '|' + padding + this.face() + ' '); - write('\n'); - - write(color); - padding = self.tick ? ' ' : ' '; - write(padding + '"" "" '); - write('\n'); - - this.cursorUp(this.numberOfLines); -}; - -/** - * Draw nyan cat face. - * - * @return {String} - * @api private - */ - -NyanCat.prototype.face = function() { - var stats = this.stats; - if (stats.failures) { - return '( x .x)'; - } else if (stats.pending) { - return '( o .o)'; - } else if(stats.passes) { - return '( ^ .^)'; - } else { - return '( - .-)'; - } -} - -/** - * Move cursor up `n`. - * - * @param {Number} n - * @api private - */ - -NyanCat.prototype.cursorUp = function(n) { - write('\u001b[' + n + 'A'); -}; - -/** - * Move cursor down `n`. - * - * @param {Number} n - * @api private - */ - -NyanCat.prototype.cursorDown = function(n) { - write('\u001b[' + n + 'B'); -}; - -/** - * Generate rainbow colors. - * - * @return {Array} - * @api private - */ - -NyanCat.prototype.generateColors = function(){ - var colors = []; - - for (var i = 0; i < (6 * 7); i++) { - var pi3 = Math.floor(Math.PI / 3); - var n = (i * (1.0 / 6)); - var r = Math.floor(3 * Math.sin(n) + 3); - var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3); - var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3); - colors.push(36 * r + 6 * g + b + 16); - } - - return colors; -}; - -/** - * Apply rainbow to the given `str`. - * - * @param {String} str - * @return {String} - * @api private - */ - -NyanCat.prototype.rainbowify = function(str){ - var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length]; - this.colorIndex += 1; - return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m'; -}; - -/** - * Stdout helper. - */ - -function write(string) { - process.stdout.write(string); -} - -/** - * Inherit from `Base.prototype`. - */ - -NyanCat.prototype.__proto__ = Base.prototype; diff --git a/tests/mocha/lib/reporters/progress.js b/tests/mocha/lib/reporters/progress.js deleted file mode 100644 index 59536386cb..0000000000 --- a/tests/mocha/lib/reporters/progress.js +++ /dev/null @@ -1,86 +0,0 @@ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , cursor = Base.cursor - , color = Base.color; - -/** - * Expose `Progress`. - */ - -exports = module.exports = Progress; - -/** - * General progress bar color. - */ - -Base.colors.progress = 90; - -/** - * Initialize a new `Progress` bar test reporter. - * - * @param {Runner} runner - * @param {Object} options - * @api public - */ - -function Progress(runner, options) { - Base.call(this, runner); - - var self = this - , options = options || {} - , stats = this.stats - , width = Base.window.width * .50 | 0 - , total = runner.total - , complete = 0 - , max = Math.max; - - // default chars - options.open = options.open || '['; - options.complete = options.complete || '▬'; - options.incomplete = options.incomplete || Base.symbols.dot; - options.close = options.close || ']'; - options.verbose = false; - - // tests started - runner.on('start', function(){ - console.log(); - cursor.hide(); - }); - - // tests complete - runner.on('test end', function(){ - complete++; - var incomplete = total - complete - , percent = complete / total - , n = width * percent | 0 - , i = width - n; - - cursor.CR(); - process.stdout.write('\u001b[J'); - process.stdout.write(color('progress', ' ' + options.open)); - process.stdout.write(Array(n).join(options.complete)); - process.stdout.write(Array(i).join(options.incomplete)); - process.stdout.write(color('progress', options.close)); - if (options.verbose) { - process.stdout.write(color('progress', ' ' + complete + ' of ' + total)); - } - }); - - // tests are complete, output some stats - // and the failures if any - runner.on('end', function(){ - cursor.show(); - console.log(); - self.epilogue(); - }); -} - -/** - * Inherit from `Base.prototype`. - */ - -Progress.prototype.__proto__ = Base.prototype; diff --git a/tests/mocha/lib/reporters/spec.js b/tests/mocha/lib/reporters/spec.js deleted file mode 100644 index ada25c3eae..0000000000 --- a/tests/mocha/lib/reporters/spec.js +++ /dev/null @@ -1,83 +0,0 @@ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , cursor = Base.cursor - , color = Base.color; - -/** - * Expose `Spec`. - */ - -exports = module.exports = Spec; - -/** - * Initialize a new `Spec` test reporter. - * - * @param {Runner} runner - * @api public - */ - -function Spec(runner) { - Base.call(this, runner); - - var self = this - , stats = this.stats - , indents = 0 - , n = 0; - - function indent() { - return Array(indents).join(' ') - } - - runner.on('start', function(){ - console.log(); - }); - - runner.on('suite', function(suite){ - ++indents; - console.log(color('suite', '%s%s'), indent(), suite.title); - }); - - runner.on('suite end', function(suite){ - --indents; - if (1 == indents) console.log(); - }); - - runner.on('pending', function(test){ - var fmt = indent() + color('pending', ' - %s'); - console.log(fmt, test.title); - }); - - runner.on('pass', function(test){ - if ('fast' == test.speed) { - var fmt = indent() - + color('checkmark', ' ' + Base.symbols.ok) - + color('pass', ' %s '); - cursor.CR(); - console.log(fmt, test.title); - } else { - var fmt = indent() - + color('checkmark', ' ' + Base.symbols.ok) - + color('pass', ' %s ') - + color(test.speed, '(%dms)'); - cursor.CR(); - console.log(fmt, test.title, test.duration); - } - }); - - runner.on('fail', function(test, err){ - cursor.CR(); - console.log(indent() + color('fail', ' %d) %s'), ++n, test.title); - }); - - runner.on('end', self.epilogue.bind(self)); -} - -/** - * Inherit from `Base.prototype`. - */ - -Spec.prototype.__proto__ = Base.prototype; diff --git a/tests/mocha/lib/reporters/tap.js b/tests/mocha/lib/reporters/tap.js deleted file mode 100644 index 2bcd995baa..0000000000 --- a/tests/mocha/lib/reporters/tap.js +++ /dev/null @@ -1,73 +0,0 @@ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , cursor = Base.cursor - , color = Base.color; - -/** - * Expose `TAP`. - */ - -exports = module.exports = TAP; - -/** - * Initialize a new `TAP` reporter. - * - * @param {Runner} runner - * @api public - */ - -function TAP(runner) { - Base.call(this, runner); - - var self = this - , stats = this.stats - , n = 1 - , passes = 0 - , failures = 0; - - runner.on('start', function(){ - var total = runner.grepTotal(runner.suite); - console.log('%d..%d', 1, total); - }); - - runner.on('test end', function(){ - ++n; - }); - - runner.on('pending', function(test){ - console.log('ok %d %s # SKIP -', n, title(test)); - }); - - runner.on('pass', function(test){ - passes++; - console.log('ok %d %s', n, title(test)); - }); - - runner.on('fail', function(test, err){ - failures++; - console.log('not ok %d %s', n, title(test)); - if (err.stack) console.log(err.stack.replace(/^/gm, ' ')); - }); - - runner.on('end', function(){ - console.log('# tests ' + (passes + failures)); - console.log('# pass ' + passes); - console.log('# fail ' + failures); - }); -} - -/** - * Return a TAP-safe title of `test` - * - * @param {Object} test - * @return {String} - * @api private - */ - -function title(test) { - return test.fullTitle().replace(/#/g, ''); -} diff --git a/tests/mocha/lib/reporters/templates/coverage.jade b/tests/mocha/lib/reporters/templates/coverage.jade deleted file mode 100644 index b78119fbc1..0000000000 --- a/tests/mocha/lib/reporters/templates/coverage.jade +++ /dev/null @@ -1,50 +0,0 @@ -!!! 5 -html - head - title Coverage - include script.html - include style.html - body - #coverage - h1#overview Coverage - include menu - - #stats(class=coverageClass(cov.coverage)) - .percentage #{cov.coverage | 0}% - .sloc= cov.sloc - .hits= cov.hits - .misses= cov.misses - - #files - for file in cov.files - .file - h2(id=file.filename)= file.filename - #stats(class=coverageClass(file.coverage)) - .percentage #{file.coverage | 0}% - .sloc= file.sloc - .hits= file.hits - .misses= file.misses - - table#source - thead - tr - th Line - th Hits - th Source - tbody - for line, number in file.source - if line.coverage > 0 - tr.hit - td.line= number - td.hits= line.coverage - td.source= line.source - else if 0 === line.coverage - tr.miss - td.line= number - td.hits 0 - td.source= line.source - else - tr - td.line= number - td.hits - td.source= line.source || ' ' diff --git a/tests/mocha/lib/reporters/templates/menu.jade b/tests/mocha/lib/reporters/templates/menu.jade deleted file mode 100644 index e9ba4648a6..0000000000 --- a/tests/mocha/lib/reporters/templates/menu.jade +++ /dev/null @@ -1,13 +0,0 @@ -#menu - li - a(href='#overview') overview - for file in cov.files - li - span.cov(class=coverageClass(file.coverage)) #{file.coverage | 0} - a(href='##{file.filename}') - segments = file.filename.split('/') - basename = segments.pop() - if segments.length - span.dirname= segments.join('/') + '/' - span.basename= basename - a#logo(href='http://visionmedia.github.io/mocha/') m diff --git a/tests/mocha/lib/reporters/templates/script.html b/tests/mocha/lib/reporters/templates/script.html deleted file mode 100644 index 073cf7939c..0000000000 --- a/tests/mocha/lib/reporters/templates/script.html +++ /dev/null @@ -1,34 +0,0 @@ - diff --git a/tests/mocha/lib/reporters/templates/style.html b/tests/mocha/lib/reporters/templates/style.html deleted file mode 100644 index 643c0ab7b8..0000000000 --- a/tests/mocha/lib/reporters/templates/style.html +++ /dev/null @@ -1,320 +0,0 @@ - \ No newline at end of file diff --git a/tests/mocha/lib/reporters/xunit.js b/tests/mocha/lib/reporters/xunit.js deleted file mode 100644 index e956380d8e..0000000000 --- a/tests/mocha/lib/reporters/xunit.js +++ /dev/null @@ -1,119 +0,0 @@ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , utils = require('../utils') - , escape = utils.escape; - -/** - * Save timer references to avoid Sinon interfering (see GH-237). - */ - -var Date = global.Date - , setTimeout = global.setTimeout - , setInterval = global.setInterval - , clearTimeout = global.clearTimeout - , clearInterval = global.clearInterval; - -/** - * Expose `XUnit`. - */ - -exports = module.exports = XUnit; - -/** - * Initialize a new `XUnit` reporter. - * - * @param {Runner} runner - * @api public - */ - -function XUnit(runner) { - Base.call(this, runner); - var stats = this.stats - , tests = [] - , self = this; - - runner.on('pending', function(test){ - tests.push(test); - }); - - runner.on('pass', function(test){ - tests.push(test); - }); - - runner.on('fail', function(test){ - tests.push(test); - }); - - runner.on('end', function(){ - console.log(tag('testsuite', { - name: 'Mocha Tests' - , tests: stats.tests - , failures: stats.failures - , errors: stats.failures - , skipped: stats.tests - stats.failures - stats.passes - , timestamp: (new Date).toUTCString() - , time: (stats.duration / 1000) || 0 - }, false)); - - tests.forEach(test); - console.log(''); - }); -} - -/** - * Inherit from `Base.prototype`. - */ - -XUnit.prototype.__proto__ = Base.prototype; - -/** - * Output tag for the given `test.` - */ - -function test(test) { - var attrs = { - classname: test.parent.fullTitle() - , name: test.title - , time: (test.duration / 1000) || 0 - }; - - if ('failed' == test.state) { - var err = test.err; - attrs.message = escape(err.message); - console.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack)))); - } else if (test.pending) { - console.log(tag('testcase', attrs, false, tag('skipped', {}, true))); - } else { - console.log(tag('testcase', attrs, true) ); - } -} - -/** - * HTML tag helper. - */ - -function tag(name, attrs, close, content) { - var end = close ? '/>' : '>' - , pairs = [] - , tag; - - for (var key in attrs) { - pairs.push(key + '="' + escape(attrs[key]) + '"'); - } - - tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end; - if (content) tag += content + ''; -} diff --git a/tests/mocha/lib/runnable.js b/tests/mocha/lib/runnable.js deleted file mode 100644 index 6940bb77d3..0000000000 --- a/tests/mocha/lib/runnable.js +++ /dev/null @@ -1,231 +0,0 @@ - -/** - * Module dependencies. - */ - -var EventEmitter = require('events').EventEmitter - , debug = require('debug')('mocha:runnable') - , milliseconds = require('./ms'); - -/** - * Save timer references to avoid Sinon interfering (see GH-237). - */ - -var Date = global.Date - , setTimeout = global.setTimeout - , setInterval = global.setInterval - , clearTimeout = global.clearTimeout - , clearInterval = global.clearInterval; - -/** - * Object#toString(). - */ - -var toString = Object.prototype.toString; - -/** - * Expose `Runnable`. - */ - -module.exports = Runnable; - -/** - * Initialize a new `Runnable` with the given `title` and callback `fn`. - * - * @param {String} title - * @param {Function} fn - * @api private - */ - -function Runnable(title, fn) { - this.title = title; - this.fn = fn; - this.async = fn && fn.length; - this.sync = ! this.async; - this._timeout = 2000; - this._slow = 75; - this.timedOut = false; -} - -/** - * Inherit from `EventEmitter.prototype`. - */ - -Runnable.prototype.__proto__ = EventEmitter.prototype; - -/** - * Set & get timeout `ms`. - * - * @param {Number|String} ms - * @return {Runnable|Number} ms or self - * @api private - */ - -Runnable.prototype.timeout = function(ms){ - if (0 == arguments.length) return this._timeout; - if ('string' == typeof ms) ms = milliseconds(ms); - debug('timeout %d', ms); - this._timeout = ms; - if (this.timer) this.resetTimeout(); - return this; -}; - -/** - * Set & get slow `ms`. - * - * @param {Number|String} ms - * @return {Runnable|Number} ms or self - * @api private - */ - -Runnable.prototype.slow = function(ms){ - if (0 === arguments.length) return this._slow; - if ('string' == typeof ms) ms = milliseconds(ms); - debug('timeout %d', ms); - this._slow = ms; - return this; -}; - -/** - * Return the full title generated by recursively - * concatenating the parent's full title. - * - * @return {String} - * @api public - */ - -Runnable.prototype.fullTitle = function(){ - return this.parent.fullTitle() + ' ' + this.title; -}; - -/** - * Clear the timeout. - * - * @api private - */ - -Runnable.prototype.clearTimeout = function(){ - clearTimeout(this.timer); -}; - -/** - * Inspect the runnable void of private properties. - * - * @return {String} - * @api private - */ - -Runnable.prototype.inspect = function(){ - return JSON.stringify(this, function(key, val){ - if ('_' == key[0]) return; - if ('parent' == key) return '#'; - if ('ctx' == key) return '#'; - return val; - }, 2); -}; - -/** - * Reset the timeout. - * - * @api private - */ - -Runnable.prototype.resetTimeout = function(){ - var self = this; - var ms = this.timeout() || 1e9; - - this.clearTimeout(); - this.timer = setTimeout(function(){ - self.callback(new Error('timeout of ' + ms + 'ms exceeded')); - self.timedOut = true; - }, ms); -}; - -/** - * Whitelist these globals for this test run - * - * @api private - */ -Runnable.prototype.globals = function(arr){ - var self = this; - this._allowedGlobals = arr; -}; - -/** - * Run the test and invoke `fn(err)`. - * - * @param {Function} fn - * @api private - */ - -Runnable.prototype.run = function(fn){ - var self = this - , ms = this.timeout() - , start = new Date - , ctx = this.ctx - , finished - , emitted; - - if (ctx) ctx.runnable(this); - - // called multiple times - function multiple(err) { - if (emitted) return; - emitted = true; - self.emit('error', err || new Error('done() called multiple times')); - } - - // finished - function done(err) { - if (self.timedOut) return; - if (finished) return multiple(err); - self.clearTimeout(); - self.duration = new Date - start; - finished = true; - fn(err); - } - - // for .resetTimeout() - this.callback = done; - - // explicit async with `done` argument - if (this.async) { - this.resetTimeout(); - - try { - this.fn.call(ctx, function(err){ - if (err instanceof Error || toString.call(err) === "[object Error]") return done(err); - if (null != err) return done(new Error('done() invoked with non-Error: ' + err)); - done(); - }); - } catch (err) { - done(err); - } - return; - } - - if (this.asyncOnly) { - return done(new Error('--async-only option in use without declaring `done()`')); - } - - // sync or promise-returning - try { - if (this.pending) { - done(); - } else { - callFn(this.fn); - } - } catch (err) { - done(err); - } - - function callFn(fn) { - var result = fn.call(ctx); - if (result && typeof result.then === 'function') { - self.resetTimeout(); - result.then(function(){ done() }, done); - } else { - done(); - } - } -}; diff --git a/tests/mocha/lib/runner.js b/tests/mocha/lib/runner.js deleted file mode 100644 index deb442e145..0000000000 --- a/tests/mocha/lib/runner.js +++ /dev/null @@ -1,661 +0,0 @@ -/** - * Module dependencies. - */ - -var EventEmitter = require('events').EventEmitter - , debug = require('debug')('mocha:runner') - , Test = require('./test') - , utils = require('./utils') - , filter = utils.filter - , keys = utils.keys; - -/** - * Non-enumerable globals. - */ - -var globals = [ - 'setTimeout', - 'clearTimeout', - 'setInterval', - 'clearInterval', - 'XMLHttpRequest', - 'Date' -]; - -/** - * Expose `Runner`. - */ - -module.exports = Runner; - -/** - * Initialize a `Runner` for the given `suite`. - * - * Events: - * - * - `start` execution started - * - `end` execution complete - * - `suite` (suite) test suite execution started - * - `suite end` (suite) all tests (and sub-suites) have finished - * - `test` (test) test execution started - * - `test end` (test) test completed - * - `hook` (hook) hook execution started - * - `hook end` (hook) hook complete - * - `pass` (test) test passed - * - `fail` (test, err) test failed - * - `pending` (test) test pending - * - * @api public - */ - -function Runner(suite) { - var self = this; - this._globals = []; - this._abort = false; - this.suite = suite; - this.total = suite.total(); - this.failures = 0; - this.on('test end', function(test){ self.checkGlobals(test); }); - this.on('hook end', function(hook){ self.checkGlobals(hook); }); - this.grep(/.*/); - this.globals(this.globalProps().concat(extraGlobals())); -} - -/** - * Wrapper for setImmediate, process.nextTick, or browser polyfill. - * - * @param {Function} fn - * @api private - */ - -Runner.immediately = global.setImmediate || process.nextTick; - -/** - * Inherit from `EventEmitter.prototype`. - */ - -Runner.prototype.__proto__ = EventEmitter.prototype; - -/** - * Run tests with full titles matching `re`. Updates runner.total - * with number of tests matched. - * - * @param {RegExp} re - * @param {Boolean} invert - * @return {Runner} for chaining - * @api public - */ - -Runner.prototype.grep = function(re, invert){ - debug('grep %s', re); - this._grep = re; - this._invert = invert; - this.total = this.grepTotal(this.suite); - return this; -}; - -/** - * Returns the number of tests matching the grep search for the - * given suite. - * - * @param {Suite} suite - * @return {Number} - * @api public - */ - -Runner.prototype.grepTotal = function(suite) { - var self = this; - var total = 0; - - suite.eachTest(function(test){ - var match = self._grep.test(test.fullTitle()); - if (self._invert) match = !match; - if (match) total++; - }); - - return total; -}; - -/** - * Return a list of global properties. - * - * @return {Array} - * @api private - */ - -Runner.prototype.globalProps = function() { - var props = utils.keys(global); - - // non-enumerables - for (var i = 0; i < globals.length; ++i) { - if (~utils.indexOf(props, globals[i])) continue; - props.push(globals[i]); - } - - return props; -}; - -/** - * Allow the given `arr` of globals. - * - * @param {Array} arr - * @return {Runner} for chaining - * @api public - */ - -Runner.prototype.globals = function(arr){ - if (0 == arguments.length) return this._globals; - debug('globals %j', arr); - this._globals = this._globals.concat(arr); - return this; -}; - -/** - * Check for global variable leaks. - * - * @api private - */ - -Runner.prototype.checkGlobals = function(test){ - if (this.ignoreLeaks) return; - var ok = this._globals; - - var globals = this.globalProps(); - var isNode = process.kill; - var leaks; - - if (test) { - ok = ok.concat(test._allowedGlobals || []); - } - - if(this.prevGlobalsLength == globals.length) return; - this.prevGlobalsLength = globals.length; - - leaks = filterLeaks(ok, globals); - this._globals = this._globals.concat(leaks); - - if (leaks.length > 1) { - this.fail(test, new Error('global leaks detected: ' + leaks.join(', ') + '')); - } else if (leaks.length) { - this.fail(test, new Error('global leak detected: ' + leaks[0])); - } -}; - -/** - * Fail the given `test`. - * - * @param {Test} test - * @param {Error} err - * @api private - */ - -Runner.prototype.fail = function(test, err){ - ++this.failures; - test.state = 'failed'; - - if ('string' == typeof err) { - err = new Error('the string "' + err + '" was thrown, throw an Error :)'); - } - - this.emit('fail', test, err); -}; - -/** - * Fail the given `hook` with `err`. - * - * Hook failures work in the following pattern: - * - If bail, then exit - * - Failed `before` hook skips all tests in a suite and subsuites, - * but jumps to corresponding `after` hook - * - Failed `before each` hook skips remaining tests in a - * suite and jumps to corresponding `after each` hook, - * which is run only once - * - Failed `after` hook does not alter - * execution order - * - Failed `after each` hook skips remaining tests in a - * suite and subsuites, but executes other `after each` - * hooks - * - * @param {Hook} hook - * @param {Error} err - * @api private - */ - -Runner.prototype.failHook = function(hook, err){ - this.fail(hook, err); - if (this.suite.bail()) { - this.emit('end'); - } -}; - -/** - * Run hook `name` callbacks and then invoke `fn()`. - * - * @param {String} name - * @param {Function} function - * @api private - */ - -Runner.prototype.hook = function(name, fn){ - var suite = this.suite - , hooks = suite['_' + name] - , self = this - , timer; - - function next(i) { - var hook = hooks[i]; - if (!hook) return fn(); - if (self.failures && suite.bail()) return fn(); - self.currentRunnable = hook; - - hook.ctx.currentTest = self.test; - - self.emit('hook', hook); - - hook.on('error', function(err){ - self.failHook(hook, err); - }); - - hook.run(function(err){ - hook.removeAllListeners('error'); - var testError = hook.error(); - if (testError) self.fail(self.test, testError); - if (err) { - self.failHook(hook, err); - - // stop executing hooks, notify callee of hook err - return fn(err); - } - self.emit('hook end', hook); - delete hook.ctx.currentTest; - next(++i); - }); - } - - Runner.immediately(function(){ - next(0); - }); -}; - -/** - * Run hook `name` for the given array of `suites` - * in order, and callback `fn(err, errSuite)`. - * - * @param {String} name - * @param {Array} suites - * @param {Function} fn - * @api private - */ - -Runner.prototype.hooks = function(name, suites, fn){ - var self = this - , orig = this.suite; - - function next(suite) { - self.suite = suite; - - if (!suite) { - self.suite = orig; - return fn(); - } - - self.hook(name, function(err){ - if (err) { - var errSuite = self.suite; - self.suite = orig; - return fn(err, errSuite); - } - - next(suites.pop()); - }); - } - - next(suites.pop()); -}; - -/** - * Run hooks from the top level down. - * - * @param {String} name - * @param {Function} fn - * @api private - */ - -Runner.prototype.hookUp = function(name, fn){ - var suites = [this.suite].concat(this.parents()).reverse(); - this.hooks(name, suites, fn); -}; - -/** - * Run hooks from the bottom up. - * - * @param {String} name - * @param {Function} fn - * @api private - */ - -Runner.prototype.hookDown = function(name, fn){ - var suites = [this.suite].concat(this.parents()); - this.hooks(name, suites, fn); -}; - -/** - * Return an array of parent Suites from - * closest to furthest. - * - * @return {Array} - * @api private - */ - -Runner.prototype.parents = function(){ - var suite = this.suite - , suites = []; - while (suite = suite.parent) suites.push(suite); - return suites; -}; - -/** - * Run the current test and callback `fn(err)`. - * - * @param {Function} fn - * @api private - */ - -Runner.prototype.runTest = function(fn){ - var test = this.test - , self = this; - - if (this.asyncOnly) test.asyncOnly = true; - - try { - test.on('error', function(err){ - self.fail(test, err); - }); - test.run(fn); - } catch (err) { - fn(err); - } -}; - -/** - * Run tests in the given `suite` and invoke - * the callback `fn()` when complete. - * - * @param {Suite} suite - * @param {Function} fn - * @api private - */ - -Runner.prototype.runTests = function(suite, fn){ - var self = this - , tests = suite.tests.slice() - , test; - - - function hookErr(err, errSuite, after) { - // before/after Each hook for errSuite failed: - var orig = self.suite; - - // for failed 'after each' hook start from errSuite parent, - // otherwise start from errSuite itself - self.suite = after ? errSuite.parent : errSuite; - - if (self.suite) { - // call hookUp afterEach - self.hookUp('afterEach', function(err2, errSuite2) { - self.suite = orig; - // some hooks may fail even now - if (err2) return hookErr(err2, errSuite2, true); - // report error suite - fn(errSuite); - }); - } else { - // there is no need calling other 'after each' hooks - self.suite = orig; - fn(errSuite); - } - } - - function next(err, errSuite) { - // if we bail after first err - if (self.failures && suite._bail) return fn(); - - if (self._abort) return fn(); - - if (err) return hookErr(err, errSuite, true); - - // next test - test = tests.shift(); - - // all done - if (!test) return fn(); - - // grep - var match = self._grep.test(test.fullTitle()); - if (self._invert) match = !match; - if (!match) return next(); - - // pending - if (test.pending) { - self.emit('pending', test); - self.emit('test end', test); - return next(); - } - - // execute test and hook(s) - self.emit('test', self.test = test); - self.hookDown('beforeEach', function(err, errSuite){ - - if (err) return hookErr(err, errSuite, false); - - self.currentRunnable = self.test; - self.runTest(function(err){ - test = self.test; - - if (err) { - self.fail(test, err); - self.emit('test end', test); - return self.hookUp('afterEach', next); - } - - test.state = 'passed'; - self.emit('pass', test); - self.emit('test end', test); - self.hookUp('afterEach', next); - }); - }); - } - - this.next = next; - next(); -}; - -/** - * Run the given `suite` and invoke the - * callback `fn()` when complete. - * - * @param {Suite} suite - * @param {Function} fn - * @api private - */ - -Runner.prototype.runSuite = function(suite, fn){ - var total = this.grepTotal(suite) - , self = this - , i = 0; - - debug('run suite %s', suite.fullTitle()); - - if (!total) return fn(); - - this.emit('suite', this.suite = suite); - - function next(errSuite) { - if (errSuite) { - // current suite failed on a hook from errSuite - if (errSuite == suite) { - // if errSuite is current suite - // continue to the next sibling suite - return done(); - } else { - // errSuite is among the parents of current suite - // stop execution of errSuite and all sub-suites - return done(errSuite); - } - } - - if (self._abort) return done(); - - var curr = suite.suites[i++]; - if (!curr) return done(); - self.runSuite(curr, next); - } - - function done(errSuite) { - self.suite = suite; - self.hook('afterAll', function(){ - self.emit('suite end', suite); - fn(errSuite); - }); - } - - this.hook('beforeAll', function(err){ - if (err) return done(); - self.runTests(suite, next); - }); -}; - -/** - * Handle uncaught exceptions. - * - * @param {Error} err - * @api private - */ - -Runner.prototype.uncaught = function(err){ - debug('uncaught exception %s', err.message); - var runnable = this.currentRunnable; - if (!runnable || 'failed' == runnable.state) return; - runnable.clearTimeout(); - err.uncaught = true; - this.fail(runnable, err); - - // recover from test - if ('test' == runnable.type) { - this.emit('test end', runnable); - this.hookUp('afterEach', this.next); - return; - } - - // bail on hooks - this.emit('end'); -}; - -/** - * Run the root suite and invoke `fn(failures)` - * on completion. - * - * @param {Function} fn - * @return {Runner} for chaining - * @api public - */ - -Runner.prototype.run = function(fn){ - var self = this - , fn = fn || function(){}; - - function uncaught(err){ - self.uncaught(err); - } - - debug('start'); - - // callback - this.on('end', function(){ - debug('end'); - process.removeListener('uncaughtException', uncaught); - fn(self.failures); - }); - - // run suites - this.emit('start'); - this.runSuite(this.suite, function(){ - debug('finished running'); - self.emit('end'); - }); - - // uncaught exception - process.on('uncaughtException', uncaught); - - return this; -}; - -/** - * Cleanly abort execution - * - * @return {Runner} for chaining - * @api public - */ -Runner.prototype.abort = function(){ - debug('aborting'); - this._abort = true; -} - -/** - * Filter leaks with the given globals flagged as `ok`. - * - * @param {Array} ok - * @param {Array} globals - * @return {Array} - * @api private - */ - -function filterLeaks(ok, globals) { - return filter(globals, function(key){ - // Firefox and Chrome exposes iframes as index inside the window object - if (/^d+/.test(key)) return false; - - // in firefox - // if runner runs in an iframe, this iframe's window.getInterface method not init at first - // it is assigned in some seconds - if (global.navigator && /^getInterface/.test(key)) return false; - - // an iframe could be approached by window[iframeIndex] - // in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak - if (global.navigator && /^\d+/.test(key)) return false; - - // Opera and IE expose global variables for HTML element IDs (issue #243) - if (/^mocha-/.test(key)) return false; - - var matched = filter(ok, function(ok){ - if (~ok.indexOf('*')) return 0 == key.indexOf(ok.split('*')[0]); - return key == ok; - }); - return matched.length == 0 && (!global.navigator || 'onerror' !== key); - }); -} - -/** - * Array of globals dependent on the environment. - * - * @return {Array} - * @api private - */ - - function extraGlobals() { - if (typeof(process) === 'object' && - typeof(process.version) === 'string') { - - var nodeVersion = process.version.split('.').reduce(function(a, v) { - return a << 8 | v; - }); - - // 'errno' was renamed to process._errno in v0.9.11. - - if (nodeVersion < 0x00090B) { - return ['errno']; - } - } - - return []; - } diff --git a/tests/mocha/lib/suite.js b/tests/mocha/lib/suite.js deleted file mode 100644 index d403a83f38..0000000000 --- a/tests/mocha/lib/suite.js +++ /dev/null @@ -1,320 +0,0 @@ - -/** - * Module dependencies. - */ - -var EventEmitter = require('events').EventEmitter - , debug = require('debug')('mocha:suite') - , milliseconds = require('./ms') - , utils = require('./utils') - , Hook = require('./hook'); - -/** - * Expose `Suite`. - */ - -exports = module.exports = Suite; - -/** - * Create a new `Suite` with the given `title` - * and parent `Suite`. When a suite with the - * same title is already present, that suite - * is returned to provide nicer reporter - * and more flexible meta-testing. - * - * @param {Suite} parent - * @param {String} title - * @return {Suite} - * @api public - */ - -exports.create = function(parent, title){ - var suite = new Suite(title, parent.ctx); - suite.parent = parent; - if (parent.pending) suite.pending = true; - title = suite.fullTitle(); - parent.addSuite(suite); - return suite; -}; - -/** - * Initialize a new `Suite` with the given - * `title` and `ctx`. - * - * @param {String} title - * @param {Context} ctx - * @api private - */ - -function Suite(title, ctx) { - this.title = title; - this.ctx = ctx; - this.suites = []; - this.tests = []; - this.pending = false; - this._beforeEach = []; - this._beforeAll = []; - this._afterEach = []; - this._afterAll = []; - this.root = !title; - this._timeout = 2000; - this._slow = 75; - this._bail = false; -} - -/** - * Inherit from `EventEmitter.prototype`. - */ - -Suite.prototype.__proto__ = EventEmitter.prototype; - -/** - * Return a clone of this `Suite`. - * - * @return {Suite} - * @api private - */ - -Suite.prototype.clone = function(){ - var suite = new Suite(this.title); - debug('clone'); - suite.ctx = this.ctx; - suite.timeout(this.timeout()); - suite.slow(this.slow()); - suite.bail(this.bail()); - return suite; -}; - -/** - * Set timeout `ms` or short-hand such as "2s". - * - * @param {Number|String} ms - * @return {Suite|Number} for chaining - * @api private - */ - -Suite.prototype.timeout = function(ms){ - if (0 == arguments.length) return this._timeout; - if ('string' == typeof ms) ms = milliseconds(ms); - debug('timeout %d', ms); - this._timeout = parseInt(ms, 10); - return this; -}; - -/** - * Set slow `ms` or short-hand such as "2s". - * - * @param {Number|String} ms - * @return {Suite|Number} for chaining - * @api private - */ - -Suite.prototype.slow = function(ms){ - if (0 === arguments.length) return this._slow; - if ('string' == typeof ms) ms = milliseconds(ms); - debug('slow %d', ms); - this._slow = ms; - return this; -}; - -/** - * Sets whether to bail after first error. - * - * @parma {Boolean} bail - * @return {Suite|Number} for chaining - * @api private - */ - -Suite.prototype.bail = function(bail){ - if (0 == arguments.length) return this._bail; - debug('bail %s', bail); - this._bail = bail; - return this; -}; - -/** - * Run `fn(test[, done])` before running tests. - * - * @param {Function} fn - * @return {Suite} for chaining - * @api private - */ - -Suite.prototype.beforeAll = function(title, fn){ - if (this.pending) return this; - if ('function' === typeof title) { - fn = title; - title = fn.name; - } - title = '"before all" hook' + (title ? ': ' + title : ''); - - var hook = new Hook(title, fn); - hook.parent = this; - hook.timeout(this.timeout()); - hook.slow(this.slow()); - hook.ctx = this.ctx; - this._beforeAll.push(hook); - this.emit('beforeAll', hook); - return this; -}; - -/** - * Run `fn(test[, done])` after running tests. - * - * @param {Function} fn - * @return {Suite} for chaining - * @api private - */ - -Suite.prototype.afterAll = function(title, fn){ - if (this.pending) return this; - if ('function' === typeof title) { - fn = title; - title = fn.name; - } - title = '"after all" hook' + (title ? ': ' + title : ''); - - var hook = new Hook(title, fn); - hook.parent = this; - hook.timeout(this.timeout()); - hook.slow(this.slow()); - hook.ctx = this.ctx; - this._afterAll.push(hook); - this.emit('afterAll', hook); - return this; -}; - -/** - * Run `fn(test[, done])` before each test case. - * - * @param {Function} fn - * @return {Suite} for chaining - * @api private - */ - -Suite.prototype.beforeEach = function(title, fn){ - if (this.pending) return this; - if ('function' === typeof title) { - fn = title; - title = fn.name; - } - title = '"before each" hook' + (title ? ': ' + title : ''); - - var hook = new Hook(title, fn); - hook.parent = this; - hook.timeout(this.timeout()); - hook.slow(this.slow()); - hook.ctx = this.ctx; - this._beforeEach.push(hook); - this.emit('beforeEach', hook); - return this; -}; - -/** - * Run `fn(test[, done])` after each test case. - * - * @param {Function} fn - * @return {Suite} for chaining - * @api private - */ - -Suite.prototype.afterEach = function(title, fn){ - if (this.pending) return this; - if ('function' === typeof title) { - fn = title; - title = fn.name; - } - title = '"after each" hook' + (title ? ': ' + title : ''); - - var hook = new Hook(title, fn); - hook.parent = this; - hook.timeout(this.timeout()); - hook.slow(this.slow()); - hook.ctx = this.ctx; - this._afterEach.push(hook); - this.emit('afterEach', hook); - return this; -}; - -/** - * Add a test `suite`. - * - * @param {Suite} suite - * @return {Suite} for chaining - * @api private - */ - -Suite.prototype.addSuite = function(suite){ - suite.parent = this; - suite.timeout(this.timeout()); - suite.slow(this.slow()); - suite.bail(this.bail()); - this.suites.push(suite); - this.emit('suite', suite); - return this; -}; - -/** - * Add a `test` to this suite. - * - * @param {Test} test - * @return {Suite} for chaining - * @api private - */ - -Suite.prototype.addTest = function(test){ - test.parent = this; - test.timeout(this.timeout()); - test.slow(this.slow()); - test.ctx = this.ctx; - this.tests.push(test); - this.emit('test', test); - return this; -}; - -/** - * Return the full title generated by recursively - * concatenating the parent's full title. - * - * @return {String} - * @api public - */ - -Suite.prototype.fullTitle = function(){ - if (this.parent) { - var full = this.parent.fullTitle(); - if (full) return full + ' ' + this.title; - } - return this.title; -}; - -/** - * Return the total number of tests. - * - * @return {Number} - * @api public - */ - -Suite.prototype.total = function(){ - return utils.reduce(this.suites, function(sum, suite){ - return sum + suite.total(); - }, 0) + this.tests.length; -}; - -/** - * Iterates through each suite recursively to find - * all tests. Applies a function in the format - * `fn(test)`. - * - * @param {Function} fn - * @return {Suite} - * @api private - */ - -Suite.prototype.eachTest = function(fn){ - utils.forEach(this.tests, fn); - utils.forEach(this.suites, function(suite){ - suite.eachTest(fn); - }); - return this; -}; diff --git a/tests/mocha/lib/template.html b/tests/mocha/lib/template.html deleted file mode 100644 index 0590d4aac2..0000000000 --- a/tests/mocha/lib/template.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - Mocha - - - - - -
      - - - - - - diff --git a/tests/mocha/lib/test.js b/tests/mocha/lib/test.js deleted file mode 100644 index 11773e0cc9..0000000000 --- a/tests/mocha/lib/test.js +++ /dev/null @@ -1,32 +0,0 @@ - -/** - * Module dependencies. - */ - -var Runnable = require('./runnable'); - -/** - * Expose `Test`. - */ - -module.exports = Test; - -/** - * Initialize a new `Test` with the given `title` and callback `fn`. - * - * @param {String} title - * @param {Function} fn - * @api private - */ - -function Test(title, fn) { - Runnable.call(this, title, fn); - this.pending = !fn; - this.type = 'test'; -} - -/** - * Inherit from `Runnable.prototype`. - */ - -Test.prototype.__proto__ = Runnable.prototype; diff --git a/tests/mocha/lib/utils.js b/tests/mocha/lib/utils.js deleted file mode 100644 index 37fd5d7e1b..0000000000 --- a/tests/mocha/lib/utils.js +++ /dev/null @@ -1,299 +0,0 @@ -/** - * Module dependencies. - */ - -var fs = require('fs') - , path = require('path') - , join = path.join - , debug = require('debug')('mocha:watch'); - -/** - * Ignored directories. - */ - -var ignore = ['node_modules', '.git']; - -/** - * Escape special characters in the given string of html. - * - * @param {String} html - * @return {String} - * @api private - */ - -exports.escape = function(html){ - return String(html) - .replace(/&/g, '&') - .replace(/"/g, '"') - .replace(//g, '>'); -}; - -/** - * Array#forEach (<=IE8) - * - * @param {Array} array - * @param {Function} fn - * @param {Object} scope - * @api private - */ - -exports.forEach = function(arr, fn, scope){ - for (var i = 0, l = arr.length; i < l; i++) - fn.call(scope, arr[i], i); -}; - -/** - * Array#map (<=IE8) - * - * @param {Array} array - * @param {Function} fn - * @param {Object} scope - * @api private - */ - -exports.map = function(arr, fn, scope){ - var result = []; - for (var i = 0, l = arr.length; i < l; i++) - result.push(fn.call(scope, arr[i], i)); - return result; -}; - -/** - * Array#indexOf (<=IE8) - * - * @parma {Array} arr - * @param {Object} obj to find index of - * @param {Number} start - * @api private - */ - -exports.indexOf = function(arr, obj, start){ - for (var i = start || 0, l = arr.length; i < l; i++) { - if (arr[i] === obj) - return i; - } - return -1; -}; - -/** - * Array#reduce (<=IE8) - * - * @param {Array} array - * @param {Function} fn - * @param {Object} initial value - * @api private - */ - -exports.reduce = function(arr, fn, val){ - var rval = val; - - for (var i = 0, l = arr.length; i < l; i++) { - rval = fn(rval, arr[i], i, arr); - } - - return rval; -}; - -/** - * Array#filter (<=IE8) - * - * @param {Array} array - * @param {Function} fn - * @api private - */ - -exports.filter = function(arr, fn){ - var ret = []; - - for (var i = 0, l = arr.length; i < l; i++) { - var val = arr[i]; - if (fn(val, i, arr)) ret.push(val); - } - - return ret; -}; - -/** - * Object.keys (<=IE8) - * - * @param {Object} obj - * @return {Array} keys - * @api private - */ - -exports.keys = Object.keys || function(obj) { - var keys = [] - , has = Object.prototype.hasOwnProperty // for `window` on <=IE8 - - for (var key in obj) { - if (has.call(obj, key)) { - keys.push(key); - } - } - - return keys; -}; - -/** - * Watch the given `files` for changes - * and invoke `fn(file)` on modification. - * - * @param {Array} files - * @param {Function} fn - * @api private - */ - -exports.watch = function(files, fn){ - var options = { interval: 100 }; - files.forEach(function(file){ - debug('file %s', file); - fs.watchFile(file, options, function(curr, prev){ - if (prev.mtime < curr.mtime) fn(file); - }); - }); -}; - -/** - * Ignored files. - */ - -function ignored(path){ - return !~ignore.indexOf(path); -} - -/** - * Lookup files in the given `dir`. - * - * @return {Array} - * @api private - */ - -exports.files = function(dir, ret){ - ret = ret || []; - - fs.readdirSync(dir) - .filter(ignored) - .forEach(function(path){ - path = join(dir, path); - if (fs.statSync(path).isDirectory()) { - exports.files(path, ret); - } else if (path.match(/\.(js|coffee|litcoffee|coffee.md)$/)) { - ret.push(path); - } - }); - - return ret; -}; - -/** - * Compute a slug from the given `str`. - * - * @param {String} str - * @return {String} - * @api private - */ - -exports.slug = function(str){ - return str - .toLowerCase() - .replace(/ +/g, '-') - .replace(/[^-\w]/g, ''); -}; - -/** - * Strip the function definition from `str`, - * and re-indent for pre whitespace. - */ - -exports.clean = function(str) { - str = str - .replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, '') - .replace(/^function *\(.*\) *{/, '') - .replace(/\s+\}$/, ''); - - var spaces = str.match(/^\n?( *)/)[1].length - , tabs = str.match(/^\n?(\t*)/)[1].length - , re = new RegExp('^\n?' + (tabs ? '\t' : ' ') + '{' + (tabs ? tabs : spaces) + '}', 'gm'); - - str = str.replace(re, ''); - - return exports.trim(str); -}; - -/** - * Escape regular expression characters in `str`. - * - * @param {String} str - * @return {String} - * @api private - */ - -exports.escapeRegexp = function(str){ - return str.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&"); -}; - -/** - * Trim the given `str`. - * - * @param {String} str - * @return {String} - * @api private - */ - -exports.trim = function(str){ - return str.replace(/^\s+|\s+$/g, ''); -}; - -/** - * Parse the given `qs`. - * - * @param {String} qs - * @return {Object} - * @api private - */ - -exports.parseQuery = function(qs){ - return exports.reduce(qs.replace('?', '').split('&'), function(obj, pair){ - var i = pair.indexOf('=') - , key = pair.slice(0, i) - , val = pair.slice(++i); - - obj[key] = decodeURIComponent(val); - return obj; - }, {}); -}; - -/** - * Highlight the given string of `js`. - * - * @param {String} js - * @return {String} - * @api private - */ - -function highlight(js) { - return js - .replace(//g, '>') - .replace(/\/\/(.*)/gm, '//$1') - .replace(/('.*?')/gm, '$1') - .replace(/(\d+\.\d+)/gm, '$1') - .replace(/(\d+)/gm, '$1') - .replace(/\bnew *(\w+)/gm, 'new $1') - .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '$1') -} - -/** - * Highlight the contents of tag `name`. - * - * @param {String} name - * @api private - */ - -exports.highlightTags = function(name) { - var code = document.getElementsByTagName(name); - for (var i = 0, len = code.length; i < len; ++i) { - code[i].innerHTML = highlight(code[i].innerHTML); - } -}; diff --git a/tests/mocha/node_modules/commander/History.md b/tests/mocha/node_modules/commander/History.md deleted file mode 100644 index 2e665828cf..0000000000 --- a/tests/mocha/node_modules/commander/History.md +++ /dev/null @@ -1,179 +0,0 @@ - -2.0.0 / 2013-07-18 -================== - - * remove input methods (.prompt, .confirm, etc) - -1.3.2 / 2013-07-18 -================== - - * add support for sub-commands to co-exist with the original command - -1.3.1 / 2013-07-18 -================== - - * add quick .runningCommand hack so you can opt-out of other logic when running a sub command - -1.3.0 / 2013-07-09 -================== - - * add EACCES error handling - * fix sub-command --help - -1.2.0 / 2013-06-13 -================== - - * allow "-" hyphen as an option argument - * support for RegExp coercion - -1.1.1 / 2012-11-20 -================== - - * add more sub-command padding - * fix .usage() when args are present. Closes #106 - -1.1.0 / 2012-11-16 -================== - - * add git-style executable subcommand support. Closes #94 - -1.0.5 / 2012-10-09 -================== - - * fix `--name` clobbering. Closes #92 - * fix examples/help. Closes #89 - -1.0.4 / 2012-09-03 -================== - - * add `outputHelp()` method. - -1.0.3 / 2012-08-30 -================== - - * remove invalid .version() defaulting - -1.0.2 / 2012-08-24 -================== - - * add `--foo=bar` support [arv] - * fix password on node 0.8.8. Make backward compatible with 0.6 [focusaurus] - -1.0.1 / 2012-08-03 -================== - - * fix issue #56 - * fix tty.setRawMode(mode) was moved to tty.ReadStream#setRawMode() (i.e. process.stdin.setRawMode()) - -1.0.0 / 2012-07-05 -================== - - * add support for optional option descriptions - * add defaulting of `.version()` to package.json's version - -0.6.1 / 2012-06-01 -================== - - * Added: append (yes or no) on confirmation - * Added: allow node.js v0.7.x - -0.6.0 / 2012-04-10 -================== - - * Added `.prompt(obj, callback)` support. Closes #49 - * Added default support to .choose(). Closes #41 - * Fixed the choice example - -0.5.1 / 2011-12-20 -================== - - * Fixed `password()` for recent nodes. Closes #36 - -0.5.0 / 2011-12-04 -================== - - * Added sub-command option support [itay] - -0.4.3 / 2011-12-04 -================== - - * Fixed custom help ordering. Closes #32 - -0.4.2 / 2011-11-24 -================== - - * Added travis support - * Fixed: line-buffered input automatically trimmed. Closes #31 - -0.4.1 / 2011-11-18 -================== - - * Removed listening for "close" on --help - -0.4.0 / 2011-11-15 -================== - - * Added support for `--`. Closes #24 - -0.3.3 / 2011-11-14 -================== - - * Fixed: wait for close event when writing help info [Jerry Hamlet] - -0.3.2 / 2011-11-01 -================== - - * Fixed long flag definitions with values [felixge] - -0.3.1 / 2011-10-31 -================== - - * Changed `--version` short flag to `-V` from `-v` - * Changed `.version()` so it's configurable [felixge] - -0.3.0 / 2011-10-31 -================== - - * Added support for long flags only. Closes #18 - -0.2.1 / 2011-10-24 -================== - - * "node": ">= 0.4.x < 0.7.0". Closes #20 - -0.2.0 / 2011-09-26 -================== - - * Allow for defaults that are not just boolean. Default peassignment only occurs for --no-*, optional, and required arguments. [Jim Isaacs] - -0.1.0 / 2011-08-24 -================== - - * Added support for custom `--help` output - -0.0.5 / 2011-08-18 -================== - - * Changed: when the user enters nothing prompt for password again - * Fixed issue with passwords beginning with numbers [NuckChorris] - -0.0.4 / 2011-08-15 -================== - - * Fixed `Commander#args` - -0.0.3 / 2011-08-15 -================== - - * Added default option value support - -0.0.2 / 2011-08-15 -================== - - * Added mask support to `Command#password(str[, mask], fn)` - * Added `Command#password(str, fn)` - -0.0.1 / 2010-01-03 -================== - - * Initial release diff --git a/tests/mocha/node_modules/commander/Readme.md b/tests/mocha/node_modules/commander/Readme.md deleted file mode 100644 index d1644012c5..0000000000 --- a/tests/mocha/node_modules/commander/Readme.md +++ /dev/null @@ -1,195 +0,0 @@ -# Commander.js - - The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/visionmedia/commander). - - [![Build Status](https://secure.travis-ci.org/visionmedia/commander.js.png)](http://travis-ci.org/visionmedia/commander.js) - -## Installation - - $ npm install commander - -## Option parsing - - Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options. - -```js -#!/usr/bin/env node - -/** - * Module dependencies. - */ - -var program = require('commander'); - -program - .version('0.0.1') - .option('-p, --peppers', 'Add peppers') - .option('-P, --pineapple', 'Add pineapple') - .option('-b, --bbq', 'Add bbq sauce') - .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble') - .parse(process.argv); - -console.log('you ordered a pizza with:'); -if (program.peppers) console.log(' - peppers'); -if (program.pineapple) console.log(' - pineapple'); -if (program.bbq) console.log(' - bbq'); -console.log(' - %s cheese', program.cheese); -``` - - Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc. - -## Automated --help - - The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free: - -``` - $ ./examples/pizza --help - - Usage: pizza [options] - - Options: - - -V, --version output the version number - -p, --peppers Add peppers - -P, --pineapple Add pineapple - -b, --bbq Add bbq sauce - -c, --cheese Add the specified type of cheese [marble] - -h, --help output usage information - -``` - -## Coercion - -```js -function range(val) { - return val.split('..').map(Number); -} - -function list(val) { - return val.split(','); -} - -program - .version('0.0.1') - .usage('[options] ') - .option('-i, --integer ', 'An integer argument', parseInt) - .option('-f, --float ', 'A float argument', parseFloat) - .option('-r, --range ..', 'A range', range) - .option('-l, --list ', 'A list', list) - .option('-o, --optional [value]', 'An optional value') - .parse(process.argv); - -console.log(' int: %j', program.integer); -console.log(' float: %j', program.float); -console.log(' optional: %j', program.optional); -program.range = program.range || []; -console.log(' range: %j..%j', program.range[0], program.range[1]); -console.log(' list: %j', program.list); -console.log(' args: %j', program.args); -``` - -## Custom help - - You can display arbitrary `-h, --help` information - by listening for "--help". Commander will automatically - exit once you are done so that the remainder of your program - does not execute causing undesired behaviours, for example - in the following executable "stuff" will not output when - `--help` is used. - -```js -#!/usr/bin/env node - -/** - * Module dependencies. - */ - -var program = require('../'); - -function list(val) { - return val.split(',').map(Number); -} - -program - .version('0.0.1') - .option('-f, --foo', 'enable some foo') - .option('-b, --bar', 'enable some bar') - .option('-B, --baz', 'enable some baz'); - -// must be before .parse() since -// node's emit() is immediate - -program.on('--help', function(){ - console.log(' Examples:'); - console.log(''); - console.log(' $ custom-help --help'); - console.log(' $ custom-help -h'); - console.log(''); -}); - -program.parse(process.argv); - -console.log('stuff'); -``` - -yielding the following help output: - -``` - -Usage: custom-help [options] - -Options: - - -h, --help output usage information - -V, --version output the version number - -f, --foo enable some foo - -b, --bar enable some bar - -B, --baz enable some baz - -Examples: - - $ custom-help --help - $ custom-help -h - -``` - -## .outputHelp() - - Output help information without exiting. - -## .help() - - Output help information and exit immediately. - -## Links - - - [API documentation](http://visionmedia.github.com/commander.js/) - - [ascii tables](https://github.com/LearnBoost/cli-table) - - [progress bars](https://github.com/visionmedia/node-progress) - - [more progress bars](https://github.com/substack/node-multimeter) - - [examples](https://github.com/visionmedia/commander.js/tree/master/examples) - -## License - -(The MIT License) - -Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca> - -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 copies 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 all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 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. diff --git a/tests/mocha/node_modules/commander/index.js b/tests/mocha/node_modules/commander/index.js deleted file mode 100644 index d5778a75ba..0000000000 --- a/tests/mocha/node_modules/commander/index.js +++ /dev/null @@ -1,847 +0,0 @@ - -/** - * Module dependencies. - */ - -var EventEmitter = require('events').EventEmitter; -var spawn = require('child_process').spawn; -var fs = require('fs'); -var exists = fs.existsSync; -var path = require('path'); -var dirname = path.dirname; -var basename = path.basename; - -/** - * Expose the root command. - */ - -exports = module.exports = new Command; - -/** - * Expose `Command`. - */ - -exports.Command = Command; - -/** - * Expose `Option`. - */ - -exports.Option = Option; - -/** - * Initialize a new `Option` with the given `flags` and `description`. - * - * @param {String} flags - * @param {String} description - * @api public - */ - -function Option(flags, description) { - this.flags = flags; - this.required = ~flags.indexOf('<'); - this.optional = ~flags.indexOf('['); - this.bool = !~flags.indexOf('-no-'); - flags = flags.split(/[ ,|]+/); - if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift(); - this.long = flags.shift(); - this.description = description || ''; -} - -/** - * Return option name. - * - * @return {String} - * @api private - */ - -Option.prototype.name = function(){ - return this.long - .replace('--', '') - .replace('no-', ''); -}; - -/** - * Check if `arg` matches the short or long flag. - * - * @param {String} arg - * @return {Boolean} - * @api private - */ - -Option.prototype.is = function(arg){ - return arg == this.short - || arg == this.long; -}; - -/** - * Initialize a new `Command`. - * - * @param {String} name - * @api public - */ - -function Command(name) { - this.commands = []; - this.options = []; - this._execs = []; - this._args = []; - this._name = name; -} - -/** - * Inherit from `EventEmitter.prototype`. - */ - -Command.prototype.__proto__ = EventEmitter.prototype; - -/** - * Add command `name`. - * - * The `.action()` callback is invoked when the - * command `name` is specified via __ARGV__, - * and the remaining arguments are applied to the - * function for access. - * - * When the `name` is "*" an un-matched command - * will be passed as the first arg, followed by - * the rest of __ARGV__ remaining. - * - * Examples: - * - * program - * .version('0.0.1') - * .option('-C, --chdir ', 'change the working directory') - * .option('-c, --config ', 'set config path. defaults to ./deploy.conf') - * .option('-T, --no-tests', 'ignore test hook') - * - * program - * .command('setup') - * .description('run remote setup commands') - * .action(function(){ - * console.log('setup'); - * }); - * - * program - * .command('exec ') - * .description('run the given remote command') - * .action(function(cmd){ - * console.log('exec "%s"', cmd); - * }); - * - * program - * .command('*') - * .description('deploy the given env') - * .action(function(env){ - * console.log('deploying "%s"', env); - * }); - * - * program.parse(process.argv); - * - * @param {String} name - * @param {String} [desc] - * @return {Command} the new command - * @api public - */ - -Command.prototype.command = function(name, desc){ - var args = name.split(/ +/); - var cmd = new Command(args.shift()); - if (desc) cmd.description(desc); - if (desc) this.executables = true; - if (desc) this._execs[cmd._name] = true; - this.commands.push(cmd); - cmd.parseExpectedArgs(args); - cmd.parent = this; - if (desc) return this; - return cmd; -}; - -/** - * Add an implicit `help [cmd]` subcommand - * which invokes `--help` for the given command. - * - * @api private - */ - -Command.prototype.addImplicitHelpCommand = function() { - this.command('help [cmd]', 'display help for [cmd]'); -}; - -/** - * Parse expected `args`. - * - * For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`. - * - * @param {Array} args - * @return {Command} for chaining - * @api public - */ - -Command.prototype.parseExpectedArgs = function(args){ - if (!args.length) return; - var self = this; - args.forEach(function(arg){ - switch (arg[0]) { - case '<': - self._args.push({ required: true, name: arg.slice(1, -1) }); - break; - case '[': - self._args.push({ required: false, name: arg.slice(1, -1) }); - break; - } - }); - return this; -}; - -/** - * Register callback `fn` for the command. - * - * Examples: - * - * program - * .command('help') - * .description('display verbose help') - * .action(function(){ - * // output help here - * }); - * - * @param {Function} fn - * @return {Command} for chaining - * @api public - */ - -Command.prototype.action = function(fn){ - var self = this; - this.parent.on(this._name, function(args, unknown){ - // Parse any so-far unknown options - unknown = unknown || []; - var parsed = self.parseOptions(unknown); - - // Output help if necessary - outputHelpIfNecessary(self, parsed.unknown); - - // If there are still any unknown options, then we simply - // die, unless someone asked for help, in which case we give it - // to them, and then we die. - if (parsed.unknown.length > 0) { - self.unknownOption(parsed.unknown[0]); - } - - // Leftover arguments need to be pushed back. Fixes issue #56 - if (parsed.args.length) args = parsed.args.concat(args); - - self._args.forEach(function(arg, i){ - if (arg.required && null == args[i]) { - self.missingArgument(arg.name); - } - }); - - // Always append ourselves to the end of the arguments, - // to make sure we match the number of arguments the user - // expects - if (self._args.length) { - args[self._args.length] = self; - } else { - args.push(self); - } - - fn.apply(this, args); - }); - return this; -}; - -/** - * Define option with `flags`, `description` and optional - * coercion `fn`. - * - * The `flags` string should contain both the short and long flags, - * separated by comma, a pipe or space. The following are all valid - * all will output this way when `--help` is used. - * - * "-p, --pepper" - * "-p|--pepper" - * "-p --pepper" - * - * Examples: - * - * // simple boolean defaulting to false - * program.option('-p, --pepper', 'add pepper'); - * - * --pepper - * program.pepper - * // => Boolean - * - * // simple boolean defaulting to false - * program.option('-C, --no-cheese', 'remove cheese'); - * - * program.cheese - * // => true - * - * --no-cheese - * program.cheese - * // => true - * - * // required argument - * program.option('-C, --chdir ', 'change the working directory'); - * - * --chdir /tmp - * program.chdir - * // => "/tmp" - * - * // optional argument - * program.option('-c, --cheese [type]', 'add cheese [marble]'); - * - * @param {String} flags - * @param {String} description - * @param {Function|Mixed} fn or default - * @param {Mixed} defaultValue - * @return {Command} for chaining - * @api public - */ - -Command.prototype.option = function(flags, description, fn, defaultValue){ - var self = this - , option = new Option(flags, description) - , oname = option.name() - , name = camelcase(oname); - - // default as 3rd arg - if ('function' != typeof fn) defaultValue = fn, fn = null; - - // preassign default value only for --no-*, [optional], or - if (false == option.bool || option.optional || option.required) { - // when --no-* we make sure default is true - if (false == option.bool) defaultValue = true; - // preassign only if we have a default - if (undefined !== defaultValue) self[name] = defaultValue; - } - - // register the option - this.options.push(option); - - // when it's passed assign the value - // and conditionally invoke the callback - this.on(oname, function(val){ - // coercion - if (null != val && fn) val = fn(val); - - // unassigned or bool - if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) { - // if no value, bool true, and we have a default, then use it! - if (null == val) { - self[name] = option.bool - ? defaultValue || true - : false; - } else { - self[name] = val; - } - } else if (null !== val) { - // reassign - self[name] = val; - } - }); - - return this; -}; - -/** - * Parse `argv`, settings options and invoking commands when defined. - * - * @param {Array} argv - * @return {Command} for chaining - * @api public - */ - -Command.prototype.parse = function(argv){ - // implicit help - if (this.executables) this.addImplicitHelpCommand(); - - // store raw args - this.rawArgs = argv; - - // guess name - this._name = this._name || basename(argv[1]); - - // process argv - var parsed = this.parseOptions(this.normalize(argv.slice(2))); - var args = this.args = parsed.args; - - var result = this.parseArgs(this.args, parsed.unknown); - - // executable sub-commands - var name = result.args[0]; - if (this._execs[name]) return this.executeSubCommand(argv, args, parsed.unknown); - - return result; -}; - -/** - * Execute a sub-command executable. - * - * @param {Array} argv - * @param {Array} args - * @param {Array} unknown - * @api private - */ - -Command.prototype.executeSubCommand = function(argv, args, unknown) { - args = args.concat(unknown); - - if (!args.length) this.help(); - if ('help' == args[0] && 1 == args.length) this.help(); - - // --help - if ('help' == args[0]) { - args[0] = args[1]; - args[1] = '--help'; - } - - // executable - var dir = dirname(argv[1]); - var bin = basename(argv[1]) + '-' + args[0]; - - // check for ./ first - var local = path.join(dir, bin); - - // run it - args = args.slice(1); - var proc = spawn(local, args, { stdio: 'inherit', customFds: [0, 1, 2] }); - proc.on('error', function(err){ - if (err.code == "ENOENT") { - console.error('\n %s(1) does not exist, try --help\n', bin); - } else if (err.code == "EACCES") { - console.error('\n %s(1) not executable. try chmod or run with root\n', bin); - } - }); - - this.runningCommand = proc; -}; - -/** - * Normalize `args`, splitting joined short flags. For example - * the arg "-abc" is equivalent to "-a -b -c". - * This also normalizes equal sign and splits "--abc=def" into "--abc def". - * - * @param {Array} args - * @return {Array} - * @api private - */ - -Command.prototype.normalize = function(args){ - var ret = [] - , arg - , index; - - for (var i = 0, len = args.length; i < len; ++i) { - arg = args[i]; - if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) { - arg.slice(1).split('').forEach(function(c){ - ret.push('-' + c); - }); - } else if (/^--/.test(arg) && ~(index = arg.indexOf('='))) { - ret.push(arg.slice(0, index), arg.slice(index + 1)); - } else { - ret.push(arg); - } - } - - return ret; -}; - -/** - * Parse command `args`. - * - * When listener(s) are available those - * callbacks are invoked, otherwise the "*" - * event is emitted and those actions are invoked. - * - * @param {Array} args - * @return {Command} for chaining - * @api private - */ - -Command.prototype.parseArgs = function(args, unknown){ - var cmds = this.commands - , len = cmds.length - , name; - - if (args.length) { - name = args[0]; - if (this.listeners(name).length) { - this.emit(args.shift(), args, unknown); - } else { - this.emit('*', args); - } - } else { - outputHelpIfNecessary(this, unknown); - - // If there were no args and we have unknown options, - // then they are extraneous and we need to error. - if (unknown.length > 0) { - this.unknownOption(unknown[0]); - } - } - - return this; -}; - -/** - * Return an option matching `arg` if any. - * - * @param {String} arg - * @return {Option} - * @api private - */ - -Command.prototype.optionFor = function(arg){ - for (var i = 0, len = this.options.length; i < len; ++i) { - if (this.options[i].is(arg)) { - return this.options[i]; - } - } -}; - -/** - * Parse options from `argv` returning `argv` - * void of these options. - * - * @param {Array} argv - * @return {Array} - * @api public - */ - -Command.prototype.parseOptions = function(argv){ - var args = [] - , len = argv.length - , literal - , option - , arg; - - var unknownOptions = []; - - // parse options - for (var i = 0; i < len; ++i) { - arg = argv[i]; - - // literal args after -- - if ('--' == arg) { - literal = true; - continue; - } - - if (literal) { - args.push(arg); - continue; - } - - // find matching Option - option = this.optionFor(arg); - - // option is defined - if (option) { - // requires arg - if (option.required) { - arg = argv[++i]; - if (null == arg) return this.optionMissingArgument(option); - if ('-' == arg[0] && '-' != arg) return this.optionMissingArgument(option, arg); - this.emit(option.name(), arg); - // optional arg - } else if (option.optional) { - arg = argv[i+1]; - if (null == arg || ('-' == arg[0] && '-' != arg)) { - arg = null; - } else { - ++i; - } - this.emit(option.name(), arg); - // bool - } else { - this.emit(option.name()); - } - continue; - } - - // looks like an option - if (arg.length > 1 && '-' == arg[0]) { - unknownOptions.push(arg); - - // If the next argument looks like it might be - // an argument for this option, we pass it on. - // If it isn't, then it'll simply be ignored - if (argv[i+1] && '-' != argv[i+1][0]) { - unknownOptions.push(argv[++i]); - } - continue; - } - - // arg - args.push(arg); - } - - return { args: args, unknown: unknownOptions }; -}; - -/** - * Argument `name` is missing. - * - * @param {String} name - * @api private - */ - -Command.prototype.missingArgument = function(name){ - console.error(); - console.error(" error: missing required argument `%s'", name); - console.error(); - process.exit(1); -}; - -/** - * `Option` is missing an argument, but received `flag` or nothing. - * - * @param {String} option - * @param {String} flag - * @api private - */ - -Command.prototype.optionMissingArgument = function(option, flag){ - console.error(); - if (flag) { - console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag); - } else { - console.error(" error: option `%s' argument missing", option.flags); - } - console.error(); - process.exit(1); -}; - -/** - * Unknown option `flag`. - * - * @param {String} flag - * @api private - */ - -Command.prototype.unknownOption = function(flag){ - console.error(); - console.error(" error: unknown option `%s'", flag); - console.error(); - process.exit(1); -}; - - -/** - * Set the program version to `str`. - * - * This method auto-registers the "-V, --version" flag - * which will print the version number when passed. - * - * @param {String} str - * @param {String} flags - * @return {Command} for chaining - * @api public - */ - -Command.prototype.version = function(str, flags){ - if (0 == arguments.length) return this._version; - this._version = str; - flags = flags || '-V, --version'; - this.option(flags, 'output the version number'); - this.on('version', function(){ - console.log(str); - process.exit(0); - }); - return this; -}; - -/** - * Set the description `str`. - * - * @param {String} str - * @return {String|Command} - * @api public - */ - -Command.prototype.description = function(str){ - if (0 == arguments.length) return this._description; - this._description = str; - return this; -}; - -/** - * Set / get the command usage `str`. - * - * @param {String} str - * @return {String|Command} - * @api public - */ - -Command.prototype.usage = function(str){ - var args = this._args.map(function(arg){ - return arg.required - ? '<' + arg.name + '>' - : '[' + arg.name + ']'; - }); - - var usage = '[options' - + (this.commands.length ? '] [command' : '') - + ']' - + (this._args.length ? ' ' + args : ''); - - if (0 == arguments.length) return this._usage || usage; - this._usage = str; - - return this; -}; - -/** - * Return the largest option length. - * - * @return {Number} - * @api private - */ - -Command.prototype.largestOptionLength = function(){ - return this.options.reduce(function(max, option){ - return Math.max(max, option.flags.length); - }, 0); -}; - -/** - * Return help for options. - * - * @return {String} - * @api private - */ - -Command.prototype.optionHelp = function(){ - var width = this.largestOptionLength(); - - // Prepend the help information - return [pad('-h, --help', width) + ' ' + 'output usage information'] - .concat(this.options.map(function(option){ - return pad(option.flags, width) - + ' ' + option.description; - })) - .join('\n'); -}; - -/** - * Return command help documentation. - * - * @return {String} - * @api private - */ - -Command.prototype.commandHelp = function(){ - if (!this.commands.length) return ''; - return [ - '' - , ' Commands:' - , '' - , this.commands.map(function(cmd){ - var args = cmd._args.map(function(arg){ - return arg.required - ? '<' + arg.name + '>' - : '[' + arg.name + ']'; - }).join(' '); - - return pad(cmd._name - + (cmd.options.length - ? ' [options]' - : '') + ' ' + args, 22) - + (cmd.description() - ? ' ' + cmd.description() - : ''); - }).join('\n').replace(/^/gm, ' ') - , '' - ].join('\n'); -}; - -/** - * Return program help documentation. - * - * @return {String} - * @api private - */ - -Command.prototype.helpInformation = function(){ - return [ - '' - , ' Usage: ' + this._name + ' ' + this.usage() - , '' + this.commandHelp() - , ' Options:' - , '' - , '' + this.optionHelp().replace(/^/gm, ' ') - , '' - , '' - ].join('\n'); -}; - -/** - * Output help information for this command - * - * @api public - */ - -Command.prototype.outputHelp = function(){ - process.stdout.write(this.helpInformation()); - this.emit('--help'); -}; - -/** - * Output help information and exit. - * - * @api public - */ - -Command.prototype.help = function(){ - this.outputHelp(); - process.exit(); -}; - -/** - * Camel-case the given `flag` - * - * @param {String} flag - * @return {String} - * @api private - */ - -function camelcase(flag) { - return flag.split('-').reduce(function(str, word){ - return str + word[0].toUpperCase() + word.slice(1); - }); -} - -/** - * Pad `str` to `width`. - * - * @param {String} str - * @param {Number} width - * @return {String} - * @api private - */ - -function pad(str, width) { - var len = Math.max(0, width - str.length); - return str + Array(len + 1).join(' '); -} - -/** - * Output help information if necessary - * - * @param {Command} command to output help for - * @param {Array} array of options to search for -h or --help - * @api private - */ - -function outputHelpIfNecessary(cmd, options) { - options = options || []; - for (var i = 0; i < options.length; i++) { - if (options[i] == '--help' || options[i] == '-h') { - cmd.outputHelp(); - process.exit(0); - } - } -} diff --git a/tests/mocha/node_modules/commander/package.json b/tests/mocha/node_modules/commander/package.json deleted file mode 100644 index e25ed273ff..0000000000 --- a/tests/mocha/node_modules/commander/package.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "commander", - "version": "2.0.0", - "description": "the complete solution for node.js command-line programs", - "keywords": [ - "command", - "option", - "parser", - "prompt", - "stdin" - ], - "author": { - "name": "TJ Holowaychuk", - "email": "tj@vision-media.ca" - }, - "repository": { - "type": "git", - "url": "https://github.com/visionmedia/commander.js.git" - }, - "devDependencies": { - "should": ">= 0.0.1" - }, - "scripts": { - "test": "make test" - }, - "main": "index", - "engines": { - "node": ">= 0.6.x" - }, - "readme": "# Commander.js\n\n The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/visionmedia/commander).\n\n [![Build Status](https://secure.travis-ci.org/visionmedia/commander.js.png)](http://travis-ci.org/visionmedia/commander.js)\n\n## Installation\n\n $ npm install commander\n\n## Option parsing\n\n Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options.\n\n```js\n#!/usr/bin/env node\n\n/**\n * Module dependencies.\n */\n\nvar program = require('commander');\n\nprogram\n .version('0.0.1')\n .option('-p, --peppers', 'Add peppers')\n .option('-P, --pineapple', 'Add pineapple')\n .option('-b, --bbq', 'Add bbq sauce')\n .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')\n .parse(process.argv);\n\nconsole.log('you ordered a pizza with:');\nif (program.peppers) console.log(' - peppers');\nif (program.pineapple) console.log(' - pineapple');\nif (program.bbq) console.log(' - bbq');\nconsole.log(' - %s cheese', program.cheese);\n```\n\n Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as \"--template-engine\" are camel-cased, becoming `program.templateEngine` etc.\n\n## Automated --help\n\n The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free:\n\n``` \n $ ./examples/pizza --help\n\n Usage: pizza [options]\n\n Options:\n\n -V, --version output the version number\n -p, --peppers Add peppers\n -P, --pineapple Add pineapple\n -b, --bbq Add bbq sauce\n -c, --cheese Add the specified type of cheese [marble]\n -h, --help output usage information\n\n```\n\n## Coercion\n\n```js\nfunction range(val) {\n return val.split('..').map(Number);\n}\n\nfunction list(val) {\n return val.split(',');\n}\n\nprogram\n .version('0.0.1')\n .usage('[options] ')\n .option('-i, --integer ', 'An integer argument', parseInt)\n .option('-f, --float ', 'A float argument', parseFloat)\n .option('-r, --range ..', 'A range', range)\n .option('-l, --list ', 'A list', list)\n .option('-o, --optional [value]', 'An optional value')\n .parse(process.argv);\n\nconsole.log(' int: %j', program.integer);\nconsole.log(' float: %j', program.float);\nconsole.log(' optional: %j', program.optional);\nprogram.range = program.range || [];\nconsole.log(' range: %j..%j', program.range[0], program.range[1]);\nconsole.log(' list: %j', program.list);\nconsole.log(' args: %j', program.args);\n```\n\n## Custom help\n\n You can display arbitrary `-h, --help` information\n by listening for \"--help\". Commander will automatically\n exit once you are done so that the remainder of your program\n does not execute causing undesired behaviours, for example\n in the following executable \"stuff\" will not output when\n `--help` is used.\n\n```js\n#!/usr/bin/env node\n\n/**\n * Module dependencies.\n */\n\nvar program = require('../');\n\nfunction list(val) {\n return val.split(',').map(Number);\n}\n\nprogram\n .version('0.0.1')\n .option('-f, --foo', 'enable some foo')\n .option('-b, --bar', 'enable some bar')\n .option('-B, --baz', 'enable some baz');\n\n// must be before .parse() since\n// node's emit() is immediate\n\nprogram.on('--help', function(){\n console.log(' Examples:');\n console.log('');\n console.log(' $ custom-help --help');\n console.log(' $ custom-help -h');\n console.log('');\n});\n\nprogram.parse(process.argv);\n\nconsole.log('stuff');\n```\n\nyielding the following help output:\n\n```\n\nUsage: custom-help [options]\n\nOptions:\n\n -h, --help output usage information\n -V, --version output the version number\n -f, --foo enable some foo\n -b, --bar enable some bar\n -B, --baz enable some baz\n\nExamples:\n\n $ custom-help --help\n $ custom-help -h\n\n```\n\n## .outputHelp()\n\n Output help information without exiting.\n\n## .help()\n\n Output help information and exit immediately.\n\n## Links\n\n - [API documentation](http://visionmedia.github.com/commander.js/)\n - [ascii tables](https://github.com/LearnBoost/cli-table)\n - [progress bars](https://github.com/visionmedia/node-progress)\n - [more progress bars](https://github.com/substack/node-multimeter)\n - [examples](https://github.com/visionmedia/commander.js/tree/master/examples)\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", - "readmeFilename": "Readme.md", - "bugs": { - "url": "https://github.com/visionmedia/commander.js/issues" - }, - "homepage": "https://github.com/visionmedia/commander.js", - "_id": "commander@2.0.0", - "_from": "commander@2.0.0" -} diff --git a/tests/mocha/node_modules/diff/README.md b/tests/mocha/node_modules/diff/README.md deleted file mode 100644 index 95bd8da294..0000000000 --- a/tests/mocha/node_modules/diff/README.md +++ /dev/null @@ -1,101 +0,0 @@ -# jsdiff - -[![Build Status](https://secure.travis-ci.org/kpdecker/jsdiff.png)](http://travis-ci.org/kpdecker/jsdiff) - -A javascript text differencing implementation. - -Based on the algorithm proposed in -["An O(ND) Difference Algorithm and its Variations" (Myers, 1986)](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927). - -## Installation - - npm install diff - -or - - git clone git://github.com/kpdecker/jsdiff.git - -## API - -* JsDiff.diffChars(oldStr, newStr) - Diffs two blocks of text, comparing character by character. - - Returns a list of change objects (See below). - -* JsDiff.diffWords(oldStr, newStr) - Diffs two blocks of text, comparing word by word. - - Returns a list of change objects (See below). - -* JsDiff.diffLines(oldStr, newStr) - Diffs two blocks of text, comparing line by line. - - Returns a list of change objects (See below). - -* JsDiff.diffCss(oldStr, newStr) - Diffs two blocks of text, comparing CSS tokens. - - Returns a list of change objects (See below). - -* JsDiff.createPatch(fileName, oldStr, newStr, oldHeader, newHeader) - Creates a unified diff patch. - - Parameters: - * fileName : String to be output in the filename sections of the patch - * oldStr : Original string value - * newStr : New string value - * oldHeader : Additional information to include in the old file header - * newHeader : Additional information to include in thew new file header - -* JsDiff.applyPatch(oldStr, diffStr) - Applies a unified diff patch. - - Return a string containing new version of provided data. - -* convertChangesToXML(changes) - Converts a list of changes to a serialized XML format - -### Change Objects -Many of the methods above return change objects. These objects are consist of the following fields: - -* value: Text content -* added: True if the value was inserted into the new string -* removed: True of the value was removed from the old string - -Note that some cases may omit a particular flag field. Comparison on the flag fields should always be done in a truthy or falsy manner. - -## [Example](http://kpdecker.github.com/jsdiff) - -## License - -Software License Agreement (BSD License) - -Copyright (c) 2009-2011, Kevin Decker kpdecker@gmail.com - -All rights reserved. - -Redistribution and use of this software in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of Kevin Decker nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER -IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/tests/mocha/node_modules/diff/diff.js b/tests/mocha/node_modules/diff/diff.js deleted file mode 100644 index a34c22a044..0000000000 --- a/tests/mocha/node_modules/diff/diff.js +++ /dev/null @@ -1,354 +0,0 @@ -/* See LICENSE file for terms of use */ - -/* - * Text diff implementation. - * - * This library supports the following APIS: - * JsDiff.diffChars: Character by character diff - * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace - * JsDiff.diffLines: Line based diff - * - * JsDiff.diffCss: Diff targeted at CSS content - * - * These methods are based on the implementation proposed in - * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986). - * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 - */ -var JsDiff = (function() { - /*jshint maxparams: 5*/ - function clonePath(path) { - return { newPos: path.newPos, components: path.components.slice(0) }; - } - function removeEmpty(array) { - var ret = []; - for (var i = 0; i < array.length; i++) { - if (array[i]) { - ret.push(array[i]); - } - } - return ret; - } - function escapeHTML(s) { - var n = s; - n = n.replace(/&/g, '&'); - n = n.replace(//g, '>'); - n = n.replace(/"/g, '"'); - - return n; - } - - var Diff = function(ignoreWhitespace) { - this.ignoreWhitespace = ignoreWhitespace; - }; - Diff.prototype = { - diff: function(oldString, newString) { - // Handle the identity case (this is due to unrolling editLength == 0 - if (newString === oldString) { - return [{ value: newString }]; - } - if (!newString) { - return [{ value: oldString, removed: true }]; - } - if (!oldString) { - return [{ value: newString, added: true }]; - } - - newString = this.tokenize(newString); - oldString = this.tokenize(oldString); - - var newLen = newString.length, oldLen = oldString.length; - var maxEditLength = newLen + oldLen; - var bestPath = [{ newPos: -1, components: [] }]; - - // Seed editLength = 0 - var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); - if (bestPath[0].newPos+1 >= newLen && oldPos+1 >= oldLen) { - return bestPath[0].components; - } - - for (var editLength = 1; editLength <= maxEditLength; editLength++) { - for (var diagonalPath = -1*editLength; diagonalPath <= editLength; diagonalPath+=2) { - var basePath; - var addPath = bestPath[diagonalPath-1], - removePath = bestPath[diagonalPath+1]; - oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; - if (addPath) { - // No one else is going to attempt to use this value, clear it - bestPath[diagonalPath-1] = undefined; - } - - var canAdd = addPath && addPath.newPos+1 < newLen; - var canRemove = removePath && 0 <= oldPos && oldPos < oldLen; - if (!canAdd && !canRemove) { - bestPath[diagonalPath] = undefined; - continue; - } - - // Select the diagonal that we want to branch from. We select the prior - // path whose position in the new string is the farthest from the origin - // and does not pass the bounds of the diff graph - if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) { - basePath = clonePath(removePath); - this.pushComponent(basePath.components, oldString[oldPos], undefined, true); - } else { - basePath = clonePath(addPath); - basePath.newPos++; - this.pushComponent(basePath.components, newString[basePath.newPos], true, undefined); - } - - var oldPos = this.extractCommon(basePath, newString, oldString, diagonalPath); - - if (basePath.newPos+1 >= newLen && oldPos+1 >= oldLen) { - return basePath.components; - } else { - bestPath[diagonalPath] = basePath; - } - } - } - }, - - pushComponent: function(components, value, added, removed) { - var last = components[components.length-1]; - if (last && last.added === added && last.removed === removed) { - // We need to clone here as the component clone operation is just - // as shallow array clone - components[components.length-1] = - {value: this.join(last.value, value), added: added, removed: removed }; - } else { - components.push({value: value, added: added, removed: removed }); - } - }, - extractCommon: function(basePath, newString, oldString, diagonalPath) { - var newLen = newString.length, - oldLen = oldString.length, - newPos = basePath.newPos, - oldPos = newPos - diagonalPath; - while (newPos+1 < newLen && oldPos+1 < oldLen && this.equals(newString[newPos+1], oldString[oldPos+1])) { - newPos++; - oldPos++; - - this.pushComponent(basePath.components, newString[newPos], undefined, undefined); - } - basePath.newPos = newPos; - return oldPos; - }, - - equals: function(left, right) { - var reWhitespace = /\S/; - if (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)) { - return true; - } else { - return left === right; - } - }, - join: function(left, right) { - return left + right; - }, - tokenize: function(value) { - return value; - } - }; - - var CharDiff = new Diff(); - - var WordDiff = new Diff(true); - var WordWithSpaceDiff = new Diff(); - WordDiff.tokenize = WordWithSpaceDiff.tokenize = function(value) { - return removeEmpty(value.split(/(\s+|\b)/)); - }; - - var CssDiff = new Diff(true); - CssDiff.tokenize = function(value) { - return removeEmpty(value.split(/([{}:;,]|\s+)/)); - }; - - var LineDiff = new Diff(); - LineDiff.tokenize = function(value) { - return value.split(/^/m); - }; - - return { - Diff: Diff, - - diffChars: function(oldStr, newStr) { return CharDiff.diff(oldStr, newStr); }, - diffWords: function(oldStr, newStr) { return WordDiff.diff(oldStr, newStr); }, - diffWordsWithSpace: function(oldStr, newStr) { return WordWithSpaceDiff.diff(oldStr, newStr); }, - diffLines: function(oldStr, newStr) { return LineDiff.diff(oldStr, newStr); }, - - diffCss: function(oldStr, newStr) { return CssDiff.diff(oldStr, newStr); }, - - createPatch: function(fileName, oldStr, newStr, oldHeader, newHeader) { - var ret = []; - - ret.push('Index: ' + fileName); - ret.push('==================================================================='); - ret.push('--- ' + fileName + (typeof oldHeader === 'undefined' ? '' : '\t' + oldHeader)); - ret.push('+++ ' + fileName + (typeof newHeader === 'undefined' ? '' : '\t' + newHeader)); - - var diff = LineDiff.diff(oldStr, newStr); - if (!diff[diff.length-1].value) { - diff.pop(); // Remove trailing newline add - } - diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier - - function contextLines(lines) { - return lines.map(function(entry) { return ' ' + entry; }); - } - function eofNL(curRange, i, current) { - var last = diff[diff.length-2], - isLast = i === diff.length-2, - isLastOfType = i === diff.length-3 && (current.added !== last.added || current.removed !== last.removed); - - // Figure out if this is the last line for the given file and missing NL - if (!/\n$/.test(current.value) && (isLast || isLastOfType)) { - curRange.push('\\ No newline at end of file'); - } - } - - var oldRangeStart = 0, newRangeStart = 0, curRange = [], - oldLine = 1, newLine = 1; - for (var i = 0; i < diff.length; i++) { - var current = diff[i], - lines = current.lines || current.value.replace(/\n$/, '').split('\n'); - current.lines = lines; - - if (current.added || current.removed) { - if (!oldRangeStart) { - var prev = diff[i-1]; - oldRangeStart = oldLine; - newRangeStart = newLine; - - if (prev) { - curRange = contextLines(prev.lines.slice(-4)); - oldRangeStart -= curRange.length; - newRangeStart -= curRange.length; - } - } - curRange.push.apply(curRange, lines.map(function(entry) { return (current.added?'+':'-') + entry; })); - eofNL(curRange, i, current); - - if (current.added) { - newLine += lines.length; - } else { - oldLine += lines.length; - } - } else { - if (oldRangeStart) { - // Close out any changes that have been output (or join overlapping) - if (lines.length <= 8 && i < diff.length-2) { - // Overlapping - curRange.push.apply(curRange, contextLines(lines)); - } else { - // end the range and output - var contextSize = Math.min(lines.length, 4); - ret.push( - '@@ -' + oldRangeStart + ',' + (oldLine-oldRangeStart+contextSize) - + ' +' + newRangeStart + ',' + (newLine-newRangeStart+contextSize) - + ' @@'); - ret.push.apply(ret, curRange); - ret.push.apply(ret, contextLines(lines.slice(0, contextSize))); - if (lines.length <= 4) { - eofNL(ret, i, current); - } - - oldRangeStart = 0; newRangeStart = 0; curRange = []; - } - } - oldLine += lines.length; - newLine += lines.length; - } - } - - return ret.join('\n') + '\n'; - }, - - applyPatch: function(oldStr, uniDiff) { - var diffstr = uniDiff.split('\n'); - var diff = []; - var remEOFNL = false, - addEOFNL = false; - - for (var i = (diffstr[0][0]==='I'?4:0); i < diffstr.length; i++) { - if(diffstr[i][0] === '@') { - var meh = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); - diff.unshift({ - start:meh[3], - oldlength:meh[2], - oldlines:[], - newlength:meh[4], - newlines:[] - }); - } else if(diffstr[i][0] === '+') { - diff[0].newlines.push(diffstr[i].substr(1)); - } else if(diffstr[i][0] === '-') { - diff[0].oldlines.push(diffstr[i].substr(1)); - } else if(diffstr[i][0] === ' ') { - diff[0].newlines.push(diffstr[i].substr(1)); - diff[0].oldlines.push(diffstr[i].substr(1)); - } else if(diffstr[i][0] === '\\') { - if (diffstr[i-1][0] === '+') { - remEOFNL = true; - } else if(diffstr[i-1][0] === '-') { - addEOFNL = true; - } - } - } - - var str = oldStr.split('\n'); - for (var i = diff.length - 1; i >= 0; i--) { - var d = diff[i]; - for (var j = 0; j < d.oldlength; j++) { - if(str[d.start-1+j] !== d.oldlines[j]) { - return false; - } - } - Array.prototype.splice.apply(str,[d.start-1,+d.oldlength].concat(d.newlines)); - } - - if (remEOFNL) { - while (!str[str.length-1]) { - str.pop(); - } - } else if (addEOFNL) { - str.push(''); - } - return str.join('\n'); - }, - - convertChangesToXML: function(changes){ - var ret = []; - for ( var i = 0; i < changes.length; i++) { - var change = changes[i]; - if (change.added) { - ret.push(''); - } else if (change.removed) { - ret.push(''); - } - - ret.push(escapeHTML(change.value)); - - if (change.added) { - ret.push(''); - } else if (change.removed) { - ret.push(''); - } - } - return ret.join(''); - }, - - // See: http://code.google.com/p/google-diff-match-patch/wiki/API - convertChangesToDMP: function(changes){ - var ret = [], change; - for ( var i = 0; i < changes.length; i++) { - change = changes[i]; - ret.push([(change.added ? 1 : change.removed ? -1 : 0), change.value]); - } - return ret; - } - }; -})(); - -if (typeof module !== 'undefined') { - module.exports = JsDiff; -} diff --git a/tests/mocha/node_modules/diff/package.json b/tests/mocha/node_modules/diff/package.json deleted file mode 100644 index 867fd0ce84..0000000000 --- a/tests/mocha/node_modules/diff/package.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "diff", - "version": "1.0.7", - "description": "A javascript text diff implementation.", - "keywords": [ - "diff", - "javascript" - ], - "maintainers": [ - { - "name": "Kevin Decker", - "email": "kpdecker@gmail.com", - "url": "http://incaseofstairs.com/" - } - ], - "bugs": { - "url": "http://github.com/kpdecker/jsdiff/issues", - "email": "kpdecker@gmail.com" - }, - "licenses": [ - { - "type": "BSD", - "url": "http://github.com/kpdecker/jsdiff/blob/master/LICENSE" - } - ], - "repository": { - "type": "git", - "url": "git://github.com/kpdecker/jsdiff.git" - }, - "engines": { - "node": ">=0.3.1" - }, - "main": "./diff", - "scripts": { - "test": "mocha test/*.js" - }, - "dependencies": {}, - "devDependencies": { - "mocha": "~1.6", - "should": "~1.2" - }, - "optionalDependencies": {}, - "files": [ - "diff.js" - ], - "readme": "# jsdiff\n\n[![Build Status](https://secure.travis-ci.org/kpdecker/jsdiff.png)](http://travis-ci.org/kpdecker/jsdiff)\n\nA javascript text differencing implementation.\n\nBased on the algorithm proposed in\n[\"An O(ND) Difference Algorithm and its Variations\" (Myers, 1986)](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927).\n\n## Installation\n\n npm install diff\n\nor\n\n git clone git://github.com/kpdecker/jsdiff.git\n\n## API\n\n* JsDiff.diffChars(oldStr, newStr)\n Diffs two blocks of text, comparing character by character.\n\n Returns a list of change objects (See below).\n\n* JsDiff.diffWords(oldStr, newStr)\n Diffs two blocks of text, comparing word by word.\n\n Returns a list of change objects (See below).\n\n* JsDiff.diffLines(oldStr, newStr)\n Diffs two blocks of text, comparing line by line.\n\n Returns a list of change objects (See below).\n\n* JsDiff.diffCss(oldStr, newStr)\n Diffs two blocks of text, comparing CSS tokens.\n\n Returns a list of change objects (See below).\n\n* JsDiff.createPatch(fileName, oldStr, newStr, oldHeader, newHeader)\n Creates a unified diff patch.\n\n Parameters:\n * fileName : String to be output in the filename sections of the patch\n * oldStr : Original string value\n * newStr : New string value\n * oldHeader : Additional information to include in the old file header\n * newHeader : Additional information to include in thew new file header\n\n* JsDiff.applyPatch(oldStr, diffStr)\n Applies a unified diff patch.\n\n Return a string containing new version of provided data.\n\n* convertChangesToXML(changes)\n Converts a list of changes to a serialized XML format\n\n### Change Objects\nMany of the methods above return change objects. These objects are consist of the following fields:\n\n* value: Text content\n* added: True if the value was inserted into the new string\n* removed: True of the value was removed from the old string\n\nNote that some cases may omit a particular flag field. Comparison on the flag fields should always be done in a truthy or falsy manner.\n\n## [Example](http://kpdecker.github.com/jsdiff)\n\n## License\n\nSoftware License Agreement (BSD License)\n\nCopyright (c) 2009-2011, Kevin Decker kpdecker@gmail.com\n\nAll rights reserved.\n\nRedistribution and use of this software in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above\n copyright notice, this list of conditions and the\n following disclaimer.\n\n* Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the\n following disclaimer in the documentation and/or other\n materials provided with the distribution.\n\n* Neither the name of Kevin Decker nor the names of its\n contributors may be used to endorse or promote products\n derived from this software without specific prior\n written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR\nIMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND\nFITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\nCONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER\nIN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\nOF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n", - "readmeFilename": "README.md", - "homepage": "https://github.com/kpdecker/jsdiff", - "_id": "diff@1.0.7", - "_from": "diff@1.0.7" -} diff --git a/tests/mocha/node_modules/glob/.npmignore b/tests/mocha/node_modules/glob/.npmignore deleted file mode 100644 index 2af4b71c93..0000000000 --- a/tests/mocha/node_modules/glob/.npmignore +++ /dev/null @@ -1,2 +0,0 @@ -.*.swp -test/a/ diff --git a/tests/mocha/node_modules/glob/.travis.yml b/tests/mocha/node_modules/glob/.travis.yml deleted file mode 100644 index baa0031d50..0000000000 --- a/tests/mocha/node_modules/glob/.travis.yml +++ /dev/null @@ -1,3 +0,0 @@ -language: node_js -node_js: - - 0.8 diff --git a/tests/mocha/node_modules/glob/LICENSE b/tests/mocha/node_modules/glob/LICENSE deleted file mode 100644 index 0c44ae716d..0000000000 --- a/tests/mocha/node_modules/glob/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) Isaac Z. Schlueter ("Author") -All rights reserved. - -The BSD License - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/tests/mocha/node_modules/glob/README.md b/tests/mocha/node_modules/glob/README.md deleted file mode 100644 index cc69164510..0000000000 --- a/tests/mocha/node_modules/glob/README.md +++ /dev/null @@ -1,250 +0,0 @@ -# Glob - -Match files using the patterns the shell uses, like stars and stuff. - -This is a glob implementation in JavaScript. It uses the `minimatch` -library to do its matching. - -## Attention: node-glob users! - -The API has changed dramatically between 2.x and 3.x. This library is -now 100% JavaScript, and the integer flags have been replaced with an -options object. - -Also, there's an event emitter class, proper tests, and all the other -things you've come to expect from node modules. - -And best of all, no compilation! - -## Usage - -```javascript -var glob = require("glob") - -// options is optional -glob("**/*.js", options, function (er, files) { - // files is an array of filenames. - // If the `nonull` option is set, and nothing - // was found, then files is ["**/*.js"] - // er is an error object or null. -}) -``` - -## Features - -Please see the [minimatch -documentation](https://github.com/isaacs/minimatch) for more details. - -Supports these glob features: - -* Brace Expansion -* Extended glob matching -* "Globstar" `**` matching - -See: - -* `man sh` -* `man bash` -* `man 3 fnmatch` -* `man 5 gitignore` -* [minimatch documentation](https://github.com/isaacs/minimatch) - -## glob(pattern, [options], cb) - -* `pattern` {String} Pattern to be matched -* `options` {Object} -* `cb` {Function} - * `err` {Error | null} - * `matches` {Array} filenames found matching the pattern - -Perform an asynchronous glob search. - -## glob.sync(pattern, [options]) - -* `pattern` {String} Pattern to be matched -* `options` {Object} -* return: {Array} filenames found matching the pattern - -Perform a synchronous glob search. - -## Class: glob.Glob - -Create a Glob object by instanting the `glob.Glob` class. - -```javascript -var Glob = require("glob").Glob -var mg = new Glob(pattern, options, cb) -``` - -It's an EventEmitter, and starts walking the filesystem to find matches -immediately. - -### new glob.Glob(pattern, [options], [cb]) - -* `pattern` {String} pattern to search for -* `options` {Object} -* `cb` {Function} Called when an error occurs, or matches are found - * `err` {Error | null} - * `matches` {Array} filenames found matching the pattern - -Note that if the `sync` flag is set in the options, then matches will -be immediately available on the `g.found` member. - -### Properties - -* `minimatch` The minimatch object that the glob uses. -* `options` The options object passed in. -* `error` The error encountered. When an error is encountered, the - glob object is in an undefined state, and should be discarded. -* `aborted` Boolean which is set to true when calling `abort()`. There - is no way at this time to continue a glob search after aborting, but - you can re-use the statCache to avoid having to duplicate syscalls. -* `statCache` Collection of all the stat results the glob search - performed. -* `cache` Convenience object. Each field has the following possible - values: - * `false` - Path does not exist - * `true` - Path exists - * `1` - Path exists, and is not a directory - * `2` - Path exists, and is a directory - * `[file, entries, ...]` - Path exists, is a directory, and the - array value is the results of `fs.readdir` - -### Events - -* `end` When the matching is finished, this is emitted with all the - matches found. If the `nonull` option is set, and no match was found, - then the `matches` list contains the original pattern. The matches - are sorted, unless the `nosort` flag is set. -* `match` Every time a match is found, this is emitted with the matched. -* `error` Emitted when an unexpected error is encountered, or whenever - any fs error occurs if `options.strict` is set. -* `abort` When `abort()` is called, this event is raised. - -### Methods - -* `abort` Stop the search. - -### Options - -All the options that can be passed to Minimatch can also be passed to -Glob to change pattern matching behavior. Also, some have been added, -or have glob-specific ramifications. - -All options are false by default, unless otherwise noted. - -All options are added to the glob object, as well. - -* `cwd` The current working directory in which to search. Defaults - to `process.cwd()`. -* `root` The place where patterns starting with `/` will be mounted - onto. Defaults to `path.resolve(options.cwd, "/")` (`/` on Unix - systems, and `C:\` or some such on Windows.) -* `dot` Include `.dot` files in normal matches and `globstar` matches. - Note that an explicit dot in a portion of the pattern will always - match dot files. -* `nomount` By default, a pattern starting with a forward-slash will be - "mounted" onto the root setting, so that a valid filesystem path is - returned. Set this flag to disable that behavior. -* `mark` Add a `/` character to directory matches. Note that this - requires additional stat calls. -* `nosort` Don't sort the results. -* `stat` Set to true to stat *all* results. This reduces performance - somewhat, and is completely unnecessary, unless `readdir` is presumed - to be an untrustworthy indicator of file existence. It will cause - ELOOP to be triggered one level sooner in the case of cyclical - symbolic links. -* `silent` When an unusual error is encountered - when attempting to read a directory, a warning will be printed to - stderr. Set the `silent` option to true to suppress these warnings. -* `strict` When an unusual error is encountered - when attempting to read a directory, the process will just continue on - in search of other matches. Set the `strict` option to raise an error - in these cases. -* `cache` See `cache` property above. Pass in a previously generated - cache object to save some fs calls. -* `statCache` A cache of results of filesystem information, to prevent - unnecessary stat calls. While it should not normally be necessary to - set this, you may pass the statCache from one glob() call to the - options object of another, if you know that the filesystem will not - change between calls. (See "Race Conditions" below.) -* `sync` Perform a synchronous glob search. -* `nounique` In some cases, brace-expanded patterns can result in the - same file showing up multiple times in the result set. By default, - this implementation prevents duplicates in the result set. - Set this flag to disable that behavior. -* `nonull` Set to never return an empty set, instead returning a set - containing the pattern itself. This is the default in glob(3). -* `nocase` Perform a case-insensitive match. Note that case-insensitive - filesystems will sometimes result in glob returning results that are - case-insensitively matched anyway, since readdir and stat will not - raise an error. -* `debug` Set to enable debug logging in minimatch and glob. -* `globDebug` Set to enable debug logging in glob, but not minimatch. - -## Comparisons to other fnmatch/glob implementations - -While strict compliance with the existing standards is a worthwhile -goal, some discrepancies exist between node-glob and other -implementations, and are intentional. - -If the pattern starts with a `!` character, then it is negated. Set the -`nonegate` flag to suppress this behavior, and treat leading `!` -characters normally. This is perhaps relevant if you wish to start the -pattern with a negative extglob pattern like `!(a|B)`. Multiple `!` -characters at the start of a pattern will negate the pattern multiple -times. - -If a pattern starts with `#`, then it is treated as a comment, and -will not match anything. Use `\#` to match a literal `#` at the -start of a line, or set the `nocomment` flag to suppress this behavior. - -The double-star character `**` is supported by default, unless the -`noglobstar` flag is set. This is supported in the manner of bsdglob -and bash 4.1, where `**` only has special significance if it is the only -thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but -`a/**b` will not. - -If an escaped pattern has no matches, and the `nonull` flag is set, -then glob returns the pattern as-provided, rather than -interpreting the character escapes. For example, -`glob.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than -`"*a?"`. This is akin to setting the `nullglob` option in bash, except -that it does not resolve escaped pattern characters. - -If brace expansion is not disabled, then it is performed before any -other interpretation of the glob pattern. Thus, a pattern like -`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded -**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are -checked for validity. Since those two are valid, matching proceeds. - -## Windows - -**Please only use forward-slashes in glob expressions.** - -Though windows uses either `/` or `\` as its path separator, only `/` -characters are used by this glob implementation. You must use -forward-slashes **only** in glob expressions. Back-slashes will always -be interpreted as escape characters, not path separators. - -Results from absolute patterns such as `/foo/*` are mounted onto the -root setting using `path.join`. On windows, this will by default result -in `/foo/*` matching `C:\foo\bar.txt`. - -## Race Conditions - -Glob searching, by its very nature, is susceptible to race conditions, -since it relies on directory walking and such. - -As a result, it is possible that a file that exists when glob looks for -it may have been deleted or modified by the time it returns the result. - -As part of its internal implementation, this program caches all stat -and readdir calls that it makes, in order to cut down on system -overhead. However, this also makes it even more susceptible to races, -especially if the cache or statCache objects are reused between glob -calls. - -Users are thus advised not to use a glob result as a guarantee of -filesystem state in the face of rapid changes. For the vast majority -of operations, this is never a problem. diff --git a/tests/mocha/node_modules/glob/examples/g.js b/tests/mocha/node_modules/glob/examples/g.js deleted file mode 100644 index be122df002..0000000000 --- a/tests/mocha/node_modules/glob/examples/g.js +++ /dev/null @@ -1,9 +0,0 @@ -var Glob = require("../").Glob - -var pattern = "test/a/**/[cg]/../[cg]" -console.log(pattern) - -var mg = new Glob(pattern, {mark: true, sync:true}, function (er, matches) { - console.log("matches", matches) -}) -console.log("after") diff --git a/tests/mocha/node_modules/glob/examples/usr-local.js b/tests/mocha/node_modules/glob/examples/usr-local.js deleted file mode 100644 index 327a425e47..0000000000 --- a/tests/mocha/node_modules/glob/examples/usr-local.js +++ /dev/null @@ -1,9 +0,0 @@ -var Glob = require("../").Glob - -var pattern = "{./*/*,/*,/usr/local/*}" -console.log(pattern) - -var mg = new Glob(pattern, {mark: true}, function (er, matches) { - console.log("matches", matches) -}) -console.log("after") diff --git a/tests/mocha/node_modules/glob/glob.js b/tests/mocha/node_modules/glob/glob.js deleted file mode 100644 index f0118a4f47..0000000000 --- a/tests/mocha/node_modules/glob/glob.js +++ /dev/null @@ -1,675 +0,0 @@ -// Approach: -// -// 1. Get the minimatch set -// 2. For each pattern in the set, PROCESS(pattern) -// 3. Store matches per-set, then uniq them -// -// PROCESS(pattern) -// Get the first [n] items from pattern that are all strings -// Join these together. This is PREFIX. -// If there is no more remaining, then stat(PREFIX) and -// add to matches if it succeeds. END. -// readdir(PREFIX) as ENTRIES -// If fails, END -// If pattern[n] is GLOBSTAR -// // handle the case where the globstar match is empty -// // by pruning it out, and testing the resulting pattern -// PROCESS(pattern[0..n] + pattern[n+1 .. $]) -// // handle other cases. -// for ENTRY in ENTRIES (not dotfiles) -// // attach globstar + tail onto the entry -// PROCESS(pattern[0..n] + ENTRY + pattern[n .. $]) -// -// else // not globstar -// for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot) -// Test ENTRY against pattern[n] -// If fails, continue -// If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $]) -// -// Caveat: -// Cache all stats and readdirs results to minimize syscall. Since all -// we ever care about is existence and directory-ness, we can just keep -// `true` for files, and [children,...] for directories, or `false` for -// things that don't exist. - - - -module.exports = glob - -var fs = require("graceful-fs") -, minimatch = require("minimatch") -, Minimatch = minimatch.Minimatch -, inherits = require("inherits") -, EE = require("events").EventEmitter -, path = require("path") -, isDir = {} -, assert = require("assert").ok - -function glob (pattern, options, cb) { - if (typeof options === "function") cb = options, options = {} - if (!options) options = {} - - if (typeof options === "number") { - deprecated() - return - } - - var g = new Glob(pattern, options, cb) - return g.sync ? g.found : g -} - -glob.fnmatch = deprecated - -function deprecated () { - throw new Error("glob's interface has changed. Please see the docs.") -} - -glob.sync = globSync -function globSync (pattern, options) { - if (typeof options === "number") { - deprecated() - return - } - - options = options || {} - options.sync = true - return glob(pattern, options) -} - - -glob.Glob = Glob -inherits(Glob, EE) -function Glob (pattern, options, cb) { - if (!(this instanceof Glob)) { - return new Glob(pattern, options, cb) - } - - if (typeof cb === "function") { - this.on("error", cb) - this.on("end", function (matches) { - cb(null, matches) - }) - } - - options = options || {} - - this.EOF = {} - this._emitQueue = [] - - this.maxDepth = options.maxDepth || 1000 - this.maxLength = options.maxLength || Infinity - this.cache = options.cache || {} - this.statCache = options.statCache || {} - - this.changedCwd = false - var cwd = process.cwd() - if (!options.hasOwnProperty("cwd")) this.cwd = cwd - else { - this.cwd = options.cwd - this.changedCwd = path.resolve(options.cwd) !== cwd - } - - this.root = options.root || path.resolve(this.cwd, "/") - this.root = path.resolve(this.root) - if (process.platform === "win32") - this.root = this.root.replace(/\\/g, "/") - - this.nomount = !!options.nomount - - if (!pattern) { - throw new Error("must provide pattern") - } - - // base-matching: just use globstar for that. - if (options.matchBase && -1 === pattern.indexOf("/")) { - if (options.noglobstar) { - throw new Error("base matching requires globstar") - } - pattern = "**/" + pattern - } - - this.strict = options.strict !== false - this.dot = !!options.dot - this.mark = !!options.mark - this.sync = !!options.sync - this.nounique = !!options.nounique - this.nonull = !!options.nonull - this.nosort = !!options.nosort - this.nocase = !!options.nocase - this.stat = !!options.stat - - this.debug = !!options.debug || !!options.globDebug - if (this.debug) - this.log = console.error - - this.silent = !!options.silent - - var mm = this.minimatch = new Minimatch(pattern, options) - this.options = mm.options - pattern = this.pattern = mm.pattern - - this.error = null - this.aborted = false - - // list of all the patterns that ** has resolved do, so - // we can avoid visiting multiple times. - this._globstars = {} - - EE.call(this) - - // process each pattern in the minimatch set - var n = this.minimatch.set.length - - // The matches are stored as {: true,...} so that - // duplicates are automagically pruned. - // Later, we do an Object.keys() on these. - // Keep them as a list so we can fill in when nonull is set. - this.matches = new Array(n) - - this.minimatch.set.forEach(iterator.bind(this)) - function iterator (pattern, i, set) { - this._process(pattern, 0, i, function (er) { - if (er) this.emit("error", er) - if (-- n <= 0) this._finish() - }) - } -} - -Glob.prototype.log = function () {} - -Glob.prototype._finish = function () { - assert(this instanceof Glob) - - var nou = this.nounique - , all = nou ? [] : {} - - for (var i = 0, l = this.matches.length; i < l; i ++) { - var matches = this.matches[i] - this.log("matches[%d] =", i, matches) - // do like the shell, and spit out the literal glob - if (!matches) { - if (this.nonull) { - var literal = this.minimatch.globSet[i] - if (nou) all.push(literal) - else all[literal] = true - } - } else { - // had matches - var m = Object.keys(matches) - if (nou) all.push.apply(all, m) - else m.forEach(function (m) { - all[m] = true - }) - } - } - - if (!nou) all = Object.keys(all) - - if (!this.nosort) { - all = all.sort(this.nocase ? alphasorti : alphasort) - } - - if (this.mark) { - // at *some* point we statted all of these - all = all.map(function (m) { - var sc = this.cache[m] - if (!sc) - return m - var isDir = (Array.isArray(sc) || sc === 2) - if (isDir && m.slice(-1) !== "/") { - return m + "/" - } - if (!isDir && m.slice(-1) === "/") { - return m.replace(/\/+$/, "") - } - return m - }, this) - } - - this.log("emitting end", all) - - this.EOF = this.found = all - this.emitMatch(this.EOF) -} - -function alphasorti (a, b) { - a = a.toLowerCase() - b = b.toLowerCase() - return alphasort(a, b) -} - -function alphasort (a, b) { - return a > b ? 1 : a < b ? -1 : 0 -} - -Glob.prototype.abort = function () { - this.aborted = true - this.emit("abort") -} - -Glob.prototype.pause = function () { - if (this.paused) return - if (this.sync) - this.emit("error", new Error("Can't pause/resume sync glob")) - this.paused = true - this.emit("pause") -} - -Glob.prototype.resume = function () { - if (!this.paused) return - if (this.sync) - this.emit("error", new Error("Can't pause/resume sync glob")) - this.paused = false - this.emit("resume") - this._processEmitQueue() - //process.nextTick(this.emit.bind(this, "resume")) -} - -Glob.prototype.emitMatch = function (m) { - if (!this.stat || this.statCache[m] || m === this.EOF) { - this._emitQueue.push(m) - this._processEmitQueue() - } else { - this._stat(m, function(exists, isDir) { - if (exists) { - this._emitQueue.push(m) - this._processEmitQueue() - } - }) - } -} - -Glob.prototype._processEmitQueue = function (m) { - while (!this._processingEmitQueue && - !this.paused) { - this._processingEmitQueue = true - var m = this._emitQueue.shift() - if (!m) { - this._processingEmitQueue = false - break - } - - this.log('emit!', m === this.EOF ? "end" : "match") - - this.emit(m === this.EOF ? "end" : "match", m) - this._processingEmitQueue = false - } -} - -Glob.prototype._process = function (pattern, depth, index, cb_) { - assert(this instanceof Glob) - - var cb = function cb (er, res) { - assert(this instanceof Glob) - if (this.paused) { - if (!this._processQueue) { - this._processQueue = [] - this.once("resume", function () { - var q = this._processQueue - this._processQueue = null - q.forEach(function (cb) { cb() }) - }) - } - this._processQueue.push(cb_.bind(this, er, res)) - } else { - cb_.call(this, er, res) - } - }.bind(this) - - if (this.aborted) return cb() - - if (depth > this.maxDepth) return cb() - - // Get the first [n] parts of pattern that are all strings. - var n = 0 - while (typeof pattern[n] === "string") { - n ++ - } - // now n is the index of the first one that is *not* a string. - - // see if there's anything else - var prefix - switch (n) { - // if not, then this is rather simple - case pattern.length: - prefix = pattern.join("/") - this._stat(prefix, function (exists, isDir) { - // either it's there, or it isn't. - // nothing more to do, either way. - if (exists) { - if (prefix && isAbsolute(prefix) && !this.nomount) { - if (prefix.charAt(0) === "/") { - prefix = path.join(this.root, prefix) - } else { - prefix = path.resolve(this.root, prefix) - } - } - - if (process.platform === "win32") - prefix = prefix.replace(/\\/g, "/") - - this.matches[index] = this.matches[index] || {} - this.matches[index][prefix] = true - this.emitMatch(prefix) - } - return cb() - }) - return - - case 0: - // pattern *starts* with some non-trivial item. - // going to readdir(cwd), but not include the prefix in matches. - prefix = null - break - - default: - // pattern has some string bits in the front. - // whatever it starts with, whether that's "absolute" like /foo/bar, - // or "relative" like "../baz" - prefix = pattern.slice(0, n) - prefix = prefix.join("/") - break - } - - // get the list of entries. - var read - if (prefix === null) read = "." - else if (isAbsolute(prefix) || isAbsolute(pattern.join("/"))) { - if (!prefix || !isAbsolute(prefix)) { - prefix = path.join("/", prefix) - } - read = prefix = path.resolve(prefix) - - // if (process.platform === "win32") - // read = prefix = prefix.replace(/^[a-zA-Z]:|\\/g, "/") - - this.log('absolute: ', prefix, this.root, pattern, read) - } else { - read = prefix - } - - this.log('readdir(%j)', read, this.cwd, this.root) - - return this._readdir(read, function (er, entries) { - if (er) { - // not a directory! - // this means that, whatever else comes after this, it can never match - return cb() - } - - // globstar is special - if (pattern[n] === minimatch.GLOBSTAR) { - // test without the globstar, and with every child both below - // and replacing the globstar. - var s = [ pattern.slice(0, n).concat(pattern.slice(n + 1)) ] - entries.forEach(function (e) { - if (e.charAt(0) === "." && !this.dot) return - // instead of the globstar - s.push(pattern.slice(0, n).concat(e).concat(pattern.slice(n + 1))) - // below the globstar - s.push(pattern.slice(0, n).concat(e).concat(pattern.slice(n))) - }, this) - - s = s.filter(function (pattern) { - var key = gsKey(pattern) - var seen = !this._globstars[key] - this._globstars[key] = true - return seen - }, this) - - if (!s.length) - return cb() - - // now asyncForEach over this - var l = s.length - , errState = null - s.forEach(function (gsPattern) { - this._process(gsPattern, depth + 1, index, function (er) { - if (errState) return - if (er) return cb(errState = er) - if (--l <= 0) return cb() - }) - }, this) - - return - } - - // not a globstar - // It will only match dot entries if it starts with a dot, or if - // dot is set. Stuff like @(.foo|.bar) isn't allowed. - var pn = pattern[n] - var rawGlob = pattern[n]._glob - , dotOk = this.dot || rawGlob.charAt(0) === "." - - entries = entries.filter(function (e) { - return (e.charAt(0) !== "." || dotOk) && - e.match(pattern[n]) - }) - - // If n === pattern.length - 1, then there's no need for the extra stat - // *unless* the user has specified "mark" or "stat" explicitly. - // We know that they exist, since the readdir returned them. - if (n === pattern.length - 1 && - !this.mark && - !this.stat) { - entries.forEach(function (e) { - if (prefix) { - if (prefix !== "/") e = prefix + "/" + e - else e = prefix + e - } - if (e.charAt(0) === "/" && !this.nomount) { - e = path.join(this.root, e) - } - - if (process.platform === "win32") - e = e.replace(/\\/g, "/") - - this.matches[index] = this.matches[index] || {} - this.matches[index][e] = true - this.emitMatch(e) - }, this) - return cb.call(this) - } - - - // now test all the remaining entries as stand-ins for that part - // of the pattern. - var l = entries.length - , errState = null - if (l === 0) return cb() // no matches possible - entries.forEach(function (e) { - var p = pattern.slice(0, n).concat(e).concat(pattern.slice(n + 1)) - this._process(p, depth + 1, index, function (er) { - if (errState) return - if (er) return cb(errState = er) - if (--l === 0) return cb.call(this) - }) - }, this) - }) - -} - -function gsKey (pattern) { - return '**' + pattern.map(function (p) { - return (p === minimatch.GLOBSTAR) ? '**' : (''+p) - }).join('/') -} - -Glob.prototype._stat = function (f, cb) { - assert(this instanceof Glob) - var abs = f - if (f.charAt(0) === "/") { - abs = path.join(this.root, f) - } else if (this.changedCwd) { - abs = path.resolve(this.cwd, f) - } - - if (f.length > this.maxLength) { - var er = new Error("Path name too long") - er.code = "ENAMETOOLONG" - er.path = f - return this._afterStat(f, abs, cb, er) - } - - this.log('stat', [this.cwd, f, '=', abs]) - - if (!this.stat && this.cache.hasOwnProperty(f)) { - var exists = this.cache[f] - , isDir = exists && (Array.isArray(exists) || exists === 2) - if (this.sync) return cb.call(this, !!exists, isDir) - return process.nextTick(cb.bind(this, !!exists, isDir)) - } - - var stat = this.statCache[abs] - if (this.sync || stat) { - var er - try { - stat = fs.statSync(abs) - } catch (e) { - er = e - } - this._afterStat(f, abs, cb, er, stat) - } else { - fs.stat(abs, this._afterStat.bind(this, f, abs, cb)) - } -} - -Glob.prototype._afterStat = function (f, abs, cb, er, stat) { - var exists - assert(this instanceof Glob) - - if (abs.slice(-1) === "/" && stat && !stat.isDirectory()) { - this.log("should be ENOTDIR, fake it") - - er = new Error("ENOTDIR, not a directory '" + abs + "'") - er.path = abs - er.code = "ENOTDIR" - stat = null - } - - var emit = !this.statCache[abs] - this.statCache[abs] = stat - - if (er || !stat) { - exists = false - } else { - exists = stat.isDirectory() ? 2 : 1 - if (emit) - this.emit('stat', f, stat) - } - this.cache[f] = this.cache[f] || exists - cb.call(this, !!exists, exists === 2) -} - -Glob.prototype._readdir = function (f, cb) { - assert(this instanceof Glob) - var abs = f - if (f.charAt(0) === "/") { - abs = path.join(this.root, f) - } else if (isAbsolute(f)) { - abs = f - } else if (this.changedCwd) { - abs = path.resolve(this.cwd, f) - } - - if (f.length > this.maxLength) { - var er = new Error("Path name too long") - er.code = "ENAMETOOLONG" - er.path = f - return this._afterReaddir(f, abs, cb, er) - } - - this.log('readdir', [this.cwd, f, abs]) - if (this.cache.hasOwnProperty(f)) { - var c = this.cache[f] - if (Array.isArray(c)) { - if (this.sync) return cb.call(this, null, c) - return process.nextTick(cb.bind(this, null, c)) - } - - if (!c || c === 1) { - // either ENOENT or ENOTDIR - var code = c ? "ENOTDIR" : "ENOENT" - , er = new Error((c ? "Not a directory" : "Not found") + ": " + f) - er.path = f - er.code = code - this.log(f, er) - if (this.sync) return cb.call(this, er) - return process.nextTick(cb.bind(this, er)) - } - - // at this point, c === 2, meaning it's a dir, but we haven't - // had to read it yet, or c === true, meaning it's *something* - // but we don't have any idea what. Need to read it, either way. - } - - if (this.sync) { - var er, entries - try { - entries = fs.readdirSync(abs) - } catch (e) { - er = e - } - return this._afterReaddir(f, abs, cb, er, entries) - } - - fs.readdir(abs, this._afterReaddir.bind(this, f, abs, cb)) -} - -Glob.prototype._afterReaddir = function (f, abs, cb, er, entries) { - assert(this instanceof Glob) - if (entries && !er) { - this.cache[f] = entries - // if we haven't asked to stat everything for suresies, then just - // assume that everything in there exists, so we can avoid - // having to stat it a second time. This also gets us one step - // further into ELOOP territory. - if (!this.mark && !this.stat) { - entries.forEach(function (e) { - if (f === "/") e = f + e - else e = f + "/" + e - this.cache[e] = true - }, this) - } - - return cb.call(this, er, entries) - } - - // now handle errors, and cache the information - if (er) switch (er.code) { - case "ENOTDIR": // totally normal. means it *does* exist. - this.cache[f] = 1 - return cb.call(this, er) - case "ENOENT": // not terribly unusual - case "ELOOP": - case "ENAMETOOLONG": - case "UNKNOWN": - this.cache[f] = false - return cb.call(this, er) - default: // some unusual error. Treat as failure. - this.cache[f] = false - if (this.strict) this.emit("error", er) - if (!this.silent) console.error("glob error", er) - return cb.call(this, er) - } -} - -var isAbsolute = process.platform === "win32" ? absWin : absUnix - -function absWin (p) { - if (absUnix(p)) return true - // pull off the device/UNC bit from a windows path. - // from node's lib/path.js - var splitDeviceRe = - /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/ - , result = splitDeviceRe.exec(p) - , device = result[1] || '' - , isUnc = device && device.charAt(1) !== ':' - , isAbsolute = !!result[2] || isUnc // UNC paths are always absolute - - return isAbsolute -} - -function absUnix (p) { - return p.charAt(0) === "/" || p === "" -} diff --git a/tests/mocha/node_modules/glob/node_modules/graceful-fs/.npmignore b/tests/mocha/node_modules/glob/node_modules/graceful-fs/.npmignore deleted file mode 100644 index c2658d7d1b..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/graceful-fs/.npmignore +++ /dev/null @@ -1 +0,0 @@ -node_modules/ diff --git a/tests/mocha/node_modules/glob/node_modules/graceful-fs/LICENSE b/tests/mocha/node_modules/glob/node_modules/graceful-fs/LICENSE deleted file mode 100644 index 0c44ae716d..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/graceful-fs/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) Isaac Z. Schlueter ("Author") -All rights reserved. - -The BSD License - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/tests/mocha/node_modules/glob/node_modules/graceful-fs/README.md b/tests/mocha/node_modules/glob/node_modules/graceful-fs/README.md deleted file mode 100644 index eb1a109356..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/graceful-fs/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# graceful-fs - -graceful-fs functions as a drop-in replacement for the fs module, -making various improvements. - -The improvements are meant to normalize behavior across different -platforms and environments, and to make filesystem access more -resilient to errors. - -## Improvements over fs module - -graceful-fs: - -* Queues up `open` and `readdir` calls, and retries them once - something closes if there is an EMFILE error from too many file - descriptors. -* fixes `lchmod` for Node versions prior to 0.6.2. -* implements `fs.lutimes` if possible. Otherwise it becomes a noop. -* ignores `EINVAL` and `EPERM` errors in `chown`, `fchown` or - `lchown` if the user isn't root. -* makes `lchmod` and `lchown` become noops, if not available. -* retries reading a file if `read` results in EAGAIN error. - -On Windows, it retries renaming a file for up to one second if `EACCESS` -or `EPERM` error occurs, likely because antivirus software has locked -the directory. diff --git a/tests/mocha/node_modules/glob/node_modules/graceful-fs/graceful-fs.js b/tests/mocha/node_modules/glob/node_modules/graceful-fs/graceful-fs.js deleted file mode 100644 index c84db91019..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/graceful-fs/graceful-fs.js +++ /dev/null @@ -1,160 +0,0 @@ -// Monkey-patching the fs module. -// It's ugly, but there is simply no other way to do this. -var fs = module.exports = require('fs') - -var assert = require('assert') - -// fix up some busted stuff, mostly on windows and old nodes -require('./polyfills.js') - -// The EMFILE enqueuing stuff - -var util = require('util') - -function noop () {} - -var debug = noop -if (util.debuglog) - debug = util.debuglog('gfs') -else if (/\bgfs\b/i.test(process.env.NODE_DEBUG || '')) - debug = function() { - var m = util.format.apply(util, arguments) - m = 'GFS: ' + m.split(/\n/).join('\nGFS: ') - console.error(m) - } - -if (/\bgfs\b/i.test(process.env.NODE_DEBUG || '')) { - process.on('exit', function() { - debug('fds', fds) - debug(queue) - assert.equal(queue.length, 0) - }) -} - - -var originalOpen = fs.open -fs.open = open - -function open(path, flags, mode, cb) { - if (typeof mode === "function") cb = mode, mode = null - if (typeof cb !== "function") cb = noop - new OpenReq(path, flags, mode, cb) -} - -function OpenReq(path, flags, mode, cb) { - this.path = path - this.flags = flags - this.mode = mode - this.cb = cb - Req.call(this) -} - -util.inherits(OpenReq, Req) - -OpenReq.prototype.process = function() { - originalOpen.call(fs, this.path, this.flags, this.mode, this.done) -} - -var fds = {} -OpenReq.prototype.done = function(er, fd) { - debug('open done', er, fd) - if (fd) - fds['fd' + fd] = this.path - Req.prototype.done.call(this, er, fd) -} - - -var originalReaddir = fs.readdir -fs.readdir = readdir - -function readdir(path, cb) { - if (typeof cb !== "function") cb = noop - new ReaddirReq(path, cb) -} - -function ReaddirReq(path, cb) { - this.path = path - this.cb = cb - Req.call(this) -} - -util.inherits(ReaddirReq, Req) - -ReaddirReq.prototype.process = function() { - originalReaddir.call(fs, this.path, this.done) -} - -ReaddirReq.prototype.done = function(er, files) { - if (files && files.sort) - files = files.sort() - Req.prototype.done.call(this, er, files) - onclose() -} - - -var originalClose = fs.close -fs.close = close - -function close (fd, cb) { - debug('close', fd) - if (typeof cb !== "function") cb = noop - delete fds['fd' + fd] - originalClose.call(fs, fd, function(er) { - onclose() - cb(er) - }) -} - - -var originalCloseSync = fs.closeSync -fs.closeSync = closeSync - -function closeSync (fd) { - try { - return originalCloseSync(fd) - } finally { - onclose() - } -} - - -// Req class -function Req () { - // start processing - this.done = this.done.bind(this) - this.failures = 0 - this.process() -} - -Req.prototype.done = function (er, result) { - var tryAgain = false - if (er) { - var code = er.code - var tryAgain = code === "EMFILE" - if (process.platform === "win32") - tryAgain = tryAgain || code === "OK" - } - - if (tryAgain) { - this.failures ++ - enqueue(this) - } else { - var cb = this.cb - cb(er, result) - } -} - -var queue = [] - -function enqueue(req) { - queue.push(req) - debug('enqueue %d %s', queue.length, req.constructor.name, req) -} - -function onclose() { - var req = queue.shift() - if (req) { - debug('process', req.constructor.name, req) - req.process() - } -} diff --git a/tests/mocha/node_modules/glob/node_modules/graceful-fs/package.json b/tests/mocha/node_modules/glob/node_modules/graceful-fs/package.json deleted file mode 100644 index 89d7462c3e..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/graceful-fs/package.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "author": { - "name": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/" - }, - "name": "graceful-fs", - "description": "A drop-in replacement for fs, making various improvements.", - "version": "2.0.3", - "repository": { - "type": "git", - "url": "git://github.com/isaacs/node-graceful-fs.git" - }, - "main": "graceful-fs.js", - "engines": { - "node": ">=0.4.0" - }, - "directories": { - "test": "test" - }, - "scripts": { - "test": "tap test/*.js" - }, - "keywords": [ - "fs", - "module", - "reading", - "retry", - "retries", - "queue", - "error", - "errors", - "handling", - "EMFILE", - "EAGAIN", - "EINVAL", - "EPERM", - "EACCESS" - ], - "license": "BSD", - "readme": "# graceful-fs\n\ngraceful-fs functions as a drop-in replacement for the fs module,\nmaking various improvements.\n\nThe improvements are meant to normalize behavior across different\nplatforms and environments, and to make filesystem access more\nresilient to errors.\n\n## Improvements over fs module\n\ngraceful-fs:\n\n* Queues up `open` and `readdir` calls, and retries them once\n something closes if there is an EMFILE error from too many file\n descriptors.\n* fixes `lchmod` for Node versions prior to 0.6.2.\n* implements `fs.lutimes` if possible. Otherwise it becomes a noop.\n* ignores `EINVAL` and `EPERM` errors in `chown`, `fchown` or\n `lchown` if the user isn't root.\n* makes `lchmod` and `lchown` become noops, if not available.\n* retries reading a file if `read` results in EAGAIN error.\n\nOn Windows, it retries renaming a file for up to one second if `EACCESS`\nor `EPERM` error occurs, likely because antivirus software has locked\nthe directory.\n", - "readmeFilename": "README.md", - "bugs": { - "url": "https://github.com/isaacs/node-graceful-fs/issues" - }, - "homepage": "https://github.com/isaacs/node-graceful-fs", - "_id": "graceful-fs@2.0.3", - "_from": "graceful-fs@~2.0.0" -} diff --git a/tests/mocha/node_modules/glob/node_modules/graceful-fs/polyfills.js b/tests/mocha/node_modules/glob/node_modules/graceful-fs/polyfills.js deleted file mode 100644 index afc83b3f2c..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/graceful-fs/polyfills.js +++ /dev/null @@ -1,228 +0,0 @@ -var fs = require('fs') -var constants = require('constants') - -var origCwd = process.cwd -var cwd = null -process.cwd = function() { - if (!cwd) - cwd = origCwd.call(process) - return cwd -} -var chdir = process.chdir -process.chdir = function(d) { - cwd = null - chdir.call(process, d) -} - -// (re-)implement some things that are known busted or missing. - -// lchmod, broken prior to 0.6.2 -// back-port the fix here. -if (constants.hasOwnProperty('O_SYMLINK') && - process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) { - fs.lchmod = function (path, mode, callback) { - callback = callback || noop - fs.open( path - , constants.O_WRONLY | constants.O_SYMLINK - , mode - , function (err, fd) { - if (err) { - callback(err) - return - } - // prefer to return the chmod error, if one occurs, - // but still try to close, and report closing errors if they occur. - fs.fchmod(fd, mode, function (err) { - fs.close(fd, function(err2) { - callback(err || err2) - }) - }) - }) - } - - fs.lchmodSync = function (path, mode) { - var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode) - - // prefer to return the chmod error, if one occurs, - // but still try to close, and report closing errors if they occur. - var err, err2 - try { - var ret = fs.fchmodSync(fd, mode) - } catch (er) { - err = er - } - try { - fs.closeSync(fd) - } catch (er) { - err2 = er - } - if (err || err2) throw (err || err2) - return ret - } -} - - -// lutimes implementation, or no-op -if (!fs.lutimes) { - if (constants.hasOwnProperty("O_SYMLINK")) { - fs.lutimes = function (path, at, mt, cb) { - fs.open(path, constants.O_SYMLINK, function (er, fd) { - cb = cb || noop - if (er) return cb(er) - fs.futimes(fd, at, mt, function (er) { - fs.close(fd, function (er2) { - return cb(er || er2) - }) - }) - }) - } - - fs.lutimesSync = function (path, at, mt) { - var fd = fs.openSync(path, constants.O_SYMLINK) - , err - , err2 - , ret - - try { - var ret = fs.futimesSync(fd, at, mt) - } catch (er) { - err = er - } - try { - fs.closeSync(fd) - } catch (er) { - err2 = er - } - if (err || err2) throw (err || err2) - return ret - } - - } else if (fs.utimensat && constants.hasOwnProperty("AT_SYMLINK_NOFOLLOW")) { - // maybe utimensat will be bound soonish? - fs.lutimes = function (path, at, mt, cb) { - fs.utimensat(path, at, mt, constants.AT_SYMLINK_NOFOLLOW, cb) - } - - fs.lutimesSync = function (path, at, mt) { - return fs.utimensatSync(path, at, mt, constants.AT_SYMLINK_NOFOLLOW) - } - - } else { - fs.lutimes = function (_a, _b, _c, cb) { process.nextTick(cb) } - fs.lutimesSync = function () {} - } -} - - -// https://github.com/isaacs/node-graceful-fs/issues/4 -// Chown should not fail on einval or eperm if non-root. - -fs.chown = chownFix(fs.chown) -fs.fchown = chownFix(fs.fchown) -fs.lchown = chownFix(fs.lchown) - -fs.chownSync = chownFixSync(fs.chownSync) -fs.fchownSync = chownFixSync(fs.fchownSync) -fs.lchownSync = chownFixSync(fs.lchownSync) - -function chownFix (orig) { - if (!orig) return orig - return function (target, uid, gid, cb) { - return orig.call(fs, target, uid, gid, function (er, res) { - if (chownErOk(er)) er = null - cb(er, res) - }) - } -} - -function chownFixSync (orig) { - if (!orig) return orig - return function (target, uid, gid) { - try { - return orig.call(fs, target, uid, gid) - } catch (er) { - if (!chownErOk(er)) throw er - } - } -} - -function chownErOk (er) { - // if there's no getuid, or if getuid() is something other than 0, - // and the error is EINVAL or EPERM, then just ignore it. - // This specific case is a silent failure in cp, install, tar, - // and most other unix tools that manage permissions. - // When running as root, or if other types of errors are encountered, - // then it's strict. - if (!er || (!process.getuid || process.getuid() !== 0) - && (er.code === "EINVAL" || er.code === "EPERM")) return true -} - - -// if lchmod/lchown do not exist, then make them no-ops -if (!fs.lchmod) { - fs.lchmod = function (path, mode, cb) { - process.nextTick(cb) - } - fs.lchmodSync = function () {} -} -if (!fs.lchown) { - fs.lchown = function (path, uid, gid, cb) { - process.nextTick(cb) - } - fs.lchownSync = function () {} -} - - - -// on Windows, A/V software can lock the directory, causing this -// to fail with an EACCES or EPERM if the directory contains newly -// created files. Try again on failure, for up to 1 second. -if (process.platform === "win32") { - var rename_ = fs.rename - fs.rename = function rename (from, to, cb) { - var start = Date.now() - rename_(from, to, function CB (er) { - if (er - && (er.code === "EACCES" || er.code === "EPERM") - && Date.now() - start < 1000) { - return rename_(from, to, CB) - } - cb(er) - }) - } -} - - -// if read() returns EAGAIN, then just try it again. -var read = fs.read -fs.read = function (fd, buffer, offset, length, position, callback_) { - var callback - if (callback_ && typeof callback_ === 'function') { - var eagCounter = 0 - callback = function (er, _, __) { - if (er && er.code === 'EAGAIN' && eagCounter < 10) { - eagCounter ++ - return read.call(fs, fd, buffer, offset, length, position, callback) - } - callback_.apply(this, arguments) - } - } - return read.call(fs, fd, buffer, offset, length, position, callback) -} - -var readSync = fs.readSync -fs.readSync = function (fd, buffer, offset, length, position) { - var eagCounter = 0 - while (true) { - try { - return readSync.call(fs, fd, buffer, offset, length, position) - } catch (er) { - if (er.code === 'EAGAIN' && eagCounter < 10) { - eagCounter ++ - continue - } - throw er - } - } -} - diff --git a/tests/mocha/node_modules/glob/node_modules/graceful-fs/test/open.js b/tests/mocha/node_modules/glob/node_modules/graceful-fs/test/open.js deleted file mode 100644 index 104f36b0b9..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/graceful-fs/test/open.js +++ /dev/null @@ -1,39 +0,0 @@ -var test = require('tap').test -var fs = require('../graceful-fs.js') - -test('graceful fs is monkeypatched fs', function (t) { - t.equal(fs, require('fs')) - t.end() -}) - -test('open an existing file works', function (t) { - var fd = fs.openSync(__filename, 'r') - fs.closeSync(fd) - fs.open(__filename, 'r', function (er, fd) { - if (er) throw er - fs.close(fd, function (er) { - if (er) throw er - t.pass('works') - t.end() - }) - }) -}) - -test('open a non-existing file throws', function (t) { - var er - try { - var fd = fs.openSync('this file does not exist', 'r') - } catch (x) { - er = x - } - t.ok(er, 'should throw') - t.notOk(fd, 'should not get an fd') - t.equal(er.code, 'ENOENT') - - fs.open('neither does this file', 'r', function (er, fd) { - t.ok(er, 'should throw') - t.notOk(fd, 'should not get an fd') - t.equal(er.code, 'ENOENT') - t.end() - }) -}) diff --git a/tests/mocha/node_modules/glob/node_modules/graceful-fs/test/readdir-sort.js b/tests/mocha/node_modules/glob/node_modules/graceful-fs/test/readdir-sort.js deleted file mode 100644 index aeaedf1c16..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/graceful-fs/test/readdir-sort.js +++ /dev/null @@ -1,21 +0,0 @@ -var test = require("tap").test -var fs = require("fs") - -var readdir = fs.readdir -fs.readdir = function(path, cb) { - process.nextTick(function() { - cb(null, ["b", "z", "a"]) - }) -} - -var g = require("../") - -test("readdir reorder", function (t) { - g.readdir("whatevers", function (er, files) { - if (er) - throw er - console.error(files) - t.same(files, [ "a", "b", "z" ]) - t.end() - }) -}) diff --git a/tests/mocha/node_modules/glob/node_modules/inherits/LICENSE b/tests/mocha/node_modules/glob/node_modules/inherits/LICENSE deleted file mode 100644 index dea3013d67..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/inherits/LICENSE +++ /dev/null @@ -1,16 +0,0 @@ -The ISC License - -Copyright (c) Isaac Z. Schlueter - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. - diff --git a/tests/mocha/node_modules/glob/node_modules/inherits/README.md b/tests/mocha/node_modules/glob/node_modules/inherits/README.md deleted file mode 100644 index b1c5665855..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/inherits/README.md +++ /dev/null @@ -1,42 +0,0 @@ -Browser-friendly inheritance fully compatible with standard node.js -[inherits](http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor). - -This package exports standard `inherits` from node.js `util` module in -node environment, but also provides alternative browser-friendly -implementation through [browser -field](https://gist.github.com/shtylman/4339901). Alternative -implementation is a literal copy of standard one located in standalone -module to avoid requiring of `util`. It also has a shim for old -browsers with no `Object.create` support. - -While keeping you sure you are using standard `inherits` -implementation in node.js environment, it allows bundlers such as -[browserify](https://github.com/substack/node-browserify) to not -include full `util` package to your client code if all you need is -just `inherits` function. It worth, because browser shim for `util` -package is large and `inherits` is often the single function you need -from it. - -It's recommended to use this package instead of -`require('util').inherits` for any code that has chances to be used -not only in node.js but in browser too. - -## usage - -```js -var inherits = require('inherits'); -// then use exactly as the standard one -``` - -## note on version ~1.0 - -Version ~1.0 had completely different motivation and is not compatible -neither with 2.0 nor with standard node.js `inherits`. - -If you are using version ~1.0 and planning to switch to ~2.0, be -careful: - -* new version uses `super_` instead of `super` for referencing - superclass -* new version overwrites current prototype while old one preserves any - existing fields on it diff --git a/tests/mocha/node_modules/glob/node_modules/inherits/inherits.js b/tests/mocha/node_modules/glob/node_modules/inherits/inherits.js deleted file mode 100644 index 29f5e24f57..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/inherits/inherits.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('util').inherits diff --git a/tests/mocha/node_modules/glob/node_modules/inherits/inherits_browser.js b/tests/mocha/node_modules/glob/node_modules/inherits/inherits_browser.js deleted file mode 100644 index c1e78a75e6..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/inherits/inherits_browser.js +++ /dev/null @@ -1,23 +0,0 @@ -if (typeof Object.create === 'function') { - // implementation from standard node.js 'util' module - module.exports = function inherits(ctor, superCtor) { - ctor.super_ = superCtor - ctor.prototype = Object.create(superCtor.prototype, { - constructor: { - value: ctor, - enumerable: false, - writable: true, - configurable: true - } - }); - }; -} else { - // old school shim for old browsers - module.exports = function inherits(ctor, superCtor) { - ctor.super_ = superCtor - var TempCtor = function () {} - TempCtor.prototype = superCtor.prototype - ctor.prototype = new TempCtor() - ctor.prototype.constructor = ctor - } -} diff --git a/tests/mocha/node_modules/glob/node_modules/inherits/package.json b/tests/mocha/node_modules/glob/node_modules/inherits/package.json deleted file mode 100644 index c532ca6d14..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/inherits/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "inherits", - "description": "Browser-friendly inheritance fully compatible with standard node.js inherits()", - "version": "2.0.1", - "keywords": [ - "inheritance", - "class", - "klass", - "oop", - "object-oriented", - "inherits", - "browser", - "browserify" - ], - "main": "./inherits.js", - "browser": "./inherits_browser.js", - "repository": { - "type": "git", - "url": "git://github.com/isaacs/inherits" - }, - "license": "ISC", - "scripts": { - "test": "node test" - }, - "readme": "Browser-friendly inheritance fully compatible with standard node.js\n[inherits](http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor).\n\nThis package exports standard `inherits` from node.js `util` module in\nnode environment, but also provides alternative browser-friendly\nimplementation through [browser\nfield](https://gist.github.com/shtylman/4339901). Alternative\nimplementation is a literal copy of standard one located in standalone\nmodule to avoid requiring of `util`. It also has a shim for old\nbrowsers with no `Object.create` support.\n\nWhile keeping you sure you are using standard `inherits`\nimplementation in node.js environment, it allows bundlers such as\n[browserify](https://github.com/substack/node-browserify) to not\ninclude full `util` package to your client code if all you need is\njust `inherits` function. It worth, because browser shim for `util`\npackage is large and `inherits` is often the single function you need\nfrom it.\n\nIt's recommended to use this package instead of\n`require('util').inherits` for any code that has chances to be used\nnot only in node.js but in browser too.\n\n## usage\n\n```js\nvar inherits = require('inherits');\n// then use exactly as the standard one\n```\n\n## note on version ~1.0\n\nVersion ~1.0 had completely different motivation and is not compatible\nneither with 2.0 nor with standard node.js `inherits`.\n\nIf you are using version ~1.0 and planning to switch to ~2.0, be\ncareful:\n\n* new version uses `super_` instead of `super` for referencing\n superclass\n* new version overwrites current prototype while old one preserves any\n existing fields on it\n", - "readmeFilename": "README.md", - "bugs": { - "url": "https://github.com/isaacs/inherits/issues" - }, - "homepage": "https://github.com/isaacs/inherits", - "_id": "inherits@2.0.1", - "_from": "inherits@~2.0.1" -} diff --git a/tests/mocha/node_modules/glob/node_modules/inherits/test.js b/tests/mocha/node_modules/glob/node_modules/inherits/test.js deleted file mode 100644 index fc53012d31..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/inherits/test.js +++ /dev/null @@ -1,25 +0,0 @@ -var inherits = require('./inherits.js') -var assert = require('assert') - -function test(c) { - assert(c.constructor === Child) - assert(c.constructor.super_ === Parent) - assert(Object.getPrototypeOf(c) === Child.prototype) - assert(Object.getPrototypeOf(Object.getPrototypeOf(c)) === Parent.prototype) - assert(c instanceof Child) - assert(c instanceof Parent) -} - -function Child() { - Parent.call(this) - test(this) -} - -function Parent() {} - -inherits(Child, Parent) - -var c = new Child -test(c) - -console.log('ok') diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/.npmignore b/tests/mocha/node_modules/glob/node_modules/minimatch/.npmignore deleted file mode 100644 index 3c3629e647..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/.npmignore +++ /dev/null @@ -1 +0,0 @@ -node_modules diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/LICENSE b/tests/mocha/node_modules/glob/node_modules/minimatch/LICENSE deleted file mode 100644 index 05a4010949..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ -Copyright 2009, 2010, 2011 Isaac Z. Schlueter. -All rights reserved. - -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 -copies 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 all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER 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. diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/README.md b/tests/mocha/node_modules/glob/node_modules/minimatch/README.md deleted file mode 100644 index 978268e275..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/README.md +++ /dev/null @@ -1,218 +0,0 @@ -# minimatch - -A minimal matching utility. - -[![Build Status](https://secure.travis-ci.org/isaacs/minimatch.png)](http://travis-ci.org/isaacs/minimatch) - - -This is the matching library used internally by npm. - -Eventually, it will replace the C binding in node-glob. - -It works by converting glob expressions into JavaScript `RegExp` -objects. - -## Usage - -```javascript -var minimatch = require("minimatch") - -minimatch("bar.foo", "*.foo") // true! -minimatch("bar.foo", "*.bar") // false! -minimatch("bar.foo", "*.+(bar|foo)", { debug: true }) // true, and noisy! -``` - -## Features - -Supports these glob features: - -* Brace Expansion -* Extended glob matching -* "Globstar" `**` matching - -See: - -* `man sh` -* `man bash` -* `man 3 fnmatch` -* `man 5 gitignore` - -## Minimatch Class - -Create a minimatch object by instanting the `minimatch.Minimatch` class. - -```javascript -var Minimatch = require("minimatch").Minimatch -var mm = new Minimatch(pattern, options) -``` - -### Properties - -* `pattern` The original pattern the minimatch object represents. -* `options` The options supplied to the constructor. -* `set` A 2-dimensional array of regexp or string expressions. - Each row in the - array corresponds to a brace-expanded pattern. Each item in the row - corresponds to a single path-part. For example, the pattern - `{a,b/c}/d` would expand to a set of patterns like: - - [ [ a, d ] - , [ b, c, d ] ] - - If a portion of the pattern doesn't have any "magic" in it - (that is, it's something like `"foo"` rather than `fo*o?`), then it - will be left as a string rather than converted to a regular - expression. - -* `regexp` Created by the `makeRe` method. A single regular expression - expressing the entire pattern. This is useful in cases where you wish - to use the pattern somewhat like `fnmatch(3)` with `FNM_PATH` enabled. -* `negate` True if the pattern is negated. -* `comment` True if the pattern is a comment. -* `empty` True if the pattern is `""`. - -### Methods - -* `makeRe` Generate the `regexp` member if necessary, and return it. - Will return `false` if the pattern is invalid. -* `match(fname)` Return true if the filename matches the pattern, or - false otherwise. -* `matchOne(fileArray, patternArray, partial)` Take a `/`-split - filename, and match it against a single row in the `regExpSet`. This - method is mainly for internal use, but is exposed so that it can be - used by a glob-walker that needs to avoid excessive filesystem calls. - -All other methods are internal, and will be called as necessary. - -## Functions - -The top-level exported function has a `cache` property, which is an LRU -cache set to store 100 items. So, calling these methods repeatedly -with the same pattern and options will use the same Minimatch object, -saving the cost of parsing it multiple times. - -### minimatch(path, pattern, options) - -Main export. Tests a path against the pattern using the options. - -```javascript -var isJS = minimatch(file, "*.js", { matchBase: true }) -``` - -### minimatch.filter(pattern, options) - -Returns a function that tests its -supplied argument, suitable for use with `Array.filter`. Example: - -```javascript -var javascripts = fileList.filter(minimatch.filter("*.js", {matchBase: true})) -``` - -### minimatch.match(list, pattern, options) - -Match against the list of -files, in the style of fnmatch or glob. If nothing is matched, and -options.nonull is set, then return a list containing the pattern itself. - -```javascript -var javascripts = minimatch.match(fileList, "*.js", {matchBase: true})) -``` - -### minimatch.makeRe(pattern, options) - -Make a regular expression object from the pattern. - -## Options - -All options are `false` by default. - -### debug - -Dump a ton of stuff to stderr. - -### nobrace - -Do not expand `{a,b}` and `{1..3}` brace sets. - -### noglobstar - -Disable `**` matching against multiple folder names. - -### dot - -Allow patterns to match filenames starting with a period, even if -the pattern does not explicitly have a period in that spot. - -Note that by default, `a/**/b` will **not** match `a/.d/b`, unless `dot` -is set. - -### noext - -Disable "extglob" style patterns like `+(a|b)`. - -### nocase - -Perform a case-insensitive match. - -### nonull - -When a match is not found by `minimatch.match`, return a list containing -the pattern itself. When set, an empty list is returned if there are -no matches. - -### matchBase - -If set, then patterns without slashes will be matched -against the basename of the path if it contains slashes. For example, -`a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`. - -### nocomment - -Suppress the behavior of treating `#` at the start of a pattern as a -comment. - -### nonegate - -Suppress the behavior of treating a leading `!` character as negation. - -### flipNegate - -Returns from negate expressions the same as if they were not negated. -(Ie, true on a hit, false on a miss.) - - -## Comparisons to other fnmatch/glob implementations - -While strict compliance with the existing standards is a worthwhile -goal, some discrepancies exist between minimatch and other -implementations, and are intentional. - -If the pattern starts with a `!` character, then it is negated. Set the -`nonegate` flag to suppress this behavior, and treat leading `!` -characters normally. This is perhaps relevant if you wish to start the -pattern with a negative extglob pattern like `!(a|B)`. Multiple `!` -characters at the start of a pattern will negate the pattern multiple -times. - -If a pattern starts with `#`, then it is treated as a comment, and -will not match anything. Use `\#` to match a literal `#` at the -start of a line, or set the `nocomment` flag to suppress this behavior. - -The double-star character `**` is supported by default, unless the -`noglobstar` flag is set. This is supported in the manner of bsdglob -and bash 4.1, where `**` only has special significance if it is the only -thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but -`a/**b` will not. - -If an escaped pattern has no matches, and the `nonull` flag is set, -then minimatch.match returns the pattern as-provided, rather than -interpreting the character escapes. For example, -`minimatch.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than -`"*a?"`. This is akin to setting the `nullglob` option in bash, except -that it does not resolve escaped pattern characters. - -If brace expansion is not disabled, then it is performed before any -other interpretation of the glob pattern. Thus, a pattern like -`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded -**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are -checked for validity. Since those two are valid, matching proceeds. diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/minimatch.js b/tests/mocha/node_modules/glob/node_modules/minimatch/minimatch.js deleted file mode 100644 index c633f89fab..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/minimatch.js +++ /dev/null @@ -1,1055 +0,0 @@ -;(function (require, exports, module, platform) { - -if (module) module.exports = minimatch -else exports.minimatch = minimatch - -if (!require) { - require = function (id) { - switch (id) { - case "sigmund": return function sigmund (obj) { - return JSON.stringify(obj) - } - case "path": return { basename: function (f) { - f = f.split(/[\/\\]/) - var e = f.pop() - if (!e) e = f.pop() - return e - }} - case "lru-cache": return function LRUCache () { - // not quite an LRU, but still space-limited. - var cache = {} - var cnt = 0 - this.set = function (k, v) { - cnt ++ - if (cnt >= 100) cache = {} - cache[k] = v - } - this.get = function (k) { return cache[k] } - } - } - } -} - -minimatch.Minimatch = Minimatch - -var LRU = require("lru-cache") - , cache = minimatch.cache = new LRU({max: 100}) - , GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {} - , sigmund = require("sigmund") - -var path = require("path") - // any single thing other than / - // don't need to escape / when using new RegExp() - , qmark = "[^/]" - - // * => any number of characters - , star = qmark + "*?" - - // ** when dots are allowed. Anything goes, except .. and . - // not (^ or / followed by one or two dots followed by $ or /), - // followed by anything, any number of times. - , twoStarDot = "(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?" - - // not a ^ or / followed by a dot, - // followed by anything, any number of times. - , twoStarNoDot = "(?:(?!(?:\\\/|^)\\.).)*?" - - // characters that need to be escaped in RegExp. - , reSpecials = charSet("().*{}+?[]^$\\!") - -// "abc" -> { a:true, b:true, c:true } -function charSet (s) { - return s.split("").reduce(function (set, c) { - set[c] = true - return set - }, {}) -} - -// normalizes slashes. -var slashSplit = /\/+/ - -minimatch.filter = filter -function filter (pattern, options) { - options = options || {} - return function (p, i, list) { - return minimatch(p, pattern, options) - } -} - -function ext (a, b) { - a = a || {} - b = b || {} - var t = {} - Object.keys(b).forEach(function (k) { - t[k] = b[k] - }) - Object.keys(a).forEach(function (k) { - t[k] = a[k] - }) - return t -} - -minimatch.defaults = function (def) { - if (!def || !Object.keys(def).length) return minimatch - - var orig = minimatch - - var m = function minimatch (p, pattern, options) { - return orig.minimatch(p, pattern, ext(def, options)) - } - - m.Minimatch = function Minimatch (pattern, options) { - return new orig.Minimatch(pattern, ext(def, options)) - } - - return m -} - -Minimatch.defaults = function (def) { - if (!def || !Object.keys(def).length) return Minimatch - return minimatch.defaults(def).Minimatch -} - - -function minimatch (p, pattern, options) { - if (typeof pattern !== "string") { - throw new TypeError("glob pattern string required") - } - - if (!options) options = {} - - // shortcut: comments match nothing. - if (!options.nocomment && pattern.charAt(0) === "#") { - return false - } - - // "" only matches "" - if (pattern.trim() === "") return p === "" - - return new Minimatch(pattern, options).match(p) -} - -function Minimatch (pattern, options) { - if (!(this instanceof Minimatch)) { - return new Minimatch(pattern, options, cache) - } - - if (typeof pattern !== "string") { - throw new TypeError("glob pattern string required") - } - - if (!options) options = {} - pattern = pattern.trim() - - // windows: need to use /, not \ - // On other platforms, \ is a valid (albeit bad) filename char. - if (platform === "win32") { - pattern = pattern.split("\\").join("/") - } - - // lru storage. - // these things aren't particularly big, but walking down the string - // and turning it into a regexp can get pretty costly. - var cacheKey = pattern + "\n" + sigmund(options) - var cached = minimatch.cache.get(cacheKey) - if (cached) return cached - minimatch.cache.set(cacheKey, this) - - this.options = options - this.set = [] - this.pattern = pattern - this.regexp = null - this.negate = false - this.comment = false - this.empty = false - - // make the set of regexps etc. - this.make() -} - -Minimatch.prototype.debug = function() {} - -Minimatch.prototype.make = make -function make () { - // don't do it more than once. - if (this._made) return - - var pattern = this.pattern - var options = this.options - - // empty patterns and comments match nothing. - if (!options.nocomment && pattern.charAt(0) === "#") { - this.comment = true - return - } - if (!pattern) { - this.empty = true - return - } - - // step 1: figure out negation, etc. - this.parseNegate() - - // step 2: expand braces - var set = this.globSet = this.braceExpand() - - if (options.debug) this.debug = console.error - - this.debug(this.pattern, set) - - // step 3: now we have a set, so turn each one into a series of path-portion - // matching patterns. - // These will be regexps, except in the case of "**", which is - // set to the GLOBSTAR object for globstar behavior, - // and will not contain any / characters - set = this.globParts = set.map(function (s) { - return s.split(slashSplit) - }) - - this.debug(this.pattern, set) - - // glob --> regexps - set = set.map(function (s, si, set) { - return s.map(this.parse, this) - }, this) - - this.debug(this.pattern, set) - - // filter out everything that didn't compile properly. - set = set.filter(function (s) { - return -1 === s.indexOf(false) - }) - - this.debug(this.pattern, set) - - this.set = set -} - -Minimatch.prototype.parseNegate = parseNegate -function parseNegate () { - var pattern = this.pattern - , negate = false - , options = this.options - , negateOffset = 0 - - if (options.nonegate) return - - for ( var i = 0, l = pattern.length - ; i < l && pattern.charAt(i) === "!" - ; i ++) { - negate = !negate - negateOffset ++ - } - - if (negateOffset) this.pattern = pattern.substr(negateOffset) - this.negate = negate -} - -// Brace expansion: -// a{b,c}d -> abd acd -// a{b,}c -> abc ac -// a{0..3}d -> a0d a1d a2d a3d -// a{b,c{d,e}f}g -> abg acdfg acefg -// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg -// -// Invalid sets are not expanded. -// a{2..}b -> a{2..}b -// a{b}c -> a{b}c -minimatch.braceExpand = function (pattern, options) { - return new Minimatch(pattern, options).braceExpand() -} - -Minimatch.prototype.braceExpand = braceExpand -function braceExpand (pattern, options) { - options = options || this.options - pattern = typeof pattern === "undefined" - ? this.pattern : pattern - - if (typeof pattern === "undefined") { - throw new Error("undefined pattern") - } - - if (options.nobrace || - !pattern.match(/\{.*\}/)) { - // shortcut. no need to expand. - return [pattern] - } - - var escaping = false - - // examples and comments refer to this crazy pattern: - // a{b,c{d,e},{f,g}h}x{y,z} - // expected: - // abxy - // abxz - // acdxy - // acdxz - // acexy - // acexz - // afhxy - // afhxz - // aghxy - // aghxz - - // everything before the first \{ is just a prefix. - // So, we pluck that off, and work with the rest, - // and then prepend it to everything we find. - if (pattern.charAt(0) !== "{") { - this.debug(pattern) - var prefix = null - for (var i = 0, l = pattern.length; i < l; i ++) { - var c = pattern.charAt(i) - this.debug(i, c) - if (c === "\\") { - escaping = !escaping - } else if (c === "{" && !escaping) { - prefix = pattern.substr(0, i) - break - } - } - - // actually no sets, all { were escaped. - if (prefix === null) { - this.debug("no sets") - return [pattern] - } - - var tail = braceExpand.call(this, pattern.substr(i), options) - return tail.map(function (t) { - return prefix + t - }) - } - - // now we have something like: - // {b,c{d,e},{f,g}h}x{y,z} - // walk through the set, expanding each part, until - // the set ends. then, we'll expand the suffix. - // If the set only has a single member, then'll put the {} back - - // first, handle numeric sets, since they're easier - var numset = pattern.match(/^\{(-?[0-9]+)\.\.(-?[0-9]+)\}/) - if (numset) { - this.debug("numset", numset[1], numset[2]) - var suf = braceExpand.call(this, pattern.substr(numset[0].length), options) - , start = +numset[1] - , end = +numset[2] - , inc = start > end ? -1 : 1 - , set = [] - for (var i = start; i != (end + inc); i += inc) { - // append all the suffixes - for (var ii = 0, ll = suf.length; ii < ll; ii ++) { - set.push(i + suf[ii]) - } - } - return set - } - - // ok, walk through the set - // We hope, somewhat optimistically, that there - // will be a } at the end. - // If the closing brace isn't found, then the pattern is - // interpreted as braceExpand("\\" + pattern) so that - // the leading \{ will be interpreted literally. - var i = 1 // skip the \{ - , depth = 1 - , set = [] - , member = "" - , sawEnd = false - , escaping = false - - function addMember () { - set.push(member) - member = "" - } - - this.debug("Entering for") - FOR: for (i = 1, l = pattern.length; i < l; i ++) { - var c = pattern.charAt(i) - this.debug("", i, c) - - if (escaping) { - escaping = false - member += "\\" + c - } else { - switch (c) { - case "\\": - escaping = true - continue - - case "{": - depth ++ - member += "{" - continue - - case "}": - depth -- - // if this closes the actual set, then we're done - if (depth === 0) { - addMember() - // pluck off the close-brace - i ++ - break FOR - } else { - member += c - continue - } - - case ",": - if (depth === 1) { - addMember() - } else { - member += c - } - continue - - default: - member += c - continue - } // switch - } // else - } // for - - // now we've either finished the set, and the suffix is - // pattern.substr(i), or we have *not* closed the set, - // and need to escape the leading brace - if (depth !== 0) { - this.debug("didn't close", pattern) - return braceExpand.call(this, "\\" + pattern, options) - } - - // x{y,z} -> ["xy", "xz"] - this.debug("set", set) - this.debug("suffix", pattern.substr(i)) - var suf = braceExpand.call(this, pattern.substr(i), options) - // ["b", "c{d,e}","{f,g}h"] -> - // [["b"], ["cd", "ce"], ["fh", "gh"]] - var addBraces = set.length === 1 - this.debug("set pre-expanded", set) - set = set.map(function (p) { - return braceExpand.call(this, p, options) - }, this) - this.debug("set expanded", set) - - - // [["b"], ["cd", "ce"], ["fh", "gh"]] -> - // ["b", "cd", "ce", "fh", "gh"] - set = set.reduce(function (l, r) { - return l.concat(r) - }) - - if (addBraces) { - set = set.map(function (s) { - return "{" + s + "}" - }) - } - - // now attach the suffixes. - var ret = [] - for (var i = 0, l = set.length; i < l; i ++) { - for (var ii = 0, ll = suf.length; ii < ll; ii ++) { - ret.push(set[i] + suf[ii]) - } - } - return ret -} - -// parse a component of the expanded set. -// At this point, no pattern may contain "/" in it -// so we're going to return a 2d array, where each entry is the full -// pattern, split on '/', and then turned into a regular expression. -// A regexp is made at the end which joins each array with an -// escaped /, and another full one which joins each regexp with |. -// -// Following the lead of Bash 4.1, note that "**" only has special meaning -// when it is the *only* thing in a path portion. Otherwise, any series -// of * is equivalent to a single *. Globstar behavior is enabled by -// default, and can be disabled by setting options.noglobstar. -Minimatch.prototype.parse = parse -var SUBPARSE = {} -function parse (pattern, isSub) { - var options = this.options - - // shortcuts - if (!options.noglobstar && pattern === "**") return GLOBSTAR - if (pattern === "") return "" - - var re = "" - , hasMagic = !!options.nocase - , escaping = false - // ? => one single character - , patternListStack = [] - , plType - , stateChar - , inClass = false - , reClassStart = -1 - , classStart = -1 - // . and .. never match anything that doesn't start with ., - // even when options.dot is set. - , patternStart = pattern.charAt(0) === "." ? "" // anything - // not (start or / followed by . or .. followed by / or end) - : options.dot ? "(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))" - : "(?!\\.)" - , self = this - - function clearStateChar () { - if (stateChar) { - // we had some state-tracking character - // that wasn't consumed by this pass. - switch (stateChar) { - case "*": - re += star - hasMagic = true - break - case "?": - re += qmark - hasMagic = true - break - default: - re += "\\"+stateChar - break - } - self.debug('clearStateChar %j %j', stateChar, re) - stateChar = false - } - } - - for ( var i = 0, len = pattern.length, c - ; (i < len) && (c = pattern.charAt(i)) - ; i ++ ) { - - this.debug("%s\t%s %s %j", pattern, i, re, c) - - // skip over any that are escaped. - if (escaping && reSpecials[c]) { - re += "\\" + c - escaping = false - continue - } - - SWITCH: switch (c) { - case "/": - // completely not allowed, even escaped. - // Should already be path-split by now. - return false - - case "\\": - clearStateChar() - escaping = true - continue - - // the various stateChar values - // for the "extglob" stuff. - case "?": - case "*": - case "+": - case "@": - case "!": - this.debug("%s\t%s %s %j <-- stateChar", pattern, i, re, c) - - // all of those are literals inside a class, except that - // the glob [!a] means [^a] in regexp - if (inClass) { - this.debug(' in class') - if (c === "!" && i === classStart + 1) c = "^" - re += c - continue - } - - // if we already have a stateChar, then it means - // that there was something like ** or +? in there. - // Handle the stateChar, then proceed with this one. - self.debug('call clearStateChar %j', stateChar) - clearStateChar() - stateChar = c - // if extglob is disabled, then +(asdf|foo) isn't a thing. - // just clear the statechar *now*, rather than even diving into - // the patternList stuff. - if (options.noext) clearStateChar() - continue - - case "(": - if (inClass) { - re += "(" - continue - } - - if (!stateChar) { - re += "\\(" - continue - } - - plType = stateChar - patternListStack.push({ type: plType - , start: i - 1 - , reStart: re.length }) - // negation is (?:(?!js)[^/]*) - re += stateChar === "!" ? "(?:(?!" : "(?:" - this.debug('plType %j %j', stateChar, re) - stateChar = false - continue - - case ")": - if (inClass || !patternListStack.length) { - re += "\\)" - continue - } - - clearStateChar() - hasMagic = true - re += ")" - plType = patternListStack.pop().type - // negation is (?:(?!js)[^/]*) - // The others are (?:) - switch (plType) { - case "!": - re += "[^/]*?)" - break - case "?": - case "+": - case "*": re += plType - case "@": break // the default anyway - } - continue - - case "|": - if (inClass || !patternListStack.length || escaping) { - re += "\\|" - escaping = false - continue - } - - clearStateChar() - re += "|" - continue - - // these are mostly the same in regexp and glob - case "[": - // swallow any state-tracking char before the [ - clearStateChar() - - if (inClass) { - re += "\\" + c - continue - } - - inClass = true - classStart = i - reClassStart = re.length - re += c - continue - - case "]": - // a right bracket shall lose its special - // meaning and represent itself in - // a bracket expression if it occurs - // first in the list. -- POSIX.2 2.8.3.2 - if (i === classStart + 1 || !inClass) { - re += "\\" + c - escaping = false - continue - } - - // finish up the class. - hasMagic = true - inClass = false - re += c - continue - - default: - // swallow any state char that wasn't consumed - clearStateChar() - - if (escaping) { - // no need - escaping = false - } else if (reSpecials[c] - && !(c === "^" && inClass)) { - re += "\\" - } - - re += c - - } // switch - } // for - - - // handle the case where we left a class open. - // "[abc" is valid, equivalent to "\[abc" - if (inClass) { - // split where the last [ was, and escape it - // this is a huge pita. We now have to re-walk - // the contents of the would-be class to re-translate - // any characters that were passed through as-is - var cs = pattern.substr(classStart + 1) - , sp = this.parse(cs, SUBPARSE) - re = re.substr(0, reClassStart) + "\\[" + sp[0] - hasMagic = hasMagic || sp[1] - } - - // handle the case where we had a +( thing at the *end* - // of the pattern. - // each pattern list stack adds 3 chars, and we need to go through - // and escape any | chars that were passed through as-is for the regexp. - // Go through and escape them, taking care not to double-escape any - // | chars that were already escaped. - var pl - while (pl = patternListStack.pop()) { - var tail = re.slice(pl.reStart + 3) - // maybe some even number of \, then maybe 1 \, followed by a | - tail = tail.replace(/((?:\\{2})*)(\\?)\|/g, function (_, $1, $2) { - if (!$2) { - // the | isn't already escaped, so escape it. - $2 = "\\" - } - - // need to escape all those slashes *again*, without escaping the - // one that we need for escaping the | character. As it works out, - // escaping an even number of slashes can be done by simply repeating - // it exactly after itself. That's why this trick works. - // - // I am sorry that you have to see this. - return $1 + $1 + $2 + "|" - }) - - this.debug("tail=%j\n %s", tail, tail) - var t = pl.type === "*" ? star - : pl.type === "?" ? qmark - : "\\" + pl.type - - hasMagic = true - re = re.slice(0, pl.reStart) - + t + "\\(" - + tail - } - - // handle trailing things that only matter at the very end. - clearStateChar() - if (escaping) { - // trailing \\ - re += "\\\\" - } - - // only need to apply the nodot start if the re starts with - // something that could conceivably capture a dot - var addPatternStart = false - switch (re.charAt(0)) { - case ".": - case "[": - case "(": addPatternStart = true - } - - // if the re is not "" at this point, then we need to make sure - // it doesn't match against an empty path part. - // Otherwise a/* will match a/, which it should not. - if (re !== "" && hasMagic) re = "(?=.)" + re - - if (addPatternStart) re = patternStart + re - - // parsing just a piece of a larger pattern. - if (isSub === SUBPARSE) { - return [ re, hasMagic ] - } - - // skip the regexp for non-magical patterns - // unescape anything in it, though, so that it'll be - // an exact match against a file etc. - if (!hasMagic) { - return globUnescape(pattern) - } - - var flags = options.nocase ? "i" : "" - , regExp = new RegExp("^" + re + "$", flags) - - regExp._glob = pattern - regExp._src = re - - return regExp -} - -minimatch.makeRe = function (pattern, options) { - return new Minimatch(pattern, options || {}).makeRe() -} - -Minimatch.prototype.makeRe = makeRe -function makeRe () { - if (this.regexp || this.regexp === false) return this.regexp - - // at this point, this.set is a 2d array of partial - // pattern strings, or "**". - // - // It's better to use .match(). This function shouldn't - // be used, really, but it's pretty convenient sometimes, - // when you just want to work with a regex. - var set = this.set - - if (!set.length) return this.regexp = false - var options = this.options - - var twoStar = options.noglobstar ? star - : options.dot ? twoStarDot - : twoStarNoDot - , flags = options.nocase ? "i" : "" - - var re = set.map(function (pattern) { - return pattern.map(function (p) { - return (p === GLOBSTAR) ? twoStar - : (typeof p === "string") ? regExpEscape(p) - : p._src - }).join("\\\/") - }).join("|") - - // must match entire pattern - // ending in a * or ** will make it less strict. - re = "^(?:" + re + ")$" - - // can match anything, as long as it's not this. - if (this.negate) re = "^(?!" + re + ").*$" - - try { - return this.regexp = new RegExp(re, flags) - } catch (ex) { - return this.regexp = false - } -} - -minimatch.match = function (list, pattern, options) { - var mm = new Minimatch(pattern, options) - list = list.filter(function (f) { - return mm.match(f) - }) - if (options.nonull && !list.length) { - list.push(pattern) - } - return list -} - -Minimatch.prototype.match = match -function match (f, partial) { - this.debug("match", f, this.pattern) - // short-circuit in the case of busted things. - // comments, etc. - if (this.comment) return false - if (this.empty) return f === "" - - if (f === "/" && partial) return true - - var options = this.options - - // windows: need to use /, not \ - // On other platforms, \ is a valid (albeit bad) filename char. - if (platform === "win32") { - f = f.split("\\").join("/") - } - - // treat the test path as a set of pathparts. - f = f.split(slashSplit) - this.debug(this.pattern, "split", f) - - // just ONE of the pattern sets in this.set needs to match - // in order for it to be valid. If negating, then just one - // match means that we have failed. - // Either way, return on the first hit. - - var set = this.set - this.debug(this.pattern, "set", set) - - var splitFile = path.basename(f.join("/")).split("/") - - for (var i = 0, l = set.length; i < l; i ++) { - var pattern = set[i], file = f - if (options.matchBase && pattern.length === 1) { - file = splitFile - } - var hit = this.matchOne(file, pattern, partial) - if (hit) { - if (options.flipNegate) return true - return !this.negate - } - } - - // didn't get any hits. this is success if it's a negative - // pattern, failure otherwise. - if (options.flipNegate) return false - return this.negate -} - -// set partial to true to test if, for example, -// "/a/b" matches the start of "/*/b/*/d" -// Partial means, if you run out of file before you run -// out of pattern, then that's fine, as long as all -// the parts match. -Minimatch.prototype.matchOne = function (file, pattern, partial) { - var options = this.options - - this.debug("matchOne", - { "this": this - , file: file - , pattern: pattern }) - - this.debug("matchOne", file.length, pattern.length) - - for ( var fi = 0 - , pi = 0 - , fl = file.length - , pl = pattern.length - ; (fi < fl) && (pi < pl) - ; fi ++, pi ++ ) { - - this.debug("matchOne loop") - var p = pattern[pi] - , f = file[fi] - - this.debug(pattern, p, f) - - // should be impossible. - // some invalid regexp stuff in the set. - if (p === false) return false - - if (p === GLOBSTAR) { - this.debug('GLOBSTAR', [pattern, p, f]) - - // "**" - // a/**/b/**/c would match the following: - // a/b/x/y/z/c - // a/x/y/z/b/c - // a/b/x/b/x/c - // a/b/c - // To do this, take the rest of the pattern after - // the **, and see if it would match the file remainder. - // If so, return success. - // If not, the ** "swallows" a segment, and try again. - // This is recursively awful. - // - // a/**/b/**/c matching a/b/x/y/z/c - // - a matches a - // - doublestar - // - matchOne(b/x/y/z/c, b/**/c) - // - b matches b - // - doublestar - // - matchOne(x/y/z/c, c) -> no - // - matchOne(y/z/c, c) -> no - // - matchOne(z/c, c) -> no - // - matchOne(c, c) yes, hit - var fr = fi - , pr = pi + 1 - if (pr === pl) { - this.debug('** at the end') - // a ** at the end will just swallow the rest. - // We have found a match. - // however, it will not swallow /.x, unless - // options.dot is set. - // . and .. are *never* matched by **, for explosively - // exponential reasons. - for ( ; fi < fl; fi ++) { - if (file[fi] === "." || file[fi] === ".." || - (!options.dot && file[fi].charAt(0) === ".")) return false - } - return true - } - - // ok, let's see if we can swallow whatever we can. - WHILE: while (fr < fl) { - var swallowee = file[fr] - - this.debug('\nglobstar while', - file, fr, pattern, pr, swallowee) - - // XXX remove this slice. Just pass the start index. - if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { - this.debug('globstar found match!', fr, fl, swallowee) - // found a match. - return true - } else { - // can't swallow "." or ".." ever. - // can only swallow ".foo" when explicitly asked. - if (swallowee === "." || swallowee === ".." || - (!options.dot && swallowee.charAt(0) === ".")) { - this.debug("dot detected!", file, fr, pattern, pr) - break WHILE - } - - // ** swallows a segment, and continue. - this.debug('globstar swallow a segment, and continue') - fr ++ - } - } - // no match was found. - // However, in partial mode, we can't say this is necessarily over. - // If there's more *pattern* left, then - if (partial) { - // ran out of file - this.debug("\n>>> no match, partial?", file, fr, pattern, pr) - if (fr === fl) return true - } - return false - } - - // something other than ** - // non-magic patterns just have to match exactly - // patterns with magic have been turned into regexps. - var hit - if (typeof p === "string") { - if (options.nocase) { - hit = f.toLowerCase() === p.toLowerCase() - } else { - hit = f === p - } - this.debug("string match", p, f, hit) - } else { - hit = f.match(p) - this.debug("pattern match", p, f, hit) - } - - if (!hit) return false - } - - // Note: ending in / means that we'll get a final "" - // at the end of the pattern. This can only match a - // corresponding "" at the end of the file. - // If the file ends in /, then it can only match a - // a pattern that ends in /, unless the pattern just - // doesn't have any more for it. But, a/b/ should *not* - // match "a/b/*", even though "" matches against the - // [^/]*? pattern, except in partial mode, where it might - // simply not be reached yet. - // However, a/b/ should still satisfy a/* - - // now either we fell off the end of the pattern, or we're done. - if (fi === fl && pi === pl) { - // ran out of pattern and filename at the same time. - // an exact hit! - return true - } else if (fi === fl) { - // ran out of file, but still had pattern left. - // this is ok if we're doing the match as part of - // a glob fs traversal. - return partial - } else if (pi === pl) { - // ran out of pattern, still have file left. - // this is only acceptable if we're on the very last - // empty segment of a file with a trailing slash. - // a/* should match a/b/ - var emptyFileEnd = (fi === fl - 1) && (file[fi] === "") - return emptyFileEnd - } - - // should be unreachable. - throw new Error("wtf?") -} - - -// replace stuff like \* with * -function globUnescape (s) { - return s.replace(/\\(.)/g, "$1") -} - - -function regExpEscape (s) { - return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&") -} - -})( typeof require === "function" ? require : null, - this, - typeof module === "object" ? module : null, - typeof process === "object" ? process.platform : "win32" - ) diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/.npmignore b/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/.npmignore deleted file mode 100644 index 07e6e472cc..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/.npmignore +++ /dev/null @@ -1 +0,0 @@ -/node_modules diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/CONTRIBUTORS b/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/CONTRIBUTORS deleted file mode 100644 index 4a0bc5033a..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/CONTRIBUTORS +++ /dev/null @@ -1,14 +0,0 @@ -# Authors, sorted by whether or not they are me -Isaac Z. Schlueter -Brian Cottingham -Carlos Brito Lage -Jesse Dailey -Kevin O'Hara -Marco Rogers -Mark Cavage -Marko Mikulicic -Nathan Rajlich -Satheesh Natesan -Trent Mick -ashleybrener -n4kz diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/LICENSE b/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/LICENSE deleted file mode 100644 index 05a4010949..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ -Copyright 2009, 2010, 2011 Isaac Z. Schlueter. -All rights reserved. - -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 -copies 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 all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER 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. diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/README.md b/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/README.md deleted file mode 100644 index 03ee0f9850..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/README.md +++ /dev/null @@ -1,97 +0,0 @@ -# lru cache - -A cache object that deletes the least-recently-used items. - -## Usage: - -```javascript -var LRU = require("lru-cache") - , options = { max: 500 - , length: function (n) { return n * 2 } - , dispose: function (key, n) { n.close() } - , maxAge: 1000 * 60 * 60 } - , cache = LRU(options) - , otherCache = LRU(50) // sets just the max size - -cache.set("key", "value") -cache.get("key") // "value" - -cache.reset() // empty the cache -``` - -If you put more stuff in it, then items will fall out. - -If you try to put an oversized thing in it, then it'll fall out right -away. - -## Options - -* `max` The maximum size of the cache, checked by applying the length - function to all values in the cache. Not setting this is kind of - silly, since that's the whole purpose of this lib, but it defaults - to `Infinity`. -* `maxAge` Maximum age in ms. Items are not pro-actively pruned out - as they age, but if you try to get an item that is too old, it'll - drop it and return undefined instead of giving it to you. -* `length` Function that is used to calculate the length of stored - items. If you're storing strings or buffers, then you probably want - to do something like `function(n){return n.length}`. The default is - `function(n){return 1}`, which is fine if you want to store `n` - like-sized things. -* `dispose` Function that is called on items when they are dropped - from the cache. This can be handy if you want to close file - descriptors or do other cleanup tasks when items are no longer - accessible. Called with `key, value`. It's called *before* - actually removing the item from the internal cache, so if you want - to immediately put it back in, you'll have to do that in a - `nextTick` or `setTimeout` callback or it won't do anything. -* `stale` By default, if you set a `maxAge`, it'll only actually pull - stale items out of the cache when you `get(key)`. (That is, it's - not pre-emptively doing a `setTimeout` or anything.) If you set - `stale:true`, it'll return the stale value before deleting it. If - you don't set this, then it'll return `undefined` when you try to - get a stale entry, as if it had already been deleted. - -## API - -* `set(key, value)` -* `get(key) => value` - - Both of these will update the "recently used"-ness of the key. - They do what you think. - -* `peek(key)` - - Returns the key value (or `undefined` if not found) without - updating the "recently used"-ness of the key. - - (If you find yourself using this a lot, you *might* be using the - wrong sort of data structure, but there are some use cases where - it's handy.) - -* `del(key)` - - Deletes a key out of the cache. - -* `reset()` - - Clear the cache entirely, throwing away all values. - -* `has(key)` - - Check if a key is in the cache, without updating the recent-ness - or deleting it for being stale. - -* `forEach(function(value,key,cache), [thisp])` - - Just like `Array.prototype.forEach`. Iterates over all the keys - in the cache, in order of recent-ness. (Ie, more recently used - items are iterated over first.) - -* `keys()` - - Return an array of the keys in the cache. - -* `values()` - - Return an array of the values in the cache. diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/lib/lru-cache.js b/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/lib/lru-cache.js deleted file mode 100644 index d1d1381720..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/lib/lru-cache.js +++ /dev/null @@ -1,252 +0,0 @@ -;(function () { // closure for web browsers - -if (typeof module === 'object' && module.exports) { - module.exports = LRUCache -} else { - // just set the global for non-node platforms. - this.LRUCache = LRUCache -} - -function hOP (obj, key) { - return Object.prototype.hasOwnProperty.call(obj, key) -} - -function naiveLength () { return 1 } - -function LRUCache (options) { - if (!(this instanceof LRUCache)) - return new LRUCache(options) - - if (typeof options === 'number') - options = { max: options } - - if (!options) - options = {} - - this._max = options.max - // Kind of weird to have a default max of Infinity, but oh well. - if (!this._max || !(typeof this._max === "number") || this._max <= 0 ) - this._max = Infinity - - this._lengthCalculator = options.length || naiveLength - if (typeof this._lengthCalculator !== "function") - this._lengthCalculator = naiveLength - - this._allowStale = options.stale || false - this._maxAge = options.maxAge || null - this._dispose = options.dispose - this.reset() -} - -// resize the cache when the max changes. -Object.defineProperty(LRUCache.prototype, "max", - { set : function (mL) { - if (!mL || !(typeof mL === "number") || mL <= 0 ) mL = Infinity - this._max = mL - if (this._length > this._max) trim(this) - } - , get : function () { return this._max } - , enumerable : true - }) - -// resize the cache when the lengthCalculator changes. -Object.defineProperty(LRUCache.prototype, "lengthCalculator", - { set : function (lC) { - if (typeof lC !== "function") { - this._lengthCalculator = naiveLength - this._length = this._itemCount - for (var key in this._cache) { - this._cache[key].length = 1 - } - } else { - this._lengthCalculator = lC - this._length = 0 - for (var key in this._cache) { - this._cache[key].length = this._lengthCalculator(this._cache[key].value) - this._length += this._cache[key].length - } - } - - if (this._length > this._max) trim(this) - } - , get : function () { return this._lengthCalculator } - , enumerable : true - }) - -Object.defineProperty(LRUCache.prototype, "length", - { get : function () { return this._length } - , enumerable : true - }) - - -Object.defineProperty(LRUCache.prototype, "itemCount", - { get : function () { return this._itemCount } - , enumerable : true - }) - -LRUCache.prototype.forEach = function (fn, thisp) { - thisp = thisp || this - var i = 0; - for (var k = this._mru - 1; k >= 0 && i < this._itemCount; k--) if (this._lruList[k]) { - i++ - var hit = this._lruList[k] - if (this._maxAge && (Date.now() - hit.now > this._maxAge)) { - del(this, hit) - if (!this._allowStale) hit = undefined - } - if (hit) { - fn.call(thisp, hit.value, hit.key, this) - } - } -} - -LRUCache.prototype.keys = function () { - var keys = new Array(this._itemCount) - var i = 0 - for (var k = this._mru - 1; k >= 0 && i < this._itemCount; k--) if (this._lruList[k]) { - var hit = this._lruList[k] - keys[i++] = hit.key - } - return keys -} - -LRUCache.prototype.values = function () { - var values = new Array(this._itemCount) - var i = 0 - for (var k = this._mru - 1; k >= 0 && i < this._itemCount; k--) if (this._lruList[k]) { - var hit = this._lruList[k] - values[i++] = hit.value - } - return values -} - -LRUCache.prototype.reset = function () { - if (this._dispose && this._cache) { - for (var k in this._cache) { - this._dispose(k, this._cache[k].value) - } - } - - this._cache = Object.create(null) // hash of items by key - this._lruList = Object.create(null) // list of items in order of use recency - this._mru = 0 // most recently used - this._lru = 0 // least recently used - this._length = 0 // number of items in the list - this._itemCount = 0 -} - -// Provided for debugging/dev purposes only. No promises whatsoever that -// this API stays stable. -LRUCache.prototype.dump = function () { - return this._cache -} - -LRUCache.prototype.dumpLru = function () { - return this._lruList -} - -LRUCache.prototype.set = function (key, value) { - if (hOP(this._cache, key)) { - // dispose of the old one before overwriting - if (this._dispose) this._dispose(key, this._cache[key].value) - if (this._maxAge) this._cache[key].now = Date.now() - this._cache[key].value = value - this.get(key) - return true - } - - var len = this._lengthCalculator(value) - var age = this._maxAge ? Date.now() : 0 - var hit = new Entry(key, value, this._mru++, len, age) - - // oversized objects fall out of cache automatically. - if (hit.length > this._max) { - if (this._dispose) this._dispose(key, value) - return false - } - - this._length += hit.length - this._lruList[hit.lu] = this._cache[key] = hit - this._itemCount ++ - - if (this._length > this._max) trim(this) - return true -} - -LRUCache.prototype.has = function (key) { - if (!hOP(this._cache, key)) return false - var hit = this._cache[key] - if (this._maxAge && (Date.now() - hit.now > this._maxAge)) { - return false - } - return true -} - -LRUCache.prototype.get = function (key) { - return get(this, key, true) -} - -LRUCache.prototype.peek = function (key) { - return get(this, key, false) -} - -LRUCache.prototype.pop = function () { - var hit = this._lruList[this._lru] - del(this, hit) - return hit || null -} - -LRUCache.prototype.del = function (key) { - del(this, this._cache[key]) -} - -function get (self, key, doUse) { - var hit = self._cache[key] - if (hit) { - if (self._maxAge && (Date.now() - hit.now > self._maxAge)) { - del(self, hit) - if (!self._allowStale) hit = undefined - } else { - if (doUse) use(self, hit) - } - if (hit) hit = hit.value - } - return hit -} - -function use (self, hit) { - shiftLU(self, hit) - hit.lu = self._mru ++ - self._lruList[hit.lu] = hit -} - -function trim (self) { - while (self._lru < self._mru && self._length > self._max) - del(self, self._lruList[self._lru]) -} - -function shiftLU (self, hit) { - delete self._lruList[ hit.lu ] - while (self._lru < self._mru && !self._lruList[self._lru]) self._lru ++ -} - -function del (self, hit) { - if (hit) { - if (self._dispose) self._dispose(hit.key, hit.value) - self._length -= hit.length - self._itemCount -- - delete self._cache[ hit.key ] - shiftLU(self, hit) - } -} - -// classy, since V8 prefers predictable objects. -function Entry (key, value, lu, length, now) { - this.key = key - this.value = value - this.lu = lu - this.length = length - this.now = now -} - -})() diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/package.json b/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/package.json deleted file mode 100644 index 4472725df9..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "lru-cache", - "description": "A cache object that deletes the least-recently-used items.", - "version": "2.5.0", - "author": { - "name": "Isaac Z. Schlueter", - "email": "i@izs.me" - }, - "scripts": { - "test": "tap test --gc" - }, - "main": "lib/lru-cache.js", - "repository": { - "type": "git", - "url": "git://github.com/isaacs/node-lru-cache.git" - }, - "devDependencies": { - "tap": "", - "weak": "" - }, - "license": { - "type": "MIT", - "url": "http://github.com/isaacs/node-lru-cache/raw/master/LICENSE" - }, - "readme": "# lru cache\n\nA cache object that deletes the least-recently-used items.\n\n## Usage:\n\n```javascript\nvar LRU = require(\"lru-cache\")\n , options = { max: 500\n , length: function (n) { return n * 2 }\n , dispose: function (key, n) { n.close() }\n , maxAge: 1000 * 60 * 60 }\n , cache = LRU(options)\n , otherCache = LRU(50) // sets just the max size\n\ncache.set(\"key\", \"value\")\ncache.get(\"key\") // \"value\"\n\ncache.reset() // empty the cache\n```\n\nIf you put more stuff in it, then items will fall out.\n\nIf you try to put an oversized thing in it, then it'll fall out right\naway.\n\n## Options\n\n* `max` The maximum size of the cache, checked by applying the length\n function to all values in the cache. Not setting this is kind of\n silly, since that's the whole purpose of this lib, but it defaults\n to `Infinity`.\n* `maxAge` Maximum age in ms. Items are not pro-actively pruned out\n as they age, but if you try to get an item that is too old, it'll\n drop it and return undefined instead of giving it to you.\n* `length` Function that is used to calculate the length of stored\n items. If you're storing strings or buffers, then you probably want\n to do something like `function(n){return n.length}`. The default is\n `function(n){return 1}`, which is fine if you want to store `n`\n like-sized things.\n* `dispose` Function that is called on items when they are dropped\n from the cache. This can be handy if you want to close file\n descriptors or do other cleanup tasks when items are no longer\n accessible. Called with `key, value`. It's called *before*\n actually removing the item from the internal cache, so if you want\n to immediately put it back in, you'll have to do that in a\n `nextTick` or `setTimeout` callback or it won't do anything.\n* `stale` By default, if you set a `maxAge`, it'll only actually pull\n stale items out of the cache when you `get(key)`. (That is, it's\n not pre-emptively doing a `setTimeout` or anything.) If you set\n `stale:true`, it'll return the stale value before deleting it. If\n you don't set this, then it'll return `undefined` when you try to\n get a stale entry, as if it had already been deleted.\n\n## API\n\n* `set(key, value)`\n* `get(key) => value`\n\n Both of these will update the \"recently used\"-ness of the key.\n They do what you think.\n\n* `peek(key)`\n\n Returns the key value (or `undefined` if not found) without\n updating the \"recently used\"-ness of the key.\n\n (If you find yourself using this a lot, you *might* be using the\n wrong sort of data structure, but there are some use cases where\n it's handy.)\n\n* `del(key)`\n\n Deletes a key out of the cache.\n\n* `reset()`\n\n Clear the cache entirely, throwing away all values.\n\n* `has(key)`\n\n Check if a key is in the cache, without updating the recent-ness\n or deleting it for being stale.\n\n* `forEach(function(value,key,cache), [thisp])`\n\n Just like `Array.prototype.forEach`. Iterates over all the keys\n in the cache, in order of recent-ness. (Ie, more recently used\n items are iterated over first.)\n\n* `keys()`\n\n Return an array of the keys in the cache.\n\n* `values()`\n\n Return an array of the values in the cache.\n", - "readmeFilename": "README.md", - "bugs": { - "url": "https://github.com/isaacs/node-lru-cache/issues" - }, - "homepage": "https://github.com/isaacs/node-lru-cache", - "_id": "lru-cache@2.5.0", - "_from": "lru-cache@2" -} diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/basic.js b/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/basic.js deleted file mode 100644 index f72697c461..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/basic.js +++ /dev/null @@ -1,369 +0,0 @@ -var test = require("tap").test - , LRU = require("../") - -test("basic", function (t) { - var cache = new LRU({max: 10}) - cache.set("key", "value") - t.equal(cache.get("key"), "value") - t.equal(cache.get("nada"), undefined) - t.equal(cache.length, 1) - t.equal(cache.max, 10) - t.end() -}) - -test("least recently set", function (t) { - var cache = new LRU(2) - cache.set("a", "A") - cache.set("b", "B") - cache.set("c", "C") - t.equal(cache.get("c"), "C") - t.equal(cache.get("b"), "B") - t.equal(cache.get("a"), undefined) - t.end() -}) - -test("lru recently gotten", function (t) { - var cache = new LRU(2) - cache.set("a", "A") - cache.set("b", "B") - cache.get("a") - cache.set("c", "C") - t.equal(cache.get("c"), "C") - t.equal(cache.get("b"), undefined) - t.equal(cache.get("a"), "A") - t.end() -}) - -test("del", function (t) { - var cache = new LRU(2) - cache.set("a", "A") - cache.del("a") - t.equal(cache.get("a"), undefined) - t.end() -}) - -test("max", function (t) { - var cache = new LRU(3) - - // test changing the max, verify that the LRU items get dropped. - cache.max = 100 - for (var i = 0; i < 100; i ++) cache.set(i, i) - t.equal(cache.length, 100) - for (var i = 0; i < 100; i ++) { - t.equal(cache.get(i), i) - } - cache.max = 3 - t.equal(cache.length, 3) - for (var i = 0; i < 97; i ++) { - t.equal(cache.get(i), undefined) - } - for (var i = 98; i < 100; i ++) { - t.equal(cache.get(i), i) - } - - // now remove the max restriction, and try again. - cache.max = "hello" - for (var i = 0; i < 100; i ++) cache.set(i, i) - t.equal(cache.length, 100) - for (var i = 0; i < 100; i ++) { - t.equal(cache.get(i), i) - } - // should trigger an immediate resize - cache.max = 3 - t.equal(cache.length, 3) - for (var i = 0; i < 97; i ++) { - t.equal(cache.get(i), undefined) - } - for (var i = 98; i < 100; i ++) { - t.equal(cache.get(i), i) - } - t.end() -}) - -test("reset", function (t) { - var cache = new LRU(10) - cache.set("a", "A") - cache.set("b", "B") - cache.reset() - t.equal(cache.length, 0) - t.equal(cache.max, 10) - t.equal(cache.get("a"), undefined) - t.equal(cache.get("b"), undefined) - t.end() -}) - - -// Note: `.dump()` is a debugging tool only. No guarantees are made -// about the format/layout of the response. -test("dump", function (t) { - var cache = new LRU(10) - var d = cache.dump(); - t.equal(Object.keys(d).length, 0, "nothing in dump for empty cache") - cache.set("a", "A") - var d = cache.dump() // { a: { key: "a", value: "A", lu: 0 } } - t.ok(d.a) - t.equal(d.a.key, "a") - t.equal(d.a.value, "A") - t.equal(d.a.lu, 0) - - cache.set("b", "B") - cache.get("b") - d = cache.dump() - t.ok(d.b) - t.equal(d.b.key, "b") - t.equal(d.b.value, "B") - t.equal(d.b.lu, 2) - - t.end() -}) - - -test("basic with weighed length", function (t) { - var cache = new LRU({ - max: 100, - length: function (item) { return item.size } - }) - cache.set("key", {val: "value", size: 50}) - t.equal(cache.get("key").val, "value") - t.equal(cache.get("nada"), undefined) - t.equal(cache.lengthCalculator(cache.get("key")), 50) - t.equal(cache.length, 50) - t.equal(cache.max, 100) - t.end() -}) - - -test("weighed length item too large", function (t) { - var cache = new LRU({ - max: 10, - length: function (item) { return item.size } - }) - t.equal(cache.max, 10) - - // should fall out immediately - cache.set("key", {val: "value", size: 50}) - - t.equal(cache.length, 0) - t.equal(cache.get("key"), undefined) - t.end() -}) - -test("least recently set with weighed length", function (t) { - var cache = new LRU({ - max:8, - length: function (item) { return item.length } - }) - cache.set("a", "A") - cache.set("b", "BB") - cache.set("c", "CCC") - cache.set("d", "DDDD") - t.equal(cache.get("d"), "DDDD") - t.equal(cache.get("c"), "CCC") - t.equal(cache.get("b"), undefined) - t.equal(cache.get("a"), undefined) - t.end() -}) - -test("lru recently gotten with weighed length", function (t) { - var cache = new LRU({ - max: 8, - length: function (item) { return item.length } - }) - cache.set("a", "A") - cache.set("b", "BB") - cache.set("c", "CCC") - cache.get("a") - cache.get("b") - cache.set("d", "DDDD") - t.equal(cache.get("c"), undefined) - t.equal(cache.get("d"), "DDDD") - t.equal(cache.get("b"), "BB") - t.equal(cache.get("a"), "A") - t.end() -}) - -test("set returns proper booleans", function(t) { - var cache = new LRU({ - max: 5, - length: function (item) { return item.length } - }) - - t.equal(cache.set("a", "A"), true) - - // should return false for max exceeded - t.equal(cache.set("b", "donuts"), false) - - t.equal(cache.set("b", "B"), true) - t.equal(cache.set("c", "CCCC"), true) - t.end() -}) - -test("drop the old items", function(t) { - var cache = new LRU({ - max: 5, - maxAge: 50 - }) - - cache.set("a", "A") - - setTimeout(function () { - cache.set("b", "b") - t.equal(cache.get("a"), "A") - }, 25) - - setTimeout(function () { - cache.set("c", "C") - // timed out - t.notOk(cache.get("a")) - }, 60) - - setTimeout(function () { - t.notOk(cache.get("b")) - t.equal(cache.get("c"), "C") - }, 90) - - setTimeout(function () { - t.notOk(cache.get("c")) - t.end() - }, 155) -}) - -test("disposal function", function(t) { - var disposed = false - var cache = new LRU({ - max: 1, - dispose: function (k, n) { - disposed = n - } - }) - - cache.set(1, 1) - cache.set(2, 2) - t.equal(disposed, 1) - cache.set(3, 3) - t.equal(disposed, 2) - cache.reset() - t.equal(disposed, 3) - t.end() -}) - -test("disposal function on too big of item", function(t) { - var disposed = false - var cache = new LRU({ - max: 1, - length: function (k) { - return k.length - }, - dispose: function (k, n) { - disposed = n - } - }) - var obj = [ 1, 2 ] - - t.equal(disposed, false) - cache.set("obj", obj) - t.equal(disposed, obj) - t.end() -}) - -test("has()", function(t) { - var cache = new LRU({ - max: 1, - maxAge: 10 - }) - - cache.set('foo', 'bar') - t.equal(cache.has('foo'), true) - cache.set('blu', 'baz') - t.equal(cache.has('foo'), false) - t.equal(cache.has('blu'), true) - setTimeout(function() { - t.equal(cache.has('blu'), false) - t.end() - }, 15) -}) - -test("stale", function(t) { - var cache = new LRU({ - maxAge: 10, - stale: true - }) - - cache.set('foo', 'bar') - t.equal(cache.get('foo'), 'bar') - t.equal(cache.has('foo'), true) - setTimeout(function() { - t.equal(cache.has('foo'), false) - t.equal(cache.get('foo'), 'bar') - t.equal(cache.get('foo'), undefined) - t.end() - }, 15) -}) - -test("lru update via set", function(t) { - var cache = LRU({ max: 2 }); - - cache.set('foo', 1); - cache.set('bar', 2); - cache.del('bar'); - cache.set('baz', 3); - cache.set('qux', 4); - - t.equal(cache.get('foo'), undefined) - t.equal(cache.get('bar'), undefined) - t.equal(cache.get('baz'), 3) - t.equal(cache.get('qux'), 4) - t.end() -}) - -test("least recently set w/ peek", function (t) { - var cache = new LRU(2) - cache.set("a", "A") - cache.set("b", "B") - t.equal(cache.peek("a"), "A") - cache.set("c", "C") - t.equal(cache.get("c"), "C") - t.equal(cache.get("b"), "B") - t.equal(cache.get("a"), undefined) - t.end() -}) - -test("pop the least used item", function (t) { - var cache = new LRU(3) - , last - - cache.set("a", "A") - cache.set("b", "B") - cache.set("c", "C") - - t.equal(cache.length, 3) - t.equal(cache.max, 3) - - // Ensure we pop a, c, b - cache.get("b", "B") - - last = cache.pop() - t.equal(last.key, "a") - t.equal(last.value, "A") - t.equal(cache.length, 2) - t.equal(cache.max, 3) - - last = cache.pop() - t.equal(last.key, "c") - t.equal(last.value, "C") - t.equal(cache.length, 1) - t.equal(cache.max, 3) - - last = cache.pop() - t.equal(last.key, "b") - t.equal(last.value, "B") - t.equal(cache.length, 0) - t.equal(cache.max, 3) - - last = cache.pop() - t.equal(last, null) - t.equal(cache.length, 0) - t.equal(cache.max, 3) - - t.end() -}) diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/foreach.js b/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/foreach.js deleted file mode 100644 index eefb80d9d1..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/foreach.js +++ /dev/null @@ -1,52 +0,0 @@ -var test = require('tap').test -var LRU = require('../') - -test('forEach', function (t) { - var l = new LRU(5) - for (var i = 0; i < 10; i ++) { - l.set(i.toString(), i.toString(2)) - } - - var i = 9 - l.forEach(function (val, key, cache) { - t.equal(cache, l) - t.equal(key, i.toString()) - t.equal(val, i.toString(2)) - i -= 1 - }) - - // get in order of most recently used - l.get(6) - l.get(8) - - var order = [ 8, 6, 9, 7, 5 ] - var i = 0 - - l.forEach(function (val, key, cache) { - var j = order[i ++] - t.equal(cache, l) - t.equal(key, j.toString()) - t.equal(val, j.toString(2)) - }) - - t.end() -}) - -test('keys() and values()', function (t) { - var l = new LRU(5) - for (var i = 0; i < 10; i ++) { - l.set(i.toString(), i.toString(2)) - } - - t.similar(l.keys(), ['9', '8', '7', '6', '5']) - t.similar(l.values(), ['1001', '1000', '111', '110', '101']) - - // get in order of most recently used - l.get(6) - l.get(8) - - t.similar(l.keys(), ['8', '6', '9', '7', '5']) - t.similar(l.values(), ['1000', '110', '1001', '111', '101']) - - t.end() -}) diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/memory-leak.js b/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/memory-leak.js deleted file mode 100644 index 7af45b0221..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/lru-cache/test/memory-leak.js +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env node --expose_gc - -var weak = require('weak'); -var test = require('tap').test -var LRU = require('../') -var l = new LRU({ max: 10 }) -var refs = 0 -function X() { - refs ++ - weak(this, deref) -} - -function deref() { - refs -- -} - -test('no leaks', function (t) { - // fill up the cache - for (var i = 0; i < 100; i++) { - l.set(i, new X); - // throw some gets in there, too. - if (i % 2 === 0) - l.get(i / 2) - } - - gc() - - var start = process.memoryUsage() - - // capture the memory - var startRefs = refs - - // do it again, but more - for (var i = 0; i < 10000; i++) { - l.set(i, new X); - // throw some gets in there, too. - if (i % 2 === 0) - l.get(i / 2) - } - - gc() - - var end = process.memoryUsage() - t.equal(refs, startRefs, 'no leaky refs') - - console.error('start: %j\n' + - 'end: %j', start, end); - t.pass(); - t.end(); -}) diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/LICENSE b/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/LICENSE deleted file mode 100644 index 0c44ae716d..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) Isaac Z. Schlueter ("Author") -All rights reserved. - -The BSD License - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/README.md b/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/README.md deleted file mode 100644 index 7e365129e4..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# sigmund - -Quick and dirty signatures for Objects. - -This is like a much faster `deepEquals` comparison, which returns a -string key suitable for caches and the like. - -## Usage - -```javascript -function doSomething (someObj) { - var key = sigmund(someObj, maxDepth) // max depth defaults to 10 - var cached = cache.get(key) - if (cached) return cached) - - var result = expensiveCalculation(someObj) - cache.set(key, result) - return result -} -``` - -The resulting key will be as unique and reproducible as calling -`JSON.stringify` or `util.inspect` on the object, but is much faster. -In order to achieve this speed, some differences are glossed over. -For example, the object `{0:'foo'}` will be treated identically to the -array `['foo']`. - -Also, just as there is no way to summon the soul from the scribblings -of a cocain-addled psychoanalyst, there is no way to revive the object -from the signature string that sigmund gives you. In fact, it's -barely even readable. - -As with `sys.inspect` and `JSON.stringify`, larger objects will -produce larger signature strings. - -Because sigmund is a bit less strict than the more thorough -alternatives, the strings will be shorter, and also there is a -slightly higher chance for collisions. For example, these objects -have the same signature: - - var obj1 = {a:'b',c:/def/,g:['h','i',{j:'',k:'l'}]} - var obj2 = {a:'b',c:'/def/',g:['h','i','{jkl']} - -Like a good Freudian, sigmund is most effective when you already have -some understanding of what you're looking for. It can help you help -yourself, but you must be willing to do some work as well. - -Cycles are handled, and cyclical objects are silently omitted (though -the key is included in the signature output.) - -The second argument is the maximum depth, which defaults to 10, -because that is the maximum object traversal depth covered by most -insurance carriers. diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/bench.js b/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/bench.js deleted file mode 100644 index 5acfd6d90d..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/bench.js +++ /dev/null @@ -1,283 +0,0 @@ -// different ways to id objects -// use a req/res pair, since it's crazy deep and cyclical - -// sparseFE10 and sigmund are usually pretty close, which is to be expected, -// since they are essentially the same algorithm, except that sigmund handles -// regular expression objects properly. - - -var http = require('http') -var util = require('util') -var sigmund = require('./sigmund.js') -var sreq, sres, creq, cres, test - -http.createServer(function (q, s) { - sreq = q - sres = s - sres.end('ok') - this.close(function () { setTimeout(function () { - start() - }, 200) }) -}).listen(1337, function () { - creq = http.get({ port: 1337 }) - creq.on('response', function (s) { cres = s }) -}) - -function start () { - test = [sreq, sres, creq, cres] - // test = sreq - // sreq.sres = sres - // sreq.creq = creq - // sreq.cres = cres - - for (var i in exports.compare) { - console.log(i) - var hash = exports.compare[i]() - console.log(hash) - console.log(hash.length) - console.log('') - } - - require('bench').runMain() -} - -function customWs (obj, md, d) { - d = d || 0 - var to = typeof obj - if (to === 'undefined' || to === 'function' || to === null) return '' - if (d > md || !obj || to !== 'object') return ('' + obj).replace(/[\n ]+/g, '') - - if (Array.isArray(obj)) { - return obj.map(function (i, _, __) { - return customWs(i, md, d + 1) - }).reduce(function (a, b) { return a + b }, '') - } - - var keys = Object.keys(obj) - return keys.map(function (k, _, __) { - return k + ':' + customWs(obj[k], md, d + 1) - }).reduce(function (a, b) { return a + b }, '') -} - -function custom (obj, md, d) { - d = d || 0 - var to = typeof obj - if (to === 'undefined' || to === 'function' || to === null) return '' - if (d > md || !obj || to !== 'object') return '' + obj - - if (Array.isArray(obj)) { - return obj.map(function (i, _, __) { - return custom(i, md, d + 1) - }).reduce(function (a, b) { return a + b }, '') - } - - var keys = Object.keys(obj) - return keys.map(function (k, _, __) { - return k + ':' + custom(obj[k], md, d + 1) - }).reduce(function (a, b) { return a + b }, '') -} - -function sparseFE2 (obj, maxDepth) { - var seen = [] - var soFar = '' - function ch (v, depth) { - if (depth > maxDepth) return - if (typeof v === 'function' || typeof v === 'undefined') return - if (typeof v !== 'object' || !v) { - soFar += v - return - } - if (seen.indexOf(v) !== -1 || depth === maxDepth) return - seen.push(v) - soFar += '{' - Object.keys(v).forEach(function (k, _, __) { - // pseudo-private values. skip those. - if (k.charAt(0) === '_') return - var to = typeof v[k] - if (to === 'function' || to === 'undefined') return - soFar += k + ':' - ch(v[k], depth + 1) - }) - soFar += '}' - } - ch(obj, 0) - return soFar -} - -function sparseFE (obj, maxDepth) { - var seen = [] - var soFar = '' - function ch (v, depth) { - if (depth > maxDepth) return - if (typeof v === 'function' || typeof v === 'undefined') return - if (typeof v !== 'object' || !v) { - soFar += v - return - } - if (seen.indexOf(v) !== -1 || depth === maxDepth) return - seen.push(v) - soFar += '{' - Object.keys(v).forEach(function (k, _, __) { - // pseudo-private values. skip those. - if (k.charAt(0) === '_') return - var to = typeof v[k] - if (to === 'function' || to === 'undefined') return - soFar += k - ch(v[k], depth + 1) - }) - } - ch(obj, 0) - return soFar -} - -function sparse (obj, maxDepth) { - var seen = [] - var soFar = '' - function ch (v, depth) { - if (depth > maxDepth) return - if (typeof v === 'function' || typeof v === 'undefined') return - if (typeof v !== 'object' || !v) { - soFar += v - return - } - if (seen.indexOf(v) !== -1 || depth === maxDepth) return - seen.push(v) - soFar += '{' - for (var k in v) { - // pseudo-private values. skip those. - if (k.charAt(0) === '_') continue - var to = typeof v[k] - if (to === 'function' || to === 'undefined') continue - soFar += k - ch(v[k], depth + 1) - } - } - ch(obj, 0) - return soFar -} - -function noCommas (obj, maxDepth) { - var seen = [] - var soFar = '' - function ch (v, depth) { - if (depth > maxDepth) return - if (typeof v === 'function' || typeof v === 'undefined') return - if (typeof v !== 'object' || !v) { - soFar += v - return - } - if (seen.indexOf(v) !== -1 || depth === maxDepth) return - seen.push(v) - soFar += '{' - for (var k in v) { - // pseudo-private values. skip those. - if (k.charAt(0) === '_') continue - var to = typeof v[k] - if (to === 'function' || to === 'undefined') continue - soFar += k + ':' - ch(v[k], depth + 1) - } - soFar += '}' - } - ch(obj, 0) - return soFar -} - - -function flatten (obj, maxDepth) { - var seen = [] - var soFar = '' - function ch (v, depth) { - if (depth > maxDepth) return - if (typeof v === 'function' || typeof v === 'undefined') return - if (typeof v !== 'object' || !v) { - soFar += v - return - } - if (seen.indexOf(v) !== -1 || depth === maxDepth) return - seen.push(v) - soFar += '{' - for (var k in v) { - // pseudo-private values. skip those. - if (k.charAt(0) === '_') continue - var to = typeof v[k] - if (to === 'function' || to === 'undefined') continue - soFar += k + ':' - ch(v[k], depth + 1) - soFar += ',' - } - soFar += '}' - } - ch(obj, 0) - return soFar -} - -exports.compare = -{ - // 'custom 2': function () { - // return custom(test, 2, 0) - // }, - // 'customWs 2': function () { - // return customWs(test, 2, 0) - // }, - 'JSON.stringify (guarded)': function () { - var seen = [] - return JSON.stringify(test, function (k, v) { - if (typeof v !== 'object' || !v) return v - if (seen.indexOf(v) !== -1) return undefined - seen.push(v) - return v - }) - }, - - 'flatten 10': function () { - return flatten(test, 10) - }, - - // 'flattenFE 10': function () { - // return flattenFE(test, 10) - // }, - - 'noCommas 10': function () { - return noCommas(test, 10) - }, - - 'sparse 10': function () { - return sparse(test, 10) - }, - - 'sparseFE 10': function () { - return sparseFE(test, 10) - }, - - 'sparseFE2 10': function () { - return sparseFE2(test, 10) - }, - - sigmund: function() { - return sigmund(test, 10) - }, - - - // 'util.inspect 1': function () { - // return util.inspect(test, false, 1, false) - // }, - // 'util.inspect undefined': function () { - // util.inspect(test) - // }, - // 'util.inspect 2': function () { - // util.inspect(test, false, 2, false) - // }, - // 'util.inspect 3': function () { - // util.inspect(test, false, 3, false) - // }, - // 'util.inspect 4': function () { - // util.inspect(test, false, 4, false) - // }, - // 'util.inspect Infinity': function () { - // util.inspect(test, false, Infinity, false) - // } -} - -/** results -**/ diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/package.json b/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/package.json deleted file mode 100644 index cb7e2bd4b7..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/package.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "sigmund", - "version": "1.0.0", - "description": "Quick and dirty signatures for Objects.", - "main": "sigmund.js", - "directories": { - "test": "test" - }, - "dependencies": {}, - "devDependencies": { - "tap": "~0.3.0" - }, - "scripts": { - "test": "tap test/*.js", - "bench": "node bench.js" - }, - "repository": { - "type": "git", - "url": "git://github.com/isaacs/sigmund" - }, - "keywords": [ - "object", - "signature", - "key", - "data", - "psychoanalysis" - ], - "author": { - "name": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/" - }, - "license": "BSD", - "readme": "# sigmund\n\nQuick and dirty signatures for Objects.\n\nThis is like a much faster `deepEquals` comparison, which returns a\nstring key suitable for caches and the like.\n\n## Usage\n\n```javascript\nfunction doSomething (someObj) {\n var key = sigmund(someObj, maxDepth) // max depth defaults to 10\n var cached = cache.get(key)\n if (cached) return cached)\n\n var result = expensiveCalculation(someObj)\n cache.set(key, result)\n return result\n}\n```\n\nThe resulting key will be as unique and reproducible as calling\n`JSON.stringify` or `util.inspect` on the object, but is much faster.\nIn order to achieve this speed, some differences are glossed over.\nFor example, the object `{0:'foo'}` will be treated identically to the\narray `['foo']`.\n\nAlso, just as there is no way to summon the soul from the scribblings\nof a cocain-addled psychoanalyst, there is no way to revive the object\nfrom the signature string that sigmund gives you. In fact, it's\nbarely even readable.\n\nAs with `sys.inspect` and `JSON.stringify`, larger objects will\nproduce larger signature strings.\n\nBecause sigmund is a bit less strict than the more thorough\nalternatives, the strings will be shorter, and also there is a\nslightly higher chance for collisions. For example, these objects\nhave the same signature:\n\n var obj1 = {a:'b',c:/def/,g:['h','i',{j:'',k:'l'}]}\n var obj2 = {a:'b',c:'/def/',g:['h','i','{jkl']}\n\nLike a good Freudian, sigmund is most effective when you already have\nsome understanding of what you're looking for. It can help you help\nyourself, but you must be willing to do some work as well.\n\nCycles are handled, and cyclical objects are silently omitted (though\nthe key is included in the signature output.)\n\nThe second argument is the maximum depth, which defaults to 10,\nbecause that is the maximum object traversal depth covered by most\ninsurance carriers.\n", - "readmeFilename": "README.md", - "bugs": { - "url": "https://github.com/isaacs/sigmund/issues" - }, - "homepage": "https://github.com/isaacs/sigmund", - "_id": "sigmund@1.0.0", - "_from": "sigmund@~1.0.0" -} diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/sigmund.js b/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/sigmund.js deleted file mode 100644 index 82c7ab8ce9..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/sigmund.js +++ /dev/null @@ -1,39 +0,0 @@ -module.exports = sigmund -function sigmund (subject, maxSessions) { - maxSessions = maxSessions || 10; - var notes = []; - var analysis = ''; - var RE = RegExp; - - function psychoAnalyze (subject, session) { - if (session > maxSessions) return; - - if (typeof subject === 'function' || - typeof subject === 'undefined') { - return; - } - - if (typeof subject !== 'object' || !subject || - (subject instanceof RE)) { - analysis += subject; - return; - } - - if (notes.indexOf(subject) !== -1 || session === maxSessions) return; - - notes.push(subject); - analysis += '{'; - Object.keys(subject).forEach(function (issue, _, __) { - // pseudo-private values. skip those. - if (issue.charAt(0) === '_') return; - var to = typeof subject[issue]; - if (to === 'function' || to === 'undefined') return; - analysis += issue; - psychoAnalyze(subject[issue], session + 1); - }); - } - psychoAnalyze(subject, 0); - return analysis; -} - -// vim: set softtabstop=4 shiftwidth=4: diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/test/basic.js b/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/test/basic.js deleted file mode 100644 index 50c53a13e9..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/node_modules/sigmund/test/basic.js +++ /dev/null @@ -1,24 +0,0 @@ -var test = require('tap').test -var sigmund = require('../sigmund.js') - - -// occasionally there are duplicates -// that's an acceptable edge-case. JSON.stringify and util.inspect -// have some collision potential as well, though less, and collision -// detection is expensive. -var hash = '{abc/def/g{0h1i2{jkl' -var obj1 = {a:'b',c:/def/,g:['h','i',{j:'',k:'l'}]} -var obj2 = {a:'b',c:'/def/',g:['h','i','{jkl']} - -var obj3 = JSON.parse(JSON.stringify(obj1)) -obj3.c = /def/ -obj3.g[2].cycle = obj3 -var cycleHash = '{abc/def/g{0h1i2{jklcycle' - -test('basic', function (t) { - t.equal(sigmund(obj1), hash) - t.equal(sigmund(obj2), hash) - t.equal(sigmund(obj3), cycleHash) - t.end() -}) - diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/package.json b/tests/mocha/node_modules/glob/node_modules/minimatch/package.json deleted file mode 100644 index f8f545aa3f..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "author": { - "name": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/" - }, - "name": "minimatch", - "description": "a glob matcher in javascript", - "version": "0.2.14", - "repository": { - "type": "git", - "url": "git://github.com/isaacs/minimatch.git" - }, - "main": "minimatch.js", - "scripts": { - "test": "tap test/*.js" - }, - "engines": { - "node": "*" - }, - "dependencies": { - "lru-cache": "2", - "sigmund": "~1.0.0" - }, - "devDependencies": { - "tap": "" - }, - "license": { - "type": "MIT", - "url": "http://github.com/isaacs/minimatch/raw/master/LICENSE" - }, - "readme": "# minimatch\n\nA minimal matching utility.\n\n[![Build Status](https://secure.travis-ci.org/isaacs/minimatch.png)](http://travis-ci.org/isaacs/minimatch)\n\n\nThis is the matching library used internally by npm.\n\nEventually, it will replace the C binding in node-glob.\n\nIt works by converting glob expressions into JavaScript `RegExp`\nobjects.\n\n## Usage\n\n```javascript\nvar minimatch = require(\"minimatch\")\n\nminimatch(\"bar.foo\", \"*.foo\") // true!\nminimatch(\"bar.foo\", \"*.bar\") // false!\nminimatch(\"bar.foo\", \"*.+(bar|foo)\", { debug: true }) // true, and noisy!\n```\n\n## Features\n\nSupports these glob features:\n\n* Brace Expansion\n* Extended glob matching\n* \"Globstar\" `**` matching\n\nSee:\n\n* `man sh`\n* `man bash`\n* `man 3 fnmatch`\n* `man 5 gitignore`\n\n## Minimatch Class\n\nCreate a minimatch object by instanting the `minimatch.Minimatch` class.\n\n```javascript\nvar Minimatch = require(\"minimatch\").Minimatch\nvar mm = new Minimatch(pattern, options)\n```\n\n### Properties\n\n* `pattern` The original pattern the minimatch object represents.\n* `options` The options supplied to the constructor.\n* `set` A 2-dimensional array of regexp or string expressions.\n Each row in the\n array corresponds to a brace-expanded pattern. Each item in the row\n corresponds to a single path-part. For example, the pattern\n `{a,b/c}/d` would expand to a set of patterns like:\n\n [ [ a, d ]\n , [ b, c, d ] ]\n\n If a portion of the pattern doesn't have any \"magic\" in it\n (that is, it's something like `\"foo\"` rather than `fo*o?`), then it\n will be left as a string rather than converted to a regular\n expression.\n\n* `regexp` Created by the `makeRe` method. A single regular expression\n expressing the entire pattern. This is useful in cases where you wish\n to use the pattern somewhat like `fnmatch(3)` with `FNM_PATH` enabled.\n* `negate` True if the pattern is negated.\n* `comment` True if the pattern is a comment.\n* `empty` True if the pattern is `\"\"`.\n\n### Methods\n\n* `makeRe` Generate the `regexp` member if necessary, and return it.\n Will return `false` if the pattern is invalid.\n* `match(fname)` Return true if the filename matches the pattern, or\n false otherwise.\n* `matchOne(fileArray, patternArray, partial)` Take a `/`-split\n filename, and match it against a single row in the `regExpSet`. This\n method is mainly for internal use, but is exposed so that it can be\n used by a glob-walker that needs to avoid excessive filesystem calls.\n\nAll other methods are internal, and will be called as necessary.\n\n## Functions\n\nThe top-level exported function has a `cache` property, which is an LRU\ncache set to store 100 items. So, calling these methods repeatedly\nwith the same pattern and options will use the same Minimatch object,\nsaving the cost of parsing it multiple times.\n\n### minimatch(path, pattern, options)\n\nMain export. Tests a path against the pattern using the options.\n\n```javascript\nvar isJS = minimatch(file, \"*.js\", { matchBase: true })\n```\n\n### minimatch.filter(pattern, options)\n\nReturns a function that tests its\nsupplied argument, suitable for use with `Array.filter`. Example:\n\n```javascript\nvar javascripts = fileList.filter(minimatch.filter(\"*.js\", {matchBase: true}))\n```\n\n### minimatch.match(list, pattern, options)\n\nMatch against the list of\nfiles, in the style of fnmatch or glob. If nothing is matched, and\noptions.nonull is set, then return a list containing the pattern itself.\n\n```javascript\nvar javascripts = minimatch.match(fileList, \"*.js\", {matchBase: true}))\n```\n\n### minimatch.makeRe(pattern, options)\n\nMake a regular expression object from the pattern.\n\n## Options\n\nAll options are `false` by default.\n\n### debug\n\nDump a ton of stuff to stderr.\n\n### nobrace\n\nDo not expand `{a,b}` and `{1..3}` brace sets.\n\n### noglobstar\n\nDisable `**` matching against multiple folder names.\n\n### dot\n\nAllow patterns to match filenames starting with a period, even if\nthe pattern does not explicitly have a period in that spot.\n\nNote that by default, `a/**/b` will **not** match `a/.d/b`, unless `dot`\nis set.\n\n### noext\n\nDisable \"extglob\" style patterns like `+(a|b)`.\n\n### nocase\n\nPerform a case-insensitive match.\n\n### nonull\n\nWhen a match is not found by `minimatch.match`, return a list containing\nthe pattern itself. When set, an empty list is returned if there are\nno matches.\n\n### matchBase\n\nIf set, then patterns without slashes will be matched\nagainst the basename of the path if it contains slashes. For example,\n`a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`.\n\n### nocomment\n\nSuppress the behavior of treating `#` at the start of a pattern as a\ncomment.\n\n### nonegate\n\nSuppress the behavior of treating a leading `!` character as negation.\n\n### flipNegate\n\nReturns from negate expressions the same as if they were not negated.\n(Ie, true on a hit, false on a miss.)\n\n\n## Comparisons to other fnmatch/glob implementations\n\nWhile strict compliance with the existing standards is a worthwhile\ngoal, some discrepancies exist between minimatch and other\nimplementations, and are intentional.\n\nIf the pattern starts with a `!` character, then it is negated. Set the\n`nonegate` flag to suppress this behavior, and treat leading `!`\ncharacters normally. This is perhaps relevant if you wish to start the\npattern with a negative extglob pattern like `!(a|B)`. Multiple `!`\ncharacters at the start of a pattern will negate the pattern multiple\ntimes.\n\nIf a pattern starts with `#`, then it is treated as a comment, and\nwill not match anything. Use `\\#` to match a literal `#` at the\nstart of a line, or set the `nocomment` flag to suppress this behavior.\n\nThe double-star character `**` is supported by default, unless the\n`noglobstar` flag is set. This is supported in the manner of bsdglob\nand bash 4.1, where `**` only has special significance if it is the only\nthing in a path part. That is, `a/**/b` will match `a/x/y/b`, but\n`a/**b` will not.\n\nIf an escaped pattern has no matches, and the `nonull` flag is set,\nthen minimatch.match returns the pattern as-provided, rather than\ninterpreting the character escapes. For example,\n`minimatch.match([], \"\\\\*a\\\\?\")` will return `\"\\\\*a\\\\?\"` rather than\n`\"*a?\"`. This is akin to setting the `nullglob` option in bash, except\nthat it does not resolve escaped pattern characters.\n\nIf brace expansion is not disabled, then it is performed before any\nother interpretation of the glob pattern. Thus, a pattern like\n`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded\n**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are\nchecked for validity. Since those two are valid, matching proceeds.\n", - "readmeFilename": "README.md", - "bugs": { - "url": "https://github.com/isaacs/minimatch/issues" - }, - "homepage": "https://github.com/isaacs/minimatch", - "_id": "minimatch@0.2.14", - "_from": "minimatch@~0.2.11" -} diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/test/basic.js b/tests/mocha/node_modules/glob/node_modules/minimatch/test/basic.js deleted file mode 100644 index ae7ac73c77..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/test/basic.js +++ /dev/null @@ -1,399 +0,0 @@ -// http://www.bashcookbook.com/bashinfo/source/bash-1.14.7/tests/glob-test -// -// TODO: Some of these tests do very bad things with backslashes, and will -// most likely fail badly on windows. They should probably be skipped. - -var tap = require("tap") - , globalBefore = Object.keys(global) - , mm = require("../") - , files = [ "a", "b", "c", "d", "abc" - , "abd", "abe", "bb", "bcd" - , "ca", "cb", "dd", "de" - , "bdir/", "bdir/cfile"] - , next = files.concat([ "a-b", "aXb" - , ".x", ".y" ]) - - -var patterns = - [ "http://www.bashcookbook.com/bashinfo/source/bash-1.14.7/tests/glob-test" - , ["a*", ["a", "abc", "abd", "abe"]] - , ["X*", ["X*"], {nonull: true}] - - // allow null glob expansion - , ["X*", []] - - // isaacs: Slightly different than bash/sh/ksh - // \\* is not un-escaped to literal "*" in a failed match, - // but it does make it get treated as a literal star - , ["\\*", ["\\*"], {nonull: true}] - , ["\\**", ["\\**"], {nonull: true}] - , ["\\*\\*", ["\\*\\*"], {nonull: true}] - - , ["b*/", ["bdir/"]] - , ["c*", ["c", "ca", "cb"]] - , ["**", files] - - , ["\\.\\./*/", ["\\.\\./*/"], {nonull: true}] - , ["s/\\..*//", ["s/\\..*//"], {nonull: true}] - - , "legendary larry crashes bashes" - , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/" - , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"], {nonull: true}] - , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/" - , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"], {nonull: true}] - - , "character classes" - , ["[a-c]b*", ["abc", "abd", "abe", "bb", "cb"]] - , ["[a-y]*[^c]", ["abd", "abe", "bb", "bcd", - "bdir/", "ca", "cb", "dd", "de"]] - , ["a*[^c]", ["abd", "abe"]] - , function () { files.push("a-b", "aXb") } - , ["a[X-]b", ["a-b", "aXb"]] - , function () { files.push(".x", ".y") } - , ["[^a-c]*", ["d", "dd", "de"]] - , function () { files.push("a*b/", "a*b/ooo") } - , ["a\\*b/*", ["a*b/ooo"]] - , ["a\\*?/*", ["a*b/ooo"]] - , ["*\\\\!*", [], {null: true}, ["echo !7"]] - , ["*\\!*", ["echo !7"], null, ["echo !7"]] - , ["*.\\*", ["r.*"], null, ["r.*"]] - , ["a[b]c", ["abc"]] - , ["a[\\b]c", ["abc"]] - , ["a?c", ["abc"]] - , ["a\\*c", [], {null: true}, ["abc"]] - , ["", [""], { null: true }, [""]] - - , "http://www.opensource.apple.com/source/bash/bash-23/" + - "bash/tests/glob-test" - , function () { files.push("man/", "man/man1/", "man/man1/bash.1") } - , ["*/man*/bash.*", ["man/man1/bash.1"]] - , ["man/man1/bash.1", ["man/man1/bash.1"]] - , ["a***c", ["abc"], null, ["abc"]] - , ["a*****?c", ["abc"], null, ["abc"]] - , ["?*****??", ["abc"], null, ["abc"]] - , ["*****??", ["abc"], null, ["abc"]] - , ["?*****?c", ["abc"], null, ["abc"]] - , ["?***?****c", ["abc"], null, ["abc"]] - , ["?***?****?", ["abc"], null, ["abc"]] - , ["?***?****", ["abc"], null, ["abc"]] - , ["*******c", ["abc"], null, ["abc"]] - , ["*******?", ["abc"], null, ["abc"]] - , ["a*cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]] - , ["a**?**cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]] - , ["a**?**cd**?**??k***", ["abcdecdhjk"], null, ["abcdecdhjk"]] - , ["a**?**cd**?**??***k", ["abcdecdhjk"], null, ["abcdecdhjk"]] - , ["a**?**cd**?**??***k**", ["abcdecdhjk"], null, ["abcdecdhjk"]] - , ["a****c**?**??*****", ["abcdecdhjk"], null, ["abcdecdhjk"]] - , ["[-abc]", ["-"], null, ["-"]] - , ["[abc-]", ["-"], null, ["-"]] - , ["\\", ["\\"], null, ["\\"]] - , ["[\\\\]", ["\\"], null, ["\\"]] - , ["[[]", ["["], null, ["["]] - , ["[", ["["], null, ["["]] - , ["[*", ["[abc"], null, ["[abc"]] - , "a right bracket shall lose its special meaning and\n" + - "represent itself in a bracket expression if it occurs\n" + - "first in the list. -- POSIX.2 2.8.3.2" - , ["[]]", ["]"], null, ["]"]] - , ["[]-]", ["]"], null, ["]"]] - , ["[a-\z]", ["p"], null, ["p"]] - , ["??**********?****?", [], { null: true }, ["abc"]] - , ["??**********?****c", [], { null: true }, ["abc"]] - , ["?************c****?****", [], { null: true }, ["abc"]] - , ["*c*?**", [], { null: true }, ["abc"]] - , ["a*****c*?**", [], { null: true }, ["abc"]] - , ["a********???*******", [], { null: true }, ["abc"]] - , ["[]", [], { null: true }, ["a"]] - , ["[abc", [], { null: true }, ["["]] - - , "nocase tests" - , ["XYZ", ["xYz"], { nocase: true, null: true } - , ["xYz", "ABC", "IjK"]] - , ["ab*", ["ABC"], { nocase: true, null: true } - , ["xYz", "ABC", "IjK"]] - , ["[ia]?[ck]", ["ABC", "IjK"], { nocase: true, null: true } - , ["xYz", "ABC", "IjK"]] - - // [ pattern, [matches], MM opts, files, TAP opts] - , "onestar/twostar" - , ["{/*,*}", [], {null: true}, ["/asdf/asdf/asdf"]] - , ["{/?,*}", ["/a", "bb"], {null: true} - , ["/a", "/b/b", "/a/b/c", "bb"]] - - , "dots should not match unless requested" - , ["**", ["a/b"], {}, ["a/b", "a/.d", ".a/.d"]] - - // .. and . can only match patterns starting with ., - // even when options.dot is set. - , function () { - files = ["a/./b", "a/../b", "a/c/b", "a/.d/b"] - } - , ["a/*/b", ["a/c/b", "a/.d/b"], {dot: true}] - , ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: true}] - , ["a/*/b", ["a/c/b"], {dot:false}] - , ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: false}] - - - // this also tests that changing the options needs - // to change the cache key, even if the pattern is - // the same! - , ["**", ["a/b","a/.d",".a/.d"], { dot: true } - , [ ".a/.d", "a/.d", "a/b"]] - - , "paren sets cannot contain slashes" - , ["*(a/b)", ["*(a/b)"], {nonull: true}, ["a/b"]] - - // brace sets trump all else. - // - // invalid glob pattern. fails on bash4 and bsdglob. - // however, in this implementation, it's easier just - // to do the intuitive thing, and let brace-expansion - // actually come before parsing any extglob patterns, - // like the documentation seems to say. - // - // XXX: if anyone complains about this, either fix it - // or tell them to grow up and stop complaining. - // - // bash/bsdglob says this: - // , ["*(a|{b),c)}", ["*(a|{b),c)}"], {}, ["a", "ab", "ac", "ad"]] - // but we do this instead: - , ["*(a|{b),c)}", ["a", "ab", "ac"], {}, ["a", "ab", "ac", "ad"]] - - // test partial parsing in the presence of comment/negation chars - , ["[!a*", ["[!ab"], {}, ["[!ab", "[ab"]] - , ["[#a*", ["[#ab"], {}, ["[#ab", "[ab"]] - - // like: {a,b|c\\,d\\\|e} except it's unclosed, so it has to be escaped. - , ["+(a|*\\|c\\\\|d\\\\\\|e\\\\\\\\|f\\\\\\\\\\|g" - , ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g"] - , {} - , ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g", "a", "b\\c"]] - - - // crazy nested {,,} and *(||) tests. - , function () { - files = [ "a", "b", "c", "d" - , "ab", "ac", "ad" - , "bc", "cb" - , "bc,d", "c,db", "c,d" - , "d)", "(b|c", "*(b|c" - , "b|c", "b|cc", "cb|c" - , "x(a|b|c)", "x(a|c)" - , "(a|b|c)", "(a|c)"] - } - , ["*(a|{b,c})", ["a", "b", "c", "ab", "ac"]] - , ["{a,*(b|c,d)}", ["a","(b|c", "*(b|c", "d)"]] - // a - // *(b|c) - // *(b|d) - , ["{a,*(b|{c,d})}", ["a","b", "bc", "cb", "c", "d"]] - , ["*(a|{b|c,c})", ["a", "b", "c", "ab", "ac", "bc", "cb"]] - - - // test various flag settings. - , [ "*(a|{b|c,c})", ["x(a|b|c)", "x(a|c)", "(a|b|c)", "(a|c)"] - , { noext: true } ] - , ["a?b", ["x/y/acb", "acb/"], {matchBase: true} - , ["x/y/acb", "acb/", "acb/d/e", "x/y/acb/d"] ] - , ["#*", ["#a", "#b"], {nocomment: true}, ["#a", "#b", "c#d"]] - - - // begin channelling Boole and deMorgan... - , "negation tests" - , function () { - files = ["d", "e", "!ab", "!abc", "a!b", "\\!a"] - } - - // anything that is NOT a* matches. - , ["!a*", ["\\!a", "d", "e", "!ab", "!abc"]] - - // anything that IS !a* matches. - , ["!a*", ["!ab", "!abc"], {nonegate: true}] - - // anything that IS a* matches - , ["!!a*", ["a!b"]] - - // anything that is NOT !a* matches - , ["!\\!a*", ["a!b", "d", "e", "\\!a"]] - - // negation nestled within a pattern - , function () { - files = [ "foo.js" - , "foo.bar" - // can't match this one without negative lookbehind. - , "foo.js.js" - , "blar.js" - , "foo." - , "boo.js.boo" ] - } - , ["*.!(js)", ["foo.bar", "foo.", "boo.js.boo"] ] - - // https://github.com/isaacs/minimatch/issues/5 - , function () { - files = [ 'a/b/.x/c' - , 'a/b/.x/c/d' - , 'a/b/.x/c/d/e' - , 'a/b/.x' - , 'a/b/.x/' - , 'a/.x/b' - , '.x' - , '.x/' - , '.x/a' - , '.x/a/b' - , 'a/.x/b/.x/c' - , '.x/.x' ] - } - , ["**/.x/**", [ '.x/' - , '.x/a' - , '.x/a/b' - , 'a/.x/b' - , 'a/b/.x/' - , 'a/b/.x/c' - , 'a/b/.x/c/d' - , 'a/b/.x/c/d/e' ] ] - - ] - -var regexps = - [ '/^(?:(?=.)a[^/]*?)$/', - '/^(?:(?=.)X[^/]*?)$/', - '/^(?:(?=.)X[^/]*?)$/', - '/^(?:\\*)$/', - '/^(?:(?=.)\\*[^/]*?)$/', - '/^(?:\\*\\*)$/', - '/^(?:(?=.)b[^/]*?\\/)$/', - '/^(?:(?=.)c[^/]*?)$/', - '/^(?:(?:(?!(?:\\/|^)\\.).)*?)$/', - '/^(?:\\.\\.\\/(?!\\.)(?=.)[^/]*?\\/)$/', - '/^(?:s\\/(?=.)\\.\\.[^/]*?\\/)$/', - '/^(?:\\/\\^root:\\/\\{s\\/(?=.)\\^[^:][^/]*?:[^:][^/]*?:\\([^:]\\)[^/]*?\\.[^/]*?\\$\\/1\\/)$/', - '/^(?:\\/\\^root:\\/\\{s\\/(?=.)\\^[^:][^/]*?:[^:][^/]*?:\\([^:]\\)[^/]*?\\.[^/]*?\\$\\/\u0001\\/)$/', - '/^(?:(?!\\.)(?=.)[a-c]b[^/]*?)$/', - '/^(?:(?!\\.)(?=.)[a-y][^/]*?[^c])$/', - '/^(?:(?=.)a[^/]*?[^c])$/', - '/^(?:(?=.)a[X-]b)$/', - '/^(?:(?!\\.)(?=.)[^a-c][^/]*?)$/', - '/^(?:a\\*b\\/(?!\\.)(?=.)[^/]*?)$/', - '/^(?:(?=.)a\\*[^/]\\/(?!\\.)(?=.)[^/]*?)$/', - '/^(?:(?!\\.)(?=.)[^/]*?\\\\\\![^/]*?)$/', - '/^(?:(?!\\.)(?=.)[^/]*?\\![^/]*?)$/', - '/^(?:(?!\\.)(?=.)[^/]*?\\.\\*)$/', - '/^(?:(?=.)a[b]c)$/', - '/^(?:(?=.)a[b]c)$/', - '/^(?:(?=.)a[^/]c)$/', - '/^(?:a\\*c)$/', - 'false', - '/^(?:(?!\\.)(?=.)[^/]*?\\/(?=.)man[^/]*?\\/(?=.)bash\\.[^/]*?)$/', - '/^(?:man\\/man1\\/bash\\.1)$/', - '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?c)$/', - '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]c)$/', - '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/])$/', - '/^(?:(?!\\.)(?=.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/])$/', - '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]c)$/', - '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?c)$/', - '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/])$/', - '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?)$/', - '/^(?:(?!\\.)(?=.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?c)$/', - '/^(?:(?!\\.)(?=.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/])$/', - '/^(?:(?=.)a[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/]k)$/', - '/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/]k)$/', - '/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/]k[^/]*?[^/]*?[^/]*?)$/', - '/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/][^/]*?[^/]*?[^/]*?k)$/', - '/^(?:(?=.)a[^/]*?[^/]*?[^/][^/]*?[^/]*?cd[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/][^/]*?[^/]*?[^/]*?k[^/]*?[^/]*?)$/', - '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?c[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?)$/', - '/^(?:(?!\\.)(?=.)[-abc])$/', - '/^(?:(?!\\.)(?=.)[abc-])$/', - '/^(?:\\\\)$/', - '/^(?:(?!\\.)(?=.)[\\\\])$/', - '/^(?:(?!\\.)(?=.)[\\[])$/', - '/^(?:\\[)$/', - '/^(?:(?=.)\\[(?!\\.)(?=.)[^/]*?)$/', - '/^(?:(?!\\.)(?=.)[\\]])$/', - '/^(?:(?!\\.)(?=.)[\\]-])$/', - '/^(?:(?!\\.)(?=.)[a-z])$/', - '/^(?:(?!\\.)(?=.)[^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/])$/', - '/^(?:(?!\\.)(?=.)[^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?c)$/', - '/^(?:(?!\\.)(?=.)[^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?c[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/]*?[^/]*?[^/]*?[^/]*?)$/', - '/^(?:(?!\\.)(?=.)[^/]*?c[^/]*?[^/][^/]*?[^/]*?)$/', - '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?c[^/]*?[^/][^/]*?[^/]*?)$/', - '/^(?:(?=.)a[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/][^/][^/][^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?)$/', - '/^(?:\\[\\])$/', - '/^(?:\\[abc)$/', - '/^(?:(?=.)XYZ)$/i', - '/^(?:(?=.)ab[^/]*?)$/i', - '/^(?:(?!\\.)(?=.)[ia][^/][ck])$/i', - '/^(?:\\/(?!\\.)(?=.)[^/]*?|(?!\\.)(?=.)[^/]*?)$/', - '/^(?:\\/(?!\\.)(?=.)[^/]|(?!\\.)(?=.)[^/]*?)$/', - '/^(?:(?:(?!(?:\\/|^)\\.).)*?)$/', - '/^(?:a\\/(?!(?:^|\\/)\\.{1,2}(?:$|\\/))(?=.)[^/]*?\\/b)$/', - '/^(?:a\\/(?=.)\\.[^/]*?\\/b)$/', - '/^(?:a\\/(?!\\.)(?=.)[^/]*?\\/b)$/', - '/^(?:a\\/(?=.)\\.[^/]*?\\/b)$/', - '/^(?:(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?)$/', - '/^(?:(?!\\.)(?=.)[^/]*?\\(a\\/b\\))$/', - '/^(?:(?!\\.)(?=.)(?:a|b)*|(?!\\.)(?=.)(?:a|c)*)$/', - '/^(?:(?=.)\\[(?=.)\\!a[^/]*?)$/', - '/^(?:(?=.)\\[(?=.)#a[^/]*?)$/', - '/^(?:(?=.)\\+\\(a\\|[^/]*?\\|c\\\\\\\\\\|d\\\\\\\\\\|e\\\\\\\\\\\\\\\\\\|f\\\\\\\\\\\\\\\\\\|g)$/', - '/^(?:(?!\\.)(?=.)(?:a|b)*|(?!\\.)(?=.)(?:a|c)*)$/', - '/^(?:a|(?!\\.)(?=.)[^/]*?\\(b\\|c|d\\))$/', - '/^(?:a|(?!\\.)(?=.)(?:b|c)*|(?!\\.)(?=.)(?:b|d)*)$/', - '/^(?:(?!\\.)(?=.)(?:a|b|c)*|(?!\\.)(?=.)(?:a|c)*)$/', - '/^(?:(?!\\.)(?=.)[^/]*?\\(a\\|b\\|c\\)|(?!\\.)(?=.)[^/]*?\\(a\\|c\\))$/', - '/^(?:(?=.)a[^/]b)$/', - '/^(?:(?=.)#[^/]*?)$/', - '/^(?!^(?:(?=.)a[^/]*?)$).*$/', - '/^(?:(?=.)\\!a[^/]*?)$/', - '/^(?:(?=.)a[^/]*?)$/', - '/^(?!^(?:(?=.)\\!a[^/]*?)$).*$/', - '/^(?:(?!\\.)(?=.)[^/]*?\\.(?:(?!js)[^/]*?))$/', - '/^(?:(?:(?!(?:\\/|^)\\.).)*?\\/\\.x\\/(?:(?!(?:\\/|^)\\.).)*?)$/' ] -var re = 0; - -tap.test("basic tests", function (t) { - var start = Date.now() - - // [ pattern, [matches], MM opts, files, TAP opts] - patterns.forEach(function (c) { - if (typeof c === "function") return c() - if (typeof c === "string") return t.comment(c) - - var pattern = c[0] - , expect = c[1].sort(alpha) - , options = c[2] || {} - , f = c[3] || files - , tapOpts = c[4] || {} - - // options.debug = true - var m = new mm.Minimatch(pattern, options) - var r = m.makeRe() - var expectRe = regexps[re++] - tapOpts.re = String(r) || JSON.stringify(r) - tapOpts.files = JSON.stringify(f) - tapOpts.pattern = pattern - tapOpts.set = m.set - tapOpts.negated = m.negate - - var actual = mm.match(f, pattern, options) - actual.sort(alpha) - - t.equivalent( actual, expect - , JSON.stringify(pattern) + " " + JSON.stringify(expect) - , tapOpts ) - - t.equal(tapOpts.re, expectRe, tapOpts) - }) - - t.comment("time=" + (Date.now() - start) + "ms") - t.end() -}) - -tap.test("global leak test", function (t) { - var globalAfter = Object.keys(global) - t.equivalent(globalAfter, globalBefore, "no new globals, please") - t.end() -}) - -function alpha (a, b) { - return a > b ? 1 : -1 -} diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/test/brace-expand.js b/tests/mocha/node_modules/glob/node_modules/minimatch/test/brace-expand.js deleted file mode 100644 index 7ee278a274..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/test/brace-expand.js +++ /dev/null @@ -1,33 +0,0 @@ -var tap = require("tap") - , minimatch = require("../") - -tap.test("brace expansion", function (t) { - // [ pattern, [expanded] ] - ; [ [ "a{b,c{d,e},{f,g}h}x{y,z}" - , [ "abxy" - , "abxz" - , "acdxy" - , "acdxz" - , "acexy" - , "acexz" - , "afhxy" - , "afhxz" - , "aghxy" - , "aghxz" ] ] - , [ "a{1..5}b" - , [ "a1b" - , "a2b" - , "a3b" - , "a4b" - , "a5b" ] ] - , [ "a{b}c", ["a{b}c"] ] - ].forEach(function (tc) { - var p = tc[0] - , expect = tc[1] - t.equivalent(minimatch.braceExpand(p), expect, p) - }) - console.error("ending") - t.end() -}) - - diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/test/caching.js b/tests/mocha/node_modules/glob/node_modules/minimatch/test/caching.js deleted file mode 100644 index 0fec4b0fad..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/test/caching.js +++ /dev/null @@ -1,14 +0,0 @@ -var Minimatch = require("../minimatch.js").Minimatch -var tap = require("tap") -tap.test("cache test", function (t) { - var mm1 = new Minimatch("a?b") - var mm2 = new Minimatch("a?b") - t.equal(mm1, mm2, "should get the same object") - // the lru should drop it after 100 entries - for (var i = 0; i < 100; i ++) { - new Minimatch("a"+i) - } - mm2 = new Minimatch("a?b") - t.notEqual(mm1, mm2, "cache should have dropped") - t.end() -}) diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/test/defaults.js b/tests/mocha/node_modules/glob/node_modules/minimatch/test/defaults.js deleted file mode 100644 index 25f1f601cd..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/test/defaults.js +++ /dev/null @@ -1,274 +0,0 @@ -// http://www.bashcookbook.com/bashinfo/source/bash-1.14.7/tests/glob-test -// -// TODO: Some of these tests do very bad things with backslashes, and will -// most likely fail badly on windows. They should probably be skipped. - -var tap = require("tap") - , globalBefore = Object.keys(global) - , mm = require("../") - , files = [ "a", "b", "c", "d", "abc" - , "abd", "abe", "bb", "bcd" - , "ca", "cb", "dd", "de" - , "bdir/", "bdir/cfile"] - , next = files.concat([ "a-b", "aXb" - , ".x", ".y" ]) - -tap.test("basic tests", function (t) { - var start = Date.now() - - // [ pattern, [matches], MM opts, files, TAP opts] - ; [ "http://www.bashcookbook.com/bashinfo" + - "/source/bash-1.14.7/tests/glob-test" - , ["a*", ["a", "abc", "abd", "abe"]] - , ["X*", ["X*"], {nonull: true}] - - // allow null glob expansion - , ["X*", []] - - // isaacs: Slightly different than bash/sh/ksh - // \\* is not un-escaped to literal "*" in a failed match, - // but it does make it get treated as a literal star - , ["\\*", ["\\*"], {nonull: true}] - , ["\\**", ["\\**"], {nonull: true}] - , ["\\*\\*", ["\\*\\*"], {nonull: true}] - - , ["b*/", ["bdir/"]] - , ["c*", ["c", "ca", "cb"]] - , ["**", files] - - , ["\\.\\./*/", ["\\.\\./*/"], {nonull: true}] - , ["s/\\..*//", ["s/\\..*//"], {nonull: true}] - - , "legendary larry crashes bashes" - , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/" - , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\\1/"], {nonull: true}] - , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/" - , ["/^root:/{s/^[^:]*:[^:]*:\([^:]*\).*$/\1/"], {nonull: true}] - - , "character classes" - , ["[a-c]b*", ["abc", "abd", "abe", "bb", "cb"]] - , ["[a-y]*[^c]", ["abd", "abe", "bb", "bcd", - "bdir/", "ca", "cb", "dd", "de"]] - , ["a*[^c]", ["abd", "abe"]] - , function () { files.push("a-b", "aXb") } - , ["a[X-]b", ["a-b", "aXb"]] - , function () { files.push(".x", ".y") } - , ["[^a-c]*", ["d", "dd", "de"]] - , function () { files.push("a*b/", "a*b/ooo") } - , ["a\\*b/*", ["a*b/ooo"]] - , ["a\\*?/*", ["a*b/ooo"]] - , ["*\\\\!*", [], {null: true}, ["echo !7"]] - , ["*\\!*", ["echo !7"], null, ["echo !7"]] - , ["*.\\*", ["r.*"], null, ["r.*"]] - , ["a[b]c", ["abc"]] - , ["a[\\b]c", ["abc"]] - , ["a?c", ["abc"]] - , ["a\\*c", [], {null: true}, ["abc"]] - , ["", [""], { null: true }, [""]] - - , "http://www.opensource.apple.com/source/bash/bash-23/" + - "bash/tests/glob-test" - , function () { files.push("man/", "man/man1/", "man/man1/bash.1") } - , ["*/man*/bash.*", ["man/man1/bash.1"]] - , ["man/man1/bash.1", ["man/man1/bash.1"]] - , ["a***c", ["abc"], null, ["abc"]] - , ["a*****?c", ["abc"], null, ["abc"]] - , ["?*****??", ["abc"], null, ["abc"]] - , ["*****??", ["abc"], null, ["abc"]] - , ["?*****?c", ["abc"], null, ["abc"]] - , ["?***?****c", ["abc"], null, ["abc"]] - , ["?***?****?", ["abc"], null, ["abc"]] - , ["?***?****", ["abc"], null, ["abc"]] - , ["*******c", ["abc"], null, ["abc"]] - , ["*******?", ["abc"], null, ["abc"]] - , ["a*cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]] - , ["a**?**cd**?**??k", ["abcdecdhjk"], null, ["abcdecdhjk"]] - , ["a**?**cd**?**??k***", ["abcdecdhjk"], null, ["abcdecdhjk"]] - , ["a**?**cd**?**??***k", ["abcdecdhjk"], null, ["abcdecdhjk"]] - , ["a**?**cd**?**??***k**", ["abcdecdhjk"], null, ["abcdecdhjk"]] - , ["a****c**?**??*****", ["abcdecdhjk"], null, ["abcdecdhjk"]] - , ["[-abc]", ["-"], null, ["-"]] - , ["[abc-]", ["-"], null, ["-"]] - , ["\\", ["\\"], null, ["\\"]] - , ["[\\\\]", ["\\"], null, ["\\"]] - , ["[[]", ["["], null, ["["]] - , ["[", ["["], null, ["["]] - , ["[*", ["[abc"], null, ["[abc"]] - , "a right bracket shall lose its special meaning and\n" + - "represent itself in a bracket expression if it occurs\n" + - "first in the list. -- POSIX.2 2.8.3.2" - , ["[]]", ["]"], null, ["]"]] - , ["[]-]", ["]"], null, ["]"]] - , ["[a-\z]", ["p"], null, ["p"]] - , ["??**********?****?", [], { null: true }, ["abc"]] - , ["??**********?****c", [], { null: true }, ["abc"]] - , ["?************c****?****", [], { null: true }, ["abc"]] - , ["*c*?**", [], { null: true }, ["abc"]] - , ["a*****c*?**", [], { null: true }, ["abc"]] - , ["a********???*******", [], { null: true }, ["abc"]] - , ["[]", [], { null: true }, ["a"]] - , ["[abc", [], { null: true }, ["["]] - - , "nocase tests" - , ["XYZ", ["xYz"], { nocase: true, null: true } - , ["xYz", "ABC", "IjK"]] - , ["ab*", ["ABC"], { nocase: true, null: true } - , ["xYz", "ABC", "IjK"]] - , ["[ia]?[ck]", ["ABC", "IjK"], { nocase: true, null: true } - , ["xYz", "ABC", "IjK"]] - - // [ pattern, [matches], MM opts, files, TAP opts] - , "onestar/twostar" - , ["{/*,*}", [], {null: true}, ["/asdf/asdf/asdf"]] - , ["{/?,*}", ["/a", "bb"], {null: true} - , ["/a", "/b/b", "/a/b/c", "bb"]] - - , "dots should not match unless requested" - , ["**", ["a/b"], {}, ["a/b", "a/.d", ".a/.d"]] - - // .. and . can only match patterns starting with ., - // even when options.dot is set. - , function () { - files = ["a/./b", "a/../b", "a/c/b", "a/.d/b"] - } - , ["a/*/b", ["a/c/b", "a/.d/b"], {dot: true}] - , ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: true}] - , ["a/*/b", ["a/c/b"], {dot:false}] - , ["a/.*/b", ["a/./b", "a/../b", "a/.d/b"], {dot: false}] - - - // this also tests that changing the options needs - // to change the cache key, even if the pattern is - // the same! - , ["**", ["a/b","a/.d",".a/.d"], { dot: true } - , [ ".a/.d", "a/.d", "a/b"]] - - , "paren sets cannot contain slashes" - , ["*(a/b)", ["*(a/b)"], {nonull: true}, ["a/b"]] - - // brace sets trump all else. - // - // invalid glob pattern. fails on bash4 and bsdglob. - // however, in this implementation, it's easier just - // to do the intuitive thing, and let brace-expansion - // actually come before parsing any extglob patterns, - // like the documentation seems to say. - // - // XXX: if anyone complains about this, either fix it - // or tell them to grow up and stop complaining. - // - // bash/bsdglob says this: - // , ["*(a|{b),c)}", ["*(a|{b),c)}"], {}, ["a", "ab", "ac", "ad"]] - // but we do this instead: - , ["*(a|{b),c)}", ["a", "ab", "ac"], {}, ["a", "ab", "ac", "ad"]] - - // test partial parsing in the presence of comment/negation chars - , ["[!a*", ["[!ab"], {}, ["[!ab", "[ab"]] - , ["[#a*", ["[#ab"], {}, ["[#ab", "[ab"]] - - // like: {a,b|c\\,d\\\|e} except it's unclosed, so it has to be escaped. - , ["+(a|*\\|c\\\\|d\\\\\\|e\\\\\\\\|f\\\\\\\\\\|g" - , ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g"] - , {} - , ["+(a|b\\|c\\\\|d\\\\|e\\\\\\\\|f\\\\\\\\|g", "a", "b\\c"]] - - - // crazy nested {,,} and *(||) tests. - , function () { - files = [ "a", "b", "c", "d" - , "ab", "ac", "ad" - , "bc", "cb" - , "bc,d", "c,db", "c,d" - , "d)", "(b|c", "*(b|c" - , "b|c", "b|cc", "cb|c" - , "x(a|b|c)", "x(a|c)" - , "(a|b|c)", "(a|c)"] - } - , ["*(a|{b,c})", ["a", "b", "c", "ab", "ac"]] - , ["{a,*(b|c,d)}", ["a","(b|c", "*(b|c", "d)"]] - // a - // *(b|c) - // *(b|d) - , ["{a,*(b|{c,d})}", ["a","b", "bc", "cb", "c", "d"]] - , ["*(a|{b|c,c})", ["a", "b", "c", "ab", "ac", "bc", "cb"]] - - - // test various flag settings. - , [ "*(a|{b|c,c})", ["x(a|b|c)", "x(a|c)", "(a|b|c)", "(a|c)"] - , { noext: true } ] - , ["a?b", ["x/y/acb", "acb/"], {matchBase: true} - , ["x/y/acb", "acb/", "acb/d/e", "x/y/acb/d"] ] - , ["#*", ["#a", "#b"], {nocomment: true}, ["#a", "#b", "c#d"]] - - - // begin channelling Boole and deMorgan... - , "negation tests" - , function () { - files = ["d", "e", "!ab", "!abc", "a!b", "\\!a"] - } - - // anything that is NOT a* matches. - , ["!a*", ["\\!a", "d", "e", "!ab", "!abc"]] - - // anything that IS !a* matches. - , ["!a*", ["!ab", "!abc"], {nonegate: true}] - - // anything that IS a* matches - , ["!!a*", ["a!b"]] - - // anything that is NOT !a* matches - , ["!\\!a*", ["a!b", "d", "e", "\\!a"]] - - // negation nestled within a pattern - , function () { - files = [ "foo.js" - , "foo.bar" - // can't match this one without negative lookbehind. - , "foo.js.js" - , "blar.js" - , "foo." - , "boo.js.boo" ] - } - , ["*.!(js)", ["foo.bar", "foo.", "boo.js.boo"] ] - - ].forEach(function (c) { - if (typeof c === "function") return c() - if (typeof c === "string") return t.comment(c) - - var pattern = c[0] - , expect = c[1].sort(alpha) - , options = c[2] || {} - , f = c[3] || files - , tapOpts = c[4] || {} - - // options.debug = true - var Class = mm.defaults(options).Minimatch - var m = new Class(pattern, {}) - var r = m.makeRe() - tapOpts.re = String(r) || JSON.stringify(r) - tapOpts.files = JSON.stringify(f) - tapOpts.pattern = pattern - tapOpts.set = m.set - tapOpts.negated = m.negate - - var actual = mm.match(f, pattern, options) - actual.sort(alpha) - - t.equivalent( actual, expect - , JSON.stringify(pattern) + " " + JSON.stringify(expect) - , tapOpts ) - }) - - t.comment("time=" + (Date.now() - start) + "ms") - t.end() -}) - -tap.test("global leak test", function (t) { - var globalAfter = Object.keys(global) - t.equivalent(globalAfter, globalBefore, "no new globals, please") - t.end() -}) - -function alpha (a, b) { - return a > b ? 1 : -1 -} diff --git a/tests/mocha/node_modules/glob/node_modules/minimatch/test/extglob-ending-with-state-char.js b/tests/mocha/node_modules/glob/node_modules/minimatch/test/extglob-ending-with-state-char.js deleted file mode 100644 index 6676e2629a..0000000000 --- a/tests/mocha/node_modules/glob/node_modules/minimatch/test/extglob-ending-with-state-char.js +++ /dev/null @@ -1,8 +0,0 @@ -var test = require('tap').test -var minimatch = require('../') - -test('extglob ending with statechar', function(t) { - t.notOk(minimatch('ax', 'a?(b*)')) - t.ok(minimatch('ax', '?(a*|b)')) - t.end() -}) diff --git a/tests/mocha/node_modules/glob/package.json b/tests/mocha/node_modules/glob/package.json deleted file mode 100644 index bfbbbcca06..0000000000 --- a/tests/mocha/node_modules/glob/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "author": { - "name": "Isaac Z. Schlueter", - "email": "i@izs.me", - "url": "http://blog.izs.me/" - }, - "name": "glob", - "description": "a little globber", - "version": "3.2.3", - "repository": { - "type": "git", - "url": "git://github.com/isaacs/node-glob.git" - }, - "main": "glob.js", - "engines": { - "node": "*" - }, - "dependencies": { - "minimatch": "~0.2.11", - "graceful-fs": "~2.0.0", - "inherits": "2" - }, - "devDependencies": { - "tap": "~0.4.0", - "mkdirp": "0", - "rimraf": "1" - }, - "scripts": { - "test": "tap test/*.js" - }, - "license": "BSD", - "readme": "# Glob\n\nMatch files using the patterns the shell uses, like stars and stuff.\n\nThis is a glob implementation in JavaScript. It uses the `minimatch`\nlibrary to do its matching.\n\n## Attention: node-glob users!\n\nThe API has changed dramatically between 2.x and 3.x. This library is\nnow 100% JavaScript, and the integer flags have been replaced with an\noptions object.\n\nAlso, there's an event emitter class, proper tests, and all the other\nthings you've come to expect from node modules.\n\nAnd best of all, no compilation!\n\n## Usage\n\n```javascript\nvar glob = require(\"glob\")\n\n// options is optional\nglob(\"**/*.js\", options, function (er, files) {\n // files is an array of filenames.\n // If the `nonull` option is set, and nothing\n // was found, then files is [\"**/*.js\"]\n // er is an error object or null.\n})\n```\n\n## Features\n\nPlease see the [minimatch\ndocumentation](https://github.com/isaacs/minimatch) for more details.\n\nSupports these glob features:\n\n* Brace Expansion\n* Extended glob matching\n* \"Globstar\" `**` matching\n\nSee:\n\n* `man sh`\n* `man bash`\n* `man 3 fnmatch`\n* `man 5 gitignore`\n* [minimatch documentation](https://github.com/isaacs/minimatch)\n\n## glob(pattern, [options], cb)\n\n* `pattern` {String} Pattern to be matched\n* `options` {Object}\n* `cb` {Function}\n * `err` {Error | null}\n * `matches` {Array} filenames found matching the pattern\n\nPerform an asynchronous glob search.\n\n## glob.sync(pattern, [options])\n\n* `pattern` {String} Pattern to be matched\n* `options` {Object}\n* return: {Array} filenames found matching the pattern\n\nPerform a synchronous glob search.\n\n## Class: glob.Glob\n\nCreate a Glob object by instanting the `glob.Glob` class.\n\n```javascript\nvar Glob = require(\"glob\").Glob\nvar mg = new Glob(pattern, options, cb)\n```\n\nIt's an EventEmitter, and starts walking the filesystem to find matches\nimmediately.\n\n### new glob.Glob(pattern, [options], [cb])\n\n* `pattern` {String} pattern to search for\n* `options` {Object}\n* `cb` {Function} Called when an error occurs, or matches are found\n * `err` {Error | null}\n * `matches` {Array} filenames found matching the pattern\n\nNote that if the `sync` flag is set in the options, then matches will\nbe immediately available on the `g.found` member.\n\n### Properties\n\n* `minimatch` The minimatch object that the glob uses.\n* `options` The options object passed in.\n* `error` The error encountered. When an error is encountered, the\n glob object is in an undefined state, and should be discarded.\n* `aborted` Boolean which is set to true when calling `abort()`. There\n is no way at this time to continue a glob search after aborting, but\n you can re-use the statCache to avoid having to duplicate syscalls.\n* `statCache` Collection of all the stat results the glob search\n performed.\n* `cache` Convenience object. Each field has the following possible\n values:\n * `false` - Path does not exist\n * `true` - Path exists\n * `1` - Path exists, and is not a directory\n * `2` - Path exists, and is a directory\n * `[file, entries, ...]` - Path exists, is a directory, and the\n array value is the results of `fs.readdir`\n\n### Events\n\n* `end` When the matching is finished, this is emitted with all the\n matches found. If the `nonull` option is set, and no match was found,\n then the `matches` list contains the original pattern. The matches\n are sorted, unless the `nosort` flag is set.\n* `match` Every time a match is found, this is emitted with the matched.\n* `error` Emitted when an unexpected error is encountered, or whenever\n any fs error occurs if `options.strict` is set.\n* `abort` When `abort()` is called, this event is raised.\n\n### Methods\n\n* `abort` Stop the search.\n\n### Options\n\nAll the options that can be passed to Minimatch can also be passed to\nGlob to change pattern matching behavior. Also, some have been added,\nor have glob-specific ramifications.\n\nAll options are false by default, unless otherwise noted.\n\nAll options are added to the glob object, as well.\n\n* `cwd` The current working directory in which to search. Defaults\n to `process.cwd()`.\n* `root` The place where patterns starting with `/` will be mounted\n onto. Defaults to `path.resolve(options.cwd, \"/\")` (`/` on Unix\n systems, and `C:\\` or some such on Windows.)\n* `dot` Include `.dot` files in normal matches and `globstar` matches.\n Note that an explicit dot in a portion of the pattern will always\n match dot files.\n* `nomount` By default, a pattern starting with a forward-slash will be\n \"mounted\" onto the root setting, so that a valid filesystem path is\n returned. Set this flag to disable that behavior.\n* `mark` Add a `/` character to directory matches. Note that this\n requires additional stat calls.\n* `nosort` Don't sort the results.\n* `stat` Set to true to stat *all* results. This reduces performance\n somewhat, and is completely unnecessary, unless `readdir` is presumed\n to be an untrustworthy indicator of file existence. It will cause\n ELOOP to be triggered one level sooner in the case of cyclical\n symbolic links.\n* `silent` When an unusual error is encountered\n when attempting to read a directory, a warning will be printed to\n stderr. Set the `silent` option to true to suppress these warnings.\n* `strict` When an unusual error is encountered\n when attempting to read a directory, the process will just continue on\n in search of other matches. Set the `strict` option to raise an error\n in these cases.\n* `cache` See `cache` property above. Pass in a previously generated\n cache object to save some fs calls.\n* `statCache` A cache of results of filesystem information, to prevent\n unnecessary stat calls. While it should not normally be necessary to\n set this, you may pass the statCache from one glob() call to the\n options object of another, if you know that the filesystem will not\n change between calls. (See \"Race Conditions\" below.)\n* `sync` Perform a synchronous glob search.\n* `nounique` In some cases, brace-expanded patterns can result in the\n same file showing up multiple times in the result set. By default,\n this implementation prevents duplicates in the result set.\n Set this flag to disable that behavior.\n* `nonull` Set to never return an empty set, instead returning a set\n containing the pattern itself. This is the default in glob(3).\n* `nocase` Perform a case-insensitive match. Note that case-insensitive\n filesystems will sometimes result in glob returning results that are\n case-insensitively matched anyway, since readdir and stat will not\n raise an error.\n* `debug` Set to enable debug logging in minimatch and glob.\n* `globDebug` Set to enable debug logging in glob, but not minimatch.\n\n## Comparisons to other fnmatch/glob implementations\n\nWhile strict compliance with the existing standards is a worthwhile\ngoal, some discrepancies exist between node-glob and other\nimplementations, and are intentional.\n\nIf the pattern starts with a `!` character, then it is negated. Set the\n`nonegate` flag to suppress this behavior, and treat leading `!`\ncharacters normally. This is perhaps relevant if you wish to start the\npattern with a negative extglob pattern like `!(a|B)`. Multiple `!`\ncharacters at the start of a pattern will negate the pattern multiple\ntimes.\n\nIf a pattern starts with `#`, then it is treated as a comment, and\nwill not match anything. Use `\\#` to match a literal `#` at the\nstart of a line, or set the `nocomment` flag to suppress this behavior.\n\nThe double-star character `**` is supported by default, unless the\n`noglobstar` flag is set. This is supported in the manner of bsdglob\nand bash 4.1, where `**` only has special significance if it is the only\nthing in a path part. That is, `a/**/b` will match `a/x/y/b`, but\n`a/**b` will not.\n\nIf an escaped pattern has no matches, and the `nonull` flag is set,\nthen glob returns the pattern as-provided, rather than\ninterpreting the character escapes. For example,\n`glob.match([], \"\\\\*a\\\\?\")` will return `\"\\\\*a\\\\?\"` rather than\n`\"*a?\"`. This is akin to setting the `nullglob` option in bash, except\nthat it does not resolve escaped pattern characters.\n\nIf brace expansion is not disabled, then it is performed before any\nother interpretation of the glob pattern. Thus, a pattern like\n`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded\n**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are\nchecked for validity. Since those two are valid, matching proceeds.\n\n## Windows\n\n**Please only use forward-slashes in glob expressions.**\n\nThough windows uses either `/` or `\\` as its path separator, only `/`\ncharacters are used by this glob implementation. You must use\nforward-slashes **only** in glob expressions. Back-slashes will always\nbe interpreted as escape characters, not path separators.\n\nResults from absolute patterns such as `/foo/*` are mounted onto the\nroot setting using `path.join`. On windows, this will by default result\nin `/foo/*` matching `C:\\foo\\bar.txt`.\n\n## Race Conditions\n\nGlob searching, by its very nature, is susceptible to race conditions,\nsince it relies on directory walking and such.\n\nAs a result, it is possible that a file that exists when glob looks for\nit may have been deleted or modified by the time it returns the result.\n\nAs part of its internal implementation, this program caches all stat\nand readdir calls that it makes, in order to cut down on system\noverhead. However, this also makes it even more susceptible to races,\nespecially if the cache or statCache objects are reused between glob\ncalls.\n\nUsers are thus advised not to use a glob result as a guarantee of\nfilesystem state in the face of rapid changes. For the vast majority\nof operations, this is never a problem.\n", - "readmeFilename": "README.md", - "bugs": { - "url": "https://github.com/isaacs/node-glob/issues" - }, - "homepage": "https://github.com/isaacs/node-glob", - "_id": "glob@3.2.3", - "_from": "glob@3.2.3" -} diff --git a/tests/mocha/node_modules/glob/test/00-setup.js b/tests/mocha/node_modules/glob/test/00-setup.js deleted file mode 100644 index 245afafda4..0000000000 --- a/tests/mocha/node_modules/glob/test/00-setup.js +++ /dev/null @@ -1,176 +0,0 @@ -// just a little pre-run script to set up the fixtures. -// zz-finish cleans it up - -var mkdirp = require("mkdirp") -var path = require("path") -var i = 0 -var tap = require("tap") -var fs = require("fs") -var rimraf = require("rimraf") - -var files = -[ "a/.abcdef/x/y/z/a" -, "a/abcdef/g/h" -, "a/abcfed/g/h" -, "a/b/c/d" -, "a/bc/e/f" -, "a/c/d/c/b" -, "a/cb/e/f" -] - -var symlinkTo = path.resolve(__dirname, "a/symlink/a/b/c") -var symlinkFrom = "../.." - -files = files.map(function (f) { - return path.resolve(__dirname, f) -}) - -tap.test("remove fixtures", function (t) { - rimraf(path.resolve(__dirname, "a"), function (er) { - t.ifError(er, "remove fixtures") - t.end() - }) -}) - -files.forEach(function (f) { - tap.test(f, function (t) { - var d = path.dirname(f) - mkdirp(d, 0755, function (er) { - if (er) { - t.fail(er) - return t.bailout() - } - fs.writeFile(f, "i like tests", function (er) { - t.ifError(er, "make file") - t.end() - }) - }) - }) -}) - -if (process.platform !== "win32") { - tap.test("symlinky", function (t) { - var d = path.dirname(symlinkTo) - console.error("mkdirp", d) - mkdirp(d, 0755, function (er) { - t.ifError(er) - fs.symlink(symlinkFrom, symlinkTo, "dir", function (er) { - t.ifError(er, "make symlink") - t.end() - }) - }) - }) -} - -;["foo","bar","baz","asdf","quux","qwer","rewq"].forEach(function (w) { - w = "/tmp/glob-test/" + w - tap.test("create " + w, function (t) { - mkdirp(w, function (er) { - if (er) - throw er - t.pass(w) - t.end() - }) - }) -}) - - -// generate the bash pattern test-fixtures if possible -if (process.platform === "win32" || !process.env.TEST_REGEN) { - console.error("Windows, or TEST_REGEN unset. Using cached fixtures.") - return -} - -var spawn = require("child_process").spawn; -var globs = - // put more patterns here. - // anything that would be directly in / should be in /tmp/glob-test - ["test/a/*/+(c|g)/./d" - ,"test/a/**/[cg]/../[cg]" - ,"test/a/{b,c,d,e,f}/**/g" - ,"test/a/b/**" - ,"test/**/g" - ,"test/a/abc{fed,def}/g/h" - ,"test/a/abc{fed/g,def}/**/" - ,"test/a/abc{fed/g,def}/**///**/" - ,"test/**/a/**/" - ,"test/+(a|b|c)/a{/,bc*}/**" - ,"test/*/*/*/f" - ,"test/**/f" - ,"test/a/symlink/a/b/c/a/b/c/a/b/c//a/b/c////a/b/c/**/b/c/**" - ,"{./*/*,/tmp/glob-test/*}" - ,"{/tmp/glob-test/*,*}" // evil owl face! how you taunt me! - ,"test/a/!(symlink)/**" - ] -var bashOutput = {} -var fs = require("fs") - -globs.forEach(function (pattern) { - tap.test("generate fixture " + pattern, function (t) { - var cmd = "shopt -s globstar && " + - "shopt -s extglob && " + - "shopt -s nullglob && " + - // "shopt >&2; " + - "eval \'for i in " + pattern + "; do echo $i; done\'" - var cp = spawn("bash", ["-c", cmd], { cwd: path.dirname(__dirname) }) - var out = [] - cp.stdout.on("data", function (c) { - out.push(c) - }) - cp.stderr.pipe(process.stderr) - cp.on("close", function (code) { - out = flatten(out) - if (!out) - out = [] - else - out = cleanResults(out.split(/\r*\n/)) - - bashOutput[pattern] = out - t.notOk(code, "bash test should finish nicely") - t.end() - }) - }) -}) - -tap.test("save fixtures", function (t) { - var fname = path.resolve(__dirname, "bash-results.json") - var data = JSON.stringify(bashOutput, null, 2) + "\n" - fs.writeFile(fname, data, function (er) { - t.ifError(er) - t.end() - }) -}) - -function cleanResults (m) { - // normalize discrepancies in ordering, duplication, - // and ending slashes. - return m.map(function (m) { - return m.replace(/\/+/g, "/").replace(/\/$/, "") - }).sort(alphasort).reduce(function (set, f) { - if (f !== set[set.length - 1]) set.push(f) - return set - }, []).sort(alphasort).map(function (f) { - // de-windows - return (process.platform !== 'win32') ? f - : f.replace(/^[a-zA-Z]:\\\\/, '/').replace(/\\/g, '/') - }) -} - -function flatten (chunks) { - var s = 0 - chunks.forEach(function (c) { s += c.length }) - var out = new Buffer(s) - s = 0 - chunks.forEach(function (c) { - c.copy(out, s) - s += c.length - }) - - return out.toString().trim() -} - -function alphasort (a, b) { - a = a.toLowerCase() - b = b.toLowerCase() - return a > b ? 1 : a < b ? -1 : 0 -} diff --git a/tests/mocha/node_modules/glob/test/bash-comparison.js b/tests/mocha/node_modules/glob/test/bash-comparison.js deleted file mode 100644 index 239ed1a9c3..0000000000 --- a/tests/mocha/node_modules/glob/test/bash-comparison.js +++ /dev/null @@ -1,63 +0,0 @@ -// basic test -// show that it does the same thing by default as the shell. -var tap = require("tap") -, child_process = require("child_process") -, bashResults = require("./bash-results.json") -, globs = Object.keys(bashResults) -, glob = require("../") -, path = require("path") - -// run from the root of the project -// this is usually where you're at anyway, but be sure. -process.chdir(path.resolve(__dirname, "..")) - -function alphasort (a, b) { - a = a.toLowerCase() - b = b.toLowerCase() - return a > b ? 1 : a < b ? -1 : 0 -} - -globs.forEach(function (pattern) { - var expect = bashResults[pattern] - // anything regarding the symlink thing will fail on windows, so just skip it - if (process.platform === "win32" && - expect.some(function (m) { - return /\/symlink\//.test(m) - })) - return - - tap.test(pattern, function (t) { - glob(pattern, function (er, matches) { - if (er) - throw er - - // sort and unmark, just to match the shell results - matches = cleanResults(matches) - - t.deepEqual(matches, expect, pattern) - t.end() - }) - }) - - tap.test(pattern + " sync", function (t) { - var matches = cleanResults(glob.sync(pattern)) - - t.deepEqual(matches, expect, "should match shell") - t.end() - }) -}) - -function cleanResults (m) { - // normalize discrepancies in ordering, duplication, - // and ending slashes. - return m.map(function (m) { - return m.replace(/\/+/g, "/").replace(/\/$/, "") - }).sort(alphasort).reduce(function (set, f) { - if (f !== set[set.length - 1]) set.push(f) - return set - }, []).sort(alphasort).map(function (f) { - // de-windows - return (process.platform !== 'win32') ? f - : f.replace(/^[a-zA-Z]:[\/\\]+/, '/').replace(/[\\\/]+/g, '/') - }) -} diff --git a/tests/mocha/node_modules/glob/test/bash-results.json b/tests/mocha/node_modules/glob/test/bash-results.json deleted file mode 100644 index a9bc347dea..0000000000 --- a/tests/mocha/node_modules/glob/test/bash-results.json +++ /dev/null @@ -1,350 +0,0 @@ -{ - "test/a/*/+(c|g)/./d": [ - "test/a/b/c/./d" - ], - "test/a/**/[cg]/../[cg]": [ - "test/a/abcdef/g/../g", - "test/a/abcfed/g/../g", - "test/a/b/c/../c", - "test/a/c/../c", - "test/a/c/d/c/../c", - "test/a/symlink/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/../c" - ], - "test/a/{b,c,d,e,f}/**/g": [], - "test/a/b/**": [ - "test/a/b", - "test/a/b/c", - "test/a/b/c/d" - ], - "test/**/g": [ - "test/a/abcdef/g", - "test/a/abcfed/g" - ], - "test/a/abc{fed,def}/g/h": [ - "test/a/abcdef/g/h", - "test/a/abcfed/g/h" - ], - "test/a/abc{fed/g,def}/**/": [ - "test/a/abcdef", - "test/a/abcdef/g", - "test/a/abcfed/g" - ], - "test/a/abc{fed/g,def}/**///**/": [ - "test/a/abcdef", - "test/a/abcdef/g", - "test/a/abcfed/g" - ], - "test/**/a/**/": [ - "test/a", - "test/a/abcdef", - "test/a/abcdef/g", - "test/a/abcfed", - "test/a/abcfed/g", - "test/a/b", - "test/a/b/c", - "test/a/bc", - "test/a/bc/e", - "test/a/c", - "test/a/c/d", - "test/a/c/d/c", - "test/a/cb", - "test/a/cb/e", - "test/a/symlink", - "test/a/symlink/a", - "test/a/symlink/a/b", - "test/a/symlink/a/b/c", - "test/a/symlink/a/b/c/a", - "test/a/symlink/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b" - ], - "test/+(a|b|c)/a{/,bc*}/**": [ - "test/a/abcdef", - "test/a/abcdef/g", - "test/a/abcdef/g/h", - "test/a/abcfed", - "test/a/abcfed/g", - "test/a/abcfed/g/h" - ], - "test/*/*/*/f": [ - "test/a/bc/e/f", - "test/a/cb/e/f" - ], - "test/**/f": [ - "test/a/bc/e/f", - "test/a/cb/e/f" - ], - "test/a/symlink/a/b/c/a/b/c/a/b/c//a/b/c////a/b/c/**/b/c/**": [ - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b", - "test/a/symlink/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c/a/b/c" - ], - "{./*/*,/tmp/glob-test/*}": [ - "./examples/g.js", - "./examples/usr-local.js", - "./node_modules/graceful-fs", - "./node_modules/inherits", - "./node_modules/minimatch", - "./node_modules/mkdirp", - "./node_modules/rimraf", - "./node_modules/tap", - "./test/00-setup.js", - "./test/a", - "./test/bash-comparison.js", - "./test/bash-results.json", - "./test/cwd-test.js", - "./test/globstar-match.js", - "./test/mark.js", - "./test/nocase-nomagic.js", - "./test/pause-resume.js", - "./test/root-nomount.js", - "./test/root.js", - "./test/stat.js", - "./test/zz-cleanup.js", - "/tmp/glob-test/asdf", - "/tmp/glob-test/bar", - "/tmp/glob-test/baz", - "/tmp/glob-test/foo", - "/tmp/glob-test/quux", - "/tmp/glob-test/qwer", - "/tmp/glob-test/rewq" - ], - "{/tmp/glob-test/*,*}": [ - "/tmp/glob-test/asdf", - "/tmp/glob-test/bar", - "/tmp/glob-test/baz", - "/tmp/glob-test/foo", - "/tmp/glob-test/quux", - "/tmp/glob-test/qwer", - "/tmp/glob-test/rewq", - "examples", - "glob.js", - "LICENSE", - "node_modules", - "package.json", - "README.md", - "test" - ], - "test/a/!(symlink)/**": [ - "test/a/abcdef", - "test/a/abcdef/g", - "test/a/abcdef/g/h", - "test/a/abcfed", - "test/a/abcfed/g", - "test/a/abcfed/g/h", - "test/a/b", - "test/a/b/c", - "test/a/b/c/d", - "test/a/bc", - "test/a/bc/e", - "test/a/bc/e/f", - "test/a/c", - "test/a/c/d", - "test/a/c/d/c", - "test/a/c/d/c/b", - "test/a/cb", - "test/a/cb/e", - "test/a/cb/e/f" - ] -} diff --git a/tests/mocha/node_modules/glob/test/cwd-test.js b/tests/mocha/node_modules/glob/test/cwd-test.js deleted file mode 100644 index 352c27efad..0000000000 --- a/tests/mocha/node_modules/glob/test/cwd-test.js +++ /dev/null @@ -1,55 +0,0 @@ -var tap = require("tap") - -var origCwd = process.cwd() -process.chdir(__dirname) - -tap.test("changing cwd and searching for **/d", function (t) { - var glob = require('../') - var path = require('path') - t.test('.', function (t) { - glob('**/d', function (er, matches) { - t.ifError(er) - t.like(matches, [ 'a/b/c/d', 'a/c/d' ]) - t.end() - }) - }) - - t.test('a', function (t) { - glob('**/d', {cwd:path.resolve('a')}, function (er, matches) { - t.ifError(er) - t.like(matches, [ 'b/c/d', 'c/d' ]) - t.end() - }) - }) - - t.test('a/b', function (t) { - glob('**/d', {cwd:path.resolve('a/b')}, function (er, matches) { - t.ifError(er) - t.like(matches, [ 'c/d' ]) - t.end() - }) - }) - - t.test('a/b/', function (t) { - glob('**/d', {cwd:path.resolve('a/b/')}, function (er, matches) { - t.ifError(er) - t.like(matches, [ 'c/d' ]) - t.end() - }) - }) - - t.test('.', function (t) { - glob('**/d', {cwd: process.cwd()}, function (er, matches) { - t.ifError(er) - t.like(matches, [ 'a/b/c/d', 'a/c/d' ]) - t.end() - }) - }) - - t.test('cd -', function (t) { - process.chdir(origCwd) - t.end() - }) - - t.end() -}) diff --git a/tests/mocha/node_modules/glob/test/globstar-match.js b/tests/mocha/node_modules/glob/test/globstar-match.js deleted file mode 100644 index 9b234fa2a8..0000000000 --- a/tests/mocha/node_modules/glob/test/globstar-match.js +++ /dev/null @@ -1,19 +0,0 @@ -var Glob = require("../glob.js").Glob -var test = require('tap').test - -test('globstar should not have dupe matches', function(t) { - var pattern = 'a/**/[gh]' - var g = new Glob(pattern, { cwd: __dirname }) - var matches = [] - g.on('match', function(m) { - console.error('match %j', m) - matches.push(m) - }) - g.on('end', function(set) { - console.error('set', set) - matches = matches.sort() - set = set.sort() - t.same(matches, set, 'should have same set of matches') - t.end() - }) -}) diff --git a/tests/mocha/node_modules/glob/test/mark.js b/tests/mocha/node_modules/glob/test/mark.js deleted file mode 100644 index ed68a335c3..0000000000 --- a/tests/mocha/node_modules/glob/test/mark.js +++ /dev/null @@ -1,74 +0,0 @@ -var test = require("tap").test -var glob = require('../') -process.chdir(__dirname) - -test("mark, no / on pattern", function (t) { - glob("a/*", {mark: true}, function (er, results) { - if (er) - throw er - var expect = [ 'a/abcdef/', - 'a/abcfed/', - 'a/b/', - 'a/bc/', - 'a/c/', - 'a/cb/' ] - - if (process.platform !== "win32") - expect.push('a/symlink/') - - t.same(results, expect) - t.end() - }) -}) - -test("mark=false, no / on pattern", function (t) { - glob("a/*", function (er, results) { - if (er) - throw er - var expect = [ 'a/abcdef', - 'a/abcfed', - 'a/b', - 'a/bc', - 'a/c', - 'a/cb' ] - - if (process.platform !== "win32") - expect.push('a/symlink') - t.same(results, expect) - t.end() - }) -}) - -test("mark=true, / on pattern", function (t) { - glob("a/*/", {mark: true}, function (er, results) { - if (er) - throw er - var expect = [ 'a/abcdef/', - 'a/abcfed/', - 'a/b/', - 'a/bc/', - 'a/c/', - 'a/cb/' ] - if (process.platform !== "win32") - expect.push('a/symlink/') - t.same(results, expect) - t.end() - }) -}) - -test("mark=false, / on pattern", function (t) { - glob("a/*/", function (er, results) { - if (er) - throw er - var expect = [ 'a/abcdef/', - 'a/abcfed/', - 'a/b/', - 'a/bc/', - 'a/c/', - 'a/cb/' ] - if (process.platform !== "win32") - expect.push('a/symlink/') - t.same(results, expect) - t.end() - }) -}) diff --git a/tests/mocha/node_modules/glob/test/nocase-nomagic.js b/tests/mocha/node_modules/glob/test/nocase-nomagic.js deleted file mode 100644 index d86297098d..0000000000 --- a/tests/mocha/node_modules/glob/test/nocase-nomagic.js +++ /dev/null @@ -1,113 +0,0 @@ -var fs = require('graceful-fs'); -var test = require('tap').test; -var glob = require('../'); - -test('mock fs', function(t) { - var stat = fs.stat - var statSync = fs.statSync - var readdir = fs.readdir - var readdirSync = fs.readdirSync - - function fakeStat(path) { - var ret - switch (path.toLowerCase()) { - case '/tmp': case '/tmp/': - ret = { isDirectory: function() { return true } } - break - case '/tmp/a': - ret = { isDirectory: function() { return false } } - break - } - return ret - } - - fs.stat = function(path, cb) { - var f = fakeStat(path); - if (f) { - process.nextTick(function() { - cb(null, f) - }) - } else { - stat.call(fs, path, cb) - } - } - - fs.statSync = function(path) { - return fakeStat(path) || statSync.call(fs, path) - } - - function fakeReaddir(path) { - var ret - switch (path.toLowerCase()) { - case '/tmp': case '/tmp/': - ret = [ 'a', 'A' ] - break - case '/': - ret = ['tmp', 'tMp', 'tMP', 'TMP'] - } - return ret - } - - fs.readdir = function(path, cb) { - var f = fakeReaddir(path) - if (f) - process.nextTick(function() { - cb(null, f) - }) - else - readdir.call(fs, path, cb) - } - - fs.readdirSync = function(path) { - return fakeReaddir(path) || readdirSync.call(fs, path) - } - - t.pass('mocked') - t.end() -}) - -test('nocase, nomagic', function(t) { - var n = 2 - var want = [ '/TMP/A', - '/TMP/a', - '/tMP/A', - '/tMP/a', - '/tMp/A', - '/tMp/a', - '/tmp/A', - '/tmp/a' ] - glob('/tmp/a', { nocase: true }, function(er, res) { - if (er) - throw er - t.same(res.sort(), want) - if (--n === 0) t.end() - }) - glob('/tmp/A', { nocase: true }, function(er, res) { - if (er) - throw er - t.same(res.sort(), want) - if (--n === 0) t.end() - }) -}) - -test('nocase, with some magic', function(t) { - t.plan(2) - var want = [ '/TMP/A', - '/TMP/a', - '/tMP/A', - '/tMP/a', - '/tMp/A', - '/tMp/a', - '/tmp/A', - '/tmp/a' ] - glob('/tmp/*', { nocase: true }, function(er, res) { - if (er) - throw er - t.same(res.sort(), want) - }) - glob('/tmp/*', { nocase: true }, function(er, res) { - if (er) - throw er - t.same(res.sort(), want) - }) -}) diff --git a/tests/mocha/node_modules/glob/test/pause-resume.js b/tests/mocha/node_modules/glob/test/pause-resume.js deleted file mode 100644 index e1ffbab1c5..0000000000 --- a/tests/mocha/node_modules/glob/test/pause-resume.js +++ /dev/null @@ -1,73 +0,0 @@ -// show that no match events happen while paused. -var tap = require("tap") -, child_process = require("child_process") -// just some gnarly pattern with lots of matches -, pattern = "test/a/!(symlink)/**" -, bashResults = require("./bash-results.json") -, patterns = Object.keys(bashResults) -, glob = require("../") -, Glob = glob.Glob -, path = require("path") - -// run from the root of the project -// this is usually where you're at anyway, but be sure. -process.chdir(path.resolve(__dirname, "..")) - -function alphasort (a, b) { - a = a.toLowerCase() - b = b.toLowerCase() - return a > b ? 1 : a < b ? -1 : 0 -} - -function cleanResults (m) { - // normalize discrepancies in ordering, duplication, - // and ending slashes. - return m.map(function (m) { - return m.replace(/\/+/g, "/").replace(/\/$/, "") - }).sort(alphasort).reduce(function (set, f) { - if (f !== set[set.length - 1]) set.push(f) - return set - }, []).sort(alphasort).map(function (f) { - // de-windows - return (process.platform !== 'win32') ? f - : f.replace(/^[a-zA-Z]:\\\\/, '/').replace(/\\/g, '/') - }) -} - -var globResults = [] -tap.test("use a Glob object, and pause/resume it", function (t) { - var g = new Glob(pattern) - , paused = false - , res = [] - , expect = bashResults[pattern] - - g.on("pause", function () { - console.error("pause") - }) - - g.on("resume", function () { - console.error("resume") - }) - - g.on("match", function (m) { - t.notOk(g.paused, "must not be paused") - globResults.push(m) - g.pause() - t.ok(g.paused, "must be paused") - setTimeout(g.resume.bind(g), 10) - }) - - g.on("end", function (matches) { - t.pass("reached glob end") - globResults = cleanResults(globResults) - matches = cleanResults(matches) - t.deepEqual(matches, globResults, - "end event matches should be the same as match events") - - t.deepEqual(matches, expect, - "glob matches should be the same as bash results") - - t.end() - }) -}) - diff --git a/tests/mocha/node_modules/glob/test/root-nomount.js b/tests/mocha/node_modules/glob/test/root-nomount.js deleted file mode 100644 index 3ac5979b05..0000000000 --- a/tests/mocha/node_modules/glob/test/root-nomount.js +++ /dev/null @@ -1,39 +0,0 @@ -var tap = require("tap") - -var origCwd = process.cwd() -process.chdir(__dirname) - -tap.test("changing root and searching for /b*/**", function (t) { - var glob = require('../') - var path = require('path') - t.test('.', function (t) { - glob('/b*/**', { globDebug: true, root: '.', nomount: true }, function (er, matches) { - t.ifError(er) - t.like(matches, []) - t.end() - }) - }) - - t.test('a', function (t) { - glob('/b*/**', { globDebug: true, root: path.resolve('a'), nomount: true }, function (er, matches) { - t.ifError(er) - t.like(matches, [ '/b', '/b/c', '/b/c/d', '/bc', '/bc/e', '/bc/e/f' ]) - t.end() - }) - }) - - t.test('root=a, cwd=a/b', function (t) { - glob('/b*/**', { globDebug: true, root: 'a', cwd: path.resolve('a/b'), nomount: true }, function (er, matches) { - t.ifError(er) - t.like(matches, [ '/b', '/b/c', '/b/c/d', '/bc', '/bc/e', '/bc/e/f' ]) - t.end() - }) - }) - - t.test('cd -', function (t) { - process.chdir(origCwd) - t.end() - }) - - t.end() -}) diff --git a/tests/mocha/node_modules/glob/test/root.js b/tests/mocha/node_modules/glob/test/root.js deleted file mode 100644 index 95c23f99ca..0000000000 --- a/tests/mocha/node_modules/glob/test/root.js +++ /dev/null @@ -1,46 +0,0 @@ -var t = require("tap") - -var origCwd = process.cwd() -process.chdir(__dirname) - -var glob = require('../') -var path = require('path') - -t.test('.', function (t) { - glob('/b*/**', { globDebug: true, root: '.' }, function (er, matches) { - t.ifError(er) - t.like(matches, []) - t.end() - }) -}) - - -t.test('a', function (t) { - console.error("root=" + path.resolve('a')) - glob('/b*/**', { globDebug: true, root: path.resolve('a') }, function (er, matches) { - t.ifError(er) - var wanted = [ - '/b', '/b/c', '/b/c/d', '/bc', '/bc/e', '/bc/e/f' - ].map(function (m) { - return path.join(path.resolve('a'), m).replace(/\\/g, '/') - }) - - t.like(matches, wanted) - t.end() - }) -}) - -t.test('root=a, cwd=a/b', function (t) { - glob('/b*/**', { globDebug: true, root: 'a', cwd: path.resolve('a/b') }, function (er, matches) { - t.ifError(er) - t.like(matches, [ '/b', '/b/c', '/b/c/d', '/bc', '/bc/e', '/bc/e/f' ].map(function (m) { - return path.join(path.resolve('a'), m).replace(/\\/g, '/') - })) - t.end() - }) -}) - -t.test('cd -', function (t) { - process.chdir(origCwd) - t.end() -}) diff --git a/tests/mocha/node_modules/glob/test/stat.js b/tests/mocha/node_modules/glob/test/stat.js deleted file mode 100644 index 62917114b4..0000000000 --- a/tests/mocha/node_modules/glob/test/stat.js +++ /dev/null @@ -1,32 +0,0 @@ -var glob = require('../') -var test = require('tap').test -var path = require('path') - -test('stat all the things', function(t) { - var g = new glob.Glob('a/*abc*/**', { stat: true, cwd: __dirname }) - var matches = [] - g.on('match', function(m) { - matches.push(m) - }) - var stats = [] - g.on('stat', function(m) { - stats.push(m) - }) - g.on('end', function(eof) { - stats = stats.sort() - matches = matches.sort() - eof = eof.sort() - t.same(stats, matches) - t.same(eof, matches) - var cache = Object.keys(this.statCache) - t.same(cache.map(function (f) { - return path.relative(__dirname, f) - }).sort(), matches) - - cache.forEach(function(c) { - t.equal(typeof this.statCache[c], 'object') - }, this) - - t.end() - }) -}) diff --git a/tests/mocha/node_modules/glob/test/zz-cleanup.js b/tests/mocha/node_modules/glob/test/zz-cleanup.js deleted file mode 100644 index e085f0fa77..0000000000 --- a/tests/mocha/node_modules/glob/test/zz-cleanup.js +++ /dev/null @@ -1,11 +0,0 @@ -// remove the fixtures -var tap = require("tap") -, rimraf = require("rimraf") -, path = require("path") - -tap.test("cleanup fixtures", function (t) { - rimraf(path.resolve(__dirname, "a"), function (er) { - t.ifError(er, "removed") - t.end() - }) -}) diff --git a/tests/mocha/node_modules/growl/History.md b/tests/mocha/node_modules/growl/History.md deleted file mode 100644 index a4b7b49f27..0000000000 --- a/tests/mocha/node_modules/growl/History.md +++ /dev/null @@ -1,63 +0,0 @@ - -1.7.0 / 2012-12-30 -================== - - * support transient notifications in Gnome - -1.6.1 / 2012-09-25 -================== - - * restore compatibility with node < 0.8 [fgnass] - -1.6.0 / 2012-09-06 -================== - - * add notification center support [drudge] - -1.5.1 / 2012-04-08 -================== - - * Merge pull request #16 from KyleAMathews/patch-1 - * Fixes #15 - -1.5.0 / 2012-02-08 -================== - - * Added windows support [perfusorius] - -1.4.1 / 2011-12-28 -================== - - * Fixed: dont exit(). Closes #9 - -1.4.0 / 2011-12-17 -================== - - * Changed API: `growl.notify()` -> `growl()` - -1.3.0 / 2011-12-17 -================== - - * Added support for Ubuntu/Debian/Linux users [niftylettuce] - * Fixed: send notifications even if title not specified [alessioalex] - -1.2.0 / 2011-10-06 -================== - - * Add support for priority. - -1.1.0 / 2011-03-15 -================== - - * Added optional callbacks - * Added parsing of version - -1.0.1 / 2010-03-26 -================== - - * Fixed; sys.exec -> child_process.exec to support latest node - -1.0.0 / 2010-03-19 -================== - - * Initial release diff --git a/tests/mocha/node_modules/growl/Readme.md b/tests/mocha/node_modules/growl/Readme.md deleted file mode 100644 index 48d717ccb3..0000000000 --- a/tests/mocha/node_modules/growl/Readme.md +++ /dev/null @@ -1,99 +0,0 @@ -# Growl for nodejs - -Growl support for Nodejs. This is essentially a port of my [Ruby Growl Library](http://github.com/visionmedia/growl). Ubuntu/Linux support added thanks to [@niftylettuce](http://github.com/niftylettuce). - -## Installation - -### Install - -### Mac OS X (Darwin): - - Install [growlnotify(1)](http://growl.info/extras.php#growlnotify). On OS X 10.8, Notification Center is supported using [terminal-notifier](https://github.com/alloy/terminal-notifier). To install: - - $ sudo gem install terminal-notifier - - Install [npm](http://npmjs.org/) and run: - - $ npm install growl - -### Ubuntu (Linux): - - Install `notify-send` through the [libnotify-bin](http://packages.ubuntu.com/libnotify-bin) package: - - $ sudo apt-get install libnotify-bin - - Install [npm](http://npmjs.org/) and run: - - $ npm install growl - -### Windows: - - Download and install [Growl for Windows](http://www.growlforwindows.com/gfw/default.aspx) - - Download [growlnotify](http://www.growlforwindows.com/gfw/help/growlnotify.aspx) - **IMPORTANT :** Unpack growlnotify to a folder that is present in your path! - - Install [npm](http://npmjs.org/) and run: - - $ npm install growl - -## Examples - -Callback functions are optional - - var growl = require('growl') - growl('You have mail!') - growl('5 new messages', { sticky: true }) - growl('5 new emails', { title: 'Email Client', image: 'Safari', sticky: true }) - growl('Message with title', { title: 'Title'}) - growl('Set priority', { priority: 2 }) - growl('Show Safari icon', { image: 'Safari' }) - growl('Show icon', { image: 'path/to/icon.icns' }) - growl('Show image', { image: 'path/to/my.image.png' }) - growl('Show png filesystem icon', { image: 'png' }) - growl('Show pdf filesystem icon', { image: 'article.pdf' }) - growl('Show pdf filesystem icon', { image: 'article.pdf' }, function(err){ - // ... notified - }) - -## Options - - - title - - notification title - - name - - application name - - priority - - priority for the notification (default is 0) - - sticky - - weither or not the notification should remainin until closed - - image - - Auto-detects the context: - - path to an icon sets --iconpath - - path to an image sets --image - - capitalized word sets --appIcon - - filename uses extname as --icon - - otherwise treated as --icon - -## License - -(The MIT License) - -Copyright (c) 2009 TJ Holowaychuk - -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 copies 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 all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 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. diff --git a/tests/mocha/node_modules/growl/lib/growl.js b/tests/mocha/node_modules/growl/lib/growl.js deleted file mode 100644 index 04f4f9b487..0000000000 --- a/tests/mocha/node_modules/growl/lib/growl.js +++ /dev/null @@ -1,234 +0,0 @@ -// Growl - Copyright TJ Holowaychuk (MIT Licensed) - -/** - * Module dependencies. - */ - -var exec = require('child_process').exec - , fs = require('fs') - , path = require('path') - , exists = fs.existsSync || path.existsSync - , os = require('os') - , quote = JSON.stringify - , cmd; - -function which(name) { - var paths = process.env.PATH.split(':'); - var loc; - - for (var i = 0, len = paths.length; i < len; ++i) { - loc = path.join(paths[i], name); - if (exists(loc)) return loc; - } -} - -switch(os.type()) { - case 'Darwin': - if (which('terminal-notifier')) { - cmd = { - type: "Darwin-NotificationCenter" - , pkg: "terminal-notifier" - , msg: '-message' - , title: '-title' - , subtitle: '-subtitle' - , priority: { - cmd: '-execute' - , range: [] - } - }; - } else { - cmd = { - type: "Darwin-Growl" - , pkg: "growlnotify" - , msg: '-m' - , sticky: '--sticky' - , priority: { - cmd: '--priority' - , range: [ - -2 - , -1 - , 0 - , 1 - , 2 - , "Very Low" - , "Moderate" - , "Normal" - , "High" - , "Emergency" - ] - } - }; - } - break; - case 'Linux': - cmd = { - type: "Linux" - , pkg: "notify-send" - , msg: '' - , sticky: '-t 0' - , icon: '-i' - , priority: { - cmd: '-u' - , range: [ - "low" - , "normal" - , "critical" - ] - } - }; - break; - case 'Windows_NT': - cmd = { - type: "Windows" - , pkg: "growlnotify" - , msg: '' - , sticky: '/s:true' - , title: '/t:' - , icon: '/i:' - , priority: { - cmd: '/p:' - , range: [ - -2 - , -1 - , 0 - , 1 - , 2 - ] - } - }; - break; -} - -/** - * Expose `growl`. - */ - -exports = module.exports = growl; - -/** - * Node-growl version. - */ - -exports.version = '1.4.1' - -/** - * Send growl notification _msg_ with _options_. - * - * Options: - * - * - title Notification title - * - sticky Make the notification stick (defaults to false) - * - priority Specify an int or named key (default is 0) - * - name Application name (defaults to growlnotify) - * - image - * - path to an icon sets --iconpath - * - path to an image sets --image - * - capitalized word sets --appIcon - * - filename uses extname as --icon - * - otherwise treated as --icon - * - * Examples: - * - * growl('New email') - * growl('5 new emails', { title: 'Thunderbird' }) - * growl('Email sent', function(){ - * // ... notification sent - * }) - * - * @param {string} msg - * @param {object} options - * @param {function} fn - * @api public - */ - -function growl(msg, options, fn) { - var image - , args - , options = options || {} - , fn = fn || function(){}; - - // noop - if (!cmd) return fn(new Error('growl not supported on this platform')); - args = [cmd.pkg]; - - // image - if (image = options.image) { - switch(cmd.type) { - case 'Darwin-Growl': - var flag, ext = path.extname(image).substr(1) - flag = flag || ext == 'icns' && 'iconpath' - flag = flag || /^[A-Z]/.test(image) && 'appIcon' - flag = flag || /^png|gif|jpe?g$/.test(ext) && 'image' - flag = flag || ext && (image = ext) && 'icon' - flag = flag || 'icon' - args.push('--' + flag, image) - break; - case 'Linux': - args.push(cmd.icon + " " + image); - // libnotify defaults to sticky, set a hint for transient notifications - if (!options.sticky) args.push('--hint=int:transient:1'); - break; - case 'Windows': - args.push(cmd.icon + quote(image)); - break; - } - } - - // sticky - if (options.sticky) args.push(cmd.sticky); - - // priority - if (options.priority) { - var priority = options.priority + ''; - var checkindexOf = cmd.priority.range.indexOf(priority); - if (~cmd.priority.range.indexOf(priority)) { - args.push(cmd.priority, options.priority); - } - } - - // name - if (options.name && cmd.type === "Darwin-Growl") { - args.push('--name', options.name); - } - - switch(cmd.type) { - case 'Darwin-Growl': - args.push(cmd.msg); - args.push(quote(msg)); - if (options.title) args.push(quote(options.title)); - break; - case 'Darwin-NotificationCenter': - args.push(cmd.msg); - args.push(quote(msg)); - if (options.title) { - args.push(cmd.title); - args.push(quote(options.title)); - } - if (options.subtitle) { - args.push(cmd.subtitle); - args.push(quote(options.title)); - } - break; - case 'Darwin-Growl': - args.push(cmd.msg); - args.push(quote(msg)); - if (options.title) args.push(quote(options.title)); - break; - case 'Linux': - if (options.title) { - args.push(quote(options.title)); - args.push(cmd.msg); - args.push(quote(msg)); - } else { - args.push(quote(msg)); - } - break; - case 'Windows': - args.push(quote(msg)); - if (options.title) args.push(cmd.title + quote(options.title)); - break; - } - - // execute - exec(args.join(' '), fn); -}; diff --git a/tests/mocha/node_modules/growl/package.json b/tests/mocha/node_modules/growl/package.json deleted file mode 100644 index 6565e46de6..0000000000 --- a/tests/mocha/node_modules/growl/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "growl", - "version": "1.7.0", - "description": "Growl unobtrusive notifications", - "author": { - "name": "TJ Holowaychuk", - "email": "tj@vision-media.ca" - }, - "main": "./lib/growl.js", - "readme": "# Growl for nodejs\n\nGrowl support for Nodejs. This is essentially a port of my [Ruby Growl Library](http://github.com/visionmedia/growl). Ubuntu/Linux support added thanks to [@niftylettuce](http://github.com/niftylettuce). \n\n## Installation\n\n### Install \n\n### Mac OS X (Darwin):\n\n Install [growlnotify(1)](http://growl.info/extras.php#growlnotify). On OS X 10.8, Notification Center is supported using [terminal-notifier](https://github.com/alloy/terminal-notifier). To install:\n \n $ sudo gem install terminal-notifier\n \n Install [npm](http://npmjs.org/) and run:\n \n $ npm install growl\n\n### Ubuntu (Linux):\n\n Install `notify-send` through the [libnotify-bin](http://packages.ubuntu.com/libnotify-bin) package:\n\n $ sudo apt-get install libnotify-bin\n\n Install [npm](http://npmjs.org/) and run:\n \n $ npm install growl\n\n### Windows:\n\n Download and install [Growl for Windows](http://www.growlforwindows.com/gfw/default.aspx)\n\n Download [growlnotify](http://www.growlforwindows.com/gfw/help/growlnotify.aspx) - **IMPORTANT :** Unpack growlnotify to a folder that is present in your path!\n\n Install [npm](http://npmjs.org/) and run:\n \n $ npm install growl\n\n## Examples\n\nCallback functions are optional\n\n var growl = require('growl')\n growl('You have mail!')\n growl('5 new messages', { sticky: true })\n growl('5 new emails', { title: 'Email Client', image: 'Safari', sticky: true })\n growl('Message with title', { title: 'Title'})\n growl('Set priority', { priority: 2 })\n growl('Show Safari icon', { image: 'Safari' })\n growl('Show icon', { image: 'path/to/icon.icns' })\n growl('Show image', { image: 'path/to/my.image.png' })\n growl('Show png filesystem icon', { image: 'png' })\n growl('Show pdf filesystem icon', { image: 'article.pdf' })\n growl('Show pdf filesystem icon', { image: 'article.pdf' }, function(err){\n // ... notified\n })\n\n## Options\n\n - title\n - notification title\n - name\n - application name\n - priority\n - priority for the notification (default is 0)\n - sticky\n - weither or not the notification should remainin until closed\n - image\n - Auto-detects the context:\n - path to an icon sets --iconpath\n - path to an image sets --image\n - capitalized word sets --appIcon\n - filename uses extname as --icon\n - otherwise treated as --icon\n \n## License \n\n(The MIT License)\n\nCopyright (c) 2009 TJ Holowaychuk \n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", - "readmeFilename": "Readme.md", - "_id": "growl@1.7.0", - "_from": "growl@1.7.x" -} diff --git a/tests/mocha/node_modules/growl/test.js b/tests/mocha/node_modules/growl/test.js deleted file mode 100644 index cf22d90b2c..0000000000 --- a/tests/mocha/node_modules/growl/test.js +++ /dev/null @@ -1,20 +0,0 @@ - -var growl = require('./lib/growl') - -growl('You have mail!') -growl('5 new messages', { sticky: true }) -growl('5 new emails', { title: 'Email Client', image: 'Safari', sticky: true }) -growl('Message with title', { title: 'Title'}) -growl('Set priority', { priority: 2 }) -growl('Show Safari icon', { image: 'Safari' }) -growl('Show icon', { image: 'path/to/icon.icns' }) -growl('Show image', { image: 'path/to/my.image.png' }) -growl('Show png filesystem icon', { image: 'png' }) -growl('Show pdf filesystem icon', { image: 'article.pdf' }) -growl('Show pdf filesystem icon', { image: 'article.pdf' }, function(){ - console.log('callback'); -}) -growl('Show pdf filesystem icon', { title: 'Use show()', image: 'article.pdf' }) -growl('here \' are \n some \\ characters that " need escaping', {}, function(error, stdout, stderr) { - if (error !== null) throw new Error('escaping failed:\n' + stdout + stderr); -}) diff --git a/tests/mocha/node_modules/mkdirp/.npmignore b/tests/mocha/node_modules/mkdirp/.npmignore deleted file mode 100644 index 9303c347ee..0000000000 --- a/tests/mocha/node_modules/mkdirp/.npmignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules/ -npm-debug.log \ No newline at end of file diff --git a/tests/mocha/node_modules/mkdirp/.travis.yml b/tests/mocha/node_modules/mkdirp/.travis.yml deleted file mode 100644 index 84fd7ca248..0000000000 --- a/tests/mocha/node_modules/mkdirp/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: node_js -node_js: - - 0.6 - - 0.8 - - 0.9 diff --git a/tests/mocha/node_modules/mkdirp/LICENSE b/tests/mocha/node_modules/mkdirp/LICENSE deleted file mode 100644 index 432d1aeb01..0000000000 --- a/tests/mocha/node_modules/mkdirp/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -Copyright 2010 James Halliday (mail@substack.net) - -This project is free software released under the MIT/X11 license: - -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 -copies 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 -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER 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. diff --git a/tests/mocha/node_modules/mkdirp/examples/pow.js b/tests/mocha/node_modules/mkdirp/examples/pow.js deleted file mode 100644 index e6924212e6..0000000000 --- a/tests/mocha/node_modules/mkdirp/examples/pow.js +++ /dev/null @@ -1,6 +0,0 @@ -var mkdirp = require('mkdirp'); - -mkdirp('/tmp/foo/bar/baz', function (err) { - if (err) console.error(err) - else console.log('pow!') -}); diff --git a/tests/mocha/node_modules/mkdirp/index.js b/tests/mocha/node_modules/mkdirp/index.js deleted file mode 100644 index fda6de8a2c..0000000000 --- a/tests/mocha/node_modules/mkdirp/index.js +++ /dev/null @@ -1,82 +0,0 @@ -var path = require('path'); -var fs = require('fs'); - -module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; - -function mkdirP (p, mode, f, made) { - if (typeof mode === 'function' || mode === undefined) { - f = mode; - mode = 0777 & (~process.umask()); - } - if (!made) made = null; - - var cb = f || function () {}; - if (typeof mode === 'string') mode = parseInt(mode, 8); - p = path.resolve(p); - - fs.mkdir(p, mode, function (er) { - if (!er) { - made = made || p; - return cb(null, made); - } - switch (er.code) { - case 'ENOENT': - mkdirP(path.dirname(p), mode, function (er, made) { - if (er) cb(er, made); - else mkdirP(p, mode, cb, made); - }); - break; - - // In the case of any other error, just see if there's a dir - // there already. If so, then hooray! If not, then something - // is borked. - default: - fs.stat(p, function (er2, stat) { - // if the stat fails, then that's super weird. - // let the original error be the failure reason. - if (er2 || !stat.isDirectory()) cb(er, made) - else cb(null, made); - }); - break; - } - }); -} - -mkdirP.sync = function sync (p, mode, made) { - if (mode === undefined) { - mode = 0777 & (~process.umask()); - } - if (!made) made = null; - - if (typeof mode === 'string') mode = parseInt(mode, 8); - p = path.resolve(p); - - try { - fs.mkdirSync(p, mode); - made = made || p; - } - catch (err0) { - switch (err0.code) { - case 'ENOENT' : - made = sync(path.dirname(p), mode, made); - sync(p, mode, made); - break; - - // In the case of any other error, just see if there's a dir - // there already. If so, then hooray! If not, then something - // is borked. - default: - var stat; - try { - stat = fs.statSync(p); - } - catch (err1) { - throw err0; - } - if (!stat.isDirectory()) throw err0; - break; - } - } - - return made; -}; diff --git a/tests/mocha/node_modules/mkdirp/package.json b/tests/mocha/node_modules/mkdirp/package.json deleted file mode 100644 index a71d8b77a9..0000000000 --- a/tests/mocha/node_modules/mkdirp/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "mkdirp", - "description": "Recursively mkdir, like `mkdir -p`", - "version": "0.3.5", - "author": { - "name": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net/" - }, - "main": "./index", - "keywords": [ - "mkdir", - "directory" - ], - "repository": { - "type": "git", - "url": "http://github.com/substack/node-mkdirp.git" - }, - "scripts": { - "test": "tap test/*.js" - }, - "devDependencies": { - "tap": "~0.4.0" - }, - "license": "MIT", - "readme": "# mkdirp\n\nLike `mkdir -p`, but in node.js!\n\n[![build status](https://secure.travis-ci.org/substack/node-mkdirp.png)](http://travis-ci.org/substack/node-mkdirp)\n\n# example\n\n## pow.js\n\n```js\nvar mkdirp = require('mkdirp');\n \nmkdirp('/tmp/foo/bar/baz', function (err) {\n if (err) console.error(err)\n else console.log('pow!')\n});\n```\n\nOutput\n\n```\npow!\n```\n\nAnd now /tmp/foo/bar/baz exists, huzzah!\n\n# methods\n\n```js\nvar mkdirp = require('mkdirp');\n```\n\n## mkdirp(dir, mode, cb)\n\nCreate a new directory and any necessary subdirectories at `dir` with octal\npermission string `mode`.\n\nIf `mode` isn't specified, it defaults to `0777 & (~process.umask())`.\n\n`cb(err, made)` fires with the error or the first directory `made`\nthat had to be created, if any.\n\n## mkdirp.sync(dir, mode)\n\nSynchronously create a new directory and any necessary subdirectories at `dir`\nwith octal permission string `mode`.\n\nIf `mode` isn't specified, it defaults to `0777 & (~process.umask())`.\n\nReturns the first directory that had to be created, if any.\n\n# install\n\nWith [npm](http://npmjs.org) do:\n\n```\nnpm install mkdirp\n```\n\n# license\n\nMIT\n", - "readmeFilename": "readme.markdown", - "bugs": { - "url": "https://github.com/substack/node-mkdirp/issues" - }, - "homepage": "https://github.com/substack/node-mkdirp", - "_id": "mkdirp@0.3.5", - "_from": "mkdirp@0.3.5" -} diff --git a/tests/mocha/node_modules/mkdirp/readme.markdown b/tests/mocha/node_modules/mkdirp/readme.markdown deleted file mode 100644 index 83b0216ab5..0000000000 --- a/tests/mocha/node_modules/mkdirp/readme.markdown +++ /dev/null @@ -1,63 +0,0 @@ -# mkdirp - -Like `mkdir -p`, but in node.js! - -[![build status](https://secure.travis-ci.org/substack/node-mkdirp.png)](http://travis-ci.org/substack/node-mkdirp) - -# example - -## pow.js - -```js -var mkdirp = require('mkdirp'); - -mkdirp('/tmp/foo/bar/baz', function (err) { - if (err) console.error(err) - else console.log('pow!') -}); -``` - -Output - -``` -pow! -``` - -And now /tmp/foo/bar/baz exists, huzzah! - -# methods - -```js -var mkdirp = require('mkdirp'); -``` - -## mkdirp(dir, mode, cb) - -Create a new directory and any necessary subdirectories at `dir` with octal -permission string `mode`. - -If `mode` isn't specified, it defaults to `0777 & (~process.umask())`. - -`cb(err, made)` fires with the error or the first directory `made` -that had to be created, if any. - -## mkdirp.sync(dir, mode) - -Synchronously create a new directory and any necessary subdirectories at `dir` -with octal permission string `mode`. - -If `mode` isn't specified, it defaults to `0777 & (~process.umask())`. - -Returns the first directory that had to be created, if any. - -# install - -With [npm](http://npmjs.org) do: - -``` -npm install mkdirp -``` - -# license - -MIT diff --git a/tests/mocha/node_modules/mkdirp/test/chmod.js b/tests/mocha/node_modules/mkdirp/test/chmod.js deleted file mode 100644 index 520dcb8e9b..0000000000 --- a/tests/mocha/node_modules/mkdirp/test/chmod.js +++ /dev/null @@ -1,38 +0,0 @@ -var mkdirp = require('../').mkdirp; -var path = require('path'); -var fs = require('fs'); -var test = require('tap').test; - -var ps = [ '', 'tmp' ]; - -for (var i = 0; i < 25; i++) { - var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - ps.push(dir); -} - -var file = ps.join('/'); - -test('chmod-pre', function (t) { - var mode = 0744 - mkdirp(file, mode, function (er) { - t.ifError(er, 'should not error'); - fs.stat(file, function (er, stat) { - t.ifError(er, 'should exist'); - t.ok(stat && stat.isDirectory(), 'should be directory'); - t.equal(stat && stat.mode & 0777, mode, 'should be 0744'); - t.end(); - }); - }); -}); - -test('chmod', function (t) { - var mode = 0755 - mkdirp(file, mode, function (er) { - t.ifError(er, 'should not error'); - fs.stat(file, function (er, stat) { - t.ifError(er, 'should exist'); - t.ok(stat && stat.isDirectory(), 'should be directory'); - t.end(); - }); - }); -}); diff --git a/tests/mocha/node_modules/mkdirp/test/clobber.js b/tests/mocha/node_modules/mkdirp/test/clobber.js deleted file mode 100644 index 0eb7099870..0000000000 --- a/tests/mocha/node_modules/mkdirp/test/clobber.js +++ /dev/null @@ -1,37 +0,0 @@ -var mkdirp = require('../').mkdirp; -var path = require('path'); -var fs = require('fs'); -var test = require('tap').test; - -var ps = [ '', 'tmp' ]; - -for (var i = 0; i < 25; i++) { - var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - ps.push(dir); -} - -var file = ps.join('/'); - -// a file in the way -var itw = ps.slice(0, 3).join('/'); - - -test('clobber-pre', function (t) { - console.error("about to write to "+itw) - fs.writeFileSync(itw, 'I AM IN THE WAY, THE TRUTH, AND THE LIGHT.'); - - fs.stat(itw, function (er, stat) { - t.ifError(er) - t.ok(stat && stat.isFile(), 'should be file') - t.end() - }) -}) - -test('clobber', function (t) { - t.plan(2); - mkdirp(file, 0755, function (err) { - t.ok(err); - t.equal(err.code, 'ENOTDIR'); - t.end(); - }); -}); diff --git a/tests/mocha/node_modules/mkdirp/test/mkdirp.js b/tests/mocha/node_modules/mkdirp/test/mkdirp.js deleted file mode 100644 index b07cd70c10..0000000000 --- a/tests/mocha/node_modules/mkdirp/test/mkdirp.js +++ /dev/null @@ -1,28 +0,0 @@ -var mkdirp = require('../'); -var path = require('path'); -var fs = require('fs'); -var test = require('tap').test; - -test('woo', function (t) { - t.plan(2); - var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - - var file = '/tmp/' + [x,y,z].join('/'); - - mkdirp(file, 0755, function (err) { - if (err) t.fail(err); - else path.exists(file, function (ex) { - if (!ex) t.fail('file not created') - else fs.stat(file, function (err, stat) { - if (err) t.fail(err) - else { - t.equal(stat.mode & 0777, 0755); - t.ok(stat.isDirectory(), 'target not a directory'); - t.end(); - } - }) - }) - }); -}); diff --git a/tests/mocha/node_modules/mkdirp/test/perm.js b/tests/mocha/node_modules/mkdirp/test/perm.js deleted file mode 100644 index 23a7abbd23..0000000000 --- a/tests/mocha/node_modules/mkdirp/test/perm.js +++ /dev/null @@ -1,32 +0,0 @@ -var mkdirp = require('../'); -var path = require('path'); -var fs = require('fs'); -var test = require('tap').test; - -test('async perm', function (t) { - t.plan(2); - var file = '/tmp/' + (Math.random() * (1<<30)).toString(16); - - mkdirp(file, 0755, function (err) { - if (err) t.fail(err); - else path.exists(file, function (ex) { - if (!ex) t.fail('file not created') - else fs.stat(file, function (err, stat) { - if (err) t.fail(err) - else { - t.equal(stat.mode & 0777, 0755); - t.ok(stat.isDirectory(), 'target not a directory'); - t.end(); - } - }) - }) - }); -}); - -test('async root perm', function (t) { - mkdirp('/tmp', 0755, function (err) { - if (err) t.fail(err); - t.end(); - }); - t.end(); -}); diff --git a/tests/mocha/node_modules/mkdirp/test/perm_sync.js b/tests/mocha/node_modules/mkdirp/test/perm_sync.js deleted file mode 100644 index f685f60906..0000000000 --- a/tests/mocha/node_modules/mkdirp/test/perm_sync.js +++ /dev/null @@ -1,39 +0,0 @@ -var mkdirp = require('../'); -var path = require('path'); -var fs = require('fs'); -var test = require('tap').test; - -test('sync perm', function (t) { - t.plan(2); - var file = '/tmp/' + (Math.random() * (1<<30)).toString(16) + '.json'; - - mkdirp.sync(file, 0755); - path.exists(file, function (ex) { - if (!ex) t.fail('file not created') - else fs.stat(file, function (err, stat) { - if (err) t.fail(err) - else { - t.equal(stat.mode & 0777, 0755); - t.ok(stat.isDirectory(), 'target not a directory'); - t.end(); - } - }) - }); -}); - -test('sync root perm', function (t) { - t.plan(1); - - var file = '/tmp'; - mkdirp.sync(file, 0755); - path.exists(file, function (ex) { - if (!ex) t.fail('file not created') - else fs.stat(file, function (err, stat) { - if (err) t.fail(err) - else { - t.ok(stat.isDirectory(), 'target not a directory'); - t.end(); - } - }) - }); -}); diff --git a/tests/mocha/node_modules/mkdirp/test/race.js b/tests/mocha/node_modules/mkdirp/test/race.js deleted file mode 100644 index 96a0447636..0000000000 --- a/tests/mocha/node_modules/mkdirp/test/race.js +++ /dev/null @@ -1,41 +0,0 @@ -var mkdirp = require('../').mkdirp; -var path = require('path'); -var fs = require('fs'); -var test = require('tap').test; - -test('race', function (t) { - t.plan(4); - var ps = [ '', 'tmp' ]; - - for (var i = 0; i < 25; i++) { - var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - ps.push(dir); - } - var file = ps.join('/'); - - var res = 2; - mk(file, function () { - if (--res === 0) t.end(); - }); - - mk(file, function () { - if (--res === 0) t.end(); - }); - - function mk (file, cb) { - mkdirp(file, 0755, function (err) { - if (err) t.fail(err); - else path.exists(file, function (ex) { - if (!ex) t.fail('file not created') - else fs.stat(file, function (err, stat) { - if (err) t.fail(err) - else { - t.equal(stat.mode & 0777, 0755); - t.ok(stat.isDirectory(), 'target not a directory'); - if (cb) cb(); - } - }) - }) - }); - } -}); diff --git a/tests/mocha/node_modules/mkdirp/test/rel.js b/tests/mocha/node_modules/mkdirp/test/rel.js deleted file mode 100644 index 79858243ab..0000000000 --- a/tests/mocha/node_modules/mkdirp/test/rel.js +++ /dev/null @@ -1,32 +0,0 @@ -var mkdirp = require('../'); -var path = require('path'); -var fs = require('fs'); -var test = require('tap').test; - -test('rel', function (t) { - t.plan(2); - var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - - var cwd = process.cwd(); - process.chdir('/tmp'); - - var file = [x,y,z].join('/'); - - mkdirp(file, 0755, function (err) { - if (err) t.fail(err); - else path.exists(file, function (ex) { - if (!ex) t.fail('file not created') - else fs.stat(file, function (err, stat) { - if (err) t.fail(err) - else { - process.chdir(cwd); - t.equal(stat.mode & 0777, 0755); - t.ok(stat.isDirectory(), 'target not a directory'); - t.end(); - } - }) - }) - }); -}); diff --git a/tests/mocha/node_modules/mkdirp/test/return.js b/tests/mocha/node_modules/mkdirp/test/return.js deleted file mode 100644 index bce68e5613..0000000000 --- a/tests/mocha/node_modules/mkdirp/test/return.js +++ /dev/null @@ -1,25 +0,0 @@ -var mkdirp = require('../'); -var path = require('path'); -var fs = require('fs'); -var test = require('tap').test; - -test('return value', function (t) { - t.plan(4); - var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - - var file = '/tmp/' + [x,y,z].join('/'); - - // should return the first dir created. - // By this point, it would be profoundly surprising if /tmp didn't - // already exist, since every other test makes things in there. - mkdirp(file, function (err, made) { - t.ifError(err); - t.equal(made, '/tmp/' + x); - mkdirp(file, function (err, made) { - t.ifError(err); - t.equal(made, null); - }); - }); -}); diff --git a/tests/mocha/node_modules/mkdirp/test/return_sync.js b/tests/mocha/node_modules/mkdirp/test/return_sync.js deleted file mode 100644 index 7c222d3558..0000000000 --- a/tests/mocha/node_modules/mkdirp/test/return_sync.js +++ /dev/null @@ -1,24 +0,0 @@ -var mkdirp = require('../'); -var path = require('path'); -var fs = require('fs'); -var test = require('tap').test; - -test('return value', function (t) { - t.plan(2); - var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - - var file = '/tmp/' + [x,y,z].join('/'); - - // should return the first dir created. - // By this point, it would be profoundly surprising if /tmp didn't - // already exist, since every other test makes things in there. - // Note that this will throw on failure, which will fail the test. - var made = mkdirp.sync(file); - t.equal(made, '/tmp/' + x); - - // making the same file again should have no effect. - made = mkdirp.sync(file); - t.equal(made, null); -}); diff --git a/tests/mocha/node_modules/mkdirp/test/root.js b/tests/mocha/node_modules/mkdirp/test/root.js deleted file mode 100644 index 97ad7a2f35..0000000000 --- a/tests/mocha/node_modules/mkdirp/test/root.js +++ /dev/null @@ -1,18 +0,0 @@ -var mkdirp = require('../'); -var path = require('path'); -var fs = require('fs'); -var test = require('tap').test; - -test('root', function (t) { - // '/' on unix, 'c:/' on windows. - var file = path.resolve('/'); - - mkdirp(file, 0755, function (err) { - if (err) throw err - fs.stat(file, function (er, stat) { - if (er) throw er - t.ok(stat.isDirectory(), 'target is a directory'); - t.end(); - }) - }); -}); diff --git a/tests/mocha/node_modules/mkdirp/test/sync.js b/tests/mocha/node_modules/mkdirp/test/sync.js deleted file mode 100644 index 7530cada84..0000000000 --- a/tests/mocha/node_modules/mkdirp/test/sync.js +++ /dev/null @@ -1,32 +0,0 @@ -var mkdirp = require('../'); -var path = require('path'); -var fs = require('fs'); -var test = require('tap').test; - -test('sync', function (t) { - t.plan(2); - var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - - var file = '/tmp/' + [x,y,z].join('/'); - - try { - mkdirp.sync(file, 0755); - } catch (err) { - t.fail(err); - return t.end(); - } - - path.exists(file, function (ex) { - if (!ex) t.fail('file not created') - else fs.stat(file, function (err, stat) { - if (err) t.fail(err) - else { - t.equal(stat.mode & 0777, 0755); - t.ok(stat.isDirectory(), 'target not a directory'); - t.end(); - } - }); - }); -}); diff --git a/tests/mocha/node_modules/mkdirp/test/umask.js b/tests/mocha/node_modules/mkdirp/test/umask.js deleted file mode 100644 index 64ccafe22b..0000000000 --- a/tests/mocha/node_modules/mkdirp/test/umask.js +++ /dev/null @@ -1,28 +0,0 @@ -var mkdirp = require('../'); -var path = require('path'); -var fs = require('fs'); -var test = require('tap').test; - -test('implicit mode from umask', function (t) { - t.plan(2); - var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - - var file = '/tmp/' + [x,y,z].join('/'); - - mkdirp(file, function (err) { - if (err) t.fail(err); - else path.exists(file, function (ex) { - if (!ex) t.fail('file not created') - else fs.stat(file, function (err, stat) { - if (err) t.fail(err) - else { - t.equal(stat.mode & 0777, 0777 & (~process.umask())); - t.ok(stat.isDirectory(), 'target not a directory'); - t.end(); - } - }) - }) - }); -}); diff --git a/tests/mocha/node_modules/mkdirp/test/umask_sync.js b/tests/mocha/node_modules/mkdirp/test/umask_sync.js deleted file mode 100644 index 35bd5cbbf4..0000000000 --- a/tests/mocha/node_modules/mkdirp/test/umask_sync.js +++ /dev/null @@ -1,32 +0,0 @@ -var mkdirp = require('../'); -var path = require('path'); -var fs = require('fs'); -var test = require('tap').test; - -test('umask sync modes', function (t) { - t.plan(2); - var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); - - var file = '/tmp/' + [x,y,z].join('/'); - - try { - mkdirp.sync(file); - } catch (err) { - t.fail(err); - return t.end(); - } - - path.exists(file, function (ex) { - if (!ex) t.fail('file not created') - else fs.stat(file, function (err, stat) { - if (err) t.fail(err) - else { - t.equal(stat.mode & 0777, (0777 & (~process.umask()))); - t.ok(stat.isDirectory(), 'target not a directory'); - t.end(); - } - }); - }); -}); diff --git a/tests/mocha/package.json b/tests/mocha/package.json deleted file mode 100644 index 4328f9178c..0000000000 --- a/tests/mocha/package.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "name": "mocha", - "version": "1.18.2", - "description": "simple, flexible, fun test framework", - "keywords": [ - "mocha", - "test", - "bdd", - "tdd", - "tap" - ], - "author": { - "name": "TJ Holowaychuk", - "email": "tj@vision-media.ca" - }, - "repository": { - "type": "git", - "url": "git://github.com/visionmedia/mocha.git" - }, - "main": "./index", - "bin": { - "mocha": "./bin/mocha", - "_mocha": "./bin/_mocha" - }, - "engines": { - "node": ">= 0.4.x" - }, - "scripts": { - "test": "make test-all" - }, - "dependencies": { - "commander": "2.0.0", - "growl": "1.7.x", - "jade": "0.26.3", - "diff": "1.0.7", - "debug": "*", - "mkdirp": "0.3.5", - "glob": "3.2.3" - }, - "devDependencies": { - "should": ">= 2.0.x", - "coffee-script": "1.2" - }, - "files": [ - "bin", - "images", - "lib", - "index.js", - "mocha.css", - "mocha.js" - ], - "readme": " [![Build Status](https://secure.travis-ci.org/visionmedia/mocha.png)](http://travis-ci.org/visionmedia/mocha)\n\n [![Mocha test framework](http://f.cl.ly/items/3l1k0n2A1U3M1I1L210p/Screen%20Shot%202012-02-24%20at%202.21.43%20PM.png)](http://visionmedia.github.io/mocha)\n\n Mocha is a simple, flexible, fun JavaScript test framework for node.js and the browser. For more information view the [documentation](http://visionmedia.github.io/mocha).\n\n## Contributors\n\n```\n\n project : mocha\n repo age : 2 years, 4 months ago\n commits : 1314\n active : 372 days\n files : 141\n authors :\n 582 TJ Holowaychuk 44.3%\n 389 Tj Holowaychuk 29.6%\n 46 Travis Jeffery 3.5%\n 31 Guillermo Rauch 2.4%\n 13 Attila Domokos 1.0%\n 10 John Firebaugh 0.8%\n 8 Jo Liss 0.6%\n 7 Nathan Rajlich 0.5%\n 6 Mike Pennisi 0.5%\n 6 James Carr 0.5%\n 6 Brendan Nee 0.5%\n 5 Aaron Heckmann 0.4%\n 5 Ryunosuke SATO 0.4%\n 4 hokaccha 0.3%\n 4 Joshua Krall 0.3%\n 4 Xavier Antoviaque 0.3%\n 3 Jesse Dailey 0.2%\n 3 Forbes Lindesay 0.2%\n 3 Sindre Sorhus 0.2%\n 3 Cory Thomas 0.2%\n 3 Fredrik Enestad 0.2%\n 3 Ben Lindsey 0.2%\n 3 Tyson Tate 0.2%\n 3 Mathieu Desvé 0.2%\n 3 Valentin Agachi 0.2%\n 3 Wil Moore III 0.2%\n 3 Merrick Christensen 0.2%\n 3 eiji.ienaga 0.2%\n 3 fool2fish 0.2%\n 3 Nathan Bowser 0.2%\n 3 Paul Miller 0.2%\n 2 Juzer Ali 0.2%\n 2 Pete Hawkins 0.2%\n 2 Jonas Westerlund 0.2%\n 2 Arian Stolwijk 0.2%\n 2 Quang Van 0.2%\n 2 Glen Mailer 0.2%\n 2 Justin DuJardin 0.2%\n 2 FARKAS Máté 0.2%\n 2 Raynos 0.2%\n 2 Michael Riley 0.2%\n 2 Michael Schoonmaker 0.2%\n 2 Domenic Denicola 0.2%\n 2 Simon Gaeremynck 0.2%\n 2 Konstantin Käfer 0.2%\n 2 domenic 0.2%\n 2 Paul Armstrong 0.2%\n 2 fcrisci 0.2%\n 2 Alexander Early 0.2%\n 2 Shawn Krisman 0.2%\n 2 Brian Beck 0.2%\n 2 Nathan Alderson 0.2%\n 2 David Henderson 0.2%\n 2 Timo Tijhof 0.2%\n 2 Ian Storm Taylor 0.2%\n 2 travis jeffery 0.2%\n 1 Matt Smith 0.1%\n 1 Matthew Shanley 0.1%\n 1 Nathan Black 0.1%\n 1 Phil Sung 0.1%\n 1 R56 0.1%\n 1 Refael Ackermann 0.1%\n 1 Richard Dingwall 0.1%\n 1 Romain Prieto 0.1%\n 1 Roman Neuhauser 0.1%\n 1 Roman Shtylman 0.1%\n 1 Russ Bradberry 0.1%\n 1 Russell Munson 0.1%\n 1 Rustem Mustafin 0.1%\n 1 Salehen Shovon Rahman 0.1%\n 1 Sasha Koss 0.1%\n 1 Seiya Konno 0.1%\n 1 Simon Goumaz 0.1%\n 1 Standa Opichal 0.1%\n 1 Stephen Mathieson 0.1%\n 1 Steve Mason 0.1%\n 1 Tapiwa Kelvin 0.1%\n 1 Teddy Zeenny 0.1%\n 1 Tim Ehat 0.1%\n 1 Vadim Nikitin 0.1%\n 1 Victor Costan 0.1%\n 1 Will Langstroth 0.1%\n 1 Yanis Wang 0.1%\n 1 Yuest Wang 0.1%\n 1 abrkn 0.1%\n 1 airportyh 0.1%\n 1 badunk 0.1%\n 1 fengmk2 0.1%\n 1 grasGendarme 0.1%\n 1 lodr 0.1%\n 1 tgautier@yahoo.com 0.1%\n 1 traleig1 0.1%\n 1 vlad 0.1%\n 1 yuitest 0.1%\n 1 Adam Crabtree 0.1%\n 1 Andreas Brekken 0.1%\n 1 Andreas Lind Petersen 0.1%\n 1 Andrew Nesbitt 0.1%\n 1 Andrey Popp 0.1%\n 1 Arnaud Brousseau 0.1%\n 1 Atsuya Takagi 0.1%\n 1 Austin Birch 0.1%\n 1 Bjørge Næss 0.1%\n 1 Brian Lalor 0.1%\n 1 Brian M. Carlson 0.1%\n 1 Brian Moore 0.1%\n 1 Bryan Donovan 0.1%\n 1 Casey Foster 0.1%\n 1 ChrisWren 0.1%\n 1 Corey Butler 0.1%\n 1 Daniel Stockman 0.1%\n 1 Dave McKenna 0.1%\n 1 Di Wu 0.1%\n 1 Dmitry Shirokov 0.1%\n 1 Fedor Indutny 0.1%\n 1 Florian Margaine 0.1%\n 1 Frederico Silva 0.1%\n 1 Fredrik Lindin 0.1%\n 1 Gareth Murphy 0.1%\n 1 Gavin Mogan 0.1%\n 1 Glen Huang 0.1%\n 1 Greg Perkins 0.1%\n 1 Harry Brundage 0.1%\n 1 Herman Junge 0.1%\n 1 Ian Young 0.1%\n 1 Ivan 0.1%\n 1 JP Bochi 0.1%\n 1 Jaakko Salonen 0.1%\n 1 Jakub Nešetřil 0.1%\n 1 James Bowes 0.1%\n 1 James Lal 0.1%\n 1 Jason Barry 0.1%\n 1 Javier Aranda 0.1%\n 1 Jeff Kunkle 0.1%\n 1 Jeremy Martin 0.1%\n 1 Jimmy Cuadra 0.1%\n 1 Jonathan Creamer 0.1%\n 1 Jussi Virtanen 0.1%\n 1 Katie Gengler 0.1%\n 1 Kazuhito Hokamura 0.1%\n 1 Kirill Korolyov 0.1%\n 1 Koen Punt 0.1%\n 1 Laszlo Bacsi 0.1%\n 1 Liam Newman 0.1%\n 1 László Bácsi 0.1%\n 1 Maciej Małecki 0.1%\n 1 Mal Graty 0.1%\n 1 Marc Kuo 0.1%\n 1 Matt Robenolt 0.1%\n```\n\n## Links\n\n - [Google Group](http://groups.google.com/group/mochajs)\n - [Wiki](https://github.com/visionmedia/mocha/wiki)\n - Mocha [Extensions and reporters](https://github.com/visionmedia/mocha/wiki)\n", - "readmeFilename": "Readme.md", - "bugs": { - "url": "https://github.com/visionmedia/mocha/issues" - }, - "homepage": "https://github.com/visionmedia/mocha", - "_id": "mocha@1.18.2", - "_from": "mocha@1.18.2" -} diff --git a/tests/package.json b/tests/package.json index dc6b5c490d..b148c1ed24 100644 --- a/tests/package.json +++ b/tests/package.json @@ -19,6 +19,7 @@ "ms": "0.3.0", "should": "*", "coffee-script": "1.2", + "mocha": "1.7.4", "nw_test_loop": "git+https://github.com/owenc4a4/nw_test_loop_without_handle.git", "bignum": "git+https://github.com/owenc4a4/bignum.git", "node-dtrace-provider": "git+https://github.com/chrisa/node-dtrace-provider.git", diff --git a/tests/server/cookie_server.js b/tests/server/cookie_server.js deleted file mode 100644 index 726f137b00..0000000000 --- a/tests/server/cookie_server.js +++ /dev/null @@ -1,71 +0,0 @@ -var http = require('http'); -var url = require('url'); - -var expires_second = 86400000; - -var id_list = []; - -var request_listener = function(req,res){ - var location = url.parse(req.url,true); - var id = undefined; - var lang = undefined; - var cookies = {}; - var expires = new Date(Date.now()+expires_second).toGMTString(); - var set_cookies = null; - - var cookie_header = req.headers['cookie']; - - if (!cookie_header || cookie_header.length === 0){ - id = Math .random(); - lang = cookies["lang"]||"balabala"; - } else { - var cookie_tokens = cookie_header.split("; "); - for (var i=0;i0){ - headers["Set-Cookie"] = set_cookies; - } - res.writeHead(200,headers); - var html = JSON.stringify(cookies); - res.end(html); -}; - -var server = http.createServer(request_listener); -server.listen(8125); -console.log("Cookie server is running!"); diff --git a/tests/server/server.js b/tests/server/server.js index f751864fe9..9b7d497b3d 100644 --- a/tests/server/server.js +++ b/tests/server/server.js @@ -85,6 +85,3 @@ for (var i = 0; i < ports.length; i++) { } exports.res_save = res_save; - -//import cookie server -require(__dirname+'/cookie_server.js'); diff --git a/tools/aws_uploader.py b/tools/aws_uploader.py index 872a332339..35b3b6fd26 100755 --- a/tools/aws_uploader.py +++ b/tools/aws_uploader.py @@ -21,6 +21,7 @@ parser.add_argument('-r','--revision', help='Commit revision',required=True) parser.add_argument('-n','--number', help='Build number', required=True) parser.add_argument('-t','--bucket', help='AWS bucket name', required=True) +parser.add_argument('-d','--dlpath', help='AWS bucket path', required=True) args = parser.parse_args() @@ -31,12 +32,15 @@ got_revision = args.revision build_number = args.number bucket_name = args.bucket +dlpath = args.dlpath date = datetime.date.today().strftime('%m-%d-%Y') # If the binaries location is not given, calculate it from script related dir. if dist_dir == None: dist_dir = os.path.join(os.path.dirname(__file__), - os.pardir, os.pardir, os.pardir, 'out', 'Release', 'dist') + os.pardir, os.pardir, os.pardir, 'out', 'Release') + +dist_dir = os.path.join(dist_dir, 'dist') if not os.path.isabs(dist_dir): dist_dir = os.path.join(os.getcwd(), dist_dir) @@ -47,8 +51,9 @@ dist_dir = os.path.normpath(dist_dir) # it's for S3, so always use '/' here -upload_path = ''.join(['/' + date, - '/' + builder_name + '-build-' + build_number + '-' + got_revision]) +#upload_path = ''.join(['/' + date, +# '/' + builder_name + '-build-' + build_number + '-' + got_revision]) +upload_path = '/' + dlpath; file_list = os.listdir(dist_dir) if len(file_list) == 0: @@ -78,7 +83,10 @@ def aws_upload(upload_path, file_list): print 'Uploading "' + f + '" ...' sys.stdout.flush() # use '/' for s3 - key = bucket.new_key(upload_path + '/' + f) + path_prefix = '' + if builder_name.startswith("win64") and (f == 'nw.lib' or f == 'nw.exp') : + path_prefix = 'x64' + key = bucket.new_key(upload_path + '/' + path_prefix + '/' + f) key.set_contents_from_filename(filename=os.path.join(dist_dir, f), cb=print_progress, num_cb=50, replace=True) for retry in range(3): diff --git a/tools/commit_id.py b/tools/commit_id.py new file mode 100644 index 0000000000..40aebce038 --- /dev/null +++ b/tools/commit_id.py @@ -0,0 +1,43 @@ +import subprocess as sp +import sys +import os + +# Usage: commit_id.py check (checks if git is present) +# Usage: commit_id.py gen (generates commit id) + +def grab_output(command, cwd): + return sp.Popen(command, stdout=sp.PIPE, shell=True, cwd=cwd).communicate()[0].strip() + +operation = sys.argv[1] +cwd = sys.argv[2] +repos = ['content/nw', '.', 'third_party/WebKit', 'v8', 'third_party/node', 'breakpad/src' ] + +if operation == 'check': + for repo in repos: + index_path = os.path.join(cwd, repo, '.git', 'index') + if not os.path.exists(index_path): + print("0") + sys.exit(0) + print("1") + sys.exit(0) + +output_file = sys.argv[3] +commit_id_size = 7 +final_hash = '' + +for repo in repos: + try: + repo_path = os.path.join(cwd, repo) + final_hash += grab_output('git rev-parse --short=%d HEAD' % commit_id_size, repo_path) + final_hash += '-' + except: + final_hash = 'invalid-hash' + break + +final_hash = final_hash.rstrip('-') +hfile = open(output_file, 'w') + +hfile.write('#define NW_COMMIT_HASH "%s"\n' % final_hash) +hfile.write('#define NW_COMMIT_HASH_SIZE %d\n' % len(final_hash)) + +hfile.close() diff --git a/tools/dump_mac_syms b/tools/dump_mac_syms index a0f3c25ef2..dd567e1f13 100755 --- a/tools/dump_mac_syms +++ b/tools/dump_mac_syms @@ -39,14 +39,14 @@ TOP="${SRCROOT}/.." #BRAND_SCRIPT="${TOP}/build/branding_value.sh" #SRC_APP_NAME=$("${BRAND_SCRIPT}" "${BUILD_BRANDING}" PRODUCT_FULLNAME) -SRC_APP_NAME=node-webkit +SRC_APP_NAME=nwjs #. "${TOP}/chrome/VERSION" BREAKPAD_DUMP_SYMS="${BUILT_PRODUCTS_DIR}/dump_syms" #FULL_VERSION="${MAJOR}.${MINOR}.${BUILD}.${PATCH}" FULL_VERSION=$1 -DSYM_TAR_PATH="${BUILT_PRODUCTS_DIR}/${SRC_APP_NAME}.breakpad.tar.bz2" +DSYM_TAR_PATH="${BUILT_PRODUCTS_DIR}/${SRC_APP_NAME}.breakpad.tar" # Starting with an already-dumped symbol file at ${original_sym_path}, # transforms the MODULE line (which must be the first line) from referring to @@ -158,5 +158,5 @@ done if [ ! -e "${DSYM_TAR_PATH}" ] ; then # Change directory so that absolute paths aren't included in the archive. (cd "${BUILT_PRODUCTS_DIR}" && - tar -jcf "${DSYM_TAR_PATH}" "${DSYMS[@]}") + tar -cf "${DSYM_TAR_PATH}" "${DSYMS[@]}") fi diff --git a/tools/make-nw-headers.py b/tools/make-nw-headers.py index d7c995ff5a..27fbde92da 100644 --- a/tools/make-nw-headers.py +++ b/tools/make-nw-headers.py @@ -2,18 +2,32 @@ import os import tarfile import sys +import getnwisrelease import getnwversion import shutil import distutils.core import re +def update_uvh(tmp_dir, header_files): + for file in header_files: + header_f = os.path.join(tmp_dir, 'node', 'src', file) + rfile = open(header_f, 'r') + old = rfile.read() + new = re.sub('third_party/node/deps/uv/include/uv.h', 'uv.h', old, 0) + wfile = open(header_f, 'w') + wfile.write(new) + wfile.close() + return + script_dir = os.path.dirname(__file__) nw_root = os.path.normpath(os.path.join(script_dir, os.pardir)) project_root = os.path.normpath(os.path.join(nw_root, os.pardir, os.pardir)) third_party_dir = os.path.normpath(os.path.join(project_root, 'third_party')) -tmp_dir = tmp_dir = os.path.normpath(os.path.join(nw_root, 'tmp')) +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 ''' @@ -21,7 +35,7 @@ ''' if '-t' in sys.argv: nw_version = sys.argv[sys.argv.index('-t') + 1] -tarname = 'node-v' + nw_version + '.tar.gz' +tarname = 'nw-headers-v' + nw_version + '.tar.gz' tarpath = os.path.join(tmp_dir, tarname) #make tmpdir @@ -71,21 +85,13 @@ if os.path.exists(os.path.join(tmp_dir, 'node', 'deps', 'npm', 'node_modules')): shutil.rmtree(os.path.join(tmp_dir, 'node', 'deps', 'npm', 'node_modules')) -rfile = open(os.path.join(tmp_dir, 'node', 'src', 'node.h'), 'r') -filer = rfile.read() -sub = re.sub('third_party/node/deps/uv/include/uv.h', 'uv.h', filer, 0) -rfile.close() -wfile = open(os.path.join(tmp_dir, 'node', 'src', 'node.h'), 'w') -wfile.write(sub) -wfile.close() +header_files = ['node.h', 'env.h', 'env-inl.h'] +update_uvh(tmp_dir, header_files) print 'copy file end' print 'Begin compress file' -tar = tarfile.open(tarpath, 'w:gz') -for dirpath, dirnames, filenames in os.walk(tmp_dir): - for name in filenames: - path = os.path.normpath(os.path.join(dirpath, name)) - tar.add(path, path.replace(tmp_dir + os.sep, '')) -tar.close() -print 'compress end' \ No newline at end of file +with tarfile.open(tarpath, 'w:gz') as tar: + tar.add(os.path.join(tmp_dir, 'node'), arcname='node') + +print 'compress end' diff --git a/tools/make-nw-headers.sh b/tools/make-nw-headers.sh index 48944b3100..8d2a86ab12 100644 --- a/tools/make-nw-headers.sh +++ b/tools/make-nw-headers.sh @@ -10,8 +10,10 @@ rm -rf $tmpdir/node/deps/v8/test rm -rf $tmpdir/node/deps/v8/out rm -rf $tmpdir/node/deps/npm/node_modules -cat $tmpdir/node/src/node.h | sed -e 's|third_party/node/deps/uv/include/uv.h|uv.h|' > tmp_node.h && mv tmp_node.h $tmpdir/node/src/node.h +for h in env.h env-inl.h node.h; do +cat $tmpdir/node/src/$h | sed -e 's|third_party/node/deps/uv/include/uv.h|uv.h|' > tmp_$h && mv tmp_$h $tmpdir/node/src/$h +done pushd tmp -tar czf ../nw-headers-v0.6.1.tar.gz node +tar czf ../nw-headers-v0.11.0-rc1.tar.gz node popd diff --git a/tools/package_binaries.py b/tools/package_binaries.py index 3035f6e587..cd1b8540cb 100755 --- a/tools/package_binaries.py +++ b/tools/package_binaries.py @@ -10,11 +10,15 @@ import tarfile import zipfile -steps = ['nw', 'chromedriver', 'symbol', 'others'] +from subprocess import call + +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) @@ -30,6 +34,11 @@ nw_ver = None # x.xx dist_dir = None # .../out/Release/dist +package_name = 'nwjs' + +if args.mode == 'mas': + package_name = 'nwjs-macappstore' + step = args.step skip = args.skip binaries_location = args.path @@ -68,9 +77,21 @@ print 'Unsupported arch: ' + _arch exit(-1) -if platform_name == 'win' or platform_name == 'osx': +if platform_name == 'win': arch = 'ia32' +if platform_name == 'osx': + # detect output arch + nw_bin = binaries_location + '/nwjs.app/Contents/MacOS/nwjs' + import subprocess + if 'i386' in subprocess.check_output(['file',nw_bin]): + arch = 'ia32' + else: # should be 'x86_64' + arch = 'x64' + +if args.arch != None: + arch = args.arch + nw_ver = getnwversion.nw_version if getnwisrelease.release == 0: nw_ver += getnwisrelease.postfix @@ -81,7 +102,7 @@ # target example: # { # 'input' : [ 'nw', 'nw.pak', ... ] -# 'output' : 'node-webkit-v0.9.2-linux-x64' +# 'output' : 'nwjs-v0.9.2-linux-x64' # 'compress' : 'tar.gz' # 'folder' : True # Optional. More than 2 files will be put into a seprate folder # # normally, if you want do to this for only 1 file, set this flag. @@ -90,7 +111,7 @@ def generate_target_nw(platform_name, arch, version): target = {} # Output target['output'] = ''.join([ - 'node-webkit-', + package_name, '-', 'v', version, '-', platform_name, '-', arch]) @@ -105,25 +126,29 @@ def generate_target_nw(platform_name, arch, version): 'credits.html', 'libffmpegsumo.so', 'nw.pak', - 'nwsnapshot', + 'nwjc', 'nw', 'icudtl.dat', + 'locales', ] elif platform_name == 'win': target['input'] = [ + 'd3dcompiler_47.dll', 'ffmpegsumo.dll', 'icudtl.dat', 'libEGL.dll', 'libGLESv2.dll', + 'pdf.dll', 'nw.exe', 'nw.pak', - 'nwsnapshot.exe', + 'locales', + 'nwjc.exe', 'credits.html', ] elif platform_name == 'osx': target['input'] = [ - 'node-webkit.app', - 'nwsnapshot', + 'nwjs.app', + 'nwjc', 'credits.html', ] else: @@ -132,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([ @@ -154,29 +182,76 @@ def generate_target_chromedriver(platform_name, arch, version): def generate_target_symbols(platform_name, arch, version): target = {} - target['output'] = ''.join(['node-webkit-symbol-', + target['output'] = ''.join([package_name, '-symbol-', 'v', version, '-', platform_name, '-', arch]) if platform_name == 'linux': target['compress'] = 'tar.gz' target['input'] = ['nw.breakpad.' + arch] + target['folder'] = True elif platform_name == 'win': - target['compress'] = 'zip' -# target['input'] = ['nw.exe.pdb'] - target['input'] = [] + target['compress'] = None + target['input'] = ['nw.sym.7z'] + target['output'] = ''.join([package_name, '-symbol-', + 'v', version, + '-', platform_name, + '-', arch, '.7z']) elif platform_name == 'osx': - target['compress'] = 'tar.gz' + target['compress'] = 'zip' target['input'] = [ -# 'node-webkit.app.dSYM', -# 'node-webkit Helper.app.dSYM', -# 'node-webkit Framework.framework.dSYM', -# 'ffmpegsumo.so.dSYM', + 'nwjs.breakpad.tar' ] + target['folder'] = True else: print 'Unsupported platform: ' + platform_name exit(-1) - target['folder'] = True + return target + +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 == 'osx': + target['input'] = [] + # here , call make-nw-headers.py to generate nw headers + 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: + 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): @@ -184,9 +259,9 @@ def generate_target_others(platform_name, arch, version): target['output'] = '' target['compress'] = None if platform_name == 'win': - target['input'] = ['nw.exp', 'nw.lib', 'nw.sym.7z'] - elif platform_name == 'osx' : - target['input'] = ['node-webkit.breakpad.tar.bz2'] + target['input'] = ['nw.exp', 'nw.lib'] + elif platform_name == 'linux' : + target['input'] = [] else: target['input'] = [] return target @@ -252,11 +327,12 @@ def make_packages(targets): if len(t['input']) == 0: continue if t['compress'] == None: - if t['output'] != '': - os.mkdir(dist_dir + t['output']) for f in t['input']: src = os.path.join(binaries_location, f) - dest = os.path.join(dist_dir + t['output'], f) + if t['output'] != '': + dest = os.path.join(dist_dir, t['output']) + else: + dest = os.path.join(dist_dir, f) print "Copying " + f shutil.copy(src, dest) elif (t.has_key('folder') and t['folder'] == True) or len(t['input']) > 1: @@ -284,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