diff --git a/DEPS b/DEPS index dba2fe3..fdcda64 100644 --- a/DEPS +++ b/DEPS
@@ -40,7 +40,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': '3c322e23a013e78fcbe0edd7adccd580af8466bc', + 'skia_revision': '61cd31a5d3cacb13ff3bbc3ada7e25fa98c6810c', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. @@ -64,7 +64,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': 'a11ac1bedef8c7a55b7e35ec89f5bdcbfcdc5025', + 'pdfium_revision': 'd7de8e15eaae5a37abfcb8d1cf7028fbcebbaccd', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling openmax_dl # and whatever else without interference from each other. @@ -96,7 +96,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': 'c81620124f0f99a051d5362667043bda6f1ffddd', + 'catapult_revision': '61f955fed373838b150579c6c251732da1a86534', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -124,7 +124,7 @@ deps = { 'src/breakpad/src': - Var('chromium_git') + '/breakpad/breakpad/src.git' + '@' + '37070c5e680f3356f7b14d239518a104edbc2c97', + Var('chromium_git') + '/breakpad/breakpad/src.git' + '@' + '10162f61e9ca4992e7851c69c7b8c6eddaab9e8e', 'src/buildtools': Var('chromium_git') + '/chromium/buildtools.git' + '@' + Var('buildtools_revision'),
diff --git a/WATCHLISTS b/WATCHLISTS index 370dde6..186915ee 100644 --- a/WATCHLISTS +++ b/WATCHLISTS
@@ -281,6 +281,9 @@ 'chrome_elf': { 'filepath': 'chrome_elf', }, + 'chrome_grc': { + 'filepath': 'services/resource_coordinator', + }, 'chrome_views': { 'filepath': 'chrome/browser/ui/views', }, @@ -1813,6 +1816,7 @@ 'rsleevi+watch@chromium.org'], 'chrome_elf': ['caitkp+watch@chromium.org', 'pennymac+watch@chromium.org'], + 'chrome_grc': ['chrome-grc-reviews@chromium.org'], 'chrome_views': ['tfarina@chromium.org'], 'chromecast': ['alokp+watch@chromium.org', 'halliwell+watch@chromium.org',
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnFormResubmissionTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnFormResubmissionTest.java index 62bbe5e..5acc2e69 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnFormResubmissionTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnFormResubmissionTest.java
@@ -8,8 +8,7 @@ import android.os.Message; import android.support.test.filters.SmallTest; - -import org.apache.http.util.EncodingUtils; +import android.util.Base64; import org.chromium.android_webview.AwContents; import org.chromium.base.test.util.Feature; @@ -101,7 +100,7 @@ protected void doReload() throws Throwable { String url = mServer.setResponse("/form", LOAD_RESPONSE, null); String postData = "content=blabla"; - byte[] data = EncodingUtils.getBytes(postData, "BASE64"); + byte[] data = Base64.encode(postData.getBytes("UTF-8"), Base64.DEFAULT); postUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), url, data); assertEquals(0, mContentsClient.getResubmissions()); assertEquals("Load", getTitleOnUiThread(mAwContents));
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java index 3c9c995..9f402a79 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java
@@ -7,9 +7,6 @@ import android.support.test.filters.SmallTest; import android.util.Pair; -import org.apache.http.Header; -import org.apache.http.HttpRequest; - import org.chromium.android_webview.AwContents; import org.chromium.android_webview.AwSettings; import org.chromium.android_webview.test.util.CommonResources; @@ -23,7 +20,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -115,21 +111,20 @@ return result; } - private void validateRequestHeaders(String[] refNamesAndValues, HttpRequest request) { + private void validateRequestHeaders(String[] refNamesAndValues, List<String> request) { + List<String> matchingHeaders; for (int i = 0; i < refNamesAndValues.length; i += 2) { - Header[] matchingHeaders = request.getHeaders(refNamesAndValues[i]); - assertEquals(1, matchingHeaders.length); - - Header header = matchingHeaders[0]; - assertEquals(refNamesAndValues[i].toLowerCase(Locale.ENGLISH), header.getName()); - assertEquals(refNamesAndValues[i + 1], header.getValue()); + matchingHeaders = TestWebServer.getMatchingHeadersValues(request, refNamesAndValues[i]); + assertEquals(1, matchingHeaders.size()); + assertEquals(refNamesAndValues[i + 1], matchingHeaders.get(0)); } } - private void validateNoRequestHeaders(String[] refNamesAndValues, HttpRequest request) { + private void validateNoRequestHeaders(String[] refNamesAndValues, List<String> request) { + List<String> matchingHeaders; for (int i = 0; i < refNamesAndValues.length; i += 2) { - Header[] matchingHeaders = request.getHeaders(refNamesAndValues[i]); - assertEquals(0, matchingHeaders.length); + matchingHeaders = TestWebServer.getMatchingHeadersValues(request, refNamesAndValues[i]); + assertEquals(0, matchingHeaders.size()); } } @@ -183,22 +178,18 @@ path, "<html><body>foo</body></html>", null); - String[] extraHeaders = { - "user-agent", "007" - }; + String[] extraHeaders = {"user-agent", "Borewicz 07 & Bond 007"}; loadUrlWithExtraHeadersSync(awContents, contentsClient.getOnPageFinishedHelper(), url, createHeadersMap(extraHeaders)); - Header[] matchingHeaders = webServer.getLastRequest(path).getHeaders(extraHeaders[0]); - assertEquals(1, matchingHeaders.length); - Header header = matchingHeaders[0]; - assertEquals(extraHeaders[0].toLowerCase(Locale.ENGLISH), - header.getName().toLowerCase(Locale.ENGLISH)); + List<String> matchingHeaders = TestWebServer.getMatchingHeadersValues( + webServer.getLastRequest(path), extraHeaders[0]); + assertEquals(1, matchingHeaders.size()); // Just check that the value is there, and it's not the one we provided. - assertTrue(header.getValue().length() > 0); - assertFalse(extraHeaders[1].equals(header.getValue())); + assertTrue(matchingHeaders.get(0).length() > 0); + assertFalse(extraHeaders[1].equals(matchingHeaders.get(0))); } finally { webServer.shutdown(); }
diff --git a/ash/OWNERS b/ash/OWNERS index a3b6999b..1c08337 100644 --- a/ash/OWNERS +++ b/ash/OWNERS
@@ -6,7 +6,6 @@ # These are for the common case of adding or renaming a few items. If you're # doing structural changes, please get a review from a reviewer above. per-file ash_strings.grd=* -per-file ash_chromeos_strings.grdp=* per-file ash_switches.*=* per-file BUILD.gn=*
diff --git a/ash/ash_chromeos_strings.grdp b/ash/ash_chromeos_strings.grdp deleted file mode 100644 index b818e2c..0000000 --- a/ash/ash_chromeos_strings.grdp +++ /dev/null
@@ -1,620 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- ChromeOS-specific strings (included from ash_strings.grd). - Everything in this file is wrapped in <if expr="chromeos">. --> -<grit-part> - - <!-- Device types --> - <message name="IDS_ASH_CHROMEBASE" desc="The device name for a Chromebase (versus say Chromebox)"> - Chromebase - </message> - <message name="IDS_ASH_CHROMEBIT" desc="The device name for a Chromebit (versus say Chromebox)"> - Chromebit - </message> - <message name="IDS_ASH_CHROMEBOOK" desc="The device name for a Chromebook (versus say Chromebox)"> - Chromebook - </message> - <message name="IDS_ASH_CHROMEBOX" desc="The device name for a Chromebox (versus say Chromebook)"> - Chromebox - </message> - <message name="IDS_ASH_CHROMEDEVICE" desc="The device name for a generic Chrome device"> - Chrome device - </message> - - <!-- Status tray charging strings. --> - <message name="IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_TITLE" desc="The title of a notification indicating that a low-current USB charger has been connected."> - Low-power charger connected - </message> - <message name="IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_MESSAGE" desc="The message body of a notification indicating that a low-current USB charger has been connected."> - Your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> may not charge while it is turned on. Consider using the official charger. - </message> - <message name="IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_MESSAGE_SHORT" desc="The message body of a notification indicating that a low-current USB charger has been connected, short version."> - Your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> may not charge while it is turned on. - </message> - <message name="IDS_ASH_STATUS_TRAY_CHARGING_FROM_DUAL_ROLE_TITLE" desc="The message title of a notification indicating that the device is being charged by a connected dual-role USB Type-C device."> - Charging from <ph name="POWER_SOURCE">$1<ex>USB-C device (left port)</ex></ph> - </message> - <message name="IDS_ASH_STATUS_TRAY_CHARGING_DUAL_ROLE_DEVICE_TITLE" desc="The message title of a notification indicating that the device is charging the connected USB Type-C device."> - Charging <ph name="POWER_SOURCE">$1<ex>USB-C device (left port)</ex></ph> - </message> - <message name="IDS_ASH_STATUS_TRAY_CHARGING_DUAL_ROLE_DEVICES_TITLE" desc="The message title of a notification indicating that the device is charging multiple connected USB Type-C devices."> - Charging connected USB-C devices - </message> - <message name="IDS_ASH_STATUS_TRAY_DUAL_ROLE_MESSAGE" desc="The message body of a notification, which indicates that the user can click the notification to see and change which device is being used as a power source."> - Click for more options - </message> - <message name="IDS_ASH_POWER_SOURCE_PORT_LEFT" desc="The text identifying an external device, when that device is connected to the USB Type-C port on the left side of this device."> - USB-C device (left port) - </message> - <message name="IDS_ASH_POWER_SOURCE_PORT_RIGHT" desc="The text identifying an external device, when that device is connected to the USB Type-C port on the right side of this device."> - USB-C device (right port) - </message> - <message name="IDS_ASH_POWER_SOURCE_PORT_BACK" desc="The text identifying an external device, when that device is connected to the USB Type-C port on the back of this device."> - USB-C device (rear port) - </message> - <message name="IDS_ASH_POWER_SOURCE_PORT_FRONT" desc="The text identifying an external device, when that device is connected to the USB Type-C port on the front of this device."> - USB-C device (front port) - </message> - <message name="IDS_ASH_POWER_SOURCE_PORT_LEFT_FRONT" desc="The text identifying an external device, when that device is connected to a USB Type-C port on the left side of this device. In this case there are two ports on that side, so this text should refer to the front one on that side."> - USB-C device (left side front port) - </message> - <message name="IDS_ASH_POWER_SOURCE_PORT_LEFT_BACK" desc="The text identifying an external device, when that device is connected to a USB Type-C port on the left side of this device. In this case there are two ports on that side, so this text should refer to the back one on that side."> - USB-C device (left side back port) - </message> - <message name="IDS_ASH_POWER_SOURCE_PORT_RIGHT_FRONT" desc="The text identifying an external device, when that device is connected to a USB Type-C port on the right side of this device. In this case there are two ports on that side, so this text should refer to the front one on that side."> - USB-C device (right side front port) - </message> - <message name="IDS_ASH_POWER_SOURCE_PORT_RIGHT_BACK" desc="The text identifying an external device, when that device is connected to a USB Type-C port on the right side of this device. In this case there are two ports on that side, so this text should refer to the back one on that side."> - USB-C device (right side back port) - </message> - <message name="IDS_ASH_POWER_SOURCE_PORT_BACK_LEFT" desc="The text identifying an external device, when that device is connected to a USB Type-C port on the back of this device. In this case there are two ports on the back, so this text should refer to the one on the left."> - USB-C device (left port in the back) - </message> - <message name="IDS_ASH_POWER_SOURCE_PORT_BACK_RIGHT" desc="The text identifying an external device, when that device is connected to a USB Type-C port on the back of this device. In this case there are two ports on the back, so this text should refer to the one on the right."> - USB-C device (right port in the back) - </message> - <message name="IDS_ASH_POWER_SOURCE_PORT_UNKNOWN" desc="The text referring to the device to charge from, when its port location cannot be determined."> - USB-C device - </message> - - <message name="IDS_CHARGER_REPLACEMENT_ACCESSIBILITY_NOTIFICATION" desc="The notification string used in accessibility to tell user to contact Google for replacing the possible bad charger."> - You may have a bad charger. If you live in the US, please call 866-628-1371 in order to receive help and a replacement. If you live in the UK, please call 0800-026-0613. If you live in Ireland, please call 1-800-832-664. If you live in Canada, please call 866-628-1372. If you live in Australia, please call 1-800-067-460. - </message> - - <!-- Status Tray Network strings --> - <message name="IDS_ASH_STATUS_TRAY_NETWORK" desc="The label used in the network dialog header."> - Network - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTED" desc="The label used when a network connection is connected."> - Connected - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTING" desc="The label used when a network connection is connecting."> - Connecting... - </message> - <message name="IDS_ASH_STATUS_TRAY_VPN" desc="The label used in the VPN detailed view header."> - Private network - </message> - <message name="IDS_ASH_STATUS_TRAY_VPN_BUILT_IN_PROVIDER" desc="The name of the built-in VPN provider that supports OpenVPN and L2TP over IPsec."> - OpenVPN / L2TP - </message> - <message name="IDS_ASH_STATUS_TRAY_VPN_DISCONNECT" desc="Message on button that disconnects from the current VPN."> - Disconnect - </message> - <message name="IDS_ASH_STATUS_TRAY_VPN_MONITORED" desc="The label used when a VPN connection may be monitored."> - Connection may be monitored - </message> - <message name="IDS_ASH_STATUS_TRAY_WIFI" desc="The wifi network device."> - Wi-Fi - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_INFO" desc="The accessible text for the network info button."> - Network info - </message> - <message name="IDS_ASH_STATUS_TRAY_IP" desc="The label for the IP address of the network:"> - IP address - </message> - <message name="IDS_ASH_STATUS_TRAY_IPV6" desc="The label for the IP address of the network:"> - IPv6 address - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_SETTINGS" desc="The label used in the settings entry in the network dialog."> - Network settings - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_PROXY_SETTINGS" desc="The label used in the proxy settings entry in the network dialog in the login screen."> - Proxy settings - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_TECHNOLOGY_ENFORCED_BY_POLICY" desc="The label used for tell users that this technology is disabled by the administrator"> - This network setting is disabled by the administrator. - </message> - <message name="IDS_ASH_STATUS_TRAY_ENABLE_WIFI" desc="The label used for the item to enable wifi."> - Enable Wi-Fi - </message> - <message name="IDS_ASH_STATUS_TRAY_DISABLE_WIFI" desc="The label used for the item to disable wifi."> - Disable Wi-Fi - </message> - <message name="IDS_ASH_STATUS_TRAY_OTHER_WIFI" desc="The label used for the item to display other Wi-Fi networks."> - Join other... - </message> - <message name="IDS_ASH_STATUS_TRAY_EXTENSION_CONTROLLED_WIFI" desc="The accessible text for the badge in the detailed network list indicating that the respective network's configuration may be controlled by an extension."> - The extension "<ph name="EXTENSION_NAME">$1<ex>Nexus S</ex></ph>" can help connect to this network. - </message> - <message name="IDS_ASH_STATUS_TRAY_TURN_ON_WIFI" desc="The label used for the item to turn on Wi-Fi networks."> - Turn Wi-Fi on - </message> - <message name="IDS_ASH_STATUS_TRAY_ENABLE_MOBILE" desc="The label used for the item to enable mobile networks."> - Enable mobile data - </message> - <message name="IDS_ASH_STATUS_TRAY_DISABLE_MOBILE" desc="The label used for the item to disable mobile networks."> - Disable mobile data - </message> - <message name="IDS_ASH_STATUS_TRAY_SETUP_MOBILE" desc="The label used for the item to setup mobile network when no SIM card in installed."> - Set up mobile data - </message> - <message name="IDS_ASH_STATUS_TRAY_OTHER_MOBILE" desc="The label used for the item to display other mobile networks."> - Mobile ... - </message> - <message name="IDS_ASH_STATUS_TRAY_MOBILE_VIEW_ACCOUNT" desc="In the network popup bubble, the text of the top-up URL link."> - View mobile account - </message> - <message name="IDS_ASH_STATUS_TRAY_BLUETOOTH_DISCOVERABLE" desc="Notification shown when a Bluetooth adapter is discoverable."> - Your computer is discoverable to nearby Bluetooth devices and will appear as "<ph name="NAME">$1<ex>Chromebook</ex></ph>" with address <ph name="ADDRESS">$2<ex>01:23:45:67:89:0A</ex></ph> - </message> - <message name="IDS_ASH_STATUS_TRAY_BLUETOOTH_DISPLAY_PINCODE" desc="Bluetooth pairing message typically shown on a request from a 2.0 device that has a keyboard."> - Bluetooth device "<ph name="DEVICE_NAME">$1<ex>Nexus S</ex></ph>" would like permission to pair. Please enter this PIN code on that device: <ph name="PINCODE">$2<ex>123456</ex></ph> - </message> - <message name="IDS_ASH_STATUS_TRAY_BLUETOOTH_DISPLAY_PASSKEY" desc="Bluetooth pairing message typically shown on a request from a device that has a keyboard."> - Bluetooth device "<ph name="DEVICE_NAME">$1<ex>Nexus S</ex></ph>" would like permission to pair. Please enter this passkey on that device: <ph name="PASSKEY">$2<ex>123456</ex></ph> - </message> - <message name="IDS_ASH_STATUS_TRAY_BLUETOOTH_CONFIRM_PASSKEY" desc="Bluetooth pairing message typically shown on a request from a device that has a display."> - Bluetooth device "<ph name="DEVICE_NAME">$1<ex>Nexus S</ex></ph>" would like permission to pair. Before accepting, please confirm that this passkey is shown on that device: <ph name="PASSKEY">$2<ex>123456</ex></ph> - </message> - <message name="IDS_ASH_STATUS_TRAY_BLUETOOTH_AUTHORIZE_PAIRING" desc="Bluetooth pairing message typically shown on a request from a device without input or display."> - Bluetooth device "<ph name="DEVICE_NAME">$1<ex>Nexus S</ex></ph>" would like permission to pair. - </message> - <message name="IDS_ASH_STATUS_TRAY_BLUETOOTH_ACCEPT" desc="Message on button to accept Bluetooth pairing request."> - Accept - </message> - <message name="IDS_ASH_STATUS_TRAY_BLUETOOTH_REJECT" desc="Message on button to reject Bluetooth pairing request."> - Reject - </message> - <message name="IDS_ASH_STATUS_TRAY_BLUETOOTH_PAIRED" desc="Notification shown when a new device is paired with the computer."> - Bluetooth device "<ph name="DEVICE_NAME">$1<ex>Nexus S</ex></ph>" has been paired and is now available to all users. You may remove this pairing using Settings. - </message> - - <!-- Ash multi-user warning dialog --> - <message name="IDS_DESKTOP_CASTING_ACTIVE_TITLE" desc="The title for the dialog which tells the user that desktop casting is in progress, asking if the casting should be stopped before switching - or - the switch should be aborted."> - Stop screen sharing? - </message> - <message name="IDS_DESKTOP_CASTING_ACTIVE_MESSAGE" desc="The message for the dialog which tells the user that desktop casting is in progress, asking if the casting should be stopped before switching - or - the switch should be aborted."> - Screen sharing will stop when you switch to another user. Do you want to continue? - </message> - <message name="IDS_DESKTOP_CASTING_ACTIVE_BUTTON_SWITCH_USER" desc="The button label for the dialog which tells the user that desktop casting is in progress. By selecting this button the casting will be stopped and the user will get switched."> - Yes - </message> - <message name="IDS_DESKTOP_CASTING_ACTIVE_BUTTON_ABORT_USER_SWITCH" desc="The button label for the dialog which tells the user that desktop casting is in progress. By selecting this button the user switch will be cancelled."> - No - </message> - - <!-- Status tray screen capture strings. --> - <message name="IDS_ASH_STATUS_TRAY_SCREEN_CAPTURE_STOP" desc="label used for screen capture stop button"> - Stop - </message> - - <!-- Status tray media recording state strings. --> - <message name="IDS_ASH_STATUS_TRAY_MEDIA_RECORDING_AUDIO" desc="label used to indicate that the microphone is used by a background user"> - Microphone is in use. - </message> - <message name="IDS_ASH_STATUS_TRAY_MEDIA_RECORDING_VIDEO" desc="label used to indicate that the camera is used by a background user"> - Camera is in use. - </message> - <message name="IDS_ASH_STATUS_TRAY_MEDIA_RECORDING_AUDIO_VIDEO" desc="label used to indicate that the camera and microphone are used by a background user"> - Camera and microphone are in use. - </message> - - <!-- Status tray screen share strings. --> - <message name="IDS_ASH_STATUS_TRAY_SCREEN_SHARE_STOP" desc="label used for screen sharing stop button"> - Stop - </message> - <message name="IDS_ASH_STATUS_TRAY_SCREEN_SHARE_BEING_HELPED" desc="label for screen sharing notification"> - Sharing control of your screen via Remote Assistance. - </message> - <message name="IDS_ASH_STATUS_TRAY_SCREEN_SHARE_BEING_HELPED_NAME" desc="label for screen sharing notification with name"> - Sharing control of your screen with <ph name="HELPER_NAME">$1<ex>Walder Frey</ex></ph> via Remote Assistance. - </message> - - <!-- Status tray audio strings. --> - <message name="IDS_ASH_STATUS_TRAY_AUDIO_FRONT_MIC" desc="label used for front microphone"> - Front microphone - </message> - - <message name="IDS_ASH_STATUS_TRAY_AUDIO_HEADPHONE" desc="label used for audio headphone device"> - Headphone - </message> - - <message name="IDS_ASH_STATUS_TRAY_AUDIO_INTERNAL_SPEAKER" desc="label used for internal audio speaker"> - Speaker (internal) - </message> - - <message name="IDS_ASH_STATUS_TRAY_AUDIO_INTERNAL_MIC" desc="label used for internal microphone"> - Microphone (internal) - </message> - - <message name="IDS_ASH_STATUS_TRAY_AUDIO_REAR_MIC" desc="label used for rear microphone"> - Rear microphone - </message> - - <message name="IDS_ASH_STATUS_TRAY_AUDIO_USB_DEVICE" desc="label used for usb audio device"> - <ph name="device_name">$1<ex>Headphone</ex></ph> (USB) - </message> - - <message name="IDS_ASH_STATUS_TRAY_AUDIO_BLUETOOTH_DEVICE" desc="label used for bluetooth audio device"> - <ph name="device_name">$1<ex>Headphone</ex></ph> (Bluetooth) - </message> - - <message name="IDS_ASH_STATUS_TRAY_AUDIO_HDMI_DEVICE" desc="label used for hdmi audio device"> - <ph name="device_name">$1<ex>Speaker</ex></ph> (HDMI/DP) - </message> - <message name="IDS_ASH_STATUS_TRAY_AUDIO_MIC_JACK_DEVICE" desc="label used for mic jack device"> - Mic jack - </message> - - <message name="IDS_AURA_SET_DESKTOP_WALLPAPER" desc="The label used for change wallpaper in context menu"> - Set wallpaper... - </message> - - <message name="IDS_ASH_STATUS_TRAY_TRACING" desc="The status tray item indicating that performance tracing is running."> - Performance tracing enabled - </message> - <message name="IDS_ASH_STATUS_TRAY_BRIGHTNESS" desc="The accessible text for the brightness slider."> - Brightness - </message> - <message name="IDS_ASH_STATUS_TRAY_AUDIO" desc="The label used in audio detailed page bottom header of ash tray pop up."> - Audio settings - </message> - <message name="IDS_ASH_STATUS_TRAY_AUDIO_OUTPUT" desc="The label used in audio detailed page for audio output section of ash tray pop up."> - Output - </message> - <message name="IDS_ASH_STATUS_TRAY_AUDIO_INPUT" desc="The label used in audio detailed page for audio input section of ash tray pop up."> - Input - </message> - <message name="IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING" desc="The label used in the tray to show that the current status is mirroring."> - Mirroring to <ph name="DISPLAY_NAME">$1</ph> - </message> - <message name="IDS_ASH_STATUS_TRAY_DISPLAY_MIRROR_EXIT" desc="The label used in the tray to show that mirror mode is exiting."> - Exiting mirror mode - </message> - <message name="IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED" desc="The label used in the tray to show that the current status is extended."> - Extending screen to <ph name="DISPLAY_NAME">$1</ph> - </message> - <message name="IDS_ASH_STATUS_TRAY_DISPLAY_REMOVED" desc="The text of the notification to show when a display is removed."> - Removed display <ph name="DISPLAY_NAME">$1</ph> - </message> - <message name="IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING_NO_INTERNAL" desc="The label used in the tray to show that the current status is mirroring and the device doesn't have the internal display."> - Mirroring - </message> - <message name="IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED_NO_INTERNAL" desc="The label used in the tray to show that the current status is extended and the device doesn't have the internal display."> - Extending screen - </message> - <message name="IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED" desc="The label used in the tray to notify that the display resolution settings has changed."> - <ph name="DISPLAY_NAME">$1</ph> resolution was changed to <ph name="RESOLUTION">$2</ph> - </message> - <message name="IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED_TO_UNSUPPORTED" desc="The label used in the tray to notify that the display resolution settings has changed to an unsupported resolution and the system falls back to another resolution."> - <ph name="DISPLAY_NAME">$1</ph> doesn't support <ph name="SPECIFIED_RESOLUTION">$2<ex>2560x1600</ex></ph>. The resolution was changed to <ph name="FALLBACK_RESOLUTION">$3<ex>1920x1200</ex></ph> - </message> - <message name="IDS_ASH_STATUS_TRAY_DISPLAY_ROTATED" desc="The label used in the tray to notify that the display rotation settings has changed."> - <ph name="DISPLAY_NAME">$1</ph> was rotated to <ph name="ROTATION">$2</ph> - </message> - <message name="IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATED_NAME" desc="Label used to show a display name with annotation (like screen resolution or overscan info)."> - <ph name="DISPLAY_NAME">$1</ph> (<ph name="ANNOTATION">$2</ph>) - </message> - <message name="IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION_OVERSCAN" desc="Label used to describe that the system notice that this display device may have overscan area."> - overscan - </message> - <message name="IDS_ASH_STATUS_TRAY_DISPLAY_STANDARD_ORIENTATION" desc="The default value of display orientation option item."> - 0° - </message> - <message name="IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_90" desc="The value of display orientation option item: 90-degree rotated"> - 90° - </message> - <message name="IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_180" desc="The value of display orientation option item: 180-degree rotated"> - 180° - </message> - <message name="IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270" desc="The value of display orientation option item: 270-degree rotated"> - 270° - </message> - <message name="IDS_ASH_STATUS_TRAY_DISPLAY_UNIFIED" desc="The title of the notification indicating that the system is in unified desktop mode."> - Unified desktop mode - </message> - <message name="IDS_ASH_STATUS_TRAY_DISPLAY_UNIFIED_EXITING" desc="The title of the notification indicating that the system is exiting unified desktop mode."> - Exiting unified desktop mode - </message> - <message name="IDS_ASH_STATUS_TRAY_CAPS_LOCK_ENABLED" desc="The label used for the tray item to indicate caps lock is on and to toggle caps lock by the click."> - CAPS LOCK is on - </message> - <message name="IDS_ASH_STATUS_TRAY_CAPS_LOCK_DISABLED" desc="The label used for the tray item to indicate caps lock is on and to toggle caps lock by the click."> - CAPS LOCK is off - </message> - <message name="IDS_ASH_STATUS_TRAY_CAPS_LOCK_SHORTCUT_SEARCH" desc="The shortcut text used for the caps lock tray item."> - Search - </message> - <message name="IDS_ASH_STATUS_TRAY_CAPS_LOCK_SHORTCUT_SEARCH_OR_SHIFT" desc="The shortcut text used for the caps lock tray item."> - Search or Shift - </message> - <message name="IDS_ASH_STATUS_TRAY_CAPS_LOCK_SHORTCUT_ALT_SEARCH" desc="The shortcut text used for the caps lock tray item."> - Alt+Search - </message> - <message name="IDS_ASH_STATUS_TRAY_CAPS_LOCK_SHORTCUT_ALT_SEARCH_OR_SHIFT" desc="The shortcut text used for the caps lock tray item."> - Alt+Search or Shift - </message> - <message name="IDS_ASH_STATUS_TRAY_CAPS_LOCK_CANCEL_BY_ALT_SEARCH" desc="The message shown on a bubble when caps lock is turned on."> -CAPS LOCK is on. -Press Alt+Search or Shift to cancel. - </message> - <message name="IDS_ASH_STATUS_TRAY_CAPS_LOCK_CANCEL_BY_SEARCH" desc="The message shown on a bubble when caps lock is turned on."> -CAPS LOCK is on. -Press Search or Shift to cancel. - </message> - <message name="IDS_ASH_STATUS_TRAY_IME_TURNED_ON_BUBBLE" desc="The message shown on a bubble when an IME is enabled"> - Your input method has changed to <ph name="INPUT_METHOD_ID">$1<ex>EN</ex></ph>. -Press Shift + Alt to switch. - </message> - <message name="IDS_ASH_STATUS_TRAY_THIRD_PARTY_IME_TURNED_ON_BUBBLE" desc="The message shown on a bubble when a third party IME is enabled"> - Your input method has changed to <ph name="INPUT_METHOD_ID">$1<ex>EN</ex></ph>*(<ph name="BEGIN_LINK"><a target="_blank" href="$2"></ph>3rd party<ph name="END_LINK"></a><ex></a></ex></ph>). -Press Shift + Alt to switch. - </message> - <message name="IDS_ASH_STATUS_TRAY_BATTERY_FULL" desc="The label in the tray dialog to indicate that the battery is full."> - Battery full - </message> - <message name="IDS_ASH_STATUS_TRAY_BATTERY_PERCENT" desc="The label in the tray dialog to show the remaining battery power as a percent."> - <ph name="percentage">{0,number,percent}<ex>56%</ex></ph> remaining - </message> - <message name="IDS_ASH_STATUS_TRAY_BATTERY_TIME_UNTIL_FULL" desc="The label in the tray dialog to show a time estimate until the battery is fully charged."> - <ph name="time">$1<ex>2h 53m</ex></ph> until full - </message> - <message name="IDS_ASH_STATUS_TRAY_BATTERY_CALCULATING" desc="The label in the tray dialog indicating that the time to charge or discharge the battery is being calculated."> - Calculating... - </message> - <message name="IDS_ASH_STATUS_TRAY_BATTERY_TIME_LEFT_SHORT" desc="The label in the tray bubble settings row to show a time estimate until the battery is empty."> - <ph name="time">$1<ex>2:53</ex></ph> left - </message> - <message name="IDS_ASH_STATUS_TRAY_BATTERY_TIME_UNTIL_FULL_SHORT" desc="The label in the tray bubble settings row to show a time estimate until the battery is fully charged."> - <ph name="time">$1<ex>2:53</ex></ph> until full - </message> - <message name="IDS_ASH_STATUS_TRAY_BATTERY_CHARGING_UNRELIABLE" desc="The label in the tray dialog to indicate that battery charging is unreliable."> - Low-power charger - </message> - <message name="IDS_ASH_STATUS_TRAY_BATTERY_FULL_CHARGE_ACCESSIBLE" desc="The message used by accessibility to show battery is fully charged."> - Battery is full. - </message> - <message name="IDS_ASH_STATUS_TRAY_BATTERY_PERCENT_ACCESSIBLE" desc="The message used by accessibility to show battery is discharging."> - Battery is <ph name="percentage">$1<ex>56</ex></ph>% full. - </message> - <message name="IDS_ASH_STATUS_TRAY_BATTERY_PERCENT_CHARGING_ACCESSIBLE" desc="The message used by accessibility to show battery is being charged."> - Battery is <ph name="percentage">$1<ex>56</ex></ph>% full and charging. - </message> - <message name="IDS_ASH_STATUS_TRAY_BATTERY_CALCULATING_ACCESSIBLE" desc="The message used by accessibility to show battery is calculating its time in short message."> - Calculating battery time. - </message> - <message name= "IDS_ASH_STATUS_TRAY_BATTERY_TIME_LEFT_ACCESSIBLE" desc="The message used by accessibility to read remaining battery time until empty."> - Time left until battery is empty, <ph name="time_left">$1<ex>1 hour and 15 minutes</ex></ph> - </message> - <message name= "IDS_ASH_STATUS_TRAY_BATTERY_TIME_UNTIL_FULL_ACCESSIBLE" desc="The message used by accessibility to read the estimated time until full."> - Time remaining until battery is fully charged, <ph name="time_remaining">$1<ex>1 hour and 15 minutes</ex></ph> - </message> - <message name = "IDS_ASH_STATUS_TRAY_BATTERY_TIME_ACCESSIBLE" desc="The message used by accessibility to read battery time, which includes both non-zero hours and minutes."> - <ph name="hour">$1<ex> 1 hour</ex></ph> and <ph name="minute">$2<ex>15 minutes</ex></ph> - </message> - <message name="IDS_ASH_STATUS_TRAY_BATTERY_CHARGING_UNRELIABLE_ACCESSIBLE" desc="The message used by accessibility to indicate that battery charging is unreliable."> - Plugged in to a low-power charger. Battery charging may not be reliable. - </message> - <message name="IDS_ASH_STATUS_TRAY_BUBBLE_SESSION_LENGTH_LIMIT" desc="Tray bubble item shown to inform the user that the session length is limited."> - Session ends in <ph name="session_time_remaining">$1<ex>4 minutes 23 seconds</ex></ph>. - </message> - <message name="IDS_ASH_STATUS_TRAY_NOTIFICATION_SESSION_LENGTH_LIMIT" desc="Notification shown to inform the user that the session length is limited."> - Session ends in <ph name="session_time_remaining">$1<ex>4 minutes 23 seconds</ex></ph>. You will be signed out. - </message> - <message name="IDS_ASH_STATUS_TRAY_ROTATION_LOCK_AUTO" desc="The text shown in the tray menu when rotation is set to auto and the user can enable the rotation lock by tapping."> - Enable rotation lock - </message> - <message name="IDS_ASH_STATUS_TRAY_ROTATION_LOCK_LOCKED" desc="The text shown in the tray menu when rotation is set to locked and tapping will disable the lock."> - Disable rotation lock - </message> - <message name="IDS_ASH_STATUS_TRAY_KEYBOARD_DISABLED" desc="The text shown in the tray menu when the virtual keyboard is disabled."> - On-screen keyboard disabled - </message> - <message name="IDS_ASH_STATUS_TRAY_KEYBOARD_ENABLED" desc="The text shown in the tray menu when the virtual keyboard is enabled."> - On-screen keyboard enabled - </message> - <message name="IDS_ASH_STATUS_TRAY_ENABLE_KEYBOARD" desc="A button label in the tray menu to enable the keyboard."> - Enable on-screen keyboard - </message> - <message name="IDS_ASH_STATUS_TRAY_DISABLE_KEYBOARD" desc="A button label in the tray menu to disable the keyboard."> - Disable on-screen keyboard - </message> - - <!-- Status tray networking strings. --> - <message name="IDS_ASH_STATUS_TRAY_MOBILE_SCANNING" desc="Message when scanning for mobile networks"> - Searching for mobile networks... - </message> - <message name="IDS_ASH_STATUS_TRAY_ETHERNET" desc="The ethernet network device."> - Ethernet - </message> - <message name="IDS_ASH_STATUS_TRAY_INITIALIZING_CELLULAR" desc="Message for the status area when initializing the cellular device."> - Initializing... - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_ACTIVATING" desc="Message for the network tray tooltip and network list when activating a network."> - Activating <ph name="NAME">$1<ex>YBH Cellular</ex></ph> - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_CONNECTED" desc="Message for the network tray tooltip and network list when connected to a network."> - Connected to <ph name="NAME">$1<ex>GoogleGuest</ex></ph> - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_CONNECTING" desc="Message for the network tray tooltip and network list when connecting to a network."> - Connecting to <ph name="NAME">$1<ex>GoogleGuest</ex></ph> - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_RECONNECTING" desc="Message for the network tray tooltip and network list when reconnecting to a network."> - Reconnecting to <ph name="NAME">$1<ex>Company VPN</ex></ph> - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATE" desc="Message for the network list to activate the network."> - Activate <ph name="NETWORKSERVICE">$1<ex>YBH Cellular</ex></ph> - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATING" desc="Message for the network list when activating a network."> - <ph name="NAME">$1<ex>YBH Cellular</ex></ph>: Activating... - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_LIST_CONNECTING" desc="Message for the network list when connecting to a network."> - <ph name="NAME">$1<ex>GoogleGuest</ex></ph>: Connecting... - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_LIST_RECONNECTING" desc="Message for the network list when reconnecting to a network."> - <ph name="NAME">$1<ex>Company VPN</ex></ph>: Reconnecting... - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_NOT_CONNECTED" desc="Description in status area or network list when no network is connected."> - No network - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_PROHIBITED" desc="Tooltip in network list when no network is prohibited by policy."> - This network is disabled by your administrator. - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_PROHIBITED_OTHER" desc="Tooltip in network list for joining other network when connecting unmanaged network is prohibited"> - Connecting to other networks is disabled by your administrator. - </message> - <message name="IDS_ASH_STATUS_TRAY_NO_NETWORKS" desc="The message to display in the network info bubble when it is otherwise empty."> - No network information available - </message> - <message name="IDS_ASH_STATUS_TRAY_NO_MOBILE_NETWORKS" desc="The message to display in the network list when no mobile networks are available."> - No mobile network available - </message> - <message name="IDS_ASH_STATUS_TRAY_VPN_DISCONNECTED" desc="The label used in system tray bubble to display vpn is disconnected."> - VPN disconnected - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_WIFI_ENABLED" desc="The label used in the tray popup to notify that Wi-Fi is turned on."> - Wi-Fi is turned on. - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_WIFI_DISABLED" desc="The label used in the tray popup to notify that Wi-Fi is turned off."> - Wi-Fi is turned off. - </message> - <message name="IDS_ASH_STATUS_TRAY_WIFI_SCANNING_MESSAGE" desc="Scanning for wifi networks"> - Searching for Wi-Fi networks... - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_MOBILE" desc="The label used in the tray popup to separate mobile networks."> - Mobile data - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_WIFI" desc="The label used in the tray popup to separate Wi-Fi networks."> - Wi-Fi - </message> - <message name="IDS_ASH_STATUS_TRAY_NETWORK_TETHER" desc="The label used in the tray popup to separate Instant Tethering networks."> - Instant Tethering - </message> - <message name="IDS_ASH_STATUS_TRAY_ADD_CONNECTION" desc="Title for control to add a new network connection." > - Add connection - </message> - - <message name="IDS_ASH_VIRTUAL_KEYBOARD_TRAY_ACCESSIBLE_NAME" desc="The accessible text for virtual keyboard icon in status tray."> - Show on-screen keyboard - </message> - <message name="IDS_ASH_DISPLAY_FAILURE_ON_MIRRORING" desc="An error message to show that the system failed to enter the mirroring mode."> - Could not mirror displays since no supported resolutions found. Entered extended desktop instead. - </message> - <message name="IDS_ASH_DISPLAY_FAILURE_ON_NON_MIRRORING" desc="An error message to show that the system failed to enter the extended desktop mode or unknown status. Please translate the parenthesized text."> - This monitor isn't getting along with your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> (the monitor is not supported). - </message> - <message name="IDS_ASH_DISPLAY_FAILURE_SEND_FEEDBACK" desc="Button text below error report to allow user to send feedback report."> - Send feedback - </message> - <message name="IDS_ASH_DISPLAY_MIRRORING_NOT_SUPPORTED" desc="A message used to explain that mirroring with more than two displays is not supported."> - Mirroring with more than two displays is not supported. - </message> - <message name="IDS_ASH_DISPLAY_RESOLUTION_CHANGE_ACCEPT" desc="A button label shown in the notification for the resolution change to accept the change"> - Accept - </message> - <message name="IDS_ASH_DISPLAY_RESOLUTION_CHANGE_REVERT" desc="A button label shown in the notification for the resolution change to revert the change"> - Revert - </message> - <message name="IDS_ASH_DISPLAY_RESOLUTION_TIMEOUT" desc="A message for to notify the user about the timeout of display resolution change."> - Reverting to old resolution in <ph name="TIMEOUT_SECONDS">$1</ph> - </message> - <message name="IDS_ASH_LOW_PERIPHERAL_BATTERY_NOTIFICATION_TEXT" desc="The text of the notification when a peripheral device is in low battery condition."> - Battery low (<ph name="percentage">$1<ex>56</ex></ph>%) - </message> - <message name="IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_SUCCESS" desc="The title of the notification when a screenshot was taken."> - Screenshot taken - </message> - <message name="IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_DISABLED" desc="The title of the notification when taking a screenshot failed because screenshots are disabled."> - Screenshots disabled - </message> - <message name="IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_FAIL" desc="The title of the notification when taking a screenshot failed."> - An error occurred - </message> - <message name="IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_SUCCESS" desc="The text of the notification when a screenshot was taken. If clicked, the screenshot file is displayed in the Files app."> - Show in folder - </message> - <message name="IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_DISABLED" desc="The text of the notification when taking a screenshot failed because screenshots are disabled by administrator policy."> - The ability to take screenshots has been disabled by your administrator. - </message> - <message name="IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_FAIL" desc="The text of the notification when taking a screenshot failed."> - Failed to save screenshot - </message> - <message name="IDS_ASH_SIGN_OUT_WARNING_POPUP_TEXT" desc="The text of the popup when the user preses the exit shortcut in chrome-os."> - Press Ctrl+Shift+Q twice to sign out. - </message> - <message name="IDS_ASH_SIGN_OUT_WARNING_POPUP_TEXT_ACCESSIBLE" desc="The message used by accessibility to indicate the content of the popup when the user preses the exit shortcut in chrome-os."> - Press Control Shift Q twice to sign out. - </message> - <message name="IDS_ASH_LOGOUT_CONFIRMATION_TITLE" desc="Dialog title for the logout confirmation dialog."> - Exiting session - </message> - <message name="IDS_ASH_LOGOUT_CONFIRMATION_WARNING" desc="The text for the logout confirmation dialog."> - You will automatically be signed out in <ph name="LOGOUT_TIME_LEFT">$1<ex>5 seconds</ex></ph>. - </message> - <message name="IDS_ASH_LOGOUT_CONFIRMATION_WARNING_NOW" desc="The text for the logout confirmation dialog with no time left."> - You will be signed out now. - </message> - <message name="IDS_ASH_LOGOUT_CONFIRMATION_BUTTON" desc="The text for okay button of the logout confirmation dialog."> - Sign out now - </message> - - <!-- HaTS Strings --> - <message name="IDS_ASH_HATS_NOTIFICATION_TITLE" desc="The title of the notification for Happiness Tracking Survey."> - Help us improve Chromebooks - </message> - <message name="IDS_ASH_HATS_NOTIFICATION_BODY" desc="The body of the notification for Happiness Tracking Survey."> - Let us know how we’re doing by answering a few questions. - </message> - <message name="IDS_ASH_HATS_NOTIFICATION_TAKE_SURVEY_BUTTON" desc="The title of the button in the notification for Happiness Tracking Survey to take the said survey."> - Take survey - </message> - <message name="IDS_ASH_IME_MENU_ACCESSIBLE_NAME" desc="The accessible text for opt-in IME menu icon in status tray."> - IME menu button - </message> - - <!-- Quick Unlock Strings --> - <message name="IDS_ASH_QUICK_UNLOCK_NOTIFICATION_TITLE" desc="The title of the notification for Quick Unlock feature notification."> - QU Feature notification title here - </message> - <message name="IDS_ASH_QUICK_UNLOCK_NOTIFICATION_BODY" desc="The body of the notification for Quick Unlock feature notification."> - QU Feature notification body goes here. QU Feature notification body goes here. QU Feature notification body goes here. QU Feature notification body goes here. QU Feature notification body goes here. - </message> - - <!-- Deprecated Accelerators Messages --> - <!-- Shortcut keys MUST be connected with pluses (e.g. Ctrl+Shift+L). --> - <message name="IDS_SHORTCUT_LOCK_SCREEN_OLD" desc="the old deprecated shortcut to lock the screen."> - Ctrl+Shift+L - </message> - <message name="IDS_SHORTCUT_LOCK_SCREEN_NEW" desc="the new shortcut to lock the screen."> - Search+L - </message> - <message name="IDS_SHORTCUT_TASK_MANAGER_OLD" desc="the old deprecated shortcut to open the task manager."> - Shift+Esc - </message> - <message name="IDS_SHORTCUT_TASK_MANAGER_NEW" desc="the new shortcut to open the task manager."> - Search+Esc - </message> - <message name="IDS_DEPRECATED_LOCK_SCREEN_MSG" - desc="Notification message to tell users about the deprecation of Ctrl+Shift+L shortcut to lock the screen."> - The shortcut to lock the screen has changed. Please use <ph name="NEW_SHORTCUT">$1<ex>Search+L</ex></ph> instead of <ph name="OLD_SHORTCUT">$2<ex>Ctrl+Shift+L</ex></ph>. - </message> - <message name="IDS_DEPRECATED_SHOW_TASK_MANAGER_MSG" - desc="Notification message to tell users about the deprecation of Shift+ESC shortcut to show the task manager."> - The shortcut to open the task manager has changed. Please use <ph name="NEW_SHORTCUT">$1<ex>Search+Esc</ex></ph> instead of <ph name="OLD_SHORTCUT">$2<ex>Shift+Esc</ex></ph>. - </message> - - <!-- Notification that an accelerator was pressed, so the user knows how to toggle it back --> - <message name="IDS_HIGH_CONTRAST_ACCEL_MSG" - desc="Notification message to tell users that they just pressed the Search+Shift+H shortcut to toggle High Contrast Mode, and that pressing it again will toggle it off."> - High Contrast Mode enabled. Press Search+Shift+H again to toggle it off. - </message> -</grit-part>
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index 00c6a35..dd9208d4e 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd
@@ -405,11 +405,620 @@ Toggle window overview </message> - <!-- ChromeOS-specific strings --> - <if expr="chromeos"> - <part file="ash_chromeos_strings.grdp" /> - </if> + <!-- Device types --> + <message name="IDS_ASH_CHROMEBASE" desc="The device name for a Chromebase (versus say Chromebox)"> + Chromebase + </message> + <message name="IDS_ASH_CHROMEBIT" desc="The device name for a Chromebit (versus say Chromebox)"> + Chromebit + </message> + <message name="IDS_ASH_CHROMEBOOK" desc="The device name for a Chromebook (versus say Chromebox)"> + Chromebook + </message> + <message name="IDS_ASH_CHROMEBOX" desc="The device name for a Chromebox (versus say Chromebook)"> + Chromebox + </message> + <message name="IDS_ASH_CHROMEDEVICE" desc="The device name for a generic Chrome device"> + Chrome device + </message> + <!-- Status tray charging strings. --> + <message name="IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_TITLE" desc="The title of a notification indicating that a low-current USB charger has been connected."> + Low-power charger connected + </message> + <message name="IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_MESSAGE" desc="The message body of a notification indicating that a low-current USB charger has been connected."> + Your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> may not charge while it is turned on. Consider using the official charger. + </message> + <message name="IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_MESSAGE_SHORT" desc="The message body of a notification indicating that a low-current USB charger has been connected, short version."> + Your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> may not charge while it is turned on. + </message> + <message name="IDS_ASH_STATUS_TRAY_CHARGING_FROM_DUAL_ROLE_TITLE" desc="The message title of a notification indicating that the device is being charged by a connected dual-role USB Type-C device."> + Charging from <ph name="POWER_SOURCE">$1<ex>USB-C device (left port)</ex></ph> + </message> + <message name="IDS_ASH_STATUS_TRAY_CHARGING_DUAL_ROLE_DEVICE_TITLE" desc="The message title of a notification indicating that the device is charging the connected USB Type-C device."> + Charging <ph name="POWER_SOURCE">$1<ex>USB-C device (left port)</ex></ph> + </message> + <message name="IDS_ASH_STATUS_TRAY_CHARGING_DUAL_ROLE_DEVICES_TITLE" desc="The message title of a notification indicating that the device is charging multiple connected USB Type-C devices."> + Charging connected USB-C devices + </message> + <message name="IDS_ASH_STATUS_TRAY_DUAL_ROLE_MESSAGE" desc="The message body of a notification, which indicates that the user can click the notification to see and change which device is being used as a power source."> + Click for more options + </message> + <message name="IDS_ASH_POWER_SOURCE_PORT_LEFT" desc="The text identifying an external device, when that device is connected to the USB Type-C port on the left side of this device."> + USB-C device (left port) + </message> + <message name="IDS_ASH_POWER_SOURCE_PORT_RIGHT" desc="The text identifying an external device, when that device is connected to the USB Type-C port on the right side of this device."> + USB-C device (right port) + </message> + <message name="IDS_ASH_POWER_SOURCE_PORT_BACK" desc="The text identifying an external device, when that device is connected to the USB Type-C port on the back of this device."> + USB-C device (rear port) + </message> + <message name="IDS_ASH_POWER_SOURCE_PORT_FRONT" desc="The text identifying an external device, when that device is connected to the USB Type-C port on the front of this device."> + USB-C device (front port) + </message> + <message name="IDS_ASH_POWER_SOURCE_PORT_LEFT_FRONT" desc="The text identifying an external device, when that device is connected to a USB Type-C port on the left side of this device. In this case there are two ports on that side, so this text should refer to the front one on that side."> + USB-C device (left side front port) + </message> + <message name="IDS_ASH_POWER_SOURCE_PORT_LEFT_BACK" desc="The text identifying an external device, when that device is connected to a USB Type-C port on the left side of this device. In this case there are two ports on that side, so this text should refer to the back one on that side."> + USB-C device (left side back port) + </message> + <message name="IDS_ASH_POWER_SOURCE_PORT_RIGHT_FRONT" desc="The text identifying an external device, when that device is connected to a USB Type-C port on the right side of this device. In this case there are two ports on that side, so this text should refer to the front one on that side."> + USB-C device (right side front port) + </message> + <message name="IDS_ASH_POWER_SOURCE_PORT_RIGHT_BACK" desc="The text identifying an external device, when that device is connected to a USB Type-C port on the right side of this device. In this case there are two ports on that side, so this text should refer to the back one on that side."> + USB-C device (right side back port) + </message> + <message name="IDS_ASH_POWER_SOURCE_PORT_BACK_LEFT" desc="The text identifying an external device, when that device is connected to a USB Type-C port on the back of this device. In this case there are two ports on the back, so this text should refer to the one on the left."> + USB-C device (left port in the back) + </message> + <message name="IDS_ASH_POWER_SOURCE_PORT_BACK_RIGHT" desc="The text identifying an external device, when that device is connected to a USB Type-C port on the back of this device. In this case there are two ports on the back, so this text should refer to the one on the right."> + USB-C device (right port in the back) + </message> + <message name="IDS_ASH_POWER_SOURCE_PORT_UNKNOWN" desc="The text referring to the device to charge from, when its port location cannot be determined."> + USB-C device + </message> + + <message name="IDS_CHARGER_REPLACEMENT_ACCESSIBILITY_NOTIFICATION" desc="The notification string used in accessibility to tell user to contact Google for replacing the possible bad charger."> + You may have a bad charger. If you live in the US, please call 866-628-1371 in order to receive help and a replacement. If you live in the UK, please call 0800-026-0613. If you live in Ireland, please call 1-800-832-664. If you live in Canada, please call 866-628-1372. If you live in Australia, please call 1-800-067-460. + </message> + + <!-- Status Tray Network strings --> + <message name="IDS_ASH_STATUS_TRAY_NETWORK" desc="The label used in the network dialog header."> + Network + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTED" desc="The label used when a network connection is connected."> + Connected + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTING" desc="The label used when a network connection is connecting."> + Connecting... + </message> + <message name="IDS_ASH_STATUS_TRAY_VPN" desc="The label used in the VPN detailed view header."> + Private network + </message> + <message name="IDS_ASH_STATUS_TRAY_VPN_BUILT_IN_PROVIDER" desc="The name of the built-in VPN provider that supports OpenVPN and L2TP over IPsec."> + OpenVPN / L2TP + </message> + <message name="IDS_ASH_STATUS_TRAY_VPN_DISCONNECT" desc="Message on button that disconnects from the current VPN."> + Disconnect + </message> + <message name="IDS_ASH_STATUS_TRAY_VPN_MONITORED" desc="The label used when a VPN connection may be monitored."> + Connection may be monitored + </message> + <message name="IDS_ASH_STATUS_TRAY_WIFI" desc="The wifi network device."> + Wi-Fi + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_INFO" desc="The accessible text for the network info button."> + Network info + </message> + <message name="IDS_ASH_STATUS_TRAY_IP" desc="The label for the IP address of the network:"> + IP address + </message> + <message name="IDS_ASH_STATUS_TRAY_IPV6" desc="The label for the IP address of the network:"> + IPv6 address + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_SETTINGS" desc="The label used in the settings entry in the network dialog."> + Network settings + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_PROXY_SETTINGS" desc="The label used in the proxy settings entry in the network dialog in the login screen."> + Proxy settings + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_TECHNOLOGY_ENFORCED_BY_POLICY" desc="The label used for tell users that this technology is disabled by the administrator"> + This network setting is disabled by the administrator. + </message> + <message name="IDS_ASH_STATUS_TRAY_ENABLE_WIFI" desc="The label used for the item to enable wifi."> + Enable Wi-Fi + </message> + <message name="IDS_ASH_STATUS_TRAY_DISABLE_WIFI" desc="The label used for the item to disable wifi."> + Disable Wi-Fi + </message> + <message name="IDS_ASH_STATUS_TRAY_OTHER_WIFI" desc="The label used for the item to display other Wi-Fi networks."> + Join other... + </message> + <message name="IDS_ASH_STATUS_TRAY_EXTENSION_CONTROLLED_WIFI" desc="The accessible text for the badge in the detailed network list indicating that the respective network's configuration may be controlled by an extension."> + The extension "<ph name="EXTENSION_NAME">$1<ex>Nexus S</ex></ph>" can help connect to this network. + </message> + <message name="IDS_ASH_STATUS_TRAY_TURN_ON_WIFI" desc="The label used for the item to turn on Wi-Fi networks."> + Turn Wi-Fi on + </message> + <message name="IDS_ASH_STATUS_TRAY_ENABLE_MOBILE" desc="The label used for the item to enable mobile networks."> + Enable mobile data + </message> + <message name="IDS_ASH_STATUS_TRAY_DISABLE_MOBILE" desc="The label used for the item to disable mobile networks."> + Disable mobile data + </message> + <message name="IDS_ASH_STATUS_TRAY_SETUP_MOBILE" desc="The label used for the item to setup mobile network when no SIM card in installed."> + Set up mobile data + </message> + <message name="IDS_ASH_STATUS_TRAY_OTHER_MOBILE" desc="The label used for the item to display other mobile networks."> + Mobile ... + </message> + <message name="IDS_ASH_STATUS_TRAY_MOBILE_VIEW_ACCOUNT" desc="In the network popup bubble, the text of the top-up URL link."> + View mobile account + </message> + <message name="IDS_ASH_STATUS_TRAY_BLUETOOTH_DISCOVERABLE" desc="Notification shown when a Bluetooth adapter is discoverable."> + Your computer is discoverable to nearby Bluetooth devices and will appear as "<ph name="NAME">$1<ex>Chromebook</ex></ph>" with address <ph name="ADDRESS">$2<ex>01:23:45:67:89:0A</ex></ph> + </message> + <message name="IDS_ASH_STATUS_TRAY_BLUETOOTH_DISPLAY_PINCODE" desc="Bluetooth pairing message typically shown on a request from a 2.0 device that has a keyboard."> + Bluetooth device "<ph name="DEVICE_NAME">$1<ex>Nexus S</ex></ph>" would like permission to pair. Please enter this PIN code on that device: <ph name="PINCODE">$2<ex>123456</ex></ph> + </message> + <message name="IDS_ASH_STATUS_TRAY_BLUETOOTH_DISPLAY_PASSKEY" desc="Bluetooth pairing message typically shown on a request from a device that has a keyboard."> + Bluetooth device "<ph name="DEVICE_NAME">$1<ex>Nexus S</ex></ph>" would like permission to pair. Please enter this passkey on that device: <ph name="PASSKEY">$2<ex>123456</ex></ph> + </message> + <message name="IDS_ASH_STATUS_TRAY_BLUETOOTH_CONFIRM_PASSKEY" desc="Bluetooth pairing message typically shown on a request from a device that has a display."> + Bluetooth device "<ph name="DEVICE_NAME">$1<ex>Nexus S</ex></ph>" would like permission to pair. Before accepting, please confirm that this passkey is shown on that device: <ph name="PASSKEY">$2<ex>123456</ex></ph> + </message> + <message name="IDS_ASH_STATUS_TRAY_BLUETOOTH_AUTHORIZE_PAIRING" desc="Bluetooth pairing message typically shown on a request from a device without input or display."> + Bluetooth device "<ph name="DEVICE_NAME">$1<ex>Nexus S</ex></ph>" would like permission to pair. + </message> + <message name="IDS_ASH_STATUS_TRAY_BLUETOOTH_ACCEPT" desc="Message on button to accept Bluetooth pairing request."> + Accept + </message> + <message name="IDS_ASH_STATUS_TRAY_BLUETOOTH_REJECT" desc="Message on button to reject Bluetooth pairing request."> + Reject + </message> + <message name="IDS_ASH_STATUS_TRAY_BLUETOOTH_PAIRED" desc="Notification shown when a new device is paired with the computer."> + Bluetooth device "<ph name="DEVICE_NAME">$1<ex>Nexus S</ex></ph>" has been paired and is now available to all users. You may remove this pairing using Settings. + </message> + + <!-- Ash multi-user warning dialog --> + <message name="IDS_DESKTOP_CASTING_ACTIVE_TITLE" desc="The title for the dialog which tells the user that desktop casting is in progress, asking if the casting should be stopped before switching - or - the switch should be aborted."> + Stop screen sharing? + </message> + <message name="IDS_DESKTOP_CASTING_ACTIVE_MESSAGE" desc="The message for the dialog which tells the user that desktop casting is in progress, asking if the casting should be stopped before switching - or - the switch should be aborted."> + Screen sharing will stop when you switch to another user. Do you want to continue? + </message> + <message name="IDS_DESKTOP_CASTING_ACTIVE_BUTTON_SWITCH_USER" desc="The button label for the dialog which tells the user that desktop casting is in progress. By selecting this button the casting will be stopped and the user will get switched."> + Yes + </message> + <message name="IDS_DESKTOP_CASTING_ACTIVE_BUTTON_ABORT_USER_SWITCH" desc="The button label for the dialog which tells the user that desktop casting is in progress. By selecting this button the user switch will be cancelled."> + No + </message> + + <!-- Status tray screen capture strings. --> + <message name="IDS_ASH_STATUS_TRAY_SCREEN_CAPTURE_STOP" desc="label used for screen capture stop button"> + Stop + </message> + + <!-- Status tray media recording state strings. --> + <message name="IDS_ASH_STATUS_TRAY_MEDIA_RECORDING_AUDIO" desc="label used to indicate that the microphone is used by a background user"> + Microphone is in use. + </message> + <message name="IDS_ASH_STATUS_TRAY_MEDIA_RECORDING_VIDEO" desc="label used to indicate that the camera is used by a background user"> + Camera is in use. + </message> + <message name="IDS_ASH_STATUS_TRAY_MEDIA_RECORDING_AUDIO_VIDEO" desc="label used to indicate that the camera and microphone are used by a background user"> + Camera and microphone are in use. + </message> + + <!-- Status tray screen share strings. --> + <message name="IDS_ASH_STATUS_TRAY_SCREEN_SHARE_STOP" desc="label used for screen sharing stop button"> + Stop + </message> + <message name="IDS_ASH_STATUS_TRAY_SCREEN_SHARE_BEING_HELPED" desc="label for screen sharing notification"> + Sharing control of your screen via Remote Assistance. + </message> + <message name="IDS_ASH_STATUS_TRAY_SCREEN_SHARE_BEING_HELPED_NAME" desc="label for screen sharing notification with name"> + Sharing control of your screen with <ph name="HELPER_NAME">$1<ex>Walder Frey</ex></ph> via Remote Assistance. + </message> + + <!-- Status tray audio strings. --> + <message name="IDS_ASH_STATUS_TRAY_AUDIO_FRONT_MIC" desc="label used for front microphone"> + Front microphone + </message> + + <message name="IDS_ASH_STATUS_TRAY_AUDIO_HEADPHONE" desc="label used for audio headphone device"> + Headphone + </message> + + <message name="IDS_ASH_STATUS_TRAY_AUDIO_INTERNAL_SPEAKER" desc="label used for internal audio speaker"> + Speaker (internal) + </message> + + <message name="IDS_ASH_STATUS_TRAY_AUDIO_INTERNAL_MIC" desc="label used for internal microphone"> + Microphone (internal) + </message> + + <message name="IDS_ASH_STATUS_TRAY_AUDIO_REAR_MIC" desc="label used for rear microphone"> + Rear microphone + </message> + + <message name="IDS_ASH_STATUS_TRAY_AUDIO_USB_DEVICE" desc="label used for usb audio device"> + <ph name="device_name">$1<ex>Headphone</ex></ph> (USB) + </message> + + <message name="IDS_ASH_STATUS_TRAY_AUDIO_BLUETOOTH_DEVICE" desc="label used for bluetooth audio device"> + <ph name="device_name">$1<ex>Headphone</ex></ph> (Bluetooth) + </message> + + <message name="IDS_ASH_STATUS_TRAY_AUDIO_HDMI_DEVICE" desc="label used for hdmi audio device"> + <ph name="device_name">$1<ex>Speaker</ex></ph> (HDMI/DP) + </message> + <message name="IDS_ASH_STATUS_TRAY_AUDIO_MIC_JACK_DEVICE" desc="label used for mic jack device"> + Mic jack + </message> + + <message name="IDS_AURA_SET_DESKTOP_WALLPAPER" desc="The label used for change wallpaper in context menu"> + Set wallpaper... + </message> + + <message name="IDS_ASH_STATUS_TRAY_TRACING" desc="The status tray item indicating that performance tracing is running."> + Performance tracing enabled + </message> + <message name="IDS_ASH_STATUS_TRAY_BRIGHTNESS" desc="The accessible text for the brightness slider."> + Brightness + </message> + <message name="IDS_ASH_STATUS_TRAY_AUDIO" desc="The label used in audio detailed page bottom header of ash tray pop up."> + Audio settings + </message> + <message name="IDS_ASH_STATUS_TRAY_AUDIO_OUTPUT" desc="The label used in audio detailed page for audio output section of ash tray pop up."> + Output + </message> + <message name="IDS_ASH_STATUS_TRAY_AUDIO_INPUT" desc="The label used in audio detailed page for audio input section of ash tray pop up."> + Input + </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING" desc="The label used in the tray to show that the current status is mirroring."> + Mirroring to <ph name="DISPLAY_NAME">$1</ph> + </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_MIRROR_EXIT" desc="The label used in the tray to show that mirror mode is exiting."> + Exiting mirror mode + </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED" desc="The label used in the tray to show that the current status is extended."> + Extending screen to <ph name="DISPLAY_NAME">$1</ph> + </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_REMOVED" desc="The text of the notification to show when a display is removed."> + Removed display <ph name="DISPLAY_NAME">$1</ph> + </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING_NO_INTERNAL" desc="The label used in the tray to show that the current status is mirroring and the device doesn't have the internal display."> + Mirroring + </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED_NO_INTERNAL" desc="The label used in the tray to show that the current status is extended and the device doesn't have the internal display."> + Extending screen + </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED" desc="The label used in the tray to notify that the display resolution settings has changed."> + <ph name="DISPLAY_NAME">$1</ph> resolution was changed to <ph name="RESOLUTION">$2</ph> + </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED_TO_UNSUPPORTED" desc="The label used in the tray to notify that the display resolution settings has changed to an unsupported resolution and the system falls back to another resolution."> + <ph name="DISPLAY_NAME">$1</ph> doesn't support <ph name="SPECIFIED_RESOLUTION">$2<ex>2560x1600</ex></ph>. The resolution was changed to <ph name="FALLBACK_RESOLUTION">$3<ex>1920x1200</ex></ph> + </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_ROTATED" desc="The label used in the tray to notify that the display rotation settings has changed."> + <ph name="DISPLAY_NAME">$1</ph> was rotated to <ph name="ROTATION">$2</ph> + </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATED_NAME" desc="Label used to show a display name with annotation (like screen resolution or overscan info)."> + <ph name="DISPLAY_NAME">$1</ph> (<ph name="ANNOTATION">$2</ph>) + </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION_OVERSCAN" desc="Label used to describe that the system notice that this display device may have overscan area."> + overscan + </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_STANDARD_ORIENTATION" desc="The default value of display orientation option item."> + 0° + </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_90" desc="The value of display orientation option item: 90-degree rotated"> + 90° + </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_180" desc="The value of display orientation option item: 180-degree rotated"> + 180° + </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270" desc="The value of display orientation option item: 270-degree rotated"> + 270° + </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_UNIFIED" desc="The title of the notification indicating that the system is in unified desktop mode."> + Unified desktop mode + </message> + <message name="IDS_ASH_STATUS_TRAY_DISPLAY_UNIFIED_EXITING" desc="The title of the notification indicating that the system is exiting unified desktop mode."> + Exiting unified desktop mode + </message> + <message name="IDS_ASH_STATUS_TRAY_CAPS_LOCK_ENABLED" desc="The label used for the tray item to indicate caps lock is on and to toggle caps lock by the click."> + CAPS LOCK is on + </message> + <message name="IDS_ASH_STATUS_TRAY_CAPS_LOCK_DISABLED" desc="The label used for the tray item to indicate caps lock is on and to toggle caps lock by the click."> + CAPS LOCK is off + </message> + <message name="IDS_ASH_STATUS_TRAY_CAPS_LOCK_SHORTCUT_SEARCH" desc="The shortcut text used for the caps lock tray item."> + Search + </message> + <message name="IDS_ASH_STATUS_TRAY_CAPS_LOCK_SHORTCUT_SEARCH_OR_SHIFT" desc="The shortcut text used for the caps lock tray item."> + Search or Shift + </message> + <message name="IDS_ASH_STATUS_TRAY_CAPS_LOCK_SHORTCUT_ALT_SEARCH" desc="The shortcut text used for the caps lock tray item."> + Alt+Search + </message> + <message name="IDS_ASH_STATUS_TRAY_CAPS_LOCK_SHORTCUT_ALT_SEARCH_OR_SHIFT" desc="The shortcut text used for the caps lock tray item."> + Alt+Search or Shift + </message> + <message name="IDS_ASH_STATUS_TRAY_CAPS_LOCK_CANCEL_BY_ALT_SEARCH" desc="The message shown on a bubble when caps lock is turned on."> + CAPS LOCK is on. + Press Alt+Search or Shift to cancel. + </message> + <message name="IDS_ASH_STATUS_TRAY_CAPS_LOCK_CANCEL_BY_SEARCH" desc="The message shown on a bubble when caps lock is turned on."> + CAPS LOCK is on. + Press Search or Shift to cancel. + </message> + <message name="IDS_ASH_STATUS_TRAY_IME_TURNED_ON_BUBBLE" desc="The message shown on a bubble when an IME is enabled"> + Your input method has changed to <ph name="INPUT_METHOD_ID">$1<ex>EN</ex></ph>. + Press Shift + Alt to switch. + </message> + <message name="IDS_ASH_STATUS_TRAY_THIRD_PARTY_IME_TURNED_ON_BUBBLE" desc="The message shown on a bubble when a third party IME is enabled"> + Your input method has changed to <ph name="INPUT_METHOD_ID">$1<ex>EN</ex></ph>*(<ph name="BEGIN_LINK"><a target="_blank" href="$2"></ph>3rd party<ph name="END_LINK"></a><ex></a></ex></ph>). + Press Shift + Alt to switch. + </message> + <message name="IDS_ASH_STATUS_TRAY_BATTERY_FULL" desc="The label in the tray dialog to indicate that the battery is full."> + Battery full + </message> + <message name="IDS_ASH_STATUS_TRAY_BATTERY_PERCENT" desc="The label in the tray dialog to show the remaining battery power as a percent."> + <ph name="percentage">{0,number,percent}<ex>56%</ex></ph> remaining + </message> + <message name="IDS_ASH_STATUS_TRAY_BATTERY_TIME_UNTIL_FULL" desc="The label in the tray dialog to show a time estimate until the battery is fully charged."> + <ph name="time">$1<ex>2h 53m</ex></ph> until full + </message> + <message name="IDS_ASH_STATUS_TRAY_BATTERY_CALCULATING" desc="The label in the tray dialog indicating that the time to charge or discharge the battery is being calculated."> + Calculating... + </message> + <message name="IDS_ASH_STATUS_TRAY_BATTERY_TIME_LEFT_SHORT" desc="The label in the tray bubble settings row to show a time estimate until the battery is empty."> + <ph name="time">$1<ex>2:53</ex></ph> left + </message> + <message name="IDS_ASH_STATUS_TRAY_BATTERY_TIME_UNTIL_FULL_SHORT" desc="The label in the tray bubble settings row to show a time estimate until the battery is fully charged."> + <ph name="time">$1<ex>2:53</ex></ph> until full + </message> + <message name="IDS_ASH_STATUS_TRAY_BATTERY_CHARGING_UNRELIABLE" desc="The label in the tray dialog to indicate that battery charging is unreliable."> + Low-power charger + </message> + <message name="IDS_ASH_STATUS_TRAY_BATTERY_FULL_CHARGE_ACCESSIBLE" desc="The message used by accessibility to show battery is fully charged."> + Battery is full. + </message> + <message name="IDS_ASH_STATUS_TRAY_BATTERY_PERCENT_ACCESSIBLE" desc="The message used by accessibility to show battery is discharging."> + Battery is <ph name="percentage">$1<ex>56</ex></ph>% full. + </message> + <message name="IDS_ASH_STATUS_TRAY_BATTERY_PERCENT_CHARGING_ACCESSIBLE" desc="The message used by accessibility to show battery is being charged."> + Battery is <ph name="percentage">$1<ex>56</ex></ph>% full and charging. + </message> + <message name="IDS_ASH_STATUS_TRAY_BATTERY_CALCULATING_ACCESSIBLE" desc="The message used by accessibility to show battery is calculating its time in short message."> + Calculating battery time. + </message> + <message name= "IDS_ASH_STATUS_TRAY_BATTERY_TIME_LEFT_ACCESSIBLE" desc="The message used by accessibility to read remaining battery time until empty."> + Time left until battery is empty, <ph name="time_left">$1<ex>1 hour and 15 minutes</ex></ph> + </message> + <message name= "IDS_ASH_STATUS_TRAY_BATTERY_TIME_UNTIL_FULL_ACCESSIBLE" desc="The message used by accessibility to read the estimated time until full."> + Time remaining until battery is fully charged, <ph name="time_remaining">$1<ex>1 hour and 15 minutes</ex></ph> + </message> + <message name = "IDS_ASH_STATUS_TRAY_BATTERY_TIME_ACCESSIBLE" desc="The message used by accessibility to read battery time, which includes both non-zero hours and minutes."> + <ph name="hour">$1<ex> 1 hour</ex></ph> and <ph name="minute">$2<ex>15 minutes</ex></ph> + </message> + <message name="IDS_ASH_STATUS_TRAY_BATTERY_CHARGING_UNRELIABLE_ACCESSIBLE" desc="The message used by accessibility to indicate that battery charging is unreliable."> + Plugged in to a low-power charger. Battery charging may not be reliable. + </message> + <message name="IDS_ASH_STATUS_TRAY_BUBBLE_SESSION_LENGTH_LIMIT" desc="Tray bubble item shown to inform the user that the session length is limited."> + Session ends in <ph name="session_time_remaining">$1<ex>4 minutes 23 seconds</ex></ph>. + </message> + <message name="IDS_ASH_STATUS_TRAY_NOTIFICATION_SESSION_LENGTH_LIMIT" desc="Notification shown to inform the user that the session length is limited."> + Session ends in <ph name="session_time_remaining">$1<ex>4 minutes 23 seconds</ex></ph>. You will be signed out. + </message> + <message name="IDS_ASH_STATUS_TRAY_ROTATION_LOCK_AUTO" desc="The text shown in the tray menu when rotation is set to auto and the user can enable the rotation lock by tapping."> + Enable rotation lock + </message> + <message name="IDS_ASH_STATUS_TRAY_ROTATION_LOCK_LOCKED" desc="The text shown in the tray menu when rotation is set to locked and tapping will disable the lock."> + Disable rotation lock + </message> + <message name="IDS_ASH_STATUS_TRAY_KEYBOARD_DISABLED" desc="The text shown in the tray menu when the virtual keyboard is disabled."> + On-screen keyboard disabled + </message> + <message name="IDS_ASH_STATUS_TRAY_KEYBOARD_ENABLED" desc="The text shown in the tray menu when the virtual keyboard is enabled."> + On-screen keyboard enabled + </message> + <message name="IDS_ASH_STATUS_TRAY_ENABLE_KEYBOARD" desc="A button label in the tray menu to enable the keyboard."> + Enable on-screen keyboard + </message> + <message name="IDS_ASH_STATUS_TRAY_DISABLE_KEYBOARD" desc="A button label in the tray menu to disable the keyboard."> + Disable on-screen keyboard + </message> + + <!-- Status tray networking strings. --> + <message name="IDS_ASH_STATUS_TRAY_MOBILE_SCANNING" desc="Message when scanning for mobile networks"> + Searching for mobile networks... + </message> + <message name="IDS_ASH_STATUS_TRAY_ETHERNET" desc="The ethernet network device."> + Ethernet + </message> + <message name="IDS_ASH_STATUS_TRAY_INITIALIZING_CELLULAR" desc="Message for the status area when initializing the cellular device."> + Initializing... + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_ACTIVATING" desc="Message for the network tray tooltip and network list when activating a network."> + Activating <ph name="NAME">$1<ex>YBH Cellular</ex></ph> + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_CONNECTED" desc="Message for the network tray tooltip and network list when connected to a network."> + Connected to <ph name="NAME">$1<ex>GoogleGuest</ex></ph> + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_CONNECTING" desc="Message for the network tray tooltip and network list when connecting to a network."> + Connecting to <ph name="NAME">$1<ex>GoogleGuest</ex></ph> + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_RECONNECTING" desc="Message for the network tray tooltip and network list when reconnecting to a network."> + Reconnecting to <ph name="NAME">$1<ex>Company VPN</ex></ph> + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATE" desc="Message for the network list to activate the network."> + Activate <ph name="NETWORKSERVICE">$1<ex>YBH Cellular</ex></ph> + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATING" desc="Message for the network list when activating a network."> + <ph name="NAME">$1<ex>YBH Cellular</ex></ph>: Activating... + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_LIST_CONNECTING" desc="Message for the network list when connecting to a network."> + <ph name="NAME">$1<ex>GoogleGuest</ex></ph>: Connecting... + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_LIST_RECONNECTING" desc="Message for the network list when reconnecting to a network."> + <ph name="NAME">$1<ex>Company VPN</ex></ph>: Reconnecting... + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_NOT_CONNECTED" desc="Description in status area or network list when no network is connected."> + No network + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_PROHIBITED" desc="Tooltip in network list when no network is prohibited by policy."> + This network is disabled by your administrator. + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_PROHIBITED_OTHER" desc="Tooltip in network list for joining other network when connecting unmanaged network is prohibited"> + Connecting to other networks is disabled by your administrator. + </message> + <message name="IDS_ASH_STATUS_TRAY_NO_NETWORKS" desc="The message to display in the network info bubble when it is otherwise empty."> + No network information available + </message> + <message name="IDS_ASH_STATUS_TRAY_NO_MOBILE_NETWORKS" desc="The message to display in the network list when no mobile networks are available."> + No mobile network available + </message> + <message name="IDS_ASH_STATUS_TRAY_VPN_DISCONNECTED" desc="The label used in system tray bubble to display vpn is disconnected."> + VPN disconnected + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_WIFI_ENABLED" desc="The label used in the tray popup to notify that Wi-Fi is turned on."> + Wi-Fi is turned on. + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_WIFI_DISABLED" desc="The label used in the tray popup to notify that Wi-Fi is turned off."> + Wi-Fi is turned off. + </message> + <message name="IDS_ASH_STATUS_TRAY_WIFI_SCANNING_MESSAGE" desc="Scanning for wifi networks"> + Searching for Wi-Fi networks... + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_MOBILE" desc="The label used in the tray popup to separate mobile networks."> + Mobile data + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_WIFI" desc="The label used in the tray popup to separate Wi-Fi networks."> + Wi-Fi + </message> + <message name="IDS_ASH_STATUS_TRAY_NETWORK_TETHER" desc="The label used in the tray popup to separate Instant Tethering networks."> + Instant Tethering + </message> + <message name="IDS_ASH_STATUS_TRAY_ADD_CONNECTION" desc="Title for control to add a new network connection." > + Add connection + </message> + + <message name="IDS_ASH_VIRTUAL_KEYBOARD_TRAY_ACCESSIBLE_NAME" desc="The accessible text for virtual keyboard icon in status tray."> + Show on-screen keyboard + </message> + <message name="IDS_ASH_DISPLAY_FAILURE_ON_MIRRORING" desc="An error message to show that the system failed to enter the mirroring mode."> + Could not mirror displays since no supported resolutions found. Entered extended desktop instead. + </message> + <message name="IDS_ASH_DISPLAY_FAILURE_ON_NON_MIRRORING" desc="An error message to show that the system failed to enter the extended desktop mode or unknown status. Please translate the parenthesized text."> + This monitor isn't getting along with your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> (the monitor is not supported). + </message> + <message name="IDS_ASH_DISPLAY_FAILURE_SEND_FEEDBACK" desc="Button text below error report to allow user to send feedback report."> + Send feedback + </message> + <message name="IDS_ASH_DISPLAY_MIRRORING_NOT_SUPPORTED" desc="A message used to explain that mirroring with more than two displays is not supported."> + Mirroring with more than two displays is not supported. + </message> + <message name="IDS_ASH_DISPLAY_RESOLUTION_CHANGE_ACCEPT" desc="A button label shown in the notification for the resolution change to accept the change"> + Accept + </message> + <message name="IDS_ASH_DISPLAY_RESOLUTION_CHANGE_REVERT" desc="A button label shown in the notification for the resolution change to revert the change"> + Revert + </message> + <message name="IDS_ASH_DISPLAY_RESOLUTION_TIMEOUT" desc="A message for to notify the user about the timeout of display resolution change."> + Reverting to old resolution in <ph name="TIMEOUT_SECONDS">$1</ph> + </message> + <message name="IDS_ASH_LOW_PERIPHERAL_BATTERY_NOTIFICATION_TEXT" desc="The text of the notification when a peripheral device is in low battery condition."> + Battery low (<ph name="percentage">$1<ex>56</ex></ph>%) + </message> + <message name="IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_SUCCESS" desc="The title of the notification when a screenshot was taken."> + Screenshot taken + </message> + <message name="IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_DISABLED" desc="The title of the notification when taking a screenshot failed because screenshots are disabled."> + Screenshots disabled + </message> + <message name="IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_FAIL" desc="The title of the notification when taking a screenshot failed."> + An error occurred + </message> + <message name="IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_SUCCESS" desc="The text of the notification when a screenshot was taken. If clicked, the screenshot file is displayed in the Files app."> + Show in folder + </message> + <message name="IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_DISABLED" desc="The text of the notification when taking a screenshot failed because screenshots are disabled by administrator policy."> + The ability to take screenshots has been disabled by your administrator. + </message> + <message name="IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_FAIL" desc="The text of the notification when taking a screenshot failed."> + Failed to save screenshot + </message> + <message name="IDS_ASH_SIGN_OUT_WARNING_POPUP_TEXT" desc="The text of the popup when the user preses the exit shortcut in chrome-os."> + Press Ctrl+Shift+Q twice to sign out. + </message> + <message name="IDS_ASH_SIGN_OUT_WARNING_POPUP_TEXT_ACCESSIBLE" desc="The message used by accessibility to indicate the content of the popup when the user preses the exit shortcut in chrome-os."> + Press Control Shift Q twice to sign out. + </message> + <message name="IDS_ASH_LOGOUT_CONFIRMATION_TITLE" desc="Dialog title for the logout confirmation dialog."> + Exiting session + </message> + <message name="IDS_ASH_LOGOUT_CONFIRMATION_WARNING" desc="The text for the logout confirmation dialog."> + You will automatically be signed out in <ph name="LOGOUT_TIME_LEFT">$1<ex>5 seconds</ex></ph>. + </message> + <message name="IDS_ASH_LOGOUT_CONFIRMATION_WARNING_NOW" desc="The text for the logout confirmation dialog with no time left."> + You will be signed out now. + </message> + <message name="IDS_ASH_LOGOUT_CONFIRMATION_BUTTON" desc="The text for okay button of the logout confirmation dialog."> + Sign out now + </message> + + <!-- HaTS Strings --> + <message name="IDS_ASH_HATS_NOTIFICATION_TITLE" desc="The title of the notification for Happiness Tracking Survey."> + Help us improve Chromebooks + </message> + <message name="IDS_ASH_HATS_NOTIFICATION_BODY" desc="The body of the notification for Happiness Tracking Survey."> + Let us know how we’re doing by answering a few questions. + </message> + <message name="IDS_ASH_HATS_NOTIFICATION_TAKE_SURVEY_BUTTON" desc="The title of the button in the notification for Happiness Tracking Survey to take the said survey."> + Take survey + </message> + <message name="IDS_ASH_IME_MENU_ACCESSIBLE_NAME" desc="The accessible text for opt-in IME menu icon in status tray."> + IME menu button + </message> + + <!-- Quick Unlock Strings --> + <message name="IDS_ASH_QUICK_UNLOCK_NOTIFICATION_TITLE" desc="The title of the notification for Quick Unlock feature notification."> + QU Feature notification title here + </message> + <message name="IDS_ASH_QUICK_UNLOCK_NOTIFICATION_BODY" desc="The body of the notification for Quick Unlock feature notification."> + QU Feature notification body goes here. QU Feature notification body goes here. QU Feature notification body goes here. QU Feature notification body goes here. QU Feature notification body goes here. + </message> + + <!-- Deprecated Accelerators Messages --> + <!-- Shortcut keys MUST be connected with pluses (e.g. Ctrl+Shift+L). --> + <message name="IDS_SHORTCUT_LOCK_SCREEN_OLD" desc="the old deprecated shortcut to lock the screen."> + Ctrl+Shift+L + </message> + <message name="IDS_SHORTCUT_LOCK_SCREEN_NEW" desc="the new shortcut to lock the screen."> + Search+L + </message> + <message name="IDS_SHORTCUT_TASK_MANAGER_OLD" desc="the old deprecated shortcut to open the task manager."> + Shift+Esc + </message> + <message name="IDS_SHORTCUT_TASK_MANAGER_NEW" desc="the new shortcut to open the task manager."> + Search+Esc + </message> + <message name="IDS_DEPRECATED_LOCK_SCREEN_MSG" + desc="Notification message to tell users about the deprecation of Ctrl+Shift+L shortcut to lock the screen."> + The shortcut to lock the screen has changed. Please use <ph name="NEW_SHORTCUT">$1<ex>Search+L</ex></ph> instead of <ph name="OLD_SHORTCUT">$2<ex>Ctrl+Shift+L</ex></ph>. + </message> + <message name="IDS_DEPRECATED_SHOW_TASK_MANAGER_MSG" + desc="Notification message to tell users about the deprecation of Shift+ESC shortcut to show the task manager."> + The shortcut to open the task manager has changed. Please use <ph name="NEW_SHORTCUT">$1<ex>Search+Esc</ex></ph> instead of <ph name="OLD_SHORTCUT">$2<ex>Shift+Esc</ex></ph>. + </message> + + <!-- Notification that an accelerator was pressed, so the user knows how to toggle it back --> + <message name="IDS_HIGH_CONTRAST_ACCEL_MSG" + desc="Notification message to tell users that they just pressed the Search+Shift+H shortcut to toggle High Contrast Mode, and that pressing it again will toggle it off."> + High Contrast Mode enabled. Press Search+Shift+H again to toggle it off. + </message> </messages> </release> </grit>
diff --git a/ash/common/accelerators/accelerator_table.cc b/ash/common/accelerators/accelerator_table.cc index c6f5674..426c5a6 100644 --- a/ash/common/accelerators/accelerator_table.cc +++ b/ash/common/accelerators/accelerator_table.cc
@@ -184,10 +184,10 @@ // notification to be shown to the user. This is to prevent duplication of // same notification. // - The ID of the localized notification message to give the users telling -// them about the deprecation (Add one in |ash_chromeos_strings.grdp|. -// Search for the comment <!-- Deprecated Accelerators Messages -->). +// them about the deprecation (Add one in |ash_strings.grd|. Search for +// the comment <!-- Deprecated Accelerators Messages -->). // - The IDs of the localized old and new shortcut text to be used to fill -// the notification text. Also found in |ash_chromeos_strings.grdp|. +// the notification text. Also found in |ash_strings.grd|. // - {true or false} whether the deprecated accelerator is still enabled (we // don't disable a deprecated accelerator abruptly). // 5- Don't forget to update the keyboard overlay. Find 'shortcut' in the file
diff --git a/ash/common/shelf/overflow_bubble_view.cc b/ash/common/shelf/overflow_bubble_view.cc index 19aa369..637c6e53 100644 --- a/ash/common/shelf/overflow_bubble_view.cc +++ b/ash/common/shelf/overflow_bubble_view.cc
@@ -48,7 +48,7 @@ SetAnchorView(anchor); set_arrow(GetBubbleArrow()); set_mirror_arrow_in_rtl(false); - set_background(NULL); + set_background(nullptr); set_color(kShelfDefaultBaseColor); set_margins(gfx::Insets(kPadding, kPadding, kPadding, kPadding)); // Overflow bubble should not get focus. If it get focus when it is shown,
diff --git a/ash/common/shelf/overflow_button.cc b/ash/common/shelf/overflow_button.cc index 3d57ca0..6db7e08 100644 --- a/ash/common/shelf/overflow_button.cc +++ b/ash/common/shelf/overflow_button.cc
@@ -4,26 +4,17 @@ #include "ash/common/shelf/overflow_button.h" -#include "ash/common/ash_constants.h" -#include "ash/common/material_design/material_design_controller.h" -#include "ash/common/shelf/ink_drop_button_listener.h" #include "ash/common/shelf/shelf_constants.h" #include "ash/common/shelf/shelf_view.h" #include "ash/common/shelf/wm_shelf.h" -#include "ash/common/shelf/wm_shelf_util.h" #include "ash/common/strings/grit/ash_strings.h" -#include "ash/resources/grit/ash_resources.h" #include "ash/resources/vector_icons/vector_icons.h" #include "base/memory/ptr_util.h" -#include "third_party/skia/include/core/SkPath.h" #include "ui/base/l10n/l10n_util.h" -#include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" #include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/skbitmap_operations.h" -#include "ui/gfx/skia_util.h" -#include "ui/gfx/transform.h" #include "ui/views/animation/flood_fill_ink_drop_ripple.h" #include "ui/views/animation/ink_drop_impl.h" #include "ui/views/animation/ink_drop_mask.h" @@ -32,22 +23,16 @@ OverflowButton::OverflowButton(ShelfView* shelf_view, WmShelf* wm_shelf) : CustomButton(nullptr), - bottom_image_(nullptr), shelf_view_(shelf_view), wm_shelf_(wm_shelf), background_color_(kShelfDefaultBaseColor) { DCHECK(shelf_view_); - if (MaterialDesignController::IsShelfMaterial()) { - SetInkDropMode(InkDropMode::ON); - set_ink_drop_base_color(kShelfInkDropBaseColor); - set_ink_drop_visible_opacity(kShelfInkDropVisibleOpacity); - set_hide_ink_drop_when_showing_context_menu(false); - bottom_image_md_ = CreateVectorIcon(kShelfOverflowIcon, kShelfIconColor); - bottom_image_ = &bottom_image_md_; - } else { - bottom_image_ = ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( - IDR_ASH_SHELF_OVERFLOW); - } + + SetInkDropMode(InkDropMode::ON); + set_ink_drop_base_color(kShelfInkDropBaseColor); + set_ink_drop_visible_opacity(kShelfInkDropVisibleOpacity); + set_hide_ink_drop_when_showing_context_menu(false); + bottom_image_ = gfx::CreateVectorIcon(kShelfOverflowIcon, kShelfIconColor); SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY); SetAccessibleName(l10n_util::GetStringUTF16(IDS_ASH_SHELF_OVERFLOW_NAME)); @@ -61,14 +46,10 @@ void OverflowButton::OnOverflowBubbleShown() { AnimateInkDrop(views::InkDropState::ACTIVATED, nullptr); - if (!ash::MaterialDesignController::IsShelfMaterial()) - SchedulePaint(); } void OverflowButton::OnOverflowBubbleHidden() { AnimateInkDrop(views::InkDropState::DEACTIVATED, nullptr); - if (!ash::MaterialDesignController::IsShelfMaterial()) - SchedulePaint(); } void OverflowButton::UpdateShelfItemBackground(SkColor color) { @@ -118,17 +99,10 @@ void OverflowButton::PaintBackground(gfx::Canvas* canvas, const gfx::Rect& bounds) { - if (MaterialDesignController::IsShelfMaterial()) { - cc::PaintFlags flags; - flags.setFlags(cc::PaintFlags::kAntiAlias_Flag); - flags.setColor(background_color_); - canvas->DrawRoundRect(bounds, kOverflowButtonCornerRadius, flags); - } else { - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - const gfx::ImageSkia* background = - rb.GetImageNamed(NonMaterialBackgroundImageId()).ToImageSkia(); - canvas->DrawImageInt(*background, bounds.x(), bounds.y()); - } + cc::PaintFlags flags; + flags.setFlags(cc::PaintFlags::kAntiAlias_Flag); + flags.setColor(background_color_); + canvas->DrawRoundRect(bounds, kOverflowButtonCornerRadius, flags); } void OverflowButton::PaintForeground(gfx::Canvas* canvas, @@ -139,19 +113,19 @@ case SHELF_ALIGNMENT_LEFT: if (left_image_.isNull()) { left_image_ = gfx::ImageSkiaOperations::CreateRotatedImage( - *bottom_image_, SkBitmapOperations::ROTATION_90_CW); + bottom_image_, SkBitmapOperations::ROTATION_90_CW); } image = &left_image_; break; case SHELF_ALIGNMENT_RIGHT: if (right_image_.isNull()) { right_image_ = gfx::ImageSkiaOperations::CreateRotatedImage( - *bottom_image_, SkBitmapOperations::ROTATION_270_CW); + bottom_image_, SkBitmapOperations::ROTATION_270_CW); } image = &right_image_; break; default: - image = bottom_image_; + image = &bottom_image_; break; } @@ -160,46 +134,17 @@ bounds.y() + ((bounds.height() - image->height()) / 2)); } -int OverflowButton::NonMaterialBackgroundImageId() const { - if (shelf_view_->IsShowingOverflowBubble()) - return IDR_AURA_NOTIFICATION_BACKGROUND_PRESSED; - return IDR_AURA_NOTIFICATION_BACKGROUND_NORMAL; -} - gfx::Rect OverflowButton::CalculateButtonBounds() const { ShelfAlignment alignment = wm_shelf_->GetAlignment(); - gfx::Rect bounds(GetContentsBounds()); - if (MaterialDesignController::IsShelfMaterial()) { - // Align the button to the top of a bottom-aligned shelf, to the right edge - // a left-aligned shelf, and to the left edge of a right-aligned shelf. - const int inset = (GetShelfConstant(SHELF_SIZE) - kOverflowButtonSize) / 2; - const int x = alignment == SHELF_ALIGNMENT_LEFT - ? bounds.right() - inset - kOverflowButtonSize - : bounds.x() + inset; - bounds = gfx::Rect(x, bounds.y() + inset, kOverflowButtonSize, - kOverflowButtonSize); - } else { - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - const gfx::ImageSkia* background = - rb.GetImageNamed(NonMaterialBackgroundImageId()).ToImageSkia(); - if (alignment == SHELF_ALIGNMENT_LEFT) { - bounds = - gfx::Rect(bounds.right() - background->width() - kShelfItemInset, - bounds.y() + (bounds.height() - background->height()) / 2, - background->width(), background->height()); - } else if (alignment == SHELF_ALIGNMENT_RIGHT) { - bounds = - gfx::Rect(bounds.x() + kShelfItemInset, - bounds.y() + (bounds.height() - background->height()) / 2, - background->width(), background->height()); - } else { - bounds = - gfx::Rect(bounds.x() + (bounds.width() - background->width()) / 2, - bounds.y() + kShelfItemInset, background->width(), - background->height()); - } - } - return bounds; + gfx::Rect content_bounds = GetContentsBounds(); + // Align the button to the top of a bottom-aligned shelf, to the right edge + // a left-aligned shelf, and to the left edge of a right-aligned shelf. + const int inset = (GetShelfConstant(SHELF_SIZE) - kOverflowButtonSize) / 2; + const int x = alignment == SHELF_ALIGNMENT_LEFT + ? content_bounds.right() - inset - kOverflowButtonSize + : content_bounds.x() + inset; + return gfx::Rect(x, content_bounds.y() + inset, kOverflowButtonSize, + kOverflowButtonSize); } } // namespace ash
diff --git a/ash/common/shelf/overflow_button.h b/ash/common/shelf/overflow_button.h index 503811de..32bf382 100644 --- a/ash/common/shelf/overflow_button.h +++ b/ash/common/shelf/overflow_button.h
@@ -6,6 +6,7 @@ #define ASH_COMMON_SHELF_OVERFLOW_BUTTON_H_ #include "base/macros.h" +#include "ui/gfx/geometry/rect.h" #include "ui/gfx/image/image_skia.h" #include "ui/views/controls/button/custom_button.h" @@ -41,22 +42,11 @@ void PaintBackground(gfx::Canvas* canvas, const gfx::Rect& bounds); void PaintForeground(gfx::Canvas* canvas, const gfx::Rect& bounds); - // Returns the id of the asset to use for the button backgound based on the - // current shelf state. - // TODO(tdanderson): Remove this once the material design shelf is enabled - // by default. See crbug.com/614453. - int NonMaterialBackgroundImageId() const; - // Calculates the bounds of the overflow button based on the shelf alignment. gfx::Rect CalculateButtonBounds() const; - // Used for bottom shelf alignment. |bottom_image_| points to - // |bottom_image_md_| for material design, otherwise it is points to a - // resource owned by the ResourceBundle. - // TODO(tdanderson): Remove the non-md code once the material design shelf is - // enabled by default. See crbug.com/614453. - const gfx::ImageSkia* bottom_image_; - gfx::ImageSkia bottom_image_md_; + // Used for bottom shelf alignment. + gfx::ImageSkia bottom_image_; // Cached rotations of |bottom_image_| used for left and right shelf // alignments.
diff --git a/ash/common/shelf/shelf_button.cc b/ash/common/shelf/shelf_button.cc index 3b4e8a36..2a9d2ad 100644 --- a/ash/common/shelf/shelf_button.cc +++ b/ash/common/shelf/shelf_button.cc
@@ -7,25 +7,19 @@ #include <algorithm> #include "ash/common/ash_constants.h" -#include "ash/common/material_design/material_design_controller.h" #include "ash/common/shelf/ink_drop_button_listener.h" #include "ash/common/shelf/shelf_constants.h" #include "ash/common/shelf/shelf_view.h" #include "ash/common/shelf/wm_shelf.h" -#include "ash/resources/grit/ash_resources.h" #include "base/memory/ptr_util.h" #include "base/time/time.h" #include "skia/ext/image_operations.h" #include "ui/accessibility/ax_node_data.h" -#include "ui/base/resource/resource_bundle.h" #include "ui/compositor/layer.h" -#include "ui/compositor/scoped_layer_animation_settings.h" -#include "ui/events/event_constants.h" #include "ui/gfx/animation/animation_delegate.h" #include "ui/gfx/animation/throb_animation.h" #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/vector2d.h" -#include "ui/gfx/image/image.h" #include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/skbitmap_operations.h" #include "ui/views/animation/ink_drop_impl.h" @@ -53,10 +47,8 @@ // Padding from the edge of the shelf to the application icon when the shelf // is horizontally and vertically aligned, respectively. -const int kIconPaddingHorizontal = 5; -const int kIconPaddingHorizontalMD = 7; -const int kIconPaddingVertical = 6; -const int kIconPaddingVerticalMD = 8; +const int kIconPaddingHorizontal = 7; +const int kIconPaddingVertical = 8; // Paints an activity indicator on |canvas| whose |size| is specified in DIP. void PaintIndicatorOnCanvas(gfx::Canvas* canvas, const gfx::Size& size) { @@ -257,11 +249,9 @@ state_(STATE_NORMAL), destroyed_flag_(nullptr) { SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY); - if (ash::MaterialDesignController::IsShelfMaterial()) { - SetInkDropMode(InkDropMode::ON); - set_ink_drop_base_color(kShelfInkDropBaseColor); - set_ink_drop_visible_opacity(kShelfInkDropVisibleOpacity); - } + SetInkDropMode(InkDropMode::ON); + set_ink_drop_base_color(kShelfInkDropBaseColor); + set_ink_drop_visible_opacity(kShelfInkDropVisibleOpacity); const gfx::ShadowValue kShadows[] = { gfx::ShadowValue(gfx::Vector2d(0, 2), 0, SkColorSetARGB(0x1A, 0, 0, 0)), @@ -403,11 +393,8 @@ const gfx::Rect button_bounds(GetContentsBounds()); WmShelf* wm_shelf = shelf_view_->wm_shelf(); const bool is_horizontal_shelf = wm_shelf->IsHorizontalAlignment(); - const int icon_pad = ash::MaterialDesignController::IsShelfMaterial() - ? (is_horizontal_shelf ? kIconPaddingHorizontalMD - : kIconPaddingVerticalMD) - : (is_horizontal_shelf ? kIconPaddingHorizontal - : kIconPaddingVertical); + const int icon_pad = + is_horizontal_shelf ? kIconPaddingHorizontal : kIconPaddingVertical; int x_offset = is_horizontal_shelf ? 0 : icon_pad; int y_offset = is_horizontal_shelf ? icon_pad : 0; @@ -545,33 +532,18 @@ } void ShelfButton::UpdateBar() { - if (state_ & STATE_HIDDEN) { - bar_->SetVisible(false); - return; - } + bool draw_bar = !(state_ & STATE_HIDDEN) && + (state_ & STATE_ACTIVE || state_ & STATE_ATTENTION || + state_ & STATE_RUNNING); - int bar_id = 0; - if (state_ & (STATE_ACTIVE)) - bar_id = IDR_ASH_SHELF_UNDERLINE_ACTIVE; - else if (state_ & STATE_ATTENTION) - bar_id = IDR_ASH_SHELF_UNDERLINE_ATTENTION; - else if (state_ & STATE_RUNNING) - bar_id = IDR_ASH_SHELF_UNDERLINE_RUNNING; - - if (bar_id != 0) { + if (draw_bar) { WmShelf* wm_shelf = shelf_view_->wm_shelf(); gfx::ImageSkia image; - if (ash::MaterialDesignController::IsShelfMaterial()) { - if (wm_shelf->IsVisible()) { - gfx::Size size(GetShelfConstant(SHELF_BUTTON_SIZE), - GetShelfConstant(SHELF_SIZE)); - gfx::Canvas canvas(size, kIndicatorCanvasScale, true /* is_opaque */); - PaintIndicatorOnCanvas(&canvas, size); - image = gfx::ImageSkia(canvas.ExtractImageRep()); - } - } else { - ResourceBundle* rb = &ResourceBundle::GetSharedInstance(); - image = *rb->GetImageNamed(bar_id).ToImageSkia(); + if (wm_shelf->IsVisible()) { + gfx::Size size(kShelfButtonSize, GetShelfConstant(SHELF_SIZE)); + gfx::Canvas canvas(size, kIndicatorCanvasScale, true /* is_opaque */); + PaintIndicatorOnCanvas(&canvas, size); + image = gfx::ImageSkia(canvas.ExtractImageRep()); } ShelfAlignment shelf_alignment = wm_shelf->GetAlignment(); if (!wm_shelf->IsHorizontalAlignment()) { @@ -598,7 +570,8 @@ } bar_->SchedulePaint(); } - bar_->SetVisible(bar_id != 0 && state_ != STATE_NORMAL); + + bar_->SetVisible(draw_bar); } } // namespace ash
diff --git a/ash/common/shelf/shelf_button.h b/ash/common/shelf/shelf_button.h index d52ef98..8e2bc3d 100644 --- a/ash/common/shelf/shelf_button.h +++ b/ash/common/shelf/shelf_button.h
@@ -9,7 +9,10 @@ #include "base/macros.h" #include "ui/gfx/shadow_value.h" #include "ui/views/controls/button/custom_button.h" -#include "ui/views/controls/image_view.h" + +namespace views { +class ImageView; +} namespace ash { class InkDropButtonListener;
diff --git a/ash/common/shelf/shelf_constants.cc b/ash/common/shelf/shelf_constants.cc index 53b90f4..4c7db93 100644 --- a/ash/common/shelf/shelf_constants.cc +++ b/ash/common/shelf/shelf_constants.cc
@@ -15,6 +15,8 @@ const int kShelfAutoHideSize = 3; const int kShelfItemInset = 3; const SkColor kShelfDefaultBaseColor = SK_ColorBLACK; +const int kShelfButtonSize = 48; +const int kShelfButtonSpacing = 16; const SkColor kShelfButtonActivatedHighlightColor = SkColorSetA(SK_ColorWHITE, 100); const SkColor kShelfInkDropBaseColor = SK_ColorWHITE; @@ -27,8 +29,6 @@ int GetShelfConstant(ShelfConstant shelf_constant) { const int kShelfSize[] = {47, 48, 48}; - const int kShelfButtonSpacing[] = {10, 16, 16}; - const int kShelfButtonSize[] = {44, 48, 48}; const int kShelfInsetsForAutoHide[] = {3, 0, 0}; // TODO(estade): clean this up --- remove unneeded constants and reduce @@ -37,10 +37,6 @@ switch (shelf_constant) { case SHELF_SIZE: return kShelfSize[mode]; - case SHELF_BUTTON_SPACING: - return kShelfButtonSpacing[mode]; - case SHELF_BUTTON_SIZE: - return kShelfButtonSize[mode]; case SHELF_INSETS_FOR_AUTO_HIDE: return kShelfInsetsForAutoHide[mode]; }
diff --git a/ash/common/shelf/shelf_constants.h b/ash/common/shelf/shelf_constants.h index e931194..f1180ae 100644 --- a/ash/common/shelf/shelf_constants.h +++ b/ash/common/shelf/shelf_constants.h
@@ -15,12 +15,6 @@ // width when the shelf is vertical). SHELF_SIZE, - // Size of the space between buttons on the shelf. - SHELF_BUTTON_SPACING, - - // Size allocated for each app button on the shelf. - SHELF_BUTTON_SIZE, - // Insets allocated for shelf when it is auto hidden. SHELF_INSETS_FOR_AUTO_HIDE }; @@ -47,6 +41,12 @@ // applied based on the desired shelf opacity level. ASH_EXPORT extern const SkColor kShelfDefaultBaseColor; +// Size allocated for each app button on the shelf. +ASH_EXPORT extern const int kShelfButtonSize; + +// Size of the space between buttons on the shelf. +ASH_EXPORT extern const int kShelfButtonSpacing; + // Highlight color used for shelf button activated states. // TODO(bruthig|mohsen): Use of this color is temporary. Draw the active state // using the material design ripple animation.
diff --git a/ash/common/shelf/shelf_view.cc b/ash/common/shelf/shelf_view.cc index 9dfc053..8037b53 100644 --- a/ash/common/shelf/shelf_view.cc +++ b/ash/common/shelf/shelf_view.cc
@@ -786,11 +786,9 @@ int x = 0; int y = 0; - int button_size = GetShelfConstant(SHELF_BUTTON_SIZE); - int button_spacing = GetShelfConstant(SHELF_BUTTON_SPACING); - int w = wm_shelf_->PrimaryAxisValue(button_size, width()); - int h = wm_shelf_->PrimaryAxisValue(height(), button_size); + int w = wm_shelf_->PrimaryAxisValue(kShelfButtonSize, width()); + int h = wm_shelf_->PrimaryAxisValue(height(), kShelfButtonSize); for (int i = 0; i < view_model_->view_size(); ++i) { if (i < first_visible_index_) { view_model_->set_ideal_bounds(i, gfx::Rect(x, y, 0, 0)); @@ -798,8 +796,8 @@ } view_model_->set_ideal_bounds(i, gfx::Rect(x, y, w, h)); - x = wm_shelf_->PrimaryAxisValue(x + w + button_spacing, x); - y = wm_shelf_->PrimaryAxisValue(y, y + h + button_spacing); + x = wm_shelf_->PrimaryAxisValue(x + w + kShelfButtonSpacing, x); + y = wm_shelf_->PrimaryAxisValue(y, y + h + kShelfButtonSpacing); } if (is_overflow_mode()) { @@ -812,8 +810,8 @@ x = wm_shelf_->PrimaryAxisValue(end_position, 0); y = wm_shelf_->PrimaryAxisValue(0, end_position); for (int i = view_model_->view_size() - 1; i >= first_panel_index; --i) { - x = wm_shelf_->PrimaryAxisValue(x - w - button_spacing, x); - y = wm_shelf_->PrimaryAxisValue(y, y - h - button_spacing); + x = wm_shelf_->PrimaryAxisValue(x - w - kShelfButtonSpacing, x); + y = wm_shelf_->PrimaryAxisValue(y, y - h - kShelfButtonSpacing); view_model_->set_ideal_bounds(i, gfx::Rect(x, y, w, h)); end_position = wm_shelf_->PrimaryAxisValue(x, y); } @@ -824,7 +822,7 @@ wm_shelf_->PrimaryAxisValue( view_model_->ideal_bounds(last_button_index).right(), view_model_->ideal_bounds(last_button_index).bottom()) + - button_spacing; + kShelfButtonSpacing; int reserved_icon_space = available_size * kReservedNonPanelIconProportion; if (last_icon_position < reserved_icon_space) end_position = last_icon_position; @@ -836,7 +834,7 @@ wm_shelf_->PrimaryAxisValue(height(), h))); last_visible_index_ = - DetermineLastVisibleIndex(end_position - button_spacing); + DetermineLastVisibleIndex(end_position - kShelfButtonSpacing); last_hidden_index_ = DetermineFirstVisiblePanelIndex(end_position) - 1; bool show_overflow = last_visible_index_ < last_button_index || last_hidden_index_ >= first_panel_index; @@ -892,8 +890,8 @@ if (last_visible_index_ >= 0) { // Add more space between last visible item and overflow button. // Without this, two buttons look too close compared with other items. - x = wm_shelf_->PrimaryAxisValue(x + button_spacing, x); - y = wm_shelf_->PrimaryAxisValue(y, y + button_spacing); + x = wm_shelf_->PrimaryAxisValue(x + kShelfButtonSpacing, x); + y = wm_shelf_->PrimaryAxisValue(y, y + kShelfButtonSpacing); } // Set all hidden panel icon positions to be on the overflow button.
diff --git a/ash/resources/ash_resources.grd b/ash/resources/ash_resources.grd index 4e214cb4..401d5c7 100644 --- a/ash/resources/ash_resources.grd +++ b/ash/resources/ash_resources.grd
@@ -14,17 +14,11 @@ SAME CONDITIONALS. --> <structure type="chrome_scaled_image" name="IDR_ASH_SHELF_LIST_BROWSER" file="common/shelf/window_switcher_icon_normal.png" /> <structure type="chrome_scaled_image" name="IDR_ASH_SHELF_LIST_INCOGNITO_BROWSER" file="common/shelf/window_switcher_icon_incognito.png" /> - <structure type="chrome_scaled_image" name="IDR_ASH_SHELF_OVERFLOW" file="common/shelf/shelf_overflow.png" /> - <structure type="chrome_scaled_image" name="IDR_ASH_SHELF_UNDERLINE_ACTIVE" file="common/shelf/shelf_underline_active.png" /> - <structure type="chrome_scaled_image" name="IDR_ASH_SHELF_UNDERLINE_ATTENTION" file="common/shelf/shelf_underline_attention.png" /> - <structure type="chrome_scaled_image" name="IDR_ASH_SHELF_UNDERLINE_RUNNING" file="common/shelf/shelf_underline_running.png" /> <structure type="chrome_scaled_image" name="IDR_ASH_SHELF_ICON_SETTINGS" file="common/shelf/settings_app_icon.png" /> <structure type="chrome_scaled_image" name="IDR_ASH_SHELF_ICON_MD_SETTINGS" file="common/shelf/md_settings_app_icon.png" /> <structure type="chrome_scaled_image" name="IDR_ASH_SHELF_ICON_TASK_MANAGER" file="common/shelf/task_manager.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_MULTI_WINDOW_RESIZE_H" file="common/multi_window_resize_horizontal.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_MULTI_WINDOW_RESIZE_V" file="common/multi_window_resize_vertical.png" /> - <structure type="chrome_scaled_image" name="IDR_AURA_NOTIFICATION_BACKGROUND_NORMAL" file="common/shelf/status_icon_background_normal.png" /> - <structure type="chrome_scaled_image" name="IDR_AURA_NOTIFICATION_BACKGROUND_PRESSED" file="common/shelf/status_icon_background_pressed.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_PHANTOM_WINDOW_BOTTOM" file="common/phantom_window_bottom.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_PHANTOM_WINDOW_BOTTOM_LEFT" file="common/phantom_window_bottom_left.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_PHANTOM_WINDOW_BOTTOM_RIGHT" file="common/phantom_window_bottom_right.png" />
diff --git a/ash/resources/default_100_percent/common/shelf/shelf_overflow.png b/ash/resources/default_100_percent/common/shelf/shelf_overflow.png deleted file mode 100644 index 05cc71d..0000000 --- a/ash/resources/default_100_percent/common/shelf/shelf_overflow.png +++ /dev/null Binary files differ
diff --git a/ash/resources/default_100_percent/common/shelf/shelf_underline_active.png b/ash/resources/default_100_percent/common/shelf/shelf_underline_active.png deleted file mode 100644 index b0e9b53..0000000 --- a/ash/resources/default_100_percent/common/shelf/shelf_underline_active.png +++ /dev/null Binary files differ
diff --git a/ash/resources/default_100_percent/common/shelf/shelf_underline_attention.png b/ash/resources/default_100_percent/common/shelf/shelf_underline_attention.png deleted file mode 100644 index 474987b..0000000 --- a/ash/resources/default_100_percent/common/shelf/shelf_underline_attention.png +++ /dev/null Binary files differ
diff --git a/ash/resources/default_100_percent/common/shelf/shelf_underline_running.png b/ash/resources/default_100_percent/common/shelf/shelf_underline_running.png deleted file mode 100644 index bd03767..0000000 --- a/ash/resources/default_100_percent/common/shelf/shelf_underline_running.png +++ /dev/null Binary files differ
diff --git a/ash/resources/default_100_percent/common/shelf/status_icon_background_normal.png b/ash/resources/default_100_percent/common/shelf/status_icon_background_normal.png deleted file mode 100644 index 565c5010..0000000 --- a/ash/resources/default_100_percent/common/shelf/status_icon_background_normal.png +++ /dev/null Binary files differ
diff --git a/ash/resources/default_100_percent/common/shelf/status_icon_background_pressed.png b/ash/resources/default_100_percent/common/shelf/status_icon_background_pressed.png deleted file mode 100644 index d8cdd77..0000000 --- a/ash/resources/default_100_percent/common/shelf/status_icon_background_pressed.png +++ /dev/null Binary files differ
diff --git a/ash/resources/default_200_percent/common/shelf/shelf_overflow.png b/ash/resources/default_200_percent/common/shelf/shelf_overflow.png deleted file mode 100644 index bddc06cb..0000000 --- a/ash/resources/default_200_percent/common/shelf/shelf_overflow.png +++ /dev/null Binary files differ
diff --git a/ash/resources/default_200_percent/common/shelf/shelf_underline_active.png b/ash/resources/default_200_percent/common/shelf/shelf_underline_active.png deleted file mode 100644 index f89d8f5..0000000 --- a/ash/resources/default_200_percent/common/shelf/shelf_underline_active.png +++ /dev/null Binary files differ
diff --git a/ash/resources/default_200_percent/common/shelf/shelf_underline_attention.png b/ash/resources/default_200_percent/common/shelf/shelf_underline_attention.png deleted file mode 100644 index c971b78..0000000 --- a/ash/resources/default_200_percent/common/shelf/shelf_underline_attention.png +++ /dev/null Binary files differ
diff --git a/ash/resources/default_200_percent/common/shelf/shelf_underline_running.png b/ash/resources/default_200_percent/common/shelf/shelf_underline_running.png deleted file mode 100644 index ede3d2d..0000000 --- a/ash/resources/default_200_percent/common/shelf/shelf_underline_running.png +++ /dev/null Binary files differ
diff --git a/ash/resources/default_200_percent/common/shelf/status_icon_background_normal.png b/ash/resources/default_200_percent/common/shelf/status_icon_background_normal.png deleted file mode 100644 index cc2d1b7..0000000 --- a/ash/resources/default_200_percent/common/shelf/status_icon_background_normal.png +++ /dev/null Binary files differ
diff --git a/ash/resources/default_200_percent/common/shelf/status_icon_background_pressed.png b/ash/resources/default_200_percent/common/shelf/status_icon_background_pressed.png deleted file mode 100644 index 498b80bc..0000000 --- a/ash/resources/default_200_percent/common/shelf/status_icon_background_pressed.png +++ /dev/null Binary files differ
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc index 896586f9..7a1a46e 100644 --- a/ash/shelf/shelf_view_unittest.cc +++ b/ash/shelf/shelf_view_unittest.cc
@@ -630,10 +630,9 @@ int drop_index = 1; gfx::Point drop_point = test_api_->GetButton(drop_index)->GetBoundsInScreen().CenterPoint(); - int item_width = test_api_for_overflow.GetButtonSize(); // To insert at |drop_index|, more smaller x-axis value of |drop_point| // should be used. - gfx::Point modified_drop_point(drop_point.x() - item_width / 4, + gfx::Point modified_drop_point(drop_point.x() - kShelfButtonSize / 4, drop_point.y()); generator.MoveMouseTo(modified_drop_point); test_api_for_overflow.RunMessageLoopUntilAnimationsDone(); @@ -1596,8 +1595,7 @@ int ripped_index = test_for_overflow_view.GetLastVisibleIndex(); gfx::Size bubble_size = test_for_overflow_view.GetPreferredSize(); - int item_width = test_for_overflow_view.GetButtonSize() + - test_for_overflow_view.GetButtonSpacing(); + int item_width = kShelfButtonSize + kShelfButtonSpacing; ui::test::EventGenerator& generator = GetEventGenerator(); ShelfButton* button = test_for_overflow_view.GetButton(ripped_index); @@ -1649,7 +1647,7 @@ test_api_->ShowOverflowBubble(); ASSERT_TRUE(test_api_->IsShowingOverflowBubble()); - int item_width = test_api_->GetButtonSize() + test_api_->GetButtonSpacing(); + int item_width = kShelfButtonSize + kShelfButtonSpacing; OverflowBubbleView* bubble_view = test_api_->overflow_bubble()->bubble_view(); OverflowBubbleViewTestAPI bubble_view_api(bubble_view);
diff --git a/ash/test/shelf_view_test_api.cc b/ash/test/shelf_view_test_api.cc index 92afe5db..6231c15 100644 --- a/ash/test/shelf_view_test_api.cc +++ b/ash/test/shelf_view_test_api.cc
@@ -135,14 +135,6 @@ return shelf_view_->GetPreferredSize(); } -int ShelfViewTestAPI::GetButtonSize() { - return GetShelfConstant(SHELF_BUTTON_SIZE); -} - -int ShelfViewTestAPI::GetButtonSpacing() { - return GetShelfConstant(SHELF_BUTTON_SPACING); -} - int ShelfViewTestAPI::GetMinimumDragDistance() const { return ShelfView::kMinimumDragDistance; }
diff --git a/ash/test/shelf_view_test_api.h b/ash/test/shelf_view_test_api.h index b32121f..be7185b57 100644 --- a/ash/test/shelf_view_test_api.h +++ b/ash/test/shelf_view_test_api.h
@@ -96,12 +96,6 @@ // Returns the preferred size of |shelf_view_|. gfx::Size GetPreferredSize(); - // Returns the button size. - int GetButtonSize(); - - // Returns the button space size. - int GetButtonSpacing(); - // Returns minimum distance before drag starts. int GetMinimumDragDistance() const;
diff --git a/breakpad/symupload.exe b/breakpad/symupload.exe index 9ddf91d..b06a0ce 100755 --- a/breakpad/symupload.exe +++ b/breakpad/symupload.exe Binary files differ
diff --git a/build/android/BUILD.gn b/build/android/BUILD.gn index 6cf8bf1..53366db 100644 --- a/build/android/BUILD.gn +++ b/build/android/BUILD.gn
@@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/android/config.gni") import("//build/config/android/rules.gni") if (enable_java_templates) { @@ -51,6 +52,7 @@ _data += "android_sdk_root=" + rebase_path(android_sdk_root, root_build_dir) + CR _data += "android_sdk_version=$android_sdk_version$CR" + _data += "android_tool_prefix=$android_tool_prefix" write_file("$root_build_dir/build_vars.txt", _data) }
diff --git a/build/android/gyp/javac.py b/build/android/gyp/javac.py index 8ab8ea0..51d1b8dd 100755 --- a/build/android/gyp/javac.py +++ b/build/android/gyp/javac.py
@@ -187,6 +187,9 @@ if any(p in changed_paths for p in classpath_inputs): changed_paths = None + if options.incremental: + pdb_path = options.jar_path + '.pdb' + if incremental: # jmake is a compiler wrapper that figures out the minimal set of .java # files that need to be rebuilt given a set of .java files that have @@ -198,7 +201,6 @@ # .class files are newer than their .java files, and convey to jmake which # sources are stale by having their .class files be missing entirely # (by not extracting them). - pdb_path = options.jar_path + '.pdb' javac_cmd = _ConvertToJMakeArgs(javac_cmd, pdb_path) if srcjars: _FixTempPathsInIncrementalMetadata(pdb_path, temp_dir) @@ -264,7 +266,8 @@ '(http://crbug.com/551449).') os.unlink(pdb_path) attempt_build() - elif incremental: + + if options.incremental and (not java_files or not incremental): # Make sure output exists. build_utils.Touch(pdb_path)
diff --git a/build/android/gyp/util/build_utils.py b/build/android/gyp/util/build_utils.py index 8b39513..7b2f48dd 100644 --- a/build/android/gyp/util/build_utils.py +++ b/build/android/gyp/util/build_utils.py
@@ -224,6 +224,7 @@ if not zipfile.is_zipfile(zip_path): raise Exception('Invalid zip file: %s' % zip_path) + extracted = [] with zipfile.ZipFile(zip_path) as z: for name in z.namelist(): if name.endswith('/'): @@ -244,8 +245,12 @@ dest = os.path.join(path, name) MakeDirectory(os.path.dirname(dest)) os.symlink(z.read(name), dest) + extracted.append(dest) else: z.extract(name, path) + extracted.append(os.path.join(path, name)) + + return extracted def AddToZipHermetic(zip_file, zip_path, src_path=None, data=None,
diff --git a/build/android/resource_sizes.py b/build/android/resource_sizes.py index 6f531f0..976f888 100755 --- a/build/android/resource_sizes.py +++ b/build/android/resource_sizes.py
@@ -9,14 +9,14 @@ and assign size contributions to different classes of file. """ +import argparse import collections +from contextlib import contextmanager import json import logging import operator -import optparse import os import re -import shutil import struct import sys import tempfile @@ -33,6 +33,8 @@ _AAPT_PATH = lazy.WeakConstant(lambda: build_tools.GetPath('aapt')) _GRIT_PATH = os.path.join(host_paths.DIR_SOURCE_ROOT, 'tools', 'grit') +_BUILD_UTILS_PATH = os.path.join( + host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'gyp') # Prepend the grit module from the source tree so it takes precedence over other # grit versions that might present in the search path. @@ -42,6 +44,9 @@ with host_paths.SysPath(host_paths.BUILD_COMMON_PATH): import perf_tests_results_helper # pylint: disable=import-error +with host_paths.SysPath(_BUILD_UTILS_PATH, 1): + from util import build_utils # pylint: disable=import-error + # Python had a bug in zipinfo parsing that triggers on ChromeModern.apk # https://bugs.python.org/issue14315 @@ -114,31 +119,30 @@ } -def _ExtractMainLibSectionSizesFromApk(apk_path, main_lib_path): - tmpdir = tempfile.mkdtemp(suffix='_apk_extract') - grouped_section_sizes = collections.defaultdict(int) - try: - with zipfile.ZipFile(apk_path, 'r') as z: - extracted_lib_path = z.extract(main_lib_path, tmpdir) - section_sizes = _CreateSectionNameSizeMap(extracted_lib_path) - - for group_name, section_names in _READELF_SIZES_METRICS.iteritems(): - for section_name in section_names: - if section_name in section_sizes: - grouped_section_sizes[group_name] += section_sizes.pop(section_name) - - # Group any unknown section headers into the "other" group. - for section_header, section_size in section_sizes.iteritems(): - print "Unknown elf section header:", section_header - grouped_section_sizes['other'] += section_size - - return grouped_section_sizes - finally: - shutil.rmtree(tmpdir) +def _RunReadelf(so_path, options, tools_prefix=''): + return cmd_helper.GetCmdOutput( + [tools_prefix + 'readelf'] + options + [so_path]) -def _CreateSectionNameSizeMap(so_path): - stdout = cmd_helper.GetCmdOutput(['readelf', '-S', '--wide', so_path]) +def _ExtractMainLibSectionSizesFromApk(apk_path, main_lib_path, tools_prefix): + with Unzip(apk_path, filename=main_lib_path) as extracted_lib_path: + grouped_section_sizes = collections.defaultdict(int) + section_sizes = _CreateSectionNameSizeMap(extracted_lib_path, tools_prefix) + for group_name, section_names in _READELF_SIZES_METRICS.iteritems(): + for section_name in section_names: + if section_name in section_sizes: + grouped_section_sizes[group_name] += section_sizes.pop(section_name) + + # Group any unknown section headers into the "other" group. + for section_header, section_size in section_sizes.iteritems(): + print "Unknown elf section header:", section_header + grouped_section_sizes['other'] += section_size + + return grouped_section_sizes + + +def _CreateSectionNameSizeMap(so_path, tools_prefix): + stdout = _RunReadelf(so_path, ['-S', '--wide'], tools_prefix) section_sizes = {} # Matches [ 2] .hash HASH 00000000006681f0 0001f0 003154 04 A 3 0 8 for match in re.finditer(r'\[[\s\d]+\] (\..*)$', stdout, re.MULTILINE): @@ -148,7 +152,14 @@ return section_sizes -def CountStaticInitializers(so_path): +def _ParseLibBuildId(so_path, tools_prefix): + """Returns the Build ID of the given native library.""" + stdout = _RunReadelf(so_path, ['n'], tools_prefix) + match = re.search(r'Build ID: (\w+)', stdout) + return match.group(1) if match else None + + +def CountStaticInitializers(so_path, tools_prefix): # Static initializers expected in official builds. Note that this list is # built using 'nm' on libchrome.so which results from a GCC official build # (i.e. Clang is not supported currently). @@ -163,7 +174,7 @@ # Find the number of files with at least one static initializer. # First determine if we're 32 or 64 bit - stdout = cmd_helper.GetCmdOutput(['readelf', '-h', so_path]) + stdout = _RunReadelf(so_path, ['-h'], tools_prefix) elf_class_line = re.search('Class:.*$', stdout, re.MULTILINE).group(0) elf_class = re.split(r'\W+', elf_class_line)[1] if elf_class == 'ELF32': @@ -175,7 +186,7 @@ # NOTE: this is very implementation-specific and makes assumptions # about how compiler and linker implement global static initializers. si_count = 0 - stdout = cmd_helper.GetCmdOutput(['readelf', '-SW', so_path]) + stdout = _RunReadelf(so_path, ['-SW'], tools_prefix) has_init_array, init_array_size = get_elf_section_size(stdout, 'init_array') if has_init_array: si_count = init_array_size / word_size @@ -183,10 +194,11 @@ return si_count -def GetStaticInitializers(so_path): +def GetStaticInitializers(so_path, tools_prefix): output = cmd_helper.GetCmdOutput([_DUMP_STATIC_INITIALIZERS_PATH, '-d', - so_path]) - return output.splitlines() + so_path, '-t', tools_prefix]) + summary = re.search(r'Found \d+ static initializers in (\d+) files.', output) + return output.splitlines()[:-1], int(summary.group(1)) def _NormalizeResourcesArsc(apk_path): @@ -258,17 +270,6 @@ graph_title, trace_title, [value], units) -def PrintResourceSizes(files, chartjson=None): - """Prints the sizes of each given file. - - Args: - files: List of files to print sizes for. - """ - for f in files: - ReportPerfResult(chartjson, 'ResourceSizes', os.path.basename(f) + ' size', - os.path.getsize(f), 'bytes') - - class _FileGroup(object): """Represents a category that apk files can fall into.""" @@ -311,7 +312,7 @@ return self.ComputeExtractedSize() + self.ComputeZippedSize() -def PrintApkAnalysis(apk_filename, chartjson=None): +def PrintApkAnalysis(apk_filename, tools_prefix, chartjson=None): """Analyse APK to determine size contributions of different file classes.""" file_groups = [] @@ -415,7 +416,7 @@ 'other lib size', secondary_size, 'bytes') main_lib_section_sizes = _ExtractMainLibSectionSizesFromApk( - apk_filename, main_lib_info.filename) + apk_filename, main_lib_info.filename, tools_prefix) for metric_name, size in main_lib_section_sizes.iteritems(): ReportPerfResult(chartjson, apk_basename + '_MainLibInfo', metric_name, size, 'bytes') @@ -583,7 +584,8 @@ return id_name_map, id_header_map -def _PrintStaticInitializersCountFromApk(apk_filename, chartjson=None): +def _PrintStaticInitializersCountFromApk(apk_filename, tools_prefix, + chartjson=None): print 'Finding static initializers (can take a minute)' with zipfile.ZipFile(apk_filename) as z: infolist = z.infolist() @@ -595,7 +597,8 @@ lib_name = os.path.basename(zip_info.filename).replace('crazy.', '') unstripped_path = os.path.join(out_dir, 'lib.unstripped', lib_name) if os.path.exists(unstripped_path): - si_count += _PrintStaticInitializersCount(unstripped_path) + si_count += _PrintStaticInitializersCount( + apk_filename, zip_info.filename, unstripped_path, tools_prefix) else: raise Exception('Unstripped .so not found. Looked here: %s', unstripped_path) @@ -603,33 +606,38 @@ 'count') -def _PrintStaticInitializersCount(so_with_symbols_path): +def _PrintStaticInitializersCount(apk_path, apk_so_name, so_with_symbols_path, + tools_prefix): """Counts the number of static initializers in the given shared library. Additionally, files for which static initializers were found are printed on the standard output. Args: - so_with_symbols_path: Path to the unstripped libchrome.so file. - + apk_path: Path to the apk. + apk_so_name: Name of the so. + so_with_symbols_path: Path to the unstripped libchrome.so file. + tools_prefix: Prefix for arch-specific version of binary utility tools. Returns: The number of static initializers found. """ # GetStaticInitializers uses get-static-initializers.py to get a list of all # static initializers. This does not work on all archs (particularly arm). # TODO(rnephew): Get rid of warning when crbug.com/585588 is fixed. - si_count = CountStaticInitializers(so_with_symbols_path) - static_initializers = GetStaticInitializers(so_with_symbols_path) - static_initializers_count = len(static_initializers) - 1 # Minus summary. - if si_count != static_initializers_count: + with Unzip(apk_path, filename=apk_so_name) as unzipped_so: + _VerifyLibBuildIdsMatch(tools_prefix, unzipped_so, so_with_symbols_path) + readelf_si_count = CountStaticInitializers(unzipped_so, tools_prefix) + sis, dump_si_count = GetStaticInitializers( + so_with_symbols_path, tools_prefix) + if readelf_si_count != dump_si_count: print ('There are %d files with static initializers, but ' - 'dump-static-initializers found %d:' % - (si_count, static_initializers_count)) + 'dump-static-initializers found %d: files' % + (readelf_si_count, dump_si_count)) else: print '%s - Found %d files with static initializers:' % ( - os.path.basename(so_with_symbols_path), si_count) - print '\n'.join(static_initializers) + os.path.basename(so_with_symbols_path), dump_si_count) + print '\n'.join(sis) - return si_count + return readelf_si_count def _FormatBytes(byts): """Pretty-print a number of bytes.""" @@ -666,75 +674,71 @@ 'bytes') -def main(argv): - usage = """Usage: %prog [options] file1 file2 ... +@contextmanager +def Unzip(zip_file, filename=None): + """Utility for temporary use of a single file in a zip archive.""" + with build_utils.TempDir() as unzipped_dir: + unzipped_files = build_utils.ExtractAll( + zip_file, unzipped_dir, True, pattern=filename) + if len(unzipped_files) == 0: + raise Exception( + '%s not found in %s' % (filename, zip_file)) + yield unzipped_files[0] -Pass any number of files to graph their sizes. Any files with the extension -'.apk' will be broken down into their components on a separate graph.""" - option_parser = optparse.OptionParser(usage=usage) - option_parser.add_option('--so-path', - help='Obsolete. Pass .so as positional arg instead.') - option_parser.add_option('--so-with-symbols-path', - help='Mostly obsolete. Use .so within .apk instead.') - option_parser.add_option('--min-pak-resource-size', type='int', - default=20*1024, - help='Minimum byte size of displayed pak resources.') - option_parser.add_option('--build_type', dest='build_type', default='Debug', - help='Obsoleted by --chromium-output-directory.') - option_parser.add_option('--chromium-output-directory', - help='Location of the build artifacts. ' - 'Takes precidence over --build_type.') - option_parser.add_option('--chartjson', action='store_true', - help='Sets output mode to chartjson.') - option_parser.add_option('--output-dir', default='.', - help='Directory to save chartjson to.') - option_parser.add_option('--no-output-dir', action='store_true', - help='Skip all measurements that rely on having ' - 'output-dir') - option_parser.add_option('-d', '--device', - help='Dummy option for perf runner.') - options, args = option_parser.parse_args(argv) - files = args[1:] - chartjson = _BASE_CHART.copy() if options.chartjson else None - constants.SetBuildType(options.build_type) - if options.chromium_output_directory: - constants.SetOutputDirectory(options.chromium_output_directory) - if not options.no_output_dir: +def _VerifyLibBuildIdsMatch(tools_prefix, *so_files): + if len(set(_ParseLibBuildId(f, tools_prefix) for f in so_files)) > 1: + raise Exception('Found differing build ids in output directory and apk. ' + 'Your output directory is likely stale.') + + +def _ReadBuildVars(output_dir): + with open(os.path.join(output_dir, 'build_vars.txt')) as f: + return dict(l.replace('//', '').rstrip().split('=', 1) for l in f) + + +def main(): + argparser = argparse.ArgumentParser(description='Print APK size metrics.') + argparser.add_argument('--min-pak-resource-size', type=int, default=20*1024, + help='Minimum byte size of displayed pak resources.') + argparser.add_argument('--chromium-output-directory', + help='Location of the build artifacts.') + argparser.add_argument('--chartjson', action='store_true', + help='Sets output mode to chartjson.') + argparser.add_argument('--output-dir', default='.', + help='Directory to save chartjson to.') + argparser.add_argument('--no-output-dir', action='store_true', + help='Skip all measurements that rely on having ' + 'output-dir') + argparser.add_argument('-d', '--device', + help='Dummy option for perf runner.') + argparser.add_argument('apk', help='APK file path.') + args = argparser.parse_args() + + chartjson = _BASE_CHART.copy() if args.chartjson else None + + if args.chromium_output_directory: + constants.SetOutputDirectory(args.chromium_output_directory) + if not args.no_output_dir: constants.CheckOutputDirectory() devil_chromium.Initialize() + build_vars = _ReadBuildVars(constants.GetOutDirectory()) + tools_prefix = build_vars['android_tool_prefix'] + else: + tools_prefix = '' - # For backward compatibilty with buildbot scripts, treat --so-path as just - # another file to print the size of. We don't need it for anything special any - # more. - if options.so_path: - files.append(options.so_path) - - if not files: - option_parser.error('Must specify a file') - - if options.so_with_symbols_path: - si_count = _PrintStaticInitializersCount(options.so_with_symbols_path) - ReportPerfResult(chartjson, 'StaticInitializersCount', 'count', si_count, - 'count') - - PrintResourceSizes(files, chartjson=chartjson) - - for f in files: - if f.endswith('.apk'): - PrintApkAnalysis(f, chartjson=chartjson) - _PrintDexAnalysis(f, chartjson=chartjson) - if not options.no_output_dir: - PrintPakAnalysis(f, options.min_pak_resource_size) - if not options.so_with_symbols_path: - _PrintStaticInitializersCountFromApk(f, chartjson=chartjson) - + PrintApkAnalysis(args.apk, tools_prefix, chartjson=chartjson) + _PrintDexAnalysis(args.apk, chartjson=chartjson) + if not args.no_output_dir: + PrintPakAnalysis(args.apk, args.min_pak_resource_size) + _PrintStaticInitializersCountFromApk( + args.apk, tools_prefix, chartjson=chartjson) if chartjson: - results_path = os.path.join(options.output_dir, 'results-chart.json') + results_path = os.path.join(args.output_dir, 'results-chart.json') logging.critical('Dumping json to %s', results_path) with open(results_path, 'w') as json_file: json.dump(chartjson, json_file) if __name__ == '__main__': - sys.exit(main(sys.argv)) + sys.exit(main())
diff --git a/cc/ipc/mojo_compositor_frame_sink.mojom b/cc/ipc/mojo_compositor_frame_sink.mojom index aad4152e..f2b9e44 100644 --- a/cc/ipc/mojo_compositor_frame_sink.mojom +++ b/cc/ipc/mojo_compositor_frame_sink.mojom
@@ -38,16 +38,6 @@ // Notify that the surface is no longer in use (and is okay to be evicted) so // that its resources gets returned in time. EvictFrame(); - - // TODO(staraz): Delete Require() and Satisfy() once surface references - // (CL 2541683004) are ready. - // Add the provided |sequence| as a destruction dependency of the - // surface associated with the provided |local_surface_id|. - Require(cc.mojom.LocalSurfaceId local_surface_id, - cc.mojom.SurfaceSequence sequence); - - // Mark the sequence as satisfied and garbage collect surfaces. - Satisfy(cc.mojom.SurfaceSequence sequence); }; interface MojoCompositorFrameSinkClient {
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h index d2b5c39..396cda82 100644 --- a/cc/layers/layer_impl.h +++ b/cc/layers/layer_impl.h
@@ -394,6 +394,10 @@ return is_drawn_render_surface_layer_list_member_; } + bool IsDrawnScrollbar() { + return ToScrollbarLayer() && is_drawn_render_surface_layer_list_member_; + } + void set_may_contain_video(bool yes) { may_contain_video_ = yes; } bool may_contain_video() const { return may_contain_video_; }
diff --git a/cc/layers/surface_layer.cc b/cc/layers/surface_layer.cc index 768141c..4577a8e 100644 --- a/cc/layers/surface_layer.cc +++ b/cc/layers/surface_layer.cc
@@ -70,7 +70,7 @@ layer_tree_host(), primary_surface_info_.id()); } UpdateDrawsContent(HasDrawableContent()); - SetNeedsPushProperties(); + SetNeedsCommitNoRebuild(); } void SurfaceLayer::SetFallbackSurfaceInfo(const SurfaceInfo& surface_info) { @@ -80,7 +80,7 @@ fallback_reference_returner_ = ref_factory_->CreateReference( layer_tree_host(), fallback_surface_info_.id()); } - SetNeedsPushProperties(); + SetNeedsCommitNoRebuild(); } void SurfaceLayer::SetStretchContentToFillBounds(
diff --git a/cc/surfaces/compositor_frame_sink_support.cc b/cc/surfaces/compositor_frame_sink_support.cc index 6829117..7930d9e 100644 --- a/cc/surfaces/compositor_frame_sink_support.cc +++ b/cc/surfaces/compositor_frame_sink_support.cc
@@ -79,16 +79,6 @@ weak_factory_.GetWeakPtr())); } -void CompositorFrameSinkSupport::Require(const LocalSurfaceId& local_surface_id, - const SurfaceSequence& sequence) { - surface_manager_->RequireSequence(SurfaceId(frame_sink_id_, local_surface_id), - sequence); -} - -void CompositorFrameSinkSupport::Satisfy(const SurfaceSequence& sequence) { - surface_manager_->SatisfySequence(sequence); -} - void CompositorFrameSinkSupport::UpdateSurfaceReferences( const SurfaceId& last_surface_id, const LocalSurfaceId& local_surface_id) {
diff --git a/cc/surfaces/compositor_frame_sink_support.h b/cc/surfaces/compositor_frame_sink_support.h index ac60f67..88bf5b8f00 100644 --- a/cc/surfaces/compositor_frame_sink_support.h +++ b/cc/surfaces/compositor_frame_sink_support.h
@@ -50,9 +50,6 @@ void SetNeedsBeginFrame(bool needs_begin_frame); void SubmitCompositorFrame(const LocalSurfaceId& local_surface_id, CompositorFrame frame); - void Require(const LocalSurfaceId& local_surface_id, - const SurfaceSequence& sequence); - void Satisfy(const SurfaceSequence& sequence); void RequestCopyOfSurface(std::unique_ptr<CopyOutputRequest> request); void ForceReclaimResources();
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index ad42f70..fa252c5a0 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc
@@ -162,15 +162,10 @@ return false; auto* property_trees = child->layer_tree_impl()->property_trees(); - auto ancestor_scroll_id = - property_trees->layer_id_to_scroll_node_index.find(ancestor->id()); - if (ancestor_scroll_id == property_trees->layer_id_to_scroll_node_index.end()) - return false; - ScrollTree& scroll_tree = property_trees->scroll_tree; for (ScrollNode* scroll_node = scroll_tree.Node(child->scroll_tree_index()); scroll_node; scroll_node = scroll_tree.parent(scroll_node)) { - if (scroll_node->id == ancestor_scroll_id->second) + if (scroll_node->id == ancestor->scroll_tree_index()) return true; } return false; @@ -2634,28 +2629,6 @@ return active_tree_->LayerById(impl_scroll_node->owning_layer_id); } -static bool IsClosestScrollAncestor(LayerImpl* child, - LayerImpl* scroll_ancestor) { - DCHECK(scroll_ancestor); - if (!child) - return false; - - auto* property_trees = child->layer_tree_impl()->property_trees(); - auto ancestor_scroll_id = - property_trees->layer_id_to_scroll_node_index.find(scroll_ancestor->id()); - if (ancestor_scroll_id == property_trees->layer_id_to_scroll_node_index.end()) - return false; - - ScrollTree& scroll_tree = property_trees->scroll_tree; - ScrollNode* scroll_node = scroll_tree.Node(child->scroll_tree_index()); - for (; scroll_tree.parent(scroll_node); - scroll_node = scroll_tree.parent(scroll_node)) { - if (scroll_node->scrollable) - return scroll_node->id == ancestor_scroll_id->second; - } - return false; -} - InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBeginImpl( ScrollState* scroll_state, LayerImpl* scrolling_layer_impl, @@ -2729,11 +2702,7 @@ active_tree_->FindLayerThatIsHitByPoint(device_viewport_point); if (layer_impl) { - LayerImpl* scroll_layer_impl = - active_tree_->FindFirstScrollingLayerOrScrollbarLayerThatIsHitByPoint( - device_viewport_point); - if (scroll_layer_impl && - !IsClosestScrollAncestor(layer_impl, scroll_layer_impl)) { + if (!IsInitialScrollHitTestReliable(layer_impl, device_viewport_point)) { scroll_status.thread = SCROLL_UNKNOWN; scroll_status.main_thread_scrolling_reasons = MainThreadScrollingReason::kFailedHitTest; @@ -2759,6 +2728,46 @@ return ScrollBeginImpl(scroll_state, scrolling_layer_impl, type); } +// Some initial scroll tests are known to be unreliable and require falling +// back to main thread scrolling. +bool LayerTreeHostImpl::IsInitialScrollHitTestReliable( + LayerImpl* layer_impl, + const gfx::PointF& device_viewport_point) { + LayerImpl* first_scrolling_layer_or_drawn_scrollbar = + active_tree_->FindFirstScrollingLayerOrDrawnScrollbarThatIsHitByPoint( + device_viewport_point); + if (!first_scrolling_layer_or_drawn_scrollbar) + return true; + + ScrollNode* closest_scroll_node = nullptr; + auto& scroll_tree = active_tree_->property_trees()->scroll_tree; + ScrollNode* scroll_node = scroll_tree.Node(layer_impl->scroll_tree_index()); + for (; scroll_tree.parent(scroll_node); + scroll_node = scroll_tree.parent(scroll_node)) { + if (scroll_node->scrollable) { + closest_scroll_node = scroll_node; + break; + } + } + if (!closest_scroll_node) + return false; + + // If |first_scrolling_layer_or_drawn_scrollbar| is scrollable, it will + // create a scroll node. If this scroll node corresponds to first scrollable + // ancestor along the scroll tree for |layer_impl|, the hit test has not + // escaped to other areas of the scroll tree and is reliable. + if (first_scrolling_layer_or_drawn_scrollbar->scrollable()) { + return closest_scroll_node->id == + first_scrolling_layer_or_drawn_scrollbar->scroll_tree_index(); + } + + // If |first_scrolling_layer_or_drawn_scrollbar| is not scrollable, it must + // be a drawn scrollbar. These hit tests require falling back to main-thread + // scrolling. + DCHECK(first_scrolling_layer_or_drawn_scrollbar->IsDrawnScrollbar()); + return false; +} + InputHandler::ScrollStatus LayerTreeHostImpl::ScrollAnimatedBegin( const gfx::Point& viewport_point) { InputHandler::ScrollStatus scroll_status;
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h index b54dcbb..4f62793 100644 --- a/cc/trees/layer_tree_host_impl.h +++ b/cc/trees/layer_tree_host_impl.h
@@ -657,6 +657,7 @@ ScrollState* scroll_state, LayerImpl* scrolling_layer_impl, InputHandler::ScrollInputType type); + bool IsInitialScrollHitTestReliable(LayerImpl* layer, const gfx::PointF&); void DistributeScrollDelta(ScrollState* scroll_state); bool AnimatePageScale(base::TimeTicks monotonic_time);
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index a82de3169..4f9c6ee 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -1018,6 +1018,66 @@ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); } +TEST_F(LayerTreeHostImplTest, ScrolledOverlappingDrawnScrollbarLayer) { + LayerTreeImpl* layer_tree_impl = host_impl_->active_tree(); + gfx::Size content_size = gfx::Size(360, 600); + gfx::Size scroll_content_size = gfx::Size(345, 3800); + gfx::Size scrollbar_size = gfx::Size(15, 600); + + host_impl_->SetViewportSize(content_size); + std::unique_ptr<LayerImpl> root = LayerImpl::Create(layer_tree_impl, 1); + root->SetBounds(content_size); + root->SetPosition(gfx::PointF()); + + std::unique_ptr<LayerImpl> clip = LayerImpl::Create(layer_tree_impl, 2); + clip->SetBounds(content_size); + clip->SetPosition(gfx::PointF()); + + std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 3); + scroll->SetBounds(scroll_content_size); + scroll->SetScrollClipLayer(clip->id()); + scroll->SetDrawsContent(true); + + std::unique_ptr<SolidColorScrollbarLayerImpl> drawn_scrollbar = + SolidColorScrollbarLayerImpl::Create(layer_tree_impl, 4, VERTICAL, 10, 0, + false, true); + drawn_scrollbar->SetBounds(scrollbar_size); + drawn_scrollbar->SetPosition(gfx::PointF(345, 0)); + drawn_scrollbar->SetScrollLayerId(scroll->id()); + drawn_scrollbar->SetDrawsContent(true); + drawn_scrollbar->test_properties()->opacity = 1.f; + + std::unique_ptr<LayerImpl> squash = LayerImpl::Create(layer_tree_impl, 5); + squash->SetBounds(gfx::Size(140, 300)); + squash->SetPosition(gfx::PointF(220, 0)); + squash->SetDrawsContent(true); + + scroll->test_properties()->AddChild(std::move(drawn_scrollbar)); + scroll->test_properties()->AddChild(std::move(squash)); + clip->test_properties()->AddChild(std::move(scroll)); + root->test_properties()->AddChild(std::move(clip)); + + layer_tree_impl->SetRootLayerForTesting(std::move(root)); + layer_tree_impl->BuildPropertyTreesForTesting(); + layer_tree_impl->DidBecomeActive(); + + // The point hits squash layer and also scrollbar layer, but because the + // scrollbar layer is a drawn scrollbar, we cannot scroll on the impl thread. + auto status = host_impl_->ScrollBegin(BeginState(gfx::Point(350, 150)).get(), + InputHandler::WHEEL); + EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread); + EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest, + status.main_thread_scrolling_reasons); + + // The point hits the drawn scrollbar layer completely and should not scroll + // on the impl thread. + status = host_impl_->ScrollBegin(BeginState(gfx::Point(350, 500)).get(), + InputHandler::WHEEL); + EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread); + EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest, + status.main_thread_scrolling_reasons); +} + TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionBasic) { SetupScrollAndContentsLayers(gfx::Size(200, 200)); host_impl_->SetViewportSize(gfx::Size(100, 100));
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc index 5d91fc7..fae5f7c 100644 --- a/cc/trees/layer_tree_impl.cc +++ b/cc/trees/layer_tree_impl.cc
@@ -1929,32 +1929,26 @@ } } -static bool IsScrollableOrDrawnScrollbarLayer(LayerImpl* layer) { - return layer->scrollable() || - (layer->ToScrollbarLayer() && - layer->is_drawn_render_surface_layer_list_member()); -} - -struct FindScrollingLayerOrScrollbarLayerFunctor { +struct FindScrollingLayerOrDrawnScrollbarFunctor { bool operator()(LayerImpl* layer) const { - return IsScrollableOrDrawnScrollbarLayer(layer); + return layer->scrollable() || layer->IsDrawnScrollbar(); } }; LayerImpl* -LayerTreeImpl::FindFirstScrollingLayerOrScrollbarLayerThatIsHitByPoint( +LayerTreeImpl::FindFirstScrollingLayerOrDrawnScrollbarThatIsHitByPoint( const gfx::PointF& screen_space_point) { FindClosestMatchingLayerState state; LayerImpl* root_layer = layer_list_.empty() ? nullptr : layer_list_[0]; FindClosestMatchingLayer(screen_space_point, root_layer, - FindScrollingLayerOrScrollbarLayerFunctor(), &state); + FindScrollingLayerOrDrawnScrollbarFunctor(), &state); return state.closest_match; } struct HitTestVisibleScrollableOrTouchableFunctor { bool operator()(LayerImpl* layer) const { - return layer->is_drawn_render_surface_layer_list_member() || - IsScrollableOrDrawnScrollbarLayer(layer) || + return layer->scrollable() || + layer->is_drawn_render_surface_layer_list_member() || !layer->touch_event_handler_region().IsEmpty(); } };
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h index a12b6b56..df46775 100644 --- a/cc/trees/layer_tree_impl.h +++ b/cc/trees/layer_tree_impl.h
@@ -392,7 +392,7 @@ void RemoveSurfaceLayer(LayerImpl* layer); const LayerImplList& SurfaceLayers() const { return surface_layers_; } - LayerImpl* FindFirstScrollingLayerOrScrollbarLayerThatIsHitByPoint( + LayerImpl* FindFirstScrollingLayerOrDrawnScrollbarThatIsHitByPoint( const gfx::PointF& screen_space_point); LayerImpl* FindLayerThatIsHitByPoint(const gfx::PointF& screen_space_point);
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn index e723038..197a7f6 100644 --- a/chrome/BUILD.gn +++ b/chrome/BUILD.gn
@@ -273,8 +273,12 @@ # Only ChromeOS has precompiled Flash that needs to get copied to the output # directory. On other platforms, Flash is either component-updated only or # not supported at all. + # As well, the scripts to sign files for Widevine need to be copied to the + # output directory so that the scripts can be run on the executables after + # stripping. if (is_chromeos) { deps += [ "//third_party/adobe/flash:flapper_binaries" ] + data_deps += [ "//third_party/widevine/cdm:widevine_signature_scripts" ] } data_deps += [ "//third_party/widevine/cdm:widevinecdmadapter" ]
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index a3587b9..59e5f97 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn
@@ -471,8 +471,10 @@ "//content/test/data/android/geolocation.html", "//content/test/data/android/media_permissions.html", "//content/test/data/android/webshare.html", + "//content/test/data/media/bear.webm", "//content/test/data/media/getusermedia.html", "//content/test/data/media/session/", + "//content/test/data/media/video-player.html", "//content/test/data/media/webrtc_test_utilities.js", ] } @@ -800,10 +802,12 @@ ":chrome_test_java", "//chrome/android/webapk/libs/runtime_library:runtime_library_javatests", "//chrome/android/webapk/shell_apk:shell_apk_javatests", + "//chrome/android/webapk/shell_apk:webapk_javatests", "//third_party/android_support_test_runner:runner_java", ] additional_apks = [ "//chrome/android/webapk/libs/runtime_library/javatests/apk_with_webapk_service:apk_with_webapk_service", + "//chrome/android/webapk/shell_apk:webapk_javatests", "//chrome/android/webapk/shell_apk/javatests/dex_optimizer:dex_optimizer_apk", "//chrome/test/android/chrome_public_test_support:chrome_public_test_support_apk", "//net/android:net_test_support_apk",
diff --git a/chrome/android/java/res/layout/chrome_home_new_tab_page.xml b/chrome/android/java/res/layout/chrome_home_new_tab_page.xml new file mode 100644 index 0000000..4b7a446 --- /dev/null +++ b/chrome/android/java/res/layout/chrome_home_new_tab_page.xml
@@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2017 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. --> + +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:chrome="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:focusable="true" + android:focusableInTouchMode="true" + android:background="@color/ntp_bg" > + + <org.chromium.chrome.browser.widget.TintedImageButton + android:id="@+id/close_button" + android:layout_width="40dp" + android:layout_height="40dp" + android:layout_alignParentTop="true" + android:layout_alignParentStart="true" + android:src="@drawable/btn_close" + android:scaleType="center" + android:background="@android:color/transparent" + chrome:tint="@color/dark_mode_tint" /> + + <View + android:id="@+id/centered_view" + android:layout_width="match_parent" + android:layout_height="30dp" + android:layout_centerInParent="true" /> + + <org.chromium.chrome.browser.ntp.LogoView + android:id="@+id/search_provider_logo" + android:layout_width="match_parent" + android:layout_height="@dimen/ntp_logo_height" + android:padding="16dp" + android:layout_marginEnd="16dp" + android:layout_marginBottom="16dp" + android:layout_marginTop="16dp" + android:layout_above="@id/centered_view" /> + +</RelativeLayout>
diff --git a/chrome/android/java/res/layout/physical_web_list_item_nearby_url.xml b/chrome/android/java/res/layout/physical_web_list_item_nearby_url.xml deleted file mode 100644 index af10e15..0000000 --- a/chrome/android/java/res/layout/physical_web_list_item_nearby_url.xml +++ /dev/null
@@ -1,58 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2015 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. --> - -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:padding="16dp" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - <ImageView - android:id="@+id/nearby_urls_icon" - android:layout_width="32dp" - android:layout_height="32dp" - android:layout_marginTop="4dp" - android:layout_alignParentTop="true" - android:layout_alignParentStart="true" - android:contentDescription="@null"> - </ImageView> - <TextView - android:id="@+id/nearby_urls_title" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="48dp" - android:layout_alignParentTop="true" - android:layout_alignParentStart="true" - android:textSize="16sp" - android:textColor="@color/physical_web_header_color" - android:singleLine="true" - android:ellipsize="end"> - </TextView> - <TextView - android:id="@+id/nearby_urls_url" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="4dp" - android:layout_marginStart="48dp" - android:layout_below="@id/nearby_urls_title" - android:layout_alignParentStart="true" - android:textSize="12sp" - android:textColor="@color/physical_web_url_color" - android:singleLine="true" - android:ellipsize="end"> - </TextView> - <TextView - android:id="@+id/nearby_urls_description" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="12dp" - android:layout_marginStart="48dp" - android:layout_below="@id/nearby_urls_url" - android:layout_alignParentStart="true" - android:textSize="14sp" - android:textColor="@color/physical_web_url_description_color" - android:lines="2" - android:ellipsize="end" - android:lineSpacingExtra="1.5dp"> - </TextView> -</RelativeLayout>
diff --git a/chrome/android/java/res/layout/physical_web_list_urls_activity.xml b/chrome/android/java/res/layout/physical_web_list_urls_activity.xml deleted file mode 100644 index 74cb500..0000000 --- a/chrome/android/java/res/layout/physical_web_list_urls_activity.xml +++ /dev/null
@@ -1,50 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2015 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. --> - -<org.chromium.chrome.browser.physicalweb.SwipeRefreshWidget - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/physical_web_swipe_refresh_widget" - android:layout_width="match_parent" - android:layout_height="match_parent" > - - <FrameLayout - android:layout_height="match_parent" - android:layout_width="match_parent" > - - <ListView - android:id="@+id/physical_web_urls_list" - android:layout_height="match_parent" - android:layout_width="match_parent" - android:divider="@null" /> - - <RelativeLayout - android:id="@+id/physical_web_empty" - android:layout_height="match_parent" - android:layout_width="match_parent" > - - <TextView - android:id="@+id/physical_web_empty_list_text" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:layout_centerInParent="true" - android:text="@string/physical_web_empty_list_scanning" - android:textSize="16sp" - android:textColor="@color/light_normal_color" /> - - <ImageView - android:id="@+id/physical_web_logo" - android:layout_height="140dp" - android:layout_width="140dp" - android:layout_marginBottom="5dp" - android:layout_above="@id/physical_web_empty_list_text" - android:layout_centerHorizontal="true" - android:src="@drawable/physical_web_logo" - android:contentDescription="@null" /> - - </RelativeLayout> - - </FrameLayout> - -</org.chromium.chrome.browser.physicalweb.SwipeRefreshWidget>
diff --git a/chrome/android/java/res/layout/physical_web_optin.xml b/chrome/android/java/res/layout/physical_web_optin.xml deleted file mode 100644 index ced039c..0000000 --- a/chrome/android/java/res/layout/physical_web_optin.xml +++ /dev/null
@@ -1,95 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2015 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. --> - -<merge - xmlns:android="http://schemas.android.com/apk/res/android" > - - <ScrollView - android:layout_height="match_parent" - android:layout_width="match_parent" - android:layout_marginBottom="57dp" - android:fillViewport="true" - android:scrollbarStyle="outsideOverlay" > - - <LinearLayout - android:layout_height="wrap_content" - android:layout_width="match_parent" - android:paddingStart="40dp" - android:paddingEnd="40dp" - android:gravity="center" - android:orientation="vertical" > - - <ImageView - android:layout_height="140dp" - android:layout_width="140dp" - android:src="@drawable/physical_web_logo" - android:contentDescription="@null" /> - <TextView - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:layout_marginTop="10dp" - android:layout_marginBottom="20dp" - android:text="@string/physical_web_optin_title" - android:textSize="24sp" - android:textColor="@android:color/black" /> - <TextView - android:id="@+id/physical_web_optin_description" - android:layout_height="wrap_content" - android:layout_width="match_parent" - android:text="@string/physical_web_optin_description" - android:textSize="16sp" - android:textColor="@color/light_normal_color" - android:textColorLink="@color/light_active_color" /> - - </LinearLayout> - - </ScrollView> - - <View - style="@style/ButtonBarTopDivider" - android:layout_gravity="bottom" - android:layout_marginBottom="56dp" /> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="56dp" - android:layout_gravity="bottom" - android:orientation="horizontal" > - - <!--suppress ButtonStyle --> - <Button - android:id="@+id/physical_web_decline" - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="1" - android:background="?attr/listChoiceBackgroundIndicator" - android:gravity="start|center_vertical" - android:textDirection="locale" - android:paddingStart="16dp" - android:paddingEnd="16dp" - android:text="@string/no_thanks" - android:textAllCaps="true" - android:textColor="@color/light_normal_color" - android:textSize="14sp" /> - - <!--suppress ButtonStyle --> - <Button - android:id="@+id/physical_web_enable" - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="1" - android:background="?attr/listChoiceBackgroundIndicator" - android:gravity="end|center_vertical" - android:textDirection="locale" - android:paddingStart="16dp" - android:paddingEnd="16dp" - android:text="@string/enable" - android:textAllCaps="true" - android:textColor="@color/light_active_color" - android:textSize="14sp" /> - - </LinearLayout> - -</merge> \ No newline at end of file
diff --git a/chrome/android/java/res/values/colors.xml b/chrome/android/java/res/values/colors.xml index a7f3933e..0e3ebd2 100644 --- a/chrome/android/java/res/values/colors.xml +++ b/chrome/android/java/res/values/colors.xml
@@ -179,11 +179,6 @@ <!-- Bookmark widget colors --> <color name="bookmark_widget_pressed_highlight">#1D000000</color> - <!-- Physical Web colors --> - <color name="physical_web_header_color">#333</color> - <color name="physical_web_url_color">#969696</color> - <color name="physical_web_url_description_color">#646464</color> - <!-- Physical Web diagnostics colors --> <color name="physical_web_diags_success_color">#00933b</color> <color name="physical_web_diags_failure_color">#f90101</color>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java index 6447d0f..4f0a496 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -246,6 +246,7 @@ private AppMenuHandler mAppMenuHandler; private ToolbarManager mToolbarManager; private BottomSheet mBottomSheet; + private FadingBackgroundView mFadingBackgroundView; // Time in ms that it took took us to inflate the initial layout private long mInflateInitialLayoutDurationMs; @@ -351,15 +352,14 @@ mBottomSheet.setTabModelSelector(mTabModelSelector); mBottomSheet.setFullscreenManager(mFullscreenManager); - final FadingBackgroundView fadingView = - (FadingBackgroundView) findViewById(R.id.fading_focus_target); + mFadingBackgroundView = (FadingBackgroundView) findViewById(R.id.fading_focus_target); mBottomSheet.addObserver(new EmptyBottomSheetObserver() { @Override public void onTransitionPeekToHalf(float transitionFraction) { - fadingView.setViewAlpha(transitionFraction); + mFadingBackgroundView.setViewAlpha(transitionFraction); } }); - fadingView.addObserver(mBottomSheet); + mFadingBackgroundView.addObserver(mBottomSheet); } ((BottomContainer) findViewById(R.id.bottom_container)).initialize(mFullscreenManager); } @@ -505,6 +505,8 @@ mRegularTabCreator = tabCreators.first; mIncognitoTabCreator = tabCreators.second; + OfflinePageUtils.observeTabModelSelector(this, mTabModelSelector); + if (mTabModelSelectorTabObserver != null) mTabModelSelectorTabObserver.destroy(); mTabModelSelectorTabObserver = new TabModelSelectorTabObserver(mTabModelSelector) { @@ -621,6 +623,13 @@ } /** + * @return The View used to obscure content and bring focus to a foreground view. + */ + public FadingBackgroundView getFadingBackgroundView() { + return mFadingBackgroundView; + } + + /** * @return {@link AppMenuPropertiesDelegate} instance that the {@link AppMenuHandler} * should be using in this activity. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java index 9f24a05..69da4dd 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -135,6 +135,7 @@ public static final String NTP_CONDENSED_TILE_LAYOUT = "NTPCondensedTileLayout"; public static final String NTP_FAKE_OMNIBOX_TEXT = "NTPFakeOmniboxText"; public static final String NTP_FOREIGN_SESSIONS_SUGGESTIONS = "NTPForeignSessionsSuggestions"; + public static final String NTP_LAUNCH_AFTER_INACTIVITY = "NTPLaunchAfterInactivity"; public static final String NTP_OFFLINE_PAGES_FEATURE_NAME = "NTPOfflinePages"; public static final String NTP_SHOW_GOOGLE_G_IN_OMNIBOX = "NTPShowGoogleGInOmnibox"; public static final String NTP_SNIPPETS_INCREASED_VISIBILITY = "NTPSnippetsIncreasedVisibility";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java index 7295363..5a91d0ceb 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -135,6 +135,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.TimeUnit; /** * This is the main activity for ChromeMobile when not running in document mode. All the tabs @@ -199,6 +200,10 @@ private static final String ACTION_CLOSE_TABS = "com.google.android.apps.chrome.ACTION_CLOSE_TABS"; + private static final String LAST_BACKGROUNDED_TIME_MS_PREF = + "ChromeTabbedActivity.BackgroundTimeMs"; + private static final String NTP_LAUNCH_DELAY_IN_MINS_PARAM = "delay_in_mins"; + /** The task id of the activity that tabs were merged into. */ private static int sMergedInstanceTaskId; @@ -538,6 +543,11 @@ mTabModelSelectorImpl.saveState(); StartupMetrics.getInstance().recordHistogram(true); mActivityStopMetrics.onStopWithNative(this); + + ContextUtils.getAppSharedPreferences() + .edit() + .putLong(LAST_BACKGROUNDED_TIME_MS_PREF, System.currentTimeMillis()) + .apply(); } @Override @@ -560,7 +570,8 @@ try { TraceEvent.begin("ChromeTabbedActivity.onNewIntentWithNative"); - super.onNewIntentWithNative(intent); + if (!maybeLaunchNtpFromIntent(intent)) super.onNewIntentWithNative(intent); + if (CommandLine.getInstance().hasSwitch(ContentSwitches.ENABLE_TEST_INTENTS)) { handleDebugIntent(intent); } @@ -695,6 +706,52 @@ } } + /** + * Determines if the intent should trigger an NTP and launches it if applicable. + * + * @param intent The intent to check whether an NTP should be triggered. + * @return Whether an NTP was triggered as a result of this intent. + */ + private boolean maybeLaunchNtpFromIntent(Intent intent) { + if (intent == null || !TextUtils.equals(intent.getAction(), Intent.ACTION_MAIN)) { + return false; + } + if (!mIntentHandler.isIntentUserVisible()) return false; + + if (!ChromeFeatureList.isEnabled(ChromeFeatureList.NTP_LAUNCH_AFTER_INACTIVITY)) { + return false; + } + + int ntpLaunchDelayInMins = ChromeFeatureList.getFieldTrialParamByFeatureAsInt( + ChromeFeatureList.NTP_LAUNCH_AFTER_INACTIVITY, NTP_LAUNCH_DELAY_IN_MINS_PARAM, -1); + if (ntpLaunchDelayInMins == -1) { + Log.e(TAG, "No NTP launch delay specified despite enabled field trial"); + return false; + } + + long lastBackgroundedTimeMs = + ContextUtils.getAppSharedPreferences().getLong(LAST_BACKGROUNDED_TIME_MS_PREF, -1); + if (lastBackgroundedTimeMs == -1) return false; + + long backgroundDurationMinutes = TimeUnit.MINUTES.convert( + System.currentTimeMillis() - lastBackgroundedTimeMs, TimeUnit.MILLISECONDS); + + if (backgroundDurationMinutes < ntpLaunchDelayInMins) { + Log.i(TAG, "Not launching NTP due to inactivity, background time: %d, launch delay: %d", + backgroundDurationMinutes, ntpLaunchDelayInMins); + return false; + } + + PartnerBrowserCustomizations.setOnInitializeAsyncFinished(new Runnable() { + @Override + public void run() { + createInitialTab(); + } + }, INITIAL_TAB_CREATION_TIMEOUT_MS); + + return true; + } + @Override public void initializeState() { // This method goes through 3 steps: @@ -739,6 +796,8 @@ // TODO(mthiesse): Improve startup when started from a VR intent. Right now // we launch out of VR, partially load out of VR, then switch into VR. mVrShellDelegate.enterVRIfNecessary(); + } else if (maybeLaunchNtpFromIntent(getIntent())) { + mIntentWithEffect = true; } else if (!mIntentHandler.shouldIgnoreIntent(intent)) { mIntentWithEffect = mIntentHandler.onNewIntent(intent); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java index 18bc4d3a..48cdfe9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java
@@ -33,6 +33,7 @@ import org.chromium.chrome.browser.tabmodel.TabModel; import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.tabmodel.TabModelUtils; +import org.chromium.chrome.browser.util.FeatureUtilities; import org.chromium.chrome.browser.util.MathUtils; import org.chromium.ui.base.LocalizationUtils; import org.chromium.ui.resources.ResourceManager; @@ -246,6 +247,12 @@ startMarginAnimation(false); startYOffsetAnimation(false); finishScrollStacks(); + + // TODO(twellington): Add a proper tab selection animation rather than disabling the current + // animation. + if (FeatureUtilities.isChromeHomeEnabled()) { + onUpdateAnimation(System.currentTimeMillis(), true); + } } @Override @@ -346,6 +353,12 @@ mStacks[getTabStackIndex(id)].tabCreated(time, id); startMarginAnimation(false); uiPreemptivelySelectTabModel(newIsIncognito); + + // TODO(twellington): Add a proper tab creation animation rather than disabling the current + // animation. + if (FeatureUtilities.isChromeHomeEnabled()) { + onUpdateAnimation(System.currentTimeMillis(), true); + } } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java index 7484e5f..96c4b91 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
@@ -57,7 +57,8 @@ private Bundle mSavedInstanceState; private int mCurrentOrientation = Surface.ROTATION_0; private boolean mDestroyed; - private NativeInitializationController mNativeInitializationController; + private final NativeInitializationController mNativeInitializationController = + new NativeInitializationController(this); private MemoryUma mMemoryUma; private long mLastUserInteractionTime; private boolean mIsTablet; @@ -113,17 +114,17 @@ @Override public final void setContentViewAndLoadLibrary() { + // Unless it was called before (due to delaying all browser startup on purpose), // setContentView inflating the decorView and the basic UI hierarhcy as stubs. // This is done here before kicking long running I/O because inflation includes accessing // resource files(xmls etc) even if we are inflating views defined by the framework. If this // operation gets blocked because other long running I/O are running, we delay onCreate(), // onStart() and first draw consequently. - setContentView(); + if (!shouldDelayBrowserStartup()) setContentView(); if (mLaunchBehindWorkaround != null) mLaunchBehindWorkaround.onSetContentView(); // Kick off long running IO tasks that can be done in parallel. - mNativeInitializationController = new NativeInitializationController(this); mNativeInitializationController.startBackgroundTasks(shouldAllocateChildConnection()); } @@ -242,7 +243,22 @@ mOnCreateTimestampUptimeMs = SystemClock.uptimeMillis(); mSavedInstanceState = savedInstanceState; - ChromeBrowserInitializer.getInstance(this).handlePreNativeStartup(this); + if (shouldDelayBrowserStartup()) { + // Handle on UI inflation here if the browser startup is going to be delayed, since + // we still need to initialize UI. + setContentView(); + } else { + ChromeBrowserInitializer.getInstance( + getApplicationContext()).handlePreNativeStartup(this); + } + } + + /** + * @return Whether the browser startup should be delayed. Note that changing this return value + * will have direct impact on startup performance. + */ + protected boolean shouldDelayBrowserStartup() { + return false; } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/invalidation/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/invalidation/OWNERS index bc330c6..5700d9b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/invalidation/OWNERS +++ b/chrome/android/java/src/org/chromium/chrome/browser/invalidation/OWNERS
@@ -2,3 +2,5 @@ nyquist@chromium.org yfriedman@chromium.org zea@chromium.org + +# COMPONENT: Services>Invalidation
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java index 9236349..a4e96c4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java
@@ -327,23 +327,6 @@ provider.sendStringMessage(routeId, message, callbackId); } - /** - * Sends a binary message to the specified route. - * @param routeId The id of the route to send the message to. - * @param data The binary message to send. - * @param callbackId The id of the result callback tracked by the native side. - */ - @CalledByNative - public void sendBinaryMessage(String routeId, byte[] data, int callbackId) { - MediaRouteProvider provider = mRouteIdsToProviders.get(routeId); - if (provider == null) { - nativeOnMessageSentResult(mNativeMediaRouterAndroid, false, callbackId); - return; - } - - provider.sendBinaryMessage(routeId, data, callbackId); - } - @VisibleForTesting protected ChromeMediaRouter(long nativeMediaRouter) { mNativeMediaRouterAndroid = nativeMediaRouter;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/MediaRouteProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/MediaRouteProvider.java index e4884b9..a80dceaf 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/MediaRouteProvider.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/MediaRouteProvider.java
@@ -81,13 +81,4 @@ * @param nativeCallbackId The id of the result callback tracked by the native side. */ void sendStringMessage(String routeId, String message, int nativeCallbackId); - - /** - * Sends a binary message to the route with the given id. The route must be created by this - * provider. - * @param routeId The id of the route to send the message to. - * @param data The binary message to send. - * @param nativeCallbackId The id of the result callback tracked by the native side. - */ - void sendBinaryMessage(String routeId, byte[] data, int nativeCallbackId); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java index e687e5b..211afd2 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java
@@ -376,15 +376,6 @@ mManager.onMessageSentResult(success, nativeCallbackId); } - @Override - public void sendBinaryMessage(String routeId, byte[] data, int nativeCallbackId) { - // TODO(crbug.com/524128): Cast API does not support sending binary message - // to receiver application. Binary data may be converted to String and send as - // an app_message within it's own message namespace, using the string version. - // Sending failure in the result callback for now. - mManager.onMessageSentResult(false, nativeCallbackId); - } - private boolean handleClientConnectMessage(JSONObject jsonMessage) throws JSONException { String clientId = jsonMessage.getString("clientId"); if (clientId == null) return false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/ChromeHomeNewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/ChromeHomeNewTabPage.java new file mode 100644 index 0000000..03fa4e7 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/ChromeHomeNewTabPage.java
@@ -0,0 +1,184 @@ +// Copyright 2017 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. + +package org.chromium.chrome.browser.ntp; + +import android.content.Context; +import android.content.res.Resources; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; + +import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.ChromeActivity; +import org.chromium.chrome.browser.NativePage; +import org.chromium.chrome.browser.UrlConstants; +import org.chromium.chrome.browser.ntp.LogoBridge.Logo; +import org.chromium.chrome.browser.ntp.LogoBridge.LogoObserver; +import org.chromium.chrome.browser.search_engines.TemplateUrlService; +import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrlServiceObserver; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver; +import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType; +import org.chromium.chrome.browser.tabmodel.TabModelObserver; +import org.chromium.chrome.browser.tabmodel.TabModelSelector; +import org.chromium.chrome.browser.widget.BottomSheet; + +/** + * The new tab page to display when Chrome Home is enabled. + */ +public class ChromeHomeNewTabPage implements NativePage, TemplateUrlServiceObserver { + private final Tab mTab; + private final TabModelObserver mTabModelObserver; + private final TabModelSelector mTabModelSelector; + private final LogoView.Delegate mLogoDelegate; + + private final View mView; + private final LogoView mLogoView; + + private final String mTitle; + private final int mBackgroundColor; + private final int mThemeColor; + + /** + * Constructs a ChromeHomeNewTabPage. + * @param context The context used to inflate the view. + * @param tab The Tab that is showing this new tab page. + * @param tabModelSelector The TabModelSelector used to open tabs. + */ + public ChromeHomeNewTabPage( + final Context context, final Tab tab, final TabModelSelector tabModelSelector) { + mTab = tab; + mTabModelSelector = tabModelSelector; + mView = LayoutInflater.from(context).inflate(R.layout.chrome_home_new_tab_page, null); + + Resources res = context.getResources(); + mTitle = res.getString(R.string.button_new_tab); + mBackgroundColor = ApiCompatibilityUtils.getColor(res, R.color.ntp_bg); + mThemeColor = ApiCompatibilityUtils.getColor(res, R.color.default_primary_color); + + TemplateUrlService.getInstance().addObserver(this); + + mLogoView = (LogoView) mView.findViewById(R.id.search_provider_logo); + mLogoDelegate = new LogoDelegateImpl(tab, mLogoView); + mLogoDelegate.getSearchProviderLogo(new LogoObserver() { + @Override + public void onLogoAvailable(Logo logo, boolean fromCache) { + if (logo == null && fromCache) return; + mLogoView.setDelegate(mLogoDelegate); + mLogoView.updateLogo(logo); + } + }); + updateSearchProviderLogoVisibility(); + + final ChromeActivity activity = tab.getActivity(); + + // TODO(twellington): remove this after only one NTP may be open at a time and NTP is + // destroyed after user navigates to a different tab. + mTabModelObserver = new EmptyTabModelObserver() { + @Override + public void didSelectTab(Tab tab, TabSelectionType type, int lastId) { + boolean isNewTabPage = isTabChromeHomeNewTabPage(tab); + activity.getFadingBackgroundView().setEnabled(!isNewTabPage); + if (isNewTabPage) onNewTabPageShown(); + } + }; + mTabModelSelector.getModel(false).addObserver(mTabModelObserver); + + View closeButton = mView.findViewById(R.id.close_button); + closeButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + activity.getBottomSheet().setSheetState(BottomSheet.SHEET_STATE_PEEK, true); + mTabModelSelector.closeTab(tab); + // TODO(twellington): show overview mode. + } + }); + + onNewTabPageShown(); + + // TODO(twellington): disallow moving the NTP to the other window in Android N+ + // multi-window mode. + } + + @Override + public View getView() { + return mView; + } + + @Override + public String getTitle() { + return mTitle; + } + + @Override + public String getUrl() { + return UrlConstants.NTP_URL; + } + + @Override + public String getHost() { + return UrlConstants.NTP_HOST; + } + + @Override + public int getBackgroundColor() { + return mBackgroundColor; + } + + @Override + public int getThemeColor() { + return mThemeColor; + } + + @Override + public boolean needsToolbarShadow() { + return false; + } + + @Override + public void updateForUrl(String url) {} + + @Override + public void destroy() { + mLogoDelegate.destroy(); + + // The next tab will be selected before this one is destroyed. If the currently selected + // tab is a Chrome Home new tab page, the FadingBackgroundView should not be enabled. + mTab.getActivity().getFadingBackgroundView().setEnabled( + !isTabChromeHomeNewTabPage(mTabModelSelector.getCurrentTab())); + + mTabModelSelector.getModel(false).removeObserver(mTabModelObserver); + } + + private void updateSearchProviderLogoVisibility() { + boolean hasLogo = TemplateUrlService.getInstance().isDefaultSearchEngineGoogle(); + mLogoView.setVisibility(hasLogo ? View.VISIBLE : View.GONE); + } + + private void onNewTabPageShown() { + mTab.getActivity().getFadingBackgroundView().setEnabled(false); + + // This method may be called when an NTP is selected due to the user switching tab models. + // In this case, we do not want the bottom sheet to open. Unfortunately, without observing + // OverviewModeBehavior, we have no good signal to show the BottomSheet when an NTP is + // selected in the tab switcher. Eventually this won't matter because we will not allow + // NTPs to remain open after the user leaves them. + if (mTab.getActivity().isInOverviewMode()) return; + + mTab.getActivity().getBottomSheet().setSheetState(BottomSheet.SHEET_STATE_HALF, true); + } + + private boolean isTabChromeHomeNewTabPage(Tab tab) { + return tab.getUrl().equals(getUrl()) && !tab.isIncognito(); + } + + // TemplateUrlServiceObserver overrides + + @Override + public void onTemplateURLServiceChanged() { + updateSearchProviderLogoVisibility(); + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/LogoDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/LogoDelegateImpl.java new file mode 100644 index 0000000..dcbefbb --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/LogoDelegateImpl.java
@@ -0,0 +1,96 @@ +// Copyright 2017 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. + +package org.chromium.chrome.browser.ntp; + +import org.chromium.base.metrics.RecordHistogram; +import org.chromium.chrome.browser.ntp.LogoBridge.Logo; +import org.chromium.chrome.browser.ntp.LogoBridge.LogoObserver; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.content_public.browser.LoadUrlParams; +import org.chromium.ui.base.PageTransition; + +import jp.tomorrowkey.android.gifplayer.BaseGifImage; + +/** + * An implementation of {@link LogoView.Delegate}. + */ +public class LogoDelegateImpl implements LogoView.Delegate { + // UMA enum constants. CTA means the "click-to-action" icon. + private static final String LOGO_SHOWN_UMA_NAME = "NewTabPage.LogoShown"; + private static final int STATIC_LOGO_SHOWN = 0; + private static final int CTA_IMAGE_SHOWN = 1; + + private static final String LOGO_CLICK_UMA_NAME = "NewTabPage.LogoClick"; + private static final int STATIC_LOGO_CLICKED = 0; + private static final int CTA_IMAGE_CLICKED = 1; + private static final int ANIMATED_LOGO_CLICKED = 2; + + private final Tab mTab; + private LogoView mLogoView; + + private LogoBridge mLogoBridge; + private String mOnLogoClickUrl; + private String mAnimatedLogoUrl; + + private boolean mIsDestroyed; + + /** + * Construct a new {@link LogoDelegateImpl}. + * @param tab The tab showing the logo view. + * @param logoView The view that shows the search provider logo. + */ + public LogoDelegateImpl(Tab tab, LogoView logoView) { + mTab = tab; + mLogoView = logoView; + mLogoBridge = new LogoBridge(tab.getProfile()); + } + + @Override + public void destroy() { + mIsDestroyed = true; + } + + @Override + public void onLogoClicked(boolean isAnimatedLogoShowing) { + if (mIsDestroyed) return; + + if (!isAnimatedLogoShowing && mAnimatedLogoUrl != null) { + RecordHistogram.recordSparseSlowlyHistogram(LOGO_CLICK_UMA_NAME, CTA_IMAGE_CLICKED); + mLogoView.showLoadingView(); + mLogoBridge.getAnimatedLogo(new LogoBridge.AnimatedLogoCallback() { + @Override + public void onAnimatedLogoAvailable(BaseGifImage animatedLogoImage) { + if (mIsDestroyed) return; + mLogoView.playAnimatedLogo(animatedLogoImage); + } + }, mAnimatedLogoUrl); + } else if (mOnLogoClickUrl != null) { + RecordHistogram.recordSparseSlowlyHistogram(LOGO_CLICK_UMA_NAME, + isAnimatedLogoShowing ? ANIMATED_LOGO_CLICKED : STATIC_LOGO_CLICKED); + mTab.loadUrl(new LoadUrlParams(mOnLogoClickUrl, PageTransition.LINK)); + } + } + + @Override + public void getSearchProviderLogo(final LogoObserver logoObserver) { + if (mIsDestroyed) return; + + LogoObserver wrapperCallback = new LogoObserver() { + @Override + public void onLogoAvailable(Logo logo, boolean fromCache) { + if (mIsDestroyed) return; + mOnLogoClickUrl = logo != null ? logo.onClickUrl : null; + mAnimatedLogoUrl = logo != null ? logo.animatedLogoUrl : null; + if (logo != null) { + RecordHistogram.recordSparseSlowlyHistogram(LOGO_SHOWN_UMA_NAME, + logo.animatedLogoUrl == null ? STATIC_LOGO_SHOWN : CTA_IMAGE_SHOWN); + } + logoObserver.onLogoAvailable(logo, fromCache); + } + }; + + mLogoBridge.getCurrentLogo(wrapperCallback); + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/LogoView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/LogoView.java index 0bf53e9..38c4db9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/LogoView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/LogoView.java
@@ -24,7 +24,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.ntp.LogoBridge.Logo; -import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager; +import org.chromium.chrome.browser.ntp.LogoBridge.LogoObserver; import org.chromium.chrome.browser.widget.LoadingView; import java.lang.ref.WeakReference; @@ -67,7 +67,7 @@ */ private float mTransitionAmount; - private NewTabPageManager mManager; + private Delegate mDelegate; private final Property<LogoView, Float> mTransitionProperty = new Property<LogoView, Float>(Float.class, "") { @@ -88,6 +88,28 @@ }; /** + * Handles tasks for the {@link LogoView} shown on an NTP. + */ + public interface Delegate { + /** + * Called when the user clicks on the logo. + * @param isAnimatedLogoShowing Whether the animated GIF logo is playing. + */ + void onLogoClicked(boolean isAnimatedLogoShowing); + + /** + * Gets the default search provider's logo and calls logoObserver with the result. + * @param logoObserver The callback to notify when the logo is available. + */ + void getSearchProviderLogo(LogoObserver logoObserver); + + /** + * Should be called when the owning class is destroyed. + */ + void destroy(); + } + + /** * Constructor used to inflate a LogoView from XML. */ public LogoView(Context context, AttributeSet attrs) { @@ -115,10 +137,10 @@ } /** - * Sets the NewTabPageManager to notify when the logo is pressed. + * Sets the {@link Delegate} to notify when the logo is pressed. */ - public void setMananger(NewTabPageManager manager) { - mManager = manager; + public void setDelegate(Delegate delegate) { + mDelegate = delegate; } /** @@ -321,8 +343,8 @@ @Override public void onClick(View view) { - if (view == this && mManager != null && !isTransitioning()) { - mManager.onLogoClicked(isAnimatedLogoShowing()); + if (view == this && mDelegate != null && !isTransitioning()) { + mDelegate.onLogoClicked(isAnimatedLogoShowing()); } } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageFactory.java index 1974177c..a15bb6c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageFactory.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageFactory.java
@@ -22,6 +22,7 @@ import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tabmodel.TabModel; import org.chromium.chrome.browser.tabmodel.TabModelSelector; +import org.chromium.chrome.browser.util.FeatureUtilities; import org.chromium.content_public.browser.LoadUrlParams; /** @@ -34,7 +35,9 @@ static class NativePageBuilder { protected NativePage buildNewTabPage(ChromeActivity activity, Tab tab, TabModelSelector tabModelSelector) { - if (tab.isIncognito()) { + if (FeatureUtilities.isChromeHomeEnabled() && !tab.isIncognito()) { + return new ChromeHomeNewTabPage(activity, tab, tabModelSelector); + } else if (tab.isIncognito()) { return new IncognitoNewTabPage(activity); } else { return new NewTabPage(activity, new TabShim(tab), tabModelSelector);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java index c953e93..3b3bb882 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -34,8 +34,6 @@ import org.chromium.chrome.browser.compositor.layouts.content.InvalidationAwareThumbnailProvider; import org.chromium.chrome.browser.download.DownloadManagerService; import org.chromium.chrome.browser.metrics.StartupMetrics; -import org.chromium.chrome.browser.ntp.LogoBridge.Logo; -import org.chromium.chrome.browser.ntp.LogoBridge.LogoObserver; import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager; import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge; import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource; @@ -58,18 +56,14 @@ import org.chromium.chrome.browser.tabmodel.TabModelUtils; import org.chromium.chrome.browser.util.FeatureUtilities; import org.chromium.chrome.browser.util.UrlUtilities; -import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.content_public.browser.NavigationController; import org.chromium.content_public.browser.NavigationEntry; import org.chromium.net.NetworkChangeNotifier; import org.chromium.ui.base.DeviceFormFactor; -import org.chromium.ui.base.PageTransition; import org.chromium.ui.mojom.WindowOpenDisposition; import java.util.concurrent.TimeUnit; -import jp.tomorrowkey.android.gifplayer.BaseGifImage; - /** * Provides functionality when the user interacts with the NTP. */ @@ -77,16 +71,6 @@ implements NativePage, InvalidationAwareThumbnailProvider, TemplateUrlServiceObserver { private static final String TAG = "NewTabPage"; - // UMA enum constants. CTA means the "click-to-action" icon. - private static final String LOGO_SHOWN_UMA_NAME = "NewTabPage.LogoShown"; - private static final int STATIC_LOGO_SHOWN = 0; - private static final int CTA_IMAGE_SHOWN = 1; - - private static final String LOGO_CLICK_UMA_NAME = "NewTabPage.LogoClick"; - private static final int STATIC_LOGO_CLICKED = 0; - private static final int CTA_IMAGE_CLICKED = 1; - private static final int ANIMATED_LOGO_CLICKED = 2; - // Key for the scroll position data that may be stored in a navigation entry. private static final String NAVIGATION_ENTRY_SCROLL_POSITION_KEY = "NewTabPageScrollPosition"; @@ -105,8 +89,6 @@ private TabObserver mTabObserver; private LogoBridge mLogoBridge; private boolean mSearchProviderHasLogo; - private String mOnLogoClickUrl; - private String mAnimatedLogoUrl; private FakeboxDelegate mFakeboxDelegate; private SnippetsBridge mSnippetsBridge; @@ -259,46 +241,6 @@ } @Override - public void onLogoClicked(boolean isAnimatedLogoShowing) { - if (mIsDestroyed) return; - - if (!isAnimatedLogoShowing && mAnimatedLogoUrl != null) { - RecordHistogram.recordSparseSlowlyHistogram(LOGO_CLICK_UMA_NAME, CTA_IMAGE_CLICKED); - mNewTabPageView.showLogoLoadingView(); - mLogoBridge.getAnimatedLogo(new LogoBridge.AnimatedLogoCallback() { - @Override - public void onAnimatedLogoAvailable(BaseGifImage animatedLogoImage) { - if (mIsDestroyed) return; - mNewTabPageView.playAnimatedLogo(animatedLogoImage); - } - }, mAnimatedLogoUrl); - } else if (mOnLogoClickUrl != null) { - RecordHistogram.recordSparseSlowlyHistogram(LOGO_CLICK_UMA_NAME, - isAnimatedLogoShowing ? ANIMATED_LOGO_CLICKED : STATIC_LOGO_CLICKED); - mTab.loadUrl(new LoadUrlParams(mOnLogoClickUrl, PageTransition.LINK)); - } - } - - @Override - public void getSearchProviderLogo(final LogoObserver logoObserver) { - if (mIsDestroyed) return; - LogoObserver wrapperCallback = new LogoObserver() { - @Override - public void onLogoAvailable(Logo logo, boolean fromCache) { - if (mIsDestroyed) return; - mOnLogoClickUrl = logo != null ? logo.onClickUrl : null; - mAnimatedLogoUrl = logo != null ? logo.animatedLogoUrl : null; - if (logo != null) { - RecordHistogram.recordSparseSlowlyHistogram(LOGO_SHOWN_UMA_NAME, - logo.animatedLogoUrl == null ? STATIC_LOGO_SHOWN : CTA_IMAGE_SHOWN); - } - logoObserver.onLogoAvailable(logo, fromCache); - } - }; - mLogoBridge.getCurrentLogo(wrapperCallback); - } - - @Override public SuggestionsSource getSuggestionsSource() { if (sSuggestionsSourceForTests != null) return sSuggestionsSourceForTests; return mSnippetsBridge;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java index 3ef166e..c566a2a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
@@ -53,8 +53,6 @@ import org.chromium.chrome.browser.widget.displaystyle.UiConfig; import org.chromium.ui.base.DeviceFormFactor; -import jp.tomorrowkey.android.gifplayer.BaseGifImage; - /** * The native new tab page, represented by some basic data such as title and url, and an Android * View that displays the page. @@ -101,6 +99,7 @@ private ChromeActivity mActivity; private NewTabPageManager mManager; + private LogoView.Delegate mLogoDelegate; private TileGroup.Delegate mTileGroupDelegate; private TileGroup mTileGroup; private UiConfig mUiConfig; @@ -149,18 +148,6 @@ void focusSearchBox(boolean beginVoiceSearch, String pastedText); /** - * Called when the user clicks on the logo. - * @param isAnimatedLogoShowing Whether the animated GIF logo is playing. - */ - void onLogoClicked(boolean isAnimatedLogoShowing); - - /** - * Gets the default search provider's logo and calls logoObserver with the result. - * @param logoObserver The callback to notify when the logo is available. - */ - void getSearchProviderLogo(LogoObserver logoObserver); - - /** * @return whether the {@link NewTabPage} associated with this manager is the current page * displayed to the user. */ @@ -246,6 +233,7 @@ mSearchProviderLogoView = (LogoView) mNewTabPageLayout.findViewById(R.id.search_provider_logo); + mLogoDelegate = new LogoDelegateImpl(tab, mSearchProviderLogoView); mSearchBoxView = mNewTabPageLayout.findViewById(R.id.search_box); mNoSearchLogoSpacer = mNewTabPageLayout.findViewById(R.id.no_search_logo_spacer); @@ -507,11 +495,11 @@ * Loads the search provider logo (e.g. Google doodle), if any. */ private void loadSearchProviderLogo() { - mManager.getSearchProviderLogo(new LogoObserver() { + mLogoDelegate.getSearchProviderLogo(new LogoObserver() { @Override public void onLogoAvailable(Logo logo, boolean fromCache) { if (logo == null && fromCache) return; - mSearchProviderLogoView.setMananger(mManager); + mSearchProviderLogoView.setDelegate(mLogoDelegate); mSearchProviderLogoView.updateLogo(logo); mSnapshotTileGridChanged = true; } @@ -570,20 +558,6 @@ } /** - * Shows a progressbar indicating the animated logo is being downloaded. - */ - void showLogoLoadingView() { - mSearchProviderLogoView.showLoadingView(); - } - - /** - * Starts playing the given animated GIF logo. - */ - void playAnimatedLogo(BaseGifImage gifImage) { - mSearchProviderLogoView.playAnimatedLogo(gifImage); - } - - /** * @return Whether URL focus animations are currently disabled. */ boolean urlFocusAnimationsDisabled() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java index 16e4ce0..04ee162 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
@@ -439,6 +439,21 @@ nativeCheckPagesExistOffline(mNativeOfflinePageBridge, urlArray, callbackInternal); } + /** Tells the native side that a new tab has been added for this profile. */ + void registerRecentTab(int tabId) { + nativeRegisterRecentTab(mNativeOfflinePageBridge, tabId); + } + + /** Tells the native side that the tab of |webContents| will be closed. */ + void willCloseTab(WebContents webContents) { + nativeWillCloseTab(mNativeOfflinePageBridge, webContents); + } + + /** Tells the native side that a new tab has been removed for this profile. */ + void unregisterRecentTab(int tabId) { + nativeUnregisterRecentTab(mNativeOfflinePageBridge, tabId); + } + @VisibleForTesting static void setOfflineBookmarksEnabledForTesting(boolean enabled) { sOfflineBookmarksEnabled = enabled; @@ -506,12 +521,14 @@ private static native boolean nativeIsPageSharingEnabled(); private static native boolean nativeCanSavePage(String url); private static native OfflinePageBridge nativeGetOfflinePageBridgeForProfile(Profile profile); - @VisibleForTesting native void nativeGetAllPages(long nativeOfflinePageBridge, List<OfflinePageItem> offlinePages, final Callback<List<OfflinePageItem>> callback); private native void nativeCheckPagesExistOffline(long nativeOfflinePageBridge, Object[] urls, CheckPagesExistOfflineCallbackInternal callback); + private native void nativeRegisterRecentTab(long nativeOfflinePageBridge, int tabId); + private native void nativeWillCloseTab(long nativeOfflinePageBridge, WebContents webContents); + private native void nativeUnregisterRecentTab(long nativeOfflinePageBridge, int tabId); @VisibleForTesting native void nativeGetRequestsInQueue( long nativeOfflinePageBridge, Callback<SavePageRequest[]> callback);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java index dc67d574..0435dd7 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java
@@ -15,6 +15,8 @@ import android.os.BatteryManager; import android.os.Environment; +import org.chromium.base.ActivityState; +import org.chromium.base.ApplicationStatus; import org.chromium.base.Callback; import org.chromium.base.FileUtils; import org.chromium.base.Log; @@ -31,7 +33,9 @@ import org.chromium.chrome.browser.snackbar.SnackbarManager; import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController; import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tabmodel.TabModel; import org.chromium.chrome.browser.tabmodel.TabModelSelector; +import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver; import org.chromium.components.bookmarks.BookmarkId; import org.chromium.components.offlinepages.SavePageResult; import org.chromium.content_public.browser.LoadUrlParams; @@ -45,7 +49,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.nio.channels.FileChannel; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -70,6 +76,13 @@ private static File sOfflineSharingDirectory; + /** + * Tracks the observers of ChromeActivity's TabModelSelectors. This is weak so the activity can + * be garbage collected without worrying about this map. The RecentTabTracker is held here so + * that it can be destroyed when the ChromeActivity gets a new TabModelSelector. + */ + private static Map<ChromeActivity, RecentTabTracker> sTabModelObservers = new HashMap<>(); + private static OfflinePageUtils getInstance() { if (sInstance == null) { sInstance = new OfflinePageUtils(); @@ -703,6 +716,90 @@ return ConnectionType.CONNECTION_UNKNOWN; } + /** + * Tracks tab creation and closure for the Recent Tabs feature. UI needs to stop showing + * recent offline pages as soon as the tab is closed. The TabModel is used to get profile + * information because Tab's profile is tied to the native WebContents, which may not exist at + * tab adding or tab closing time. + */ + private static class RecentTabTracker extends TabModelSelectorTabModelObserver { + private TabModelSelector mTabModelSelector; + + public RecentTabTracker(TabModelSelector selector) { + super(selector); + mTabModelSelector = selector; + } + + @Override + public void didAddTab(Tab tab, TabModel.TabLaunchType type) { + Profile profile = mTabModelSelector.getModel(tab.isIncognito()).getProfile(); + OfflinePageBridge bridge = OfflinePageBridge.getForProfile(profile); + if (bridge == null) return; + bridge.registerRecentTab(tab.getId()); + } + + @Override + public void willCloseTab(Tab tab, boolean animate) { + Profile profile = mTabModelSelector.getModel(tab.isIncognito()).getProfile(); + OfflinePageBridge bridge = OfflinePageBridge.getForProfile(profile); + if (bridge == null) return; + + WebContents webContents = tab.getWebContents(); + if (webContents != null) bridge.willCloseTab(webContents); + } + + @Override + public void didCloseTab(int tabId, boolean incognito) { + Profile profile = mTabModelSelector.getModel(incognito).getProfile(); + OfflinePageBridge bridge = OfflinePageBridge.getForProfile(profile); + if (bridge == null) return; + + // First, unregister the tab with the UI. + bridge.unregisterRecentTab(tabId); + + // Then, delete any "Last N" offline pages as well. This is an optimization because + // the UI will no longer show the page, and the page would also be cleaned up by GC + // given enough time. + ClientId clientId = + new ClientId(OfflinePageBridge.LAST_N_NAMESPACE, Integer.toString(tabId)); + List<ClientId> clientIds = new ArrayList<>(); + clientIds.add(clientId); + + bridge.deletePagesByClientId(clientIds, new Callback<Integer>() { + @Override + public void onResult(Integer result) { + // Result is ignored. + } + }); + } + } + + /** + * Starts tracking the tab models in the given selector for tab addition and closure, + * destroying obsolete observers as necessary. + */ + public static void observeTabModelSelector( + ChromeActivity activity, TabModelSelector tabModelSelector) { + RecentTabTracker previousObserver = + sTabModelObservers.put(activity, new RecentTabTracker(tabModelSelector)); + if (previousObserver != null) { + previousObserver.destroy(); + } else { + // This is the 1st time we see this activity so register a state listener with it. + ApplicationStatus.registerStateListenerForActivity( + new ApplicationStatus.ActivityStateListener() { + @Override + public void onActivityStateChange(Activity activity, int newState) { + if (newState == ActivityState.DESTROYED) { + sTabModelObservers.remove(activity).destroy(); + ApplicationStatus.unregisterActivityStateListener(this); + } + } + }, + activity); + } + } + @VisibleForTesting static void setInstanceForTesting(OfflinePageUtils instance) { sInstance = instance;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/SwipeRefreshWidget.java b/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/SwipeRefreshWidget.java deleted file mode 100644 index 9bd335e..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/physicalweb/SwipeRefreshWidget.java +++ /dev/null
@@ -1,37 +0,0 @@ -// Copyright 2015 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. - -package org.chromium.chrome.browser.physicalweb; - -import android.content.Context; -import android.util.AttributeSet; -import android.widget.ListView; - -import org.chromium.chrome.R; - -/** - * Subclass of {@link android.support.v4.widget.SwipeRefreshLayout} that supports containing a - * single ViewGroup. The ViewGroup must contain a ListView such that the top of the list is - * aligned to the top of the parent view. Scrolling past the top of the list triggers a - * refresh. - */ -public class SwipeRefreshWidget extends android.support.v4.widget.SwipeRefreshLayout { - private ListView mListView; - - public SwipeRefreshWidget(Context context, AttributeSet attributeSet) { - super(context, attributeSet); - } - - @Override - public boolean canChildScrollUp() { - if (mListView == null) { - mListView = (ListView) findViewById(R.id.physical_web_urls_list); - } - - // The real child we care about is the list, so check if that can scroll. - return mListView.getChildCount() > 0 - && (mListView.getFirstVisiblePosition() > 0 - || mListView.getChildAt(0).getTop() < mListView.getPaddingTop()); - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProvider.java index 020ccc8..50b19faa 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProvider.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProvider.java
@@ -45,10 +45,8 @@ private static Object sEnabledLock = new Object(); private static Object sContentProviderLock = new Object(); - private static final String TAG = "SupervisedUserContent"; - private static final String ACCOUNTS_GOOGLE_COM = "accounts.google.com"; - private static final String GOOGLE_PLAY_SERVICES_UI_PACKAGE = "com.google.android.gms.ui"; + private static final String GOOGLE_PLAY_SERVICES_PACKAGE = "com.google.android.gms"; // Three value "boolean" caching enabled state, null if not yet known. private static Boolean sEnabled; @@ -210,7 +208,7 @@ private boolean requestIsWhitelisted(String callingPackage, String url) { // Always allow Google Play Services to show the reauthentication page (which is necessary // to fix account issues). - return TextUtils.equals(callingPackage, GOOGLE_PLAY_SERVICES_UI_PACKAGE) + return TextUtils.equals(callingPackage, GOOGLE_PLAY_SERVICES_PACKAGE) && Uri.parse(url).getHost().equals(ACCOUNTS_GOOGLE_COM); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/sync/OWNERS index bc330c6..91e7103 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/sync/OWNERS +++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/OWNERS
@@ -2,3 +2,5 @@ nyquist@chromium.org yfriedman@chromium.org zea@chromium.org + +# COMPONENT: Services>Sync
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java index 3c4bd64..68943599 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
@@ -46,6 +46,9 @@ */ private static final int MAX_UPDATE_ATTEMPTS = 3; + /** Whether updates are enabled. Some tests disable updates. */ + private static boolean sUpdatesEnabled = true; + /** Data extracted from the WebAPK's launch intent and from the WebAPK's Android Manifest. */ private WebApkInfo mInfo; @@ -124,6 +127,10 @@ return mPendingUpdate != null; } + public static void setUpdatesEnabledForTesting(boolean enabled) { + sUpdatesEnabled = enabled; + } + @Override public void onWebManifestForInitialUrlNotWebApkCompatible() { onGotManifestData(null, null); @@ -308,6 +315,10 @@ */ private boolean shouldCheckIfWebManifestUpdated( WebappDataStorage storage, WebApkInfo info, boolean previousUpdateSucceeded) { + if (!sUpdatesEnabled) { + return false; + } + if (CommandLine.getInstance().hasSwitch( ChromeSwitches.CHECK_FOR_WEB_MANIFEST_UPDATE_ON_STARTUP)) { return true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java index 2bdb9a1..b7193081 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -158,8 +158,8 @@ StrictMode.setThreadPolicy(oldPolicy); } - ScreenOrientationProvider.lockOrientation(getWindowAndroid(), - (byte) mWebappInfo.orientation(), this); + ScreenOrientationProvider.lockOrientation( + getWindowAndroid(), (byte) mWebappInfo.orientation()); super.preInflationStartup(); } @@ -641,7 +641,7 @@ } @VisibleForTesting - boolean isSplashScreenVisibleForTests() { + public boolean isSplashScreenVisibleForTests() { return mSplashScreen != null; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/FadingBackgroundView.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/FadingBackgroundView.java index 54e6d854..a65240b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/widget/FadingBackgroundView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/FadingBackgroundView.java
@@ -17,6 +17,8 @@ /** * This view is used to obscure content and bring focus to a foreground view (i.e. the Chrome Home * bottom sheet or the omnibox suggestions). + * + * If the view is disabled, then its alpha will be set to 0f and it will not receive touch events. */ public class FadingBackgroundView extends View implements View.OnClickListener { /** @@ -64,13 +66,23 @@ * @param alpha The desired alpha for this view. */ public void setViewAlpha(float alpha) { - if (MathUtils.areFloatsEqual(alpha, getAlpha())) return; + if (!isEnabled() || MathUtils.areFloatsEqual(alpha, getAlpha())) return; setAlpha(alpha); if (mOverlayAnimator != null) mOverlayAnimator.cancel(); } + @Override + public void setEnabled(boolean isEnabled) { + super.setEnabled(isEnabled); + + if (!isEnabled) { + if (mOverlayAnimator != null) mOverlayAnimator.cancel(); + setAlpha(0f); + } + } + /** * Sets the alpha for this view and alters visibility based on that value. * WARNING: This method should not be called externally for this view! Use setViewAlpha instead.
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd index a0cb6cd..608accdf 100644 --- a/chrome/android/java/strings/android_chrome_strings.grd +++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -2595,37 +2595,6 @@ You must have Bluetooth and Location turned on in order to use the Physical Web. </message> - <message name="IDS_PHYSICAL_WEB_OPTIN_TITLE" desc="The title for a dialog inviting the user to opt-in to the Physical Web."> - Chrome + Physical Web - </message> - <message name="IDS_PHYSICAL_WEB_OPTIN_DESCRIPTION" desc="Description of the Physical Web displayed in the opt-in dialog"> - Devices nearby you are broadcasting web pages over Bluetooth. Chrome will scan for pages and display them when you wake up your device. These pages will go through a Google service to improve the quality of page results. - -You can control the Physical Web in Chrome Settings. - -<ph name="BEGIN_LINK"><learnmore></ph>Learn more<ph name="END_LINK"></learnmore></ph> - </message> - <message name="IDS_PHYSICAL_WEB_LIST_URLS_ACTIVITY_TITLE" desc="The title for a dialog that shows a list of URLs broadcasted from nearby devices."> - The Physical Web - </message> - <message name="IDS_PHYSICAL_WEB_NOTIFICATION_TITLE" desc="The message displayed in a notification when there are nearby URLs."> - Physical Web pages nearby - </message> - <message name="IDS_PHYSICAL_WEB_OPTIN_NOTIFICATION_TITLE" desc="The first line in a notification message informing the user there are nearby devices broadcasting URLs and Chrome can be used to browse the list of URLs."> - Web pages are nearby - </message> - <message name="IDS_PHYSICAL_WEB_OPTIN_NOTIFICATION_TEXT" desc="The second line in a notification message informing the user there are nearby devices broadcasting URLs and Chrome can be used to browse the list of URLs."> - View them in Chrome - </message> - <message name="IDS_PHYSICAL_WEB_EMPTY_LIST" desc="The message displayed in place of a list of nearby URLs when there are no URLs to display."> - No nearby Physical Web pages found - </message> - <message name="IDS_PHYSICAL_WEB_EMPTY_LIST_SCANNING" desc="The message displayed in place of a list of nearby URLs while still scanning for nearby URLs."> - Looking for nearby Physical Web pages - </message> - <message name="IDS_PHYSICAL_WEB_REFRESH" desc="The label for a button that initiates a scan for nearby URLs"> - Refresh - </message> <message name="IDS_PHYSICAL_WEB_LAUNCH_BUTTON" desc="The label for a button that opens a list of nearby URLs"> See what's nearby </message>
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni index c535eca..98bc6c51 100644 --- a/chrome/android/java_sources.gni +++ b/chrome/android/java_sources.gni
@@ -556,6 +556,7 @@ "java/src/org/chromium/chrome/browser/notifications/NotificationSystemStatusUtil.java", "java/src/org/chromium/chrome/browser/notifications/StandardNotificationBuilder.java", "java/src/org/chromium/chrome/browser/notifications/WebApkNotificationClient.java", + "java/src/org/chromium/chrome/browser/ntp/ChromeHomeNewTabPage.java", "java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java", "java/src/org/chromium/chrome/browser/ntp/ContextMenuManager.java", "java/src/org/chromium/chrome/browser/ntp/FakeRecentlyClosedTabManager.java", @@ -563,6 +564,7 @@ "java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPage.java", "java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPageView.java", "java/src/org/chromium/chrome/browser/ntp/LogoBridge.java", + "java/src/org/chromium/chrome/browser/ntp/LogoDelegateImpl.java", "java/src/org/chromium/chrome/browser/ntp/LogoView.java", "java/src/org/chromium/chrome/browser/ntp/NativePageAssassin.java", "java/src/org/chromium/chrome/browser/ntp/NativePageDialog.java", @@ -755,7 +757,6 @@ "java/src/org/chromium/chrome/browser/physicalweb/PwsClient.java", "java/src/org/chromium/chrome/browser/physicalweb/PwsClientImpl.java", "java/src/org/chromium/chrome/browser/physicalweb/PwsResult.java", - "java/src/org/chromium/chrome/browser/physicalweb/SwipeRefreshWidget.java", "java/src/org/chromium/chrome/browser/physicalweb/UrlInfo.java", "java/src/org/chromium/chrome/browser/physicalweb/UrlManager.java", "java/src/org/chromium/chrome/browser/physicalweb/Utils.java", @@ -1229,6 +1230,7 @@ "javatests/src/org/chromium/chrome/browser/TabsTest.java", "javatests/src/org/chromium/chrome/browser/UrlSchemeTest.java", "javatests/src/org/chromium/chrome/browser/UsbChooserDialogTest.java", + "javatests/src/org/chromium/chrome/browser/VideoFullscreenOrientationLockChromeTest.java", "javatests/src/org/chromium/chrome/browser/WarmupManagerTest.java", "javatests/src/org/chromium/chrome/browser/WebShareTest.java", "javatests/src/org/chromium/chrome/browser/accessibility/FontSizePrefsTest.java", @@ -1526,6 +1528,7 @@ "javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenThemeColorTest.java", "javatests/src/org/chromium/chrome/browser/webapps/WebappUrlBarTest.java", "javatests/src/org/chromium/chrome/browser/webapps/WebappVisibilityTest.java", + "javatests/src/org/chromium/chrome/browser/webapps/WebApkIntegrationTest.java", "javatests/src/org/chromium/chrome/browser/widget/BottomSheetObserverTest.java", "javatests/src/org/chromium/chrome/browser/widget/DualControlLayoutTest.java", "javatests/src/org/chromium/chrome/browser/widget/OverviewListLayoutTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/VideoFullscreenOrientationLockChromeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/VideoFullscreenOrientationLockChromeTest.java new file mode 100644 index 0000000..6150bfc --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/VideoFullscreenOrientationLockChromeTest.java
@@ -0,0 +1,129 @@ +// Copyright 2017 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. + +package org.chromium.chrome.browser; + +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.net.Uri; +import android.support.test.filters.MediumTest; +import android.view.KeyEvent; + +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.Feature; +import org.chromium.base.test.util.UrlUtils; +import org.chromium.chrome.browser.download.DownloadUtils; +import org.chromium.chrome.test.ChromeTabbedActivityTestBase; +import org.chromium.content.browser.test.util.Criteria; +import org.chromium.content.browser.test.util.CriteriaHelper; +import org.chromium.content.browser.test.util.DOMUtils; +import org.chromium.content.browser.test.util.JavaScriptUtils; +import org.chromium.content.common.ContentSwitches; +import org.chromium.content_public.browser.WebContents; +import org.chromium.ui.base.DeviceFormFactor; + +import java.util.concurrent.Callable; +import java.util.concurrent.TimeoutException; + +/** + * Integration tests for the feature that auto locks the orientation when a video goes fullscreen. + * See also content layer org.chromium.content.browser.VideoFullscreenOrientationLockTest + */ +@CommandLineFlags.Add({"enable-features=VideoFullscreenOrientationLock", + ContentSwitches.DISABLE_GESTURE_REQUIREMENT_FOR_MEDIA_PLAYBACK}) +public class VideoFullscreenOrientationLockChromeTest extends ChromeTabbedActivityTestBase { + private static final String TEST_URL = "content/test/data/media/video-player.html"; + private static final String VIDEO_URL = "content/test/data/media/bear.webm"; + private static final String VIDEO_ID = "video"; + + private WebContents getWebContents() { + return getActivity().getCurrentContentViewCore().getWebContents(); + } + + private void waitForContentsFullscreenState(boolean fullscreenValue) + throws InterruptedException { + CriteriaHelper.pollInstrumentationThread( + Criteria.equals(fullscreenValue, new Callable<Boolean>() { + @Override + public Boolean call() throws InterruptedException, TimeoutException { + return DOMUtils.isFullscreen(getWebContents()); + } + })); + } + + private boolean isScreenOrientationLocked() { + return getActivity().getRequestedOrientation() + != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + } + + private boolean isScreenOrientationLandscape() throws InterruptedException, TimeoutException { + StringBuilder sb = new StringBuilder(); + sb.append("(function() {"); + sb.append(" return screen.orientation.type.startsWith('landscape');"); + sb.append("})();"); + + return JavaScriptUtils.executeJavaScriptAndWaitForResult(getWebContents(), sb.toString()) + .equals("true"); + } + + private void waitUntilLockedToLandscape() throws InterruptedException { + CriteriaHelper.pollInstrumentationThread(new Criteria() { + @Override + public boolean isSatisfied() { + try { + return isScreenOrientationLocked() && isScreenOrientationLandscape(); + } catch (InterruptedException e) { + return false; + } catch (TimeoutException e) { + return false; + } + } + }); + } + + private void waitUntilUnlocked() throws InterruptedException { + CriteriaHelper.pollInstrumentationThread(new Criteria() { + @Override + public boolean isSatisfied() { + return !isScreenOrientationLocked(); + } + }); + } + + @Override + public void startMainActivity() throws InterruptedException { + startMainActivityWithURL(UrlUtils.getIsolatedTestFileUrl(TEST_URL)); + } + + @MediumTest + @Feature({"VideoFullscreenOrientationLock"}) + public void testUnlockWithDownloadViewerActivity() throws Exception { + if (DeviceFormFactor.isTablet(getInstrumentation().getContext())) return; + + // Start playback to guarantee it's properly loaded. + assertTrue(DOMUtils.isMediaPaused(getWebContents(), VIDEO_ID)); + DOMUtils.playMedia(getWebContents(), VIDEO_ID); + DOMUtils.waitForMediaPlay(getWebContents(), VIDEO_ID); + + // Trigger requestFullscreen() via a click on a button. + assertTrue(DOMUtils.clickNode(getActivity().getCurrentContentViewCore(), "fullscreen")); + waitForContentsFullscreenState(true); + + // Should be locked to landscape now, `waitUntilLockedToLandscape` will throw otherwise. + waitUntilLockedToLandscape(); + + // Orientation lock should be disabled when download viewer activity is started. + Uri fileUri = Uri.parse(UrlUtils.getIsolatedTestFileUrl(VIDEO_URL)); + String mimeType = "video/mp4"; + Intent intent = + DownloadUtils.getMediaViewerIntentForDownloadItem(fileUri, fileUri, mimeType); + IntentHandler.startActivityForTrustedIntent(intent); + waitUntilUnlocked(); + + // Sometimes the web page doesn't transition out of fullscreen until we exit the download + // activity and return to the web page. + sendKeys(KeyEvent.KEYCODE_BACK); + waitForContentsFullscreenState(false); + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/OWNERS b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/OWNERS index bc330c6..91e7103 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/OWNERS +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/OWNERS
@@ -2,3 +2,5 @@ nyquist@chromium.org yfriedman@chromium.org zea@chromium.org + +# COMPONENT: Services>Sync
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkIntegrationTest.java new file mode 100644 index 0000000..14c79254 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkIntegrationTest.java
@@ -0,0 +1,87 @@ +// Copyright 2017 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. + +package org.chromium.chrome.browser.webapps; + +import android.content.Intent; +import android.support.test.filters.LargeTest; + +import org.chromium.base.test.util.Feature; +import org.chromium.base.test.util.ScalableTimeout; +import org.chromium.chrome.browser.ShortcutHelper; +import org.chromium.chrome.test.ChromeActivityTestCaseBase; +import org.chromium.chrome.test.util.ChromeTabUtils; +import org.chromium.content.browser.test.util.Criteria; +import org.chromium.content.browser.test.util.CriteriaHelper; +import org.chromium.net.test.EmbeddedTestServer; + +/** Integration tests for WebAPK feature. */ +public class WebApkIntegrationTest extends ChromeActivityTestCaseBase<WebApkActivity> { + private static final long STARTUP_TIMEOUT = ScalableTimeout.scaleTimeout(10000); + + private EmbeddedTestServer mTestServer; + + public WebApkIntegrationTest() { + super(WebApkActivity.class); + } + + public void startWebApkActivity(String webApkPackageName, final String startUrl) + throws InterruptedException { + Intent intent = new Intent(getInstrumentation().getTargetContext(), WebApkActivity.class); + intent.putExtra(ShortcutHelper.EXTRA_WEBAPK_PACKAGE_NAME, webApkPackageName); + intent.putExtra(ShortcutHelper.EXTRA_URL, startUrl); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + setActivity(getInstrumentation().startActivitySync(intent)); + getInstrumentation().waitForIdleSync(); + + CriteriaHelper.pollInstrumentationThread(new Criteria() { + @Override + public boolean isSatisfied() { + return getActivity().getActivityTab() != null; + } + }, STARTUP_TIMEOUT, CriteriaHelper.DEFAULT_POLLING_INTERVAL); + + ChromeTabUtils.waitForTabPageLoaded(getActivity().getActivityTab(), startUrl); + } + + /** Waits for the splash screen to be hidden. */ + public void waitUntilSplashscreenHides() { + CriteriaHelper.pollInstrumentationThread(new Criteria() { + @Override + public boolean isSatisfied() { + return !getActivity().isSplashScreenVisibleForTests(); + } + }); + } + + @Override + public final void startMainActivity() throws InterruptedException { + // Do nothing + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mTestServer = EmbeddedTestServer.createAndStartServer(getInstrumentation().getContext()); + WebApkUpdateManager.setUpdatesEnabledForTesting(false); + } + + @Override + protected void tearDown() throws Exception { + mTestServer.stopAndDestroyServer(); + super.tearDown(); + } + + /** + * Test launching a WebAPK. Test that loading the start page works and that the splashscreen + * eventually hides. + */ + @LargeTest + @Feature({"WebApk"}) + public void testLaunch() throws InterruptedException { + startWebApkActivity("org.chromium.webapk.test", + mTestServer.getURL("/chrome/test/data/android/test.html")); + waitUntilSplashscreenHides(); + } +}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProviderUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProviderUnitTest.java index c4f41ef..bab8d55 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProviderUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/superviseduser/SupervisedUserContentProviderUnitTest.java
@@ -278,11 +278,11 @@ anyString()); WebRestrictionsResult allowed = mSupervisedUserContentProvider.shouldProceed( - "com.google.android.gms.ui", "https://accounts.google.com/reauth"); + "com.google.android.gms", "https://accounts.google.com/reauth"); assertThat(allowed.shouldProceed(), is(true)); WebRestrictionsResult wrongUrl = mSupervisedUserContentProvider.shouldProceed( - "com.google.android.gms.ui", "http://www.example.com"); + "com.google.android.gms", "http://www.example.com"); assertThat(wrongUrl.shouldProceed(), is(false)); WebRestrictionsResult wrongCallingPackage = mSupervisedUserContentProvider.shouldProceed(
diff --git a/chrome/android/sync_shell/OWNERS b/chrome/android/sync_shell/OWNERS index bc330c6..91e7103 100644 --- a/chrome/android/sync_shell/OWNERS +++ b/chrome/android/sync_shell/OWNERS
@@ -2,3 +2,5 @@ nyquist@chromium.org yfriedman@chromium.org zea@chromium.org + +# COMPONENT: Services>Sync
diff --git a/chrome/android/webapk/shell_apk/BUILD.gn b/chrome/android/webapk/shell_apk/BUILD.gn index 7ecd6f6c..46ef6c1b 100644 --- a/chrome/android/webapk/shell_apk/BUILD.gn +++ b/chrome/android/webapk/shell_apk/BUILD.gn
@@ -6,10 +6,6 @@ import("shell_apk_version.gni") declare_args() { - # The origin URL of the WebAPK. Used to generate a unique package name for - # WebAPK. Example: "foo.com" - webapk_manifest_package_origin = "template" - # The browser that the WebAPK will be bound to. webapk_runtime_host = "com.google.android.apps.chrome" @@ -48,10 +44,9 @@ webapk_version_name = "1.0" } -shell_apk_manifest_package = - "org.chromium.webapk.$webapk_manifest_package_origin" - shell_apk_manifest = "$target_gen_dir/shell_apk_manifest/AndroidManifest.xml" +shell_apk_manifest_javatests = + "$target_gen_dir/shell_apk_manifest_javatests/AndroidManifest.xml" jinja_template("shell_apk_manifest") { input = "AndroidManifest.xml" @@ -59,7 +54,7 @@ variables = [ "shell_apk_version=$shell_apk_version", - "manifest_package=$shell_apk_manifest_package", + "manifest_package=org.chromium.webapk", "runtime_host=$webapk_runtime_host", "start_url=$webapk_start_url", "name=$webapk_name", @@ -79,6 +74,33 @@ ] } +# Manifest for instrumentation tests. Cannot be customized via GN args. +jinja_template("shell_apk_manifest_javatests") { + input = "AndroidManifest.xml" + output = shell_apk_manifest_javatests + + variables = [ + "shell_apk_version=$shell_apk_version", + "manifest_package=org.chromium.webapk.test", + "runtime_host=org.chromium.chrome", + "start_url=https://www.template.com/home_page", + "name=Test", + "short_name=Test", + "scope_url=https://www.template.com", + "display_mode=standalone", + "orientation=portrait", + "theme_color=2147483648L", # HostBrowserLauncher#MANIFEST_COLOR_INVALID_OR_MISSING + "background_color=2147483648L", # HostBrowserLauncher#MANIFEST_COLOR_INVALID_OR_MISSING + "icon_urls_and_icon_murmur2_hashes=", + "scope_url_scheme=https", + "scope_url_host=template.com", + "scope_url_path=/", + "web_manifest_url=https://www.template.com/manifest.json", + "version_code=1", + "version_name=1.0", + ] +} + android_resources("shell_apk_resources") { resource_dirs = [ "res" ] custom_package = "org.chromium.webapk.shell_apk" @@ -103,24 +125,47 @@ ] } +template("webapk_tmpl") { + android_apk(target_name) { + forward_variables_from(invoker, + [ + "android_manifest", + "android_manifest_dep", + "apk_name", + ]) + deps = [ + ":webapk_java", + ] + never_incremental = true + + # Force 32 bit mode on 64 bit devices. + native_lib_placeholders = [ "libfoo.so" ] + + if (!is_java_debug) { + proguard_enabled = true + proguard_configs = [ + "//chrome/android/java/proguard.flags", + "//base/android/proguard/chromium_apk.flags", + "//base/android/proguard/chromium_code.flags", + ] + } + } +} + # Template for WebAPK. When a WebAPK is generated: # - Android manifest is customized to the website. # - App icon is extracted from the website and added to the APK's resources. -android_apk("webapk") { +webapk_tmpl("webapk") { android_manifest = shell_apk_manifest android_manifest_dep = ":shell_apk_manifest" - apk_name = "WebApk.$webapk_manifest_package_origin" - deps = [ - ":webapk_java", - ] - if (!is_java_debug) { - proguard_enabled = true - proguard_configs = [ - "//chrome/android/java/proguard.flags", - "//base/android/proguard/chromium_apk.flags", - "//base/android/proguard/chromium_code.flags", - ] - } + apk_name = "webapk" +} + +# WebAPK for instrumentation tests. +webapk_tmpl("webapk_javatests") { + android_manifest = shell_apk_manifest_javatests + android_manifest_dep = ":shell_apk_manifest_javatests" + apk_name = "webapk_javatests" } android_library("shell_apk_javatests") {
diff --git a/chrome/app/nibs/BookmarkBar.xib b/chrome/app/nibs/BookmarkBar.xib index c1cfabd8..a3d01fad 100644 --- a/chrome/app/nibs/BookmarkBar.xib +++ b/chrome/app/nibs/BookmarkBar.xib
@@ -34,7 +34,7 @@ <rect key="frame" x="0.0" y="-2" width="584" height="144"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/> <subviews> - <customView hidden="YES" id="109" customClass="BookmarkBarItemContainer"> + <customView hidden="YES" id="109" customClass="GTMWidthBasedTweaker"> <rect key="frame" x="0.0" y="103" width="596" height="41"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES"/> <subviews> @@ -49,7 +49,7 @@ <action selector="importBookmarks:" target="-2" id="116"/> </connections> </button> - <textField verticalHuggingPriority="750" id="82" customClass="BookmarkBarTextField"> + <textField verticalHuggingPriority="750" id="82" customClass="NSTextField"> <rect key="frame" x="5" y="20" width="167" height="14"/> <autoresizingMask key="autoresizingMask" widthSizable="YES"/> <textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingTail" enabled="NO" sendsActionOnEndEditing="YES" title="^IDS_BOOKMARKS_NO_ITEMS" id="83">
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp index e796328..cf5ff1ed 100644 --- a/chrome/app/settings_strings.grdp +++ b/chrome/app/settings_strings.grdp
@@ -325,6 +325,9 @@ <message name="IDS_SETTINGS_CONTROLLED_BY_EXTENSION" desc="Label text of an indicator that a setting's value is being controlled by an extension."> <ph name="NAME">$1<ex>Adblocker plusplus</ex></ph> is controlling this setting </message> + <message name="IDS_SETTINGS_CLEAR" desc="Label used on a menu item or button to clear an item."> + Clear + </message> <message name="IDS_SETTINGS_EDIT" desc="Label used on a menu item or button, used in different contexts to edit an item."> Edit </message> @@ -991,6 +994,9 @@ <message name="IDS_SETTINGS_DISCONNECT_GOOGLE_DRIVE" desc="Label for the checkbox which enables disconnecting from Google Drive account."> Disconnect Google Drive account </message> + <message name="IDS_SETTINGS_OPEN_FILE_TYPES_AUTOMATICALLY" desc="The information label for the 'Clear auto-opening settings' button"> + Open certain file types automatically after downloading + </message> <if expr="chromeos"> <!-- Date/Time Page -->
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index bc9d0c29..99001871 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -1458,6 +1458,7 @@ "//components/offline_pages/core", "//components/offline_pages/core/background:background_offliner", "//components/offline_pages/core/downloads:offline_pages_ui_adapter", + "//components/offline_pages/core/recent_tabs", "//components/offline_pages/core/request_header:request_header", "//components/omnibox/browser", "//components/os_crypt",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 8f2e9b0..f726549 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -211,13 +211,8 @@ const FeatureEntry::Choice kMarkHttpAsChoices[] = { {IDS_GENERIC_EXPERIMENT_CHOICE_DEFAULT, "", ""}, - {IDS_MARK_HTTP_AS_NEUTRAL, security_state::switches::kMarkHttpAs, - security_state::switches::kMarkHttpAsNeutral}, {IDS_MARK_HTTP_AS_DANGEROUS, security_state::switches::kMarkHttpAs, - security_state::switches::kMarkHttpAsDangerous}, - {IDS_MARK_HTTP_WITH_PASSWORDS_OR_CC_WITH_CHIP, - security_state::switches::kMarkHttpAs, - security_state::switches::kMarkHttpWithPasswordsOrCcWithChip}}; + security_state::switches::kMarkHttpAsDangerous}}; const FeatureEntry::Choice kDataReductionProxyLoFiChoices[] = { { IDS_GENERIC_EXPERIMENT_CHOICE_DEFAULT, "", "" },
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc index 76962c3..0272b99 100644 --- a/chrome/browser/android/chrome_feature_list.cc +++ b/chrome/browser/android/chrome_feature_list.cc
@@ -57,6 +57,7 @@ &kNTPCondensedLayoutFeature, &kNTPCondensedTileLayoutFeature, &kNTPFakeOmniboxTextFeature, + &kNTPLaunchAfterInactivity, &kNTPOfflinePagesFeature, &NTPShowGoogleGInOmniboxFeature, &kNTPSuggestionsStandaloneUIFeature, @@ -133,15 +134,18 @@ const base::Feature kNoCreditCardAbort{"NoCreditCardAbort", base::FEATURE_DISABLED_BY_DEFAULT}; -const base::Feature kNTPFakeOmniboxTextFeature{ - "NTPFakeOmniboxText", base::FEATURE_DISABLED_BY_DEFAULT}; - const base::Feature kNTPCondensedLayoutFeature{ "NTPCondensedLayout", base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kNTPCondensedTileLayoutFeature{ "NTPCondensedTileLayout", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kNTPFakeOmniboxTextFeature{ + "NTPFakeOmniboxText", base::FEATURE_DISABLED_BY_DEFAULT}; + +const base::Feature kNTPLaunchAfterInactivity{ + "NTPLaunchAfterInactivity", base::FEATURE_DISABLED_BY_DEFAULT}; + const base::Feature kNTPOfflinePagesFeature{"NTPOfflinePages", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h index 1951766..a4bfc83 100644 --- a/chrome/browser/android/chrome_feature_list.h +++ b/chrome/browser/android/chrome_feature_list.h
@@ -29,6 +29,7 @@ extern const base::Feature kNTPCondensedLayoutFeature; extern const base::Feature kNTPCondensedTileLayoutFeature; extern const base::Feature kNTPFakeOmniboxTextFeature; +extern const base::Feature kNTPLaunchAfterInactivity; extern const base::Feature kNTPOfflinePagesFeature; extern const base::Feature NTPShowGoogleGInOmniboxFeature; extern const base::Feature kNTPSuggestionsStandaloneUIFeature;
diff --git a/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.cc b/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.cc index f3d5f2f2..eae5197 100644 --- a/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.cc +++ b/chrome/browser/android/offline_pages/downloads/offline_page_download_bridge.cc
@@ -54,6 +54,7 @@ // DownloadUIAdapter::Delegate bool IsVisibleInUI(const ClientId& client_id) override; bool IsTemporarilyHiddenInUI(const ClientId& client_id) override; + void SetUIAdapter(DownloadUIAdapter* ui_adapter) override; private: // Not owned, cached service pointer. @@ -74,6 +75,8 @@ return false; } +void DownloadUIAdapterDelegate::SetUIAdapter(DownloadUIAdapter* ui_adapter) {} + // TODO(dewittj): Move to Download UI Adapter. content::WebContents* GetWebContentsFromJavaTab( const ScopedJavaGlobalRef<jobject>& j_tab_ref) {
diff --git a/chrome/browser/android/offline_pages/offline_page_bridge.cc b/chrome/browser/android/offline_pages/offline_page_bridge.cc index 126025e..15bef35 100644 --- a/chrome/browser/android/offline_pages/offline_page_bridge.cc +++ b/chrome/browser/android/offline_pages/offline_page_bridge.cc
@@ -19,6 +19,7 @@ #include "chrome/browser/android/offline_pages/offline_page_mhtml_archiver.h" #include "chrome/browser/android/offline_pages/offline_page_model_factory.h" #include "chrome/browser/android/offline_pages/offline_page_utils.h" +#include "chrome/browser/android/offline_pages/recent_tab_helper.h" #include "chrome/browser/android/offline_pages/request_coordinator_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_android.h" @@ -28,6 +29,7 @@ #include "components/offline_pages/core/offline_page_feature.h" #include "components/offline_pages/core/offline_page_item.h" #include "components/offline_pages/core/offline_page_model.h" +#include "components/offline_pages/core/recent_tabs/recent_tabs_ui_adapter_delegate.h" #include "components/offline_pages/core/request_header/offline_page_header.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/web_contents.h" @@ -572,6 +574,48 @@ request_ids, base::Bind(&OnRemoveRequestsDone, j_callback_ref)); } +void OfflinePageBridge::RegisterRecentTab(JNIEnv* env, + const JavaParamRef<jobject>& obj, + int tab_id) { + RequestCoordinator* request_coordinator = + RequestCoordinatorFactory::GetForBrowserContext(browser_context_); + + RecentTabsUIAdapterDelegate* ui_adapter_delegate = + RecentTabsUIAdapterDelegate::FromDownloadUIAdapter( + RecentTabsUIAdapterDelegate::GetOrCreateRecentTabsUIAdapter( + offline_page_model_, request_coordinator)); + ui_adapter_delegate->RegisterTab(tab_id); +} + +void OfflinePageBridge::WillCloseTab( + JNIEnv* env, + const JavaParamRef<jobject>& obj, + const JavaParamRef<jobject>& j_web_contents) { + DCHECK(j_web_contents); + content::WebContents* web_contents = + content::WebContents::FromJavaWebContents(j_web_contents); + DCHECK(web_contents); + if (!web_contents) + return; + + RecentTabHelper* tab_helper = RecentTabHelper::FromWebContents(web_contents); + if (tab_helper) + tab_helper->WillCloseTab(); +} + +void OfflinePageBridge::UnregisterRecentTab(JNIEnv* env, + const JavaParamRef<jobject>& obj, + int tab_id) { + RequestCoordinator* request_coordinator = + RequestCoordinatorFactory::GetForBrowserContext(browser_context_); + + RecentTabsUIAdapterDelegate* ui_adapter_delegate = + RecentTabsUIAdapterDelegate::FromDownloadUIAdapter( + RecentTabsUIAdapterDelegate::GetOrCreateRecentTabsUIAdapter( + offline_page_model_, request_coordinator)); + ui_adapter_delegate->UnregisterTab(tab_id); +} + void OfflinePageBridge::NotifyIfDoneLoading() const { if (!offline_page_model_->is_loaded()) return;
diff --git a/chrome/browser/android/offline_pages/offline_page_bridge.h b/chrome/browser/android/offline_pages/offline_page_bridge.h index f0ee0d17..ddf12fe 100644 --- a/chrome/browser/android/offline_pages/offline_page_bridge.h +++ b/chrome/browser/android/offline_pages/offline_page_bridge.h
@@ -125,6 +125,16 @@ const base::android::JavaParamRef<jlongArray>& j_request_ids_array, const base::android::JavaParamRef<jobject>& j_callback_obj); + void RegisterRecentTab(JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj, + int tab_id); + void WillCloseTab(JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj, + const base::android::JavaParamRef<jobject>& j_web_contents); + void UnregisterRecentTab(JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj, + int tab_id); + base::android::ScopedJavaGlobalRef<jobject> java_ref() { return java_ref_; } private:
diff --git a/chrome/browser/android/offline_pages/recent_tab_helper.cc b/chrome/browser/android/offline_pages/recent_tab_helper.cc index a4afe42..191754d8f 100644 --- a/chrome/browser/android/offline_pages/recent_tab_helper.cc +++ b/chrome/browser/android/offline_pages/recent_tab_helper.cc
@@ -270,10 +270,11 @@ // Return immediately if last_n is not listening to tab hidden events, if a // last_n snapshot is currently being saved or if the tab is closing. - if (!last_n_listen_to_tab_hidden_ || last_n_ongoing_snapshot_info_) { + if (!last_n_listen_to_tab_hidden_ || last_n_ongoing_snapshot_info_ || + tab_is_closing_) { DVLOG(1) << "Will not snapshot for last_n (reasons: " << !last_n_listen_to_tab_hidden_ << ", " - << !!last_n_ongoing_snapshot_info_ + << !!last_n_ongoing_snapshot_info_ << ", " << tab_is_closing_ << ") for: " << web_contents()->GetLastCommittedURL().spec(); return; } @@ -300,6 +301,19 @@ last_n_ongoing_snapshot_info_.get())); } +void RecentTabHelper::WasShown() { + // If the tab was closing and is now being shown, the closure was reverted. + DVLOG_IF(0, tab_is_closing_) << "Tab is not closing anymore: " + << web_contents()->GetLastCommittedURL().spec(); + tab_is_closing_ = false; +} + +void RecentTabHelper::WillCloseTab() { + DVLOG(1) << "Tab is now closing: " + << web_contents()->GetLastCommittedURL().spec(); + tab_is_closing_ = true; +} + // TODO(carlosk): rename this to RequestSnapshot and make it return a bool // representing the acceptance of the snapshot request. void RecentTabHelper::StartSnapshot() {
diff --git a/chrome/browser/android/offline_pages/recent_tab_helper.h b/chrome/browser/android/offline_pages/recent_tab_helper.h index 50a3cda..6c43a23b 100644 --- a/chrome/browser/android/offline_pages/recent_tab_helper.h +++ b/chrome/browser/android/offline_pages/recent_tab_helper.h
@@ -40,6 +40,11 @@ void DocumentOnLoadCompletedInMainFrame() override; void WebContentsDestroyed() override; void WasHidden() override; + void WasShown() override; + + // Notifies that the tab of the associated WebContents will (most probably) be + // closed. This call is expected to always happen before the one to WasHidden. + void WillCloseTab(); // SnapshotController::Client void StartSnapshot() override; @@ -144,6 +149,10 @@ // current page being loaded. bool last_n_listen_to_tab_hidden_ = false; + // Set to true when the tab containing the associated WebContents is in the + // process of being closed. + bool tab_is_closing_ = false; + base::WeakPtrFactory<RecentTabHelper> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(RecentTabHelper);
diff --git a/chrome/browser/android/offline_pages/recent_tab_helper_unittest.cc b/chrome/browser/android/offline_pages/recent_tab_helper_unittest.cc index c9626478..507bd80 100644 --- a/chrome/browser/android/offline_pages/recent_tab_helper_unittest.cc +++ b/chrome/browser/android/offline_pages/recent_tab_helper_unittest.cc
@@ -887,4 +887,31 @@ ASSERT_EQ(1U, GetAllPages().size()); } +// Checks that a closing tab doesn't trigger the creation of a snapshot. And +// also that if the closure is reverted, a snapshot is saved upon the next hide +// event. +TEST_F(RecentTabHelperTest, NoSaveIfTabIsClosing) { + // Navigates and fully load then close and hide the tab. No snapshots are + // expected. + NavigateAndCommit(kTestPageUrl); + recent_tab_helper()->DocumentOnLoadCompletedInMainFrame(); + FastForwardSnapshotController(); + // Note: These two next calls are always expected to happen in this order. + recent_tab_helper()->WillCloseTab(); + recent_tab_helper()->WasHidden(); + RunUntilIdle(); + EXPECT_EQ(0U, page_added_count()); + EXPECT_EQ(0U, model_removed_count()); + ASSERT_EQ(0U, GetAllPages().size()); + + // Simulates the page being restored and shown again, then hidden. At this + // moment a snapshot should be created. + recent_tab_helper()->WasShown(); + recent_tab_helper()->WasHidden(); + RunUntilIdle(); + EXPECT_EQ(1U, page_added_count()); + EXPECT_EQ(0U, model_removed_count()); + ASSERT_EQ(1U, GetAllPages().size()); +} + } // namespace offline_pages
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index 4498926..6e3e8103 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -159,12 +159,16 @@ "accessibility/accessibility_util.h", "accessibility/chromevox_panel.cc", "accessibility/chromevox_panel.h", + "accessibility/event_handler_common.cc", + "accessibility/event_handler_common.h", "accessibility/magnification_manager.cc", "accessibility/magnification_manager.h", "accessibility/select_to_speak_event_handler.cc", "accessibility/select_to_speak_event_handler.h", "accessibility/spoken_feedback_event_rewriter.cc", "accessibility/spoken_feedback_event_rewriter.h", + "accessibility/switch_access_event_handler.cc", + "accessibility/switch_access_event_handler.h", "app_mode/app_launch_utils.cc", "app_mode/app_launch_utils.h", "app_mode/app_session.cc",
diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.cc b/chrome/browser/chromeos/accessibility/accessibility_manager.cc index 6cc9829..9e25cb6b3 100644 --- a/chrome/browser/chromeos/accessibility/accessibility_manager.cc +++ b/chrome/browser/chromeos/accessibility/accessibility_manager.cc
@@ -41,6 +41,7 @@ #include "chrome/browser/chromeos/accessibility/accessibility_highlight_manager.h" #include "chrome/browser/chromeos/accessibility/magnification_manager.h" #include "chrome/browser/chromeos/accessibility/select_to_speak_event_handler.h" +#include "chrome/browser/chromeos/accessibility/switch_access_event_handler.h" #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" @@ -940,8 +941,11 @@ if (enabled) { switch_access_loader_->Load(profile_, base::Closure() /* done_cb */); + switch_access_event_handler_.reset( + new chromeos::SwitchAccessEventHandler()); } else { switch_access_loader_->Unload(); + switch_access_event_handler_.reset(nullptr); } }
diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.h b/chrome/browser/chromeos/accessibility/accessibility_manager.h index c224c732..94ba83db 100644 --- a/chrome/browser/chromeos/accessibility/accessibility_manager.h +++ b/chrome/browser/chromeos/accessibility/accessibility_manager.h
@@ -37,6 +37,7 @@ class AccessibilityExtensionLoader; class AccessibilityHighlightManager; class SelectToSpeakEventHandler; +class SwitchAccessEventHandler; enum AccessibilityNotificationType { ACCESSIBILITY_MANAGER_SHUTDOWN, @@ -421,6 +422,9 @@ std::unique_ptr<AccessibilityExtensionLoader> switch_access_loader_; + std::unique_ptr<chromeos::SwitchAccessEventHandler> + switch_access_event_handler_; + base::WeakPtrFactory<AccessibilityManager> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(AccessibilityManager);
diff --git a/chrome/browser/chromeos/accessibility/event_handler_common.cc b/chrome/browser/chromeos/accessibility/event_handler_common.cc new file mode 100644 index 0000000..d1da17a1 --- /dev/null +++ b/chrome/browser/chromeos/accessibility/event_handler_common.cc
@@ -0,0 +1,54 @@ +// Copyright 2017 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/chromeos/accessibility/event_handler_common.h" + +#include "chrome/browser/chromeos/accessibility/accessibility_manager.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_widget_host.h" +#include "extensions/browser/extension_host.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/process_manager.h" + +namespace chromeos { + +extensions::ExtensionHost* GetAccessibilityExtensionHost( + const std::string& extension_id) { + if (!AccessibilityManager::Get()) + return nullptr; + + content::BrowserContext* context = ProfileManager::GetActiveUserProfile(); + if (!context) + return nullptr; + + const extensions::Extension* extension = + extensions::ExtensionRegistry::Get(context)->enabled_extensions().GetByID( + extension_id); + if (!extension) + return nullptr; + + extensions::ExtensionHost* host = + extensions::ProcessManager::Get(context)->GetBackgroundHostForExtension( + extension->id()); + return host; +} + +void ForwardKeyToExtension(const ui::KeyEvent& key_event, + extensions::ExtensionHost* host) { + if (!host) { + LOG(ERROR) << "Unable to forward key to extension"; + return; + } + + content::RenderViewHost* rvh = host->render_view_host(); + if (!rvh) { + LOG(ERROR) << "Unable to forward key to extension"; + return; + } + + const content::NativeWebKeyboardEvent web_event(key_event); + rvh->GetWidget()->ForwardKeyboardEvent(web_event); +} +} // namespace chromeos
diff --git a/chrome/browser/chromeos/accessibility/event_handler_common.h b/chrome/browser/chromeos/accessibility/event_handler_common.h new file mode 100644 index 0000000..d65bfa3b --- /dev/null +++ b/chrome/browser/chromeos/accessibility/event_handler_common.h
@@ -0,0 +1,24 @@ +// Copyright 2017 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_CHROMEOS_ACCESSIBILITY_EVENT_HANDLER_COMMON_H_ +#define CHROME_BROWSER_CHROMEOS_ACCESSIBILITY_EVENT_HANDLER_COMMON_H_ + +#include "extensions/browser/extension_host.h" +#include "ui/events/event_handler.h" + +namespace chromeos { + +// Gets the extension host for the corresponding extension ID. +extensions::ExtensionHost* GetAccessibilityExtensionHost( + const std::string& extension_id); + +// Forwards the key event to the extension background page for the +// corresponding host. +void ForwardKeyToExtension(const ui::KeyEvent& key_event, + extensions::ExtensionHost* host); + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_ACCESSIBILITY_EVENT_HANDLER_COMMON_H_
diff --git a/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.cc b/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.cc index 62ddc312..e4d237a 100644 --- a/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.cc +++ b/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.cc
@@ -5,6 +5,7 @@ #include "chrome/browser/chromeos/accessibility/select_to_speak_event_handler.h" #include "ash/shell.h" +#include "base/logging.h" #include "chrome/browser/speech/tts_controller.h" #include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h" #include "content/public/browser/browser_thread.h" @@ -28,6 +29,8 @@ } void SelectToSpeakEventHandler::OnKeyEvent(ui::KeyEvent* event) { + DCHECK(event); + // We can only call TtsController on the UI thread, make sure we // don't ever try to run this code on some other thread. CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); @@ -73,6 +76,7 @@ } void SelectToSpeakEventHandler::OnMouseEvent(ui::MouseEvent* event) { + DCHECK(event); if (state_ == INACTIVE) return; @@ -157,6 +161,7 @@ } void SelectToSpeakEventHandler::CancelEvent(ui::Event* event) { + DCHECK(event); if (event->cancelable()) { event->SetHandled(); event->StopPropagation();
diff --git a/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter.cc b/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter.cc index 6808e22..d0c2956 100644 --- a/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter.cc +++ b/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter.cc
@@ -9,15 +9,11 @@ #include "ash/shell.h" #include "chrome/browser/chromeos/accessibility/accessibility_manager.h" -#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/chromeos/accessibility/event_handler_common.h" #include "chrome/common/extensions/extension_constants.h" -#include "content/public/browser/render_view_host.h" -#include "content/public/browser/render_widget_host.h" #include "content/public/browser/web_contents.h" #include "extensions/browser/event_router.h" #include "extensions/browser/extension_host.h" -#include "extensions/browser/extension_registry.h" -#include "extensions/browser/process_manager.h" #include "ui/aura/window_tree_host.h" #include "ui/content_accelerators/accelerator_util.h" #include "ui/events/event.h" @@ -32,26 +28,6 @@ bool SpokenFeedbackEventRewriterDelegate::DispatchKeyEventToChromeVox( const ui::KeyEvent& key_event, bool capture) { - if (!chromeos::AccessibilityManager::Get()) - return false; - - content::BrowserContext* context = ProfileManager::GetActiveUserProfile(); - if (!context) - return false; - - const extensions::Extension* extension = - extensions::ExtensionRegistry::Get(context)->enabled_extensions().GetByID( - extension_misc::kChromeVoxExtensionId); - if (!extension) - return false; - - extensions::ExtensionHost* host = - extensions::ProcessManager::Get(context) - ->GetBackgroundHostForExtension(extension->id()); - if (!host) - return false; - - content::RenderViewHost* rvh = host->render_view_host(); // Always capture the Search key. capture |= key_event.IsCommandDown(); @@ -63,6 +39,11 @@ if (key_event.GetDomKey() == ui::DomKey::TAB) capture = false; + extensions::ExtensionHost* host = chromeos::GetAccessibilityExtensionHost( + extension_misc::kChromeVoxExtensionId); + if (!host) + return false; + // Listen for any unhandled keyboard events from ChromeVox's background page // when capturing keys to reinject. if (capture) @@ -71,8 +52,7 @@ host->host_contents()->SetDelegate(nullptr); // Forward all key events to ChromeVox's background page. - const content::NativeWebKeyboardEvent web_event(key_event); - rvh->GetWidget()->ForwardKeyboardEvent(web_event); + chromeos::ForwardKeyToExtension(key_event, host); if ((key_event.key_code() >= ui::VKEY_F1) && (key_event.key_code() <= ui::VKEY_F12))
diff --git a/chrome/browser/chromeos/accessibility/switch_access_event_handler.cc b/chrome/browser/chromeos/accessibility/switch_access_event_handler.cc new file mode 100644 index 0000000..2542156 --- /dev/null +++ b/chrome/browser/chromeos/accessibility/switch_access_event_handler.cc
@@ -0,0 +1,56 @@ +// Copyright 2017 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/chromeos/accessibility/switch_access_event_handler.h" + +#include "ash/shell.h" +#include "base/logging.h" +#include "chrome/browser/chromeos/accessibility/accessibility_manager.h" +#include "chrome/browser/chromeos/accessibility/event_handler_common.h" +#include "chrome/common/extensions/extension_constants.h" +#include "extensions/browser/extension_host.h" +#include "ui/events/event.h" + +namespace chromeos { + +SwitchAccessEventHandler::SwitchAccessEventHandler() { + if (ash::Shell::HasInstance()) + ash::Shell::GetInstance()->AddPreTargetHandler(this); +} + +SwitchAccessEventHandler::~SwitchAccessEventHandler() { + if (ash::Shell::HasInstance()) + ash::Shell::GetInstance()->RemovePreTargetHandler(this); +} + +void SwitchAccessEventHandler::OnKeyEvent(ui::KeyEvent* event) { + DCHECK(event); + + ui::KeyboardCode key_code = event->key_code(); + if (key_code == ui::VKEY_1 || key_code == ui::VKEY_2 || + key_code == ui::VKEY_3 || key_code == ui::VKEY_4 || + key_code == ui::VKEY_5) { + CancelEvent(event); + LOG(ERROR) << "Dispatching key " << key_code - ui::VKEY_0 + << " to switch access"; + DispatchKeyEventToSwitchAccess(*event); + } +} + +void SwitchAccessEventHandler::CancelEvent(ui::Event* event) { + DCHECK(event); + if (event->cancelable()) { + event->SetHandled(); + event->StopPropagation(); + } +} + +void SwitchAccessEventHandler::DispatchKeyEventToSwitchAccess( + const ui::KeyEvent& event) { + extensions::ExtensionHost* host = + GetAccessibilityExtensionHost(extension_misc::kSwitchAccessExtensionId); + ForwardKeyToExtension(event, host); +} + +} // namespace chromeos
diff --git a/chrome/browser/chromeos/accessibility/switch_access_event_handler.h b/chrome/browser/chromeos/accessibility/switch_access_event_handler.h new file mode 100644 index 0000000..331f836 --- /dev/null +++ b/chrome/browser/chromeos/accessibility/switch_access_event_handler.h
@@ -0,0 +1,37 @@ +// Copyright 2017 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_CHROMEOS_ACCESSIBILITY_SWITCH_ACCESS_EVENT_HANDLER_H_ +#define CHROME_BROWSER_CHROMEOS_ACCESSIBILITY_SWITCH_ACCESS_EVENT_HANDLER_H_ + +#include "base/macros.h" +#include "ui/events/event_handler.h" + +namespace chromeos { + +// When Switch Access is enabled, intercepts a few keys before the rest of the +// OS has a chance to handle them, as a method for controlling the entire OS +// with a small number of switches. The key events do not propagate to the rest +// of the system. +// +// This class just intercepts the keys and forwards them to the component +// extension. +class SwitchAccessEventHandler : public ui::EventHandler { + public: + SwitchAccessEventHandler(); + ~SwitchAccessEventHandler() override; + + private: + // EventHandler: + void OnKeyEvent(ui::KeyEvent* event) override; + + void CancelEvent(ui::Event* event); + void DispatchKeyEventToSwitchAccess(const ui::KeyEvent& event); + + DISALLOW_COPY_AND_ASSIGN(SwitchAccessEventHandler); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_ACCESSIBILITY_SWITCH_ACCESS_EVENT_HANDLER_H_
diff --git a/chrome/browser/chromeos/display/output_protection_delegate.cc b/chrome/browser/chromeos/display/output_protection_delegate.cc index e5a9041..e057004 100644 --- a/chrome/browser/chromeos/display/output_protection_delegate.cc +++ b/chrome/browser/chromeos/display/output_protection_delegate.cc
@@ -6,9 +6,9 @@ #include "chrome/browser/chromeos/display/output_protection_controller_ash.h" #include "chrome/browser/chromeos/display/output_protection_controller_mus.h" +#include "chrome/browser/ui/ash/ash_util.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_frame_host.h" -#include "services/service_manager/runner/common/client_util.h" #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/display/types/display_constants.h" @@ -17,10 +17,6 @@ namespace { -bool IsRunningInMash() { - return service_manager::ServiceManagerIsRemote(); -} - bool GetCurrentDisplayId(content::RenderFrameHost* rfh, int64_t* display_id) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK(rfh); @@ -106,7 +102,7 @@ if (!window) return false; - if (IsRunningInMash()) + if (chrome::IsRunningInMash()) controller_ = base::MakeUnique<OutputProtectionControllerMus>(); else controller_ = base::MakeUnique<OutputProtectionControllerAsh>();
diff --git a/chrome/browser/conflicts/module_database_win.cc b/chrome/browser/conflicts/module_database_win.cc index 399d931..da6397a 100644 --- a/chrome/browser/conflicts/module_database_win.cc +++ b/chrome/browser/conflicts/module_database_win.cc
@@ -226,7 +226,8 @@ // This handles the special case of removing the max element in O(1), as // FindLoadAddressIndexById processes the elements in reverse order. size_t i = FindLoadAddressIndexById(module_id, *load_addresses); - RemoveLoadAddressByIndex(i, load_addresses); + if (i != kInvalidIndex) + RemoveLoadAddressByIndex(i, load_addresses); } // static
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc index e32baa1..a0054d0 100644 --- a/chrome/browser/extensions/api/settings_private/prefs_util.cc +++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -478,6 +478,7 @@ #endif const Extension* extension = GetExtensionControllingPref(*pref_object); + if (extension) { pref_object->controlled_by = settings_private::ControlledBy::CONTROLLED_BY_EXTENSION; @@ -738,7 +739,15 @@ if (pref_object.key == proxy_config::prefs::kProxy) return GetExtensionOverridingProxy(profile_); - return nullptr; + // If it's none of the above, attempt a more general strategy. + std::string extension_id = + ExtensionPrefValueMapFactory::GetForBrowserContext(profile_) + ->GetExtensionControllingPref(pref_object.key); + if (extension_id.empty()) + return nullptr; + + return ExtensionRegistry::Get(profile_)->GetExtensionById( + extension_id, ExtensionRegistry::ENABLED); } } // namespace extensions
diff --git a/chrome/browser/media/android/router/media_router_android.cc b/chrome/browser/media/android/router/media_router_android.cc index 91cae7090..0dc832c1 100644 --- a/chrome/browser/media/android/router/media_router_android.cc +++ b/chrome/browser/media/android/router/media_router_android.cc
@@ -184,15 +184,8 @@ const MediaRoute::Id& route_id, std::unique_ptr<std::vector<uint8_t>> data, const SendRouteMessageCallback& callback) { - int callback_id = message_callbacks_.Add( - base::MakeUnique<SendRouteMessageCallback>(callback)); - JNIEnv* env = base::android::AttachCurrentThread(); - ScopedJavaLocalRef<jstring> jroute_id = - base::android::ConvertUTF8ToJavaString(env, route_id); - ScopedJavaLocalRef<jbyteArray> jbyte_array = - base::android::ToJavaByteArray(env, &((*data)[0]), data->size()); - Java_ChromeMediaRouter_sendBinaryMessage(env, java_media_router_, jroute_id, - jbyte_array, callback_id); + // Binary messaging is not supported on Android. + callback.Run(false); } void MediaRouterAndroid::AddIssue(const IssueInfo& issue_info) {
diff --git a/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc b/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc index 8070663d..fce8e2e 100644 --- a/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc +++ b/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc
@@ -59,21 +59,26 @@ #include "chrome/browser/android/chrome_feature_list.h" #include "chrome/browser/android/ntp/ntp_snippets_launcher.h" #include "chrome/browser/android/offline_pages/offline_page_model_factory.h" +#include "chrome/browser/android/offline_pages/request_coordinator_factory.h" #include "chrome/browser/download/download_history.h" #include "chrome/browser/download/download_service.h" #include "chrome/browser/download/download_service_factory.h" #include "chrome/browser/ntp_snippets/download_suggestions_provider.h" #include "components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.h" #include "components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h" +#include "components/offline_pages/core/background/request_coordinator.h" #include "components/offline_pages/core/offline_page_feature.h" #include "components/offline_pages/core/offline_page_model.h" +#include "components/offline_pages/core/recent_tabs/recent_tabs_ui_adapter_delegate.h" #include "components/physical_web/data_source/physical_web_data_source.h" using content::DownloadManager; using ntp_snippets::PhysicalWebPageSuggestionsProvider; using ntp_snippets::RecentTabSuggestionsProvider; using offline_pages::OfflinePageModel; +using offline_pages::RequestCoordinator; using offline_pages::OfflinePageModelFactory; +using offline_pages::RequestCoordinatorFactory; using physical_web::PhysicalWebDataSource; #endif // OS_ANDROID @@ -120,10 +125,14 @@ } void RegisterRecentTabProvider(OfflinePageModel* offline_page_model, + RequestCoordinator* request_coordinator, ContentSuggestionsService* service, PrefService* pref_service) { + offline_pages::DownloadUIAdapter* ui_adapter = offline_pages:: + RecentTabsUIAdapterDelegate::GetOrCreateRecentTabsUIAdapter( + offline_page_model, request_coordinator); auto provider = base::MakeUnique<RecentTabSuggestionsProvider>( - service, offline_page_model, pref_service); + service, ui_adapter, pref_service); service->RegisterProvider(std::move(provider)); } @@ -290,6 +299,8 @@ #if defined(OS_ANDROID) OfflinePageModel* offline_page_model = OfflinePageModelFactory::GetForBrowserContext(profile); + RequestCoordinator* request_coordinator = + RequestCoordinatorFactory::GetForBrowserContext(profile); DownloadManager* download_manager = content::BrowserContext::GetDownloadManager(profile); DownloadService* download_service = @@ -309,7 +320,8 @@ #if defined(OS_ANDROID) if (IsRecentTabProviderEnabled()) { - RegisterRecentTabProvider(offline_page_model, service, pref_service); + RegisterRecentTabProvider(offline_page_model, request_coordinator, service, + pref_service); } bool show_asset_downloads =
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc index bd43931..bcaa73d 100644 --- a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc +++ b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
@@ -146,7 +146,7 @@ ~TimingUpdatedObserver() override {} std::unique_ptr<base::RunLoop> run_loop_; - int matching_fields_; // A bitvector composed from ExpectedTimingFields. + int matching_fields_ = 0; // A bitvector composed from ExpectedTimingFields. bool matched_timing_update_ = false; int match_document_write_block_reload_ = 0; };
diff --git a/chrome/browser/resources/chromeos/switch_access/compiled_resources2.gyp b/chrome/browser/resources/chromeos/switch_access/compiled_resources2.gyp new file mode 100644 index 0000000..c070260b --- /dev/null +++ b/chrome/browser/resources/chromeos/switch_access/compiled_resources2.gyp
@@ -0,0 +1,17 @@ +# Copyright 2017 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. + +{ + 'targets': [ + { + 'target_name': 'switch_access', + 'dependencies': [ + '<(EXTERNS_GYP):accessibility_private', + '<(EXTERNS_GYP):automation', + '<(EXTERNS_GYP):chrome_extensions', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + ], +}
diff --git a/chrome/browser/resources/chromeos/switch_access/switch_access.js b/chrome/browser/resources/chromeos/switch_access/switch_access.js index ce21b3a..5b35495 100644 --- a/chrome/browser/resources/chromeos/switch_access/switch_access.js +++ b/chrome/browser/resources/chromeos/switch_access/switch_access.js
@@ -2,4 +2,152 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -console.log("Switch access is enabled"); +var AutomationNode = chrome.automation.AutomationNode; + +/** + * @constructor + */ +var SwitchAccess = function() { + console.log("Switch access is enabled"); + + // Currently selected node. + /** @private {AutomationNode} */ + this.node_ = null; + + // List of nodes to push to / pop from in case this.node_ is lost. + /** @private {!Array<!AutomationNode>} */ + this.ancestorList_ = []; + + chrome.automation.getDesktop(function(desktop) { + this.node_ = desktop; + console.log("AutomationNode for desktop is loaded"); + this.printDetails_(); + + document.addEventListener("keyup", function(event) { + if (event.key === "1") { + console.log("1 = go to previous element"); + this.moveToPrevious_(); + } else if (event.key === "2") { + console.log("2 = go to next element"); + this.moveToNext_(); + } else if (event.key === "3") { + console.log("3 = go to child element"); + this.moveToFirstChild_(); + } else if (event.key === "4") { + console.log("4 = go to parent element"); + this.moveToParent_(); + } else if (event.key === "5") { + console.log("5 is not yet implemented"); + console.log("\n"); + } + chrome.accessibilityPrivate.setFocusRing([this.node_.location]); + }.bind(this)); + }.bind(this)); +}; + +SwitchAccess.prototype = { + /** + * Move to the previous sibling of this.node_ if it has one. + */ + moveToPrevious_: function() { + var previous = this.node_.previousSibling; + if (previous) { + this.node_ = previous; + this.printDetails_(); + } else { + console.log("Node is first of siblings"); + console.log("\n"); + } + }, + + /** + * Move to the next sibling of this.node_ if it has one. + */ + moveToNext_: function() { + var next = this.node_.nextSibling; + if (next) { + this.node_ = next; + this.printDetails_(); + } else { + console.log("Node is last of siblings"); + console.log("\n"); + } + }, + + /** + * Move to the first child of this.node_ if it has one. + */ + moveToFirstChild_: function() { + var child = this.node_.firstChild; + if (child) { + this.ancestorList_.push(this.node_); + this.node_ = child; + this.printDetails_(); + } else { + console.log("Node has no children"); + console.log("\n"); + } + }, + + /** + * Move to the parent of this.node_ if it has one. If it does not have a + * parent but it is not the top level root node, then this.node_ lost track of + * its neighbors, and we move to an ancestor node. + */ + moveToParent_: function() { + var parent = this.node_.parent; + if (parent) { + this.ancestorList_.pop(); + this.node_ = parent; + this.printDetails_(); + } else if (this.ancestorList_.length === 0) { + console.log("Node has no parent"); + console.log("\n"); + } else { + console.log( + "Node could not find its parent, so moved to recent ancestor"); + var ancestor = this.ancestorList_.pop(); + this.node_ = ancestor; + this.printDetails_(); + } + }, + + /** + * Print out details about the currently selected node and the list of + * ancestors. + * + * @private + */ + printDetails_: function() { + this.printNode_(this.node_); + console.log(this.ancestorList_); + console.log("\n"); + }, + + /** + * Print out details about a node. + * + * @param {AutomationNode} node + * @private + */ + printNode_: function(node) { + if (node) { + console.log("Name = " + node.name); + console.log("Role = " + node.role); + if (!node.parent) { + console.log("At index " + node.indexInParent + ", has no parent"); + } else { + var numSiblings = node.parent.children.length; + console.log( + "At index " + node.indexInParent + ", there are " + + numSiblings + " siblings"); + } + console.log("Has " + node.children.length + " children"); + } else { + console.log("Node is null"); + } + console.log(node); + } +}; + +new SwitchAccess();
diff --git a/chrome/browser/resources/polymer_css_build_gn.py b/chrome/browser/resources/polymer_css_build_gn.py index 7a55fde..14fe7c55 100755 --- a/chrome/browser/resources/polymer_css_build_gn.py +++ b/chrome/browser/resources/polymer_css_build_gn.py
@@ -20,8 +20,8 @@ in_paths = map(lambda f: os.path.join(out_path, f), input_files) out_paths = map(lambda f: os.path.join(out_path, f), output_files) - node.RunNode([node_modules.PathToPolymerCssBuild(), '-f'] + in_paths + - ['-o'] + out_paths) + args = ['--no-inline-includes', '-f'] + in_paths + ['-o'] + out_paths + node.RunNode([node_modules.PathToPolymerCssBuild()] + args) def main():
diff --git a/chrome/browser/resources/print_preview/data/ticket_items/copies.js b/chrome/browser/resources/print_preview/data/ticket_items/copies.js index 91e6acf47..22098be 100644 --- a/chrome/browser/resources/print_preview/data/ticket_items/copies.js +++ b/chrome/browser/resources/print_preview/data/ticket_items/copies.js
@@ -24,14 +24,7 @@ /** @override */ wouldValueBeValid: function(value) { - if (/[^\d]+/.test(value)) { - return false; - } - var copies = parseInt(value, 10); - if (copies > 999 || copies < 1) { - return false; - } - return true; + return value != ''; }, /** @override */ @@ -41,7 +34,8 @@ /** @return {number} The number of copies indicated by the ticket item. */ getValueAsNumber: function() { - return parseInt(this.getValue(), 10); + var value = this.getValue(); + return value == '' ? 0 : parseInt(value, 10); }, /** @override */
diff --git a/chrome/browser/resources/print_preview/data/ticket_items/scaling.js b/chrome/browser/resources/print_preview/data/ticket_items/scaling.js index c7f3a37..aa193597 100644 --- a/chrome/browser/resources/print_preview/data/ticket_items/scaling.js +++ b/chrome/browser/resources/print_preview/data/ticket_items/scaling.js
@@ -26,45 +26,19 @@ documentInfo); }; - /** - * Maximum scaling percentage - * @private {number} - * @const - */ - Scaling.MAX_VAL = 200; - - /** - * Minimum scaling percentage - * @private {number} - * @const - */ - Scaling.MIN_VAL = 10; - Scaling.prototype = { __proto__: print_preview.ticket_items.TicketItem.prototype, /** @override */ wouldValueBeValid: function(value) { - if (/[^\d]+/.test(value)) { - return false; - } - var scaling = parseInt(value, 10); - return scaling >= Scaling.MIN_VAL && scaling <= Scaling.MAX_VAL; - }, + return true; + }, /** @override */ isValueEqual: function(value) { return this.getValueAsNumber() == value; }, - /** - * @param {number} The desired scaling percentage. - * @return {number} Nearest valid scaling percentage - */ - getNearestValidValue: function(value) { - return Math.min(Math.max(value, Scaling.MIN_VAL), Scaling.MAX_VAL); - }, - /** @override */ isCapabilityAvailable: function() { // This is not a function of the printer, but should be disabled if we are
diff --git a/chrome/browser/resources/print_preview/settings/copies_settings.js b/chrome/browser/resources/print_preview/settings/copies_settings.js index 6ac9a99..cfd5bbd 100644 --- a/chrome/browser/resources/print_preview/settings/copies_settings.js +++ b/chrome/browser/resources/print_preview/settings/copies_settings.js
@@ -119,23 +119,20 @@ */ updateState_: function() { if (this.isAvailable()) { - if (this.inputField_.value != this.copiesTicketItem_.getValue()) - this.inputField_.value = this.copiesTicketItem_.getValue(); - - var currentValueGreaterThan1 = false; - if (this.copiesTicketItem_.isValid()) { - this.inputField_.classList.remove('invalid'); - fadeOutElement(this.getChildElement('.hint')); - var currentValue = this.copiesTicketItem_.getValueAsNumber(); - currentValueGreaterThan1 = currentValue > 1; - } else { + if (!this.inputField_.validity.valid) { this.inputField_.classList.add('invalid'); fadeInElement(this.getChildElement('.hint')); + this.getChildElement('.collate-container').hidden = true; + this.updateUiStateInternal(); + return; } - + if (this.inputField_.value != this.copiesTicketItem_.getValue()) + this.inputField_.value = this.copiesTicketItem_.getValue(); + this.inputField_.classList.remove('invalid'); + fadeOutElement(this.getChildElement('.hint')); if (!(this.getChildElement('.collate-container').hidden = !this.collateTicketItem_.isCapabilityAvailable() || - !currentValueGreaterThan1)) { + this.copiesTicketItem_.getValueAsNumber() <= 1)) { this.getChildElement('input.collate').checked = this.collateTicketItem_.getValue(); } @@ -149,10 +146,14 @@ */ onTextfieldTimeout_: function() { this.textfieldTimeout_ = null; - var copiesVal = this.inputField_.value; - if (copiesVal != '') { - this.copiesTicketItem_.updateValue(copiesVal); + var newValue = (this.inputField_.validity.valid && + this.inputField_.value != '') ? + this.inputField_.valueAsNumber.toString() : ''; + if (this.copiesTicketItem_.getValue() === newValue) { + this.updateState_(); + return; } + this.copiesTicketItem_.updateValue(newValue); }, /** @@ -189,7 +190,7 @@ * @private */ onTextfieldBlur_: function() { - if (this.inputField_.value == '') { + if (this.inputField_.validity.valid && this.inputField_.value == '') { if (this.copiesTicketItem_.getValue() == '1') { // No need to update the ticket, but change the display to match. this.inputField_.value = '1';
diff --git a/chrome/browser/resources/print_preview/settings/scaling_settings.js b/chrome/browser/resources/print_preview/settings/scaling_settings.js index 9ac5438..3857127 100644 --- a/chrome/browser/resources/print_preview/settings/scaling_settings.js +++ b/chrome/browser/resources/print_preview/settings/scaling_settings.js
@@ -123,9 +123,7 @@ * @private */ getInputAsNumber: function() { - return (/[^\d]+/.test(this.inputField_.value)) ? - 0 : // Return an invalid scaling so that the hint will display. - parseInt(this.inputField_.value, 10); + return this.inputField_.valueAsNumber; }, /** @@ -138,6 +136,15 @@ }, /** + * Whether the displayed scaling value matches the fit to page scaling. + * @private + */ + displayMatchesFitToPage: function() { + return (this.getInputAsNumber() == this.fitToPageScaling_ || + (this.inputField_.value == '' && !this.fitToPageScaling_)); + }, + + /** * Updates the fit to page scaling value of the scalings settings UI. * @param {number} fitToPageScaling The updated percentage scaling required * to fit the document to page. @@ -156,34 +163,25 @@ updateState_: function() { if (this.isAvailable()) { var displayedValue = this.getInputAsNumber(); - var inputString = this.inputField_.value; - // Display should be updated to match the ticket item unless displayed - // value is invalid and non-blank or fit to page is checked. In these - // cases, the ticket item is not updated and retains its last valid, - // user specified value. - if ((inputString == '' || - (displayedValue !== this.scalingTicketItem_.getValueAsNumber() && - this.scalingTicketItem_.wouldValueBeValid(inputString))) && - !this.isFitToPageSelected()) { - this.inputField_.value = this.scalingTicketItem_.getValue(); - displayedValue = this.getInputAsNumber(); - inputString = this.inputField_.value; - } - - // If fit to page is selected and the value matches the fit to page - // scaling, or if the value is valid, no hint is displayed - if (this.scalingTicketItem_.wouldValueBeValid(inputString) || - (this.isFitToPageSelected() && - (displayedValue == this.fitToPageScaling_ || - (!this.fitToPageScaling_ && inputString == '')))) { + // If fit to page is selected and the display matches, mark valid + // and return. + if (this.isFitToPageSelected() && this.displayMatchesFitToPage()) { this.inputField_.classList.remove('invalid'); fadeOutElement(this.getChildElement('.hint')); - } else { - // Invalid value. Display the hint. - this.inputField_.classList.add('invalid'); - fadeInElement(this.getChildElement('.hint')); + this.updateUiStateInternal(); + return; } + if (!this.inputField_.validity.valid) { + this.inputField_.classList.add('invalid'); + fadeInElement(this.getChildElement('.hint')); + this.updateUiStateInternal(); + return; + } + + this.inputField_.value = this.scalingTicketItem_.getValue(); + this.inputField_.classList.remove('invalid'); + fadeOutElement(this.getChildElement('.hint')); } this.updateUiStateInternal(); }, @@ -198,8 +196,7 @@ // Fit to page was checked. Set scaling to the fit to page scaling. this.displayFitToPageScaling(); } else if (this.fitToPageTicketItem_.isCapabilityAvailable() && - (this.getInputAsNumber() == this.fitToPageScaling_ || - this.inputField_.value == '')) { + this.displayMatchesFitToPage()) { // Fit to page unchecked. Return to last scaling. this.inputField_.value = this.scalingTicketItem_.getValue(); } @@ -211,25 +208,25 @@ */ onTextfieldTimeout_: function() { this.textfieldTimeout_ = null; - var scalingValue = this.inputField_.value; - if (scalingValue == '') - return; - if (this.scalingTicketItem_.wouldValueBeValid(scalingValue)) { - if (scalingValue === this.scalingTicketItem_.getValue()) { - // Make sure this still gets called in case returning to a valid - // value. - this.updateState_(); - } - if (this.isFitToPageSelected() && - parseInt(scalingValue, 10) != this.fitToPageScaling_) { - // User modified value away from fit to page. - this.fitToPageTicketItem_.updateValue(false); - } - this.scalingTicketItem_.updateValue(scalingValue); - this.inputField_.value = scalingValue; - } else { + if (!this.inputField_.validity.valid){ this.updateState_(); + return; } + if (this.isFitToPageSelected() && !this.displayMatchesFitToPage()) { + // User modified value away from fit to page. + this.fitToPageTicketItem_.updateValue(false); + } + // Check this after checking for fit to page in case the user went + // back to their old value. + if (this.inputField_.valueAsNumber.toString() === + this.scalingTicketItem_.getValue()) { + this.updateState_(); + return; + } + if (this.inputField_.value == '') + return; + this.scalingTicketItem_.updateValue( + this.inputField_.valueAsNumber.toString()); }, /** @@ -267,7 +264,7 @@ * @private */ onTextfieldBlur_: function() { - if (this.inputField_.value == '') { + if (this.inputField_.value == '' && this.inputField_.validity.valid) { if (this.isFitToPageSelected()) { this.displayFitToPageScaling(); } else {
diff --git a/chrome/browser/resources/settings/downloads_page/compiled_resources2.gyp b/chrome/browser/resources/settings/downloads_page/compiled_resources2.gyp index 575d24af..679ee9e 100644 --- a/chrome/browser/resources/settings/downloads_page/compiled_resources2.gyp +++ b/chrome/browser/resources/settings/downloads_page/compiled_resources2.gyp
@@ -4,11 +4,21 @@ { 'targets': [ { + 'target_name': 'downloads_browser_proxy', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { 'target_name': 'downloads_page', 'dependencies': [ '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior', '../settings_ui/compiled_resources2.gyp:settings_ui_types', + 'downloads_browser_proxy', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], },
diff --git a/chrome/browser/resources/settings/downloads_page/downloads_browser_proxy.html b/chrome/browser/resources/settings/downloads_page/downloads_browser_proxy.html new file mode 100644 index 0000000..0a32fb4 --- /dev/null +++ b/chrome/browser/resources/settings/downloads_page/downloads_browser_proxy.html
@@ -0,0 +1,3 @@ +<link rel="import" href="chrome://resources/html/assert.html"> +<link rel="import" href="chrome://resources/html/cr.html"> +<script src="downloads_browser_proxy.js"></script>
diff --git a/chrome/browser/resources/settings/downloads_page/downloads_browser_proxy.js b/chrome/browser/resources/settings/downloads_page/downloads_browser_proxy.js new file mode 100644 index 0000000..2f510a1 --- /dev/null +++ b/chrome/browser/resources/settings/downloads_page/downloads_browser_proxy.js
@@ -0,0 +1,46 @@ +// Copyright 2017 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. + +cr.define('settings', function() { + /** @interface */ + function DownloadsBrowserProxy() {} + + DownloadsBrowserProxy.prototype = { + initializeDownloads: assertNotReached, + + selectDownloadLocation: assertNotReached, + + resetAutoOpenFileTypes: assertNotReached, + }; + + /** + * @implements {settings.DownloadsBrowserProxy} + * @constructor + */ + function DownloadsBrowserProxyImpl() {} + + cr.addSingletonGetter(DownloadsBrowserProxyImpl); + + DownloadsBrowserProxyImpl.prototype = { + /** @override */ + initializeDownloads: function() { + chrome.send('initializeDownloads'); + }, + + /** @override */ + selectDownloadLocation: function() { + chrome.send('selectDownloadLocation'); + }, + + /** @override */ + resetAutoOpenFileTypes: function() { + chrome.send('resetAutoOpenFileTypes'); + }, + }; + + return { + DownloadsBrowserProxy: DownloadsBrowserProxy, + DownloadsBrowserProxyImpl: DownloadsBrowserProxyImpl, + }; +});
diff --git a/chrome/browser/resources/settings/downloads_page/downloads_page.html b/chrome/browser/resources/settings/downloads_page/downloads_page.html index 64af1e7a..a648126 100644 --- a/chrome/browser/resources/settings/downloads_page/downloads_page.html +++ b/chrome/browser/resources/settings/downloads_page/downloads_page.html
@@ -1,8 +1,10 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/classes/iron-flex-layout.html"> <link rel="import" href="../controls/controlled_button.html"> <link rel="import" href="../controls/settings_toggle_button.html"> <link rel="import" href="../settings_shared_css.html"> +<link rel="import" href="downloads_browser_proxy.html"> <dom-module id="settings-downloads-page"> <template> @@ -41,6 +43,19 @@ </settings-toggle-button> </if> </div> + <template is="dom-if" if="[[autoOpenDownloads_]]" restamp> + <div class="settings-box"> + <div class="start"> + $i18n{openFileTypesAutomatically} + </div> + <div class="secondary-action"> + <paper-button id="resetAutoOpenFileTypes" class="secondary-button" + on-tap="onClearAutoOpenFileTypesTap_"> + $i18n{clear} + </paper-button> + </div> + </div> + </template> </template> <script src="downloads_page.js"></script> </dom-module>
diff --git a/chrome/browser/resources/settings/downloads_page/downloads_page.js b/chrome/browser/resources/settings/downloads_page/downloads_page.js index f1d1fc6..143180c 100644 --- a/chrome/browser/resources/settings/downloads_page/downloads_page.js +++ b/chrome/browser/resources/settings/downloads_page/downloads_page.js
@@ -18,6 +18,8 @@ Polymer({ is: 'settings-downloads-page', + behaviors: [WebUIListenerBehavior], + properties: { /** * Preferences state. @@ -32,14 +34,34 @@ * @type {!DownloadsPageVisibility} */ pageVisibility: Object, + + /** @private */ + autoOpenDownloads_: { + type: Boolean, + value: false, + }, + }, + + /** @private {?settings.DownloadsBrowserProxy} */ + browserProxy_: null, + + /** @override */ + attached: function() { + this.browserProxy_ = settings.DownloadsBrowserProxyImpl.getInstance(); + + this.addWebUIListener('auto-open-downloads-changed', function(autoOpen) { + this.autoOpenDownloads_ = autoOpen; + }.bind(this)); + + this.browserProxy_.initializeDownloads(); }, /** @private */ selectDownloadLocation_: function() { - chrome.send('selectDownloadLocation'); + this.browserProxy_.selectDownloadLocation(); }, -// <if expr="chromeos"> + // <if expr="chromeos"> /** * @param {string} path * @return {string} The download location string that is suitable to display @@ -56,5 +78,10 @@ path = path.replace(/\//g, ' \u203a '); return path; }, -// </if> + // </if> + + /** @private */ + onClearAutoOpenFileTypesTap_: function() { + this.browserProxy_.resetAutoOpenFileTypes(); + }, });
diff --git a/chrome/browser/resources/settings/people_page/sync_page.html b/chrome/browser/resources/settings/people_page/sync_page.html index 828ac37..b7bd67fb 100644 --- a/chrome/browser/resources/settings/people_page/sync_page.html +++ b/chrome/browser/resources/settings/people_page/sync_page.html
@@ -34,7 +34,7 @@ #existingPassphraseContainer, #passphraseRecoverHint { - align-items: flex-end; + align-items: center; } #existingPassphraseInput { @@ -43,6 +43,8 @@ display: inline-block; --paper-input-container: { padding: 0; + /* TODO(scottchen): remove margin once large font properly padded. */ + margin-bottom: 1rem; }; }
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd index 3f1799e1..37f9871 100644 --- a/chrome/browser/resources/settings/settings_resources.grd +++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -533,6 +533,12 @@ <structure name="IDR_SETTINGS_DIRECTION_DELEGATE_JS" file="direction_delegate.js" type="chrome_html" /> + <structure name="IDR_SETTINGS_DOWNLOADS_BROWSER_PROXY_HTML" + file="downloads_page/downloads_browser_proxy.html" + type="chrome_html" /> + <structure name="IDR_SETTINGS_DOWNLOADS_BROWSER_PROXY_JS" + file="downloads_page/downloads_browser_proxy.js" + type="chrome_html" /> <structure name="IDR_SETTINGS_DOWNLOADS_PAGE_HTML" file="downloads_page/downloads_page.html" type="chrome_html"
diff --git a/chrome/browser/resources/settings/settings_vars_css.html b/chrome/browser/resources/settings/settings_vars_css.html index c4d1409..1fa88f76 100644 --- a/chrome/browser/resources/settings/settings_vars_css.html +++ b/chrome/browser/resources/settings/settings_vars_css.html
@@ -103,8 +103,18 @@ color: inherit; font-size: inherit; font-weight: inherit; + line-height: inherit; vertical-align: baseline; }; + --paper-input-container-label: { + font-size: inherit; + line-height: inherit; + } + --paper-input-error: { + font-size: 92.31%; /* Should be 12px when 100% is 13px. */ + line-height: inherit; + overflow: visible; /* Half-visible error message is not useful at all. */ + } --paper-input-max-width: 264px; --paper-radio-button-ink-size: 40px; --paper-radio-button-label-color: inherit;
diff --git a/chrome/browser/ssl/security_state_tab_helper.cc b/chrome/browser/ssl/security_state_tab_helper.cc index 7c3c996..bbaf1405 100644 --- a/chrome/browser/ssl/security_state_tab_helper.cc +++ b/chrome/browser/ssl/security_state_tab_helper.cc
@@ -61,30 +61,19 @@ DCHECK(time_of_http_warning_on_current_navigation_.is_null()); time_of_http_warning_on_current_navigation_ = base::Time::Now(); - std::string warning; - bool warning_is_user_visible = false; - switch (security_info.security_level) { - case security_state::HTTP_SHOW_WARNING: - warning = - "This page includes a password or credit card input in a non-secure " - "context. A warning has been added to the URL bar. For more " - "information, see https://goo.gl/zmWq3m."; - warning_is_user_visible = true; - break; - case security_state::NONE: - case security_state::DANGEROUS: - warning = - "This page includes a password or credit card input in a non-secure " - "context. A warning will be added to the URL bar in Chrome 56 (Jan " - "2017). For more information, see https://goo.gl/zmWq3m."; - break; - default: - return; - } - logged_http_warning_on_current_navigation_ = true; web_contents()->GetMainFrame()->AddMessageToConsole( - content::CONSOLE_MESSAGE_LEVEL_WARNING, warning); + content::CONSOLE_MESSAGE_LEVEL_WARNING, + "This page includes a password or credit card input in a non-secure " + "context. A warning has been added to the URL bar. For more " + "information, see https://goo.gl/zmWq3m."); + + // |warning_is_user_visible| will only be false if the user has set the flag + // for marking HTTP pages as Dangerous. In that case, the page will be + // flagged as Dangerous, but it isn't distinguished from other HTTP pages, + // which is why this code records it as not-user-visible. + bool warning_is_user_visible = + (security_info.security_level == security_state::HTTP_SHOW_WARNING); if (security_info.displayed_credit_card_field_on_http) { UMA_HISTOGRAM_BOOLEAN(
diff --git a/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc b/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc index e0a50dd..c66dfdc 100644 --- a/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc +++ b/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc
@@ -287,6 +287,13 @@ original_file_path, replacement_text, replacement_path); } +GURL GetURLWithNonLocalHostname(net::EmbeddedTestServer* server, + const std::string& path) { + GURL::Replacements replace_host; + replace_host.SetHostStr("example.test"); + return server->GetURL(path).ReplaceComponents(replace_host); +} + class SecurityStateTabHelperTest : public CertVerifierBrowserTest { public: SecurityStateTabHelperTest() @@ -294,12 +301,15 @@ https_server_.ServeFilesFromSourceDirectory(base::FilePath(kDocRoot)); } + void SetUpOnMainThread() override { + ASSERT_TRUE(embedded_test_server()->Start()); + ASSERT_TRUE(https_server_.Start()); + host_resolver()->AddRule("*", embedded_test_server()->GetURL("/").host()); + } + void SetUpCommandLine(base::CommandLine* command_line) override { // Browser will both run and display insecure content. command_line->AppendSwitch(switches::kAllowRunningInsecureContent); - command_line->AppendSwitchASCII( - security_state::switches::kMarkHttpAs, - security_state::switches::kMarkHttpAsNeutral); } protected: @@ -314,40 +324,6 @@ mock_cert_verifier()->AddResultForCert(cert, verify_result, net_result); } - net::EmbeddedTestServer https_server_; - - private: - DISALLOW_COPY_AND_ASSIGN(SecurityStateTabHelperTest); -}; - -GURL GetURLWithNonLocalHostname(net::EmbeddedTestServer* server, - const std::string& path) { - GURL::Replacements replace_host; - replace_host.SetHostStr("example.test"); - return server->GetURL(path).ReplaceComponents(replace_host); -} - -class SecurityStateTabHelperTestWithPasswordCcSwitch - : public SecurityStateTabHelperTest, - public testing::WithParamInterface<bool> { - public: - SecurityStateTabHelperTestWithPasswordCcSwitch() - : SecurityStateTabHelperTest() {} - - void SetUpOnMainThread() override { - ASSERT_TRUE(embedded_test_server()->Start()); - ASSERT_TRUE(https_server_.Start()); - host_resolver()->AddRule("*", embedded_test_server()->GetURL("/").host()); - SetUpMockCertVerifierForHttpsServer(0, net::OK); - } - - void SetUpCommandLine(base::CommandLine* command_line) override { - SecurityStateTabHelperTest::SetUpCommandLine(command_line); - command_line->AppendSwitchASCII( - security_state::switches::kMarkHttpAs, - security_state::switches::kMarkHttpWithPasswordsOrCcWithChip); - } - // Navigates to an empty page and runs |javascript| to create a URL with with // a scheme of |scheme|. If |expect_warning| is true, expects a password // warning. @@ -393,8 +369,10 @@ } } + net::EmbeddedTestServer https_server_; + private: - DISALLOW_COPY_AND_ASSIGN(SecurityStateTabHelperTestWithPasswordCcSwitch); + DISALLOW_COPY_AND_ASSIGN(SecurityStateTabHelperTest); }; class DidChangeVisibleSecurityStateTest : public InProcessBrowserTest { @@ -417,7 +395,6 @@ }; IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, HttpPage) { - ASSERT_TRUE(embedded_test_server()->Start()); ui_test_utils::NavigateToURL( browser(), embedded_test_server()->GetURL("/ssl/google.html")); content::WebContents* contents = @@ -442,7 +419,6 @@ } IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, HttpsPage) { - ASSERT_TRUE(https_server_.Start()); SetUpMockCertVerifierForHttpsServer(0, net::OK); ui_test_utils::NavigateToURL(browser(), @@ -456,7 +432,6 @@ // Test security state after clickthrough for a SHA-1 certificate that is // blocked by default. IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, SHA1CertificateBlocked) { - ASSERT_TRUE(https_server_.Start()); SetUpMockCertVerifierForHttpsServer( net::CERT_STATUS_SHA1_SIGNATURE_PRESENT | net::CERT_STATUS_WEAK_SIGNATURE_ALGORITHM, @@ -497,7 +472,6 @@ // Test security state for a SHA-1 certificate that is allowed by policy. IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, SHA1CertificateWarning) { - ASSERT_TRUE(https_server_.Start()); SetUpMockCertVerifierForHttpsServer(net::CERT_STATUS_SHA1_SIGNATURE_PRESENT, net::OK); @@ -521,8 +495,6 @@ } IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, MixedContent) { - ASSERT_TRUE(embedded_test_server()->Start()); - ASSERT_TRUE(https_server_.Start()); SetUpMockCertVerifierForHttpsServer(0, net::OK); host_resolver()->AddRule("example.test", https_server_.GetURL("/title1.html").host()); @@ -607,7 +579,6 @@ IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, ActiveContentWithCertErrors) { - ASSERT_TRUE(https_server_.Start()); SetUpMockCertVerifierForHttpsServer(0, net::OK); // Navigate to an HTTPS page and simulate active content with @@ -636,7 +607,6 @@ IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, PassiveContentWithCertErrors) { - ASSERT_TRUE(https_server_.Start()); SetUpMockCertVerifierForHttpsServer(0, net::OK); // Navigate to an HTTPS page and simulate passive content with @@ -665,7 +635,6 @@ IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, ActiveAndPassiveContentWithCertErrors) { - ASSERT_TRUE(https_server_.Start()); SetUpMockCertVerifierForHttpsServer(0, net::OK); // Navigate to an HTTPS page and simulate active and passive content @@ -697,8 +666,6 @@ // Same as SecurityStateTabHelperTest.ActiveAndPassiveContentWithCertErrors but // with a SHA1 cert. IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, MixedContentWithSHA1Cert) { - ASSERT_TRUE(embedded_test_server()->Start()); - ASSERT_TRUE(https_server_.Start()); SetUpMockCertVerifierForHttpsServer(net::CERT_STATUS_SHA1_SIGNATURE_PRESENT, net::OK); @@ -767,7 +734,6 @@ // Tests that the Content Security Policy block-all-mixed-content // directive stops mixed content from running. IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, MixedContentStrictBlocking) { - ASSERT_TRUE(https_server_.Start()); SetUpMockCertVerifierForHttpsServer(0, net::OK); // Navigate to an HTTPS page that tries to run mixed content in an @@ -790,8 +756,6 @@ } IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, BrokenHTTPS) { - ASSERT_TRUE(embedded_test_server()->Start()); - ASSERT_TRUE(https_server_.Start()); SetUpMockCertVerifierForHttpsServer(net::CERT_STATUS_DATE_INVALID, net::ERR_CERT_DATE_INVALID); @@ -1001,9 +965,8 @@ } // Tests that when a visible password field is detected on an HTTP page -// load, and when the command-line flag is set, the security level is -// downgraded to HTTP_SHOW_WARNING. -IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTestWithPasswordCcSwitch, +// load, the security level is downgraded to HTTP_SHOW_WARNING. +IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, PasswordSecurityLevelDowngraded) { content::WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents(); @@ -1027,10 +990,9 @@ content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP); } -// Tests that when a visible password field is detected on a blob URL, and when -// the command-line flag is set, the security level is downgraded to -// HTTP_SHOW_WARNING. -IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTestWithPasswordCcSwitch, +// Tests that when a visible password field is detected on a blob URL, the +// security level is downgraded to HTTP_SHOW_WARNING. +IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, PasswordSecurityLevelDowngradedOnBlobUrl) { TestPasswordFieldOnBlobOrFilesystemURL( "blob", @@ -1040,10 +1002,9 @@ true /* expect_warning */); } -// Tests that when no password field is detected on a blob URL, and when the -// command-line flag is set the security level is not downgraded to -// HTTP_SHOW_WARNING. -IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTestWithPasswordCcSwitch, +// Tests that when no password field is detected on a blob URL, the security +// level is not downgraded to HTTP_SHOW_WARNING. +IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, DefaultSecurityLevelOnBlobUrl) { TestPasswordFieldOnBlobOrFilesystemURL( "blob", @@ -1055,7 +1016,7 @@ // Same as PasswordSecurityLevelDowngradedOnBlobUrl, but instead of a blob URL, // this creates a filesystem URL. -IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTestWithPasswordCcSwitch, +IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, PasswordSecurityLevelDowngradedOnFilesystemUrl) { TestPasswordFieldOnBlobOrFilesystemURL( "filesystem", @@ -1077,7 +1038,7 @@ // Same as DefaultSecurityLevelOnBlobUrl, but instead of a blob URL, // this creates a filesystem URL. -IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTestWithPasswordCcSwitch, +IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, DefaultSecurityLevelOnFilesystemUrl) { TestPasswordFieldOnBlobOrFilesystemURL( "filesystem", @@ -1097,10 +1058,9 @@ false /* expect_warning */); } -// Tests that when an invisible password field is present on an HTTP page -// load, and when the command-line flag is set, the security level is -// *not* downgraded to HTTP_SHOW_WARNING. -IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTestWithPasswordCcSwitch, +// Tests that when an invisible password field is present on an HTTP page load, +// the security level is *not* downgraded to HTTP_SHOW_WARNING. +IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, PasswordSecurityLevelNotDowngradedForInvisibleInput) { content::WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents(); @@ -1125,10 +1085,9 @@ content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP); } -// Tests that when a visible password field is detected inside an iframe -// on an HTTP page load, and when the command-line flag is set, the -// security level is downgraded to HTTP_SHOW_WARNING. -IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTestWithPasswordCcSwitch, +// Tests that when a visible password field is detected inside an iframe on an +// HTTP page load, the security level is downgraded to HTTP_SHOW_WARNING. +IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, PasswordSecurityLevelDowngradedFromIframe) { content::WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents(); @@ -1153,12 +1112,12 @@ content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP); } -// Tests that when a visible password field is detected inside an iframe -// on an HTTP page load, and when the command-line flag is set, the -// security level is downgraded to HTTP_SHOW_WARNING, even if the iframe -// itself was loaded over HTTPS. -IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTestWithPasswordCcSwitch, +// Tests that when a visible password field is detected inside an iframe on an +// HTTP page load, the security level is downgraded to HTTP_SHOW_WARNING, even +// if the iframe itself was loaded over HTTPS. +IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, PasswordSecurityLevelDowngradedFromHttpsIframe) { + SetUpMockCertVerifierForHttpsServer(0, net::OK); content::WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents(); ASSERT_TRUE(contents); @@ -1187,11 +1146,11 @@ content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP); } -// Tests that when a visible password field is detected on an HTTPS page -// load, and when the command-line flag is set, the security level is -// *not* downgraded to HTTP_SHOW_WARNING. -IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTestWithPasswordCcSwitch, +// Tests that when a visible password field is detected on an HTTPS page load, +// the security level is *not* downgraded to HTTP_SHOW_WARNING. +IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, PasswordSecurityLevelNotDowngradedOnHttps) { + SetUpMockCertVerifierForHttpsServer(0, net::OK); content::WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents(); ASSERT_TRUE(contents); @@ -1271,23 +1230,10 @@ messages[0].find(base::ASCIIToUTF16("warning has been added"))); } -// Checks that |delegate| has observed exactly one console message for -// NONE that will be HTTP_SHOW_WARNING in future. To avoid brittleness, -// this just looks for keywords in the string rather than the exact -// text. -void CheckForOneFutureHttpWarningConsoleMessage( - ConsoleWebContentsDelegate* delegate) { - const std::vector<base::string16>& messages = delegate->console_messages(); - ASSERT_EQ(1u, messages.size()); - EXPECT_NE(base::string16::npos, - messages[0].find(base::ASCIIToUTF16("warning will be added"))); -} - // Tests that console messages are printed upon a call to // GetSecurityInfo() on an HTTP_SHOW_WARNING page, exactly once per // main-frame navigation. -IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTestWithPasswordCcSwitch, - ConsoleMessage) { +IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, ConsoleMessage) { ConsoleWebContentsDelegate* delegate = new ConsoleWebContentsDelegate( Browser::CreateParams(browser()->profile(), true)); content::WebContents* original_contents = @@ -1352,100 +1298,9 @@ ASSERT_NO_FATAL_FAILURE(CheckForOneHttpWarningConsoleMessage(delegate)); } -// Tests that console messages are printed upon a call to -// GetSecurityInfo() on a NONE page that will be marked -// HTTP_SHOW_WARNING in future, exactly once per main-frame navigation. -IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, ConsoleMessage) { - ASSERT_TRUE(embedded_test_server()->Start()); - host_resolver()->AddRule("*", embedded_test_server()->GetURL("/").host()); - ConsoleWebContentsDelegate* delegate = new ConsoleWebContentsDelegate( - Browser::CreateParams(browser()->profile(), true)); - content::WebContents* original_contents = - browser()->tab_strip_model()->GetActiveWebContents(); - content::WebContents* contents = - content::WebContents::Create(content::WebContents::CreateParams( - original_contents->GetBrowserContext())); - ASSERT_TRUE(contents); - contents->SetDelegate(delegate); - delegate->tab_strip_model()->AppendWebContents(contents, true); - int index = delegate->tab_strip_model()->GetIndexOfWebContents(contents); - delegate->tab_strip_model()->ActivateTabAt(index, true); - ASSERT_EQ(contents, delegate->tab_strip_model()->GetActiveWebContents()); - - // Navigate to an HTTP page. Use a non-local hostname so that is it - // not considered secure. - GURL http_url = - GetURLWithNonLocalHostname(embedded_test_server(), "/title1.html"); - ui_test_utils::NavigateToURL(delegate, http_url); - content::NavigationEntry* entry = contents->GetController().GetVisibleEntry(); - ASSERT_TRUE(entry); - EXPECT_EQ(http_url, entry->GetURL()); - EXPECT_TRUE(delegate->console_messages().empty()); - - // Trigger the a state that will be marked as HTTP_SHOW_WARNING in future. - base::RunLoop first_message; - delegate->set_console_message_callback(first_message.QuitClosure()); - contents->OnPasswordInputShownOnHttp(); - first_message.Run(); - - // Check that the correct state was actually triggered. - SecurityStateTabHelper* helper = - SecurityStateTabHelper::FromWebContents(contents); - ASSERT_TRUE(helper); - security_state::SecurityInfo security_info; - helper->GetSecurityInfo(&security_info); - EXPECT_EQ(security_state::NONE, security_info.security_level); - EXPECT_TRUE(security_info.displayed_password_field_on_http); - - // Check that the expected console message is present. - ASSERT_NO_FATAL_FAILURE(CheckForOneFutureHttpWarningConsoleMessage(delegate)); - delegate->ClearConsoleMessages(); - - // Two subsequent triggers of VisibleSecurityStateChanged -- one on the - // same navigation and one on another navigation -- should only result - // in one additional console message. - contents->OnCreditCardInputShownOnHttp(); - GURL second_http_url = - GetURLWithNonLocalHostname(embedded_test_server(), "/title2.html"); - ui_test_utils::NavigateToURL(delegate, second_http_url); - entry = contents->GetController().GetVisibleEntry(); - ASSERT_TRUE(entry); - EXPECT_EQ(second_http_url, entry->GetURL()); - - base::RunLoop second_message; - delegate->set_console_message_callback(second_message.QuitClosure()); - contents->OnPasswordInputShownOnHttp(); - second_message.Run(); - - helper->GetSecurityInfo(&security_info); - EXPECT_EQ(security_state::NONE, security_info.security_level); - EXPECT_TRUE(security_info.displayed_password_field_on_http); - EXPECT_FALSE(security_info.displayed_credit_card_field_on_http); - - ASSERT_NO_FATAL_FAILURE(CheckForOneFutureHttpWarningConsoleMessage(delegate)); - delegate->ClearConsoleMessages(); - - // Check that a console message is printed for credit card field shown. - ui_test_utils::NavigateToURL(delegate, http_url); - entry = contents->GetController().GetVisibleEntry(); - ASSERT_TRUE(entry); - EXPECT_EQ(http_url, entry->GetURL()); - - base::RunLoop third_message; - delegate->set_console_message_callback(third_message.QuitClosure()); - contents->OnCreditCardInputShownOnHttp(); - third_message.Run(); - - helper->GetSecurityInfo(&security_info); - EXPECT_EQ(security_state::NONE, security_info.security_level); - EXPECT_TRUE(security_info.displayed_credit_card_field_on_http); - - ASSERT_NO_FATAL_FAILURE(CheckForOneFutureHttpWarningConsoleMessage(delegate)); -} - // Tests that additional HTTP_SHOW_WARNING console messages are not // printed after subframe navigations. -IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTestWithPasswordCcSwitch, +IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, ConsoleMessageNotPrintedForFrameNavigation) { ConsoleWebContentsDelegate* delegate = new ConsoleWebContentsDelegate( Browser::CreateParams(browser()->profile(), true)); @@ -1526,7 +1381,7 @@ // Tests that additional HTTP_SHOW_WARNING console messages are not // printed after pushState navigations. -IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTestWithPasswordCcSwitch, +IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, ConsoleMessageNotPrintedForPushStateNavigation) { ConsoleWebContentsDelegate* delegate = new ConsoleWebContentsDelegate( Browser::CreateParams(browser()->profile(), true)); @@ -1603,7 +1458,6 @@ // Tests that the security state for a WebContents is up to date when the // WebContents is inserted into a Browser's TabStripModel. IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest, AddedTab) { - ASSERT_TRUE(https_server_.Start()); SetUpMockCertVerifierForHttpsServer(0, net::OK); content::WebContents* tab = @@ -1633,8 +1487,8 @@ // with the current style on HTTP, broken HTTPS, and valid HTTPS pages. IN_PROC_BROWSER_TEST_F(DidChangeVisibleSecurityStateTest, DidChangeVisibleSecurityStateObserver) { - ASSERT_TRUE(https_server_.Start()); ASSERT_TRUE(embedded_test_server()->Start()); + ASSERT_TRUE(https_server_.Start()); net::EmbeddedTestServer https_test_server_expired( net::EmbeddedTestServer::TYPE_HTTPS);
diff --git a/chrome/browser/ssl/security_state_tab_helper_unittest.cc b/chrome/browser/ssl/security_state_tab_helper_unittest.cc index 8d0b4d0..610bc9d 100644 --- a/chrome/browser/ssl/security_state_tab_helper_unittest.cc +++ b/chrome/browser/ssl/security_state_tab_helper_unittest.cc
@@ -63,10 +63,6 @@ // level to HTTP_SHOW_WARNING and navigating away. TEST_P(SecurityStateTabHelperHistogramTest, HTTPOmniboxWarningNavigationHistogram) { - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - security_state::switches::kMarkHttpAs, - security_state::switches::kMarkHttpWithPasswordsOrCcWithChip); - base::HistogramTester histograms; SignalSensitiveInput(); // Make sure that if the omnibox warning gets dynamically hidden, the @@ -81,54 +77,10 @@ histograms.ExpectTotalCount(kHTTPBadWebContentsDestroyedHistogram, 0); } -// Tests that an UMA histogram is recorded after showing a console -// warning for a sensitive input on HTTP and navigating away. -TEST_P(SecurityStateTabHelperHistogramTest, - HTTPConsoleWarningNavigationHistogram) { - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - security_state::switches::kMarkHttpAs, - security_state::switches::kMarkHttpAsNeutral); - - // Same as HTTPOmniboxWarningNavigationHistogram, but ensuring that - // the histogram gets recorded even if the command-line switch to show - // the omnibox warning is not set. - base::HistogramTester histograms; - SignalSensitiveInput(); - NavigateToDifferentHTTPPage(); - // Destroy the WebContents to simulate the tab being closed after a - // navigation. - SetContents(nullptr); - histograms.ExpectTotalCount(kHTTPBadNavigationHistogram, 1); - histograms.ExpectTotalCount(kHTTPBadWebContentsDestroyedHistogram, 0); -} - // Tests that an UMA histogram is recorded after setting the security // level to HTTP_SHOW_WARNING and closing the tab. TEST_P(SecurityStateTabHelperHistogramTest, HTTPOmniboxWarningTabClosedHistogram) { - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - security_state::switches::kMarkHttpAs, - security_state::switches::kMarkHttpWithPasswordsOrCcWithChip); - - base::HistogramTester histograms; - SignalSensitiveInput(); - // Destroy the WebContents to simulate the tab being closed. - SetContents(nullptr); - histograms.ExpectTotalCount(kHTTPBadNavigationHistogram, 0); - histograms.ExpectTotalCount(kHTTPBadWebContentsDestroyedHistogram, 1); -} - -// Tests that an UMA histogram is recorded after showing a console -// warning for a sensitive input on HTTP and closing the tab. -TEST_P(SecurityStateTabHelperHistogramTest, - HTTPConsoleWarningTabClosedHistogram) { - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - security_state::switches::kMarkHttpAs, - security_state::switches::kMarkHttpAsNeutral); - - // Same as HTTPOmniboxWarningTabClosedHistogram, but ensuring that the - // histogram gets recorded even if the command-line switch to show the - // omnibox warning is not set. base::HistogramTester histograms; SignalSensitiveInput(); // Destroy the WebContents to simulate the tab being closed. @@ -140,11 +92,6 @@ // Tests that UMA logs the omnibox warning when security level is // HTTP_SHOW_WARNING. TEST_P(SecurityStateTabHelperHistogramTest, HTTPOmniboxWarningHistogram) { - // Show Warning Chip. - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - security_state::switches::kMarkHttpAs, - security_state::switches::kMarkHttpWithPasswordsOrCcWithChip); - base::HistogramTester histograms; SignalSensitiveInput(); histograms.ExpectUniqueSample(HistogramName(), true, 1); @@ -160,28 +107,6 @@ histograms.ExpectUniqueSample(HistogramName(), true, 2); } -// Tests that UMA logs the console warning when security level is NONE. -TEST_P(SecurityStateTabHelperHistogramTest, HTTPConsoleWarningHistogram) { - // Show Neutral for HTTP - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - security_state::switches::kMarkHttpAs, - security_state::switches::kMarkHttpAsNeutral); - - base::HistogramTester histograms; - SignalSensitiveInput(); - histograms.ExpectUniqueSample(HistogramName(), false, 1); - - // Fire again and ensure no sample is recorded. - SignalSensitiveInput(); - histograms.ExpectUniqueSample(HistogramName(), false, 1); - - // Navigate to a new page and ensure a sample is recorded. - NavigateToDifferentHTTPPage(); - histograms.ExpectUniqueSample(HistogramName(), false, 1); - SignalSensitiveInput(); - histograms.ExpectUniqueSample(HistogramName(), false, 2); -} - INSTANTIATE_TEST_CASE_P(SecurityStateTabHelperHistogramTest, SecurityStateTabHelperHistogramTest, // Here 'true' to test password field triggered
diff --git a/chrome/browser/supervised_user/supervised_user_content_provider_android.cc b/chrome/browser/supervised_user/supervised_user_content_provider_android.cc index f98fea4..4c22915 100644 --- a/chrome/browser/supervised_user/supervised_user_content_provider_android.cc +++ b/chrome/browser/supervised_user/supervised_user_content_provider_android.cc
@@ -20,25 +20,17 @@ using base::android::ScopedJavaGlobalRef; using base::android::AttachCurrentThread; -namespace { +SupervisedUserContentProvider::UrlFilterObserver::UrlFilterObserver( + JNIEnv* env, + const ScopedJavaGlobalRef<jobject>& java_content_provider) + : java_content_provider_(java_content_provider) {} -class UrlFilterObserver : public SupervisedUserURLFilter::Observer { - public: - UrlFilterObserver(JNIEnv* env, - const ScopedJavaGlobalRef<jobject>& java_content_provider) - : java_content_provider_(java_content_provider) {} +SupervisedUserContentProvider::UrlFilterObserver::~UrlFilterObserver() {} - virtual ~UrlFilterObserver() {} - - private: - void OnSiteListUpdated() override { - Java_SupervisedUserContentProvider_onSupervisedUserFilterUpdated( - AttachCurrentThread(), java_content_provider_); - } - ScopedJavaGlobalRef<jobject> java_content_provider_; -}; - -} // namespace +void SupervisedUserContentProvider::UrlFilterObserver::OnURLFilterChanged() { + Java_SupervisedUserContentProvider_onSupervisedUserFilterUpdated( + AttachCurrentThread(), java_content_provider_); +} static jlong CreateSupervisedUserContentProvider( JNIEnv* env, @@ -52,17 +44,22 @@ const JavaParamRef<jobject>& caller) : profile_(ProfileManager::GetLastUsedProfile()), java_content_provider_(env, caller), + url_filter_observer_(new UrlFilterObserver(env, java_content_provider_)), weak_factory_(this) { if (profile_->IsSupervised()) { SupervisedUserService* supervised_user_service = SupervisedUserServiceFactory::GetForProfile(profile_); - SupervisedUserURLFilter* url_filter = - supervised_user_service->GetURLFilterForUIThread(); - url_filter->AddObserver(new UrlFilterObserver(env, java_content_provider_)); + supervised_user_service->AddObserver(url_filter_observer_.get()); } } -SupervisedUserContentProvider::~SupervisedUserContentProvider() {} +SupervisedUserContentProvider::~SupervisedUserContentProvider() { + if (profile_->IsSupervised()) { + SupervisedUserService* supervised_user_service = + SupervisedUserServiceFactory::GetForProfile(profile_); + supervised_user_service->RemoveObserver(url_filter_observer_.get()); + } +} void SupervisedUserContentProvider::ShouldProceed( JNIEnv* env,
diff --git a/chrome/browser/supervised_user/supervised_user_content_provider_android.h b/chrome/browser/supervised_user/supervised_user_content_provider_android.h index 1743b7b..bcdbf4c 100644 --- a/chrome/browser/supervised_user/supervised_user_content_provider_android.h +++ b/chrome/browser/supervised_user/supervised_user_content_provider_android.h
@@ -6,9 +6,11 @@ #define CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_CONTENT_PROVIDER_ANDROID_H_ #include <jni.h> +#include <memory> #include "base/android/scoped_java_ref.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" +#include "chrome/browser/supervised_user/supervised_user_service_observer.h" #include "chrome/browser/supervised_user/supervised_user_url_filter.h" #include "components/supervised_user_error_page/supervised_user_error_page.h" @@ -37,6 +39,19 @@ static bool Register(JNIEnv* env); private: + class UrlFilterObserver : public SupervisedUserServiceObserver { + public: + UrlFilterObserver(JNIEnv* env, + const base::android::ScopedJavaGlobalRef<jobject>& + java_content_provider); + + ~UrlFilterObserver() override; + + private: + void OnURLFilterChanged() override; + base::android::ScopedJavaGlobalRef<jobject> java_content_provider_; + }; + void OnQueryComplete( base::android::ScopedJavaGlobalRef<jobject> query_reply_jobj, SupervisedUserURLFilter::FilteringBehavior behavior, @@ -47,6 +62,7 @@ bool sent_ok); Profile* profile_; base::android::ScopedJavaGlobalRef<jobject> java_content_provider_; + std::unique_ptr<UrlFilterObserver> url_filter_observer_; base::WeakPtrFactory<SupervisedUserContentProvider> weak_factory_;
diff --git a/chrome/browser/sync/test/integration/session_hierarchy_match_checker.cc b/chrome/browser/sync/test/integration/session_hierarchy_match_checker.cc new file mode 100644 index 0000000..ee2e9aa --- /dev/null +++ b/chrome/browser/sync/test/integration/session_hierarchy_match_checker.cc
@@ -0,0 +1,22 @@ +// Copyright (c) 2017 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/sync/test/integration/session_hierarchy_match_checker.h" + +SessionHierarchyMatchChecker::SessionHierarchyMatchChecker( + const fake_server::SessionsHierarchy& sessions_hierarchy, + browser_sync::ProfileSyncService* service, + fake_server::FakeServer* fake_server) + : SingleClientStatusChangeChecker(service), + sessions_hierarchy_(sessions_hierarchy), + verifier_(fake_server) {} + +bool SessionHierarchyMatchChecker::IsExitConditionSatisfied() { + return verifier_.VerifySessions(sessions_hierarchy_); +} + +std::string SessionHierarchyMatchChecker::GetDebugMessage() const { + return "Waiting for matching sessions hierarchy to be reflected in fake " + "server"; +}
diff --git a/chrome/browser/sync/test/integration/session_hierarchy_match_checker.h b/chrome/browser/sync/test/integration/session_hierarchy_match_checker.h new file mode 100644 index 0000000..b8243f7 --- /dev/null +++ b/chrome/browser/sync/test/integration/session_hierarchy_match_checker.h
@@ -0,0 +1,30 @@ +// Copyright (c) 2017 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_SYNC_TEST_INTEGRATION_SESSION_HIERARCHY_MATCH_CHECKER_H_ +#define CHROME_BROWSER_SYNC_TEST_INTEGRATION_SESSION_HIERARCHY_MATCH_CHECKER_H_ + +#include "chrome/browser/sync/test/integration/single_client_status_change_checker.h" +#include "components/sync/test/fake_server/fake_server_verifier.h" +#include "components/sync/test/fake_server/sessions_hierarchy.h" + +// Checker to block until the FakeServer records a SessionsHierarchy identical +// to the SessionsHierarchy specified in the constructor. +class SessionHierarchyMatchChecker : public SingleClientStatusChangeChecker { + public: + SessionHierarchyMatchChecker( + const fake_server::SessionsHierarchy& sessions_hierarchy, + browser_sync::ProfileSyncService* service, + fake_server::FakeServer* fake_server); + + // StatusChangeChecker implementation. + bool IsExitConditionSatisfied() override; + std::string GetDebugMessage() const override; + + private: + const fake_server::SessionsHierarchy sessions_hierarchy_; + fake_server::FakeServerVerifier verifier_; +}; + +#endif // CHROME_BROWSER_SYNC_TEST_INTEGRATION_SESSION_HIERARCHY_MATCH_CHECKER_H_
diff --git a/chrome/browser/sync/test/integration/sessions_helper.cc b/chrome/browser/sync/test/integration/sessions_helper.cc index 98e0936e..84a843a 100644 --- a/chrome/browser/sync/test/integration/sessions_helper.cc +++ b/chrome/browser/sync/test/integration/sessions_helper.cc
@@ -23,11 +23,19 @@ #include "chrome/browser/sync/sessions/notification_service_sessions_router.h" #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h" #include "chrome/browser/sync/test/integration/sync_datatype_helper.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/singleton_tabs.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_switches.h" +#include "chrome/test/base/ui_test_utils.h" #include "components/browser_sync/profile_sync_service.h" #include "components/sync/driver/sync_client.h" +#include "components/sync/test/fake_server/fake_server.h" +#include "components/sync/test/fake_server/sessions_hierarchy.h" #include "components/sync_sessions/open_tabs_ui_delegate.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/web_contents.h" #include "content/public/test/test_utils.h" #include "url/gurl.h" @@ -36,9 +44,10 @@ namespace sessions_helper { bool GetLocalSession(int index, const sync_sessions::SyncedSession** session) { - return ProfileSyncServiceFactory::GetInstance()->GetForProfile( - test()->GetProfile(index))->GetOpenTabsUIDelegate()-> - GetLocalSession(session); + return ProfileSyncServiceFactory::GetInstance() + ->GetForProfile(test()->GetProfile(index)) + ->GetOpenTabsUIDelegate() + ->GetLocalSession(session); } bool ModelAssociatorHasTabWithUrl(int index, const GURL& url) { @@ -85,23 +94,66 @@ } bool OpenTab(int index, const GURL& url) { - DVLOG(1) << "Opening tab: " << url.spec() << " using profile " - << index << "."; - chrome::ShowSingletonTab(test()->GetBrowser(index), url); - return WaitForTabsToLoad(index, std::vector<GURL>(1, url)); + DVLOG(1) << "Opening tab: " << url.spec() << " using browser " << index + << "."; + return OpenTabAtIndex(index, 0, url); +} + +bool OpenTabAtIndex(int index, int tab_index, const GURL& url) { + chrome::AddTabAt(test()->GetBrowser(index), url, tab_index, true); + return WaitForTabsToLoad(index, {url}); } bool OpenMultipleTabs(int index, const std::vector<GURL>& urls) { Browser* browser = test()->GetBrowser(index); for (std::vector<GURL>::const_iterator it = urls.begin(); it != urls.end(); ++it) { - DVLOG(1) << "Opening tab: " << it->spec() << " using profile " << index + DVLOG(1) << "Opening tab: " << it->spec() << " using browser " << index << "."; chrome::ShowSingletonTab(browser, *it); } return WaitForTabsToLoad(index, urls); } +void MoveTab(int from_index, int to_index, int tab_index) { + content::WebContents* detached_contents = + test() + ->GetBrowser(from_index) + ->tab_strip_model() + ->DetachWebContentsAt(tab_index); + + TabStripModel* target_strip = test()->GetBrowser(to_index)->tab_strip_model(); + target_strip->InsertWebContentsAt(target_strip->count(), detached_contents, + TabStripModel::ADD_ACTIVE); +} + +bool NavigateTab(int index, const GURL& url) { + chrome::NavigateParams params(test()->GetBrowser(index), url, + ui::PAGE_TRANSITION_LINK); + params.disposition = WindowOpenDisposition::CURRENT_TAB; + + ui_test_utils::NavigateToURL(¶ms); + return WaitForTabsToLoad(index, {url}); +} + +void NavigateTabBack(int index) { + test() + ->GetBrowser(index) + ->tab_strip_model() + ->GetWebContentsAt(0) + ->GetController() + .GoBack(); +} + +void NavigateTabForward(int index) { + test() + ->GetBrowser(index) + ->tab_strip_model() + ->GetWebContentsAt(0) + ->GetController() + .GoForward(); +} + namespace { class TabEventHandler : public sync_sessions::LocalSessionEventHandler { @@ -199,15 +251,6 @@ return true; } -bool OpenTabAndGetLocalWindows(int index, - const GURL& url, - ScopedWindowMap* local_windows) { - if (!OpenTab(index, url)) { - return false; - } - return GetLocalWindows(index, local_windows); -} - bool CheckInitialState(int index) { if (0 != GetNumWindows(index)) return false; @@ -226,20 +269,20 @@ int GetNumForeignSessions(int index) { SyncedSessionVector sessions; - if (!ProfileSyncServiceFactory::GetInstance()->GetForProfile( - test()->GetProfile(index))-> - GetOpenTabsUIDelegate()->GetAllForeignSessions( - &sessions)) { + if (!ProfileSyncServiceFactory::GetInstance() + ->GetForProfile(test()->GetProfile(index)) + ->GetOpenTabsUIDelegate() + ->GetAllForeignSessions(&sessions)) { return 0; } return sessions.size(); } bool GetSessionData(int index, SyncedSessionVector* sessions) { - if (!ProfileSyncServiceFactory::GetInstance()->GetForProfile( - test()->GetProfile(index))-> - GetOpenTabsUIDelegate()->GetAllForeignSessions( - sessions)) { + if (!ProfileSyncServiceFactory::GetInstance() + ->GetForProfile(test()->GetProfile(index)) + ->GetOpenTabsUIDelegate() + ->GetAllForeignSessions(sessions)) { return false; } SortSyncedSessions(sessions); @@ -345,9 +388,8 @@ return WindowsMatchImpl(win1, win2); } -bool CheckForeignSessionsAgainst( - int index, - const std::vector<ScopedWindowMap>& windows) { +bool CheckForeignSessionsAgainst(int index, + const std::vector<ScopedWindowMap>& windows) { SyncedSessionVector sessions; if (!GetSessionData(index, &sessions)) { @@ -357,8 +399,9 @@ for (size_t w_index = 0; w_index < windows.size(); ++w_index) { // Skip the client's local window - if (static_cast<int>(w_index) == index) + if (static_cast<int>(w_index) == index) { continue; + } size_t s_index = 0;
diff --git a/chrome/browser/sync/test/integration/sessions_helper.h b/chrome/browser/sync/test/integration/sessions_helper.h index 365bc091..c7b9f30 100644 --- a/chrome/browser/sync/test/integration/sessions_helper.h +++ b/chrome/browser/sync/test/integration/sessions_helper.h
@@ -27,21 +27,10 @@ using ScopedWindowMap = std::map<SessionID::id_type, std::unique_ptr<sessions::SessionWindow>>; -// Copies the local session windows of profile |index| to |local_windows|. +// Copies the local session windows of profile at |index| to |local_windows|. // Returns true if successful. bool GetLocalWindows(int index, ScopedWindowMap* local_windows); -// Creates and verifies the creation of a new window for profile |index| with -// one tab displaying |url|. Copies the SessionWindow associated with the new -// window to |local_windows|. Returns true if successful. This call results in -// multiple sessions changes, and performs synchronous blocking. It is rare, but -// possible, that multiple sync cycle commits occur as a result of this call. -// Test cases should be written to handle this possibility, otherwise they may -// flake. -bool OpenTabAndGetLocalWindows(int index, - const GURL& url, - ScopedWindowMap* local_windows); - // Checks that window count and foreign session count are 0. bool CheckInitialState(int index); @@ -84,14 +73,36 @@ // with a reference SessionWindow list. // Returns true if the session windows of the foreign session matches the // reference. -bool CheckForeignSessionsAgainst( - int index, - const std::vector<ScopedWindowMap>& windows); +bool CheckForeignSessionsAgainst(int index, + const std::vector<ScopedWindowMap>& windows); -// Open a single tab and block until the session model associator is aware -// of it. Returns true upon success, false otherwise. +// Open a single tab in the browser at |index| and block until the +// session model associator is aware of it. Returns true upon success, false +// otherwise. bool OpenTab(int index, const GURL& url); +// See OpenTab, except that the tab is opened in position |tab_index|. +// If |tab_index| is -1 or greater than the number of tabs, the tab will be +// appended to the end of the strip. i.e. if tab_index is 3 for a tab strip of +// size 1, the new tab will be in position 1. +bool OpenTabAtIndex(int index, int tab_index, const GURL& url); + +// Moves the tab in position |tab_index| in the TabStrip for browser at +// |from_index| to the TabStrip for browser at |to_index|. +void MoveTab(int from_index, int to_index, int tab_index); + +// Navigate the active tab for browser in position |index| to the given +// url, and blocks until the session model associator is aware of it. +bool NavigateTab(int index, const GURL& url); + +// Navigate the active tab for browser in position |index| back by one; +// if this isn't possible, does nothing +void NavigateTabBack(int index); + +// Navigate the active tab for browser in position |index| forward by +// one; if this isn't possible, does nothing +void NavigateTabForward(int index); + // Open multiple tabs and block until the session model associator is aware // of all of them. Returns true on success, false on failure. bool OpenMultipleTabs(int index, const std::vector<GURL>& urls);
diff --git a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc index 69c51bc..8ef7f6c 100644 --- a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc +++ b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
@@ -6,6 +6,7 @@ #include "base/test/histogram_tester.h" #include "chrome/browser/sessions/session_service.h" #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h" +#include "chrome/browser/sync/test/integration/session_hierarchy_match_checker.h" #include "chrome/browser/sync/test/integration/sessions_helper.h" #include "chrome/browser/sync/test/integration/sync_test.h" #include "chrome/browser/sync/test/integration/typed_urls_helper.h" @@ -17,7 +18,6 @@ #include "components/sessions/core/session_types.h" #include "components/sync/base/time.h" #include "components/sync/driver/sync_driver_switches.h" -#include "components/sync/test/fake_server/fake_server_verifier.h" #include "components/sync/test/fake_server/sessions_hierarchy.h" #if defined(OS_CHROMEOS) @@ -32,7 +32,12 @@ using sessions_helper::GetLocalWindows; using sessions_helper::GetSessionData; using sessions_helper::ModelAssociatorHasTabWithUrl; -using sessions_helper::OpenTabAndGetLocalWindows; +using sessions_helper::MoveTab; +using sessions_helper::NavigateTab; +using sessions_helper::NavigateTabBack; +using sessions_helper::NavigateTabForward; +using sessions_helper::OpenTab; +using sessions_helper::OpenTabAtIndex; using sessions_helper::ScopedWindowMap; using sessions_helper::SessionWindowMap; using sessions_helper::SyncedSessionVector; @@ -81,6 +86,36 @@ #endif } + void ExpectNavigationChain(const std::vector<GURL>& urls) { + ScopedWindowMap windows; + ASSERT_TRUE(GetLocalWindows(0, &windows)); + ASSERT_EQ(windows.begin()->second->tabs.size(), 1u); + sessions::SessionTab* tab = windows.begin()->second->tabs[0].get(); + + int index = 0; + EXPECT_EQ(urls.size(), tab->navigations.size()); + for (auto it = tab->navigations.begin(); it != tab->navigations.end(); + ++it, ++index) { + EXPECT_EQ(urls[index], it->virtual_url()); + } + } + + // Block until the expected hierarchy is recorded on the FakeServer for + // profile 0. This will time out if the hierarchy is never + // recorded. + void WaitForHierarchyOnServer( + const fake_server::SessionsHierarchy& hierarchy) { + SessionHierarchyMatchChecker checker(hierarchy, GetSyncService(0), + GetFakeServer()); + EXPECT_TRUE(checker.Wait()); + } + + // Shortcut to call WaitForHierarchyOnServer for only |url| in a single + // window. + void WaitForURLOnServer(const GURL& url) { + WaitForHierarchyOnServer({{url.spec()}}); + } + private: DISALLOW_COPY_AND_ASSIGN(SingleClientSessionsSyncTest); }; @@ -93,7 +128,8 @@ // Add a new session to client 0 and wait for it to sync. ScopedWindowMap old_windows; GURL url = GURL("http://127.0.0.1/bubba"); - ASSERT_TRUE(OpenTabAndGetLocalWindows(0, url, &old_windows)); + ASSERT_TRUE(OpenTab(0, url)); + ASSERT_TRUE(GetLocalWindows(0, &old_windows)); ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait()); // Get foreign session data from client 0. @@ -106,18 +142,13 @@ ASSERT_TRUE(GetLocalWindows(0, &new_windows)); ASSERT_TRUE(WindowsMatch(old_windows, new_windows)); - fake_server::FakeServerVerifier fake_server_verifier(GetFakeServer()); - SessionsHierarchy expected_sessions; - expected_sessions.AddWindow(url.spec()); - ASSERT_TRUE(fake_server_verifier.VerifySessions(expected_sessions)); + WaitForURLOnServer(url); } IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, NoSessions) { ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; - fake_server::FakeServerVerifier fake_server_verifier(GetFakeServer()); - SessionsHierarchy expected_sessions; - ASSERT_TRUE(fake_server_verifier.VerifySessions(expected_sessions)); + WaitForHierarchyOnServer(SessionsHierarchy()); } IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, ChromeHistory) { @@ -125,17 +156,8 @@ ASSERT_TRUE(CheckInitialState(0)); - // Add a new session to client 0 and wait for it to sync. - ScopedWindowMap old_windows; - ASSERT_TRUE(OpenTabAndGetLocalWindows(0, GURL(chrome::kChromeUIHistoryURL), - &old_windows)); - std::vector<GURL> urls; - urls.push_back(GURL(chrome::kChromeUIHistoryURL)); - ASSERT_TRUE(WaitForTabsToLoad(0, urls)); - - // Verify the chrome history page synced. - ASSERT_TRUE(ModelAssociatorHasTabWithUrl(0, - GURL(chrome::kChromeUIHistoryURL))); + ASSERT_TRUE(OpenTab(0, GURL(chrome::kChromeUIHistoryURL))); + WaitForURLOnServer(GURL(chrome::kChromeUIHistoryURL)); } IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, TimestampMatchesHistory) { @@ -147,7 +169,8 @@ const GURL url("data:text/html,<html><title>Test</title></html>"); ScopedWindowMap windows; - ASSERT_TRUE(OpenTabAndGetLocalWindows(0, url, &windows)); + ASSERT_TRUE(OpenTab(0, url)); + ASSERT_TRUE(GetLocalWindows(0, &windows)); int found_navigations = 0; for (auto it = windows.begin(); it != windows.end(); ++it) { @@ -178,7 +201,8 @@ const GURL url("data:text/html,<html><title>Test</title></html>"); ScopedWindowMap windows; - ASSERT_TRUE(OpenTabAndGetLocalWindows(0, url, &windows)); + ASSERT_TRUE(OpenTab(0, url)); + ASSERT_TRUE(GetLocalWindows(0, &windows)); int found_navigations = 0; for (auto it = windows.begin(); it != windows.end(); ++it) { @@ -194,6 +218,132 @@ ASSERT_EQ(1, found_navigations); } +IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, FragmentURLNavigation) { + ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; + ASSERT_TRUE(CheckInitialState(0)); + + GURL url = GURL("http://127.0.0.1/bubba"); + ASSERT_TRUE(OpenTab(0, url)); + WaitForURLOnServer(url); + + GURL fragment_url = GURL("http://127.0.0.1/bubba#fragment"); + NavigateTab(0, fragment_url); + WaitForURLOnServer(fragment_url); +} + +IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, + NavigationChainForwardBack) { + ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; + ASSERT_TRUE(CheckInitialState(0)); + + GURL first_url = GURL("http://127.0.0.1/foobar"); + ASSERT_TRUE(OpenTab(0, first_url)); + WaitForURLOnServer(first_url); + + GURL second_url = GURL("http://127.0.0.1/barbaz"); + NavigateTab(0, second_url); + WaitForURLOnServer(second_url); + + NavigateTabBack(0); + WaitForURLOnServer(first_url); + + ExpectNavigationChain({first_url, second_url}); + + NavigateTabForward(0); + WaitForURLOnServer(second_url); + + ExpectNavigationChain({first_url, second_url}); +} + +IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, + NavigationChainAlteredDestructively) { + ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; + ASSERT_TRUE(CheckInitialState(0)); + + GURL base_url = GURL("http://127.0.0.1/bubba"); + ASSERT_TRUE(OpenTab(0, base_url)); + WaitForURLOnServer(base_url); + + GURL first_url = GURL("http://127.0.0.1/foobar"); + ASSERT_TRUE(NavigateTab(0, first_url)); + WaitForURLOnServer(first_url); + + // Check that the navigation chain matches the above sequence of {base_url, + // first_url}. + ExpectNavigationChain({base_url, first_url}); + + NavigateTabBack(0); + WaitForURLOnServer(base_url); + + GURL second_url = GURL("http://127.0.0.1/barbaz"); + NavigateTab(0, second_url); + WaitForURLOnServer(second_url); + + NavigateTabBack(0); + WaitForURLOnServer(base_url); + + // Check that the navigation chain contains second_url where first_url was + // before. + ExpectNavigationChain({base_url, second_url}); +} + +IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, OpenNewTab) { + ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; + ASSERT_TRUE(CheckInitialState(0)); + + GURL base_url = GURL("http://127.0.0.1/bubba"); + ASSERT_TRUE(OpenTabAtIndex(0, 0, base_url)); + + WaitForURLOnServer(base_url); + + GURL new_tab_url = GURL("http://127.0.0.1/foobar"); + ASSERT_TRUE(OpenTabAtIndex(0, 1, new_tab_url)); + + WaitForHierarchyOnServer( + SessionsHierarchy({{base_url.spec(), new_tab_url.spec()}})); +} + +IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, OpenNewWindow) { + ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; + ASSERT_TRUE(CheckInitialState(0)); + + GURL base_url = GURL("http://127.0.0.1/bubba"); + ASSERT_TRUE(OpenTab(0, base_url)); + + WaitForURLOnServer(base_url); + + GURL new_window_url = GURL("http://127.0.0.1/foobar"); + AddBrowser(0); + ASSERT_TRUE(OpenTab(1, new_window_url)); + + WaitForHierarchyOnServer( + SessionsHierarchy({{base_url.spec()}, {new_window_url.spec()}})); +} + +IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, TabMovedToOtherWindow) { + ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; + ASSERT_TRUE(CheckInitialState(0)); + + GURL base_url = GURL("http://127.0.0.1/bubba"); + GURL moved_tab_url = GURL("http://127.0.0.1/foobar"); + + ASSERT_TRUE(OpenTab(0, base_url)); + ASSERT_TRUE(OpenTabAtIndex(0, 1, moved_tab_url)); + + GURL new_window_url = GURL("http://127.0.0.1/barbaz"); + AddBrowser(0); + ASSERT_TRUE(OpenTab(1, new_window_url)); + + WaitForHierarchyOnServer(SessionsHierarchy( + {{base_url.spec(), moved_tab_url.spec()}, {new_window_url.spec()}})); + + // Move tab 1 in browser 0 to browser 1. + MoveTab(0, 1, 1); + + WaitForHierarchyOnServer(SessionsHierarchy( + {{base_url.spec()}, {new_window_url.spec(), moved_tab_url.spec()}})); +} + IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, CookieJarMismatch) { ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; @@ -208,9 +358,8 @@ // Add a new session to client 0 and wait for it to sync. GURL url = GURL("http://127.0.0.1/bubba"); - ASSERT_TRUE(OpenTabAndGetLocalWindows(0, url, &old_windows)); - TriggerSyncForModelTypes(0, syncer::ModelTypeSet(syncer::SESSIONS)); - ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait()); + ASSERT_TRUE(OpenTab(0, url)); + WaitForURLOnServer(url); // The cookie jar mismatch value will be true by default due to // the way integration tests trigger signin (which does not involve a normal @@ -219,7 +368,7 @@ ASSERT_TRUE(message.commit().config_params().cookie_jar_mismatch()); // It is possible that multiple sync cycles occured during the call to - // OpenTabAndGetLocalWindows, which would cause multiple identical samples. + // OpenTab, which would cause multiple identical samples. ExpectUniqueSampleGE(histogram_tester, "Sync.CookieJarMatchOnNavigation", false, 1); ExpectUniqueSampleGE(histogram_tester, "Sync.CookieJarEmptyOnMismatch", @@ -242,7 +391,8 @@ // Trigger a sync and wait for it. GURL url = GURL("http://127.0.0.1/bubba2"); - ASSERT_TRUE(OpenTabAndGetLocalWindows(0, url, &old_windows)); + ASSERT_TRUE(OpenTab(0, url)); + ASSERT_TRUE(GetLocalWindows(0, &old_windows)); TriggerSyncForModelTypes(0, syncer::ModelTypeSet(syncer::SESSIONS)); ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc index f40d1f9..5008bee 100644 --- a/chrome/browser/sync/test/integration/sync_test.cc +++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -419,11 +419,14 @@ } Profile* SyncTest::GetProfile(int index) { - if (profiles_.empty()) - LOG(FATAL) << "SetupClients() has not yet been called."; - if (index < 0 || index >= static_cast<int>(profiles_.size())) - LOG(FATAL) << "GetProfile(): Index is out of bounds."; - return profiles_[index]; + EXPECT_FALSE(profiles_.empty()) << "SetupClients() has not yet been called."; + EXPECT_FALSE(index < 0 || index >= static_cast<int>(profiles_.size())) + << "GetProfile(): Index is out of bounds."; + + Profile* profile = profiles_[index]; + EXPECT_NE(nullptr, profile) << "No profile found at index: " << index; + + return profile; } std::vector<Profile*> SyncTest::GetAllProfiles() { @@ -438,13 +441,24 @@ } Browser* SyncTest::GetBrowser(int index) { - if (browsers_.empty()) - LOG(FATAL) << "SetupClients() has not yet been called."; - if (index < 0 || index >= static_cast<int>(browsers_.size())) - LOG(FATAL) << "GetBrowser(): Index is out of bounds."; + EXPECT_FALSE(browsers_.empty()) << "SetupClients() has not yet been called."; + EXPECT_FALSE(index < 0 || index >= static_cast<int>(browsers_.size())) + << "GetBrowser(): Index is out of bounds."; + + Browser* browser = browsers_[index]; + EXPECT_NE(browser, nullptr); + return browsers_[index]; } +Browser* SyncTest::AddBrowser(int profile_index) { + Profile* profile = GetProfile(profile_index); + browsers_.push_back(new Browser(Browser::CreateParams(profile, true))); + profiles_.push_back(profile); + + return browsers_[browsers_.size() - 1]; +} + ProfileSyncServiceHarness* SyncTest::GetClient(int index) { if (clients_.empty()) LOG(FATAL) << "SetupClients() has not yet been called."; @@ -487,7 +501,6 @@ profiles_.resize(num_clients_); profile_delegates_.resize(num_clients_ + 1); // + 1 for the verifier. tmp_profile_paths_.resize(num_clients_); - browsers_.resize(num_clients_); clients_.resize(num_clients_); invalidation_forwarders_.resize(num_clients_); sync_refreshers_.resize(num_clients_); @@ -528,11 +541,7 @@ DCHECK(profile); profiles_[index] = profile; - // CheckInitialState() assumes that no windows are open at startup. - browsers_[index] = - new Browser(Browser::CreateParams(GetProfile(index), true)); - - EXPECT_NE(nullptr, GetBrowser(index)) << "Could not create Browser " << index; + AddBrowser(index); // Make sure the ProfileSyncService has been created before creating the // ProfileSyncServiceHarness - some tests expect the ProfileSyncService to
diff --git a/chrome/browser/sync/test/integration/sync_test.h b/chrome/browser/sync/test/integration/sync_test.h index 818fecd..fb151cf 100644 --- a/chrome/browser/sync/test/integration/sync_test.h +++ b/chrome/browser/sync/test/integration/sync_test.h
@@ -126,11 +126,11 @@ void SetUpCommandLine(base::CommandLine* cl) override; // Used to get the number of sync clients used by a test. - int num_clients() WARN_UNUSED_RESULT { return num_clients_; } + int num_clients() { return num_clients_; } // Returns a pointer to a particular sync profile. Callee owns the object // and manages its lifetime. - Profile* GetProfile(int index) WARN_UNUSED_RESULT; + Profile* GetProfile(int index); // Returns a list of all profiles including the verifier if available. Callee // owns the objects and manages its lifetime. @@ -138,17 +138,23 @@ // Returns a pointer to a particular browser. Callee owns the object // and manages its lifetime. - Browser* GetBrowser(int index) WARN_UNUSED_RESULT; + Browser* GetBrowser(int index); + + // Adds a new browser belonging to the profile at |profile_index|, and appends + // it to the list of browsers. Creates a copy of the Profile pointer in + // position |profile_index| and appends it to the list of profiles. This is + // done so that the profile associated with the new browser can be found at + // the same index as it. Tests typically use browser indexes and profile + // indexes interchangeably; this allows them to do so freely. + Browser* AddBrowser(int profile_index); // Returns a pointer to a particular sync client. Callee owns the object // and manages its lifetime. - ProfileSyncServiceHarness* GetClient(int index) WARN_UNUSED_RESULT; + ProfileSyncServiceHarness* GetClient(int index); // Returns a reference to the collection of sync clients. Callee owns the // object and manages its lifetime. - std::vector<ProfileSyncServiceHarness*>& clients() WARN_UNUSED_RESULT { - return clients_.get(); - } + std::vector<ProfileSyncServiceHarness*>& clients() { return clients_.get(); } // Returns a ProfileSyncService at the given index. browser_sync::ProfileSyncService* GetSyncService(int index); @@ -158,10 +164,10 @@ // Returns a pointer to the sync profile that is used to verify changes to // individual sync profiles. Callee owns the object and manages its lifetime. - Profile* verifier() WARN_UNUSED_RESULT; + Profile* verifier(); // Used to determine whether the verifier profile should be updated or not. - bool use_verifier() WARN_UNUSED_RESULT { return use_verifier_; } + bool use_verifier() { return use_verifier_; } // After calling this method, changes made to a profile will no longer be // reflected in the verifier profile. Note: Not all datatypes use this.
diff --git a/chrome/browser/sync/test/integration/two_client_sessions_sync_test.cc b/chrome/browser/sync/test/integration/two_client_sessions_sync_test.cc index da1f810..f3fc775 100644 --- a/chrome/browser/sync/test/integration/two_client_sessions_sync_test.cc +++ b/chrome/browser/sync/test/integration/two_client_sessions_sync_test.cc
@@ -19,7 +19,9 @@ using sessions_helper::DeleteForeignSession; using sessions_helper::GetLocalWindows; using sessions_helper::GetSessionData; -using sessions_helper::OpenTabAndGetLocalWindows; +using sessions_helper::NavigateTab; +using sessions_helper::OpenTab; +using sessions_helper::OpenTabAtIndex; using sessions_helper::ScopedWindowMap; using sessions_helper::SessionWindowMap; using sessions_helper::SyncedSessionVector; @@ -30,12 +32,26 @@ TwoClientSessionsSyncTest() : SyncTest(TWO_CLIENT) {} ~TwoClientSessionsSyncTest() override {} + void WaitForWindowsInForeignSession(int index, ScopedWindowMap windows) { + std::vector<ScopedWindowMap> expected_windows; + expected_windows.push_back(std::move(windows)); + EXPECT_TRUE(ForeignSessionsMatchChecker(index, expected_windows).Wait()); + } + + void WaitForForeignSessionsToSync(int local_index, int non_local_index) { + ScopedWindowMap client_windows; + ASSERT_TRUE(GetLocalWindows(local_index, &client_windows)); + WaitForWindowsInForeignSession(non_local_index, std::move(client_windows)); + } + private: DISALLOW_COPY_AND_ASSIGN(TwoClientSessionsSyncTest); }; static const char* kURL1 = "http://127.0.0.1/bubba1"; static const char* kURL2 = "http://127.0.0.1/bubba2"; +static const char* kURL3 = "http://127.0.0.1/foobar"; +static const char* kURL4 = "http://127.0.0.1/barbaz"; // TODO(zea): Test each individual session command we care about separately. // (as well as multi-window). We're currently only checking basic single-window/ @@ -50,14 +66,9 @@ ScopedWindowMap client0_windows; std::string url = base::StringPrintf("http://127.0.0.1/bubba%s", base::GenerateGUID().c_str()); - ASSERT_TRUE(OpenTabAndGetLocalWindows(0, GURL(url), &client0_windows)); - // Retain the window information on client 0 - std::vector<ScopedWindowMap> expected_windows(1); - expected_windows[0] = std::move(client0_windows); - - // Check the foreign windows on client 1 - ASSERT_TRUE(ForeignSessionsMatchChecker(1, expected_windows).Wait()); + ASSERT_TRUE(OpenTab(0, GURL(url))); + WaitForForeignSessionsToSync(0, 1); } IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest, @@ -70,7 +81,8 @@ ScopedWindowMap windows; std::string url = base::StringPrintf("http://127.0.0.1/bubba%s", base::GenerateGUID().c_str()); - ASSERT_TRUE(OpenTabAndGetLocalWindows(i, GURL(url), &windows)); + ASSERT_TRUE(OpenTab(i, GURL(url))); + ASSERT_TRUE(GetLocalWindows(i, &windows)); client_windows[i] = std::move(windows); } @@ -104,19 +116,9 @@ ASSERT_TRUE(CheckInitialState(0)); ASSERT_TRUE(CheckInitialState(1)); - ScopedWindowMap client0_windows; - ASSERT_TRUE(OpenTabAndGetLocalWindows(0, GURL(kURL1), &client0_windows)); + ASSERT_TRUE(OpenTab(0, GURL(kURL1))); ASSERT_TRUE(EnableEncryption(0)); - ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1))); - - // Get foreign session data from client 1. - ASSERT_TRUE(IsEncryptionComplete(1)); - SyncedSessionVector sessions1; - ASSERT_TRUE(GetSessionData(1, &sessions1)); - - // Verify client 1's foreign session matches client 0 current window. - ASSERT_EQ(1U, sessions1.size()); - ASSERT_TRUE(WindowsMatch(sessions1[0]->windows, client0_windows)); + WaitForForeignSessionsToSync(0, 1); } IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest, @@ -139,27 +141,15 @@ ASSERT_TRUE(CheckInitialState(0)); ASSERT_TRUE(CheckInitialState(1)); - // Open tabs on both clients and retain window information. - ScopedWindowMap client0_windows; - ASSERT_TRUE(OpenTabAndGetLocalWindows(0, GURL(kURL2), &client0_windows)); - ScopedWindowMap client1_windows; - ASSERT_TRUE(OpenTabAndGetLocalWindows(1, GURL(kURL1), &client1_windows)); + ASSERT_TRUE(OpenTab(0, GURL(kURL1))); + ASSERT_TRUE(OpenTab(1, GURL(kURL2))); - // Wait for sync. - ASSERT_TRUE(AwaitQuiescence()); + WaitForForeignSessionsToSync(0, 1); + WaitForForeignSessionsToSync(1, 0); - // Get foreign session data from client 0 and 1. - SyncedSessionVector sessions0; - SyncedSessionVector sessions1; - ASSERT_TRUE(GetSessionData(0, &sessions0)); - ASSERT_TRUE(GetSessionData(1, &sessions1)); - - // Verify client 1's foreign session matches client 0's current window and - // vice versa. - ASSERT_EQ(1U, sessions0.size()); - ASSERT_EQ(1U, sessions1.size()); - ASSERT_TRUE(WindowsMatch(sessions1[0]->windows, client0_windows)); - ASSERT_TRUE(WindowsMatch(sessions0[0]->windows, client1_windows)); + // Check that a navigation in client 0 is reflected on client 1. + NavigateTab(0, GURL(kURL3)); + WaitForForeignSessionsToSync(0, 1); } IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest, DeleteIdleSession) { @@ -169,19 +159,13 @@ ASSERT_TRUE(CheckInitialState(1)); // Client 0 opened some tabs then went idle. - ScopedWindowMap client0_windows; - ASSERT_TRUE(OpenTabAndGetLocalWindows(0, GURL(kURL1), &client0_windows)); - - ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1))); + ASSERT_TRUE(OpenTab(0, GURL(kURL1))); + WaitForForeignSessionsToSync(0, 1); // Get foreign session data from client 1. SyncedSessionVector sessions1; ASSERT_TRUE(GetSessionData(1, &sessions1)); - // Verify client 1's foreign session matches client 0 current window. - ASSERT_EQ(1U, sessions1.size()); - ASSERT_TRUE(WindowsMatch(sessions1[0]->windows, client0_windows)); - // Client 1 now deletes client 0's tabs. This frees the memory of sessions1. DeleteForeignSession(1, sessions1[0]->session_tag); ASSERT_TRUE(GetClient(1)->AwaitMutualSyncCycleCompletion(GetClient(0))); @@ -197,14 +181,12 @@ ASSERT_TRUE(CheckInitialState(1)); // Client 0 opened some tabs then went idle. - ScopedWindowMap client0_windows; - ASSERT_TRUE(OpenTabAndGetLocalWindows(0, GURL(kURL1), &client0_windows)); + ASSERT_TRUE(OpenTab(0, GURL(kURL1))); + WaitForForeignSessionsToSync(0, 1); - ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1))); SyncedSessionVector sessions1; ASSERT_TRUE(GetSessionData(1, &sessions1)); ASSERT_EQ(1U, sessions1.size()); - ASSERT_TRUE(WindowsMatch(sessions1[0]->windows, client0_windows)); // Client 1 now deletes client 0's tabs. This frees the memory of sessions1. DeleteForeignSession(1, sessions1[0]->session_tag); @@ -212,9 +194,23 @@ ASSERT_FALSE(GetSessionData(1, &sessions1)); // Client 0 becomes active again with a new tab. - ASSERT_TRUE(OpenTabAndGetLocalWindows(0, GURL(kURL2), &client0_windows)); - ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1))); - ASSERT_TRUE(GetSessionData(1, &sessions1)); - ASSERT_EQ(1U, sessions1.size()); - ASSERT_TRUE(WindowsMatch(sessions1[0]->windows, client0_windows)); + ASSERT_TRUE(OpenTab(0, GURL(kURL2))); + WaitForForeignSessionsToSync(0, 1); +} + +IN_PROC_BROWSER_TEST_F(TwoClientSessionsSyncTest, MultipleWindowsMultipleTabs) { + ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; + + ASSERT_TRUE(CheckInitialState(0)); + ASSERT_TRUE(CheckInitialState(1)); + + EXPECT_TRUE(OpenTab(0, GURL(kURL1))); + EXPECT_TRUE(OpenTabAtIndex(0, 1, GURL(kURL2))); + + // Add a second browser for profile 0. This browser ends up in index 2. + AddBrowser(0); + EXPECT_TRUE(OpenTab(2, GURL(kURL3))); + EXPECT_TRUE(OpenTabAtIndex(2, 2, GURL(kURL4))); + + WaitForForeignSessionsToSync(0, 1); }
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view_cocoa.h b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view_cocoa.h index f123576..62626ae 100644 --- a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view_cocoa.h +++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view_cocoa.h
@@ -13,8 +13,6 @@ #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h" @class BookmarkBarController; -@class BookmarkBarItemContainer; -@class BookmarkBarTextField; @interface BookmarkBarView : NSView { @private @@ -22,15 +20,15 @@ CGFloat dropIndicatorPosition_; // x position IBOutlet BookmarkBarController* controller_; - IBOutlet BookmarkBarTextField* noItemTextfield_; + IBOutlet NSTextField* noItemTextfield_; IBOutlet NSButton* importBookmarksButton_; - BookmarkBarItemContainer* noItemContainer_; + GTMWidthBasedTweaker* noItemContainer_; } -- (BookmarkBarTextField*)noItemTextfield; +- (NSTextField*)noItemTextfield; - (NSButton*)importBookmarksButton; - (BookmarkBarController*)controller; -@property(nonatomic, assign) IBOutlet BookmarkBarItemContainer* noItemContainer; +@property(nonatomic, assign) IBOutlet GTMWidthBasedTweaker* noItemContainer; @end @interface BookmarkBarView() // TestingOrInternalAPI @@ -39,23 +37,4 @@ - (void)setController:(id)controller; @end - -// NSTextField subclass responsible for routing -menu to the BookmarBarView. -// This is necessary when building with the 10.6 SDK because -rightMouseDown: -// does not follow the responder chain. -@interface BookmarkBarTextField : NSTextField { - @private - IBOutlet BookmarkBarView* barView_; -} -@end - -// GTMWidthBasedTweaker subclass responsible for routing -menu to the -// BookmarBarView. This is necessary when building with the 10.6 SDK because -// -rightMouseDown: does not follow the responder chain. -@interface BookmarkBarItemContainer : GTMWidthBasedTweaker { - @private - IBOutlet BookmarkBarView* barView_; -} -@end - #endif // CHROME_BROWSER_UI_COCOA_BOOKMARKS_BOOKMARK_BAR_VIEW_COCOA_H_
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view_cocoa.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view_cocoa.mm index eb1a0da06..cda5ade 100644 --- a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view_cocoa.mm +++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view_cocoa.mm
@@ -113,7 +113,7 @@ return NO; } -- (BookmarkBarTextField*)noItemTextfield { +- (NSTextField*)noItemTextfield { return noItemTextfield_; } @@ -290,19 +290,3 @@ } @end // @implementation BookmarkBarView - -@implementation BookmarkBarTextField - -- (NSMenu*)menu { - return [barView_ menu]; -} - -@end // @implementation BookmarkBarTextField - -@implementation BookmarkBarItemContainer - -- (NSMenu*)menu { - return [barView_ menu]; -} - -@end // @implementation BookmarkBarItemContainer
diff --git a/chrome/browser/ui/location_bar/location_bar.cc b/chrome/browser/ui/location_bar/location_bar.cc index f01caef..4752eb2c 100644 --- a/chrome/browser/ui/location_bar/location_bar.cc +++ b/chrome/browser/ui/location_bar/location_bar.cc
@@ -41,8 +41,11 @@ base::string16 LocationBar::GetExtensionName( const GURL& url, content::WebContents* web_contents) { - CHECK(web_contents); - if (!url.SchemeIs(extensions::kExtensionScheme)) + // On ChromeOS, this function can be called using web_contents from + // SimpleWebViewDialog::GetWebContents() which always returns null. + // TODO(crbug.com/680329) Remove the null check and make + // SimpleWebViewDialog::GetWebContents return the proper web contents instead. + if (!web_contents || !url.SchemeIs(extensions::kExtensionScheme)) return base::string16(); content::BrowserContext* browser_context = web_contents->GetBrowserContext();
diff --git a/chrome/browser/ui/views/device_chooser_content_view.cc b/chrome/browser/ui/views/device_chooser_content_view.cc index 6946d87..c37bf36 100644 --- a/chrome/browser/ui/views/device_chooser_content_view.cc +++ b/chrome/browser/ui/views/device_chooser_content_view.cc
@@ -7,6 +7,7 @@ #include "base/memory/ptr_util.h" #include "base/numerics/safe_conversions.h" #include "chrome/app/vector_icons/vector_icons.h" +#include "chrome/browser/ui/views/harmony/layout_delegate.h" #include "chrome/grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" @@ -110,6 +111,16 @@ views::View::Layout(); } +gfx::Size DeviceChooserContentView::GetPreferredSize() const { + constexpr int kHeight = 320; + constexpr int kDefaultWidth = 402; + int width = LayoutDelegate::Get()->GetDialogPreferredWidth( + LayoutDelegate::DialogWidth::MEDIUM); + if (!width) + width = kDefaultWidth; + return gfx::Size(width, kHeight); +} + int DeviceChooserContentView::RowCount() { // When there are no devices, the table contains a message saying there // are no devices, so the number of rows is always at least 1.
diff --git a/chrome/browser/ui/views/device_chooser_content_view.h b/chrome/browser/ui/views/device_chooser_content_view.h index fe1df0b3..de3a430 100644 --- a/chrome/browser/ui/views/device_chooser_content_view.h +++ b/chrome/browser/ui/views/device_chooser_content_view.h
@@ -36,6 +36,7 @@ // views::View: void Layout() override; + gfx::Size GetPreferredSize() const override; // ui::TableModel: int RowCount() override;
diff --git a/chrome/browser/ui/views/website_settings/permission_selector_row.cc b/chrome/browser/ui/views/website_settings/permission_selector_row.cc index 6ba6b2270..6554dae 100644 --- a/chrome/browser/ui/views/website_settings/permission_selector_row.cc +++ b/chrome/browser/ui/views/website_settings/permission_selector_row.cc
@@ -173,8 +173,7 @@ class PermissionCombobox : public views::Combobox, public views::ComboboxListener { public: - PermissionCombobox(const base::string16& text, - ComboboxModelAdapter* model, + PermissionCombobox(ComboboxModelAdapter* model, bool enabled, bool use_default); ~PermissionCombobox() override; @@ -191,8 +190,7 @@ ComboboxModelAdapter* model_; }; -PermissionCombobox::PermissionCombobox(const base::string16& text, - ComboboxModelAdapter* model, +PermissionCombobox::PermissionCombobox(ComboboxModelAdapter* model, bool enabled, bool use_default) : views::Combobox(model), model_(model) { @@ -335,9 +333,6 @@ combobox_model_adapter_.reset( new internal::ComboboxModelAdapter(menu_model_.get())); combobox_ = new internal::PermissionCombobox( - WebsiteSettingsUI::PermissionActionToUIString( - profile_, permission.type, permission.setting, - permission.default_setting, permission.source), combobox_model_adapter_.get(), button_enabled, true); combobox_->SetEnabled(button_enabled); combobox_->SetAccessibleName(
diff --git a/chrome/browser/ui/website_settings/permission_menu_model.cc b/chrome/browser/ui/website_settings/permission_menu_model.cc index 80665ac..2828c77 100644 --- a/chrome/browser/ui/website_settings/permission_menu_model.cc +++ b/chrome/browser/ui/website_settings/permission_menu_model.cc
@@ -12,6 +12,7 @@ #include "content/public/common/origin_util.h" #include "ppapi/features/features.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/material_design/material_design_controller.h" PermissionMenuModel::PermissionMenuModel( Profile* profile, @@ -61,6 +62,16 @@ default: break; } + + // The Material UI for site settings uses comboboxes instead of menubuttons, + // which means the elements of the menu themselves have to be shorter, instead + // of simply setting a shorter label on the menubutton. + if (ui::MaterialDesignController::IsSecondaryUiMaterial()) { + label = WebsiteSettingsUI::PermissionActionToUIString( + profile, info.type, info.setting, effective_default_setting, + info.source); + } + AddCheckItem(CONTENT_SETTING_DEFAULT, label); // Notifications does not support CONTENT_SETTING_ALLOW in incognito.
diff --git a/chrome/browser/ui/webui/settings/downloads_handler.cc b/chrome/browser/ui/webui/settings/downloads_handler.cc index 93e74d8..72d30c69 100644 --- a/chrome/browser/ui/webui/settings/downloads_handler.cc +++ b/chrome/browser/ui/webui/settings/downloads_handler.cc
@@ -36,6 +36,10 @@ "initializeDownloads", base::Bind(&DownloadsHandler::HandleInitialize, base::Unretained(this))); web_ui()->RegisterMessageCallback( + "resetAutoOpenFileTypes", + base::Bind(&DownloadsHandler::HandleResetAutoOpenFileTypes, + base::Unretained(this))); + web_ui()->RegisterMessageCallback( "selectDownloadLocation", base::Bind(&DownloadsHandler::HandleSelectDownloadLocation, base::Unretained(this))); @@ -68,6 +72,15 @@ base::FundamentalValue(auto_open_downloads)); } +void DownloadsHandler::HandleResetAutoOpenFileTypes( + const base::ListValue* args) { + content::RecordAction(UserMetricsAction("Options_ResetAutoOpenFiles")); + content::DownloadManager* manager = + content::BrowserContext::GetDownloadManager(profile_); + if (manager) + DownloadPrefs::FromDownloadManager(manager)->ResetAutoOpen(); +} + void DownloadsHandler::HandleSelectDownloadLocation( const base::ListValue* args) { PrefService* pref_service = profile_->GetPrefs();
diff --git a/chrome/browser/ui/webui/settings/downloads_handler.h b/chrome/browser/ui/webui/settings/downloads_handler.h index b8c1561..289cf74 100644 --- a/chrome/browser/ui/webui/settings/downloads_handler.h +++ b/chrome/browser/ui/webui/settings/downloads_handler.h
@@ -39,6 +39,9 @@ void SendAutoOpenDownloadsToJavascript(); + // Resets the list of filetypes that are auto-opened after download. + void HandleResetAutoOpenFileTypes(const base::ListValue* args); + // Callback for the "selectDownloadLocation" message. This will prompt the // user for a destination folder using platform-specific APIs. void HandleSelectDownloadLocation(const base::ListValue* args);
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc index c6122de..864e00f 100644 --- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc +++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -74,6 +74,7 @@ {"back", IDS_ACCNAME_BACK}, {"basicPageTitle", IDS_SETTINGS_BASIC}, {"cancel", IDS_CANCEL}, + {"clear", IDS_SETTINGS_CLEAR}, {"close", IDS_CLOSE}, {"confirm", IDS_CONFIRM}, {"controlledByExtension", IDS_SETTINGS_CONTROLLED_BY_EXTENSION}, @@ -698,7 +699,8 @@ {"changeDownloadLocation", IDS_SETTINGS_CHANGE_DOWNLOAD_LOCATION}, {"promptForDownload", IDS_SETTINGS_PROMPT_FOR_DOWNLOAD}, {"disconnectGoogleDriveAccount", IDS_SETTINGS_DISCONNECT_GOOGLE_DRIVE}, - }; + {"openFileTypesAutomatically", + IDS_SETTINGS_OPEN_FILE_TYPES_AUTOMATICALLY}}; AddLocalizedStringsBulk(html_source, localized_strings, arraysize(localized_strings)); }
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler.cc b/chrome/browser/ui/webui/signin/inline_login_handler.cc index cac43a1..27bb4d1 100644 --- a/chrome/browser/ui/webui/signin/inline_login_handler.cc +++ b/chrome/browser/ui/webui/signin/inline_login_handler.cc
@@ -54,6 +54,7 @@ } void InlineLoginHandler::HandleInitializeMessage(const base::ListValue* args) { + AllowJavascript(); content::WebContents* contents = web_ui()->GetWebContents(); content::StoragePartition* partition = content::BrowserContext::GetStoragePartitionForSite( @@ -233,9 +234,7 @@ params.SetBoolean("readOnlyEmail", !read_only_email.empty()); SetExtraInitParams(params); - - web_ui()->CallJavascriptFunctionUnsafe("inline.login.loadAuthExtension", - params); + CallJavascriptFunction("inline.login.loadAuthExtension", params); } void InlineLoginHandler::HandleCompleteLoginMessage( @@ -267,7 +266,7 @@ ui::PAGE_TRANSITION_AUTO_TOPLEVEL); chrome::Navigate(¶ms); - web_ui()->CallJavascriptFunctionUnsafe("inline.login.closeDialog"); + CloseDialogFromJavascript(); } void InlineLoginHandler::HandleNavigationButtonClicked( @@ -287,3 +286,8 @@ // Does nothing if user manager is not showing. UserManagerProfileDialog::HideDialog(); } + +void InlineLoginHandler::CloseDialogFromJavascript() { + if (IsJavascriptAllowed()) + CallJavascriptFunction("inline.login.closeDialog"); +}
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler.h b/chrome/browser/ui/webui/signin/inline_login_handler.h index 51bbdbe..350d4d01 100644 --- a/chrome/browser/ui/webui/signin/inline_login_handler.h +++ b/chrome/browser/ui/webui/signin/inline_login_handler.h
@@ -35,6 +35,11 @@ kDesktopAuthMode = 2 }; + // Closes the dialog by calling the |inline.login.closeDialog| Javascript + // function. + // Does nothing if calling Javascript functions is not allowed. + void CloseDialogFromJavascript(); + private: // Record correspond sign in user action for an access point. void RecordSigninUserActionForAccessPoint(
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc index 1c28984..b79e4883 100644 --- a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc +++ b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
@@ -132,7 +132,8 @@ } } if (handler) - handler->web_ui()->CallJavascriptFunctionUnsafe("inline.login.closeDialog"); + handler->CloseDialogFromJavascript(); + UserManager::Hide(); }
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_impl.h b/chrome/browser/ui/webui/signin/inline_login_handler_impl.h index 1de6f99..74c5aed 100644 --- a/chrome/browser/ui/webui/signin/inline_login_handler_impl.h +++ b/chrome/browser/ui/webui/signin/inline_login_handler_impl.h
@@ -27,6 +27,7 @@ ~InlineLoginHandlerImpl() override; using InlineLoginHandler::web_ui; + using InlineLoginHandler::CloseDialogFromJavascript; base::WeakPtr<InlineLoginHandlerImpl> GetWeakPtr() { return weak_factory_.GetWeakPtr();
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc index a4f67e1..265c073 100644 --- a/chrome/renderer/chrome_content_renderer_client.cc +++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -366,6 +366,10 @@ if (base::FeatureList::IsEnabled(features::kModuleDatabase)) { thread->GetRemoteInterfaces()->GetInterface(&module_event_sink_); + // Rebind the ModuleEventSink so that it can be accessed on the IO thread. + module_event_sink_.Bind(module_event_sink_.PassInterface(), + thread->GetIOTaskRunner()); + // It is safe to pass an unretained pointer to |module_event_sink_|, as it // is owned by the process singleton ChromeContentRendererClient, which is // leaked.
diff --git a/chrome/renderer/resources/extensions/browser_action_custom_bindings.js b/chrome/renderer/resources/extensions/browser_action_custom_bindings.js index a8080a78..9975d31 100644 --- a/chrome/renderer/resources/extensions/browser_action_custom_bindings.js +++ b/chrome/renderer/resources/extensions/browser_action_custom_bindings.js
@@ -4,11 +4,12 @@ // Custom binding for the browserAction API. -var binding = require('binding').Binding.create('browserAction'); +var binding = apiBridge || require('binding').Binding.create('browserAction'); var setIcon = require('setIcon').setIcon; var getExtensionViews = requireNative('runtime').GetExtensionViews; -var sendRequest = require('sendRequest').sendRequest; +var sendRequest = apiBridge ? + apiBridge.sendRequest.bind(apiBridge) : require('sendRequest').sendRequest; var lastError = require('lastError'); binding.registerCustomHook(function(bindingsAPI) { @@ -16,7 +17,9 @@ apiFunctions.setHandleRequest('setIcon', function(details, callback) { setIcon(details, function(args) { - sendRequest(this.name, [args, callback], this.definition.parameters); + sendRequest('browserAction.setIcon', + [args, callback], + apiBridge ? undefined : this.definition.parameters); }.bind(this)); }); @@ -34,4 +37,5 @@ }); }); -exports.$set('binding', binding.generate()); +if (!apiBridge) + exports.$set('binding', binding.generate());
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 03d45d7..6830f7a 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -858,6 +858,8 @@ "../browser/sync/test/integration/retry_verifier.h", "../browser/sync/test/integration/search_engines_helper.cc", "../browser/sync/test/integration/search_engines_helper.h", + "../browser/sync/test/integration/session_hierarchy_match_checker.cc", + "../browser/sync/test/integration/session_hierarchy_match_checker.h", "../browser/sync/test/integration/sessions_helper.cc", "../browser/sync/test/integration/sessions_helper.h", "../browser/sync/test/integration/single_client_status_change_checker.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/sync/OWNERS b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/sync/OWNERS index bc330c6..91e7103 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/sync/OWNERS +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/sync/OWNERS
@@ -2,3 +2,5 @@ nyquist@chromium.org yfriedman@chromium.org zea@chromium.org + +# COMPONENT: Services>Sync
diff --git a/chrome/test/data/extensions/api_test/native_bindings/extension/background.js b/chrome/test/data/extensions/api_test/native_bindings/extension/background.js index 0a401d5..ef260c44 100644 --- a/chrome/test/data/extensions/api_test/native_bindings/extension/background.js +++ b/chrome/test/data/extensions/api_test/native_bindings/extension/background.js
@@ -152,6 +152,10 @@ }); }); }, + function testBrowserActionWithCustomSendRequest() { + // browserAction.setIcon uses a custom hook that calls sendRequest(). + chrome.browserAction.setIcon({path: 'icon.png'}, chrome.test.succeed); + }, ]; chrome.test.getConfig(config => {
diff --git a/chrome/test/data/extensions/api_test/native_bindings/extension/icon.png b/chrome/test/data/extensions/api_test/native_bindings/extension/icon.png new file mode 100644 index 0000000..84c4be34 --- /dev/null +++ b/chrome/test/data/extensions/api_test/native_bindings/extension/icon.png Binary files differ
diff --git a/chrome/test/data/extensions/api_test/native_bindings/extension/manifest.json b/chrome/test/data/extensions/api_test/native_bindings/extension/manifest.json index c3f995a5..92541e8 100644 --- a/chrome/test/data/extensions/api_test/native_bindings/extension/manifest.json +++ b/chrome/test/data/extensions/api_test/native_bindings/extension/manifest.json
@@ -13,5 +13,6 @@ "content_scripts": [{ "matches": ["*://*/*messaging_test.html"], "js": ["content_script.js"] - }] + }], + "browser_action": {} }
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js index 193be25..565e212 100644 --- a/chrome/test/data/webui/settings/cr_settings_browsertest.js +++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -557,6 +557,30 @@ mocha.run(); }); +/** + * @constructor + * @extends {CrSettingsBrowserTest} + */ +function CrSettingsDownloadsPageTest() {} + +CrSettingsDownloadsPageTest.prototype = { + __proto__: CrSettingsBrowserTest.prototype, + + /** @override */ + browsePreload: 'chrome://md-settings/downloads_page/downloads_page.html', + + /** @override */ + extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([ + ROOT_PATH + 'ui/webui/resources/js/promise_resolver.js', + 'test_browser_proxy.js', + 'downloads_page_test.js', + ]), +}; + +TEST_F('CrSettingsDownloadsPageTest', 'All', function() { + mocha.run(); +}); + GEN('#if !defined(OS_CHROMEOS)'); /** * Test fixture for chrome/browser/resources/settings/default_browser_page/.
diff --git a/chrome/test/data/webui/settings/downloads_page_test.js b/chrome/test/data/webui/settings/downloads_page_test.js new file mode 100644 index 0000000..c8af19c --- /dev/null +++ b/chrome/test/data/webui/settings/downloads_page_test.js
@@ -0,0 +1,85 @@ +// Copyright 2017 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. + +/** + * @constructor + * @implements {settings.DownloadsBrowserProxy} + * @extends {settings.TestBrowserProxy} + */ +var TestDownloadsBrowserProxy = function() { + settings.TestBrowserProxy.call(this, [ + 'initializeDownloads', + 'selectDownloadLocation', + 'resetAutoOpenFileTypes', + ]); +}; + +TestDownloadsBrowserProxy.prototype = { + __proto__: settings.TestBrowserProxy.prototype, + + /** @override */ + initializeDownloads: function() { + this.methodCalled('initializeDownloads'); + }, + + /** @override */ + selectDownloadLocation: function() { + this.methodCalled('selectDownloadLocation'); + }, + + /** @override */ + resetAutoOpenFileTypes: function() { + this.methodCalled('resetAutoOpenFileTypes'); + }, +}; + +var downloadsPage = null; + +/** @type {?TestDownloadsBrowserProxy} */ +var DownloadsBrowserProxy = null; + +suite('DownloadsHandler', function() { + setup(function() { + DownloadsBrowserProxy = new TestDownloadsBrowserProxy(); + settings.DownloadsBrowserProxyImpl.instance_ = DownloadsBrowserProxy; + + PolymerTest.clearBody(); + + downloadsPage = document.createElement('settings-downloads-page'); + document.body.appendChild(downloadsPage); + + // Page element must call 'initializeDownloads' upon attachment to the DOM. + return DownloadsBrowserProxy.whenCalled('initializeDownloads'); + }); + + teardown(function() { + downloadsPage.remove(); + }); + + test('select downloads location', function() { + var button = downloadsPage.$$('#changeDownloadsPath'); + assertTrue(!!button); + MockInteractions.tap(button); + return DownloadsBrowserProxy.whenCalled('selectDownloadLocation'); + }); + + test('openAdvancedDownloadsettings', function() { + var button = downloadsPage.$$('#resetAutoOpenFileTypes'); + assertTrue(!button); + + cr.webUIListenerCallback('auto-open-downloads-changed', true); + Polymer.dom.flush(); + var button = downloadsPage.$$('#resetAutoOpenFileTypes'); + assertTrue(!!button); + + MockInteractions.tap(button); + return DownloadsBrowserProxy.whenCalled('resetAutoOpenFileTypes') + .then(function() { + cr.webUIListenerCallback('auto-open-downloads-changed', false); + Polymer.dom.flush(); + var button = downloadsPage.$$('#resetAutoOpenFileTypes'); + assertTrue(!button); + }); + }); +});
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java index e45dd8f..a821c42 100644 --- a/components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java +++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java
@@ -25,9 +25,9 @@ private static final String LIBRARY_NAME = "cronet"; private static final String TAG = CronetLibraryLoader.class.getSimpleName(); // Has library loading commenced? Setting guarded by sLoadLock. - private static volatile boolean sInitStarted = false; + private static volatile boolean sLibraryLoaded = false; // Has ensureMainThreadInitialized() completed? Only accessed on main thread. - private static boolean sMainThreadInitDone = false; + private static volatile boolean sMainThreadInitDone = false; /** * Ensure that native library is loaded and initialized. Can be called from @@ -36,38 +36,41 @@ public static void ensureInitialized( final Context applicationContext, final CronetEngineBuilderImpl builder) { synchronized (sLoadLock) { - if (sInitStarted) { - return; - } - sInitStarted = true; - ContextUtils.initApplicationContext(applicationContext); - if (builder.libraryLoader() != null) { - builder.libraryLoader().loadLibrary(LIBRARY_NAME); - } else { - System.loadLibrary(LIBRARY_NAME); - } - ContextUtils.initApplicationContextForNative(); - String implVersion = ImplVersion.getCronetVersion(); - if (!implVersion.equals(nativeGetCronetVersion())) { - throw new RuntimeException(String.format("Expected Cronet version number %s, " - + "actual version number %s.", - implVersion, nativeGetCronetVersion())); - } - Log.i(TAG, "Cronet version: %s, arch: %s", implVersion, System.getProperty("os.arch")); - // Init native Chromium CronetEngine on Main UI thread. - Runnable task = new Runnable() { - @Override - public void run() { - ensureInitializedOnMainThread(applicationContext); + if (!sLibraryLoaded) { + ContextUtils.initApplicationContext(applicationContext); + if (builder.libraryLoader() != null) { + builder.libraryLoader().loadLibrary(LIBRARY_NAME); + } else { + System.loadLibrary(LIBRARY_NAME); } - }; - // Run task immediately or post it to the UI thread. - if (Looper.getMainLooper() == Looper.myLooper()) { - task.run(); - } else { - // The initOnMainThread will complete on the main thread prior - // to other tasks posted to the main thread. - new Handler(Looper.getMainLooper()).post(task); + ContextUtils.initApplicationContextForNative(); + String implVersion = ImplVersion.getCronetVersion(); + if (!implVersion.equals(nativeGetCronetVersion())) { + throw new RuntimeException(String.format("Expected Cronet version number %s, " + + "actual version number %s.", + implVersion, nativeGetCronetVersion())); + } + Log.i(TAG, "Cronet version: %s, arch: %s", implVersion, + System.getProperty("os.arch")); + sLibraryLoaded = true; + } + + if (!sMainThreadInitDone) { + // Init native Chromium CronetEngine on Main UI thread. + Runnable task = new Runnable() { + @Override + public void run() { + ensureInitializedOnMainThread(applicationContext); + } + }; + // Run task immediately or post it to the UI thread. + if (Looper.getMainLooper() == Looper.myLooper()) { + task.run(); + } else { + // The initOnMainThread will complete on the main thread prior + // to other tasks posted to the main thread. + new Handler(Looper.getMainLooper()).post(task); + } } } } @@ -78,7 +81,7 @@ * main thread native MessageLoop is initialized. */ static void ensureInitializedOnMainThread(Context context) { - assert sInitStarted; + assert sLibraryLoaded; assert Looper.getMainLooper() == Looper.myLooper(); if (sMainThreadInitDone) { return;
diff --git a/components/cronet/url_request_context_config.cc b/components/cronet/url_request_context_config.cc index 5e1bc35..00979d3 100644 --- a/components/cronet/url_request_context_config.cc +++ b/components/cronet/url_request_context_config.cc
@@ -15,6 +15,7 @@ #include "base/strings/string_split.h" #include "base/values.h" #include "components/cronet/stale_host_resolver.h" +#include "net/base/address_family.h" #include "net/cert/caching_cert_verifier.h" #include "net/cert/cert_verifier.h" #include "net/cert/cert_verify_proc.h" @@ -81,6 +82,11 @@ const char kHostResolverRulesFieldTrialName[] = "HostResolverRules"; const char kHostResolverRules[] = "host_resolver_rules"; +// Disable IPv6. This should almost never be necessary because the network stack +// has IPv6 detection logic. Please do not turn on this option without first +// reporting a bug. See http://crbug.com/696569 for the currently known issue. +const char kDisableIPv6[] = "disable_ipv6"; + const char kSSLKeyLogFile[] = "ssl_key_log_file"; void ParseAndSetExperimentalOptions( @@ -209,6 +215,7 @@ bool async_dns_enable = false; bool stale_dns_enable = false; bool host_resolver_rules_enable = false; + bool disable_ipv6 = false; StaleHostResolver::StaleOptions stale_dns_options; std::string host_resolver_rules_string; @@ -247,19 +254,24 @@ kHostResolverRules, &host_resolver_rules_string); } - if (async_dns_enable || stale_dns_enable || host_resolver_rules_enable) { + dict->GetBoolean(kDisableIPv6, &disable_ipv6); + + if (async_dns_enable || stale_dns_enable || host_resolver_rules_enable || + disable_ipv6) { if (net_log == nullptr) { - CHECK(false) << "AsyncDNS, StaleDNS, and HostResolverRules experiments " - << "require NetLog."; + CHECK(false) << "All DNS-related experiments require NetLog."; } std::unique_ptr<net::HostResolver> host_resolver; if (stale_dns_enable) { + DCHECK(!disable_ipv6); host_resolver.reset(new StaleHostResolver( net::HostResolver::CreateDefaultResolverImpl(net_log), stale_dns_options)); } else { host_resolver = net::HostResolver::CreateDefaultResolver(net_log); } + if (disable_ipv6) + host_resolver->SetDefaultAddressFamily(net::ADDRESS_FAMILY_IPV4); if (async_dns_enable) host_resolver->SetDnsClientEnabled(true); if (host_resolver_rules_enable) {
diff --git a/components/cronet/url_request_context_config_unittest.cc b/components/cronet/url_request_context_config_unittest.cc index b072527..30c9feea 100644 --- a/components/cronet/url_request_context_config_unittest.cc +++ b/components/cronet/url_request_context_config_unittest.cc
@@ -51,7 +51,9 @@ "\"connection_options\":\"TIME,TBBR,REJ\"}," "\"AsyncDNS\":{\"enable\":true}," "\"HostResolverRules\":{\"host_resolver_rules\":" - "\"MAP * 127.0.0.1\"}}", + "\"MAP * 127.0.0.1\"}," + // See http://crbug.com/696569. + "\"disable_ipv6\":true}", // Data reduction proxy key. "", // Data reduction proxy. @@ -111,6 +113,10 @@ // Check AsyncDNS resolver is enabled. EXPECT_TRUE(context->host_resolver()->GetDnsConfigAsValue()); + // Check IPv6 is disabled. + EXPECT_EQ(net::ADDRESS_FAMILY_IPV4, + context->host_resolver()->GetDefaultAddressFamily()); + net::HostResolver::RequestInfo info(net::HostPortPair("abcde", 80)); net::AddressList addresses; EXPECT_EQ(net::OK, context->host_resolver()->ResolveFromCache(
diff --git a/components/display_compositor/gpu_compositor_frame_sink.cc b/components/display_compositor/gpu_compositor_frame_sink.cc index ce450b6..19e66aca 100644 --- a/components/display_compositor/gpu_compositor_frame_sink.cc +++ b/components/display_compositor/gpu_compositor_frame_sink.cc
@@ -52,15 +52,6 @@ support_->SubmitCompositorFrame(local_surface_id, std::move(frame)); } -void GpuCompositorFrameSink::Require(const cc::LocalSurfaceId& local_surface_id, - const cc::SurfaceSequence& sequence) { - support_->Require(local_surface_id, sequence); -} - -void GpuCompositorFrameSink::Satisfy(const cc::SurfaceSequence& sequence) { - support_->Satisfy(sequence); -} - void GpuCompositorFrameSink::DidReceiveCompositorFrameAck() { if (client_) client_->DidReceiveCompositorFrameAck();
diff --git a/components/display_compositor/gpu_compositor_frame_sink.h b/components/display_compositor/gpu_compositor_frame_sink.h index b500825..f1ebaf5 100644 --- a/components/display_compositor/gpu_compositor_frame_sink.h +++ b/components/display_compositor/gpu_compositor_frame_sink.h
@@ -41,9 +41,6 @@ void SetNeedsBeginFrame(bool needs_begin_frame) override; void SubmitCompositorFrame(const cc::LocalSurfaceId& local_surface_id, cc::CompositorFrame frame) override; - void Require(const cc::LocalSurfaceId& local_surface_id, - const cc::SurfaceSequence& sequence) override; - void Satisfy(const cc::SurfaceSequence& sequence) override; // cc::mojom::MojoCompositorFrameSinkPrivate: void RequestCopyOfSurface(
diff --git a/components/display_compositor/gpu_root_compositor_frame_sink.cc b/components/display_compositor/gpu_root_compositor_frame_sink.cc index 76180bab..ea6d2df 100644 --- a/components/display_compositor/gpu_root_compositor_frame_sink.cc +++ b/components/display_compositor/gpu_root_compositor_frame_sink.cc
@@ -89,16 +89,6 @@ support_->SubmitCompositorFrame(local_surface_id, std::move(frame)); } -void GpuRootCompositorFrameSink::Require( - const cc::LocalSurfaceId& local_surface_id, - const cc::SurfaceSequence& sequence) { - support_->Require(local_surface_id, sequence); -} - -void GpuRootCompositorFrameSink::Satisfy(const cc::SurfaceSequence& sequence) { - support_->Satisfy(sequence); -} - void GpuRootCompositorFrameSink::RequestCopyOfSurface( std::unique_ptr<cc::CopyOutputRequest> request) { support_->RequestCopyOfSurface(std::move(request));
diff --git a/components/display_compositor/gpu_root_compositor_frame_sink.h b/components/display_compositor/gpu_root_compositor_frame_sink.h index e9b41df..f502a63 100644 --- a/components/display_compositor/gpu_root_compositor_frame_sink.h +++ b/components/display_compositor/gpu_root_compositor_frame_sink.h
@@ -59,9 +59,6 @@ void SetNeedsBeginFrame(bool needs_begin_frame) override; void SubmitCompositorFrame(const cc::LocalSurfaceId& local_surface_id, cc::CompositorFrame frame) override; - void Require(const cc::LocalSurfaceId& local_surface_id, - const cc::SurfaceSequence& sequence) override; - void Satisfy(const cc::SurfaceSequence& sequence) override; // cc::mojom::MojoCompositorFrameSinkPrivate: void RequestCopyOfSurface(
diff --git a/components/exo/compositor_frame_sink.cc b/components/exo/compositor_frame_sink.cc index 0c197afc..895d513 100644 --- a/components/exo/compositor_frame_sink.cc +++ b/components/exo/compositor_frame_sink.cc
@@ -45,15 +45,6 @@ support_.EvictFrame(); } -void CompositorFrameSink::Require(const cc::LocalSurfaceId& local_surface_id, - const cc::SurfaceSequence& sequence) { - support_.Require(local_surface_id, sequence); -} - -void CompositorFrameSink::Satisfy(const cc::SurfaceSequence& sequence) { - support_.Satisfy(sequence); -} - //////////////////////////////////////////////////////////////////////////////// // cc::CompositorFrameSinkSupportClient overrides:
diff --git a/components/exo/compositor_frame_sink.h b/components/exo/compositor_frame_sink.h index c269004..3bfc7dd 100644 --- a/components/exo/compositor_frame_sink.h +++ b/components/exo/compositor_frame_sink.h
@@ -30,9 +30,6 @@ void SubmitCompositorFrame(const cc::LocalSurfaceId& local_surface_id, cc::CompositorFrame frame) override; void EvictFrame() override; - void Require(const cc::LocalSurfaceId& local_surface_id, - const cc::SurfaceSequence& sequence) override; - void Satisfy(const cc::SurfaceSequence& sequence) override; // Overridden from cc::CompositorFrameSinkSupportClient: void DidReceiveCompositorFrameAck() override;
diff --git a/components/exo/compositor_frame_sink_holder.cc b/components/exo/compositor_frame_sink_holder.cc index 11e0b5e..3d55a7d 100644 --- a/components/exo/compositor_frame_sink_holder.cc +++ b/components/exo/compositor_frame_sink_holder.cc
@@ -41,15 +41,6 @@ OnNeedsBeginFrames(needs_begin_frame); } -void CompositorFrameSinkHolder::Satisfy(const cc::SurfaceSequence& sequence) { - frame_sink_->Satisfy(sequence); -} - -void CompositorFrameSinkHolder::Require(const cc::SurfaceId& id, - const cc::SurfaceSequence& sequence) { - frame_sink_->Require(id.local_surface_id(), sequence); -} - //////////////////////////////////////////////////////////////////////////////// // cc::mojom::MojoCompositorFrameSinkClient overrides:
diff --git a/components/exo/compositor_frame_sink_holder.h b/components/exo/compositor_frame_sink_holder.h index 08c94f7..4f3b0a2 100644 --- a/components/exo/compositor_frame_sink_holder.h +++ b/components/exo/compositor_frame_sink_holder.h
@@ -47,9 +47,6 @@ void SetNeedsBeginFrame(bool needs_begin_frame); - void Satisfy(const cc::SurfaceSequence& sequence); - void Require(const cc::SurfaceId& id, const cc::SurfaceSequence& sequence); - // Overridden from cc::mojom::MojoCompositorFrameSinkClient: void DidReceiveCompositorFrameAck() override; void OnBeginFrame(const cc::BeginFrameArgs& args) override;
diff --git a/components/exo/surface.cc b/components/exo/surface.cc index 85cca33..a1b92daf 100644 --- a/components/exo/surface.cc +++ b/components/exo/surface.cc
@@ -467,7 +467,7 @@ window_->layer()->SetBounds( gfx::Rect(window_->layer()->bounds().origin(), content_size_)); cc::SurfaceId surface_id(frame_sink_id_, local_surface_id_); - window_->layer()->SetShowSurface( + window_->layer()->SetShowPrimarySurface( cc::SurfaceInfo(surface_id, contents_surface_to_layer_scale, content_size_), surface_reference_factory_);
diff --git a/components/favicon/content/content_favicon_driver_unittest.cc b/components/favicon/content/content_favicon_driver_unittest.cc index 0c75c52a..d1ede99 100644 --- a/components/favicon/content/content_favicon_driver_unittest.cc +++ b/components/favicon/content/content_favicon_driver_unittest.cc
@@ -8,11 +8,14 @@ #include <vector> #include "base/macros.h" +#include "base/run_loop.h" #include "components/favicon/core/favicon_client.h" +#include "components/favicon/core/favicon_handler.h" #include "components/favicon/core/test/mock_favicon_service.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/common/favicon_url.h" #include "content/public/test/test_renderer_host.h" +#include "content/public/test/web_contents_tester.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -21,12 +24,23 @@ namespace favicon { namespace { -using testing::Mock; using testing::Return; +using testing::_; class ContentFaviconDriverTest : public content::RenderViewHostTestHarness { protected: - ContentFaviconDriverTest() {} + const std::vector<gfx::Size> kEmptyIconSizes; + const std::vector<SkBitmap> kEmptyIcons; + const std::vector<favicon_base::FaviconRawBitmapResult> kEmptyRawBitmapResult; + const GURL kPageURL = GURL("http://www.google.com/"); + const GURL kIconURL = GURL("http://www.google.com/favicon.ico"); + + ContentFaviconDriverTest() { + ON_CALL(favicon_service_, UpdateFaviconMappingsAndFetch(_, _, _, _, _, _)) + .WillByDefault(PostReply<6>(kEmptyRawBitmapResult)); + ON_CALL(favicon_service_, GetFaviconForPageURL(_, _, _, _, _)) + .WillByDefault(PostReply<5>(kEmptyRawBitmapResult)); + } ~ContentFaviconDriverTest() override {} @@ -38,53 +52,80 @@ web_contents(), &favicon_service_, nullptr, nullptr); } - testing::StrictMock<MockFaviconService> favicon_service_; + content::WebContentsTester* web_contents_tester() { + return content::WebContentsTester::For(web_contents()); + } + + void TestFetchFaviconForPage( + const GURL& page_url, + const std::vector<content::FaviconURL>& candidates) { + ContentFaviconDriver* favicon_driver = + ContentFaviconDriver::FromWebContents(web_contents()); + web_contents_tester()->NavigateAndCommit(page_url); + static_cast<content::WebContentsObserver*>(favicon_driver) + ->DidUpdateFaviconURL(candidates); + base::RunLoop().RunUntilIdle(); + } + + testing::NiceMock<MockFaviconService> favicon_service_; }; +// Test that UnableToDownloadFavicon() is not called as a result of a favicon +// download with 200 status. +TEST_F(ContentFaviconDriverTest, ShouldNotCallUnableToDownloadFaviconFor200) { + EXPECT_CALL(favicon_service_, UnableToDownloadFavicon(kIconURL)).Times(0); + // Mimic a page load. + TestFetchFaviconForPage( + kPageURL, + {content::FaviconURL(kIconURL, content::FaviconURL::FAVICON, + kEmptyIconSizes)}); + // Completing the download should not cause a call to + // UnableToDownloadFavicon(). + EXPECT_TRUE(web_contents_tester()->TestDidDownloadImage( + kIconURL, 200, kEmptyIcons, kEmptyIconSizes)); +} + +// Test that UnableToDownloadFavicon() is called as a result of a favicon +// download with 404 status. +TEST_F(ContentFaviconDriverTest, ShouldCallUnableToDownloadFaviconFor404) { + EXPECT_CALL(favicon_service_, UnableToDownloadFavicon(kIconURL)); + // Mimic a page load. + TestFetchFaviconForPage( + kPageURL, + {content::FaviconURL(kIconURL, content::FaviconURL::FAVICON, + kEmptyIconSizes)}); + // Mimic the completion of an image download. + EXPECT_TRUE(web_contents_tester()->TestDidDownloadImage( + kIconURL, 404, kEmptyIcons, kEmptyIconSizes)); +} + +// Test that UnableToDownloadFavicon() is not called as a result of a favicon +// download with 503 status. +TEST_F(ContentFaviconDriverTest, ShouldNotCallUnableToDownloadFaviconFor503) { + EXPECT_CALL(favicon_service_, UnableToDownloadFavicon(kIconURL)).Times(0); + // Mimic a page load. + TestFetchFaviconForPage( + kPageURL, + {content::FaviconURL(kIconURL, content::FaviconURL::FAVICON, + kEmptyIconSizes)}); + // Completing the download should not cause a call to + // UnableToDownloadFavicon(). + EXPECT_TRUE(web_contents_tester()->TestDidDownloadImage( + kIconURL, 503, kEmptyIcons, kEmptyIconSizes)); +} + // Test that Favicon is not requested repeatedly during the same session if -// server returns HTTP 404 status. -TEST_F(ContentFaviconDriverTest, UnableToDownloadFavicon) { - const GURL missing_icon_url("http://www.google.com/favicon.ico"); - - ContentFaviconDriver* content_favicon_driver = - ContentFaviconDriver::FromWebContents(web_contents()); - - std::vector<SkBitmap> empty_icons; - std::vector<gfx::Size> empty_icon_sizes; - int download_id = 0; - - // Try to download missing icon. - EXPECT_CALL(favicon_service_, WasUnableToDownloadFavicon(missing_icon_url)) - .WillOnce(Return(false)); - download_id = content_favicon_driver->StartDownload(missing_icon_url, 0); - EXPECT_NE(0, download_id); - - // Report download failure with HTTP 503 status. - content_favicon_driver->DidDownloadFavicon(download_id, 503, missing_icon_url, - empty_icons, empty_icon_sizes); - Mock::VerifyAndClearExpectations(&favicon_service_); - - // Try to download again. - EXPECT_CALL(favicon_service_, WasUnableToDownloadFavicon(missing_icon_url)) - .WillOnce(Return(false)); - download_id = content_favicon_driver->StartDownload(missing_icon_url, 0); - EXPECT_NE(0, download_id); - Mock::VerifyAndClearExpectations(&favicon_service_); - - // Report download failure with HTTP 404 status, which causes the icon to be - // marked as UnableToDownload. - EXPECT_CALL(favicon_service_, UnableToDownloadFavicon(missing_icon_url)); - content_favicon_driver->DidDownloadFavicon(download_id, 404, missing_icon_url, - empty_icons, empty_icon_sizes); - Mock::VerifyAndClearExpectations(&favicon_service_); - - // Try to download again. - EXPECT_CALL(favicon_service_, WasUnableToDownloadFavicon(missing_icon_url)) - .WillOnce(Return(true)); - download_id = content_favicon_driver->StartDownload(missing_icon_url, 0); - // Download is not started and Icon is still marked as UnableToDownload. - EXPECT_EQ(0, download_id); - Mock::VerifyAndClearExpectations(&favicon_service_); +// the favicon is known to be unavailable (e.g. due to HTTP 404 status). +TEST_F(ContentFaviconDriverTest, ShouldNotRequestRepeatedlyIfUnavailable) { + ON_CALL(favicon_service_, WasUnableToDownloadFavicon(kIconURL)) + .WillByDefault(Return(true)); + // Mimic a page load. + TestFetchFaviconForPage( + kPageURL, + {content::FaviconURL(kIconURL, content::FaviconURL::FAVICON, + kEmptyIconSizes)}); + // Verify that no download request is pending for the image. + EXPECT_FALSE(web_contents_tester()->HasPendingDownloadImage(kIconURL)); } // Test that ContentFaviconDriver ignores updated favicon URLs if there is no
diff --git a/components/network_time/OWNERS b/components/network_time/OWNERS index 03d8b24..5f6b84aa 100644 --- a/components/network_time/OWNERS +++ b/components/network_time/OWNERS
@@ -1,2 +1,4 @@ zea@chromium.org estark@chromium.org + +# COMPONENT: UI>Browser>Interstitials
diff --git a/components/ntp_snippets/BUILD.gn b/components/ntp_snippets/BUILD.gn index 3bc38a3..335ed6db 100644 --- a/components/ntp_snippets/BUILD.gn +++ b/components/ntp_snippets/BUILD.gn
@@ -103,6 +103,8 @@ "//components/metrics", "//components/ntp_snippets/remote/proto", "//components/offline_pages/core", + "//components/offline_pages/core/downloads:offline_pages_ui_adapter", + "//components/offline_pages/core/recent_tabs", "//components/physical_web/data_source", "//components/sessions", "//components/strings", @@ -163,6 +165,9 @@ "//components/ntp_snippets/remote/proto", "//components/offline_pages/core", "//components/offline_pages/core:test_support", + "//components/offline_pages/core/background:test_support", + "//components/offline_pages/core/downloads:offline_pages_ui_adapter", + "//components/offline_pages/core/recent_tabs", "//components/physical_web/data_source:test_support", "//components/sessions", "//components/sessions:test_support",
diff --git a/components/ntp_snippets/offline_pages/offline_pages_test_utils.cc b/components/ntp_snippets/offline_pages/offline_pages_test_utils.cc index 81d84f73..22a406a 100644 --- a/components/ntp_snippets/offline_pages/offline_pages_test_utils.cc +++ b/components/ntp_snippets/offline_pages/offline_pages_test_utils.cc
@@ -59,16 +59,22 @@ is_loaded_ = value; } -OfflinePageItem CreateDummyOfflinePageItem(int id, - const std::string& name_space) { +OfflinePageItem CreateDummyOfflinePageItem( + int id, + const offline_pages::ClientId& client_id) { std::string id_string = base::IntToString(id); return OfflinePageItem( - GURL("http://dummy.com/" + id_string), id, - ClientId(name_space, base::GenerateGUID()), + GURL("http://dummy.com/" + id_string), id, client_id, base::FilePath::FromUTF8Unsafe("some/folder/test" + id_string + ".mhtml"), 0, base::Time::Now()); } +OfflinePageItem CreateDummyOfflinePageItem(int id, + const std::string& name_space) { + return CreateDummyOfflinePageItem(id, + ClientId(name_space, base::GenerateGUID())); +} + void CaptureDismissedSuggestions( std::vector<ContentSuggestion>* captured_suggestions, std::vector<ContentSuggestion> dismissed_suggestions) {
diff --git a/components/ntp_snippets/offline_pages/offline_pages_test_utils.h b/components/ntp_snippets/offline_pages/offline_pages_test_utils.h index caa4f9c..61147b7 100644 --- a/components/ntp_snippets/offline_pages/offline_pages_test_utils.h +++ b/components/ntp_snippets/offline_pages/offline_pages_test_utils.h
@@ -39,6 +39,10 @@ offline_pages::OfflinePageItem CreateDummyOfflinePageItem( int id, + const offline_pages::ClientId& client_id); + +offline_pages::OfflinePageItem CreateDummyOfflinePageItem( + int id, const std::string& name_space); void CaptureDismissedSuggestions(
diff --git a/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.cc b/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.cc index ac5b0d3..1145b091 100644 --- a/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.cc +++ b/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.cc
@@ -16,8 +16,7 @@ #include "components/ntp_snippets/pref_names.h" #include "components/ntp_snippets/pref_util.h" #include "components/offline_pages/core/client_policy_controller.h" -#include "components/offline_pages/core/offline_page_item.h" -#include "components/offline_pages/core/offline_page_model_query.h" +#include "components/offline_pages/core/recent_tabs/recent_tabs_ui_adapter_delegate.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "components/strings/grit/components_strings.h" @@ -26,9 +25,8 @@ #include "ui/gfx/image/image.h" using offline_pages::ClientId; -using offline_pages::OfflinePageItem; -using offline_pages::OfflinePageModelQuery; -using offline_pages::OfflinePageModelQueryBuilder; +using offline_pages::DownloadUIAdapter; +using offline_pages::DownloadUIItem; namespace ntp_snippets { @@ -44,57 +42,42 @@ kDefaultMaxSuggestionsCount); } -struct OrderOfflinePagesByMostRecentlyCreatedFirst { - bool operator()(const OfflinePageItem* left, - const OfflinePageItem* right) const { - return left->creation_time > right->creation_time; +struct OrderUIItemsByMostRecentlyCreatedFirst { + bool operator()(const DownloadUIItem* left, + const DownloadUIItem* right) const { + return left->start_time > right->start_time; } }; -struct OrderOfflinePagesByUrlAndThenMostRecentlyCreatedFirst { - bool operator()(const OfflinePageItem* left, - const OfflinePageItem* right) const { +struct OrderUIItemsByUrlAndThenMostRecentlyCreatedFirst { + bool operator()(const DownloadUIItem* left, + const DownloadUIItem* right) const { if (left->url != right->url) { return left->url < right->url; } - return left->creation_time > right->creation_time; + return left->start_time > right->start_time; } }; -std::unique_ptr<OfflinePageModelQuery> BuildRecentTabsQuery( - offline_pages::OfflinePageModel* model) { - OfflinePageModelQueryBuilder builder; - builder.RequireShownAsRecentlyVisitedSite( - OfflinePageModelQuery::Requirement::INCLUDE_MATCHING); - return builder.Build(model->GetPolicyController()); -} - -bool IsRecentTab(offline_pages::ClientPolicyController* policy_controller, - const OfflinePageItem& offline_page) { - return policy_controller->IsShownAsRecentlyVisitedSite( - offline_page.client_id.name_space); -} - } // namespace RecentTabSuggestionsProvider::RecentTabSuggestionsProvider( ContentSuggestionsProvider::Observer* observer, - offline_pages::OfflinePageModel* offline_page_model, + offline_pages::DownloadUIAdapter* ui_adapter, PrefService* pref_service) : ContentSuggestionsProvider(observer), category_status_(CategoryStatus::AVAILABLE_LOADING), provided_category_( Category::FromKnownCategory(KnownCategories::RECENT_TABS)), - offline_page_model_(offline_page_model), + recent_tabs_ui_adapter_(ui_adapter), pref_service_(pref_service), weak_ptr_factory_(this) { observer->OnCategoryStatusChanged(this, provided_category_, category_status_); - offline_page_model_->AddObserver(this); - FetchRecentTabs(); + recent_tabs_ui_adapter_->AddObserver(this); } RecentTabSuggestionsProvider::~RecentTabSuggestionsProvider() { - offline_page_model_->RemoveObserver(this); + recent_tabs_ui_adapter_->RemoveObserver(this); } CategoryStatus RecentTabSuggestionsProvider::GetCategoryStatus( @@ -165,15 +148,21 @@ const DismissedSuggestionsCallback& callback) { DCHECK_EQ(provided_category_, category); - // Offline pages which are not related to recent tabs are also queried here, - // so that they can be returned if they happen to be dismissed (e.g. due to a - // bug). - OfflinePageModelQueryBuilder query_builder; - offline_page_model_->GetPagesMatchingQuery( - query_builder.Build(offline_page_model_->GetPolicyController()), - base::Bind(&RecentTabSuggestionsProvider:: - GetPagesMatchingQueryCallbackForGetDismissedSuggestions, - weak_ptr_factory_.GetWeakPtr(), callback)); + std::vector<const DownloadUIItem*> items = + recent_tabs_ui_adapter_->GetAllItems(); + + std::set<std::string> dismissed_ids = ReadDismissedIDsFromPrefs(); + std::vector<ContentSuggestion> suggestions; + for (const DownloadUIItem* item : items) { + int64_t offline_page_id = + recent_tabs_ui_adapter_->GetOfflineIdByGuid(item->guid); + if (!dismissed_ids.count(base::IntToString(offline_page_id))) { + continue; + } + + suggestions.push_back(ConvertUIItem(*item)); + } + callback.Run(std::move(suggestions)); } void RecentTabSuggestionsProvider::ClearDismissedSuggestionsForDebugging( @@ -192,77 +181,54 @@ //////////////////////////////////////////////////////////////////////////////// // Private methods -void RecentTabSuggestionsProvider:: - GetPagesMatchingQueryCallbackForGetDismissedSuggestions( - const DismissedSuggestionsCallback& callback, - const std::vector<OfflinePageItem>& offline_pages) const { - std::set<std::string> dismissed_ids = ReadDismissedIDsFromPrefs(); - std::vector<ContentSuggestion> suggestions; - for (const OfflinePageItem& item : offline_pages) { - if (!dismissed_ids.count(base::IntToString(item.offline_id))) { - continue; - } - - suggestions.push_back(ConvertOfflinePage(item)); - } - callback.Run(std::move(suggestions)); +void RecentTabSuggestionsProvider::ItemsLoaded() { + FetchRecentTabs(); } -void RecentTabSuggestionsProvider::OfflinePageModelLoaded( - offline_pages::OfflinePageModel* model) {} +void RecentTabSuggestionsProvider::ItemAdded(const DownloadUIItem& ui_item) { + FetchRecentTabs(); +} -void RecentTabSuggestionsProvider::OfflinePageAdded( - offline_pages::OfflinePageModel* model, - const offline_pages::OfflinePageItem& added_page) { - DCHECK_EQ(offline_page_model_, model); - if (IsRecentTab(model->GetPolicyController(), added_page)) { - FetchRecentTabs(); +void RecentTabSuggestionsProvider::ItemUpdated(const DownloadUIItem& ui_item) { + FetchRecentTabs(); +} + +void RecentTabSuggestionsProvider::ItemDeleted( + const std::string& ui_item_guid) { + // Because we never switch to NOT_PROVIDED dynamically, there can be no open + // UI containing an invalidated suggestion unless the status is something + // other than NOT_PROVIDED, so only notify invalidation in that case. + if (category_status_ != CategoryStatus::NOT_PROVIDED) { + InvalidateSuggestion(ui_item_guid); } } -void RecentTabSuggestionsProvider:: - GetPagesMatchingQueryCallbackForFetchRecentTabs( - const std::vector<OfflinePageItem>& offline_pages) { +void RecentTabSuggestionsProvider::FetchRecentTabs() { + std::vector<const DownloadUIItem*> ui_items = + recent_tabs_ui_adapter_->GetAllItems(); NotifyStatusChanged(CategoryStatus::AVAILABLE); std::set<std::string> old_dismissed_ids = ReadDismissedIDsFromPrefs(); std::set<std::string> new_dismissed_ids; - std::vector<const OfflinePageItem*> recent_tab_items; - for (const OfflinePageItem& item : offline_pages) { - std::string offline_page_id = base::IntToString(item.offline_id); + std::vector<const DownloadUIItem*> non_dismissed_items; + + for (const DownloadUIItem* item : ui_items) { + std::string offline_page_id = base::IntToString( + recent_tabs_ui_adapter_->GetOfflineIdByGuid(item->guid)); if (old_dismissed_ids.count(offline_page_id)) { new_dismissed_ids.insert(offline_page_id); } else { - recent_tab_items.push_back(&item); + non_dismissed_items.push_back(item); } } observer()->OnNewSuggestions( this, provided_category_, - GetMostRecentlyCreatedWithoutDuplicates(std::move(recent_tab_items))); + GetMostRecentlyCreatedWithoutDuplicates(std::move(non_dismissed_items))); if (new_dismissed_ids.size() != old_dismissed_ids.size()) { StoreDismissedIDsToPrefs(new_dismissed_ids); } } -void RecentTabSuggestionsProvider::OfflinePageDeleted( - int64_t offline_id, - const ClientId& client_id) { - // Because we never switch to NOT_PROVIDED dynamically, there can be no open - // UI containing an invalidated suggestion unless the status is something - // other than NOT_PROVIDED, so only notify invalidation in that case. - if (category_status_ != CategoryStatus::NOT_PROVIDED) { - InvalidateSuggestion(offline_id); - } -} - -void RecentTabSuggestionsProvider::FetchRecentTabs() { - offline_page_model_->GetPagesMatchingQuery( - BuildRecentTabsQuery(offline_page_model_), - base::Bind(&RecentTabSuggestionsProvider:: - GetPagesMatchingQueryCallbackForFetchRecentTabs, - weak_ptr_factory_.GetWeakPtr())); -} - void RecentTabSuggestionsProvider::NotifyStatusChanged( CategoryStatus new_status) { DCHECK_NE(CategoryStatus::NOT_PROVIDED, category_status_); @@ -273,51 +239,49 @@ observer()->OnCategoryStatusChanged(this, provided_category_, new_status); } -ContentSuggestion RecentTabSuggestionsProvider::ConvertOfflinePage( - const OfflinePageItem& offline_page) const { +ContentSuggestion RecentTabSuggestionsProvider::ConvertUIItem( + const DownloadUIItem& ui_item) const { + // UI items have the Tab ID embedded in the GUID and the offline ID is + // available by querying. + // // TODO(vitaliii): Make sure the URL is opened in the existing tab. + int64_t offline_page_id = + recent_tabs_ui_adapter_->GetOfflineIdByGuid(ui_item.guid); ContentSuggestion suggestion(provided_category_, - base::IntToString(offline_page.offline_id), - offline_page.url); - - if (offline_page.title.empty()) { - // TODO(vitaliii): Remove this fallback once the OfflinePageModel provides - // titles for all (relevant) OfflinePageItems. - suggestion.set_title(base::UTF8ToUTF16(offline_page.url.spec())); - } else { - suggestion.set_title(offline_page.title); - } - suggestion.set_publish_date(offline_page.creation_time); - suggestion.set_publisher_name(base::UTF8ToUTF16(offline_page.url.host())); + base::IntToString(offline_page_id), ui_item.url); + suggestion.set_title(ui_item.title); + suggestion.set_publish_date(ui_item.start_time); + suggestion.set_publisher_name(base::UTF8ToUTF16(ui_item.url.host())); auto extra = base::MakeUnique<RecentTabSuggestionExtra>(); int tab_id; - bool success = base::StringToInt(offline_page.client_id.id, &tab_id); + bool success = base::StringToInt(ui_item.guid, &tab_id); DCHECK(success); extra->tab_id = tab_id; - extra->offline_page_id = offline_page.offline_id; + extra->offline_page_id = offline_page_id; suggestion.set_recent_tab_suggestion_extra(std::move(extra)); + return suggestion; } std::vector<ContentSuggestion> RecentTabSuggestionsProvider::GetMostRecentlyCreatedWithoutDuplicates( - std::vector<const OfflinePageItem*> offline_page_items) const { + std::vector<const DownloadUIItem*> ui_items) const { // |std::unique| only removes duplicates that immediately follow each other. // Thus, first, we have to sort by URL and creation time and only then remove // duplicates and sort the remaining items by creation time. - std::sort(offline_page_items.begin(), offline_page_items.end(), - OrderOfflinePagesByUrlAndThenMostRecentlyCreatedFirst()); - std::vector<const OfflinePageItem*>::iterator new_end = std::unique( - offline_page_items.begin(), offline_page_items.end(), - [](const OfflinePageItem* left, const OfflinePageItem* right) { - return left->url == right->url; - }); - offline_page_items.erase(new_end, offline_page_items.end()); - std::sort(offline_page_items.begin(), offline_page_items.end(), - OrderOfflinePagesByMostRecentlyCreatedFirst()); + std::sort(ui_items.begin(), ui_items.end(), + OrderUIItemsByUrlAndThenMostRecentlyCreatedFirst()); + std::vector<const DownloadUIItem*>::iterator new_end = + std::unique(ui_items.begin(), ui_items.end(), + [](const DownloadUIItem* left, const DownloadUIItem* right) { + return left->url == right->url; + }); + ui_items.erase(new_end, ui_items.end()); + std::sort(ui_items.begin(), ui_items.end(), + OrderUIItemsByMostRecentlyCreatedFirst()); std::vector<ContentSuggestion> suggestions; - for (const OfflinePageItem* offline_page_item : offline_page_items) { - suggestions.push_back(ConvertOfflinePage(*offline_page_item)); + for (const DownloadUIItem* ui_item : ui_items) { + suggestions.push_back(ConvertUIItem(*ui_item)); if (static_cast<int>(suggestions.size()) == GetMaxSuggestionsCount()) { break; } @@ -325,8 +289,10 @@ return suggestions; } -void RecentTabSuggestionsProvider::InvalidateSuggestion(int64_t offline_id) { - std::string offline_page_id = base::IntToString(offline_id); +void RecentTabSuggestionsProvider::InvalidateSuggestion( + const std::string& ui_item_guid) { + std::string offline_page_id = base::IntToString( + recent_tabs_ui_adapter_->GetOfflineIdByGuid(ui_item_guid)); observer()->OnSuggestionInvalidated( this, ContentSuggestion::ID(provided_category_, offline_page_id));
diff --git a/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.h b/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.h index 6a4e71dc..52da1cc7 100644 --- a/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.h +++ b/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.h
@@ -17,6 +17,8 @@ #include "components/ntp_snippets/category_status.h" #include "components/ntp_snippets/content_suggestion.h" #include "components/ntp_snippets/content_suggestions_provider.h" +#include "components/offline_pages/core/downloads/download_ui_adapter.h" +#include "components/offline_pages/core/downloads/download_ui_item.h" #include "components/offline_pages/core/offline_page_model.h" class PrefRegistrySimple; @@ -27,12 +29,11 @@ // Provides recent tabs content suggestions from the offline pages model. class RecentTabSuggestionsProvider : public ContentSuggestionsProvider, - public offline_pages::OfflinePageModel::Observer { + public offline_pages::DownloadUIAdapter::Observer { public: - RecentTabSuggestionsProvider( - ContentSuggestionsProvider::Observer* observer, - offline_pages::OfflinePageModel* offline_page_model, - PrefService* pref_service); + RecentTabSuggestionsProvider(ContentSuggestionsProvider::Observer* observer, + offline_pages::DownloadUIAdapter* ui_adapter, + PrefService* pref_service); ~RecentTabSuggestionsProvider() override; // ContentSuggestionsProvider implementation. @@ -57,47 +58,37 @@ static void RegisterProfilePrefs(PrefRegistrySimple* registry); private: - friend class RecentTabSuggestionsProviderTest; + friend class RecentTabSuggestionsProviderTestNoLoad; - void GetPagesMatchingQueryCallbackForGetDismissedSuggestions( - const DismissedSuggestionsCallback& callback, - const std::vector<offline_pages::OfflinePageItem>& offline_pages) const; - - // OfflinePageModel::Observer implementation. - void OfflinePageModelLoaded(offline_pages::OfflinePageModel* model) override; - void OfflinePageAdded( - offline_pages::OfflinePageModel* model, - const offline_pages::OfflinePageItem& added_page) override; - void OfflinePageDeleted(int64_t offline_id, - const offline_pages::ClientId& client_id) override; - - void GetPagesMatchingQueryCallbackForFetchRecentTabs( - const std::vector<offline_pages::OfflinePageItem>& offline_pages); + // DownloadUIAdapter::Observer implementation. + void ItemsLoaded() override; + void ItemAdded(const offline_pages::DownloadUIItem& ui_item) override; + void ItemUpdated(const offline_pages::DownloadUIItem& ui_item) override; + void ItemDeleted(const std::string& ui_item_guid) override; // Updates the |category_status_| of the |provided_category_| and notifies the // |observer_|, if necessary. void NotifyStatusChanged(CategoryStatus new_status); - // Manually requests all offline pages and updates the suggestions. + // Manually requests all Recent Tabs UI items and updates the suggestions. void FetchRecentTabs(); - // Converts an OfflinePageItem to a ContentSuggestion for the + // Converts an DownloadUIItem to a ContentSuggestion for the // |provided_category_|. - ContentSuggestion ConvertOfflinePage( - const offline_pages::OfflinePageItem& offline_page) const; + ContentSuggestion ConvertUIItem( + const offline_pages::DownloadUIItem& ui_item) const; // Removes duplicates for the same URL leaving only the most recently created // items, returns at most |GetMaxSuggestionsCount()| ContentSuggestions // corresponding to the remaining items, sorted by creation time (newer // first). std::vector<ContentSuggestion> GetMostRecentlyCreatedWithoutDuplicates( - std::vector<const offline_pages::OfflinePageItem*> offline_page_items) - const; + std::vector<const offline_pages::DownloadUIItem*> ui_items) const; // Fires the |OnSuggestionInvalidated| event for the suggestion corresponding // to the given |offline_id| and deletes it from the dismissed IDs list, if // necessary. - void InvalidateSuggestion(int64_t offline_id); + void InvalidateSuggestion(const std::string& ui_item_guid); // Reads dismissed IDs from Prefs. std::set<std::string> ReadDismissedIDsFromPrefs() const; @@ -107,7 +98,7 @@ CategoryStatus category_status_; const Category provided_category_; - offline_pages::OfflinePageModel* offline_page_model_; + offline_pages::DownloadUIAdapter* recent_tabs_ui_adapter_; PrefService* pref_service_;
diff --git a/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider_unittest.cc b/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider_unittest.cc index bc3d6d2..0fecc96 100644 --- a/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider_unittest.cc +++ b/components/ntp_snippets/offline_pages/recent_tab_suggestions_provider_unittest.cc
@@ -10,14 +10,18 @@ #include "base/bind.h" #include "base/files/file_path.h" #include "base/strings/string_number_conversions.h" +#include "base/test/test_simple_task_runner.h" +#include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "components/ntp_snippets/category.h" #include "components/ntp_snippets/content_suggestions_provider.h" #include "components/ntp_snippets/mock_content_suggestions_provider_observer.h" #include "components/ntp_snippets/offline_pages/offline_pages_test_utils.h" +#include "components/offline_pages/core/background/request_coordinator_stub_taco.h" #include "components/offline_pages/core/client_namespace_constants.h" +#include "components/offline_pages/core/downloads/download_ui_adapter.h" #include "components/offline_pages/core/offline_page_item.h" -#include "components/offline_pages/core/stub_offline_page_model.h" +#include "components/offline_pages/core/recent_tabs/recent_tabs_ui_adapter_delegate.h" #include "components/prefs/testing_pref_service.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -27,7 +31,6 @@ using offline_pages::ClientId; using offline_pages::MultipleOfflinePageItemCallback; using offline_pages::OfflinePageItem; -using offline_pages::StubOfflinePageModel; using testing::_; using testing::IsEmpty; using testing::Mock; @@ -38,11 +41,14 @@ namespace { -OfflinePageItem CreateDummyRecentTab(int id) { - OfflinePageItem item = - test::CreateDummyOfflinePageItem(id, offline_pages::kLastNNamespace); - item.client_id.id = base::IntToString(id); - return item; +OfflinePageItem CreateDummyRecentTab(int offline_id) { + // This is used to assign unique tab IDs to pages. Since offline IDs are + // typically small integers like 1, 2, 3 etc, we start at 1001 to ensure that + // they are different, and can catch bugs where offline page ID is used in + // place of tab ID and vice versa. + std::string tab_id = base::IntToString(offline_id + 1000); + ClientId client_id(offline_pages::kLastNNamespace, tab_id); + return test::CreateDummyOfflinePageItem(offline_id, client_id); } std::vector<OfflinePageItem> CreateDummyRecentTabs( @@ -63,15 +69,25 @@ } // namespace -class RecentTabSuggestionsProviderTest : public testing::Test { +class RecentTabSuggestionsProviderTestNoLoad : public testing::Test { public: - RecentTabSuggestionsProviderTest() - : pref_service_(new TestingPrefServiceSimple()) { + RecentTabSuggestionsProviderTestNoLoad() + : task_runner_(new base::TestSimpleTaskRunner()), + task_runner_handle_(task_runner_), + pref_service_(new TestingPrefServiceSimple()) { RecentTabSuggestionsProvider::RegisterProfilePrefs( pref_service()->registry()); - provider_.reset( - new RecentTabSuggestionsProvider(&observer_, &model_, pref_service())); + taco_ = base::MakeUnique<offline_pages::RequestCoordinatorStubTaco>(); + taco_->CreateRequestCoordinator(); + + ui_adapter_ = offline_pages::RecentTabsUIAdapterDelegate:: + GetOrCreateRecentTabsUIAdapter(&model_, taco_->request_coordinator()); + delegate_ = + offline_pages::RecentTabsUIAdapterDelegate::FromDownloadUIAdapter( + ui_adapter_); + provider_ = base::MakeUnique<RecentTabSuggestionsProvider>( + &observer_, ui_adapter_, pref_service()); } Category recent_tabs_category() { @@ -82,18 +98,25 @@ return ContentSuggestion::ID(recent_tabs_category(), base::IntToString(id)); } + void AddTabAndOfflinePageToModel(const OfflinePageItem& item) { + AddTab(offline_pages::RecentTabsUIAdapterDelegate::TabIdFromClientId( + item.client_id)); + AddOfflinePageToModel(item); + } + + void AddTab(int tab_id) { delegate_->RegisterTab(tab_id); } + + void RemoveTab(int tab_id) { delegate_->UnregisterTab(tab_id); } + void AddOfflinePageToModel(const OfflinePageItem& item) { - model_.mutable_items()->push_back(item); - provider_->OfflinePageAdded(&model_, item); + ui_adapter_->OfflinePageAdded(&model_, item); } void FireOfflinePageDeleted(const OfflinePageItem& item) { - auto iter = std::remove(model_.mutable_items()->begin(), - model_.mutable_items()->end(), item); - auto end = model_.mutable_items()->end(); - model_.mutable_items()->erase(iter, end); - - provider_->OfflinePageDeleted(item.offline_id, item.client_id); + int tab_id = offline_pages::RecentTabsUIAdapterDelegate::TabIdFromClientId( + item.client_id); + RemoveTab(tab_id); + ui_adapter_->OfflinePageDeleted(item.offline_id, item.client_id); } std::set<std::string> ReadDismissedIDsFromPrefs() { @@ -101,26 +124,45 @@ } RecentTabSuggestionsProvider* provider() { return provider_.get(); } - FakeOfflinePageModel* model() { return &model_; } MockContentSuggestionsProviderObserver* observer() { return &observer_; } TestingPrefServiceSimple* pref_service() { return pref_service_.get(); } + base::TestSimpleTaskRunner* task_runner() { return task_runner_.get(); } private: FakeOfflinePageModel model_; + offline_pages::DownloadUIAdapter* ui_adapter_; + offline_pages::RecentTabsUIAdapterDelegate* delegate_; + scoped_refptr<base::TestSimpleTaskRunner> task_runner_; + base::ThreadTaskRunnerHandle task_runner_handle_; + std::unique_ptr<offline_pages::RequestCoordinatorStubTaco> taco_; MockContentSuggestionsProviderObserver observer_; std::unique_ptr<TestingPrefServiceSimple> pref_service_; // Last so that the dependencies are deleted after the provider. std::unique_ptr<RecentTabSuggestionsProvider> provider_; + DISALLOW_COPY_AND_ASSIGN(RecentTabSuggestionsProviderTestNoLoad); +}; + +// Test that always loads the model before the start of the test. +class RecentTabSuggestionsProviderTest + : public RecentTabSuggestionsProviderTestNoLoad { + public: + RecentTabSuggestionsProviderTest() = default; + + void SetUp() override { + // The UI adapter always fires asynchronously upon loading, so we want to + // run past that moment before each test. Expect a call to hide warnings. + EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(1); + task_runner()->RunUntilIdle(); + Mock::VerifyAndClearExpectations(observer()); + } + + private: DISALLOW_COPY_AND_ASSIGN(RecentTabSuggestionsProviderTest); }; TEST_F(RecentTabSuggestionsProviderTest, ShouldConvertToSuggestions) { - auto recent_tabs_list = CreateDummyRecentTabs({1, 2}); EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(2); - for (OfflinePageItem& recent_tab : recent_tabs_list) - AddOfflinePageToModel(recent_tab); - EXPECT_CALL( *observer(), OnNewSuggestions(_, recent_tabs_category(), @@ -131,7 +173,11 @@ GURL("http://dummy.com/2")), Property(&ContentSuggestion::url, GURL("http://dummy.com/3"))))); - AddOfflinePageToModel(CreateDummyRecentTab(3)); + + auto recent_tabs_list = CreateDummyRecentTabs({1, 2, 3}); + for (OfflinePageItem& recent_tab : recent_tabs_list) { + AddTabAndOfflinePageToModel(recent_tab); + } } TEST_F(RecentTabSuggestionsProviderTest, ShouldSortByCreationTime) { @@ -148,7 +194,7 @@ OnNewSuggestions(_, recent_tabs_category(), ElementsAre(Property(&ContentSuggestion::url, GURL("http://dummy.com/1"))))); - AddOfflinePageToModel(CreateDummyRecentTab(1, now)); + AddTabAndOfflinePageToModel(CreateDummyRecentTab(1, now)); EXPECT_CALL( *observer(), @@ -157,7 +203,7 @@ ElementsAre( Property(&ContentSuggestion::url, GURL("http://dummy.com/1")), Property(&ContentSuggestion::url, GURL("http://dummy.com/2"))))); - AddOfflinePageToModel(CreateDummyRecentTab(2, yesterday)); + AddTabAndOfflinePageToModel(CreateDummyRecentTab(2, yesterday)); offline_pages[1].last_access_time = offline_pages[0].last_access_time + base::TimeDelta::FromHours(1); @@ -172,7 +218,7 @@ GURL("http://dummy.com/1")), Property(&ContentSuggestion::url, GURL("http://dummy.com/2"))))); - AddOfflinePageToModel(CreateDummyRecentTab(3, tomorrow)); + AddTabAndOfflinePageToModel(CreateDummyRecentTab(3, tomorrow)); } TEST_F(RecentTabSuggestionsProviderTest, ShouldDeliverCorrectCategoryInfo) { @@ -189,7 +235,7 @@ EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(3); auto recent_tabs_list = CreateDummyRecentTabs({1, 2, 3}); for (OfflinePageItem& recent_tab : recent_tabs_list) { - AddOfflinePageToModel(recent_tab); + AddTabAndOfflinePageToModel(recent_tab); } // Dismiss 2 and 3. @@ -207,7 +253,7 @@ Property(&ContentSuggestion::url, GURL("http://dummy.com/1")), Property(&ContentSuggestion::url, GURL("http://dummy.com/4"))))); - AddOfflinePageToModel(CreateDummyRecentTab(4)); + AddTabAndOfflinePageToModel(CreateDummyRecentTab(4)); Mock::VerifyAndClearExpectations(observer()); // And appear in the dismissed suggestions. @@ -235,7 +281,7 @@ // And appear in the reported suggestions for the category again. EXPECT_CALL(*observer(), OnNewSuggestions(_, recent_tabs_category(), SizeIs(5))); - AddOfflinePageToModel(CreateDummyRecentTab(5)); + AddTabAndOfflinePageToModel(CreateDummyRecentTab(5)); Mock::VerifyAndClearExpectations(observer()); } @@ -244,7 +290,7 @@ EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(3); std::vector<OfflinePageItem> offline_pages = CreateDummyRecentTabs({1, 2, 3}); for (OfflinePageItem& recent_tab : offline_pages) - AddOfflinePageToModel(recent_tab); + AddTabAndOfflinePageToModel(recent_tab); // Invalidation of suggestion 2 should be forwarded. EXPECT_CALL(*observer(), OnSuggestionInvalidated(_, GetDummySuggestionId(2))); @@ -255,7 +301,7 @@ EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(3); std::vector<OfflinePageItem> offline_pages = CreateDummyRecentTabs({1, 2, 3}); for (OfflinePageItem& recent_tab : offline_pages) - AddOfflinePageToModel(recent_tab); + AddTabAndOfflinePageToModel(recent_tab); EXPECT_THAT(ReadDismissedIDsFromPrefs(), IsEmpty()); provider()->DismissSuggestion(GetDummySuggestionId(2)); @@ -269,7 +315,7 @@ EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(3); std::vector<OfflinePageItem> offline_pages = CreateDummyRecentTabs({1, 2, 3}); for (OfflinePageItem& recent_tab : offline_pages) - AddOfflinePageToModel(recent_tab); + AddTabAndOfflinePageToModel(recent_tab); provider()->DismissSuggestion(GetDummySuggestionId(2)); provider()->DismissSuggestion(GetDummySuggestionId(3)); @@ -294,8 +340,8 @@ // We leave IDs different, but make the URLs the same. offline_pages[2].url = offline_pages[0].url; - AddOfflinePageToModel(offline_pages[0]); - AddOfflinePageToModel(offline_pages[1]); + AddTabAndOfflinePageToModel(offline_pages[0]); + AddTabAndOfflinePageToModel(offline_pages[1]); Mock::VerifyAndClearExpectations(observer()); EXPECT_CALL(*observer(), OnNewSuggestions( @@ -304,13 +350,11 @@ Property(&ContentSuggestion::publish_date, now), Property(&ContentSuggestion::publish_date, tomorrow)))); - AddOfflinePageToModel(offline_pages[2]); + AddTabAndOfflinePageToModel(offline_pages[2]); } TEST_F(RecentTabSuggestionsProviderTest, ShouldNotFetchIfAddedOfflinePageIsNotRecentTab) { - // The provider is not notified about the first recent tab yet. - model()->mutable_items()->push_back(CreateDummyRecentTab(1)); // It should not fetch when not a recent tab is added, thus, it should not // report the first recent tab (which it is not aware about). EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(0); @@ -319,11 +363,27 @@ } TEST_F(RecentTabSuggestionsProviderTest, - ShouldFetchIfAddedOfflinePageIsRecentTab) { - // The provider is not notified about the first recent tab yet. - model()->mutable_items()->push_back(CreateDummyRecentTab(1)); - // However, it must return the first recent tab (i.e. manually fetch it) even - // when notified about a different recent tab. + ShouldInvalidateSuggestionWhenTabGone) { + OfflinePageItem first_tab = CreateDummyRecentTab(1); + AddTabAndOfflinePageToModel(first_tab); + Mock::VerifyAndClearExpectations(observer()); + + EXPECT_CALL(*observer(), OnSuggestionInvalidated(_, GetDummySuggestionId(1))) + .Times(1); + RemoveTab(offline_pages::RecentTabsUIAdapterDelegate::TabIdFromClientId( + first_tab.client_id)); + // Removing an unknown tab should not cause extra invalidations. + RemoveTab(42); +} + +TEST_F(RecentTabSuggestionsProviderTest, ShouldNotShowPagesWithoutTab) { + EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(0); + // The provider is not notified about the first recent tab yet (no tab). + OfflinePageItem first_tab = CreateDummyRecentTab(1); + AddOfflinePageToModel(first_tab); + + Mock::VerifyAndClearExpectations(observer()); + EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(1); EXPECT_CALL( *observer(), OnNewSuggestions( @@ -331,7 +391,52 @@ UnorderedElementsAre( Property(&ContentSuggestion::url, GURL("http://dummy.com/1")), Property(&ContentSuggestion::url, GURL("http://dummy.com/2"))))); - AddOfflinePageToModel(CreateDummyRecentTab(2)); + + AddTab(offline_pages::RecentTabsUIAdapterDelegate::TabIdFromClientId( + first_tab.client_id)); + OfflinePageItem second_tab = CreateDummyRecentTab(2); + AddTabAndOfflinePageToModel(second_tab); + + Mock::VerifyAndClearExpectations(observer()); + + EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(0); + // |RemoveTab| by itself doesn't cause OnNewSuggestions to be called. + RemoveTab(offline_pages::RecentTabsUIAdapterDelegate::TabIdFromClientId( + second_tab.client_id)); + Mock::VerifyAndClearExpectations(observer()); + + // But when we get another tab, OnNewSuggestions will be called. + EXPECT_CALL( + *observer(), + OnNewSuggestions( + _, recent_tabs_category(), + UnorderedElementsAre( + Property(&ContentSuggestion::url, GURL("http://dummy.com/1")), + Property(&ContentSuggestion::url, GURL("http://dummy.com/3"))))); + + AddTabAndOfflinePageToModel(CreateDummyRecentTab(3)); +} + +// The following test uses a different fixture that does not automatically pump +// the event loop in SetUp, which means that until |RunUntilIdle| is called, the +// UI adapter will not be loaded (and should not fire any events). + +TEST_F(RecentTabSuggestionsProviderTestNoLoad, ShouldFetchOnLoad) { + // Tabs are added to the model before the UI adapter is loaded, so there + // should only be a single |OnNewSuggestions| call, at load time. + EXPECT_CALL( + *observer(), + OnNewSuggestions( + _, recent_tabs_category(), + UnorderedElementsAre( + Property(&ContentSuggestion::url, GURL("http://dummy.com/1")), + Property(&ContentSuggestion::url, GURL("http://dummy.com/2"))))); + + AddTabAndOfflinePageToModel(CreateDummyRecentTab(1)); + AddTabAndOfflinePageToModel(CreateDummyRecentTab(2)); + // The provider is not notified about the recent tabs yet. + task_runner()->RunUntilIdle(); + // However, it must return both tabs when the model is loaded. } } // namespace ntp_snippets
diff --git a/components/offline_pages/core/BUILD.gn b/components/offline_pages/core/BUILD.gn index 088c953c..21708d2b 100644 --- a/components/offline_pages/core/BUILD.gn +++ b/components/offline_pages/core/BUILD.gn
@@ -108,6 +108,7 @@ ":core", ":switches", ":test_support", + "recent_tabs:unit_tests", "//base", "//base/test:test_support", "//sql:sql",
diff --git a/components/offline_pages/core/downloads/download_ui_adapter.cc b/components/offline_pages/core/downloads/download_ui_adapter.cc index cf92370c..8543314 100644 --- a/components/offline_pages/core/downloads/download_ui_adapter.cc +++ b/components/offline_pages/core/downloads/download_ui_adapter.cc
@@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2017 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. @@ -39,19 +39,21 @@ model->SetUserData(kDownloadUIAdapterKey, adapter); } -DownloadUIAdapter::ItemInfo::ItemInfo(const OfflinePageItem& page) +DownloadUIAdapter::ItemInfo::ItemInfo(const OfflinePageItem& page, + bool temporarily_hidden) : ui_item(base::MakeUnique<DownloadUIItem>(page)), is_request(false), offline_id(page.offline_id), client_id(page.client_id), - temporarily_hidden(false) {} + temporarily_hidden(temporarily_hidden) {} -DownloadUIAdapter::ItemInfo::ItemInfo(const SavePageRequest& request) +DownloadUIAdapter::ItemInfo::ItemInfo(const SavePageRequest& request, + bool temporarily_hidden) : ui_item(base::MakeUnique<DownloadUIItem>(request)), is_request(true), offline_id(request.request_id()), client_id(request.client_id()), - temporarily_hidden(false) {} + temporarily_hidden() {} DownloadUIAdapter::ItemInfo::~ItemInfo() {} @@ -63,7 +65,9 @@ delegate_(std::move(delegate)), state_(State::NOT_LOADED), observers_count_(0), - weak_ptr_factory_(this) {} + weak_ptr_factory_(this) { + delegate_->SetUIAdapter(this); +} DownloadUIAdapter::~DownloadUIAdapter() {} @@ -106,7 +110,9 @@ if (!delegate_->IsVisibleInUI(added_page.client_id)) return; - AddItemHelper(base::MakeUnique<ItemInfo>(added_page)); + bool temporarily_hidden = + delegate_->IsTemporarilyHiddenInUI(added_page.client_id); + AddItemHelper(base::MakeUnique<ItemInfo>(added_page, temporarily_hidden)); } void DownloadUIAdapter::OfflinePageDeleted(int64_t offline_id, @@ -121,7 +127,9 @@ if (!delegate_->IsVisibleInUI(added_request.client_id())) return; - AddItemHelper(base::MakeUnique<ItemInfo>(added_request)); + bool temporarily_hidden = + delegate_->IsTemporarilyHiddenInUI(added_request.client_id()); + AddItemHelper(base::MakeUnique<ItemInfo>(added_request, temporarily_hidden)); } // RequestCoordinator::Observer @@ -144,7 +152,9 @@ return; std::string guid = request.client_id().id; - items_[guid] = base::MakeUnique<ItemInfo>(request); + bool temporarily_hidden = + delegate_->IsTemporarilyHiddenInUI(request.client_id()); + items_[guid] = base::MakeUnique<ItemInfo>(request, temporarily_hidden); if (state_ != State::LOADED) return; @@ -207,6 +217,9 @@ } int64_t DownloadUIAdapter::GetOfflineIdByGuid(const std::string& guid) const { + if (deleting_item_ && deleting_item_->ui_item->guid == guid) + return deleting_item_->offline_id; + DownloadUIItems::const_iterator it = items_.find(guid); if (it != items_.end()) return it->second->offline_id; @@ -256,9 +269,10 @@ if (delegate_->IsVisibleInUI(page.client_id)) { std::string guid = page.client_id.id; DCHECK(items_.find(guid) == items_.end()); - std::unique_ptr<ItemInfo> item = base::MakeUnique<ItemInfo>(page); - item->temporarily_hidden = + bool temporarily_hidden = delegate_->IsTemporarilyHiddenInUI(page.client_id); + std::unique_ptr<ItemInfo> item = + base::MakeUnique<ItemInfo>(page, temporarily_hidden); items_[guid] = std::move(item); } } @@ -281,10 +295,10 @@ if (delegate_->IsVisibleInUI(request->client_id())) { std::string guid = request->client_id().id; DCHECK(items_.find(guid) == items_.end()); - std::unique_ptr<ItemInfo> item = - base::MakeUnique<ItemInfo>(*request.get()); - item->temporarily_hidden = + bool temporarily_hidden = delegate_->IsTemporarilyHiddenInUI(request->client_id()); + std::unique_ptr<ItemInfo> item = + base::MakeUnique<ItemInfo>(*request.get(), temporarily_hidden); items_[guid] = std::move(item); } } @@ -321,27 +335,35 @@ return; DownloadUIItem* download_ui_item = items_[guid]->ui_item.get(); + if (request_to_page_transition) { download_ui_item->download_state = DownloadUIItem::DownloadState::COMPLETE; - for (Observer& observer : observers_) - observer.ItemUpdated(*download_ui_item); + if (!items_[guid]->temporarily_hidden) { + for (Observer& observer : observers_) + observer.ItemUpdated(*download_ui_item); + } } else { - for (Observer& observer : observers_) - observer.ItemAdded(*download_ui_item); + if (!items_[guid]->temporarily_hidden) { + for (Observer& observer : observers_) + observer.ItemAdded(*download_ui_item); + } } } void DownloadUIAdapter::DeleteItemHelper(const std::string& guid) { - DownloadUIItems::const_iterator it = items_.find(guid); + DownloadUIItems::iterator it = items_.find(guid); if (it == items_.end()) return; + DCHECK(deleting_item_ == nullptr); + deleting_item_ = std::move(it->second); items_.erase(it); - if (state_ != State::LOADED) - return; + if (!deleting_item_->temporarily_hidden && state_ == State::LOADED) { + for (Observer& observer : observers_) + observer.ItemDeleted(guid); + } - for (Observer& observer : observers_) - observer.ItemDeleted(guid); + deleting_item_.reset(); } } // namespace offline_pages
diff --git a/components/offline_pages/core/downloads/download_ui_adapter.h b/components/offline_pages/core/downloads/download_ui_adapter.h index 8bfb0a3..4fef8e1b 100644 --- a/components/offline_pages/core/downloads/download_ui_adapter.h +++ b/components/offline_pages/core/downloads/download_ui_adapter.h
@@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2017 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. @@ -48,6 +48,10 @@ // of temporary visibility changes, the Delegate is supposed to call // DownloadUIAdapter::TemporarilyHiddenStatusChanged(). virtual bool IsTemporarilyHiddenInUI(const ClientId& client_id) = 0; + + // Delegates need a reference to the UI adapter in order to notify it about + // visibility changes. + virtual void SetUIAdapter(DownloadUIAdapter* ui_adapter) = 0; }; // Observer, normally implemented by UI or a Bridge. @@ -118,12 +122,14 @@ // change. void TemporaryHiddenStatusChanged(const ClientId& client_id); + Delegate* delegate() { return delegate_.get(); } + private: enum class State { NOT_LOADED, LOADING_PAGES, LOADING_REQUESTS, LOADED }; struct ItemInfo { - explicit ItemInfo(const OfflinePageItem& page); - explicit ItemInfo(const SavePageRequest& request); + ItemInfo(const OfflinePageItem& page, bool temporarily_hidden); + ItemInfo(const SavePageRequest& request, bool temporarily_hidden); ~ItemInfo(); std::unique_ptr<DownloadUIItem> ui_item; @@ -163,6 +169,9 @@ void OnDeletePagesDone(DeletePageResult result); void AddItemHelper(std::unique_ptr<ItemInfo> item_info); + // This function is not re-entrant. It temporarily sets |deleting_item_| + // while it runs, so that functions such as |GetOfflineIdByGuid| will work + // during the |ItemDeleted| callback. void DeleteItemHelper(const std::string& guid); // Always valid, this class is a member of the model. @@ -179,6 +188,8 @@ // The cache of UI items. The key is DownloadUIItem.guid. DownloadUIItems items_; + std::unique_ptr<ItemInfo> deleting_item_; + // The observers. base::ObserverList<Observer> observers_; int observers_count_;
diff --git a/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc b/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc index 12db77e..181bb3d 100644 --- a/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc +++ b/components/offline_pages/core/downloads/download_ui_adapter_unittest.cc
@@ -60,6 +60,7 @@ bool IsTemporarilyHiddenInUI(const ClientId& client_id) override { return is_temporarily_hidden; } + void SetUIAdapter(DownloadUIAdapter* ui_adapter) override{}; bool is_visible = true; bool is_temporarily_hidden = false;
diff --git a/components/offline_pages/core/recent_tabs/BUILD.gn b/components/offline_pages/core/recent_tabs/BUILD.gn new file mode 100644 index 0000000..fa8f938a --- /dev/null +++ b/components/offline_pages/core/recent_tabs/BUILD.gn
@@ -0,0 +1,39 @@ +# Copyright 2017 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. + +if (is_android) { + import("//build/config/android/rules.gni") +} + +source_set("recent_tabs") { + sources = [ + "recent_tabs_ui_adapter_delegate.cc", + "recent_tabs_ui_adapter_delegate.h", + ] + + deps = [ + "//base", + "//components/offline_pages/core", + "//components/offline_pages/core/downloads:offline_pages_ui_adapter", + "//url", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "recent_tabs_ui_adapter_delegate_unittest.cc", + ] + + deps = [ + ":recent_tabs", + "//base", + "//base/test:test_support", + "//components/offline_pages/core", + "//components/offline_pages/core:test_support", + "//components/offline_pages/core/background:test_support", + "//components/offline_pages/core/downloads:offline_pages_ui_adapter", + "//testing/gtest", + ] +}
diff --git a/components/offline_pages/core/recent_tabs/recent_tabs_ui_adapter_delegate.cc b/components/offline_pages/core/recent_tabs/recent_tabs_ui_adapter_delegate.cc new file mode 100644 index 0000000..ebfb511 --- /dev/null +++ b/components/offline_pages/core/recent_tabs/recent_tabs_ui_adapter_delegate.cc
@@ -0,0 +1,97 @@ +// Copyright 2016 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 "components/offline_pages/core/recent_tabs/recent_tabs_ui_adapter_delegate.h" + +#include "base/bind.h" +#include "base/threading/thread_task_runner_handle.h" +#include "components/offline_pages/core/client_policy_controller.h" +#include "components/offline_pages/core/offline_page_model.h" + +namespace offline_pages { + +namespace { +const char kRecentTabsUIAdapterKey[] = "recent-tabs-download-ui-adapter"; +} // namespace + +RecentTabsUIAdapterDelegate::RecentTabsUIAdapterDelegate( + OfflinePageModel* model) + : model_(model) {} + +RecentTabsUIAdapterDelegate::~RecentTabsUIAdapterDelegate() = default; + +offline_pages::DownloadUIAdapter* +RecentTabsUIAdapterDelegate::GetOrCreateRecentTabsUIAdapter( + offline_pages::OfflinePageModel* offline_page_model, + offline_pages::RequestCoordinator* request_coordinator) { + offline_pages::DownloadUIAdapter* recent_tabs_ui_adapter = + static_cast<DownloadUIAdapter*>( + offline_page_model->GetUserData(kRecentTabsUIAdapterKey)); + + if (!recent_tabs_ui_adapter) { + auto delegate = + base::MakeUnique<RecentTabsUIAdapterDelegate>(offline_page_model); + recent_tabs_ui_adapter = new DownloadUIAdapter( + offline_page_model, request_coordinator, std::move(delegate)); + offline_page_model->SetUserData(kRecentTabsUIAdapterKey, + recent_tabs_ui_adapter); + } + + return recent_tabs_ui_adapter; +} + +// static +RecentTabsUIAdapterDelegate* RecentTabsUIAdapterDelegate::FromDownloadUIAdapter( + DownloadUIAdapter* ui_adapter) { + return static_cast<RecentTabsUIAdapterDelegate*>(ui_adapter->delegate()); +} + +// static +int RecentTabsUIAdapterDelegate::TabIdFromClientId(const ClientId& client_id) { + int tab_id = 0; + bool convert_result = base::StringToInt(client_id.id, &tab_id); + DCHECK(convert_result); + return tab_id; +} + +bool RecentTabsUIAdapterDelegate::IsVisibleInUI(const ClientId& client_id) { + return IsRecentTab(client_id); +} + +bool RecentTabsUIAdapterDelegate::IsTemporarilyHiddenInUI( + const ClientId& client_id) { + return active_tabs_.count(TabIdFromClientId(client_id)) == 0; +} + +void RecentTabsUIAdapterDelegate::SetUIAdapter(DownloadUIAdapter* adapter) { + ui_adapter_ = adapter; +} + +void RecentTabsUIAdapterDelegate::RegisterTab(int tab_id) { + DCHECK(ui_adapter_); + if (active_tabs_.count(tab_id) > 0) + return; + active_tabs_.insert(tab_id); + ui_adapter_->TemporaryHiddenStatusChanged(ClientIdFromTabId(tab_id)); +} + +void RecentTabsUIAdapterDelegate::UnregisterTab(int tab_id) { + DCHECK(ui_adapter_); + if (active_tabs_.count(tab_id) == 0) + return; + active_tabs_.erase(tab_id); + ui_adapter_->TemporaryHiddenStatusChanged(ClientIdFromTabId(tab_id)); +} + +// static +ClientId RecentTabsUIAdapterDelegate::ClientIdFromTabId(int tab_id) { + return ClientId(kLastNNamespace, base::IntToString(tab_id)); +} + +bool RecentTabsUIAdapterDelegate::IsRecentTab(const ClientId& page) { + return model_->GetPolicyController()->IsShownAsRecentlyVisitedSite( + page.name_space); +} + +} // namespace offline_pages
diff --git a/components/offline_pages/core/recent_tabs/recent_tabs_ui_adapter_delegate.h b/components/offline_pages/core/recent_tabs/recent_tabs_ui_adapter_delegate.h new file mode 100644 index 0000000..bd166e448 --- /dev/null +++ b/components/offline_pages/core/recent_tabs/recent_tabs_ui_adapter_delegate.h
@@ -0,0 +1,85 @@ +// Copyright 2016 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 COMPONENTS_OFFLINE_PAGES_CORE_RECENT_TABS_RECENT_TABS_UI_ADAPTER_DELEGATE_H_ +#define COMPONENTS_OFFLINE_PAGES_CORE_RECENT_TABS_RECENT_TABS_UI_ADAPTER_DELEGATE_H_ + +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "base/observer_list.h" +#include "base/supports_user_data.h" +#include "components/offline_pages/core/client_namespace_constants.h" +#include "components/offline_pages/core/downloads/download_ui_adapter.h" +#include "components/offline_pages/core/offline_page_model.h" +#include "components/offline_pages/core/offline_page_types.h" +#include "url/gurl.h" + +namespace offline_pages { + +// Keeps track of all the tabs open for the profile, and restricts its view of +// the offline pages to only those that have an open tab. This begins +// observation early, as soon as the first Tab is created, and even before that +// tab gets a web contents. +// +// In this UI adapter, |guid| represents the tab ID for a given page. +class RecentTabsUIAdapterDelegate : public DownloadUIAdapter::Delegate { + public: + explicit RecentTabsUIAdapterDelegate(OfflinePageModel* model); + ~RecentTabsUIAdapterDelegate() override; + + static DownloadUIAdapter* GetOrCreateRecentTabsUIAdapter( + OfflinePageModel* offline_page_model, + RequestCoordinator* request_coordinator); + static RecentTabsUIAdapterDelegate* FromDownloadUIAdapter( + DownloadUIAdapter* adapter); + // This extracts the tab ID out of the |id| field of the client ID using a + // string conversion. Crashes if conversion fails. + static int TabIdFromClientId(const ClientId& client_id); + + // This override returns true if the client ID is shown on the NTP as a recent + // tab. This is determined by policy. + bool IsVisibleInUI(const ClientId& client_id) override; + // This returns true if there does not exist a tab ID for the given client ID. + bool IsTemporarilyHiddenInUI(const ClientId& client_id) override; + // Sets our reference to the UI adapter so we can notify it of visibility + // changes. + void SetUIAdapter(DownloadUIAdapter* ui_adapter) override; + + // Register/UnregisterTab add and remove tab IDs from the list. These + // functions can be called before a page actually exists with the given tab + // ID. + // Note that these change temporary visibility and therefore |set_ui_adapter| + // must be called before either of these functions. + void RegisterTab(int tab_id); + void UnregisterTab(int tab_id); + + private: + // Constructs the client ID assuming that we use the Last N namespace. + // + // TODO(dewittj): Make this more resilient to adding more namespaces. + static ClientId ClientIdFromTabId(int tab_id); + + // Checks a client ID for proper namespace and ID format to be shown in the + // Downloads Home UI. + bool IsRecentTab(const ClientId& page); + + // Always valid, this class is owned by DownloadUIAdapter, which is owned by + // Offline Page Model. + OfflinePageModel* model_; + + // This is set by the UI adapter itself. + DownloadUIAdapter* ui_adapter_ = nullptr; + + // The tabs we've seen. + std::unordered_set<int> active_tabs_; + + DISALLOW_COPY_AND_ASSIGN(RecentTabsUIAdapterDelegate); +}; + +} // namespace offline_pages + +#endif // COMPONENTS_OFFLINE_PAGES_CORE_RECENT_TABS_RECENT_TABS_UI_ADAPTER_DELEGATE_H_
diff --git a/components/offline_pages/core/recent_tabs/recent_tabs_ui_adapter_delegate_unittest.cc b/components/offline_pages/core/recent_tabs/recent_tabs_ui_adapter_delegate_unittest.cc new file mode 100644 index 0000000..2065916 --- /dev/null +++ b/components/offline_pages/core/recent_tabs/recent_tabs_ui_adapter_delegate_unittest.cc
@@ -0,0 +1,122 @@ +// Copyright 2017 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 "components/offline_pages/core/recent_tabs/recent_tabs_ui_adapter_delegate.h" + +#include <stdint.h> + +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/run_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "base/test/test_mock_time_task_runner.h" +#include "base/threading/thread_task_runner_handle.h" +#include "base/time/time.h" +#include "components/offline_pages/core/background/request_coordinator_stub_taco.h" +#include "components/offline_pages/core/client_namespace_constants.h" +#include "components/offline_pages/core/client_policy_controller.h" +#include "components/offline_pages/core/downloads/download_ui_adapter.h" +#include "components/offline_pages/core/stub_offline_page_model.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace offline_pages { + +namespace { +static const char kTestGuid1[] = "1"; +static const char kTestGuid2[] = "2"; +static const char kTestBadGuid[] = "ccccccc-cccc-0ccc-0ccc-ccccccccccc0"; +static const ClientId kTestClientIdOtherNamespace(kAsyncNamespace, kTestGuid1); +static const ClientId kTestClientIdOtherGuid(kLastNNamespace, kTestBadGuid); +static const ClientId kTestClientId1(kLastNNamespace, kTestGuid1); +static const ClientId kTestClientId2(kLastNNamespace, kTestGuid2); +} // namespace + +// Creates mock versions for OfflinePageModel, RequestCoordinator and their +// dependencies, then passes them to DownloadUIAdapter for testing. +// Note that initially the OfflienPageModel is not "loaded". PumpLoop() will +// load it, firing ItemsLoaded callback to the Adapter. Hence some tests +// start from PumpLoop() right away if they don't need to test this. +class RecentTabsUIAdapterDelegateTest : public testing::Test { + public: + RecentTabsUIAdapterDelegateTest(); + ~RecentTabsUIAdapterDelegateTest() override; + + // Runs until all of the tasks that are not delayed are gone from the task + // queue. + void PumpLoop(); + + RequestCoordinator* request_coordinator() { + return request_coordinator_taco_->request_coordinator(); + } + + StubOfflinePageModel model; + RecentTabsUIAdapterDelegate* adapter_delegate; + std::unique_ptr<DownloadUIAdapter> adapter; + + private: + std::unique_ptr<RequestCoordinatorStubTaco> request_coordinator_taco_; + scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; + base::ThreadTaskRunnerHandle task_runner_handle_; +}; + +RecentTabsUIAdapterDelegateTest::RecentTabsUIAdapterDelegateTest() + : task_runner_(new base::TestMockTimeTaskRunner()), + task_runner_handle_(task_runner_) { + request_coordinator_taco_ = base::MakeUnique<RequestCoordinatorStubTaco>(); + request_coordinator_taco_->CreateRequestCoordinator(); + + auto delegate = base::MakeUnique<RecentTabsUIAdapterDelegate>(&model); + adapter_delegate = delegate.get(); + + adapter = base::MakeUnique<DownloadUIAdapter>( + &model, request_coordinator_taco_->request_coordinator(), + std::move(delegate)); +} + +RecentTabsUIAdapterDelegateTest::~RecentTabsUIAdapterDelegateTest() = default; + +void RecentTabsUIAdapterDelegateTest::PumpLoop() { + task_runner_->RunUntilIdle(); +} + +TEST_F(RecentTabsUIAdapterDelegateTest, TabIdFromClientId) { + EXPECT_EQ(1, RecentTabsUIAdapterDelegate::TabIdFromClientId(kTestClientId1)); + EXPECT_EQ(2, RecentTabsUIAdapterDelegate::TabIdFromClientId(kTestClientId2)); +} + +TEST_F(RecentTabsUIAdapterDelegateTest, IsVisibleInUI) { + EXPECT_FALSE(adapter_delegate->IsVisibleInUI(kTestClientIdOtherNamespace)); + EXPECT_TRUE(adapter_delegate->IsVisibleInUI(kTestClientId1)); + EXPECT_TRUE(adapter_delegate->IsVisibleInUI(kTestClientId2)); +} + +TEST_F(RecentTabsUIAdapterDelegateTest, IsTemporarilyHiddenInUI) { + adapter_delegate->RegisterTab(3); + adapter_delegate->RegisterTab(4); + + EXPECT_TRUE(adapter_delegate->IsTemporarilyHiddenInUI(kTestClientId1)); + EXPECT_TRUE(adapter_delegate->IsTemporarilyHiddenInUI(kTestClientId2)); + + adapter_delegate->RegisterTab(1); + EXPECT_FALSE(adapter_delegate->IsTemporarilyHiddenInUI(kTestClientId1)); + EXPECT_TRUE(adapter_delegate->IsTemporarilyHiddenInUI(kTestClientId2)); + + adapter_delegate->RegisterTab(2); + EXPECT_FALSE(adapter_delegate->IsTemporarilyHiddenInUI(kTestClientId1)); + EXPECT_FALSE(adapter_delegate->IsTemporarilyHiddenInUI(kTestClientId2)); + + adapter_delegate->UnregisterTab(2); + EXPECT_FALSE(adapter_delegate->IsTemporarilyHiddenInUI(kTestClientId1)); + EXPECT_TRUE(adapter_delegate->IsTemporarilyHiddenInUI(kTestClientId2)); +} + +} // namespace offline_pages
diff --git a/components/security_state/core/security_state.cc b/components/security_state/core/security_state.cc index 36ac2a7e..577d93b 100644 --- a/components/security_state/core/security_state.cc +++ b/components/security_state/core/security_state.cc
@@ -19,7 +19,12 @@ // Do not change or reorder this enum, and add new values at the end. It is used // in the MarkHttpAs histogram. -enum MarkHttpStatus { NEUTRAL, NON_SECURE, HTTP_SHOW_WARNING, LAST_STATUS }; +enum MarkHttpStatus { + NEUTRAL /* deprecated */, + NON_SECURE, + HTTP_SHOW_WARNING_ON_SENSITIVE_FIELDS, + LAST_STATUS +}; // If |switch_or_field_trial_group| corresponds to a valid // MarkHttpAs group, sets |*level| and |*histogram_status| to the @@ -29,30 +34,11 @@ bool displayed_sensitive_input_on_http, SecurityLevel* level, MarkHttpStatus* histogram_status) { - if (switch_or_field_trial_group == switches::kMarkHttpAsNeutral) { - *level = NONE; - *histogram_status = NEUTRAL; - return true; - } - - if (switch_or_field_trial_group == switches::kMarkHttpAsDangerous) { - *level = DANGEROUS; - *histogram_status = NON_SECURE; - return true; - } - - if (switch_or_field_trial_group == - switches::kMarkHttpWithPasswordsOrCcWithChip) { - if (displayed_sensitive_input_on_http) { - *level = security_state::HTTP_SHOW_WARNING; - } else { - *level = NONE; - } - *histogram_status = HTTP_SHOW_WARNING; - return true; - } - - return false; + if (switch_or_field_trial_group != switches::kMarkHttpAsDangerous) + return false; + *level = DANGEROUS; + *histogram_status = NON_SECURE; + return true; } SecurityLevel GetSecurityLevelForNonSecureFieldTrial( @@ -73,18 +59,10 @@ choice, displayed_sensitive_input_on_http, &level, &status)) { if (!GetSecurityLevelAndHistogramValueForNonSecureFieldTrial( group, displayed_sensitive_input_on_http, &level, &status)) { - // If neither the command-line switch nor field trial group is set, then - // nonsecure defaults to neutral for iOS and HTTP_SHOW_WARNING on other - // platforms. -#if defined(OS_IOS) - status = NEUTRAL; - level = NONE; -#else - status = HTTP_SHOW_WARNING; + status = HTTP_SHOW_WARNING_ON_SENSITIVE_FIELDS; level = displayed_sensitive_input_on_http ? security_state::HTTP_SHOW_WARNING : NONE; -#endif } }
diff --git a/components/security_state/core/security_state_unittest.cc b/components/security_state/core/security_state_unittest.cc index 00753de0..9b9ddad 100644 --- a/components/security_state/core/security_state_unittest.cc +++ b/components/security_state/core/security_state_unittest.cc
@@ -10,7 +10,6 @@ #include "base/command_line.h" #include "base/memory/ptr_util.h" #include "base/test/histogram_tester.h" -#include "components/security_state/core/switches.h" #include "net/cert/x509_certificate.h" #include "net/ssl/ssl_cipher_suite_names.h" #include "net/ssl/ssl_connection_status_flags.h" @@ -266,10 +265,8 @@ } // Tests that password fields cause the security level to be downgraded -// to HTTP_SHOW_WARNING when the command-line switch is set. +// to HTTP_SHOW_WARNING. TEST(SecurityStateTest, PasswordFieldWarning) { - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - switches::kMarkHttpAs, switches::kMarkHttpWithPasswordsOrCcWithChip); TestSecurityStateHelper helper; helper.SetUrl(GURL(kHttpUrl)); helper.set_displayed_password_field_on_http(true); @@ -280,10 +277,8 @@ } // Tests that password fields cause the security level to be downgraded -// to HTTP_SHOW_WARNING on pseudo URLs when the command-line switch is set. +// to HTTP_SHOW_WARNING on pseudo URLs. TEST(SecurityStateTest, PasswordFieldWarningOnPseudoUrls) { - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - switches::kMarkHttpAs, switches::kMarkHttpWithPasswordsOrCcWithChip); for (const char* const url : kPseudoUrls) { TestSecurityStateHelper helper; helper.SetUrl(GURL(url)); @@ -296,10 +291,8 @@ } // Tests that credit card fields cause the security level to be downgraded -// to HTTP_SHOW_WARNING when the command-line switch is set. +// to HTTP_SHOW_WARNING. TEST(SecurityStateTest, CreditCardFieldWarning) { - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - switches::kMarkHttpAs, switches::kMarkHttpWithPasswordsOrCcWithChip); TestSecurityStateHelper helper; helper.SetUrl(GURL(kHttpUrl)); helper.set_displayed_credit_card_field_on_http(true); @@ -310,10 +303,8 @@ } // Tests that credit card fields cause the security level to be downgraded -// to HTTP_SHOW_WARNING on pseudo URLs when the command-line switch is set. +// to HTTP_SHOW_WARNING on pseudo URLs. TEST(SecurityStateTest, CreditCardFieldWarningOnPseudoUrls) { - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - switches::kMarkHttpAs, switches::kMarkHttpWithPasswordsOrCcWithChip); for (const char* const url : kPseudoUrls) { TestSecurityStateHelper helper; helper.SetUrl(GURL(url)); @@ -358,8 +349,6 @@ TEST(SecurityStateTest, MarkHttpAsStatusHistogram) { const char* kHistogramName = "SSL.MarkHttpAsStatus"; base::HistogramTester histograms; - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - switches::kMarkHttpAs, switches::kMarkHttpWithPasswordsOrCcWithChip); TestSecurityStateHelper helper; helper.SetUrl(GURL(kHttpUrl));
diff --git a/components/security_state/core/switches.cc b/components/security_state/core/switches.cc index ced029d..c8542f8 100644 --- a/components/security_state/core/switches.cc +++ b/components/security_state/core/switches.cc
@@ -9,10 +9,7 @@ // Use to opt-in to marking HTTP as non-secure. const char kMarkHttpAs[] = "mark-non-secure-as"; -const char kMarkHttpAsNeutral[] = "neutral"; const char kMarkHttpAsDangerous[] = "non-secure"; -const char kMarkHttpWithPasswordsOrCcWithChip[] = - "show-non-secure-passwords-cc-ui"; } // namespace switches } // namespace security_state
diff --git a/components/security_state/core/switches.h b/components/security_state/core/switches.h index 1c6ba29..4216cd2 100644 --- a/components/security_state/core/switches.h +++ b/components/security_state/core/switches.h
@@ -9,9 +9,7 @@ namespace switches { extern const char kMarkHttpAs[]; -extern const char kMarkHttpAsNeutral[]; extern const char kMarkHttpAsDangerous[]; -extern const char kMarkHttpWithPasswordsOrCcWithChip[]; } } // namespace security_state
diff --git a/components/sync/OWNERS b/components/sync/OWNERS index 25423c1..7f146cf 100644 --- a/components/sync/OWNERS +++ b/components/sync/OWNERS
@@ -3,3 +3,5 @@ skym@chromium.org stanisc@chromium.org zea@chromium.org + +# COMPONENT: Services>Sync
diff --git a/components/sync/test/fake_server/sessions_hierarchy.cc b/components/sync/test/fake_server/sessions_hierarchy.cc index 8cdde963..204c8cd6 100644 --- a/components/sync/test/fake_server/sessions_hierarchy.cc +++ b/components/sync/test/fake_server/sessions_hierarchy.cc
@@ -10,12 +10,16 @@ SessionsHierarchy::SessionsHierarchy() {} +SessionsHierarchy::SessionsHierarchy(const SessionsHierarchy& other) = default; + +SessionsHierarchy::SessionsHierarchy( + std::initializer_list<std::multiset<std::string>> windows) + : windows_(windows) {} + SessionsHierarchy::~SessionsHierarchy() {} void SessionsHierarchy::AddWindow(const std::string& tab) { - SessionsHierarchy::Window window; - window.insert(tab); - windows_.insert(window); + windows_.insert({tab}); } void SessionsHierarchy::AddWindow(const std::multiset<std::string>& tabs) {
diff --git a/components/sync/test/fake_server/sessions_hierarchy.h b/components/sync/test/fake_server/sessions_hierarchy.h index 8d323d4..93199ca 100644 --- a/components/sync/test/fake_server/sessions_hierarchy.h +++ b/components/sync/test/fake_server/sessions_hierarchy.h
@@ -21,6 +21,12 @@ // Creates an empty (no windows) SessionsHierachy. SessionsHierarchy(); + // Copy constructor. + SessionsHierarchy(const SessionsHierarchy& other); + + // Creates a SessionsHierarchy with specified set of windows. + SessionsHierarchy(std::initializer_list<std::multiset<std::string>> windows); + ~SessionsHierarchy(); // Add a window to the builder with one tab.
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index 3beb56b7..f74e19a 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -317,7 +317,6 @@ delegate_(delegate), site_instance_(static_cast<SiteInstanceImpl*>(site_instance)), process_(site_instance->GetProcess()), - cross_process_frame_connector_(NULL), render_frame_proxy_host_(NULL), frame_tree_(frame_tree), frame_tree_node_(frame_tree_node),
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h index 0dd8f60..50a02227 100644 --- a/content/browser/frame_host/render_frame_host_impl.h +++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -80,7 +80,6 @@ namespace content { class AssociatedInterfaceProviderImpl; -class CrossProcessFrameConnector; class FeaturePolicy; class FrameTree; class FrameTreeNode; @@ -312,18 +311,6 @@ GlobalFrameRoutingId GetGlobalFrameRoutingId(); - // This function is called when this is a swapped out RenderFrameHost that - // lives in the same process as the parent frame. The - // |cross_process_frame_connector| allows the non-swapped-out - // RenderFrameHost for a frame to communicate with the parent process - // so that it may composite drawing data. - // - // Ownership is not transfered. - void set_cross_process_frame_connector( - CrossProcessFrameConnector* cross_process_frame_connector) { - cross_process_frame_connector_ = cross_process_frame_connector; - } - void set_render_frame_proxy_host(RenderFrameProxyHost* proxy) { render_frame_proxy_host_ = proxy; } @@ -913,18 +900,6 @@ // Cache a pointer to avoid unnecessary process creation. RenderProcessHost* process_; - // |cross_process_frame_connector_| passes messages from an out-of-process - // child frame to the parent process for compositing. - // - // This is only non-NULL when this is the swapped out RenderFrameHost in - // the same site instance as this frame's parent. - // - // See the class comment above CrossProcessFrameConnector for more - // information. - // - // This will move to RenderFrameProxyHost when that class is created. - CrossProcessFrameConnector* cross_process_frame_connector_; - // The proxy created for this RenderFrameHost. It is used to send and receive // IPC messages while in swapped out state. // TODO(nasko): This can be removed once we don't have a swapped out state on
diff --git a/content/browser/frame_host/render_widget_host_view_child_frame_unittest.cc b/content/browser/frame_host/render_widget_host_view_child_frame_unittest.cc index ea75cfde..dfbba97 100644 --- a/content/browser/frame_host/render_widget_host_view_child_frame_unittest.cc +++ b/content/browser/frame_host/render_widget_host_view_child_frame_unittest.cc
@@ -150,7 +150,16 @@ return frame; } -TEST_F(RenderWidgetHostViewChildFrameTest, VisibilityTest) { +// http://crbug.com/696919 +#if defined(OS_WIN) +#define MAYBE_VisibilityTest DISABLED_VisibilityTest +#define MAYBE_SwapCompositorFrame DISABLED_SwapCompositorFrame +#else +#define MAYBE_VisibilityTest VisibilityTest +#define MAYBE_SwapCompositorFrame SwapCompositorFrame +#endif + +TEST_F(RenderWidgetHostViewChildFrameTest, MAYBE_VisibilityTest) { view_->Show(); ASSERT_TRUE(view_->IsShowing()); @@ -158,13 +167,6 @@ ASSERT_FALSE(view_->IsShowing()); } -// http://crbug.com/696919 -#if defined(OS_WIN) -#define MAYBE_SwapCompositorFrame DISABLED_SwapCompositorFrame -#else -#define MAYBE_SwapCompositorFrame SwapCompositorFrame -#endif - // Verify that OnSwapCompositorFrame behavior is correct when a delegated // frame is received from a renderer process. TEST_F(RenderWidgetHostViewChildFrameTest, MAYBE_SwapCompositorFrame) {
diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc index ffd64e3e..d2c861f 100644 --- a/content/browser/renderer_host/delegated_frame_host.cc +++ b/content/browser/renderer_host/delegated_frame_host.cc
@@ -477,7 +477,7 @@ cc::SurfaceId surface_id(frame_sink_id_, local_surface_id_); cc::SurfaceInfo surface_info(surface_id, frame_device_scale_factor, frame_size); - client_->DelegatedFrameHostGetLayer()->SetShowSurface( + client_->DelegatedFrameHostGetLayer()->SetShowPrimarySurface( surface_info, manager->reference_factory()); current_surface_size_ = frame_size; current_scale_factor_ = frame_device_scale_factor;
diff --git a/content/browser/renderer_host/offscreen_canvas_compositor_frame_sink.cc b/content/browser/renderer_host/offscreen_canvas_compositor_frame_sink.cc index e52843a..21b35f9 100644 --- a/content/browser/renderer_host/offscreen_canvas_compositor_frame_sink.cc +++ b/content/browser/renderer_host/offscreen_canvas_compositor_frame_sink.cc
@@ -52,17 +52,6 @@ support_.EvictFrame(); } -void OffscreenCanvasCompositorFrameSink::Require( - const cc::LocalSurfaceId& local_surface_id, - const cc::SurfaceSequence& sequence) { - support_.Require(local_surface_id, sequence); -} - -void OffscreenCanvasCompositorFrameSink::Satisfy( - const cc::SurfaceSequence& sequence) { - support_.Satisfy(sequence); -} - void OffscreenCanvasCompositorFrameSink::DidReceiveCompositorFrameAck() { if (client_) client_->DidReceiveCompositorFrameAck();
diff --git a/content/browser/renderer_host/offscreen_canvas_compositor_frame_sink.h b/content/browser/renderer_host/offscreen_canvas_compositor_frame_sink.h index af97705..819d1d7 100644 --- a/content/browser/renderer_host/offscreen_canvas_compositor_frame_sink.h +++ b/content/browser/renderer_host/offscreen_canvas_compositor_frame_sink.h
@@ -33,9 +33,6 @@ void SubmitCompositorFrame(const cc::LocalSurfaceId& local_surface_id, cc::CompositorFrame frame) override; void EvictFrame() override; - void Require(const cc::LocalSurfaceId& local_surface_id, - const cc::SurfaceSequence& sequence) override; - void Satisfy(const cc::SurfaceSequence& sequence) override; // Overridden from cc::CompositorFrameSinkSupportClient: void DidReceiveCompositorFrameAck() override;
diff --git a/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationProvider.java b/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationProvider.java index 80a80cd..5fd2b411 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationProvider.java +++ b/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationProvider.java
@@ -10,7 +10,6 @@ import android.content.pm.PackageManager; import android.view.Surface; -import org.chromium.base.ApplicationStatus; import org.chromium.base.Log; import org.chromium.base.ThreadUtils; import org.chromium.base.annotations.CalledByNative; @@ -71,14 +70,15 @@ } } - // Note that WindowAndroid may be null if the tab is being reparented. @CalledByNative - static void lockOrientation(@Nullable WindowAndroid window, byte orientation) { - lockOrientation(window, orientation, ApplicationStatus.getLastTrackedFocusedActivity()); - } + public static void lockOrientation(@Nullable WindowAndroid window, byte webScreenOrientation) { + // WindowAndroid may be null if the tab is being reparented. + if (window == null) return; + Activity activity = window.getActivity().get(); - public static void lockOrientation(@Nullable WindowAndroid window, byte webScreenOrientation, - Activity activity) { + // Locking orientation is only supported for web contents that have an associated activity. + // Note that we can't just use the focused activity, as that would lead to bugs where + // unlockOrientation unlocks a different activity to the one that was locked. if (activity == null) return; int orientation = getOrientationFromWebScreenOrientations(webScreenOrientation, window, @@ -90,13 +90,16 @@ activity.setRequestedOrientation(orientation); } - // Note that WindowAndroid may be null if the tab is being reparented. @CalledByNative static void unlockOrientation(@Nullable WindowAndroid window) { - Activity activity = ApplicationStatus.getLastTrackedFocusedActivity(); - if (activity == null) { - return; - } + // WindowAndroid may be null if the tab is being reparented. + if (window == null) return; + Activity activity = window.getActivity().get(); + + // Locking orientation is only supported for web contents that have an associated activity. + // Note that we can't just use the focused activity, as that would lead to bugs where + // unlockOrientation unlocks a different activity to the one that was locked. + if (activity == null) return; int defaultOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ScreenOrientationListenerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ScreenOrientationListenerTest.java index 4f3e373..a03f016 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/ScreenOrientationListenerTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/ScreenOrientationListenerTest.java
@@ -229,7 +229,8 @@ ThreadUtils.runOnUiThreadBlocking(new Runnable() { @Override public void run() { - ScreenOrientationProvider.lockOrientation(null, (byte) orientationValue); + ScreenOrientationProvider.lockOrientation( + getContentViewCore().getWindowAndroid(), (byte) orientationValue); } }); mCallbackHelper.waitForCallback(callCount);
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/VideoFullscreenOrientationLockTest.java b/content/public/android/javatests/src/org/chromium/content/browser/VideoFullscreenOrientationLockTest.java index 642f5666..133fc31 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/VideoFullscreenOrientationLockTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/VideoFullscreenOrientationLockTest.java
@@ -4,6 +4,7 @@ package org.chromium.content.browser; +import android.content.pm.ActivityInfo; import android.graphics.Rect; import android.support.test.filters.MediumTest; @@ -22,7 +23,8 @@ import java.util.concurrent.TimeoutException; /** - * Integration tests for the feature that auto lock the orientation when a video goes fullscreen. + * Integration tests for the feature that auto locks the orientation when a video goes fullscreen. + * See also chrome layer org.chromium.chrome.browser.VideoFullscreenOrientationLockChromeTest */ @CommandLineFlags.Add({ "enable-features=VideoFullscreenOrientationLock", ContentSwitches.DISABLE_GESTURE_REQUIREMENT_FOR_MEDIA_PLAYBACK }) @@ -42,6 +44,11 @@ ); } + private boolean isScreenOrientationLocked() { + return getActivity().getRequestedOrientation() + != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + } + private boolean isScreenOrientationLandscape() throws InterruptedException, TimeoutException { StringBuilder sb = new StringBuilder(); sb.append("(function() {"); @@ -52,12 +59,12 @@ .equals("true"); } - private void waitForLandscapeOrientation() throws InterruptedException { + private void waitUntilLockedToLandscape() throws InterruptedException { CriteriaHelper.pollInstrumentationThread(new Criteria() { @Override public boolean isSatisfied() { try { - return isScreenOrientationLandscape(); + return isScreenOrientationLocked() && isScreenOrientationLandscape(); } catch (InterruptedException e) { return false; } catch (TimeoutException e) { @@ -67,6 +74,15 @@ }); } + private void waitUntilUnlocked() throws InterruptedException { + CriteriaHelper.pollInstrumentationThread(new Criteria() { + @Override + public boolean isSatisfied() { + return !isScreenOrientationLocked(); + } + }); + } + // TODO(mlamouri): move this constants and the controlBar(), fullscreenButton() methods to a // dedicated helper file for media tests. private static final int CONTROLS_HEIGHT = 35; @@ -116,8 +132,8 @@ assertTrue(clickFullscreenButton()); waitForContentsFullscreenState(true); - // Should be landscape now, `waitForLandscapeOrientation` will throw otherwise. - waitForLandscapeOrientation(); + // Should be locked to landscape now, `waitUntilLockedToLandscape` will throw otherwise. + waitUntilLockedToLandscape(); // Because of the fullscreen animation, the click on the exit fullscreen button will fail // roughly 10% of the time. Settling down the UI reduces the flake to 0%. @@ -126,6 +142,7 @@ // Leave fullscreen by clicking back on the button. assertTrue(clickFullscreenButton()); waitForContentsFullscreenState(false); + waitUntilUnlocked(); } @MediumTest @@ -142,11 +159,59 @@ assertTrue(DOMUtils.clickNode(getContentViewCore(), "fullscreen")); waitForContentsFullscreenState(true); - // Should be landscape now, `waitForLandscapeOrientation` will throw otherwise. - waitForLandscapeOrientation(); + // Should be locked to landscape now, `waitUntilLockedToLandscape` will throw otherwise. + waitUntilLockedToLandscape(); // Leave fullscreen from API. DOMUtils.exitFullscreen(getWebContents()); waitForContentsFullscreenState(false); + waitUntilUnlocked(); + } + + @MediumTest + @Feature({"VideoFullscreenOrientationLock"}) + public void testExitFullscreenByRemovingVideo() throws Exception { + if (DeviceFormFactor.isTablet(getInstrumentation().getContext())) return; + + // Start playback to guarantee it's properly loaded. + assertTrue(DOMUtils.isMediaPaused(getWebContents(), VIDEO_ID)); + DOMUtils.playMedia(getWebContents(), VIDEO_ID); + DOMUtils.waitForMediaPlay(getWebContents(), VIDEO_ID); + + // Trigger requestFullscreen() via a click on a button. + assertTrue(DOMUtils.clickNode(getContentViewCore(), "fullscreen")); + waitForContentsFullscreenState(true); + + // Should be locked to landscape now, `waitUntilLockedToLandscape` will throw otherwise. + waitUntilLockedToLandscape(); + + // Leave fullscreen by removing video element from page. + JavaScriptUtils.executeJavaScriptAndWaitForResult( + getWebContents(), "document.body.innerHTML = '';"); + waitForContentsFullscreenState(false); + waitUntilUnlocked(); + } + + @MediumTest + @Feature({"VideoFullscreenOrientationLock"}) + public void testExitFullscreenWithNavigation() throws Exception { + if (DeviceFormFactor.isTablet(getInstrumentation().getContext())) return; + + // Start playback to guarantee it's properly loaded. + assertTrue(DOMUtils.isMediaPaused(getWebContents(), VIDEO_ID)); + DOMUtils.playMedia(getWebContents(), VIDEO_ID); + DOMUtils.waitForMediaPlay(getWebContents(), VIDEO_ID); + + // Trigger requestFullscreen() via a click on a button. + assertTrue(DOMUtils.clickNode(getContentViewCore(), "fullscreen")); + waitForContentsFullscreenState(true); + + // Should be locked to landscape now, `waitUntilLockedToLandscape` will throw otherwise. + waitUntilLockedToLandscape(); + + // Leave fullscreen by navigating page. + JavaScriptUtils.executeJavaScriptAndWaitForResult(getWebContents(), "location.reload();"); + waitForContentsFullscreenState(false); + waitUntilUnlocked(); } }
diff --git a/content/public/test/web_contents_tester.h b/content/public/test/web_contents_tester.h index bb2b8b9..ec12cecf 100644 --- a/content/public/test/web_contents_tester.h +++ b/content/public/test/web_contents_tester.h
@@ -6,11 +6,17 @@ #define CONTENT_PUBLIC_TEST_WEB_CONTENTS_TESTER_H_ #include <string> +#include <vector> #include "content/public/browser/site_instance.h" #include "ui/base/page_transition_types.h" class GURL; +class SkBitmap; + +namespace gfx { +class Size; +} namespace content { @@ -124,6 +130,18 @@ // Returns headers that were passed in the previous SaveFrameWithHeaders(...) // call. virtual const std::string& GetSaveFrameHeaders() = 0; + + // Returns whether a download request triggered via DownloadImage() is in + // progress for |url|. + virtual bool HasPendingDownloadImage(const GURL& url) = 0; + + // Simulates a request completion for DownloadImage(). For convenience, it + // returns whether an actual download associated to |url| was pending. + virtual bool TestDidDownloadImage( + const GURL& url, + int http_status_code, + const std::vector<SkBitmap>& bitmaps, + const std::vector<gfx::Size>& original_bitmap_sizes) = 0; }; } // namespace content
diff --git a/content/shell/android/java/src/org/chromium/content_shell/Shell.java b/content/shell/android/java/src/org/chromium/content_shell/Shell.java index 6215bf40..f2afcfbd 100644 --- a/content/shell/android/java/src/org/chromium/content_shell/Shell.java +++ b/content/shell/android/java/src/org/chromium/content_shell/Shell.java
@@ -356,13 +356,17 @@ @Override public void enterFullscreenVideo(View view, boolean isVideoLoaded) { super.enterFullscreenVideo(view, isVideoLoaded); - mContentViewRenderView.setOverlayVideoMode(true); + if (mContentViewRenderView != null) { + mContentViewRenderView.setOverlayVideoMode(true); + } } @Override public void exitFullscreenVideo() { super.exitFullscreenVideo(); - mContentViewRenderView.setOverlayVideoMode(false); + if (mContentViewRenderView != null) { + mContentViewRenderView.setOverlayVideoMode(false); + } } }; }
diff --git a/content/test/test_web_contents.cc b/content/test/test_web_contents.cc index 34360030..4cfe826 100644 --- a/content/test/test_web_contents.cc +++ b/content/test/test_web_contents.cc
@@ -93,7 +93,10 @@ bool bypass_cache, const ImageDownloadCallback& callback) { static int g_next_image_download_id = 0; - return ++g_next_image_download_id; + ++g_next_image_download_id; + pending_image_downloads_[url].emplace_back(g_next_image_download_id, + callback); + return g_next_image_download_id; } void TestWebContents::TestDidNavigate(RenderFrameHost* render_frame_host, @@ -186,6 +189,24 @@ return save_frame_headers_; } +bool TestWebContents::HasPendingDownloadImage(const GURL& url) { + return !pending_image_downloads_[url].empty(); +} + +bool TestWebContents::TestDidDownloadImage( + const GURL& url, + int http_status_code, + const std::vector<SkBitmap>& bitmaps, + const std::vector<gfx::Size>& original_bitmap_sizes) { + if (!HasPendingDownloadImage(url)) + return false; + int id = pending_image_downloads_[url].front().first; + ImageDownloadCallback callback = pending_image_downloads_[url].front().second; + pending_image_downloads_[url].pop_front(); + callback.Run(id, http_status_code, url, bitmaps, original_bitmap_sizes); + return true; +} + bool TestWebContents::CrossProcessNavigationPending() { if (IsBrowserSideNavigationEnabled()) { return GetRenderManager()->speculative_render_frame_host_ != nullptr;
diff --git a/content/test/test_web_contents.h b/content/test/test_web_contents.h index 1deb67a..82d719d 100644 --- a/content/test/test_web_contents.h +++ b/content/test/test_web_contents.h
@@ -7,6 +7,8 @@ #include <stdint.h> +#include <list> +#include <map> #include <string> #include "content/browser/web_contents/web_contents_impl.h" @@ -17,6 +19,11 @@ class GURL; class Referrer; +class SkBitmap; + +namespace gfx { +class Size; +} namespace content { @@ -73,6 +80,12 @@ int item_sequence_number, int document_sequence_number); const std::string& GetSaveFrameHeaders() override; + bool HasPendingDownloadImage(const GURL& url) override; + bool TestDidDownloadImage( + const GURL& url, + int http_status_code, + const std::vector<SkBitmap>& bitmaps, + const std::vector<gfx::Size>& original_bitmap_sizes) override; // True if a cross-site navigation is pending. bool CrossProcessNavigationPending(); @@ -161,6 +174,9 @@ int expect_set_history_offset_and_length_history_offset_; int expect_set_history_offset_and_length_history_length_; std::string save_frame_headers_; + // Map keyed by image URL. Values are <id, callback> pairs. + std::map<GURL, std::list<std::pair<int, ImageDownloadCallback>>> + pending_image_downloads_; }; } // namespace content
diff --git a/extensions/renderer/api_binding.cc b/extensions/renderer/api_binding.cc index 3ff9e2b4..e16bef1 100644 --- a/extensions/renderer/api_binding.cc +++ b/extensions/renderer/api_binding.cc
@@ -75,16 +75,14 @@ } // namespace struct APIBinding::MethodData { - MethodData(std::string full_name, - const base::ListValue& method_signature) - : full_name(std::move(full_name)), - signature(method_signature) {} + MethodData(std::string full_name, const APISignature* signature) + : full_name(std::move(full_name)), signature(signature) {} // The fully-qualified name of this api (e.g. runtime.sendMessage instead of // sendMessage). std::string full_name; // The expected API signature. - APISignature signature; + const APISignature* signature; // The callback used by the v8 function. APIBinding::HandlerCallback callback; }; @@ -157,9 +155,11 @@ const base::ListValue* params = nullptr; CHECK(func_dict->GetList("parameters", ¶ms)); - methods_[name] = base::MakeUnique<MethodData>( - base::StringPrintf("%s.%s", api_name_.c_str(), name.c_str()), - *params); + auto signature = base::MakeUnique<APISignature>(*params); + std::string full_name = + base::StringPrintf("%s.%s", api_name_.c_str(), name.c_str()); + methods_[name] = base::MakeUnique<MethodData>(full_name, signature.get()); + type_refs->AddAPIMethodSignature(full_name, std::move(signature)); } } @@ -251,16 +251,11 @@ } } - binding_hooks_->InitializeInContext(context, api_name_); + binding_hooks_->InitializeInContext(context); return object; } -v8::Local<v8::Object> APIBinding::GetJSHookInterface( - v8::Local<v8::Context> context) { - return binding_hooks_->GetJSHookInterface(api_name_, context); -} - void APIBinding::InitializeTemplate(v8::Isolate* isolate) { DCHECK(object_template_.IsEmpty()); v8::Local<v8::ObjectTemplate> object_template = @@ -271,7 +266,7 @@ DCHECK(method.callback.is_null()); method.callback = base::Bind(&APIBinding::HandleCall, weak_factory_.GetWeakPtr(), - method.full_name, &method.signature); + method.full_name, method.signature); object_template->Set( gin::StringToSymbol(isolate, key_value.first), @@ -425,9 +420,8 @@ v8::Local<v8::Function> custom_callback; { v8::TryCatch try_catch(isolate); - APIBindingHooks::RequestResult hooks_result = - binding_hooks_->RunHooks(api_name_, name, context, - signature, &argument_list, *type_refs_); + APIBindingHooks::RequestResult hooks_result = binding_hooks_->RunHooks( + name, context, signature, &argument_list, *type_refs_); switch (hooks_result.code) { case APIBindingHooks::RequestResult::INVALID_INVOCATION:
diff --git a/extensions/renderer/api_binding.h b/extensions/renderer/api_binding.h index acb4c43..870f8c1 100644 --- a/extensions/renderer/api_binding.h +++ b/extensions/renderer/api_binding.h
@@ -74,9 +74,7 @@ v8::Isolate* isolate, const AvailabilityCallback& is_available); - // Returns the JS interface to use when registering hooks with legacy custom - // bindings. - v8::Local<v8::Object> GetJSHookInterface(v8::Local<v8::Context> context); + APIBindingHooks* hooks() { return binding_hooks_.get(); } private: // Initializes the object_template_ for this API. Called lazily when the
diff --git a/extensions/renderer/api_binding_bridge.cc b/extensions/renderer/api_binding_bridge.cc index e8321ff4..d3b5805e 100644 --- a/extensions/renderer/api_binding_bridge.cc +++ b/extensions/renderer/api_binding_bridge.cc
@@ -4,6 +4,11 @@ #include "extensions/renderer/api_binding_bridge.h" +#include "base/values.h" +#include "extensions/renderer/api_binding_hooks.h" +#include "extensions/renderer/api_request_handler.h" +#include "extensions/renderer/api_signature.h" +#include "extensions/renderer/api_type_reference_map.h" #include "gin/converter.h" #include "gin/object_template_builder.h" @@ -23,13 +28,18 @@ gin::WrapperInfo APIBindingBridge::kWrapperInfo = {gin::kEmbedderNativeGin}; -APIBindingBridge::APIBindingBridge(v8::Local<v8::Context> context, +APIBindingBridge::APIBindingBridge(const APITypeReferenceMap* type_refs, + APIRequestHandler* request_handler, + APIBindingHooks* hooks, + v8::Local<v8::Context> context, v8::Local<v8::Value> api_object, - v8::Local<v8::Value> js_hook_interface, const std::string& extension_id, const std::string& context_type, const binding::RunJSFunction& run_js) - : extension_id_(extension_id), + : type_refs_(type_refs), + request_handler_(request_handler), + hooks_(hooks), + extension_id_(extension_id), context_type_(context_type), run_js_(run_js) { v8::Isolate* isolate = context->GetIsolate(); @@ -40,6 +50,7 @@ NOTREACHED(); return; } + v8::Local<v8::Object> js_hook_interface = hooks_->GetJSHookInterface(context); result = wrapper->SetPrivate(context, GetPrivatePropertyName(isolate, kHookInterfaceKey), @@ -52,7 +63,8 @@ gin::ObjectTemplateBuilder APIBindingBridge::GetObjectTemplateBuilder( v8::Isolate* isolate) { return Wrappable<APIBindingBridge>::GetObjectTemplateBuilder(isolate) - .SetMethod("registerCustomHook", &APIBindingBridge::RegisterCustomHook); + .SetMethod("registerCustomHook", &APIBindingBridge::RegisterCustomHook) + .SetMethod("sendRequest", &APIBindingBridge::SendRequest); } void APIBindingBridge::RegisterCustomHook(v8::Isolate* isolate, @@ -96,4 +108,29 @@ run_js_.Run(function, context, arraysize(args), args); } +void APIBindingBridge::SendRequest( + gin::Arguments* arguments, + const std::string& name, + const std::vector<v8::Local<v8::Value>>& request_args) { + v8::Isolate* isolate = arguments->isolate(); + v8::HandleScope handle_scope(isolate); + v8::Local<v8::Object> holder; + CHECK(arguments->GetHolder(&holder)); + v8::Local<v8::Context> context = holder->CreationContext(); + + const APISignature* signature = type_refs_->GetAPIMethodSignature(name); + DCHECK(signature); + + std::unique_ptr<base::ListValue> converted_arguments; + v8::Local<v8::Function> callback; + std::string error; + CHECK(signature->ParseArgumentsToJSON(context, request_args, *type_refs_, + &converted_arguments, &callback, + &error)); + + request_handler_->StartRequest(context, name, std::move(converted_arguments), + callback, + hooks_->GetCustomJSCallback(name, context)); +} + } // namespace extensions
diff --git a/extensions/renderer/api_binding_bridge.h b/extensions/renderer/api_binding_bridge.h index cfd85ea6..3a3fe64 100644 --- a/extensions/renderer/api_binding_bridge.h +++ b/extensions/renderer/api_binding_bridge.h
@@ -12,16 +12,25 @@ #include "gin/wrappable.h" #include "v8/include/v8.h" +namespace gin { +class Arguments; +} + namespace extensions { +class APIBindingHooks; +class APIRequestHandler; +class APITypeReferenceMap; // An object that serves as a bridge between the current JS-centric bindings and // the new native bindings system. This basically needs to conform to the public // methods of the Binding prototype in binding.js. class APIBindingBridge final : public gin::Wrappable<APIBindingBridge> { public: - APIBindingBridge(v8::Local<v8::Context> context, + APIBindingBridge(const APITypeReferenceMap* type_refs, + APIRequestHandler* request_handler, + APIBindingHooks* hooks, + v8::Local<v8::Context> context, v8::Local<v8::Value> api_object, - v8::Local<v8::Value> js_hook_interface, const std::string& extension_id, const std::string& context_type, const binding::RunJSFunction& run_js); @@ -45,6 +54,21 @@ void RegisterCustomHook(v8::Isolate* isolate, v8::Local<v8::Function> function); + // A handler to initiate an API request through the APIRequestHandler. A + // replacement for custom bindings that utilize require('sendRequest'). + void SendRequest(gin::Arguments* arguments, + const std::string& name, + const std::vector<v8::Local<v8::Value>>& request_args); + + // Type references. Guaranteed to outlive this object. + const APITypeReferenceMap* type_refs_; + + // The request handler. Guaranteed to outlive this object. + APIRequestHandler* request_handler_; + + // The hooks associated with this API. Guaranteed to outlive this object. + APIBindingHooks* hooks_; + // The id of the extension that owns the context this belongs to. std::string extension_id_;
diff --git a/extensions/renderer/api_binding_hooks.cc b/extensions/renderer/api_binding_hooks.cc index 2d07276..b284071 100644 --- a/extensions/renderer/api_binding_hooks.cc +++ b/extensions/renderer/api_binding_hooks.cc
@@ -201,8 +201,9 @@ APIBindingHooks::RequestResult::RequestResult(const RequestResult& other) = default; -APIBindingHooks::APIBindingHooks(const binding::RunJSFunctionSync& run_js) - : run_js_(run_js) {} +APIBindingHooks::APIBindingHooks(const std::string& api_name, + const binding::RunJSFunctionSync& run_js) + : api_name_(api_name), run_js_(run_js) {} APIBindingHooks::~APIBindingHooks() {} void APIBindingHooks::RegisterHandleRequest(const std::string& method_name, @@ -218,7 +219,6 @@ } APIBindingHooks::RequestResult APIBindingHooks::RunHooks( - const std::string& api_name, const std::string& method_name, v8::Local<v8::Context> context, const APISignature* signature, @@ -239,7 +239,7 @@ // Harder case: looking up a custom hook registered on the context (since // these are JS, each context has a separate instance). v8::Local<v8::Object> hook_interface_object = - GetJSHookInterfaceObject(api_name, context, false); + GetJSHookInterfaceObject(api_name_, context, false); if (hook_interface_object.IsEmpty()) return RequestResult(RequestResult::NOT_HANDLED); @@ -315,9 +315,7 @@ return result; } -void APIBindingHooks::InitializeInContext( - v8::Local<v8::Context> context, - const std::string& api_name) { +void APIBindingHooks::InitializeInContext(v8::Local<v8::Context> context) { if (js_hooks_source_.IsEmpty()) return; @@ -332,15 +330,29 @@ v8::Local<v8::Function> function; if (!gin::ConvertFromV8(context->GetIsolate(), func_as_value, &function)) return; - v8::Local<v8::Value> api_hooks = GetJSHookInterface(api_name, context); + v8::Local<v8::Value> api_hooks = GetJSHookInterface(context); v8::Local<v8::Value> args[] = {api_hooks}; run_js_.Run(function, context, arraysize(args), args); } v8::Local<v8::Object> APIBindingHooks::GetJSHookInterface( - const std::string& api_name, v8::Local<v8::Context> context) { - return GetJSHookInterfaceObject(api_name, context, true); + return GetJSHookInterfaceObject(api_name_, context, true); +} + +v8::Local<v8::Function> APIBindingHooks::GetCustomJSCallback( + const std::string& name, + v8::Local<v8::Context> context) { + v8::Local<v8::Object> hooks = + GetJSHookInterfaceObject(api_name_, context, false); + if (hooks.IsEmpty()) + return v8::Local<v8::Function>(); + JSHookInterface* hook_interface = nullptr; + gin::Converter<JSHookInterface*>::FromV8(context->GetIsolate(), hooks, + &hook_interface); + CHECK(hook_interface); + + return hook_interface->GetCustomCallback(name, context->GetIsolate()); } bool APIBindingHooks::UpdateArguments(
diff --git a/extensions/renderer/api_binding_hooks.h b/extensions/renderer/api_binding_hooks.h index 4896771a..80c7eb2 100644 --- a/extensions/renderer/api_binding_hooks.h +++ b/extensions/renderer/api_binding_hooks.h
@@ -63,7 +63,8 @@ std::vector<v8::Local<v8::Value>>*, const APITypeReferenceMap&)>; - explicit APIBindingHooks(const binding::RunJSFunctionSync& run_js); + APIBindingHooks(const std::string& api_name, + const binding::RunJSFunctionSync& run_js); ~APIBindingHooks(); // Register a custom binding to handle requests. @@ -76,21 +77,22 @@ v8::Global<v8::String> resource_name); // Initializes JS hooks within a context. - void InitializeInContext(v8::Local<v8::Context> context, - const std::string& api_name); + void InitializeInContext(v8::Local<v8::Context> context); // Looks for any custom hooks associated with the given request, and, if any // are found, runs them. Returns the result of running the hooks, if any. - RequestResult RunHooks(const std::string& api_name, - const std::string& method_name, + RequestResult RunHooks(const std::string& method_name, v8::Local<v8::Context> context, const APISignature* signature, std::vector<v8::Local<v8::Value>>* arguments, const APITypeReferenceMap& type_refs); // Returns a JS interface that can be used to register hooks. - v8::Local<v8::Object> GetJSHookInterface(const std::string& api_name, - v8::Local<v8::Context> context); + v8::Local<v8::Object> GetJSHookInterface(v8::Local<v8::Context> context); + + // Gets the custom-set JS callback for the given method, if one exists. + v8::Local<v8::Function> GetCustomJSCallback(const std::string& method_name, + v8::Local<v8::Context> context); private: // Updates the |arguments| by running |function| and settings arguments to the @@ -112,6 +114,9 @@ // to make exception stack traces more readable. v8::Global<v8::String> js_resource_name_; + // The name of the associated API. + std::string api_name_; + // We use synchronous JS execution here because at every point we execute JS, // it's in direct response to JS calling in. There should be no reason that // script is disabled.
diff --git a/extensions/renderer/api_binding_unittest.cc b/extensions/renderer/api_binding_unittest.cc index bac451e2..86706c7 100644 --- a/extensions/renderer/api_binding_unittest.cc +++ b/extensions/renderer/api_binding_unittest.cc
@@ -25,6 +25,8 @@ namespace { +const char kBindingName[] = "test"; + // Function spec; we use single quotes for readability and then replace them. const char kFunctions[] = "[{" @@ -180,14 +182,14 @@ void InitializeBinding() { if (!binding_hooks_) { - binding_hooks_ = - base::MakeUnique<APIBindingHooks>(binding::RunJSFunctionSync()); + binding_hooks_ = base::MakeUnique<APIBindingHooks>( + kBindingName, binding::RunJSFunctionSync()); } event_handler_ = base::MakeUnique<APIEventHandler>( base::Bind(&RunFunctionOnGlobalAndIgnoreResult), base::Bind(&OnEventListenersChanged)); binding_ = base::MakeUnique<APIBinding>( - "test", binding_functions_.get(), binding_types_.get(), + kBindingName, binding_functions_.get(), binding_types_.get(), binding_events_.get(), binding_properties_.get(), create_custom_type_, std::move(binding_hooks_), &type_refs_, request_handler_.get(), event_handler_.get()); @@ -667,7 +669,7 @@ // Register a hook for the test.oneString method. auto hooks = base::MakeUnique<APIBindingHooks>( - base::Bind(&RunFunctionOnGlobalAndReturnHandle)); + kBindingName, base::Bind(&RunFunctionOnGlobalAndReturnHandle)); bool did_call = false; auto hook = [](bool* did_call, const APISignature* signature, v8::Local<v8::Context> context, @@ -710,7 +712,7 @@ TEST_F(APIBindingUnittest, TestJSCustomHook) { // Register a hook for the test.oneString method. auto hooks = base::MakeUnique<APIBindingHooks>( - base::Bind(&RunFunctionOnGlobalAndReturnHandle)); + kBindingName, base::Bind(&RunFunctionOnGlobalAndReturnHandle)); v8::HandleScope handle_scope(isolate()); v8::Local<v8::Context> context = ContextLocal(); @@ -762,7 +764,7 @@ TEST_F(APIBindingUnittest, TestUpdateArgumentsPreValidate) { // Register a hook for the test.oneString method. auto hooks = base::MakeUnique<APIBindingHooks>( - base::Bind(&RunFunctionOnGlobalAndReturnHandle)); + kBindingName, base::Bind(&RunFunctionOnGlobalAndReturnHandle)); v8::HandleScope handle_scope(isolate()); v8::Local<v8::Context> context = ContextLocal(); @@ -823,7 +825,7 @@ // Register a hook for the test.oneString method. auto hooks = base::MakeUnique<APIBindingHooks>( - base::Bind(run_js_and_allow_error)); + kBindingName, base::Bind(run_js_and_allow_error)); v8::HandleScope handle_scope(isolate()); v8::Local<v8::Context> context = ContextLocal(); @@ -865,7 +867,7 @@ TEST_F(APIBindingUnittest, TestReturningResultFromCustomJSHook) { // Register a hook for the test.oneString method. auto hooks = base::MakeUnique<APIBindingHooks>( - base::Bind(&RunFunctionOnGlobalAndReturnHandle)); + kBindingName, base::Bind(&RunFunctionOnGlobalAndReturnHandle)); v8::HandleScope handle_scope(isolate()); v8::Local<v8::Context> context = ContextLocal(); @@ -926,7 +928,7 @@ }; // Register a hook for the test.oneString method. auto hooks = base::MakeUnique<APIBindingHooks>( - base::Bind(run_js_and_expect_error)); + kBindingName, base::Bind(run_js_and_expect_error)); v8::HandleScope handle_scope(isolate()); v8::Local<v8::Context> context = ContextLocal(); @@ -969,7 +971,7 @@ // Register a hook for the test.oneString method. auto hooks = base::MakeUnique<APIBindingHooks>( - base::Bind(&RunFunctionOnGlobalAndReturnHandle)); + kBindingName, base::Bind(&RunFunctionOnGlobalAndReturnHandle)); bool did_call = false; auto hook = [](bool* did_call, const APISignature* signature, v8::Local<v8::Context> context, @@ -1032,7 +1034,7 @@ TEST_F(APIBindingUnittest, TestUpdateArgumentsPostValidate) { // Register a hook for the test.oneString method. auto hooks = base::MakeUnique<APIBindingHooks>( - base::Bind(&RunFunctionOnGlobalAndReturnHandle)); + kBindingName, base::Bind(&RunFunctionOnGlobalAndReturnHandle)); v8::HandleScope handle_scope(isolate()); v8::Local<v8::Context> context = ContextLocal();
diff --git a/extensions/renderer/api_bindings_system.cc b/extensions/renderer/api_bindings_system.cc index 02680c4..fab9fe83 100644 --- a/extensions/renderer/api_bindings_system.cc +++ b/extensions/renderer/api_bindings_system.cc
@@ -33,12 +33,12 @@ v8::Local<v8::Context> context, v8::Isolate* isolate, const APIBinding::AvailabilityCallback& is_available, - v8::Local<v8::Object>* hooks_interface_out) { + APIBindingHooks** hooks_out) { std::unique_ptr<APIBinding>& binding = api_bindings_[api_name]; if (!binding) binding = CreateNewAPIBinding(api_name); - if (hooks_interface_out) - *hooks_interface_out = binding->GetJSHookInterface(context); + if (hooks_out) + *hooks_out = binding->hooks(); return binding->CreateInstance(context, isolate, is_available); } @@ -65,7 +65,7 @@ hooks = std::move(iter->second); binding_hooks_.erase(iter); } else { - hooks = base::MakeUnique<APIBindingHooks>(call_js_sync_); + hooks = base::MakeUnique<APIBindingHooks>(api_name, call_js_sync_); } return base::MakeUnique<APIBinding>( @@ -113,7 +113,7 @@ << "Hook registration must happen before creating any binding instances."; std::unique_ptr<APIBindingHooks>& hooks = binding_hooks_[api_name]; if (!hooks) - hooks = base::MakeUnique<APIBindingHooks>(call_js_sync_); + hooks = base::MakeUnique<APIBindingHooks>(api_name, call_js_sync_); return hooks.get(); }
diff --git a/extensions/renderer/api_bindings_system.h b/extensions/renderer/api_bindings_system.h index c8f6e72..d8a4711 100644 --- a/extensions/renderer/api_bindings_system.h +++ b/extensions/renderer/api_bindings_system.h
@@ -24,7 +24,7 @@ } namespace extensions { -class APIRequestHandler; +class APIBindingHooks; // A class encompassing the necessary pieces to construct the JS entry points // for Extension APIs. Designed to be used on a single thread, but safe between @@ -54,7 +54,7 @@ v8::Local<v8::Context> context, v8::Isolate* isolate, const APIBinding::AvailabilityCallback& is_available, - v8::Local<v8::Object>* hooks_interface_out); + APIBindingHooks** hooks_out); // Responds to the request with the given |request_id|, calling the callback // with |response|. If |error| is non-empty, sets the last error. @@ -83,6 +83,9 @@ void RegisterCustomType(const std::string& type_name, const CustomTypeHandler& function); + APIRequestHandler* request_handler() { return &request_handler_; } + APITypeReferenceMap* type_reference_map() { return &type_reference_map_; } + private: // Creates a new APIBinding for the given |api_name|. std::unique_ptr<APIBinding> CreateNewAPIBinding(const std::string& api_name);
diff --git a/extensions/renderer/api_type_reference_map.cc b/extensions/renderer/api_type_reference_map.cc index 328cf28..bc0b129 100644 --- a/extensions/renderer/api_type_reference_map.cc +++ b/extensions/renderer/api_type_reference_map.cc
@@ -30,6 +30,24 @@ return iter == type_refs_.end() ? nullptr : iter->second.get(); } +void APITypeReferenceMap::AddAPIMethodSignature( + const std::string& name, + std::unique_ptr<APISignature> signature) { + DCHECK(api_methods_.find(name) == api_methods_.end()) + << "Cannot re-register signature for: " << name; + api_methods_[name] = std::move(signature); +} + +const APISignature* APITypeReferenceMap::GetAPIMethodSignature( + const std::string& name) const { + auto iter = api_methods_.find(name); + if (iter == api_methods_.end()) { + initialize_type_.Run(name); + iter = api_methods_.find(name); + } + return iter == api_methods_.end() ? nullptr : iter->second.get(); +} + void APITypeReferenceMap::AddTypeMethodSignature( const std::string& name, std::unique_ptr<APISignature> signature) {
diff --git a/extensions/renderer/api_type_reference_map.h b/extensions/renderer/api_type_reference_map.h index e960996..f444332b 100644 --- a/extensions/renderer/api_type_reference_map.h +++ b/extensions/renderer/api_type_reference_map.h
@@ -33,6 +33,15 @@ const ArgumentSpec* GetSpec(const std::string& name) const; // Adds the |signature| to the map under the given |name|. |name| is expected + // to be fully qualified with the API and method name, e.g. tabs.create. + void AddAPIMethodSignature(const std::string& name, + std::unique_ptr<APISignature> signature); + + // Returns the signature for the given |name|. |name| is expected + // to be fully qualified with the API and method name, e.g. tabs.create. + const APISignature* GetAPIMethodSignature(const std::string& name) const; + + // Adds the |signature| to the map under the given |name|. |name| is expected // to be fully qualified with API, type, and method (e.g. // storage.StorageArea.get). void AddTypeMethodSignature(const std::string& name, @@ -50,7 +59,7 @@ InitializeTypeCallback initialize_type_; std::map<std::string, std::unique_ptr<ArgumentSpec>> type_refs_; - + std::map<std::string, std::unique_ptr<APISignature>> api_methods_; std::map<std::string, std::unique_ptr<APISignature>> type_methods_; DISALLOW_COPY_AND_ASSIGN(APITypeReferenceMap);
diff --git a/extensions/renderer/native_extension_bindings_system.cc b/extensions/renderer/native_extension_bindings_system.cc index 68917ed..b8aa63d 100644 --- a/extensions/renderer/native_extension_bindings_system.cc +++ b/extensions/renderer/native_extension_bindings_system.cc
@@ -179,15 +179,16 @@ const std::string& name, APIBindingsSystem* bindings_system, v8::Local<v8::Function> get_internal_api) { - v8::Local<v8::Object> hooks_interface; + APIBindingHooks* hooks = nullptr; v8::Local<v8::Object> binding_object = bindings_system->CreateAPIInstance( name, context, context->GetIsolate(), - base::Bind(&IsAPIMethodAvailable, script_context), &hooks_interface); + base::Bind(&IsAPIMethodAvailable, script_context), &hooks); gin::Handle<APIBindingBridge> bridge_handle = gin::CreateHandle( context->GetIsolate(), - new APIBindingBridge(context, binding_object, hooks_interface, - script_context->GetExtensionID(), + new APIBindingBridge(bindings_system->type_reference_map(), + bindings_system->request_handler(), hooks, context, + binding_object, script_context->GetExtensionID(), script_context->GetContextTypeDescription(), base::Bind(&CallJsFunction))); v8::Local<v8::Value> native_api_bridge = bridge_handle.ToV8();
diff --git a/extensions/renderer/native_extension_bindings_system_unittest.cc b/extensions/renderer/native_extension_bindings_system_unittest.cc index 58dd924c..74c42f6 100644 --- a/extensions/renderer/native_extension_bindings_system_unittest.cc +++ b/extensions/renderer/native_extension_bindings_system_unittest.cc
@@ -474,6 +474,49 @@ last_params().arguments.Equals(ListValueFromString("[50]").get())); } +TEST_F(NativeExtensionBindingsSystemUnittest, TestSendRequestHook) { + // Custom binding code. This basically utilizes the interface in binding.js in + // order to test backwards compatibility. + const char kCustomBinding[] = + "apiBridge.registerCustomHook((api) => {\n" + " api.apiFunctions.setHandleRequest('queryState',\n" + " (time, callback) => {\n" + " apiBridge.sendRequest('idle.queryState', [time, callback]);\n" + " });\n" + "});\n"; + + source_map()->RegisterModule("idle", kCustomBinding); + + scoped_refptr<Extension> extension = + CreateExtension("foo", ItemType::EXTENSION, {"idle"}); + RegisterExtension(extension->id()); + + v8::HandleScope handle_scope(isolate()); + v8::Local<v8::Context> context = ContextLocal(); + + ScriptContext* script_context = CreateScriptContext( + context, extension.get(), Feature::BLESSED_EXTENSION_CONTEXT); + script_context->set_url(extension->url()); + + bindings_system()->UpdateBindingsForContext(script_context); + + { + // Call the function correctly. + const char kCallIdleQueryState[] = + "(function() { chrome.idle.queryState(30, function() {}); });"; + + v8::Local<v8::Function> call_idle_query_state = + FunctionFromString(context, kCallIdleQueryState); + RunFunctionOnGlobal(call_idle_query_state, context, 0, nullptr); + } + EXPECT_EQ(extension->id(), last_params().extension_id); + EXPECT_EQ("idle.queryState", last_params().name); + EXPECT_EQ(extension->url(), last_params().source_url); + EXPECT_TRUE(last_params().has_callback); + EXPECT_TRUE( + last_params().arguments.Equals(ListValueFromString("[30]").get())); +} + // Tests that we can notify the browser as event listeners are added or removed. // Note: the notification logic is tested more thoroughly in the APIEventHandler // unittests.
diff --git a/google_apis/gcm/OWNERS b/google_apis/gcm/OWNERS index efe9165d..3597104 100644 --- a/google_apis/gcm/OWNERS +++ b/google_apis/gcm/OWNERS
@@ -1,2 +1,4 @@ zea@chromium.org dimich@chromium.org + +# COMPONENT: Services>CloudMessaging
diff --git a/gpu/command_buffer/service/texture_definition.cc b/gpu/command_buffer/service/texture_definition.cc index a033939..03540ab 100644 --- a/gpu/command_buffer/service/texture_definition.cc +++ b/gpu/command_buffer/service/texture_definition.cc
@@ -11,9 +11,6 @@ #include "base/lazy_instance.h" #include "base/synchronization/lock.h" #include "base/threading/thread_local.h" -// gl_stream_texture_image.h is included to work around crbug.com/595189, a -// compiler bug in VS 2015 Update 2 RC. -#include "gpu/command_buffer/service/gl_stream_texture_image.h" #include "gpu/command_buffer/service/texture_manager.h" #include "ui/gl/gl_image.h" #include "ui/gl/gl_implementation.h"
diff --git a/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_observer.cc b/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_observer.cc index 0188b287..ec4e30b 100644 --- a/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_observer.cc +++ b/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_observer.cc
@@ -19,7 +19,7 @@ // These values are written to logs. New values can be added, but existing // values must never be reordered or deleted and reused. const char* kDesktopIOSPromotionEntrypointHistogramPrefix[] = { - "SavePasswordsNewBubble", "BookmarksNewBubble", "BookmarksExistingBubble", + "SavePasswordsNewBubble", "BookmarksNewBubble", "BookmarksFootNote", "HistoryPage", }; @@ -47,12 +47,18 @@ desktop_metrics_logger_initiated_ = true; bool done_logging = pref_service_->GetBoolean(prefs::kDesktopIOSPromotionDone); + bool is_eligible = + pref_service_->GetBoolean(prefs::kDesktopIOSPromotionEligible); double last_impression = pref_service_->GetDouble(prefs::kDesktopIOSPromotionLastImpression); base::TimeDelta delta = base::Time::Now() - base::Time::FromDoubleT(last_impression); if (done_logging || delta.InDays() >= 7) { sync_service_->RemoveObserver(this); + // If the user was eligible but didn't see the promo on the last 7 days and + // installed Chrome then their eligiblity pref is reset to false. + if (delta.InDays() >= 7 && is_eligible) + pref_service_->SetBoolean(prefs::kDesktopIOSPromotionEligible, false); return; } @@ -64,8 +70,8 @@ pref_service_->GetInteger(prefs::kDesktopIOSPromotionShownEntryPoints); // Entry points are represented on the preference by integers [1..4]. - // TODO(crbug.com/681885): Add reference to the Entry point Constants defined - // in the desktop code side. + // Entry points constants are defined on: + // chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h int entrypoint_prefixes_count = arraysize(kDesktopIOSPromotionEntrypointHistogramPrefix); for (int i = 1; i < entrypoint_prefixes_count + 1; i++) {
diff --git a/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service.cc b/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service.cc index c4fb1ddc..75722b30 100644 --- a/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service.cc +++ b/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service.cc
@@ -19,6 +19,9 @@ // static void DesktopPromotionSyncService::RegisterDesktopPromotionUserPrefs( user_prefs::PrefRegistrySyncable* registry) { + registry->RegisterBooleanPref( + prefs::kDesktopIOSPromotionEligible, false, + user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF); registry->RegisterIntegerPref( prefs::kDesktopIOSPromotionSMSEntryPoint, 0, user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
diff --git a/ios/chrome/browser/native_app_launcher/native_app_navigation_util_unittest.mm b/ios/chrome/browser/native_app_launcher/native_app_navigation_util_unittest.mm index d02e65fd..b775670 100644 --- a/ios/chrome/browser/native_app_launcher/native_app_navigation_util_unittest.mm +++ b/ios/chrome/browser/native_app_launcher/native_app_navigation_util_unittest.mm
@@ -22,7 +22,7 @@ // for setting up NavigationManager entries. std::unique_ptr<web::WebStateImpl> web_state( new web::WebStateImpl(GetBrowserState())); - web_state->GetNavigationManagerImpl().InitializeSession(nil, nil, NO, 0); + web_state->GetNavigationManagerImpl().InitializeSession(nil, NO); web_state->SetWebUsageEnabled(true); web_state_.reset(web_state.release()); }
diff --git a/ios/chrome/browser/pref_names.cc b/ios/chrome/browser/pref_names.cc index 056788e9..83d104b 100644 --- a/ios/chrome/browser/pref_names.cc +++ b/ios/chrome/browser/pref_names.cc
@@ -160,4 +160,7 @@ // SMS. const char kDesktopIOSPromotionDone[] = "ios.desktop_ios_promo_done"; +// True if the user is eligible to recieve "desktop to iOS" promotion. +const char kDesktopIOSPromotionEligible[] = "ios.desktoptomobileeligible"; + } // namespace prefs
diff --git a/ios/chrome/browser/pref_names.h b/ios/chrome/browser/pref_names.h index 6c60722..941e582 100644 --- a/ios/chrome/browser/pref_names.h +++ b/ios/chrome/browser/pref_names.h
@@ -51,6 +51,7 @@ extern const char kDesktopIOSPromotionShownEntryPoints[]; extern const char kDesktopIOSPromotionLastImpression[]; extern const char kDesktopIOSPromotionDone[]; +extern const char kDesktopIOSPromotionEligible[]; } // namespace prefs
diff --git a/ios/chrome/browser/services/OWNERS b/ios/chrome/browser/services/OWNERS index e42d91c7..b380428a 100644 --- a/ios/chrome/browser/services/OWNERS +++ b/ios/chrome/browser/services/OWNERS
@@ -1 +1,3 @@ zea@chromium.org + +# COMPONENT: Services>CloudMessaging
diff --git a/ios/chrome/browser/services/gcm/OWNERS b/ios/chrome/browser/services/gcm/OWNERS index 0f8cc7a..295d996 100644 --- a/ios/chrome/browser/services/gcm/OWNERS +++ b/ios/chrome/browser/services/gcm/OWNERS
@@ -2,3 +2,5 @@ fgorski@chromium.org jianli@chromium.org zea@chromium.org + +# COMPONENT: Services>CloudMessaging
diff --git a/ios/chrome/browser/sessions/session_window_unittest.mm b/ios/chrome/browser/sessions/session_window_unittest.mm index 8f9d4fd..52101b17 100644 --- a/ios/chrome/browser/sessions/session_window_unittest.mm +++ b/ios/chrome/browser/sessions/session_window_unittest.mm
@@ -44,8 +44,8 @@ NSString* opener, BOOL openedByDOM) const { WebStateImpl* webState = new WebStateImpl(chrome_browser_state_.get()); - webState->GetNavigationManagerImpl().InitializeSession(window_name, opener, - openedByDOM, 0); + webState->GetNavigationManagerImpl().InitializeSession(window_name, + openedByDOM); return webState; } @@ -84,11 +84,7 @@ std::unique_ptr<WebStateImpl> webState1( CreateWebState(windowName1, nil, YES)); - NSString* openerId1 = - webState1->GetNavigationManagerImpl().GetSessionController().openerId; std::unique_ptr<WebStateImpl> webState2(CreateWebState(windowName2, nil, NO)); - NSString* openerId2 = - webState2->GetNavigationManagerImpl().GetSessionController().openerId; [sessionWindow addSerializedSessionStorage:webState1->BuildSessionStorage()]; [sessionWindow addSerializedSessionStorage:webState2->BuildSessionStorage()]; @@ -107,12 +103,10 @@ ASSERT_EQ(2U, sessions.count); CRWSessionStorage* unarchivedSession1 = sessions[0]; EXPECT_NSEQ(windowName1, unarchivedSession1.windowName); - EXPECT_NSEQ(openerId1, unarchivedSession1.openerID); EXPECT_TRUE(unarchivedSession1.openedByDOM); CRWSessionStorage* unarchivedSession2 = sessions[1]; EXPECT_NSEQ(windowName2, unarchivedSession2.windowName); - EXPECT_NSEQ(openerId2, unarchivedSession2.openerID); EXPECT_FALSE(unarchivedSession2.openedByDOM); }
diff --git a/ios/chrome/browser/tabs/tab.h b/ios/chrome/browser/tabs/tab.h index 14acb25..62b5e39a 100644 --- a/ios/chrome/browser/tabs/tab.h +++ b/ios/chrome/browser/tabs/tab.h
@@ -116,9 +116,15 @@ @property(nonatomic, readonly) NSString* urlDisplayString; @property(nonatomic, readonly) NSString* windowName; -// ID associated with this tab, from the SessionManager. +// ID associated with this tab. @property(nonatomic, readonly) NSString* tabId; +// ID of the opener of this tab. +@property(nonatomic, readonly) NSString* openerID; + +// NavigationIndex of the opener of this tab. +@property(nonatomic, readonly) NSInteger openerNavigationIndex; + // |YES| if snapshot overlay should load from the grey image cache. @property(nonatomic, assign) BOOL useGreyImageCache;
diff --git a/ios/chrome/browser/tabs/tab.mm b/ios/chrome/browser/tabs/tab.mm index 2dc15af..539c5d4 100644 --- a/ios/chrome/browser/tabs/tab.mm +++ b/ios/chrome/browser/tabs/tab.mm
@@ -178,6 +178,14 @@ // data. NSString* const kTabIDKey = @"TabID"; +// The key under which the opener Tab ID is stored in the WebState's +// serializable user data. +NSString* const kOpenerIDKey = @"OpenerID"; + +// The key under which the opener navigation index is stored in the WebState's +// serializable user data. +NSString* const kOpenerNavigationIndexKey = @"OpenerNavigationIndex"; + // Name of histogram for recording the state of the tab when the renderer is // terminated. const char kRendererTerminationStateHistogram[] = @@ -497,15 +505,18 @@ openedByDOM:(BOOL)openedByDOM model:(TabModel*)parentModel browserState:(ios::ChromeBrowserState*)browserState { - NSInteger openerIndex = -1; - if ([opener navigationManager]) { - NavigationManagerImpl* openerNavManager = [opener navigationManager]; - openerIndex = openerNavManager->GetLastCommittedItemIndex(); - } std::unique_ptr<web::WebStateImpl> webState( new web::WebStateImpl(browserState)); - webState->GetNavigationManagerImpl().InitializeSession( - windowName, opener.tabId, openedByDOM, openerIndex); + webState->GetNavigationManagerImpl().InitializeSession(windowName, + openedByDOM); + if ([opener navigationManager]) { + web::SerializableUserDataManager* userDataManager = + web::SerializableUserDataManager::FromWebState(webState.get()); + userDataManager->AddSerializableData(opener.tabId, kOpenerIDKey); + userDataManager->AddSerializableData( + @([opener navigationManager]->GetLastCommittedItemIndex()), + kOpenerNavigationIndexKey); + } return [self initWithWebState:std::move(webState) model:parentModel]; } @@ -799,6 +810,27 @@ return base::mac::ObjCCastStrict<NSString>(tabID); } +- (NSString*)openerID { + DCHECK(self.webState); + web::SerializableUserDataManager* userDataManager = + web::SerializableUserDataManager::FromWebState(self.webState); + id<NSCoding> openerID = + userDataManager->GetValueForSerializationKey(kOpenerIDKey); + return base::mac::ObjCCastStrict<NSString>(openerID); +} + +- (NSInteger)openerNavigationIndex { + DCHECK(self.webState); + web::SerializableUserDataManager* userDataManager = + web::SerializableUserDataManager::FromWebState(self.webState); + id<NSCoding> openerNavigationIndex = + userDataManager->GetValueForSerializationKey(kOpenerNavigationIndexKey); + if (!openerNavigationIndex) + return -1; + return base::mac::ObjCCastStrict<NSNumber>(openerNavigationIndex) + .integerValue; +} + - (web::WebState*)webState { return webStateImpl_.get(); }
diff --git a/ios/chrome/browser/tabs/tab_model.mm b/ios/chrome/browser/tabs/tab_model.mm index 310c4a1..59558dc2 100644 --- a/ios/chrome/browser/tabs/tab_model.mm +++ b/ios/chrome/browser/tabs/tab_model.mm
@@ -121,13 +121,11 @@ // checking the associated Tab tabId (should be removed once the opener // is passed to the insertTab:atIndex: and replaceTab:withTab: methods). Tab* GetOpenerForTab(id<NSFastEnumeration> tabs, Tab* tab) { - NSString* opener_id = - [tab navigationManager]->GetSessionController().openerId; - if (!opener_id) + if (!tab.openerID) return nullptr; for (Tab* currentTab in tabs) { - if ([opener_id isEqualToString:currentTab.tabId]) + if ([tab.openerID isEqualToString:currentTab.tabId]) return currentTab; }
diff --git a/ios/chrome/browser/tabs/tab_model_unittest.mm b/ios/chrome/browser/tabs/tab_model_unittest.mm index 5c06b91..e06edc0 100644 --- a/ios/chrome/browser/tabs/tab_model_unittest.mm +++ b/ios/chrome/browser/tabs/tab_model_unittest.mm
@@ -26,6 +26,7 @@ #import "ios/web/navigation/navigation_manager_impl.h" #import "ios/web/public/navigation_manager.h" #include "ios/web/public/referrer.h" +#import "ios/web/public/serializable_user_data_manager.h" #include "ios/web/public/test/scoped_testing_web_client.h" #include "ios/web/public/test/test_web_thread_bundle.h" #include "ios/web/public/web_thread.h" @@ -70,8 +71,7 @@ auto webStateImpl = base::MakeUnique<WebStateImpl>(browserState); webStateImpl->SetWebController(webControllerMock); - webStateImpl->GetNavigationManagerImpl().InitializeSession(windowName, - @"opener", NO, -1); + webStateImpl->GetNavigationManagerImpl().InitializeSession(windowName, NO); [webStateImpl->GetNavigationManagerImpl().GetSessionController() setLastVisitedTimestamp:lastVisitedTimestamp]; @@ -161,8 +161,16 @@ NSString* opener, NSInteger index) { auto webState = base::MakeUnique<WebStateImpl>(chrome_browser_state_.get()); - webState->GetNavigationManagerImpl().InitializeSession(windowName, opener, - NO, index); + webState->GetNavigationManagerImpl().InitializeSession(windowName, NO); + if ([opener length] != 0) { + // Duplicate code from Tab initializer. Will be removed once the code + // is rewritten to remove the use of internal ios/web/ API (see issue + // http://crbug.com/620465 for progress). + web::SerializableUserDataManager* userDataManager = + web::SerializableUserDataManager::FromWebState(webState.get()); + userDataManager->AddSerializableData(opener, @"OpenerID"); + userDataManager->AddSerializableData(@(index), @"OpenerNavigationIndex"); + } return webState; }
diff --git a/ios/chrome/browser/tabs/tab_unittest.mm b/ios/chrome/browser/tabs/tab_unittest.mm index 7594a6980..62f29d9 100644 --- a/ios/chrome/browser/tabs/tab_unittest.mm +++ b/ios/chrome/browser/tabs/tab_unittest.mm
@@ -195,8 +195,8 @@ [OCMockObject niceMockForClass:[CRWWebController class]]; auto web_state_impl = base::MakeUnique<WebStateImpl>(browser_state); web_state_impl->SetWebController(mock_web_controller_); - web_state_impl->GetNavigationManagerImpl().InitializeSession( - @"window1", @"opener", NO, 0); + web_state_impl->GetNavigationManagerImpl().InitializeSession(@"window1", + NO); web_state_impl_ = web_state_impl.get(); [[[static_cast<OCMockObject*>(mock_web_controller_) stub] andReturnValue:OCMOCK_VALUE(web_state_impl_)] webStateImpl];
diff --git a/ios/chrome/browser/ui/webui/gcm/OWNERS b/ios/chrome/browser/ui/webui/gcm/OWNERS index 0f8cc7a..295d996 100644 --- a/ios/chrome/browser/ui/webui/gcm/OWNERS +++ b/ios/chrome/browser/ui/webui/gcm/OWNERS
@@ -2,3 +2,5 @@ fgorski@chromium.org jianli@chromium.org zea@chromium.org + +# COMPONENT: Services>CloudMessaging
diff --git a/ios/web/navigation/crw_session_controller+private_constructors.h b/ios/web/navigation/crw_session_controller+private_constructors.h index 0f2a8b6..326eb12e 100644 --- a/ios/web/navigation/crw_session_controller+private_constructors.h +++ b/ios/web/navigation/crw_session_controller+private_constructors.h
@@ -21,19 +21,16 @@ // web/, these methods can go back into session_controller.h. crbug.com/318974 @interface CRWSessionController (PrivateConstructors) // Initializes a session controller, supplying a unique textual identifier for -// the window, or nil. |opener| is the tab id of the parent tab. It may be -// nil or empty if there is no parent. -- (id)initWithWindowName:(NSString*)windowName - openerId:(NSString*)opener - openedByDOM:(BOOL)openedByDOM - openerNavigationIndex:(NSInteger)openerIndex - browserState:(web::BrowserState*)browserState; +// the window, or nil. +- (instancetype)initWithWindowName:(NSString*)windowName + openedByDOM:(BOOL)openedByDOM + browserState:(web::BrowserState*)browserState; // Initializes a session controller, supplying a list of NavigationItem objects // and the current index in the navigation history. -- (id)initWithNavigationItems:(web::ScopedNavigationItemList)items - currentIndex:(NSUInteger)currentIndex - browserState:(web::BrowserState*)browserState; +- (instancetype)initWithNavigationItems:(web::ScopedNavigationItemList)items + currentIndex:(NSUInteger)currentIndex + browserState:(web::BrowserState*)browserState; @end #endif // IOS_WEB_NAVIGATION_CRW_SESSION_CONTROLLER_PRIVATE_CONSTRUCTORS_H_
diff --git a/ios/web/navigation/crw_session_controller.h b/ios/web/navigation/crw_session_controller.h index d5cfeb5..61d453fa 100644 --- a/ios/web/navigation/crw_session_controller.h +++ b/ios/web/navigation/crw_session_controller.h
@@ -43,8 +43,6 @@ @property(nonatomic, readonly, strong) CRWSessionCertificatePolicyManager* sessionCertificatePolicyManager; @property(nonatomic, assign) NSTimeInterval lastVisitedTimestamp; -@property(nonatomic, readonly, copy) NSString* openerId; -@property(nonatomic, readonly, assign) NSInteger openerNavigationIndex; // The list of CRWSessionEntries in |_entries|'s NavigationItemImpls. @property(nonatomic, readonly) web::NavigationItemList items;
diff --git a/ios/web/navigation/crw_session_controller.mm b/ios/web/navigation/crw_session_controller.mm index 7f7eec76..562db5d4 100644 --- a/ios/web/navigation/crw_session_controller.mm +++ b/ios/web/navigation/crw_session_controller.mm
@@ -37,11 +37,6 @@ // the incremental merging of the two classes. web::NavigationManagerImpl* _navigationManager; - NSString* _openerId; // Id of tab who opened this tab, empty/nil if none. - // Navigation index of the tab which opened this tab. Do not rely on the - // value of this member variable to indicate whether or not this tab has - // an opener, as both 0 and -1 are used as navigationIndex values. - NSInteger _openerNavigationIndex; // Identifies the index of the current navigation in the CRWSessionEntry // array. NSInteger _currentNavigationIndex; @@ -94,9 +89,7 @@ // Expose setters for serialization properties. These are exposed in a category // in SessionStorageBuilder, and will be removed as ownership of // their backing ivars moves to NavigationManagerImpl. -@property(nonatomic, readwrite, copy) NSString* openerId; @property(nonatomic, readwrite, getter=isOpenedByDOM) BOOL openedByDOM; -@property(nonatomic, readwrite, assign) NSInteger openerNavigationIndex; @property(nonatomic, readwrite, assign) NSInteger previousNavigationIndex; // Removes all entries after currentNavigationIndex_. @@ -124,22 +117,16 @@ @synthesize entries = _entries; @synthesize windowName = _windowName; @synthesize lastVisitedTimestamp = _lastVisitedTimestamp; -@synthesize openerId = _openerId; @synthesize openedByDOM = _openedByDOM; -@synthesize openerNavigationIndex = _openerNavigationIndex; @synthesize sessionCertificatePolicyManager = _sessionCertificatePolicyManager; -- (id)initWithWindowName:(NSString*)windowName - openerId:(NSString*)openerId - openedByDOM:(BOOL)openedByDOM - openerNavigationIndex:(NSInteger)openerIndex - browserState:(web::BrowserState*)browserState { +- (instancetype)initWithWindowName:(NSString*)windowName + openedByDOM:(BOOL)openedByDOM + browserState:(web::BrowserState*)browserState { self = [super init]; if (self) { self.windowName = windowName; - _openerId = [openerId copy]; _openedByDOM = openedByDOM; - _openerNavigationIndex = openerIndex; _browserState = browserState; _entries = [NSMutableArray array]; _lastVisitedTimestamp = [[NSDate date] timeIntervalSince1970]; @@ -152,13 +139,12 @@ return self; } -- (id)initWithNavigationItems: - (std::vector<std::unique_ptr<web::NavigationItem>>)items - currentIndex:(NSUInteger)currentIndex - browserState:(web::BrowserState*)browserState { +- (instancetype)initWithNavigationItems: + (std::vector<std::unique_ptr<web::NavigationItem>>)items + currentIndex:(NSUInteger)currentIndex + browserState:(web::BrowserState*)browserState { self = [super init]; if (self) { - _openerId = nil; _browserState = browserState; // Create entries array from list of navigations. @@ -187,9 +173,7 @@ - (id)copyWithZone:(NSZone*)zone { CRWSessionController* copy = [[[self class] alloc] init]; - copy->_openerId = [_openerId copy]; copy->_openedByDOM = _openedByDOM; - copy->_openerNavigationIndex = _openerNavigationIndex; copy.windowName = self.windowName; copy->_currentNavigationIndex = _currentNavigationIndex; copy->_previousNavigationIndex = _previousNavigationIndex;
diff --git a/ios/web/navigation/crw_session_controller_unittest.mm b/ios/web/navigation/crw_session_controller_unittest.mm index 5e9057f5..c08fb6d 100644 --- a/ios/web/navigation/crw_session_controller_unittest.mm +++ b/ios/web/navigation/crw_session_controller_unittest.mm
@@ -49,9 +49,7 @@ void SetUp() override { session_controller_.reset( [[CRWSessionController alloc] initWithWindowName:@"test window" - openerId:@"opener" openedByDOM:NO - openerNavigationIndex:0 browserState:&browser_state_]); } @@ -66,7 +64,6 @@ TEST_F(CRWSessionControllerTest, InitWithWindowName) { EXPECT_NSEQ(@"test window", [session_controller_ windowName]); - EXPECT_NSEQ(@"opener", [session_controller_ openerId]); EXPECT_FALSE([session_controller_ isOpenedByDOM]); EXPECT_EQ(0U, [[session_controller_ entries] count]); EXPECT_EQ(nil, [session_controller_ currentEntry]); @@ -514,9 +511,7 @@ // Create source session controller with 1 committed entry. base::scoped_nsobject<CRWSessionController> other_session_controller( [[CRWSessionController alloc] initWithWindowName:nil - openerId:nil openedByDOM:NO - openerNavigationIndex:0 browserState:&browser_state_]); [other_session_controller setWindowName:@"test-window"]; [other_session_controller @@ -568,9 +563,7 @@ // Create empty source session controller. base::scoped_nsobject<CRWSessionController> other_session_controller( [[CRWSessionController alloc] initWithWindowName:nil - openerId:nil openedByDOM:NO - openerNavigationIndex:0 browserState:&browser_state_]); [other_session_controller setWindowName:@"test-window"]; @@ -596,9 +589,7 @@ // pending entry. base::scoped_nsobject<CRWSessionController> other_session_controller( [[CRWSessionController alloc] initWithWindowName:nil - openerId:nil openedByDOM:NO - openerNavigationIndex:0 browserState:&browser_state_]); [other_session_controller setWindowName:@"test-window"]; [other_session_controller @@ -657,9 +648,7 @@ // Create source session controller with 1 committed entry. base::scoped_nsobject<CRWSessionController> other_session_controller( [[CRWSessionController alloc] initWithWindowName:nil - openerId:nil openedByDOM:NO - openerNavigationIndex:0 browserState:&browser_state_]); [other_session_controller setWindowName:@"test-window"]; [other_session_controller @@ -746,7 +735,6 @@ CRWSessionEntry* current_entry = controller.get().currentEntry; EXPECT_EQ(current_entry.navigationItem->GetURL(), GURL("http://www.yahoo.com")); - EXPECT_EQ([[controller openerId] length], 0UL); } // Tests index of previous navigation entry.
diff --git a/ios/web/navigation/crw_session_storage_unittest.mm b/ios/web/navigation/crw_session_storage_unittest.mm index ffed4aa..7626ff4 100644 --- a/ios/web/navigation/crw_session_storage_unittest.mm +++ b/ios/web/navigation/crw_session_storage_unittest.mm
@@ -48,9 +48,7 @@ NSArray* items1 = session1.itemStorages; NSArray* items2 = session2.itemStorages; return ItemStorageListsAreEqual(items1, items2) && - [session1.openerID isEqual:session2.openerID] && session1.openedByDOM == session2.openedByDOM && - session1.openerNavigationIndex == session2.openerNavigationIndex && [session1.windowName isEqual:session2.windowName] && session1.currentNavigationIndex == session2.currentNavigationIndex && session1.previousNavigationIndex == session2.previousNavigationIndex && @@ -64,9 +62,7 @@ CRWNSessionStorageTest() : session_storage_([[CRWSessionStorage alloc] init]) { // Set up |session_storage_|. - [session_storage_ setOpenerID:@"openerID"]; [session_storage_ setOpenedByDOM:YES]; - [session_storage_ setOpenerNavigationIndex:2]; [session_storage_ setWindowName:@"windowName"]; [session_storage_ setCurrentNavigationIndex:4]; [session_storage_ setPreviousNavigationIndex:3];
diff --git a/ios/web/navigation/navigation_manager_impl.h b/ios/web/navigation/navigation_manager_impl.h index a7108ff..b3d2c1e 100644 --- a/ios/web/navigation/navigation_manager_impl.h +++ b/ios/web/navigation/navigation_manager_impl.h
@@ -67,14 +67,8 @@ void SetSessionController(CRWSessionController* session_controller); // Initializes a new session history, supplying a unique |window_name| for the - // window (or nil). |opener_id| is the id of opener, or nil if there is none. - // |opened_by_dom| is YES if the page was opened by DOM. - // |opener_index| is the navigation index of the opener, or -1 if there is - // none. - void InitializeSession(NSString* window_name, - NSString* opener_id, - BOOL opened_by_dom, - int opener_navigation_index); + // window (or nil). |opened_by_dom| is YES if the page was opened by DOM. + void InitializeSession(NSString* window_name, BOOL opened_by_dom); // Replace the session history with a new one, where |items| is the // complete set of navigation items in the new history, and |current_index|
diff --git a/ios/web/navigation/navigation_manager_impl.mm b/ios/web/navigation/navigation_manager_impl.mm index bb16cba..9eb0475d 100644 --- a/ios/web/navigation/navigation_manager_impl.mm +++ b/ios/web/navigation/navigation_manager_impl.mm
@@ -95,14 +95,10 @@ } void NavigationManagerImpl::InitializeSession(NSString* window_name, - NSString* opener_id, - BOOL opened_by_dom, - int opener_navigation_index) { + BOOL opened_by_dom) { SetSessionController([[CRWSessionController alloc] initWithWindowName:window_name - openerId:opener_id openedByDOM:opened_by_dom - openerNavigationIndex:opener_navigation_index browserState:browser_state_]); }
diff --git a/ios/web/navigation/navigation_manager_impl_unittest.mm b/ios/web/navigation/navigation_manager_impl_unittest.mm index 420beea8..cf50b780 100644 --- a/ios/web/navigation/navigation_manager_impl_unittest.mm +++ b/ios/web/navigation/navigation_manager_impl_unittest.mm
@@ -34,9 +34,7 @@ manager_->SetBrowserState(&browser_state_); controller_.reset([[CRWSessionController alloc] initWithWindowName:nil - openerId:nil openedByDOM:NO - openerNavigationIndex:0 browserState:&browser_state_]); manager_->SetSessionController(controller_.get()); }
diff --git a/ios/web/navigation/serializable_user_data_manager_impl.mm b/ios/web/navigation/serializable_user_data_manager_impl.mm index b35e6f7..4a496eb 100644 --- a/ios/web/navigation/serializable_user_data_manager_impl.mm +++ b/ios/web/navigation/serializable_user_data_manager_impl.mm
@@ -59,8 +59,11 @@ } SerializableUserDataImpl::SerializableUserDataImpl() - : data_([[NSDictionary alloc] init]), - legacy_key_conversions_(@{@"tabId" : @"TabID"}) {} + : data_(@{}), legacy_key_conversions_(@{ + @"tabId" : @"TabID", + @"openerId" : @"OpenerID", + @"openerNavigationIndex" : @"OpenerNavigationIndex" + }) {} SerializableUserDataImpl::~SerializableUserDataImpl() {}
diff --git a/ios/web/navigation/session_storage_builder.mm b/ios/web/navigation/session_storage_builder.mm index e2dcc432..ba2ce49e 100644 --- a/ios/web/navigation/session_storage_builder.mm +++ b/ios/web/navigation/session_storage_builder.mm
@@ -22,9 +22,7 @@ // NavigationManagerImpl; @interface CRWSessionController (ExposedForSerialization) @property(nonatomic, readwrite, retain) NSString* tabId; -@property(nonatomic, readwrite, copy) NSString* openerId; @property(nonatomic, readwrite, getter=isOpenedByDOM) BOOL openedByDOM; -@property(nonatomic, readwrite, assign) NSInteger openerNavigationIndex; @property(nonatomic, readwrite, assign) NSInteger previousNavigationIndex; @property(nonatomic, readwrite, retain) CRWSessionCertificatePolicyManager* sessionCertificatePolicyManager; @@ -42,10 +40,7 @@ [[CRWSessionStorage alloc] init]; CRWSessionController* session_controller = navigation_manager->GetSessionController(); - serialized_navigation_manager.openerID = session_controller.openerId; serialized_navigation_manager.openedByDOM = session_controller.openedByDOM; - serialized_navigation_manager.openerNavigationIndex = - session_controller.openerNavigationIndex; serialized_navigation_manager.windowName = session_controller.windowName; serialized_navigation_manager.currentNavigationIndex = session_controller.currentNavigationIndex; @@ -88,9 +83,7 @@ [[CRWSessionController alloc] initWithNavigationItems:std::move(items) currentIndex:current_index browserState:nullptr]); - [session_controller setOpenerId:storage.openerID]; [session_controller setOpenedByDOM:storage.openedByDOM]; - [session_controller setOpenerNavigationIndex:storage.openerNavigationIndex]; [session_controller setWindowName:storage.windowName]; [session_controller setPreviousNavigationIndex:storage.previousNavigationIndex];
diff --git a/ios/web/public/crw_session_storage.h b/ios/web/public/crw_session_storage.h index 97d6948..facfe80 100644 --- a/ios/web/public/crw_session_storage.h +++ b/ios/web/public/crw_session_storage.h
@@ -18,9 +18,7 @@ // TODO(crbug.com/685388): Investigate using code from the sessions component. @interface CRWSessionStorage : NSObject<NSCoding> -@property(nonatomic, copy) NSString* openerID; @property(nonatomic, getter=isOpenedByDOM) BOOL openedByDOM; -@property(nonatomic, assign) NSInteger openerNavigationIndex; @property(nonatomic, copy) NSString* windowName; @property(nonatomic, assign) NSInteger currentNavigationIndex; @property(nonatomic, assign) NSInteger previousNavigationIndex;
diff --git a/ios/web/public/crw_session_storage.mm b/ios/web/public/crw_session_storage.mm index 8c5b2254..5f377365 100644 --- a/ios/web/public/crw_session_storage.mm +++ b/ios/web/public/crw_session_storage.mm
@@ -17,9 +17,7 @@ NSString* const kCurrentNavigationIndexKey = @"currentNavigationIndex"; NSString* const kItemStoragesKey = @"entries"; NSString* const kLastVisitedTimestampKey = @"lastVisitedTimestamp"; -NSString* const kOpenerIDKey = @"openerId"; NSString* const kOpenedByDOMKey = @"openedByDOM"; -NSString* const kOpenerNavigationIndexKey = @"openerNavigationIndex"; NSString* const kPreviousNavigationIndexKey = @"previousNavigationIndex"; NSString* const kWindowNameKey = @"windowName"; } @@ -33,9 +31,7 @@ @implementation CRWSessionStorage -@synthesize openerID = _openerID; @synthesize openedByDOM = _openedByDOM; -@synthesize openerNavigationIndex = _openerNavigationIndex; @synthesize windowName = _windowName; @synthesize currentNavigationIndex = _currentNavigationIndex; @synthesize previousNavigationIndex = _previousNavigationIndex; @@ -60,10 +56,7 @@ self = [super init]; if (self) { _windowName = [[decoder decodeObjectForKey:kWindowNameKey] copy]; - _openerID = [[decoder decodeObjectForKey:kOpenerIDKey] copy]; _openedByDOM = [decoder decodeBoolForKey:kOpenedByDOMKey]; - _openerNavigationIndex = - [decoder decodeIntForKey:kOpenerNavigationIndexKey]; _currentNavigationIndex = [decoder decodeIntForKey:kCurrentNavigationIndexKey]; _previousNavigationIndex = @@ -88,9 +81,7 @@ } - (void)encodeWithCoder:(NSCoder*)coder { - [coder encodeObject:self.openerID forKey:kOpenerIDKey]; [coder encodeBool:self.openedByDOM forKey:kOpenedByDOMKey]; - [coder encodeInt:self.openerNavigationIndex forKey:kOpenerNavigationIndexKey]; [coder encodeObject:self.windowName forKey:kWindowNameKey]; [coder encodeInt:self.currentNavigationIndex forKey:kCurrentNavigationIndexKey];
diff --git a/ios/web/public/test/web_test_with_web_state.mm b/ios/web/public/test/web_test_with_web_state.mm index 1b868c9d..d82e1cc 100644 --- a/ios/web/public/test/web_test_with_web_state.mm +++ b/ios/web/public/test/web_test_with_web_state.mm
@@ -30,7 +30,7 @@ void WebTestWithWebState::SetUp() { WebTest::SetUp(); std::unique_ptr<WebStateImpl> web_state(new WebStateImpl(GetBrowserState())); - web_state->GetNavigationManagerImpl().InitializeSession(nil, nil, NO, 0); + web_state->GetNavigationManagerImpl().InitializeSession(nil, NO); web_state->SetWebUsageEnabled(true); web_state_.reset(web_state.release());
diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm index 51541e2..4edf113b5 100644 --- a/ios/web/web_state/ui/crw_web_controller_unittest.mm +++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -194,7 +194,7 @@ NavigationManagerImpl& navigationManager = [web_controller() webStateImpl]->GetNavigationManagerImpl(); - navigationManager.InitializeSession(@"name", nil, NO, 0); + navigationManager.InitializeSession(@"name", NO); navigationManager.AddPendingItem( GURL("http://www.google.com/?q=foo#bar"), web::Referrer(), ui::PAGE_TRANSITION_TYPED, @@ -807,7 +807,7 @@ void Load(const GURL& URL) { NavigationManagerImpl& navigation_manager = [web_controller() webStateImpl]->GetNavigationManagerImpl(); - navigation_manager.InitializeSession(@"name", nil, NO, 0); + navigation_manager.InitializeSession(@"name", NO); navigation_manager.AddPendingItem( URL, web::Referrer(), ui::PAGE_TRANSITION_TYPED, web::NavigationInitiationType::USER_INITIATED);
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm index b386a03b..31d6fbdf 100644 --- a/ios/web/web_state/web_state_impl.mm +++ b/ios/web/web_state/web_state_impl.mm
@@ -49,11 +49,9 @@ // Initialized the new session. NSString* window_name = nil; - NSString* opener_id = nil; BOOL opened_by_dom = NO; - int opener_navigation_index = 0; - web_state->GetNavigationManagerImpl().InitializeSession( - window_name, opener_id, opened_by_dom, opener_navigation_index); + web_state->GetNavigationManagerImpl().InitializeSession(window_name, + opened_by_dom); // This std::move is required to compile with the version of clang shipping // with Xcode 8.0+. Evalute whether the issue is fixed once a new version of
diff --git a/ios/web/web_state/web_state_impl_unittest.mm b/ios/web/web_state/web_state_impl_unittest.mm index dfd34691..128ac61 100644 --- a/ios/web/web_state/web_state_impl_unittest.mm +++ b/ios/web/web_state/web_state_impl_unittest.mm
@@ -249,7 +249,7 @@ // Loads specified html page into WebState. void LoadHtml(std::string html) { - web_state_->GetNavigationManagerImpl().InitializeSession(nil, nil, NO, 0); + web_state_->GetNavigationManagerImpl().InitializeSession(nil, NO); // Use data: url for loading html page. std::string encoded_html;
diff --git a/ios/web/webui/web_ui_mojo_inttest.mm b/ios/web/webui/web_ui_mojo_inttest.mm index e2bdad6..7ba68df3 100644 --- a/ios/web/webui/web_ui_mojo_inttest.mm +++ b/ios/web/webui/web_ui_mojo_inttest.mm
@@ -134,7 +134,7 @@ WebUIMojoTest() : web_state_(new WebStateImpl(GetBrowserState())), ui_handler_(new TestUIHandler()) { - web_state_->GetNavigationManagerImpl().InitializeSession(nil, nil, NO, 0); + web_state_->GetNavigationManagerImpl().InitializeSession(nil, NO); WebUIIOSControllerFactory::RegisterFactory( new TestWebUIControllerFactory(ui_handler_.get())); }
diff --git a/ipc/ipc_mojo_bootstrap.cc b/ipc/ipc_mojo_bootstrap.cc index ffa1155..7dc7484e0 100644 --- a/ipc/ipc_mojo_bootstrap.cc +++ b/ipc/ipc_mojo_bootstrap.cc
@@ -385,8 +385,9 @@ void SignalSyncMessageEvent() { controller_->lock_.AssertAcquired(); - EnsureSyncMessageEventExists(); - sync_message_event_->Signal(); + + if (sync_message_event_) + sync_message_event_->Signal(); } MessageWrapper PopSyncMessage(uint32_t id) { @@ -489,9 +490,11 @@ { base::AutoLock locker(controller_->lock_); - EnsureSyncMessageEventExists(); - if (!sync_messages_.empty()) - SignalSyncMessageEvent(); + if (!sync_message_event_) { + sync_message_event_.reset(new MojoEvent); + if (peer_closed_ || !sync_messages_.empty()) + SignalSyncMessageEvent(); + } } sync_watcher_.reset(new mojo::SyncHandleWatcher(
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc index 5f1fc99..e97de2c7 100644 --- a/media/filters/chunk_demuxer.cc +++ b/media/filters/chunk_demuxer.cc
@@ -494,6 +494,9 @@ std::vector<DemuxerStream*> ChunkDemuxer::GetAllStreams() { base::AutoLock auto_lock(lock_); std::vector<DemuxerStream*> result; + // Put enabled streams at the beginning of the list so that + // MediaResource::GetFirstStream returns the enabled stream if there is one. + // TODO(servolk): Revisit this after media track switching is supported. for (const auto& stream : audio_streams_) { if (stream->enabled()) result.push_back(stream.get()); @@ -502,6 +505,15 @@ if (stream->enabled()) result.push_back(stream.get()); } + // Put disabled streams at the end of the vector. + for (const auto& stream : audio_streams_) { + if (!stream->enabled()) + result.push_back(stream.get()); + } + for (const auto& stream : video_streams_) { + if (!stream->enabled()) + result.push_back(stream.get()); + } return result; }
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc index f7747e6..4d75b7c3 100644 --- a/media/filters/ffmpeg_demuxer.cc +++ b/media/filters/ffmpeg_demuxer.cc
@@ -1037,10 +1037,18 @@ std::vector<DemuxerStream*> FFmpegDemuxer::GetAllStreams() { DCHECK(task_runner_->BelongsToCurrentThread()); std::vector<DemuxerStream*> result; + // Put enabled streams at the beginning of the list so that + // MediaResource::GetFirstStream returns the enabled stream if there is one. + // TODO(servolk): Revisit this after media track switching is supported. for (const auto& stream : streams_) { if (stream && stream->enabled()) result.push_back(stream.get()); } + // And include disabled streams at the end of the list. + for (const auto& stream : streams_) { + if (stream && !stream->enabled()) + result.push_back(stream.get()); + } return result; }
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc index 2774610..f303061 100644 --- a/media/test/pipeline_integration_test.cc +++ b/media/test/pipeline_integration_test.cc
@@ -1059,6 +1059,53 @@ ASSERT_TRUE(WaitUntilOnEnded()); } +TEST_F(PipelineIntegrationTest, ReinitRenderersWhileAudioTrackIsDisabled) { + ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm")); + Play(); + + // These get triggered every time playback is resumed. + EXPECT_CALL(*this, OnVideoNaturalSizeChange(gfx::Size(320, 240))) + .Times(AnyNumber()); + EXPECT_CALL(*this, OnVideoOpacityChange(true)).Times(AnyNumber()); + + // Disable the audio track. + std::vector<MediaTrack::Id> track_ids; + pipeline_->OnEnabledAudioTracksChanged(track_ids); + // pipeline.Suspend() releases renderers and pipeline.Resume() recreates and + // reinitializes renderers while the audio track is disabled. + ASSERT_TRUE(Suspend()); + ASSERT_TRUE(Resume(TimestampMs(100))); + // Now re-enable the audio track, playback should continue successfully. + EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH)).Times(1); + track_ids.push_back("2"); + pipeline_->OnEnabledAudioTracksChanged(track_ids); + ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(TimestampMs(200))); + + Stop(); +} + +TEST_F(PipelineIntegrationTest, ReinitRenderersWhileVideoTrackIsDisabled) { + ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm", kHashed)); + Play(); + + // These get triggered every time playback is resumed. + EXPECT_CALL(*this, OnVideoNaturalSizeChange(gfx::Size(320, 240))) + .Times(AnyNumber()); + EXPECT_CALL(*this, OnVideoOpacityChange(true)).Times(AnyNumber()); + + // Disable the video track. + pipeline_->OnSelectedVideoTrackChanged(base::nullopt); + // pipeline.Suspend() releases renderers and pipeline.Resume() recreates and + // reinitializes renderers while the video track is disabled. + ASSERT_TRUE(Suspend()); + ASSERT_TRUE(Resume(TimestampMs(100))); + // Now re-enable the video track, playback should continue successfully. + pipeline_->OnSelectedVideoTrackChanged(MediaTrack::Id("1")); + ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(TimestampMs(200))); + + Stop(); +} + TEST_F(PipelineIntegrationTest, PipelineStoppedWhileAudioRestartPending) { ASSERT_EQ(PIPELINE_OK, Start("bear-320x240.webm")); Play();
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.cc b/mojo/public/cpp/bindings/lib/multiplex_router.cc index bb71a6b..2da459a 100644 --- a/mojo/public/cpp/bindings/lib/multiplex_router.cc +++ b/mojo/public/cpp/bindings/lib/multiplex_router.cc
@@ -111,8 +111,10 @@ if (event_signalled_) return; - EnsureEventMessagePipeExists(); event_signalled_ = true; + if (!sync_message_event_sender_.is_valid()) + return; + MojoResult result = WriteMessageRaw(sync_message_event_sender_.get(), nullptr, 0, nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE); @@ -125,12 +127,14 @@ if (!event_signalled_) return; - DCHECK(sync_message_event_receiver_.is_valid()); + event_signalled_ = false; + if (!sync_message_event_receiver_.is_valid()) + return; + MojoResult result = ReadMessageRaw(sync_message_event_receiver_.get(), nullptr, nullptr, nullptr, nullptr, MOJO_READ_MESSAGE_FLAG_MAY_DISCARD); DCHECK_EQ(MOJO_RESULT_OK, result); - event_signalled_ = false; } // --------------------------------------------------------------------------- @@ -203,11 +207,20 @@ { MayAutoLock locker(&router_->lock_); - EnsureEventMessagePipeExists(); - auto iter = router_->sync_message_tasks_.find(id_); - if (iter != router_->sync_message_tasks_.end() && !iter->second.empty()) - SignalSyncMessageEvent(); + if (!sync_message_event_sender_.is_valid()) { + MojoResult result = + CreateMessagePipe(nullptr, &sync_message_event_sender_, + &sync_message_event_receiver_); + DCHECK_EQ(MOJO_RESULT_OK, result); + + if (event_signalled_) { + // Reset the flag so that SignalSyncMessageEvent() will actually + // signal using the newly-created message pipe. + event_signalled_ = false; + SignalSyncMessageEvent(); + } + } } sync_watcher_.reset(new SyncHandleWatcher( @@ -215,17 +228,6 @@ base::Bind(&InterfaceEndpoint::OnHandleReady, base::Unretained(this)))); } - void EnsureEventMessagePipeExists() { - router_->AssertLockAcquired(); - - if (sync_message_event_receiver_.is_valid()) - return; - - MojoResult result = CreateMessagePipe(nullptr, &sync_message_event_sender_, - &sync_message_event_receiver_); - DCHECK_EQ(MOJO_RESULT_OK, result); - } - // --------------------------------------------------------------------------- // The following members are safe to access from any threads. @@ -581,8 +583,11 @@ for (auto iter = endpoints_.begin(); iter != endpoints_.end(); ++iter) { auto sync_iter = sync_message_tasks_.find(iter->first); - if (sync_iter != sync_message_tasks_.end() && !sync_iter->second.empty()) + if (iter->second->peer_closed() || + (sync_iter != sync_message_tasks_.end() && + !sync_iter->second.empty())) { iter->second->SignalSyncMessageEvent(); + } } ProcessTasks(NO_DIRECT_CLIENT_CALLS, nullptr); @@ -926,16 +931,13 @@ void MultiplexRouter::UpdateEndpointStateMayRemove( InterfaceEndpoint* endpoint, EndpointStateUpdateType type) { - switch (type) { - case ENDPOINT_CLOSED: - endpoint->set_closed(); - break; - case PEER_ENDPOINT_CLOSED: - endpoint->set_peer_closed(); - // If the interface endpoint is performing a sync watch, this makes sure - // it is notified and eventually exits the sync watch. - endpoint->SignalSyncMessageEvent(); - break; + if (type == ENDPOINT_CLOSED) { + endpoint->set_closed(); + } else { + endpoint->set_peer_closed(); + // If the interface endpoint is performing a sync watch, this makes sure + // it is notified and eventually exits the sync watch. + endpoint->SignalSyncMessageEvent(); } if (endpoint->closed() && endpoint->peer_closed()) endpoints_.erase(endpoint->id());
diff --git a/mojo/public/cpp/bindings/tests/sync_method_unittest.cc b/mojo/public/cpp/bindings/tests/sync_method_unittest.cc index b9c9890..d0e5f10 100644 --- a/mojo/public/cpp/bindings/tests/sync_method_unittest.cc +++ b/mojo/public/cpp/bindings/tests/sync_method_unittest.cc
@@ -332,8 +332,8 @@ return base::Bind(&CallAsyncEchoCallback<Func>, func); } -// TestSync and TestSyncMaster exercise Router and MultiplexRouter, -// respectively. +// TestSync (without associated interfaces) and TestSyncMaster (with associated +// interfaces) exercise MultiplexRouter with different configurations. using InterfaceTypes = testing::Types<TestSync, TestSyncMaster>; TYPED_TEST_CASE(SyncMethodCommonTest, InterfaceTypes);
diff --git a/net/dns/host_resolver.cc b/net/dns/host_resolver.cc index df958933..f0cd91b 100644 --- a/net/dns/host_resolver.cc +++ b/net/dns/host_resolver.cc
@@ -132,6 +132,14 @@ const PersistCallback& persist_callback, std::unique_ptr<const base::Value> old_data) {} +void HostResolver::SetDefaultAddressFamily(AddressFamily address_family) { + NOTREACHED(); +} + +AddressFamily HostResolver::GetDefaultAddressFamily() const { + return ADDRESS_FAMILY_UNSPECIFIED; +} + // static std::unique_ptr<HostResolver> HostResolver::CreateSystemResolver( const Options& options,
diff --git a/net/dns/host_resolver.h b/net/dns/host_resolver.h index fe38138f..33cdd34 100644 --- a/net/dns/host_resolver.h +++ b/net/dns/host_resolver.h
@@ -213,6 +213,14 @@ const PersistCallback& persist_callback, std::unique_ptr<const base::Value> old_data); + // Sets the default AddressFamily to use when requests have left it + // unspecified. For example, this could be used to restrict resolution + // results to AF_INET by passing in ADDRESS_FAMILY_IPV4, or to + // AF_INET6 by passing in ADDRESS_FAMILY_IPV6. See http://crbug.com/696569 for + // why this option is necessary. + virtual void SetDefaultAddressFamily(AddressFamily address_family); + virtual AddressFamily GetDefaultAddressFamily() const; + // Creates a HostResolver implementation that queries the underlying system. // (Except if a unit-test has changed the global HostResolverProc using // ScopedHostResolverProc to intercept requests to the system).
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc index dfd4752..7596c77 100644 --- a/net/dns/host_resolver_impl.cc +++ b/net/dns/host_resolver_impl.cc
@@ -2011,6 +2011,7 @@ net_log_(net_log), received_dns_config_(false), num_dns_failures_(0), + default_address_family_(ADDRESS_FAMILY_UNSPECIFIED), use_local_ipv6_(false), last_ipv6_probe_result_(true), resolved_known_ipv6_hostname_(false), @@ -2184,6 +2185,15 @@ return rv; } +void HostResolverImpl::SetDefaultAddressFamily(AddressFamily address_family) { + DCHECK(CalledOnValidThread()); + default_address_family_ = address_family; +} + +AddressFamily HostResolverImpl::GetDefaultAddressFamily() const { + return default_address_family_; +} + bool HostResolverImpl::ResolveAsIP(const Key& key, const RequestInfo& info, const IPAddress* ip_address, @@ -2196,6 +2206,11 @@ *net_error = OK; AddressFamily family = GetAddressFamily(*ip_address); + if (family == ADDRESS_FAMILY_IPV6 && + default_address_family_ == ADDRESS_FAMILY_IPV4) { + // Don't return IPv6 addresses if default address family is set to IPv4. + *net_error = ERR_NAME_NOT_RESOLVED; + } if (key.address_family != ADDRESS_FAMILY_UNSPECIFIED && key.address_family != family) { // Don't return IPv6 addresses for IPv4 queries, and vice versa. @@ -2338,20 +2353,19 @@ info.host_resolver_flags() | additional_resolver_flags_; AddressFamily effective_address_family = info.address_family(); - if (info.address_family() == ADDRESS_FAMILY_UNSPECIFIED) { - if (!use_local_ipv6_ && - // When resolving IPv4 literals, there's no need to probe for IPv6. - // When resolving IPv6 literals, there's no benefit to artificially - // limiting our resolution based on a probe. Prior logic ensures - // that this query is UNSPECIFIED (see info.address_family() - // check above) so the code requesting the resolution should be amenable - // to receiving a IPv6 resolution. - ip_address == nullptr) { - if (!IsIPv6Reachable(net_log)) { - effective_address_family = ADDRESS_FAMILY_IPV4; - effective_flags |= HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6; - } - } + if (info.address_family() == ADDRESS_FAMILY_UNSPECIFIED) + effective_address_family = default_address_family_; + + if (effective_address_family == ADDRESS_FAMILY_UNSPECIFIED && + // When resolving IPv4 literals, there's no need to probe for IPv6. + // When resolving IPv6 literals, there's no benefit to artificially + // limiting our resolution based on a probe. Prior logic ensures + // that this query is UNSPECIFIED (see effective_address_family + // check above) so the code requesting the resolution should be amenable + // to receiving a IPv6 resolution. + !use_local_ipv6_ && ip_address == nullptr && !IsIPv6Reachable(net_log)) { + effective_address_family = ADDRESS_FAMILY_IPV4; + effective_flags |= HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6; } return Key(info.hostname(), effective_address_family, effective_flags);
diff --git a/net/dns/host_resolver_impl.h b/net/dns/host_resolver_impl.h index 0ad7146d..24062d9 100644 --- a/net/dns/host_resolver_impl.h +++ b/net/dns/host_resolver_impl.h
@@ -158,6 +158,9 @@ const PersistCallback& persist_callback, std::unique_ptr<const base::Value> old_data) override; + void SetDefaultAddressFamily(AddressFamily address_family) override; + AddressFamily GetDefaultAddressFamily() const override; + void set_proc_params_for_test(const ProcTaskParams& proc_params) { proc_params_ = proc_params; } @@ -346,6 +349,10 @@ // Number of consecutive failures of DnsTask, counted when fallback succeeds. unsigned num_dns_failures_; + // Address family to use when the request doesn't specify one. See + // http://crbug.com/696569 for why the option is needed. + AddressFamily default_address_family_; + // True if DnsConfigService detected that system configuration depends on // local IPv6 connectivity. Disables probing. bool use_local_ipv6_;
diff --git a/net/dns/host_resolver_impl_unittest.cc b/net/dns/host_resolver_impl_unittest.cc index 0b4807a9..ce5776a 100644 --- a/net/dns/host_resolver_impl_unittest.cc +++ b/net/dns/host_resolver_impl_unittest.cc
@@ -2522,4 +2522,98 @@ EXPECT_EQ(1, count2); } +// Tests that after changing the default AddressFamily to IPV4, requests +// with UNSPECIFIED address family map to IPV4. +TEST_F(HostResolverImplTest, SetDefaultAddressFamily_IPv4) { + CreateSerialResolver(); // To guarantee order of resolutions. + + proc_->AddRule("h1", ADDRESS_FAMILY_IPV4, "1.0.0.1"); + proc_->AddRule("h1", ADDRESS_FAMILY_IPV6, "::2"); + + resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV4); + + CreateRequest("h1", 80, MEDIUM, ADDRESS_FAMILY_UNSPECIFIED); + CreateRequest("h1", 80, MEDIUM, ADDRESS_FAMILY_IPV4); + CreateRequest("h1", 80, MEDIUM, ADDRESS_FAMILY_IPV6); + + // Start all of the requests. + for (size_t i = 0; i < requests_.size(); ++i) { + EXPECT_EQ(ERR_IO_PENDING, requests_[i]->Resolve()) << i; + } + + proc_->SignalMultiple(requests_.size()); + + // Wait for all the requests to complete. + for (size_t i = 0u; i < requests_.size(); ++i) { + EXPECT_EQ(OK, requests_[i]->WaitForResult()) << i; + } + + // Since the requests all had the same priority and we limited the thread + // count to 1, they should have completed in the same order as they were + // requested. Moreover, request0 and request1 will have been serviced by + // the same job. + + MockHostResolverProc::CaptureList capture_list = proc_->GetCaptureList(); + ASSERT_EQ(2u, capture_list.size()); + + EXPECT_EQ("h1", capture_list[0].hostname); + EXPECT_EQ(ADDRESS_FAMILY_IPV4, capture_list[0].address_family); + + EXPECT_EQ("h1", capture_list[1].hostname); + EXPECT_EQ(ADDRESS_FAMILY_IPV6, capture_list[1].address_family); + + // Now check that the correct resolved IP addresses were returned. + EXPECT_TRUE(requests_[0]->HasOneAddress("1.0.0.1", 80)); + EXPECT_TRUE(requests_[1]->HasOneAddress("1.0.0.1", 80)); + EXPECT_TRUE(requests_[2]->HasOneAddress("::2", 80)); +} + +// This is the exact same test as SetDefaultAddressFamily_IPv4, except the +// default family is set to IPv6 and the family of requests is flipped where +// specified. +TEST_F(HostResolverImplTest, SetDefaultAddressFamily_IPv6) { + CreateSerialResolver(); // To guarantee order of resolutions. + + // Don't use IPv6 replacements here since some systems don't support it. + proc_->AddRule("h1", ADDRESS_FAMILY_IPV4, "1.0.0.1"); + proc_->AddRule("h1", ADDRESS_FAMILY_IPV6, "::2"); + + resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV6); + + CreateRequest("h1", 80, MEDIUM, ADDRESS_FAMILY_UNSPECIFIED); + CreateRequest("h1", 80, MEDIUM, ADDRESS_FAMILY_IPV6); + CreateRequest("h1", 80, MEDIUM, ADDRESS_FAMILY_IPV4); + + // Start all of the requests. + for (size_t i = 0; i < requests_.size(); ++i) { + EXPECT_EQ(ERR_IO_PENDING, requests_[i]->Resolve()) << i; + } + + proc_->SignalMultiple(requests_.size()); + + // Wait for all the requests to complete. + for (size_t i = 0u; i < requests_.size(); ++i) { + EXPECT_EQ(OK, requests_[i]->WaitForResult()) << i; + } + + // Since the requests all had the same priority and we limited the thread + // count to 1, they should have completed in the same order as they were + // requested. Moreover, request0 and request1 will have been serviced by + // the same job. + + MockHostResolverProc::CaptureList capture_list = proc_->GetCaptureList(); + ASSERT_EQ(2u, capture_list.size()); + + EXPECT_EQ("h1", capture_list[0].hostname); + EXPECT_EQ(ADDRESS_FAMILY_IPV6, capture_list[0].address_family); + + EXPECT_EQ("h1", capture_list[1].hostname); + EXPECT_EQ(ADDRESS_FAMILY_IPV4, capture_list[1].address_family); + + // Now check that the correct resolved IP addresses were returned. + EXPECT_TRUE(requests_[0]->HasOneAddress("::2", 80)); + EXPECT_TRUE(requests_[1]->HasOneAddress("::2", 80)); + EXPECT_TRUE(requests_[2]->HasOneAddress("1.0.0.1", 80)); +} + } // namespace net
diff --git a/net/dns/mapped_host_resolver.cc b/net/dns/mapped_host_resolver.cc index e29c85e..39c313c 100644 --- a/net/dns/mapped_host_resolver.cc +++ b/net/dns/mapped_host_resolver.cc
@@ -56,6 +56,14 @@ return impl_->GetDnsConfigAsValue(); } +void MappedHostResolver::SetDefaultAddressFamily(AddressFamily address_family) { + impl_->SetDefaultAddressFamily(address_family); +} + +AddressFamily MappedHostResolver::GetDefaultAddressFamily() const { + return impl_->GetDefaultAddressFamily(); +} + int MappedHostResolver::ApplyRules(RequestInfo* info) const { HostPortPair host_port(info->host_port_pair()); if (rules_.RewriteHost(&host_port)) {
diff --git a/net/dns/mapped_host_resolver.h b/net/dns/mapped_host_resolver.h index ddc753be..54342e2 100644 --- a/net/dns/mapped_host_resolver.h +++ b/net/dns/mapped_host_resolver.h
@@ -57,6 +57,8 @@ void SetDnsClientEnabled(bool enabled) override; HostCache* GetHostCache() override; std::unique_ptr<base::Value> GetDnsConfigAsValue() const override; + void SetDefaultAddressFamily(AddressFamily address_family) override; + AddressFamily GetDefaultAddressFamily() const override; private: // Modify the request |info| according to |rules_|. Returns either OK or
diff --git a/net/test/android/javatests/src/org/chromium/net/test/util/TestWebServer.java b/net/test/android/javatests/src/org/chromium/net/test/util/TestWebServer.java index ab6f5cd..4b5730d1 100644 --- a/net/test/android/javatests/src/org/chromium/net/test/util/TestWebServer.java +++ b/net/test/android/javatests/src/org/chromium/net/test/util/TestWebServer.java
@@ -9,28 +9,17 @@ import android.util.Log; import android.util.Pair; -import org.apache.http.Header; -import org.apache.http.HttpException; -import org.apache.http.HttpRequest; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.HttpVersion; -import org.apache.http.RequestLine; -import org.apache.http.StatusLine; -import org.apache.http.entity.ByteArrayEntity; -import org.apache.http.impl.DefaultHttpServerConnection; -import org.apache.http.impl.cookie.DateUtils; -import org.apache.http.message.BasicHttpResponse; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.CoreProtocolPNames; -import org.apache.http.params.HttpParams; - +import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintStream; import java.net.MalformedURLException; import java.net.ServerSocket; import java.net.Socket; +import java.net.SocketException; import java.net.URI; import java.net.URL; import java.net.URLConnection; @@ -40,11 +29,12 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.X509Certificate; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; -import java.util.Hashtable; import java.util.List; +import java.util.Locale; import java.util.Map; import javax.net.ssl.HostnameVerifier; @@ -62,14 +52,13 @@ * for loopback testing without the need to setup tcp forwarding to the * host computer. * - * Based heavily on the CTSWebServer in Android. + * Originally based heavily on the CTSWebServer in Android. */ public class TestWebServer { private static final String TAG = "TestWebServer"; private static TestWebServer sInstance; private static TestWebServer sSecureInstance; - private static Hashtable<Integer, String> sReasons; private final ServerThread mServerThread; private String mServerUri; @@ -106,7 +95,7 @@ private final Object mLock = new Object(); private final Map<String, Response> mResponseMap = new HashMap<String, Response>(); private final Map<String, Integer> mResponseCountMap = new HashMap<String, Integer>(); - private final Map<String, HttpRequest> mLastRequestMap = new HashMap<String, HttpRequest>(); + private final Map<String, List<String>> mLastRequestMap = new HashMap<String, List<String>>(); /** * Create and start a local HTTP server instance. @@ -403,7 +392,7 @@ /** * Returns the last HttpRequest at this path. Can return null if it is never requested. */ - public HttpRequest getLastRequest(String requestPath) { + public List<String> getLastRequest(String requestPath) { synchronized (mLock) { if (!mLastRequestMap.containsKey(requestPath)) throw new IllegalArgumentException("Path not set: " + requestPath); @@ -475,11 +464,28 @@ } } - private void servedResponseFor(String path, HttpRequest request) { - synchronized (mLock) { - mResponseCountMap.put(path, Integer.valueOf( - mResponseCountMap.get(path).intValue() + 1)); - mLastRequestMap.put(path, request); + public static List<String> getMatchingHeadersValues(List<String> request, String headerName) { + List<String> matchingHeaders = new ArrayList<String>(); + for (int j = 1; j < request.size(); j++) { + if (request.get(j).isEmpty()) break; + if (request.get(j) + .toLowerCase(Locale.ENGLISH) + .startsWith(headerName.toLowerCase(Locale.ENGLISH) + ": ")) { + matchingHeaders.add(request.get(j).substring(headerName.length() + 2)); + } + } + return matchingHeaders; + } + + private static class WebServerPrintStream extends PrintStream { + WebServerPrintStream(OutputStream out) { + super(out); + } + + @Override + public void println(String s) { + Log.w(TAG, s); + super.println(s); } } @@ -491,131 +497,111 @@ * <p>If there is an action associated with the response, it will be executed inside of * this function. * - * @throws InterruptedException + * @throws NoSuchAlgorithmException, IOException */ - private HttpResponse getResponse(HttpRequest request) throws InterruptedException { - assert Thread.currentThread() == mServerThread - : "getResponse called from non-server thread"; + private void outputResponse(List<String> requestLines, WebServerPrintStream stream) + throws NoSuchAlgorithmException, IOException { + assert Thread.currentThread() + == mServerThread : "outputResponse called from non-server thread"; + assert requestLines.get(0).split(" ").length + == 3 : "Incorrect header sent to outputResponse"; - RequestLine requestLine = request.getRequestLine(); - HttpResponse httpResponse = null; - Log.i(TAG, requestLine.getMethod() + ": " + requestLine.getUri()); - String uriString = requestLine.getUri(); - URI uri = URI.create(uriString); - String path = uri.getPath(); + // Don't dump headers to decrease log. + Log.w(TAG, requestLines.get(0)); - Response response = null; + final String bodyTemplate = "<html><head><title>%s</title></head>" + + "<body>%s</body></html>"; + + boolean copyHeadersToResponse = true; + boolean copyBinaryBodyToResponse = false; + boolean contentLengthAlreadyIncluded = false; + boolean contentTypeAlreadyIncluded = false; + String path = URI.create(requestLines.get(0).split(" ")[1]).getPath(); + StringBuilder textBody = new StringBuilder(); + + Response response; synchronized (mLock) { response = mResponseMap.get(path); } - if (response == null) { - httpResponse = createResponse(HttpStatus.SC_NOT_FOUND); - } else if (response.mIsNotFound) { - httpResponse = createResponse(HttpStatus.SC_NOT_FOUND); - for (Pair<String, String> header : response.mResponseHeaders) { - httpResponse.addHeader(header.first, header.second); - } - servedResponseFor(path, request); - } else if (response.mIsNoContent) { - httpResponse = createResponse(HttpStatus.SC_NO_CONTENT); - httpResponse.setHeader("Content-Length", "0"); - servedResponseFor(path, request); - } else if (response.mIsEmptyResponse) { - httpResponse = createResponse(HttpStatus.SC_FORBIDDEN); // arbitrary failure status - httpResponse.setHeader("Content-Length", "0"); - servedResponseFor(path, request); - } else if (response.mIsRedirect) { - httpResponse = createResponse(HttpStatus.SC_MOVED_TEMPORARILY); - for (Pair<String, String> header : response.mResponseHeaders) { - httpResponse.addHeader(header.first, header.second); - } - servedResponseFor(path, request); + + if (response == null || response.mIsNotFound) { + stream.println("HTTP/1.0 404 Not Found"); + textBody.append(String.format(bodyTemplate, "Not Found", "Not Found")); } else if (response.mForWebSocket) { - Header[] keys = request.getHeaders("Sec-WebSocket-Key"); - try { - if (keys.length == 1) { - final String key = keys[0].getValue(); - httpResponse = createResponse(HttpStatus.SC_SWITCHING_PROTOCOLS); - for (Pair<String, String> header : response.mResponseHeaders) { - httpResponse.addHeader(header.first, header.second); - } - httpResponse.addHeader("Sec-WebSocket-Accept", computeWebSocketAccept(key)); - } else { - httpResponse = createResponse(HttpStatus.SC_NOT_FOUND); - } - } catch (NoSuchAlgorithmException e) { - httpResponse = createResponse(HttpStatus.SC_NOT_FOUND); + List<String> keyHeaders = getMatchingHeadersValues(requestLines, "Sec-WebSocket-Key"); + if (keyHeaders.size() == 1) { + stream.println("HTTP/1.0 101 Switching Protocols"); + stream.println( + "Sec-WebSocket-Accept: " + computeWebSocketAccept(keyHeaders.get(0))); + } else { + stream.println("HTTP/1.0 404 Not Found"); + textBody.append(String.format(bodyTemplate, "Not Found", "Not Found")); + copyHeadersToResponse = false; } - servedResponseFor(path, request); + } else if (response.mIsNoContent) { + stream.println("HTTP/1.0 200 OK"); + copyHeadersToResponse = false; + } else if (response.mIsRedirect) { + stream.println("HTTP/1.0 302 Found"); + textBody.append(String.format(bodyTemplate, "Found", "Found")); + } else if (response.mIsEmptyResponse) { + stream.println("HTTP/1.0 403 Forbidden"); + copyHeadersToResponse = false; } else { if (response.mResponseAction != null) response.mResponseAction.run(); - httpResponse = createResponse(HttpStatus.SC_OK); - ByteArrayEntity entity = createEntity(response.mResponseData); - httpResponse.setEntity(entity); - httpResponse.setHeader("Content-Length", "" + entity.getContentLength()); - for (Pair<String, String> header : response.mResponseHeaders) { - httpResponse.addHeader(header.first, header.second); + stream.println("HTTP/1.0 200 OK"); + copyBinaryBodyToResponse = true; + } + + if (response != null) { + if (copyHeadersToResponse) { + for (Pair<String, String> header : response.mResponseHeaders) { + stream.println(header.first + ": " + header.second); + if (header.first.toLowerCase(Locale.ENGLISH).equals("content-length")) { + contentLengthAlreadyIncluded = true; + } else if (header.first.toLowerCase(Locale.ENGLISH).equals("content-type")) { + contentTypeAlreadyIncluded = true; + } + } } - servedResponseFor(path, request); - } - StatusLine sl = httpResponse.getStatusLine(); - Log.i(TAG, sl.getStatusCode() + "(" + sl.getReasonPhrase() + ")"); - - if (path.endsWith(".js")) { - httpResponse.addHeader("Content-Type", "application/javascript"); - } - - setDateHeaders(httpResponse); - return httpResponse; - } - - private void setDateHeaders(HttpResponse response) { - response.addHeader("Date", DateUtils.formatDate(new Date(), DateUtils.PATTERN_RFC1123)); - } - - /** - * Create an empty response with the given status. - */ - private HttpResponse createResponse(int status) { - HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_0, status, null); - String reason = null; - - // This synchronized silences findbugs. - synchronized (TestWebServer.class) { - if (sReasons == null) { - sReasons = new Hashtable<Integer, String>(); - sReasons.put(HttpStatus.SC_UNAUTHORIZED, "Unauthorized"); - sReasons.put(HttpStatus.SC_NOT_FOUND, "Not Found"); - sReasons.put(HttpStatus.SC_FORBIDDEN, "Forbidden"); - sReasons.put(HttpStatus.SC_MOVED_TEMPORARILY, "Moved Temporarily"); + synchronized (mLock) { + mResponseCountMap.put( + path, Integer.valueOf(mResponseCountMap.get(path).intValue() + 1)); + mLastRequestMap.put(path, requestLines); } - // Fill in error reason. Avoid use of the ReasonPhraseCatalog, which is - // Locale-dependent. - reason = sReasons.get(status); } - if (reason != null) { - StringBuilder buf = new StringBuilder("<html><head><title>"); - buf.append(reason); - buf.append("</title></head><body>"); - buf.append(reason); - buf.append("</body></html>"); - ByteArrayEntity entity = createEntity(buf.toString().getBytes()); - response.setEntity(entity); - response.setHeader("Content-Length", "" + entity.getContentLength()); - response.setReasonPhrase(reason); - } - return response; - } + // RFC 1123 + final SimpleDateFormat dateFormat = + new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); - /** - * Create a string entity for the given content. - */ - private ByteArrayEntity createEntity(byte[] data) { - ByteArrayEntity entity = new ByteArrayEntity(data); - entity.setContentType("text/html"); - return entity; + // Using print and println() because we don't want to dump it into log. + stream.print("Date: " + dateFormat.format(new Date())); + stream.println(); + + if (textBody.length() != 0) { + if (!contentTypeAlreadyIncluded && (path.endsWith(".html") || path.endsWith(".htm"))) { + stream.println("Content-Type: text/html"); + } + stream.println("Content-Length: " + textBody.length()); + stream.println(); + stream.print(textBody.toString()); + } else if (copyBinaryBodyToResponse) { + if (!contentTypeAlreadyIncluded && path.endsWith(".js")) { + stream.println("Content-Type: application/javascript"); + } else if (!contentTypeAlreadyIncluded + && (path.endsWith(".html") || path.endsWith(".htm"))) { + stream.println("Content-Type: text/html"); + } + if (!contentLengthAlreadyIncluded) { + stream.println("Content-Length: " + response.mResponseData.length); + } + stream.println(); + stream.write(response.mResponseData); + } else { + stream.println(); + } } /** @@ -629,7 +615,7 @@ md.update(key); md.update(guid); byte[] output = md.digest(); - return Base64.encodeToString(output, Base64.DEFAULT); + return Base64.encodeToString(output, Base64.NO_WRAP); } private static class ServerThread extends Thread { @@ -749,49 +735,43 @@ @Override public void run() { - HttpParams params = new BasicHttpParams(); - params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_0); - while (!getIsCancelled()) { - Socket socket = null; - try { - socket = mSocket.accept(); + try { + while (!getIsCancelled()) { + BufferedReader inputStream = null; + WebServerPrintStream outputStream = null; + Socket socket = mSocket.accept(); setCurrentRequestSocket(socket); + try { + inputStream = + new BufferedReader(new InputStreamReader(socket.getInputStream())); + List<String> lines = new ArrayList<String>(); + String line; - DefaultHttpServerConnection conn = new DefaultHttpServerConnection(); - conn.bind(socket, params); - - if (getIsCancelled()) continue; - HttpRequest request = conn.receiveRequestHeader(); - HttpResponse response = mServer.getResponse(request); - conn.sendResponseHeader(response); - conn.sendResponseEntity(response); - - conn.close(); - socket = null; - } catch (IOException e) { - // normal during shutdown, ignore - Log.w(TAG, e); - } catch (HttpException e) { - Log.w(TAG, e); - } catch (InterruptedException e) { - Log.w(TAG, e); - } catch (UnsupportedOperationException e) { - // DefaultHttpServerConnection's close() throws an - // UnsupportedOperationException. - Log.w(TAG, e); - } finally { - // Since DefaultHttpServerConnection can raise an exception - // during conn.close() (in the case of SSL), we always force - // the socket to close, since it may be left open. This will - // be a no-op if the connection managed to close the socket. - if (socket != null) { - try { - socket.close(); - } catch (IOException ignored) { - // safe to ignore + while (true) { + if (getIsCancelled()) return; + line = inputStream.readLine(); + // We ignore message body if any. + if (line == null || line.length() == 0) break; + lines.add(line); } + + if (lines.size() == 0) { + Log.w(TAG, "Empty request"); + } else { + outputStream = new WebServerPrintStream(socket.getOutputStream()); + mServer.outputResponse(lines, outputStream); + } + } finally { + // We ignore keep-alive header. + if (outputStream != null) outputStream.close(); + if (inputStream != null) inputStream.close(); + socket.close(); } } + } catch (SocketException e) { + } catch (NoSuchAlgorithmException ignore) { + } catch (IOException e) { + Log.w(TAG, e); } } }
diff --git a/services/ui/public/cpp/property_type_converters.cc b/services/ui/public/cpp/property_type_converters.cc index 276d200..1f077640 100644 --- a/services/ui/public/cpp/property_type_converters.cc +++ b/services/ui/public/cpp/property_type_converters.cc
@@ -41,6 +41,9 @@ // static gfx::Rect TypeConverter<gfx::Rect, std::vector<uint8_t>>::Convert( const std::vector<uint8_t>& input) { + if (input.size() != 16) + return gfx::Rect(); + return gfx::Rect( input[0] << 24 | input[1] << 16 | input[2] << 8 | input[3], input[4] << 24 | input[5] << 16 | input[6] << 8 | input[7], @@ -66,6 +69,9 @@ // static gfx::Size TypeConverter<gfx::Size, std::vector<uint8_t>>::Convert( const std::vector<uint8_t>& input) { + if (input.size() != 8) + return gfx::Size(); + return gfx::Size(input[0] << 24 | input[1] << 16 | input[2] << 8 | input[3], input[4] << 24 | input[5] << 16 | input[6] << 8 | input[7]); } @@ -84,6 +90,9 @@ // static int32_t TypeConverter<int32_t, std::vector<uint8_t>>::Convert( const std::vector<uint8_t>& input) { + if (input.size() != 4) + return 0; + return input[0] << 24 | input[1] << 16 | input[2] << 8 | input[3]; } @@ -105,10 +114,9 @@ // static int64_t TypeConverter<int64_t, std::vector<uint8_t>>::Convert( const std::vector<uint8_t>& input) { - if (input.size() < 8) { - NOTREACHED(); + if (input.size() != 8) return 0; - } + return static_cast<int64_t>(input[0]) << 56 | static_cast<int64_t>(input[1]) << 48 | static_cast<int64_t>(input[2]) << 40 |
diff --git a/services/ui/public/cpp/tests/BUILD.gn b/services/ui/public/cpp/tests/BUILD.gn index 559675d..fee5782 100644 --- a/services/ui/public/cpp/tests/BUILD.gn +++ b/services/ui/public/cpp/tests/BUILD.gn
@@ -22,6 +22,7 @@ "//services/service_manager/public/cpp", "//services/ui/public/cpp", "//testing/gtest", + "//ui/gfx:test_support", "//ui/gfx/geometry", "//ui/gfx/geometry/mojo", ]
diff --git a/services/ui/public/cpp/tests/property_type_converters_unittest.cc b/services/ui/public/cpp/tests/property_type_converters_unittest.cc index f4d5b4c..daefd6a 100644 --- a/services/ui/public/cpp/tests/property_type_converters_unittest.cc +++ b/services/ui/public/cpp/tests/property_type_converters_unittest.cc
@@ -22,5 +22,51 @@ EXPECT_TRUE(gfx::BitmapsAreEqual(bitmap, out)); } +TEST(PropertyTypeConvertersTest, Rectangle) { + const gfx::Rect rect(1, 2, 3, 4); + const auto encoded = mojo::ConvertTo<std::vector<uint8_t>>(rect); + const gfx::Rect decoded = mojo::ConvertTo<gfx::Rect>(encoded); + EXPECT_EQ(rect, decoded); + + // Verify a vector with an invalid size results in an empty rect. + EXPECT_EQ(gfx::Rect(), mojo::ConvertTo<gfx::Rect>(std::vector<uint8_t>())); +} + +TEST(PropertyTypeConvertersTest, Size) { + const gfx::Size size(121, 987); + const auto encoded = mojo::ConvertTo<std::vector<uint8_t>>(size); + const gfx::Size decoded = mojo::ConvertTo<gfx::Size>(encoded); + EXPECT_EQ(size, decoded); + + // Verify a vector with an invalid size results in an empty size. + EXPECT_EQ(gfx::Size(), mojo::ConvertTo<gfx::Size>(std::vector<uint8_t>())); +} + +TEST(PropertyTypeConvertersTest, Int32) { + const int32_t value = 0xFEDCBA90; + const auto encoded = mojo::ConvertTo<std::vector<uint8_t>>(value); + const int32_t decoded = mojo::ConvertTo<int32_t>(encoded); + EXPECT_EQ(value, decoded); + + // Verify a vector with an invalid size results in 0. + EXPECT_EQ(0, mojo::ConvertTo<int32_t>(std::vector<uint8_t>())); + EXPECT_EQ(0, mojo::ConvertTo<int32_t>(std::vector<uint8_t>(1, 1))); + EXPECT_EQ(0, mojo::ConvertTo<int32_t>(std::vector<uint8_t>(2, 1))); + EXPECT_EQ(0, mojo::ConvertTo<int32_t>(std::vector<uint8_t>(5, 1))); +} + +TEST(PropertyTypeConvertersTest, Int64) { + const int64_t value = 0xFEDCBA0123456789; + const auto encoded = mojo::ConvertTo<std::vector<uint8_t>>(value); + const int64_t decoded = mojo::ConvertTo<int64_t>(encoded); + EXPECT_EQ(value, decoded); + + // Verify a vector with an invalid size results in 0. + EXPECT_EQ(0, mojo::ConvertTo<int64_t>(std::vector<uint8_t>())); + EXPECT_EQ(0, mojo::ConvertTo<int64_t>(std::vector<uint8_t>(1, 1))); + EXPECT_EQ(0, mojo::ConvertTo<int64_t>(std::vector<uint8_t>(2, 1))); + EXPECT_EQ(0, mojo::ConvertTo<int64_t>(std::vector<uint8_t>(10, 1))); +} + } // namespace } // namespace mojo
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h index 7388854..644644ac 100644 --- a/skia/config/SkUserConfig.h +++ b/skia/config/SkUserConfig.h
@@ -228,6 +228,10 @@ #define SK_SUPPORT_LEGACY_PATHEFFECT_SUBCLASSES #endif +#ifndef SK_DISABLE_DEFERRED_PROXIES +#define SK_DISABLE_DEFERRED_PROXIES +#endif + ///////////////////////// Imported from BUILD.gn and skia_common.gypi /* In some places Skia can use static initializers for global initialization,
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process index 81f62a3..bfb239d 100644 --- a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process +++ b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
@@ -1,6 +1,9 @@ # These tests currently fail when they run with --site-per-process. # See https://crbug.com/477150. +# https://crbug.com/697111: assert failures in test added in r451938. +crbug.com/697111 svg/dom/getScreenCTM-ancestor-transform.html [ Failure ] + # https://crbug.com/670105: uninvestigated test timeout caused by r435352. crbug.com/670105 http/tests/security/opened-document-security-origin-resets-on-navigation.html [ Timeout ] crbug.com/670105 virtual/mojo-loading/http/tests/security/opened-document-security-origin-resets-on-navigation.html [ Timeout ]
diff --git a/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-downmix8-2channel-input-expected.txt b/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-downmix8-2channel-input-expected.txt deleted file mode 100644 index 9260ede..0000000 --- a/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-downmix8-2channel-input-expected.txt +++ /dev/null
@@ -1,9 +0,0 @@ -Tests downmixing an 8-channel source connected to a JavaScriptAudioNode with 2-channel input. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - -PASS onaudioprocess was called with correct input data. -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-downmix8-2channel-input.html b/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-downmix8-2channel-input.html index cdf1127..e4f1b88 100644 --- a/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-downmix8-2channel-input.html +++ b/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-downmix8-2channel-input.html
@@ -2,37 +2,33 @@ <html> <head> -<script src="../../resources/js-test.js"></script> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> <script src="../resources/audit-util.js"></script> -<script src="../resources/audio-testing.js"></script> -<script type="text/javascript" src="../resources/scriptprocessornode-testing.js"></script> +<script src="../resources/audit.js"></script> +<script src="../resources/scriptprocessornode-testing-audit.js"></script> </head> <body> - -<div id="description"></div> -<div id="console"></div> - <script> -description("Tests downmixing an 8-channel source connected to a JavaScriptAudioNode with 2-channel input."); +let audit = Audit.createTaskRunner(); -var sampleRate = 44100.0; -var sourceChannels = 8; -var inputChannels = 2; -var outputChannels = 6; +let sampleRate = 44100.0; +let sourceChannels = 8; +let inputChannels = 2; +let outputChannels = 6; -function runTest() { - if (window.testRunner) { - testRunner.dumpAsText(); - testRunner.waitUntilDone(); - } +audit.define( + { + label: 'test', + description: + 'downmix 8-channel source to ScriprProcessNode with 2-channel input' + }, + (task, should) => { + runJSNodeTest(should).then(() => task.done()); + }); - window.jsTestIsAsync = true; - - runJSNodeTest(); -} - -runTest(); +audit.run(); </script>
diff --git a/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-expected.txt b/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-expected.txt deleted file mode 100644 index ed4bf82f..0000000 --- a/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-expected.txt +++ /dev/null
@@ -1,22 +0,0 @@ -Tests ScriptProcessorNode. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - -PASS Exception was thrown when both numberOfInputChannels and numberOfOutputChannels are zero. -PASS Successfully created ScriptProcessorNode with numberOfInputChannels = 1 and numberOfOutputChannels = 0. -PASS Successfully created ScriptProcessorNode with numberOfInputChannels = 2 and numberOfOutputChannels = 0. -PASS Successfully created ScriptProcessorNode with numberOfInputChannels = 0 and numberOfOutputChannels = 1. -PASS Successfully created ScriptProcessorNode with numberOfInputChannels = 0 and numberOfOutputChannels = 2. -PASS Exception was thrown for illegal bufferSize. -PASS Successfully created ScriptProcessorNode with bufferSize = 256. -PASS Successfully created ScriptProcessorNode with bufferSize = 512. -PASS Successfully created ScriptProcessorNode with bufferSize = 1024. -PASS Successfully created ScriptProcessorNode with bufferSize = 2048. -PASS Successfully created ScriptProcessorNode with bufferSize = 4096. -PASS Successfully created ScriptProcessorNode with bufferSize = 8192. -PASS Successfully created ScriptProcessorNode with bufferSize = 16384. -PASS onaudioprocess was called with correct data. -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-upmix2-8channel-input-expected.txt b/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-upmix2-8channel-input-expected.txt deleted file mode 100644 index a1550d4..0000000 --- a/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-upmix2-8channel-input-expected.txt +++ /dev/null
@@ -1,9 +0,0 @@ -Tests upmixing a 2-channel source connected to a JavaScriptAudioNode with 8-channel input. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - -PASS onaudioprocess was called with correct input data. -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-upmix2-8channel-input.html b/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-upmix2-8channel-input.html index e390c67a..33f6535822 100644 --- a/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-upmix2-8channel-input.html +++ b/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-upmix2-8channel-input.html
@@ -2,35 +2,34 @@ <html> <head> -<script src="../../resources/js-test.js"></script> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> <script src="../resources/audit-util.js"></script> -<script src="../resources/audio-testing.js"></script> -<script type="text/javascript" src="../resources/scriptprocessornode-testing.js"></script> +<script src="../resources/audit.js"></script> +<script src="../resources/scriptprocessornode-testing-audit.js"></script> </head> <body> - -<div id="description"></div> -<div id="console"></div> - <script> -description("Tests upmixing a 2-channel source connected to a JavaScriptAudioNode with 8-channel input."); +let audit = Audit.createTaskRunner(); -var sampleRate = 44100.0; -var sourceChannels = 2; -var inputChannels = 8; -var outputChannels = 6; +let sampleRate = 44100.0; +let sourceChannels = 2; +let inputChannels = 8; +let outputChannels = 6; -window.jsTestIsAsync = true; +audit.define( + { + label: 'test', + description: + 'upmix a 2-channel source ScriptProcessorNode with 8-channel output' + }, + (task, should) => { + runJSNodeTest(should).then(() => task.done()); + ; + }); -function runTest() { - if (window.testRunner) - testRunner.dumpAsText(); - runJSNodeTest(); -} - -runTest(); - +audit.run(); </script> </body>
diff --git a/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-zero-input-channels-expected.txt b/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-zero-input-channels-expected.txt deleted file mode 100644 index 0a2ffcf..0000000 --- a/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-zero-input-channels-expected.txt +++ /dev/null
@@ -1,10 +0,0 @@ -Tests that ScriptProcessorNode accepts 0 input channels. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - -PASS Successfully created ScriptProcessorNode. -PASS ScriptProcessorNode accepts 0 input channels. -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-zero-input-channels.html b/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-zero-input-channels.html index 4a05ed93..c1af136f 100644 --- a/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-zero-input-channels.html +++ b/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode-zero-input-channels.html
@@ -1,67 +1,56 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> -<script src="../../resources/js-test.js"></script> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> <script src="../resources/audit-util.js"></script> -<script src="../resources/audio-testing.js"></script> +<script src="../resources/audit.js"></script> </head> <body> -<div id="description"></div> -<div id="console"></div> - <script> -description("Tests that ScriptProcessorNode accepts 0 input channels."); +let audit = Audit.createTaskRunner(); -var sampleRate = 44100.0; -var renderLengthInFrames = 512; -var bufferSize = 512; +let sampleRate = 44100.0; +let renderLengthInFrames = 512; +let bufferSize = 512; -function checkResult(e) -{ - testPassed("ScriptProcessorNode accepts 0 input channels."); +audit.define( + { + label: 'test', + description: 'Tests that ScriptProcessorNode accepts 0 input channels' + }, + (task, should) => { + let context = + new OfflineAudioContext(1, renderLengthInFrames, sampleRate); - finishJSTest(); -} + let node; -function runTest() -{ - if (window.testRunner) { - testRunner.dumpAsText(); - testRunner.waitUntilDone(); - } - - window.jsTestIsAsync = true; - - var context = new OfflineAudioContext(1, renderLengthInFrames, sampleRate); - - var node; - - try { + should(() => { node = context.createScriptProcessor(bufferSize, 0, 1); - testPassed("Successfully created ScriptProcessorNode."); - } catch (e) { - testFailed("Failed to create ScriptProcessorNode."); - } + }, 'node = context.createScriptProcessor(bufferSize, 0, 1)').notThrow(); + let source = context.createBufferSource(); + source.buffer = createImpulseBuffer(context, bufferSize); - var source = context.createBufferSource(); - source.buffer = createImpulseBuffer(context, bufferSize); + // The onaudioprocess function doesn't need to do anything. We just need + // the process to start to test that implementation accepts 0 input + // channels. + // + // FIXME: check the .inputBuffer attribute of the AudioProcessingEvent. + node.onaudioprocess = function(e) {}; + source.connect(node); + node.connect(context.destination); + source.start(0); - // The onaudioprocess function doesn't need to do anything. We just need the process to start - // to test that implementation accepts 0 input channels. - // - // FIXME: check the .inputBuffer attribute of the AudioProcessingEvent. - node.onaudioprocess = function(e) { }; - source.connect(node); - node.connect(context.destination); - source.start(0); + context.oncomplete = event => { + should(true, 'ScriptProcessorNode accepts 0 input channels').beTrue(); + task.done(); + }; - context.oncomplete = checkResult; - context.startRendering(); -} + context.startRendering(); + }); -runTest(); -successfullyParsed = true; +audit.run(); </script> </body>
diff --git a/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode.html b/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode.html index 5bc37cea..dbdec638 100644 --- a/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode.html +++ b/third_party/WebKit/LayoutTests/webaudio/ScriptProcessor/scriptprocessornode.html
@@ -1,171 +1,134 @@ <!DOCTYPE html> <html> <head> -<script src="../../resources/js-test.js"></script> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> <script src="../resources/audit-util.js"></script> -<script src="../resources/audio-testing.js"></script> +<script src="../resources/audit.js"></script> </head> <body> - -<div id="description"></div> -<div id="console"></div> - <script> -description("Tests ScriptProcessorNode."); +let audit = Audit.createTaskRunner(); -var sampleRate = 44100.0; -var outputChannels = 6; -var playbackTime = 0.0; +let sampleRate = 44100.0; +let outputChannels = 6; +let playbackTime = 0.0; -// For the current implementation of ScriptProcessorNode, when it works with OfflineAudioContext (which runs much faster -// than real-time) the event.inputBuffer might be overwrite again before onaudioprocess ever get chance to be called. -// We carefully arrange the renderLengthInFrames and bufferSize to have exactly the same value to avoid this issue. -var renderLengthInFrames = 512; -var bufferSize = 512; +// For the current implementation of ScriptProcessorNode, when it works with +// OfflineAudioContext (which runs much faster than real-time) the +// event.inputBuffer might be overwrite again before onaudioprocess ever get +// chance to be called. We carefully arrange the renderLengthInFrames and +// bufferSize to have exactly the same value to avoid this issue. +let renderLengthInFrames = 512; +let bufferSize = 512; -var context; +let context; function createBuffer(context, length) { - var audioBuffer = context.createBuffer(2, length, sampleRate); - var n = audioBuffer.length; - var dataL = audioBuffer.getChannelData(0); - var dataR = audioBuffer.getChannelData(1); + let audioBuffer = context.createBuffer(2, length, sampleRate); + let n = audioBuffer.length; + let dataL = audioBuffer.getChannelData(0); + let dataR = audioBuffer.getChannelData(1); - for (var i = 0; i < n; ++i) { - dataL[i] = -1; - dataR[i] = 1; - } + for (let i = 0; i < n; ++i) { + dataL[i] = -1; + dataR[i] = 1; + } - return audioBuffer; + return audioBuffer; } -function processAudioData(event) { - playbackTime = event.playbackTime; - var expectedTime = context.currentTime + (bufferSize / context.sampleRate); - var allowedTimeGap = 0.0000001; +function processAudioData(event, should) { + playbackTime = event.playbackTime; + let expectedTime = context.currentTime + (bufferSize / context.sampleRate); + let allowedTimeGap = 0.0000001; - // There may be a little time gap which is from different thread operation - // between currentTime when main thread fires onaudioprocess() and currentTime when read in JS - // since currentTime is continuously increasing on audio thread. + // There may be a little time gap which is from different thread operation + // between currentTime when main thread fires onaudioprocess() and currentTime + // when read in JS since currentTime is continuously increasing on audio + // thread. - shouldBeCloseTo("playbackTime", expectedTime, allowedTimeGap, true); + should(playbackTime, 'playbackTime').beCloseTo(expectedTime, { + threshold: allowedTimeGap + }); - buffer = event.outputBuffer; - if (buffer.numberOfChannels != outputChannels) - testFailed("numberOfOutputChannels doesn't match!"); + buffer = event.outputBuffer; + should(buffer.numberOfChannels, 'Number of output channels') + .beEqualTo(outputChannels); + should(buffer.length, 'Length of buffer').beEqualTo(bufferSize); - if (buffer.length != bufferSize) - testFailed("numberOfOutputChannels doesn't match!"); + buffer = event.inputBuffer; + let bufferDataL = buffer.getChannelData(0); + let bufferDataR = buffer.getChannelData(1); - buffer = event.inputBuffer; - var bufferDataL = buffer.getChannelData(0); - var bufferDataR = buffer.getChannelData(1); - - var success = true; - // Go through every sample and make sure it's all -1 for the left-channel, and all +1 for the right-channel. - for (var i = 0; i < buffer.length; ++i) { - if (bufferDataL[i] != -1 || bufferDataR[i] != 1) { - success = false; - break; - } - } - - if (success) { - testPassed("onaudioprocess was called with correct data."); - } else { - testFailed("onaudioprocess was called with wrong data."); - } + should(bufferDataL, 'Left channel').beConstantValueOf(-1); + should(bufferDataR, 'Right channel').beConstantValueOf(1); } -function doBufferSizeTest(size) { - try { - var jsnode = context.createScriptProcessor(size, 1, 1); - testPassed("Successfully created ScriptProcessorNode with bufferSize = " + size + "."); - } catch(e) { - testFailed("Failed to create ScriptProcessorNode with bufferSize = " + size + "."); - } +function doBufferSizeTest(size, should) { + should(() => { + context.createScriptProcessor(size, 1, 1); + }, 'context.createScriptProcessor(' + size + ', 1, 1)').notThrow(); } -function runTest() { - if (window.testRunner) { - testRunner.dumpAsText(); - testRunner.waitUntilDone(); - } +audit.define( + {label: 'test', description: 'Basic ScriptProcessorNode properties'}, + (task, should) => { + // Create offline audio context. + context = new OfflineAudioContext(2, renderLengthInFrames, sampleRate); - window.jsTestIsAsync = true; + should(() => { + context.createScriptProcessor(512, 0, 0); + }, 'createScriptProcessor(512, 0, 0)').throw(); - // Create offline audio context. - context = new OfflineAudioContext(2, renderLengthInFrames, sampleRate); + should(() => { + context.createScriptProcessor(512, 1, 0); + }, 'context.createScriptProcessor(512, 1, 0)').notThrow(); - try { - var jsnode = context.createScriptProcessor(512, 0, 0); - testFailed("Exception should be thrown when both numberOfInputChannels and numberOfOutputChannels are zero."); - } catch(e) { - testPassed("Exception was thrown when both numberOfInputChannels and numberOfOutputChannels are zero."); - } - - try { - var jsnode = context.createScriptProcessor(512, 1, 0); - testPassed("Successfully created ScriptProcessorNode with numberOfInputChannels = 1 and numberOfOutputChannels = 0."); - } catch(e) { - testFailed("Exception should not be thrown when numberOfInputChannels = 1 and numberOfOutputChannels = 0."); - } - - try { - var jsnode = context.createScriptProcessor(512, 2, 0); - testPassed("Successfully created ScriptProcessorNode with numberOfInputChannels = 2 and numberOfOutputChannels = 0."); - } catch(e) { - testFailed("Exception should not be thrown when numberOfInputChannels = 2 and numberOfOutputChannels = 0."); - } - - try { - var jsnode = context.createScriptProcessor(512, 0, 1); - testPassed("Successfully created ScriptProcessorNode with numberOfInputChannels = 0 and numberOfOutputChannels = 1."); - } catch(e) { - testFailed("Exception should not be thrown when numberOfInputChannels = 0 and numberOfOutputChannels = 1."); - } - - try { - var jsnode = context.createScriptProcessor(512, 0, 2); - testPassed("Successfully created ScriptProcessorNode with numberOfInputChannels = 0 and numberOfOutputChannels = 2."); - } catch(e) { - testFailed("Exception should not be thrown when numberOfInputChannels = 0 and numberOfOutputChannels = 2."); - } - - try { - var jsnode = context.createScriptProcessor(511, 1, 1); - testFailed("Exception should be thrown for illegal bufferSize."); - } catch(e) { - testPassed("Exception was thrown for illegal bufferSize."); - } + should(() => { + context.createScriptProcessor(512, 2, 0); + }, 'context.createScriptProcessor(512, 2, 0)').notThrow(); - doBufferSizeTest(256); - doBufferSizeTest(512); - doBufferSizeTest(1024); - doBufferSizeTest(2048); - doBufferSizeTest(4096); - doBufferSizeTest(8192); - doBufferSizeTest(16384); - - var sourceBuffer = createBuffer(context, renderLengthInFrames); + should(() => { + context.createScriptProcessor(512, 0, 1); + }, 'context.createScriptProcessor(512, 0, 1)').notThrow(); - var bufferSource = context.createBufferSource(); - bufferSource.buffer = sourceBuffer; + should(() => { + context.createScriptProcessor(512, 0, 2); + }, 'context.createScriptProcessor(512, 0, 2)').notThrow(); + should(() => { + context.createScriptProcessor(511, 1, 1); + }, 'context.createScriptProcessor(511, 1, 1)').throw(); - var jsnode = context.createScriptProcessor(bufferSize, 2, outputChannels); + doBufferSizeTest(256, should); + doBufferSizeTest(512, should); + doBufferSizeTest(1024, should); + doBufferSizeTest(2048, should); + doBufferSizeTest(4096, should); + doBufferSizeTest(8192, should); + doBufferSizeTest(16384, should); - bufferSource.connect(jsnode); - jsnode.connect(context.destination); - jsnode.onaudioprocess = processAudioData; + let sourceBuffer = createBuffer(context, renderLengthInFrames); - bufferSource.start(0); - context.oncomplete = finishJSTest; - context.startRendering(); -} + let bufferSource = context.createBufferSource(); + bufferSource.buffer = sourceBuffer; -runTest(); + let jsnode = context.createScriptProcessor(bufferSize, 2, outputChannels); + bufferSource.connect(jsnode); + jsnode.connect(context.destination); + jsnode.onaudioprocess = event => { + processAudioData(event, should); + }; + + bufferSource.start(0); + + context.startRendering().then(() => task.done()); + ; + }); + +audit.run(); </script> </body>
diff --git a/third_party/WebKit/LayoutTests/webaudio/resources/scriptprocessornode-testing-audit.js b/third_party/WebKit/LayoutTests/webaudio/resources/scriptprocessornode-testing-audit.js new file mode 100644 index 0000000..271fd6d --- /dev/null +++ b/third_party/WebKit/LayoutTests/webaudio/resources/scriptprocessornode-testing-audit.js
@@ -0,0 +1,85 @@ +// For the current implementation of JavaScriptAudioNode, when it works with +// OfflineAudioContext (which runs much faster than real-time) the +// event.inputBuffer might be overwrite again before onaudioprocess ever get +// chance to be called. We carefully arrange the renderLengthInFrames and +// bufferSize to have exactly the same value to avoid this issue. +let renderLengthInFrames = 512; +let bufferSize = 512; + +let context; + +function createBuffer(context, numberOfChannels, length) { + let audioBuffer = context.createBuffer(numberOfChannels, length, sampleRate); + + fillData(audioBuffer, numberOfChannels, audioBuffer.length); + return audioBuffer; +} + +function processAudioData(event, should) { + buffer = event.outputBuffer; + + should(buffer.numberOfChannels, 'Number of channels in output buffer') + .beEqualTo(outputChannels); + should(buffer.length, 'Length of output buffer').beEqualTo(bufferSize); + + buffer = event.inputBuffer; + + let success = checkStereoOnlyData(buffer, inputChannels, buffer.length); + + should(success, 'onaudioprocess was called with the correct input data') + .beTrue(); +} + +function fillData(buffer, numberOfChannels, length) { + for (let i = 0; i < numberOfChannels; ++i) { + let data = buffer.getChannelData(i); + + for (let j = 0; j < length; ++j) + if (i < 2) + data[j] = i * 2 - 1; + else + data[j] = 0; + } +} + +// Both 2 to 8 upmix and 8 to 2 downmix are just directly copy the first two +// channels and left channels are zeroed. +function checkStereoOnlyData(buffer, numberOfChannels, length) { + for (let i = 0; i < numberOfChannels; ++i) { + let data = buffer.getChannelData(i); + + for (let j = 0; j < length; ++j) { + if (i < 2) { + if (data[j] != i * 2 - 1) + return false; + } else { + if (data[j] != 0) + return false; + } + } + } + return true; +} + +function runJSNodeTest(should) { + // Create offline audio context. + context = new OfflineAudioContext(2, renderLengthInFrames, sampleRate); + + let sourceBuffer = + createBuffer(context, sourceChannels, renderLengthInFrames); + + let bufferSource = context.createBufferSource(); + bufferSource.buffer = sourceBuffer; + + let scriptNode = + context.createScriptProcessor(bufferSize, inputChannels, outputChannels); + + bufferSource.connect(scriptNode); + scriptNode.connect(context.destination); + scriptNode.onaudioprocess = event => { + processAudioData(event, should); + }; + + bufferSource.start(0); + return context.startRendering(); +}
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn index 06d1d35..7876782 100644 --- a/third_party/WebKit/Source/core/BUILD.gn +++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -1351,6 +1351,7 @@ "paint/PaintControllerPaintTest.cpp", "paint/PaintControllerPaintTest.h", "paint/PaintInfoTest.cpp", + "paint/PaintInvalidationTest.cpp", "paint/PaintLayerClipperTest.cpp", "paint/PaintLayerPainterTest.cpp", "paint/PaintLayerScrollableAreaTest.cpp",
diff --git a/third_party/WebKit/Source/core/frame/FrameView.cpp b/third_party/WebKit/Source/core/frame/FrameView.cpp index ca933a14..c805167 100644 --- a/third_party/WebKit/Source/core/frame/FrameView.cpp +++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -835,8 +835,7 @@ return; // If the visualViewport supplies scrollbars, we won't get a paint - // invalidation from computeScrollbarExistence so we need to force one - // TODO(bokan): We should avoid computeScrollbarExistence otherwise. + // invalidation from computeScrollbarExistence so we need to force one. if (visualViewportSuppliesScrollbars()) layoutViewItem.setMayNeedPaintInvalidation();
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp b/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp index 567a7a75..ff8d47b 100644 --- a/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp +++ b/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp
@@ -770,6 +770,13 @@ double time = value().toDouble(); + double duration = mediaElement().duration(); + // Workaround for floating point error - it's possible for this element's max + // attribute to be rounded to a value slightly higher than the duration. If + // this happens and scrubber is dragged near the max, seek to duration. + if (time > duration) + time = duration; + // FIXME: This will need to take the timeline offset into consideration // once that concept is supported, see https://crbug.com/312699 if (mediaElement().seekable()->contain(time))
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControlsOrientationLockDelegate.h b/third_party/WebKit/Source/core/html/shadow/MediaControlsOrientationLockDelegate.h index df02374b..c30cb7b7 100644 --- a/third_party/WebKit/Source/core/html/shadow/MediaControlsOrientationLockDelegate.h +++ b/third_party/WebKit/Source/core/html/shadow/MediaControlsOrientationLockDelegate.h
@@ -16,7 +16,7 @@ // MediaControlsOrientationLockDelegate is implementing the orientation lock // feature when a <video> is fullscreen. It is meant to be created by -// `MediaControls` when the feature apply. Once created, it will use events to +// `MediaControls` when the feature applies. Once created, it will use events to // change state. // // The different states of the class are:
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControlsTest.cpp b/third_party/WebKit/Source/core/html/shadow/MediaControlsTest.cpp index 271c810..4382731 100644 --- a/third_party/WebKit/Source/core/html/shadow/MediaControlsTest.cpp +++ b/third_party/WebKit/Source/core/html/shadow/MediaControlsTest.cpp
@@ -13,6 +13,7 @@ #include "core/dom/StyleEngine.h" #include "core/frame/Settings.h" #include "core/html/HTMLVideoElement.h" +#include "core/html/shadow/MediaControlElementTypes.h" #include "core/loader/EmptyClients.h" #include "core/testing/DummyPageHolder.h" #include "platform/heap/Handle.h" @@ -38,7 +39,7 @@ void setRate(double) override{}; void setVolume(double) override{}; WebTimeRanges buffered() const override { return WebTimeRanges(); }; - WebTimeRanges seekable() const override { return WebTimeRanges(); }; + WebTimeRanges seekable() const override { return m_seekable; }; void setSinkId(const WebString& sinkId, const WebSecurityOrigin&, WebSetSinkIdCallbacks*) override{}; @@ -63,6 +64,8 @@ size_t audioDecodedByteCount() const override { return 0; }; size_t videoDecodedByteCount() const override { return 0; }; void paint(WebCanvas*, const WebRect&, PaintFlags&) override{}; + + WebTimeRanges m_seekable; }; class MockWebRemotePlaybackClient : public WebRemotePlaybackClient { @@ -173,6 +176,10 @@ void simulateLoadedMetadata() { m_mediaControls->onLoadedMetadata(); } MediaControls& mediaControls() { return *m_mediaControls; } + MockVideoWebMediaPlayer* webMediaPlayer() { + return static_cast<MockVideoWebMediaPlayer*>( + mediaControls().mediaElement().webMediaPlayer()); + } Document& document() { return m_pageHolder->document(); } private: @@ -395,4 +402,37 @@ EXPECT_FALSE(isElementVisible(*downloadButton)); } +TEST_F(MediaControlsTest, TimelineSeekToRoundedEnd) { + ensureLayout(); + + MediaControlTimelineElement* timeline = + static_cast<MediaControlTimelineElement*>(getElementByShadowPseudoId( + mediaControls(), "-webkit-media-controls-timeline")); + ASSERT_NE(nullptr, timeline); + + mediaControls().mediaElement().setSrc("https://example.com/foo.mp4"); + testing::runPendingTasks(); + + // Tests the case where the real length of the video, |exactDuration|, gets + // rounded up slightly to |roundedUpDuration| when setting the timeline's + // |max| attribute (crbug.com/695065). + double exactDuration = 596.586667; + double roundedUpDuration = 596.587; + + WebTimeRange timeRange(0.0, exactDuration); + webMediaPlayer()->m_seekable.assign(&timeRange, 1); + mediaControls().mediaElement().durationChanged(exactDuration, + false /* requestSeek */); + simulateLoadedMetadata(); + + // Simulate a click slightly past the end of the track of the timeline's + // underlying <input type="range">. This would set the |value| to the |max| + // attribute, which can be slightly rounded relative to the duration. + timeline->setValueAsNumber(roundedUpDuration, ASSERT_NO_EXCEPTION); + ASSERT_EQ(roundedUpDuration, timeline->valueAsNumber()); + EXPECT_EQ(0.0, mediaControls().mediaElement().currentTime()); + timeline->dispatchInputEvent(); + EXPECT_EQ(exactDuration, mediaControls().mediaElement().currentTime()); +} + } // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_line_builder.cc b/third_party/WebKit/Source/core/layout/ng/ng_line_builder.cc index 082a27b0..4c4aa6c5 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_line_builder.cc +++ b/third_party/WebKit/Source/core/layout/ng/ng_line_builder.cc
@@ -410,9 +410,4 @@ } } -DEFINE_TRACE(NGLineBuilder) { - visitor->trace(inline_box_); - visitor->trace(constraint_space_); -} - } // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_line_builder.h b/third_party/WebKit/Source/core/layout/ng/ng_line_builder.h index 6816813..f8569c1 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_line_builder.h +++ b/third_party/WebKit/Source/core/layout/ng/ng_line_builder.h
@@ -26,8 +26,9 @@ // |end| can be extended multiple times before creating a line, usually until // |!CanFitOnLine()|. // |SetBreakOpportunity| can mark the last confirmed offset that can fit. -class CORE_EXPORT NGLineBuilder final - : public GarbageCollectedFinalized<NGLineBuilder> { +class CORE_EXPORT NGLineBuilder final { + STACK_ALLOCATED(); + public: NGLineBuilder(NGInlineNode*, const NGConstraintSpace*); @@ -87,8 +88,6 @@ // are placed. void CopyFragmentDataToLayoutBlockFlow(); - DECLARE_VIRTUAL_TRACE(); - private: struct LineItemChunk { unsigned index; @@ -140,8 +139,8 @@ const LineItemChunk&, LineBoxData*); - Member<NGInlineNode> inline_box_; - Member<const NGConstraintSpace> constraint_space_; + Persistent<NGInlineNode> inline_box_; + Persistent<const NGConstraintSpace> constraint_space_; Vector<RefPtr<NGPhysicalFragment>, 32> fragments_; Vector<NGLogicalOffset, 32> offsets_; Vector<LineBoxData, 32> line_box_data_list_;
diff --git a/third_party/WebKit/Source/core/paint/PaintInvalidationTest.cpp b/third_party/WebKit/Source/core/paint/PaintInvalidationTest.cpp new file mode 100644 index 0000000..5743de1 --- /dev/null +++ b/third_party/WebKit/Source/core/paint/PaintInvalidationTest.cpp
@@ -0,0 +1,57 @@ +// Copyright 2017 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 "core/frame/FrameView.h" +#include "core/layout/LayoutTestHelper.h" +#include "core/layout/LayoutView.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace blink { + +namespace { + +class PaintInvalidationTest : public RenderingTest { + public: + PaintInvalidationTest() + : RenderingTest(SingleChildLocalFrameClient::create()) {} +}; + +// Changing style in a way that changes overflow without layout should cause +// the layout view to possibly need a paint invalidation since we may have +// revealed additional background that can be scrolled into view. +TEST_F(PaintInvalidationTest, RecalcOverflowInvalidatesBackground) { + document().page()->settings().setViewportEnabled(true); + setBodyInnerHTML( + "<!DOCTYPE html>" + "<style type='text/css'>" + " body, html {" + " width: 100%;" + " height: 100%;" + " margin: 0px;" + " }" + " #container {" + " width: 100%;" + " height: 100%;" + " }" + "</style>" + "<div id='container'></div>"); + + document().view()->updateAllLifecyclePhases(); + + ScrollableArea* scrollableArea = document().view(); + ASSERT_EQ(scrollableArea->maximumScrollOffset().height(), 0); + EXPECT_FALSE(document().layoutView()->mayNeedPaintInvalidation()); + + Element* container = document().getElementById("container"); + container->setAttribute(HTMLNames::styleAttr, + "transform: translateY(1000px);"); + document().updateStyleAndLayoutTree(); + + EXPECT_EQ(scrollableArea->maximumScrollOffset().height(), 1000); + EXPECT_TRUE(document().layoutView()->mayNeedPaintInvalidation()); +} + +} // namespace + +} // namespace blink
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/common.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/common.py index 35e7288..e58ab8d 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/common.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/common.py
@@ -12,7 +12,7 @@ WPT_DEST_NAME = 'wpt' CSS_DEST_NAME = 'csswg-test' -WPT_GH_REPO_URL = 'https://github.com/w3c/web-platform-tests.git' +WPT_GH_REPO_URL_TEMPLATE = 'https://{}@github.com/w3c/web-platform-tests.git' # TODO(qyearsley): This directory should be able to be constructed with # WebKitFinder and WPT_DEST_NAME, plus the string "external".
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/local_wpt.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/local_wpt.py index 6c9b0ec..f4ce3e0c 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/local_wpt.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/local_wpt.py
@@ -8,7 +8,7 @@ from webkitpy.common.system.executive import ScriptError from webkitpy.w3c.chromium_commit import ChromiumCommit -from webkitpy.w3c.common import WPT_GH_REPO_URL, CHROMIUM_WPT_DIR +from webkitpy.w3c.common import WPT_GH_REPO_URL_TEMPLATE, CHROMIUM_WPT_DIR _log = logging.getLogger(__name__) @@ -16,7 +16,7 @@ class LocalWPT(object): - def __init__(self, host, path='/tmp/wpt'): + def __init__(self, host, gh_token=None, path='/tmp/wpt'): """ Args: host: A Host object. @@ -26,16 +26,19 @@ """ self.host = host self.path = path + self.gh_token = gh_token self.branch_name = 'chromium-export-try' def fetch(self): + assert self.gh_token, 'LocalWPT.gh_token required for fetch' if self.host.filesystem.exists(self.path): _log.info('WPT checkout exists at %s, fetching latest', self.path) self.run(['git', 'fetch', 'origin']) self.run(['git', 'checkout', 'origin/master']) else: - _log.info('Cloning %s into %s', WPT_GH_REPO_URL, self.path) - self.host.executive.run_command(['git', 'clone', WPT_GH_REPO_URL, self.path]) + _log.info('Cloning GitHub w3c/web-platform-tests into %s', self.path) + remote_url = WPT_GH_REPO_URL_TEMPLATE.format(self.gh_token) + self.host.executive.run_command(['git', 'clone', remote_url, self.path]) def run(self, command, **kwargs): """Runs a command in the local WPT directory."""
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/local_wpt_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/local_wpt_unittest.py index 6d3ae4f..ea5ee5f9 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/local_wpt_unittest.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/local_wpt_unittest.py
@@ -12,13 +12,20 @@ class LocalWPTTest(unittest.TestCase): + def test_fetch_requires_gh_token(self): + host = MockHost() + local_wpt = LocalWPT(host) + + with self.assertRaises(AssertionError): + local_wpt.fetch() + def test_fetch_when_wpt_dir_exists(self): host = MockHost() host.filesystem = MockFileSystem(files={ '/tmp/wpt': '' }) - local_wpt = LocalWPT(host) + local_wpt = LocalWPT(host, 'token') local_wpt.fetch() self.assertEqual(host.executive.calls, [ @@ -30,22 +37,22 @@ host = MockHost() host.filesystem = MockFileSystem() - local_wpt = LocalWPT(host) + local_wpt = LocalWPT(host, 'token') local_wpt.fetch() self.assertEqual(host.executive.calls, [ - ['git', 'clone', 'https://github.com/w3c/web-platform-tests.git', '/tmp/wpt'], + ['git', 'clone', 'https://token@github.com/w3c/web-platform-tests.git', '/tmp/wpt'], ]) def test_constructor(self): host = MockHost() - LocalWPT(host) + LocalWPT(host, 'token') self.assertEqual(len(host.executive.calls), 0) def test_run(self): host = MockHost() host.filesystem = MockFileSystem() - local_wpt = LocalWPT(host) + local_wpt = LocalWPT(host, 'token') local_wpt.run(['echo', 'rutabaga']) self.assertEqual(host.executive.calls, [['echo', 'rutabaga']]) @@ -57,7 +64,7 @@ 'crrev-parse': 'add087a97844f4b9e307d9a216940582d96db306', }, strict=True) host.filesystem = MockFileSystem() - local_wpt = LocalWPT(host) + local_wpt = LocalWPT(host, 'token') wpt_sha, chromium_commit = local_wpt.most_recent_chromium_commit() self.assertEqual(wpt_sha, '9ea4fc353a4b1c11c6e524270b11baa4d1ddfde8') @@ -68,7 +75,7 @@ host = MockHost() host.executive = MockExecutive(run_command_fn=lambda _: '') host.filesystem = MockFileSystem() - local_wpt = LocalWPT(host) + local_wpt = LocalWPT(host, 'token') commit = local_wpt.most_recent_chromium_commit() self.assertEqual(commit, (None, None)) @@ -77,13 +84,13 @@ host = MockHost() host.filesystem = MockFileSystem() - local_wpt = LocalWPT(host) + local_wpt = LocalWPT(host, 'token') local_wpt.fetch() local_branch_name = local_wpt.create_branch_with_patch('message', 'patch', 'author') self.assertEqual(local_branch_name, 'chromium-export-try') self.assertEqual(host.executive.calls, [ - ['git', 'clone', 'https://github.com/w3c/web-platform-tests.git', '/tmp/wpt'], + ['git', 'clone', 'https://token@github.com/w3c/web-platform-tests.git', '/tmp/wpt'], ['git', 'reset', '--hard', 'HEAD'], ['git', 'clean', '-fdx'], ['git', 'checkout', 'origin/master'],
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter.py index 2db7810..e8fa7bc2 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter.py
@@ -6,17 +6,18 @@ from webkitpy.w3c.local_wpt import LocalWPT from webkitpy.w3c.common import exportable_commits_since +from webkitpy.w3c.wpt_github import WPTGitHub _log = logging.getLogger(__name__) class TestExporter(object): - def __init__(self, host, wpt_github, dry_run=False): + def __init__(self, host, gh_user, gh_token, dry_run=False): self.host = host - self.wpt_github = wpt_github + self.wpt_github = WPTGitHub(host, gh_user, gh_token) self.dry_run = dry_run - self.local_wpt = LocalWPT(self.host) + self.local_wpt = LocalWPT(self.host, gh_token) self.local_wpt.fetch() def run(self):
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter_unittest.py index c31ec6e2..3543f0e 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter_unittest.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter_unittest.py
@@ -14,39 +14,42 @@ def setUp(self): self.host = MockHost() - self.wpt_github = MockWPTGitHub(pull_requests=[]) def test_stops_if_more_than_one_pr_is_in_flight(self): host = MockHost() - wpt_github = MockWPTGitHub(pull_requests=[{'id': 1}, {'id': 2}]) + test_exporter = TestExporter(host, 'gh-username', 'gh-token') + test_exporter.wpt_github = MockWPTGitHub(pull_requests=[{'id': 1}, {'id': 2}]) # TODO: make Exception more specific with self.assertRaises(Exception): - TestExporter(host, wpt_github).run() + test_exporter.run() def test_if_pr_exists_merges_it(self): host = MockHost() - wpt_github = MockWPTGitHub(pull_requests=[{'number': 1, 'title': 'abc'}]) - TestExporter(host, wpt_github).run() + test_exporter = TestExporter(host, 'gh-username', 'gh-token') + test_exporter.wpt_github = MockWPTGitHub(pull_requests=[{'number': 1, 'title': 'abc'}]) + test_exporter.run() - self.assertIn('merge_pull_request', wpt_github.calls) + self.assertIn('merge_pull_request', test_exporter.wpt_github.calls) def test_merge_failure_errors_out(self): host = MockHost() - wpt_github = MockWPTGitHub(pull_requests=[{'number': 1, 'title': 'abc'}], - unsuccessful_merge=True) + test_exporter = TestExporter(host, 'gh-username', 'gh-token') + test_exporter.wpt_github = MockWPTGitHub(pull_requests=[{'number': 1, 'title': 'abc'}], + unsuccessful_merge=True) # TODO: make Exception more specific with self.assertRaises(Exception): - TestExporter(host, wpt_github).run() + test_exporter.run() def test_dry_run_stops_before_creating_pr(self): host = MockHost() host.executive = MockExecutive(output='beefcafe') - wpt_github = MockWPTGitHub(pull_requests=[{'number': 1, 'title': 'abc'}]) - TestExporter(host, wpt_github, dry_run=True).run() + test_exporter = TestExporter(host, 'gh-username', 'gh-token', dry_run=True) + test_exporter.wpt_github = MockWPTGitHub(pull_requests=[{'number': 1, 'title': 'abc'}]) + test_exporter.run() - self.assertEqual(wpt_github.calls, ['in_flight_pull_requests']) + self.assertEqual(test_exporter.wpt_github.calls, ['in_flight_pull_requests']) def test_creates_pull_request_for_earliest_commit(self): host = MockHost() @@ -65,10 +68,10 @@ return canned_git_outputs.get(args[1], '') host.executive = MockExecutive(run_command_fn=mock_command) - wpt_github = MockWPTGitHub(pull_requests=[]) + test_exporter = TestExporter(host, 'gh-username', 'gh-token') + test_exporter.wpt_github = MockWPTGitHub(pull_requests=[]) + test_exporter.run() - TestExporter(host, wpt_github).run() - - self.assertEqual(wpt_github.calls, ['in_flight_pull_requests', 'create_pr']) - self.assertEqual(wpt_github.pull_requests_created, + self.assertEqual(test_exporter.wpt_github.calls, ['in_flight_pull_requests', 'create_pr']) + self.assertEqual(test_exporter.wpt_github.pull_requests_created, [('chromium-export-try', 'older fake text', 'older fake text')])
diff --git a/third_party/WebKit/Tools/Scripts/wpt-export b/third_party/WebKit/Tools/Scripts/wpt-export index c107e70..8d5039a 100755 --- a/third_party/WebKit/Tools/Scripts/wpt-export +++ b/third_party/WebKit/Tools/Scripts/wpt-export
@@ -16,7 +16,6 @@ from webkitpy.common.host import Host from webkitpy.w3c.test_exporter import TestExporter -from webkitpy.w3c.wpt_github import WPTGitHub _log = logging.getLogger(__name__) @@ -44,24 +43,23 @@ 'This will override credentials that were passed via command line or env var.') args = parser.parse_args() host = Host() + gh_user = args.gh_user + gh_token = args.gh_token - if not args.gh_user: - args.gh_user = host.environ.get('GH_USER') - if not args.gh_token: - args.gh_token = host.environ.get('GH_TOKEN') + if not gh_user: + gh_user = host.environ.get('GH_USER') + if not gh_token: + gh_token = host.environ.get('GH_TOKEN') if args.github_credentials_json: with open(args.github_credentials_json, 'r') as f: contents = json.load(f) - args.gh_user = contents['GH_USER'] - args.gh_token = contents['GH_TOKEN'] + gh_user = contents['GH_USER'] + gh_token = contents['GH_TOKEN'] - if not (args.gh_user and args.gh_token): + if not (gh_user and gh_token): parser.error('Must provide both gh_user and gh_token for GitHub.') - wpt_github = WPTGitHub(host, args.gh_user, args.gh_token) - test_exporter = TestExporter(host, wpt_github, dry_run=args.dry_run) - - test_exporter.run() + TestExporter(host, gh_user, gh_token, dry_run=args.dry_run).run() if __name__ == '__main__':
diff --git a/third_party/closure_compiler/compiled_resources2.gyp b/third_party/closure_compiler/compiled_resources2.gyp index bc926abe..91ae90f 100644 --- a/third_party/closure_compiler/compiled_resources2.gyp +++ b/third_party/closure_compiler/compiled_resources2.gyp
@@ -19,6 +19,7 @@ '<(DEPTH)/chrome/browser/resources/chromeos/network_ui/compiled_resources2.gyp:*', '<(DEPTH)/chrome/browser/resources/chromeos/quick_unlock/compiled_resources2.gyp:*', '<(DEPTH)/chrome/browser/resources/chromeos/select_to_speak/compiled_resources2.gyp:*', + '<(DEPTH)/chrome/browser/resources/chromeos/switch_access/compiled_resources2.gyp:*', '<(DEPTH)/chrome/browser/resources/extensions/compiled_resources2.gyp:*', '<(DEPTH)/chrome/browser/resources/history/compiled_resources2.gyp:*', '<(DEPTH)/chrome/browser/resources/md_bookmarks/compiled_resources2.gyp:*',
diff --git a/third_party/widevine/cdm/BUILD.gn b/third_party/widevine/cdm/BUILD.gn index 413b1fa2..5c02682 100644 --- a/third_party/widevine/cdm/BUILD.gn +++ b/third_party/widevine/cdm/BUILD.gn
@@ -25,7 +25,10 @@ if (is_chromeos) { widevine_cdm_version_h_file = "chromeos/$widevine_arch/widevine_cdm_version.h" - widevine_cdm_binary_files = [ "chromeos/$widevine_arch/libwidevinecdm.so" ] + widevine_cdm_binary_files = [ + "chromeos/$widevine_arch/libwidevinecdm.so", + "chromeos/$widevine_arch/libwidevinecdm.so.sig", + ] } else if (is_linux) { widevine_cdm_version_h_file = "linux/$widevine_arch/widevine_cdm_version.h" widevine_cdm_binary_files = [
diff --git a/tools/licenses.py b/tools/licenses.py index 9ba1cff..b5b68ff 100755 --- a/tools/licenses.py +++ b/tools/licenses.py
@@ -18,6 +18,7 @@ import argparse import cgi import os +import subprocess import sys # TODO(agrieve): Move build_utils.WriteDepFile into a non-android directory. @@ -461,6 +462,20 @@ return FilterDirsWithFiles(third_party_dirs, root) +def FindThirdPartyDeps(gn_out_dir, gn_target): + if not gn_out_dir: + raise RuntimeError("--gn-out-dir is required if --gn-target is used.") + + gn_deps = subprocess.check_output(["gn", "desc", gn_out_dir, gn_target, + "deps", "--as=buildfile", "--all"]) + third_party_deps = set() + for build_dep in gn_deps.split(): + if ("third_party" in build_dep and + os.path.basename(build_dep) == "BUILD.gn"): + third_party_deps.add(os.path.dirname(build_dep)) + return third_party_deps + + def ScanThirdPartyDirs(root=None): """Scan a list of directories and report on any problems we find.""" if root is None: @@ -483,7 +498,7 @@ def GenerateCredits( file_template_file, entry_template_file, output_file, target_os, - depfile=None): + gn_out_dir, gn_target, depfile=None): """Generate about:credits.""" def EvaluateTemplate(template, env, escape=True): @@ -495,7 +510,15 @@ template = template.replace('{{%s}}' % key, val) return template - third_party_dirs = FindThirdPartyDirs(PRUNE_PATHS, _REPOSITORY_ROOT) + if gn_target: + third_party_dirs = FindThirdPartyDeps(gn_out_dir, gn_target) + + # Sanity-check to raise a build error if invalid gn_... settings are + # somehow passed to this script. + if not third_party_dirs: + raise RuntimeError("No deps found.") + else: + third_party_dirs = FindThirdPartyDirs(PRUNE_PATHS, _REPOSITORY_ROOT) if not file_template_file: file_template_file = os.path.join(_REPOSITORY_ROOT, 'components', @@ -516,8 +539,12 @@ continue if metadata['License File'] == NOT_SHIPPED: continue - if target_os == 'ios': - # Skip over files that are known not to be used on iOS. + if target_os == 'ios' and not gn_target: + # Skip over files that are known not to be used on iOS. But + # skipping is unnecessary if GN was used to query the actual + # dependencies. + # TODO(lambroslambrou): Remove this step once the iOS build is + # updated to provide --gn-target to this script. if path in KNOWN_NON_IOS_LIBRARIES: continue env = { @@ -570,6 +597,10 @@ help='Template HTML to use for each license.') parser.add_argument('--target-os', help='OS that this build is targeting.') + parser.add_argument('--gn-out-dir', + help='GN output directory for scanning dependencies.') + parser.add_argument('--gn-target', + help='GN target to scan for dependencies.') parser.add_argument('command', choices=['help', 'scan', 'credits']) parser.add_argument('output_file', nargs='?') build_utils.AddDepfileOption(parser) @@ -580,7 +611,8 @@ return 1 elif args.command == 'credits': if not GenerateCredits(args.file_template, args.entry_template, - args.output_file, args.target_os, args.depfile): + args.output_file, args.target_os, + args.gn_out_dir, args.gn_target, args.depfile): return 1 else: print __doc__
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 52b7c8e..be6691f 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -99575,9 +99575,9 @@ </enum> <enum name="MarkHttpAsStatus" type="int"> - <int value="0" label="Neutral"/> + <int value="0" label="Neutral (deprecated February 2017)"/> <int value="1" label="Non-Secure"/> - <int value="2" label="Neutral with a verbose warning"/> + <int value="2" label="Neutral with a verbose warning on sensitive fields"/> </enum> <enum name="MarkNonSecureAsStatus" type="int">
diff --git a/ui/aura/mus/client_surface_embedder.cc b/ui/aura/mus/client_surface_embedder.cc index da2bb61..9bd4106 100644 --- a/ui/aura/mus/client_surface_embedder.cc +++ b/ui/aura/mus/client_surface_embedder.cc
@@ -67,7 +67,7 @@ clip_layer_->SetBounds( gfx::Rect(0, 0, window_->bounds().width(), window_->bounds().height())); - surface_layer_->SetShowSurface( + surface_layer_->SetShowPrimarySurface( surface_info, make_scoped_refptr(new StubSurfaceReferenceFactory)); }
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc index 371bf4a4..5995786 100644 --- a/ui/compositor/layer.cc +++ b/ui/compositor/layer.cc
@@ -183,8 +183,8 @@ // cc::Layer state. if (surface_layer_ && surface_layer_->primary_surface_info().id().is_valid()) { - clone->SetShowSurface(surface_layer_->primary_surface_info(), - surface_layer_->surface_reference_factory()); + clone->SetShowPrimarySurface(surface_layer_->primary_surface_info(), + surface_layer_->surface_reference_factory()); } else if (type_ == LAYER_SOLID_COLOR) { clone->SetColor(GetTargetColor()); } @@ -653,24 +653,26 @@ return texture_layer_->flipped(); } -void Layer::SetShowSurface( +void Layer::SetShowPrimarySurface( const cc::SurfaceInfo& surface_info, scoped_refptr<cc::SurfaceReferenceFactory> ref_factory) { DCHECK(type_ == LAYER_TEXTURED || type_ == LAYER_SOLID_COLOR); - scoped_refptr<cc::SurfaceLayer> new_layer = - cc::SurfaceLayer::Create(ref_factory); - new_layer->SetPrimarySurfaceInfo(surface_info); - SwitchToLayer(new_layer); - surface_layer_ = new_layer; + if (!surface_layer_) { + scoped_refptr<cc::SurfaceLayer> new_layer = + cc::SurfaceLayer::Create(ref_factory); + SwitchToLayer(new_layer); + surface_layer_ = new_layer; + } + + surface_layer_->SetPrimarySurfaceInfo(surface_info); frame_size_in_dip_ = gfx::ConvertSizeToDIP(surface_info.device_scale_factor(), surface_info.size_in_pixels()); RecomputeDrawsContentAndUVRect(); - for (const auto& mirror : mirrors_) { - mirror->dest()->SetShowSurface(surface_info, ref_factory); - } + for (const auto& mirror : mirrors_) + mirror->dest()->SetShowPrimarySurface(surface_info, ref_factory); } void Layer::SetShowSolidColorContent() {
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h index 6295c41..ac7a176 100644 --- a/ui/compositor/layer.h +++ b/ui/compositor/layer.h
@@ -294,9 +294,10 @@ void SetTextureFlipped(bool flipped); bool TextureFlipped() const; - // Begins showing content from a surface with a particular id. - void SetShowSurface(const cc::SurfaceInfo& surface_info, - scoped_refptr<cc::SurfaceReferenceFactory> surface_ref); + // Begins showing content from a surface with a particular ID. + void SetShowPrimarySurface( + const cc::SurfaceInfo& surface_info, + scoped_refptr<cc::SurfaceReferenceFactory> surface_ref); bool has_external_content() { return texture_layer_.get() || surface_layer_.get();
diff --git a/ui/compositor/layer_unittest.cc b/ui/compositor/layer_unittest.cc index f52ae02..8258ac2 100644 --- a/ui/compositor/layer_unittest.cc +++ b/ui/compositor/layer_unittest.cc
@@ -28,6 +28,7 @@ #include "cc/output/copy_output_result.h" #include "cc/surfaces/sequence_surface_reference_factory.h" #include "cc/surfaces/surface_id.h" +#include "cc/surfaces/surface_reference_factory.h" #include "cc/surfaces/surface_sequence.h" #include "cc/test/pixel_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" @@ -1806,7 +1807,7 @@ // Showing surface content changes the underlying cc layer. before = child->cc_layer_for_testing(); - child->SetShowSurface( + child->SetShowPrimarySurface( cc::SurfaceInfo(cc::SurfaceId(), 1.0, gfx::Size(10, 10)), new TestSurfaceReferenceFactory()); EXPECT_TRUE(child->cc_layer_for_testing()); @@ -1821,12 +1822,14 @@ TEST_F(LayerWithDelegateTest, ExternalContentMirroring) { std::unique_ptr<Layer> layer(CreateLayer(LAYER_SOLID_COLOR)); + scoped_refptr<cc::SurfaceReferenceFactory> reference_factory( + new TestSurfaceReferenceFactory()); cc::SurfaceId surface_id( cc::FrameSinkId(0, 1), cc::LocalSurfaceId(2, base::UnguessableToken::Create())); cc::SurfaceInfo surface_info(surface_id, 1.0f, gfx::Size(10, 10)); - layer->SetShowSurface(surface_info, new TestSurfaceReferenceFactory()); + layer->SetShowPrimarySurface(surface_info, reference_factory); const auto mirror = layer->Mirror(); auto* const cc_layer = mirror->cc_layer_for_testing(); @@ -1839,17 +1842,17 @@ cc::SurfaceId(cc::FrameSinkId(1, 2), cc::LocalSurfaceId(3, base::UnguessableToken::Create())); cc::SurfaceInfo surface_info_2(surface_id, 2.0f, gfx::Size(20, 20)); - layer->SetShowSurface(surface_info_2, new TestSurfaceReferenceFactory()); + layer->SetShowPrimarySurface(surface_info_2, reference_factory); - // A new cc::Layer should be created for the mirror. - EXPECT_NE(cc_layer, mirror->cc_layer_for_testing()); - surface = static_cast<cc::SurfaceLayer*>(mirror->cc_layer_for_testing()); + // The mirror should continue to use the same cc_layer. + EXPECT_EQ(cc_layer, mirror->cc_layer_for_testing()); + layer->SetShowPrimarySurface(surface_info_2, reference_factory); // Surface updates propagate to the mirror. EXPECT_EQ(surface_info_2, surface->primary_surface_info()); } -// Test if frame size in dip is properly calculated in SetShowSurface +// Test if frame size in dip is properly calculated in SetShowPrimarySurface. TEST_F(LayerWithDelegateTest, FrameSizeInDip) { std::unique_ptr<Layer> layer(CreateLayer(LAYER_SOLID_COLOR)); @@ -1857,8 +1860,9 @@ cc::FrameSinkId(0, 1), cc::LocalSurfaceId(2, base::UnguessableToken::Create())); - layer->SetShowSurface(cc::SurfaceInfo(surface_id, 2.0f, gfx::Size(30, 40)), - new TestSurfaceReferenceFactory()); + layer->SetShowPrimarySurface( + cc::SurfaceInfo(surface_id, 2.0f, gfx::Size(30, 40)), + new TestSurfaceReferenceFactory()); EXPECT_EQ(layer->frame_size_in_dip_for_testing(), gfx::Size(15, 20)); } @@ -1877,7 +1881,7 @@ // Showing surface content changes the underlying cc layer. scoped_refptr<cc::Layer> before = layer->cc_layer_for_testing(); - layer->SetShowSurface( + layer->SetShowPrimarySurface( cc::SurfaceInfo(cc::SurfaceId(), 1.0, gfx::Size(10, 10)), new TestSurfaceReferenceFactory()); EXPECT_EQ(layer->layer_grayscale(), 0.5f); @@ -2201,7 +2205,7 @@ FrameDamageCheckingDelegate delegate; layer->set_delegate(&delegate); - layer->SetShowSurface( + layer->SetShowPrimarySurface( cc::SurfaceInfo(cc::SurfaceId(), 1.0, gfx::Size(10, 10)), new TestSurfaceReferenceFactory());
diff --git a/ui/webui/resources/js/polymer_config.js b/ui/webui/resources/js/polymer_config.js index 02e4d9f3..4d23989 100644 --- a/ui/webui/resources/js/polymer_config.js +++ b/ui/webui/resources/js/polymer_config.js
@@ -6,6 +6,7 @@ Polymer = { dom: 'shadow', lazyRegister: true, + preserveStyleIncludes: true, // Only matters when using polymer-css-build. useNativeCSSProperties: true, }; } else {