Compare commits

..

124 Commits

Author SHA1 Message Date
Electron Bot
b763d81d79 Bump v4.2.2 2019-05-16 14:21:46 -07:00
Cheng Zhao
7521454161 fix: lagging when using tabbed window (#18314) 2019-05-16 14:20:09 -07:00
Electron Bot
36f0583077 Bump v4.2.1 2019-05-14 11:52:55 -07:00
Jeremy Apthorp
96391dbb3b fix: default enable_negotiate_port to false (4-2-x) (#18287)
* fix: default enable_negotiate_port to false (#18251)

* chore: command_line is not a pointer
2019-05-14 11:16:00 -07:00
Electron Bot
d5cf2e1244 Bump v4.2.0 2019-05-02 14:34:52 -07:00
Samuel Attard
9a665eb5fb feat: allow setting of global fallback user agent (#18110) 2019-05-02 13:06:01 -07:00
Samuel Attard
6b16695ba3 build: reset version to 4.2.0-beta.0 in prep for 4.2.0 2019-05-01 16:55:28 -07:00
Milan Burda
ac714a1d02 fix: allow stream protocols to return headers with multiple values (#14887) (#18094)
* fix: allow stream protocols to return headers with multiple values

This allows stream protocols to return headers with multiple values as
an array of values.

Fixes https://github.com/electron/electron/issues/14778

* Prefer ConvertFromV8

* Cleanup header conversion

1. Deduplicate the code by using a lambda
2. Remove duplicate calls to headers->Get(key)

* Fix broken test

Headers with multiple values are now being converted correctly, this
test asserted the wrong behavior.
2019-05-01 16:36:42 -07:00
trop[bot]
e5e94fce02 docs: fix webContents 'new-window' event handler sample (#18096) 2019-05-01 13:43:39 -07:00
trop[bot]
f65a7983c6 chore: disable Vulkan validation layers (#18060) 2019-05-01 10:46:08 -07:00
trop[bot]
d1f5d20098 fix: explicitly focus window on win.show() (#18079) 2019-05-01 10:44:37 -07:00
Milan Burda
2f67cd5b35 Revert "build: disable Vulkan support by default (#17789)" (#17987)
This reverts commit ccad8ec125.
2019-04-30 06:43:12 -07:00
Milan Burda
3fd0ac6d44 fix: remove non-existent gpu-crashed event on <webview> (#17317) (#18003) 2019-04-29 15:22:01 -07:00
Electron Bot
4a6cad7ba5 Bump v4.1.5 2019-04-24 14:35:12 -07:00
Taiki Akita
eccede0c0d fix: add a compatibility EVP_CIPH_OCB_MODE value (#16214). (#17873)
* fix: add a compatibility EVP_CIPH_OCB_MODE value (#16214).

Backported google/boringssl@4b9683 to 4.1.x branch. This patch
replaces the no-op modes with negative numbers rather than zero.
Stream ciphers like RC4 report a "mode" of zero, so code comparing
the mode to a dummy value will get confused.

This patch fixed issue #16214.

* revert: "fix: add a compatibility EVP_CIPH_OCB_MODE value (#16214)."

This reverts commit f1156a8ee4.

* fix: (recommit) add a compatibility EVP_CIPH_OCB_MODE value (#16214).

Recommited change @f1156a with patches generated by
"git-export-patches".
2019-04-23 09:10:42 -07:00
Jeff Genovy
9ed83e7512 fix: Add support for the new Japanese Era (Reiwa) to ICU (#17833)
This change backports changes from the upstream ICU project's
maintenance release for ICU 62.2 in order to enable support for the new
Japanese Era "Reiwa" in Chromium 69 (which uses ICU version 62.1).

Upstream release: http://site.icu-project.org/download/62

Additionally, this change also includes the following fixes/updates:
 - Update IANA time zone tzdata to 2019a.
 - Japanese calendar era calculation should use local time, not UTC.
 - Japanese calendar: use all valid eras for calendar calculations.
 - Fix LocalMemory move constructor delete issue.

This change requires re-generating the checked-in Chromium ICU data
files (ex: icudtl.dat) in order for them to have the updated data for
the new era name. However, that will be done with a separate commit in
order to make the ".patch" files cleaner.

Note: All of the checked-in files are be regenerated, even though
the Electron project only uses the "common" one.

Also included in this change are some minor changes to the scripts used
by Chromium for building the data files so that they actually work on
Ubuntu.
2019-04-19 14:41:21 -07:00
Milan Burda
b3ed83055c fix: execute session preload scripts in sandboxed renderers (#17875) 2019-04-19 12:50:29 -07:00
Milan Burda
2360f3eb11 fix: copy pixels in AddImageSkiaRepFromBuffer (#17843) (#17862) 2019-04-18 08:53:32 -07:00
trop[bot]
4175e947bb doc: document occasional mismatch between NSImageName and string (#17848) 2019-04-17 21:01:17 -04:00
trop[bot]
1cbcd05ab7 fix: correct crash reporter for Windows on Arm (#17793)
ARM64 cleans up and moves around RUNTIME_FUNCTION structure fields.
With this patch applied, electron should build cleanly for Windows on Arm.
2019-04-16 19:42:01 -04:00
trop[bot]
40c7e767ef fix: reset the NSUserNotication handle on dismiss (#17821) 2019-04-16 16:24:16 -07:00
Milan Burda
66c5a8362a fix: emit process 'loaded' event in sandboxed renderers (#17807) 2019-04-16 16:23:53 -07:00
Shelley Vohr
d6ba1421fa docs: note desktop audio limitation on macOS (#17815) 2019-04-16 18:52:37 -04:00
Alexey Kuzmin
ccad8ec125 build: disable Vulkan support by default (#17789)
Is was enabled in
327326656f
which landed in 69.0.3460.0

Vulkan support would require a command line parameter "--enable-vulkan"
to be passes to a binary to be enabled anyway,
so this change doesn't actual alter the current behaviour of Electron.

(cherry picked from commit e05e45d62d)
2019-04-16 11:30:15 -04:00
Milan Burda
d2bff97199 fix: update fix_font_thickness.patch (#17737) 2019-04-15 19:53:10 -04:00
Milan Burda
46a1ce8117 fix: report module name when require fails in sandboxed renderers (#17704) 2019-04-09 21:00:45 -04:00
Electron Bot
042f24c5b6 Bump v4.1.4 2019-04-04 11:36:42 -07:00
Milan Burda
c3624116ae fix: Issue 907211: Heap-use-after-free in viz::HostFrameSinkManager::InvalidateFrameSinkId (#17658) 2019-04-04 11:33:48 -07:00
Milan Burda
492397b815 fix: Issue 912211: Security: a use-after-free in RenderFrameImple can lead to an RCE (#17659) 2019-04-04 09:11:45 -07:00
Shelley Vohr
3a0b72e5dc fix: expose process.getProcessMemoryInfo to sandbox (#17669) 2019-04-04 09:09:00 -07:00
trop[bot]
914939c793 build: optionally exclude some parts of patches from being applied (#17684) 2019-04-04 09:08:46 -07:00
trop[bot]
b8874913f2 docs: clarify cookie domain normalization (#17662) 2019-04-02 18:26:42 -07:00
Alexey Kuzmin
dc959414a3 fix: Make --explicitly-allowed-ports work with NetworkService. (#17642)
https://chromium-review.googlesource.com/c/1400042
2019-04-02 13:25:14 -07:00
Milan Burda
bcdc4435b4 fix: Issue 888678: Heap-use-after-free in content::KeyboardLockServiceImpl::GetKeyboardLayoutMap (#17632) 2019-04-01 18:55:51 -07:00
Electron Bot
303da32dd3 Bump v4.1.3 2019-03-29 10:19:35 -07:00
Robo
8fd91cc35b fix: make StreamSubscriber ref counted (#17267)
It is owned by URLRequestStreamJob on the IO thread once request starts,
but if the ownership was abondoned while transfering it to IO thread
which is possible when a request is aborted, then we need to make sure
its destroyed on the right thread to avoid lock in v8.
2019-03-29 12:28:55 -04:00
Milan Burda
3001c76483 fix: backport [IntersectionObserver] Report coordinates as CSS pixels. (#17583) 2019-03-29 17:18:39 +01:00
Electron Bot
28a9963ca7 Bump v4.1.2 2019-03-28 13:04:12 -07:00
Samuel Attard
b5f290d8d2 fix: add patch for framework file permissions (#17582) 2019-03-28 13:01:05 -07:00
trop[bot]
4574a21ed3 fix: handle a race condition between preload scripts executing and navigations (#17596)
There is a race condition between DidCreateScriptContext and another
navigation occuring in the main process. If the navigation occurs while
the preload script is running, the same process is re-used.  This
ensures that any pending navigations are completely removed / ignored
when we trigger a new navigation.

Fixes #17576
2019-03-28 12:56:23 -07:00
Samuel Attard
7aaaa24da7 Revert "Bump v4.1.2"
This reverts commit ded4a94a92.
2019-03-28 12:53:49 -07:00
Electron Bot
ded4a94a92 Bump v4.1.2 2019-03-28 08:57:59 -07:00
trop[bot]
c6fd15e641 fix: correctly return properties on the netLog module (#17544) 2019-03-27 15:38:16 -07:00
Electron Bot
36965a6b4e Revert "Bump v4.1.2"
This reverts commit 53c3e01af6.
2019-03-27 14:22:01 -07:00
Electron Bot
53c3e01af6 Bump v4.1.2 2019-03-27 13:58:56 -07:00
Jeff Genovy
d2571f3a9e fix: backport ICU changes for Japanese Era placeholder name (#17518)
This change backports changes from the upstream ICU 62.1 maintenance
branch into the Chromium copy of ICU, in order to enable the
"placeholder era" for the upcoming new Japanese Era.

Upstream change: https://github.com/unicode-org/icu/pull/514
Upstream ticket: https://unicode-org.atlassian.net/browse/ICU-20482

With this change you can set/define an Environment Variable
`ICU_ENABLE_TENTATIVE_ERA` to `true which will enable the placeholder
era name.

Note: This change requires re-generating the checked-in Chromium ICU
data files (icudtl.dat) in order for them to have the updated data
for the placeholder era name.

Also included in this change are some minor changes to the scripts
used by Chromium for building the data files so that they actually
work on Ubuntu.
2019-03-27 10:22:45 -04:00
trop[bot]
e1a8050eab fix: add missing buffer size check in nativeImage (#17567) 2019-03-27 09:37:13 -04:00
Samuel Attard
7b1f5a9cea fix: backport KDE icon size fix (#17497) 2019-03-21 14:19:11 -05:00
trop[bot]
5b9393c173 docs: clarify documentation for getLastCrashReport (backport: 4-1-x) (#17494)
* docs: clarify documentation for getLastCrashReport

* address feedback
2019-03-20 22:04:15 -07:00
trop[bot]
98180568f2 fix: take foreground visibility into account for isVisible on macOS (#17492) 2019-03-20 20:40:52 -07:00
Electron Bot
f005ac8d8b Bump v4.1.1 2019-03-20 13:49:36 -07:00
trop[bot]
a0d824ccf5 fix: use a more unique identifier for NSUserNotification instances (#17483)
So although apple has it documented that notifications with duplicate identifiers in the same session won't be presented.  They apparently forgot to mention that macOS also non-deterministically and without any errors, logs or warnings will also not present some notifications in future sessions if they have a previously used identifier.

As such, we're going to truly randomize these identifiers so they are
unique between apps and sessions.  The identifier now consists of a
randomly generated UUID and the app bundle id.
2019-03-20 11:19:08 -07:00
trop[bot]
ee1529587c fix: throw error when inserting menu items out-of-range (#17461)
* fix: throw error when inserting menu items out-of-range

* also check pos < 0
2019-03-19 12:59:11 -07:00
trop[bot]
5e665c1d38 fix: don't crash when nativeImage.createFromBuffer() called with invalid buffer (#17373) 2019-03-19 12:14:11 -07:00
Milan Burda
61d1df2b43 fix: remove crashReporterRenderer.sendSync() workaround for init() (#17409) 2019-03-19 11:56:39 -07:00
Electron Bot
6c4ee66165 Bump v4.1.0 2019-03-13 16:12:16 -07:00
Samuel Attard
84b014577f build: reset version to 4.1.0-beta.0 in preparation for 4.1.0 stable release 2019-03-13 15:18:05 -07:00
trop[bot]
84ec42463b fix: remove label/image from segment if they are mutated to undefined/null (#17335) 2019-03-11 17:19:53 -07:00
Jeremy Apthorp
01c8f698ee fix: crash in ECDH.setPrivateKey (#17297) 2019-03-08 14:06:57 -08:00
trop[bot]
f53a9c1268 test: fix incorrect reference to skip (#17280) 2019-03-08 16:12:23 -05:00
Electron Bot
56c545f679 Bump v4.0.8 2019-03-07 11:31:03 -08:00
Jeremy Apthorp
34c1a53441 fix: FileReader: Make a copy of the ArrayBuffer when returning partial results (#17256)
backports https://chromium-review.googlesource.com/c/chromium/src/+/1495209
2019-03-07 11:26:01 -08:00
Electron Bot
8d330f7dde Bump v4.0.7 2019-03-04 14:50:36 -08:00
Jeremy Apthorp
3c0d90eca8 fix: correct chrome version in chrome_version.h (#17218) 2019-03-04 13:42:08 -08:00
trop[bot]
9d30245fb4 fix: access of out-of-scope reference in ShowOpenDialog and ShowSaveDialog (#17176)
In the mac file dialog implementation of show*OpenDialog, a settings
object is passed down to the dialog completion handler.
However at the time the completion handler is invoked, the settings
object is already out-of-scope, resulting in an invalid access to
the security_scoped_bookmarks flag.
The fix is to capture the value of the flag and passing that directly
to the completion handler.

fixes issue #16664
2019-03-01 08:18:50 +00:00
Alexey Kuzmin
0efccf45bc chore: remove an unused header (#17158) 2019-02-28 17:32:49 +01:00
trop[bot]
4e0d4c4785 fix: set cancelId to 1 when defaultId == 0 and no 'cancel' button (#17151) 2019-02-27 13:01:34 +00:00
trop[bot]
6d313b48f2 Fix webContents.print() (#17116) 2019-02-27 08:25:15 +00:00
Electron Bot
330e8abd16 Bump v4.0.6 2019-02-26 15:01:41 -08:00
Roller Bot
ffd8c36f4f chore: bump chromium in DEPS to 69.0.3497.128 (#16636) 2019-02-25 18:00:06 -08:00
trop[bot]
efa12608e0 fix: pass result to chrome.tabs.executeScript callback (backport: 4-0-x) (#16948)
* fix: pass result to chrome.tabs.executeScript callback

Additionally, remove `nextId` counter in favor of `originResultID` counter which is more widely used in this file.

* fix: remove need for eslint override and better match style
2019-02-15 10:21:46 -08:00
Jeremy Apthorp
969ac4ced1 fix: enable inputpane virtual keyboard by default (#16944)
backports e6c18518b from chromium, fixes #13832
2019-02-15 07:38:47 -10:00
trop[bot]
a2d77352e5 docs: update dialog documentation for "undefined" ret vals (#16976) 2019-02-14 17:33:44 -08:00
Electron Bot
55c48efb90 Bump v4.0.5 2019-02-14 14:09:13 -08:00
Jeremy Apthorp
7a285cd0ea fix: renderer hang in cc BeginMainFrame (#16946)
* chore: re-export chromium patches

this is just git-import-patches && git-export-patches

* fix: renderer hang in cc BeginMainFrame

backports https://chromium-review.googlesource.com/c/chromium/src/+/1419132
2019-02-14 05:50:22 -10:00
John Kleinschmidt
415fbfaf41 ci: Move MacOS builds to CircleCI (4-0-x) (#16933)
* ci: build mac on CircleCI (#16656)

* ci: make macOS CI faster (#16766)

* ci: cache brew update result

* ci: checkout and sync the macOS build on a linux machine for speed

* ci: set MAS_BUILD=true on mas builds (#16824)

* build: remove non-arm vstsJobs (#16793)

* ci: fix binaries for chromedriver build on macOS
2019-02-13 11:25:03 -10:00
trop[bot]
1a41e196e8 build: ensure that the uploaded symbol path is correct for our symbol server (#16916) 2019-02-13 07:08:50 -10:00
trop[bot]
4f63509ebd fix: backport patch to sync exposed crypto (backport: 4-0-x) (#16912) 2019-02-12 21:29:57 -08:00
Cheng Zhao
45a554f305 Fix memory leak when using webFrame and spell checker (4-0-x) (#16772)
* fix: do not create native api::WebFrame in webFrame

When reloading a page without restarting renderer process (for example
sandbox mode), the blink::WebFrame is not destroyed, but api::WebFrame
is always recreated for the new page context. This leaves a leak of
api::WebFrame.

* fix: remove spell checker when page context is released
2019-02-12 06:36:23 -10:00
trop[bot]
29a0bc23c4 fix: release-notes plays more nicely with clerk (#16901)
Explicitly look not just for Clerk's "notes persisted"
message but also its "no release notes" message.
2019-02-12 10:17:40 -06:00
trop[bot]
f7508f17c5 chore: fix "creates offscreen window with correct size" spec on Mac with Retina display (#16877) 2019-02-11 13:14:44 -08:00
trop[bot]
d2538cd3b1 fix: enable property having no effect on submenus (#16857) 2019-02-09 09:58:31 -08:00
trop[bot]
fae52d8e4a fix: don't forward IPC filtering events to app for dev-tools and extensions (#16716) 2019-02-08 15:35:18 -08:00
trop[bot]
064f198162 chore: make crash-reporter specs not use URL module (#16849) 2019-02-08 15:32:41 -08:00
trop[bot]
fd2a9cb056 chore: disable get/setLoginItemSettings specs (#16844) 2019-02-08 15:13:44 -08:00
trop[bot]
b4c27eeaa1 fix: don't construct submenu if it's invisible (#16845) 2019-02-08 15:13:07 -08:00
trop[bot]
93b4d20c59 fix: display empty menu item for non-visible submenus (backport: 4-0-x) (#16847)
* fix: display empty menu item for nonvisible submenus

* use Chromium UI string ID
2019-02-08 15:12:34 -08:00
trop[bot]
c647bf5d27 docs: added webContents.getType() method (backport: 4-0-x) (#16786)
* docs: added webContents.getType() method

* docs: add enumeration of return value for webContents.getType()

* docs: getType() in WebContents should be class method not module method
2019-02-07 08:51:54 -08:00
trop[bot]
cbca75d184 build: ensure index.json is actually valid JSON before uploading (backport: 4-0-x) (#16750)
* build: ensure index.json is actually valid JSON before uploading

* chore: fix py linting for validation of index.json
2019-02-05 15:01:09 -08:00
trop[bot]
8054fc83ac fix: crash when calling setProgressBar on macOS (backport: 4-0-x) (#16727)
* fix: correctly check whether dock has progress bar

* fix: do not leak memory when setting dockTile
2019-02-04 20:56:38 -08:00
Shelley Vohr
0b7680aa14 fix: correctly handle IPC for promise-based methods (#16433) (#16654) 2019-02-04 16:01:42 -08:00
Shelley Vohr
668e85dd7c fix: show proper clerk notes in release notes script (#16694)
* fix: show proper clerk notes in release notes script (backport: 4-0-x) (#16678)

* fix: Note detection in PR

* fix: 'BREAKING CHANGE' detection in PR body

* fix: when to include PRs that landed in other branches too

* fix: when available, use clerk's notes

* address throw edge case
2019-02-04 14:09:56 -08:00
Milan Burda
e253c9bfe6 feat: add additional remote APIs filtering (#16688) 2019-02-04 08:46:54 -08:00
Birunthan Mohanathas
c00d3536d1 fix: use async save dialog for anchor download attribute (4-0-x) (#16647)
Backport of #16612 and #16646

Notes: Fix broken save dialog on macOS for `<a>` downloads
2019-02-03 23:55:18 -08:00
Electron Bot
0710d69acd Bump v4.0.4 2019-02-01 18:16:15 -08:00
Shelley Vohr
8a25cfcadc Revert "fix: show proper clerk notes in release notes script (backport: 4-0-x) (#16678)"
This reverts commit 3f68d69c40.
2019-02-01 18:14:19 -08:00
Shelley Vohr
6865206ea7 Revert "Bump v4.0.4"
This reverts commit 27ff31899b.
2019-02-01 18:07:58 -08:00
Samuel Attard
abb1a09f16 fix: use a new Electron dedicated ABI number for Electron 4.0 (#16687) 2019-02-01 17:44:31 -08:00
trop[bot]
a184e37f25 docs: fix referrer typedef in OnCompletedDetails (#16674) 2019-02-01 17:44:05 -08:00
trop[bot]
3f68d69c40 fix: show proper clerk notes in release notes script (backport: 4-0-x) (#16678)
* fix: Note detection in PR

* fix: 'BREAKING CHANGE' detection in PR body

* fix: when to include PRs that landed in other branches too

* fix: when available, use clerk's notes
2019-02-01 17:43:57 -08:00
Shelley Vohr
0dd8fd57de Undo (#16690)
* Revert "fix asset fail workaround (#16680)"

This reverts commit e61c8543f1.

* Revert "build: hack around GitHub upload API failure / flake (#16667)"

This reverts commit 1098d0f414.
2019-02-01 17:42:56 -08:00
Electron Bot
27ff31899b Bump v4.0.4 2019-02-01 10:23:38 -08:00
Shelley Vohr
e61c8543f1 fix asset fail workaround (#16680)
* fix asset fail workaround

* uploadResponse.data.id => uploadResponse.id

* asset_id => id
2019-02-01 10:21:50 -08:00
Shelley Vohr
f774303923 Revert "Bump v4.0.4"
This reverts commit 39f26838ef.
2019-02-01 09:48:37 -08:00
Electron Bot
39f26838ef Bump v4.0.4 2019-02-01 08:28:02 -08:00
Shelley Vohr
1098d0f414 build: hack around GitHub upload API failure / flake (#16667) 2019-02-01 08:26:26 -08:00
Shelley Vohr
ca6f494ba6 Revert "Bump v4.0.4"
This reverts commit 7f6c2372f8.
2019-02-01 08:20:26 -08:00
Electron Bot
7f6c2372f8 Bump v4.0.4 2019-01-31 08:45:46 -08:00
Charles Kerr
20a6be8962 fix: move open handling to web-contents.js (#16623) 2019-01-31 14:57:30 +01:00
trop[bot]
28c19dad8f fix: expose aes-cfb ciphers from boringssl (#16617)
Ref #16195
2019-01-30 16:48:13 -08:00
Nitish Sakhawalkar
ada60a938a feat: Implement process.getProcessMemoryInfo to get the process memory usage (#14847) (#16591)
* feat: Implement process.getMemoryFootprint to get the process memory usage

* Add spec

* fix: must enter node env in callback

* Update function call

* Update spec

* Update API data

* update spec

* Update include

* update test for shared bytes

* Update atom/common/api/atom_bindings.cc

Co-Authored-By: nitsakh <nitsakh@icloud.com>

* Update atom/common/api/atom_bindings.cc

Co-Authored-By: nitsakh <nitsakh@icloud.com>

* Update API

* Update the callback isolate

* Update to work after app ready

* Update docs

* Update docs/api/process.md

Co-Authored-By: nitsakh <nitsakh@icloud.com>

* Update docs/api/process.md

Co-Authored-By: nitsakh <nitsakh@icloud.com>

* Fix crash
2019-01-30 12:44:55 -08:00
trop[bot]
0ee1f51883 docs: cancel is optional in OnHeadersReceivedResponse (#16548) 2019-01-29 21:09:42 -08:00
Jeremy Apthorp
56276d2102 fix: expose ripemd160 hash from boringssl (#16454) (#16574)
Ref #16195
2019-01-28 17:34:48 -08:00
Electron Bot
90407259a6 Bump v4.0.3 2019-01-28 15:40:32 -08:00
Jeremy Apthorp
939e65d262 fix: backport a memory leak fix in webrtc (#16555) 2019-01-28 11:47:33 -08:00
Electron Bot
6a797f2199 Revert "Bump v4.0.3"
This reverts commit 4b5afb5ccf.
2019-01-25 16:04:49 -08:00
Electron Bot
4b5afb5ccf Bump v4.0.3 2019-01-25 15:48:02 -08:00
Jeremy Apthorp
cf079f6c43 fix: registerStreamProtocol callback with large chunks (backport: 4-0-x) (#16540) 2019-01-25 10:59:08 -08:00
Nitish Sakhawalkar
0659093dfa fix: correctly destroy spellcheck client (#16524)
* fix: Destroy spellcheck client

* Address review comments
2019-01-25 10:39:46 -06:00
Lionel RADISSON
fb8bde0094 fix extra closing parenthesis (#16528) 2019-01-25 09:43:38 -05:00
Andy Dill
f747a66109 refactor: move text-to-speech out of chromium_src (#15024) (#16437)
* chore: add tts patch and buildflag, makes tts work again

* chore: add tts patch and buildflag, makes tts work again

* fix: make things compile

* build: add relevant tts files for linux

* fix: update patch and patch description, should now compile on mac

* build: move chrome specific sources under chromium_src:chrome target

* build: enable_extensions again

We are depending on them, check `//electron/chromium_src:chrome` target
for more info.

* fix: update tts.patch to receive notifications about browser context destruction

* fix: extend browser process from chrome layer

The global state g_browser_process is shared between //chrome
and //electron.

* spec: add basic speech synthesis test

* spec: skip speech tests on ci

* build: fix compilation on windows
2019-01-23 13:33:55 -05:00
trop[bot]
67ac6648c4 build: fail a build if some hooks don't succeed (#16505) 2019-01-23 11:29:43 -05:00
trop[bot]
84f1dc7f8c fix: takeHeapSnapshot() using public IPC channel for internal implementation (#16484) 2019-01-22 11:07:30 -08:00
trop[bot]
7c55db280b docs: fix web-request.md listener signatures in electron.d.ts (#16487) 2019-01-22 11:07:01 -08:00
175 changed files with 134205 additions and 4548 deletions

File diff suppressed because it is too large Load Diff

12
DEPS
View File

@@ -10,9 +10,9 @@ gclient_gn_args = [
vars = {
'chromium_version':
'69.0.3497.106',
'69.0.3497.128',
'node_version':
'4d44266b78256449dd6ae86e419e3ec07257b569',
'8bc5d171a0873c0ba49f9433798bc8b67399788c',
'boto_version': 'f7574aa6cc2c819430c1f05e9a1a1a666ef8169b',
'pyyaml_version': '3.12',
@@ -107,7 +107,7 @@ hooks = [
'action': [
'python',
'-c',
'import os; os.chdir("src"); os.chdir("electron"); os.system("npm install")',
'import os, subprocess; os.chdir(os.path.join("src", "electron")); subprocess.check_call(["python", "script/lib/npm.py", "install"]);',
],
},
{
@@ -117,7 +117,7 @@ hooks = [
'action': [
'python',
'-c',
'import os; os.chdir("src"); os.chdir("electron"); os.chdir("vendor"); os.chdir("boto"); os.system("python setup.py build");',
'import os, subprocess; os.chdir(os.path.join("src", "electron", "vendor", "boto")); subprocess.check_call(["python", "setup.py", "build"]);',
],
},
{
@@ -127,9 +127,9 @@ hooks = [
'action': [
'python',
'-c',
'import os; os.chdir("src"); os.chdir("electron"); os.chdir("vendor"); os.chdir("requests"); os.system("python setup.py build");',
'import os, subprocess; os.chdir(os.path.join("src", "electron", "vendor", "requests")); subprocess.check_call(["python", "setup.py", "build"]);',
],
}
},
]
recursedeps = [

View File

@@ -1 +1 @@
4.0.2
4.2.2

View File

@@ -7,6 +7,7 @@
#include <string>
#include <vector>
#include "atom/browser/browser.h"
#include "atom/common/atom_version.h"
#include "atom/common/chrome_version.h"
#include "atom/common/options_switches.h"
@@ -14,8 +15,10 @@
#include "base/files/file_util.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/common/content_constants.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/pepper_plugin_info.h"
#include "content/public/common/user_agent.h"
#include "electron/buildflags/buildflags.h"
@@ -173,6 +176,27 @@ void ConvertStringWithSeparatorToVector(std::vector<std::string>* vec,
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
}
std::string RemoveWhitespace(const std::string& str) {
std::string trimmed;
if (base::RemoveChars(str, " ", &trimmed))
return trimmed;
else
return str;
}
bool IsBrowserProcess() {
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
std::string process_type =
command_line->GetSwitchValueASCII(::switches::kProcessType);
return process_type.empty();
}
std::string BuildDefaultUserAgent() {
return "Chrome/" CHROME_VERSION_STRING " " ATOM_PRODUCT_NAME
"/" ATOM_VERSION_STRING;
}
} // namespace
AtomContentClient::AtomContentClient() {}
@@ -184,9 +208,35 @@ std::string AtomContentClient::GetProduct() const {
}
std::string AtomContentClient::GetUserAgent() const {
return content::BuildUserAgentFromProduct("Chrome/" CHROME_VERSION_STRING
" " ATOM_PRODUCT_NAME
"/" ATOM_VERSION_STRING);
if (IsBrowserProcess()) {
if (user_agent_override_.empty()) {
auto* browser = Browser::Get();
std::string name = RemoveWhitespace(browser->GetName());
std::string user_agent;
if (name == ATOM_PRODUCT_NAME) {
user_agent = BuildDefaultUserAgent();
} else {
user_agent = base::StringPrintf(
"%s/%s Chrome/%s " ATOM_PRODUCT_NAME "/" ATOM_VERSION_STRING,
name.c_str(), browser->GetVersion().c_str(), CHROME_VERSION_STRING);
}
return content::BuildUserAgentFromProduct(user_agent);
}
return user_agent_override_;
}
// In a renderer process the user agent should be provided on the CLI
// If it's not we just fallback to the default one, this should never happen
// but we want to handle it gracefully
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
std::string cli_user_agent = command_line->GetSwitchValueASCII("user-agent");
if (cli_user_agent.empty())
return BuildDefaultUserAgent();
return cli_user_agent;
}
void AtomContentClient::SetUserAgent(const std::string& user_agent) {
user_agent_override_ = user_agent;
}
base::string16 AtomContentClient::GetLocalizedString(int message_id) const {

View File

@@ -18,10 +18,12 @@ class AtomContentClient : public brightray::ContentClient {
AtomContentClient();
~AtomContentClient() override;
std::string GetUserAgent() const override;
void SetUserAgent(const std::string& user_agent);
protected:
// content::ContentClient:
std::string GetProduct() const override;
std::string GetUserAgent() const override;
base::string16 GetLocalizedString(int message_id) const override;
void AddAdditionalSchemes(Schemes* schemes) override;
void AddPepperPlugins(
@@ -31,6 +33,8 @@ class AtomContentClient : public brightray::ContentClient {
std::vector<media::CdmHostFilePath>* cdm_host_file_paths) override;
private:
std::string user_agent_override_ = "";
DISALLOW_COPY_AND_ASSIGN(AtomContentClient);
};

View File

@@ -7,6 +7,7 @@
#include <string>
#include <vector>
#include "atom/app/atom_content_client.h"
#include "atom/browser/api/atom_api_menu.h"
#include "atom/browser/api/atom_api_session.h"
#include "atom/browser/api/atom_api_web_contents.h"
@@ -43,6 +44,7 @@
#include "content/public/browser/client_certificate_delegate.h"
#include "content/public/browser/gpu_data_manager.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "media/audio/audio_manager.h"
#include "native_mate/object_template_builder.h"
@@ -1236,6 +1238,18 @@ void App::EnableMixedSandbox(mate::Arguments* args) {
command_line->AppendSwitch(switches::kEnableMixedSandbox);
}
void App::SetUserAgentFallback(const std::string& user_agent) {
AtomContentClient* client =
static_cast<AtomContentClient*>(content::GetContentClient());
client->SetUserAgent(user_agent);
}
std::string App::GetUserAgentFallback() {
AtomContentClient* client =
static_cast<AtomContentClient*>(content::GetContentClient());
return client->GetUserAgent();
}
#if defined(OS_MACOSX)
bool App::MoveToApplicationsFolder(mate::Arguments* args) {
return ui::cocoa::AtomBundleMover::Move(args);
@@ -1342,6 +1356,8 @@ void App::BuildPrototype(v8::Isolate* isolate,
.SetMethod("startAccessingSecurityScopedResource",
&App::StartAccessingSecurityScopedResource)
#endif
.SetProperty("userAgentFallback", &App::GetUserAgentFallback,
&App::SetUserAgentFallback)
.SetMethod("enableSandbox", &App::EnableSandbox)
.SetMethod("enableMixedSandbox", &App::EnableMixedSandbox);
}

View File

@@ -205,6 +205,8 @@ class App : public AtomBrowserClient::Delegate,
const std::string& info_type);
void EnableSandbox(mate::Arguments* args);
void EnableMixedSandbox(mate::Arguments* args);
void SetUserAgentFallback(const std::string& user_agent);
std::string GetUserAgentFallback();
#if defined(OS_MACOSX)
bool MoveToApplicationsFolder(mate::Arguments* args);

View File

@@ -1184,6 +1184,9 @@ void WebContents::LoadURL(const GURL& url, const mate::Dictionary& options) {
params.transition_type = ui::PAGE_TRANSITION_TYPED;
params.should_clear_history_list = true;
params.override_user_agent = content::NavigationController::UA_OVERRIDE_TRUE;
// Discord non-committed entries to ensure that we don't re-use a pending
// entry
web_contents()->GetController().DiscardNonCommittedEntries();
web_contents()->GetController().LoadURLWithParams(params);
// Set the background color of RenderWidgetHostView.
@@ -1455,7 +1458,7 @@ bool WebContents::IsDOMReady() const {
#if BUILDFLAG(ENABLE_PRINTING)
void WebContents::Print(mate::Arguments* args) {
bool silent, print_background = false;
bool silent = false, print_background = false;
base::string16 device_name;
mate::Dictionary options = mate::Dictionary::CreateEmpty(args->isolate());
base::DictionaryValue settings;

View File

@@ -17,12 +17,15 @@ namespace mate {
StreamSubscriber::StreamSubscriber(
v8::Isolate* isolate,
v8::Local<v8::Object> emitter,
base::WeakPtr<atom::URLRequestStreamJob> url_job)
: isolate_(isolate),
base::WeakPtr<atom::URLRequestStreamJob> url_job,
scoped_refptr<base::SequencedTaskRunner> ui_task_runner)
: base::RefCountedDeleteOnSequence<StreamSubscriber>(ui_task_runner),
isolate_(isolate),
emitter_(isolate, emitter),
url_job_(url_job),
weak_factory_(this) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(ui_task_runner->RunsTasksInCurrentSequence());
auto weak_self = weak_factory_.GetWeakPtr();
On("data", base::Bind(&StreamSubscriber::OnData, weak_self));
On("end", base::Bind(&StreamSubscriber::OnEnd, weak_self));
@@ -30,13 +33,12 @@ StreamSubscriber::StreamSubscriber(
}
StreamSubscriber::~StreamSubscriber() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
RemoveAllListeners();
}
void StreamSubscriber::On(const std::string& event,
EventCallback&& callback) { // NOLINT
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
DCHECK(js_handlers_.find(event) == js_handlers_.end());
v8::Locker locker(isolate_);
@@ -50,7 +52,7 @@ void StreamSubscriber::On(const std::string& event,
}
void StreamSubscriber::Off(const std::string& event) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
DCHECK(js_handlers_.find(event) != js_handlers_.end());
v8::Locker locker(isolate_);
@@ -96,6 +98,7 @@ void StreamSubscriber::OnError(mate::Arguments* args) {
}
void StreamSubscriber::RemoveAllListeners() {
DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
v8::Locker locker(isolate_);
v8::Isolate::Scope isolate_scope(isolate_);
v8::HandleScope handle_scope(isolate_);

View File

@@ -11,6 +11,8 @@
#include <vector>
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/memory/ref_counted_delete_on_sequence.h"
#include "base/memory/weak_ptr.h"
#include "content/public/browser/browser_thread.h"
#include "v8/include/v8.h"
@@ -23,17 +25,25 @@ namespace mate {
class Arguments;
class StreamSubscriber {
class StreamSubscriber
: public base::RefCountedDeleteOnSequence<StreamSubscriber> {
public:
REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();
StreamSubscriber(v8::Isolate* isolate,
v8::Local<v8::Object> emitter,
base::WeakPtr<atom::URLRequestStreamJob> url_job);
~StreamSubscriber();
base::WeakPtr<atom::URLRequestStreamJob> url_job,
scoped_refptr<base::SequencedTaskRunner> ui_task_runner);
private:
friend class base::DeleteHelper<StreamSubscriber>;
friend class base::RefCountedDeleteOnSequence<StreamSubscriber>;
using JSHandlersMap = std::map<std::string, v8::Global<v8::Value>>;
using EventCallback = base::Callback<void(mate::Arguments* args)>;
~StreamSubscriber();
void On(const std::string& event, EventCallback&& callback); // NOLINT
void Off(const std::string& event);

View File

@@ -90,6 +90,10 @@
#include "components/services/pdf_compositor/public/interfaces/pdf_compositor.mojom.h"
#endif // BUILDFLAG(ENABLE_PRINTING)
#if BUILDFLAG(ENABLE_TTS)
#include "chrome/browser/speech/tts_message_filter.h"
#endif // BUILDFLAG(ENABLE_TTS)
using content::BrowserThread;
namespace atom {
@@ -222,6 +226,10 @@ void AtomBrowserClient::RenderProcessWillLaunch(
process_id, host->GetBrowserContext()));
#endif
#if BUILDFLAG(ENABLE_TTS)
host->AddFilter(new TtsMessageFilter(host->GetBrowserContext()));
#endif
ProcessPreferences prefs;
auto* web_preferences =
WebContentsPreferences::From(GetWebContentsFromProcessID(process_id));

View File

@@ -6,6 +6,7 @@
#include <utility>
#include "atom/app/atom_content_client.h"
#include "atom/browser/atom_blob_reader.h"
#include "atom/browser/atom_browser_main_parts.h"
#include "atom/browser/atom_download_manager_delegate.h"
@@ -42,6 +43,7 @@
#include "components/proxy_config/proxy_config_pref_names.h"
#include "content/browser/blob_storage/chrome_blob_storage_context.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_client.h"
#include "content/public/common/user_agent.h"
#include "net/base/escape.h"
@@ -51,14 +53,6 @@ namespace atom {
namespace {
std::string RemoveWhitespace(const std::string& str) {
std::string trimmed;
if (base::RemoveChars(str, " ", &trimmed))
return trimmed;
else
return str;
}
// Convert string to lower case and escape it.
std::string MakePartitionName(const std::string& input) {
return net::EscapePath(base::ToLowerASCII(input));
@@ -78,19 +72,9 @@ AtomBrowserContext::AtomBrowserContext(const std::string& partition,
storage_policy_(new SpecialStoragePolicy),
in_memory_(in_memory),
weak_factory_(this) {
// Construct user agent string.
Browser* browser = Browser::Get();
std::string name = RemoveWhitespace(browser->GetName());
std::string user_agent;
if (name == ATOM_PRODUCT_NAME) {
user_agent = "Chrome/" CHROME_VERSION_STRING " " ATOM_PRODUCT_NAME
"/" ATOM_VERSION_STRING;
} else {
user_agent = base::StringPrintf(
"%s/%s Chrome/%s " ATOM_PRODUCT_NAME "/" ATOM_VERSION_STRING,
name.c_str(), browser->GetVersion().c_str(), CHROME_VERSION_STRING);
}
user_agent_ = content::BuildUserAgentFromProduct(user_agent);
AtomContentClient* client =
static_cast<AtomContentClient*>(content::GetContentClient());
user_agent_ = client->GetUserAgent();
// Read options.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();

View File

@@ -88,19 +88,53 @@ void AtomDownloadManagerDelegate::OnDownloadPathGenerated(
if (relay)
window = relay->GetNativeWindow();
auto* web_preferences = WebContentsPreferences::From(web_contents);
bool offscreen =
!web_preferences || web_preferences->IsEnabled(options::kOffscreen);
// Show save dialog if save path was not set already on item
base::FilePath path;
GetItemSavePath(item, &path);
// Show save dialog if save path was not set already on item
file_dialog::DialogSettings settings;
settings.parent_window = window;
settings.force_detached = offscreen;
settings.title = item->GetURL().spec();
settings.default_path = default_path;
if (path.empty() && file_dialog::ShowSaveDialog(settings, &path)) {
if (path.empty()) {
file_dialog::DialogSettings settings;
settings.parent_window = window;
settings.title = item->GetURL().spec();
settings.default_path = default_path;
auto* web_preferences = WebContentsPreferences::From(web_contents);
const bool offscreen =
!web_preferences || web_preferences->IsEnabled(options::kOffscreen);
settings.force_detached = offscreen;
auto dialog_callback =
base::Bind(&AtomDownloadManagerDelegate::OnDownloadSaveDialogDone,
base::Unretained(this), download_id, callback);
file_dialog::ShowSaveDialog(settings, dialog_callback);
} else {
callback.Run(path, download::DownloadItem::TARGET_DISPOSITION_PROMPT,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, path,
download::DOWNLOAD_INTERRUPT_REASON_NONE);
}
}
#if defined(MAS_BUILD)
void AtomDownloadManagerDelegate::OnDownloadSaveDialogDone(
uint32_t download_id,
const content::DownloadTargetCallback& download_callback,
bool result,
const base::FilePath& path,
const std::string& bookmark)
#else
void AtomDownloadManagerDelegate::OnDownloadSaveDialogDone(
uint32_t download_id,
const content::DownloadTargetCallback& download_callback,
bool result,
const base::FilePath& path)
#endif
{
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
auto* item = download_manager_->GetDownload(download_id);
if (!item)
return;
if (result) {
// Remember the last selected download directory.
AtomBrowserContext* browser_context = static_cast<AtomBrowserContext*>(
download_manager_->GetBrowserContext());
@@ -117,12 +151,16 @@ void AtomDownloadManagerDelegate::OnDownloadPathGenerated(
}
// Running the DownloadTargetCallback with an empty FilePath signals that the
// download should be cancelled.
// If user cancels the file save dialog, run the callback with empty FilePath.
callback.Run(path, download::DownloadItem::TARGET_DISPOSITION_PROMPT,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, path,
path.empty() ? download::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED
: download::DOWNLOAD_INTERRUPT_REASON_NONE);
// download should be cancelled. If user cancels the file save dialog, run
// the callback with empty FilePath.
const base::FilePath download_path = result ? path : base::FilePath();
const auto interrupt_reason =
download_path.empty() ? download::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED
: download::DOWNLOAD_INTERRUPT_REASON_NONE;
download_callback.Run(download_path,
download::DownloadItem::TARGET_DISPOSITION_PROMPT,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
download_path, interrupt_reason);
}
void AtomDownloadManagerDelegate::Shutdown() {

View File

@@ -24,10 +24,6 @@ class AtomDownloadManagerDelegate : public content::DownloadManagerDelegate {
explicit AtomDownloadManagerDelegate(content::DownloadManager* manager);
~AtomDownloadManagerDelegate() override;
void OnDownloadPathGenerated(uint32_t download_id,
const content::DownloadTargetCallback& callback,
const base::FilePath& default_path);
// content::DownloadManagerDelegate:
void Shutdown() override;
bool DetermineDownloadTarget(
@@ -42,6 +38,25 @@ class AtomDownloadManagerDelegate : public content::DownloadManagerDelegate {
// Get the save path set on the associated api::DownloadItem object
void GetItemSavePath(download::DownloadItem* item, base::FilePath* path);
void OnDownloadPathGenerated(uint32_t download_id,
const content::DownloadTargetCallback& callback,
const base::FilePath& default_path);
#if defined(MAS_BUILD)
void OnDownloadSaveDialogDone(
uint32_t download_id,
const content::DownloadTargetCallback& download_callback,
bool result,
const base::FilePath& path,
const std::string& bookmark);
#else
void OnDownloadSaveDialogDone(
uint32_t download_id,
const content::DownloadTargetCallback& download_callback,
bool result,
const base::FilePath& path);
#endif
content::DownloadManager* download_manager_;
base::WeakPtrFactory<AtomDownloadManagerDelegate> weak_ptr_factory_;

View File

@@ -48,6 +48,8 @@ network::mojom::HttpAuthDynamicParamsPtr CreateHttpAuthDynamicParams(
command_line.GetSwitchValueASCII(switches::kAuthServerWhitelist);
auth_dynamic_params->delegate_whitelist = command_line.GetSwitchValueASCII(
switches::kAuthNegotiateDelegateWhitelist);
auth_dynamic_params->enable_negotiate_port =
command_line.HasSwitch(atom::switches::kEnableAuthNegotiatePort);
return auth_dynamic_params;
}

View File

@@ -585,7 +585,12 @@ void NativeWindowMac::Hide() {
}
bool NativeWindowMac::IsVisible() {
return [window_ isVisible];
bool occluded = [window_ occlusionState] == NSWindowOcclusionStateVisible;
// For a window to be visible, it must be visible to the user in the
// foreground of the app, which means that it should not be minimized or
// occluded
return [window_ isVisible] && !occluded && !IsMinimized();
}
bool NativeWindowMac::IsEnabled() {
@@ -1118,17 +1123,23 @@ void NativeWindowMac::SetProgressBar(double progress,
const NativeWindow::ProgressState state) {
NSDockTile* dock_tile = [NSApp dockTile];
// Sometimes macOS would install a default contentView for dock, we must
// verify whether NSProgressIndicator has been installed.
bool first_time = !dock_tile.contentView ||
[[dock_tile.contentView subviews] count] == 0 ||
![[[dock_tile.contentView subviews] lastObject]
isKindOfClass:[NSProgressIndicator class]];
// For the first time API invoked, we need to create a ContentView in
// DockTile.
if (dock_tile.contentView == nullptr) {
NSImageView* image_view = [[NSImageView alloc] init];
if (first_time) {
NSImageView* image_view = [[[NSImageView alloc] init] autorelease];
[image_view setImage:[NSApp applicationIconImage]];
[dock_tile setContentView:image_view];
}
if ([[dock_tile.contentView subviews] count] == 0) {
NSProgressIndicator* progress_indicator = [[AtomProgressBar alloc]
initWithFrame:NSMakeRect(0.0f, 0.0f, dock_tile.size.width, 15.0)];
NSRect frame = NSMakeRect(0.0f, 0.0f, dock_tile.size.width, 15.0);
NSProgressIndicator* progress_indicator =
[[[AtomProgressBar alloc] initWithFrame:frame] autorelease];
[progress_indicator setStyle:NSProgressIndicatorBarStyle];
[progress_indicator setIndeterminate:NO];
[progress_indicator setBezeled:YES];
@@ -1139,7 +1150,7 @@ void NativeWindowMac::SetProgressBar(double progress,
}
NSProgressIndicator* progress_indicator = static_cast<NSProgressIndicator*>(
[[[dock_tile contentView] subviews] objectAtIndex:0]);
[[[dock_tile contentView] subviews] lastObject]);
if (progress < 0) {
[progress_indicator setHidden:YES];
} else if (progress > 1) {

View File

@@ -349,6 +349,9 @@ void NativeWindowViews::Show() {
widget()->native_widget_private()->ShowWithWindowState(GetRestoredState());
// explicitly focus the window
widget()->Activate();
NotifyWindowShow();
#if defined(USE_X11)

View File

@@ -13,14 +13,16 @@
#include "atom/common/api/event_emitter_caller.h"
#include "atom/common/atom_constants.h"
#include "atom/common/native_mate_converters/net_converter.h"
#include "atom/common/node_includes.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "native_mate/dictionary.h"
#include "net/base/net_errors.h"
#include "net/filter/gzip_source_stream.h"
#include "atom/common/node_includes.h"
namespace atom {
namespace {
@@ -82,14 +84,14 @@ void BeforeStartInUI(base::WeakPtr<URLRequestStreamJob> job,
return;
}
auto subscriber = std::make_unique<mate::StreamSubscriber>(
args->isolate(), data.GetHandle(), job);
auto subscriber = base::MakeRefCounted<mate::StreamSubscriber>(
args->isolate(), data.GetHandle(), job,
base::ThreadTaskRunnerHandle::Get());
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::BindOnce(&URLRequestStreamJob::StartAsync, job,
std::move(subscriber), base::RetainedRef(response_headers),
ended, error));
base::BindOnce(&URLRequestStreamJob::StartAsync, job, subscriber,
base::RetainedRef(response_headers), ended, error));
}
} // namespace
@@ -104,10 +106,7 @@ URLRequestStreamJob::URLRequestStreamJob(net::URLRequest* request,
weak_factory_(this) {}
URLRequestStreamJob::~URLRequestStreamJob() {
if (subscriber_) {
content::BrowserThread::DeleteSoon(content::BrowserThread::UI, FROM_HERE,
std::move(subscriber_));
}
DCHECK(!subscriber_ || subscriber_->HasOneRef());
}
void URLRequestStreamJob::Start() {
@@ -121,7 +120,7 @@ void URLRequestStreamJob::Start() {
}
void URLRequestStreamJob::StartAsync(
std::unique_ptr<mate::StreamSubscriber> subscriber,
scoped_refptr<mate::StreamSubscriber> subscriber,
scoped_refptr<net::HttpResponseHeaders> response_headers,
bool ended,
int error) {
@@ -133,12 +132,13 @@ void URLRequestStreamJob::StartAsync(
ended_ = ended;
response_headers_ = response_headers;
subscriber_ = std::move(subscriber);
subscriber_ = subscriber;
request_start_time_ = base::TimeTicks::Now();
NotifyHeadersComplete();
}
void URLRequestStreamJob::OnData(std::vector<char>&& buffer) { // NOLINT
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (write_buffer_.empty()) {
// Quick branch without copying.
write_buffer_ = std::move(buffer);
@@ -173,7 +173,7 @@ void URLRequestStreamJob::OnError(int error) {
int URLRequestStreamJob::ReadRawData(net::IOBuffer* dest, int dest_size) {
response_start_time_ = base::TimeTicks::Now();
if (ended_)
if (ended_ && write_buffer_.empty())
return 0;
// When write_buffer_ is empty, there is no data valable yet, we have to save
@@ -191,12 +191,13 @@ int URLRequestStreamJob::ReadRawData(net::IOBuffer* dest, int dest_size) {
}
void URLRequestStreamJob::DoneReading() {
content::BrowserThread::DeleteSoon(content::BrowserThread::UI, FROM_HERE,
std::move(subscriber_));
write_buffer_.clear();
}
void URLRequestStreamJob::DoneReadingRedirectResponse() {
if (subscriber_) {
subscriber_ = nullptr;
}
DoneReading();
}

View File

@@ -24,7 +24,7 @@ class URLRequestStreamJob : public JsAsker, public net::URLRequestJob {
net::NetworkDelegate* network_delegate);
~URLRequestStreamJob() override;
void StartAsync(std::unique_ptr<mate::StreamSubscriber> subscriber,
void StartAsync(scoped_refptr<mate::StreamSubscriber> subscriber,
scoped_refptr<net::HttpResponseHeaders> response_headers,
bool ended,
int error);
@@ -62,7 +62,7 @@ class URLRequestStreamJob : public JsAsker, public net::URLRequestJob {
base::TimeTicks request_start_time_;
base::TimeTicks response_start_time_;
scoped_refptr<net::HttpResponseHeaders> response_headers_;
std::unique_ptr<mate::StreamSubscriber> subscriber_;
scoped_refptr<mate::StreamSubscriber> subscriber_;
base::WeakPtrFactory<URLRequestStreamJob> weak_factory_;

View File

@@ -17,9 +17,9 @@
<key>CFBundleIconFile</key>
<string>electron.icns</string>
<key>CFBundleVersion</key>
<string>4.0.2</string>
<string>4.2.2</string>
<key>CFBundleShortVersionString</key>
<string>4.0.2</string>
<string>4.2.2</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.developer-tools</string>
<key>LSMinimumSystemVersion</key>

View File

@@ -50,8 +50,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 4,0,2,0
PRODUCTVERSION 4,0,2,0
FILEVERSION 4,2,2,0
PRODUCTVERSION 4,2,2,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -68,12 +68,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "GitHub, Inc."
VALUE "FileDescription", "Electron"
VALUE "FileVersion", "4.0.2"
VALUE "FileVersion", "4.2.2"
VALUE "InternalName", "electron.exe"
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
VALUE "OriginalFilename", "electron.exe"
VALUE "ProductName", "Electron"
VALUE "ProductVersion", "4.0.2"
VALUE "ProductVersion", "4.2.2"
VALUE "SquirrelAwareVersion", "1"
END
END

View File

@@ -15,6 +15,7 @@
#include "ui/base/l10n/l10n_util_mac.h"
#include "ui/events/cocoa/cocoa_event_utils.h"
#include "ui/gfx/image/image.h"
#include "ui/strings/grit/ui_strings.h"
using content::BrowserThread;
@@ -56,6 +57,29 @@ Role kRolesMap[] = {
{@selector(clearRecentDocuments:), "clearrecentdocuments"},
};
// Called when adding a submenu to the menu and checks if the submenu, via its
// |model|, has visible child items.
bool MenuHasVisibleItems(const atom::AtomMenuModel* model) {
int count = model->GetItemCount();
for (int index = 0; index < count; index++) {
if (model->IsVisibleAt(index))
return true;
}
return false;
}
// Called when an empty submenu is created. This inserts a menu item labeled
// "(empty)" into the submenu. Matches Windows behavior.
NSMenu* MakeEmptySubmenu() {
base::scoped_nsobject<NSMenu> submenu([[NSMenu alloc] initWithTitle:@""]);
NSString* empty_menu_title =
l10n_util::GetNSString(IDS_APP_MENU_EMPTY_SUBMENU);
[submenu addItemWithTitle:empty_menu_title action:NULL keyEquivalent:@""];
[[submenu itemAtIndex:0] setEnabled:NO];
return submenu.autorelease();
}
} // namespace
// Menu item is located for ease of removing it from the parent owner
@@ -218,13 +242,21 @@ static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_;
NSMenu* submenu = [[NSMenu alloc] initWithTitle:label];
[item setSubmenu:submenu];
[NSApp setServicesMenu:submenu];
} else if (type == atom::AtomMenuModel::TYPE_SUBMENU) {
} else if (type == atom::AtomMenuModel::TYPE_SUBMENU &&
model->IsVisibleAt(index)) {
// We need to specifically check that the submenu top-level item has been
// enabled as it's not validated by validateUserInterfaceItem
if (!model->IsEnabledAt(index))
[item setEnabled:NO];
// Recursively build a submenu from the sub-model at this index.
[item setTarget:nil];
[item setAction:nil];
atom::AtomMenuModel* submenuModel =
static_cast<atom::AtomMenuModel*>(model->GetSubmenuModelAt(index));
NSMenu* submenu = [self menuFromModel:submenuModel];
NSMenu* submenu = MenuHasVisibleItems(submenuModel)
? [self menuFromModel:submenuModel]
: MakeEmptySubmenu();
[submenu setTitle:[item title]];
[item setSubmenu:submenu];

View File

@@ -623,9 +623,14 @@ static NSString* const ImageScrubberItemIdentifier = @"scrubber.image.item";
segments[i].Get("enabled", &enabled);
if (segments[i].Get("label", &label)) {
[control setLabel:base::SysUTF8ToNSString(label) forSegment:i];
} else if (segments[i].Get("icon", &image)) {
} else {
[control setLabel:@"" forSegment:i];
}
if (segments[i].Get("icon", &image)) {
[control setImage:image.AsNSImage() forSegment:i];
[control setImageScaling:NSImageScaleProportionallyUpOrDown forSegment:i];
} else {
[control setImage:nil forSegment:i];
}
[control setEnabled:enabled forSegment:i];
}

View File

@@ -284,7 +284,7 @@ bool ShowOpenDialog(const DialogSettings& settings,
void OpenDialogCompletion(int chosen,
NSOpenPanel* dialog,
const DialogSettings& settings,
bool security_scoped_bookmarks,
const OpenDialogCallback& callback) {
if (chosen == NSFileHandlingPanelCancelButton) {
#if defined(MAS_BUILD)
@@ -297,7 +297,7 @@ void OpenDialogCompletion(int chosen,
std::vector<base::FilePath> paths;
#if defined(MAS_BUILD)
std::vector<std::string> bookmarks;
if (settings.security_scoped_bookmarks) {
if (security_scoped_bookmarks) {
ReadDialogPathsWithBookmarks(dialog, &paths, &bookmarks);
} else {
ReadDialogPaths(dialog, &paths);
@@ -320,17 +320,21 @@ void ShowOpenDialog(const DialogSettings& settings,
// 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;
// Capture the value of the security_scoped_bookmarks settings flag
// and pass it to the completion handler.
bool security_scoped_bookmarks = settings.security_scoped_bookmarks;
if (!settings.parent_window || !settings.parent_window->GetNativeWindow() ||
settings.force_detached) {
[dialog beginWithCompletionHandler:^(NSInteger chosen) {
OpenDialogCompletion(chosen, dialog, settings, callback);
OpenDialogCompletion(chosen, dialog, security_scoped_bookmarks, callback);
}];
} else {
NSWindow* window = settings.parent_window->GetNativeWindow();
[dialog beginSheetModalForWindow:window
completionHandler:^(NSInteger chosen) {
OpenDialogCompletion(chosen, dialog, settings, callback);
OpenDialogCompletion(chosen, dialog,
security_scoped_bookmarks, callback);
}];
}
}
@@ -351,7 +355,7 @@ bool ShowSaveDialog(const DialogSettings& settings, base::FilePath* path) {
void SaveDialogCompletion(int chosen,
NSSavePanel* dialog,
const DialogSettings& settings,
bool security_scoped_bookmarks,
const SaveDialogCallback& callback) {
if (chosen == NSFileHandlingPanelCancelButton) {
#if defined(MAS_BUILD)
@@ -363,7 +367,7 @@ void SaveDialogCompletion(int chosen,
std::string path = base::SysNSStringToUTF8([[dialog URL] path]);
#if defined(MAS_BUILD)
std::string bookmark;
if (settings.security_scoped_bookmarks) {
if (security_scoped_bookmarks) {
bookmark = GetBookmarkDataFromNSURL([dialog URL]);
}
callback.Run(true, base::FilePath(path), bookmark);
@@ -381,17 +385,19 @@ void ShowSaveDialog(const DialogSettings& settings,
[dialog setCanSelectHiddenExtension:YES];
__block SaveDialogCallback callback = c;
bool security_scoped_bookmarks = settings.security_scoped_bookmarks;
if (!settings.parent_window || !settings.parent_window->GetNativeWindow() ||
settings.force_detached) {
[dialog beginWithCompletionHandler:^(NSInteger chosen) {
SaveDialogCompletion(chosen, dialog, settings, callback);
SaveDialogCompletion(chosen, dialog, security_scoped_bookmarks, callback);
}];
} else {
NSWindow* window = settings.parent_window->GetNativeWindow();
[dialog beginSheetModalForWindow:window
completionHandler:^(NSInteger chosen) {
SaveDialogCompletion(chosen, dialog, settings, callback);
SaveDialogCompletion(chosen, dialog,
security_scoped_bookmarks, callback);
}];
}
}

View File

@@ -20,6 +20,7 @@
#include "cc/base/switches.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/web_preferences.h"
#include "native_mate/dictionary.h"
@@ -229,6 +230,9 @@ WebContentsPreferences* WebContentsPreferences::From(
void WebContentsPreferences::AppendCommandLineSwitches(
base::CommandLine* command_line) {
// Append UA Override
command_line->AppendSwitchASCII("user-agent",
content::GetContentClient()->GetUserAgent());
// Check if plugins are enabled.
if (IsEnabled(options::kPlugins))
command_line->AppendSwitch(switches::kEnablePlugins);

View File

@@ -81,49 +81,71 @@ float GetScaleFactorFromOptions(mate::Arguments* args) {
return scale_factor;
}
bool AddImageSkiaRep(gfx::ImageSkia* image,
const unsigned char* data,
size_t size,
int width,
int height,
double scale_factor) {
auto decoded = std::make_unique<SkBitmap>();
bool AddImageSkiaRepFromPNG(gfx::ImageSkia* image,
const unsigned char* data,
size_t size,
double scale_factor) {
SkBitmap bitmap;
if (!gfx::PNGCodec::Decode(data, size, &bitmap))
return false;
// Try PNG first.
if (!gfx::PNGCodec::Decode(data, size, decoded.get())) {
// Try JPEG.
decoded = gfx::JPEGCodec::Decode(data, size);
if (decoded) {
// `JPEGCodec::Decode()` doesn't tell `SkBitmap` instance it creates
// that all of its pixels are opaque, that's why the bitmap gets
// an alpha type `kPremul_SkAlphaType` instead of `kOpaque_SkAlphaType`.
// Let's fix it here.
// TODO(alexeykuzmin): This workaround should be removed
// when the `JPEGCodec::Decode()` code is fixed.
// See https://github.com/electron/electron/issues/11294.
decoded->setAlphaType(SkAlphaType::kOpaque_SkAlphaType);
}
}
if (!decoded) {
// Try Bitmap
if (width > 0 && height > 0) {
decoded.reset(new SkBitmap);
decoded->allocN32Pixels(width, height, false);
decoded->setPixels(
const_cast<void*>(reinterpret_cast<const void*>(data)));
} else {
return false;
}
}
image->AddRepresentation(gfx::ImageSkiaRep(*decoded, scale_factor));
image->AddRepresentation(gfx::ImageSkiaRep(bitmap, scale_factor));
return true;
}
bool AddImageSkiaRep(gfx::ImageSkia* image,
const base::FilePath& path,
double scale_factor) {
bool AddImageSkiaRepFromJPEG(gfx::ImageSkia* image,
const unsigned char* data,
size_t size,
double scale_factor) {
auto bitmap = gfx::JPEGCodec::Decode(data, size);
if (!bitmap)
return false;
// `JPEGCodec::Decode()` doesn't tell `SkBitmap` instance it creates
// that all of its pixels are opaque, that's why the bitmap gets
// an alpha type `kPremul_SkAlphaType` instead of `kOpaque_SkAlphaType`.
// Let's fix it here.
// TODO(alexeykuzmin): This workaround should be removed
// when the `JPEGCodec::Decode()` code is fixed.
// See https://github.com/electron/electron/issues/11294.
bitmap->setAlphaType(SkAlphaType::kOpaque_SkAlphaType);
image->AddRepresentation(gfx::ImageSkiaRep(*bitmap, scale_factor));
return true;
}
bool AddImageSkiaRepFromBuffer(gfx::ImageSkia* image,
const unsigned char* data,
size_t size,
int width,
int height,
double scale_factor) {
// Try PNG first.
if (AddImageSkiaRepFromPNG(image, data, size, scale_factor))
return true;
// Try JPEG second.
if (AddImageSkiaRepFromJPEG(image, data, size, scale_factor))
return true;
if (width == 0 || height == 0)
return false;
auto info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType);
if (size < info.computeMinByteSize())
return false;
SkBitmap bitmap;
bitmap.allocN32Pixels(width, height, false);
bitmap.writePixels({info, data, bitmap.rowBytes()});
image->AddRepresentation(gfx::ImageSkiaRep(bitmap, scale_factor));
return true;
}
bool AddImageSkiaRepFromPath(gfx::ImageSkia* image,
const base::FilePath& path,
double scale_factor) {
std::string file_contents;
{
base::ThreadRestrictions::ScopedAllowIO allow_io;
@@ -134,7 +156,8 @@ bool AddImageSkiaRep(gfx::ImageSkia* image,
const unsigned char* data =
reinterpret_cast<const unsigned char*>(file_contents.data());
size_t size = file_contents.size();
return AddImageSkiaRep(image, data, size, 0, 0, scale_factor);
return AddImageSkiaRepFromBuffer(image, data, size, 0, 0, scale_factor);
}
bool PopulateImageSkiaRepsFromPath(gfx::ImageSkia* image,
@@ -143,12 +166,12 @@ bool PopulateImageSkiaRepsFromPath(gfx::ImageSkia* image,
std::string filename(path.BaseName().RemoveExtension().AsUTF8Unsafe());
if (base::MatchPattern(filename, "*@*x"))
// Don't search for other representations if the DPI has been specified.
return AddImageSkiaRep(image, path, GetScaleFactorFromPath(path));
return AddImageSkiaRepFromPath(image, path, GetScaleFactorFromPath(path));
else
succeed |= AddImageSkiaRep(image, path, 1.0f);
succeed |= AddImageSkiaRepFromPath(image, path, 1.0f);
for (const ScaleFactorPair& pair : kScaleFactorPairs)
succeed |= AddImageSkiaRep(
succeed |= AddImageSkiaRepFromPath(
image, path.InsertBeforeExtensionASCII(pair.name), pair.scale);
return succeed;
}
@@ -423,19 +446,20 @@ void NativeImage::AddRepresentation(const mate::Dictionary& options) {
v8::Local<v8::Value> buffer;
GURL url;
if (options.Get("buffer", &buffer) && node::Buffer::HasInstance(buffer)) {
AddImageSkiaRep(
&image_skia,
reinterpret_cast<unsigned char*>(node::Buffer::Data(buffer)),
node::Buffer::Length(buffer), width, height, scale_factor);
skia_rep_added = true;
auto* data = reinterpret_cast<unsigned char*>(node::Buffer::Data(buffer));
auto size = node::Buffer::Length(buffer);
skia_rep_added = AddImageSkiaRepFromBuffer(&image_skia, data, size, width,
height, scale_factor);
} else if (options.Get("dataURL", &url)) {
std::string mime_type, charset, data;
if (net::DataURL::Parse(url, &mime_type, &charset, &data)) {
if (mime_type == "image/png" || mime_type == "image/jpeg") {
AddImageSkiaRep(&image_skia,
reinterpret_cast<const unsigned char*>(data.c_str()),
data.size(), width, height, scale_factor);
skia_rep_added = true;
auto* data_ptr = reinterpret_cast<const unsigned char*>(data.c_str());
if (mime_type == "image/png") {
skia_rep_added = AddImageSkiaRepFromPNG(&image_skia, data_ptr,
data.size(), scale_factor);
} else if (mime_type == "image/jpeg") {
skia_rep_added = AddImageSkiaRepFromJPEG(&image_skia, data_ptr,
data.size(), scale_factor);
}
}
}
@@ -509,6 +533,11 @@ mate::Handle<NativeImage> NativeImage::CreateFromPath(
mate::Handle<NativeImage> NativeImage::CreateFromBuffer(
mate::Arguments* args,
v8::Local<v8::Value> buffer) {
if (!node::Buffer::HasInstance(buffer)) {
args->ThrowError("buffer must be a node Buffer");
return mate::Handle<NativeImage>();
}
int width = 0;
int height = 0;
double scale_factor = 1.;
@@ -521,9 +550,9 @@ mate::Handle<NativeImage> NativeImage::CreateFromBuffer(
}
gfx::ImageSkia image_skia;
AddImageSkiaRep(&image_skia,
reinterpret_cast<unsigned char*>(node::Buffer::Data(buffer)),
node::Buffer::Length(buffer), width, height, scale_factor);
AddImageSkiaRepFromBuffer(
&image_skia, reinterpret_cast<unsigned char*>(node::Buffer::Data(buffer)),
node::Buffer::Length(buffer), width, height, scale_factor);
return Create(args->isolate(), gfx::Image(image_skia));
}

View File

@@ -7,21 +7,31 @@
#include <algorithm>
#include <iostream>
#include <string>
#include <utility>
#include <vector>
#include "atom/browser/browser.h"
#include "atom/common/api/locker.h"
#include "atom/common/atom_version.h"
#include "atom/common/chrome_version.h"
#include "atom/common/heap_snapshot.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/native_mate_converters/string16_converter.h"
#include "atom/common/node_includes.h"
#include "atom/common/promise_util.h"
#include "base/logging.h"
#include "base/process/process_handle.h"
#include "base/process/process_info.h"
#include "base/process/process_metrics_iocounters.h"
#include "base/sys_info.h"
#include "base/threading/thread_restrictions.h"
#include "brightray/common/application_info.h"
#include "native_mate/dictionary.h"
#include "services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h"
#include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
// Must be the last in the includes list, otherwise the definition of chromium
// macros conflicts with node macros.
#include "atom/common/node_includes.h"
namespace atom {
@@ -61,6 +71,7 @@ void AtomBindings::BindTo(v8::Isolate* isolate, v8::Local<v8::Object> process) {
dict.SetMethod("getHeapStatistics", &GetHeapStatistics);
dict.SetMethod("getCreationTime", &GetCreationTime);
dict.SetMethod("getSystemMemoryInfo", &GetSystemMemoryInfo);
dict.SetMethod("getProcessMemoryInfo", &GetProcessMemoryInfo);
dict.SetMethod("getCPUUsage", base::Bind(&AtomBindings::GetCPUUsage,
base::Unretained(metrics_.get())));
dict.SetMethod("getIOCounters", &GetIOCounters);
@@ -209,6 +220,67 @@ v8::Local<v8::Value> AtomBindings::GetSystemMemoryInfo(v8::Isolate* isolate,
return dict.GetHandle();
}
// static
v8::Local<v8::Promise> AtomBindings::GetProcessMemoryInfo(
v8::Isolate* isolate) {
scoped_refptr<util::Promise> promise = new util::Promise(isolate);
if (mate::Locker::IsBrowserProcess() && !Browser::Get()->is_ready()) {
promise->RejectWithErrorMessage(
"Memory Info is available only after app ready");
return promise->GetHandle();
}
v8::Global<v8::Context> context(isolate, isolate->GetCurrentContext());
memory_instrumentation::MemoryInstrumentation::GetInstance()
->RequestGlobalDumpForPid(base::GetCurrentProcId(),
std::vector<std::string>(),
base::Bind(&AtomBindings::DidReceiveMemoryDump,
std::move(context), promise));
return promise->GetHandle();
}
// static
void AtomBindings::DidReceiveMemoryDump(
const v8::Global<v8::Context>& context,
scoped_refptr<util::Promise> promise,
bool success,
std::unique_ptr<memory_instrumentation::GlobalMemoryDump> global_dump) {
v8::Isolate* isolate = promise->isolate();
mate::Locker locker(isolate);
v8::HandleScope handle_scope(isolate);
v8::MicrotasksScope script_scope(isolate,
v8::MicrotasksScope::kRunMicrotasks);
v8::Context::Scope context_scope(
v8::Local<v8::Context>::New(isolate, context));
if (!success) {
promise->RejectWithErrorMessage("Failed to create memory dump");
return;
}
bool resolved = false;
for (const memory_instrumentation::GlobalMemoryDump::ProcessDump& dump :
global_dump->process_dumps()) {
if (base::GetCurrentProcId() == dump.pid()) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
const auto& osdump = dump.os_dump();
#if defined(OS_LINUX) || defined(OS_WIN)
dict.Set("residentSet", osdump.resident_set_kb);
#endif
dict.Set("private", osdump.private_footprint_kb);
dict.Set("shared", osdump.shared_footprint_kb);
promise->Resolve(dict.GetHandle());
resolved = true;
break;
}
}
if (!resolved) {
promise->RejectWithErrorMessage(
R"(Failed to find current process memory details in memory dump)");
}
}
// static
v8::Local<v8::Value> AtomBindings::GetCPUUsage(base::ProcessMetrics* metrics,
v8::Isolate* isolate) {

View File

@@ -10,18 +10,27 @@
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/process/process_metrics.h"
#include "base/strings/string16.h"
#include "native_mate/arguments.h"
#include "uv.h" // NOLINT(build/include)
#include "v8/include/v8.h"
namespace memory_instrumentation {
class GlobalMemoryDump;
}
namespace node {
class Environment;
}
namespace atom {
namespace util {
class Promise;
}
class AtomBindings {
public:
explicit AtomBindings(uv_loop_t* loop);
@@ -41,6 +50,7 @@ class AtomBindings {
static v8::Local<v8::Value> GetCreationTime(v8::Isolate* isolate);
static v8::Local<v8::Value> GetSystemMemoryInfo(v8::Isolate* isolate,
mate::Arguments* args);
static v8::Local<v8::Promise> GetProcessMemoryInfo(v8::Isolate* isolate);
static v8::Local<v8::Value> GetCPUUsage(base::ProcessMetrics* metrics,
v8::Isolate* isolate);
static v8::Local<v8::Value> GetIOCounters(v8::Isolate* isolate);
@@ -52,6 +62,12 @@ class AtomBindings {
static void OnCallNextTick(uv_async_t* handle);
static void DidReceiveMemoryDump(
const v8::Global<v8::Context>& context,
scoped_refptr<util::Promise> promise,
bool success,
std::unique_ptr<memory_instrumentation::GlobalMemoryDump> dump);
uv_async_t call_next_tick_async_;
std::list<node::Environment*> pending_next_ticks_;
std::unique_ptr<base::ProcessMetrics> metrics_;

View File

@@ -39,6 +39,10 @@ bool IsPrintingEnabled() {
return BUILDFLAG(ENABLE_PRINTING);
}
bool IsTtsEnabled() {
return BUILDFLAG(ENABLE_TTS);
}
void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
@@ -52,6 +56,7 @@ void Initialize(v8::Local<v8::Object> exports,
&IsFakeLocationProviderEnabled);
dict.SetMethod("isViewApiEnabled", &IsViewApiEnabled);
dict.SetMethod("isPrintingEnabled", &IsPrintingEnabled);
dict.SetMethod("isTtsEnabled", &IsTtsEnabled);
}
} // namespace

View File

@@ -6,7 +6,7 @@
#define ATOM_COMMON_ATOM_VERSION_H_
#define ATOM_MAJOR_VERSION 4
#define ATOM_MINOR_VERSION 0
#define ATOM_MINOR_VERSION 2
#define ATOM_PATCH_VERSION 2
// clang-format off
// #define ATOM_PRE_RELEASE_VERSION

View File

@@ -8,7 +8,7 @@
#ifndef ATOM_COMMON_CHROME_VERSION_H_
#define ATOM_COMMON_CHROME_VERSION_H_
#define CHROME_VERSION_STRING "69.0.3497.106"
#define CHROME_VERSION_STRING "69.0.3497.128"
#define CHROME_VERSION "v" CHROME_VERSION_STRING
#endif // ATOM_COMMON_CHROME_VERSION_H_

View File

@@ -93,8 +93,13 @@ bool RegisterNonABICompliantCodeRange(void* start, size_t size_in_bytes) {
// All addresses are 32bit relative offsets to start.
record->runtime_function.BeginAddress = 0;
#if defined(_M_ARM64)
record->runtime_function.FunctionLength =
base::checked_cast<DWORD>(size_in_bytes);
#else
record->runtime_function.EndAddress =
base::checked_cast<DWORD>(size_in_bytes);
#endif
record->runtime_function.UnwindData =
offsetof(ExceptionHandlerRecord, unwind_info);

View File

@@ -181,24 +181,45 @@ bool Converter<net::HttpResponseHeaders*>::FromV8(
if (!val->IsObject()) {
return false;
}
auto addHeaderFromValue = [&isolate, &out](
const std::string& key,
const v8::Local<v8::Value>& localVal) {
auto context = isolate->GetCurrentContext();
v8::Local<v8::String> localStrVal;
if (!localVal->ToString(context).ToLocal(&localStrVal)) {
return false;
}
std::string value;
mate::ConvertFromV8(isolate, localStrVal, &value);
out->AddHeader(key + ": " + value);
return true;
};
auto context = isolate->GetCurrentContext();
auto headers = v8::Local<v8::Object>::Cast(val);
auto keys = headers->GetOwnPropertyNames();
for (uint32_t i = 0; i < keys->Length(); i++) {
v8::Local<v8::String> key, value;
if (!keys->Get(i)->ToString(context).ToLocal(&key)) {
v8::Local<v8::String> keyVal;
if (!keys->Get(i)->ToString(context).ToLocal(&keyVal)) {
return false;
}
if (!headers->Get(key)->ToString(context).ToLocal(&value)) {
return false;
std::string key;
mate::ConvertFromV8(isolate, keyVal, &key);
auto localVal = headers->Get(keyVal);
if (localVal->IsArray()) {
auto values = v8::Local<v8::Array>::Cast(localVal);
for (uint32_t j = 0; j < values->Length(); j++) {
if (!addHeaderFromValue(key, values->Get(j))) {
return false;
}
}
} else {
if (!addHeaderFromValue(key, localVal)) {
return false;
}
}
v8::String::Utf8Value key_utf8(key);
v8::String::Utf8Value value_utf8(value);
std::string k(*key_utf8, key_utf8.length());
std::string v(*value_utf8, value_utf8.length());
std::ostringstream tmp;
tmp << k << ": " << v;
out->AddHeader(tmp.str());
}
return true;
}

View File

@@ -247,6 +247,9 @@ const char kAuthServerWhitelist[] = "auth-server-whitelist";
const char kAuthNegotiateDelegateWhitelist[] =
"auth-negotiate-delegate-whitelist";
// If set, include the port in generated Kerberos SPNs.
const char kEnableAuthNegotiatePort[] = "enable-auth-negotiate-port";
} // namespace switches
} // namespace atom

View File

@@ -118,6 +118,7 @@ extern const char kDiskCacheSize[];
extern const char kIgnoreConnectionsLimit[];
extern const char kAuthServerWhitelist[];
extern const char kAuthNegotiateDelegateWhitelist[];
extern const char kEnableAuthNegotiatePort[];
} // namespace switches

View File

@@ -2,7 +2,10 @@
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/renderer/api/atom_api_web_frame.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "atom/common/api/api_messages.h"
#include "atom/common/api/event_emitter_caller.h"
@@ -109,15 +112,15 @@ class ScriptExecutionCallback : public blink::WebScriptExecutionCallback {
DISALLOW_COPY_AND_ASSIGN(ScriptExecutionCallback);
};
class FrameSpellChecker : public content::RenderFrameVisitor {
class FrameSetSpellChecker : public content::RenderFrameVisitor {
public:
explicit FrameSpellChecker(SpellCheckClient* spell_check_client,
content::RenderFrame* main_frame)
: spell_check_client_(spell_check_client), main_frame_(main_frame) {}
~FrameSpellChecker() override {
spell_check_client_ = nullptr;
main_frame_ = nullptr;
FrameSetSpellChecker(SpellCheckClient* spell_check_client,
content::RenderFrame* main_frame)
: spell_check_client_(spell_check_client), main_frame_(main_frame) {
content::RenderFrame::ForEach(this);
main_frame->GetWebFrame()->SetSpellCheckPanelHostClient(spell_check_client);
}
bool Visit(content::RenderFrame* render_frame) override {
auto* view = render_frame->GetRenderView();
if (view->GetMainRenderFrame() == main_frame_ ||
@@ -130,73 +133,130 @@ class FrameSpellChecker : public content::RenderFrameVisitor {
private:
SpellCheckClient* spell_check_client_;
content::RenderFrame* main_frame_;
DISALLOW_COPY_AND_ASSIGN(FrameSpellChecker);
DISALLOW_COPY_AND_ASSIGN(FrameSetSpellChecker);
};
class SpellCheckerHolder : public content::RenderFrameObserver {
public:
// Find existing holder for the |render_frame|.
static SpellCheckerHolder* FromRenderFrame(
content::RenderFrame* render_frame) {
for (auto* holder : instances_) {
if (holder->render_frame() == render_frame)
return holder;
}
return nullptr;
}
SpellCheckerHolder(content::RenderFrame* render_frame,
std::unique_ptr<SpellCheckClient> spell_check_client)
: content::RenderFrameObserver(render_frame),
spell_check_client_(std::move(spell_check_client)) {
DCHECK(!FromRenderFrame(render_frame));
instances_.insert(this);
}
~SpellCheckerHolder() final { instances_.erase(this); }
void UnsetAndDestroy() {
FrameSetSpellChecker set_spell_checker(nullptr, render_frame());
delete this;
}
// RenderFrameObserver implementation.
void OnDestruct() final {
// Since we delete this in WillReleaseScriptContext, this method is unlikely
// to be called, but override anyway since I'm not sure if there are some
// corner cases.
//
// Note that while there are two "delete this", it is totally fine as the
// observer unsubscribes automatically in destructor and the other one won't
// be called.
//
// Also note that we should not call UnsetAndDestroy here, as the render
// frame is going to be destroyed.
delete this;
}
void WillReleaseScriptContext(v8::Local<v8::Context> context,
int world_id) final {
// Unset spell checker when the script context is going to be released, as
// the spell check implementation lives there.
UnsetAndDestroy();
}
private:
static std::set<SpellCheckerHolder*> instances_;
std::unique_ptr<SpellCheckClient> spell_check_client_;
};
} // namespace
WebFrame::WebFrame(v8::Isolate* isolate)
: web_frame_(blink::WebLocalFrame::FrameForCurrentContext()) {
Init(isolate);
// static
std::set<SpellCheckerHolder*> SpellCheckerHolder::instances_;
void SetName(v8::Local<v8::Value> window, const std::string& name) {
GetRenderFrame(window)->GetWebFrame()->SetName(
blink::WebString::FromUTF8(name));
}
WebFrame::WebFrame(v8::Isolate* isolate, blink::WebLocalFrame* blink_frame)
: web_frame_(blink_frame) {
Init(isolate);
}
WebFrame::~WebFrame() {}
void WebFrame::SetName(const std::string& name) {
web_frame_->SetName(blink::WebString::FromUTF8(name));
}
double WebFrame::SetZoomLevel(double level) {
double SetZoomLevel(v8::Local<v8::Value> window, double level) {
double result = 0.0;
content::RenderFrame* render_frame =
content::RenderFrame::FromWebFrame(web_frame_);
content::RenderFrame* render_frame = GetRenderFrame(window);
render_frame->Send(new AtomFrameHostMsg_SetTemporaryZoomLevel(
render_frame->GetRoutingID(), level, &result));
return result;
}
double WebFrame::GetZoomLevel() const {
double GetZoomLevel(v8::Local<v8::Value> window) {
double result = 0.0;
content::RenderFrame* render_frame =
content::RenderFrame::FromWebFrame(web_frame_);
content::RenderFrame* render_frame = GetRenderFrame(window);
render_frame->Send(
new AtomFrameHostMsg_GetZoomLevel(render_frame->GetRoutingID(), &result));
return result;
}
double WebFrame::SetZoomFactor(double factor) {
double SetZoomFactor(v8::Local<v8::Value> window, double factor) {
return blink::WebView::ZoomLevelToZoomFactor(
SetZoomLevel(blink::WebView::ZoomFactorToZoomLevel(factor)));
SetZoomLevel(window, blink::WebView::ZoomFactorToZoomLevel(factor)));
}
double WebFrame::GetZoomFactor() const {
return blink::WebView::ZoomLevelToZoomFactor(GetZoomLevel());
double GetZoomFactor(v8::Local<v8::Value> window) {
return blink::WebView::ZoomLevelToZoomFactor(GetZoomLevel(window));
}
void WebFrame::SetVisualZoomLevelLimits(double min_level, double max_level) {
web_frame_->View()->SetDefaultPageScaleLimits(min_level, max_level);
web_frame_->View()->SetIgnoreViewportTagScaleLimits(true);
void SetVisualZoomLevelLimits(v8::Local<v8::Value> window,
double min_level,
double max_level) {
blink::WebFrame* web_frame = GetRenderFrame(window)->GetWebFrame();
web_frame->View()->SetDefaultPageScaleLimits(min_level, max_level);
web_frame->View()->SetIgnoreViewportTagScaleLimits(true);
}
void WebFrame::SetLayoutZoomLevelLimits(double min_level, double max_level) {
web_frame_->View()->ZoomLimitsChanged(min_level, max_level);
void SetLayoutZoomLevelLimits(v8::Local<v8::Value> window,
double min_level,
double max_level) {
blink::WebFrame* web_frame = GetRenderFrame(window)->GetWebFrame();
web_frame->View()->ZoomLimitsChanged(min_level, max_level);
}
v8::Local<v8::Value> WebFrame::RegisterEmbedderCustomElement(
v8::Local<v8::Value> RegisterEmbedderCustomElement(
v8::Local<v8::Value> window,
v8::Local<v8::Object> context,
const base::string16& name,
v8::Local<v8::Object> options) {
v8::Context::Scope context_scope(context->CreationContext());
return web_frame_->GetDocument().RegisterEmbedderCustomElement(
blink::WebString::FromUTF16(name), options);
return GetRenderFrame(context)
->GetWebFrame()
->GetDocument()
.RegisterEmbedderCustomElement(blink::WebString::FromUTF16(name),
options);
}
int WebFrame::GetWebFrameId(v8::Local<v8::Value> content_window) {
int GetWebFrameId(v8::Local<v8::Value> window,
v8::Local<v8::Value> content_window) {
// Get the WebLocalFrame before (possibly) executing any user-space JS while
// getting the |params|. We track the status of the RenderFrame via an
// observer in case it is deleted during user code execution.
@@ -215,34 +275,42 @@ int WebFrame::GetWebFrameId(v8::Local<v8::Value> content_window) {
return render_frame->GetRoutingID();
}
void WebFrame::SetSpellCheckProvider(mate::Arguments* args,
const std::string& language,
bool auto_spell_correct_turned_on,
v8::Local<v8::Object> provider) {
void SetSpellCheckProvider(mate::Arguments* args,
v8::Local<v8::Value> window,
const std::string& language,
bool auto_spell_correct_turned_on,
v8::Local<v8::Object> provider) {
if (!provider->Has(mate::StringToV8(args->isolate(), "spellCheck"))) {
args->ThrowError("\"spellCheck\" has to be defined");
return;
}
auto client = std::make_unique<SpellCheckClient>(
language, auto_spell_correct_turned_on, args->isolate(), provider);
// Remove the old client.
content::RenderFrame* render_frame = GetRenderFrame(window);
auto* existing = SpellCheckerHolder::FromRenderFrame(render_frame);
if (existing)
existing->UnsetAndDestroy();
// Set spellchecker for all live frames in the same process or
// in the sandbox mode for all live sub frames to this WebFrame.
FrameSpellChecker spell_checker(
client.get(), content::RenderFrame::FromWebFrame(web_frame_));
content::RenderFrame::ForEach(&spell_checker);
spell_check_client_.swap(client);
web_frame_->SetSpellCheckPanelHostClient(spell_check_client_.get());
auto spell_check_client = std::make_unique<SpellCheckClient>(
language, auto_spell_correct_turned_on, args->isolate(), provider);
FrameSetSpellChecker spell_checker(spell_check_client.get(), render_frame);
// Attach the spell checker to RenderFrame.
new SpellCheckerHolder(render_frame, std::move(spell_check_client));
}
void WebFrame::RegisterURLSchemeAsBypassingCSP(const std::string& scheme) {
void RegisterURLSchemeAsBypassingCSP(v8::Local<v8::Value> window,
const std::string& scheme) {
// Register scheme to bypass pages's Content Security Policy.
blink::SchemeRegistry::RegisterURLSchemeAsBypassingContentSecurityPolicy(
WTF::String::FromUTF8(scheme.data(), scheme.length()));
}
void WebFrame::RegisterURLSchemeAsPrivileged(const std::string& scheme,
mate::Arguments* args) {
void RegisterURLSchemeAsPrivileged(v8::Local<v8::Value> window,
const std::string& scheme,
mate::Arguments* args) {
// TODO(deepak1556): blink::SchemeRegistry methods should be called
// before any renderer threads are created. Fixing this would break
// current api. Change it with 2.0.
@@ -253,7 +321,7 @@ void WebFrame::RegisterURLSchemeAsPrivileged(const std::string& scheme,
bool allowServiceWorkers = true;
bool supportFetchAPI = true;
bool corsEnabled = true;
if (args->Length() == 2) {
if (args->Length() == 3) {
mate::Dictionary options;
if (args->GetNext(&options)) {
options.Get("secure", &secure);
@@ -283,30 +351,42 @@ void WebFrame::RegisterURLSchemeAsPrivileged(const std::string& scheme,
}
}
void WebFrame::InsertText(const std::string& text) {
web_frame_->FrameWidget()->GetActiveWebInputMethodController()->CommitText(
blink::WebString::FromUTF8(text),
blink::WebVector<blink::WebImeTextSpan>(), blink::WebRange(), 0);
void InsertText(v8::Local<v8::Value> window, const std::string& text) {
blink::WebFrame* web_frame = GetRenderFrame(window)->GetWebFrame();
if (web_frame->IsWebLocalFrame()) {
web_frame->ToWebLocalFrame()
->FrameWidget()
->GetActiveWebInputMethodController()
->CommitText(blink::WebString::FromUTF8(text),
blink::WebVector<blink::WebImeTextSpan>(),
blink::WebRange(), 0);
}
}
void WebFrame::InsertCSS(const std::string& css) {
web_frame_->GetDocument().InsertStyleSheet(blink::WebString::FromUTF8(css));
void InsertCSS(v8::Local<v8::Value> window, const std::string& css) {
blink::WebFrame* web_frame = GetRenderFrame(window)->GetWebFrame();
if (web_frame->IsWebLocalFrame()) {
web_frame->ToWebLocalFrame()->GetDocument().InsertStyleSheet(
blink::WebString::FromUTF8(css));
}
}
void WebFrame::ExecuteJavaScript(const base::string16& code,
mate::Arguments* args) {
void ExecuteJavaScript(v8::Local<v8::Value> window,
const base::string16& code,
mate::Arguments* args) {
bool has_user_gesture = false;
args->GetNext(&has_user_gesture);
ScriptExecutionCallback::CompletionCallback completion_callback;
args->GetNext(&completion_callback);
std::unique_ptr<blink::WebScriptExecutionCallback> callback(
new ScriptExecutionCallback(completion_callback));
web_frame_->RequestExecuteScriptAndReturnValue(
GetRenderFrame(window)->GetWebFrame()->RequestExecuteScriptAndReturnValue(
blink::WebScriptSource(blink::WebString::FromUTF16(code)),
has_user_gesture, callback.release());
}
void WebFrame::ExecuteJavaScriptInIsolatedWorld(
void ExecuteJavaScriptInIsolatedWorld(
v8::Local<v8::Value> window,
int world_id,
const std::vector<mate::Dictionary>& scripts,
mate::Arguments* args) {
@@ -341,186 +421,132 @@ void WebFrame::ExecuteJavaScriptInIsolatedWorld(
std::unique_ptr<blink::WebScriptExecutionCallback> callback(
new ScriptExecutionCallback(completion_callback));
web_frame_->RequestExecuteScriptInIsolatedWorld(
GetRenderFrame(window)->GetWebFrame()->RequestExecuteScriptInIsolatedWorld(
world_id, &sources.front(), sources.size(), has_user_gesture,
scriptExecutionType, callback.release());
}
void WebFrame::SetIsolatedWorldSecurityOrigin(int world_id,
const std::string& origin_url) {
web_frame_->SetIsolatedWorldSecurityOrigin(
void SetIsolatedWorldSecurityOrigin(v8::Local<v8::Value> window,
int world_id,
const std::string& origin_url) {
GetRenderFrame(window)->GetWebFrame()->SetIsolatedWorldSecurityOrigin(
world_id, blink::WebSecurityOrigin::CreateFromString(
blink::WebString::FromUTF8(origin_url)));
}
void WebFrame::SetIsolatedWorldContentSecurityPolicy(
int world_id,
const std::string& security_policy) {
web_frame_->SetIsolatedWorldContentSecurityPolicy(
void SetIsolatedWorldContentSecurityPolicy(v8::Local<v8::Value> window,
int world_id,
const std::string& security_policy) {
GetRenderFrame(window)->GetWebFrame()->SetIsolatedWorldContentSecurityPolicy(
world_id, blink::WebString::FromUTF8(security_policy));
}
void WebFrame::SetIsolatedWorldHumanReadableName(int world_id,
const std::string& name) {
web_frame_->SetIsolatedWorldHumanReadableName(
void SetIsolatedWorldHumanReadableName(v8::Local<v8::Value> window,
int world_id,
const std::string& name) {
GetRenderFrame(window)->GetWebFrame()->SetIsolatedWorldHumanReadableName(
world_id, blink::WebString::FromUTF8(name));
}
// static
mate::Handle<WebFrame> WebFrame::Create(v8::Isolate* isolate) {
return mate::CreateHandle(isolate, new WebFrame(isolate));
}
blink::WebCache::ResourceTypeStats WebFrame::GetResourceUsage(
v8::Isolate* isolate) {
blink::WebCache::ResourceTypeStats GetResourceUsage(v8::Isolate* isolate) {
blink::WebCache::ResourceTypeStats stats;
blink::WebCache::GetResourceTypeStats(&stats);
return stats;
}
void WebFrame::ClearCache(v8::Isolate* isolate) {
void ClearCache(v8::Isolate* isolate) {
isolate->IdleNotificationDeadline(0.5);
blink::WebCache::Clear();
base::MemoryPressureListener::NotifyMemoryPressure(
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
}
v8::Local<v8::Value> WebFrame::Opener() const {
blink::WebFrame* frame = web_frame_->Opener();
if (frame && frame->IsWebLocalFrame())
return mate::CreateHandle(isolate(),
new WebFrame(isolate(), frame->ToWebLocalFrame()))
.ToV8();
else
return v8::Null(isolate());
}
v8::Local<v8::Value> WebFrame::Parent() const {
blink::WebFrame* frame = web_frame_->Parent();
if (frame && frame->IsWebLocalFrame())
return mate::CreateHandle(isolate(),
new WebFrame(isolate(), frame->ToWebLocalFrame()))
.ToV8();
else
return v8::Null(isolate());
}
v8::Local<v8::Value> WebFrame::Top() const {
blink::WebFrame* frame = web_frame_->Top();
if (frame && frame->IsWebLocalFrame())
return mate::CreateHandle(isolate(),
new WebFrame(isolate(), frame->ToWebLocalFrame()))
.ToV8();
else
return v8::Null(isolate());
}
v8::Local<v8::Value> WebFrame::FirstChild() const {
blink::WebFrame* frame = web_frame_->FirstChild();
if (frame && frame->IsWebLocalFrame())
return mate::CreateHandle(isolate(),
new WebFrame(isolate(), frame->ToWebLocalFrame()))
.ToV8();
else
return v8::Null(isolate());
}
v8::Local<v8::Value> WebFrame::NextSibling() const {
blink::WebFrame* frame = web_frame_->NextSibling();
if (frame && frame->IsWebLocalFrame())
return mate::CreateHandle(isolate(),
new WebFrame(isolate(), frame->ToWebLocalFrame()))
.ToV8();
else
return v8::Null(isolate());
}
v8::Local<v8::Value> WebFrame::GetFrameForSelector(
const std::string& selector) const {
blink::WebElement element = web_frame_->GetDocument().QuerySelector(
blink::WebString::FromUTF8(selector));
blink::WebLocalFrame* element_frame =
blink::WebLocalFrame::FromFrameOwnerElement(element);
if (element_frame)
return mate::CreateHandle(isolate(), new WebFrame(isolate(), element_frame))
.ToV8();
else
return v8::Null(isolate());
}
v8::Local<v8::Value> WebFrame::FindFrameByName(const std::string& name) const {
blink::WebLocalFrame* local_frame =
web_frame_->FindFrameByName(blink::WebString::FromUTF8(name))
->ToWebLocalFrame();
if (local_frame)
return mate::CreateHandle(isolate(), new WebFrame(isolate(), local_frame))
.ToV8();
else
return v8::Null(isolate());
}
v8::Local<v8::Value> WebFrame::FindFrameByRoutingId(int routing_id) const {
v8::Local<v8::Value> FindFrameByRoutingId(v8::Isolate* isolate,
v8::Local<v8::Value> window,
int routing_id) {
content::RenderFrame* render_frame =
content::RenderFrame::FromRoutingID(routing_id);
blink::WebLocalFrame* local_frame = nullptr;
if (render_frame)
local_frame = render_frame->GetWebFrame();
if (local_frame)
return mate::CreateHandle(isolate(), new WebFrame(isolate(), local_frame))
.ToV8();
return render_frame->GetWebFrame()->MainWorldScriptContext()->Global();
else
return v8::Null(isolate());
return v8::Null(isolate);
}
v8::Local<v8::Value> WebFrame::RoutingId() const {
int routing_id = content::RenderFrame::GetRoutingIdForWebFrame(web_frame_);
return v8::Number::New(isolate(), routing_id);
v8::Local<v8::Value> GetOpener(v8::Isolate* isolate,
v8::Local<v8::Value> window) {
blink::WebFrame* frame = GetRenderFrame(window)->GetWebFrame()->Opener();
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
else
return v8::Null(isolate);
}
// static
void WebFrame::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype) {
prototype->SetClassName(mate::StringToV8(isolate, "WebFrame"));
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
.SetMethod("setName", &WebFrame::SetName)
.SetMethod("setZoomLevel", &WebFrame::SetZoomLevel)
.SetMethod("getZoomLevel", &WebFrame::GetZoomLevel)
.SetMethod("setZoomFactor", &WebFrame::SetZoomFactor)
.SetMethod("getZoomFactor", &WebFrame::GetZoomFactor)
.SetMethod("setVisualZoomLevelLimits",
&WebFrame::SetVisualZoomLevelLimits)
.SetMethod("setLayoutZoomLevelLimits",
&WebFrame::SetLayoutZoomLevelLimits)
.SetMethod("registerEmbedderCustomElement",
&WebFrame::RegisterEmbedderCustomElement)
.SetMethod("getWebFrameId", &WebFrame::GetWebFrameId)
.SetMethod("setSpellCheckProvider", &WebFrame::SetSpellCheckProvider)
.SetMethod("registerURLSchemeAsBypassingCSP",
&WebFrame::RegisterURLSchemeAsBypassingCSP)
.SetMethod("registerURLSchemeAsPrivileged",
&WebFrame::RegisterURLSchemeAsPrivileged)
.SetMethod("insertText", &WebFrame::InsertText)
.SetMethod("insertCSS", &WebFrame::InsertCSS)
.SetMethod("executeJavaScript", &WebFrame::ExecuteJavaScript)
.SetMethod("executeJavaScriptInIsolatedWorld",
&WebFrame::ExecuteJavaScriptInIsolatedWorld)
.SetMethod("setIsolatedWorldSecurityOrigin",
&WebFrame::SetIsolatedWorldSecurityOrigin)
.SetMethod("setIsolatedWorldContentSecurityPolicy",
&WebFrame::SetIsolatedWorldContentSecurityPolicy)
.SetMethod("setIsolatedWorldHumanReadableName",
&WebFrame::SetIsolatedWorldHumanReadableName)
.SetMethod("getResourceUsage", &WebFrame::GetResourceUsage)
.SetMethod("clearCache", &WebFrame::ClearCache)
.SetMethod("getFrameForSelector", &WebFrame::GetFrameForSelector)
.SetMethod("findFrameByName", &WebFrame::FindFrameByName)
.SetProperty("opener", &WebFrame::Opener)
.SetProperty("parent", &WebFrame::Parent)
.SetProperty("top", &WebFrame::Top)
.SetProperty("firstChild", &WebFrame::FirstChild)
.SetProperty("nextSibling", &WebFrame::NextSibling)
.SetProperty("routingId", &WebFrame::RoutingId)
.SetMethod("findFrameByRoutingId", &WebFrame::FindFrameByRoutingId);
// Don't name it as GetParent, Windows has API with same name.
v8::Local<v8::Value> GetFrameParent(v8::Isolate* isolate,
v8::Local<v8::Value> window) {
blink::WebFrame* frame = GetRenderFrame(window)->GetWebFrame()->Parent();
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
else
return v8::Null(isolate);
}
v8::Local<v8::Value> GetTop(v8::Isolate* isolate, v8::Local<v8::Value> window) {
blink::WebFrame* frame = GetRenderFrame(window)->GetWebFrame()->Top();
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
else
return v8::Null(isolate);
}
v8::Local<v8::Value> GetFirstChild(v8::Isolate* isolate,
v8::Local<v8::Value> window) {
blink::WebFrame* frame = GetRenderFrame(window)->GetWebFrame()->FirstChild();
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
else
return v8::Null(isolate);
}
v8::Local<v8::Value> GetNextSibling(v8::Isolate* isolate,
v8::Local<v8::Value> window) {
blink::WebFrame* frame = GetRenderFrame(window)->GetWebFrame()->NextSibling();
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
else
return v8::Null(isolate);
}
v8::Local<v8::Value> GetFrameForSelector(v8::Isolate* isolate,
v8::Local<v8::Value> window,
const std::string& selector) {
blink::WebElement element =
GetRenderFrame(window)->GetWebFrame()->GetDocument().QuerySelector(
blink::WebString::FromUTF8(selector));
if (element.IsNull()) // not found
return v8::Null(isolate);
blink::WebFrame* frame = blink::WebFrame::FromFrameOwnerElement(element);
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
else
return v8::Null(isolate);
}
v8::Local<v8::Value> FindFrameByName(v8::Isolate* isolate,
v8::Local<v8::Value> window,
const std::string& name) {
blink::WebFrame* frame =
GetRenderFrame(window)->GetWebFrame()->FindFrameByName(
blink::WebString::FromUTF8(name));
if (frame && frame->IsWebLocalFrame())
return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global();
else
return v8::Null(isolate);
}
int GetRoutingId(v8::Local<v8::Value> window) {
return GetRenderFrame(window)->GetRoutingID();
}
} // namespace api
@@ -529,16 +555,51 @@ void WebFrame::BuildPrototype(v8::Isolate* isolate,
namespace {
using atom::api::WebFrame;
void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv) {
using namespace atom::api; // NOLINT(build/namespaces)
v8::Isolate* isolate = context->GetIsolate();
mate::Dictionary dict(isolate, exports);
dict.Set("webFrame", WebFrame::Create(isolate));
dict.Set("WebFrame", WebFrame::GetConstructor(isolate)->GetFunction());
dict.SetMethod("setName", &SetName);
dict.SetMethod("setZoomLevel", &SetZoomLevel);
dict.SetMethod("getZoomLevel", &GetZoomLevel);
dict.SetMethod("setZoomFactor", &SetZoomFactor);
dict.SetMethod("getZoomFactor", &GetZoomFactor);
dict.SetMethod("setVisualZoomLevelLimits", &SetVisualZoomLevelLimits);
dict.SetMethod("setLayoutZoomLevelLimits", &SetLayoutZoomLevelLimits);
dict.SetMethod("registerEmbedderCustomElement",
&RegisterEmbedderCustomElement);
dict.SetMethod("getWebFrameId", &GetWebFrameId);
dict.SetMethod("setSpellCheckProvider", &SetSpellCheckProvider);
dict.SetMethod("registerURLSchemeAsBypassingCSP",
&RegisterURLSchemeAsBypassingCSP);
dict.SetMethod("registerURLSchemeAsPrivileged",
&RegisterURLSchemeAsPrivileged);
dict.SetMethod("insertText", &InsertText);
dict.SetMethod("insertCSS", &InsertCSS);
dict.SetMethod("executeJavaScript", &ExecuteJavaScript);
dict.SetMethod("executeJavaScriptInIsolatedWorld",
&ExecuteJavaScriptInIsolatedWorld);
dict.SetMethod("setIsolatedWorldSecurityOrigin",
&SetIsolatedWorldSecurityOrigin);
dict.SetMethod("setIsolatedWorldContentSecurityPolicy",
&SetIsolatedWorldContentSecurityPolicy);
dict.SetMethod("setIsolatedWorldHumanReadableName",
&SetIsolatedWorldHumanReadableName);
dict.SetMethod("getResourceUsage", &GetResourceUsage);
dict.SetMethod("clearCache", &ClearCache);
dict.SetMethod("_findFrameByRoutingId", &FindFrameByRoutingId);
dict.SetMethod("_getFrameForSelector", &GetFrameForSelector);
dict.SetMethod("_findFrameByName", &FindFrameByName);
dict.SetMethod("_getOpener", &GetOpener);
dict.SetMethod("_getParent", &GetFrameParent);
dict.SetMethod("_getTop", &GetTop);
dict.SetMethod("_getFirstChild", &GetFirstChild);
dict.SetMethod("_getNextSibling", &GetNextSibling);
dict.SetMethod("_getRoutingId", &GetRoutingId);
}
} // namespace

View File

@@ -1,114 +0,0 @@
// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_RENDERER_API_ATOM_API_WEB_FRAME_H_
#define ATOM_RENDERER_API_ATOM_API_WEB_FRAME_H_
#include <memory>
#include <string>
#include <vector>
#include "native_mate/handle.h"
#include "native_mate/wrappable.h"
#include "third_party/blink/public/platform/web_cache.h"
namespace blink {
class WebLocalFrame;
}
namespace mate {
class Dictionary;
class Arguments;
} // namespace mate
namespace atom {
namespace api {
class SpellCheckClient;
class WebFrame : public mate::Wrappable<WebFrame> {
public:
static mate::Handle<WebFrame> Create(v8::Isolate* isolate);
static void BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype);
private:
explicit WebFrame(v8::Isolate* isolate);
explicit WebFrame(v8::Isolate* isolate, blink::WebLocalFrame* blink_frame);
~WebFrame() override;
void SetName(const std::string& name);
double SetZoomLevel(double level);
double GetZoomLevel() const;
double SetZoomFactor(double factor);
double GetZoomFactor() const;
void SetVisualZoomLevelLimits(double min_level, double max_level);
void SetLayoutZoomLevelLimits(double min_level, double max_level);
v8::Local<v8::Value> RegisterEmbedderCustomElement(
v8::Local<v8::Object> context,
const base::string16& name,
v8::Local<v8::Object> options);
int GetWebFrameId(v8::Local<v8::Value> content_window);
// Set the provider that will be used by SpellCheckClient for spell check.
void SetSpellCheckProvider(mate::Arguments* args,
const std::string& language,
bool auto_spell_correct_turned_on,
v8::Local<v8::Object> provider);
void RegisterURLSchemeAsBypassingCSP(const std::string& scheme);
void RegisterURLSchemeAsPrivileged(const std::string& scheme,
mate::Arguments* args);
// Editing.
void InsertText(const std::string& text);
void InsertCSS(const std::string& css);
// Executing scripts.
void ExecuteJavaScript(const base::string16& code, mate::Arguments* args);
void ExecuteJavaScriptInIsolatedWorld(
int world_id,
const std::vector<mate::Dictionary>& scripts,
mate::Arguments* args);
// Isolated world related methods
void SetIsolatedWorldSecurityOrigin(int world_id,
const std::string& origin_url);
void SetIsolatedWorldContentSecurityPolicy(
int world_id,
const std::string& security_policy);
void SetIsolatedWorldHumanReadableName(int world_id, const std::string& name);
// Resource related methods
blink::WebCache::ResourceTypeStats GetResourceUsage(v8::Isolate* isolate);
void ClearCache(v8::Isolate* isolate);
// Frame navigation
v8::Local<v8::Value> Opener() const;
v8::Local<v8::Value> Parent() const;
v8::Local<v8::Value> Top() const;
v8::Local<v8::Value> FirstChild() const;
v8::Local<v8::Value> NextSibling() const;
v8::Local<v8::Value> GetFrameForSelector(const std::string& selector) const;
v8::Local<v8::Value> FindFrameByName(const std::string& name) const;
v8::Local<v8::Value> FindFrameByRoutingId(int routing_id) const;
v8::Local<v8::Value> RoutingId() const;
std::unique_ptr<SpellCheckClient> spell_check_client_;
blink::WebLocalFrame* web_frame_;
DISALLOW_COPY_AND_ASSIGN(WebFrame);
};
} // namespace api
} // namespace atom
#endif // ATOM_RENDERER_API_ATOM_API_WEB_FRAME_H_

View File

@@ -216,7 +216,7 @@ void AtomRenderFrameObserver::OnTakeHeapSnapshot(
args.AppendBoolean(success);
render_frame_->Send(new AtomFrameHostMsg_Message(
render_frame_->GetRoutingID(), "ipc-message", args));
render_frame_->GetRoutingID(), "ipc-internal-message", args));
}
void AtomRenderFrameObserver::EmitIPCEvent(blink::WebLocalFrame* frame,

View File

@@ -158,6 +158,8 @@ void AtomSandboxedRendererClient::InitializeBindings(
process.SetMethod("hang", AtomBindings::Hang);
process.SetMethod("getHeapStatistics", &AtomBindings::GetHeapStatistics);
process.SetMethod("getSystemMemoryInfo", &AtomBindings::GetSystemMemoryInfo);
process.SetMethod("getProcessMemoryInfo",
&AtomBindings::GetProcessMemoryInfo);
process.SetMethod(
"getCPUUsage",
base::Bind(&AtomBindings::GetCPUUsage, base::Unretained(metrics_.get())));
@@ -230,6 +232,8 @@ void AtomSandboxedRendererClient::DidCreateScriptContext(
// Execute the function with proper arguments
ignore_result(
func->Call(context, v8::Null(isolate), node::arraysize(args), args));
InvokeIpcCallback(context, "onLoaded", std::vector<v8::Local<v8::Value>>());
}
void AtomSandboxedRendererClient::WillReleaseScriptContext(

View File

@@ -19,7 +19,6 @@
#include "base/command_line.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "chrome/renderer/tts_dispatcher.h"
#include "content/public/common/content_constants.h"
#include "content/public/common/content_switches.h"
#include "content/public/renderer/render_frame.h"
@@ -56,6 +55,10 @@
#include "components/printing/renderer/print_render_frame_helper.h"
#endif // BUILDFLAG(ENABLE_PRINTING)
#if BUILDFLAG(ENABLE_TTS)
#include "chrome/renderer/tts_dispatcher.h"
#endif // BUILDFLAG(ENABLE_TTS)
namespace atom {
namespace {
@@ -228,7 +231,11 @@ void RendererClientBase::DidClearWindowObject(
std::unique_ptr<blink::WebSpeechSynthesizer>
RendererClientBase::OverrideSpeechSynthesizer(
blink::WebSpeechSynthesizerClient* client) {
#if BUILDFLAG(ENABLE_TTS)
return std::make_unique<TtsDispatcher>(client);
#else
return nullptr;
#endif
}
bool RendererClientBase::OverrideCreatePlugin(

View File

@@ -13,8 +13,6 @@
namespace brightray {
int g_identifier_ = 1;
CocoaNotification::CocoaNotification(NotificationDelegate* delegate,
NotificationPresenter* presenter)
: Notification(delegate, presenter) {}
@@ -29,7 +27,9 @@ void CocoaNotification::Show(const NotificationOptions& options) {
notification_.reset([[NSUserNotification alloc] init]);
NSString* identifier =
[NSString stringWithFormat:@"ElectronNotification%d", g_identifier_++];
[NSString stringWithFormat:@"%@:notification:%@",
[[NSBundle mainBundle] bundleIdentifier],
[[[NSUUID alloc] init] UUIDString]];
[notification_ setTitle:base::SysUTF16ToNSString(options.title)];
[notification_ setSubtitle:base::SysUTF16ToNSString(options.subtitle)];
@@ -115,6 +115,8 @@ void CocoaNotification::Dismiss() {
NotificationDismissed();
this->LogAction("dismissed");
notification_.reset(nil);
}
void CocoaNotification::NotificationDisplayed() {

View File

@@ -20,10 +20,6 @@ std::string GetProductInternal() {
GetApplicationVersion().c_str());
}
std::string GetBrightrayUserAgent() {
return content::BuildUserAgentFromProduct(GetProductInternal());
}
ContentClient::ContentClient() {}
ContentClient::~ContentClient() {}
@@ -32,10 +28,6 @@ std::string ContentClient::GetProduct() const {
return GetProductInternal();
}
std::string ContentClient::GetUserAgent() const {
return GetBrightrayUserAgent();
}
base::string16 ContentClient::GetLocalizedString(int message_id) const {
return l10n_util::GetStringUTF16(message_id);
}

View File

@@ -21,7 +21,6 @@ class ContentClient : public content::ContentClient {
private:
std::string GetProduct() const override;
std::string GetUserAgent() const override;
base::string16 GetLocalizedString(int message_id) const override;
base::StringPiece GetDataResource(int resource_id,
ui::ScaleFactor) const override;

View File

@@ -12,5 +12,6 @@ proprietary_codecs = true
ffmpeg_branding = "Chrome"
enable_basic_printing = true
angle_enable_vulkan_validation_layers = false
is_cfi = false

View File

@@ -15,6 +15,7 @@ buildflag_header("buildflags") {
"ENABLE_VIEW_API=$enable_view_api",
"ENABLE_PEPPER_FLASH=$enable_pepper_flash",
"ENABLE_PDF_VIEWER=$enable_pdf_viewer",
"ENABLE_TTS=$enable_tts",
"OVERRIDE_LOCATION_PROVIDER=$enable_fake_location_provider",
]
}

View File

@@ -14,6 +14,8 @@ declare_args() {
enable_pdf_viewer = false
enable_tts = true
# Provide a fake location provider for mocking
# the geolocation responses. Disable it if you
# need to test with chromium's location provider.

View File

@@ -99,4 +99,28 @@ source_set("chrome") {
]
}
}
if (enable_tts) {
sources += [
"//chrome/browser/speech/tts_controller.h",
"//chrome/browser/speech/tts_controller_impl.cc",
"//chrome/browser/speech/tts_controller_impl.h",
"//chrome/browser/speech/tts_mac.mm",
"//chrome/browser/speech/tts_message_filter.cc",
"//chrome/browser/speech/tts_message_filter.h",
"//chrome/browser/speech/tts_platform.cc",
"//chrome/browser/speech/tts_platform.h",
"//chrome/browser/speech/tts_win.cc",
"//chrome/common/tts_messages.h",
"//chrome/common/tts_utterance_request.cc",
"//chrome/common/tts_utterance_request.h",
"//chrome/renderer/tts_dispatcher.cc",
"//chrome/renderer/tts_dispatcher.h",
]
if (is_linux) {
sources += [ "//chrome/browser/speech/tts_linux.cc" ]
deps += [ "//third_party/speech-dispatcher" ]
}
}
}

View File

@@ -1,336 +0,0 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_SPEECH_TTS_CONTROLLER_H_
#define CHROME_BROWSER_SPEECH_TTS_CONTROLLER_H_
#include <memory>
#include <queue>
#include <set>
#include <string>
#include <vector>
#include "base/memory/singleton.h"
#include "base/memory/weak_ptr.h"
#include "url/gurl.h"
class Utterance;
class TtsPlatformImpl;
namespace base {
class Value;
}
namespace content {
class BrowserContext;
}
// Events sent back from the TTS engine indicating the progress.
enum TtsEventType {
TTS_EVENT_START,
TTS_EVENT_END,
TTS_EVENT_WORD,
TTS_EVENT_SENTENCE,
TTS_EVENT_MARKER,
TTS_EVENT_INTERRUPTED,
TTS_EVENT_CANCELLED,
TTS_EVENT_ERROR,
TTS_EVENT_PAUSE,
TTS_EVENT_RESUME
};
enum TtsGenderType { TTS_GENDER_NONE, TTS_GENDER_MALE, TTS_GENDER_FEMALE };
// Returns true if this event type is one that indicates an utterance
// is finished and can be destroyed.
bool IsFinalTtsEventType(TtsEventType event_type);
// The continuous parameters that apply to a given utterance.
struct UtteranceContinuousParameters {
UtteranceContinuousParameters();
double rate;
double pitch;
double volume;
};
// Information about one voice.
struct VoiceData {
VoiceData();
VoiceData(const VoiceData&);
~VoiceData();
std::string name;
std::string lang;
TtsGenderType gender;
std::string extension_id;
std::set<TtsEventType> events;
// If true, the synthesis engine is a remote network resource.
// It may be higher latency and may incur bandwidth costs.
bool remote;
// If true, this is implemented by this platform's subclass of
// TtsPlatformImpl. If false, this is implemented by an extension.
bool native;
std::string native_voice_identifier;
};
// Interface that delegates TTS requests to user-installed extensions.
class TtsEngineDelegate {
public:
virtual ~TtsEngineDelegate() {}
// Return a list of all available voices registered.
virtual void GetVoices(content::BrowserContext* browser_context,
std::vector<VoiceData>* out_voices) = 0;
// Speak the given utterance by sending an event to the given TTS engine.
virtual void Speak(Utterance* utterance, const VoiceData& voice) = 0;
// Stop speaking the given utterance by sending an event to the target
// associated with this utterance.
virtual void Stop(Utterance* utterance) = 0;
// Pause in the middle of speaking this utterance.
virtual void Pause(Utterance* utterance) = 0;
// Resume speaking this utterance.
virtual void Resume(Utterance* utterance) = 0;
// Load the built-in component extension for ChromeOS.
virtual bool LoadBuiltInTtsExtension(
content::BrowserContext* browser_context) = 0;
};
// Class that wants to receive events on utterances.
class UtteranceEventDelegate {
public:
virtual ~UtteranceEventDelegate() {}
virtual void OnTtsEvent(Utterance* utterance,
TtsEventType event_type,
int char_index,
const std::string& error_message) = 0;
};
// Class that wants to be notified when the set of
// voices has changed.
class VoicesChangedDelegate {
public:
virtual ~VoicesChangedDelegate() {}
virtual void OnVoicesChanged() = 0;
};
// One speech utterance.
class Utterance {
public:
// Construct an utterance given a profile and a completion task to call
// when the utterance is done speaking. Before speaking this utterance,
// its other parameters like text, rate, pitch, etc. should all be set.
explicit Utterance(content::BrowserContext* browser_context);
~Utterance();
// Sends an event to the delegate. If the event type is TTS_EVENT_END
// or TTS_EVENT_ERROR, deletes the utterance. If |char_index| is -1,
// uses the last good value.
void OnTtsEvent(TtsEventType event_type,
int char_index,
const std::string& error_message);
// Finish an utterance without sending an event to the delegate.
void Finish();
// Getters and setters for the text to speak and other speech options.
void set_text(const std::string& text) { text_ = text; }
const std::string& text() const { return text_; }
void set_options(const base::Value* options);
const base::Value* options() const { return options_.get(); }
void set_src_extension_id(const std::string& src_extension_id) {
src_extension_id_ = src_extension_id;
}
const std::string& src_extension_id() { return src_extension_id_; }
void set_src_id(int src_id) { src_id_ = src_id; }
int src_id() { return src_id_; }
void set_src_url(const GURL& src_url) { src_url_ = src_url; }
const GURL& src_url() { return src_url_; }
void set_voice_name(const std::string& voice_name) {
voice_name_ = voice_name;
}
const std::string& voice_name() const { return voice_name_; }
void set_lang(const std::string& lang) { lang_ = lang; }
const std::string& lang() const { return lang_; }
void set_gender(TtsGenderType gender) { gender_ = gender; }
TtsGenderType gender() const { return gender_; }
void set_continuous_parameters(const UtteranceContinuousParameters& params) {
continuous_parameters_ = params;
}
const UtteranceContinuousParameters& continuous_parameters() {
return continuous_parameters_;
}
void set_can_enqueue(bool can_enqueue) { can_enqueue_ = can_enqueue; }
bool can_enqueue() const { return can_enqueue_; }
void set_required_event_types(const std::set<TtsEventType>& types) {
required_event_types_ = types;
}
const std::set<TtsEventType>& required_event_types() const {
return required_event_types_;
}
void set_desired_event_types(const std::set<TtsEventType>& types) {
desired_event_types_ = types;
}
const std::set<TtsEventType>& desired_event_types() const {
return desired_event_types_;
}
const std::string& extension_id() const { return extension_id_; }
void set_extension_id(const std::string& extension_id) {
extension_id_ = extension_id;
}
UtteranceEventDelegate* event_delegate() const {
return event_delegate_.get();
}
void set_event_delegate(
base::WeakPtr<UtteranceEventDelegate> event_delegate) {
event_delegate_ = event_delegate;
}
// Getters and setters for internal state.
content::BrowserContext* browser_context() const { return browser_context_; }
int id() const { return id_; }
bool finished() const { return finished_; }
private:
// The BrowserContext that initiated this utterance.
content::BrowserContext* browser_context_;
// The extension ID of the extension providing TTS for this utterance, or
// empty if native TTS is being used.
std::string extension_id_;
// The unique ID of this utterance, used to associate callback functions
// with utterances.
int id_;
// The id of the next utterance, so we can associate requests with
// responses.
static int next_utterance_id_;
// The text to speak.
std::string text_;
// The full options arg passed to tts.speak, which may include fields
// other than the ones we explicitly parse, below.
std::unique_ptr<base::Value> options_;
// The extension ID of the extension that called speak() and should
// receive events.
std::string src_extension_id_;
// The source extension's ID of this utterance, so that it can associate
// events with the appropriate callback.
int src_id_;
// The URL of the page where the source extension called speak.
GURL src_url_;
// The delegate to be called when an utterance event is fired.
base::WeakPtr<UtteranceEventDelegate> event_delegate_;
// The parsed options.
std::string voice_name_;
std::string lang_;
TtsGenderType gender_;
UtteranceContinuousParameters continuous_parameters_;
bool can_enqueue_;
std::set<TtsEventType> required_event_types_;
std::set<TtsEventType> desired_event_types_;
// The index of the current char being spoken.
int char_index_;
// True if this utterance received an event indicating it's done.
bool finished_;
};
// Singleton class that manages text-to-speech for the TTS and TTS engine
// extension APIs, maintaining a queue of pending utterances and keeping
// track of all state.
class TtsController {
public:
// Get the single instance of this class.
static TtsController* GetInstance();
// Returns true if we're currently speaking an utterance.
virtual bool IsSpeaking() = 0;
// Speak the given utterance. If the utterance's can_enqueue flag is true
// and another utterance is in progress, adds it to the end of the queue.
// Otherwise, interrupts any current utterance and speaks this one
// immediately.
virtual void SpeakOrEnqueue(Utterance* utterance) = 0;
// Stop all utterances and flush the queue. Implies leaving pause mode
// as well.
virtual void Stop() = 0;
// Pause the speech queue. Some engines may support pausing in the middle
// of an utterance.
virtual void Pause() = 0;
// Resume speaking.
virtual void Resume() = 0;
// Handle events received from the speech engine. Events are forwarded to
// the callback function, and in addition, completion and error events
// trigger finishing the current utterance and starting the next one, if
// any.
virtual void OnTtsEvent(int utterance_id,
TtsEventType event_type,
int char_index,
const std::string& error_message) = 0;
// Return a list of all available voices, including the native voice,
// if supported, and all voices registered by extensions.
virtual void GetVoices(content::BrowserContext* browser_context,
std::vector<VoiceData>* out_voices) = 0;
// Called by the extension system or platform implementation when the
// list of voices may have changed and should be re-queried.
virtual void VoicesChanged() = 0;
// Add a delegate that wants to be notified when the set of voices changes.
virtual void AddVoicesChangedDelegate(VoicesChangedDelegate* delegate) = 0;
// Remove delegate that wants to be notified when the set of voices changes.
virtual void RemoveVoicesChangedDelegate(VoicesChangedDelegate* delegate) = 0;
// Set the delegate that processes TTS requests with user-installed
// extensions.
virtual void SetTtsEngineDelegate(TtsEngineDelegate* delegate) = 0;
// Get the delegate that processes TTS requests with user-installed
// extensions.
virtual TtsEngineDelegate* GetTtsEngineDelegate() = 0;
// For unit testing.
virtual void SetPlatformImpl(TtsPlatformImpl* platform_impl) = 0;
virtual int QueueSize() = 0;
protected:
virtual ~TtsController() {}
};
#endif // CHROME_BROWSER_SPEECH_TTS_CONTROLLER_H_

View File

@@ -1,447 +0,0 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/speech/tts_controller_impl.h"
#include <string>
#include <vector>
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/speech/tts_platform.h"
namespace {
// A value to be used to indicate that there is no char index available.
const int kInvalidCharIndex = -1;
// Given a language/region code of the form 'fr-FR', returns just the basic
// language portion, e.g. 'fr'.
std::string TrimLanguageCode(std::string lang) {
if (lang.size() >= 5 && lang[2] == '-')
return lang.substr(0, 2);
else
return lang;
}
} // namespace
bool IsFinalTtsEventType(TtsEventType event_type) {
return (event_type == TTS_EVENT_END || event_type == TTS_EVENT_INTERRUPTED ||
event_type == TTS_EVENT_CANCELLED || event_type == TTS_EVENT_ERROR);
}
//
// UtteranceContinuousParameters
//
UtteranceContinuousParameters::UtteranceContinuousParameters()
: rate(-1), pitch(-1), volume(-1) {}
//
// VoiceData
//
VoiceData::VoiceData()
: gender(TTS_GENDER_NONE), remote(false), native(false) {}
VoiceData::VoiceData(const VoiceData&) = default;
VoiceData::~VoiceData() = default;
//
// Utterance
//
// static
int Utterance::next_utterance_id_ = 0;
Utterance::Utterance(content::BrowserContext* browser_context)
: browser_context_(browser_context),
id_(next_utterance_id_++),
src_id_(-1),
gender_(TTS_GENDER_NONE),
can_enqueue_(false),
char_index_(0),
finished_(false) {
options_.reset(new base::DictionaryValue());
}
Utterance::~Utterance() {
DCHECK(finished_);
}
void Utterance::OnTtsEvent(TtsEventType event_type,
int char_index,
const std::string& error_message) {
if (char_index >= 0)
char_index_ = char_index;
if (IsFinalTtsEventType(event_type))
finished_ = true;
if (event_delegate_)
event_delegate_->OnTtsEvent(this, event_type, char_index, error_message);
if (finished_)
event_delegate_.reset();
}
void Utterance::Finish() {
finished_ = true;
}
void Utterance::set_options(const base::Value* options) {
options_.reset(options->DeepCopy());
}
TtsController* TtsController::GetInstance() {
return TtsControllerImpl::GetInstance();
}
//
// TtsControllerImpl
//
// static
TtsControllerImpl* TtsControllerImpl::GetInstance() {
return base::Singleton<TtsControllerImpl>::get();
}
TtsControllerImpl::TtsControllerImpl()
: current_utterance_(NULL),
paused_(false),
platform_impl_(NULL),
tts_engine_delegate_(NULL) {}
TtsControllerImpl::~TtsControllerImpl() {
if (current_utterance_) {
current_utterance_->Finish();
delete current_utterance_;
}
// Clear any queued utterances too.
ClearUtteranceQueue(false); // Don't sent events.
}
void TtsControllerImpl::SpeakOrEnqueue(Utterance* utterance) {
// If we're paused and we get an utterance that can't be queued,
// flush the queue but stay in the paused state.
if (paused_ && !utterance->can_enqueue()) {
Stop();
paused_ = true;
delete utterance;
return;
}
if (paused_ || (IsSpeaking() && utterance->can_enqueue())) {
utterance_queue_.push(utterance);
} else {
Stop();
SpeakNow(utterance);
}
}
void TtsControllerImpl::SpeakNow(Utterance* utterance) {
// Ensure we have all built-in voices loaded. This is a no-op if already
// loaded.
bool loaded_built_in =
GetPlatformImpl()->LoadBuiltInTtsExtension(utterance->browser_context());
// Get all available voices and try to find a matching voice.
std::vector<VoiceData> voices;
GetVoices(utterance->browser_context(), &voices);
int index = GetMatchingVoice(utterance, voices);
VoiceData voice;
if (index != -1) {
// Select the matching voice.
voice = voices[index];
} else {
// However, if no match was found on a platform without native tts voices,
// attempt to get a voice based only on the current locale without respect
// to any supplied voice names.
std::vector<VoiceData> native_voices;
if (GetPlatformImpl()->PlatformImplAvailable())
GetPlatformImpl()->GetVoices(&native_voices);
if (native_voices.empty() && !voices.empty()) {
// TODO(dtseng): Notify extension caller of an error.
utterance->set_voice_name("");
// TODO(gaochun): Replace the global variable g_browser_process with
// GetContentClient()->browser() to eliminate the dependency of browser
// once TTS implementation was moved to content.
utterance->set_lang(g_browser_process->GetApplicationLocale());
index = GetMatchingVoice(utterance, voices);
// If even that fails, just take the first available voice.
if (index == -1)
index = 0;
voice = voices[index];
} else {
// Otherwise, simply give native voices a chance to handle this utterance.
voice.native = true;
}
}
GetPlatformImpl()->WillSpeakUtteranceWithVoice(utterance, voice);
if (!voice.native) {
#if !defined(OS_ANDROID)
DCHECK(!voice.extension_id.empty());
current_utterance_ = utterance;
utterance->set_extension_id(voice.extension_id);
if (tts_engine_delegate_)
tts_engine_delegate_->Speak(utterance, voice);
bool sends_end_event =
voice.events.find(TTS_EVENT_END) != voice.events.end();
if (!sends_end_event) {
utterance->Finish();
delete utterance;
current_utterance_ = NULL;
SpeakNextUtterance();
}
#endif
} else {
// It's possible for certain platforms to send start events immediately
// during |speak|.
current_utterance_ = utterance;
GetPlatformImpl()->clear_error();
bool success = GetPlatformImpl()->Speak(utterance->id(), utterance->text(),
utterance->lang(), voice,
utterance->continuous_parameters());
if (!success)
current_utterance_ = NULL;
// If the native voice wasn't able to process this speech, see if
// the browser has built-in TTS that isn't loaded yet.
if (!success && loaded_built_in) {
utterance_queue_.push(utterance);
return;
}
if (!success) {
utterance->OnTtsEvent(TTS_EVENT_ERROR, kInvalidCharIndex,
GetPlatformImpl()->error());
delete utterance;
return;
}
}
}
void TtsControllerImpl::Stop() {
paused_ = false;
if (current_utterance_ && !current_utterance_->extension_id().empty()) {
#if !defined(OS_ANDROID)
if (tts_engine_delegate_)
tts_engine_delegate_->Stop(current_utterance_);
#endif
} else {
GetPlatformImpl()->clear_error();
GetPlatformImpl()->StopSpeaking();
}
if (current_utterance_)
current_utterance_->OnTtsEvent(TTS_EVENT_INTERRUPTED, kInvalidCharIndex,
std::string());
FinishCurrentUtterance();
ClearUtteranceQueue(true); // Send events.
}
void TtsControllerImpl::Pause() {
paused_ = true;
if (current_utterance_ && !current_utterance_->extension_id().empty()) {
#if !defined(OS_ANDROID)
if (tts_engine_delegate_)
tts_engine_delegate_->Pause(current_utterance_);
#endif
} else if (current_utterance_) {
GetPlatformImpl()->clear_error();
GetPlatformImpl()->Pause();
}
}
void TtsControllerImpl::Resume() {
paused_ = false;
if (current_utterance_ && !current_utterance_->extension_id().empty()) {
#if !defined(OS_ANDROID)
if (tts_engine_delegate_)
tts_engine_delegate_->Resume(current_utterance_);
#endif
} else if (current_utterance_) {
GetPlatformImpl()->clear_error();
GetPlatformImpl()->Resume();
} else {
SpeakNextUtterance();
}
}
void TtsControllerImpl::OnTtsEvent(int utterance_id,
TtsEventType event_type,
int char_index,
const std::string& error_message) {
// We may sometimes receive completion callbacks "late", after we've
// already finished the utterance (for example because another utterance
// interrupted or we got a call to Stop). This is normal and we can
// safely just ignore these events.
if (!current_utterance_ || utterance_id != current_utterance_->id()) {
return;
}
current_utterance_->OnTtsEvent(event_type, char_index, error_message);
if (current_utterance_->finished()) {
FinishCurrentUtterance();
SpeakNextUtterance();
}
}
void TtsControllerImpl::GetVoices(content::BrowserContext* browser_context,
std::vector<VoiceData>* out_voices) {
#if !defined(OS_ANDROID)
if (browser_context && tts_engine_delegate_)
tts_engine_delegate_->GetVoices(browser_context, out_voices);
#endif
TtsPlatformImpl* platform_impl = GetPlatformImpl();
if (platform_impl) {
// Ensure we have all built-in voices loaded. This is a no-op if already
// loaded.
platform_impl->LoadBuiltInTtsExtension(browser_context);
if (platform_impl->PlatformImplAvailable())
platform_impl->GetVoices(out_voices);
}
}
bool TtsControllerImpl::IsSpeaking() {
return current_utterance_ != NULL || GetPlatformImpl()->IsSpeaking();
}
void TtsControllerImpl::FinishCurrentUtterance() {
if (current_utterance_) {
if (!current_utterance_->finished())
current_utterance_->OnTtsEvent(TTS_EVENT_INTERRUPTED, kInvalidCharIndex,
std::string());
delete current_utterance_;
current_utterance_ = NULL;
}
}
void TtsControllerImpl::SpeakNextUtterance() {
if (paused_)
return;
// Start speaking the next utterance in the queue. Keep trying in case
// one fails but there are still more in the queue to try.
while (!utterance_queue_.empty() && !current_utterance_) {
Utterance* utterance = utterance_queue_.front();
utterance_queue_.pop();
SpeakNow(utterance);
}
}
void TtsControllerImpl::ClearUtteranceQueue(bool send_events) {
while (!utterance_queue_.empty()) {
Utterance* utterance = utterance_queue_.front();
utterance_queue_.pop();
if (send_events)
utterance->OnTtsEvent(TTS_EVENT_CANCELLED, kInvalidCharIndex,
std::string());
else
utterance->Finish();
delete utterance;
}
}
void TtsControllerImpl::SetPlatformImpl(TtsPlatformImpl* platform_impl) {
platform_impl_ = platform_impl;
}
int TtsControllerImpl::QueueSize() {
return static_cast<int>(utterance_queue_.size());
}
TtsPlatformImpl* TtsControllerImpl::GetPlatformImpl() {
if (!platform_impl_)
platform_impl_ = TtsPlatformImpl::GetInstance();
return platform_impl_;
}
int TtsControllerImpl::GetMatchingVoice(const Utterance* utterance,
std::vector<VoiceData>& voices) {
// Make two passes: the first time, do strict language matching
// ('fr-FR' does not match 'fr-CA'). The second time, do prefix
// language matching ('fr-FR' matches 'fr' and 'fr-CA')
for (int pass = 0; pass < 2; ++pass) {
for (size_t i = 0; i < voices.size(); ++i) {
const VoiceData& voice = voices[i];
if (!utterance->extension_id().empty() &&
utterance->extension_id() != voice.extension_id) {
continue;
}
if (!voice.name.empty() && !utterance->voice_name().empty() &&
voice.name != utterance->voice_name()) {
continue;
}
if (!voice.lang.empty() && !utterance->lang().empty()) {
std::string voice_lang = voice.lang;
std::string utterance_lang = utterance->lang();
if (pass == 1) {
voice_lang = TrimLanguageCode(voice_lang);
utterance_lang = TrimLanguageCode(utterance_lang);
}
if (voice_lang != utterance_lang) {
continue;
}
}
if (voice.gender != TTS_GENDER_NONE &&
utterance->gender() != TTS_GENDER_NONE &&
voice.gender != utterance->gender()) {
continue;
}
if (utterance->required_event_types().size() > 0) {
bool has_all_required_event_types = true;
for (std::set<TtsEventType>::const_iterator iter =
utterance->required_event_types().begin();
iter != utterance->required_event_types().end(); ++iter) {
if (voice.events.find(*iter) == voice.events.end()) {
has_all_required_event_types = false;
break;
}
}
if (!has_all_required_event_types)
continue;
}
return static_cast<int>(i);
}
}
return -1;
}
void TtsControllerImpl::VoicesChanged() {
for (std::set<VoicesChangedDelegate*>::iterator iter =
voices_changed_delegates_.begin();
iter != voices_changed_delegates_.end(); ++iter) {
(*iter)->OnVoicesChanged();
}
}
void TtsControllerImpl::AddVoicesChangedDelegate(
VoicesChangedDelegate* delegate) {
voices_changed_delegates_.insert(delegate);
}
void TtsControllerImpl::RemoveVoicesChangedDelegate(
VoicesChangedDelegate* delegate) {
voices_changed_delegates_.erase(delegate);
}
void TtsControllerImpl::SetTtsEngineDelegate(TtsEngineDelegate* delegate) {
tts_engine_delegate_ = delegate;
}
TtsEngineDelegate* TtsControllerImpl::GetTtsEngineDelegate() {
return tts_engine_delegate_;
}

View File

@@ -1,102 +0,0 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_SPEECH_TTS_CONTROLLER_IMPL_H_
#define CHROME_BROWSER_SPEECH_TTS_CONTROLLER_IMPL_H_
#include <memory>
#include <queue>
#include <set>
#include <string>
#include <vector>
#include "base/memory/singleton.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/speech/tts_controller.h"
#include "url/gurl.h"
namespace content {
class BrowserContext;
}
// Singleton class that manages text-to-speech for the TTS and TTS engine
// extension APIs, maintaining a queue of pending utterances and keeping
// track of all state.
class TtsControllerImpl : public TtsController {
public:
// Get the single instance of this class.
static TtsControllerImpl* GetInstance();
// TtsController methods
bool IsSpeaking() override;
void SpeakOrEnqueue(Utterance* utterance) override;
void Stop() override;
void Pause() override;
void Resume() override;
void OnTtsEvent(int utterance_id,
TtsEventType event_type,
int char_index,
const std::string& error_message) override;
void GetVoices(content::BrowserContext* browser_context,
std::vector<VoiceData>* out_voices) override;
void VoicesChanged() override;
void AddVoicesChangedDelegate(VoicesChangedDelegate* delegate) override;
void RemoveVoicesChangedDelegate(VoicesChangedDelegate* delegate) override;
void SetTtsEngineDelegate(TtsEngineDelegate* delegate) override;
TtsEngineDelegate* GetTtsEngineDelegate() override;
void SetPlatformImpl(TtsPlatformImpl* platform_impl) override;
int QueueSize() override;
protected:
TtsControllerImpl();
~TtsControllerImpl() override;
private:
// Get the platform TTS implementation (or injected mock).
TtsPlatformImpl* GetPlatformImpl();
// Start speaking the given utterance. Will either take ownership of
// |utterance| or delete it if there's an error. Returns true on success.
void SpeakNow(Utterance* utterance);
// Clear the utterance queue. If send_events is true, will send
// TTS_EVENT_CANCELLED events on each one.
void ClearUtteranceQueue(bool send_events);
// Finalize and delete the current utterance.
void FinishCurrentUtterance();
// Start speaking the next utterance in the queue.
void SpeakNextUtterance();
// Given an utterance and a vector of voices, return the
// index of the voice that best matches the utterance.
int GetMatchingVoice(const Utterance* utterance,
std::vector<VoiceData>& voices);
friend struct base::DefaultSingletonTraits<TtsControllerImpl>;
// The current utterance being spoken.
Utterance* current_utterance_;
// Whether the queue is paused or not.
bool paused_;
// A queue of utterances to speak after the current one finishes.
std::queue<Utterance*> utterance_queue_;
// A set of delegates that want to be notified when the voices change.
std::set<VoicesChangedDelegate*> voices_changed_delegates_;
// A pointer to the platform implementation of text-to-speech, for
// dependency injection.
TtsPlatformImpl* platform_impl_;
// The delegate that processes TTS requests with user-installed extensions.
TtsEngineDelegate* tts_engine_delegate_;
DISALLOW_COPY_AND_ASSIGN(TtsControllerImpl);
};
#endif // CHROME_BROWSER_SPEECH_TTS_CONTROLLER_IMPL_H_

View File

@@ -1,349 +0,0 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <math.h>
#include <map>
#include <memory>
#include "base/command_line.h"
#include "base/debug/leak_annotations.h"
#include "base/memory/singleton.h"
#include "base/synchronization/lock.h"
#include "base/task_scheduler/post_task.h"
#include "chrome/browser/speech/tts_platform.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_switches.h"
#include "library_loaders/libspeechd.h"
using content::BrowserThread;
namespace {
const char kNotSupportedError[] =
"Native speech synthesis not supported on this platform.";
struct SPDChromeVoice {
std::string name;
std::string module;
};
} // namespace
class TtsPlatformImplLinux : public TtsPlatformImpl {
public:
bool PlatformImplAvailable() override;
bool Speak(int utterance_id,
const std::string& utterance,
const std::string& lang,
const VoiceData& voice,
const UtteranceContinuousParameters& params) override;
bool StopSpeaking() override;
void Pause() override;
void Resume() override;
bool IsSpeaking() override;
void GetVoices(std::vector<VoiceData>* out_voices) override;
void OnSpeechEvent(SPDNotificationType type);
// Get the single instance of this class.
static TtsPlatformImplLinux* GetInstance();
private:
TtsPlatformImplLinux();
~TtsPlatformImplLinux() override;
// Initiate the connection with the speech dispatcher.
void Initialize();
// Resets the connection with speech dispatcher.
void Reset();
static void NotificationCallback(size_t msg_id,
size_t client_id,
SPDNotificationType type);
static void IndexMarkCallback(size_t msg_id,
size_t client_id,
SPDNotificationType state,
char* index_mark);
static SPDNotificationType current_notification_;
base::Lock initialization_lock_;
LibSpeechdLoader libspeechd_loader_;
SPDConnection* conn_;
// These apply to the current utterance only.
std::string utterance_;
int utterance_id_;
// Map a string composed of a voicename and module to the voicename. Used to
// uniquely identify a voice across all available modules.
std::unique_ptr<std::map<std::string, SPDChromeVoice>> all_native_voices_;
friend struct base::DefaultSingletonTraits<TtsPlatformImplLinux>;
DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplLinux);
};
// static
SPDNotificationType TtsPlatformImplLinux::current_notification_ = SPD_EVENT_END;
TtsPlatformImplLinux::TtsPlatformImplLinux() : utterance_id_(0) {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (!command_line.HasSwitch(switches::kEnableSpeechDispatcher))
return;
base::PostTaskWithTraits(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
base::Bind(&TtsPlatformImplLinux::Initialize, base::Unretained(this)));
}
void TtsPlatformImplLinux::Initialize() {
base::AutoLock lock(initialization_lock_);
if (!libspeechd_loader_.Load("libspeechd.so.2"))
return;
{
// spd_open has memory leaks which are hard to suppress.
// http://crbug.com/317360
ANNOTATE_SCOPED_MEMORY_LEAK;
conn_ = libspeechd_loader_.spd_open("chrome", "extension_api", NULL,
SPD_MODE_THREADED);
}
if (!conn_)
return;
// Register callbacks for all events.
conn_->callback_begin = conn_->callback_end = conn_->callback_cancel =
conn_->callback_pause = conn_->callback_resume = &NotificationCallback;
conn_->callback_im = &IndexMarkCallback;
libspeechd_loader_.spd_set_notification_on(conn_, SPD_BEGIN);
libspeechd_loader_.spd_set_notification_on(conn_, SPD_END);
libspeechd_loader_.spd_set_notification_on(conn_, SPD_CANCEL);
libspeechd_loader_.spd_set_notification_on(conn_, SPD_PAUSE);
libspeechd_loader_.spd_set_notification_on(conn_, SPD_RESUME);
}
TtsPlatformImplLinux::~TtsPlatformImplLinux() {
base::AutoLock lock(initialization_lock_);
if (conn_) {
libspeechd_loader_.spd_close(conn_);
conn_ = NULL;
}
}
void TtsPlatformImplLinux::Reset() {
base::AutoLock lock(initialization_lock_);
if (conn_)
libspeechd_loader_.spd_close(conn_);
conn_ = libspeechd_loader_.spd_open("chrome", "extension_api", NULL,
SPD_MODE_THREADED);
}
bool TtsPlatformImplLinux::PlatformImplAvailable() {
if (!initialization_lock_.Try())
return false;
bool result = libspeechd_loader_.loaded() && (conn_ != NULL);
initialization_lock_.Release();
return result;
}
bool TtsPlatformImplLinux::Speak(int utterance_id,
const std::string& utterance,
const std::string& lang,
const VoiceData& voice,
const UtteranceContinuousParameters& params) {
if (!PlatformImplAvailable()) {
error_ = kNotSupportedError;
return false;
}
// Speech dispatcher's speech params are around 3x at either limit.
float rate = params.rate > 3 ? 3 : params.rate;
rate = params.rate < 0.334 ? 0.334 : rate;
float pitch = params.pitch > 3 ? 3 : params.pitch;
pitch = params.pitch < 0.334 ? 0.334 : pitch;
std::map<std::string, SPDChromeVoice>::iterator it =
all_native_voices_->find(voice.name);
if (it != all_native_voices_->end()) {
libspeechd_loader_.spd_set_output_module(conn_, it->second.module.c_str());
libspeechd_loader_.spd_set_synthesis_voice(conn_, it->second.name.c_str());
}
// Map our multiplicative range to Speech Dispatcher's linear range.
// .334 = -100.
// 3 = 100.
libspeechd_loader_.spd_set_voice_rate(conn_, 100 * log10(rate) / log10(3));
libspeechd_loader_.spd_set_voice_pitch(conn_, 100 * log10(pitch) / log10(3));
// Support languages other than the default
if (!lang.empty())
libspeechd_loader_.spd_set_language(conn_, lang.c_str());
utterance_ = utterance;
utterance_id_ = utterance_id;
if (libspeechd_loader_.spd_say(conn_, SPD_TEXT, utterance.c_str()) == -1) {
Reset();
return false;
}
return true;
}
bool TtsPlatformImplLinux::StopSpeaking() {
if (!PlatformImplAvailable())
return false;
if (libspeechd_loader_.spd_stop(conn_) == -1) {
Reset();
return false;
}
return true;
}
void TtsPlatformImplLinux::Pause() {
if (!PlatformImplAvailable())
return;
libspeechd_loader_.spd_pause(conn_);
}
void TtsPlatformImplLinux::Resume() {
if (!PlatformImplAvailable())
return;
libspeechd_loader_.spd_resume(conn_);
}
bool TtsPlatformImplLinux::IsSpeaking() {
return current_notification_ == SPD_EVENT_BEGIN;
}
void TtsPlatformImplLinux::GetVoices(std::vector<VoiceData>* out_voices) {
if (!all_native_voices_.get()) {
all_native_voices_.reset(new std::map<std::string, SPDChromeVoice>());
char** modules = libspeechd_loader_.spd_list_modules(conn_);
if (!modules)
return;
for (int i = 0; modules[i]; i++) {
char* module = modules[i];
libspeechd_loader_.spd_set_output_module(conn_, module);
SPDVoice** native_voices =
libspeechd_loader_.spd_list_synthesis_voices(conn_);
if (!native_voices) {
free(module);
continue;
}
for (int j = 0; native_voices[j]; j++) {
SPDVoice* native_voice = native_voices[j];
SPDChromeVoice native_data;
native_data.name = native_voice->name;
native_data.module = module;
std::string key;
key.append(native_data.name);
key.append(" ");
key.append(native_data.module);
all_native_voices_->insert(
std::pair<std::string, SPDChromeVoice>(key, native_data));
free(native_voices[j]);
}
free(modules[i]);
}
}
for (std::map<std::string, SPDChromeVoice>::iterator it =
all_native_voices_->begin();
it != all_native_voices_->end(); it++) {
out_voices->push_back(VoiceData());
VoiceData& voice = out_voices->back();
voice.native = true;
voice.name = it->first;
voice.events.insert(TTS_EVENT_START);
voice.events.insert(TTS_EVENT_END);
voice.events.insert(TTS_EVENT_CANCELLED);
voice.events.insert(TTS_EVENT_MARKER);
voice.events.insert(TTS_EVENT_PAUSE);
voice.events.insert(TTS_EVENT_RESUME);
}
}
void TtsPlatformImplLinux::OnSpeechEvent(SPDNotificationType type) {
TtsController* controller = TtsController::GetInstance();
switch (type) {
case SPD_EVENT_BEGIN:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, std::string());
break;
case SPD_EVENT_RESUME:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME, 0, std::string());
break;
case SPD_EVENT_END:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_END, utterance_.size(),
std::string());
break;
case SPD_EVENT_PAUSE:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_PAUSE, utterance_.size(),
std::string());
break;
case SPD_EVENT_CANCEL:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_CANCELLED, 0,
std::string());
break;
case SPD_EVENT_INDEX_MARK:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_MARKER, 0, std::string());
break;
}
}
// static
void TtsPlatformImplLinux::NotificationCallback(size_t msg_id,
size_t client_id,
SPDNotificationType type) {
// We run Speech Dispatcher in threaded mode, so these callbacks should always
// be in a separate thread.
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
current_notification_ = type;
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&TtsPlatformImplLinux::OnSpeechEvent,
base::Unretained(TtsPlatformImplLinux::GetInstance()),
type));
}
}
// static
void TtsPlatformImplLinux::IndexMarkCallback(size_t msg_id,
size_t client_id,
SPDNotificationType state,
char* index_mark) {
// TODO(dtseng): index_mark appears to specify an index type supplied by a
// client. Need to explore how this is used before hooking it up with existing
// word, sentence events.
// We run Speech Dispatcher in threaded mode, so these callbacks should always
// be in a separate thread.
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
current_notification_ = state;
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&TtsPlatformImplLinux::OnSpeechEvent,
base::Unretained(TtsPlatformImplLinux::GetInstance()),
state));
}
}
// static
TtsPlatformImplLinux* TtsPlatformImplLinux::GetInstance() {
return base::Singleton<
TtsPlatformImplLinux,
base::LeakySingletonTraits<TtsPlatformImplLinux>>::get();
}
// static
TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
return TtsPlatformImplLinux::GetInstance();
}

View File

@@ -1,344 +0,0 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include "base/mac/scoped_nsobject.h"
#include "base/memory/singleton.h"
#include "base/strings/sys_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/speech/tts_controller.h"
#include "chrome/browser/speech/tts_platform.h"
#import <Cocoa/Cocoa.h>
class TtsPlatformImplMac;
@interface ChromeTtsDelegate : NSObject <NSSpeechSynthesizerDelegate> {
@private
TtsPlatformImplMac* ttsImplMac_; // weak.
}
- (id)initWithPlatformImplMac:(TtsPlatformImplMac*)ttsImplMac;
@end
// Subclass of NSSpeechSynthesizer that takes an utterance
// string on initialization, retains it and only allows it
// to be spoken once.
//
// We construct a new NSSpeechSynthesizer for each utterance, for
// two reasons:
// 1. To associate delegate callbacks with a particular utterance,
// without assuming anything undocumented about the protocol.
// 2. To work around http://openradar.appspot.com/radar?id=2854403,
// where Nuance voices don't retain the utterance string and
// crash when trying to call willSpeakWord.
@interface SingleUseSpeechSynthesizer : NSSpeechSynthesizer {
@private
base::scoped_nsobject<NSString> utterance_;
bool didSpeak_;
}
- (id)initWithUtterance:(NSString*)utterance;
- (bool)startSpeakingRetainedUtterance;
- (bool)startSpeakingString:(NSString*)utterance;
@end
class TtsPlatformImplMac : public TtsPlatformImpl {
public:
bool PlatformImplAvailable() override { return true; }
bool Speak(int utterance_id,
const std::string& utterance,
const std::string& lang,
const VoiceData& voice,
const UtteranceContinuousParameters& params) override;
bool StopSpeaking() override;
void Pause() override;
void Resume() override;
bool IsSpeaking() override;
void GetVoices(std::vector<VoiceData>* out_voices) override;
// Called by ChromeTtsDelegate when we get a callback from the
// native speech engine.
void OnSpeechEvent(NSSpeechSynthesizer* sender,
TtsEventType event_type,
int char_index,
const std::string& error_message);
// Get the single instance of this class.
static TtsPlatformImplMac* GetInstance();
private:
TtsPlatformImplMac();
~TtsPlatformImplMac() override;
base::scoped_nsobject<SingleUseSpeechSynthesizer> speech_synthesizer_;
base::scoped_nsobject<ChromeTtsDelegate> delegate_;
int utterance_id_;
std::string utterance_;
int last_char_index_;
bool paused_;
friend struct base::DefaultSingletonTraits<TtsPlatformImplMac>;
DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplMac);
};
// static
TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
return TtsPlatformImplMac::GetInstance();
}
bool TtsPlatformImplMac::Speak(int utterance_id,
const std::string& utterance,
const std::string& lang,
const VoiceData& voice,
const UtteranceContinuousParameters& params) {
// TODO: convert SSML to SAPI xml. http://crbug.com/88072
utterance_ = utterance;
paused_ = false;
NSString* utterance_nsstring =
[NSString stringWithUTF8String:utterance_.c_str()];
// Deliberately construct a new speech synthesizer every time Speak is
// called, otherwise there's no way to know whether calls to the delegate
// apply to the current utterance or a previous utterance. In
// experimentation, the overhead of constructing and destructing a
// NSSpeechSynthesizer is minimal.
speech_synthesizer_.reset([[SingleUseSpeechSynthesizer alloc]
initWithUtterance:utterance_nsstring]);
[speech_synthesizer_ setDelegate:delegate_];
if (!voice.native_voice_identifier.empty()) {
NSString* native_voice_identifier =
[NSString stringWithUTF8String:voice.native_voice_identifier.c_str()];
[speech_synthesizer_ setVoice:native_voice_identifier];
}
utterance_id_ = utterance_id;
// TODO: support languages other than the default: crbug.com/88059
if (params.rate >= 0.0) {
// The TTS api defines rate via words per minute. Let 200 be the default.
[speech_synthesizer_ setObject:[NSNumber numberWithInt:params.rate * 200]
forProperty:NSSpeechRateProperty
error:nil];
}
if (params.pitch >= 0.0) {
// The input is a float from 0.0 to 2.0, with 1.0 being the default.
// Get the default pitch for this voice and modulate it by 50% - 150%.
NSError* errorCode;
NSNumber* defaultPitchObj =
[speech_synthesizer_ objectForProperty:NSSpeechPitchBaseProperty
error:&errorCode];
int defaultPitch = defaultPitchObj ? [defaultPitchObj intValue] : 48;
int newPitch = static_cast<int>(defaultPitch * (0.5 * params.pitch + 0.5));
[speech_synthesizer_ setObject:[NSNumber numberWithInt:newPitch]
forProperty:NSSpeechPitchBaseProperty
error:nil];
}
if (params.volume >= 0.0) {
[speech_synthesizer_ setObject:[NSNumber numberWithFloat:params.volume]
forProperty:NSSpeechVolumeProperty
error:nil];
}
bool success = [speech_synthesizer_ startSpeakingRetainedUtterance];
if (success) {
TtsController* controller = TtsController::GetInstance();
controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, "");
}
return success;
}
bool TtsPlatformImplMac::StopSpeaking() {
if (speech_synthesizer_.get()) {
[speech_synthesizer_ stopSpeaking];
speech_synthesizer_.reset(nil);
}
paused_ = false;
return true;
}
void TtsPlatformImplMac::Pause() {
if (speech_synthesizer_.get() && utterance_id_ && !paused_) {
[speech_synthesizer_ pauseSpeakingAtBoundary:NSSpeechImmediateBoundary];
paused_ = true;
TtsController::GetInstance()->OnTtsEvent(utterance_id_, TTS_EVENT_PAUSE,
last_char_index_, "");
}
}
void TtsPlatformImplMac::Resume() {
if (speech_synthesizer_.get() && utterance_id_ && paused_) {
[speech_synthesizer_ continueSpeaking];
paused_ = false;
TtsController::GetInstance()->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME,
last_char_index_, "");
}
}
bool TtsPlatformImplMac::IsSpeaking() {
if (speech_synthesizer_)
return [speech_synthesizer_ isSpeaking];
return false;
}
void TtsPlatformImplMac::GetVoices(std::vector<VoiceData>* outVoices) {
NSArray* voices = [NSSpeechSynthesizer availableVoices];
// Create a new temporary array of the available voices with
// the default voice first.
NSMutableArray* orderedVoices =
[NSMutableArray arrayWithCapacity:[voices count]];
NSString* defaultVoice = [NSSpeechSynthesizer defaultVoice];
if (defaultVoice) {
[orderedVoices addObject:defaultVoice];
}
for (NSString* voiceIdentifier in voices) {
if (![voiceIdentifier isEqualToString:defaultVoice])
[orderedVoices addObject:voiceIdentifier];
}
for (NSString* voiceIdentifier in orderedVoices) {
outVoices->push_back(VoiceData());
VoiceData& data = outVoices->back();
NSDictionary* attributes =
[NSSpeechSynthesizer attributesForVoice:voiceIdentifier];
NSString* name = [attributes objectForKey:NSVoiceName];
NSString* gender = [attributes objectForKey:NSVoiceGender];
NSString* localeIdentifier =
[attributes objectForKey:NSVoiceLocaleIdentifier];
data.native = true;
data.native_voice_identifier = base::SysNSStringToUTF8(voiceIdentifier);
data.name = base::SysNSStringToUTF8(name);
NSDictionary* localeComponents =
[NSLocale componentsFromLocaleIdentifier:localeIdentifier];
NSString* language = [localeComponents objectForKey:NSLocaleLanguageCode];
NSString* country = [localeComponents objectForKey:NSLocaleCountryCode];
if (language && country) {
data.lang =
[[NSString stringWithFormat:@"%@-%@", language, country] UTF8String];
} else {
data.lang = base::SysNSStringToUTF8(language);
}
if ([gender isEqualToString:NSVoiceGenderMale])
data.gender = TTS_GENDER_MALE;
else if ([gender isEqualToString:NSVoiceGenderFemale])
data.gender = TTS_GENDER_FEMALE;
else
data.gender = TTS_GENDER_NONE;
data.events.insert(TTS_EVENT_START);
data.events.insert(TTS_EVENT_END);
data.events.insert(TTS_EVENT_WORD);
data.events.insert(TTS_EVENT_ERROR);
data.events.insert(TTS_EVENT_CANCELLED);
data.events.insert(TTS_EVENT_INTERRUPTED);
data.events.insert(TTS_EVENT_PAUSE);
data.events.insert(TTS_EVENT_RESUME);
}
}
void TtsPlatformImplMac::OnSpeechEvent(NSSpeechSynthesizer* sender,
TtsEventType event_type,
int char_index,
const std::string& error_message) {
// Don't send events from an utterance that's already completed.
// This depends on the fact that we construct a new NSSpeechSynthesizer
// each time we call Speak.
if (sender != speech_synthesizer_.get())
return;
if (event_type == TTS_EVENT_END)
char_index = utterance_.size();
TtsController* controller = TtsController::GetInstance();
controller->OnTtsEvent(utterance_id_, event_type, char_index, error_message);
last_char_index_ = char_index;
}
TtsPlatformImplMac::TtsPlatformImplMac() {
utterance_id_ = -1;
paused_ = false;
delegate_.reset([[ChromeTtsDelegate alloc] initWithPlatformImplMac:this]);
}
TtsPlatformImplMac::~TtsPlatformImplMac() {}
// static
TtsPlatformImplMac* TtsPlatformImplMac::GetInstance() {
return base::Singleton<TtsPlatformImplMac>::get();
}
@implementation ChromeTtsDelegate
- (id)initWithPlatformImplMac:(TtsPlatformImplMac*)ttsImplMac {
if ((self = [super init])) {
ttsImplMac_ = ttsImplMac;
}
return self;
}
- (void)speechSynthesizer:(NSSpeechSynthesizer*)sender
didFinishSpeaking:(BOOL)finished_speaking {
ttsImplMac_->OnSpeechEvent(sender, TTS_EVENT_END, 0, "");
}
- (void)speechSynthesizer:(NSSpeechSynthesizer*)sender
willSpeakWord:(NSRange)character_range
ofString:(NSString*)string {
ttsImplMac_->OnSpeechEvent(sender, TTS_EVENT_WORD, character_range.location,
"");
}
- (void)speechSynthesizer:(NSSpeechSynthesizer*)sender
didEncounterErrorAtIndex:(NSUInteger)character_index
ofString:(NSString*)string
message:(NSString*)message {
std::string message_utf8 = base::SysNSStringToUTF8(message);
ttsImplMac_->OnSpeechEvent(sender, TTS_EVENT_ERROR, character_index,
message_utf8);
}
@end
@implementation SingleUseSpeechSynthesizer
- (id)initWithUtterance:(NSString*)utterance {
self = [super init];
if (self) {
utterance_.reset([utterance retain]);
didSpeak_ = false;
}
return self;
}
- (bool)startSpeakingRetainedUtterance {
CHECK(!didSpeak_);
CHECK(utterance_);
didSpeak_ = true;
return [super startSpeakingString:utterance_];
}
- (bool)startSpeakingString:(NSString*)utterance {
CHECK(false);
return false;
}
@end

View File

@@ -1,176 +0,0 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/speech/tts_message_filter.h"
#include "base/bind.h"
#include "base/logging.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_process_host.h"
using content::BrowserThread;
TtsMessageFilter::TtsMessageFilter(int render_process_id,
content::BrowserContext* browser_context)
: BrowserMessageFilter(TtsMsgStart),
render_process_id_(render_process_id),
browser_context_(browser_context),
weak_ptr_factory_(this) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TtsController::GetInstance()->AddVoicesChangedDelegate(this);
// Balanced in OnChannelClosingInUIThread() to keep the ref-count be non-zero
// until all WeakPtr's are invalidated.
AddRef();
}
void TtsMessageFilter::OverrideThreadForMessage(const IPC::Message& message,
BrowserThread::ID* thread) {
switch (message.type()) {
case TtsHostMsg_InitializeVoiceList::ID:
case TtsHostMsg_Speak::ID:
case TtsHostMsg_Pause::ID:
case TtsHostMsg_Resume::ID:
case TtsHostMsg_Cancel::ID:
*thread = BrowserThread::UI;
break;
}
}
bool TtsMessageFilter::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(TtsMessageFilter, message)
IPC_MESSAGE_HANDLER(TtsHostMsg_InitializeVoiceList, OnInitializeVoiceList)
IPC_MESSAGE_HANDLER(TtsHostMsg_Speak, OnSpeak)
IPC_MESSAGE_HANDLER(TtsHostMsg_Pause, OnPause)
IPC_MESSAGE_HANDLER(TtsHostMsg_Resume, OnResume)
IPC_MESSAGE_HANDLER(TtsHostMsg_Cancel, OnCancel)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void TtsMessageFilter::OnChannelClosing() {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&TtsMessageFilter::OnChannelClosingInUIThread, this));
}
void TtsMessageFilter::OnDestruct() const {
BrowserThread::DeleteOnUIThread::Destruct(this);
}
void TtsMessageFilter::OnInitializeVoiceList() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TtsController* tts_controller = TtsController::GetInstance();
std::vector<VoiceData> voices;
tts_controller->GetVoices(browser_context_, &voices);
std::vector<TtsVoice> out_voices;
out_voices.resize(voices.size());
for (size_t i = 0; i < voices.size(); ++i) {
TtsVoice& out_voice = out_voices[i];
out_voice.voice_uri = voices[i].name;
out_voice.name = voices[i].name;
out_voice.lang = voices[i].lang;
out_voice.local_service = !voices[i].remote;
out_voice.is_default = (i == 0);
}
Send(new TtsMsg_SetVoiceList(out_voices));
}
void TtsMessageFilter::OnSpeak(const TtsUtteranceRequest& request) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
std::unique_ptr<Utterance> utterance(new Utterance(browser_context_));
utterance->set_src_id(request.id);
utterance->set_text(request.text);
utterance->set_lang(request.lang);
utterance->set_voice_name(request.voice);
utterance->set_can_enqueue(true);
UtteranceContinuousParameters params;
params.rate = request.rate;
params.pitch = request.pitch;
params.volume = request.volume;
utterance->set_continuous_parameters(params);
utterance->set_event_delegate(weak_ptr_factory_.GetWeakPtr());
TtsController::GetInstance()->SpeakOrEnqueue(utterance.release());
}
void TtsMessageFilter::OnPause() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TtsController::GetInstance()->Pause();
}
void TtsMessageFilter::OnResume() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TtsController::GetInstance()->Resume();
}
void TtsMessageFilter::OnCancel() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TtsController::GetInstance()->Stop();
}
void TtsMessageFilter::OnTtsEvent(Utterance* utterance,
TtsEventType event_type,
int char_index,
const std::string& error_message) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
switch (event_type) {
case TTS_EVENT_START:
Send(new TtsMsg_DidStartSpeaking(utterance->src_id()));
break;
case TTS_EVENT_END:
Send(new TtsMsg_DidFinishSpeaking(utterance->src_id()));
break;
case TTS_EVENT_WORD:
Send(new TtsMsg_WordBoundary(utterance->src_id(), char_index));
break;
case TTS_EVENT_SENTENCE:
Send(new TtsMsg_SentenceBoundary(utterance->src_id(), char_index));
break;
case TTS_EVENT_MARKER:
Send(new TtsMsg_MarkerEvent(utterance->src_id(), char_index));
break;
case TTS_EVENT_INTERRUPTED:
Send(new TtsMsg_WasInterrupted(utterance->src_id()));
break;
case TTS_EVENT_CANCELLED:
Send(new TtsMsg_WasCancelled(utterance->src_id()));
break;
case TTS_EVENT_ERROR:
Send(
new TtsMsg_SpeakingErrorOccurred(utterance->src_id(), error_message));
break;
case TTS_EVENT_PAUSE:
Send(new TtsMsg_DidPauseSpeaking(utterance->src_id()));
break;
case TTS_EVENT_RESUME:
Send(new TtsMsg_DidResumeSpeaking(utterance->src_id()));
break;
}
}
void TtsMessageFilter::OnVoicesChanged() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
OnInitializeVoiceList();
}
void TtsMessageFilter::OnChannelClosingInUIThread() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TtsController::GetInstance()->RemoveVoicesChangedDelegate(this);
weak_ptr_factory_.InvalidateWeakPtrs();
Release(); // Balanced in TtsMessageFilter().
}
TtsMessageFilter::~TtsMessageFilter() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!weak_ptr_factory_.HasWeakPtrs());
TtsController::GetInstance()->RemoveVoicesChangedDelegate(this);
}

View File

@@ -1,62 +0,0 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_SPEECH_TTS_MESSAGE_FILTER_H_
#define CHROME_BROWSER_SPEECH_TTS_MESSAGE_FILTER_H_
#include "base/memory/weak_ptr.h"
#include "chrome/browser/speech/tts_controller.h"
#include "chrome/common/tts_messages.h"
#include "content/public/browser/browser_message_filter.h"
namespace content {
class BrowserContext;
}
class TtsMessageFilter : public content::BrowserMessageFilter,
public UtteranceEventDelegate,
public VoicesChangedDelegate {
public:
explicit TtsMessageFilter(int render_process_id,
content::BrowserContext* browser_context);
// content::BrowserMessageFilter implementation.
void OverrideThreadForMessage(const IPC::Message& message,
content::BrowserThread::ID* thread) override;
bool OnMessageReceived(const IPC::Message& message) override;
void OnChannelClosing() override;
void OnDestruct() const override;
// UtteranceEventDelegate implementation.
void OnTtsEvent(Utterance* utterance,
TtsEventType event_type,
int char_index,
const std::string& error_message) override;
// VoicesChangedDelegate implementation.
void OnVoicesChanged() override;
private:
friend class content::BrowserThread;
friend class base::DeleteHelper<TtsMessageFilter>;
~TtsMessageFilter() override;
void OnInitializeVoiceList();
void OnSpeak(const TtsUtteranceRequest& utterance);
void OnPause();
void OnResume();
void OnCancel();
void OnChannelClosingInUIThread();
int render_process_id_;
content::BrowserContext* browser_context_;
base::WeakPtrFactory<TtsMessageFilter> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(TtsMessageFilter);
};
#endif // CHROME_BROWSER_SPEECH_TTS_MESSAGE_FILTER_H_

View File

@@ -1,28 +0,0 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/speech/tts_platform.h"
#include <string>
bool TtsPlatformImpl::LoadBuiltInTtsExtension(
content::BrowserContext* browser_context) {
return false;
}
std::string TtsPlatformImpl::error() {
return error_;
}
void TtsPlatformImpl::clear_error() {
error_ = std::string();
}
void TtsPlatformImpl::set_error(const std::string& error) {
error_ = error;
}
void TtsPlatformImpl::WillSpeakUtteranceWithVoice(const Utterance* utterance,
const VoiceData& voice_data) {
}

View File

@@ -1,80 +0,0 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_SPEECH_TTS_PLATFORM_H_
#define CHROME_BROWSER_SPEECH_TTS_PLATFORM_H_
#include <string>
#include "chrome/browser/speech/tts_controller.h"
// Abstract class that defines the native platform TTS interface,
// subclassed by specific implementations on Win, Mac, etc.
class TtsPlatformImpl {
public:
static TtsPlatformImpl* GetInstance();
// Returns true if this platform implementation is supported and available.
virtual bool PlatformImplAvailable() = 0;
// Some platforms may provide a built-in TTS extension. Returns true
// if the extension was not previously loaded and is now loading, and
// false if it's already loaded or if there's no extension to load.
// Will call TtsController::RetrySpeakingQueuedUtterances when
// the extension finishes loading.
virtual bool LoadBuiltInTtsExtension(
content::BrowserContext* browser_context);
// Speak the given utterance with the given parameters if possible,
// and return true on success. Utterance will always be nonempty.
// If rate, pitch, or volume are -1.0, they will be ignored.
//
// The TtsController will only try to speak one utterance at
// a time. If it wants to interrupt speech, it will always call Stop
// before speaking again.
virtual bool Speak(int utterance_id,
const std::string& utterance,
const std::string& lang,
const VoiceData& voice,
const UtteranceContinuousParameters& params) = 0;
// Stop speaking immediately and return true on success.
virtual bool StopSpeaking() = 0;
// Returns whether any speech is on going.
virtual bool IsSpeaking() = 0;
// Append information about voices provided by this platform implementation
// to |out_voices|.
virtual void GetVoices(std::vector<VoiceData>* out_voices) = 0;
// Pause the current utterance, if any, until a call to Resume,
// Speak, or StopSpeaking.
virtual void Pause() = 0;
// Resume speaking the current utterance, if it was paused.
virtual void Resume() = 0;
// Allows the platform to monitor speech commands and the voices used
// for each one.
virtual void WillSpeakUtteranceWithVoice(const Utterance* utterance,
const VoiceData& voice_data);
virtual std::string error();
virtual void clear_error();
virtual void set_error(const std::string& error);
protected:
TtsPlatformImpl() {}
// On some platforms this may be a leaky singleton - do not rely on the
// destructor being called! http://crbug.com/122026
virtual ~TtsPlatformImpl() {}
std::string error_;
DISALLOW_COPY_AND_ASSIGN(TtsPlatformImpl);
};
#endif // CHROME_BROWSER_SPEECH_TTS_PLATFORM_H_

View File

@@ -1,311 +0,0 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <math.h>
#include <objbase.h>
#include <sapi.h>
#include <sphelper.h>
#include <wrl/client.h>
#include "base/memory/singleton.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "base/win/scoped_co_mem.h"
#include "chrome/browser/speech/tts_controller.h"
#include "chrome/browser/speech/tts_platform.h"
namespace {
// ISpObjectToken key and value names.
const wchar_t kAttributesKey[] = L"Attributes";
const wchar_t kGenderValue[] = L"Gender";
const wchar_t kLanguageValue[] = L"Language";
} // anonymous namespace.
class TtsPlatformImplWin : public TtsPlatformImpl {
public:
bool PlatformImplAvailable() override { return true; }
bool Speak(int utterance_id,
const std::string& utterance,
const std::string& lang,
const VoiceData& voice,
const UtteranceContinuousParameters& params) override;
bool StopSpeaking() override;
void Pause() override;
void Resume() override;
bool IsSpeaking() override;
void GetVoices(std::vector<VoiceData>* out_voices) override;
// Get the single instance of this class.
static TtsPlatformImplWin* GetInstance();
static void __stdcall SpeechEventCallback(WPARAM w_param, LPARAM l_param);
private:
TtsPlatformImplWin();
~TtsPlatformImplWin() override {}
void OnSpeechEvent();
void SetVoiceFromName(const std::string& name);
Microsoft::WRL::ComPtr<ISpVoice> speech_synthesizer_;
// These apply to the current utterance only.
std::wstring utterance_;
int utterance_id_;
int prefix_len_;
ULONG stream_number_;
int char_position_;
bool paused_;
std::string last_voice_name_;
friend struct base::DefaultSingletonTraits<TtsPlatformImplWin>;
DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplWin);
};
// static
TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
return TtsPlatformImplWin::GetInstance();
}
bool TtsPlatformImplWin::Speak(int utterance_id,
const std::string& src_utterance,
const std::string& lang,
const VoiceData& voice,
const UtteranceContinuousParameters& params) {
std::wstring prefix;
std::wstring suffix;
if (!speech_synthesizer_.Get())
return false;
SetVoiceFromName(voice.name);
if (params.rate >= 0.0) {
// Map our multiplicative range of 0.1x to 10.0x onto Microsoft's
// linear range of -10 to 10:
// 0.1 -> -10
// 1.0 -> 0
// 10.0 -> 10
speech_synthesizer_->SetRate(static_cast<int32_t>(10 * log10(params.rate)));
}
if (params.pitch >= 0.0) {
// The TTS api allows a range of -10 to 10 for speech pitch.
// TODO(dtseng): cleanup if we ever use any other properties that
// require xml.
std::wstring pitch_value =
base::IntToString16(static_cast<int>(params.pitch * 10 - 10));
prefix = L"<pitch absmiddle=\"" + pitch_value + L"\">";
suffix = L"</pitch>";
}
if (params.volume >= 0.0) {
// The TTS api allows a range of 0 to 100 for speech volume.
speech_synthesizer_->SetVolume(static_cast<uint16_t>(params.volume * 100));
}
// TODO(dmazzoni): convert SSML to SAPI xml. http://crbug.com/88072
utterance_ = base::UTF8ToWide(src_utterance);
utterance_id_ = utterance_id;
char_position_ = 0;
std::wstring merged_utterance = prefix + utterance_ + suffix;
prefix_len_ = prefix.size();
HRESULT result = speech_synthesizer_->Speak(merged_utterance.c_str(),
SPF_ASYNC, &stream_number_);
return (result == S_OK);
}
bool TtsPlatformImplWin::StopSpeaking() {
if (speech_synthesizer_.Get()) {
// Clear the stream number so that any further events relating to this
// utterance are ignored.
stream_number_ = 0;
if (IsSpeaking()) {
// Stop speech by speaking the empty string with the purge flag.
speech_synthesizer_->Speak(L"", SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL);
}
if (paused_) {
speech_synthesizer_->Resume();
paused_ = false;
}
}
return true;
}
void TtsPlatformImplWin::Pause() {
if (speech_synthesizer_.Get() && utterance_id_ && !paused_) {
speech_synthesizer_->Pause();
paused_ = true;
TtsController::GetInstance()->OnTtsEvent(utterance_id_, TTS_EVENT_PAUSE,
char_position_, "");
}
}
void TtsPlatformImplWin::Resume() {
if (speech_synthesizer_.Get() && utterance_id_ && paused_) {
speech_synthesizer_->Resume();
paused_ = false;
TtsController::GetInstance()->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME,
char_position_, "");
}
}
bool TtsPlatformImplWin::IsSpeaking() {
if (speech_synthesizer_.Get()) {
SPVOICESTATUS status;
HRESULT result = speech_synthesizer_->GetStatus(&status, NULL);
if (result == S_OK) {
if (status.dwRunningState == 0 || // 0 == waiting to speak
status.dwRunningState == SPRS_IS_SPEAKING) {
return true;
}
}
}
return false;
}
void TtsPlatformImplWin::GetVoices(std::vector<VoiceData>* out_voices) {
Microsoft::WRL::ComPtr<IEnumSpObjectTokens> voice_tokens;
unsigned long voice_count;
if (S_OK !=
SpEnumTokens(SPCAT_VOICES, NULL, NULL, voice_tokens.GetAddressOf()))
return;
if (S_OK != voice_tokens->GetCount(&voice_count))
return;
for (unsigned i = 0; i < voice_count; i++) {
VoiceData voice;
Microsoft::WRL::ComPtr<ISpObjectToken> voice_token;
if (S_OK != voice_tokens->Next(1, voice_token.GetAddressOf(), NULL))
return;
base::win::ScopedCoMem<WCHAR> description;
if (S_OK != SpGetDescription(voice_token.Get(), &description))
continue;
voice.name = base::WideToUTF8(description.get());
Microsoft::WRL::ComPtr<ISpDataKey> attributes;
if (S_OK != voice_token->OpenKey(kAttributesKey, attributes.GetAddressOf()))
continue;
base::win::ScopedCoMem<WCHAR> gender;
if (S_OK == attributes->GetStringValue(kGenderValue, &gender)) {
if (0 == _wcsicmp(gender.get(), L"male"))
voice.gender = TTS_GENDER_MALE;
else if (0 == _wcsicmp(gender.get(), L"female"))
voice.gender = TTS_GENDER_FEMALE;
}
base::win::ScopedCoMem<WCHAR> language;
if (S_OK == attributes->GetStringValue(kLanguageValue, &language)) {
int lcid_value;
base::HexStringToInt(base::WideToUTF8(language.get()), &lcid_value);
LCID lcid = MAKELCID(lcid_value, SORT_DEFAULT);
WCHAR locale_name[LOCALE_NAME_MAX_LENGTH] = {0};
LCIDToLocaleName(lcid, locale_name, LOCALE_NAME_MAX_LENGTH, 0);
voice.lang = base::WideToUTF8(locale_name);
}
voice.native = true;
voice.events.insert(TTS_EVENT_START);
voice.events.insert(TTS_EVENT_END);
voice.events.insert(TTS_EVENT_MARKER);
voice.events.insert(TTS_EVENT_WORD);
voice.events.insert(TTS_EVENT_SENTENCE);
voice.events.insert(TTS_EVENT_PAUSE);
voice.events.insert(TTS_EVENT_RESUME);
out_voices->push_back(voice);
}
}
void TtsPlatformImplWin::OnSpeechEvent() {
TtsController* controller = TtsController::GetInstance();
SPEVENT event;
while (S_OK == speech_synthesizer_->GetEvents(1, &event, NULL)) {
if (event.ulStreamNum != stream_number_)
continue;
switch (event.eEventId) {
case SPEI_START_INPUT_STREAM:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0,
std::string());
break;
case SPEI_END_INPUT_STREAM:
char_position_ = utterance_.size();
controller->OnTtsEvent(utterance_id_, TTS_EVENT_END, char_position_,
std::string());
break;
case SPEI_TTS_BOOKMARK:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_MARKER, char_position_,
std::string());
break;
case SPEI_WORD_BOUNDARY:
char_position_ = static_cast<ULONG>(event.lParam) - prefix_len_;
controller->OnTtsEvent(utterance_id_, TTS_EVENT_WORD, char_position_,
std::string());
break;
case SPEI_SENTENCE_BOUNDARY:
char_position_ = static_cast<ULONG>(event.lParam) - prefix_len_;
controller->OnTtsEvent(utterance_id_, TTS_EVENT_SENTENCE,
char_position_, std::string());
break;
default:
break;
}
}
}
void TtsPlatformImplWin::SetVoiceFromName(const std::string& name) {
if (name.empty() || name == last_voice_name_)
return;
last_voice_name_ = name;
Microsoft::WRL::ComPtr<IEnumSpObjectTokens> voice_tokens;
unsigned long voice_count;
if (S_OK !=
SpEnumTokens(SPCAT_VOICES, NULL, NULL, voice_tokens.GetAddressOf()))
return;
if (S_OK != voice_tokens->GetCount(&voice_count))
return;
for (unsigned i = 0; i < voice_count; i++) {
Microsoft::WRL::ComPtr<ISpObjectToken> voice_token;
if (S_OK != voice_tokens->Next(1, voice_token.GetAddressOf(), NULL))
return;
base::win::ScopedCoMem<WCHAR> description;
if (S_OK != SpGetDescription(voice_token.Get(), &description))
continue;
if (name == base::WideToUTF8(description.get())) {
speech_synthesizer_->SetVoice(voice_token.Get());
break;
}
}
}
TtsPlatformImplWin::TtsPlatformImplWin()
: utterance_id_(0),
prefix_len_(0),
stream_number_(0),
char_position_(0),
paused_(false) {
::CoCreateInstance(CLSID_SpVoice, nullptr, CLSCTX_ALL,
IID_PPV_ARGS(&speech_synthesizer_));
if (speech_synthesizer_.Get()) {
ULONGLONG event_mask =
SPFEI(SPEI_START_INPUT_STREAM) | SPFEI(SPEI_TTS_BOOKMARK) |
SPFEI(SPEI_WORD_BOUNDARY) | SPFEI(SPEI_SENTENCE_BOUNDARY) |
SPFEI(SPEI_END_INPUT_STREAM);
speech_synthesizer_->SetInterest(event_mask, event_mask);
speech_synthesizer_->SetNotifyCallbackFunction(
TtsPlatformImplWin::SpeechEventCallback, 0, 0);
}
}
// static
TtsPlatformImplWin* TtsPlatformImplWin::GetInstance() {
return base::Singleton<TtsPlatformImplWin,
base::LeakySingletonTraits<TtsPlatformImplWin>>::get();
}
// static
void TtsPlatformImplWin::SpeechEventCallback(WPARAM w_param, LPARAM l_param) {
GetInstance()->OnSpeechEvent();
}

View File

@@ -1,61 +0,0 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Multiply-included message file, hence no include guard.
#include <vector>
#include "chrome/common/tts_utterance_request.h"
#include "ipc/ipc_message_macros.h"
#include "ipc/ipc_param_traits.h"
#define IPC_MESSAGE_START TtsMsgStart
IPC_STRUCT_TRAITS_BEGIN(TtsUtteranceRequest)
IPC_STRUCT_TRAITS_MEMBER(id)
IPC_STRUCT_TRAITS_MEMBER(text)
IPC_STRUCT_TRAITS_MEMBER(lang)
IPC_STRUCT_TRAITS_MEMBER(voice)
IPC_STRUCT_TRAITS_MEMBER(volume)
IPC_STRUCT_TRAITS_MEMBER(rate)
IPC_STRUCT_TRAITS_MEMBER(pitch)
IPC_STRUCT_TRAITS_END()
IPC_STRUCT_TRAITS_BEGIN(TtsVoice)
IPC_STRUCT_TRAITS_MEMBER(voice_uri)
IPC_STRUCT_TRAITS_MEMBER(name)
IPC_STRUCT_TRAITS_MEMBER(lang)
IPC_STRUCT_TRAITS_MEMBER(local_service)
IPC_STRUCT_TRAITS_MEMBER(is_default)
IPC_STRUCT_TRAITS_END()
// Renderer -> Browser messages.
IPC_MESSAGE_CONTROL0(TtsHostMsg_InitializeVoiceList)
IPC_MESSAGE_CONTROL1(TtsHostMsg_Speak, TtsUtteranceRequest)
IPC_MESSAGE_CONTROL0(TtsHostMsg_Pause)
IPC_MESSAGE_CONTROL0(TtsHostMsg_Resume)
IPC_MESSAGE_CONTROL0(TtsHostMsg_Cancel)
// Browser -> Renderer messages.
IPC_MESSAGE_CONTROL1(TtsMsg_SetVoiceList, std::vector<TtsVoice>)
IPC_MESSAGE_CONTROL1(TtsMsg_DidStartSpeaking, int /* utterance id */)
IPC_MESSAGE_CONTROL1(TtsMsg_DidFinishSpeaking, int /* utterance id */)
IPC_MESSAGE_CONTROL1(TtsMsg_DidPauseSpeaking, int /* utterance id */)
IPC_MESSAGE_CONTROL1(TtsMsg_DidResumeSpeaking, int /* utterance id */)
IPC_MESSAGE_CONTROL2(TtsMsg_WordBoundary,
int /* utterance id */,
int /* char index */)
IPC_MESSAGE_CONTROL2(TtsMsg_SentenceBoundary,
int /* utterance id */,
int /* char index */)
IPC_MESSAGE_CONTROL2(TtsMsg_MarkerEvent,
int /* utterance id */,
int /* char index */)
IPC_MESSAGE_CONTROL1(TtsMsg_WasInterrupted, int /* utterance id */)
IPC_MESSAGE_CONTROL1(TtsMsg_WasCancelled, int /* utterance id */)
IPC_MESSAGE_CONTROL2(TtsMsg_SpeakingErrorOccurred,
int /* utterance id */,
std::string /* error message */)

View File

@@ -1,20 +0,0 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/common/tts_utterance_request.h"
TtsUtteranceRequest::TtsUtteranceRequest()
: id(0), volume(1.0), rate(1.0), pitch(1.0) {}
TtsUtteranceRequest::~TtsUtteranceRequest() {}
TtsVoice::TtsVoice() : local_service(true), is_default(false) {}
TtsVoice::TtsVoice(const TtsVoice&) = default;
TtsVoice::~TtsVoice() {}
TtsUtteranceResponse::TtsUtteranceResponse() : id(0) {}
TtsUtteranceResponse::~TtsUtteranceResponse() {}

View File

@@ -1,45 +0,0 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_COMMON_TTS_UTTERANCE_REQUEST_H_
#define CHROME_COMMON_TTS_UTTERANCE_REQUEST_H_
#include <vector>
#include "base/macros.h"
#include "base/strings/string16.h"
struct TtsUtteranceRequest {
TtsUtteranceRequest();
~TtsUtteranceRequest();
int id;
std::string text;
std::string lang;
std::string voice;
float volume;
float rate;
float pitch;
};
struct TtsVoice {
TtsVoice();
TtsVoice(const TtsVoice&);
~TtsVoice();
std::string voice_uri;
std::string name;
std::string lang;
bool local_service;
bool is_default;
};
struct TtsUtteranceResponse {
TtsUtteranceResponse();
~TtsUtteranceResponse();
int id;
};
#endif // CHROME_COMMON_TTS_UTTERANCE_REQUEST_H_

View File

@@ -1,198 +0,0 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/renderer/tts_dispatcher.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/common/tts_messages.h"
#include "chrome/common/tts_utterance_request.h"
#include "content/public/renderer/render_thread.h"
#include "third_party/blink/public/platform/web_speech_synthesis_utterance.h"
#include "third_party/blink/public/platform/web_speech_synthesis_voice.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_vector.h"
using blink::WebSpeechSynthesisUtterance;
using blink::WebSpeechSynthesisVoice;
using blink::WebSpeechSynthesizerClient;
using blink::WebString;
using blink::WebVector;
using content::RenderThread;
int TtsDispatcher::next_utterance_id_ = 1;
TtsDispatcher::TtsDispatcher(WebSpeechSynthesizerClient* client)
: synthesizer_client_(client) {
RenderThread::Get()->AddObserver(this);
}
TtsDispatcher::~TtsDispatcher() {
RenderThread::Get()->RemoveObserver(this);
}
bool TtsDispatcher::OnControlMessageReceived(const IPC::Message& message) {
IPC_BEGIN_MESSAGE_MAP(TtsDispatcher, message)
IPC_MESSAGE_HANDLER(TtsMsg_SetVoiceList, OnSetVoiceList)
IPC_MESSAGE_HANDLER(TtsMsg_DidStartSpeaking, OnDidStartSpeaking)
IPC_MESSAGE_HANDLER(TtsMsg_DidFinishSpeaking, OnDidFinishSpeaking)
IPC_MESSAGE_HANDLER(TtsMsg_DidPauseSpeaking, OnDidPauseSpeaking)
IPC_MESSAGE_HANDLER(TtsMsg_DidResumeSpeaking, OnDidResumeSpeaking)
IPC_MESSAGE_HANDLER(TtsMsg_WordBoundary, OnWordBoundary)
IPC_MESSAGE_HANDLER(TtsMsg_SentenceBoundary, OnSentenceBoundary)
IPC_MESSAGE_HANDLER(TtsMsg_MarkerEvent, OnMarkerEvent)
IPC_MESSAGE_HANDLER(TtsMsg_WasInterrupted, OnWasInterrupted)
IPC_MESSAGE_HANDLER(TtsMsg_WasCancelled, OnWasCancelled)
IPC_MESSAGE_HANDLER(TtsMsg_SpeakingErrorOccurred, OnSpeakingErrorOccurred)
IPC_END_MESSAGE_MAP()
// Always return false because there may be multiple TtsDispatchers
// and we want them all to have a chance to handle this message.
return false;
}
void TtsDispatcher::UpdateVoiceList() {
RenderThread::Get()->Send(new TtsHostMsg_InitializeVoiceList());
}
void TtsDispatcher::Speak(const WebSpeechSynthesisUtterance& web_utterance) {
int id = next_utterance_id_++;
utterance_id_map_[id] = web_utterance;
TtsUtteranceRequest utterance;
utterance.id = id;
utterance.text = web_utterance.GetText().Utf8();
utterance.lang = web_utterance.Lang().Utf8();
utterance.voice = web_utterance.Voice().Utf8();
utterance.volume = web_utterance.Volume();
utterance.rate = web_utterance.Rate();
utterance.pitch = web_utterance.Pitch();
RenderThread::Get()->Send(new TtsHostMsg_Speak(utterance));
}
void TtsDispatcher::Pause() {
RenderThread::Get()->Send(new TtsHostMsg_Pause());
}
void TtsDispatcher::Resume() {
RenderThread::Get()->Send(new TtsHostMsg_Resume());
}
void TtsDispatcher::Cancel() {
RenderThread::Get()->Send(new TtsHostMsg_Cancel());
}
WebSpeechSynthesisUtterance TtsDispatcher::FindUtterance(int utterance_id) {
base::hash_map<int, WebSpeechSynthesisUtterance>::const_iterator iter =
utterance_id_map_.find(utterance_id);
if (iter == utterance_id_map_.end())
return WebSpeechSynthesisUtterance();
return iter->second;
}
void TtsDispatcher::OnSetVoiceList(const std::vector<TtsVoice>& voices) {
WebVector<WebSpeechSynthesisVoice> out_voices(voices.size());
for (size_t i = 0; i < voices.size(); ++i) {
out_voices[i] = WebSpeechSynthesisVoice();
out_voices[i].SetVoiceURI(WebString::FromUTF8(voices[i].voice_uri));
out_voices[i].SetName(WebString::FromUTF8(voices[i].name));
out_voices[i].SetLanguage(WebString::FromUTF8(voices[i].lang));
out_voices[i].SetIsLocalService(voices[i].local_service);
out_voices[i].SetIsDefault(voices[i].is_default);
}
synthesizer_client_->SetVoiceList(out_voices);
}
void TtsDispatcher::OnDidStartSpeaking(int utterance_id) {
if (utterance_id_map_.find(utterance_id) == utterance_id_map_.end())
return;
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
if (utterance.IsNull())
return;
synthesizer_client_->DidStartSpeaking(utterance);
}
void TtsDispatcher::OnDidFinishSpeaking(int utterance_id) {
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
if (utterance.IsNull())
return;
synthesizer_client_->DidFinishSpeaking(utterance);
utterance_id_map_.erase(utterance_id);
}
void TtsDispatcher::OnDidPauseSpeaking(int utterance_id) {
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
if (utterance.IsNull())
return;
synthesizer_client_->DidPauseSpeaking(utterance);
}
void TtsDispatcher::OnDidResumeSpeaking(int utterance_id) {
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
if (utterance.IsNull())
return;
synthesizer_client_->DidResumeSpeaking(utterance);
}
void TtsDispatcher::OnWordBoundary(int utterance_id, int char_index) {
CHECK(char_index >= 0);
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
if (utterance.IsNull())
return;
synthesizer_client_->WordBoundaryEventOccurred(
utterance, static_cast<unsigned>(char_index));
}
void TtsDispatcher::OnSentenceBoundary(int utterance_id, int char_index) {
CHECK(char_index >= 0);
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
if (utterance.IsNull())
return;
synthesizer_client_->SentenceBoundaryEventOccurred(
utterance, static_cast<unsigned>(char_index));
}
void TtsDispatcher::OnMarkerEvent(int utterance_id, int char_index) {
// Not supported yet.
}
void TtsDispatcher::OnWasInterrupted(int utterance_id) {
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
if (utterance.IsNull())
return;
// The web speech API doesn't support "interrupted".
synthesizer_client_->DidFinishSpeaking(utterance);
utterance_id_map_.erase(utterance_id);
}
void TtsDispatcher::OnWasCancelled(int utterance_id) {
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
if (utterance.IsNull())
return;
// The web speech API doesn't support "cancelled".
synthesizer_client_->DidFinishSpeaking(utterance);
utterance_id_map_.erase(utterance_id);
}
void TtsDispatcher::OnSpeakingErrorOccurred(int utterance_id,
const std::string& error_message) {
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
if (utterance.IsNull())
return;
// The web speech API doesn't support an error message.
synthesizer_client_->SpeakingErrorOccurred(utterance);
utterance_id_map_.erase(utterance_id);
}

View File

@@ -1,73 +0,0 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_RENDERER_TTS_DISPATCHER_H_
#define CHROME_RENDERER_TTS_DISPATCHER_H_
#include <vector>
#include "base/containers/hash_tables.h"
#include "content/public/renderer/render_thread_observer.h"
#include "third_party/blink/public/platform/web_speech_synthesizer.h"
#include "third_party/blink/public/platform/web_speech_synthesizer_client.h"
namespace IPC {
class Message;
}
struct TtsVoice;
// TtsDispatcher is a delegate for methods used by Blink for speech synthesis
// APIs. It's the complement of TtsDispatcherHost (owned by RenderViewHost).
// Each TtsDispatcher is owned by the WebSpeechSynthesizerClient in Blink;
// it registers itself to listen to IPC upon construction and unregisters
// itself when deleted. There can be multiple TtsDispatchers alive at once,
// so each one routes IPC messages to its WebSpeechSynthesizerClient only if
// the utterance id (which is globally unique) matches.
class TtsDispatcher : public blink::WebSpeechSynthesizer,
public content::RenderThreadObserver {
public:
explicit TtsDispatcher(blink::WebSpeechSynthesizerClient* client);
~TtsDispatcher() override;
private:
// RenderProcessObserver override.
bool OnControlMessageReceived(const IPC::Message& message) override;
// blink::WebSpeechSynthesizer implementation.
void UpdateVoiceList() override;
void Speak(const blink::WebSpeechSynthesisUtterance& utterance) override;
void Pause() override;
void Resume() override;
void Cancel() override;
blink::WebSpeechSynthesisUtterance FindUtterance(int utterance_id);
void OnSetVoiceList(const std::vector<TtsVoice>& voices);
void OnDidStartSpeaking(int utterance_id);
void OnDidFinishSpeaking(int utterance_id);
void OnDidPauseSpeaking(int utterance_id);
void OnDidResumeSpeaking(int utterance_id);
void OnWordBoundary(int utterance_id, int char_index);
void OnSentenceBoundary(int utterance_id, int char_index);
void OnMarkerEvent(int utterance_id, int char_index);
void OnWasInterrupted(int utterance_id);
void OnWasCancelled(int utterance_id);
void OnSpeakingErrorOccurred(int utterance_id,
const std::string& error_message);
// The WebKit client class that we use to send events back to the JS world.
// Weak reference, this will be valid as long as this object exists.
blink::WebSpeechSynthesizerClient* synthesizer_client_;
// Next utterance id, used to map response IPCs to utterance objects.
static int next_utterance_id_;
// Map from id to utterance objects.
base::hash_map<int, blink::WebSpeechSynthesisUtterance> utterance_id_map_;
DISALLOW_COPY_AND_ASSIGN(TtsDispatcher);
};
#endif // CHROME_RENDERER_TTS_DISPATCHER_H_

View File

@@ -1,52 +0,0 @@
// This is generated file. Do not modify directly.
// Path to the code generator:
// tools/generate_library_loader/generate_library_loader.py .
#ifndef LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H
#define LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H
#include "third_party/speech-dispatcher/libspeechd.h"
#define LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN
#include <string>
class LibSpeechdLoader {
public:
LibSpeechdLoader();
~LibSpeechdLoader();
bool Load(const std::string& library_name)
__attribute__((warn_unused_result));
bool loaded() const { return loaded_; }
decltype(&::spd_open) spd_open;
decltype(&::spd_say) spd_say;
decltype(&::spd_stop) spd_stop;
decltype(&::spd_close) spd_close;
decltype(&::spd_pause) spd_pause;
decltype(&::spd_resume) spd_resume;
decltype(&::spd_set_notification_on) spd_set_notification_on;
decltype(&::spd_set_voice_rate) spd_set_voice_rate;
decltype(&::spd_set_voice_pitch) spd_set_voice_pitch;
decltype(&::spd_list_synthesis_voices) spd_list_synthesis_voices;
decltype(&::spd_set_synthesis_voice) spd_set_synthesis_voice;
decltype(&::spd_list_modules) spd_list_modules;
decltype(&::spd_set_output_module) spd_set_output_module;
decltype(&::spd_set_language) spd_set_language;
private:
void CleanUp(bool unload);
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
void* library_;
#endif
bool loaded_;
// Disallow copy constructor and assignment operator.
LibSpeechdLoader(const LibSpeechdLoader&);
void operator=(const LibSpeechdLoader&);
};
#endif // LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H

View File

@@ -1,252 +0,0 @@
// This is generated file. Do not modify directly.
// Path to the code generator:
// tools/generate_library_loader/generate_library_loader.py .
#include "library_loaders/libspeechd.h"
#include <dlfcn.h>
// Put these sanity checks here so that they fire at most once
// (to avoid cluttering the build output).
#if !defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) && \
!defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
#error neither LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN nor LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED defined
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) && \
defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
#error both LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN and LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED defined
#endif
LibSpeechdLoader::LibSpeechdLoader() : loaded_(false) {}
LibSpeechdLoader::~LibSpeechdLoader() {
CleanUp(loaded_);
}
bool LibSpeechdLoader::Load(const std::string& library_name) {
if (loaded_)
return false;
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
library_ = dlopen(library_name.c_str(), RTLD_LAZY);
if (!library_)
return false;
#endif
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_open =
reinterpret_cast<decltype(this->spd_open)>(dlsym(library_, "spd_open"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_open = &::spd_open;
#endif
if (!spd_open) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_say =
reinterpret_cast<decltype(this->spd_say)>(dlsym(library_, "spd_say"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_say = &::spd_say;
#endif
if (!spd_say) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_stop =
reinterpret_cast<decltype(this->spd_stop)>(dlsym(library_, "spd_stop"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_stop = &::spd_stop;
#endif
if (!spd_stop) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_close =
reinterpret_cast<decltype(this->spd_close)>(dlsym(library_, "spd_close"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_close = &::spd_close;
#endif
if (!spd_close) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_pause =
reinterpret_cast<decltype(this->spd_pause)>(dlsym(library_, "spd_pause"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_pause = &::spd_pause;
#endif
if (!spd_pause) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_resume = reinterpret_cast<decltype(this->spd_resume)>(
dlsym(library_, "spd_resume"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_resume = &::spd_resume;
#endif
if (!spd_resume) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_set_notification_on =
reinterpret_cast<decltype(this->spd_set_notification_on)>(
dlsym(library_, "spd_set_notification_on"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_set_notification_on = &::spd_set_notification_on;
#endif
if (!spd_set_notification_on) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_set_voice_rate = reinterpret_cast<decltype(this->spd_set_voice_rate)>(
dlsym(library_, "spd_set_voice_rate"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_set_voice_rate = &::spd_set_voice_rate;
#endif
if (!spd_set_voice_rate) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_set_voice_pitch = reinterpret_cast<decltype(this->spd_set_voice_pitch)>(
dlsym(library_, "spd_set_voice_pitch"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_set_voice_pitch = &::spd_set_voice_pitch;
#endif
if (!spd_set_voice_pitch) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_list_synthesis_voices =
reinterpret_cast<decltype(this->spd_list_synthesis_voices)>(
dlsym(library_, "spd_list_synthesis_voices"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_list_synthesis_voices = &::spd_list_synthesis_voices;
#endif
if (!spd_list_synthesis_voices) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_set_synthesis_voice =
reinterpret_cast<decltype(this->spd_set_synthesis_voice)>(
dlsym(library_, "spd_set_synthesis_voice"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_set_synthesis_voice = &::spd_set_synthesis_voice;
#endif
if (!spd_set_synthesis_voice) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_list_modules = reinterpret_cast<decltype(this->spd_list_modules)>(
dlsym(library_, "spd_list_modules"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_list_modules = &::spd_list_modules;
#endif
if (!spd_list_modules) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_set_output_module =
reinterpret_cast<decltype(this->spd_set_output_module)>(
dlsym(library_, "spd_set_output_module"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_set_output_module = &::spd_set_output_module;
#endif
if (!spd_set_output_module) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_set_language = reinterpret_cast<decltype(this->spd_set_language)>(
dlsym(library_, "spd_set_language"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_set_language = &::spd_set_language;
#endif
if (!spd_set_language) {
CleanUp(true);
return false;
}
loaded_ = true;
return true;
}
void LibSpeechdLoader::CleanUp(bool unload) {
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
if (unload) {
dlclose(library_);
library_ = NULL;
}
#endif
loaded_ = false;
spd_open = NULL;
spd_say = NULL;
spd_stop = NULL;
spd_close = NULL;
spd_pause = NULL;
spd_resume = NULL;
spd_set_notification_on = NULL;
spd_set_voice_rate = NULL;
spd_set_voice_pitch = NULL;
spd_list_synthesis_voices = NULL;
spd_set_synthesis_voice = NULL;
spd_list_modules = NULL;
spd_set_output_module = NULL;
spd_set_language = NULL;
}

View File

@@ -422,6 +422,52 @@ Emitted when `remote.getGlobal()` is called in the renderer process of `webConte
Calling `event.preventDefault()` will prevent the global from being returned.
Custom value can be returned by setting `event.returnValue`.
### Event: 'remote-get-builtin'
Returns:
* `event` Event
* `webContents` [WebContents](web-contents.md)
* `moduleName` String
Emitted when `remote.getBuiltin()` is called in the renderer process of `webContents`.
Calling `event.preventDefault()` will prevent the module from being returned.
Custom value can be returned by setting `event.returnValue`.
### Event: 'remote-get-current-window'
Returns:
* `event` Event
* `webContents` [WebContents](web-contents.md)
Emitted when `remote.getCurrentWindow()` is called in the renderer process of `webContents`.
Calling `event.preventDefault()` will prevent the object from being returned.
Custom value can be returned by setting `event.returnValue`.
### Event: 'remote-get-current-web-contents'
Returns:
* `event` Event
* `webContents` [WebContents](web-contents.md)
Emitted when `remote.getCurrentWebContents()` is called in the renderer process of `webContents`.
Calling `event.preventDefault()` will prevent the object from being returned.
Custom value can be returned by setting `event.returnValue`.
### Event: 'remote-get-guest-web-contents'
Returns:
* `event` Event
* `webContents` [WebContents](web-contents.md)
* `guestWebContents` [WebContents](web-contents.md)
Emitted when `<webview>.getWebContents()` is called in the renderer process of `webContents`.
Calling `event.preventDefault()` will prevent the object from being returned.
Custom value can be returned by setting `event.returnValue`.
## Methods
The `app` object has the following methods:
@@ -1215,6 +1261,15 @@ Sets the `image` associated with this dock icon.
## Properties
### `app.userAgentFallback`
A `String` which is the user agent string Electron will use as a global fallback.
This is the user agent that will be used when no user agent is set at the
`webContents` or `session` level. Useful for ensuring your entire
app has the same user agent. Set to a custom value as early as possible
in your apps initialization to ensure that your overridden value is used.
### `app.isPackaged`
A `Boolean` property that returns `true` if the app is packaged, `false` otherwise. For many apps, this property can be used to distinguish development and production environments.

View File

@@ -79,7 +79,7 @@ with `callback(error, cookies)` on complete.
* `url` String - The url to associate the cookie with.
* `name` String (optional) - The name of the cookie. Empty by default if omitted.
* `value` String (optional) - The value of the cookie. Empty by default if omitted.
* `domain` String (optional) - The domain of the cookie. Empty by default if omitted.
* `domain` String (optional) - The domain of the cookie; this will be normalized with a preceding dot so that it's also valid for subdomains. Empty by default if omitted.
* `path` String (optional) - The path of the cookie. Empty by default if omitted.
* `secure` Boolean (optional) - Whether the cookie should be marked as Secure. Defaults to
false.

View File

@@ -95,8 +95,7 @@ them will get reported without `companyName`, `productName` or any of the `extra
Returns [`CrashReport`](structures/crash-report.md):
Returns the date and ID of the last crash report. If no crash reports have been
sent or the crash reporter has not been started, `null` is returned.
Returns the date and ID of the last crash report. Only crash reports that have been uploaded will be returned; even if a crash report is present on disk it will not be returned until it is uploaded. In the case that there are no uploaded reports, `null` is returned.
### `crashReporter.getUploadedReports()`

View File

@@ -92,3 +92,9 @@ objects, each `DesktopCapturerSource` represents a screen or an individual windo
captured.
[`navigator.mediaDevices.getUserMedia`]: https://developer.mozilla.org/en/docs/Web/API/MediaDevices/getUserMedia
### Caveats
`navigator.mediaDevices.getUserMedia` does not work on macOS for audio capture due to a fundamental limitation whereby apps that want to access the system's audio require a [signed kernel extension](https://developer.apple.com/library/archive/documentation/Security/Conceptual/System_Integrity_Protection_Guide/KernelExtensions/KernelExtensions.html). Chromium, and by extension Electron, does not provide this.
It is possible to circumvent this limitation by capturing system audio with another macOS app like Soundflower and passing it through a virtual audio input device. This virtual device can then be queried with `navigator.mediaDevices.getUserMedia`.

View File

@@ -55,7 +55,7 @@ The `dialog` module has the following methods:
* `filePaths` String[] - An array of file paths chosen by the user
* `bookmarks` String[] _macOS_ _mas_ - An array matching the `filePaths` array of base64 encoded strings which contains security scoped bookmark data. `securityScopedBookmarks` must be enabled for this to be populated.
Returns `String[]`, an array of file paths chosen by the user,
Returns `String[] | undefined`, an array of file paths chosen by the user,
if the callback is provided it returns `undefined`.
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
@@ -106,8 +106,8 @@ shown.
* `filename` String
* `bookmark` String _macOS_ _mas_ - Base64 encoded string which contains the security scoped bookmark data for the saved file. `securityScopedBookmarks` must be enabled for this to be present.
Returns `String`, the path of the file chosen by the user,
if a callback is provided it returns `undefined`.
Returns `String | undefined`, the path of the file chosen by the user,
if a callback is provided or the dialog is cancelled it returns `undefined`.
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.

View File

@@ -253,7 +253,7 @@ const { remote } = require('electron')
const { Menu, MenuItem } = remote
const menu = new Menu()
menu.append(new MenuItem({ label: 'MenuItem1', click() { console.log('item 1 clicked') } })))
menu.append(new MenuItem({ label: 'MenuItem1', click() { console.log('item 1 clicked') } }))
menu.append(new MenuItem({ type: 'separator' }))
menu.append(new MenuItem({ label: 'MenuItem2', type: 'checkbox', checked: true }))

View File

@@ -185,6 +185,12 @@ The `hslShift` is applied to the image with the following rules
This means that `[-1, 0, 1]` will make the image completely white and
`[-1, 1, 0]` will make the image completely black.
In some cases, the `NSImageName` doesn't match its string representation; one example of this is `NSFolderImageName`, whose string representation would actually be `NSFolder`. Therefore, you'll need to determine the correct string representation for your image before passing it in. This can be done with the following:
`echo -e '#import <Cocoa/Cocoa.h>\nint main() { NSLog(@"%@", SYSTEM_IMAGE_NAME); }' | clang -otest -x objective-c -framework Cocoa - && ./test`
where `SYSTEM_IMAGE_NAME` should be replaced with any value from [this list](https://developer.apple.com/documentation/appkit/nsimagename?language=objc).
## Class: NativeImage
> Natively wrap images such as tray, dock, and application icons.

View File

@@ -14,6 +14,7 @@ In sandboxed renderers the `process` object contains only a subset of the APIs:
- `crash()`
- `hang()`
- `getHeapStatistics()`
- `getProcessMemoryInfo()`
- `getSystemMemoryInfo()`
- `getCPUUsage()`
- `getIOCounters()`
@@ -157,6 +158,27 @@ Returns `Object`:
Returns an object with V8 heap statistics. Note that all statistics are reported in Kilobytes.
### `process.getProcessMemoryInfo()`
Returns `Object`:
* `residentSet` Integer _Linux_ and _Windows_ - The amount of memory
currently pinned to actual physical RAM in Kilobytes.
* `private` Integer - The amount of memory not shared by other processes, such as
JS heap or HTML content in Kilobytes.
* `shared` Integer - The amount of memory shared between processes, typically
memory consumed by the Electron code itself in Kilobytes.
Returns an object giving memory usage statistics about the current process. Note
that all statistics are reported in Kilobytes.
This api should be called after app ready.
Chromium does not provide `residentSet` value for macOS. This is because macOS
performs in-memory compression of pages that haven't been recently used. As a
result the resident set size value is not what one would expect. `private` memory
is more representative of the actual pre-compression memory usage of the process
on macOS.
### `process.getSystemMemoryInfo()`
Returns `Object`:

View File

@@ -2,8 +2,8 @@
* `name` String - The name of the cookie.
* `value` String - The value of the cookie.
* `domain` String (optional) - The domain of the cookie.
* `hostOnly` Boolean (optional) - Whether the cookie is a host-only cookie.
* `domain` String (optional) - The domain of the cookie; this will be normalized with a preceding dot so that it's also valid for subdomains.
* `hostOnly` Boolean (optional) - Whether the cookie is a host-only cookie; this will only be `true` if no domain was passed.
* `path` String (optional) - The path of the cookie.
* `secure` Boolean (optional) - Whether the cookie is marked as secure.
* `httpOnly` Boolean (optional) - Whether the cookie is marked as HTTP only.

View File

@@ -138,11 +138,16 @@ new [`BrowserWindow`](browser-window.md). If you call `event.preventDefault()` a
instance, failing to do so may result in unexpected behavior. For example:
```javascript
myBrowserWindow.webContents.on('new-window', (event, url) => {
myBrowserWindow.webContents.on('new-window', (event, url, frameName, disposition, options) => {
event.preventDefault()
const win = new BrowserWindow({ show: false })
const win = new BrowserWindow({
webContents: options.webContents, // use existing webContents if provided
show: false
})
win.once('ready-to-show', () => win.show())
win.loadURL(url)
if (!options.webContents) {
win.loadURL(url) // existing webContents will be navigated automatically
}
event.newGuest = win
})
```
@@ -685,6 +690,48 @@ Emitted when `remote.getGlobal()` is called in the renderer process.
Calling `event.preventDefault()` will prevent the global from being returned.
Custom value can be returned by setting `event.returnValue`.
#### Event: 'remote-get-builtin'
Returns:
* `event` Event
* `moduleName` String
Emitted when `remote.getBuiltin()` is called in the renderer process.
Calling `event.preventDefault()` will prevent the module from being returned.
Custom value can be returned by setting `event.returnValue`.
#### Event: 'remote-get-current-window'
Returns:
* `event` Event
Emitted when `remote.getCurrentWindow()` is called in the renderer process.
Calling `event.preventDefault()` will prevent the object from being returned.
Custom value can be returned by setting `event.returnValue`.
#### Event: 'remote-get-current-web-contents'
Returns:
* `event` Event
Emitted when `remote.getCurrentWebContents()` is called in the renderer process.
Calling `event.preventDefault()` will prevent the object from being returned.
Custom value can be returned by setting `event.returnValue`.
#### Event: 'remote-get-guest-web-contents'
Returns:
* `event` Event
* `guestWebContents` [WebContents](web-contents.md)
Emitted when `<webview>.getWebContents()` is called in the renderer process.
Calling `event.preventDefault()` will prevent the object from being returned.
Custom value can be returned by setting `event.returnValue`.
### Instance Methods
#### `contents.loadURL(url[, options])`
@@ -1540,6 +1587,10 @@ Takes a V8 heap snapshot and saves it to `filePath`.
Controls whether or not this WebContents will throttle animations and timers
when the page becomes backgrounded. This also affects the Page Visibility API.
#### `contents.getType()`
Returns `String` - the type of the webContent. Can be `backgroundPage`, `window`, `browserView`, `remote`, `webview` or `offscreen`.
### Instance Properties
#### `contents.id`

View File

@@ -32,7 +32,7 @@ const filter = {
session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
details.requestHeaders['User-Agent'] = 'MyAgent'
callback({ cancel: false, requestHeaders: details.requestHeaders })
callback({ requestHeaders: details.requestHeaders })
})
```
@@ -73,25 +73,24 @@ The `callback` has to be called with an `response` object.
* `urls` String[] - Array of URL patterns that will be used to filter out the
requests that do not match the URL patterns.
* `listener` Function
* `details` Object
* `id` Integer
* `url` String
* `method` String
* `webContentsId` Integer (optional)
* `resourceType` String
* `timestamp` Double
* `requestHeaders` Object
* `callback` Function
* `response` Object
* `cancel` Boolean (optional)
* `requestHeaders` Object (optional) - When provided, request will be made
with these headers.
The `listener` will be called with `listener(details, callback)` before sending
an HTTP request, once the request headers are available. This may occur after a
TCP connection is made to the server, but before any http data is sent.
* `details` Object
* `id` Integer
* `url` String
* `method` String
* `webContentsId` Integer (optional)
* `resourceType` String
* `timestamp` Double
* `requestHeaders` Object
* `callback` Function
* `response` Object
* `cancel` Boolean (optional)
* `requestHeaders` Object (optional) - When provided, request will be made
with these headers.
The `callback` has to be called with an `response` object.
#### `webRequest.onSendHeaders([filter, ]listener)`
@@ -119,29 +118,28 @@ response are visible by the time this listener is fired.
* `urls` String[] - Array of URL patterns that will be used to filter out the
requests that do not match the URL patterns.
* `listener` Function
* `details` Object
* `id` Integer
* `url` String
* `method` String
* `webContentsId` Integer (optional)
* `resourceType` String
* `timestamp` Double
* `statusLine` String
* `statusCode` Integer
* `responseHeaders` Object
* `callback` Function
* `response` Object
* `cancel` Boolean (optional)
* `responseHeaders` Object (optional) - When provided, the server is assumed
to have responded with these headers.
* `statusLine` String (optional) - Should be provided when overriding
`responseHeaders` to change header status otherwise original response
header's status will be used.
The `listener` will be called with `listener(details, callback)` when HTTP
response headers of a request have been received.
* `details` Object
* `id` Integer
* `url` String
* `method` String
* `webContentsId` Integer (optional)
* `resourceType` String
* `timestamp` Double
* `statusLine` String
* `statusCode` Integer
* `responseHeaders` Object
* `callback` Function
* `response` Object
* `cancel` Boolean
* `responseHeaders` Object (optional) - When provided, the server is assumed
to have responded with these headers.
* `statusLine` String (optional) - Should be provided when overriding
`responseHeaders` to change header status otherwise original response
header's status will be used.
The `callback` has to be called with an `response` object.
#### `webRequest.onResponseStarted([filter, ]listener)`
@@ -202,6 +200,7 @@ redirect is about to occur.
* `method` String
* `webContentsId` Integer (optional)
* `resourceType` String
* `referrer` String
* `timestamp` Double
* `responseHeaders` Object
* `fromCache` Boolean

View File

@@ -866,10 +866,6 @@ ipcRenderer.on('ping', () => {
Fired when the renderer process is crashed.
### Event: 'gpu-crashed'
Fired when the gpu process is crashed.
### Event: 'plugin-crashed'
Returns:

View File

@@ -41,6 +41,7 @@ filenames = {
"lib/browser/api/web-contents.js",
"lib/browser/api/web-contents-view.js",
"lib/browser/chrome-extension.js",
"lib/browser/crash-reporter-init.js",
"lib/browser/guest-view-manager.js",
"lib/browser/guest-window-manager.js",
"lib/browser/init.js",
@@ -559,7 +560,6 @@ filenames = {
"atom/renderer/api/atom_api_spell_check_client.cc",
"atom/renderer/api/atom_api_spell_check_client.h",
"atom/renderer/api/atom_api_web_frame.cc",
"atom/renderer/api/atom_api_web_frame.h",
"atom/renderer/atom_autofill_agent.cc",
"atom/renderer/atom_autofill_agent.h",
"atom/renderer/atom_render_frame_observer.cc",
@@ -598,31 +598,14 @@ filenames = {
"chromium_src/chrome/browser/process_singleton_posix.cc",
"chromium_src/chrome/browser/process_singleton_win.cc",
"chromium_src/chrome/browser/process_singleton.h",
"chromium_src/chrome/browser/speech/tts_controller.h",
"chromium_src/chrome/browser/speech/tts_controller_impl.cc",
"chromium_src/chrome/browser/speech/tts_controller_impl.h",
"chromium_src/chrome/browser/speech/tts_linux.cc",
"chromium_src/chrome/browser/speech/tts_mac.mm",
"chromium_src/chrome/browser/speech/tts_message_filter.cc",
"chromium_src/chrome/browser/speech/tts_message_filter.h",
"chromium_src/chrome/browser/speech/tts_platform.cc",
"chromium_src/chrome/browser/speech/tts_platform.h",
"chromium_src/chrome/browser/speech/tts_win.cc",
"chromium_src/chrome/browser/ui/browser_dialogs.h",
"chromium_src/chrome/browser/ui/cocoa/color_chooser_mac.mm",
"chromium_src/chrome/browser/ui/views/color_chooser_aura.cc",
"chromium_src/chrome/browser/ui/views/color_chooser_aura.h",
"chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc",
"chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h",
"chromium_src/chrome/common/tts_messages.h",
"chromium_src/chrome/common/tts_utterance_request.cc",
"chromium_src/chrome/common/tts_utterance_request.h",
"chromium_src/chrome/renderer/tts_dispatcher.cc",
"chromium_src/chrome/renderer/tts_dispatcher.h",
"chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.cc",
"chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.h",
"chromium_src/library_loaders/libspeechd_loader.cc",
"chromium_src/library_loaders/libspeechd.h",
]
lib_sources_nss = [

View File

@@ -3,8 +3,6 @@
const electron = require('electron')
const { WebContentsView, TopLevelWindow } = electron
const { BrowserWindow } = process.atomBinding('window')
const v8Util = process.atomBinding('v8_util')
const ipcMain = require('@electron/internal/browser/ipc-main-internal')
Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype)
@@ -27,69 +25,6 @@ BrowserWindow.prototype._init = function () {
nativeSetBounds.call(this, bounds, ...opts)
}
// Make new windows requested by links behave like "window.open"
this.webContents.on('-new-window', (event, url, frameName, disposition,
additionalFeatures, postData,
referrer) => {
const options = {
show: true,
width: 800,
height: 600
}
ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN',
event, url, referrer, frameName, disposition,
options, additionalFeatures, postData)
})
this.webContents.on('-web-contents-created', (event, webContents, url,
frameName) => {
v8Util.setHiddenValue(webContents, 'url-framename', { url, frameName })
})
// Create a new browser window for the native implementation of
// "window.open", used in sandbox and nativeWindowOpen mode
this.webContents.on('-add-new-contents', (event, webContents, disposition,
userGesture, left, top, width,
height) => {
const urlFrameName = v8Util.getHiddenValue(webContents, 'url-framename')
if ((disposition !== 'foreground-tab' && disposition !== 'new-window' &&
disposition !== 'background-tab') || !urlFrameName) {
event.preventDefault()
return
}
if (webContents.getLastWebPreferences().nodeIntegration === true) {
const message =
'Enabling Node.js integration in child windows opened with the ' +
'"nativeWindowOpen" option will cause memory leaks, please turn off ' +
'the "nodeIntegration" option.\\n' +
'From 5.x child windows opened with the "nativeWindowOpen" option ' +
'will always have Node.js integration disabled.\\n' +
'See https://github.com/electron/electron/pull/15076 for more.'
// console is only available after DOM is created.
const printWarning = () => this.webContents.executeJavaScript(`console.warn('${message}')`)
if (this.webContents.isDomReady()) {
printWarning()
} else {
this.webContents.once('dom-ready', printWarning)
}
}
const { url, frameName } = urlFrameName
v8Util.deleteHiddenValue(webContents, 'url-framename')
const options = {
show: true,
x: left,
y: top,
width: width || 800,
height: height || 600,
webContents: webContents
}
const referrer = { url: '', policy: 'default' }
ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN',
event, url, referrer, frameName, disposition, options)
})
// window.resizeTo(...)
// window.moveTo(...)
this.webContents.on('move', (event, size) => {

View File

@@ -1,13 +1,11 @@
'use strict'
const CrashReporter = require('@electron/internal/common/crash-reporter')
const ipcMain = require('@electron/internal/browser/ipc-main-internal')
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init')
class CrashReporterMain extends CrashReporter {
sendSync (channel, ...args) {
const event = {}
ipcMain.emit(channel, event, ...args)
return event.returnValue
init (options) {
return crashReporterInit(options)
}
}

View File

@@ -263,7 +263,8 @@ module.exports = {
// Choose a default button to get selected when dialog is cancelled.
if (cancelId == null) {
cancelId = 0
// If the defaultId is set to 0, ensure the cancel button is a different index (1)
cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0
for (let i = 0; i < buttons.length; i++) {
const text = buttons[i].toLowerCase()
if (text === 'cancel' || text === 'no') {

View File

@@ -109,6 +109,12 @@ Menu.prototype.insert = function (pos, item) {
throw new TypeError('Invalid item')
}
if (pos < 0) {
throw new RangeError(`Position ${pos} cannot be less than 0`)
} else if (pos > this.getItemCount()) {
throw new RangeError(`Position ${pos} cannot be greater than the total MenuItem count`)
}
// insert item depending on its type
insertItemByType.call(this, item, pos)

View File

@@ -10,8 +10,12 @@ Object.setPrototypeOf(module.exports, new Proxy({}, {
if (!app.isReady()) return
const netLog = session.defaultSession.netLog
if (!Object.getPrototypeOf(netLog).hasOwnProperty(property)) return
// check for properties on the prototype chain that aren't functions
if (typeof netLog[property] !== 'function') return netLog[property]
// Returning a native function directly would throw error.
return (...args) => netLog[property](...args)
},

View File

@@ -5,6 +5,7 @@ const { EventEmitter } = require('events')
const electron = require('electron')
const path = require('path')
const url = require('url')
const v8Util = process.atomBinding('v8_util')
const { app, ipcMain, session, NavigationController, deprecate } = electron
const ipcMainInternal = require('@electron/internal/browser/ipc-main-internal')
@@ -199,7 +200,7 @@ WebContents.prototype.executeJavaScript = function (code, hasUserGesture, callba
WebContents.prototype.takeHeapSnapshot = function (filePath) {
return new Promise((resolve, reject) => {
const channel = `ELECTRON_TAKE_HEAP_SNAPSHOT_RESULT_${getNextId()}`
ipcMain.once(channel, (event, success) => {
ipcMainInternal.once(channel, (event, success) => {
if (success) {
resolve()
} else {
@@ -207,7 +208,7 @@ WebContents.prototype.takeHeapSnapshot = function (filePath) {
}
})
if (!this._takeHeapSnapshot(filePath, channel)) {
ipcMain.emit(channel, false)
ipcMainInternal.emit(channel, false)
}
})
}
@@ -262,7 +263,7 @@ WebContents.prototype.printToPDF = function (options, callback) {
WebContents.prototype.print = function (...args) {
if (features.isPrintingEnabled()) {
this._print(args)
this._print(...args)
} else {
console.error('Error: Printing feature is disabled.')
}
@@ -320,6 +321,17 @@ WebContents.prototype.findInPage = function (text, options = {}) {
return this._findInPage(text, options)
}
const safeProtocols = new Set([
'chrome-devtools:',
'chrome-extension:'
])
const isWebContentsTrusted = function (contents) {
const pageURL = contents._getURL()
const { protocol } = url.parse(pageURL)
return safeProtocols.has(protocol)
}
// Add JavaScript wrappers for WebContents class.
WebContents.prototype._init = function () {
// The navigation controller.
@@ -368,13 +380,22 @@ WebContents.prototype._init = function () {
})
})
this.on('remote-require', (event, ...args) => {
app.emit('remote-require', event, this, ...args)
})
const forwardedEvents = [
'remote-require',
'remote-get-global',
'remote-get-builtin',
'remote-get-current-window',
'remote-get-current-web-contents',
'remote-get-guest-web-contents'
]
this.on('remote-get-global', (event, ...args) => {
app.emit('remote-get-global', event, this, ...args)
})
for (const eventName of forwardedEvents) {
this.on(eventName, (event, ...args) => {
if (!isWebContentsTrusted(event.sender)) {
app.emit(eventName, event, this, ...args)
}
})
}
deprecate.event(this, 'did-get-response-details', '-did-get-response-details')
deprecate.event(this, 'did-get-redirect-request', '-did-get-redirect-request')
@@ -384,6 +405,72 @@ WebContents.prototype._init = function () {
this.reload()
})
// Handle window.open for BrowserWindow and BrowserView.
if (['browserView', 'window'].includes(this.getType())) {
// Make new windows requested by links behave like "window.open"
this.webContents.on('-new-window', (event, url, frameName, disposition,
additionalFeatures, postData,
referrer) => {
const options = {
show: true,
width: 800,
height: 600
}
ipcMainInternal.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN',
event, url, referrer, frameName, disposition,
options, additionalFeatures, postData)
})
this.webContents.on('-web-contents-created', (event, webContents, url,
frameName) => {
v8Util.setHiddenValue(webContents, 'url-framename', { url, frameName })
})
// Create a new browser window for the native implementation of
// "window.open", used in sandbox and nativeWindowOpen mode
this.webContents.on('-add-new-contents', (event, webContents, disposition,
userGesture, left, top, width,
height) => {
const urlFrameName = v8Util.getHiddenValue(webContents, 'url-framename')
if ((disposition !== 'foreground-tab' && disposition !== 'new-window' &&
disposition !== 'background-tab') || !urlFrameName) {
event.preventDefault()
return
}
if (webContents.getLastWebPreferences().nodeIntegration === true) {
const message =
'Enabling Node.js integration in child windows opened with the ' +
'"nativeWindowOpen" option will cause memory leaks, please turn off ' +
'the "nodeIntegration" option.\\n' +
'From 5.x child windows opened with the "nativeWindowOpen" option ' +
'will always have Node.js integration disabled.\\n' +
'See https://github.com/electron/electron/pull/15076 for more.'
// console is only available after DOM is created.
const printWarning = () => this.webContents.executeJavaScript(`console.warn('${message}')`)
if (this.webContents.isDomReady()) {
printWarning()
} else {
this.webContents.once('dom-ready', printWarning)
}
}
const { url, frameName } = urlFrameName
v8Util.deleteHiddenValue(webContents, 'url-framename')
const options = {
show: true,
x: left,
y: top,
width: width || 800,
height: height || 600,
webContents
}
const referrer = { url: '', policy: 'default' }
ipcMainInternal.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN',
event, url, referrer, frameName, disposition, options)
})
}
app.emit('web-contents-created', {}, this)
}

View File

@@ -0,0 +1,46 @@
'use strict'
const { app } = require('electron')
const cp = require('child_process')
const os = require('os')
const path = require('path')
const getTempDirectory = function () {
try {
return app.getPath('temp')
} catch (error) {
return os.tmpdir()
}
}
exports.crashReporterInit = function (options) {
const productName = options.productName || app.getName()
const crashesDirectory = path.join(getTempDirectory(), `${productName} Crashes`)
let crashServicePid
if (process.platform === 'win32') {
const env = {
ELECTRON_INTERNAL_CRASH_SERVICE: 1
}
const args = [
'--reporter-url=' + options.submitURL,
'--application-name=' + productName,
'--crashes-directory=' + crashesDirectory,
'--v=1'
]
const crashServiceProcess = cp.spawn(process.helperExecPath, args, {
env,
detached: true
})
crashServicePid = crashServiceProcess.pid
}
return {
productName,
crashesDirectory,
crashServicePid,
appVersion: app.getVersion()
}
}

View File

@@ -4,7 +4,11 @@ const { webContents } = require('electron')
const ipcMain = require('@electron/internal/browser/ipc-main-internal')
const parseFeaturesString = require('@electron/internal/common/parse-features-string')
const errorUtils = require('@electron/internal/common/error-utils')
const { syncMethods, asyncMethods } = require('@electron/internal/common/web-view-methods')
const {
syncMethods,
asyncCallbackMethods,
asyncPromiseMethods
} = require('@electron/internal/common/web-view-methods')
// Doesn't exist in early initialization.
let webViewManager = null
@@ -32,7 +36,6 @@ const supportedWebViewEvents = [
'focus-change',
'close',
'crashed',
'gpu-crashed',
'plugin-crashed',
'destroyed',
'page-title-updated',
@@ -391,7 +394,7 @@ ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event, focus, g
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', function (event, requestId, guestInstanceId, method, args, hasCallback) {
new Promise(resolve => {
const guest = getGuestForWebContents(guestInstanceId, event.sender)
if (!asyncMethods.has(method)) {
if (!asyncCallbackMethods.has(method) && !asyncPromiseMethods.has(method)) {
throw new Error(`Invalid method: ${method}`)
}
if (hasCallback) {

View File

@@ -1,16 +1,14 @@
'use strict'
const { spawn } = require('child_process')
const electron = require('electron')
const { EventEmitter } = require('events')
const fs = require('fs')
const os = require('os')
const path = require('path')
const v8Util = process.atomBinding('v8_util')
const eventBinding = process.atomBinding('event')
const { isPromise } = electron
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init')
const ipcMain = require('@electron/internal/browser/ipc-main-internal')
const objectsRegistry = require('@electron/internal/browser/objects-registry')
const guestViewManager = require('@electron/internal/browser/guest-view-manager')
@@ -296,42 +294,75 @@ handleRemoteCommand('ELECTRON_BROWSER_REQUIRE', function (event, contextId, modu
const customEvent = eventBinding.createWithSender(event.sender)
event.sender.emit('remote-require', customEvent, moduleName)
if (customEvent.defaultPrevented) {
if (typeof customEvent.returnValue === 'undefined') {
throw new Error(`Invalid module: ${moduleName}`)
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error(`Blocked remote.require('${moduleName}')`)
} else {
customEvent.returnValue = process.mainModule.require(moduleName)
}
} else {
customEvent.returnValue = process.mainModule.require(moduleName)
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
})
handleRemoteCommand('ELECTRON_BROWSER_GET_BUILTIN', function (event, contextId, module) {
return valueToMeta(event.sender, contextId, electron[module])
handleRemoteCommand('ELECTRON_BROWSER_GET_BUILTIN', function (event, contextId, moduleName) {
const customEvent = eventBinding.createWithSender(event.sender)
event.sender.emit('remote-get-builtin', customEvent, moduleName)
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error(`Blocked remote.getBuiltin('${moduleName}')`)
} else {
customEvent.returnValue = electron[moduleName]
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
})
handleRemoteCommand('ELECTRON_BROWSER_GLOBAL', function (event, contextId, globalName) {
const customEvent = eventBinding.createWithSender(event.sender)
event.sender.emit('remote-get-global', customEvent, globalName)
if (customEvent.defaultPrevented) {
if (typeof customEvent.returnValue === 'undefined') {
throw new Error(`Invalid global: ${globalName}`)
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error(`Blocked remote.getGlobal('${globalName}')`)
} else {
customEvent.returnValue = global[globalName]
}
} else {
customEvent.returnValue = global[globalName]
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
})
handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WINDOW', function (event, contextId) {
return valueToMeta(event.sender, contextId, event.sender.getOwnerBrowserWindow())
const customEvent = eventBinding.createWithSender(event.sender)
event.sender.emit('remote-get-current-window', customEvent)
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error('Blocked remote.getCurrentWindow()')
} else {
customEvent.returnValue = event.sender.getOwnerBrowserWindow()
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
})
handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event, contextId) {
return valueToMeta(event.sender, contextId, event.sender)
const customEvent = eventBinding.createWithSender(event.sender)
event.sender.emit('remote-get-current-web-contents', customEvent)
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error('Blocked remote.getCurrentWebContents()')
} else {
customEvent.returnValue = event.sender
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
})
handleRemoteCommand('ELECTRON_BROWSER_CONSTRUCTOR', function (event, contextId, id, args) {
@@ -411,7 +442,19 @@ handleRemoteCommand('ELECTRON_BROWSER_CONTEXT_RELEASE', (event, contextId) => {
handleRemoteCommand('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, contextId, guestInstanceId) {
const guest = guestViewManager.getGuestForWebContents(guestInstanceId, event.sender)
return valueToMeta(event.sender, contextId, guest)
const customEvent = eventBinding.createWithSender(event.sender)
event.sender.emit('remote-get-guest-web-contents', customEvent, guest)
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error(`Blocked remote.getGuestForWebContents()`)
} else {
customEvent.returnValue = guest
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
})
// Implements window.close()
@@ -423,46 +466,6 @@ ipcMain.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) {
event.returnValue = null
})
const getTempDirectory = function () {
try {
return electron.app.getPath('temp')
} catch (error) {
return os.tmpdir()
}
}
const crashReporterInit = function (options) {
const productName = options.productName || electron.app.getName()
const crashesDirectory = path.join(getTempDirectory(), `${productName} Crashes`)
let crashServicePid
if (process.platform === 'win32') {
const env = {
ELECTRON_INTERNAL_CRASH_SERVICE: 1
}
const args = [
'--reporter-url=' + options.submitURL,
'--application-name=' + productName,
'--crashes-directory=' + crashesDirectory,
'--v=1'
]
const crashServiceProcess = spawn(process.helperExecPath, args, {
env,
detached: true
})
crashServicePid = crashServiceProcess.pid
}
return {
productName,
crashesDirectory,
crashServicePid,
appVersion: electron.app.getVersion()
}
}
const setReturnValue = function (event, getValue) {
try {
event.returnValue = [null, getValue()]
@@ -487,20 +490,27 @@ ipcMain.on('ELECTRON_BROWSER_CLIPBOARD_WRITE_FIND_TEXT', function (event, text)
setReturnValue(event, () => electron.clipboard.writeFindText(text))
})
ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) {
const preloadPath = event.sender._getPreloadPath()
const getPreloadScript = function (preloadPath) {
let preloadSrc = null
let preloadError = null
if (preloadPath) {
try {
preloadSrc = fs.readFileSync(preloadPath).toString()
} catch (err) {
preloadError = { stack: err ? err.stack : (new Error(`Failed to load "${preloadPath}"`)).stack }
preloadError = errorUtils.serialize(err)
}
}
return { preloadPath, preloadSrc, preloadError }
}
ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) {
const preloadPaths = [
...(event.sender.session ? event.sender.session.getPreloads() : []),
event.sender._getPreloadPath()
]
event.returnValue = {
preloadSrc,
preloadError,
preloadScripts: preloadPaths.map(path => getPreloadScript(path)),
isRemoteModuleEnabled: event.sender._isRemoteModuleEnabled(),
process: {
arch: process.arch,

View File

@@ -2,28 +2,16 @@
const binding = process.atomBinding('crash_reporter')
const errorUtils = require('@electron/internal/common/error-utils')
class CrashReporter {
contructor () {
this.productName = null
this.crashesDirectory = null
}
sendSync (channel, ...args) {
init (options) {
throw new Error('Not implemented')
}
invoke (command, ...args) {
const [ error, result ] = this.sendSync(command, ...args)
if (error) {
throw errorUtils.deserialize(error)
}
return result
}
start (options) {
if (options == null) options = {}
@@ -51,7 +39,7 @@ class CrashReporter {
throw new Error('submitURL is a required option to crashReporter.start')
}
const ret = this.invoke('ELECTRON_CRASH_REPORTER_INIT', {
const ret = this.init({
submitURL,
productName
})

View File

@@ -50,18 +50,20 @@ exports.syncMethods = new Set([
'setZoomLevel'
])
exports.asyncMethods = new Set([
exports.asyncCallbackMethods = new Set([
'insertCSS',
'insertText',
'send',
'sendInputEvent',
'setLayoutZoomLevelLimits',
'setVisualZoomLevelLimits',
// with callback
'capturePage',
'executeJavaScript',
'getZoomFactor',
'getZoomLevel',
'print',
'printToPDF'
])
exports.asyncPromiseMethods = new Set([
'capturePage',
'executeJavaScript'
])

View File

@@ -2,10 +2,21 @@
const CrashReporter = require('@electron/internal/common/crash-reporter')
const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
const errorUtils = require('@electron/internal/common/error-utils')
const invoke = function (command, ...args) {
const [ error, result ] = ipcRenderer.sendSync(command, ...args)
if (error) {
throw errorUtils.deserialize(error)
}
return result
}
class CrashReporterRenderer extends CrashReporter {
sendSync (channel, ...args) {
return ipcRenderer.sendSync(channel, ...args)
init (options) {
return invoke('ELECTRON_CRASH_REPORTER_INIT', options)
}
}

View File

@@ -1,13 +1,67 @@
'use strict'
const { EventEmitter } = require('events')
const { webFrame, WebFrame } = process.atomBinding('web_frame')
const binding = process.atomBinding('web_frame')
// WebFrame is an EventEmitter.
Object.setPrototypeOf(WebFrame.prototype, EventEmitter.prototype)
EventEmitter.call(webFrame)
class WebFrame extends EventEmitter {
constructor (context) {
super()
// Lots of webview would subscribe to webFrame's events.
webFrame.setMaxListeners(0)
this.context = context
// Lots of webview would subscribe to webFrame's events.
this.setMaxListeners(0)
}
module.exports = webFrame
findFrameByRoutingId (...args) {
return getWebFrame(binding._findFrameByRoutingId(this.context, ...args))
}
getFrameForSelector (...args) {
return getWebFrame(binding._getFrameForSelector(this.context, ...args))
}
findFrameByName (...args) {
return getWebFrame(binding._findFrameByName(this.context, ...args))
}
get opener () {
return getWebFrame(binding._getOpener(this.context))
}
get parent () {
return getWebFrame(binding._getParent(this.context))
}
get top () {
return getWebFrame(binding._getTop(this.context))
}
get firstChild () {
return getWebFrame(binding._getFirstChild(this.context))
}
get nextSibling () {
return getWebFrame(binding._getNextSibling(this.context))
}
get routingId () {
return binding._getRoutingId(this.context)
}
}
// Populate the methods.
for (const name in binding) {
if (!name.startsWith('_')) { // some methods are manully populated above
WebFrame.prototype[name] = function (...args) {
return binding[name](this.context, ...args)
}
}
}
// Helper to return WebFrame or null depending on context.
// TODO(zcbenz): Consider returning same WebFrame for the same context.
function getWebFrame (context) {
return context ? new WebFrame(context) : null
}
module.exports = new WebFrame(window)

View File

@@ -4,8 +4,6 @@ const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
const Event = require('@electron/internal/renderer/extensions/event')
const url = require('url')
let nextId = 0
class Tab {
constructor (tabId) {
this.id = tabId
@@ -146,14 +144,12 @@ exports.injectTo = function (extensionId, isBackgroundPage, context) {
}
chrome.tabs = {
executeScript (tabId, details, callback) {
const requestId = ++nextId
ipcRenderer.once(`CHROME_TABS_EXECUTESCRIPT_RESULT_${requestId}`, (event, result) => {
// Disabled due to false positive in StandardJS
// eslint-disable-next-line standard/no-callback-literal
callback([event.result])
})
ipcRenderer.send('CHROME_TABS_EXECUTESCRIPT', requestId, tabId, extensionId, details)
executeScript (tabId, details, resultCallback) {
if (resultCallback) {
ipcRenderer.once(`CHROME_TABS_EXECUTESCRIPT_RESULT_${originResultID}`, (event, result) => resultCallback([result]))
}
ipcRenderer.send('CHROME_TABS_EXECUTESCRIPT', originResultID, tabId, extensionId, details)
originResultID++
},
sendMessage (tabId, message, options, responseCallback) {

View File

@@ -28,7 +28,6 @@ const WEB_VIEW_EVENTS = {
'focus-change': ['focus', 'guestInstanceId'],
'close': [],
'crashed': [],
'gpu-crashed': [],
'plugin-crashed': ['name', 'version'],
'destroyed': [],
'page-title-updated': ['title', 'explicitSet'],

View File

@@ -7,7 +7,11 @@ const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
const guestViewInternal = require('@electron/internal/renderer/web-view/guest-view-internal')
const webViewConstants = require('@electron/internal/renderer/web-view/web-view-constants')
const errorUtils = require('@electron/internal/common/error-utils')
const { syncMethods, asyncMethods } = require('@electron/internal/common/web-view-methods')
const {
syncMethods,
asyncCallbackMethods,
asyncPromiseMethods
} = require('@electron/internal/common/web-view-methods')
// ID generator.
let nextId = 0
@@ -268,10 +272,36 @@ const registerWebViewElement = (window) => {
ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', requestId, getGuestInstanceId(this), method, args, callback != null)
}
}
for (const method of asyncMethods) {
for (const method of asyncCallbackMethods) {
proto[method] = createNonBlockHandler(method)
}
const createPromiseHandler = function (method) {
return function (...args) {
return new Promise((resolve, reject) => {
const callback = (typeof args[args.length - 1] === 'function') ? args.pop() : null
const requestId = getNextId()
ipcRenderer.once(`ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL_RESPONSE_${requestId}`, function (event, error, result) {
if (error == null) {
if (callback) {
callback(result)
}
resolve(result)
} else {
reject(errorUtils.deserialize(error))
}
})
ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', requestId, getGuestInstanceId(this), method, args, callback != null)
})
}
}
for (const method of asyncPromiseMethods) {
proto[method] = createPromiseHandler(method)
}
// WebContents associated with this webview.
proto.getWebContents = function () {
const { getRemoteForUsage } = require('@electron/internal/renderer/remote')

View File

@@ -58,12 +58,16 @@ ipcNative.onMessage = function (channel, args, senderId) {
electron.ipcRenderer.emit(channel, { sender: electron.ipcRenderer, senderId }, ...args)
}
ipcNative.onLoaded = function () {
process.emit('loaded')
}
ipcNative.onExit = function () {
process.emit('exit')
}
const {
preloadSrc, preloadError, isRemoteModuleEnabled, process: processProps
preloadScripts, isRemoteModuleEnabled, process: processProps
} = ipcRenderer.sendSync('ELECTRON_BROWSER_SANDBOX_LOAD')
const makePropertyNonConfigurable = function (object, name) {
@@ -90,6 +94,7 @@ Object.assign(preloadProcess, processProps)
Object.assign(process, binding.process)
Object.assign(process, processProps)
process.on('loaded', () => preloadProcess.emit('loaded'))
process.on('exit', () => preloadProcess.emit('exit'))
// This is the `require` function that will be visible to the preload script
@@ -100,7 +105,7 @@ function preloadRequire (module) {
if (remoteModules.has(module)) {
return require(module)
}
throw new Error('module not found')
throw new Error(`module not found: ${module}`)
}
switch (window.location.protocol) {
@@ -126,6 +131,8 @@ if (!process.guestInstanceId && preloadProcess.argv.includes('--webview-tag=true
require('@electron/internal/renderer/web-view/web-view').setupWebView(window)
}
const errorUtils = require('@electron/internal/common/error-utils')
// Wrap the script into a function executed in global scope. It won't have
// access to the current scope, so we'll expose a few objects as arguments:
//
@@ -145,7 +152,7 @@ if (!process.guestInstanceId && preloadProcess.argv.includes('--webview-tag=true
// and any `require('electron')` calls in `preload.js` will work as expected
// since browserify won't try to include `electron` in the bundle, falling back
// to the `preloadRequire` function above.
if (preloadSrc) {
function runPreloadScript (preloadSrc) {
const preloadWrapperSrc = `(function(require, process, Buffer, global, setImmediate, clearImmediate) {
${preloadSrc}
})`
@@ -153,9 +160,21 @@ if (preloadSrc) {
// eval in window scope
const preloadFn = binding.createPreloadScript(preloadWrapperSrc)
const { setImmediate, clearImmediate } = require('timers')
preloadFn(preloadRequire, preloadProcess, Buffer, global, setImmediate, clearImmediate)
} else if (preloadError) {
console.error(preloadError.stack)
}
for (const { preloadPath, preloadSrc, preloadError } of preloadScripts) {
try {
if (preloadSrc) {
runPreloadScript(preloadSrc)
} else if (preloadError) {
throw errorUtils.deserialize(preloadError)
}
} catch (error) {
console.error(`Unable to load preload script: ${preloadPath}`)
console.error(`${error}`)
}
}
// Warn about security issues

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "electron",
"version": "4.0.2",
"version": "4.2.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "electron",
"version": "4.0.2",
"version": "4.2.2",
"repository": "https://github.com/electron/electron",
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
"devDependencies": {

View File

@@ -1 +1,6 @@
implement_ssl_get_tlsext_status_type.patch
expose_ripemd160.patch
expose_aes-cfb.patch
sync_sorted_ciphers.patch
handle_pub_key_null_in_ec_key_set_public_key.patch
add_a_compatibility_evp_ciph_ocb_mode_value.patch

View File

@@ -0,0 +1,35 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: David Benjamin <davidben@google.com>
Date: Sun, 14 Oct 2018 11:01:40 -0500
Subject: Add a compatibility EVP_CIPH_OCB_MODE value.
Node references it these days. Also replace the no-op modes with negative
numbers rather than zero. Stream ciphers like RC4 report a "mode" of zero, so
code comparing the mode to a dummy value will get confused.
(I came across https://github.com/nodejs/node/pull/23635, though we'd have run
into it sooner or later anyway. Better to just define the value and avoid ifdef
proliferation.)
Change-Id: I223f25663e138480ad83f35aa16f5218f1425563
Reviewed-on: https://boringssl-review.googlesource.com/c/32464
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/include/openssl/cipher.h b/include/openssl/cipher.h
index e2ab9449275a62ee8a93bd48284b39e8df88a14f..7d4d78b3730022fb61ae63c6d3a86a61cb0c91e2 100644
--- a/include/openssl/cipher.h
+++ b/include/openssl/cipher.h
@@ -425,8 +425,9 @@ OPENSSL_EXPORT const EVP_CIPHER *EVP_aes_256_cfb128(void);
// The following flags do nothing and are included only to make it easier to
// compile code with BoringSSL.
-#define EVP_CIPH_CCM_MODE 0
-#define EVP_CIPH_WRAP_MODE 0
+#define EVP_CIPH_CCM_MODE (-1)
+#define EVP_CIPH_OCB_MODE (-2)
+#define EVP_CIPH_WRAP_MODE (-3)
#define EVP_CIPHER_CTX_FLAG_WRAP_ALLOW 0
// EVP_CIPHER_CTX_set_flags does nothing.

View File

@@ -0,0 +1,84 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jeremy Apthorp <nornagon@nornagon.net>
Date: Fri, 18 Jan 2019 14:23:28 -0800
Subject: expose aes-{128,256}-cfb
diff --git a/crypto/cipher_extra/cipher_extra.c b/crypto/cipher_extra/cipher_extra.c
index 1b23ad32f8cff2a00512ba58d24b47b628e7920c..be7ef07b2c188a76890deb0f305cf92fcc57a64e 100644
--- a/crypto/cipher_extra/cipher_extra.c
+++ b/crypto/cipher_extra/cipher_extra.c
@@ -101,10 +101,14 @@ const EVP_CIPHER *EVP_get_cipherbyname(const char *name) {
return EVP_des_ede3_cbc();
} else if (OPENSSL_strcasecmp(name, "aes-128-cbc") == 0) {
return EVP_aes_128_cbc();
+ } else if (OPENSSL_strcasecmp(name, "aes-128-cfb") == 0) {
+ return EVP_aes_128_cfb128();
} else if (OPENSSL_strcasecmp(name, "aes-192-cbc") == 0) {
return EVP_aes_192_cbc();
} else if (OPENSSL_strcasecmp(name, "aes-256-cbc") == 0) {
return EVP_aes_256_cbc();
+ } else if (OPENSSL_strcasecmp(name, "aes-256-cfb") == 0) {
+ return EVP_aes_256_cfb128();
} else if (OPENSSL_strcasecmp(name, "aes-128-ctr") == 0) {
return EVP_aes_128_ctr();
} else if (OPENSSL_strcasecmp(name, "aes-192-ctr") == 0) {
diff --git a/decrepit/cfb/cfb.c b/decrepit/cfb/cfb.c
index d3a176163303a202baeb1f95727c6ed3525439d6..21d108a7b73d454aa6b0e324df4b67088d60302a 100644
--- a/decrepit/cfb/cfb.c
+++ b/decrepit/cfb/cfb.c
@@ -57,4 +57,12 @@ static const EVP_CIPHER aes_128_cfb128 = {
NULL /* cleanup */, NULL /* ctrl */,
};
+static const EVP_CIPHER aes_256_cfb128 = {
+ NID_aes_128_cfb128, 1 /* block_size */, 32 /* key_size */,
+ 16 /* iv_len */, sizeof(EVP_CFB_CTX), EVP_CIPH_CFB_MODE,
+ NULL /* app_data */, aes_cfb_init_key, aes_cfb128_cipher,
+ NULL /* cleanup */, NULL /* ctrl */,
+};
+
const EVP_CIPHER *EVP_aes_128_cfb128(void) { return &aes_128_cfb128; }
+const EVP_CIPHER *EVP_aes_256_cfb128(void) { return &aes_256_cfb128; }
diff --git a/decrepit/evp/evp_do_all.c b/decrepit/evp/evp_do_all.c
index acc4719b7e9c4c4461fc6142f2ae9156b407915b..8b008a401ec2f2d0673f6876609dd5786cace4c2 100644
--- a/decrepit/evp/evp_do_all.c
+++ b/decrepit/evp/evp_do_all.c
@@ -20,10 +20,12 @@ void EVP_CIPHER_do_all_sorted(void (*callback)(const EVP_CIPHER *cipher,
const char *unused, void *arg),
void *arg) {
callback(EVP_aes_128_cbc(), "AES-128-CBC", NULL, arg);
+ callback(EVP_aes_128_cfb128(), "AES-128-CFB", NULL, arg);
callback(EVP_aes_128_ctr(), "AES-128-CTR", NULL, arg);
callback(EVP_aes_128_ecb(), "AES-128-ECB", NULL, arg);
callback(EVP_aes_128_ofb(), "AES-128-OFB", NULL, arg);
callback(EVP_aes_256_cbc(), "AES-256-CBC", NULL, arg);
+ callback(EVP_aes_256_cfb128(), "AES-256-CFB", NULL, arg);
callback(EVP_aes_256_ctr(), "AES-256-CTR", NULL, arg);
callback(EVP_aes_256_ecb(), "AES-256-ECB", NULL, arg);
callback(EVP_aes_256_ofb(), "AES-256-OFB", NULL, arg);
@@ -38,10 +40,12 @@ void EVP_CIPHER_do_all_sorted(void (*callback)(const EVP_CIPHER *cipher,
// OpenSSL returns everything twice, the second time in lower case.
callback(EVP_aes_128_cbc(), "aes-128-cbc", NULL, arg);
+ callback(EVP_aes_128_cfb128(), "aes-128-cfb", NULL, arg);
callback(EVP_aes_128_ctr(), "aes-128-ctr", NULL, arg);
callback(EVP_aes_128_ecb(), "aes-128-ecb", NULL, arg);
callback(EVP_aes_128_ofb(), "aes-128-ofb", NULL, arg);
callback(EVP_aes_256_cbc(), "aes-256-cbc", NULL, arg);
+ callback(EVP_aes_256_cfb128(), "aes-256-cfb", NULL, arg);
callback(EVP_aes_256_ctr(), "aes-256-ctr", NULL, arg);
callback(EVP_aes_256_ecb(), "aes-256-ecb", NULL, arg);
callback(EVP_aes_256_ofb(), "aes-256-ofb", NULL, arg);
diff --git a/include/openssl/cipher.h b/include/openssl/cipher.h
index 7d99d49ba7ae2d8a4eb80681cbd9b41eee86bac7..e2ab9449275a62ee8a93bd48284b39e8df88a14f 100644
--- a/include/openssl/cipher.h
+++ b/include/openssl/cipher.h
@@ -421,6 +421,7 @@ OPENSSL_EXPORT const EVP_CIPHER *EVP_aes_192_ofb(void);
// EVP_aes_128_cfb128 is only available in decrepit.
OPENSSL_EXPORT const EVP_CIPHER *EVP_aes_128_cfb128(void);
+OPENSSL_EXPORT const EVP_CIPHER *EVP_aes_256_cfb128(void);
// The following flags do nothing and are included only to make it easier to
// compile code with BoringSSL.

View File

@@ -0,0 +1,95 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jeremy Apthorp <nornagon@nornagon.net>
Date: Fri, 18 Jan 2019 13:56:52 -0800
Subject: expose ripemd160
This adds references to the decrepit/ module from non-decrepit source,
which is not allowed in upstream. Until upstream has a way to interface
with node.js that allows exposing additional digests without patching,
this patch is required to provide ripemd160 support in the nodejs crypto
module.
diff --git a/crypto/digest_extra/digest_extra.c b/crypto/digest_extra/digest_extra.c
index 4b4bb38135e6089eaf6f47afda0199567a2397ef..43b7eca808b82a032055f56ce726ce4f38c5f2c5 100644
--- a/crypto/digest_extra/digest_extra.c
+++ b/crypto/digest_extra/digest_extra.c
@@ -81,6 +81,7 @@ static const struct nid_to_digest nid_to_digest_mapping[] = {
{NID_sha384, EVP_sha384, SN_sha384, LN_sha384},
{NID_sha512, EVP_sha512, SN_sha512, LN_sha512},
{NID_md5_sha1, EVP_md5_sha1, SN_md5_sha1, LN_md5_sha1},
+ {NID_ripemd160, EVP_ripemd160, SN_ripemd160, LN_ripemd160},
// As a remnant of signing |EVP_MD|s, OpenSSL returned the corresponding
// hash function when given a signature OID. To avoid unintended lax parsing
// of hash OIDs, this is no longer supported for lookup by OID or NID.
diff --git a/crypto/fipsmodule/digest/digests.c b/crypto/fipsmodule/digest/digests.c
index f2fa349c2b32ae88766624af3109ece4b1d69909..bcaed59c5401bef071acba9b9919d9069e3ccd4d 100644
--- a/crypto/fipsmodule/digest/digests.c
+++ b/crypto/fipsmodule/digest/digests.c
@@ -63,6 +63,7 @@
#include <openssl/md5.h>
#include <openssl/nid.h>
#include <openssl/sha.h>
+#include <openssl/ripemd.h>
#include "internal.h"
#include "../delocate.h"
@@ -277,4 +278,27 @@ DEFINE_METHOD_FUNCTION(EVP_MD, EVP_md5_sha1) {
out->ctx_size = sizeof(MD5_SHA1_CTX);
}
+static void ripemd160_init(EVP_MD_CTX *ctx) {
+ CHECK(RIPEMD160_Init(ctx->md_data));
+}
+
+static void ripemd160_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
+ CHECK(RIPEMD160_Update(ctx->md_data, data, count));
+}
+
+static void ripemd160_final(EVP_MD_CTX *ctx, uint8_t *md) {
+ CHECK(RIPEMD160_Final(md, ctx->md_data));
+}
+
+DEFINE_METHOD_FUNCTION(EVP_MD, EVP_ripemd160) {
+ out->type = NID_ripemd160;
+ out->md_size = RIPEMD160_DIGEST_LENGTH;
+ out->flags = 0;
+ out->init = ripemd160_init;
+ out->update = ripemd160_update;
+ out->final = ripemd160_final;
+ out->block_size = 64;
+ out->ctx_size = sizeof(RIPEMD160_CTX);
+}
+
#undef CHECK
diff --git a/decrepit/evp/evp_do_all.c b/decrepit/evp/evp_do_all.c
index 38b8f9f78f76050174096740596ac59a0fe18757..acc4719b7e9c4c4461fc6142f2ae9156b407915b 100644
--- a/decrepit/evp/evp_do_all.c
+++ b/decrepit/evp/evp_do_all.c
@@ -66,6 +66,7 @@ void EVP_MD_do_all_sorted(void (*callback)(const EVP_MD *cipher,
callback(EVP_sha256(), "SHA256", NULL, arg);
callback(EVP_sha384(), "SHA384", NULL, arg);
callback(EVP_sha512(), "SHA512", NULL, arg);
+ callback(EVP_ripemd160(), "RIPEMD160", NULL, arg);
callback(EVP_md4(), "md4", NULL, arg);
callback(EVP_md5(), "md5", NULL, arg);
@@ -74,4 +75,5 @@ void EVP_MD_do_all_sorted(void (*callback)(const EVP_MD *cipher,
callback(EVP_sha256(), "sha256", NULL, arg);
callback(EVP_sha384(), "sha384", NULL, arg);
callback(EVP_sha512(), "sha512", NULL, arg);
+ callback(EVP_ripemd160(), "ripemd160", NULL, arg);
}
diff --git a/include/openssl/digest.h b/include/openssl/digest.h
index 4077d902a07c215659ed61b54a468231536d70ee..f15df35d16402256fa00263e2c2e71d55ce67d1a 100644
--- a/include/openssl/digest.h
+++ b/include/openssl/digest.h
@@ -88,6 +88,9 @@ OPENSSL_EXPORT const EVP_MD *EVP_sha512(void);
// MD5 and SHA-1, as used in TLS 1.1 and below.
OPENSSL_EXPORT const EVP_MD *EVP_md5_sha1(void);
+// EVP_ripemd160 is in decrepit and not available by default.
+OPENSSL_EXPORT const EVP_MD *EVP_ripemd160(void);
+
// EVP_get_digestbynid returns an |EVP_MD| for the given NID, or NULL if no
// such digest is known.
OPENSSL_EXPORT const EVP_MD *EVP_get_digestbynid(int nid);

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