diff --git a/.gitignore b/.gitignore index b8a221c9e..eb9aedb4e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store +.tags* /.idea/ /build/ /dist/ diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..444ce0b4c --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,24 @@ +# Contributor Code of Conduct + +As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. + +We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery +- Personal attacks +- Trolling or insulting/derogatory comments +- Public or private harassment +- Publishing other's private information, such as physical or electronic addresses, without explicit permission +- Other unethical or unprofessional conduct + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a project maintainer at [atom@github.com](mailto:atom@github.com). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident. + +This Code of Conduct is adapted from the Contributor Covenant, version 1.3.0, available from http://contributor-covenant.org/version/1/3/0/ diff --git a/CONTRIBUTING-ko.md b/CONTRIBUTING-ko.md new file mode 100644 index 000000000..52887c8d3 --- /dev/null +++ b/CONTRIBUTING-ko.md @@ -0,0 +1,80 @@ +# Electron에 기여하기 + +:+1::tada: 먼저, 이 프로젝트에 기여해주셔서 감사합니다! :tada::+1: + +이 프로젝트는 기여자 규약 [행동강령](CODE_OF_CONDUCT.md)을 준수합니다. 따라서 이 +프로젝트의 개발에 참여하려면 이 규약을 지켜야 합니다. 받아들일 수 없는 행위를 발견했을 +경우 atom@github.com로 보고 하십시오. + +다음 항목들은 Electron에 기여하는 가이드라인을 제시합니다. +참고로 이 항목들은 그저 가이드라인에 불과하며 규칙이 아닙니다. 따라서 스스로의 적절한 +판단에 따라 이 문서의 변경을 제안할 수 있으며 변경시 pull request를 넣으면 됩니다. + +## 이슈 제출 + +* [여기](https://github.com/atom/electron/issues/new)에서 새로운 이슈를 만들 수 +있습니다. 하지만 이슈를 작성하기 전에 아래의 항목들을 숙지하고 가능한한 이슈 보고에 +대해 최대한 많은 정보와 자세한 설명을 포함해야 합니다. 가능하다면 다음 항목을 포함해야 +합니다: + * 사용하고 있는 Electron의 버전 + * 현재 사용중인 운영체제 + * 가능하다면 무엇을 하려고 했고, 어떤 결과를 예측했으며, 어떤 것이 예측된대로 + 작동하지 않았는지에 대해 서술해야 합니다. +* 추가로 다음 사항을 준수하면 이슈를 해결하는데 큰 도움이 됩니다: + * 스크린샷 또는 GIF 애니메이션 이미지들 + * 터미널에 출력된 에러의 내용 또는 개발자 도구, 알림창에 뜬 내용 + * [Cursory search](https://github.com/atom/electron/issues?utf8=✓&q=is%3Aissue+)를 + 통해 이미 비슷한 내용의 이슈가 등록되어있는지 확인 + +## Pull Request 하기 + +* 가능한한 스크린샷과 GIF 애니메이션 이미지를 pull request에 추가 +* CoffeeScript, JavaScript, C++과 Python등 +[참조문서에 정의된 코딩스타일](/docs-translations/ko-KR/development/coding-style.md)을 +준수 +* [문서 스타일 가이드](/docs-translations/ko-KR/styleguide.md)에 따라 문서를 +[Markdown](https://daringfireball.net/projects/markdown) 형식으로 작성. +* 짧은, 현재 시제 커밋 메시지 사용. [커밋 메시지 스타일 가이드](#Git-커밋-메시지)를 +참고하세요 + +## 스타일 가이드 + +### 공통 코드 + +* 파일 마지막에 공백 라인(newline) 추가 +* 다음 순서에 맞춰서 require 코드 작성: + * Node 빌트인 모듈 (`path` 같은) + * Electron 모듈 (`ipc`, `app` 같은) + * 로컬 모듈 (상대 경로상에 있는) +* 다음 순서에 맞춰서 클래스 속성 지정: + * 클래스 메서드와 속성 (메서드는 `@`로 시작) + * 인스턴스 메서드와 속성 +* 플랫폼 종속적인 코드 자제: + * 파일 이름 결합시 `path.join()`을 사용. + * 임시 디렉터리가 필요할 땐 `/tmp` 대신 `os.tmpdir()`을 통해 접근. +* 명시적인 함수 종료가 필요할 땐 `return` 만 사용. + * `return null`, `return undefined`, `null`, 또는 `undefined` 사용 X + +### Git 커밋 메시지 + +* 현재 시제 사용 ("Added feature" 대신 "Add feature" 사용) +* 필수적 분위기(imperative mood) 사용 ("Moves cursor to..." 대신 "Move cursor to..." 사용) +* 첫 줄은 72자에 맞추거나 그 보다 적게 제한 +* 자유롭게 필요에 따라 이슈나 PR링크를 참조 +* 단순한 문서 변경일 경우 `[ci skip]`을 커밋 메시지에 추가 +* 커밋 메시지의 도입부에 의미있는 이모티콘 사용: + * :art: `:art:` 코드의 포맷이나 구조를 개선(추가)했을 때 + * :racehorse: `:racehorse:` 성능을 개선했을 때 + * :non-potable_water: `:non-potable_water:` 메모리 누수를 연결했을 때 + * :memo: `:memo:` 문서를 작성했을 때 + * :penguin: `:penguin:` Linux에 대한 패치를 했을 때 + * :apple: `:apple:` Mac OS에 대한 패치를 했을 때 + * :checkered_flag: `:checkered_flag:` Windows에 대한 패치를 했을 때 + * :bug: `:bug:` 버그를 고쳤을 때 + * :fire: `:fire:` 코드 또는 파일을 삭제했을 때 + * :green_heart: `:green_heart:` CI 빌드를 고쳤을 때 + * :white_check_mark: `:white_check_mark:` 테스트를 추가했을 때 + * :lock: `:lock:` 보안 문제를 해결했을 때 + * :arrow_up: `:arrow_up:` 종속성 라이브러리를 업데이트 했을 때 + * :arrow_down: `:arrow_down:` 종속성 라이브러리를 다운그레이드 했을 때 + * :shirt: `:shirt:` linter(코드 검사기)의 경고를 제거했을 때 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6ca3ea5d2..67c132320 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,8 +2,9 @@ :+1::tada: First off, thanks for taking the time to contribute! :tada::+1: -This project adheres to the [Contributor Covenant 1.2](http://contributor-covenant.org/version/1/2/0). -By participating, you are expected to uphold this code. Please report unacceptable behavior to atom@github.com. +This project adheres to the Contributor Covenant [code of conduct](CODE_OF_CONDUCT.md). +By participating, you are expected to uphold this code. Please report unacceptable +behavior to atom@github.com. The following is a set of guidelines for contributing to Electron. These are just guidelines, not rules, use your best judgment and feel free to @@ -57,6 +58,7 @@ possible with your report. If you can, please include: * Use the imperative mood ("Move cursor to..." not "Moves cursor to...") * Limit the first line to 72 characters or less * Reference issues and pull requests liberally +* When only changing documentation, include `[ci skip]` in the commit description * Consider starting the commit message with an applicable emoji: * :art: `:art:` when improving the format/structure of the code * :racehorse: `:racehorse:` when improving performance diff --git a/README-ko.md b/README-ko.md index 606d4e973..a2db99929 100644 --- a/README-ko.md +++ b/README-ko.md @@ -16,9 +16,9 @@ Cross-Platform 데스크톱 어플리케이션을 개발할 수 있도록 해주 Electron에 대한 중요한 알림을 받고 싶다면 Twitter에서 [@ElectronJS](https://twitter.com/electronjs)를 팔로우 하세요. -이 프로젝트는 [기여자 규약 1.2](http://contributor-covenant.org/version/1/2/0/)을 -준수합니다. 따라서 이 프로젝트의 개발에 참여하려면 이 계약을 지켜야 합니다. 받아들일 수 -없는 행위를 발견했을 경우 atom@github.com로 보고 하십시오. +이 프로젝트는 기여자 규약 [행동강령](CODE_OF_CONDUCT.md)을 준수합니다. 따라서 이 +프로젝트의 개발에 참여하려면 이 규약을 지켜야 합니다. 받아들일 수 없는 행위를 발견했을 +경우 atom@github.com로 보고 하십시오. ## 다운로드 @@ -49,12 +49,14 @@ API 레퍼런스가 있습니다. Electron을 빌드 하는 방법과 프로젝 ## 참조 문서 (번역) -- [브라질 포르투칼어](https://github.com/atom/electron/tree/master/docs-translations/pt-BR) +- [브라질 포르투갈어](https://github.com/atom/electron/tree/master/docs-translations/pt-BR) - [한국어](https://github.com/atom/electron/tree/master/docs-translations/ko-KR) - [일본어](https://github.com/atom/electron/tree/master/docs-translations/jp) - [스페인어](https://github.com/atom/electron/tree/master/docs-translations/es) - [중국어 간체](https://github.com/atom/electron/tree/master/docs-translations/zh-CN) - [중국어 번체](https://github.com/atom/electron/tree/master/docs-translations/zh-TW) +- [우크라이나어](https://github.com/atom/electron/tree/master/docs-translations/uk-UA) +- [러시아어](https://github.com/atom/electron/tree/master/docs-translations/ru-RU) ## 시작하기 @@ -68,6 +70,7 @@ API 레퍼런스가 있습니다. Electron을 빌드 하는 방법과 프로젝 - Atom 포럼의 [`electron`](http://discuss.atom.io/c/electron) 카테고리 - Freenode 채팅의 `#atom-shell` 채널 - Slack의 [`Atom`](http://atom-slack.herokuapp.com/) 채널 +- [`electron-br`](https://electron-br.slack.com) *(브라질 포르투갈어)* [awesome-electron](https://github.com/sindresorhus/awesome-electron) 프로젝트에 커뮤니티가 운영중인 유용한 예제 어플리케이션과 도구, 리소스가 있으니 한번 참고해 보시기 diff --git a/README.md b/README.md index beb96b4e5..edc8ebb6b 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ editor](https://github.com/atom/atom). Follow [@ElectronJS](https://twitter.com/electronjs) on Twitter for important announcements. -This project adheres to the [Contributor Covenant 1.2](http://contributor-covenant.org/version/1/2/0/). -By participating, you are expected to uphold this code. Please report -unacceptable behavior to atom@github.com. +This project adheres to the Contributor Covenant [code of conduct](CODE_OF_CONDUCT.md). +By participating, you are expected to uphold this code. Please report unacceptable +behavior to atom@github.com. ## Downloads @@ -52,6 +52,8 @@ contains documents describing how to build and contribute to Electron. - [Spanish](https://github.com/atom/electron/tree/master/docs-translations/es) - [Simplified Chinese](https://github.com/atom/electron/tree/master/docs-translations/zh-CN) - [Traditional Chinese](https://github.com/atom/electron/tree/master/docs-translations/zh-TW) +- [Ukrainian](https://github.com/atom/electron/tree/master/docs-translations/uk-UA) +- [Russian](https://github.com/atom/electron/tree/master/docs-translations/ru-RU) ## Quick Start @@ -66,6 +68,7 @@ locations: forums - `#atom-shell` channel on Freenode - [`Atom`](http://atom-slack.herokuapp.com/) channel on Slack +- [`electron-br`](https://electron-br.slack.com) *(Brazilian Portuguese)* Check out [awesome-electron](https://github.com/sindresorhus/awesome-electron) for a community maintained list of useful example apps, tools and resources. diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..0fa0c0d9b --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,23 @@ +# appveyor file +# http://www.appveyor.com/docs/appveyor-yml +version: "{build}" + +init: + - git config --global core.autocrlf input + +platform: + - x86 + - x64 + +install: + - cmd: SET PATH=C:\Program Files (x86)\MSBuild\12.0\bin\;%PATH% + - cmd: SET PATH=C:\python27;%PATH% + - cmd: python script/cibuild + +branches: + only: + - master + +# disable build and test pahses +build: off +test: off diff --git a/atom.gyp b/atom.gyp index 9d6674105..eb4302d74 100644 --- a/atom.gyp +++ b/atom.gyp @@ -4,7 +4,7 @@ 'product_name%': 'Electron', 'company_name%': 'GitHub, Inc', 'company_abbr%': 'github', - 'version%': '0.35.1', + 'version%': '0.36.2', }, 'includes': [ 'filenames.gypi', @@ -121,10 +121,6 @@ ], }], ], - }, { # OS=="mac" - 'dependencies': [ - 'make_locale_paks', - ], }], # OS!="mac" ['OS=="win"', { 'include_dirs': [ @@ -155,6 +151,7 @@ 'destination': '<(PRODUCT_DIR)', 'files': [ '<@(copied_libraries)', + '<(libchromiumcontent_dir)/locales', '<(libchromiumcontent_dir)/libEGL.dll', '<(libchromiumcontent_dir)/libGLESv2.dll', '<(libchromiumcontent_dir)/icudtl.dat', @@ -203,6 +200,7 @@ 'destination': '<(PRODUCT_DIR)', 'files': [ '<@(copied_libraries)', + '<(libchromiumcontent_dir)/locales', '<(libchromiumcontent_dir)/icudtl.dat', '<(libchromiumcontent_dir)/content_shell.pak', '<(libchromiumcontent_dir)/natives_blob.bin', @@ -235,6 +233,8 @@ # Defined in Chromium but not exposed in its gyp file. 'V8_USE_EXTERNAL_STARTUP_DATA', 'ENABLE_PLUGINS', + 'ENABLE_PEPPER_CDMS', + 'USE_PROPRIETARY_CODECS', ], 'sources': [ '<@(lib_sources)', @@ -256,6 +256,12 @@ 'vendor/node/deps/cares/include', # The `third_party/WebKit/Source/platform/weborigin/SchemeRegistry.h` is using `platform/PlatformExport.h`. '<(libchromiumcontent_src_dir)/third_party/WebKit/Source', + # The 'third_party/libyuv/include/libyuv/scale_argb.h' is using 'libyuv/basic_types.h'. + '<(libchromiumcontent_src_dir)/third_party/libyuv/include', + # The 'third_party/webrtc/modules/desktop_capture/desktop_frame.h' is using 'webrtc/base/scoped_ptr.h'. + '<(libchromiumcontent_src_dir)/third_party/', + '<(libchromiumcontent_src_dir)/components/cdm', + '<(libchromiumcontent_src_dir)/third_party/widevine', ], 'direct_dependent_settings': { 'include_dirs': [ @@ -282,6 +288,7 @@ '-lcomctl32.lib', '-lcomdlg32.lib', '-lwininet.lib', + '-lwinmm.lib', ], }, 'dependencies': [ @@ -493,6 +500,16 @@ 'Libraries', ], }, + { + 'postbuild_name': 'Copy locales', + 'action': [ + 'tools/mac/copy-locales.py', + '-d', + '<(libchromiumcontent_dir)/locales', + '${BUILT_PRODUCTS_DIR}/<(product_name) Framework.framework/Resources', + '<@(locales)', + ], + }, ], 'conditions': [ ['mas_build==0', { @@ -537,31 +554,6 @@ }, }, # target helper ], - }, { # OS=="mac" - 'targets': [ - { - 'target_name': 'make_locale_paks', - 'type': 'none', - 'actions': [ - { - 'action_name': 'Make Empty Paks', - 'inputs': [ - 'tools/make_locale_paks.py', - ], - 'outputs': [ - '<(PRODUCT_DIR)/locales' - ], - 'action': [ - 'python', - 'tools/make_locale_paks.py', - '<(PRODUCT_DIR)', - '<@(locales)', - ], - 'msvs_cygwin_shell': 0, - }, - ], - }, - ], }], # OS!="mac" ], } diff --git a/atom/app/atom_content_client.cc b/atom/app/atom_content_client.cc index 0931a1b55..25e9d0f19 100644 --- a/atom/app/atom_content_client.cc +++ b/atom/app/atom_content_client.cc @@ -11,12 +11,21 @@ #include "atom/common/chrome_version.h" #include "atom/common/options_switches.h" #include "base/command_line.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 "content/public/common/content_constants.h" #include "content/public/common/pepper_plugin_info.h" #include "content/public/common/user_agent.h" #include "ppapi/shared_impl/ppapi_permissions.h" +#include "third_party/widevine/cdm/stub/widevine_cdm_version.h" +#include "ui/base/l10n/l10n_util.h" +#include "url/url_constants.h" + +#if defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS) +#include "chrome/common/widevine_cdm_constants.h" +#endif namespace atom { @@ -31,8 +40,8 @@ content::PepperPluginInfo CreatePepperFlashInfo(const base::FilePath& path, plugin.path = path; plugin.permissions = ppapi::PERMISSION_ALL_BITS; - std::vector flash_version_numbers; - base::SplitString(version, '.', &flash_version_numbers); + std::vector flash_version_numbers = base::SplitString( + version, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); if (flash_version_numbers.size() < 1) flash_version_numbers.push_back("11"); // |SplitString()| puts in an empty string given an empty string. :( @@ -47,7 +56,7 @@ content::PepperPluginInfo CreatePepperFlashInfo(const base::FilePath& path, // E.g., "Shockwave Flash 10.2 r154": plugin.description = plugin.name + " " + flash_version_numbers[0] + "." + flash_version_numbers[1] + " r" + flash_version_numbers[2]; - plugin.version = JoinString(flash_version_numbers, '.'); + plugin.version = base::JoinString(flash_version_numbers, "."); content::WebPluginMimeType swf_mime_type( content::kFlashPluginSwfMimeType, content::kFlashPluginSwfExtension, @@ -62,8 +71,95 @@ content::PepperPluginInfo CreatePepperFlashInfo(const base::FilePath& path, return plugin; } +#if defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS) +content::PepperPluginInfo CreateWidevineCdmInfo(const base::FilePath& path, + const std::string& version) { + content::PepperPluginInfo widevine_cdm; + widevine_cdm.is_out_of_process = true; + widevine_cdm.path = path; + widevine_cdm.name = kWidevineCdmDisplayName; + widevine_cdm.description = kWidevineCdmDescription + + std::string(" (version: ") + + version + ")"; + widevine_cdm.version = version; + content::WebPluginMimeType widevine_cdm_mime_type( + kWidevineCdmPluginMimeType, + kWidevineCdmPluginExtension, + kWidevineCdmPluginMimeTypeDescription); + + // Add the supported codecs as if they came from the component manifest. + std::vector codecs; + codecs.push_back(kCdmSupportedCodecVorbis); + codecs.push_back(kCdmSupportedCodecVp8); + codecs.push_back(kCdmSupportedCodecVp9); +#if defined(USE_PROPRIETARY_CODECS) + codecs.push_back(kCdmSupportedCodecAac); + codecs.push_back(kCdmSupportedCodecAvc1); +#endif // defined(USE_PROPRIETARY_CODECS) + std::string codec_string = base::JoinString( + codecs, std::string(1, kCdmSupportedCodecsValueDelimiter)); + widevine_cdm_mime_type.additional_param_names.push_back( + base::ASCIIToUTF16(kCdmSupportedCodecsParamName)); + widevine_cdm_mime_type.additional_param_values.push_back( + base::ASCIIToUTF16(codec_string)); + + widevine_cdm.mime_types.push_back(widevine_cdm_mime_type); + widevine_cdm.permissions = kWidevineCdmPluginPermissions; + + return widevine_cdm; +} +#endif + +void ConvertStringWithSeparatorToVector(std::vector* vec, + const char* separator, + const char* cmd_switch) { + auto command_line = base::CommandLine::ForCurrentProcess(); + auto string_with_separator = command_line->GetSwitchValueASCII(cmd_switch); + if (!string_with_separator.empty()) + *vec = base::SplitString(string_with_separator, separator, + base::TRIM_WHITESPACE, + base::SPLIT_WANT_NONEMPTY); +} + } // namespace +void AddPepperFlashFromCommandLine( + std::vector* plugins) { + auto command_line = base::CommandLine::ForCurrentProcess(); + auto flash_path = command_line->GetSwitchValueNative( + switches::kPpapiFlashPath); + if (flash_path.empty()) + return; + + auto flash_version = command_line->GetSwitchValueASCII( + switches::kPpapiFlashVersion); + + plugins->push_back( + CreatePepperFlashInfo(base::FilePath(flash_path), flash_version)); +} + +#if defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS) +void AddWidevineCdmFromCommandLine( + std::vector* plugins) { + auto command_line = base::CommandLine::ForCurrentProcess(); + auto widevine_cdm_path = command_line->GetSwitchValueNative( + switches::kWidevineCdmPath); + if (widevine_cdm_path.empty()) + return; + + if (!base::PathExists(base::FilePath(widevine_cdm_path))) + return; + + auto widevine_cdm_version = command_line->GetSwitchValueASCII( + switches::kWidevineCdmVersion); + if (widevine_cdm_version.empty()) + return; + + plugins->push_back(CreateWidevineCdmInfo(base::FilePath(widevine_cdm_path), + widevine_cdm_version)); +} +#endif + AtomContentClient::AtomContentClient() { } @@ -80,35 +176,41 @@ std::string AtomContentClient::GetUserAgent() const { ATOM_PRODUCT_NAME "/" ATOM_VERSION_STRING); } +base::string16 AtomContentClient::GetLocalizedString(int message_id) const { + return l10n_util::GetStringUTF16(message_id); +} + void AtomContentClient::AddAdditionalSchemes( - std::vector* standard_schemes, + std::vector* standard_schemes, std::vector* savable_schemes) { - auto command_line = base::CommandLine::ForCurrentProcess(); - auto custom_schemes = command_line->GetSwitchValueASCII( - switches::kRegisterStandardSchemes); - if (!custom_schemes.empty()) { - std::vector schemes; - base::SplitString(custom_schemes, ',', &schemes); - standard_schemes->insert(standard_schemes->end(), - schemes.begin(), - schemes.end()); + std::vector schemes; + ConvertStringWithSeparatorToVector(&schemes, ",", + switches::kRegisterStandardSchemes); + if (!schemes.empty()) { + for (const std::string& scheme : schemes) + standard_schemes->push_back({scheme.c_str(), url::SCHEME_WITHOUT_PORT}); } - standard_schemes->push_back("chrome-extension"); + standard_schemes->push_back({"chrome-extension", url::SCHEME_WITHOUT_PORT}); } void AtomContentClient::AddPepperPlugins( std::vector* plugins) { - auto command_line = base::CommandLine::ForCurrentProcess(); - auto flash_path = command_line->GetSwitchValuePath( - switches::kPpapiFlashPath); - if (flash_path.empty()) - return; + AddPepperFlashFromCommandLine(plugins); +#if defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS) + AddWidevineCdmFromCommandLine(plugins); +#endif +} - auto flash_version = command_line->GetSwitchValueASCII( - switches::kPpapiFlashVersion); - - plugins->push_back( - CreatePepperFlashInfo(flash_path, flash_version)); +void AtomContentClient::AddServiceWorkerSchemes( + std::set* service_worker_schemes) { + std::vector schemes; + ConvertStringWithSeparatorToVector(&schemes, ",", + switches::kRegisterServiceWorkerSchemes); + if (!schemes.empty()) { + for (const std::string& scheme : schemes) + service_worker_schemes->insert(scheme); + } + service_worker_schemes->insert(url::kFileScheme); } } // namespace atom diff --git a/atom/app/atom_content_client.h b/atom/app/atom_content_client.h index a6b2f73e7..33dbe19d9 100644 --- a/atom/app/atom_content_client.h +++ b/atom/app/atom_content_client.h @@ -5,6 +5,7 @@ #ifndef ATOM_APP_ATOM_CONTENT_CLIENT_H_ #define ATOM_APP_ATOM_CONTENT_CLIENT_H_ +#include #include #include @@ -21,11 +22,14 @@ class AtomContentClient : public brightray::ContentClient { // content::ContentClient: std::string GetProduct() const override; std::string GetUserAgent() const override; + base::string16 GetLocalizedString(int message_id) const override; void AddAdditionalSchemes( - std::vector* standard_schemes, + std::vector* standard_schemes, std::vector* savable_schemes) override; void AddPepperPlugins( std::vector* plugins) override; + void AddServiceWorkerSchemes( + std::set* service_worker_schemes) override; private: DISALLOW_COPY_AND_ASSIGN(AtomContentClient); diff --git a/atom/app/atom_main.cc b/atom/app/atom_main.cc index 26dcb9421..1ffab8d92 100644 --- a/atom/app/atom_main.cc +++ b/atom/app/atom_main.cc @@ -5,13 +5,8 @@ #include "atom/app/atom_main.h" #include -#include #if defined(OS_WIN) -#include -#include -#include - #include #include #include @@ -36,10 +31,27 @@ #include "base/at_exit.h" #include "base/i18n/icu_util.h" -#if defined(OS_WIN) - namespace { +const char* kRunAsNode = "ELECTRON_RUN_AS_NODE"; +const char* kOldRunAsNode = "ATOM_SHELL_INTERNAL_RUN_AS_NODE"; + +bool IsEnvSet(const char* name) { +#if defined(OS_WIN) + size_t required_size; + getenv_s(&required_size, nullptr, 0, name); + return required_size != 0; +#else + char* indicator = getenv(name); + return indicator && indicator[0] != '\0'; +#endif +} + +bool IsRunAsNode() { + return IsEnvSet(kRunAsNode) || IsEnvSet(kOldRunAsNode); +} + +#if defined(OS_WIN) // Win8.1 supports monitor-specific DPI scaling. bool SetProcessDpiAwarenessWrapper(PROCESS_DPI_AWARENESS value) { typedef HRESULT(WINAPI *SetProcessDpiAwarenessPtr)(PROCESS_DPI_AWARENESS); @@ -77,24 +89,22 @@ void EnableHighDPISupport() { SetProcessDPIAwareWrapper(); } } +#endif } // namespace +#if defined(OS_WIN) int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) { int argc = 0; wchar_t** wargv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); - scoped_ptr env(base::Environment::Create()); - // Make output work in console if we are not in cygiwn. - std::string os; - if (env->GetVar("OS", &os) && os != "cygwin") { + if (!IsEnvSet("TERM") && !IsEnvSet("ELECTRON_NO_ATTACH_CONSOLE")) { AttachConsole(ATTACH_PARENT_PROCESS); FILE* dontcare; freopen_s(&dontcare, "CON", "w", stdout); freopen_s(&dontcare, "CON", "w", stderr); - freopen_s(&dontcare, "CON", "r", stdin); } // Convert argv to to UTF8 @@ -131,16 +141,12 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) { } } - std::string node_indicator, crash_service_indicator; - if (env->GetVar("ATOM_SHELL_INTERNAL_RUN_AS_NODE", &node_indicator) && - node_indicator == "1") { + if (IsRunAsNode()) { // Now that argv conversion is done, we can finally start. base::AtExitManager atexit_manager; base::i18n::InitializeICU(); return atom::NodeMain(argc, argv); - } else if (env->GetVar("ATOM_SHELL_INTERNAL_CRASH_SERVICE", - &crash_service_indicator) && - crash_service_indicator == "1") { + } else if (IsEnvSet("ATOM_SHELL_INTERNAL_CRASH_SERVICE")) { return crash_service::Main(cmd); } @@ -164,8 +170,7 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) { #elif defined(OS_LINUX) // defined(OS_WIN) int main(int argc, const char* argv[]) { - char* node_indicator = getenv("ATOM_SHELL_INTERNAL_RUN_AS_NODE"); - if (node_indicator != NULL && strcmp(node_indicator, "1") == 0) { + if (IsRunAsNode()) { base::i18n::InitializeICU(); base::AtExitManager atexit_manager; return atom::NodeMain(argc, const_cast(argv)); @@ -182,8 +187,7 @@ int main(int argc, const char* argv[]) { #else // defined(OS_LINUX) int main(int argc, const char* argv[]) { - char* node_indicator = getenv("ATOM_SHELL_INTERNAL_RUN_AS_NODE"); - if (node_indicator != NULL && strcmp(node_indicator, "1") == 0) { + if (IsRunAsNode()) { return AtomInitializeICUandStartNode(argc, const_cast(argv)); } diff --git a/atom/app/atom_main_delegate.cc b/atom/app/atom_main_delegate.cc index 802831310..698da4f4d 100644 --- a/atom/app/atom_main_delegate.cc +++ b/atom/app/atom_main_delegate.cc @@ -18,6 +18,7 @@ #include "base/logging.h" #include "chrome/common/chrome_paths.h" #include "content/public/common/content_switches.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" namespace atom { @@ -137,16 +138,4 @@ scoped_ptr AtomMainDelegate::CreateContentClient() { return scoped_ptr(new AtomContentClient).Pass(); } -void AtomMainDelegate::AddDataPackFromPath( - ui::ResourceBundle* bundle, const base::FilePath& pak_dir) { -#if defined(OS_WIN) - bundle->AddDataPackFromPath( - pak_dir.Append(FILE_PATH_LITERAL("ui_resources_200_percent.pak")), - ui::SCALE_FACTOR_200P); - bundle->AddDataPackFromPath( - pak_dir.Append(FILE_PATH_LITERAL("content_resources_200_percent.pak")), - ui::SCALE_FACTOR_200P); -#endif -} - } // namespace atom diff --git a/atom/app/atom_main_delegate.h b/atom/app/atom_main_delegate.h index 7bcde8125..5f4369302 100644 --- a/atom/app/atom_main_delegate.h +++ b/atom/app/atom_main_delegate.h @@ -25,8 +25,6 @@ class AtomMainDelegate : public brightray::MainDelegate { // brightray::MainDelegate: scoped_ptr CreateContentClient() override; - void AddDataPackFromPath( - ui::ResourceBundle* bundle, const base::FilePath& pak_dir) override; #if defined(OS_MACOSX) void OverrideChildProcessPath() override; void OverrideFrameworkBundlePath() override; diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index 697d6eca6..256ecff53 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -181,7 +181,8 @@ void App::OnWindowAllClosed() { } void App::OnQuit() { - Emit("quit"); + int exitCode = AtomBrowserMainParts::Get()->GetExitCode(); + Emit("quit", exitCode); if (process_singleton_.get()) { process_singleton_->Cleanup(); diff --git a/atom/browser/api/atom_api_cookies.cc b/atom/browser/api/atom_api_cookies.cc index a3b2c37c9..6323e5110 100644 --- a/atom/browser/api/atom_api_cookies.cc +++ b/atom/browser/api/atom_api_cookies.cc @@ -7,7 +7,6 @@ #include "atom/common/native_mate_converters/callback.h" #include "atom/common/native_mate_converters/gurl_converter.h" #include "atom/common/native_mate_converters/value_converter.h" -#include "base/bind.h" #include "base/time/time.h" #include "base/values.h" #include "content/public/browser/browser_context.h" @@ -20,139 +19,21 @@ #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" -using atom::api::Cookies; using content::BrowserThread; -namespace { - -bool GetCookieListFromStore( - net::CookieStore* cookie_store, - const std::string& url, - const net::CookieMonster::GetCookieListCallback& callback) { - DCHECK(cookie_store); - GURL gurl(url); - net::CookieMonster* monster = cookie_store->GetCookieMonster(); - // Empty url will match all url cookies. - if (url.empty()) { - monster->GetAllCookiesAsync(callback); - return true; - } - - if (!gurl.is_valid()) - return false; - - monster->GetAllCookiesForURLAsync(gurl, callback); - return true; -} - -void RunGetCookiesCallbackOnUIThread(v8::Isolate* isolate, - const std::string& error_message, - const net::CookieList& cookie_list, - const Cookies::CookiesCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - v8::Locker locker(isolate); - v8::HandleScope handle_scope(isolate); - - if (!error_message.empty()) { - v8::Local error = mate::ConvertToV8(isolate, error_message); - callback.Run(error, v8::Null(isolate)); - return; - } - callback.Run(v8::Null(isolate), mate::ConvertToV8(isolate, cookie_list)); -} - -void RunRemoveCookiesCallbackOnUIThread( - v8::Isolate* isolate, - const std::string& error_message, - const Cookies::CookiesCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - v8::Locker locker(isolate); - v8::HandleScope handle_scope(isolate); - - if (!error_message.empty()) { - v8::Local error = mate::ConvertToV8(isolate, error_message); - callback.Run(error, v8::Null(isolate)); - return; - } - - callback.Run(v8::Null(isolate), v8::Null(isolate)); -} - -void RunSetCookiesCallbackOnUIThread(v8::Isolate* isolate, - const std::string& error_message, - bool set_success, - const Cookies::CookiesCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - v8::Locker locker(isolate); - v8::HandleScope handle_scope(isolate); - - if (!error_message.empty()) { - v8::Local error = mate::ConvertToV8(isolate, error_message); - callback.Run(error, v8::Null(isolate)); - return; - } - if (!set_success) { - v8::Local error = mate::ConvertToV8( - isolate, "Failed to set cookies"); - callback.Run(error, v8::Null(isolate)); - } - - callback.Run(v8::Null(isolate), v8::Null(isolate)); -} - -bool MatchesDomain(const base::DictionaryValue* filter, - const std::string& cookie_domain) { - std::string filter_domain; - if (!filter->GetString("domain", &filter_domain)) - return true; - - // Add a leading '.' character to the filter domain if it doesn't exist. - if (net::cookie_util::DomainIsHostOnly(filter_domain)) - filter_domain.insert(0, "."); - - std::string sub_domain(cookie_domain); - // Strip any leading '.' character from the input cookie domain. - if (!net::cookie_util::DomainIsHostOnly(sub_domain)) - sub_domain = sub_domain.substr(1); - - // Now check whether the domain argument is a subdomain of the filter domain. - for (sub_domain.insert(0, "."); - sub_domain.length() >= filter_domain.length();) { - if (sub_domain == filter_domain) { - return true; - } - const size_t next_dot = sub_domain.find('.', 1); // Skip over leading dot. - sub_domain.erase(0, next_dot); - } - return false; -} - -bool MatchesCookie(const base::DictionaryValue* filter, - const net::CanonicalCookie& cookie) { - std::string name, domain, path; - bool is_secure, session; - if (filter->GetString("name", &name) && name != cookie.Name()) - return false; - if (filter->GetString("path", &path) && path != cookie.Path()) - return false; - if (!MatchesDomain(filter, cookie.Domain())) - return false; - if (filter->GetBoolean("secure", &is_secure) && - is_secure != cookie.IsSecure()) - return false; - if (filter->GetBoolean("session", &session) && - session != cookie.IsPersistent()) - return false; - return true; -} - -} // namespace - namespace mate { +template<> +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + atom::api::Cookies::Error val) { + if (val == atom::api::Cookies::SUCCESS) + return v8::Null(isolate); + else + return v8::Exception::Error(StringToV8(isolate, "failed")); + } +}; + template<> struct Converter { static v8::Local ToV8(v8::Isolate* isolate, @@ -161,11 +42,11 @@ struct Converter { dict.Set("name", val.Name()); dict.Set("value", val.Value()); dict.Set("domain", val.Domain()); - dict.Set("host_only", net::cookie_util::DomainIsHostOnly(val.Domain())); + dict.Set("hostOnly", net::cookie_util::DomainIsHostOnly(val.Domain())); dict.Set("path", val.Path()); dict.Set("secure", val.IsSecure()); - dict.Set("http_only", val.IsHttpOnly()); - dict.Set("session", val.IsPersistent()); + dict.Set("httpOnly", val.IsHttpOnly()); + dict.Set("session", !val.IsPersistent()); if (!val.IsPersistent()) dict.Set("expirationDate", val.ExpiryDate().ToDoubleT()); return dict.GetHandle(); @@ -178,121 +59,117 @@ namespace atom { namespace api { -Cookies::Cookies(content::BrowserContext* browser_context) - : request_context_getter_(browser_context->GetRequestContext()) { -} +namespace { -Cookies::~Cookies() { -} +// Returns whether |domain| matches |filter|. +bool MatchesDomain(std::string filter, const std::string& domain) { + // Add a leading '.' character to the filter domain if it doesn't exist. + if (net::cookie_util::DomainIsHostOnly(filter)) + filter.insert(0, "."); -void Cookies::Get(const base::DictionaryValue& options, - const CookiesCallback& callback) { - scoped_ptr filter( - options.DeepCopyWithoutEmptyChildren()); + std::string sub_domain(domain); + // Strip any leading '.' character from the input cookie domain. + if (!net::cookie_util::DomainIsHostOnly(sub_domain)) + sub_domain = sub_domain.substr(1); - content::BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&Cookies::GetCookiesOnIOThread, base::Unretained(this), - Passed(&filter), callback)); -} - -void Cookies::GetCookiesOnIOThread(scoped_ptr filter, - const CookiesCallback& callback) { - std::string url; - filter->GetString("url", &url); - if (!GetCookieListFromStore(GetCookieStore(), url, - base::Bind(&Cookies::OnGetCookies, base::Unretained(this), - Passed(&filter), callback))) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&RunGetCookiesCallbackOnUIThread, isolate(), - "URL is not valid", net::CookieList(), callback)); + // Now check whether the domain argument is a subdomain of the filter domain. + for (sub_domain.insert(0, "."); sub_domain.length() >= filter.length();) { + if (sub_domain == filter) + return true; + const size_t next_dot = sub_domain.find('.', 1); // Skip over leading dot. + sub_domain.erase(0, next_dot); } + return false; } -void Cookies::OnGetCookies(scoped_ptr filter, - const CookiesCallback& callback, - const net::CookieList& cookie_list) { +// Returns whether |cookie| matches |filter|. +bool MatchesCookie(const base::DictionaryValue* filter, + const net::CanonicalCookie& cookie) { + std::string str; + bool b; + if (filter->GetString("name", &str) && str != cookie.Name()) + return false; + if (filter->GetString("path", &str) && str != cookie.Path()) + return false; + if (filter->GetString("domain", &str) && !MatchesDomain(str, cookie.Domain())) + return false; + if (filter->GetBoolean("secure", &b) && b != cookie.IsSecure()) + return false; + if (filter->GetBoolean("session", &b) && b != !cookie.IsPersistent()) + return false; + return true; +} + +// Helper to returns the CookieStore. +inline net::CookieStore* GetCookieStore( + scoped_refptr getter) { + return getter->GetURLRequestContext()->cookie_store(); +} + +// Run |callback| on UI thread. +void RunCallbackInUI(const base::Closure& callback) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback); +} + +// Remove cookies from |list| not matching |filter|, and pass it to |callback|. +void FilterCookies(scoped_ptr filter, + const Cookies::GetCallback& callback, + const net::CookieList& list) { net::CookieList result; - for (const auto& cookie : cookie_list) { + for (const auto& cookie : list) { if (MatchesCookie(filter.get(), cookie)) result.push_back(cookie); } - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( - &RunGetCookiesCallbackOnUIThread, isolate(), "", result, callback)); + RunCallbackInUI(base::Bind(callback, Cookies::SUCCESS, result)); } -void Cookies::Remove(const mate::Dictionary& details, - const CookiesCallback& callback) { - GURL url; - std::string name; - std::string error_message; - if (!details.Get("url", &url) || !details.Get("name", &name)) { - error_message = "Details(url, name) of removing cookie are required."; - } - if (error_message.empty() && !url.is_valid()) { - error_message = "URL is not valid."; - } - if (!error_message.empty()) { - RunRemoveCookiesCallbackOnUIThread(isolate(), error_message, callback); - return; - } - content::BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&Cookies::RemoveCookiesOnIOThread, base::Unretained(this), - url, name, callback)); -} - -void Cookies::RemoveCookiesOnIOThread(const GURL& url, const std::string& name, - const CookiesCallback& callback) { - GetCookieStore()->DeleteCookieAsync(url, name, - base::Bind(&Cookies::OnRemoveCookies, base::Unretained(this), callback)); -} - -void Cookies::OnRemoveCookies(const CookiesCallback& callback) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&RunRemoveCookiesCallbackOnUIThread, isolate(), "", callback)); -} - -void Cookies::Set(const base::DictionaryValue& options, - const CookiesCallback& callback) { +// Receives cookies matching |filter| in IO thread. +void GetCookiesOnIO(scoped_refptr getter, + scoped_ptr filter, + const Cookies::GetCallback& callback) { std::string url; - std::string error_message; - if (!options.GetString("url", &url)) { - error_message = "The url field is required."; - } + filter->GetString("url", &url); - GURL gurl(url); - if (error_message.empty() && !gurl.is_valid()) { - error_message = "URL is not valid."; - } + auto filtered_callback = + base::Bind(FilterCookies, base::Passed(&filter), callback); - if (!error_message.empty()) { - RunSetCookiesCallbackOnUIThread(isolate(), error_message, false, callback); - return; - } - - scoped_ptr details( - options.DeepCopyWithoutEmptyChildren()); - - content::BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&Cookies::SetCookiesOnIOThread, base::Unretained(this), - Passed(&details), gurl, callback)); + net::CookieMonster* monster = GetCookieStore(getter)->GetCookieMonster(); + // Empty url will match all url cookies. + if (url.empty()) + monster->GetAllCookiesAsync(filtered_callback); + else + monster->GetAllCookiesForURLAsync(GURL(url), filtered_callback); } -void Cookies::SetCookiesOnIOThread(scoped_ptr details, - const GURL& url, - const CookiesCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); +// Removes cookie with |url| and |name| in IO thread. +void RemoveCookieOnIOThread(scoped_refptr getter, + const GURL& url, const std::string& name, + const base::Closure& callback) { + GetCookieStore(getter)->DeleteCookieAsync( + url, name, base::Bind(RunCallbackInUI, callback)); +} - std::string name, value, domain, path; +// Callback of SetCookie. +void OnSetCookie(const Cookies::SetCallback& callback, bool success) { + RunCallbackInUI( + base::Bind(callback, success ? Cookies::SUCCESS : Cookies::FAILED)); +} + +// Sets cookie with |details| in IO thread. +void SetCookieOnIO(scoped_refptr getter, + scoped_ptr details, + const Cookies::SetCallback& callback) { + std::string url, name, value, domain, path; bool secure = false; bool http_only = false; double expiration_date; - + details->GetString("url", &url); details->GetString("name", &name); details->GetString("value", &value); details->GetString("domain", &domain); details->GetString("path", &path); details->GetBoolean("secure", &secure); - details->GetBoolean("http_only", &http_only); + details->GetBoolean("httpOnly", &http_only); base::Time expiration_time; if (details->GetDouble("expirationDate", &expiration_date)) { @@ -301,37 +178,44 @@ void Cookies::SetCookiesOnIOThread(scoped_ptr details, base::Time::FromDoubleT(expiration_date); } - GetCookieStore()->GetCookieMonster()->SetCookieWithDetailsAsync( - url, - name, - value, - domain, - path, - expiration_time, - secure, - http_only, - false, - net::COOKIE_PRIORITY_DEFAULT, - base::Bind(&Cookies::OnSetCookies, base::Unretained(this), callback)); + GetCookieStore(getter)->GetCookieMonster()->SetCookieWithDetailsAsync( + GURL(url), name, value, domain, path, expiration_time, secure, http_only, + false, net::COOKIE_PRIORITY_DEFAULT, base::Bind(OnSetCookie, callback)); } -void Cookies::OnSetCookies(const CookiesCallback& callback, - bool set_success) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&RunSetCookiesCallbackOnUIThread, isolate(), "", set_success, - callback)); +} // namespace + +Cookies::Cookies(content::BrowserContext* browser_context) + : request_context_getter_(browser_context->GetRequestContext()) { } -mate::ObjectTemplateBuilder Cookies::GetObjectTemplateBuilder( - v8::Isolate* isolate) { - return mate::ObjectTemplateBuilder(isolate) - .SetMethod("get", &Cookies::Get) - .SetMethod("remove", &Cookies::Remove) - .SetMethod("set", &Cookies::Set); +Cookies::~Cookies() { } -net::CookieStore* Cookies::GetCookieStore() { - return request_context_getter_->GetURLRequestContext()->cookie_store(); +void Cookies::Get(const base::DictionaryValue& filter, + const GetCallback& callback) { + scoped_ptr copied(filter.CreateDeepCopy()); + auto getter = make_scoped_refptr(request_context_getter_); + content::BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(GetCookiesOnIO, getter, Passed(&copied), callback)); +} + +void Cookies::Remove(const GURL& url, const std::string& name, + const base::Closure& callback) { + auto getter = make_scoped_refptr(request_context_getter_); + content::BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(RemoveCookieOnIOThread, getter, url, name, callback)); +} + +void Cookies::Set(const base::DictionaryValue& details, + const SetCallback& callback) { + scoped_ptr copied(details.CreateDeepCopy()); + auto getter = make_scoped_refptr(request_context_getter_); + content::BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(SetCookieOnIO, getter, Passed(&copied), callback)); } // static @@ -341,6 +225,15 @@ mate::Handle Cookies::Create( return mate::CreateHandle(isolate, new Cookies(browser_context)); } +// static +void Cookies::BuildPrototype(v8::Isolate* isolate, + v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) + .SetMethod("get", &Cookies::Get) + .SetMethod("remove", &Cookies::Remove) + .SetMethod("set", &Cookies::Set); +} + } // namespace api } // namespace atom diff --git a/atom/browser/api/atom_api_cookies.h b/atom/browser/api/atom_api_cookies.h index 0c309b3f1..302fd1b25 100644 --- a/atom/browser/api/atom_api_cookies.h +++ b/atom/browser/api/atom_api_cookies.h @@ -7,8 +7,8 @@ #include +#include "atom/browser/api/trackable_object.h" #include "base/callback.h" -#include "native_mate/wrappable.h" #include "native_mate/handle.h" #include "net/cookies/canonical_cookie.h" @@ -20,12 +20,7 @@ namespace content { class BrowserContext; } -namespace mate { -class Dictionary; -} - namespace net { -class CookieStore; class URLRequestContextGetter; } @@ -33,51 +28,33 @@ namespace atom { namespace api { -class Cookies : public mate::Wrappable { +class Cookies : public mate::TrackableObject { public: - // node.js style callback function(error, result) - typedef base::Callback, v8::Local)> - CookiesCallback; + enum Error { + SUCCESS, + FAILED, + }; + + using GetCallback = base::Callback; + using SetCallback = base::Callback; static mate::Handle Create(v8::Isolate* isolate, content::BrowserContext* browser_context); + // mate::TrackableObject: + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + protected: explicit Cookies(content::BrowserContext* browser_context); ~Cookies(); - void Get(const base::DictionaryValue& options, - const CookiesCallback& callback); - void Remove(const mate::Dictionary& details, - const CookiesCallback& callback); - void Set(const base::DictionaryValue& details, - const CookiesCallback& callback); - - void GetCookiesOnIOThread(scoped_ptr filter, - const CookiesCallback& callback); - void OnGetCookies(scoped_ptr filter, - const CookiesCallback& callback, - const net::CookieList& cookie_list); - - void RemoveCookiesOnIOThread(const GURL& url, - const std::string& name, - const CookiesCallback& callback); - void OnRemoveCookies(const CookiesCallback& callback); - - void SetCookiesOnIOThread(scoped_ptr details, - const GURL& url, - const CookiesCallback& callback); - void OnSetCookies(const CookiesCallback& callback, - bool set_success); - - // mate::Wrappable: - mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) override; + void Get(const base::DictionaryValue& filter, const GetCallback& callback); + void Remove(const GURL& url, const std::string& name, + const base::Closure& callback); + void Set(const base::DictionaryValue& details, const SetCallback& callback); private: - // Must be called on IO thread. - net::CookieStore* GetCookieStore(); - net::URLRequestContextGetter* request_context_getter_; DISALLOW_COPY_AND_ASSIGN(Cookies); diff --git a/atom/browser/api/atom_api_desktop_capturer.cc b/atom/browser/api/atom_api_desktop_capturer.cc new file mode 100644 index 000000000..ceb69deca --- /dev/null +++ b/atom/browser/api/atom_api_desktop_capturer.cc @@ -0,0 +1,120 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/api/atom_api_desktop_capturer.h" + +#include "atom/common/api/atom_api_native_image.h" +#include "atom/common/node_includes.h" +#include "atom/common/native_mate_converters/gfx_converter.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/media/desktop_media_list.h" +#include "native_mate/dictionary.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 mate { + +template<> +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + const DesktopMediaList::Source& source) { + mate::Dictionary dict(isolate, v8::Object::New(isolate)); + content::DesktopMediaID id = source.id; + dict.Set("name", base::UTF16ToUTF8(source.name)); + dict.Set("id", id.ToString()); + dict.Set( + "thumbnail", + atom::api::NativeImage::Create(isolate, gfx::Image(source.thumbnail))); + return ConvertToV8(isolate, dict); + } +}; + +} // namespace mate + +namespace atom { + +namespace api { + +DesktopCapturer::DesktopCapturer() { +} + +DesktopCapturer::~DesktopCapturer() { +} + +void DesktopCapturer::StartHandling(bool capture_window, + bool capture_screen, + const gfx::Size& thumbnail_size) { + webrtc::DesktopCaptureOptions options = + webrtc::DesktopCaptureOptions::CreateDefault(); + +#if defined(OS_WIN) + // On windows, desktop effects (e.g. Aero) will be disabled when the Desktop + // capture API is active by default. + // We keep the desktop effects in most times. Howerver, the screen still + // fickers when the API is capturing the window due to limitation of current + // implemetation. This is a known and wontFix issue in webrtc (see: + // http://code.google.com/p/webrtc/issues/detail?id=3373) + options.set_disable_effects(false); +#endif + + scoped_ptr screen_capturer( + capture_screen ? webrtc::ScreenCapturer::Create(options) : nullptr); + scoped_ptr window_capturer( + capture_window ? webrtc::WindowCapturer::Create(options) : nullptr); + media_list_.reset(new NativeDesktopMediaList(screen_capturer.Pass(), + window_capturer.Pass())); + + media_list_->SetThumbnailSize(thumbnail_size); + media_list_->StartUpdating(this); +} + +void DesktopCapturer::OnSourceAdded(int index) { +} + +void DesktopCapturer::OnSourceRemoved(int index) { +} + +void DesktopCapturer::OnSourceMoved(int old_index, int new_index) { +} + +void DesktopCapturer::OnSourceNameChanged(int index) { +} + +void DesktopCapturer::OnSourceThumbnailChanged(int index) { +} + +bool DesktopCapturer::OnRefreshFinished() { + Emit("finished", media_list_->GetSources()); + media_list_.reset(); + return false; +} + +mate::ObjectTemplateBuilder DesktopCapturer::GetObjectTemplateBuilder( + v8::Isolate* isolate) { + return mate::ObjectTemplateBuilder(isolate) + .SetMethod("startHandling", &DesktopCapturer::StartHandling); +} + +// static +mate::Handle DesktopCapturer::Create(v8::Isolate* isolate) { + return mate::CreateHandle(isolate, new DesktopCapturer); +} + +} // namespace api + +} // namespace atom + +namespace { + +void Initialize(v8::Local exports, v8::Local unused, + v8::Local context, void* priv) { + v8::Isolate* isolate = context->GetIsolate(); + mate::Dictionary dict(isolate, exports); + dict.Set("desktopCapturer", atom::api::DesktopCapturer::Create(isolate)); +} + +} // namespace + +NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_desktop_capturer, Initialize); diff --git a/atom/browser/api/atom_api_desktop_capturer.h b/atom/browser/api/atom_api_desktop_capturer.h new file mode 100644 index 000000000..c22c8a448 --- /dev/null +++ b/atom/browser/api/atom_api_desktop_capturer.h @@ -0,0 +1,52 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_API_ATOM_API_DESKTOP_CAPTURER_H_ +#define ATOM_BROWSER_API_ATOM_API_DESKTOP_CAPTURER_H_ + +#include "atom/browser/api/event_emitter.h" +#include "chrome/browser/media/desktop_media_list_observer.h" +#include "chrome/browser/media/native_desktop_media_list.h" +#include "native_mate/handle.h" + +namespace atom { + +namespace api { + +class DesktopCapturer: public mate::EventEmitter, + public DesktopMediaListObserver { + public: + static mate::Handle Create(v8::Isolate* isolate); + + void StartHandling(bool capture_window, + bool capture_screen, + const gfx::Size& thumbnail_size); + + protected: + DesktopCapturer(); + ~DesktopCapturer(); + + // DesktopMediaListObserver overrides. + 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; + bool OnRefreshFinished() override; + + private: + // mate::Wrappable: + mate::ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) override; + + scoped_ptr media_list_; + + DISALLOW_COPY_AND_ASSIGN(DesktopCapturer); +}; + +} // namespace api + +} // namespace atom + +#endif // ATOM_BROWSER_API_ATOM_API_DESKTOP_CAPTURER_H_ diff --git a/atom/browser/api/atom_api_download_item.cc b/atom/browser/api/atom_api_download_item.cc index 7dd271304..f186821e7 100644 --- a/atom/browser/api/atom_api_download_item.cc +++ b/atom/browser/api/atom_api_download_item.cc @@ -70,29 +70,20 @@ DownloadItem::DownloadItem(content::DownloadItem* download_item) : } DownloadItem::~DownloadItem() { - Destroy(); -} - -void DownloadItem::Destroy() { - if (download_item_) { - download_item_->RemoveObserver(this); - auto iter = g_download_item_objects.find(download_item_->GetId()); - if (iter != g_download_item_objects.end()) - g_download_item_objects.erase(iter); - download_item_ = nullptr; - } -} - -bool DownloadItem::IsDestroyed() const { - return download_item_ == nullptr; + if (download_item_) + OnDownloadDestroyed(download_item_); } void DownloadItem::OnDownloadUpdated(content::DownloadItem* item) { download_item_->IsDone() ? Emit("done", item->GetState()) : Emit("updated"); } -void DownloadItem::OnDownloadDestroyed(content::DownloadItem* download) { - Destroy(); +void DownloadItem::OnDownloadDestroyed(content::DownloadItem* download_item) { + download_item_->RemoveObserver(this); + auto iter = g_download_item_objects.find(download_item_->GetId()); + if (iter != g_download_item_objects.end()) + g_download_item_objects.erase(iter); + download_item_ = nullptr; } int64 DownloadItem::GetReceivedBytes() { @@ -144,9 +135,11 @@ void DownloadItem::Cancel() { download_item_->Cancel(true); } -mate::ObjectTemplateBuilder DownloadItem::GetObjectTemplateBuilder( - v8::Isolate* isolate) { - return mate::ObjectTemplateBuilder(isolate) +// static +void DownloadItem::BuildPrototype(v8::Isolate* isolate, + v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) + .MakeDestroyable() .SetMethod("pause", &DownloadItem::Pause) .SetMethod("resume", &DownloadItem::Resume) .SetMethod("cancel", &DownloadItem::Cancel) diff --git a/atom/browser/api/atom_api_download_item.h b/atom/browser/api/atom_api_download_item.h index 955801cd9..471913c22 100644 --- a/atom/browser/api/atom_api_download_item.h +++ b/atom/browser/api/atom_api_download_item.h @@ -17,7 +17,7 @@ namespace atom { namespace api { -class DownloadItem : public mate::EventEmitter, +class DownloadItem : public mate::TrackableObject, public content::DownloadItem::Observer { public: class SavePathData : public base::SupportsUserData::Data { @@ -32,6 +32,10 @@ class DownloadItem : public mate::EventEmitter, content::DownloadItem* item); static void* UserDataKey(); + // mate::TrackableObject: + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + protected: explicit DownloadItem(content::DownloadItem* download_item); ~DownloadItem(); @@ -53,13 +57,6 @@ class DownloadItem : public mate::EventEmitter, void SetSavePath(const base::FilePath& path); private: - // mate::Wrappable: - mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) override; - bool IsDestroyed() const override; - - void Destroy(); - content::DownloadItem* download_item_; DISALLOW_COPY_AND_ASSIGN(DownloadItem); diff --git a/atom/browser/api/atom_api_global_shortcut.cc b/atom/browser/api/atom_api_global_shortcut.cc index 6ab4fa4b6..f5a03e4ab 100644 --- a/atom/browser/api/atom_api_global_shortcut.cc +++ b/atom/browser/api/atom_api_global_shortcut.cc @@ -23,9 +23,6 @@ GlobalShortcut::GlobalShortcut() { } GlobalShortcut::~GlobalShortcut() { -} - -void GlobalShortcut::Destroy() { UnregisterAll(); } diff --git a/atom/browser/api/atom_api_global_shortcut.h b/atom/browser/api/atom_api_global_shortcut.h index 93eb7853a..d7057b000 100644 --- a/atom/browser/api/atom_api_global_shortcut.h +++ b/atom/browser/api/atom_api_global_shortcut.h @@ -27,9 +27,6 @@ class GlobalShortcut : public extensions::GlobalShortcutListener::Observer, GlobalShortcut(); ~GlobalShortcut() override; - // mate::TrackableObject: - void Destroy() override; - // mate::Wrappable implementations: mate::ObjectTemplateBuilder GetObjectTemplateBuilder( v8::Isolate* isolate) override; diff --git a/atom/browser/api/atom_api_menu.cc b/atom/browser/api/atom_api_menu.cc index 1f16a428d..96cba3fe9 100644 --- a/atom/browser/api/atom_api_menu.cc +++ b/atom/browser/api/atom_api_menu.cc @@ -27,14 +27,6 @@ Menu::Menu() Menu::~Menu() { } -void Menu::Destroy() { - model_.reset(); -} - -bool Menu::IsDestroyed() const { - return !model_; -} - void Menu::AfterInit(v8::Isolate* isolate) { mate::Dictionary wrappable(isolate, GetWrapper(isolate)); mate::Dictionary delegate; @@ -159,6 +151,7 @@ bool Menu::IsVisibleAt(int index) const { void Menu::BuildPrototype(v8::Isolate* isolate, v8::Local prototype) { mate::ObjectTemplateBuilder(isolate, prototype) + .MakeDestroyable() .SetMethod("insertItem", &Menu::InsertItemAt) .SetMethod("insertCheckItem", &Menu::InsertCheckItemAt) .SetMethod("insertRadioItem", &Menu::InsertRadioItemAt) diff --git a/atom/browser/api/atom_api_menu.h b/atom/browser/api/atom_api_menu.h index 545dd18e3..17bb9073a 100644 --- a/atom/browser/api/atom_api_menu.h +++ b/atom/browser/api/atom_api_menu.h @@ -39,11 +39,7 @@ class Menu : public mate::TrackableObject, Menu(); ~Menu() override; - // mate::TrackableObject: - void Destroy() override; - // mate::Wrappable: - bool IsDestroyed() const override; void AfterInit(v8::Isolate* isolate) override; // ui::SimpleMenuModel::Delegate: diff --git a/atom/browser/api/atom_api_menu_mac.h b/atom/browser/api/atom_api_menu_mac.h index baa2aff34..5a086776a 100644 --- a/atom/browser/api/atom_api_menu_mac.h +++ b/atom/browser/api/atom_api_menu_mac.h @@ -19,7 +19,6 @@ class MenuMac : public Menu { protected: MenuMac(); - void Destroy() override; void Popup(Window* window) override; void PopupAt(Window* window, int x, int y) override; diff --git a/atom/browser/api/atom_api_menu_mac.mm b/atom/browser/api/atom_api_menu_mac.mm index 5936e0439..071753218 100644 --- a/atom/browser/api/atom_api_menu_mac.mm +++ b/atom/browser/api/atom_api_menu_mac.mm @@ -18,11 +18,6 @@ namespace api { MenuMac::MenuMac() { } -void MenuMac::Destroy() { - menu_controller_.reset(); - Menu::Destroy(); -} - void MenuMac::Popup(Window* window) { NativeWindow* native_window = window->window(); if (!native_window) diff --git a/atom/browser/api/atom_api_power_monitor.cc b/atom/browser/api/atom_api_power_monitor.cc index eeb9475c2..31b35e10c 100644 --- a/atom/browser/api/atom_api_power_monitor.cc +++ b/atom/browser/api/atom_api_power_monitor.cc @@ -19,9 +19,6 @@ PowerMonitor::PowerMonitor() { } PowerMonitor::~PowerMonitor() { -} - -void PowerMonitor::Destroy() { base::PowerMonitor::Get()->RemoveObserver(this); } diff --git a/atom/browser/api/atom_api_power_monitor.h b/atom/browser/api/atom_api_power_monitor.h index 9303b3ab2..8fb52eeec 100644 --- a/atom/browser/api/atom_api_power_monitor.h +++ b/atom/browser/api/atom_api_power_monitor.h @@ -23,9 +23,6 @@ class PowerMonitor : public mate::TrackableObject, PowerMonitor(); ~PowerMonitor() override; - // mate::TrackableObject: - void Destroy() override; - // base::PowerObserver implementations: void OnPowerStateChange(bool on_battery_power) override; void OnSuspend() override; diff --git a/atom/browser/api/atom_api_power_save_blocker.cc b/atom/browser/api/atom_api_power_save_blocker.cc index f77979ae4..58983e6c8 100644 --- a/atom/browser/api/atom_api_power_save_blocker.cc +++ b/atom/browser/api/atom_api_power_save_blocker.cc @@ -45,11 +45,6 @@ PowerSaveBlocker::PowerSaveBlocker() PowerSaveBlocker::~PowerSaveBlocker() { } -void PowerSaveBlocker::Destroy() { - power_save_blocker_types_.clear(); - power_save_blocker_.reset(); -} - void PowerSaveBlocker::UpdatePowerSaveBlocker() { if (power_save_blocker_types_.empty()) { power_save_blocker_.reset(); diff --git a/atom/browser/api/atom_api_power_save_blocker.h b/atom/browser/api/atom_api_power_save_blocker.h index e7ce97878..a698d746c 100644 --- a/atom/browser/api/atom_api_power_save_blocker.h +++ b/atom/browser/api/atom_api_power_save_blocker.h @@ -28,9 +28,6 @@ class PowerSaveBlocker : public mate::TrackableObject { PowerSaveBlocker(); ~PowerSaveBlocker() override; - // mate::TrackableObject: - void Destroy() override; - // mate::Wrappable implementations: mate::ObjectTemplateBuilder GetObjectTemplateBuilder( v8::Isolate* isolate) override; diff --git a/atom/browser/api/atom_api_protocol.cc b/atom/browser/api/atom_api_protocol.cc index b1ad79813..09da9c71c 100644 --- a/atom/browser/api/atom_api_protocol.cc +++ b/atom/browser/api/atom_api_protocol.cc @@ -32,6 +32,8 @@ mate::ObjectTemplateBuilder Protocol::GetObjectTemplateBuilder( v8::Isolate* isolate) { return mate::ObjectTemplateBuilder(isolate) .SetMethod("registerStandardSchemes", &Protocol::RegisterStandardSchemes) + .SetMethod("registerServiceWorkerSchemes", + &Protocol::RegisterServiceWorkerSchemes) .SetMethod("registerStringProtocol", &Protocol::RegisterProtocol) .SetMethod("registerBufferProtocol", @@ -58,6 +60,11 @@ void Protocol::RegisterStandardSchemes( atom::AtomBrowserClient::SetCustomSchemes(schemes); } +void Protocol::RegisterServiceWorkerSchemes( + const std::vector& schemes) { + atom::AtomBrowserClient::SetCustomServiceWorkerSchemes(schemes); +} + void Protocol::UnregisterProtocol( const std::string& scheme, mate::Arguments* args) { CompletionCallback callback; diff --git a/atom/browser/api/atom_api_protocol.h b/atom/browser/api/atom_api_protocol.h index 9f98eb767..8aef406fb 100644 --- a/atom/browser/api/atom_api_protocol.h +++ b/atom/browser/api/atom_api_protocol.h @@ -92,6 +92,9 @@ class Protocol : public mate::Wrappable { // Register schemes to standard scheme list. void RegisterStandardSchemes(const std::vector& schemes); + // Register schemes that can handle service worker. + void RegisterServiceWorkerSchemes(const std::vector& schemes); + // Register the protocol with certain request job. template void RegisterProtocol(const std::string& scheme, diff --git a/atom/browser/api/atom_api_screen.cc b/atom/browser/api/atom_api_screen.cc index b73bda9ce..407a71f0c 100644 --- a/atom/browser/api/atom_api_screen.cc +++ b/atom/browser/api/atom_api_screen.cc @@ -41,7 +41,7 @@ std::vector MetricsToArray(uint32_t metrics) { if (metrics & gfx::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR) array.push_back("scaleFactor"); if (metrics & gfx::DisplayObserver::DISPLAY_METRIC_ROTATION) - array.push_back("rotaion"); + array.push_back("rotation"); return array; } diff --git a/atom/browser/api/atom_api_session.cc b/atom/browser/api/atom_api_session.cc index 27e1521f3..8b73d6162 100644 --- a/atom/browser/api/atom_api_session.cc +++ b/atom/browser/api/atom_api_session.cc @@ -10,6 +10,7 @@ #include "atom/browser/api/atom_api_cookies.h" #include "atom/browser/api/atom_api_download_item.h" #include "atom/browser/api/atom_api_web_contents.h" +#include "atom/browser/api/atom_api_web_request.h" #include "atom/browser/api/save_page_handler.h" #include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_main_parts.h" @@ -51,7 +52,7 @@ struct ClearStorageDataOptions { uint32 GetStorageMask(const std::vector& storage_types) { uint32 storage_mask = 0; for (const auto& it : storage_types) { - auto type = base::StringToLowerASCII(it); + auto type = base::ToLowerASCII(it); if (type == "appcache") storage_mask |= StoragePartition::REMOVE_DATA_MASK_APPCACHE; else if (type == "cookies") @@ -75,7 +76,7 @@ uint32 GetStorageMask(const std::vector& storage_types) { uint32 GetQuotaMask(const std::vector& quota_types) { uint32 quota_mask = 0; for (const auto& it : quota_types) { - auto type = base::StringToLowerASCII(it); + auto type = base::ToLowerASCII(it); if (type == "temporary") quota_mask |= StoragePartition::QUOTA_MANAGED_STORAGE_MASK_TEMPORARY; else if (type == "persistent") @@ -233,7 +234,8 @@ void SetProxyInIO(net::URLRequestContextGetter* getter, const net::ProxyConfig& config, const base::Closure& callback) { auto proxy_service = getter->GetURLRequestContext()->proxy_service(); - proxy_service->ResetConfigService(new net::ProxyConfigServiceFixed(config)); + proxy_service->ResetConfigService(make_scoped_ptr( + new net::ProxyConfigServiceFixed(config))); // Refetches and applies the new pac script if provided. proxy_service->ForceReloadProxyConfig(); RunCallbackInUI(callback); @@ -253,7 +255,6 @@ Session::Session(AtomBrowserContext* browser_context) Session::~Session() { content::BrowserContext::GetDownloadManager(browser_context())-> RemoveObserver(this); - Destroy(); } void Session::OnDownloadCreated(content::DownloadManager* manager, @@ -271,14 +272,6 @@ void Session::OnDownloadCreated(content::DownloadManager* manager, } } -bool Session::IsDestroyed() const { - return !browser_context_; -} - -void Session::Destroy() { - browser_context_ = nullptr; -} - void Session::ResolveProxy(const GURL& url, ResolveProxyCallback callback) { new ResolveProxyHelper(browser_context(), url, callback); } @@ -376,18 +369,12 @@ v8::Local Session::Cookies(v8::Isolate* isolate) { return v8::Local::New(isolate, cookies_); } -mate::ObjectTemplateBuilder Session::GetObjectTemplateBuilder( - v8::Isolate* isolate) { - return mate::ObjectTemplateBuilder(isolate) - .SetMethod("resolveProxy", &Session::ResolveProxy) - .SetMethod("clearCache", &Session::ClearCache) - .SetMethod("clearStorageData", &Session::ClearStorageData) - .SetMethod("setProxy", &Session::SetProxy) - .SetMethod("setDownloadPath", &Session::SetDownloadPath) - .SetMethod("enableNetworkEmulation", &Session::EnableNetworkEmulation) - .SetMethod("disableNetworkEmulation", &Session::DisableNetworkEmulation) - .SetMethod("setCertificateVerifyProc", &Session::SetCertVerifyProc) - .SetProperty("cookies", &Session::Cookies); +v8::Local Session::WebRequest(v8::Isolate* isolate) { + if (web_request_.IsEmpty()) { + auto handle = atom::api::WebRequest::Create(isolate, browser_context()); + web_request_.Reset(isolate, handle.ToV8()); + } + return v8::Local::New(isolate, web_request_); } // static @@ -410,6 +397,23 @@ mate::Handle Session::FromPartition( static_cast(browser_context.get())); } +// static +void Session::BuildPrototype(v8::Isolate* isolate, + v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) + .MakeDestroyable() + .SetMethod("resolveProxy", &Session::ResolveProxy) + .SetMethod("clearCache", &Session::ClearCache) + .SetMethod("clearStorageData", &Session::ClearStorageData) + .SetMethod("setProxy", &Session::SetProxy) + .SetMethod("setDownloadPath", &Session::SetDownloadPath) + .SetMethod("enableNetworkEmulation", &Session::EnableNetworkEmulation) + .SetMethod("disableNetworkEmulation", &Session::DisableNetworkEmulation) + .SetMethod("setCertificateVerifyProc", &Session::SetCertVerifyProc) + .SetProperty("cookies", &Session::Cookies) + .SetProperty("webRequest", &Session::WebRequest); +} + void ClearWrapSession() { g_wrap_session.Reset(); } diff --git a/atom/browser/api/atom_api_session.h b/atom/browser/api/atom_api_session.h index 01dc0a408..0034b1480 100644 --- a/atom/browser/api/atom_api_session.h +++ b/atom/browser/api/atom_api_session.h @@ -48,6 +48,10 @@ class Session: public mate::TrackableObject, AtomBrowserContext* browser_context() const { return browser_context_.get(); } + // mate::TrackableObject: + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + protected: explicit Session(AtomBrowserContext* browser_context); ~Session(); @@ -56,15 +60,7 @@ class Session: public mate::TrackableObject, void OnDownloadCreated(content::DownloadManager* manager, content::DownloadItem* item) override; - // mate::Wrappable: - mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) override; - bool IsDestroyed() const override; - private: - // mate::TrackableObject: - void Destroy() override; - void ResolveProxy(const GURL& url, ResolveProxyCallback callback); void ClearCache(const net::CompletionCallback& callback); void ClearStorageData(mate::Arguments* args); @@ -74,9 +70,11 @@ class Session: public mate::TrackableObject, void DisableNetworkEmulation(); void SetCertVerifyProc(v8::Local proc, mate::Arguments* args); v8::Local Cookies(v8::Isolate* isolate); + v8::Local WebRequest(v8::Isolate* isolate); - // Cached object for cookies API. + // Cached object. v8::Global cookies_; + v8::Global web_request_; scoped_refptr browser_context_; diff --git a/atom/browser/api/atom_api_tray.cc b/atom/browser/api/atom_api_tray.cc index 5e32657f0..5f351f485 100644 --- a/atom/browser/api/atom_api_tray.cc +++ b/atom/browser/api/atom_api_tray.cc @@ -94,14 +94,6 @@ void Tray::OnDragEnded() { Emit("drag-end"); } -bool Tray::IsDestroyed() const { - return !tray_icon_; -} - -void Tray::Destroy() { - tray_icon_.reset(); -} - void Tray::SetImage(mate::Arguments* args, const gfx::Image& image) { tray_icon_->SetImage(image); } @@ -137,9 +129,11 @@ void Tray::DisplayBalloon(mate::Arguments* args, } void Tray::PopUpContextMenu(mate::Arguments* args) { + mate::Handle menu; + args->GetNext(&menu); gfx::Point pos; args->GetNext(&pos); - tray_icon_->PopUpContextMenu(pos); + tray_icon_->PopUpContextMenu(pos, menu.IsEmpty() ? nullptr : menu->model()); } void Tray::SetContextMenu(mate::Arguments* args, Menu* menu) { @@ -160,8 +154,7 @@ v8::Local Tray::ModifiersToObject(v8::Isolate* isolate, void Tray::BuildPrototype(v8::Isolate* isolate, v8::Local prototype) { mate::ObjectTemplateBuilder(isolate, prototype) - .SetMethod("destroy", &Tray::Destroy, true) - .SetMethod("isDestroyed", &Tray::IsDestroyed, true) + .MakeDestroyable() .SetMethod("setImage", &Tray::SetImage) .SetMethod("setPressedImage", &Tray::SetPressedImage) .SetMethod("setToolTip", &Tray::SetToolTip) diff --git a/atom/browser/api/atom_api_tray.h b/atom/browser/api/atom_api_tray.h index d8d6dcead..0e0d153ad 100644 --- a/atom/browser/api/atom_api_tray.h +++ b/atom/browser/api/atom_api_tray.h @@ -54,12 +54,6 @@ class Tray : public mate::TrackableObject, void OnDragExited() override; void OnDragEnded() override; - // mate::Wrappable: - bool IsDestroyed() const override; - - // mate::TrackableObject: - void Destroy() override; - void SetImage(mate::Arguments* args, const gfx::Image& image); void SetPressedImage(mate::Arguments* args, const gfx::Image& image); void SetToolTip(mate::Arguments* args, const std::string& tool_tip); diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 066ca9cc7..3f39519a3 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -5,6 +5,7 @@ #include "atom/browser/api/atom_api_web_contents.h" #include +#include #include "atom/browser/api/atom_api_session.h" #include "atom/browser/api/atom_api_window.h" @@ -148,7 +149,7 @@ struct Converter { std::string key; std::string value; while (headers->EnumerateHeaderLines(&iter, &key, &value)) { - key = base::StringToLowerASCII(key); + key = base::ToLowerASCII(key); if (response_headers.HasKey(key)) { base::ListValue* values = nullptr; if (response_headers.GetList(key, &values)) @@ -171,7 +172,7 @@ struct Converter { std::string save_type; if (!ConvertFromV8(isolate, val, &save_type)) return false; - save_type = base::StringToLowerASCII(save_type); + save_type = base::ToLowerASCII(save_type); if (save_type == "htmlonly") { *out = content::SAVE_PAGE_TYPE_AS_ONLY_HTML; } else if (save_type == "htmlcomplete") { @@ -194,8 +195,6 @@ namespace api { namespace { -v8::Persistent template_; - // The wrapWebContents function which is implemented in JavaScript using WrapWebContentsCallback = base::Callback)>; WrapWebContentsCallback g_wrap_web_contents; @@ -225,7 +224,8 @@ WebContents::WebContents(content::WebContents* web_contents) } WebContents::WebContents(v8::Isolate* isolate, - const mate::Dictionary& options) { + const mate::Dictionary& options) + : request_id_(0) { // Whether it is a guest WebContents. bool is_guest = false; options.Get("isGuest", &is_guest); @@ -290,7 +290,15 @@ WebContents::WebContents(v8::Isolate* isolate, } WebContents::~WebContents() { - Destroy(); + if (type_ == WEB_VIEW && managed_web_contents()) { + // When force destroying the "destroyed" event is not emitted. + WebContentsDestroyed(); + + guest_delegate_->Destroy(); + + Observe(nullptr); + DestroyWebContents(); + } } bool WebContents::AddMessageToConsole(content::WebContents* source, @@ -414,6 +422,34 @@ bool WebContents::HandleContextMenu(const content::ContextMenuParams& params) { return true; } +bool WebContents::OnGoToEntryOffset(int offset) { + GoToOffset(offset); + return false; +} + +void WebContents::FindReply(content::WebContents* web_contents, + int request_id, + int number_of_matches, + const gfx::Rect& selection_rect, + int active_match_ordinal, + bool final_update) { + v8::Locker locker(isolate()); + v8::HandleScope handle_scope(isolate()); + + mate::Dictionary result = mate::Dictionary::CreateEmpty(isolate()); + if (number_of_matches == -1) { + result.Set("requestId", request_id); + result.Set("selectionArea", selection_rect); + result.Set("finalUpdate", final_update); + Emit("found-in-page", result); + } else if (final_update) { + result.Set("requestId", request_id); + result.Set("matches", number_of_matches); + result.Set("finalUpdate", final_update); + Emit("found-in-page", result); + } +} + void WebContents::BeforeUnloadFired(const base::TimeTicks& proceed_time) { // Do nothing, we override this method just to avoid compilation error since // there are two virtual functions named BeforeUnloadFired. @@ -445,6 +481,22 @@ void WebContents::PluginCrashed(const base::FilePath& plugin_path, Emit("plugin-crashed", info.name, info.version); } +void WebContents::MediaStartedPlaying() { + Emit("media-started-playing"); +} + +void WebContents::MediaPaused() { + Emit("media-paused"); +} + +void WebContents::DidChangeThemeColor(SkColor theme_color) { + std::string hex_theme_color = base::StringPrintf("#%02X%02X%02X", + SkColorGetR(theme_color), + SkColorGetG(theme_color), + SkColorGetB(theme_color)); + Emit("did-change-theme-color", hex_theme_color); +} + void WebContents::DocumentLoadedInFrame( content::RenderFrameHost* render_frame_host) { if (!render_frame_host->GetParent()) @@ -460,14 +512,13 @@ void WebContents::DidFinishLoad(content::RenderFrameHost* render_frame_host, Emit("did-finish-load"); } -// this error occurs when host could not be found void WebContents::DidFailProvisionalLoad( content::RenderFrameHost* render_frame_host, - const GURL& validated_url, + const GURL& url, int error_code, const base::string16& error_description, bool was_ignored_by_handler) { - Emit("did-fail-load", error_code, error_description, validated_url); + Emit("did-fail-provisional-load", error_code, error_description, url); } void WebContents::DidFailLoad(content::RenderFrameHost* render_frame_host, @@ -515,14 +566,17 @@ void WebContents::DidNavigateMainFrame( const content::LoadCommittedDetails& details, const content::FrameNavigateParams& params) { if (details.is_navigation_to_different_page()) - Emit("did-navigate-to-different-page"); + Emit("did-navigate", params.url); + else if (details.is_in_page) + Emit("did-navigate-in-page", params.url); } void WebContents::TitleWasSet(content::NavigationEntry* entry, bool explicit_set) { - // Back/Forward navigation may have pruned entries. if (entry) - Emit("page-title-set", entry->GetTitle(), explicit_set); + Emit("-page-title-updated", entry->GetTitle(), explicit_set); + else + Emit("-page-title-updated", "", explicit_set); } void WebContents::DidUpdateFaviconURL( @@ -591,19 +645,6 @@ void WebContents::NavigationEntryCommitted( details.is_in_page, details.did_replace_entry); } -void WebContents::Destroy() { - session_.Reset(); - if (type_ == WEB_VIEW && managed_web_contents()) { - // When force destroying the "destroyed" event is not emitted. - WebContentsDestroyed(); - - guest_delegate_->Destroy(); - - Observe(nullptr); - DestroyWebContents(); - } -} - int WebContents::GetID() const { return web_contents()->GetRenderProcessHost()->GetID(); } @@ -634,6 +675,15 @@ void WebContents::LoadURL(const GURL& url, const mate::Dictionary& options) { web_contents()->GetController().LoadURLWithParams(params); } +void WebContents::DownloadURL(const GURL& url) { + auto browser_context = web_contents()->GetBrowserContext(); + auto download_manager = + content::BrowserContext::GetDownloadManager(browser_context); + + download_manager->DownloadUrl( + content::DownloadUrlParameters::FromWebContents(web_contents(), url)); +} + GURL WebContents::GetURL() const { return web_contents()->GetURL(); } @@ -732,6 +782,13 @@ bool WebContents::IsDevToolsOpened() { return managed_web_contents()->IsDevToolsViewShowing(); } +bool WebContents::IsDevToolsFocused() { + if (type_ == REMOTE) + return false; + + return managed_web_contents()->GetView()->IsDevToolsViewFocused(); +} + void WebContents::EnableDeviceEmulation( const blink::WebDeviceEmulationParams& params) { if (type_ == REMOTE) @@ -886,6 +943,25 @@ void WebContents::ReplaceMisspelling(const base::string16& word) { web_contents()->ReplaceMisspelling(word); } +uint32 WebContents::FindInPage(mate::Arguments* args) { + uint32 request_id = GetNextRequestId(); + base::string16 search_text; + blink::WebFindOptions options; + if (!args->GetNext(&search_text) || search_text.empty()) { + args->ThrowError("Must provide a non-empty search content"); + return 0; + } + + args->GetNext(&options); + + web_contents()->Find(request_id, search_text, options); + return request_id; +} + +void WebContents::StopFindInPage(content::StopFindAction action) { + web_contents()->StopFinding(action); +} + void WebContents::Focus() { web_contents()->Focus(); } @@ -987,82 +1063,76 @@ v8::Local WebContents::DevToolsWebContents(v8::Isolate* isolate) { return v8::Local::New(isolate, devtools_web_contents_); } -mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder( - v8::Isolate* isolate) { - if (template_.IsEmpty()) - template_.Reset(isolate, mate::ObjectTemplateBuilder(isolate) - .SetMethod("destroy", &WebContents::Destroy, true) - .SetMethod("isDestroyed", &WebContents::IsDestroyed, true) - .SetMethod("getId", &WebContents::GetID) - .SetMethod("equal", &WebContents::Equal) - .SetMethod("_loadURL", &WebContents::LoadURL) - .SetMethod("_getURL", &WebContents::GetURL) - .SetMethod("getTitle", &WebContents::GetTitle) - .SetMethod("isLoading", &WebContents::IsLoading) - .SetMethod("isWaitingForResponse", &WebContents::IsWaitingForResponse) - .SetMethod("_stop", &WebContents::Stop) - .SetMethod("_goBack", &WebContents::GoBack) - .SetMethod("_goForward", &WebContents::GoForward) - .SetMethod("_goToOffset", &WebContents::GoToOffset) - .SetMethod("isCrashed", &WebContents::IsCrashed) - .SetMethod("setUserAgent", &WebContents::SetUserAgent) - .SetMethod("getUserAgent", &WebContents::GetUserAgent) - .SetMethod("insertCSS", &WebContents::InsertCSS) - .SetMethod("savePage", &WebContents::SavePage) - .SetMethod("_executeJavaScript", &WebContents::ExecuteJavaScript) - .SetMethod("openDevTools", &WebContents::OpenDevTools) - .SetMethod("closeDevTools", &WebContents::CloseDevTools) - .SetMethod("isDevToolsOpened", &WebContents::IsDevToolsOpened) - .SetMethod("enableDeviceEmulation", - &WebContents::EnableDeviceEmulation) - .SetMethod("disableDeviceEmulation", - &WebContents::DisableDeviceEmulation) - .SetMethod("toggleDevTools", &WebContents::ToggleDevTools) - .SetMethod("inspectElement", &WebContents::InspectElement) - .SetMethod("setAudioMuted", &WebContents::SetAudioMuted) - .SetMethod("isAudioMuted", &WebContents::IsAudioMuted) - .SetMethod("undo", &WebContents::Undo) - .SetMethod("redo", &WebContents::Redo) - .SetMethod("cut", &WebContents::Cut) - .SetMethod("copy", &WebContents::Copy) - .SetMethod("paste", &WebContents::Paste) - .SetMethod("pasteAndMatchStyle", &WebContents::PasteAndMatchStyle) - .SetMethod("delete", &WebContents::Delete) - .SetMethod("selectAll", &WebContents::SelectAll) - .SetMethod("unselect", &WebContents::Unselect) - .SetMethod("replace", &WebContents::Replace) - .SetMethod("replaceMisspelling", &WebContents::ReplaceMisspelling) - .SetMethod("focus", &WebContents::Focus) - .SetMethod("tabTraverse", &WebContents::TabTraverse) - .SetMethod("_send", &WebContents::SendIPCMessage, true) - .SetMethod("sendInputEvent", &WebContents::SendInputEvent) - .SetMethod("beginFrameSubscription", - &WebContents::BeginFrameSubscription) - .SetMethod("endFrameSubscription", &WebContents::EndFrameSubscription) - .SetMethod("setSize", &WebContents::SetSize) - .SetMethod("setAllowTransparency", &WebContents::SetAllowTransparency) - .SetMethod("isGuest", &WebContents::IsGuest) - .SetMethod("getWebPreferences", &WebContents::GetWebPreferences) - .SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow) - .SetMethod("hasServiceWorker", &WebContents::HasServiceWorker) - .SetMethod("unregisterServiceWorker", - &WebContents::UnregisterServiceWorker) - .SetMethod("inspectServiceWorker", &WebContents::InspectServiceWorker) - .SetMethod("print", &WebContents::Print) - .SetMethod("_printToPDF", &WebContents::PrintToPDF) - .SetMethod("addWorkSpace", &WebContents::AddWorkSpace) - .SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace) - .SetProperty("session", &WebContents::Session, true) - .SetProperty("devToolsWebContents", - &WebContents::DevToolsWebContents, true) - .Build()); - - return mate::ObjectTemplateBuilder( - isolate, v8::Local::New(isolate, template_)); -} - -bool WebContents::IsDestroyed() const { - return !web_contents(); +// static +void WebContents::BuildPrototype(v8::Isolate* isolate, + v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) + .MakeDestroyable() + .SetMethod("getId", &WebContents::GetID) + .SetMethod("equal", &WebContents::Equal) + .SetMethod("_loadURL", &WebContents::LoadURL) + .SetMethod("downloadURL", &WebContents::DownloadURL) + .SetMethod("_getURL", &WebContents::GetURL) + .SetMethod("getTitle", &WebContents::GetTitle) + .SetMethod("isLoading", &WebContents::IsLoading) + .SetMethod("isWaitingForResponse", &WebContents::IsWaitingForResponse) + .SetMethod("_stop", &WebContents::Stop) + .SetMethod("_goBack", &WebContents::GoBack) + .SetMethod("_goForward", &WebContents::GoForward) + .SetMethod("_goToOffset", &WebContents::GoToOffset) + .SetMethod("isCrashed", &WebContents::IsCrashed) + .SetMethod("setUserAgent", &WebContents::SetUserAgent) + .SetMethod("getUserAgent", &WebContents::GetUserAgent) + .SetMethod("insertCSS", &WebContents::InsertCSS) + .SetMethod("savePage", &WebContents::SavePage) + .SetMethod("_executeJavaScript", &WebContents::ExecuteJavaScript) + .SetMethod("openDevTools", &WebContents::OpenDevTools) + .SetMethod("closeDevTools", &WebContents::CloseDevTools) + .SetMethod("isDevToolsOpened", &WebContents::IsDevToolsOpened) + .SetMethod("isDevToolsFocused", &WebContents::IsDevToolsFocused) + .SetMethod("enableDeviceEmulation", + &WebContents::EnableDeviceEmulation) + .SetMethod("disableDeviceEmulation", + &WebContents::DisableDeviceEmulation) + .SetMethod("toggleDevTools", &WebContents::ToggleDevTools) + .SetMethod("inspectElement", &WebContents::InspectElement) + .SetMethod("setAudioMuted", &WebContents::SetAudioMuted) + .SetMethod("isAudioMuted", &WebContents::IsAudioMuted) + .SetMethod("undo", &WebContents::Undo) + .SetMethod("redo", &WebContents::Redo) + .SetMethod("cut", &WebContents::Cut) + .SetMethod("copy", &WebContents::Copy) + .SetMethod("paste", &WebContents::Paste) + .SetMethod("pasteAndMatchStyle", &WebContents::PasteAndMatchStyle) + .SetMethod("delete", &WebContents::Delete) + .SetMethod("selectAll", &WebContents::SelectAll) + .SetMethod("unselect", &WebContents::Unselect) + .SetMethod("replace", &WebContents::Replace) + .SetMethod("replaceMisspelling", &WebContents::ReplaceMisspelling) + .SetMethod("findInPage", &WebContents::FindInPage) + .SetMethod("stopFindInPage", &WebContents::StopFindInPage) + .SetMethod("focus", &WebContents::Focus) + .SetMethod("tabTraverse", &WebContents::TabTraverse) + .SetMethod("_send", &WebContents::SendIPCMessage) + .SetMethod("sendInputEvent", &WebContents::SendInputEvent) + .SetMethod("beginFrameSubscription", + &WebContents::BeginFrameSubscription) + .SetMethod("endFrameSubscription", &WebContents::EndFrameSubscription) + .SetMethod("setSize", &WebContents::SetSize) + .SetMethod("setAllowTransparency", &WebContents::SetAllowTransparency) + .SetMethod("isGuest", &WebContents::IsGuest) + .SetMethod("getWebPreferences", &WebContents::GetWebPreferences) + .SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow) + .SetMethod("hasServiceWorker", &WebContents::HasServiceWorker) + .SetMethod("unregisterServiceWorker", + &WebContents::UnregisterServiceWorker) + .SetMethod("inspectServiceWorker", &WebContents::InspectServiceWorker) + .SetMethod("print", &WebContents::Print) + .SetMethod("_printToPDF", &WebContents::PrintToPDF) + .SetMethod("addWorkSpace", &WebContents::AddWorkSpace) + .SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace) + .SetProperty("session", &WebContents::Session) + .SetProperty("devToolsWebContents", &WebContents::DevToolsWebContents); } AtomBrowserContext* WebContents::GetBrowserContext() const { diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index 568a563e2..bd7149e38 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -54,12 +54,10 @@ class WebContents : public mate::TrackableObject, static mate::Handle Create( v8::Isolate* isolate, const mate::Dictionary& options); - // mate::TrackableObject: - void Destroy() override; - int GetID() const; bool Equal(const WebContents* web_contents) const; void LoadURL(const GURL& url, const mate::Dictionary& options); + void DownloadURL(const GURL& url); GURL GetURL() const; base::string16 GetTitle() const; bool IsLoading() const; @@ -81,6 +79,7 @@ class WebContents : public mate::TrackableObject, void OpenDevTools(mate::Arguments* args); void CloseDevTools(); bool IsDevToolsOpened(); + bool IsDevToolsFocused(); void ToggleDevTools(); void EnableDeviceEmulation(const blink::WebDeviceEmulationParams& params); void DisableDeviceEmulation(); @@ -112,6 +111,8 @@ class WebContents : public mate::TrackableObject, void Unselect(); void Replace(const base::string16& word); void ReplaceMisspelling(const base::string16& word); + uint32 FindInPage(mate::Arguments* args); + void StopFindInPage(content::StopFindAction action); // Focus. void Focus(); @@ -144,16 +145,15 @@ class WebContents : public mate::TrackableObject, v8::Local Session(v8::Isolate* isolate); v8::Local DevToolsWebContents(v8::Isolate* isolate); + // mate::TrackableObject: + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + protected: explicit WebContents(content::WebContents* web_contents); WebContents(v8::Isolate* isolate, const mate::Dictionary& options); ~WebContents(); - // mate::Wrappable: - mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) override; - bool IsDestroyed() const override; - // content::WebContentsDelegate: bool AddMessageToConsole(content::WebContents* source, int32 level, @@ -189,6 +189,13 @@ class WebContents : public mate::TrackableObject, void RendererUnresponsive(content::WebContents* source) override; void RendererResponsive(content::WebContents* source) override; bool HandleContextMenu(const content::ContextMenuParams& params) override; + bool OnGoToEntryOffset(int offset) override; + void FindReply(content::WebContents* web_contents, + int request_id, + int number_of_matches, + const gfx::Rect& selection_rect, + int active_match_ordinal, + bool final_update) override; // content::WebContentsObserver: void BeforeUnloadFired(const base::TimeTicks& proceed_time) override; @@ -227,6 +234,9 @@ class WebContents : public mate::TrackableObject, const std::vector& urls) override; void PluginCrashed(const base::FilePath& plugin_path, base::ProcessId plugin_pid) override; + void MediaStartedPlaying() override; + void MediaPaused() override; + void DidChangeThemeColor(SkColor theme_color) override; // brightray::InspectableWebContentsViewDelegate: void DevToolsFocused() override; @@ -242,6 +252,10 @@ class WebContents : public mate::TrackableObject, AtomBrowserContext* GetBrowserContext() const; + uint32 GetNextRequestId() { + return ++request_id_; + } + // Called when received a message from renderer. void OnRendererMessage(const base::string16& channel, const base::ListValue& args); @@ -263,6 +277,9 @@ class WebContents : public mate::TrackableObject, // The type of current WebContents. Type type_; + // Request id used for findInPage request. + uint32 request_id_; + DISALLOW_COPY_AND_ASSIGN(WebContents); }; diff --git a/atom/browser/api/atom_api_web_request.cc b/atom/browser/api/atom_api_web_request.cc new file mode 100644 index 000000000..a987369ed --- /dev/null +++ b/atom/browser/api/atom_api_web_request.cc @@ -0,0 +1,119 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/api/atom_api_web_request.h" + +#include + +#include "atom/browser/atom_browser_context.h" +#include "atom/browser/net/atom_network_delegate.h" +#include "atom/common/native_mate_converters/callback.h" +#include "atom/common/native_mate_converters/net_converter.h" +#include "atom/common/native_mate_converters/value_converter.h" +#include "content/public/browser/browser_thread.h" +#include "native_mate/dictionary.h" +#include "native_mate/object_template_builder.h" + +using content::BrowserThread; + +namespace mate { + +template<> +struct Converter { + static bool FromV8(v8::Isolate* isolate, v8::Local val, + extensions::URLPattern* out) { + std::string pattern; + if (!ConvertFromV8(isolate, val, &pattern)) + return false; + return out->Parse(pattern) == extensions::URLPattern::PARSE_SUCCESS; + } +}; + +} // namespace mate + +namespace atom { + +namespace api { + +WebRequest::WebRequest(AtomBrowserContext* browser_context) + : browser_context_(browser_context) { +} + +WebRequest::~WebRequest() { +} + +template +void WebRequest::SetSimpleListener(mate::Arguments* args) { + SetListener( + &AtomNetworkDelegate::SetSimpleListenerInIO, type, args); +} + +template +void WebRequest::SetResponseListener(mate::Arguments* args) { + SetListener( + &AtomNetworkDelegate::SetResponseListenerInIO, type, args); +} + +template +void WebRequest::SetListener(Method method, Event type, mate::Arguments* args) { + // { urls }. + URLPatterns patterns; + mate::Dictionary dict; + args->GetNext(&dict) && dict.Get("urls", &patterns); + + // Function or null. + v8::Local value; + Listener listener; + if (!args->GetNext(&listener) && + !(args->GetNext(&value) && value->IsNull())) { + args->ThrowError("Must pass null or a Function"); + return; + } + + auto delegate = browser_context_->network_delegate(); + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, + base::Bind(method, base::Unretained(delegate), type, + patterns, listener)); +} + +// static +mate::Handle WebRequest::Create( + v8::Isolate* isolate, + AtomBrowserContext* browser_context) { + return mate::CreateHandle(isolate, new WebRequest(browser_context)); +} + +// static +void WebRequest::BuildPrototype(v8::Isolate* isolate, + v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) + .SetMethod("onBeforeRequest", + &WebRequest::SetResponseListener< + AtomNetworkDelegate::kOnBeforeRequest>) + .SetMethod("onBeforeSendHeaders", + &WebRequest::SetResponseListener< + AtomNetworkDelegate::kOnBeforeSendHeaders>) + .SetMethod("onHeadersReceived", + &WebRequest::SetResponseListener< + AtomNetworkDelegate::kOnHeadersReceived>) + .SetMethod("onSendHeaders", + &WebRequest::SetSimpleListener< + AtomNetworkDelegate::kOnSendHeaders>) + .SetMethod("onBeforeRedirect", + &WebRequest::SetSimpleListener< + AtomNetworkDelegate::kOnBeforeRedirect>) + .SetMethod("onResponseStarted", + &WebRequest::SetSimpleListener< + AtomNetworkDelegate::kOnResponseStarted>) + .SetMethod("onCompleted", + &WebRequest::SetSimpleListener< + AtomNetworkDelegate::kOnCompleted>) + .SetMethod("onErrorOccurred", + &WebRequest::SetSimpleListener< + AtomNetworkDelegate::kOnErrorOccurred>); +} + +} // namespace api + +} // namespace atom diff --git a/atom/browser/api/atom_api_web_request.h b/atom/browser/api/atom_api_web_request.h new file mode 100644 index 000000000..9a6e17a04 --- /dev/null +++ b/atom/browser/api/atom_api_web_request.h @@ -0,0 +1,50 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_API_ATOM_API_WEB_REQUEST_H_ +#define ATOM_BROWSER_API_ATOM_API_WEB_REQUEST_H_ + +#include "atom/browser/api/trackable_object.h" +#include "atom/browser/net/atom_network_delegate.h" +#include "native_mate/arguments.h" +#include "native_mate/handle.h" + +namespace atom { + +class AtomBrowserContext; + +namespace api { + +class WebRequest : public mate::TrackableObject { + public: + static mate::Handle Create(v8::Isolate* isolate, + AtomBrowserContext* browser_context); + + // mate::TrackableObject: + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + + protected: + explicit WebRequest(AtomBrowserContext* browser_context); + ~WebRequest(); + + // C++ can not distinguish overloaded member function. + template + void SetSimpleListener(mate::Arguments* args); + template + void SetResponseListener(mate::Arguments* args); + template + void SetListener(Method method, Event type, mate::Arguments* args); + + private: + scoped_refptr browser_context_; + + DISALLOW_COPY_AND_ASSIGN(WebRequest); +}; + +} // namespace api + +} // namespace atom + +#endif // ATOM_BROWSER_API_ATOM_API_WEB_REQUEST_H_ diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index 7f5b78a79..222f2cae0 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -157,13 +157,12 @@ Window::Window(v8::Isolate* isolate, const mate::Dictionary& options) { } Window::~Window() { - if (window_) - Destroy(); -} + if (!window_->IsClosed()) + window_->CloseContents(nullptr); -void Window::OnPageTitleUpdated(bool* prevent_default, - const std::string& title) { - *prevent_default = Emit("page-title-updated", title); + // Destroy the native window in next tick because the native code might be + // iterating all windows. + base::MessageLoop::current()->DeleteSoon(FROM_HERE, window_.release()); } void Window::WillCloseWindow(bool* prevent_default) { @@ -171,19 +170,19 @@ void Window::WillCloseWindow(bool* prevent_default) { } void Window::OnWindowClosed() { - if (api_web_contents_) { - api_web_contents_->DestroyWebContents(); - api_web_contents_ = nullptr; - web_contents_.Reset(); - } + api_web_contents_->DestroyWebContents(); RemoveFromWeakMap(); window_->RemoveObserver(this); + // We can not call Destroy here because we need to call Emit first, but we + // also do not want any method to be used, so just mark as destroyed here. + MarkDestroyed(); + Emit("closed"); - // Clean up the resources after window has been closed. - base::MessageLoop::current()->DeleteSoon(FROM_HERE, window_.release()); + // Destroy the native class when window is closed. + base::MessageLoop::current()->PostTask(FROM_HERE, GetDestroyClosure()); } void Window::OnWindowBlur() { @@ -261,25 +260,26 @@ void Window::OnWindowMessage(UINT message, WPARAM w_param, LPARAM l_param) { #endif // static -mate::Wrappable* Window::New(v8::Isolate* isolate, - const mate::Dictionary& options) { +mate::Wrappable* Window::New(v8::Isolate* isolate, mate::Arguments* args) { if (!Browser::Get()->is_ready()) { isolate->ThrowException(v8::Exception::Error(mate::StringToV8( isolate, "Cannot create BrowserWindow before app is ready"))); return nullptr; } + + if (args->Length() > 1) { + args->ThrowError(); + return nullptr; + } + + mate::Dictionary options; + if (!(args->Length() == 1 && args->GetNext(&options))) { + options = mate::Dictionary::CreateEmpty(isolate); + } + return new Window(isolate, options); } -bool Window::IsDestroyed() const { - return !window_ || window_->IsClosed(); -} - -void Window::Destroy() { - if (window_) - window_->CloseContents(nullptr); -} - void Window::Close() { window_->Close(); } @@ -468,10 +468,6 @@ bool Window::IsWebViewFocused() { return window_->IsWebViewFocused(); } -bool Window::IsDevToolsFocused() { - return window_->IsDevToolsFocused(); -} - void Window::SetRepresentedFilename(const std::string& filename) { window_->SetRepresentedFilename(filename); } @@ -488,6 +484,10 @@ bool Window::IsDocumentEdited() { return window_->IsDocumentEdited(); } +void Window::SetIgnoreMouseEvents(bool ignore) { + return window_->SetIgnoreMouseEvents(ignore); +} + void Window::CapturePage(mate::Arguments* args) { gfx::Rect rect; base::Callback callback; @@ -617,8 +617,7 @@ v8::Local Window::WebContents(v8::Isolate* isolate) { void Window::BuildPrototype(v8::Isolate* isolate, v8::Local prototype) { mate::ObjectTemplateBuilder(isolate, prototype) - .SetMethod("destroy", &Window::Destroy, true) - .SetMethod("isDestroyed", &Window::IsDestroyed, true) + .MakeDestroyable() .SetMethod("close", &Window::Close) .SetMethod("focus", &Window::Focus) .SetMethod("isFocused", &Window::IsFocused) @@ -663,10 +662,10 @@ void Window::BuildPrototype(v8::Isolate* isolate, .SetMethod("getRepresentedFilename", &Window::GetRepresentedFilename) .SetMethod("setDocumentEdited", &Window::SetDocumentEdited) .SetMethod("isDocumentEdited", &Window::IsDocumentEdited) + .SetMethod("setIgnoreMouseEvents", &Window::SetIgnoreMouseEvents) .SetMethod("focusOnWebView", &Window::FocusOnWebView) .SetMethod("blurWebView", &Window::BlurWebView) .SetMethod("isWebViewFocused", &Window::IsWebViewFocused) - .SetMethod("isDevToolsFocused", &Window::IsDevToolsFocused) .SetMethod("capturePage", &Window::CapturePage) .SetMethod("setProgressBar", &Window::SetProgressBar) .SetMethod("setOverlayIcon", &Window::SetOverlayIcon) @@ -690,8 +689,8 @@ void Window::BuildPrototype(v8::Isolate* isolate, .SetMethod("showDefinitionForSelection", &Window::ShowDefinitionForSelection) #endif - .SetProperty("id", &Window::ID, true) - .SetProperty("webContents", &Window::WebContents, true); + .SetProperty("id", &Window::ID) + .SetProperty("webContents", &Window::WebContents); } // static diff --git a/atom/browser/api/atom_api_window.h b/atom/browser/api/atom_api_window.h index 416158420..9297b2fe7 100644 --- a/atom/browser/api/atom_api_window.h +++ b/atom/browser/api/atom_api_window.h @@ -38,8 +38,7 @@ class WebContents; class Window : public mate::TrackableObject, public NativeWindowObserver { public: - static mate::Wrappable* New(v8::Isolate* isolate, - const mate::Dictionary& options); + static mate::Wrappable* New(v8::Isolate* isolate, mate::Arguments* args); static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); @@ -55,8 +54,6 @@ class Window : public mate::TrackableObject, virtual ~Window(); // NativeWindowObserver: - void OnPageTitleUpdated(bool* prevent_default, - const std::string& title) override; void WillCloseWindow(bool* prevent_default) override; void OnWindowClosed() override; void OnWindowBlur() override; @@ -80,13 +77,7 @@ class Window : public mate::TrackableObject, void OnWindowMessage(UINT message, WPARAM w_param, LPARAM l_param) override; #endif - // mate::Wrappable: - bool IsDestroyed() const override; - private: - // mate::TrackableObject: - void Destroy() override; - // APIs for NativeWindow. void Close(); void Focus(); @@ -130,11 +121,11 @@ class Window : public mate::TrackableObject, void FocusOnWebView(); void BlurWebView(); bool IsWebViewFocused(); - bool IsDevToolsFocused(); void SetRepresentedFilename(const std::string& filename); std::string GetRepresentedFilename(); void SetDocumentEdited(bool edited); bool IsDocumentEdited(); + void SetIgnoreMouseEvents(bool ignore); void CapturePage(mate::Arguments* args); void SetProgressBar(double progress); void SetOverlayIcon(const gfx::Image& overlay, diff --git a/atom/browser/api/frame_subscriber.cc b/atom/browser/api/frame_subscriber.cc index cf0eae14a..5b7241486 100644 --- a/atom/browser/api/frame_subscriber.cc +++ b/atom/browser/api/frame_subscriber.cc @@ -24,12 +24,11 @@ bool FrameSubscriber::ShouldCaptureFrame( base::TimeTicks present_time, scoped_refptr* storage, DeliverFrameCallback* callback) { - *storage = media::VideoFrame::CreateFrame(media::VideoFrame::YV12, size_, - gfx::Rect(size_), size_, - base::TimeDelta()); + *storage = media::VideoFrame::CreateFrame( + media::PIXEL_FORMAT_YV12, + size_, gfx::Rect(size_), size_, base::TimeDelta()); *callback = base::Bind(&FrameSubscriber::OnFrameDelivered, - base::Unretained(this), - *storage); + base::Unretained(this), *storage); return true; } diff --git a/atom/browser/api/lib/app.coffee b/atom/browser/api/lib/app.coffee index a2fdb847e..d0ec41c4d 100644 --- a/atom/browser/api/lib/app.coffee +++ b/atom/browser/api/lib/app.coffee @@ -54,7 +54,7 @@ deprecate.event app, 'finish-launching', 'ready', -> setImmediate => # give default app a chance to setup default menu. @emit 'finish-launching' deprecate.event app, 'activate-with-no-open-windows', 'activate', (event, hasVisibleWindows) -> - @emit 'activate-with-no-open-windows' if not hasVisibleWindows + @emit 'activate-with-no-open-windows', event if not hasVisibleWindows deprecate.event app, 'select-certificate', 'select-client-certificate' # Wrappers for native classes. @@ -65,7 +65,6 @@ wrapDownloadItem = (downloadItem) -> deprecate.property downloadItem, 'url', 'getURL' deprecate.property downloadItem, 'filename', 'getFilename' deprecate.property downloadItem, 'mimeType', 'getMimeType' - deprecate.property downloadItem, 'hasUserGesture', 'hasUserGesture' deprecate.rename downloadItem, 'getUrl', 'getURL' downloadItemBindings._setWrapDownloadItem wrapDownloadItem diff --git a/atom/browser/api/lib/browser-window.coffee b/atom/browser/api/lib/browser-window.coffee index 4cdffae87..92a230ba4 100644 --- a/atom/browser/api/lib/browser-window.coffee +++ b/atom/browser/api/lib/browser-window.coffee @@ -31,6 +31,11 @@ BrowserWindow::_init = -> @webContents.on 'crashed', => @emit 'crashed' + # Change window title to page title. + @webContents.on 'page-title-updated', (event, title, explicitSet) => + @emit 'page-title-updated', event, title + @setTitle title unless event.defaultPrevented + # Sometimes the webContents doesn't get focus when window is shown, so we have # to force focusing on webContents in this case. The safest way is to focus it # when we first start to load URL, if we do it earlier it won't have effect, @@ -78,6 +83,7 @@ BrowserWindow::send = -> @webContents.send.apply @webContents, arguments BrowserWindow::openDevTools = -> @webContents.openDevTools.apply @webContents, arguments BrowserWindow::closeDevTools = -> @webContents.closeDevTools() BrowserWindow::isDevToolsOpened = -> @webContents.isDevToolsOpened() +BrowserWindow::isDevToolsFocused = -> @webContents.isDevToolsFocused() BrowserWindow::toggleDevTools = -> @webContents.toggleDevTools() BrowserWindow::inspectElement = -> @webContents.inspectElement.apply @webContents, arguments BrowserWindow::inspectServiceWorker = -> @webContents.inspectServiceWorker() @@ -90,16 +96,18 @@ deprecate.member BrowserWindow, 'copy', 'webContents' deprecate.member BrowserWindow, 'paste', 'webContents' deprecate.member BrowserWindow, 'selectAll', 'webContents' deprecate.member BrowserWindow, 'reloadIgnoringCache', 'webContents' -deprecate.member BrowserWindow, 'getPageTitle', 'webContents' deprecate.member BrowserWindow, 'isLoading', 'webContents' deprecate.member BrowserWindow, 'isWaitingForResponse', 'webContents' deprecate.member BrowserWindow, 'stop', 'webContents' deprecate.member BrowserWindow, 'isCrashed', 'webContents' -deprecate.member BrowserWindow, 'executeJavaScriptInDevTools', 'webContents' deprecate.member BrowserWindow, 'print', 'webContents' deprecate.member BrowserWindow, 'printToPDF', 'webContents' deprecate.rename BrowserWindow, 'restart', 'reload' deprecate.rename BrowserWindow, 'loadUrl', 'loadURL' deprecate.rename BrowserWindow, 'getUrl', 'getURL' +BrowserWindow::executeJavaScriptInDevTools = deprecate 'executeJavaScriptInDevTools', 'devToolsWebContents.executeJavaScript', (code) -> + @devToolsWebContents?.executeJavaScript code +BrowserWindow::getPageTitle = deprecate 'getPageTitle', 'webContents.getTitle', -> + @webContents?.getTitle() module.exports = BrowserWindow diff --git a/atom/browser/api/lib/exports/electron.coffee b/atom/browser/api/lib/exports/electron.coffee index 3f7d9b1a1..9c61a5507 100644 --- a/atom/browser/api/lib/exports/electron.coffee +++ b/atom/browser/api/lib/exports/electron.coffee @@ -1,7 +1,9 @@ -# Import common modules. -module.exports = require '../../../../common/api/lib/exports/electron' +common = require '../../../../common/api/lib/exports/electron' -Object.defineProperties module.exports, +# Import common modules. +common.defineProperties exports + +Object.defineProperties exports, # Browser side modules, please sort with alphabet order. app: enumerable: true diff --git a/atom/browser/api/lib/ipc.coffee b/atom/browser/api/lib/ipc.coffee index 8019a385d..018cb6bb0 100644 --- a/atom/browser/api/lib/ipc.coffee +++ b/atom/browser/api/lib/ipc.coffee @@ -1,6 +1,6 @@ {deprecate, ipcMain} = require 'electron' # This module is deprecated, we mirror everything from ipcMain. -deprecate.warn 'ipc module', 'ipcMain module' +deprecate.warn 'ipc module', 'require("electron").ipcMain' module.exports = ipcMain diff --git a/atom/browser/api/lib/menu-item.coffee b/atom/browser/api/lib/menu-item.coffee index 92e2283b4..242a48f54 100644 --- a/atom/browser/api/lib/menu-item.coffee +++ b/atom/browser/api/lib/menu-item.coffee @@ -13,6 +13,11 @@ rolesMap = minimize: 'minimize' close: 'close' +# Maps methods that should be called directly on the BrowserWindow instance +methodInBrowserWindow = + minimize: true + close: true + class MenuItem @types = ['normal', 'separator', 'submenu', 'checkbox', 'radio'] @@ -21,6 +26,8 @@ class MenuItem {click, @selector, @type, @role, @label, @sublabel, @accelerator, @icon, @enabled, @visible, @checked, @submenu} = options + if @submenu? and @submenu.constructor isnt Menu + @submenu = Menu.buildFromTemplate @submenu @type = 'submenu' if not @type? and @submenu? throw new Error('Invalid submenu') if @type is 'submenu' and @submenu?.constructor isnt Menu @@ -42,8 +49,12 @@ class MenuItem # Manually flip the checked flags when clicked. @checked = !@checked if @type in ['checkbox', 'radio'] - if @role and rolesMap[@role] and process.platform isnt 'darwin' - focusedWindow?[rolesMap[@role]]() + if @role and rolesMap[@role] and process.platform isnt 'darwin' and focusedWindow? + methodName = rolesMap[@role] + if methodInBrowserWindow[methodName] + focusedWindow[methodName]() + else + focusedWindow.webContents?[methodName]() else if typeof click is 'function' click this, focusedWindow else if typeof @selector is 'string' diff --git a/atom/browser/api/lib/menu.coffee b/atom/browser/api/lib/menu.coffee index 26e2dc233..d81c345f4 100644 --- a/atom/browser/api/lib/menu.coffee +++ b/atom/browser/api/lib/menu.coffee @@ -169,9 +169,8 @@ Menu.buildFromTemplate = (template) -> for item in positionedTemplate throw new TypeError('Invalid template for MenuItem') unless typeof item is 'object' - item.submenu = Menu.buildFromTemplate item.submenu if item.submenu? menuItem = new MenuItem(item) - menuItem[key] = value for key, value of item when not menuItem[key]? + menuItem[key] ?= value for key, value of item menu.append menuItem menu diff --git a/atom/browser/api/lib/session.coffee b/atom/browser/api/lib/session.coffee index 6abfe7925..5c65aa29c 100644 --- a/atom/browser/api/lib/session.coffee +++ b/atom/browser/api/lib/session.coffee @@ -6,6 +6,7 @@ PERSIST_PERFIX = 'persist:' # Returns the Session from |partition| string. exports.fromPartition = (partition='') -> + return exports.defaultSession if partition is '' if partition.startsWith PERSIST_PERFIX bindings.fromPartition partition.substr(PERSIST_PERFIX.length), false else @@ -14,7 +15,7 @@ exports.fromPartition = (partition='') -> # Returns the default session. Object.defineProperty exports, 'defaultSession', enumerable: true - get: -> exports.fromPartition '' + get: -> bindings.fromPartition '', false wrapSession = (session) -> # session is an EventEmitter. diff --git a/atom/browser/api/lib/web-contents.coffee b/atom/browser/api/lib/web-contents.coffee index 335928dce..2eda00f06 100644 --- a/atom/browser/api/lib/web-contents.coffee +++ b/atom/browser/api/lib/web-contents.coffee @@ -7,6 +7,11 @@ nextId = 0 getNextId = -> ++nextId PDFPageSize = + A5: + custom_display_name: "A5" + height_microns: 210000 + name: "ISO_A5" + width_microns: 148000 A4: custom_display_name: "A4" height_microns: 297000 @@ -70,9 +75,21 @@ wrapWebContents = (webContents) -> menu = Menu.buildFromTemplate params.menu menu.popup params.x, params.y + # This error occurs when host could not be found. + webContents.on 'did-fail-provisional-load', (args...) -> + # Calling loadURL during this event might cause crash, so delay the event + # until next tick. + setImmediate => @emit 'did-fail-load', args... + + # Delays the page-title-updated event to next tick. + webContents.on '-page-title-updated', (args...) -> + setImmediate => @emit 'page-title-updated', args... + # Deprecated. deprecate.rename webContents, 'loadUrl', 'loadURL' deprecate.rename webContents, 'getUrl', 'getURL' + deprecate.event webContents, 'page-title-set', 'page-title-updated', (args...) -> + @emit 'page-title-set', args... webContents.printToPDF = (options, callback) -> printingSetting = diff --git a/atom/browser/api/trackable_object.cc b/atom/browser/api/trackable_object.cc index 50bfa59e6..77a936cde 100644 --- a/atom/browser/api/trackable_object.cc +++ b/atom/browser/api/trackable_object.cc @@ -30,11 +30,11 @@ class IDUserData : public base::SupportsUserData::Data { TrackableObjectBase::TrackableObjectBase() : weak_map_id_(0), wrapped_(nullptr), weak_factory_(this) { - RegisterDestructionCallback( - base::Bind(&TrackableObjectBase::Destroy, weak_factory_.GetWeakPtr())); + cleanup_ = RegisterDestructionCallback(GetDestroyClosure()); } TrackableObjectBase::~TrackableObjectBase() { + cleanup_.Run(); } void TrackableObjectBase::AfterInit(v8::Isolate* isolate) { @@ -42,6 +42,18 @@ void TrackableObjectBase::AfterInit(v8::Isolate* isolate) { AttachAsUserData(wrapped_); } +void TrackableObjectBase::MarkDestroyed() { + GetWrapper(isolate())->SetAlignedPointerInInternalField(0, nullptr); +} + +base::Closure TrackableObjectBase::GetDestroyClosure() { + return base::Bind(&TrackableObjectBase::Destroy, weak_factory_.GetWeakPtr()); +} + +void TrackableObjectBase::Destroy() { + delete this; +} + void TrackableObjectBase::AttachAsUserData(base::SupportsUserData* wrapped) { if (weak_map_id_ != 0) { wrapped->SetUserData(kTrackedObjectKey, new IDUserData(weak_map_id_)); @@ -63,9 +75,9 @@ int32_t TrackableObjectBase::GetIDFromWrappedClass(base::SupportsUserData* w) { } // static -void TrackableObjectBase::RegisterDestructionCallback( - const base::Closure& closure) { - atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback(closure); +base::Closure TrackableObjectBase::RegisterDestructionCallback( + const base::Closure& c) { + return atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback(c); } } // namespace mate diff --git a/atom/browser/api/trackable_object.h b/atom/browser/api/trackable_object.h index 8ff7d1c04..7c4ed03fe 100644 --- a/atom/browser/api/trackable_object.h +++ b/atom/browser/api/trackable_object.h @@ -12,6 +12,7 @@ #include "base/bind.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" +#include "native_mate/object_template_builder.h" namespace base { class SupportsUserData; @@ -30,26 +31,32 @@ class TrackableObjectBase : public mate::EventEmitter { // Wrap TrackableObject into a class that SupportsUserData. void AttachAsUserData(base::SupportsUserData* wrapped); - // Subclasses should implement this to destroy their native types. - virtual void Destroy() = 0; - protected: ~TrackableObjectBase() override; // mate::Wrappable: void AfterInit(v8::Isolate* isolate) override; + // Mark the JS object as destroyed. + void MarkDestroyed(); + + // Returns a closure that can destroy the native class. + base::Closure GetDestroyClosure(); + // Get the weak_map_id from SupportsUserData. static int32_t GetIDFromWrappedClass(base::SupportsUserData* wrapped); // Register a callback that should be destroyed before JavaScript environment // gets destroyed. - static void RegisterDestructionCallback(const base::Closure& closure); + static base::Closure RegisterDestructionCallback(const base::Closure& c); int32_t weak_map_id_; base::SupportsUserData* wrapped_; private: + void Destroy(); + + base::Closure cleanup_; base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(TrackableObjectBase); @@ -91,11 +98,6 @@ class TrackableObject : public TrackableObjectBase { return std::vector>(); } - TrackableObject() { - RegisterDestructionCallback( - base::Bind(&TrackableObject::ReleaseAllWeakReferences)); - } - // Removes this instance from the weak map. void RemoveFromWeakMap() { if (weak_map_ && weak_map_->Has(weak_map_id())) @@ -103,28 +105,49 @@ class TrackableObject : public TrackableObjectBase { } protected: + TrackableObject() {} ~TrackableObject() override { RemoveFromWeakMap(); } void AfterInit(v8::Isolate* isolate) override { - if (!weak_map_) + if (!weak_map_) { weak_map_.reset(new atom::IDWeakMap); + RegisterDestructionCallback( + base::Bind(&TrackableObject::ReleaseAllWeakReferences)); + } weak_map_id_ = weak_map_->Add(isolate, GetWrapper(isolate)); TrackableObjectBase::AfterInit(isolate); } private: + // mate::Wrappable: + mate::ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) override { + if (template_.IsEmpty()) { + auto templ = v8::ObjectTemplate::New(isolate); + T::BuildPrototype(isolate, templ); + template_.Reset(isolate, templ); + } + + return ObjectTemplateBuilder( + isolate, v8::Local::New(isolate, template_)); + } + // Releases all weak references in weak map, called when app is terminating. static void ReleaseAllWeakReferences() { weak_map_.reset(); } + static v8::Persistent template_; static scoped_ptr weak_map_; DISALLOW_COPY_AND_ASSIGN(TrackableObject); }; +template +v8::Persistent TrackableObject::template_; + template scoped_ptr TrackableObject::weak_map_; diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index 38fdc0e19..5ad8e69ff 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -25,6 +25,7 @@ #include "base/strings/string_number_conversions.h" #include "chrome/browser/printing/printing_message_filter.h" #include "chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h" +#include "chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.h" #include "chrome/browser/speech/tts_message_filter.h" #include "content/public/browser/browser_ppapi_host.h" #include "content/public/browser/client_certificate_delegate.h" @@ -54,6 +55,8 @@ bool g_suppress_renderer_process_restart = false; // Custom schemes to be registered to standard. std::string g_custom_schemes = ""; +// Custom schemes to be registered to handle service worker. +std::string g_custom_service_worker_schemes = ""; scoped_refptr ImportCertFromFile( const base::FilePath& path) { @@ -84,7 +87,12 @@ void AtomBrowserClient::SuppressRendererProcessRestartForOnce() { void AtomBrowserClient::SetCustomSchemes( const std::vector& schemes) { - g_custom_schemes = JoinString(schemes, ','); + g_custom_schemes = base::JoinString(schemes, ","); +} + +void AtomBrowserClient::SetCustomServiceWorkerSchemes( + const std::vector& schemes) { + g_custom_service_worker_schemes = base::JoinString(schemes, ","); } AtomBrowserClient::AtomBrowserClient() : delegate_(nullptr) { @@ -98,6 +106,8 @@ void AtomBrowserClient::RenderProcessWillLaunch( int process_id = host->GetID(); host->AddFilter(new printing::PrintingMessageFilter(process_id)); host->AddFilter(new TtsMessageFilter(process_id, host->GetBrowserContext())); + host->AddFilter( + new WidevineCdmMessageFilter(process_id, host->GetBrowserContext())); } content::SpeechRecognitionManagerDelegate* @@ -116,7 +126,6 @@ void AtomBrowserClient::OverrideWebkitPrefs( prefs->javascript_can_open_windows_automatically = true; prefs->plugins_enabled = true; prefs->dom_paste_enabled = true; - prefs->java_enabled = false; prefs->allow_scripts_to_close_windows = true; prefs->javascript_can_access_clipboard = true; prefs->local_storage_enabled = true; @@ -173,6 +182,11 @@ void AtomBrowserClient::AppendExtraCommandLineSwitches( command_line->AppendSwitchASCII(switches::kRegisterStandardSchemes, g_custom_schemes); + // The registered service worker schemes. + if (!g_custom_service_worker_schemes.empty()) + command_line->AppendSwitchASCII(switches::kRegisterServiceWorkerSchemes, + g_custom_service_worker_schemes); + #if defined(OS_WIN) // Append --app-user-model-id. PWSTR current_app_id; @@ -186,9 +200,16 @@ void AtomBrowserClient::AppendExtraCommandLineSwitches( if (ContainsKey(pending_processes_, process_id)) process_id = pending_processes_[process_id]; + + // Certain render process will be created with no associated render view, + // for example: ServiceWorker. + auto rvh = content::RenderViewHost::FromID(process_id, kDefaultRoutingID); + if (!rvh) + return; + // Get the WebContents of the render process. - content::WebContents* web_contents = content::WebContents::FromRenderViewHost( - content::RenderViewHost::FromID(process_id, kDefaultRoutingID)); + content::WebContents* web_contents = + content::WebContents::FromRenderViewHost(rvh); if (!web_contents) return; diff --git a/atom/browser/atom_browser_client.h b/atom/browser/atom_browser_client.h index 75e174945..3c54fab40 100644 --- a/atom/browser/atom_browser_client.h +++ b/atom/browser/atom_browser_client.h @@ -38,6 +38,9 @@ class AtomBrowserClient : public brightray::BrowserClient, static void SuppressRendererProcessRestartForOnce(); // Custom schemes to be registered to standard. static void SetCustomSchemes(const std::vector& schemes); + // Custom schemes to be registered to handle service worker. + static void SetCustomServiceWorkerSchemes( + const std::vector& schemes); protected: // content::ContentBrowserClient: diff --git a/atom/browser/atom_browser_context.cc b/atom/browser/atom_browser_context.cc index 08c799962..12e012575 100644 --- a/atom/browser/atom_browser_context.cc +++ b/atom/browser/atom_browser_context.cc @@ -8,6 +8,7 @@ #include "atom/browser/atom_download_manager_delegate.h" #include "atom/browser/browser.h" #include "atom/browser/net/atom_cert_verifier.h" +#include "atom/browser/net/atom_network_delegate.h" #include "atom/browser/net/atom_ssl_config_service.h" #include "atom/browser/net/atom_url_request_job_factory.h" #include "atom/browser/net/asar/asar_protocol_handler.h" @@ -61,14 +62,19 @@ std::string RemoveWhitespace(const std::string& str) { AtomBrowserContext::AtomBrowserContext(const std::string& partition, bool in_memory) : brightray::BrowserContext(partition, in_memory), - cert_verifier_(new AtomCertVerifier), + cert_verifier_(nullptr), job_factory_(new AtomURLRequestJobFactory), + network_delegate_(new AtomNetworkDelegate), allow_ntlm_everywhere_(false) { } AtomBrowserContext::~AtomBrowserContext() { } +net::NetworkDelegate* AtomBrowserContext::CreateNetworkDelegate() { + return network_delegate_; +} + std::string AtomBrowserContext::GetUserAgent() { Browser* browser = Browser::Get(); std::string name = RemoveWhitespace(browser->GetName()); @@ -86,7 +92,8 @@ std::string AtomBrowserContext::GetUserAgent() { return content::BuildUserAgentFromProduct(user_agent); } -net::URLRequestJobFactory* AtomBrowserContext::CreateURLRequestJobFactory( +scoped_ptr +AtomBrowserContext::CreateURLRequestJobFactory( content::ProtocolHandlerMap* handlers, content::URLRequestInterceptorScopedVector* interceptors) { scoped_ptr job_factory(job_factory_); @@ -131,7 +138,7 @@ net::URLRequestJobFactory* AtomBrowserContext::CreateURLRequestJobFactory( top_job_factory.Pass(), make_scoped_ptr(*it))); interceptors->weak_clear(); - return top_job_factory.release(); + return top_job_factory.Pass(); } net::HttpCache::BackendFactory* @@ -160,8 +167,10 @@ content::BrowserPluginGuestManager* AtomBrowserContext::GetGuestManager() { return guest_manager_.get(); } -net::CertVerifier* AtomBrowserContext::CreateCertVerifier() { - return cert_verifier_; +scoped_ptr AtomBrowserContext::CreateCertVerifier() { + DCHECK(!cert_verifier_); + cert_verifier_ = new AtomCertVerifier; + return make_scoped_ptr(cert_verifier_); } net::SSLConfigService* AtomBrowserContext::CreateSSLConfigService() { diff --git a/atom/browser/atom_browser_context.h b/atom/browser/atom_browser_context.h index d3d7735c8..9c94a60c3 100644 --- a/atom/browser/atom_browser_context.h +++ b/atom/browser/atom_browser_context.h @@ -13,6 +13,7 @@ namespace atom { class AtomDownloadManagerDelegate; class AtomCertVerifier; +class AtomNetworkDelegate; class AtomURLRequestJobFactory; class WebViewManager; @@ -22,13 +23,14 @@ class AtomBrowserContext : public brightray::BrowserContext { ~AtomBrowserContext() override; // brightray::URLRequestContextGetter::Delegate: + net::NetworkDelegate* CreateNetworkDelegate() override; std::string GetUserAgent() override; - net::URLRequestJobFactory* CreateURLRequestJobFactory( + scoped_ptr CreateURLRequestJobFactory( content::ProtocolHandlerMap* handlers, content::URLRequestInterceptorScopedVector* interceptors) override; net::HttpCache::BackendFactory* CreateHttpCacheBackendFactory( const base::FilePath& base_path) override; - net::CertVerifier* CreateCertVerifier() override; + scoped_ptr CreateCertVerifier() override; net::SSLConfigService* CreateSSLConfigService() override; bool AllowNTLMCredentialsForDomain(const GURL& auth_origin) override; @@ -45,6 +47,8 @@ class AtomBrowserContext : public brightray::BrowserContext { AtomURLRequestJobFactory* job_factory() const { return job_factory_; } + AtomNetworkDelegate* network_delegate() const { return network_delegate_; } + private: scoped_ptr download_manager_delegate_; scoped_ptr guest_manager_; @@ -52,6 +56,7 @@ class AtomBrowserContext : public brightray::BrowserContext { // Managed by brightray::BrowserContext. AtomCertVerifier* cert_verifier_; AtomURLRequestJobFactory* job_factory_; + AtomNetworkDelegate* network_delegate_; bool allow_ntlm_everywhere_; diff --git a/atom/browser/atom_browser_main_parts.cc b/atom/browser/atom_browser_main_parts.cc index 0a8c16ca2..eadd52ac4 100644 --- a/atom/browser/atom_browser_main_parts.cc +++ b/atom/browser/atom_browser_main_parts.cc @@ -25,6 +25,11 @@ namespace atom { +template +void Erase(T* container, typename T::iterator iter) { + container->erase(iter); +} + // static AtomBrowserMainParts* AtomBrowserMainParts::self_ = NULL; @@ -56,9 +61,14 @@ bool AtomBrowserMainParts::SetExitCode(int code) { return true; } -void AtomBrowserMainParts::RegisterDestructionCallback( +int AtomBrowserMainParts::GetExitCode() { + return exit_code_ != nullptr ? *exit_code_ : 0; +} + +base::Closure AtomBrowserMainParts::RegisterDestructionCallback( const base::Closure& callback) { - destruction_callbacks_.push_back(callback); + auto iter = destructors_.insert(destructors_.end(), callback); + return base::Bind(&Erase>, &destructors_, iter); } void AtomBrowserMainParts::PreEarlyInitialization() { @@ -114,7 +124,8 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() { 1000)); brightray::BrowserMainParts::PreMainMessageLoopRun(); - BridgeTaskRunner::MessageLoopIsReady(); + bridge_task_runner_->MessageLoopIsReady(); + bridge_task_runner_ = nullptr; #if defined(USE_X11) libgtk2ui::GtkInitFromCommandLine(*base::CommandLine::ForCurrentProcess()); @@ -149,8 +160,13 @@ void AtomBrowserMainParts::PostMainMessageLoopRun() { // Make sure destruction callbacks are called before message loop is // destroyed, otherwise some objects that need to be deleted on IO thread // won't be freed. - for (const auto& callback : destruction_callbacks_) + // We don't use ranged for loop because iterators are getting invalided when + // the callback runs. + for (auto iter = destructors_.begin(); iter != destructors_.end();) { + base::Closure& callback = *iter; + ++iter; callback.Run(); + } // Destroy JavaScript environment immediately after running destruction // callbacks. diff --git a/atom/browser/atom_browser_main_parts.h b/atom/browser/atom_browser_main_parts.h index bb4b20466..c1c0c89c6 100644 --- a/atom/browser/atom_browser_main_parts.h +++ b/atom/browser/atom_browser_main_parts.h @@ -34,9 +34,13 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts { // Sets the exit code, will fail if the the message loop is not ready. bool SetExitCode(int code); + // Gets the exit code + int GetExitCode(); + // Register a callback that should be destroyed before JavaScript environment // gets destroyed. - void RegisterDestructionCallback(const base::Closure& callback); + // Returns a closure that can be used to remove |callback| from the list. + base::Closure RegisterDestructionCallback(const base::Closure& callback); Browser* browser() { return browser_.get(); } @@ -82,7 +86,7 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts { base::Timer gc_timer_; // List of callbacks should be executed before destroying JS env. - std::list destruction_callbacks_; + std::list destructors_; static AtomBrowserMainParts* self_; diff --git a/atom/browser/atom_browser_main_parts_mac.mm b/atom/browser/atom_browser_main_parts_mac.mm index 42e3100f4..d6e83fd96 100644 --- a/atom/browser/atom_browser_main_parts_mac.mm +++ b/atom/browser/atom_browser_main_parts_mac.mm @@ -13,20 +13,14 @@ namespace atom { void AtomBrowserMainParts::PreMainMessageLoopStart() { - // Initialize locale setting. - l10n_util::OverrideLocaleWithCocoaLocale(); - // Force the NSApplication subclass to be used. - NSApplication* application = [AtomApplication sharedApplication]; + [AtomApplication sharedApplication]; + // Set our own application delegate. AtomApplicationDelegate* delegate = [[AtomApplicationDelegate alloc] init]; [NSApp setDelegate:(id)delegate]; - NSBundle* frameworkBundle = base::mac::FrameworkBundle(); - NSNib* mainNib = [[NSNib alloc] initWithNibNamed:@"MainMenu" - bundle:frameworkBundle]; - [mainNib instantiateWithOwner:application topLevelObjects:nil]; - [mainNib release]; + brightray::BrowserMainParts::PreMainMessageLoopStart(); // Prevent Cocoa from turning command-line arguments into // |-application:openFiles:|, since we already handle them directly. diff --git a/atom/browser/bridge_task_runner.cc b/atom/browser/bridge_task_runner.cc index 882a3050d..36c8d1735 100644 --- a/atom/browser/bridge_task_runner.cc +++ b/atom/browser/bridge_task_runner.cc @@ -8,11 +8,6 @@ namespace atom { -// static -std::vector BridgeTaskRunner::tasks_; -std::vector BridgeTaskRunner::non_nestable_tasks_; - -// static void BridgeTaskRunner::MessageLoopIsReady() { auto message_loop = base::MessageLoop::current(); CHECK(message_loop); diff --git a/atom/browser/bridge_task_runner.h b/atom/browser/bridge_task_runner.h index 12508f009..b69b33b29 100644 --- a/atom/browser/bridge_task_runner.h +++ b/atom/browser/bridge_task_runner.h @@ -20,7 +20,7 @@ class BridgeTaskRunner : public base::SingleThreadTaskRunner { ~BridgeTaskRunner() override {} // Called when message loop is ready. - static void MessageLoopIsReady(); + void MessageLoopIsReady(); // base::SingleThreadTaskRunner: bool PostDelayedTask(const tracked_objects::Location& from_here, @@ -35,8 +35,8 @@ class BridgeTaskRunner : public base::SingleThreadTaskRunner { private: using TaskPair = base::Tuple< tracked_objects::Location, base::Closure, base::TimeDelta>; - static std::vector tasks_; - static std::vector non_nestable_tasks_; + std::vector tasks_; + std::vector non_nestable_tasks_; DISALLOW_COPY_AND_ASSIGN(BridgeTaskRunner); }; diff --git a/atom/browser/browser.cc b/atom/browser/browser.cc index c77f35976..7a2c22ea9 100644 --- a/atom/browser/browser.cc +++ b/atom/browser/browser.cc @@ -55,7 +55,7 @@ void Browser::Exit(int code) { // Must destroy windows before quitting, otherwise bad things can happen. atom::WindowList* window_list = atom::WindowList::GetInstance(); if (window_list->size() == 0) { - NotifyAndShutdown(); + Shutdown(); } else { // Unlike Quit(), we do not ask to close window, but destroy the window // without asking. diff --git a/atom/browser/browser_mac.mm b/atom/browser/browser_mac.mm index 6589057c2..8b1dd31f3 100644 --- a/atom/browser/browser_mac.mm +++ b/atom/browser/browser_mac.mm @@ -19,11 +19,17 @@ void Browser::Focus() { } void Browser::AddRecentDocument(const base::FilePath& path) { - NSURL* u = [NSURL fileURLWithPath:base::mac::FilePathToNSString(path)]; + NSString* path_string = base::mac::FilePathToNSString(path); + if (!path_string) + return; + NSURL* u = [NSURL fileURLWithPath:path_string]; + if (!u) + return; [[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:u]; } void Browser::ClearRecentDocuments() { + [[NSDocumentController sharedDocumentController] clearRecentDocuments:nil]; } void Browser::SetAppUserModelID(const base::string16& name) { @@ -38,7 +44,8 @@ std::string Browser::GetExecutableFileProductName() const { } int Browser::DockBounce(BounceType type) { - return [[AtomApplication sharedApplication] requestUserAttention:(NSRequestUserAttentionType)type]; + return [[AtomApplication sharedApplication] + requestUserAttention:(NSRequestUserAttentionType)type]; } void Browser::DockCancelBounce(int rid) { @@ -65,8 +72,29 @@ void Browser::DockHide() { } void Browser::DockShow() { + BOOL active = [[NSRunningApplication currentApplication] isActive]; ProcessSerialNumber psn = { 0, kCurrentProcess }; - TransformProcessType(&psn, kProcessTransformToForegroundApplication); + if (active) { + // Workaround buggy behavior of TransformProcessType. + // http://stackoverflow.com/questions/7596643/ + NSArray* runningApps = [NSRunningApplication + runningApplicationsWithBundleIdentifier:@"com.apple.dock"]; + for (NSRunningApplication* app in runningApps) { + [app activateWithOptions:NSApplicationActivateIgnoringOtherApps]; + break; + } + dispatch_time_t one_ms = dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC); + dispatch_after(one_ms, dispatch_get_main_queue(), ^{ + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + dispatch_time_t one_ms = dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC); + dispatch_after(one_ms, dispatch_get_main_queue(), ^{ + [[NSRunningApplication currentApplication] + activateWithOptions:NSApplicationActivateIgnoringOtherApps]; + }); + }); + } else { + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + } } void Browser::DockSetMenu(ui::MenuModel* model) { diff --git a/atom/browser/browser_win.cc b/atom/browser/browser_win.cc index ce36d56b6..fdf4bd8c3 100644 --- a/atom/browser/browser_win.cc +++ b/atom/browser/browser_win.cc @@ -127,7 +127,7 @@ void Browser::SetUserTasks(const std::vector& tasks) { PCWSTR Browser::GetAppUserModelID() { if (app_user_model_id_.empty()) { - SetAppUserModelID(ReplaceStringPlaceholders( + SetAppUserModelID(base::ReplaceStringPlaceholders( kAppUserModelIDFormat, base::UTF8ToUTF16(GetName()), nullptr)); } diff --git a/atom/browser/common_web_contents_delegate.cc b/atom/browser/common_web_contents_delegate.cc index 8b7a159dd..72a664f8c 100644 --- a/atom/browser/common_web_contents_delegate.cc +++ b/atom/browser/common_web_contents_delegate.cc @@ -380,7 +380,7 @@ gfx::ImageSkia CommonWebContentsDelegate::GetDevToolsWindowIcon() { void CommonWebContentsDelegate::GetDevToolsWindowWMClass( std::string* name, std::string* class_name) { *class_name = Browser::Get()->GetName(); - *name = base::StringToLowerASCII(*class_name); + *name = base::ToLowerASCII(*class_name); } #endif diff --git a/atom/browser/default_app/index.html b/atom/browser/default_app/index.html index e55cdf77b..ec16a38bc 100644 --- a/atom/browser/default_app/index.html +++ b/atom/browser/default_app/index.html @@ -76,7 +76,11 @@ }; -

Welcome to Electron

+

+ +

To run your app with Electron, execute the following command under your @@ -87,8 +91,18 @@

The path-to-your-app should be the path to your own Electron - app, you can read the quick start - guide in Electron's docs + app, you can read the + + guide in Electron's + on how to write one.

diff --git a/atom/browser/default_app/main.js b/atom/browser/default_app/main.js index 3916cfb28..c325aed02 100644 --- a/atom/browser/default_app/main.js +++ b/atom/browser/default_app/main.js @@ -148,7 +148,11 @@ app.once('ready', function() { }, { label: 'Documentation', - click: function() { shell.openExternal('https://github.com/atom/electron/tree/master/docs#readme') } + click: function() { + shell.openExternal( + `https://github.com/atom/electron/tree/v${process.versions.electron}/docs#readme` + ) + } }, { label: 'Community Discussions', @@ -189,11 +193,11 @@ app.once('ready', function() { { label: 'Hide Others', accelerator: 'Command+Shift+H', - role: 'hideothers:' + role: 'hideothers' }, { label: 'Show All', - role: 'unhide:' + role: 'unhide' }, { type: 'separator' @@ -249,7 +253,11 @@ if (option.file && !option.webdriver) { } catch(e) { if (e.code == 'MODULE_NOT_FOUND') { app.focus(); - dialog.showErrorBox('Error opening app', 'The app provided is not a valid electron app, please read the docs on how to write one:\nhttps://github.com/atom/electron/tree/master/docs\n\n' + e.toString()); + dialog.showErrorBox( + 'Error opening app', + 'The app provided is not a valid Electron app, please read the docs on how to write one:\n' + + `https://github.com/atom/electron/tree/v${process.versions.electron}/docs\n\n${e.toString()}` + ); process.exit(1); } else { console.error('App threw an error when running', e); diff --git a/atom/browser/lib/desktop-capturer.coffee b/atom/browser/lib/desktop-capturer.coffee new file mode 100644 index 000000000..a7fb29ff7 --- /dev/null +++ b/atom/browser/lib/desktop-capturer.coffee @@ -0,0 +1,37 @@ +{ipcMain} = require 'electron' +{desktopCapturer} = process.atomBinding 'desktop_capturer' + +deepEqual = (opt1, opt2) -> + return JSON.stringify(opt1) is JSON.stringify(opt2) + +# A queue for holding all requests from renderer process. +requestsQueue = [] + +ipcMain.on 'ATOM_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', (event, captureWindow, captureScreen, thumbnailSize, id) -> + request = id: id, options: {captureWindow, captureScreen, thumbnailSize}, webContents: event.sender + requestsQueue.push request + desktopCapturer.startHandling captureWindow, captureScreen, thumbnailSize if requestsQueue.length is 1 + # If the WebContents is destroyed before receiving result, just remove the + # reference from requestsQueue to make the module not send the result to it. + event.sender.once 'destroyed', -> + request.webContents = null + +desktopCapturer.emit = (event, name, sources) -> + # Receiving sources result from main process, now send them back to renderer. + handledRequest = requestsQueue.shift 0 + result = ({ id: source.id, name: source.name, thumbnail: source.thumbnail.toDataUrl() } for source in sources) + handledRequest.webContents?.send "ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_#{handledRequest.id}", result + + # Check the queue to see whether there is other same request. If has, handle + # it for reducing redunplicated `desktopCaptuer.startHandling` calls. + unhandledRequestsQueue = [] + for request in requestsQueue + if deepEqual handledRequest.options, request.options + request.webContents?.send "ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_#{request.id}", errorMessage, result + else + unhandledRequestsQueue.push request + requestsQueue = unhandledRequestsQueue + # If the requestsQueue is not empty, start a new request handling. + if requestsQueue.length > 0 + {captureWindow, captureScreen, thumbnailSize} = requestsQueue[0].options + desktopCapturer.startHandling captureWindow, captureScreen, thumbnailSize diff --git a/atom/browser/lib/guest-view-manager.coffee b/atom/browser/lib/guest-view-manager.coffee index e6be05a90..c99bf5757 100644 --- a/atom/browser/lib/guest-view-manager.coffee +++ b/atom/browser/lib/guest-view-manager.coffee @@ -13,16 +13,26 @@ supportedWebViewEvents = [ 'did-get-redirect-request' 'dom-ready' 'console-message' + 'devtools-opened' + 'devtools-closed' + 'devtools-focused' 'new-window' + 'will-navigate' + 'did-navigate' + 'did-navigate-in-page' 'close' 'crashed' 'gpu-crashed' 'plugin-crashed' 'destroyed' - 'page-title-set' + 'page-title-updated' 'page-favicon-updated' 'enter-html-full-screen' 'leave-html-full-screen' + 'media-started-playing' + 'media-paused' + 'found-in-page' + 'did-change-theme-color' ] nextInstanceId = 0 @@ -47,7 +57,7 @@ createGuest = (embedder, params) -> guestInstances[id] = {guest, embedder} # Destroy guest when the embedder is gone or navigated. - destroyEvents = ['destroyed', 'crashed', 'did-navigate-to-different-page'] + destroyEvents = ['destroyed', 'crashed', 'did-navigate'] destroy = -> destroyGuest embedder, id if guestInstances[id]? for event in destroyEvents diff --git a/atom/browser/lib/guest-window-manager.coffee b/atom/browser/lib/guest-window-manager.coffee index c311e01cf..af73db5c7 100644 --- a/atom/browser/lib/guest-window-manager.coffee +++ b/atom/browser/lib/guest-window-manager.coffee @@ -5,7 +5,7 @@ frameToGuest = {} # Copy attribute of |parent| to |child| if it is not defined in |child|. mergeOptions = (child, parent) -> - for own key, value of parent when key not in child + for own key, value of parent when key not of child if typeof value is 'object' child[key] = mergeOptions {}, value else @@ -30,20 +30,22 @@ createGuest = (embedder, url, frameName, options) -> guest.loadURL url return guest.id + # Remember the embedder window's id. + options.webPreferences ?= {} + options.webPreferences.openerId = BrowserWindow.fromWebContents(embedder)?.id + guest = new BrowserWindow(options) guest.loadURL url - # Remember the embedder, will be used by window.opener methods. - v8Util.setHiddenValue guest.webContents, 'embedder', embedder - # When |embedder| is destroyed we should also destroy attached guest, and if # guest is closed by user then we should prevent |embedder| from double # closing guest. + guestId = guest.id closedByEmbedder = -> guest.removeListener 'closed', closedByUser guest.destroy() closedByUser = -> - embedder.send 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED', guest.id + embedder.send "ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_#{guestId}" embedder.removeListener 'render-view-deleted', closedByEmbedder embedder.once 'render-view-deleted', closedByEmbedder guest.once 'closed', closedByUser @@ -72,24 +74,13 @@ ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', (event, guestId) -> ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_METHOD', (event, guestId, method, args...) -> BrowserWindow.fromId(guestId)?[method] args... -ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', (event, guestId, message, targetOrigin) -> +ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', (event, guestId, message, targetOrigin, sourceOrigin) -> + sourceId = BrowserWindow.fromWebContents(event.sender)?.id + return unless sourceId? + guestContents = BrowserWindow.fromId(guestId)?.webContents if guestContents?.getURL().indexOf(targetOrigin) is 0 or targetOrigin is '*' - guestContents.send 'ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', guestId, message, targetOrigin - -ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPENER_POSTMESSAGE', (event, guestId, message, targetOrigin, sourceOrigin) -> - embedder = v8Util.getHiddenValue event.sender, 'embedder' - if embedder?.getURL().indexOf(targetOrigin) is 0 or targetOrigin is '*' - embedder.send 'ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', guestId, message, sourceOrigin + guestContents?.send 'ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestId, method, args...) -> BrowserWindow.fromId(guestId)?.webContents?[method] args... - -ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_GET_GUEST_ID', (event) -> - embedder = v8Util.getHiddenValue event.sender, 'embedder' - if embedder? - guest = BrowserWindow.fromWebContents event.sender - if guest? - event.returnValue = guest.id - return - event.returnValue = null diff --git a/atom/browser/lib/init.coffee b/atom/browser/lib/init.coffee index ec0fd16c8..c3d56d9c7 100644 --- a/atom/browser/lib/init.coffee +++ b/atom/browser/lib/init.coffee @@ -53,8 +53,8 @@ process.on 'uncaughtException', (error) -> # Emit 'exit' event on quit. {app} = require 'electron' -app.on 'quit', -> - process.emit 'exit' +app.on 'quit', (event, exitCode) -> + process.emit 'exit', exitCode # Map process.exit to app.exit, which quits gracefully. process.exit = app.exit @@ -108,6 +108,9 @@ app.setAppPath packagePath # Load the chrome extension support. require './chrome-extension' +# Load internal desktop-capturer module. +require './desktop-capturer' + # Set main startup script of the app. mainStartupScript = packageJson.main or 'index.js' diff --git a/atom/browser/lib/rpc-server.coffee b/atom/browser/lib/rpc-server.coffee index ce63d9189..7b05fa3d1 100644 --- a/atom/browser/lib/rpc-server.coffee +++ b/atom/browser/lib/rpc-server.coffee @@ -43,6 +43,8 @@ valueToMeta = (sender, value, optimizeSimpleObject=false) -> meta.then = valueToMeta sender, value.then.bind(value) else if meta.type is 'error' meta.members = plainObjectToMeta value + # Error.name is not part of own properties. + meta.members.push {name: 'name', value: value.name} else if meta.type is 'date' meta.value = value.getTime() else @@ -67,6 +69,7 @@ unwrapArgs = (sender, args) -> when 'remote-object' then objectsRegistry.get meta.id when 'array' then unwrapArgs sender, meta.value when 'buffer' then new Buffer(meta.value) + when 'date' then new Date(meta.value) when 'promise' then Promise.resolve(then: metaToValue(meta.then)) when 'object' ret = v8Util.createObjectWithName meta.name @@ -107,7 +110,7 @@ unwrapArgs = (sender, args) -> # style function and the caller didn't pass a callback. callFunction = (event, func, caller, args) -> funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous') - funcPassedCallback = args[args.length - 1] is 'function' + funcPassedCallback = typeof args[args.length - 1] is 'function' try if funcMarkedAsync and not funcPassedCallback @@ -219,5 +222,10 @@ ipcMain.on 'ATOM_BROWSER_GUEST_WEB_CONTENTS', (event, guestInstanceId) -> catch e event.returnValue = exceptionToMeta e -ipcMain.on 'ATOM_BROWSER_LIST_MODULES', (event) -> - event.returnValue = (name for name of electron) +ipcMain.on 'ATOM_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', (event, guestInstanceId, method, args...) -> + try + guestViewManager = require './guest-view-manager' + guest = guestViewManager.getGuest(guestInstanceId) + guest[method].apply(guest, args) + catch e + event.returnValue = exceptionToMeta e diff --git a/atom/browser/mac/atom_application_delegate.mm b/atom/browser/mac/atom_application_delegate.mm index a18d2fe40..7662162ab 100644 --- a/atom/browser/mac/atom_application_delegate.mm +++ b/atom/browser/mac/atom_application_delegate.mm @@ -21,6 +21,9 @@ } - (void)applicationWillFinishLaunching:(NSNotification*)notify { + // Don't add the "Enter Full Screen" menu item automatically. + [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"NSFullScreenMenuItemEverywhere"]; + atom::Browser::Get()->WillFinishLaunching(); } diff --git a/atom/browser/native_window.cc b/atom/browser/native_window.cc index ad7a6c4b0..bed42b101 100644 --- a/atom/browser/native_window.cc +++ b/atom/browser/native_window.cc @@ -32,10 +32,10 @@ #include "ipc/ipc_message_macros.h" #include "native_mate/dictionary.h" #include "ui/gfx/codec/png_codec.h" -#include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" +#include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/screen.h" #include "ui/gl/gpu_switching_manager.h" @@ -245,6 +245,9 @@ bool NativeWindow::IsDocumentEdited() { return false; } +void NativeWindow::SetIgnoreMouseEvents(bool ignore) { +} + void NativeWindow::SetMenu(ui::MenuModel* menu) { } @@ -265,10 +268,6 @@ bool NativeWindow::IsWebViewFocused() { return host_view && host_view->HasFocus(); } -bool NativeWindow::IsDevToolsFocused() { - return inspectable_web_contents_->GetView()->IsDevToolsViewFocused(); -} - void NativeWindow::CapturePage(const gfx::Rect& rect, const CapturePageCallback& callback) { const auto view = web_contents()->GetRenderWidgetHostView(); @@ -291,10 +290,10 @@ void NativeWindow::CapturePage(const gfx::Rect& rect, const float scale = screen->GetDisplayNearestWindow(native_view).device_scale_factor(); if (scale > 1.0f) - bitmap_size = gfx::ToCeiledSize(gfx::ScaleSize(view_size, scale)); + bitmap_size = gfx::ScaleToCeiledSize(view_size, scale); host->CopyFromBackingStore( - rect.IsEmpty() ? gfx::Rect(view_size) : rect, + gfx::Rect(rect.origin(), view_size), bitmap_size, base::Bind(&NativeWindow::OnCapturePageDone, weak_factory_.GetWeakPtr(), @@ -505,17 +504,6 @@ void NativeWindow::BeforeUnloadDialogCancelled() { window_unresposive_closure_.Cancel(); } -void NativeWindow::TitleWasSet(content::NavigationEntry* entry, - bool explicit_set) { - bool prevent_default = false; - std::string text = entry ? base::UTF16ToUTF8(entry->GetTitle()) : ""; - FOR_EACH_OBSERVER(NativeWindowObserver, - observers_, - OnPageTitleUpdated(&prevent_default, text)); - if (!prevent_default && !is_closed_) - SetTitle(text); -} - bool NativeWindow::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(NativeWindow, message) diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index e32b94811..c60c0dd5f 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -139,6 +139,7 @@ class NativeWindow : public base::SupportsUserData, virtual std::string GetRepresentedFilename(); virtual void SetDocumentEdited(bool edited); virtual bool IsDocumentEdited(); + virtual void SetIgnoreMouseEvents(bool ignore); virtual void SetMenu(ui::MenuModel* menu); virtual bool HasModalDialog(); virtual gfx::NativeWindow GetNativeWindow() = 0; @@ -156,7 +157,6 @@ class NativeWindow : public base::SupportsUserData, virtual void FocusOnWebView(); virtual void BlurWebView(); virtual bool IsWebViewFocused(); - virtual bool IsDevToolsFocused(); // Captures the page with |rect|, |callback| would be called when capturing is // done. @@ -262,7 +262,6 @@ class NativeWindow : public base::SupportsUserData, // content::WebContentsObserver: void RenderViewCreated(content::RenderViewHost* render_view_host) override; void BeforeUnloadDialogCancelled() override; - void TitleWasSet(content::NavigationEntry* entry, bool explicit_set) override; bool OnMessageReceived(const IPC::Message& message) override; private: diff --git a/atom/browser/native_window_mac.h b/atom/browser/native_window_mac.h index 08f9198e4..23f210d59 100644 --- a/atom/browser/native_window_mac.h +++ b/atom/browser/native_window_mac.h @@ -62,6 +62,7 @@ class NativeWindowMac : public NativeWindow { std::string GetRepresentedFilename() override; void SetDocumentEdited(bool edited) override; bool IsDocumentEdited() override; + void SetIgnoreMouseEvents(bool ignore) override; bool HasModalDialog() override; gfx::NativeWindow GetNativeWindow() override; void SetProgressBar(double progress) override; @@ -77,6 +78,10 @@ class NativeWindowMac : public NativeWindow { UpdateDraggableRegionViews(draggable_regions_); } + bool should_hide_native_toolbar_in_fullscreen() const { + return should_hide_native_toolbar_in_fullscreen_; + } + protected: // NativeWindow: void HandleKeyboardEvent( @@ -117,6 +122,8 @@ class NativeWindowMac : public NativeWindow { // The presentation options before entering kiosk mode. NSApplicationPresentationOptions kiosk_options_; + bool should_hide_native_toolbar_in_fullscreen_; + DISALLOW_COPY_AND_ASSIGN(NativeWindowMac); }; diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index 7959eb04f..3735772fe 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -6,7 +6,6 @@ #include -#import "atom/browser/ui/cocoa/event_processing_window.h" #include "atom/common/draggable_region.h" #include "atom/common/options_switches.h" #include "base/mac/mac_util.h" @@ -177,8 +176,27 @@ bool ScopedDisableResize::disable_resize_ = false; return YES; } +- (void)windowWillEnterFullScreen:(NSNotification*)notification { + // Hide the native toolbar before entering fullscreen, so there is no visual + // artifacts. + if (shell_->should_hide_native_toolbar_in_fullscreen()) { + NSWindow* window = shell_->GetNativeWindow(); + [window setToolbar:nil]; + } +} + - (void)windowDidEnterFullScreen:(NSNotification*)notification { shell_->NotifyWindowEnterFullScreen(); + + // Restore the native toolbar immediately after entering fullscreen, if we do + // this before leaving fullscreen, traffic light buttons will be jumping. + if (shell_->should_hide_native_toolbar_in_fullscreen()) { + NSWindow* window = shell_->GetNativeWindow(); + base::scoped_nsobject toolbar( + [[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]); + [toolbar setShowsBaselineSeparator:NO]; + [window setToolbar:toolbar]; + } } - (void)windowDidExitFullScreen:(NSNotification*)notification { @@ -192,6 +210,11 @@ bool ScopedDisableResize::disable_resize_ = false; - (void)windowWillClose:(NSNotification*)notification { shell_->NotifyWindowClosed(); + + // Clears the delegate when window is going to be closed, since EL Capitan it + // is possible that the methods of delegate would get called after the window + // has been closed. + [shell_->GetNativeWindow() setDelegate:nil]; } - (BOOL)windowShouldClose:(id)window { @@ -204,7 +227,7 @@ bool ScopedDisableResize::disable_resize_ = false; @end -@interface AtomNSWindow : EventProcessingWindow { +@interface AtomNSWindow : NSWindow { @private atom::NativeWindowMac* shell_; bool enable_larger_than_screen_; @@ -227,6 +250,8 @@ bool ScopedDisableResize::disable_resize_ = false; enable_larger_than_screen_ = enable; } +// NSWindow overrides. + - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen*)screen { // Resizing is disabled. if (ScopedDisableResize::IsResizeDisabled()) @@ -328,7 +353,8 @@ NativeWindowMac::NativeWindowMac( const mate::Dictionary& options) : NativeWindow(web_contents, options), is_kiosk_(false), - attention_request_id_(0) { + attention_request_id_(0), + should_hide_native_toolbar_in_fullscreen_(false) { int width = 800, height = 600; options.Get(options::kWidth, &width); options.Get(options::kHeight, &height); @@ -371,6 +397,12 @@ NativeWindowMac::NativeWindowMac( styleMask |= NSFullSizeContentViewWindowMask; styleMask |= NSUnifiedTitleAndToolbarWindowMask; } + // We capture this because we need to access the option later when + // entering/exiting fullscreen and since the options dict is only passed to + // the constructor but not stored, let’s store this option this way. + if (titleBarStyle == "hidden-inset") { + should_hide_native_toolbar_in_fullscreen_ = true; + } window_.reset([[AtomNSWindow alloc] initWithContentRect:cocoa_bounds @@ -437,12 +469,17 @@ NativeWindowMac::NativeWindowMac( [window_ setDisableAutoHideCursor:disableAutoHideCursor]; // Disable fullscreen button when 'fullscreen' is specified to false. - bool fullscreen; + bool fullscreen = false; if (!(options.Get(options::kFullscreen, &fullscreen) && !fullscreen)) { NSUInteger collectionBehavior = [window_ collectionBehavior]; collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary; [window_ setCollectionBehavior:collectionBehavior]; + } else if (base::mac::IsOSElCapitanOrLater()) { + // On EL Capitan this flag is required to hide fullscreen button. + NSUInteger collectionBehavior = [window_ collectionBehavior]; + collectionBehavior |= NSWindowCollectionBehaviorFullScreenAuxiliary; + [window_ setCollectionBehavior:collectionBehavior]; } NSView* view = inspectable_web_contents()->GetView()->GetNativeView(); @@ -682,6 +719,10 @@ bool NativeWindowMac::IsDocumentEdited() { return [window_ isDocumentEdited]; } +void NativeWindowMac::SetIgnoreMouseEvents(bool ignore) { + [window_ setIgnoresMouseEvents:ignore]; +} + bool NativeWindowMac::HasModalDialog() { return [window_ attachedSheet] != nil; } @@ -761,20 +802,14 @@ void NativeWindowMac::HandleKeyboardEvent( event.type == content::NativeWebKeyboardEvent::Char) return; - if (event.os_event.window == window_.get()) { - EventProcessingWindow* event_window = - static_cast(window_); - DCHECK([event_window isKindOfClass:[EventProcessingWindow class]]); - [event_window redispatchKeyEvent:event.os_event]; - } else { + BOOL handled = [[NSApp mainMenu] performKeyEquivalent:event.os_event]; + if (!handled && event.os_event.window != window_.get()) { // The event comes from detached devtools view, and it has already been - // handled by the devtools itself, we now send it to application menu to - // make menu acclerators work. - BOOL handled = [[NSApp mainMenu] performKeyEquivalent:event.os_event]; - // Handle the cmd+~ shortcut. if (!handled && (event.os_event.modifierFlags & NSCommandKeyMask) && - (event.os_event.keyCode == 50 /* ~ key */)) + (event.os_event.keyCode == 50 /* ~ key */)) { + // Handle the cmd+~ shortcut. Focus(true); + } } } diff --git a/atom/browser/native_window_observer.h b/atom/browser/native_window_observer.h index 54004a300..ce2d41c6f 100644 --- a/atom/browser/native_window_observer.h +++ b/atom/browser/native_window_observer.h @@ -21,10 +21,6 @@ class NativeWindowObserver { public: virtual ~NativeWindowObserver() {} - // Called when the web page of the window has updated it's document title. - virtual void OnPageTitleUpdated(bool* prevent_default, - const std::string& title) {} - // Called when the web page in window wants to create a popup window. virtual void WillCreatePopupWindow(const base::string16& frame_name, const GURL& target_url, diff --git a/atom/browser/native_window_views.cc b/atom/browser/native_window_views.cc index c12ae1986..35e12ad3c 100644 --- a/atom/browser/native_window_views.cc +++ b/atom/browser/native_window_views.cc @@ -60,12 +60,7 @@ const int kMenuBarHeight = 25; #endif bool IsAltKey(const content::NativeWebKeyboardEvent& event) { -#if defined(USE_X11) - // 164 and 165 represent VK_LALT and VK_RALT. - return event.windowsKeyCode == 164 || event.windowsKeyCode == 165; -#else return event.windowsKeyCode == ui::VKEY_MENU; -#endif } bool IsAltModifier(const content::NativeWebKeyboardEvent& event) { @@ -180,7 +175,7 @@ NativeWindowViews::NativeWindowViews( // Set WM_WINDOW_ROLE. params.wm_role_name = "browser-window"; // Set WM_CLASS. - params.wm_class_name = base::StringToLowerASCII(name); + params.wm_class_name = base::ToLowerASCII(name); params.wm_class_class = name; #endif @@ -429,12 +424,14 @@ void NativeWindowViews::SetResizable(bool resizable) { // WS_MAXIMIZEBOX => Maximize button // WS_MINIMIZEBOX => Minimize button // WS_THICKFRAME => Resize handle - DWORD style = ::GetWindowLong(GetAcceleratedWidget(), GWL_STYLE); - if (resizable) - style |= WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME; - else - style = (style & ~(WS_MAXIMIZEBOX | WS_THICKFRAME)) | WS_MINIMIZEBOX; - ::SetWindowLong(GetAcceleratedWidget(), GWL_STYLE, style); + if (!transparent()) { + DWORD style = ::GetWindowLong(GetAcceleratedWidget(), GWL_STYLE); + if (resizable) + style |= WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME; + else + style = (style & ~(WS_MAXIMIZEBOX | WS_THICKFRAME)) | WS_MINIMIZEBOX; + ::SetWindowLong(GetAcceleratedWidget(), GWL_STYLE, style); + } #elif defined(USE_X11) if (resizable != resizable_) { // On Linux there is no "resizable" property of a window, we have to set diff --git a/atom/browser/native_window_views_win.cc b/atom/browser/native_window_views_win.cc index 02ebd2ef3..513b33bcb 100644 --- a/atom/browser/native_window_views_win.cc +++ b/atom/browser/native_window_views_win.cc @@ -84,20 +84,6 @@ bool NativeWindowViews::PreHandleMSG( NotifyWindowMessage(message, w_param, l_param); switch (message) { - // Screen readers send WM_GETOBJECT in order to get the accessibility - // object, so take this opportunity to push Chromium into accessible - // mode if it isn't already, always say we didn't handle the message - // because we still want Chromium to handle returning the actual - // accessibility object. - case WM_GETOBJECT: { - const DWORD obj_id = static_cast(l_param); - if (obj_id == OBJID_CLIENT) { - const auto axState = content::BrowserAccessibilityState::GetInstance(); - if (axState && !axState->IsAccessibleBrowser()) - axState->OnScreenReaderDetected(); - } - return false; - } case WM_COMMAND: // Handle thumbar button click message. if (HIWORD(w_param) == THBN_CLICKED) diff --git a/atom/browser/net/asar/url_request_asar_job.cc b/atom/browser/net/asar/url_request_asar_job.cc index 9b9a3c69d..d926d1111 100644 --- a/atom/browser/net/asar/url_request_asar_job.cc +++ b/atom/browser/net/asar/url_request_asar_job.cc @@ -7,13 +7,14 @@ #include #include +#include "atom/common/asar/archive.h" +#include "atom/common/asar/asar_util.h" +#include "atom/common/atom_constants.h" #include "base/bind.h" #include "base/files/file_util.h" #include "base/strings/string_util.h" #include "base/synchronization/lock.h" #include "base/task_runner.h" -#include "atom/common/asar/archive.h" -#include "atom/common/asar/asar_util.h" #include "net/base/file_stream.h" #include "net/base/filename_util.h" #include "net/base/io_buffer.h" @@ -227,6 +228,19 @@ void URLRequestAsarJob::SetExtraRequestHeaders( } } +int URLRequestAsarJob::GetResponseCode() const { + // Request Job gets created only if path exists. + return 200; +} + +void URLRequestAsarJob::GetResponseInfo(net::HttpResponseInfo* info) { + std::string status("HTTP/1.1 200 OK"); + net::HttpResponseHeaders* headers = new net::HttpResponseHeaders(status); + + headers->AddHeader(atom::kCORSHeader); + info->headers = headers; +} + void URLRequestAsarJob::FetchMetaInfo(const base::FilePath& file_path, FileMetaInfo* meta_info) { base::File::Info file_info; diff --git a/atom/browser/net/asar/url_request_asar_job.h b/atom/browser/net/asar/url_request_asar_job.h index 15a723d79..29d1afc52 100644 --- a/atom/browser/net/asar/url_request_asar_job.h +++ b/atom/browser/net/asar/url_request_asar_job.h @@ -61,6 +61,8 @@ class URLRequestAsarJob : public net::URLRequestJob { net::Filter* SetupFilter() const override; bool GetMimeType(std::string* mime_type) const override; void SetExtraRequestHeaders(const net::HttpRequestHeaders& headers) override; + int GetResponseCode() const override; + void GetResponseInfo(net::HttpResponseInfo* info) override; private: // Meta information about the file. It's used as a member in the diff --git a/atom/browser/net/atom_network_delegate.cc b/atom/browser/net/atom_network_delegate.cc new file mode 100644 index 000000000..4f9bc835e --- /dev/null +++ b/atom/browser/net/atom_network_delegate.cc @@ -0,0 +1,397 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/net/atom_network_delegate.h" + +#include + +#include "atom/common/native_mate_converters/net_converter.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/resource_request_info.h" +#include "net/url_request/url_request.h" + +using content::BrowserThread; + +namespace atom { + +namespace { + +const char* ResourceTypeToString(content::ResourceType type) { + switch (type) { + case content::RESOURCE_TYPE_MAIN_FRAME: + return "mainFrame"; + case content::RESOURCE_TYPE_SUB_FRAME: + return "subFrame"; + case content::RESOURCE_TYPE_STYLESHEET: + return "stylesheet"; + case content::RESOURCE_TYPE_SCRIPT: + return "script"; + case content::RESOURCE_TYPE_IMAGE: + return "image"; + case content::RESOURCE_TYPE_OBJECT: + return "object"; + case content::RESOURCE_TYPE_XHR: + return "xhr"; + default: + return "other"; + } +} + +void RunSimpleListener(const AtomNetworkDelegate::SimpleListener& listener, + scoped_ptr details) { + return listener.Run(*(details.get())); +} + +void RunResponseListener( + const AtomNetworkDelegate::ResponseListener& listener, + scoped_ptr details, + const AtomNetworkDelegate::ResponseCallback& callback) { + return listener.Run(*(details.get()), callback); +} + +// Test whether the URL of |request| matches |patterns|. +bool MatchesFilterCondition(net::URLRequest* request, + const URLPatterns& patterns) { + if (patterns.empty()) + return true; + + for (const auto& pattern : patterns) { + if (pattern.MatchesURL(request->url())) + return true; + } + return false; +} + +// Overloaded by multiple types to fill the |details| object. +void ToDictionary(base::DictionaryValue* details, net::URLRequest* request) { + details->SetInteger("id", request->identifier()); + details->SetString("url", request->url().spec()); + details->SetString("method", request->method()); + details->SetDouble("timestamp", base::Time::Now().ToDoubleT() * 1000); + auto info = content::ResourceRequestInfo::ForRequest(request); + details->SetString("resourceType", + info ? ResourceTypeToString(info->GetResourceType()) + : "other"); +} + +void ToDictionary(base::DictionaryValue* details, + const net::HttpRequestHeaders& headers) { + scoped_ptr dict(new base::DictionaryValue); + net::HttpRequestHeaders::Iterator it(headers); + while (it.GetNext()) + dict->SetString(it.name(), it.value()); + details->Set("requestHeaders", dict.Pass()); +} + +void ToDictionary(base::DictionaryValue* details, + const net::HttpResponseHeaders* headers) { + if (!headers) + return; + + scoped_ptr dict(new base::DictionaryValue); + void* iter = nullptr; + std::string key; + std::string value; + while (headers->EnumerateHeaderLines(&iter, &key, &value)) { + if (dict->HasKey(key)) { + base::ListValue* values = nullptr; + if (dict->GetList(key, &values)) + values->AppendString(value); + } else { + scoped_ptr values(new base::ListValue); + values->AppendString(value); + dict->Set(key, values.Pass()); + } + } + details->Set("responseHeaders", dict.Pass()); + details->SetString("statusLine", headers->GetStatusLine()); + details->SetInteger("statusCode", headers->response_code()); +} + +void ToDictionary(base::DictionaryValue* details, const GURL& location) { + details->SetString("redirectURL", location.spec()); +} + +void ToDictionary(base::DictionaryValue* details, + const net::HostPortPair& host_port) { + if (host_port.host().empty()) + details->SetString("ip", host_port.host()); +} + +void ToDictionary(base::DictionaryValue* details, bool from_cache) { + details->SetBoolean("fromCache", from_cache); +} + +void ToDictionary(base::DictionaryValue* details, + const net::URLRequestStatus& status) { + details->SetString("error", net::ErrorToString(status.error())); +} + +// Helper function to fill |details| with arbitrary |args|. +template +void FillDetailsObject(base::DictionaryValue* details, Arg arg) { + ToDictionary(details, arg); +} + +template +void FillDetailsObject(base::DictionaryValue* details, Arg arg, Args... args) { + ToDictionary(details, arg); + FillDetailsObject(details, args...); +} + +// Fill the native types with the result from the response object. +void ReadFromResponseObject(const base::DictionaryValue& response, + GURL* new_location) { + std::string url; + if (response.GetString("redirectURL", &url)) + *new_location = GURL(url); +} + +void ReadFromResponseObject(const base::DictionaryValue& response, + net::HttpRequestHeaders* headers) { + const base::DictionaryValue* dict; + if (response.GetDictionary("requestHeaders", &dict)) { + headers->Clear(); + for (base::DictionaryValue::Iterator it(*dict); + !it.IsAtEnd(); + it.Advance()) { + std::string value; + if (it.value().GetAsString(&value)) + headers->SetHeader(it.key(), value); + } + } +} + +void ReadFromResponseObject(const base::DictionaryValue& response, + scoped_refptr* headers) { + const base::DictionaryValue* dict; + if (response.GetDictionary("responseHeaders", &dict)) { + *headers = new net::HttpResponseHeaders(""); + for (base::DictionaryValue::Iterator it(*dict); + !it.IsAtEnd(); + it.Advance()) { + const base::ListValue* list; + if (it.value().GetAsList(&list)) { + (*headers)->RemoveHeader(it.key()); + for (size_t i = 0; i < list->GetSize(); ++i) { + std::string value; + if (list->GetString(i, &value)) + (*headers)->AddHeader(it.key() + " : " + value); + } + } + } + } +} + +} // namespace + +AtomNetworkDelegate::AtomNetworkDelegate() { +} + +AtomNetworkDelegate::~AtomNetworkDelegate() { +} + +void AtomNetworkDelegate::SetSimpleListenerInIO( + SimpleEvent type, + const URLPatterns& patterns, + const SimpleListener& callback) { + if (callback.is_null()) + simple_listeners_.erase(type); + else + simple_listeners_[type] = { patterns, callback }; +} + +void AtomNetworkDelegate::SetResponseListenerInIO( + ResponseEvent type, + const URLPatterns& patterns, + const ResponseListener& callback) { + if (callback.is_null()) + response_listeners_.erase(type); + else + response_listeners_[type] = { patterns, callback }; +} + +int AtomNetworkDelegate::OnBeforeURLRequest( + net::URLRequest* request, + const net::CompletionCallback& callback, + GURL* new_url) { + if (!ContainsKey(response_listeners_, kOnBeforeRequest)) + return brightray::NetworkDelegate::OnBeforeURLRequest( + request, callback, new_url); + + return HandleResponseEvent(kOnBeforeRequest, request, callback, new_url); +} + +int AtomNetworkDelegate::OnBeforeSendHeaders( + net::URLRequest* request, + const net::CompletionCallback& callback, + net::HttpRequestHeaders* headers) { + if (!ContainsKey(response_listeners_, kOnBeforeSendHeaders)) + return brightray::NetworkDelegate::OnBeforeSendHeaders( + request, callback, headers); + + return HandleResponseEvent( + kOnBeforeSendHeaders, request, callback, headers, *headers); +} + +void AtomNetworkDelegate::OnSendHeaders( + net::URLRequest* request, + const net::HttpRequestHeaders& headers) { + if (!ContainsKey(simple_listeners_, kOnSendHeaders)) { + brightray::NetworkDelegate::OnSendHeaders(request, headers); + return; + } + + HandleSimpleEvent(kOnSendHeaders, request, headers); +} + +int AtomNetworkDelegate::OnHeadersReceived( + net::URLRequest* request, + const net::CompletionCallback& callback, + const net::HttpResponseHeaders* original, + scoped_refptr* override, + GURL* allowed) { + if (!ContainsKey(response_listeners_, kOnHeadersReceived)) + return brightray::NetworkDelegate::OnHeadersReceived( + request, callback, original, override, allowed); + + return HandleResponseEvent( + kOnHeadersReceived, request, callback, override, original); +} + +void AtomNetworkDelegate::OnBeforeRedirect(net::URLRequest* request, + const GURL& new_location) { + if (!ContainsKey(simple_listeners_, kOnBeforeRedirect)) { + brightray::NetworkDelegate::OnBeforeRedirect(request, new_location); + return; + } + + HandleSimpleEvent(kOnBeforeRedirect, request, new_location, + request->response_headers(), request->GetSocketAddress(), + request->was_cached()); +} + +void AtomNetworkDelegate::OnResponseStarted(net::URLRequest* request) { + if (!ContainsKey(simple_listeners_, kOnResponseStarted)) { + brightray::NetworkDelegate::OnResponseStarted(request); + return; + } + + if (request->status().status() != net::URLRequestStatus::SUCCESS) + return; + + HandleSimpleEvent(kOnResponseStarted, request, request->response_headers(), + request->was_cached()); +} + +void AtomNetworkDelegate::OnCompleted(net::URLRequest* request, bool started) { + // OnCompleted may happen before other events. + callbacks_.erase(request->identifier()); + + if (request->status().status() == net::URLRequestStatus::FAILED || + request->status().status() == net::URLRequestStatus::CANCELED) { + // Error event. + OnErrorOccurred(request, started); + return; + } else if (request->response_headers() && + net::HttpResponseHeaders::IsRedirectResponseCode( + request->response_headers()->response_code())) { + // Redirect event. + brightray::NetworkDelegate::OnCompleted(request, started); + return; + } + + if (!ContainsKey(simple_listeners_, kOnCompleted)) { + brightray::NetworkDelegate::OnCompleted(request, started); + return; + } + + HandleSimpleEvent(kOnCompleted, request, request->response_headers(), + request->was_cached()); +} + +void AtomNetworkDelegate::OnURLRequestDestroyed(net::URLRequest* request) { + callbacks_.erase(request->identifier()); +} + +void AtomNetworkDelegate::OnErrorOccurred( + net::URLRequest* request, bool started) { + if (!ContainsKey(simple_listeners_, kOnErrorOccurred)) { + brightray::NetworkDelegate::OnCompleted(request, started); + return; + } + + HandleSimpleEvent(kOnErrorOccurred, request, request->was_cached(), + request->status()); +} + +template +int AtomNetworkDelegate::HandleResponseEvent( + ResponseEvent type, + net::URLRequest* request, + const net::CompletionCallback& callback, + Out out, + Args... args) { + const auto& info = response_listeners_[type]; + if (!MatchesFilterCondition(request, info.url_patterns)) + return net::OK; + + scoped_ptr details(new base::DictionaryValue); + FillDetailsObject(details.get(), request, args...); + + // The |request| could be destroyed before the |callback| is called. + callbacks_[request->identifier()] = callback; + + ResponseCallback response = + base::Bind(&AtomNetworkDelegate::OnListenerResultInUI, + base::Unretained(this), request->identifier(), out); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(RunResponseListener, info.listener, base::Passed(&details), + response)); + return net::ERR_IO_PENDING; +} + +template +void AtomNetworkDelegate::HandleSimpleEvent( + SimpleEvent type, net::URLRequest* request, Args... args) { + const auto& info = simple_listeners_[type]; + if (!MatchesFilterCondition(request, info.url_patterns)) + return; + + scoped_ptr details(new base::DictionaryValue); + FillDetailsObject(details.get(), request, args...); + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(RunSimpleListener, info.listener, base::Passed(&details))); +} + +template +void AtomNetworkDelegate::OnListenerResultInIO( + uint64_t id, T out, scoped_ptr response) { + // The request has been destroyed. + if (!ContainsKey(callbacks_, id)) + return; + + ReadFromResponseObject(*response.get(), out); + + bool cancel = false; + response->GetBoolean("cancel", &cancel); + callbacks_[id].Run(cancel ? net::ERR_BLOCKED_BY_CLIENT : net::OK); +} + +template +void AtomNetworkDelegate::OnListenerResultInUI( + uint64_t id, T out, const base::DictionaryValue& response) { + scoped_ptr copy = response.CreateDeepCopy(); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&AtomNetworkDelegate::OnListenerResultInIO, + base::Unretained(this), id, out, base::Passed(©))); +} + +} // namespace atom diff --git a/atom/browser/net/atom_network_delegate.h b/atom/browser/net/atom_network_delegate.h new file mode 100644 index 000000000..4f55f7c09 --- /dev/null +++ b/atom/browser/net/atom_network_delegate.h @@ -0,0 +1,121 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_NET_ATOM_NETWORK_DELEGATE_H_ +#define ATOM_BROWSER_NET_ATOM_NETWORK_DELEGATE_H_ + +#include +#include + +#include "brightray/browser/network_delegate.h" +#include "base/callback.h" +#include "base/values.h" +#include "extensions/common/url_pattern.h" +#include "net/base/net_errors.h" +#include "net/http/http_request_headers.h" +#include "net/http/http_response_headers.h" + +namespace extensions { +class URLPattern; +} + +namespace atom { + +using URLPatterns = std::set; + +class AtomNetworkDelegate : public brightray::NetworkDelegate { + public: + using ResponseCallback = base::Callback; + using SimpleListener = base::Callback; + using ResponseListener = base::Callback; + + enum SimpleEvent { + kOnSendHeaders, + kOnBeforeRedirect, + kOnResponseStarted, + kOnCompleted, + kOnErrorOccurred, + }; + + enum ResponseEvent { + kOnBeforeRequest, + kOnBeforeSendHeaders, + kOnHeadersReceived, + }; + + struct SimpleListenerInfo { + URLPatterns url_patterns; + SimpleListener listener; + }; + + struct ResponseListenerInfo { + URLPatterns url_patterns; + ResponseListener listener; + }; + + AtomNetworkDelegate(); + ~AtomNetworkDelegate() override; + + void SetSimpleListenerInIO(SimpleEvent type, + const URLPatterns& patterns, + const SimpleListener& callback); + void SetResponseListenerInIO(ResponseEvent type, + const URLPatterns& patterns, + const ResponseListener& callback); + + protected: + // net::NetworkDelegate: + 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; + void OnBeforeRedirect(net::URLRequest* request, + const GURL& new_location) override; + void OnResponseStarted(net::URLRequest* request) override; + void OnCompleted(net::URLRequest* request, bool started) override; + void OnURLRequestDestroyed(net::URLRequest* request) override; + + private: + void OnErrorOccurred(net::URLRequest* request, bool started); + + template + void HandleSimpleEvent(SimpleEvent type, + net::URLRequest* request, + Args... args); + template + int HandleResponseEvent(ResponseEvent type, + net::URLRequest* request, + const net::CompletionCallback& callback, + Out out, + Args... args); + + // Deal with the results of Listener. + template + void OnListenerResultInIO( + uint64_t id, T out, scoped_ptr response); + template + void OnListenerResultInUI( + uint64_t id, T out, const base::DictionaryValue& response); + + std::map simple_listeners_; + std::map response_listeners_; + std::map callbacks_; + + DISALLOW_COPY_AND_ASSIGN(AtomNetworkDelegate); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_NET_ATOM_NETWORK_DELEGATE_H_ diff --git a/atom/browser/net/url_request_async_asar_job.cc b/atom/browser/net/url_request_async_asar_job.cc index 303bfc017..3578f3b79 100644 --- a/atom/browser/net/url_request_async_asar_job.cc +++ b/atom/browser/net/url_request_async_asar_job.cc @@ -4,6 +4,10 @@ #include "atom/browser/net/url_request_async_asar_job.h" +#include + +#include "atom/common/atom_constants.h" + namespace atom { URLRequestAsyncAsarJob::URLRequestAsyncAsarJob( @@ -34,4 +38,12 @@ void URLRequestAsyncAsarJob::StartAsync(scoped_ptr options) { } } +void URLRequestAsyncAsarJob::GetResponseInfo(net::HttpResponseInfo* info) { + std::string status("HTTP/1.1 200 OK"); + net::HttpResponseHeaders* headers = new net::HttpResponseHeaders(status); + + headers->AddHeader(kCORSHeader); + info->headers = headers; +} + } // namespace atom diff --git a/atom/browser/net/url_request_async_asar_job.h b/atom/browser/net/url_request_async_asar_job.h index df1aed350..d65142f0b 100644 --- a/atom/browser/net/url_request_async_asar_job.h +++ b/atom/browser/net/url_request_async_asar_job.h @@ -18,6 +18,9 @@ class URLRequestAsyncAsarJob : public JsAsker { // JsAsker: void StartAsync(scoped_ptr options) override; + // URLRequestJob: + void GetResponseInfo(net::HttpResponseInfo* info) override; + private: DISALLOW_COPY_AND_ASSIGN(URLRequestAsyncAsarJob); }; diff --git a/atom/browser/net/url_request_buffer_job.cc b/atom/browser/net/url_request_buffer_job.cc index affc3dd37..aa273bf81 100644 --- a/atom/browser/net/url_request_buffer_job.cc +++ b/atom/browser/net/url_request_buffer_job.cc @@ -6,6 +6,7 @@ #include +#include "atom/common/atom_constants.h" #include "base/strings/string_number_conversions.h" #include "net/base/net_errors.h" @@ -50,6 +51,8 @@ void URLRequestBufferJob::GetResponseInfo(net::HttpResponseInfo* info) { status.append("\0\0", 2); net::HttpResponseHeaders* headers = new net::HttpResponseHeaders(status); + headers->AddHeader(kCORSHeader); + if (!mime_type_.empty()) { std::string content_type_header(net::HttpRequestHeaders::kContentType); content_type_header.append(": "); diff --git a/atom/browser/net/url_request_fetch_job.cc b/atom/browser/net/url_request_fetch_job.cc index a8a16e286..6f4182901 100644 --- a/atom/browser/net/url_request_fetch_job.cc +++ b/atom/browser/net/url_request_fetch_job.cc @@ -14,6 +14,7 @@ #include "net/http/http_response_headers.h" #include "net/url_request/url_fetcher.h" #include "net/url_request/url_fetcher_response_writer.h" +#include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_status.h" @@ -23,7 +24,7 @@ namespace { // Convert string to RequestType. net::URLFetcher::RequestType GetRequestType(const std::string& raw) { - std::string method = base::StringToUpperASCII(raw); + std::string method = base::ToUpperASCII(raw); if (method.empty() || method == "GET") return net::URLFetcher::GET; else if (method == "POST") @@ -89,12 +90,14 @@ void URLRequestFetchJob::StartAsync(scoped_ptr options) { std::string url, method, referrer; base::Value* session = nullptr; + base::DictionaryValue* upload_data = nullptr; base::DictionaryValue* dict = static_cast(options.get()); dict->GetString("url", &url); dict->GetString("method", &method); dict->GetString("referrer", &referrer); dict->Get("session", &session); + dict->GetDictionary("uploadData", &upload_data); // Check if URL is valid. GURL formated_url(url); @@ -126,6 +129,14 @@ void URLRequestFetchJob::StartAsync(scoped_ptr options) { else fetcher_->SetReferrer(referrer); + // Set the data needed for POSTs. + if (upload_data && request_type == net::URLFetcher::POST) { + std::string content_type, data; + upload_data->GetString("contentType", &content_type); + upload_data->GetString("data", &data); + fetcher_->SetUploadData(content_type, data); + } + // Use |request|'s headers. fetcher_->SetExtraRequestHeaders( request()->extra_request_headers().ToString()); @@ -138,8 +149,9 @@ net::URLRequestContextGetter* URLRequestFetchJob::CreateRequestContext() { auto task_runner = base::ThreadTaskRunnerHandle::Get(); net::URLRequestContextBuilder builder; builder.set_proxy_service(net::ProxyService::CreateDirect()); - url_request_context_getter_ = - new net::TrivialURLRequestContextGetter(builder.Build(), task_runner); + request_context_ = builder.Build(); + url_request_context_getter_ = new net::TrivialURLRequestContextGetter( + request_context_.get(), task_runner); } return url_request_context_getter_.get(); } @@ -181,6 +193,11 @@ void URLRequestFetchJob::Kill() { bool URLRequestFetchJob::ReadRawData(net::IOBuffer* dest, int dest_size, int* bytes_read) { + if (GetResponseCode() == 204) { + *bytes_read = 0; + request()->set_received_response_content_length(prefilter_bytes_read()); + return true; + } pending_buffer_ = dest; pending_buffer_size_ = dest_size; SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0)); @@ -188,7 +205,7 @@ bool URLRequestFetchJob::ReadRawData(net::IOBuffer* dest, } bool URLRequestFetchJob::GetMimeType(std::string* mime_type) const { - if (!response_info_) + if (!response_info_ || !response_info_->headers) return false; return response_info_->headers->GetMimeType(mime_type); @@ -200,13 +217,21 @@ void URLRequestFetchJob::GetResponseInfo(net::HttpResponseInfo* info) { } int URLRequestFetchJob::GetResponseCode() const { - if (!response_info_) + if (!response_info_ || !response_info_->headers) return -1; return response_info_->headers->response_code(); } void URLRequestFetchJob::OnURLFetchComplete(const net::URLFetcher* source) { + if (!response_info_) { + // Since we notify header completion only after first write there will be + // no response object constructed for http respones with no content 204. + // We notify header completion here. + HeadersCompleted(); + return; + } + pending_buffer_ = nullptr; pending_buffer_size_ = 0; NotifyDone(fetcher_->GetStatus()); diff --git a/atom/browser/net/url_request_fetch_job.h b/atom/browser/net/url_request_fetch_job.h index 189cebf01..399f78ae3 100644 --- a/atom/browser/net/url_request_fetch_job.h +++ b/atom/browser/net/url_request_fetch_job.h @@ -45,6 +45,7 @@ class URLRequestFetchJob : public JsAsker, // Create a independent request context. net::URLRequestContextGetter* CreateRequestContext(); + scoped_ptr request_context_; scoped_refptr url_request_context_getter_; scoped_ptr fetcher_; scoped_refptr pending_buffer_; diff --git a/atom/browser/net/url_request_string_job.cc b/atom/browser/net/url_request_string_job.cc index 4a631b813..606781142 100644 --- a/atom/browser/net/url_request_string_job.cc +++ b/atom/browser/net/url_request_string_job.cc @@ -6,6 +6,7 @@ #include +#include "atom/common/atom_constants.h" #include "net/base/net_errors.h" namespace atom { @@ -32,6 +33,8 @@ void URLRequestStringJob::GetResponseInfo(net::HttpResponseInfo* info) { std::string status("HTTP/1.1 200 OK"); net::HttpResponseHeaders* headers = new net::HttpResponseHeaders(status); + headers->AddHeader(kCORSHeader); + if (!mime_type_.empty()) { std::string content_type_header(net::HttpRequestHeaders::kContentType); content_type_header.append(": "); diff --git a/atom/browser/resources/mac/Info.plist b/atom/browser/resources/mac/Info.plist index 481945b8e..2f321fa8f 100644 --- a/atom/browser/resources/mac/Info.plist +++ b/atom/browser/resources/mac/Info.plist @@ -17,9 +17,9 @@ CFBundleIconFile atom.icns CFBundleVersion - 0.35.1 + 0.36.2 CFBundleShortVersionString - 0.35.1 + 0.36.2 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion diff --git a/atom/browser/resources/win/atom.rc b/atom/browser/resources/win/atom.rc index 875727cda..225d910bc 100644 --- a/atom/browser/resources/win/atom.rc +++ b/atom/browser/resources/win/atom.rc @@ -56,8 +56,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,35,1,0 - PRODUCTVERSION 0,35,1,0 + FILEVERSION 0,36,2,0 + PRODUCTVERSION 0,36,2,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -74,12 +74,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "0.35.1" + VALUE "FileVersion", "0.36.2" VALUE "InternalName", "electron.exe" VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "OriginalFilename", "electron.exe" VALUE "ProductName", "Electron" - VALUE "ProductVersion", "0.35.1" + VALUE "ProductVersion", "0.36.2" VALUE "SquirrelAwareVersion", "1" END END diff --git a/atom/browser/ui/accelerator_util.cc b/atom/browser/ui/accelerator_util.cc index e25e14b79..a0b90e0c7 100644 --- a/atom/browser/ui/accelerator_util.cc +++ b/atom/browser/ui/accelerator_util.cc @@ -24,10 +24,10 @@ bool StringToAccelerator(const std::string& description, LOG(ERROR) << "The accelerator string can only contain ASCII characters"; return false; } - std::string shortcut(base::StringToLowerASCII(description)); + std::string shortcut(base::ToLowerASCII(description)); - std::vector tokens; - base::SplitString(shortcut, '+', &tokens); + std::vector tokens = base::SplitString( + shortcut, "+", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); // Now, parse it into an accelerator. int modifiers = ui::EF_NONE; diff --git a/atom/browser/ui/cocoa/atom_menu_controller.mm b/atom/browser/ui/cocoa/atom_menu_controller.mm index e3aa78aa2..9c8c99da9 100644 --- a/atom/browser/ui/cocoa/atom_menu_controller.mm +++ b/atom/browser/ui/cocoa/atom_menu_controller.mm @@ -148,10 +148,11 @@ Role kRolesMap[] = { // Set submenu's role. base::string16 role = model->GetRoleAt(index); - if (role == base::ASCIIToUTF16("window")) + if (role == base::ASCIIToUTF16("window") && [submenu numberOfItems]) [NSApp setWindowsMenu:submenu]; else if (role == base::ASCIIToUTF16("help")) [NSApp setHelpMenu:submenu]; + if (role == base::ASCIIToUTF16("services")) [NSApp setServicesMenu:submenu]; } else { diff --git a/atom/browser/ui/cocoa/event_processing_window.h b/atom/browser/ui/cocoa/event_processing_window.h deleted file mode 100644 index 88242711f..000000000 --- a/atom/browser/ui/cocoa/event_processing_window.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2013 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef ATOM_BROWSER_UI_COCOA_EVENT_PROCESSING_WINDOW_H_ -#define ATOM_BROWSER_UI_COCOA_EVENT_PROCESSING_WINDOW_H_ - -#import - -// Override NSWindow to access unhandled keyboard events (for command -// processing); subclassing NSWindow is the only method to do -// this. -@interface EventProcessingWindow : NSWindow { - @private - BOOL redispatchingEvent_; - BOOL eventHandled_; -} - -// Sends a key event to |NSApp sendEvent:|, but also makes sure that it's not -// short-circuited to the RWHV. This is used to send keyboard events to the menu -// and the cmd-` handler if a keyboard event comes back unhandled from the -// renderer. The event must be of type |NSKeyDown|, |NSKeyUp|, or -// |NSFlagsChanged|. -// Returns |YES| if |event| has been handled. -- (BOOL)redispatchKeyEvent:(NSEvent*)event; - -- (BOOL)performKeyEquivalent:(NSEvent*)theEvent; -@end - -#endif // ATOM_BROWSER_UI_COCOA_EVENT_PROCESSING_WINDOW_H_ diff --git a/atom/browser/ui/cocoa/event_processing_window.mm b/atom/browser/ui/cocoa/event_processing_window.mm deleted file mode 100644 index d47cdf37b..000000000 --- a/atom/browser/ui/cocoa/event_processing_window.mm +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) 2013 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#import "atom/browser/ui/cocoa/event_processing_window.h" - -#include "base/logging.h" -#import "content/public/browser/render_widget_host_view_mac_base.h" - -@interface EventProcessingWindow () -// Duplicate the given key event, but changing the associated window. -- (NSEvent*)keyEventForWindow:(NSWindow*)window fromKeyEvent:(NSEvent*)event; -@end - -@implementation EventProcessingWindow - -- (BOOL)redispatchKeyEvent:(NSEvent*)event { - DCHECK(event); - NSEventType eventType = [event type]; - if (eventType != NSKeyDown && - eventType != NSKeyUp && - eventType != NSFlagsChanged) { - NOTREACHED(); - return YES; // Pretend it's been handled in an effort to limit damage. - } - - // Ordinarily, the event's window should be this window. However, when - // switching between normal and fullscreen mode, we switch out the window, and - // the event's window might be the previous window (or even an earlier one if - // the renderer is running slowly and several mode switches occur). In this - // rare case, we synthesize a new key event so that its associate window - // (number) is our own. - if ([event window] != self) - event = [self keyEventForWindow:self fromKeyEvent:event]; - - // Redispatch the event. - eventHandled_ = YES; - redispatchingEvent_ = YES; - [NSApp sendEvent:event]; - redispatchingEvent_ = NO; - - // If the event was not handled by [NSApp sendEvent:], the sendEvent: - // method below will be called, and because |redispatchingEvent_| is YES, - // |eventHandled_| will be set to NO. - return eventHandled_; -} - -- (void)sendEvent:(NSEvent*)event { - if (!redispatchingEvent_) - [super sendEvent:event]; - else - eventHandled_ = NO; -} - -- (NSEvent*)keyEventForWindow:(NSWindow*)window fromKeyEvent:(NSEvent*)event { - NSEventType eventType = [event type]; - - // Convert the event's location from the original window's coordinates into - // our own. - NSPoint eventLoc = [event locationInWindow]; - eventLoc = [self convertRectFromScreen: - [[event window] convertRectToScreen:NSMakeRect(eventLoc.x, eventLoc.y, 0, 0)]].origin; - - // Various things *only* apply to key down/up. - BOOL eventIsARepeat = NO; - NSString* eventCharacters = nil; - NSString* eventUnmodCharacters = nil; - if (eventType == NSKeyDown || eventType == NSKeyUp) { - eventIsARepeat = [event isARepeat]; - eventCharacters = [event characters]; - eventUnmodCharacters = [event charactersIgnoringModifiers]; - } - - // This synthesis may be slightly imperfect: we provide nil for the context, - // since I (viettrungluu) am sceptical that putting in the original context - // (if one is given) is valid. - return [NSEvent keyEventWithType:eventType - location:eventLoc - modifierFlags:[event modifierFlags] - timestamp:[event timestamp] - windowNumber:[window windowNumber] - context:nil - characters:eventCharacters - charactersIgnoringModifiers:eventUnmodCharacters - isARepeat:eventIsARepeat - keyCode:[event keyCode]]; -} - - -- (BOOL)performKeyEquivalent:(NSEvent*)event { - if (redispatchingEvent_) - return NO; - - // Give the web site a chance to handle the event. If it doesn't want to - // handle it, it will call us back with one of the |handle*| methods above. - NSResponder* r = [self firstResponder]; - if ([r conformsToProtocol:@protocol(RenderWidgetHostViewMacBase)]) - return [r performKeyEquivalent:event]; - - if ([super performKeyEquivalent:event]) - return YES; - - return NO; -} - -@end // EventProcessingWindow diff --git a/atom/browser/ui/file_dialog_gtk.cc b/atom/browser/ui/file_dialog_gtk.cc index 5885ffe36..ed7944965 100644 --- a/atom/browser/ui/file_dialog_gtk.cc +++ b/atom/browser/ui/file_dialog_gtk.cc @@ -22,7 +22,9 @@ gboolean FileFilterCaseInsensitive(const GtkFileFilterInfo* file_info, // Makes .* file extension matches all file types. if (*file_extension == ".*") return true; - return base::EndsWith(file_info->filename, *file_extension, false); + return base::EndsWith( + file_info->filename, + *file_extension, base::CompareCase::INSENSITIVE_ASCII); } // Deletes |data| when gtk_file_filter_add_custom() is done with it. diff --git a/atom/browser/ui/file_dialog_mac.mm b/atom/browser/ui/file_dialog_mac.mm index 1cbe46e3b..49662c69b 100644 --- a/atom/browser/ui/file_dialog_mac.mm +++ b/atom/browser/ui/file_dialog_mac.mm @@ -34,7 +34,13 @@ void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) { [file_type_set addObject:base::mac::CFToNSCast(ext_cf.get())]; } } - [dialog setAllowedFileTypes:[file_type_set allObjects]]; + + // Passing empty array to setAllowedFileTypes will cause exception. + NSArray* file_types = nil; + if ([file_type_set count]) + file_types = [file_type_set allObjects]; + + [dialog setAllowedFileTypes:file_types]; } void SetupDialog(NSSavePanel* dialog, diff --git a/atom/browser/ui/file_dialog_win.cc b/atom/browser/ui/file_dialog_win.cc index d218beaa2..6577e4c08 100644 --- a/atom/browser/ui/file_dialog_win.cc +++ b/atom/browser/ui/file_dialog_win.cc @@ -51,7 +51,7 @@ void ConvertFilters(const Filters& filters, std::vector extensions(filter.second); for (size_t j = 0; j < extensions.size(); ++j) extensions[j].insert(0, "*."); - buffer->push_back(base::UTF8ToWide(JoinString(extensions, ";"))); + buffer->push_back(base::UTF8ToWide(base::JoinString(extensions, ";"))); spec.pszSpec = buffer->back().c_str(); filterspec->push_back(spec); @@ -262,28 +262,7 @@ bool ShowSaveDialog(atom::NativeWindow* parent_window, if (FAILED(hr)) return false; - std::string file_name = base::WideToUTF8(std::wstring(buffer)); - - // Append extension according to selected filter. - if (!filters.empty()) { - UINT filter_index = 1; - save_dialog.GetPtr()->GetFileTypeIndex(&filter_index); - const Filter& filter = filters[filter_index - 1]; - - bool matched = false; - for (size_t i = 0; i < filter.second.size(); ++i) { - if (filter.second[i] == "*" || - base::EndsWith(file_name, filter.second[i], false)) { - matched = true; - break;; - } - } - - if (!matched && !filter.second.empty()) - file_name += ("." + filter.second[0]); - } - - *path = base::FilePath(base::UTF8ToUTF16(file_name)); + *path = base::FilePath(buffer); return true; } diff --git a/atom/browser/ui/message_box_gtk.cc b/atom/browser/ui/message_box_gtk.cc index 41682190e..de8d994e5 100644 --- a/atom/browser/ui/message_box_gtk.cc +++ b/atom/browser/ui/message_box_gtk.cc @@ -92,7 +92,7 @@ class GtkMessageBox { } const char* TranslateToStock(int id, const std::string& text) { - std::string lower = base::StringToLowerASCII(text); + std::string lower = base::ToLowerASCII(text); if (lower == "cancel") return GTK_STOCK_CANCEL; else if (lower == "no") diff --git a/atom/browser/ui/message_box_win.cc b/atom/browser/ui/message_box_win.cc index 697a7ad41..656be9f10 100644 --- a/atom/browser/ui/message_box_win.cc +++ b/atom/browser/ui/message_box_win.cc @@ -34,7 +34,7 @@ struct CommonButtonID { int id; }; CommonButtonID GetCommonID(const base::string16& button) { - base::string16 lower = base::StringToLowerASCII(button); + base::string16 lower = base::ToLowerASCII(button); if (lower == L"ok") return { TDCBF_OK_BUTTON, IDOK }; else if (lower == L"yes") diff --git a/atom/browser/ui/tray_icon.cc b/atom/browser/ui/tray_icon.cc index 1696aab27..60923c2ad 100644 --- a/atom/browser/ui/tray_icon.cc +++ b/atom/browser/ui/tray_icon.cc @@ -26,7 +26,8 @@ void TrayIcon::DisplayBalloon(const gfx::Image& icon, const base::string16& contents) { } -void TrayIcon::PopUpContextMenu(const gfx::Point& pos) { +void TrayIcon::PopUpContextMenu(const gfx::Point& pos, + ui::SimpleMenuModel* menu_model) { } void TrayIcon::NotifyClicked(const gfx::Rect& bounds, int modifiers) { diff --git a/atom/browser/ui/tray_icon.h b/atom/browser/ui/tray_icon.h index bc29acd8a..c80ff08d6 100644 --- a/atom/browser/ui/tray_icon.h +++ b/atom/browser/ui/tray_icon.h @@ -47,7 +47,9 @@ class TrayIcon { const base::string16& title, const base::string16& contents); - virtual void PopUpContextMenu(const gfx::Point& pos); + // Popups the menu. + virtual void PopUpContextMenu(const gfx::Point& pos, + ui::SimpleMenuModel* menu_model); // Set the context menu for this icon. virtual void SetContextMenu(ui::SimpleMenuModel* menu_model) = 0; diff --git a/atom/browser/ui/tray_icon_cocoa.h b/atom/browser/ui/tray_icon_cocoa.h index 7781c93a1..59e2241aa 100644 --- a/atom/browser/ui/tray_icon_cocoa.h +++ b/atom/browser/ui/tray_icon_cocoa.h @@ -29,7 +29,8 @@ class TrayIconCocoa : public TrayIcon, void SetToolTip(const std::string& tool_tip) override; void SetTitle(const std::string& title) override; void SetHighlightMode(bool highlight) override; - void PopUpContextMenu(const gfx::Point& pos) override; + void PopUpContextMenu(const gfx::Point& pos, + ui::SimpleMenuModel* menu_model) override; void SetContextMenu(ui::SimpleMenuModel* menu_model) override; protected: diff --git a/atom/browser/ui/tray_icon_cocoa.mm b/atom/browser/ui/tray_icon_cocoa.mm index e25f8ab5c..d2a2fe834 100644 --- a/atom/browser/ui/tray_icon_cocoa.mm +++ b/atom/browser/ui/tray_icon_cocoa.mm @@ -23,6 +23,7 @@ const CGFloat kVerticalTitleMargin = 2; atom::TrayIconCocoa* trayIcon_; // weak AtomMenuController* menuController_; // weak BOOL isHighlightEnable_; + BOOL forceHighlight_; BOOL inMouseEventSequence_; base::scoped_nsobject image_; base::scoped_nsobject alternateImage_; @@ -39,6 +40,8 @@ const CGFloat kVerticalTitleMargin = 2; image_.reset([image copy]); trayIcon_ = icon; isHighlightEnable_ = YES; + forceHighlight_ = NO; + inMouseEventSequence_ = NO; if ((self = [super initWithFrame: CGRectZero])) { // Setup the image view. @@ -89,7 +92,7 @@ const CGFloat kVerticalTitleMargin = 2; // Make use of NSImageView to draw the image, which can correctly draw // template image under dark menu bar. - if (highlight && alternateImage_ && + if (inMouseEventSequence_ && alternateImage_ && [image_view_ image] != alternateImage_.get()) { [image_view_ setImage:alternateImage_]; } else if ([image_view_ image] != image_.get()) { @@ -238,7 +241,19 @@ const CGFloat kVerticalTitleMargin = 2; [self setNeedsDisplay:YES]; } -- (void)popUpContextMenu { +- (void)popUpContextMenu:(ui::SimpleMenuModel*)menu_model { + // Show a custom menu. + if (menu_model) { + base::scoped_nsobject menuController( + [[AtomMenuController alloc] initWithModel:menu_model]); + forceHighlight_ = YES; // Should highlight when showing menu. + [self setNeedsDisplay:YES]; + [statusItem_ popUpStatusItemMenu:[menuController menu]]; + forceHighlight_ = NO; + [self setNeedsDisplay:YES]; + return; + } + if (menuController_ && ![menuController_ isMenuOpen]) { // Redraw the dray icon to show highlight if it is enabled. [self setNeedsDisplay:YES]; @@ -266,14 +281,14 @@ const CGFloat kVerticalTitleMargin = 2; - (void)draggingEnded:(id )sender { trayIcon_->NotifyDragEnded(); + + if (NSPointInRect([sender draggingLocation], self.frame)) { + trayIcon_->NotifyDrop(); + [self handleDrop:sender]; + } } -- (BOOL)prepareForDragOperation:(id )sender { - trayIcon_->NotifyDrop(); - return YES; -} - -- (BOOL)performDragOperation:(id )sender { +- (BOOL)handleDrop:(id )sender { NSPasteboard* pboard = [sender draggingPasteboard]; if ([[pboard types] containsObject:NSFilenamesPboardType]) { @@ -287,7 +302,17 @@ const CGFloat kVerticalTitleMargin = 2; return NO; } +- (BOOL)prepareForDragOperation:(id )sender { + return YES; +} + +- (BOOL)performDragOperation:(id )sender { + return YES; +} + - (BOOL)shouldHighlight { + if (isHighlightEnable_ && forceHighlight_) + return true; BOOL isMenuOpen = menuController_ && [menuController_ isMenuOpen]; return isHighlightEnable_ && (inMouseEventSequence_ || isMenuOpen); } @@ -338,8 +363,9 @@ void TrayIconCocoa::SetHighlightMode(bool highlight) { [status_item_view_ setHighlight:highlight]; } -void TrayIconCocoa::PopUpContextMenu(const gfx::Point& pos) { - [status_item_view_ popUpContextMenu]; +void TrayIconCocoa::PopUpContextMenu(const gfx::Point& pos, + ui::SimpleMenuModel* menu_model) { + [status_item_view_ popUpContextMenu:menu_model]; } void TrayIconCocoa::SetContextMenu(ui::SimpleMenuModel* menu_model) { diff --git a/atom/browser/ui/views/menu_bar.cc b/atom/browser/ui/views/menu_bar.cc index d3059a50a..ba0a542c1 100644 --- a/atom/browser/ui/views/menu_bar.cc +++ b/atom/browser/ui/views/menu_bar.cc @@ -17,7 +17,6 @@ #if defined(OS_WIN) #include "ui/gfx/color_utils.h" #elif defined(USE_X11) -#include "chrome/browser/ui/libgtk2ui/owned_widget_gtk2.h" #include "chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h" #endif @@ -33,15 +32,16 @@ const SkColor kDefaultColor = SkColorSetARGB(255, 233, 233, 233); #if defined(USE_X11) void GetMenuBarColor(SkColor* enabled, SkColor* disabled, SkColor* highlight, SkColor* hover, SkColor* background) { - libgtk2ui::OwnedWidgetGtk fake_menu_bar; - fake_menu_bar.Own(gtk_menu_bar_new()); + GtkWidget* menu_bar = gtk_menu_bar_new(); - GtkStyle* style = gtk_rc_get_style(fake_menu_bar.get()); + GtkStyle* style = gtk_rc_get_style(menu_bar); *enabled = libgtk2ui::GdkColorToSkColor(style->fg[GTK_STATE_NORMAL]); *disabled = libgtk2ui::GdkColorToSkColor(style->fg[GTK_STATE_INSENSITIVE]); *highlight = libgtk2ui::GdkColorToSkColor(style->fg[GTK_STATE_SELECTED]); *hover = libgtk2ui::GdkColorToSkColor(style->fg[GTK_STATE_PRELIGHT]); *background = libgtk2ui::GdkColorToSkColor(style->bg[GTK_STATE_NORMAL]); + + gtk_widget_destroy(menu_bar); } #endif @@ -155,8 +155,8 @@ void MenuBar::OnMenuButtonClicked(views::View* source, if (type != ui::MenuModel::TYPE_SUBMENU) return; - menu_delegate_.reset(new MenuDelegate(this)); - menu_delegate_->RunMenu(menu_model_->GetSubmenuModelAt(id), button); + MenuDelegate menu_delegate(this); + menu_delegate.RunMenu(menu_model_->GetSubmenuModelAt(id), button); } } // namespace atom diff --git a/atom/browser/ui/views/menu_bar.h b/atom/browser/ui/views/menu_bar.h index ac82711f8..9d77cfdf2 100644 --- a/atom/browser/ui/views/menu_bar.h +++ b/atom/browser/ui/views/menu_bar.h @@ -71,7 +71,6 @@ class MenuBar : public views::View, #endif ui::MenuModel* menu_model_; - scoped_ptr menu_delegate_; DISALLOW_COPY_AND_ASSIGN(MenuBar); }; diff --git a/atom/browser/ui/views/menu_delegate.cc b/atom/browser/ui/views/menu_delegate.cc index 84c35d9cd..8ef8bca72 100644 --- a/atom/browser/ui/views/menu_delegate.cc +++ b/atom/browser/ui/views/menu_delegate.cc @@ -5,7 +5,7 @@ #include "atom/browser/ui/views/menu_delegate.h" #include "atom/browser/ui/views/menu_bar.h" -#include "base/stl_util.h" +#include "content/public/browser/browser_thread.h" #include "ui/views/controls/button/menu_button.h" #include "ui/views/controls/menu/menu_item_view.h" #include "ui/views/controls/menu/menu_model_adapter.h" @@ -16,13 +16,10 @@ namespace atom { MenuDelegate::MenuDelegate(MenuBar* menu_bar) : menu_bar_(menu_bar), - id_(-1), - items_(menu_bar_->GetItemCount()), - delegates_(menu_bar_->GetItemCount()) { + id_(-1) { } MenuDelegate::~MenuDelegate() { - STLDeleteElements(&delegates_); } void MenuDelegate::RunMenu(ui::MenuModel* model, views::MenuButton* button) { @@ -33,12 +30,15 @@ void MenuDelegate::RunMenu(ui::MenuModel* model, views::MenuButton* button) { button->height() - 1); id_ = button->tag(); - views::MenuItemView* item = BuildMenu(model); + adapter_.reset(new views::MenuModelAdapter(model)); - views::MenuRunner menu_runner( + views::MenuItemView* item = new views::MenuItemView(this); + static_cast(adapter_.get())->BuildMenu(item); + + menu_runner_.reset(new views::MenuRunner( item, - views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS); - ignore_result(menu_runner.RunMenuAt( + views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS)); + ignore_result(menu_runner_->RunMenuAt( button->GetWidget()->GetTopLevelWidget(), button, bounds, @@ -46,68 +46,53 @@ void MenuDelegate::RunMenu(ui::MenuModel* model, views::MenuButton* button) { ui::MENU_SOURCE_MOUSE)); } -views::MenuItemView* MenuDelegate::BuildMenu(ui::MenuModel* model) { - DCHECK_GE(id_, 0); - - if (!items_[id_]) { - views::MenuModelAdapter* delegate = new views::MenuModelAdapter(model); - delegates_[id_] = delegate; - - views::MenuItemView* item = new views::MenuItemView(this); - delegate->BuildMenu(item); - items_[id_] = item; - } - - return items_[id_]; -} - void MenuDelegate::ExecuteCommand(int id) { - delegate()->ExecuteCommand(id); + adapter_->ExecuteCommand(id); } void MenuDelegate::ExecuteCommand(int id, int mouse_event_flags) { - delegate()->ExecuteCommand(id, mouse_event_flags); + adapter_->ExecuteCommand(id, mouse_event_flags); } bool MenuDelegate::IsTriggerableEvent(views::MenuItemView* source, const ui::Event& e) { - return delegate()->IsTriggerableEvent(source, e); + return adapter_->IsTriggerableEvent(source, e); } bool MenuDelegate::GetAccelerator(int id, ui::Accelerator* accelerator) const { - return delegate()->GetAccelerator(id, accelerator); + return adapter_->GetAccelerator(id, accelerator); } base::string16 MenuDelegate::GetLabel(int id) const { - return delegate()->GetLabel(id); + return adapter_->GetLabel(id); } const gfx::FontList* MenuDelegate::GetLabelFontList(int id) const { - return delegate()->GetLabelFontList(id); + return adapter_->GetLabelFontList(id); } bool MenuDelegate::IsCommandEnabled(int id) const { - return delegate()->IsCommandEnabled(id); + return adapter_->IsCommandEnabled(id); } bool MenuDelegate::IsCommandVisible(int id) const { - return delegate()->IsCommandVisible(id); + return adapter_->IsCommandVisible(id); } bool MenuDelegate::IsItemChecked(int id) const { - return delegate()->IsItemChecked(id); + return adapter_->IsItemChecked(id); } void MenuDelegate::SelectionChanged(views::MenuItemView* menu) { - delegate()->SelectionChanged(menu); + adapter_->SelectionChanged(menu); } void MenuDelegate::WillShowMenu(views::MenuItemView* menu) { - delegate()->WillShowMenu(menu); + adapter_->WillShowMenu(menu); } void MenuDelegate::WillHideMenu(views::MenuItemView* menu) { - delegate()->WillHideMenu(menu); + adapter_->WillHideMenu(menu); } views::MenuItemView* MenuDelegate::GetSiblingMenu( @@ -115,16 +100,29 @@ views::MenuItemView* MenuDelegate::GetSiblingMenu( const gfx::Point& screen_point, views::MenuAnchorPosition* anchor, bool* has_mnemonics, - views::MenuButton** button) { + views::MenuButton**) { + views::MenuButton* button; ui::MenuModel* model; - if (!menu_bar_->GetMenuButtonFromScreenPoint(screen_point, &model, button)) - return NULL; + if (menu_bar_->GetMenuButtonFromScreenPoint(screen_point, &model, &button) && + button->tag() != id_) { + // Switch to sibling menu on next tick, otherwise crash may happen. + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&MenuDelegate::SwitchToSiblingMenu, + base::Unretained(this), button)); + } - *anchor = views::MENU_ANCHOR_TOPLEFT; - *has_mnemonics = true; + return nullptr; +} - id_ = (*button)->tag(); - return BuildMenu(model); +void MenuDelegate::SwitchToSiblingMenu(views::MenuButton* button) { + menu_runner_->Cancel(); + // After canceling the menu, we need to wait until next tick so we are out of + // nested message loop. + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(base::IgnoreResult(&views::MenuButton::Activate), + base::Unretained(button))); } } // namespace atom diff --git a/atom/browser/ui/views/menu_delegate.h b/atom/browser/ui/views/menu_delegate.h index a837e5d53..f83e5896c 100644 --- a/atom/browser/ui/views/menu_delegate.h +++ b/atom/browser/ui/views/menu_delegate.h @@ -5,12 +5,11 @@ #ifndef ATOM_BROWSER_UI_VIEWS_MENU_DELEGATE_H_ #define ATOM_BROWSER_UI_VIEWS_MENU_DELEGATE_H_ -#include - +#include "base/memory/scoped_ptr.h" #include "ui/views/controls/menu/menu_delegate.h" namespace views { -class MenuModelAdapter; +class MenuRunner; } namespace ui { @@ -51,20 +50,13 @@ class MenuDelegate : public views::MenuDelegate { views::MenuButton** button) override; private: - // Gets the cached menu item view from the model. - views::MenuItemView* BuildMenu(ui::MenuModel* model); - - // Returns delegate for current item. - views::MenuDelegate* delegate() const { return delegates_[id_]; } + // Close this menu and run the menu of |button|. + void SwitchToSiblingMenu(views::MenuButton* button); MenuBar* menu_bar_; - - // Current item's id. int id_; - // Cached menu items, managed by MenuRunner. - std::vector items_; - // Cached menu delegates for each menu item, managed by us. - std::vector delegates_; + scoped_ptr adapter_; + scoped_ptr menu_runner_; DISALLOW_COPY_AND_ASSIGN(MenuDelegate); }; diff --git a/atom/browser/ui/win/notify_icon.cc b/atom/browser/ui/win/notify_icon.cc index b2ca4bcee..1ac29f136 100644 --- a/atom/browser/ui/win/notify_icon.cc +++ b/atom/browser/ui/win/notify_icon.cc @@ -13,6 +13,7 @@ #include "ui/gfx/image/image.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/screen.h" #include "ui/views/controls/menu/menu_runner.h" namespace atom { @@ -45,8 +46,7 @@ NotifyIcon::~NotifyIcon() { Shell_NotifyIcon(NIM_DELETE, &icon_data); } -void NotifyIcon::HandleClickEvent(const gfx::Point& cursor_pos, - int modifiers, +void NotifyIcon::HandleClickEvent(int modifiers, bool left_mouse_click, bool double_button_click) { NOTIFYICONIDENTIFIER icon_id; @@ -66,7 +66,7 @@ void NotifyIcon::HandleClickEvent(const gfx::Point& cursor_pos, return; } else if (!double_button_click) { // single right click if (menu_model_) - PopUpContextMenu(cursor_pos); + PopUpContextMenu(gfx::Point(), menu_model_); else NotifyRightClicked(gfx::Rect(rect), modifiers); } @@ -142,24 +142,26 @@ void NotifyIcon::DisplayBalloon(const gfx::Image& icon, LOG(WARNING) << "Unable to create status tray balloon."; } -void NotifyIcon::PopUpContextMenu(const gfx::Point& pos) { +void NotifyIcon::PopUpContextMenu(const gfx::Point& pos, + ui::SimpleMenuModel* menu_model) { // Returns if context menu isn't set. - if (!menu_model_) + if (!menu_model) return; // Set our window as the foreground window, so the context menu closes when // we click away from it. if (!SetForegroundWindow(window_)) return; + // Show menu at mouse's position by default. + gfx::Rect rect(pos, gfx::Size()); + if (pos.IsOrigin()) + rect.set_origin(gfx::Screen::GetNativeScreen()->GetCursorScreenPoint()); + views::MenuRunner menu_runner( - menu_model_, + menu_model, views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS); ignore_result(menu_runner.RunMenuAt( - NULL, - NULL, - gfx::Rect(pos, gfx::Size()), - views::MENU_ANCHOR_TOPLEFT, - ui::MENU_SOURCE_MOUSE)); + NULL, NULL, rect, views::MENU_ANCHOR_TOPLEFT, ui::MENU_SOURCE_MOUSE)); } void NotifyIcon::SetContextMenu(ui::SimpleMenuModel* menu_model) { diff --git a/atom/browser/ui/win/notify_icon.h b/atom/browser/ui/win/notify_icon.h index d368dec71..23608c7c7 100644 --- a/atom/browser/ui/win/notify_icon.h +++ b/atom/browser/ui/win/notify_icon.h @@ -33,8 +33,7 @@ class NotifyIcon : public TrayIcon { // Handles a click event from the user - if |left_button_click| is true and // there is a registered observer, passes the click event to the observer, // otherwise displays the context menu if there is one. - void HandleClickEvent(const gfx::Point& cursor_pos, - int modifiers, + void HandleClickEvent(int modifiers, bool left_button_click, bool double_button_click); @@ -52,7 +51,8 @@ class NotifyIcon : public TrayIcon { void DisplayBalloon(const gfx::Image& icon, const base::string16& title, const base::string16& contents) override; - void PopUpContextMenu(const gfx::Point& pos) override; + void PopUpContextMenu(const gfx::Point& pos, + ui::SimpleMenuModel* menu_model) override; void SetContextMenu(ui::SimpleMenuModel* menu_model) override; private: diff --git a/atom/browser/ui/win/notify_icon_host.cc b/atom/browser/ui/win/notify_icon_host.cc index 2c84837e7..a0d4287ff 100644 --- a/atom/browser/ui/win/notify_icon_host.cc +++ b/atom/browser/ui/win/notify_icon_host.cc @@ -15,7 +15,6 @@ #include "base/win/win_util.h" #include "base/win/wrapped_window_proc.h" #include "ui/events/event_constants.h" -#include "ui/gfx/screen.h" #include "ui/gfx/win/hwnd_util.h" namespace atom { @@ -172,10 +171,7 @@ LRESULT CALLBACK NotifyIconHost::WndProc(HWND hwnd, case WM_CONTEXTMENU: // Walk our icons, find which one was clicked on, and invoke its // HandleClickEvent() method. - gfx::Point cursor_pos( - gfx::Screen::GetNativeScreen()->GetCursorScreenPoint()); win_icon->HandleClickEvent( - cursor_pos, GetKeyboardModifers(), (lparam == WM_LBUTTONDOWN || lparam == WM_LBUTTONDBLCLK), (lparam == WM_LBUTTONDBLCLK || lparam == WM_RBUTTONDBLCLK)); diff --git a/atom/browser/ui/x/x_window_utils.cc b/atom/browser/ui/x/x_window_utils.cc index f5c3f54ec..db83753bb 100644 --- a/atom/browser/ui/x/x_window_utils.cc +++ b/atom/browser/ui/x/x_window_utils.cc @@ -42,7 +42,7 @@ void SetWindowType(::Window xwindow, const std::string& type) { XDisplay* xdisplay = gfx::GetXDisplay(); std::string type_prefix = "_NET_WM_WINDOW_TYPE_"; ::Atom window_type = XInternAtom( - xdisplay, (type_prefix + base::StringToUpperASCII(type)).c_str(), False); + xdisplay, (type_prefix + base::ToUpperASCII(type)).c_str(), False); XChangeProperty(xdisplay, xwindow, XInternAtom(xdisplay, "_NET_WM_WINDOW_TYPE", False), XA_ATOM, diff --git a/atom/browser/web_contents_preferences.cc b/atom/browser/web_contents_preferences.cc index 83145368c..c75b0daaf 100644 --- a/atom/browser/web_contents_preferences.cc +++ b/atom/browser/web_contents_preferences.cc @@ -10,6 +10,7 @@ #include "atom/common/options_switches.h" #include "base/command_line.h" #include "base/strings/string_number_conversions.h" +#include "content/public/common/content_switches.h" #include "content/public/common/web_preferences.h" #include "native_mate/dictionary.h" #include "net/base/filename_util.h" @@ -22,30 +23,6 @@ DEFINE_WEB_CONTENTS_USER_DATA_KEY(atom::WebContentsPreferences); namespace atom { -namespace { - -// Array of available web runtime features. -struct FeaturePair { - const char* name; - const char* cmd; -}; -FeaturePair kWebRuntimeFeatures[] = { - { options::kExperimentalFeatures, - switches::kExperimentalFeatures }, - { options::kExperimentalCanvasFeatures, - switches::kExperimentalCanvasFeatures }, - { options::kOverlayScrollbars, - switches::kOverlayScrollbars }, - { options::kOverlayFullscreenVideo, - switches::kOverlayFullscreenVideo }, - { options::kSharedWorker, - switches::kSharedWorker }, - { options::kPageVisibility, - switches::kPageVisibility }, -}; - -} // namespace - WebContentsPreferences::WebContentsPreferences( content::WebContents* web_contents, const mate::Dictionary& web_preferences) { @@ -87,13 +64,12 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches( if (web_preferences.GetBoolean("plugins", &b) && b) command_line->AppendSwitch(switches::kEnablePlugins); - // This set of options are not availabe in WebPreferences, so we have to pass - // them via command line and enable them in renderer procss. - for (size_t i = 0; i < arraysize(kWebRuntimeFeatures); ++i) { - const auto& feature = kWebRuntimeFeatures[i]; - if (web_preferences.GetBoolean(feature.name, &b)) - command_line->AppendSwitchASCII(feature.cmd, b ? "true" : "false"); - } + // Experimental flags. + if (web_preferences.GetBoolean(options::kExperimentalFeatures, &b) && b) + command_line->AppendSwitch( + ::switches::kEnableExperimentalWebPlatformFeatures); + if (web_preferences.GetBoolean(options::kExperimentalCanvasFeatures, &b) && b) + command_line->AppendSwitch(::switches::kEnableExperimentalCanvasFeatures); // Check if we have node integration specified. bool node_integration = true; @@ -134,6 +110,18 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches( if (web_preferences.GetInteger(options::kGuestInstanceID, &guest_instance_id)) command_line->AppendSwitchASCII(switches::kGuestInstanceID, base::IntToString(guest_instance_id)); + + // Pass the opener's window id. + int opener_id; + if (web_preferences.GetInteger(options::kOpenerID, &opener_id)) + command_line->AppendSwitchASCII(switches::kOpenerID, + base::IntToString(opener_id)); + + // Enable blink features. + std::string blink_features; + if (web_preferences.GetString(options::kBlinkFeatures, &blink_features)) + command_line->AppendSwitchASCII(::switches::kEnableBlinkFeatures, + blink_features); } // static @@ -148,8 +136,6 @@ void WebContentsPreferences::OverrideWebkitPrefs( prefs->javascript_enabled = b; if (self->web_preferences_.GetBoolean("images", &b)) prefs->images_enabled = b; - if (self->web_preferences_.GetBoolean("java", &b)) - prefs->java_enabled = b; if (self->web_preferences_.GetBoolean("textAreasAreResizable", &b)) prefs->text_areas_are_resizable = b; if (self->web_preferences_.GetBoolean("webgl", &b)) diff --git a/atom/common/api/atom_api_asar.cc b/atom/common/api/atom_api_asar.cc index 4ea7d8c5c..7aee71fc3 100644 --- a/atom/common/api/atom_api_asar.cc +++ b/atom/common/api/atom_api_asar.cc @@ -18,6 +18,8 @@ namespace { +v8::Persistent template_; + class Archive : public mate::Wrappable { public: static v8::Local Create(v8::Isolate* isolate, @@ -101,15 +103,20 @@ class Archive : public mate::Wrappable { // mate::Wrappable: mate::ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) { - return mate::ObjectTemplateBuilder(isolate) - .SetValue("path", archive_->path()) - .SetMethod("getFileInfo", &Archive::GetFileInfo) - .SetMethod("stat", &Archive::Stat) - .SetMethod("readdir", &Archive::Readdir) - .SetMethod("realpath", &Archive::Realpath) - .SetMethod("copyFileOut", &Archive::CopyFileOut) - .SetMethod("getFd", &Archive::GetFD) - .SetMethod("destroy", &Archive::Destroy); + if (template_.IsEmpty()) + template_.Reset(isolate, mate::ObjectTemplateBuilder(isolate) + .SetValue("path", archive_->path()) + .SetMethod("getFileInfo", &Archive::GetFileInfo) + .SetMethod("stat", &Archive::Stat) + .SetMethod("readdir", &Archive::Readdir) + .SetMethod("realpath", &Archive::Realpath) + .SetMethod("copyFileOut", &Archive::CopyFileOut) + .SetMethod("getFd", &Archive::GetFD) + .SetMethod("destroy", &Archive::Destroy) + .Build()); + + return mate::ObjectTemplateBuilder( + isolate, v8::Local::New(isolate, template_)); } private: diff --git a/atom/common/api/atom_api_native_image.cc b/atom/common/api/atom_api_native_image.cc index e0f0940a7..a810069e7 100644 --- a/atom/common/api/atom_api_native_image.cc +++ b/atom/common/api/atom_api_native_image.cc @@ -63,7 +63,8 @@ float GetScaleFactorFromPath(const base::FilePath& path) { // We don't try to convert string to float here because it is very very // expensive. for (unsigned i = 0; i < arraysize(kScaleFactorPairs); ++i) { - if (base::EndsWith(filename, kScaleFactorPairs[i].name, true)) + if (base::EndsWith(filename, kScaleFactorPairs[i].name, + base::CompareCase::INSENSITIVE_ASCII)) return kScaleFactorPairs[i].scale; } diff --git a/atom/common/api/atom_api_v8_util.cc b/atom/common/api/atom_api_v8_util.cc index bba3399a8..c86335adb 100644 --- a/atom/common/api/atom_api_v8_util.cc +++ b/atom/common/api/atom_api_v8_util.cc @@ -2,22 +2,51 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. +#include +#include + #include "atom/common/api/object_life_monitor.h" #include "atom/common/node_includes.h" +#include "base/stl_util.h" #include "native_mate/dictionary.h" #include "v8/include/v8-profiler.h" namespace { +// A Persistent that can be copied and will not free itself. +template +struct LeakedPersistentTraits { + typedef v8::Persistent > LeakedPersistent; + static const bool kResetInDestructor = false; + template + static V8_INLINE void Copy(const v8::Persistent& source, + LeakedPersistent* dest) { + // do nothing, just allow copy + } +}; + +// The handles are leaked on purpose. +using FunctionTemplateHandle = + LeakedPersistentTraits::LeakedPersistent; +std::map function_templates_; + v8::Local CreateObjectWithName(v8::Isolate* isolate, - v8::Local name) { + const std::string& name) { + if (name == "Object") + return v8::Object::New(isolate); + + if (ContainsKey(function_templates_, name)) + return v8::Local::New( + isolate, function_templates_[name])->GetFunction()->NewInstance(); + v8::Local t = v8::FunctionTemplate::New(isolate); - t->SetClassName(name); + t->SetClassName(mate::StringToV8(isolate, name)); + function_templates_[name] = FunctionTemplateHandle(isolate, t); return t->GetFunction()->NewInstance(); } v8::Local GetHiddenValue(v8::Local object, - v8::Local key) { + v8::Local key) { return object->GetHiddenValue(key); } diff --git a/atom/common/api/event_emitter_caller.cc b/atom/common/api/event_emitter_caller.cc index 94eb9ce9e..4b44553d3 100644 --- a/atom/common/api/event_emitter_caller.cc +++ b/atom/common/api/event_emitter_caller.cc @@ -19,7 +19,7 @@ v8::Local CallEmitWithArgs(v8::Isolate* isolate, // Perform microtask checkpoint after running JavaScript. scoped_ptr script_scope( Locker::IsBrowserProcess() ? - nullptr : new blink::WebScopedRunV8Script(isolate)); + nullptr : new blink::WebScopedRunV8Script); // Use node::MakeCallback to call the callback, and it will also run pending // tasks in Node.js. return node::MakeCallback( diff --git a/atom/common/api/lib/crash-reporter.coffee b/atom/common/api/lib/crash-reporter.coffee index bd98ae2a4..544791b7d 100644 --- a/atom/common/api/lib/crash-reporter.coffee +++ b/atom/common/api/lib/crash-reporter.coffee @@ -19,8 +19,6 @@ class CrashReporter {app} = if process.type is 'browser' then electron else electron.remote @productName ?= app.getName() - companyName ?= 'GitHub, Inc' - submitURL ?= 'http://54.249.141.255:1127/post' autoSubmit ?= true ignoreSystemCrashHandler ?= false extra ?= {} @@ -29,6 +27,14 @@ class CrashReporter extra._companyName ?= companyName extra._version ?= app.getVersion() + unless companyName? + deprecate.log('companyName is now a required option to crashReporter.start') + return + + unless submitURL? + deprecate.log('submitURL is now a required option to crashReporter.start') + return + start = => binding.start @productName, companyName, submitURL, autoSubmit, ignoreSystemCrashHandler, extra if process.platform is 'win32' @@ -40,9 +46,7 @@ class CrashReporter env = ATOM_SHELL_INTERNAL_CRASH_SERVICE: 1 spawn process.execPath, args, {env, detached: true} - start() - else - start() + start() getLastCrashReport: -> reports = this.getUploadedReports() @@ -61,6 +65,5 @@ class CrashReporter path.join tmpdir, "#{@productName} Crashes", 'uploads.log' binding._getUploadedReports log - crashRepoter = new CrashReporter module.exports = crashRepoter diff --git a/atom/common/api/lib/deprecate.coffee b/atom/common/api/lib/deprecate.coffee index 1daf5e471..71c2ee5ca 100644 --- a/atom/common/api/lib/deprecate.coffee +++ b/atom/common/api/lib/deprecate.coffee @@ -52,9 +52,12 @@ deprecate.event = (emitter, oldName, newName, fn) -> else @emit oldName, args... -# Print deprecate warning. +# Print deprecation warning. deprecate.warn = (oldName, newName) -> - message = "#{oldName} is deprecated. Use #{newName} instead." + deprecate.log "#{oldName} is deprecated. Use #{newName} instead." + +# Print deprecation message. +deprecate.log = (message) -> if process.throwDeprecation throw new Error(message) else if process.traceDeprecation diff --git a/atom/common/api/lib/exports/electron.coffee b/atom/common/api/lib/exports/electron.coffee index e98144306..7598f9ee3 100644 --- a/atom/common/api/lib/exports/electron.coffee +++ b/atom/common/api/lib/exports/electron.coffee @@ -5,23 +5,25 @@ exports.hideInternalModules = -> # Remove the "common/api/lib" and "browser-or-renderer/api/lib". globalPaths.splice 0, 2 -Object.defineProperties exports, - # Common modules, please sort with alphabet order. - clipboard: - # Must be enumerable, otherwise it woulde be invisible to remote module. - enumerable: true - get: -> require '../clipboard' - crashReporter: - enumerable: true - get: -> require '../crash-reporter' - nativeImage: - enumerable: true - get: -> require '../native-image' - shell: - enumerable: true - get: -> require '../shell' - # The internal modules, invisible unless you know their names. - CallbacksRegistry: - get: -> require '../callbacks-registry' - deprecate: - get: -> require '../deprecate' +# Attaches properties to |exports|. +exports.defineProperties = (exports) -> + Object.defineProperties exports, + # Common modules, please sort with alphabet order. + clipboard: + # Must be enumerable, otherwise it woulde be invisible to remote module. + enumerable: true + get: -> require '../clipboard' + crashReporter: + enumerable: true + get: -> require '../crash-reporter' + nativeImage: + enumerable: true + get: -> require '../native-image' + shell: + enumerable: true + get: -> require '../shell' + # The internal modules, invisible unless you know their names. + CallbacksRegistry: + get: -> require '../callbacks-registry' + deprecate: + get: -> require '../deprecate' diff --git a/atom/common/asar/archive.cc b/atom/common/asar/archive.cc index 969f95895..ebb80cc2c 100644 --- a/atom/common/asar/archive.cc +++ b/atom/common/asar/archive.cc @@ -13,6 +13,7 @@ #include "atom/common/asar/scoped_temporary_file.h" #include "base/files/file.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/pickle.h" #include "base/json/json_reader.h" @@ -96,7 +97,6 @@ bool FillFileInfoWithNode(Archive::FileInfo* info, return false; info->size = static_cast(size); - info->unpacked = false; if (node->GetBoolean("unpacked", &info->unpacked) && info->unpacked) return true; @@ -107,6 +107,8 @@ bool FillFileInfoWithNode(Archive::FileInfo* info, return false; info->offset += header_size; + node->GetBoolean("executable", &info->executable); + return true; } @@ -270,9 +272,17 @@ bool Archive::CopyFileOut(const base::FilePath& path, base::FilePath* out) { } scoped_ptr temp_file(new ScopedTemporaryFile); - if (!temp_file->InitFromFile(&file_, info.offset, info.size)) + base::FilePath::StringType ext = path.Extension(); + if (!temp_file->InitFromFile(&file_, ext, info.offset, info.size)) return false; +#if defined(OS_POSIX) + if (info.executable) { + // chmod a+x temp_file; + base::SetPosixFilePermissions(temp_file->path(), 0755); + } +#endif + *out = temp_file->path(); external_files_.set(path, temp_file.Pass()); return true; diff --git a/atom/common/asar/archive.h b/atom/common/asar/archive.h index f2ff2f76d..de5b9de60 100644 --- a/atom/common/asar/archive.h +++ b/atom/common/asar/archive.h @@ -25,8 +25,9 @@ class ScopedTemporaryFile; class Archive { public: struct FileInfo { - FileInfo() : size(0), offset(0) {} + FileInfo() : unpacked(false), executable(false), size(0), offset(0) {} bool unpacked; + bool executable; uint32 size; uint64 offset; }; diff --git a/atom/common/asar/scoped_temporary_file.cc b/atom/common/asar/scoped_temporary_file.cc index 6fccc9434..6dd12782d 100644 --- a/atom/common/asar/scoped_temporary_file.cc +++ b/atom/common/asar/scoped_temporary_file.cc @@ -28,20 +28,34 @@ ScopedTemporaryFile::~ScopedTemporaryFile() { } } -bool ScopedTemporaryFile::Init() { +bool ScopedTemporaryFile::Init(const base::FilePath::StringType& ext) { if (!path_.empty()) return true; base::ThreadRestrictions::ScopedAllowIO allow_io; - return base::CreateTemporaryFile(&path_); + if (!base::CreateTemporaryFile(&path_)) + return false; + +#if defined(OS_WIN) + // Keep the original extension. + if (!ext.empty()) { + base::FilePath new_path = path_.AddExtension(ext); + if (!base::Move(path_, new_path)) + return false; + path_ = new_path; + } +#endif + + return true; } bool ScopedTemporaryFile::InitFromFile(base::File* src, + const base::FilePath::StringType& ext, uint64 offset, uint64 size) { if (!src->IsValid()) return false; - if (!Init()) + if (!Init(ext)) return false; std::vector buf(size); diff --git a/atom/common/asar/scoped_temporary_file.h b/atom/common/asar/scoped_temporary_file.h index ffaee22e5..23660a239 100644 --- a/atom/common/asar/scoped_temporary_file.h +++ b/atom/common/asar/scoped_temporary_file.h @@ -22,11 +22,13 @@ class ScopedTemporaryFile { ScopedTemporaryFile(); virtual ~ScopedTemporaryFile(); - // Init an empty temporary file. - bool Init(); + // Init an empty temporary file with a certain extension. + bool Init(const base::FilePath::StringType& ext); // Init an temporary file and fill it with content of |path|. - bool InitFromFile(base::File* src, uint64 offset, uint64 size); + bool InitFromFile(base::File* src, + const base::FilePath::StringType& ext, + uint64 offset, uint64 size); base::FilePath path() const { return path_; } diff --git a/atom/common/atom_constants.cc b/atom/common/atom_constants.cc new file mode 100644 index 000000000..dacda3c81 --- /dev/null +++ b/atom/common/atom_constants.cc @@ -0,0 +1,11 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/common/atom_constants.h" + +namespace atom { + +const char* kCORSHeader = "Access-Control-Allow-Origin: *"; + +} // namespace atom diff --git a/atom/common/atom_constants.h b/atom/common/atom_constants.h new file mode 100644 index 000000000..e0d42e83e --- /dev/null +++ b/atom/common/atom_constants.h @@ -0,0 +1,15 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_COMMON_ATOM_CONSTANTS_H_ +#define ATOM_COMMON_ATOM_CONSTANTS_H_ + +namespace atom { + +// Header to ignore CORS. +extern const char* kCORSHeader; + +} // namespace atom + +#endif // ATOM_COMMON_ATOM_CONSTANTS_H_ diff --git a/atom/common/atom_version.h b/atom/common/atom_version.h index 093da7b5c..9ad405fae 100644 --- a/atom/common/atom_version.h +++ b/atom/common/atom_version.h @@ -6,8 +6,8 @@ #define ATOM_VERSION_H #define ATOM_MAJOR_VERSION 0 -#define ATOM_MINOR_VERSION 35 -#define ATOM_PATCH_VERSION 1 +#define ATOM_MINOR_VERSION 36 +#define ATOM_PATCH_VERSION 2 #define ATOM_VERSION_IS_RELEASE 1 diff --git a/atom/common/chrome_version.h b/atom/common/chrome_version.h index 250051683..c92fee164 100644 --- a/atom/common/chrome_version.h +++ b/atom/common/chrome_version.h @@ -8,7 +8,7 @@ #ifndef ATOM_COMMON_CHROME_VERSION_H_ #define ATOM_COMMON_CHROME_VERSION_H_ -#define CHROME_VERSION_STRING "45.0.2454.85" +#define CHROME_VERSION_STRING "47.0.2526.73" #define CHROME_VERSION "v" CHROME_VERSION_STRING #endif // ATOM_COMMON_CHROME_VERSION_H_ diff --git a/atom/common/common_message_generator.h b/atom/common/common_message_generator.h index 24f0f63d9..832de1abf 100644 --- a/atom/common/common_message_generator.h +++ b/atom/common/common_message_generator.h @@ -7,3 +7,4 @@ #include "atom/common/api/api_messages.h" #include "chrome/common/print_messages.h" #include "chrome/common/tts_messages.h" +#include "chrome/common/widevine_cdm_messages.h" diff --git a/atom/common/crash_reporter/crash_reporter.cc b/atom/common/crash_reporter/crash_reporter.cc index b87ce54ac..f4f0ff9b7 100644 --- a/atom/common/crash_reporter/crash_reporter.cc +++ b/atom/common/crash_reporter/crash_reporter.cc @@ -48,11 +48,11 @@ CrashReporter::GetUploadedReports(const std::string& path) { std::vector result; if (base::ReadFileToString(base::FilePath::FromUTF8Unsafe(path), &file_content)) { - std::vector reports; - base::SplitString(file_content, '\n', &reports); + std::vector reports = base::SplitString( + file_content, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); for (const std::string& report : reports) { - std::vector report_item; - base::SplitString(report, ',', &report_item); + std::vector report_item = base::SplitString( + report, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); int report_time = 0; if (report_item.size() >= 2 && base::StringToInt(report_item[0], &report_time)) { diff --git a/atom/common/crash_reporter/crash_reporter_linux.cc b/atom/common/crash_reporter/crash_reporter_linux.cc index 8a5608dad..6fe69f486 100644 --- a/atom/common/crash_reporter/crash_reporter_linux.cc +++ b/atom/common/crash_reporter/crash_reporter_linux.cc @@ -130,7 +130,7 @@ bool CrashReporterLinux::CrashDone(const MinidumpDescriptor& minidump, // static CrashReporterLinux* CrashReporterLinux::GetInstance() { - return Singleton::get(); + return base::Singleton::get(); } // static diff --git a/atom/common/crash_reporter/crash_reporter_linux.h b/atom/common/crash_reporter/crash_reporter_linux.h index 2f7d639e9..165c288ab 100644 --- a/atom/common/crash_reporter/crash_reporter_linux.h +++ b/atom/common/crash_reporter/crash_reporter_linux.h @@ -12,7 +12,9 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" +namespace base { template struct DefaultSingletonTraits; +} namespace google_breakpad { class ExceptionHandler; @@ -34,7 +36,7 @@ class CrashReporterLinux : public CrashReporter { void SetUploadParameters() override; private: - friend struct DefaultSingletonTraits; + friend struct base::DefaultSingletonTraits; CrashReporterLinux(); virtual ~CrashReporterLinux(); diff --git a/atom/common/crash_reporter/crash_reporter_mac.h b/atom/common/crash_reporter/crash_reporter_mac.h index cbdb3c65f..f03154359 100644 --- a/atom/common/crash_reporter/crash_reporter_mac.h +++ b/atom/common/crash_reporter/crash_reporter_mac.h @@ -14,7 +14,9 @@ #include "base/strings/string_piece.h" #include "vendor/crashpad/client/simple_string_dictionary.h" +namespace base { template struct DefaultSingletonTraits; +} namespace crash_reporter { @@ -31,7 +33,7 @@ class CrashReporterMac : public CrashReporter { void SetUploadParameters() override; private: - friend struct DefaultSingletonTraits; + friend struct base::DefaultSingletonTraits; CrashReporterMac(); virtual ~CrashReporterMac(); diff --git a/atom/common/crash_reporter/crash_reporter_mac.mm b/atom/common/crash_reporter/crash_reporter_mac.mm index 00f37cc3f..74ac70125 100644 --- a/atom/common/crash_reporter/crash_reporter_mac.mm +++ b/atom/common/crash_reporter/crash_reporter_mac.mm @@ -126,7 +126,7 @@ CrashReporterMac::GetUploadedReports(const std::string& path) { // static CrashReporterMac* CrashReporterMac::GetInstance() { - return Singleton::get(); + return base::Singleton::get(); } // static diff --git a/atom/common/crash_reporter/crash_reporter_win.cc b/atom/common/crash_reporter/crash_reporter_win.cc index 240c229ca..939a02f09 100644 --- a/atom/common/crash_reporter/crash_reporter_win.cc +++ b/atom/common/crash_reporter/crash_reporter_win.cc @@ -153,9 +153,9 @@ void CrashReporterWin::InitBreakpad(const std::string& product_name, return; } - base::string16 pipe_name = ReplaceStringPlaceholders( + base::string16 pipe_name = base::ReplaceStringPlaceholders( kPipeNameFormat, base::UTF8ToUTF16(product_name), NULL); - base::string16 wait_name = ReplaceStringPlaceholders( + base::string16 wait_name = base::ReplaceStringPlaceholders( kWaitEventFormat, base::UTF8ToUTF16(product_name), NULL); // Wait until the crash service is started. @@ -259,7 +259,7 @@ google_breakpad::CustomClientInfo* CrashReporterWin::GetCustomInfo( // static CrashReporterWin* CrashReporterWin::GetInstance() { - return Singleton::get(); + return base::Singleton::get(); } // static diff --git a/atom/common/crash_reporter/crash_reporter_win.h b/atom/common/crash_reporter/crash_reporter_win.h index 09c7ff4ea..181c9eabd 100644 --- a/atom/common/crash_reporter/crash_reporter_win.h +++ b/atom/common/crash_reporter/crash_reporter_win.h @@ -13,7 +13,9 @@ #include "base/memory/scoped_ptr.h" #include "vendor/breakpad/src/client/windows/handler/exception_handler.h" +namespace base { template struct DefaultSingletonTraits; +} namespace crash_reporter { @@ -33,7 +35,7 @@ class CrashReporterWin : public CrashReporter { int CrashForException(EXCEPTION_POINTERS* info); private: - friend struct DefaultSingletonTraits; + friend struct base::DefaultSingletonTraits; CrashReporterWin(); virtual ~CrashReporterWin(); diff --git a/atom/common/crash_reporter/win/crash_service.cc b/atom/common/crash_reporter/win/crash_service.cc index 9b6ba7e03..67e22381a 100644 --- a/atom/common/crash_reporter/win/crash_service.cc +++ b/atom/common/crash_reporter/win/crash_service.cc @@ -118,7 +118,7 @@ HWND g_top_window = NULL; bool CreateTopWindow(HINSTANCE instance, const base::string16& application_name, bool visible) { - base::string16 class_name = ReplaceStringPlaceholders( + base::string16 class_name = base::ReplaceStringPlaceholders( kClassNameFormat, application_name, NULL); WNDCLASSEXW wcx = {0}; @@ -309,7 +309,7 @@ bool CrashService::Initialize(const base::string16& application_name, // Create or open an event to signal the browser process that the crash // service is initialized. - base::string16 wait_name = ReplaceStringPlaceholders( + base::string16 wait_name = base::ReplaceStringPlaceholders( kWaitEventFormat, application_name, NULL); HANDLE wait_event = ::CreateEventW(NULL, TRUE, TRUE, wait_name.c_str()); ::SetEvent(wait_event); @@ -524,4 +524,3 @@ PSECURITY_DESCRIPTOR CrashService::GetSecurityDescriptorForLowIntegrity() { } } // namespace breakpad - diff --git a/atom/common/crash_reporter/win/crash_service_main.cc b/atom/common/crash_reporter/win/crash_service_main.cc index 7a5eeb101..56d46970b 100644 --- a/atom/common/crash_reporter/win/crash_service_main.cc +++ b/atom/common/crash_reporter/win/crash_service_main.cc @@ -68,7 +68,7 @@ int Main(const wchar_t* cmd) { VLOG(1) << "Session start. cmdline is [" << cmd << "]"; // Setting the crash reporter. - base::string16 pipe_name = ReplaceStringPlaceholders(kPipeNameFormat, + base::string16 pipe_name = base::ReplaceStringPlaceholders(kPipeNameFormat, application_name, NULL); cmd_line.AppendSwitch("no-window"); diff --git a/atom/common/lib/asar.coffee b/atom/common/lib/asar.coffee index f7eeceb3f..5f690e9a8 100644 --- a/atom/common/lib/asar.coffee +++ b/atom/common/lib/asar.coffee @@ -18,6 +18,7 @@ process.on 'exit', -> # Separate asar package's path from full path. splitPath = (p) -> + return [false] if process.noAsar # shortcut to disable asar. return [false] if typeof p isnt 'string' return [true, p, ''] if p.substr(-5) is '.asar' p = path.normalize p @@ -62,6 +63,15 @@ notFoundError = (asarPath, filePath, callback) -> throw error process.nextTick -> callback error +# Create a ENOTDIR error. +notDirError = (callback) -> + error = new Error('ENOTDIR, not a directory') + error.code = 'ENOTDIR' + error.errno = -20 + unless typeof callback is 'function' + throw error + process.nextTick -> callback error + # Create invalid archive error. invalidArchiveError = (asarPath, callback) -> error = new Error("Invalid package #{asarPath}") @@ -350,6 +360,24 @@ exports.wrapFsWithAsar = (fs) -> if stats.isDirectory then return 1 else return 0 + # Calling mkdir for directory inside asar archive should throw ENOTDIR + # error, but on Windows it throws ENOENT. + # This is to work around the recursive looping bug of mkdirp since it is + # widely used. + if process.platform is 'win32' + mkdir = fs.mkdir + fs.mkdir = (p, mode, callback) -> + callback = mode if typeof mode is 'function' + [isAsar, asarPath, filePath] = splitPath p + return notDirError callback if isAsar and filePath.length + mkdir p, mode, callback + + mkdirSync = fs.mkdirSync + fs.mkdirSync = (p, mode) -> + [isAsar, asarPath, filePath] = splitPath p + notDirError() if isAsar and filePath.length + mkdirSync p, mode + overrideAPI fs, 'open' overrideAPI child_process, 'execFile' overrideAPISync process, 'dlopen', 1 diff --git a/atom/common/native_mate_converters/blink_converter.cc b/atom/common/native_mate_converters/blink_converter.cc index 2c871276b..095490ab8 100644 --- a/atom/common/native_mate_converters/blink_converter.cc +++ b/atom/common/native_mate_converters/blink_converter.cc @@ -13,6 +13,7 @@ #include "content/public/browser/native_web_keyboard_event.h" #include "native_mate/dictionary.h" #include "third_party/WebKit/public/web/WebDeviceEmulationParams.h" +#include "third_party/WebKit/public/web/WebFindOptions.h" #include "third_party/WebKit/public/web/WebInputEvent.h" namespace { @@ -45,7 +46,7 @@ template<> struct Converter { static bool FromV8(v8::Isolate* isolate, v8::Handle val, blink::WebInputEvent::Type* out) { - std::string type = base::StringToLowerASCII(V8ToString(val)); + std::string type = base::ToLowerASCII(V8ToString(val)); if (type == "mousedown") *out = blink::WebInputEvent::MouseDown; else if (type == "mouseup") @@ -82,7 +83,7 @@ template<> struct Converter { static bool FromV8(v8::Isolate* isolate, v8::Handle val, blink::WebMouseEvent::Button* out) { - std::string button = base::StringToLowerASCII(V8ToString(val)); + std::string button = base::ToLowerASCII(V8ToString(val)); if (button == "left") *out = blink::WebMouseEvent::Button::ButtonLeft; else if (button == "middle") @@ -97,7 +98,7 @@ template<> struct Converter { static bool FromV8(v8::Isolate* isolate, v8::Handle val, blink::WebInputEvent::Modifiers* out) { - std::string modifier = base::StringToLowerASCII(V8ToString(val)); + std::string modifier = base::ToLowerASCII(V8ToString(val)); if (modifier == "shift") *out = blink::WebInputEvent::ShiftKey; else if (modifier == "control" || modifier == "ctrl") @@ -166,7 +167,7 @@ bool Converter::FromV8( out->windowsKeyCode = atom::KeyboardCodeFromCharCode(code, &shifted); else if (dict.Get("keyCode", &identifier)) out->windowsKeyCode = atom::KeyboardCodeFromKeyIdentifier( - base::StringToLowerASCII(identifier)); + base::ToLowerASCII(identifier)); else return false; @@ -263,7 +264,7 @@ bool Converter::FromV8( std::string screen_position; if (dict.Get("screenPosition", &screen_position)) { - screen_position = base::StringToLowerASCII(screen_position); + screen_position = base::ToLowerASCII(screen_position); if (screen_position == "mobile") out->screenPosition = blink::WebDeviceEmulationParams::Mobile; else if (screen_position == "desktop") @@ -282,4 +283,20 @@ bool Converter::FromV8( return true; } +bool Converter::FromV8( + v8::Isolate* isolate, + v8::Local val, + blink::WebFindOptions* out) { + mate::Dictionary dict; + if (!ConvertFromV8(isolate, val, &dict)) + return false; + + dict.Get("forward", &out->forward); + dict.Get("matchCase", &out->matchCase); + dict.Get("findNext", &out->findNext); + dict.Get("wordStart", &out->wordStart); + dict.Get("medialCapitalAsWordStart", &out->medialCapitalAsWordStart); + return true; +} + } // namespace mate diff --git a/atom/common/native_mate_converters/blink_converter.h b/atom/common/native_mate_converters/blink_converter.h index 17bb108d3..6a3601929 100644 --- a/atom/common/native_mate_converters/blink_converter.h +++ b/atom/common/native_mate_converters/blink_converter.h @@ -13,10 +13,11 @@ class WebMouseEvent; class WebMouseWheelEvent; class WebKeyboardEvent; struct WebDeviceEmulationParams; +struct WebFindOptions; struct WebFloatPoint; struct WebPoint; struct WebSize; -} +} // namespace blink namespace content { struct NativeWebKeyboardEvent; @@ -80,6 +81,12 @@ struct Converter { blink::WebDeviceEmulationParams* out); }; +template<> +struct Converter { + static bool FromV8(v8::Isolate* isolate, v8::Local val, + blink::WebFindOptions* out); +}; + } // namespace mate #endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_BLINK_CONVERTER_H_ diff --git a/atom/common/native_mate_converters/callback.cc b/atom/common/native_mate_converters/callback.cc index 87faa3df3..8bf5c459b 100644 --- a/atom/common/native_mate_converters/callback.cc +++ b/atom/common/native_mate_converters/callback.cc @@ -4,7 +4,9 @@ #include "atom/common/native_mate_converters/callback.h" -#include "atom/browser/atom_browser_main_parts.h" +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; namespace mate { @@ -56,31 +58,59 @@ v8::Local BindFunctionWith(v8::Isolate* isolate, } // namespace +// Destroy the class on UI thread when possible. +struct DeleteOnUIThread { + template + static void Destruct(const T* x) { + if (Locker::IsBrowserProcess() && + !BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, x); + } else { + delete x; + } + } +}; + +// Like v8::Global, but ref-counted. +template +class RefCountedGlobal : public base::RefCountedThreadSafe, + DeleteOnUIThread> { + public: + RefCountedGlobal(v8::Isolate* isolate, v8::Local value) + : handle_(isolate, v8::Local::Cast(value)) { + } + + bool IsAlive() const { + return !handle_.IsEmpty(); + } + + v8::Local NewHandle(v8::Isolate* isolate) const { + return v8::Local::New(isolate, handle_); + } + + private: + v8::Global handle_; + + DISALLOW_COPY_AND_ASSIGN(RefCountedGlobal); +}; + SafeV8Function::SafeV8Function(v8::Isolate* isolate, v8::Local value) - : v8_function_(new RefCountedPersistent(isolate, value)), - weak_factory_(this) { - Init(); + : v8_function_(new RefCountedGlobal(isolate, value)) { } SafeV8Function::SafeV8Function(const SafeV8Function& other) - : v8_function_(other.v8_function_), - weak_factory_(this) { - Init(); + : v8_function_(other.v8_function_) { } -v8::Local SafeV8Function::NewHandle() const { - return v8_function_->NewHandle(); +SafeV8Function::~SafeV8Function() { } -void SafeV8Function::Init() { - if (Locker::IsBrowserProcess() && atom::AtomBrowserMainParts::Get()) - atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback( - base::Bind(&SafeV8Function::FreeHandle, weak_factory_.GetWeakPtr())); +bool SafeV8Function::IsAlive() const { + return v8_function_.get() && v8_function_->IsAlive(); } -void SafeV8Function::FreeHandle() { - Locker locker(v8_function_->isolate()); - v8_function_ = nullptr; +v8::Local SafeV8Function::NewHandle(v8::Isolate* isolate) const { + return v8_function_->NewHandle(isolate); } v8::Local CreateFunctionFromTranslater( diff --git a/atom/common/native_mate_converters/callback.h b/atom/common/native_mate_converters/callback.h index 5dd9d3cec..6ef8e74c7 100644 --- a/atom/common/native_mate_converters/callback.h +++ b/atom/common/native_mate_converters/callback.h @@ -19,23 +19,21 @@ namespace mate { namespace internal { -// Manages the V8 function with RAII, and automatically cleans the handle when -// JavaScript context is destroyed, even when the class is not destroyed. +template +class RefCountedGlobal; + +// Manages the V8 function with RAII. class SafeV8Function { public: SafeV8Function(v8::Isolate* isolate, v8::Local value); SafeV8Function(const SafeV8Function& other); + ~SafeV8Function(); - bool is_alive() const { return v8_function_.get(); } - - v8::Local NewHandle() const; + bool IsAlive() const; + v8::Local NewHandle(v8::Isolate* isolate) const; private: - void Init(); - void FreeHandle(); - - scoped_refptr> v8_function_; - base::WeakPtrFactory weak_factory_; + scoped_refptr> v8_function_; }; // Helper to invoke a V8 function with C++ parameters. @@ -49,12 +47,12 @@ struct V8FunctionInvoker(ArgTypes...)> { ArgTypes... raw) { Locker locker(isolate); v8::EscapableHandleScope handle_scope(isolate); - if (!function.is_alive()) + if (!function.IsAlive()) return v8::Null(isolate); scoped_ptr script_scope( Locker::IsBrowserProcess() ? - nullptr : new blink::WebScopedRunV8Script(isolate)); - v8::Local holder = function.NewHandle(); + nullptr : new blink::WebScopedRunV8Script); + v8::Local holder = function.NewHandle(isolate); v8::Local context = holder->CreationContext(); v8::Context::Scope context_scope(context); std::vector> args = { ConvertToV8(isolate, raw)... }; @@ -70,12 +68,12 @@ struct V8FunctionInvoker { ArgTypes... raw) { Locker locker(isolate); v8::HandleScope handle_scope(isolate); - if (!function.is_alive()) + if (!function.IsAlive()) return; scoped_ptr script_scope( Locker::IsBrowserProcess() ? - nullptr : new blink::WebScopedRunV8Script(isolate)); - v8::Local holder = function.NewHandle(); + nullptr : new blink::WebScopedRunV8Script); + v8::Local holder = function.NewHandle(isolate); v8::Local context = holder->CreationContext(); v8::Context::Scope context_scope(context); std::vector> args = { ConvertToV8(isolate, raw)... }; @@ -91,12 +89,12 @@ struct V8FunctionInvoker { Locker locker(isolate); v8::HandleScope handle_scope(isolate); ReturnType ret = ReturnType(); - if (!function.is_alive()) + if (!function.IsAlive()) return ret; scoped_ptr script_scope( Locker::IsBrowserProcess() ? - nullptr : new blink::WebScopedRunV8Script(isolate)); - v8::Local holder = function.NewHandle(); + nullptr : new blink::WebScopedRunV8Script); + v8::Local holder = function.NewHandle(isolate); v8::Local context = holder->CreationContext(); v8::Context::Scope context_scope(context); std::vector> args = { ConvertToV8(isolate, raw)... }; diff --git a/atom/common/native_mate_converters/content_converter.cc b/atom/common/native_mate_converters/content_converter.cc index 15a57dea5..d79094f79 100644 --- a/atom/common/native_mate_converters/content_converter.cc +++ b/atom/common/native_mate_converters/content_converter.cc @@ -4,6 +4,7 @@ #include "atom/common/native_mate_converters/content_converter.h" +#include #include #include "atom/common/native_mate_converters/callback.h" @@ -97,4 +98,25 @@ v8::Local Converter::ToV8( return mate::ConvertToV8(isolate, dict); } +// static +bool Converter::FromV8( + v8::Isolate* isolate, + v8::Local val, + content::StopFindAction* out) { + std::string action; + if (!ConvertFromV8(isolate, val, &action)) + return false; + + if (action == "clearSelection") + *out = content::STOP_FIND_ACTION_CLEAR_SELECTION; + else if (action == "keepSelection") + *out = content::STOP_FIND_ACTION_KEEP_SELECTION; + else if (action == "activateSelection") + *out = content::STOP_FIND_ACTION_ACTIVATE_SELECTION; + else + return false; + + return true; +} + } // namespace mate diff --git a/atom/common/native_mate_converters/content_converter.h b/atom/common/native_mate_converters/content_converter.h index 7edee24fa..a5708e022 100644 --- a/atom/common/native_mate_converters/content_converter.h +++ b/atom/common/native_mate_converters/content_converter.h @@ -8,6 +8,7 @@ #include #include "content/public/common/menu_item.h" +#include "content/public/common/stop_find_action.h" #include "native_mate/converter.h" namespace content { @@ -32,6 +33,12 @@ struct Converter { const ContextMenuParamsWithWebContents& val); }; +template<> +struct Converter { + static bool FromV8(v8::Isolate* isolate, v8::Local val, + content::StopFindAction* out); +}; + } // namespace mate #endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_CONTENT_CONVERTER_H_ diff --git a/atom/common/native_mate_converters/net_converter.cc b/atom/common/native_mate_converters/net_converter.cc index 4749a4fed..7a1b48d93 100644 --- a/atom/common/native_mate_converters/net_converter.cc +++ b/atom/common/native_mate_converters/net_converter.cc @@ -5,10 +5,18 @@ #include "atom/common/native_mate_converters/net_converter.h" #include +#include #include "atom/common/node_includes.h" +#include "atom/common/native_mate_converters/gurl_converter.h" +#include "atom/common/native_mate_converters/value_converter.h" #include "native_mate/dictionary.h" +#include "net/base/upload_bytes_element_reader.h" +#include "net/base/upload_data_stream.h" +#include "net/base/upload_element_reader.h" +#include "net/base/upload_file_element_reader.h" #include "net/cert/x509_certificate.h" +#include "net/http/http_response_headers.h" #include "net/url_request/url_request.h" namespace mate { @@ -20,6 +28,30 @@ v8::Local Converter::ToV8( dict.Set("method", val->method()); dict.Set("url", val->url().spec()); dict.Set("referrer", val->referrer()); + const net::UploadDataStream* upload_data = val->get_upload(); + if (upload_data) { + const ScopedVector* readers = + upload_data->GetElementReaders(); + std::vector upload_data_list; + upload_data_list.reserve(readers->size()); + for (const auto& reader : *readers) { + auto upload_data_dict = mate::Dictionary::CreateEmpty(isolate); + if (reader->AsBytesReader()) { + const net::UploadBytesElementReader* bytes_reader = + reader->AsBytesReader(); + auto bytes = + node::Buffer::Copy(isolate, bytes_reader->bytes(), + bytes_reader->length()).ToLocalChecked(); + upload_data_dict.Set("bytes", bytes); + } else if (reader->AsFileReader()) { + const net::UploadFileElementReader* file_reader = + reader->AsFileReader(); + upload_data_dict.Set("file", file_reader->path().AsUTF8Unsafe()); + } + upload_data_list.push_back(upload_data_dict); + } + dict.Set("uploadData", upload_data_list); + } return mate::ConvertToV8(isolate, dict); } diff --git a/atom/common/node_bindings.cc b/atom/common/node_bindings.cc index dbd0bd8d9..b1cb84eea 100644 --- a/atom/common/node_bindings.cc +++ b/atom/common/node_bindings.cc @@ -35,6 +35,7 @@ REFERENCE_MODULE(atom_browser_app); REFERENCE_MODULE(atom_browser_auto_updater); REFERENCE_MODULE(atom_browser_content_tracing); REFERENCE_MODULE(atom_browser_dialog); +REFERENCE_MODULE(atom_browser_desktop_capturer); REFERENCE_MODULE(atom_browser_download_item); REFERENCE_MODULE(atom_browser_menu); REFERENCE_MODULE(atom_browser_power_monitor); @@ -227,7 +228,7 @@ void NodeBindings::UvRunOnce() { // Perform microtask checkpoint after running JavaScript. scoped_ptr script_scope( - is_browser_ ? nullptr : new blink::WebScopedRunV8Script(env->isolate())); + is_browser_ ? nullptr : new blink::WebScopedRunV8Script); // Deal with uv events. int r = uv_run(uv_loop_, UV_RUN_NOWAIT); diff --git a/atom/common/options_switches.cc b/atom/common/options_switches.cc index 1124cfba4..6c4e8477c 100644 --- a/atom/common/options_switches.cc +++ b/atom/common/options_switches.cc @@ -85,18 +85,18 @@ const char kNodeIntegration[] = "nodeIntegration"; // Instancd ID of guest WebContents. const char kGuestInstanceID[] = "guestInstanceId"; -// Set page visiblity to always visible. -const char kPageVisibility[] = "pageVisibility"; - // Enable DirectWrite on Windows. const char kDirectWrite[] = "directWrite"; // Web runtime features. const char kExperimentalFeatures[] = "experimentalFeatures"; const char kExperimentalCanvasFeatures[] = "experimentalCanvasFeatures"; -const char kOverlayScrollbars[] = "overlayScrollbars"; -const char kOverlayFullscreenVideo[] = "overlayFullscreenVideo"; -const char kSharedWorker[] = "sharedWorker"; + +// Opener window's ID. +const char kOpenerID[] = "openerId"; + +// Enable blink features. +const char kBlinkFeatures[] = "blinkFeatures"; } // namespace options @@ -120,6 +120,9 @@ const char kDisableHttpCache[] = "disable-http-cache"; // Register schemes to standard. const char kRegisterStandardSchemes[] = "register-standard-schemes"; +// Register schemes to handle service worker. +const char kRegisterServiceWorkerSchemes[] = "register-service-worker-schemes"; + // The minimum SSL/TLS version ("tls1", "tls1.1", or "tls1.2") that // TLS fallback will accept. const char kSSLVersionFallbackMin[] = "ssl-version-fallback-min"; @@ -136,12 +139,13 @@ const char kPreloadScript[] = "preload"; const char kPreloadURL[] = "preload-url"; const char kNodeIntegration[] = "node-integration"; const char kGuestInstanceID[] = "guest-instance-id"; -const char kExperimentalFeatures[] = "experimental-features"; -const char kExperimentalCanvasFeatures[] = "experimental-canvas-features"; -const char kOverlayScrollbars[] = "overlay-scrollbars"; -const char kOverlayFullscreenVideo[] = "overlay-fullscreen-video"; -const char kSharedWorker[] = "shared-worker"; -const char kPageVisibility[] = "page-visiblity"; +const char kOpenerID[] = "opener-id"; + +// Widevine options +// Path to Widevine CDM binaries. +const char kWidevineCdmPath[] = "widevine-cdm-path"; +// Widevine CDM version. +const char kWidevineCdmVersion[] = "widevine-cdm-version"; } // namespace switches diff --git a/atom/common/options_switches.h b/atom/common/options_switches.h index cd52c9759..f03852167 100644 --- a/atom/common/options_switches.h +++ b/atom/common/options_switches.h @@ -49,10 +49,8 @@ extern const char kNodeIntegration[]; extern const char kGuestInstanceID[]; extern const char kExperimentalFeatures[]; extern const char kExperimentalCanvasFeatures[]; -extern const char kOverlayScrollbars[]; -extern const char kOverlayFullscreenVideo[]; -extern const char kSharedWorker[]; -extern const char kPageVisibility[]; +extern const char kOpenerID[]; +extern const char kBlinkFeatures[]; } // namespace options @@ -67,6 +65,7 @@ extern const char kPpapiFlashVersion[]; extern const char kClientCertificate[]; extern const char kDisableHttpCache[]; extern const char kRegisterStandardSchemes[]; +extern const char kRegisterServiceWorkerSchemes[]; extern const char kSSLVersionFallbackMin[]; extern const char kCipherSuiteBlacklist[]; extern const char kAppUserModelId[]; @@ -76,12 +75,10 @@ extern const char kPreloadScript[]; extern const char kPreloadURL[]; extern const char kNodeIntegration[]; extern const char kGuestInstanceID[]; -extern const char kExperimentalFeatures[]; -extern const char kExperimentalCanvasFeatures[]; -extern const char kOverlayScrollbars[]; -extern const char kOverlayFullscreenVideo[]; -extern const char kSharedWorker[]; -extern const char kPageVisibility[]; +extern const char kOpenerID[]; + +extern const char kWidevineCdmPath[]; +extern const char kWidevineCdmVersion[]; } // namespace switches diff --git a/atom/common/platform_util_mac.mm b/atom/common/platform_util_mac.mm index 2f9e2b764..7184593ae 100644 --- a/atom/common/platform_util_mac.mm +++ b/atom/common/platform_util_mac.mm @@ -12,6 +12,7 @@ #include "base/mac/mac_logging.h" #include "base/mac/scoped_aedesc.h" #include "base/strings/sys_string_conversions.h" +#include "net/base/mac/url_conversions.h" #include "url/gurl.h" namespace platform_util { @@ -120,8 +121,7 @@ void OpenItem(const base::FilePath& full_path) { bool OpenExternal(const GURL& url) { DCHECK([NSThread isMainThread]); - NSString* url_string = base::SysUTF8ToNSString(url.spec()); - NSURL* ns_url = [NSURL URLWithString:url_string]; + NSURL* ns_url = net::NSURLWithGURL(url); if (!ns_url) { return false; } diff --git a/atom/common/platform_util_win.cc b/atom/common/platform_util_win.cc index cca392952..735e974d1 100644 --- a/atom/common/platform_util_win.cc +++ b/atom/common/platform_util_win.cc @@ -340,13 +340,26 @@ bool MoveItemToTrash(const base::FilePath& path) { // Elevation prompt enabled for UAC protected files. This overrides the // SILENT, NO_UI and NOERRORUI flags. - if (FAILED(pfo->SetOperationFlags(FOF_NO_UI | - FOF_ALLOWUNDO | - FOF_NOERRORUI | - FOF_SILENT | - FOFX_SHOWELEVATIONPROMPT | - FOFX_RECYCLEONDELETE))) - return false; + + if (base::win::GetVersion() >= base::win::VERSION_WIN8) { + // Windows 8 introduces the flag RECYCLEONDELETE and deprecates the + // ALLOWUNDO in favor of ADDUNDORECORD. + if (FAILED(pfo->SetOperationFlags(FOF_NO_UI | + FOFX_ADDUNDORECORD | + FOF_NOERRORUI | + FOF_SILENT | + FOFX_SHOWELEVATIONPROMPT | + FOFX_RECYCLEONDELETE))) + return false; + } else { + // For Windows 7 and Vista, RecycleOnDelete is the default behavior. + if (FAILED(pfo->SetOperationFlags(FOF_NO_UI | + FOF_ALLOWUNDO | + FOF_NOERRORUI | + FOF_SILENT | + FOFX_SHOWELEVATIONPROMPT))) + return false; + } // Create an IShellItem from the supplied source path. base::win::ScopedComPtr delete_item; diff --git a/atom/renderer/api/atom_api_renderer_ipc.cc b/atom/renderer/api/atom_api_renderer_ipc.cc index 061293e80..a82562f93 100644 --- a/atom/renderer/api/atom_api_renderer_ipc.cc +++ b/atom/renderer/api/atom_api_renderer_ipc.cc @@ -54,8 +54,6 @@ base::string16 SendSync(mate::Arguments* args, IPC::SyncMessage* message = new AtomViewHostMsg_Message_Sync( render_view->GetRoutingID(), channel, arguments, &json); - // Enable the UI thread in browser to receive messages. - message->EnableMessagePumping(); bool success = render_view->Send(message); if (!success) diff --git a/atom/renderer/api/atom_api_web_frame.cc b/atom/renderer/api/atom_api_web_frame.cc index e9b2b0305..83d67a8b6 100644 --- a/atom/renderer/api/atom_api_web_frame.cc +++ b/atom/renderer/api/atom_api_web_frame.cc @@ -112,6 +112,8 @@ void WebFrame::RegisterURLSchemeAsPrivileged(const std::string& scheme) { privileged_scheme); blink::WebSecurityPolicy::registerURLSchemeAsAllowingServiceWorkers( privileged_scheme); + blink::WebSecurityPolicy::registerURLSchemeAsSupportingFetchAPI( + privileged_scheme); } mate::ObjectTemplateBuilder WebFrame::GetObjectTemplateBuilder( diff --git a/atom/renderer/api/lib/desktop-capturer.coffee b/atom/renderer/api/lib/desktop-capturer.coffee new file mode 100644 index 000000000..7c7c89824 --- /dev/null +++ b/atom/renderer/api/lib/desktop-capturer.coffee @@ -0,0 +1,20 @@ +{ipcRenderer, nativeImage} = require 'electron' + +nextId = 0 +getNextId = -> ++nextId + +# |options.type| can not be empty and has to include 'window' or 'screen'. +isValid = (options) -> + return options?.types? and Array.isArray options.types + +exports.getSources = (options, callback) -> + return callback new Error('Invalid options') unless isValid options + + captureWindow = 'window' in options.types + captureScreen = 'screen' in options.types + options.thumbnailSize ?= width: 150, height: 150 + + id = getNextId() + ipcRenderer.send 'ATOM_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, options.thumbnailSize, id + ipcRenderer.once "ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_#{id}", (event, sources) -> + callback null, ({id: source.id, name: source.name, thumbnail: nativeImage.createFromDataURL source.thumbnail} for source in sources) diff --git a/atom/renderer/api/lib/exports/electron.coffee b/atom/renderer/api/lib/exports/electron.coffee index 5d7f2a57e..d0b3af3c5 100644 --- a/atom/renderer/api/lib/exports/electron.coffee +++ b/atom/renderer/api/lib/exports/electron.coffee @@ -1,8 +1,13 @@ -# Import common modules. -module.exports = require '../../../../common/api/lib/exports/electron' +common = require '../../../../common/api/lib/exports/electron' -Object.defineProperties module.exports, +# Import common modules. +common.defineProperties exports + +Object.defineProperties exports, # Renderer side modules, please sort with alphabet order. + desktopCapturer: + enumerable: true + get: -> require '../desktop-capturer' ipcRenderer: enumerable: true get: -> require '../ipc-renderer' diff --git a/atom/renderer/api/lib/ipc-renderer.coffee b/atom/renderer/api/lib/ipc-renderer.coffee index 29004d212..0dd629e54 100644 --- a/atom/renderer/api/lib/ipc-renderer.coffee +++ b/atom/renderer/api/lib/ipc-renderer.coffee @@ -1,3 +1,5 @@ +{EventEmitter} = require 'events' + binding = process.atomBinding 'ipc' v8Util = process.atomBinding 'v8_util' diff --git a/atom/renderer/api/lib/ipc.coffee b/atom/renderer/api/lib/ipc.coffee index b0e951d70..edd7d29b6 100644 --- a/atom/renderer/api/lib/ipc.coffee +++ b/atom/renderer/api/lib/ipc.coffee @@ -2,7 +2,7 @@ {EventEmitter} = require 'events' # This module is deprecated, we mirror everything from ipcRenderer. -deprecate.warn 'ipc module', 'ipcRenderer module' +deprecate.warn 'ipc module', 'require("electron").ipcRenderer' # Routes events of ipcRenderer. ipc = new EventEmitter diff --git a/atom/renderer/api/lib/remote.coffee b/atom/renderer/api/lib/remote.coffee index 48cdd937f..b73fbf50b 100644 --- a/atom/renderer/api/lib/remote.coffee +++ b/atom/renderer/api/lib/remote.coffee @@ -18,6 +18,8 @@ wrapArgs = (args, visited=[]) -> type: 'array', value: wrapArgs(value, visited) else if Buffer.isBuffer value type: 'buffer', value: Array::slice.call(value, 0) + else if value instanceof Date + type: 'date', value: value.getTime() else if value?.constructor.name is 'Promise' type: 'promise', then: valueToMeta(value.then.bind(value)) else if value? and typeof value is 'object' and v8Util.getHiddenValue value, 'atomId' @@ -65,39 +67,17 @@ metaToValue = (meta) -> return metaToValue obj else # Function call. - ret = ipcRenderer.sendSync 'ATOM_BROWSER_FUNCTION_CALL', meta.id, wrapArgs(arguments) - return metaToValue ret + obj = ipcRenderer.sendSync 'ATOM_BROWSER_FUNCTION_CALL', meta.id, wrapArgs(arguments) + return metaToValue obj else ret = v8Util.createObjectWithName meta.name # Polulate delegate members. for member in meta.members - do (member) -> - if member.type is 'function' - ret[member.name] = - class RemoteMemberFunction - constructor: -> - if @constructor is RemoteMemberFunction - # Constructor call. - obj = ipcRenderer.sendSync 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', meta.id, member.name, wrapArgs(arguments) - return metaToValue obj - else - # Call member function. - ret = ipcRenderer.sendSync 'ATOM_BROWSER_MEMBER_CALL', meta.id, member.name, wrapArgs(arguments) - return metaToValue ret - else - Object.defineProperty ret, member.name, - enumerable: true, - configurable: false, - set: (value) -> - # Set member data. - ipcRenderer.sendSync 'ATOM_BROWSER_MEMBER_SET', meta.id, member.name, value - value - - get: -> - # Get member data. - ret = ipcRenderer.sendSync 'ATOM_BROWSER_MEMBER_GET', meta.id, member.name - metaToValue ret + if member.type is 'function' + ret[member.name] = createRemoteMemberFunction meta.id, member.name + else + Object.defineProperty ret, member.name, createRemoteMemberProperty(meta.id, member.name) # Track delegate object's life time, and tell the browser to clean up # when the object is GCed. @@ -117,6 +97,35 @@ metaToPlainObject = (meta) -> obj[name] = value for {name, value} in meta.members obj +# Create a RemoteMemberFunction instance. +# This function's content should not be inlined into metaToValue, otherwise V8 +# may consider it circular reference. +createRemoteMemberFunction = (metaId, name) -> + class RemoteMemberFunction + constructor: -> + if @constructor is RemoteMemberFunction + # Constructor call. + ret = ipcRenderer.sendSync 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', metaId, name, wrapArgs(arguments) + return metaToValue ret + else + # Call member function. + ret = ipcRenderer.sendSync 'ATOM_BROWSER_MEMBER_CALL', metaId, name, wrapArgs(arguments) + return metaToValue ret + +# Create configuration for defineProperty. +# This function's content should not be inlined into metaToValue, otherwise V8 +# may consider it circular reference. +createRemoteMemberProperty = (metaId, name) -> + enumerable: true, + configurable: false, + set: (value) -> + # Set member data. + ipcRenderer.sendSync 'ATOM_BROWSER_MEMBER_SET', metaId, name, value + value + get: -> + # Get member data. + metaToValue ipcRenderer.sendSync('ATOM_BROWSER_MEMBER_GET', metaId, name) + # Browser calls a callback in renderer. ipcRenderer.on 'ATOM_RENDERER_CALLBACK', (event, id, args) -> callbacksRegistry.apply id, metaToValue(args) @@ -126,9 +135,9 @@ ipcRenderer.on 'ATOM_RENDERER_RELEASE_CALLBACK', (event, id) -> callbacksRegistry.remove id # List all built-in modules in browser process. -browserModules = ipcRenderer.sendSync 'ATOM_BROWSER_LIST_MODULES' +browserModules = require '../../../browser/api/lib/exports/electron' # And add a helper receiver for each one. -for name in browserModules +for name of browserModules do (name) -> Object.defineProperty exports, name, get: -> exports.getBuiltin name diff --git a/atom/renderer/atom_renderer_client.cc b/atom/renderer/atom_renderer_client.cc index 362b0b802..d9c364c37 100644 --- a/atom/renderer/atom_renderer_client.cc +++ b/atom/renderer/atom_renderer_client.cc @@ -5,6 +5,7 @@ #include "atom/renderer/atom_renderer_client.h" #include +#include #include "atom/common/api/api_messages.h" #include "atom/common/api/atom_bindings.h" @@ -15,6 +16,7 @@ #include "atom/renderer/guest_view_container.h" #include "atom/renderer/node_array_buffer_bridge.h" #include "base/command_line.h" +#include "chrome/renderer/media/chrome_key_systems.h" #include "chrome/renderer/pepper/pepper_helper.h" #include "chrome/renderer/printing/print_web_view_helper.h" #include "chrome/renderer/tts_dispatcher.h" @@ -27,6 +29,7 @@ #include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebPluginParams.h" #include "third_party/WebKit/public/web/WebKit.h" +#include "third_party/WebKit/public/web/WebSecurityPolicy.h" #include "third_party/WebKit/public/web/WebRuntimeFeatures.h" #include "third_party/WebKit/public/web/WebView.h" @@ -38,11 +41,6 @@ namespace atom { namespace { -bool IsSwitchEnabled(base::CommandLine* command_line, - const char* switch_string) { - return command_line->GetSwitchValueASCII(switch_string) == "true"; -} - // Helper class to forward the messages to the client. class AtomRenderFrameObserver : public content::RenderFrameObserver { public: @@ -92,8 +90,6 @@ AtomRendererClient::~AtomRendererClient() { } void AtomRendererClient::WebKitInitialized() { - EnableWebRuntimeFeatures(); - blink::WebCustomElement::addEmbedderCustomElementName("webview"); blink::WebCustomElement::addEmbedderCustomElementName("browserplugin"); @@ -129,6 +125,9 @@ void AtomRendererClient::RenderFrameCreated( content::RenderFrame* render_frame) { new PepperHelper(render_frame); new AtomRenderFrameObserver(render_frame, this); + + // Allow file scheme to handle service worker by default. + blink::WebSecurityPolicy::registerURLSchemeAsAllowingServiceWorkers("file"); } void AtomRendererClient::RenderViewCreated(content::RenderView* render_view) { @@ -190,7 +189,7 @@ bool AtomRendererClient::ShouldFork(blink::WebLocalFrame* frame, // the OpenURLFromTab is triggered, which means form posting would not work, // we should solve this by patching Chromium in future. *send_referrer = true; - return http_method == "GET"; + return http_method == "GET" && !is_server_redirect; } content::BrowserPluginDelegate* AtomRendererClient::CreateBrowserPluginDelegate( @@ -204,32 +203,9 @@ content::BrowserPluginDelegate* AtomRendererClient::CreateBrowserPluginDelegate( } } -bool AtomRendererClient::ShouldOverridePageVisibilityState( - const content::RenderFrame* render_frame, - blink::WebPageVisibilityState* override_state) { - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - - if (IsSwitchEnabled(command_line, switches::kPageVisibility)) { - *override_state = blink::WebPageVisibilityStateVisible; - return true; - } - - return false; -} - -void AtomRendererClient::EnableWebRuntimeFeatures() { - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - - if (IsSwitchEnabled(command_line, switches::kExperimentalFeatures)) - blink::WebRuntimeFeatures::enableExperimentalFeatures(true); - if (IsSwitchEnabled(command_line, switches::kExperimentalCanvasFeatures)) - blink::WebRuntimeFeatures::enableExperimentalCanvasFeatures(true); - if (IsSwitchEnabled(command_line, switches::kOverlayScrollbars)) - blink::WebRuntimeFeatures::enableOverlayScrollbars(true); - if (IsSwitchEnabled(command_line, switches::kOverlayFullscreenVideo)) - blink::WebRuntimeFeatures::enableOverlayFullscreenVideo(true); - if (IsSwitchEnabled(command_line, switches::kSharedWorker)) - blink::WebRuntimeFeatures::enableSharedWorker(true); +void AtomRendererClient::AddKeySystems( + std::vector* key_systems) { + AddChromeKeySystems(key_systems); } } // namespace atom diff --git a/atom/renderer/atom_renderer_client.h b/atom/renderer/atom_renderer_client.h index 206ed9f9b..beeeb9d53 100644 --- a/atom/renderer/atom_renderer_client.h +++ b/atom/renderer/atom_renderer_client.h @@ -6,6 +6,7 @@ #define ATOM_RENDERER_ATOM_RENDERER_CLIENT_H_ #include +#include #include "content/public/renderer/content_renderer_client.h" #include "content/public/renderer/render_process_observer.h" @@ -55,11 +56,7 @@ class AtomRendererClient : public content::ContentRendererClient, content::RenderFrame* render_frame, const std::string& mime_type, const GURL& original_url) override; - bool ShouldOverridePageVisibilityState( - const content::RenderFrame* render_frame, - blink::WebPageVisibilityState* override_state) override; - - void EnableWebRuntimeFeatures(); + void AddKeySystems(std::vector* key_systems) override; scoped_ptr node_bindings_; scoped_ptr atom_bindings_; diff --git a/atom/renderer/lib/init.coffee b/atom/renderer/lib/init.coffee index d9f104f8d..b9028f2dd 100644 --- a/atom/renderer/lib/init.coffee +++ b/atom/renderer/lib/init.coffee @@ -30,6 +30,9 @@ for arg in process.argv if arg.indexOf('--guest-instance-id=') == 0 # This is a guest web view. process.guestInstanceId = parseInt arg.substr(arg.indexOf('=') + 1) + else if arg.indexOf('--opener-id=') == 0 + # This is a guest BrowserWindow. + process.openerId = parseInt arg.substr(arg.indexOf('=') + 1) else if arg.indexOf('--node-integration=') == 0 nodeIntegration = arg.substr arg.indexOf('=') + 1 else if arg.indexOf('--preload=') == 0 diff --git a/atom/renderer/lib/inspector.coffee b/atom/renderer/lib/inspector.coffee index d5ddfd72e..364ccc39d 100644 --- a/atom/renderer/lib/inspector.coffee +++ b/atom/renderer/lib/inspector.coffee @@ -27,7 +27,9 @@ convertToMenuTemplate = (items) -> label: item.label enabled: item.enabled if item.id? - transformed.click = -> DevToolsAPI.contextMenuItemSelected item.id + transformed.click = -> + DevToolsAPI.contextMenuItemSelected item.id + DevToolsAPI.contextMenuCleared() template.push transformed template @@ -37,9 +39,7 @@ createMenu = (x, y, items, document) -> menu = Menu.buildFromTemplate convertToMenuTemplate(items) # The menu is expected to show asynchronously. - setImmediate -> - menu.popup remote.getCurrentWindow() - DevToolsAPI.contextMenuCleared() + setTimeout -> menu.popup remote.getCurrentWindow() showFileChooserDialog = (callback) -> {remote} = require 'electron' diff --git a/atom/renderer/lib/override.coffee b/atom/renderer/lib/override.coffee index 0b60ce0d6..5280f1927 100644 --- a/atom/renderer/lib/override.coffee +++ b/atom/renderer/lib/override.coffee @@ -8,11 +8,19 @@ resolveURL = (url) -> # Window object returned by "window.open". class BrowserWindowProxy + @proxies: {} + + @getOrCreate: (guestId) -> + @proxies[guestId] ?= new BrowserWindowProxy(guestId) + + @remove: (guestId) -> + delete @proxies[guestId] + constructor: (@guestId) -> @closed = false - ipcRenderer.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED', (event, guestId) => - if guestId is @guestId - @closed = true + ipcRenderer.once "ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_#{@guestId}", => + BrowserWindowProxy.remove(@guestId) + @closed = true close: -> ipcRenderer.send 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', @guestId @@ -24,7 +32,7 @@ class BrowserWindowProxy ipcRenderer.send 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_METHOD', @guestId, 'blur' postMessage: (message, targetOrigin='*') -> - ipcRenderer.send 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', @guestId, message, targetOrigin + ipcRenderer.send 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', @guestId, message, targetOrigin, location.origin eval: (args...) -> ipcRenderer.send 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', @guestId, 'executeJavaScript', args... @@ -61,7 +69,7 @@ window.open = (url, frameName='', features='') -> guestId = ipcRenderer.sendSync 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, frameName, options if guestId - new BrowserWindowProxy(guestId) + BrowserWindowProxy.getOrCreate(guestId) else null @@ -83,21 +91,17 @@ window.confirm = (message, title='') -> window.prompt = -> throw new Error('prompt() is and will not be supported.') -# Implement window.postMessage if current window is a guest window. -guestId = ipcRenderer.sendSync 'ATOM_SHELL_GUEST_WINDOW_MANAGER_GET_GUEST_ID' -if guestId? - window.opener = - postMessage: (message, targetOrigin='*') -> - ipcRenderer.send 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPENER_POSTMESSAGE', guestId, message, targetOrigin, location.origin +if process.openerId? + window.opener = BrowserWindowProxy.getOrCreate process.openerId -ipcRenderer.on 'ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', (event, guestId, message, sourceOrigin) -> +ipcRenderer.on 'ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', (event, sourceId, message, sourceOrigin) -> # Manually dispatch event instead of using postMessage because we also need to # set event.source. event = document.createEvent 'Event' event.initEvent 'message', false, false event.data = message event.origin = sourceOrigin - event.source = new BrowserWindowProxy(guestId) + event.source = BrowserWindowProxy.getOrCreate(sourceId) window.dispatchEvent event # Forward history operations to browser. @@ -114,6 +118,12 @@ Object.defineProperty window.history, 'length', get: -> getHistoryOperation 'length' -# Make document.hidden return the correct value. +# Make document.hidden and document.visibilityState return the correct value. Object.defineProperty document, 'hidden', - get: -> !remote.getCurrentWindow().isVisible() + get: -> + currentWindow = remote.getCurrentWindow() + currentWindow.isMinimized() || !currentWindow.isVisible() + +Object.defineProperty document, 'visibilityState', + get: -> + if document.hidden then "hidden" else "visible" diff --git a/atom/renderer/lib/web-view/guest-view-internal.coffee b/atom/renderer/lib/web-view/guest-view-internal.coffee index 61a93d8cb..30fe3d2a6 100644 --- a/atom/renderer/lib/web-view/guest-view-internal.coffee +++ b/atom/renderer/lib/web-view/guest-view-internal.coffee @@ -15,29 +15,43 @@ WEB_VIEW_EVENTS = 'did-get-redirect-request': ['oldURL', 'newURL', 'isMainFrame'] 'dom-ready': [] 'console-message': ['level', 'message', 'line', 'sourceId'] + 'devtools-opened': [] + 'devtools-closed': [] + 'devtools-focused': [] 'new-window': ['url', 'frameName', 'disposition', 'options'] + 'will-navigate': ['url'] + 'did-navigate': ['url'] + 'did-navigate-in-page': ['url'] 'close': [] 'crashed': [] 'gpu-crashed': [] 'plugin-crashed': ['name', 'version'] + 'media-started-playing': [] + 'media-paused': [] + 'did-change-theme-color': ['themeColor'] 'destroyed': [] - 'page-title-set': ['title', 'explicitSet'] + 'page-title-updated': ['title', 'explicitSet'] 'page-favicon-updated': ['favicons'] 'enter-html-full-screen': [] 'leave-html-full-screen': [] + 'found-in-page': ['result'] -dispatchEvent = (webView, event, args...) -> - throw new Error("Unknown event #{event}") unless WEB_VIEW_EVENTS[event]? - domEvent = new Event(event) - for f, i in WEB_VIEW_EVENTS[event] +DEPRECATED_EVENTS = + 'page-title-updated': 'page-title-set' + +dispatchEvent = (webView, eventName, eventKey, args...) -> + if DEPRECATED_EVENTS[eventName]? + dispatchEvent webView, DEPRECATED_EVENTS[eventName], eventKey, args... + domEvent = new Event(eventName) + for f, i in WEB_VIEW_EVENTS[eventKey] domEvent[f] = args[i] webView.dispatchEvent domEvent - webView.onLoadCommit domEvent if event == 'load-commit' + webView.onLoadCommit domEvent if eventName is 'load-commit' module.exports = registerEvents: (webView, viewInstanceId) -> - ipcRenderer.on "ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-#{viewInstanceId}", (event, domEvent, args...) -> - dispatchEvent webView, domEvent, args... + ipcRenderer.on "ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-#{viewInstanceId}", (event, eventName, args...) -> + dispatchEvent webView, eventName, eventName, args... ipcRenderer.on "ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-#{viewInstanceId}", (event, channel, args...) -> domEvent = new Event('ipc-message') diff --git a/atom/renderer/lib/web-view/web-view.coffee b/atom/renderer/lib/web-view/web-view.coffee index 5e3f7d6ba..fc0172574 100644 --- a/atom/renderer/lib/web-view/web-view.coffee +++ b/atom/renderer/lib/web-view/web-view.coffee @@ -1,4 +1,4 @@ -{deprecate, webFrame, remote} = require 'electron' +{deprecate, webFrame, remote, ipcRenderer} = require 'electron' v8Util = process.atomBinding 'v8_util' guestViewInternal = require './guest-view-internal' @@ -270,11 +270,10 @@ registerWebViewElement = -> 'isCrashed' 'setUserAgent' 'getUserAgent' - 'executeJavaScript' - 'insertCSS' 'openDevTools' 'closeDevTools' 'isDevToolsOpened' + 'isDevToolsFocused' 'inspectElement' 'setAudioMuted' 'isAudioMuted' @@ -289,20 +288,35 @@ registerWebViewElement = -> 'unselect' 'replace' 'replaceMisspelling' - 'send' + 'findInPage' + 'stopFindInPage' 'getId' + 'downloadURL' 'inspectServiceWorker' 'print' 'printToPDF' - 'sendInputEvent' + ] + + nonblockMethods = [ + 'send', + 'sendInputEvent', + 'executeJavaScript', + 'insertCSS' ] # Forward proto.foo* method calls to WebViewImpl.foo*. - createHandler = (m) -> + createBlockHandler = (m) -> (args...) -> internal = v8Util.getHiddenValue this, 'internal' internal.webContents[m] args... - proto[m] = createHandler m for m in methods + proto[m] = createBlockHandler m for m in methods + + createNonBlockHandler = (m) -> + (args...) -> + internal = v8Util.getHiddenValue this, 'internal' + ipcRenderer.send('ATOM_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', internal.guestInstanceId, m, args...) + + proto[m] = createNonBlockHandler m for m in nonblockMethods # Deprecated. deprecate.rename proto, 'getUrl', 'getURL' diff --git a/chromium_src/chrome/browser/media/desktop_media_list.h b/chromium_src/chrome/browser/media/desktop_media_list.h new file mode 100644 index 000000000..7ef703e8b --- /dev/null +++ b/chromium_src/chrome/browser/media/desktop_media_list.h @@ -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. + +#ifndef CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_LIST_H_ +#define CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_LIST_H_ + +#include "base/basictypes.h" +#include "base/strings/string16.h" +#include "base/time/time.h" +#include "content/public/browser/desktop_media_id.h" +#include "ui/gfx/image/image_skia.h" + +class DesktopMediaListObserver; + +// DesktopMediaList provides the list of desktop media source (screens, windows, +// tabs), and their thumbnails, to the desktop media picker dialog. It +// transparently updates the list in the background, and notifies the desktop +// media picker when something changes. +class DesktopMediaList { + public: + // Struct used to represent each entry in the list. + struct Source { + // Id of the source. + content::DesktopMediaID id; + + // Name of the source that should be shown to the user. + base::string16 name; + + // The thumbnail for the source. + gfx::ImageSkia thumbnail; + }; + + virtual ~DesktopMediaList() {} + + // Sets time interval between updates. By default list of sources and their + // thumbnail are updated once per second. If called after StartUpdating() then + // it will take effect only after the next update. + virtual void SetUpdatePeriod(base::TimeDelta period) = 0; + + // Sets size to which the thumbnails should be scaled. If called after + // StartUpdating() then some thumbnails may be still scaled to the old size + // until they are updated. + virtual void SetThumbnailSize(const gfx::Size& thumbnail_size) = 0; + + // Sets ID of the hosting desktop picker dialog. The window with this ID will + // be filtered out from the list of sources. + virtual void SetViewDialogWindowId(content::DesktopMediaID::Id dialog_id) = 0; + + // Starts updating the model. The model is initially empty, so OnSourceAdded() + // notifications will be generated for each existing source as it is + // enumerated. After the initial enumeration the model will be refreshed based + // on the update period, and notifications generated only for changes in the + // model. + virtual void StartUpdating(DesktopMediaListObserver* observer) = 0; + + virtual int GetSourceCount() const = 0; + virtual const Source& GetSource(int index) const = 0; + virtual std::vector GetSources() const = 0; +}; + +#endif // CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_LIST_H_ diff --git a/chromium_src/chrome/browser/media/desktop_media_list_observer.h b/chromium_src/chrome/browser/media/desktop_media_list_observer.h new file mode 100644 index 000000000..34cd626bf --- /dev/null +++ b/chromium_src/chrome/browser/media/desktop_media_list_observer.h @@ -0,0 +1,23 @@ +// 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_MEDIA_DESKTOP_MEDIA_LIST_OBSERVER_H_ +#define CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_LIST_OBSERVER_H_ + +// Interface implemented by the desktop media picker dialog to receive +// notifications about changes in DesktopMediaList. +class DesktopMediaListObserver { + public: + virtual void OnSourceAdded(int index) = 0; + virtual void OnSourceRemoved(int index) = 0; + virtual void OnSourceMoved(int old_index, int new_index) = 0; + virtual void OnSourceNameChanged(int index) = 0; + virtual void OnSourceThumbnailChanged(int index) = 0; + virtual bool OnRefreshFinished() = 0; + + protected: + virtual ~DesktopMediaListObserver() {} +}; + +#endif // CHROME_BROWSER_MEDIA_DESKTOP_MEDIA_LIST_OBSERVER_H_ diff --git a/chromium_src/chrome/browser/media/native_desktop_media_list.cc b/chromium_src/chrome/browser/media/native_desktop_media_list.cc new file mode 100644 index 000000000..4a7fb5896 --- /dev/null +++ b/chromium_src/chrome/browser/media/native_desktop_media_list.cc @@ -0,0 +1,375 @@ +// 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/browser/media/native_desktop_media_list.h" + +#include +#include +#include + +#include "base/hash.h" +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "base/threading/sequenced_worker_pool.h" +#include "chrome/browser/media/desktop_media_list_observer.h" +#include "content/public/browser/browser_thread.h" +#include "media/base/video_util.h" +#include "third_party/libyuv/include/libyuv/scale_argb.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" +#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" +#include "third_party/webrtc/modules/desktop_capture/window_capturer.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/gfx/skia_util.h" + +using content::BrowserThread; +using content::DesktopMediaID; + +namespace { + +// Update the list every second. +const int kDefaultUpdatePeriod = 1000; + +// Returns a hash of a DesktopFrame content to detect when image for a desktop +// media source has changed. +uint32 GetFrameHash(webrtc::DesktopFrame* frame) { + int data_size = frame->stride() * frame->size().height(); + return base::SuperFastHash(reinterpret_cast(frame->data()), data_size); +} + +gfx::ImageSkia ScaleDesktopFrame(scoped_ptr frame, + gfx::Size size) { + gfx::Rect scaled_rect = media::ComputeLetterboxRegion( + gfx::Rect(0, 0, size.width(), size.height()), + gfx::Size(frame->size().width(), frame->size().height())); + + SkBitmap result; + result.allocN32Pixels(scaled_rect.width(), scaled_rect.height(), true); + result.lockPixels(); + + uint8* pixels_data = reinterpret_cast(result.getPixels()); + libyuv::ARGBScale(frame->data(), frame->stride(), + frame->size().width(), frame->size().height(), + pixels_data, result.rowBytes(), + scaled_rect.width(), scaled_rect.height(), + libyuv::kFilterBilinear); + + // Set alpha channel values to 255 for all pixels. + // TODO(sergeyu): Fix screen/window capturers to capture alpha channel and + // remove this code. Currently screen/window capturers (at least some + // implementations) only capture R, G and B channels and set Alpha to 0. + // crbug.com/264424 + for (int y = 0; y < result.height(); ++y) { + for (int x = 0; x < result.width(); ++x) { + pixels_data[result.rowBytes() * y + x * result.bytesPerPixel() + 3] = + 0xff; + } + } + + result.unlockPixels(); + + return gfx::ImageSkia::CreateFrom1xBitmap(result); +} + +} // namespace + +NativeDesktopMediaList::SourceDescription::SourceDescription( + DesktopMediaID id, + const base::string16& name) + : id(id), + name(name) { +} + +class NativeDesktopMediaList::Worker + : public webrtc::DesktopCapturer::Callback { + public: + Worker(base::WeakPtr media_list, + scoped_ptr screen_capturer, + scoped_ptr window_capturer); + ~Worker() override; + + void Refresh(const gfx::Size& thumbnail_size, + content::DesktopMediaID::Id view_dialog_id); + + private: + typedef std::map ImageHashesMap; + + // webrtc::DesktopCapturer::Callback interface. + webrtc::SharedMemory* CreateSharedMemory(size_t size) override; + void OnCaptureCompleted(webrtc::DesktopFrame* frame) override; + + base::WeakPtr media_list_; + + scoped_ptr screen_capturer_; + scoped_ptr window_capturer_; + + scoped_ptr current_frame_; + + ImageHashesMap image_hashes_; + + DISALLOW_COPY_AND_ASSIGN(Worker); +}; + +NativeDesktopMediaList::Worker::Worker( + base::WeakPtr media_list, + scoped_ptr screen_capturer, + scoped_ptr window_capturer) + : media_list_(media_list), + screen_capturer_(screen_capturer.Pass()), + window_capturer_(window_capturer.Pass()) { + if (screen_capturer_) + screen_capturer_->Start(this); + if (window_capturer_) + window_capturer_->Start(this); +} + +NativeDesktopMediaList::Worker::~Worker() {} + +void NativeDesktopMediaList::Worker::Refresh( + const gfx::Size& thumbnail_size, + content::DesktopMediaID::Id view_dialog_id) { + std::vector sources; + + if (screen_capturer_) { + webrtc::ScreenCapturer::ScreenList screens; + if (screen_capturer_->GetScreenList(&screens)) { + bool mutiple_screens = screens.size() > 1; + base::string16 title; + for (size_t i = 0; i < screens.size(); ++i) { + if (mutiple_screens) { + title = base::UTF8ToUTF16("Screen " + base::IntToString(i+1)); + } else { + title = base::UTF8ToUTF16("Entire screen"); + } + sources.push_back(SourceDescription(DesktopMediaID( + DesktopMediaID::TYPE_SCREEN, screens[i].id), title)); + } + } + } + + if (window_capturer_) { + webrtc::WindowCapturer::WindowList windows; + if (window_capturer_->GetWindowList(&windows)) { + for (webrtc::WindowCapturer::WindowList::iterator it = windows.begin(); + it != windows.end(); ++it) { + // Skip the picker dialog window. + if (it->id != view_dialog_id) { + sources.push_back(SourceDescription( + DesktopMediaID(DesktopMediaID::TYPE_WINDOW, it->id), + base::UTF8ToUTF16(it->title))); + } + } + } + } + // Update list of windows before updating thumbnails. + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&NativeDesktopMediaList::OnSourcesList, + media_list_, sources)); + + ImageHashesMap new_image_hashes; + + // Get a thumbnail for each source. + for (size_t i = 0; i < sources.size(); ++i) { + SourceDescription& source = sources[i]; + switch (source.id.type) { + case DesktopMediaID::TYPE_SCREEN: + if (!screen_capturer_->SelectScreen(source.id.id)) + continue; + screen_capturer_->Capture(webrtc::DesktopRegion()); + break; + + case DesktopMediaID::TYPE_WINDOW: + if (!window_capturer_->SelectWindow(source.id.id)) + continue; + window_capturer_->Capture(webrtc::DesktopRegion()); + break; + + default: + NOTREACHED(); + } + + // Expect that DesktopCapturer to always captures frames synchronously. + // |current_frame_| may be NULL if capture failed (e.g. because window has + // been closed). + if (current_frame_) { + uint32 frame_hash = GetFrameHash(current_frame_.get()); + new_image_hashes[source.id] = frame_hash; + + // Scale the image only if it has changed. + ImageHashesMap::iterator it = image_hashes_.find(source.id); + if (it == image_hashes_.end() || it->second != frame_hash) { + gfx::ImageSkia thumbnail = + ScaleDesktopFrame(current_frame_.Pass(), thumbnail_size); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&NativeDesktopMediaList::OnSourceThumbnail, + media_list_, i, thumbnail)); + } + } + } + + image_hashes_.swap(new_image_hashes); + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&NativeDesktopMediaList::OnRefreshFinished, media_list_)); +} + +webrtc::SharedMemory* NativeDesktopMediaList::Worker::CreateSharedMemory( + size_t size) { + return NULL; +} + +void NativeDesktopMediaList::Worker::OnCaptureCompleted( + webrtc::DesktopFrame* frame) { + current_frame_.reset(frame); +} + +NativeDesktopMediaList::NativeDesktopMediaList( + scoped_ptr screen_capturer, + scoped_ptr window_capturer) + : screen_capturer_(screen_capturer.Pass()), + window_capturer_(window_capturer.Pass()), + update_period_(base::TimeDelta::FromMilliseconds(kDefaultUpdatePeriod)), + thumbnail_size_(100, 100), + view_dialog_id_(-1), + observer_(NULL), + weak_factory_(this) { + base::SequencedWorkerPool* worker_pool = BrowserThread::GetBlockingPool(); + capture_task_runner_ = worker_pool->GetSequencedTaskRunner( + worker_pool->GetSequenceToken()); +} + +NativeDesktopMediaList::~NativeDesktopMediaList() { + capture_task_runner_->DeleteSoon(FROM_HERE, worker_.release()); +} + +void NativeDesktopMediaList::SetUpdatePeriod(base::TimeDelta period) { + DCHECK(!observer_); + update_period_ = period; +} + +void NativeDesktopMediaList::SetThumbnailSize( + const gfx::Size& thumbnail_size) { + thumbnail_size_ = thumbnail_size; +} + +void NativeDesktopMediaList::SetViewDialogWindowId( + content::DesktopMediaID::Id dialog_id) { + view_dialog_id_ = dialog_id; +} + +void NativeDesktopMediaList::StartUpdating(DesktopMediaListObserver* observer) { + DCHECK(!observer_); + DCHECK(screen_capturer_ || window_capturer_); + + observer_ = observer; + + worker_.reset(new Worker(weak_factory_.GetWeakPtr(), + screen_capturer_.Pass(), window_capturer_.Pass())); + Refresh(); +} + +int NativeDesktopMediaList::GetSourceCount() const { + return sources_.size(); +} + +const DesktopMediaList::Source& NativeDesktopMediaList::GetSource( + int index) const { + return sources_[index]; +} + +std::vector NativeDesktopMediaList::GetSources() const { + return sources_; +} + +void NativeDesktopMediaList::Refresh() { + capture_task_runner_->PostTask( + FROM_HERE, base::Bind(&Worker::Refresh, base::Unretained(worker_.get()), + thumbnail_size_, view_dialog_id_)); +} + +void NativeDesktopMediaList::OnSourcesList( + const std::vector& new_sources) { + typedef std::set SourceSet; + SourceSet new_source_set; + for (size_t i = 0; i < new_sources.size(); ++i) { + new_source_set.insert(new_sources[i].id); + } + // Iterate through the old sources to find the removed sources. + for (size_t i = 0; i < sources_.size(); ++i) { + if (new_source_set.find(sources_[i].id) == new_source_set.end()) { + observer_->OnSourceRemoved(i); + sources_.erase(sources_.begin() + i); + --i; + } + } + // Iterate through the new sources to find the added sources. + if (new_sources.size() > sources_.size()) { + SourceSet old_source_set; + for (size_t i = 0; i < sources_.size(); ++i) { + old_source_set.insert(sources_[i].id); + } + + for (size_t i = 0; i < new_sources.size(); ++i) { + if (old_source_set.find(new_sources[i].id) == old_source_set.end()) { + sources_.insert(sources_.begin() + i, Source()); + sources_[i].id = new_sources[i].id; + sources_[i].name = new_sources[i].name; + observer_->OnSourceAdded(i); + } + } + } + DCHECK_EQ(new_sources.size(), sources_.size()); + + // Find the moved/changed sources. + size_t pos = 0; + while (pos < sources_.size()) { + if (!(sources_[pos].id == new_sources[pos].id)) { + // Find the source that should be moved to |pos|, starting from |pos + 1| + // of |sources_|, because entries before |pos| should have been sorted. + size_t old_pos = pos + 1; + for (; old_pos < sources_.size(); ++old_pos) { + if (sources_[old_pos].id == new_sources[pos].id) + break; + } + DCHECK(sources_[old_pos].id == new_sources[pos].id); + + // Move the source from |old_pos| to |pos|. + Source temp = sources_[old_pos]; + sources_.erase(sources_.begin() + old_pos); + sources_.insert(sources_.begin() + pos, temp); + + observer_->OnSourceMoved(old_pos, pos); + } + + if (sources_[pos].name != new_sources[pos].name) { + sources_[pos].name = new_sources[pos].name; + observer_->OnSourceNameChanged(pos); + } + ++pos; + } +} + +void NativeDesktopMediaList::OnSourceThumbnail( + int index, + const gfx::ImageSkia& image) { + DCHECK_LT(index, static_cast(sources_.size())); + sources_[index].thumbnail = image; + observer_->OnSourceThumbnailChanged(index); +} + +void NativeDesktopMediaList::OnRefreshFinished() { + // Give a chance to the observer to stop the refresh work. + bool is_continue = observer_->OnRefreshFinished(); + if (is_continue) { + BrowserThread::PostDelayedTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&NativeDesktopMediaList::Refresh, + weak_factory_.GetWeakPtr()), + update_period_); + } +} diff --git a/chromium_src/chrome/browser/media/native_desktop_media_list.h b/chromium_src/chrome/browser/media/native_desktop_media_list.h new file mode 100644 index 000000000..943d3dd32 --- /dev/null +++ b/chromium_src/chrome/browser/media/native_desktop_media_list.h @@ -0,0 +1,101 @@ +// 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_MEDIA_NATIVE_DESKTOP_MEDIA_LIST_H_ +#define CHROME_BROWSER_MEDIA_NATIVE_DESKTOP_MEDIA_LIST_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/sequenced_task_runner.h" +#include "chrome/browser/media/desktop_media_list.h" +#include "content/public/browser/desktop_media_id.h" +#include "ui/gfx/image/image_skia.h" + +namespace webrtc { +class ScreenCapturer; +class WindowCapturer; +} + +// Implementation of DesktopMediaList that shows native screens and +// native windows. +class NativeDesktopMediaList : public DesktopMediaList { + public: + // Caller may pass NULL for either of the arguments in case when only some + // types of sources the model should be populated with (e.g. it will only + // contain windows, if |screen_capturer| is NULL). + NativeDesktopMediaList( + scoped_ptr screen_capturer, + scoped_ptr window_capturer); + ~NativeDesktopMediaList() override; + + // DesktopMediaList interface. + void SetUpdatePeriod(base::TimeDelta period) override; + void SetThumbnailSize(const gfx::Size& thumbnail_size) override; + void StartUpdating(DesktopMediaListObserver* observer) override; + int GetSourceCount() const override; + const Source& GetSource(int index) const override; + std::vector GetSources() const override; + void SetViewDialogWindowId(content::DesktopMediaID::Id dialog_id) override; + + private: + class Worker; + friend class Worker; + + // Struct used to represent sources list the model gets from the Worker. + struct SourceDescription { + SourceDescription(content::DesktopMediaID id, const base::string16& name); + + content::DesktopMediaID id; + base::string16 name; + }; + + // Order comparator for sources. Used to sort list of sources. + static bool CompareSources(const SourceDescription& a, + const SourceDescription& b); + + // Post a task for the |worker_| to update list of windows and get thumbnails. + void Refresh(); + + // Called by |worker_| to refresh the model. First it posts tasks for + // OnSourcesList() with the fresh list of sources, then follows with + // OnSourceThumbnail() for each changed thumbnail and then calls + // OnRefreshFinished() at the end. + void OnSourcesList(const std::vector& sources); + void OnSourceThumbnail(int index, const gfx::ImageSkia& thumbnail); + void OnRefreshFinished(); + + // Capturers specified in SetCapturers() and passed to the |worker_| later. + scoped_ptr screen_capturer_; + scoped_ptr window_capturer_; + + // Time interval between mode updates. + base::TimeDelta update_period_; + + // Size of thumbnails generated by the model. + gfx::Size thumbnail_size_; + + // ID of the hosting dialog. + content::DesktopMediaID::Id view_dialog_id_; + + // The observer passed to StartUpdating(). + DesktopMediaListObserver* observer_; + + // Task runner used for the |worker_|. + scoped_refptr capture_task_runner_; + + // An object that does all the work of getting list of sources on a background + // thread (see |capture_task_runner_|). Destroyed on |capture_task_runner_| + // after the model is destroyed. + scoped_ptr worker_; + + // Current list of sources. + std::vector sources_; + + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(NativeDesktopMediaList); +}; + +#endif // CHROME_BROWSER_MEDIA_NATIVE_DESKTOP_MEDIA_LIST_H_ diff --git a/chromium_src/chrome/browser/printing/print_view_manager_base.cc b/chromium_src/chrome/browser/printing/print_view_manager_base.cc index 35c62fbc5..ede1d3b8b 100644 --- a/chromium_src/chrome/browser/printing/print_view_manager_base.cc +++ b/chromium_src/chrome/browser/printing/print_view_manager_base.cc @@ -410,7 +410,7 @@ bool PrintViewManagerBase::RunInnerMessageLoop() { // be CPU bound, the page overly complex/large or the system just // memory-bound. static const int kPrinterSettingsTimeout = 60000; - base::OneShotTimer quit_timer; + base::OneShotTimer quit_timer; quit_timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(kPrinterSettingsTimeout), base::MessageLoop::current(), &base::MessageLoop::Quit); diff --git a/chromium_src/chrome/browser/process_singleton_posix.cc b/chromium_src/chrome/browser/process_singleton_posix.cc index b03ce431e..98fb94873 100644 --- a/chromium_src/chrome/browser/process_singleton_posix.cc +++ b/chromium_src/chrome/browser/process_singleton_posix.cc @@ -503,7 +503,7 @@ class ProcessSingleton::LinuxWatcher // reads. size_t bytes_read_; - base::OneShotTimer timer_; + base::OneShotTimer timer_; DISALLOW_COPY_AND_ASSIGN(SocketReader); }; diff --git a/chromium_src/chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.cc b/chromium_src/chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.cc new file mode 100644 index 000000000..c926b9e94 --- /dev/null +++ b/chromium_src/chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.cc @@ -0,0 +1,70 @@ +// Copyright (c) 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/browser/renderer_host/pepper/widevine_cdm_message_filter.h" + +#include "base/bind.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/common/webplugininfo.h" +#include "content/public/browser/plugin_service.h" + +using content::PluginService; +using content::WebPluginInfo; +using content::BrowserThread; + +WidevineCdmMessageFilter::WidevineCdmMessageFilter( + int render_process_id, + content::BrowserContext* browser_context) + : BrowserMessageFilter(ChromeMsgStart), + render_process_id_(render_process_id), + browser_context_(browser_context) { +} + +bool WidevineCdmMessageFilter::OnMessageReceived(const IPC::Message& message) { + IPC_BEGIN_MESSAGE_MAP(WidevineCdmMessageFilter, message) +#if defined(ENABLE_PEPPER_CDMS) + IPC_MESSAGE_HANDLER( + ChromeViewHostMsg_IsInternalPluginAvailableForMimeType, + OnIsInternalPluginAvailableForMimeType) +#endif + IPC_MESSAGE_UNHANDLED(return false) + IPC_END_MESSAGE_MAP() + return true; +} + +#if defined(ENABLE_PEPPER_CDMS) +void WidevineCdmMessageFilter::OnIsInternalPluginAvailableForMimeType( + const std::string& mime_type, + bool* is_available, + std::vector* additional_param_names, + std::vector* additional_param_values) { + std::vector plugins; + PluginService::GetInstance()->GetInternalPlugins(&plugins); + + for (size_t i = 0; i < plugins.size(); ++i) { + const WebPluginInfo& plugin = plugins[i]; + const std::vector& mime_types = + plugin.mime_types; + for (size_t j = 0; j < mime_types.size(); ++j) { + + if (mime_types[j].mime_type == mime_type) { + *is_available = true; + *additional_param_names = mime_types[j].additional_param_names; + *additional_param_values = mime_types[j].additional_param_values; + return; + } + } + } + + *is_available = false; +} +#endif // defined(ENABLE_PEPPER_CDMS) + +void WidevineCdmMessageFilter::OnDestruct() const { + BrowserThread::DeleteOnUIThread::Destruct(this); +} + +WidevineCdmMessageFilter::~WidevineCdmMessageFilter() { +} diff --git a/chromium_src/chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.h b/chromium_src/chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.h new file mode 100644 index 000000000..b8f3d562a --- /dev/null +++ b/chromium_src/chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.h @@ -0,0 +1,49 @@ +// Copyright (c) 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_WIDEVINE_CDM_MESSAGE_FILTER_H_ +#define CHROME_BROWSER_RENDERER_HOST_PEPPER_WIDEVINE_CDM_MESSAGE_FILTER_H_ + +#include "chrome/common/widevine_cdm_messages.h" +#include "content/public/browser/browser_message_filter.h" + +namespace content { +class BrowserContext; +} + +class WidevineCdmMessageFilter : public content::BrowserMessageFilter { + public: + explicit WidevineCdmMessageFilter(int render_process_id, + content::BrowserContext* browser_context); + bool OnMessageReceived(const IPC::Message& message) override; + void OnDestruct() const override; + + private: + friend class content::BrowserThread; + friend class base::DeleteHelper; + + virtual ~WidevineCdmMessageFilter(); + + #if defined(ENABLE_PEPPER_CDMS) + // Returns whether any internal plugin supporting |mime_type| is registered + // and enabled. Does not determine whether the plugin can actually be + // instantiated (e.g. whether it has all its dependencies). + // When the returned *|is_available| is true, |additional_param_names| and + // |additional_param_values| contain the name-value pairs, if any, specified + // for the *first* non-disabled plugin found that is registered for + // |mime_type|. + void OnIsInternalPluginAvailableForMimeType( + const std::string& mime_type, + bool* is_available, + std::vector* additional_param_names, + std::vector* additional_param_values); +#endif + + int render_process_id_; + content::BrowserContext* browser_context_; + + DISALLOW_COPY_AND_ASSIGN(WidevineCdmMessageFilter); +}; + +#endif // CHROME_BROWSER_RENDERER_HOST_PEPPER_WIDEVINE_CDM_MESSAGE_FILTER_H_ diff --git a/chromium_src/chrome/browser/speech/tts_controller_impl.cc b/chromium_src/chrome/browser/speech/tts_controller_impl.cc index 6b66b6a61..610ce1656 100644 --- a/chromium_src/chrome/browser/speech/tts_controller_impl.cc +++ b/chromium_src/chrome/browser/speech/tts_controller_impl.cc @@ -111,7 +111,7 @@ TtsController* TtsController::GetInstance() { // static TtsControllerImpl* TtsControllerImpl::GetInstance() { - return Singleton::get(); + return base::Singleton::get(); } TtsControllerImpl::TtsControllerImpl() diff --git a/chromium_src/chrome/browser/speech/tts_controller_impl.h b/chromium_src/chrome/browser/speech/tts_controller_impl.h index 651f836cd..6c8aa5747 100644 --- a/chromium_src/chrome/browser/speech/tts_controller_impl.h +++ b/chromium_src/chrome/browser/speech/tts_controller_impl.h @@ -77,7 +77,7 @@ class TtsControllerImpl : public TtsController { int GetMatchingVoice(const Utterance* utterance, std::vector& voices); - friend struct DefaultSingletonTraits; + friend struct base::DefaultSingletonTraits; // The current utterance being spoken. Utterance* current_utterance_; @@ -101,4 +101,4 @@ class TtsControllerImpl : public TtsController { DISALLOW_COPY_AND_ASSIGN(TtsControllerImpl); }; -#endif // CHROME_BROWSER_SPEECH_TTS_CONTROLLER_IMPL_H_ \ No newline at end of file +#endif // CHROME_BROWSER_SPEECH_TTS_CONTROLLER_IMPL_H_ diff --git a/chromium_src/chrome/browser/speech/tts_linux.cc b/chromium_src/chrome/browser/speech/tts_linux.cc index 43b28a5ea..ba15516ce 100644 --- a/chromium_src/chrome/browser/speech/tts_linux.cc +++ b/chromium_src/chrome/browser/speech/tts_linux.cc @@ -13,6 +13,7 @@ #include "base/synchronization/lock.h" #include "chrome/browser/speech/tts_platform.h" #include "content/public/browser/browser_thread.h" +#include "content/public/common/content_switches.h" #include "library_loaders/libspeechd.h" @@ -32,18 +33,17 @@ struct SPDChromeVoice { class TtsPlatformImplLinux : public TtsPlatformImpl { public: - virtual bool PlatformImplAvailable() override; - virtual bool Speak( - int utterance_id, - const std::string& utterance, - const std::string& lang, - const VoiceData& voice, - const UtteranceContinuousParameters& params) override; - virtual bool StopSpeaking() override; - virtual void Pause() override; - virtual void Resume() override; - virtual bool IsSpeaking() override; - virtual void GetVoices(std::vector* out_voices) override; + bool PlatformImplAvailable() override; + bool Speak(int utterance_id, + const std::string& utterance, + const std::string& lang, + const VoiceData& voice, + const UtteranceContinuousParameters& params) override; + bool StopSpeaking() override; + void Pause() override; + void Resume() override; + bool IsSpeaking() override; + void GetVoices(std::vector* out_voices) override; void OnSpeechEvent(SPDNotificationType type); @@ -52,7 +52,7 @@ class TtsPlatformImplLinux : public TtsPlatformImpl { private: TtsPlatformImplLinux(); - virtual ~TtsPlatformImplLinux(); + ~TtsPlatformImplLinux() override; // Initiate the connection with the speech dispatcher. void Initialize(); @@ -83,7 +83,7 @@ class TtsPlatformImplLinux : public TtsPlatformImpl { // uniquely identify a voice across all available modules. scoped_ptr > all_native_voices_; - friend struct DefaultSingletonTraits; + friend struct base::DefaultSingletonTraits; DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplLinux); }; @@ -94,6 +94,11 @@ SPDNotificationType TtsPlatformImplLinux::current_notification_ = TtsPlatformImplLinux::TtsPlatformImplLinux() : utterance_id_(0) { + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + if (!command_line.HasSwitch(switches::kEnableSpeechDispatcher)) + return; + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(&TtsPlatformImplLinux::Initialize, @@ -111,7 +116,7 @@ void TtsPlatformImplLinux::Initialize() { // http://crbug.com/317360 ANNOTATE_SCOPED_MEMORY_LEAK; conn_ = libspeechd_loader_.spd_open( - "chrome", "extension_api", NULL, SPD_MODE_SINGLE); + "chrome", "extension_api", NULL, SPD_MODE_THREADED); } if (!conn_) return; @@ -146,7 +151,7 @@ void TtsPlatformImplLinux::Reset() { if (conn_) libspeechd_loader_.spd_close(conn_); conn_ = libspeechd_loader_.spd_open( - "chrome", "extension_api", NULL, SPD_MODE_SINGLE); + "chrome", "extension_api", NULL, SPD_MODE_THREADED); } bool TtsPlatformImplLinux::PlatformImplAvailable() { @@ -187,6 +192,10 @@ bool TtsPlatformImplLinux::Speak( libspeechd_loader_.spd_set_voice_rate(conn_, 100 * log10(rate) / log10(3)); libspeechd_loader_.spd_set_voice_pitch(conn_, 100 * log10(pitch) / log10(3)); + // Support languages other than the default + if (!lang.empty()) + libspeechd_loader_.spd_set_language(conn_, lang.c_str()); + utterance_ = utterance; utterance_id_ = utterance_id; @@ -337,8 +346,9 @@ void TtsPlatformImplLinux::IndexMarkCallback(size_t msg_id, // static TtsPlatformImplLinux* TtsPlatformImplLinux::GetInstance() { - return Singleton >::get(); + return base::Singleton< + TtsPlatformImplLinux, + base::LeakySingletonTraits>::get(); } // static diff --git a/chromium_src/chrome/browser/speech/tts_mac.mm b/chromium_src/chrome/browser/speech/tts_mac.mm index acfa5b58b..aafbd4692 100644 --- a/chromium_src/chrome/browser/speech/tts_mac.mm +++ b/chromium_src/chrome/browser/speech/tts_mac.mm @@ -91,7 +91,7 @@ class TtsPlatformImplMac : public TtsPlatformImpl { int last_char_index_; bool paused_; - friend struct DefaultSingletonTraits; + friend struct base::DefaultSingletonTraits; DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplMac); }; @@ -291,7 +291,7 @@ TtsPlatformImplMac::~TtsPlatformImplMac() { // static TtsPlatformImplMac* TtsPlatformImplMac::GetInstance() { - return Singleton::get(); + return base::Singleton::get(); } @implementation ChromeTtsDelegate diff --git a/chromium_src/chrome/browser/speech/tts_win.cc b/chromium_src/chrome/browser/speech/tts_win.cc index c7b0a0ca7..ac2582058 100644 --- a/chromium_src/chrome/browser/speech/tts_win.cc +++ b/chromium_src/chrome/browser/speech/tts_win.cc @@ -15,26 +15,26 @@ class TtsPlatformImplWin : public TtsPlatformImpl { public: - virtual bool PlatformImplAvailable() { + bool PlatformImplAvailable() override { return true; } - virtual bool Speak( + bool Speak( int utterance_id, const std::string& utterance, const std::string& lang, const VoiceData& voice, - const UtteranceContinuousParameters& params); + const UtteranceContinuousParameters& params) override; - virtual bool StopSpeaking(); + bool StopSpeaking() override; - virtual void Pause(); + void Pause() override; - virtual void Resume(); + void Resume() override; - virtual bool IsSpeaking(); + bool IsSpeaking() override; - virtual void GetVoices(std::vector* out_voices) override; + void GetVoices(std::vector* out_voices) override; // Get the single instance of this class. static TtsPlatformImplWin* GetInstance(); @@ -43,7 +43,7 @@ class TtsPlatformImplWin : public TtsPlatformImpl { private: TtsPlatformImplWin(); - virtual ~TtsPlatformImplWin() {} + ~TtsPlatformImplWin() override {} void OnSpeechEvent(); @@ -57,7 +57,7 @@ class TtsPlatformImplWin : public TtsPlatformImpl { int char_position_; bool paused_; - friend struct DefaultSingletonTraits; + friend struct base::DefaultSingletonTraits; DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplWin); }; @@ -220,6 +220,8 @@ void TtsPlatformImplWin::OnSpeechEvent() { utterance_id_, TTS_EVENT_SENTENCE, char_position_, std::string()); break; + default: + break; } } } @@ -246,12 +248,12 @@ TtsPlatformImplWin::TtsPlatformImplWin() // static TtsPlatformImplWin* TtsPlatformImplWin::GetInstance() { - return Singleton >::get(); + return base::Singleton>::get(); } // static void TtsPlatformImplWin::SpeechEventCallback( WPARAM w_param, LPARAM l_param) { GetInstance()->OnSpeechEvent(); -} \ No newline at end of file +} diff --git a/chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc b/chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc index 0d2a6dd73..cead675a7 100644 --- a/chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc +++ b/chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc @@ -21,7 +21,7 @@ const char kAppMenuRegistrarPath[] = "/com/canonical/AppMenu/Registrar"; // static GlobalMenuBarRegistrarX11* GlobalMenuBarRegistrarX11::GetInstance() { - return Singleton::get(); + return base::Singleton::get(); } void GlobalMenuBarRegistrarX11::OnWindowMapped(unsigned long xid) { @@ -39,7 +39,7 @@ void GlobalMenuBarRegistrarX11::OnWindowUnmapped(unsigned long xid) { } GlobalMenuBarRegistrarX11::GlobalMenuBarRegistrarX11() - : registrar_proxy_(NULL) { + : registrar_proxy_(nullptr) { // libdbusmenu uses the gio version of dbus; I tried using the code in dbus/, // but it looks like that's isn't sharing the bus name with the gio version, // even when |connection_type| is set to SHARED. @@ -49,11 +49,11 @@ GlobalMenuBarRegistrarX11::GlobalMenuBarRegistrarX11() G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START), - NULL, + nullptr, kAppMenuRegistrarName, kAppMenuRegistrarPath, kAppMenuRegistrarName, - NULL, // TODO: Probalby want a real cancelable. + nullptr, // TODO: Probalby want a real cancelable. static_cast(OnProxyCreatedThunk), this); } @@ -84,9 +84,9 @@ void GlobalMenuBarRegistrarX11::RegisterXID(unsigned long xid) { "RegisterWindow", g_variant_new("(uo)", xid, path.c_str()), G_DBUS_CALL_FLAGS_NONE, -1, - NULL, - NULL, - NULL); + nullptr, + nullptr, + nullptr); } void GlobalMenuBarRegistrarX11::UnregisterXID(unsigned long xid) { @@ -105,14 +105,14 @@ void GlobalMenuBarRegistrarX11::UnregisterXID(unsigned long xid) { "UnregisterWindow", g_variant_new("(u)", xid), G_DBUS_CALL_FLAGS_NONE, -1, - NULL, - NULL, - NULL); + nullptr, + nullptr, + nullptr); } void GlobalMenuBarRegistrarX11::OnProxyCreated(GObject* source, GAsyncResult* result) { - GError* error = NULL; + GError* error = nullptr; GDBusProxy* proxy = g_dbus_proxy_new_for_bus_finish(result, &error); if (error) { g_error_free(error); @@ -128,7 +128,7 @@ void GlobalMenuBarRegistrarX11::OnProxyCreated(GObject* source, g_signal_connect(registrar_proxy_, "notify::g-name-owner", G_CALLBACK(OnNameOwnerChangedThunk), this); - OnNameOwnerChanged(NULL, NULL); + OnNameOwnerChanged(nullptr, nullptr); } void GlobalMenuBarRegistrarX11::OnNameOwnerChanged(GObject* /* ignored */, diff --git a/chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h b/chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h index e35e87c0d..694f776b2 100644 --- a/chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h +++ b/chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h @@ -28,7 +28,7 @@ class GlobalMenuBarRegistrarX11 { void OnWindowUnmapped(unsigned long xid); private: - friend struct DefaultSingletonTraits; + friend struct base::DefaultSingletonTraits; GlobalMenuBarRegistrarX11(); ~GlobalMenuBarRegistrarX11(); diff --git a/chromium_src/chrome/common/chrome_paths.cc b/chromium_src/chrome/common/chrome_paths.cc index d8a32446f..293ea2638 100644 --- a/chromium_src/chrome/common/chrome_paths.cc +++ b/chromium_src/chrome/common/chrome_paths.cc @@ -15,6 +15,8 @@ #include "base/version.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths_internal.h" +#include "chrome/common/widevine_cdm_constants.h" +#include "third_party/widevine/cdm/stub/widevine_cdm_version.h" #if defined(OS_ANDROID) #include "base/android/path_utils.h" @@ -362,7 +364,7 @@ bool PathProvider(int key, base::FilePath* result) { case chrome::DIR_COMPONENT_WIDEVINE_CDM: if (!PathService::Get(chrome::DIR_USER_DATA, &cur)) return false; - cur = cur.Append(FILE_PATH_LITERAL("WidevineCDM")); + cur = cur.Append(kWidevineCdmBaseDirectory); break; #endif // defined(WIDEVINE_CDM_IS_COMPONENT) // TODO(xhwang): FILE_WIDEVINE_CDM_ADAPTER has different meanings. diff --git a/chromium_src/chrome/common/chrome_paths.h b/chromium_src/chrome/common/chrome_paths.h index 581fdc06f..3a76e3295 100644 --- a/chromium_src/chrome/common/chrome_paths.h +++ b/chromium_src/chrome/common/chrome_paths.h @@ -17,7 +17,7 @@ class FilePath; namespace chrome { enum { - PATH_START = 1000, + PATH_START = 2000, DIR_APP = PATH_START, // Directory where dlls and data reside. DIR_LOGS, // Directory where logs should be written. diff --git a/chromium_src/chrome/common/widevine_cdm_constants.cc b/chromium_src/chrome/common/widevine_cdm_constants.cc new file mode 100644 index 000000000..60f487e2a --- /dev/null +++ b/chromium_src/chrome/common/widevine_cdm_constants.cc @@ -0,0 +1,16 @@ +// Copyright (c) 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/common/widevine_cdm_constants.h" + +#include "build/build_config.h" +#include "ppapi/shared_impl/ppapi_permissions.h" + +const base::FilePath::CharType kWidevineCdmBaseDirectory[] = + FILE_PATH_LITERAL("WidevineCDM"); + +const char kWidevineCdmPluginExtension[] = ""; + +const int32 kWidevineCdmPluginPermissions = ppapi::PERMISSION_DEV | + ppapi::PERMISSION_PRIVATE; diff --git a/chromium_src/chrome/common/widevine_cdm_constants.h b/chromium_src/chrome/common/widevine_cdm_constants.h new file mode 100644 index 000000000..b626079a1 --- /dev/null +++ b/chromium_src/chrome/common/widevine_cdm_constants.h @@ -0,0 +1,19 @@ +// Copyright (c) 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_COMMON_WIDEVINE_CDM_CONSTANTS_H_ +#define CHROME_COMMON_WIDEVINE_CDM_CONSTANTS_H_ + +#include "base/basictypes.h" +#include "base/files/file_path.h" + +// The Widevine CDM adapter and Widevine CDM are in this directory. +extern const base::FilePath::CharType kWidevineCdmBaseDirectory[]; + +extern const char kWidevineCdmPluginExtension[]; + +// Permission bits for Widevine CDM plugin. +extern const int32 kWidevineCdmPluginPermissions; + +#endif // CHROME_COMMON_WIDEVINE_CDM_CONSTANTS_H_ diff --git a/chromium_src/chrome/common/widevine_cdm_messages.h b/chromium_src/chrome/common/widevine_cdm_messages.h new file mode 100644 index 000000000..17c908776 --- /dev/null +++ b/chromium_src/chrome/common/widevine_cdm_messages.h @@ -0,0 +1,31 @@ +// Copyright (c) 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. + +// Multiply-included message file, hence no include guard. + +#include + +#include "ipc/ipc_message_macros.h" +// #include "ipc/ipc_param_traits.h" + +#define IPC_MESSAGE_START ChromeMsgStart + +// Renderer -> Browser messages. + +#if defined(ENABLE_PEPPER_CDMS) +// Returns whether any internal plugin supporting |mime_type| is registered and +// enabled. Does not determine whether the plugin can actually be instantiated +// (e.g. whether it has all its dependencies). +// When the returned *|is_available| is true, |additional_param_names| and +// |additional_param_values| contain the name-value pairs, if any, specified +// for the *first* non-disabled plugin found that is registered for |mime_type|. +IPC_SYNC_MESSAGE_CONTROL1_3( + ChromeViewHostMsg_IsInternalPluginAvailableForMimeType, + std::string /* mime_type */, + bool /* is_available */, + std::vector /* additional_param_names */, + std::vector /* additional_param_values */) +#endif + +// Browser -> Renderer messages. diff --git a/chromium_src/chrome/renderer/media/chrome_key_systems.cc b/chromium_src/chrome/renderer/media/chrome_key_systems.cc new file mode 100644 index 000000000..5d9716900 --- /dev/null +++ b/chromium_src/chrome/renderer/media/chrome_key_systems.cc @@ -0,0 +1,157 @@ +// 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/renderer/media/chrome_key_systems.h" + +#include +#include + +#include "base/logging.h" +#include "base/strings/string16.h" +#include "base/strings/string_split.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/common/widevine_cdm_messages.h" +#include "components/cdm/renderer/widevine_key_systems.h" +#include "content/public/renderer/render_thread.h" +#include "media/base/eme_constants.h" + +#if defined(OS_ANDROID) +#include "components/cdm/renderer/android_key_systems.h" +#endif + +// #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR. +#include "third_party/widevine/cdm/stub/widevine_cdm_version.h" + +// The following must be after widevine_cdm_version.h. + +#if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_MIN_GLIBC_VERSION) +#include +#include "base/version.h" +#endif + +using media::KeySystemInfo; +using media::SupportedCodecs; + +#if defined(ENABLE_PEPPER_CDMS) +static bool IsPepperCdmAvailable( + const std::string& pepper_type, + std::vector* additional_param_names, + std::vector* additional_param_values) { + bool is_available = false; + content::RenderThread::Get()->Send( + new ChromeViewHostMsg_IsInternalPluginAvailableForMimeType( + pepper_type, + &is_available, + additional_param_names, + additional_param_values)); + + return is_available; +} + +#if defined(WIDEVINE_CDM_AVAILABLE) +// This function finds "codecs" and parses the value into the vector |codecs|. +// Converts the codec strings to UTF-8 since we only expect ASCII strings and +// this simplifies the rest of the code in this file. +void GetSupportedCodecsForPepperCdm( + const std::vector& additional_param_names, + const std::vector& additional_param_values, + std::vector* codecs) { + DCHECK(codecs->empty()); + DCHECK_EQ(additional_param_names.size(), additional_param_values.size()); + for (size_t i = 0; i < additional_param_names.size(); ++i) { + if (additional_param_names[i] == + base::ASCIIToUTF16(kCdmSupportedCodecsParamName)) { + const base::string16& codecs_string16 = additional_param_values[i]; + std::string codecs_string; + if (!base::UTF16ToUTF8(codecs_string16.c_str(), + codecs_string16.length(), + &codecs_string)) { + DLOG(WARNING) << "Non-UTF-8 codecs string."; + // Continue using the best effort conversion. + } + *codecs = base::SplitString( + codecs_string, std::string(1, kCdmSupportedCodecsValueDelimiter), + base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + break; + } + } +} + +static void AddPepperBasedWidevine( + std::vector* concrete_key_systems) { +#if defined(WIDEVINE_CDM_MIN_GLIBC_VERSION) + Version glibc_version(gnu_get_libc_version()); + DCHECK(glibc_version.IsValid()); + if (glibc_version.IsOlderThan(WIDEVINE_CDM_MIN_GLIBC_VERSION)) + return; +#endif // defined(WIDEVINE_CDM_MIN_GLIBC_VERSION) + + std::vector additional_param_names; + std::vector additional_param_values; + if (!IsPepperCdmAvailable(kWidevineCdmPluginMimeType, + &additional_param_names, + &additional_param_values)) { + DVLOG(1) << "Widevine CDM is not currently available."; + return; + } + + std::vector codecs; + GetSupportedCodecsForPepperCdm(additional_param_names, + additional_param_values, + &codecs); + + SupportedCodecs supported_codecs = media::EME_CODEC_NONE; + + // Audio codecs are always supported. + // TODO(sandersd): Distinguish these from those that are directly supported, + // as those may offer a higher level of protection. + supported_codecs |= media::EME_CODEC_WEBM_OPUS; + supported_codecs |= media::EME_CODEC_WEBM_VORBIS; +#if defined(USE_PROPRIETARY_CODECS) + supported_codecs |= media::EME_CODEC_MP4_AAC; +#endif // defined(USE_PROPRIETARY_CODECS) + + for (size_t i = 0; i < codecs.size(); ++i) { + if (codecs[i] == kCdmSupportedCodecVp8) + supported_codecs |= media::EME_CODEC_WEBM_VP8; + if (codecs[i] == kCdmSupportedCodecVp9) + supported_codecs |= media::EME_CODEC_WEBM_VP9; +#if defined(USE_PROPRIETARY_CODECS) + if (codecs[i] == kCdmSupportedCodecAvc1) + supported_codecs |= media::EME_CODEC_MP4_AVC1; +#endif // defined(USE_PROPRIETARY_CODECS) + } + + cdm::AddWidevineWithCodecs( + cdm::WIDEVINE, supported_codecs, +#if defined(OS_CHROMEOS) + media::EmeRobustness::HW_SECURE_ALL, // Maximum audio robustness. + media::EmeRobustness::HW_SECURE_ALL, // Maximim video robustness. + media::EmeSessionTypeSupport:: + SUPPORTED_WITH_IDENTIFIER, // Persistent-license. + media::EmeSessionTypeSupport:: + NOT_SUPPORTED, // Persistent-release-message. + media::EmeFeatureSupport::REQUESTABLE, // Persistent state. + media::EmeFeatureSupport::REQUESTABLE, // Distinctive identifier. +#else // (Desktop) + media::EmeRobustness::SW_SECURE_CRYPTO, // Maximum audio robustness. + media::EmeRobustness::SW_SECURE_DECODE, // Maximum video robustness. + media::EmeSessionTypeSupport::NOT_SUPPORTED, // persistent-license. + media::EmeSessionTypeSupport:: + NOT_SUPPORTED, // persistent-release-message. + media::EmeFeatureSupport::REQUESTABLE, // Persistent state. + media::EmeFeatureSupport::NOT_SUPPORTED, // Distinctive identifier. +#endif // defined(OS_CHROMEOS) + concrete_key_systems); +} +#endif // defined(WIDEVINE_CDM_AVAILABLE) +#endif // defined(ENABLE_PEPPER_CDMS) + +void AddChromeKeySystems(std::vector* key_systems_info) { +#if defined(ENABLE_PEPPER_CDMS) +#if defined(WIDEVINE_CDM_AVAILABLE) + AddPepperBasedWidevine(key_systems_info); +#endif // defined(WIDEVINE_CDM_AVAILABLE) +#endif // defined(ENABLE_PEPPER_CDMS) +} diff --git a/chromium_src/chrome/renderer/media/chrome_key_systems.h b/chromium_src/chrome/renderer/media/chrome_key_systems.h new file mode 100644 index 000000000..dfec84f3b --- /dev/null +++ b/chromium_src/chrome/renderer/media/chrome_key_systems.h @@ -0,0 +1,14 @@ +// 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_RENDERER_MEDIA_CHROME_KEY_SYSTEMS_H_ +#define CHROME_RENDERER_MEDIA_CHROME_KEY_SYSTEMS_H_ + +#include + +#include "media/base/key_system_info.h" + +void AddChromeKeySystems(std::vector* key_systems_info); + +#endif // CHROME_RENDERER_MEDIA_CHROME_KEY_SYSTEMS_H_ diff --git a/chromium_src/chrome/renderer/pepper/pepper_flash_renderer_host.cc b/chromium_src/chrome/renderer/pepper/pepper_flash_renderer_host.cc index fe5e28ebb..66edd3f93 100644 --- a/chromium_src/chrome/renderer/pepper/pepper_flash_renderer_host.cc +++ b/chromium_src/chrome/renderer/pepper/pepper_flash_renderer_host.cc @@ -29,7 +29,6 @@ #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/core/SkPoint.h" -#include "third_party/skia/include/core/SkTemplates.h" #include "third_party/skia/include/core/SkTypeface.h" #include "ui/gfx/geometry/rect.h" #include "url/gurl.h" @@ -315,7 +314,7 @@ int32_t PepperFlashRendererHost::OnNavigate( bool rejected = false; while (header_iter.GetNext()) { std::string lower_case_header_name = - base::StringToLowerASCII(header_iter.name()); + base::ToLowerASCII(header_iter.name()); if (!IsSimpleHeader(lower_case_header_name, header_iter.values())) { rejected = true; diff --git a/chromium_src/chrome/renderer/printing/print_web_view_helper.cc b/chromium_src/chrome/renderer/printing/print_web_view_helper.cc index 20ac1fdc9..3bfe719a0 100644 --- a/chromium_src/chrome/renderer/printing/print_web_view_helper.cc +++ b/chromium_src/chrome/renderer/printing/print_web_view_helper.cc @@ -544,7 +544,6 @@ void PrepareFrameAndViewForPrint::CopySelection( // on the page). WebPreferences prefs = preferences; prefs.javascript_enabled = false; - prefs.java_enabled = false; blink::WebView* web_view = blink::WebView::create(this); owns_web_view_ = true; diff --git a/chromium_src/chrome/renderer/printing/print_web_view_helper_pdf_win.cc b/chromium_src/chrome/renderer/printing/print_web_view_helper_pdf_win.cc index dcd388fd1..0b21de469 100644 --- a/chromium_src/chrome/renderer/printing/print_web_view_helper_pdf_win.cc +++ b/chromium_src/chrome/renderer/printing/print_web_view_helper_pdf_win.cc @@ -135,7 +135,8 @@ bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame* frame, printed_page_params.page_size = page_size_in_dpi[i]; printed_page_params.content_area = content_area_in_dpi[i]; Send(new PrintHostMsg_DidPrintPage(routing_id(), printed_page_params)); - printed_page_params.metafile_data_handle = INVALID_HANDLE_VALUE; + // Send the rest of the pages with an invalid metafile handle. + printed_page_params.metafile_data_handle = base::SharedMemoryHandle(); } return true; } diff --git a/chromium_src/extensions/common/url_pattern.cc b/chromium_src/extensions/common/url_pattern.cc new file mode 100644 index 000000000..4303689fe --- /dev/null +++ b/chromium_src/extensions/common/url_pattern.cc @@ -0,0 +1,619 @@ +// 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 "extensions/common/url_pattern.h" + +#include + +#include "base/strings/pattern.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "content/public/common/url_constants.h" +#include "net/base/registry_controlled_domains/registry_controlled_domain.h" +#include "url/gurl.h" +#include "url/url_util.h" + +const char extensions::URLPattern::kAllUrlsPattern[] = ""; +const char kExtensionScheme[] = "chrome-extension"; + +namespace { + +// TODO(aa): What about more obscure schemes like data: and javascript: ? +// Note: keep this array in sync with kValidSchemeMasks. +const char* kValidSchemes[] = { + url::kHttpScheme, + url::kHttpsScheme, + url::kFileScheme, + url::kFtpScheme, + content::kChromeUIScheme, + kExtensionScheme, + url::kFileSystemScheme, +}; + +const int kValidSchemeMasks[] = { + extensions::URLPattern::SCHEME_HTTP, + extensions::URLPattern::SCHEME_HTTPS, + extensions::URLPattern::SCHEME_FILE, + extensions::URLPattern::SCHEME_FTP, + extensions::URLPattern::SCHEME_CHROMEUI, + extensions::URLPattern::SCHEME_EXTENSION, + extensions::URLPattern::SCHEME_FILESYSTEM, +}; + +static_assert(arraysize(kValidSchemes) == arraysize(kValidSchemeMasks), + "must keep these arrays in sync"); + +const char kParseSuccess[] = "Success."; +const char kParseErrorMissingSchemeSeparator[] = "Missing scheme separator."; +const char kParseErrorInvalidScheme[] = "Invalid scheme."; +const char kParseErrorWrongSchemeType[] = "Wrong scheme type."; +const char kParseErrorEmptyHost[] = "Host can not be empty."; +const char kParseErrorInvalidHostWildcard[] = "Invalid host wildcard."; +const char kParseErrorEmptyPath[] = "Empty path."; +const char kParseErrorInvalidPort[] = "Invalid port."; +const char kParseErrorInvalidHost[] = "Invalid host."; + +// Message explaining each URLPattern::ParseResult. +const char* const kParseResultMessages[] = { + kParseSuccess, + kParseErrorMissingSchemeSeparator, + kParseErrorInvalidScheme, + kParseErrorWrongSchemeType, + kParseErrorEmptyHost, + kParseErrorInvalidHostWildcard, + kParseErrorEmptyPath, + kParseErrorInvalidPort, + kParseErrorInvalidHost, +}; + +static_assert(extensions::URLPattern::NUM_PARSE_RESULTS == arraysize(kParseResultMessages), + "must add message for each parse result"); + +const char kPathSeparator[] = "/"; + +bool IsStandardScheme(const std::string& scheme) { + // "*" gets the same treatment as a standard scheme. + if (scheme == "*") + return true; + + return url::IsStandard(scheme.c_str(), + url::Component(0, static_cast(scheme.length()))); +} + +bool IsValidPortForScheme(const std::string& scheme, const std::string& port) { + if (port == "*") + return true; + + // Only accept non-wildcard ports if the scheme uses ports. + if (url::DefaultPortForScheme(scheme.c_str(), scheme.length()) == + url::PORT_UNSPECIFIED) { + return false; + } + + int parsed_port = url::PORT_UNSPECIFIED; + if (!base::StringToInt(port, &parsed_port)) + return false; + return (parsed_port >= 0) && (parsed_port < 65536); +} + +// Returns |path| with the trailing wildcard stripped if one existed. +// +// The functions that rely on this (OverlapsWith and Contains) are only +// called for the patterns inside URLPatternSet. In those cases, we know that +// the path will have only a single wildcard at the end. This makes figuring +// out overlap much easier. It seems like there is probably a computer-sciency +// way to solve the general case, but we don't need that yet. +std::string StripTrailingWildcard(const std::string& path) { + size_t wildcard_index = path.find('*'); + size_t path_last = path.size() - 1; + return wildcard_index == path_last ? path.substr(0, path_last) : path; +} + +} // namespace + +namespace extensions { +// static +bool URLPattern::IsValidSchemeForExtensions(const std::string& scheme) { + for (size_t i = 0; i < arraysize(kValidSchemes); ++i) { + if (scheme == kValidSchemes[i]) + return true; + } + return false; +} + +URLPattern::URLPattern() + : valid_schemes_(SCHEME_ALL), + match_all_urls_(false), + match_subdomains_(false), + port_("*") {} + +URLPattern::URLPattern(int valid_schemes) + : valid_schemes_(valid_schemes), + match_all_urls_(false), + match_subdomains_(false), + port_("*") {} + +URLPattern::URLPattern(int valid_schemes, const std::string& pattern) + // Strict error checking is used, because this constructor is only + // appropriate when we know |pattern| is valid. + : valid_schemes_(valid_schemes), + match_all_urls_(false), + match_subdomains_(false), + port_("*") { + ParseResult result = Parse(pattern); + if (PARSE_SUCCESS != result) + NOTREACHED() << "URLPattern invalid: " << pattern << " result " << result; +} + +URLPattern::~URLPattern() { +} + +bool URLPattern::operator<(const URLPattern& other) const { + return GetAsString() < other.GetAsString(); +} + +bool URLPattern::operator>(const URLPattern& other) const { + return GetAsString() > other.GetAsString(); +} + +bool URLPattern::operator==(const URLPattern& other) const { + return GetAsString() == other.GetAsString(); +} + +std::ostream& operator<<(std::ostream& out, const URLPattern& url_pattern) { + return out << '"' << url_pattern.GetAsString() << '"'; +} + +URLPattern::ParseResult URLPattern::Parse(const std::string& pattern) { + spec_.clear(); + SetMatchAllURLs(false); + SetMatchSubdomains(false); + SetPort("*"); + + // Special case pattern to match every valid URL. + if (pattern == kAllUrlsPattern) { + SetMatchAllURLs(true); + return PARSE_SUCCESS; + } + + // Parse out the scheme. + size_t scheme_end_pos = pattern.find(url::kStandardSchemeSeparator); + bool has_standard_scheme_separator = true; + + // Some urls also use ':' alone as the scheme separator. + if (scheme_end_pos == std::string::npos) { + scheme_end_pos = pattern.find(':'); + has_standard_scheme_separator = false; + } + + if (scheme_end_pos == std::string::npos) + return PARSE_ERROR_MISSING_SCHEME_SEPARATOR; + + if (!SetScheme(pattern.substr(0, scheme_end_pos))) + return PARSE_ERROR_INVALID_SCHEME; + + bool standard_scheme = IsStandardScheme(scheme_); + if (standard_scheme != has_standard_scheme_separator) + return PARSE_ERROR_WRONG_SCHEME_SEPARATOR; + + // Advance past the scheme separator. + scheme_end_pos += + (standard_scheme ? strlen(url::kStandardSchemeSeparator) : 1); + if (scheme_end_pos >= pattern.size()) + return PARSE_ERROR_EMPTY_HOST; + + // Parse out the host and path. + size_t host_start_pos = scheme_end_pos; + size_t path_start_pos = 0; + + if (!standard_scheme) { + path_start_pos = host_start_pos; + } else if (scheme_ == url::kFileScheme) { + size_t host_end_pos = pattern.find(kPathSeparator, host_start_pos); + if (host_end_pos == std::string::npos) { + // Allow hostname omission. + // e.g. file://* is interpreted as file:///*, + // file://foo* is interpreted as file:///foo*. + path_start_pos = host_start_pos - 1; + } else { + // Ignore hostname if scheme is file://. + // e.g. file://localhost/foo is equal to file:///foo. + path_start_pos = host_end_pos; + } + } else { + size_t host_end_pos = pattern.find(kPathSeparator, host_start_pos); + + // Host is required. + if (host_start_pos == host_end_pos) + return PARSE_ERROR_EMPTY_HOST; + + if (host_end_pos == std::string::npos) + return PARSE_ERROR_EMPTY_PATH; + + host_ = pattern.substr(host_start_pos, host_end_pos - host_start_pos); + + // The first component can optionally be '*' to match all subdomains. + std::vector host_components = base::SplitString( + host_, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + + // Could be empty if the host only consists of whitespace characters. + if (host_components.empty() || + (host_components.size() == 1 && host_components[0].empty())) + return PARSE_ERROR_EMPTY_HOST; + + if (host_components[0] == "*") { + match_subdomains_ = true; + host_components.erase(host_components.begin(), + host_components.begin() + 1); + } + host_ = base::JoinString(host_components, "."); + + path_start_pos = host_end_pos; + } + + SetPath(pattern.substr(path_start_pos)); + + size_t port_pos = host_.find(':'); + if (port_pos != std::string::npos) { + if (!SetPort(host_.substr(port_pos + 1))) + return PARSE_ERROR_INVALID_PORT; + host_ = host_.substr(0, port_pos); + } + + // No other '*' can occur in the host, though. This isn't necessary, but is + // done as a convenience to developers who might otherwise be confused and + // think '*' works as a glob in the host. + if (host_.find('*') != std::string::npos) + return PARSE_ERROR_INVALID_HOST_WILDCARD; + + // Null characters are not allowed in hosts. + if (host_.find('\0') != std::string::npos) + return PARSE_ERROR_INVALID_HOST; + + return PARSE_SUCCESS; +} + +void URLPattern::SetValidSchemes(int valid_schemes) { + spec_.clear(); + valid_schemes_ = valid_schemes; +} + +void URLPattern::SetHost(const std::string& host) { + spec_.clear(); + host_ = host; +} + +void URLPattern::SetMatchAllURLs(bool val) { + spec_.clear(); + match_all_urls_ = val; + + if (val) { + match_subdomains_ = true; + scheme_ = "*"; + host_.clear(); + SetPath("/*"); + } +} + +void URLPattern::SetMatchSubdomains(bool val) { + spec_.clear(); + match_subdomains_ = val; +} + +bool URLPattern::SetScheme(const std::string& scheme) { + spec_.clear(); + scheme_ = scheme; + if (scheme_ == "*") { + valid_schemes_ &= (SCHEME_HTTP | SCHEME_HTTPS); + } else if (!IsValidScheme(scheme_)) { + return false; + } + return true; +} + +bool URLPattern::IsValidScheme(const std::string& scheme) const { + if (valid_schemes_ == SCHEME_ALL) + return true; + + for (size_t i = 0; i < arraysize(kValidSchemes); ++i) { + if (scheme == kValidSchemes[i] && (valid_schemes_ & kValidSchemeMasks[i])) + return true; + } + + return false; +} + +void URLPattern::SetPath(const std::string& path) { + spec_.clear(); + path_ = path; + path_escaped_ = path_; + base::ReplaceSubstringsAfterOffset(&path_escaped_, 0, "\\", "\\\\"); + base::ReplaceSubstringsAfterOffset(&path_escaped_, 0, "?", "\\?"); +} + +bool URLPattern::SetPort(const std::string& port) { + spec_.clear(); + if (IsValidPortForScheme(scheme_, port)) { + port_ = port; + return true; + } + return false; +} + +bool URLPattern::MatchesURL(const GURL& test) const { + const GURL* test_url = &test; + bool has_inner_url = test.inner_url() != NULL; + + if (has_inner_url) { + if (!test.SchemeIsFileSystem()) + return false; // The only nested URLs we handle are filesystem URLs. + test_url = test.inner_url(); + } + + if (!MatchesScheme(test_url->scheme())) + return false; + + if (match_all_urls_) + return true; + + std::string path_for_request = test.PathForRequest(); + if (has_inner_url) + path_for_request = test_url->path() + path_for_request; + + return MatchesSecurityOriginHelper(*test_url) && + MatchesPath(path_for_request); +} + +bool URLPattern::MatchesSecurityOrigin(const GURL& test) const { + const GURL* test_url = &test; + bool has_inner_url = test.inner_url() != NULL; + + if (has_inner_url) { + if (!test.SchemeIsFileSystem()) + return false; // The only nested URLs we handle are filesystem URLs. + test_url = test.inner_url(); + } + + if (!MatchesScheme(test_url->scheme())) + return false; + + if (match_all_urls_) + return true; + + return MatchesSecurityOriginHelper(*test_url); +} + +bool URLPattern::MatchesScheme(const std::string& test) const { + if (!IsValidScheme(test)) + return false; + + return scheme_ == "*" || test == scheme_; +} + +bool URLPattern::MatchesHost(const std::string& host) const { + std::string test(url::kHttpScheme); + test += url::kStandardSchemeSeparator; + test += host; + test += "/"; + return MatchesHost(GURL(test)); +} + +bool URLPattern::MatchesHost(const GURL& test) const { + // If the hosts are exactly equal, we have a match. + if (test.host() == host_) + return true; + + // If we're matching subdomains, and we have no host in the match pattern, + // that means that we're matching all hosts, which means we have a match no + // matter what the test host is. + if (match_subdomains_ && host_.empty()) + return true; + + // Otherwise, we can only match if our match pattern matches subdomains. + if (!match_subdomains_) + return false; + + // We don't do subdomain matching against IP addresses, so we can give up now + // if the test host is an IP address. + if (test.HostIsIPAddress()) + return false; + + // Check if the test host is a subdomain of our host. + if (test.host().length() <= (host_.length() + 1)) + return false; + + if (test.host().compare(test.host().length() - host_.length(), + host_.length(), host_) != 0) + return false; + + return test.host()[test.host().length() - host_.length() - 1] == '.'; +} + +bool URLPattern::ImpliesAllHosts() const { + // Check if it matches all urls or is a pattern like http://*/*. + if (match_all_urls_ || + (match_subdomains_ && host_.empty() && port_ == "*" && path_ == "/*")) { + return true; + } + + // If this doesn't even match subdomains, it can't possibly imply all hosts. + if (!match_subdomains_) + return false; + + // If |host_| is a recognized TLD, this will be 0. We don't include private + // TLDs, so that, e.g., *.appspot.com does not imply all hosts. + size_t registry_length = net::registry_controlled_domains::GetRegistryLength( + host_, + net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES, + net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); + // If there was more than just a TLD in the host (e.g., *.foobar.com), it + // doesn't imply all hosts. + if (registry_length > 0) + return false; + + // At this point the host could either be just a TLD ("com") or some unknown + // TLD-like string ("notatld"). To disambiguate between them construct a + // fake URL, and check the registry. This returns 0 if the TLD is + // unrecognized, or the length of the recognized TLD. + registry_length = net::registry_controlled_domains::GetRegistryLength( + base::StringPrintf("foo.%s", host_.c_str()), + net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES, + net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); + // If we recognized this TLD, then this is a pattern like *.com, and it + // should imply all hosts. Otherwise, this doesn't imply all hosts. + return registry_length > 0; +} + +bool URLPattern::MatchesSingleOrigin() const { + // Strictly speaking, the port is part of the origin, but in URLPattern it + // defaults to *. It's not very interesting anyway, so leave it out. + return !ImpliesAllHosts() && scheme_ != "*" && !match_subdomains_; +} + +bool URLPattern::MatchesPath(const std::string& test) const { + // Make the behaviour of OverlapsWith consistent with MatchesURL, which is + // need to match hosted apps on e.g. 'google.com' also run on 'google.com/'. + if (test + "/*" == path_escaped_) + return true; + + return base::MatchPattern(test, path_escaped_); +} + +const std::string& URLPattern::GetAsString() const { + if (!spec_.empty()) + return spec_; + + if (match_all_urls_) { + spec_ = kAllUrlsPattern; + return spec_; + } + + bool standard_scheme = IsStandardScheme(scheme_); + + std::string spec = scheme_ + + (standard_scheme ? url::kStandardSchemeSeparator : ":"); + + if (scheme_ != url::kFileScheme && standard_scheme) { + if (match_subdomains_) { + spec += "*"; + if (!host_.empty()) + spec += "."; + } + + if (!host_.empty()) + spec += host_; + + if (port_ != "*") { + spec += ":"; + spec += port_; + } + } + + if (!path_.empty()) + spec += path_; + + spec_ = spec; + return spec_; +} + +bool URLPattern::OverlapsWith(const URLPattern& other) const { + if (match_all_urls() || other.match_all_urls()) + return true; + return (MatchesAnyScheme(other.GetExplicitSchemes()) || + other.MatchesAnyScheme(GetExplicitSchemes())) + && (MatchesHost(other.host()) || other.MatchesHost(host())) + && (MatchesPortPattern(other.port()) || other.MatchesPortPattern(port())) + && (MatchesPath(StripTrailingWildcard(other.path())) || + other.MatchesPath(StripTrailingWildcard(path()))); +} + +bool URLPattern::Contains(const URLPattern& other) const { + if (match_all_urls()) + return true; + return MatchesAllSchemes(other.GetExplicitSchemes()) && + MatchesHost(other.host()) && + (!other.match_subdomains_ || match_subdomains_) && + MatchesPortPattern(other.port()) && + MatchesPath(StripTrailingWildcard(other.path())); +} + +bool URLPattern::MatchesAnyScheme( + const std::vector& schemes) const { + for (std::vector::const_iterator i = schemes.begin(); + i != schemes.end(); ++i) { + if (MatchesScheme(*i)) + return true; + } + + return false; +} + +bool URLPattern::MatchesAllSchemes( + const std::vector& schemes) const { + for (std::vector::const_iterator i = schemes.begin(); + i != schemes.end(); ++i) { + if (!MatchesScheme(*i)) + return false; + } + + return true; +} + +bool URLPattern::MatchesSecurityOriginHelper(const GURL& test) const { + // Ignore hostname if scheme is file://. + if (scheme_ != url::kFileScheme && !MatchesHost(test)) + return false; + + if (!MatchesPortPattern(base::IntToString(test.EffectiveIntPort()))) + return false; + + return true; +} + +bool URLPattern::MatchesPortPattern(const std::string& port) const { + return port_ == "*" || port_ == port; +} + +std::vector URLPattern::GetExplicitSchemes() const { + std::vector result; + + if (scheme_ != "*" && !match_all_urls_ && IsValidScheme(scheme_)) { + result.push_back(scheme_); + return result; + } + + for (size_t i = 0; i < arraysize(kValidSchemes); ++i) { + if (MatchesScheme(kValidSchemes[i])) { + result.push_back(kValidSchemes[i]); + } + } + + return result; +} + +std::vector URLPattern::ConvertToExplicitSchemes() const { + std::vector explicit_schemes = GetExplicitSchemes(); + std::vector result; + + for (std::vector::const_iterator i = explicit_schemes.begin(); + i != explicit_schemes.end(); ++i) { + URLPattern temp = *this; + temp.SetScheme(*i); + temp.SetMatchAllURLs(false); + result.push_back(temp); + } + + return result; +} + +// static +const char* URLPattern::GetParseResultString( + URLPattern::ParseResult parse_result) { + return kParseResultMessages[parse_result]; +} + +} // namespace extensions diff --git a/chromium_src/extensions/common/url_pattern.h b/chromium_src/extensions/common/url_pattern.h new file mode 100644 index 000000000..fa9b6495e --- /dev/null +++ b/chromium_src/extensions/common/url_pattern.h @@ -0,0 +1,264 @@ +// 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 EXTENSIONS_COMMON_URL_PATTERN_H_ +#define EXTENSIONS_COMMON_URL_PATTERN_H_ + +#include +#include +#include +#include + +class GURL; + +namespace extensions { +// A pattern that can be used to match URLs. A URLPattern is a very restricted +// subset of URL syntax: +// +// := :// | '' +// := '*' | 'http' | 'https' | 'file' | 'ftp' | 'chrome' | +// 'chrome-extension' | 'filesystem' +// := '*' | '*.' + +// := [':' ('*' | )] +// := '/' +// +// * Host is not used when the scheme is 'file'. +// * The path can have embedded '*' characters which act as glob wildcards. +// * '' is a special pattern that matches any URL that contains a +// valid scheme (as specified by valid_schemes_). +// * The '*' scheme pattern excludes file URLs. +// +// Examples of valid patterns: +// - http://*/* +// - http://*/foo* +// - https://*.google.com/foo*bar +// - file://monkey* +// - http://127.0.0.1/* +// +// Examples of invalid patterns: +// - http://* -- path not specified +// - http://*foo/bar -- * not allowed as substring of host component +// - http://foo.*.bar/baz -- * must be first component +// - http:/bar -- scheme separator not found +// - foo://* -- invalid scheme +// - chrome:// -- we don't support chrome internal URLs +class URLPattern { + public: + // A collection of scheme bitmasks for use with valid_schemes. + enum SchemeMasks { + SCHEME_NONE = 0, + SCHEME_HTTP = 1 << 0, + SCHEME_HTTPS = 1 << 1, + SCHEME_FILE = 1 << 2, + SCHEME_FTP = 1 << 3, + SCHEME_CHROMEUI = 1 << 4, + SCHEME_EXTENSION = 1 << 5, + SCHEME_FILESYSTEM = 1 << 6, + + // IMPORTANT! + // SCHEME_ALL will match every scheme, including chrome://, chrome- + // extension://, about:, etc. Because this has lots of security + // implications, third-party extensions should usually not be able to get + // access to URL patterns initialized this way. If there is a reason + // for violating this general rule, document why this it safe. + SCHEME_ALL = -1, + }; + + // Error codes returned from Parse(). + enum ParseResult { + PARSE_SUCCESS = 0, + PARSE_ERROR_MISSING_SCHEME_SEPARATOR, + PARSE_ERROR_INVALID_SCHEME, + PARSE_ERROR_WRONG_SCHEME_SEPARATOR, + PARSE_ERROR_EMPTY_HOST, + PARSE_ERROR_INVALID_HOST_WILDCARD, + PARSE_ERROR_EMPTY_PATH, + PARSE_ERROR_INVALID_PORT, + PARSE_ERROR_INVALID_HOST, + NUM_PARSE_RESULTS + }; + + // The string pattern. + static const char kAllUrlsPattern[]; + + // Returns true if the given |scheme| is considered valid for extensions. + static bool IsValidSchemeForExtensions(const std::string& scheme); + + explicit URLPattern(int valid_schemes); + + // Convenience to construct a URLPattern from a string. If the string is not + // known ahead of time, use Parse() instead, which returns success or failure. + URLPattern(int valid_schemes, const std::string& pattern); + + URLPattern(); + ~URLPattern(); + + bool operator<(const URLPattern& other) const; + bool operator>(const URLPattern& other) const; + bool operator==(const URLPattern& other) const; + + // Initializes this instance by parsing the provided string. Returns + // URLPattern::PARSE_SUCCESS on success, or an error code otherwise. On + // failure, this instance will have some intermediate values and is in an + // invalid state. + ParseResult Parse(const std::string& pattern_str); + + // Gets the bitmask of valid schemes. + int valid_schemes() const { return valid_schemes_; } + void SetValidSchemes(int valid_schemes); + + // Gets the host the pattern matches. This can be an empty string if the + // pattern matches all hosts (the input was ://*/). + const std::string& host() const { return host_; } + void SetHost(const std::string& host); + + // Gets whether to match subdomains of host(). + bool match_subdomains() const { return match_subdomains_; } + void SetMatchSubdomains(bool val); + + // Gets the path the pattern matches with the leading slash. This can have + // embedded asterisks which are interpreted using glob rules. + const std::string& path() const { return path_; } + void SetPath(const std::string& path); + + // Returns true if this pattern matches all urls. + bool match_all_urls() const { return match_all_urls_; } + void SetMatchAllURLs(bool val); + + // Sets the scheme for pattern matches. This can be a single '*' if the + // pattern matches all valid schemes (as defined by the valid_schemes_ + // property). Returns false on failure (if the scheme is not valid). + bool SetScheme(const std::string& scheme); + // Note: You should use MatchesScheme() instead of this getter unless you + // absolutely need the exact scheme. This is exposed for testing. + const std::string& scheme() const { return scheme_; } + + // Returns true if the specified scheme can be used in this URL pattern, and + // false otherwise. Uses valid_schemes_ to determine validity. + bool IsValidScheme(const std::string& scheme) const; + + // Returns true if this instance matches the specified URL. + bool MatchesURL(const GURL& test) const; + + // Returns true if this instance matches the specified security origin. + bool MatchesSecurityOrigin(const GURL& test) const; + + // Returns true if |test| matches our scheme. + // Note that if test is "filesystem", this may fail whereas MatchesURL + // may succeed. MatchesURL is smart enough to look at the inner_url instead + // of the outer "filesystem:" part. + bool MatchesScheme(const std::string& test) const; + + // Returns true if |test| matches our host. + bool MatchesHost(const std::string& test) const; + bool MatchesHost(const GURL& test) const; + + // Returns true if |test| matches our path. + bool MatchesPath(const std::string& test) const; + + // Returns true if the pattern is vague enough that it implies all hosts, + // such as *://*/*. + // This is an expensive method, and should be used sparingly! + // You should probably use URLPatternSet::ShouldWarnAllHosts(), which is + // cached. + bool ImpliesAllHosts() const; + + // Returns true if the pattern only matches a single origin. The pattern may + // include a path. + bool MatchesSingleOrigin() const; + + // Sets the port. Returns false if the port is invalid. + bool SetPort(const std::string& port); + const std::string& port() const { return port_; } + + // Returns a string representing this instance. + const std::string& GetAsString() const; + + // Determines whether there is a URL that would match this instance and + // another instance. This method is symmetrical: Calling + // other.OverlapsWith(this) would result in the same answer. + bool OverlapsWith(const URLPattern& other) const; + + // Returns true if this pattern matches all possible URLs that |other| can + // match. For example, http://*.google.com encompasses http://www.google.com. + bool Contains(const URLPattern& other) const; + + // Converts this URLPattern into an equivalent set of URLPatterns that don't + // use a wildcard in the scheme component. If this URLPattern doesn't use a + // wildcard scheme, then the returned set will contain one element that is + // equivalent to this instance. + std::vector ConvertToExplicitSchemes() const; + + static bool EffectiveHostCompare(const URLPattern& a, const URLPattern& b) { + if (a.match_all_urls_ && b.match_all_urls_) + return false; + return a.host_.compare(b.host_) < 0; + } + + // Used for origin comparisons in a std::set. + class EffectiveHostCompareFunctor { + public: + bool operator()(const URLPattern& a, const URLPattern& b) const { + return EffectiveHostCompare(a, b); + } + }; + + // Get an error string for a ParseResult. + static const char* GetParseResultString(URLPattern::ParseResult parse_result); + + private: + // Returns true if any of the |schemes| items matches our scheme. + bool MatchesAnyScheme(const std::vector& schemes) const; + + // Returns true if all of the |schemes| items matches our scheme. + bool MatchesAllSchemes(const std::vector& schemes) const; + + bool MatchesSecurityOriginHelper(const GURL& test) const; + + // Returns true if our port matches the |port| pattern (it may be "*"). + bool MatchesPortPattern(const std::string& port) const; + + // If the URLPattern contains a wildcard scheme, returns a list of + // equivalent literal schemes, otherwise returns the current scheme. + std::vector GetExplicitSchemes() const; + + // A bitmask containing the schemes which are considered valid for this + // pattern. Parse() uses this to decide whether a pattern contains a valid + // scheme. + int valid_schemes_; + + // True if this is a special-case "" pattern. + bool match_all_urls_; + + // The scheme for the pattern. + std::string scheme_; + + // The host without any leading "*" components. + std::string host_; + + // Whether we should match subdomains of the host. This is true if the first + // component of the pattern's host was "*". + bool match_subdomains_; + + // The port. + std::string port_; + + // The path to match. This is everything after the host of the URL, or + // everything after the scheme in the case of file:// URLs. + std::string path_; + + // The path with "?" and "\" characters escaped for use with the + // MatchPattern() function. + std::string path_escaped_; + + // A string representing this URLPattern. + mutable std::string spec_; +}; + +std::ostream& operator<<(std::ostream& out, const URLPattern& url_pattern); + +typedef std::vector URLPatternList; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_URL_PATTERN_H_ diff --git a/chromium_src/library_loaders/libspeechd.h b/chromium_src/library_loaders/libspeechd.h index 0d62f2c5d..f7b276287 100644 --- a/chromium_src/library_loaders/libspeechd.h +++ b/chromium_src/library_loaders/libspeechd.h @@ -33,6 +33,7 @@ class LibSpeechdLoader { decltype(&::spd_set_synthesis_voice) spd_set_synthesis_voice; decltype(&::spd_list_modules) spd_list_modules; decltype(&::spd_set_output_module) spd_set_output_module; + decltype(&::spd_set_language) spd_set_language; private: diff --git a/chromium_src/library_loaders/libspeechd_loader.cc b/chromium_src/library_loaders/libspeechd_loader.cc index 606661000..f09ea3ae8 100644 --- a/chromium_src/library_loaders/libspeechd_loader.cc +++ b/chromium_src/library_loaders/libspeechd_loader.cc @@ -201,6 +201,19 @@ bool LibSpeechdLoader::Load(const std::string& library_name) { return false; } +#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) + spd_set_language = + reinterpret_castspd_set_language)>( + dlsym(library_, "spd_set_language")); +#endif +#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED) + spd_set_language = &::spd_set_language; +#endif + if (!spd_set_language) { + CleanUp(true); + return false; + } + loaded_ = true; return true; @@ -227,5 +240,6 @@ void LibSpeechdLoader::CleanUp(bool unload) { spd_set_synthesis_voice = NULL; spd_list_modules = NULL; spd_set_output_module = NULL; + spd_set_language = NULL; } diff --git a/chromium_src/net/test/embedded_test_server/stream_listen_socket.cc b/chromium_src/net/test/embedded_test_server/stream_listen_socket.cc index 1056983a8..897b23bbd 100644 --- a/chromium_src/net/test/embedded_test_server/stream_listen_socket.cc +++ b/chromium_src/net/test/embedded_test_server/stream_listen_socket.cc @@ -228,7 +228,7 @@ void StreamListenSocket::CloseSocket() { void StreamListenSocket::WatchSocket(WaitState state) { #if defined(OS_WIN) WSAEventSelect(socket_, socket_event_, FD_ACCEPT | FD_CLOSE | FD_READ); - watcher_.StartWatching(socket_event_, this); + watcher_.StartWatchingOnce(socket_event_, this); #elif defined(OS_POSIX) // Implicitly calls StartWatchingFileDescriptor(). base::MessageLoopForIO::current()->WatchFileDescriptor( @@ -264,7 +264,7 @@ void StreamListenSocket::OnObjectSignaled(HANDLE object) { return; } // The object was reset by WSAEnumNetworkEvents. Watch for the next signal. - watcher_.StartWatching(object, this); + watcher_.StartWatchingOnce(object, this); if (ev.lNetworkEvents == 0) { // Occasionally the event is set even though there is no new data. diff --git a/docs-translations/es/api/synopsis.md b/docs-translations/es/api/synopsis.md index eb4fcb39f..534fafcf2 100644 --- a/docs-translations/es/api/synopsis.md +++ b/docs-translations/es/api/synopsis.md @@ -25,7 +25,7 @@ var window = null; app.on('ready', function() { window = new BrowserWindow({width: 800, height: 600}); - window.loadUrl('https://github.com'); + window.loadURL('https://github.com'); }); ``` diff --git a/docs-translations/es/tutorial/application-packaging.md b/docs-translations/es/tutorial/application-packaging.md index 56698c1aa..c0cca7ecd 100644 --- a/docs-translations/es/tutorial/application-packaging.md +++ b/docs-translations/es/tutorial/application-packaging.md @@ -70,7 +70,7 @@ También puedes mostrar una página web contenida en un `asar` utilizando `Brows ```javascript var BrowserWindow = require('browser-window'); var win = new BrowserWindow({width: 800, height: 600}); -win.loadUrl('file:///path/to/example.asar/static/index.html'); +win.loadURL('file:///path/to/example.asar/static/index.html'); ``` ### API Web diff --git a/docs-translations/es/tutorial/online-offline-events.md b/docs-translations/es/tutorial/online-offline-events.md index 0e43f9b16..450702e2f 100644 --- a/docs-translations/es/tutorial/online-offline-events.md +++ b/docs-translations/es/tutorial/online-offline-events.md @@ -12,7 +12,7 @@ var onlineStatusWindow; app.on('ready', function() { onlineStatusWindow = new BrowserWindow({ width: 0, height: 0, show: false }); - onlineStatusWindow.loadUrl('file://' + __dirname + '/online-status.html'); + onlineStatusWindow.loadURL('file://' + __dirname + '/online-status.html'); }); ``` @@ -50,7 +50,7 @@ var onlineStatusWindow; app.on('ready', function() { onlineStatusWindow = new BrowserWindow({ width: 0, height: 0, show: false }); - onlineStatusWindow.loadUrl('file://' + __dirname + '/online-status.html'); + onlineStatusWindow.loadURL('file://' + __dirname + '/online-status.html'); }); ipc.on('online-status-changed', function(event, status) { diff --git a/docs-translations/es/tutorial/quick-start.md b/docs-translations/es/tutorial/quick-start.md index ee1127eb0..b038f7cb0 100644 --- a/docs-translations/es/tutorial/quick-start.md +++ b/docs-translations/es/tutorial/quick-start.md @@ -70,9 +70,6 @@ El `main.js` debería crear las ventanas y gestionar los eventos del sistema, un var app = require('app'); // Módulo para controlar el ciclo de vida de la aplicación. var BrowserWindow = require('browser-window'); // Módulo para crear uan ventana de navegador. -// Reportar crashes a nuestro servidor. -require('crash-reporter').start(); - // Mantener una referencia global al objeto window, si no lo haces, esta ventana // se cerrará automáticamente cuando el objeto JavaScript sea recolectado (garbage collected): var mainWindow = null; diff --git a/docs-translations/es/tutorial/using-pepper-flash-plugin.md b/docs-translations/es/tutorial/using-pepper-flash-plugin.md index 53d2d024c..5c41ff878 100644 --- a/docs-translations/es/tutorial/using-pepper-flash-plugin.md +++ b/docs-translations/es/tutorial/using-pepper-flash-plugin.md @@ -15,9 +15,6 @@ También puedes agregar la opción `plugins` de `browser-window`. Por ejemplo, var app = require('app'); var BrowserWindow = require('browser-window'); -// Report crashes to our server. -require('crash-reporter').start(); - // Keep a global reference of the window object, if you don't, the window will // be closed automatically when the javascript object is GCed. var mainWindow = null; @@ -46,7 +43,7 @@ app.on('ready', function() { 'plugins': true } }); - mainWindow.loadUrl('file://' + __dirname + '/index.html'); + mainWindow.loadURL('file://' + __dirname + '/index.html'); // Something else }); ``` diff --git a/docs-translations/jp/tutorial/quick-start.md b/docs-translations/jp/tutorial/quick-start.md index 9a929ff84..4bd816cb6 100644 --- a/docs-translations/jp/tutorial/quick-start.md +++ b/docs-translations/jp/tutorial/quick-start.md @@ -53,9 +53,6 @@ your-app/ var app = require('app'); // Module to control application life. var BrowserWindow = require('browser-window'); // Module to create native browser window. -// Report crashes to our server. -require('crash-reporter').start(); - // Keep a global reference of the window object, if you don't, the window will // be closed automatically when the javascript object is GCed. var mainWindow = null; diff --git a/docs-translations/ko-KR/README.md b/docs-translations/ko-KR/README.md index d70894f8a..473f52113 100644 --- a/docs-translations/ko-KR/README.md +++ b/docs-translations/ko-KR/README.md @@ -1,4 +1,17 @@ -## 개발 가이드 +반드시 사용하는 Electron 버전과 문서 버전을 일치시켜야 합니다. 버전 숫자는 문서 페이지 +URL에 포함되어 있습니다. 만약 그렇지 않다면, 아마 현재 보고 있는 문서는 개발 중인 +브랜치의 문서를 보고 있을 가능성이 있으며 해당 문서는 추후 API의 변경 가능성이 있고 +현재 사용하고 있는 Electron의 버전과 호환되지 않을 수 있습니다. 이 경우 atom.io의 +[사용할 수 있는 버전](http://electron.atom.io/docs/) 목록에서 다른 버전으로 변경할 +수 있습니다. 또한 GitHub 인터페이스의 "Switch branches/tags" 드롭다운 메뉴에서도 +사용 중인 Electron 버전으로 변경할 수 있습니다. + +**역주:** 한국어 번역 문서는 atom.io에 반영이 되어있지 않습니다. 따라서 번역 문서는 +GitHub 프로젝트내에서만 볼 수 있고 `master` 브랜치의 문서는 현재 개발중인 프로젝트의 +문서입니다. 한국어 번역 문서는 현재 `upstream` 원본 문서의 변경에 따라 최대한 문서의 +버전을 맞추려고 노력하고 있지만 가끔 누락된 번역이 존재할 수 있습니다. + +## 개발 가이드 * [지원하는 플랫폼](tutorial/supported-platforms.md) * [어플리케이션 배포](tutorial/application-distribution.md) @@ -9,6 +22,7 @@ * [Selenium 과 WebDriver 사용하기](tutorial/using-selenium-and-webdriver.md) * [개발자 도구 확장 기능](tutorial/devtools-extension.md) * [Pepper 플래시 플러그인 사용하기](tutorial/using-pepper-flash-plugin.md) +* [Widevine CDM 플러그인 사용하기](tutorial/using-widevine-cdm-plugin.md) ## 튜토리얼 @@ -21,6 +35,7 @@ * [개요](api/synopsis.md) * [Process 객체](api/process.md) * [크롬 명령줄 스위치 지원](api/chrome-command-line-switches.md) +* [환경 변수](api/environment-variables.md) ### 커스텀 DOM elements: @@ -48,6 +63,7 @@ ### 랜더러 프로세스에서 사용할 수 있는 모듈 (웹 페이지): +* [desktopCapturer](api/desktop-capturer.md) * [ipcRenderer](api/ipc-renderer.md) * [remote](api/remote.md) * [webFrame](api/web-frame.md) diff --git a/docs-translations/ko-KR/api/app.md b/docs-translations/ko-KR/api/app.md index 6dbec14ba..503864d04 100644 --- a/docs-translations/ko-KR/api/app.md +++ b/docs-translations/ko-KR/api/app.md @@ -68,9 +68,14 @@ Returns: ### Event: 'quit' +Returns: + +* `event` Event +* `exitCode` Integer + 어플리케이션이 종료될 때 발생하는 이벤트입니다. -### Event: 'open-file' +### Event: 'open-file' _OS X_ Returns: @@ -87,7 +92,9 @@ Returns: 이 이벤트를 처리할 땐 반드시 `event.preventDefault()`를 호출해야 합니다. -### Event: 'open-url' +Windows에선 `process.argv`를 통해 파일 경로를 얻을 수 있습니다. + +### Event: 'open-url' _OS X_ Returns: @@ -128,7 +135,7 @@ Returns: [browserWindow](browser-window.md)에 대한 포커스가 발생했을 때 발생하는 이벤트 입니다. -**역주:** _포커스_는 창을 클릭해서 활성화 시켰을 때를 말합니다. +**역주:** _포커스_ 는 창을 클릭해서 활성화 시켰을 때를 말합니다. ### Event: 'browser-window-created' @@ -139,6 +146,35 @@ Returns: 새로운 [browserWindow](browser-window.md)가 생성되었을 때 발생하는 이벤트 입니다. +### Event: 'certificate-error' + +Returns: + +* `event` Event +* `webContents` [WebContents](web-contents.md) +* `url` URL +* `error` String - 에러 코드 +* `certificate` Object + * `data` Buffer - PEM 인코딩된 데이터 + * `issuerName` String +* `callback` Function + +`url`에 대한 `certificate` 인증서의 유효성 검증에 실패했을 때 발생하는 이벤트입니다. +인증서를 신뢰한다면 `event.preventDefault()` 와 `callback(true)`를 호출하여 +기본 동작을 방지하고 인증을 승인할 수 있습니다. + +```javascript +session.on('certificate-error', function(event, webContents, url, error, certificate, callback) { + if (url == "https://github.com") { + // Verification logic. + event.preventDefault(); + callback(true); + } else { + callback(false); + } +}); +``` + ### Event: 'select-client-certificate' Returns: @@ -151,7 +187,7 @@ Returns: * `issuerName` String - 발급자의 공통 이름 * `callback` Function -사용자 인증이 요청되었을 때 발생하는 이벤트 입니다. +클라이언트 인증이 요청되었을 때 발생하는 이벤트 입니다. `url`은 클라이언트 인증서를 요청하는 탐색 항목에 해당합니다. 그리고 `callback`은 목록에서 필터링된 항목과 함께 호출될 필요가 있습니다. @@ -294,14 +330,6 @@ npm 모듈 규칙에 따라 대부분의 경우 `package.json`의 `name` 필드 현재 어플리케이션의 [로케일](https://ko.wikipedia.org/wiki/%EB%A1%9C%EC%BC%80%EC%9D%BC)을 반환합니다. -### `app.resolveProxy(url, callback)` - -* `url` URL -* `callback` Function - -`url`의 프록시 정보를 해석합니다. `callback`은 요청이 수행되었을 때 -`callback(proxy)` 형태로 호출됩니다. - ### `app.addRecentDocument(path)` _OS X_ _Windows_ * `path` String @@ -323,7 +351,7 @@ Windows에서 사용할 수 있는 JumpList의 [Tasks][tasks] 카테고리에 `t `tasks`는 다음과 같은 구조를 가지는 `Task` 객체의 배열입니다: -`Task` Object +`Task` Object: * `program` String - 실행할 프로그램의 경로. 보통 현재 작동중인 어플리케이션의 경로인 `process.execPath`를 지정합니다. * `arguments` String - `program`이 실행될 때 사용될 명령줄 인자. @@ -350,7 +378,7 @@ Windows에서 사용할 수 있는 JumpList의 [Tasks][tasks] 카테고리에 `t * `callback` Function -현재 어플리케이션을 **Single Instance Application**으로 만들어줍니다. +현재 어플리케이션을 **Single Instance Application** 으로 만들어줍니다. 이 메서드는 어플리케이션이 여러 번 실행됐을 때 다중 인스턴스가 생성되는 대신 한 개의 주 인스턴스만 유지되도록 만들 수 있습니다. 이때 중복 생성된 인스턴스는 주 인스턴스에 신호를 보내고 종료됩니다. @@ -369,7 +397,7 @@ Windows에서 사용할 수 있는 JumpList의 [Tasks][tasks] 카테고리에 `t 중복 생성된 인스턴스는 즉시 종료시켜야 합니다. OS X에선 사용자가 Finder에서 어플리케이션의 두 번째 인스턴스를 열려고 했을 때 자동으로 -**Single Instance**화 하고 `open-file`과 `open-url` 이벤트를 발생시킵니다. 그러나 +**Single Instance** 화 하고 `open-file`과 `open-url` 이벤트를 발생시킵니다. 그러나 사용자가 어플리케이션을 CLI 터미널에서 실행하면 운영체제 시스템의 싱글 인스턴스 메커니즘이 무시되며 그대로 중복 실행됩니다. 따라서 OS X에서도 이 메서드를 통해 확실히 중복 실행을 방지하는 것이 좋습니다. diff --git a/docs-translations/ko-KR/api/browser-window.md b/docs-translations/ko-KR/api/browser-window.md index ef1f270a0..a278a5075 100644 --- a/docs-translations/ko-KR/api/browser-window.md +++ b/docs-translations/ko-KR/api/browser-window.md @@ -5,8 +5,12 @@ 다음 예제는 윈도우 창을 생성합니다: ```javascript +// 메인 프로세스에서 const BrowserWindow = require('electron').BrowserWindow; +// 또는 랜더러 프로세스에서 +const BrowserWindow = require('electron').remote.BrowserWindow; + var win = new BrowserWindow({ width: 800, height: 600, show: false }); win.on('closed', function() { win = null; @@ -27,50 +31,62 @@ win.show(); `BrowserWindow`는 `options`를 통해 네이티브 속성을 포함한 새로운 윈도우 창을 생성합니다. -### `new BrowserWindow(options)` +### `new BrowserWindow([options])` -`options` 객체에서 사용할 수 있는 속성들: +`options` 객체 (optional), 사용할 수 있는 속성들: -* `width` Integer - 윈도우 창의 가로 너비. -* `height` Integer - 윈도우 창의 세로 높이. -* `x` Integer - 화면을 기준으로 창 좌측을 오프셋 한 위치. -* `y` Integer - 화면을 기준으로 창 상단을 오프셋 한 위치. +* `width` Integer - 윈도우 창의 가로 너비. 기본값은 `800`입니다. +* `height` Integer - 윈도우 창의 세로 높이. 기본값은 `600`입니다. +* `x` Integer - 화면을 기준으로 창 좌측을 오프셋 한 위치. 기본값은 `화면중앙`입니다. +* `y` Integer - 화면을 기준으로 창 상단을 오프셋 한 위치. 기본값은 `화면중앙`입니다. * `useContentSize` Boolean - `width`와 `height`를 웹 페이지의 크기로 사용합니다. 이 속성을 사용하면 웹 페이지의 크기에 윈도우 프레임 크기가 추가되므로 실제 창은 조금 - 더 커질 수 있습니다. + 더 커질 수 있습니다. 기본값은 `false`입니다. * `center` Boolean - 윈도우 창을 화면 정 중앙에 위치시킵니다. -* `minWidth` Integer - 윈도우 창의 최소 가로 너비. -* `minHeight` Integer - 윈도우 창의 최소 세로 높이. -* `maxWidth` Integer - 윈도우 창의 최대 가로 너비. -* `maxHeight` Integer - 윈도우 창의 최대 세로 높이. -* `resizable` Boolean - 윈도우 창의 크기를 재조정 할 수 있는지 여부. +* `minWidth` Integer - 윈도우 창의 최소 가로 너비. 기본값은 `0`입니다. +* `minHeight` Integer - 윈도우 창의 최소 세로 높이. 기본값은 `0`입니다. +* `maxWidth` Integer - 윈도우 창의 최대 가로 너비. 기본값은 `제한없음`입니다. +* `maxHeight` Integer - 윈도우 창의 최대 세로 높이. 기본값은 `제한없음`입니다. +* `resizable` Boolean - 윈도우 창의 크기를 재조정 할 수 있는지 여부. 기본값은 `true` + 입니다. * `alwaysOnTop` Boolean - 윈도우 창이 언제나 다른 창들 위에 유지되는지 여부. -* `fullscreen` Boolean - 윈도우 창의 전체화면 활성화 여부. + 기본값은 `false`입니다. +* `fullscreen` Boolean - 윈도우 창의 전체화면 활성화 여부. 기본값은 `false` 입니다. `false`로 지정했을 경우 OS X에선 전체화면 버튼이 숨겨지거나 비활성화됩니다. -* `skipTaskbar` Boolean - 작업표시줄 어플리케이션 아이콘 표시 여부. -* `kiosk` Boolean - Kiosk(키오스크) 모드. -* `title` String - 기본 윈도우 창 제목. +* `skipTaskbar` Boolean - 작업표시줄 어플리케이션 아이콘 표시 스킵 여부. 기본값은 + `false`입니다. +* `kiosk` Boolean - Kiosk(키오스크) 모드. 기본값은 `false`입니다. +* `title` String - 기본 윈도우 창 제목. 기본값은 `"Electron"`입니다. * `icon` [NativeImage](native-image.md) - 윈도우 아이콘, 생략하면 실행 파일의 아이콘이 대신 사용됩니다. -* `show` Boolean - 윈도우가 생성되면 보여줄지 여부. +* `show` Boolean - 윈도우가 생성되면 보여줄지 여부. 기본값은 `true`입니다. * `frame` Boolean - `false`로 지정하면 창을 [Frameless Window](frameless-window.md) - 형태로 생성합니다. + 형태로 생성합니다. 기본값은 `true`입니다. * `acceptFirstMouse` Boolean - 윈도우가 비활성화 상태일 때 내부 컨텐츠 클릭 시 - 활성화 되는 동시에 단일 mouse-down 이벤트를 발생시킬지 여부. -* `disableAutoHideCursor` Boolean - 파이핑중 자동으로 커서를 숨길지 여부. + 활성화 되는 동시에 단일 mouse-down 이벤트를 발생시킬지 여부. 기본값은 `false`입니다. +* `disableAutoHideCursor` Boolean - 파이핑중 자동으로 커서를 숨길지 여부. 기본값은 + `false`입니다. * `autoHideMenuBar` Boolean - `Alt`를 누르지 않는 한 어플리케이션 메뉴바를 숨길지 - 여부. + 여부. 기본값은 `false`입니다. * `enableLargerThanScreen` Boolean - 윈도우 창 크기가 화면 크기보다 크게 재조정 될 - 수 있는지 여부. + 수 있는지 여부. 기본값은 `false`입니다. * `backgroundColor` String - 16진수로 표현된 윈도우의 배경 색. `#66CD00` 또는 - `#FFF`가 사용될 수 있습니다. - 이 속성은 Linux와 Windows에만 구현되어 있습니다. + `#FFF`가 사용될 수 있습니다. 이 속성은 Linux와 Windows에만 구현되어 있습니다. + 기본값은 `#000`(검정)입니다. * `darkTheme` Boolean - 설정에 상관 없이 무조건 어두운 윈도우 테마를 사용합니다. - 몇몇 GTK+3 데스크톱 환경에서만 작동합니다. -* `transparent` Boolean - 윈도우 창을 [투명화](frameless-window.md)합니다. -* `type` String - 윈도우 창 종류를 지정합니다. - 사용할 수 있는 창 종류는 `desktop`, `dock`, `toolbar`, `splash`, - `notification`와 같습니다. 이 속성은 Linux에서만 작동합니다. + 몇몇 GTK+3 데스크톱 환경에서만 작동합니다. 기본값은 `false`입니다. +* `transparent` Boolean - 윈도우 창을 [투명화](frameless-window.md)합니다. 기본값은 + `false`입니다. +* `type` String - 특정 플랫폼에만 적용되는 윈도우 창의 종류를 지정합니다. 기본적으로 + 이 속성이 `undefined`일 경우 표준 윈도우가 사용됩니다. 사용할 수 있는 창의 종류는 + 다음과 같습니다: + * Linux의 경우: `desktop`, `dock`, `toolbar`, `splash`, `notification` 종류를 + 사용할 수 있습니다. + * OS X의 경우: `desktop`, `textured` 종류를 사용할 수 있습니다. `textured` 종류는 + 창을 그라디언트 형태로 표현합니다 (`NSTexturedBackgroundWindowMask`) `desktop` + 종류는 데스크탑 배경 레벨에 윈도우를 배치합니다 (`kCGDesktopWindowLevel - 1`). + 참고로 이렇게 만들어진 윈도우는 포커스, 키보드, 마우스 이벤트를 받을 수 없습니다. + 하지만 편법으로 `globalShortcut`을 통해 키 입력을 받을 수 있습니다. * `standardWindow` Boolean - OS X의 표준 윈도우를 텍스쳐 윈도우 대신 사용합니다. 기본 값은 `true`입니다. * `titleBarStyle` String, OS X - 윈도우 타이틀 바 스타일을 지정합니다. 이 속성은 @@ -87,11 +103,9 @@ win.show(); * `preload` String - 스크립트를 지정하면 페이지 내의 다른 스크립트가 작동하기 전에 로드됩니다. 여기서 지정한 스크립트는 node 통합 활성화 여부에 상관없이 언제나 모든 node API에 접근할 수 있습니다. 이 속성의 스크립트 경로는 절대 경로로 지정해야 - 합니다. - - note 통합이 비활성화되어있을 경우, preload 스크립트는 Node의 global 심볼들을 - 다시 global 스코프로 다시 포함 시킬 수 있습니다. [여기](process.md#event-loaded) - 의 예제를 참고하세요. + 합니다. node 통합이 비활성화되어있을 경우, preload 스크립트는 node의 global + 심볼들을 다시 global 스코프로 다시 포함 시킬 수 있습니다. + [여기](process.md#event-loaded)의 예제를 참고하세요. * `partition` String - 페이지에서 사용할 세션을 지정합니다. 만약 `partition`이 `persist:`로 시작하면 페이지는 지속성 세션을 사용하며 다른 모든 앱 내의 페이지에서 같은 `partition`을 사용할 수 있습니다. 만약 `persist:` 접두어로 @@ -99,32 +113,37 @@ win.show(); `partition`을 지정하면 같은 세션을 공유할 수 있습니다. `partition`을 지정하지 않으면 어플리케이션의 기본 세션이 사용됩니다. * `zoomFactor` Number - 페이지의 기본 줌 값을 지정합니다. 예를 들어 `300%`를 - 표현하려면 `3.0`으로 지정합니다. - * `javascript` Boolean + 표현하려면 `3.0`으로 지정합니다. 기본값은 `1.0`입니다. + * `javascript` Boolean - 자바스크립트를 활성화합니다. 기본값은 `false`입니다. * `webSecurity` Boolean - `false`로 지정하면 same-origin 정책을 비활성화합니다. - (이 속성은 보통 사람에 의해 웹 사이트를 테스트할 때 사용합니다) 그리고 - `allowDisplayingInsecureContent`와 `allowRunningInsecureContent`이 - 사용자로부터 `true`로 지정되지 않은 경우 `true`로 지정합니다. + (이 속성은 보통 사람들에 의해 웹 사이트를 테스트할 때 사용합니다) 그리고 + `allowDisplayingInsecureContent`와 `allowRunningInsecureContent` 두 속성을 + 사용자가 `true`로 지정되지 않은 경우 `true`로 지정합니다. 기본값은 + `true`입니다. * `allowDisplayingInsecureContent` Boolean - https 페이지에서 http URL에서 - 로드한 이미지 같은 리소스를 표시할 수 있도록 허용합니다. + 로드한 이미지 같은 리소스를 표시할 수 있도록 허용합니다. 기본값은 `false`입니다. * `allowRunningInsecureContent` Boolean - https 페이지에서 http URL에서 로드한 - JavaScript와 CSS 또는 플러그인을 실행시킬 수 있도록 허용합니다. - * `images` Boolean - * `java` Boolean - * `textAreasAreResizable` Boolean - * `webgl` Boolean - * `webaudio` Boolean - * `plugins` Boolean - 어떤 플러그인이 활성화되어야 하는지 지정합니다. - * `experimentalFeatures` Boolean - * `experimentalCanvasFeatures` Boolean - * `overlayScrollbars` Boolean - * `overlayFullscreenVideo` Boolean - * `sharedWorker` Boolean + JavaScript와 CSS 또는 플러그인을 실행시킬 수 있도록 허용합니다. 기본값은 + `false`입니다. + * `images` Boolean - 이미지 지원을 활성화합니다. 기본값은 `true`입니다. + * `textAreasAreResizable` Boolean - HTML TextArea 요소의 크기를 재조정을 + 허용합니다. 기본값은 `true`입니다. + * `webgl` Boolean - WebGL 지원을 활성화합니다. 기본값은 `true`입니다. + * `webaudio` Boolean - WebAudio 지원을 활성화합니다. 기본값은 `true`입니다. + * `plugins` Boolean - 플러그인 활성화 여부를 지정합니다. 기본값은 `false`입니다. + * `experimentalFeatures` Boolean - Chrome의 실험적인 기능을 활성화합니다. + 기본값은 `false`입니다. + * `experimentalCanvasFeatures` Boolean - Chrome의 실험적인 캔버스(canvas) 기능을 + 활성화합니다. 기본값은 `false`입니다. + * `overlayScrollbars` Boolean - 오버레이 스크롤바를 활성화합니다. 기본값은 + `false`입니다. + * `sharedWorker` Boolean - SharedWorker 기능을 활성화합니다. 기본값은 + `false`입니다. * `directWrite` Boolean - Windows에서 폰트 랜더링을 위해 DirectWrite를 - 사용하는지를 지정합니다. + 사용하는지를 지정합니다. 기본값은 `true`입니다. * `pageVisibility` Boolean - 현재 윈도우의 가시성을 반영하는 대신 페이지가 visible 또는 hidden 중 지정된 상태를 계속 유지하도록 합니다. 이 속성을 `true`로 - 지정하면 DOM 타이머의 스로틀링을 방지할 수 있습니다. + 지정하면 DOM 타이머의 스로틀링을 방지할 수 있습니다. 기본값은 `false`입니다. ## Events @@ -722,3 +741,9 @@ Linux 플랫폼에선 Unity 데스크톱 환경만 지원합니다. 그리고 윈도우가 모든 워크스페이스에서 표시될지 여부를 반환합니다. **참고:** 이 API는 Windows에서 언제나 false를 반환합니다. + +### `win.setIgnoreMouseEvents(ignore)` _OS X_ + +* `ignore` Boolean + +윈도우에서 일어나는 모든 마우스 이벤트를 무시합니다. diff --git a/docs-translations/ko-KR/api/chrome-command-line-switches.md b/docs-translations/ko-KR/api/chrome-command-line-switches.md index 3c825b1ab..f13f37920 100644 --- a/docs-translations/ko-KR/api/chrome-command-line-switches.md +++ b/docs-translations/ko-KR/api/chrome-command-line-switches.md @@ -49,6 +49,21 @@ $ electron --js-flags="--harmony_proxies --harmony_collections" your-app 그리고 WebSocket 요청에만 적용됩니다. 그리고 모든 프록시 서버가 HTTPS가 WebSocket 요청을 지원하지 않고 있을 수 있으므로 사용시 주의해야 합니다. +## --proxy-bypass-list=`hosts` + +Electron이 세미콜론으로 구분된 호스트 리스트에서 지정한 프록시 서버를 건너뛰도록 +지시합니다. + +예시: + +```javascript +app.commandLine.appendSwitch('proxy-bypass-list', ';*.google.com;*foo.com;1.2.3.4:5678'); +``` + +위 예시는 로컬 주소(`localhost`, `127.0.0.1`, 등)와 `google.com`의 서브도메인, +`foo.com`을 접미사로 가지는 호스트, `1.2.3.4:5678` 호스트를 제외한 모든 연결에서 +프록시 서버를 사용합니다. + ## --proxy-pac-url=`url` 지정한 `url`의 PAC 스크립트를 사용합니다. diff --git a/docs-translations/ko-KR/api/crash-reporter.md b/docs-translations/ko-KR/api/crash-reporter.md index a4d42f013..0b7daba14 100644 --- a/docs-translations/ko-KR/api/crash-reporter.md +++ b/docs-translations/ko-KR/api/crash-reporter.md @@ -25,8 +25,8 @@ crashReporter.start({ * `options` Object, properties: * `productName` String, 기본값: Electron -* `companyName` String, 기본값: GitHub, Inc -* `submitURL` String, 기본값: http://54.249.141.255:1127/post +* `companyName` String (**필수항목**) +* `submitURL` String, (**필수항목**) * 크래시 리포트는 POST 방식으로 이 URL로 전송됩니다. * `autoSubmit` Boolean, 기본값: true * true로 지정할 경우 유저의 승인 없이 자동으로 오류를 보고합니다. diff --git a/docs-translations/ko-KR/api/desktop-capturer.md b/docs-translations/ko-KR/api/desktop-capturer.md new file mode 100644 index 000000000..5a531bf44 --- /dev/null +++ b/docs-translations/ko-KR/api/desktop-capturer.md @@ -0,0 +1,77 @@ +# desktopCapturer + +`desktopCapturer` 모듈은 `getUserMedia`에서 사용 가능한 소스를 가져올 때 사용할 수 +있습니다. + +```javascript +// 렌더러 프로세스 내부 +var desktopCapturer = require('electron').desktopCapturer; + +desktopCapturer.getSources({types: ['window', 'screen']}, function(error, sources) { + if (error) throw error; + for (var i = 0; i < sources.length; ++i) { + if (sources[i].name == "Electron") { + navigator.webkitGetUserMedia({ + audio: false, + video: { + mandatory: { + chromeMediaSource: 'desktop', + chromeMediaSourceId: sources[i].id, + minWidth: 1280, + maxWidth: 1280, + minHeight: 720, + maxHeight: 720 + } + } + }, gotStream, getUserMediaError); + return; + } + } +}); + +function gotStream(stream) { + document.querySelector('video').src = URL.createObjectURL(stream); +} + +function getUserMediaError(e) { + console.log('getUserMediaError'); +} +``` + +`navigator.webkitGetUserMedia` 호출에 대해 제약된 객체를 생성할 때 +`desktopCapturer`에서 소스를 사용한다면 `chromeMediaSource`은 반드시 +`"desktop"`으로 지정되어야 하며, `audio` 도 반드시 `false`로 지정되어야 합니다. + +만약 전체 데스크탑에서 오디오와 비디오 모두 캡쳐를 하고 싶을 땐 `chromeMediaSource`를 +`"screen"` 그리고 `audio`를 `true`로 지정할 수 있습니다. 이 방법을 사용하면 +`chromeMediaSourceId`를 지정할 수 없습니다. + + +## Methods + +`desktopCapturer` 모듈은 다음과 같은 메서드를 가지고 있습니다: + +### `desktopCapturer.getSources(options, callback)` + +* `options` Object + * `types` Array - 캡쳐될 데스크탑 소스의 타입에 대한 리스트를 배열로 지정합니다. + 사용 가능한 타입은 `screen`과 `window`입니다. + * `thumbnailSize` Object (optional) - 섬네일 크기를 조정할 때 최대한 맞춰질 크기, + 기본값은 `{width: 150, height: 150}`입니다. +* `callback` Function + +모든 데스크탑 소스를 요청합니다. 요청의 처리가 완료되면 `callback`은 +`callback(error, sources)` 형식으로 호출됩니다. + +`sources`는 `Source` 객체의 배열입니다. 각 `Source`는 캡쳐된 화면과 독립적인 +윈도우를 표현합니다. 그리고 다음과 같은 속성을 가지고 있습니다: +* `id` String - `navigator.webkitGetUserMedia` API에서 사용할 수 있는 캡쳐된 윈도우 + 또는 화면의 id입니다. 포맷은 `window:XX` 또는 `screen:XX`로 표현되며 `XX` 는 + 무작위로 생성된 숫자입니다. +* `name` String - 캡쳐된 화면과 윈도우에 대해 묘사된 이름입니다. 만약 소스가 + 화면이라면, `Entire Screen` 또는 `Screen `가 될 것이고 소스가 윈도우라면, + 해당 윈도우의 제목이 반환됩니다. +* `thumbnail` [NativeImage](NativeImage.md) - 섬네일 이미지. + +**참고:** `source.thumbnail`의 크기는 언제나 `options`의 `thumnbailSize`와 같다고 +보장할 수 없습니다. 섬네일의 크기는 화면과 윈도우의 크기에 의존하여 조정됩니다. diff --git a/docs-translations/ko-KR/api/dialog.md b/docs-translations/ko-KR/api/dialog.md index bb92c1f43..3fff8683c 100644 --- a/docs-translations/ko-KR/api/dialog.md +++ b/docs-translations/ko-KR/api/dialog.md @@ -19,10 +19,10 @@ console.log(dialog.showOpenDialog({ properties: [ 'openFile', 'openDirectory', ' `dialog` 모듈은 다음과 같은 메서드를 가지고 있습니다: -### `dialog.showOpenDialog([browserWindow][, options][, callback])` +### `dialog.showOpenDialog([browserWindow, ]options[, callback])` * `browserWindow` BrowserWindow (optional) -* `options` Object (optional) +* `options` Object * `title` String * `defaultPath` String * `filters` Array @@ -63,10 +63,10 @@ console.log(dialog.showOpenDialog({ properties: [ 'openFile', 'openDirectory', ' 없습니다. 이러한 이유로 `properties`를 `['openFile', 'openDirectory']`로 설정하면 디렉터리 선택 대화 상자가 표시됩니다. -### `dialog.showSaveDialog([browserWindow][, options][, callback])` +### `dialog.showSaveDialog([browserWindow, ]options[, callback])` * `browserWindow` BrowserWindow (optional) -* `options` Object (optional) +* `options` Object * `title` String * `defaultPath` String * `filters` Array @@ -80,9 +80,9 @@ console.log(dialog.showOpenDialog({ properties: [ 'openFile', 'openDirectory', ' `callback`이 전달되면 메서드가 비동기로 작동되며 결과는 `callback(filename)`을 통해 전달됩니다. -### `dialog.showMessageBox([browserWindow][, options][, callback])` +### `dialog.showMessageBox([browserWindow, ]options[, callback])` -* `browserWindow` BrowserWindow +* `browserWindow` BrowserWindow (optional) * `options` Object * `type` String - `"none"`, `"info"`, `"error"`, `"question"`, `"warning"` 중 하나를 사용할 수 있습니다. Windows에선 따로 `icon`을 설정하지 않은 이상 @@ -101,7 +101,7 @@ console.log(dialog.showOpenDialog({ properties: [ 'openFile', 'openDirectory', ' 버튼을 찾으려고 시도하고 대화 상자 내에서 해당 버튼을 커맨드 링크처럼 만듭니다. 이 기능으로 앱을 좀 더 Modern Windows 앱처럼 만들 수 있습니다. 이 기능을 원하지 않으면 `noLink`를 true로 지정하면 됩니다. -* `callback` Function +* `callback` Function (optional) 대화 상자를 표시합니다. `browserWindow`를 지정하면 대화 상자가 완전히 닫힐 때까지 지정한 창을 사용할 수 없습니다. 완료 시 유저가 선택한 버튼의 인덱스를 반환합니다. diff --git a/docs-translations/ko-KR/api/environment-variables.md b/docs-translations/ko-KR/api/environment-variables.md new file mode 100644 index 000000000..11d350692 --- /dev/null +++ b/docs-translations/ko-KR/api/environment-variables.md @@ -0,0 +1,49 @@ +# 환경 변수 + +Electron의 몇몇 동작은 명령 줄과 어플리케이션의 코드보다 먼저 초기화되어야 하므로 환경 변수에 의해 작동합니다. + +POSIX 쉘의 예시입니다: + +```bash +$ export ELECTRON_ENABLE_LOGGING=true +$ electron +``` + +Windows 콘솔의 예시입니다: + +```powershell +> set ELECTRON_ENABLE_LOGGING=true +> electron +``` + +## `ELECTRON_RUN_AS_NODE` + +프로세스를 일반 Node.js 프로세스처럼 시작합니다. (electron 모듈 제외) + +## `ELECTRON_ENABLE_LOGGING` + +Chrome의 내부 로그를 콘솔에 출력합니다. + +## `ELECTRON_ENABLE_STACK_DUMPING` + +Electron이 크래시되면, 콘솔에 stack trace를 출력합니다. + +이 환경 변수는 `crashReporter`가 시작되지 않았을 경우 작동하지 않습니다. + +## `ELECTRON_DEFAULT_ERROR_MODE` _Windows_ + +Electron이 크래시되면, 크래시 정보 창을 표시합니다. + +이 환경 변수는 `crashReporter`가 시작되지 않았을 경우 작동하지 않습니다. + +## `ELECTRON_NO_ATTACH_CONSOLE` _Windows_ + +현재 콘솔 세션에 소속시키지 않습니다. + +## `ELECTRON_FORCE_WINDOW_MENU_BAR` _Linux_ + +Linux의 글로벌 메뉴 막대를 사용하지 않습니다. + +## `ELECTRON_HIDE_INTERNAL_MODULES` + +`require('ipc')`같은 예전 방식의 빌트인 모듈을 비활성화합니다. diff --git a/docs-translations/ko-KR/api/global-shortcut.md b/docs-translations/ko-KR/api/global-shortcut.md index 1b8363bcb..9617d463e 100644 --- a/docs-translations/ko-KR/api/global-shortcut.md +++ b/docs-translations/ko-KR/api/global-shortcut.md @@ -46,7 +46,11 @@ app.on('will-quit', function() { * `callback` Function `accelerator`로 표현된 전역 단축키를 등록합니다. 유저로부터 등록된 단축키가 눌렸을 -경우 `callback` 함수가 호출됩니다. +경우 `callback` 함수가 호출됩니다. `accelerator` 단축키가 등록되었을 경우 +`true`를 반환합니다. 그 외엔 `false`를 반환합니다. 예를 들어 지정한 +`accelerator`가 이미 다른 호출자 또는 네이티브 어플리케이션에서 등록된 상태를 +생각할 수 있습니다. + ### `globalShortcut.isRegistered(accelerator)` diff --git a/docs-translations/ko-KR/api/ipc-main.md b/docs-translations/ko-KR/api/ipc-main.md index 2dffcaf79..4714fd7af 100644 --- a/docs-translations/ko-KR/api/ipc-main.md +++ b/docs-translations/ko-KR/api/ipc-main.md @@ -7,7 +7,7 @@ ## 메시지 전송 물론 메시지를 받는 것 말고도 메인 프로세스에서 랜더러 프로세스로 보내는 것도 가능합니다. -자세한 내용은 [webContents.send](web-contents.md#webcontentssendchannel-args)를 +자세한 내용은 [webContents.send](web-contents.md#webcontentssendchannel-arg1-arg2-)를 참고하세요. * 메시지를 전송할 때 이벤트 이름은 `channel`이 됩니다. @@ -19,12 +19,12 @@ ```javascript // 메인 프로세스 const ipcMain = require('electron').ipcMain; -ipc.on('asynchronous-message', function(event, arg) { +ipcMain.on('asynchronous-message', function(event, arg) { console.log(arg); // "ping" 출력 event.sender.send('asynchronous-reply', 'pong'); }); -ipc.on('synchronous-message', function(event, arg) { +ipcMain.on('synchronous-message', function(event, arg) { console.log(arg); // "ping" 출력 event.returnValue = 'pong'; }); @@ -35,17 +35,17 @@ ipc.on('synchronous-message', function(event, arg) { const ipcRenderer = require('electron').ipcRenderer; console.log(ipc.sendSync('synchronous-message', 'ping')); // "pong" 출력 -ipc.on('asynchronous-reply', function(arg) { +ipcRenderer.on('asynchronous-reply', function(arg) { console.log(arg); // "pong" 출력 }); -ipc.send('asynchronous-message', 'ping'); +ipcRenderer.send('asynchronous-message', 'ping'); ``` ## 메시지 리스닝 `ipcMain`은 다음과 같은 이벤트 리스닝 메서드를 가지고 있습니다: -### `ipc.on(channel, callback)` +### `ipcMain.on(channel, callback)` * `channel` String - 이벤트 이름 * `callback` Function @@ -56,14 +56,13 @@ ipc.send('asynchronous-message', 'ping'); `callback`에서 전달된 `event` 객체는 다음과 같은 메서드와 속성을 가지고 있습니다: -### `Event.returnValue` +### `event.returnValue` 이 메시지를 지정하면 동기 메시지를 전달합니다. -### `Event.sender` +### `event.sender` 메시지를 보낸 `webContents` 객체를 반환합니다. `event.sender.send` 메서드를 통해 비동기로 메시지를 전달할 수 있습니다. 자세한 내용은 -[webContents.send][webcontents-send]를 참고하세요. - -[webcontents-send]: web-contents.md#webcontentssendchannel-args +[webContents.send](web-contents.md#webcontentssendchannel-arg1-arg2-)를 +참고하세요. diff --git a/docs-translations/ko-KR/api/menu-item.md b/docs-translations/ko-KR/api/menu-item.md index 388df1c7d..0e723fc65 100644 --- a/docs-translations/ko-KR/api/menu-item.md +++ b/docs-translations/ko-KR/api/menu-item.md @@ -17,7 +17,8 @@ * `role` String - 메뉴 아이템의 액션을 정의합니다. 이 속성을 지정하면 `click` 속성이 무시됩니다. * `type` String - `MenuItem`의 타입 `normal`, `separator`, `submenu`, - `checkbox` 또는 `radio` 사용가능 + `checkbox` 또는 `radio`를 사용할 수 있습니다. 만약 값이 `Menu`가 아니면 + `Menu.buildFromTemplate`를 통해 자동으로 변환됩니다. * `label` String * `sublabel` String * `accelerator` [Accelerator](accelerator.md) diff --git a/docs-translations/ko-KR/api/menu.md b/docs-translations/ko-KR/api/menu.md index 3c5740de5..f01021ae1 100644 --- a/docs-translations/ko-KR/api/menu.md +++ b/docs-translations/ko-KR/api/menu.md @@ -221,7 +221,7 @@ Linux에선 각 창의 상단에 표시됩니다. * `action` String `action`을 어플리케이션의 first responder에 전달합니다. 이 메서드는 Cocoa 메뉴 -동작을 에뮬레이트 하는데 사용되며 보통 `MenuItem`의 `selector` 속성에 사용됩니다. +동작을 에뮬레이트 하는데 사용되며 보통 `MenuItem`의 `role` 속성에 사용됩니다. **참고:** 이 메서드는 OS X에서만 사용할 수 있습니다. diff --git a/docs-translations/ko-KR/api/native-image.md b/docs-translations/ko-KR/api/native-image.md index 917dbb3a7..80be2beea 100644 --- a/docs-translations/ko-KR/api/native-image.md +++ b/docs-translations/ko-KR/api/native-image.md @@ -123,7 +123,7 @@ var image = nativeImage.createFromPath('/Users/somebody/images/icon.png'); ### `image.toJpeg(quality)` -* `quality` Integer 0 - 100 사이의 값 (**required**) +* `quality` Integer (**required**) 0 - 100 사이의 값 `JPEG` 이미지를 인코딩한 데이터를 [Buffer][buffer]로 반환합니다. diff --git a/docs-translations/ko-KR/api/process.md b/docs-translations/ko-KR/api/process.md index afbc6afd7..de24b5ac1 100644 --- a/docs-translations/ko-KR/api/process.md +++ b/docs-translations/ko-KR/api/process.md @@ -30,6 +30,13 @@ process.once('loaded', function() { }); ``` +## Properties + +### `process.noAsar` + +이 속성을 `true`로 지정하면 Node 빌트인 모듈의 `asar` 아카이브 지원을 비활성화 시킬 +수 있습니다. + ## Methods `process` 객체는 다음과 같은 메서드를 가지고 있습니다: diff --git a/docs-translations/ko-KR/api/protocol.md b/docs-translations/ko-KR/api/protocol.md index a85ac1428..f6f2ac65c 100644 --- a/docs-translations/ko-KR/api/protocol.md +++ b/docs-translations/ko-KR/api/protocol.md @@ -35,6 +35,10 @@ app.on('ready', function() { 표준 `scheme`의 형식은 RFC 3986 [일반 URI 구문](https://tools.ietf.org/html/rfc3986#section-3) 표준을 따릅니다. 이 형식은 `file:`과 `filesystem:`을 포함합니다. +### `protocol.registerServiceWorkerSchemes(schemes)` + +* `schemes` Array - 등록될 서비스 워커를 조작할 커스텀 스키마 + ### `protocol.registerFileProtocol(scheme, handler[, completion])` * `scheme` String @@ -97,12 +101,17 @@ protocol.registerBufferProtocol('atom', function(request, callback) { * `completion` Function (optional) `scheme`에 HTTP 요청을 응답으로 보내는 프로토콜을 등록합니다. 반드시 `url`, -`method`, `referrer`, `session` 속성을 포함하는 객체를 인자에 포함하여 `callback`을 -호출해야 합니다. +`method`, `referrer`, `uploadData` 그리고 `session` 속성을 포함하는 객체를 인자에 +포함하여 `callback`을 호출해야 합니다. 기본적으로 HTTP 요청은 현재 세션을 재사용합니다. 만약 서로 다른 세션에 요청을 보내고 싶으면 `session`을 `null`로 지정해야 합니다. +POST 요청은 반드시 `uploadData` 객체가 제공되어야 합니다. +* `uploadData` object + * `contentType` String - 컨텐츠의 MIME 타입. + * `data` String - 전송할 컨텐츠. + ### `protocol.unregisterProtocol(scheme[, completion])` * `scheme` String diff --git a/docs-translations/ko-KR/api/session.md b/docs-translations/ko-KR/api/session.md index aa5ab2ed3..c857e1115 100644 --- a/docs-translations/ko-KR/api/session.md +++ b/docs-translations/ko-KR/api/session.md @@ -1,8 +1,9 @@ # session -`session` 객체는 [`BrowserWindow`](browser-window.md)의 -[`webContents`](web-contents.md)의 프로퍼티입니다. 다음과 같이 `BrowserWindow` -인스턴스에서 접근할 수 있습니다: +`session` 모듈은 새로운 `Session` 객체를 만드는데 사용할 수 있습니다. + +또한 존재하는 [`BrowserWindow`](browser-window.md)의 +[`webContents`](web-contents.md)에서 `session` 속성으로 접근할 수도 있습니다. ```javascript var BrowserWindow = require('browser-window'); @@ -10,12 +11,47 @@ var BrowserWindow = require('browser-window'); var win = new BrowserWindow({ width: 800, height: 600 }); win.loadURL("http://github.com"); -var session = win.webContents.session +var ses = win.webContents.session; ``` -## Events +## Methods -### Event: 'will-download' +`session` 모듈은 다음과 같은 메서드를 가지고 있습니다: + +### session.fromPartition(partition) + +* `partition` String + +`partition` 문자열로 부터 새로운 `Session` 인스턴스를 만들어 반환합니다. + +`partition`이 `persist:`로 시작하면 페이지는 지속성 세션을 사용하며 다른 모든 앱 내의 +페이지에서 같은 `partition`을 사용할 수 있습니다. 만약 `persist:` 접두어로 시작하지 +않으면 페이지는 인-메모리 세션을 사용합니다. `partition`을 지정하지 않으면 어플리케이션의 +기본 세션이 반환됩니다. + +## Properties + +`session` 모듈은 다음과 같은 속성을 가지고 있습니다: + +### session.defaultSession + +어플리케이션의 기본 세션 객체를 반환합니다. + +## Class: Session + +`session` 모듈을 사용하여 `Session` 객체를 생성할 수 있습니다: + +```javascript +const session = require('electron').session; + +var ses = session.fromPartition('persist:name'); + ``` + +### Instance Events + +`Session` 객체는 다음과 같은 이벤트를 가지고 있습니다: + +#### Event: 'will-download' * `event` Event * `item` [DownloadItem](download-item.md) @@ -26,7 +62,7 @@ Electron의 `webContents`에서 `item`을 다운로드할 때 발생하는 이 `event.preventDefault()` 메서드를 호출하면 다운로드를 취소합니다. ```javascript -session.on('will-download', function(event, item, webContents) { +session.defaultSession.on('will-download', function(event, item, webContents) { event.preventDefault(); require('request')(item.getURL(), function(data) { require('fs').writeFileSync('/somewhere', data); @@ -34,134 +70,101 @@ session.on('will-download', function(event, item, webContents) { }); ``` -### Event: 'untrusted-certificate' +### Instance Methods -* `event` Event -* `hostname` String -* `certificate` Object - * `data` Buffer - PEM encoded data - * `issuerName` String -* `callback` Function +`Session` 객체는 다음과 같은 메서드와 속성을 가지고 있습니다: -`hostname`에 대한 `certificate`의 유효성 검증이 실패했을 때 발생하는 이벤트 입니다. -인증서를 신뢰한다면 `event.preventDefault()` 와 `callback(true)`를 호출하여 기본 -동작을 방지해야 합니다. - -```javascript -session.on('verify-certificate', function(event, hostname, certificate, callback) { - if (hostname == "github.com") { - // verification logic. - event.preventDefault(); - callback(true); - } else { - callback(false); - } -}); -``` - -## Methods - -`session` 객체는 다음과 같은 메서드와 속성을 가지고 있습니다: - -### `session.cookies` +#### `ses.cookies` `cookies` 속성은 쿠키를 조작하는 방법을 제공합니다. 예를 들어 다음과 같이 할 수 있습니다: ```javascript -var BrowserWindow = require('browser-window'); +// 모든 쿠키를 요청합니다. +session.defaultSession.cookies.get({}, function(error, cookies) { + console.log(cookies); +}); -var win = new BrowserWindow({ width: 800, height: 600 }); +// url에 관련된 쿠키를 모두 가져옵니다. +session.defaultSession.cookies.get({ url : "http://www.github.com" }, function(error, cookies) { + console.log(cookies); +}); -win.loadURL('https://github.com'); - -win.webContents.on('did-finish-load', function() { - // 모든 쿠키를 가져옵니다. - win.webContents.session.cookies.get({}, function(error, cookies) { - if (error) throw error; - console.log(cookies); - }); - - // Url에 관련된 쿠키를 모두 가져옵니다. - win.webContents.session.cookies.get({ url : "http://www.github.com" }, - function(error, cookies) { - if (error) throw error; - console.log(cookies); - }); - - // 지정한 쿠키 데이터를 설정합니다. - // 동일한 쿠키가 있으면 해당 쿠키를 덮어씁니다. - win.webContents.session.cookies.set( - { url : "http://www.github.com", name : "dummy_name", value : "dummy"}, - function(error, cookies) { - if (error) throw error; - console.log(cookies); - }); +// 지정한 쿠키 데이터를 설정합니다. +// 동일한 쿠키가 있으면 해당 쿠키를 덮어씁니다. +var cookie = { url : "http://www.github.com", name : "dummy_name", value : "dummy" }; +session.defaultSession.cookies.set(cookie, function(error) { + if (error) + console.error(error); }); ``` -### `session.cookies.get(details, callback)` +#### `ses.cookies.get(filter, callback)` -`details` Object, properties: +* `filter` Object + * `url` String (optional) - `url`에 해당하는 쿠키를 취득합니다. 이 속성을 + 생략하면 모든 url에서 찾습니다. + * `name` String (optional) - 쿠키의 이름입니다. + * `domain` String (optional) - 도메인 또는 서브 도메인에 일치하는 쿠키를 + 취득합니다. + * `path` String (optional) - `path`에 일치하는 쿠키를 취득합니다. + * `secure` Boolean (optional) - 보안 속성에 따라 쿠키를 필터링합니다. + * `session` Boolean (optional) - 세션 또는 지속성 쿠키를 필터링합니다. +* `callback` Function -* `url` String - `url`에 관련된 쿠키를 가져옵니다. 이 속성을 비워두면 모든 url의 - 쿠키를 가져옵니다. -* `name` String - 이름을 기준으로 쿠키를 필터링합니다. -* `domain` String - `domain`과 일치하는 도메인과 서브 도메인에 대한 쿠키를 가져옵니다. -* `path` String - `path`와 일치하는 경로에 대한 쿠키를 가져옵니다. -* `secure` Boolean - 보안 속성을 기준으로 쿠키를 필터링합니다. -* `session` Boolean - 세션 또는 영구 쿠키를 필터링합니다. +`details` 객체에서 묘사한 모든 쿠키를 요청합니다. 모든 작업이 끝나면 `callback`이 +`callback(error, cookies)` 형태로 호출됩니다. -* `callback` Function - function(error, cookies) -* `error` Error -* `cookies` Array - `cookie` 객체의 배열, 속성은 다음과 같습니다: +`cookies`는 `cookie` 객체의 배열입니다. + +* `cookie` Object * `name` String - 쿠키의 이름. * `value` String - 쿠키의 값. * `domain` String - 쿠키의 도메인. - * `host_only` String - 쿠키가 호스트 전용인가에 대한 여부. + * `hostOnly` String - 쿠키가 호스트 전용인가에 대한 여부. * `path` String - 쿠키의 경로. - * `secure` Boolean - 쿠키가 안전한 것으로 표시되는지에 대한 여부. (일반적으로 - HTTPS) - * `http_only` Boolean - 쿠키가 HttpOnly로 표시되는지에 대한 여부. + * `secure` Boolean - 쿠키가 안전한 것으로 표시되는지에 대한 여부. + * `httpOnly` Boolean - 쿠키가 HTTP로만 표시되는지에 대한 여부. * `session` Boolean - 쿠키가 세션 쿠키 또는 만료일이 있는 영구 쿠키인지에 대한 여부. * `expirationDate` Double - (Option) UNIX 시간으로 표시되는 쿠키의 만료일에 대한 초 단위 시간. 세션 쿠키는 지원되지 않음. -### `session.cookies.set(details, callback)` +#### `ses.cookies.set(details, callback)` -`details` Object, properties: +* `details` Object + * `url` String - `url`에 관련된 쿠키를 가져옵니다. + * `name` String - 쿠키의 이름입니다. 기본적으로 비워두면 생략됩니다. + * `value` String - 쿠키의 값입니다. 기본적으로 비워두면 생략됩니다. + * `domain` String - 쿠키의 도메인입니다. 기본적으로 비워두면 생략됩니다. + * `path` String - 쿠키의 경로입니다. 기본적으로 비워두면 생략됩니다. + * `secure` Boolean - 쿠키가 안전한 것으로 표시되는지에 대한 여부입니다. 기본값은 + false입니다. + * `session` Boolean - 쿠키가 HttpOnly로 표시되는지에 대한 여부입니다. 기본값은 + false입니다. + * `expirationDate` Double (optional) - UNIX 시간으로 표시되는 쿠키의 만료일에 + 대한 초 단위 시간입니다. 세션 쿠키에 제공되지 않습니다. +* `callback` Function -* `url` String - `url`에 관련된 쿠키를 가져옵니다. -* `name` String - 쿠키의 이름입니다. 기본적으로 비워두면 생략됩니다. -* `value` String - 쿠키의 값입니다. 기본적으로 비워두면 생략됩니다. -* `domain` String - 쿠키의 도메인입니다. 기본적으로 비워두면 생략됩니다. -* `path` String - 쿠키의 경로입니다. 기본적으로 비워두면 생략됩니다. -* `secure` Boolean - 쿠키가 안전한 것으로 표시되는지에 대한 여부입니다. 기본값은 - false입니다. -* `session` Boolean - 쿠키가 HttpOnly로 표시되는지에 대한 여부입니다. 기본값은 - false입니다. -* `expirationDate` Double - UNIX 시간으로 표시되는 쿠키의 만료일에 대한 초 단위 - 시간입니다. 생략하면 쿠키는 세션 쿠키가 됩니다. +`details` 객체에 따라 쿠키를 설정합니다. 작업이 완료되면 `callback`이 +`callback(error)` 형태로 호출됩니다. -* `callback` Function - function(error) - * `error` Error +#### `ses.cookies.remove(url, name, callback)` -### `session.cookies.remove(details, callback)` +* `url` String - 쿠키와 관련된 URL입니다. +* `name` String - 지울 쿠키의 이름입니다. +* `callback` Function -* `details` Object, proprties: - * `url` String - 쿠키와 관련된 URL입니다. - * `name` String - 지울 쿠키의 이름입니다. -* `callback` Function - function(error) - * `error` Error +`url`과 `name`에 일치하는 쿠키를 삭제합니다. 작업이 완료되면 `callback`이 +`callback()` 형식으로 호출됩니다. -### `session.clearCache(callback)` +#### `ses.clearCache(callback)` * `callback` Function - 작업이 완료되면 호출됩니다. 세션의 HTTP 캐시를 비웁니다. -### `session.clearStorageData([options, ]callback)` +#### `ses.clearStorageData([options, ]callback)` * `options` Object (optional), proprties: * `origin` String - `scheme://host:port`와 같은 `window.location.origin` 규칙을 @@ -175,7 +178,7 @@ win.webContents.on('did-finish-load', function() { 웹 스토리지의 데이터를 비웁니다. -### `session.setProxy(config, callback)` +#### `ses.setProxy(config, callback)` * `config` String * `callback` Function - 작업이 완료되면 호출됩니다. @@ -216,14 +219,22 @@ proxy-uri = ["://"][":"] 프록시를 다른 모든 URL에 사용합니다. ``` -### `session.setDownloadPath(path)` +### `app.resolveProxy(url, callback)` + +* `url` URL +* `callback` Function + +`url`의 프록시 정보를 해석합니다. `callback`은 요청이 수행되었을 때 +`callback(proxy)` 형태로 호출됩니다. + +#### `ses.setDownloadPath(path)` * `path` String - 다운로드 위치 다운로드 저장 위치를 지정합니다. 기본 다운로드 위치는 각 어플리케이션 데이터 디렉터리의 `Downloads` 폴더입니다. -### `session.enableNetworkEmulation(options)` +#### `ses.enableNetworkEmulation(options)` * `options` Object * `offline` Boolean - 네트워크의 오프라인 상태 여부 @@ -245,6 +256,214 @@ window.webContents.session.enableNetworkEmulation({ window.webContents.session.enableNetworkEmulation({offline: true}); ``` -### `session.disableNetworkEmulation` +#### `ses.disableNetworkEmulation()` 활성화된 `session`의 에뮬레이션을 비활성화합니다. 기본 네트워크 설정으로 돌아갑니다. + +#### `ses.setCertificateVerifyProc(proc)` + + * `proc` Function + +`session`에 인증서의 유효성을 확인하는 프로세스(proc)를 등록합니다. `proc`은 서버 +인증서 유효성 검증 요청이 들어왔을 때 언제나 `proc(hostname, certificate, callback)` +형식으로 호출됩니다. `callback(true)`을 호출하면 인증을 승인하고 `callback(false)`를 +호출하면 인증을 거부합니다. + +`setCertificateVerifyProc(null)`을 호출하면 기본 검증 프로세스로 되돌립니다. + +```javascript +myWindow.webContents.session.setCertificateVerifyProc(function(hostname, cert, callback) { + if (hostname == 'github.com') + callback(true); + else + callback(false); +}); +``` + +#### `ses.webRequest` + +`webRequest` API는 생명주기의 다양한 단계에 맞춰 요청 컨텐츠를 가로채거나 변경할 수 +있도록 합니다. + +각 API는 `filter`와 `listener`를 선택적으로 받을 수 있습니다. `listener`는 API의 +이벤트가 발생했을 때 `listener(details)` 형태로 호출되며 `defails`는 요청을 묘사하는 +객체입니다. `listener`에 `null`을 전달하면 이벤트 수신을 중지합니다. + +`filter`는 `urls` 속성을 가진 객체입니다. 이 속성은 URL 규칙의 배열이며 URL 규칙에 +일치하지 않는 요청을 모두 거르는데 사용됩니다. 만약 `filter`가 생략되면 모든 요청을 +여과 없이 통과시킵니다. + +어떤 `listener`의 이벤트들은 `callback`을 같이 전달하는데, 이벤트 처리시 +`listener`의 작업을 완료한 후 `response` 객체를 포함하여 호출해야 합니다. + +```javascript +// 다음 url에 대한 User Agent를 조작합니다. +var filter = { + urls: ["https://*.github.com/*", "*://electron.github.io"] +}; + +session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, callback) { + details.requestHeaders['User-Agent'] = "MyAgent"; + callback({cancel: false, requestHeaders: details.requestHeaders}); +}); +``` + +#### `ses.webRequest.onBeforeRequest([filter, ]listener)` + +* `filter` Object +* `listener` Function + +요청이 발생하면 `listener`가 `listener(details, callback)` 형태로 호출됩니다. + +* `details` Object + * `id` Integer + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + +`callback`은 `response` 객체와 함께 호출되어야 합니다: + +* `response` Object + * `cancel` Boolean (optional) + * `redirectURL` String (optional) - 원래 요청은 전송과 완료가 방지되지만 이 + 속성을 지정하면 해당 URL로 리다이렉트됩니다. + +#### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` + +* `filter` Object +* `listener` Function + +HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener`가 +`listener(details, callback)` 형태로 호출됩니다. 이 이벤트는 서버와의 TCP 연결이 +완료된 후에 발생할 수도 있지만 http 데이터가 전송되기 전에 발생합니다. + +* `details` Object + * `id` Integer + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `requestHeaders` Object + +`callback`은 `response` 객체와 함께 호출되어야 합니다: + +* `response` Object + * `cancel` Boolean (optional) + * `requestHeaders` Object (optional) - 이 속성이 제공되면, 요청은 이 헤더로 + 만들어 집니다. + +#### `ses.webRequest.onSendHeaders([filter, ]listener)` + +* `filter` Object +* `listener` Function + +서버에 요청이 전송되기 바로 전에 `listener`가 `listener(details)` 형태로 호출됩니다. +이전 `onBeforeSendHeaders`의 response와 다른점은 리스너가 호출되는 시간으로 볼 수 +있습니다. + +* `details` Object + * `id` Integer + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `requestHeaders` Object + +#### `ses.webRequest.onHeadersReceived([filter,] listener)` + +* `filter` Object +* `listener` Function + +요청의 HTTP 응답 헤더를 받았을 때 `listener`가 `listener(details, callback)` 형태로 +호출됩니다. + +* `details` Object + * `id` String + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `statusLine` String + * `statusCode` Integer + * `responseHeaders` Object + +`callback`은 `response` 객체와 함께 호출되어야 합니다: + +* `response` Object + * `cancel` Boolean + * `responseHeaders` Object (optional) - 이 속성이 제공되면 서버는 이 헤더와 + 함께 응답합니다. + +#### `ses.webRequest.onResponseStarted([filter, ]listener)` + +* `filter` Object +* `listener` Function + +요청 본문의 첫 번째 바이트를 받았을 때 `listener`가 `listener(details)` 형태로 +호출됩니다. 이는 HTTP 요청에서 상태 줄과 요청 헤더가 사용 가능한 상태를 의미합니다. + +* `details` Object + * `id` Integer + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `responseHeaders` Object + * `fromCache` Boolean - 응답을 디스크 캐시에서 가져올지에 대한 여부. + * `statusCode` Integer + * `statusLine` String + +#### `ses.webRequest.onBeforeRedirect([filter, ]listener)` + +* `filter` Object +* `listener` Function + +서버에서 시작된 리다이렉트가 발생했을 때 `listener`가 `listener(details)` 형태로 +호출됩니다. + +* `details` Object + * `id` String + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `redirectURL` String + * `statusCode` Integer + * `ip` String (optional) - 요청이 실질적으로 전송될 서버 아이피 주소. + * `fromCache` Boolean + * `responseHeaders` Object + +#### `ses.webRequest.onCompleted([filter, ]listener)` + +* `filter` Object +* `listener` Function + +요청이 완료되면 `listener`가 `listener(details)` 형태로 호출됩니다. + +* `details` Object + * `id` Integer + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `responseHeaders` Object + * `fromCache` Boolean + * `statusCode` Integer + * `statusLine` String + +#### `ses.webRequest.onErrorOccurred([filter, ]listener)` + +* `filter` Object +* `listener` Function + +에러가 발생하면 `listener`가 `listener(details)` 형태로 호출됩니다. + +* `details` Object + * `id` Integer + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `fromCache` Boolean + * `error` String - 에러 설명. diff --git a/docs-translations/ko-KR/api/tray.md b/docs-translations/ko-KR/api/tray.md index 97db97f42..e182f6471 100644 --- a/docs-translations/ko-KR/api/tray.md +++ b/docs-translations/ko-KR/api/tray.md @@ -184,12 +184,16 @@ __주의:__ `bounds`는 OS X 와 Windows에서만 작동합니다. 트레이에 풍선 팝업을 생성합니다. -### `Tray.popContextMenu([position])` _OS X_ _Windows_ +### `Tray.popUpContextMenu([menu, position])` _OS X_ _Windows_ -* `position` Object (optional) - 팝업 메뉴 위치 +* `menu` Menu (optional) +* `position` Object (optional) - 팝업 메뉴의 위치 * `x` Integer * `y` Integer +트레이 아이콘의 컨텍스트 메뉴를 팝업시킵니다. `menu`가 전달되면, `menu`가 트레이 +메뉴의 컨텍스트 메뉴 대신 표시됩니다. + `position`은 Windows에서만 사용할 수 있으며 기본값은 (0, 0)입니다. ### `Tray.setContextMenu(menu)` diff --git a/docs-translations/ko-KR/api/web-contents.md b/docs-translations/ko-KR/api/web-contents.md index edec4d472..59b7d0c15 100644 --- a/docs-translations/ko-KR/api/web-contents.md +++ b/docs-translations/ko-KR/api/web-contents.md @@ -130,11 +130,39 @@ Returns: 사용자 또는 페이지가 새로운 페이지로 이동할 때 발생하는 이벤트입니다. `window.location` 객체가 변경되거나 사용자가 페이지의 링크를 클릭했을 때 발생합니다. -이 이벤트는 `webContents.loadURL`과 `webContents.back` 같은 API를 이용하여 +이 이벤트는 `webContents.loadURL`과 `webContents.back` 같은 API를 이용한 프로그램적으로 시작된 탐색에 대해서는 발생하지 않습니다. +이 이벤트는 앵커 링크를 클릭하거나 `window.location.hash`의 값을 변경하는 등의 페이지 +내 탐색시엔 발생하지 않습니다. 대신 `did-navigate-in-page` 이벤트를 사용해야 합니다. + `event.preventDefault()`를 호출하면 탐색을 방지할 수 있습니다. +### Event: 'did-navigate' + +Returns: + +* `event` Event +* `url` String + +탐색이 완료되면 발생하는 이벤트입니다. + +이 이벤트는 앵커 링크를 클릭하거나 `window.location.hash`의 값을 변경하는 등의 페이지 +내 탐색시엔 발생하지 않습니다. 대신 `did-navigate-in-page` 이벤트를 사용해야 합니다. + +### Event: 'did-navigate-in-page' + +Returns: + +* `event` Event +* `url` String + +페이지 내의 탐색이 완료되면 발생하는 이벤트입니다. + +페이지 내의 탐색이 발생하면 페이지 URL이 변경되지만 페이지 밖으로의 탐색은 일어나지 +않습니다. 예를 들어 앵커 링크를 클릭했을 때, 또는 DOM `hashchange` 이벤트가 발생했을 +때로 볼 수 있습니다. + ### Event: 'crashed' 렌더러 프로세스가 예기치 못하게 종료되었을 때 발생되는 이벤트입니다. @@ -165,6 +193,39 @@ Returns: 개발자 도구에 포커스가 가거나 개발자 도구가 열렸을 때 발생되는 이벤트입니다. +### Event: 'certificate-error' + +Returns: + +* `event` Event +* `url` URL +* `error` String - 에러 코드 +* `certificate` Object + * `data` Buffer - PEM 인코딩된 데이터 + * `issuerName` String +* `callback` Function + +`url`에 대한 `certificate` 인증서의 유효성 검증에 실패했을 때 발생하는 이벤트입니다. + +사용법은 [`app`의 `certificate-error` 이벤트](app.md#event-certificate-error)와 +같습니다. + +### Event: 'select-client-certificate' + +Returns: + +* `event` Event +* `url` URL +* `certificateList` [Objects] + * `data` Buffer - PEM 인코딩된 데이터 + * `issuerName` String - 인증서 발급자 이름 +* `callback` Function + +클라이언트 인증이 요청되었을 때 발생하는 이벤트 입니다. + +사용법은 [`app`의 `select-client-certificate` 이벤트](app.md#event-select-client-certificate)와 +같습니다. + ### Event: 'login' Returns: @@ -186,16 +247,41 @@ Returns: [`app`의 `login`이벤트](app.md#event-login)와 사용 방법은 같습니다. +### Event: 'found-in-page' + +Returns: + +* `event` Event +* `result` Object + * `requestId` Integer + * `finalUpdate` Boolean - 더 많은 응답이 따르는 경우를 표시합니다. + * `matches` Integer (Optional) - 일치하는 개수. + * `selectionArea` Object (Optional) - 첫 일치 부위의 좌표. + +[`webContents.findInPage`](web-contents.md#webcontentsfindinpage) 요청의 결과를 +사용할 수 있을 때 발생하는 이벤트입니다. + +### Event: 'media-started-playing' + +미디어가 재생되기 시작할 때 발생하는 이벤트입니다. + +### Event: 'media-paused' + +미디어가 중지되거나 재생이 완료되었을 때 발생하는 이벤트입니다. + +### Event: 'did-change-theme-color' + +페이지의 테마 색이 변경될 때 발생하는 이벤트입니다. 이 이벤트는 보통 meta 태그에 +의해서 발생합니다: + +```html + +``` + ## Instance Methods `webContents`객체는 다음과 같은 인스턴스 메서드들을 가지고 있습니다. -### `webContents.session` - -webContents에서 사용되는 `session`객체를 반환합니다. - -[session 문서](session.md)에서 이 객체의 메서드들을 확인할 수 있습니다. - ### `webContents.loadURL(url[, options])` * `url` URL @@ -213,6 +299,13 @@ const options = {"extraHeaders" : "pragma: no-cache\n"} webContents.loadURL(url, options) ``` +### `webContents.downloadURL(url)` + +* `url` URL + +`url`의 리소스를 탐색 없이 다운로드를 시작합니다. `session`의 `will-download` +이벤트가 발생합니다. + ### `webContents.getURL()` 현재 웹 페이지의 URL을 반환합니다. @@ -319,7 +412,7 @@ CSS 코드를 현재 웹 페이지에 삽입합니다. ### `webContents.setAudioMuted(muted)` -+ `muted` Boolean +* `muted` Boolean 현재 웹 페이지의 소리를 음소거합니다. @@ -375,6 +468,46 @@ CSS 코드를 현재 웹 페이지에 삽입합니다. 웹 페이지에서 `replaceMisspelling` 편집 커맨드를 실행합니다. +### `webContents.findInPage(text[, options])` + +* `text` String - 찾을 컨텐츠, 반드시 공백이 아니여야 합니다. +* `options` Object (Optional) + * `forward` Boolean - 앞에서부터 검색할지 뒤에서부터 검색할지 여부입니다. 기본값은 + `true`입니다. + * `findNext` Boolean - 작업을 계속 처리할지 첫 요청만 처리할지 여부입니다. 기본값은 + `false`입니다. + * `matchCase` Boolean - 검색이 대소문자를 구분할지 여부입니다. 기본값은 + `false`입니다. + * `wordStart` Boolean - 단어의 시작 부분만 볼 지 여부입니다. 기본값은 + `false`입니다. + * `medialCapitalAsWordStart` Boolean - `wordStart`와 합쳐질 때, 소문자 또는 + 비문자가 따라붙은 대문자로 일치가 시작하는 경우 단어 중간의 일치를 허용합니다. + 여러가지 다른 단어 내의 일치를 허용합니다. 기본값은 `false`입니다. + +웹 페이지에서 `text`에 일치하는 모든 대상을 찾는 요청을 시작하고 요청에 사용된 요청을 +표현하는 `정수(integer)`를 반환합니다. 요청의 결과는 +[`found-in-page`](web-contents.md#event-found-in-page) 이벤트를 통해 취득할 수 +있습니다. + +### `webContents.stopFindInPage(action)` + +* `action` String - [`webContents.findInPage`](web-contents.md#webcontentfindinpage) + 요청이 종료되었을 때 일어날 수 있는 작업을 지정합니다. + * `clearSelection` - 선택을 일반 선택으로 변경합니다. + * `keepSelection` - 선택을 취소합니다. + * `activateSelection` - 포커스한 후 선택된 노드를 클릭합니다. + +제공된 `action`에 대한 `webContents`의 모든 `findInPage` 요청을 중지합니다. + +```javascript +webContents.on('found-in-page', function(event, result) { + if (result.finalUpdate) + webContents.stopFindInPage("clearSelection"); +}); + +const requestId = webContents.findInPage("api"); +``` + ### `webContents.hasServiceWorker(callback)` * `callback` Function @@ -417,6 +550,7 @@ print기능을 사용하지 않는 경우 전체 바이너리 크기를 줄이 * 1 - none * 2 - minimum * `pageSize` String - 생성되는 PDF의 페이지 크기를 지정합니다. + * `A5` * `A4` * `A3` * `Legal` @@ -470,7 +604,14 @@ win.webContents.on("did-finish-load", function() { * `path` String -특정 경로를 개발자 도구의 워크스페이스에 추가합니다. +특정 경로를 개발자 도구의 워크스페이스에 추가합니다. 반드시 개발자 도구의 생성이 완료된 +이후에 사용해야 합니다. + +```javascript +mainWindow.webContents.on('devtools-opened', function() { + mainWindow.webContents.addWorkSpace(__dirname); +}); +``` ### `webContents.removeWorkSpace(path)` @@ -499,7 +640,7 @@ win.webContents.on("did-finish-load", function() { ### `webContents.isDevToolsFocused()` -개발자 도구에 포커스가 가있는지 여부를 반화합니다. +개발자 도구에 포커스 되어있는지 여부를 반환합니다. ### `webContents.inspectElement(x, y)` @@ -645,17 +786,6 @@ Input `event`를 웹 페이지로 전송합니다. 프레임 프레젠테이션 이벤트들에 대한 구독을 중지합니다. -## Instance Properties - -`WebContents`객체들은 다음 속성들을 가지고 있습니다: - -### `webContents.devToolsWebContents` - -이 `WebContents`에 대한 개발자 도구의 `WebContents`를 가져옵니다. - -**참고:** 사용자가 절대로 이 객체를 저장해서는 안 됩니다. 개발자 도구가 닫혔을 때, -`null`이 반환될 수 있습니다. - ### `webContents.savePage(fullPath, saveType, callback)` * `fullPath` String - 전체 파일 경로. @@ -678,3 +808,18 @@ win.webContents.on('did-finish-load', function() { }); }); ``` + +## Instance Properties + +`WebContents`객체들은 다음 속성들을 가지고 있습니다: + +### `webContents.session` + +이 webContents에서 사용하는 [session](session.md) 객체를 반환합니다. + +### `webContents.devToolsWebContents` + +이 `WebContents`에 대한 개발자 도구의 `WebContents`를 가져옵니다. + +**참고:** 사용자가 절대로 이 객체를 저장해서는 안 됩니다. 개발자 도구가 닫혔을 때, +`null`이 반환될 수 있습니다. diff --git a/docs-translations/ko-KR/api/web-frame.md b/docs-translations/ko-KR/api/web-frame.md index 2e4a469b5..8181c0f3b 100644 --- a/docs-translations/ko-KR/api/web-frame.md +++ b/docs-translations/ko-KR/api/web-frame.md @@ -70,7 +70,7 @@ webFrame.setSpellCheckProvider("en-US", true, { * `scheme` String -지정한 `scheme`을 보안 스킴으로 등록합니다. +`scheme`을 보안 스킴으로 등록합니다. 보안 스킴은 혼합된 컨텐츠 경고를 발생시키지 않습니다. 예를 들어 `https` 와 `data`는 네트워크 공격자로부터 손상될 가능성이 없기 때문에 보안 스킴이라고 할 수 있습니다. @@ -85,6 +85,7 @@ webFrame.setSpellCheckProvider("en-US", true, { * `scheme` String -보안 `scheme`를 지정합니다. 리소스와 ServiceWorker 설정에 대해 보안 정책을 우회합니다. +`scheme`를 보안된 스킴으로 등록합니다. 리소스에 대해 보안 정책을 우회하며, +ServiceWorker의 등록과 fetch API를 사용할 수 있도록 지원합니다. [spellchecker]: https://github.com/atom/node-spellchecker diff --git a/docs-translations/ko-KR/api/web-view-tag.md b/docs-translations/ko-KR/api/web-view-tag.md index 4ef7f47c8..591721752 100644 --- a/docs-translations/ko-KR/api/web-view-tag.md +++ b/docs-translations/ko-KR/api/web-view-tag.md @@ -158,6 +158,7 @@ API를 사용할 수 있습니다. 이를 지정하면 내부에서 로우레벨 **참고:** 태그 객체의 메서드는 페이지 로드가 끝난 뒤에만 사용할 수 있습니다. **예제** + ```javascript webview.addEventListener("dom-ready", function() { webview.openDevTools(); @@ -276,6 +277,10 @@ webview.addEventListener("dom-ready", function() { 페이지에 대한 개발자 도구가 열려있는지 확인합니다. 불린 값을 반환합니다. +### `.isDevToolsFocused()` + +페이지의 개발자 도구에 포커스 되어있는지 여부를 반화합니다. + ### `.inspectElement(x, y)` * `x` Integer @@ -335,6 +340,37 @@ Service worker에 대한 개발자 도구를 엽니다. 페이지에서 `replaceMisspelling` 커맨드를 실행합니다. +### `webContents.findInPage(text[, options])` + +* `text` String - 찾을 컨텐츠, 반드시 공백이 아니여야 합니다. +* `options` Object (Optional) + * `forward` Boolean - 앞에서부터 검색할지 뒤에서부터 검색할지 여부입니다. 기본값은 + `true`입니다. + * `findNext` Boolean - 작업을 계속 처리할지 첫 요청만 처리할지 여부입니다. 기본값은 + `false`입니다. + * `matchCase` Boolean - 검색이 대소문자를 구분할지 여부입니다. 기본값은 + `false`입니다. + * `wordStart` Boolean - 단어의 시작 부분만 볼 지 여부입니다. 기본값은 + `false`입니다. + * `medialCapitalAsWordStart` Boolean - `wordStart`와 합쳐질 때, 소문자 또는 + 비문자가 따라붙은 대문자로 일치가 시작하는 경우 단어 중간의 일치를 허용합니다. + 여러가지 다른 단어 내의 일치를 허용합니다. 기본값은 `false`입니다. + +웹 페이지에서 `text`에 일치하는 모든 대상을 찾는 요청을 시작하고 요청에 사용된 요청을 +표현하는 `정수(integer)`를 반환합니다. 요청의 결과는 +[`found-in-page`](web-view-tag.md#event-found-in-page) 이벤트를 통해 취득할 수 +있습니다. + +### `webContents.stopFindInPage(action)` + +* `action` String - [`.findInPage`](web-view-tag.md#webviewtagfindinpage) + 요청이 종료되었을 때 일어날 수 있는 작업을 지정합니다. + * `clearSelection` - 선택을 일반 선택으로 변경합니다. + * `keepSelection` - 선택을 취소합니다. + * `activateSelection` - 포커스한 후 선택된 노드를 클릭합니다. + +제공된 `action`에 대한 `webContents`의 모든 `findInPage` 요청을 중지합니다. + ### `.print([options])` Webview 페이지를 인쇄합니다. `webContents.print([options])` 메서드와 같습니다. @@ -441,7 +477,7 @@ Returns: 프레임 문서의 로드가 끝나면 발생하는 이벤트입니다. -### Event: 'page-title-set' +### Event: 'page-title-updated' Returns: @@ -449,7 +485,7 @@ Returns: * `explicitSet` Boolean 탐색하는 동안에 페이지의 제목이 설정되면 발생하는 이벤트입니다. `explicitSet`는 파일 -URL에서 종합(synthesised)된 제목인 경우 false로 표시됩니다. +URL에서 합성(synthesised)된 제목인 경우 false로 표시됩니다. ### Event: 'page-favicon-updated' @@ -487,6 +523,28 @@ webview.addEventListener('console-message', function(e) { }); ``` +### Event: 'found-in-page' + +Returns: + +* `result` Object + * `requestId` Integer + * `finalUpdate` Boolean - 더 많은 응답이 따르는 경우를 표시합니다. + * `matches` Integer (Optional) - 일치하는 개수. + * `selectionArea` Object (Optional) - 첫 일치 부위의 좌표. + +[`webContents.findInPage`](web-contents.md#webcontentsfindinpage) 요청의 결과를 +사용할 수 있을 때 발생하는 이벤트입니다. + +```javascript +webview.addEventListener('found-in-page', function(e) { + if (e.result.finalUpdate) + webview.stopFindInPage("keepSelection"); +}); + +const rquestId = webview.findInPage("test"); +``` + ### Event: 'new-window' Returns: @@ -507,6 +565,46 @@ webview.addEventListener('new-window', function(e) { }); ``` +### Event: 'will-navigate' + +Returns: + +* `url` String + +사용자 또는 페이지가 새로운 페이지로 이동할 때 발생하는 이벤트입니다. +`window.location` 객체가 변경되거나 사용자가 페이지의 링크를 클릭했을 때 발생합니다. + +이 이벤트는 `.loadURL`과 `.back` 같은 API를 이용한 +프로그램적으로 시작된 탐색에 대해서는 발생하지 않습니다. + +이 이벤트는 앵커 링크를 클릭하거나 `window.location.hash`의 값을 변경하는 등의 페이지 +내 탐색시엔 발생하지 않습니다. 대신 `did-navigate-in-page` 이벤트를 사용해야 합니다. + +`event.preventDefault()`를 호출하는 것은 __아무__ 효과도 내지 않습니다. + +### Event: 'did-navigate' + +Returns: + +* `url` String + +탐색이 완료되면 발생하는 이벤트입니다. + +이 이벤트는 앵커 링크를 클릭하거나 `window.location.hash`의 값을 변경하는 등의 페이지 +내 탐색시엔 발생하지 않습니다. 대신 `did-navigate-in-page` 이벤트를 사용해야 합니다. + +### Event: 'did-navigate-in-page' + +Returns: + +* `url` String + +페이지 내의 탐색이 완료되면 발생하는 이벤트입니다. + +페이지 내의 탐색이 발생하면 페이지 URL이 변경되지만 페이지 밖으로의 탐색은 일어나지 +않습니다. 예를 들어 앵커 링크를 클릭했을 때, 또는 DOM `hashchange` 이벤트가 발생했을 +때로 볼 수 있습니다. + ### Event: 'close' 페이지가 자체적으로 닫힐 때 발생하는 이벤트입니다. @@ -569,3 +667,31 @@ Returns: ### Event: 'destroyed' WebContents가 파괴될 때 발생하는 이벤트입니다. + +### Event: 'media-started-playing' + +미디어가 재생되기 시작할 때 발생하는 이벤트입니다. + +### Event: 'media-paused' + +미디어가 중지되거나 재생이 완료되었을 때 발생하는 이벤트입니다. + +### Event: 'did-change-theme-color' + +페이지의 테마 색이 변경될 때 발생하는 이벤트입니다. 이 이벤트는 보통 meta 태그에 +의해서 발생합니다: + +```html + +``` +### Event: 'devtools-opened' + +개발자 도구가 열렸을 때 발생하는 이벤트입니다. + +### Event: 'devtools-closed' + +개발자 도구가 닫혔을 때 발생하는 이벤트입니다. + +### Event: 'devtools-focused' + +개발자 도구가 포커스되거나 열렸을 때 발생하는 이벤트입니다. diff --git a/docs-translations/ko-KR/development/build-instructions-windows.md b/docs-translations/ko-KR/development/build-instructions-windows.md index 13bbf47a3..a5b0de78b 100644 --- a/docs-translations/ko-KR/development/build-instructions-windows.md +++ b/docs-translations/ko-KR/development/build-instructions-windows.md @@ -5,7 +5,7 @@ ## 빌드전 요구 사항 * Windows 7 / Server 2008 R2 또는 최신 버전 -* Visual Studio 2013 - [VS 2013 커뮤니티 에디션 무료 다운로드](http://www.visualstudio.com/products/visual-studio-community-vs) +* Visual Studio 2013 Update 5 - [VS 2013 커뮤니티 에디션 무료 다운로드](http://www.visualstudio.com/products/visual-studio-community-vs) * [Python 2.7](http://www.python.org/download/releases/2.7/) * [Node.js](http://nodejs.org/download/) * [Git](http://git-scm.com) diff --git a/docs-translations/ko-KR/development/build-system-overview.md b/docs-translations/ko-KR/development/build-system-overview.md index 71002ff58..79ead3271 100644 --- a/docs-translations/ko-KR/development/build-system-overview.md +++ b/docs-translations/ko-KR/development/build-system-overview.md @@ -48,7 +48,7 @@ $ ./script/bootstrap.py --dev $ ./script/build.py -c D ``` -## 프로젝트 생성 (two-phrase) +## 두 절차에 따른 프로젝트 생성 Electron은 `Release`와 `Debug` 빌드가 서로 다른 라이브러리 링크 방식을 사용합니다. 하지만 `gyp`는 따로 빌드 설정을 분리하여 라이브러리 링크 방식을 정의하는 방법을 diff --git a/docs-translations/ko-KR/styleguide.md b/docs-translations/ko-KR/styleguide.md index dd02629c1..8327cd3c1 100644 --- a/docs-translations/ko-KR/styleguide.md +++ b/docs-translations/ko-KR/styleguide.md @@ -57,7 +57,7 @@ Electron 문서 구조를 이해하는 데 참고할 수 있는 유용한 도움 `methodName(required[, optional]))` -* `require` String, **필수** +* `require` String (**required**) * `optional` Integer --- diff --git a/docs-translations/ko-KR/tutorial/application-packaging.md b/docs-translations/ko-KR/tutorial/application-packaging.md index 7a9141976..9183f0eaf 100644 --- a/docs-translations/ko-KR/tutorial/application-packaging.md +++ b/docs-translations/ko-KR/tutorial/application-packaging.md @@ -103,6 +103,14 @@ var originalFs = require('original-fs'); originalFs.readFileSync('/path/to/example.asar'); ``` +또한 `process.noAsar`를 `true`로 지정하면 `fs` 모듈의 `asar` 지원을 비활성화 시킬 수 +있습니다. + +```javascript +process.noAsar = true; +fs.readFileSync('/path/to/example.asar'); +``` + ## Node API의 한계 `asar` 아카이브를 Node API가 최대한 디렉터리 구조로 작동하도록 노력해왔지만 여전히 @@ -129,16 +137,29 @@ API가 원할하게 작동할 수 있도록 임시 경로에 해당되는 파일 위 예외에 해당하는 API 메서드는 다음과 같습니다: * `child_process.execFile` +* `child_process.execFileSync` * `fs.open` * `fs.openSync` * `process.dlopen` - Used by `require` on native modules -### `fs.stat`의 잘못된 스테이터스 정보 +### `fs.stat`의 모조된(예측) 스테이터스 정보 `fs.stat` 로부터 반환되는 `Stats` 객체와 비슷한 API들은 `asar` 아카이브를 타겟으로 할 경우 예측된 디렉터리 파일 정보를 가집니다. 왜냐하면 아카이브의 디렉터리 경로는 실제 -파일 시스템에 존재하지 않기 때문입니다. 그러한 이유로 파일 크기와 -파일 타입 등을 확인할 때 `Stats` 객체를 신뢰해선 안됩니다. +파일 시스템에 존재하지 않기 때문입니다. 그러한 이유로 파일 크기와 파일 타입 등을 확인할 +때 `Stats` 객체를 신뢰해선 안됩니다. + +### `asar` 아카이브 내부의 바이너리 실행 + +Node API에는 `child_process.exec`, `child_process.spawn` 그리고 +`child_process.execFile`와 같은 바이너리를 실행시킬 수 있는 API가 있습니다. 하지만 +`asar` 아카이브 내에선 `execFile` API만 사용할 수 있습니다. + +이 한계가 존재하는 이유는 `exec`와 `spawn`은 `file` 대신 `command`를 인수로 허용하고 +있고 `command`는 shell에서 작동하기 때문입니다. Electron은 어떤 커맨드가 `asar` +아카이브 내의 파일을 사용하는지 결정하는데 적절한 방법을 가지고 있지 않으며, 심지어 +그게 가능하다고 해도 부작용 없이 명령 경로를 대체할 수 있는지에 대해 확실히 알 수 있는 +방법이 없습니다. ## `asar` 아카이브에 미리 압축 해제된 파일 추가하기 diff --git a/docs-translations/ko-KR/tutorial/debugging-main-process.md b/docs-translations/ko-KR/tutorial/debugging-main-process.md index 32a6bd00f..ee7e8432d 100644 --- a/docs-translations/ko-KR/tutorial/debugging-main-process.md +++ b/docs-translations/ko-KR/tutorial/debugging-main-process.md @@ -19,9 +19,9 @@ ## node-inspector로 디버깅 하기 -__참고:__ Electron은 node v0.11.13 버전을 사용합니다. 그리고 현재 node-inspector -유틸리티와 호환성 문제가 있습니다. 추가로 node-inspector 콘솔 내에서 메인 프로세스의 -`process` 객체를 탐색할 경우 크래시가 발생할 수 있습니다. +__참고:__ Electron은 현재 node-inspector 유틸리티와 호환성 문제가 있습니다. 따라서 +node-inspector 콘솔 내에서 메인 프로세스의 `process` 객체를 탐색할 경우 크래시가 +발생할 수 있습니다. ### 1. [node-inspector][node-inspector] 서버 시작 diff --git a/docs-translations/ko-KR/tutorial/desktop-environment-integration.md b/docs-translations/ko-KR/tutorial/desktop-environment-integration.md index 1dc5f6613..d2e50beb6 100644 --- a/docs-translations/ko-KR/tutorial/desktop-environment-integration.md +++ b/docs-translations/ko-KR/tutorial/desktop-environment-integration.md @@ -24,7 +24,7 @@ myNotification.onclick = function () { } ``` -위 코드를 통해 생성한 데스크톱 알림은 각 운영체제 모두 비슷한 사용자 경험을 제공합니다. +위 코드를 통해 생성한 데스크톱 알림은 각 운영체제 모두 비슷한 사용자 경험을 제공하지만, 하지만 몇 가지 다른 점들이 있습니다. ### Windows @@ -47,7 +47,7 @@ new Notification('Title', { }); ``` -또한 `body`의 최대 길이는 250자 입니다. Windows 개발팀에선 알림 문자열을 200자 이하로 +또한 본문의 최대 길이는 250자 입니다. Windows 개발팀에선 알림 문자열을 200자 이하로 유지하는 것을 권장합니다. ### Linux @@ -67,8 +67,8 @@ OS X에서의 데스크톱 알림은 아주 직관적입니다. 하지만 데스 ## 최근 사용한 문서 (Windows & OS X) -알다 싶이 Windows와 OS X는 JumpList 또는 dock 메뉴를 통해 최근 문서 리스트에 쉽게 -접근할 수 있습니다. +Windows와 OS X는 JumpList 또는 dock 메뉴를 통해 최근 문서 리스트에 쉽게 접근할 수 +있습니다. __JumpList:__ diff --git a/docs-translations/ko-KR/tutorial/mac-app-store-submission-guide.md b/docs-translations/ko-KR/tutorial/mac-app-store-submission-guide.md index 16a74d03e..d316d16da 100644 --- a/docs-translations/ko-KR/tutorial/mac-app-store-submission-guide.md +++ b/docs-translations/ko-KR/tutorial/mac-app-store-submission-guide.md @@ -4,6 +4,10 @@ Electron은 v0.34.0 버전부터 앱 패키지를 Mac App Store(MAS)에 제출 되었습니다. 이 가이드는 어플리케이션을 앱 스토어에 등록하는 방법과 빌드의 한계에 대한 설명을 제공합니다. +__참고:__ Mac App Store에 어플리케이션을 등록하려면 +[Apple Developer Program][developer-program]에 등록되어 있어야 하며 비용이 발생할 +수 있습니다. + ## 앱 스토어에 어플리케이션을 등록하는 방법 다음 몇 가지 간단한 절차에 따라 앱 스토어에 어플리케이션을 등록하는 방법을 알아봅니다. @@ -109,6 +113,7 @@ productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$RES **역주:** [Mac 앱 배포 가이드 공식 문서](https://developer.apple.com/osx/distribution/kr/) +[developer-program]: https://developer.apple.com/support/compare-memberships/ [submitting-your-app]: https://developer.apple.com/library/mac/documentation/IDEs/Conceptual/AppDistributionGuide/SubmittingYourApp/SubmittingYourApp.html [nwjs-guide]: https://github.com/nwjs/nw.js/wiki/Mac-App-Store-%28MAS%29-Submission-Guideline#first-steps [enable-app-sandbox]: https://developer.apple.com/library/ios/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html diff --git a/docs-translations/ko-KR/tutorial/quick-start.md b/docs-translations/ko-KR/tutorial/quick-start.md index 8946f4471..708a48080 100644 --- a/docs-translations/ko-KR/tutorial/quick-start.md +++ b/docs-translations/ko-KR/tutorial/quick-start.md @@ -70,17 +70,16 @@ your-app/ __알림__: 만약 `main` 필드가 `package.json`에 설정되어 있지 않으면 Electron은 자동으로 같은 디렉터리의 `index.js`를 로드합니다. -반드시 `main.js`에서 창을 만들고 시스템 이밴트를 처리해야합니다. 대표적인 예제로 +반드시 `main.js`에서 창을 만들고 시스템 이벤트를 처리해야 합니다. 대표적인 예제로 다음과 같이 작성할 수 있습니다: ```javascript +'use strict'; + const electron = require('electron'); const app = electron.app; // 어플리케이션 기반을 조작 하는 모듈. const BrowserWindow = electron.BrowserWindow; // 네이티브 브라우저 창을 만드는 모듈. -// Electron 개발자에게 crash-report를 보냄. -electron.crashReporter.start(); - // 윈도우 객체를 전역에 유지합니다. 만약 이렇게 하지 않으면 // 자바스크립트 GC가 일어날 때 창이 멋대로 닫혀버립니다. var mainWindow = null; diff --git a/docs-translations/ko-KR/tutorial/using-widevine-cdm-plugin.md b/docs-translations/ko-KR/tutorial/using-widevine-cdm-plugin.md new file mode 100644 index 000000000..d729c0efc --- /dev/null +++ b/docs-translations/ko-KR/tutorial/using-widevine-cdm-plugin.md @@ -0,0 +1,78 @@ +# Widevine CDM 플러그인 사용하기 + +Electron에선 Chrome 브라우저에서 취득해온 Widevine CDM 플러그인을 사용할 수 있습니다. + +## 플러그인 취득 + +Electron은 라이센스상의 문제로 Widevine CDM 플러그인을 직접 제공하지 않습니다. +따라서 플러그인을 얻으려면 먼저 사용할 Electron 빌드의 아키텍쳐와 버전에 맞춰 공식 +Chrome 브라우저를 설치해야 합니다. + +### Windows & OS X + +Chrome 브라우저에서 `chrome://components/`를 열고 `WidevineCdm`을 찾은 후 확실히 +최신버전인지 확인합니다. 여기까지 하면 모든 플러그인 바이너리를 +`APP_DATA/Google/Chrome/WidevineCDM/VERSION/_platform_specific/PLATFORM_ARCH/` +디렉터리에서 찾을 수 있습니다. + +`APP_DATA`는 어플리케이션 데이터를 저장하고 있는 시스템 경로입니다. Windows에선 +`%LOCALAPPDATA%`로 접근할 수 있고 OS X에선 `~/Library/Application Support`로 +접근할 수 있습니다. `VERSION`은 `1.4.8.866` 같은 Widevine CDM 플러그인의 버전 +문자열입니다. `PLATFORM`은 플랫폼을 뜻하며 `mac` 또는 `win`이 될 수 있으며 `ARCH`는 +아키텍쳐를 뜻하고 `x86` 또는 `x64`가 될 수 있습니다. + +Windows에선 `widevinecdm.dll` 와 `widevinecdmadapter.dll` 같은 바이너리를 +요구하며 OS X에선 `libwidevinecdm.dylib`와 `widevinecdmadapter.plugin` 바이너리를 +요구합니다. 원하는 곳에 이들을 복사해 놓을 수 있습니다. 하지만 반드시 바이너리는 같은 +위치에 두어야 합니다. + +### Linux + +Linux에선 플러그인 바이너리들이 Chrome 브라우저와 함께 제공됩니다. +`/opt/google/chrome` 경로에서 찾을 수 있으며, 파일 이름은 `libwidevinecdm.so`와 +`libwidevinecdmadapter.so`입니다. + +## 플러그인 사용 + +플러그인 파일을 가져온 후, Electron의 `--widevine-cdm-path` 커맨드 라인 스위치에 +`widevinecdmadapter`의 위치를 전달하고 플러그인의 버전을 `--widevine-cdm-version` +스위치에 전달해야 합니다. + +__참고:__ `widevinecdmadapter` 바이너리가 Electron으로 전달되어도, `widevinecdm` +바이너리는 옆에 같이 두어야 합니다. + +커맨드 라인 스위치들은 `app` 모듈의 `ready` 이벤트가 발생하기 전에 전달되어야 합니다. +그리고 이 플러그인을 사용하는 페이지는 플러그인(속성)이 활성화되어있어야 합니다. + +예시 코드: + +```javascript +// `widevinecdmadapter`의 파일 이름을 이곳에 전달해야 합니다. 파일 이름은 +// * OS X에선 `widevinecdmadapter.plugin`로 지정합니다, +// * Linux에선 `libwidevinecdmadapter.so`로 지정합니다, +// * Windows에선 `widevinecdmadapter.dll`로 지정합니다. +app.commandLine.appendSwitch('widevine-cdm-path', '/path/to/widevinecdmadapter.plugin'); +// 플러그인의 버전은 크롬의 `chrome://plugins` 페이지에서 취득할 수 있습니다. +app.commandLine.appendSwitch('widevine-cdm-version', '1.4.8.866'); + +var mainWindow = null; +app.on('ready', function() { + mainWindow = new BrowserWindow({ + webPreferences: { + // `plugins`은 활성화되어야 합니다. + plugins: true + } + }) +}); +``` + +## 플러그인 작동 확인 + +플러그인 정상적으로 작동하는지 확인하려면 다음과 같은 방법을 사용할 수 있습니다: + +* 개발자 도구를 연 후 `navigator.plugins`를 확인하여 Widevine CDM 플러그인이 + 포함되었는지 알 수 있습니다. +* https://shaka-player-demo.appspot.com/를 열어, `Widevine`을 사용하는 설정을 + 로드합니다. +* http://www.dash-player.com/demo/drm-test-area/를 열어, 페이지에서 + `bitdash uses Widevine in your browser`라고 적혀있는지 확인하고 비디오를 재생합니다. diff --git a/docs-translations/pt-BR/README.md b/docs-translations/pt-BR/README.md index db88edaf7..f458e51da 100644 --- a/docs-translations/pt-BR/README.md +++ b/docs-translations/pt-BR/README.md @@ -1,71 +1,82 @@ +Por favor, certifique-se de que está utilizando a documentação que corresponde à sua versão do Electron. +O número da versão deve ser uma parte da URL da página. Se não for, você provavelmente está utilizando +a documentação de um branch de desenvolvimento que pode conter mudanças na API que não são compatíveis +com a sua versão do Electron. Se este for o caso, você pode mudar para uma versão diferente da +documentação na lista de [versões disponíveis](http://electron.atom.io/docs/) em atom.io, +ou se você estiver usando a interface do GitHub, abra o *dropdown* "Switch branches/tags" e +selecione a *tag* que corresponde à sua versão. + ## Guias +* [Platformas Suportadas](../../tutorial/supported-platforms.md) * [Distribuição de Aplicações](tutorial/application-distribution.md) -* [Empacotamento da aplicação](tutorial/application-packaging.md) -* [Usando módulos nativos](tutorial/using-native-node-modules.md) -* [Depuração do processo principal](tutorial/debugging-main-process.md) +* [Guia de Submissão da Mac App Store](../../tutorial/mac-app-store-submission-guide.md) +* [Empacotamento da Aplicação](tutorial/application-packaging.md) +* [Usando Módulos Nativos do Node](tutorial/using-native-node-modules.md) +* [Depuração do Processo Principal](tutorial/debugging-main-process.md) * [Usando Selenium e WebDriver](../../docs/tutorial/using-selenium-and-webdriver.md) * [Extensão DevTools](../../docs/tutorial/devtools-extension.md) -* [Usando o plugin papper flash](tutorial/using-pepper-flash-plugin.md) +* [Usando o Plugin Pepper Flash](tutorial/using-pepper-flash-plugin.md) ## Tutoriais * [Introdução](tutorial/quick-start.md) -* [A integração com o ambiente de desenvolvimento](tutorial/desktop-environment-integration.md) -* [Evento de detecção on-line/off-line](tutorial/online-offline-events.md) +* [Integração com o Ambiente de Desenvolvimento](tutorial/desktop-environment-integration.md) +* [Evento de Detecção Online/Offline](tutorial/online-offline-events.md) -## API - Referencias +## API - Referências * [Sinopse](../../docs/api/synopsis.md) * [Processos](api/process.md) * [Aceleradores (Teclas de Atalho)](api/accelerator.md) * [Parâmetros CLI suportados (Chrome)](../../docs/api/chrome-command-line-switches.md) +* [Variáveis de Ambiente](../../docs/api/environment-variables.md) -DOM elementos personalizados: +### Elementos DOM Personalizados: * [Objeto `File`](../../docs/api/file-object.md) * [Tag ``](../../docs/api/web-view-tag.md) * [Função `window.open`](../../docs/api/window-open.md) -Os principais módulos: +### Módulos para o Processo Principal: -* [app](../../docs/api/app.md) -* [auto-updater](../../docs/api/auto-updater.md) -* [browser-window](../../docs/api/browser-window.md) -* [content-tracing](../../docs/api/content-tracing.md) +* [app](api/app.md) +* [autoUpdater](api/auto-updater.md) +* [BrowserWindow](../../docs/api/browser-window.md) +* [contentTracing](../../docs/api/content-tracing.md) * [dialog](../../docs/api/dialog.md) -* [global-shortcut](../../docs/api/global-shortcut.md) -* [ipc (main process)](../../docs/api/ipc-main-process.md) -* [menu](../../docs/api/menu.md) -* [menu-item](../../docs/api/menu-item.md) -* [power-monitor](../../docs/api/power-monitor.md) -* [power-save-blocker](../../docs/api/power-save-blocker.md) +* [globalShortcut](../../docs/api/global-shortcut.md) +* [ipcMain](../../docs/api/ipc-main-process.md) +* [Menu](../../docs/api/menu.md) +* [MenuItem](../../docs/api/menu-item.md) +* [powerMonitor](../../docs/api/power-monitor.md) +* [powerSaveBlocker](../../docs/api/power-save-blocker.md) * [protocol](../../docs/api/protocol.md) * [session](../../docs/api/session.md) * [webContents](../../docs/api/web-contents.md) -* [tray](../../docs/api/tray.md) +* [Tray](../../docs/api/tray.md) -Módulos do renderizador (web page): +### Módulos para o Processo Renderizador: -* [ipc (renderer)](../../docs/api/ipc-renderer.md) +* [ipcRenderer](../../docs/api/ipc-renderer.md) * [remote](../../docs/api/remote.md) -* [web-frame](../../docs/api/web-frame.md) +* [webFrame](../../docs/api/web-frame.md) -Módulos de ambos os processos: +### Módulos para ambos os processos: * [clipboard](../../docs/api/clipboard.md) -* [crash-reporter](../../docs/api/crash-reporter.md) -* [native-image](../../docs/api/native-image.md) +* [crashReporter](../../docs/api/crash-reporter.md) +* [nativeImage](../../docs/api/native-image.md) * [screen](../../docs/api/screen.md) * [shell](api/shell.md) ## Desenvolvimento -* [Estilo de código](../../docs/development/coding-style.md) -* [Estrutura de diretórios padrão](../../docs/development/source-code-directory-structure.md) -* [Diferenças técnicas do NW.js (antigo node-webkit)](../../docs/development/atom-shell-vs-node-webkit.md) -* [Visão geral do build](../../docs/development/build-system-overview.md) -* [Instrução de build (Mac)](../../docs/development/build-instructions-osx.md) -* [Instrução de build (Windows)](../../docs/development/build-instructions-windows.md) -* [Instrução de build (Linux)](../../docs/development/build-instructions-linux.md) -* [Configurando um symbol server no debugger](../../docs/development/setting-up-symbol-server.md) +* [Estilo de Código](development/coding-style.md) +* [Estrutura de Diretórios de Código Fonte](../../docs/development/source-code-directory-structure.md) +* [Diferenças Técnicas do NW.js (antigo node-webkit)](../../docs/development/atom-shell-vs-node-webkit.md) +* [Visão Geral do Build](../../docs/development/build-system-overview.md) +* [Instrução de Build (Mac)](../../docs/development/build-instructions-osx.md) +* [Instrução de Build (Windows)](../../docs/development/build-instructions-windows.md) +* [Instrução de Build (Linux)](../../docs/development/build-instructions-linux.md) +* [Configurando um Symbol Server no Debugger](../../docs/development/setting-up-symbol-server.md) diff --git a/docs-translations/pt-BR/api/accelerator.md b/docs-translations/pt-BR/api/accelerator.md index 87987258b..92d70c844 100644 --- a/docs-translations/pt-BR/api/accelerator.md +++ b/docs-translations/pt-BR/api/accelerator.md @@ -1,7 +1,7 @@ # Acelerador (teclas de atalhos) -Um acelerador é uma string que representa um atalho de tecla. Isso pode conter -multiplos modificadores e códigos chaves, combinado pelo caracter `+`. +Um acelerador é uma string que representa um atalho de tecla. Ele pode conter +múltiplos modificadores e códigos chaves, combinados pelo caractere `+`. Exemplos: @@ -11,13 +11,13 @@ Exemplos: ## Aviso sobre plataformas No Linux e no Windows a tecla `Command` não tem nenhum efeito, -então use `CommandOrControl` que representa a tecla `Command` existente no OSX e +então use `CommandOrControl` que representa a tecla `Command` existente no OS X e `Control` no Linux e no Windows para definir aceleradores (atalhos). A chave `Super` está mapeada para a tecla `Windows` para Windows e Linux, -e para a tecla `Cmd` para OSX. +e para a tecla `Cmd` para OS X. -## Modificadores disponiveis +## Modificadores disponíveis * `Command` (ou `Cmd` abreviado) * `Control` (ou `Ctrl` abreviado) @@ -26,21 +26,21 @@ e para a tecla `Cmd` para OSX. * `Shift` * `Super` -## Códigos chaves disponiveis +## Códigos chaves disponíveis -* `0` to `9` -* `A` to `Z` -* `F1` to `F24` -* Punctuations like `~`, `!`, `@`, `#`, `$`, etc. +* `0` até `9` +* `A` até `Z` +* `F1` até `F24` +* Pontuações como `~`, `!`, `@`, `#`, `$`, etc. * `Plus` * `Space` * `Backspace` * `Delete` * `Insert` -* `Return` (or `Enter` as alias) -* `Up`, `Down`, `Left` and `Right` -* `Home` and `End` -* `PageUp` and `PageDown` -* `Escape` (or `Esc` for short) -* `VolumeUp`, `VolumeDown` and `VolumeMute` -* `MediaNextTrack`, `MediaPreviousTrack`, `MediaStop` and `MediaPlayPause` \ No newline at end of file +* `Return` (ou `Enter` como pseudônimo) +* `Up`, `Down`, `Left` e `Right` +* `Home` e `End` +* `PageUp` e `PageDown` +* `Escape` (ou `Esc` abreviado) +* `VolumeUp`, `VolumeDown` e `VolumeMute` +* `MediaNextTrack`, `MediaPreviousTrack`, `MediaStop` e `MediaPlayPause` diff --git a/docs-translations/pt-BR/api/app.md b/docs-translations/pt-BR/api/app.md new file mode 100644 index 000000000..85cfaade5 --- /dev/null +++ b/docs-translations/pt-BR/api/app.md @@ -0,0 +1,452 @@ +# app + +O módulo `app` é responsável por controlar o ciclo de vida do aplicativo. + +O exemplo a seguir mostra como fechar o aplicativo quando a última janela é fechada: + +```javascript +const app = require('electron').app; +app.on('window-all-closed', function() { + app.quit(); +}); +``` + +## Eventos + +O objeto `app` emite os seguintes eventos: + +### Evento: 'will-finish-launching' + +Emitido quando o aplicativo finaliza a inicialização básica. No Windows e no Linux, +o evento `will-finish-launching` é o mesmo que o evento `ready`; No OS X, +esse evento representa a notificação `applicationWillFinishLaunching` do `NSApplication`. +Normalmente aqui seriam criados *listeners* para os eventos `open-file` e `open-url`, e inicializar o *crash reporter* e atualizador automático. + +Na maioria dos casos, você deve fazer tudo no manipulador de eventos do `ready`. + +### Evento: 'ready' + +Emitido quando o Electron finaliza a inicialização. + +### Evento: 'window-all-closed' + +Emitido quando todas as janelas forem fechadas. + +Este evento só é emitido quando o aplicativo não for fechar. Se o +usuário pressionou`Cmd + Q`, ou o desenvolvedor chamou `app.quit()`, +o Electron tentará primeiro fechar todas as janelas e então emitir o +evento `will-quit`, e neste caso o evento `window-all-closed` não +seria emitido. + +### Evento: 'before-quit' + +Retorna: + +* `event` Event + +Emitido antes que o aplicativo comece a fechar suas janelas. +Chamar `event.preventDefault()` irá impedir o comportamento padrão, +que é terminar o aplicativo. + +### Evento: 'will-quit' + +Retorna: + +* `event` Event + +Emitido quando todas as janelas foram fechadas e o aplicativo irá finalizar. +Chamar `event.preventDefault()` irá impedir o comportamento padrão, +que é terminar o aplicativo. + +Veja a descrição do evento `window-all-closed` para as diferenças entre o +evento `will-quit` e `window-all-closed`. + +### Evento: 'quit' + +Emitido quando o aplicativo está finalizando. + +### Evento: 'open-file' _OS X_ + +Retorna: + +* `event` Event +* `path` String + +Emitido quando o usuário deseja abrir um arquivo com o aplicativo. O evento +`open-file` normalmente é emitido quando o aplicativo já está aberto e o S.O. +quer reutilizar o aplicativo para abrir o arquivo. `open-file` também é emitido +quando um arquivo é jogado no *dock* e o aplicativo ainda não está rodando. +Certifique-se de utilizar um *listener* para o evento `open-file` cedo na +inicialização do seu aplicativo para cuidar deste caso (antes mesmo do evento +`ready` ser emitido). + +Você deve chamar `event.preventDefault()` se quiser cuidar deste caso. + +No Windows, você deve fazer o *parse* do `process.argv` para pegar o +endereço do arquivo. + +### Evento: 'open-url' _OS X_ + +Retorna: + +* `event` Event +* `url` String + +Emitido quando o usuário deseja abrir uma URL com o aplicativo. O esquema deve +ser registrado para ser aberto pelo seu aplicativo. + +Você deve chamar `event.preventDefault()` se quiser cuidar deste caso. + +### Evento: 'activate' _OS X_ + +Retorna: + +* `event` Event +* `hasVisibleWindows` Boolean + +Emitido quando o aplicativo é ativado, que normalmente acontece quando o ícone +do aplicativo no *dock* é clicado. + +### Evento: 'browser-window-blur' + +Retorna: + +* `event` Event +* `window` BrowserWindow + +Emitido quando uma [browserWindow](../../../docs/api/browser-window.md) fica embaçada. + +### Evento: 'browser-window-focus' + +Retorna: + +* `event` Event +* `window` BrowserWindow + +Emitido quando uma [browserWindow](../../../docs/api/browser-window.md) é focada. + +### Evento: 'browser-window-created' + +Retorna: + +* `event` Event +* `window` BrowserWindow + +Emitido quando uma nova [browserWindow](../../../docs/api/browser-window.md) é criada. + +### Evento: 'certificate-error' + +Returns: + +* `event` Event +* `webContents` [WebContents](../../../docs/api/web-contents.md) +* `url` URL +* `error` String - O código de erro +* `certificate` Object + * `data` Buffer - dados codificados PEM + * `issuerName` String +* `callback` Function + +Emitido quando há uma falha na verificação do `certificate` para a `url`, +para confiar no certificado, você deve impedir o comportamento padrão com +`event.preventDefault()` e chamar `callback(true)`. + +```javascript +session.on('certificate-error', function(event, webContents, url, error, certificate, callback) { + if (url == "https://github.com") { + // Lógica de verificação. + event.preventDefault(); + callback(true); + } else { + callback(false); + } +}); +``` + +### Evento: 'select-client-certificate' + +Retorna: + +* `event` Event +* `webContents` [WebContents](../../../docs/api/web-contents.md) +* `url` URL +* `certificateList` [Objects] + * `data` Buffer - dados codificados PEM + * `issuerName` String - Nome Comum do Emissor +* `callback` Function + +Emitido quando um certificado de cliente é requisitado. + +A `url` corresponde à entrada de navegação requisitando o certificado do +cliente e `callback` precisa ser chamada com uma entrada filtrada da lista. +Usar `event.preventDefault()` impede o aplicativo de usar o primeiro certificado +da memória. + +```javascript +app.on('select-client-certificate', function(event, webContents, url, list, callback) { + event.preventDefault(); + callback(list[0]); +}) +``` + +### Evento: 'login' + +Retorna: + +* `event` Event +* `webContents` [WebContents](../../../docs/api/web-contents.md) +* `request` Object + * `method` String + * `url` URL + * `referrer` URL +* `authInfo` Object + * `isProxy` Boolean + * `scheme` String + * `host` String + * `port` Integer + * `realm` String +* `callback` Function + +Emitido quando `webContents` deseja fazer autenticação básica. + +O comportamento padrão é cancelar todas as autenticações, para sobrescrever +isto, você deve impedir esse comportamento com `event.preventDefault()` e +chamar `callback(username, password)` com as credenciais. + +```javascript +app.on('login', function(event, webContents, request, authInfo, callback) { + event.preventDefault(); + callback('username', 'secret'); +}) +``` + +### Evento: 'gpu-process-crashed' + +Emitido quando o processo da gpu falha. + +## Métodos + +O objeto `app` possui os seguintes métodos: + +**Nota:** Alguns métodos só estão disponíveis em sistemas operacionais específicos e estão rotulados como tal. + +### `app.quit()` + +Tente fechar todas as janelas. O evento `before-quit` será emitido primeiro. Se todas +as janelas fecharem com sucesso, o evento `will-quit` será emitido e por padrão o +aplicativo irá terminar. + +Este método garante que todos os manipuladores de evento `beforeunload` e `unload` +sejam corretamente executados. É possível que uma janela cancele o processo de +encerramento ao retornar `false` no manipulador de evento `beforeunload`. + +### `app.exit(exitCode)` + +* `exitCode` Integer + +Finaliza imediatamente com `exitCode`. + +Todas as janelas serão fechadas imediatamente sem perguntar ao usuário, e os eventos +`before-quit` e `will-quit` não serão emitidos. + +### `app.getAppPath()` + +Retorna o atual diretório do aplicativo. + +### `app.getPath(name)` + +* `name` String + +Retorna um endereço para um diretório especial ou arquivo associado com `nome`. +Numa falha um `Error` é lançado. + +Você pode requisitar os seguintes endereços pelo nome: + +* `home` Diretório *home* do usuário. +* `appData` Diretório de dados do aplicativo por usuário, que por padrão aponta para: + * `%APPDATA%` no Windows + * `$XDG_CONFIG_HOME` ou `~/.config` no Linux + * `~/Library/Application Support` no OS X +* `userData` O diretório para guardar os arquivos de configuração do seu aplicativo, que por padrão é o diretório `appData` concatenado com o nome do seu aplicativo. +* `temp` Diretório temporário. +* `exe` O arquivo executável atual. +* `module` A biblioteca `libchromiumcontent`. +* `desktop` O diretório *Desktop* do usuário atual. +* `documents` Diretório "Meus Documentos" do usuário. +* `downloads` Diretório dos downloads do usuário. +* `music` Diretório de músicas do usuário. +* `pictures` Diretório de imagens do usuário. +* `videos` Diretório de vídeos do usuário. + +### `app.setPath(name, path)` + +* `name` String +* `path` String + +Sobrescreve o `path` para um diretório especial ou arquivo associado com `name`. +Se o endereço especifica um diretório não existente, o diretório será criado por +este método. Numa falha um `Error` é lançado. + +Você pode sobrescrever apenas endereços com um `name` definido em `app.getPath`. + +Por padrão, *cookies* e *caches* de páginas web serão guardadas no diretório `userData`. Se você quiser mudar esta localização, você deve sobrescrever o +endereço `userData` antes que o evento `ready` do módulo `app` seja emitido. + +### `app.getVersion()` + +Retorna a versão do aplicativo carregado. Se nenhuma versão for encontrada no +arquivo `package.json` do aplicativo, a versão do pacote ou executável atual é +retornada. + +### `app.getName()` + +Retorna o nome do aplicativo atual, que é o nome no arquivo `package.json` do +aplicativo. + +Normalmente o campo `name` do `package.json` é um nome curto em letras minúsculas, +de acordo com as especificações de módulos npm. Normalmente você deve também +especificar um campo `productName`, que é o nome completo em letras maiúsculas do +seu aplicativo, e que será preferido ao `name` pelo Electron. + +### `app.getLocale()` + +Retorna a localidade atual do aplicativo. + +### `app.addRecentDocument(path)` _OS X_ _Windows_ + +* `path` String + +Adiciona `path` à lista de documentos recentes. + +Esta lista é gerenciada pelo S.O.. No Windows você pode visitar a lista pela +barra de tarefas, e no OS X você pode visita-la pelo *dock*. + +### `app.clearRecentDocuments()` _OS X_ _Windows_ + +Limpa a lista de documentos recentes. + +### `app.setUserTasks(tasks)` _Windows_ + +* `tasks` Array - Vetor de objetos `Task` + +Adiciona `tasks` à categoria [Tasks][tasks] do JumpList no Windows. + +`tasks` é um vetor de objetos `Task` no seguinte formato: + +`Task` Object +* `program` String - Endereço do programa a ser executado, normalmente você deve especificar `process.execPath` que abre o programa atual. +* `arguments` String - Os argumentos de linha de comando quando `program` é executado. +* `title` String - A string a ser exibida em uma JumpList. +* `description` String - Descrição desta *task*. +* `iconPath` String - O endereço absoluto para um ícone a ser exibido em uma JumpList, que pode ser um arquivo arbitrário que contém um ícone. Normalmente você pode especificar `process.execPath` para mostrar o ícone do programa. +* `iconIndex` Integer - O índice do ícone do arquivo do icone. Se um arquivo de ícone consiste de dois ou mais ícones, defina este valor para identificar o ícone. Se o arquivo de ícone consiste de um ícone apenas, este valor é 0. + +### `app.allowNTLMCredentialsForAllDomains(allow)` + +* `allow` Boolean + +Define dinamicamente se sempre envia credenciais para HTTP NTLM ou autenticação *Negotiate* - normalmente, o Electron irá mandar apenas credenciais NTLM/Kerberos para URLs que se enquadram em sites "Intranet Local" (estão no mesmo domínio que você). +Entretanto, esta detecção frequentemente falha quando redes corporativas são mal configuradas, então isso permite optar por esse comportamento e habilitá-lo para todas as URLs. + +### `app.makeSingleInstance(callback)` + +* `callback` Function + +Este método faz da sua aplicação uma Aplicação de Instância Única - invés de permitir múltiplas instâncias do seu aplicativo rodarem, isto irá assegurar que apenas uma única instância do seu aplicativo rodará, e outras instâncias sinalizam esta instância e finalizam. + +`callback` será chamado com `callback(argv, workingDirectory)` quando uma segunda instância tenha sido executada. `argv` é um vetor de argumentos de linha de comando da segunda instância, e `workingDirectory` é o atual endereço de seu diretório. +Normalmente aplicativos respondem à isso não minimizando sua janela primária e dando foco à ela. + +É garantida a execução do `callback` após o evento `ready` do `app` ser emitido. + +Este método retorna `false` caso seu processo seja a instância primária do aplicativo e seu aplicativo deve continuar carregando. E retorna `true` caso seu processo tenha enviado seus parâmetros para outra instância, e você deve imediatamente finalizar. + +No OS X o sistema enforça instância única automaticamente quando usuários tentam abrir uma segunda instância do seu aplicativo no *Finder*, e os eventos `open-file` e `open-url` serão emitidos para isso. Entretanto, quando usuários inicializam seu aplicativo na linha de comando, o mecanismo de instância única do sistema será ignorado e você terá de utilizar esse método para assegurar-se de ter uma instância única. + +Um exemplo de ativação da janela de primeira instância quando uma segunda instância inicializa: + +```js +var myWindow = null; + +var shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) { + // Alguém tentou rodar uma segunda instância, devemos focar nossa janela + if (myWindow) { + if (myWindow.isMinimized()) myWindow.restore(); + myWindow.focus(); + } + return true; +}); + +if (shouldQuit) { + app.quit(); + return; +} + +// Cria myWindow, carrega o resto do aplicativo, etc... +app.on('ready', function() { +}); +``` + +### `app.setAppUserModelId(id)` _Windows_ + +* `id` String + +Muda o [Application User Model ID][app-user-model-id] para `id`. + +### `app.commandLine.appendSwitch(switch[, value])` + +Adiciona uma opção (com `value` opcional) à linha de comando do Chromium. + +**Nota:** Isto não irá afetar `process.argv`, e é utilizado principalmente por desenvolvedores para controlar alguns comportamentos de baixo nível do Chromium. + +### `app.commandLine.appendArgument(value)` + +Adiciona um argumento à linha de comando do Chromium. O argumento será passado com aspas corretamente. + +**Nota:** Isto não irá afetar `process.argv`. + +### `app.dock.bounce([type])` _OS X_ + +* `type` String (opcional) - Pode ser `critical` ou `informational`. O padrão é + `informational` + +Quando `critical` é passado, o ícone do *dock* irá pular até que o aplicativo se torne ativo ou a requisição seja cancelada. + +Quando `informational` é passado, o ícone do *dock* irá pular por um segundo. +Entretanto, a requisição se mantém ativa até que o aplicativo se torne ativo ou a requisição seja cancelada. + +Retorna um ID representando a requisição. + +### `app.dock.cancelBounce(id)` _OS X_ + +* `id` Integer + +Cancela o salto do `id`. + +### `app.dock.setBadge(text)` _OS X_ + +* `text` String + +Define a string a ser exibida na área de *badging* do *dock*. + +### `app.dock.getBadge()` _OS X_ + +Retorna a string da *badge* do *dock*. + +### `app.dock.hide()` _OS X_ + +Esconde o ícone do *dock*. + +### `app.dock.show()` _OS X_ + +Exibe o ícone do *dock*. + +### `app.dock.setMenu(menu)` _OS X_ + +* `menu` Menu + +Define o [menu do dock][dock-menu] do aplicativo. + +[dock-menu]:https://developer.apple.com/library/mac/documentation/Carbon/Conceptual/customizing_docktile/concepts/dockconcepts.html#//apple_ref/doc/uid/TP30000986-CH2-TPXREF103 +[tasks]:http://msdn.microsoft.com/en-us/library/windows/desktop/dd378460(v=vs.85).aspx#tasks +[app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx diff --git a/docs-translations/pt-BR/api/auto-updater.md b/docs-translations/pt-BR/api/auto-updater.md new file mode 100644 index 000000000..27c3ef3c0 --- /dev/null +++ b/docs-translations/pt-BR/api/auto-updater.md @@ -0,0 +1,85 @@ +# autoUpdater + +Este módulo oferece uma interface para o framework de atualização automática `Squirrel`. + +## Avisos sobre Plataformas + +Embora o `autoUpdater` ofereça uma API uniforme para diferentes plataformas, existem diferenças sutis em cada plataforma. + +### OS X + +No OS X, o módulo `autoUpdater` é construído sobre o [Squirrel.Mac][squirrel-mac], o que significa que você não precisa de nenhuma configuração especial para fazê-lo funcionar. Para requerimentos de servidor, você pode ler [Server Support][server-support]. + +### Windows + +No Windows, você deve instalar seu aplicativo na máquina de um usuário antes que possa usar o auto-updater, então é recomendado utilizar o módulo [grunt-electron-installer][installer] para gerar um instalador do Windows. + +O instalador gerado com Squirrel irá criar um ícone de atalho com um [Application User Model ID][app-user-model-id] no formato `com.squirrel.PACKAGE_ID.YOUR_EXE_WITHOUT_DOT_EXE`, por exemplo: `com.squirrel.slack.Slack` e `com.squirrel.code.Code`. Você precisa usar o mesmo ID para seu aplicativo a API `app.setAppUserModelId`, senão o Windows não conseguirá fixar seu aplicativo corretamente na barra de tarefas. + +A configuração do servidor também é diferente do OS X. Você pode ler a documentação do [Squirrel.Windows][squirrel-windows] para mais detalhes. + +### Linux + +Não há suporte nativo do auto-updater para Linux, então é recomendado utilizar o gerenciador de pacotes da distribuição para atualizar seu aplicativo. + +## Eventos + +O objeto `autoUpdater` emite os seguintes eventos: + +### Evento: 'error' + +Retorna: + +* `error` Error + +Emitido quando há um erro durante a atualização. + +### Evento: 'checking-for-update' + +Emitido quando está verificando se uma atualização foi inicializada. + +### Evento: 'update-available' + +Emitido quando há uma atualização disponível. A autalização é baixada automaticamente. + +### Evento: 'update-not-available' + +Emitido quando não há uma atualização disponível. + +### Evento: 'update-downloaded' + +Retorna: + +* `event` Event +* `releaseNotes` String +* `releaseName` String +* `releaseDate` Date +* `updateURL` String + +Emitido quando uma atualização foi baixada. + +No Windows apenas `releaseName` está disponível. + +## Métodos + +O objeto `autoUpdater` possui os seguintes métodos: + +### `autoUpdater.setFeedURL(url)` + +* `url` String + +Define a `url` e inicializa o auto-updater. A `url` não pode ser alterada uma vez que foi definida. + +### `autoUpdater.checkForUpdates()` + +Pergunta ao servidor se há uma atualização. Você deve chamar `setFeedURL` antes de usar esta API. + +### `autoUpdater.quitAndInstall()` + +Reinicia o aplicativo e instala a atualização após esta ter sido baixada. Só deve ser chamado após o `update-downloaded` ter sido emitido. + +[squirrel-mac]: https://github.com/Squirrel/Squirrel.Mac +[server-support]: https://github.com/Squirrel/Squirrel.Mac#server-support +[squirrel-windows]: https://github.com/Squirrel/Squirrel.Windows +[installer]: https://github.com/atom/grunt-electron-installer +[app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx diff --git a/docs-translations/pt-BR/api/browser-window.md b/docs-translations/pt-BR/api/browser-window.md new file mode 100644 index 000000000..143b6368b --- /dev/null +++ b/docs-translations/pt-BR/api/browser-window.md @@ -0,0 +1,632 @@ +# BrowserWindow + +A classe `BrowserWindow` lhe dá a habilidade de criar uma janela do browser. Por exemplo: + +```javascript +const BrowserWindow = require('electron').BrowserWindow; + +var win = new BrowserWindow({ width: 800, height: 600, show: false }); +win.on('closed', function() { + win = null; +}); + +win.loadURL('https://github.com'); +win.show(); +``` + +Você também pode criar uma janela sem o chrome utilizando a API [Frameless Window](../../../docs/api/frameless-window.md). + +## Classe: BrowserWindow + +`BrowserWindow` é um [EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter). + +Ela cria uma nova `BrowserWindow` com propriedades nativas definidas pelo `options`. + +### `new BrowserWindow([options])` + +`options` Objeto (opcional), propriedades: + +* `width` Integer - Largura da janela em pixels. O padrão é `800`. +* `height` Integer - Altura da janela em pixels. O padrão é `600`. +* `x` Integer - Deslocamento da janela da esquerda da tela. O padrão é centralizar a janela. +* `y` Integer - Deslocamento da janela do topo da tela. O padrão é centralizar a janela. +* `useContentSize` Boolean - `width` e `height` seriam utilizados como o tamanho da página web, o que significa que o tamanho real da janela irá incluir o tamanho da moldura da janela e será um pouco maior. O padrão é `false`. +* `center` Boolean - Mostra a janela no centro da tela. +* `minWidth` Integer - Largura mínima da janela. O padrão é `0`. +* `minHeight` Integer - Altura mínima da janela. O padrão é `0`. +* `maxWidth` Integer - Largura máxima da janela. O padrão é sem limites. +* `maxHeight` Integer - Altura máxima da janela. O padrão é sem limites. +* `resizable` Boolean - Se é possível modificar o tamanho da janela. O padrão é `true`. +* `alwaysOnTop` Boolean - Se a janela deve sempre ficar à frente de outras janelas. O padrão é `false`. +* `fullscreen` Boolean - Se a janela deve estar em tela cheia. Quando definido como `false`, o botão de tela cheia estará escondido ou desabilitado no OS X. O padrão é `false`. +* `skipTaskbar` Boolean - Se deve mostrar a janela na barra de tarefas. O padrão é `false`. +* `kiosk` Boolean - Modo *kiosk*. O padrão é `false`. +* `title` String - Título padrão da janela. O padrão é `"Electron"`. +* `icon` [NativeImage](../../../docs/api/native-image.md) - O ícone da janela, quando omitido no Windows, o ícone do executável é utilizado como o ícone da janela. +* `show` Boolean - Se a janela deve ser exibida quando criada. O padrão é `true`. +* `frame` Boolean - Defina como `false` para criar uma [Frameless Window](../../../docs/api/frameless-window.md). O padrão é `true`. +* `acceptFirstMouse` Boolean - Se o *web view* aceita um evento *mouse-down* que ativa a janela simultaneamente. O padrão é `false`. +* `disableAutoHideCursor` Boolean - Se deve esconder o cursor quando estiver digitando. O padrão é `false`. +* `autoHideMenuBar` Boolean - Esconde a barra de menu automaticamente, a não ser que a tecla `Alt` seja pressionada. O padrão é `false`. +* `enableLargerThanScreen` Boolean - Possibilita que o tamanho da janela seja maior que a tela. O padrão é `false`. +* `backgroundColor` String - Cor de fundo da janela em hexadecimal, como `#66CD00` ou `#FFF`. Só é implementado no Linux e Windows. O padrão é `#000` (preto). +* `darkTheme` Boolean - Força a utilização do tema *dark* na janela, só funciona em alguns ambientes de desktop GTK+3. O padrão é `false`. +* `transparent` Boolean - Torna a janela [transparente](../../../docs/api/frameless-window.md). O padrão é `false`. +* `type` String - Define o tipo da janela, que aplica propriedades adicionais específicas da plataforma. Por padrão é indefinido e será criada uma janela de aplicativo comum. Possíveis valores: + * No Linux, os tipos possíveis são `desktop`, `dock`, `toolbar`, `splash`, + `notification`. + * No OS X, os tipos possíveis são `desktop`, `textured`. O tipo `textured` adiciona a aparência degradê metálica (`NSTexturedBackgroundWindowMask`). O tipo `desktop` coloca a janela no nível do fundo de tela do desktop (`kCGDesktopWindowLevel - 1`). Note que a janela `desktop` não irá receber foco, eventos de teclado ou mouse, mas você pode usar `globalShortcut` para receber entrada de dados ocasionalmente. +* `titleBarStyle` String, OS X - Define o estilo da barra de título da janela. Esta opção está suportada a partir da versão OS X 10.10 Yosemite. Há três possíveis valores: + * `default` ou não definido, resulta na barra de título cinza opaca padrão do Mac. + * `hidden` resulta numa barra de título oculta e a janela de conteúdo no tamanho máximo, porém a barra de título ainda possui os controles padrões de janela ("semáforo") no canto superior esquerdo. + * `hidden-inset` resulta numa barra de título oculta com uma aparência alternativa onde os botões de semáforo estão ligeiramente mais longe do canto da janela. +* `webPreferences` Object - Configurações das características da página web, propriedades: + * `nodeIntegration` Boolean - Se a integração com node está habilitada. O padrão é `true`. + * `preload` String - Define um *script* que será carregado antes que outros scripts rodem na página. Este script sempre terá acesso às APIs do node, não importa se `nodeIntegration` esteja habilitada ou não. O valor deve ser o endereço absoluto do scriot. Quando `nodeIntegration` não está habilitada, o script `preload` pode reintroduzir símbolos globais Node de volta ao escopo global. Veja um exemplo [aqui](process.md#evento-loaded). + * `partition` String - Define a sessão utilizada pela página. Se `partition` começa com `persist:`, a página irá utilizar uma sessão persistente disponível para todas as páginas do aplicativo com a mesma `partition`. Se não houver o prefixo `persist:`, a página irá utilizar uma sessão em memória. Ao utilizar a mesma `partition`, várias páginas podem compartilhar a mesma sessão. Se a `partition` for indefinida, então a sessão padrão do aplicativo será utilizada. + * `zoomFactor` Number - O fator de *zoom* da página, `3.0` representa `300%`. O padrão é `1.0`. + * `javascript` Boolean - Habilita suporte à JavaScript. O padrão é `true`. + * `webSecurity` Boolean - Quando definido como `false`, irá desabilitar a política de mesma origem (Geralmente usando sites de teste por pessoas), e definir `allowDisplayingInsecureContent` e `allowRunningInsecureContent` como + `true` se estas duas opções não tiverem sido definidas pelo usuário. O padrão é `true`. + * `allowDisplayingInsecureContent` Boolean - Permite que uma página https exiba conteúdo como imagens de URLs http. O padrão é `false`. + * `allowRunningInsecureContent` Boolean - Permite que uma página https rode JavaScript, CSS ou plugins de URLs http. O padrão é `false`. + * `images` Boolean - Habilita suporte a imagens. O padrão é `true`. + * `java` Boolean - Habilita suporte a Java. O padrão é `false`. + * `textAreasAreResizable` Boolean - Faz com que os elementos *TextArea* elements tenham tamanho variável. O padrão é `true`. + * `webgl` Boolean - Habiltia suporte a WebGL. O padrão é `true`. + * `webaudio` Boolean - Habilita suporte a WebAudio. O padrão é `true`. + * `plugins` Boolean - Se plugins devem ser habilitados. O padrão é `false`. + * `experimentalFeatures` Boolean - Habilita as características experimentais do Chromium. O padrão é `false`. + * `experimentalCanvasFeatures` Boolean - Habilita as características experimentais de canvas do Chromium. O padrão é `false`. + * `overlayScrollbars` Boolean - Habilita sobreposição das barras de rolagem. O padrão é `false`. + * `overlayFullscreenVideo` Boolean - Habilita sobreposição do vídeo em tela cheia. O padrão é `false`. + * `sharedWorker` Boolean - Habilita suporte a *Shared Worker*. O padrão é `false`. + * `directWrite` Boolean - Habilita o sistema de renderização de fontes *DirectWrite* no Windows. O padrão é `true`. + * `pageVisibility` Boolean - A página é forçada a permanecer visível ou oculta quando definido, em vez de refletir a visibilidade atual da janela. Usuários podem definir como `true` para evitar que os temporizadores do *DOM* sejam suprimidos. O padrão é `false`. + +## Eventos + +O objeto `BrowserWindow` emite os seguintes eventos: + +**Nota:** Alguns eventos só estão disponíveis em sistemas operacionais específicos e estão rotulados como tal. + +### Evento: 'page-title-updated' + +Retorna: + +* `event` Evento + +Emitido quando o documento muda seu título, chamar `event.preventDefault()` previne que o título nativo da janela mude. + +### Evento: 'close' + +Retorna: + +* `event` Evento + +Emitido quando a janela for fechar. É emitido antes dos eventos `beforeunload` e `unload` do DOM. Chamar `event.preventDefault()` cancela o fechamento. + +Normalmente você utiliza o manipulador de eventos do `beforeunload` para decidir se a janela deve ser fechada, que também será chamado quando a janela é recarregada. No Electron, retornar uma string vazia ou `false` cancela o fechamento. Por exemplo: + +```javascript +window.onbeforeunload = function(e) { + console.log('Não quero ser fechada'); + + // Diferente de navegadores comuns, nos quais uma string deve ser retornada e + // o usuário deve confirmar se a janela será fechada, o Electron dá mais opções + // aos desenvolvedores. Retornar uma string vazia ou false cancela o fechamento. + // Você também pode usar a API de diálogo para deixar que o usuário confirme o + // fechamento do aplicativo. + e.returnValue = false; +}; +``` + +### Evento: 'closed' + +Emitido quando a janela é fechada. Após você ter recebido este evento, você deve remover a referência da janela e evitar utilizá-la. + +### Evento: 'unresponsive' + +Emitido quando a página web para de responder. + +### Evento: 'responsive' + +Emitido quando a página web que não respondia volta a responder novamente. + +### Evento: 'blur' + +Emitido quando a janela perde foco. + +### Evento: 'focus' + +Emitido quando a janela ganha foco. + +### Evento: 'maximize' + +Emitido quando a janela é maximizada. + +### Evento: 'unmaximize' + +Emitido quando a janela sai do estado maximizado. + +### Evento: 'minimize' + +Emitido quando a janela é minimizada. + +### Evento: 'restore' + +Emitido quando a janela é restaurada do estado minimizado. + +### Evento: 'resize' + +Emitido quando o tamanho da janela está sendo alterado. + +### Evento: 'move' + +Emitido quando está sendo movida para uma nova posição. + +__Note__: No OS X este evento é apenas um apelido de `moved`. + +### Evento: 'moved' _OS X_ + +Emitido uma vez quando a janela é movida para uma nova posição. + +### Evento: 'enter-full-screen' + +Emitido quando a janela entra no estado tela cheia. + +### Evento: 'leave-full-screen' + +Emitido quando a janela sai do estado de tela cheia. + +### Evento: 'enter-html-full-screen' + +Emitido quando a janela entra no estado tela cheia, ocasionado por uma api de html. + +### Evento: 'leave-html-full-screen' + +Emitido quando a janela sai do estado de tela cheia, ocasionado por uma api de html. + +### Evento: 'app-command' _Windows_ + +Emitido quando um [App Command](https://msdn.microsoft.com/en-us/library/windows/desktop/ms646275(v=vs.85).aspx) é invocado. Estes estão tipicamente relacionado às teclas de mídia do teclado, ou comandos do browser, assim como o botão "Voltar" existente em alguns modelos de mouse no Windows. + +```js +someWindow.on('app-command', function(e, cmd) { + // Navega a janela 'para trás' quando o usuário pressiona o botão voltar do mouse + if (cmd === 'browser-backward' && someWindow.webContents.canGoBack()) { + someWindow.webContents.goBack(); + } +}); +``` + +## Métodos + +O objeto `BrowserWindow` possui os seguintes métodos: + +### `BrowserWindow.getAllWindows()` + +Retorna um array de todas as janelas abertas. + +### `BrowserWindow.getFocusedWindow()` + +Retorna a janela que está focada no aplicativo. + +### `BrowserWindow.fromWebContents(webContents)` + +* `webContents` [WebContents](../../../docs/api/web-contents.md) + +Acha uma janela de acordo com os `webContents` que ela possui. + +### `BrowserWindow.fromId(id)` + +* `id` Integer + +Acha uma janela de acordo com o seu ID. + +### `BrowserWindow.addDevToolsExtension(path)` + +* `path` String + +Adiciona a extenção DevTools localizada no endereço `path`, e retorna o nome da extenção. + +A extenção será lembrada, então você só precisa chamar esta API uma vez, esta API não é para uso de programação. + +### `BrowserWindow.removeDevToolsExtension(name)` + +* `name` String + +Remove a extenção DevTools cujo nome é `name`. + +## Propriedades de Instância + +Objetos criados com `new BrowserWindow` possuem as seguintes propriedades: + +```javascript +// Neste exemplo `win` é a nossa instância +var win = new BrowserWindow({ width: 800, height: 600 }); +``` + +### `win.webContents` + +Todas os eventos e operações relacionados à pagina web serão feitos através do objeto `WebContents` que a esta janela possui. + +Veja a [documentação do `webContents`](../../../docs/api/web-contents.md) para seus métodos e eventos. + +### `win.id` + +O ID único desta janela. + +## Métodos de instância + +Objetos criados com `new BrowserWindow` possuem os seguintes métodos de instância: + +**Nota:** Alguns métodos só estão disponíveis em sistemas operacionais específicos e estão rotulados como tal. + +### `win.destroy()` + +Força o fechamento da janela, os eventos `unload` e `beforeunload` não serão emitidos para a página web, e o evento `close` também não será emitido para esta janela, mas garante que o evento `closed` será emitido. + +Você só deve utilizar este método quando o processo renderizador (página web) congelar. + +### `win.close()` + +Tenta fechar a janela, tem o mesmo efeito que o usuário manualmente clicar o botão de fechar a janela. Entretanto, a página web pode cancelar o fechamento, veja o [evento close](#evento-close). + +### `win.focus()` + +Foca na janela. + +### `win.isFocused()` + +Retorna um boolean, indicando se a janela está com foco. + +### `win.show()` + +Exibe e dá foco à janela. + +### `win.showInactive()` + +Exibe a janela, porém não dá foco à ela. + +### `win.hide()` + +Esconde a janela. + +### `win.isVisible()` + +Retorna um boolean, indicando se a janela está visível para o usuário. + +### `win.maximize()` + +Maximiza a janela. + +### `win.unmaximize()` + +Sai do estado maximizado da janela. + +### `win.isMaximized()` + +Retorna um boolean, indicando se a janela está maximizada. + +### `win.minimize()` + +Minimiza a janela. Em algumas plataformas, a janela minimizada será exibida no *Dock*. + +### `win.restore()` + +Restaura a janela do estado minimizado para o estado anterior. + +### `win.isMinimized()` + +Retorna um boolean, indicando se a janela está minimizada. + +### `win.setFullScreen(flag)` + +* `flag` Boolean + +Define se a janela deve estar em modo tela cheia. + +### `win.isFullScreen()` + +Retorna um boolean, indicando se a janela está em modo tela cheia. + +### `win.setAspectRatio(aspectRatio[, extraSize])` _OS X_ + +* `aspectRatio` A proporção que queremos manter para uma porção do conteúdo da *view*. +* `extraSize` Object (opcional) - O tamanho extra não incluído enquanto a proporção é mantida. Propriedades: + * `width` Integer + * `height` Integer + +Isto fará com que a janela mantenha sua proporção. O `extraSize` permite que o desenvolvedor tenha espaço, definido em pixels, não incluídos no cálculo da proporção. Esta API já leva em consideração a diferença entre o tamanho da janela e o tamanho de seu conteúdo. + +Suponha que exista uma janela normal com um *player* de vídeo HD e seus controles associados. Talvez tenha 15 pixels de controles na borda esquerda, 25 pixels de controles na borda direita e 50 abaixo do player. Para que seja mantida a proporção de 16:9 (proporção padrão para HD em 1920x1080) no próprio player, nós chamaríamos esta função com os argumentos 16/9 e [ 40, 50 ]. Para o segundo argumento, não interessa onde a largura e altura extras estão no conteúdo da view--apenas que elas existam. Apenas some qualquer área de largura e altura extras que você tem dentro da view do conteúdo. + +### `win.setBounds(options)` + +`options` Object, propriedades: + +* `x` Integer +* `y` Integer +* `width` Integer +* `height` Integer + +Redefine o tamanho e move a janela para `width`, `height`, `x`, `y`. + +### `win.getBounds()` + +Retorna um objeto que contém a largura, altura, posição x e y da janela. + +### `win.setSize(width, height)` + +* `width` Integer +* `height` Integer + +Redefine o tamanho da janela para largura `width` e altura `height`. + +### `win.getSize()` + +Retorna um array que contém a largura e altura da janela. + +### `win.setContentSize(width, height)` + +* `width` Integer +* `height` Integer + +Redefine a área de cliente da janela (a página web) para largura `width` e altura `height`. + +### `win.getContentSize()` + +Retorna um array que contém a largura e altura da área de cliente da janela. + +### `win.setMinimumSize(width, height)` + +* `width` Integer +* `height` Integer + +Define o tamanho mínimo da janela para largura `width` e altura `height`. + +### `win.getMinimumSize()` + +Retorna uma array que contém o tamanho mínimo da largura e altura da janela. + +### `win.setMaximumSize(width, height)` + +* `width` Integer +* `height` Integer + +Define o tamanho máximo da janela para largura `width` e altura `height`. + +### `win.getMaximumSize()` + +Retorna uma array que contém o tamanho máximo da largura e altura da janela. + +### `win.setResizable(resizable)` + +* `resizable` Boolean + +Define se a janela pode ter seu tamanho redefinido manualmente pelo usuário. + +### `win.isResizable()` + +Retorna um boolean indicando se a janela pode ter seu tamanho redefinido manualmente pelo usuário. + +### `win.setAlwaysOnTop(flag)` + +* `flag` Boolean + +Define se a janela deve estar sempre em cima de outras janelas. Após definir isso, a janela continua sendo uma janela normal, não uma janela *toolbox* que não pode receber foco. + +### `win.isAlwaysOnTop()` + +Retorna um boolean indicando se a janela está sempre em cima de outras janelas. + +### `win.center()` + +Move a janela para o centro da tela. + +### `win.setPosition(x, y)` + +* `x` Integer +* `y` Integer + +Move a janela para `x` e `y`. + +### `win.getPosition()` + +Retorna um array com a posição atual da janela. + +### `win.setTitle(title)` + +* `title` String + +Muda o título da janela nativa para `title`. + +### `win.getTitle()` + +Retorna o título da janela nativa. + +**Nota:** O título da página web pode ser diferente do título da janela nativa. + +### `win.flashFrame(flag)` + +* `flag` Boolean + +Inicia ou para de piscar a janela para atrair a atenção do usuário. + +### `win.setSkipTaskbar(skip)` + +* `skip` Boolean + +Faz com que a janela não apareça na barra de tarefas. + +### `win.setKiosk(flag)` + +* `flag` Boolean + +Entra ou sai do modo *kiosk*. + +### `win.isKiosk()` + +Retorna um boolean indicando se janela está no modo *kiosk*. + +### `win.hookWindowMessage(message, callback)` _Windows_ + +* `message` Integer +* `callback` Function + +Engancha uma mensagem de janela. O `callback` é chamado quando a mensagem é recebida no WndProc. + +### `win.isWindowMessageHooked(message)` _Windows_ + +* `message` Integer + +Retorna `true` ou `false` indicando se a mensagem está enganchada ou não. + +### `win.unhookWindowMessage(message)` _Windows_ + +* `message` Integer + +Desengancha a mensagem de janela. + +### `win.unhookAllWindowMessages()` _Windows_ + +Desengancha todas as mensagens de janela. + +### `win.setRepresentedFilename(filename)` _OS X_ + +* `filename` String + +Define o endereço do arquivo que a janela representa, e o ícone do arquivo será exibido na barra de título da janela. + +### `win.getRepresentedFilename()` _OS X_ + +Retorna o endereço do arquivo que a janela representa. + +### `win.setDocumentEdited(edited)` _OS X_ + +* `edited` Boolean + +Define se o documento da janela foi editado, e o ícone na barra de título se torna cinza quando definido como `true`. + +### `win.isDocumentEdited()` _OS X_ + +Retorna um boolean indicando se o documento da janela foi editado. + +### `win.focusOnWebView()` + +### `win.blurWebView()` + +### `win.capturePage([rect, ]callback)` + +* `rect` Object (opcional)- A área da página a ser capturada. Propriedades: + * `x` Integer + * `y` Integer + * `width` Integer + * `height` Integer +* `callback` Function + +Captura uma imagem da página dentro do `rect`. Após completar, `callback` será chamada com `callback(imagem)`. `imagem` é uma instância de [NativeImage](../../../docs/api/native-image.md) que guarda dados sobre a imagem. Omitir `rect` captura toda a página visível. + +### `win.print([options])` + +Igual a `webContents.print([options])` + +### `win.printToPDF(options, callback)` + +Igual a `webContents.printToPDF(options, callback)` + +### `win.loadURL(url[, options])` + +Igual a `webContents.loadURL(url[, options])`. + +### `win.reload()` + +Igual a `webContents.reload`. + +### `win.setMenu(menu)` _Linux_ _Windows_ + +* `menu` Menu + +Define `menu` como a barra de menu da janela, definir como `null` remove a barra de menu. + +### `win.setProgressBar(progress)` + +* `progress` Double + +Define o valor do progresso na barra de progresso. Extensão válida é [0, 1.0]. + +Remove a barra de progresso quando `progress` < 0. +Muda para o modo indeterminado quando `progress` > 1. + +Na plataforma Linux, apenas suporta o ambiente de desktop Unity, você precisa definir o nome do arquivo como `*.desktop` no campo `desktopName` no `package.json`. Por padrão, irá assumir `app.getName().desktop`. + +### `win.setOverlayIcon(overlay, description)` _Windows 7+_ + +* `overlay` [NativeImage](../../../docs/api/native-image.md) - o ícone a ser exibido no canto inferior direito da barra de tarefas. Se este parâmetro for `null`, a sobreposição é eliminada. +* `description` String - uma descrição que será providenciada a leitores de tela de acessibilidade. + +Define uma sobreposição de 16px sobre o ícone da barra de tarefas atual, geralmente utilizado para indicar algum tipo de status de aplicação, ou notificar passivamente o usuário. + +### `win.setThumbarButtons(buttons)` _Windows 7+_ + +`buttons` Array de objetos `button`: + +`button` Object, propriedades: + +* `icon` [NativeImage](../../../docs/api/native-image.md) - O ícone exibido na barra de ferramentas miniatura. +* `tooltip` String (opcional) - O texto do balão de dica do botão. +* `flags` Array (opcional) - Controla estados e comportamentos específicos do botão. Utiliza `enabled` por padrão. Pode incluir as seguintes strings: + * `enabled` - O botão está ativo e disponível para o usuário. + * `disabled` - O botão está desabilitado. Está presente, mas possui um estado visual indicando que não irá responder às ações do usuário. + * `dismissonclick` - Quando o botão é clicado, o *flyout* do botão da barra de tarefas fecha imediatamente. + * `nobackground` - Não desenhe a borda do botão, apenas utilize a imagem. + * `hidden` - O botão não é exibido para o usuário. + * `noninteractive` - O botão está habilitado, mas não interage; Não é exibido o estado de botão pressionado. Este valor está destinado a instâncias nas quais o botão é utilizado em uma notificação. +* `click` - Função + +Adiciona uma barra de ferramentes miniatura com um conjunto de botões específicos à imagem miniatura de uma janela no layout de um botão de barra de tarefas. Retorna um objeto `Boolean` indicando se a miniatura foi adicionada com sucesso. + +O número de botões na barra de ferramentas miniatura não deve ser maior que 7 devido ao espaço limitado. Uma vez que você define a barra de ferramentas miniatura, ela não pode ser removida por causa da limitação da plataforma. Mas você pode chamar a API com um array vazio para limpar todos os botões. + +### `win.showDefinitionForSelection()` _OS X_ + +Mostra um dicionário *pop-up* que procura a palavra selecionada na página. + +### `win.setAutoHideMenuBar(hide)` + +* `hide` Boolean + +Define se a barra de menu da janela deve se esconder automaticamente. Uma vez que for definida, a barra de menu só será exibida quando usuários pressionarem a tecla `Alt`. + +Se a barra de menu já estiver visível, chamar `setAutoHideMenuBar(true)` não irá escondê-la imediatamente. + +### `win.isMenuBarAutoHide()` + +Retorna um boolean indicando se a barra de menu se esconde automaticamente. + +### `win.setMenuBarVisibility(visible)` + +* `visible` Boolean + +Define se a barra de menu deve ser visível. Se a barra de menu se esconde automaticamente, os usuários ainda podem exibí-la ao pressionar a tecla `Alt`. + +### `win.isMenuBarVisible()` + +Retorna um boolean indicando se a barra de menu está visível. + +### `win.setVisibleOnAllWorkspaces(visible)` + +* `visible` Boolean + +Define se a janela deve estar visível em todos os *workspaces*. + +**Nota:** Esta API não faz nada no Windows. + +### `win.isVisibleOnAllWorkspaces()` + +Retorna um boolean indicando se a janela está visível em todos os *workspaces*. + +**Nota:** Esta API sempre retorna `false` no Windows. + + diff --git a/docs-translations/pt-BR/api/process.md b/docs-translations/pt-BR/api/process.md index 3da0dc583..1c20e2df1 100644 --- a/docs-translations/pt-BR/api/process.md +++ b/docs-translations/pt-BR/api/process.md @@ -1,22 +1,48 @@ # process -O objeto `process` no Electron tem as seguintes diferenças de um upstream node: +O objeto `process` no Electron tem as seguintes diferenças do objeto no upstream node: -* `process.type` String - Tipo de processo, pode ser `browser` (i.e. main process) +* `process.type` String - Tipo de processo, pode ser `browser` (processo principal) ou `renderer`. * `process.versions['electron']` String - Versão do Electron. * `process.versions['chrome']` String - Versão do Chromium. -* `process.resourcesPath` String - Caminho para os códigos fontes JavaScript. +* `process.resourcesPath` String - Caminho para o código fonte JavaScript. +* `process.mas` Boolean - Para build da Mac App Store, este valor é `true`, para outros builds é `undefined`. + +## Eventos + +### Evento: 'loaded' + +Emitido quando o Electron carregou seu script de inicialização interno e está começando a carregar a página web ou o script principal. + +Pode ser utilizado pelo script pré-carregamento (preload.js abaixo) para adicionar símbolos globais do Node removidos para o escopo global quando a integração do node é desligada: + +```js +// preload.js +var _setImmediate = setImmediate; +var _clearImmediate = clearImmediate; +process.once('loaded', function() { + global.setImmediate = _setImmediate; + global.clearImmediate = _clearImmediate; +}); +``` + +## Propriedades + +### `process.noAsar` + +Definir isto para `true` pode desabilitar o suporte para arquivos `asar` nos módulos nativos do Node. # Métodos -O objeto `process` tem os seguintes método: + +O objeto `process` tem os seguintes métodos: ### `process.hang` -Afeta a thread principal do processo atual. +Faz com que o *thread* principal do processo congele. -## process.setFdLimit(MaxDescritores) _OS X_ _Linux_ +### `process.setFdLimit(maxDescriptors)` _OS X_ _Linux_ * `maxDescriptors` Integer Define o limite do arquivo descritor para `maxDescriptors` ou para o limite do OS, -o que for menor para o processo atual. \ No newline at end of file +o que for menor para o processo atual. diff --git a/docs-translations/pt-BR/api/shell.md b/docs-translations/pt-BR/api/shell.md index 7c3f24ade..65e0a2d42 100644 --- a/docs-translations/pt-BR/api/shell.md +++ b/docs-translations/pt-BR/api/shell.md @@ -1,11 +1,11 @@ # shell -O módulo `shell` fornece funções relacionadas intereções com o OS do usuário. +O módulo `shell` fornece funções relacionadas à integração com o desktop. Um exemplo para abrir uma URL no browser padrão do usuário: ```javascript -var shell = require('shell'); +const shell = require('shell'); shell.openExternal('https://github.com'); ``` @@ -17,26 +17,26 @@ O módulo `shell` tem os seguintes métodos: * `fullPath` String -Exibe o arquivo no gerenciador de arquivos padrão do sistema. Se possivel, seleciona o arquivo automaticamente. +Exibe o arquivo num gerenciador de arquivos. Se possivel, seleciona o arquivo. ### `shell.openItem(fullPath)` * `fullPath` String -Abre o arquivo em seu programa padrão. +Abre o arquivo de maneira padrão do desktop. ### `shell.openExternal(url)` * `url` String -Abre o arquivo seguido de um protocol em seu programa padrão. (Por -exemplo, mailto:foo@bar.com.) +Abre a URL de protocolo externo de maneira padrão do desktop. (Por +exemplo, mailto: URLs no programa de email padrão do usuário) ### `shell.moveItemToTrash(fullPath)` * `fullPath` String -Move o arquivo para a lixeira e retorna um boolean com o resultado da operação. +Move o arquivo para a lixeira e retorna um status boolean com o resultado da operação. ### `shell.beep()` diff --git a/docs-translations/pt-BR/development/build-instructions-linux.md b/docs-translations/pt-BR/development/build-instructions-linux.md new file mode 100644 index 000000000..532892a40 --- /dev/null +++ b/docs-translations/pt-BR/development/build-instructions-linux.md @@ -0,0 +1,142 @@ +# Instruções de Build (Linux) + +Siga as orientações abaixo pra fazer o build do Electron no Linux. + +## Pré-requisitos + +* Python 2.7.x. Algumas distribuições como CentOS ainda usam Python 2.6.x, + então você precisa checar a sua versão do Python com `python -V`. +* Node.js v0.12.x. Há várias maneiras de instalar o Node. Você pode baixar o + código fonte do [Node.js](http://nodejs.org) e compilar a partir dele. + Fazer isto permite que você instale o Node no seu próprio diretório home + como um usuário comum. + Ou tente repositórios como [NodeSource](https://nodesource.com/blog/nodejs-v012-iojs-and-the-nodesource-linux-repositories). +* Clang 3.4 ou mais recente. +* Cabeçalhos de desenvolvimento do GTK+ e libnotify. + +No Ubuntu, instale as seguintes bibliotecas: + +```bash +$ sudo apt-get install build-essential clang libdbus-1-dev libgtk2.0-dev \ + libnotify-dev libgnome-keyring-dev libgconf2-dev \ + libasound2-dev libcap-dev libcups2-dev libxtst-dev \ + libxss1 libnss3-dev gcc-multilib g++-multilib +``` + +No Fedora, instale as seguintes bibliotecas: + +```bash +$ sudo yum install clang dbus-devel gtk2-devel libnotify-devel libgnome-keyring-devel \ + xorg-x11-server-utils libcap-devel cups-devel libXtst-devel \ + alsa-lib-devel libXrandr-devel GConf2-devel nss-devel +``` + +Outras distribuições podem oferecer pacotes similares para instalação através +do gerenciador de pacotes como o pacman. Ou você pode compilar a partir do +código fonte. + +## Se Você Utilizar Máquinas Virtuais Para Fazer O Build + +Se você planeja fazer o build do Electron numa máquina virtual, você vai precisar +de um container de tamanho fixo de pelo menos 25 gigabytes. + +## Baixando o Código + +```bash +$ git clone https://github.com/atom/electron.git +``` + +## Bootstrapping + +O script de *boostrap* irá baixar todas as dependências de build necessárias +e criar os arquivos de projeto do build. Você deve ter o Python 2.7.x para +executar o script com sucesso. +Baixar certos arquivos pode demorar bastante. Note que estamos utilizando +`ninja` para fazer o build do Electron, então não há um `Makefile` gerado. + +```bash +$ cd electron +$ ./script/bootstrap.py -v +``` + +### Compilação Cruzada + +Se você quer fazer o build para `arm`, você também deve instalar as seguintes +dependências: + +```bash +$ sudo apt-get install libc6-dev-armhf-cross linux-libc-dev-armhf-cross \ + g++-arm-linux-gnueabihf +``` + +E para fazer a compilação cruzada para `arm` ou `ia32`, você deve passar +o parâmetro `--target_arch` para o script `bootstrap.py`: + +```bash +$ ./script/bootstrap.py -v --target_arch=arm +``` + +## Building + +Se você quiser fazer o build para `Release` e `Debug`: + +```bash +$ ./script/build.py +``` + +Este script irá fazer com que um executável bem pesado do Electron seja +criado no diretório `out/R`. O arquivo possui mais de 1.3 gigabytes. +Isso acontece por que o binário do Release contém símbolos de debugging. +Para reduzir o tamanho do arquivo, rode o script `create-dist.py`: + +```bash +$ ./script/create-dist.py +``` + +Isso irá colocar uma distribuição funcional com arquivos muito menores +no diretório `dist`. Depois de rodar o script `create-dist.py`, talvez +você queira remover o binário de 1.3+ gigabytes que ainda está em `out/R`. + +Você também pode fazer apenas o build de `Debug`: + +```bash +$ ./script/build.py -c D +``` + +Depois de completar o build, você pode encontrar o binário de debug do `electron` +em `out/D`. + +## Limpando + +Para limpar os arquivos de build: + +```bash +$ ./script/clean.py +``` + +## Troubleshooting + +Certifique-se de que você tenha instalado todas as dependências do build. + +### Error While Loading Shared Libraries: libtinfo.so.5 + +O `clang` prebuilt irá tentar fazer o link com `libtinfo.so.5`. Dependendo +da arquitetura do host, faça um link simbólico para o `libncurses` apropriado: + +```bash +$ sudo ln -s /usr/lib/libncurses.so.5 /usr/lib/libtinfo.so.5 +``` + +## Testes + +Teste suas modificações conforme o estilo de código do projeto utilizando: + +```bash +$ ./script/cpplint.py +``` + +Teste funcionalidade utilizando: + +```bash +$ ./script/test.py +``` diff --git a/docs-translations/pt-BR/development/coding-style.md b/docs-translations/pt-BR/development/coding-style.md index f3b4d6d58..b0068d568 100644 --- a/docs-translations/pt-BR/development/coding-style.md +++ b/docs-translations/pt-BR/development/coding-style.md @@ -2,28 +2,25 @@ Estas são as diretrizes de estilo para codificar no Electron. -## C++ and Python +## C++ e Python -Para C ++ e Python, seguimos os padrões do projeto Chromium [Estilo de Codificação](http://www.chromium.org/developers/coding-style). Há também um +Para C++ e Python, seguimos o [Estilo de Codificação](http://www.chromium.org/developers/coding-style) do projeto Chromium. Há também um script `script/cpplint.py` para verificar se todos os arquivos estão em conformidade. A versão Python que estamos usando agora é a Python 2.7. -O código C ++ usa do Chromium's um monte de tipos e abstrações, por isso é recomendada para se familiarizar com eles. Um bom lugar para começar com a documentação do Chromium's [Important Abstractions and Data Structures](https://www.chromium.org/developers/coding-style/important-abstractions-and-data-structures). O documento menciona alguns tipos especiais, com escopo tipos (que automaticamente libera sua memória quando sai do escopo), registrando mecanismos etc. +O código C++ usa várias abstrações e tipos do Chromium, por isso é recomendado familiarizar-se com eles. Um bom lugar para começar é com a documentação do Chromium [Important Abstractions and Data Structures](https://www.chromium.org/developers/coding-style/important-abstractions-and-data-structures). O documento menciona alguns tipos especiais, *scoped types* (que automaticamente liberam sua memória ao sair do escopo), mecanismos de *log* etc. ## CoffeeScript -For CoffeeScript, we follow GitHub's [Style -Guide](https://github.com/styleguide/javascript) and the following rules: +Para CoffeeScript, seguimos o [Guia de Estilo] (https://github.com/styleguide/javascript) do GitHub com as seguintes regras: -Para CoffeeScript, seguimos o estilo do GitHub [Guia de Estilo] (https://github.com/styleguide/javascript) com as seguintes regras: +* Os arquivos **NÃO DEVEM** terminar com uma nova linha, porque queremos corresponder aos padrões de estilo Google. -* Os arquivos devem **NÃO DEVEM** com nova linha no final, porque queremos corresponder aos padrões de estilo Google. - -* Os nomes dos arquivos devem ser concatenados com o `-` em vez de`_`, por exemplo, `file-name.coffee` em vez de`file_name.coffee`, porque no [github/atom](https://github.com/github/atom) os nomes dos módulos são geralmente em o formulário `module-name`. Esta regra só se aplica aos arquivos com extensão `.coffee`. +* Os nomes dos arquivos devem ser concatenados com `-` em vez de `_`, por exemplo, `file-name.coffee` em vez de `file_name.coffee`, porque no [github/atom](https://github.com/github/atom) os nomes dos módulos são geralmente da forma `module-name`. Esta regra só se aplica aos arquivos com extensão `.coffee`. * -## API Names +## Nomes de APIs -Ao criar uma nova API, devemos preferencialmente utilizar métodos getters e setters em vez de -estilo de uma função do jQuery. Por exemplo, `.getText()` e `.setText(text)` utilize `.text([text])`. Existe uma +Ao criar uma nova API, devemos preferencialmente utilizar métodos getters e setters em vez do +estilo de uma função única do jQuery. Por exemplo, `.getText()` e `.setText(text)` são preferenciais a `.text([text])`. Existe uma [discussão](https://github.com/atom/electron/issues/46) sobre este assunto. diff --git a/docs-translations/pt-BR/tutorial/application-packaging.md b/docs-translations/pt-BR/tutorial/application-packaging.md index f55cbb2f7..209188b2f 100644 --- a/docs-translations/pt-BR/tutorial/application-packaging.md +++ b/docs-translations/pt-BR/tutorial/application-packaging.md @@ -71,7 +71,7 @@ Você também pode renderizar uma página web apartir de um arquivo `asar` utili ```javascript var BrowserWindow = require('browser-window'); var win = new BrowserWindow({width: 800, height: 600}); -win.loadUrl('file:///path/to/example.asar/static/index.html'); +win.loadURL('file:///path/to/example.asar/static/index.html'); ``` ### API Web @@ -155,4 +155,4 @@ Depois de executar o comando, além do `app.asar`, há também `app.asar.unpacked` pasta gerada que contém os arquivos descompactados, você deve copiá-lo juntamente com `app.asar` quando enviá-lo para os usuários. -Mais informações no repositório [asar](https://github.com/atom/asar) \ No newline at end of file +Mais informações no repositório [asar](https://github.com/atom/asar) diff --git a/docs-translations/pt-BR/tutorial/online-offline-events.md b/docs-translations/pt-BR/tutorial/online-offline-events.md index 294a62e7a..8cdc1a664 100644 --- a/docs-translations/pt-BR/tutorial/online-offline-events.md +++ b/docs-translations/pt-BR/tutorial/online-offline-events.md @@ -13,7 +13,7 @@ var onlineStatusWindow; app.on('ready', function() { onlineStatusWindow = new BrowserWindow({ width: 0, height: 0, show: false }); - onlineStatusWindow.loadUrl('file://' + __dirname + '/online-status.html'); + onlineStatusWindow.loadURL('file://' + __dirname + '/online-status.html'); }); ``` @@ -53,7 +53,7 @@ var onlineStatusWindow; app.on('ready', function() { onlineStatusWindow = new BrowserWindow({ width: 0, height: 0, show: false }); - onlineStatusWindow.loadUrl('file://' + __dirname + '/online-status.html'); + onlineStatusWindow.loadURL('file://' + __dirname + '/online-status.html'); }); ipc.on('online-status-changed', function(event, status) { diff --git a/docs-translations/pt-BR/tutorial/quick-start.md b/docs-translations/pt-BR/tutorial/quick-start.md index f9883144c..1b1d69423 100644 --- a/docs-translations/pt-BR/tutorial/quick-start.md +++ b/docs-translations/pt-BR/tutorial/quick-start.md @@ -4,7 +4,7 @@ Electron permite criar aplicações desktop com puro JavaScript através de um runtime com APIs ricas e nativas. Você pode ver isso como uma variação do runtime do io.js que é focado em aplicações desktop em vez de web servers. -Isso não significa que o Electron é uma ligação em JavaScript para blibliotécas +Isso não significa que o Electron é uma ligação em JavaScript para bibliotecas de interface gráfica (GUI). Em vez disso, Electron usa páginas web como interface gráfica, então você pode ver isso também como um navegador Chromium mínimo, controlado por JavaScript. @@ -17,13 +17,13 @@ mostrar uma GUI criando páginas web. ### Processo Renderizador -Desde que o Electron usa o Chromium para mostrar as páginas web, a arquitetura +Já que o Electron usa o Chromium para mostrar as páginas web, a arquitetura multi-processo do Chromium também é usada. Cada página web no Electron roda em seu próprio processo, o que é chamado de __processo renderizador__. Em navegadores comuns, as páginas web normalmente rodam em um ambiente em sandbox -e não tem permissão de acesso para recursos nativos. Usuários Electron, entretanto, -tem o poder de usar as APIs do io.js nas páginas web, permitindo interações de baixo +e não têm permissão de acesso para recursos nativos. Usuários Electron, entretanto, +têm o poder de usar as APIs do io.js nas páginas web, permitindo interações de baixo nível no sistema operacional. ### Diferenças Entre o Processo Principal e o Processo Renderizador @@ -33,12 +33,12 @@ Cada instância de `BrowserWindow` roda a página web em seu próprio processo r Quando uma instância de `BrowserWindow` é destruída, o processo renderizador correspondente também é finalizado. -O processo principal gerência todas as páginas web de seus processos renderizadores +O processo principal gerencia todas as páginas web de seus processos renderizadores correspondentes. Cada processo renderizador é isolado e toma conta de sua respectiva página web. Nas páginas web, chamar APIs nativas relacionadas à GUI não é permitido porque -gerênciar recursos de GUI em páginas web é muito perigoso e torna fácil o vazamento de +gerenciar recursos de GUI em páginas web é muito perigoso e torna fácil o vazamento de recursos. Se você quer realizar operações com GUI em páginas web, o processo renderizador da página web deve se comunicar com o processo principal para requisitar que o processo principal realize estas operações. @@ -52,26 +52,26 @@ módulo [remoto](../../../docs/api/remote.md) para comunicação RPC. Geralmente, um app Electron é estruturado assim: ```text -your-app/ +seu-app/ ├── package.json ├── main.js └── index.html ``` -O formato de `package.json` é exatamente o mesmo que os dos módulos do Node, e +O formato de `package.json` é exatamente o mesmo que o dos módulos do Node, e o script especificado pelo campo `main` é o script de inicialização do seu app, que irá executar o processo principal. Um exemplo do seu `package.json` deve parecer com isso: ```json { - "name" : "your-app", + "name" : "seu-app", "version" : "0.1.0", "main" : "main.js" } ``` -__Nota__: Se o campo `main` não estiver presente no `package.jso`, o Electron irá +__Nota__: Se o campo `main` não estiver presente no `package.json`, o Electron irá tentar carregar um `index.js` O `main.js` deve criar as janelas e os manipuladores de eventos do sistema, um típico @@ -81,9 +81,6 @@ exemplo: var app = require('app'); // Módulo para controlar o ciclo de vida do app. var BrowserWindow = require('browser-window'); // Módulo para criar uma janela nativa do browser. -// Relate falhas para nossos servidores. -require('crash-reporter').start(); - // Mantenha uma referência global para o objeto window, se você não o fizer, // a janela será fechada automaticamente quando o objeto JavaScript for // coletado pelo garbage collector. @@ -140,8 +137,8 @@ Finalmente o `index.html` é a página web que você quer mostrar: ## Execute seu App -Uma vez que você criou seus arquivos `main.js`, `index.html, e `package.json` iniciais, -você provavelmente vai querer tentar executar seu app localmente para testa-lo a ter +Uma vez que você criou seus arquivos `main.js`, `index.html`, e `package.json` iniciais, +você provavelmente vai querer tentar executar seu app localmente para testa-lo e ter certeza que funciona como você espera. ### electron-prebuilt @@ -167,19 +164,19 @@ executar seu app diretamente. #### Windows ```bash -$ .\electron\electron.exe your-app\ +$ .\electron\electron.exe seu-app\ ``` #### Linux ```bash -$ ./electron/electron your-app/ +$ ./electron/electron seu-app/ ``` #### OS X ```bash -$ ./Electron.app/Contents/MacOS/Electron your-app/ +$ ./Electron.app/Contents/MacOS/Electron seu-app/ ``` `Electron.app` aqui é uma parte do pacote de lançamento do Electron, você pode baixa-lo @@ -188,5 +185,5 @@ $ ./Electron.app/Contents/MacOS/Electron your-app/ ### Executar como uma distribuição Depois de terminar seu app, você pode criar uma distribuição seguindo o guia -[Application Distribution](./application-distribution.md) e então executar o app +[Distribuição de aplicações](./application-distribution.md) e então executar o app empacotado. diff --git a/docs-translations/pt-BR/tutorial/using-pepper-flash-plugin.md b/docs-translations/pt-BR/tutorial/using-pepper-flash-plugin.md index dfcca01a5..7ffba7cc3 100644 --- a/docs-translations/pt-BR/tutorial/using-pepper-flash-plugin.md +++ b/docs-translations/pt-BR/tutorial/using-pepper-flash-plugin.md @@ -23,9 +23,6 @@ Por exemplo: var app = require('app'); var BrowserWindow = require('browser-window'); -// Informa os erros ao ao servidor. -require('crash-reporter').start(); - // Mantém uma referência global da janela, se não manter, a janela irá fechar // automaticamente quando o objeto javascript for GCed. var mainWindow = null; @@ -54,7 +51,7 @@ app.on('ready', function() { 'plugins': true } }); - mainWindow.loadUrl('file://' + __dirname + '/index.html'); + mainWindow.loadURL('file://' + __dirname + '/index.html'); // Algo mais }); ``` diff --git a/docs-translations/ru-RU/README.md b/docs-translations/ru-RU/README.md new file mode 100644 index 000000000..07e50028d --- /dev/null +++ b/docs-translations/ru-RU/README.md @@ -0,0 +1,82 @@ +Пожалуйста, убедитесь, что вы используете документацию, которые соответствует вашей версии Electron. +Номер версии должен быть частью адреса страницы. Если это не так, вы +возможно,используете документицию ветки разработки, которая может содержать изменения api, +которые не совместимы с вашей версией Electron. Если это так, +Вы можете переключиться на другую версию документации в списке +[доступные версии](http://electron.atom.io/docs/) на atom.io, или +если вы используете интерфейс GitHub, откройте список "переключение ветки/тега" и +выберите тег, который соответствует вашей версии. + +## Руководства + +* [Поддерживаемые платформы](tutorial/supported-platforms.md) +* [Application Distribution](tutorial/application-distribution.md) +* [Mac App Store Submission Guide](tutorial/mac-app-store-submission-guide.md) +* [Application Packaging](tutorial/application-packaging.md) +* [Using Native Node Modules](tutorial/using-native-node-modules.md) +* [Отладка главного процесса](tutorial/debugging-main-process.md) +* [Использование Selenium и WebDriver](tutorial/using-selenium-and-webdriver.md) +* [DevTools Extension](tutorial/devtools-extension.md) +* [Использование Pepper Flash Plugin](tutorial/using-pepper-flash-plugin.md) + +## Учебники + +* [Быстрый старт](tutorial/quick-start.md) +* [Desktop Environment Integration](tutorial/desktop-environment-integration.md) +* [Online/Offline Event Detection](tutorial/online-offline-events.md) + +## API References + +* [Краткий обзор](api/synopsis.md) +* [Process Object](api/process.md) +* [Поддерживаемые параметры командной строки Chrome](api/chrome-command-line-switches.md) + +### Пользовательские элементы DOM: + +* [`File` Object](api/file-object.md) +* [`` Tag](api/web-view-tag.md) +* [`window.open` Function](api/window-open.md) + +### Modules for the Main Process: + +* [app](api/app.md) +* [autoUpdater](api/auto-updater.md) +* [BrowserWindow](api/browser-window.md) +* [contentTracing](api/content-tracing.md) +* [dialog](api/dialog.md) +* [globalShortcut](api/global-shortcut.md) +* [ipcMain](api/ipc-main.md) +* [Menu](api/menu.md) +* [MenuItem](api/menu-item.md) +* [powerMonitor](api/power-monitor.md) +* [powerSaveBlocker](api/power-save-blocker.md) +* [protocol](api/protocol.md) +* [session](api/session.md) +* [webContents](api/web-contents.md) +* [Tray](api/tray.md) + +### Модули для Renderer Process (Web Page): + +* [ipcRenderer](api/ipc-renderer.md) +* [remote](api/remote.md) +* [webFrame](api/web-frame.md) + +### Modules for Both Processes: + +* [clipboard](api/clipboard.md) +* [crashReporter](api/crash-reporter.md) +* [nativeImage](api/native-image.md) +* [screen](api/screen.md) +* [shell](api/shell.md) + +## Разработка + +* [Стиль кодирования](development/coding-style.md) +* [Source Code Directory Structure](development/source-code-directory-structure.md) +* [Technical Differences to NW.js (formerly node-webkit)](development/atom-shell-vs-node-webkit.md) +* [Обзор системы сборки](development/build-system-overview.md) +* [Инструкции по сборке (OS X)](development/build-instructions-osx.md) +* [Инструкции по сборке (Windows)](development/build-instructions-windows.md) +* [Инструкции по сборке (Linux)](development/build-instructions-linux.md) +* [Настройка сервера символов для отладчика](development/setting-up-symbol-server.md) + diff --git a/docs-translations/uk-UA/README.md b/docs-translations/uk-UA/README.md new file mode 100644 index 000000000..ef503a884 --- /dev/null +++ b/docs-translations/uk-UA/README.md @@ -0,0 +1,83 @@ +Будь ласка, переконайтесь що ви використовуєте документацію, яка відповідає вашій версії Electron. +Номер версії повинен бути присутнім в URL адресі сторінки. Якщо це не так, Ви можливо, +використовуєте документацію із development гілки, +яка може містити зміни в API, які не сумісні з вашою версією Electron. +Якщо це так, тоді Ви можете переключитись на іншу версію документації +із списку [доступних версій](http://electron.atom.io/docs/) на atom.io, +або якщо ви використовуєте інтеррфейс GitHub, +тоді відкрийте список "Switch branches/tags" і виберіть потрібну вам +версію із списку тегів. + +## Довідник + +* [Підтримувані платформи](tutorial/supported-platforms.md) +* [Application Distribution](tutorial/application-distribution.md) +* [Mac App Store Submission Guide](tutorial/mac-app-store-submission-guide.md) +* [Упаковка додатку](tutorial/application-packaging.md) +* [Використання Native Node модулів](tutorial/using-native-node-modules.md) +* [Debugging Main Process](tutorial/debugging-main-process.md) +* [Використання Selenium and WebDriver](tutorial/using-selenium-and-webdriver.md) +* [DevTools Extension](tutorial/devtools-extension.md) +* [Використання Pepper Flash плагіну](tutorial/using-pepper-flash-plugin.md) + +## Підручники + +* [Швидкий старт](tutorial/quick-start.md) +* [Desktop Environment Integration](tutorial/desktop-environment-integration.md) +* [Online/Offline Event Detection](tutorial/online-offline-events.md) + +## API References + +* [Synopsis](api/synopsis.md) +* [Process Object](api/process.md) +* [Supported Chrome Command Line Switches](api/chrome-command-line-switches.md) +* [Environment Variables](api/environment-variables.md) + +### Користувальницькі елементи DOM + +* [`File` Object](api/file-object.md) +* [`` Tag](api/web-view-tag.md) +* [`window.open` Function](api/window-open.md) + +### Модулі для Main Process: + +* [app](api/app.md) +* [autoUpdater](api/auto-updater.md) +* [BrowserWindow](api/browser-window.md) +* [contentTracing](api/content-tracing.md) +* [dialog](api/dialog.md) +* [globalShortcut](api/global-shortcut.md) +* [ipcMain](api/ipc-main.md) +* [Menu](api/menu.md) +* [MenuItem](api/menu-item.md) +* [powerMonitor](api/power-monitor.md) +* [powerSaveBlocker](api/power-save-blocker.md) +* [protocol](api/protocol.md) +* [session](api/session.md) +* [webContents](api/web-contents.md) +* [Tray](api/tray.md) + +### Модулі для Renderer Process (Web Page): + +* [ipcRenderer](api/ipc-renderer.md) +* [remote](api/remote.md) +* [webFrame](api/web-frame.md) + +### Модулі для Both Processes: + +* [clipboard](api/clipboard.md) +* [crashReporter](api/crash-reporter.md) +* [nativeImage](api/native-image.md) +* [screen](api/screen.md) +* [shell](api/shell.md) + +## Розробка + +* [Стиль кодування](development/coding-style.md) +* [Source Code Directory Structure](development/source-code-directory-structure.md) +* [Technical Differences to NW.js (formerly node-webkit)](development/atom-shell-vs-node-webkit.md) +* [Build System Overview](development/build-system-overview.md) +* [Build Instructions (OS X)](development/build-instructions-osx.md) +* [Build Instructions (Windows)](development/build-instructions-windows.md) +* [Build Instructions (Linux)](development/build-instructions-linux.md) +* [Setting Up Symbol Server in debugger](development/setting-up-symbol-server.md) diff --git a/docs-translations/uk-UA/styleguide.md b/docs-translations/uk-UA/styleguide.md new file mode 100644 index 000000000..041596700 --- /dev/null +++ b/docs-translations/uk-UA/styleguide.md @@ -0,0 +1,97 @@ +# Electron Documentation Styleguide + +Find the appropriate section for your task: [reading Electron documentation](#reading-electron-documentation) +or [writing Electron documentation](#writing-electron-documentation). + +## Написання документації для Electron + +Існує кілька способів за допомогою яких ми ствоюємо документацію для Electron. + +- Maximum one `h1` title per page. +- Use `bash` instead of `cmd` in code blocks (because of syntax highlighter). +- Doc `h1` titles should match object name (i.e. `browser-window` → + `BrowserWindow`). + - Hyphen separated filenames, however, are fine. +- No headers following headers, add at least a one-sentence description. +- Methods headers are wrapped in `code` ticks. +- Event headers are wrapped in single 'quotation' marks. +- No nesting lists more than 2 levels (unfortunately because of markdown + renderer). +- Add section titles: Events, Class Methods and Instance Methods. +- Use 'will' over 'would' when describing outcomes. +- Events and methods are `h3` headers. +- Optional arguments written as `function (required[, optional])`. +- Optional arguments are denoted when called out in list. +- Line length is 80-column wrapped. +- Platform specific methods are noted in italics following method header. + - ```### `method(foo, bar)` _OS X_``` +- Prefer 'in the ___ process' over 'on' + +### Переклад документації + +Переклади документації знаходяться в дерикторії `docs-translations`. + +Щоб додати переклад (або частковий переклад) документації: + +- Create a subdirectory named by language abbreviation. +- Within that subdirectory, duplicate the `docs` directory, keeping the + names of directories and files same. +- Translate the files. +- Update the `README.md` within your language directory to link to the files + you have translated. +- Add a link to your translation directory on the main Electron [README](https://github.com/atom/electron#documentation-translations). + +## Читання документації Electron + +Кілька порад для того щоб легше зрозуміти синтаксис документації Electron. + +### Методи + +Приклад [методу](https://developer.mozilla.org/en-US/docs/Glossary/Method) +в документації: + +--- + +`methodName(required[, optional]))` + +* `require` String, **required** +* `optional` Integer + +--- + +The method name is followed by the arguments it takes. Optional arguments are +notated by brackets surrounding the optional argument as well as the comma +required if this optional argument follows another argument. + +Below the method is more detailed information on each of the arguments. The type +of argument is notated by either the common types: +[`String`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String), +[`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number), +[`Object`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object), +[`Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) +or a custom type like Electron's [`webContent`](api/web-content.md). + +### Події + +Приклад [події](https://developer.mozilla.org/en-US/docs/Web/API/Event) +в документації: + +--- + +Подія: 'wake-up' + +Повердає: + +* `time` Текст + +--- + +The event is a string that is used after a `.on` listener method. If it returns +a value it and its type is noted below. If you were to listen and respond to +this event it might look something like this: + +```javascript +Alarm.on('wake-up', function(time) { + console.log(time) +}) +``` diff --git a/docs-translations/zh-CN/development/atom-shell-vs-node-webkit.md b/docs-translations/zh-CN/development/atom-shell-vs-node-webkit.md index 9774580ee..beea91e3f 100644 --- a/docs-translations/zh-CN/development/atom-shell-vs-node-webkit.md +++ b/docs-translations/zh-CN/development/atom-shell-vs-node-webkit.md @@ -12,7 +12,7 @@ __1. 应用的入口__ 在 Electron 中,入口是一个 JavaScript 脚本。不同于直接提供一个URL,你需要手动创建一个浏览器窗口,然后通过 API 加载 HTML 文件。你还可以监听窗口事件,决定何时让应用退出。 -Electron 的工作方式更像 Node.js 运行时。 Electron 的 APIs 更加底层,因此你可以它替代 [PhantomJS](http://phantomjs.org/) 做浏览器测试。 +Electron 的工作方式更像 Node.js 运行时。 Electron 的 APIs 更加底层,因此你可以用它替代 [PhantomJS](http://phantomjs.org/) 做浏览器测试。 __2. 构建系统__ diff --git a/docs-translations/zh-CN/tutorial/application-packaging.md b/docs-translations/zh-CN/tutorial/application-packaging.md new file mode 100644 index 000000000..ee4b7bf63 --- /dev/null +++ b/docs-translations/zh-CN/tutorial/application-packaging.md @@ -0,0 +1,141 @@ +# 应用打包 + +为舒缓Windows下路径名过长的问题[issues](https://github.com/joyent/node/issues/6960), 也略对`require`加速以及简单隐匿你的源代码, 你可以通过极小的源代码改动将你的应用打包成[asar][asar]. + +## 生成`asar`包 + +[asar][asar]是一种将多个文件合并成一个文件的类tar风格的归档格式。 Electron可以无需解压,即从其中读取任意文件内容。 + +参照如下步骤将你的应用打包成`asar`: + +### 1. 安装asar + +```bash +$ npm install -g asar +``` + +### 2. 用`asar pack`打包 + +```bash +$ asar pack your-app app.asar +``` + +## 使用`asar`包 + +在Electron中有两类APIs:Node.js提供的Node APIs和Chromium提供的Web APIs。这两种APIs都支持从`asar`包中读取文件。 + +### Node API + +由于Electron中打了特别补丁, Node APIs中如`fs.readFile`或者`require`之类的方法可以将`asar`视之为虚拟文件夹,读取`asar`里面的文件就和从真实的文件系统中读取一样。 + +例如,假设我们在`/path/to`文件夹下有个`example.asar`包: + +```bash +$ asar list /path/to/example.asar +/app.js +/file.txt +/dir/module.js +/static/index.html +/static/main.css +/static/jquery.min.js +``` + +从`asar`包读取一个文件: + +```javascript +const fs = require('fs'); +fs.readFileSync('/path/to/example.asar/file.txt'); +``` + +列出`asar`包中根目录下的所有文件: + +```javascript +const fs = require('fs'); +fs.readdirSync('/path/to/example.asar'); +``` + +使用`asar`包中的一个模块: + +```javascript +require('/path/to/example.asar/dir/module.js'); +``` + +你也可以使用`BrowserWindow`来显示一个`asar`包里的web页面: + +```javascript +const BrowserWindow = require('electron').BrowserWindow; +var win = new BrowserWindow({width: 800, height: 600}); +win.loadURL('file:///path/to/example.asar/static/index.html'); +``` + +### Web API + +在Web页面里,用`file:`协议可以获取`asar`包中文件。和Node API一样,视`asar`包如虚拟文件夹。 + +例如,用`$.get`获取文件: + +```html + +``` + +### 像“文件”那样处理`asar`包 + +有些场景,如:核查`asar`包的校验和,我们需要像读取“文件”那样读取`asar`包的内容(而不是当成虚拟文件夹)。你可以使用内置的`original-fs`(提供和`fs`一样的APIs)模块来读取`asar`包的真实信息。 + +```javascript +var originalFs = require('original-fs'); +originalFs.readFileSync('/path/to/example.asar'); +``` + +## Node API缺陷 + +尽管我们已经尽了最大努力使得`asar`包在Node API下的应用尽可能的趋向于真实的目录结构,但仍有一些底层Node API我们无法保证其正常工作。 + +### `asar`包是只读的 + +`asar`包中的内容不可更改,所以Node APIs里那些可以用来修改文件的方法在对待`asar`包时都无法正常工作。 + +### Working Directory在`asar`包中无效 + +尽管`asar`包是虚拟文件夹,但其实并没有真实的目录架构对应在文件系统里,所以你不可能将working Directory设置成`asar`包里的一个文件夹。将`asar`中的文件夹以`cwd`形式作为参数传入一些API中也会报错。 + +### API中的额外“开箱” + +大部分`fs`API可以无需解压即从`asar`包中读取文件或者文件的信息,但是在处理一些依赖真实文件路径的底层系统方法时,Electron会将所需文件解压到临时目录下,然后将临时目录下的真实文件路径传给底层系统方法使其正常工作。 对于这类API,耗费会略多一些。 + +以下是一些需要额外解压的APIs: + +* `child_process.execFile` +* `child_process.execFileSync` +* `fs.open` +* `fs.openSync` +* `process.dlopen` - `require`native模块时用到 + +### `fs.stat`获取的stat信息不可靠 + +对`asar`包中的文件取`fs.stat`,返回的`Stats`对象不是精确值,因为这些文件不是真实存在于文件系统里。所以除了文件大小和文件类型以外,你不应该依赖`Stats`对象的值。 + +### 执行`asar`包中的程序 + +Node中有一些可以执行程序的API,如`child_process.exec`,`child_process.spawn`和`child_process.execFile`等,但只有`execFile`可以执行`asar`包中的程序。 + +因为`exec`和`spawn`允许`command`替代`file`作为输入,而`command`是需要在shell下执行的,目前没有可靠的方法来判断`command`中是否在操作一个`asar`包中的文件,而且即便可以判断,我们依旧无法保证可以在无任何副作用的情况下替换`command`中的文件路径。 + +## 打包时排除文件 + +如上所述,一些Node API会在调用时将文件解压到文件系统中,除了效率问题外,也有可能引起杀毒软件的注意! + +为解决这个问题,你可以在生成`asar`包时使用`--unpack`选项来排除一些文件,使其不打包到`asar`包中,下面是如何排除一些用作共享用途的native模块的方法: + +```bash +$ asar pack app app.asar --unpack *.node +``` + +经过上述命令后,除了生成的`app.asar`包以外,还有一个包含了排除文件的`app.asar.unpacked`文件夹,你需要将这个文件夹一起拷贝,提供给用户。 + +[asar]: https://github.com/atom/asar diff --git a/docs-translations/zh-CN/tutorial/desktop-environment-integration.md b/docs-translations/zh-CN/tutorial/desktop-environment-integration.md index f8fbdaaaf..6ded33162 100644 --- a/docs-translations/zh-CN/tutorial/desktop-environment-integration.md +++ b/docs-translations/zh-CN/tutorial/desktop-environment-integration.md @@ -14,14 +14,14 @@ Windows 和 OS X 提供获取最近文档列表的便捷方式,那就是打开 为了增加一个文件到最近文件列表,你可以使用 [app.addRecentDocument][3] API: -```` +```javascript var app = require('app'); app.addRecentDocument('/Users/USERNAME/Desktop/work.type'); -```` +``` 或者你也可以使用 [app.clearRecentDocuments][4] API 来清空最近文件列表。 -```` +```javascript app.clearRecentDocuments(); -```` +``` ## Windows 需注意 为了这个特性在 Windows 上表现正常,你的应用需要被注册成为一种文件类型的句柄,否则,在你注册之前,文件不会出现在跳转列表。你可以在 [Application Registration][5] 里找到任何关于注册事宜的说明。 @@ -34,7 +34,7 @@ OS X 可以让开发者定制自己的菜单,通常会包含一些常用特性 [Dock menu of Terminal.app][6] 使用 `app.dock.setMenu` API 来设置你的菜单,这仅在 OS X 上可行: -```` +```javascript var app = require('app'); var Menu = require('menu'); var dockMenu = Menu.buildFromTemplate([ @@ -46,7 +46,7 @@ var dockMenu = Menu.buildFromTemplate([ { label: 'New Command...'} ]); app.dock.setMenu(dockMenu); -```` +``` ## 用户任务(Windows) 在 Windows,你可以特别定义跳转列表的 `Tasks` 目录的行为,引用 MSDN 的文档: @@ -60,7 +60,7 @@ app.dock.setMenu(dockMenu); 不同于 OS X 的鱼眼菜单,Windows 上的用户任务表现得更像一个快捷方式,比如当用户点击一个任务,一个程序将会被传入特定的参数并且运行。 你可以使用 [app.setUserTasks][8] API 来设置你的应用中的用户任务: -```` +```javascript var app = require('app'); app.setUserTasks([ { @@ -72,11 +72,11 @@ app.setUserTasks([ description: 'Create a new window' } ]); -```` +``` 调用 `app.setUserTasks` 并传入空数组就可以清除你的任务列表: -```` +```javascript app.setUserTasks([]); -```` +``` 当你的应用关闭时,用户任务会仍然会出现,在你的应用被卸载前,任务指定的图标和程序的路径必须是存在的。 ### 缩略图工具栏 @@ -90,7 +90,7 @@ app.setUserTasks([]); ### Windows Media Player 的缩略图工具栏 ![Thumbnail toolbar of Windows Media Player][9] 你可以使用 [BrowserWindow.setThumbarButtons][10] 来设置你的应用的缩略图工具栏。 -```` +```javascript var BrowserWindow = require('browser-window'); var path = require('path'); var win = new BrowserWindow({ @@ -110,11 +110,11 @@ win.setThumbarButtons([ click: function() { console.log("button2 clicked."); } } ]); -```` +``` 调用 `BrowserWindow.setThumbarButtons` 并传入空数组即可清空缩略图工具栏: -```` +```javascript win.setThumbarButtons([]); -```` +``` ## Unity launcher 快捷方式(Linux) 在 Unity,你可以通过改变 `.desktop` 文件来增加自定义运行器的快捷方式,详情看 [Adding shortcuts to a launcher][11]。 @@ -132,20 +132,20 @@ Unity DE 也具有同样的特性,在运行器上显示进度条。 ![Progress bar in Unity launcher][14] 给一个窗口设置进度条,你可以调用 [BrowserWindow.setProgressBar][15] API: -```` +```javascript var window = new BrowserWindow({...}); window.setProgressBar(0.5); -```` +``` 在 OS X,一个窗口可以设置它展示的文件,文件的图标可以出现在标题栏,当用户 Command-Click 或者 Control-Click 标题栏,文件路径弹窗将会出现。 ### 展示文件弹窗菜单: ![Represented file popup menu][16] 你可以调用 [BrowserWindow.setRepresentedFilename][17] 和 [BrowserWindow.setDocumentEdited][18] APIs: -```` +```javascript var window = new BrowserWindow({...}); window.setRepresentedFilename('/etc/passwd'); window.setDocumentEdited(true); -```` +``` [1]:https://camo.githubusercontent.com/3310597e01f138b1d687e07aa618c50908a88dec/687474703a2f2f692e6d73646e2e6d6963726f736f66742e636f6d2f64796e696d672f49433432303533382e706e67 [2]: https://cloud.githubusercontent.com/assets/639601/5069610/2aa80758-6e97-11e4-8cfb-c1a414a10774.png @@ -164,4 +164,4 @@ window.setDocumentEdited(true); [15]: https://github.com/atom/electron/blob/master/docs-translations/zh-CN/api/browser-window.md [16]: https://cloud.githubusercontent.com/assets/639601/5082061/670a949a-6f14-11e4-987a-9aaa04b23c1d.png [17]: https://github.com/atom/electron/blob/master/docs-translations/zh-CN/api/browser-window.md - [18]: https://github.com/atom/electron/blob/master/docs-translations/zh-CN/api/browser-window.md \ No newline at end of file + [18]: https://github.com/atom/electron/blob/master/docs-translations/zh-CN/api/browser-window.md diff --git a/docs-translations/zh-CN/tutorial/devtools-extension.md b/docs-translations/zh-CN/tutorial/devtools-extension.md new file mode 100644 index 000000000..9a2d9fc88 --- /dev/null +++ b/docs-translations/zh-CN/tutorial/devtools-extension.md @@ -0,0 +1,45 @@ +# DevTools扩展 + +为了使调试更容易,Electron原生支持[Chrome DevTools Extension][devtools-extension]。 + +对于大多数DevTools的扩展,你可以直接下载源码,然后通过`BrowserWindow.addDevToolsExtension`API加载它们。Electron会记住已经加载了哪些扩展,所以你不需要每次创建一个新window时都调用`BrowserWindow.addDevToolsExtension`API。 + +** 注:React DevTools目前不能直接工作,详情留意[https://github.com/atom/electron/issues/915](https://github.com/atom/electron/issues/915) ** + +例如,要用[React DevTools Extension](https://github.com/facebook/react-devtools),你得先下载他的源码: + +```bash +$ cd /some-directory +$ git clone --recursive https://github.com/facebook/react-devtools.git +``` + +参考[`react-devtools/shells/chrome/Readme.md`](https://github.com/facebook/react-devtools/blob/master/shells/chrome/Readme.md)来编译这个扩展源码。 + +然后你就可以在任意页面的DevTools里加载React DevTools了,通过控制台输入如下命令加载扩展: + +```javascript +const BrowserWindow = require('electron').remote.BrowserWindow; +BrowserWindow.addDevToolsExtension('/some-directory/react-devtools/shells/chrome'); +``` + +要卸载扩展,可以调用`BrowserWindow.removeDevToolsExtension`API(扩展名作为参数传入),该扩展在下次打开DevTools时就不会加载了: + +```javascript +BrowserWindow.removeDevToolsExtension('React Developer Tools'); +``` + +## DevTools扩展的格式 + +理论上,Electron可以加载所有为chrome浏览器编写的DevTools扩展,但它们必须存放在文件夹里。那些以`crx`形式发布的扩展是不能被加载的,除非你把它们解压到一个文件夹里。 + +## 后台运行(background pages) + +Electron目前并不支持chrome扩展里的后台运行(background pages)功能,所以那些依赖此特性的DevTools扩展在Electron里可能无法正常工作。 + +## `chrome.*` APIs + +有些chrome扩展使用了`chrome.*`APIs,而且这些扩展在Electron中需要额外实现一些代码才能使用,所以并不是所有的这类扩展都已经在Electron中实现完毕了。 + +考虑到并非所有的`chrome.*`APIs都实现完毕,如果DevTools正在使用除了`chrome.devtools.*`之外的其它APIs,这个扩展很可能无法正常工作。你可以通过报告这个扩展的异常信息,这样做方便我们对该扩展的支持。 + +[devtools-extension]: https://developer.chrome.com/extensions/devtools diff --git a/docs-translations/zh-CN/tutorial/online-offline-events.md b/docs-translations/zh-CN/tutorial/online-offline-events.md index de292490f..7389afa4c 100644 --- a/docs-translations/zh-CN/tutorial/online-offline-events.md +++ b/docs-translations/zh-CN/tutorial/online-offline-events.md @@ -2,19 +2,19 @@ 使用标准 HTML5 APIs 可以实现在线和离线事件的探测,就像以下例子: *main.js* -```` +```javascript var app = require('app'); var BrowserWindow = require('browser-window'); var onlineStatusWindow; app.on('ready', function() { onlineStatusWindow = new BrowserWindow({ width: 0, height: 0, show: false }); - onlineStatusWindow.loadUrl('file://' + __dirname + '/online-status.html'); + onlineStatusWindow.loadURL('file://' + __dirname + '/online-status.html'); }); -```` +``` *online-status.html* -```` +```html @@ -30,12 +30,12 @@ app.on('ready', function() { -```` +``` 也会有人想要在主进程也有回应这些事件的实例。然后主进程没有 `navigator` 对象因此不能直接探测在线还是离线。使用 Electron 的进程间通讯工具,事件就可以在主进程被使,就像下面的例子: *main.js* -```` +```javascript var app = require('app'); var ipc = require('ipc'); var BrowserWindow = require('browser-window'); @@ -43,16 +43,16 @@ var onlineStatusWindow; app.on('ready', function() { onlineStatusWindow = new BrowserWindow({ width: 0, height: 0, show: false }); - onlineStatusWindow.loadUrl('file://' + __dirname + '/online-status.html'); + onlineStatusWindow.loadURL('file://' + __dirname + '/online-status.html'); }); ipc.on('online-status-changed', function(event, status) { console.log(status); }); -```` +``` *online-status.html* -```` +```html @@ -69,4 +69,4 @@ ipc.on('online-status-changed', function(event, status) { -```` \ No newline at end of file +``` diff --git a/docs-translations/zh-CN/tutorial/quick-start.md b/docs-translations/zh-CN/tutorial/quick-start.md index 906db8f44..d54dddbfc 100644 --- a/docs-translations/zh-CN/tutorial/quick-start.md +++ b/docs-translations/zh-CN/tutorial/quick-start.md @@ -31,23 +31,20 @@ your-app/ └── index.html ```` `package.json `的格式和 Node 的完全一致,并且那个被 `main` 字段声明的脚本文件是你的应用的启动脚本,它运行在主进程上。你应用里的 `package.json` 看起来应该像: -```` +```json { "name" : "your-app", "version" : "0.1.0", "main" : "main.js" } -```` +``` **注意**:如果 `main` 字段没有在 `package.json` 声明,Electron会优先加载 `index.js`。 `main.js` 应该用于创建窗口和处理系统时间,一个典型的例子如下: -```` +```javascript var app = require('app'); // 控制应用生命周期的模块。 var BrowserWindow = require('browser-window'); // 创建原生浏览器窗口的模块 -// 给我们的服务器发送异常报告。 -require('crash-reporter').start(); - // 保持一个对于 window 对象的全局引用,不然,当 JavaScript 被 GC, // window 会被自动地关闭 var mainWindow = null; @@ -81,9 +78,9 @@ app.on('ready', function() { mainWindow = null; }); }); -```` +``` 最后,你想展示的 `index.html` : -```` +```html @@ -95,35 +92,35 @@ app.on('ready', function() { and Electron . -```` +``` # 运行你的应用 一旦你创建了最初的 `main.js`, `index.html` 和 `package.json` 这几个文件,你可能会想尝试在本地运行并测试,看看是不是和期望的那样正常运行。 ## electron-prebuild 如果你已经用 `npm` 全局安装了 `electron-prebuilt`,你只需要按照如下方式直接运行你的应用: -```` +```bash electron . -```` +``` 如果你是局部安装,那运行: -```` +```bash ./node_modules/.bin/electron . -```` +``` ## 手工下载 Electron 二进制文件 如果你手工下载了 Electron 的二进制文件,你也可以直接使用其中的二进制文件直接运行你的应用。 ### Windows -```` +```bash $ .\electron\electron.exe your-app\ -```` +``` ### Linux -```` +```bash $ ./electron/electron your-app/ -```` +``` ### OS X -```` +```bash $ ./Electron.app/Contents/MacOS/Electron your-app/ -```` +``` `Electron.app` 里面是 Electron 发布包,你可以在[这里][3]下载到。 # 以发行版本运行 diff --git a/docs-translations/zh-CN/tutorial/using-native-node-modules.md b/docs-translations/zh-CN/tutorial/using-native-node-modules.md new file mode 100644 index 000000000..e03e7b894 --- /dev/null +++ b/docs-translations/zh-CN/tutorial/using-native-node-modules.md @@ -0,0 +1,53 @@ +# 使用原生模块 + +Electron同样也支持原生模块,但由于和官方的Node相比使用了不同的V8引擎,如果你想编译原生模块,则需要手动设置Electron的headers的位置。 + +## 原生Node模块的兼容性 + +当Node开始换新的V8引擎版本时,原生模块可能“坏”掉。为确保一切工作正常,你需要检查你想要使用的原生模块是否被Electron内置的Node支持。你可以在[这里](https://github.com/atom/electron/releases)查看Electron内置的Node版本,或者使用`process.version`(参考:[快速入门](https://github.com/atom/electron/blob/master/docs/tutorial/quick-start.md))查看。 + +考虑到[NAN](https://github.com/nodejs/nan/)可以使你的开发更容易对多版本Node的支持,建议使用它来开发你自己的模块。你也可以使用[NAN](https://github.com/nodejs/nan/)来移植旧的模块到新的Node版本,以使它们可以在新的Electron下良好工作。 + +## 如何安装原生模块 + +如下三种方法教你安装原生模块: + +### 最简单方式 + +最简单的方式就是通过[`electron-rebuild`](https://github.com/paulcbetts/electron-rebuild)包重新编译原生模块,它帮你自动完成了下载headers、编译原生模块等步骤: + +```sh +npm install --save-dev electron-rebuild + +# 每次运行"npm install"时,也运行这条命令 +./node_modules/.bin/electron-rebuild + +# 在windows下如果上述命令遇到了问题,尝试这个: +.\node_modules\.bin\electron-rebuild.cmd +``` + +### 通过npm安装 + +你当然也可以通过`npm`安装原生模块。大部分步骤和安装普通模块时一样,除了以下一些系统环境变量你需要自己操作: + +```bash +export npm_config_disturl=https://atom.io/download/atom-shell +export npm_config_target=0.33.1 +export npm_config_arch=x64 +export npm_config_runtime=electron +HOME=~/.electron-gyp npm install module-name +``` + +### 通过node-gyp安装 + +你需要告诉`node-gyp`去哪下载Electron的headers,以及下载什么版本: + +```bash +$ cd /path-to-module/ +$ HOME=~/.electron-gyp node-gyp rebuild --target=0.29.1 --arch=x64 --dist-url=https://atom.io/download/atom-shell +``` + +`HOME=~/.electron-gyp`设置了去哪找开发时的headers。 +`--target=0.29.1`设置了Electron的版本 +`--dist-url=...`设置了Electron的headers的下载地址 +`--arch=x64`设置了该模块为适配64bit操作系统而编译 diff --git a/docs-translations/zh-CN/tutorial/using-selenium-and-webdriver.md b/docs-translations/zh-CN/tutorial/using-selenium-and-webdriver.md new file mode 100644 index 000000000..c05a3190e --- /dev/null +++ b/docs-translations/zh-CN/tutorial/using-selenium-and-webdriver.md @@ -0,0 +1,119 @@ +# 使用Selenium和WebDriver + +引自[ChromeDriver - WebDriver for Chrome][chrome-driver]: + +> WebDriver是一款开源的支持多浏览器的自动化测试工具。它提供了操作网页、用户输入、JavaScript执行等能力。ChromeDriver是一个实现了WebDriver与Chromium联接协议的独立服务。它也是由开发了Chromium和WebDriver的团队开发的。 + +为了能够使`chromedriver`和Electron一起正常工作,我们需要告诉它Electron在哪,并且让它相信Electron就是Chrome浏览器。 + +## 通过WebDriverJs配置 + +[WebDriverJs](https://code.google.com/p/selenium/wiki/WebDriverJs) 是一个可以配合WebDriver做测试的node模块,我们会用它来做个演示。 + +### 1. 启动ChromeDriver + +首先,你要下载`chromedriver`,然后运行以下命令: + +```bash +$ ./chromedriver +Starting ChromeDriver (v2.10.291558) on port 9515 +Only local connections are allowed. +``` + +记住`9515`这个端口号,我们后面会用到 + +### 2. 安装WebDriverJS + +```bash +$ npm install selenium-webdriver +``` + +### 3. 联接到ChromeDriver + +在Electron下使用`selenium-webdriver`和其平时的用法并没有大的差异,只是你需要手动设置连接ChromeDriver,以及Electron的路径: + +```javascript +const webdriver = require('selenium-webdriver'); + +var driver = new webdriver.Builder() + // "9515" 是ChromeDriver使用的端口 + .usingServer('http://localhost:9515') + .withCapabilities({ + chromeOptions: { + // 这里设置Electron的路径 + binary: '/Path-to-Your-App.app/Contents/MacOS/Atom', + } + }) + .forBrowser('electron') + .build(); + +driver.get('http://www.google.com'); +driver.findElement(webdriver.By.name('q')).sendKeys('webdriver'); +driver.findElement(webdriver.By.name('btnG')).click(); +driver.wait(function() { + return driver.getTitle().then(function(title) { + return title === 'webdriver - Google Search'; + }); +}, 1000); + +driver.quit(); +``` + +## 通过WebdriverIO配置 + +[WebdriverIO](http://webdriver.io/)也是一个配合WebDriver用来测试的node模块 + +### 1. 启动ChromeDriver + +首先,下载`chromedriver`,然后运行以下命令: + +```bash +$ chromedriver --url-base=wd/hub --port=9515 +Starting ChromeDriver (v2.10.291558) on port 9515 +Only local connections are allowed. +``` + +记住`9515`端口,后面会用到 + +### 2. 安装WebdriverIO + +```bash +$ npm install webdriverio +``` + +### 3. 连接到ChromeDriver + +```javascript +const webdriverio = require('webdriverio'); +var options = { + host: "localhost", // 使用localhost作为ChromeDriver服务器 + port: 9515, // "9515"是ChromeDriver使用的端口 + desiredCapabilities: { + browserName: 'chrome', + chromeOptions: { + binary: '/Path-to-Your-App/electron', // Electron的路径 + args: [/* cli arguments */] // 可选参数,类似:'app=' + /path/to/your/app/ + } + } +}; + +var client = webdriverio.remote(options); + +client + .init() + .url('http://google.com') + .setValue('#q', 'webdriverio') + .click('#btnG') + .getTitle().then(function(title) { + console.log('Title was: ' + title); + }) + .end(); +``` + +## 工作流程 + +无需重新编译Electron,只要把app的源码放到[Electron的资源目录](https://github.com/atom/electron/blob/master/docs/tutorial/application-distribution.md)里就可直接开始测试了。 + +当然,你也可以在运行Electron时传入参数指定你app的所在文件夹。这步可以免去你拷贝-粘贴你的app到Electron的资源目录。 + +[chrome-driver]: https://sites.google.com/a/chromium.org/chromedriver/ diff --git a/docs-translations/zh-TW/api/synopsis.md b/docs-translations/zh-TW/api/synopsis.md index 484f5e353..e28d1fd8c 100644 --- a/docs-translations/zh-TW/api/synopsis.md +++ b/docs-translations/zh-TW/api/synopsis.md @@ -20,7 +20,7 @@ var window = null; app.on('ready', function() { window = new BrowserWindow({width: 800, height: 600}); - window.loadUrl('https://github.com'); + window.loadURL('https://github.com'); }); ``` diff --git a/docs-translations/zh-TW/tutorial/online-offline-events.md b/docs-translations/zh-TW/tutorial/online-offline-events.md index a4366f88a..234a02710 100644 --- a/docs-translations/zh-TW/tutorial/online-offline-events.md +++ b/docs-translations/zh-TW/tutorial/online-offline-events.md @@ -12,7 +12,7 @@ var onlineStatusWindow; app.on('ready', function() { onlineStatusWindow = new BrowserWindow({ width: 0, height: 0, show: false }); - onlineStatusWindow.loadUrl('file://' + __dirname + '/online-status.html'); + onlineStatusWindow.loadURL('file://' + __dirname + '/online-status.html'); }); ``` @@ -50,7 +50,7 @@ var onlineStatusWindow; app.on('ready', function() { onlineStatusWindow = new BrowserWindow({ width: 0, height: 0, show: false }); - onlineStatusWindow.loadUrl('file://' + __dirname + '/online-status.html'); + onlineStatusWindow.loadURL('file://' + __dirname + '/online-status.html'); }); ipc.on('online-status-changed', function(event, status) { diff --git a/docs-translations/zh-TW/tutorial/quick-start.md b/docs-translations/zh-TW/tutorial/quick-start.md index 18c62c5e7..8c5c701f1 100644 --- a/docs-translations/zh-TW/tutorial/quick-start.md +++ b/docs-translations/zh-TW/tutorial/quick-start.md @@ -62,9 +62,6 @@ __注意__:如果 `main` 沒有在 `package.json` 裏, Electron會嘗試載 var app = require('app'); // 控制應用程式生命週期的模組。 var BrowserWindow = require('browser-window'); // 創造原生瀏覽器窗口的模組 -// 對我們的伺服器傳送異常報告。 -require('crash-reporter').start(); - // 保持一個對於 window 物件的全域的引用,不然,當 JavaScript 被GC, // window 會被自動地關閉 var mainWindow = null; diff --git a/docs/README.md b/docs/README.md index 208ff8bf4..9d2b36eb6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,3 +1,19 @@ +Please make sure that you use the documents that match your Electron version. +The version number should be a part of the page URL. If it's not, you are +probably using the documentation of a development branch which may contain API +changes that are not compatible with your Electron version. If that's the case, +you can switch to a different version of the documentation at the +[available versions](http://electron.atom.io/docs/) list on atom.io, or if +you're using the GitHub interface, open the "Switch branches/tags" dropdown and +select the tag that matches your version. + +## FAQ + +There are questions that are asked quite often, check this out before creating +an issue: + +* [Electron FAQ](faq/electron-faq.md) + ## Guides * [Supported Platforms](tutorial/supported-platforms.md) @@ -9,6 +25,7 @@ * [Using Selenium and WebDriver](tutorial/using-selenium-and-webdriver.md) * [DevTools Extension](tutorial/devtools-extension.md) * [Using Pepper Flash Plugin](tutorial/using-pepper-flash-plugin.md) +* [Using Widevine CDM Plugin](tutorial/using-widevine-cdm-plugin.md) ## Tutorials @@ -21,6 +38,7 @@ * [Synopsis](api/synopsis.md) * [Process Object](api/process.md) * [Supported Chrome Command Line Switches](api/chrome-command-line-switches.md) +* [Environment Variables](api/environment-variables.md) ### Custom DOM Elements: @@ -48,6 +66,7 @@ ### Modules for the Renderer Process (Web Page): +* [desktopCapturer](api/desktop-capturer.md) * [ipcRenderer](api/ipc-renderer.md) * [remote](api/remote.md) * [webFrame](api/web-frame.md) diff --git a/docs/api/app.md b/docs/api/app.md index a1d87d2fe..abe73ab20 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -64,9 +64,14 @@ the `will-quit` and `window-all-closed` events. ### Event: 'quit' +Returns: + +* `event` Event +* `exitCode` Integer + Emitted when the application is quitting. -### Event: 'open-file' +### Event: 'open-file' _OS X_ Returns: @@ -82,7 +87,9 @@ handle this case (even before the `ready` event is emitted). You should call `event.preventDefault()` if you want to handle this event. -### Event: 'open-url' +On Windows, you have to parse `process.argv` to get the filepath. + +### Event: 'open-url' _OS X_ Returns: @@ -332,7 +339,8 @@ Adds `tasks` to the [Tasks][tasks] category of the JumpList on Windows. `tasks` is an array of `Task` objects in the following format: -`Task` Object +`Task` Object: + * `program` String - Path of the program to execute, usually you should specify `process.execPath` which opens the current program. * `arguments` String - The command line arguments when `program` is diff --git a/docs/api/auto-updater.md b/docs/api/auto-updater.md index fa4f92cb9..0a5ca2d2d 100644 --- a/docs/api/auto-updater.md +++ b/docs/api/auto-updater.md @@ -31,7 +31,7 @@ The server-side setup is also different from OS X. You can read the documents of ### Linux -There is not built-in support for auto-updater on Linux, so it is recommended to +There is no built-in support for auto-updater on Linux, so it is recommended to use the distribution's package manager to update your app. ## Events diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 52b1bb8d5..8fb64b561 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -4,8 +4,12 @@ The `BrowserWindow` class gives you the ability to create a browser window. For example: ```javascript +// In the main process. const BrowserWindow = require('electron').BrowserWindow; +// Or in the renderer process. +const BrowserWindow = require('electron').remote.BrowserWindow; + var win = new BrowserWindow({ width: 800, height: 600, show: false }); win.on('closed', function() { win = null; @@ -25,112 +29,129 @@ You can also create a window without chrome by using It creates a new `BrowserWindow` with native properties as set by the `options`. -### `new BrowserWindow(options)` +### `new BrowserWindow([options])` -`options` Object, properties: +* `options` Object + * `width` Integer - Window's width in pixels. Default is `800`. + * `height` Integer - Window's height in pixels. Default is `600`. + * `x` Integer - Window's left offset from screen. Default is to center the + window. + * `y` Integer - Window's top offset from screen. Default is to center the + window. + * `useContentSize` Boolean - The `width` and `height` would be used as web + page's size, which means the actual window's size will include window + frame's size and be slightly larger. Default is `false`. + * `center` Boolean - Show window in the center of the screen. + * `minWidth` Integer - Window's minimum width. Default is `0`. + * `minHeight` Integer - Window's minimum height. Default is `0`. + * `maxWidth` Integer - Window's maximum width. Default is no limit. + * `maxHeight` Integer - Window's maximum height. Default is no limit. + * `resizable` Boolean - Whether window is resizable. Default is `true`. + * `alwaysOnTop` Boolean - Whether the window should always stay on top of + other windows. Default is `false`. + * `fullscreen` Boolean - Whether the window should show in fullscreen. When + set to `false` the fullscreen button will be hidden or disabled on OS X. + Default is `false`. + * `skipTaskbar` Boolean - Whether to show the window in taskbar. Default is + `false`. + * `kiosk` Boolean - The kiosk mode. Default is `false`. + * `title` String - Default window title. Default is `"Electron"`. + * `icon` [NativeImage](native-image.md) - The window icon, when omitted on + Windows the executable's icon would be used as window icon. + * `show` Boolean - Whether window should be shown when created. Default is + `true`. + * `frame` Boolean - Specify `false` to create a + [Frameless Window](frameless-window.md). Default is `true`. + * `acceptFirstMouse` Boolean - Whether the web view accepts a single + mouse-down event that simultaneously activates the window. Default is `false`. + * `disableAutoHideCursor` Boolean - Whether to hide cursor when typing. Default + is `false`. + * `autoHideMenuBar` Boolean - Auto hide the menu bar unless the `Alt` + key is pressed. Default is `false`. + * `enableLargerThanScreen` Boolean - Enable the window to be resized larger + than screen. Default is `false`. + * `backgroundColor` String - Window's background color as Hexadecimal value, + like `#66CD00` or `#FFF`. This is only implemented on Linux and Windows. + Default is `#000` (black). + * `darkTheme` Boolean - Forces using dark theme for the window, only works on + some GTK+3 desktop environments. Default is `false`. + * `transparent` Boolean - Makes the window [transparent](frameless-window.md). + Default is `false`. + * `type` String - The type of window, default is normal window. See more about + this bellow. + * `titleBarStyle` String - The style of window title bar. See more about this + bellow. + * `webPreferences` Object - Settings of web page's features. See more about + this bellow. -* `width` Integer - Window's width. -* `height` Integer - Window's height. -* `x` Integer - Window's left offset from screen. -* `y` Integer - Window's top offset from screen. -* `useContentSize` Boolean - The `width` and `height` would be used as web - page's size, which means the actual window's size will include window - frame's size and be slightly larger. -* `center` Boolean - Show window in the center of the screen. -* `minWidth` Integer - Window's minimum width. -* `minHeight` Integer - Window's minimum height. -* `maxWidth` Integer - Window's maximum width. -* `maxHeight` Integer - Window's maximum height. -* `resizable` Boolean - Whether window is resizable. -* `alwaysOnTop` Boolean - Whether the window should always stay on top of - other windows. -* `fullscreen` Boolean - Whether the window should show in fullscreen. When - set to `false` the fullscreen button will be hidden or disabled on OS X. -* `skipTaskbar` Boolean - Whether to show the window in taskbar. -* `kiosk` Boolean - The kiosk mode. -* `title` String - Default window title. -* `icon` [NativeImage](native-image.md) - The window icon, when omitted on - Windows the executable's icon would be used as window icon. -* `show` Boolean - Whether window should be shown when created. -* `frame` Boolean - Specify `false` to create a -[Frameless Window](frameless-window.md). -* `acceptFirstMouse` Boolean - Whether the web view accepts a single - mouse-down event that simultaneously activates the window. -* `disableAutoHideCursor` Boolean - Whether to hide cursor when typing. -* `autoHideMenuBar` Boolean - Auto hide the menu bar unless the `Alt` - key is pressed. -* `enableLargerThanScreen` Boolean - Enable the window to be resized larger - than screen. -* `backgroundColor` String - Window's background color as Hexadecimal value, - like `#66CD00` or `#FFF`. This is only implemented on Linux and Windows. -* `darkTheme` Boolean - Forces using dark theme for the window, only works on - some GTK+3 desktop environments. -* `transparent` Boolean - Makes the window [transparent](frameless-window.md). -* `type` String - Specifies the type of the window, which applies - additional platform-specific properties. - * On Linux, possible types are `desktop`, `dock`, `toolbar`, `splash`, - `notification`. - * On OS X, possible types are `desktop`, `textured`. The `textured` type adds - metal gradient appearance (`NSTexturedBackgroundWindowMask`). The `desktop` - type places the window at the desktop background window level +The possible values and behaviors of `type` option are platform dependent, +supported values are: + +* On Linux, possible types are `desktop`, `dock`, `toolbar`, `splash`, + `notification`. +* On OS X, possible types are `desktop`, `textured`. + * The `textured` type adds metal gradient appearance + (`NSTexturedBackgroundWindowMask`). + * The `desktop` type places the window at the desktop background window level (`kCGDesktopWindowLevel - 1`). Note that desktop window will not receive focus, keyboard or mouse events, but you can use `globalShortcut` to receive input sparingly. -* `titleBarStyle` String, OS X - specifies the style of window title bar. - This option is supported on OS X 10.10 Yosemite and newer. There are three - possible values: - * `default` or not specified results in the standard gray opaque Mac title + +The `titleBarStyle` option is only supported on OS X 10.10 Yosemite and newer. +Possible values are: + +* `default` or not specified, results in the standard gray opaque Mac title bar. - * `hidden` results in a hidden title bar and a full size content window, yet +* `hidden` results in a hidden title bar and a full size content window, yet the title bar still has the standard window controls ("traffic lights") in the top left. - * `hidden-inset` results in a hidden title bar with an alternative look +* `hidden-inset` results in a hidden title bar with an alternative look where the traffic light buttons are slightly more inset from the window edge. -* `webPreferences` Object - Settings of web page's features, properties: - * `nodeIntegration` Boolean - Whether node integration is enabled. Default - is `true`. - * `preload` String - Specifies a script that will be loaded before other - scripts run in the page. This script will always have access to node APIs - no matter whether node integration is turned on or off. The value should - be the absolute file path to the script. - When node integration is turned off, the preload script can reintroduce - Node global symbols back to the global scope. See example - [here](process.md#event-loaded). - * `partition` String - Sets the session used by the page. If `partition` - starts with `persist:`, the page will use a persistent session available to - all pages in the app with the same `partition`. if there is no `persist:` - prefix, the page will use an in-memory session. By assigning the same - `partition`, multiple pages can share the same session. If the `partition` - is unset then default session of the app will be used. - * `zoomFactor` Number - The default zoom factor of the page, `3.0` represents - `300%`. - * `javascript` Boolean - * `webSecurity` Boolean - When setting `false`, it will disable the - same-origin policy (Usually using testing websites by people), and set - `allowDisplayingInsecureContent` and `allowRunningInsecureContent` to - `true` if these two options are not set by user. - * `allowDisplayingInsecureContent` Boolean - Allow an https page to display - content like images from http URLs. - * `allowRunningInsecureContent` Boolean - Allow a https page to run - JavaScript, CSS or plugins from http URLs. - * `images` Boolean - * `java` Boolean - * `textAreasAreResizable` Boolean - * `webgl` Boolean - * `webaudio` Boolean - * `plugins` Boolean - Whether plugins should be enabled. - * `experimentalFeatures` Boolean - * `experimentalCanvasFeatures` Boolean - * `overlayScrollbars` Boolean - * `overlayFullscreenVideo` Boolean - * `sharedWorker` Boolean - * `directWrite` Boolean - Whether the DirectWrite font rendering system on - Windows is enabled. - * `pageVisibility` Boolean - Page would be forced to be always in visible - or hidden state once set, instead of reflecting current window's - visibility. Users can set it to `true` to prevent throttling of DOM - timers. +The `webPreferences` option is an object that can have following properties: + +* `nodeIntegration` Boolean - Whether node integration is enabled. Default + is `true`. +* `preload` String - Specifies a script that will be loaded before other + scripts run in the page. This script will always have access to node APIs + no matter whether node integration is turned on or off. The value should + be the absolute file path to the script. + When node integration is turned off, the preload script can reintroduce + Node global symbols back to the global scope. See example + [here](process.md#event-loaded). +* `partition` String - Sets the session used by the page. If `partition` + starts with `persist:`, the page will use a persistent session available to + all pages in the app with the same `partition`. if there is no `persist:` + prefix, the page will use an in-memory session. By assigning the same + `partition`, multiple pages can share the same session. If the `partition` + is unset then default session of the app will be used. +* `zoomFactor` Number - The default zoom factor of the page, `3.0` represents + `300%`. Default is `1.0`. +* `javascript` Boolean - Enables JavaScript support. Default is `true`. +* `webSecurity` Boolean - When setting `false`, it will disable the + same-origin policy (Usually using testing websites by people), and set + `allowDisplayingInsecureContent` and `allowRunningInsecureContent` to + `true` if these two options are not set by user. Default is `true`. +* `allowDisplayingInsecureContent` Boolean - Allow an https page to display + content like images from http URLs. Default is `false`. +* `allowRunningInsecureContent` Boolean - Allow a https page to run + JavaScript, CSS or plugins from http URLs. Default is `false`. +* `images` Boolean - Enables image support. Default is `true`. +* `textAreasAreResizable` Boolean - Make TextArea elements resizable. Default + is `true`. +* `webgl` Boolean - Enables WebGL support. Default is `true`. +* `webaudio` Boolean - Enables WebAudio support. Default is `true`. +* `plugins` Boolean - Whether plugins should be enabled. Default is `false`. +* `experimentalFeatures` Boolean - Enables Chromium's experimental features. + Default is `false`. +* `experimentalCanvasFeatures` Boolean - Enables Chromium's experimental + canvas features. Default is `false`. +* `directWrite` Boolean - Enables DirectWrite font rendering system on + Windows. Default is `true`. +* `blinkFeatures` String - A list of feature strings separated by `,`, like + `CSSVariables,KeyboardEventKey`. The full list of supported feature strings + can be found in the [setFeatureEnabledFromString][blink-feature-string] + function. ## Events @@ -737,3 +758,11 @@ Sets whether the window should be visible on all workspaces. Returns whether the window is visible on all workspaces. **Note:** This API always returns false on Windows. + +### `win.setIgnoreMouseEvents(ignore)` _OS X_ + +* `ignore` Boolean + +Ignore all moused events that happened in the window. + +[blink-feature-string]: https://code.google.com/p/chromium/codesearch#chromium/src/out/Debug/gen/blink/platform/RuntimeEnabledFeatures.cpp&sq=package:chromium&type=cs&l=527 diff --git a/docs/api/chrome-command-line-switches.md b/docs/api/chrome-command-line-switches.md index c1adf3c42..65f096eac 100644 --- a/docs/api/chrome-command-line-switches.md +++ b/docs/api/chrome-command-line-switches.md @@ -1,9 +1,9 @@ # Supported Chrome command line switches -This page lists the command line switches used by the Chrome browser that are also supported by -Electron. You can use [app.commandLine.appendSwitch][append-switch] to append -them in your app's main script before the [ready][ready] event of [app][app] -module is emitted: +This page lists the command line switches used by the Chrome browser that are +also supported by Electron. You can use +[app.commandLine.appendSwitch][append-switch] to append them in your app's main +script before the [ready][ready] event of [app][app] module is emitted: ```javascript const app = require('electron').app; @@ -47,6 +47,22 @@ only affects requests with HTTP protocol, including HTTPS and WebSocket requests. It is also noteworthy that not all proxy servers support HTTPS and WebSocket requests. +## --proxy-bypass-list=`hosts` + +Instructs Electron to bypass the proxy server for the given semi-colon-separated +list of hosts. This flag has an effect only if used in tandem with +`--proxy-server`. + +For example: + +```javascript +app.commandLine.appendSwitch('proxy-bypass-list', ';*.google.com;*foo.com;1.2.3.4:5678') +``` + +Will use the proxy server for all hosts except for local addresses (`localhost`, +`127.0.0.1` etc.), `google.com` subdomains, hosts that contain the suffix +`foo.com` and anything at `1.2.3.4:5678`. + ## --proxy-pac-url=`url` Uses the PAC script at the specified `url`. @@ -78,10 +94,6 @@ connection, and the endpoint host in a `SOCKS` proxy connection). Like `--host-rules` but these `rules` only apply to the host resolver. -[app]: app.md -[append-switch]: app.md#appcommandlineappendswitchswitch-value -[ready]: app.md#event-ready - ## --ignore-certificate-errors Ignores certificate related errors. @@ -105,7 +117,16 @@ fallback will accept. ## --cipher-suite-blacklist=`cipher_suites` -Specify comma-separated list of SSL cipher suites to disable. +Specifies comma-separated list of SSL cipher suites to disable. + +## --disable-renderer-backgrounding + +Prevents Chromium from lowering the priority of invisible pages' renderer +processes. + +This flag is global to all renderer processes, if you only want to disable +throttling in one window, you can take the hack of +[playing silent audio][play-silent-audio]. ## --enable-logging @@ -133,3 +154,8 @@ whole pathname and not just the module. E.g. `*/foo/bar/*=2` would change the logging level for all code in the source files under a `foo/bar` directory. This switch only works when `--enable-logging` is also passed. + +[app]: app.md +[append-switch]: app.md#appcommandlineappendswitchswitch-value +[ready]: app.md#event-ready +[play-silent-audio]: https://github.com/atom/atom/pull/9485/files diff --git a/docs/api/crash-reporter.md b/docs/api/crash-reporter.md index 6c66a855f..1b0928263 100644 --- a/docs/api/crash-reporter.md +++ b/docs/api/crash-reporter.md @@ -25,8 +25,8 @@ The `crash-reporter` module has the following methods: `options` Object, properties: * `productName` String, default: Electron. -* `companyName` String, default: GitHub, Inc. -* `submitURL` String, default: http://54.249.141.255:1127/post. +* `companyName` String (**required**) +* `submitURL` String, (**required**) * URL that crash reports will be sent to as POST. * `autoSubmit` Boolean, default: `true`. * Send the crash report without user interaction. diff --git a/docs/api/desktop-capturer.md b/docs/api/desktop-capturer.md new file mode 100644 index 000000000..7863622e3 --- /dev/null +++ b/docs/api/desktop-capturer.md @@ -0,0 +1,78 @@ +# desktopCapturer + +The `desktopCapturer` module can be used to get available sources that can be +used to be captured with `getUserMedia`. + +```javascript +// In the renderer process. +var desktopCapturer = require('electron').desktopCapturer; + +desktopCapturer.getSources({types: ['window', 'screen']}, function(error, sources) { + if (error) throw error; + for (var i = 0; i < sources.length; ++i) { + if (sources[i].name == "Electron") { + navigator.webkitGetUserMedia({ + audio: false, + video: { + mandatory: { + chromeMediaSource: 'desktop', + chromeMediaSourceId: sources[i].id, + minWidth: 1280, + maxWidth: 1280, + minHeight: 720, + maxHeight: 720 + } + } + }, gotStream, getUserMediaError); + return; + } + } +}); + +function gotStream(stream) { + document.querySelector('video').src = URL.createObjectURL(stream); +} + +function getUserMediaError(e) { + console.log('getUserMediaError'); +} +``` + +When creating a constraints object for the `navigator.webkitGetUserMedia` call, +if you are using a source from `desktopCapturer` your `chromeMediaSource` must +be set to `"desktop"` and your `audio` must be set to `false`. + +If you wish to +capture the audio and video from the entire desktop you can set +`chromeMediaSource` to `"screen"` and `audio` to `true`. When using this method +you cannot specify a `chromeMediaSourceId`. + +## Methods + +The `desktopCapturer` module has the following methods: + +### `desktopCapturer.getSources(options, callback)` + +* `options` Object + * `types` Array - An array of String that lists the types of desktop sources + to be captured, available types are `screen` and `window`. + * `thumbnailSize` Object (optional) - The suggested size that thumbnail should + be scaled, it is `{width: 150, height: 150}` by default. +* `callback` Function + +Starts a request to get all desktop sources, `callback` will be called with +`callback(error, sources)` when the request is completed. + +The `sources` is an array of `Source` objects, each `Source` represents a +captured screen or individual window, and has following properties: +* `id` String - The id of the captured window or screen used in + `navigator.webkitGetUserMedia`. The format looks like `window:XX` or + `screen:XX` where `XX` is a random generated number. +* `name` String - The described name of the capturing screen or window. If the + source is a screen, the name will be `Entire Screen` or `Screen `; if + it is a window, the name will be the window's title. +* `thumbnail` [NativeImage](NativeImage.md) - A thumbnail image. + +**Note:** There is no guarantee that the size of `source.thumbnail` is always +the same as the `thumnbailSize` in `options`. It also depends on the scale of +the screen or window. diff --git a/docs/api/dialog.md b/docs/api/dialog.md index 884fb7c07..80a5289e9 100644 --- a/docs/api/dialog.md +++ b/docs/api/dialog.md @@ -20,10 +20,10 @@ parameter. The `dialog` module has the following methods: -### `dialog.showOpenDialog([browserWindow][, options][, callback])` +### `dialog.showOpenDialog([browserWindow, ]options[, callback])` * `browserWindow` BrowserWindow (optional) -* `options` Object (optional) +* `options` Object * `title` String * `defaultPath` String * `filters` Array @@ -61,10 +61,10 @@ and a directory selector, so if you set `properties` to `['openFile', 'openDirectory']` on these platforms, a directory selector will be shown. -### `dialog.showSaveDialog([browserWindow][, options][, callback])` +### `dialog.showSaveDialog([browserWindow, ]options[, callback])` * `browserWindow` BrowserWindow (optional) -* `options` Object (optional) +* `options` Object * `title` String * `defaultPath` String * `filters` Array @@ -79,10 +79,10 @@ The `filters` specifies an array of file types that can be displayed, see If a `callback` is passed, the API call will be asynchronous and the result will be passed via `callback(filename)` -### `dialog.showMessageBox([browserWindow][, options][, callback])` +### `dialog.showMessageBox([browserWindow, ]options[, callback])` * `browserWindow` BrowserWindow (optional) -* `options` Object (optional) +* `options` Object * `type` String - Can be `"none"`, `"info"`, `"error"`, `"question"` or `"warning"`. On Windows, "question" displays the same icon as "info", unless you set an icon using the "icon" option. diff --git a/docs/api/environment-variables.md b/docs/api/environment-variables.md new file mode 100644 index 000000000..6b000aaa1 --- /dev/null +++ b/docs/api/environment-variables.md @@ -0,0 +1,50 @@ +# Environment variables + +Some behaviors of Electron are controlled by environment variables, because they +are initialized earlier than command line and the app's code. + +Examples on POSIX shells: + +```bash +$ export ELECTRON_ENABLE_LOGGING=true +$ electron +``` + +on Windows console: + +```powershell +> set ELECTRON_ENABLE_LOGGING=true +> electron +``` + +## `ELECTRON_RUN_AS_NODE` + +Starts the process as a normal Node.js process. + +## `ELECTRON_ENABLE_LOGGING` + +Prints Chrome's internal logging to console. + +## `ELECTRON_ENABLE_STACK_DUMPING` + +When Electron crashed, prints the stack trace to console. + +This environment variable will not work if `crashReporter` is started. + +## `ELECTRON_DEFAULT_ERROR_MODE` _Windows_ + +Shows Windows's crash dialog when Electron crashed. + +This environment variable will not work if `crashReporter` is started. + +## `ELECTRON_NO_ATTACH_CONSOLE` _Windows_ + +Don't attach to current console session. + +## `ELECTRON_FORCE_WINDOW_MENU_BAR` _Linux_ + +Don't use global menu bar on Linux. + +## `ELECTRON_HIDE_INTERNAL_MODULES` + +Turns off compatibility mode for old built-in modules like `require('ipc')`. diff --git a/docs/api/global-shortcut.md b/docs/api/global-shortcut.md index a0f069d7f..1ec0503bd 100644 --- a/docs/api/global-shortcut.md +++ b/docs/api/global-shortcut.md @@ -1,6 +1,6 @@ -# global-shortcut +# globalShortcut -The `global-shortcut` module can register/unregister a global keyboard shortcut +The `globalShortcut` module can register/unregister a global keyboard shortcut with the operating system so that you can customize the operations for various shortcuts. @@ -38,7 +38,7 @@ app.on('will-quit', function() { ## Methods -The `global-shortcut` module has the following methods: +The `globalShortcut` module has the following methods: ### `globalShortcut.register(accelerator, callback)` @@ -48,12 +48,19 @@ The `global-shortcut` module has the following methods: Registers a global shortcut of `accelerator`. The `callback` is called when the registered shortcut is pressed by the user. +When the accelerator is already taken by other applications, this call will +silently fail. This behavior is intended by operating systems, since they don't +want applications to fight for global shortcuts. + ### `globalShortcut.isRegistered(accelerator)` * `accelerator` [Accelerator](accelerator.md) -Returns `true` or `false` depending on whether the shortcut `accelerator` is -registered. +Returns whether this application has registered `accelerator`. + +When the accelerator is already taken by other applications, this call will +still return `false`. This behavior is intended by operating systems, since they +don't want applications to fight for global shortcuts. ### `globalShortcut.unregister(accelerator)` diff --git a/docs/api/ipc-main.md b/docs/api/ipc-main.md index cdbc0ce34..bbaa3c004 100644 --- a/docs/api/ipc-main.md +++ b/docs/api/ipc-main.md @@ -7,7 +7,7 @@ a renderer will be emitted to this module. ## Sending Messages It is also possible to send messages from the main process to the renderer -process, see [webContents.send][webcontents-send] for more information. +process, see [webContents.send](web-contents.md#webcontentssendchannel-arg1-arg2-) for more information. * When sending a message, the event name is the `channel`. * To reply a synchronous message, you need to set `event.returnValue`. @@ -66,6 +66,4 @@ Set this to the value to be returned in a synchronous message. Returns the `webContents` that sent the message, you can call `event.sender.send` to reply to the asynchronous message, see -[webContents.send][webcontents-send] for more information. - -[webcontents-send]: web-contents.md#webcontentssendchannel-args +[webContents.send](web-contents.md#webcontentssendchannel-arg1-arg2-) for more information. diff --git a/docs/api/menu-item.md b/docs/api/menu-item.md index 37079233f..af945e8ac 100644 --- a/docs/api/menu-item.md +++ b/docs/api/menu-item.md @@ -26,7 +26,9 @@ Create a new `MenuItem` with the following method: * `visible` Boolean * `checked` Boolean * `submenu` Menu - Should be specified for `submenu` type menu item, when - it's specified the `type: 'submenu'` can be omitted for the menu item + it's specified the `type: 'submenu'` can be omitted for the menu item. + If the value is not a `Menu` then it will be automatically converted to one + using `Menu.buildFromTemplate`. * `id` String - Unique within a single menu. If defined then it can be used as a reference to this item by the position attribute. * `position` String - This field allows fine-grained definition of the diff --git a/docs/api/menu.md b/docs/api/menu.md index 1d8196821..b5f2fbe95 100644 --- a/docs/api/menu.md +++ b/docs/api/menu.md @@ -225,7 +225,7 @@ will be set as each window's top menu. Sends the `action` to the first responder of application. This is used for emulating default Cocoa menu behaviors, usually you would just use the -`selector` property of `MenuItem`. +`role` property of `MenuItem`. ### `Menu.buildFromTemplate(template)` diff --git a/docs/api/native-image.md b/docs/api/native-image.md index c08788965..515e89fba 100644 --- a/docs/api/native-image.md +++ b/docs/api/native-image.md @@ -125,7 +125,7 @@ Returns a [Buffer][buffer] that contains the image's `PNG` encoded data. ### `image.toJpeg(quality)` -* `quality` Integer between 0 - 100 (**required**) +* `quality` Integer (**required**) - Between 0 - 100. Returns a [Buffer][buffer] that contains the image's `JPEG` encoded data. diff --git a/docs/api/process.md b/docs/api/process.md index 22fe452b0..620ad6dc9 100644 --- a/docs/api/process.md +++ b/docs/api/process.md @@ -31,11 +31,18 @@ process.once('loaded', function() { }); ``` +## Properties + +### `process.noAsar` + +Setting this to `true` can disable the support for `asar` archives in Node's +built-in modules. + ## Methods The `process` object has the following method: -### `process.hang` +### `process.hang()` Causes the main thread of the current process hang. diff --git a/docs/api/protocol.md b/docs/api/protocol.md index 5f34165fa..cac811265 100644 --- a/docs/api/protocol.md +++ b/docs/api/protocol.md @@ -38,6 +38,10 @@ A standard `scheme` adheres to what RFC 3986 calls [generic URI syntax](https://tools.ietf.org/html/rfc3986#section-3). This includes `file:` and `filesystem:`. +### `protocol.registerServiceWorkerSchemes(schemes)` + +* `schemes` Array - Custom schemes to be registered to handle service workers. + ### `protocol.registerFileProtocol(scheme, handler[, completion])` * `scheme` String @@ -103,11 +107,16 @@ Registers a protocol of `scheme` that will send a `String` as a response. The Registers a protocol of `scheme` that will send an HTTP request as a response. The `callback` should be called with an object that has the `url`, `method`, -`referrer`, and `session` properties. +`referrer`, `uploadData` and `session` properties. By default the HTTP request will reuse the current session. If you want the request to have a different session you should set `session` to `null`. +POST request should provide an `uploadData` object. +* `uploadData` object + * `contentType` String - MIME type of the content. + * `data` String - Content to be sent. + ### `protocol.unregisterProtocol(scheme[, completion])` * `scheme` String diff --git a/docs/api/screen.md b/docs/api/screen.md index eafb56a36..61210c9e1 100644 --- a/docs/api/screen.md +++ b/docs/api/screen.md @@ -54,6 +54,23 @@ app.on('ready', function() { }); ``` +## The `Display` object + +The `Display` object represents a physical display connected to the system. A +fake `Display` may exist on a headless system, or a `Display` may correspond to +a remote, virtual display. + +* `display` object + * `id` Integer - Unique identifier associated with the display. + * `rotation` Integer - Can be 0, 1, 2, 3, each represents screen rotation in + clock-wise degrees of 0, 90, 180, 270. + * `scaleFactor` Number - Output device's pixel scale factor. + * `touchSupport` String - Can be `available`, `unavailable`, `unknown`. + * `bounds` Object + * `size` Object + * `workArea` Object + * `workAreaSize` Object + ## Events The `screen` module emits the following events: diff --git a/docs/api/session.md b/docs/api/session.md index a69c5939e..09fa61e21 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -12,7 +12,7 @@ const BrowserWindow = require('electron').BrowserWindow; var win = new BrowserWindow({ width: 800, height: 600 }); win.loadURL("http://github.com"); -var ses = win.webContents.session +var ses = win.webContents.session; ``` ## Methods @@ -63,7 +63,7 @@ Emitted when Electron is about to download `item` in `webContents`. Calling `event.preventDefault()` will cancel the download. ```javascript -session.on('will-download', function(event, item, webContents) { +session.defaultSession.on('will-download', function(event, item, webContents) { event.preventDefault(); require('request')(item.getURL(), function(data) { require('fs').writeFileSync('/somewhere', data); @@ -80,91 +80,84 @@ The following methods are available on instances of `Session`: The `cookies` gives you ability to query and modify cookies. For example: ```javascript -const BrowserWindow = require('electron').BrowserWindow; +// Query all cookies. +session.defaultSession.cookies.get({}, function(error, cookies) { + console.log(cookies); +}); -var win = new BrowserWindow({ width: 800, height: 600 }); +// Query all cookies associated with a specific url. +session.defaultSession.cookies.get({ url : "http://www.github.com" }, function(error, cookies) { + console.log(cookies); +}); -win.loadURL('https://github.com'); - -win.webContents.on('did-finish-load', function() { - // Query all cookies. - win.webContents.session.cookies.get({}, function(error, cookies) { - if (error) throw error; - console.log(cookies); - }); - - // Query all cookies associated with a specific url. - win.webContents.session.cookies.get({ url : "http://www.github.com" }, - function(error, cookies) { - if (error) throw error; - console.log(cookies); - }); - - // Set a cookie with the given cookie data; - // may overwrite equivalent cookies if they exist. - win.webContents.session.cookies.set( - { url : "http://www.github.com", name : "dummy_name", value : "dummy"}, - function(error, cookies) { - if (error) throw error; - console.log(cookies); - }); +// Set a cookie with the given cookie data; +// may overwrite equivalent cookies if they exist. +var cookie = { url : "http://www.github.com", name : "dummy_name", value : "dummy" }; +session.defaultSession.cookies.set(cookie, function(error) { + if (error) + console.error(error); }); ``` -#### `ses.cookies.get(details, callback)` +#### `ses.cookies.get(filter, callback)` -`details` Object, properties: +* `filter` Object + * `url` String (optional) - Retrieves cookies which are associated with + `url`. Empty implies retrieving cookies of all urls. + * `name` String (optional) - Filters cookies by name. + * `domain` String (optional) - Retrieves cookies whose domains match or are + subdomains of `domains` + * `path` String (optional) - Retrieves cookies whose path matches `path`. + * `secure` Boolean (optional) - Filters cookies by their Secure property. + * `session` Boolean (optional) - Filters out session or persistent cookies. +* `callback` Function -* `url` String - Retrieves cookies which are associated with `url`. - Empty implies retrieving cookies of all urls. -* `name` String - Filters cookies by name -* `domain` String - Retrieves cookies whose domains match or are subdomains of - `domains` -* `path` String - Retrieves cookies whose path matches `path` -* `secure` Boolean - Filters cookies by their Secure property -* `session` Boolean - Filters out session or persistent cookies. -* `callback` Function - function(error, cookies) -* `error` Error -* `cookies` Array - array of `cookie` objects, properties: +Sends a request to get all cookies matching `details`, `callback` will be called +with `callback(error, cookies)` on complete. + +`cookies` is an Array of `cookie` objects. + +* `cookie` Object * `name` String - The name of the cookie. * `value` String - The value of the cookie. * `domain` String - The domain of the cookie. - * `host_only` String - Whether the cookie is a host-only cookie. + * `hostOnly` String - Whether the cookie is a host-only cookie. * `path` String - The path of the cookie. - * `secure` Boolean - Whether the cookie is marked as Secure (typically HTTPS). - * `http_only` Boolean - Whether the cookie is marked as HttpOnly. + * `secure` Boolean - Whether the cookie is marked as secure. + * `httpOnly` Boolean - Whether the cookie is marked as HTTP only. * `session` Boolean - Whether the cookie is a session cookie or a persistent cookie with an expiration date. - * `expirationDate` Double - (Option) The expiration date of the cookie as + * `expirationDate` Double (optional) - The expiration date of the cookie as the number of seconds since the UNIX epoch. Not provided for session cookies. #### `ses.cookies.set(details, callback)` -`details` Object, properties: - -* `url` String - Retrieves cookies which are associated with `url` -* `name` String - The name of the cookie. Empty by default if omitted. -* `value` String - The value of the cookie. Empty by default if omitted. -* `domain` String - The domain of the cookie. Empty by default if omitted. -* `path` String - The path of the cookie. Empty by default if omitted. -* `secure` Boolean - Whether the cookie should be marked as Secure. Defaults to - false. -* `session` Boolean - Whether the cookie should be marked as HttpOnly. Defaults - to false. -* `expirationDate` Double - The expiration date of the cookie as the number of - seconds since the UNIX epoch. If omitted, the cookie becomes a session cookie. - -* `callback` Function - function(error) - * `error` Error - -#### `ses.cookies.remove(details, callback)` - * `details` Object - * `url` String - The URL associated with the cookie - * `name` String - The name of cookie to remove -* `callback` Function - function(error) - * `error` Error + * `url` String - Retrieves cookies which are associated with `url` + * `name` String - The name of the cookie. Empty by default if omitted. + * `value` String - The value of the cookie. Empty by default if omitted. + * `domain` String - The domain of the cookie. Empty by default if omitted. + * `path` String - The path of the cookie. Empty by default if omitted. + * `secure` Boolean - Whether the cookie should be marked as Secure. Defaults to + false. + * `session` Boolean - Whether the cookie should be marked as HttpOnly. Defaults + to false. + * `expirationDate` Double - The expiration date of the cookie as the number of + seconds since the UNIX epoch. If omitted, the cookie becomes a session cookie. +* `callback` Function + +Sets the cookie with `details`, `callback` will be called with `callback(error)` +on complete. + +#### `ses.cookies.remove(url, name, callback)` + +* `url` String - The URL associated with the cookie. +* `name` String - The name of cookie to remove. +* `callback` Function + +Removes the cookies matching `url` and `name`, `callback` will called with +`callback()` on complete. #### `ses.clearCache(callback)` @@ -286,3 +279,197 @@ myWindow.webContents.session.setCertificateVerifyProc(function(hostname, cert, c callback(false); }); ``` + +#### `ses.webRequest` + +The `webRequest` API set allows to intercept and modify contents of a request at +various stages of its lifetime. + +Each API accepts an optional `filter` and a `listener`, the `listener` will be +called with `listener(details)` when the API's event has happened, the `details` +is an object that describes the request. Passing `null` as `listener` will +unsubscribe from the event. + +The `filter` is an object that has an `urls` property, which is an Array of URL +patterns that will be used to filter out the requests that do not match the URL +patterns. If the `filter` is omitted then all requests will be matched. + +For certain events the `listener` is passed with a `callback`, which should be +called with an `response` object when `listener` has done its work. + +```javascript +// Modify the user agent for all requests to the following urls. +var filter = { + urls: ["https://*.github.com/*", "*://electron.github.io"] +}; + +session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, callback) { + details.requestHeaders['User-Agent'] = "MyAgent"; + callback({cancel: false, requestHeaders: details.requestHeaders}); +}); +``` + +#### `ses.webRequest.onBeforeRequest([filter, ]listener)` + +* `filter` Object +* `listener` Function + +The `listener` will be called with `listener(details, callback)` when a request +is about to occur. + +* `details` Object + * `id` Integer + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + +The `callback` has to be called with an `response` object: + +* `response` Object + * `cancel` Boolean (optional) + * `redirectURL` String (optional) - The original request is prevented from + being sent or completed, and is instead redirected to the given URL. + +#### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` + +* `filter` Object +* `listener` Function + +The `listener` will be called with `listener(details, callback)` before sending +an HTTP request, once the request headers are available. This may occur after a +TCP connection is made to the server, but before any http data is sent. + +* `details` Object + * `id` Integer + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `requestHeaders` Object + +The `callback` has to be called with an `response` object: + +* `response` Object + * `cancel` Boolean (optional) + * `requestHeaders` Object (optional) - When provided, request will be made + with these headers. + +#### `ses.webRequest.onSendHeaders([filter, ]listener)` + +* `filter` Object +* `listener` Function + +The `listener` will be called with `listener(details)` just before a request is +going to be sent to the server, modifications of previous `onBeforeSendHeaders` +response are visible by the time this listener is fired. + +* `details` Object + * `id` Integer + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `requestHeaders` Object + +#### `ses.webRequest.onHeadersReceived([filter,] listener)` + +* `filter` Object +* `listener` Function + +The `listener` will be called with `listener(details, callback)` when HTTP +response headers of a request have been received. + +* `details` Object + * `id` String + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `statusLine` String + * `statusCode` Integer + * `responseHeaders` Object + +The `callback` has to be called with an `response` object: + +* `response` Object + * `cancel` Boolean + * `responseHeaders` Object (optional) - When provided, the server is assumed + to have responded with these headers. + +#### `ses.webRequest.onResponseStarted([filter, ]listener)` + +* `filter` Object +* `listener` Function + +The `listener` will be called with `listener(details)` when first byte of the +response body is received. For HTTP requests, this means that the status line +and response headers are available. + +* `details` Object + * `id` Integer + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `responseHeaders` Object + * `fromCache` Boolean - Indicates whether the response was fetched from disk + cache. + * `statusCode` Integer + * `statusLine` String + +#### `ses.webRequest.onBeforeRedirect([filter, ]listener)` + +* `filter` Object +* `listener` Function + +The `listener` will be called with `listener(details)` when a server initiated +redirect is about to occur. + +* `details` Object + * `id` String + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `redirectURL` String + * `statusCode` Integer + * `ip` String (optional) - The server IP address that the request was + actually sent to. + * `fromCache` Boolean + * `responseHeaders` Object + +#### `ses.webRequest.onCompleted([filter, ]listener)` + +* `filter` Object +* `listener` Function + +The `listener` will be called with `listener(details)` when a request is +completed. + +* `details` Object + * `id` Integer + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `responseHeaders` Object + * `fromCache` Boolean + * `statusCode` Integer + * `statusLine` String + +#### `ses.webRequest.onErrorOccurred([filter, ]listener)` + +* `filter` Object +* `listener` Function + +The `listener` will be called with `listener(details)` when an error occurs. + +* `details` Object + * `id` Integer + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `fromCache` Boolean + * `error` String - The error description. diff --git a/docs/api/synopsis.md b/docs/api/synopsis.md index fcb7da2c1..6886c6d78 100644 --- a/docs/api/synopsis.md +++ b/docs/api/synopsis.md @@ -11,7 +11,7 @@ both processes. The basic rule is: if a module is [GUI][gui] or low-level system related, then it should be only available in the main process. You need to be familiar with -the concept of [main process vs. renderer process][mai-process] scripts to be +the concept of [main process vs. renderer process][main-process] scripts to be able to use those modules. The main process script is just like a normal Node.js script: diff --git a/docs/api/tray.md b/docs/api/tray.md index f230c324e..08a43638b 100644 --- a/docs/api/tray.md +++ b/docs/api/tray.md @@ -187,12 +187,16 @@ when the tray icon is clicked. Defaults to true. Displays a tray balloon. -### `Tray.popUpContextMenu([position])` _OS X_ _Windows_ +### `Tray.popUpContextMenu([menu, position])` _OS X_ _Windows_ -* `position` Object (optional)- The pop up position. +* `menu` Menu (optional) +* `position` Object (optional) - The pop up position. * `x` Integer * `y` Integer +Popups the context menu of tray icon. When `menu` is passed, the `menu` will +showed instead of the tray's context menu. + The `position` is only available on Windows, and it is (0, 0) by default. ### `Tray.setContextMenu(menu)` diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index a716bdc59..b399e143a 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -129,14 +129,44 @@ Returns: * `event` Event * `url` String -Emitted when a user or the page wants to start navigation. It can happen when the -`window.location` object is changed or a user clicks a link in the page. +Emitted when a user or the page wants to start navigation. It can happen when +the `window.location` object is changed or a user clicks a link in the page. This event will not emit when the navigation is started programmatically with APIs like `webContents.loadURL` and `webContents.back`. +It is also not emitted for in-page navigations, such as clicking anchor links +or updating the `window.location.hash`. Use `did-navigate-in-page` event for +this purpose. + Calling `event.preventDefault()` will prevent the navigation. +### Event: 'did-navigate' + +Returns: + +* `event` Event +* `url` String + +Emitted when a navigation is done. + +This event is not emitted for in-page navigations, such as clicking anchor links +or updating the `window.location.hash`. Use `did-navigate-in-page` event for +this purpose. + +### Event: 'did-navigate-in-page' + +Returns: + +* `event` Event +* `url` String + +Emitted when an in-page navigation happened. + +When in-page navigation happens, the page URL changes but does not cause +navigation outside of the page. Examples of this occurring are when anchor links +are clicked or when the DOM `hashchange` event is triggered. + ### Event: 'crashed' Emitted when the renderer process has crashed. @@ -221,6 +251,36 @@ Emitted when `webContents` wants to do basic auth. The usage is the same with [the `login` event of `app`](app.md#event-login). +### Event: 'found-in-page' + +Returns: + +* `event` Event +* `result` Object + * `requestId` Integer + * `finalUpdate` Boolean - Indicates if more responses are to follow. + * `matches` Integer (Optional) - Number of Matches. + * `selectionArea` Object (Optional) - Coordinates of first match region. + +Emitted when a result is available for +[`webContents.findInPage`](web-contents.md#webcontentsfindinpage) request. + +### Event: 'media-started-playing' + +Emitted when media starts playing. + +### Event: 'media-paused' + +Emitted when media is paused or done playing. + +### Event: 'did-change-theme-color' + +Emitted when a page's theme color changes. This is usually due to encountering a meta tag: + +```html + +``` + ## Instance Methods The `webContents` object has the following instance methods: @@ -242,6 +302,13 @@ const options = {"extraHeaders" : "pragma: no-cache\n"} webContents.loadURL(url, options) ``` +### `webContents.downloadURL(url)` + +* `url` URL + +Initiates a download of the resource at `url` without navigating. The +`will-download` event of `session` will be triggered. + ### `webContents.getURL()` Returns URL of the current web page. @@ -349,7 +416,7 @@ this limitation. ### `webContents.setAudioMuted(muted)` -+ `muted` Boolean +* `muted` Boolean Mute the audio on the current web page. @@ -405,6 +472,45 @@ Executes the editing command `replace` in web page. Executes the editing command `replaceMisspelling` in web page. +### `webContents.findInPage(text[, options])` + +* `text` String - Content to be searched, must not be empty. +* `options` Object (Optional) + * `forward` Boolean - Whether to search forward or backward, defaults to `true`. + * `findNext` Boolean - Whether the operation is first request or a follow up, + defaults to `false`. + * `matchCase` Boolean - Whether search should be case-sensitive, + defaults to `false`. + * `wordStart` Boolean - Whether to look only at the start of words. + defaults to `false`. + * `medialCapitalAsWordStart` Boolean - When combined with `wordStart`, + accepts a match in the middle of a word if the match begins with an + uppercase letter followed by a lowercase or non-letter. + Accepts several other intra-word matches, defaults to `false`. + +Starts a request to find all matches for the `text` in the web page and returns an `Integer` +representing the request id used for the request. The result of the request can be +obtained by subscribing to [`found-in-page`](web-contents.md#event-found-in-page) event. + +### `webContents.stopFindInPage(action)` + +* `action` String - Specifies the action to take place when ending + [`webContents.findInPage`](web-contents.md#webcontentfindinpage) request. + * `clearSelection` - Translate the selection into a normal selection. + * `keepSelection` - Clear the selection. + * `activateSelection` - Focus and click the selection node. + +Stops any `findInPage` request for the `webContents` with the provided `action`. + +```javascript +webContents.on('found-in-page', function(event, result) { + if (result.finalUpdate) + webContents.stopFindInPage("clearSelection"); +}); + +const requestId = webContents.findInPage("api"); +``` + ### `webContents.hasServiceWorker(callback)` * `callback` Function @@ -447,6 +553,7 @@ size. * 1 - none * 2 - minimum * `pageSize` String - Specify page size of the generated PDF. + * `A5` * `A4` * `A3` * `Legal` @@ -499,7 +606,14 @@ win.webContents.on("did-finish-load", function() { * `path` String -Adds the specified path to DevTools workspace. +Adds the specified path to DevTools workspace. Must be used after DevTools +creation: + +```javascript +mainWindow.webContents.on('devtools-opened', function() { + mainWindow.webContents.addWorkSpace(__dirname); +}); +``` ### `webContents.removeWorkSpace(path)` @@ -512,15 +626,19 @@ Removes the specified path from DevTools workspace. * `options` Object (optional). Properties: * `detach` Boolean - opens DevTools in a new window -Opens the developer tools. +Opens the devtools. ### `webContents.closeDevTools()` -Closes the developer tools. +Closes the devtools. ### `webContents.isDevToolsOpened()` -Returns whether the developer tools are opened. +Returns whether the devtools is opened. + +### `webContents.isDevToolsFocused()` + +Returns whether the devtools view is focused . ### `webContents.toggleDevTools()` diff --git a/docs/api/web-frame.md b/docs/api/web-frame.md index 38c5e30db..114afd041 100644 --- a/docs/api/web-frame.md +++ b/docs/api/web-frame.md @@ -87,7 +87,7 @@ Content Security Policy. * `scheme` String -Registers the `scheme` as secure, bypasses content security policy for resources and -allows registering ServiceWorker. +Registers the `scheme` as secure, bypasses content security policy for resources, +allows registering ServiceWorker and supports fetch API. [spellchecker]: https://github.com/atom/node-spellchecker diff --git a/docs/api/web-view-tag.md b/docs/api/web-view-tag.md index 47a3050a0..8b8f5ffd4 100644 --- a/docs/api/web-view-tag.md +++ b/docs/api/web-view-tag.md @@ -164,6 +164,7 @@ The `webview` tag has the following methods: **Note:** The webview element must be loaded before using the methods. **Example** + ```javascript webview.addEventListener("dom-ready", function() { webview.openDevTools(); @@ -278,6 +279,10 @@ Closes the DevTools window of guest page. Returns a boolean whether guest page has a DevTools window attached. +### `.isDevToolsFocused()` + +Returns a boolean whether DevTools window of guest page is focused. + ### `.inspectElement(x, y)` * `x` Integer @@ -347,6 +352,36 @@ Executes editing command `replace` in page. Executes editing command `replaceMisspelling` in page. +### `.findInPage(text[, options])` + +* `text` String - Content to be searched, must not be empty. +* `options` Object (Optional) + * `forward` Boolean - Whether to search forward or backward, defaults to `true`. + * `findNext` Boolean - Whether the operation is first request or a follow up, + defaults to `false`. + * `matchCase` Boolean - Whether search should be case-sensitive, + defaults to `false`. + * `wordStart` Boolean - Whether to look only at the start of words. + defaults to `false`. + * `medialCapitalAsWordStart` Boolean - When combined with `wordStart`, + accepts a match in the middle of a word if the match begins with an + uppercase letter followed by a lowercase or non-letter. + Accepts several other intra-word matches, defaults to `false`. + +Starts a request to find all matches for the `text` in the web page and returns an `Integer` +representing the request id used for the request. The result of the request can be +obtained by subscribing to [`found-in-page`](web-view-tag.md#event-found-in-page) event. + +### `.stopFindInPage(action)` + +* `action` String - Specifies the action to take place when ending + [`.findInPage`](web-view-tag.md#webviewtagfindinpage) request. + * `clearSelection` - Translate the selection into a normal selection. + * `keepSelection` - Clear the selection. + * `activateSelection` - Focus and click the selection node. + +Stops any `findInPage` request for the `webview` with the provided `action`. + ### `.print([options])` Prints `webview`'s web page. Same with `webContents.print([options])`. @@ -452,15 +487,15 @@ Fired when a redirect was received while requesting a resource. Fired when document in the given frame is loaded. -### Event: 'page-title-set' +### Event: 'page-title-updated' Returns: * `title` String * `explicitSet` Boolean -Fired when page title is set during navigation. `explicitSet` is false when title is synthesised from file -url. +Fired when page title is set during navigation. `explicitSet` is false when +title is synthesised from file url. ### Event: 'page-favicon-updated' @@ -498,6 +533,28 @@ webview.addEventListener('console-message', function(e) { }); ``` +### Event: 'found-in-page' + +Returns: + +* `result` Object + * `requestId` Integer + * `finalUpdate` Boolean - Indicates if more responses are to follow. + * `matches` Integer (Optional) - Number of Matches. + * `selectionArea` Object (Optional) - Coordinates of first match region. + +Fired when a result is available for +[`webview.findInPage`](web-view-tag.md#webviewtagfindinpage) request. + +```javascript +webview.addEventListener('found-in-page', function(e) { + if (e.result.finalUpdate) + webview.stopFindInPage("keepSelection"); +}); + +const rquestId = webview.findInPage("test"); +``` + ### Event: 'new-window' Returns: @@ -519,6 +576,48 @@ webview.addEventListener('new-window', function(e) { }); ``` +### Event: 'will-navigate' + +Returns: + +* `url` String + +Emitted when a user or the page wants to start navigation. It can happen when +the `window.location` object is changed or a user clicks a link in the page. + +This event will not emit when the navigation is started programmatically with +APIs like `.loadURL` and `.back`. + +It is also not emitted during in-page navigation, such as clicking anchor links +or updating the `window.location.hash`. Use `did-navigate-in-page` event for +this purpose. + +Calling `event.preventDefault()` does __NOT__ have any effect. + +### Event: 'did-navigate' + +Returns: + +* `url` String + +Emitted when a navigation is done. + +This event is not emitted for in-page navigations, such as clicking anchor links +or updating the `window.location.hash`. Use `did-navigate-in-page` event for +this purpose. + +### Event: 'did-navigate-in-page' + +Returns: + +* `url` String + +Emitted when an in-page navigation happened. + +When in-page navigation happens, the page URL changes but does not cause +navigation outside of the page. Examples of this occurring are when anchor links +are clicked or when the DOM `hashchange` event is triggered. + ### Event: 'close' Fired when the guest page attempts to close itself. @@ -581,3 +680,31 @@ Fired when a plugin process is crashed. ### Event: 'destroyed' Fired when the WebContents is destroyed. + +### Event: 'media-started-playing' + +Emitted when media starts playing. + +### Event: 'media-paused' + +Emitted when media is paused or done playing. + +### Event: 'did-change-theme-color' + +Emitted when a page's theme color changes. This is usually due to encountering a meta tag: + +```html + +``` + +### Event: 'devtools-opened' + +Emitted when DevTools is opened. + +### Event: 'devtools-closed' + +Emitted when DevTools is closed. + +### Event: 'devtools-focused' + +Emitted when DevTools is focused / opened. diff --git a/docs/development/build-instructions-windows.md b/docs/development/build-instructions-windows.md index 733966f1d..2342131a7 100644 --- a/docs/development/build-instructions-windows.md +++ b/docs/development/build-instructions-windows.md @@ -5,7 +5,7 @@ Follow the guidelines below for building Electron on Windows. ## Prerequisites * Windows 7 / Server 2008 R2 or higher -* Visual Studio 2013 - [download VS 2013 Community Edition for +* Visual Studio 2013 with Update 5 - [download VS 2013 Community Edition for free](https://www.visualstudio.com/downloads/download-visual-studio-vs). * [Python 2.7](http://www.python.org/download/releases/2.7/) * [Node.js](http://nodejs.org/download/) diff --git a/docs/development/build-system-overview.md b/docs/development/build-system-overview.md index 856c53273..04067ce8c 100644 --- a/docs/development/build-system-overview.md +++ b/docs/development/build-system-overview.md @@ -51,7 +51,7 @@ $ ./script/bootstrap.py --dev $ ./script/build.py -c D ``` -## Two-Phrase Project Generation +## Two-Phase Project Generation Electron links with different sets of libraries in `Release` and `Debug` builds. `gyp`, however, doesn't support configuring different link settings for diff --git a/docs/faq/electron-faq.md b/docs/faq/electron-faq.md new file mode 100644 index 000000000..e8f5b191d --- /dev/null +++ b/docs/faq/electron-faq.md @@ -0,0 +1,83 @@ +# Electron FAQ + +## When will Electron upgrade to latest Chrome? + +The Chrome version of Electron is usually bumped within one or two weeks after +a new stable Chrome version gets released. + +Also we only use stable channel of Chrome, if an important fix is in beta or dev +channel, we will back-port it. + +## When will Electron upgrade to latest Node.js? + +When a new version of Node.js gets released, we usually wait for about a month +before upgrading the one in Electron. So we can avoid getting affected by bugs +introduced in new Node.js versions, which happens very often. + +New features of Node.js are usually brought by V8 upgrades, since Electron is +using the V8 shipped by Chrome browser, the shiny new JavaScript feature of a +new Node.js version is usually already in Electron. + +## My app's window/tray disappeared after a few minutes. + +This happens when the variable which is used to store the window/tray gets +garbage collected. + +It is recommended to have a reading of following articles you encountered this +problem: + +* [Memory Management][memory-management] +* [Variable Scope][variable-scope] + +If you want a quick fix, you can make the variables global by changing your +code from this: + +```javascript +app.on('ready', function() { + var tray = new Tray('/path/to/icon.png'); +}) +``` + +to this: + +```javascript +var tray = null; +app.on('ready', function() { + tray = new Tray('/path/to/icon.png'); +}) +``` + +## I can not use jQuery/RequireJS/Meteor/AngularJS in Electron. + +Due to the Node.js integration of Electron, there are some extra symbols +inserted into DOM, like `module`, `exports`, `require`. This causes troubles for +some libraries since they want to insert the symbols with same names. + +To solve this, you can turn off node integration in Electron: + +```javascript +// In the main process. +var mainWindow = new BrowserWindow({ + webPreferences: { + nodeIntegration: false + } +}); +``` + +But if you want to keep the abilities of using Node.js and Electron APIs, you +have to rename the symbols in the page before including other libraries: + +```html + + + + +``` + +[memory-management]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management +[variable-scope]: https://msdn.microsoft.com/library/bzt2dkta(v=vs.94).aspx diff --git a/docs/styleguide.md b/docs/styleguide.md index b471c19fb..6b745b988 100644 --- a/docs/styleguide.md +++ b/docs/styleguide.md @@ -55,7 +55,7 @@ documentation: `methodName(required[, optional]))` -* `require` String, **required** +* `require` String (**required**) * `optional` Integer --- diff --git a/docs/tutorial/application-distribution.md b/docs/tutorial/application-distribution.md index d65bc08ca..e8b944e7f 100644 --- a/docs/tutorial/application-distribution.md +++ b/docs/tutorial/application-distribution.md @@ -118,3 +118,11 @@ a Grunt task has been created that will handle this automatically: This task will automatically handle editing the `.gyp` file, building from source, then rebuilding your app's native Node modules to match the new executable name. + +## Packaging Tools + +Apart from packaging your app manually, you can also choose to use third party +packaging tools to do the work for you: + +* [electron-packager](https://github.com/maxogden/electron-packager) +* [electron-builder](https://github.com/loopline-systems/electron-builder) diff --git a/docs/tutorial/application-packaging.md b/docs/tutorial/application-packaging.md index 45973e49e..b42a2f929 100644 --- a/docs/tutorial/application-packaging.md +++ b/docs/tutorial/application-packaging.md @@ -103,6 +103,14 @@ var originalFs = require('original-fs'); originalFs.readFileSync('/path/to/example.asar'); ``` +You can also set `process.noAsar` to `true` to disable the support for `asar` in +the `fs` module: + +```javascript +process.noAsar = true; +fs.readFileSync('/path/to/example.asar'); +``` + ## Limitations on Node API Even though we tried hard to make `asar` archives in the Node API work like @@ -132,6 +140,7 @@ work. This adds a little overhead for those APIs. APIs that requires extra unpacking are: * `child_process.execFile` +* `child_process.execFileSync` * `fs.open` * `fs.openSync` * `process.dlopen` - Used by `require` on native modules @@ -143,6 +152,17 @@ archives is generated by guessing, because those files do not exist on the filesystem. So you should not trust the `Stats` object except for getting file size and checking file type. +### Executing Binaries Inside `asar` Archive + +There are Node APIs that can execute binaries like `child_process.exec`, +`child_process.spawn` and `child_process.execFile`, but only `execFile` is +supported to execute binaries inside `asar` archive. + +This is because `exec` and `spawn` accept `command` instead of `file` as input, +and `command`s are executed under shell. There is no reliable way to determine +whether a command uses a file in asar archive, and even if we do, we can not be +sure whether we can replace the path in command without side effects. + ## Adding Unpacked Files in `asar` Archive As stated above, some Node APIs will unpack the file to filesystem when diff --git a/docs/tutorial/debugging-main-process.md b/docs/tutorial/debugging-main-process.md index 38c6e61ff..73710211e 100644 --- a/docs/tutorial/debugging-main-process.md +++ b/docs/tutorial/debugging-main-process.md @@ -19,17 +19,34 @@ Like `--debug` but pauses the script on the first line. ## Use node-inspector for Debugging -__Note:__ Electron uses node v0.11.13, which currently doesn't work very well +__Note:__ Electron doesn't currently work very well with node-inspector, and the main process will crash if you inspect the `process` object under node-inspector's console. -### 1. Start the [node-inspector][node-inspector] server +### 1. Make sure you have [node-gyp required tools][node-gyp-required-tools] installed + +### 2. Install [node-inspector][node-inspector] ```bash -$ node-inspector +$ npm install node-inspector ``` -### 2. Enable debug mode for Electron +### 3. Install a patched version of `node-pre-gyp` + +```bash +$ npm install git+https://git@github.com/enlight/node-pre-gyp.git#detect-electron-runtime-in-find +``` + +### 4. Recompile the `node-inspector` `v8` modules for electron (change the target to your electron version number) + +```bash +$ node_modules/.bin/node-pre-gyp --target=0.36.2 --runtime=electron --fallback-to-build --directory node_modules/v8-debug/ --dist-url=https://atom.io/download/atom-shell reinstall +$ node_modules/.bin/node-pre-gyp --target=0.36.2 --runtime=electron --fallback-to-build --directory node_modules/v8-profiler/ --dist-url=https://atom.io/download/atom-shell reinstall +``` + +See also [How to install native modules](how-to-install-native-modules). + +### 5. Enable debug mode for Electron You can either start Electron with a debug flag like: @@ -43,8 +60,17 @@ or, to pause your script on the first line: $ electron --debug-brk=5858 your/app ``` -### 3. Load the debugger UI +### 5. Start the [node-inspector][node-inspector] server using electron -Open http://127.0.0.1:8080/debug?ws=127.0.0.1:8080&port=5858 in the Chrome browser. +```bash +$ ELECTRON_RUN_AS_NODE=true path/to/electron.exe node_modules/node-inspector/bin/inspector.js +``` + +### 6. Load the debugger UI + +Open http://127.0.0.1:8080/debug?ws=127.0.0.1:8080&port=5858 in the Chrome browser. You may have to click pause if starting with debug-brk to see the entry line. [node-inspector]: https://github.com/node-inspector/node-inspector +[node-gyp-required-tools]: https://github.com/nodejs/node-gyp#installation +[how-to-install-native-modules]: using-native-node-modules.md#how-to-install-native-modules + diff --git a/docs/tutorial/desktop-environment-integration.md b/docs/tutorial/desktop-environment-integration.md index 6e570ee71..c5b5cd680 100644 --- a/docs/tutorial/desktop-environment-integration.md +++ b/docs/tutorial/desktop-environment-integration.md @@ -25,7 +25,7 @@ myNotification.onclick = function () { } ``` -While code and user experience across operating systems are similar, but there +While code and user experience across operating systems are similar, there are fine differences. ### Windows @@ -39,7 +39,7 @@ however, that it does not need to be pinned to the Start screen. To use an image in your notification, pass a local image file (preferably `png`) in the `icon` property of your notification's options. The notification will -still display if you submit and incorrect or `http/https`-based URL, but the +still display if you submit an incorrect or `http/https`-based URL, but the image will not be displayed. ```javascript @@ -49,7 +49,7 @@ new Notification('Title', { }); ``` -Keep furthermore in mind that the maximum length for the body is 250 characters, +Furthermore, keep in mind that the maximum length for the body is 250 characters, with the Windows team recommending that notifications should be kept to 200 characters. diff --git a/docs/tutorial/mac-app-store-submission-guide.md b/docs/tutorial/mac-app-store-submission-guide.md index 43b2d9a0a..036676fe8 100644 --- a/docs/tutorial/mac-app-store-submission-guide.md +++ b/docs/tutorial/mac-app-store-submission-guide.md @@ -4,6 +4,9 @@ Since v0.34.0, Electron allows submitting packaged apps to the Mac App Store (MAS). This guide provides information on: how to submit your app and the limitations of the MAS build. +__Note:__ Submitting an app to Mac App Store requires enrolling [Apple Developer +Program][developer-program], which costs money. + ## How to Submit Your App The following steps introduce a simple way to submit your app to Mac App Store. @@ -108,6 +111,7 @@ Also, due to the usage of app sandboxing, the resources which can be accessed by the app are strictly limited; you can read [App Sandboxing][app-sandboxing] for more information. +[developer-program]: https://developer.apple.com/support/compare-memberships/ [submitting-your-app]: https://developer.apple.com/library/mac/documentation/IDEs/Conceptual/AppDistributionGuide/SubmittingYourApp/SubmittingYourApp.html [nwjs-guide]: https://github.com/nwjs/nw.js/wiki/Mac-App-Store-%28MAS%29-Submission-Guideline#first-steps [enable-app-sandbox]: https://developer.apple.com/library/ios/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html diff --git a/docs/tutorial/quick-start.md b/docs/tutorial/quick-start.md index 4c6141343..ebf907e07 100644 --- a/docs/tutorial/quick-start.md +++ b/docs/tutorial/quick-start.md @@ -78,13 +78,12 @@ The `main.js` should create windows and handle system events, a typical example being: ```javascript +'use strict'; + const electron = require('electron'); const app = electron.app; // Module to control application life. const BrowserWindow = electron.BrowserWindow; // Module to create native browser window. -// Report crashes to our server. -electron.crashReporter.start(); - // Keep a global reference of the window object, if you don't, the window will // be closed automatically when the JavaScript object is garbage collected. var mainWindow = null; diff --git a/docs/tutorial/using-widevine-cdm-plugin.md b/docs/tutorial/using-widevine-cdm-plugin.md new file mode 100644 index 000000000..340dad343 --- /dev/null +++ b/docs/tutorial/using-widevine-cdm-plugin.md @@ -0,0 +1,78 @@ +# Using Widevine CDM Plugin + +In Electron you can use the Widevine CDM plugin shipped with Chrome browser. + +## Getting the plugin + +Electron doesn't ship with the Widevine CDM plugin for license reasons, to get +it, you need to install the official Chrome browser first, which should match +the architecture and Chrome version of the Electron build you use. + +### Windows & OS X + +Open `chrome://components/` in Chrome browser, find `WidevineCdm` and make +sure it is up to date, then you can find all the plugin binaries from the +`APP_DATA/Google/Chrome/WidevineCDM/VERSION/_platform_specific/PLATFORM_ARCH/` +directory. + +`APP_DATA` is system's location for storing app data, on Windows it is +`%LOCALAPPDATA%`, on OS X it is `~/Library/Application Support`. `VERSION` is +Widevine CDM plugin's version string, like `1.4.8.866`. `PLATFORM` is `mac` or +`win`. `ARCH` is `x86` or `x64`. + +On Windows the required binaries are `widevinecdm.dll` and +`widevinecdmadapter.dll`, on OS X they are `libwidevinecdm.dylib` and +`widevinecdmadapter.plugin`. You can copy them to anywhere you like, but they +have to be put together. + +### Linux + +On Linux the plugin binaries are shipped together with Chrome browser, you can +find them under `/opt/google/chrome`, the filenames are `libwidevinecdm.so` and +`libwidevinecdmadapter.so`. + +## Using the plugin + +After getting the plugin files, you should pass the `widevinecdmadapter`'s path +to Electron with `--widevine-cdm-path` command line switch, and the plugin's +version with `--widevine-cdm-version` switch. + +__Note:__ Though only the `widevinecdmadapter` binary is passed to Electron, the +`widevinecdm` binary has to be put aside it. + +The command line switches have to be passed before the `ready` event of `app` +module gets emitted, and the page that uses this plugin must have plugin +enabled. + +Example code: + +```javascript +// You have to pass the filename of `widevinecdmadapter` here, it is +// * `widevinecdmadapter.plugin` on OS X, +// * `libwidevinecdmadapter.so` on Linux, +// * `widevinecdmadapter.dll` on Windows. +app.commandLine.appendSwitch('widevine-cdm-path', '/path/to/widevinecdmadapter.plugin'); +// The version of plugin can be got from `chrome://plugins` page in Chrome. +app.commandLine.appendSwitch('widevine-cdm-version', '1.4.8.866'); + +var mainWindow = null; +app.on('ready', function() { + mainWindow = new BrowserWindow({ + webPreferences: { + // The `plugins` have to be enabled. + plugins: true + } + }) +}); +``` + +## Verifying the plugin + +To verify whether the plugin works, you can use following ways: + +* Open devtools and check whether `navigator.plugins` includes the Widevine +CDM plugin. +* Open https://shaka-player-demo.appspot.com/ and load a manifest that uses +`Widevine`. +* Open http://www.dash-player.com/demo/drm-test-area/, check whether the page +says `bitdash uses Widevine in your browser`, then play the video. diff --git a/filenames.gypi b/filenames.gypi index 715707917..d7eb84092 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -31,6 +31,7 @@ 'atom/browser/api/lib/tray.coffee', 'atom/browser/api/lib/web-contents.coffee', 'atom/browser/lib/chrome-extension.coffee', + 'atom/browser/lib/desktop-capturer.coffee', 'atom/browser/lib/guest-view-manager.coffee', 'atom/browser/lib/guest-window-manager.coffee', 'atom/browser/lib/init.coffee', @@ -53,6 +54,7 @@ 'atom/renderer/lib/web-view/web-view.coffee', 'atom/renderer/lib/web-view/web-view-attributes.coffee', 'atom/renderer/lib/web-view/web-view-constants.coffee', + 'atom/renderer/api/lib/desktop-capturer.coffee', 'atom/renderer/api/lib/exports/electron.coffee', 'atom/renderer/api/lib/ipc.coffee', 'atom/renderer/api/lib/ipc-renderer.coffee', @@ -81,6 +83,8 @@ 'atom/browser/api/atom_api_content_tracing.cc', 'atom/browser/api/atom_api_cookies.cc', 'atom/browser/api/atom_api_cookies.h', + 'atom/browser/api/atom_api_desktop_capturer.cc', + 'atom/browser/api/atom_api_desktop_capturer.h', 'atom/browser/api/atom_api_download_item.cc', 'atom/browser/api/atom_api_download_item.h', 'atom/browser/api/atom_api_dialog.cc', @@ -106,6 +110,8 @@ 'atom/browser/api/atom_api_tray.h', 'atom/browser/api/atom_api_web_contents.cc', 'atom/browser/api/atom_api_web_contents.h', + 'atom/browser/api/atom_api_web_request.cc', + 'atom/browser/api/atom_api_web_request.h', 'atom/browser/api/atom_api_web_view_manager.cc', 'atom/browser/api/atom_api_window.cc', 'atom/browser/api/atom_api_window.h', @@ -174,6 +180,8 @@ 'atom/browser/net/asar/url_request_asar_job.h', 'atom/browser/net/atom_cert_verifier.cc', 'atom/browser/net/atom_cert_verifier.h', + 'atom/browser/net/atom_network_delegate.cc', + 'atom/browser/net/atom_network_delegate.h', 'atom/browser/net/atom_ssl_config_service.cc', 'atom/browser/net/atom_ssl_config_service.h', 'atom/browser/net/atom_url_request_job_factory.cc', @@ -200,8 +208,6 @@ 'atom/browser/ui/atom_menu_model.h', 'atom/browser/ui/cocoa/atom_menu_controller.h', 'atom/browser/ui/cocoa/atom_menu_controller.mm', - 'atom/browser/ui/cocoa/event_processing_window.h', - 'atom/browser/ui/cocoa/event_processing_window.mm', 'atom/browser/ui/file_dialog.h', 'atom/browser/ui/file_dialog_gtk.cc', 'atom/browser/ui/file_dialog_mac.mm', @@ -286,6 +292,8 @@ 'atom/common/asar/scoped_temporary_file.h', 'atom/common/atom_command_line.cc', 'atom/common/atom_command_line.h', + 'atom/common/atom_constants.cc', + 'atom/common/atom_constants.h', 'atom/common/common_message_generator.cc', 'atom/common/common_message_generator.h', 'atom/common/crash_reporter/crash_reporter.cc', @@ -374,6 +382,10 @@ 'chromium_src/chrome/browser/extensions/global_shortcut_listener_x11.h', 'chromium_src/chrome/browser/extensions/global_shortcut_listener_win.cc', 'chromium_src/chrome/browser/extensions/global_shortcut_listener_win.h', + 'chromium_src/chrome/browser/media/desktop_media_list.h', + 'chromium_src/chrome/browser/media/desktop_media_list_observer.h', + 'chromium_src/chrome/browser/media/native_desktop_media_list.cc', + 'chromium_src/chrome/browser/media/native_desktop_media_list.h', 'chromium_src/chrome/browser/printing/print_job.cc', 'chromium_src/chrome/browser/printing/print_job.h', 'chromium_src/chrome/browser/printing/print_job_manager.cc', @@ -406,6 +418,8 @@ 'chromium_src/chrome/browser/renderer_host/pepper/pepper_flash_clipboard_message_filter.h', 'chromium_src/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc', 'chromium_src/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.h', + 'chromium_src/chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.cc', + 'chromium_src/chrome/browser/renderer_host/pepper/widevine_cdm_message_filter.h', 'chromium_src/chrome/browser/speech/tts_controller.h', 'chromium_src/chrome/browser/speech/tts_controller_impl.cc', 'chromium_src/chrome/browser/speech/tts_controller_impl.h', @@ -438,6 +452,11 @@ 'chromium_src/chrome/common/tts_messages.h', 'chromium_src/chrome/common/tts_utterance_request.cc', 'chromium_src/chrome/common/tts_utterance_request.h', + 'chromium_src/chrome/common/widevine_cdm_messages.h', + 'chromium_src/chrome/common/widevine_cdm_constants.cc', + 'chromium_src/chrome/common/widevine_cdm_constants.h', + 'chromium_src/chrome/renderer/media/chrome_key_systems.cc', + 'chromium_src/chrome/renderer/media/chrome_key_systems.h', 'chromium_src/chrome/renderer/pepper/chrome_renderer_pepper_host_factory.cc', 'chromium_src/chrome/renderer/pepper/chrome_renderer_pepper_host_factory.h', 'chromium_src/chrome/renderer/pepper/pepper_flash_font_file_host.cc', @@ -464,6 +483,8 @@ 'chromium_src/chrome/utility/utility_message_handler.h', 'chromium_src/extensions/browser/app_window/size_constraints.cc', 'chromium_src/extensions/browser/app_window/size_constraints.h', + 'chromium_src/extensions/common/url_pattern.cc', + 'chromium_src/extensions/common/url_pattern.h', 'chromium_src/library_loaders/libspeechd_loader.cc', 'chromium_src/library_loaders/libspeechd.h', 'chromium_src/net/test/embedded_test_server/stream_listen_socket.cc', diff --git a/package.json b/package.json index a5d56e3a9..f7202f28d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,8 @@ { "name": "electron", + "version": "0.36.2", "devDependencies": { - "asar": "^0.8.0", + "asar": "^0.9.0", "coffee-script": "^1.9.2", "coffeelint": "^1.9.4", "request": "*" @@ -11,6 +12,7 @@ }, "private": true, "scripts": { - "preinstall": "node -e 'process.exit(0)'" + "preinstall": "node -e 'process.exit(0)'", + "test": "python ./script/test.py" } } diff --git a/script/bootstrap.py b/script/bootstrap.py index ef48c1f80..6eaf635bf 100755 --- a/script/bootstrap.py +++ b/script/bootstrap.py @@ -196,7 +196,7 @@ def create_chrome_version_h(): def touch_config_gypi(): config_gypi = os.path.join(SOURCE_ROOT, 'vendor', 'node', 'config.gypi') with open(config_gypi, 'w+') as f: - content = '\n{}' + content = "\n{'variables':{}}" if f.read() != content: f.write(content) diff --git a/script/bump-version.py b/script/bump-version.py index 3ee0b23df..e2c22e8cd 100755 --- a/script/bump-version.py +++ b/script/bump-version.py @@ -31,6 +31,7 @@ def main(): update_win_rc(version, versions) update_version_h(versions) update_info_plist(version) + update_package_json(version) tag_version(version) @@ -113,6 +114,21 @@ def update_info_plist(version): f.write(''.join(lines)) +def update_package_json(version): + package_json = 'package.json' + with open(package_json, 'r') as f: + lines = f.readlines() + + for i in range(0, len(lines)): + line = lines[i]; + if 'version' in line: + lines[i] = ' "version": "{0}",\n'.format(version) + break + + with open(package_json, 'w') as f: + f.write(''.join(lines)) + + def tag_version(version): execute(['git', 'commit', '-a', '-m', 'Bump v{0}'.format(version)]) diff --git a/script/cibuild b/script/cibuild index 08d59385c..c0798dc7e 100755 --- a/script/cibuild +++ b/script/cibuild @@ -17,6 +17,7 @@ LINUX_DEPS = [ 'libgtk2.0-dev', 'libnotify-dev', 'libnss3-dev', + 'libxtst-dev', 'gcc-multilib', 'g++-multilib', ] diff --git a/script/create-dist.py b/script/create-dist.py index 29f81ebd8..a619e04d3 100755 --- a/script/create-dist.py +++ b/script/create-dist.py @@ -72,7 +72,6 @@ TARGET_DIRECTORIES = { SYSTEM_LIBRARIES = [ 'libgcrypt.so', - 'libnotify.so', ] diff --git a/script/lib/config.py b/script/lib/config.py index 2fb841acf..932562fdc 100644 --- a/script/lib/config.py +++ b/script/lib/config.py @@ -7,8 +7,8 @@ import sys BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \ - 'http://gh-contractor-zcbenz.s3.amazonaws.com/libchromiumcontent' -LIBCHROMIUMCONTENT_COMMIT = '17a4337f7948a45b5ea4b8f391df152ba8db5979' + 'https://s3.amazonaws.com/github-janky-artifacts/libchromiumcontent' +LIBCHROMIUMCONTENT_COMMIT = '0fbded6cf3d9244389db05f0c022e474a06ad32a' PLATFORM = { 'cygwin': 'win32', diff --git a/script/update-clang.sh b/script/update-clang.sh index 5f9b00da7..801d60397 100755 --- a/script/update-clang.sh +++ b/script/update-clang.sh @@ -8,7 +8,7 @@ # Do NOT CHANGE this if you don't know what you're doing -- see # https://code.google.com/p/chromium/wiki/UpdatingClang # Reverting problematic clang rolls is safe, though. -CLANG_REVISION=245965 +CLANG_REVISION=247874 # This is incremented when pushing a new build of Clang at the same revision. CLANG_SUB_REVISION=1 diff --git a/script/upload.py b/script/upload.py index c021d743a..3245d9caa 100755 --- a/script/upload.py +++ b/script/upload.py @@ -174,11 +174,10 @@ def create_or_get_release_draft(github, releases, tag, tag_exists): def create_release_draft(github, tag): + name = '{0} {1}'.format(PROJECT_NAME, tag) if os.environ.has_key('CI'): - name = '{0} pending draft'.format(PROJECT_NAME) body = '(placeholder)' else: - name = '{0} {1}'.format(PROJECT_NAME, tag) body = get_text_with_editor(name) if body == '': sys.stderr.write('Quit due to empty release note.\n') diff --git a/spec/api-app-spec.coffee b/spec/api-app-spec.coffee index 490727488..adb64d363 100644 --- a/spec/api-app-spec.coffee +++ b/spec/api-app-spec.coffee @@ -1,4 +1,6 @@ assert = require 'assert' +ChildProcess = require 'child_process' +path = require 'path' {remote} = require 'electron' {app, BrowserWindow} = remote.require 'electron' @@ -29,6 +31,23 @@ describe 'app module', -> it 'should not be empty', -> assert.notEqual app.getLocale(), '' + describe 'app.exit(exitCode)', -> + appProcess = null + afterEach -> + appProcess?.kill() + + it 'emits a process exit event with the code', (done) -> + appPath = path.join(__dirname, 'fixtures', 'api', 'quit-app') + electronPath = remote.getGlobal('process').execPath + appProcess = ChildProcess.spawn(electronPath, [appPath]) + + output = '' + appProcess.stdout.on 'data', (data) -> output += data + appProcess.on 'close', (code) -> + assert.notEqual output.indexOf('Exit event with code: 123'), -1 + assert.equal code, 123 + done() + describe 'BrowserWindow events', -> w = null afterEach -> diff --git a/spec/api-browser-window-spec.coffee b/spec/api-browser-window-spec.coffee index 00437ae41..38c821704 100644 --- a/spec/api-browser-window-spec.coffee +++ b/spec/api-browser-window-spec.coffee @@ -8,7 +8,7 @@ os = require 'os' {remote, screen} = require 'electron' {ipcMain, BrowserWindow} = remote.require 'electron' -isCI = remote.process.argv[2] == '--ci' +isCI = remote.getGlobal('isCi') describe 'browser-window module', -> fixtures = path.resolve __dirname, 'fixtures' @@ -262,6 +262,7 @@ describe 'browser-window module', -> w.loadURL "file://#{fixtures}/pages/window-open.html" it 'emits when link with target is called', (done) -> + @timeout 10000 w.webContents.once 'new-window', (e, url, frameName) -> e.preventDefault() assert.equal url, 'http://host/' @@ -322,3 +323,11 @@ describe 'browser-window module', -> done() w.loadURL "file://#{fixtures}/pages/save_page/index.html" + + describe 'BrowserWindow options argument is optional', -> + it 'should create a window with default size (800x600)', -> + w.destroy() + w = new BrowserWindow() + size = w.getSize() + assert.equal size[0], 800 + assert.equal size[1], 600 diff --git a/spec/api-crash-reporter-spec.coffee b/spec/api-crash-reporter-spec.coffee index 676dbf9d6..334956d3c 100644 --- a/spec/api-crash-reporter-spec.coffee +++ b/spec/api-crash-reporter-spec.coffee @@ -18,7 +18,7 @@ describe 'crash-reporter module', -> return if process.mas # The crash-reporter test is not reliable on CI machine. - isCI = remote.process.argv[2] == '--ci' + isCI = remote.getGlobal('isCi') return if isCI it 'should send minidump when renderer crashes', (done) -> @@ -55,5 +55,12 @@ describe 'crash-reporter module', -> pathname: path.join fixtures, 'api', 'crash.html' search: "?port=#{port}" if process.platform is 'darwin' - crashReporter.start {'submitURL': 'http://127.0.0.1:' + port} + crashReporter.start + companyName: 'Umbrella Corporation' + submitURL: "http://127.0.0.1:#{port}" w.loadURL url + + describe ".start(options)", -> + it 'requires that the companyName and submitURL options be specified', -> + assert.throws(-> crashReporter.start({companyName: 'Missing submitURL'})) + assert.throws(-> crashReporter.start({submitURL: 'Missing companyName'})) diff --git a/spec/api-desktop-capturer.coffee b/spec/api-desktop-capturer.coffee new file mode 100644 index 000000000..6f1842dd1 --- /dev/null +++ b/spec/api-desktop-capturer.coffee @@ -0,0 +1,9 @@ +assert = require 'assert' +{desktopCapturer} = require 'electron' + +describe 'desktopCapturer', -> + it 'should returns something', (done) -> + desktopCapturer.getSources {types: ['window', 'screen']}, (error, sources) -> + assert.equal error, null + assert.notEqual sources.length, 0 + done() diff --git a/spec/api-ipc-spec.coffee b/spec/api-ipc-spec.coffee index a8d2a65cd..67ef717d5 100644 --- a/spec/api-ipc-spec.coffee +++ b/spec/api-ipc-spec.coffee @@ -53,11 +53,19 @@ describe 'ipc module', -> assert.equal obj.test, 'test' describe 'remote value in browser', -> + print = path.join(fixtures, 'module', 'print_name.js') + it 'keeps its constructor name for objects', -> buf = new Buffer('test') - print_name = remote.require path.join(fixtures, 'module', 'print_name.js') + print_name = remote.require print assert.equal print_name.print(buf), 'Buffer' + it 'supports instanceof Date', -> + now = new Date() + print_name = remote.require print + assert.equal print_name.print(now), 'Date' + assert.deepEqual print_name.echo(now), now + describe 'remote promise', -> it 'can be used as promise in each side', (done) -> promise = remote.require path.join(fixtures, 'module', 'promise.js') @@ -89,14 +97,14 @@ describe 'ipc module', -> w.loadURL 'file://' + path.join(fixtures, 'api', 'send-sync-message.html') describe 'remote listeners', -> - it 'can be added and removed correctly', -> - count = 0 - w = new BrowserWindow(show: false) - listener = () -> - count += 1 - w.removeListener 'blur', listener - w.on 'blur', listener - w.emit 'blur' - w.emit 'blur' - assert.equal count, 1 + w = null + afterEach -> w.destroy() + + it 'can be added and removed correctly', -> + w = new BrowserWindow(show: false) + listener = -> + w.on 'test', listener + assert.equal w.listenerCount('test'), 1 + w.removeListener 'test', listener + assert.equal w.listenerCount('test'), 0 diff --git a/spec/api-menu-spec.coffee b/spec/api-menu-spec.coffee index f23face22..2f1f64658 100644 --- a/spec/api-menu-spec.coffee +++ b/spec/api-menu-spec.coffee @@ -1,6 +1,6 @@ assert = require 'assert' -{remote} = require 'electron' +{remote, ipcRenderer} = require 'electron' {Menu, MenuItem} = remote.require 'electron' describe 'menu module', -> @@ -9,6 +9,14 @@ describe 'menu module', -> menu = Menu.buildFromTemplate [label: 'text', extra: 'field'] assert.equal menu.items[0].extra, 'field' + it 'does not modify the specified template', -> + template = ipcRenderer.sendSync 'eval', """ + var template = [{label: 'text', submenu: [{label: 'sub'}]}]; + require('electron').Menu.buildFromTemplate(template); + template; + """ + assert.deepStrictEqual template, [label: 'text', submenu: [label: 'sub']] + describe 'Menu.buildFromTemplate should reorder based on item position specifiers', -> it 'should position before existing item', -> menu = Menu.buildFromTemplate [ diff --git a/spec/api-protocol-spec.coffee b/spec/api-protocol-spec.coffee index d160512cd..77eb90259 100644 --- a/spec/api-protocol-spec.coffee +++ b/spec/api-protocol-spec.coffee @@ -1,6 +1,7 @@ assert = require 'assert' http = require 'http' path = require 'path' +qs = require 'querystring' {remote} = require 'electron' {protocol} = remote.require 'electron' @@ -8,6 +9,9 @@ path = require 'path' describe 'protocol module', -> protocolName = 'sp' text = 'valar morghulis' + postData = + name: 'post test' + type: 'string' afterEach (done) -> protocol.unregisterProtocol protocolName, -> @@ -81,6 +85,21 @@ describe 'protocol module', -> error: (xhr, errorType, error) -> done(error) + it 'sets Access-Control-Allow-Origin', (done) -> + handler = (request, callback) -> callback(text) + protocol.registerStringProtocol protocolName, handler, (error) -> + return done(error) if error + $.ajax + url: "#{protocolName}://fake-host" + success: (data, status, request) -> + assert.equal data, text + assert.equal( + request.getResponseHeader('Access-Control-Allow-Origin'), + '*') + done() + error: (xhr, errorType, error) -> + done(error) + it 'sends object as response', (done) -> handler = (request, callback) -> callback(data: text, mimeType: 'text/html') protocol.registerStringProtocol protocolName, handler, (error) -> @@ -120,6 +139,21 @@ describe 'protocol module', -> error: (xhr, errorType, error) -> done(error) + it 'sets Access-Control-Allow-Origin', (done) -> + handler = (request, callback) -> callback(buffer) + protocol.registerBufferProtocol protocolName, handler, (error) -> + return done(error) if error + $.ajax + url: "#{protocolName}://fake-host" + success: (data, status, request) -> + assert.equal data, text + assert.equal( + request.getResponseHeader('Access-Control-Allow-Origin'), + '*') + done() + error: (xhr, errorType, error) -> + done(error) + it 'sends object as response', (done) -> handler = (request, callback) -> callback(data: buffer, mimeType: 'text/html') protocol.registerBufferProtocol protocolName, handler, (error) -> @@ -163,6 +197,21 @@ describe 'protocol module', -> error: (xhr, errorType, error) -> done(error) + it 'sets Access-Control-Allow-Origin', (done) -> + handler = (request, callback) -> callback(filePath) + protocol.registerFileProtocol protocolName, handler, (error) -> + return done(error) if error + $.ajax + url: "#{protocolName}://fake-host" + success: (data, status, request) -> + assert.equal data, String(fileContent) + assert.equal( + request.getResponseHeader('Access-Control-Allow-Origin'), + '*') + done() + error: (xhr, errorType, error) -> + done(error) + it 'sends object as response', (done) -> handler = (request, callback) -> callback(path: filePath) protocol.registerFileProtocol protocolName, handler, (error) -> @@ -320,6 +369,9 @@ describe 'protocol module', -> done(error) it 'sends error when callback is called with nothing', (done) -> + # Flaky on Travis. + return done() if process.env.TRAVIS is 'true' + protocol.interceptBufferProtocol 'http', emptyHandler, (error) -> return done(error) if error $.ajax @@ -357,6 +409,22 @@ describe 'protocol module', -> error: (xhr, errorType, error) -> done(error) + it 'can receive post data', (done) -> + handler = (request, callback) -> + uploadData = request.uploadData[0].bytes.toString() + callback({data: uploadData}) + protocol.interceptStringProtocol 'http', handler, (error) -> + return done(error) if error + $.ajax + url: "http://fake-host" + type: "POST" + data: postData + success: (data) -> + assert.deepEqual qs.parse(data), postData + done() + error: (xhr, errorType, error) -> + done(error) + describe 'protocol.interceptBufferProtocol', -> it 'can intercept http protocol', (done) -> handler = (request, callback) -> callback(new Buffer(text)) @@ -370,6 +438,55 @@ describe 'protocol module', -> error: (xhr, errorType, error) -> done(error) + it 'can receive post data', (done) -> + handler = (request, callback) -> + uploadData = request.uploadData[0].bytes + callback(uploadData) + protocol.interceptBufferProtocol 'http', handler, (error) -> + return done(error) if error + $.ajax + url: "http://fake-host" + type: "POST" + data: postData + success: (data) -> + assert.equal data, $.param postData + done() + error: (xhr, errorType, error) -> + done(error) + + describe 'protocol.interceptHttpProtocol', -> + it 'can send POST request', (done) -> + server = http.createServer (req, res) -> + body = '' + req.on 'data', (chunk) -> + body += chunk + req.on 'end', -> + res.end body + server.close() + server.listen 0, '127.0.0.1', -> + {port} = server.address() + url = "http://127.0.0.1:#{port}" + handler = (request, callback) -> + data = + url: url + method: 'POST' + uploadData: + contentType: 'application/x-www-form-urlencoded' + data: request.uploadData[0].bytes.toString() + session: null + callback(data) + protocol.interceptHttpProtocol 'http', handler, (error) -> + return done(error) if error + $.ajax + url: "http://fake-host" + type: "POST" + data: postData + success: (data) -> + assert.deepEqual qs.parse(data), postData + done() + error: (xhr, errorType, error) -> + done(error) + describe 'protocol.uninterceptProtocol', -> it 'returns error when scheme does not exist', (done) -> protocol.uninterceptProtocol 'not-exist', (error) -> diff --git a/spec/api-session-spec.coffee b/spec/api-session-spec.coffee index 6d0a8ac16..03c62c1f1 100644 --- a/spec/api-session-spec.coffee +++ b/spec/api-session-spec.coffee @@ -49,12 +49,11 @@ describe 'session module', -> it 'should remove cookies', (done) -> session.defaultSession.cookies.set {url: url, name: '2', value: '2'}, (error) -> return done(error) if error - session.defaultSession.cookies.remove {url: url, name: '2'}, (error) -> - return done(error) if error + session.defaultSession.cookies.remove url, '2', -> session.defaultSession.cookies.get {url: url}, (error, list) -> return done(error) if error for cookie in list when cookie.name is '2' - return done('Cookie not deleted') + return done('Cookie not deleted') done() describe 'session.clearStorageData(options)', -> @@ -87,23 +86,43 @@ describe 'session module', -> res.end mockPDF downloadServer.close() - it 'can download successfully', (done) -> + assertDownload = (event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename, port) -> + assert.equal state, 'completed' + assert.equal filename, 'mock.pdf' + assert.equal url, "http://127.0.0.1:#{port}/" + assert.equal mimeType, 'application/pdf' + assert.equal receivedBytes, mockPDF.length + assert.equal totalBytes, mockPDF.length + assert.equal disposition, contentDisposition + assert fs.existsSync downloadFilePath + fs.unlinkSync downloadFilePath + + it 'can download using BrowserWindow.loadURL', (done) -> downloadServer.listen 0, '127.0.0.1', -> {port} = downloadServer.address() ipcRenderer.sendSync 'set-download-option', false w.loadURL "#{url}:#{port}" ipcRenderer.once 'download-done', (event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) -> - assert.equal state, 'completed' - assert.equal filename, 'mock.pdf' - assert.equal url, "http://127.0.0.1:#{port}/" - assert.equal mimeType, 'application/pdf' - assert.equal receivedBytes, mockPDF.length - assert.equal totalBytes, mockPDF.length - assert.equal disposition, contentDisposition - assert fs.existsSync downloadFilePath - fs.unlinkSync downloadFilePath + assertDownload event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename, port done() + it 'can download using WebView.downloadURL', (done) -> + downloadServer.listen 0, '127.0.0.1', -> + {port} = downloadServer.address() + ipcRenderer.sendSync 'set-download-option', false + + webview = new WebView + webview.src = "file://#{fixtures}/api/blank.html" + webview.addEventListener 'did-finish-load', -> + webview.downloadURL "#{url}:#{port}/" + + ipcRenderer.once 'download-done', (event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) -> + assertDownload event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename, port + document.body.removeChild(webview) + done() + + document.body.appendChild webview + it 'can cancel download', (done) -> downloadServer.listen 0, '127.0.0.1', -> {port} = downloadServer.address() diff --git a/spec/api-web-frame-spec.coffee b/spec/api-web-frame-spec.coffee new file mode 100644 index 000000000..cece32908 --- /dev/null +++ b/spec/api-web-frame-spec.coffee @@ -0,0 +1,18 @@ +assert = require 'assert' +path = require 'path' + +{webFrame} = require 'electron' + +describe 'webFrame module', -> + fixtures = path.resolve __dirname, 'fixtures' + + describe 'webFrame.registerURLSchemeAsPrivileged', -> + it 'supports fetch api', (done) -> + webFrame.registerURLSchemeAsPrivileged 'file' + url = "file://#{fixtures}/assets/logo.png" + + fetch(url).then((response) -> + assert response.ok + done() + ).catch (err) -> + done('unexpected error : ' + err) diff --git a/spec/api-web-request-spec.coffee b/spec/api-web-request-spec.coffee new file mode 100644 index 000000000..5c78ef1d3 --- /dev/null +++ b/spec/api-web-request-spec.coffee @@ -0,0 +1,243 @@ +assert = require 'assert' +http = require 'http' + +{remote} = require 'electron' +{session} = remote + +describe 'webRequest module', -> + ses = session.defaultSession + server = http.createServer (req, res) -> + res.setHeader('Custom', ['Header']) + content = req.url + if req.headers.accept is '*/*;test/header' + content += 'header/received' + res.end content + defaultURL = null + + before (done) -> + server.listen 0, '127.0.0.1', -> + {port} = server.address() + defaultURL = "http://127.0.0.1:#{port}/" + done() + after -> + server.close() + + describe 'webRequest.onBeforeRequest', -> + afterEach -> + ses.webRequest.onBeforeRequest null + + it 'can cancel the request', (done) -> + ses.webRequest.onBeforeRequest (details, callback) -> + callback(cancel: true) + $.ajax + url: defaultURL + success: (data) -> done('unexpected success') + error: (xhr, errorType, error) -> done() + + it 'can filter URLs', (done) -> + filter = urls: ["#{defaultURL}filter/*"] + ses.webRequest.onBeforeRequest filter, (details, callback) -> + callback(cancel: true) + $.ajax + url: "#{defaultURL}nofilter/test" + success: (data) -> + assert.equal data, '/nofilter/test' + $.ajax + url: "#{defaultURL}filter/test" + success: (data) -> done('unexpected success') + error: (xhr, errorType, error) -> done() + error: (xhr, errorType, error) -> done(errorType) + + it 'receives details object', (done) -> + ses.webRequest.onBeforeRequest (details, callback) -> + assert.equal typeof details.id, 'number' + assert.equal typeof details.timestamp, 'number' + assert.equal details.url, defaultURL + assert.equal details.method, 'GET' + assert.equal details.resourceType, 'xhr' + callback({}) + $.ajax + url: defaultURL + success: (data) -> + assert.equal data, '/' + done() + error: (xhr, errorType, error) -> done(errorType) + + it 'can redirect the request', (done) -> + ses.webRequest.onBeforeRequest (details, callback) -> + if details.url is defaultURL + callback(redirectURL: "#{defaultURL}redirect") + else + callback({}) + $.ajax + url: defaultURL + success: (data) -> + assert.equal data, '/redirect' + done() + error: (xhr, errorType, error) -> done(errorType) + + describe 'webRequest.onBeforeSendHeaders', -> + afterEach -> + ses.webRequest.onBeforeSendHeaders null + + it 'receives details object', (done) -> + ses.webRequest.onBeforeSendHeaders (details, callback) -> + assert.equal typeof details.requestHeaders, 'object' + callback({}) + $.ajax + url: defaultURL + success: (data) -> + assert.equal data, '/' + done() + error: (xhr, errorType, error) -> done(errorType) + + it 'can change the request headers', (done) -> + ses.webRequest.onBeforeSendHeaders (details, callback) -> + {requestHeaders} = details + requestHeaders.Accept = '*/*;test/header' + callback({requestHeaders}) + $.ajax + url: defaultURL + success: (data, textStatus, request) -> + assert.equal data, '/header/received' + done() + error: (xhr, errorType, error) -> done(errorType) + + it 'resets the whole headers', (done) -> + requestHeaders = Test: 'header' + ses.webRequest.onBeforeSendHeaders (details, callback) -> + callback({requestHeaders}) + ses.webRequest.onSendHeaders (details) -> + assert.deepEqual details.requestHeaders, requestHeaders + done() + $.ajax + url: defaultURL + error: (xhr, errorType, error) -> done(errorType) + + describe 'webRequest.onSendHeaders', -> + afterEach -> + ses.webRequest.onSendHeaders null + + it 'receives details object', (done) -> + ses.webRequest.onSendHeaders (details) -> + assert.equal typeof details.requestHeaders, 'object' + $.ajax + url: defaultURL + success: (data) -> + assert.equal data, '/' + done() + error: (xhr, errorType, error) -> done(errorType) + + describe 'webRequest.onHeadersReceived', -> + afterEach -> + ses.webRequest.onHeadersReceived null + + it 'receives details object', (done) -> + ses.webRequest.onHeadersReceived (details, callback) -> + assert.equal details.statusLine, 'HTTP/1.1 200 OK' + assert.equal details.statusCode, 200 + assert.equal details.responseHeaders['Custom'], 'Header' + callback({}) + $.ajax + url: defaultURL + success: (data) -> + assert.equal data, '/' + done() + error: (xhr, errorType, error) -> done(errorType) + + it 'can change the response header', (done) -> + ses.webRequest.onHeadersReceived (details, callback) -> + {responseHeaders} = details + responseHeaders['Custom'] = ['Changed'] + callback({responseHeaders}) + $.ajax + url: defaultURL + success: (data, status, xhr) -> + assert.equal xhr.getResponseHeader('Custom'), 'Changed' + assert.equal data, '/' + done() + error: (xhr, errorType, error) -> done(errorType) + + it 'does not change header by default', (done) -> + ses.webRequest.onHeadersReceived (details, callback) -> + callback({}) + $.ajax + url: defaultURL + success: (data, status, xhr) -> + assert.equal xhr.getResponseHeader('Custom'), 'Header' + assert.equal data, '/' + done() + error: (xhr, errorType, error) -> done(errorType) + + describe 'webRequest.onResponseStarted', -> + afterEach -> + ses.webRequest.onResponseStarted null + + it 'receives details object', (done) -> + ses.webRequest.onResponseStarted (details) -> + assert.equal typeof details.fromCache, 'boolean' + assert.equal details.statusLine, 'HTTP/1.1 200 OK' + assert.equal details.statusCode, 200 + assert.equal details.responseHeaders['Custom'], 'Header' + $.ajax + url: defaultURL + success: (data, status, xhr) -> + assert.equal xhr.getResponseHeader('Custom'), 'Header' + assert.equal data, '/' + done() + error: (xhr, errorType, error) -> done(errorType) + + describe 'webRequest.onBeforeRedirect', -> + afterEach -> + ses.webRequest.onBeforeRedirect null + ses.webRequest.onBeforeRequest null + + it 'receives details object', (done) -> + redirectURL = "#{defaultURL}redirect" + ses.webRequest.onBeforeRequest (details, callback) -> + if details.url is defaultURL + callback({redirectURL}) + else + callback({}) + ses.webRequest.onBeforeRedirect (details) -> + assert.equal typeof details.fromCache, 'boolean' + assert.equal details.statusLine, 'HTTP/1.1 307 Internal Redirect' + assert.equal details.statusCode, 307 + assert.equal details.redirectURL, redirectURL + $.ajax + url: defaultURL + success: (data, status, xhr) -> + assert.equal data, '/redirect' + done() + error: (xhr, errorType, error) -> done(errorType) + + describe 'webRequest.onCompleted', -> + afterEach -> + ses.webRequest.onCompleted null + + it 'receives details object', (done) -> + ses.webRequest.onCompleted (details) -> + assert.equal typeof details.fromCache, 'boolean' + assert.equal details.statusLine, 'HTTP/1.1 200 OK' + assert.equal details.statusCode, 200 + $.ajax + url: defaultURL + success: (data, status, xhr) -> + assert.equal data, '/' + done() + error: (xhr, errorType, error) -> done(errorType) + + describe 'webRequest.onErrorOccurred', -> + afterEach -> + ses.webRequest.onErrorOccurred null + ses.webRequest.onBeforeRequest null + + it 'receives details object', (done) -> + ses.webRequest.onBeforeRequest (details, callback) -> + callback(cancel: true) + ses.webRequest.onErrorOccurred (details) -> + assert.equal details.error, 'net::ERR_BLOCKED_BY_CLIENT' + done() + $.ajax + url: defaultURL + success: (data) -> done('unexpected success') diff --git a/spec/asar-spec.coffee b/spec/asar-spec.coffee index ad480a582..7642283ce 100644 --- a/spec/asar-spec.coffee +++ b/spec/asar-spec.coffee @@ -374,6 +374,18 @@ describe 'asar package', -> assert.equal err.code, 'ENOENT' done() + describe 'fs.mkdir', -> + it 'throws error when calling inside asar archive', (done) -> + p = path.join fixtures, 'asar', 'a.asar', 'not-exist' + fs.mkdir p, (err) -> + assert.equal err.code, 'ENOTDIR' + done() + + describe 'fs.mkdirSync', -> + it 'throws error when calling inside asar archive', -> + p = path.join fixtures, 'asar', 'a.asar', 'not-exist' + assert.throws (-> fs.mkdirSync p), new RegExp('ENOTDIR') + describe 'child_process.fork', -> child_process = require 'child_process' @@ -392,6 +404,23 @@ describe 'asar package', -> done() child.send file + describe 'child_process.execFile', -> + return unless process.platform is 'darwin' + + {execFile, execFileSync} = require 'child_process' + echo = path.join fixtures, 'asar', 'echo.asar', 'echo' + + it 'executes binaries', (done) -> + child = execFile echo, ['test'], (error, stdout) -> + assert.equal error, null + assert.equal stdout, 'test\n' + done() + + # execFileSync makes the test flaky after a refresh. + xit 'execFileSync executes binaries', -> + output = execFileSync echo, ['test'] + assert.equal String(output), 'test\n' + describe 'internalModuleReadFile', -> internalModuleReadFile = process.binding('fs').internalModuleReadFile @@ -407,6 +436,43 @@ describe 'asar package', -> p = path.join fixtures, 'asar', 'unpack.asar', 'a.txt' assert.equal internalModuleReadFile(p).toString().trim(), 'a' + describe 'process.noAsar', -> + errorName = if process.platform is 'win32' then 'ENOENT' else 'ENOTDIR' + + beforeEach -> + process.noAsar = true + afterEach -> + process.noAsar = false + + it 'disables asar support in sync API', -> + file = path.join fixtures, 'asar', 'a.asar', 'file1' + dir = path.join fixtures, 'asar', 'a.asar', 'dir1' + assert.throws (-> fs.readFileSync file), new RegExp(errorName) + assert.throws (-> fs.lstatSync file), new RegExp(errorName) + assert.throws (-> fs.realpathSync file), new RegExp(errorName) + assert.throws (-> fs.readdirSync dir), new RegExp(errorName) + + it 'disables asar support in async API', (done) -> + file = path.join fixtures, 'asar', 'a.asar', 'file1' + dir = path.join fixtures, 'asar', 'a.asar', 'dir1' + fs.readFile file, (error) -> + assert.equal error.code, errorName + fs.lstat file, (error) -> + assert.equal error.code, errorName + fs.realpath file, (error) -> + assert.equal error.code, errorName + fs.readdir dir, (error) -> + assert.equal error.code, errorName + done() + + it 'treats *.asar as normal file', -> + originalFs = require 'original-fs' + asar = path.join fixtures, 'asar', 'a.asar' + content1 = fs.readFileSync asar + content2 = originalFs.readFileSync asar + assert.equal content1.compare(content2), 0 + assert.throws (-> fs.readdirSync asar), /ENOTDIR/ + describe 'asar protocol', -> url = require 'url' @@ -493,6 +559,13 @@ describe 'asar package', -> it 'does not touch global fs object', -> assert.notEqual fs.readdir, gfs.readdir + describe 'mkdirp module', -> + mkdirp = require 'mkdirp' + + it 'throws error when calling inside asar archive', -> + p = path.join fixtures, 'asar', 'a.asar', 'not-exist' + assert.throws (-> mkdirp.sync p), new RegExp('ENOTDIR') + describe 'native-image', -> it 'reads image from asar archive', -> p = path.join fixtures, 'asar', 'logo.asar', 'logo.png' diff --git a/spec/chromium-spec.coffee b/spec/chromium-spec.coffee index 435f7bbbe..cc28f2f7a 100644 --- a/spec/chromium-spec.coffee +++ b/spec/chromium-spec.coffee @@ -5,7 +5,7 @@ path = require 'path' ws = require 'ws' {remote} = require 'electron' -{BrowserWindow} = remote.require 'electron' +{BrowserWindow, session} = remote.require 'electron' describe 'chromium feature', -> fixtures = path.resolve __dirname, 'fixtures' @@ -45,7 +45,15 @@ describe 'chromium feature', -> done() w.loadURL url - describe 'navigator.webkitGetUserMedia', -> + it 'is set correctly when window is inactive', (done) -> + w = new BrowserWindow(show:false) + w.webContents.on 'ipc-message', (event, args) -> + assert.deepEqual args, ['hidden', false] + done() + w.showInactive() + w.loadURL url + + xdescribe 'navigator.webkitGetUserMedia', -> it 'calls its callbacks', (done) -> @timeout 5000 navigator.webkitGetUserMedia audio: true, video: false, @@ -56,8 +64,28 @@ describe 'chromium feature', -> it 'should not be empty', -> assert.notEqual navigator.language, '' + describe 'navigator.serviceWorker', -> + url = "file://#{fixtures}/pages/service-worker/index.html" + w = null + + afterEach -> + w?.destroy() + + it 'should register for file scheme', (done) -> + w = new BrowserWindow(show:false) + w.webContents.on 'ipc-message', (event, args) -> + if args[0] == 'reload' + w.webContents.reload() + else if args[0] == 'error' + done('unexpected error : ' + args[1]) + else if args[0] == 'response' + assert.equal args[1], 'Hello from serviceWorker!' + session.defaultSession.clearStorageData {storages: ['serviceworkers']}, -> + done() + w.loadURL url + describe 'window.open', -> - @timeout 10000 + @timeout 20000 it 'returns a BrowserWindowProxy object', -> b = window.open 'about:blank', '', 'show=no' @@ -75,13 +103,22 @@ describe 'chromium feature', -> it 'inherit options of parent window', (done) -> listener = (event) -> - size = remote.getCurrentWindow().getSize() - assert.equal event.data, "size: #{size.width} #{size.height}" + [width, height] = remote.getCurrentWindow().getSize() + assert.equal event.data, "size: #{width} #{height}" b.close() done() window.addEventListener 'message', listener b = window.open "file://#{fixtures}/pages/window-open-size.html", '', 'show=no' + it 'does not override child options', (done) -> + size = {width: 350, height: 450} + listener = (event) -> + assert.equal event.data, "size: #{size.width} #{size.height}" + b.close() + done() + window.addEventListener 'message', listener + b = window.open "file://#{fixtures}/pages/window-open-size.html", '', "show=no,width=#{size.width},height=#{size.height}" + describe 'window.opener', -> @timeout 10000 @@ -106,12 +143,30 @@ describe 'chromium feature', -> window.addEventListener 'message', listener b = window.open url, '', 'show=no' + describe 'window.postMessage', -> + it 'sets the source and origin correctly', (done) -> + sourceId = remote.getCurrentWindow().id + listener = (event) -> + window.removeEventListener 'message', listener + b.close() + message = JSON.parse(event.data) + assert.equal message.data, 'testing' + assert.equal message.origin, 'file://' + assert.equal message.sourceEqualsOpener, true + assert.equal message.sourceId, sourceId + assert.equal event.origin, 'file://' + done() + window.addEventListener 'message', listener + b = window.open "file://#{fixtures}/pages/window-open-postMessage.html", '', 'show=no' + BrowserWindow.fromId(b.guestId).webContents.once 'did-finish-load', -> + b.postMessage('testing', '*') + describe 'window.opener.postMessage', -> it 'sets source and origin correctly', (done) -> listener = (event) -> window.removeEventListener 'message', listener b.close() - assert.equal event.source.guestId, b.guestId + assert.equal event.source, b assert.equal event.origin, 'file://' done() window.addEventListener 'message', listener @@ -201,7 +256,7 @@ describe 'chromium feature', -> setImmediate -> called = false Promise.resolve().then -> - done(if called then undefined else new Error('wrong sequnce')) + done(if called then undefined else new Error('wrong sequence')) document.createElement 'x-element' called = true @@ -215,6 +270,6 @@ describe 'chromium feature', -> remote.getGlobal('setImmediate') -> called = false Promise.resolve().then -> - done(if called then undefined else new Error('wrong sequnce')) + done(if called then undefined else new Error('wrong sequence')) document.createElement 'y-element' called = true diff --git a/spec/fixtures/api/quit-app/main.js b/spec/fixtures/api/quit-app/main.js new file mode 100644 index 000000000..e2f97affe --- /dev/null +++ b/spec/fixtures/api/quit-app/main.js @@ -0,0 +1,12 @@ +var app = require('electron').app + +app.on('ready', function () { + // This setImmediate call gets the spec passing on Linux + setImmediate(function () { + app.exit(123) + }) +}) + +process.on('exit', function (code) { + console.log('Exit event with code: ' + code) +}) diff --git a/spec/fixtures/api/quit-app/package.json b/spec/fixtures/api/quit-app/package.json new file mode 100644 index 000000000..ea5bb1643 --- /dev/null +++ b/spec/fixtures/api/quit-app/package.json @@ -0,0 +1,4 @@ +{ + "name": "quit-app", + "main": "main.js" +} diff --git a/spec/fixtures/asar/echo.asar b/spec/fixtures/asar/echo.asar new file mode 100644 index 000000000..4d72f7a92 Binary files /dev/null and b/spec/fixtures/asar/echo.asar differ diff --git a/spec/fixtures/assets/LICENSE b/spec/fixtures/assets/LICENSE new file mode 100644 index 000000000..ec56b38f4 --- /dev/null +++ b/spec/fixtures/assets/LICENSE @@ -0,0 +1,3 @@ +tone.wav +http://soundbible.com/1815-A-Tone.html +License: Public Domain diff --git a/spec/fixtures/assets/tone.wav b/spec/fixtures/assets/tone.wav new file mode 100644 index 000000000..7fbc54cbe Binary files /dev/null and b/spec/fixtures/assets/tone.wav differ diff --git a/spec/fixtures/module/print_name.js b/spec/fixtures/module/print_name.js index 09ec33341..01d13f4ba 100644 --- a/spec/fixtures/module/print_name.js +++ b/spec/fixtures/module/print_name.js @@ -1,3 +1,7 @@ exports.print = function(obj) { return obj.constructor.name; } + +exports.echo = function(obj) { + return obj; +} diff --git a/spec/fixtures/pages/audio.html b/spec/fixtures/pages/audio.html new file mode 100644 index 000000000..0fda8e707 --- /dev/null +++ b/spec/fixtures/pages/audio.html @@ -0,0 +1 @@ + diff --git a/spec/fixtures/pages/base-page.html b/spec/fixtures/pages/base-page.html new file mode 100644 index 000000000..7879e1ce9 --- /dev/null +++ b/spec/fixtures/pages/base-page.html @@ -0,0 +1,4 @@ + + + + diff --git a/spec/fixtures/pages/content.html b/spec/fixtures/pages/content.html new file mode 100644 index 000000000..e37ba34c1 --- /dev/null +++ b/spec/fixtures/pages/content.html @@ -0,0 +1,16 @@ +

+ Virtual member functions are key to the object-oriented paradigm, + such as making it easy for old code to call new code. + A virtual function allows derived classes to replace the implementation + provided by the base class. The compiler makes sure the replacement is + always called whenever the object in question is actually of the derived class, + even if the object is accessed by a base pointer rather than a derived pointer. + This allows algorithms in the base class to be replaced in the derived class, + even if users dont know about the derived class. + + class A { + public: + virtual void foo() {} + } + +

diff --git a/spec/fixtures/pages/service-worker/index.html b/spec/fixtures/pages/service-worker/index.html new file mode 100644 index 000000000..3495827a2 --- /dev/null +++ b/spec/fixtures/pages/service-worker/index.html @@ -0,0 +1,18 @@ + diff --git a/spec/fixtures/pages/service-worker/service-worker.js b/spec/fixtures/pages/service-worker/service-worker.js new file mode 100644 index 000000000..854b3558a --- /dev/null +++ b/spec/fixtures/pages/service-worker/service-worker.js @@ -0,0 +1,9 @@ +self.addEventListener('fetch', function(event) { + var requestUrl = new URL(event.request.url); + + if (requestUrl.pathname === '/echo' && + event.request.headers.has('X-Mock-Response')) { + var mockResponse = new Response('Hello from serviceWorker!'); + event.respondWith(mockResponse); + } +}); diff --git a/spec/fixtures/pages/theme-color.html b/spec/fixtures/pages/theme-color.html new file mode 100644 index 000000000..57c49f337 --- /dev/null +++ b/spec/fixtures/pages/theme-color.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/spec/fixtures/pages/webview-did-navigate-in-page-with-hash.html b/spec/fixtures/pages/webview-did-navigate-in-page-with-hash.html new file mode 100644 index 000000000..894095d47 --- /dev/null +++ b/spec/fixtures/pages/webview-did-navigate-in-page-with-hash.html @@ -0,0 +1,9 @@ + + + + + diff --git a/spec/fixtures/pages/webview-did-navigate-in-page-with-history.html b/spec/fixtures/pages/webview-did-navigate-in-page-with-history.html new file mode 100644 index 000000000..b32b7ebcd --- /dev/null +++ b/spec/fixtures/pages/webview-did-navigate-in-page-with-history.html @@ -0,0 +1,9 @@ + + + + + diff --git a/spec/fixtures/pages/webview-did-navigate-in-page.html b/spec/fixtures/pages/webview-did-navigate-in-page.html new file mode 100644 index 000000000..2686b414f --- /dev/null +++ b/spec/fixtures/pages/webview-did-navigate-in-page.html @@ -0,0 +1,12 @@ + + + Click me. + This is content. + + + diff --git a/spec/fixtures/pages/webview-will-navigate.html b/spec/fixtures/pages/webview-will-navigate.html new file mode 100644 index 000000000..52cb4966b --- /dev/null +++ b/spec/fixtures/pages/webview-will-navigate.html @@ -0,0 +1,11 @@ + + + Test + + + diff --git a/spec/fixtures/pages/window-open-postMessage.html b/spec/fixtures/pages/window-open-postMessage.html new file mode 100644 index 000000000..0e7e4d8cc --- /dev/null +++ b/spec/fixtures/pages/window-open-postMessage.html @@ -0,0 +1,14 @@ + + + + + diff --git a/spec/fixtures/pages/window-open-size.html b/spec/fixtures/pages/window-open-size.html index b32e39889..93b5039f7 100644 --- a/spec/fixtures/pages/window-open-size.html +++ b/spec/fixtures/pages/window-open-size.html @@ -2,7 +2,7 @@ diff --git a/spec/package.json b/spec/package.json index 38bd83796..6d49f0da8 100644 --- a/spec/package.json +++ b/spec/package.json @@ -5,13 +5,15 @@ "version": "0.1.0", "devDependencies": { "basic-auth": "^1.0.0", - "multiparty": "4.1.2", "graceful-fs": "3.0.5", "mocha": "2.1.0", + "mkdirp": "0.5.1", + "multiparty": "4.1.2", "q": "0.9.7", "temp": "0.8.1", "walkdir": "0.0.7", - "ws": "0.7.2" + "ws": "0.7.2", + "yargs": "^3.31.0" }, "optionalDependencies": { "ffi": "2.0.0", diff --git a/spec/static/index.html b/spec/static/index.html index ea86f6ee3..f958e1b7e 100644 --- a/spec/static/index.html +++ b/spec/static/index.html @@ -18,8 +18,7 @@ var remote = electron.remote; var ipcRenderer = electron.ipcRenderer; - var argv = remote.process.argv; - var isCi = argv[2] == '--ci'; + var isCi = remote.getGlobal('isCi') if (!isCi) { var win = remote.getCurrentWindow(); diff --git a/spec/static/main.js b/spec/static/main.js index be3690cd9..b295ba185 100644 --- a/spec/static/main.js +++ b/spec/static/main.js @@ -1,3 +1,6 @@ +// Disable use of deprecated functions. +process.throwDeprecation = true; + const electron = require('electron'); const app = electron.app; const ipcMain = electron.ipcMain; @@ -5,6 +8,13 @@ const dialog = electron.dialog; const BrowserWindow = electron.BrowserWindow; const path = require('path'); +const url = require('url'); + +var argv = require('yargs') + .boolean('ci') + .string('g').alias('g', 'grep') + .boolean('i').alias('i', 'invert') + .argv; var window = null; process.port = 0; // will be used by crash-reporter spec. @@ -42,7 +52,8 @@ ipcMain.on('echo', function(event, msg) { event.returnValue = msg; }); -if (process.argv[2] == '--ci') { +global.isCi = !!argv.ci; +if (global.isCi) { process.removeAllListeners('uncaughtException'); process.on('uncaughtException', function(error) { console.error(error, error.stack); @@ -67,7 +78,14 @@ app.on('ready', function() { javascript: true // Test whether web-preferences crashes. }, }); - window.loadURL('file://' + __dirname + '/index.html'); + window.loadURL(url.format({ + pathname: __dirname + '/index.html', + protocol: 'file', + query: { + grep: argv.grep, + invert: argv.invert ? 'true': '' + } + })); window.on('unresponsive', function() { var chosen = dialog.showMessageBox(window, { type: 'warning', diff --git a/spec/webview-spec.coffee b/spec/webview-spec.coffee index c60af8d74..4754ea434 100644 --- a/spec/webview-spec.coffee +++ b/spec/webview-spec.coffee @@ -1,6 +1,7 @@ assert = require 'assert' path = require 'path' http = require 'http' +url = require 'url' describe ' tag', -> @timeout 10000 @@ -261,16 +262,71 @@ describe ' tag', -> it 'emits when favicon urls are received', (done) -> webview.addEventListener 'page-favicon-updated', (e) -> assert.equal e.favicons.length, 2 - url = + pageUrl = if process.platform is 'win32' 'file:///C:/favicon.png' else 'file:///favicon.png' - assert.equal e.favicons[0], url + assert.equal e.favicons[0], pageUrl done() webview.src = "file://#{fixtures}/pages/a.html" document.body.appendChild webview + describe 'will-navigate event', -> + it 'emits when a url that leads to oustide of the page is clicked', (done) -> + webview.addEventListener 'will-navigate', (e) -> + assert.equal e.url, "http://host/" + done() + + webview.src = "file://#{fixtures}/pages/webview-will-navigate.html" + document.body.appendChild webview + + describe 'did-navigate event', -> + p = path.join fixtures, 'pages', 'webview-will-navigate.html' + p = p.replace /\\/g, '/' + pageUrl = url.format protocol: 'file', slashes: true, pathname: p + + it 'emits when a url that leads to outside of the page is clicked', (done) -> + webview.addEventListener 'did-navigate', (e) -> + assert.equal e.url, pageUrl + done() + + webview.src = pageUrl + document.body.appendChild webview + + describe 'did-navigate-in-page event', -> + it 'emits when an anchor link is clicked', (done) -> + p = path.join fixtures, 'pages', 'webview-did-navigate-in-page.html' + p = p.replace /\\/g, '/' + pageUrl = url.format protocol: 'file', slashes: true, pathname: p + + webview.addEventListener 'did-navigate-in-page', (e) -> + assert.equal e.url, "#{pageUrl}#test_content" + done() + + webview.src = pageUrl + document.body.appendChild webview + + it 'emits when window.history.replaceState is called', (done) -> + webview.addEventListener 'did-navigate-in-page', (e) -> + assert.equal e.url, "http://host/" + done() + + webview.src = "file://#{fixtures}/pages/webview-did-navigate-in-page-with-history.html" + document.body.appendChild webview + + it 'emits when window.location.hash is changed', (done) -> + p = path.join fixtures, 'pages', 'webview-did-navigate-in-page-with-hash.html' + p = p.replace /\\/g, '/' + pageUrl = url.format protocol: 'file', slashes: true, pathname: p + + webview.addEventListener 'did-navigate-in-page', (e) -> + assert.equal e.url, "#{pageUrl}#test" + done() + + webview.src = pageUrl + document.body.appendChild webview + describe 'close event', -> it 'should fire when interior page calls window.close', (done) -> webview.addEventListener 'close', -> @@ -279,6 +335,52 @@ describe ' tag', -> webview.src = "file://#{fixtures}/pages/close.html" document.body.appendChild webview + describe 'devtools-opened event', -> + it 'should fire when webview.openDevTools() is called', (done) -> + listener = -> + webview.removeEventListener 'devtools-opened', listener + webview.closeDevTools() + done() + + webview.addEventListener 'devtools-opened', listener + webview.addEventListener 'dom-ready', -> + webview.openDevTools() + + webview.src = "file://#{fixtures}/pages/base-page.html" + document.body.appendChild webview + + describe 'devtools-closed event', -> + it 'should fire when webview.closeDevTools() is called', (done) -> + listener2 = -> + webview.removeEventListener 'devtools-closed', listener2 + done() + + listener = -> + webview.removeEventListener 'devtools-opened', listener + webview.closeDevTools() + + webview.addEventListener 'devtools-opened', listener + webview.addEventListener 'devtools-closed', listener2 + webview.addEventListener 'dom-ready', -> + webview.openDevTools() + + webview.src = "file://#{fixtures}/pages/base-page.html" + document.body.appendChild webview + + describe 'devtools-focused event', -> + it 'should fire when webview.openDevTools() is called', (done) -> + listener = -> + webview.removeEventListener 'devtools-focused', listener + webview.closeDevTools() + done() + + webview.addEventListener 'devtools-focused', listener + webview.addEventListener 'dom-ready', -> + webview.openDevTools() + + webview.src = "file://#{fixtures}/pages/base-page.html" + document.body.appendChild webview + describe '.reload()', -> it 'should emit beforeunload handler', (done) -> listener = (e) -> @@ -379,3 +481,37 @@ describe ' tag', -> webview.src = "file://#{fixtures}/pages/onmouseup.html" webview.setAttribute 'nodeintegration', 'on' document.body.appendChild webview + + describe 'media-started-playing media-paused events', -> + it 'emits when audio starts and stops playing', (done) -> + audioPlayed = false + webview.addEventListener 'media-started-playing', -> + audioPlayed = true + webview.addEventListener 'media-paused', -> + assert audioPlayed + done() + webview.src = "file://#{fixtures}/pages/audio.html" + document.body.appendChild webview + + describe 'found-in-page event', -> + it 'emits when a request is made', (done) -> + requestId = null + listener = (e) -> + assert.equal e.result.requestId, requestId + if e.result.finalUpdate + assert.equal e.result.matches, 3 + webview.stopFindInPage "clearSelection" + done() + listener2 = (e) -> + requestId = webview.findInPage "virtual" + webview.addEventListener 'found-in-page', listener + webview.addEventListener 'did-finish-load', listener2 + webview.src = "file://#{fixtures}/pages/content.html" + document.body.appendChild webview + + xdescribe 'did-change-theme-color event', -> + it 'emits when theme color changes', (done) -> + webview.addEventListener 'did-change-theme-color', (e) -> + done() + webview.src = "file://#{fixtures}/pages/theme-color.html" + document.body.appendChild webview diff --git a/tools/mac/copy-locales.py b/tools/mac/copy-locales.py new file mode 100755 index 000000000..30d478800 --- /dev/null +++ b/tools/mac/copy-locales.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# Copyright (c) 2013 GitHub, Inc. +# Use of this source code is governed by the MIT license that can be +# found in the LICENSE file. + +import errno +import optparse +import os +import shutil +import sys + +def main(argv): + parser = optparse.OptionParser() + usage = 'usage: %s [options ...] src dest locale_list' + parser.set_usage(usage.replace('%s', '%prog')) + parser.add_option('-d', dest='dash_to_underscore', action="store_true", + default=False, + help='map "en-US" to "en" and "-" to "_" in locales') + + (options, arglist) = parser.parse_args(argv) + + if len(arglist) < 4: + print 'ERROR: need src, dest and list of locales' + return 1 + + src = arglist[1] + dest = arglist[2] + locales = arglist[3:] + + for locale in locales: + # For Cocoa to find the locale at runtime, it needs to use '_' instead + # of '-' (http://crbug.com/20441). Also, 'en-US' should be represented + # simply as 'en' (http://crbug.com/19165, http://crbug.com/25578). + dirname = locale + if options.dash_to_underscore: + if locale == 'en-US': + dirname = 'en' + else: + dirname = locale.replace('-', '_') + + dirname = os.path.join(dest, dirname + '.lproj') + safe_mkdir(dirname) + shutil.copy2(os.path.join(src, locale + '.pak'), + os.path.join(dirname, 'locale.pak')) + + +def safe_mkdir(path): + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/vendor/brightray b/vendor/brightray index d4fab3342..8550f2a03 160000 --- a/vendor/brightray +++ b/vendor/brightray @@ -1 +1 @@ -Subproject commit d4fab33427eb728a553896527f1931887ce6d9d9 +Subproject commit 8550f2a032b332d86bd8a7ec235685e22d028906 diff --git a/vendor/native_mate b/vendor/native_mate index 939849410..a3dcf8ced 160000 --- a/vendor/native_mate +++ b/vendor/native_mate @@ -1 +1 @@ -Subproject commit 93984941005bab194a2d47aff655d525c064efcb +Subproject commit a3dcf8ced663e974ac94ad5e50a1d25a43995a9d diff --git a/vendor/node b/vendor/node index 1445826ca..3b044608e 160000 --- a/vendor/node +++ b/vendor/node @@ -1 +1 @@ -Subproject commit 1445826ca73cc79bc57d503dd11d4ffaf695625c +Subproject commit 3b044608ee51ca001dabe944cade6e64f46b0724