Compare commits

..

471 Commits

Author SHA1 Message Date
Cheng Zhao
4af646b760 Bump v0.6.12. 2013-11-22 15:18:21 +08:00
Cheng Zhao
5720d4f802 Merge pull request #128 from atom/api-capture-page
Add BrowserWindow.capturePage API.
2013-11-21 22:52:22 -08:00
Cheng Zhao
80824b13c6 Add spec for BrowserWindow.capturePage. 2013-11-22 14:42:39 +08:00
Cheng Zhao
33ad68f7a7 doc: Explain BrowserWindow.capturePage. 2013-11-22 14:39:10 +08:00
Cheng Zhao
4af802f215 Enable ignoring |rect| when calling BrowserWindow.capturePage. 2013-11-22 14:29:20 +08:00
Cheng Zhao
3b4a45ac65 Add BrowserWindow.capturePage API. 2013-11-22 14:23:19 +08:00
Cheng Zhao
a0548530e7 Enable converting V8 value to gfx::Rect. 2013-11-22 14:22:28 +08:00
Cheng Zhao
8d6764e0a0 Return screenshot's data buffer instead of directly saving to file. 2013-11-21 22:03:29 +08:00
Cheng Zhao
36ecb35cb1 Add API to capture a page into file. 2013-11-21 21:50:06 +08:00
Cheng Zhao
9ad3b7939f Bump v0.6.11. 2013-11-20 22:51:26 +08:00
Cheng Zhao
8493975840 doc: Mention that Command would not be meaningful on Windows. 2013-11-20 22:48:47 +08:00
Cheng Zhao
ec880d64bf Merge pull request #125 from atom/dont-map-meta
Don't map command to ctrl
2013-11-20 06:35:20 -08:00
Matt Colyer
113d644615 Don't map command to ctrl
We no longer what this behavior because it's confusing unless you
understand that atom-shell does this.
2013-11-19 15:18:54 -08:00
Cheng Zhao
6c0621fe05 Bump v0.6.10. 2013-11-11 20:04:15 +08:00
Cheng Zhao
baa6d9730c win: Enable setting window icon in creation options. 2013-11-11 19:29:40 +08:00
Cheng Zhao
d5ffa4dc77 Fix a possible dead lock when quiting.
It could happen that we are quitting when the embed thread is still
waiting for the main thread, so we make sure embed thread is always
signaled when quitting.
2013-11-11 16:59:57 +08:00
Cheng Zhao
01dd5638d0 Update brightray for fixing the 'ARCHS' issue. 2013-11-08 19:05:30 -08:00
Cheng Zhao
ec5cd2fb1c Do not set sdk_root, it's already set by brightray. 2013-11-08 19:01:56 -08:00
Cheng Zhao
44c0dc9104 Bump v0.6.9. 2013-11-07 16:59:45 +08:00
Cheng Zhao
7324f0468b 💄 fix typo. 2013-11-07 16:59:33 +08:00
Cheng Zhao
64e3a6b437 Add x permission for bump-version.py. 2013-11-07 16:58:09 +08:00
Cheng Zhao
690ab7d4ae Add tag and commit after version is bumped. 2013-11-07 15:41:14 +08:00
Cheng Zhao
48a0ac9a96 Update Info.plist when bumping version. 2013-11-07 15:37:21 +08:00
Cheng Zhao
7212fa1056 Also update atom_version.h and atom.rc in bump-version.py. 2013-11-07 15:19:37 +08:00
Cheng Zhao
8d710609fd win: Fix setting icon after format of atom.rc has changed. 2013-11-07 15:09:45 +08:00
Cheng Zhao
7dcbd11863 Conver atom.rc from unicode file to ASCII file. 2013-11-07 15:06:57 +08:00
Cheng Zhao
e075a8b49e Add tool to help bump version. 2013-11-07 12:52:26 +08:00
Cheng Zhao
d1dc041aaf win: Use exe's icon as window icon, fixed #123. 2013-11-07 11:06:25 +08:00
Cheng Zhao
85243edf4e win: Add an icon. 2013-11-07 09:55:59 +08:00
Cheng Zhao
faeea88b16 Write correct version in .rc file. 2013-11-05 22:26:33 +08:00
Cheng Zhao
139d581e5b Bump v0.6.8. 2013-11-05 13:15:17 +08:00
Cheng Zhao
64b2d0da36 Add a corresponding .h file for atom_main.cc.
It's required if we want to include Chromium headers before C headers
and do not break cpplint rules in the meanwhile.
2013-11-05 13:12:13 +08:00
Cheng Zhao
dc1a8b644a Get the status of devtools from brightray, fixed #27. 2013-11-05 10:32:45 +08:00
Cheng Zhao
8bb3b53833 Make cpplint pass headers test without setting "NOLINT". 2013-11-05 10:00:11 +08:00
Cheng Zhao
8112fe741e Merge pull request #122 from atom/fix-console-stdio
Fix console STDIO
2013-11-04 17:11:53 -08:00
Paul Betts
60f24eb22b This method seems to work, :shipit: 2013-11-04 16:56:50 -08:00
Paul Betts
fd20050fc9 First hack at fixing console IO 2013-11-04 14:15:19 -08:00
Cheng Zhao
c5946bf977 Bump v0.6.7. 2013-11-02 16:49:21 +08:00
Cheng Zhao
618d0c6397 Fix the "'true' is not recognized as an internal or external command" error. 2013-11-01 10:15:41 +08:00
Cheng Zhao
362bb6e68b Mention required options of WDK and Win7 SDK, fixed #114. 2013-10-31 20:06:36 +08:00
Cheng Zhao
b88168c0d2 Bump v0.6.6. 2013-10-28 10:47:57 +08:00
Cheng Zhao
df919a4be8 Expose libuv's symbols. 2013-10-28 10:47:04 +08:00
Cheng Zhao
45f864d2b4 Make atom-shell compile-able with VS 2010 Express. 2013-10-26 19:39:45 +08:00
Cheng Zhao
602f02b867 💄 Fix typo of doc. 2013-10-26 18:09:22 +08:00
Cheng Zhao
5cf61c9ab4 Add more details on building with VS 2010 Express. 2013-10-26 18:07:20 +08:00
Cheng Zhao
49a7954824 Bump v0.6.5. 2013-10-26 17:49:44 +08:00
Cheng Zhao
198b52bf40 win: Ship the pdb file, fixes #112. 2013-10-26 17:44:51 +08:00
Cheng Zhao
cb31140d3a Do "compress" the archive when creating zip.
By default the zipfile module doesn't do the compression unless we
explictly specify the method.
2013-10-26 17:42:12 +08:00
Cheng Zhao
f2c1d8f4b2 Update to latest apm. 2013-10-26 17:24:40 +08:00
Cheng Zhao
3ddba90d94 Fix creating zip distribution on Windows. 2013-10-26 17:23:16 +08:00
Cheng Zhao
887a2a3c00 Bump v0.6.4. 2013-10-22 10:25:53 +08:00
Cheng Zhao
585777e62d Allow single-character accelerators. 2013-10-22 09:38:19 +08:00
Cheng Zhao
640f45a5b3 Fix a possible crash when calling AdapterRequestJob::Kill(). 2013-10-22 09:33:38 +08:00
Cheng Zhao
8bcb545119 Bump v0.6.3. 2013-10-21 18:22:16 +08:00
Cheng Zhao
aa911cdb2e Merge pull request #110 from atom/better-accelerator
Support more accelerators in menu, closes #107.
2013-10-21 01:53:34 -07:00
Cheng Zhao
27be89d39a Should not touch process.argv when starting the default_app. 2013-10-21 16:48:52 +08:00
Cheng Zhao
5ce66fca5c Do not use sscanf. 2013-10-21 15:59:45 +08:00
Cheng Zhao
94e4044160 Allow "Space" in accelerator. 2013-10-21 15:35:54 +08:00
Cheng Zhao
30eabfb9f6 Allow "F1" - "F24" in accelerator. 2013-10-21 15:33:19 +08:00
Cheng Zhao
a6eb261af0 Get rid of the NormalizeShortcutSuggestion function. 2013-10-21 14:34:25 +08:00
Cheng Zhao
6e4f74ae35 Allow more special keys in the accelerator. 2013-10-21 14:25:36 +08:00
Cheng Zhao
f091352c56 Slightly optimize accelerator token comparing. 2013-10-21 14:05:43 +08:00
Cheng Zhao
534ff5efdb Be case-insensible of accelerators. 2013-10-21 13:46:37 +08:00
Cheng Zhao
51d92454ce Map all single symbols in the keyboard to accelerators. 2013-10-21 13:39:55 +08:00
Cheng Zhao
af413b2d97 Bump v0.6.2. 2013-10-17 10:22:35 +08:00
Cheng Zhao
7c30d037a3 mac: Enable to set the 'Help' menu, fixes #109. 2013-10-17 10:21:24 +08:00
Cheng Zhao
acda56210b Specify the version instead of using "*" for dependencies in package.json. 2013-10-17 09:58:55 +08:00
Cheng Zhao
61b2377cf2 Use stream-uploading to upload the asset to GitHub. 2013-10-15 09:04:05 +08:00
Cheng Zhao
7fa6407f65 Bump v0.6.1. 2013-10-14 11:59:19 +08:00
Cheng Zhao
41de0b420a Unescape the script path before passing it to cefode.js, fixed #108. 2013-10-14 11:58:20 +08:00
Cheng Zhao
0ad1fc842d Do not multipart-encode the uploaded file. 2013-10-10 18:05:01 +08:00
Cheng Zhao
7f0e7f3835 Check the build version of Release build in upload script. 2013-10-10 14:39:54 +08:00
Cheng Zhao
6ff644fe60 Bump v0.6.0. 2013-10-10 14:35:37 +08:00
Cheng Zhao
92c68797d0 Enable more common ASCII tokens to be used in accelerator, fixes #107. 2013-10-10 14:33:59 +08:00
Cheng Zhao
9a2fc8f4ea Add 'Quit' to spec window's menu. 2013-10-10 14:33:42 +08:00
Cheng Zhao
6a712d4db4 Merge pull request #105 from atom/windows-menu
Implement menu API on Windows, fixes #75.
2013-10-06 17:58:42 -07:00
Cheng Zhao
a182de20a4 win: Newly created window should also be aware of application menu. 2013-10-07 08:56:52 +08:00
Cheng Zhao
caaab22841 Do not dereference remote callback if its renderer view is released. 2013-10-05 20:53:31 +08:00
Cheng Zhao
1e1fec15b6 Make objects registry code more structured. 2013-10-05 20:47:49 +08:00
Cheng Zhao
1524ced816 Set application menu would set menu for all windows on Windows. 2013-10-05 14:31:30 +08:00
Cheng Zhao
93f1a3dbd5 Make app.setApplicationMenu an alias to Menu.setApplicationMenu. 2013-10-05 13:46:48 +08:00
Cheng Zhao
666f6b3a01 Add app.getBrowserWindows() API. 2013-10-05 13:13:04 +08:00
Cheng Zhao
61cc0bba25 💄 fix typo in doc. 2013-10-05 13:03:08 +08:00
Cheng Zhao
587484a5d0 Make sure application menu always get referenced. 2013-10-05 12:56:30 +08:00
Cheng Zhao
defb6c9882 Don't wait for request in http spec.
This spec is used to crash atom-shell, we don't care if http request
succeeds.
2013-10-04 23:39:34 +08:00
Cheng Zhao
55a35d473d win: Make web view focused initialy. 2013-10-04 23:38:37 +08:00
Cheng Zhao
d86172cc87 win: Make window menu's accelerators work. 2013-10-04 23:36:31 +08:00
Cheng Zhao
32432cc770 Convert "Command" to "Ctrl" in accelerators on non-Mac. 2013-10-04 23:01:29 +08:00
Cheng Zhao
ae98d9c8b6 💄 NativeWindowClientView no longer needs a extra content view. 2013-10-04 09:04:32 +08:00
Cheng Zhao
4e2d3f3d12 win: Make native window a views::View. 2013-10-04 08:52:45 +08:00
Cheng Zhao
a2f679e4bd win: Respond to events of window menu. 2013-10-03 23:34:42 +08:00
Cheng Zhao
06ae5c06b8 Make sure binary version is dumped before uploading. 2013-10-03 11:48:13 +08:00
Cheng Zhao
0483871388 Add --version flag to print atom-shell's version. 2013-10-03 11:36:17 +08:00
Cheng Zhao
7f7e13edc3 Dump v0.5.4. 2013-10-03 10:06:34 +08:00
Cheng Zhao
bbb9c37f70 BrowserWindow.focus() should not make window become visible, fixed #106. 2013-10-03 09:42:20 +08:00
Cheng Zhao
88ce2a5390 Add BrowserWindow.isVisible() API. 2013-10-03 08:28:22 +08:00
Cheng Zhao
6748573dee Show a simple menu in default_app on Windows. 2013-10-02 21:51:19 +08:00
Cheng Zhao
5c8566e0d4 win: Show popup menu at right place. 2013-10-02 21:43:52 +08:00
Cheng Zhao
5a6ff0f80d win: Add BrowserWindow.setMenu API. 2013-10-02 21:24:21 +08:00
Cheng Zhao
2024ae5dba 💄 Fix build on Windows. 2013-10-02 20:43:30 +08:00
Cheng Zhao
aa6d5a3ff6 Update to latest apm. 2013-10-02 20:26:25 +08:00
Cheng Zhao
419da689c9 Dump v0.5.3. 2013-09-29 21:50:19 +08:00
Cheng Zhao
709563c090 Set proxy service at the right place. 2013-09-29 21:43:17 +08:00
Cheng Zhao
b50a2c9dc1 Add spec for sending http request. 2013-09-29 20:57:57 +08:00
Cheng Zhao
43a5453de5 💄 Fix target version of atom-shell's uploading. 2013-09-29 20:18:55 +08:00
Cheng Zhao
87d94ff1ae Do not require uploading a tag before publish release. 2013-09-29 15:31:20 +08:00
Cheng Zhao
8b4414345b Dump v0.5.2. 2013-09-29 15:28:58 +08:00
Cheng Zhao
6e43074586 Still upload distribution file to S3 since Atom hasn't used Releases API yet. 2013-09-29 15:15:16 +08:00
Cheng Zhao
99d901bc9a Switch to use the ProxyResolverV8, fixes atom/atom#894. 2013-09-29 15:06:29 +08:00
Cheng Zhao
95e79124eb Do not publish release when body of release note is empty. 2013-09-27 15:55:46 +08:00
Cheng Zhao
54b0d0c9c0 Free the native window's memory after the event were sent for all observers.
If we delete the window immediately other observers may get a invalid
window and cause random crashes.
2013-09-27 15:24:03 +08:00
Cheng Zhao
10a46b4229 Merge pull request #104 from atom/pylint
Lint python and coffee scripts
2013-09-26 20:06:10 -07:00
Cheng Zhao
af3f07f4cc 💄 Adding trailing comma in package.json. 2013-09-27 11:03:38 +08:00
Cheng Zhao
f4f42b30ee 💄 Fix warnings of coffeelint. 2013-09-27 11:03:23 +08:00
Cheng Zhao
f3eef6cc2e Do not warn about 80 columns in coffeelint, it's not required. 2013-09-27 11:01:40 +08:00
Cheng Zhao
11f387743f Add wrapper script for coffeelint. 2013-09-27 10:49:55 +08:00
Cheng Zhao
561857d640 Add coffeelint to dependencies. 2013-09-27 10:44:16 +08:00
Cheng Zhao
3b3585f575 Skip the check for lib.github. 2013-09-27 10:43:10 +08:00
Cheng Zhao
6be716c6e8 Run pylint in cibuild. 2013-09-27 10:22:13 +08:00
Cheng Zhao
2b923b988c Add wrapper script for pylint. 2013-09-27 10:21:49 +08:00
Cheng Zhao
ef8a05be38 💄 Fix violations against pylint. 2013-09-27 10:21:27 +08:00
Cheng Zhao
c029ff2055 Merge pull request #103 from atom/releases-api
Upload atom-shell's binaries with Releases API
2013-09-26 18:46:35 -07:00
Cheng Zhao
e376850552 💄 Fix cpplint warnings. 2013-09-27 09:26:36 +08:00
Cheng Zhao
1ce61c7845 Get the body of release with current $EDITOR. 2013-09-27 09:10:22 +08:00
Cheng Zhao
68a3fd6f05 Publish the release after the uploading is end. 2013-09-26 22:02:09 +08:00
Cheng Zhao
ce012043fa Upload both atom-shell and node's headers. 2013-09-26 21:20:38 +08:00
Cheng Zhao
ae70d5cb64 Silence the output of upload script. 2013-09-26 20:58:51 +08:00
Cheng Zhao
db359ae75c Upload the asset after release note is created. 2013-09-26 20:32:11 +08:00
Cheng Zhao
3586565bba Handle the assets uploading in the GitHub API library. 2013-09-26 20:31:17 +08:00
Cheng Zhao
fe67ecd7fd 💄 Find the release even when commit isn't tagged. 2013-09-26 19:54:28 +08:00
Cheng Zhao
33c509b867 Create new release note or get the existing one when uploading. 2013-09-26 19:49:09 +08:00
Cheng Zhao
3edfb7d5c3 Correctly deal with API errors. 2013-09-26 19:48:48 +08:00
Cheng Zhao
eed2b0fd81 Accept still-preview apis. 2013-09-26 16:38:08 +08:00
Cheng Zhao
4c4b8a8486 Add a simple wrapper of GitHub API. 2013-09-26 16:32:39 +08:00
Cheng Zhao
2b9a533d1d Add process.getCurrentStackTrace(), returning V8::getCurrentStackTrace(). 2013-09-26 13:31:17 +08:00
Cheng Zhao
b3a2302283 Dumpv 0.5.1. 2013-09-26 11:30:16 +08:00
Cheng Zhao
6bee435170 Destroy the event object immediately after the reply is sent, fixes #102. 2013-09-26 11:28:53 +08:00
Cheng Zhao
5480cf58c2 Add spec for destroying synchronous event. 2013-09-26 11:27:09 +08:00
Cheng Zhao
65176761f4 Dump v0.5.0. 2013-09-25 16:51:55 +08:00
Cheng Zhao
cf75e4ac49 Move atom_event_processing_window to browser/ui. 2013-09-25 16:51:55 +08:00
Cheng Zhao
ac68589291 cocoa: Don't use Chrome's UnderlayOpenGLHostingWindow.
The UnderlayOpenGLHostingWindow would add a semi-transparent layer under
the window, I'm not very sure what does this class exactly do, but
removing it seems no harm to the renderer.

Fixes atom/atom#877.
2013-09-25 16:51:33 +08:00
Cheng Zhao
bc95cac3ed 💄 Same indent. 2013-09-25 16:18:33 +08:00
Cheng Zhao
eed8f6cabc Remove the CustomFrameView, it's no longer needed. 2013-09-25 15:40:22 +08:00
Cheng Zhao
6aefb0f76f Merge pull request #101 from atom/no-blocking-browser
Make the browser non-blocking
2013-09-24 16:48:29 -07:00
Cheng Zhao
14de58a6b7 Calling asynchronous functions in renderer now doesn't block browser. 2013-09-24 18:01:12 +08:00
Cheng Zhao
085f0a2544 win: Implement asynchronous ShowMessageBox. 2013-09-24 16:11:23 +08:00
Cheng Zhao
4000734504 win: Add dummy implementation for asynchronous open/save dialog. 2013-09-24 14:47:39 +08:00
Cheng Zhao
1b5e22f9c4 Default template parameter is not supported on Windows. 2013-09-24 14:41:44 +08:00
Cheng Zhao
53c6d51d56 win: Accept parent window in ShowOpenDialog. 2013-09-24 10:13:43 +08:00
Cheng Zhao
ac76017702 Merge branch 'master' into no-blocking-browser 2013-09-24 10:09:35 +08:00
Cheng Zhao
9927b4bf4c Merge pull request #93 from atom/file-dialog
Implement file dialogs API on Windows
2013-09-23 19:08:00 -07:00
Cheng Zhao
11ca836afc Use the convient V8 wrapper in all files. 2013-09-24 09:41:54 +08:00
Cheng Zhao
a824c88352 Use same name convention in string16_conversions. 2013-09-23 23:00:58 +08:00
Cheng Zhao
8fdd3b3044 Use FromV8Arguments in atom_api_dialog.cc. 2013-09-23 22:52:48 +08:00
Cheng Zhao
1e5e0194bd Add convient function for converting args from V8 Arguments. 2013-09-23 22:48:55 +08:00
Cheng Zhao
a0d1a7620c Put FromV8Value and ToV8Value to a new header. 2013-09-23 21:30:54 +08:00
Cheng Zhao
770a0068a3 Simplify conversions between native types and v8 types. 2013-09-23 21:12:40 +08:00
Cheng Zhao
26f0e49c9a Make dialog.showSaveDialog accept a callback. 2013-09-23 20:08:49 +08:00
Cheng Zhao
c7637c78d1 mac: Add asynchronous ShowSaveDialog. 2013-09-23 20:08:32 +08:00
Cheng Zhao
30ca085fd8 Make dialog.showSaveDialog accept no parent window. 2013-09-23 19:59:00 +08:00
Cheng Zhao
e824b6c910 💄 Pick duplicate code together. 2013-09-23 19:42:07 +08:00
Cheng Zhao
43b492c641 mac: make ShowSaveDialog accept no parent window. 2013-09-23 19:36:52 +08:00
Cheng Zhao
c95cfc9540 Make dialog.showOpenDialog accept callback. 2013-09-23 19:23:49 +08:00
Cheng Zhao
d3dd2b4332 mac: Add asynchronous ShowOpenDialog. 2013-09-23 19:22:36 +08:00
Cheng Zhao
7e86ee37f3 💄 cpplint. 2013-09-23 17:27:32 +08:00
Cheng Zhao
f444e9dc74 💄 CoffeeScript is cute. 2013-09-23 16:51:00 +08:00
Cheng Zhao
76ac8f2719 Enable taking window as parameter in dialog.showOpenDialog. 2013-09-23 16:36:33 +08:00
Cheng Zhao
a4262bc39d mac: Make ShowOpenDialog able to be shown as sheet. 2013-09-23 16:27:22 +08:00
Cheng Zhao
85d6588661 Make dialog.showMessageBox asynchronous. 2013-09-23 14:29:55 +08:00
Cheng Zhao
b70722feb6 mac: Implement async ShowMessageBox. 2013-09-22 18:47:00 +08:00
Cheng Zhao
bfe59480e3 Add header for asynchronous version of ShowMessageBox. 2013-09-22 17:11:09 +08:00
Cheng Zhao
68bdad9a23 Add spec for ipc.sendSync. 2013-09-22 12:06:41 +08:00
Cheng Zhao
761b9d22c8 Do not reply sync messages when window is closed. 2013-09-22 10:44:18 +08:00
Cheng Zhao
1e4762ce92 Do not store the event.returnValue. 2013-09-22 10:03:47 +08:00
Cheng Zhao
d443b36446 Send reply for sync messages when event.returnValue is set. 2013-09-22 09:52:58 +08:00
Cheng Zhao
ef4b36d621 Use string16 instead of std::string when sending IPC messages.
The underlying V8::String is represented in UTF18, by using string16 in
IPC messages we can avoid the overhead of encode conversion.
2013-09-20 22:55:42 +08:00
Cheng Zhao
ef5a4b5fe0 Pass synchronous messages by JSON string.
We are going to use IPC_MESSAGE_HANDLER_DELAY_REPLY to handle
synchronous messages but DictionaryValue is not copyable, so we pass the
JSON string instead.
2013-09-20 22:32:59 +08:00
Cheng Zhao
07b5039c64 Make sure all sync messages get a return value. 2013-09-20 21:39:07 +08:00
Cheng Zhao
a9c824eba1 Use event.returnValue instead of event.result in atom-shell's code.
event.result is still kept for backward compatible.
2013-09-20 21:37:47 +08:00
Cheng Zhao
b225a59a15 Prefer event.returnValue to event.result for sync messages. 2013-09-20 21:37:00 +08:00
Cheng Zhao
50b5272354 Dump v0.4.9. 2013-09-20 18:50:18 +08:00
Cheng Zhao
8879334468 Merge pull request #100 from atom/fix-protocol-crash
Fix crash when using protocol module on startup.
2013-09-20 03:49:33 -07:00
Cheng Zhao
2be1145a9e Guard against using protocol module too early. 2013-09-20 18:45:53 +08:00
Cheng Zhao
e65220adb0 doc: Mention when protocol module is safe to use. 2013-09-20 18:36:16 +08:00
Cheng Zhao
92e157de30 Fix crash when using protocol module on startup.
The job factory was not created before any request was sent, so when the
app used the protocol module on startup it would cause a crash.
2013-09-20 18:32:05 +08:00
Cheng Zhao
c908cae72c Dump v0.4.8. 2013-09-20 16:55:43 +08:00
Cheng Zhao
3f357f184d Use custom url request getter to setup request job factory. 2013-09-20 16:47:47 +08:00
Cheng Zhao
3fdec5c6e3 win: Implement ShowOpenDialog. 2013-09-19 22:28:18 +08:00
Cheng Zhao
6fced224c7 win: Open dialog and save dialog can have different options. 2013-09-18 22:27:40 +08:00
Cheng Zhao
9ed64548d4 💄 Fix comparing extension. 2013-09-18 22:24:46 +08:00
Cheng Zhao
575fe06f29 win: Append extension to save dialog's result according to the selected filter. 2013-09-18 22:21:28 +08:00
Cheng Zhao
6cb2ece285 win: Make common part of ShowSaveDialog a independet class. 2013-09-18 21:42:14 +08:00
Cheng Zhao
29e071a1ad win: Rewrite ShowSaveDialog with IFileSaveDialog. 2013-09-18 20:28:56 +08:00
Cheng Zhao
9e9579a858 Remove dialog hack for Windows XP. 2013-09-18 16:19:32 +08:00
Paul Betts
9849844e89 Merge pull request #92 from atom/paulcbetts-patch-1
Need PathService on Win32
2013-09-17 13:35:12 -07:00
Paul Betts
c578a2cbc2 Need PathService on Win32
This was accidentally left out of 8708d061
2013-09-17 11:53:31 -07:00
Cheng Zhao
583d34b9f7 Dump v0.4.7. 2013-09-13 10:12:02 +08:00
Steve Smith
a2c3690592 Merge pull request #91 from atom/rounded-corner
Round whole frame in Frameless mode
2013-09-12 08:43:30 -07:00
Steve Smith
7a21ae831f Skip the whole AtomFramelessWindow.
Just round the web view and set the bounds to the window, not the main
view.
2013-09-12 11:20:38 -04:00
Cheng Zhao
8708d0611a mac: Force using "Atom" as application name in framework bundle path. 2013-09-12 17:51:45 +08:00
Cheng Zhao
ef92cd8b45 mac: Makes sure MainMenu.nib is alwasys loaded from Atom.framework. 2013-09-12 17:16:33 +08:00
Cheng Zhao
8cb624d828 Dump v0.4.6. 2013-09-12 16:10:11 +08:00
Cheng Zhao
cec640f572 mac: Always use "Atom" as name when find helper process. Fixes #89. 2013-09-12 15:42:36 +08:00
Cheng Zhao
7a1365673e Remove code which are used for borderless window. 2013-09-12 15:13:56 +08:00
Cheng Zhao
6c098deb57 💄 Fix cpplint warnings. 2013-09-12 13:03:57 +08:00
Steve Smith
cf4a566290 Just round the top also and use a standard window.
Using borderless window causes too many issues. Smaller, less
window-like shadows being the most annoying.
2013-09-11 22:02:42 -04:00
Steve Smith
aabba3c641 Fix view autoresizing. 2013-09-11 17:25:42 -04:00
Steve Smith
8f31bf8615 Get radius working with frameless window.
Right now it's set as a constant `AtomWindowCornerRadius`, but it could
be set to a variable.
2013-09-11 16:23:17 -04:00
Cheng Zhao
5d49fc4bee WIP: rounded corner of frameless window. 2013-09-11 18:10:28 +08:00
Cheng Zhao
f38eb1b66f mac: Import chromium's CustomFrameView code. 2013-09-11 13:46:36 +08:00
Cheng Zhao
a567ba08ea Hide the fullscreen button when leaving fullscreen mode. Fixes #88. 2013-09-11 13:05:08 +08:00
Cheng Zhao
91d54a74e1 doc: Metion the frameless-window-demo. 2013-09-09 20:17:47 +08:00
Cheng Zhao
da9cce3f2d Dump v0.4.5. 2013-09-09 16:13:01 +08:00
Cheng Zhao
92241b91ce doc: Separate pages into sub directories. 2013-09-09 15:49:13 +08:00
Cheng Zhao
6b81070f67 doc: Add titles for all pages. 2013-09-09 15:35:57 +08:00
Cheng Zhao
3715dd2a20 💄 Remove a mistyped character. 2013-09-09 15:27:19 +08:00
Cheng Zhao
eb6fa98ed0 Merge pull request #85 from atom/frameless-window
Add frameless window support, fixes #72.
2013-09-09 00:24:20 -07:00
Cheng Zhao
8ddb85774a doc: Add titles for browser-window and frameless-window. 2013-09-09 15:19:09 +08:00
Cheng Zhao
8caf5fac06 doc: Document frameless window. 2013-09-09 14:52:46 +08:00
Cheng Zhao
cc62978ac3 win: Add NativeWindowFramelessView as non-client view. 2013-09-09 14:30:07 +08:00
Cheng Zhao
f833423a2f win: Save draggable region. 2013-09-09 12:12:17 +08:00
Cheng Zhao
3c0671c179 Quit when all windows are closed if running an app by passing it in command line. 2013-09-09 10:54:08 +08:00
Cheng Zhao
a00bf3e1e1 Print stack when got error on startup. 2013-09-09 10:49:28 +08:00
Cheng Zhao
ce487fe1da Make sure child scripts are quit after specs are done. 2013-09-09 09:53:08 +08:00
Cheng Zhao
a73aea3bda Update apm: set both HOME and USERPROFILE in environment under Windows. 2013-09-09 09:24:54 +08:00
Cheng Zhao
b9d994dca2 Make sure the cursor doesn't drift away when dragging window. 2013-09-06 12:12:17 +08:00
Cheng Zhao
b7c2295a1c Don't use setMouseDownCanMoveWindow to implement draggable area.
It would not work when we have the in-window devtools.
2013-09-06 11:54:52 +08:00
Cheng Zhao
da2ded5453 Implement frameless window on OS X.
Most of the code came from Chromium's packaged app window.
2013-09-05 23:52:29 +08:00
Cheng Zhao
a5eb9ea08f Add has_frame_ attribute for NativeWindow. 2013-09-05 21:43:47 +08:00
Cheng Zhao
4223867dbc Send and receive the AtomViewHostMsg_UpdateDraggableRegions message. 2013-09-05 20:06:54 +08:00
Cheng Zhao
40273cf37d Add IPC messages and structs for passing draggable regions. 2013-09-05 19:46:12 +08:00
Cheng Zhao
bc9c95d77d 💄 fix the protocol module spec on OS X. 2013-09-05 18:28:48 +08:00
Cheng Zhao
6a322f8bd6 Update apm for node v0.10.18. 2013-09-05 15:41:44 +08:00
Cheng Zhao
e7bc368785 Dump version to v0.4.4. 2013-09-05 15:17:05 +08:00
Cheng Zhao
aad0c8e996 Update node to v0.10.18. 2013-09-05 15:15:13 +08:00
Cheng Zhao
bf4756fdfb Pass PATH environment in the child_process.fork spec.
The uv_spawn under Windows requires the PATH environment variable to be
there, otherwise it would throw a 203 system error, it should be a bug
of node.
2013-09-05 14:55:22 +08:00
Cheng Zhao
8acd6d6c8a 💄 fix spec failure caused by win32 path delimiter. 2013-09-05 12:24:08 +08:00
Cheng Zhao
256215b749 Use base::Environment to replace getenv. 2013-09-05 12:18:19 +08:00
Cheng Zhao
a3e5b21118 Merge pull request #84 from atom/nested-fork
Use environment variable to detect whether to run as node.

Fixed # 83.
2013-09-04 18:55:20 -07:00
Cheng Zhao
84a3eb5411 Also fix nested child_process.fork on Windows. 2013-09-05 09:49:22 +08:00
Cheng Zhao
e17da272f4 Make child_process.fork work when options.env is set. 2013-09-05 09:47:32 +08:00
Cheng Zhao
88bdff5832 Use environment variable to detect whether to run as node. 2013-09-05 09:22:24 +08:00
Cheng Zhao
ae18a90f7e Add test case for #83. 2013-09-05 09:21:39 +08:00
Cheng Zhao
3b7dd85d3f Merge pull request #65 from atom/custom-protocol
Support custom protocols
2013-09-04 03:33:09 -07:00
Cheng Zhao
128d9c78db Add documentation for protocol module. 2013-09-03 18:22:40 +08:00
Cheng Zhao
c7fed48c4a Emit erros when getting errors in IO thread. 2013-09-03 17:21:10 +08:00
Cheng Zhao
7737708fdd Add protocol interceptor API. 2013-09-03 16:50:10 +08:00
Cheng Zhao
9ba08d5e67 💄 fix reversed flag for updating latest version. 2013-09-02 18:17:06 +08:00
Cheng Zhao
6c3dc9e526 Dump v0.4.3. 2013-09-02 17:59:17 +08:00
Cheng Zhao
75a24a2e67 Update specs to match the documents. 2013-09-02 16:54:54 +08:00
Cheng Zhao
893309aa8a 💄 fix the window specs. 2013-09-02 16:47:53 +08:00
Cheng Zhao
f17864372e Use setTimeout instead of setImmediate in fixtures.
The setImmediate is implemented in node and may have unexpected affects.
2013-09-02 16:46:08 +08:00
Cheng Zhao
610ac5b045 Revert "💄 for the beforeunload handler."
Restore the previous behavior of beforeunload handler.

This reverts commit b1f30c1eb6.
2013-09-02 16:39:00 +08:00
Cheng Zhao
cfb957a603 Add specs for the beforeunload handler. 2013-09-02 16:28:36 +08:00
Cheng Zhao
e423f601c0 Dump v0.4.2. 2013-09-02 13:34:58 +08:00
Cheng Zhao
504f96ae08 Merge pull request #81 from atom/window-native-modules
Fix node native modules support on Windows
2013-09-01 22:31:31 -07:00
Cheng Zhao
3b149945bf Upload a fake empty x64 node.lib.
We only allow building ia32 build of atom-shell, so the x64 node.lib is
not in use, but it's required by node-gyp.
2013-09-02 13:27:26 +08:00
Cheng Zhao
5fe9f281ac Only generate node.lib when we need to upload node headers. 2013-08-31 17:00:13 +08:00
Cheng Zhao
766347ffae Generate node.lib from atom.lib and chromiumcontent.dll.lib. 2013-08-31 16:37:01 +08:00
Cheng Zhao
a5bc2fdb44 Don't use the cmd paramter passed by WinMain.
It doesn't include the argv[0], 💩.
2013-08-31 15:42:41 +08:00
Cheng Zhao
e7d4b44d05 💄 for node version and upload node.lib. 2013-08-31 15:20:59 +08:00
Cheng Zhao
30c9cd4318 Use dummy stdin stream on Windows. 2013-08-31 12:13:08 +08:00
Cheng Zhao
5787b4cd6f Also ship ffmpegsumo.dll in the distribution. 2013-08-31 11:13:11 +08:00
Cheng Zhao
a26308d902 Use node's version for the node headers tarball. 2013-08-31 10:51:53 +08:00
Cheng Zhao
c2093946c8 Add flag for upload script to skip updating version.
When uploading multiple distributions in future, we need to make sure
all distributions have to ben uploaded before triggering the
update-atom-shell script of Atom.
2013-08-31 10:48:47 +08:00
Cheng Zhao
0286379706 Don't require multipart for uploading to S3.
The file we are uploading is not large, and multipart works really bad
on bad networking.
2013-08-31 10:36:13 +08:00
Cheng Zhao
6765ec30f1 The distribution name should contain version and platform. 2013-08-31 10:35:01 +08:00
Cheng Zhao
beba27ed1e Create zip distribution for binaries on Windows. 2013-08-31 09:37:23 +08:00
Cheng Zhao
24f510ca03 Ship correct binaries on Windows. 2013-08-31 09:06:27 +08:00
Cheng Zhao
2cbe823773 Use Chromium V8's headers in the node headers tarball. 2013-08-31 08:22:16 +08:00
Cheng Zhao
790c53825b Merge pull request #79 from atom/message-box-button-order
Fix button order on Win32
2013-08-30 16:41:25 -07:00
Paul Betts
10bd2384d0 Fix button order on Win32
On Windows, the button order is left to right, with the primary action on the
left (i.e. "Ok" "Cancel"). On Mac, the button order is that the primary action
is nearest to the corner.
2013-08-30 12:08:04 -07:00
Cheng Zhao
335db788a5 Add InterceptProtocol AtomURLRequestJobFactory. 2013-08-30 21:15:46 +08:00
Cheng Zhao
d8cd3d78ff Make ReadRawData a public member, so there is no need to detect types. 2013-08-30 20:49:27 +08:00
Cheng Zhao
2a462cc2b7 💄 fix cpplint warning. 2013-08-30 20:24:42 +08:00
Cheng Zhao
df30f130d3 Separate logics between low level URLRequestJob and js calls. 2013-08-30 20:02:17 +08:00
Cheng Zhao
24e613c827 Separate URLRequestStringJob from atom_api_protocol. 2013-08-30 16:16:41 +08:00
Cheng Zhao
7df256f8dc Move atom_url_request_job_factory to browser/net. 2013-08-30 16:10:36 +08:00
Cheng Zhao
2000f88c84 Allow passing a fallback protocol handler for request jobs. 2013-08-30 15:48:57 +08:00
Cheng Zhao
db890feb51 Don't pollute console with error stack. 2013-08-30 13:04:21 +08:00
Cheng Zhao
738cbd4080 💄 Fix protocol specs. 2013-08-30 13:04:02 +08:00
Cheng Zhao
11221979e5 Disable overriding built-in protocols with protocol.registerProtocol. 2013-08-30 12:51:15 +08:00
Cheng Zhao
72c604f741 Custom the implementation of url request job factory.
It's needed for interceptor API.
2013-08-30 12:04:51 +08:00
Cheng Zhao
04910b8391 Skelecton for interceptor APIs. 2013-08-30 10:15:15 +08:00
Paul Betts
132eb09d96 Merge pull request #71 from atom/78-chars-or-death
Indent all the files to 78-characters so that doc diffs are usable
2013-08-29 10:41:39 -07:00
Paul Betts
38b37f2520 Indent all the files to 78-characters so that doc diffs are usable 2013-08-29 16:37:51 +02:00
Cheng Zhao
4bdd1b88ad Wrap passed 'url' and 'referrer' in an 'request' object. 2013-08-29 21:12:48 +08:00
Cheng Zhao
abd3e86fb1 💄 2013-08-29 21:06:22 +08:00
Cheng Zhao
c2fd43c3e8 Add spec for protocol.isHandledProtocol. 2013-08-29 20:57:09 +08:00
Cheng Zhao
261f50701a Add 'registered' and 'unregistered' events for protocol module.
This is only used for writing specs.
2013-08-29 20:56:25 +08:00
Cheng Zhao
bc4201f911 Make protocol module an EventEmitter. 2013-08-29 20:38:04 +08:00
Cheng Zhao
6915f020d9 Add protocol.isHandledProtocol API. 2013-08-29 20:22:52 +08:00
Cheng Zhao
f7de0e8d38 Disable spec for window.close() since it would not be fixed for a while. 2013-08-29 18:03:56 +08:00
Cheng Zhao
efd2bbbede 💄 Avoid duplicate temporary file names. 2013-08-29 18:03:14 +08:00
Cheng Zhao
b1f30c1eb6 💄 for the beforeunload handler.
Returning text in beforeunload handler should prevent the close instead
of allow the close.
2013-08-29 15:36:09 +08:00
Cheng Zhao
34e1800716 Add 'loading-state-changed' event for BrowserWindow.
It's required for testing the BrowserWindow class.
2013-08-29 11:47:07 +08:00
Cheng Zhao
e00d3d4b37 Add spec for #70. 2013-08-29 11:40:07 +08:00
Cheng Zhao
19aa2b7979 Update apm. 2013-08-29 11:30:56 +08:00
Cheng Zhao
dbdf2d8d54 Add OnLoadingStateChanged for NativeWindowObserver. 2013-08-29 11:19:34 +08:00
Cheng Zhao
3be4a01963 Dump v0.4.1.
Should make this automatic in future.
2013-08-29 11:19:34 +08:00
Paul Betts
d0ab7e2c1e Merge pull request #69 from atom/apm-vs2012
Version bump atom/apm to fix script/bootstrap.py on Win32
2013-08-28 04:29:20 -07:00
Paul Betts
566b8136c9 Force pushery? 2013-08-28 13:27:49 +02:00
Paul Betts
c8150e570b Version bump atom/apm to fix script/bootstrap.py on Win32 2013-08-28 13:14:54 +02:00
Cheng Zhao
d2b4b761ba Update node: fix #66. 2013-08-27 18:39:32 +08:00
Cheng Zhao
018a48770a Add spec for heap snapshot crash (#66). 2013-08-27 17:47:44 +08:00
Cheng Zhao
799d9ada7d Make sure referrer is sent to the protocol handler. 2013-08-27 11:37:06 +08:00
Cheng Zhao
15ba32b489 Only redirect output to browser when running in CI. 2013-08-25 20:54:15 +08:00
Cheng Zhao
0f6617ec26 Make sure protocol module's specs do not pollute main.js. 2013-08-25 20:45:34 +08:00
Cheng Zhao
9e16e41bb3 Enable creating object from remote object's member. 2013-08-25 20:44:52 +08:00
Cheng Zhao
c86acc4cd7 Add remote.createFunctionWithReturnValue API. 2013-08-25 17:22:36 +08:00
Cheng Zhao
d88676bf65 💄 2013-08-25 16:49:54 +08:00
Cheng Zhao
d56a7d75de Use jQuery instead of zepto.
zepto always fails when requesting custom protocols, 💩.
2013-08-25 16:23:40 +08:00
Cheng Zhao
244d7eaf17 Allow returning file for custom protocol. 2013-08-25 16:06:29 +08:00
Cheng Zhao
1ed77371c0 Enable customing mime type and charset when returning reqeust string job. 2013-08-25 15:07:07 +08:00
Cheng Zhao
912bac698c Allow returning string in custom protocols. 2013-08-25 12:36:06 +08:00
Cheng Zhao
8464fb4f64 Pass weak pointers when posting tasks to other threads. 2013-08-24 20:32:12 +08:00
Cheng Zhao
0b01e49cd3 💄 2013-08-24 20:21:46 +08:00
Cheng Zhao
8cd7ccdc0d Call the JS handler to get the type of job. 2013-08-24 20:18:12 +08:00
Cheng Zhao
214df5ef69 Use zepto in the specs. 2013-08-24 20:15:31 +08:00
Cheng Zhao
b7c9f8ba1c The request should go through UI thread. 2013-08-24 19:46:38 +08:00
Cheng Zhao
f63661256f Return AdapterRequestJob when creating job. 2013-08-24 19:33:23 +08:00
Cheng Zhao
33279b1a2f Actually set and remove protocol handlers in IO thread.. 2013-08-24 17:59:34 +08:00
Cheng Zhao
96c173217f Remember registered schemes. 2013-08-24 16:38:19 +08:00
Cheng Zhao
b9cbfb8103 Skelecton for the protocol module. 2013-08-24 15:26:10 +08:00
Kevin Sawicki
fd299cb3fe Upgrade apm for more logging on errors 2013-08-21 10:47:48 -07:00
Cheng Zhao
c87956ef4f Make sure cibuild calls all necessary scripts. 2013-08-21 21:41:34 +08:00
Cheng Zhao
833190e8fa 💄 for cpplint. 2013-08-21 21:36:16 +08:00
Cheng Zhao
7cc1589097 Abort when there is a error in cpplint. 2013-08-21 21:35:41 +08:00
Cheng Zhao
e8ecbec3a7 💄 2013-08-21 21:24:18 +08:00
Cheng Zhao
4914dd67b6 Also upload node's headers to S3. 2013-08-21 12:16:40 +08:00
Cheng Zhao
1e895bdf76 Create the tarball containing node's headers. 2013-08-21 12:09:26 +08:00
Cheng Zhao
5b74dff8f1 Generate the distribution file under 'dist' directory. 2013-08-21 11:57:35 +08:00
Cheng Zhao
5b862fdf60 Also copy gyp files when copying headers. 2013-08-21 11:52:13 +08:00
Cheng Zhao
b411657b76 Hide window when running in CI. 2013-08-21 11:41:08 +08:00
Cheng Zhao
d160da7752 Add script to run specs in CI, fixes #61. 2013-08-21 11:41:08 +08:00
Cheng Zhao
0cd3f3cc40 No need to open video when testing webkitGetUserMedia.
The purpose of this test is to test whether the callback can be called.
2013-08-21 11:41:08 +08:00
Cheng Zhao
9105914b9e Merge pull request #58 from atom/windows-bootstrap
Fix invocation of npm on non-Cygwin
2013-08-20 10:14:06 -07:00
Kevin Sawicki
dea52ae767 Upgrade apm for output fixes 2013-08-20 09:41:10 -07:00
Kevin Sawicki
79d6e88d99 Update link to docs 2013-08-20 09:40:40 -07:00
Kevin Sawicki
f63cf3f283 Update apm URL to new location in atom org. 2013-08-20 09:30:05 -07:00
Paul Betts
22c8b55cb9 Fix invocation of npm on non-Cygwin 2013-08-20 16:37:02 +02:00
Cheng Zhao
7c85479e90 Update libchromiumcontent: Add "path" attribute for "File" class. 2013-08-19 21:27:24 +08:00
Cheng Zhao
dde07f03ec [Win] Remove the extra MenuWrapper class. 2013-08-19 16:30:25 +08:00
Cheng Zhao
738a929f8c [Win] Add flag to build window menu. 2013-08-17 17:10:16 +08:00
Cheng Zhao
6193a889ff [Win] Use the modified Menu2 class. 2013-08-17 16:45:21 +08:00
Cheng Zhao
ee10c469c8 [Win] Ship with Chromium's menu library.
I will hack it.
2013-08-17 16:42:12 +08:00
Cheng Zhao
cdbb16d8fe 💄 for cpplint. 2013-08-17 16:28:06 +08:00
Cheng Zhao
16d039ba47 Add spec for child_process.fork. 2013-08-16 18:51:51 +08:00
Cheng Zhao
beb62566e4 [Win] Fix the command line spliting code. 2013-08-16 18:48:02 +08:00
Cheng Zhao
d0b4800282 [Win] Fix conversion between wchar_t* and const wchar_t*. 2013-08-16 16:38:09 +08:00
Cheng Zhao
91a9fe41c9 [Win] Fix compilation error. 2013-08-16 16:33:32 +08:00
Cheng Zhao
979ec05ed3 [Win] Fix running node from python.
There is a mysterious "WindowsError [error 5] Access is denied" error is
the "executable" is not specified under Windows.
2013-08-16 16:28:45 +08:00
Cheng Zhao
a949e9542d Add AtomWindow.isWebViewFocused() API, fixes #57. 2013-08-16 12:56:25 +08:00
Cheng Zhao
a33c068ed1 Should inherit brightray's default web contents delegate, fixes #31. 2013-08-16 10:38:02 +08:00
Cheng Zhao
4a70077b10 The camera could start very slow. 2013-08-16 10:37:01 +08:00
Cheng Zhao
437d8fdb24 Add spec for #31. 2013-08-16 10:30:42 +08:00
Cheng Zhao
fc3bad0d4f 💄 2013-08-15 17:50:30 +08:00
Cheng Zhao
e648689981 Dump v0.3.4. 2013-08-15 16:11:25 +08:00
Cheng Zhao
c700d291e5 Don't overwrite existing fields when build menu from template. 2013-08-15 16:11:25 +08:00
Cheng Zhao
c858066709 Merge pull request #56 from atom/move-wiki-to-docs
Move wiki to docs
2013-08-15 00:04:30 -07:00
probablycorey
44cd21f0fd Normalize document names 2013-08-14 15:57:52 -07:00
probablycorey
41b57c673e Convert wiki links to relative markdown links 2013-08-14 15:52:00 -07:00
probablycorey
6bc2a3a5e8 Move wiki content to docs dir 2013-08-14 15:43:35 -07:00
Cheng Zhao
63b85bccab [Mac] Clean up the menu controller. 2013-08-14 23:03:02 +08:00
Cheng Zhao
f908619630 [Mac] Move the FixMenuTitles hack to MenuController. 2013-08-14 22:33:18 +08:00
Cheng Zhao
22012d41d8 [Mac] Ship the MenuController implementation from Chromium. 2013-08-14 22:24:21 +08:00
Cheng Zhao
ee2c696577 Make sure all MenuItem's options have a default value. 2013-08-14 21:59:49 +08:00
Cheng Zhao
2fd91e8c96 Enable passing objects and arrays when calling remote function. 2013-08-14 21:51:51 +08:00
Cheng Zhao
b39df5ea87 Use Menu.buildFromTemplate instead of raw menu APIs. 2013-08-14 21:49:13 +08:00
Cheng Zhao
5189caa610 💄 2013-08-14 21:19:26 +08:00
Cheng Zhao
341f1be07f Revert "Use named property handler for createObjectWithName."
This reverts commit 0c31494ffa.
2013-08-14 21:17:15 +08:00
Cheng Zhao
00d48b20bd Add spec for changing remote object's properties. 2013-08-14 13:28:15 +08:00
Cheng Zhao
0c31494ffa Use named property handler for createObjectWithName. 2013-08-14 13:14:28 +08:00
Cheng Zhao
971b72c2b6 When calling menu item's click callback, pass the item object. 2013-08-14 12:51:47 +08:00
Cheng Zhao
f267a7812f Add spec for MenuItem.click. 2013-08-14 12:51:37 +08:00
Cheng Zhao
58bfa11f7d Add spec for Menu.insert. 2013-08-14 12:09:53 +08:00
Cheng Zhao
114d247efb Add spec for attaching extra fields to menu items. 2013-08-14 12:04:16 +08:00
Cheng Zhao
d4a148ea52 The Menu object should store items by its index. 2013-08-14 12:03:37 +08:00
Cheng Zhao
c6f18f095e Enable to attach extra fields to menu items. 2013-08-14 11:07:44 +08:00
Cheng Zhao
452c350798 💄 for cpplint. 2013-08-13 17:07:36 +08:00
Cheng Zhao
f4425afb39 Move accelerator_util to browser/ui. 2013-08-13 17:07:25 +08:00
Cheng Zhao
d2f1a03b51 Move nsalert_synchronous_sheet to browser/ui. 2013-08-13 17:00:30 +08:00
Cheng Zhao
97bf979b22 Move file_dialog and message_box to browser/ui. 2013-08-13 16:51:47 +08:00
Cheng Zhao
149e2a4680 Update brightray: Disable Chromium's DataExtension. 2013-08-13 16:38:24 +08:00
Cheng Zhao
543ed1de98 Fix test case for the d3 module. 2013-08-12 17:11:47 +08:00
Cheng Zhao
a27009f0a9 Upload to S3 with readable version. 2013-08-12 15:01:05 +08:00
Cheng Zhao
3da4736a2b Collect header files from vendor/node. 2013-08-12 15:00:50 +08:00
Cheng Zhao
3d9af77b37 💄 2013-08-12 14:15:28 +08:00
Cheng Zhao
f9750f9ea9 Add test for d3 module. 2013-08-12 13:59:34 +08:00
Cheng Zhao
963b73a8aa Use result of git describe as version. 2013-08-08 17:49:35 +08:00
Cheng Zhao
dd05759c01 Add process.versions["atom-shell"]. 2013-08-08 17:26:20 +08:00
Cheng Zhao
6684cdd72a Add version file. 2013-08-08 17:22:35 +08:00
Cheng Zhao
47be87fe5f Only zip specified files. 2013-08-08 17:13:45 +08:00
Cheng Zhao
b0b6214416 Update node: enable running like upstream node. 2013-08-08 16:57:07 +08:00
Cheng Zhao
317bd0debf Discard the extra node binary.
We now use atom-shell's binary to execute scripts.
2013-08-08 16:57:07 +08:00
Cheng Zhao
574811fee0 Fix process.argv[0] for child_process.fork. 2013-08-08 16:57:07 +08:00
Cheng Zhao
216f5917bf When passing --atom-child_process-fork, run like upstream node. 2013-08-08 16:57:07 +08:00
Cheng Zhao
c5d48607d9 Fix README on the license thing. 2013-08-08 16:57:07 +08:00
Paul Betts
beba4efc9f Fix broken submodule reference 2013-08-07 09:49:02 -07:00
Cheng Zhao
3b615d7fad Update README. 2013-08-07 14:20:32 +08:00
Cheng Zhao
b2872eaf60 [Win] Implement context menu. 2013-08-06 21:08:52 +08:00
Cheng Zhao
286335c8f9 Only set application menu on OS X. 2013-08-06 21:08:30 +08:00
Cheng Zhao
296feb1a50 💄 on string comparison. 2013-08-06 20:44:18 +08:00
Cheng Zhao
f46d5eb651 Update apm: Add support for Windows. 2013-08-06 20:24:51 +08:00
Cheng Zhao
c5e0ae6495 Fix calling apm under cygwin. 2013-08-06 20:14:28 +08:00
Cheng Zhao
17a4842eaa [Win] Should filters in the save as dialog. 2013-08-06 19:39:10 +08:00
Cheng Zhao
ea1f81aa52 [Mac] Add app.dock.getBadge() API. 2013-08-06 16:39:31 +08:00
Cheng Zhao
b1f88d680b [Mac] Add API for dock, fixes #46. 2013-08-06 16:19:56 +08:00
Cheng Zhao
6e90430df5 Use apm to install third party node modules. 2013-08-03 16:39:54 +08:00
Cheng Zhao
192216ea64 Update node before running npm. 2013-08-03 16:38:02 +08:00
Cheng Zhao
55034be104 Add power-monitor module, fixes #45. 2013-08-03 16:03:56 +08:00
Cheng Zhao
79870494df Add test for native modules. 2013-08-01 15:40:54 +08:00
Cheng Zhao
394fb77ee0 [Win] Implement dialog.showSaveDialog. 2013-08-01 15:39:53 +08:00
Cheng Zhao
d888f5cc31 Revert "Enable getUserMedia(). Part of fixes for #31."
Now the getUserMedia is implemented in brightray.

This reverts commit f26db5c7a1.

Conflicts:
	atom.gyp
2013-08-01 14:50:06 +08:00
Cheng Zhao
51cf1db652 Update brightray. 2013-07-31 13:05:30 +08:00
Cheng Zhao
2c237e9d08 Update node: fix child_process.fork on Windows. 2013-07-31 12:36:29 +08:00
Cheng Zhao
0acaff48b8 Update node: fix process.stdio. 2013-07-31 12:30:07 +08:00
Cheng Zhao
247f7a51eb Fix the update-node script. 2013-07-31 12:14:58 +08:00
Cheng Zhao
52a3dc8c9d Show devtools on right click in specs window. 2013-07-31 10:55:47 +08:00
Cheng Zhao
02de9c3b39 Enable idle GC for both browser and renderer. 2013-07-29 20:50:03 +08:00
Cheng Zhao
4acbbd1ad7 Discard the idle gc module. 2013-07-29 20:34:07 +08:00
Cheng Zhao
e995383401 Guard against double erasing an object, fixes #44. 2013-07-29 19:14:35 +08:00
Cheng Zhao
402dc2f5d2 Expose gc() in specs. 2013-07-29 16:35:42 +08:00
Cheng Zhao
cd19666307 Remove the useless remote.getObject API. 2013-07-29 16:35:25 +08:00
Cheng Zhao
e1ac21aa7a Add script translate addresses in crash report into symbols. 2013-07-28 10:04:56 +08:00
Cheng Zhao
f8090dc010 Fix the update-node script for our new style of URLs. 2013-07-26 19:52:47 +08:00
Cheng Zhao
d7351f102f Update node to v0.10.5. 2013-07-26 19:31:58 +08:00
Cheng Zhao
64b68add3d Update to latest libchromiumcontent and node. 2013-07-26 19:18:27 +08:00
Cheng Zhao
51a632d7d9 Don't make use of the 'id' property when sending objects.
Fixes #43.
2013-07-26 16:40:29 +08:00
Cheng Zhao
511b2a4422 Add test for id property of remote object. 2013-07-26 16:40:29 +08:00
Cheng Zhao
47686b3233 [Win] Add key bindings for message box. 2013-07-25 21:04:33 +08:00
Cheng Zhao
2f2cbce9b9 [Win] Override node's console and output stream with chromium's logging.
On Window node doesn't outputing as GUI program, so we have to switch to
chromium's implementation. Hacking into node (like what we did before
this commit) would sometimes make the outputing blocked.
2013-07-25 20:06:23 +08:00
Cheng Zhao
a163b148ce [Win] Show title and message's detail. 2013-07-25 17:06:08 +08:00
Cheng Zhao
21f59ec730 [Win] Message box now returns the right result. 2013-07-25 16:56:02 +08:00
Cheng Zhao
e2898d4f2a [Win] Layout the buttons of message box manually. 2013-07-25 16:19:40 +08:00
Cheng Zhao
da2a21f97d [Win] Layout the buttons. 2013-07-25 15:47:12 +08:00
Cheng Zhao
23452172ed [Win] Use system's window background for message box. 2013-07-25 15:30:44 +08:00
Cheng Zhao
489c5daff5 [Win] Show buttons in message box. 2013-07-25 15:22:44 +08:00
Cheng Zhao
2e7dcade25 [Win] Make message box modal. 2013-07-24 18:51:03 +08:00
Cheng Zhao
8d41c27a8c [Win] Show an empty window for ShowMessageBox. 2013-07-24 18:34:50 +08:00
Cheng Zhao
b91f29429e Fix updating the win32 python repo. 2013-07-24 18:33:49 +08:00
Cheng Zhao
41f0f9bcf3 [Win] Ignore 4049 link warning. 2013-07-24 18:33:27 +08:00
Cheng Zhao
bbc049ba3a [Win] Update node: fix console and process.stdout. 2013-07-24 15:37:40 +08:00
Cheng Zhao
a7557a9626 Do not end the process when other exception occurs. 2013-07-24 15:20:59 +08:00
Cheng Zhao
85e4e99e7f [Win] Poll events with GetQueuedCompletionStatus, fixes #41. 2013-07-23 19:19:11 +08:00
Cheng Zhao
767354476b Should force the embed thread to quit when terminating. 2013-07-23 16:55:09 +08:00
Cheng Zhao
7d61dcdde1 [Win] Write log to file in Debug build. 2013-07-23 16:37:00 +08:00
Cheng Zhao
e248e2ffc8 Delay the unresponsive message for a second, fixes #42.
It could happen that a window became responsive immediately after the
unresponsive message is sent (for example, the window was blocked by
showing a save as dialog), by delaying sending the unresponsive message
for a second, we can give the window a chance to whether it's really
unresponsive or not.
2013-07-23 15:29:56 +08:00
Cheng Zhao
9efde9577a Move the watcher queue hack to NodeBindingsMac.
It's mac only.
2013-07-23 13:08:40 +08:00
Cheng Zhao
620c9fa109 remote.require should return the same object for the same module.
This is required to use jasmine to test methods of the remote module.
2013-07-23 11:59:07 +08:00
Cheng Zhao
2f9c30dd50 [Win] Simply poll events for every 50ms.
This only makes async functions half work, will use IOCP for the
polling.
2013-07-22 16:43:58 +08:00
Cheng Zhao
7ad8c3b569 Use walkdir instead of findit, which supports Windows. 2013-07-22 16:41:52 +08:00
Cheng Zhao
008b8d404d Don't separate node bindings into renderer and browser part.
Since we are going to use embeding thread to implement message
integration on all platforms, we do not need to separate renderer and
browser anymore.
2013-07-22 16:05:35 +08:00
Cheng Zhao
bc62d8c2c5 Ship the filename_rules.gypi which is modified from chromium. 2013-07-22 15:59:59 +08:00
Cheng Zhao
fa9e939745 Cleanup the node bindings code. 2013-07-22 15:25:39 +08:00
Cheng Zhao
1f019b36d2 Remove redundant calls to wake up main thread. 2013-07-22 15:06:08 +08:00
Cheng Zhao
00fda0e778 Move common message integration code to NodeBindings' base class. 2013-07-22 14:58:25 +08:00
Cheng Zhao
41ff753339 Don't use gcd to post tasks to main thread. 2013-07-22 13:03:13 +08:00
Cheng Zhao
5bbb41415a Improve spec window's default behavior. 2013-07-19 10:47:00 +08:00
Cheng Zhao
99c37fc902 [Win] Pass relative path in the test script.
Parameter starting with '/' could be thought as switches under Windows.
2013-07-19 10:41:24 +08:00
Cheng Zhao
d59fb55a81 Add BrowserWindow.inspectElement(x, y) API. 2013-07-18 19:21:25 +08:00
Cheng Zhao
4ddd1bccb4 Fix how we detect renderer crash. 2013-07-18 18:37:40 +08:00
Cheng Zhao
36f125f3d3 Add test for the unzip module. 2013-07-18 15:19:36 +08:00
Cheng Zhao
42a6a7d0d3 Add two tests regarding crashes in context switching. 2013-07-17 18:52:02 +08:00
Cheng Zhao
ec33b4d579 Install spec's third party modules in the source root. 2013-07-17 18:51:16 +08:00
Cheng Zhao
1d9fca25dc Add test for setImmediate and process.nextTick. 2013-07-17 17:07:31 +08:00
Cheng Zhao
5f443ebbb0 Fix .gitignore. 2013-07-17 17:07:14 +08:00
Cheng Zhao
2af41ae56d Add script to run test. 2013-07-17 16:42:06 +08:00
Cheng Zhao
45285c8c9c Should install node modules for default_app and spec when bootstrap. 2013-07-17 16:29:40 +08:00
Cheng Zhao
2560782d54 Add empty spec skeleton. 2013-07-17 16:28:14 +08:00
Cheng Zhao
9b828ce39a Start the specified app if there is one in command line parameters. 2013-07-17 16:21:33 +08:00
215 changed files with 10151 additions and 2073 deletions

15
.gitignore vendored
View File

@@ -1,14 +1,11 @@
.DS_Store
atom-shell.zip
version
build/
dist/
frameworks/
node/
/build/
/dist/
/frameworks/
/out/
/vendor/brightray/vendor/download/
/vendor/python_26/
node_modules/
out/
vendor/brightray/vendor/download/
vendor/python_26/
*.xcodeproj
*.swp
*.pyc

3
.gitmodules vendored
View File

@@ -10,3 +10,6 @@
[submodule "vendor/depot_tools"]
path = vendor/depot_tools
url = https://chromium.googlesource.com/chromium/tools/depot_tools.git
[submodule "vendor/apm"]
path = vendor/apm
url = https://github.com/atom/apm.git

View File

@@ -1,18 +1,19 @@
# Atom Shell
Experimental native layer for the [Atom](https://github.com/github/atom).
Native layer for the [Atom](https://github.com/github/atom).
## Development
## Features
### One-time setup
* Write desktop applications with web techniques
* Support built-in and third-party modules of node.js
* Support native node.js modules
* Extended built-in modules for desktop programming
* JavaScript on browser side
* Easy API for cross-process communication
$ script/bootstrap
## Usage & Development
### Building
$ script/build
This will build the app into the `build` directory.
See the docs [here](https://github.com/atom/atom-shell/tree/master/docs).
## License

View File

@@ -2,27 +2,101 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/public/app/content_main.h"
#include "app/atom_main.h"
#include <stdlib.h>
#include <string.h>
#if defined(OS_WIN)
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include <windows.h>
#include <shellapi.h>
#include "app/atom_main_delegate.h"
#include "base/environment.h"
#include "content/public/app/startup_helper_win.h"
#include "sandbox/win/src/sandbox_types.h"
#else // defined(OS_WIN)
#include "app/atom_library_main.h"
#endif // defined(OS_MACOSX) || defined(OS_LINUX)
// Declaration of node::Start.
namespace node {
int Start(int argc, char *argv[]);
}
#if defined(OS_WIN)
#include "app/atom_main_delegate.h"
#include "content/public/app/startup_helper_win.h"
#include "sandbox/win/src/sandbox_types.h"
int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) {
int argc = 0;
wchar_t** wargv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
// Attach to the parent console if we've got one so that stdio works
AttachConsole(ATTACH_PARENT_PROCESS);
FILE* dontcare;
freopen_s(&dontcare, "CON", "w", stdout);
freopen_s(&dontcare, "CON", "w", stderr);
freopen_s(&dontcare, "CON", "r", stdin);
scoped_ptr<base::Environment> env(base::Environment::Create());
std::string node_indicator;
if (env->GetVar("ATOM_SHELL_INTERNAL_RUN_AS_NODE", &node_indicator) &&
node_indicator == "1") {
// Convert argv to to UTF8
char** argv = new char*[argc];
for (int i = 0; i < argc; i++) {
// Compute the size of the required buffer
DWORD size = WideCharToMultiByte(CP_UTF8,
0,
wargv[i],
-1,
NULL,
0,
NULL,
NULL);
if (size == 0) {
// This should never happen.
fprintf(stderr, "Could not convert arguments to utf8.");
exit(1);
}
// Do the actual conversion
argv[i] = new char[size];
DWORD result = WideCharToMultiByte(CP_UTF8,
0,
wargv[i],
-1,
argv[i],
size,
NULL,
NULL);
if (result == 0) {
// This should never happen.
fprintf(stderr, "Could not convert arguments to utf8.");
exit(1);
}
}
// Now that conversion is done, we can finally start.
return node::Start(argc, argv);
}
int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t*, int) {
sandbox::SandboxInterfaceInfo sandbox_info = {0};
content::InitializeSandboxInfo(&sandbox_info);
atom::AtomMainDelegate delegate;
return content::ContentMain(instance, &sandbox_info, &delegate);
}
#else
#include "app/atom_library_main.h"
#else // 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)
return node::Start(argc, const_cast<char**>(argv));
return AtomMain(argc, argv);
}
#endif // OS_WIN
#endif // defined(OS_MACOSX) || defined(OS_LINUX)

5
app/atom_main.h Normal file
View File

@@ -0,0 +1,5 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/public/app/content_main.h"

View File

@@ -9,6 +9,8 @@
#include "browser/atom_browser_client.h"
#include "content/public/common/content_switches.h"
#include "renderer/atom_renderer_client.h"
#include "ui/base/resource/resource_bundle.h"
#include "base/path_service.h"
namespace atom {
@@ -23,24 +25,46 @@ bool AtomMainDelegate::BasicStartupComplete(int* exit_code) {
#if defined(OS_WIN)
logging::InitLogging(
L"debug.log",
#if defined(DEBUG)
logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG ,
#else
logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
#endif // defined(NDEBUG)
logging::LOCK_LOG_FILE,
logging::DELETE_OLD_LOG_FILE,
logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);
logging::SetLogItems(true, false, true, false);
#endif
#endif // defined(OS_WIN)
return brightray::MainDelegate::BasicStartupComplete(exit_code);
}
void AtomMainDelegate::PreSandboxStartup() {
brightray::MainDelegate::PreSandboxStartup();
#if defined(OS_MACOSX)
OverrideChildProcessPath();
OverrideFrameworkBundlePath();
SetProcessName();
#endif
InitializeResourceBundle();
// Disable renderer sandbox for most of node's functions.
CommandLine* command_line = CommandLine::ForCurrentProcess();
command_line->AppendSwitch(switches::kNoSandbox);
}
void AtomMainDelegate::InitializeResourceBundle() {
base::FilePath path;
#if defined(OS_MACOSX)
path = GetResourcesPakFilePath();
#else
base::FilePath pak_dir;
PathService::Get(base::DIR_MODULE, &pak_dir);
path = pak_dir.Append(FILE_PATH_LITERAL("content_shell.pak"));
#endif
ui::ResourceBundle::InitSharedInstanceWithPakPath(path);
}
content::ContentBrowserClient* AtomMainDelegate::CreateContentBrowserClient() {
browser_client_.reset(new AtomBrowserClient);
return browser_client_.get();

View File

@@ -17,6 +17,14 @@ class AtomMainDelegate : public brightray::MainDelegate {
protected:
virtual bool BasicStartupComplete(int* exit_code) OVERRIDE;
virtual void PreSandboxStartup() OVERRIDE;
virtual void InitializeResourceBundle();
#if defined(OS_MACOSX)
virtual base::FilePath GetResourcesPakFilePath();
virtual void OverrideChildProcessPath();
virtual void OverrideFrameworkBundlePath();
virtual void SetProcessName();
#endif
private:
virtual content::ContentBrowserClient* CreateContentBrowserClient() OVERRIDE;

View File

@@ -0,0 +1,66 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "app/atom_main_delegate.h"
#import "base/mac/bundle_locations.h"
#import "base/mac/foundation_util.h"
#import "base/mac/mac_util.h"
#include "base/command_line.h"
#include "base/path_service.h"
#include "base/strings/sys_string_conversions.h"
#include "content/public/common/content_paths.h"
#include "content/public/common/content_switches.h"
#include "vendor/brightray/common/application_info.h"
#include "vendor/brightray/common/mac/main_application_bundle.h"
namespace atom {
namespace {
base::FilePath GetFrameworksPath() {
return brightray::MainApplicationBundlePath().Append("Contents")
.Append("Frameworks");
}
} // namespace
base::FilePath AtomMainDelegate::GetResourcesPakFilePath() {
NSString* path = [base::mac::FrameworkBundle()
pathForResource:@"content_shell" ofType:@"pak"];
return base::mac::NSStringToFilePath(path);
}
void AtomMainDelegate::OverrideFrameworkBundlePath() {
base::mac::SetOverrideFrameworkBundlePath(
GetFrameworksPath().Append("Atom.framework"));
}
void AtomMainDelegate::OverrideChildProcessPath() {
base::FilePath helper_path = GetFrameworksPath().Append("Atom Helper.app")
.Append("Contents")
.Append("MacOS")
.Append("Atom Helper");
PathService::Override(content::CHILD_PROCESS_EXE, helper_path);
}
void AtomMainDelegate::SetProcessName() {
const auto& command_line = *CommandLine::ForCurrentProcess();
auto process_type = command_line.GetSwitchValueASCII(switches::kProcessType);
std::string suffix;
if (process_type == switches::kRendererProcess)
suffix = "Renderer";
else if (process_type == switches::kPluginProcess ||
process_type == switches::kPpapiPluginProcess)
suffix = "Plug-In Host";
else if (process_type == switches::kUtilityProcess)
suffix = "Utility";
else
return;
base::mac::SetProcessName(base::mac::NSToCFCast(base::SysUTF8ToNSString(
brightray::GetApplicationName() + " " + suffix)));
}
} // namespace atom

BIN
app/win/atom.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 KiB

Binary file not shown.

View File

@@ -1,9 +1,10 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by brightray_example.rc
#define IDR_MAINFRAME 1
// Next default values for new objects
//
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101

125
atom.gyp
View File

@@ -4,6 +4,7 @@
'product_name': 'Atom',
'app_sources': [
'app/atom_main.cc',
'app/atom_main.h',
],
'bundle_sources': [
'browser/mac/atom.icns',
@@ -18,6 +19,8 @@
'browser/api/lib/ipc.coffee',
'browser/api/lib/menu.coffee',
'browser/api/lib/menu-item.coffee',
'browser/api/lib/power-monitor.coffee',
'browser/api/lib/protocol.coffee',
'browser/atom/atom.coffee',
'browser/atom/objects-registry.coffee',
'browser/atom/rpc-server.coffee',
@@ -31,10 +34,7 @@
'lib_sources': [
'app/atom_main_delegate.cc',
'app/atom_main_delegate.h',
'browser/accelerator_util.cc',
'browser/accelerator_util.h',
'browser/accelerator_util_mac.mm',
'browser/accelerator_util_win.cc',
'app/atom_main_delegate_mac.mm',
'browser/api/atom_api_app.cc',
'browser/api/atom_api_app.h',
'browser/api/atom_api_auto_updater.cc',
@@ -55,6 +55,10 @@
'browser/api/atom_api_menu_mac.mm',
'browser/api/atom_api_menu_win.cc',
'browser/api/atom_api_menu_win.h',
'browser/api/atom_api_power_monitor.cc',
'browser/api/atom_api_power_monitor.h',
'browser/api/atom_api_protocol.cc',
'browser/api/atom_api_protocol.h',
'browser/api/atom_api_window.cc',
'browser/api/atom_api_window.h',
'browser/api/atom_browser_bindings.cc',
@@ -76,8 +80,6 @@
'browser/atom_browser_main_parts.cc',
'browser/atom_browser_main_parts.h',
'browser/atom_browser_main_parts_mac.mm',
'browser/atom_event_processing_window.h',
'browser/atom_event_processing_window.mm',
'browser/atom_javascript_dialog_manager.cc',
'browser/atom_javascript_dialog_manager.h',
'browser/browser.cc',
@@ -88,16 +90,6 @@
'browser/crash_reporter.h',
'browser/crash_reporter_mac.mm',
'browser/crash_reporter_win.cc',
'browser/file_dialog.h',
'browser/file_dialog_mac.mm',
'browser/file_dialog_win.cc',
'browser/media/media_capture_devices_dispatcher.cc',
'browser/media/media_capture_devices_dispatcher.h',
'browser/media/media_stream_devices_controller.cc',
'browser/media/media_stream_devices_controller.h',
'browser/message_box.h',
'browser/message_box_mac.mm',
'browser/message_box_win.cc',
'browser/native_window.cc',
'browser/native_window.h',
'browser/native_window_mac.h',
@@ -105,10 +97,34 @@
'browser/native_window_win.cc',
'browser/native_window_win.h',
'browser/native_window_observer.h',
'browser/node_bindings_browser_win.cc',
'browser/node_bindings_browser_win.h',
'browser/nsalert_synchronous_sheet.h',
'browser/nsalert_synchronous_sheet.mm',
'browser/net/adapter_request_job.cc',
'browser/net/adapter_request_job.h',
'browser/net/atom_url_request_context_getter.cc',
'browser/net/atom_url_request_context_getter.h',
'browser/net/atom_url_request_job_factory.cc',
'browser/net/atom_url_request_job_factory.h',
'browser/net/url_request_string_job.cc',
'browser/net/url_request_string_job.h',
'browser/ui/accelerator_util.cc',
'browser/ui/accelerator_util.h',
'browser/ui/accelerator_util_mac.mm',
'browser/ui/accelerator_util_win.cc',
'browser/ui/atom_event_processing_window.h',
'browser/ui/atom_event_processing_window.mm',
'browser/ui/atom_menu_controller_mac.h',
'browser/ui/atom_menu_controller_mac.mm',
'browser/ui/file_dialog.h',
'browser/ui/file_dialog_mac.mm',
'browser/ui/file_dialog_win.cc',
'browser/ui/message_box.h',
'browser/ui/message_box_mac.mm',
'browser/ui/message_box_win.cc',
'browser/ui/nsalert_synchronous_sheet_mac.h',
'browser/ui/nsalert_synchronous_sheet_mac.mm',
'browser/ui/win/menu_2.cc',
'browser/ui/win/menu_2.h',
'browser/ui/win/native_menu_win.cc',
'browser/ui/win/native_menu_win.h',
'browser/window_list.cc',
'browser/window_list.h',
'browser/window_list_observer.h',
@@ -116,7 +132,6 @@
'common/api/api_messages.h',
'common/api/atom_api_clipboard.cc',
'common/api/atom_api_clipboard.h',
'common/api/atom_api_idle_gc.cc',
'common/api/atom_api_id_weak_map.cc',
'common/api/atom_api_id_weak_map.h',
'common/api/atom_api_shell.cc',
@@ -128,15 +143,20 @@
'common/api/atom_extensions.h',
'common/api/object_life_monitor.cc',
'common/api/object_life_monitor.h',
'common/draggable_region.cc',
'common/draggable_region.h',
'common/node_bindings.cc',
'common/node_bindings.h',
'common/node_bindings_mac.cc',
'common/node_bindings_mac.h',
'common/node_bindings_mac.mm',
'common/node_bindings_win.cc',
'common/node_bindings_win.h',
'common/options_switches.cc',
'common/options_switches.h',
'common/platform_util.h',
'common/platform_util_mac.mm',
'common/platform_util_win.cc',
'common/v8_conversions.h',
'common/v8_value_converter_impl.cc',
'common/v8_value_converter_impl.h',
'renderer/api/atom_api_renderer_ipc.cc',
@@ -147,8 +167,6 @@
'renderer/atom_render_view_observer.h',
'renderer/atom_renderer_client.cc',
'renderer/atom_renderer_client.h',
'renderer/node_bindings_renderer_win.cc',
'renderer/node_bindings_renderer_win.h',
],
'framework_sources': [
'app/atom_library_main.cc',
@@ -158,6 +176,7 @@
['OS=="win"', {
'app_sources': [
'app/win/resource.h',
'app/win/atom.ico',
'app/win/atom.rc',
'<(libchromiumcontent_src_dir)/content/app/startup_helper_win.cc',
],
@@ -179,6 +198,17 @@
'mac_framework_dirs': [
'<(atom_source_root)/frameworks',
],
'includes': [
# Rules for excluding e.g. foo_win.cc from the build on non-Windows.
'filename_rules.gypi',
],
'configurations': {
'Debug': {
'defines': [
'DEBUG',
],
},
},
},
'targets': [
{
@@ -230,13 +260,6 @@
'browser/default_app',
],
},
{
# Copy node binary for worker process support.
'destination': '<(PRODUCT_DIR)/<(product_name).app/Contents/Resources',
'files': [
'node/node',
],
},
],
'postbuilds': [
{
@@ -269,6 +292,7 @@
'destination': '<(PRODUCT_DIR)',
'files': [
'<(libchromiumcontent_library_dir)/chromiumcontent.dll',
'<(libchromiumcontent_library_dir)/ffmpegsumo.dll',
'<(libchromiumcontent_library_dir)/icudt.dll',
'<(libchromiumcontent_library_dir)/libGLESv2.dll',
'<(libchromiumcontent_resources_dir)/content_shell.pak',
@@ -280,12 +304,6 @@
'browser/default_app',
]
},
{
'destination': '<(PRODUCT_DIR)/resources',
'files': [
'node/node.exe',
]
},
],
}], # OS=="win"
],
@@ -318,6 +336,7 @@
'libraries': [
'-limm32.lib',
'-loleacc.lib',
'-lComdlg32.lib',
'<(atom_source_root)/<(libchromiumcontent_library_dir)/chromiumviews.lib',
],
},
@@ -468,5 +487,37 @@
}, # target helper
],
}], # OS==Mac
['OS=="win"', {
'targets': [
{
'target_name': 'generate_node_lib',
'type': 'none',
'dependencies': [
'<(project_name)',
],
'actions': [
{
'action_name': 'Create node.lib',
'inputs': [
'<(PRODUCT_DIR)/atom.lib',
'<(libchromiumcontent_library_dir)/chromiumcontent.dll.lib',
],
'outputs': [
'<(PRODUCT_DIR)/node.lib',
],
'action': [
'lib.exe',
'/nologo',
# We can't use <(_outputs) here because that escapes the
# backslash in the path, which confuses lib.exe.
'/OUT:<(PRODUCT_DIR)\\node.lib',
'<@(_inputs)',
],
'msvs_cygwin_shell': 0,
},
],
}, # target generate_node_lib
],
}], # OS==win
],
}

View File

@@ -1,91 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/accelerator_util.h"
#include <string>
#include "base/string_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "ui/base/accelerators/accelerator.h"
namespace accelerator_util {
namespace {
// For Mac, we convert "Ctrl" to "Command" and "MacCtrl" to "Ctrl". Other
// platforms leave the shortcut untouched.
std::string NormalizeShortcutSuggestion(const std::string& suggestion) {
#if !defined(OS_MACOSX)
return suggestion;
#endif
std::string key;
std::vector<std::string> tokens;
base::SplitString(suggestion, '+', &tokens);
for (size_t i = 0; i < tokens.size(); i++) {
if (tokens[i] == "Ctrl")
tokens[i] = "Command";
else if (tokens[i] == "MacCtrl")
tokens[i] = "Ctrl";
}
return JoinString(tokens, '+');
}
} // namespace
bool StringToAccelerator(const std::string& description,
ui::Accelerator* accelerator) {
std::string shortcut(NormalizeShortcutSuggestion(description));
std::vector<std::string> tokens;
base::SplitString(shortcut, '+', &tokens);
if (tokens.size() < 2 || tokens.size() > 4) {
LOG(WARNING) << "Invalid accelerator description: " << description;
return false;
}
// Now, parse it into an accelerator.
int modifiers = ui::EF_NONE;
ui::KeyboardCode key = ui::VKEY_UNKNOWN;
for (size_t i = 0; i < tokens.size(); i++) {
if (tokens[i] == "Ctrl") {
modifiers |= ui::EF_CONTROL_DOWN;
} else if (tokens[i] == "Command") {
modifiers |= ui::EF_COMMAND_DOWN;
} else if (tokens[i] == "Alt") {
modifiers |= ui::EF_ALT_DOWN;
} else if (tokens[i] == "Shift") {
modifiers |= ui::EF_SHIFT_DOWN;
} else if (tokens[i].size() == 1) {
if (key != ui::VKEY_UNKNOWN) {
// Multiple key assignments.
key = ui::VKEY_UNKNOWN;
break;
}
if (tokens[i][0] >= 'A' && tokens[i][0] <= 'Z') {
key = static_cast<ui::KeyboardCode>(ui::VKEY_A + (tokens[i][0] - 'A'));
} else if (tokens[i][0] >= '0' && tokens[i][0] <= '9') {
key = static_cast<ui::KeyboardCode>(ui::VKEY_0 + (tokens[i][0] - '0'));
} else if (tokens[i][0] >= '+' && tokens[i][0] <= '.') {
key = static_cast<ui::KeyboardCode>(
ui::VKEY_OEM_PLUS + (tokens[i][0] - '+'));
} else {
LOG(WARNING) << "Invalid accelerator character: " << tokens[i];
key = ui::VKEY_UNKNOWN;
break;
}
} else {
LOG(WARNING) << "Invalid accelerator token: " << tokens[i];
return false;
}
}
*accelerator = ui::Accelerator(key, modifiers);
SetPlatformAccelerator(accelerator);
return true;
}
} // namespace accelerator_util

View File

@@ -7,6 +7,7 @@
#include "base/values.h"
#include "base/command_line.h"
#include "browser/browser.h"
#include "common/v8_conversions.h"
#include "vendor/node/src/node.h"
namespace atom {
@@ -111,14 +112,14 @@ v8::Handle<v8::Value> App::GetVersion(const v8::Arguments &args) {
v8::Handle<v8::Value> App::AppendSwitch(const v8::Arguments &args) {
v8::HandleScope scope;
if (!args[0]->IsString())
std::string switch_string;
if (!FromV8Arguments(args, &switch_string))
return node::ThrowError("Bad argument");
std::string switch_string(*v8::String::Utf8Value(args[0]));
if (args.Length() == 1) {
CommandLine::ForCurrentProcess()->AppendSwitch(switch_string);
} else {
std::string value(*v8::String::Utf8Value(args[1]));
std::string value = FromV8Value(args[1]);
CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switch_string, value);
}
@@ -130,15 +131,52 @@ v8::Handle<v8::Value> App::AppendSwitch(const v8::Arguments &args) {
v8::Handle<v8::Value> App::AppendArgument(const v8::Arguments &args) {
v8::HandleScope scope;
if (!args[0]->IsString())
std::string value;
if (!FromV8Arguments(args, &value))
return node::ThrowError("Bad argument");
std::string value(*v8::String::Utf8Value(args[0]));
CommandLine::ForCurrentProcess()->AppendArg(value);
return v8::Undefined();
}
#if defined(OS_MACOSX)
// static
v8::Handle<v8::Value> App::DockBounce(const v8::Arguments& args) {
std::string type = FromV8Value(args[0]);
int request_id = -1;
if (type == "critical")
request_id = Browser::Get()->DockBounce(Browser::BOUNCE_CRITICAL);
else if (type == "informational")
request_id = Browser::Get()->DockBounce(Browser::BOUNCE_INFORMATIONAL);
else
return node::ThrowTypeError("Invalid bounce type");
return v8::Integer::New(request_id);
}
// static
v8::Handle<v8::Value> App::DockCancelBounce(const v8::Arguments& args) {
Browser::Get()->DockCancelBounce(FromV8Value(args[0]));
return v8::Undefined();
}
// static
v8::Handle<v8::Value> App::DockSetBadgeText(const v8::Arguments& args) {
Browser::Get()->DockSetBadgeText(FromV8Value(args[0]));
return v8::Undefined();
}
// static
v8::Handle<v8::Value> App::DockGetBadgeText(const v8::Arguments& args) {
std::string text(Browser::Get()->DockGetBadgeText());
return ToV8Value(text);
}
#endif // defined(OS_MACOSX)
// static
void App::Initialize(v8::Handle<v8::Object> target) {
v8::HandleScope scope;
@@ -157,6 +195,13 @@ void App::Initialize(v8::Handle<v8::Object> target) {
NODE_SET_METHOD(target, "appendSwitch", AppendSwitch);
NODE_SET_METHOD(target, "appendArgument", AppendArgument);
#if defined(OS_MACOSX)
NODE_SET_METHOD(target, "dockBounce", DockBounce);
NODE_SET_METHOD(target, "dockCancelBounce", DockCancelBounce);
NODE_SET_METHOD(target, "dockSetBadgeText", DockSetBadgeText);
NODE_SET_METHOD(target, "dockGetBadgeText", DockGetBadgeText);
#endif // defined(OS_MACOSX)
}
} // namespace api

View File

@@ -43,6 +43,13 @@ class App : public EventEmitter,
static v8::Handle<v8::Value> AppendSwitch(const v8::Arguments &args);
static v8::Handle<v8::Value> AppendArgument(const v8::Arguments &args);
#if defined(OS_MACOSX)
static v8::Handle<v8::Value> DockBounce(const v8::Arguments& args);
static v8::Handle<v8::Value> DockCancelBounce(const v8::Arguments& args);
static v8::Handle<v8::Value> DockSetBadgeText(const v8::Arguments& args);
static v8::Handle<v8::Value> DockGetBadgeText(const v8::Arguments& args);
#endif // defined(OS_MACOSX)
DISALLOW_COPY_AND_ASSIGN(App);
};

View File

@@ -6,6 +6,7 @@
#include "base/values.h"
#include "browser/auto_updater.h"
#include "common/v8_conversions.h"
namespace atom {
@@ -56,7 +57,7 @@ v8::Handle<v8::Value> AutoUpdater::New(const v8::Arguments &args) {
// static
v8::Handle<v8::Value> AutoUpdater::SetFeedURL(const v8::Arguments &args) {
auto_updater::AutoUpdater::SetFeedURL(*v8::String::Utf8Value(args[0]));
auto_updater::AutoUpdater::SetFeedURL(FromV8Value(args[0]));
return v8::Undefined();
}

View File

@@ -6,6 +6,7 @@
#include "base/values.h"
#include "common/api/api_messages.h"
#include "common/v8_conversions.h"
#include "common/v8_value_converter_impl.h"
#include "content/public/browser/render_view_host.h"
#include "vendor/node/src/node.h"
@@ -22,13 +23,11 @@ namespace api {
v8::Handle<v8::Value> BrowserIPC::Send(const v8::Arguments &args) {
v8::HandleScope scope;
if (!args[0]->IsString() || !args[1]->IsNumber() || !args[2]->IsNumber())
string16 channel;
int process_id, routing_id;
if (!FromV8Arguments(args, &channel, &process_id, &routing_id))
return node::ThrowTypeError("Bad argument");
std::string channel(*v8::String::Utf8Value(args[0]));
int process_id = args[1]->IntegerValue();
int routing_id = args[2]->IntegerValue();
RenderViewHost* render_view_host(RenderViewHost::FromID(
process_id, routing_id));
if (!render_view_host)

View File

@@ -5,6 +5,7 @@
#include "browser/api/atom_api_crash_reporter.h"
#include "browser/crash_reporter.h"
#include "common/v8_conversions.h"
#include "vendor/node/src/node.h"
#include "vendor/node/src/node_internals.h"
@@ -14,22 +15,20 @@ namespace api {
// static
v8::Handle<v8::Value> CrashReporter::SetCompanyName(const v8::Arguments &args) {
std::string name(*v8::String::Utf8Value(args[0]));
crash_reporter::CrashReporter::SetCompanyName(name);
crash_reporter::CrashReporter::SetCompanyName(FromV8Value(args[0]));
return v8::Undefined();
}
// static
v8::Handle<v8::Value> CrashReporter::SetSubmissionURL(
const v8::Arguments &args) {
std::string url(*v8::String::Utf8Value(args[0]));
crash_reporter::CrashReporter::SetSubmissionURL(url);
crash_reporter::CrashReporter::SetSubmissionURL(FromV8Value(args[0]));
return v8::Undefined();
}
// static
v8::Handle<v8::Value> CrashReporter::SetAutoSubmit(const v8::Arguments &args) {
crash_reporter::CrashReporter::SetAutoSubmit(args[0]->BooleanValue());
crash_reporter::CrashReporter::SetAutoSubmit(FromV8Value(args[0]));
return v8::Undefined();
}

View File

@@ -4,14 +4,14 @@
#include "browser/api/atom_api_dialog.h"
#include <string>
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "browser/api/atom_api_window.h"
#include "browser/file_dialog.h"
#include "browser/message_box.h"
#include "base/bind.h"
#include "browser/native_window.h"
#include "browser/ui/file_dialog.h"
#include "browser/ui/message_box.h"
#include "common/v8_conversions.h"
#include "vendor/node/src/node_internals.h"
using node::node_isolate;
namespace atom {
@@ -19,14 +19,24 @@ namespace api {
namespace {
base::FilePath V8ValueToFilePath(v8::Handle<v8::Value> path) {
std::string path_string(*v8::String::Utf8Value(path));
return base::FilePath::FromUTF8Unsafe(path_string);
template<typename T>
void CallV8Function(v8::Persistent<v8::Function> callback, T arg) {
DCHECK(!callback.IsEmpty());
v8::HandleScope scope;
v8::Handle<v8::Value> value = ToV8Value(arg);
callback->Call(callback, 1, &value);
callback.Dispose(node_isolate);
}
v8::Handle<v8::Value> FilePathToV8Value(const base::FilePath path) {
std::string path_string(path.AsUTF8Unsafe());
return v8::String::New(path_string.data(), path_string.size());
template<typename T>
void CallV8Function2(v8::Persistent<v8::Function> callback,
bool result,
T arg) {
if (result)
return CallV8Function<T>(callback, arg);
else
return CallV8Function<void*>(callback, NULL);
}
void Initialize(v8::Handle<v8::Object> target) {
@@ -42,84 +52,103 @@ void Initialize(v8::Handle<v8::Object> target) {
v8::Handle<v8::Value> ShowMessageBox(const v8::Arguments &args) {
v8::HandleScope scope;
if (!args[0]->IsNumber() || // type
!args[1]->IsArray() || // buttons
!args[2]->IsString() || // title
!args[3]->IsString() || // message
!args[4]->IsString()) // detail
int type;
std::vector<std::string> buttons;
std::string title, message, detail;
if (!FromV8Arguments(args, &type, &buttons, &title, &message, &detail))
return node::ThrowTypeError("Bad argument");
NativeWindow* native_window = NULL;
if (args[5]->IsObject()) {
Window* window = Window::Unwrap<Window>(args[5]->ToObject());
if (!window || !window->window())
return node::ThrowError("Invalid window");
NativeWindow* native_window = FromV8Value(args[5]);
v8::Persistent<v8::Function> callback = FromV8Value(args[6]);
native_window = window->window();
if (callback.IsEmpty()) {
int chosen = atom::ShowMessageBox(
native_window,
(MessageBoxType)type,
buttons,
title,
message,
detail);
return scope.Close(v8::Integer::New(chosen));
} else {
atom::ShowMessageBox(
native_window,
(MessageBoxType)type,
buttons,
title,
message,
detail,
base::Bind(&CallV8Function<int>, callback));
return v8::Undefined();
}
MessageBoxType type = (MessageBoxType)(args[0]->IntegerValue());
std::vector<std::string> buttons;
v8::Handle<v8::Array> v8_buttons = v8::Handle<v8::Array>::Cast(args[1]);
for (uint32_t i = 0; i < v8_buttons->Length(); ++i)
buttons.push_back(*v8::String::Utf8Value(v8_buttons->Get(i)));
std::string title(*v8::String::Utf8Value(args[2]));
std::string message(*v8::String::Utf8Value(args[3]));
std::string detail(*v8::String::Utf8Value(args[4]));
int chosen = atom::ShowMessageBox(
native_window, type, buttons, title, message, detail);
return scope.Close(v8::Integer::New(chosen));
}
v8::Handle<v8::Value> ShowOpenDialog(const v8::Arguments &args) {
v8::HandleScope scope;
if (!args[0]->IsString() || // title
!args[1]->IsString() || // default_path
!args[2]->IsNumber()) // properties
std::string title;
base::FilePath default_path;
int properties;
if (!FromV8Arguments(args, &title, &default_path, &properties))
return node::ThrowTypeError("Bad argument");
std::string title(*v8::String::Utf8Value(args[0]));
base::FilePath default_path(V8ValueToFilePath(args[1]));
int properties = args[2]->IntegerValue();
NativeWindow* native_window = FromV8Value(args[3]);
v8::Persistent<v8::Function> callback = FromV8Value(args[4]);
std::vector<base::FilePath> paths;
if (!file_dialog::ShowOpenDialog(title, default_path, properties, &paths))
if (callback.IsEmpty()) {
std::vector<base::FilePath> paths;
if (!file_dialog::ShowOpenDialog(native_window,
title,
default_path,
properties,
&paths))
return v8::Undefined();
v8::Handle<v8::Array> result = v8::Array::New(paths.size());
for (size_t i = 0; i < paths.size(); ++i)
result->Set(i, ToV8Value(paths[i]));
return scope.Close(result);
} else {
file_dialog::ShowOpenDialog(
native_window,
title,
default_path,
properties,
base::Bind(&CallV8Function2<const std::vector<base::FilePath>&>,
callback));
return v8::Undefined();
v8::Handle<v8::Array> result = v8::Array::New(paths.size());
for (size_t i = 0; i < paths.size(); ++i)
result->Set(i, FilePathToV8Value(paths[i]));
return scope.Close(result);
}
}
v8::Handle<v8::Value> ShowSaveDialog(const v8::Arguments &args) {
v8::HandleScope scope;
if (!args[0]->IsObject() || // window
!args[1]->IsString() || // title
!args[2]->IsString()) // default_path
std::string title;
base::FilePath default_path;
if (!FromV8Arguments(args, &title, &default_path))
return node::ThrowTypeError("Bad argument");
Window* window = Window::Unwrap<Window>(args[0]->ToObject());
if (!window || !window->window())
return node::ThrowError("Invalid window");
NativeWindow* native_window = FromV8Value(args[2]);
v8::Persistent<v8::Function> callback = FromV8Value(args[3]);
std::string title(*v8::String::Utf8Value(args[1]));
base::FilePath default_path(V8ValueToFilePath(args[2]));
if (callback.IsEmpty()) {
base::FilePath path;
if (!file_dialog::ShowSaveDialog(native_window,
title,
default_path,
&path))
return v8::Undefined();
base::FilePath path;
if (!file_dialog::ShowSaveDialog(window->window(),
title,
default_path,
&path))
return scope.Close(ToV8Value(path));
} else {
file_dialog::ShowSaveDialog(
native_window,
title,
default_path,
base::Bind(&CallV8Function2<const base::FilePath&>, callback));
return v8::Undefined();
return scope.Close(FilePathToV8Value(path));
}
}
} // namespace api

View File

@@ -4,6 +4,10 @@
#include "browser/api/atom_api_event.h"
#include "browser/native_window.h"
#include "common/api/api_messages.h"
#include "common/v8_conversions.h"
using node::node_isolate;
namespace atom {
@@ -13,10 +17,14 @@ namespace api {
v8::Persistent<v8::FunctionTemplate> Event::constructor_template_;
Event::Event()
: prevent_default_(false) {
: sender_(NULL),
message_(NULL),
prevent_default_(false) {
}
Event::~Event() {
if (sender_ != NULL)
sender_->RemoveObserver(this);
}
// static
@@ -31,6 +39,8 @@ v8::Handle<v8::Object> Event::CreateV8Object() {
constructor_template_->SetClassName(v8::String::NewSymbol("Event"));
NODE_SET_PROTOTYPE_METHOD(t, "preventDefault", PreventDefault);
NODE_SET_PROTOTYPE_METHOD(t, "sendReply", SendReply);
NODE_SET_PROTOTYPE_METHOD(t, "destroy", Destroy);
}
v8::Handle<v8::Object> v8_event =
@@ -39,14 +49,30 @@ v8::Handle<v8::Object> Event::CreateV8Object() {
return scope.Close(v8_event);
}
v8::Handle<v8::Value> Event::New(const v8::Arguments &args) {
void Event::SetSenderAndMessage(NativeWindow* sender, IPC::Message* message) {
DCHECK(!sender_);
DCHECK(!message_);
sender_ = sender;
message_ = message;
sender_->AddObserver(this);
}
void Event::OnWindowClosed() {
sender_ = NULL;
message_ = NULL;
}
// static
v8::Handle<v8::Value> Event::New(const v8::Arguments& args) {
Event* event = new Event;
event->Wrap(args.This());
return args.This();
}
v8::Handle<v8::Value> Event::PreventDefault(const v8::Arguments &args) {
// static
v8::Handle<v8::Value> Event::PreventDefault(const v8::Arguments& args) {
Event* event = Unwrap<Event>(args.This());
if (event == NULL)
return node::ThrowError("Event is already destroyed");
@@ -56,6 +82,31 @@ v8::Handle<v8::Value> Event::PreventDefault(const v8::Arguments &args) {
return v8::Undefined();
}
// static
v8::Handle<v8::Value> Event::SendReply(const v8::Arguments& args) {
Event* event = Unwrap<Event>(args.This());
if (event == NULL)
return node::ThrowError("Event is already destroyed");
if (event->message_ == NULL || event->sender_ == NULL)
return node::ThrowError("Can only send reply to synchronous events");
string16 json = FromV8Value(args[0]);
AtomViewHostMsg_Message_Sync::WriteReplyParams(event->message_, json);
event->sender_->Send(event->message_);
delete event;
return v8::Undefined();
}
// static
v8::Handle<v8::Value> Event::Destroy(const v8::Arguments& args) {
delete Unwrap<Event>(args.This());
return v8::Undefined();
}
} // namespace api
} // namespace atom

View File

@@ -6,19 +6,32 @@
#define ATOM_BROWSER_ATOM_API_EVENT_H_
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/string16.h"
#include "browser/native_window_observer.h"
#include "vendor/node/src/node_object_wrap.h"
namespace IPC {
class Message;
}
namespace atom {
class NativeWindow;
namespace api {
class Event : public node::ObjectWrap {
class Event : public node::ObjectWrap,
public NativeWindowObserver {
public:
virtual ~Event();
// Create a V8 Event object.
static v8::Handle<v8::Object> CreateV8Object();
// Pass the sender and message to be replied.
void SetSenderAndMessage(NativeWindow* sender, IPC::Message* message);
// Accessor to return handle_, this follows Google C++ Style.
v8::Persistent<v8::Object>& handle() { return handle_; }
@@ -28,12 +41,22 @@ class Event : public node::ObjectWrap {
protected:
Event();
// NativeWindowObserver implementations:
virtual void OnWindowClosed() OVERRIDE;
private:
static v8::Handle<v8::Value> New(const v8::Arguments &args);
static v8::Handle<v8::Value> PreventDefault(const v8::Arguments &args);
static v8::Handle<v8::Value> New(const v8::Arguments& args);
static v8::Handle<v8::Value> PreventDefault(const v8::Arguments& args);
static v8::Handle<v8::Value> SendReply(const v8::Arguments& args);
static v8::Handle<v8::Value> Destroy(const v8::Arguments& args);
static v8::Persistent<v8::FunctionTemplate> constructor_template_;
// Replyer for the synchronous messages.
NativeWindow* sender_;
IPC::Message* message_;
bool prevent_default_;
DISALLOW_COPY_AND_ASSIGN(Event);

View File

@@ -4,8 +4,8 @@
#include "browser/api/atom_api_menu.h"
#include "browser/accelerator_util.h"
#include "browser/api/atom_api_window.h"
#include "browser/ui/accelerator_util.h"
#include "common/v8_conversions.h"
#define UNWRAP_MEMNU_AND_CHECK \
Menu* self = ObjectWrap::Unwrap<Menu>(args.This()); \
@@ -18,17 +18,6 @@ namespace api {
namespace {
// Converts a V8 value to a string16.
string16 V8ValueToUTF16(v8::Handle<v8::Value> value) {
v8::String::Value s(value);
return string16(reinterpret_cast<const char16*>(*s), s.length());
}
// Converts string16 to V8 String.
v8::Handle<v8::Value> UTF16ToV8Value(const string16& s) {
return v8::String::New(reinterpret_cast<const uint16_t*>(s.data()), s.size());
}
// Call method of delegate object.
v8::Handle<v8::Value> CallDelegate(v8::Handle<v8::Value> default_value,
v8::Handle<v8::Object> menu,
@@ -93,7 +82,7 @@ bool Menu::GetAcceleratorForCommandId(int command_id,
"getAcceleratorForCommandId",
command_id);
if (shortcut->IsString()) {
std::string shortcut_str(*v8::String::Utf8Value(shortcut));
std::string shortcut_str = FromV8Value(shortcut);
return accelerator_util::StringToAccelerator(shortcut_str, accelerator);
}
@@ -110,18 +99,18 @@ bool Menu::IsItemForCommandIdDynamic(int command_id) const {
string16 Menu::GetLabelForCommandId(int command_id) const {
v8::HandleScope scope;
return V8ValueToUTF16(CallDelegate(v8::False(),
handle(),
"getLabelForCommandId",
command_id));
return FromV8Value(CallDelegate(v8::False(),
handle(),
"getLabelForCommandId",
command_id));
}
string16 Menu::GetSublabelForCommandId(int command_id) const {
v8::HandleScope scope;
return V8ValueToUTF16(CallDelegate(v8::False(),
handle(),
"getSubLabelForCommandId",
command_id));
return FromV8Value(CallDelegate(v8::False(),
handle(),
"getSubLabelForCommandId",
command_id));
}
void Menu::ExecuteCommand(int command_id, int event_flags) {
@@ -145,16 +134,15 @@ v8::Handle<v8::Value> Menu::New(const v8::Arguments &args) {
v8::Handle<v8::Value> Menu::InsertItem(const v8::Arguments &args) {
UNWRAP_MEMNU_AND_CHECK;
if (!args[0]->IsNumber() || !args[1]->IsNumber() || !args[2]->IsString())
int index, command_id;
string16 label;
if (!FromV8Arguments(args, &index, &command_id, &label))
return node::ThrowTypeError("Bad argument");
int index = args[0]->IntegerValue();
if (index < 0)
self->model_->AddItem(args[1]->IntegerValue(), V8ValueToUTF16(args[2]));
self->model_->AddItem(command_id, label);
else
self->model_->InsertItemAt(
index, args[1]->IntegerValue(), V8ValueToUTF16(args[2]));
self->model_->InsertItemAt(index, command_id, label);
return v8::Undefined();
}
@@ -163,16 +151,15 @@ v8::Handle<v8::Value> Menu::InsertItem(const v8::Arguments &args) {
v8::Handle<v8::Value> Menu::InsertCheckItem(const v8::Arguments &args) {
UNWRAP_MEMNU_AND_CHECK;
if (!args[0]->IsNumber() || !args[1]->IsNumber() || !args[2]->IsString())
int index, command_id;
string16 label;
if (!FromV8Arguments(args, &index, &command_id, &label))
return node::ThrowTypeError("Bad argument");
int index = args[0]->IntegerValue();
int command_id = args[1]->IntegerValue();
if (index < 0)
self->model_->AddCheckItem(command_id, V8ValueToUTF16(args[2]));
self->model_->AddCheckItem(command_id, label);
else
self->model_->InsertCheckItemAt(index, command_id, V8ValueToUTF16(args[2]));
self->model_->InsertCheckItemAt(index, command_id, label);
return v8::Undefined();
}
@@ -181,21 +168,15 @@ v8::Handle<v8::Value> Menu::InsertCheckItem(const v8::Arguments &args) {
v8::Handle<v8::Value> Menu::InsertRadioItem(const v8::Arguments &args) {
UNWRAP_MEMNU_AND_CHECK;
if (!args[0]->IsNumber() ||
!args[1]->IsNumber() ||
!args[2]->IsString() ||
!args[3]->IsNumber())
int index, command_id, group_id;
string16 label;
if (!FromV8Arguments(args, &index, &command_id, &label, &group_id))
return node::ThrowTypeError("Bad argument");
int index = args[0]->IntegerValue();
int command_id = args[1]->IntegerValue();
int group_id = args[3]->IntegerValue();
if (index < 0)
self->model_->AddRadioItem(command_id, V8ValueToUTF16(args[2]), group_id);
self->model_->AddRadioItem(command_id, label, group_id);
else
self->model_->InsertRadioItemAt(
index, command_id, V8ValueToUTF16(args[2]), group_id);
self->model_->InsertRadioItemAt(index, command_id, label, group_id);
return v8::Undefined();
}
@@ -204,11 +185,10 @@ v8::Handle<v8::Value> Menu::InsertRadioItem(const v8::Arguments &args) {
v8::Handle<v8::Value> Menu::InsertSeparator(const v8::Arguments &args) {
UNWRAP_MEMNU_AND_CHECK;
if (!args[0]->IsNumber())
int index;
if (!FromV8Arguments(args, &index))
return node::ThrowTypeError("Bad argument");
int index = args[0]->IntegerValue();
if (index < 0)
self->model_->AddSeparator(ui::NORMAL_SEPARATOR);
else
@@ -221,25 +201,20 @@ v8::Handle<v8::Value> Menu::InsertSeparator(const v8::Arguments &args) {
v8::Handle<v8::Value> Menu::InsertSubMenu(const v8::Arguments &args) {
UNWRAP_MEMNU_AND_CHECK;
if (!args[0]->IsNumber() ||
!args[1]->IsNumber() ||
!args[2]->IsString() ||
!args[3]->IsObject())
int index, command_id;
string16 label;
if (!FromV8Arguments(args, &index, &command_id, &label))
return node::ThrowTypeError("Bad argument");
Menu* submenu = ObjectWrap::Unwrap<Menu>(args[3]->ToObject());
if (!submenu)
return node::ThrowTypeError("The submenu is already destroyed");
int index = args[0]->IntegerValue();
int command_id = args[1]->IntegerValue();
if (index < 0)
self->model_->AddSubMenu(
command_id, V8ValueToUTF16(args[2]), submenu->model_.get());
self->model_->AddSubMenu(command_id, label, submenu->model_.get());
else
self->model_->InsertSubMenuAt(
index, command_id, V8ValueToUTF16(args[2]), submenu->model_.get());
index, command_id, label, submenu->model_.get());
return v8::Undefined();
}
@@ -248,7 +223,9 @@ v8::Handle<v8::Value> Menu::InsertSubMenu(const v8::Arguments &args) {
v8::Handle<v8::Value> Menu::SetIcon(const v8::Arguments &args) {
UNWRAP_MEMNU_AND_CHECK;
if (!args[0]->IsNumber() || !args[1]->IsString())
int index;
base::FilePath path;
if (!FromV8Arguments(args, &index, &path))
return node::ThrowTypeError("Bad argument");
// FIXME use webkit_glue's image decoder here.
@@ -260,10 +237,12 @@ v8::Handle<v8::Value> Menu::SetIcon(const v8::Arguments &args) {
v8::Handle<v8::Value> Menu::SetSublabel(const v8::Arguments &args) {
UNWRAP_MEMNU_AND_CHECK;
if (!args[0]->IsNumber() || !args[1]->IsString())
int index;
string16 label;
if (!FromV8Arguments(args, &index, &label))
return node::ThrowTypeError("Bad argument");
self->model_->SetSublabel(args[0]->IntegerValue(), V8ValueToUTF16(args[1]));
self->model_->SetSublabel(index, label);
return v8::Undefined();
}
@@ -301,14 +280,14 @@ v8::Handle<v8::Value> Menu::GetCommandIdAt(const v8::Arguments &args) {
v8::Handle<v8::Value> Menu::GetLabelAt(const v8::Arguments &args) {
UNWRAP_MEMNU_AND_CHECK;
int index = args[0]->IntegerValue();
return UTF16ToV8Value(self->model_->GetLabelAt(index));
return ToV8Value(self->model_->GetLabelAt(index));
}
// static
v8::Handle<v8::Value> Menu::GetSublabelAt(const v8::Arguments &args) {
UNWRAP_MEMNU_AND_CHECK;
int index = args[0]->IntegerValue();
return UTF16ToV8Value(self->model_->GetSublabelAt(index));
return ToV8Value(self->model_->GetSublabelAt(index));
}
// static
@@ -334,11 +313,11 @@ v8::Handle<v8::Value> Menu::IsVisibleAt(const v8::Arguments &args) {
v8::Handle<v8::Value> Menu::Popup(const v8::Arguments &args) {
UNWRAP_MEMNU_AND_CHECK;
Window* window = Window::Unwrap<Window>(args[0]->ToObject());
if (!window)
return node::ThrowTypeError("Invalid window");
atom::NativeWindow* window;
if (!FromV8Arguments(args, &window))
return node::ThrowTypeError("Bad argument");
self->Popup(window->window());
self->Popup(window);
return v8::Undefined();
}
@@ -372,6 +351,10 @@ void Menu::Initialize(v8::Handle<v8::Object> target) {
NODE_SET_PROTOTYPE_METHOD(t, "popup", Popup);
#if defined(OS_WIN)
NODE_SET_PROTOTYPE_METHOD(t, "attachToWindow", AttachToWindow);
#endif
target->Set(v8::String::NewSymbol("Menu"), t->GetFunction());
#if defined(OS_MACOSX)

View File

@@ -68,7 +68,9 @@ class Menu : public EventEmitter,
static v8::Handle<v8::Value> Popup(const v8::Arguments &args);
#if defined(OS_MACOSX)
#if defined(OS_WIN)
static v8::Handle<v8::Value> AttachToWindow(const v8::Arguments &args);
#elif defined(OS_MACOSX)
static v8::Handle<v8::Value> SetApplicationMenu(const v8::Arguments &args);
static v8::Handle<v8::Value> SendActionToFirstResponder(
const v8::Arguments &args);

View File

@@ -7,7 +7,7 @@
#include "browser/api/atom_api_menu.h"
#import "chrome/browser/ui/cocoa/menu_controller.h"
#import "browser/ui/atom_menu_controller_mac.h"
namespace atom {
@@ -21,16 +21,11 @@ class MenuMac : public Menu {
protected:
virtual void Popup(NativeWindow* window) OVERRIDE;
scoped_nsobject<MenuController> menu_controller_;
scoped_nsobject<AtomMenuController> menu_controller_;
private:
friend class Menu;
// The MenuController doesn't set title for menus, however it's required by
// application menu to show submenus correctly, fix it by iterating all
// submenus and set their titles.
static void FixMenuTitles(NSMenu* menu);
// Fake sending an action from the application menu.
static void SendActionToFirstResponder(const std::string& action);

View File

@@ -8,6 +8,7 @@
#include "base/mac/scoped_sending_event.h"
#include "base/strings/sys_string_conversions.h"
#include "browser/native_window.h"
#include "common/v8_conversions.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
@@ -23,9 +24,8 @@ MenuMac::~MenuMac() {
}
void MenuMac::Popup(NativeWindow* native_window) {
scoped_nsobject<MenuController> menu_controller(
[[MenuController alloc] initWithModel:model_.get()
useWithPopUpButtonCell:NO]);
scoped_nsobject<AtomMenuController> menu_controller(
[[AtomMenuController alloc] initWithModel:model_.get()]);
NSWindow* window = native_window->GetNativeWindow();
content::WebContents* web_contents = native_window->GetWebContents();
@@ -62,22 +62,6 @@ void MenuMac::Popup(NativeWindow* native_window) {
}
}
// static
void MenuMac::FixMenuTitles(NSMenu* menu) {
int size = [menu numberOfItems];
for (int i = 0; i < size; ++i) {
NSMenuItem* item = [menu itemAtIndex:i];
if ([item hasSubmenu]) {
NSString* title = [item title];
NSMenu* submenu = [item submenu];
[submenu setTitle:title];
if ([title isEqualToString:@"Window"] && [submenu numberOfItems] > 0)
[NSApp setWindowsMenu:submenu];
}
}
}
// static
void MenuMac::SendActionToFirstResponder(const std::string& action) {
SEL selector = NSSelectorFromString(base::SysUTF8ToNSString(action));
@@ -97,10 +81,8 @@ v8::Handle<v8::Value> Menu::SetApplicationMenu(const v8::Arguments &args) {
if (!menu)
return node::ThrowError("Menu is destroyed");
scoped_nsobject<MenuController> menu_controller(
[[MenuController alloc] initWithModel:menu->model_.get()
useWithPopUpButtonCell:NO]);
MenuMac::FixMenuTitles([menu_controller menu]);
scoped_nsobject<AtomMenuController> menu_controller(
[[AtomMenuController alloc] initWithModel:menu->model_.get()]);
[NSApp setMainMenu:[menu_controller menu]];
// Ensure the menu_controller_ is destroyed after main menu is set.
@@ -114,10 +96,10 @@ v8::Handle<v8::Value> Menu::SendActionToFirstResponder(
const v8::Arguments &args) {
v8::HandleScope scope;
if (!args[0]->IsString())
std::string action;
if (!FromV8Arguments(args, &action))
return node::ThrowTypeError("Bad argument");
std::string action(*v8::String::Utf8Value(args[0]));
MenuMac::SendActionToFirstResponder(action);
return v8::Undefined();

View File

@@ -4,6 +4,12 @@
#include "browser/api/atom_api_menu_win.h"
#include "browser/native_window_win.h"
#include "browser/ui/win/menu_2.h"
#include "common/v8_conversions.h"
#include "ui/gfx/point.h"
#include "ui/gfx/screen.h"
namespace atom {
namespace api {
@@ -16,6 +22,26 @@ MenuWin::~MenuWin() {
}
void MenuWin::Popup(NativeWindow* native_window) {
gfx::Point cursor = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint();
menu_.reset(new atom::Menu2(model_.get()));
menu_->RunContextMenuAt(cursor);
}
// static
v8::Handle<v8::Value> Menu::AttachToWindow(const v8::Arguments& args) {
v8::HandleScope scope;
Menu* self = ObjectWrap::Unwrap<Menu>(args.This());
if (self == NULL)
return node::ThrowError("Menu is already destroyed");
NativeWindow* native_window;
if (!FromV8Arguments(args, &native_window))
return node::ThrowTypeError("Bad argument");
static_cast<NativeWindowWin*>(native_window)->SetMenu(self->model_.get());
return v8::Undefined();
}
// static

View File

@@ -9,6 +9,8 @@
namespace atom {
class Menu2;
namespace api {
class MenuWin : public Menu {
@@ -20,6 +22,8 @@ class MenuWin : public Menu {
virtual void Popup(NativeWindow* window) OVERRIDE;
private:
scoped_ptr<atom::Menu2> menu_;
DISALLOW_COPY_AND_ASSIGN(MenuWin);
};

View File

@@ -0,0 +1,66 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/api/atom_api_power_monitor.h"
#include "base/power_monitor/power_monitor.h"
namespace atom {
namespace api {
PowerMonitor::PowerMonitor(v8::Handle<v8::Object> wrapper)
: EventEmitter(wrapper) {
base::PowerMonitor::Get()->AddObserver(this);
}
PowerMonitor::~PowerMonitor() {
base::PowerMonitor::Get()->RemoveObserver(this);
}
void PowerMonitor::OnPowerStateChange(bool on_battery_power) {
if (on_battery_power)
Emit("on-battery");
else
Emit("on-ac");
}
void PowerMonitor::OnSuspend() {
Emit("suspend");
}
void PowerMonitor::OnResume() {
Emit("resume");
}
// static
v8::Handle<v8::Value> PowerMonitor::New(const v8::Arguments& args) {
v8::HandleScope scope;
if (!args.IsConstructCall())
return node::ThrowError("Require constructor call");
new PowerMonitor(args.This());
return args.This();
}
// static
void PowerMonitor::Initialize(v8::Handle<v8::Object> target) {
v8::HandleScope scope;
v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(
PowerMonitor::New);
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(v8::String::NewSymbol("PowerMonitor"));
target->Set(v8::String::NewSymbol("PowerMonitor"), t->GetFunction());
}
} // namespace api
} // namespace atom
NODE_MODULE(atom_browser_power_monitor, atom::api::PowerMonitor::Initialize)

View File

@@ -0,0 +1,41 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_API_ATOM_API_POWER_MONITOR_H_
#define ATOM_BROWSER_API_ATOM_API_POWER_MONITOR_H_
#include "browser/api/atom_api_event_emitter.h"
#include "base/compiler_specific.h"
#include "base/power_monitor/power_observer.h"
namespace atom {
namespace api {
class PowerMonitor : public EventEmitter,
public base::PowerObserver {
public:
virtual ~PowerMonitor();
static void Initialize(v8::Handle<v8::Object> target);
protected:
explicit PowerMonitor(v8::Handle<v8::Object> wrapper);
virtual void OnPowerStateChange(bool on_battery_power) OVERRIDE;
virtual void OnSuspend() OVERRIDE;
virtual void OnResume() OVERRIDE;
private:
static v8::Handle<v8::Value> New(const v8::Arguments &args);
DISALLOW_COPY_AND_ASSIGN(PowerMonitor);
};
} // namespace api
} // namespace atom
#endif // ATOM_BROWSER_API_ATOM_API_POWER_MONITOR_H_

View File

@@ -0,0 +1,378 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/api/atom_api_protocol.h"
#include "base/stl_util.h"
#include "browser/atom_browser_context.h"
#include "browser/net/adapter_request_job.h"
#include "browser/net/atom_url_request_context_getter.h"
#include "browser/net/atom_url_request_job_factory.h"
#include "common/v8_conversions.h"
#include "content/public/browser/browser_thread.h"
#include "net/url_request/url_request_context.h"
#include "vendor/node/src/node.h"
#include "vendor/node/src/node_internals.h"
namespace atom {
namespace api {
typedef net::URLRequestJobFactory::ProtocolHandler ProtocolHandler;
namespace {
// The protocol module object.
v8::Persistent<v8::Object> g_protocol_object;
// Registered protocol handlers.
typedef std::map<std::string, v8::Persistent<v8::Function>> HandlersMap;
static HandlersMap g_handlers;
static const char* kEarlyUseProtocolError = "This method can only be used"
"after the application has finished launching.";
// Emit an event for the protocol module.
void EmitEventInUI(const std::string& event, const std::string& parameter) {
v8::HandleScope scope;
v8::Handle<v8::Value> argv[] = {
ToV8Value(event),
ToV8Value(parameter),
};
node::MakeCallback(g_protocol_object, "emit", arraysize(argv), argv);
}
// Convert the URLRequest object to V8 object.
v8::Handle<v8::Object> ConvertURLRequestToV8Object(
const net::URLRequest* request) {
v8::Local<v8::Object> obj = v8::Object::New();
obj->Set(v8::String::New("method"),
v8::String::New(request->method().c_str()));
obj->Set(v8::String::New("url"),
v8::String::New(request->url().spec().c_str()));
obj->Set(v8::String::New("referrer"),
v8::String::New(request->referrer().c_str()));
return obj;
}
// Get the job factory.
AtomURLRequestJobFactory* GetRequestJobFactory() {
return AtomBrowserContext::Get()->url_request_context_getter()->job_factory();
}
class CustomProtocolRequestJob : public AdapterRequestJob {
public:
CustomProtocolRequestJob(ProtocolHandler* protocol_handler,
net::URLRequest* request,
net::NetworkDelegate* network_delegate)
: AdapterRequestJob(protocol_handler, request, network_delegate) {
}
// AdapterRequestJob:
virtual void GetJobTypeInUI() OVERRIDE {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
// Call the JS handler.
v8::HandleScope scope;
v8::Handle<v8::Value> argv[] = {
ConvertURLRequestToV8Object(request()),
};
v8::Handle<v8::Value> result = g_handlers[request()->url().scheme()]->Call(
v8::Context::GetCurrent()->Global(), arraysize(argv), argv);
// Determine the type of the job we are going to create.
if (result->IsString()) {
std::string data = FromV8Value(result);
content::BrowserThread::PostTask(
content::BrowserThread::IO,
FROM_HERE,
base::Bind(&AdapterRequestJob::CreateStringJobAndStart,
GetWeakPtr(),
"text/plain",
"UTF-8",
data));
return;
} else if (result->IsObject()) {
v8::Handle<v8::Object> obj = result->ToObject();
std::string name = FromV8Value(obj->GetConstructorName());
if (name == "RequestStringJob") {
std::string mime_type = FromV8Value(obj->Get(
v8::String::New("mimeType")));
std::string charset = FromV8Value(obj->Get(v8::String::New("charset")));
std::string data = FromV8Value(obj->Get(v8::String::New("data")));
content::BrowserThread::PostTask(
content::BrowserThread::IO,
FROM_HERE,
base::Bind(&AdapterRequestJob::CreateStringJobAndStart,
GetWeakPtr(),
mime_type,
charset,
data));
return;
} else if (name == "RequestFileJob") {
base::FilePath path = FromV8Value(obj->Get(v8::String::New("path")));
content::BrowserThread::PostTask(
content::BrowserThread::IO,
FROM_HERE,
base::Bind(&AdapterRequestJob::CreateFileJobAndStart,
GetWeakPtr(),
path));
return;
}
}
// Try the default protocol handler if we have.
if (default_protocol_handler()) {
content::BrowserThread::PostTask(
content::BrowserThread::IO,
FROM_HERE,
base::Bind(&AdapterRequestJob::CreateJobFromProtocolHandlerAndStart,
GetWeakPtr()));
return;
}
// Fallback to the not implemented error.
content::BrowserThread::PostTask(
content::BrowserThread::IO,
FROM_HERE,
base::Bind(&AdapterRequestJob::CreateErrorJobAndStart,
GetWeakPtr(),
net::ERR_NOT_IMPLEMENTED));
}
};
// Always return the same CustomProtocolRequestJob for all requests, because
// the content API needs the ProtocolHandler to return a job immediately, and
// getting the real job from the JS requires asynchronous calls, so we have
// to create an adapter job first.
// Users can also pass an extra ProtocolHandler as the fallback one when
// registered handler doesn't want to deal with the request.
class CustomProtocolHandler : public ProtocolHandler {
public:
explicit CustomProtocolHandler(ProtocolHandler* protocol_handler = NULL)
: protocol_handler_(protocol_handler) {
}
virtual net::URLRequestJob* MaybeCreateJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const OVERRIDE {
return new CustomProtocolRequestJob(protocol_handler_.get(),
request,
network_delegate);
}
ProtocolHandler* ReleaseDefaultProtocolHandler() {
return protocol_handler_.release();
}
ProtocolHandler* original_handler() { return protocol_handler_.get(); }
private:
scoped_ptr<ProtocolHandler> protocol_handler_;
DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler);
};
} // namespace
// static
v8::Handle<v8::Value> Protocol::RegisterProtocol(const v8::Arguments& args) {
std::string scheme;
v8::Persistent<v8::Function> callback;
if (!FromV8Arguments(args, &scheme, &callback))
return node::ThrowTypeError("Bad argument");
if (g_handlers.find(scheme) != g_handlers.end() ||
net::URLRequest::IsHandledProtocol(scheme))
return node::ThrowError("The scheme is already registered");
if (AtomBrowserContext::Get()->url_request_context_getter() == NULL)
return node::ThrowError(kEarlyUseProtocolError);
// Store the handler in a map.
g_handlers[scheme] = callback;
content::BrowserThread::PostTask(content::BrowserThread::IO,
FROM_HERE,
base::Bind(&RegisterProtocolInIO, scheme));
return v8::Undefined();
}
// static
v8::Handle<v8::Value> Protocol::UnregisterProtocol(const v8::Arguments& args) {
std::string scheme;
if (!FromV8Arguments(args, &scheme))
return node::ThrowTypeError("Bad argument");
if (AtomBrowserContext::Get()->url_request_context_getter() == NULL)
return node::ThrowError(kEarlyUseProtocolError);
// Erase the handler from map.
HandlersMap::iterator it(g_handlers.find(scheme));
if (it == g_handlers.end())
return node::ThrowError("The scheme has not been registered");
g_handlers.erase(it);
content::BrowserThread::PostTask(content::BrowserThread::IO,
FROM_HERE,
base::Bind(&UnregisterProtocolInIO, scheme));
return v8::Undefined();
}
// static
v8::Handle<v8::Value> Protocol::IsHandledProtocol(const v8::Arguments& args) {
return ToV8Value(net::URLRequest::IsHandledProtocol(FromV8Value(args[0])));
}
// static
v8::Handle<v8::Value> Protocol::InterceptProtocol(const v8::Arguments& args) {
std::string scheme;
v8::Persistent<v8::Function> callback;
if (!FromV8Arguments(args, &scheme, &callback))
return node::ThrowTypeError("Bad argument");
if (!GetRequestJobFactory()->HasProtocolHandler(scheme))
return node::ThrowError("Cannot intercept procotol");
if (ContainsKey(g_handlers, scheme))
return node::ThrowError("Cannot intercept custom procotols");
if (AtomBrowserContext::Get()->url_request_context_getter() == NULL)
return node::ThrowError(kEarlyUseProtocolError);
// Store the handler in a map.
g_handlers[scheme] = callback;
content::BrowserThread::PostTask(content::BrowserThread::IO,
FROM_HERE,
base::Bind(&InterceptProtocolInIO, scheme));
return v8::Undefined();
}
// static
v8::Handle<v8::Value> Protocol::UninterceptProtocol(const v8::Arguments& args) {
std::string scheme;
if (!FromV8Arguments(args, &scheme))
return node::ThrowTypeError("Bad argument");
if (AtomBrowserContext::Get()->url_request_context_getter() == NULL)
return node::ThrowError(kEarlyUseProtocolError);
// Erase the handler from map.
HandlersMap::iterator it(g_handlers.find(scheme));
if (it == g_handlers.end())
return node::ThrowError("The scheme has not been registered");
g_handlers.erase(it);
content::BrowserThread::PostTask(content::BrowserThread::IO,
FROM_HERE,
base::Bind(&UninterceptProtocolInIO,
scheme));
return v8::Undefined();
}
// static
void Protocol::RegisterProtocolInIO(const std::string& scheme) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());
job_factory->SetProtocolHandler(scheme, new CustomProtocolHandler);
content::BrowserThread::PostTask(content::BrowserThread::UI,
FROM_HERE,
base::Bind(&EmitEventInUI,
"registered",
scheme));
}
// static
void Protocol::UnregisterProtocolInIO(const std::string& scheme) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());
job_factory->SetProtocolHandler(scheme, NULL);
content::BrowserThread::PostTask(content::BrowserThread::UI,
FROM_HERE,
base::Bind(&EmitEventInUI,
"unregistered",
scheme));
}
// static
void Protocol::InterceptProtocolInIO(const std::string& scheme) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());
ProtocolHandler* original_handler = job_factory->GetProtocolHandler(scheme);
if (original_handler == NULL) {
content::BrowserThread::PostTask(
content::BrowserThread::UI,
FROM_HERE,
base::Bind(&EmitEventInUI,
"error",
"There is no protocol handler to intercpet"));
return;
}
job_factory->ReplaceProtocol(scheme,
new CustomProtocolHandler(original_handler));
content::BrowserThread::PostTask(content::BrowserThread::UI,
FROM_HERE,
base::Bind(&EmitEventInUI,
"intercepted",
scheme));
}
// static
void Protocol::UninterceptProtocolInIO(const std::string& scheme) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());
// Check if the protocol handler is intercepted.
CustomProtocolHandler* handler = static_cast<CustomProtocolHandler*>(
job_factory->GetProtocolHandler(scheme));
if (handler->original_handler() == NULL) {
content::BrowserThread::PostTask(
content::BrowserThread::UI,
FROM_HERE,
base::Bind(&EmitEventInUI,
"error",
"The protocol is not intercpeted"));
return;
}
// Reset the protocol handler to the orignal one and delete current
// protocol handler.
ProtocolHandler* original_handler = handler->ReleaseDefaultProtocolHandler();
delete job_factory->ReplaceProtocol(scheme, original_handler);
content::BrowserThread::PostTask(content::BrowserThread::UI,
FROM_HERE,
base::Bind(&EmitEventInUI,
"unintercepted",
scheme));
}
// static
void Protocol::Initialize(v8::Handle<v8::Object> target) {
// Remember the protocol object, used for emitting event later.
g_protocol_object = v8::Persistent<v8::Object>::New(
node::node_isolate, target);
node::SetMethod(target, "registerProtocol", RegisterProtocol);
node::SetMethod(target, "unregisterProtocol", UnregisterProtocol);
node::SetMethod(target, "isHandledProtocol", IsHandledProtocol);
node::SetMethod(target, "interceptProtocol", InterceptProtocol);
node::SetMethod(target, "uninterceptProtocol", UninterceptProtocol);
}
} // namespace api
} // namespace atom
NODE_MODULE(atom_browser_protocol, atom::api::Protocol::Initialize)

View File

@@ -0,0 +1,43 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_API_ATOM_API_PROTOCOL_H_
#define ATOM_BROWSER_API_ATOM_API_PROTOCOL_H_
#include <string>
#include <map>
#include "base/basictypes.h"
#include "v8/include/v8.h"
namespace atom {
namespace api {
class Protocol {
public:
static void Initialize(v8::Handle<v8::Object> target);
private:
static v8::Handle<v8::Value> RegisterProtocol(const v8::Arguments& args);
static v8::Handle<v8::Value> UnregisterProtocol(const v8::Arguments& args);
static v8::Handle<v8::Value> IsHandledProtocol(const v8::Arguments& args);
static v8::Handle<v8::Value> InterceptProtocol(const v8::Arguments& args);
static v8::Handle<v8::Value> UninterceptProtocol(const v8::Arguments& args);
static void RegisterProtocolInIO(const std::string& scheme);
static void UnregisterProtocolInIO(const std::string& scheme);
static void InterceptProtocolInIO(const std::string& scheme);
static void UninterceptProtocolInIO(const std::string& scheme);
DISALLOW_IMPLICIT_CONSTRUCTORS(Protocol);
};
} // namespace api
} // namespace atom
#endif // ATOM_BROWSER_API_ATOM_API_PROTOCOL_H_

View File

@@ -6,6 +6,7 @@
#include "base/values.h"
#include "browser/native_window.h"
#include "common/v8_conversions.h"
#include "common/v8_value_converter_impl.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/web_contents.h"
@@ -13,6 +14,7 @@
#include "ui/gfx/point.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/size.h"
#include "vendor/node/src/node_buffer.h"
using content::V8ValueConverter;
using content::NavigationController;
@@ -27,15 +29,6 @@ namespace atom {
namespace api {
namespace {
// Converts string16 to V8 String.
v8::Handle<v8::String> UTF16ToV8String(const string16& s) {
return v8::String::New(reinterpret_cast<const uint16_t*>(s.data()), s.size());
}
} // namespace
Window::Window(v8::Handle<v8::Object> wrapper, base::DictionaryValue* options)
: EventEmitter(wrapper),
window_(NativeWindow::Create(options)) {
@@ -56,6 +49,12 @@ void Window::OnPageTitleUpdated(bool* prevent_default,
*prevent_default = Emit("page-title-updated", &args);
}
void Window::OnLoadingStateChanged(bool is_loading) {
base::ListValue args;
args.AppendBoolean(is_loading);
Emit("loading-state-changed", &args);
}
void Window::WillCloseWindow(bool* prevent_default) {
*prevent_default = Emit("close");
}
@@ -63,8 +62,9 @@ void Window::WillCloseWindow(bool* prevent_default) {
void Window::OnWindowClosed() {
Emit("closed");
// Free memory immediately when window is closed.
delete this;
// Free memory when native window is closed, the delete is delayed so other
// observers would not get a invalid pointer of NativeWindow.
base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
}
void Window::OnWindowBlur() {
@@ -83,6 +83,20 @@ void Window::OnRendererCrashed() {
Emit("crashed");
}
void Window::OnCapturePageDone(v8::Persistent<v8::Function> callback,
const std::vector<unsigned char>& data) {
v8::HandleScope scope;
// TODO(zcbenz): Use new Buffer API when we updated to node v0.12.x.
node::Buffer* buffer = node::Buffer::New(
reinterpret_cast<const char*>(data.data()),
data.size());
v8::Handle<v8::Value> arg = buffer->handle_;
callback->Call(v8::Context::GetCurrent()->Global(), 1, &arg);
callback.Dispose(v8::Isolate::GetCurrent());
}
// static
v8::Handle<v8::Value> Window::New(const v8::Arguments &args) {
v8::HandleScope scope;
@@ -102,6 +116,9 @@ v8::Handle<v8::Value> Window::New(const v8::Arguments &args) {
new Window(args.This(), static_cast<base::DictionaryValue*>(options.get()));
// Give js code a chance to do initialization.
node::MakeCallback(args.This(), "_init", 0, NULL);
return args.This();
}
@@ -135,7 +152,7 @@ v8::Handle<v8::Value> Window::Focus(const v8::Arguments &args) {
// static
v8::Handle<v8::Value> Window::IsFocused(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
return v8::Boolean::New(self->window_->IsFocused());
return ToV8Value(self->window_->IsFocused());
}
// static
@@ -156,6 +173,12 @@ v8::Handle<v8::Value> Window::Hide(const v8::Arguments &args) {
return v8::Undefined();
}
// static
v8::Handle<v8::Value> Window::IsVisible(const v8::Arguments& args) {
UNWRAP_WINDOW_AND_CHECK;
return ToV8Value(self->window_->IsVisible());
}
// static
v8::Handle<v8::Value> Window::Maximize(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
@@ -196,10 +219,11 @@ v8::Handle<v8::Value> Window::Restore(const v8::Arguments &args) {
v8::Handle<v8::Value> Window::SetFullscreen(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
if (args.Length() < 1 || !args[0]->IsBoolean())
bool fs;
if (!FromV8Arguments(args, &fs))
return node::ThrowTypeError("Bad argument");
self->window_->SetFullscreen(args[0]->BooleanValue());
self->window_->SetFullscreen(fs);
return v8::Undefined();
}
@@ -207,18 +231,18 @@ v8::Handle<v8::Value> Window::SetFullscreen(const v8::Arguments &args) {
v8::Handle<v8::Value> Window::IsFullscreen(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
return v8::Boolean::New(self->window_->IsFullscreen());
return ToV8Value(self->window_->IsFullscreen());
}
// static
v8::Handle<v8::Value> Window::SetSize(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
if (args.Length() < 2)
int width, height;
if (!FromV8Arguments(args, &width, &height))
return node::ThrowTypeError("Bad argument");
self->window_->SetSize(
gfx::Size(args[0]->IntegerValue(), args[1]->IntegerValue()));
self->window_->SetSize(gfx::Size(width, height));
return v8::Undefined();
}
@@ -228,8 +252,8 @@ v8::Handle<v8::Value> Window::GetSize(const v8::Arguments &args) {
gfx::Size size = self->window_->GetSize();
v8::Handle<v8::Array> ret = v8::Array::New(2);
ret->Set(0, v8::Integer::New(size.width()));
ret->Set(1, v8::Integer::New(size.height()));
ret->Set(0, ToV8Value(size.width()));
ret->Set(1, ToV8Value(size.height()));
return ret;
}
@@ -238,11 +262,11 @@ v8::Handle<v8::Value> Window::GetSize(const v8::Arguments &args) {
v8::Handle<v8::Value> Window::SetMinimumSize(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
if (args.Length() < 2)
int width, height;
if (!FromV8Arguments(args, &width, &height))
return node::ThrowTypeError("Bad argument");
self->window_->SetMinimumSize(
gfx::Size(args[0]->IntegerValue(), args[1]->IntegerValue()));
self->window_->SetMinimumSize(gfx::Size(width, height));
return v8::Undefined();
}
@@ -252,8 +276,8 @@ v8::Handle<v8::Value> Window::GetMinimumSize(const v8::Arguments &args) {
gfx::Size size = self->window_->GetMinimumSize();
v8::Handle<v8::Array> ret = v8::Array::New(2);
ret->Set(0, v8::Integer::New(size.width()));
ret->Set(1, v8::Integer::New(size.height()));
ret->Set(0, ToV8Value(size.width()));
ret->Set(1, ToV8Value(size.height()));
return ret;
}
@@ -262,11 +286,12 @@ v8::Handle<v8::Value> Window::GetMinimumSize(const v8::Arguments &args) {
v8::Handle<v8::Value> Window::SetMaximumSize(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
if (args.Length() < 2)
return node::ThrowTypeError("Bad argument");
self->window_->SetMaximumSize(
gfx::Size(args[0]->IntegerValue(), args[1]->IntegerValue()));
int width, height;
if (!FromV8Arguments(args, &width, &height))
return node::ThrowTypeError("Bad argument");
self->window_->SetMaximumSize(gfx::Size(width, height));
return v8::Undefined();
}
@@ -276,8 +301,8 @@ v8::Handle<v8::Value> Window::GetMaximumSize(const v8::Arguments &args) {
gfx::Size size = self->window_->GetMaximumSize();
v8::Handle<v8::Array> ret = v8::Array::New(2);
ret->Set(0, v8::Integer::New(size.width()));
ret->Set(1, v8::Integer::New(size.height()));
ret->Set(0, ToV8Value(size.width()));
ret->Set(1, ToV8Value(size.height()));
return ret;
}
@@ -286,10 +311,11 @@ v8::Handle<v8::Value> Window::GetMaximumSize(const v8::Arguments &args) {
v8::Handle<v8::Value> Window::SetResizable(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
if (args.Length() < 1 || !args[0]->IsBoolean())
bool resizable;
if (!FromV8Arguments(args, &resizable))
return node::ThrowTypeError("Bad argument");
self->window_->SetResizable(args[0]->BooleanValue());
self->window_->SetResizable(resizable);
return v8::Undefined();
}
@@ -297,17 +323,18 @@ v8::Handle<v8::Value> Window::SetResizable(const v8::Arguments &args) {
v8::Handle<v8::Value> Window::IsResizable(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
return v8::Boolean::New(self->window_->IsResizable());
return ToV8Value(self->window_->IsResizable());
}
// static
v8::Handle<v8::Value> Window::SetAlwaysOnTop(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
if (args.Length() < 1 || !args[0]->IsBoolean())
bool top;
if (!FromV8Arguments(args, &top))
return node::ThrowTypeError("Bad argument");
self->window_->SetAlwaysOnTop(args[0]->BooleanValue());
self->window_->SetAlwaysOnTop(top);
return v8::Undefined();
}
@@ -315,7 +342,7 @@ v8::Handle<v8::Value> Window::SetAlwaysOnTop(const v8::Arguments &args) {
v8::Handle<v8::Value> Window::IsAlwaysOnTop(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
return v8::Boolean::New(self->window_->IsAlwaysOnTop());
return ToV8Value(self->window_->IsAlwaysOnTop());
}
// static
@@ -331,11 +358,11 @@ v8::Handle<v8::Value> Window::Center(const v8::Arguments &args) {
v8::Handle<v8::Value> Window::SetPosition(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
if (args.Length() < 2)
int x, y;
if (!FromV8Arguments(args, &x, &y))
return node::ThrowTypeError("Bad argument");
self->window_->SetPosition(
gfx::Point(args[0]->IntegerValue(), args[1]->IntegerValue()));
self->window_->SetPosition(gfx::Point(x, y));
return v8::Undefined();
}
@@ -345,8 +372,8 @@ v8::Handle<v8::Value> Window::GetPosition(const v8::Arguments &args) {
gfx::Point pos = self->window_->GetPosition();
v8::Handle<v8::Array> ret = v8::Array::New(2);
ret->Set(0, v8::Integer::New(pos.x()));
ret->Set(1, v8::Integer::New(pos.y()));
ret->Set(0, ToV8Value(pos.x()));
ret->Set(1, ToV8Value(pos.y()));
return ret;
}
@@ -355,20 +382,18 @@ v8::Handle<v8::Value> Window::GetPosition(const v8::Arguments &args) {
v8::Handle<v8::Value> Window::SetTitle(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
if (args.Length() < 1 || !args[0]->IsString())
std::string title;
if (!FromV8Arguments(args, &title))
return node::ThrowTypeError("Bad argument");
self->window_->SetTitle(*v8::String::Utf8Value(args[0]));
self->window_->SetTitle(title);
return v8::Undefined();
}
// static
v8::Handle<v8::Value> Window::GetTitle(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
std::string title = self->window_->GetTitle();
return v8::String::New(title.c_str(), title.size());
return ToV8Value(self->window_->GetTitle());
}
// static
@@ -385,10 +410,11 @@ v8::Handle<v8::Value> Window::FlashFrame(const v8::Arguments &args) {
v8::Handle<v8::Value> Window::SetKiosk(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
if (args.Length() < 1 || !args[0]->IsBoolean())
bool kiosk;
if (!FromV8Arguments(args, &kiosk))
return node::ThrowTypeError("Bad argument");
self->window_->SetKiosk(args[0]->BooleanValue());
self->window_->SetKiosk(kiosk);
return v8::Undefined();
}
@@ -396,7 +422,7 @@ v8::Handle<v8::Value> Window::SetKiosk(const v8::Arguments &args) {
v8::Handle<v8::Value> Window::IsKiosk(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
return v8::Boolean::New(self->window_->IsKiosk());
return ToV8Value(self->window_->IsKiosk());
}
// static
@@ -417,6 +443,24 @@ v8::Handle<v8::Value> Window::CloseDevTools(const v8::Arguments &args) {
return v8::Undefined();
}
// static
v8::Handle<v8::Value> Window::IsDevToolsOpened(const v8::Arguments& args) {
UNWRAP_WINDOW_AND_CHECK;
return ToV8Value(self->window_->IsDevToolsOpened());
}
// static
v8::Handle<v8::Value> Window::InspectElement(const v8::Arguments& args) {
UNWRAP_WINDOW_AND_CHECK;
int x, y;
if (!FromV8Arguments(args, &x, &y))
return node::ThrowTypeError("Bad argument");
self->window_->InspectElement(x, y);
return v8::Undefined();
}
// static
v8::Handle<v8::Value> Window::FocusOnWebView(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
@@ -435,6 +479,12 @@ v8::Handle<v8::Value> Window::BlurWebView(const v8::Arguments &args) {
return v8::Undefined();
}
// static
v8::Handle<v8::Value> Window::IsWebViewFocused(const v8::Arguments& args) {
UNWRAP_WINDOW_AND_CHECK;
return ToV8Value(self->window_->IsWebViewFocused());
}
// static
v8::Handle<v8::Value> Window::RestartHangMonitorTimeout(
const v8::Arguments &args) {
@@ -445,28 +495,42 @@ v8::Handle<v8::Value> Window::RestartHangMonitorTimeout(
return v8::Undefined();
}
// static
v8::Handle<v8::Value> Window::CapturePage(const v8::Arguments& args) {
UNWRAP_WINDOW_AND_CHECK;
gfx::Rect rect;
v8::Persistent<v8::Function> callback;
if (!FromV8Arguments(args, &rect, &callback) &&
!FromV8Arguments(args, &callback))
return node::ThrowTypeError("Bad argument");
self->window_->CapturePage(rect, base::Bind(&Window::OnCapturePageDone,
base::Unretained(self),
callback));
return v8::Undefined();
}
// static
v8::Handle<v8::Value> Window::GetPageTitle(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
string16 title = self->window_->GetWebContents()->GetTitle();
return UTF16ToV8String(title);
return ToV8Value(self->window_->GetWebContents()->GetTitle());
}
// static
v8::Handle<v8::Value> Window::IsLoading(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
return v8::Boolean::New(self->window_->GetWebContents()->IsLoading());
return ToV8Value(self->window_->GetWebContents()->IsLoading());
}
// static
v8::Handle<v8::Value> Window::IsWaitingForResponse(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
return v8::Boolean::New(
self->window_->GetWebContents()->IsWaitingForResponse());
return ToV8Value(self->window_->GetWebContents()->IsWaitingForResponse());
}
// static
@@ -482,14 +546,14 @@ v8::Handle<v8::Value> Window::Stop(const v8::Arguments &args) {
v8::Handle<v8::Value> Window::GetRoutingID(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
return v8::Integer::New(self->window_->GetWebContents()->GetRoutingID());
return ToV8Value(self->window_->GetWebContents()->GetRoutingID());
}
// static
v8::Handle<v8::Value> Window::GetProcessID(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
return v8::Integer::New(
return ToV8Value(
self->window_->GetWebContents()->GetRenderProcessHost()->GetID());
}
@@ -497,19 +561,20 @@ v8::Handle<v8::Value> Window::GetProcessID(const v8::Arguments &args) {
v8::Handle<v8::Value> Window::IsCrashed(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
return v8::Boolean::New(self->window_->GetWebContents()->IsCrashed());
return ToV8Value(self->window_->GetWebContents()->IsCrashed());
}
// static
v8::Handle<v8::Value> Window::LoadURL(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
if (args.Length() < 1 || !args[0]->IsString())
std::string url;
if (!FromV8Arguments(args, &url))
return node::ThrowTypeError("Bad argument");
NavigationController& controller =
self->window_->GetWebContents()->GetController();
controller.LoadURL(GURL(*v8::String::Utf8Value(args[0])),
controller.LoadURL(GURL(url),
content::Referrer(),
content::PAGE_TRANSITION_AUTO_TOPLEVEL,
std::string());
@@ -527,7 +592,7 @@ v8::Handle<v8::Value> Window::GetURL(const v8::Arguments &args) {
if (controller.GetActiveEntry())
url = controller.GetActiveEntry()->GetVirtualURL().spec();
return v8::String::New(url.c_str(), url.size());
return ToV8Value(url);
}
// static
@@ -537,7 +602,7 @@ v8::Handle<v8::Value> Window::CanGoBack(const v8::Arguments &args) {
NavigationController& controller =
self->window_->GetWebContents()->GetController();
return v8::Boolean::New(controller.CanGoBack());
return ToV8Value(controller.CanGoBack());
}
// static
@@ -547,21 +612,21 @@ v8::Handle<v8::Value> Window::CanGoForward(const v8::Arguments &args) {
NavigationController& controller =
self->window_->GetWebContents()->GetController();
return v8::Boolean::New(controller.CanGoForward());
return ToV8Value(controller.CanGoForward());
}
// static
v8::Handle<v8::Value> Window::CanGoToOffset(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
if (args.Length() < 1)
int offset;
if (!FromV8Arguments(args, &offset))
return node::ThrowTypeError("Bad argument");
NavigationController& controller =
self->window_->GetWebContents()->GetController();
int offset = args[0]->IntegerValue();
return v8::Boolean::New(controller.CanGoToOffset(offset));
return ToV8Value(controller.CanGoToOffset(offset));
}
// static
@@ -590,12 +655,13 @@ v8::Handle<v8::Value> Window::GoForward(const v8::Arguments &args) {
v8::Handle<v8::Value> Window::GoToIndex(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
if (args.Length() < 1)
int index;
if (!FromV8Arguments(args, &index))
return node::ThrowTypeError("Bad argument");
NavigationController& controller =
self->window_->GetWebContents()->GetController();
controller.GoToIndex(args[0]->IntegerValue());
controller.GoToIndex(index);
return v8::Undefined();
}
@@ -604,12 +670,13 @@ v8::Handle<v8::Value> Window::GoToIndex(const v8::Arguments &args) {
v8::Handle<v8::Value> Window::GoToOffset(const v8::Arguments &args) {
UNWRAP_WINDOW_AND_CHECK;
if (args.Length() < 1)
int offset;
if (!FromV8Arguments(args, &offset))
return node::ThrowTypeError("Bad argument");
NavigationController& controller =
self->window_->GetWebContents()->GetController();
controller.GoToOffset(args[0]->IntegerValue());
controller.GoToOffset(offset);
return v8::Undefined();
}
@@ -651,6 +718,7 @@ void Window::Initialize(v8::Handle<v8::Object> target) {
NODE_SET_PROTOTYPE_METHOD(t, "isFocused", IsFocused);
NODE_SET_PROTOTYPE_METHOD(t, "show", Show);
NODE_SET_PROTOTYPE_METHOD(t, "hide", Hide);
NODE_SET_PROTOTYPE_METHOD(t, "isVisible", IsVisible);
NODE_SET_PROTOTYPE_METHOD(t, "maximize", Maximize);
NODE_SET_PROTOTYPE_METHOD(t, "unmaximize", Unmaximize);
NODE_SET_PROTOTYPE_METHOD(t, "minimize", Minimize);
@@ -677,11 +745,15 @@ void Window::Initialize(v8::Handle<v8::Object> target) {
NODE_SET_PROTOTYPE_METHOD(t, "isKiosk", IsKiosk);
NODE_SET_PROTOTYPE_METHOD(t, "openDevTools", OpenDevTools);
NODE_SET_PROTOTYPE_METHOD(t, "closeDevTools", CloseDevTools);
NODE_SET_PROTOTYPE_METHOD(t, "isDevToolsOpened", IsDevToolsOpened);
NODE_SET_PROTOTYPE_METHOD(t, "inspectElement", InspectElement);
NODE_SET_PROTOTYPE_METHOD(t, "focusOnWebView", FocusOnWebView);
NODE_SET_PROTOTYPE_METHOD(t, "blurWebView", BlurWebView);
NODE_SET_PROTOTYPE_METHOD(t, "isWebViewFocused", IsWebViewFocused);
NODE_SET_PROTOTYPE_METHOD(t,
"restartHangMonitorTimeout",
RestartHangMonitorTimeout);
NODE_SET_PROTOTYPE_METHOD(t, "capturePage", CapturePage);
NODE_SET_PROTOTYPE_METHOD(t, "getPageTitle", GetPageTitle);
NODE_SET_PROTOTYPE_METHOD(t, "isLoading", IsLoading);

View File

@@ -5,6 +5,8 @@
#ifndef ATOM_BROWSER_API_ATOM_API_WINDOW_H_
#define ATOM_BROWSER_API_ATOM_API_WINDOW_H_
#include <vector>
#include "base/memory/scoped_ptr.h"
#include "browser/api/atom_api_event_emitter.h"
#include "browser/native_window_observer.h"
@@ -35,6 +37,7 @@ class Window : public EventEmitter,
// Implementations of NativeWindowObserver.
virtual void OnPageTitleUpdated(bool* prevent_default,
const std::string& title) OVERRIDE;
virtual void OnLoadingStateChanged(bool is_loading) OVERRIDE;
virtual void WillCloseWindow(bool* prevent_default) OVERRIDE;
virtual void OnWindowClosed() OVERRIDE;
virtual void OnWindowBlur() OVERRIDE;
@@ -52,6 +55,7 @@ class Window : public EventEmitter,
static v8::Handle<v8::Value> IsFocused(const v8::Arguments &args);
static v8::Handle<v8::Value> Show(const v8::Arguments &args);
static v8::Handle<v8::Value> Hide(const v8::Arguments &args);
static v8::Handle<v8::Value> IsVisible(const v8::Arguments &args);
static v8::Handle<v8::Value> Maximize(const v8::Arguments &args);
static v8::Handle<v8::Value> Unmaximize(const v8::Arguments &args);
static v8::Handle<v8::Value> Minimize(const v8::Arguments &args);
@@ -78,10 +82,14 @@ class Window : public EventEmitter,
static v8::Handle<v8::Value> IsKiosk(const v8::Arguments &args);
static v8::Handle<v8::Value> OpenDevTools(const v8::Arguments &args);
static v8::Handle<v8::Value> CloseDevTools(const v8::Arguments &args);
static v8::Handle<v8::Value> IsDevToolsOpened(const v8::Arguments &args);
static v8::Handle<v8::Value> InspectElement(const v8::Arguments &args);
static v8::Handle<v8::Value> FocusOnWebView(const v8::Arguments &args);
static v8::Handle<v8::Value> BlurWebView(const v8::Arguments &args);
static v8::Handle<v8::Value> IsWebViewFocused(const v8::Arguments& args);
static v8::Handle<v8::Value> RestartHangMonitorTimeout(
const v8::Arguments &args);
static v8::Handle<v8::Value> CapturePage(const v8::Arguments& args);
// APIs for WebContents.
static v8::Handle<v8::Value> GetPageTitle(const v8::Arguments &args);
@@ -105,6 +113,10 @@ class Window : public EventEmitter,
static v8::Handle<v8::Value> Reload(const v8::Arguments &args);
static v8::Handle<v8::Value> ReloadIgnoringCache(const v8::Arguments &args);
// Called when capturePage is done.
void OnCapturePageDone(v8::Persistent<v8::Function> callback,
const std::vector<unsigned char>& data);
scoped_ptr<NativeWindow> window_;
DISALLOW_COPY_AND_ASSIGN(Window);

View File

@@ -8,6 +8,8 @@
#include "base/logging.h"
#include "base/values.h"
#include "browser/api/atom_api_event.h"
#include "common/v8_conversions.h"
#include "common/v8_value_converter_impl.h"
#include "content/public/browser/browser_thread.h"
#include "vendor/node/src/node.h"
@@ -42,7 +44,7 @@ void AtomBrowserBindings::AfterLoad() {
void AtomBrowserBindings::OnRendererMessage(int process_id,
int routing_id,
const std::string& channel,
const string16& channel,
const base::ListValue& args) {
v8::HandleScope scope;
@@ -53,7 +55,7 @@ void AtomBrowserBindings::OnRendererMessage(int process_id,
// process.emit(channel, 'message', process_id, routing_id);
std::vector<v8::Handle<v8::Value>> arguments;
arguments.reserve(3 + args.GetSize());
arguments.push_back(v8::String::New(channel.c_str(), channel.size()));
arguments.push_back(ToV8Value(channel));
const base::Value* value;
if (args.Get(0, &value))
arguments.push_back(converter->ToV8Value(value, context));
@@ -72,21 +74,24 @@ void AtomBrowserBindings::OnRendererMessage(int process_id,
void AtomBrowserBindings::OnRendererMessageSync(
int process_id,
int routing_id,
const std::string& channel,
const string16& channel,
const base::ListValue& args,
base::DictionaryValue* result) {
NativeWindow* sender,
IPC::Message* message) {
v8::HandleScope scope;
v8::Handle<v8::Context> context = v8::Context::GetCurrent();
scoped_ptr<V8ValueConverter> converter(new V8ValueConverterImpl());
v8::Handle<v8::Object> event = v8::Object::New();
// Create the event object.
v8::Handle<v8::Object> event = api::Event::CreateV8Object();
api::Event::Unwrap<api::Event>(event)->SetSenderAndMessage(sender, message);
// process.emit(channel, 'sync-message', event, process_id, routing_id);
std::vector<v8::Handle<v8::Value>> arguments;
arguments.reserve(3 + args.GetSize());
arguments.push_back(v8::String::New(channel.c_str(), channel.size()));
arguments.push_back(ToV8Value(channel));
const base::Value* value;
if (args.Get(0, &value))
arguments.push_back(converter->ToV8Value(value, context));
@@ -101,11 +106,6 @@ void AtomBrowserBindings::OnRendererMessageSync(
}
node::MakeCallback(node::process, "emit", arguments.size(), &arguments[0]);
scoped_ptr<base::Value> base_event(converter->FromV8Value(event, context));
DCHECK(base_event && base_event->IsType(base::Value::TYPE_DICTIONARY));
result->Swap(static_cast<base::DictionaryValue*>(base_event.get()));
}
} // namespace atom

View File

@@ -5,17 +5,21 @@
#ifndef ATOM_BROWSER_API_ATOM_BROWSER_BINDINGS_
#define ATOM_BROWSER_API_ATOM_BROWSER_BINDINGS_
#include <string>
#include "base/string16.h"
#include "common/api/atom_bindings.h"
namespace base {
class DictionaryValue;
class ListValue;
}
namespace IPC {
class Message;
}
namespace atom {
class NativeWindow;
class AtomBrowserBindings : public AtomBindings {
public:
AtomBrowserBindings();
@@ -27,15 +31,16 @@ class AtomBrowserBindings : public AtomBindings {
// Called when received a message from renderer.
void OnRendererMessage(int process_id,
int routing_id,
const std::string& channel,
const string16& channel,
const base::ListValue& args);
// Called when received a synchronous message from renderer.
void OnRendererMessageSync(int process_id,
int routing_id,
const std::string& channel,
const string16& channel,
const base::ListValue& args,
base::DictionaryValue* result);
NativeWindow* sender,
IPC::Message* message);
// The require('atom').browserMainParts object.
v8::Handle<v8::Object> browser_main_parts() {

View File

@@ -9,9 +9,25 @@ app = new Application
app.getHomeDir = ->
process.env[if process.platform is 'win32' then 'USERPROFILE' else 'HOME']
app.getBrowserWindows = ->
require('../../atom/objects-registry.js').getAllWindows()
app.setApplicationMenu = (menu) ->
require('menu').setApplicationMenu menu
app.getApplicationMenu = ->
require('menu').getApplicationMenu()
app.commandLine =
appendSwitch: bindings.appendSwitch,
appendArgument: bindings.appendArgument
if process.platform is 'darwin'
app.dock =
bounce: (type = 'informational') -> bindings.dockBounce type
cancelBounce: bindings.dockCancelBounce
setBadge: bindings.dockSetBadgeText
getBadge: bindings.dockGetBadgeText
# Only one App object pemitted.
module.exports = app

View File

@@ -1,28 +1,36 @@
EventEmitter = require('events').EventEmitter
app = require 'app'
v8Util = process.atomBinding 'v8_util'
objectsRegistry = require '../../atom/objects-registry.js'
BrowserWindow = process.atomBinding('window').BrowserWindow
BrowserWindow::__proto__ = EventEmitter.prototype
BrowserWindow::_init = ->
# Simulate the application menu on platforms other than OS X.
if process.platform isnt 'darwin'
menu = app.getApplicationMenu()
@setMenu menu if menu?
BrowserWindow::toggleDevTools = ->
opened = v8Util.getHiddenValue this, 'devtoolsOpened'
if opened
@closeDevTools()
v8Util.setHiddenValue this, 'devtoolsOpened', false
else
@openDevTools()
v8Util.setHiddenValue this, 'devtoolsOpened', true
if @isDevToolsOpened() then @closeDevTools() else @openDevTools()
BrowserWindow::restart = ->
@loadUrl(@getUrl())
BrowserWindow::setMenu = (menu) ->
throw new Error('BrowserWindow.setMenu is only available on Windows') unless process.platform is 'win32'
throw new TypeError('Invalid menu') unless menu?.constructor?.name is 'Menu'
@menu = menu # Keep a reference of menu in case of GC.
@menu.attachToWindow this
BrowserWindow.getFocusedWindow = ->
windows = objectsRegistry.getAllWindows()
windows = app.getBrowserWindows()
return window for window in windows when window.isFocused()
BrowserWindow.fromProcessIdAndRoutingId = (processId, routingId) ->
windows = objectsRegistry.getAllWindows()
windows = app.getBrowserWindows()
return window for window in windows when window.getProcessId() == processId and
window.getRoutingId() == routingId

View File

@@ -1,4 +1,5 @@
binding = process.atomBinding 'dialog'
v8Util = process.atomBinding 'v8_util'
BrowserWindow = require 'browser-window'
fileDialogProperties =
@@ -7,48 +8,71 @@ fileDialogProperties =
messageBoxTypes = ['none', 'info', 'warning']
module.exports =
showOpenDialog: (options) ->
options = title: 'Open', properties: ['openFile'] unless options?
options.properties = options.properties ? ['openFile']
showOpenDialog: (window, options, callback) ->
unless window?.constructor is BrowserWindow
# Shift.
callback = options
options = window
window = null
options ?= title: 'Open', properties: ['openFile']
options.properties ?= ['openFile']
throw new TypeError('Properties need to be array') unless Array.isArray options.properties
properties = 0
for prop, value of fileDialogProperties
properties |= value if prop in options.properties
options.title = options.title ? ''
options.defaultPath = options.defaultPath ? ''
options.title ?= ''
options.defaultPath ?= ''
binding.showOpenDialog options.title, options.defaultPath, properties
binding.showOpenDialog String(options.title),
String(options.defaultPath),
properties,
window,
callback
showSaveDialog: (window, options) ->
throw new TypeError('Invalid window') unless window?.constructor is BrowserWindow
options = title: 'Save' unless options?
options.title = options.title ? ''
options.defaultPath = options.defaultPath ? ''
binding.showSaveDialog window, options.title, options.defaultPath
showMessageBox: (window, options) ->
if window? and window.constructor isnt BrowserWindow
showSaveDialog: (window, options, callback) ->
unless window?.constructor is BrowserWindow
# Shift.
callback = options
options = window
window = null
options = type: 'none' unless options?
options.type = options.type ? 'none'
options ?= title: 'Save'
options.title ?= ''
options.defaultPath ?= ''
binding.showSaveDialog String(options.title),
String(options.defaultPath),
window,
callback
showMessageBox: (window, options, callback) ->
unless window?.constructor is BrowserWindow
# Shift.
callback = options
options = window
window = null
options ?= type: 'none'
options.type ?= 'none'
options.type = messageBoxTypes.indexOf options.type
throw new TypeError('Invalid message box type') unless options.type > -1
throw new TypeError('Buttons need to be array') unless Array.isArray options.buttons
options.title = options.title ? ''
options.message = options.message ? ''
options.detail = options.detail ? ''
options.title ?= ''
options.message ?= ''
options.detail ?= ''
binding.showMessageBox options.type,
options.buttons,
String(options.title),
String(options.message),
String(options.detail),
window
window,
callback
# Mark standard asynchronous functions.
v8Util.setHiddenValue f, 'asynchronous', true for k, f of module.exports

View File

@@ -14,8 +14,13 @@ class Ipc extends EventEmitter
constructor: ->
process.on 'ATOM_INTERNAL_MESSAGE', (args...) =>
@emit(args...)
process.on 'ATOM_INTERNAL_MESSAGE_SYNC', (args...) =>
@emit(args...)
process.on 'ATOM_INTERNAL_MESSAGE_SYNC', (channel, event, args...) =>
set = (value) -> event.sendReply JSON.stringify(value)
Object.defineProperty event, 'returnValue', {set}
Object.defineProperty event, 'result', {set}
@emit(channel, event, args...)
send: (processId, routingId, args...) ->
@sendChannel(processId, routingId, 'message', args...)

View File

@@ -1,3 +1,5 @@
BrowserWindow = require 'browser-window'
nextCommandId = 0
class MenuItem
@@ -6,7 +8,7 @@ class MenuItem
constructor: (options) ->
Menu = require 'menu'
{click, selector, @type, @label, @sublabel, @accelerator, @enabled, @visible, @checked, @groupId, @submenu} = options
{click, @selector, @type, @label, @sublabel, @accelerator, @enabled, @visible, @checked, @groupId, @submenu} = options
@type = 'submenu' if not @type? and @submenu?
throw new Error('Invalid submenu') if @type is 'submenu' and @submenu?.constructor isnt Menu
@@ -14,16 +16,20 @@ class MenuItem
@type = @type ? 'normal'
@label = @label ? ''
@sublabel = @sublabel ? ''
@accelerator = @accelerator ? null
@enabled = @enabled ? true
@visible = @visible ? true
@checked = @checked ? false
@groupId = @groupId ? null
@submenu = @submenu ? null
throw new Error('Unknown menu type') if MenuItem.types.indexOf(@type) is -1
@commandId = ++nextCommandId
@click = ->
@click = =>
if typeof click is 'function'
click()
else if typeof selector is 'string'
Menu.sendActionToFirstResponder selector
click this, BrowserWindow.getFocusedWindow()
else if typeof @selector is 'string'
Menu.sendActionToFirstResponder @selector
module.exports = MenuItem

View File

@@ -1,7 +1,9 @@
EventEmitter = require('events').EventEmitter
BrowserWindow = require 'browser-window'
EventEmitter = require('events').EventEmitter
IDWeakMap = require 'id-weak-map'
MenuItem = require 'menu-item'
app = require 'app'
bindings = process.atomBinding 'menu'
Menu = bindings.Menu
@@ -28,19 +30,32 @@ Menu::insert = (pos, item) ->
@setSublabel pos, item.sublabel if item.sublabel?
unless @items?
@items = {}
unless @delegate?
@commandsMap = {}
@items = []
@delegate =
isCommandIdChecked: (commandId) => @items[commandId]?.checked
isCommandIdEnabled: (commandId) => @items[commandId]?.enabled
isCommandIdVisible: (commandId) => @items[commandId]?.visible
getAcceleratorForCommandId: (commandId) => @items[commandId]?.accelerator
executeCommand: (commandId) => @items[commandId]?.click()
@items[item.commandId] = item
isCommandIdChecked: (commandId) => @commandsMap[commandId]?.checked
isCommandIdEnabled: (commandId) => @commandsMap[commandId]?.enabled
isCommandIdVisible: (commandId) => @commandsMap[commandId]?.visible
getAcceleratorForCommandId: (commandId) => @commandsMap[commandId]?.accelerator
executeCommand: (commandId) =>
activeItem = @commandsMap[commandId]
activeItem.click() if activeItem?
@items.splice pos, 0, item
@commandsMap[item.commandId] = item
applicationMenu = null
Menu.setApplicationMenu = (menu) ->
throw new TypeError('Invalid menu') unless menu?.constructor is Menu
bindings.setApplicationMenu menu
applicationMenu = menu # Keep a reference.
if process.platform is 'darwin'
bindings.setApplicationMenu menu
else
windows = app.getBrowserWindows()
w.setMenu menu for w in windows
Menu.getApplicationMenu = -> applicationMenu
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder
@@ -52,7 +67,10 @@ Menu.buildFromTemplate = (template) ->
throw new TypeError('Invalid template for MenuItem') unless typeof item is 'object'
item.submenu = Menu.buildFromTemplate item.submenu if item.submenu?
menu.append new MenuItem(item)
menuItem = new MenuItem(item)
menuItem[key] = value for key, value of item when not menuItem[key]?
menu.append menuItem
menu

View File

@@ -0,0 +1,7 @@
bindings = process.atomBinding 'power_monitor'
EventEmitter = require('events').EventEmitter
PowerMonitor = bindings.PowerMonitor
PowerMonitor::__proto__ = EventEmitter.prototype
module.exports = new PowerMonitor

View File

@@ -0,0 +1,20 @@
protocol = process.atomBinding 'protocol'
EventEmitter = require('events').EventEmitter
protocol[key] = value for key, value of EventEmitter.prototype
protocol.RequestStringJob =
class RequestStringJob
constructor: ({mimeType, charset, data}) ->
if typeof data isnt 'string' and not data instanceof Buffer
throw new TypeError('Data should be string or Buffer')
@mimeType = mimeType ? 'text/plain'
@charset = charset ? 'UTF-8'
@data = String data
protocol.RequestFileJob =
class RequestFileJob
constructor: (@path) ->
module.exports = protocol

View File

@@ -1,8 +1,17 @@
fs = require 'fs'
path = require 'path'
# Enable idle gc.
process.atomBinding('idle_gc').start()
if process.platform is 'win32'
# Redirect node's console to use our own implementations, since node can not
# handle output when running as GUI program.
console.log = console.error = console.warn = process.log
process.stdout.write = process.stderr.write = process.log
# Always returns EOF for stdin stream.
Readable = require('stream').Readable
stdin = new Readable
stdin.push null
process.__defineGetter__ 'stdin', -> stdin
# Provide default Content API implementations.
atom = {}
@@ -17,7 +26,7 @@ atom.browserMainParts =
global.__atom = atom
# Add browser/api/lib to require's search paths,
# which contains javascript part of Atom's built-in libraries.
# which contains javascript part of Atom's built-in libraries.
globalPaths = require('module').globalPaths
globalPaths.push path.join process.resourcesPath, 'browser', 'api', 'lib'
@@ -29,11 +38,11 @@ process.on 'uncaughtException', (error) ->
# Show error in GUI.
message = error.stack ? "#{error.name}: #{error.message}"
require('dialog').showMessageBox
type: 'warning'
title: 'An javascript error occured in the browser'
message: 'uncaughtException'
detail: message
buttons: ['OK']
type: 'warning'
title: 'An javascript error occured in the browser'
message: 'uncaughtException'
detail: message
buttons: ['OK']
# Load the RPC server.
require './rpc-server.js'

View File

@@ -1,6 +1,9 @@
BrowserWindow = require 'browser-window'
EventEmitter = require('events').EventEmitter
IDWeakMap = require 'id-weak-map'
v8Util = process.atomBinding 'v8_util'
# Class to reference all objects.
class ObjectsStore
@stores = {}
@@ -36,50 +39,57 @@ class ObjectsStore
key = "#{processId}_#{routingId}"
delete @stores[key]
# Objects in weak map will be not referenced (so we won't leak memory), and
# every object created in browser will have a unique id in weak map.
objectsWeakMap = new IDWeakMap
objectsWeakMap.add = (obj) ->
id = IDWeakMap::add.call this, obj
Object.defineProperty obj, 'id',
enumerable: true, writable: false, value: id
id
class ObjectsRegistry extends EventEmitter
constructor: ->
# Objects in weak map will be not referenced (so we won't leak memory), and
# every object created in browser will have a unique id in weak map.
@objectsWeakMap = new IDWeakMap
@objectsWeakMap.add = (obj) ->
id = IDWeakMap::add.call this, obj
v8Util.setHiddenValue obj, 'atomId', id
id
windowsWeakMap = new IDWeakMap
# Remember all windows in the weak map.
@windowsWeakMap = new IDWeakMap
process.on 'ATOM_BROWSER_INTERNAL_NEW', (obj) =>
if obj.constructor is BrowserWindow
id = @windowsWeakMap.add obj
obj.on 'destroyed', => @windowsWeakMap.remove id
process.on 'ATOM_BROWSER_INTERNAL_NEW', (obj) ->
# It's possible that user created a object in browser side and then want to
# get it in renderer via remote.getObject. So we must add every native object
# created in browser to the weak map even it may not be referenced by the
# renderer.
objectsWeakMap.add obj
# Register a new object, the object would be kept referenced until you release
# it explicitly.
add: (processId, routingId, obj) ->
# Some native objects may already been added to objectsWeakMap, be care not
# to add it twice.
@objectsWeakMap.add obj unless v8Util.getHiddenValue obj, 'atomId'
id = v8Util.getHiddenValue obj, 'atomId'
# Also remember all windows.
if obj.constructor is BrowserWindow
id = windowsWeakMap.add obj
obj.on 'destroyed', ->
windowsWeakMap.remove id
# Store and reference the object, then return the storeId which points to
# where the object is stored. The caller can later dereference the object
# with the storeId.
# We use a difference key because the same object can be referenced for
# multiple times by the same renderer view.
store = ObjectsStore.forRenderView processId, routingId
storeId = store.add obj
exports.add = (processId, routingId, obj) ->
# Some native objects may already been added to objectsWeakMap, be care not
# to add it twice.
objectsWeakMap.add obj unless obj.id? and objectsWeakMap.has obj.id
[id, storeId]
# Store and reference the object, then return the storeId which points to
# where the object is stored. The caller can later dereference the object
# with the storeId.
store = ObjectsStore.forRenderView processId, routingId
store.add obj
# Get an object according to its id.
get: (id) ->
@objectsWeakMap.get id
exports.get = (id) ->
objectsWeakMap.get id
# Remove an object according to its storeId.
remove: (processId, routingId, storeId) ->
ObjectsStore.forRenderView(processId, routingId).remove storeId
exports.getAllWindows = () ->
keys = windowsWeakMap.keys()
windowsWeakMap.get key for key in keys
# Clear all references to objects from renderer view.
clear: (processId, routingId) ->
@emit "release-renderer-view-#{processId}-#{routingId}"
ObjectsStore.releaseForRenderView processId, routingId
exports.remove = (processId, routingId, storeId) ->
ObjectsStore.forRenderView(processId, routingId).remove storeId
# Return an array of all browser windows.
getAllWindows: ->
keys = @windowsWeakMap.keys()
@windowsWeakMap.get key for key in keys
exports.clear = (processId, routingId) ->
ObjectsStore.releaseForRenderView processId, routingId
module.exports = new ObjectsRegistry

View File

@@ -22,8 +22,7 @@ valueToMeta = (processId, routingId, value) ->
# Reference the original value if it's an object, because when it's
# passed to renderer we would assume the renderer keeps a reference of
# it.
meta.storeId = objectsRegistry.add processId, routingId, value
meta.id = value.id
[meta.id, meta.storeId] = objectsRegistry.add processId, routingId, value
meta.members = []
meta.members.push {name: prop, type: typeof field} for prop, field of value
@@ -39,40 +38,69 @@ errorToMeta = (error) ->
# Convert array of meta data from renderer into array of real values.
unwrapArgs = (processId, routingId, args) ->
args.map (meta) ->
metaToValue = (meta) ->
switch meta.type
when 'value' then meta.value
when 'object' then objectsRegistry.get meta.id
when 'remote-object' then objectsRegistry.get meta.id
when 'array' then unwrapArgs processId, routingId, meta.value
when 'object'
ret = v8Util.createObjectWithName meta.name
for member in meta.members
ret[member.name] = metaToValue(member.value)
ret
when 'function-with-return-value'
returnValue = metaToValue meta.value
-> returnValue
when 'function'
rendererReleased = false
objectsRegistry.once "release-renderer-view-#{processId}-#{routingId}", ->
rendererReleased = true
ret = ->
throw new Error('Calling a callback of released renderer view') if rendererReleased
ipc.sendChannel processId, routingId, 'ATOM_RENDERER_CALLBACK', meta.id, valueToMeta(processId, routingId, arguments)
v8Util.setDestructor ret, ->
return if rendererReleased
ipc.sendChannel processId, routingId, 'ATOM_RENDERER_RELEASE_CALLBACK', meta.id
ret
else throw new TypeError("Unknown type: #{meta.type}")
args.map metaToValue
# Call a function and send reply asynchronously if it's a an asynchronous
# style function and the caller didn't pass a callback.
callFunction = (event, processId, routingId, func, caller, args) ->
if v8Util.getHiddenValue(func, 'asynchronous') and typeof args[args.length - 1] isnt 'function'
args.push (ret) ->
event.returnValue = valueToMeta processId, routingId, ret
func.apply caller, args
else
ret = func.apply caller, args
event.returnValue = valueToMeta processId, routingId, ret
ipc.on 'ATOM_BROWSER_REQUIRE', (event, processId, routingId, module) ->
try
event.result = valueToMeta processId, routingId, require(module)
event.returnValue = valueToMeta processId, routingId, require(module)
catch e
event.result = errorToMeta e
event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_GLOBAL', (event, processId, routingId, name) ->
try
event.result = valueToMeta processId, routingId, global[name]
event.returnValue = valueToMeta processId, routingId, global[name]
catch e
event.result = errorToMeta e
event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_RELEASE_RENDER_VIEW', (event, processId, routingId) ->
objectsRegistry.clear processId, routingId
event.returnValue = null
ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event, processId, routingId) ->
try
BrowserWindow = require 'browser-window'
window = BrowserWindow.fromProcessIdAndRoutingId processId, routingId
event.result = valueToMeta processId, routingId, window
event.returnValue = valueToMeta processId, routingId, window
catch e
event.result = errorToMeta e
event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_CONSTRUCTOR', (event, processId, routingId, id, args) ->
try
@@ -81,48 +109,50 @@ ipc.on 'ATOM_BROWSER_CONSTRUCTOR', (event, processId, routingId, id, args) ->
# Call new with array of arguments.
# http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
obj = new (Function::bind.apply(constructor, [null].concat(args)))
event.result = valueToMeta processId, routingId, obj
event.returnValue = valueToMeta processId, routingId, obj
catch e
event.result = errorToMeta e
event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_FUNCTION_CALL', (event, processId, routingId, id, args) ->
try
args = unwrapArgs processId, routingId, args
func = objectsRegistry.get id
ret = func.apply global, args
event.result = valueToMeta processId, routingId, ret
callFunction event, processId, routingId, func, global, args
catch e
event.result = errorToMeta e
event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, processId, routingId, id, method, args) ->
try
args = unwrapArgs processId, routingId, args
constructor = objectsRegistry.get(id)[method]
# Call new with array of arguments.
obj = new (Function::bind.apply(constructor, [null].concat(args)))
event.returnValue = valueToMeta processId, routingId, obj
catch e
event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_MEMBER_CALL', (event, processId, routingId, id, method, args) ->
try
args = unwrapArgs processId, routingId, args
obj = objectsRegistry.get id
ret = obj[method].apply(obj, args)
event.result = valueToMeta processId, routingId, ret
callFunction event, processId, routingId, obj[method], obj, args
catch e
event.result = errorToMeta e
event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_MEMBER_SET', (event, processId, routingId, id, name, value) ->
try
obj = objectsRegistry.get id
obj[name] = value
event.returnValue = null
catch e
event.result = errorToMeta e
event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_MEMBER_GET', (event, processId, routingId, id, name) ->
try
obj = objectsRegistry.get id
event.result = valueToMeta processId, routingId, obj[name]
event.returnValue = valueToMeta processId, routingId, obj[name]
catch e
event.result = errorToMeta e
ipc.on 'ATOM_BROWSER_REFERENCE', (event, processId, routingId, id) ->
try
obj = objectsRegistry.get id
event.result = valueToMeta processId, routingId, obj
catch e
event.result = errorToMeta e
event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_DEREFERENCE', (processId, routingId, storeId) ->
objectsRegistry.remove processId, routingId, storeId

View File

@@ -4,8 +4,9 @@
#include "browser/atom_browser_client.h"
#include "browser/atom_browser_context.h"
#include "browser/atom_browser_main_parts.h"
#include "browser/media/media_capture_devices_dispatcher.h"
#include "browser/net/atom_url_request_context_getter.h"
#include "webkit/glue/webpreferences.h"
namespace atom {
@@ -16,6 +17,13 @@ AtomBrowserClient::AtomBrowserClient() {
AtomBrowserClient::~AtomBrowserClient() {
}
net::URLRequestContextGetter* AtomBrowserClient::CreateRequestContext(
content::BrowserContext* browser_context,
content::ProtocolHandlerMap* protocol_handlers) {
return static_cast<AtomBrowserContext*>(browser_context)->
CreateRequestContext(protocol_handlers);
}
void AtomBrowserClient::OverrideWebkitPrefs(
content::RenderViewHost* render_view_host,
const GURL& url,
@@ -38,10 +46,6 @@ void AtomBrowserClient::OverrideWebkitPrefs(
prefs->allow_running_insecure_content = true;
}
content::MediaObserver* AtomBrowserClient::GetMediaObserver() {
return MediaCaptureDevicesDispatcher::GetInstance();
}
bool AtomBrowserClient::ShouldSwapProcessesForNavigation(
content::SiteInstance* site_instance,
const GURL& current_url,

View File

@@ -15,10 +15,12 @@ class AtomBrowserClient : public brightray::BrowserClient {
virtual ~AtomBrowserClient();
protected:
net::URLRequestContextGetter* CreateRequestContext(
content::BrowserContext* browser_context,
content::ProtocolHandlerMap* protocol_handlers) OVERRIDE;
virtual void OverrideWebkitPrefs(content::RenderViewHost* render_view_host,
const GURL& url,
WebPreferences* prefs) OVERRIDE;
virtual content::MediaObserver* GetMediaObserver() OVERRIDE;
virtual bool ShouldSwapProcessesForNavigation(
content::SiteInstance* site_instance,
const GURL& current_url,

View File

@@ -5,15 +5,65 @@
#include "browser/atom_browser_context.h"
#include "browser/atom_browser_main_parts.h"
#include "browser/net/atom_url_request_context_getter.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/resource_context.h"
#include "vendor/brightray/browser/network_delegate.h"
namespace atom {
AtomBrowserContext::AtomBrowserContext() {
using content::BrowserThread;
class AtomResourceContext : public content::ResourceContext {
public:
AtomResourceContext() : getter_(NULL) {}
void set_url_request_context_getter(AtomURLRequestContextGetter* getter) {
getter_ = getter;
}
protected:
virtual net::HostResolver* GetHostResolver() OVERRIDE {
DCHECK(getter_);
return getter_->host_resolver();
}
virtual net::URLRequestContext* GetRequestContext() OVERRIDE {
DCHECK(getter_);
return getter_->GetURLRequestContext();
}
private:
AtomURLRequestContextGetter* getter_;
DISALLOW_COPY_AND_ASSIGN(AtomResourceContext);
};
AtomBrowserContext::AtomBrowserContext()
: resource_context_(new AtomResourceContext) {
}
AtomBrowserContext::~AtomBrowserContext() {
}
AtomURLRequestContextGetter* AtomBrowserContext::CreateRequestContext(
content::ProtocolHandlerMap* protocol_handlers) {
DCHECK(!url_request_getter_);
url_request_getter_ = new AtomURLRequestContextGetter(
GetPath(),
BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::IO),
BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::FILE),
CreateNetworkDelegate().Pass(),
protocol_handlers);
resource_context_->set_url_request_context_getter(url_request_getter_.get());
return url_request_getter_.get();
}
content::ResourceContext* AtomBrowserContext::GetResourceContext() {
return resource_context_.get();
}
// static
AtomBrowserContext* AtomBrowserContext::Get() {
return static_cast<AtomBrowserContext*>(

View File

@@ -10,14 +10,34 @@
namespace atom {
class AtomResourceContext;
class AtomURLRequestContextGetter;
class AtomBrowserContext : public brightray::BrowserContext {
public:
AtomBrowserContext();
virtual ~AtomBrowserContext();
// Returns the browser context singleton.
static AtomBrowserContext* Get();
// Creates or returns the request context.
AtomURLRequestContextGetter* CreateRequestContext(
content::ProtocolHandlerMap*);
AtomURLRequestContextGetter* url_request_context_getter() const {
DCHECK(url_request_getter_);
return url_request_getter_.get();
}
protected:
// content::BrowserContext implementations:
virtual content::ResourceContext* GetResourceContext() OVERRIDE;
private:
scoped_ptr<AtomResourceContext> resource_context_;
scoped_refptr<AtomURLRequestContextGetter> url_request_getter_;
DISALLOW_COPY_AND_ASSIGN(AtomBrowserContext);
};

View File

@@ -4,11 +4,13 @@
#include "browser/atom_browser_main_parts.h"
#include "base/power_monitor/power_monitor.h"
#include "browser/api/atom_browser_bindings.h"
#include "browser/atom_browser_client.h"
#include "browser/atom_browser_context.h"
#include "browser/browser.h"
#include "common/node_bindings.h"
#include "net/proxy/proxy_resolver_v8.h"
#include "vendor/node/src/node.h"
#include "vendor/node/src/node_internals.h"
@@ -20,7 +22,7 @@ AtomBrowserMainParts* AtomBrowserMainParts::self_ = NULL;
AtomBrowserMainParts::AtomBrowserMainParts()
: atom_bindings_(new AtomBrowserBindings),
browser_(new Browser),
node_bindings_(NodeBindings::CreateInBrowser()) {
node_bindings_(NodeBindings::Create(true)) {
DCHECK(!self_) << "Cannot have two AtomBrowserMainParts";
self_ = this;
}
@@ -41,6 +43,10 @@ brightray::BrowserContext* AtomBrowserMainParts::CreateBrowserContext() {
void AtomBrowserMainParts::PostEarlyInitialization() {
brightray::BrowserMainParts::PostEarlyInitialization();
#if defined(OS_MACOSX)
base::PowerMonitor::AllocateSystemIOPorts();
#endif
node_bindings_->Initialize();
// Wrap whole process in one global context.
@@ -70,6 +76,11 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() {
node_bindings_->RunMessageLoop();
// Make sure the url request job factory is created before the
// will-finish-launching event.
static_cast<content::BrowserContext*>(AtomBrowserContext::Get())->
GetRequestContext();
#if !defined(OS_MACOSX)
// The corresponding call in OS X is in AtomApplicationDelegate.
Browser::Get()->WillFinishLaunching();
@@ -77,4 +88,10 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() {
#endif
}
int AtomBrowserMainParts::PreCreateThreads() {
// TODO(zcbenz): Calling CreateIsolate() on Windows when updated to Chrome30.
net::ProxyResolverV8::RememberDefaultIsolate();
return 0;
}
} // namespace atom

View File

@@ -30,6 +30,7 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts {
// Implementations of content::BrowserMainParts.
virtual void PostEarlyInitialization() OVERRIDE;
virtual void PreMainMessageLoopRun() OVERRIDE;
virtual int PreCreateThreads() OVERRIDE;
#if defined(OS_MACOSX)
virtual void PreMainMessageLoopStart() OVERRIDE;
virtual void PostDestroyThreads() OVERRIDE;

View File

@@ -4,9 +4,11 @@
#include "browser/atom_browser_main_parts.h"
#import "base/mac/bundle_locations.h"
#include "base/files/file_path.h"
#import "base/mac/foundation_util.h"
#import "browser/atom_application_mac.h"
#import "browser/atom_application_delegate_mac.h"
#import "vendor/brightray/common/mac/main_application_bundle.h"
namespace atom {
@@ -17,10 +19,12 @@ void AtomBrowserMainParts::PreMainMessageLoopStart() {
AtomApplicationDelegate* delegate = [AtomApplicationDelegate alloc];
[NSApp setDelegate:delegate];
auto infoDictionary = base::mac::OuterBundle().infoDictionary;
NSString *mainNibName = [infoDictionary objectForKey:@"NSMainNibFile"];
auto mainNib = [[NSNib alloc] initWithNibNamed:mainNibName bundle:base::mac::FrameworkBundle()];
base::FilePath frameworkPath = brightray::MainApplicationBundlePath()
.Append("Contents").Append("Frameworks").Append("Atom.framework");
NSBundle* frameworkBundle = [NSBundle
bundleWithPath:base::mac::FilePathToNSString(frameworkPath)];
NSNib* mainNib = [[NSNib alloc] initWithNibNamed:@"MainMenu"
bundle:frameworkBundle];
[mainNib instantiateWithOwner:application topLevelObjects:nil];
[mainNib release];

View File

@@ -33,6 +33,20 @@ class Browser : public WindowListObserver {
// Returns the version of the executable (or bundle).
std::string GetVersion();
#if defined(OS_MACOSX)
// Bounce the dock icon.
enum BounceType {
BOUNCE_CRITICAL = 0,
BOUNCE_INFORMATIONAL = 10,
};
int DockBounce(BounceType type);
void DockCancelBounce(int request_id);
// Set/Get dock's badge text.
void DockSetBadgeText(const std::string& label);
std::string DockGetBadgeText();
#endif // defined(OS_MACOSX)
// Tell the application to open a file.
bool OpenFile(const std::string& file_path);

View File

@@ -29,4 +29,22 @@ void Browser::CancelQuit() {
[[AtomApplication sharedApplication] replyToApplicationShouldTerminate:NO];
}
int Browser::DockBounce(BounceType type) {
return [[AtomApplication sharedApplication] requestUserAttention:type];
}
void Browser::DockCancelBounce(int rid) {
[[AtomApplication sharedApplication] cancelUserAttentionRequest:rid];
}
void Browser::DockSetBadgeText(const std::string& label) {
NSDockTile *tile = [[AtomApplication sharedApplication] dockTile];
[tile setBadgeLabel:base::SysUTF8ToNSString(label)];
}
std::string Browser::DockGetBadgeText() {
NSDockTile *tile = [[AtomApplication sharedApplication] dockTile];
return base::SysNSStringToUTF8([tile badgeLabel]);
}
} // namespace atom

View File

@@ -0,0 +1,205 @@
var app = require('app');
var dialog = require('dialog');
var delegate = require('atom-delegate');
var ipc = require('ipc');
var Menu = require('menu');
var MenuItem = require('menu-item');
var BrowserWindow = require('browser-window');
var mainWindow = null;
var menu = null;
// Quit when all windows are closed.
app.on('window-all-closed', function() {
app.terminate();
});
app.on('open-url', function(event, url) {
dialog.showMessageBox({message: url, buttons: ['OK']});
});
app.on('finish-launching', function() {
app.commandLine.appendSwitch('js-flags', '--harmony_collections');
mainWindow = new BrowserWindow({ width: 800, height: 600 });
mainWindow.loadUrl('file://' + __dirname + '/index.html');
mainWindow.on('page-title-updated', function(event, title) {
event.preventDefault();
this.setTitle('Atom Shell - ' + title);
});
mainWindow.on('closed', function() {
mainWindow = null;
});
mainWindow.on('unresponsive', function() {
console.log('unresponsive');
});
if (process.platform == 'darwin') {
var template = [
{
label: 'Atom Shell',
submenu: [
{
label: 'About Atom Shell',
selector: 'orderFrontStandardAboutPanel:'
},
{
type: 'separator'
},
{
label: 'Hide Atom Shell',
accelerator: 'Command+H',
selector: 'hide:'
},
{
label: 'Hide Others',
accelerator: 'Command+Shift+H',
selector: 'hideOtherApplications:'
},
{
label: 'Show All',
selector: 'unhideAllApplications:'
},
{
type: 'separator'
},
{
label: 'Quit',
accelerator: 'Command+Q',
click: function() { app.quit(); }
},
]
},
{
label: 'Edit',
submenu: [
{
label: 'Undo',
accelerator: 'Command+Z',
selector: 'undo:'
},
{
label: 'Redo',
accelerator: 'Shift+Command+Z',
selector: 'redo:'
},
{
type: 'separator'
},
{
label: 'Cut',
accelerator: 'Command+X',
selector: 'cut:'
},
{
label: 'Copy',
accelerator: 'Command+C',
selector: 'copy:'
},
{
label: 'Paste',
accelerator: 'Command+V',
selector: 'paste:'
},
{
label: 'Select All',
accelerator: 'Command+A',
selector: 'selectAll:'
},
]
},
{
label: 'View',
submenu: [
{
label: 'Reload',
accelerator: 'Command+R',
click: function() { BrowserWindow.getFocusedWindow().restart(); }
},
{
label: 'Enter Fullscreen',
click: function() { BrowserWindow.getFocusedWindow().setFullscreen(true); }
},
{
label: 'Toggle DevTools',
accelerator: 'Alt+Command+I',
click: function() { BrowserWindow.getFocusedWindow().toggleDevTools(); }
},
]
},
{
label: 'Window',
submenu: [
{
label: 'Minimize',
accelerator: 'Command+M',
selector: 'performMiniaturize:'
},
{
label: 'Close',
accelerator: 'Command+W',
selector: 'performClose:'
},
{
type: 'separator'
},
{
label: 'Bring All to Front',
selector: 'arrangeInFront:'
},
]
},
];
menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
} else {
var template = [
{
label: 'File',
submenu: [
{
label: 'Open',
accelerator: 'Command+O',
},
{
label: 'Close',
accelerator: 'Command+W',
click: function() { mainWindow.close(); }
},
]
},
{
label: 'View',
submenu: [
{
label: 'Reload',
accelerator: 'Command+R',
click: function() { mainWindow.restart(); }
},
{
label: 'Enter Fullscreen',
click: function() { mainWindow.setFullscreen(true); }
},
{
label: 'Toggle DevTools',
accelerator: 'Alt+Command+I',
click: function() { mainWindow.toggleDevTools(); }
},
]
},
];
menu = Menu.buildFromTemplate(template);
mainWindow.setMenu(menu);
}
ipc.on('message', function(processId, routingId, type) {
if (type == 'menu')
menu.popup(mainWindow);
});
});

View File

@@ -7,7 +7,6 @@
This is the default mode of Atom Shell, please follow the instructions in
wiki to get started.
<script type="text/javascript" charset="utf-8">
var ipc = require('ipc');
window.addEventListener('contextmenu', function (e) {

View File

@@ -1,165 +1,33 @@
var app = require('app');
var dialog = require('dialog');
var delegate = require('atom-delegate');
var ipc = require('ipc');
var Menu = require('menu');
var MenuItem = require('menu-item');
var BrowserWindow = require('browser-window');
var path = require('path');
var optimist = require('optimist');
var mainWindow = null;
var menu = null;
// Quit when all windows are closed.
// Quit when all windows are closed and no other one is listening to this.
app.on('window-all-closed', function() {
app.terminate();
if (app.listeners('window-all-closed').length == 1)
app.quit();
});
app.on('open-url', function(event, url) {
dialog.showMessageBox({message: url, buttons: ['OK']});
});
var argv = optimist(process.argv.slice(1)).argv;
delegate.browserMainParts.preMainMessageLoopRun = function() {
app.commandLine.appendSwitch('js-flags', '--harmony_collections');
mainWindow = new BrowserWindow({ width: 800, height: 600 });
mainWindow.loadUrl('file://' + __dirname + '/index.html');
mainWindow.on('page-title-updated', function(event, title) {
event.preventDefault();
this.setTitle('Atom Shell - ' + title);
});
mainWindow.on('closed', function() {
console.log('closed');
mainWindow = null;
});
mainWindow.on('unresponsive', function() {
console.log('unresponsive');
});
var template = [
{
label: 'Atom Shell',
submenu: [
{
label: 'About Atom Shell',
selector: 'orderFrontStandardAboutPanel:'
},
{
type: 'separator'
},
{
label: 'Hide Atom Shell',
accelerator: 'Command+H',
selector: 'hide:'
},
{
label: 'Hide Others',
accelerator: 'Command+Shift+H',
selector: 'hideOtherApplications:'
},
{
label: 'Show All',
selector: 'unhideAllApplications:'
},
{
type: 'separator'
},
{
label: 'Quit',
accelerator: 'Command+Q',
click: function() { app.quit(); }
},
]
},
{
label: 'Edit',
submenu: [
{
label: 'Undo',
accelerator: 'Command+Z',
selector: 'undo:'
},
{
label: 'Redo',
accelerator: 'Shift+Command+Z',
selector: 'redo:'
},
{
type: 'separator'
},
{
label: 'Cut',
accelerator: 'Command+X',
selector: 'cut:'
},
{
label: 'Copy',
accelerator: 'Command+C',
selector: 'copy:'
},
{
label: 'Paste',
accelerator: 'Command+V',
selector: 'paste:'
},
{
label: 'Select All',
accelerator: 'Command+A',
selector: 'selectAll:'
},
]
},
{
label: 'View',
submenu: [
{
label: 'Reload',
accelerator: 'Command+R',
click: function() { BrowserWindow.getFocusedWindow().restart(); }
},
{
label: 'Enter Fullscreen',
click: function() { BrowserWindow.getFocusedWindow().setFullscreen(true); }
},
{
label: 'Toggle DevTools',
accelerator: 'Alt+Command+I',
click: function() { BrowserWindow.getFocusedWindow().toggleDevTools(); }
},
]
},
{
label: 'Window',
submenu: [
{
label: 'Minimize',
accelerator: 'Command+M',
selector: 'performMiniaturize:'
},
{
label: 'Close',
accelerator: 'Command+W',
selector: 'performClose:'
},
{
type: 'separator'
},
{
label: 'Bring All to Front',
selector: 'arrangeInFront:'
},
]
},
];
menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
ipc.on('message', function(processId, routingId, type) {
if (type == 'menu')
menu.popup(mainWindow);
});
// Start the specified app if there is one specified in command line, otherwise
// start the default app.
if (argv._.length > 0) {
try {
require(path.resolve(argv._[0]));
} catch(e) {
if (e.code == 'MODULE_NOT_FOUND') {
console.error(e.stack);
console.error('Specified app is invalid');
process.exit(1);
} else {
throw e;
}
}
} else if (argv.version) {
console.log('v' + process.versions['atom-shell']);
process.exit(0);
} else {
require('./default_app.js');
}

View File

@@ -1,5 +1,8 @@
{
"name" : "atom",
"version" : "0.1.0",
"main" : "main.js"
"name": "atom",
"version": "0.1.0",
"main": "main.js",
"dependencies": {
"optimist": "*"
}
}

View File

@@ -1,38 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BROWSER_FILE_DIALOG_H_
#define BROWSER_FILE_DIALOG_H_
#include <string>
#include <vector>
#include "base/files/file_path.h"
namespace atom {
class NativeWindow;
}
namespace file_dialog {
enum FileDialogProperty {
FILE_DIALOG_OPEN_FILE = 1,
FILE_DIALOG_OPEN_DIRECTORY = 2,
FILE_DIALOG_MULTI_SELECTIONS = 4,
FILE_DIALOG_CREATE_DIRECTORY = 8,
};
bool ShowOpenDialog(const std::string& title,
const base::FilePath& default_path,
int properties,
std::vector<base::FilePath>* paths);
bool ShowSaveDialog(atom::NativeWindow* window,
const std::string& title,
const base::FilePath& default_path,
base::FilePath* path);
} // namespace file_dialog
#endif // BROWSER_FILE_DIALOG_H_

View File

@@ -1,107 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/file_dialog.h"
#import <Cocoa/Cocoa.h>
#include <CoreServices/CoreServices.h>
#include "base/file_util.h"
#include "base/strings/sys_string_conversions.h"
#include "browser/native_window.h"
namespace file_dialog {
namespace {
void SetupDialog(NSSavePanel* dialog,
const std::string& title,
const base::FilePath& default_path) {
if (!title.empty())
[dialog setTitle:base::SysUTF8ToNSString(title)];
NSString* default_dir = nil;
NSString* default_filename = nil;
if (!default_path.empty()) {
if (file_util::DirectoryExists(default_path)) {
default_dir = base::SysUTF8ToNSString(default_path.value());
} else {
default_dir = base::SysUTF8ToNSString(default_path.DirName().value());
default_filename =
base::SysUTF8ToNSString(default_path.BaseName().value());
}
}
if (default_dir)
[dialog setDirectoryURL:[NSURL fileURLWithPath:default_dir]];
if (default_filename)
[dialog setNameFieldStringValue:default_filename];
[dialog setAllowsOtherFileTypes:YES];
}
} // namespace
bool ShowOpenDialog(const std::string& title,
const base::FilePath& default_path,
int properties,
std::vector<base::FilePath>* paths) {
DCHECK(paths);
NSOpenPanel* dialog = [NSOpenPanel openPanel];
SetupDialog(dialog, title, default_path);
[dialog setCanChooseFiles:(properties & FILE_DIALOG_OPEN_FILE)];
if (properties & FILE_DIALOG_OPEN_DIRECTORY)
[dialog setCanChooseDirectories:YES];
if (properties & FILE_DIALOG_CREATE_DIRECTORY)
[dialog setCanCreateDirectories:YES];
if (properties & FILE_DIALOG_MULTI_SELECTIONS)
[dialog setAllowsMultipleSelection:YES];
if ([dialog runModal] == NSFileHandlingPanelCancelButton)
return false;
NSArray* urls = [dialog URLs];
for (NSURL* url in urls)
if ([url isFileURL])
paths->push_back(base::FilePath(base::SysNSStringToUTF8([url path])));
return true;
}
bool ShowSaveDialog(atom::NativeWindow* window,
const std::string& title,
const base::FilePath& default_path,
base::FilePath* path) {
DCHECK(window);
DCHECK(path);
NSSavePanel* dialog = [NSSavePanel savePanel];
SetupDialog(dialog, title, default_path);
[dialog setCanSelectHiddenExtension:YES];
__block bool result = false;
__block base::FilePath ret_path;
[dialog beginSheetModalForWindow:window->GetNativeWindow()
completionHandler:^(NSInteger chosen) {
if (chosen == NSFileHandlingPanelCancelButton ||
![[dialog URL] isFileURL]) {
result = false;
} else {
result = true;
ret_path = base::FilePath(base::SysNSStringToUTF8([[dialog URL] path]));
}
[NSApp stopModal];
}];
[NSApp runModalForWindow:window->GetNativeWindow()];
*path = ret_path;
return result;
}
} // namespace file_dialog

View File

@@ -1,23 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/file_dialog.h"
namespace file_dialog {
bool ShowOpenDialog(const std::string& title,
const base::FilePath& default_path,
int properties,
std::vector<base::FilePath>* paths) {
return false;
}
bool ShowSaveDialog(atom::NativeWindow* window,
const std::string& title,
const base::FilePath& default_path,
base::FilePath* path) {
return false;
}
} // namespace file_dialog

View File

@@ -11,7 +11,7 @@
<key>CFBundleIconFile</key>
<string>atom.icns</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<string>0.6.12</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>

View File

@@ -1,157 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/media/media_capture_devices_dispatcher.h"
#include "base/prefs/pref_service.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/media_devices_monitor.h"
#include "content/public/common/media_stream_request.h"
using content::BrowserThread;
using content::MediaStreamDevices;
namespace {
const content::MediaStreamDevice* FindDefaultDeviceWithId(
const content::MediaStreamDevices& devices,
const std::string& device_id) {
if (devices.empty())
return NULL;
content::MediaStreamDevices::const_iterator iter = devices.begin();
for (; iter != devices.end(); ++iter) {
if (iter->id == device_id) {
return &(*iter);
}
}
return &(*devices.begin());
};
} // namespace
MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() {
return Singleton<MediaCaptureDevicesDispatcher>::get();
}
MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher()
: devices_enumerated_(false) {}
MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {}
const MediaStreamDevices&
MediaCaptureDevicesDispatcher::GetAudioCaptureDevices() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!devices_enumerated_) {
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&content::EnsureMonitorCaptureDevices));
devices_enumerated_ = true;
}
return audio_devices_;
}
const MediaStreamDevices&
MediaCaptureDevicesDispatcher::GetVideoCaptureDevices() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!devices_enumerated_) {
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&content::EnsureMonitorCaptureDevices));
devices_enumerated_ = true;
}
return video_devices_;
}
void MediaCaptureDevicesDispatcher::GetRequestedDevice(
const std::string& requested_device_id,
bool audio,
bool video,
content::MediaStreamDevices* devices) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(audio || video);
if (audio) {
const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices();
const content::MediaStreamDevice* const device =
FindDefaultDeviceWithId(audio_devices, requested_device_id);
if (device)
devices->push_back(*device);
}
if (video) {
const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices();
const content::MediaStreamDevice* const device =
FindDefaultDeviceWithId(video_devices, requested_device_id);
if (device)
devices->push_back(*device);
}
}
void MediaCaptureDevicesDispatcher::GetDefaultDevices(
bool audio,
bool video,
content::MediaStreamDevices* devices) {
if (audio) {
GetRequestedDevice(std::string(), true, false, devices);
}
if (video) {
GetRequestedDevice(std::string(), false, true, devices);
}
}
void MediaCaptureDevicesDispatcher::OnAudioCaptureDevicesChanged(
const content::MediaStreamDevices& devices) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&MediaCaptureDevicesDispatcher::UpdateAudioDevicesOnUIThread,
base::Unretained(this), devices));
}
void MediaCaptureDevicesDispatcher::OnVideoCaptureDevicesChanged(
const content::MediaStreamDevices& devices) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&MediaCaptureDevicesDispatcher::UpdateVideoDevicesOnUIThread,
base::Unretained(this), devices));
}
void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(
int render_process_id,
int render_view_id,
const content::MediaStreamDevice& device,
content::MediaRequestState state) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(
&MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread,
base::Unretained(this), render_process_id, render_view_id, device,
state));
}
void MediaCaptureDevicesDispatcher::UpdateAudioDevicesOnUIThread(
const content::MediaStreamDevices& devices) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
devices_enumerated_ = true;
audio_devices_ = devices;
}
void MediaCaptureDevicesDispatcher::UpdateVideoDevicesOnUIThread(
const content::MediaStreamDevices& devices) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
devices_enumerated_ = true;
video_devices_ = devices;
}
void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread(
int render_process_id,
int render_view_id,
const content::MediaStreamDevice& device,
content::MediaRequestState state) {
}

View File

@@ -1,78 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_MEDIA_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_
#define CHROME_BROWSER_MEDIA_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_
#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/singleton.h"
#include "base/observer_list.h"
#include "content/public/browser/media_observer.h"
#include "content/public/common/media_stream_request.h"
// This singleton is used to receive updates about media events from the content
// layer.
class MediaCaptureDevicesDispatcher : public content::MediaObserver {
public:
static MediaCaptureDevicesDispatcher* GetInstance();
// Helper for picking the device that was requested for an OpenDevice request.
// If the device requested is not available it will revert to using the first
// available one instead or will return an empty list if no devices of the
// requested kind are present.
void GetRequestedDevice(const std::string& requested_device_id,
bool audio,
bool video,
content::MediaStreamDevices* devices);
void GetDefaultDevices(bool audio,
bool video,
content::MediaStreamDevices* devices);
const content::MediaStreamDevices& GetAudioCaptureDevices();
const content::MediaStreamDevices& GetVideoCaptureDevices();
// Overridden from content::MediaObserver:
virtual void OnAudioCaptureDevicesChanged(
const content::MediaStreamDevices& devices) OVERRIDE;
virtual void OnVideoCaptureDevicesChanged(
const content::MediaStreamDevices& devices) OVERRIDE;
virtual void OnMediaRequestStateChanged(
int render_process_id,
int render_view_id,
const content::MediaStreamDevice& device,
content::MediaRequestState state) OVERRIDE;
virtual void OnAudioStreamPlayingChanged(
int render_process_id,
int render_view_id,
int stream_id,
bool playing) OVERRIDE {}
private:
friend struct DefaultSingletonTraits<MediaCaptureDevicesDispatcher>;
MediaCaptureDevicesDispatcher();
virtual ~MediaCaptureDevicesDispatcher();
// Called by the MediaObserver() functions, executed on UI thread.
void UpdateAudioDevicesOnUIThread(const content::MediaStreamDevices& devices);
void UpdateVideoDevicesOnUIThread(const content::MediaStreamDevices& devices);
void UpdateMediaRequestStateOnUIThread(
int render_process_id,
int render_view_id,
const content::MediaStreamDevice& device,
content::MediaRequestState state);
// A list of cached audio capture devices.
content::MediaStreamDevices audio_devices_;
// A list of cached video capture devices.
content::MediaStreamDevices video_devices_;
// Flag to indicate if device enumeration has been done/doing.
// Only accessed on UI thread.
bool devices_enumerated_;
};
#endif // CHROME_BROWSER_MEDIA_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_

View File

@@ -1,86 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/media/media_stream_devices_controller.h"
#include "base/values.h"
#include "browser/media/media_capture_devices_dispatcher.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/media_stream_request.h"
namespace atom {
namespace {
bool HasAnyAvailableDevice() {
const content::MediaStreamDevices& audio_devices =
MediaCaptureDevicesDispatcher::GetInstance()->GetAudioCaptureDevices();
const content::MediaStreamDevices& video_devices =
MediaCaptureDevicesDispatcher::GetInstance()->GetVideoCaptureDevices();
return !audio_devices.empty() || !video_devices.empty();
};
} // namespace
MediaStreamDevicesController::MediaStreamDevicesController(
const content::MediaStreamRequest& request,
const content::MediaResponseCallback& callback)
: request_(request),
callback_(callback),
microphone_requested_(
request_.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE),
webcam_requested_(
request_.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) {
}
MediaStreamDevicesController::~MediaStreamDevicesController() {}
bool MediaStreamDevicesController::TakeAction() {
// Deny the request if there is no device attached to the OS.
if (!HasAnyAvailableDevice()) {
Deny();
return true;
}
Accept();
return true;
}
void MediaStreamDevicesController::Accept() {
// Get the default devices for the request.
content::MediaStreamDevices devices;
if (microphone_requested_ || webcam_requested_) {
switch (request_.request_type) {
case content::MEDIA_OPEN_DEVICE:
// For open device request pick the desired device or fall back to the
// first available of the given type.
MediaCaptureDevicesDispatcher::GetInstance()->GetRequestedDevice(
request_.requested_device_id,
microphone_requested_,
webcam_requested_,
&devices);
break;
case content::MEDIA_DEVICE_ACCESS:
case content::MEDIA_GENERATE_STREAM:
case content::MEDIA_ENUMERATE_DEVICES:
// Get the default devices for the request.
MediaCaptureDevicesDispatcher::GetInstance()->
GetDefaultDevices(microphone_requested_,
webcam_requested_,
&devices);
break;
}
}
LOG(ERROR) << "Accept";
callback_.Run(devices, scoped_ptr<content::MediaStreamUI>());
}
void MediaStreamDevicesController::Deny() {
callback_.Run(content::MediaStreamDevices(),
scoped_ptr<content::MediaStreamUI>());
}
} // namespace atom

View File

@@ -1,46 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_MEDIA_MEDIA_STREAM_DEVICES_CONTROLLER_H_
#define ATOM_BROWSER_MEDIA_MEDIA_STREAM_DEVICES_CONTROLLER_H_
#include "content/public/browser/web_contents_delegate.h"
namespace atom {
class MediaStreamDevicesController {
public:
MediaStreamDevicesController(const content::MediaStreamRequest& request,
const content::MediaResponseCallback& callback);
virtual ~MediaStreamDevicesController();
// Public method to be called before creating the MediaStreamInfoBarDelegate.
// This function will check the content settings exceptions and take the
// corresponding action on exception which matches the request.
bool TakeAction();
// Public methods to be called by MediaStreamInfoBarDelegate;
bool has_audio() const { return microphone_requested_; }
bool has_video() const { return webcam_requested_; }
void Accept();
void Deny();
private:
// The original request for access to devices.
const content::MediaStreamRequest request_;
// The callback that needs to be Run to notify WebRTC of whether access to
// audio/video devices was granted or not.
content::MediaResponseCallback callback_;
bool microphone_requested_;
bool webcam_requested_;
DISALLOW_COPY_AND_ASSIGN(MediaStreamDevicesController);
};
} // namespace atom
#endif // ATOM_BROWSER_MEDIA_MEDIA_STREAM_DEVICES_CONTROLLER_H_

View File

@@ -1,49 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/message_box.h"
#import <Cocoa/Cocoa.h>
#include "base/strings/sys_string_conversions.h"
#include "browser/native_window.h"
#include "browser/nsalert_synchronous_sheet.h"
namespace atom {
int ShowMessageBox(NativeWindow* parent_window,
MessageBoxType type,
const std::vector<std::string>& buttons,
const std::string& title,
const std::string& message,
const std::string& detail) {
// Ignore the title; it's the window title on other platforms and ignorable.
NSAlert* alert = [[[NSAlert alloc] init] autorelease];
[alert setMessageText:base::SysUTF8ToNSString(message)];
[alert setInformativeText:base::SysUTF8ToNSString(detail)];
switch (type) {
case MESSAGE_BOX_TYPE_INFORMATION:
[alert setAlertStyle:NSInformationalAlertStyle];
break;
case MESSAGE_BOX_TYPE_WARNING:
[alert setAlertStyle:NSWarningAlertStyle];
break;
default:
break;
}
for (size_t i = 0; i < buttons.size(); ++i) {
NSString* title = base::SysUTF8ToNSString(buttons[i]);
NSButton* button = [alert addButtonWithTitle:title];
[button setTag:i];
}
if (parent_window)
return [alert runModalSheetForWindow:parent_window->GetNativeWindow()];
else
return [alert runModal];
}
} // namespace atom

View File

@@ -1,18 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/message_box.h"
namespace atom {
int ShowMessageBox(NativeWindow* parent_window,
MessageBoxType type,
const std::vector<std::string>& buttons,
const std::string& title,
const std::string& message,
const std::string& detail) {
return 0;
}
} // namespace atom

View File

@@ -6,6 +6,8 @@
#include <string>
#include "base/file_util.h"
#include "base/message_loop.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "brightray/browser/inspectable_web_contents.h"
@@ -14,8 +16,8 @@
#include "browser/atom_browser_context.h"
#include "browser/atom_browser_main_parts.h"
#include "browser/atom_javascript_dialog_manager.h"
#include "browser/media/media_stream_devices_controller.h"
#include "browser/window_list.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/invalidate_type.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/notification_details.h"
@@ -23,12 +25,15 @@
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "common/api/api_messages.h"
#include "common/options_switches.h"
#include "ipc/ipc_message_macros.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/point.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/size.h"
#include "webkit/glue/image_decoder.h"
using content::NavigationEntry;
@@ -37,9 +42,19 @@ namespace atom {
NativeWindow::NativeWindow(content::WebContents* web_contents,
base::DictionaryValue* options)
: content::WebContentsObserver(web_contents),
has_frame_(true),
is_closed_(false),
not_responding_(false),
inspectable_web_contents_(
brightray::InspectableWebContents::Create(web_contents)) {
options->GetBoolean(switches::kFrame, &has_frame_);
std::string icon;
if (options->GetString(switches::kIcon, &icon)) {
if (!SetIcon(icon))
LOG(ERROR) << "Failed to set icon to " << icon;
}
web_contents->SetDelegate(this);
WindowList::AddWindow(this);
@@ -135,6 +150,18 @@ void NativeWindow::CloseDevTools() {
inspectable_web_contents()->GetView()->CloseDevTools();
}
bool NativeWindow::IsDevToolsOpened() {
return inspectable_web_contents()->IsDevToolsOpened();
}
void NativeWindow::InspectElement(int x, int y) {
OpenDevTools();
content::RenderViewHost* rvh = GetWebContents()->GetRenderViewHost();
scoped_refptr<content::DevToolsAgentHost> agent(
content::DevToolsAgentHost::GetOrCreateFor(rvh));
agent->InspectElement(x, y);
}
void NativeWindow::FocusOnWebView() {
GetWebContents()->GetRenderViewHost()->Focus();
}
@@ -143,10 +170,45 @@ void NativeWindow::BlurWebView() {
GetWebContents()->GetRenderViewHost()->Blur();
}
bool NativeWindow::IsWebViewFocused() {
return GetWebContents()->GetRenderViewHost()->GetView()->HasFocus();
}
void NativeWindow::RestartHangMonitorTimeout() {
GetWebContents()->GetRenderViewHost()->RestartHangMonitorTimeout();
}
bool NativeWindow::SetIcon(const std::string& str_path) {
base::FilePath path = base::FilePath::FromUTF8Unsafe(str_path);
// Read the file from disk.
std::string file_contents;
if (path.empty() || !file_util::ReadFileToString(path, &file_contents))
return false;
// Decode the bitmap using WebKit's image decoder.
const unsigned char* data =
reinterpret_cast<const unsigned char*>(file_contents.data());
webkit_glue::ImageDecoder decoder;
scoped_ptr<SkBitmap> decoded(new SkBitmap());
*decoded = decoder.Decode(data, file_contents.length());
if (decoded->empty())
return false; // Unable to decode.
icon_ = gfx::Image::CreateFrom1xBitmap(*decoded.release());
return true;
}
void NativeWindow::CapturePage(const gfx::Rect& rect,
const CapturePageCallback& callback) {
GetWebContents()->GetRenderViewHost()->CopyFromBackingStore(
rect,
gfx::Size(),
base::Bind(&NativeWindow::OnCapturePageDone,
base::Unretained(this),
callback));
}
void NativeWindow::CloseWebContents() {
bool prevent_default = false;
FOR_EACH_OBSERVER(NativeWindowObserver,
@@ -223,14 +285,6 @@ void NativeWindow::RequestToLockMouse(content::WebContents* web_contents,
GetWebContents()->GotResponseToLockMouseRequest(true);
}
void NativeWindow::RequestMediaAccessPermission(
content::WebContents* web_contents,
const content::MediaStreamRequest& request,
const content::MediaResponseCallback& callback) {
MediaStreamDevicesController controller(request, callback);
controller.TakeAction();
}
bool NativeWindow::CanOverscrollContent() const {
return false;
}
@@ -243,6 +297,13 @@ void NativeWindow::DeactivateContents(content::WebContents* contents) {
BlurWebView();
}
void NativeWindow::LoadingStateChanged(content::WebContents* source) {
bool is_loading = source->IsLoading();
FOR_EACH_OBSERVER(NativeWindowObserver,
observers_,
OnLoadingStateChanged(is_loading));
}
void NativeWindow::MoveContents(content::WebContents* source,
const gfx::Rect& pos) {
SetPosition(pos.origin());
@@ -266,31 +327,44 @@ bool NativeWindow::IsPopupOrPanel(const content::WebContents* source) const {
}
void NativeWindow::RendererUnresponsive(content::WebContents* source) {
FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnRendererUnresponsive());
not_responding_ = true;
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&NativeWindow::RendererUnresponsiveDelayed,
base::Unretained(this)),
base::TimeDelta::FromSeconds(1));
}
void NativeWindow::RendererResponsive(content::WebContents* source) {
not_responding_ = false;
FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnRendererResponsive());
}
void NativeWindow::NavigationStateChanged(const content::WebContents* source,
unsigned changed_flags) {
if (changed_flags == content::INVALIDATE_TYPE_TAB &&
source->IsCrashed())
FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnRendererCrashed());
}
bool NativeWindow::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(NativeWindow, message)
IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message, OnRendererMessage)
IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message_Sync, OnRendererMessageSync)
IPC_MESSAGE_HANDLER_DELAY_REPLY(AtomViewHostMsg_Message_Sync,
OnRendererMessageSync)
IPC_MESSAGE_HANDLER(AtomViewHostMsg_UpdateDraggableRegions,
UpdateDraggableRegions)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void NativeWindow::RenderViewGone(base::TerminationStatus status) {
FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnRendererCrashed());
}
void NativeWindow::RendererUnresponsiveDelayed() {
if (not_responding_)
FOR_EACH_OBSERVER(NativeWindowObserver,
observers_,
OnRendererUnresponsive());
}
void NativeWindow::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
@@ -311,7 +385,16 @@ void NativeWindow::Observe(int type,
}
}
void NativeWindow::OnRendererMessage(const std::string& channel,
void NativeWindow::OnCapturePageDone(const CapturePageCallback& callback,
bool succeed,
const SkBitmap& bitmap) {
std::vector<unsigned char> data;
if (succeed)
gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, true, &data);
callback.Run(data);
}
void NativeWindow::OnRendererMessage(const string16& channel,
const base::ListValue& args) {
AtomBrowserMainParts::Get()->atom_bindings()->OnRendererMessage(
GetWebContents()->GetRenderProcessHost()->GetID(),
@@ -320,15 +403,16 @@ void NativeWindow::OnRendererMessage(const std::string& channel,
args);
}
void NativeWindow::OnRendererMessageSync(const std::string& channel,
void NativeWindow::OnRendererMessageSync(const string16& channel,
const base::ListValue& args,
base::DictionaryValue* result) {
IPC::Message* reply_msg) {
AtomBrowserMainParts::Get()->atom_bindings()->OnRendererMessageSync(
GetWebContents()->GetRenderProcessHost()->GetID(),
GetWebContents()->GetRoutingID(),
channel,
args,
result);
this,
reply_msg);
}
} // namespace atom

View File

@@ -12,8 +12,9 @@
#include "browser/native_window_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h"
#include "ui/gfx/image/image.h"
#include "vendor/brightray/browser/default_web_contents_delegate.h"
namespace base {
class DictionaryValue;
@@ -35,14 +36,22 @@ class Rect;
class Size;
}
namespace IPC {
class Message;
}
namespace atom {
class AtomJavaScriptDialogManager;
struct DraggableRegion;
class NativeWindow : public content::WebContentsDelegate,
class NativeWindow : public brightray::DefaultWebContentsDelegate,
public content::WebContentsObserver,
public content::NotificationObserver {
public:
typedef base::Callback<void(const std::vector<unsigned char>& buffer)>
CapturePageCallback;
virtual ~NativeWindow();
// Create window with existing WebContents.
@@ -64,6 +73,7 @@ class NativeWindow : public content::WebContentsDelegate,
virtual bool IsFocused() = 0;
virtual void Show() = 0;
virtual void Hide() = 0;
virtual bool IsVisible() = 0;
virtual void Maximize() = 0;
virtual void Unmaximize() = 0;
virtual void Minimize() = 0;
@@ -93,9 +103,18 @@ class NativeWindow : public content::WebContentsDelegate,
virtual bool IsClosed() const { return is_closed_; }
virtual void OpenDevTools();
virtual void CloseDevTools();
virtual bool IsDevToolsOpened();
virtual void InspectElement(int x, int y);
virtual void FocusOnWebView();
virtual void BlurWebView();
virtual bool IsWebViewFocused();
virtual void RestartHangMonitorTimeout();
virtual bool SetIcon(const std::string& path);
// Captures the page with |rect|, |callback| would be called when capturing is
// done.
virtual void CapturePage(const gfx::Rect& rect,
const CapturePageCallback& callback);
// The same with closing a tab in a real browser.
//
@@ -112,6 +131,8 @@ class NativeWindow : public content::WebContentsDelegate,
observers_.RemoveObserver(obs);
}
bool has_frame() const { return has_frame_; }
protected:
explicit NativeWindow(content::WebContents* web_contents,
base::DictionaryValue* options);
@@ -123,6 +144,10 @@ class NativeWindow : public content::WebContentsDelegate,
void NotifyWindowClosed();
void NotifyWindowBlur();
// Called when the window needs to update its draggable region.
virtual void UpdateDraggableRegions(
const std::vector<DraggableRegion>& regions) = 0;
// Implementations of content::WebContentsDelegate.
virtual void WebContentsCreated(content::WebContents* source_contents,
int64 source_frame_id,
@@ -137,13 +162,10 @@ class NativeWindow : public content::WebContentsDelegate,
virtual void RequestToLockMouse(content::WebContents* web_contents,
bool user_gesture,
bool last_unlocked_by_target) OVERRIDE;
virtual void RequestMediaAccessPermission(
content::WebContents* web_contents,
const content::MediaStreamRequest& request,
const content::MediaResponseCallback& callback) OVERRIDE;
virtual bool CanOverscrollContent() const OVERRIDE;
virtual void ActivateContents(content::WebContents* contents) OVERRIDE;
virtual void DeactivateContents(content::WebContents* contents) OVERRIDE;
virtual void LoadingStateChanged(content::WebContents* source) OVERRIDE;
virtual void MoveContents(content::WebContents* source,
const gfx::Rect& pos) OVERRIDE;
virtual void CloseContents(content::WebContents* source) OVERRIDE;
@@ -151,10 +173,9 @@ class NativeWindow : public content::WebContentsDelegate,
const content::WebContents* source) const OVERRIDE;
virtual void RendererUnresponsive(content::WebContents* source) OVERRIDE;
virtual void RendererResponsive(content::WebContents* source) OVERRIDE;
virtual void NavigationStateChanged(const content::WebContents* source,
unsigned changed_flags) OVERRIDE;
// Implementations of content::WebContentsObserver.
virtual void RenderViewGone(base::TerminationStatus status) OVERRIDE;
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
// Implementations of content::NotificationObserver
@@ -162,13 +183,26 @@ class NativeWindow : public content::WebContentsDelegate,
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE;
// Whether window has standard frame.
bool has_frame_;
// Window icon.
gfx::Image icon_;
private:
void OnRendererMessage(const std::string& channel,
void RendererUnresponsiveDelayed();
// Called when CapturePage has done.
void OnCapturePageDone(const CapturePageCallback& callback,
bool succeed,
const SkBitmap& bitmap);
void OnRendererMessage(const string16& channel,
const base::ListValue& args);
void OnRendererMessageSync(const std::string& channel,
void OnRendererMessageSync(const string16& channel,
const base::ListValue& args,
base::DictionaryValue* result);
IPC::Message* reply_msg);
// Notification manager.
content::NotificationRegistrar registrar_;
@@ -176,8 +210,12 @@ class NativeWindow : public content::WebContentsDelegate,
// Observers of this window.
ObserverList<NativeWindowObserver> observers_;
// The windows has been closed.
bool is_closed_;
// The window is not responding.
bool not_responding_;
scoped_ptr<AtomJavaScriptDialogManager> dialog_manager_;
scoped_ptr<brightray::InspectableWebContents> inspectable_web_contents_;

View File

@@ -10,6 +10,8 @@
#include "base/memory/scoped_ptr.h"
#include "browser/native_window.h"
class SkRegion;
namespace atom {
class NativeWindowMac : public NativeWindow {
@@ -26,6 +28,7 @@ class NativeWindowMac : public NativeWindow {
virtual bool IsFocused() OVERRIDE;
virtual void Show() OVERRIDE;
virtual void Hide() OVERRIDE;
virtual bool IsVisible() OVERRIDE;
virtual void Maximize() OVERRIDE;
virtual void Unmaximize() OVERRIDE;
virtual void Minimize() OVERRIDE;
@@ -52,11 +55,25 @@ class NativeWindowMac : public NativeWindow {
virtual bool IsKiosk() OVERRIDE;
virtual gfx::NativeWindow GetNativeWindow() OVERRIDE;
NSWindow*& window() { return window_; }
void NotifyWindowBlur() { NativeWindow::NotifyWindowBlur(); }
// Returns true if |point| in local Cocoa coordinate system falls within
// the draggable region.
bool IsWithinDraggableRegion(NSPoint point) const;
// Called to handle a mouse event.
void HandleMouseEvent(NSEvent* event);
// Clip web view to rounded corner.
void ClipWebView();
NSWindow*& window() { return window_; }
SkRegion* draggable_region() const { return draggable_region_.get(); }
protected:
virtual void UpdateDraggableRegions(
const std::vector<DraggableRegion>& regions) OVERRIDE;
// Implementations of content::WebContentsDelegate.
virtual void HandleKeyboardEvent(
content::WebContents*,
@@ -65,6 +82,9 @@ class NativeWindowMac : public NativeWindow {
private:
void InstallView();
void UninstallView();
void InstallDraggableRegionViews();
void UpdateDraggableRegionsForCustomDrag(
const std::vector<DraggableRegion>& regions);
NSWindow* window_;
@@ -72,6 +92,18 @@ class NativeWindowMac : public NativeWindow {
NSInteger attention_request_id_; // identifier from requestUserAttention
// For system drag, the whole window is draggable and the non-draggable areas
// have to been explicitly excluded.
std::vector<gfx::Rect> system_drag_exclude_areas_;
// For custom drag, the whole window is non-draggable and the draggable region
// has to been explicitly provided.
scoped_ptr<SkRegion> draggable_region_; // used in custom drag.
// Mouse location since the last mouse event, in screen coordinates. This is
// used in custom drag to compute the window movement.
NSPoint last_mouse_offset_;
DISALLOW_COPY_AND_ASSIGN(NativeWindowMac);
};

View File

@@ -13,15 +13,22 @@
#include "base/mac/mac_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/values.h"
#import "browser/atom_event_processing_window.h"
#import "browser/ui/atom_event_processing_window.h"
#include "brightray/browser/inspectable_web_contents.h"
#include "brightray/browser/inspectable_web_contents_view.h"
#include "common/draggable_region.h"
#include "common/options_switches.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
#include "content/public/browser/render_view_host.h"
static const CGFloat kAtomWindowCornerRadius = 4.0;
@interface NSView (PrivateMethods)
- (CGFloat)roundedCornerRadius;
@end
@interface AtomNSWindowDelegate : NSObject<NSWindowDelegate> {
@private
atom::NativeWindowMac* shell_;
@@ -41,6 +48,11 @@
shell_->NotifyWindowBlur();
}
- (void)windowDidResize:(NSNotification*)otification {
if (!shell_->has_frame())
shell_->ClipWebView();
}
- (void)windowWillClose:(NSNotification*)notification {
shell_->window() = nil;
[self autorelease];
@@ -54,10 +66,17 @@
return NO;
}
- (void)windowDidExitFullScreen:(NSNotification*)notification {
if (!shell_->has_frame()) {
NSWindow* window = shell_->GetNativeWindow();
[[window standardWindowButton:NSWindowFullScreenButton] setHidden:YES];
}
}
@end
@interface AtomNSWindow : AtomEventProcessingWindow {
@private
@protected
atom::NativeWindowMac* shell_;
}
- (void)setShell:(atom::NativeWindowMac*)shell;
@@ -80,6 +99,41 @@
@end
@interface ControlRegionView : NSView {
@private
atom::NativeWindowMac* shellWindow_; // Weak; owns self.
}
@end
@implementation ControlRegionView
- (id)initWithShellWindow:(atom::NativeWindowMac*)shellWindow {
if ((self = [super init]))
shellWindow_ = shellWindow;
return self;
}
- (BOOL)mouseDownCanMoveWindow {
return NO;
}
- (NSView*)hitTest:(NSPoint)aPoint {
if (!shellWindow_->IsWithinDraggableRegion(aPoint)) {
return nil;
}
return self;
}
- (void)mouseDown:(NSEvent*)event {
shellWindow_->HandleMouseEvent(event);
}
- (void)mouseDragged:(NSEvent*)event {
shellWindow_->HandleMouseEvent(event);
}
@end
namespace atom {
NativeWindowMac::NativeWindowMac(content::WebContents* web_contents,
@@ -93,21 +147,24 @@ NativeWindowMac::NativeWindowMac(content::WebContents* web_contents,
NSRect main_screen_rect = [[[NSScreen screens] objectAtIndex:0] frame];
NSRect cocoa_bounds = NSMakeRect(
(NSWidth(main_screen_rect) - width) / 2,
(NSHeight(main_screen_rect) - height) / 2,
round((NSWidth(main_screen_rect) - width) / 2) ,
round((NSHeight(main_screen_rect) - height) / 2),
width,
height);
NSUInteger style_mask = NSTitledWindowMask | NSClosableWindowMask |
NSMiniaturizableWindowMask | NSResizableWindowMask |
NSTexturedBackgroundWindowMask;
AtomNSWindow* atom_window = [[AtomNSWindow alloc]
AtomNSWindow* atomWindow;
atomWindow = [[AtomNSWindow alloc]
initWithContentRect:cocoa_bounds
styleMask:style_mask
styleMask:NSTitledWindowMask | NSClosableWindowMask |
NSMiniaturizableWindowMask | NSResizableWindowMask |
NSTexturedBackgroundWindowMask
backing:NSBackingStoreBuffered
defer:YES];
[atom_window setShell:this];
window_ = atom_window;
[atomWindow setShell:this];
window_ = atomWindow;
[window() setDelegate:[[AtomNSWindowDelegate alloc] initWithShell:this]];
// Disable fullscreen button when 'fullscreen' is specified to false.
@@ -119,6 +176,9 @@ NativeWindowMac::NativeWindowMac(content::WebContents* web_contents,
[window() setCollectionBehavior:collectionBehavior];
}
NSView* view = inspectable_web_contents()->GetView()->GetNativeView();
[view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
InstallView();
}
@@ -149,7 +209,10 @@ void NativeWindowMac::Move(const gfx::Rect& pos) {
}
void NativeWindowMac::Focus(bool focus) {
if (focus && [window() isVisible]) {
if (!IsVisible())
return;
if (focus) {
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
[window() makeKeyAndOrderFront:nil];
} else {
@@ -169,6 +232,10 @@ void NativeWindowMac::Hide() {
[window() orderOut:nil];
}
bool NativeWindowMac::IsVisible() {
return [window() isVisible];
}
void NativeWindowMac::Maximize() {
[window() zoom:nil];
}
@@ -300,7 +367,7 @@ void NativeWindowMac::SetKiosk(bool kiosk) {
if (kiosk) {
NSApplicationPresentationOptions options =
NSApplicationPresentationHideDock +
NSApplicationPresentationHideMenuBar +
NSApplicationPresentationHideMenuBar +
NSApplicationPresentationDisableAppleMenu +
NSApplicationPresentationDisableProcessSwitching +
NSApplicationPresentationDisableForceQuit +
@@ -324,6 +391,43 @@ gfx::NativeWindow NativeWindowMac::GetNativeWindow() {
return window();
}
bool NativeWindowMac::IsWithinDraggableRegion(NSPoint point) const {
if (!draggable_region_)
return false;
NSView* webView = GetWebContents()->GetView()->GetNativeView();
NSInteger webViewHeight = NSHeight([webView bounds]);
// |draggable_region_| is stored in local platform-indepdent coordiate system
// while |point| is in local Cocoa coordinate system. Do the conversion
// to match these two.
return draggable_region_->contains(point.x, webViewHeight - point.y);
}
void NativeWindowMac::HandleMouseEvent(NSEvent* event) {
NSPoint current_mouse_location =
[window() convertBaseToScreen:[event locationInWindow]];
if ([event type] == NSLeftMouseDown) {
NSPoint frame_origin = [window() frame].origin;
last_mouse_offset_ = NSMakePoint(
frame_origin.x - current_mouse_location.x,
frame_origin.y - current_mouse_location.y);
} else if ([event type] == NSLeftMouseDragged) {
[window() setFrameOrigin:NSMakePoint(
current_mouse_location.x + last_mouse_offset_.x,
current_mouse_location.y + last_mouse_offset_.y)];
}
}
void NativeWindowMac::UpdateDraggableRegions(
const std::vector<DraggableRegion>& regions) {
// Draggable region is not supported for non-frameless window.
if (has_frame_)
return;
UpdateDraggableRegionsForCustomDrag(regions);
InstallDraggableRegionViews();
}
void NativeWindowMac::HandleKeyboardEvent(
content::WebContents*,
const content::NativeWebKeyboardEvent& event) {
@@ -339,16 +443,97 @@ void NativeWindowMac::HandleKeyboardEvent(
void NativeWindowMac::InstallView() {
NSView* view = inspectable_web_contents()->GetView()->GetNativeView();
[view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[view setFrame:[[window() contentView] bounds]];
[[window() contentView] addSubview:view];
if (has_frame_) {
[view setFrame:[[window() contentView] bounds]];
[[window() contentView] addSubview:view];
} else {
NSView* frameView = [[window() contentView] superview];
[view setFrame:[frameView bounds]];
[frameView addSubview:view];
ClipWebView();
[[window() standardWindowButton:NSWindowZoomButton] setHidden:YES];
[[window() standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
[[window() standardWindowButton:NSWindowCloseButton] setHidden:YES];
[[window() standardWindowButton:NSWindowFullScreenButton] setHidden:YES];
}
}
void NativeWindowMac::UninstallView() {
NSView* view = GetWebContents()->GetView()->GetNativeView();
NSView* view = inspectable_web_contents()->GetView()->GetNativeView();
[view removeFromSuperview];
}
void NativeWindowMac::ClipWebView() {
NSView* view = GetWebContents()->GetView()->GetNativeView();
view.wantsLayer = YES;
view.layer.masksToBounds = YES;
view.layer.cornerRadius = kAtomWindowCornerRadius;
}
void NativeWindowMac::InstallDraggableRegionViews() {
DCHECK(!has_frame_);
// All ControlRegionViews should be added as children of the WebContentsView,
// because WebContentsView will be removed and re-added when entering and
// leaving fullscreen mode.
NSView* webView = GetWebContents()->GetView()->GetNativeView();
NSInteger webViewHeight = NSHeight([webView bounds]);
// Remove all ControlRegionViews that are added last time.
// Note that [webView subviews] returns the view's mutable internal array and
// it should be copied to avoid mutating the original array while enumerating
// it.
scoped_nsobject<NSArray> subviews([[webView subviews] copy]);
for (NSView* subview in subviews.get())
if ([subview isKindOfClass:[ControlRegionView class]])
[subview removeFromSuperview];
// Create and add ControlRegionView for each region that needs to be excluded
// from the dragging.
for (std::vector<gfx::Rect>::const_iterator iter =
system_drag_exclude_areas_.begin();
iter != system_drag_exclude_areas_.end();
++iter) {
scoped_nsobject<NSView> controlRegion(
[[ControlRegionView alloc] initWithShellWindow:this]);
[controlRegion setFrame:NSMakeRect(iter->x(),
webViewHeight - iter->bottom(),
iter->width(),
iter->height())];
[webView addSubview:controlRegion];
}
}
void NativeWindowMac::UpdateDraggableRegionsForCustomDrag(
const std::vector<DraggableRegion>& regions) {
// We still need one ControlRegionView to cover the whole window such that
// mouse events could be captured.
NSView* web_view = GetWebContents()->GetView()->GetNativeView();
gfx::Rect window_bounds(
0, 0, NSWidth([web_view bounds]), NSHeight([web_view bounds]));
system_drag_exclude_areas_.clear();
system_drag_exclude_areas_.push_back(window_bounds);
// Aggregate the draggable areas and non-draggable areas such that hit test
// could be performed easily.
SkRegion* draggable_region = new SkRegion;
for (std::vector<DraggableRegion>::const_iterator iter = regions.begin();
iter != regions.end();
++iter) {
const DraggableRegion& region = *iter;
draggable_region->op(
region.bounds.x(),
region.bounds.y(),
region.bounds.right(),
region.bounds.bottom(),
region.draggable ? SkRegion::kUnion_Op : SkRegion::kDifference_Op);
}
draggable_region_.reset(draggable_region);
}
// static
NativeWindow* NativeWindow::Create(content::WebContents* web_contents,
base::DictionaryValue* options) {

View File

@@ -17,6 +17,9 @@ class NativeWindowObserver {
virtual void OnPageTitleUpdated(bool* prevent_default,
const std::string& title) {}
// Called when the window is starting or is done loading a page.
virtual void OnLoadingStateChanged(bool is_loading) {}
// Called when the window is gonna closed.
virtual void WillCloseWindow(bool* prevent_default) {}

View File

@@ -4,12 +4,24 @@
#include "browser/native_window_win.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "browser/api/atom_api_menu.h"
#include "browser/ui/win/menu_2.h"
#include "browser/ui/win/native_menu_win.h"
#include "common/draggable_region.h"
#include "common/options_switches.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
#include "ui/gfx/path.h"
#include "ui/base/models/simple_menu_model.h"
#include "ui/views/controls/webview/webview.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/native_widget_win.h"
#include "ui/views/window/client_view.h"
#include "ui/views/window/native_frame_view.h"
@@ -17,24 +29,52 @@ namespace atom {
namespace {
const int kResizeInsideBoundsSize = 5;
const int kResizeAreaCornerSize = 16;
// Wrapper of NativeWidgetWin to handle WM_MENUCOMMAND messages, which are
// triggered by window menus.
class MenuCommandNativeWidget : public views::NativeWidgetWin {
public:
explicit MenuCommandNativeWidget(NativeWindowWin* delegate)
: views::NativeWidgetWin(delegate->window()),
delegate_(delegate) {}
virtual ~MenuCommandNativeWidget() {}
protected:
virtual bool PreHandleMSG(UINT message,
WPARAM w_param,
LPARAM l_param,
LRESULT* result) OVERRIDE {
if (message == WM_MENUCOMMAND) {
delegate_->OnMenuCommand(w_param, reinterpret_cast<HMENU>(l_param));
*result = 0;
return true;
} else {
return false;
}
}
private:
NativeWindowWin* delegate_;
DISALLOW_COPY_AND_ASSIGN(MenuCommandNativeWidget);
};
class NativeWindowClientView : public views::ClientView {
public:
NativeWindowClientView(views::Widget* widget,
views::View* contents_view,
NativeWindowWin* shell)
: views::ClientView(widget, contents_view),
shell_(shell) {
NativeWindowWin* contents_view)
: views::ClientView(widget, contents_view) {
}
virtual ~NativeWindowClientView() {}
virtual bool CanClose() OVERRIDE {
shell_->CloseWebContents();
static_cast<NativeWindowWin*>(contents_view())->CloseWebContents();
return false;
}
private:
NativeWindowWin* shell_;
DISALLOW_COPY_AND_ASSIGN(NativeWindowClientView);
};
@@ -60,6 +100,99 @@ class NativeWindowFrameView : public views::NativeFrameView {
DISALLOW_COPY_AND_ASSIGN(NativeWindowFrameView);
};
class NativeWindowFramelessView : public views::NonClientFrameView {
public:
explicit NativeWindowFramelessView(views::Widget* frame,
NativeWindowWin* shell)
: frame_(frame),
shell_(shell) {
}
virtual ~NativeWindowFramelessView() {}
// views::NonClientFrameView implementations:
virtual gfx::Rect NativeWindowFramelessView::GetBoundsForClientView() const
OVERRIDE {
return bounds();
}
virtual gfx::Rect NativeWindowFramelessView::GetWindowBoundsForClientBounds(
const gfx::Rect& client_bounds) const OVERRIDE {
gfx::Rect window_bounds = client_bounds;
// Enforce minimum size (1, 1) in case that client_bounds is passed with
// empty size. This could occur when the frameless window is being
// initialized.
if (window_bounds.IsEmpty()) {
window_bounds.set_width(1);
window_bounds.set_height(1);
}
return window_bounds;
}
virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE {
if (frame_->IsFullscreen())
return HTCLIENT;
// Check the frame first, as we allow a small area overlapping the contents
// to be used for resize handles.
bool can_ever_resize = frame_->widget_delegate() ?
frame_->widget_delegate()->CanResize() :
false;
// Don't allow overlapping resize handles when the window is maximized or
// fullscreen, as it can't be resized in those states.
int resize_border =
frame_->IsMaximized() || frame_->IsFullscreen() ? 0 :
kResizeInsideBoundsSize;
int frame_component = GetHTComponentForFrame(point,
resize_border,
resize_border,
kResizeAreaCornerSize,
kResizeAreaCornerSize,
can_ever_resize);
if (frame_component != HTNOWHERE)
return frame_component;
// Check for possible draggable region in the client area for the frameless
// window.
if (shell_->draggable_region() &&
shell_->draggable_region()->contains(point.x(), point.y()))
return HTCAPTION;
int client_component = frame_->client_view()->NonClientHitTest(point);
if (client_component != HTNOWHERE)
return client_component;
// Caption is a safe default.
return HTCAPTION;
}
virtual void GetWindowMask(const gfx::Size& size,
gfx::Path* window_mask) OVERRIDE {}
virtual void ResetWindowControls() OVERRIDE {}
virtual void UpdateWindowIcon() OVERRIDE {}
virtual void UpdateWindowTitle() OVERRIDE {}
// views::View implementations:
virtual gfx::Size NativeWindowFramelessView::GetPreferredSize() OVERRIDE {
gfx::Size pref = frame_->client_view()->GetPreferredSize();
gfx::Rect bounds(0, 0, pref.width(), pref.height());
return frame_->non_client_view()->GetWindowBoundsForClientBounds(
bounds).size();
}
virtual gfx::Size GetMinimumSize() OVERRIDE {
return shell_->GetMinimumSize();
}
virtual gfx::Size GetMaximumSize() OVERRIDE {
return shell_->GetMaximumSize();
}
private:
views::Widget* frame_;
NativeWindowWin* shell_;
DISALLOW_COPY_AND_ASSIGN(NativeWindowFramelessView);
};
} // namespace
@@ -71,6 +204,8 @@ NativeWindowWin::NativeWindowWin(content::WebContents* web_contents,
resizable_(true) {
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
params.delegate = this;
params.native_widget = new MenuCommandNativeWidget(this);
params.remove_standard_frame = !has_frame_;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
window_->set_frame_type(views::Widget::FRAME_TYPE_FORCE_NATIVE);
window_->Init(params);
@@ -82,7 +217,10 @@ NativeWindowWin::NativeWindowWin(content::WebContents* web_contents,
gfx::Size size(width, height);
window_->CenterWindow(size);
window_->UpdateWindowIcon();
web_view_->SetWebContents(web_contents);
OnViewWasResized();
}
NativeWindowWin::~NativeWindowWin() {
@@ -127,6 +265,10 @@ void NativeWindowWin::Unmaximize() {
window_->Restore();
}
bool NativeWindowWin::IsVisible() {
return window_->IsVisible();
}
void NativeWindowWin::Minimize() {
window_->Minimize();
}
@@ -221,15 +363,92 @@ gfx::NativeWindow NativeWindowWin::GetNativeWindow() {
return window_->GetNativeView();
}
void NativeWindowWin::OnMenuCommand(int position, HMENU menu) {
DCHECK(menu_);
menu_->wrapper()->OnMenuCommand(position, menu);
}
void NativeWindowWin::SetMenu(ui::MenuModel* menu_model) {
menu_.reset(new atom::Menu2(menu_model, true));
::SetMenu(GetNativeWindow(), menu_->GetNativeMenu());
RegisterAccelerators();
}
void NativeWindowWin::UpdateDraggableRegions(
const std::vector<DraggableRegion>& regions) {
if (has_frame_)
return;
SkRegion* draggable_region = new SkRegion;
// By default, the whole window is non-draggable. We need to explicitly
// include those draggable regions.
for (std::vector<DraggableRegion>::const_iterator iter = regions.begin();
iter != regions.end(); ++iter) {
const DraggableRegion& region = *iter;
draggable_region->op(
region.bounds.x(),
region.bounds.y(),
region.bounds.right(),
region.bounds.bottom(),
region.draggable ? SkRegion::kUnion_Op : SkRegion::kDifference_Op);
}
draggable_region_.reset(draggable_region);
OnViewWasResized();
}
void NativeWindowWin::HandleKeyboardEvent(
content::WebContents*,
const content::NativeWebKeyboardEvent& event) {
if (event.type == WebKit::WebInputEvent::KeyUp) {
ui::Accelerator accelerator(
static_cast<ui::KeyboardCode>(event.windowsKeyCode),
content::GetModifiersFromNativeWebKeyboardEvent(event));
if (GetFocusManager()->ProcessAccelerator(accelerator)) {
return;
}
}
// Any unhandled keyboard/character messages should be defproced.
// This allows stuff like F10, etc to work correctly.
DefWindowProc(event.os_event.hwnd, event.os_event.message,
event.os_event.wParam, event.os_event.lParam);
}
void NativeWindowWin::Layout() {
DCHECK(web_view_);
web_view_->SetBounds(0, 0, width(), height());
OnViewWasResized();
}
void NativeWindowWin::ViewHierarchyChanged(bool is_add,
views::View* parent,
views::View* child) {
if (is_add && child == this)
AddChildView(web_view_);
}
bool NativeWindowWin::AcceleratorPressed(
const ui::Accelerator& accelerator) {
if (ContainsKey(accelerator_table_, accelerator)) {
const MenuItem& item = accelerator_table_[accelerator];
item.model->ActivatedAt(item.position);
return true;
} else {
return false;
}
}
void NativeWindowWin::DeleteDelegate() {
// Do nothing, window is managed by users.
}
views::View* NativeWindowWin::GetInitiallyFocusedView() {
return web_view_;
}
bool NativeWindowWin::CanResize() const {
return resizable_;
}
@@ -246,8 +465,15 @@ bool NativeWindowWin::ShouldHandleSystemCommands() const {
return true;
}
bool NativeWindowWin::ShouldShowWindowIcon() const {
return true;
gfx::ImageSkia NativeWindowWin::GetWindowAppIcon() {
if (icon_.IsEmpty())
return gfx::ImageSkia();
else
return *icon_.ToImageSkia();
}
gfx::ImageSkia NativeWindowWin::GetWindowIcon() {
return GetWindowAppIcon();
}
views::Widget* NativeWindowWin::GetWidget() {
@@ -259,12 +485,83 @@ const views::Widget* NativeWindowWin::GetWidget() const {
}
views::ClientView* NativeWindowWin::CreateClientView(views::Widget* widget) {
return new NativeWindowClientView(widget, web_view_, this);
return new NativeWindowClientView(widget, this);
}
views::NonClientFrameView* NativeWindowWin::CreateNonClientFrameView(
views::Widget* widget) {
return new NativeWindowFrameView(widget, this);
if (has_frame_)
return new NativeWindowFrameView(widget, this);
return new NativeWindowFramelessView(widget, this);
}
void NativeWindowWin::OnViewWasResized() {
// Set the window shape of the RWHV.
gfx::Size sz = web_view_->size();
int height = sz.height(), width = sz.width();
gfx::Path path;
path.addRect(0, 0, width, height);
SetWindowRgn(web_contents()->GetView()->GetNativeView(),
path.CreateNativeRegion(),
1);
SkRegion* rgn = new SkRegion;
if (!window_->IsFullscreen() && !window_->IsMaximized()) {
if (draggable_region())
rgn->op(*draggable_region(), SkRegion::kUnion_Op);
if (!has_frame_ && CanResize()) {
rgn->op(0, 0, width, kResizeInsideBoundsSize, SkRegion::kUnion_Op);
rgn->op(0, 0, kResizeInsideBoundsSize, height, SkRegion::kUnion_Op);
rgn->op(width - kResizeInsideBoundsSize, 0, width, height,
SkRegion::kUnion_Op);
rgn->op(0, height - kResizeInsideBoundsSize, width, height,
SkRegion::kUnion_Op);
}
}
content::WebContents* web_contents = GetWebContents();
if (web_contents->GetRenderViewHost()->GetView())
web_contents->GetRenderViewHost()->GetView()->SetClickthroughRegion(rgn);
}
void NativeWindowWin::RegisterAccelerators() {
views::FocusManager* focus_manager = GetFocusManager();
accelerator_table_.clear();
focus_manager->UnregisterAccelerators(this);
GenerateAcceleratorTable();
for (AcceleratorTable::const_iterator iter = accelerator_table_.begin();
iter != accelerator_table_.end(); ++iter) {
focus_manager->RegisterAccelerator(
iter->first, ui::AcceleratorManager::kNormalPriority, this);
}
}
void NativeWindowWin::GenerateAcceleratorTable() {
DCHECK(menu_);
ui::SimpleMenuModel* model = static_cast<ui::SimpleMenuModel*>(
menu_->model());
FillAcceleratorTable(&accelerator_table_, model);
}
void NativeWindowWin::FillAcceleratorTable(AcceleratorTable* table,
ui::MenuModel* model) {
int count = model->GetItemCount();
for (int i = 0; i < count; ++i) {
ui::MenuModel::ItemType type = model->GetTypeAt(i);
if (type == ui::MenuModel::TYPE_SUBMENU) {
ui::MenuModel* submodel = model->GetSubmenuModelAt(i);
FillAcceleratorTable(table, submodel);
} else {
ui::Accelerator accelerator;
if (model->GetAcceleratorAt(i, &accelerator)) {
MenuItem item = { i, model };
(*table)[accelerator] = item;
}
}
}
}
// static

View File

@@ -12,6 +12,10 @@
#include "ui/gfx/size.h"
#include "ui/views/widget/widget_delegate.h"
namespace ui {
class MenuModel;
}
namespace views {
class WebView;
class Widget;
@@ -19,8 +23,10 @@ class Widget;
namespace atom {
class Menu2;
class NativeWindowWin : public NativeWindow,
public views::WidgetDelegate {
public views::WidgetDelegateView {
public:
explicit NativeWindowWin(content::WebContents* web_contents,
base::DictionaryValue* options);
@@ -34,6 +40,7 @@ class NativeWindowWin : public NativeWindow,
virtual bool IsFocused() OVERRIDE;
virtual void Show() OVERRIDE;
virtual void Hide() OVERRIDE;
virtual bool IsVisible() OVERRIDE;
virtual void Maximize() OVERRIDE;
virtual void Unmaximize() OVERRIDE;
virtual void Minimize() OVERRIDE;
@@ -60,18 +67,39 @@ class NativeWindowWin : public NativeWindow,
virtual bool IsKiosk() OVERRIDE;
virtual gfx::NativeWindow GetNativeWindow() OVERRIDE;
void OnMenuCommand(int position, HMENU menu);
// Set the native window menu.
void SetMenu(ui::MenuModel* menu_model);
views::Widget* window() const { return window_.get(); }
SkRegion* draggable_region() { return draggable_region_.get(); }
protected:
virtual void UpdateDraggableRegions(
const std::vector<DraggableRegion>& regions) OVERRIDE;
// Overridden from content::WebContentsDelegate:
virtual void HandleKeyboardEvent(
content::WebContents*,
const content::NativeWebKeyboardEvent&) OVERRIDE;
// Overridden from views::View:
virtual void Layout() OVERRIDE;
virtual void ViewHierarchyChanged(bool is_add,
views::View* parent,
views::View* child) OVERRIDE;
virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE;
// Overridden from views::WidgetDelegate:
virtual void DeleteDelegate() OVERRIDE;
virtual views::View* GetInitiallyFocusedView() OVERRIDE;
virtual bool CanResize() const OVERRIDE;
virtual bool CanMaximize() const OVERRIDE;
virtual string16 GetWindowTitle() const OVERRIDE;
virtual bool ShouldHandleSystemCommands() const OVERRIDE;
virtual bool ShouldShowWindowIcon() const OVERRIDE;
virtual gfx::ImageSkia GetWindowAppIcon() OVERRIDE;
virtual gfx::ImageSkia GetWindowIcon() OVERRIDE;
virtual views::Widget* GetWidget() OVERRIDE;
virtual const views::Widget* GetWidget() const OVERRIDE;
virtual views::ClientView* CreateClientView(views::Widget* widget) OVERRIDE;
@@ -79,9 +107,32 @@ class NativeWindowWin : public NativeWindow,
views::Widget* widget) OVERRIDE;
private:
typedef struct { int position; ui::MenuModel* model; } MenuItem;
typedef std::map<ui::Accelerator, MenuItem> AcceleratorTable;
void OnViewWasResized();
// Register accelerators supported by the menu model.
void RegisterAccelerators();
// Generate a table that contains memu model's accelerators and command ids.
void GenerateAcceleratorTable();
// Helper to fill the accelerator table from the model.
void FillAcceleratorTable(AcceleratorTable* table,
ui::MenuModel* model);
scoped_ptr<views::Widget> window_;
views::WebView* web_view_; // managed by window_.
// The window menu.
scoped_ptr<atom::Menu2> menu_;
// Map from accelerator to menu item's command id.
AcceleratorTable accelerator_table_;
scoped_ptr<SkRegion> draggable_region_;
bool resizable_;
string16 title_;
gfx::Size minimum_size_;

View File

@@ -0,0 +1,105 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/net/adapter_request_job.h"
#include "browser/net/url_request_string_job.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/net_errors.h"
#include "net/url_request/url_request_error_job.h"
#include "net/url_request/url_request_file_job.h"
namespace atom {
AdapterRequestJob::AdapterRequestJob(ProtocolHandler* protocol_handler,
net::URLRequest* request,
net::NetworkDelegate* network_delegate)
: URLRequestJob(request, network_delegate),
protocol_handler_(protocol_handler),
weak_factory_(this) {
}
void AdapterRequestJob::Start() {
DCHECK(!real_job_);
content::BrowserThread::PostTask(
content::BrowserThread::UI,
FROM_HERE,
base::Bind(&AdapterRequestJob::GetJobTypeInUI,
weak_factory_.GetWeakPtr()));
}
void AdapterRequestJob::Kill() {
if (real_job_) // Kill could happen when real_job_ is created.
real_job_->Kill();
}
bool AdapterRequestJob::ReadRawData(net::IOBuffer* buf,
int buf_size,
int *bytes_read) {
DCHECK(real_job_);
return real_job_->ReadRawData(buf, buf_size, bytes_read);
}
bool AdapterRequestJob::IsRedirectResponse(GURL* location,
int* http_status_code) {
DCHECK(real_job_);
return real_job_->IsRedirectResponse(location, http_status_code);
}
net::Filter* AdapterRequestJob::SetupFilter() const {
DCHECK(real_job_);
return real_job_->SetupFilter();
}
bool AdapterRequestJob::GetMimeType(std::string* mime_type) const {
DCHECK(real_job_);
return real_job_->GetMimeType(mime_type);
}
bool AdapterRequestJob::GetCharset(std::string* charset) {
DCHECK(real_job_);
return real_job_->GetCharset(charset);
}
base::WeakPtr<AdapterRequestJob> AdapterRequestJob::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
void AdapterRequestJob::CreateErrorJobAndStart(int error_code) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
real_job_ = new net::URLRequestErrorJob(
request(), network_delegate(), error_code);
real_job_->Start();
}
void AdapterRequestJob::CreateStringJobAndStart(const std::string& mime_type,
const std::string& charset,
const std::string& data) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
real_job_ = new URLRequestStringJob(
request(), network_delegate(), mime_type, charset, data);
real_job_->Start();
}
void AdapterRequestJob::CreateFileJobAndStart(const base::FilePath& path) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
real_job_ = new net::URLRequestFileJob(request(), network_delegate(), path);
real_job_->Start();
}
void AdapterRequestJob::CreateJobFromProtocolHandlerAndStart() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
DCHECK(protocol_handler_);
real_job_ = protocol_handler_->MaybeCreateJob(request(),
network_delegate());
if (!real_job_.get())
CreateErrorJobAndStart(net::ERR_NOT_IMPLEMENTED);
else
real_job_->Start();
}
} // namespace atom

View File

@@ -0,0 +1,68 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_NET_ADAPTER_REQUEST_JOB_H_
#define ATOM_BROWSER_NET_ADAPTER_REQUEST_JOB_H_
#include "base/memory/weak_ptr.h"
#include "net/url_request/url_request_job.h"
#include "net/url_request/url_request_job_factory.h"
namespace base {
class FilePath;
}
namespace atom {
// Ask JS which type of job it wants, and then delegate corresponding methods.
class AdapterRequestJob : public net::URLRequestJob {
public:
typedef net::URLRequestJobFactory::ProtocolHandler ProtocolHandler;
AdapterRequestJob(ProtocolHandler* protocol_handler,
net::URLRequest* request,
net::NetworkDelegate* network_delegate);
public:
// net::URLRequestJob:
virtual void Start() OVERRIDE;
virtual void Kill() OVERRIDE;
virtual bool ReadRawData(net::IOBuffer* buf,
int buf_size,
int *bytes_read) OVERRIDE;
virtual bool IsRedirectResponse(GURL* location,
int* http_status_code) OVERRIDE;
virtual net::Filter* SetupFilter() const OVERRIDE;
virtual bool GetMimeType(std::string* mime_type) const OVERRIDE;
virtual bool GetCharset(std::string* charset) OVERRIDE;
base::WeakPtr<AdapterRequestJob> GetWeakPtr();
ProtocolHandler* default_protocol_handler() { return protocol_handler_; }
// Override this function to determine which job should be started.
virtual void GetJobTypeInUI() = 0;
void CreateErrorJobAndStart(int error_code);
void CreateStringJobAndStart(const std::string& mime_type,
const std::string& charset,
const std::string& data);
void CreateFileJobAndStart(const base::FilePath& path);
void CreateJobFromProtocolHandlerAndStart();
private:
// The delegated request job.
scoped_refptr<net::URLRequestJob> real_job_;
// Default protocol handler.
ProtocolHandler* protocol_handler_;
base::WeakPtrFactory<AdapterRequestJob> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(AdapterRequestJob);
};
} // namespace atom
#endif // ATOM_BROWSER_NET_ADAPTER_REQUEST_JOB_H_

View File

@@ -0,0 +1,169 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/net/atom_url_request_context_getter.h"
#include "base/string_util.h"
#include "base/threading/worker_pool.h"
#include "browser/net/atom_url_request_job_factory.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/cookie_store_factory.h"
#include "content/public/common/url_constants.h"
#include "net/cert/cert_verifier.h"
#include "net/cookies/cookie_monster.h"
#include "net/http/http_auth_handler_factory.h"
#include "net/http/http_cache.h"
#include "net/http/http_server_properties_impl.h"
#include "net/proxy/dhcp_proxy_script_fetcher_factory.h"
#include "net/proxy/proxy_config_service.h"
#include "net/proxy/proxy_script_fetcher_impl.h"
#include "net/proxy/proxy_service.h"
#include "net/proxy/proxy_service_v8.h"
#include "net/ssl/default_server_bound_cert_store.h"
#include "net/ssl/server_bound_cert_service.h"
#include "net/ssl/ssl_config_service_defaults.h"
#include "net/url_request/data_protocol_handler.h"
#include "net/url_request/file_protocol_handler.h"
#include "net/url_request/static_http_user_agent_settings.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_storage.h"
#include "vendor/brightray/browser/network_delegate.h"
namespace atom {
using content::BrowserThread;
AtomURLRequestContextGetter::AtomURLRequestContextGetter(
const base::FilePath& base_path,
MessageLoop* io_loop,
MessageLoop* file_loop,
scoped_ptr<brightray::NetworkDelegate> network_delegate,
content::ProtocolHandlerMap* protocol_handlers)
: base_path_(base_path),
io_loop_(io_loop),
file_loop_(file_loop),
job_factory_(NULL),
network_delegate_(network_delegate.Pass()) {
// Must first be created on the UI thread.
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
std::swap(protocol_handlers_, *protocol_handlers);
// We must create the proxy config service on the UI loop on Linux because it
// must synchronously run on the glib message loop. This will be passed to
// the URLRequestContextStorage on the IO thread in GetURLRequestContext().
proxy_config_service_.reset(
net::ProxyService::CreateSystemProxyConfigService(
io_loop_->message_loop_proxy(),
file_loop_));
}
AtomURLRequestContextGetter::~AtomURLRequestContextGetter() {
}
net::URLRequestContext* AtomURLRequestContextGetter::GetURLRequestContext() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!url_request_context_.get()) {
url_request_context_.reset(new net::URLRequestContext());
url_request_context_->set_network_delegate(network_delegate_.get());
storage_.reset(
new net::URLRequestContextStorage(url_request_context_.get()));
storage_->set_cookie_store(content::CreatePersistentCookieStore(
base_path_.Append(FILE_PATH_LITERAL("Cookies")),
false,
nullptr,
nullptr));
storage_->set_server_bound_cert_service(new net::ServerBoundCertService(
new net::DefaultServerBoundCertStore(NULL),
base::WorkerPool::GetTaskRunner(true)));
storage_->set_http_user_agent_settings(
new net::StaticHttpUserAgentSettings(
"en-us,en", EmptyString()));
scoped_ptr<net::HostResolver> host_resolver(
net::HostResolver::CreateDefaultResolver(NULL));
net::DhcpProxyScriptFetcherFactory dhcp_factory;
storage_->set_cert_verifier(net::CertVerifier::CreateDefault());
storage_->set_proxy_service(
net::CreateProxyServiceUsingV8ProxyResolver(
proxy_config_service_.release(),
new net::ProxyScriptFetcherImpl(url_request_context_.get()),
dhcp_factory.Create(url_request_context_.get()),
host_resolver.get(),
NULL,
url_request_context_->network_delegate()));
storage_->set_ssl_config_service(new net::SSLConfigServiceDefaults);
storage_->set_http_auth_handler_factory(
net::HttpAuthHandlerFactory::CreateDefault(host_resolver.get()));
storage_->set_http_server_properties(new net::HttpServerPropertiesImpl);
base::FilePath cache_path = base_path_.Append(FILE_PATH_LITERAL("Cache"));
net::HttpCache::DefaultBackend* main_backend =
new net::HttpCache::DefaultBackend(
net::DISK_CACHE,
net::CACHE_BACKEND_DEFAULT,
cache_path,
0,
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE));
net::HttpNetworkSession::Params network_session_params;
network_session_params.cert_verifier =
url_request_context_->cert_verifier();
network_session_params.server_bound_cert_service =
url_request_context_->server_bound_cert_service();
network_session_params.proxy_service =
url_request_context_->proxy_service();
network_session_params.ssl_config_service =
url_request_context_->ssl_config_service();
network_session_params.http_auth_handler_factory =
url_request_context_->http_auth_handler_factory();
network_session_params.network_delegate =
url_request_context_->network_delegate();
network_session_params.http_server_properties =
url_request_context_->http_server_properties();
network_session_params.ignore_certificate_errors = false;
// Give |storage_| ownership at the end in case it's |mapped_host_resolver|.
storage_->set_host_resolver(host_resolver.Pass());
network_session_params.host_resolver =
url_request_context_->host_resolver();
net::HttpCache* main_cache = new net::HttpCache(
network_session_params, main_backend);
storage_->set_http_transaction_factory(main_cache);
DCHECK(!job_factory_);
job_factory_ = new AtomURLRequestJobFactory;
for (content::ProtocolHandlerMap::iterator it = protocol_handlers_.begin();
it != protocol_handlers_.end();
++it) {
bool set_protocol = job_factory_->SetProtocolHandler(
it->first,
it->second.release());
DCHECK(set_protocol);
}
protocol_handlers_.clear();
job_factory_->SetProtocolHandler(chrome::kDataScheme,
new net::DataProtocolHandler);
job_factory_->SetProtocolHandler(chrome::kFileScheme,
new net::FileProtocolHandler);
storage_->set_job_factory(job_factory_);
}
return url_request_context_.get();
}
scoped_refptr<base::SingleThreadTaskRunner>
AtomURLRequestContextGetter::GetNetworkTaskRunner() const {
return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
}
net::HostResolver* AtomURLRequestContextGetter::host_resolver() {
return url_request_context_->host_resolver();
}
} // namespace atom

View File

@@ -0,0 +1,69 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_NET_ATOM_URL_REQUEST_CONTEXT_GETTER_H_
#define ATOM_BROWSER_NET_ATOM_URL_REQUEST_CONTEXT_GETTER_H_
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "content/public/browser/content_browser_client.h"
#include "net/url_request/url_request_context_getter.h"
namespace base {
class MessageLoop;
}
namespace brightray {
class NetworkDelegate;
}
namespace net {
class HostResolver;
class ProxyConfigService;
class URLRequestContextStorage;
}
namespace atom {
class AtomURLRequestJobFactory;
class AtomURLRequestContextGetter : public net::URLRequestContextGetter {
public:
AtomURLRequestContextGetter(
const base::FilePath& base_path,
base::MessageLoop* io_loop,
base::MessageLoop* file_loop,
scoped_ptr<brightray::NetworkDelegate> network_delegate,
content::ProtocolHandlerMap* protocol_handlers);
// net::URLRequestContextGetter implementations:
virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE;
virtual scoped_refptr<base::SingleThreadTaskRunner>
GetNetworkTaskRunner() const OVERRIDE;
net::HostResolver* host_resolver();
net::URLRequestContextStorage* storage() const { return storage_.get(); }
AtomURLRequestJobFactory* job_factory() const { return job_factory_; }
protected:
virtual ~AtomURLRequestContextGetter();
private:
base::FilePath base_path_;
base::MessageLoop* io_loop_;
base::MessageLoop* file_loop_;
AtomURLRequestJobFactory* job_factory_;
scoped_ptr<net::ProxyConfigService> proxy_config_service_;
scoped_ptr<brightray::NetworkDelegate> network_delegate_;
scoped_ptr<net::URLRequestContextStorage> storage_;
scoped_ptr<net::URLRequestContext> url_request_context_;
content::ProtocolHandlerMap protocol_handlers_;
DISALLOW_COPY_AND_ASSIGN(AtomURLRequestContextGetter);
};
} // namespace atom
#endif // ATOM_BROWSER_NET_ATOM_URL_REQUEST_CONTEXT_GETTER_H_

View File

@@ -0,0 +1,105 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/net/atom_url_request_job_factory.h"
#include "base/stl_util.h"
#include "googleurl/src/gurl.h"
#include "net/base/load_flags.h"
#include "net/url_request/url_request.h"
namespace atom {
typedef net::URLRequestJobFactory::ProtocolHandler ProtocolHandler;
AtomURLRequestJobFactory::AtomURLRequestJobFactory() {}
AtomURLRequestJobFactory::~AtomURLRequestJobFactory() {
STLDeleteValues(&protocol_handler_map_);
}
bool AtomURLRequestJobFactory::SetProtocolHandler(
const std::string& scheme,
ProtocolHandler* protocol_handler) {
DCHECK(CalledOnValidThread());
base::AutoLock locked(lock_);
if (!protocol_handler) {
ProtocolHandlerMap::iterator it = protocol_handler_map_.find(scheme);
if (it == protocol_handler_map_.end())
return false;
delete it->second;
protocol_handler_map_.erase(it);
return true;
}
if (ContainsKey(protocol_handler_map_, scheme))
return false;
protocol_handler_map_[scheme] = protocol_handler;
return true;
}
ProtocolHandler* AtomURLRequestJobFactory::ReplaceProtocol(
const std::string& scheme,
ProtocolHandler* protocol_handler) {
DCHECK(CalledOnValidThread());
DCHECK(protocol_handler);
base::AutoLock locked(lock_);
if (!ContainsKey(protocol_handler_map_, scheme))
return NULL;
ProtocolHandler* original_protocol_handler = protocol_handler_map_[scheme];
protocol_handler_map_[scheme] = protocol_handler;
return original_protocol_handler;
}
ProtocolHandler* AtomURLRequestJobFactory::GetProtocolHandler(
const std::string& scheme) const {
DCHECK(CalledOnValidThread());
base::AutoLock locked(lock_);
ProtocolHandlerMap::const_iterator it = protocol_handler_map_.find(scheme);
if (it == protocol_handler_map_.end())
return NULL;
return it->second;
}
bool AtomURLRequestJobFactory::HasProtocolHandler(
const std::string& scheme) const {
base::AutoLock locked(lock_);
return ContainsKey(protocol_handler_map_, scheme);
}
net::URLRequestJob* AtomURLRequestJobFactory::MaybeCreateJobWithProtocolHandler(
const std::string& scheme,
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const {
DCHECK(CalledOnValidThread());
base::AutoLock locked(lock_);
ProtocolHandlerMap::const_iterator it = protocol_handler_map_.find(scheme);
if (it == protocol_handler_map_.end())
return NULL;
return it->second->MaybeCreateJob(request, network_delegate);
}
bool AtomURLRequestJobFactory::IsHandledProtocol(
const std::string& scheme) const {
DCHECK(CalledOnValidThread());
return HasProtocolHandler(scheme) ||
net::URLRequest::IsHandledProtocol(scheme);
}
bool AtomURLRequestJobFactory::IsHandledURL(const GURL& url) const {
if (!url.is_valid()) {
// We handle error cases.
return true;
}
return IsHandledProtocol(url.scheme());
}
} // namespace atom

View File

@@ -0,0 +1,61 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_NET_ATOM_URL_REQUEST_URL_REQUEST_JOB_FACTORY_H_
#define ATOM_BROWSER_NET_ATOM_URL_REQUEST_URL_REQUEST_JOB_FACTORY_H_
#include <map>
#include <vector>
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/synchronization/lock.h"
#include "net/url_request/url_request_job_factory.h"
namespace atom {
class AtomURLRequestJobFactory : public net::URLRequestJobFactory {
public:
AtomURLRequestJobFactory();
virtual ~AtomURLRequestJobFactory();
// Sets the ProtocolHandler for a scheme. Returns true on success, false on
// failure (a ProtocolHandler already exists for |scheme|). On success,
// URLRequestJobFactory takes ownership of |protocol_handler|.
bool SetProtocolHandler(const std::string& scheme,
ProtocolHandler* protocol_handler);
// Intercepts the ProtocolHandler for a scheme. Returns the original protocol
// handler on success, otherwise returns NULL.
ProtocolHandler* ReplaceProtocol(const std::string& scheme,
ProtocolHandler* protocol_handler);
// Returns the protocol handler registered with scheme.
ProtocolHandler* GetProtocolHandler(const std::string& scheme) const;
// Whether the protocol handler is registered by the job factory.
bool HasProtocolHandler(const std::string& scheme) const;
// URLRequestJobFactory implementation
virtual net::URLRequestJob* MaybeCreateJobWithProtocolHandler(
const std::string& scheme,
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const OVERRIDE;
virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE;
virtual bool IsHandledURL(const GURL& url) const OVERRIDE;
private:
typedef std::map<std::string, ProtocolHandler*> ProtocolHandlerMap;
ProtocolHandlerMap protocol_handler_map_;
mutable base::Lock lock_;
DISALLOW_COPY_AND_ASSIGN(AtomURLRequestJobFactory);
};
} // namespace atom
#endif // ATOM_BROWSER_NET_ATOM_URL_REQUEST_URL_REQUEST_JOB_FACTORY_H_

View File

@@ -0,0 +1,33 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/net/url_request_string_job.h"
#include "net/base/net_errors.h"
namespace atom {
URLRequestStringJob::URLRequestStringJob(net::URLRequest* request,
net::NetworkDelegate* network_delegate,
const std::string& mime_type,
const std::string& charset,
const std::string& data)
: net::URLRequestSimpleJob(request, network_delegate),
mime_type_(mime_type),
charset_(charset),
data_(data) {
}
int URLRequestStringJob::GetData(
std::string* mime_type,
std::string* charset,
std::string* data,
const net::CompletionCallback& callback) const {
*mime_type = mime_type_;
*charset = charset_;
*data = data_;
return net::OK;
}
} // namespace atom

View File

@@ -0,0 +1,36 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_NET_URL_REQUEST_STRING_JOB_H_
#define ATOM_BROWSER_NET_URL_REQUEST_STRING_JOB_H_
#include "net/url_request/url_request_simple_job.h"
namespace atom {
class URLRequestStringJob : public net::URLRequestSimpleJob {
public:
URLRequestStringJob(net::URLRequest* request,
net::NetworkDelegate* network_delegate,
const std::string& mime_type,
const std::string& charset,
const std::string& data);
// URLRequestSimpleJob:
virtual int GetData(std::string* mime_type,
std::string* charset,
std::string* data,
const net::CompletionCallback& callback) const OVERRIDE;
private:
std::string mime_type_;
std::string charset_;
std::string data_;
DISALLOW_COPY_AND_ASSIGN(URLRequestStringJob);
};
} // namespace atom
#endif // ATOM_BROWSER_NET_URL_REQUEST_STRING_JOB_H_

View File

@@ -1,27 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/node_bindings_browser_win.h"
namespace atom {
NodeBindingsBrowserWin::NodeBindingsBrowserWin()
: NodeBindings(true) {
}
NodeBindingsBrowserWin::~NodeBindingsBrowserWin() {
}
void NodeBindingsBrowserWin::PrepareMessageLoop() {
}
void NodeBindingsBrowserWin::RunMessageLoop() {
}
// static
NodeBindings* NodeBindings::CreateInBrowser() {
return new NodeBindingsBrowserWin();
}
} // namespace atom

View File

@@ -1,27 +0,0 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_NODE_BINDINGS_BROWSER_WIN_H_
#define ATOM_BROWSER_NODE_BINDINGS_BROWSER_WIN_H_
#include "base/compiler_specific.h"
#include "common/node_bindings.h"
namespace atom {
class NodeBindingsBrowserWin : public NodeBindings {
public:
NodeBindingsBrowserWin();
virtual ~NodeBindingsBrowserWin();
virtual void PrepareMessageLoop() OVERRIDE;
virtual void RunMessageLoop() OVERRIDE;
private:
DISALLOW_COPY_AND_ASSIGN(NodeBindingsBrowserWin);
};
} // namespace atom
#endif // ATOM_BROWSER_NODE_BINDINGS_BROWSER_WIN_H_

View File

@@ -0,0 +1,183 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/ui/accelerator_util.h"
#include <stdio.h>
#include <string>
#include "base/string_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "ui/base/accelerators/accelerator.h"
namespace accelerator_util {
namespace {
// Return key code of the char.
ui::KeyboardCode KeyboardCodeFromCharCode(char c, bool* shifted) {
*shifted = false;
switch (c) {
case 8: case 0x7F: return ui::VKEY_BACK;
case 9: return ui::VKEY_TAB;
case 0xD: case 3: return ui::VKEY_RETURN;
case 0x1B: return ui::VKEY_ESCAPE;
case ' ': return ui::VKEY_SPACE;
case 'a': return ui::VKEY_A;
case 'b': return ui::VKEY_B;
case 'c': return ui::VKEY_C;
case 'd': return ui::VKEY_D;
case 'e': return ui::VKEY_E;
case 'f': return ui::VKEY_F;
case 'g': return ui::VKEY_G;
case 'h': return ui::VKEY_H;
case 'i': return ui::VKEY_I;
case 'j': return ui::VKEY_J;
case 'k': return ui::VKEY_K;
case 'l': return ui::VKEY_L;
case 'm': return ui::VKEY_M;
case 'n': return ui::VKEY_N;
case 'o': return ui::VKEY_O;
case 'p': return ui::VKEY_P;
case 'q': return ui::VKEY_Q;
case 'r': return ui::VKEY_R;
case 's': return ui::VKEY_S;
case 't': return ui::VKEY_T;
case 'u': return ui::VKEY_U;
case 'v': return ui::VKEY_V;
case 'w': return ui::VKEY_W;
case 'x': return ui::VKEY_X;
case 'y': return ui::VKEY_Y;
case 'z': return ui::VKEY_Z;
case ')': *shifted = true; case '0': return ui::VKEY_0;
case '!': *shifted = true; case '1': return ui::VKEY_1;
case '@': *shifted = true; case '2': return ui::VKEY_2;
case '#': *shifted = true; case '3': return ui::VKEY_3;
case '$': *shifted = true; case '4': return ui::VKEY_4;
case '%': *shifted = true; case '5': return ui::VKEY_5;
case '^': *shifted = true; case '6': return ui::VKEY_6;
case '&': *shifted = true; case '7': return ui::VKEY_7;
case '*': *shifted = true; case '8': return ui::VKEY_8;
case '(': *shifted = true; case '9': return ui::VKEY_9;
case ':': *shifted = true; case ';': return ui::VKEY_OEM_1;
case '+': *shifted = true; case '=': return ui::VKEY_OEM_PLUS;
case '<': *shifted = true; case ',': return ui::VKEY_OEM_COMMA;
case '_': *shifted = true; case '-': return ui::VKEY_OEM_MINUS;
case '>': *shifted = true; case '.': return ui::VKEY_OEM_PERIOD;
case '?': *shifted = true; case '/': return ui::VKEY_OEM_2;
case '~': *shifted = true; case '`': return ui::VKEY_OEM_3;
case '{': *shifted = true; case '[': return ui::VKEY_OEM_4;
case '|': *shifted = true; case '\\': return ui::VKEY_OEM_5;
case '}': *shifted = true; case ']': return ui::VKEY_OEM_6;
case '"': *shifted = true; case '\'': return ui::VKEY_OEM_7;
default: return ui::VKEY_UNKNOWN;
}
}
} // namespace
bool StringToAccelerator(const std::string& description,
ui::Accelerator* accelerator) {
if (!IsStringASCII(description)) {
LOG(ERROR) << "The accelerator string can only contain ASCII characters";
return false;
}
std::string shortcut(StringToLowerASCII(description));
std::vector<std::string> tokens;
base::SplitString(shortcut, '+', &tokens);
// Now, parse it into an accelerator.
int modifiers = ui::EF_NONE;
ui::KeyboardCode key = ui::VKEY_UNKNOWN;
for (size_t i = 0; i < tokens.size(); i++) {
// We use straight comparing instead of map because the accelerator tends
// to be correct and usually only uses few special tokens.
if (tokens[i].size() == 1) {
bool shifted = false;
key = KeyboardCodeFromCharCode(tokens[i][0], &shifted);
if (shifted)
modifiers |= ui::EF_SHIFT_DOWN;
} else if (tokens[i] == "ctrl") {
modifiers |= ui::EF_CONTROL_DOWN;
} else if (tokens[i] == "command") {
modifiers |= ui::EF_COMMAND_DOWN;
} else if (tokens[i] == "alt") {
modifiers |= ui::EF_ALT_DOWN;
} else if (tokens[i] == "shift") {
modifiers |= ui::EF_SHIFT_DOWN;
} else if (tokens[i] == "tab") {
key = ui::VKEY_TAB;
} else if (tokens[i] == "space") {
key = ui::VKEY_SPACE;
} else if (tokens[i] == "backspace") {
key = ui::VKEY_BACK;
} else if (tokens[i] == "delete") {
key = ui::VKEY_DELETE;
} else if (tokens[i] == "enter" || tokens[i] == "return") {
key = ui::VKEY_RETURN;
} else if (tokens[i] == "up") {
key = ui::VKEY_UP;
} else if (tokens[i] == "down") {
key = ui::VKEY_DOWN;
} else if (tokens[i] == "left") {
key = ui::VKEY_LEFT;
} else if (tokens[i] == "right") {
key = ui::VKEY_RIGHT;
} else if (tokens[i] == "home") {
key = ui::VKEY_HOME;
} else if (tokens[i] == "end") {
key = ui::VKEY_END;
} else if (tokens[i] == "pagedown") {
key = ui::VKEY_PRIOR;
} else if (tokens[i] == "pageup") {
key = ui::VKEY_NEXT;
} else if (tokens[i] == "esc") {
key = ui::VKEY_ESCAPE;
} else if (tokens[i] == "volumemute") {
key = ui::VKEY_VOLUME_MUTE;
} else if (tokens[i] == "volumeup") {
key = ui::VKEY_VOLUME_UP;
} else if (tokens[i] == "volumedown") {
key = ui::VKEY_VOLUME_DOWN;
} else if (tokens[i] == "medianexttrack") {
key = ui::VKEY_MEDIA_NEXT_TRACK;
} else if (tokens[i] == "mediaprevioustrack") {
key = ui::VKEY_MEDIA_PREV_TRACK;
} else if (tokens[i] == "mediastop") {
key = ui::VKEY_MEDIA_STOP;
} else if (tokens[i] == "mediaplaypause") {
key = ui::VKEY_MEDIA_PLAY_PAUSE;
} else if (tokens[i].size() > 1 && tokens[i][0] == 'f') {
// F1 - F24.
int n;
if (base::StringToInt(tokens[i].c_str() + 1, &n)) {
key = static_cast<ui::KeyboardCode>(ui::VKEY_F1 + n - 1);
} else {
LOG(WARNING) << tokens[i] << "is not available on keyboard";
return false;
}
} else {
LOG(WARNING) << "Invalid accelerator token: " << tokens[i];
return false;
}
}
if (key == ui::VKEY_UNKNOWN) {
LOG(WARNING) << "The accelerator doesn't contain a valid key";
return false;
}
*accelerator = ui::Accelerator(key, modifiers);
SetPlatformAccelerator(accelerator);
return true;
}
} // namespace accelerator_util

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BROWSER_ACCELERATOR_UTIL_H_
#define BROWSER_ACCELERATOR_UTIL_H_
#ifndef BROWSER_UI_ACCELERATOR_UTIL_H_
#define BROWSER_UI_ACCELERATOR_UTIL_H_
#include <string>
@@ -22,4 +22,4 @@ void SetPlatformAccelerator(ui::Accelerator* accelerator);
} // namespace accelerator_util
#endif // BROWSER_ACCELERATOR_UTIL_H_
#endif // BROWSER_UI_ACCELERATOR_UTIL_H_

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/accelerator_util.h"
#include "browser/ui/accelerator_util.h"
#include "ui/base/accelerators/accelerator.h"
#import "ui/base/accelerators/platform_accelerator_cocoa.h"

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/accelerator_util.h"
#include "browser/ui/accelerator_util.h"
#include "ui/base/accelerators/accelerator.h"

View File

@@ -2,18 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_ATOM_EVENT_PROCESSING_WINDOW_H_
#define ATOM_BROWSER_ATOM_EVENT_PROCESSING_WINDOW_H_
#ifndef ATOM_BROWSER_UI_ATOM_EVENT_PROCESSING_WINDOW_H_
#define ATOM_BROWSER_UI_ATOM_EVENT_PROCESSING_WINDOW_H_
#import <Cocoa/Cocoa.h>
#include "base/memory/scoped_nsobject.h"
#import "ui/base/cocoa/underlay_opengl_hosting_window.h"
// Override NSWindow to access unhandled keyboard events (for command
// processing); subclassing NSWindow is the only method to do
// this.
@interface AtomEventProcessingWindow : UnderlayOpenGLHostingWindow {
@interface AtomEventProcessingWindow : NSWindow {
@private
BOOL redispatchingEvent_;
BOOL eventHandled_;
@@ -30,4 +29,4 @@
- (BOOL)performKeyEquivalent:(NSEvent*)theEvent;
@end
#endif // ATOM_BROWSER_ATOM_EVENT_PROCESSING_WINDOW_H_
#endif // ATOM_BROWSER_UI_ATOM_EVENT_PROCESSING_WINDOW_H_

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "browser/atom_event_processing_window.h"
#import "browser/ui/atom_event_processing_window.h"
#include "base/logging.h"
#import "content/public/browser/render_widget_host_view_mac_base.h"

View File

@@ -0,0 +1,72 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_UI_ATOM_MENU_CONTROLLER_MAC_H_
#define ATOM_BROWSER_UI_ATOM_MENU_CONTROLLER_MAC_H_
#import <Cocoa/Cocoa.h>
#include "base/memory/scoped_nsobject.h"
#include "base/string16.h"
namespace ui {
class MenuModel;
}
// A controller for the cross-platform menu model. The menu that's created
// has the tag and represented object set for each menu item. The object is a
// NSValue holding a pointer to the model for that level of the menu (to
// allow for hierarchical menus). The tag is the index into that model for
// that particular item. It is important that the model outlives this object
// as it only maintains weak references.
@interface AtomMenuController : NSObject<NSMenuDelegate> {
@protected
ui::MenuModel* model_; // weak
scoped_nsobject<NSMenu> menu_;
BOOL isMenuOpen_;
}
@property(nonatomic, assign) ui::MenuModel* model;
// NIB-based initializer. This does not create a menu. Clients can set the
// properties of the object and the menu will be created upon the first call to
// |-menu|. Note that the menu will be immutable after creation.
- (id)init;
// Builds a NSMenu from the pre-built model (must not be nil). Changes made
// to the contents of the model after calling this will not be noticed.
- (id)initWithModel:(ui::MenuModel*)model;
// Programmatically close the constructed menu.
- (void)cancel;
// Access to the constructed menu if the complex initializer was used. If the
// default initializer was used, then this will create the menu on first call.
- (NSMenu*)menu;
// Whether the menu is currently open.
- (BOOL)isMenuOpen;
// NSMenuDelegate methods this class implements. Subclasses should call super
// if extending the behavior.
- (void)menuWillOpen:(NSMenu*)menu;
- (void)menuDidClose:(NSMenu*)menu;
@end
// Exposed only for unit testing, do not call directly.
@interface AtomMenuController (PrivateExposedForTesting)
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item;
@end
// Protected methods that subclassers can override.
@interface AtomMenuController (Protected)
- (void)addItemToMenu:(NSMenu*)menu
atIndex:(NSInteger)index
fromModel:(ui::MenuModel*)model;
- (NSMenu*)menuFromModel:(ui::MenuModel*)model;
@end
#endif // ATOM_BROWSER_UI_ATOM_MENU_CONTROLLER_MAC_H_

View File

@@ -0,0 +1,258 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "browser/ui/atom_menu_controller_mac.h"
#include "base/logging.h"
#include "base/strings/sys_string_conversions.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/accelerators/platform_accelerator_cocoa.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "ui/base/models/simple_menu_model.h"
#include "ui/gfx/image/image.h"
namespace {
bool isLeftButtonEvent(NSEvent* event) {
NSEventType type = [event type];
return type == NSLeftMouseDown ||
type == NSLeftMouseDragged ||
type == NSLeftMouseUp;
}
bool isRightButtonEvent(NSEvent* event) {
NSEventType type = [event type];
return type == NSRightMouseDown ||
type == NSRightMouseDragged ||
type == NSRightMouseUp;
}
bool isMiddleButtonEvent(NSEvent* event) {
if ([event buttonNumber] != 2)
return false;
NSEventType type = [event type];
return type == NSOtherMouseDown ||
type == NSOtherMouseDragged ||
type == NSOtherMouseUp;
}
int EventFlagsFromNSEventWithModifiers(NSEvent* event, NSUInteger modifiers) {
int flags = 0;
flags |= (modifiers & NSAlphaShiftKeyMask) ? ui::EF_CAPS_LOCK_DOWN : 0;
flags |= (modifiers & NSShiftKeyMask) ? ui::EF_SHIFT_DOWN : 0;
flags |= (modifiers & NSControlKeyMask) ? ui::EF_CONTROL_DOWN : 0;
flags |= (modifiers & NSAlternateKeyMask) ? ui::EF_ALT_DOWN : 0;
flags |= (modifiers & NSCommandKeyMask) ? ui::EF_COMMAND_DOWN : 0;
flags |= isLeftButtonEvent(event) ? ui::EF_LEFT_MOUSE_BUTTON : 0;
flags |= isRightButtonEvent(event) ? ui::EF_RIGHT_MOUSE_BUTTON : 0;
flags |= isMiddleButtonEvent(event) ? ui::EF_MIDDLE_MOUSE_BUTTON : 0;
return flags;
}
// Retrieves a bitsum of ui::EventFlags from NSEvent.
int EventFlagsFromNSEvent(NSEvent* event) {
NSUInteger modifiers = [event modifierFlags];
return EventFlagsFromNSEventWithModifiers(event, modifiers);
}
} // namespace
@interface AtomMenuController (Private)
- (void)addSeparatorToMenu:(NSMenu*)menu
atIndex:(int)index;
@end
@implementation AtomMenuController
@synthesize model = model_;
- (id)init {
self = [super init];
return self;
}
- (id)initWithModel:(ui::MenuModel*)model {
if ((self = [super init])) {
model_ = model;
[self menu];
}
return self;
}
- (void)dealloc {
[menu_ setDelegate:nil];
// Close the menu if it is still open. This could happen if a tab gets closed
// while its context menu is still open.
[self cancel];
model_ = NULL;
[super dealloc];
}
- (void)cancel {
if (isMenuOpen_) {
[menu_ cancelTracking];
model_->MenuClosed();
isMenuOpen_ = NO;
}
}
// Creates a NSMenu from the given model. If the model has submenus, this can
// be invoked recursively.
- (NSMenu*)menuFromModel:(ui::MenuModel*)model {
NSMenu* menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
const int count = model->GetItemCount();
for (int index = 0; index < count; index++) {
if (model->GetTypeAt(index) == ui::MenuModel::TYPE_SEPARATOR)
[self addSeparatorToMenu:menu atIndex:index];
else
[self addItemToMenu:menu atIndex:index fromModel:model];
}
return menu;
}
// Adds a separator item at the given index. As the separator doesn't need
// anything from the model, this method doesn't need the model index as the
// other method below does.
- (void)addSeparatorToMenu:(NSMenu*)menu
atIndex:(int)index {
NSMenuItem* separator = [NSMenuItem separatorItem];
[menu insertItem:separator atIndex:index];
}
// Adds an item or a hierarchical menu to the item at the |index|,
// associated with the entry in the model identified by |modelIndex|.
- (void)addItemToMenu:(NSMenu*)menu
atIndex:(NSInteger)index
fromModel:(ui::MenuModel*)model {
string16 label16 = model->GetLabelAt(index);
NSString* label = l10n_util::FixUpWindowsStyleLabel(label16);
scoped_nsobject<NSMenuItem> item(
[[NSMenuItem alloc] initWithTitle:label
action:@selector(itemSelected:)
keyEquivalent:@""]);
// If the menu item has an icon, set it.
gfx::Image icon;
if (model->GetIconAt(index, &icon) && !icon.IsEmpty())
[item setImage:icon.ToNSImage()];
ui::MenuModel::ItemType type = model->GetTypeAt(index);
if (type == ui::MenuModel::TYPE_SUBMENU) {
// Recursively build a submenu from the sub-model at this index.
[item setTarget:nil];
[item setAction:nil];
ui::MenuModel* submenuModel = model->GetSubmenuModelAt(index);
NSMenu* submenu =
[self menuFromModel:(ui::SimpleMenuModel*)submenuModel];
[submenu setTitle:[item title]];
[item setSubmenu:submenu];
// Hack to set window and help menu.
if ([[item title] isEqualToString:@"Window"] && [submenu numberOfItems] > 0)
[NSApp setWindowsMenu:submenu];
else if ([[item title] isEqualToString:@"Help"])
[NSApp setHelpMenu:submenu];
} else {
// The MenuModel works on indexes so we can't just set the command id as the
// tag like we do in other menus. Also set the represented object to be
// the model so hierarchical menus check the correct index in the correct
// model. Setting the target to |self| allows this class to participate
// in validation of the menu items.
[item setTag:index];
[item setTarget:self];
NSValue* modelObject = [NSValue valueWithPointer:model];
[item setRepresentedObject:modelObject]; // Retains |modelObject|.
ui::Accelerator accelerator;
if (model->GetAcceleratorAt(index, &accelerator)) {
const ui::PlatformAcceleratorCocoa* platformAccelerator =
static_cast<const ui::PlatformAcceleratorCocoa*>(
accelerator.platform_accelerator());
if (platformAccelerator) {
[item setKeyEquivalent:platformAccelerator->characters()];
[item setKeyEquivalentModifierMask:
platformAccelerator->modifier_mask()];
}
}
}
[menu insertItem:item atIndex:index];
}
// Called before the menu is to be displayed to update the state (enabled,
// radio, etc) of each item in the menu. Also will update the title if
// the item is marked as "dynamic".
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
SEL action = [item action];
if (action != @selector(itemSelected:))
return NO;
NSInteger modelIndex = [item tag];
ui::MenuModel* model =
static_cast<ui::MenuModel*>(
[[(id)item representedObject] pointerValue]);
DCHECK(model);
if (model) {
BOOL checked = model->IsItemCheckedAt(modelIndex);
DCHECK([(id)item isKindOfClass:[NSMenuItem class]]);
[(id)item setState:(checked ? NSOnState : NSOffState)];
[(id)item setHidden:(!model->IsVisibleAt(modelIndex))];
if (model->IsItemDynamicAt(modelIndex)) {
// Update the label and the icon.
NSString* label =
l10n_util::FixUpWindowsStyleLabel(model->GetLabelAt(modelIndex));
[(id)item setTitle:label];
gfx::Image icon;
model->GetIconAt(modelIndex, &icon);
[(id)item setImage:icon.IsEmpty() ? nil : icon.ToNSImage()];
}
return model->IsEnabledAt(modelIndex);
}
return NO;
}
// Called when the user chooses a particular menu item. |sender| is the menu
// item chosen.
- (void)itemSelected:(id)sender {
NSInteger modelIndex = [sender tag];
ui::MenuModel* model =
static_cast<ui::MenuModel*>(
[[sender representedObject] pointerValue]);
DCHECK(model);
if (model) {
int event_flags = EventFlagsFromNSEvent([NSApp currentEvent]);
model->ActivatedAt(modelIndex, event_flags);
}
}
- (NSMenu*)menu {
if (!menu_ && model_) {
menu_.reset([[self menuFromModel:model_] retain]);
[menu_ setDelegate:self];
}
return menu_.get();
}
- (BOOL)isMenuOpen {
return isMenuOpen_;
}
- (void)menuWillOpen:(NSMenu*)menu {
isMenuOpen_ = YES;
model_->MenuWillShow();
}
- (void)menuDidClose:(NSMenu*)menu {
if (isMenuOpen_) {
model_->MenuClosed();
isMenuOpen_ = NO;
}
}
@end

57
browser/ui/file_dialog.h Normal file
View File

@@ -0,0 +1,57 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BROWSER_UI_FILE_DIALOG_H_
#define BROWSER_UI_FILE_DIALOG_H_
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/files/file_path.h"
namespace atom {
class NativeWindow;
}
namespace file_dialog {
enum FileDialogProperty {
FILE_DIALOG_OPEN_FILE = 1,
FILE_DIALOG_OPEN_DIRECTORY = 2,
FILE_DIALOG_MULTI_SELECTIONS = 4,
FILE_DIALOG_CREATE_DIRECTORY = 8,
};
typedef base::Callback<void(
bool result, const std::vector<base::FilePath>& paths)> OpenDialogCallback;
typedef base::Callback<void(
bool result, const base::FilePath& path)> SaveDialogCallback;
bool ShowOpenDialog(atom::NativeWindow* parent_window,
const std::string& title,
const base::FilePath& default_path,
int properties,
std::vector<base::FilePath>* paths);
void ShowOpenDialog(atom::NativeWindow* parent_window,
const std::string& title,
const base::FilePath& default_path,
int properties,
const OpenDialogCallback& callback);
bool ShowSaveDialog(atom::NativeWindow* parent_window,
const std::string& title,
const base::FilePath& default_path,
base::FilePath* path);
void ShowSaveDialog(atom::NativeWindow* parent_window,
const std::string& title,
const base::FilePath& default_path,
const SaveDialogCallback& callback);
} // namespace file_dialog
#endif // BROWSER_UI_FILE_DIALOG_H_

View File

@@ -0,0 +1,168 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/ui/file_dialog.h"
#import <Cocoa/Cocoa.h>
#include <CoreServices/CoreServices.h>
#include "base/file_util.h"
#include "base/strings/sys_string_conversions.h"
#include "browser/native_window.h"
namespace file_dialog {
namespace {
void SetupDialog(NSSavePanel* dialog,
const std::string& title,
const base::FilePath& default_path) {
if (!title.empty())
[dialog setTitle:base::SysUTF8ToNSString(title)];
NSString* default_dir = nil;
NSString* default_filename = nil;
if (!default_path.empty()) {
if (file_util::DirectoryExists(default_path)) {
default_dir = base::SysUTF8ToNSString(default_path.value());
} else {
default_dir = base::SysUTF8ToNSString(default_path.DirName().value());
default_filename =
base::SysUTF8ToNSString(default_path.BaseName().value());
}
}
if (default_dir)
[dialog setDirectoryURL:[NSURL fileURLWithPath:default_dir]];
if (default_filename)
[dialog setNameFieldStringValue:default_filename];
[dialog setCanSelectHiddenExtension:YES];
[dialog setAllowsOtherFileTypes:YES];
}
void SetupDialogForProperties(NSOpenPanel* dialog, int properties) {
[dialog setCanChooseFiles:(properties & FILE_DIALOG_OPEN_FILE)];
if (properties & FILE_DIALOG_OPEN_DIRECTORY)
[dialog setCanChooseDirectories:YES];
if (properties & FILE_DIALOG_CREATE_DIRECTORY)
[dialog setCanCreateDirectories:YES];
if (properties & FILE_DIALOG_MULTI_SELECTIONS)
[dialog setAllowsMultipleSelection:YES];
}
// Run modal dialog with parent window and return user's choice.
int RunModalDialog(NSSavePanel* dialog, atom::NativeWindow* parent_window) {
__block int chosen = NSFileHandlingPanelCancelButton;
if (parent_window == NULL) {
chosen = [dialog runModal];
} else {
NSWindow* window = parent_window->GetNativeWindow();
[dialog beginSheetModalForWindow:window
completionHandler:^(NSInteger c) {
chosen = c;
[NSApp stopModal];
}];
[NSApp runModalForWindow:window];
}
return chosen;
}
void ReadDialogPaths(NSOpenPanel* dialog, std::vector<base::FilePath>* paths) {
NSArray* urls = [dialog URLs];
for (NSURL* url in urls)
if ([url isFileURL])
paths->push_back(base::FilePath(base::SysNSStringToUTF8([url path])));
}
} // namespace
bool ShowOpenDialog(atom::NativeWindow* parent_window,
const std::string& title,
const base::FilePath& default_path,
int properties,
std::vector<base::FilePath>* paths) {
DCHECK(paths);
NSOpenPanel* dialog = [NSOpenPanel openPanel];
SetupDialog(dialog, title, default_path);
SetupDialogForProperties(dialog, properties);
int chosen = RunModalDialog(dialog, parent_window);
if (chosen == NSFileHandlingPanelCancelButton)
return false;
ReadDialogPaths(dialog, paths);
return true;
}
void ShowOpenDialog(atom::NativeWindow* parent_window,
const std::string& title,
const base::FilePath& default_path,
int properties,
const OpenDialogCallback& c) {
NSOpenPanel* dialog = [NSOpenPanel openPanel];
SetupDialog(dialog, title, default_path);
SetupDialogForProperties(dialog, properties);
// Duplicate the callback object here since c is a reference and gcd would
// only store the pointer, by duplication we can force gcd to store a copy.
__block OpenDialogCallback callback = c;
NSWindow* window = parent_window ? parent_window->GetNativeWindow() : NULL;
[dialog beginSheetModalForWindow:window
completionHandler:^(NSInteger chosen) {
if (chosen == NSFileHandlingPanelCancelButton) {
callback.Run(false, std::vector<base::FilePath>());
} else {
std::vector<base::FilePath> paths;
ReadDialogPaths(dialog, &paths);
callback.Run(true, paths);
}
}];
}
bool ShowSaveDialog(atom::NativeWindow* parent_window,
const std::string& title,
const base::FilePath& default_path,
base::FilePath* path) {
DCHECK(path);
NSSavePanel* dialog = [NSSavePanel savePanel];
SetupDialog(dialog, title, default_path);
int chosen = RunModalDialog(dialog, parent_window);
if (chosen == NSFileHandlingPanelCancelButton || ![[dialog URL] isFileURL])
return false;
*path = base::FilePath(base::SysNSStringToUTF8([[dialog URL] path]));
return true;
}
void ShowSaveDialog(atom::NativeWindow* parent_window,
const std::string& title,
const base::FilePath& default_path,
const SaveDialogCallback& c) {
NSSavePanel* dialog = [NSSavePanel savePanel];
SetupDialog(dialog, title, default_path);
__block SaveDialogCallback callback = c;
NSWindow* window = parent_window ? parent_window->GetNativeWindow() : NULL;
[dialog beginSheetModalForWindow:window
completionHandler:^(NSInteger chosen) {
if (chosen == NSFileHandlingPanelCancelButton) {
callback.Run(false, base::FilePath());
} else {
std::string path = base::SysNSStringToUTF8([[dialog URL] path]);
callback.Run(true, base::FilePath(path));
}
}];
}
} // namespace file_dialog

View File

@@ -0,0 +1,318 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/ui/file_dialog.h"
#include <atlbase.h>
#include <windows.h>
#include <commdlg.h>
#include <shlobj.h>
#include "base/file_util.h"
#include "base/i18n/case_conversion.h"
#include "base/string_util.h"
#include "base/strings/string_split.h"
#include "base/utf_string_conversions.h"
#include "base/win/registry.h"
#include "browser/native_window.h"
#include "third_party/wtl/include/atlapp.h"
#include "third_party/wtl/include/atldlgs.h"
namespace file_dialog {
namespace {
// Distinguish directories from regular files.
bool IsDirectory(const base::FilePath& path) {
base::PlatformFileInfo file_info;
return file_util::GetFileInfo(path, &file_info) ?
file_info.is_directory : path.EndsWithSeparator();
}
// Get the file type description from the registry. This will be "Text Document"
// for .txt files, "JPEG Image" for .jpg files, etc. If the registry doesn't
// have an entry for the file type, we return false, true if the description was
// found. 'file_ext' must be in form ".txt".
static bool GetRegistryDescriptionFromExtension(const std::wstring& file_ext,
std::wstring* reg_description) {
DCHECK(reg_description);
base::win::RegKey reg_ext(HKEY_CLASSES_ROOT, file_ext.c_str(), KEY_READ);
std::wstring reg_app;
if (reg_ext.ReadValue(NULL, &reg_app) == ERROR_SUCCESS && !reg_app.empty()) {
base::win::RegKey reg_link(HKEY_CLASSES_ROOT, reg_app.c_str(), KEY_READ);
if (reg_link.ReadValue(NULL, reg_description) == ERROR_SUCCESS)
return true;
}
return false;
}
// Set up a filter for a Save/Open dialog, which will consist of |file_ext| file
// extensions (internally separated by semicolons), |ext_desc| as the text
// descriptions of the |file_ext| types (optional), and (optionally) the default
// 'All Files' view. The purpose of the filter is to show only files of a
// particular type in a Windows Save/Open dialog box. The resulting filter is
// returned. The filters created here are:
// 1. only files that have 'file_ext' as their extension
// 2. all files (only added if 'include_all_files' is true)
// Example:
// file_ext: { "*.txt", "*.htm;*.html" }
// ext_desc: { "Text Document" }
// returned: "Text Document\0*.txt\0HTML Document\0*.htm;*.html\0"
// "All Files\0*.*\0\0" (in one big string)
// If a description is not provided for a file extension, it will be retrieved
// from the registry. If the file extension does not exist in the registry, it
// will be omitted from the filter, as it is likely a bogus extension.
void FormatFilterForExtensions(
std::vector<std::wstring>* file_ext,
std::vector<std::wstring>* ext_desc,
bool include_all_files,
std::vector<COMDLG_FILTERSPEC>* file_types) {
DCHECK(file_ext->size() >= ext_desc->size());
if (file_ext->empty())
include_all_files = true;
for (size_t i = 0; i < file_ext->size(); ++i) {
std::wstring ext = (*file_ext)[i];
std::wstring desc;
if (i < ext_desc->size())
desc = (*ext_desc)[i];
if (ext.empty()) {
// Force something reasonable to appear in the dialog box if there is no
// extension provided.
include_all_files = true;
continue;
}
if (desc.empty()) {
DCHECK(ext.find(L'.') != std::wstring::npos);
std::wstring first_extension = ext.substr(ext.find(L'.'));
size_t first_separator_index = first_extension.find(L';');
if (first_separator_index != std::wstring::npos)
first_extension = first_extension.substr(0, first_separator_index);
// Find the extension name without the preceeding '.' character.
std::wstring ext_name = first_extension;
size_t ext_index = ext_name.find_first_not_of(L'.');
if (ext_index != std::wstring::npos)
ext_name = ext_name.substr(ext_index);
if (!GetRegistryDescriptionFromExtension(first_extension, &desc)) {
// The extension doesn't exist in the registry. Create a description
// based on the unknown extension type (i.e. if the extension is .qqq,
// the we create a description "QQQ File (.qqq)").
include_all_files = true;
// TODO(zcbenz): should be localized.
desc = base::i18n::ToUpper(WideToUTF16(ext_name)) + L" File";
}
desc += L" (*." + ext_name + L")";
// Store the description.
ext_desc->push_back(desc);
}
COMDLG_FILTERSPEC spec = { (*ext_desc)[i].c_str(), (*file_ext)[i].c_str() };
file_types->push_back(spec);
}
if (include_all_files) {
// TODO(zcbenz): Should be localized.
ext_desc->push_back(L"All Files (*.*)");
file_ext->push_back(L"*.*");
COMDLG_FILTERSPEC spec = {
(*ext_desc)[ext_desc->size() - 1].c_str(),
(*file_ext)[file_ext->size() - 1].c_str(),
};
file_types->push_back(spec);
}
}
// Generic class to delegate common open/save dialog's behaviours, users need to
// call interface methods via GetPtr().
template <typename T>
class FileDialog {
public:
FileDialog(const base::FilePath& default_path,
const std::string title,
int options,
const std::vector<std::wstring>& file_ext,
const std::vector<std::wstring>& desc_ext)
: file_ext_(file_ext),
desc_ext_(desc_ext) {
std::vector<COMDLG_FILTERSPEC> filters;
FormatFilterForExtensions(&file_ext_, &desc_ext_, true, &filters);
std::wstring file_part;
if (!IsDirectory(default_path))
file_part = default_path.BaseName().value();
dialog_.reset(new T(
file_part.c_str(),
options,
NULL,
filters.data(),
filters.size()));
if (!title.empty())
GetPtr()->SetTitle(UTF8ToUTF16(title).c_str());
SetDefaultFolder(default_path);
}
bool Show(atom::NativeWindow* parent_window) {
HWND window = parent_window ? parent_window->GetNativeWindow() : NULL;
return dialog_->DoModal(window) == IDOK;
}
T* GetDialog() { return dialog_.get(); }
IFileDialog* GetPtr() const { return dialog_->GetPtr(); }
const std::vector<std::wstring> file_ext() const { return file_ext_; }
private:
// Set up the initial directory for the dialog.
void SetDefaultFolder(const base::FilePath file_path) {
std::wstring directory = IsDirectory(file_path) ?
file_path.value() :
file_path.DirName().value();
ATL::CComPtr<IShellItem> folder_item;
HRESULT hr = SHCreateItemFromParsingName(directory.c_str(),
NULL,
IID_PPV_ARGS(&folder_item));
if (SUCCEEDED(hr))
GetPtr()->SetDefaultFolder(folder_item);
}
scoped_ptr<T> dialog_;
std::vector<std::wstring> file_ext_;
std::vector<std::wstring> desc_ext_;
std::vector<COMDLG_FILTERSPEC> filters_;
DISALLOW_COPY_AND_ASSIGN(FileDialog);
};
} // namespace
bool ShowOpenDialog(atom::NativeWindow* parent_window,
const std::string& title,
const base::FilePath& default_path,
int properties,
std::vector<base::FilePath>* paths) {
int options = FOS_FORCEFILESYSTEM | FOS_FILEMUSTEXIST;
if (properties & FILE_DIALOG_OPEN_DIRECTORY)
options |= FOS_PICKFOLDERS;
if (properties & FILE_DIALOG_MULTI_SELECTIONS)
options |= FOS_ALLOWMULTISELECT;
FileDialog<CShellFileOpenDialog> open_dialog(
default_path,
title,
options,
std::vector<std::wstring>(),
std::vector<std::wstring>());
if (!open_dialog.Show(parent_window))
return false;
ATL::CComPtr<IShellItemArray> items;
HRESULT hr = static_cast<IFileOpenDialog*>(open_dialog.GetPtr())->GetResults(
&items);
if (FAILED(hr))
return false;
ATL::CComPtr<IShellItem> item;
DWORD count = 0;
hr = items->GetCount(&count);
if (FAILED(hr))
return false;
paths->reserve(count);
for (DWORD i = 0; i < count; ++i) {
hr = items->GetItemAt(i, &item);
if (FAILED(hr))
return false;
wchar_t file_name[MAX_PATH];
hr = CShellFileOpenDialog::GetFileNameFromShellItem(
item, SIGDN_FILESYSPATH, file_name, MAX_PATH);
if (FAILED(hr))
return false;
paths->push_back(base::FilePath(file_name));
}
return true;
}
void ShowOpenDialog(atom::NativeWindow* parent_window,
const std::string& title,
const base::FilePath& default_path,
int properties,
const OpenDialogCallback& callback) {
std::vector<base::FilePath> paths;
bool result = ShowOpenDialog(parent_window,
title,
default_path,
properties,
&paths);
callback.Run(result, paths);
}
bool ShowSaveDialog(atom::NativeWindow* parent_window,
const std::string& title,
const base::FilePath& default_path,
base::FilePath* path) {
// TODO(zcbenz): Accept custom filters from caller.
std::vector<std::wstring> file_ext;
std::wstring extension = default_path.Extension();
if (!extension.empty())
file_ext.push_back(extension.insert(0, L"*"));
FileDialog<CShellFileSaveDialog> save_dialog(
default_path,
title,
FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_OVERWRITEPROMPT,
file_ext,
std::vector<std::wstring>());
if (!save_dialog.Show(parent_window))
return false;
wchar_t file_name[MAX_PATH];
HRESULT hr = save_dialog.GetDialog()->GetFilePath(file_name, MAX_PATH);
if (FAILED(hr))
return false;
// Append extension according to selected filter.
UINT filter_index = 1;
save_dialog.GetPtr()->GetFileTypeIndex(&filter_index);
std::wstring selected_filter = save_dialog.file_ext()[filter_index - 1];
if (selected_filter != L"*.*") {
std::wstring result = file_name;
if (!EndsWith(result, selected_filter.substr(1), false)) {
if (result[result.length() - 1] != L'.')
result.push_back(L'.');
result.append(selected_filter.substr(2));
*path = base::FilePath(result);
return true;
}
}
*path = base::FilePath(file_name);
return true;
}
void ShowSaveDialog(atom::NativeWindow* parent_window,
const std::string& title,
const base::FilePath& default_path,
const SaveDialogCallback& callback) {
base::FilePath path;
bool result = ShowSaveDialog(parent_window, title, default_path, &path);
callback.Run(result, path);
}
} // namespace file_dialog

View File

@@ -2,12 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BROWSER_MESSAGE_BOX_H_
#define BROWSER_MESSAGE_BOX_H_
#ifndef BROWSER_UI_MESSAGE_BOX_H_
#define BROWSER_UI_MESSAGE_BOX_H_
#include <string>
#include <vector>
#include "base/callback_forward.h"
namespace atom {
class NativeWindow;
@@ -18,6 +20,8 @@ enum MessageBoxType {
MESSAGE_BOX_TYPE_WARNING
};
typedef base::Callback<void(int code)> MessageBoxCallback;
int ShowMessageBox(NativeWindow* parent_window,
MessageBoxType type,
const std::vector<std::string>& buttons,
@@ -25,6 +29,14 @@ int ShowMessageBox(NativeWindow* parent_window,
const std::string& message,
const std::string& detail);
void ShowMessageBox(NativeWindow* parent_window,
MessageBoxType type,
const std::vector<std::string>& buttons,
const std::string& title,
const std::string& message,
const std::string& detail,
const MessageBoxCallback& callback);
} // namespace atom
#endif // BROWSER_MESSAGE_BOX_H_
#endif // BROWSER_UI_MESSAGE_BOX_H_

View File

@@ -0,0 +1,116 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/ui/message_box.h"
#import <Cocoa/Cocoa.h>
#include "base/callback.h"
#include "base/strings/sys_string_conversions.h"
#include "browser/native_window.h"
#include "browser/ui/nsalert_synchronous_sheet_mac.h"
@interface ModalDelegate : NSObject {
@private
atom::MessageBoxCallback callback_;
NSAlert* alert_;
}
- (id)initWithCallback:(const atom::MessageBoxCallback&)callback
andAlert:(NSAlert*)alert;
@end
@implementation ModalDelegate
- (id)initWithCallback:(const atom::MessageBoxCallback&)callback
andAlert:(NSAlert*)alert {
if ((self = [super init])) {
callback_ = callback;
alert_ = alert;
}
return self;
}
- (void)alertDidEnd:(NSAlert*)alert
returnCode:(NSInteger)returnCode
contextInfo:(void*)contextInfo {
callback_.Run(returnCode);
[alert_ release];
[self release];
}
@end
namespace atom {
namespace {
NSAlert* CreateNSAlert(NativeWindow* parent_window,
MessageBoxType type,
const std::vector<std::string>& buttons,
const std::string& title,
const std::string& message,
const std::string& detail) {
// Ignore the title; it's the window title on other platforms and ignorable.
NSAlert* alert = [[NSAlert alloc] init];
[alert setMessageText:base::SysUTF8ToNSString(message)];
[alert setInformativeText:base::SysUTF8ToNSString(detail)];
switch (type) {
case MESSAGE_BOX_TYPE_INFORMATION:
[alert setAlertStyle:NSInformationalAlertStyle];
break;
case MESSAGE_BOX_TYPE_WARNING:
[alert setAlertStyle:NSWarningAlertStyle];
break;
default:
break;
}
for (size_t i = 0; i < buttons.size(); ++i) {
NSString* title = base::SysUTF8ToNSString(buttons[i]);
NSButton* button = [alert addButtonWithTitle:title];
[button setTag:i];
}
return alert;
}
} // namespace
int ShowMessageBox(NativeWindow* parent_window,
MessageBoxType type,
const std::vector<std::string>& buttons,
const std::string& title,
const std::string& message,
const std::string& detail) {
NSAlert* alert = CreateNSAlert(
parent_window, type, buttons, title, message, detail);
[alert autorelease];
if (parent_window)
return [alert runModalSheetForWindow:parent_window->GetNativeWindow()];
else
return [alert runModal];
}
void ShowMessageBox(NativeWindow* parent_window,
MessageBoxType type,
const std::vector<std::string>& buttons,
const std::string& title,
const std::string& message,
const std::string& detail,
const MessageBoxCallback& callback) {
NSAlert* alert = CreateNSAlert(
parent_window, type, buttons, title, message, detail);
ModalDelegate* delegate = [[ModalDelegate alloc] initWithCallback:callback
andAlert:alert];
NSWindow* window = parent_window ? parent_window->GetNativeWindow() : nil;
[alert beginSheetModalForWindow:window
modalDelegate:delegate
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:)
contextInfo:nil];
}
} // namespace atom

View File

@@ -0,0 +1,294 @@
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "browser/ui/message_box.h"
#include "base/callback.h"
#include "base/message_loop.h"
#include "base/run_loop.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "browser/native_window.h"
#include "skia/ext/skia_utils_win.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/controls/message_box_view.h"
#include "ui/views/layout/grid_layout.h"
#include "ui/views/layout/layout_constants.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
namespace atom {
namespace {
// The group used by the buttons. This name is chosen voluntarily big not to
// conflict with other groups that could be in the dialog content.
const int kButtonGroup = 1127;
class MessageDialog : public base::MessageLoop::Dispatcher,
public views::WidgetDelegate,
public views::View,
public views::ButtonListener {
public:
MessageDialog(NativeWindow* parent_window,
MessageBoxType type,
const std::vector<std::string>& buttons,
const std::string& title,
const std::string& message,
const std::string& detail);
virtual ~MessageDialog();
void Show();
int GetResult() const;
void set_callback(const MessageBoxCallback& callback) {
delete_on_close_ = true;
callback_ = callback;
}
private:
// Overridden from MessageLoop::Dispatcher:
virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE;
// Overridden from views::WidgetDelegate:
virtual string16 GetWindowTitle() const;
virtual void WindowClosing() OVERRIDE;
virtual views::Widget* GetWidget() OVERRIDE;
virtual const views::Widget* GetWidget() const OVERRIDE;
virtual views::View* GetContentsView() OVERRIDE;
virtual views::View* GetInitiallyFocusedView() OVERRIDE;
virtual ui::ModalType GetModalType() const OVERRIDE;
// Overridden from views::View:
virtual gfx::Size GetPreferredSize() OVERRIDE;
virtual void Layout() OVERRIDE;
virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE;
// Overridden from views::ButtonListener:
virtual void ButtonPressed(views::Button* sender,
const ui::Event& event) OVERRIDE;
bool should_close_;
bool delete_on_close_;
int result_;
string16 title_;
views::Widget* widget_;
views::MessageBoxView* message_box_view_;
std::vector<views::LabelButton*> buttons_;
MessageBoxCallback callback_;
DISALLOW_COPY_AND_ASSIGN(MessageDialog);
};
////////////////////////////////////////////////////////////////////////////////
// MessageDialog, public:
MessageDialog::MessageDialog(NativeWindow* parent_window,
MessageBoxType type,
const std::vector<std::string>& buttons,
const std::string& title,
const std::string& message,
const std::string& detail)
: should_close_(false),
delete_on_close_(false),
result_(-1),
title_(UTF8ToUTF16(title)),
widget_(NULL),
message_box_view_(NULL) {
DCHECK_GT(buttons.size(), 0u);
set_owned_by_client();
views::MessageBoxView::InitParams params(UTF8ToUTF16(title));
params.message = UTF8ToUTF16(message + "\n" + detail);
message_box_view_ = new views::MessageBoxView(params);
AddChildView(message_box_view_);
for (size_t i = 0; i < buttons.size(); ++i) {
views::LabelButton* button = new views::LabelButton(
this, UTF8ToUTF16(buttons[i]));
button->set_tag(i);
button->set_min_size(gfx::Size(60, 20));
button->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON);
button->SetGroup(kButtonGroup);
buttons_.push_back(button);
AddChildView(button);
}
// First button is always default button.
buttons_[0]->SetIsDefault(true);
buttons_[0]->AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
views::Widget::InitParams widget_params;
widget_params.delegate = this;
widget_params.top_level = true;
if (parent_window)
widget_params.parent = parent_window->GetNativeWindow();
widget_ = new views::Widget;
widget_->set_frame_type(views::Widget::FRAME_TYPE_FORCE_NATIVE);
widget_->Init(widget_params);
// Bind to ESC.
AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
set_background(views::Background::CreateSolidBackground(
skia::COLORREFToSkColor(GetSysColor(COLOR_WINDOW))));
}
MessageDialog::~MessageDialog() {
}
void MessageDialog::Show() {
widget_->Show();
}
int MessageDialog::GetResult() const {
// When the dialog is closed without choosing anything, we think the user
// chose 'Cancel', otherwise we think the default behavior is chosen.
if (result_ == -1) {
for (size_t i = 0; i < buttons_.size(); ++i)
if (LowerCaseEqualsASCII(buttons_[i]->GetText(), "cancel")) {
return i;
}
return 0;
} else {
return result_;
}
}
////////////////////////////////////////////////////////////////////////////////
// MessageDialog, private:
bool MessageDialog::Dispatch(const base::NativeEvent& event) {
TranslateMessage(&event);
DispatchMessage(&event);
return !should_close_;
}
string16 MessageDialog::GetWindowTitle() const {
return title_;
}
void MessageDialog::WindowClosing() {
should_close_ = true;
if (delete_on_close_) {
callback_.Run(GetResult());
base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
}
}
views::Widget* MessageDialog::GetWidget() {
return widget_;
}
const views::Widget* MessageDialog::GetWidget() const {
return widget_;
}
views::View* MessageDialog::GetContentsView() {
return this;
}
views::View* MessageDialog::GetInitiallyFocusedView() {
if (buttons_.size() > 0)
return buttons_[0];
else
return this;
}
ui::ModalType MessageDialog::GetModalType() const {
return ui::MODAL_TYPE_WINDOW;
}
gfx::Size MessageDialog::GetPreferredSize() {
gfx::Size size(0, buttons_[0]->GetPreferredSize().height());
for (size_t i = 0; i < buttons_.size(); ++i)
size.Enlarge(buttons_[i]->GetPreferredSize().width(), 0);
// Button spaces.
size.Enlarge(views::kRelatedButtonHSpacing * (buttons_.size() - 1),
views::kRelatedControlVerticalSpacing);
// The message box view.
gfx::Size contents_size = message_box_view_->GetPreferredSize();
size.Enlarge(0, contents_size.height());
if (contents_size.width() > size.width())
size.set_width(contents_size.width());
return size;
}
void MessageDialog::Layout() {
gfx::Rect bounds = GetContentsBounds();
// Layout the row containing the buttons.
int x = bounds.width();
int height = buttons_[0]->GetPreferredSize().height() +
views::kRelatedControlVerticalSpacing;
// NB: We iterate through the buttons backwards here because
// Mac and Windows buttons are laid out in opposite order.
for (int i = buttons_.size() - 1; i >= 0; --i) {
gfx::Size size = buttons_[i]->GetPreferredSize();
x -= size.width() + views::kRelatedButtonHSpacing;
buttons_[i]->SetBounds(x, bounds.height() - height,
size.width(), size.height());
}
// Layout the message box view.
message_box_view_->SetBounds(bounds.x(), bounds.y(), bounds.width(),
bounds.height() - height);
}
bool MessageDialog::AcceleratorPressed(const ui::Accelerator& accelerator) {
DCHECK_EQ(accelerator.key_code(), ui::VKEY_ESCAPE);
widget_->Close();
return true;
}
void MessageDialog::ButtonPressed(views::Button* sender,
const ui::Event& event) {
result_ = sender->tag();
widget_->Close();
}
} // namespace
int ShowMessageBox(NativeWindow* parent_window,
MessageBoxType type,
const std::vector<std::string>& buttons,
const std::string& title,
const std::string& message,
const std::string& detail) {
MessageDialog dialog(parent_window, type, buttons, title, message, detail);
dialog.Show();
{
base::MessageLoop::ScopedNestableTaskAllower allow(
base::MessageLoopForUI::current());
base::RunLoop run_loop(&dialog);
run_loop.Run();
}
return dialog.GetResult();
}
void ShowMessageBox(NativeWindow* parent_window,
MessageBoxType type,
const std::vector<std::string>& buttons,
const std::string& title,
const std::string& message,
const std::string& detail,
const MessageBoxCallback& callback) {
// The dialog would be deleted when the dialog is closed.
MessageDialog* dialog = new MessageDialog(
parent_window, type, buttons, title, message, detail);
dialog->set_callback(callback);
dialog->Show();
}
} // namespace atom

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