Merge pull request #1 from atom/master

update as upstream
This commit is contained in:
Heilig Benedek 2016-01-30 00:14:40 +01:00
commit e22cb0ca9f
809 changed files with 55169 additions and 18812 deletions

2
.gitignore vendored
View file

@ -1,4 +1,6 @@
.DS_Store
.tags*
/.idea/
/build/
/dist/
/external_binaries/

View file

@ -10,6 +10,7 @@ os:
- osx
env:
- TARGET_ARCH=x64
osx_image: xcode7
matrix:
include:

24
CODE_OF_CONDUCT.md Normal file
View file

@ -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/

80
CONTRIBUTING-ko.md Normal file
View file

@ -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(코드 검사기)의 경고를 제거했을 때

View file

@ -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
@ -29,7 +30,8 @@ possible with your report. If you can, please include:
* Include screenshots and animated GIFs in your pull request whenever possible.
* Follow the CoffeeScript, JavaScript, C++ and Python [coding style defined in docs](/docs/development/coding-style.md).
* Write documentation in [Markdown](https://daringfireball.net/projects/markdown).
* Use short, present tense commit messages. See [Commit Message Styleguide](#git-commit-messages-styleguide).
See the [Documentation Styleguide](/docs/styleguide.md).
* Use short, present tense commit messages. See [Commit Message Styleguide](#git-commit-messages).
## Styleguides
@ -56,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

View file

@ -1,29 +1,39 @@
[![Electron Logo](http://electron.atom.io/images/electron-logo.svg)](http://electron.atom.io/)
[![Electron Logo](http://electron.atom.io/images/electron-logo.svg)](http://electron.atom.io/)
[![Build Status](https://travis-ci.org/atom/electron.svg?branch=master)](https://travis-ci.org/atom/electron)
[![devDependency Status](https://david-dm.org/atom/electron/dev-status.svg)](https://david-dm.org/atom/electron#info=devDependencies)
[![Join the Electron Community on Slack](http://atom-slack.herokuapp.com/badge.svg)](http://atom-slack.herokuapp.com/)
### [Electron](https://github.com/atom/electron/) 한국어 참조문서
:zap: *이전까지 Atom Shell로 알려져 있었습니다* :zap:
:zap: *프레임워크 이름이 Atom Shell에서 Electron으로 변경되었습니다* :zap:
Electron 프레임워크는 JavaScript, HTML 그리고 CSS를 사용하여
Cross-Platform 데스크톱 어플리케이션을 개발할 수 있도록 해주는 프레임워크입니다.
[Node.js](https://nodejs.org/)와 [Chromium](http://www.chromium.org)을 기반으로
만들어졌으며 [Atom Editor](https://github.com/atom/atom)에 사용되고 있습니다.
Electron은 JavaScript, HTML 그리고 CSS를 이용하여 Cross-Platform 데스크톱 어플리케이션을 개발할 수 있도록 해주는 프레임워크입니다. 이 프레임워크는 [io.js](http://iojs.org) 와
[Chromium](http://www.chromium.org) 을 기반으로 만들어 졌으며 [Atom Editor](https://github.com/atom/atom) 에 사용되고 있습니다.
Electron에 대한 중요한 알림을 받고 싶다면 Twitter에서
[@ElectronJS](https://twitter.com/electronjs)를 팔로우 하세요.
Electron에 대한 중요한 알림을 받으려면 Twitter에서 [@ElectronJS](https://twitter.com/electronjs)를 Follow하세요.
이 프로젝트는 기여자 규약 [행동강령](CODE_OF_CONDUCT.md)을 준수합니다. 따라서 이
프로젝트의 개발에 참여하려면 이 규약을 지켜야 합니다. 받아들일 수 없는 행위를 발견했을
경우 atom@github.com로 보고 하십시오.
## 다운로드
Linux, Windows, Mac용으로 미리 빌드된 Electron 바이너리와 디버그 심볼이 준비되어 있습니다. [releases](https://github.com/atom/electron/releases) 페이지에서 받아 볼 수 있습니다.
Linux, Windows, OS X 용으로 미리 빌드된 Electron 바이너리와 디버그 심볼이 준비되어
있습니다. [releases](https://github.com/atom/electron/releases) 페이지에서 받아 볼
수 있습니다.
또한 [`npm`](https://docs.npmjs.com/)을 이용하여 미리 빌드된 Electron 바이너리를 받을 수 있습니다:
또한 [`npm`](https://docs.npmjs.com/)을 통해 미리 빌드된 Electron 바이너리를 설치할
수도 있습니다:
```sh
# $PATH에 `electron` 등록하고 전역에 설치합니다.
# $PATH에 `electron` 커맨드를 등록하고 전역에 설치합니다.
npm install electron-prebuilt -g
# 개발용 dependency로 설치합니다.
# 개발 의존성 모듈 형태로 설치합니다.
npm install electron-prebuilt --save-dev
```
@ -31,13 +41,38 @@ npm install electron-prebuilt --save-dev
- [China](https://npm.taobao.org/mirrors/electron)
## 참조문서
## 참조 문서
[docs](https://github.com/atom/electron/tree/master/docs/README-ko.md) 에 프레임워크 사용 가이드와 API 레퍼런스가 있습니다.
추가적으로 Electron을 빌드 하는 방법과 프로젝트에 기여하는 방법도 문서에 포함되어 있으니 참고하기 바랍니다.
[Docs](https://github.com/atom/electron/tree/master/docs/README.md)에 개발 지침과
API 레퍼런스가 있습니다. Electron을 빌드 하는 방법과 프로젝트에 기여하는법 또한 문서에
포함되어 있으니 참고하시기 바랍니다.
## 참조 문서 (번역)
- [브라질 포르투갈어](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)
## 시작하기
[`atom/electron-quick-start`](https://github.com/atom/electron-quick-start)
저장소를 클론하여 Electron을 간단히 접해볼 수 있습니다.
## 커뮤니티
[Atom 포럼내의 `electron` 카테고리](http://discuss.atom.io/category/electron) 와 Freenode `#atom-shell` 채팅채널이 있습니다.
다음 링크를 통해 커뮤니티에 질문을 올리거나 토론을 나눌 수 있습니다:
[awesome-electron](https://github.com/sindresorhus/awesome-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) *(브라질 포르투갈어)* 커뮤니티
- [`electron-kr`](http://www.meetup.com/electron-kr/) *(한국어)* 커뮤니티
[awesome-electron](https://github.com/sindresorhus/awesome-electron) 프로젝트에
커뮤니티가 운영중인 유용한 예제 어플리케이션과 도구, 리소스가 있으니 한번 참고해 보시기
바랍니다.

View file

@ -4,22 +4,23 @@
[![devDependency Status](https://david-dm.org/atom/electron/dev-status.svg)](https://david-dm.org/atom/electron#info=devDependencies)
[![Join the Electron Community on Slack](http://atom-slack.herokuapp.com/badge.svg)](http://atom-slack.herokuapp.com/)
:zap: *formerly known as Atom Shell* :zap:
:zap: *Formerly known as Atom Shell* :zap:
The Electron framework lets you write cross-platform desktop applications
using JavaScript, HTML and CSS. It is based on [io.js](http://iojs.org) and
using JavaScript, HTML and CSS. It is based on [Node.js](https://nodejs.org/) and
[Chromium](http://www.chromium.org) and is used in the [Atom
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
Prebuilt binaries and debug symbols of Electron for Linux, Windows and Mac can
Prebuilt binaries and debug symbols of Electron for Linux, Windows and OS X can
be found on the [releases](https://github.com/atom/electron/releases) page.
You can also use [`npm`](https://docs.npmjs.com/) to install prebuilt electron
@ -43,9 +44,32 @@ Guides and the API reference are located in the
[docs](https://github.com/atom/electron/tree/master/docs) directory. It also
contains documents describing how to build and contribute to Electron.
## Documentation Translations
- [Brazilian Portuguese](https://github.com/atom/electron/tree/master/docs-translations/pt-BR)
- [Korean](https://github.com/atom/electron/tree/master/docs-translations/ko-KR)
- [Japanese](https://github.com/atom/electron/tree/master/docs-translations/jp)
- [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
Clone and run the [`atom/electron-quick-start`](https://github.com/atom/electron-quick-start)
repository to see a minimal Electron app in action.
## Community
There is an [`electron` category on the Atom forums](http://discuss.atom.io/category/electron)
as well as an `#atom-shell` channel on Freenode.
You can ask questions and interact with the community in the following
locations:
- [`electron`](http://discuss.atom.io/c/electron) category on the Atom
forums
- `#atom-shell` channel on Freenode
- [`Atom`](http://atom-slack.herokuapp.com/) channel on Slack
- [`electron-br`](https://electron-br.slack.com) *(Brazilian Portuguese)*
- [`electron-kr`](http://www.meetup.com/electron-kr/) *(Korean)*
Check out [awesome-electron](https://github.com/sindresorhus/awesome-electron) for a community maintained list of useful example apps, tools and resources.
Check out [awesome-electron](https://github.com/sindresorhus/awesome-electron)
for a community maintained list of useful example apps, tools and resources.

23
appveyor.yml Normal file
View file

@ -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

156
atom.gyp
View file

@ -4,7 +4,7 @@
'product_name%': 'Electron',
'company_name%': 'GitHub, Inc',
'company_abbr%': 'github',
'version%': '0.30.4',
'version%': '0.36.6',
},
'includes': [
'filenames.gypi',
@ -28,7 +28,7 @@
'target_name': '<(project_name)',
'type': 'executable',
'dependencies': [
'compile_coffee',
'js2asar',
'<(project_name)_lib',
],
'sources': [
@ -64,9 +64,6 @@
'files': [
'<(PRODUCT_DIR)/<(product_name) Helper.app',
'<(PRODUCT_DIR)/<(product_name) Framework.framework',
'external_binaries/Squirrel.framework',
'external_binaries/ReactiveCocoa.framework',
'external_binaries/Mantle.framework',
],
},
{
@ -109,10 +106,20 @@
'<@(locale_dirs)',
],
},
]
}, { # OS=="mac"
'dependencies': [
'make_locale_paks',
],
'conditions': [
['mas_build==0', {
'copies': [
{
'destination': '<(PRODUCT_DIR)/<(product_name).app/Contents/Frameworks',
'files': [
'external_binaries/Squirrel.framework',
'external_binaries/ReactiveCocoa.framework',
'external_binaries/Mantle.framework',
],
},
],
}],
],
}], # OS!="mac"
['OS=="win"', {
@ -144,7 +151,7 @@
'destination': '<(PRODUCT_DIR)',
'files': [
'<@(copied_libraries)',
'<(libchromiumcontent_dir)/ffmpegsumo.dll',
'<(libchromiumcontent_dir)/locales',
'<(libchromiumcontent_dir)/libEGL.dll',
'<(libchromiumcontent_dir)/libGLESv2.dll',
'<(libchromiumcontent_dir)/icudtl.dat',
@ -193,7 +200,7 @@
'destination': '<(PRODUCT_DIR)',
'files': [
'<@(copied_libraries)',
'<(libchromiumcontent_dir)/libffmpegsumo.so',
'<(libchromiumcontent_dir)/locales',
'<(libchromiumcontent_dir)/icudtl.dat',
'<(libchromiumcontent_dir)/content_shell.pak',
'<(libchromiumcontent_dir)/natives_blob.bin',
@ -214,7 +221,7 @@
'target_name': '<(project_name)_lib',
'type': 'static_library',
'dependencies': [
'atom_coffee2c',
'atom_js2c',
'vendor/brightray/brightray.gyp:brightray',
'vendor/node/node.gyp:node',
],
@ -226,8 +233,8 @@
# Defined in Chromium but not exposed in its gyp file.
'V8_USE_EXTERNAL_STARTUP_DATA',
'ENABLE_PLUGINS',
# Needed by Node.
'NODE_WANT_INTERNALS=1',
'ENABLE_PEPPER_CDMS',
'USE_PROPRIETARY_CODECS',
],
'sources': [
'<@(lib_sources)',
@ -249,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': [
@ -275,6 +288,7 @@
'-lcomctl32.lib',
'-lcomdlg32.lib',
'-lwininet.lib',
'-lwinmm.lib',
],
},
'dependencies': [
@ -289,12 +303,28 @@
'vendor/breakpad/breakpad.gyp:breakpad_sender',
],
}], # OS=="win"
['OS=="mac"', {
['OS=="mac" and mas_build==0', {
'dependencies': [
'vendor/crashpad/client/client.gyp:crashpad_client',
'vendor/crashpad/handler/handler.gyp:crashpad_handler',
],
}], # OS=="mac"
'link_settings': {
# Do not link with QTKit for mas build.
'libraries': [
'$(SDKROOT)/System/Library/Frameworks/QTKit.framework',
],
},
}], # OS=="mac" and mas_build==0
['OS=="mac" and mas_build==1', {
'defines': [
'MAS_BUILD',
],
'sources!': [
'atom/browser/auto_updater_mac.mm',
'atom/common/crash_reporter/crash_reporter_mac.h',
'atom/common/crash_reporter/crash_reporter_mac.mm',
],
}], # OS=="mac" and mas_build==1
['OS=="linux"', {
'link_settings': {
'ldflags': [
@ -321,11 +351,11 @@
],
}, # target <(product_name)_lib
{
'target_name': 'compile_coffee',
'target_name': 'js2asar',
'type': 'none',
'actions': [
{
'action_name': 'compile_coffee',
'action_name': 'js2asar',
'variables': {
'conditions': [
['OS=="mac"', {
@ -336,41 +366,41 @@
],
},
'inputs': [
'<@(coffee_sources)',
'<@(js_sources)',
],
'outputs': [
'<(resources_path)/atom.asar',
],
'action': [
'python',
'tools/coffee2asar.py',
'tools/js2asar.py',
'<@(_outputs)',
'<@(_inputs)',
],
}
],
}, # target compile_coffee
}, # target js2asar
{
'target_name': 'atom_coffee2c',
'target_name': 'atom_js2c',
'type': 'none',
'actions': [
{
'action_name': 'atom_coffee2c',
'action_name': 'atom_js2c',
'inputs': [
'<@(coffee2c_sources)',
'<@(js2c_sources)',
],
'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/atom_natives.h',
],
'action': [
'python',
'tools/coffee2c.py',
'tools/js2c.py',
'<@(_outputs)',
'<@(_inputs)',
],
}
],
}, # target atom_coffee2c
}, # target atom_js2c
],
'conditions': [
['OS=="mac"', {
@ -397,9 +427,6 @@
'libraries': [
'$(SDKROOT)/System/Library/Frameworks/Carbon.framework',
'$(SDKROOT)/System/Library/Frameworks/QuartzCore.framework',
'external_binaries/Squirrel.framework',
'external_binaries/ReactiveCocoa.framework',
'external_binaries/Mantle.framework',
],
},
'mac_bundle': 1,
@ -441,13 +468,6 @@
'destination': '<(PRODUCT_DIR)/<(product_name) Framework.framework/Versions/A/Libraries',
'files': [
'<@(copied_libraries)',
'<(libchromiumcontent_dir)/ffmpegsumo.so',
],
},
{
'destination': '<(PRODUCT_DIR)/<(product_name) Framework.framework/Versions/A/Resources',
'files': [
'<(PRODUCT_DIR)/crashpad_handler',
],
},
],
@ -462,6 +482,16 @@
'${BUILT_PRODUCTS_DIR}/<(product_name) Framework.framework/Versions/A/<(product_name) Framework',
],
},
{
'postbuild_name': 'Fix path of ffmpeg',
'action': [
'install_name_tool',
'-change',
'@loader_path/libffmpeg.dylib',
'@rpath/libffmpeg.dylib',
'${BUILT_PRODUCTS_DIR}/<(product_name) Framework.framework/Versions/A/<(product_name) Framework',
],
},
{
'postbuild_name': 'Add symlinks for framework subdirectories',
'action': [
@ -470,6 +500,35 @@
'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', {
'link_settings': {
'libraries': [
'external_binaries/Squirrel.framework',
'external_binaries/ReactiveCocoa.framework',
'external_binaries/Mantle.framework',
],
},
'copies': [
{
'destination': '<(PRODUCT_DIR)/<(product_name) Framework.framework/Versions/A/Resources',
'files': [
'<(PRODUCT_DIR)/crashpad_handler',
],
},
],
}],
],
}, # target framework
{
@ -495,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"
],
}

View file

@ -7,14 +7,25 @@
#include <string>
#include <vector>
#include "atom/common/atom_version.h"
#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 {
@ -29,8 +40,8 @@ content::PepperPluginInfo CreatePepperFlashInfo(const base::FilePath& path,
plugin.path = path;
plugin.permissions = ppapi::PERMISSION_ALL_BITS;
std::vector<std::string> flash_version_numbers;
base::SplitString(version, '.', &flash_version_numbers);
std::vector<std::string> 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. :(
@ -45,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,
@ -60,35 +71,59 @@ 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<std::string> 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<std::string>* 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
AtomContentClient::AtomContentClient() {
}
AtomContentClient::~AtomContentClient() {
}
std::string AtomContentClient::GetProduct() const {
return "Chrome/" CHROME_VERSION_STRING;
}
void AtomContentClient::AddAdditionalSchemes(
std::vector<std::string>* standard_schemes,
std::vector<std::string>* savable_schemes) {
auto command_line = base::CommandLine::ForCurrentProcess();
auto custom_schemes = command_line->GetSwitchValueASCII(
switches::kRegisterStandardSchemes);
if (!custom_schemes.empty()) {
std::vector<std::string> schemes;
base::SplitString(custom_schemes, ',', &schemes);
standard_schemes->insert(standard_schemes->end(),
schemes.begin(),
schemes.end());
}
standard_schemes->push_back("chrome-extension");
}
void AtomContentClient::AddPepperPlugins(
void AddPepperFlashFromCommandLine(
std::vector<content::PepperPluginInfo>* plugins) {
auto command_line = base::CommandLine::ForCurrentProcess();
auto flash_path = command_line->GetSwitchValueNative(
@ -103,4 +138,79 @@ void AtomContentClient::AddPepperPlugins(
CreatePepperFlashInfo(base::FilePath(flash_path), flash_version));
}
#if defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS)
void AddWidevineCdmFromCommandLine(
std::vector<content::PepperPluginInfo>* 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() {
}
AtomContentClient::~AtomContentClient() {
}
std::string AtomContentClient::GetProduct() const {
return "Chrome/" CHROME_VERSION_STRING;
}
std::string AtomContentClient::GetUserAgent() const {
return content::BuildUserAgentFromProduct(
"Chrome/" CHROME_VERSION_STRING " "
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<url::SchemeWithType>* standard_schemes,
std::vector<std::string>* savable_schemes) {
std::vector<std::string> 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", url::SCHEME_WITHOUT_PORT});
}
void AtomContentClient::AddPepperPlugins(
std::vector<content::PepperPluginInfo>* plugins) {
AddPepperFlashFromCommandLine(plugins);
#if defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS)
AddWidevineCdmFromCommandLine(plugins);
#endif
}
void AtomContentClient::AddServiceWorkerSchemes(
std::set<std::string>* service_worker_schemes) {
std::vector<std::string> 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

View file

@ -5,6 +5,7 @@
#ifndef ATOM_APP_ATOM_CONTENT_CLIENT_H_
#define ATOM_APP_ATOM_CONTENT_CLIENT_H_
#include <set>
#include <string>
#include <vector>
@ -20,11 +21,15 @@ class AtomContentClient : public brightray::ContentClient {
protected:
// content::ContentClient:
std::string GetProduct() const override;
std::string GetUserAgent() const override;
base::string16 GetLocalizedString(int message_id) const override;
void AddAdditionalSchemes(
std::vector<std::string>* standard_schemes,
std::vector<url::SchemeWithType>* standard_schemes,
std::vector<std::string>* savable_schemes) override;
void AddPepperPlugins(
std::vector<content::PepperPluginInfo>* plugins) override;
void AddServiceWorkerSchemes(
std::set<std::string>* service_worker_schemes) override;
private:
DISALLOW_COPY_AND_ASSIGN(AtomContentClient);

View file

@ -10,6 +10,7 @@
#include "base/at_exit.h"
#include "base/i18n/icu_util.h"
#include "base/mac/bundle_locations.h"
#include "base/mac/scoped_nsautorelease_pool.h"
#include "brightray/common/mac/main_application_bundle.h"
#include "content/public/app/content_main.h"
@ -25,6 +26,7 @@ int AtomMain(int argc, const char* argv[]) {
int AtomInitializeICUandStartNode(int argc, char *argv[]) {
base::AtExitManager atexit_manager;
base::mac::ScopedNSAutoreleasePool pool;
base::mac::SetOverrideFrameworkBundlePath(
brightray::MainApplicationBundlePath()
.Append("Contents")

View file

@ -5,13 +5,8 @@
#include "atom/app/atom_main.h"
#include <stdlib.h>
#include <string.h>
#if defined(OS_WIN)
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include <windows.h>
#include <shellscalingapi.h>
#include <tchar.h>
@ -33,12 +28,30 @@
#include "atom/app/node_main.h"
#include "atom/common/atom_command_line.h"
#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);
@ -76,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<base::Environment> 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
@ -130,15 +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);
}
@ -162,9 +170,9 @@ 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<char**>(argv));
}
@ -179,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<char**>(argv));
}

View file

@ -5,6 +5,7 @@
#include "atom/app/atom_main_delegate.h"
#include <string>
#include <iostream>
#include "atom/app/atom_content_client.h"
#include "atom/browser/atom_browser_client.h"
@ -15,11 +16,22 @@
#include "base/debug/stack_trace.h"
#include "base/environment.h"
#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 {
namespace {
bool IsBrowserProcess(base::CommandLine* cmd) {
std::string process_type = cmd->GetSwitchValueASCII(switches::kProcessType);
return process_type.empty();
}
} // namespace
AtomMainDelegate::AtomMainDelegate() {
}
@ -27,10 +39,16 @@ AtomMainDelegate::~AtomMainDelegate() {
}
bool AtomMainDelegate::BasicStartupComplete(int* exit_code) {
// Disable logging out to debug.log on Windows
auto command_line = base::CommandLine::ForCurrentProcess();
logging::LoggingSettings settings;
#if defined(OS_WIN)
// On Windows the terminal returns immediately, so we add a new line to
// prevent output in the same line as the prompt.
if (IsBrowserProcess(command_line))
std::wcout << std::endl;
#if defined(DEBUG)
// Print logging to debug.log on Windows
settings.logging_dest = logging::LOG_TO_ALL;
settings.log_file = L"debug.log";
settings.lock_log = logging::LOCK_LOG_FILE;
@ -38,16 +56,32 @@ bool AtomMainDelegate::BasicStartupComplete(int* exit_code) {
#else
settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
#endif // defined(DEBUG)
#endif // defined(OS_WIN)
#else // defined(OS_WIN)
settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
#endif // !defined(OS_WIN)
// Only enable logging when --enable-logging is specified.
scoped_ptr<base::Environment> env(base::Environment::Create());
if (!command_line->HasSwitch(switches::kEnableLogging) &&
!env->HasVar("ELECTRON_ENABLE_LOGGING")) {
settings.logging_dest = logging::LOG_NONE;
logging::SetMinLogLevel(logging::LOG_NUM_SEVERITIES);
}
logging::InitLogging(settings);
// Logging with pid and timestamp.
logging::SetLogItems(true, false, true, false);
#if defined(DEBUG) && defined(OS_LINUX)
// Enable convient stack printing.
base::debug::EnableInProcessStackDumping();
bool enable_stack_dumping = env->HasVar("ELECTRON_ENABLE_STACK_DUMPING");
#if defined(DEBUG) && defined(OS_LINUX)
enable_stack_dumping = true;
#endif
if (enable_stack_dumping)
base::debug::EnableInProcessStackDumping();
chrome::RegisterPathProvider();
return brightray::MainDelegate::BasicStartupComplete(exit_code);
}
@ -69,16 +103,9 @@ void AtomMainDelegate::PreSandboxStartup() {
}
// Only append arguments for browser process.
if (!process_type.empty())
if (!IsBrowserProcess(command_line))
return;
#if defined(OS_WIN)
// Disable the LegacyRenderWidgetHostHWND, it made frameless windows unable
// to move and resize. We may consider enabling it again after upgraded to
// Chrome 38, which should have fixed the problem.
command_line->AppendSwitch(switches::kDisableLegacyIntermediateWindow);
#endif
// Disable renderer sandbox for most of node's functions.
command_line->AppendSwitch(switches::kNoSandbox);
@ -111,16 +138,4 @@ scoped_ptr<brightray::ContentClient> AtomMainDelegate::CreateContentClient() {
return scoped_ptr<brightray::ContentClient>(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

View file

@ -25,8 +25,6 @@ class AtomMainDelegate : public brightray::MainDelegate {
// brightray::MainDelegate:
scoped_ptr<brightray::ContentClient> CreateContentClient() override;
void AddDataPackFromPath(
ui::ResourceBundle* bundle, const base::FilePath& pak_dir) override;
#if defined(OS_MACOSX)
void OverrideChildProcessPath() override;
void OverrideFrameworkBundlePath() override;

View file

@ -4,42 +4,48 @@
#include "atom/app/node_main.h"
#include "atom/app/uv_task_runner.h"
#include "atom/browser/javascript_environment.h"
#include "atom/browser/node_debugger.h"
#include "base/command_line.h"
#include "atom/common/node_includes.h"
#include "base/thread_task_runner_handle.h"
#include "gin/array_buffer.h"
#include "gin/public/isolate_holder.h"
#include "gin/v8_initializer.h"
namespace atom {
int NodeMain(int argc, char *argv[]) {
argv = uv_setup_args(argc, argv);
int exec_argc;
const char** exec_argv;
node::Init(&argc, const_cast<const char**>(argv), &exec_argc, &exec_argv);
base::CommandLine::Init(argc, argv);
int exit_code = 1;
{
gin::IsolateHolder::LoadV8Snapshot();
gin::IsolateHolder::Initialize(
gin::IsolateHolder::kNonStrictMode,
gin::ArrayBufferAllocator::SharedInstance());
// Feed gin::PerIsolateData with a task runner.
argv = uv_setup_args(argc, argv);
uv_loop_t* loop = uv_default_loop();
scoped_refptr<UvTaskRunner> uv_task_runner(new UvTaskRunner(loop));
base::ThreadTaskRunnerHandle handle(uv_task_runner);
gin::V8Initializer::LoadV8Snapshot();
gin::V8Initializer::LoadV8Natives();
JavascriptEnvironment gin_env;
int exec_argc;
const char** exec_argv;
node::Init(&argc, const_cast<const char**>(argv), &exec_argc, &exec_argv);
node::Environment* env = node::CreateEnvironment(
gin_env.isolate(), uv_default_loop(), gin_env.context(), argc, argv,
gin_env.isolate(), loop, gin_env.context(), argc, argv,
exec_argc, exec_argv);
// Start debugger.
node::node_isolate = gin_env.isolate();
if (node::use_debug_agent)
node::StartDebug(env, node::debug_wait_connect);
// Start our custom debugger implementation.
NodeDebugger node_debugger(gin_env.isolate());
if (node_debugger.IsRunning())
env->AssignToContext(v8::Debug::GetDebugContext());
node::LoadEnvironment(env);
// Enable debugger.
if (node::use_debug_agent)
node::EnableDebug(env);
bool more;
do {
more = uv_run(env->event_loop(), UV_RUN_ONCE);

View file

@ -0,0 +1,60 @@
// 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/app/uv_task_runner.h"
#include "base/stl_util.h"
namespace atom {
UvTaskRunner::UvTaskRunner(uv_loop_t* loop) : loop_(loop) {
}
UvTaskRunner::~UvTaskRunner() {
for (auto& iter : tasks_) {
uv_unref(reinterpret_cast<uv_handle_t*>(iter.first));
delete iter.first;
}
}
bool UvTaskRunner::PostDelayedTask(const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay) {
uv_timer_t* timer = new uv_timer_t;
timer->data = this;
uv_timer_init(loop_, timer);
uv_timer_start(timer, UvTaskRunner::OnTimeout, delay.InMilliseconds(), 0);
tasks_[timer] = task;
return true;
}
bool UvTaskRunner::RunsTasksOnCurrentThread() const {
return true;
}
bool UvTaskRunner::PostNonNestableDelayedTask(
const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay) {
return PostDelayedTask(from_here, task, delay);
}
// static
void UvTaskRunner::OnTimeout(uv_timer_t* timer) {
UvTaskRunner* self = static_cast<UvTaskRunner*>(timer->data);
if (!ContainsKey(self->tasks_, timer))
return;
self->tasks_[timer].Run();
self->tasks_.erase(timer);
uv_timer_stop(timer);
uv_close(reinterpret_cast<uv_handle_t*>(timer), UvTaskRunner::OnClose);
}
// static
void UvTaskRunner::OnClose(uv_handle_t* handle) {
delete reinterpret_cast<uv_timer_t*>(handle);
}
} // namespace atom

45
atom/app/uv_task_runner.h Normal file
View file

@ -0,0 +1,45 @@
// 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_APP_UV_TASK_RUNNER_H_
#define ATOM_APP_UV_TASK_RUNNER_H_
#include <map>
#include "base/callback.h"
#include "base/single_thread_task_runner.h"
#include "vendor/node/deps/uv/include/uv.h"
namespace atom {
// TaskRunner implementation that posts tasks into libuv's default loop.
class UvTaskRunner : public base::SingleThreadTaskRunner {
public:
explicit UvTaskRunner(uv_loop_t* loop);
~UvTaskRunner() override;
// base::SingleThreadTaskRunner:
bool PostDelayedTask(const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay) override;
bool RunsTasksOnCurrentThread() const override;
bool PostNonNestableDelayedTask(
const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay) override;
private:
static void OnTimeout(uv_timer_t* timer);
static void OnClose(uv_handle_t* handle);
uv_loop_t* loop_;
std::map<uv_timer_t*, base::Closure> tasks_;
DISALLOW_COPY_AND_ASSIGN(UvTaskRunner);
};
} // namespace atom
#endif // ATOM_APP_UV_TASK_RUNNER_H_

View file

@ -7,35 +7,41 @@
#include <string>
#include <vector>
#if defined(OS_WIN)
#include <shlobj.h>
#endif
#include "atom/browser/api/atom_api_menu.h"
#include "atom/browser/api/atom_api_session.h"
#include "atom/browser/api/atom_api_web_contents.h"
#include "atom/browser/atom_browser_context.h"
#include "atom/browser/atom_browser_main_parts.h"
#include "atom/browser/browser.h"
#include "atom/browser/api/atom_api_web_contents.h"
#include "atom/browser/login_handler.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/net_converter.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/native_mate_converters/gurl_converter.h"
#include "atom/common/native_mate_converters/image_converter.h"
#include "atom/common/node_includes.h"
#include "atom/common/options_switches.h"
#include "base/command_line.h"
#include "base/environment.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "brightray/browser/brightray_paths.h"
#include "chrome/common/chrome_paths.h"
#include "content/public/browser/client_certificate_delegate.h"
#include "content/public/browser/gpu_data_manager.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/common/content_switches.h"
#include "native_mate/dictionary.h"
#include "native_mate/object_template_builder.h"
#include "net/ssl/ssl_cert_request_info.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/image/image.h"
#if defined(OS_WIN)
#include "base/strings/utf_string_conversions.h"
#include "ui/base/win/shell.h"
#endif
#include "atom/common/node_includes.h"
using atom::Browser;
namespace mate {
@ -61,21 +67,6 @@ struct Converter<Browser::UserTask> {
};
#endif
template<>
struct Converter<scoped_refptr<net::X509Certificate>> {
static v8::Local<v8::Value> ToV8(
v8::Isolate* isolate,
const scoped_refptr<net::X509Certificate>& val) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
std::string encoded_data;
net::X509Certificate::GetPEMEncoded(
val->os_cert_handle(), &encoded_data);
dict.Set("data", encoded_data);
dict.Set("issuerName", val->issuer().GetDisplayName());
return dict.GetHandle();
}
};
} // namespace mate
@ -99,22 +90,47 @@ int GetPathConstant(const std::string& name) {
return base::DIR_HOME;
else if (name == "temp")
return base::DIR_TEMP;
else if (name == "userDesktop")
else if (name == "userDesktop" || name == "desktop")
return base::DIR_USER_DESKTOP;
else if (name == "exe")
return base::FILE_EXE;
else if (name == "module")
return base::FILE_MODULE;
else if (name == "documents")
return chrome::DIR_USER_DOCUMENTS;
else if (name == "downloads")
return chrome::DIR_DEFAULT_DOWNLOADS;
else if (name == "music")
return chrome::DIR_USER_MUSIC;
else if (name == "pictures")
return chrome::DIR_USER_PICTURES;
else if (name == "videos")
return chrome::DIR_USER_VIDEOS;
else
return -1;
}
bool NotificationCallbackWrapper(
const ProcessSingleton::NotificationCallback& callback,
const base::CommandLine::StringVector& cmd,
const base::FilePath& cwd) {
// Make sure the callback is called after app gets ready.
if (Browser::Get()->is_ready()) {
callback.Run(cmd, cwd);
} else {
scoped_refptr<base::SingleThreadTaskRunner> task_runner(
base::ThreadTaskRunnerHandle::Get());
task_runner->PostTask(
FROM_HERE, base::Bind(base::IgnoreResult(callback), cmd, cwd));
}
// ProcessSingleton needs to know whether current process is quiting.
return !Browser::Get()->is_shutting_down();
}
void OnClientCertificateSelected(
v8::Isolate* isolate,
std::shared_ptr<content::ClientCertificateDelegate> delegate,
mate::Arguments* args) {
v8::Locker locker(isolate);
v8::HandleScope handle_scope(isolate);
mate::Dictionary cert_data;
if (!(args->Length() == 1 && args->GetNext(&cert_data))) {
args->ThrowError();
@ -128,18 +144,29 @@ void OnClientCertificateSelected(
net::X509Certificate::CreateCertificateListFromBytes(
encoded_data.data(), encoded_data.size(),
net::X509Certificate::FORMAT_AUTO);
delegate->ContinueWithCertificate(certs[0].get());
}
void PassLoginInformation(scoped_refptr<LoginHandler> login_handler,
mate::Arguments* args) {
base::string16 username, password;
if (args->GetNext(&username) && args->GetNext(&password))
login_handler->Login(username, password);
else
login_handler->CancelAuth();
}
} // namespace
App::App() {
static_cast<AtomBrowserClient*>(AtomBrowserClient::Get())->set_delegate(this);
Browser::Get()->AddObserver(this);
content::GpuDataManager::GetInstance()->AddObserver(this);
}
App::~App() {
static_cast<AtomBrowserClient*>(AtomBrowserClient::Get())->set_delegate(
nullptr);
Browser::Get()->RemoveObserver(this);
content::GpuDataManager::GetInstance()->RemoveObserver(this);
}
@ -157,7 +184,13 @@ void App::OnWindowAllClosed() {
}
void App::OnQuit() {
Emit("quit");
int exitCode = AtomBrowserMainParts::Get()->GetExitCode();
Emit("quit", exitCode);
if (process_singleton_.get()) {
process_singleton_->Cleanup();
process_singleton_.reset();
}
}
void App::OnOpenFile(bool* prevent_default, const std::string& file_path) {
@ -168,8 +201,8 @@ void App::OnOpenURL(const std::string& url) {
Emit("open-url", url);
}
void App::OnActivateWithNoOpenWindows() {
Emit("activate-with-no-open-windows");
void App::OnActivate(bool has_visible_windows) {
Emit("activate", has_visible_windows);
}
void App::OnWillFinishLaunching() {
@ -177,26 +210,62 @@ void App::OnWillFinishLaunching() {
}
void App::OnFinishLaunching() {
// Create the defaultSession.
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
auto browser_context = static_cast<AtomBrowserContext*>(
AtomBrowserMainParts::Get()->browser_context());
auto handle = Session::CreateFrom(isolate(), browser_context);
default_session_.Reset(isolate(), handle.ToV8());
Emit("ready");
}
void App::OnSelectCertificate(
void App::OnLogin(LoginHandler* login_handler) {
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
bool prevent_default = Emit(
"login",
WebContents::CreateFrom(isolate(), login_handler->GetWebContents()),
login_handler->request(),
login_handler->auth_info(),
base::Bind(&PassLoginInformation, make_scoped_refptr(login_handler)));
// Default behavior is to always cancel the auth.
if (!prevent_default)
login_handler->CancelAuth();
}
void App::AllowCertificateError(
int pid,
int fid,
int cert_error,
const net::SSLInfo& ssl_info,
const GURL& request_url,
content::ResourceType resource_type,
bool overridable,
bool strict_enforcement,
bool expired_previous_decision,
const base::Callback<void(bool)>& callback,
content::CertificateRequestResultType* request) {
auto rfh = content::RenderFrameHost::FromID(pid, fid);
auto web_contents = content::WebContents::FromRenderFrameHost(rfh);
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
bool prevent_default = Emit("certificate-error",
WebContents::CreateFrom(isolate(), web_contents),
request_url,
net::ErrorToString(cert_error),
ssl_info.cert,
callback);
// Deny the certificate by default.
if (!prevent_default)
*request = content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY;
}
void App::SelectClientCertificate(
content::WebContents* web_contents,
net::SSLCertRequestInfo* cert_request_info,
scoped_ptr<content::ClientCertificateDelegate> delegate) {
std::shared_ptr<content::ClientCertificateDelegate>
shared_delegate(delegate.release());
bool prevent_default =
Emit("select-certificate",
api::WebContents::CreateFrom(isolate(), web_contents),
Emit("select-client-certificate",
WebContents::CreateFrom(isolate(), web_contents),
cert_request_info->host_and_port.ToString(),
cert_request_info->client_certs,
base::Bind(&OnClientCertificateSelected,
@ -242,18 +311,42 @@ void App::SetDesktopName(const std::string& desktop_name) {
#endif
}
void App::SetAppUserModelId(const std::string& app_id) {
#if defined(OS_WIN)
base::string16 app_id_utf16 = base::UTF8ToUTF16(app_id);
SetCurrentProcessExplicitAppUserModelID(app_id_utf16.c_str());
#endif
void App::AllowNTLMCredentialsForAllDomains(bool should_allow) {
auto browser_context = static_cast<AtomBrowserContext*>(
AtomBrowserMainParts::Get()->browser_context());
browser_context->AllowNTLMCredentialsForAllDomains(should_allow);
}
v8::Local<v8::Value> App::DefaultSession(v8::Isolate* isolate) {
if (default_session_.IsEmpty())
return v8::Null(isolate);
else
return v8::Local<v8::Value>::New(isolate, default_session_);
std::string App::GetLocale() {
return l10n_util::GetApplicationLocale("");
}
#if defined(OS_WIN)
bool App::IsAeroGlassEnabled() {
return ui::win::IsAeroGlassEnabled();
}
#endif
bool App::MakeSingleInstance(
const ProcessSingleton::NotificationCallback& callback) {
if (process_singleton_.get())
return false;
base::FilePath user_dir;
PathService::Get(brightray::DIR_USER_DATA, &user_dir);
process_singleton_.reset(new ProcessSingleton(
user_dir, base::Bind(NotificationCallbackWrapper, callback)));
switch (process_singleton_->NotifyOtherProcessOrCreate()) {
case ProcessSingleton::NotifyResult::LOCK_ERROR:
case ProcessSingleton::NotifyResult::PROFILE_IN_USE:
case ProcessSingleton::NotifyResult::PROCESS_NOTIFIED:
process_singleton_.reset();
return true;
case ProcessSingleton::NotifyResult::PROCESS_NONE:
default: // Shouldn't be needed, but VS warns if it is not there.
return false;
}
}
mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder(
@ -261,6 +354,7 @@ mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder(
auto browser = base::Unretained(Browser::Get());
return mate::ObjectTemplateBuilder(isolate)
.SetMethod("quit", base::Bind(&Browser::Quit, browser))
.SetMethod("exit", base::Bind(&Browser::Exit, browser))
.SetMethod("focus", base::Bind(&Browser::Focus, browser))
.SetMethod("getVersion", base::Bind(&Browser::GetVersion, browser))
.SetMethod("setVersion", base::Bind(&Browser::SetVersion, browser))
@ -271,15 +365,20 @@ mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder(
base::Bind(&Browser::AddRecentDocument, browser))
.SetMethod("clearRecentDocuments",
base::Bind(&Browser::ClearRecentDocuments, browser))
.SetMethod("setAppUserModelId",
base::Bind(&Browser::SetAppUserModelID, browser))
#if defined(OS_WIN)
.SetMethod("setUserTasks",
base::Bind(&Browser::SetUserTasks, browser))
.SetMethod("isAeroGlassEnabled", &App::IsAeroGlassEnabled)
#endif
.SetMethod("setPath", &App::SetPath)
.SetMethod("getPath", &App::GetPath)
.SetMethod("setDesktopName", &App::SetDesktopName)
.SetMethod("setAppUserModelId", &App::SetAppUserModelId)
.SetProperty("defaultSession", &App::DefaultSession);
.SetMethod("allowNTLMCredentialsForAllDomains",
&App::AllowNTLMCredentialsForAllDomains)
.SetMethod("getLocale", &App::GetLocale)
.SetMethod("makeSingleInstance", &App::MakeSingleInstance);
}
// static
@ -296,6 +395,16 @@ namespace {
void AppendSwitch(const std::string& switch_string, mate::Arguments* args) {
auto command_line = base::CommandLine::ForCurrentProcess();
if (switch_string == atom::switches::kPpapiFlashPath ||
switch_string == atom::switches::kClientCertificate ||
switch_string == switches::kLogNetLog) {
base::FilePath path;
args->GetNext(&path);
command_line->AppendSwitchPath(switch_string, path);
return;
}
std::string value;
if (args->GetNext(&value))
command_line->AppendSwitchASCII(switch_string, value);
@ -341,6 +450,7 @@ void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
dict.SetMethod("dockHide", base::Bind(&Browser::DockHide, browser));
dict.SetMethod("dockShow", base::Bind(&Browser::DockShow, browser));
dict.SetMethod("dockSetMenu", &DockSetMenu);
dict.SetMethod("dockSetIcon", base::Bind(&Browser::DockSetIcon, browser));
#endif
}

View file

@ -8,7 +8,10 @@
#include <string>
#include "atom/browser/api/event_emitter.h"
#include "atom/browser/atom_browser_client.h"
#include "atom/browser/browser_observer.h"
#include "atom/common/native_mate_converters/callback.h"
#include "chrome/browser/process_singleton.h"
#include "content/public/browser/gpu_data_manager_observer.h"
#include "native_mate/handle.h"
@ -24,7 +27,8 @@ namespace atom {
namespace api {
class App : public mate::EventEmitter,
class App : public AtomBrowserClient::Delegate,
public mate::EventEmitter,
public BrowserObserver,
public content::GpuDataManagerObserver {
public:
@ -41,10 +45,25 @@ class App : public mate::EventEmitter,
void OnQuit() override;
void OnOpenFile(bool* prevent_default, const std::string& file_path) override;
void OnOpenURL(const std::string& url) override;
void OnActivateWithNoOpenWindows() override;
void OnActivate(bool has_visible_windows) override;
void OnWillFinishLaunching() override;
void OnFinishLaunching() override;
void OnSelectCertificate(
void OnLogin(LoginHandler* login_handler) override;
// content::ContentBrowserClient:
void AllowCertificateError(
int render_process_id,
int render_frame_id,
int cert_error,
const net::SSLInfo& ssl_info,
const GURL& request_url,
content::ResourceType resource_type,
bool overridable,
bool strict_enforcement,
bool expired_previous_decision,
const base::Callback<void(bool)>& callback,
content::CertificateRequestResultType* request) override;
void SelectClientCertificate(
content::WebContents* web_contents,
net::SSLCertRequestInfo* cert_request_info,
scoped_ptr<content::ClientCertificateDelegate> delegate) override;
@ -64,10 +83,16 @@ class App : public mate::EventEmitter,
const base::FilePath& path);
void SetDesktopName(const std::string& desktop_name);
void SetAppUserModelId(const std::string& app_id);
v8::Local<v8::Value> DefaultSession(v8::Isolate* isolate);
void AllowNTLMCredentialsForAllDomains(bool should_allow);
bool MakeSingleInstance(
const ProcessSingleton::NotificationCallback& callback);
std::string GetLocale();
v8::Global<v8::Value> default_session_;
#if defined(OS_WIN)
bool IsAeroGlassEnabled();
#endif
scoped_ptr<ProcessSingleton> process_singleton_;
DISALLOW_COPY_AND_ASSIGN(App);
};

View file

@ -5,12 +5,30 @@
#include "atom/browser/api/atom_api_auto_updater.h"
#include "base/time/time.h"
#include "atom/browser/auto_updater.h"
#include "atom/browser/browser.h"
#include "atom/browser/native_window.h"
#include "atom/browser/window_list.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/node_includes.h"
#include "native_mate/dictionary.h"
#include "native_mate/object_template_builder.h"
#include "atom/common/node_includes.h"
namespace mate {
template<>
struct Converter<base::Time> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const base::Time& val) {
v8::MaybeLocal<v8::Value> date = v8::Date::New(
isolate->GetCurrentContext(), val.ToJsTime());
if (date.IsEmpty())
return v8::Null(isolate);
else
return date.ToLocalChecked();
}
};
} // namespace mate
namespace atom {
@ -21,11 +39,18 @@ AutoUpdater::AutoUpdater() {
}
AutoUpdater::~AutoUpdater() {
auto_updater::AutoUpdater::SetDelegate(NULL);
auto_updater::AutoUpdater::SetDelegate(nullptr);
}
void AutoUpdater::OnError(const std::string& error) {
Emit("error", error);
void AutoUpdater::OnError(const std::string& message) {
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
auto error = v8::Exception::Error(mate::StringToV8(isolate(), message));
EmitCustomEvent(
"error",
error->ToObject(isolate()->GetCurrentContext()).ToLocalChecked(),
// Message is also emitted to keep compatibility with old code.
message);
}
void AutoUpdater::OnCheckingForUpdate() {
@ -43,26 +68,36 @@ void AutoUpdater::OnUpdateNotAvailable() {
void AutoUpdater::OnUpdateDownloaded(const std::string& release_notes,
const std::string& release_name,
const base::Time& release_date,
const std::string& update_url,
const base::Closure& quit_and_install) {
quit_and_install_ = quit_and_install;
Emit("update-downloaded-raw", release_notes, release_name,
release_date.ToJsTime(), update_url);
const std::string& url) {
Emit("update-downloaded", release_notes, release_name, release_date, url,
// Keep compatibility with old APIs.
base::Bind(&AutoUpdater::QuitAndInstall, base::Unretained(this)));
}
void AutoUpdater::OnWindowAllClosed() {
QuitAndInstall();
}
mate::ObjectTemplateBuilder AutoUpdater::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
return mate::ObjectTemplateBuilder(isolate)
.SetMethod("setFeedUrl", &auto_updater::AutoUpdater::SetFeedURL)
.SetMethod("setFeedURL", &auto_updater::AutoUpdater::SetFeedURL)
.SetMethod("checkForUpdates", &auto_updater::AutoUpdater::CheckForUpdates)
.SetMethod("_quitAndInstall", &AutoUpdater::QuitAndInstall);
.SetMethod("quitAndInstall", &AutoUpdater::QuitAndInstall);
}
void AutoUpdater::QuitAndInstall() {
if (quit_and_install_.is_null())
Browser::Get()->Shutdown();
else
quit_and_install_.Run();
// If we don't have any window then quitAndInstall immediately.
WindowList* window_list = WindowList::GetInstance();
if (window_list->size() == 0) {
auto_updater::AutoUpdater::QuitAndInstall();
return;
}
// Otherwise do the restart after all windows have been closed.
window_list->AddObserver(this);
for (NativeWindow* window : *window_list)
window->Close();
}
// static

View file

@ -7,9 +7,9 @@
#include <string>
#include "base/callback.h"
#include "atom/browser/api/event_emitter.h"
#include "atom/browser/auto_updater_delegate.h"
#include "atom/browser/auto_updater.h"
#include "atom/browser/window_list_observer.h"
#include "native_mate/handle.h"
namespace atom {
@ -17,7 +17,8 @@ namespace atom {
namespace api {
class AutoUpdater : public mate::EventEmitter,
public auto_updater::AutoUpdaterDelegate {
public auto_updater::Delegate,
public WindowListObserver {
public:
static mate::Handle<AutoUpdater> Create(v8::Isolate* isolate);
@ -25,17 +26,18 @@ class AutoUpdater : public mate::EventEmitter,
AutoUpdater();
virtual ~AutoUpdater();
// AutoUpdaterDelegate implementations.
// Delegate implementations.
void OnError(const std::string& error) override;
void OnCheckingForUpdate() override;
void OnUpdateAvailable() override;
void OnUpdateNotAvailable() override;
void OnUpdateDownloaded(
const std::string& release_notes,
const std::string& release_name,
const base::Time& release_date,
const std::string& update_url,
const base::Closure& quit_and_install) override;
void OnUpdateDownloaded(const std::string& release_notes,
const std::string& release_name,
const base::Time& release_date,
const std::string& update_url) override;
// WindowListObserver:
void OnWindowAllClosed() override;
// mate::Wrappable implementations:
mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
@ -44,8 +46,6 @@ class AutoUpdater : public mate::EventEmitter,
private:
void QuitAndInstall();
base::Closure quit_and_install_;
DISALLOW_COPY_AND_ASSIGN(AutoUpdater);
};

View file

@ -7,39 +7,30 @@
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/node_includes.h"
#include "base/bind.h"
#include "base/files/file_util.h"
#include "content/public/browser/tracing_controller.h"
#include "native_mate/dictionary.h"
#include "atom/common/node_includes.h"
using content::TracingController;
namespace mate {
template<>
struct Converter<base::trace_event::CategoryFilter> {
struct Converter<base::trace_event::TraceConfig> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
base::trace_event::CategoryFilter* out) {
std::string filter;
if (!ConvertFromV8(isolate, val, &filter))
return false;
*out = base::trace_event::CategoryFilter(filter);
return true;
}
};
template<>
struct Converter<base::trace_event::TraceOptions> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
base::trace_event::TraceOptions* out) {
std::string options;
base::trace_event::TraceConfig* out) {
Dictionary options;
if (!ConvertFromV8(isolate, val, &options))
return false;
return out->SetFromString(options);
std::string category_filter, trace_options;
if (!options.Get("categoryFilter", &category_filter) ||
!options.Get("traceOptions", &trace_options))
return false;
*out = base::trace_event::TraceConfig(category_filter, trace_options);
return true;
}
};

View file

@ -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,141 +19,21 @@
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "atom/common/node_includes.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<v8::Value> 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<v8::Value> 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<v8::Value> error = mate::ConvertToV8(isolate, error_message);
callback.Run(error, v8::Null(isolate));
return;
}
if (!set_success) {
v8::Local<v8::Value> 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<atom::api::Cookies::Error> {
static v8::Local<v8::Value> 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<net::CanonicalCookie> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
@ -163,11 +42,11 @@ struct Converter<net::CanonicalCookie> {
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();
@ -180,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<base::DictionaryValue> 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<base::DictionaryValue> 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<base::DictionaryValue> 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<net::URLRequestContextGetter> 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<base::DictionaryValue> 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<net::URLRequestContextGetter> getter,
scoped_ptr<base::DictionaryValue> 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<base::DictionaryValue> 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<base::DictionaryValue> 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<net::URLRequestContextGetter> 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<net::URLRequestContextGetter> getter,
scoped_ptr<base::DictionaryValue> 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)) {
@ -303,37 +178,44 @@ void Cookies::SetCookiesOnIOThread(scoped_ptr<base::DictionaryValue> 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<base::DictionaryValue> 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<base::DictionaryValue> 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
@ -343,6 +225,15 @@ mate::Handle<Cookies> Cookies::Create(
return mate::CreateHandle(isolate, new Cookies(browser_context));
}
// static
void Cookies::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> prototype) {
mate::ObjectTemplateBuilder(isolate, prototype)
.SetMethod("get", &Cookies::Get)
.SetMethod("remove", &Cookies::Remove)
.SetMethod("set", &Cookies::Set);
}
} // namespace api
} // namespace atom

View file

@ -7,8 +7,8 @@
#include <string>
#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,52 +28,34 @@ namespace atom {
namespace api {
class Cookies : public mate::Wrappable {
class Cookies : public mate::TrackableObject<Cookies> {
public:
// node.js style callback function(error, result)
typedef base::Callback<void(v8::Local<v8::Value>, v8::Local<v8::Value>)>
CookiesCallback;
enum Error {
SUCCESS,
FAILED,
};
using GetCallback = base::Callback<void(Error, const net::CookieList&)>;
using SetCallback = base::Callback<void(Error)>;
static mate::Handle<Cookies> Create(v8::Isolate* isolate,
content::BrowserContext* browser_context);
// mate::TrackableObject:
static void BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> 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<base::DictionaryValue> filter,
const CookiesCallback& callback);
void OnGetCookies(scoped_ptr<base::DictionaryValue> 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<base::DictionaryValue> 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();
scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
net::URLRequestContextGetter* request_context_getter_;
DISALLOW_COPY_AND_ASSIGN(Cookies);
};

View file

@ -0,0 +1,195 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/api/atom_api_debugger.h"
#include <string>
#include "atom/browser/atom_browser_main_parts.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/value_converter.h"
#include "atom/common/node_includes.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/web_contents.h"
#include "native_mate/dictionary.h"
#include "native_mate/object_template_builder.h"
using content::DevToolsAgentHost;
namespace atom {
namespace api {
namespace {
// The wrapDebugger funtion which is implemented in JavaScript.
using WrapDebuggerCallback = base::Callback<void(v8::Local<v8::Value>)>;
WrapDebuggerCallback g_wrap_debugger;
} // namespace
Debugger::Debugger(content::WebContents* web_contents)
: web_contents_(web_contents),
previous_request_id_(0) {
}
Debugger::~Debugger() {
}
void Debugger::AgentHostClosed(DevToolsAgentHost* agent_host,
bool replaced_with_another_client) {
std::string detach_reason = "target closed";
if (replaced_with_another_client)
detach_reason = "replaced with devtools";
Emit("detach", detach_reason);
}
void Debugger::DispatchProtocolMessage(DevToolsAgentHost* agent_host,
const std::string& message) {
DCHECK(agent_host == agent_host_.get());
scoped_ptr<base::Value> parsed_message(base::JSONReader::Read(message));
if (!parsed_message->IsType(base::Value::TYPE_DICTIONARY))
return;
base::DictionaryValue* dict =
static_cast<base::DictionaryValue*>(parsed_message.get());
int id;
if (!dict->GetInteger("id", &id)) {
std::string method;
if (!dict->GetString("method", &method))
return;
base::DictionaryValue* params_value = nullptr;
base::DictionaryValue params;
if (dict->GetDictionary("params", &params_value))
params.Swap(params_value);
Emit("message", method, params);
} else {
auto send_command_callback = pending_requests_[id];
pending_requests_.erase(id);
if (send_command_callback.is_null())
return;
base::DictionaryValue* error_body = nullptr;
base::DictionaryValue error;
if (dict->GetDictionary("error", &error_body))
error.Swap(error_body);
base::DictionaryValue* result_body = nullptr;
base::DictionaryValue result;
if (dict->GetDictionary("result", &result_body))
result.Swap(result_body);
send_command_callback.Run(error, result);
}
}
void Debugger::Attach(mate::Arguments* args) {
std::string protocol_version;
args->GetNext(&protocol_version);
if (!protocol_version.empty() &&
!DevToolsAgentHost::IsSupportedProtocolVersion(protocol_version)) {
args->ThrowError("Requested protocol version is not supported");
return;
}
agent_host_ = DevToolsAgentHost::GetOrCreateFor(web_contents_);
if (!agent_host_.get()) {
args->ThrowError("No target available");
return;
}
if (agent_host_->IsAttached()) {
args->ThrowError("Another debugger is already attached to this target");
return;
}
agent_host_->AttachClient(this);
}
bool Debugger::IsAttached() {
return agent_host_.get() ? agent_host_->IsAttached() : false;
}
void Debugger::Detach() {
if (!agent_host_.get())
return;
agent_host_->DetachClient();
AgentHostClosed(agent_host_.get(), false);
agent_host_ = nullptr;
}
void Debugger::SendCommand(mate::Arguments* args) {
if (!agent_host_.get())
return;
std::string method;
if (!args->GetNext(&method)) {
args->ThrowError();
return;
}
base::DictionaryValue command_params;
args->GetNext(&command_params);
SendCommandCallback callback;
args->GetNext(&callback);
base::DictionaryValue request;
int request_id = ++previous_request_id_;
pending_requests_[request_id] = callback;
request.SetInteger("id", request_id);
request.SetString("method", method);
if (!command_params.empty())
request.Set("params", command_params.DeepCopy());
std::string json_args;
base::JSONWriter::Write(request, &json_args);
agent_host_->DispatchProtocolMessage(json_args);
}
// static
mate::Handle<Debugger> Debugger::Create(
v8::Isolate* isolate,
content::WebContents* web_contents) {
auto handle = mate::CreateHandle(isolate, new Debugger(web_contents));
g_wrap_debugger.Run(handle.ToV8());
return handle;
}
// static
void Debugger::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> prototype) {
mate::ObjectTemplateBuilder(isolate, prototype)
.SetMethod("attach", &Debugger::Attach)
.SetMethod("isAttached", &Debugger::IsAttached)
.SetMethod("detach", &Debugger::Detach)
.SetMethod("sendCommand", &Debugger::SendCommand);
}
void ClearWrapDebugger() {
g_wrap_debugger.Reset();
}
void SetWrapDebugger(const WrapDebuggerCallback& callback) {
g_wrap_debugger = callback;
// Cleanup the wrapper on exit.
atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback(
base::Bind(ClearWrapDebugger));
}
} // namespace api
} // namespace atom
namespace {
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
v8::Local<v8::Context> context, void* priv) {
v8::Isolate* isolate = context->GetIsolate();
mate::Dictionary dict(isolate, exports);
dict.SetMethod("_setWrapDebugger", &atom::api::SetWrapDebugger);
}
} // namespace
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_debugger, Initialize);

View file

@ -0,0 +1,75 @@
// Copyright (c) 2016 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_API_ATOM_API_DEBUGGER_H_
#define ATOM_BROWSER_API_ATOM_API_DEBUGGER_H_
#include <map>
#include <string>
#include "atom/browser/api/trackable_object.h"
#include "base/callback.h"
#include "base/values.h"
#include "content/public/browser/devtools_agent_host_client.h"
#include "native_mate/handle.h"
namespace content {
class DevToolsAgentHost;
class WebContents;
}
namespace mate {
class Arguments;
}
namespace atom {
namespace api {
class Debugger: public mate::TrackableObject<Debugger>,
public content::DevToolsAgentHostClient {
public:
using SendCommandCallback =
base::Callback<void(const base::DictionaryValue&,
const base::DictionaryValue&)>;
static mate::Handle<Debugger> Create(
v8::Isolate* isolate, content::WebContents* web_contents);
// mate::TrackableObject:
static void BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> prototype);
protected:
explicit Debugger(content::WebContents* web_contents);
~Debugger();
// content::DevToolsAgentHostClient:
void AgentHostClosed(content::DevToolsAgentHost* agent_host,
bool replaced_with_another_client) override;
void DispatchProtocolMessage(content::DevToolsAgentHost* agent_host,
const std::string& message) override;
private:
using PendingRequestMap = std::map<int, SendCommandCallback>;
void Attach(mate::Arguments* args);
bool IsAttached();
void Detach();
void SendCommand(mate::Arguments* args);
content::WebContents* web_contents_; // Weak Reference.
scoped_refptr<content::DevToolsAgentHost> agent_host_;
PendingRequestMap pending_requests_;
int previous_request_id_;
DISALLOW_COPY_AND_ASSIGN(Debugger);
};
} // namespace api
} // namespace atom
#endif // ATOM_BROWSER_API_ATOM_API_DEBUGGER_H_

View file

@ -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<DesktopMediaList::Source> {
static v8::Local<v8::Value> 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<webrtc::ScreenCapturer> screen_capturer(
capture_screen ? webrtc::ScreenCapturer::Create(options) : nullptr);
scoped_ptr<webrtc::WindowCapturer> 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> DesktopCapturer::Create(v8::Isolate* isolate) {
return mate::CreateHandle(isolate, new DesktopCapturer);
}
} // namespace api
} // namespace atom
namespace {
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
v8::Local<v8::Context> 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);

View file

@ -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<DesktopCapturer> 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<DesktopMediaList> media_list_;
DISALLOW_COPY_AND_ASSIGN(DesktopCapturer);
};
} // namespace api
} // namespace atom
#endif // ATOM_BROWSER_API_ATOM_API_DESKTOP_CAPTURER_H_

View file

@ -41,6 +41,7 @@ namespace {
void ShowMessageBox(int type,
const std::vector<std::string>& buttons,
int default_id,
int cancel_id,
int options,
const std::string& title,
@ -54,12 +55,13 @@ void ShowMessageBox(int type,
if (mate::Converter<atom::MessageBoxCallback>::FromV8(args->isolate(),
peek,
&callback)) {
atom::ShowMessageBox(window, (atom::MessageBoxType)type, buttons, cancel_id,
options, title, message, detail, icon, callback);
atom::ShowMessageBox(window, (atom::MessageBoxType)type, buttons,
default_id, cancel_id, options, title,
message, detail, icon, callback);
} else {
int chosen = atom::ShowMessageBox(window, (atom::MessageBoxType)type,
buttons, cancel_id, options, title,
message, detail, icon);
buttons, default_id, cancel_id,
options, title, message, detail, icon);
args->Return(chosen);
}
}

View file

@ -0,0 +1,198 @@
// 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_download_item.h"
#include <map>
#include "atom/browser/atom_browser_main_parts.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/native_mate_converters/gurl_converter.h"
#include "atom/common/node_includes.h"
#include "base/memory/linked_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "native_mate/dictionary.h"
#include "net/base/filename_util.h"
namespace mate {
template<>
struct Converter<content::DownloadItem::DownloadState> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
content::DownloadItem::DownloadState state) {
std::string download_state;
switch (state) {
case content::DownloadItem::COMPLETE:
download_state = "completed";
break;
case content::DownloadItem::CANCELLED:
download_state = "cancelled";
break;
case content::DownloadItem::INTERRUPTED:
download_state = "interrupted";
break;
default:
break;
}
return ConvertToV8(isolate, download_state);
}
};
} // namespace mate
namespace atom {
namespace api {
namespace {
// The wrapDownloadItem funtion which is implemented in JavaScript
using WrapDownloadItemCallback = base::Callback<void(v8::Local<v8::Value>)>;
WrapDownloadItemCallback g_wrap_download_item;
char kDownloadItemSavePathKey[] = "DownloadItemSavePathKey";
std::map<uint32, linked_ptr<v8::Global<v8::Value>>> g_download_item_objects;
} // namespace
DownloadItem::SavePathData::SavePathData(const base::FilePath& path) :
path_(path) {
}
const base::FilePath& DownloadItem::SavePathData::path() {
return path_;
}
DownloadItem::DownloadItem(content::DownloadItem* download_item) :
download_item_(download_item) {
download_item_->AddObserver(this);
}
DownloadItem::~DownloadItem() {
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_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() {
return download_item_->GetReceivedBytes();
}
int64 DownloadItem::GetTotalBytes() {
return download_item_->GetTotalBytes();
}
const GURL& DownloadItem::GetURL() {
return download_item_->GetURL();
}
std::string DownloadItem::GetMimeType() {
return download_item_->GetMimeType();
}
bool DownloadItem::HasUserGesture() {
return download_item_->HasUserGesture();
}
std::string DownloadItem::GetFilename() {
return base::UTF16ToUTF8(net::GenerateFileName(GetURL(),
GetContentDisposition(),
std::string(),
download_item_->GetSuggestedFilename(),
GetMimeType(),
std::string()).LossyDisplayName());
}
std::string DownloadItem::GetContentDisposition() {
return download_item_->GetContentDisposition();
}
void DownloadItem::SetSavePath(const base::FilePath& path) {
download_item_->SetUserData(UserDataKey(), new SavePathData(path));
}
void DownloadItem::Pause() {
download_item_->Pause();
}
void DownloadItem::Resume() {
download_item_->Resume();
}
void DownloadItem::Cancel() {
download_item_->Cancel(true);
}
// static
void DownloadItem::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> prototype) {
mate::ObjectTemplateBuilder(isolate, prototype)
.MakeDestroyable()
.SetMethod("pause", &DownloadItem::Pause)
.SetMethod("resume", &DownloadItem::Resume)
.SetMethod("cancel", &DownloadItem::Cancel)
.SetMethod("getReceivedBytes", &DownloadItem::GetReceivedBytes)
.SetMethod("getTotalBytes", &DownloadItem::GetTotalBytes)
.SetMethod("getURL", &DownloadItem::GetURL)
.SetMethod("getMimeType", &DownloadItem::GetMimeType)
.SetMethod("hasUserGesture", &DownloadItem::HasUserGesture)
.SetMethod("getFilename", &DownloadItem::GetFilename)
.SetMethod("getContentDisposition", &DownloadItem::GetContentDisposition)
.SetMethod("setSavePath", &DownloadItem::SetSavePath);
}
// static
mate::Handle<DownloadItem> DownloadItem::Create(
v8::Isolate* isolate, content::DownloadItem* item) {
auto handle = mate::CreateHandle(isolate, new DownloadItem(item));
g_wrap_download_item.Run(handle.ToV8());
g_download_item_objects[item->GetId()] = make_linked_ptr(
new v8::Global<v8::Value>(isolate, handle.ToV8()));
return handle;
}
// static
void* DownloadItem::UserDataKey() {
return &kDownloadItemSavePathKey;
}
void ClearWrapDownloadItem() {
g_wrap_download_item.Reset();
}
void SetWrapDownloadItem(const WrapDownloadItemCallback& callback) {
g_wrap_download_item = callback;
// Cleanup the wrapper on exit.
atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback(
base::Bind(ClearWrapDownloadItem));
}
} // namespace api
} // namespace atom
namespace {
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
v8::Local<v8::Context> context, void* priv) {
v8::Isolate* isolate = context->GetIsolate();
mate::Dictionary dict(isolate, exports);
dict.SetMethod("_setWrapDownloadItem", &atom::api::SetWrapDownloadItem);
}
} // namespace
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_download_item, Initialize);

View file

@ -0,0 +1,69 @@
// 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_DOWNLOAD_ITEM_H_
#define ATOM_BROWSER_API_ATOM_API_DOWNLOAD_ITEM_H_
#include <string>
#include "atom/browser/api/trackable_object.h"
#include "base/files/file_path.h"
#include "content/public/browser/download_item.h"
#include "native_mate/handle.h"
#include "url/gurl.h"
namespace atom {
namespace api {
class DownloadItem : public mate::TrackableObject<DownloadItem>,
public content::DownloadItem::Observer {
public:
class SavePathData : public base::SupportsUserData::Data {
public:
explicit SavePathData(const base::FilePath& path);
const base::FilePath& path();
private:
base::FilePath path_;
};
static mate::Handle<DownloadItem> Create(v8::Isolate* isolate,
content::DownloadItem* item);
static void* UserDataKey();
// mate::TrackableObject:
static void BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> prototype);
protected:
explicit DownloadItem(content::DownloadItem* download_item);
~DownloadItem();
// Override content::DownloadItem::Observer methods
void OnDownloadUpdated(content::DownloadItem* download) override;
void OnDownloadDestroyed(content::DownloadItem* download) override;
void Pause();
void Resume();
void Cancel();
int64 GetReceivedBytes();
int64 GetTotalBytes();
std::string GetMimeType();
bool HasUserGesture();
std::string GetFilename();
std::string GetContentDisposition();
const GURL& GetURL();
void SetSavePath(const base::FilePath& path);
private:
content::DownloadItem* download_item_;
DISALLOW_COPY_AND_ASSIGN(DownloadItem);
};
} // namespace api
} // namespace atom
#endif // ATOM_BROWSER_API_ATOM_API_DOWNLOAD_ITEM_H_

View file

@ -8,9 +8,9 @@
#include <map>
#include <string>
#include "atom/browser/api/trackable_object.h"
#include "base/callback.h"
#include "chrome/browser/extensions/global_shortcut_listener.h"
#include "native_mate/wrappable.h"
#include "native_mate/handle.h"
#include "ui/base/accelerators/accelerator.h"
@ -19,13 +19,13 @@ namespace atom {
namespace api {
class GlobalShortcut : public extensions::GlobalShortcutListener::Observer,
public mate::Wrappable {
public mate::TrackableObject<GlobalShortcut> {
public:
static mate::Handle<GlobalShortcut> Create(v8::Isolate* isolate);
protected:
GlobalShortcut();
virtual ~GlobalShortcut();
~GlobalShortcut() override;
// mate::Wrappable implementations:
mate::ObjectTemplateBuilder GetObjectTemplateBuilder(

View file

@ -107,6 +107,10 @@ void Menu::SetSublabel(int index, const base::string16& sublabel) {
model_->SetSublabel(index, sublabel);
}
void Menu::SetRole(int index, const base::string16& role) {
model_->SetRole(index, role);
}
void Menu::Clear() {
model_->Clear();
}
@ -147,6 +151,7 @@ bool Menu::IsVisibleAt(int index) const {
void Menu::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> prototype) {
mate::ObjectTemplateBuilder(isolate, prototype)
.MakeDestroyable()
.SetMethod("insertItem", &Menu::InsertItemAt)
.SetMethod("insertCheckItem", &Menu::InsertCheckItemAt)
.SetMethod("insertRadioItem", &Menu::InsertRadioItemAt)
@ -154,6 +159,7 @@ void Menu::BuildPrototype(v8::Isolate* isolate,
.SetMethod("insertSubMenu", &Menu::InsertSubMenuAt)
.SetMethod("setIcon", &Menu::SetIcon)
.SetMethod("setSublabel", &Menu::SetSublabel)
.SetMethod("setRole", &Menu::SetRole)
.SetMethod("clear", &Menu::Clear)
.SetMethod("getIndexOfCommandId", &Menu::GetIndexOfCommandId)
.SetMethod("getItemCount", &Menu::GetItemCount)
@ -163,8 +169,7 @@ void Menu::BuildPrototype(v8::Isolate* isolate,
.SetMethod("isItemCheckedAt", &Menu::IsItemCheckedAt)
.SetMethod("isEnabledAt", &Menu::IsEnabledAt)
.SetMethod("isVisibleAt", &Menu::IsVisibleAt)
.SetMethod("_popup", &Menu::Popup)
.SetMethod("_popupAt", &Menu::PopupAt);
.SetMethod("popupAt", &Menu::PopupAt);
}
} // namespace api

View file

@ -8,16 +8,16 @@
#include <string>
#include "atom/browser/api/atom_api_window.h"
#include "atom/browser/api/trackable_object.h"
#include "atom/browser/ui/atom_menu_model.h"
#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
#include "native_mate/wrappable.h"
namespace atom {
namespace api {
class Menu : public mate::Wrappable,
class Menu : public mate::TrackableObject<Menu>,
public AtomMenuModel::Delegate {
public:
static mate::Wrappable* Create();
@ -37,7 +37,7 @@ class Menu : public mate::Wrappable,
protected:
Menu();
virtual ~Menu();
~Menu() override;
// mate::Wrappable:
void AfterInit(v8::Isolate* isolate) override;
@ -51,8 +51,9 @@ class Menu : public mate::Wrappable,
void ExecuteCommand(int command_id, int event_flags) override;
void MenuWillShow(ui::SimpleMenuModel* source) override;
virtual void Popup(Window* window) = 0;
virtual void PopupAt(Window* window, int x, int y) = 0;
virtual void PopupAt(Window* window,
int x = -1, int y = -1,
int positioning_item = 0) = 0;
scoped_ptr<AtomMenuModel> model_;
Menu* parent_;
@ -73,6 +74,7 @@ class Menu : public mate::Wrappable,
Menu* menu);
void SetIcon(int index, const gfx::Image& image);
void SetSublabel(int index, const base::string16& sublabel);
void SetRole(int index, const base::string16& role);
void Clear();
int GetIndexOfCommandId(int command_id);
int GetItemCount() const;

View file

@ -19,8 +19,7 @@ class MenuMac : public Menu {
protected:
MenuMac();
void Popup(Window* window) override;
void PopupAt(Window* window, int x, int y) override;
void PopupAt(Window* window, int x, int y, int positioning_item = 0) override;
base::scoped_nsobject<AtomMenuController> menu_controller_;

View file

@ -18,39 +18,7 @@ namespace api {
MenuMac::MenuMac() {
}
void MenuMac::Popup(Window* window) {
NativeWindow* native_window = window->window();
if (!native_window)
return;
content::WebContents* web_contents = native_window->web_contents();
if (!web_contents)
return;
NSWindow* nswindow = native_window->GetNativeWindow();
base::scoped_nsobject<AtomMenuController> menu_controller(
[[AtomMenuController alloc] initWithModel:model_.get()]);
// Fake out a context menu event.
NSEvent* currentEvent = [NSApp currentEvent];
NSPoint position = [nswindow mouseLocationOutsideOfEventStream];
NSTimeInterval eventTime = [currentEvent timestamp];
NSEvent* clickEvent = [NSEvent mouseEventWithType:NSRightMouseDown
location:position
modifierFlags:NSRightMouseDownMask
timestamp:eventTime
windowNumber:[nswindow windowNumber]
context:nil
eventNumber:0
clickCount:1
pressure:1.0];
// Show the menu.
[NSMenu popUpContextMenu:[menu_controller menu]
withEvent:clickEvent
forView:web_contents->GetContentNativeView()];
}
void MenuMac::PopupAt(Window* window, int x, int y) {
void MenuMac::PopupAt(Window* window, int x, int y, int positioning_item) {
NativeWindow* native_window = window->window();
if (!native_window)
return;
@ -63,10 +31,23 @@ void MenuMac::PopupAt(Window* window, int x, int y) {
NSMenu* menu = [menu_controller menu];
NSView* view = web_contents->GetContentNativeView();
// Which menu item to show.
NSMenuItem* item = nil;
if (positioning_item < [menu numberOfItems] && positioning_item >= 0)
item = [menu itemAtIndex:positioning_item];
// (-1, -1) means showing on mouse location.
NSPoint position;
if (x == -1 || y == -1) {
NSWindow* nswindow = native_window->GetNativeWindow();
position = [view convertPoint:[nswindow mouseLocationOutsideOfEventStream]
fromView:nil];
} else {
position = NSMakePoint(x, [view frame].size.height - y);
}
// Show the menu.
[menu popUpMenuPositioningItem:[menu itemAtIndex:0]
atLocation:NSMakePoint(x, [view frame].size.height - y)
inView:view];
[menu popUpMenuPositioningItem:item atLocation:position inView:view];
}
// static

View file

@ -16,11 +16,7 @@ namespace api {
MenuViews::MenuViews() {
}
void MenuViews::Popup(Window* window) {
PopupAtPoint(window, gfx::Screen::GetNativeScreen()->GetCursorScreenPoint());
}
void MenuViews::PopupAt(Window* window, int x, int y) {
void MenuViews::PopupAt(Window* window, int x, int y, int positioning_item) {
NativeWindow* native_window = static_cast<NativeWindow*>(window->window());
if (!native_window)
return;
@ -31,18 +27,23 @@ void MenuViews::PopupAt(Window* window, int x, int y) {
if (!view)
return;
gfx::Point origin = view->GetViewBounds().origin();
PopupAtPoint(window, gfx::Point(origin.x() + x, origin.y() + y));
}
// (-1, -1) means showing on mouse location.
gfx::Point location;
if (x == -1 || y == -1) {
location = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint();
} else {
gfx::Point origin = view->GetViewBounds().origin();
location = gfx::Point(origin.x() + x, origin.y() + y);
}
void MenuViews::PopupAtPoint(Window* window, const gfx::Point& point) {
// Show the menu.
views::MenuRunner menu_runner(
model(),
views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS);
ignore_result(menu_runner.RunMenuAt(
static_cast<NativeWindowViews*>(window->window())->widget(),
NULL,
gfx::Rect(point, gfx::Size()),
gfx::Rect(location, gfx::Size()),
views::MENU_ANCHOR_TOPLEFT,
ui::MENU_SOURCE_MOUSE));
}

View file

@ -17,12 +17,9 @@ class MenuViews : public Menu {
MenuViews();
protected:
void Popup(Window* window) override;
void PopupAt(Window* window, int x, int y) override;
void PopupAt(Window* window, int x, int y, int positioning_item = 0) override;
private:
void PopupAtPoint(Window* window, const gfx::Point& point);
DISALLOW_COPY_AND_ASSIGN(MenuViews);
};

View file

@ -5,12 +5,11 @@
#include "atom/browser/api/atom_api_power_monitor.h"
#include "atom/browser/browser.h"
#include "atom/common/node_includes.h"
#include "base/power_monitor/power_monitor.h"
#include "base/power_monitor/power_monitor_device_source.h"
#include "native_mate/dictionary.h"
#include "atom/common/node_includes.h"
namespace atom {
namespace api {
@ -41,9 +40,9 @@ void PowerMonitor::OnResume() {
// static
v8::Local<v8::Value> PowerMonitor::Create(v8::Isolate* isolate) {
if (!Browser::Get()->is_ready()) {
node::ThrowError(
isolate->ThrowException(v8::Exception::Error(mate::StringToV8(
isolate,
"Cannot initialize \"power-monitor\" module before app is ready");
"Cannot initialize \"power-monitor\" module before app is ready")));
return v8::Null(isolate);
}

View file

@ -5,7 +5,7 @@
#ifndef ATOM_BROWSER_API_ATOM_API_POWER_MONITOR_H_
#define ATOM_BROWSER_API_ATOM_API_POWER_MONITOR_H_
#include "atom/browser/api/event_emitter.h"
#include "atom/browser/api/trackable_object.h"
#include "base/compiler_specific.h"
#include "base/power_monitor/power_observer.h"
#include "native_mate/handle.h"
@ -14,14 +14,14 @@ namespace atom {
namespace api {
class PowerMonitor : public mate::EventEmitter,
class PowerMonitor : public mate::TrackableObject<PowerMonitor>,
public base::PowerObserver {
public:
static v8::Local<v8::Value> Create(v8::Isolate* isolate);
protected:
PowerMonitor();
virtual ~PowerMonitor();
~PowerMonitor() override;
// base::PowerObserver implementations:
void OnPowerStateChange(bool on_battery_power) override;

View file

@ -6,9 +6,9 @@
#include <string>
#include "atom/common/node_includes.h"
#include "content/public/browser/power_save_blocker.h"
#include "native_mate/dictionary.h"
#include "atom/common/node_includes.h"
namespace mate {

View file

@ -7,10 +7,10 @@
#include <map>
#include "atom/browser/api/trackable_object.h"
#include "base/memory/scoped_ptr.h"
#include "content/public/browser/power_save_blocker.h"
#include "native_mate/handle.h"
#include "native_mate/wrappable.h"
namespace mate {
class Dictionary;
@ -20,13 +20,13 @@ namespace atom {
namespace api {
class PowerSaveBlocker : public mate::Wrappable {
class PowerSaveBlocker : public mate::TrackableObject<PowerSaveBlocker> {
public:
static mate::Handle<PowerSaveBlocker> Create(v8::Isolate* isolate);
protected:
PowerSaveBlocker();
virtual ~PowerSaveBlocker();
~PowerSaveBlocker() override;
// mate::Wrappable implementations:
mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
@ -48,7 +48,6 @@ class PowerSaveBlocker : public mate::Wrappable {
std::map<int, content::PowerSaveBlocker::PowerSaveBlockerType>;
PowerSaveBlockerTypeMap power_save_blocker_types_;
DISALLOW_COPY_AND_ASSIGN(PowerSaveBlocker);
};

View file

@ -7,264 +7,52 @@
#include "atom/browser/atom_browser_client.h"
#include "atom/browser/atom_browser_context.h"
#include "atom/browser/atom_browser_main_parts.h"
#include "atom/browser/api/atom_api_session.h"
#include "atom/browser/net/adapter_request_job.h"
#include "atom/browser/net/atom_url_request_job_factory.h"
#include "atom/browser/net/url_request_async_asar_job.h"
#include "atom/browser/net/url_request_buffer_job.h"
#include "atom/browser/net/url_request_fetch_job.h"
#include "atom/browser/net/url_request_string_job.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/native_mate_converters/gurl_converter.h"
#include "content/public/browser/browser_thread.h"
#include "native_mate/dictionary.h"
#include "net/url_request/url_request_context.h"
#include "atom/common/native_mate_converters/net_converter.h"
#include "atom/common/node_includes.h"
#include "native_mate/dictionary.h"
using content::BrowserThread;
namespace mate {
template<>
struct Converter<const net::URLRequest*> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const net::URLRequest* val) {
return mate::ObjectTemplateBuilder(isolate)
.SetValue("method", val->method())
.SetValue("url", val->url().spec())
.SetValue("referrer", val->referrer())
.Build()->NewInstance();
}
};
} // namespace mate
namespace atom {
namespace api {
namespace {
typedef net::URLRequestJobFactory::ProtocolHandler ProtocolHandler;
scoped_refptr<base::RefCountedBytes> BufferToRefCountedBytes(
v8::Local<v8::Value> buf) {
scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes);
auto start = reinterpret_cast<const unsigned char*>(node::Buffer::Data(buf));
data->data().assign(start, start + node::Buffer::Length(buf));
return data;
}
class CustomProtocolRequestJob : public AdapterRequestJob {
public:
CustomProtocolRequestJob(Protocol* registry,
ProtocolHandler* protocol_handler,
net::URLRequest* request,
net::NetworkDelegate* network_delegate)
: AdapterRequestJob(protocol_handler, request, network_delegate),
registry_(registry) {
}
void GetJobTypeInUI(const Protocol::JsProtocolHandler& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
v8::Locker locker(registry_->isolate());
v8::HandleScope handle_scope(registry_->isolate());
// Call the JS handler.
v8::Local<v8::Value> result = callback.Run(request());
// Determine the type of the job we are going to create.
if (result->IsString()) {
std::string data = mate::V8ToString(result);
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&AdapterRequestJob::CreateStringJobAndStart,
GetWeakPtr(), "text/plain", "UTF-8", data));
return;
} else if (result->IsObject()) {
v8::Local<v8::Object> obj = result->ToObject();
mate::Dictionary dict(registry_->isolate(), obj);
std::string name = mate::V8ToString(obj->GetConstructorName());
if (name == "RequestStringJob") {
std::string mime_type, charset, data;
dict.Get("mimeType", &mime_type);
dict.Get("charset", &charset);
dict.Get("data", &data);
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&AdapterRequestJob::CreateStringJobAndStart,
GetWeakPtr(), mime_type, charset, data));
return;
} else if (name == "RequestBufferJob") {
std::string mime_type, encoding;
v8::Local<v8::Value> buffer;
dict.Get("mimeType", &mime_type);
dict.Get("encoding", &encoding);
dict.Get("data", &buffer);
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&AdapterRequestJob::CreateBufferJobAndStart,
GetWeakPtr(), mime_type, encoding,
BufferToRefCountedBytes(buffer)));
return;
} else if (name == "RequestFileJob") {
base::FilePath path;
dict.Get("path", &path);
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&AdapterRequestJob::CreateFileJobAndStart,
GetWeakPtr(), path));
return;
} else if (name == "RequestErrorJob") {
int error = net::ERR_NOT_IMPLEMENTED;
dict.Get("error", &error);
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&AdapterRequestJob::CreateErrorJobAndStart,
GetWeakPtr(), error));
return;
} else if (name == "RequestHttpJob") {
GURL url;
std::string method, referrer;
dict.Get("url", &url);
dict.Get("method", &method);
dict.Get("referrer", &referrer);
v8::Local<v8::Value> value;
mate::Handle<Session> session;
scoped_refptr<net::URLRequestContextGetter> request_context_getter;
// "session" null -> pass nullptr;
// "session" a Session object -> use passed session.
// "session" undefined -> use current session;
if (dict.Get("session", &session))
request_context_getter =
session->browser_context()->GetRequestContext();
else if (dict.Get("session", &value) && value->IsNull())
request_context_getter = nullptr;
else
request_context_getter = registry_->request_context_getter();
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&AdapterRequestJob::CreateHttpJobAndStart, GetWeakPtr(),
request_context_getter, url, method, referrer));
return;
}
}
// Try the default protocol handler if we have.
if (default_protocol_handler()) {
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&AdapterRequestJob::CreateJobFromProtocolHandlerAndStart,
GetWeakPtr()));
return;
}
// Fallback to the not implemented error.
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&AdapterRequestJob::CreateErrorJobAndStart,
GetWeakPtr(), net::ERR_NOT_IMPLEMENTED));
}
// AdapterRequestJob:
void GetJobType() override {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&CustomProtocolRequestJob::GetJobTypeInUI,
base::Unretained(this),
registry_->GetProtocolHandler(request()->url().scheme())));
}
private:
Protocol* registry_; // Weak, the Protocol class is expected to live forever.
};
// Always return the same CustomProtocolRequestJob for all requests, because
// the content API needs the ProtocolHandler to return a job immediately, and
// getting the real job from the JS requires asynchronous calls, so we have
// to create an adapter job first.
// Users can also pass an extra ProtocolHandler as the fallback one when
// registered handler doesn't want to deal with the request.
class CustomProtocolHandler : public ProtocolHandler {
public:
CustomProtocolHandler(api::Protocol* registry,
ProtocolHandler* protocol_handler = NULL)
: registry_(registry), protocol_handler_(protocol_handler) {
}
net::URLRequestJob* MaybeCreateJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const override {
return new CustomProtocolRequestJob(registry_, protocol_handler_.get(),
request, network_delegate);
}
ProtocolHandler* ReleaseDefaultProtocolHandler() {
return protocol_handler_.release();
}
ProtocolHandler* original_handler() { return protocol_handler_.get(); }
private:
Protocol* registry_; // Weak, the Protocol class is expected to live forever.
scoped_ptr<ProtocolHandler> protocol_handler_;
DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler);
};
std::string ConvertErrorCode(int error_code) {
switch (error_code) {
case Protocol::ERR_SCHEME_REGISTERED:
return "The Scheme is already registered";
case Protocol::ERR_SCHEME_UNREGISTERED:
return "The Scheme has not been registered";
case Protocol::ERR_SCHEME_INTERCEPTED:
return "There is no protocol handler to intercept";
case Protocol::ERR_SCHEME_UNINTERCEPTED:
return "The protocol is not intercepted";
case Protocol::ERR_NO_SCHEME:
return "The Scheme does not exist.";
case Protocol::ERR_SCHEME:
return "Cannot intercept custom protocols";
default:
NOTREACHED();
return std::string();
}
}
} // namespace
Protocol::Protocol(AtomBrowserContext* browser_context)
: request_context_getter_(browser_context->GetRequestContext()),
job_factory_(browser_context->job_factory()) {
CHECK(job_factory_);
}
Protocol::JsProtocolHandler Protocol::GetProtocolHandler(
const std::string& scheme) {
return protocol_handlers_[scheme];
}
void Protocol::OnIOActionCompleted(const JsCompletionCallback& callback,
int error) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
if (error) {
callback.Run(v8::Exception::Error(
mate::StringToV8(isolate(), ConvertErrorCode(error))));
return;
}
callback.Run(v8::Null(isolate()));
}
mate::ObjectTemplateBuilder Protocol::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
return mate::ObjectTemplateBuilder(isolate)
.SetMethod("registerStandardSchemes", &Protocol::RegisterStandardSchemes)
.SetMethod("isHandledProtocol", &Protocol::IsHandledProtocol)
.SetMethod("_registerProtocol", &Protocol::RegisterProtocol)
.SetMethod("_unregisterProtocol", &Protocol::UnregisterProtocol)
.SetMethod("_interceptProtocol", &Protocol::InterceptProtocol)
.SetMethod("_uninterceptProtocol", &Protocol::UninterceptProtocol);
.SetMethod("registerServiceWorkerSchemes",
&Protocol::RegisterServiceWorkerSchemes)
.SetMethod("registerStringProtocol",
&Protocol::RegisterProtocol<URLRequestStringJob>)
.SetMethod("registerBufferProtocol",
&Protocol::RegisterProtocol<URLRequestBufferJob>)
.SetMethod("registerFileProtocol",
&Protocol::RegisterProtocol<URLRequestAsyncAsarJob>)
.SetMethod("registerHttpProtocol",
&Protocol::RegisterProtocol<URLRequestFetchJob>)
.SetMethod("unregisterProtocol", &Protocol::UnregisterProtocol)
.SetMethod("isProtocolHandled", &Protocol::IsProtocolHandled)
.SetMethod("interceptStringProtocol",
&Protocol::InterceptProtocol<URLRequestStringJob>)
.SetMethod("interceptBufferProtocol",
&Protocol::InterceptProtocol<URLRequestBufferJob>)
.SetMethod("interceptFileProtocol",
&Protocol::InterceptProtocol<URLRequestAsyncAsarJob>)
.SetMethod("interceptHttpProtocol",
&Protocol::InterceptProtocol<URLRequestFetchJob>)
.SetMethod("uninterceptProtocol", &Protocol::UninterceptProtocol);
}
void Protocol::RegisterStandardSchemes(
@ -272,131 +60,91 @@ void Protocol::RegisterStandardSchemes(
atom::AtomBrowserClient::SetCustomSchemes(schemes);
}
void Protocol::IsHandledProtocol(const std::string& scheme,
const net::CompletionCallback& callback) {
BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE,
base::Bind(&AtomURLRequestJobFactory::IsHandledProtocol,
base::Unretained(job_factory_), scheme),
void Protocol::RegisterServiceWorkerSchemes(
const std::vector<std::string>& schemes) {
atom::AtomBrowserClient::SetCustomServiceWorkerSchemes(schemes);
}
void Protocol::UnregisterProtocol(
const std::string& scheme, mate::Arguments* args) {
CompletionCallback callback;
args->GetNext(&callback);
content::BrowserThread::PostTaskAndReplyWithResult(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&Protocol::UnregisterProtocolInIO,
base::Unretained(this), scheme),
base::Bind(&Protocol::OnIOCompleted,
base::Unretained(this), callback));
}
Protocol::ProtocolError Protocol::UnregisterProtocolInIO(
const std::string& scheme) {
if (!job_factory_->HasProtocolHandler(scheme))
return PROTOCOL_NOT_REGISTERED;
job_factory_->SetProtocolHandler(scheme, nullptr);
return PROTOCOL_OK;
}
void Protocol::IsProtocolHandled(const std::string& scheme,
const BooleanCallback& callback) {
content::BrowserThread::PostTaskAndReplyWithResult(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&Protocol::IsProtocolHandledInIO,
base::Unretained(this), scheme),
callback);
}
void Protocol::RegisterProtocol(v8::Isolate* isolate,
const std::string& scheme,
const JsProtocolHandler& handler,
const JsCompletionCallback& callback) {
BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE,
base::Bind(&Protocol::RegisterProtocolInIO,
base::Unretained(this), scheme, handler),
base::Bind(&Protocol::OnIOActionCompleted,
base::Unretained(this), callback));
bool Protocol::IsProtocolHandledInIO(const std::string& scheme) {
return job_factory_->IsHandledProtocol(scheme);
}
void Protocol::UnregisterProtocol(v8::Isolate* isolate,
const std::string& scheme,
const JsCompletionCallback& callback) {
BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE,
base::Bind(&Protocol::UnregisterProtocolInIO,
base::Unretained(this), scheme),
base::Bind(&Protocol::OnIOActionCompleted,
base::Unretained(this), callback));
}
void Protocol::InterceptProtocol(v8::Isolate* isolate,
const std::string& scheme,
const JsProtocolHandler& handler,
const JsCompletionCallback& callback) {
BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE,
base::Bind(&Protocol::InterceptProtocolInIO,
base::Unretained(this), scheme, handler),
base::Bind(&Protocol::OnIOActionCompleted,
base::Unretained(this), callback));
}
void Protocol::UninterceptProtocol(v8::Isolate* isolate,
const std::string& scheme,
const JsCompletionCallback& callback) {
BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE,
void Protocol::UninterceptProtocol(
const std::string& scheme, mate::Arguments* args) {
CompletionCallback callback;
args->GetNext(&callback);
content::BrowserThread::PostTaskAndReplyWithResult(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&Protocol::UninterceptProtocolInIO,
base::Unretained(this), scheme),
base::Bind(&Protocol::OnIOActionCompleted,
base::Bind(&Protocol::OnIOCompleted,
base::Unretained(this), callback));
}
int Protocol::RegisterProtocolInIO(const std::string& scheme,
const JsProtocolHandler& handler) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (ContainsKey(protocol_handlers_, scheme) ||
job_factory_->IsHandledProtocol(scheme)) {
return ERR_SCHEME_REGISTERED;
}
protocol_handlers_[scheme] = handler;
job_factory_->SetProtocolHandler(scheme, new CustomProtocolHandler(this));
return OK;
Protocol::ProtocolError Protocol::UninterceptProtocolInIO(
const std::string& scheme) {
if (!original_protocols_.contains(scheme))
return PROTOCOL_NOT_INTERCEPTED;
job_factory_->ReplaceProtocol(scheme,
original_protocols_.take_and_erase(scheme));
return PROTOCOL_OK;
}
int Protocol::UnregisterProtocolInIO(const std::string& scheme) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
void Protocol::OnIOCompleted(
const CompletionCallback& callback, ProtocolError error) {
// The completion callback is optional.
if (callback.is_null())
return;
ProtocolHandlersMap::iterator it(protocol_handlers_.find(scheme));
if (it == protocol_handlers_.end()) {
return ERR_SCHEME_UNREGISTERED;
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
if (error == PROTOCOL_OK) {
callback.Run(v8::Null(isolate()));
} else {
std::string str = ErrorCodeToString(error);
callback.Run(v8::Exception::Error(mate::StringToV8(isolate(), str)));
}
protocol_handlers_.erase(it);
job_factory_->SetProtocolHandler(scheme, NULL);
return OK;
}
int Protocol::InterceptProtocolInIO(const std::string& scheme,
const JsProtocolHandler& handler) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Force the request context to initialize, otherwise we might have nothing
// to intercept.
request_context_getter_->GetURLRequestContext();
if (!job_factory_->HasProtocolHandler(scheme))
return ERR_NO_SCHEME;
if (ContainsKey(protocol_handlers_, scheme))
return ERR_SCHEME;
protocol_handlers_[scheme] = handler;
ProtocolHandler* original_handler = job_factory_->GetProtocolHandler(scheme);
if (original_handler == nullptr) {
return ERR_SCHEME_INTERCEPTED;
std::string Protocol::ErrorCodeToString(ProtocolError error) {
switch (error) {
case PROTOCOL_FAIL: return "Failed to manipulate protocol factory";
case PROTOCOL_REGISTERED: return "The scheme has been registred";
case PROTOCOL_NOT_REGISTERED: return "The scheme has not been registred";
case PROTOCOL_INTERCEPTED: return "The scheme has been intercepted";
case PROTOCOL_NOT_INTERCEPTED: return "The scheme has not been intercepted";
default: return "Unexpected error";
}
job_factory_->ReplaceProtocol(
scheme, new CustomProtocolHandler(this, original_handler));
return OK;
}
int Protocol::UninterceptProtocolInIO(const std::string& scheme) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
ProtocolHandlersMap::iterator it(protocol_handlers_.find(scheme));
if (it == protocol_handlers_.end())
return ERR_SCHEME_UNREGISTERED;
protocol_handlers_.erase(it);
CustomProtocolHandler* handler = static_cast<CustomProtocolHandler*>(
job_factory_->GetProtocolHandler(scheme));
if (handler->original_handler() == nullptr) {
return ERR_SCHEME_UNINTERCEPTED;
}
// Reset the protocol handler to the orignal one and delete current protocol
// handler.
ProtocolHandler* original_handler = handler->ReleaseDefaultProtocolHandler();
delete job_factory_->ReplaceProtocol(scheme, original_handler);
return OK;
}
// static

View file

@ -9,10 +9,14 @@
#include <map>
#include <vector>
#include "atom/browser/api/event_emitter.h"
#include "atom/browser/net/atom_url_request_job_factory.h"
#include "base/callback.h"
#include "base/containers/scoped_ptr_hash_map.h"
#include "content/public/browser/browser_thread.h"
#include "native_mate/arguments.h"
#include "native_mate/dictionary.h"
#include "native_mate/handle.h"
#include "net/base/completion_callback.h"
#include "native_mate/wrappable.h"
namespace net {
class URLRequest;
@ -26,31 +30,16 @@ class AtomURLRequestJobFactory;
namespace api {
class Protocol : public mate::EventEmitter {
class Protocol : public mate::Wrappable {
public:
using JsProtocolHandler =
base::Callback<v8::Local<v8::Value>(const net::URLRequest*)>;
using JsCompletionCallback = base::Callback<void(v8::Local<v8::Value>)>;
enum {
OK = 0,
ERR_SCHEME_REGISTERED,
ERR_SCHEME_UNREGISTERED,
ERR_SCHEME_INTERCEPTED,
ERR_SCHEME_UNINTERCEPTED,
ERR_NO_SCHEME,
ERR_SCHEME
};
using Handler =
base::Callback<void(const net::URLRequest*, v8::Local<v8::Value>)>;
using CompletionCallback = base::Callback<void(v8::Local<v8::Value>)>;
using BooleanCallback = base::Callback<void(bool)>;
static mate::Handle<Protocol> Create(
v8::Isolate* isolate, AtomBrowserContext* browser_context);
JsProtocolHandler GetProtocolHandler(const std::string& scheme);
net::URLRequestContextGetter* request_context_getter() {
return request_context_getter_.get();
}
protected:
explicit Protocol(AtomBrowserContext* browser_context);
@ -59,48 +48,142 @@ class Protocol : public mate::EventEmitter {
v8::Isolate* isolate);
private:
typedef std::map<std::string, JsProtocolHandler> ProtocolHandlersMap;
// Possible errors.
enum ProtocolError {
PROTOCOL_OK, // no error
PROTOCOL_FAIL, // operation failed, should never occur
PROTOCOL_REGISTERED,
PROTOCOL_NOT_REGISTERED,
PROTOCOL_INTERCEPTED,
PROTOCOL_NOT_INTERCEPTED,
};
// Callback called after performing action on IO thread.
void OnIOActionCompleted(const JsCompletionCallback& callback,
int error);
// The protocol handler that will create a protocol handler for certain
// request job.
template<typename RequestJob>
class CustomProtocolHandler
: public net::URLRequestJobFactory::ProtocolHandler {
public:
CustomProtocolHandler(
v8::Isolate* isolate,
net::URLRequestContextGetter* request_context,
const Handler& handler)
: isolate_(isolate),
request_context_(request_context),
handler_(handler) {}
~CustomProtocolHandler() override {}
net::URLRequestJob* MaybeCreateJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const override {
RequestJob* request_job = new RequestJob(request, network_delegate);
request_job->SetHandlerInfo(isolate_, request_context_, handler_);
return request_job;
}
private:
v8::Isolate* isolate_;
net::URLRequestContextGetter* request_context_;
Protocol::Handler handler_;
DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler);
};
// Register schemes to standard scheme list.
void RegisterStandardSchemes(const std::vector<std::string>& schemes);
// Returns whether a scheme has been registered.
void IsHandledProtocol(const std::string& scheme,
const net::CompletionCallback& callback);
// Register schemes that can handle service worker.
void RegisterServiceWorkerSchemes(const std::vector<std::string>& schemes);
// Register/unregister an networking |scheme| which would be handled by
// |callback|.
void RegisterProtocol(v8::Isolate* isolate,
const std::string& scheme,
const JsProtocolHandler& handler,
const JsCompletionCallback& callback);
void UnregisterProtocol(v8::Isolate* isolate, const std::string& scheme,
const JsCompletionCallback& callback);
// Register the protocol with certain request job.
template<typename RequestJob>
void RegisterProtocol(const std::string& scheme,
const Handler& handler,
mate::Arguments* args) {
CompletionCallback callback;
args->GetNext(&callback);
content::BrowserThread::PostTaskAndReplyWithResult(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&Protocol::RegisterProtocolInIO<RequestJob>,
base::Unretained(this), scheme, handler),
base::Bind(&Protocol::OnIOCompleted,
base::Unretained(this), callback));
}
template<typename RequestJob>
ProtocolError RegisterProtocolInIO(const std::string& scheme,
const Handler& handler) {
if (job_factory_->IsHandledProtocol(scheme))
return PROTOCOL_REGISTERED;
scoped_ptr<CustomProtocolHandler<RequestJob>> protocol_handler(
new CustomProtocolHandler<RequestJob>(
isolate(), request_context_getter_, handler));
if (job_factory_->SetProtocolHandler(scheme, protocol_handler.Pass()))
return PROTOCOL_OK;
else
return PROTOCOL_FAIL;
}
// Intercept/unintercept an existing protocol handler.
void InterceptProtocol(v8::Isolate* isolate,
const std::string& scheme,
const JsProtocolHandler& handler,
const JsCompletionCallback& callback);
void UninterceptProtocol(v8::Isolate* isolate, const std::string& scheme,
const JsCompletionCallback& callback);
// Unregister the protocol handler that handles |scheme|.
void UnregisterProtocol(const std::string& scheme, mate::Arguments* args);
ProtocolError UnregisterProtocolInIO(const std::string& scheme);
// The networking related operations have to be done in IO thread.
int RegisterProtocolInIO(const std::string& scheme,
const JsProtocolHandler& handler);
int UnregisterProtocolInIO(const std::string& scheme);
int InterceptProtocolInIO(const std::string& scheme,
const JsProtocolHandler& handler);
int UninterceptProtocolInIO(const std::string& scheme);
// Whether the protocol has handler registered.
void IsProtocolHandled(const std::string& scheme,
const BooleanCallback& callback);
bool IsProtocolHandledInIO(const std::string& scheme);
scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
// Replace the protocol handler with a new one.
template<typename RequestJob>
void InterceptProtocol(const std::string& scheme,
const Handler& handler,
mate::Arguments* args) {
CompletionCallback callback;
args->GetNext(&callback);
content::BrowserThread::PostTaskAndReplyWithResult(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&Protocol::InterceptProtocolInIO<RequestJob>,
base::Unretained(this), scheme, handler),
base::Bind(&Protocol::OnIOCompleted,
base::Unretained(this), callback));
}
template<typename RequestJob>
ProtocolError InterceptProtocolInIO(const std::string& scheme,
const Handler& handler) {
if (!job_factory_->IsHandledProtocol(scheme))
return PROTOCOL_NOT_REGISTERED;
// It is possible a protocol is handled but can not be intercepted.
if (!job_factory_->HasProtocolHandler(scheme))
return PROTOCOL_FAIL;
if (ContainsKey(original_protocols_, scheme))
return PROTOCOL_INTERCEPTED;
scoped_ptr<CustomProtocolHandler<RequestJob>> protocol_handler(
new CustomProtocolHandler<RequestJob>(
isolate(), request_context_getter_, handler));
original_protocols_.set(
scheme,
job_factory_->ReplaceProtocol(scheme, protocol_handler.Pass()));
return PROTOCOL_OK;
}
AtomURLRequestJobFactory* job_factory_;
ProtocolHandlersMap protocol_handlers_;
// Restore the |scheme| to its original protocol handler.
void UninterceptProtocol(const std::string& scheme, mate::Arguments* args);
ProtocolError UninterceptProtocolInIO(const std::string& scheme);
// Convert error code to JS exception and call the callback.
void OnIOCompleted(const CompletionCallback& callback, ProtocolError error);
// Convert error code to string.
std::string ErrorCodeToString(ProtocolError error);
net::URLRequestContextGetter* request_context_getter_;
// Map that stores the original protocols of schemes.
using OriginalProtocolsMap = base::ScopedPtrHashMap<
std::string,
scoped_ptr<net::URLRequestJobFactory::ProtocolHandler>>;
OriginalProtocolsMap original_protocols_;
AtomURLRequestJobFactory* job_factory_; // weak ref
DISALLOW_COPY_AND_ASSIGN(Protocol);
};

View file

@ -41,7 +41,7 @@ std::vector<std::string> 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;
}
@ -113,14 +113,16 @@ mate::ObjectTemplateBuilder Screen::GetObjectTemplateBuilder(
// static
v8::Local<v8::Value> Screen::Create(v8::Isolate* isolate) {
if (!Browser::Get()->is_ready()) {
node::ThrowError(isolate,
"Cannot initialize \"screen\" module before app is ready");
isolate->ThrowException(v8::Exception::Error(mate::StringToV8(
isolate,
"Cannot initialize \"screen\" module before app is ready")));
return v8::Null(isolate);
}
gfx::Screen* screen = gfx::Screen::GetNativeScreen();
if (!screen) {
node::ThrowError(isolate, "Failed to get screen information");
isolate->ThrowException(v8::Exception::Error(mate::StringToV8(
isolate, "Failed to get screen information")));
return v8::Null(isolate);
}

View file

@ -8,14 +8,25 @@
#include <vector>
#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"
#include "atom/browser/net/atom_cert_verifier.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/gurl_converter.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/native_mate_converters/net_converter.h"
#include "atom/common/node_includes.h"
#include "base/files/file_path.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/thread_task_runner_handle.h"
#include "brightray/browser/net/devtools_network_conditions.h"
#include "brightray/browser/net/devtools_network_controller.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
@ -28,8 +39,6 @@
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "atom/common/node_includes.h"
using content::BrowserThread;
using content::StoragePartition;
@ -44,7 +53,7 @@ struct ClearStorageDataOptions {
uint32 GetStorageMask(const std::vector<std::string>& 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")
@ -68,7 +77,7 @@ uint32 GetStorageMask(const std::vector<std::string>& storage_types) {
uint32 GetQuotaMask(const std::vector<std::string>& 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")
@ -101,6 +110,35 @@ struct Converter<ClearStorageDataOptions> {
}
};
template<>
struct Converter<net::ProxyConfig> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
net::ProxyConfig* out) {
std::string proxy_rules;
GURL pac_url;
mate::Dictionary options;
// Fallback to previous API when passed String.
// https://git.io/vuhjj
if (ConvertFromV8(isolate, val, &proxy_rules)) {
pac_url = GURL(proxy_rules); // Assume it is PAC script if it is URL.
} else if (ConvertFromV8(isolate, val, &options)) {
options.Get("pacScript", &pac_url);
options.Get("proxyRules", &proxy_rules);
} else {
return false;
}
// pacScript takes precedence over proxyRules.
if (!pac_url.is_empty() && pac_url.is_valid()) {
out->set_pac_url(pac_url);
} else {
out->proxy_rules().ParseFromString(proxy_rules);
}
return true;
}
};
} // namespace mate
namespace atom {
@ -109,6 +147,10 @@ namespace api {
namespace {
// The wrapSession funtion which is implemented in JavaScript
using WrapSessionCallback = base::Callback<void(v8::Local<v8::Value>)>;
WrapSessionCallback g_wrap_session;
class ResolveProxyHelper {
public:
ResolveProxyHelper(AtomBrowserContext* browser_context,
@ -163,7 +205,7 @@ class ResolveProxyHelper {
};
// Runs the callback in UI thread.
template <typename ...T>
template<typename ...T>
void RunCallbackInUI(const base::Callback<void(T...)>& callback, T... result) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE, base::Bind(callback, result...));
@ -171,19 +213,35 @@ void RunCallbackInUI(const base::Callback<void(T...)>& callback, T... result) {
// Callback of HttpCache::GetBackend.
void OnGetBackend(disk_cache::Backend** backend_ptr,
Session::CacheAction action,
const net::CompletionCallback& callback,
int result) {
if (result != net::OK) {
RunCallbackInUI(callback, result);
} else if (backend_ptr && *backend_ptr) {
(*backend_ptr)->DoomAllEntries(base::Bind(&RunCallbackInUI<int>, callback));
if (action == Session::CacheAction::CLEAR) {
(*backend_ptr)->DoomAllEntries(base::Bind(&RunCallbackInUI<int>,
callback));
} else if (action == Session::CacheAction::STATS) {
base::StringPairs stats;
(*backend_ptr)->GetStats(&stats);
for (size_t i = 0; i < stats.size(); ++i) {
if (stats[i].first == "Current size") {
int current_size;
base::StringToInt(stats[i].second, &current_size);
RunCallbackInUI(callback, current_size);
break;
}
}
}
} else {
RunCallbackInUI<int>(callback, net::ERR_FAILED);
}
}
void ClearHttpCacheInIO(
void DoCacheActionInIO(
const scoped_refptr<net::URLRequestContextGetter>& context_getter,
Session::CacheAction action,
const net::CompletionCallback& callback) {
auto request_context = context_getter->GetURLRequestContext();
auto http_cache = request_context->http_transaction_factory()->GetCache();
@ -194,19 +252,20 @@ void ClearHttpCacheInIO(
using BackendPtr = disk_cache::Backend*;
BackendPtr* backend_ptr = new BackendPtr(nullptr);
net::CompletionCallback on_get_backend =
base::Bind(&OnGetBackend, base::Owned(backend_ptr), callback);
base::Bind(&OnGetBackend, base::Owned(backend_ptr), action, callback);
int rv = http_cache->GetBackend(backend_ptr, on_get_backend);
if (rv != net::ERR_IO_PENDING)
on_get_backend.Run(net::OK);
}
void SetProxyInIO(net::URLRequestContextGetter* getter,
const std::string& proxy,
const net::ProxyConfig& config,
const base::Closure& callback) {
net::ProxyConfig config;
config.proxy_rules().ParseFromString(proxy);
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);
}
@ -215,19 +274,42 @@ void SetProxyInIO(net::URLRequestContextGetter* getter,
Session::Session(AtomBrowserContext* browser_context)
: browser_context_(browser_context) {
AttachAsUserData(browser_context);
// Observe DownloadManger to get download notifications.
content::BrowserContext::GetDownloadManager(browser_context)->
AddObserver(this);
}
Session::~Session() {
content::BrowserContext::GetDownloadManager(browser_context())->
RemoveObserver(this);
}
void Session::OnDownloadCreated(content::DownloadManager* manager,
content::DownloadItem* item) {
auto web_contents = item->GetWebContents();
if (SavePageHandler::IsSavePageTypes(item->GetMimeType()))
return;
bool prevent_default = Emit(
"will-download",
DownloadItem::Create(isolate(), item),
api::WebContents::CreateFrom(isolate(), web_contents));
if (prevent_default) {
item->Cancel(true);
item->Remove();
}
}
void Session::ResolveProxy(const GURL& url, ResolveProxyCallback callback) {
new ResolveProxyHelper(browser_context_, url, callback);
new ResolveProxyHelper(browser_context(), url, callback);
}
void Session::ClearCache(const net::CompletionCallback& callback) {
template<Session::CacheAction action>
void Session::DoCacheAction(const net::CompletionCallback& callback) {
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&ClearHttpCacheInIO,
base::Bind(&DoCacheActionInIO,
make_scoped_refptr(browser_context_->GetRequestContext()),
action,
callback));
}
@ -242,18 +324,24 @@ void Session::ClearStorageData(mate::Arguments* args) {
}
auto storage_partition =
content::BrowserContext::GetStoragePartition(browser_context_, nullptr);
content::BrowserContext::GetStoragePartition(browser_context(), nullptr);
storage_partition->ClearData(
options.storage_types, options.quota_types, options.origin,
content::StoragePartition::OriginMatcherFunction(),
base::Time(), base::Time::Max(), callback);
}
void Session::SetProxy(const std::string& proxy,
void Session::FlushStorageData() {
auto storage_partition =
content::BrowserContext::GetStoragePartition(browser_context(), nullptr);
storage_partition->Flush();
}
void Session::SetProxy(const net::ProxyConfig& config,
const base::Closure& callback) {
auto getter = browser_context_->GetRequestContext();
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&SetProxyInIO, base::Unretained(getter), proxy, callback));
base::Bind(&SetProxyInIO, base::Unretained(getter), config, callback));
}
void Session::SetDownloadPath(const base::FilePath& path) {
@ -261,36 +349,135 @@ void Session::SetDownloadPath(const base::FilePath& path) {
prefs::kDownloadDefaultDirectory, path);
}
void Session::EnableNetworkEmulation(const mate::Dictionary& options) {
scoped_ptr<brightray::DevToolsNetworkConditions> conditions;
bool offline = false;
double latency, download_throughput, upload_throughput;
if (options.Get("offline", &offline) && offline) {
conditions.reset(new brightray::DevToolsNetworkConditions(offline));
} else {
options.Get("latency", &latency);
options.Get("downloadThroughput", &download_throughput);
options.Get("uploadThroughput", &upload_throughput);
conditions.reset(
new brightray::DevToolsNetworkConditions(false,
latency,
download_throughput,
upload_throughput));
}
auto controller = browser_context_->GetDevToolsNetworkController();
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&brightray::DevToolsNetworkController::SetNetworkState,
base::Unretained(controller),
std::string(),
base::Passed(&conditions)));
}
void Session::DisableNetworkEmulation() {
scoped_ptr<brightray::DevToolsNetworkConditions> conditions(
new brightray::DevToolsNetworkConditions(false));
auto controller = browser_context_->GetDevToolsNetworkController();
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&brightray::DevToolsNetworkController::SetNetworkState,
base::Unretained(controller),
std::string(),
base::Passed(&conditions)));
}
void Session::SetCertVerifyProc(v8::Local<v8::Value> val,
mate::Arguments* args) {
AtomCertVerifier::VerifyProc proc;
if (!(val->IsNull() || mate::ConvertFromV8(args->isolate(), val, &proc))) {
args->ThrowError("Must pass null or function");
return;
}
browser_context_->cert_verifier()->SetVerifyProc(proc);
}
v8::Local<v8::Value> Session::Cookies(v8::Isolate* isolate) {
if (cookies_.IsEmpty()) {
auto handle = atom::api::Cookies::Create(isolate, browser_context_);
auto handle = atom::api::Cookies::Create(isolate, browser_context());
cookies_.Reset(isolate, handle.ToV8());
}
return v8::Local<v8::Value>::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)
.SetProperty("cookies", &Session::Cookies);
v8::Local<v8::Value> 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<v8::Value>::New(isolate, web_request_);
}
// static
mate::Handle<Session> Session::CreateFrom(
v8::Isolate* isolate,
AtomBrowserContext* browser_context) {
v8::Isolate* isolate, AtomBrowserContext* browser_context) {
auto existing = TrackableObject::FromWrappedClass(isolate, browser_context);
if (existing)
return mate::CreateHandle(isolate, static_cast<Session*>(existing));
return mate::CreateHandle(isolate, new Session(browser_context));
auto handle = mate::CreateHandle(isolate, new Session(browser_context));
g_wrap_session.Run(handle.ToV8());
return handle;
}
// static
mate::Handle<Session> Session::FromPartition(
v8::Isolate* isolate, const std::string& partition, bool in_memory) {
auto browser_context = brightray::BrowserContext::From(partition, in_memory);
return CreateFrom(isolate,
static_cast<AtomBrowserContext*>(browser_context.get()));
}
// static
void Session::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> prototype) {
mate::ObjectTemplateBuilder(isolate, prototype)
.MakeDestroyable()
.SetMethod("resolveProxy", &Session::ResolveProxy)
.SetMethod("getCacheSize", &Session::DoCacheAction<CacheAction::STATS>)
.SetMethod("clearCache", &Session::DoCacheAction<CacheAction::CLEAR>)
.SetMethod("clearStorageData", &Session::ClearStorageData)
.SetMethod("flushStorageData", &Session::FlushStorageData)
.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();
}
void SetWrapSession(const WrapSessionCallback& callback) {
g_wrap_session = callback;
// Cleanup the wrapper on exit.
atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback(
base::Bind(ClearWrapSession));
}
} // namespace api
} // namespace atom
namespace {
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
v8::Local<v8::Context> context, void* priv) {
v8::Isolate* isolate = context->GetIsolate();
mate::Dictionary dict(isolate, exports);
dict.SetMethod("fromPartition", &atom::api::Session::FromPartition);
dict.SetMethod("_setWrapSession", &atom::api::SetWrapSession);
}
} // namespace
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_session, Initialize)

View file

@ -8,6 +8,7 @@
#include <string>
#include "atom/browser/api/trackable_object.h"
#include "content/public/browser/download_manager.h"
#include "native_mate/handle.h"
#include "net/base/completion_callback.h"
@ -19,6 +20,11 @@ class FilePath;
namespace mate {
class Arguments;
class Dictionary;
}
namespace net {
class ProxyConfig;
}
namespace atom {
@ -27,35 +33,57 @@ class AtomBrowserContext;
namespace api {
class Session: public mate::TrackableObject<Session> {
class Session: public mate::TrackableObject<Session>,
public content::DownloadManager::Observer {
public:
using ResolveProxyCallback = base::Callback<void(std::string)>;
enum class CacheAction {
CLEAR,
STATS,
};
// Gets or creates Session from the |browser_context|.
static mate::Handle<Session> CreateFrom(
v8::Isolate* isolate, AtomBrowserContext* browser_context);
AtomBrowserContext* browser_context() const { return browser_context_; }
// Gets the Session of |partition| and |in_memory|.
static mate::Handle<Session> FromPartition(
v8::Isolate* isolate, const std::string& partition, bool in_memory);
AtomBrowserContext* browser_context() const { return browser_context_.get(); }
// mate::TrackableObject:
static void BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> prototype);
protected:
explicit Session(AtomBrowserContext* browser_context);
~Session();
// mate::Wrappable implementations:
mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override;
// content::DownloadManager::Observer:
void OnDownloadCreated(content::DownloadManager* manager,
content::DownloadItem* item) override;
private:
void ResolveProxy(const GURL& url, ResolveProxyCallback callback);
void ClearCache(const net::CompletionCallback& callback);
template<CacheAction action>
void DoCacheAction(const net::CompletionCallback& callback);
void ClearStorageData(mate::Arguments* args);
void SetProxy(const std::string& proxy, const base::Closure& callback);
void FlushStorageData();
void SetProxy(const net::ProxyConfig& config, const base::Closure& callback);
void SetDownloadPath(const base::FilePath& path);
void EnableNetworkEmulation(const mate::Dictionary& options);
void DisableNetworkEmulation();
void SetCertVerifyProc(v8::Local<v8::Value> proc, mate::Arguments* args);
v8::Local<v8::Value> Cookies(v8::Isolate* isolate);
v8::Local<v8::Value> WebRequest(v8::Isolate* isolate);
// Cached object.
v8::Global<v8::Value> cookies_;
v8::Global<v8::Value> web_request_;
AtomBrowserContext* browser_context_; // weak ref
scoped_refptr<AtomBrowserContext> browser_context_;
DISALLOW_COPY_AND_ASSIGN(Session);
};

View file

@ -12,13 +12,12 @@
#include "atom/common/native_mate_converters/gfx_converter.h"
#include "atom/common/native_mate_converters/image_converter.h"
#include "atom/common/native_mate_converters/string16_converter.h"
#include "atom/common/node_includes.h"
#include "native_mate/constructor.h"
#include "native_mate/dictionary.h"
#include "ui/events/event_constants.h"
#include "ui/gfx/image/image.h"
#include "atom/common/node_includes.h"
namespace atom {
namespace api {
@ -35,7 +34,8 @@ Tray::~Tray() {
// static
mate::Wrappable* Tray::New(v8::Isolate* isolate, const gfx::Image& image) {
if (!Browser::Get()->is_ready()) {
node::ThrowError(isolate, "Cannot create Tray before app is ready");
isolate->ThrowException(v8::Exception::Error(mate::StringToV8(
isolate, "Cannot create Tray before app is ready")));
return nullptr;
}
return new Tray(image);
@ -44,21 +44,21 @@ mate::Wrappable* Tray::New(v8::Isolate* isolate, const gfx::Image& image) {
void Tray::OnClicked(const gfx::Rect& bounds, int modifiers) {
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
EmitCustomEvent("clicked",
EmitCustomEvent("click",
ModifiersToObject(isolate(), modifiers), bounds);
}
void Tray::OnDoubleClicked(const gfx::Rect& bounds, int modifiers) {
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
EmitCustomEvent("double-clicked",
EmitCustomEvent("double-click",
ModifiersToObject(isolate(), modifiers), bounds);
}
void Tray::OnRightClicked(const gfx::Rect& bounds, int modifiers) {
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
EmitCustomEvent("right-clicked",
EmitCustomEvent("right-click",
ModifiersToObject(isolate(), modifiers), bounds);
}
@ -67,23 +67,31 @@ void Tray::OnBalloonShow() {
}
void Tray::OnBalloonClicked() {
Emit("balloon-clicked");
Emit("balloon-click");
}
void Tray::OnBalloonClosed() {
Emit("balloon-closed");
}
void Tray::OnDrop() {
Emit("drop");
}
void Tray::OnDropFiles(const std::vector<std::string>& files) {
Emit("drop-files", files);
}
bool Tray::IsDestroyed() const {
return !tray_icon_;
void Tray::OnDragEntered() {
Emit("drag-enter");
}
void Tray::Destroy() {
tray_icon_.reset();
void Tray::OnDragExited() {
Emit("drag-leave");
}
void Tray::OnDragEnded() {
Emit("drag-end");
}
void Tray::SetImage(mate::Arguments* args, const gfx::Image& image) {
@ -121,9 +129,11 @@ void Tray::DisplayBalloon(mate::Arguments* args,
}
void Tray::PopUpContextMenu(mate::Arguments* args) {
mate::Handle<Menu> 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) {
@ -144,7 +154,7 @@ v8::Local<v8::Object> Tray::ModifiersToObject(v8::Isolate* isolate,
void Tray::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> prototype) {
mate::ObjectTemplateBuilder(isolate, prototype)
.SetMethod("destroy", &Tray::Destroy, true)
.MakeDestroyable()
.SetMethod("setImage", &Tray::SetImage)
.SetMethod("setPressedImage", &Tray::SetPressedImage)
.SetMethod("setToolTip", &Tray::SetToolTip)

View file

@ -8,7 +8,7 @@
#include <string>
#include <vector>
#include "atom/browser/api/event_emitter.h"
#include "atom/browser/api/trackable_object.h"
#include "atom/browser/ui/tray_icon_observer.h"
#include "base/memory/scoped_ptr.h"
@ -29,7 +29,7 @@ namespace api {
class Menu;
class Tray : public mate::EventEmitter,
class Tray : public mate::TrackableObject<Tray>,
public TrayIconObserver {
public:
static mate::Wrappable* New(v8::Isolate* isolate, const gfx::Image& image);
@ -39,7 +39,7 @@ class Tray : public mate::EventEmitter,
protected:
explicit Tray(const gfx::Image& image);
virtual ~Tray();
~Tray() override;
// TrayIconObserver:
void OnClicked(const gfx::Rect& bounds, int modifiers) override;
@ -48,12 +48,12 @@ class Tray : public mate::EventEmitter,
void OnBalloonShow() override;
void OnBalloonClicked() override;
void OnBalloonClosed() override;
void OnDrop() override;
void OnDropFiles(const std::vector<std::string>& files) override;
void OnDragEntered() override;
void OnDragExited() override;
void OnDragEnded() override;
// mate::Wrappable:
bool IsDestroyed() const override;
void Destroy();
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);

View file

@ -5,16 +5,22 @@
#include "atom/browser/api/atom_api_web_contents.h"
#include <set>
#include <string>
#include "atom/browser/api/atom_api_debugger.h"
#include "atom/browser/api/atom_api_session.h"
#include "atom/browser/api/atom_api_window.h"
#include "atom/browser/atom_browser_client.h"
#include "atom/browser/atom_browser_context.h"
#include "atom/browser/atom_browser_main_parts.h"
#include "atom/browser/native_window.h"
#include "atom/browser/web_contents_preferences.h"
#include "atom/browser/web_view_guest_delegate.h"
#include "atom/common/api/api_messages.h"
#include "atom/common/api/event_emitter_caller.h"
#include "atom/common/native_mate_converters/blink_converter.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/content_converter.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/native_mate_converters/gfx_converter.h"
#include "atom/common/native_mate_converters/gurl_converter.h"
@ -24,25 +30,32 @@
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "brightray/browser/inspectable_web_contents.h"
#include "brightray/browser/inspectable_web_contents_view.h"
#include "chrome/browser/printing/print_view_manager_basic.h"
#include "chrome/browser/printing/print_preview_message_handler.h"
#include "content/common/view_messages.h"
#include "content/public/browser/favicon_status.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/plugin_service.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/resource_request_details.h"
#include "content/public/browser/service_worker_context.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/context_menu_params.h"
#include "native_mate/dictionary.h"
#include "native_mate/object_template_builder.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/static_http_user_agent_settings.h"
#include "net/url_request/url_request_context.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/base/l10n/l10n_util.h"
#include "atom/common/node_includes.h"
@ -54,9 +67,12 @@ struct PrintSettings {
};
void SetUserAgentInIO(scoped_refptr<net::URLRequestContextGetter> getter,
std::string accept_lang,
std::string user_agent) {
getter->GetURLRequestContext()->set_http_user_agent_settings(
new net::StaticHttpUserAgentSettings("en-us,en", user_agent));
new net::StaticHttpUserAgentSettings(
net::HttpUtil::GenerateAcceptLanguageHeader(accept_lang),
user_agent));
}
} // namespace
@ -114,6 +130,53 @@ struct Converter<WindowOpenDisposition> {
}
};
template<>
struct Converter<net::HttpResponseHeaders*> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
net::HttpResponseHeaders* headers) {
base::DictionaryValue response_headers;
if (headers) {
void* iter = nullptr;
std::string key;
std::string value;
while (headers->EnumerateHeaderLines(&iter, &key, &value)) {
key = base::ToLowerASCII(key);
if (response_headers.HasKey(key)) {
base::ListValue* values = nullptr;
if (response_headers.GetList(key, &values))
values->AppendString(value);
} else {
scoped_ptr<base::ListValue> values(new base::ListValue());
values->AppendString(value);
response_headers.Set(key, values.Pass());
}
}
}
return ConvertToV8(isolate, response_headers);
}
};
template<>
struct Converter<content::SavePageType> {
static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val,
content::SavePageType* out) {
std::string save_type;
if (!ConvertFromV8(isolate, val, &save_type))
return false;
save_type = base::ToLowerASCII(save_type);
if (save_type == "htmlonly") {
*out = content::SAVE_PAGE_TYPE_AS_ONLY_HTML;
} else if (save_type == "htmlcomplete") {
*out = content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML;
} else if (save_type == "mhtml") {
*out = content::SAVE_PAGE_TYPE_AS_MHTML;
} else {
return false;
}
return true;
}
};
} // namespace mate
@ -123,9 +186,7 @@ namespace api {
namespace {
v8::Persistent<v8::ObjectTemplate> template_;
// The wrapWebContents funtion which is implemented in JavaScript
// The wrapWebContents function which is implemented in JavaScript
using WrapWebContentsCallback = base::Callback<void(v8::Local<v8::Value>)>;
WrapWebContentsCallback g_wrap_web_contents;
@ -153,23 +214,42 @@ WebContents::WebContents(content::WebContents* web_contents)
web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent());
}
WebContents::WebContents(const mate::Dictionary& options) {
WebContents::WebContents(v8::Isolate* isolate,
const mate::Dictionary& options)
: request_id_(0) {
// Whether it is a guest WebContents.
bool is_guest = false;
options.Get("isGuest", &is_guest);
type_ = is_guest ? WEB_VIEW : BROWSER_WINDOW;
auto browser_context = AtomBrowserMainParts::Get()->browser_context();
// Obtain the session.
std::string partition;
mate::Handle<api::Session> session;
if (options.Get("session", &session)) {
} else if (options.Get("partition", &partition) && !partition.empty()) {
bool in_memory = true;
if (base::StartsWith(partition, "persist:", base::CompareCase::SENSITIVE)) {
in_memory = false;
partition = partition.substr(8);
}
session = Session::FromPartition(isolate, partition, in_memory);
} else {
// Use the default session if not specified.
session = Session::FromPartition(isolate, "", false);
}
session_.Reset(isolate, session.ToV8());
content::WebContents* web_contents;
if (is_guest) {
content::SiteInstance* site_instance = content::SiteInstance::CreateForURL(
browser_context, GURL("chrome-guest://fake-host"));
content::WebContents::CreateParams params(browser_context, site_instance);
session->browser_context(), GURL("chrome-guest://fake-host"));
content::WebContents::CreateParams params(
session->browser_context(), site_instance);
guest_delegate_.reset(new WebViewGuestDelegate);
params.guest_delegate = guest_delegate_.get();
web_contents = content::WebContents::Create(params);
} else {
content::WebContents::CreateParams params(browser_context);
content::WebContents::CreateParams params(session->browser_context());
web_contents = content::WebContents::Create(params);
}
@ -177,6 +257,11 @@ WebContents::WebContents(const mate::Dictionary& options) {
AttachAsUserData(web_contents);
InitWithWebContents(web_contents);
managed_web_contents()->GetView()->SetDelegate(this);
// Save the preferences in C++.
new WebContentsPreferences(web_contents, options);
web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent());
if (is_guest) {
@ -196,7 +281,18 @@ WebContents::WebContents(const mate::Dictionary& options) {
}
WebContents::~WebContents() {
Destroy();
// The destroy() is called.
if (managed_web_contents()) {
// For webview we need to tell content module to do some cleanup work before
// destroying it.
if (type_ == WEB_VIEW)
guest_delegate_->Destroy();
// The WebContentsDestroyed will not be called automatically because we
// unsubscribe from webContents before destroying it. So we have to manually
// call it here to make sure "destroyed" event is emitted.
WebContentsDestroyed();
}
}
bool WebContents::AddMessageToConsole(content::WebContents* source,
@ -217,7 +313,7 @@ bool WebContents::ShouldCreateWebContents(
int route_id,
int main_frame_route_id,
WindowContainerType window_container_type,
const base::string16& frame_name,
const std::string& frame_name,
const GURL& target_url,
const std::string& partition_id,
content::SessionStorageNamespace* session_storage_namespace) {
@ -277,7 +373,10 @@ bool WebContents::IsPopupOrPanel(const content::WebContents* source) const {
void WebContents::HandleKeyboardEvent(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) {
if (type_ == BROWSER_WINDOW) {
if (event.windowsKeyCode == ui::VKEY_ESCAPE && is_html_fullscreen()) {
// Escape exits tabbed fullscreen mode.
ExitFullscreenModeForTab(source);
} else if (type_ == BROWSER_WINDOW) {
owner_window()->HandleKeyboardEvent(source, event);
} else if (type_ == WEB_VIEW && guest_delegate_) {
// Send the unhandled keyboard events back to the embedder.
@ -308,6 +407,43 @@ void WebContents::RendererResponsive(content::WebContents* source) {
owner_window()->RendererResponsive(source);
}
bool WebContents::HandleContextMenu(const content::ContextMenuParams& params) {
if (!params.custom_context.is_pepper_menu)
return false;
Emit("pepper-context-menu", std::make_pair(params, web_contents()));
web_contents()->NotifyContextMenuClosed(params.custom_context);
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.
@ -339,6 +475,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())
@ -354,20 +506,21 @@ 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) {
Emit("did-fail-load", error_code, error_description);
const base::string16& error_description,
bool was_ignored_by_handler) {
Emit("did-fail-provisional-load", error_code, error_description, url);
}
void WebContents::DidFailLoad(content::RenderFrameHost* render_frame_host,
const GURL& validated_url,
int error_code,
const base::string16& error_description) {
Emit("did-fail-load", error_code, error_description);
const base::string16& error_description,
bool was_ignored_by_handler) {
Emit("did-fail-load", error_code, error_description, validated_url);
}
void WebContents::DidStartLoading() {
@ -380,30 +533,6 @@ void WebContents::DidStopLoading() {
void WebContents::DidGetResourceResponseStart(
const content::ResourceRequestDetails& details) {
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
base::DictionaryValue response_headers;
net::HttpResponseHeaders* headers = details.headers.get();
if (!headers)
return;
void* iter = nullptr;
std::string key;
std::string value;
while (headers->EnumerateHeaderLines(&iter, &key, &value)) {
key = base::StringToLowerASCII(key);
value = base::StringToLowerASCII(value);
if (response_headers.HasKey(key)) {
base::ListValue* values = nullptr;
if (response_headers.GetList(key, &values))
values->AppendString(value);
} else {
scoped_ptr<base::ListValue> values(new base::ListValue());
values->AppendString(value);
response_headers.Set(key, values.Pass());
}
}
Emit("did-get-response-details",
details.socket_address.IsEmpty(),
details.url,
@ -411,7 +540,7 @@ void WebContents::DidGetResourceResponseStart(
details.http_response_code,
details.method,
details.referrer,
response_headers);
details.headers.get());
}
void WebContents::DidGetRedirectForResourceRequest(
@ -420,21 +549,28 @@ void WebContents::DidGetRedirectForResourceRequest(
Emit("did-get-redirect-request",
details.url,
details.new_url,
(details.resource_type == content::RESOURCE_TYPE_MAIN_FRAME));
(details.resource_type == content::RESOURCE_TYPE_MAIN_FRAME),
details.http_response_code,
details.method,
details.referrer,
details.headers.get());
}
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(
@ -450,6 +586,33 @@ void WebContents::DidUpdateFaviconURL(
Emit("page-favicon-updated", unique_urls);
}
void WebContents::DevToolsFocused() {
Emit("devtools-focused");
}
void WebContents::DevToolsOpened() {
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
auto handle = WebContents::CreateFrom(
isolate(), managed_web_contents()->GetDevToolsWebContents());
devtools_web_contents_.Reset(isolate(), handle.ToV8());
// Inherit owner window in devtools.
if (owner_window())
handle->SetOwnerWindow(managed_web_contents()->GetDevToolsWebContents(),
owner_window());
Emit("devtools-opened");
}
void WebContents::DevToolsClosed() {
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
devtools_web_contents_.Reset();
Emit("devtools-closed");
}
bool WebContents::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(WebContents, message)
@ -462,11 +625,37 @@ bool WebContents::OnMessageReceived(const IPC::Message& message) {
return handled;
}
// There are three ways of destroying a webContents:
// 1. call webContents.destory();
// 2. garbage collection;
// 3. user closes the window of webContents;
// For webview only #1 will happen, for BrowserWindow both #1 and #3 may
// happen. The #2 should never happen for webContents, because webview is
// managed by GuestViewManager, and BrowserWindow's webContents is managed
// by api::Window.
// For #1, the destructor will do the cleanup work and we only need to make
// sure "destroyed" event is emitted. For #3, the content::WebContents will
// be destroyed on close, and WebContentsDestroyed would be called for it, so
// we need to make sure the api::WebContents is also deleted.
void WebContents::WebContentsDestroyed() {
// The RenderViewDeleted was not called when the WebContents is destroyed.
RenderViewDeleted(web_contents()->GetRenderViewHost());
Emit("destroyed");
// This event is only for internal use, which is emitted when WebContents is
// being destroyed.
Emit("will-destroy");
// Cleanup relationships with other parts.
RemoveFromWeakMap();
// 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("destroyed");
// Destroy the native class in next tick.
base::MessageLoop::current()->PostTask(FROM_HERE, GetDestroyClosure());
}
void WebContents::NavigationEntryCommitted(
@ -475,22 +664,6 @@ void WebContents::NavigationEntryCommitted(
details.is_in_page, details.did_replace_entry);
}
void 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::IsAlive() const {
return web_contents() != NULL;
}
int WebContents::GetID() const {
return web_contents()->GetRenderProcessHost()->GetID();
}
@ -511,12 +684,29 @@ void WebContents::LoadURL(const GURL& url, const mate::Dictionary& options) {
if (options.Get("userAgent", &user_agent))
SetUserAgent(user_agent);
std::string extra_headers;
if (options.Get("extraHeaders", &extra_headers))
params.extra_headers = extra_headers;
params.transition_type = ui::PAGE_TRANSITION_TYPED;
params.should_clear_history_list = true;
params.override_user_agent = content::NavigationController::UA_OVERRIDE_TRUE;
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();
}
base::string16 WebContents::GetTitle() const {
return web_contents()->GetTitle();
}
@ -533,10 +723,6 @@ void WebContents::Stop() {
web_contents()->Stop();
}
void WebContents::ReloadIgnoringCache() {
web_contents()->GetController().ReloadIgnoringCache(false);
}
void WebContents::GoBack() {
atom::AtomBrowserClient::SuppressRendererProcessRestartForOnce();
web_contents()->GetController().GoBack();
@ -560,8 +746,10 @@ void WebContents::SetUserAgent(const std::string& user_agent) {
web_contents()->SetUserAgentOverride(user_agent);
scoped_refptr<net::URLRequestContextGetter> getter =
web_contents()->GetBrowserContext()->GetRequestContext();
auto accept_lang = l10n_util::GetApplicationLocale("");
getter->GetNetworkTaskRunner()->PostTask(FROM_HERE,
base::Bind(&SetUserAgentInIO, getter, user_agent));
base::Bind(&SetUserAgentInIO, getter, accept_lang, user_agent));
}
std::string WebContents::GetUserAgent() {
@ -572,9 +760,11 @@ void WebContents::InsertCSS(const std::string& css) {
web_contents()->InsertCSS(css);
}
void WebContents::ExecuteJavaScript(const base::string16& code,
bool has_user_gesture) {
Send(new AtomViewMsg_ExecuteJavaScript(routing_id(), code, has_user_gesture));
bool WebContents::SavePage(const base::FilePath& full_file_path,
const content::SavePageType& save_type,
const SavePageHandler::SavePageCallback& callback) {
auto handler = new SavePageHandler(web_contents(), callback);
return handler->Handle(full_file_path, save_type);
}
void WebContents::OpenDevTools(mate::Arguments* args) {
@ -606,6 +796,28 @@ 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)
return;
Send(new ViewMsg_EnableDeviceEmulation(routing_id(), params));
}
void WebContents::DisableDeviceEmulation() {
if (type_ == REMOTE)
return;
Send(new ViewMsg_DisableDeviceEmulation(routing_id()));
}
void WebContents::ToggleDevTools() {
if (IsDevToolsOpened())
CloseDevTools();
@ -637,14 +849,6 @@ void WebContents::InspectServiceWorker() {
}
}
v8::Local<v8::Value> WebContents::Session(v8::Isolate* isolate) {
if (session_.IsEmpty()) {
auto handle = Session::CreateFrom(isolate, GetBrowserContext());
session_.Reset(isolate, handle.ToV8());
}
return v8::Local<v8::Value>::New(isolate, session_);
}
void WebContents::HasServiceWorker(
const base::Callback<void(bool)>& callback) {
auto context = GetServiceWorkerContext(web_contents());
@ -691,17 +895,19 @@ void WebContents::PrintToPDF(const base::DictionaryValue& setting,
PrintToPDF(setting, callback);
}
void WebContents::AddWorkSpace(const base::FilePath& path) {
void WebContents::AddWorkSpace(mate::Arguments* args,
const base::FilePath& path) {
if (path.empty()) {
node::ThrowError(isolate(), "path cannot be empty");
args->ThrowError("path cannot be empty");
return;
}
DevToolsAddFileSystem(path);
}
void WebContents::RemoveWorkSpace(const base::FilePath& path) {
void WebContents::RemoveWorkSpace(mate::Arguments* args,
const base::FilePath& path) {
if (path.empty()) {
node::ThrowError(isolate(), "path cannot be empty");
args->ThrowError("path cannot be empty");
return;
}
DevToolsRemoveFileSystem(path);
@ -751,6 +957,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();
}
@ -764,6 +989,56 @@ bool WebContents::SendIPCMessage(const base::string16& channel,
return Send(new AtomViewMsg_Message(routing_id(), channel, args));
}
void WebContents::SendInputEvent(v8::Isolate* isolate,
v8::Local<v8::Value> input_event) {
const auto view = web_contents()->GetRenderWidgetHostView();
if (!view)
return;
const auto host = view->GetRenderWidgetHost();
if (!host)
return;
int type = mate::GetWebInputEventType(isolate, input_event);
if (blink::WebInputEvent::isMouseEventType(type)) {
blink::WebMouseEvent mouse_event;
if (mate::ConvertFromV8(isolate, input_event, &mouse_event)) {
host->ForwardMouseEvent(mouse_event);
return;
}
} else if (blink::WebInputEvent::isKeyboardEventType(type)) {
content::NativeWebKeyboardEvent keyboard_event;
if (mate::ConvertFromV8(isolate, input_event, &keyboard_event)) {
host->ForwardKeyboardEvent(keyboard_event);
return;
}
} else if (type == blink::WebInputEvent::MouseWheel) {
blink::WebMouseWheelEvent mouse_wheel_event;
if (mate::ConvertFromV8(isolate, input_event, &mouse_wheel_event)) {
host->ForwardWheelEvent(mouse_wheel_event);
return;
}
}
isolate->ThrowException(v8::Exception::Error(mate::StringToV8(
isolate, "Invalid event object")));
}
void WebContents::BeginFrameSubscription(
const FrameSubscriber::FrameCaptureCallback& callback) {
const auto view = web_contents()->GetRenderWidgetHostView();
if (view) {
scoped_ptr<FrameSubscriber> frame_subscriber(new FrameSubscriber(
isolate(), view->GetVisibleViewportSize(), callback));
view->BeginFrameSubscription(frame_subscriber.Pass());
}
}
void WebContents::EndFrameSubscription() {
const auto view = web_contents()->GetRenderWidgetHostView();
if (view)
view->EndFrameSubscription();
}
void WebContents::SetSize(const SetSizeParams& params) {
if (guest_delegate_)
guest_delegate_->SetSize(params);
@ -778,69 +1053,108 @@ bool WebContents::IsGuest() const {
return type_ == WEB_VIEW;
}
mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
if (template_.IsEmpty())
template_.Reset(isolate, mate::ObjectTemplateBuilder(isolate)
.SetMethod("destroy", &WebContents::Destroy, true)
.SetMethod("isAlive", &WebContents::IsAlive, true)
.SetMethod("getId", &WebContents::GetID)
.SetMethod("equal", &WebContents::Equal)
.SetMethod("_loadUrl", &WebContents::LoadURL)
.SetMethod("getTitle", &WebContents::GetTitle)
.SetMethod("isLoading", &WebContents::IsLoading)
.SetMethod("isWaitingForResponse", &WebContents::IsWaitingForResponse)
.SetMethod("_stop", &WebContents::Stop)
.SetMethod("_reloadIgnoringCache", &WebContents::ReloadIgnoringCache)
.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("_executeJavaScript", &WebContents::ExecuteJavaScript)
.SetMethod("openDevTools", &WebContents::OpenDevTools)
.SetMethod("closeDevTools", &WebContents::CloseDevTools)
.SetMethod("isDevToolsOpened", &WebContents::IsDevToolsOpened)
.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("setSize", &WebContents::SetSize)
.SetMethod("setAllowTransparency", &WebContents::SetAllowTransparency)
.SetMethod("isGuest", &WebContents::IsGuest)
.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)
.Build());
return mate::ObjectTemplateBuilder(
isolate, v8::Local<v8::ObjectTemplate>::New(isolate, template_));
v8::Local<v8::Value> WebContents::GetWebPreferences(v8::Isolate* isolate) {
WebContentsPreferences* web_preferences =
WebContentsPreferences::FromWebContents(web_contents());
return mate::ConvertToV8(isolate, *web_preferences->web_preferences());
}
bool WebContents::IsDestroyed() const {
return !IsAlive();
v8::Local<v8::Value> WebContents::GetOwnerBrowserWindow() {
if (owner_window())
return Window::From(isolate(), owner_window());
else
return v8::Null(isolate());
}
v8::Local<v8::Value> WebContents::Session(v8::Isolate* isolate) {
return v8::Local<v8::Value>::New(isolate, session_);
}
v8::Local<v8::Value> WebContents::DevToolsWebContents(v8::Isolate* isolate) {
if (devtools_web_contents_.IsEmpty())
return v8::Null(isolate);
else
return v8::Local<v8::Value>::New(isolate, devtools_web_contents_);
}
v8::Local<v8::Value> WebContents::Debugger(v8::Isolate* isolate) {
if (debugger_.IsEmpty()) {
auto handle = atom::api::Debugger::Create(isolate, web_contents());
debugger_.Reset(isolate, handle.ToV8());
}
return v8::Local<v8::Value>::New(isolate, debugger_);
}
// static
void WebContents::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> 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("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)
.SetProperty("debugger", &WebContents::Debugger);
}
AtomBrowserContext* WebContents::GetBrowserContext() const {
@ -877,19 +1191,23 @@ mate::Handle<WebContents> WebContents::CreateFrom(
// static
mate::Handle<WebContents> WebContents::Create(
v8::Isolate* isolate, const mate::Dictionary& options) {
auto handle = mate::CreateHandle(isolate, new WebContents(options));
auto handle = mate::CreateHandle(isolate, new WebContents(isolate, options));
g_wrap_web_contents.Run(handle.ToV8());
return handle;
}
void SetWrapWebContents(const WrapWebContentsCallback& callback) {
g_wrap_web_contents = callback;
}
void ClearWrapWebContents() {
g_wrap_web_contents.Reset();
}
void SetWrapWebContents(const WrapWebContentsCallback& callback) {
g_wrap_web_contents = callback;
// Cleanup the wrapper on exit.
atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback(
base::Bind(ClearWrapWebContents));
}
} // namespace api
} // namespace atom
@ -903,7 +1221,6 @@ void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
mate::Dictionary dict(isolate, exports);
dict.SetMethod("create", &atom::api::WebContents::Create);
dict.SetMethod("_setWrapWebContents", &atom::api::SetWrapWebContents);
dict.SetMethod("_clearWrapWebContents", &atom::api::ClearWrapWebContents);
}
} // namespace

View file

@ -8,13 +8,19 @@
#include <string>
#include <vector>
#include "atom/browser/api/frame_subscriber.h"
#include "atom/browser/api/save_page_handler.h"
#include "atom/browser/api/trackable_object.h"
#include "atom/browser/common_web_contents_delegate.h"
#include "content/public/common/favicon_url.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/favicon_url.h"
#include "native_mate/handle.h"
#include "ui/gfx/image/image.h"
namespace blink {
struct WebDeviceEmulationParams;
}
namespace brightray {
class InspectableWebContents;
}
@ -48,11 +54,11 @@ class WebContents : public mate::TrackableObject<WebContents>,
static mate::Handle<WebContents> Create(
v8::Isolate* isolate, const mate::Dictionary& options);
void Destroy();
bool IsAlive() const;
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;
bool IsWaitingForResponse() const;
@ -65,15 +71,18 @@ class WebContents : public mate::TrackableObject<WebContents>,
void SetUserAgent(const std::string& user_agent);
std::string GetUserAgent();
void InsertCSS(const std::string& css);
void ExecuteJavaScript(const base::string16& code,
bool has_user_gesture);
bool SavePage(const base::FilePath& full_file_path,
const content::SavePageType& save_type,
const SavePageHandler::SavePageCallback& callback);
void OpenDevTools(mate::Arguments* args);
void CloseDevTools();
bool IsDevToolsOpened();
bool IsDevToolsFocused();
void ToggleDevTools();
void EnableDeviceEmulation(const blink::WebDeviceEmulationParams& params);
void DisableDeviceEmulation();
void InspectElement(int x, int y);
void InspectServiceWorker();
v8::Local<v8::Value> Session(v8::Isolate* isolate);
void HasServiceWorker(const base::Callback<void(bool)>&);
void UnregisterServiceWorker(const base::Callback<void(bool)>&);
void SetAudioMuted(bool muted);
@ -85,8 +94,8 @@ class WebContents : public mate::TrackableObject<WebContents>,
const PrintToPDFCallback& callback);
// DevTools workspace api.
void AddWorkSpace(const base::FilePath& path);
void RemoveWorkSpace(const base::FilePath& path);
void AddWorkSpace(mate::Arguments* args, const base::FilePath& path);
void RemoveWorkSpace(mate::Arguments* args, const base::FilePath& path);
// Editing commands.
void Undo();
@ -100,30 +109,50 @@ class WebContents : public mate::TrackableObject<WebContents>,
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();
void TabTraverse(bool reverse);
// Sending messages to browser.
// Send messages to browser.
bool SendIPCMessage(const base::string16& channel,
const base::ListValue& args);
// Send WebInputEvent to the page.
void SendInputEvent(v8::Isolate* isolate, v8::Local<v8::Value> input_event);
// Subscribe to the frame updates.
void BeginFrameSubscription(
const FrameSubscriber::FrameCaptureCallback& callback);
void EndFrameSubscription();
// Methods for creating <webview>.
void SetSize(const SetSizeParams& params);
void SetAllowTransparency(bool allow);
bool IsGuest() const;
// Returns the web preferences of current WebContents.
v8::Local<v8::Value> GetWebPreferences(v8::Isolate* isolate);
// Returns the owner window.
v8::Local<v8::Value> GetOwnerBrowserWindow();
// Properties.
v8::Local<v8::Value> Session(v8::Isolate* isolate);
v8::Local<v8::Value> DevToolsWebContents(v8::Isolate* isolate);
v8::Local<v8::Value> Debugger(v8::Isolate* isolate);
// mate::TrackableObject:
static void BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> prototype);
protected:
explicit WebContents(content::WebContents* web_contents);
explicit WebContents(const mate::Dictionary& options);
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,
@ -135,7 +164,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
int route_id,
int main_frame_route_id,
WindowContainerType window_container_type,
const base::string16& frame_name,
const std::string& frame_name,
const GURL& target_url,
const std::string& partition_id,
content::SessionStorageNamespace* session_storage_namespace) override;
@ -158,6 +187,14 @@ class WebContents : public mate::TrackableObject<WebContents>,
void ExitFullscreenModeForTab(content::WebContents* source) override;
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;
@ -170,11 +207,13 @@ class WebContents : public mate::TrackableObject<WebContents>,
void DidFailLoad(content::RenderFrameHost* render_frame_host,
const GURL& validated_url,
int error_code,
const base::string16& error_description) override;
const base::string16& error_description,
bool was_ignored_by_handler) override;
void DidFailProvisionalLoad(content::RenderFrameHost* render_frame_host,
const GURL& validated_url,
int error_code,
const base::string16& error_description) override;
const base::string16& error_description,
bool was_ignored_by_handler) override;
void DidStartLoading() override;
void DidStopLoading() override;
void DidGetResourceResponseStart(
@ -194,6 +233,14 @@ class WebContents : public mate::TrackableObject<WebContents>,
const std::vector<content::FaviconURL>& 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;
void DevToolsOpened() override;
void DevToolsClosed() override;
private:
enum Type {
@ -204,6 +251,10 @@ class WebContents : public mate::TrackableObject<WebContents>,
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);
@ -214,12 +265,17 @@ class WebContents : public mate::TrackableObject<WebContents>,
IPC::Message* message);
v8::Global<v8::Value> session_;
v8::Global<v8::Value> devtools_web_contents_;
v8::Global<v8::Value> debugger_;
scoped_ptr<WebViewGuestDelegate> guest_delegate_;
// The type of current WebContents.
Type type_;
// Request id used for findInPage request.
uint32 request_id_;
DISALLOW_COPY_AND_ASSIGN(WebContents);
};

View file

@ -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 <string>
#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<extensions::URLPattern> {
static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> 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<AtomNetworkDelegate::SimpleEvent type>
void WebRequest::SetSimpleListener(mate::Arguments* args) {
SetListener<AtomNetworkDelegate::SimpleListener>(
&AtomNetworkDelegate::SetSimpleListenerInIO, type, args);
}
template<AtomNetworkDelegate::ResponseEvent type>
void WebRequest::SetResponseListener(mate::Arguments* args) {
SetListener<AtomNetworkDelegate::ResponseListener>(
&AtomNetworkDelegate::SetResponseListenerInIO, type, args);
}
template<typename Listener, typename Method, typename Event>
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<v8::Value> 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> 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<v8::ObjectTemplate> 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

View file

@ -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<WebRequest> {
public:
static mate::Handle<WebRequest> Create(v8::Isolate* isolate,
AtomBrowserContext* browser_context);
// mate::TrackableObject:
static void BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> prototype);
protected:
explicit WebRequest(AtomBrowserContext* browser_context);
~WebRequest();
// C++ can not distinguish overloaded member function.
template<AtomNetworkDelegate::SimpleEvent type>
void SetSimpleListener(mate::Arguments* args);
template<AtomNetworkDelegate::ResponseEvent type>
void SetResponseListener(mate::Arguments* args);
template<typename Listener, typename Method, typename Event>
void SetListener(Method method, Event type, mate::Arguments* args);
private:
scoped_refptr<AtomBrowserContext> browser_context_;
DISALLOW_COPY_AND_ASSIGN(WebRequest);
};
} // namespace api
} // namespace atom
#endif // ATOM_BROWSER_API_ATOM_API_WEB_REQUEST_H_

View file

@ -3,13 +3,14 @@
// found in the LICENSE file.
#include "atom/browser/api/atom_api_web_contents.h"
#include "atom/browser/web_contents_preferences.h"
#include "atom/browser/web_view_manager.h"
#include "atom/common/native_mate_converters/gurl_converter.h"
#include "atom/common/native_mate_converters/value_converter.h"
#include "atom/common/node_includes.h"
#include "content/public/browser/browser_context.h"
#include "native_mate/dictionary.h"
#include "net/base/filename_util.h"
#include "atom/common/node_includes.h"
using atom::WebContentsPreferences;
namespace mate {
@ -25,28 +26,6 @@ struct Converter<content::WebContents*> {
}
};
template<>
struct Converter<atom::WebViewManager::WebViewInfo> {
static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val,
atom::WebViewManager::WebViewInfo* out) {
Dictionary options;
if (!ConvertFromV8(isolate, val, &options))
return false;
GURL preload_url;
if (!options.Get("preloadUrl", &preload_url))
return false;
if (!preload_url.is_empty() &&
!net::FileURLToFilePath(preload_url, &(out->preload_script)))
return false;
return options.Get("nodeIntegration", &(out->node_integration)) &&
options.Get("plugins", &(out->plugins)) &&
options.Get("disableWebSecurity", &(out->disable_web_security));
}
};
} // namespace mate
namespace {
@ -65,14 +44,13 @@ void AddGuest(int guest_instance_id,
int element_instance_id,
content::WebContents* embedder,
content::WebContents* guest_web_contents,
atom::WebViewManager::WebViewInfo info) {
const base::DictionaryValue& options) {
auto manager = GetWebViewManager(embedder);
if (manager) {
info.guest_instance_id = guest_instance_id;
info.embedder = embedder;
if (manager)
manager->AddGuest(guest_instance_id, element_instance_id, embedder,
guest_web_contents, info);
}
guest_web_contents);
WebContentsPreferences::FromWebContents(guest_web_contents)->Merge(options);
}
void RemoveGuest(content::WebContents* embedder, int guest_instance_id) {

View file

@ -3,32 +3,29 @@
// found in the LICENSE file.
#include "atom/browser/api/atom_api_window.h"
#include "atom/common/native_mate_converters/value_converter.h"
#include "atom/browser/api/atom_api_menu.h"
#include "atom/browser/api/atom_api_web_contents.h"
#include "atom/browser/browser.h"
#include "atom/browser/native_window.h"
#include "atom/common/node_includes.h"
#include "atom/common/options_switches.h"
#include "atom/common/event_types.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/gfx_converter.h"
#include "atom/common/native_mate_converters/gurl_converter.h"
#include "atom/common/native_mate_converters/image_converter.h"
#include "atom/common/native_mate_converters/string16_converter.h"
#include "atom/common/node_includes.h"
#include "atom/common/options_switches.h"
#include "content/public/browser/render_process_host.h"
#include "native_mate/constructor.h"
#include "native_mate/dictionary.h"
#include "ui/gfx/geometry/rect.h"
#include "v8/include/v8.h"
#if defined(OS_WIN)
#include "atom/browser/native_window_views.h"
#include "atom/browser/ui/win/taskbar_host.h"
#endif
#include "atom/common/node_includes.h"
#if defined(OS_WIN)
namespace mate {
@ -64,58 +61,126 @@ void OnCapturePageDone(
callback.Run(gfx::Image::CreateFrom1xBitmap(bitmap));
}
// Converts min-width to minWidth, returns false if no conversion is needed.
bool TranslateOldKey(const std::string& key, std::string* new_key) {
if (key.find('-') == std::string::npos)
return false;
new_key->reserve(key.size());
bool next_upper_case = false;
for (char c : key) {
if (c == '-') {
next_upper_case = true;
} else if (next_upper_case) {
new_key->push_back(base::ToUpperASCII(c));
next_upper_case = false;
} else {
new_key->push_back(c);
}
}
return true;
}
// Converts min-width to minWidth recursively in the dictionary.
void TranslateOldOptions(v8::Isolate* isolate, v8::Local<v8::Object> options) {
auto context = isolate->GetCurrentContext();
auto maybe_keys = options->GetOwnPropertyNames(context);
if (maybe_keys.IsEmpty())
return;
std::vector<std::string> keys;
if (!mate::ConvertFromV8(isolate, maybe_keys.ToLocalChecked(), &keys))
return;
mate::Dictionary dict(isolate, options);
for (const auto& key : keys) {
v8::Local<v8::Value> value;
if (!dict.Get(key, &value)) // Shouldn't happen, but guard it anyway.
continue;
// Go recursively.
v8::Local<v8::Object> sub_options;
if (mate::ConvertFromV8(isolate, value, &sub_options))
TranslateOldOptions(isolate, sub_options);
// Translate key.
std::string new_key;
if (TranslateOldKey(key, &new_key)) {
dict.Set(new_key, value);
dict.Delete(key);
}
}
}
// Converts binary data to Buffer.
v8::Local<v8::Value> ToBuffer(v8::Isolate* isolate, void* val, int size) {
auto buffer = node::Buffer::New(isolate, static_cast<char*>(val), size);
if (buffer.IsEmpty())
return v8::Null(isolate);
else
return buffer.ToLocalChecked();
}
} // namespace
Window::Window(v8::Isolate* isolate, const mate::Dictionary& options) {
// Be compatible with old style field names like min-width.
TranslateOldOptions(isolate, options.GetHandle());
// Use options.webPreferences to create WebContents.
mate::Dictionary web_preferences = mate::Dictionary::CreateEmpty(isolate);
options.Get(options::kWebPreferences, &web_preferences);
// Be compatible with old options which are now in web_preferences.
v8::Local<v8::Value> value;
if (options.Get(options::kNodeIntegration, &value))
web_preferences.Set(options::kNodeIntegration, value);
if (options.Get(options::kPreloadScript, &value))
web_preferences.Set(options::kPreloadScript, value);
if (options.Get(options::kZoomFactor, &value))
web_preferences.Set(options::kZoomFactor, value);
// Creates the WebContents used by BrowserWindow.
mate::Dictionary web_contents_options(isolate, v8::Object::New(isolate));
auto web_contents = WebContents::Create(isolate, web_contents_options);
auto web_contents = WebContents::Create(isolate, web_preferences);
web_contents_.Reset(isolate, web_contents.ToV8());
api_web_contents_ = web_contents.get();
// Keep a copy of the options for later use.
mate::Dictionary(isolate, web_contents->GetWrapper(isolate)).Set(
"browserWindowOptions", options);
// Creates BrowserWindow.
window_.reset(NativeWindow::Create(web_contents->managed_web_contents(),
options));
web_contents->SetOwnerWindow(window_.get());
window_->InitFromOptions(options);
window_->AddObserver(this);
AttachAsUserData(window_.get());
}
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) {
*prevent_default = Emit("close");
}
void Window::OnFrameRendered(scoped_ptr<uint8[]> rgb, const int size) {
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
auto data = node::Buffer::New(isolate(), reinterpret_cast<const char*>(rgb.get()), static_cast<size_t>(size));
Emit("frame-rendered", data, size);
}
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");
// Destroy the native class when window is closed.
base::MessageLoop::current()->PostTask(FROM_HERE, GetDestroyClosure());
}
void Window::OnWindowBlur() {
@ -162,6 +227,14 @@ void Window::OnWindowLeaveFullScreen() {
Emit("leave-full-screen");
}
void Window::OnWindowScrollTouchBegin() {
Emit("scroll-touch-begin");
}
void Window::OnWindowScrollTouchEnd() {
Emit("scroll-touch-end");
}
void Window::OnWindowEnterHtmlFullScreen() {
Emit("enter-html-full-screen");
}
@ -178,59 +251,45 @@ void Window::OnRendererResponsive() {
Emit("responsive");
}
void Window::OnDevToolsFocus() {
Emit("devtools-focused");
}
void Window::OnDevToolsOpened() {
Emit("devtools-opened");
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
auto handle = WebContents::CreateFrom(
isolate(), api_web_contents_->GetDevToolsWebContents());
devtools_web_contents_.Reset(isolate(), handle.ToV8());
}
void Window::OnDevToolsClosed() {
Emit("devtools-closed");
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
devtools_web_contents_.Reset();
}
void Window::OnExecuteWindowsCommand(const std::string& command_name) {
Emit("app-command", command_name);
}
#if defined(OS_WIN)
void Window::OnWindowMessage(UINT message, WPARAM w_param, LPARAM l_param) {
if (IsWindowMessageHooked(message)) {
messages_callback_map_[message].Run(
ToBuffer(isolate(), static_cast<void*>(&w_param), sizeof(WPARAM)),
ToBuffer(isolate(), static_cast<void*>(&l_param), sizeof(LPARAM)));
}
}
#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()) {
node::ThrowError(isolate,
"Cannot create BrowserWindow before app 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() {
window_->CloseContents(nullptr);
}
void Window::Close() {
window_->Close();
}
bool Window::IsClosed() {
return window_->IsClosed();
}
void Window::Focus() {
window_->Focus(true);
}
@ -287,16 +346,20 @@ bool Window::IsFullscreen() {
return window_->IsFullscreen();
}
void Window::SetBounds(const gfx::Rect& bounds) {
window_->SetBounds(bounds);
void Window::SetBounds(const gfx::Rect& bounds, mate::Arguments* args) {
bool animate = false;
args->GetNext(&animate);
window_->SetBounds(bounds, animate);
}
gfx::Rect Window::GetBounds() {
return window_->GetBounds();
}
void Window::SetSize(int width, int height) {
window_->SetSize(gfx::Size(width, height));
void Window::SetSize(int width, int height, mate::Arguments* args) {
bool animate = false;
args->GetNext(&animate);
window_->SetSize(gfx::Size(width, height), animate);
}
std::vector<int> Window::GetSize() {
@ -307,8 +370,10 @@ std::vector<int> Window::GetSize() {
return result;
}
void Window::SetContentSize(int width, int height) {
window_->SetContentSize(gfx::Size(width, height));
void Window::SetContentSize(int width, int height, mate::Arguments* args) {
bool animate = false;
args->GetNext(&animate);
window_->SetContentSize(gfx::Size(width, height), animate);
}
std::vector<int> Window::GetContentSize() {
@ -351,6 +416,46 @@ bool Window::IsResizable() {
return window_->IsResizable();
}
void Window::SetMovable(bool movable) {
window_->SetMovable(movable);
}
bool Window::IsMovable() {
return window_->IsMovable();
}
void Window::SetMinimizable(bool minimizable) {
window_->SetMinimizable(minimizable);
}
bool Window::IsMinimizable() {
return window_->IsMinimizable();
}
void Window::SetMaximizable(bool maximizable) {
window_->SetMaximizable(maximizable);
}
bool Window::IsMaximizable() {
return window_->IsMaximizable();
}
void Window::SetFullScreenable(bool fullscreenable) {
window_->SetFullScreenable(fullscreenable);
}
bool Window::IsFullScreenable() {
return window_->IsFullScreenable();
}
void Window::SetClosable(bool closable) {
window_->SetClosable(closable);
}
bool Window::IsClosable() {
return window_->IsClosable();
}
void Window::SetAlwaysOnTop(bool top) {
window_->SetAlwaysOnTop(top);
}
@ -363,8 +468,10 @@ void Window::Center() {
window_->Center();
}
void Window::SetPosition(int x, int y) {
window_->SetPosition(gfx::Point(x, y));
void Window::SetPosition(int x, int y, mate::Arguments* args) {
bool animate = false;
args->GetNext(&animate);
window_->SetPosition(gfx::Point(x, y), animate);
}
std::vector<int> Window::GetPosition() {
@ -399,6 +506,18 @@ bool Window::IsKiosk() {
return window_->IsKiosk();
}
void Window::SetBackgroundColor(const std::string& color_name) {
window_->SetBackgroundColor(color_name);
}
void Window::SetHasShadow(bool has_shadow) {
window_->SetHasShadow(has_shadow);
}
bool Window::HasShadow() {
return window_->HasShadow();
}
void Window::FocusOnWebView() {
window_->FocusOnWebView();
}
@ -427,6 +546,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<void(const gfx::Image&)> callback;
@ -442,14 +565,6 @@ void Window::CapturePage(mate::Arguments* args) {
rect, base::Bind(&OnCapturePageDone, args->isolate(), callback));
}
void Window::BeginFrameSubscription() {
window_->SetFrameSubscription(true);
}
void Window::EndFrameSubscription() {
window_->SetFrameSubscription(false);
}
void Window::SetProgressBar(double progress) {
window_->SetProgressBar(progress);
}
@ -506,6 +621,29 @@ bool Window::IsMenuBarVisible() {
return window_->IsMenuBarVisible();
}
#if defined(OS_WIN)
bool Window::HookWindowMessage(UINT message,
const MessageCallback& callback) {
messages_callback_map_[message] = callback;
return true;
}
void Window::UnhookWindowMessage(UINT message) {
if (!ContainsKey(messages_callback_map_, message))
return;
messages_callback_map_.erase(message);
}
bool Window::IsWindowMessageHooked(UINT message) {
return ContainsKey(messages_callback_map_, message);
}
void Window::UnhookAllWindowMessages() {
messages_callback_map_.clear();
}
#endif
#if defined(OS_MACOSX)
void Window::ShowDefinitionForSelection() {
window_->ShowDefinitionForSelection();
@ -518,6 +656,12 @@ void Window::SetAspectRatio(double aspect_ratio, mate::Arguments* args) {
window_->SetAspectRatio(aspect_ratio, extra_size);
}
v8::Local<v8::Value> Window::GetNativeWindowHandle() {
gfx::AcceleratedWidget handle = window_->GetAcceleratedWidget();
return ToBuffer(
isolate(), static_cast<void*>(&handle), sizeof(gfx::AcceleratedWidget));
}
void Window::SetVisibleOnAllWorkspaces(bool visible) {
return window_->SetVisibleOnAllWorkspaces(visible);
}
@ -526,127 +670,6 @@ bool Window::IsVisibleOnAllWorkspaces() {
return window_->IsVisibleOnAllWorkspaces();
}
void Window::SendKeyboardEvent(v8::Isolate* isolate, const mate::Dictionary& data){
auto type = blink::WebInputEvent::Type::Char;
int modifiers = 0;
int keycode = 0;
int native = 0;
std::string type_str = "";
mate::Dictionary modifier_list = mate::Dictionary::CreateEmpty(isolate);
data.Get(switches::kEventType, &type_str);
data.Get(switches::kModifiers, &modifier_list);
data.Get(switches::kKeyCode, &keycode);
data.Get(switches::kNativeKeyCode, &native);
if(type_str.compare(event_types::kKeyDown) == 0){
type = blink::WebInputEvent::Type::KeyDown;
}else if(type_str.compare(event_types::kKeyUp) == 0){
type = blink::WebInputEvent::Type::KeyUp;
}else if(type_str.compare(event_types::kChar) == 0){
type = blink::WebInputEvent::Type::Char;
}
std::map<std::string, bool> modifier_types;
modifier_types[event_types::kModifierIsKeyPad] = false;
modifier_types[event_types::kModifierIsAutoRepeat] = false;
modifier_types[event_types::kModifierIsLeft] = false;
modifier_types[event_types::kModifierIsRight] = false;
modifier_types[event_types::kModifierShiftKey] = false;
modifier_types[event_types::kModifierControlKey] = false;
modifier_types[event_types::kModifierAltKey] = false;
modifier_types[event_types::kModifierMetaKey] = false;
modifier_types[event_types::kModifierCapsLockOn] = false;
modifier_types[event_types::kModifierNumLockOn] = false;
for(std::map<std::string, bool>::iterator it = modifier_types.begin(); it != modifier_types.end(); ++it){
modifier_list.Get(it->first,&(it->second));
if(it->second) modifiers = modifiers & event_types::modifierStrToWebModifier(it->first);
}
window_->SendKeyboardEvent(type, modifiers, keycode, native);
}
void Window::SendMouseEvent(v8::Isolate* isolate, const mate::Dictionary& data){
int x, y, movementX, movementY, clickCount;
std::string type_str = "";
std::string button_str = "";
mate::Dictionary modifier_list = mate::Dictionary::CreateEmpty(isolate);
blink::WebInputEvent::Type type = blink::WebInputEvent::Type::MouseMove;
blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::ButtonNone;
int modifiers = 0;
data.Get(switches::kEventType, &type_str);
data.Get(switches::kMouseEventButton, &button_str);
data.Get(switches::kModifiers, &modifier_list);
if(type_str.compare(event_types::kMouseDown) == 0){
type = blink::WebInputEvent::Type::MouseDown;
}else if(type_str.compare(event_types::kMouseUp) == 0){
type = blink::WebInputEvent::Type::MouseUp;
}else if(type_str.compare(event_types::kMouseMove) == 0){
type = blink::WebInputEvent::Type::MouseMove;
}else if(type_str.compare(event_types::kMouseEnter) == 0){
type = blink::WebInputEvent::Type::MouseEnter;
}else if(type_str.compare(event_types::kMouseLeave) == 0){
type = blink::WebInputEvent::Type::MouseLeave;
}else if(type_str.compare(event_types::kContextMenu) == 0){
type = blink::WebInputEvent::Type::ContextMenu;
}else if(type_str.compare(event_types::kMouseWheel) == 0){
type = blink::WebInputEvent::Type::MouseWheel;
}
std::map<std::string, bool> modifier_types;
modifier_types[event_types::kMouseLeftButton] = false;
modifier_types[event_types::kMouseRightButton] = false;
modifier_types[event_types::kMouseMiddleButton] = false;
modifier_types[event_types::kModifierLeftButtonDown] = false;
modifier_types[event_types::kModifierMiddleButtonDown] = false;
modifier_types[event_types::kModifierRightButtonDown] = false;
modifier_types[event_types::kModifierShiftKey] = false;
modifier_types[event_types::kModifierControlKey] = false;
modifier_types[event_types::kModifierAltKey] = false;
modifier_types[event_types::kModifierMetaKey] = false;
modifier_types[event_types::kModifierCapsLockOn] = false;
modifier_types[event_types::kModifierNumLockOn] = false;
for(std::map<std::string, bool>::iterator it = modifier_types.begin(); it != modifier_types.end(); ++it){
modifier_list.Get(it->first,&(it->second));
if(it->second) modifiers = modifiers & event_types::modifierStrToWebModifier(it->first);
}
if(type == blink::WebInputEvent::Type::MouseWheel){
bool precise = true;
x = 0;
y = 0;
data.Get(switches::kX, &x);
data.Get(switches::kY, &y);
data.Get(switches::kMouseWheelPrecise, &precise);
window_->SendMouseWheelEvent(modifiers, x, y, precise);
}else{
if (data.Get(switches::kX, &x) && data.Get(switches::kY, &y)) {
if(!data.Get(switches::kMovementX, &movementX)){
movementX = 0;
}
if(!data.Get(switches::kMovementY, &movementY)){
movementY = 0;
}
if(!data.Get(switches::kClickCount, &clickCount)){
clickCount = 0;
}
window_->SendMouseEvent(type, modifiers, button, x, y, movementX, movementY, clickCount);
}
}
}
int32_t Window::ID() const {
return weak_map_id();
}
@ -658,20 +681,12 @@ v8::Local<v8::Value> Window::WebContents(v8::Isolate* isolate) {
return v8::Local<v8::Value>::New(isolate, web_contents_);
}
v8::Local<v8::Value> Window::DevToolsWebContents(v8::Isolate* isolate) {
if (devtools_web_contents_.IsEmpty())
return v8::Null(isolate);
else
return v8::Local<v8::Value>::New(isolate, devtools_web_contents_);
}
// static
void Window::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> prototype) {
mate::ObjectTemplateBuilder(isolate, prototype)
.SetMethod("destroy", &Window::Destroy, true)
.MakeDestroyable()
.SetMethod("close", &Window::Close)
.SetMethod("isClosed", &Window::IsClosed)
.SetMethod("focus", &Window::Focus)
.SetMethod("isFocused", &Window::IsFocused)
.SetMethod("show", &Window::Show)
@ -687,6 +702,7 @@ void Window::BuildPrototype(v8::Isolate* isolate,
.SetMethod("setFullScreen", &Window::SetFullScreen)
.SetMethod("isFullScreen", &Window::IsFullscreen)
.SetMethod("setAspectRatio", &Window::SetAspectRatio)
.SetMethod("getNativeWindowHandle", &Window::GetNativeWindowHandle)
.SetMethod("getBounds", &Window::GetBounds)
.SetMethod("setBounds", &Window::SetBounds)
.SetMethod("getSize", &Window::GetSize)
@ -699,6 +715,16 @@ void Window::BuildPrototype(v8::Isolate* isolate,
.SetMethod("getMaximumSize", &Window::GetMaximumSize)
.SetMethod("setResizable", &Window::SetResizable)
.SetMethod("isResizable", &Window::IsResizable)
.SetMethod("setMovable", &Window::SetMovable)
.SetMethod("isMovable", &Window::IsMovable)
.SetMethod("setMinimizable", &Window::SetMinimizable)
.SetMethod("isMinimizable", &Window::IsMinimizable)
.SetMethod("setMaximizable", &Window::SetMaximizable)
.SetMethod("isMaximizable", &Window::IsMaximizable)
.SetMethod("setFullScreenable", &Window::SetFullScreenable)
.SetMethod("isFullScreenable", &Window::IsFullScreenable)
.SetMethod("setClosable", &Window::SetClosable)
.SetMethod("isClosable", &Window::IsClosable)
.SetMethod("setAlwaysOnTop", &Window::SetAlwaysOnTop)
.SetMethod("isAlwaysOnTop", &Window::IsAlwaysOnTop)
.SetMethod("center", &Window::Center)
@ -710,10 +736,14 @@ void Window::BuildPrototype(v8::Isolate* isolate,
.SetMethod("setSkipTaskbar", &Window::SetSkipTaskbar)
.SetMethod("setKiosk", &Window::SetKiosk)
.SetMethod("isKiosk", &Window::IsKiosk)
.SetMethod("setBackgroundColor", &Window::SetBackgroundColor)
.SetMethod("setHasShadow", &Window::SetHasShadow)
.SetMethod("hasShadow", &Window::HasShadow)
.SetMethod("setRepresentedFilename", &Window::SetRepresentedFilename)
.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)
@ -730,17 +760,28 @@ void Window::BuildPrototype(v8::Isolate* isolate,
&Window::SetVisibleOnAllWorkspaces)
.SetMethod("isVisibleOnAllWorkspaces",
&Window::IsVisibleOnAllWorkspaces)
.SetMethod("sendMouseEvent", &Window::SendMouseEvent)
.SetMethod("sendKeyboardEvent", &Window::SendKeyboardEvent)
.SetMethod("beginFrameSubscription", &Window::BeginFrameSubscription)
.SetMethod("endFrameSubscription", &Window::EndFrameSubscription)
#if defined(OS_WIN)
.SetMethod("hookWindowMessage", &Window::HookWindowMessage)
.SetMethod("isWindowMessageHooked", &Window::IsWindowMessageHooked)
.SetMethod("unhookWindowMessage", &Window::UnhookWindowMessage)
.SetMethod("unhookAllWindowMessages", &Window::UnhookAllWindowMessages)
#endif
#if defined(OS_MACOSX)
.SetMethod("showDefinitionForSelection",
&Window::ShowDefinitionForSelection)
#endif
.SetProperty("id", &Window::ID, true)
.SetProperty("webContents", &Window::WebContents, true)
.SetProperty("devToolsWebContents", &Window::DevToolsWebContents, true);
.SetProperty("id", &Window::ID)
.SetProperty("webContents", &Window::WebContents);
}
// static
v8::Local<v8::Value> Window::From(v8::Isolate* isolate,
NativeWindow* native_window) {
auto existing = TrackableObject::FromWrappedClass(isolate, native_window);
if (existing)
return existing->GetWrapper(isolate);
else
return v8::Null(isolate);
}
} // namespace api

View file

@ -5,6 +5,7 @@
#ifndef ATOM_BROWSER_API_ATOM_API_WINDOW_H_
#define ATOM_BROWSER_API_ATOM_API_WINDOW_H_
#include <map>
#include <string>
#include <vector>
@ -37,12 +38,15 @@ class WebContents;
class Window : public mate::TrackableObject<Window>,
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<v8::ObjectTemplate> prototype);
// Returns the BrowserWindow object from |native_window|.
static v8::Local<v8::Value> From(v8::Isolate* isolate,
NativeWindow* native_window);
NativeWindow* window() const { return window_.get(); }
protected:
@ -50,10 +54,7 @@ class Window : public mate::TrackableObject<Window>,
virtual ~Window();
// NativeWindowObserver:
void OnPageTitleUpdated(bool* prevent_default,
const std::string& title) override;
void WillCloseWindow(bool* prevent_default) override;
void OnFrameRendered(scoped_ptr<uint8[]> rgb, const int size) override;
void OnWindowClosed() override;
void OnWindowBlur() override;
void OnWindowFocus() override;
@ -64,25 +65,23 @@ class Window : public mate::TrackableObject<Window>,
void OnWindowResize() override;
void OnWindowMove() override;
void OnWindowMoved() override;
void OnWindowScrollTouchBegin() override;
void OnWindowScrollTouchEnd() override;
void OnWindowEnterFullScreen() override;
void OnWindowLeaveFullScreen() override;
void OnWindowEnterHtmlFullScreen() override;
void OnWindowLeaveHtmlFullScreen() override;
void OnRendererUnresponsive() override;
void OnRendererResponsive() override;
void OnDevToolsFocus() override;
void OnDevToolsOpened() override;
void OnDevToolsClosed() override;
void OnExecuteWindowsCommand(const std::string& command_name) override;
// mate::Wrappable:
bool IsDestroyed() const override;
#if defined(OS_WIN)
void OnWindowMessage(UINT message, WPARAM w_param, LPARAM l_param) override;
#endif
private:
// APIs for NativeWindow.
void Destroy();
void Close();
bool IsClosed();
void Focus();
bool IsFocused();
void Show();
@ -97,11 +96,11 @@ class Window : public mate::TrackableObject<Window>,
bool IsMinimized();
void SetFullScreen(bool fullscreen);
bool IsFullscreen();
void SetBounds(const gfx::Rect& bounds);
void SetBounds(const gfx::Rect& bounds, mate::Arguments* args);
gfx::Rect GetBounds();
void SetSize(int width, int height);
void SetSize(int width, int height, mate::Arguments* args);
std::vector<int> GetSize();
void SetContentSize(int width, int height);
void SetContentSize(int width, int height, mate::Arguments* args);
std::vector<int> GetContentSize();
void SetMinimumSize(int width, int height);
std::vector<int> GetMinimumSize();
@ -109,10 +108,20 @@ class Window : public mate::TrackableObject<Window>,
std::vector<int> GetMaximumSize();
void SetResizable(bool resizable);
bool IsResizable();
void SetMovable(bool movable);
bool IsMovable();
void SetMinimizable(bool minimizable);
bool IsMinimizable();
void SetMaximizable(bool maximizable);
bool IsMaximizable();
void SetFullScreenable(bool fullscreenable);
bool IsFullScreenable();
void SetClosable(bool closable);
bool IsClosable();
void SetAlwaysOnTop(bool top);
bool IsAlwaysOnTop();
void Center();
void SetPosition(int x, int y);
void SetPosition(int x, int y, mate::Arguments* args);
std::vector<int> GetPosition();
void SetTitle(const std::string& title);
std::string GetTitle();
@ -120,6 +129,9 @@ class Window : public mate::TrackableObject<Window>,
void SetSkipTaskbar(bool skip);
void SetKiosk(bool kiosk);
bool IsKiosk();
void SetBackgroundColor(const std::string& color_name);
void SetHasShadow(bool has_shadow);
bool HasShadow();
void FocusOnWebView();
void BlurWebView();
bool IsWebViewFocused();
@ -127,6 +139,7 @@ class Window : public mate::TrackableObject<Window>,
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,
@ -138,11 +151,17 @@ class Window : public mate::TrackableObject<Window>,
void SetMenuBarVisibility(bool visible);
bool IsMenuBarVisible();
void SetAspectRatio(double aspect_ratio, mate::Arguments* args);
v8::Local<v8::Value> GetNativeWindowHandle();
void SendKeyboardEvent(v8::Isolate* isolate, const mate::Dictionary& data);
void SendMouseEvent(v8::Isolate* isolate, const mate::Dictionary& data);
void BeginFrameSubscription();
void EndFrameSubscription();
#if defined(OS_WIN)
typedef base::Callback<void(v8::Local<v8::Value>,
v8::Local<v8::Value>)> MessageCallback;
bool HookWindowMessage(UINT message, const MessageCallback& callback);
bool IsWindowMessageHooked(UINT message);
void UnhookWindowMessage(UINT message);
void UnhookAllWindowMessages();
#endif
#if defined(OS_MACOSX)
void ShowDefinitionForSelection();
@ -153,10 +172,13 @@ class Window : public mate::TrackableObject<Window>,
int32_t ID() const;
v8::Local<v8::Value> WebContents(v8::Isolate* isolate);
v8::Local<v8::Value> DevToolsWebContents(v8::Isolate* isolate);
#if defined(OS_WIN)
typedef std::map<UINT, MessageCallback> MessageCallbackMap;
MessageCallbackMap messages_callback_map_;
#endif
v8::Global<v8::Value> web_contents_;
v8::Global<v8::Value> devtools_web_contents_;
v8::Global<v8::Value> menu_;
api::WebContents* api_web_contents_;

View file

@ -56,7 +56,7 @@ v8::Local<v8::Object> EventEmitter::CreateJSEvent(
v8::Local<v8::Object> EventEmitter::CreateCustomEvent(
v8::Isolate* isolate, v8::Local<v8::Object> custom_event) {
v8::Local<v8::Object> event = CreateEventObject(isolate);
event->SetPrototype(custom_event->CreationContext(), custom_event);
(void)event->SetPrototype(custom_event->CreationContext(), custom_event);
mate::Dictionary(isolate, event).Set("sender", GetWrapper(isolate));
return event;
}

View file

@ -45,6 +45,8 @@ class EventEmitter : public Wrappable {
content::WebContents* sender,
IPC::Message* message,
const Args&... args) {
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
v8::Local<v8::Object> event = CreateJSEvent(isolate(), sender, message);
return EmitWithEvent(name, event, args...);
}

View file

@ -0,0 +1,66 @@
// 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/frame_subscriber.h"
#include "atom/common/node_includes.h"
#include "base/bind.h"
#include "media/base/video_frame.h"
#include "media/base/yuv_convert.h"
namespace atom {
namespace api {
FrameSubscriber::FrameSubscriber(v8::Isolate* isolate,
const gfx::Size& size,
const FrameCaptureCallback& callback)
: isolate_(isolate), size_(size), callback_(callback) {
}
bool FrameSubscriber::ShouldCaptureFrame(
const gfx::Rect& damage_rect,
base::TimeTicks present_time,
scoped_refptr<media::VideoFrame>* storage,
DeliverFrameCallback* callback) {
*storage = media::VideoFrame::CreateFrame(
media::PIXEL_FORMAT_YV12,
size_, gfx::Rect(size_), size_, base::TimeDelta());
*callback = base::Bind(&FrameSubscriber::OnFrameDelivered,
base::Unretained(this), *storage);
return true;
}
void FrameSubscriber::OnFrameDelivered(
scoped_refptr<media::VideoFrame> frame, base::TimeTicks, bool result) {
if (!result)
return;
v8::Locker locker(isolate_);
v8::HandleScope handle_scope(isolate_);
gfx::Rect rect = frame->visible_rect();
size_t rgb_arr_size = rect.width() * rect.height() * 4;
v8::MaybeLocal<v8::Object> buffer = node::Buffer::New(isolate_, rgb_arr_size);
if (buffer.IsEmpty())
return;
// Convert a frame of YUV to 32 bit ARGB.
media::ConvertYUVToRGB32(frame->data(media::VideoFrame::kYPlane),
frame->data(media::VideoFrame::kUPlane),
frame->data(media::VideoFrame::kVPlane),
reinterpret_cast<uint8*>(
node::Buffer::Data(buffer.ToLocalChecked())),
rect.width(), rect.height(),
frame->stride(media::VideoFrame::kYPlane),
frame->stride(media::VideoFrame::kUVPlane),
rect.width() * 4,
media::YV12);
callback_.Run(buffer.ToLocalChecked());
}
} // namespace api
} // namespace atom

View file

@ -0,0 +1,45 @@
// 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_FRAME_SUBSCRIBER_H_
#define ATOM_BROWSER_API_FRAME_SUBSCRIBER_H_
#include "base/callback.h"
#include "content/public/browser/render_widget_host_view_frame_subscriber.h"
#include "ui/gfx/geometry/size.h"
#include "v8/include/v8.h"
namespace atom {
namespace api {
class FrameSubscriber : public content::RenderWidgetHostViewFrameSubscriber {
public:
using FrameCaptureCallback = base::Callback<void(v8::Local<v8::Value>)>;
FrameSubscriber(v8::Isolate* isolate,
const gfx::Size& size,
const FrameCaptureCallback& callback);
bool ShouldCaptureFrame(const gfx::Rect& damage_rect,
base::TimeTicks present_time,
scoped_refptr<media::VideoFrame>* storage,
DeliverFrameCallback* callback) override;
private:
void OnFrameDelivered(
scoped_refptr<media::VideoFrame> frame, base::TimeTicks, bool);
v8::Isolate* isolate_;
gfx::Size size_;
FrameCaptureCallback callback_;
DISALLOW_COPY_AND_ASSIGN(FrameSubscriber);
};
} // namespace api
} // namespace atom
#endif // ATOM_BROWSER_API_FRAME_SUBSCRIBER_H_

View file

@ -1,45 +0,0 @@
EventEmitter = require('events').EventEmitter
bindings = process.atomBinding 'app'
app = bindings.app
app.__proto__ = EventEmitter.prototype
app.setApplicationMenu = (menu) ->
require('menu').setApplicationMenu menu
app.getApplicationMenu = ->
require('menu').getApplicationMenu()
app.commandLine =
appendSwitch: bindings.appendSwitch,
appendArgument: bindings.appendArgument
if process.platform is 'darwin'
app.dock =
bounce: (type='informational') -> bindings.dockBounce type
cancelBounce: bindings.dockCancelBounce
setBadge: bindings.dockSetBadgeText
getBadge: bindings.dockGetBadgeText
hide: bindings.dockHide
show: bindings.dockShow
setMenu: bindings.dockSetMenu
appPath = null
app.setAppPath = (path) ->
appPath = path
app.getAppPath = ->
appPath
# Be compatible with old API.
app.once 'ready', -> @emit 'finish-launching'
app.terminate = app.quit
app.exit = process.exit
app.getHomeDir = -> @getPath 'home'
app.getDataPath = -> @getPath 'userData'
app.setDataPath = (path) -> @setPath 'userData', path
app.resolveProxy = -> @defaultSession.resolveProxy.apply @defaultSession, arguments
# Only one App object pemitted.
module.exports = app

123
atom/browser/api/lib/app.js Normal file
View file

@ -0,0 +1,123 @@
const deprecate = require('electron').deprecate;
const session = require('electron').session;
const Menu = require('electron').Menu;
const EventEmitter = require('events').EventEmitter;
const bindings = process.atomBinding('app');
const downloadItemBindings = process.atomBinding('download_item');
const app = bindings.app;
var slice = [].slice;
app.__proto__ = EventEmitter.prototype;
app.setApplicationMenu = function(menu) {
return Menu.setApplicationMenu(menu);
};
app.getApplicationMenu = function() {
return Menu.getApplicationMenu();
};
app.commandLine = {
appendSwitch: bindings.appendSwitch,
appendArgument: bindings.appendArgument
};
if (process.platform === 'darwin') {
app.dock = {
bounce: function(type) {
if (type == null) {
type = 'informational';
}
return bindings.dockBounce(type);
},
cancelBounce: bindings.dockCancelBounce,
setBadge: bindings.dockSetBadgeText,
getBadge: bindings.dockGetBadgeText,
hide: bindings.dockHide,
show: bindings.dockShow,
setMenu: bindings.dockSetMenu,
setIcon: bindings.dockSetIcon
};
}
var appPath = null;
app.setAppPath = function(path) {
return appPath = path;
};
app.getAppPath = function() {
return appPath;
};
// Routes the events to webContents.
var ref1 = ['login', 'certificate-error', 'select-client-certificate'];
var fn = function(name) {
return app.on(name, function() {
var args, event, webContents;
event = arguments[0], webContents = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
return webContents.emit.apply(webContents, [name, event].concat(slice.call(args)));
});
};
var i, len;
for (i = 0, len = ref1.length; i < len; i++) {
fn(ref1[i]);
}
// Deprecated.
app.getHomeDir = deprecate('app.getHomeDir', 'app.getPath', function() {
return this.getPath('home');
});
app.getDataPath = deprecate('app.getDataPath', 'app.getPath', function() {
return this.getPath('userData');
});
app.setDataPath = deprecate('app.setDataPath', 'app.setPath', function(path) {
return this.setPath('userData', path);
});
app.resolveProxy = deprecate('app.resolveProxy', 'session.defaultSession.resolveProxy', function(url, callback) {
return session.defaultSession.resolveProxy(url, callback);
});
deprecate.rename(app, 'terminate', 'quit');
deprecate.event(app, 'finish-launching', 'ready', function() {
// give default app a chance to setup default menu.
return setImmediate((function(_this) {
return function() {
return _this.emit('finish-launching');
};
})(this));
});
deprecate.event(app, 'activate-with-no-open-windows', 'activate', function(event, hasVisibleWindows) {
if (!hasVisibleWindows) {
return this.emit('activate-with-no-open-windows', event);
}
});
deprecate.event(app, 'select-certificate', 'select-client-certificate');
// Wrappers for native classes.
var wrapDownloadItem = function(downloadItem) {
// downloadItem is an EventEmitter.
downloadItem.__proto__ = EventEmitter.prototype;
// Deprecated.
deprecate.property(downloadItem, 'url', 'getURL');
deprecate.property(downloadItem, 'filename', 'getFilename');
deprecate.property(downloadItem, 'mimeType', 'getMimeType');
return deprecate.rename(downloadItem, 'getUrl', 'getURL');
};
downloadItemBindings._setWrapDownloadItem(wrapDownloadItem);
// Only one App object pemitted.
module.exports = app;

View file

@ -1,6 +0,0 @@
module.exports =
browserMainParts:
preMainMessageLoopRun: ->
setImmediate ->
module.exports.browserMainParts.preMainMessageLoopRun()

View file

@ -1,24 +0,0 @@
autoUpdater = process.atomBinding('auto_updater').autoUpdater
EventEmitter = require('events').EventEmitter
autoUpdater.__proto__ = EventEmitter.prototype
autoUpdater.on 'update-downloaded-raw', (args...) ->
args[3] = new Date(args[3]) # releaseDate
@emit 'update-downloaded', args..., => @quitAndInstall()
autoUpdater.quitAndInstall = ->
# If we don't have any window then quitAndInstall immediately.
BrowserWindow = require 'browser-window'
windows = BrowserWindow.getAllWindows()
if windows.length is 0
@_quitAndInstall()
return
# Do the restart after all windows have been closed.
app = require 'app'
app.removeAllListeners 'window-all-closed'
app.once 'window-all-closed', @_quitAndInstall.bind(this)
win.close() for win in windows
module.exports = autoUpdater

View file

@ -0,0 +1,7 @@
const deprecate = require('electron').deprecate;
const autoUpdater = process.platform === 'win32' ? require('./auto-updater/auto-updater-win') : require('./auto-updater/auto-updater-native');
// Deprecated.
deprecate.rename(autoUpdater, 'setFeedUrl', 'setFeedURL');
module.exports = autoUpdater;

View file

@ -0,0 +1,6 @@
const EventEmitter = require('events').EventEmitter;
const autoUpdater = process.atomBinding('auto_updater').autoUpdater;
autoUpdater.__proto__ = EventEmitter.prototype;
module.exports = autoUpdater;

View file

@ -0,0 +1,62 @@
'use strict';
const app = require('electron').app;
const EventEmitter = require('events').EventEmitter;
const squirrelUpdate = require('./squirrel-update-win');
class AutoUpdater extends EventEmitter {
constructor() {
super();
}
quitAndInstall() {
squirrelUpdate.processStart();
return app.quit();
}
setFeedURL(updateURL) {
return this.updateURL = updateURL;
}
checkForUpdates() {
if (!this.updateURL) {
return this.emitError('Update URL is not set');
}
if (!squirrelUpdate.supported()) {
return this.emitError('Can not find Squirrel');
}
this.emit('checking-for-update');
return squirrelUpdate.download(this.updateURL, (function(_this) {
return function(error, update) {
if (error != null) {
return _this.emitError(error);
}
if (update == null) {
return _this.emit('update-not-available');
}
_this.emit('update-available');
return squirrelUpdate.update(_this.updateURL, function(error) {
var date, releaseNotes, version;
if (error != null) {
return _this.emitError(error);
}
releaseNotes = update.releaseNotes, version = update.version;
// Following information is not available on Windows, so fake them.
date = new Date;
return _this.emit('update-downloaded', {}, releaseNotes, version, date, _this.updateURL, function() {
return _this.quitAndInstall();
});
});
};
})(this));
}
// Private: Emit both error object and message, this is to keep compatibility
// with Old APIs.
emitError(message) {
return this.emit('error', new Error(message), message);
}
}
module.exports = new AutoUpdater;

View file

@ -0,0 +1,98 @@
const fs = require('fs');
const path = require('path');
const spawn = require('child_process').spawn;
// i.e. my-app/app-0.1.13/
const appFolder = path.dirname(process.execPath);
// i.e. my-app/Update.exe
const updateExe = path.resolve(appFolder, '..', 'Update.exe');
const exeName = path.basename(process.execPath);
// Spawn a command and invoke the callback when it completes with an error
// and the output from standard out.
var spawnUpdate = function(args, detached, callback) {
var error, errorEmitted, spawnedProcess, stderr, stdout;
try {
spawnedProcess = spawn(updateExe, args, {
detached: detached
});
} catch (error1) {
error = error1;
// Shouldn't happen, but still guard it.
process.nextTick(function() {
return callback(error);
});
return;
}
stdout = '';
stderr = '';
spawnedProcess.stdout.on('data', function(data) {
return stdout += data;
});
spawnedProcess.stderr.on('data', function(data) {
return stderr += data;
});
errorEmitted = false;
spawnedProcess.on('error', function(error) {
errorEmitted = true;
return callback(error);
});
return spawnedProcess.on('exit', function(code, signal) {
// We may have already emitted an error.
if (errorEmitted) {
return;
}
// Process terminated with error.
if (code !== 0) {
return callback("Command failed: " + (signal != null ? signal : code) + "\n" + stderr);
}
// Success.
return callback(null, stdout);
});
};
// Start an instance of the installed app.
exports.processStart = function() {
return spawnUpdate(['--processStart', exeName], true, function() {});
};
// Download the releases specified by the URL and write new results to stdout.
exports.download = function(updateURL, callback) {
return spawnUpdate(['--download', updateURL], false, function(error, stdout) {
var json, ref, ref1, update;
if (error != null) {
return callback(error);
}
try {
// Last line of output is the JSON details about the releases
json = stdout.trim().split('\n').pop();
update = (ref = JSON.parse(json)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === "function" ? ref1.pop() : void 0 : void 0 : void 0;
} catch (jsonError) {
return callback("Invalid result:\n" + stdout);
}
return callback(null, update);
});
};
// Update the application to the latest remote version specified by URL.
exports.update = function(updateURL, callback) {
return spawnUpdate(['--update', updateURL], false, callback);
};
// Is the Update.exe installed with the current application?
exports.supported = function() {
try {
fs.accessSync(updateExe, fs.R_OK);
return true;
} catch (error) {
return false;
}
};

View file

@ -1,84 +0,0 @@
EventEmitter = require('events').EventEmitter
app = require 'app'
ipc = require 'ipc'
BrowserWindow = process.atomBinding('window').BrowserWindow
BrowserWindow::__proto__ = EventEmitter.prototype
BrowserWindow::_init = ->
# Simulate the application menu on platforms other than OS X.
if process.platform isnt 'darwin'
menu = app.getApplicationMenu()
@setMenu menu if menu?
# Make new windows requested by links behave like "window.open"
@webContents.on '-new-window', (event, url, frameName) ->
options = show: true, width: 800, height: 600
ipc.emit 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, options
# window.resizeTo(...)
# window.moveTo(...)
@webContents.on 'move', (event, size) =>
@setBounds size
# Hide the auto-hide menu when webContents is focused.
@webContents.on 'activate', =>
if process.platform isnt 'darwin' and @isMenuBarAutoHide() and @isMenuBarVisible()
@setMenuBarVisibility false
# Forward the crashed event.
@webContents.on 'crashed', =>
@emit 'crashed'
# 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,
# if we do it later we might move focus in the page.
# Though this hack is only needed on OS X when the app is launched from
# Finder, we still do it on all platforms in case of other bugs we don't know.
@webContents.once 'load-url', ->
@focus()
# Redirect focus/blur event to app instance too.
@on 'blur', (event) =>
app.emit 'browser-window-blur', event, this
@on 'focus', (event) =>
app.emit 'browser-window-focus', event, this
BrowserWindow.getFocusedWindow = ->
windows = BrowserWindow.getAllWindows()
return window for window in windows when window.isFocused()
BrowserWindow.fromWebContents = (webContents) ->
windows = BrowserWindow.getAllWindows()
return window for window in windows when window.webContents?.equal webContents
BrowserWindow.fromDevToolsWebContents = (webContents) ->
windows = BrowserWindow.getAllWindows()
return window for window in windows when window.devToolsWebContents?.equal webContents
# Helpers.
BrowserWindow::loadUrl = -> @webContents.loadUrl.apply @webContents, arguments
BrowserWindow::send = -> @webContents.send.apply @webContents, arguments
# Be compatible with old API.
BrowserWindow::restart = -> @webContents.reload()
BrowserWindow::getUrl = -> @webContents.getUrl()
BrowserWindow::reload = -> @webContents.reload.apply @webContents, arguments
BrowserWindow::reloadIgnoringCache = -> @webContents.reloadIgnoringCache.apply @webContents, arguments
BrowserWindow::getPageTitle = -> @webContents.getTitle()
BrowserWindow::isLoading = -> @webContents.isLoading()
BrowserWindow::isWaitingForResponse = -> @webContents.isWaitingForResponse()
BrowserWindow::stop = -> @webContents.stop()
BrowserWindow::isCrashed = -> @webContents.isCrashed()
BrowserWindow::executeJavaScriptInDevTools = (code) -> @devToolsWebContents?.executeJavaScript code
BrowserWindow::openDevTools = -> @webContents.openDevTools.apply @webContents, arguments
BrowserWindow::closeDevTools = -> @webContents.closeDevTools()
BrowserWindow::isDevToolsOpened = -> @webContents.isDevToolsOpened()
BrowserWindow::toggleDevTools = -> @webContents.toggleDevTools()
BrowserWindow::inspectElement = -> @webContents.inspectElement.apply @webContents, arguments
BrowserWindow::inspectServiceWorker = -> @webContents.inspectServiceWorker()
BrowserWindow::print = -> @webContents.print.apply @webContents, arguments
BrowserWindow::printToPDF = -> @webContents.printToPDF.apply @webContents, arguments
module.exports = BrowserWindow

View file

@ -0,0 +1,241 @@
const ipcMain = require('electron').ipcMain;
const deprecate = require('electron').deprecate;
const EventEmitter = require('events').EventEmitter;
const BrowserWindow = process.atomBinding('window').BrowserWindow;
BrowserWindow.prototype.__proto__ = EventEmitter.prototype;
BrowserWindow.prototype._init = function() {
// avoid recursive require.
var app, menu;
app = require('electron').app;
// Simulate the application menu on platforms other than OS X.
if (process.platform !== 'darwin') {
menu = app.getApplicationMenu();
if (menu != null) {
this.setMenu(menu);
}
}
// Make new windows requested by links behave like "window.open"
this.webContents.on('-new-window', function(event, url, frameName) {
var options;
options = {
show: true,
width: 800,
height: 600
};
return ipcMain.emit('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, options);
});
// window.resizeTo(...)
// window.moveTo(...)
this.webContents.on('move', (function(_this) {
return function(event, size) {
return _this.setBounds(size);
};
})(this));
// Hide the auto-hide menu when webContents is focused.
this.webContents.on('activate', (function(_this) {
return function() {
if (process.platform !== 'darwin' && _this.isMenuBarAutoHide() && _this.isMenuBarVisible()) {
return _this.setMenuBarVisibility(false);
}
};
})(this));
// Forward the crashed event.
this.webContents.on('crashed', (function(_this) {
return function() {
return _this.emit('crashed');
};
})(this));
// Change window title to page title.
this.webContents.on('page-title-updated', (function(_this) {
return function(event, title) {
_this.emit('page-title-updated', event, title);
if (!event.defaultPrevented) {
return _this.setTitle(title);
}
};
})(this));
// 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,
// if we do it later we might move focus in the page.
// Though this hack is only needed on OS X when the app is launched from
// Finder, we still do it on all platforms in case of other bugs we don't know.
this.webContents.once('load-url', function() {
return this.focus();
});
// Redirect focus/blur event to app instance too.
this.on('blur', (function(_this) {
return function(event) {
return app.emit('browser-window-blur', event, _this);
};
})(this));
this.on('focus', (function(_this) {
return function(event) {
return app.emit('browser-window-focus', event, _this);
};
})(this));
// Notify the creation of the window.
app.emit('browser-window-created', {}, this);
// Be compatible with old APIs.
this.webContents.on('devtools-focused', (function(_this) {
return function() {
return _this.emit('devtools-focused');
};
})(this));
this.webContents.on('devtools-opened', (function(_this) {
return function() {
return _this.emit('devtools-opened');
};
})(this));
this.webContents.on('devtools-closed', (function(_this) {
return function() {
return _this.emit('devtools-closed');
};
})(this));
return Object.defineProperty(this, 'devToolsWebContents', {
enumerable: true,
configurable: false,
get: function() {
return this.webContents.devToolsWebContents;
}
});
};
BrowserWindow.getFocusedWindow = function() {
var i, len, window, windows;
windows = BrowserWindow.getAllWindows();
for (i = 0, len = windows.length; i < len; i++) {
window = windows[i];
if (window.isFocused()) {
return window;
}
}
return null;
};
BrowserWindow.fromWebContents = function(webContents) {
var i, len, ref1, window, windows;
windows = BrowserWindow.getAllWindows();
for (i = 0, len = windows.length; i < len; i++) {
window = windows[i];
if ((ref1 = window.webContents) != null ? ref1.equal(webContents) : void 0) {
return window;
}
}
};
BrowserWindow.fromDevToolsWebContents = function(webContents) {
var i, len, ref1, window, windows;
windows = BrowserWindow.getAllWindows();
for (i = 0, len = windows.length; i < len; i++) {
window = windows[i];
if ((ref1 = window.devToolsWebContents) != null ? ref1.equal(webContents) : void 0) {
return window;
}
}
};
// Helpers.
BrowserWindow.prototype.loadURL = function() {
return this.webContents.loadURL.apply(this.webContents, arguments);
};
BrowserWindow.prototype.getURL = function() {
return this.webContents.getURL();
};
BrowserWindow.prototype.reload = function() {
return this.webContents.reload.apply(this.webContents, arguments);
};
BrowserWindow.prototype.send = function() {
return this.webContents.send.apply(this.webContents, arguments);
};
BrowserWindow.prototype.openDevTools = function() {
return this.webContents.openDevTools.apply(this.webContents, arguments);
};
BrowserWindow.prototype.closeDevTools = function() {
return this.webContents.closeDevTools();
};
BrowserWindow.prototype.isDevToolsOpened = function() {
return this.webContents.isDevToolsOpened();
};
BrowserWindow.prototype.isDevToolsFocused = function() {
return this.webContents.isDevToolsFocused();
};
BrowserWindow.prototype.toggleDevTools = function() {
return this.webContents.toggleDevTools();
};
BrowserWindow.prototype.inspectElement = function() {
return this.webContents.inspectElement.apply(this.webContents, arguments);
};
BrowserWindow.prototype.inspectServiceWorker = function() {
return this.webContents.inspectServiceWorker();
};
// Deprecated.
deprecate.member(BrowserWindow, 'undo', 'webContents');
deprecate.member(BrowserWindow, 'redo', 'webContents');
deprecate.member(BrowserWindow, 'cut', 'webContents');
deprecate.member(BrowserWindow, 'copy', 'webContents');
deprecate.member(BrowserWindow, 'paste', 'webContents');
deprecate.member(BrowserWindow, 'selectAll', 'webContents');
deprecate.member(BrowserWindow, 'reloadIgnoringCache', 'webContents');
deprecate.member(BrowserWindow, 'isLoading', 'webContents');
deprecate.member(BrowserWindow, 'isWaitingForResponse', 'webContents');
deprecate.member(BrowserWindow, 'stop', 'webContents');
deprecate.member(BrowserWindow, 'isCrashed', '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.prototype.executeJavaScriptInDevTools = deprecate('executeJavaScriptInDevTools', 'devToolsWebContents.executeJavaScript', function(code) {
var ref1;
return (ref1 = this.devToolsWebContents) != null ? ref1.executeJavaScript(code) : void 0;
});
BrowserWindow.prototype.getPageTitle = deprecate('getPageTitle', 'webContents.getTitle', function() {
var ref1;
return (ref1 = this.webContents) != null ? ref1.getTitle() : void 0;
});
module.exports = BrowserWindow;

View file

@ -1 +0,0 @@
module.exports = process.atomBinding 'content_tracing'

View file

@ -0,0 +1 @@
module.exports = process.atomBinding('content_tracing');

View file

@ -1,125 +0,0 @@
binding = process.atomBinding 'dialog'
v8Util = process.atomBinding 'v8_util'
app = require 'app'
BrowserWindow = require 'browser-window'
fileDialogProperties =
openFile: 1 << 0
openDirectory: 1 << 1
multiSelections: 1 << 2
createDirectory: 1 << 3
messageBoxTypes = ['none', 'info', 'warning', 'error', 'question']
messageBoxOptions =
noLink: 1 << 0
parseArgs = (window, options, callback) ->
unless window is null or window?.constructor is BrowserWindow
# Shift.
callback = options
options = window
window = null
if not callback? and typeof options is 'function'
# Shift.
callback = options
options = null
[window, options, callback]
checkAppInitialized = ->
throw new Error('dialog module can only be used after app is ready') unless app.isReady()
module.exports =
showOpenDialog: (args...) ->
checkAppInitialized()
[window, options, callback] = parseArgs args...
options ?= title: 'Open', properties: ['openFile']
options.properties ?= ['openFile']
throw new TypeError('Properties need to be array') unless Array.isArray options.properties
properties = 0
for prop, value of fileDialogProperties
properties |= value if prop in options.properties
options.title ?= ''
options.defaultPath ?= ''
options.filters ?= []
wrappedCallback =
if typeof callback is 'function'
(success, result) -> callback(if success then result)
else
null
binding.showOpenDialog String(options.title),
String(options.defaultPath),
options.filters
properties,
window,
wrappedCallback
showSaveDialog: (args...) ->
checkAppInitialized()
[window, options, callback] = parseArgs args...
options ?= title: 'Save'
options.title ?= ''
options.defaultPath ?= ''
options.filters ?= []
wrappedCallback =
if typeof callback is 'function'
(success, result) -> callback(if success then result)
else
null
binding.showSaveDialog String(options.title),
String(options.defaultPath),
options.filters
window,
wrappedCallback
showMessageBox: (args...) ->
checkAppInitialized()
[window, options, callback] = parseArgs args...
options ?= type: 'none'
options.type ?= 'none'
messageBoxType = messageBoxTypes.indexOf options.type
throw new TypeError('Invalid message box type') unless messageBoxType > -1
throw new TypeError('Buttons need to be array') unless Array.isArray options.buttons
options.title ?= ''
options.message ?= ''
options.detail ?= ''
options.icon ?= null
# Choose a default button to get selected when dialog is cancelled.
unless options.cancelId?
options.cancelId = 0
for text, i in options.buttons
if text.toLowerCase() in ['cancel', 'no']
options.cancelId = i
break
flags = if options.noLink then messageBoxOptions.noLink else 0
binding.showMessageBox messageBoxType,
options.buttons,
options.cancelId,
flags,
options.title,
options.message,
options.detail,
options.icon,
window,
callback
showErrorBox: (args...) ->
binding.showErrorBox args...
# Mark standard asynchronous functions.
for api in ['showMessageBox', 'showOpenDialog', 'showSaveDialog']
v8Util.setHiddenValue module.exports[api], 'asynchronous', true

View file

@ -0,0 +1,170 @@
const app = require('electron').app;
const BrowserWindow = require('electron').BrowserWindow;
const binding = process.atomBinding('dialog');
const v8Util = process.atomBinding('v8_util');
var slice = [].slice;
var includes = [].includes;
var fileDialogProperties = {
openFile: 1 << 0,
openDirectory: 1 << 1,
multiSelections: 1 << 2,
createDirectory: 1 << 3
};
var messageBoxTypes = ['none', 'info', 'warning', 'error', 'question'];
var messageBoxOptions = {
noLink: 1 << 0
};
var parseArgs = function(window, options, callback) {
if (!(window === null || (window != null ? window.constructor : void 0) === BrowserWindow)) {
// Shift.
callback = options;
options = window;
window = null;
}
if ((callback == null) && typeof options === 'function') {
// Shift.
callback = options;
options = null;
}
return [window, options, callback];
};
var checkAppInitialized = function() {
if (!app.isReady()) {
throw new Error('dialog module can only be used after app is ready');
}
};
module.exports = {
showOpenDialog: function() {
var args, callback, options, prop, properties, ref1, value, window, wrappedCallback;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
checkAppInitialized();
ref1 = parseArgs.apply(null, args), window = ref1[0], options = ref1[1], callback = ref1[2];
if (options == null) {
options = {
title: 'Open',
properties: ['openFile']
};
}
if (options.properties == null) {
options.properties = ['openFile'];
}
if (!Array.isArray(options.properties)) {
throw new TypeError('Properties need to be array');
}
properties = 0;
for (prop in fileDialogProperties) {
value = fileDialogProperties[prop];
if (includes.call(options.properties, prop)) {
properties |= value;
}
}
if (options.title == null) {
options.title = '';
}
if (options.defaultPath == null) {
options.defaultPath = '';
}
if (options.filters == null) {
options.filters = [];
}
wrappedCallback = typeof callback === 'function' ? function(success, result) {
return callback(success ? result : void 0);
} : null;
return binding.showOpenDialog(String(options.title), String(options.defaultPath), options.filters, properties, window, wrappedCallback);
},
showSaveDialog: function() {
var args, callback, options, ref1, window, wrappedCallback;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
checkAppInitialized();
ref1 = parseArgs.apply(null, args), window = ref1[0], options = ref1[1], callback = ref1[2];
if (options == null) {
options = {
title: 'Save'
};
}
if (options.title == null) {
options.title = '';
}
if (options.defaultPath == null) {
options.defaultPath = '';
}
if (options.filters == null) {
options.filters = [];
}
wrappedCallback = typeof callback === 'function' ? function(success, result) {
return callback(success ? result : void 0);
} : null;
return binding.showSaveDialog(String(options.title), String(options.defaultPath), options.filters, window, wrappedCallback);
},
showMessageBox: function() {
var args, callback, flags, i, j, len, messageBoxType, options, ref1, ref2, ref3, text, window;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
checkAppInitialized();
ref1 = parseArgs.apply(null, args), window = ref1[0], options = ref1[1], callback = ref1[2];
if (options == null) {
options = {
type: 'none'
};
}
if (options.type == null) {
options.type = 'none';
}
messageBoxType = messageBoxTypes.indexOf(options.type);
if (!(messageBoxType > -1)) {
throw new TypeError('Invalid message box type');
}
if (!Array.isArray(options.buttons)) {
throw new TypeError('Buttons need to be array');
}
if (options.title == null) {
options.title = '';
}
if (options.message == null) {
options.message = '';
}
if (options.detail == null) {
options.detail = '';
}
if (options.icon == null) {
options.icon = null;
}
if (options.defaultId == null) {
options.defaultId = -1;
}
// Choose a default button to get selected when dialog is cancelled.
if (options.cancelId == null) {
options.cancelId = 0;
ref2 = options.buttons;
for (i = j = 0, len = ref2.length; j < len; i = ++j) {
text = ref2[i];
if ((ref3 = text.toLowerCase()) === 'cancel' || ref3 === 'no') {
options.cancelId = i;
break;
}
}
}
flags = options.noLink ? messageBoxOptions.noLink : 0;
return binding.showMessageBox(messageBoxType, options.buttons, options.defaultId, options.cancelId, flags, options.title, options.message, options.detail, options.icon, window, callback);
},
showErrorBox: function() {
var args;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
return binding.showErrorBox.apply(binding, args);
}
};
// Mark standard asynchronous functions.
var ref1 = ['showMessageBox', 'showOpenDialog', 'showSaveDialog'];
var j, len, api;
for (j = 0, len = ref1.length; j < len; j++) {
api = ref1[j];
v8Util.setHiddenValue(module.exports[api], 'asynchronous', true);
}

View file

@ -0,0 +1,112 @@
const common = require('../../../../common/api/lib/exports/electron');
// Import common modules.
common.defineProperties(exports);
Object.defineProperties(exports, {
// Browser side modules, please sort with alphabet order.
app: {
enumerable: true,
get: function() {
return require('../app');
}
},
autoUpdater: {
enumerable: true,
get: function() {
return require('../auto-updater');
}
},
BrowserWindow: {
enumerable: true,
get: function() {
return require('../browser-window');
}
},
contentTracing: {
enumerable: true,
get: function() {
return require('../content-tracing');
}
},
dialog: {
enumerable: true,
get: function() {
return require('../dialog');
}
},
ipcMain: {
enumerable: true,
get: function() {
return require('../ipc-main');
}
},
globalShortcut: {
enumerable: true,
get: function() {
return require('../global-shortcut');
}
},
Menu: {
enumerable: true,
get: function() {
return require('../menu');
}
},
MenuItem: {
enumerable: true,
get: function() {
return require('../menu-item');
}
},
powerMonitor: {
enumerable: true,
get: function() {
return require('../power-monitor');
}
},
powerSaveBlocker: {
enumerable: true,
get: function() {
return require('../power-save-blocker');
}
},
protocol: {
enumerable: true,
get: function() {
return require('../protocol');
}
},
screen: {
enumerable: true,
get: function() {
return require('../screen');
}
},
session: {
enumerable: true,
get: function() {
return require('../session');
}
},
Tray: {
enumerable: true,
get: function() {
return require('../tray');
}
},
// The internal modules, invisible unless you know their names.
NavigationController: {
get: function() {
return require('../navigation-controller');
}
},
webContents: {
get: function() {
return require('../web-contents');
}
}
});

View file

@ -1,5 +0,0 @@
bindings = process.atomBinding 'global_shortcut'
globalShortcut = bindings.globalShortcut
module.exports = globalShortcut

View file

@ -0,0 +1,5 @@
var globalShortcut;
globalShortcut = process.atomBinding('global_shortcut').globalShortcut;
module.exports = globalShortcut;

View file

@ -0,0 +1,3 @@
const EventEmitter = require('events').EventEmitter;
module.exports = new EventEmitter;

View file

@ -1,3 +0,0 @@
EventEmitter = require('events').EventEmitter
module.exports = new EventEmitter

View file

@ -0,0 +1,7 @@
const deprecate = require('electron').deprecate;
const ipcMain = require('electron').ipcMain;
// This module is deprecated, we mirror everything from ipcMain.
deprecate.warn('ipc module', 'require("electron").ipcMain');
module.exports = ipcMain;

View file

@ -1,49 +0,0 @@
BrowserWindow = require 'browser-window'
v8Util = process.atomBinding 'v8_util'
nextCommandId = 0
class MenuItem
@types = ['normal', 'separator', 'submenu', 'checkbox', 'radio']
constructor: (options) ->
Menu = require 'menu'
{click, @selector, @type, @label, @sublabel, @accelerator, @icon, @enabled, @visible, @checked, @submenu} = options
@type = 'submenu' if not @type? and @submenu?
throw new Error('Invalid submenu') if @type is 'submenu' and @submenu?.constructor isnt Menu
@overrideReadOnlyProperty 'type', 'normal'
@overrideReadOnlyProperty 'accelerator'
@overrideReadOnlyProperty 'icon'
@overrideReadOnlyProperty 'submenu'
@overrideProperty 'label', ''
@overrideProperty 'sublabel', ''
@overrideProperty 'enabled', true
@overrideProperty 'visible', true
@overrideProperty 'checked', false
throw new Error("Unknown menu type #{@type}") if MenuItem.types.indexOf(@type) is -1
@commandId = ++nextCommandId
@click = =>
# Manually flip the checked flags when clicked.
@checked = !@checked if @type in ['checkbox', 'radio']
if typeof click is 'function'
click this, BrowserWindow.getFocusedWindow()
else if typeof @selector is 'string'
Menu.sendActionToFirstResponder @selector
overrideProperty: (name, defaultValue=null) ->
this[name] ?= defaultValue
overrideReadOnlyProperty: (name, defaultValue=null) ->
this[name] ?= defaultValue
Object.defineProperty this, name,
enumerable: true
writable: false
value: this[name]
module.exports = MenuItem

View file

@ -0,0 +1,102 @@
var MenuItem, methodInBrowserWindow, nextCommandId, rolesMap;
nextCommandId = 0;
// Maps role to methods of webContents
rolesMap = {
undo: 'undo',
redo: 'redo',
cut: 'cut',
copy: 'copy',
paste: 'paste',
selectall: 'selectAll',
minimize: 'minimize',
close: 'close'
};
// Maps methods that should be called directly on the BrowserWindow instance
methodInBrowserWindow = {
minimize: true,
close: true
};
MenuItem = (function() {
MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio'];
function MenuItem(options) {
var click, ref;
const Menu = require('electron').Menu;
click = options.click, this.selector = options.selector, this.type = options.type, this.role = options.role, this.label = options.label, this.sublabel = options.sublabel, this.accelerator = options.accelerator, this.icon = options.icon, this.enabled = options.enabled, this.visible = options.visible, this.checked = options.checked, this.submenu = options.submenu;
if ((this.submenu != null) && this.submenu.constructor !== Menu) {
this.submenu = Menu.buildFromTemplate(this.submenu);
}
if ((this.type == null) && (this.submenu != null)) {
this.type = 'submenu';
}
if (this.type === 'submenu' && ((ref = this.submenu) != null ? ref.constructor : void 0) !== Menu) {
throw new Error('Invalid submenu');
}
this.overrideReadOnlyProperty('type', 'normal');
this.overrideReadOnlyProperty('role');
this.overrideReadOnlyProperty('accelerator');
this.overrideReadOnlyProperty('icon');
this.overrideReadOnlyProperty('submenu');
this.overrideProperty('label', '');
this.overrideProperty('sublabel', '');
this.overrideProperty('enabled', true);
this.overrideProperty('visible', true);
this.overrideProperty('checked', false);
if (MenuItem.types.indexOf(this.type) === -1) {
throw new Error("Unknown menu type " + this.type);
}
this.commandId = ++nextCommandId;
this.click = (function(_this) {
return function(focusedWindow) {
// Manually flip the checked flags when clicked.
var methodName, ref1, ref2;
if ((ref1 = _this.type) === 'checkbox' || ref1 === 'radio') {
_this.checked = !_this.checked;
}
if (_this.role && rolesMap[_this.role] && process.platform !== 'darwin' && (focusedWindow != null)) {
methodName = rolesMap[_this.role];
if (methodInBrowserWindow[methodName]) {
return focusedWindow[methodName]();
} else {
return (ref2 = focusedWindow.webContents) != null ? ref2[methodName]() : void 0;
}
} else if (typeof click === 'function') {
return click(_this, focusedWindow);
} else if (typeof _this.selector === 'string') {
return Menu.sendActionToFirstResponder(_this.selector);
}
};
})(this);
}
MenuItem.prototype.overrideProperty = function(name, defaultValue) {
if (defaultValue == null) {
defaultValue = null;
}
return this[name] != null ? this[name] : this[name] = defaultValue;
};
MenuItem.prototype.overrideReadOnlyProperty = function(name, defaultValue) {
if (defaultValue == null) {
defaultValue = null;
}
if (this[name] == null) {
this[name] = defaultValue;
}
return Object.defineProperty(this, name, {
enumerable: true,
writable: false,
value: this[name]
});
};
return MenuItem;
})();
module.exports = MenuItem;

View file

@ -1,174 +0,0 @@
BrowserWindow = require 'browser-window'
EventEmitter = require('events').EventEmitter
MenuItem = require 'menu-item'
v8Util = process.atomBinding 'v8_util'
bindings = process.atomBinding 'menu'
# Automatically generated radio menu item's group id.
nextGroupId = 0
# Search between seperators to find a radio menu item and return its group id,
# otherwise generate a group id.
generateGroupId = (items, pos) ->
if pos > 0
for i in [pos - 1..0]
item = items[i]
return item.groupId if item.type is 'radio'
break if item.type is 'separator'
else if pos < items.length
for i in [pos..items.length - 1]
item = items[i]
return item.groupId if item.type is 'radio'
break if item.type is 'separator'
++nextGroupId
# Returns the index of item according to |id|.
indexOfItemById = (items, id) ->
return i for item, i in items when item.id is id
-1
# Returns the index of where to insert the item according to |position|.
indexToInsertByPosition = (items, position) ->
return items.length unless position
[query, id] = position.split '='
insertIndex = indexOfItemById items, id
if insertIndex is -1 and query isnt 'endof'
console.warn "Item with id '#{id}' is not found"
return items.length
switch query
when 'after'
insertIndex++
when 'endof'
# If the |id| doesn't exist, then create a new group with the |id|.
if insertIndex is -1
items.push id: id, type: 'separator'
insertIndex = items.length - 1
# Find the end of the group.
insertIndex++
while insertIndex < items.length and items[insertIndex].type isnt 'separator'
insertIndex++
insertIndex
Menu = bindings.Menu
Menu::__proto__ = EventEmitter.prototype
Menu::_init = ->
@commandsMap = {}
@groupsMap = {}
@items = []
@delegate =
isCommandIdChecked: (commandId) => @commandsMap[commandId]?.checked
isCommandIdEnabled: (commandId) => @commandsMap[commandId]?.enabled
isCommandIdVisible: (commandId) => @commandsMap[commandId]?.visible
getAcceleratorForCommandId: (commandId) => @commandsMap[commandId]?.accelerator
getIconForCommandId: (commandId) => @commandsMap[commandId]?.icon
executeCommand: (commandId) => @commandsMap[commandId]?.click()
menuWillShow: =>
# Make sure radio groups have at least one menu item seleted.
for id, group of @groupsMap
checked = false
for radioItem in group when radioItem.checked
checked = true
break
v8Util.setHiddenValue group[0], 'checked', true unless checked
Menu::popup = (window, x, y) ->
throw new TypeError('Invalid window') unless window?.constructor is BrowserWindow
if x? and y?
@_popupAt(window, x, y)
else
@_popup window
Menu::append = (item) ->
@insert @getItemCount(), item
Menu::insert = (pos, item) ->
throw new TypeError('Invalid item') unless item?.constructor is MenuItem
switch item.type
when 'normal' then @insertItem pos, item.commandId, item.label
when 'checkbox' then @insertCheckItem pos, item.commandId, item.label
when 'separator' then @insertSeparator pos
when 'submenu' then @insertSubMenu pos, item.commandId, item.label, item.submenu
when 'radio'
# Grouping radio menu items.
item.overrideReadOnlyProperty 'groupId', generateGroupId(@items, pos)
@groupsMap[item.groupId] ?= []
@groupsMap[item.groupId].push item
# Setting a radio menu item should flip other items in the group.
v8Util.setHiddenValue item, 'checked', item.checked
Object.defineProperty item, 'checked',
enumerable: true
get: -> v8Util.getHiddenValue item, 'checked'
set: (val) =>
for otherItem in @groupsMap[item.groupId] when otherItem isnt item
v8Util.setHiddenValue otherItem, 'checked', false
v8Util.setHiddenValue item, 'checked', true
@insertRadioItem pos, item.commandId, item.label, item.groupId
@setSublabel pos, item.sublabel if item.sublabel?
@setIcon pos, item.icon if item.icon?
# Make menu accessable to items.
item.overrideReadOnlyProperty 'menu', this
# Remember the items.
@items.splice pos, 0, item
@commandsMap[item.commandId] = item
# Force menuWillShow to be called
Menu::_callMenuWillShow = ->
@delegate?.menuWillShow()
item.submenu._callMenuWillShow() for item in @items when item.submenu?
applicationMenu = null
Menu.setApplicationMenu = (menu) ->
throw new TypeError('Invalid menu') unless menu is null or menu.constructor is Menu
applicationMenu = menu # Keep a reference.
if process.platform is 'darwin'
return if menu is null
menu._callMenuWillShow()
bindings.setApplicationMenu menu
else
windows = BrowserWindow.getAllWindows()
w.setMenu menu for w in windows
Menu.getApplicationMenu = -> applicationMenu
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder
Menu.buildFromTemplate = (template) ->
throw new TypeError('Invalid template for Menu') unless Array.isArray template
positionedTemplate = []
insertIndex = 0
for item in template
if item.position
insertIndex = indexToInsertByPosition positionedTemplate, item.position
else
# If no |position| is specified, insert after last item.
insertIndex++
positionedTemplate.splice insertIndex, 0, item
menu = new Menu
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]?
menu.append menuItem
menu
module.exports = Menu

View file

@ -0,0 +1,335 @@
const BrowserWindow = require('electron').BrowserWindow;
const MenuItem = require('electron').MenuItem;
const EventEmitter = require('events').EventEmitter;
const v8Util = process.atomBinding('v8_util');
const bindings = process.atomBinding('menu');
// Automatically generated radio menu item's group id.
var nextGroupId = 0;
// Search between seperators to find a radio menu item and return its group id,
// otherwise generate a group id.
var generateGroupId = function(items, pos) {
var i, item, j, k, ref1, ref2, ref3;
if (pos > 0) {
for (i = j = ref1 = pos - 1; ref1 <= 0 ? j <= 0 : j >= 0; i = ref1 <= 0 ? ++j : --j) {
item = items[i];
if (item.type === 'radio') {
return item.groupId;
}
if (item.type === 'separator') {
break;
}
}
} else if (pos < items.length) {
for (i = k = ref2 = pos, ref3 = items.length - 1; ref2 <= ref3 ? k <= ref3 : k >= ref3; i = ref2 <= ref3 ? ++k : --k) {
item = items[i];
if (item.type === 'radio') {
return item.groupId;
}
if (item.type === 'separator') {
break;
}
}
}
return ++nextGroupId;
};
// Returns the index of item according to |id|.
var indexOfItemById = function(items, id) {
var i, item, j, len;
for (i = j = 0, len = items.length; j < len; i = ++j) {
item = items[i];
if (item.id === id) {
return i;
}
}
return -1;
};
// Returns the index of where to insert the item according to |position|.
var indexToInsertByPosition = function(items, position) {
var id, insertIndex, query, ref1;
if (!position) {
return items.length;
}
ref1 = position.split('='), query = ref1[0], id = ref1[1];
insertIndex = indexOfItemById(items, id);
if (insertIndex === -1 && query !== 'endof') {
console.warn("Item with id '" + id + "' is not found");
return items.length;
}
switch (query) {
case 'after':
insertIndex++;
break;
case 'endof':
// If the |id| doesn't exist, then create a new group with the |id|.
if (insertIndex === -1) {
items.push({
id: id,
type: 'separator'
});
insertIndex = items.length - 1;
}
// Find the end of the group.
insertIndex++;
while (insertIndex < items.length && items[insertIndex].type !== 'separator') {
insertIndex++;
}
}
return insertIndex;
};
const Menu = bindings.Menu;
Menu.prototype.__proto__ = EventEmitter.prototype;
Menu.prototype._init = function() {
this.commandsMap = {};
this.groupsMap = {};
this.items = [];
return this.delegate = {
isCommandIdChecked: (function(_this) {
return function(commandId) {
var ref1;
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.checked : void 0;
};
})(this),
isCommandIdEnabled: (function(_this) {
return function(commandId) {
var ref1;
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.enabled : void 0;
};
})(this),
isCommandIdVisible: (function(_this) {
return function(commandId) {
var ref1;
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.visible : void 0;
};
})(this),
getAcceleratorForCommandId: (function(_this) {
return function(commandId) {
var ref1;
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.accelerator : void 0;
};
})(this),
getIconForCommandId: (function(_this) {
return function(commandId) {
var ref1;
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.icon : void 0;
};
})(this),
executeCommand: (function(_this) {
return function(commandId) {
var ref1;
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.click(BrowserWindow.getFocusedWindow()) : void 0;
};
})(this),
menuWillShow: (function(_this) {
return function() {
// Make sure radio groups have at least one menu item seleted.
var checked, group, id, j, len, radioItem, ref1, results;
ref1 = _this.groupsMap;
results = [];
for (id in ref1) {
group = ref1[id];
checked = false;
for (j = 0, len = group.length; j < len; j++) {
radioItem = group[j];
if (!radioItem.checked) {
continue;
}
checked = true;
break;
}
if (!checked) {
results.push(v8Util.setHiddenValue(group[0], 'checked', true));
} else {
results.push(void 0);
}
}
return results;
};
})(this)
};
};
Menu.prototype.popup = function(window, x, y, positioningItem) {
if (typeof window != 'object' || window.constructor !== BrowserWindow) {
// Shift.
positioningItem = y;
y = x;
x = window;
window = BrowserWindow.getFocusedWindow();
}
// Default parameters.
if (typeof x !== 'number') x = -1;
if (typeof y !== 'number') y = -1;
if (typeof positioningItem !== 'number') positioningItem = 0;
this.popupAt(window, x, y, positioningItem);
};
Menu.prototype.append = function(item) {
return this.insert(this.getItemCount(), item);
};
Menu.prototype.insert = function(pos, item) {
var base, name;
if ((item != null ? item.constructor : void 0) !== MenuItem) {
throw new TypeError('Invalid item');
}
switch (item.type) {
case 'normal':
this.insertItem(pos, item.commandId, item.label);
break;
case 'checkbox':
this.insertCheckItem(pos, item.commandId, item.label);
break;
case 'separator':
this.insertSeparator(pos);
break;
case 'submenu':
this.insertSubMenu(pos, item.commandId, item.label, item.submenu);
break;
case 'radio':
// Grouping radio menu items.
item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos));
if ((base = this.groupsMap)[name = item.groupId] == null) {
base[name] = [];
}
this.groupsMap[item.groupId].push(item);
// Setting a radio menu item should flip other items in the group.
v8Util.setHiddenValue(item, 'checked', item.checked);
Object.defineProperty(item, 'checked', {
enumerable: true,
get: function() {
return v8Util.getHiddenValue(item, 'checked');
},
set: (function(_this) {
return function() {
var j, len, otherItem, ref1;
ref1 = _this.groupsMap[item.groupId];
for (j = 0, len = ref1.length; j < len; j++) {
otherItem = ref1[j];
if (otherItem !== item) {
v8Util.setHiddenValue(otherItem, 'checked', false);
}
}
return v8Util.setHiddenValue(item, 'checked', true);
};
})(this)
});
this.insertRadioItem(pos, item.commandId, item.label, item.groupId);
}
if (item.sublabel != null) {
this.setSublabel(pos, item.sublabel);
}
if (item.icon != null) {
this.setIcon(pos, item.icon);
}
if (item.role != null) {
this.setRole(pos, item.role);
}
// Make menu accessable to items.
item.overrideReadOnlyProperty('menu', this);
// Remember the items.
this.items.splice(pos, 0, item);
return this.commandsMap[item.commandId] = item;
};
// Force menuWillShow to be called
Menu.prototype._callMenuWillShow = function() {
var item, j, len, ref1, ref2, results;
if ((ref1 = this.delegate) != null) {
ref1.menuWillShow();
}
ref2 = this.items;
results = [];
for (j = 0, len = ref2.length; j < len; j++) {
item = ref2[j];
if (item.submenu != null) {
results.push(item.submenu._callMenuWillShow());
}
}
return results;
};
var applicationMenu = null;
Menu.setApplicationMenu = function(menu) {
var j, len, results, w, windows;
if (!(menu === null || menu.constructor === Menu)) {
throw new TypeError('Invalid menu');
}
// Keep a reference.
applicationMenu = menu;
if (process.platform === 'darwin') {
if (menu === null) {
return;
}
menu._callMenuWillShow();
return bindings.setApplicationMenu(menu);
} else {
windows = BrowserWindow.getAllWindows();
results = [];
for (j = 0, len = windows.length; j < len; j++) {
w = windows[j];
results.push(w.setMenu(menu));
}
return results;
}
};
Menu.getApplicationMenu = function() {
return applicationMenu;
};
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder;
Menu.buildFromTemplate = function(template) {
var insertIndex, item, j, k, key, len, len1, menu, menuItem, positionedTemplate, value;
if (!Array.isArray(template)) {
throw new TypeError('Invalid template for Menu');
}
positionedTemplate = [];
insertIndex = 0;
for (j = 0, len = template.length; j < len; j++) {
item = template[j];
if (item.position) {
insertIndex = indexToInsertByPosition(positionedTemplate, item.position);
} else {
// If no |position| is specified, insert after last item.
insertIndex++;
}
positionedTemplate.splice(insertIndex, 0, item);
}
menu = new Menu;
for (k = 0, len1 = positionedTemplate.length; k < len1; k++) {
item = positionedTemplate[k];
if (typeof item !== 'object') {
throw new TypeError('Invalid template for MenuItem');
}
menuItem = new MenuItem(item);
for (key in item) {
value = item[key];
if (menuItem[key] == null) {
menuItem[key] = value;
}
}
menu.append(menuItem);
}
return menu;
};
module.exports = Menu;

View file

@ -1,117 +0,0 @@
ipc = require 'ipc'
# The history operation in renderer is redirected to browser.
ipc.on 'ATOM_SHELL_NAVIGATION_CONTROLLER', (event, method, args...) ->
event.sender[method] args...
ipc.on 'ATOM_SHELL_SYNC_NAVIGATION_CONTROLLER', (event, method, args...) ->
event.returnValue = event.sender[method] args...
# JavaScript implementation of Chromium's NavigationController.
# Instead of relying on Chromium for history control, we compeletely do history
# control on user land, and only rely on WebContents.loadUrl for navigation.
# This helps us avoid Chromium's various optimizations so we can ensure renderer
# process is restarted everytime.
class NavigationController
constructor: (@webContents) ->
@clearHistory()
@webContents.on 'navigation-entry-commited', (event, url, inPage, replaceEntry) =>
if @inPageIndex > -1 and not inPage
# Navigated to a new page, clear in-page mark.
@inPageIndex = -1
else if @inPageIndex is -1 and inPage
# Started in-page navigations.
@inPageIndex = @currentIndex
if @pendingIndex >= 0 # Go to index.
@currentIndex = @pendingIndex
@pendingIndex = -1
@history[@currentIndex] = url
else if replaceEntry # Non-user initialized navigation.
@history[@currentIndex] = url
else # Normal navigation.
@history = @history.slice 0, @currentIndex + 1 # Clear history.
currentEntry = @history[@currentIndex]
if currentEntry?.url isnt url
@currentIndex++
@history.push url
loadUrl: (url, options={}) ->
@pendingIndex = -1
@webContents._loadUrl url, options
@webContents.emit 'load-url', url, options
getUrl: ->
if @currentIndex is -1
''
else
@history[@currentIndex]
stop: ->
@pendingIndex = -1
@webContents._stop()
reload: ->
@pendingIndex = @currentIndex
@webContents._loadUrl @getUrl(), {}
reloadIgnoringCache: ->
@webContents._reloadIgnoringCache() # Rely on WebContents to clear cache.
@reload()
canGoBack: ->
@getActiveIndex() > 0
canGoForward: ->
@getActiveIndex() < @history.length - 1
canGoToIndex: (index) ->
index >=0 and index < @history.length
canGoToOffset: (offset) ->
@canGoToIndex @currentIndex + offset
clearHistory: ->
@history = []
@currentIndex = -1
@pendingIndex = -1
@inPageIndex = -1
goBack: ->
return unless @canGoBack()
@pendingIndex = @getActiveIndex() - 1
if @inPageIndex > -1 and @pendingIndex >= @inPageIndex
@webContents._goBack()
else
@webContents._loadUrl @history[@pendingIndex], {}
goForward: ->
return unless @canGoForward()
@pendingIndex = @getActiveIndex() + 1
if @inPageIndex > -1 and @pendingIndex >= @inPageIndex
@webContents._goForward()
else
@webContents._loadUrl @history[@pendingIndex], {}
goToIndex: (index) ->
return unless @canGoToIndex index
@pendingIndex = index
@webContents._loadUrl @history[@pendingIndex], {}
goToOffset: (offset) ->
return unless @canGoToOffset offset
pendingIndex = @currentIndex + offset
if @inPageIndex > -1 and pendingIndex >= @inPageIndex
@pendingIndex = pendingIndex
@webContents._goToOffset offset
else
@goToIndex pendingIndex
getActiveIndex: ->
if @pendingIndex is -1 then @currentIndex else @pendingIndex
length: ->
@history.length
module.exports = NavigationController

View file

@ -0,0 +1,188 @@
const ipcMain = require('electron').ipcMain;
var slice = [].slice;
// The history operation in renderer is redirected to browser.
ipcMain.on('ATOM_SHELL_NAVIGATION_CONTROLLER', function() {
var args, event, method, ref;
event = arguments[0], method = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
return (ref = event.sender)[method].apply(ref, args);
});
ipcMain.on('ATOM_SHELL_SYNC_NAVIGATION_CONTROLLER', function() {
var args, event, method, ref;
event = arguments[0], method = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
return event.returnValue = (ref = event.sender)[method].apply(ref, args);
});
// JavaScript implementation of Chromium's NavigationController.
// Instead of relying on Chromium for history control, we compeletely do history
// control on user land, and only rely on WebContents.loadURL for navigation.
// This helps us avoid Chromium's various optimizations so we can ensure renderer
// process is restarted everytime.
var NavigationController = (function() {
function NavigationController(webContents) {
this.webContents = webContents;
this.clearHistory();
// webContents may have already navigated to a page.
if (this.webContents._getURL()) {
this.currentIndex++;
this.history.push(this.webContents._getURL());
}
this.webContents.on('navigation-entry-commited', (function(_this) {
return function(event, url, inPage, replaceEntry) {
var currentEntry;
if (_this.inPageIndex > -1 && !inPage) {
// Navigated to a new page, clear in-page mark.
_this.inPageIndex = -1;
} else if (_this.inPageIndex === -1 && inPage) {
// Started in-page navigations.
_this.inPageIndex = _this.currentIndex;
}
if (_this.pendingIndex >= 0) {
// Go to index.
_this.currentIndex = _this.pendingIndex;
_this.pendingIndex = -1;
return _this.history[_this.currentIndex] = url;
} else if (replaceEntry) {
// Non-user initialized navigation.
return _this.history[_this.currentIndex] = url;
} else {
// Normal navigation. Clear history.
_this.history = _this.history.slice(0, _this.currentIndex + 1);
currentEntry = _this.history[_this.currentIndex];
if ((currentEntry != null ? currentEntry.url : void 0) !== url) {
_this.currentIndex++;
return _this.history.push(url);
}
}
};
})(this));
}
NavigationController.prototype.loadURL = function(url, options) {
if (options == null) {
options = {};
}
this.pendingIndex = -1;
this.webContents._loadURL(url, options);
return this.webContents.emit('load-url', url, options);
};
NavigationController.prototype.getURL = function() {
if (this.currentIndex === -1) {
return '';
} else {
return this.history[this.currentIndex];
}
};
NavigationController.prototype.stop = function() {
this.pendingIndex = -1;
return this.webContents._stop();
};
NavigationController.prototype.reload = function() {
this.pendingIndex = this.currentIndex;
return this.webContents._loadURL(this.getURL(), {});
};
NavigationController.prototype.reloadIgnoringCache = function() {
this.pendingIndex = this.currentIndex;
return this.webContents._loadURL(this.getURL(), {
extraHeaders: "pragma: no-cache\n"
});
};
NavigationController.prototype.canGoBack = function() {
return this.getActiveIndex() > 0;
};
NavigationController.prototype.canGoForward = function() {
return this.getActiveIndex() < this.history.length - 1;
};
NavigationController.prototype.canGoToIndex = function(index) {
return index >= 0 && index < this.history.length;
};
NavigationController.prototype.canGoToOffset = function(offset) {
return this.canGoToIndex(this.currentIndex + offset);
};
NavigationController.prototype.clearHistory = function() {
this.history = [];
this.currentIndex = -1;
this.pendingIndex = -1;
return this.inPageIndex = -1;
};
NavigationController.prototype.goBack = function() {
if (!this.canGoBack()) {
return;
}
this.pendingIndex = this.getActiveIndex() - 1;
if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
return this.webContents._goBack();
} else {
return this.webContents._loadURL(this.history[this.pendingIndex], {});
}
};
NavigationController.prototype.goForward = function() {
if (!this.canGoForward()) {
return;
}
this.pendingIndex = this.getActiveIndex() + 1;
if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
return this.webContents._goForward();
} else {
return this.webContents._loadURL(this.history[this.pendingIndex], {});
}
};
NavigationController.prototype.goToIndex = function(index) {
if (!this.canGoToIndex(index)) {
return;
}
this.pendingIndex = index;
return this.webContents._loadURL(this.history[this.pendingIndex], {});
};
NavigationController.prototype.goToOffset = function(offset) {
var pendingIndex;
if (!this.canGoToOffset(offset)) {
return;
}
pendingIndex = this.currentIndex + offset;
if (this.inPageIndex > -1 && pendingIndex >= this.inPageIndex) {
this.pendingIndex = pendingIndex;
return this.webContents._goToOffset(offset);
} else {
return this.goToIndex(pendingIndex);
}
};
NavigationController.prototype.getActiveIndex = function() {
if (this.pendingIndex === -1) {
return this.currentIndex;
} else {
return this.pendingIndex;
}
};
NavigationController.prototype.length = function() {
return this.history.length;
};
return NavigationController;
})();
module.exports = NavigationController;

View file

@ -1,6 +0,0 @@
powerMonitor = process.atomBinding('power_monitor').powerMonitor
EventEmitter = require('events').EventEmitter
powerMonitor.__proto__ = EventEmitter.prototype
module.exports = powerMonitor

View file

@ -0,0 +1,6 @@
const EventEmitter = require('events').EventEmitter;
const powerMonitor = process.atomBinding('power_monitor').powerMonitor;
powerMonitor.__proto__ = EventEmitter.prototype;
module.exports = powerMonitor;

View file

@ -1,3 +0,0 @@
bindings = process.atomBinding 'power_save_blocker'
module.exports = bindings.powerSaveBlocker

View file

@ -0,0 +1,5 @@
var powerSaveBlocker;
powerSaveBlocker = process.atomBinding('power_save_blocker').powerSaveBlocker;
module.exports = powerSaveBlocker;

View file

@ -1,64 +0,0 @@
app = require 'app'
throw new Error('Can not initialize protocol module before app is ready') unless app.isReady()
protocol = process.atomBinding('protocol').protocol
EventEmitter = require('events').EventEmitter
protocol.__proto__ = EventEmitter.prototype
GetWrappedCallback = (scheme, callback, notification) ->
wrappedCallback = (error) ->
if not callback?
if error
throw error
else
protocol.emit notification, scheme
else
callback error, scheme
# Compatibility with old api.
protocol.registerProtocol = (scheme, handler, callback) ->
protocol._registerProtocol scheme, handler, GetWrappedCallback(scheme, callback, 'registered')
protocol.unregisterProtocol = (scheme, callback) ->
protocol._unregisterProtocol scheme, GetWrappedCallback(scheme, callback, 'unregistered')
protocol.interceptProtocol = (scheme, handler, callback) ->
protocol._interceptProtocol scheme, handler, GetWrappedCallback(scheme, callback, 'intercepted')
protocol.uninterceptProtocol = (scheme, callback) ->
protocol._uninterceptProtocol scheme, GetWrappedCallback(scheme, callback, 'unintercepted')
protocol.RequestStringJob =
class RequestStringJob
constructor: ({mimeType, charset, data}) ->
if typeof data isnt 'string' and not data instanceof Buffer
throw new TypeError('Data should be string or Buffer')
@mimeType = mimeType ? 'text/plain'
@charset = charset ? 'UTF-8'
@data = String data
protocol.RequestBufferJob =
class RequestBufferJob
constructor: ({mimeType, encoding, data}) ->
if not data instanceof Buffer
throw new TypeError('Data should be Buffer')
@mimeType = mimeType ? 'application/octet-stream'
@encoding = encoding ? 'utf8'
@data = new Buffer(data)
protocol.RequestFileJob =
class RequestFileJob
constructor: (@path) ->
protocol.RequestErrorJob =
class RequestErrorJob
constructor: (@error) ->
protocol.RequestHttpJob =
class RequestHttpJob
constructor: ({@session, @url, @method, @referrer}) ->
module.exports = protocol

View file

@ -0,0 +1,31 @@
const app = require('electron').app;
if (!app.isReady()) {
throw new Error('Can not initialize protocol module before app is ready');
}
const protocol = process.atomBinding('protocol').protocol;
// Warn about removed APIs.
var logAndThrow = function(callback, message) {
console.error(message);
if (callback) {
return callback(new Error(message));
} else {
throw new Error(message);
}
};
protocol.registerProtocol = function(scheme, handler, callback) {
return logAndThrow(callback, 'registerProtocol API has been replaced by the register[File/Http/Buffer/String]Protocol API family, please switch to the new APIs.');
};
protocol.isHandledProtocol = function(scheme, callback) {
return logAndThrow(callback, 'isHandledProtocol API has been replaced by isProtocolHandled.');
};
protocol.interceptProtocol = function(scheme, handler, callback) {
return logAndThrow(callback, 'interceptProtocol API has been replaced by the intercept[File/Http/Buffer/String]Protocol API family, please switch to the new APIs.');
};
module.exports = protocol;

View file

@ -1,6 +0,0 @@
EventEmitter = require('events').EventEmitter
screen = process.atomBinding('screen').screen
screen.__proto__ = EventEmitter.prototype
module.exports = screen

View file

@ -0,0 +1,6 @@
const EventEmitter = require('events').EventEmitter;
const screen = process.atomBinding('screen').screen;
screen.__proto__ = EventEmitter.prototype;
module.exports = screen;

View file

@ -0,0 +1,33 @@
const EventEmitter = require('events').EventEmitter;
const bindings = process.atomBinding('session');
const PERSIST_PERFIX = 'persist:';
// Returns the Session from |partition| string.
exports.fromPartition = function(partition) {
if (partition == null) {
partition = '';
}
if (partition === '') {
return exports.defaultSession;
}
if (partition.startsWith(PERSIST_PERFIX)) {
return bindings.fromPartition(partition.substr(PERSIST_PERFIX.length), false);
} else {
return bindings.fromPartition(partition, true);
}
};
// Returns the default session.
Object.defineProperty(exports, 'defaultSession', {
enumerable: true,
get: function() {
return bindings.fromPartition('', false);
}
});
var wrapSession = function(session) {
// session is an EventEmitter.
return session.__proto__ = EventEmitter.prototype;
};
bindings._setWrapSession(wrapSession);

View file

@ -1,14 +0,0 @@
EventEmitter = require('events').EventEmitter
bindings = process.atomBinding 'tray'
Tray = bindings.Tray
Tray::__proto__ = EventEmitter.prototype
Tray::setContextMenu = (menu) ->
@_setContextMenu menu
@menu = menu # Keep a strong reference of menu.
# Keep compatibility with old APIs.
Tray::popContextMenu = Tray::popUpContextMenu
module.exports = Tray

View file

@ -0,0 +1,23 @@
const deprecate = require('electron').deprecate;
const EventEmitter = require('events').EventEmitter;
const Tray = process.atomBinding('tray').Tray;
Tray.prototype.__proto__ = EventEmitter.prototype;
Tray.prototype._init = function() {
// Deprecated.
deprecate.rename(this, 'popContextMenu', 'popUpContextMenu');
deprecate.event(this, 'clicked', 'click');
deprecate.event(this, 'double-clicked', 'double-click');
deprecate.event(this, 'right-clicked', 'right-click');
return deprecate.event(this, 'balloon-clicked', 'balloon-click');
};
Tray.prototype.setContextMenu = function(menu) {
this._setContextMenu(menu);
// Keep a strong reference of menu.
return this.menu = menu;
};
module.exports = Tray;

View file

@ -1,114 +0,0 @@
EventEmitter = require('events').EventEmitter
NavigationController = require './navigation-controller'
binding = process.atomBinding 'web_contents'
ipc = require 'ipc'
nextId = 0
getNextId = -> ++nextId
PDFPageSize =
A4:
custom_display_name: "A4"
height_microns: 297000
name: "ISO_A4"
is_default: "true"
width_microns: 210000
A3:
custom_display_name: "A3"
height_microns: 420000
name: "ISO_A3"
width_microns: 297000
Legal:
custom_display_name: "Legal"
height_microns: 355600
name: "NA_LEGAL"
width_microns: 215900
Letter:
custom_display_name: "Letter"
height_microns: 279400
name: "NA_LETTER"
width_microns: 215900
Tabloid:
height_microns: 431800
name: "NA_LEDGER"
width_microns: 279400
custom_display_name: "Tabloid"
wrapWebContents = (webContents) ->
# webContents is an EventEmitter.
webContents.__proto__ = EventEmitter.prototype
# WebContents::send(channel, args..)
webContents.send = (channel, args...) ->
@_send channel, [args...]
# Make sure webContents.executeJavaScript would run the code only when the
# web contents has been loaded.
webContents.loaded = false
webContents.once 'did-finish-load', -> @loaded = true
webContents.executeJavaScript = (code, hasUserGesture=false) ->
if @loaded
@_executeJavaScript code, hasUserGesture
else
webContents.once 'did-finish-load', @_executeJavaScript.bind(this, code, hasUserGesture)
# The navigation controller.
controller = new NavigationController(webContents)
for name, method of NavigationController.prototype when method instanceof Function
do (name, method) ->
webContents[name] = -> method.apply controller, arguments
# Dispatch IPC messages to the ipc module.
webContents.on 'ipc-message', (event, packed) ->
[channel, args...] = packed
ipc.emit channel, event, args...
webContents.on 'ipc-message-sync', (event, packed) ->
[channel, args...] = packed
Object.defineProperty event, 'returnValue', set: (value) -> event.sendReply JSON.stringify(value)
ipc.emit channel, event, args...
webContents.printToPDF = (options, callback) ->
printingSetting =
pageRage: []
mediaSize: {}
landscape: false
color: 2
headerFooterEnabled: false
marginsType: 0
isFirstRequest: false
requestID: getNextId()
previewModifiable: true
printToPDF: true
printWithCloudPrint: false
printWithPrivet: false
printWithExtension: false
deviceName: "Save as PDF"
generateDraftData: true
fitToPageEnabled: false
duplex: 0
copies: 1
collate: true
shouldPrintBackgrounds: false
shouldPrintSelectionOnly: false
if options.landscape
printingSetting.landscape = options.landscape
if options.marginsType
printingSetting.marginsType = options.marginsType
if options.printSelectionOnly
printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly
if options.printBackgrounds
printingSetting.shouldPrintBackgrounds = options.printBackground
if options.pageSize and PDFPageSize[options.pageSize]
printingSetting.mediaSize = PDFPageSize[options.pageSize]
else
printingSetting.mediaSize = PDFPageSize['A4']
@_printToPDF printingSetting, callback
binding._setWrapWebContents wrapWebContents
process.once 'exit', binding._clearWrapWebContents
module.exports.create = (options={}) ->
binding.create(options)

View file

@ -0,0 +1,233 @@
'use strict';
const EventEmitter = require('events').EventEmitter;
const deprecate = require('electron').deprecate;
const ipcMain = require('electron').ipcMain;
const NavigationController = require('electron').NavigationController;
const Menu = require('electron').Menu;
const binding = process.atomBinding('web_contents');
const debuggerBinding = process.atomBinding('debugger');
let slice = [].slice;
let nextId = 0;
let getNextId = function() {
return ++nextId;
};
let PDFPageSize = {
A5: {
custom_display_name: "A5",
height_microns: 210000,
name: "ISO_A5",
width_microns: 148000
},
A4: {
custom_display_name: "A4",
height_microns: 297000,
name: "ISO_A4",
is_default: "true",
width_microns: 210000
},
A3: {
custom_display_name: "A3",
height_microns: 420000,
name: "ISO_A3",
width_microns: 297000
},
Legal: {
custom_display_name: "Legal",
height_microns: 355600,
name: "NA_LEGAL",
width_microns: 215900
},
Letter: {
custom_display_name: "Letter",
height_microns: 279400,
name: "NA_LETTER",
width_microns: 215900
},
Tabloid: {
height_microns: 431800,
name: "NA_LEDGER",
width_microns: 279400,
custom_display_name: "Tabloid"
}
};
// Following methods are mapped to webFrame.
const webFrameMethods = [
'executeJavaScript',
'insertText',
'setZoomFactor',
'setZoomLevel',
'setZoomLevelLimits'
];
let wrapWebContents = function(webContents) {
// webContents is an EventEmitter.
var controller, method, name, ref1;
webContents.__proto__ = EventEmitter.prototype;
// Every remote callback from renderer process would add a listenter to the
// render-view-deleted event, so ignore the listenters warning.
webContents.setMaxListeners(0);
// WebContents::send(channel, args..)
webContents.send = function() {
var args, channel;
channel = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
return this._send(channel, slice.call(args));
};
// The navigation controller.
controller = new NavigationController(webContents);
ref1 = NavigationController.prototype;
for (name in ref1) {
method = ref1[name];
if (method instanceof Function) {
(function(name, method) {
return webContents[name] = function() {
return method.apply(controller, arguments);
};
})(name, method);
}
}
// Mapping webFrame methods.
for (let method of webFrameMethods) {
webContents[method] = function() {
let args = Array.prototype.slice.call(arguments);
this.send('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, args);
};
}
// Make sure webContents.executeJavaScript would run the code only when the
// webContents has been loaded.
const executeJavaScript = webContents.executeJavaScript;
webContents.executeJavaScript = function(code, hasUserGesture) {
if (this.getURL() && !this.isLoading())
return executeJavaScript.call(this, code, hasUserGesture);
else
return this.once('did-finish-load', executeJavaScript.bind(this, code, hasUserGesture));
};
// Dispatch IPC messages to the ipc module.
webContents.on('ipc-message', function(event, packed) {
var args, channel;
channel = packed[0], args = 2 <= packed.length ? slice.call(packed, 1) : [];
return ipcMain.emit.apply(ipcMain, [channel, event].concat(slice.call(args)));
});
webContents.on('ipc-message-sync', function(event, packed) {
var args, channel;
channel = packed[0], args = 2 <= packed.length ? slice.call(packed, 1) : [];
Object.defineProperty(event, 'returnValue', {
set: function(value) {
return event.sendReply(JSON.stringify(value));
}
});
return ipcMain.emit.apply(ipcMain, [channel, event].concat(slice.call(args)));
});
// Handle context menu action request from pepper plugin.
webContents.on('pepper-context-menu', function(event, params) {
var menu;
menu = Menu.buildFromTemplate(params.menu);
return menu.popup(params.x, params.y);
});
// This error occurs when host could not be found.
webContents.on('did-fail-provisional-load', function() {
var args;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
// Calling loadURL during this event might cause crash, so delay the event
// until next tick.
return setImmediate((function(_this) {
return function() {
return _this.emit.apply(_this, ['did-fail-load'].concat(slice.call(args)));
};
})(this));
});
// Delays the page-title-updated event to next tick.
webContents.on('-page-title-updated', function() {
var args;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
return setImmediate((function(_this) {
return function() {
return _this.emit.apply(_this, ['page-title-updated'].concat(slice.call(args)));
};
})(this));
});
// Deprecated.
deprecate.rename(webContents, 'loadUrl', 'loadURL');
deprecate.rename(webContents, 'getUrl', 'getURL');
deprecate.event(webContents, 'page-title-set', 'page-title-updated', function() {
var args;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
return this.emit.apply(this, ['page-title-set'].concat(slice.call(args)));
});
return webContents.printToPDF = function(options, callback) {
var printingSetting;
printingSetting = {
pageRage: [],
mediaSize: {},
landscape: false,
color: 2,
headerFooterEnabled: false,
marginsType: 0,
isFirstRequest: false,
requestID: getNextId(),
previewModifiable: true,
printToPDF: true,
printWithCloudPrint: false,
printWithPrivet: false,
printWithExtension: false,
deviceName: "Save as PDF",
generateDraftData: true,
fitToPageEnabled: false,
duplex: 0,
copies: 1,
collate: true,
shouldPrintBackgrounds: false,
shouldPrintSelectionOnly: false
};
if (options.landscape) {
printingSetting.landscape = options.landscape;
}
if (options.marginsType) {
printingSetting.marginsType = options.marginsType;
}
if (options.printSelectionOnly) {
printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly;
}
if (options.printBackground) {
printingSetting.shouldPrintBackgrounds = options.printBackground;
}
if (options.pageSize && PDFPageSize[options.pageSize]) {
printingSetting.mediaSize = PDFPageSize[options.pageSize];
} else {
printingSetting.mediaSize = PDFPageSize['A4'];
}
return this._printToPDF(printingSetting, callback);
};
};
// Wrapper for native class.
let wrapDebugger = function(webContentsDebugger) {
// debugger is an EventEmitter.
webContentsDebugger.__proto__ = EventEmitter.prototype;
};
binding._setWrapWebContents(wrapWebContents);
debuggerBinding._setWrapDebugger(wrapDebugger);
module.exports.create = function(options) {
if (options == null) {
options = {};
}
return binding.create(options);
};

Some files were not shown because too many files have changed in this diff Show more