Browse Source

Merge branch 'master' into desktop-capture-api

Cheng Zhao 9 years ago
parent
commit
04f7ceab73
100 changed files with 2541 additions and 998 deletions
  1. 2 0
      .gitignore
  2. 1 0
      .travis.yml
  3. 24 0
      CODE_OF_CONDUCT.md
  4. 80 0
      CONTRIBUTING-ko.md
  5. 4 2
      CONTRIBUTING.md
  6. 28 12
      README-ko.md
  7. 15 8
      README.md
  8. 53 16
      atom.gyp
  9. 11 12
      atom/app/atom_content_client.cc
  10. 1 1
      atom/app/atom_content_client.h
  11. 2 0
      atom/app/atom_library_main.mm
  12. 31 17
      atom/app/atom_main.cc
  13. 35 11
      atom/app/atom_main_delegate.cc
  14. 7 10
      atom/app/node_main.cc
  15. 7 2
      atom/app/uv_task_runner.cc
  16. 1 0
      atom/app/uv_task_runner.h
  17. 138 45
      atom/browser/api/atom_api_app.cc
  18. 25 5
      atom/browser/api/atom_api_app.h
  19. 51 15
      atom/browser/api/atom_api_auto_updater.cc
  20. 12 12
      atom/browser/api/atom_api_auto_updater.h
  21. 12 11
      atom/browser/api/atom_api_cookies.cc
  22. 6 6
      atom/browser/api/atom_api_cookies.h
  23. 30 33
      atom/browser/api/atom_api_download_item.cc
  24. 6 9
      atom/browser/api/atom_api_download_item.h
  25. 3 3
      atom/browser/api/atom_api_global_shortcut.h
  26. 1 0
      atom/browser/api/atom_api_menu.cc
  27. 3 3
      atom/browser/api/atom_api_menu.h
  28. 3 3
      atom/browser/api/atom_api_power_monitor.h
  29. 3 4
      atom/browser/api/atom_api_power_save_blocker.h
  30. 3 18
      atom/browser/api/atom_api_protocol.cc
  31. 1 1
      atom/browser/api/atom_api_screen.cc
  32. 106 32
      atom/browser/api/atom_api_session.cc
  33. 13 9
      atom/browser/api/atom_api_session.h
  34. 20 10
      atom/browser/api/atom_api_tray.cc
  35. 7 7
      atom/browser/api/atom_api_tray.h
  36. 219 117
      atom/browser/api/atom_api_web_contents.cc
  37. 27 10
      atom/browser/api/atom_api_web_contents.h
  38. 150 69
      atom/browser/api/atom_api_window.cc
  39. 25 15
      atom/browser/api/atom_api_window.h
  40. 7 7
      atom/browser/api/frame_subscriber.cc
  41. 34 30
      atom/browser/api/lib/app.coffee
  42. 0 6
      atom/browser/api/lib/atom-delegate.coffee
  43. 8 20
      atom/browser/api/lib/auto-updater.coffee
  44. 6 0
      atom/browser/api/lib/auto-updater/auto-updater-native.coffee
  45. 42 0
      atom/browser/api/lib/auto-updater/auto-updater-win.coffee
  46. 67 0
      atom/browser/api/lib/auto-updater/squirrel-update-win.coffee
  47. 45 26
      atom/browser/api/lib/browser-window.coffee
  48. 2 2
      atom/browser/api/lib/dialog.coffee
  49. 55 0
      atom/browser/api/lib/exports/electron.coffee
  50. 1 3
      atom/browser/api/lib/global-shortcut.coffee
  51. 3 0
      atom/browser/api/lib/ipc-main.coffee
  52. 5 2
      atom/browser/api/lib/ipc.coffee
  53. 12 4
      atom/browser/api/lib/menu-item.coffee
  54. 8 5
      atom/browser/api/lib/menu.coffee
  55. 15 15
      atom/browser/api/lib/navigation-controller.coffee
  56. 3 2
      atom/browser/api/lib/power-monitor.coffee
  57. 2 2
      atom/browser/api/lib/power-save-blocker.coffee
  58. 3 2
      atom/browser/api/lib/protocol.coffee
  59. 2 2
      atom/browser/api/lib/screen.coffee
  60. 23 0
      atom/browser/api/lib/session.coffee
  61. 11 6
      atom/browser/api/lib/tray.coffee
  62. 27 7
      atom/browser/api/lib/web-contents.coffee
  63. 83 0
      atom/browser/api/save_page_handler.cc
  64. 60 0
      atom/browser/api/save_page_handler.h
  65. 17 5
      atom/browser/api/trackable_object.cc
  66. 33 10
      atom/browser/api/trackable_object.h
  67. 3 3
      atom/browser/atom_access_token_store.cc
  68. 35 8
      atom/browser/atom_browser_client.cc
  69. 23 0
      atom/browser/atom_browser_client.h
  70. 24 4
      atom/browser/atom_browser_context.cc
  71. 11 1
      atom/browser/atom_browser_context.h
  72. 64 17
      atom/browser/atom_browser_main_parts.cc
  73. 20 5
      atom/browser/atom_browser_main_parts.h
  74. 0 73
      atom/browser/atom_browser_main_parts_linux.cc
  75. 1 1
      atom/browser/atom_browser_main_parts_mac.mm
  76. 225 0
      atom/browser/atom_browser_main_parts_posix.cc
  77. 40 0
      atom/browser/atom_resource_dispatcher_host_delegate.cc
  78. 31 0
      atom/browser/atom_resource_dispatcher_host_delegate.h
  79. 14 3
      atom/browser/auto_updater.cc
  80. 31 4
      atom/browser/auto_updater.h
  81. 0 45
      atom/browser/auto_updater_delegate.h
  82. 0 17
      atom/browser/auto_updater_linux.cc
  83. 11 15
      atom/browser/auto_updater_mac.mm
  84. 0 17
      atom/browser/auto_updater_win.cc
  85. 22 5
      atom/browser/bridge_task_runner.cc
  86. 12 1
      atom/browser/bridge_task_runner.h
  87. 45 19
      atom/browser/browser.cc
  88. 19 8
      atom/browser/browser.h
  89. 3 0
      atom/browser/browser_linux.cc
  90. 3 0
      atom/browser/browser_mac.mm
  91. 4 16
      atom/browser/browser_observer.h
  92. 18 7
      atom/browser/browser_win.cc
  93. 30 1
      atom/browser/common_web_contents_delegate.cc
  94. 14 1
      atom/browser/common_web_contents_delegate.h
  95. 6 5
      atom/browser/default_app/default_app.js
  96. 23 5
      atom/browser/default_app/index.html
  97. 35 14
      atom/browser/default_app/main.js
  98. 16 1
      atom/browser/javascript_environment.cc
  99. 4 4
      atom/browser/lib/chrome-extension.coffee
  100. 14 14
      atom/browser/lib/guest-view-manager.coffee

+ 2 - 0
.gitignore

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

+ 1 - 0
.travis.yml

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

+ 24 - 0
CODE_OF_CONDUCT.md

@@ -0,0 +1,24 @@
+# Contributor Code of Conduct
+
+As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
+
+We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
+
+Examples of unacceptable behavior by participants include:
+
+- The use of sexualized language or imagery
+- Personal attacks
+- Trolling or insulting/derogatory comments
+- Public or private harassment
+- Publishing other's private information, such as physical or electronic addresses, without explicit permission
+- Other unethical or unprofessional conduct
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a project maintainer at [[email protected]](mailto:[email protected]). 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 - 0
CONTRIBUTING-ko.md

@@ -0,0 +1,80 @@
+# Electron에 기여하기
+
+:+1::tada: 먼저, 이 프로젝트에 기여해주셔서 감사합니다! :tada::+1:
+
+이 프로젝트는 기여자 규약 [행동강령](CODE_OF_CONDUCT.md)을 준수합니다. 따라서 이
+프로젝트의 개발에 참여하려면 이 규약을 지켜야 합니다. 받아들일 수 없는 행위를 발견했을
+경우 [email protected]로 보고 하십시오.
+
+다음 항목들은 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(코드 검사기)의 경고를 제거했을 때

+ 4 - 2
CONTRIBUTING.md

@@ -2,8 +2,9 @@
 
 :+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
 
-This project adheres to the [Contributor Covenant 1.2](http://contributor-covenant.org/version/1/2/0).
-By participating, you are expected to uphold this code. Please report unacceptable behavior to [email protected].
+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 [email protected].
 
 The following is a set of guidelines for contributing to Electron.
 These are just guidelines, not rules, use your best judgment and feel free to
@@ -57,6 +58,7 @@ possible with your report. If you can, please include:
 * Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
 * Limit the first line to 72 characters or less
 * Reference issues and pull requests liberally
+* When only changing documentation, include `[ci skip]` in the commit description
 * Consider starting the commit message with an applicable emoji:
   * :art: `:art:` when improving the format/structure of the code
   * :racehorse: `:racehorse:` when improving performance

+ 28 - 12
README-ko.md

@@ -8,18 +8,26 @@
 
 :zap: *프레임워크 이름이 Atom Shell에서 Electron으로 변경되었습니다* :zap:
 
-Electron 프레임워크는 JavaScript, HTML 그리고 CSS를 사용하여 Cross-Platform 데스크톱 어플리케이션을 개발할 수 있도록 해주는 프레임워크입니다. 이 프레임워크는 [io.js](http://iojs.org) 와
-[Chromium](http://www.chromium.org)을 기반으로 만들어 졌으며 [Atom Editor](https://github.com/atom/atom)에 사용되고 있습니다.
+Electron 프레임워크는 JavaScript, HTML 그리고 CSS를 사용하여
+Cross-Platform 데스크톱 어플리케이션을 개발할 수 있도록 해주는 프레임워크입니다.
+[Node.js](https://nodejs.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)를 팔로우 하세요.
 
-이 프로젝트는 기여자 규약 1.2를 준수합니다. 이 프로젝트에 참여할 때 코드를 유지해야 합니다. 받아들일 수 없는 행동은 [email protected]로 보고 하십시오.
+이 프로젝트는 기여자 규약 [행동강령](CODE_OF_CONDUCT.md)을 준수합니다. 따라서 이
+프로젝트의 개발에 참여하려면 이 규약을 지켜야 합니다. 받아들일 수 없는 행위를 발견했을
+경우 [email protected]로 보고 하십시오.
 
 ## 다운로드
 
-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` 커맨드를 등록하고 전역에 설치합니다.
@@ -35,8 +43,9 @@ npm install electron-prebuilt --save-dev
 
 ## 참조 문서
 
-[Docs](https://github.com/atom/electron/tree/master/docs/README.md)에 개발 가이드와 API 레퍼런스가 있습니다.
-Electron을 빌드 하는 방법과 프로젝트에 기여하는 방법도 문서에 포함되어 있으니 참고하시기 바랍니다.
+[Docs](https://github.com/atom/electron/tree/master/docs/README.md)에 개발 지침과
+API 레퍼런스가 있습니다. Electron을 빌드 하는 방법과 프로젝트에 기여하는법 또한 문서에
+포함되어 있으니 참고하시기 바랍니다.
 
 ## 참조 문서 (번역)
 
@@ -47,12 +56,19 @@ Electron을 빌드 하는 방법과 프로젝트에 기여하는 방법도 문
 - [중국어 간체](https://github.com/atom/electron/tree/master/docs-translations/zh-CN)
 - [중국어 번체](https://github.com/atom/electron/tree/master/docs-translations/zh-TW)
 
+## 시작하기
+
+[`atom/electron-quick-start`](https://github.com/atom/electron-quick-start)
+저장소를 클론하여 Electron을 간단히 접해볼 수 있습니다.
+
 ## 커뮤니티
 
-다음 링크를 통해 커뮤니티에 질문을 올리거나 토론을 나누실 수 있습니다:
+다음 링크를 통해 커뮤니티에 질문을 올리거나 토론을 나 수 있습니다:
 
-- Atom 포럼의 [`electron`](http://discuss.atom.io/category/electron) 카테고리
-- Freenode 채팅의 `#atom-shell` 채널 
+- Atom 포럼의 [`electron`](http://discuss.atom.io/c/electron) 카테고리
+- Freenode 채팅의 `#atom-shell` 채널
 - Slack의 [`Atom`](http://atom-slack.herokuapp.com/) 채널
 
-[awesome-electron](https://github.com/sindresorhus/awesome-electron) 프로젝트엔 커뮤니티가 운영중인 유용한 예제 어플리케이션과 도구, 리소스가 있으니 한번 참고해 보시기 바랍니다.
+[awesome-electron](https://github.com/sindresorhus/awesome-electron) 프로젝트에
+커뮤니티가 운영중인 유용한 예제 어플리케이션과 도구, 리소스가 있으니 한번 참고해 보시기
+바랍니다.

+ 15 - 8
README.md

@@ -7,20 +7,20 @@
 :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 [email protected].
+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 [email protected].
 
 ## 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
@@ -52,15 +52,22 @@ contains documents describing how to build and contribute to Electron.
 - [Spanish](https://github.com/atom/electron/tree/master/docs-translations/es)
 - [Simplified Chinese](https://github.com/atom/electron/tree/master/docs-translations/zh-CN)
 - [Traditional Chinese](https://github.com/atom/electron/tree/master/docs-translations/zh-TW)
+- [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
 
-You can ask questions and interact with the community in the following 
+You can ask questions and interact with the community in the following
 locations:
-- [`electron`](http://discuss.atom.io/category/electron) category on the Atom 
+- [`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)*
 
-Check out [awesome-electron](https://github.com/sindresorhus/awesome-electron) 
+Check out [awesome-electron](https://github.com/sindresorhus/awesome-electron)
 for a community maintained list of useful example apps, tools and resources.

+ 53 - 16
atom.gyp

@@ -4,7 +4,7 @@
     'product_name%': 'Electron',
     'company_name%': 'GitHub, Inc',
     'company_abbr%': 'github',
-    'version%': '0.33.1',
+    'version%': '0.36.0',
   },
   'includes': [
     'filenames.gypi',
@@ -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,7 +106,21 @@
                 '<@(locale_dirs)',
               ],
             },
-          ]
+          ],
+          '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"
           'dependencies': [
             'make_locale_paks',
@@ -290,12 +301,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': [
@@ -398,9 +425,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,
@@ -444,12 +468,6 @@
                 '<@(copied_libraries)',
               ],
             },
-            {
-              'destination': '<(PRODUCT_DIR)/<(product_name) Framework.framework/Versions/A/Resources',
-              'files': [
-                '<(PRODUCT_DIR)/crashpad_handler',
-              ],
-            },
           ],
           'postbuilds': [
             {
@@ -481,6 +499,25 @@
               ],
             },
           ],
+          '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
         {
           'target_name': '<(project_name)_helper',

+ 11 - 12
atom/app/atom_content_client.cc

@@ -31,8 +31,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. :(
@@ -47,7 +47,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,
@@ -81,25 +81,24 @@ std::string AtomContentClient::GetUserAgent() const {
 }
 
 void AtomContentClient::AddAdditionalSchemes(
-    std::vector<std::string>* standard_schemes,
+    std::vector<url::SchemeWithType>* 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());
+    std::vector<std::string> schemes = base::SplitString(
+        custom_schemes, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+    for (const std::string& scheme : schemes)
+      standard_schemes->push_back({scheme.c_str(), url::SCHEME_WITHOUT_PORT});
   }
-  standard_schemes->push_back("chrome-extension");
+  standard_schemes->push_back({"chrome-extension", url::SCHEME_WITHOUT_PORT});
 }
 
 void AtomContentClient::AddPepperPlugins(
     std::vector<content::PepperPluginInfo>* plugins) {
   auto command_line = base::CommandLine::ForCurrentProcess();
-  auto flash_path = command_line->GetSwitchValueNative(
+  auto flash_path = command_line->GetSwitchValuePath(
       switches::kPpapiFlashPath);
   if (flash_path.empty())
     return;
@@ -108,7 +107,7 @@ void AtomContentClient::AddPepperPlugins(
       switches::kPpapiFlashVersion);
 
   plugins->push_back(
-      CreatePepperFlashInfo(base::FilePath(flash_path), flash_version));
+      CreatePepperFlashInfo(flash_path, flash_version));
 }
 
 }  // namespace atom

+ 1 - 1
atom/app/atom_content_client.h

@@ -22,7 +22,7 @@ class AtomContentClient : public brightray::ContentClient {
   std::string GetProduct() const override;
   std::string GetUserAgent() 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;

+ 2 - 0
atom/app/atom_library_main.mm

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

+ 31 - 17
atom/app/atom_main.cc

@@ -5,7 +5,6 @@
 #include "atom/app/atom_main.h"
 
 #include <stdlib.h>
-#include <string.h>
 
 #if defined(OS_WIN)
 #include <stdio.h>
@@ -36,9 +35,32 @@
 #include "base/at_exit.h"
 #include "base/i18n/icu_util.h"
 
+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
+}
 
-namespace {
+bool IsRunAsNode() {
+  return IsEnvSet(kRunAsNode) || IsEnvSet(kOldRunAsNode);
+}
+
+#if defined(OS_WIN)
+bool IsCygwin() {
+  std::string os;
+  scoped_ptr<base::Environment> env(base::Environment::Create());
+  return env->GetVar("OS", &os) && os == "cygwin";
+}
 
 // Win8.1 supports monitor-specific DPI scaling.
 bool SetProcessDpiAwarenessWrapper(PROCESS_DPI_AWARENESS value) {
@@ -77,24 +99,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 (!IsCygwin() && !IsEnvSet("ELECTRON_NO_ATTACH_CONSOLE")) {
     AttachConsole(ATTACH_PARENT_PROCESS);
 
     FILE* dontcare;
     freopen_s(&dontcare, "CON", "w", stdout);
     freopen_s(&dontcare, "CON", "w", stderr);
-    freopen_s(&dontcare, "CON", "r", stdin);
   }
 
   // Convert argv to to UTF8
@@ -131,16 +151,12 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) {
     }
   }
 
-  std::string node_indicator, crash_service_indicator;
-  if (env->GetVar("ATOM_SHELL_INTERNAL_RUN_AS_NODE", &node_indicator) &&
-      node_indicator == "1") {
+  if (IsRunAsNode()) {
     // Now that argv conversion is done, we can finally start.
     base::AtExitManager atexit_manager;
     base::i18n::InitializeICU();
     return atom::NodeMain(argc, argv);
-  } else if (env->GetVar("ATOM_SHELL_INTERNAL_CRASH_SERVICE",
-                         &crash_service_indicator) &&
-      crash_service_indicator == "1") {
+  } else if (IsEnvSet("ATOM_SHELL_INTERNAL_CRASH_SERVICE")) {
     return crash_service::Main(cmd);
   }
 
@@ -164,8 +180,7 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) {
 #elif defined(OS_LINUX)  // defined(OS_WIN)
 
 int main(int argc, const char* argv[]) {
-  char* node_indicator = getenv("ATOM_SHELL_INTERNAL_RUN_AS_NODE");
-  if (node_indicator != NULL && strcmp(node_indicator, "1") == 0) {
+  if (IsRunAsNode()) {
     base::i18n::InitializeICU();
     base::AtExitManager atexit_manager;
     return atom::NodeMain(argc, const_cast<char**>(argv));
@@ -182,8 +197,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));
   }
 

+ 35 - 11
atom/app/atom_main_delegate.cc

@@ -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,21 @@
 #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/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 +38,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;
@@ -41,15 +58,29 @@ bool AtomMainDelegate::BasicStartupComplete(int* exit_code) {
 #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);
 }
@@ -71,16 +102,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);
 

+ 7 - 10
atom/app/node_main.cc

@@ -7,8 +7,8 @@
 #include "atom/app/uv_task_runner.h"
 #include "atom/browser/javascript_environment.h"
 #include "atom/browser/node_debugger.h"
-#include "atom/common/node_includes.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"
@@ -19,25 +19,22 @@ namespace atom {
 int NodeMain(int argc, char *argv[]) {
   base::CommandLine::Init(argc, 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);
-
   int exit_code = 1;
   {
     // 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();
-    gin::IsolateHolder::Initialize(
-        gin::IsolateHolder::kNonStrictMode,
-        gin::ArrayBufferAllocator::SharedInstance());
-
     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(), loop, gin_env.context(), argc, argv,
         exec_argc, exec_argv);

+ 7 - 2
atom/app/uv_task_runner.cc

@@ -48,8 +48,13 @@ void UvTaskRunner::OnTimeout(uv_timer_t* timer) {
 
   self->tasks_[timer].Run();
   self->tasks_.erase(timer);
-  uv_unref(reinterpret_cast<uv_handle_t*>(timer));
-  delete 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

+ 1 - 0
atom/app/uv_task_runner.h

@@ -31,6 +31,7 @@ class UvTaskRunner : public base::SingleThreadTaskRunner {
 
  private:
   static void OnTimeout(uv_timer_t* timer);
+  static void OnClose(uv_handle_t* handle);
 
   uv_loop_t* loop_;
 

+ 138 - 45
atom/browser/api/atom_api_app.cc

@@ -7,26 +7,29 @@
 #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/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"
@@ -61,21 +64,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 +87,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 +141,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);
 }
@@ -158,6 +182,11 @@ void App::OnWindowAllClosed() {
 
 void App::OnQuit() {
   Emit("quit");
+
+  if (process_singleton_.get()) {
+    process_singleton_->Cleanup();
+    process_singleton_.reset();
+  }
 }
 
 void App::OnOpenFile(bool* prevent_default, const std::string& file_path) {
@@ -177,26 +206,62 @@ void App::OnWillFinishLaunching() {
 }
 
 void App::OnFinishLaunching() {
-  // Create the defaultSession.
+  Emit("ready");
+}
+
+void App::OnLogin(LoginHandler* login_handler) {
   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());
+  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();
+}
 
-  Emit("ready");
+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::OnSelectCertificate(
+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,22 +307,36 @@ 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);
 }
 
 std::string App::GetLocale() {
   return l10n_util::GetApplicationLocale("");
 }
 
-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_);
+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(
@@ -265,6 +344,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))
@@ -275,6 +355,8 @@ 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))
@@ -282,9 +364,10 @@ mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder(
       .SetMethod("setPath", &App::SetPath)
       .SetMethod("getPath", &App::GetPath)
       .SetMethod("setDesktopName", &App::SetDesktopName)
-      .SetMethod("setAppUserModelId", &App::SetAppUserModelId)
+      .SetMethod("allowNTLMCredentialsForAllDomains",
+                 &App::AllowNTLMCredentialsForAllDomains)
       .SetMethod("getLocale", &App::GetLocale)
-      .SetProperty("defaultSession", &App::DefaultSession);
+      .SetMethod("makeSingleInstance", &App::MakeSingleInstance);
 }
 
 // static
@@ -301,6 +384,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);

+ 25 - 5
atom/browser/api/atom_api_app.h

@@ -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:
@@ -44,7 +48,22 @@ class App : public mate::EventEmitter,
   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,11 +83,12 @@ class App : public mate::EventEmitter,
                const base::FilePath& path);
 
   void SetDesktopName(const std::string& desktop_name);
-  void SetAppUserModelId(const std::string& app_id);
+  void AllowNTLMCredentialsForAllDomains(bool should_allow);
+  bool MakeSingleInstance(
+      const ProcessSingleton::NotificationCallback& callback);
   std::string GetLocale();
-  v8::Local<v8::Value> DefaultSession(v8::Isolate* isolate);
 
-  v8::Global<v8::Value> default_session_;
+  scoped_ptr<ProcessSingleton> process_singleton_;
 
   DISALLOW_COPY_AND_ASSIGN(App);
 };

+ 51 - 15
atom/browser/api/atom_api_auto_updater.cc

@@ -5,12 +5,31 @@
 #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"
 
+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 {
 
 namespace api {
@@ -20,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() {
@@ -42,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

+ 12 - 12
atom/browser/api/atom_api_auto_updater.h

@@ -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);
 };
 

+ 12 - 11
atom/browser/api/atom_api_cookies.cc

@@ -204,7 +204,7 @@ void Cookies::GetCookiesOnIOThread(scoped_ptr<base::DictionaryValue> filter,
               Passed(&filter), callback))) {
     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
         base::Bind(&RunGetCookiesCallbackOnUIThread, isolate(),
-                   "Url is not valid", net::CookieList(), callback));
+                   "URL is not valid", net::CookieList(), callback));
   }
 }
 
@@ -229,7 +229,7 @@ void Cookies::Remove(const mate::Dictionary& details,
     error_message = "Details(url, name) of removing cookie are required.";
   }
   if (error_message.empty() && !url.is_valid()) {
-    error_message = "Url is not valid.";
+    error_message = "URL is not valid.";
   }
   if (!error_message.empty()) {
      RunRemoveCookiesCallbackOnUIThread(isolate(), error_message, callback);
@@ -261,7 +261,7 @@ void Cookies::Set(const base::DictionaryValue& options,
 
   GURL gurl(url);
   if (error_message.empty() && !gurl.is_valid()) {
-    error_message = "Url is not valid.";
+    error_message = "URL is not valid.";
   }
 
   if (!error_message.empty()) {
@@ -322,14 +322,6 @@ void Cookies::OnSetCookies(const CookiesCallback& callback,
                  callback));
 }
 
-mate::ObjectTemplateBuilder Cookies::GetObjectTemplateBuilder(
-    v8::Isolate* isolate) {
-  return mate::ObjectTemplateBuilder(isolate)
-      .SetMethod("get", &Cookies::Get)
-      .SetMethod("remove", &Cookies::Remove)
-      .SetMethod("set", &Cookies::Set);
-}
-
 net::CookieStore* Cookies::GetCookieStore() {
   return request_context_getter_->GetURLRequestContext()->cookie_store();
 }
@@ -341,6 +333,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

+ 6 - 6
atom/browser/api/atom_api_cookies.h

@@ -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"
 
@@ -33,7 +33,7 @@ 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>)>
@@ -42,6 +42,10 @@ class Cookies : public mate::Wrappable {
   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();
@@ -70,10 +74,6 @@ class Cookies : public mate::Wrappable {
   void OnSetCookies(const CookiesCallback& callback,
                     bool set_success);
 
-  // mate::Wrappable:
-  mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
-      v8::Isolate* isolate) override;
-
  private:
   // Must be called on IO thread.
   net::CookieStore* GetCookieStore();

+ 30 - 33
atom/browser/api/atom_api_download_item.cc

@@ -6,6 +6,7 @@
 
 #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"
@@ -30,7 +31,7 @@ struct Converter<content::DownloadItem::DownloadState> {
         download_state = "cancelled";
         break;
       case content::DownloadItem::INTERRUPTED:
-        download_state = "interrputed";
+        download_state = "interrupted";
         break;
       default:
         break;
@@ -69,29 +70,20 @@ DownloadItem::DownloadItem(content::DownloadItem* download_item) :
 }
 
 DownloadItem::~DownloadItem() {
-  Destroy();
-}
-
-void DownloadItem::Destroy() {
-  if (download_item_) {
-    download_item_->RemoveObserver(this);
-    auto iter = g_download_item_objects.find(download_item_->GetId());
-    if (iter != g_download_item_objects.end())
-      g_download_item_objects.erase(iter);
-    download_item_ = nullptr;
-  }
-}
-
-bool DownloadItem::IsDestroyed() const {
-  return download_item_ == nullptr;
+  if (download_item_)
+    OnDownloadDestroyed(download_item_);
 }
 
 void DownloadItem::OnDownloadUpdated(content::DownloadItem* item) {
   download_item_->IsDone() ? Emit("done", item->GetState()) : Emit("updated");
 }
 
-void DownloadItem::OnDownloadDestroyed(content::DownloadItem* download) {
-  Destroy();
+void DownloadItem::OnDownloadDestroyed(content::DownloadItem* download_item) {
+  download_item_->RemoveObserver(this);
+  auto iter = g_download_item_objects.find(download_item_->GetId());
+  if (iter != g_download_item_objects.end())
+    g_download_item_objects.erase(iter);
+  download_item_ = nullptr;
 }
 
 int64 DownloadItem::GetReceivedBytes() {
@@ -102,7 +94,7 @@ int64 DownloadItem::GetTotalBytes() {
   return download_item_->GetTotalBytes();
 }
 
-const GURL& DownloadItem::GetUrl() {
+const GURL& DownloadItem::GetURL() {
   return download_item_->GetURL();
 }
 
@@ -115,7 +107,7 @@ bool DownloadItem::HasUserGesture() {
 }
 
 std::string DownloadItem::GetFilename() {
-  return base::UTF16ToUTF8(net::GenerateFileName(GetUrl(),
+  return base::UTF16ToUTF8(net::GenerateFileName(GetURL(),
                            GetContentDisposition(),
                            std::string(),
                            download_item_->GetSuggestedFilename(),
@@ -143,15 +135,17 @@ void DownloadItem::Cancel() {
   download_item_->Cancel(true);
 }
 
-mate::ObjectTemplateBuilder DownloadItem::GetObjectTemplateBuilder(
-    v8::Isolate* isolate) {
-  return mate::ObjectTemplateBuilder(isolate)
+// static
+void DownloadItem::BuildPrototype(v8::Isolate* isolate,
+                                  v8::Local<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("getURL", &DownloadItem::GetURL)
       .SetMethod("getMimeType", &DownloadItem::GetMimeType)
       .SetMethod("hasUserGesture", &DownloadItem::HasUserGesture)
       .SetMethod("getFilename", &DownloadItem::GetFilename)
@@ -159,14 +153,6 @@ mate::ObjectTemplateBuilder DownloadItem::GetObjectTemplateBuilder(
       .SetMethod("setSavePath", &DownloadItem::SetSavePath);
 }
 
-void SetWrapDownloadItem(const WrapDownloadItemCallback& callback) {
-  g_wrap_download_item = callback;
-}
-
-void ClearWrapDownloadItem() {
-  g_wrap_download_item.Reset();
-}
-
 // static
 mate::Handle<DownloadItem> DownloadItem::Create(
     v8::Isolate* isolate, content::DownloadItem* item) {
@@ -182,6 +168,18 @@ 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
@@ -193,7 +191,6 @@ void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
   v8::Isolate* isolate = context->GetIsolate();
   mate::Dictionary dict(isolate, exports);
   dict.SetMethod("_setWrapDownloadItem", &atom::api::SetWrapDownloadItem);
-  dict.SetMethod("_clearWrapDownloadItem", &atom::api::ClearWrapDownloadItem);
 }
 
 }  // namespace

+ 6 - 9
atom/browser/api/atom_api_download_item.h

@@ -17,7 +17,7 @@ namespace atom {
 
 namespace api {
 
-class DownloadItem : public mate::EventEmitter,
+class DownloadItem : public mate::TrackableObject<DownloadItem>,
                      public content::DownloadItem::Observer {
  public:
   class SavePathData : public base::SupportsUserData::Data {
@@ -32,6 +32,10 @@ class DownloadItem : public mate::EventEmitter,
                                            content::DownloadItem* item);
   static void* UserDataKey();
 
+  // mate::TrackableObject:
+  static void BuildPrototype(v8::Isolate* isolate,
+                             v8::Local<v8::ObjectTemplate> prototype);
+
  protected:
   explicit DownloadItem(content::DownloadItem* download_item);
   ~DownloadItem();
@@ -49,17 +53,10 @@ class DownloadItem : public mate::EventEmitter,
   bool HasUserGesture();
   std::string GetFilename();
   std::string GetContentDisposition();
-  const GURL& GetUrl();
+  const GURL& GetURL();
   void SetSavePath(const base::FilePath& path);
 
  private:
-  // mate::Wrappable:
-  mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
-      v8::Isolate* isolate) override;
-  bool IsDestroyed() const override;
-
-  void Destroy();
-
   content::DownloadItem* download_item_;
 
   DISALLOW_COPY_AND_ASSIGN(DownloadItem);

+ 3 - 3
atom/browser/api/atom_api_global_shortcut.h

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

+ 1 - 0
atom/browser/api/atom_api_menu.cc

@@ -151,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)

+ 3 - 3
atom/browser/api/atom_api_menu.h

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

+ 3 - 3
atom/browser/api/atom_api_power_monitor.h

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

+ 3 - 4
atom/browser/api/atom_api_power_save_blocker.h

@@ -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);
 };
 

+ 3 - 18
atom/browser/api/atom_api_protocol.cc

@@ -12,27 +12,12 @@
 #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/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 {
@@ -52,7 +37,7 @@ mate::ObjectTemplateBuilder Protocol::GetObjectTemplateBuilder(
       .SetMethod("registerBufferProtocol",
                  &Protocol::RegisterProtocol<URLRequestBufferJob>)
       .SetMethod("registerFileProtocol",
-                 &Protocol::RegisterProtocol<UrlRequestAsyncAsarJob>)
+                 &Protocol::RegisterProtocol<URLRequestAsyncAsarJob>)
       .SetMethod("registerHttpProtocol",
                  &Protocol::RegisterProtocol<URLRequestFetchJob>)
       .SetMethod("unregisterProtocol", &Protocol::UnregisterProtocol)
@@ -62,7 +47,7 @@ mate::ObjectTemplateBuilder Protocol::GetObjectTemplateBuilder(
       .SetMethod("interceptBufferProtocol",
                  &Protocol::InterceptProtocol<URLRequestBufferJob>)
       .SetMethod("interceptFileProtocol",
-                 &Protocol::InterceptProtocol<UrlRequestAsyncAsarJob>)
+                 &Protocol::InterceptProtocol<URLRequestAsyncAsarJob>)
       .SetMethod("interceptHttpProtocol",
                  &Protocol::InterceptProtocol<URLRequestFetchJob>)
       .SetMethod("uninterceptProtocol", &Protocol::UninterceptProtocol);

+ 1 - 1
atom/browser/api/atom_api_screen.cc

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

+ 106 - 32
atom/browser/api/atom_api_session.cc

@@ -9,16 +9,22 @@
 
 #include "atom/browser/api/atom_api_cookies.h"
 #include "atom/browser/api/atom_api_download_item.h"
-#include "atom/browser/atom_browser_context.h"
 #include "atom/browser/api/atom_api_web_contents.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_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"
@@ -45,7 +51,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")
@@ -69,7 +75,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")
@@ -102,6 +108,24 @@ 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;
+    if (!ConvertFromV8(isolate, val, &proxy))
+      return false;
+    auto pac_url = GURL(proxy);
+    if (pac_url.is_valid()) {
+      out->set_pac_url(pac_url);
+    } else {
+      out->proxy_rules().ParseFromString(proxy);
+    }
+    return true;
+  }
+};
+
 }  // namespace mate
 
 namespace atom {
@@ -206,12 +230,13 @@ void ClearHttpCacheInIO(
 }
 
 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);
 }
 
@@ -229,12 +254,13 @@ Session::Session(AtomBrowserContext* browser_context)
 Session::~Session() {
   content::BrowserContext::GetDownloadManager(browser_context())->
       RemoveObserver(this);
-  Destroy();
 }
 
 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),
@@ -245,14 +271,6 @@ void Session::OnDownloadCreated(content::DownloadManager* manager,
   }
 }
 
-bool Session::IsDestroyed() const {
-  return !browser_context_;
-}
-
-void Session::Destroy() {
-  browser_context_ = nullptr;
-}
-
 void Session::ResolveProxy(const GURL& url, ResolveProxyCallback callback) {
   new ResolveProxyHelper(browser_context(), url, callback);
 }
@@ -282,11 +300,11 @@ void Session::ClearStorageData(mate::Arguments* args) {
       base::Time(), base::Time::Max(), callback);
 }
 
-void Session::SetProxy(const std::string& proxy,
+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) {
@@ -294,6 +312,54 @@ 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());
@@ -302,17 +368,6 @@ v8::Local<v8::Value> Session::Cookies(v8::Isolate* isolate) {
   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);
-}
-
 // static
 mate::Handle<Session> Session::CreateFrom(
     v8::Isolate* isolate, AtomBrowserContext* browser_context) {
@@ -333,14 +388,34 @@ mate::Handle<Session> Session::FromPartition(
                     static_cast<AtomBrowserContext*>(browser_context.get()));
 }
 
-void SetWrapSession(const WrapSessionCallback& callback) {
-  g_wrap_session = callback;
+// static
+void Session::BuildPrototype(v8::Isolate* isolate,
+                             v8::Local<v8::ObjectTemplate> prototype) {
+  mate::ObjectTemplateBuilder(isolate, prototype)
+      .MakeDestroyable()
+      .SetMethod("resolveProxy", &Session::ResolveProxy)
+      .SetMethod("clearCache", &Session::ClearCache)
+      .SetMethod("clearStorageData", &Session::ClearStorageData)
+      .SetMethod("setProxy", &Session::SetProxy)
+      .SetMethod("setDownloadPath", &Session::SetDownloadPath)
+      .SetMethod("enableNetworkEmulation", &Session::EnableNetworkEmulation)
+      .SetMethod("disableNetworkEmulation", &Session::DisableNetworkEmulation)
+      .SetMethod("setCertificateVerifyProc", &Session::SetCertVerifyProc)
+      .SetProperty("cookies", &Session::Cookies);
 }
 
 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
@@ -353,7 +428,6 @@ void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
   mate::Dictionary dict(isolate, exports);
   dict.SetMethod("fromPartition", &atom::api::Session::FromPartition);
   dict.SetMethod("_setWrapSession", &atom::api::SetWrapSession);
-  dict.SetMethod("_clearWrapSession", &atom::api::ClearWrapSession);
 }
 
 }  // namespace

+ 13 - 9
atom/browser/api/atom_api_session.h

@@ -20,6 +20,11 @@ class FilePath;
 
 namespace mate {
 class Arguments;
+class Dictionary;
+}
+
+namespace net {
+class ProxyConfig;
 }
 
 namespace atom {
@@ -43,6 +48,10 @@ class Session: public mate::TrackableObject<Session>,
 
   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();
@@ -51,20 +60,15 @@ class Session: public mate::TrackableObject<Session>,
   void OnDownloadCreated(content::DownloadManager* manager,
                          content::DownloadItem* item) override;
 
-  // mate::Wrappable:
-  mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
-      v8::Isolate* isolate) override;
-  bool IsDestroyed() const override;
-
  private:
-  // mate::TrackableObject:
-  void Destroy() override;
-
   void ResolveProxy(const GURL& url, ResolveProxyCallback callback);
   void ClearCache(const net::CompletionCallback& callback);
   void ClearStorageData(mate::Arguments* args);
-  void SetProxy(const std::string& proxy, const base::Closure& callback);
+  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);
 
   // Cached object for cookies API.

+ 20 - 10
atom/browser/api/atom_api_tray.cc

@@ -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::OnDragExited() {
+  Emit("drag-leave");
 }
 
-void Tray::Destroy() {
-  tray_icon_.reset();
+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)

+ 7 - 7
atom/browser/api/atom_api_tray.h

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

+ 219 - 117
atom/browser/api/atom_api_web_contents.cc

@@ -7,6 +7,7 @@
 #include <set>
 
 #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"
@@ -17,6 +18,7 @@
 #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"
@@ -26,9 +28,11 @@
 #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/browser_plugin_guest_manager.h"
 #include "content/public/browser/favicon_status.h"
 #include "content/public/browser/native_web_keyboard_event.h"
 #include "content/public/browser/navigation_details.h"
@@ -43,12 +47,14 @@
 #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"
 
@@ -60,9 +66,21 @@ 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));
+}
+
+bool NotifyZoomLevelChanged(
+    double level, content::WebContents* guest_web_contents) {
+  guest_web_contents->SendToAllFrames(
+      new AtomViewMsg_SetZoomLevel(MSG_ROUTING_NONE, level));
+
+  // Return false to iterate over all guests.
+  return false;
 }
 
 }  // namespace
@@ -130,8 +148,7 @@ struct Converter<net::HttpResponseHeaders*> {
       std::string key;
       std::string value;
       while (headers->EnumerateHeaderLines(&iter, &key, &value)) {
-        key = base::StringToLowerASCII(key);
-        value = base::StringToLowerASCII(value);
+        key = base::ToLowerASCII(key);
         if (response_headers.HasKey(key)) {
           base::ListValue* values = nullptr;
           if (response_headers.GetList(key, &values))
@@ -147,6 +164,27 @@ struct Converter<net::HttpResponseHeaders*> {
   }
 };
 
+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
 
 
@@ -156,8 +194,6 @@ namespace api {
 
 namespace {
 
-v8::Persistent<v8::ObjectTemplate> template_;
-
 // The wrapWebContents function which is implemented in JavaScript
 using WrapWebContentsCallback = base::Callback<void(v8::Local<v8::Value>)>;
 WrapWebContentsCallback g_wrap_web_contents;
@@ -228,10 +264,10 @@ WebContents::WebContents(v8::Isolate* isolate,
   AttachAsUserData(web_contents);
   InitWithWebContents(web_contents);
 
+  managed_web_contents()->GetView()->SetDelegate(this);
+
   // Save the preferences in C++.
-  base::DictionaryValue web_preferences;
-  mate::ConvertFromV8(isolate, options.GetHandle(), &web_preferences);
-  new WebContentsPreferences(web_contents, &web_preferences);
+  new WebContentsPreferences(web_contents, options);
 
   web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent());
 
@@ -252,7 +288,15 @@ WebContents::WebContents(v8::Isolate* isolate,
 }
 
 WebContents::~WebContents() {
-  Destroy();
+  if (type_ == WEB_VIEW && managed_web_contents()) {
+    // When force destroying the "destroyed" event is not emitted.
+    WebContentsDestroyed();
+
+    guest_delegate_->Destroy();
+
+    Observe(nullptr);
+    DestroyWebContents();
+  }
 }
 
 bool WebContents::AddMessageToConsole(content::WebContents* source,
@@ -367,6 +411,15 @@ 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;
+}
+
 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.
@@ -413,14 +466,13 @@ void WebContents::DidFinishLoad(content::RenderFrameHost* render_frame_host,
     Emit("did-finish-load");
 }
 
-// this error occurs when host could not be found
 void WebContents::DidFailProvisionalLoad(
     content::RenderFrameHost* render_frame_host,
-    const GURL& validated_url,
+    const GURL& url,
     int error_code,
     const base::string16& error_description,
     bool was_ignored_by_handler) {
-  Emit("did-fail-load", error_code, error_description, validated_url);
+  Emit("did-fail-provisional-load", error_code, error_description, url);
 }
 
 void WebContents::DidFailLoad(content::RenderFrameHost* render_frame_host,
@@ -473,9 +525,10 @@ void WebContents::DidNavigateMainFrame(
 
 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(
@@ -491,12 +544,40 @@ 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)
     IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message, OnRendererMessage)
     IPC_MESSAGE_HANDLER_DELAY_REPLY(AtomViewHostMsg_Message_Sync,
                                     OnRendererMessageSync)
+    IPC_MESSAGE_HANDLER(AtomViewHostMsg_ZoomLevelChanged, OnZoomLevelChanged)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
 
@@ -516,23 +597,6 @@ void WebContents::NavigationEntryCommitted(
        details.is_in_page, details.did_replace_entry);
 }
 
-void WebContents::Destroy() {
-  session_.Reset();
-  if (type_ == WEB_VIEW && managed_web_contents()) {
-    // When force destroying the "destroyed" event is not emitted.
-    WebContentsDestroyed();
-
-    guest_delegate_->Destroy();
-
-    Observe(nullptr);
-    DestroyWebContents();
-  }
-}
-
-bool WebContents::IsAlive() const {
-  return web_contents() != NULL;
-}
-
 int WebContents::GetID() const {
   return web_contents()->GetRenderProcessHost()->GetID();
 }
@@ -553,12 +617,25 @@ 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();
 }
@@ -579,10 +656,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();
@@ -606,8 +679,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() {
@@ -618,6 +693,13 @@ void WebContents::InsertCSS(const std::string& css) {
   web_contents()->InsertCSS(css);
 }
 
+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::ExecuteJavaScript(const base::string16& code,
                                     bool has_user_gesture) {
   Send(new AtomViewMsg_ExecuteJavaScript(routing_id(), code, has_user_gesture));
@@ -698,10 +780,6 @@ void WebContents::InspectServiceWorker() {
   }
 }
 
-v8::Local<v8::Value> WebContents::Session(v8::Isolate* isolate) {
-  return v8::Local<v8::Value>::New(isolate, session_);
-}
-
 void WebContents::HasServiceWorker(
     const base::Callback<void(bool)>& callback) {
   auto context = GetServiceWorkerContext(web_contents());
@@ -893,79 +971,91 @@ v8::Local<v8::Value> WebContents::GetWebPreferences(v8::Isolate* isolate) {
   return mate::ConvertToV8(isolate, *web_preferences->web_preferences());
 }
 
-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("_getUrl", &WebContents::GetURL)
-        .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("enableDeviceEmulation",
-                   &WebContents::EnableDeviceEmulation)
-        .SetMethod("disableDeviceEmulation",
-                   &WebContents::DisableDeviceEmulation)
-        .SetMethod("toggleDevTools", &WebContents::ToggleDevTools)
-        .SetMethod("inspectElement", &WebContents::InspectElement)
-        .SetMethod("setAudioMuted", &WebContents::SetAudioMuted)
-        .SetMethod("isAudioMuted", &WebContents::IsAudioMuted)
-        .SetMethod("undo", &WebContents::Undo)
-        .SetMethod("redo", &WebContents::Redo)
-        .SetMethod("cut", &WebContents::Cut)
-        .SetMethod("copy", &WebContents::Copy)
-        .SetMethod("paste", &WebContents::Paste)
-        .SetMethod("pasteAndMatchStyle", &WebContents::PasteAndMatchStyle)
-        .SetMethod("delete", &WebContents::Delete)
-        .SetMethod("selectAll", &WebContents::SelectAll)
-        .SetMethod("unselect", &WebContents::Unselect)
-        .SetMethod("replace", &WebContents::Replace)
-        .SetMethod("replaceMisspelling", &WebContents::ReplaceMisspelling)
-        .SetMethod("focus", &WebContents::Focus)
-        .SetMethod("tabTraverse", &WebContents::TabTraverse)
-        .SetMethod("_send", &WebContents::SendIPCMessage, true)
-        .SetMethod("sendInputEvent", &WebContents::SendInputEvent)
-        .SetMethod("beginFrameSubscription",
-                   &WebContents::BeginFrameSubscription)
-        .SetMethod("endFrameSubscription", &WebContents::EndFrameSubscription)
-        .SetMethod("setSize", &WebContents::SetSize)
-        .SetMethod("setAllowTransparency", &WebContents::SetAllowTransparency)
-        .SetMethod("isGuest", &WebContents::IsGuest)
-        .SetMethod("getWebPreferences", &WebContents::GetWebPreferences)
-        .SetMethod("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_));
-}
-
-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_);
+}
+
+// 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("_executeJavaScript", &WebContents::ExecuteJavaScript)
+      .SetMethod("openDevTools", &WebContents::OpenDevTools)
+      .SetMethod("closeDevTools", &WebContents::CloseDevTools)
+      .SetMethod("isDevToolsOpened", &WebContents::IsDevToolsOpened)
+      .SetMethod("enableDeviceEmulation",
+                 &WebContents::EnableDeviceEmulation)
+      .SetMethod("disableDeviceEmulation",
+                 &WebContents::DisableDeviceEmulation)
+      .SetMethod("toggleDevTools", &WebContents::ToggleDevTools)
+      .SetMethod("inspectElement", &WebContents::InspectElement)
+      .SetMethod("setAudioMuted", &WebContents::SetAudioMuted)
+      .SetMethod("isAudioMuted", &WebContents::IsAudioMuted)
+      .SetMethod("undo", &WebContents::Undo)
+      .SetMethod("redo", &WebContents::Redo)
+      .SetMethod("cut", &WebContents::Cut)
+      .SetMethod("copy", &WebContents::Copy)
+      .SetMethod("paste", &WebContents::Paste)
+      .SetMethod("pasteAndMatchStyle", &WebContents::PasteAndMatchStyle)
+      .SetMethod("delete", &WebContents::Delete)
+      .SetMethod("selectAll", &WebContents::SelectAll)
+      .SetMethod("unselect", &WebContents::Unselect)
+      .SetMethod("replace", &WebContents::Replace)
+      .SetMethod("replaceMisspelling", &WebContents::ReplaceMisspelling)
+      .SetMethod("focus", &WebContents::Focus)
+      .SetMethod("tabTraverse", &WebContents::TabTraverse)
+      .SetMethod("_send", &WebContents::SendIPCMessage)
+      .SetMethod("sendInputEvent", &WebContents::SendInputEvent)
+      .SetMethod("beginFrameSubscription",
+                 &WebContents::BeginFrameSubscription)
+      .SetMethod("endFrameSubscription", &WebContents::EndFrameSubscription)
+      .SetMethod("setSize", &WebContents::SetSize)
+      .SetMethod("setAllowTransparency", &WebContents::SetAllowTransparency)
+      .SetMethod("isGuest", &WebContents::IsGuest)
+      .SetMethod("getWebPreferences", &WebContents::GetWebPreferences)
+      .SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow)
+      .SetMethod("hasServiceWorker", &WebContents::HasServiceWorker)
+      .SetMethod("unregisterServiceWorker",
+                 &WebContents::UnregisterServiceWorker)
+      .SetMethod("inspectServiceWorker", &WebContents::InspectServiceWorker)
+      .SetMethod("print", &WebContents::Print)
+      .SetMethod("_printToPDF", &WebContents::PrintToPDF)
+      .SetMethod("addWorkSpace", &WebContents::AddWorkSpace)
+      .SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace)
+      .SetProperty("session", &WebContents::Session)
+      .SetProperty("devToolsWebContents", &WebContents::DevToolsWebContents);
 }
 
 AtomBrowserContext* WebContents::GetBrowserContext() const {
@@ -985,6 +1075,15 @@ void WebContents::OnRendererMessageSync(const base::string16& channel,
   EmitWithSender(base::UTF16ToUTF8(channel), web_contents(), message, args);
 }
 
+void WebContents::OnZoomLevelChanged(double level) {
+  auto manager = web_contents()->GetBrowserContext()->GetGuestManager();
+  if (!manager)
+    return;
+  manager->ForEachGuest(web_contents(),
+                        base::Bind(&NotifyZoomLevelChanged,
+                                   level));
+}
+
 // static
 mate::Handle<WebContents> WebContents::CreateFrom(
     v8::Isolate* isolate, content::WebContents* web_contents) {
@@ -1007,12 +1106,16 @@ mate::Handle<WebContents> WebContents::Create(
   return handle;
 }
 
+void ClearWrapWebContents() {
+  g_wrap_web_contents.Reset();
+}
+
 void SetWrapWebContents(const WrapWebContentsCallback& callback) {
   g_wrap_web_contents = callback;
-}
 
-void ClearWrapWebContents() {
-  g_wrap_web_contents.Reset();
+  // Cleanup the wrapper on exit.
+  atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback(
+      base::Bind(ClearWrapWebContents));
 }
 
 }  // namespace api
@@ -1028,7 +1131,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

+ 27 - 10
atom/browser/api/atom_api_web_contents.h

@@ -9,6 +9,7 @@
 #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/browser/web_contents_observer.h"
@@ -53,13 +54,10 @@ class WebContents : public mate::TrackableObject<WebContents>,
   static mate::Handle<WebContents> Create(
       v8::Isolate* isolate, const mate::Dictionary& options);
 
-  // mate::TrackableObject:
-  void Destroy() override;
-
-  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;
@@ -73,6 +71,9 @@ class WebContents : public mate::TrackableObject<WebContents>,
   void SetUserAgent(const std::string& user_agent);
   std::string GetUserAgent();
   void InsertCSS(const std::string& css);
+  bool SavePage(const base::FilePath& full_file_path,
+                const content::SavePageType& save_type,
+                const SavePageHandler::SavePageCallback& callback);
   void ExecuteJavaScript(const base::string16& code,
                          bool has_user_gesture);
   void OpenDevTools(mate::Arguments* args);
@@ -83,7 +84,6 @@ class WebContents : public mate::TrackableObject<WebContents>,
   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);
@@ -135,16 +135,22 @@ class WebContents : public mate::TrackableObject<WebContents>,
   // 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);
+
+  // mate::TrackableObject:
+  static void BuildPrototype(v8::Isolate* isolate,
+                             v8::Local<v8::ObjectTemplate> prototype);
+
  protected:
   explicit WebContents(content::WebContents* web_contents);
   WebContents(v8::Isolate* isolate, const mate::Dictionary& options);
   ~WebContents();
 
-  // mate::Wrappable:
-  mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
-      v8::Isolate* isolate) override;
-  bool IsDestroyed() const override;
-
   // content::WebContentsDelegate:
   bool AddMessageToConsole(content::WebContents* source,
                            int32 level,
@@ -179,6 +185,7 @@ 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;
 
   // content::WebContentsObserver:
   void BeforeUnloadFired(const base::TimeTicks& proceed_time) override;
@@ -218,6 +225,11 @@ class WebContents : public mate::TrackableObject<WebContents>,
   void PluginCrashed(const base::FilePath& plugin_path,
                      base::ProcessId plugin_pid) override;
 
+  // brightray::InspectableWebContentsViewDelegate:
+  void DevToolsFocused() override;
+  void DevToolsOpened() override;
+  void DevToolsClosed() override;
+
  private:
   enum Type {
     BROWSER_WINDOW,  // Used by BrowserWindow.
@@ -236,7 +248,12 @@ class WebContents : public mate::TrackableObject<WebContents>,
                              const base::ListValue& args,
                              IPC::Message* message);
 
+  // Called when guests need to be notified of
+  // embedders' zoom level change.
+  void OnZoomLevelChanged(double level);
+
   v8::Global<v8::Value> session_;
+  v8::Global<v8::Value> devtools_web_contents_;
 
   scoped_ptr<WebViewGuestDelegate> guest_delegate_;
 

+ 150 - 69
atom/browser/api/atom_api_window.cc

@@ -3,6 +3,7 @@
 // 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"
@@ -60,22 +61,82 @@ 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);
+    }
+  }
+}
+
+#if defined(OS_WIN)
+// 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();
+}
+#endif
+
 }  // namespace
 
 
 Window::Window(v8::Isolate* isolate, const mate::Dictionary& options) {
-  // Use options['web-preferences'] to create WebContents.
+  // 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(switches::kWebPreferences, &web_preferences);
+  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(switches::kNodeIntegration, &value))
-    web_preferences.Set(switches::kNodeIntegration, value);
-  if (options.Get(switches::kPreloadScript, &value))
-    web_preferences.Set(switches::kPreloadScript, value);
-  if (options.Get(switches::kZoomFactor, &value))
-    web_preferences.Set(switches::kZoomFactor, 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.
   auto web_contents = WebContents::Create(isolate, web_preferences);
@@ -92,16 +153,16 @@ Window::Window(v8::Isolate* isolate, const mate::Dictionary& 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) {
@@ -109,19 +170,19 @@ void Window::WillCloseWindow(bool* prevent_default) {
 }
 
 void Window::OnWindowClosed() {
-  if (api_web_contents_) {
-    api_web_contents_->DestroyWebContents();
-    api_web_contents_ = nullptr;
-    web_contents_.Reset();
-  }
+  api_web_contents_->DestroyWebContents();
 
   RemoveFromWeakMap();
   window_->RemoveObserver(this);
 
+  // We can not call Destroy here because we need to call Emit first, but we
+  // also do not want any method to be used, so just mark as destroyed here.
+  MarkDestroyed();
+
   Emit("closed");
 
-  // Clean up the resources after window has been closed.
-  base::MessageLoop::current()->DeleteSoon(FROM_HERE, window_.release());
+  // Destroy the native class when window is closed.
+  base::MessageLoop::current()->PostTask(FROM_HERE, GetDestroyClosure());
 }
 
 void Window::OnWindowBlur() {
@@ -184,60 +245,45 @@ void Window::OnRendererResponsive() {
   Emit("responsive");
 }
 
-void Window::OnDevToolsFocus() {
-  Emit("devtools-focused");
-}
-
-void Window::OnDevToolsOpened() {
-  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());
-
-  Emit("devtools-opened");
-}
-
-void Window::OnDevToolsClosed() {
-  v8::Locker locker(isolate());
-  v8::HandleScope handle_scope(isolate());
-  devtools_web_contents_.Reset();
-
-  Emit("devtools-closed");
-}
-
 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()) {
     isolate->ThrowException(v8::Exception::Error(mate::StringToV8(
         isolate, "Cannot create BrowserWindow before app is ready")));
     return nullptr;
   }
-  return new Window(isolate, options);
-}
 
-bool Window::IsDestroyed() const {
-  return !window_ || window_->IsClosed();
-}
+  if (args->Length() > 1) {
+    args->ThrowError();
+    return nullptr;
+  }
 
-void Window::Destroy() {
-  if (window_)
-    window_->CloseContents(nullptr);
+  mate::Dictionary options;
+  if (!(args->Length() == 1 && args->GetNext(&options))) {
+    options = mate::Dictionary::CreateEmpty(isolate);
+  }
+
+  return new Window(isolate, options);
 }
 
 void Window::Close() {
   window_->Close();
 }
 
-bool Window::IsClosed() {
-  return window_->IsClosed();
-}
-
 void Window::Focus() {
   window_->Focus(true);
 }
@@ -406,6 +452,10 @@ bool Window::IsKiosk() {
   return window_->IsKiosk();
 }
 
+void Window::SetBackgroundColor(const std::string& color_name) {
+  window_->SetBackgroundColor(color_name);
+}
+
 void Window::FocusOnWebView() {
   window_->FocusOnWebView();
 }
@@ -509,6 +559,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();
@@ -540,20 +613,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)
@@ -592,6 +657,7 @@ void Window::BuildPrototype(v8::Isolate* isolate,
       .SetMethod("setSkipTaskbar", &Window::SetSkipTaskbar)
       .SetMethod("setKiosk", &Window::SetKiosk)
       .SetMethod("isKiosk", &Window::IsKiosk)
+      .SetMethod("setBackgroundColor", &Window::SetBackgroundColor)
       .SetMethod("setRepresentedFilename", &Window::SetRepresentedFilename)
       .SetMethod("getRepresentedFilename", &Window::GetRepresentedFilename)
       .SetMethod("setDocumentEdited", &Window::SetDocumentEdited)
@@ -613,13 +679,28 @@ void Window::BuildPrototype(v8::Isolate* isolate,
                  &Window::SetVisibleOnAllWorkspaces)
       .SetMethod("isVisibleOnAllWorkspaces",
                  &Window::IsVisibleOnAllWorkspaces)
+#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

+ 25 - 15
atom/browser/api/atom_api_window.h

@@ -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,8 +54,6 @@ 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 OnWindowClosed() override;
   void OnWindowBlur() override;
@@ -69,21 +71,15 @@ class Window : public mate::TrackableObject<Window>,
   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:
-  // mate::TrackableObject:
-  void Destroy() override;
-
   // APIs for NativeWindow.
   void Close();
-  bool IsClosed();
   void Focus();
   bool IsFocused();
   void Show();
@@ -121,6 +117,7 @@ class Window : public mate::TrackableObject<Window>,
   void SetSkipTaskbar(bool skip);
   void SetKiosk(bool kiosk);
   bool IsKiosk();
+  void SetBackgroundColor(const std::string& color_name);
   void FocusOnWebView();
   void BlurWebView();
   bool IsWebViewFocused();
@@ -141,6 +138,16 @@ class Window : public mate::TrackableObject<Window>,
   bool IsMenuBarVisible();
   void SetAspectRatio(double aspect_ratio, mate::Arguments* args);
 
+#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();
 #endif
@@ -150,10 +157,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_;

+ 7 - 7
atom/browser/api/frame_subscriber.cc

@@ -24,12 +24,11 @@ bool FrameSubscriber::ShouldCaptureFrame(
     base::TimeTicks present_time,
     scoped_refptr<media::VideoFrame>* storage,
     DeliverFrameCallback* callback) {
-  *storage = media::VideoFrame::CreateFrame(media::VideoFrame::YV12, size_,
-                                            gfx::Rect(size_), size_,
-                                            base::TimeDelta());
+  *storage = media::VideoFrame::CreateFrame(
+      media::PIXEL_FORMAT_YV12,
+      size_, gfx::Rect(size_), size_, base::TimeDelta());
   *callback = base::Bind(&FrameSubscriber::OnFrameDelivered,
-                         base::Unretained(this),
-                         *storage);
+                         base::Unretained(this), *storage);
   return true;
 }
 
@@ -38,6 +37,9 @@ void FrameSubscriber::OnFrameDelivered(
   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);
@@ -56,8 +58,6 @@ void FrameSubscriber::OnFrameDelivered(
                            rect.width() * 4,
                            media::YV12);
 
-  v8::Locker locker(isolate_);
-  v8::HandleScope handle_scope(isolate_);
   callback_.Run(buffer.ToLocalChecked());
 }
 

+ 34 - 30
atom/browser/api/lib/app.coffee

@@ -1,30 +1,17 @@
-EventEmitter = require('events').EventEmitter
+{deprecate, session, Menu} = require 'electron'
+{EventEmitter} = require 'events'
 
 bindings = process.atomBinding 'app'
-sessionBindings = process.atomBinding 'session'
 downloadItemBindings = process.atomBinding 'download_item'
 
 app = bindings.app
 app.__proto__ = EventEmitter.prototype
 
-wrapSession = (session) ->
-  # session is an Event Emitter.
-  session.__proto__ = EventEmitter.prototype
-
-wrapDownloadItem = (download_item) ->
-  # download_item is an Event Emitter.
-  download_item.__proto__ = EventEmitter.prototype
-  # Be compatible with old APIs.
-  download_item.url = download_item.getUrl()
-  download_item.filename = download_item.getFilename()
-  download_item.mimeType = download_item.getMimeType()
-  download_item.hasUserGesture = download_item.hasUserGesture()
-
 app.setApplicationMenu = (menu) ->
-  require('menu').setApplicationMenu menu
+  Menu.setApplicationMenu menu
 
 app.getApplicationMenu = ->
-  require('menu').getApplicationMenu()
+  Menu.getApplicationMenu()
 
 app.commandLine =
   appendSwitch: bindings.appendSwitch,
@@ -47,22 +34,39 @@ app.setAppPath = (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
-app.on 'activate', (event, hasVisibleWindows) -> @emit 'activate-with-no-open-windows' if not hasVisibleWindows
+# Routes the events to webContents.
+for name in ['login', 'certificate-error', 'select-client-certificate']
+  do (name) ->
+    app.on name, (event, webContents, args...) ->
+      webContents.emit name, event, args...
 
-# Session wrapper.
-sessionBindings._setWrapSession wrapSession
-process.once 'exit', sessionBindings._clearWrapSession
+# Deprecated.
+app.getHomeDir = deprecate 'app.getHomeDir', 'app.getPath', ->
+  @getPath 'home'
+app.getDataPath = deprecate 'app.getDataPath', 'app.getPath', ->
+  @getPath 'userData'
+app.setDataPath = deprecate 'app.setDataPath', 'app.setPath', (path) ->
+  @setPath 'userData', path
+app.resolveProxy = deprecate 'app.resolveProxy', 'session.defaultSession.resolveProxy', (url, callback) ->
+  session.defaultSession.resolveProxy url, callback
+deprecate.rename app, 'terminate', 'quit'
+deprecate.event app, 'finish-launching', 'ready', ->
+  setImmediate => # give default app a chance to setup default menu.
+    @emit 'finish-launching'
+deprecate.event app, 'activate-with-no-open-windows', 'activate', (event, hasVisibleWindows) ->
+  @emit 'activate-with-no-open-windows', event if not hasVisibleWindows
+deprecate.event app, 'select-certificate', 'select-client-certificate'
 
+# Wrappers for native classes.
+wrapDownloadItem = (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'
+  deprecate.rename downloadItem, 'getUrl', 'getURL'
 downloadItemBindings._setWrapDownloadItem wrapDownloadItem
-process.once 'exit', downloadItemBindings._clearWrapDownloadItem
 
 # Only one App object pemitted.
 module.exports = app

+ 0 - 6
atom/browser/api/lib/atom-delegate.coffee

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

+ 8 - 20
atom/browser/api/lib/auto-updater.coffee

@@ -1,24 +1,12 @@
-autoUpdater = process.atomBinding('auto_updater').autoUpdater
-EventEmitter = require('events').EventEmitter
+{deprecate} = require 'electron'
 
-autoUpdater.__proto__ = EventEmitter.prototype
+autoUpdater =
+  if process.platform is 'win32'
+    require './auto-updater/auto-updater-win'
+  else
+    require './auto-updater/auto-updater-native'
 
-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
+# Deprecated.
+deprecate.rename autoUpdater, 'setFeedUrl', 'setFeedURL'
 
 module.exports = autoUpdater

+ 6 - 0
atom/browser/api/lib/auto-updater/auto-updater-native.coffee

@@ -0,0 +1,6 @@
+{EventEmitter} = require 'events'
+{autoUpdater} = process.atomBinding 'auto_updater'
+
+autoUpdater.__proto__ = EventEmitter.prototype
+
+module.exports = autoUpdater

+ 42 - 0
atom/browser/api/lib/auto-updater/auto-updater-win.coffee

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

+ 67 - 0
atom/browser/api/lib/auto-updater/squirrel-update-win.coffee

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

+ 45 - 26
atom/browser/api/lib/browser-window.coffee

@@ -1,11 +1,12 @@
-EventEmitter = require('events').EventEmitter
-app = require 'app'
-ipc = require 'ipc'
+{ipcMain, deprecate} = require 'electron'
+{EventEmitter} = require 'events'
 
-BrowserWindow = process.atomBinding('window').BrowserWindow
+{BrowserWindow} = process.atomBinding 'window'
 BrowserWindow::__proto__ = EventEmitter.prototype
 
 BrowserWindow::_init = ->
+  {app} = require 'electron'  # avoid recursive require.
+
   # Simulate the application menu on platforms other than OS X.
   if process.platform isnt 'darwin'
     menu = app.getApplicationMenu()
@@ -14,7 +15,7 @@ BrowserWindow::_init = ->
   # 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
+    ipcMain.emit 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, options
 
   # window.resizeTo(...)
   # window.moveTo(...)
@@ -30,6 +31,11 @@ BrowserWindow::_init = ->
   @webContents.on 'crashed', =>
     @emit 'crashed'
 
+  # Change window title to page title.
+  @webContents.on 'page-title-updated', (event, title, explicitSet) =>
+    @emit 'page-title-updated', event, title
+    @setTitle title unless event.defaultPrevented
+
   # Sometimes the webContents doesn't get focus when window is shown, so we have
   # to force focusing on webContents in this case. The safest way is to focus it
   # when we first start to load URL, if we do it earlier it won't have effect,
@@ -48,6 +54,15 @@ BrowserWindow::_init = ->
   # Notify the creation of the window.
   app.emit 'browser-window-created', {}, this
 
+  # Be compatible with old APIs.
+  @webContents.on 'devtools-focused', => @emit 'devtools-focused'
+  @webContents.on 'devtools-opened', => @emit 'devtools-opened'
+  @webContents.on 'devtools-closed', => @emit 'devtools-closed'
+  Object.defineProperty this, 'devToolsWebContents',
+    enumerable: true,
+    configurable: false,
+    get: -> @webContents.devToolsWebContents
+
 BrowserWindow.getFocusedWindow = ->
   windows = BrowserWindow.getAllWindows()
   return window for window in windows when window.isFocused()
@@ -61,33 +76,37 @@ BrowserWindow.fromDevToolsWebContents = (webContents) ->
   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::undo = -> @webContents.undo()
-BrowserWindow::redo = -> @webContents.redo()
-BrowserWindow::cut = -> @webContents.cut()
-BrowserWindow::copy = -> @webContents.copy()
-BrowserWindow::paste = -> @webContents.paste()
-BrowserWindow::selectAll = -> @webContents.selectAll()
-BrowserWindow::restart = -> @webContents.reload()
-BrowserWindow::getUrl = -> @webContents.getUrl()
+BrowserWindow::loadURL = -> @webContents.loadURL.apply @webContents, arguments
+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::send = -> @webContents.send.apply @webContents, arguments
 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
+
+# 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::executeJavaScriptInDevTools = deprecate 'executeJavaScriptInDevTools', 'devToolsWebContents.executeJavaScript', (code) ->
+  @devToolsWebContents?.executeJavaScript code
+BrowserWindow::getPageTitle = deprecate 'getPageTitle', 'webContents.getTitle', ->
+  @webContents?.getTitle()
 
 module.exports = BrowserWindow

+ 2 - 2
atom/browser/api/lib/dialog.coffee

@@ -1,7 +1,7 @@
+{app, BrowserWindow} = require 'electron'
+
 binding = process.atomBinding 'dialog'
 v8Util = process.atomBinding 'v8_util'
-app = require 'app'
-BrowserWindow = require 'browser-window'
 
 fileDialogProperties =
   openFile:        1 << 0

+ 55 - 0
atom/browser/api/lib/exports/electron.coffee

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

+ 1 - 3
atom/browser/api/lib/global-shortcut.coffee

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

+ 3 - 0
atom/browser/api/lib/ipc-main.coffee

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

+ 5 - 2
atom/browser/api/lib/ipc.coffee

@@ -1,3 +1,6 @@
-EventEmitter = require('events').EventEmitter
+{deprecate, ipcMain} = require 'electron'
 
-module.exports = new EventEmitter
+# This module is deprecated, we mirror everything from ipcMain.
+deprecate.warn 'ipc module', 'require("electron").ipcMain'
+
+module.exports = ipcMain

+ 12 - 4
atom/browser/api/lib/menu-item.coffee

@@ -1,4 +1,3 @@
-BrowserWindow = require 'browser-window'
 v8Util = process.atomBinding 'v8_util'
 
 nextCommandId = 0
@@ -14,11 +13,16 @@ rolesMap =
   minimize: 'minimize'
   close: 'close'
 
+# Maps methods that should be called directly on the BrowserWindow instance
+methodInBrowserWindow =
+  minimize: true
+  close: true
+
 class MenuItem
   @types = ['normal', 'separator', 'submenu', 'checkbox', 'radio']
 
   constructor: (options) ->
-    Menu = require 'menu'
+    {Menu} = require 'electron'
 
     {click, @selector, @type, @role, @label, @sublabel, @accelerator, @icon, @enabled, @visible, @checked, @submenu} = options
 
@@ -43,8 +47,12 @@ class MenuItem
       # Manually flip the checked flags when clicked.
       @checked = !@checked if @type in ['checkbox', 'radio']
 
-      if @role and rolesMap[@role] and process.platform isnt 'darwin'
-        focusedWindow?[rolesMap[@role]]()
+      if @role and rolesMap[@role] and process.platform isnt 'darwin' and focusedWindow?
+        methodName = rolesMap[@role]
+        if methodInBrowserWindow[methodName]
+          focusedWindow[methodName]()
+        else
+          focusedWindow.webContents?[methodName]()
       else if typeof click is 'function'
         click this, focusedWindow
       else if typeof @selector is 'string'

+ 8 - 5
atom/browser/api/lib/menu.coffee

@@ -1,8 +1,7 @@
-BrowserWindow = require 'browser-window'
-EventEmitter = require('events').EventEmitter
-MenuItem = require 'menu-item'
-v8Util = process.atomBinding 'v8_util'
+{BrowserWindow, MenuItem} = require 'electron'
+{EventEmitter} = require 'events'
 
+v8Util = process.atomBinding 'v8_util'
 bindings = process.atomBinding 'menu'
 
 # Automatically generated radio menu item's group id.
@@ -79,7 +78,11 @@ Menu::_init = ->
         v8Util.setHiddenValue group[0], 'checked', true unless checked
 
 Menu::popup = (window, x, y) ->
-  throw new TypeError('Invalid window') unless window?.constructor is BrowserWindow
+  unless window?.constructor is BrowserWindow
+    # Shift.
+    y = x
+    x = window
+    window = BrowserWindow.getFocusedWindow()
   if x? and y?
     @_popupAt(window, x, y)
   else

+ 15 - 15
atom/browser/api/lib/navigation-controller.coffee

@@ -1,15 +1,15 @@
-ipc = require 'ipc'
+{ipcMain} = require 'electron'
 
 # The history operation in renderer is redirected to browser.
-ipc.on 'ATOM_SHELL_NAVIGATION_CONTROLLER', (event, method, args...) ->
+ipcMain.on 'ATOM_SHELL_NAVIGATION_CONTROLLER', (event, method, args...) ->
   event.sender[method] args...
 
-ipc.on 'ATOM_SHELL_SYNC_NAVIGATION_CONTROLLER', (event, method, args...) ->
+ipcMain.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.
+# 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
@@ -17,9 +17,9 @@ class NavigationController
     @clearHistory()
 
     # webContents may have already navigated to a page.
-    if @webContents._getUrl()
+    if @webContents._getURL()
       @currentIndex++
-      @history.push @webContents._getUrl()
+      @history.push @webContents._getURL()
 
     @webContents.on 'navigation-entry-commited', (event, url, inPage, replaceEntry) =>
       if @inPageIndex > -1 and not inPage
@@ -42,12 +42,12 @@ class NavigationController
           @currentIndex++
           @history.push url
 
-  loadUrl: (url, options={}) ->
+  loadURL: (url, options={}) ->
     @pendingIndex = -1
-    @webContents._loadUrl url, options
+    @webContents._loadURL url, options
     @webContents.emit 'load-url', url, options
 
-  getUrl: ->
+  getURL: ->
     if @currentIndex is -1
       ''
     else
@@ -59,11 +59,11 @@ class NavigationController
 
   reload: ->
     @pendingIndex = @currentIndex
-    @webContents._loadUrl @getUrl(), {}
+    @webContents._loadURL @getURL(), {}
 
   reloadIgnoringCache: ->
-    @webContents._reloadIgnoringCache()  # Rely on WebContents to clear cache.
-    @reload()
+    @pendingIndex = @currentIndex
+    @webContents._loadURL @getURL(), {extraHeaders: "pragma: no-cache\n"}
 
   canGoBack: ->
     @getActiveIndex() > 0
@@ -89,7 +89,7 @@ class NavigationController
     if @inPageIndex > -1 and @pendingIndex >= @inPageIndex
       @webContents._goBack()
     else
-      @webContents._loadUrl @history[@pendingIndex], {}
+      @webContents._loadURL @history[@pendingIndex], {}
 
   goForward: ->
     return unless @canGoForward()
@@ -97,12 +97,12 @@ class NavigationController
     if @inPageIndex > -1 and @pendingIndex >= @inPageIndex
       @webContents._goForward()
     else
-      @webContents._loadUrl @history[@pendingIndex], {}
+      @webContents._loadURL @history[@pendingIndex], {}
 
   goToIndex: (index) ->
     return unless @canGoToIndex index
     @pendingIndex = index
-    @webContents._loadUrl @history[@pendingIndex], {}
+    @webContents._loadURL @history[@pendingIndex], {}
 
   goToOffset: (offset) ->
     return unless @canGoToOffset offset

+ 3 - 2
atom/browser/api/lib/power-monitor.coffee

@@ -1,5 +1,6 @@
-powerMonitor = process.atomBinding('power_monitor').powerMonitor
-EventEmitter = require('events').EventEmitter
+{EventEmitter} = require 'events'
+
+{powerMonitor} = process.atomBinding 'power_monitor'
 
 powerMonitor.__proto__ = EventEmitter.prototype
 

+ 2 - 2
atom/browser/api/lib/power-save-blocker.coffee

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

+ 3 - 2
atom/browser/api/lib/protocol.coffee

@@ -1,7 +1,8 @@
-app = require 'app'
+{app} = require 'electron'
+
 throw new Error('Can not initialize protocol module before app is ready') unless app.isReady()
 
-protocol = process.atomBinding('protocol').protocol
+{protocol} = process.atomBinding 'protocol'
 
 # Warn about removed APIs.
 logAndThrow = (callback, message) ->

+ 2 - 2
atom/browser/api/lib/screen.coffee

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

+ 23 - 0
atom/browser/api/lib/session.coffee

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

+ 11 - 6
atom/browser/api/lib/tray.coffee

@@ -1,14 +1,19 @@
-EventEmitter = require('events').EventEmitter
-bindings = process.atomBinding 'tray'
+{deprecate} = require 'electron'
+{EventEmitter} = require 'events'
 
-Tray = bindings.Tray
+{Tray} = process.atomBinding 'tray'
 Tray::__proto__ = EventEmitter.prototype
 
+Tray::_init = ->
+  # 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'
+  deprecate.event this, 'balloon-clicked', 'balloon-click'
+
 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

+ 27 - 7
atom/browser/api/lib/web-contents.coffee

@@ -1,7 +1,7 @@
-EventEmitter = require('events').EventEmitter
-NavigationController = require './navigation-controller'
+{EventEmitter} = require 'events'
+{deprecate, ipcMain, session, NavigationController, Menu} = require 'electron'
+
 binding = process.atomBinding 'web_contents'
-ipc = require 'ipc'
 
 nextId = 0
 getNextId = -> ++nextId
@@ -45,7 +45,7 @@ wrapWebContents = (webContents) ->
   # Make sure webContents.executeJavaScript would run the code only when the
   # web contents has been loaded.
   webContents.executeJavaScript = (code, hasUserGesture=false) ->
-    if @getUrl() and not @isLoading()
+    if @getURL() and not @isLoading()
       @_executeJavaScript code, hasUserGesture
     else
       webContents.once 'did-finish-load', @_executeJavaScript.bind(this, code, hasUserGesture)
@@ -59,11 +59,32 @@ wrapWebContents = (webContents) ->
   # Dispatch IPC messages to the ipc module.
   webContents.on 'ipc-message', (event, packed) ->
     [channel, args...] = packed
-    ipc.emit channel, event, args...
+    ipcMain.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...
+    ipcMain.emit channel, event, args...
+
+  # Handle context menu action request from pepper plugin.
+  webContents.on 'pepper-context-menu', (event, params) ->
+    menu = Menu.buildFromTemplate params.menu
+    menu.popup params.x, params.y
+
+  # This error occurs when host could not be found.
+  webContents.on 'did-fail-provisional-load', (args...) ->
+    # Calling loadURL during this event might cause crash, so delay the event
+    # until next tick.
+    setImmediate => @emit 'did-fail-load', args...
+
+  # Delays the page-title-updated event to next tick.
+  webContents.on '-page-title-updated', (args...) ->
+    setImmediate => @emit 'page-title-updated', args...
+
+  # Deprecated.
+  deprecate.rename webContents, 'loadUrl', 'loadURL'
+  deprecate.rename webContents, 'getUrl', 'getURL'
+  deprecate.event webContents, 'page-title-set', 'page-title-updated', (args...) ->
+    @emit 'page-title-set', args...
 
   webContents.printToPDF = (options, callback) ->
     printingSetting =
@@ -106,7 +127,6 @@ wrapWebContents = (webContents) ->
     @_printToPDF printingSetting, callback
 
 binding._setWrapWebContents wrapWebContents
-process.once 'exit', binding._clearWrapWebContents
 
 module.exports.create = (options={}) ->
   binding.create(options)

+ 83 - 0
atom/browser/api/save_page_handler.cc

@@ -0,0 +1,83 @@
+// 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/save_page_handler.h"
+
+#include <string>
+
+#include "atom/browser/atom_browser_context.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "content/public/browser/web_contents.h"
+
+namespace atom {
+
+namespace api {
+
+SavePageHandler::SavePageHandler(content::WebContents* web_contents,
+                                 const SavePageCallback& callback)
+    : web_contents_(web_contents),
+      callback_(callback) {
+}
+
+SavePageHandler::~SavePageHandler() {
+}
+
+void SavePageHandler::OnDownloadCreated(content::DownloadManager* manager,
+                                        content::DownloadItem* item) {
+  // OnDownloadCreated is invoked during WebContents::SavePage, so the |item|
+  // here is the one stated by WebContents::SavePage.
+  item->AddObserver(this);
+}
+
+bool SavePageHandler::Handle(const base::FilePath& full_path,
+                             const content::SavePageType& save_type) {
+  auto download_manager = content::BrowserContext::GetDownloadManager(
+      web_contents_->GetBrowserContext());
+  download_manager->AddObserver(this);
+  // Chromium will create a 'foo_files' directory under the directory of saving
+  // page 'foo.html' for holding other resource files of 'foo.html'.
+  base::FilePath saved_main_directory_path = full_path.DirName().Append(
+      full_path.RemoveExtension().BaseName().value() +
+      FILE_PATH_LITERAL("_files"));
+  bool result = web_contents_->SavePage(full_path,
+                                        saved_main_directory_path,
+                                        save_type);
+  download_manager->RemoveObserver(this);
+  // If initialization fails which means fail to create |DownloadItem|, we need
+  // to delete the |SavePageHandler| instance to avoid memory-leak.
+  if (!result)
+    delete this;
+  return result;
+}
+
+void SavePageHandler::OnDownloadUpdated(content::DownloadItem* item) {
+  if (item->IsDone()) {
+    v8::Isolate* isolate = v8::Isolate::GetCurrent();
+    v8::Locker locker(isolate);
+    v8::HandleScope handle_scope(isolate);
+    if (item->GetState() == content::DownloadItem::COMPLETE) {
+      callback_.Run(v8::Null(isolate));
+    } else {
+      v8::Local<v8::String> error_message = v8::String::NewFromUtf8(
+          isolate, "Fail to save page");
+      callback_.Run(v8::Exception::Error(error_message));
+    }
+    Destroy(item);
+  }
+}
+
+void SavePageHandler::Destroy(content::DownloadItem* item) {
+  item->RemoveObserver(this);
+  delete this;
+}
+
+// static
+bool SavePageHandler::IsSavePageTypes(const std::string& type) {
+  return type == "multipart/related" || type == "text/html";
+}
+
+}  // namespace api
+
+}  // namespace atom

+ 60 - 0
atom/browser/api/save_page_handler.h

@@ -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.
+
+#ifndef ATOM_BROWSER_API_SAVE_PAGE_HANDLER_H_
+#define ATOM_BROWSER_API_SAVE_PAGE_HANDLER_H_
+
+#include <string>
+
+#include "content/public/browser/download_item.h"
+#include "content/public/browser/download_manager.h"
+#include "content/public/browser/save_page_type.h"
+#include "v8/include/v8.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace content {
+class WebContents;
+}
+
+namespace atom {
+
+namespace api {
+
+// A self-destroyed class for handling save page request.
+class SavePageHandler : public content::DownloadManager::Observer,
+                        public content::DownloadItem::Observer {
+ public:
+  using SavePageCallback = base::Callback<void(v8::Local<v8::Value>)>;
+
+  SavePageHandler(content::WebContents* web_contents,
+                  const SavePageCallback& callback);
+  ~SavePageHandler();
+
+  bool Handle(const base::FilePath& full_path,
+              const content::SavePageType& save_type);
+
+  static bool IsSavePageTypes(const std::string& type);
+
+ private:
+  void Destroy(content::DownloadItem* item);
+
+  // content::DownloadManager::Observer:
+  void OnDownloadCreated(content::DownloadManager* manager,
+                         content::DownloadItem* item) override;
+
+  // content::DownloadItem::Observer:
+  void OnDownloadUpdated(content::DownloadItem* item) override;
+
+  content::WebContents* web_contents_;  // weak
+  SavePageCallback callback_;
+};
+
+}  // namespace api
+
+}  // namespace atom
+
+#endif  // ATOM_BROWSER_API_SAVE_PAGE_HANDLER_H_

+ 17 - 5
atom/browser/api/trackable_object.cc

@@ -30,11 +30,11 @@ class IDUserData : public base::SupportsUserData::Data {
 
 TrackableObjectBase::TrackableObjectBase()
     : weak_map_id_(0), wrapped_(nullptr), weak_factory_(this) {
-  RegisterDestructionCallback(
-      base::Bind(&TrackableObjectBase::Destroy, weak_factory_.GetWeakPtr()));
+  cleanup_ = RegisterDestructionCallback(GetDestroyClosure());
 }
 
 TrackableObjectBase::~TrackableObjectBase() {
+  cleanup_.Run();
 }
 
 void TrackableObjectBase::AfterInit(v8::Isolate* isolate) {
@@ -42,6 +42,18 @@ void TrackableObjectBase::AfterInit(v8::Isolate* isolate) {
     AttachAsUserData(wrapped_);
 }
 
+void TrackableObjectBase::MarkDestroyed() {
+  GetWrapper(isolate())->SetAlignedPointerInInternalField(0, nullptr);
+}
+
+base::Closure TrackableObjectBase::GetDestroyClosure() {
+  return base::Bind(&TrackableObjectBase::Destroy, weak_factory_.GetWeakPtr());
+}
+
+void TrackableObjectBase::Destroy() {
+  delete this;
+}
+
 void TrackableObjectBase::AttachAsUserData(base::SupportsUserData* wrapped) {
   if (weak_map_id_ != 0) {
     wrapped->SetUserData(kTrackedObjectKey, new IDUserData(weak_map_id_));
@@ -63,9 +75,9 @@ int32_t TrackableObjectBase::GetIDFromWrappedClass(base::SupportsUserData* w) {
 }
 
 // static
-void TrackableObjectBase::RegisterDestructionCallback(
-    const base::Closure& closure) {
-  atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback(closure);
+base::Closure TrackableObjectBase::RegisterDestructionCallback(
+    const base::Closure& c) {
+  return atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback(c);
 }
 
 }  // namespace mate

+ 33 - 10
atom/browser/api/trackable_object.h

@@ -12,6 +12,7 @@
 #include "base/bind.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "native_mate/object_template_builder.h"
 
 namespace base {
 class SupportsUserData;
@@ -30,26 +31,32 @@ class TrackableObjectBase : public mate::EventEmitter {
   // Wrap TrackableObject into a class that SupportsUserData.
   void AttachAsUserData(base::SupportsUserData* wrapped);
 
-  // Subclasses should implement this to destroy their native types.
-  virtual void Destroy() = 0;
-
  protected:
   ~TrackableObjectBase() override;
 
   // mate::Wrappable:
   void AfterInit(v8::Isolate* isolate) override;
 
+  // Mark the JS object as destroyed.
+  void MarkDestroyed();
+
+  // Returns a closure that can destroy the native class.
+  base::Closure GetDestroyClosure();
+
   // Get the weak_map_id from SupportsUserData.
   static int32_t GetIDFromWrappedClass(base::SupportsUserData* wrapped);
 
   // Register a callback that should be destroyed before JavaScript environment
   // gets destroyed.
-  static void RegisterDestructionCallback(const base::Closure& closure);
+  static base::Closure RegisterDestructionCallback(const base::Closure& c);
 
   int32_t weak_map_id_;
   base::SupportsUserData* wrapped_;
 
  private:
+  void Destroy();
+
+  base::Closure cleanup_;
   base::WeakPtrFactory<TrackableObjectBase> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(TrackableObjectBase);
@@ -91,11 +98,6 @@ class TrackableObject : public TrackableObjectBase {
       return std::vector<v8::Local<v8::Object>>();
   }
 
-  TrackableObject() {
-    RegisterDestructionCallback(
-        base::Bind(&TrackableObject<T>::ReleaseAllWeakReferences));
-  }
-
   // Removes this instance from the weak map.
   void RemoveFromWeakMap() {
     if (weak_map_ && weak_map_->Has(weak_map_id()))
@@ -103,28 +105,49 @@ class TrackableObject : public TrackableObjectBase {
   }
 
  protected:
+  TrackableObject() {}
   ~TrackableObject() override {
     RemoveFromWeakMap();
   }
 
   void AfterInit(v8::Isolate* isolate) override {
-    if (!weak_map_)
+    if (!weak_map_) {
       weak_map_.reset(new atom::IDWeakMap);
+      RegisterDestructionCallback(
+          base::Bind(&TrackableObject<T>::ReleaseAllWeakReferences));
+    }
     weak_map_id_ = weak_map_->Add(isolate, GetWrapper(isolate));
     TrackableObjectBase::AfterInit(isolate);
   }
 
  private:
+  // mate::Wrappable:
+  mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
+      v8::Isolate* isolate) override {
+    if (template_.IsEmpty()) {
+      auto templ = v8::ObjectTemplate::New(isolate);
+      T::BuildPrototype(isolate, templ);
+      template_.Reset(isolate, templ);
+    }
+
+    return ObjectTemplateBuilder(
+        isolate, v8::Local<v8::ObjectTemplate>::New(isolate, template_));
+  }
+
   // Releases all weak references in weak map, called when app is terminating.
   static void ReleaseAllWeakReferences() {
     weak_map_.reset();
   }
 
+  static v8::Persistent<v8::ObjectTemplate> template_;
   static scoped_ptr<atom::IDWeakMap> weak_map_;
 
   DISALLOW_COPY_AND_ASSIGN(TrackableObject);
 };
 
+template<typename T>
+v8::Persistent<v8::ObjectTemplate> TrackableObject<T>::template_;
+
 template<typename T>
 scoped_ptr<atom::IDWeakMap> TrackableObject<T>::weak_map_;
 

+ 3 - 3
atom/browser/atom_access_token_store.cc

@@ -18,7 +18,7 @@ namespace {
 // Notice that we just combined the api key with the url together here, because
 // if we use the standard {url: key} format Chromium would override our key with
 // the predefined one in common.gypi of libchromiumcontent, which is empty.
-const char* kGeolocationProviderUrl =
+const char* kGeolocationProviderURL =
     "https://www.googleapis.com/geolocation/v1/geolocate?key="
     GOOGLEAPIS_API_KEY;
 
@@ -35,11 +35,11 @@ void AtomAccessTokenStore::LoadAccessTokens(
     const LoadAccessTokensCallbackType& callback) {
   AccessTokenSet access_token_set;
 
-  // Equivelent to access_token_set[kGeolocationProviderUrl].
+  // Equivelent to access_token_set[kGeolocationProviderURL].
   // Somehow base::string16 is causing compilation errors when used in a pair
   // of std::map on Linux, this can work around it.
   std::pair<GURL, base::string16> token_pair;
-  token_pair.first = GURL(kGeolocationProviderUrl);
+  token_pair.first = GURL(kGeolocationProviderURL);
   access_token_set.insert(token_pair);
 
   auto browser_context = AtomBrowserMainParts::Get()->browser_context();

+ 35 - 8
atom/browser/atom_browser_client.cc

@@ -12,8 +12,8 @@
 #include "atom/browser/atom_browser_context.h"
 #include "atom/browser/atom_browser_main_parts.h"
 #include "atom/browser/atom_quota_permission_context.h"
+#include "atom/browser/atom_resource_dispatcher_host_delegate.h"
 #include "atom/browser/atom_speech_recognition_manager_delegate.h"
-#include "atom/browser/browser.h"
 #include "atom/browser/native_window.h"
 #include "atom/browser/web_contents_preferences.h"
 #include "atom/browser/window_list.h"
@@ -30,6 +30,7 @@
 #include "content/public/browser/client_certificate_delegate.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
+#include "content/public/browser/resource_dispatcher_host.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/web_preferences.h"
@@ -83,10 +84,10 @@ void AtomBrowserClient::SuppressRendererProcessRestartForOnce() {
 
 void AtomBrowserClient::SetCustomSchemes(
     const std::vector<std::string>& schemes) {
-  g_custom_schemes = JoinString(schemes, ',');
+  g_custom_schemes = base::JoinString(schemes, ",");
 }
 
-AtomBrowserClient::AtomBrowserClient() {
+AtomBrowserClient::AtomBrowserClient() : delegate_(nullptr) {
 }
 
 AtomBrowserClient::~AtomBrowserClient() {
@@ -115,7 +116,6 @@ void AtomBrowserClient::OverrideWebkitPrefs(
   prefs->javascript_can_open_windows_automatically = true;
   prefs->plugins_enabled = true;
   prefs->dom_paste_enabled = true;
-  prefs->java_enabled = false;
   prefs->allow_scripts_to_close_windows = true;
   prefs->javascript_can_access_clipboard = true;
   prefs->local_storage_enabled = true;
@@ -206,6 +206,26 @@ content::QuotaPermissionContext*
   return new AtomQuotaPermissionContext;
 }
 
+void AtomBrowserClient::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) {
+  if (delegate_) {
+    delegate_->AllowCertificateError(
+        render_process_id, render_frame_id, cert_error, ssl_info, request_url,
+        resource_type, overridable, strict_enforcement,
+        expired_previous_decision, callback, request);
+  }
+}
+
 void AtomBrowserClient::SelectClientCertificate(
     content::WebContents* web_contents,
     net::SSLCertRequestInfo* cert_request_info,
@@ -220,10 +240,17 @@ void AtomBrowserClient::SelectClientCertificate(
     return;
   }
 
-  if (!cert_request_info->client_certs.empty())
-    Browser::Get()->ClientCertificateSelector(web_contents,
-                                              cert_request_info,
-                                              delegate.Pass());
+  if (!cert_request_info->client_certs.empty() && delegate_) {
+    delegate_->SelectClientCertificate(
+        web_contents, cert_request_info, delegate.Pass());
+  }
+}
+
+void AtomBrowserClient::ResourceDispatcherHostCreated() {
+  resource_dispatcher_host_delegate_.reset(
+      new AtomResourceDispatcherHostDelegate);
+  content::ResourceDispatcherHost::Get()->SetDelegate(
+      resource_dispatcher_host_delegate_.get());
 }
 
 brightray::BrowserMainParts* AtomBrowserClient::OverrideCreateBrowserMainParts(

+ 23 - 0
atom/browser/atom_browser_client.h

@@ -23,12 +23,17 @@ class SSLCertRequestInfo;
 
 namespace atom {
 
+class AtomResourceDispatcherHostDelegate;
+
 class AtomBrowserClient : public brightray::BrowserClient,
                           public content::RenderProcessHostObserver {
  public:
   AtomBrowserClient();
   virtual ~AtomBrowserClient();
 
+  using Delegate = content::ContentBrowserClient;
+  void set_delegate(Delegate* delegate) { delegate_ = delegate; }
+
   // Don't force renderer process to restart for once.
   static void SuppressRendererProcessRestartForOnce();
   // Custom schemes to be registered to standard.
@@ -52,10 +57,23 @@ class AtomBrowserClient : public brightray::BrowserClient,
                                       int child_process_id) override;
   void DidCreatePpapiPlugin(content::BrowserPpapiHost* browser_host) override;
   content::QuotaPermissionContext* CreateQuotaPermissionContext() override;
+  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;
+  void ResourceDispatcherHostCreated() override;
 
   // brightray::BrowserClient:
   brightray::BrowserMainParts* OverrideCreateBrowserMainParts(
@@ -68,6 +86,11 @@ class AtomBrowserClient : public brightray::BrowserClient,
   // pending_render_process => current_render_process.
   std::map<int, int> pending_processes_;
 
+  scoped_ptr<AtomResourceDispatcherHostDelegate>
+      resource_dispatcher_host_delegate_;
+
+  Delegate* delegate_;
+
   DISALLOW_COPY_AND_ASSIGN(AtomBrowserClient);
 };
 

+ 24 - 4
atom/browser/atom_browser_context.cc

@@ -6,8 +6,9 @@
 
 #include "atom/browser/atom_browser_main_parts.h"
 #include "atom/browser/atom_download_manager_delegate.h"
-#include "atom/browser/atom_ssl_config_service.h"
 #include "atom/browser/browser.h"
+#include "atom/browser/net/atom_cert_verifier.h"
+#include "atom/browser/net/atom_ssl_config_service.h"
 #include "atom/browser/net/atom_url_request_job_factory.h"
 #include "atom/browser/net/asar/asar_protocol_handler.h"
 #include "atom/browser/net/http_protocol_handler.h"
@@ -60,7 +61,9 @@ std::string RemoveWhitespace(const std::string& str) {
 AtomBrowserContext::AtomBrowserContext(const std::string& partition,
                                        bool in_memory)
     : brightray::BrowserContext(partition, in_memory),
-      job_factory_(new AtomURLRequestJobFactory) {
+      cert_verifier_(nullptr),
+      job_factory_(new AtomURLRequestJobFactory),
+      allow_ntlm_everywhere_(false) {
 }
 
 AtomBrowserContext::~AtomBrowserContext() {
@@ -83,7 +86,8 @@ std::string AtomBrowserContext::GetUserAgent() {
   return content::BuildUserAgentFromProduct(user_agent);
 }
 
-net::URLRequestJobFactory* AtomBrowserContext::CreateURLRequestJobFactory(
+scoped_ptr<net::URLRequestJobFactory>
+AtomBrowserContext::CreateURLRequestJobFactory(
     content::ProtocolHandlerMap* handlers,
     content::URLRequestInterceptorScopedVector* interceptors) {
   scoped_ptr<AtomURLRequestJobFactory> job_factory(job_factory_);
@@ -128,7 +132,7 @@ net::URLRequestJobFactory* AtomBrowserContext::CreateURLRequestJobFactory(
         top_job_factory.Pass(), make_scoped_ptr(*it)));
   interceptors->weak_clear();
 
-  return top_job_factory.release();
+  return top_job_factory.Pass();
 }
 
 net::HttpCache::BackendFactory*
@@ -157,6 +161,12 @@ content::BrowserPluginGuestManager* AtomBrowserContext::GetGuestManager() {
   return guest_manager_.get();
 }
 
+scoped_ptr<net::CertVerifier> AtomBrowserContext::CreateCertVerifier() {
+  DCHECK(!cert_verifier_);
+  cert_verifier_ = new AtomCertVerifier;
+  return make_scoped_ptr(cert_verifier_);
+}
+
 net::SSLConfigService* AtomBrowserContext::CreateSSLConfigService() {
   return new AtomSSLConfigService;
 }
@@ -168,6 +178,16 @@ void AtomBrowserContext::RegisterPrefs(PrefRegistrySimple* pref_registry) {
                                       base::FilePath());
 }
 
+bool AtomBrowserContext::AllowNTLMCredentialsForDomain(const GURL& origin) {
+  if (allow_ntlm_everywhere_)
+    return true;
+  return Delegate::AllowNTLMCredentialsForDomain(origin);
+}
+
+void AtomBrowserContext::AllowNTLMCredentialsForAllDomains(bool should_allow) {
+  allow_ntlm_everywhere_ = should_allow;
+}
+
 }  // namespace atom
 
 namespace brightray {

+ 11 - 1
atom/browser/atom_browser_context.h

@@ -12,6 +12,7 @@
 namespace atom {
 
 class AtomDownloadManagerDelegate;
+class AtomCertVerifier;
 class AtomURLRequestJobFactory;
 class WebViewManager;
 
@@ -22,12 +23,14 @@ class AtomBrowserContext : public brightray::BrowserContext {
 
   // brightray::URLRequestContextGetter::Delegate:
   std::string GetUserAgent() override;
-  net::URLRequestJobFactory* CreateURLRequestJobFactory(
+  scoped_ptr<net::URLRequestJobFactory> CreateURLRequestJobFactory(
       content::ProtocolHandlerMap* handlers,
       content::URLRequestInterceptorScopedVector* interceptors) override;
   net::HttpCache::BackendFactory* CreateHttpCacheBackendFactory(
       const base::FilePath& base_path) override;
+  scoped_ptr<net::CertVerifier> CreateCertVerifier() override;
   net::SSLConfigService* CreateSSLConfigService() override;
+  bool AllowNTLMCredentialsForDomain(const GURL& auth_origin) override;
 
   // content::BrowserContext:
   content::DownloadManagerDelegate* GetDownloadManagerDelegate() override;
@@ -36,6 +39,10 @@ class AtomBrowserContext : public brightray::BrowserContext {
   // brightray::BrowserContext:
   void RegisterPrefs(PrefRegistrySimple* pref_registry) override;
 
+  void AllowNTLMCredentialsForAllDomains(bool should_allow);
+
+  AtomCertVerifier* cert_verifier() const { return cert_verifier_; }
+
   AtomURLRequestJobFactory* job_factory() const { return job_factory_; }
 
  private:
@@ -43,8 +50,11 @@ class AtomBrowserContext : public brightray::BrowserContext {
   scoped_ptr<WebViewManager> guest_manager_;
 
   // Managed by brightray::BrowserContext.
+  AtomCertVerifier* cert_verifier_;
   AtomURLRequestJobFactory* job_factory_;
 
+  bool allow_ntlm_everywhere_;
+
   DISALLOW_COPY_AND_ASSIGN(AtomBrowserContext);
 };
 

+ 64 - 17
atom/browser/atom_browser_main_parts.cc

@@ -25,11 +25,17 @@
 
 namespace atom {
 
+template<typename T>
+void Erase(T* container, typename T::iterator iter) {
+  container->erase(iter);
+}
+
 // static
 AtomBrowserMainParts* AtomBrowserMainParts::self_ = NULL;
 
 AtomBrowserMainParts::AtomBrowserMainParts()
     : fake_browser_process_(new BrowserProcess),
+      exit_code_(nullptr),
       browser_(new Browser),
       node_bindings_(NodeBindings::Create(true)),
       atom_bindings_(new AtomBindings),
@@ -47,29 +53,39 @@ AtomBrowserMainParts* AtomBrowserMainParts::Get() {
   return self_;
 }
 
-void AtomBrowserMainParts::RegisterDestructionCallback(
+bool AtomBrowserMainParts::SetExitCode(int code) {
+  if (!exit_code_)
+    return false;
+
+  *exit_code_ = code;
+  return true;
+}
+
+base::Closure AtomBrowserMainParts::RegisterDestructionCallback(
     const base::Closure& callback) {
-  destruction_callbacks_.push_back(callback);
+  auto iter = destructors_.insert(destructors_.end(), callback);
+  return base::Bind(&Erase<std::list<base::Closure>>, &destructors_, iter);
+}
+
+void AtomBrowserMainParts::PreEarlyInitialization() {
+  brightray::BrowserMainParts::PreEarlyInitialization();
+#if defined(OS_POSIX)
+  HandleSIGCHLD();
+#endif
 }
 
 void AtomBrowserMainParts::PostEarlyInitialization() {
   brightray::BrowserMainParts::PostEarlyInitialization();
 
-#if defined(USE_X11)
-  SetDPIFromGSettings();
-#endif
+  // Temporary set the bridge_task_runner_ as current thread's task runner,
+  // so we can fool gin::PerIsolateData to use it as its task runner, instead
+  // of getting current message loop's task runner, which is null for now.
+  bridge_task_runner_ = new BridgeTaskRunner;
+  base::ThreadTaskRunnerHandle handle(bridge_task_runner_);
 
-  {
-    // Temporary set the bridge_task_runner_ as current thread's task runner,
-    // so we can fool gin::PerIsolateData to use it as its task runner, instead
-    // of getting current message loop's task runner, which is null for now.
-    bridge_task_runner_ = new BridgeTaskRunner;
-    base::ThreadTaskRunnerHandle handle(bridge_task_runner_);
-
-    // The ProxyResolverV8 has setup a complete V8 environment, in order to
-    // avoid conflicts we only initialize our V8 environment after that.
-    js_env_.reset(new JavascriptEnvironment);
-  }
+  // The ProxyResolverV8 has setup a complete V8 environment, in order to
+  // avoid conflicts we only initialize our V8 environment after that.
+  js_env_.reset(new JavascriptEnvironment);
 
   node_bindings_->Initialize();
 
@@ -104,6 +120,8 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() {
                  1000));
 
   brightray::BrowserMainParts::PreMainMessageLoopRun();
+  bridge_task_runner_->MessageLoopIsReady();
+  bridge_task_runner_ = nullptr;
 
 #if defined(USE_X11)
   libgtk2ui::GtkInitFromCommandLine(*base::CommandLine::ForCurrentProcess());
@@ -116,14 +134,43 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() {
 #endif
 }
 
+bool AtomBrowserMainParts::MainMessageLoopRun(int* result_code) {
+  exit_code_ = result_code;
+  return brightray::BrowserMainParts::MainMessageLoopRun(result_code);
+}
+
+void AtomBrowserMainParts::PostMainMessageLoopStart() {
+  brightray::BrowserMainParts::PostMainMessageLoopStart();
+#if defined(OS_POSIX)
+  HandleShutdownSignals();
+#endif
+}
+
 void AtomBrowserMainParts::PostMainMessageLoopRun() {
   brightray::BrowserMainParts::PostMainMessageLoopRun();
 
+#if defined(OS_MACOSX)
+  FreeAppDelegate();
+#endif
+
   // Make sure destruction callbacks are called before message loop is
   // destroyed, otherwise some objects that need to be deleted on IO thread
   // won't be freed.
-  for (const auto& callback : destruction_callbacks_)
+  // We don't use ranged for loop because iterators are getting invalided when
+  // the callback runs.
+  for (auto iter = destructors_.begin(); iter != destructors_.end();) {
+    base::Closure& callback = *iter;
+    ++iter;
     callback.Run();
+  }
+
+  // Destroy JavaScript environment immediately after running destruction
+  // callbacks.
+  gc_timer_.Stop();
+  node_debugger_.reset();
+  atom_bindings_.reset();
+  node_bindings_.reset();
+  js_env_.reset();
 }
 
 }  // namespace atom

+ 20 - 5
atom/browser/atom_browser_main_parts.h

@@ -31,25 +31,37 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts {
 
   static AtomBrowserMainParts* Get();
 
+  // Sets the exit code, will fail if the the message loop is not ready.
+  bool SetExitCode(int code);
+
   // Register a callback that should be destroyed before JavaScript environment
   // gets destroyed.
-  void RegisterDestructionCallback(const base::Closure& callback);
+  // Returns a closure that can be used to remove |callback| from the list.
+  base::Closure RegisterDestructionCallback(const base::Closure& callback);
 
   Browser* browser() { return browser_.get(); }
 
  protected:
   // content::BrowserMainParts:
+  void PreEarlyInitialization() override;
   void PostEarlyInitialization() override;
   void PreMainMessageLoopRun() override;
+  bool MainMessageLoopRun(int* result_code) override;
+  void PostMainMessageLoopStart() override;
   void PostMainMessageLoopRun() override;
 #if defined(OS_MACOSX)
   void PreMainMessageLoopStart() override;
-  void PostDestroyThreads() override;
 #endif
 
  private:
-#if defined(USE_X11)
-  void SetDPIFromGSettings();
+#if defined(OS_POSIX)
+  // Set signal handlers.
+  void HandleSIGCHLD();
+  void HandleShutdownSignals();
+#endif
+
+#if defined(OS_MACOSX)
+  void FreeAppDelegate();
 #endif
 
   // A fake BrowserProcess object that used to feed the source code from chrome.
@@ -59,6 +71,9 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts {
   // with a task runner that will post all work to main loop.
   scoped_refptr<BridgeTaskRunner> bridge_task_runner_;
 
+  // Pointer to exit code.
+  int* exit_code_;
+
   scoped_ptr<Browser> browser_;
   scoped_ptr<JavascriptEnvironment> js_env_;
   scoped_ptr<NodeBindings> node_bindings_;
@@ -68,7 +83,7 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts {
   base::Timer gc_timer_;
 
   // List of callbacks should be executed before destroying JS env.
-  std::list<base::Closure> destruction_callbacks_;
+  std::list<base::Closure> destructors_;
 
   static AtomBrowserMainParts* self_;
 

+ 0 - 73
atom/browser/atom_browser_main_parts_linux.cc

@@ -1,73 +0,0 @@
-// Copyright (c) 2014 GitHub, Inc.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#include "atom/browser/atom_browser_main_parts.h"
-
-#include <gio/gio.h>
-
-#include "base/command_line.h"
-#include "base/strings/string_number_conversions.h"
-#include "ui/gfx/switches.h"
-
-namespace atom {
-
-namespace {
-
-const char* kInterfaceSchema = "org.gnome.desktop.interface";
-const char* kScaleFactor = "scaling-factor";
-
-bool SchemaExists(const char* schema_name) {
-  const gchar* const* schemas = g_settings_list_schemas();
-  while (*schemas) {
-    if (strcmp(schema_name, static_cast<const char*>(*schemas)) == 0)
-      return true;
-    schemas++;
-  }
-  return false;
-}
-
-bool KeyExists(GSettings* client, const char* key) {
-  gchar** keys = g_settings_list_keys(client);
-  if (!keys)
-    return false;
-
-  gchar** iter = keys;
-  while (*iter) {
-    if (strcmp(*iter, key) == 0)
-      break;
-    iter++;
-  }
-
-  bool exists = *iter != NULL;
-  g_strfreev(keys);
-  return exists;
-}
-
-void GetDPIFromGSettings(guint* scale_factor) {
-  GSettings* client = nullptr;
-  if (!SchemaExists(kInterfaceSchema) ||
-      !(client = g_settings_new(kInterfaceSchema))) {
-    VLOG(1) << "Cannot create gsettings client.";
-    return;
-  }
-
-  if (KeyExists(client, kScaleFactor))
-    *scale_factor = g_settings_get_uint(client, kScaleFactor);
-
-  g_object_unref(client);
-}
-
-}  // namespace
-
-void AtomBrowserMainParts::SetDPIFromGSettings() {
-  guint scale_factor = 1;
-  GetDPIFromGSettings(&scale_factor);
-  if (scale_factor == 0)
-    scale_factor = 1;
-
-  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-      switches::kForceDeviceScaleFactor, base::UintToString(scale_factor));
-}
-
-}  // namespace atom

+ 1 - 1
atom/browser/atom_browser_main_parts_mac.mm

@@ -34,7 +34,7 @@ void AtomBrowserMainParts::PreMainMessageLoopStart() {
       setObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"];
 }
 
-void AtomBrowserMainParts::PostDestroyThreads() {
+void AtomBrowserMainParts::FreeAppDelegate() {
   [[NSApp delegate] release];
   [NSApp setDelegate:nil];
 }

+ 225 - 0
atom/browser/atom_browser_main_parts_posix.cc

@@ -0,0 +1,225 @@
+// Copyright (c) 2015 GitHub, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+// Most code came from: chrome/browser/chrome_browser_main_posix.cc.
+
+#include "atom/browser/atom_browser_main_parts.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <pthread.h>
+#include <signal.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#include "atom/browser/browser.h"
+#include "base/posix/eintr_wrapper.h"
+#include "content/public/browser/browser_thread.h"
+
+using content::BrowserThread;
+
+namespace atom {
+
+namespace {
+
+// See comment in |PreEarlyInitialization()|, where sigaction is called.
+void SIGCHLDHandler(int signal) {
+}
+
+// The OSX fork() implementation can crash in the child process before
+// fork() returns.  In that case, the shutdown pipe will still be
+// shared with the parent process.  To prevent child crashes from
+// causing parent shutdowns, |g_pipe_pid| is the pid for the process
+// which registered |g_shutdown_pipe_write_fd|.
+// See <http://crbug.com/175341>.
+pid_t g_pipe_pid = -1;
+int g_shutdown_pipe_write_fd = -1;
+int g_shutdown_pipe_read_fd = -1;
+
+// Common code between SIG{HUP, INT, TERM}Handler.
+void GracefulShutdownHandler(int signal) {
+  // Reinstall the default handler.  We had one shot at graceful shutdown.
+  struct sigaction action;
+  memset(&action, 0, sizeof(action));
+  action.sa_handler = SIG_DFL;
+  RAW_CHECK(sigaction(signal, &action, NULL) == 0);
+
+  RAW_CHECK(g_pipe_pid == getpid());
+  RAW_CHECK(g_shutdown_pipe_write_fd != -1);
+  RAW_CHECK(g_shutdown_pipe_read_fd != -1);
+  size_t bytes_written = 0;
+  do {
+    int rv = HANDLE_EINTR(
+        write(g_shutdown_pipe_write_fd,
+              reinterpret_cast<const char*>(&signal) + bytes_written,
+              sizeof(signal) - bytes_written));
+    RAW_CHECK(rv >= 0);
+    bytes_written += rv;
+  } while (bytes_written < sizeof(signal));
+}
+
+// See comment in |PostMainMessageLoopStart()|, where sigaction is called.
+void SIGHUPHandler(int signal) {
+  RAW_CHECK(signal == SIGHUP);
+  GracefulShutdownHandler(signal);
+}
+
+// See comment in |PostMainMessageLoopStart()|, where sigaction is called.
+void SIGINTHandler(int signal) {
+  RAW_CHECK(signal == SIGINT);
+  GracefulShutdownHandler(signal);
+}
+
+// See comment in |PostMainMessageLoopStart()|, where sigaction is called.
+void SIGTERMHandler(int signal) {
+  RAW_CHECK(signal == SIGTERM);
+  GracefulShutdownHandler(signal);
+}
+
+class ShutdownDetector : public base::PlatformThread::Delegate {
+ public:
+  explicit ShutdownDetector(int shutdown_fd);
+
+  void ThreadMain() override;
+
+ private:
+  const int shutdown_fd_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShutdownDetector);
+};
+
+ShutdownDetector::ShutdownDetector(int shutdown_fd)
+    : shutdown_fd_(shutdown_fd) {
+  CHECK_NE(shutdown_fd_, -1);
+}
+
+// These functions are used to help us diagnose crash dumps that happen
+// during the shutdown process.
+NOINLINE void ShutdownFDReadError() {
+  // Ensure function isn't optimized away.
+  asm("");
+  sleep(UINT_MAX);
+}
+
+NOINLINE void ShutdownFDClosedError() {
+  // Ensure function isn't optimized away.
+  asm("");
+  sleep(UINT_MAX);
+}
+
+NOINLINE void ExitPosted() {
+  // Ensure function isn't optimized away.
+  asm("");
+  sleep(UINT_MAX);
+}
+
+void ShutdownDetector::ThreadMain() {
+  base::PlatformThread::SetName("CrShutdownDetector");
+
+  int signal;
+  size_t bytes_read = 0;
+  ssize_t ret;
+  do {
+    ret = HANDLE_EINTR(
+        read(shutdown_fd_,
+             reinterpret_cast<char*>(&signal) + bytes_read,
+             sizeof(signal) - bytes_read));
+    if (ret < 0) {
+      NOTREACHED() << "Unexpected error: " << strerror(errno);
+      ShutdownFDReadError();
+      break;
+    } else if (ret == 0) {
+      NOTREACHED() << "Unexpected closure of shutdown pipe.";
+      ShutdownFDClosedError();
+      break;
+    }
+    bytes_read += ret;
+  } while (bytes_read < sizeof(signal));
+  VLOG(1) << "Handling shutdown for signal " << signal << ".";
+  base::Closure task =
+      base::Bind(&Browser::Quit, base::Unretained(Browser::Get()));
+
+  if (!BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, task)) {
+    // Without a UI thread to post the exit task to, there aren't many
+    // options.  Raise the signal again.  The default handler will pick it up
+    // and cause an ungraceful exit.
+    RAW_LOG(WARNING, "No UI thread, exiting ungracefully.");
+    kill(getpid(), signal);
+
+    // The signal may be handled on another thread.  Give that a chance to
+    // happen.
+    sleep(3);
+
+    // We really should be dead by now.  For whatever reason, we're not. Exit
+    // immediately, with the exit status set to the signal number with bit 8
+    // set.  On the systems that we care about, this exit status is what is
+    // normally used to indicate an exit by this signal's default handler.
+    // This mechanism isn't a de jure standard, but even in the worst case, it
+    // should at least result in an immediate exit.
+    RAW_LOG(WARNING, "Still here, exiting really ungracefully.");
+    _exit(signal | (1 << 7));
+  }
+  ExitPosted();
+}
+
+}  // namespace
+
+void AtomBrowserMainParts::HandleSIGCHLD() {
+  // We need to accept SIGCHLD, even though our handler is a no-op because
+  // otherwise we cannot wait on children. (According to POSIX 2001.)
+  struct sigaction action;
+  memset(&action, 0, sizeof(action));
+  action.sa_handler = SIGCHLDHandler;
+  CHECK_EQ(sigaction(SIGCHLD, &action, NULL), 0);
+}
+
+void AtomBrowserMainParts::HandleShutdownSignals() {
+  int pipefd[2];
+  int ret = pipe(pipefd);
+  if (ret < 0) {
+    PLOG(DFATAL) << "Failed to create pipe";
+  } else {
+    g_pipe_pid = getpid();
+    g_shutdown_pipe_read_fd = pipefd[0];
+    g_shutdown_pipe_write_fd = pipefd[1];
+#if !defined(ADDRESS_SANITIZER) && !defined(KEEP_SHADOW_STACKS)
+    const size_t kShutdownDetectorThreadStackSize = PTHREAD_STACK_MIN * 2;
+#else
+    // ASan instrumentation and -finstrument-functions (used for keeping the
+    // shadow stacks) bloat the stack frames, so we need to increase the stack
+    // size to avoid hitting the guard page.
+    const size_t kShutdownDetectorThreadStackSize = PTHREAD_STACK_MIN * 4;
+#endif
+    // TODO(viettrungluu,willchan): crbug.com/29675 - This currently leaks, so
+    // if you change this, you'll probably need to change the suppression.
+    if (!base::PlatformThread::CreateNonJoinable(
+            kShutdownDetectorThreadStackSize,
+            new ShutdownDetector(g_shutdown_pipe_read_fd))) {
+      LOG(DFATAL) << "Failed to create shutdown detector task.";
+    }
+  }
+  // Setup signal handlers for shutdown AFTER shutdown pipe is setup because
+  // it may be called right away after handler is set.
+
+  // If adding to this list of signal handlers, note the new signal probably
+  // needs to be reset in child processes. See
+  // base/process_util_posix.cc:LaunchProcess.
+
+  // We need to handle SIGTERM, because that is how many POSIX-based distros ask
+  // processes to quit gracefully at shutdown time.
+  struct sigaction action;
+  memset(&action, 0, sizeof(action));
+  action.sa_handler = SIGTERMHandler;
+  CHECK_EQ(sigaction(SIGTERM, &action, NULL), 0);
+  // Also handle SIGINT - when the user terminates the browser via Ctrl+C. If
+  // the browser process is being debugged, GDB will catch the SIGINT first.
+  action.sa_handler = SIGINTHandler;
+  CHECK_EQ(sigaction(SIGINT, &action, NULL), 0);
+  // And SIGHUP, for when the terminal disappears. On shutdown, many Linux
+  // distros send SIGHUP, SIGTERM, and then SIGKILL.
+  action.sa_handler = SIGHUPHandler;
+  CHECK_EQ(sigaction(SIGHUP, &action, NULL), 0);
+}
+
+}  // namespace atom

+ 40 - 0
atom/browser/atom_resource_dispatcher_host_delegate.cc

@@ -0,0 +1,40 @@
+// 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/atom_resource_dispatcher_host_delegate.h"
+
+#include "atom/browser/login_handler.h"
+#include "atom/common/platform_util.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/escape.h"
+#include "url/gurl.h"
+
+using content::BrowserThread;
+
+namespace atom {
+
+AtomResourceDispatcherHostDelegate::AtomResourceDispatcherHostDelegate() {
+}
+
+bool AtomResourceDispatcherHostDelegate::HandleExternalProtocol(
+    const GURL& url,
+    int render_process_id,
+    int render_view_id,
+    bool is_main_frame,
+    ui::PageTransition transition,
+    bool has_user_gesture) {
+  GURL escaped_url(net::EscapeExternalHandlerValue(url.spec()));
+  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+      base::Bind(base::IgnoreResult(platform_util::OpenExternal), escaped_url));
+  return true;
+}
+
+content::ResourceDispatcherHostLoginDelegate*
+AtomResourceDispatcherHostDelegate::CreateLoginDelegate(
+    net::AuthChallengeInfo* auth_info,
+    net::URLRequest* request) {
+  return new LoginHandler(auth_info, request);
+}
+
+}  // namespace atom

+ 31 - 0
atom/browser/atom_resource_dispatcher_host_delegate.h

@@ -0,0 +1,31 @@
+// 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_ATOM_RESOURCE_DISPATCHER_HOST_DELEGATE_H_
+#define ATOM_BROWSER_ATOM_RESOURCE_DISPATCHER_HOST_DELEGATE_H_
+
+#include "content/public/browser/resource_dispatcher_host_delegate.h"
+
+namespace atom {
+
+class AtomResourceDispatcherHostDelegate
+    : public content::ResourceDispatcherHostDelegate {
+ public:
+  AtomResourceDispatcherHostDelegate();
+
+  // content::ResourceDispatcherHostDelegate:
+  bool HandleExternalProtocol(const GURL& url,
+                              int render_process_id,
+                              int render_view_id,
+                              bool is_main_frame,
+                              ui::PageTransition transition,
+                              bool has_user_gesture) override;
+  content::ResourceDispatcherHostLoginDelegate* CreateLoginDelegate(
+      net::AuthChallengeInfo* auth_info,
+      net::URLRequest* request) override;
+};
+
+}  // namespace atom
+
+#endif  // ATOM_BROWSER_ATOM_RESOURCE_DISPATCHER_HOST_DELEGATE_H_

+ 14 - 3
atom/browser/auto_updater.cc

@@ -6,14 +6,25 @@
 
 namespace auto_updater {
 
-AutoUpdaterDelegate* AutoUpdater::delegate_ = NULL;
+Delegate* AutoUpdater::delegate_ = nullptr;
 
-AutoUpdaterDelegate* AutoUpdater::GetDelegate() {
+Delegate* AutoUpdater::GetDelegate() {
   return delegate_;
 }
 
-void AutoUpdater::SetDelegate(AutoUpdaterDelegate* delegate) {
+void AutoUpdater::SetDelegate(Delegate* delegate) {
   delegate_ = delegate;
 }
 
+#if !defined(OS_MACOSX) || defined(MAS_BUILD)
+void AutoUpdater::SetFeedURL(const std::string& url) {
+}
+
+void AutoUpdater::CheckForUpdates() {
+}
+
+void AutoUpdater::QuitAndInstall() {
+}
+#endif
+
 }  // namespace auto_updater

+ 31 - 4
atom/browser/auto_updater.h

@@ -9,21 +9,48 @@
 
 #include "base/basictypes.h"
 
+namespace base {
+class Time;
+}
+
 namespace auto_updater {
 
-class AutoUpdaterDelegate;
+class Delegate {
+ public:
+  // An error happened.
+  virtual void OnError(const std::string& error) {}
+
+  // Checking to see if there is an update
+  virtual void OnCheckingForUpdate() {}
+
+  // There is an update available and it is being downloaded
+  virtual void OnUpdateAvailable() {}
+
+  // There is no available update.
+  virtual void OnUpdateNotAvailable() {}
+
+  // There is a new update which has been downloaded.
+  virtual void OnUpdateDownloaded(const std::string& release_notes,
+                                  const std::string& release_name,
+                                  const base::Time& release_date,
+                                  const std::string& update_url) {}
+
+ protected:
+  virtual ~Delegate() {}
+};
 
 class AutoUpdater {
  public:
   // Gets/Sets the delegate.
-  static AutoUpdaterDelegate* GetDelegate();
-  static void SetDelegate(AutoUpdaterDelegate* delegate);
+  static Delegate* GetDelegate();
+  static void SetDelegate(Delegate* delegate);
 
   static void SetFeedURL(const std::string& url);
   static void CheckForUpdates();
+  static void QuitAndInstall();
 
  private:
-  static AutoUpdaterDelegate* delegate_;
+  static Delegate* delegate_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(AutoUpdater);
 };

+ 0 - 45
atom/browser/auto_updater_delegate.h

@@ -1,45 +0,0 @@
-// Copyright (c) 2013 GitHub, Inc.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#ifndef ATOM_BROWSER_AUTO_UPDATER_DELEGATE_H_
-#define ATOM_BROWSER_AUTO_UPDATER_DELEGATE_H_
-
-#include <string>
-
-#include "base/callback_forward.h"
-
-namespace base {
-class Time;
-}
-
-namespace auto_updater {
-
-class AutoUpdaterDelegate {
- public:
-  // An error happened.
-  virtual void OnError(const std::string& error) {}
-
-  // Checking to see if there is an update
-  virtual void OnCheckingForUpdate() {}
-
-  // There is an update available and it is being downloaded
-  virtual void OnUpdateAvailable() {}
-
-  // There is no available update.
-  virtual void OnUpdateNotAvailable() {}
-
-  // There is a new update which has been downloaded.
-  virtual 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) {}
-
- protected:
-  virtual ~AutoUpdaterDelegate() {}
-};
-
-}  // namespace auto_updater
-
-#endif  // ATOM_BROWSER_AUTO_UPDATER_DELEGATE_H_

+ 0 - 17
atom/browser/auto_updater_linux.cc

@@ -1,17 +0,0 @@
-// Copyright (c) 2013 GitHub, Inc.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#include "atom/browser/auto_updater.h"
-
-namespace auto_updater {
-
-// static
-void AutoUpdater::SetFeedURL(const std::string& url) {
-}
-
-// static
-void AutoUpdater::CheckForUpdates() {
-}
-
-}  // namespace auto_updater

+ 11 - 15
atom/browser/auto_updater_mac.mm

@@ -12,9 +12,6 @@
 #include "base/bind.h"
 #include "base/time/time.h"
 #include "base/strings/sys_string_conversions.h"
-#include "atom/browser/auto_updater_delegate.h"
-
-#include <iostream>
 
 namespace auto_updater {
 
@@ -23,20 +20,12 @@ namespace {
 // The gloal SQRLUpdater object.
 SQRLUpdater* g_updater = nil;
 
-void RelaunchToInstallUpdate() {
-  [[g_updater relaunchToInstallUpdate] subscribeError:^(NSError* error) {
-    AutoUpdaterDelegate* delegate = AutoUpdater::GetDelegate();
-    if (delegate)
-      delegate->OnError(base::SysNSStringToUTF8(error.localizedDescription));
-  }];
-}
-
 }  // namespace
 
 // static
 void AutoUpdater::SetFeedURL(const std::string& feed) {
   if (g_updater == nil) {
-    AutoUpdaterDelegate* delegate = GetDelegate();
+    Delegate* delegate = GetDelegate();
     if (!delegate)
       return;
 
@@ -67,7 +56,7 @@ void AutoUpdater::SetFeedURL(const std::string& feed) {
 
 // static
 void AutoUpdater::CheckForUpdates() {
-  AutoUpdaterDelegate* delegate = GetDelegate();
+  Delegate* delegate = GetDelegate();
   if (!delegate)
     return;
 
@@ -86,8 +75,7 @@ void AutoUpdater::CheckForUpdates() {
             base::SysNSStringToUTF8(update.releaseNotes),
             base::SysNSStringToUTF8(update.releaseName),
             base::Time::FromDoubleT(update.releaseDate.timeIntervalSince1970),
-            base::SysNSStringToUTF8(update.updateURL.absoluteString),
-            base::Bind(RelaunchToInstallUpdate));
+            base::SysNSStringToUTF8(update.updateURL.absoluteString));
         } else {
           // When the completed event is sent with no update, then we know there
           // is no update available.
@@ -100,4 +88,12 @@ void AutoUpdater::CheckForUpdates() {
       }];
 }
 
+void AutoUpdater::QuitAndInstall() {
+  [[g_updater relaunchToInstallUpdate] subscribeError:^(NSError* error) {
+    Delegate* delegate = AutoUpdater::GetDelegate();
+    if (delegate)
+      delegate->OnError(base::SysNSStringToUTF8(error.localizedDescription));
+  }];
+}
+
 }  // namespace auto_updater

+ 0 - 17
atom/browser/auto_updater_win.cc

@@ -1,17 +0,0 @@
-// Copyright (c) 2013 GitHub, Inc.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#include "atom/browser/auto_updater.h"
-
-namespace auto_updater {
-
-// static
-void AutoUpdater::SetFeedURL(const std::string& url) {
-}
-
-// static
-void AutoUpdater::CheckForUpdates() {
-}
-
-}  // namespace auto_updater

+ 22 - 5
atom/browser/bridge_task_runner.cc

@@ -8,13 +8,28 @@
 
 namespace atom {
 
+void BridgeTaskRunner::MessageLoopIsReady() {
+  auto message_loop = base::MessageLoop::current();
+  CHECK(message_loop);
+  for (const TaskPair& task : tasks_) {
+    message_loop->task_runner()->PostDelayedTask(
+        base::get<0>(task), base::get<1>(task), base::get<2>(task));
+  }
+  for (const TaskPair& task : non_nestable_tasks_) {
+    message_loop->task_runner()->PostNonNestableDelayedTask(
+        base::get<0>(task), base::get<1>(task), base::get<2>(task));
+  }
+}
+
 bool BridgeTaskRunner::PostDelayedTask(
     const tracked_objects::Location& from_here,
     const base::Closure& task,
     base::TimeDelta delay) {
   auto message_loop = base::MessageLoop::current();
-  if (!message_loop)
-    return false;
+  if (!message_loop) {
+    tasks_.push_back(base::MakeTuple(from_here, task, delay));
+    return true;
+  }
 
   return message_loop->task_runner()->PostDelayedTask(from_here, task, delay);
 }
@@ -22,7 +37,7 @@ bool BridgeTaskRunner::PostDelayedTask(
 bool BridgeTaskRunner::RunsTasksOnCurrentThread() const {
   auto message_loop = base::MessageLoop::current();
   if (!message_loop)
-    return false;
+    return true;
 
   return message_loop->task_runner()->RunsTasksOnCurrentThread();
 }
@@ -32,8 +47,10 @@ bool BridgeTaskRunner::PostNonNestableDelayedTask(
     const base::Closure& task,
     base::TimeDelta delay) {
   auto message_loop = base::MessageLoop::current();
-  if (!message_loop)
-    return false;
+  if (!message_loop) {
+    non_nestable_tasks_.push_back(base::MakeTuple(from_here, task, delay));
+    return true;
+  }
 
   return message_loop->task_runner()->PostNonNestableDelayedTask(
       from_here, task, delay);

+ 12 - 1
atom/browser/bridge_task_runner.h

@@ -5,17 +5,23 @@
 #ifndef ATOM_BROWSER_BRIDGE_TASK_RUNNER_H_
 #define ATOM_BROWSER_BRIDGE_TASK_RUNNER_H_
 
+#include <vector>
+
 #include "base/single_thread_task_runner.h"
+#include "base/tuple.h"
 
 namespace atom {
 
 // Post all tasks to the current message loop's task runner if available,
-// otherwise fail silently.
+// otherwise delay the work until message loop is ready.
 class BridgeTaskRunner : public base::SingleThreadTaskRunner {
  public:
   BridgeTaskRunner() {}
   ~BridgeTaskRunner() override {}
 
+  // Called when message loop is ready.
+  void MessageLoopIsReady();
+
   // base::SingleThreadTaskRunner:
   bool PostDelayedTask(const tracked_objects::Location& from_here,
                        const base::Closure& task,
@@ -27,6 +33,11 @@ class BridgeTaskRunner : public base::SingleThreadTaskRunner {
       base::TimeDelta delay) override;
 
  private:
+  using TaskPair = base::Tuple<
+      tracked_objects::Location, base::Closure, base::TimeDelta>;
+  std::vector<TaskPair> tasks_;
+  std::vector<TaskPair> non_nestable_tasks_;
+
   DISALLOW_COPY_AND_ASSIGN(BridgeTaskRunner);
 };
 

+ 45 - 19
atom/browser/browser.cc

@@ -7,16 +7,16 @@
 #include <string>
 
 #include "atom/browser/atom_browser_main_parts.h"
+#include "atom/browser/native_window.h"
 #include "atom/browser/window_list.h"
 #include "base/message_loop/message_loop.h"
-#include "content/public/browser/client_certificate_delegate.h"
-#include "net/ssl/ssl_cert_request_info.h"
 
 namespace atom {
 
 Browser::Browser()
     : is_quiting_(false),
-      is_ready_(false) {
+      is_ready_(false),
+      is_shutdown_(false) {
   WindowList::AddObserver(this);
 }
 
@@ -30,6 +30,9 @@ Browser* Browser::Get() {
 }
 
 void Browser::Quit() {
+  if (is_quiting_)
+    return;
+
   is_quiting_ = HandleBeforeQuit();
   if (!is_quiting_)
     return;
@@ -41,12 +44,43 @@ void Browser::Quit() {
   window_list->CloseAllWindows();
 }
 
+void Browser::Exit(int code) {
+  if (!AtomBrowserMainParts::Get()->SetExitCode(code)) {
+    // Message loop is not ready, quit directly.
+    exit(code);
+  } else {
+    // Prepare to quit when all windows have been closed..
+    is_quiting_ = true;
+
+    // Must destroy windows before quitting, otherwise bad things can happen.
+    atom::WindowList* window_list = atom::WindowList::GetInstance();
+    if (window_list->size() == 0) {
+      NotifyAndShutdown();
+    } else {
+      // Unlike Quit(), we do not ask to close window, but destroy the window
+      // without asking.
+      for (NativeWindow* window : *window_list)
+        window->CloseContents(nullptr);  // e.g. Destroy()
+    }
+  }
+}
+
 void Browser::Shutdown() {
-  FOR_EACH_OBSERVER(BrowserObserver, observers_, OnQuit());
+  if (is_shutdown_)
+    return;
 
+  is_shutdown_ = true;
   is_quiting_ = true;
-  base::MessageLoop::current()->PostTask(
-      FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
+
+  FOR_EACH_OBSERVER(BrowserObserver, observers_, OnQuit());
+
+  if (base::MessageLoop::current()) {
+    base::MessageLoop::current()->PostTask(
+        FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
+  } else {
+    // There is no message loop available so we are in early stage.
+    exit(0);
+  }
 }
 
 std::string Browser::GetVersion() const {
@@ -75,10 +109,6 @@ std::string Browser::GetName() const {
 
 void Browser::SetName(const std::string& name) {
   name_override_ = name;
-
-#if defined(OS_WIN)
-  SetAppUserModelID(name);
-#endif
 }
 
 bool Browser::OpenFile(const std::string& file_path) {
@@ -109,18 +139,14 @@ void Browser::DidFinishLaunching() {
   FOR_EACH_OBSERVER(BrowserObserver, observers_, OnFinishLaunching());
 }
 
-void Browser::ClientCertificateSelector(
-    content::WebContents* web_contents,
-    net::SSLCertRequestInfo* cert_request_info,
-    scoped_ptr<content::ClientCertificateDelegate> delegate) {
-  FOR_EACH_OBSERVER(BrowserObserver,
-                    observers_,
-                    OnSelectCertificate(web_contents,
-                                        cert_request_info,
-                                        delegate.Pass()));
+void Browser::RequestLogin(LoginHandler* login_handler) {
+  FOR_EACH_OBSERVER(BrowserObserver, observers_, OnLogin(login_handler));
 }
 
 void Browser::NotifyAndShutdown() {
+  if (is_shutdown_)
+    return;
+
   bool prevent_default = false;
   FOR_EACH_OBSERVER(BrowserObserver, observers_, OnWillQuit(&prevent_default));
 

+ 19 - 8
atom/browser/browser.h

@@ -11,12 +11,12 @@
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
 #include "base/observer_list.h"
+#include "base/strings/string16.h"
 #include "atom/browser/browser_observer.h"
 #include "atom/browser/window_list_observer.h"
 
 #if defined(OS_WIN)
 #include "base/files/file_path.h"
-#include "base/strings/string16.h"
 #endif
 
 namespace base {
@@ -29,6 +29,8 @@ class MenuModel;
 
 namespace atom {
 
+class LoginHandler;
+
 // This class is used for control application-wide operations.
 class Browser : public WindowListObserver {
  public:
@@ -40,6 +42,9 @@ class Browser : public WindowListObserver {
   // Try to close all windows and quit the application.
   void Quit();
 
+  // Exit the application immediately and set exit code.
+  void Exit(int code);
+
   // Cleanup everything and shutdown the application gracefully.
   void Shutdown();
 
@@ -64,6 +69,9 @@ class Browser : public WindowListObserver {
   // Clear the recent documents list.
   void ClearRecentDocuments();
 
+  // Set the application user model ID.
+  void SetAppUserModelID(const base::string16& name);
+
 #if defined(OS_MACOSX)
   // Bounce the dock icon.
   enum BounceType {
@@ -98,8 +106,10 @@ class Browser : public WindowListObserver {
   // Add a custom task to jump list.
   void SetUserTasks(const std::vector<UserTask>& tasks);
 
-  // Set the application user model ID, called when "SetName" is called.
-  void SetAppUserModelID(const std::string& name);
+  // Returns the application user model ID, if there isn't one, then create
+  // one from app's name.
+  // The returned string managed by Browser, and should not be modified.
+  PCWSTR GetAppUserModelID();
 #endif
 
   // Tell the application to open a file.
@@ -116,11 +126,8 @@ class Browser : public WindowListObserver {
   void WillFinishLaunching();
   void DidFinishLaunching();
 
-  // Called when client certificate is required.
-  void ClientCertificateSelector(
-      content::WebContents* web_contents,
-      net::SSLCertRequestInfo* cert_request_info,
-      scoped_ptr<content::ClientCertificateDelegate> delegate);
+  // Request basic auth login.
+  void RequestLogin(LoginHandler* login_handler);
 
   void AddObserver(BrowserObserver* obs) {
     observers_.AddObserver(obs);
@@ -130,6 +137,7 @@ class Browser : public WindowListObserver {
     observers_.RemoveObserver(obs);
   }
 
+  bool is_shutting_down() const { return is_shutdown_; }
   bool is_quiting() const { return is_quiting_; }
   bool is_ready() const { return is_ready_; }
 
@@ -159,6 +167,9 @@ class Browser : public WindowListObserver {
   // Whether "ready" event has been emitted.
   bool is_ready_;
 
+  // The browse is being shutdown.
+  bool is_shutdown_;
+
   std::string version_override_;
   std::string name_override_;
 

+ 3 - 0
atom/browser/browser_linux.cc

@@ -31,6 +31,9 @@ void Browser::AddRecentDocument(const base::FilePath& path) {
 void Browser::ClearRecentDocuments() {
 }
 
+void Browser::SetAppUserModelID(const base::string16& name) {
+}
+
 std::string Browser::GetExecutableFileVersion() const {
   return brightray::GetApplicationVersion();
 }

+ 3 - 0
atom/browser/browser_mac.mm

@@ -26,6 +26,9 @@ void Browser::AddRecentDocument(const base::FilePath& path) {
 void Browser::ClearRecentDocuments() {
 }
 
+void Browser::SetAppUserModelID(const base::string16& name) {
+}
+
 std::string Browser::GetExecutableFileVersion() const {
   return brightray::GetApplicationVersion();
 }

+ 4 - 16
atom/browser/browser_observer.h

@@ -7,19 +7,10 @@
 
 #include <string>
 
-#include "base/memory/scoped_ptr.h"
-#include "content/public/browser/client_certificate_delegate.h"
-
-namespace content {
-class WebContents;
-}
-
-namespace net {
-class SSLCertRequestInfo;
-}
-
 namespace atom {
 
+class LoginHandler;
+
 class BrowserObserver {
  public:
   // The browser is about to close all windows.
@@ -51,11 +42,8 @@ class BrowserObserver {
   virtual void OnWillFinishLaunching() {}
   virtual void OnFinishLaunching() {}
 
-  // The browser requires client certificate.
-  virtual void OnSelectCertificate(
-      content::WebContents* web_contents,
-      net::SSLCertRequestInfo* cert_request_info,
-      scoped_ptr<content::ClientCertificateDelegate> delegate) {}
+  // The browser requests HTTP login.
+  virtual void OnLogin(LoginHandler* login_handler) {}
 
  protected:
   virtual ~BrowserObserver() {}

+ 18 - 7
atom/browser/browser_win.cc

@@ -15,6 +15,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/path_service.h"
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/win/win_util.h"
@@ -25,6 +26,8 @@ namespace atom {
 
 namespace {
 
+const wchar_t kAppUserModelIDFormat[] = L"electron.app.$1";
+
 BOOL CALLBACK WindowsEnumerationHandler(HWND hwnd, LPARAM param) {
   DWORD target_process_id = *reinterpret_cast<DWORD*>(param);
   DWORD process_id = 0;
@@ -56,7 +59,7 @@ void Browser::AddRecentDocument(const base::FilePath& path) {
   if (SUCCEEDED(hr)) {
     SHARDAPPIDINFO info;
     info.psi = item;
-    info.pszAppID = app_user_model_id_.c_str();
+    info.pszAppID = GetAppUserModelID();
     SHAddToRecentDocs(SHARD_APPIDINFO, &info);
   }
 }
@@ -66,16 +69,21 @@ void Browser::ClearRecentDocuments() {
   if (FAILED(destinations.CoCreateInstance(CLSID_ApplicationDestinations,
                                            NULL, CLSCTX_INPROC_SERVER)))
     return;
-  if (FAILED(destinations->SetAppID(app_user_model_id_.c_str())))
+  if (FAILED(destinations->SetAppID(GetAppUserModelID())))
     return;
   destinations->RemoveAllDestinations();
 }
 
+void Browser::SetAppUserModelID(const base::string16& name) {
+  app_user_model_id_ = name;
+  SetCurrentProcessExplicitAppUserModelID(app_user_model_id_.c_str());
+}
+
 void Browser::SetUserTasks(const std::vector<UserTask>& tasks) {
   CComPtr<ICustomDestinationList> destinations;
   if (FAILED(destinations.CoCreateInstance(CLSID_DestinationList)))
     return;
-  if (FAILED(destinations->SetAppID(app_user_model_id_.c_str())))
+  if (FAILED(destinations->SetAppID(GetAppUserModelID())))
     return;
 
   // Start a transaction that updates the JumpList of this application.
@@ -117,10 +125,13 @@ void Browser::SetUserTasks(const std::vector<UserTask>& tasks) {
   destinations->CommitList();
 }
 
-void Browser::SetAppUserModelID(const std::string& name) {
-  app_user_model_id_ = base::string16(L"electron.app.");
-  app_user_model_id_ += base::UTF8ToUTF16(name);
-  SetCurrentProcessExplicitAppUserModelID(app_user_model_id_.c_str());
+PCWSTR Browser::GetAppUserModelID() {
+  if (app_user_model_id_.empty()) {
+    SetAppUserModelID(base::ReplaceStringPlaceholders(
+        kAppUserModelIDFormat, base::UTF8ToUTF16(GetName()), nullptr));
+  }
+
+  return app_user_model_id_.c_str();
 }
 
 std::string Browser::GetExecutableFileVersion() const {

+ 30 - 1
atom/browser/common_web_contents_delegate.cc

@@ -21,6 +21,14 @@
 #include "content/public/browser/render_view_host.h"
 #include "storage/browser/fileapi/isolated_context.h"
 
+#if defined(TOOLKIT_VIEWS)
+#include "atom/browser/native_window_views.h"
+#endif
+
+#if defined(USE_X11)
+#include "atom/browser/browser.h"
+#endif
+
 using content::BrowserThread;
 
 namespace atom {
@@ -128,7 +136,11 @@ void CommonWebContentsDelegate::InitWithWebContents(
 }
 
 void CommonWebContentsDelegate::SetOwnerWindow(NativeWindow* owner_window) {
-  content::WebContents* web_contents = GetWebContents();
+  SetOwnerWindow(GetWebContents(), owner_window);
+}
+
+void CommonWebContentsDelegate::SetOwnerWindow(
+    content::WebContents* web_contents, NativeWindow* owner_window) {
   owner_window_ = owner_window->GetWeakPtr();
   NativeWindowRelay* relay = new NativeWindowRelay(owner_window_);
   web_contents->SetUserData(relay->key, relay);
@@ -355,6 +367,23 @@ void CommonWebContentsDelegate::OnDevToolsAppendToFile(
       "DevToolsAPI.appendedToURL", &url_value, nullptr, nullptr);
 }
 
+#if defined(TOOLKIT_VIEWS)
+gfx::ImageSkia CommonWebContentsDelegate::GetDevToolsWindowIcon() {
+  if (!owner_window())
+    return gfx::ImageSkia();
+  return static_cast<views::WidgetDelegate*>(static_cast<NativeWindowViews*>(
+      owner_window()))->GetWindowAppIcon();
+}
+#endif
+
+#if defined(USE_X11)
+void CommonWebContentsDelegate::GetDevToolsWindowWMClass(
+    std::string* name, std::string* class_name) {
+  *class_name = Browser::Get()->GetName();
+  *name = base::ToLowerASCII(*class_name);
+}
+#endif
+
 void CommonWebContentsDelegate::SetHtmlApiFullscreen(bool enter_fullscreen) {
   // Window is already in fullscreen mode, save the state.
   if (enter_fullscreen && owner_window_->IsFullscreen()) {

+ 14 - 1
atom/browser/common_web_contents_delegate.h

@@ -12,6 +12,7 @@
 #include "brightray/browser/default_web_contents_delegate.h"
 #include "brightray/browser/inspectable_web_contents_impl.h"
 #include "brightray/browser/inspectable_web_contents_delegate.h"
+#include "brightray/browser/inspectable_web_contents_view_delegate.h"
 
 namespace atom {
 
@@ -21,7 +22,8 @@ class WebDialogHelper;
 
 class CommonWebContentsDelegate
     : public brightray::DefaultWebContentsDelegate,
-      public brightray::InspectableWebContentsDelegate {
+      public brightray::InspectableWebContentsDelegate,
+      public brightray::InspectableWebContentsViewDelegate {
  public:
   CommonWebContentsDelegate();
   virtual ~CommonWebContentsDelegate();
@@ -32,6 +34,8 @@ class CommonWebContentsDelegate
 
   // Set the window as owner window.
   void SetOwnerWindow(NativeWindow* owner_window);
+  void SetOwnerWindow(content::WebContents* web_contents,
+                      NativeWindow* owner_window);
 
   // Destroy the managed InspectableWebContents object.
   void DestroyWebContents();
@@ -86,6 +90,15 @@ class CommonWebContentsDelegate
   void DevToolsRemoveFileSystem(
       const base::FilePath& file_system_path) override;
 
+  // brightray::InspectableWebContentsViewDelegate:
+#if defined(TOOLKIT_VIEWS)
+  gfx::ImageSkia GetDevToolsWindowIcon() override;
+#endif
+#if defined(USE_X11)
+  void GetDevToolsWindowWMClass(
+      std::string* name, std::string* class_name) override;
+#endif
+
  private:
   // Callback for when DevToolsSaveToFile has completed.
   void OnDevToolsSaveToFile(const std::string& url);

+ 6 - 5
atom/browser/default_app/default_app.js

@@ -1,5 +1,6 @@
-var app = require('app');
-var BrowserWindow = require('browser-window');
+const electron = require('electron');
+const app = electron.app;
+const BrowserWindow = electron.BrowserWindow;
 
 var mainWindow = null;
 
@@ -12,9 +13,9 @@ app.on('ready', function() {
   mainWindow = new BrowserWindow({
     width: 800,
     height: 600,
-    'auto-hide-menu-bar': true,
-    'use-content-size': true,
+    autoHideMenuBar: true,
+    useContentSize: true,
   });
-  mainWindow.loadUrl('file://' + __dirname + '/index.html');
+  mainWindow.loadURL('file://' + __dirname + '/index.html');
   mainWindow.focus();
 });

+ 23 - 5
atom/browser/default_app/index.html

@@ -57,13 +57,17 @@
 </head>
 <body>
   <script>
-    var execPath = require('remote').process.execPath;
+    const electron = require('electron');
+    const remote = electron.remote;
+    const shell = electron.shell;
+
+    var execPath = remote.process.execPath;
     var command = execPath + ' path-to-your-app';
 
     document.onclick = function(e) {
       e.preventDefault();
       if (e.target.tagName == 'A')
-        require('shell').openExternal(e.target.href);
+        shell.openExternal(e.target.href);
       return false;
     };
     document.ondragover = document.ondrop = function(e) {
@@ -72,7 +76,11 @@
     };
   </script>
 
-  <h2 style="-webkit-app-region: drag">Welcome to Electron</h2>
+  <h2>
+    <script>
+      document.write(`Welcome to Electron (v${process.versions.electron})`)
+    </script>
+  </h2>
 
   <p>
   To run your app with Electron, execute the following command under your
@@ -83,8 +91,18 @@
 
   <p>
   The <code>path-to-your-app</code> should be the path to your own Electron
-  app, you can read the <a href='https://github.com/atom/electron/blob/master/docs/tutorial/quick-start.md'>quick start</a>
-  guide in Electron's <a href='https://github.com/atom/electron/blob/master/docs'>docs</a>
+  app, you can read the
+  <script>
+    document.write(
+      `<a href='https://github.com/atom/electron/blob/v${process.versions.electron}/docs/tutorial/quick-start.md'>quick start</a>`
+    );
+  </script>
+  guide in Electron's
+  <script>
+    document.write(
+      `<a href='https://github.com/atom/electron/tree/v${process.versions.electron}/docs#readme'>docs</a>`
+    );
+  </script>
   on how to write one.
   </p>
 

+ 35 - 14
atom/browser/default_app/main.js

@@ -1,9 +1,11 @@
-var app = require('app');
-var dialog = require('dialog');
+const electron = require('electron');
+const app      = electron.app;
+const dialog   = electron.dialog;
+const shell    = electron.shell;
+const Menu     = electron.Menu;
+
 var fs = require('fs');
 var path = require('path');
-var Menu = require('menu');
-var BrowserWindow = require('browser-window');
 
 // Quit when all windows are closed and no other one is listening to this.
 app.on('window-all-closed', function() {
@@ -13,16 +15,22 @@ app.on('window-all-closed', function() {
 
 // Parse command line options.
 var argv = process.argv.slice(1);
-var option = { file: null, help: null, version: null, webdriver: null };
-for (var i in argv) {
+var option = { file: null, help: null, version: null, webdriver: null, modules: [] };
+for (var i = 0; i < argv.length; i++) {
   if (argv[i] == '--version' || argv[i] == '-v') {
     option.version = true;
     break;
+  } else if (argv[i].match(/^--app=/)) {
+    option.file = argv[i].split('=')[1];
+    break;
   } else if (argv[i] == '--help' || argv[i] == '-h') {
     option.help = true;
     break;
   } else if (argv[i] == '--test-type=webdriver') {
     option.webdriver = true;
+  } else if (argv[i] == '--require' || argv[i] == '-r') {
+    option.modules.push(argv[++i]);
+    continue;
   } else if (argv[i][0] == '-') {
     continue;
   } else {
@@ -136,19 +144,23 @@ app.once('ready', function() {
       submenu: [
         {
           label: 'Learn More',
-          click: function() { require('shell').openExternal('http://electron.atom.io') }
+          click: function() { shell.openExternal('http://electron.atom.io') }
         },
         {
           label: 'Documentation',
-          click: function() { require('shell').openExternal('https://github.com/atom/electron/tree/master/docs#readme') }
+          click: function() {
+            shell.openExternal(
+              `https://github.com/atom/electron/tree/v${process.versions.electron}/docs#readme`
+            )
+          }
         },
         {
           label: 'Community Discussions',
-          click: function() { require('shell').openExternal('https://discuss.atom.io/c/electron') }
+          click: function() { shell.openExternal('https://discuss.atom.io/c/electron') }
         },
         {
           label: 'Search Issues',
-          click: function() { require('shell').openExternal('https://github.com/atom/electron/issues') }
+          click: function() { shell.openExternal('https://github.com/atom/electron/issues') }
         }
       ]
     },
@@ -181,11 +193,11 @@ app.once('ready', function() {
         {
           label: 'Hide Others',
           accelerator: 'Command+Shift+H',
-          role: 'hideothers:'
+          role: 'hideothers'
         },
         {
           label: 'Show All',
-          role: 'unhide:'
+          role: 'unhide'
         },
         {
           type: 'separator'
@@ -212,6 +224,10 @@ app.once('ready', function() {
   Menu.setApplicationMenu(menu);
 });
 
+if (option.modules.length > 0) {
+  require('module')._preloadModules(option.modules);
+}
+
 // Start the specified app if there is one specified in command line, otherwise
 // start the default app.
 if (option.file && !option.webdriver) {
@@ -237,7 +253,11 @@ if (option.file && !option.webdriver) {
   } catch(e) {
     if (e.code == 'MODULE_NOT_FOUND') {
       app.focus();
-      dialog.showErrorBox('Error opening app', 'The app provided is not a valid electron app, please read the docs on how to write one:\nhttps://github.com/atom/electron/tree/master/docs\n\n' + e.toString());
+      dialog.showErrorBox(
+        'Error opening app',
+        'The app provided is not a valid Electron app, please read the docs on how to write one:\n' +
+        `https://github.com/atom/electron/tree/v${process.versions.electron}/docs\n\n${e.toString()}`
+      );
       process.exit(1);
     } else {
       console.error('App threw an error when running', e);
@@ -253,10 +273,11 @@ if (option.file && !option.webdriver) {
   helpMessage    += "A path to an Electron application may be specified. The path must be to \n";
   helpMessage    += "an index.js file or to a folder containing a package.json or index.js file.\n\n";
   helpMessage    += "Options:\n";
+  helpMessage    += "  -r, --require         Module to preload (option can be repeated)\n";
   helpMessage    += "  -h, --help            Print this usage message.\n";
   helpMessage    += "  -v, --version         Print the version.";
   console.log(helpMessage);
   process.exit(0);
 } else {
-  require('./default_app.js');
+  require('./default_app');
 }

+ 16 - 1
atom/browser/javascript_environment.cc

@@ -4,6 +4,10 @@
 
 #include "atom/browser/javascript_environment.h"
 
+#include <string>
+
+#include "base/command_line.h"
+#include "content/public/common/content_switches.h"
 #include "gin/array_buffer.h"
 #include "gin/v8_initializer.h"
 
@@ -20,7 +24,18 @@ JavascriptEnvironment::JavascriptEnvironment()
 }
 
 bool JavascriptEnvironment::Initialize() {
-  gin::V8Initializer::LoadV8Snapshot();
+  auto cmd = base::CommandLine::ForCurrentProcess();
+  if (cmd->HasSwitch("debug-brk")) {
+    // Need to be called before v8::Initialize().
+    const char expose_debug_as[] = "--expose_debug_as=v8debug";
+    v8::V8::SetFlagsFromString(expose_debug_as, sizeof(expose_debug_as) - 1);
+  }
+
+  // --js-flags.
+  std::string js_flags = cmd->GetSwitchValueASCII(switches::kJavaScriptFlags);
+  if (!js_flags.empty())
+    v8::V8::SetFlagsFromString(js_flags.c_str(), js_flags.size());
+
   gin::IsolateHolder::Initialize(gin::IsolateHolder::kNonStrictMode,
                                  gin::ArrayBufferAllocator::SharedInstance());
   return true;

+ 4 - 4
atom/browser/lib/chrome-extension.coffee

@@ -1,4 +1,4 @@
-app  = require 'app'
+electron = require 'electron'
 fs   = require 'fs'
 path = require 'path'
 url  = require 'url'
@@ -40,6 +40,7 @@ loadedExtensions = null
 loadedExtensionsPath = null
 
 # Persistent loaded extensions.
+{app} = electron
 app.on 'will-quit', ->
   try
     loadedExtensions = Object.keys(extensionInfoMap).map (key) -> extensionInfoMap[key].srcDirectory
@@ -51,11 +52,10 @@ app.on 'will-quit', ->
 
 # We can not use protocol or BrowserWindow until app is ready.
 app.once 'ready', ->
-  protocol = require 'protocol'
-  BrowserWindow = require 'browser-window'
+  {protocol, BrowserWindow} = electron
 
   # Load persistented extensions.
-  loadedExtensionsPath = path.join app.getDataPath(), 'DevTools Extensions'
+  loadedExtensionsPath = path.join app.getPath('userData'), 'DevTools Extensions'
 
   try
     loadedExtensions = JSON.parse fs.readFileSync(loadedExtensionsPath)

+ 14 - 14
atom/browser/lib/guest-view-manager.coffee

@@ -1,5 +1,5 @@
-ipc = require 'ipc'
-webContents = require 'web-contents'
+{ipcMain, webContents} = require 'electron'
+
 webViewManager = null  # Doesn't exist in early initialization.
 
 supportedWebViewEvents = [
@@ -19,7 +19,7 @@ supportedWebViewEvents = [
   'gpu-crashed'
   'plugin-crashed'
   'destroyed'
-  'page-title-set'
+  'page-title-updated'
   'page-favicon-updated'
   'enter-html-full-screen'
   'leave-html-full-screen'
@@ -79,7 +79,7 @@ createGuest = (embedder, params) ->
       opts = {}
       opts.httpReferrer = params.httpreferrer if params.httpreferrer
       opts.userAgent = params.useragent if params.useragent
-      @loadUrl params.src, opts
+      @loadURL params.src, opts
 
     if params.allowtransparency?
       @setAllowTransparency params.allowtransparency
@@ -118,11 +118,11 @@ attachGuest = (embedder, elementInstanceId, guestInstanceId, params) ->
     destroyGuest embedder, oldGuestInstanceId
 
   webPreferences =
-    'guest-instance-id': guestInstanceId
-    'node-integration': params.nodeintegration ? false
-    'plugins': params.plugins
-    'web-security': !params.disablewebsecurity
-  webPreferences['preload-url'] = params.preload if params.preload
+    guestInstanceId: guestInstanceId
+    nodeIntegration: params.nodeintegration ? false
+    plugins: params.plugins
+    webSecurity: !params.disablewebsecurity
+  webPreferences.preloadURL = params.preload if params.preload
   webViewManager.addGuest guestInstanceId, elementInstanceId, embedder, guest, webPreferences
 
   guest.attachParams = params
@@ -140,19 +140,19 @@ destroyGuest = (embedder, id) ->
     delete reverseEmbedderElementsMap[id]
     delete embedderElementsMap[key]
 
-ipc.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_CREATE_GUEST', (event, params, requestId) ->
+ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_CREATE_GUEST', (event, params, requestId) ->
   event.sender.send "ATOM_SHELL_RESPONSE_#{requestId}", createGuest(event.sender, params)
 
-ipc.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_ATTACH_GUEST', (event, elementInstanceId, guestInstanceId, params) ->
+ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_ATTACH_GUEST', (event, elementInstanceId, guestInstanceId, params) ->
   attachGuest event.sender, elementInstanceId, guestInstanceId, params
 
-ipc.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_DESTROY_GUEST', (event, id) ->
+ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_DESTROY_GUEST', (event, id) ->
   destroyGuest event.sender, id
 
-ipc.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_SIZE', (event, id, params) ->
+ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_SIZE', (event, id, params) ->
   guestInstances[id]?.guest.setSize params
 
-ipc.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_ALLOW_TRANSPARENCY', (event, id, allowtransparency) ->
+ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_ALLOW_TRANSPARENCY', (event, id, allowtransparency) ->
   guestInstances[id]?.guest.setAllowTransparency allowtransparency
 
 # Returns WebContents from its guest id.

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